diff --git a/.gitignore b/.gitignore
index ac31baf..944c89d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,3 +40,4 @@ Thumbs.db
# Next.js
.next
+*.local
diff --git a/README.md b/README.md
index ceb1909..d72537d 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# The fal-serverless JS Client
-
+


@@ -22,7 +22,7 @@ It also handle platform differences, so it work seamlessly across different JS r
> **Note**
>
-> Make sure you followed the [fal-serverless getting started]() so you get your credentials and register your functions.
+> Make sure you followed the [fal-serverless getting started](https://docs.fal.ai/fal-serverless/quickstart) so you get your credentials and register your functions.
1. First you need to configure your credentials:
diff --git a/apps/demo-app/pages/index.tsx b/apps/demo-app/pages/index.tsx
index 0f4f80b..181a3e4 100644
--- a/apps/demo-app/pages/index.tsx
+++ b/apps/demo-app/pages/index.tsx
@@ -1,57 +1,69 @@
-import styles from './index.module.css';
-import * as fal from '@fal-ai/serverless-client';
-
-fal.config({
- credentials: {
- userId: '',
- keyId: '',
- keySecret: '',
- },
-});
+import { getJoke } from '../services/getJoke';
export async function getServerSideProps(context) {
- console.log('About to call a fal serverless function from NodeJS');
- const result = await fal.run(
- 'e300f60b-4a7c-44cd-871d-bea588ef43d6/jokes/add',
- {
- input: {
- joke: 'fal serverless is cool, so the joke is on you!',
+ try {
+ const result = await getJoke();
+ return {
+ props: {
+ ...result,
},
- }
+ };
+ } catch (error) {
+ return {
+ props: {
+ error: error.message,
+ },
+ };
+ }
+}
+
+function Error(props) {
+ if (!props.error) {
+ return null;
+ }
+ return (
+
+ Error {props.error}
+
);
- console.log(result);
- const random = await fal.run(
- 'e300f60b-4a7c-44cd-871d-bea588ef43d6/jokes/get',
- {
- method: 'get',
- }
- );
- console.log(random);
- return {
- props: {
- random,
- result,
- },
- };
}
export function Index(props) {
+ const handleClick = async (e) => {
+ e.preventDefault();
+ try {
+ const joke = await getJoke();
+ console.log(joke);
+ } catch (e) {
+ console.log(e);
+ }
+ };
return (
-
-
- Hello fal serverless
-
-
- This page can access fal serverless functions when
- it's rendering.
-
-
- Added joke with success?{' '}
- {props.result.success.toString()}
-
-
- Joke {props.random.joke}
-
+
+
+
+ Hello fal-serverless
+
+
+ This page can access fal-serverless functions when
+ it's rendering.
+
+
+
+
+ Get Joke
+
+
+
+ Here's a joke: {props.joke}
+
+
);
}
diff --git a/apps/demo-app/services/generateImage.ts b/apps/demo-app/services/generateImage.ts
index 54d5935..2ed4e27 100644
--- a/apps/demo-app/services/generateImage.ts
+++ b/apps/demo-app/services/generateImage.ts
@@ -18,12 +18,10 @@ fal.config({
export async function generateImage(
input: GenerateImageInput
): Promise
{
- const result = await fal.run(
- 'a51c0ca0-9011-4ff0-8dc1-2ac0b42a9fd0/generate',
- {
- input,
- }
- );
+ const result = await fal.run('a51c0ca0-9011-4ff0-8dc1-2ac0b42a9fd0', {
+ path: '/generate',
+ input,
+ });
const data = result['raw_data'];
return `data:image/jpg;base64,${data}`;
}
diff --git a/apps/demo-app/services/getJoke.ts b/apps/demo-app/services/getJoke.ts
new file mode 100644
index 0000000..e4afad2
--- /dev/null
+++ b/apps/demo-app/services/getJoke.ts
@@ -0,0 +1,18 @@
+import * as fal from '@fal-ai/serverless-client';
+
+fal.config({
+ host: 'gateway.alpha.fal.ai',
+ credentials: {
+ userId: process.env.FAL_USER_ID || '',
+ keyId: process.env.FAL_KEY_ID || '',
+ keySecret: process.env.FAL_KEY_SECRET || '',
+ },
+});
+
+export type GetJokeInput = {
+ language?: string;
+};
+
+export function getJoke(input?: GetJokeInput): Promise<{ joke: string }> {
+ return fal.run('fastapi_get_joke', { input });
+}
diff --git a/libs/client/src/config.ts b/libs/client/src/config.ts
index 891db65..403ae69 100644
--- a/libs/client/src/config.ts
+++ b/libs/client/src/config.ts
@@ -12,7 +12,7 @@ export type Config = {
export type RequiredConfig = Required;
const DEFAULT_CONFIG: Partial = {
- host: 'https://gateway.shark.fal.ai',
+ host: 'gateway.shark.fal.ai',
};
let configuration: RequiredConfig | undefined = undefined;
@@ -33,7 +33,7 @@ export function config(config: Config) {
*/
export function getConfig(): RequiredConfig {
if (typeof configuration === 'undefined') {
- throw new Error('You must configure fal serverless first.');
+ throw new Error('You must configure fal-serverless first.');
}
return configuration;
}
diff --git a/libs/client/src/function.spec.ts b/libs/client/src/function.spec.ts
new file mode 100644
index 0000000..2cbd87e
--- /dev/null
+++ b/libs/client/src/function.spec.ts
@@ -0,0 +1,28 @@
+import { randomUUID } from 'crypto';
+import { config, getConfig } from './config';
+import { buildUrl } from './function';
+
+config({
+ host: 'gateway.alpha.fal.ai',
+ credentials: {
+ userId: 'github|123456',
+ keyId: 'a91ff3ca-71bc-4c8c-b400-859f6cbe804d',
+ keySecret: '0123456789abcdfeghijklmnopqrstuv',
+ },
+});
+
+describe('The function test suite', () => {
+ it('should build the URL with a function UUIDv4', () => {
+ const { credentials } = getConfig();
+ const id = randomUUID();
+ const url = buildUrl(id);
+ expect(url).toMatch(`trigger/${credentials.userId}/${id}`);
+ });
+
+ it('should build the URL with a function alias', () => {
+ const { host } = getConfig();
+ const alias = 'some-alias';
+ const url = buildUrl(alias);
+ expect(url).toMatch(`${alias}.${host}`);
+ });
+});
diff --git a/libs/client/src/function.ts b/libs/client/src/function.ts
index 091ee73..d0a5721 100644
--- a/libs/client/src/function.ts
+++ b/libs/client/src/function.ts
@@ -1,12 +1,22 @@
import fetch from 'cross-fetch';
import { getConfig } from './config';
import { getUserAgent, isBrowser } from './runtime';
+import { isUUIDv4 } from './utils';
/**
* The function input and other configuration when running
* the function, such as the HTTP method to use.
*/
type RunOptions = {
+ /**
+ * The path to the function, if any. Defaults to `/`.
+ */
+ readonly path?: string;
+
+ /**
+ * The function input. It will be submitted either as query params
+ * or the body payload, depending on the `method`.
+ */
readonly input?: Input;
/**
@@ -15,6 +25,35 @@ type RunOptions = {
readonly method?: 'get' | 'post' | 'put' | 'delete';
};
+/**
+ * Builds the final url to run the function based on its `id` or alias and
+ * a the options from `RunOptions `.
+ *
+ * @private
+ * @param id the function id or alias
+ * @param options the run options
+ * @returns the final url to run the function
+ */
+export function buildUrl (
+ id: string,
+ options: RunOptions = {}
+): string {
+ const { credentials, host } = getConfig();
+ const method = (options.method ?? 'post').toLowerCase();
+ const path = options.path ?? '';
+ const params =
+ method === 'get' ? new URLSearchParams(options.input ?? {}) : undefined;
+ let queryParams = '';
+ if (params) {
+ queryParams = `?${params.toString()}`;
+ }
+ if (isUUIDv4(id)) {
+ return `https://${host}/trigger/${credentials.userId}/${id}/${path}${queryParams}`;
+ }
+ const userId = credentials.userId.replace(/github\|/g, '');
+ return `https://${userId}-${id}.${host}/${path}${queryParams}`;
+}
+
/**
* Runs a fal serverless function identified by its `id`.
* TODO: expand documentation and provide examples
@@ -24,31 +63,32 @@ type RunOptions = {
*/
export async function run (
id: string,
- options?: RunOptions
+ options: RunOptions = {}
): Promise {
- const { credentials, host } = getConfig();
+ const { credentials } = getConfig();
const method = (options.method ?? 'post').toLowerCase();
- const params =
- method === 'get' ? new URLSearchParams(options.input ?? {}).toString() : '';
const userAgent = isBrowser ? {} : { 'User-Agent': getUserAgent() };
- const response = await fetch(
- `${host}/trigger/${credentials.userId}/${id}${params}`,
- {
- method,
- headers: {
- 'X-Fal-Key-Id': credentials.keyId,
- 'X-Fal-Key-Secret': credentials.keySecret,
- Accept: 'application/json',
- 'Content-Type': 'application/json',
- ...userAgent,
- },
- mode: 'cors',
- body:
- method !== 'get' && options.input
- ? JSON.stringify(options.input)
- : null,
- }
- );
+ const response = await fetch(buildUrl(id, options), {
+ method,
+ headers: {
+ 'X-Fal-Key-Id': credentials.keyId,
+ 'X-Fal-Key-Secret': credentials.keySecret,
+ 'Content-Type': 'application/json',
+ ...userAgent,
+ },
+ mode: 'cors',
+ body:
+ method !== 'get' && options.input
+ ? JSON.stringify(options.input)
+ : undefined,
+ });
+
+ const { status, statusText } = response;
+ if (status < 200 || status >= 300) {
+ // TODO better error type so handlers can differentiate
+ throw new Error(statusText);
+ }
+
// TODO move this elsewhere so it can be reused by websocket impl too
const contentType = response.headers.get('Content-Type');
if (contentType?.includes('application/json')) {
diff --git a/libs/client/src/index.ts b/libs/client/src/index.ts
index 2db699a..094aed8 100644
--- a/libs/client/src/index.ts
+++ b/libs/client/src/index.ts
@@ -1,4 +1,4 @@
-export type { Credentials } from './config';
export { config } from './config';
-export type { FunctionExecution, ProgressEvent } from './function';
+export type { Credentials } from './config';
export { run } from './function';
+export type { FunctionExecution, ProgressEvent } from './function';
diff --git a/libs/client/src/utils.spec.ts b/libs/client/src/utils.spec.ts
new file mode 100644
index 0000000..efc37e5
--- /dev/null
+++ b/libs/client/src/utils.spec.ts
@@ -0,0 +1,14 @@
+import { randomUUID } from 'crypto';
+import { isUUIDv4 } from './utils';
+
+describe('The utils test suite', () => {
+ it('should match a valid v4 uuid', () => {
+ const id = randomUUID();
+ expect(isUUIDv4(id)).toBe(true);
+ });
+
+ it('should not match invalid v4 id', () => {
+ const id = 'e726b886-e2c2-11ed-b5ea-0242ac120002';
+ expect(isUUIDv4(id)).toBe(false);
+ });
+});
diff --git a/libs/client/src/utils.ts b/libs/client/src/utils.ts
new file mode 100644
index 0000000..177860a
--- /dev/null
+++ b/libs/client/src/utils.ts
@@ -0,0 +1,8 @@
+export function isUUIDv4(id: string): boolean {
+ return (
+ typeof id === 'string' &&
+ id.length === 36 &&
+ id[14] === '4' &&
+ ['8', '9', 'a', 'b'].includes(id[19])
+ );
+}
diff --git a/package-lock.json b/package-lock.json
index 39f3796..0dc8a11 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3179,6 +3179,126 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/@next/swc-android-arm-eabi": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.1.tgz",
+ "integrity": "sha512-qnFCx1kT3JTWhWve4VkeWuZiyjG0b5T6J2iWuin74lORCupdrNukxkq9Pm+Z7PsatxuwVJMhjUoYz7H4cWzx2A==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-android-arm64": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.1.1.tgz",
+ "integrity": "sha512-eCiZhTzjySubNqUnNkQCjU3Fh+ep3C6b5DCM5FKzsTH/3Gr/4Y7EiaPZKILbvnXmhWtKPIdcY6Zjx51t4VeTfA==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.1.tgz",
+ "integrity": "sha512-9zRJSSIwER5tu9ADDkPw5rIZ+Np44HTXpYMr0rkM656IvssowPxmhK0rTreC1gpUCYwFsRbxarUJnJsTWiutPg==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.1.tgz",
+ "integrity": "sha512-qWr9qEn5nrnlhB0rtjSdR00RRZEtxg4EGvicIipqZWEyayPxhUu6NwKiG8wZiYZCLfJ5KWr66PGSNeDMGlNaiA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-freebsd-x64": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.1.tgz",
+ "integrity": "sha512-UwP4w/NcQ7V/VJEj3tGVszgb4pyUCt3lzJfUhjDMUmQbzG9LDvgiZgAGMYH6L21MoyAATJQPDGiAMWAPKsmumA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm-gnueabihf": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.1.tgz",
+ "integrity": "sha512-CnsxmKHco9sosBs1XcvCXP845Db+Wx1G0qouV5+Gr+HT/ZlDYEWKoHVDgnJXLVEQzq4FmHddBNGbXvgqM1Gfkg==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.1.tgz",
+ "integrity": "sha512-JfDq1eri5Dif+VDpTkONRd083780nsMCOKoFG87wA0sa4xL8LGcXIBAkUGIC1uVy9SMsr2scA9CySLD/i+Oqiw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.1.tgz",
+ "integrity": "sha512-GA67ZbDq2AW0CY07zzGt07M5b5Yaq5qUpFIoW3UFfjOPgb0Sqf3DAW7GtFMK1sF4ROHsRDMGQ9rnT0VM2dVfKA==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
"node_modules/@next/swc-linux-x64-gnu": {
"version": "13.1.1",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.1.tgz",
@@ -3209,6 +3329,51 @@
"node": ">= 10"
}
},
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.1.tgz",
+ "integrity": "sha512-pzUHOGrbgfGgPlOMx9xk3QdPJoRPU+om84hqVoe6u+E0RdwOG0Ho/2UxCgDqmvpUrMab1Deltlt6RqcXFpnigQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-ia32-msvc": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.1.tgz",
+ "integrity": "sha512-WeX8kVS46aobM9a7Xr/kEPcrTyiwJqQv/tbw6nhJ4fH9xNZ+cEcyPoQkwPo570dCOLz3Zo9S2q0E6lJ/EAUOBg==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.1.tgz",
+ "integrity": "sha512-mVF0/3/5QAc5EGVnb8ll31nNvf3BWpPY4pBb84tk+BfQglWLqc5AC9q1Ht/YMWiEgs8ALNKEQ3GQnbY0bJF2Gg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -29665,6 +29830,54 @@
}
}
},
+ "@next/swc-android-arm-eabi": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.1.tgz",
+ "integrity": "sha512-qnFCx1kT3JTWhWve4VkeWuZiyjG0b5T6J2iWuin74lORCupdrNukxkq9Pm+Z7PsatxuwVJMhjUoYz7H4cWzx2A==",
+ "optional": true
+ },
+ "@next/swc-android-arm64": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.1.1.tgz",
+ "integrity": "sha512-eCiZhTzjySubNqUnNkQCjU3Fh+ep3C6b5DCM5FKzsTH/3Gr/4Y7EiaPZKILbvnXmhWtKPIdcY6Zjx51t4VeTfA==",
+ "optional": true
+ },
+ "@next/swc-darwin-arm64": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.1.tgz",
+ "integrity": "sha512-9zRJSSIwER5tu9ADDkPw5rIZ+Np44HTXpYMr0rkM656IvssowPxmhK0rTreC1gpUCYwFsRbxarUJnJsTWiutPg==",
+ "optional": true
+ },
+ "@next/swc-darwin-x64": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.1.tgz",
+ "integrity": "sha512-qWr9qEn5nrnlhB0rtjSdR00RRZEtxg4EGvicIipqZWEyayPxhUu6NwKiG8wZiYZCLfJ5KWr66PGSNeDMGlNaiA==",
+ "optional": true
+ },
+ "@next/swc-freebsd-x64": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.1.tgz",
+ "integrity": "sha512-UwP4w/NcQ7V/VJEj3tGVszgb4pyUCt3lzJfUhjDMUmQbzG9LDvgiZgAGMYH6L21MoyAATJQPDGiAMWAPKsmumA==",
+ "optional": true
+ },
+ "@next/swc-linux-arm-gnueabihf": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.1.tgz",
+ "integrity": "sha512-CnsxmKHco9sosBs1XcvCXP845Db+Wx1G0qouV5+Gr+HT/ZlDYEWKoHVDgnJXLVEQzq4FmHddBNGbXvgqM1Gfkg==",
+ "optional": true
+ },
+ "@next/swc-linux-arm64-gnu": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.1.tgz",
+ "integrity": "sha512-JfDq1eri5Dif+VDpTkONRd083780nsMCOKoFG87wA0sa4xL8LGcXIBAkUGIC1uVy9SMsr2scA9CySLD/i+Oqiw==",
+ "optional": true
+ },
+ "@next/swc-linux-arm64-musl": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.1.tgz",
+ "integrity": "sha512-GA67ZbDq2AW0CY07zzGt07M5b5Yaq5qUpFIoW3UFfjOPgb0Sqf3DAW7GtFMK1sF4ROHsRDMGQ9rnT0VM2dVfKA==",
+ "optional": true
+ },
"@next/swc-linux-x64-gnu": {
"version": "13.1.1",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.1.tgz",
@@ -29677,6 +29890,24 @@
"integrity": "sha512-CM9xnAQNIZ8zf/igbIT/i3xWbQZYaF397H+JroF5VMOCUleElaMdQLL5riJml8wUfPoN3dtfn2s4peSr3azz/g==",
"optional": true
},
+ "@next/swc-win32-arm64-msvc": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.1.tgz",
+ "integrity": "sha512-pzUHOGrbgfGgPlOMx9xk3QdPJoRPU+om84hqVoe6u+E0RdwOG0Ho/2UxCgDqmvpUrMab1Deltlt6RqcXFpnigQ==",
+ "optional": true
+ },
+ "@next/swc-win32-ia32-msvc": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.1.tgz",
+ "integrity": "sha512-WeX8kVS46aobM9a7Xr/kEPcrTyiwJqQv/tbw6nhJ4fH9xNZ+cEcyPoQkwPo570dCOLz3Zo9S2q0E6lJ/EAUOBg==",
+ "optional": true
+ },
+ "@next/swc-win32-x64-msvc": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.1.tgz",
+ "integrity": "sha512-mVF0/3/5QAc5EGVnb8ll31nNvf3BWpPY4pBb84tk+BfQglWLqc5AC9q1Ht/YMWiEgs8ALNKEQ3GQnbY0bJF2Gg==",
+ "optional": true
+ },
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",