convex 1.24.1 → 1.25.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/browser.bundle.js +8 -4
- package/dist/browser.bundle.js.map +2 -2
- package/dist/cjs/browser/simple_client.js +7 -3
- package/dist/cjs/browser/simple_client.js.map +2 -2
- package/dist/cjs/bundler/index.js +5 -2
- package/dist/cjs/bundler/index.js.map +2 -2
- package/dist/cjs/cli/lib/convexImport.js +9 -3
- package/dist/cjs/cli/lib/convexImport.js.map +2 -2
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/server/impl/query_impl.js +6 -0
- package/dist/cjs/server/impl/query_impl.js.map +2 -2
- package/dist/cjs-types/browser/simple_client.d.ts +3 -2
- package/dist/cjs-types/browser/simple_client.d.ts.map +1 -1
- package/dist/cjs-types/bundler/index.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/convexImport.d.ts.map +1 -1
- package/dist/cjs-types/index.d.ts +1 -1
- package/dist/cjs-types/index.d.ts.map +1 -1
- package/dist/cjs-types/server/impl/query_impl.d.ts.map +1 -1
- package/dist/cli.bundle.cjs +15 -6
- package/dist/cli.bundle.cjs.map +2 -2
- package/dist/esm/browser/simple_client.js +7 -3
- package/dist/esm/browser/simple_client.js.map +2 -2
- package/dist/esm/bundler/index.js +5 -2
- package/dist/esm/bundler/index.js.map +2 -2
- package/dist/esm/cli/lib/convexImport.js +9 -3
- package/dist/esm/cli/lib/convexImport.js.map +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/server/impl/query_impl.js +6 -0
- package/dist/esm/server/impl/query_impl.js.map +2 -2
- package/dist/esm-types/browser/simple_client.d.ts +3 -2
- package/dist/esm-types/browser/simple_client.d.ts.map +1 -1
- package/dist/esm-types/bundler/index.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/convexImport.d.ts.map +1 -1
- package/dist/esm-types/index.d.ts +1 -1
- package/dist/esm-types/index.d.ts.map +1 -1
- package/dist/esm-types/server/impl/query_impl.d.ts.map +1 -1
- package/dist/react.bundle.js +1 -1
- package/dist/react.bundle.js.map +1 -1
- package/package.json +3 -3
- package/src/browser/simple_client.ts +7 -3
- package/src/bundler/index.ts +4 -2
- package/src/cli/lib/convexImport.ts +15 -4
- package/src/index.ts +1 -1
- package/src/server/impl/query_impl.ts +7 -0
|
@@ -43,13 +43,13 @@ class ConvexClient {
|
|
|
43
43
|
// A synthetic server event to run callbacks the first time
|
|
44
44
|
__publicField(this, "callNewListenersWithCurrentValuesTimer");
|
|
45
45
|
__publicField(this, "_closed");
|
|
46
|
-
__publicField(this, "
|
|
46
|
+
__publicField(this, "_disabled");
|
|
47
47
|
if (options.skipConvexDeploymentUrlCheck !== true) {
|
|
48
48
|
(0, import_common.validateDeploymentUrl)(address);
|
|
49
49
|
}
|
|
50
50
|
const { disabled, ...baseOptions } = options;
|
|
51
51
|
this._closed = false;
|
|
52
|
-
this.
|
|
52
|
+
this._disabled = !!disabled;
|
|
53
53
|
if (defaultWebSocketConstructor && !("webSocketConstructor" in baseOptions) && typeof WebSocket === "undefined") {
|
|
54
54
|
baseOptions.webSocketConstructor = defaultWebSocketConstructor;
|
|
55
55
|
}
|
|
@@ -75,6 +75,9 @@ class ConvexClient {
|
|
|
75
75
|
if (this._client) return this._client;
|
|
76
76
|
throw new Error("ConvexClient is disabled");
|
|
77
77
|
}
|
|
78
|
+
get disabled() {
|
|
79
|
+
return this._disabled;
|
|
80
|
+
}
|
|
78
81
|
/**
|
|
79
82
|
* Call a callback whenever a new result for a query is received. The callback
|
|
80
83
|
* will run soon after being registered if a result for the query is already
|
|
@@ -174,10 +177,11 @@ class ConvexClient {
|
|
|
174
177
|
* `fetchToken` will be called automatically again if a token expires.
|
|
175
178
|
* `fetchToken` should return `null` if the token cannot be retrieved, for example
|
|
176
179
|
* when the user's rights were permanently revoked.
|
|
177
|
-
* @param fetchToken - an async function returning the JWT
|
|
180
|
+
* @param fetchToken - an async function returning the JWT (typically an OpenID Connect Identity Token)
|
|
178
181
|
* @param onChange - a callback that will be called when the authentication status changes
|
|
179
182
|
*/
|
|
180
183
|
setAuth(fetchToken, onChange) {
|
|
184
|
+
if (this.disabled) return;
|
|
181
185
|
this.client.setAuth(
|
|
182
186
|
fetchToken,
|
|
183
187
|
onChange ?? (() => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/browser/simple_client.ts"],
|
|
4
|
-
"sourcesContent": ["import { validateDeploymentUrl } from \"../common/index.js\";\nimport {\n BaseConvexClient,\n BaseConvexClientOptions,\n QueryToken,\n UserIdentityAttributes,\n} from \"./index.js\";\nimport {\n FunctionArgs,\n FunctionReference,\n FunctionReturnType,\n} from \"../server/index.js\";\nimport { getFunctionName } from \"../server/api.js\";\nimport { AuthTokenFetcher } from \"./sync/authentication_manager.js\";\n\n// In Node.js builds this points to a bundled WebSocket implementation. If no\n// WebSocket implementation is manually specified or globally available,\n// this one is used.\nlet defaultWebSocketConstructor: typeof WebSocket | undefined;\n\n/** internal */\nexport function setDefaultWebSocketConstructor(ws: typeof WebSocket) {\n defaultWebSocketConstructor = ws;\n}\n\nexport type ConvexClientOptions = BaseConvexClientOptions & {\n /**\n * `disabled` makes onUpdate callback registration a no-op and actions,\n * mutations and one-shot queries throw. Setting disabled to true may be\n * useful for server-side rendering, where subscriptions don't make sense.\n */\n disabled?: boolean;\n /**\n * Whether to prompt users in browsers about queued or in-flight mutations.\n * This only works in environments where `window.onbeforeunload` is available.\n *\n * Defaults to true when `window` is defined, otherwise false.\n */\n unsavedChangesWarning?: boolean;\n};\n\n/**\n * Stops callbacks from running.\n *\n * @public\n */\nexport type Unsubscribe<T> = {\n /** Stop calling callback when query results changes. If this is the last listener on this query, stop received updates. */\n (): void;\n /** Stop calling callback when query results changes. If this is the last listener on this query, stop received updates. */\n unsubscribe(): void;\n /** Get the last known value, possibly with local optimistic updates applied. */\n getCurrentValue(): T | undefined;\n /** @internal */\n getQueryLogs(): string[] | undefined;\n};\n\n/**\n * Subscribes to Convex query functions and executes mutations and actions over a WebSocket.\n *\n * Optimistic updates for mutations are not provided for this client.\n * Third party clients may choose to wrap {@link browser.BaseConvexClient} for additional control.\n *\n * ```ts\n * const client = new ConvexClient(\"https://happy-otter-123.convex.cloud\");\n * const unsubscribe = client.onUpdate(api.messages.list, (messages) => {\n * console.log(messages[0].body);\n * });\n * ```\n *\n * @public\n */\nexport class ConvexClient {\n private listeners: Set<QueryInfo>;\n private _client: BaseConvexClient | undefined;\n // A synthetic server event to run callbacks the first time\n private callNewListenersWithCurrentValuesTimer:\n | ReturnType<typeof setTimeout>\n | undefined;\n private _closed: boolean;\n disabled: boolean;\n /**\n * Once closed no registered callbacks will fire again.\n */\n get closed(): boolean {\n return this._closed;\n }\n get client(): BaseConvexClient {\n if (this._client) return this._client;\n throw new Error(\"ConvexClient is disabled\");\n }\n\n /**\n * Construct a client and immediately initiate a WebSocket connection to the passed address.\n *\n * @public\n */\n constructor(address: string, options: ConvexClientOptions = {}) {\n if (options.skipConvexDeploymentUrlCheck !== true) {\n validateDeploymentUrl(address);\n }\n const { disabled, ...baseOptions } = options;\n this._closed = false;\n this.disabled = !!disabled;\n if (\n defaultWebSocketConstructor &&\n !(\"webSocketConstructor\" in baseOptions) &&\n typeof WebSocket === \"undefined\"\n ) {\n baseOptions.webSocketConstructor = defaultWebSocketConstructor;\n }\n if (\n typeof window === \"undefined\" &&\n !(\"unsavedChangesWarning\" in baseOptions)\n ) {\n baseOptions.unsavedChangesWarning = false;\n }\n if (!this.disabled) {\n this._client = new BaseConvexClient(\n address,\n (updatedQueries) => this._transition(updatedQueries),\n baseOptions,\n );\n }\n this.listeners = new Set();\n }\n\n /**\n * Call a callback whenever a new result for a query is received. The callback\n * will run soon after being registered if a result for the query is already\n * in memory.\n *\n * The return value is an {@link Unsubscribe} object which is both a function\n * an an object with properties. Both of the patterns below work with this object:\n *\n *```ts\n * // call the return value as a function\n * const unsubscribe = client.onUpdate(api.messages.list, {}, (messages) => {\n * console.log(messages);\n * });\n * unsubscribe();\n *\n * // unpack the return value into its properties\n * const {\n * getCurrentValue,\n * unsubscribe,\n * } = client.onUpdate(api.messages.list, {}, (messages) => {\n * console.log(messages);\n * });\n *```\n *\n * @param query - A {@link server.FunctionReference} for the public query to run.\n * @param args - The arguments to run the query with.\n * @param callback - Function to call when the query result updates.\n * @param onError - Function to call when the query result updates with an error.\n * If not provided, errors will be thrown instead of calling the callback.\n *\n * @return an {@link Unsubscribe} function to stop calling the onUpdate function.\n */\n onUpdate<Query extends FunctionReference<\"query\">>(\n query: Query,\n args: FunctionArgs<Query>,\n callback: (result: FunctionReturnType<Query>) => unknown,\n onError?: (e: Error) => unknown,\n ): Unsubscribe<Query[\"_returnType\"]> {\n if (this.disabled) {\n const disabledUnsubscribe = (() => {}) as Unsubscribe<\n Query[\"_returnType\"]\n >;\n const unsubscribeProps: RemoveCallSignature<\n Unsubscribe<Query[\"_returnType\"]>\n > = {\n unsubscribe: disabledUnsubscribe,\n getCurrentValue: () => undefined,\n getQueryLogs: () => undefined,\n };\n Object.assign(disabledUnsubscribe, unsubscribeProps);\n return disabledUnsubscribe;\n }\n\n // BaseConvexClient takes care of deduplicating queries subscriptions...\n const { queryToken, unsubscribe } = this.client.subscribe(\n getFunctionName(query),\n args,\n );\n\n // ...but we still need to bookkeep callbacks to actually call them.\n const queryInfo: QueryInfo = {\n queryToken,\n callback,\n onError,\n unsubscribe,\n hasEverRun: false,\n query,\n args,\n };\n this.listeners.add(queryInfo);\n\n // If the callback is registered for a query with a result immediately available\n // schedule a fake transition to call the callback soon instead of waiting for\n // a new server update (which could take seconds or days).\n if (\n this.queryResultReady(queryToken) &&\n this.callNewListenersWithCurrentValuesTimer === undefined\n ) {\n this.callNewListenersWithCurrentValuesTimer = setTimeout(\n () => this.callNewListenersWithCurrentValues(),\n 0,\n );\n }\n\n const unsubscribeProps: RemoveCallSignature<\n Unsubscribe<Query[\"_returnType\"]>\n > = {\n unsubscribe: () => {\n if (this.closed) {\n // all unsubscribes already ran\n return;\n }\n this.listeners.delete(queryInfo);\n unsubscribe();\n },\n getCurrentValue: () => this.client.localQueryResultByToken(queryToken),\n getQueryLogs: () => this.client.localQueryLogs(queryToken),\n };\n const ret = unsubscribeProps.unsubscribe as Unsubscribe<\n Query[\"_returnType\"]\n >;\n Object.assign(ret, unsubscribeProps);\n return ret;\n }\n\n // Run all callbacks that have never been run before if they have a query\n // result available now.\n private callNewListenersWithCurrentValues() {\n this.callNewListenersWithCurrentValuesTimer = undefined;\n this._transition([], true);\n }\n\n private queryResultReady(queryToken: QueryToken): boolean {\n return this.client.hasLocalQueryResultByToken(queryToken);\n }\n\n async close() {\n if (this.disabled) return;\n // prevent pending updates\n this.listeners.clear();\n this._closed = true;\n return this.client.close();\n }\n\n /**\n * Set the authentication token to be used for subsequent queries and mutations.\n * `fetchToken` will be called automatically again if a token expires.\n * `fetchToken` should return `null` if the token cannot be retrieved, for example\n * when the user's rights were permanently revoked.\n * @param fetchToken - an async function returning the JWT-encoded OpenID Connect Identity Token\n * @param onChange - a callback that will be called when the authentication status changes\n */\n setAuth(\n fetchToken: AuthTokenFetcher,\n onChange?: (isAuthenticated: boolean) => void,\n ) {\n this.client.setAuth(\n fetchToken,\n onChange ??\n (() => {\n // Do nothing\n }),\n );\n }\n\n /**\n * @internal\n */\n setAdminAuth(token: string, identity?: UserIdentityAttributes) {\n if (this.closed) {\n throw new Error(\"ConvexClient has already been closed.\");\n }\n if (this.disabled) return;\n this.client.setAdminAuth(token, identity);\n }\n\n /**\n * @internal\n */\n _transition(updatedQueries: QueryToken[], callNewListeners = false) {\n // Deduping subscriptions happens in the BaseConvexClient, so not much to do here.\n\n // Call all callbacks in the order they were registered\n for (const queryInfo of this.listeners) {\n const { callback, queryToken, onError, hasEverRun } = queryInfo;\n if (\n updatedQueries.includes(queryToken) ||\n (callNewListeners &&\n !hasEverRun &&\n this.client.hasLocalQueryResultByToken(queryToken))\n ) {\n queryInfo.hasEverRun = true;\n let newValue;\n try {\n newValue = this.client.localQueryResultByToken(queryToken);\n } catch (error) {\n if (!(error instanceof Error)) throw error;\n if (onError) {\n onError(\n error,\n \"Second argument to onUpdate onError is reserved for later use\",\n );\n } else {\n // Make some noise without unsubscribing or failing to call other callbacks.\n void Promise.reject(error);\n }\n continue;\n }\n callback(\n newValue,\n \"Second argument to onUpdate callback is reserved for later use\",\n );\n }\n }\n }\n\n /**\n * Execute a mutation function.\n *\n * @param mutation - A {@link server.FunctionReference} for the public mutation\n * to run.\n * @param args - An arguments object for the mutation.\n * @param options - A {@link MutationOptions} options object for the mutation.\n * @returns A promise of the mutation's result.\n */\n async mutation<Mutation extends FunctionReference<\"mutation\">>(\n mutation: Mutation,\n args: FunctionArgs<Mutation>,\n ): Promise<Awaited<FunctionReturnType<Mutation>>> {\n if (this.disabled) throw new Error(\"ConvexClient is disabled\");\n return await this.client.mutation(getFunctionName(mutation), args);\n }\n\n /**\n * Execute an action function.\n *\n * @param action - A {@link server.FunctionReference} for the public action\n * to run.\n * @param args - An arguments object for the action.\n * @returns A promise of the action's result.\n */\n async action<Action extends FunctionReference<\"action\">>(\n action: Action,\n args: FunctionArgs<Action>,\n ): Promise<Awaited<FunctionReturnType<Action>>> {\n if (this.disabled) throw new Error(\"ConvexClient is disabled\");\n return await this.client.action(getFunctionName(action), args);\n }\n\n /**\n * Fetch a query result once.\n *\n * @param query - A {@link server.FunctionReference} for the public query\n * to run.\n * @param args - An arguments object for the query.\n * @returns A promise of the query's result.\n */\n async query<Query extends FunctionReference<\"query\">>(\n query: Query,\n args: Query[\"_args\"],\n ): Promise<Awaited<Query[\"_returnType\"]>> {\n if (this.disabled) throw new Error(\"ConvexClient is disabled\");\n const value = this.client.localQueryResult(getFunctionName(query), args) as\n | Awaited<Query[\"_returnType\"]>\n | undefined;\n if (value !== undefined) return Promise.resolve(value);\n\n return new Promise((resolve, reject) => {\n const { unsubscribe } = this.onUpdate(\n query,\n args,\n (value) => {\n unsubscribe();\n resolve(value);\n },\n (e: Error) => {\n unsubscribe();\n reject(e);\n },\n );\n });\n }\n}\n\n// internal information tracked about each registered callback\ntype QueryInfo = {\n callback: (result: any, meta: unknown) => unknown;\n onError: ((e: Error, meta: unknown) => unknown) | undefined;\n unsubscribe: () => void;\n queryToken: QueryToken;\n hasEverRun: boolean;\n // query and args are just here for debugging, the queryToken is authoritative\n query: FunctionReference<\"query\">;\n args: any;\n};\n\ntype RemoveCallSignature<T> = Omit<T, never>;\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAsC;AACtC,mBAKO;AAMP,iBAAgC;AAMhC,IAAI;AAGG,SAAS,+BAA+B,IAAsB;AACnE,gCAA8B;AAChC;AAiDO,MAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,
|
|
4
|
+
"sourcesContent": ["import { validateDeploymentUrl } from \"../common/index.js\";\nimport {\n BaseConvexClient,\n BaseConvexClientOptions,\n QueryToken,\n UserIdentityAttributes,\n} from \"./index.js\";\nimport {\n FunctionArgs,\n FunctionReference,\n FunctionReturnType,\n} from \"../server/index.js\";\nimport { getFunctionName } from \"../server/api.js\";\nimport { AuthTokenFetcher } from \"./sync/authentication_manager.js\";\n\n// In Node.js builds this points to a bundled WebSocket implementation. If no\n// WebSocket implementation is manually specified or globally available,\n// this one is used.\nlet defaultWebSocketConstructor: typeof WebSocket | undefined;\n\n/** internal */\nexport function setDefaultWebSocketConstructor(ws: typeof WebSocket) {\n defaultWebSocketConstructor = ws;\n}\n\nexport type ConvexClientOptions = BaseConvexClientOptions & {\n /**\n * `disabled` makes onUpdate callback registration a no-op and actions,\n * mutations and one-shot queries throw. Setting disabled to true may be\n * useful for server-side rendering, where subscriptions don't make sense.\n */\n disabled?: boolean;\n /**\n * Whether to prompt users in browsers about queued or in-flight mutations.\n * This only works in environments where `window.onbeforeunload` is available.\n *\n * Defaults to true when `window` is defined, otherwise false.\n */\n unsavedChangesWarning?: boolean;\n};\n\n/**\n * Stops callbacks from running.\n *\n * @public\n */\nexport type Unsubscribe<T> = {\n /** Stop calling callback when query results changes. If this is the last listener on this query, stop received updates. */\n (): void;\n /** Stop calling callback when query results changes. If this is the last listener on this query, stop received updates. */\n unsubscribe(): void;\n /** Get the last known value, possibly with local optimistic updates applied. */\n getCurrentValue(): T | undefined;\n /** @internal */\n getQueryLogs(): string[] | undefined;\n};\n\n/**\n * Subscribes to Convex query functions and executes mutations and actions over a WebSocket.\n *\n * Optimistic updates for mutations are not provided for this client.\n * Third party clients may choose to wrap {@link browser.BaseConvexClient} for additional control.\n *\n * ```ts\n * const client = new ConvexClient(\"https://happy-otter-123.convex.cloud\");\n * const unsubscribe = client.onUpdate(api.messages.list, (messages) => {\n * console.log(messages[0].body);\n * });\n * ```\n *\n * @public\n */\nexport class ConvexClient {\n private listeners: Set<QueryInfo>;\n private _client: BaseConvexClient | undefined;\n // A synthetic server event to run callbacks the first time\n private callNewListenersWithCurrentValuesTimer:\n | ReturnType<typeof setTimeout>\n | undefined;\n private _closed: boolean;\n private _disabled: boolean;\n /**\n * Once closed no registered callbacks will fire again.\n */\n get closed(): boolean {\n return this._closed;\n }\n get client(): BaseConvexClient {\n if (this._client) return this._client;\n throw new Error(\"ConvexClient is disabled\");\n }\n get disabled(): boolean {\n return this._disabled;\n }\n\n /**\n * Construct a client and immediately initiate a WebSocket connection to the passed address.\n *\n * @public\n */\n constructor(address: string, options: ConvexClientOptions = {}) {\n if (options.skipConvexDeploymentUrlCheck !== true) {\n validateDeploymentUrl(address);\n }\n const { disabled, ...baseOptions } = options;\n this._closed = false;\n this._disabled = !!disabled;\n if (\n defaultWebSocketConstructor &&\n !(\"webSocketConstructor\" in baseOptions) &&\n typeof WebSocket === \"undefined\"\n ) {\n baseOptions.webSocketConstructor = defaultWebSocketConstructor;\n }\n if (\n typeof window === \"undefined\" &&\n !(\"unsavedChangesWarning\" in baseOptions)\n ) {\n baseOptions.unsavedChangesWarning = false;\n }\n if (!this.disabled) {\n this._client = new BaseConvexClient(\n address,\n (updatedQueries) => this._transition(updatedQueries),\n baseOptions,\n );\n }\n this.listeners = new Set();\n }\n\n /**\n * Call a callback whenever a new result for a query is received. The callback\n * will run soon after being registered if a result for the query is already\n * in memory.\n *\n * The return value is an {@link Unsubscribe} object which is both a function\n * an an object with properties. Both of the patterns below work with this object:\n *\n *```ts\n * // call the return value as a function\n * const unsubscribe = client.onUpdate(api.messages.list, {}, (messages) => {\n * console.log(messages);\n * });\n * unsubscribe();\n *\n * // unpack the return value into its properties\n * const {\n * getCurrentValue,\n * unsubscribe,\n * } = client.onUpdate(api.messages.list, {}, (messages) => {\n * console.log(messages);\n * });\n *```\n *\n * @param query - A {@link server.FunctionReference} for the public query to run.\n * @param args - The arguments to run the query with.\n * @param callback - Function to call when the query result updates.\n * @param onError - Function to call when the query result updates with an error.\n * If not provided, errors will be thrown instead of calling the callback.\n *\n * @return an {@link Unsubscribe} function to stop calling the onUpdate function.\n */\n onUpdate<Query extends FunctionReference<\"query\">>(\n query: Query,\n args: FunctionArgs<Query>,\n callback: (result: FunctionReturnType<Query>) => unknown,\n onError?: (e: Error) => unknown,\n ): Unsubscribe<Query[\"_returnType\"]> {\n if (this.disabled) {\n const disabledUnsubscribe = (() => {}) as Unsubscribe<\n Query[\"_returnType\"]\n >;\n const unsubscribeProps: RemoveCallSignature<\n Unsubscribe<Query[\"_returnType\"]>\n > = {\n unsubscribe: disabledUnsubscribe,\n getCurrentValue: () => undefined,\n getQueryLogs: () => undefined,\n };\n Object.assign(disabledUnsubscribe, unsubscribeProps);\n return disabledUnsubscribe;\n }\n\n // BaseConvexClient takes care of deduplicating queries subscriptions...\n const { queryToken, unsubscribe } = this.client.subscribe(\n getFunctionName(query),\n args,\n );\n\n // ...but we still need to bookkeep callbacks to actually call them.\n const queryInfo: QueryInfo = {\n queryToken,\n callback,\n onError,\n unsubscribe,\n hasEverRun: false,\n query,\n args,\n };\n this.listeners.add(queryInfo);\n\n // If the callback is registered for a query with a result immediately available\n // schedule a fake transition to call the callback soon instead of waiting for\n // a new server update (which could take seconds or days).\n if (\n this.queryResultReady(queryToken) &&\n this.callNewListenersWithCurrentValuesTimer === undefined\n ) {\n this.callNewListenersWithCurrentValuesTimer = setTimeout(\n () => this.callNewListenersWithCurrentValues(),\n 0,\n );\n }\n\n const unsubscribeProps: RemoveCallSignature<\n Unsubscribe<Query[\"_returnType\"]>\n > = {\n unsubscribe: () => {\n if (this.closed) {\n // all unsubscribes already ran\n return;\n }\n this.listeners.delete(queryInfo);\n unsubscribe();\n },\n getCurrentValue: () => this.client.localQueryResultByToken(queryToken),\n getQueryLogs: () => this.client.localQueryLogs(queryToken),\n };\n const ret = unsubscribeProps.unsubscribe as Unsubscribe<\n Query[\"_returnType\"]\n >;\n Object.assign(ret, unsubscribeProps);\n return ret;\n }\n\n // Run all callbacks that have never been run before if they have a query\n // result available now.\n private callNewListenersWithCurrentValues() {\n this.callNewListenersWithCurrentValuesTimer = undefined;\n this._transition([], true);\n }\n\n private queryResultReady(queryToken: QueryToken): boolean {\n return this.client.hasLocalQueryResultByToken(queryToken);\n }\n\n async close() {\n if (this.disabled) return;\n // prevent pending updates\n this.listeners.clear();\n this._closed = true;\n return this.client.close();\n }\n\n /**\n * Set the authentication token to be used for subsequent queries and mutations.\n * `fetchToken` will be called automatically again if a token expires.\n * `fetchToken` should return `null` if the token cannot be retrieved, for example\n * when the user's rights were permanently revoked.\n * @param fetchToken - an async function returning the JWT (typically an OpenID Connect Identity Token)\n * @param onChange - a callback that will be called when the authentication status changes\n */\n setAuth(\n fetchToken: AuthTokenFetcher,\n onChange?: (isAuthenticated: boolean) => void,\n ) {\n if (this.disabled) return;\n this.client.setAuth(\n fetchToken,\n onChange ??\n (() => {\n // Do nothing\n }),\n );\n }\n\n /**\n * @internal\n */\n setAdminAuth(token: string, identity?: UserIdentityAttributes) {\n if (this.closed) {\n throw new Error(\"ConvexClient has already been closed.\");\n }\n if (this.disabled) return;\n this.client.setAdminAuth(token, identity);\n }\n\n /**\n * @internal\n */\n _transition(updatedQueries: QueryToken[], callNewListeners = false) {\n // Deduping subscriptions happens in the BaseConvexClient, so not much to do here.\n\n // Call all callbacks in the order they were registered\n for (const queryInfo of this.listeners) {\n const { callback, queryToken, onError, hasEverRun } = queryInfo;\n if (\n updatedQueries.includes(queryToken) ||\n (callNewListeners &&\n !hasEverRun &&\n this.client.hasLocalQueryResultByToken(queryToken))\n ) {\n queryInfo.hasEverRun = true;\n let newValue;\n try {\n newValue = this.client.localQueryResultByToken(queryToken);\n } catch (error) {\n if (!(error instanceof Error)) throw error;\n if (onError) {\n onError(\n error,\n \"Second argument to onUpdate onError is reserved for later use\",\n );\n } else {\n // Make some noise without unsubscribing or failing to call other callbacks.\n void Promise.reject(error);\n }\n continue;\n }\n callback(\n newValue,\n \"Second argument to onUpdate callback is reserved for later use\",\n );\n }\n }\n }\n\n /**\n * Execute a mutation function.\n *\n * @param mutation - A {@link server.FunctionReference} for the public mutation\n * to run.\n * @param args - An arguments object for the mutation.\n * @param options - A {@link MutationOptions} options object for the mutation.\n * @returns A promise of the mutation's result.\n */\n async mutation<Mutation extends FunctionReference<\"mutation\">>(\n mutation: Mutation,\n args: FunctionArgs<Mutation>,\n ): Promise<Awaited<FunctionReturnType<Mutation>>> {\n if (this.disabled) throw new Error(\"ConvexClient is disabled\");\n return await this.client.mutation(getFunctionName(mutation), args);\n }\n\n /**\n * Execute an action function.\n *\n * @param action - A {@link server.FunctionReference} for the public action\n * to run.\n * @param args - An arguments object for the action.\n * @returns A promise of the action's result.\n */\n async action<Action extends FunctionReference<\"action\">>(\n action: Action,\n args: FunctionArgs<Action>,\n ): Promise<Awaited<FunctionReturnType<Action>>> {\n if (this.disabled) throw new Error(\"ConvexClient is disabled\");\n return await this.client.action(getFunctionName(action), args);\n }\n\n /**\n * Fetch a query result once.\n *\n * @param query - A {@link server.FunctionReference} for the public query\n * to run.\n * @param args - An arguments object for the query.\n * @returns A promise of the query's result.\n */\n async query<Query extends FunctionReference<\"query\">>(\n query: Query,\n args: Query[\"_args\"],\n ): Promise<Awaited<Query[\"_returnType\"]>> {\n if (this.disabled) throw new Error(\"ConvexClient is disabled\");\n const value = this.client.localQueryResult(getFunctionName(query), args) as\n | Awaited<Query[\"_returnType\"]>\n | undefined;\n if (value !== undefined) return Promise.resolve(value);\n\n return new Promise((resolve, reject) => {\n const { unsubscribe } = this.onUpdate(\n query,\n args,\n (value) => {\n unsubscribe();\n resolve(value);\n },\n (e: Error) => {\n unsubscribe();\n reject(e);\n },\n );\n });\n }\n}\n\n// internal information tracked about each registered callback\ntype QueryInfo = {\n callback: (result: any, meta: unknown) => unknown;\n onError: ((e: Error, meta: unknown) => unknown) | undefined;\n unsubscribe: () => void;\n queryToken: QueryToken;\n hasEverRun: boolean;\n // query and args are just here for debugging, the queryToken is authoritative\n query: FunctionReference<\"query\">;\n args: any;\n};\n\ntype RemoveCallSignature<T> = Omit<T, never>;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["unsubscribeProps", "value"]
|
|
7
7
|
}
|
|
@@ -134,8 +134,11 @@ async function doEsbuild(ctx, dir, entryPoints2, generateSourceMaps, platform, c
|
|
|
134
134
|
errorType: "invalid filesystem data",
|
|
135
135
|
// We don't print any error because esbuild already printed
|
|
136
136
|
// all the relevant information.
|
|
137
|
-
printedMessage: recommendUseNode ? `
|
|
138
|
-
|
|
137
|
+
printedMessage: recommendUseNode ? `
|
|
138
|
+
It looks like you are using Node APIs from a file without the "use node" directive.
|
|
139
|
+
Split out actions using Node.js APIs like this into a new file only containing actions that uses "use node" so these actions will run in a Node.js environment.
|
|
140
|
+
For more information see https://docs.convex.dev/functions/runtimes#nodejs-runtime
|
|
141
|
+
` : null
|
|
139
142
|
});
|
|
140
143
|
}
|
|
141
144
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/bundler/index.ts"],
|
|
4
|
-
"sourcesContent": ["import path from \"path\";\nimport chalk from \"chalk\";\nimport esbuild, { BuildFailure } from \"esbuild\";\nimport { parse as parseAST } from \"@babel/parser\";\nimport { Identifier, ImportSpecifier } from \"@babel/types\";\nimport * as Sentry from \"@sentry/node\";\nimport { Filesystem, consistentPathSort } from \"./fs.js\";\nimport { Context, logVerbose, logWarning } from \"./context.js\";\nimport { wasmPlugin } from \"./wasm.js\";\nimport {\n ExternalPackage,\n computeExternalPackages,\n createExternalPlugin,\n findExactVersionAndDependencies,\n} from \"./external.js\";\nexport { nodeFs, RecordingFs } from \"./fs.js\";\nexport type { Filesystem } from \"./fs.js\";\n\nexport const actionsDir = \"actions\";\n\n// Returns a generator of { isDir, path, depth } for all paths\n// within dirPath in some topological order (not including\n// dirPath itself).\nexport function* walkDir(\n fs: Filesystem,\n dirPath: string,\n depth?: number,\n): Generator<{ isDir: boolean; path: string; depth: number }, void, void> {\n depth = depth ?? 0;\n for (const dirEntry of fs.listDir(dirPath).sort(consistentPathSort)) {\n const childPath = path.join(dirPath, dirEntry.name);\n if (dirEntry.isDirectory()) {\n yield { isDir: true, path: childPath, depth };\n yield* walkDir(fs, childPath, depth + 1);\n } else if (dirEntry.isFile()) {\n yield { isDir: false, path: childPath, depth };\n }\n }\n}\n\n// Convex specific module environment.\ntype ModuleEnvironment = \"node\" | \"isolate\";\n\nexport interface Bundle {\n path: string;\n source: string;\n sourceMap?: string;\n environment: ModuleEnvironment;\n}\n\nexport interface BundleHash {\n path: string;\n hash: string;\n environment: ModuleEnvironment;\n}\n\ntype EsBuildResult = esbuild.BuildResult & {\n outputFiles: esbuild.OutputFile[];\n // Set of referenced external modules.\n externalModuleNames: Set<string>;\n // Set of bundled modules.\n bundledModuleNames: Set<string>;\n};\n\nasync function doEsbuild(\n ctx: Context,\n dir: string,\n entryPoints: string[],\n generateSourceMaps: boolean,\n platform: esbuild.Platform,\n chunksFolder: string,\n externalPackages: Map<string, ExternalPackage>,\n extraConditions: string[],\n): Promise<EsBuildResult> {\n const external = createExternalPlugin(ctx, externalPackages);\n try {\n const result = await esbuild.build({\n entryPoints,\n bundle: true,\n platform: platform,\n format: \"esm\",\n target: \"esnext\",\n jsx: \"automatic\",\n outdir: \"out\",\n outbase: dir,\n conditions: [\"convex\", \"module\", ...extraConditions],\n // The wasmPlugin should be last so it doesn't run on external modules.\n plugins: [external.plugin, wasmPlugin],\n write: false,\n sourcemap: generateSourceMaps,\n splitting: true,\n chunkNames: path.join(chunksFolder, \"[hash]\"),\n treeShaking: true,\n minify: false,\n keepNames: true,\n metafile: true,\n });\n\n for (const [relPath, input] of Object.entries(result.metafile!.inputs)) {\n // TODO: esbuild outputs paths prefixed with \"(disabled)\"\" when bundling our internal\n // udf-runtime package. The files do actually exist locally, though.\n if (\n relPath.indexOf(\"(disabled):\") !== -1 ||\n relPath.startsWith(\"wasm-binary:\") ||\n relPath.startsWith(\"wasm-stub:\")\n ) {\n continue;\n }\n const absPath = path.resolve(relPath);\n const st = ctx.fs.stat(absPath);\n if (st.size !== input.bytes) {\n logWarning(\n ctx,\n `Bundled file ${absPath} changed right after esbuild invocation`,\n );\n // Consider this a transient error so we'll try again and hopefully\n // no files change right after esbuild next time.\n return await ctx.crash({\n exitCode: 1,\n errorType: \"transient\",\n printedMessage: null,\n });\n }\n ctx.fs.registerPath(absPath, st);\n }\n return {\n ...result,\n externalModuleNames: external.externalModuleNames,\n bundledModuleNames: external.bundledModuleNames,\n };\n } catch (e: unknown) {\n // esbuild sometimes throws a build error instead of returning a result\n // containing an array of errors. Syntax errors are one of these cases.\n let recommendUseNode = false;\n if (isEsbuildBuildError(e)) {\n for (const error of e.errors) {\n if (error.location) {\n const absPath = path.resolve(error.location.file);\n const st = ctx.fs.stat(absPath);\n ctx.fs.registerPath(absPath, st);\n }\n if (\n platform !== \"node\" &&\n !recommendUseNode &&\n error.notes.some((note) =>\n note.text.includes(\"Are you trying to bundle for node?\"),\n )\n ) {\n recommendUseNode = true;\n }\n }\n }\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n // We don't print any error because esbuild already printed\n // all the relevant information.\n printedMessage: recommendUseNode\n ? `It looks like you are using Node APIs from a file without the \"use node\" directive.\\n` +\n `See https://docs.convex.dev/functions/runtimes#nodejs-runtime`\n : null,\n });\n }\n}\n\nfunction isEsbuildBuildError(e: any): e is BuildFailure {\n return (\n \"errors\" in e &&\n \"warnings\" in e &&\n Array.isArray(e.errors) &&\n Array.isArray(e.warnings)\n );\n}\n\nexport async function bundle(\n ctx: Context,\n dir: string,\n entryPoints: string[],\n generateSourceMaps: boolean,\n platform: esbuild.Platform,\n chunksFolder = \"_deps\",\n externalPackagesAllowList: string[] = [],\n extraConditions: string[] = [],\n): Promise<{\n modules: Bundle[];\n externalDependencies: Map<string, string>;\n bundledModuleNames: Set<string>;\n}> {\n const availableExternalPackages = await computeExternalPackages(\n ctx,\n externalPackagesAllowList,\n );\n const result = await doEsbuild(\n ctx,\n dir,\n entryPoints,\n generateSourceMaps,\n platform,\n chunksFolder,\n availableExternalPackages,\n extraConditions,\n );\n if (result.errors.length) {\n const errorMessage = result.errors\n .map((e) => `esbuild error: ${e.text}`)\n .join(\"\\n\");\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n printedMessage: errorMessage,\n });\n }\n for (const warning of result.warnings) {\n logWarning(ctx, chalk.yellow(`esbuild warning: ${warning.text}`));\n }\n const sourceMaps = new Map();\n const modules: Bundle[] = [];\n const environment = platform === \"node\" ? \"node\" : \"isolate\";\n for (const outputFile of result.outputFiles) {\n const relPath = path.relative(path.normalize(\"out\"), outputFile.path);\n if (path.extname(relPath) === \".map\") {\n sourceMaps.set(relPath, outputFile.text);\n continue;\n }\n const posixRelPath = relPath.split(path.sep).join(path.posix.sep);\n modules.push({ path: posixRelPath, source: outputFile.text, environment });\n }\n for (const module of modules) {\n const sourceMapPath = module.path + \".map\";\n const sourceMap = sourceMaps.get(sourceMapPath);\n if (sourceMap) {\n module.sourceMap = sourceMap;\n }\n }\n\n return {\n modules,\n externalDependencies: await externalPackageVersions(\n ctx,\n availableExternalPackages,\n result.externalModuleNames,\n ),\n bundledModuleNames: result.bundledModuleNames,\n };\n}\n\n// We could return the full list of availableExternalPackages, but this would be\n// installing more packages that we need. Instead, we collect all external\n// dependencies we found during bundling the /convex function, as well as their\n// respective peer and optional dependencies.\nasync function externalPackageVersions(\n ctx: Context,\n availableExternalPackages: Map<string, ExternalPackage>,\n referencedPackages: Set<string>,\n): Promise<Map<string, string>> {\n const versions = new Map<string, string>();\n const referencedPackagesQueue = Array.from(referencedPackages.keys());\n\n for (let i = 0; i < referencedPackagesQueue.length; i++) {\n const moduleName = referencedPackagesQueue[i];\n // This assertion is safe because referencedPackages can only contain\n // packages in availableExternalPackages.\n const modulePath = availableExternalPackages.get(moduleName)!.path;\n // Since we don't support lock files and different install commands yet, we\n // pick up the exact version installed on the local filesystem.\n const { version, peerAndOptionalDependencies } =\n await findExactVersionAndDependencies(ctx, moduleName, modulePath);\n versions.set(moduleName, version);\n\n for (const dependency of peerAndOptionalDependencies) {\n if (\n availableExternalPackages.has(dependency) &&\n !referencedPackages.has(dependency)\n ) {\n referencedPackagesQueue.push(dependency);\n referencedPackages.add(dependency);\n }\n }\n }\n\n return versions;\n}\n\nexport async function bundleSchema(\n ctx: Context,\n dir: string,\n extraConditions: string[],\n) {\n let target = path.resolve(dir, \"schema.ts\");\n if (!ctx.fs.exists(target)) {\n target = path.resolve(dir, \"schema.js\");\n }\n const result = await bundle(\n ctx,\n dir,\n [target],\n true,\n \"browser\",\n undefined,\n extraConditions,\n );\n return result.modules;\n}\n\nexport async function bundleAuthConfig(ctx: Context, dir: string) {\n const authConfigPath = path.resolve(dir, \"auth.config.js\");\n const authConfigTsPath = path.resolve(dir, \"auth.config.ts\");\n if (ctx.fs.exists(authConfigPath) && ctx.fs.exists(authConfigTsPath)) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n printedMessage: `Found both ${authConfigPath} and ${authConfigTsPath}, choose one.`,\n });\n }\n const chosenPath = ctx.fs.exists(authConfigTsPath)\n ? authConfigTsPath\n : authConfigPath;\n if (!ctx.fs.exists(chosenPath)) {\n return [];\n }\n const result = await bundle(ctx, dir, [chosenPath], true, \"browser\");\n return result.modules;\n}\n\nexport async function doesImportConvexHttpRouter(source: string) {\n try {\n const ast = parseAST(source, {\n sourceType: \"module\",\n plugins: [\"typescript\"],\n });\n return ast.program.body.some((node) => {\n if (node.type !== \"ImportDeclaration\") return false;\n return node.specifiers.some((s) => {\n const specifier = s as ImportSpecifier;\n const imported = specifier.imported as Identifier;\n return imported.name === \"httpRouter\";\n });\n });\n } catch {\n return (\n source.match(\n /import\\s*\\{\\s*httpRouter.*\\}\\s*from\\s*\"\\s*convex\\/server\\s*\"/,\n ) !== null\n );\n }\n}\n\nconst ENTRY_POINT_EXTENSIONS = [\n // ESBuild js loader\n \".js\",\n \".mjs\",\n \".cjs\",\n // ESBuild ts loader\n \".ts\",\n \".tsx\",\n \".mts\",\n \".cts\",\n // ESBuild jsx loader\n \".jsx\",\n // ESBuild supports css, text, json, and more but these file types are not\n // allowed to define entry points.\n];\n\nexport async function entryPoints(\n ctx: Context,\n dir: string,\n): Promise<string[]> {\n const entryPoints = [];\n\n for (const { isDir, path: fpath, depth } of walkDir(ctx.fs, dir)) {\n if (isDir) {\n continue;\n }\n const relPath = path.relative(dir, fpath);\n const parsedPath = path.parse(fpath);\n const base = parsedPath.base;\n const extension = parsedPath.ext.toLowerCase();\n\n if (relPath.startsWith(\"_deps\" + path.sep)) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n printedMessage: `The path \"${fpath}\" is within the \"_deps\" directory, which is reserved for dependencies. Please move your code to another directory.`,\n });\n }\n\n if (depth === 0 && base.toLowerCase().startsWith(\"https.\")) {\n const source = ctx.fs.readUtf8File(fpath);\n if (await doesImportConvexHttpRouter(source))\n logWarning(\n ctx,\n chalk.yellow(\n `Found ${fpath}. HTTP action routes will not be imported from this file. Did you mean to include http${extension}?`,\n ),\n );\n Sentry.captureMessage(\n `User code top level directory contains file ${base} which imports httpRouter.`,\n \"warning\",\n );\n }\n\n // This should match isEntryPoint in the convex eslint plugin.\n if (!ENTRY_POINT_EXTENSIONS.some((ext) => relPath.endsWith(ext))) {\n logVerbose(ctx, chalk.yellow(`Skipping non-JS file ${fpath}`));\n } else if (relPath.startsWith(\"_generated\" + path.sep)) {\n logVerbose(ctx, chalk.yellow(`Skipping ${fpath}`));\n } else if (base.startsWith(\".\")) {\n logVerbose(ctx, chalk.yellow(`Skipping dotfile ${fpath}`));\n } else if (base.startsWith(\"#\")) {\n logVerbose(ctx, chalk.yellow(`Skipping likely emacs tempfile ${fpath}`));\n } else if (base === \"schema.ts\" || base === \"schema.js\") {\n logVerbose(ctx, chalk.yellow(`Skipping ${fpath}`));\n } else if ((base.match(/\\./g) || []).length > 1) {\n // `auth.config.ts` and `convex.config.ts` are important not to bundle.\n // `*.test.ts` `*.spec.ts` are common in developer code.\n logVerbose(\n ctx,\n chalk.yellow(`Skipping ${fpath} that contains multiple dots`),\n );\n } else if (relPath.includes(\" \")) {\n logVerbose(\n ctx,\n chalk.yellow(`Skipping ${relPath} because it contains a space`),\n );\n } else {\n logVerbose(ctx, chalk.green(`Preparing ${fpath}`));\n entryPoints.push(fpath);\n }\n }\n\n // If using TypeScript, require that at least one line starts with `export` or `import`,\n // a TypeScript requirement. This prevents confusing type errors from empty .ts files.\n const nonEmptyEntryPoints = entryPoints.filter((fpath) => {\n // This check only makes sense for TypeScript files\n if (!fpath.endsWith(\".ts\") && !fpath.endsWith(\".tsx\")) {\n return true;\n }\n const contents = ctx.fs.readUtf8File(fpath);\n if (/^\\s{0,100}(import|export)/m.test(contents)) {\n return true;\n }\n logVerbose(\n ctx,\n chalk.yellow(\n `Skipping ${fpath} because it has no export or import to make it a valid TypeScript module`,\n ),\n );\n });\n\n return nonEmptyEntryPoints;\n}\n\n// A fallback regex in case we fail to parse the AST.\nexport const useNodeDirectiveRegex = /^\\s*(\"|')use node(\"|');?\\s*$/;\n\nfunction hasUseNodeDirective(ctx: Context, fpath: string): boolean {\n // Do a quick check for the exact string. If it doesn't exist, don't\n // bother parsing.\n const source = ctx.fs.readUtf8File(fpath);\n if (source.indexOf(\"use node\") === -1) {\n return false;\n }\n\n // We parse the AST here to extract the \"use node\" declaration. This is more\n // robust than doing a regex. We only use regex as a fallback.\n try {\n const ast = parseAST(source, {\n // parse in strict mode and allow module declarations\n sourceType: \"module\",\n\n // esbuild supports jsx and typescript by default. Allow the same plugins\n // here too.\n plugins: [\"jsx\", \"typescript\"],\n });\n return ast.program.directives\n .map((d) => d.value.value)\n .includes(\"use node\");\n } catch (error: any) {\n // Given that we have failed to parse, we are most likely going to fail in\n // the esbuild step, which seem to return better formatted error messages.\n // We don't throw here and fallback to regex.\n let lineMatches = false;\n for (const line of source.split(\"\\n\")) {\n if (line.match(useNodeDirectiveRegex)) {\n lineMatches = true;\n break;\n }\n }\n\n // Log that we failed to parse in verbose node if we need this for debugging.\n logVerbose(\n ctx,\n `Failed to parse ${fpath}. Use node is set to ${lineMatches} based on regex. Parse error: ${error.toString()}.`,\n );\n\n return lineMatches;\n }\n}\n\nexport function mustBeIsolate(relPath: string): boolean {\n // Check if the path without extension matches any of the static paths.\n return [\"http\", \"crons\", \"schema\", \"auth.config\"].includes(\n relPath.replace(/\\.[^/.]+$/, \"\"),\n );\n}\n\nasync function determineEnvironment(\n ctx: Context,\n dir: string,\n fpath: string,\n): Promise<ModuleEnvironment> {\n const relPath = path.relative(dir, fpath);\n\n const useNodeDirectiveFound = hasUseNodeDirective(ctx, fpath);\n if (useNodeDirectiveFound) {\n if (mustBeIsolate(relPath)) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n printedMessage: `\"use node\" directive is not allowed for ${relPath}.`,\n });\n }\n return \"node\";\n }\n\n const actionsPrefix = actionsDir + path.sep;\n if (relPath.startsWith(actionsPrefix)) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n printedMessage: `${relPath} is in /actions subfolder but has no \"use node\"; directive. You can now define actions in any folder and indicate they should run in node by adding \"use node\" directive. /actions is a deprecated way to choose Node.js environment, and we require \"use node\" for all files within that folder to avoid unexpected errors during the migration. See https://docs.convex.dev/functions/actions for more details`,\n });\n }\n\n return \"isolate\";\n}\n\nexport async function entryPointsByEnvironment(ctx: Context, dir: string) {\n const isolate = [];\n const node = [];\n for (const entryPoint of await entryPoints(ctx, dir)) {\n const environment = await determineEnvironment(ctx, dir, entryPoint);\n if (environment === \"node\") {\n node.push(entryPoint);\n } else {\n isolate.push(entryPoint);\n }\n }\n\n return { isolate, node };\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import path from \"path\";\nimport chalk from \"chalk\";\nimport esbuild, { BuildFailure } from \"esbuild\";\nimport { parse as parseAST } from \"@babel/parser\";\nimport { Identifier, ImportSpecifier } from \"@babel/types\";\nimport * as Sentry from \"@sentry/node\";\nimport { Filesystem, consistentPathSort } from \"./fs.js\";\nimport { Context, logVerbose, logWarning } from \"./context.js\";\nimport { wasmPlugin } from \"./wasm.js\";\nimport {\n ExternalPackage,\n computeExternalPackages,\n createExternalPlugin,\n findExactVersionAndDependencies,\n} from \"./external.js\";\nexport { nodeFs, RecordingFs } from \"./fs.js\";\nexport type { Filesystem } from \"./fs.js\";\n\nexport const actionsDir = \"actions\";\n\n// Returns a generator of { isDir, path, depth } for all paths\n// within dirPath in some topological order (not including\n// dirPath itself).\nexport function* walkDir(\n fs: Filesystem,\n dirPath: string,\n depth?: number,\n): Generator<{ isDir: boolean; path: string; depth: number }, void, void> {\n depth = depth ?? 0;\n for (const dirEntry of fs.listDir(dirPath).sort(consistentPathSort)) {\n const childPath = path.join(dirPath, dirEntry.name);\n if (dirEntry.isDirectory()) {\n yield { isDir: true, path: childPath, depth };\n yield* walkDir(fs, childPath, depth + 1);\n } else if (dirEntry.isFile()) {\n yield { isDir: false, path: childPath, depth };\n }\n }\n}\n\n// Convex specific module environment.\ntype ModuleEnvironment = \"node\" | \"isolate\";\n\nexport interface Bundle {\n path: string;\n source: string;\n sourceMap?: string;\n environment: ModuleEnvironment;\n}\n\nexport interface BundleHash {\n path: string;\n hash: string;\n environment: ModuleEnvironment;\n}\n\ntype EsBuildResult = esbuild.BuildResult & {\n outputFiles: esbuild.OutputFile[];\n // Set of referenced external modules.\n externalModuleNames: Set<string>;\n // Set of bundled modules.\n bundledModuleNames: Set<string>;\n};\n\nasync function doEsbuild(\n ctx: Context,\n dir: string,\n entryPoints: string[],\n generateSourceMaps: boolean,\n platform: esbuild.Platform,\n chunksFolder: string,\n externalPackages: Map<string, ExternalPackage>,\n extraConditions: string[],\n): Promise<EsBuildResult> {\n const external = createExternalPlugin(ctx, externalPackages);\n try {\n const result = await esbuild.build({\n entryPoints,\n bundle: true,\n platform: platform,\n format: \"esm\",\n target: \"esnext\",\n jsx: \"automatic\",\n outdir: \"out\",\n outbase: dir,\n conditions: [\"convex\", \"module\", ...extraConditions],\n // The wasmPlugin should be last so it doesn't run on external modules.\n plugins: [external.plugin, wasmPlugin],\n write: false,\n sourcemap: generateSourceMaps,\n splitting: true,\n chunkNames: path.join(chunksFolder, \"[hash]\"),\n treeShaking: true,\n minify: false,\n keepNames: true,\n metafile: true,\n });\n\n for (const [relPath, input] of Object.entries(result.metafile!.inputs)) {\n // TODO: esbuild outputs paths prefixed with \"(disabled)\"\" when bundling our internal\n // udf-runtime package. The files do actually exist locally, though.\n if (\n relPath.indexOf(\"(disabled):\") !== -1 ||\n relPath.startsWith(\"wasm-binary:\") ||\n relPath.startsWith(\"wasm-stub:\")\n ) {\n continue;\n }\n const absPath = path.resolve(relPath);\n const st = ctx.fs.stat(absPath);\n if (st.size !== input.bytes) {\n logWarning(\n ctx,\n `Bundled file ${absPath} changed right after esbuild invocation`,\n );\n // Consider this a transient error so we'll try again and hopefully\n // no files change right after esbuild next time.\n return await ctx.crash({\n exitCode: 1,\n errorType: \"transient\",\n printedMessage: null,\n });\n }\n ctx.fs.registerPath(absPath, st);\n }\n return {\n ...result,\n externalModuleNames: external.externalModuleNames,\n bundledModuleNames: external.bundledModuleNames,\n };\n } catch (e: unknown) {\n // esbuild sometimes throws a build error instead of returning a result\n // containing an array of errors. Syntax errors are one of these cases.\n let recommendUseNode = false;\n if (isEsbuildBuildError(e)) {\n for (const error of e.errors) {\n if (error.location) {\n const absPath = path.resolve(error.location.file);\n const st = ctx.fs.stat(absPath);\n ctx.fs.registerPath(absPath, st);\n }\n if (\n platform !== \"node\" &&\n !recommendUseNode &&\n error.notes.some((note) =>\n note.text.includes(\"Are you trying to bundle for node?\"),\n )\n ) {\n recommendUseNode = true;\n }\n }\n }\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n // We don't print any error because esbuild already printed\n // all the relevant information.\n printedMessage: recommendUseNode\n ? `\\nIt looks like you are using Node APIs from a file without the \"use node\" directive.\\n` +\n `Split out actions using Node.js APIs like this into a new file only containing actions that uses \"use node\" ` +\n `so these actions will run in a Node.js environment.\\n` +\n `For more information see https://docs.convex.dev/functions/runtimes#nodejs-runtime\\n`\n : null,\n });\n }\n}\n\nfunction isEsbuildBuildError(e: any): e is BuildFailure {\n return (\n \"errors\" in e &&\n \"warnings\" in e &&\n Array.isArray(e.errors) &&\n Array.isArray(e.warnings)\n );\n}\n\nexport async function bundle(\n ctx: Context,\n dir: string,\n entryPoints: string[],\n generateSourceMaps: boolean,\n platform: esbuild.Platform,\n chunksFolder = \"_deps\",\n externalPackagesAllowList: string[] = [],\n extraConditions: string[] = [],\n): Promise<{\n modules: Bundle[];\n externalDependencies: Map<string, string>;\n bundledModuleNames: Set<string>;\n}> {\n const availableExternalPackages = await computeExternalPackages(\n ctx,\n externalPackagesAllowList,\n );\n const result = await doEsbuild(\n ctx,\n dir,\n entryPoints,\n generateSourceMaps,\n platform,\n chunksFolder,\n availableExternalPackages,\n extraConditions,\n );\n if (result.errors.length) {\n const errorMessage = result.errors\n .map((e) => `esbuild error: ${e.text}`)\n .join(\"\\n\");\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n printedMessage: errorMessage,\n });\n }\n for (const warning of result.warnings) {\n logWarning(ctx, chalk.yellow(`esbuild warning: ${warning.text}`));\n }\n const sourceMaps = new Map();\n const modules: Bundle[] = [];\n const environment = platform === \"node\" ? \"node\" : \"isolate\";\n for (const outputFile of result.outputFiles) {\n const relPath = path.relative(path.normalize(\"out\"), outputFile.path);\n if (path.extname(relPath) === \".map\") {\n sourceMaps.set(relPath, outputFile.text);\n continue;\n }\n const posixRelPath = relPath.split(path.sep).join(path.posix.sep);\n modules.push({ path: posixRelPath, source: outputFile.text, environment });\n }\n for (const module of modules) {\n const sourceMapPath = module.path + \".map\";\n const sourceMap = sourceMaps.get(sourceMapPath);\n if (sourceMap) {\n module.sourceMap = sourceMap;\n }\n }\n\n return {\n modules,\n externalDependencies: await externalPackageVersions(\n ctx,\n availableExternalPackages,\n result.externalModuleNames,\n ),\n bundledModuleNames: result.bundledModuleNames,\n };\n}\n\n// We could return the full list of availableExternalPackages, but this would be\n// installing more packages that we need. Instead, we collect all external\n// dependencies we found during bundling the /convex function, as well as their\n// respective peer and optional dependencies.\nasync function externalPackageVersions(\n ctx: Context,\n availableExternalPackages: Map<string, ExternalPackage>,\n referencedPackages: Set<string>,\n): Promise<Map<string, string>> {\n const versions = new Map<string, string>();\n const referencedPackagesQueue = Array.from(referencedPackages.keys());\n\n for (let i = 0; i < referencedPackagesQueue.length; i++) {\n const moduleName = referencedPackagesQueue[i];\n // This assertion is safe because referencedPackages can only contain\n // packages in availableExternalPackages.\n const modulePath = availableExternalPackages.get(moduleName)!.path;\n // Since we don't support lock files and different install commands yet, we\n // pick up the exact version installed on the local filesystem.\n const { version, peerAndOptionalDependencies } =\n await findExactVersionAndDependencies(ctx, moduleName, modulePath);\n versions.set(moduleName, version);\n\n for (const dependency of peerAndOptionalDependencies) {\n if (\n availableExternalPackages.has(dependency) &&\n !referencedPackages.has(dependency)\n ) {\n referencedPackagesQueue.push(dependency);\n referencedPackages.add(dependency);\n }\n }\n }\n\n return versions;\n}\n\nexport async function bundleSchema(\n ctx: Context,\n dir: string,\n extraConditions: string[],\n) {\n let target = path.resolve(dir, \"schema.ts\");\n if (!ctx.fs.exists(target)) {\n target = path.resolve(dir, \"schema.js\");\n }\n const result = await bundle(\n ctx,\n dir,\n [target],\n true,\n \"browser\",\n undefined,\n extraConditions,\n );\n return result.modules;\n}\n\nexport async function bundleAuthConfig(ctx: Context, dir: string) {\n const authConfigPath = path.resolve(dir, \"auth.config.js\");\n const authConfigTsPath = path.resolve(dir, \"auth.config.ts\");\n if (ctx.fs.exists(authConfigPath) && ctx.fs.exists(authConfigTsPath)) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n printedMessage: `Found both ${authConfigPath} and ${authConfigTsPath}, choose one.`,\n });\n }\n const chosenPath = ctx.fs.exists(authConfigTsPath)\n ? authConfigTsPath\n : authConfigPath;\n if (!ctx.fs.exists(chosenPath)) {\n return [];\n }\n const result = await bundle(ctx, dir, [chosenPath], true, \"browser\");\n return result.modules;\n}\n\nexport async function doesImportConvexHttpRouter(source: string) {\n try {\n const ast = parseAST(source, {\n sourceType: \"module\",\n plugins: [\"typescript\"],\n });\n return ast.program.body.some((node) => {\n if (node.type !== \"ImportDeclaration\") return false;\n return node.specifiers.some((s) => {\n const specifier = s as ImportSpecifier;\n const imported = specifier.imported as Identifier;\n return imported.name === \"httpRouter\";\n });\n });\n } catch {\n return (\n source.match(\n /import\\s*\\{\\s*httpRouter.*\\}\\s*from\\s*\"\\s*convex\\/server\\s*\"/,\n ) !== null\n );\n }\n}\n\nconst ENTRY_POINT_EXTENSIONS = [\n // ESBuild js loader\n \".js\",\n \".mjs\",\n \".cjs\",\n // ESBuild ts loader\n \".ts\",\n \".tsx\",\n \".mts\",\n \".cts\",\n // ESBuild jsx loader\n \".jsx\",\n // ESBuild supports css, text, json, and more but these file types are not\n // allowed to define entry points.\n];\n\nexport async function entryPoints(\n ctx: Context,\n dir: string,\n): Promise<string[]> {\n const entryPoints = [];\n\n for (const { isDir, path: fpath, depth } of walkDir(ctx.fs, dir)) {\n if (isDir) {\n continue;\n }\n const relPath = path.relative(dir, fpath);\n const parsedPath = path.parse(fpath);\n const base = parsedPath.base;\n const extension = parsedPath.ext.toLowerCase();\n\n if (relPath.startsWith(\"_deps\" + path.sep)) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n printedMessage: `The path \"${fpath}\" is within the \"_deps\" directory, which is reserved for dependencies. Please move your code to another directory.`,\n });\n }\n\n if (depth === 0 && base.toLowerCase().startsWith(\"https.\")) {\n const source = ctx.fs.readUtf8File(fpath);\n if (await doesImportConvexHttpRouter(source))\n logWarning(\n ctx,\n chalk.yellow(\n `Found ${fpath}. HTTP action routes will not be imported from this file. Did you mean to include http${extension}?`,\n ),\n );\n Sentry.captureMessage(\n `User code top level directory contains file ${base} which imports httpRouter.`,\n \"warning\",\n );\n }\n\n // This should match isEntryPoint in the convex eslint plugin.\n if (!ENTRY_POINT_EXTENSIONS.some((ext) => relPath.endsWith(ext))) {\n logVerbose(ctx, chalk.yellow(`Skipping non-JS file ${fpath}`));\n } else if (relPath.startsWith(\"_generated\" + path.sep)) {\n logVerbose(ctx, chalk.yellow(`Skipping ${fpath}`));\n } else if (base.startsWith(\".\")) {\n logVerbose(ctx, chalk.yellow(`Skipping dotfile ${fpath}`));\n } else if (base.startsWith(\"#\")) {\n logVerbose(ctx, chalk.yellow(`Skipping likely emacs tempfile ${fpath}`));\n } else if (base === \"schema.ts\" || base === \"schema.js\") {\n logVerbose(ctx, chalk.yellow(`Skipping ${fpath}`));\n } else if ((base.match(/\\./g) || []).length > 1) {\n // `auth.config.ts` and `convex.config.ts` are important not to bundle.\n // `*.test.ts` `*.spec.ts` are common in developer code.\n logVerbose(\n ctx,\n chalk.yellow(`Skipping ${fpath} that contains multiple dots`),\n );\n } else if (relPath.includes(\" \")) {\n logVerbose(\n ctx,\n chalk.yellow(`Skipping ${relPath} because it contains a space`),\n );\n } else {\n logVerbose(ctx, chalk.green(`Preparing ${fpath}`));\n entryPoints.push(fpath);\n }\n }\n\n // If using TypeScript, require that at least one line starts with `export` or `import`,\n // a TypeScript requirement. This prevents confusing type errors from empty .ts files.\n const nonEmptyEntryPoints = entryPoints.filter((fpath) => {\n // This check only makes sense for TypeScript files\n if (!fpath.endsWith(\".ts\") && !fpath.endsWith(\".tsx\")) {\n return true;\n }\n const contents = ctx.fs.readUtf8File(fpath);\n if (/^\\s{0,100}(import|export)/m.test(contents)) {\n return true;\n }\n logVerbose(\n ctx,\n chalk.yellow(\n `Skipping ${fpath} because it has no export or import to make it a valid TypeScript module`,\n ),\n );\n });\n\n return nonEmptyEntryPoints;\n}\n\n// A fallback regex in case we fail to parse the AST.\nexport const useNodeDirectiveRegex = /^\\s*(\"|')use node(\"|');?\\s*$/;\n\nfunction hasUseNodeDirective(ctx: Context, fpath: string): boolean {\n // Do a quick check for the exact string. If it doesn't exist, don't\n // bother parsing.\n const source = ctx.fs.readUtf8File(fpath);\n if (source.indexOf(\"use node\") === -1) {\n return false;\n }\n\n // We parse the AST here to extract the \"use node\" declaration. This is more\n // robust than doing a regex. We only use regex as a fallback.\n try {\n const ast = parseAST(source, {\n // parse in strict mode and allow module declarations\n sourceType: \"module\",\n\n // esbuild supports jsx and typescript by default. Allow the same plugins\n // here too.\n plugins: [\"jsx\", \"typescript\"],\n });\n return ast.program.directives\n .map((d) => d.value.value)\n .includes(\"use node\");\n } catch (error: any) {\n // Given that we have failed to parse, we are most likely going to fail in\n // the esbuild step, which seem to return better formatted error messages.\n // We don't throw here and fallback to regex.\n let lineMatches = false;\n for (const line of source.split(\"\\n\")) {\n if (line.match(useNodeDirectiveRegex)) {\n lineMatches = true;\n break;\n }\n }\n\n // Log that we failed to parse in verbose node if we need this for debugging.\n logVerbose(\n ctx,\n `Failed to parse ${fpath}. Use node is set to ${lineMatches} based on regex. Parse error: ${error.toString()}.`,\n );\n\n return lineMatches;\n }\n}\n\nexport function mustBeIsolate(relPath: string): boolean {\n // Check if the path without extension matches any of the static paths.\n return [\"http\", \"crons\", \"schema\", \"auth.config\"].includes(\n relPath.replace(/\\.[^/.]+$/, \"\"),\n );\n}\n\nasync function determineEnvironment(\n ctx: Context,\n dir: string,\n fpath: string,\n): Promise<ModuleEnvironment> {\n const relPath = path.relative(dir, fpath);\n\n const useNodeDirectiveFound = hasUseNodeDirective(ctx, fpath);\n if (useNodeDirectiveFound) {\n if (mustBeIsolate(relPath)) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n printedMessage: `\"use node\" directive is not allowed for ${relPath}.`,\n });\n }\n return \"node\";\n }\n\n const actionsPrefix = actionsDir + path.sep;\n if (relPath.startsWith(actionsPrefix)) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n printedMessage: `${relPath} is in /actions subfolder but has no \"use node\"; directive. You can now define actions in any folder and indicate they should run in node by adding \"use node\" directive. /actions is a deprecated way to choose Node.js environment, and we require \"use node\" for all files within that folder to avoid unexpected errors during the migration. See https://docs.convex.dev/functions/actions for more details`,\n });\n }\n\n return \"isolate\";\n}\n\nexport async function entryPointsByEnvironment(ctx: Context, dir: string) {\n const isolate = [];\n const node = [];\n for (const entryPoint of await entryPoints(ctx, dir)) {\n const environment = await determineEnvironment(ctx, dir, entryPoint);\n if (environment === \"node\") {\n node.push(entryPoint);\n } else {\n isolate.push(entryPoint);\n }\n }\n\n return { isolate, node };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_fs", "path", "entryPoints", "esbuild", "chalk", "module", "parseAST"]
|
|
7
7
|
}
|
|
@@ -42,7 +42,8 @@ var import_run = require("./run.js");
|
|
|
42
42
|
var import_http_client = require("../../browser/http_client.js");
|
|
43
43
|
var import_server = require("../../server/index.js");
|
|
44
44
|
var import_prompts = require("./utils/prompts.js");
|
|
45
|
-
const
|
|
45
|
+
const DEFAULT_CHUNK_SIZE = 5 * 1024 * 1024;
|
|
46
|
+
const ENV_CHUNK_SIZE = process.env.CONVEX_IMPORT_CHUNK_SIZE ? parseInt(process.env.CONVEX_IMPORT_CHUNK_SIZE, 10) : void 0;
|
|
46
47
|
async function importIntoDeployment(ctx, filePath, options) {
|
|
47
48
|
if (!ctx.fs.exists(filePath)) {
|
|
48
49
|
return await ctx.crash({
|
|
@@ -335,10 +336,15 @@ async function uploadForImport(ctx, args) {
|
|
|
335
336
|
deploymentUrl,
|
|
336
337
|
adminKey
|
|
337
338
|
});
|
|
339
|
+
const fileStats = ctx.fs.stat(filePath);
|
|
340
|
+
const minChunkSize = Math.ceil(fileStats.size / 9999);
|
|
341
|
+
let chunkSize = ENV_CHUNK_SIZE ?? DEFAULT_CHUNK_SIZE;
|
|
342
|
+
if (chunkSize < minChunkSize) {
|
|
343
|
+
chunkSize = minChunkSize;
|
|
344
|
+
}
|
|
338
345
|
const data = ctx.fs.createReadStream(filePath, {
|
|
339
|
-
highWaterMark:
|
|
346
|
+
highWaterMark: chunkSize
|
|
340
347
|
});
|
|
341
|
-
const fileStats = ctx.fs.stat(filePath);
|
|
342
348
|
(0, import_context.showSpinner)(ctx, `Importing ${filePath} (${(0, import_utils.formatSize)(fileStats.size)})`);
|
|
343
349
|
let importId;
|
|
344
350
|
try {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/cli/lib/convexImport.ts"],
|
|
4
|
-
"sourcesContent": ["import chalk from \"chalk\";\nimport {\n formatSize,\n waitUntilCalled,\n deploymentFetch,\n logAndHandleFetchError,\n} from \"./utils/utils.js\";\nimport {\n logFailure,\n Context,\n showSpinner,\n logFinishedStep,\n logWarning,\n logMessage,\n stopSpinner,\n changeSpinner,\n} from \"../../bundler/context.js\";\nimport path from \"path\";\nimport { subscribe } from \"./run.js\";\nimport { ConvexHttpClient } from \"../../browser/http_client.js\";\nimport { makeFunctionReference } from \"../../server/index.js\";\nimport { promptYesNo } from \"./utils/prompts.js\";\n\n// Backend has minimum chunk size of 5MiB except for the last chunk,\n// so we use 5MiB as highWaterMark which makes fs.ReadStream[asyncIterator]\n// output 5MiB chunks before the last one.\nconst CHUNK_SIZE = 5 * 1024 * 1024;\n\nexport async function importIntoDeployment(\n ctx: Context,\n filePath: string,\n options: {\n deploymentUrl: string;\n adminKey: string;\n deploymentNotice: string;\n snapshotImportDashboardLink: string | undefined;\n table?: string;\n format?: \"csv\" | \"jsonLines\" | \"jsonArray\" | \"zip\";\n replace?: boolean;\n append?: boolean;\n replaceAll?: boolean;\n yes?: boolean;\n component?: string;\n },\n) {\n if (!ctx.fs.exists(filePath)) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n printedMessage: `Error: Path ${chalk.bold(filePath)} does not exist.`,\n });\n }\n\n const format = await determineFormat(ctx, filePath, options.format ?? null);\n const tableName = options.table ?? null;\n if (tableName === null) {\n if (format !== \"zip\") {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Error: The \\`--table\\` option is required for format ${format}`,\n });\n }\n } else {\n if (format === \"zip\") {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Error: The \\`--table\\` option is not allowed for format ${format}`,\n });\n }\n }\n\n const convexClient = new ConvexHttpClient(options.deploymentUrl);\n convexClient.setAdminAuth(options.adminKey);\n const existingImports = await convexClient.query(\n makeFunctionReference<\"query\", Record<string, never>, Array<unknown>>(\n \"_system/cli/queryImport:list\",\n ),\n {},\n );\n const ongoingImports = existingImports.filter(\n (i) => (i as any).state.state === \"in_progress\",\n );\n if (ongoingImports.length > 0) {\n await askToConfirmImportWithExistingImports(\n ctx,\n options.snapshotImportDashboardLink,\n options.yes,\n );\n }\n\n const fileStats = ctx.fs.stat(filePath);\n showSpinner(ctx, `Importing ${filePath} (${formatSize(fileStats.size)})`);\n\n let mode = \"requireEmpty\";\n if (options.append) {\n mode = \"append\";\n } else if (options.replace) {\n mode = \"replace\";\n } else if (options.replaceAll) {\n mode = \"replaceAll\";\n }\n const importArgs = {\n tableName: tableName === null ? undefined : tableName,\n componentPath: options.component,\n mode,\n format,\n };\n const tableNotice = tableName ? ` to table \"${chalk.bold(tableName)}\"` : \"\";\n const onFailure = async () => {\n logFailure(\n ctx,\n `Importing data from \"${chalk.bold(\n filePath,\n )}\"${tableNotice}${options.deploymentNotice} failed`,\n );\n };\n const importId = await uploadForImport(ctx, {\n deploymentUrl: options.deploymentUrl,\n adminKey: options.adminKey,\n filePath,\n importArgs,\n onImportFailed: onFailure,\n });\n changeSpinner(ctx, \"Parsing uploaded data\");\n const onProgress = (\n ctx: Context,\n state: InProgressImportState,\n checkpointCount: number,\n ) => {\n stopSpinner(ctx);\n while ((state.checkpoint_messages?.length ?? 0) > checkpointCount) {\n logFinishedStep(ctx, state.checkpoint_messages![checkpointCount]);\n checkpointCount += 1;\n }\n showSpinner(ctx, state.progress_message ?? \"Importing\");\n return checkpointCount;\n };\n while (true) {\n const snapshotImportState = await waitForStableImportState(ctx, {\n importId,\n deploymentUrl: options.deploymentUrl,\n adminKey: options.adminKey,\n onProgress,\n });\n switch (snapshotImportState.state) {\n case \"completed\":\n logFinishedStep(\n ctx,\n `Added ${snapshotImportState.num_rows_written} documents${tableNotice}${options.deploymentNotice}.`,\n );\n return;\n case \"failed\":\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Importing data from \"${chalk.bold(\n filePath,\n )}\"${tableNotice}${options.deploymentNotice} failed\\n\\n${chalk.red(snapshotImportState.error_message)}`,\n });\n case \"waiting_for_confirmation\": {\n // Clear spinner state so we can log and prompt without clobbering lines.\n stopSpinner(ctx);\n await askToConfirmImport(\n ctx,\n snapshotImportState.message_to_confirm,\n snapshotImportState.require_manual_confirmation,\n options.yes,\n );\n showSpinner(ctx, `Importing`);\n await confirmImport(ctx, {\n importId,\n adminKey: options.adminKey,\n deploymentUrl: options.deploymentUrl,\n onError: async () => {\n logFailure(\n ctx,\n `Importing data from \"${chalk.bold(\n filePath,\n )}\"${tableNotice}${options.deploymentNotice} failed`,\n );\n },\n });\n // Now we have kicked off the rest of the import, go around the loop again.\n break;\n }\n case \"uploaded\": {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Import canceled while parsing uploaded file`,\n });\n }\n case \"in_progress\": {\n const visitDashboardLink = options.snapshotImportDashboardLink\n ? ` Visit ${options.snapshotImportDashboardLink} to monitor its progress.`\n : \"\";\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `WARNING: Import is continuing to run on the server.${visitDashboardLink}`,\n });\n }\n default: {\n const _: never = snapshotImportState;\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `unknown error: unexpected state ${snapshotImportState as any}`,\n errForSentry: `unexpected snapshot import state ${(snapshotImportState as any).state}`,\n });\n }\n }\n }\n}\n\nasync function askToConfirmImport(\n ctx: Context,\n messageToConfirm: string | undefined,\n requireManualConfirmation: boolean | undefined,\n yes: boolean | undefined,\n) {\n if (!messageToConfirm?.length) {\n return;\n }\n logMessage(ctx, messageToConfirm);\n if (requireManualConfirmation !== false && !yes) {\n const confirmed = await promptYesNo(ctx, {\n message: \"Perform import?\",\n default: true,\n });\n if (!confirmed) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Import canceled\",\n });\n }\n }\n}\n\nasync function askToConfirmImportWithExistingImports(\n ctx: Context,\n snapshotImportDashboardLink: string | undefined,\n yes: boolean | undefined,\n) {\n const atDashboardLink = snapshotImportDashboardLink\n ? ` You can view its progress at ${snapshotImportDashboardLink}.`\n : \"\";\n logMessage(\n ctx,\n `There is already a snapshot import in progress.${atDashboardLink}`,\n );\n if (yes) {\n return;\n }\n const confirmed = await promptYesNo(ctx, {\n message: \"Start another import?\",\n default: true,\n });\n if (!confirmed) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Import canceled\",\n });\n }\n}\n\ntype InProgressImportState = {\n state: \"in_progress\";\n progress_message?: string | undefined;\n checkpoint_messages?: string[] | undefined;\n};\n\ntype SnapshotImportState =\n | { state: \"uploaded\" }\n | {\n state: \"waiting_for_confirmation\";\n message_to_confirm?: string;\n require_manual_confirmation?: boolean;\n }\n | InProgressImportState\n | { state: \"completed\"; num_rows_written: bigint }\n | { state: \"failed\"; error_message: string };\n\nexport async function waitForStableImportState(\n ctx: Context,\n args: {\n importId: string;\n deploymentUrl: string;\n adminKey: string;\n onProgress: (\n ctx: Context,\n state: InProgressImportState,\n checkpointCount: number,\n ) => number;\n },\n): Promise<SnapshotImportState> {\n const { importId, deploymentUrl, adminKey, onProgress } = args;\n const [donePromise, onDone] = waitUntilCalled();\n let snapshotImportState: SnapshotImportState;\n let checkpointCount = 0;\n await subscribe(ctx, {\n deploymentUrl,\n adminKey,\n parsedFunctionName: \"_system/cli/queryImport\",\n parsedFunctionArgs: { importId },\n componentPath: undefined,\n until: donePromise,\n callbacks: {\n onChange: (value: any) => {\n snapshotImportState = value.state;\n switch (snapshotImportState.state) {\n case \"waiting_for_confirmation\":\n case \"completed\":\n case \"failed\":\n onDone();\n break;\n case \"uploaded\":\n // Not a stable state. Ignore while the server continues working.\n return;\n case \"in_progress\":\n // Not a stable state. Ignore while the server continues working.\n checkpointCount = onProgress(\n ctx,\n snapshotImportState,\n checkpointCount,\n );\n return;\n }\n },\n },\n });\n return snapshotImportState!;\n}\n\nasync function determineFormat(\n ctx: Context,\n filePath: string,\n format: string | null,\n) {\n const fileExtension = path.extname(filePath);\n if (fileExtension !== \"\") {\n const formatToExtension: Record<string, string> = {\n csv: \".csv\",\n jsonLines: \".jsonl\",\n jsonArray: \".json\",\n zip: \".zip\",\n };\n const extensionToFormat = Object.fromEntries(\n Object.entries(formatToExtension).map((a) => a.reverse()),\n );\n if (format !== null && fileExtension !== formatToExtension[format]) {\n logWarning(\n ctx,\n chalk.yellow(\n `Warning: Extension of file ${filePath} (${fileExtension}) does not match specified format: ${format} (${formatToExtension[format]}).`,\n ),\n );\n }\n format ??= extensionToFormat[fileExtension] ?? null;\n }\n if (format === null) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"No input file format inferred by the filename extension or specified. Specify your input file's format using the `--format` flag.\",\n });\n }\n return format;\n}\n\nexport async function confirmImport(\n ctx: Context,\n args: {\n importId: string;\n adminKey: string;\n deploymentUrl: string;\n onError: (e: any) => Promise<void>;\n },\n) {\n const { importId, adminKey, deploymentUrl } = args;\n const fetch = deploymentFetch(ctx, {\n deploymentUrl,\n adminKey,\n });\n const performUrl = `/api/perform_import`;\n try {\n await fetch(performUrl, {\n method: \"POST\",\n body: JSON.stringify({ importId }),\n });\n } catch (e) {\n await args.onError(e);\n return await logAndHandleFetchError(ctx, e);\n }\n}\n\nexport async function uploadForImport(\n ctx: Context,\n args: {\n deploymentUrl: string;\n adminKey: string;\n filePath: string;\n importArgs: {\n tableName?: string;\n componentPath?: string;\n mode: string;\n format: string;\n };\n onImportFailed: (e: any) => Promise<void>;\n },\n) {\n const { deploymentUrl, adminKey, filePath } = args;\n const fetch = deploymentFetch(ctx, {\n deploymentUrl,\n adminKey,\n });\n\n const data = ctx.fs.createReadStream(filePath, {\n highWaterMark: CHUNK_SIZE,\n });\n const fileStats = ctx.fs.stat(filePath);\n\n showSpinner(ctx, `Importing ${filePath} (${formatSize(fileStats.size)})`);\n let importId: string;\n try {\n const startResp = await fetch(\"/api/import/start_upload\", {\n method: \"POST\",\n });\n const { uploadToken } = await startResp.json();\n\n const partTokens = [];\n let partNumber = 1;\n\n for await (const chunk of data) {\n const partUrl = `/api/import/upload_part?uploadToken=${encodeURIComponent(\n uploadToken,\n )}&partNumber=${partNumber}`;\n const partResp = await fetch(partUrl, {\n headers: {\n \"Content-Type\": \"application/octet-stream\",\n },\n body: chunk,\n method: \"POST\",\n });\n partTokens.push(await partResp.json());\n partNumber += 1;\n changeSpinner(\n ctx,\n `Uploading ${filePath} (${formatSize(data.bytesRead)}/${formatSize(\n fileStats.size,\n )})`,\n );\n }\n\n const finishResp = await fetch(\"/api/import/finish_upload\", {\n body: JSON.stringify({\n import: args.importArgs,\n uploadToken,\n partTokens,\n }),\n method: \"POST\",\n });\n const body = await finishResp.json();\n importId = body.importId;\n } catch (e) {\n await args.onImportFailed(e);\n return await logAndHandleFetchError(ctx, e);\n }\n return importId;\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkB;AAClB,mBAKO;AACP,qBASO;AACP,kBAAiB;AACjB,iBAA0B;AAC1B,yBAAiC;AACjC,oBAAsC;AACtC,qBAA4B;
|
|
4
|
+
"sourcesContent": ["import chalk from \"chalk\";\nimport {\n formatSize,\n waitUntilCalled,\n deploymentFetch,\n logAndHandleFetchError,\n} from \"./utils/utils.js\";\nimport {\n logFailure,\n Context,\n showSpinner,\n logFinishedStep,\n logWarning,\n logMessage,\n stopSpinner,\n changeSpinner,\n} from \"../../bundler/context.js\";\nimport path from \"path\";\nimport { subscribe } from \"./run.js\";\nimport { ConvexHttpClient } from \"../../browser/http_client.js\";\nimport { makeFunctionReference } from \"../../server/index.js\";\nimport { promptYesNo } from \"./utils/prompts.js\";\n\n// Backend has minimum chunk size of 5MiB except for the last chunk,\n// so we use 5MiB as highWaterMark which makes fs.ReadStream[asyncIterator]\n// output 5MiB chunks before the last one. This value can be overridden by\n// setting `CONVEX_IMPORT_CHUNK_SIZE` (bytes) in the environment.\nconst DEFAULT_CHUNK_SIZE = 5 * 1024 * 1024;\nconst ENV_CHUNK_SIZE = process.env.CONVEX_IMPORT_CHUNK_SIZE\n ? parseInt(process.env.CONVEX_IMPORT_CHUNK_SIZE, 10)\n : undefined;\n\nexport async function importIntoDeployment(\n ctx: Context,\n filePath: string,\n options: {\n deploymentUrl: string;\n adminKey: string;\n deploymentNotice: string;\n snapshotImportDashboardLink: string | undefined;\n table?: string;\n format?: \"csv\" | \"jsonLines\" | \"jsonArray\" | \"zip\";\n replace?: boolean;\n append?: boolean;\n replaceAll?: boolean;\n yes?: boolean;\n component?: string;\n },\n) {\n if (!ctx.fs.exists(filePath)) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"invalid filesystem data\",\n printedMessage: `Error: Path ${chalk.bold(filePath)} does not exist.`,\n });\n }\n\n const format = await determineFormat(ctx, filePath, options.format ?? null);\n const tableName = options.table ?? null;\n if (tableName === null) {\n if (format !== \"zip\") {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Error: The \\`--table\\` option is required for format ${format}`,\n });\n }\n } else {\n if (format === \"zip\") {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Error: The \\`--table\\` option is not allowed for format ${format}`,\n });\n }\n }\n\n const convexClient = new ConvexHttpClient(options.deploymentUrl);\n convexClient.setAdminAuth(options.adminKey);\n const existingImports = await convexClient.query(\n makeFunctionReference<\"query\", Record<string, never>, Array<unknown>>(\n \"_system/cli/queryImport:list\",\n ),\n {},\n );\n const ongoingImports = existingImports.filter(\n (i) => (i as any).state.state === \"in_progress\",\n );\n if (ongoingImports.length > 0) {\n await askToConfirmImportWithExistingImports(\n ctx,\n options.snapshotImportDashboardLink,\n options.yes,\n );\n }\n\n const fileStats = ctx.fs.stat(filePath);\n showSpinner(ctx, `Importing ${filePath} (${formatSize(fileStats.size)})`);\n\n let mode = \"requireEmpty\";\n if (options.append) {\n mode = \"append\";\n } else if (options.replace) {\n mode = \"replace\";\n } else if (options.replaceAll) {\n mode = \"replaceAll\";\n }\n const importArgs = {\n tableName: tableName === null ? undefined : tableName,\n componentPath: options.component,\n mode,\n format,\n };\n const tableNotice = tableName ? ` to table \"${chalk.bold(tableName)}\"` : \"\";\n const onFailure = async () => {\n logFailure(\n ctx,\n `Importing data from \"${chalk.bold(\n filePath,\n )}\"${tableNotice}${options.deploymentNotice} failed`,\n );\n };\n const importId = await uploadForImport(ctx, {\n deploymentUrl: options.deploymentUrl,\n adminKey: options.adminKey,\n filePath,\n importArgs,\n onImportFailed: onFailure,\n });\n changeSpinner(ctx, \"Parsing uploaded data\");\n const onProgress = (\n ctx: Context,\n state: InProgressImportState,\n checkpointCount: number,\n ) => {\n stopSpinner(ctx);\n while ((state.checkpoint_messages?.length ?? 0) > checkpointCount) {\n logFinishedStep(ctx, state.checkpoint_messages![checkpointCount]);\n checkpointCount += 1;\n }\n showSpinner(ctx, state.progress_message ?? \"Importing\");\n return checkpointCount;\n };\n while (true) {\n const snapshotImportState = await waitForStableImportState(ctx, {\n importId,\n deploymentUrl: options.deploymentUrl,\n adminKey: options.adminKey,\n onProgress,\n });\n switch (snapshotImportState.state) {\n case \"completed\":\n logFinishedStep(\n ctx,\n `Added ${snapshotImportState.num_rows_written} documents${tableNotice}${options.deploymentNotice}.`,\n );\n return;\n case \"failed\":\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Importing data from \"${chalk.bold(\n filePath,\n )}\"${tableNotice}${options.deploymentNotice} failed\\n\\n${chalk.red(snapshotImportState.error_message)}`,\n });\n case \"waiting_for_confirmation\": {\n // Clear spinner state so we can log and prompt without clobbering lines.\n stopSpinner(ctx);\n await askToConfirmImport(\n ctx,\n snapshotImportState.message_to_confirm,\n snapshotImportState.require_manual_confirmation,\n options.yes,\n );\n showSpinner(ctx, `Importing`);\n await confirmImport(ctx, {\n importId,\n adminKey: options.adminKey,\n deploymentUrl: options.deploymentUrl,\n onError: async () => {\n logFailure(\n ctx,\n `Importing data from \"${chalk.bold(\n filePath,\n )}\"${tableNotice}${options.deploymentNotice} failed`,\n );\n },\n });\n // Now we have kicked off the rest of the import, go around the loop again.\n break;\n }\n case \"uploaded\": {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Import canceled while parsing uploaded file`,\n });\n }\n case \"in_progress\": {\n const visitDashboardLink = options.snapshotImportDashboardLink\n ? ` Visit ${options.snapshotImportDashboardLink} to monitor its progress.`\n : \"\";\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `WARNING: Import is continuing to run on the server.${visitDashboardLink}`,\n });\n }\n default: {\n const _: never = snapshotImportState;\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `unknown error: unexpected state ${snapshotImportState as any}`,\n errForSentry: `unexpected snapshot import state ${(snapshotImportState as any).state}`,\n });\n }\n }\n }\n}\n\nasync function askToConfirmImport(\n ctx: Context,\n messageToConfirm: string | undefined,\n requireManualConfirmation: boolean | undefined,\n yes: boolean | undefined,\n) {\n if (!messageToConfirm?.length) {\n return;\n }\n logMessage(ctx, messageToConfirm);\n if (requireManualConfirmation !== false && !yes) {\n const confirmed = await promptYesNo(ctx, {\n message: \"Perform import?\",\n default: true,\n });\n if (!confirmed) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Import canceled\",\n });\n }\n }\n}\n\nasync function askToConfirmImportWithExistingImports(\n ctx: Context,\n snapshotImportDashboardLink: string | undefined,\n yes: boolean | undefined,\n) {\n const atDashboardLink = snapshotImportDashboardLink\n ? ` You can view its progress at ${snapshotImportDashboardLink}.`\n : \"\";\n logMessage(\n ctx,\n `There is already a snapshot import in progress.${atDashboardLink}`,\n );\n if (yes) {\n return;\n }\n const confirmed = await promptYesNo(ctx, {\n message: \"Start another import?\",\n default: true,\n });\n if (!confirmed) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Import canceled\",\n });\n }\n}\n\ntype InProgressImportState = {\n state: \"in_progress\";\n progress_message?: string | undefined;\n checkpoint_messages?: string[] | undefined;\n};\n\ntype SnapshotImportState =\n | { state: \"uploaded\" }\n | {\n state: \"waiting_for_confirmation\";\n message_to_confirm?: string;\n require_manual_confirmation?: boolean;\n }\n | InProgressImportState\n | { state: \"completed\"; num_rows_written: bigint }\n | { state: \"failed\"; error_message: string };\n\nexport async function waitForStableImportState(\n ctx: Context,\n args: {\n importId: string;\n deploymentUrl: string;\n adminKey: string;\n onProgress: (\n ctx: Context,\n state: InProgressImportState,\n checkpointCount: number,\n ) => number;\n },\n): Promise<SnapshotImportState> {\n const { importId, deploymentUrl, adminKey, onProgress } = args;\n const [donePromise, onDone] = waitUntilCalled();\n let snapshotImportState: SnapshotImportState;\n let checkpointCount = 0;\n await subscribe(ctx, {\n deploymentUrl,\n adminKey,\n parsedFunctionName: \"_system/cli/queryImport\",\n parsedFunctionArgs: { importId },\n componentPath: undefined,\n until: donePromise,\n callbacks: {\n onChange: (value: any) => {\n snapshotImportState = value.state;\n switch (snapshotImportState.state) {\n case \"waiting_for_confirmation\":\n case \"completed\":\n case \"failed\":\n onDone();\n break;\n case \"uploaded\":\n // Not a stable state. Ignore while the server continues working.\n return;\n case \"in_progress\":\n // Not a stable state. Ignore while the server continues working.\n checkpointCount = onProgress(\n ctx,\n snapshotImportState,\n checkpointCount,\n );\n return;\n }\n },\n },\n });\n return snapshotImportState!;\n}\n\nasync function determineFormat(\n ctx: Context,\n filePath: string,\n format: string | null,\n) {\n const fileExtension = path.extname(filePath);\n if (fileExtension !== \"\") {\n const formatToExtension: Record<string, string> = {\n csv: \".csv\",\n jsonLines: \".jsonl\",\n jsonArray: \".json\",\n zip: \".zip\",\n };\n const extensionToFormat = Object.fromEntries(\n Object.entries(formatToExtension).map((a) => a.reverse()),\n );\n if (format !== null && fileExtension !== formatToExtension[format]) {\n logWarning(\n ctx,\n chalk.yellow(\n `Warning: Extension of file ${filePath} (${fileExtension}) does not match specified format: ${format} (${formatToExtension[format]}).`,\n ),\n );\n }\n format ??= extensionToFormat[fileExtension] ?? null;\n }\n if (format === null) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"No input file format inferred by the filename extension or specified. Specify your input file's format using the `--format` flag.\",\n });\n }\n return format;\n}\n\nexport async function confirmImport(\n ctx: Context,\n args: {\n importId: string;\n adminKey: string;\n deploymentUrl: string;\n onError: (e: any) => Promise<void>;\n },\n) {\n const { importId, adminKey, deploymentUrl } = args;\n const fetch = deploymentFetch(ctx, {\n deploymentUrl,\n adminKey,\n });\n const performUrl = `/api/perform_import`;\n try {\n await fetch(performUrl, {\n method: \"POST\",\n body: JSON.stringify({ importId }),\n });\n } catch (e) {\n await args.onError(e);\n return await logAndHandleFetchError(ctx, e);\n }\n}\n\nexport async function uploadForImport(\n ctx: Context,\n args: {\n deploymentUrl: string;\n adminKey: string;\n filePath: string;\n importArgs: {\n tableName?: string;\n componentPath?: string;\n mode: string;\n format: string;\n };\n onImportFailed: (e: any) => Promise<void>;\n },\n) {\n const { deploymentUrl, adminKey, filePath } = args;\n const fetch = deploymentFetch(ctx, {\n deploymentUrl,\n adminKey,\n });\n\n const fileStats = ctx.fs.stat(filePath);\n // The backend rejects uploads of 10k or more parts. We use 9999 instead of\n // 10000 so rounding errors can't push us over the limit.\n const minChunkSize = Math.ceil(fileStats.size / 9999);\n let chunkSize = ENV_CHUNK_SIZE ?? DEFAULT_CHUNK_SIZE;\n if (chunkSize < minChunkSize) {\n chunkSize = minChunkSize;\n }\n const data = ctx.fs.createReadStream(filePath, {\n highWaterMark: chunkSize,\n });\n\n showSpinner(ctx, `Importing ${filePath} (${formatSize(fileStats.size)})`);\n let importId: string;\n try {\n const startResp = await fetch(\"/api/import/start_upload\", {\n method: \"POST\",\n });\n const { uploadToken } = await startResp.json();\n\n const partTokens = [];\n let partNumber = 1;\n\n for await (const chunk of data) {\n const partUrl = `/api/import/upload_part?uploadToken=${encodeURIComponent(\n uploadToken,\n )}&partNumber=${partNumber}`;\n const partResp = await fetch(partUrl, {\n headers: {\n \"Content-Type\": \"application/octet-stream\",\n },\n body: chunk,\n method: \"POST\",\n });\n partTokens.push(await partResp.json());\n partNumber += 1;\n changeSpinner(\n ctx,\n `Uploading ${filePath} (${formatSize(data.bytesRead)}/${formatSize(\n fileStats.size,\n )})`,\n );\n }\n\n const finishResp = await fetch(\"/api/import/finish_upload\", {\n body: JSON.stringify({\n import: args.importArgs,\n uploadToken,\n partTokens,\n }),\n method: \"POST\",\n });\n const body = await finishResp.json();\n importId = body.importId;\n } catch (e) {\n await args.onImportFailed(e);\n return await logAndHandleFetchError(ctx, e);\n }\n return importId;\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkB;AAClB,mBAKO;AACP,qBASO;AACP,kBAAiB;AACjB,iBAA0B;AAC1B,yBAAiC;AACjC,oBAAsC;AACtC,qBAA4B;AAM5B,MAAM,qBAAqB,IAAI,OAAO;AACtC,MAAM,iBAAiB,QAAQ,IAAI,2BAC/B,SAAS,QAAQ,IAAI,0BAA0B,EAAE,IACjD;AAEJ,eAAsB,qBACpB,KACA,UACA,SAaA;AACA,MAAI,CAAC,IAAI,GAAG,OAAO,QAAQ,GAAG;AAC5B,WAAO,MAAM,IAAI,MAAM;AAAA,MACrB,UAAU;AAAA,MACV,WAAW;AAAA,MACX,gBAAgB,eAAe,aAAAA,QAAM,KAAK,QAAQ,CAAC;AAAA,IACrD,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,MAAM,gBAAgB,KAAK,UAAU,QAAQ,UAAU,IAAI;AAC1E,QAAM,YAAY,QAAQ,SAAS;AACnC,MAAI,cAAc,MAAM;AACtB,QAAI,WAAW,OAAO;AACpB,aAAO,MAAM,IAAI,MAAM;AAAA,QACrB,UAAU;AAAA,QACV,WAAW;AAAA,QACX,gBAAgB,wDAAwD,MAAM;AAAA,MAChF,CAAC;AAAA,IACH;AAAA,EACF,OAAO;AACL,QAAI,WAAW,OAAO;AACpB,aAAO,MAAM,IAAI,MAAM;AAAA,QACrB,UAAU;AAAA,QACV,WAAW;AAAA,QACX,gBAAgB,2DAA2D,MAAM;AAAA,MACnF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,eAAe,IAAI,oCAAiB,QAAQ,aAAa;AAC/D,eAAa,aAAa,QAAQ,QAAQ;AAC1C,QAAM,kBAAkB,MAAM,aAAa;AAAA,QACzC;AAAA,MACE;AAAA,IACF;AAAA,IACA,CAAC;AAAA,EACH;AACA,QAAM,iBAAiB,gBAAgB;AAAA,IACrC,CAAC,MAAO,EAAU,MAAM,UAAU;AAAA,EACpC;AACA,MAAI,eAAe,SAAS,GAAG;AAC7B,UAAM;AAAA,MACJ;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV;AAAA,EACF;AAEA,QAAM,YAAY,IAAI,GAAG,KAAK,QAAQ;AACtC,kCAAY,KAAK,aAAa,QAAQ,SAAK,yBAAW,UAAU,IAAI,CAAC,GAAG;AAExE,MAAI,OAAO;AACX,MAAI,QAAQ,QAAQ;AAClB,WAAO;AAAA,EACT,WAAW,QAAQ,SAAS;AAC1B,WAAO;AAAA,EACT,WAAW,QAAQ,YAAY;AAC7B,WAAO;AAAA,EACT;AACA,QAAM,aAAa;AAAA,IACjB,WAAW,cAAc,OAAO,SAAY;AAAA,IAC5C,eAAe,QAAQ;AAAA,IACvB;AAAA,IACA;AAAA,EACF;AACA,QAAM,cAAc,YAAY,cAAc,aAAAA,QAAM,KAAK,SAAS,CAAC,MAAM;AACzE,QAAM,YAAY,YAAY;AAC5B;AAAA,MACE;AAAA,MACA,wBAAwB,aAAAA,QAAM;AAAA,QAC5B;AAAA,MACF,CAAC,IAAI,WAAW,GAAG,QAAQ,gBAAgB;AAAA,IAC7C;AAAA,EACF;AACA,QAAM,WAAW,MAAM,gBAAgB,KAAK;AAAA,IAC1C,eAAe,QAAQ;AAAA,IACvB,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,EAClB,CAAC;AACD,oCAAc,KAAK,uBAAuB;AAC1C,QAAM,aAAa,CACjBC,MACA,OACA,oBACG;AACH,oCAAYA,IAAG;AACf,YAAQ,MAAM,qBAAqB,UAAU,KAAK,iBAAiB;AACjE,0CAAgBA,MAAK,MAAM,oBAAqB,eAAe,CAAC;AAChE,yBAAmB;AAAA,IACrB;AACA,oCAAYA,MAAK,MAAM,oBAAoB,WAAW;AACtD,WAAO;AAAA,EACT;AACA,SAAO,MAAM;AACX,UAAM,sBAAsB,MAAM,yBAAyB,KAAK;AAAA,MAC9D;AAAA,MACA,eAAe,QAAQ;AAAA,MACvB,UAAU,QAAQ;AAAA,MAClB;AAAA,IACF,CAAC;AACD,YAAQ,oBAAoB,OAAO;AAAA,MACjC,KAAK;AACH;AAAA,UACE;AAAA,UACA,SAAS,oBAAoB,gBAAgB,aAAa,WAAW,GAAG,QAAQ,gBAAgB;AAAA,QAClG;AACA;AAAA,MACF,KAAK;AACH,eAAO,MAAM,IAAI,MAAM;AAAA,UACrB,UAAU;AAAA,UACV,WAAW;AAAA,UACX,gBAAgB,wBAAwB,aAAAD,QAAM;AAAA,YAC5C;AAAA,UACF,CAAC,IAAI,WAAW,GAAG,QAAQ,gBAAgB;AAAA;AAAA,EAAc,aAAAA,QAAM,IAAI,oBAAoB,aAAa,CAAC;AAAA,QACvG,CAAC;AAAA,MACH,KAAK,4BAA4B;AAE/B,wCAAY,GAAG;AACf,cAAM;AAAA,UACJ;AAAA,UACA,oBAAoB;AAAA,UACpB,oBAAoB;AAAA,UACpB,QAAQ;AAAA,QACV;AACA,wCAAY,KAAK,WAAW;AAC5B,cAAM,cAAc,KAAK;AAAA,UACvB;AAAA,UACA,UAAU,QAAQ;AAAA,UAClB,eAAe,QAAQ;AAAA,UACvB,SAAS,YAAY;AACnB;AAAA,cACE;AAAA,cACA,wBAAwB,aAAAA,QAAM;AAAA,gBAC5B;AAAA,cACF,CAAC,IAAI,WAAW,GAAG,QAAQ,gBAAgB;AAAA,YAC7C;AAAA,UACF;AAAA,QACF,CAAC;AAED;AAAA,MACF;AAAA,MACA,KAAK,YAAY;AACf,eAAO,MAAM,IAAI,MAAM;AAAA,UACrB,UAAU;AAAA,UACV,WAAW;AAAA,UACX,gBAAgB;AAAA,QAClB,CAAC;AAAA,MACH;AAAA,MACA,KAAK,eAAe;AAClB,cAAM,qBAAqB,QAAQ,8BAC/B,UAAU,QAAQ,2BAA2B,8BAC7C;AACJ,eAAO,MAAM,IAAI,MAAM;AAAA,UACrB,UAAU;AAAA,UACV,WAAW;AAAA,UACX,gBAAgB,sDAAsD,kBAAkB;AAAA,QAC1F,CAAC;AAAA,MACH;AAAA,MACA,SAAS;AACP,cAAM,IAAW;AACjB,eAAO,MAAM,IAAI,MAAM;AAAA,UACrB,UAAU;AAAA,UACV,WAAW;AAAA,UACX,gBAAgB,mCAAmC,mBAA0B;AAAA,UAC7E,cAAc,oCAAqC,oBAA4B,KAAK;AAAA,QACtF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAe,mBACb,KACA,kBACA,2BACA,KACA;AACA,MAAI,CAAC,kBAAkB,QAAQ;AAC7B;AAAA,EACF;AACA,iCAAW,KAAK,gBAAgB;AAChC,MAAI,8BAA8B,SAAS,CAAC,KAAK;AAC/C,UAAM,YAAY,UAAM,4BAAY,KAAK;AAAA,MACvC,SAAS;AAAA,MACT,SAAS;AAAA,IACX,CAAC;AACD,QAAI,CAAC,WAAW;AACd,aAAO,MAAM,IAAI,MAAM;AAAA,QACrB,UAAU;AAAA,QACV,WAAW;AAAA,QACX,gBAAgB;AAAA,MAClB,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,eAAe,sCACb,KACA,6BACA,KACA;AACA,QAAM,kBAAkB,8BACpB,iCAAiC,2BAA2B,MAC5D;AACJ;AAAA,IACE;AAAA,IACA,kDAAkD,eAAe;AAAA,EACnE;AACA,MAAI,KAAK;AACP;AAAA,EACF;AACA,QAAM,YAAY,UAAM,4BAAY,KAAK;AAAA,IACvC,SAAS;AAAA,IACT,SAAS;AAAA,EACX,CAAC;AACD,MAAI,CAAC,WAAW;AACd,WAAO,MAAM,IAAI,MAAM;AAAA,MACrB,UAAU;AAAA,MACV,WAAW;AAAA,MACX,gBAAgB;AAAA,IAClB,CAAC;AAAA,EACH;AACF;AAmBA,eAAsB,yBACpB,KACA,MAU8B;AAC9B,QAAM,EAAE,UAAU,eAAe,UAAU,WAAW,IAAI;AAC1D,QAAM,CAAC,aAAa,MAAM,QAAI,8BAAgB;AAC9C,MAAI;AACJ,MAAI,kBAAkB;AACtB,YAAM,sBAAU,KAAK;AAAA,IACnB;AAAA,IACA;AAAA,IACA,oBAAoB;AAAA,IACpB,oBAAoB,EAAE,SAAS;AAAA,IAC/B,eAAe;AAAA,IACf,OAAO;AAAA,IACP,WAAW;AAAA,MACT,UAAU,CAAC,UAAe;AACxB,8BAAsB,MAAM;AAC5B,gBAAQ,oBAAoB,OAAO;AAAA,UACjC,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AACH,mBAAO;AACP;AAAA,UACF,KAAK;AAEH;AAAA,UACF,KAAK;AAEH,8BAAkB;AAAA,cAChB;AAAA,cACA;AAAA,cACA;AAAA,YACF;AACA;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEA,eAAe,gBACb,KACA,UACA,QACA;AACA,QAAM,gBAAgB,YAAAE,QAAK,QAAQ,QAAQ;AAC3C,MAAI,kBAAkB,IAAI;AACxB,UAAM,oBAA4C;AAAA,MAChD,KAAK;AAAA,MACL,WAAW;AAAA,MACX,WAAW;AAAA,MACX,KAAK;AAAA,IACP;AACA,UAAM,oBAAoB,OAAO;AAAA,MAC/B,OAAO,QAAQ,iBAAiB,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC;AAAA,IAC1D;AACA,QAAI,WAAW,QAAQ,kBAAkB,kBAAkB,MAAM,GAAG;AAClE;AAAA,QACE;AAAA,QACA,aAAAF,QAAM;AAAA,UACJ,8BAA8B,QAAQ,KAAK,aAAa,sCAAsC,MAAM,KAAK,kBAAkB,MAAM,CAAC;AAAA,QACpI;AAAA,MACF;AAAA,IACF;AACA,wBAAW,kBAAkB,aAAa,KAAK;AAAA,EACjD;AACA,MAAI,WAAW,MAAM;AACnB,WAAO,MAAM,IAAI,MAAM;AAAA,MACrB,UAAU;AAAA,MACV,WAAW;AAAA,MACX,gBACE;AAAA,IACJ,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,eAAsB,cACpB,KACA,MAMA;AACA,QAAM,EAAE,UAAU,UAAU,cAAc,IAAI;AAC9C,QAAM,YAAQ,8BAAgB,KAAK;AAAA,IACjC;AAAA,IACA;AAAA,EACF,CAAC;AACD,QAAM,aAAa;AACnB,MAAI;AACF,UAAM,MAAM,YAAY;AAAA,MACtB,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,SAAS,CAAC;AAAA,IACnC,CAAC;AAAA,EACH,SAAS,GAAG;AACV,UAAM,KAAK,QAAQ,CAAC;AACpB,WAAO,UAAM,qCAAuB,KAAK,CAAC;AAAA,EAC5C;AACF;AAEA,eAAsB,gBACpB,KACA,MAYA;AACA,QAAM,EAAE,eAAe,UAAU,SAAS,IAAI;AAC9C,QAAM,YAAQ,8BAAgB,KAAK;AAAA,IACjC;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,YAAY,IAAI,GAAG,KAAK,QAAQ;AAGtC,QAAM,eAAe,KAAK,KAAK,UAAU,OAAO,IAAI;AACpD,MAAI,YAAY,kBAAkB;AAClC,MAAI,YAAY,cAAc;AAC5B,gBAAY;AAAA,EACd;AACA,QAAM,OAAO,IAAI,GAAG,iBAAiB,UAAU;AAAA,IAC7C,eAAe;AAAA,EACjB,CAAC;AAED,kCAAY,KAAK,aAAa,QAAQ,SAAK,yBAAW,UAAU,IAAI,CAAC,GAAG;AACxE,MAAI;AACJ,MAAI;AACF,UAAM,YAAY,MAAM,MAAM,4BAA4B;AAAA,MACxD,QAAQ;AAAA,IACV,CAAC;AACD,UAAM,EAAE,YAAY,IAAI,MAAM,UAAU,KAAK;AAE7C,UAAM,aAAa,CAAC;AACpB,QAAI,aAAa;AAEjB,qBAAiB,SAAS,MAAM;AAC9B,YAAM,UAAU,uCAAuC;AAAA,QACrD;AAAA,MACF,CAAC,eAAe,UAAU;AAC1B,YAAM,WAAW,MAAM,MAAM,SAAS;AAAA,QACpC,SAAS;AAAA,UACP,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM;AAAA,QACN,QAAQ;AAAA,MACV,CAAC;AACD,iBAAW,KAAK,MAAM,SAAS,KAAK,CAAC;AACrC,oBAAc;AACd;AAAA,QACE;AAAA,QACA,aAAa,QAAQ,SAAK,yBAAW,KAAK,SAAS,CAAC,QAAI;AAAA,UACtD,UAAU;AAAA,QACZ,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,aAAa,MAAM,MAAM,6BAA6B;AAAA,MAC1D,MAAM,KAAK,UAAU;AAAA,QACnB,QAAQ,KAAK;AAAA,QACb;AAAA,QACA;AAAA,MACF,CAAC;AAAA,MACD,QAAQ;AAAA,IACV,CAAC;AACD,UAAM,OAAO,MAAM,WAAW,KAAK;AACnC,eAAW,KAAK;AAAA,EAClB,SAAS,GAAG;AACV,UAAM,KAAK,eAAe,CAAC;AAC3B,WAAO,UAAM,qCAAuB,KAAK,CAAC;AAAA,EAC5C;AACA,SAAO;AACT;",
|
|
6
6
|
"names": ["chalk", "ctx", "path"]
|
|
7
7
|
}
|
package/dist/cjs/index.js
CHANGED
package/dist/cjs/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["export const version = \"1.
|
|
4
|
+
"sourcesContent": ["export const version = \"1.25.0-alpha.0\";"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,MAAM,UAAU;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -31,6 +31,7 @@ var import_index_range_builder_impl = require("./index_range_builder_impl.js");
|
|
|
31
31
|
var import_search_filter_builder_impl = require("./search_filter_builder_impl.js");
|
|
32
32
|
var import_validate = require("./validate.js");
|
|
33
33
|
var import__ = require("../../index.js");
|
|
34
|
+
const MAX_QUERY_OPERATORS = 256;
|
|
34
35
|
class QueryInitializerImpl {
|
|
35
36
|
constructor(tableName) {
|
|
36
37
|
__publicField(this, "tableName");
|
|
@@ -167,6 +168,11 @@ class QueryImpl {
|
|
|
167
168
|
filter(predicate) {
|
|
168
169
|
(0, import_validate.validateArg)(predicate, 1, "filter", "predicate");
|
|
169
170
|
const query = this.takeQuery();
|
|
171
|
+
if (query.operators.length >= MAX_QUERY_OPERATORS) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
`Can't construct query with more than ${MAX_QUERY_OPERATORS} operators`
|
|
174
|
+
);
|
|
175
|
+
}
|
|
170
176
|
query.operators.push({
|
|
171
177
|
filter: (0, import_filter_builder_impl.serializeExpression)(predicate(import_filter_builder_impl.filterBuilderImpl))
|
|
172
178
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/server/impl/query_impl.ts"],
|
|
4
|
-
"sourcesContent": ["import { Value, JSONValue, jsonToConvex } from \"../../values/index.js\";\nimport { PaginationResult, PaginationOptions } from \"../pagination.js\";\nimport { performAsyncSyscall, performSyscall } from \"./syscall.js\";\nimport {\n filterBuilderImpl,\n serializeExpression,\n} from \"./filter_builder_impl.js\";\nimport { Query, QueryInitializer } from \"../query.js\";\nimport { ExpressionOrValue, FilterBuilder } from \"../filter_builder.js\";\nimport { GenericTableInfo } from \"../data_model.js\";\nimport {\n IndexRangeBuilderImpl,\n SerializedRangeExpression,\n} from \"./index_range_builder_impl.js\";\nimport {\n SearchFilterBuilderImpl,\n SerializedSearchFilter,\n} from \"./search_filter_builder_impl.js\";\nimport { validateArg, validateArgIsNonNegativeInteger } from \"./validate.js\";\nimport { version } from \"../../index.js\";\n\ntype QueryOperator = { filter: JSONValue } | { limit: number };\ntype Source =\n | { type: \"FullTableScan\"; tableName: string; order: \"asc\" | \"desc\" | null }\n | {\n type: \"IndexRange\";\n indexName: string;\n range: ReadonlyArray<SerializedRangeExpression>;\n order: \"asc\" | \"desc\" | null;\n }\n | {\n type: \"Search\";\n indexName: string;\n filters: ReadonlyArray<SerializedSearchFilter>;\n };\n\ntype SerializedQuery = {\n source: Source;\n operators: Array<QueryOperator>;\n};\n\nexport class QueryInitializerImpl\n implements QueryInitializer<GenericTableInfo>\n{\n private tableName: string;\n\n constructor(tableName: string) {\n this.tableName = tableName;\n }\n\n withIndex(\n indexName: string,\n indexRange?: (q: IndexRangeBuilderImpl) => IndexRangeBuilderImpl,\n ): QueryImpl {\n validateArg(indexName, 1, \"withIndex\", \"indexName\");\n let rangeBuilder = IndexRangeBuilderImpl.new();\n if (indexRange !== undefined) {\n rangeBuilder = indexRange(rangeBuilder);\n }\n return new QueryImpl({\n source: {\n type: \"IndexRange\",\n indexName: this.tableName + \".\" + indexName,\n range: rangeBuilder.export(),\n order: null,\n },\n operators: [],\n });\n }\n\n withSearchIndex(\n indexName: string,\n searchFilter: (q: SearchFilterBuilderImpl) => SearchFilterBuilderImpl,\n ): QueryImpl {\n validateArg(indexName, 1, \"withSearchIndex\", \"indexName\");\n validateArg(searchFilter, 2, \"withSearchIndex\", \"searchFilter\");\n const searchFilterBuilder = SearchFilterBuilderImpl.new();\n return new QueryImpl({\n source: {\n type: \"Search\",\n indexName: this.tableName + \".\" + indexName,\n filters: searchFilter(searchFilterBuilder).export(),\n },\n operators: [],\n });\n }\n\n fullTableScan(): QueryImpl {\n return new QueryImpl({\n source: {\n type: \"FullTableScan\",\n tableName: this.tableName,\n order: null,\n },\n operators: [],\n });\n }\n\n order(order: \"asc\" | \"desc\"): QueryImpl {\n return this.fullTableScan().order(order);\n }\n\n // This is internal API and should not be exposed to developers yet.\n async count(): Promise<number> {\n const syscallJSON = await performAsyncSyscall(\"1.0/count\", {\n table: this.tableName,\n });\n const syscallResult = jsonToConvex(syscallJSON) as number;\n return syscallResult;\n }\n\n filter(\n predicate: (\n q: FilterBuilder<GenericTableInfo>,\n ) => ExpressionOrValue<boolean>,\n ) {\n return this.fullTableScan().filter(predicate);\n }\n\n limit(n: number) {\n return this.fullTableScan().limit(n);\n }\n\n collect(): Promise<any[]> {\n return this.fullTableScan().collect();\n }\n\n take(n: number): Promise<Array<any>> {\n return this.fullTableScan().take(n);\n }\n\n paginate(paginationOpts: PaginationOptions): Promise<PaginationResult<any>> {\n return this.fullTableScan().paginate(paginationOpts);\n }\n\n first(): Promise<any> {\n return this.fullTableScan().first();\n }\n\n unique(): Promise<any> {\n return this.fullTableScan().unique();\n }\n\n [Symbol.asyncIterator](): AsyncIterableIterator<any> {\n return this.fullTableScan()[Symbol.asyncIterator]();\n }\n}\n\n/**\n * @param type Whether the query was consumed or closed.\n * @throws An error indicating the query has been closed.\n */\nfunction throwClosedError(type: \"closed\" | \"consumed\"): never {\n throw new Error(\n type === \"consumed\"\n ? \"This query is closed and can't emit any more values.\"\n : \"This query has been chained with another operator and can't be reused.\",\n );\n}\n\nexport class QueryImpl implements Query<GenericTableInfo> {\n private state:\n | { type: \"preparing\"; query: SerializedQuery }\n | { type: \"executing\"; queryId: number }\n | { type: \"closed\" }\n | { type: \"consumed\" };\n\n constructor(query: SerializedQuery) {\n this.state = { type: \"preparing\", query };\n }\n\n private takeQuery(): SerializedQuery {\n if (this.state.type !== \"preparing\") {\n throw new Error(\n \"A query can only be chained once and can't be chained after iteration begins.\",\n );\n }\n const query = this.state.query;\n this.state = { type: \"closed\" };\n return query;\n }\n\n private startQuery(): number {\n if (this.state.type === \"executing\") {\n throw new Error(\"Iteration can only begin on a query once.\");\n }\n if (this.state.type === \"closed\" || this.state.type === \"consumed\") {\n throwClosedError(this.state.type);\n }\n const query = this.state.query;\n const { queryId } = performSyscall(\"1.0/queryStream\", { query, version });\n this.state = { type: \"executing\", queryId };\n return queryId;\n }\n\n private closeQuery() {\n if (this.state.type === \"executing\") {\n const queryId = this.state.queryId;\n performSyscall(\"1.0/queryCleanup\", { queryId });\n }\n this.state = { type: \"consumed\" };\n }\n\n order(order: \"asc\" | \"desc\"): QueryImpl {\n validateArg(order, 1, \"order\", \"order\");\n const query = this.takeQuery();\n if (query.source.type === \"Search\") {\n throw new Error(\n \"Search queries must always be in relevance order. Can not set order manually.\",\n );\n }\n if (query.source.order !== null) {\n throw new Error(\"Queries may only specify order at most once\");\n }\n query.source.order = order;\n return new QueryImpl(query);\n }\n\n filter(\n predicate: (\n q: FilterBuilder<GenericTableInfo>,\n ) => ExpressionOrValue<boolean>,\n ): any {\n validateArg(predicate, 1, \"filter\", \"predicate\");\n const query = this.takeQuery();\n query.operators.push({\n filter: serializeExpression(predicate(filterBuilderImpl)),\n });\n return new QueryImpl(query);\n }\n\n limit(n: number): any {\n validateArg(n, 1, \"limit\", \"n\");\n const query = this.takeQuery();\n query.operators.push({ limit: n });\n return new QueryImpl(query);\n }\n\n [Symbol.asyncIterator](): AsyncIterableIterator<any> {\n this.startQuery();\n return this;\n }\n\n async next(): Promise<IteratorResult<any>> {\n if (this.state.type === \"closed\" || this.state.type === \"consumed\") {\n throwClosedError(this.state.type);\n }\n // Allow calling `.next()` when the query is in \"preparing\" state to implicitly start the\n // query. This allows the developer to call `.next()` on the query without having to use\n // a `for await` statement.\n const queryId =\n this.state.type === \"preparing\" ? this.startQuery() : this.state.queryId;\n const { value, done } = await performAsyncSyscall(\"1.0/queryStreamNext\", {\n queryId,\n });\n if (done) {\n this.closeQuery();\n }\n const convexValue = jsonToConvex(value);\n return { value: convexValue, done };\n }\n\n return() {\n this.closeQuery();\n return Promise.resolve({ done: true, value: undefined });\n }\n\n async paginate(\n paginationOpts: PaginationOptions,\n ): Promise<PaginationResult<any>> {\n validateArg(paginationOpts, 1, \"paginate\", \"options\");\n if (\n typeof paginationOpts?.numItems !== \"number\" ||\n paginationOpts.numItems < 0\n ) {\n throw new Error(\n `\\`options.numItems\\` must be a positive number. Received \\`${paginationOpts?.numItems}\\`.`,\n );\n }\n const query = this.takeQuery();\n const pageSize = paginationOpts.numItems;\n const cursor = paginationOpts.cursor;\n const endCursor = paginationOpts?.endCursor ?? null;\n const maximumRowsRead = paginationOpts.maximumRowsRead ?? null;\n const { page, isDone, continueCursor, splitCursor, pageStatus } =\n await performAsyncSyscall(\"1.0/queryPage\", {\n query,\n cursor,\n endCursor,\n pageSize,\n maximumRowsRead,\n maximumBytesRead: paginationOpts.maximumBytesRead,\n version,\n });\n return {\n page: page.map((json: string) => jsonToConvex(json)),\n isDone,\n continueCursor,\n splitCursor,\n pageStatus,\n };\n }\n\n async collect(): Promise<Array<any>> {\n const out: Value[] = [];\n for await (const item of this) {\n out.push(item);\n }\n return out;\n }\n\n async take(n: number): Promise<Array<any>> {\n validateArg(n, 1, \"take\", \"n\");\n validateArgIsNonNegativeInteger(n, 1, \"take\", \"n\");\n return this.limit(n).collect();\n }\n\n async first(): Promise<any | null> {\n const first_array = await this.take(1);\n return first_array.length === 0 ? null : first_array[0];\n }\n\n async unique(): Promise<any | null> {\n const first_two_array = await this.take(2);\n if (first_two_array.length === 0) {\n return null;\n }\n if (first_two_array.length === 2) {\n throw new Error(`unique() query returned more than one result: \n [${first_two_array[0]._id}, ${first_two_array[1]._id}, ...]`);\n }\n return first_two_array[0];\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA+C;AAE/C,qBAAoD;AACpD,iCAGO;AAIP,sCAGO;AACP,wCAGO;AACP,sBAA6D;AAC7D,eAAwB;
|
|
4
|
+
"sourcesContent": ["import { Value, JSONValue, jsonToConvex } from \"../../values/index.js\";\nimport { PaginationResult, PaginationOptions } from \"../pagination.js\";\nimport { performAsyncSyscall, performSyscall } from \"./syscall.js\";\nimport {\n filterBuilderImpl,\n serializeExpression,\n} from \"./filter_builder_impl.js\";\nimport { Query, QueryInitializer } from \"../query.js\";\nimport { ExpressionOrValue, FilterBuilder } from \"../filter_builder.js\";\nimport { GenericTableInfo } from \"../data_model.js\";\nimport {\n IndexRangeBuilderImpl,\n SerializedRangeExpression,\n} from \"./index_range_builder_impl.js\";\nimport {\n SearchFilterBuilderImpl,\n SerializedSearchFilter,\n} from \"./search_filter_builder_impl.js\";\nimport { validateArg, validateArgIsNonNegativeInteger } from \"./validate.js\";\nimport { version } from \"../../index.js\";\n\nconst MAX_QUERY_OPERATORS = 256;\n\ntype QueryOperator = { filter: JSONValue } | { limit: number };\ntype Source =\n | { type: \"FullTableScan\"; tableName: string; order: \"asc\" | \"desc\" | null }\n | {\n type: \"IndexRange\";\n indexName: string;\n range: ReadonlyArray<SerializedRangeExpression>;\n order: \"asc\" | \"desc\" | null;\n }\n | {\n type: \"Search\";\n indexName: string;\n filters: ReadonlyArray<SerializedSearchFilter>;\n };\n\ntype SerializedQuery = {\n source: Source;\n operators: Array<QueryOperator>;\n};\n\nexport class QueryInitializerImpl\n implements QueryInitializer<GenericTableInfo>\n{\n private tableName: string;\n\n constructor(tableName: string) {\n this.tableName = tableName;\n }\n\n withIndex(\n indexName: string,\n indexRange?: (q: IndexRangeBuilderImpl) => IndexRangeBuilderImpl,\n ): QueryImpl {\n validateArg(indexName, 1, \"withIndex\", \"indexName\");\n let rangeBuilder = IndexRangeBuilderImpl.new();\n if (indexRange !== undefined) {\n rangeBuilder = indexRange(rangeBuilder);\n }\n return new QueryImpl({\n source: {\n type: \"IndexRange\",\n indexName: this.tableName + \".\" + indexName,\n range: rangeBuilder.export(),\n order: null,\n },\n operators: [],\n });\n }\n\n withSearchIndex(\n indexName: string,\n searchFilter: (q: SearchFilterBuilderImpl) => SearchFilterBuilderImpl,\n ): QueryImpl {\n validateArg(indexName, 1, \"withSearchIndex\", \"indexName\");\n validateArg(searchFilter, 2, \"withSearchIndex\", \"searchFilter\");\n const searchFilterBuilder = SearchFilterBuilderImpl.new();\n return new QueryImpl({\n source: {\n type: \"Search\",\n indexName: this.tableName + \".\" + indexName,\n filters: searchFilter(searchFilterBuilder).export(),\n },\n operators: [],\n });\n }\n\n fullTableScan(): QueryImpl {\n return new QueryImpl({\n source: {\n type: \"FullTableScan\",\n tableName: this.tableName,\n order: null,\n },\n operators: [],\n });\n }\n\n order(order: \"asc\" | \"desc\"): QueryImpl {\n return this.fullTableScan().order(order);\n }\n\n // This is internal API and should not be exposed to developers yet.\n async count(): Promise<number> {\n const syscallJSON = await performAsyncSyscall(\"1.0/count\", {\n table: this.tableName,\n });\n const syscallResult = jsonToConvex(syscallJSON) as number;\n return syscallResult;\n }\n\n filter(\n predicate: (\n q: FilterBuilder<GenericTableInfo>,\n ) => ExpressionOrValue<boolean>,\n ) {\n return this.fullTableScan().filter(predicate);\n }\n\n limit(n: number) {\n return this.fullTableScan().limit(n);\n }\n\n collect(): Promise<any[]> {\n return this.fullTableScan().collect();\n }\n\n take(n: number): Promise<Array<any>> {\n return this.fullTableScan().take(n);\n }\n\n paginate(paginationOpts: PaginationOptions): Promise<PaginationResult<any>> {\n return this.fullTableScan().paginate(paginationOpts);\n }\n\n first(): Promise<any> {\n return this.fullTableScan().first();\n }\n\n unique(): Promise<any> {\n return this.fullTableScan().unique();\n }\n\n [Symbol.asyncIterator](): AsyncIterableIterator<any> {\n return this.fullTableScan()[Symbol.asyncIterator]();\n }\n}\n\n/**\n * @param type Whether the query was consumed or closed.\n * @throws An error indicating the query has been closed.\n */\nfunction throwClosedError(type: \"closed\" | \"consumed\"): never {\n throw new Error(\n type === \"consumed\"\n ? \"This query is closed and can't emit any more values.\"\n : \"This query has been chained with another operator and can't be reused.\",\n );\n}\n\nexport class QueryImpl implements Query<GenericTableInfo> {\n private state:\n | { type: \"preparing\"; query: SerializedQuery }\n | { type: \"executing\"; queryId: number }\n | { type: \"closed\" }\n | { type: \"consumed\" };\n\n constructor(query: SerializedQuery) {\n this.state = { type: \"preparing\", query };\n }\n\n private takeQuery(): SerializedQuery {\n if (this.state.type !== \"preparing\") {\n throw new Error(\n \"A query can only be chained once and can't be chained after iteration begins.\",\n );\n }\n const query = this.state.query;\n this.state = { type: \"closed\" };\n return query;\n }\n\n private startQuery(): number {\n if (this.state.type === \"executing\") {\n throw new Error(\"Iteration can only begin on a query once.\");\n }\n if (this.state.type === \"closed\" || this.state.type === \"consumed\") {\n throwClosedError(this.state.type);\n }\n const query = this.state.query;\n const { queryId } = performSyscall(\"1.0/queryStream\", { query, version });\n this.state = { type: \"executing\", queryId };\n return queryId;\n }\n\n private closeQuery() {\n if (this.state.type === \"executing\") {\n const queryId = this.state.queryId;\n performSyscall(\"1.0/queryCleanup\", { queryId });\n }\n this.state = { type: \"consumed\" };\n }\n\n order(order: \"asc\" | \"desc\"): QueryImpl {\n validateArg(order, 1, \"order\", \"order\");\n const query = this.takeQuery();\n if (query.source.type === \"Search\") {\n throw new Error(\n \"Search queries must always be in relevance order. Can not set order manually.\",\n );\n }\n if (query.source.order !== null) {\n throw new Error(\"Queries may only specify order at most once\");\n }\n query.source.order = order;\n return new QueryImpl(query);\n }\n\n filter(\n predicate: (\n q: FilterBuilder<GenericTableInfo>,\n ) => ExpressionOrValue<boolean>,\n ): any {\n validateArg(predicate, 1, \"filter\", \"predicate\");\n const query = this.takeQuery();\n if (query.operators.length >= MAX_QUERY_OPERATORS) {\n throw new Error(\n `Can't construct query with more than ${MAX_QUERY_OPERATORS} operators`,\n );\n }\n query.operators.push({\n filter: serializeExpression(predicate(filterBuilderImpl)),\n });\n return new QueryImpl(query);\n }\n\n limit(n: number): any {\n validateArg(n, 1, \"limit\", \"n\");\n const query = this.takeQuery();\n query.operators.push({ limit: n });\n return new QueryImpl(query);\n }\n\n [Symbol.asyncIterator](): AsyncIterableIterator<any> {\n this.startQuery();\n return this;\n }\n\n async next(): Promise<IteratorResult<any>> {\n if (this.state.type === \"closed\" || this.state.type === \"consumed\") {\n throwClosedError(this.state.type);\n }\n // Allow calling `.next()` when the query is in \"preparing\" state to implicitly start the\n // query. This allows the developer to call `.next()` on the query without having to use\n // a `for await` statement.\n const queryId =\n this.state.type === \"preparing\" ? this.startQuery() : this.state.queryId;\n const { value, done } = await performAsyncSyscall(\"1.0/queryStreamNext\", {\n queryId,\n });\n if (done) {\n this.closeQuery();\n }\n const convexValue = jsonToConvex(value);\n return { value: convexValue, done };\n }\n\n return() {\n this.closeQuery();\n return Promise.resolve({ done: true, value: undefined });\n }\n\n async paginate(\n paginationOpts: PaginationOptions,\n ): Promise<PaginationResult<any>> {\n validateArg(paginationOpts, 1, \"paginate\", \"options\");\n if (\n typeof paginationOpts?.numItems !== \"number\" ||\n paginationOpts.numItems < 0\n ) {\n throw new Error(\n `\\`options.numItems\\` must be a positive number. Received \\`${paginationOpts?.numItems}\\`.`,\n );\n }\n const query = this.takeQuery();\n const pageSize = paginationOpts.numItems;\n const cursor = paginationOpts.cursor;\n const endCursor = paginationOpts?.endCursor ?? null;\n const maximumRowsRead = paginationOpts.maximumRowsRead ?? null;\n const { page, isDone, continueCursor, splitCursor, pageStatus } =\n await performAsyncSyscall(\"1.0/queryPage\", {\n query,\n cursor,\n endCursor,\n pageSize,\n maximumRowsRead,\n maximumBytesRead: paginationOpts.maximumBytesRead,\n version,\n });\n return {\n page: page.map((json: string) => jsonToConvex(json)),\n isDone,\n continueCursor,\n splitCursor,\n pageStatus,\n };\n }\n\n async collect(): Promise<Array<any>> {\n const out: Value[] = [];\n for await (const item of this) {\n out.push(item);\n }\n return out;\n }\n\n async take(n: number): Promise<Array<any>> {\n validateArg(n, 1, \"take\", \"n\");\n validateArgIsNonNegativeInteger(n, 1, \"take\", \"n\");\n return this.limit(n).collect();\n }\n\n async first(): Promise<any | null> {\n const first_array = await this.take(1);\n return first_array.length === 0 ? null : first_array[0];\n }\n\n async unique(): Promise<any | null> {\n const first_two_array = await this.take(2);\n if (first_two_array.length === 0) {\n return null;\n }\n if (first_two_array.length === 2) {\n throw new Error(`unique() query returned more than one result: \n [${first_two_array[0]._id}, ${first_two_array[1]._id}, ...]`);\n }\n return first_two_array[0];\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA+C;AAE/C,qBAAoD;AACpD,iCAGO;AAIP,sCAGO;AACP,wCAGO;AACP,sBAA6D;AAC7D,eAAwB;AAExB,MAAM,sBAAsB;AAsBrB,MAAM,qBAEb;AAAA,EAGE,YAAY,WAAmB;AAF/B,wBAAQ;AAGN,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,UACE,WACA,YACW;AACX,qCAAY,WAAW,GAAG,aAAa,WAAW;AAClD,QAAI,eAAe,sDAAsB,IAAI;AAC7C,QAAI,eAAe,QAAW;AAC5B,qBAAe,WAAW,YAAY;AAAA,IACxC;AACA,WAAO,IAAI,UAAU;AAAA,MACnB,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,WAAW,KAAK,YAAY,MAAM;AAAA,QAClC,OAAO,aAAa,OAAO;AAAA,QAC3B,OAAO;AAAA,MACT;AAAA,MACA,WAAW,CAAC;AAAA,IACd,CAAC;AAAA,EACH;AAAA,EAEA,gBACE,WACA,cACW;AACX,qCAAY,WAAW,GAAG,mBAAmB,WAAW;AACxD,qCAAY,cAAc,GAAG,mBAAmB,cAAc;AAC9D,UAAM,sBAAsB,0DAAwB,IAAI;AACxD,WAAO,IAAI,UAAU;AAAA,MACnB,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,WAAW,KAAK,YAAY,MAAM;AAAA,QAClC,SAAS,aAAa,mBAAmB,EAAE,OAAO;AAAA,MACpD;AAAA,MACA,WAAW,CAAC;AAAA,IACd,CAAC;AAAA,EACH;AAAA,EAEA,gBAA2B;AACzB,WAAO,IAAI,UAAU;AAAA,MACnB,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,WAAW,KAAK;AAAA,QAChB,OAAO;AAAA,MACT;AAAA,MACA,WAAW,CAAC;AAAA,IACd,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,OAAkC;AACtC,WAAO,KAAK,cAAc,EAAE,MAAM,KAAK;AAAA,EACzC;AAAA;AAAA,EAGA,MAAM,QAAyB;AAC7B,UAAM,cAAc,UAAM,oCAAoB,aAAa;AAAA,MACzD,OAAO,KAAK;AAAA,IACd,CAAC;AACD,UAAM,oBAAgB,4BAAa,WAAW;AAC9C,WAAO;AAAA,EACT;AAAA,EAEA,OACE,WAGA;AACA,WAAO,KAAK,cAAc,EAAE,OAAO,SAAS;AAAA,EAC9C;AAAA,EAEA,MAAM,GAAW;AACf,WAAO,KAAK,cAAc,EAAE,MAAM,CAAC;AAAA,EACrC;AAAA,EAEA,UAA0B;AACxB,WAAO,KAAK,cAAc,EAAE,QAAQ;AAAA,EACtC;AAAA,EAEA,KAAK,GAAgC;AACnC,WAAO,KAAK,cAAc,EAAE,KAAK,CAAC;AAAA,EACpC;AAAA,EAEA,SAAS,gBAAmE;AAC1E,WAAO,KAAK,cAAc,EAAE,SAAS,cAAc;AAAA,EACrD;AAAA,EAEA,QAAsB;AACpB,WAAO,KAAK,cAAc,EAAE,MAAM;AAAA,EACpC;AAAA,EAEA,SAAuB;AACrB,WAAO,KAAK,cAAc,EAAE,OAAO;AAAA,EACrC;AAAA,EAEA,CAAC,OAAO,aAAa,IAAgC;AACnD,WAAO,KAAK,cAAc,EAAE,OAAO,aAAa,EAAE;AAAA,EACpD;AACF;AAMA,SAAS,iBAAiB,MAAoC;AAC5D,QAAM,IAAI;AAAA,IACR,SAAS,aACL,yDACA;AAAA,EACN;AACF;AAEO,MAAM,UAA6C;AAAA,EAOxD,YAAY,OAAwB;AANpC,wBAAQ;AAON,SAAK,QAAQ,EAAE,MAAM,aAAa,MAAM;AAAA,EAC1C;AAAA,EAEQ,YAA6B;AACnC,QAAI,KAAK,MAAM,SAAS,aAAa;AACnC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM,QAAQ,KAAK,MAAM;AACzB,SAAK,QAAQ,EAAE,MAAM,SAAS;AAC9B,WAAO;AAAA,EACT;AAAA,EAEQ,aAAqB;AAC3B,QAAI,KAAK,MAAM,SAAS,aAAa;AACnC,YAAM,IAAI,MAAM,2CAA2C;AAAA,IAC7D;AACA,QAAI,KAAK,MAAM,SAAS,YAAY,KAAK,MAAM,SAAS,YAAY;AAClE,uBAAiB,KAAK,MAAM,IAAI;AAAA,IAClC;AACA,UAAM,QAAQ,KAAK,MAAM;AACzB,UAAM,EAAE,QAAQ,QAAI,+BAAe,mBAAmB,EAAE,OAAO,0BAAQ,CAAC;AACxE,SAAK,QAAQ,EAAE,MAAM,aAAa,QAAQ;AAC1C,WAAO;AAAA,EACT;AAAA,EAEQ,aAAa;AACnB,QAAI,KAAK,MAAM,SAAS,aAAa;AACnC,YAAM,UAAU,KAAK,MAAM;AAC3B,yCAAe,oBAAoB,EAAE,QAAQ,CAAC;AAAA,IAChD;AACA,SAAK,QAAQ,EAAE,MAAM,WAAW;AAAA,EAClC;AAAA,EAEA,MAAM,OAAkC;AACtC,qCAAY,OAAO,GAAG,SAAS,OAAO;AACtC,UAAM,QAAQ,KAAK,UAAU;AAC7B,QAAI,MAAM,OAAO,SAAS,UAAU;AAClC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,QAAI,MAAM,OAAO,UAAU,MAAM;AAC/B,YAAM,IAAI,MAAM,6CAA6C;AAAA,IAC/D;AACA,UAAM,OAAO,QAAQ;AACrB,WAAO,IAAI,UAAU,KAAK;AAAA,EAC5B;AAAA,EAEA,OACE,WAGK;AACL,qCAAY,WAAW,GAAG,UAAU,WAAW;AAC/C,UAAM,QAAQ,KAAK,UAAU;AAC7B,QAAI,MAAM,UAAU,UAAU,qBAAqB;AACjD,YAAM,IAAI;AAAA,QACR,wCAAwC,mBAAmB;AAAA,MAC7D;AAAA,IACF;AACA,UAAM,UAAU,KAAK;AAAA,MACnB,YAAQ,gDAAoB,UAAU,4CAAiB,CAAC;AAAA,IAC1D,CAAC;AACD,WAAO,IAAI,UAAU,KAAK;AAAA,EAC5B;AAAA,EAEA,MAAM,GAAgB;AACpB,qCAAY,GAAG,GAAG,SAAS,GAAG;AAC9B,UAAM,QAAQ,KAAK,UAAU;AAC7B,UAAM,UAAU,KAAK,EAAE,OAAO,EAAE,CAAC;AACjC,WAAO,IAAI,UAAU,KAAK;AAAA,EAC5B;AAAA,EAEA,CAAC,OAAO,aAAa,IAAgC;AACnD,SAAK,WAAW;AAChB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,OAAqC;AACzC,QAAI,KAAK,MAAM,SAAS,YAAY,KAAK,MAAM,SAAS,YAAY;AAClE,uBAAiB,KAAK,MAAM,IAAI;AAAA,IAClC;AAIA,UAAM,UACJ,KAAK,MAAM,SAAS,cAAc,KAAK,WAAW,IAAI,KAAK,MAAM;AACnE,UAAM,EAAE,OAAO,KAAK,IAAI,UAAM,oCAAoB,uBAAuB;AAAA,MACvE;AAAA,IACF,CAAC;AACD,QAAI,MAAM;AACR,WAAK,WAAW;AAAA,IAClB;AACA,UAAM,kBAAc,4BAAa,KAAK;AACtC,WAAO,EAAE,OAAO,aAAa,KAAK;AAAA,EACpC;AAAA,EAEA,SAAS;AACP,SAAK,WAAW;AAChB,WAAO,QAAQ,QAAQ,EAAE,MAAM,MAAM,OAAO,OAAU,CAAC;AAAA,EACzD;AAAA,EAEA,MAAM,SACJ,gBACgC;AAChC,qCAAY,gBAAgB,GAAG,YAAY,SAAS;AACpD,QACE,OAAO,gBAAgB,aAAa,YACpC,eAAe,WAAW,GAC1B;AACA,YAAM,IAAI;AAAA,QACR,8DAA8D,gBAAgB,QAAQ;AAAA,MACxF;AAAA,IACF;AACA,UAAM,QAAQ,KAAK,UAAU;AAC7B,UAAM,WAAW,eAAe;AAChC,UAAM,SAAS,eAAe;AAC9B,UAAM,YAAY,gBAAgB,aAAa;AAC/C,UAAM,kBAAkB,eAAe,mBAAmB;AAC1D,UAAM,EAAE,MAAM,QAAQ,gBAAgB,aAAa,WAAW,IAC5D,UAAM,oCAAoB,iBAAiB;AAAA,MACzC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,kBAAkB,eAAe;AAAA,MACjC;AAAA,IACF,CAAC;AACH,WAAO;AAAA,MACL,MAAM,KAAK,IAAI,CAAC,aAAiB,4BAAa,IAAI,CAAC;AAAA,MACnD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,UAA+B;AACnC,UAAM,MAAe,CAAC;AACtB,qBAAiB,QAAQ,MAAM;AAC7B,UAAI,KAAK,IAAI;AAAA,IACf;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,GAAgC;AACzC,qCAAY,GAAG,GAAG,QAAQ,GAAG;AAC7B,yDAAgC,GAAG,GAAG,QAAQ,GAAG;AACjD,WAAO,KAAK,MAAM,CAAC,EAAE,QAAQ;AAAA,EAC/B;AAAA,EAEA,MAAM,QAA6B;AACjC,UAAM,cAAc,MAAM,KAAK,KAAK,CAAC;AACrC,WAAO,YAAY,WAAW,IAAI,OAAO,YAAY,CAAC;AAAA,EACxD;AAAA,EAEA,MAAM,SAA8B;AAClC,UAAM,kBAAkB,MAAM,KAAK,KAAK,CAAC;AACzC,QAAI,gBAAgB,WAAW,GAAG;AAChC,aAAO;AAAA,IACT;AACA,QAAI,gBAAgB,WAAW,GAAG;AAChC,YAAM,IAAI,MAAM;AAAA,IAClB,gBAAgB,CAAC,EAAE,GAAG,KAAK,gBAAgB,CAAC,EAAE,GAAG,QAAQ;AAAA,IACzD;AACA,WAAO,gBAAgB,CAAC;AAAA,EAC1B;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -51,12 +51,13 @@ export declare class ConvexClient {
|
|
|
51
51
|
private _client;
|
|
52
52
|
private callNewListenersWithCurrentValuesTimer;
|
|
53
53
|
private _closed;
|
|
54
|
-
|
|
54
|
+
private _disabled;
|
|
55
55
|
/**
|
|
56
56
|
* Once closed no registered callbacks will fire again.
|
|
57
57
|
*/
|
|
58
58
|
get closed(): boolean;
|
|
59
59
|
get client(): BaseConvexClient;
|
|
60
|
+
get disabled(): boolean;
|
|
60
61
|
/**
|
|
61
62
|
* Construct a client and immediately initiate a WebSocket connection to the passed address.
|
|
62
63
|
*
|
|
@@ -104,7 +105,7 @@ export declare class ConvexClient {
|
|
|
104
105
|
* `fetchToken` will be called automatically again if a token expires.
|
|
105
106
|
* `fetchToken` should return `null` if the token cannot be retrieved, for example
|
|
106
107
|
* when the user's rights were permanently revoked.
|
|
107
|
-
* @param fetchToken - an async function returning the JWT
|
|
108
|
+
* @param fetchToken - an async function returning the JWT (typically an OpenID Connect Identity Token)
|
|
108
109
|
* @param onChange - a callback that will be called when the authentication status changes
|
|
109
110
|
*/
|
|
110
111
|
setAuth(fetchToken: AuthTokenFetcher, onChange?: (isAuthenticated: boolean) => void): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simple_client.d.ts","sourceRoot":"","sources":["../../../src/browser/simple_client.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EAGxB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAOpE,eAAe;AACf,wBAAgB,8BAA8B,CAAC,EAAE,EAAE,OAAO,SAAS,QAElE;AAED,MAAM,MAAM,mBAAmB,GAAG,uBAAuB,GAAG;IAC1D;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,2HAA2H;IAC3H,IAAI,IAAI,CAAC;IACT,2HAA2H;IAC3H,WAAW,IAAI,IAAI,CAAC;IACpB,gFAAgF;IAChF,eAAe,IAAI,CAAC,GAAG,SAAS,CAAC;CAGlC,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,OAAO,CAA+B;IAE9C,OAAO,CAAC,sCAAsC,CAEhC;IACd,OAAO,CAAC,OAAO,CAAU;IACzB,
|
|
1
|
+
{"version":3,"file":"simple_client.d.ts","sourceRoot":"","sources":["../../../src/browser/simple_client.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EAGxB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAOpE,eAAe;AACf,wBAAgB,8BAA8B,CAAC,EAAE,EAAE,OAAO,SAAS,QAElE;AAED,MAAM,MAAM,mBAAmB,GAAG,uBAAuB,GAAG;IAC1D;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,2HAA2H;IAC3H,IAAI,IAAI,CAAC;IACT,2HAA2H;IAC3H,WAAW,IAAI,IAAI,CAAC;IACpB,gFAAgF;IAChF,eAAe,IAAI,CAAC,GAAG,SAAS,CAAC;CAGlC,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,OAAO,CAA+B;IAE9C,OAAO,CAAC,sCAAsC,CAEhC;IACd,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,SAAS,CAAU;IAC3B;;OAEG;IACH,IAAI,MAAM,IAAI,OAAO,CAEpB;IACD,IAAI,MAAM,IAAI,gBAAgB,CAG7B;IACD,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;;OAIG;gBACS,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB;IA8B9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,QAAQ,CAAC,KAAK,SAAS,iBAAiB,CAAC,OAAO,CAAC,EAC/C,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,YAAY,CAAC,KAAK,CAAC,EACzB,QAAQ,EAAE,CAAC,MAAM,EAAE,kBAAkB,CAAC,KAAK,CAAC,KAAK,OAAO,EACxD,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,OAAO,GAC9B,WAAW,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAsEpC,OAAO,CAAC,iCAAiC;IAKzC,OAAO,CAAC,gBAAgB;IAIlB,KAAK;IAQX;;;;;;;OAOG;IACH,OAAO,CACL,UAAU,EAAE,gBAAgB,EAC5B,QAAQ,CAAC,EAAE,CAAC,eAAe,EAAE,OAAO,KAAK,IAAI;IA+D/C;;;;;;;;OAQG;IACG,QAAQ,CAAC,QAAQ,SAAS,iBAAiB,CAAC,UAAU,CAAC,EAC3D,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC,GAC3B,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAKjD;;;;;;;OAOG;IACG,MAAM,CAAC,MAAM,SAAS,iBAAiB,CAAC,QAAQ,CAAC,EACrD,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,GACzB,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC;IAK/C;;;;;;;OAOG;IACG,KAAK,CAAC,KAAK,SAAS,iBAAiB,CAAC,OAAO,CAAC,EAClD,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,GACnB,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;CAsB1C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/bundler/index.ts"],"names":[],"mappings":"AAEA,OAAO,OAAyB,MAAM,SAAS,CAAC;AAIhD,OAAO,EAAE,UAAU,EAAsB,MAAM,SAAS,CAAC;AACzD,OAAO,EAAE,OAAO,EAA0B,MAAM,cAAc,CAAC;AAQ/D,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC9C,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,eAAO,MAAM,UAAU,YAAY,CAAC;AAKpC,wBAAiB,OAAO,CACtB,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,GACb,SAAS,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAWxE;AAGD,KAAK,iBAAiB,GAAG,MAAM,GAAG,SAAS,CAAC;AAE5C,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,iBAAiB,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,iBAAiB,CAAC;CAChC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/bundler/index.ts"],"names":[],"mappings":"AAEA,OAAO,OAAyB,MAAM,SAAS,CAAC;AAIhD,OAAO,EAAE,UAAU,EAAsB,MAAM,SAAS,CAAC;AACzD,OAAO,EAAE,OAAO,EAA0B,MAAM,cAAc,CAAC;AAQ/D,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC9C,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,eAAO,MAAM,UAAU,YAAY,CAAC;AAKpC,wBAAiB,OAAO,CACtB,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,GACb,SAAS,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAWxE;AAGD,KAAK,iBAAiB,GAAG,MAAM,GAAG,SAAS,CAAC;AAE5C,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,iBAAiB,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,iBAAiB,CAAC;CAChC;AA0HD,wBAAsB,MAAM,CAC1B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EAAE,EACrB,kBAAkB,EAAE,OAAO,EAC3B,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAC1B,YAAY,SAAU,EACtB,yBAAyB,GAAE,MAAM,EAAO,EACxC,eAAe,GAAE,MAAM,EAAO,GAC7B,OAAO,CAAC;IACT,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACjC,CAAC,CAyDD;AAuCD,wBAAsB,YAAY,CAChC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,MAAM,EAAE,qBAgB1B;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,qBAkB/D;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE,MAAM,oBAqB9D;AAkBD,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,EAAE,CAAC,CAoFnB;AAGD,eAAO,MAAM,qBAAqB,QAAiC,CAAC;AA8CpE,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAKtD;AAiCD,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM;;;GAavE"}
|