convex 1.42.2 → 1.43.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 +8 -1
- package/bin/main-dev +6 -1
- package/dist/browser.bundle.js +49 -10
- package/dist/browser.bundle.js.map +2 -2
- package/dist/cjs/browser/index.js.map +2 -2
- package/dist/cjs/browser/simple_client-node.js +35 -35
- package/dist/cjs/browser/simple_client-node.js.map +2 -2
- package/dist/cjs/browser/sync/client.js.map +1 -1
- package/dist/cjs/browser/sync/paginated_query_client.js +20 -9
- package/dist/cjs/browser/sync/paginated_query_client.js.map +2 -2
- package/dist/cjs/cli/codegen_templates/component_api.js +2 -2
- package/dist/cjs/cli/codegen_templates/component_api.js.map +2 -2
- package/dist/cjs/cli/codegen_templates/validator_helpers.js +2 -0
- package/dist/cjs/cli/codegen_templates/validator_helpers.js.map +2 -2
- package/dist/cjs/cli/deployment.js +2 -1
- package/dist/cjs/cli/deployment.js.map +2 -2
- package/dist/cjs/cli/deploymentCreate.js.map +2 -2
- package/dist/cjs/cli/generatedApi.js.map +1 -1
- package/dist/cjs/cli/lib/deployApi/validator.js +1 -0
- package/dist/cjs/cli/lib/deployApi/validator.js.map +2 -2
- package/dist/cjs/cli/lib/deploymentSelection.js.map +2 -2
- package/dist/cjs/cli/lib/generateDocs.js +1 -1
- package/dist/cjs/cli/lib/generateDocs.js.map +2 -2
- package/dist/cjs/cli/lib/generatedFunctionLogsApi.js.map +1 -1
- package/dist/cjs/cli/lib/localDeployment/projectMismatch.js.map +2 -2
- package/dist/cjs/cli/lib/login.js +23 -23
- package/dist/cjs/cli/lib/login.js.map +2 -2
- package/dist/cjs/cli/lib/typecheck.js +28 -25
- package/dist/cjs/cli/lib/typecheck.js.map +2 -2
- package/dist/cjs/cli/lib/usageLimits.js +164 -0
- package/dist/cjs/cli/lib/usageLimits.js.map +7 -0
- package/dist/cjs/cli/usageLimits.js +326 -0
- package/dist/cjs/cli/usageLimits.js.map +7 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react/client.js.map +2 -2
- package/dist/cjs/server/components/index.js.map +2 -2
- package/dist/cjs/server/cron.js +33 -29
- package/dist/cjs/server/cron.js.map +2 -2
- package/dist/cjs/server/database.js.map +1 -1
- package/dist/cjs/server/impl/database_impl.js +3 -0
- package/dist/cjs/server/impl/database_impl.js.map +2 -2
- package/dist/cjs/server/log.js +2 -1
- package/dist/cjs/server/log.js.map +2 -2
- package/dist/cjs/server/logVars.js +12 -1
- package/dist/cjs/server/logVars.js.map +2 -2
- package/dist/cjs/server/meta.js.map +1 -1
- package/dist/cjs/values/compare.js +5 -1
- package/dist/cjs/values/compare.js.map +2 -2
- package/dist/cjs/values/index.js +1 -0
- package/dist/cjs/values/index.js.map +2 -2
- package/dist/cjs/values/size.js +4 -0
- package/dist/cjs/values/size.js.map +2 -2
- package/dist/cjs/values/validator.js +11 -0
- package/dist/cjs/values/validator.js.map +2 -2
- package/dist/cjs/values/validators.js +20 -0
- package/dist/cjs/values/validators.js.map +2 -2
- package/dist/cjs/values/value.js +31 -0
- package/dist/cjs/values/value.js.map +2 -2
- package/dist/cjs-types/browser/index.d.ts.map +1 -1
- package/dist/cjs-types/browser/sync/paginated_query_client.d.ts +1 -38
- package/dist/cjs-types/browser/sync/paginated_query_client.d.ts.map +1 -1
- package/dist/cjs-types/cli/codegen_templates/component_api.d.ts.map +1 -1
- package/dist/cjs-types/cli/codegen_templates/validator_helpers.d.ts.map +1 -1
- package/dist/cjs-types/cli/codegen_templates/validator_helpers.test.d.ts +2 -0
- package/dist/cjs-types/cli/codegen_templates/validator_helpers.test.d.ts.map +1 -0
- package/dist/cjs-types/cli/deployment.d.ts.map +1 -1
- package/dist/cjs-types/cli/deploymentCreate.d.ts +2 -2
- package/dist/cjs-types/cli/generatedApi.d.ts +43 -43
- package/dist/cjs-types/cli/generatedApi.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/deployApi/validator.d.ts +6 -0
- package/dist/cjs-types/cli/lib/deployApi/validator.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/deploymentSelection.d.ts +2 -2
- package/dist/cjs-types/cli/lib/deploymentSelection.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts +45 -45
- package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/localDeployment/projectMismatch.d.ts +2 -2
- package/dist/cjs-types/cli/lib/localDeployment/projectMismatch.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/login.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/mcp/tools/insights.d.ts +24 -24
- package/dist/cjs-types/cli/lib/typecheck.d.ts +2 -0
- package/dist/cjs-types/cli/lib/typecheck.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/typecheck.test.d.ts +2 -0
- package/dist/cjs-types/cli/lib/typecheck.test.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/usageLimits.d.ts +46 -0
- package/dist/cjs-types/cli/lib/usageLimits.d.ts.map +1 -0
- package/dist/cjs-types/cli/usageLimits.d.ts +22 -0
- package/dist/cjs-types/cli/usageLimits.d.ts.map +1 -0
- package/dist/cjs-types/index.d.ts +1 -1
- package/dist/cjs-types/index.d.ts.map +1 -1
- package/dist/cjs-types/react/client.d.ts.map +1 -1
- package/dist/cjs-types/server/components/index.d.ts +26 -8
- package/dist/cjs-types/server/components/index.d.ts.map +1 -1
- package/dist/cjs-types/server/cron.d.ts +20 -11
- package/dist/cjs-types/server/cron.d.ts.map +1 -1
- package/dist/cjs-types/server/cron.test.d.ts +2 -0
- package/dist/cjs-types/server/cron.test.d.ts.map +1 -0
- package/dist/cjs-types/server/database.d.ts.map +1 -1
- package/dist/cjs-types/server/impl/database_impl.d.ts.map +1 -1
- package/dist/cjs-types/server/log.d.ts.map +1 -1
- package/dist/cjs-types/server/logVars.d.ts +14 -1
- package/dist/cjs-types/server/logVars.d.ts.map +1 -1
- package/dist/cjs-types/server/meta.d.ts +2 -2
- package/dist/cjs-types/server/meta.d.ts.map +1 -1
- package/dist/cjs-types/values/compare.d.ts.map +1 -1
- package/dist/cjs-types/values/index.d.ts +2 -2
- package/dist/cjs-types/values/index.d.ts.map +1 -1
- package/dist/cjs-types/values/size.d.ts +1 -1
- package/dist/cjs-types/values/size.d.ts.map +1 -1
- package/dist/cjs-types/values/validator.d.ts +10 -1
- package/dist/cjs-types/values/validator.d.ts.map +1 -1
- package/dist/cjs-types/values/validators.d.ts +14 -3
- package/dist/cjs-types/values/validators.d.ts.map +1 -1
- package/dist/cjs-types/values/value.d.ts +18 -1
- package/dist/cjs-types/values/value.d.ts.map +1 -1
- package/dist/cli.bundle.cjs +2327 -1866
- package/dist/cli.bundle.cjs.map +4 -4
- package/dist/esm/browser/index.js.map +2 -2
- package/dist/esm/browser/simple_client-node.js +35 -35
- package/dist/esm/browser/simple_client-node.js.map +1 -1
- package/dist/esm/browser/sync/client.js.map +1 -1
- package/dist/esm/browser/sync/paginated_query_client.js +20 -9
- package/dist/esm/browser/sync/paginated_query_client.js.map +2 -2
- package/dist/esm/cli/codegen_templates/component_api.js +8 -3
- package/dist/esm/cli/codegen_templates/component_api.js.map +2 -2
- package/dist/esm/cli/codegen_templates/validator_helpers.js +2 -0
- package/dist/esm/cli/codegen_templates/validator_helpers.js.map +2 -2
- package/dist/esm/cli/deployment.js +2 -1
- package/dist/esm/cli/deployment.js.map +2 -2
- package/dist/esm/cli/deploymentCreate.js.map +2 -2
- package/dist/esm/cli/lib/deployApi/validator.js +1 -0
- package/dist/esm/cli/lib/deployApi/validator.js.map +2 -2
- package/dist/esm/cli/lib/deploymentSelection.js.map +2 -2
- package/dist/esm/cli/lib/generateDocs.js +1 -1
- package/dist/esm/cli/lib/generateDocs.js.map +2 -2
- package/dist/esm/cli/lib/localDeployment/projectMismatch.js.map +2 -2
- package/dist/esm/cli/lib/login.js +23 -23
- package/dist/esm/cli/lib/login.js.map +2 -2
- package/dist/esm/cli/lib/typecheck.js +27 -25
- package/dist/esm/cli/lib/typecheck.js.map +2 -2
- package/dist/esm/cli/lib/usageLimits.js +129 -0
- package/dist/esm/cli/lib/usageLimits.js.map +7 -0
- package/dist/esm/cli/usageLimits.js +316 -0
- package/dist/esm/cli/usageLimits.js.map +7 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/client.js.map +2 -2
- package/dist/esm/server/components/index.js.map +2 -2
- package/dist/esm/server/cron.js +33 -29
- package/dist/esm/server/cron.js.map +2 -2
- package/dist/esm/server/impl/database_impl.js +4 -1
- package/dist/esm/server/impl/database_impl.js.map +2 -2
- package/dist/esm/server/log.js +3 -2
- package/dist/esm/server/log.js.map +2 -2
- package/dist/esm/server/logVars.js +11 -1
- package/dist/esm/server/logVars.js.map +2 -2
- package/dist/esm/values/compare.js +5 -1
- package/dist/esm/values/compare.js.map +2 -2
- package/dist/esm/values/index.js +1 -1
- package/dist/esm/values/index.js.map +2 -2
- package/dist/esm/values/size.js +4 -0
- package/dist/esm/values/size.js.map +2 -2
- package/dist/esm/values/validator.js +12 -0
- package/dist/esm/values/validator.js.map +2 -2
- package/dist/esm/values/validators.js +19 -0
- package/dist/esm/values/validators.js.map +2 -2
- package/dist/esm/values/value.js +29 -1
- package/dist/esm/values/value.js.map +2 -2
- package/dist/esm-types/browser/index.d.ts.map +1 -1
- package/dist/esm-types/browser/sync/paginated_query_client.d.ts +1 -38
- package/dist/esm-types/browser/sync/paginated_query_client.d.ts.map +1 -1
- package/dist/esm-types/cli/codegen_templates/component_api.d.ts.map +1 -1
- package/dist/esm-types/cli/codegen_templates/validator_helpers.d.ts.map +1 -1
- package/dist/esm-types/cli/codegen_templates/validator_helpers.test.d.ts +2 -0
- package/dist/esm-types/cli/codegen_templates/validator_helpers.test.d.ts.map +1 -0
- package/dist/esm-types/cli/deployment.d.ts.map +1 -1
- package/dist/esm-types/cli/deploymentCreate.d.ts +2 -2
- package/dist/esm-types/cli/generatedApi.d.ts +43 -43
- package/dist/esm-types/cli/generatedApi.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/deployApi/validator.d.ts +6 -0
- package/dist/esm-types/cli/lib/deployApi/validator.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/deploymentSelection.d.ts +2 -2
- package/dist/esm-types/cli/lib/deploymentSelection.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts +45 -45
- package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/localDeployment/projectMismatch.d.ts +2 -2
- package/dist/esm-types/cli/lib/localDeployment/projectMismatch.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/login.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/mcp/tools/insights.d.ts +24 -24
- package/dist/esm-types/cli/lib/typecheck.d.ts +2 -0
- package/dist/esm-types/cli/lib/typecheck.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/typecheck.test.d.ts +2 -0
- package/dist/esm-types/cli/lib/typecheck.test.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/usageLimits.d.ts +46 -0
- package/dist/esm-types/cli/lib/usageLimits.d.ts.map +1 -0
- package/dist/esm-types/cli/usageLimits.d.ts +22 -0
- package/dist/esm-types/cli/usageLimits.d.ts.map +1 -0
- package/dist/esm-types/index.d.ts +1 -1
- package/dist/esm-types/index.d.ts.map +1 -1
- package/dist/esm-types/react/client.d.ts.map +1 -1
- package/dist/esm-types/server/components/index.d.ts +26 -8
- package/dist/esm-types/server/components/index.d.ts.map +1 -1
- package/dist/esm-types/server/cron.d.ts +20 -11
- package/dist/esm-types/server/cron.d.ts.map +1 -1
- package/dist/esm-types/server/cron.test.d.ts +2 -0
- package/dist/esm-types/server/cron.test.d.ts.map +1 -0
- package/dist/esm-types/server/database.d.ts.map +1 -1
- package/dist/esm-types/server/impl/database_impl.d.ts.map +1 -1
- package/dist/esm-types/server/log.d.ts.map +1 -1
- package/dist/esm-types/server/logVars.d.ts +14 -1
- package/dist/esm-types/server/logVars.d.ts.map +1 -1
- package/dist/esm-types/server/meta.d.ts +2 -2
- package/dist/esm-types/server/meta.d.ts.map +1 -1
- package/dist/esm-types/values/compare.d.ts.map +1 -1
- package/dist/esm-types/values/index.d.ts +2 -2
- package/dist/esm-types/values/index.d.ts.map +1 -1
- package/dist/esm-types/values/size.d.ts +1 -1
- package/dist/esm-types/values/size.d.ts.map +1 -1
- package/dist/esm-types/values/validator.d.ts +10 -1
- package/dist/esm-types/values/validator.d.ts.map +1 -1
- package/dist/esm-types/values/validators.d.ts +14 -3
- package/dist/esm-types/values/validators.d.ts.map +1 -1
- package/dist/esm-types/values/value.d.ts +18 -1
- package/dist/esm-types/values/value.d.ts.map +1 -1
- package/dist/react.bundle.js +53 -11
- package/dist/react.bundle.js.map +2 -2
- package/package.json +8 -8
- package/schemas/convex.schema.json +2 -1
- package/src/browser/index.ts +2 -0
- package/src/browser/sync/client.test.ts +8 -1
- package/src/browser/sync/client.ts +63 -0
- package/src/browser/sync/paginated_query_client.test.ts +209 -1
- package/src/browser/sync/paginated_query_client.ts +29 -11
- package/src/cli/codegen_templates/component_api.ts +8 -3
- package/src/cli/codegen_templates/validator_helpers.test.ts +31 -0
- package/src/cli/codegen_templates/validator_helpers.ts +6 -0
- package/src/cli/deployment.ts +4 -1
- package/src/cli/deploymentCreate.test.ts +2 -2
- package/src/cli/deploymentCreate.ts +2 -2
- package/src/cli/generatedApi.ts +43 -43
- package/src/cli/lib/deployApi/validator.ts +1 -0
- package/src/cli/lib/deploymentSelection.ts +2 -2
- package/src/cli/lib/generateDocs.test.ts +1 -1
- package/src/cli/lib/generateDocs.ts +3 -3
- package/src/cli/lib/generatedFunctionLogsApi.ts +45 -45
- package/src/cli/lib/localDeployment/projectMismatch.ts +2 -2
- package/src/cli/lib/login.ts +33 -30
- package/src/cli/lib/typecheck.test.ts +71 -0
- package/src/cli/lib/typecheck.ts +33 -27
- package/src/cli/lib/usageLimits.ts +242 -0
- package/src/cli/usageLimits.ts +420 -0
- package/src/index.ts +1 -1
- package/src/react/ConvexAuthState.test.tsx +24 -8
- package/src/react/auth_websocket.test.tsx +39 -29
- package/src/react/client.ts +3 -2
- package/src/react/client_provided_base_client.test.tsx +154 -36
- package/src/server/audit_logging.test.ts +2 -0
- package/src/server/components/index.test.ts +33 -1
- package/src/server/components/index.ts +41 -14
- package/src/server/cron.test.ts +58 -0
- package/src/server/cron.ts +82 -21
- package/src/server/database.ts +17 -0
- package/src/server/impl/database_impl.ts +4 -1
- package/src/server/log.ts +4 -1
- package/src/server/logVars.ts +17 -1
- package/src/server/meta.ts +2 -2
- package/src/server/schema.test.ts +6 -1
- package/src/values/compare.ts +5 -2
- package/src/values/index.ts +2 -1
- package/src/values/size.test.ts +5 -0
- package/src/values/size.ts +5 -1
- package/src/values/validator.test.ts +16 -1
- package/src/values/validator.ts +13 -0
- package/src/values/validators.ts +29 -1
- package/src/values/value.test.ts +35 -0
- package/src/values/value.ts +56 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/browser/sync/client.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * BaseConvexClient should not be used directly and does not provide a stable\n * interface. It is a \"Base\" client not because it expects to be inherited from\n * but because other clients are built around it.\n *\n * BaseConvexClient is not Convex Function type-aware: it deals\n * with queries as functions that return Value, not the specific value.\n * Use a higher-level library to get types.\n */\nimport { version } from \"../../index.js\";\nimport { convexToJson, Value } from \"../../values/index.js\";\nimport {\n createHybridErrorStacktrace,\n forwardData,\n instantiateDefaultLogger,\n instantiateNoopLogger,\n logFatalError,\n Logger,\n} from \"../logging.js\";\nimport { LocalSyncState } from \"./local_state.js\";\nimport { RequestManager } from \"./request_manager.js\";\nimport {\n OptimisticLocalStore,\n OptimisticUpdate,\n} from \"./optimistic_updates.js\";\nimport {\n OptimisticQueryResults,\n QueryResultsMap,\n} from \"./optimistic_updates_impl.js\";\nimport {\n ActionRequest,\n MutationRequest,\n QueryId,\n QueryJournal,\n ServerMessage,\n RequestId,\n TS,\n UserIdentityAttributes,\n} from \"./protocol.js\";\nimport { RemoteQuerySet } from \"./remote_query_set.js\";\nimport { QueryToken, serializePathAndArgs } from \"./udf_path_utils.js\";\nimport { ReconnectMetadata, WebSocketManager } from \"./web_socket_manager.js\";\nimport { newSessionId } from \"./session.js\";\nimport { FunctionResult } from \"./function_result.js\";\nimport {\n AuthenticationManager,\n AuthTokenFetcher,\n} from \"./authentication_manager.js\";\nexport { type AuthTokenFetcher } from \"./authentication_manager.js\";\nimport { getMarksReport, mark, MarkName } from \"./metrics.js\";\nimport { parseArgs, validateDeploymentUrl } from \"../../common/index.js\";\nimport { ConvexError } from \"../../values/errors.js\";\nimport { jwtDecode } from \"../../vendor/jwt-decode/index.js\";\n\n/**\n * Options for {@link BaseConvexClient}.\n *\n * @public\n */\nexport interface BaseConvexClientOptions {\n /**\n * Whether to prompt the user if they have unsaved changes pending\n * when navigating away or closing a web page.\n *\n * This is only possible when the `window` object exists, i.e. in a browser.\n *\n * The default value is `true` in browsers.\n */\n unsavedChangesWarning?: boolean;\n /**\n * Specifies an alternate\n * [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)\n * constructor to use for client communication with the Convex cloud.\n * The default behavior is to use `WebSocket` from the global environment.\n */\n webSocketConstructor?: typeof WebSocket;\n /**\n * Adds additional logging for debugging purposes.\n *\n * The default value is `false`.\n */\n verbose?: boolean;\n /**\n * A logger, `true`, or `false`. If not provided or `true`, logs to the console.\n * If `false`, logs are not printed anywhere.\n *\n * You can construct your own logger to customize logging to log elsewhere.\n * A logger is an object with 4 methods: log(), warn(), error(), and logVerbose().\n * These methods can receive multiple arguments of any types, like console.log().\n */\n logger?: Logger | boolean;\n /**\n * Sends additional metrics to Convex for debugging purposes.\n *\n * The default value is `false`.\n */\n reportDebugInfoToConvex?: boolean;\n /**\n * This API is experimental: it may change or disappear.\n *\n * A function to call on receiving abnormal WebSocket close messages from the\n * connected Convex deployment. The content of these messages is not stable,\n * it is an implementation detail that may change.\n *\n * Consider this API an observability stopgap until higher level codes with\n * recommendations on what to do are available, which could be a more stable\n * interface instead of `string`.\n *\n * Check `connectionState` for more quantitative metrics about connection status.\n */\n onServerDisconnectError?: (message: string) => void;\n /**\n * Skip validating that the Convex deployment URL looks like\n * `https://happy-animal-123.convex.cloud` or localhost.\n *\n * This can be useful if running a self-hosted Convex backend that uses a different\n * URL.\n *\n * The default value is `false`\n */\n skipConvexDeploymentUrlCheck?: boolean;\n /**\n * If using auth, the number of seconds before a token expires that we should refresh it.\n *\n * The default value is `10`.\n */\n authRefreshTokenLeewaySeconds?: number;\n /**\n * This API is experimental: it may change or disappear.\n *\n * Whether query, mutation, and action requests should be held back\n * until the first auth token can be sent.\n *\n * Opting into this behavior works well for pages that should\n * only be viewed by authenticated clients.\n *\n * Defaults to false, not waiting for an auth token.\n */\n expectAuth?: boolean;\n /**\n * This API is experimental: it may change or disappear.\n *\n * When true, the client reuses the initial cached auth token instead\n * of immediately fetching a fresh one. This avoids a second\n * Authenticate message that causes the server to re-execute all\n * authenticated queries.\n *\n * The cached token's remaining lifetime is estimated using the\n * server's clock skew measurement, and a refresh is scheduled\n * before it expires.\n *\n * Defaults to false, preserving the original behavior of immediately\n * fetching a fresh token after the cached token is confirmed.\n */\n initialAuthTokenReuse?: boolean;\n}\n\n/**\n * State describing the client's connection with the Convex backend.\n *\n * @public\n */\nexport type ConnectionState = {\n hasInflightRequests: boolean;\n isWebSocketConnected: boolean;\n timeOfOldestInflightRequest: Date | null;\n /**\n * True if the client has ever opened a WebSocket to the \"ready\" state.\n */\n hasEverConnected: boolean;\n /**\n * The number of times this client has connected to the Convex backend.\n *\n * A number of things can cause the client to reconnect -- server errors,\n * bad internet, auth expiring. But this number being high is an indication\n * that the client is having trouble keeping a stable connection.\n */\n connectionCount: number;\n /**\n * The number of times this client has tried (and failed) to connect to the Convex backend.\n */\n connectionRetries: number;\n /**\n * The number of mutations currently in flight.\n */\n inflightMutations: number;\n /**\n * The number of actions currently in flight.\n */\n inflightActions: number;\n};\n\n/**\n * Options for {@link BaseConvexClient.subscribe}.\n *\n * @public\n */\nexport interface SubscribeOptions {\n /**\n * An (optional) journal produced from a previous execution of this query\n * function.\n *\n * If there is an existing subscription to a query function with the same\n * name and arguments, this journal will have no effect.\n */\n journal?: QueryJournal;\n\n /**\n * @internal\n */\n componentPath?: string | undefined;\n}\n\n/**\n * Options for {@link BaseConvexClient.mutation}.\n *\n * @public\n */\nexport interface MutationOptions {\n /**\n * An optimistic update to apply along with this mutation.\n *\n * An optimistic update locally updates queries while a mutation is pending.\n * Once the mutation completes, the update will be rolled back.\n */\n optimisticUpdate?: OptimisticUpdate<any> | undefined;\n}\n\n/**\n * Type describing updates to a query within a `Transition`.\n *\n * @public\n */\nexport type QueryModification =\n // `undefined` generally comes from an optimistic update setting the query to be loading\n { kind: \"Updated\"; result: FunctionResult | undefined } | { kind: \"Removed\" };\n\n/**\n * Object describing a transition passed into the `onTransition` handler.\n *\n * These can be from receiving a transition from the server, or from applying an\n * optimistic update locally.\n *\n * @public\n */\nexport type Transition = {\n queries: Array<{ token: QueryToken; modification: QueryModification }>;\n reflectedMutations: Array<{ requestId: RequestId; result: FunctionResult }>;\n timestamp: TS;\n};\n\n/**\n * Low-level client for directly integrating state management libraries\n * with Convex.\n *\n * Most developers should use higher level clients, like\n * the {@link ConvexHttpClient} or the React hook based {@link react.ConvexReactClient}.\n *\n * @public\n */\nexport class BaseConvexClient {\n private readonly address: string;\n private readonly state: LocalSyncState;\n private readonly requestManager: RequestManager;\n private readonly webSocketManager: WebSocketManager;\n private readonly authenticationManager: AuthenticationManager;\n private remoteQuerySet: RemoteQuerySet;\n private readonly optimisticQueryResults: OptimisticQueryResults;\n private _transitionHandlerCounter = 0;\n private _nextRequestId: RequestId;\n private _onTransitionFns: Map<number, (transition: Transition) => void> =\n new Map();\n private readonly _sessionId: string;\n private firstMessageReceived = false;\n private readonly debug: boolean;\n private readonly logger: Logger;\n private maxObservedTimestamp: TS | undefined;\n private connectionStateSubscribers = new Map<\n number,\n (connectionState: ConnectionState) => void\n >();\n private nextConnectionStateSubscriberId: number = 0;\n private _lastPublishedConnectionState: ConnectionState | undefined;\n\n /**\n * @param address - The url of your Convex deployment, often provided\n * by an environment variable. E.g. `https://small-mouse-123.convex.cloud`.\n * @param onTransition - A callback receiving an array of query tokens\n * corresponding to query results that have changed -- additional handlers\n * can be added via `addOnTransitionHandler`.\n * @param options - See {@link BaseConvexClientOptions} for a full description.\n */\n constructor(\n address: string,\n onTransition: (updatedQueries: QueryToken[]) => void,\n options?: BaseConvexClientOptions,\n ) {\n if (typeof address === \"object\") {\n throw new Error(\n \"Passing a ClientConfig object is no longer supported. Pass the URL of the Convex deployment as a string directly.\",\n );\n }\n if (options?.skipConvexDeploymentUrlCheck !== true) {\n validateDeploymentUrl(address);\n }\n options = { ...options };\n const authRefreshTokenLeewaySeconds =\n options.authRefreshTokenLeewaySeconds ?? 10;\n let webSocketConstructor = options.webSocketConstructor;\n if (!webSocketConstructor && typeof WebSocket === \"undefined\") {\n throw new Error(\n \"No WebSocket global variable defined! To use Convex in an environment without WebSocket try the HTTP client: https://docs.convex.dev/api/classes/browser.ConvexHttpClient\",\n );\n }\n webSocketConstructor = webSocketConstructor || WebSocket;\n this.debug = options.reportDebugInfoToConvex ?? false;\n this.address = address;\n this.logger =\n options.logger === false\n ? instantiateNoopLogger({ verbose: options.verbose ?? false })\n : options.logger !== true && options.logger\n ? options.logger\n : instantiateDefaultLogger({ verbose: options.verbose ?? false });\n // Substitute http(s) with ws(s)\n const i = address.search(\"://\");\n if (i === -1) {\n throw new Error(\"Provided address was not an absolute URL.\");\n }\n const origin = address.substring(i + 3); // move past the double slash\n const protocol = address.substring(0, i);\n let wsProtocol;\n if (protocol === \"http\") {\n wsProtocol = \"ws\";\n } else if (protocol === \"https\") {\n wsProtocol = \"wss\";\n } else {\n throw new Error(`Unknown parent protocol ${protocol}`);\n }\n const wsUri = `${wsProtocol}://${origin}/api/${version}/sync`;\n\n this.state = new LocalSyncState();\n this.remoteQuerySet = new RemoteQuerySet(\n (queryId) => this.state.queryPath(queryId),\n this.logger,\n );\n this.requestManager = new RequestManager(\n this.logger,\n this.markConnectionStateDirty,\n );\n\n // This is a callback for AuthenticationManager (which can't call\n // this synchronously, the callback wouldn't work) so the initial\n // pause for expectAuth we call it at the end of this constructor.\n const pauseSocket = () => {\n this.webSocketManager.pause();\n this.state.pause();\n };\n this.authenticationManager = new AuthenticationManager(\n this.state,\n {\n authenticate: (token) => {\n const message = this.state.setAuth(token);\n this.webSocketManager.sendMessage(message);\n return message.baseVersion;\n },\n stopSocket: () => this.webSocketManager.stop(),\n tryRestartSocket: () => this.webSocketManager.tryRestart(),\n pauseSocket,\n resumeSocket: () => this.webSocketManager.resume(),\n clearAuth: () => {\n this.clearAuth();\n },\n },\n {\n logger: this.logger,\n refreshTokenLeewaySeconds: authRefreshTokenLeewaySeconds,\n initialAuthTokenReuse: options.initialAuthTokenReuse ?? false,\n },\n );\n this.optimisticQueryResults = new OptimisticQueryResults();\n this.addOnTransitionHandler((transition) => {\n onTransition(transition.queries.map((q) => q.token));\n });\n this._nextRequestId = 0;\n this._sessionId = newSessionId();\n\n const { unsavedChangesWarning } = options;\n if (\n typeof window === \"undefined\" ||\n typeof window.addEventListener === \"undefined\"\n ) {\n if (unsavedChangesWarning === true) {\n throw new Error(\n \"unsavedChangesWarning requested, but window.addEventListener not found! Remove {unsavedChangesWarning: true} from Convex client options.\",\n );\n }\n } else if (unsavedChangesWarning !== false) {\n // Listen for tab close events and notify the user on unsaved changes.\n window.addEventListener(\"beforeunload\", (e) => {\n if (this.requestManager.hasIncompleteRequests()) {\n // There are 3 different ways to trigger this pop up so just try all of\n // them.\n\n e.preventDefault();\n // This confirmation message doesn't actually appear in most modern\n // browsers but we tried.\n const confirmationMessage =\n \"Are you sure you want to leave? Your changes may not be saved.\";\n // Recommended method for legacy (IE) browsers.\n // casts to avoid deprecation notices\n ((e || (window as any).event) as any).returnValue =\n confirmationMessage;\n return confirmationMessage;\n }\n });\n }\n\n this.webSocketManager = new WebSocketManager(\n wsUri,\n {\n onOpen: (reconnectMetadata: ReconnectMetadata) => {\n // We have a new WebSocket!\n this.mark(\"convexWebSocketOpen\");\n this.webSocketManager.sendMessage({\n ...reconnectMetadata,\n type: \"Connect\",\n sessionId: this._sessionId,\n maxObservedTimestamp: this.maxObservedTimestamp,\n });\n\n // Throw out our remote query, reissue queries\n // and outstanding mutations, and reauthenticate.\n this.remoteQuerySet = new RemoteQuerySet(\n (queryId) => this.state.queryPath(queryId),\n this.logger,\n );\n const [querySetModification, authModification] = this.state.restart();\n if (authModification) {\n this.webSocketManager.sendMessage(authModification);\n }\n this.webSocketManager.sendMessage(querySetModification);\n for (const message of this.requestManager.restart()) {\n this.webSocketManager.sendMessage(message);\n }\n },\n onResume: () => {\n const [querySetModification, authModification] = this.state.resume();\n if (authModification) {\n this.webSocketManager.sendMessage(authModification);\n }\n if (querySetModification) {\n this.webSocketManager.sendMessage(querySetModification);\n }\n for (const message of this.requestManager.resume()) {\n this.webSocketManager.sendMessage(message);\n }\n },\n onMessage: (serverMessage: ServerMessage) => {\n // Metrics events grow linearly with reconnection attempts so this\n // conditional prevents n^2 metrics reporting.\n if (!this.firstMessageReceived) {\n this.firstMessageReceived = true;\n this.mark(\"convexFirstMessageReceived\");\n this.reportMarks();\n }\n switch (serverMessage.type) {\n case \"Transition\": {\n this.observedTimestamp(serverMessage.endVersion.ts);\n this.authenticationManager.onTransition(serverMessage);\n this.remoteQuerySet.transition(serverMessage);\n this.state.transition(serverMessage);\n const completedRequests = this.requestManager.removeCompleted(\n this.remoteQuerySet.timestamp(),\n );\n this.notifyOnQueryResultChanges(completedRequests);\n break;\n }\n case \"MutationResponse\": {\n if (serverMessage.success) {\n this.observedTimestamp(serverMessage.ts);\n }\n const completedMutationInfo =\n this.requestManager.onResponse(serverMessage);\n if (completedMutationInfo !== null) {\n this.notifyOnQueryResultChanges(\n new Map([\n [\n completedMutationInfo.requestId,\n completedMutationInfo.result,\n ],\n ]),\n );\n }\n break;\n }\n case \"ActionResponse\": {\n this.requestManager.onResponse(serverMessage);\n break;\n }\n case \"AuthError\": {\n this.authenticationManager.onAuthError(serverMessage);\n break;\n }\n case \"FatalError\": {\n const error = logFatalError(this.logger, serverMessage.error);\n void this.webSocketManager.terminate();\n throw error;\n }\n default: {\n serverMessage satisfies never;\n }\n }\n\n return {\n hasSyncedPastLastReconnect: this.hasSyncedPastLastReconnect(),\n };\n },\n onServerDisconnectError: options.onServerDisconnectError,\n },\n webSocketConstructor,\n this.logger,\n this.markConnectionStateDirty,\n this.debug,\n );\n this.mark(\"convexClientConstructed\");\n\n // Begin client in a paused state waiting for an auth token.\n if (options.expectAuth) {\n pauseSocket();\n }\n }\n\n /**\n * Return true if there is outstanding work from prior to the time of the most recent restart.\n * This indicates that the client has not proven itself to have gotten past the issue that\n * potentially led to the restart. Use this to influence when to reset backoff after a failure.\n */\n private hasSyncedPastLastReconnect() {\n const hasSyncedPastLastReconnect =\n this.requestManager.hasSyncedPastLastReconnect() &&\n this.state.hasSyncedPastLastReconnect();\n return hasSyncedPastLastReconnect;\n }\n\n private observedTimestamp(observedTs: TS) {\n if (\n this.maxObservedTimestamp === undefined ||\n this.maxObservedTimestamp.lessThanOrEqual(observedTs)\n ) {\n this.maxObservedTimestamp = observedTs;\n }\n }\n\n getMaxObservedTimestamp() {\n return this.maxObservedTimestamp;\n }\n\n /**\n * Compute the current query results based on the remoteQuerySet and the\n * current optimistic updates and call `onTransition` for all the changed\n * queries.\n *\n * @param completedMutations - A set of mutation IDs whose optimistic updates\n * are no longer needed.\n */\n private notifyOnQueryResultChanges(\n completedRequests: Map<RequestId, FunctionResult>,\n ) {\n const remoteQueryResults: Map<QueryId, FunctionResult> =\n this.remoteQuerySet.remoteQueryResults();\n const queryTokenToValue: QueryResultsMap = new Map();\n for (const [queryId, result] of remoteQueryResults) {\n const queryToken = this.state.queryToken(queryId);\n // It's possible that we've already unsubscribed to this query but\n // the server hasn't learned about that yet. If so, ignore this one.\n\n if (queryToken !== null) {\n const query = {\n result,\n udfPath: this.state.queryPath(queryId)!,\n args: this.state.queryArgs(queryId)!,\n };\n queryTokenToValue.set(queryToken, query);\n }\n }\n\n // Query tokens that are new (because of new server results or new local optimistic updates)\n // or differ from old values (because of changes from local optimistic updates or new results\n // from the server).\n const changedQueryTokens =\n this.optimisticQueryResults.ingestQueryResultsFromServer(\n queryTokenToValue,\n new Set(completedRequests.keys()),\n );\n\n this.handleTransition({\n queries: changedQueryTokens.map((token) => {\n const optimisticResult =\n this.optimisticQueryResults.rawQueryResult(token);\n return {\n token,\n modification: {\n kind: \"Updated\" as const,\n result: optimisticResult,\n },\n };\n }),\n reflectedMutations: Array.from(completedRequests).map(\n ([requestId, result]) => ({\n requestId,\n result,\n }),\n ),\n timestamp: this.remoteQuerySet.timestamp(),\n });\n }\n\n private handleTransition(transition: Transition) {\n for (const fn of this._onTransitionFns.values()) {\n fn(transition);\n }\n }\n\n /**\n * Add a handler that will be called on a transition.\n *\n * Any external side effects (e.g. setting React state) should be handled here.\n *\n * @param fn\n *\n * @returns\n */\n addOnTransitionHandler(fn: (transition: Transition) => void) {\n const id = this._transitionHandlerCounter++;\n this._onTransitionFns.set(id, fn);\n return () => this._onTransitionFns.delete(id);\n }\n\n /**\n * Get the current JWT auth token and decoded claims.\n */\n getCurrentAuthClaims():\n | { token: string; decoded: Record<string, any> }\n | undefined {\n const authToken = this.state.getAuth();\n let decoded: Record<string, any> = {};\n if (authToken && authToken.tokenType === \"User\") {\n try {\n decoded = authToken ? jwtDecode(authToken.value) : {};\n } catch {\n decoded = {};\n }\n } else {\n return undefined;\n }\n return { token: authToken.value, decoded };\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 * @param onRefreshChange - a callback called with `true` when the socket is paused to fetch a replacement token after a server rejection, and `false` when refresh completes\n */\n setAuth(\n fetchToken: AuthTokenFetcher,\n onChange: (isAuthenticated: boolean) => void,\n onRefreshChange?: (isRefreshing: boolean) => void,\n ) {\n void this.authenticationManager.setConfig(\n fetchToken,\n onChange,\n onRefreshChange,\n );\n }\n\n hasAuth() {\n return this.state.hasAuth();\n }\n\n /** @internal */\n setAdminAuth(value: string, fakeUserIdentity?: UserIdentityAttributes) {\n const message = this.state.setAdminAuth(value, fakeUserIdentity);\n this.webSocketManager.sendMessage(message);\n }\n\n clearAuth() {\n const message = this.state.clearAuth();\n this.webSocketManager.sendMessage(message);\n }\n\n /**\n * Subscribe to a query function.\n *\n * Whenever this query's result changes, the `onTransition` callback\n * passed into the constructor will be called.\n *\n * @param name - The name of the query.\n * @param args - An arguments object for the query. If this is omitted, the\n * arguments will be `{}`.\n * @param options - A {@link SubscribeOptions} options object for this query.\n\n * @returns An object containing a {@link QueryToken} corresponding to this\n * query and an `unsubscribe` callback.\n */\n subscribe(\n name: string,\n args?: Record<string, Value>,\n options?: SubscribeOptions,\n ): { queryToken: QueryToken; unsubscribe: () => void } {\n const argsObject = parseArgs(args);\n\n const { modification, queryToken, unsubscribe } = this.state.subscribe(\n name,\n argsObject,\n options?.journal,\n options?.componentPath,\n );\n if (modification !== null) {\n this.webSocketManager.sendMessage(modification);\n }\n return {\n queryToken,\n unsubscribe: () => {\n const modification = unsubscribe();\n if (modification) {\n this.webSocketManager.sendMessage(modification);\n }\n },\n };\n }\n\n /**\n * A query result based only on the current, local state.\n *\n * The only way this will return a value is if we're already subscribed to the\n * query or its value has been set optimistically.\n */\n localQueryResult(\n udfPath: string,\n args?: Record<string, Value>,\n ): Value | undefined {\n const argsObject = parseArgs(args);\n const queryToken = serializePathAndArgs(udfPath, argsObject);\n return this.optimisticQueryResults.queryResult(queryToken);\n }\n\n /**\n * Get query result by query token based on current, local state\n *\n * The only way this will return a value is if we're already subscribed to the\n * query or its value has been set optimistically.\n *\n * @internal\n */\n localQueryResultByToken(queryToken: QueryToken): Value | undefined {\n return this.optimisticQueryResults.queryResult(queryToken);\n }\n\n /**\n * Whether local query result is available for a token.\n *\n * This method does not throw if the result is an error.\n *\n * @internal\n */\n hasLocalQueryResultByToken(queryToken: QueryToken): boolean {\n return this.optimisticQueryResults.hasQueryResult(queryToken);\n }\n\n /**\n * @internal\n */\n localQueryLogs(\n udfPath: string,\n args?: Record<string, Value>,\n ): string[] | undefined {\n const argsObject = parseArgs(args);\n const queryToken = serializePathAndArgs(udfPath, argsObject);\n return this.optimisticQueryResults.queryLogs(queryToken);\n }\n\n /**\n * Retrieve the current {@link QueryJournal} for this query function.\n *\n * If we have not yet received a result for this query, this will be `undefined`.\n *\n * @param name - The name of the query.\n * @param args - The arguments object for this query.\n * @returns The query's {@link QueryJournal} or `undefined`.\n */\n queryJournal(\n name: string,\n args?: Record<string, Value>,\n ): QueryJournal | undefined {\n const argsObject = parseArgs(args);\n const queryToken = serializePathAndArgs(name, argsObject);\n return this.state.queryJournal(queryToken);\n }\n\n /**\n * Get the current {@link ConnectionState} between the client and the Convex\n * backend.\n *\n * @returns The {@link ConnectionState} with the Convex backend.\n */\n connectionState(): ConnectionState {\n const wsConnectionState = this.webSocketManager.connectionState();\n return {\n hasInflightRequests: this.requestManager.hasInflightRequests(),\n isWebSocketConnected: wsConnectionState.isConnected,\n hasEverConnected: wsConnectionState.hasEverConnected,\n connectionCount: wsConnectionState.connectionCount,\n connectionRetries: wsConnectionState.connectionRetries,\n timeOfOldestInflightRequest:\n this.requestManager.timeOfOldestInflightRequest(),\n inflightMutations: this.requestManager.inflightMutations(),\n inflightActions: this.requestManager.inflightActions(),\n };\n }\n\n /**\n * Call this whenever the connection state may have changed in a way that could\n * require publishing it. Schedules a possibly update.\n */\n private markConnectionStateDirty = () => {\n void Promise.resolve().then(() => {\n const curConnectionState = this.connectionState();\n if (\n JSON.stringify(curConnectionState) !==\n JSON.stringify(this._lastPublishedConnectionState)\n ) {\n this._lastPublishedConnectionState = curConnectionState;\n for (const cb of this.connectionStateSubscribers.values()) {\n // One of these callback throwing will prevent other callbacks\n // from running but will not leave the client in a undefined state.\n cb(curConnectionState);\n }\n }\n });\n };\n\n /**\n * Subscribe to the {@link ConnectionState} between the client and the Convex\n * backend, calling a callback each time it changes.\n *\n * Subscribed callbacks will be called when any part of ConnectionState changes.\n * ConnectionState may grow in future versions (e.g. to provide a array of\n * inflight requests) in which case callbacks would be called more frequently.\n *\n * @returns An unsubscribe function to stop listening.\n */\n subscribeToConnectionState(\n cb: (connectionState: ConnectionState) => void,\n ): () => void {\n const id = this.nextConnectionStateSubscriberId++;\n this.connectionStateSubscribers.set(id, cb);\n return () => {\n this.connectionStateSubscribers.delete(id);\n };\n }\n\n /**\n * Execute a mutation function.\n *\n * @param name - The name of the mutation.\n * @param args - An arguments object for the mutation. If this is omitted,\n * the arguments will be `{}`.\n * @param options - A {@link MutationOptions} options object for this mutation.\n\n * @returns - A promise of the mutation's result.\n */\n async mutation(\n name: string,\n args?: Record<string, Value>,\n options?: MutationOptions,\n ): Promise<any> {\n const result = await this.mutationInternal(name, args, options);\n if (!result.success) {\n if (result.errorData !== undefined) {\n throw forwardData(\n result,\n new ConvexError(\n createHybridErrorStacktrace(\"mutation\", name, result),\n ),\n );\n }\n throw new Error(createHybridErrorStacktrace(\"mutation\", name, result));\n }\n return result.value;\n }\n\n /**\n * @internal\n */\n async mutationInternal(\n udfPath: string,\n args?: Record<string, Value>,\n options?: MutationOptions,\n componentPath?: string,\n ): Promise<FunctionResult> {\n const { mutationPromise } = this.enqueueMutation(\n udfPath,\n args,\n options,\n componentPath,\n );\n return mutationPromise;\n }\n\n /**\n * @internal\n */\n enqueueMutation(\n udfPath: string,\n args?: Record<string, Value>,\n options?: MutationOptions,\n componentPath?: string,\n ): { requestId: RequestId; mutationPromise: Promise<FunctionResult> } {\n const mutationArgs = parseArgs(args);\n this.tryReportLongDisconnect();\n const requestId = this.nextRequestId;\n this._nextRequestId++;\n\n if (options !== undefined) {\n const optimisticUpdate = options.optimisticUpdate;\n if (optimisticUpdate !== undefined) {\n const wrappedUpdate = (localQueryStore: OptimisticLocalStore) => {\n const result: unknown = optimisticUpdate(\n localQueryStore,\n mutationArgs,\n );\n if (result instanceof Promise) {\n this.logger.warn(\n \"Optimistic update handler returned a Promise. Optimistic updates should be synchronous.\",\n );\n }\n };\n\n const changedQueryTokens =\n this.optimisticQueryResults.applyOptimisticUpdate(\n wrappedUpdate,\n requestId,\n );\n\n const changedQueries = changedQueryTokens.map((token) => {\n const localResult = this.localQueryResultByToken(token);\n return {\n token,\n modification: {\n kind: \"Updated\" as const,\n result:\n localResult === undefined\n ? undefined\n : {\n success: true as const,\n value: localResult,\n logLines: [],\n },\n },\n };\n });\n this.handleTransition({\n queries: changedQueries,\n reflectedMutations: [],\n timestamp: this.remoteQuerySet.timestamp(),\n });\n }\n }\n\n const message: MutationRequest = {\n type: \"Mutation\",\n requestId,\n udfPath,\n componentPath,\n args: [convexToJson(mutationArgs)],\n };\n const mightBeSent = this.webSocketManager.sendMessage(message);\n const mutationPromise = this.requestManager.request(message, mightBeSent);\n return {\n requestId,\n mutationPromise,\n };\n }\n\n /**\n * Execute an action function.\n *\n * @param name - The name of the action.\n * @param args - An arguments object for the action. If this is omitted,\n * the arguments will be `{}`.\n * @returns A promise of the action's result.\n */\n async action(name: string, args?: Record<string, Value>): Promise<any> {\n const result = await this.actionInternal(name, args);\n if (!result.success) {\n if (result.errorData !== undefined) {\n throw forwardData(\n result,\n new ConvexError(createHybridErrorStacktrace(\"action\", name, result)),\n );\n }\n throw new Error(createHybridErrorStacktrace(\"action\", name, result));\n }\n return result.value;\n }\n\n /**\n * @internal\n */\n async actionInternal(\n udfPath: string,\n args?: Record<string, Value>,\n componentPath?: string,\n ): Promise<FunctionResult> {\n const actionArgs = parseArgs(args);\n const requestId = this.nextRequestId;\n this._nextRequestId++;\n this.tryReportLongDisconnect();\n\n const message: ActionRequest = {\n type: \"Action\",\n requestId,\n udfPath,\n componentPath,\n args: [convexToJson(actionArgs)],\n };\n\n const mightBeSent = this.webSocketManager.sendMessage(message);\n return this.requestManager.request(message, mightBeSent);\n }\n\n /**\n * Close any network handles associated with this client and stop all subscriptions.\n *\n * Call this method when you're done with an {@link BaseConvexClient} to\n * dispose of its sockets and resources.\n *\n * @returns A `Promise` fulfilled when the connection has been completely closed.\n */\n async close(): Promise<void> {\n this.authenticationManager.stop();\n return this.webSocketManager.terminate();\n }\n\n /**\n * Return the address for this client, useful for creating a new client.\n *\n * Not guaranteed to match the address with which this client was constructed:\n * it may be canonicalized.\n */\n get url() {\n return this.address;\n }\n\n /**\n * @internal\n */\n get nextRequestId() {\n return this._nextRequestId;\n }\n\n /**\n * @internal\n */\n get sessionId() {\n return this._sessionId;\n }\n\n // Instance property so that `mark()` doesn't need to be called as a method.\n private mark = (name: MarkName) => {\n if (this.debug) {\n mark(name, this.sessionId);\n }\n };\n\n /**\n * Reports performance marks to the server. This should only be called when\n * we have a functional websocket.\n */\n private reportMarks() {\n if (this.debug) {\n const report = getMarksReport(this.sessionId);\n this.webSocketManager.sendMessage({\n type: \"Event\",\n eventType: \"ClientConnect\",\n event: report,\n });\n }\n }\n\n private tryReportLongDisconnect() {\n if (!this.debug) {\n return;\n }\n const timeOfOldestRequest =\n this.connectionState().timeOfOldestInflightRequest;\n if (\n timeOfOldestRequest === null ||\n Date.now() - timeOfOldestRequest.getTime() <= 60 * 1000\n ) {\n return;\n }\n const endpoint = `${this.address}/api/debug_event`;\n fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Convex-Client\": `npm-${version}`,\n },\n body: JSON.stringify({ event: \"LongWebsocketDisconnect\" }),\n })\n .then((response) => {\n if (!response.ok) {\n this.logger.warn(\n \"Analytics request failed with response:\",\n response.body,\n );\n }\n })\n .catch((error) => {\n this.logger.warn(\"Analytics response failed with error:\", error);\n });\n }\n}\n"],
|
|
4
|
+
"sourcesContent": ["/**\n * BaseConvexClient should not be used directly and does not provide a stable\n * interface. It is a \"Base\" client not because it expects to be inherited from\n * but because other clients are built around it.\n *\n * BaseConvexClient is not Convex Function type-aware: it deals\n * with queries as functions that return Value, not the specific value.\n * Use a higher-level library to get types.\n */\nimport { version } from \"../../index.js\";\nimport { convexToJson, Value } from \"../../values/index.js\";\nimport {\n createHybridErrorStacktrace,\n forwardData,\n instantiateDefaultLogger,\n instantiateNoopLogger,\n logFatalError,\n Logger,\n} from \"../logging.js\";\nimport { LocalSyncState } from \"./local_state.js\";\nimport { RequestManager } from \"./request_manager.js\";\nimport {\n OptimisticLocalStore,\n OptimisticUpdate,\n} from \"./optimistic_updates.js\";\nimport {\n OptimisticQueryResults,\n QueryResultsMap,\n} from \"./optimistic_updates_impl.js\";\nimport {\n ActionRequest,\n MutationRequest,\n QueryId,\n QueryJournal,\n ServerMessage,\n RequestId,\n TS,\n UserIdentityAttributes,\n} from \"./protocol.js\";\nimport { RemoteQuerySet } from \"./remote_query_set.js\";\nimport { QueryToken, serializePathAndArgs } from \"./udf_path_utils.js\";\nimport { ReconnectMetadata, WebSocketManager } from \"./web_socket_manager.js\";\nimport { newSessionId } from \"./session.js\";\nimport { FunctionResult } from \"./function_result.js\";\nimport {\n AuthenticationManager,\n AuthTokenFetcher,\n} from \"./authentication_manager.js\";\nexport { type AuthTokenFetcher } from \"./authentication_manager.js\";\nimport { getMarksReport, mark, MarkName } from \"./metrics.js\";\nimport { parseArgs, validateDeploymentUrl } from \"../../common/index.js\";\nimport { ConvexError } from \"../../values/errors.js\";\nimport { jwtDecode } from \"../../vendor/jwt-decode/index.js\";\n\n/**\n * Options for {@link BaseConvexClient}.\n *\n * @public\n */\nexport interface BaseConvexClientOptions {\n /**\n * Whether to prompt the user if they have unsaved changes pending\n * when navigating away or closing a web page.\n *\n * This is only possible when the `window` object exists, i.e. in a browser.\n *\n * The default value is `true` in browsers.\n */\n unsavedChangesWarning?: boolean;\n /**\n * Specifies an alternate\n * [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)\n * constructor to use for client communication with the Convex cloud.\n * The default behavior is to use `WebSocket` from the global environment.\n */\n webSocketConstructor?: typeof WebSocket;\n /**\n * Adds additional logging for debugging purposes.\n *\n * The default value is `false`.\n */\n verbose?: boolean;\n /**\n * A logger, `true`, or `false`. If not provided or `true`, logs to the console.\n * If `false`, logs are not printed anywhere.\n *\n * You can construct your own logger to customize logging to log elsewhere.\n * A logger is an object with 4 methods: log(), warn(), error(), and logVerbose().\n * These methods can receive multiple arguments of any types, like console.log().\n */\n logger?: Logger | boolean;\n /**\n * Sends additional metrics to Convex for debugging purposes.\n *\n * The default value is `false`.\n */\n reportDebugInfoToConvex?: boolean;\n /**\n * This API is experimental: it may change or disappear.\n *\n * A function to call on receiving abnormal WebSocket close messages from the\n * connected Convex deployment. The content of these messages is not stable,\n * it is an implementation detail that may change.\n *\n * Consider this API an observability stopgap until higher level codes with\n * recommendations on what to do are available, which could be a more stable\n * interface instead of `string`.\n *\n * Check `connectionState` for more quantitative metrics about connection status.\n */\n onServerDisconnectError?: (message: string) => void;\n /**\n * Skip validating that the Convex deployment URL looks like\n * `https://happy-animal-123.convex.cloud` or localhost.\n *\n * This can be useful if running a self-hosted Convex backend that uses a different\n * URL.\n *\n * The default value is `false`\n */\n skipConvexDeploymentUrlCheck?: boolean;\n /**\n * If using auth, the number of seconds before a token expires that we should refresh it.\n *\n * The default value is `10`.\n */\n authRefreshTokenLeewaySeconds?: number;\n /**\n * This API is experimental: it may change or disappear.\n *\n * Whether query, mutation, and action requests should be held back\n * until the first auth token can be sent.\n *\n * Opting into this behavior works well for pages that should\n * only be viewed by authenticated clients.\n *\n * Defaults to false, not waiting for an auth token.\n */\n expectAuth?: boolean;\n /**\n * This API is experimental: it may change or disappear.\n *\n * When true, the client reuses the initial cached auth token instead\n * of immediately fetching a fresh one. This avoids a second\n * Authenticate message that causes the server to re-execute all\n * authenticated queries.\n *\n * The cached token's remaining lifetime is estimated using the\n * server's clock skew measurement, and a refresh is scheduled\n * before it expires.\n *\n * Defaults to false, preserving the original behavior of immediately\n * fetching a fresh token after the cached token is confirmed.\n */\n initialAuthTokenReuse?: boolean;\n}\n\n/**\n * State describing the client's connection with the Convex backend.\n *\n * @public\n */\nexport type ConnectionState = {\n hasInflightRequests: boolean;\n isWebSocketConnected: boolean;\n timeOfOldestInflightRequest: Date | null;\n /**\n * True if the client has ever opened a WebSocket to the \"ready\" state.\n */\n hasEverConnected: boolean;\n /**\n * The number of times this client has connected to the Convex backend.\n *\n * A number of things can cause the client to reconnect -- server errors,\n * bad internet, auth expiring. But this number being high is an indication\n * that the client is having trouble keeping a stable connection.\n */\n connectionCount: number;\n /**\n * The number of times this client has tried (and failed) to connect to the Convex backend.\n */\n connectionRetries: number;\n /**\n * The number of mutations currently in flight.\n */\n inflightMutations: number;\n /**\n * The number of actions currently in flight.\n */\n inflightActions: number;\n};\n\n/**\n * Options for {@link BaseConvexClient.subscribe}.\n *\n * @public\n */\nexport interface SubscribeOptions {\n /**\n * An (optional) journal produced from a previous execution of this query\n * function.\n *\n * If there is an existing subscription to a query function with the same\n * name and arguments, this journal will have no effect.\n */\n journal?: QueryJournal;\n\n /**\n * @internal\n */\n componentPath?: string | undefined;\n}\n\n/**\n * Options for {@link BaseConvexClient.mutation}.\n *\n * @public\n */\nexport interface MutationOptions {\n /**\n * An optimistic update to apply along with this mutation.\n *\n * An optimistic update locally updates queries while a mutation is pending.\n * Once the mutation completes, the update will be rolled back.\n */\n optimisticUpdate?: OptimisticUpdate<any> | undefined;\n}\n\n/**\n * Type describing updates to a query within a `Transition`.\n *\n * @public\n */\nexport type QueryModification =\n // `undefined` generally comes from an optimistic update setting the query to be loading\n { kind: \"Updated\"; result: FunctionResult | undefined } | { kind: \"Removed\" };\n\n/**\n * Object describing a transition passed into the `onTransition` handler.\n *\n * These can be from receiving a transition from the server, or from applying an\n * optimistic update locally.\n *\n * @public\n */\nexport type Transition = {\n queries: Array<{ token: QueryToken; modification: QueryModification }>;\n reflectedMutations: Array<{ requestId: RequestId; result: FunctionResult }>;\n timestamp: TS;\n};\n\n/**\n * Low-level client for directly integrating state management libraries\n * with Convex.\n *\n * Most developers should use higher level clients, like\n * the {@link ConvexHttpClient} or the React hook based {@link react.ConvexReactClient}.\n *\n * @public\n */\nexport class BaseConvexClient {\n private readonly address: string;\n private readonly state: LocalSyncState;\n private readonly requestManager: RequestManager;\n private readonly webSocketManager: WebSocketManager;\n private readonly authenticationManager: AuthenticationManager;\n private remoteQuerySet: RemoteQuerySet;\n private readonly optimisticQueryResults: OptimisticQueryResults;\n private _transitionHandlerCounter = 0;\n private _nextRequestId: RequestId;\n private _onTransitionFns: Map<number, (transition: Transition) => void> =\n new Map();\n private readonly _sessionId: string;\n private firstMessageReceived = false;\n private readonly debug: boolean;\n private readonly logger: Logger;\n private maxObservedTimestamp: TS | undefined;\n private connectionStateSubscribers = new Map<\n number,\n (connectionState: ConnectionState) => void\n >();\n private nextConnectionStateSubscriberId: number = 0;\n private _lastPublishedConnectionState: ConnectionState | undefined;\n\n /**\n * @param address - The url of your Convex deployment, often provided\n * by an environment variable. E.g. `https://small-mouse-123.convex.cloud`.\n * @param onTransition - A callback receiving an array of query tokens\n * corresponding to query results that have changed -- additional handlers\n * can be added via `addOnTransitionHandler`.\n * @param options - See {@link BaseConvexClientOptions} for a full description.\n */\n constructor(\n address: string,\n onTransition: (updatedQueries: QueryToken[]) => void,\n options?: BaseConvexClientOptions,\n ) {\n if (typeof address === \"object\") {\n throw new Error(\n \"Passing a ClientConfig object is no longer supported. Pass the URL of the Convex deployment as a string directly.\",\n );\n }\n if (options?.skipConvexDeploymentUrlCheck !== true) {\n validateDeploymentUrl(address);\n }\n options = { ...options };\n const authRefreshTokenLeewaySeconds =\n options.authRefreshTokenLeewaySeconds ?? 10;\n let webSocketConstructor = options.webSocketConstructor;\n if (!webSocketConstructor && typeof WebSocket === \"undefined\") {\n throw new Error(\n \"No WebSocket global variable defined! To use Convex in an environment without WebSocket try the HTTP client: https://docs.convex.dev/api/classes/browser.ConvexHttpClient\",\n );\n }\n webSocketConstructor = webSocketConstructor || WebSocket;\n this.debug = options.reportDebugInfoToConvex ?? false;\n this.address = address;\n this.logger =\n options.logger === false\n ? instantiateNoopLogger({ verbose: options.verbose ?? false })\n : options.logger !== true && options.logger\n ? options.logger\n : instantiateDefaultLogger({ verbose: options.verbose ?? false });\n // Substitute http(s) with ws(s)\n const i = address.search(\"://\");\n if (i === -1) {\n throw new Error(\"Provided address was not an absolute URL.\");\n }\n const origin = address.substring(i + 3); // move past the double slash\n const protocol = address.substring(0, i);\n let wsProtocol;\n if (protocol === \"http\") {\n wsProtocol = \"ws\";\n } else if (protocol === \"https\") {\n wsProtocol = \"wss\";\n } else {\n throw new Error(`Unknown parent protocol ${protocol}`);\n }\n const wsUri = `${wsProtocol}://${origin}/api/${version}/sync`;\n\n this.state = new LocalSyncState();\n this.remoteQuerySet = new RemoteQuerySet(\n (queryId) => this.state.queryPath(queryId),\n this.logger,\n );\n this.requestManager = new RequestManager(\n this.logger,\n this.markConnectionStateDirty,\n );\n\n // This is a callback for AuthenticationManager (which can't call\n // this synchronously, the callback wouldn't work) so the initial\n // pause for expectAuth we call it at the end of this constructor.\n const pauseSocket = () => {\n this.webSocketManager.pause();\n this.state.pause();\n };\n this.authenticationManager = new AuthenticationManager(\n this.state,\n {\n authenticate: (token) => {\n const message = this.state.setAuth(token);\n this.webSocketManager.sendMessage(message);\n return message.baseVersion;\n },\n stopSocket: () => this.webSocketManager.stop(),\n tryRestartSocket: () => this.webSocketManager.tryRestart(),\n pauseSocket,\n resumeSocket: () => this.webSocketManager.resume(),\n clearAuth: () => {\n this.clearAuth();\n },\n },\n {\n logger: this.logger,\n refreshTokenLeewaySeconds: authRefreshTokenLeewaySeconds,\n initialAuthTokenReuse: options.initialAuthTokenReuse ?? false,\n },\n );\n this.optimisticQueryResults = new OptimisticQueryResults();\n this.addOnTransitionHandler((transition) => {\n onTransition(transition.queries.map((q) => q.token));\n });\n this._nextRequestId = 0;\n this._sessionId = newSessionId();\n\n const { unsavedChangesWarning } = options;\n if (\n typeof window === \"undefined\" ||\n typeof window.addEventListener === \"undefined\"\n ) {\n if (unsavedChangesWarning === true) {\n throw new Error(\n \"unsavedChangesWarning requested, but window.addEventListener not found! Remove {unsavedChangesWarning: true} from Convex client options.\",\n );\n }\n } else if (unsavedChangesWarning !== false) {\n // Listen for tab close events and notify the user on unsaved changes.\n window.addEventListener(\"beforeunload\", (e) => {\n if (this.requestManager.hasIncompleteRequests()) {\n // There are 3 different ways to trigger this pop up so just try all of\n // them.\n\n e.preventDefault();\n // This confirmation message doesn't actually appear in most modern\n // browsers but we tried.\n const confirmationMessage =\n \"Are you sure you want to leave? Your changes may not be saved.\";\n // Recommended method for legacy (IE) browsers.\n // casts to avoid deprecation notices\n ((e || (window as any).event) as any).returnValue =\n confirmationMessage;\n return confirmationMessage;\n }\n });\n }\n\n this.webSocketManager = new WebSocketManager(\n wsUri,\n {\n onOpen: (reconnectMetadata: ReconnectMetadata) => {\n // We have a new WebSocket!\n this.mark(\"convexWebSocketOpen\");\n this.webSocketManager.sendMessage({\n ...reconnectMetadata,\n type: \"Connect\",\n sessionId: this._sessionId,\n maxObservedTimestamp: this.maxObservedTimestamp,\n });\n\n // Throw out our remote query, reissue queries\n // and outstanding mutations, and reauthenticate.\n this.remoteQuerySet = new RemoteQuerySet(\n (queryId) => this.state.queryPath(queryId),\n this.logger,\n );\n const [querySetModification, authModification] = this.state.restart();\n if (authModification) {\n this.webSocketManager.sendMessage(authModification);\n }\n this.webSocketManager.sendMessage(querySetModification);\n for (const message of this.requestManager.restart()) {\n this.webSocketManager.sendMessage(message);\n }\n },\n onResume: () => {\n const [querySetModification, authModification] = this.state.resume();\n if (authModification) {\n this.webSocketManager.sendMessage(authModification);\n }\n if (querySetModification) {\n this.webSocketManager.sendMessage(querySetModification);\n }\n for (const message of this.requestManager.resume()) {\n this.webSocketManager.sendMessage(message);\n }\n },\n onMessage: (serverMessage: ServerMessage) => {\n // Metrics events grow linearly with reconnection attempts so this\n // conditional prevents n^2 metrics reporting.\n if (!this.firstMessageReceived) {\n this.firstMessageReceived = true;\n this.mark(\"convexFirstMessageReceived\");\n this.reportMarks();\n }\n switch (serverMessage.type) {\n case \"Transition\": {\n this.observedTimestamp(serverMessage.endVersion.ts);\n this.authenticationManager.onTransition(serverMessage);\n this.remoteQuerySet.transition(serverMessage);\n this.state.transition(serverMessage);\n const completedRequests = this.requestManager.removeCompleted(\n this.remoteQuerySet.timestamp(),\n );\n this.notifyOnQueryResultChanges(completedRequests);\n break;\n }\n case \"MutationResponse\": {\n if (serverMessage.success) {\n this.observedTimestamp(serverMessage.ts);\n }\n const completedMutationInfo =\n this.requestManager.onResponse(serverMessage);\n if (completedMutationInfo !== null) {\n this.notifyOnQueryResultChanges(\n new Map([\n [\n completedMutationInfo.requestId,\n completedMutationInfo.result,\n ],\n ]),\n );\n }\n break;\n }\n case \"ActionResponse\": {\n this.requestManager.onResponse(serverMessage);\n break;\n }\n case \"AuthError\": {\n this.authenticationManager.onAuthError(serverMessage);\n break;\n }\n case \"FatalError\": {\n const error = logFatalError(this.logger, serverMessage.error);\n void this.webSocketManager.terminate();\n throw error;\n }\n default: {\n serverMessage satisfies never;\n }\n }\n\n return {\n hasSyncedPastLastReconnect: this.hasSyncedPastLastReconnect(),\n };\n },\n onServerDisconnectError: options.onServerDisconnectError,\n },\n webSocketConstructor,\n this.logger,\n this.markConnectionStateDirty,\n this.debug,\n );\n this.mark(\"convexClientConstructed\");\n\n // Begin client in a paused state waiting for an auth token.\n if (options.expectAuth) {\n pauseSocket();\n }\n }\n\n /**\n * Return true if there is outstanding work from prior to the time of the most recent restart.\n * This indicates that the client has not proven itself to have gotten past the issue that\n * potentially led to the restart. Use this to influence when to reset backoff after a failure.\n */\n private hasSyncedPastLastReconnect() {\n const hasSyncedPastLastReconnect =\n this.requestManager.hasSyncedPastLastReconnect() &&\n this.state.hasSyncedPastLastReconnect();\n return hasSyncedPastLastReconnect;\n }\n\n private observedTimestamp(observedTs: TS) {\n if (\n this.maxObservedTimestamp === undefined ||\n this.maxObservedTimestamp.lessThanOrEqual(observedTs)\n ) {\n this.maxObservedTimestamp = observedTs;\n }\n }\n\n getMaxObservedTimestamp() {\n return this.maxObservedTimestamp;\n }\n\n /**\n * Compute the current query results based on the remoteQuerySet and the\n * current optimistic updates and call `onTransition` for all the changed\n * queries.\n *\n * @param completedMutations - A set of mutation IDs whose optimistic updates\n * are no longer needed.\n */\n private notifyOnQueryResultChanges(\n completedRequests: Map<RequestId, FunctionResult>,\n ) {\n const remoteQueryResults: Map<QueryId, FunctionResult> =\n this.remoteQuerySet.remoteQueryResults();\n const queryTokenToValue: QueryResultsMap = new Map();\n for (const [queryId, result] of remoteQueryResults) {\n const queryToken = this.state.queryToken(queryId);\n // It's possible that we've already unsubscribed to this query but\n // the server hasn't learned about that yet. If so, ignore this one.\n\n if (queryToken !== null) {\n const query = {\n result,\n udfPath: this.state.queryPath(queryId)!,\n args: this.state.queryArgs(queryId)!,\n };\n queryTokenToValue.set(queryToken, query);\n }\n }\n\n // Query tokens that are new (because of new server results or new local optimistic updates)\n // or differ from old values (because of changes from local optimistic updates or new results\n // from the server).\n const changedQueryTokens =\n this.optimisticQueryResults.ingestQueryResultsFromServer(\n queryTokenToValue,\n new Set(completedRequests.keys()),\n );\n\n this.handleTransition({\n queries: changedQueryTokens.map((token) => {\n const optimisticResult =\n this.optimisticQueryResults.rawQueryResult(token);\n return {\n token,\n modification: {\n kind: \"Updated\" as const,\n result: optimisticResult,\n },\n };\n }),\n reflectedMutations: Array.from(completedRequests).map(\n ([requestId, result]) => ({\n requestId,\n result,\n }),\n ),\n timestamp: this.remoteQuerySet.timestamp(),\n });\n }\n\n private handleTransition(transition: Transition) {\n for (const fn of this._onTransitionFns.values()) {\n fn(transition);\n }\n }\n\n /**\n * Add a handler that will be called on a transition.\n *\n * Any external side effects (e.g. setting React state) should be handled here.\n *\n * @param fn\n *\n * @returns\n */\n addOnTransitionHandler(fn: (transition: Transition) => void) {\n const id = this._transitionHandlerCounter++;\n this._onTransitionFns.set(id, fn);\n return () => this._onTransitionFns.delete(id);\n }\n\n /**\n * Get the current JWT auth token and decoded claims.\n */\n getCurrentAuthClaims():\n | { token: string; decoded: Record<string, any> }\n | undefined {\n const authToken = this.state.getAuth();\n let decoded: Record<string, any> = {};\n if (authToken && authToken.tokenType === \"User\") {\n try {\n decoded = authToken ? jwtDecode(authToken.value) : {};\n } catch {\n decoded = {};\n }\n } else {\n return undefined;\n }\n return { token: authToken.value, decoded };\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 * @param onRefreshChange - a callback called with `true` when the socket is paused to fetch a replacement token after a server rejection, and `false` when refresh completes\n */\n setAuth(\n fetchToken: AuthTokenFetcher,\n onChange: (isAuthenticated: boolean) => void,\n onRefreshChange?: (isRefreshing: boolean) => void,\n ) {\n void this.authenticationManager.setConfig(\n fetchToken,\n onChange,\n onRefreshChange,\n );\n }\n\n hasAuth() {\n return this.state.hasAuth();\n }\n\n /** @internal */\n setAdminAuth(value: string, fakeUserIdentity?: UserIdentityAttributes) {\n const message = this.state.setAdminAuth(value, fakeUserIdentity);\n this.webSocketManager.sendMessage(message);\n }\n\n clearAuth() {\n const message = this.state.clearAuth();\n this.webSocketManager.sendMessage(message);\n }\n\n /**\n * Subscribe to a query function.\n *\n * Whenever this query's result changes, the `onTransition` callback\n * passed into the constructor will be called.\n *\n * @param name - The name of the query.\n * @param args - An arguments object for the query. If this is omitted, the\n * arguments will be `{}`.\n * @param options - A {@link SubscribeOptions} options object for this query.\n\n * @returns An object containing a {@link QueryToken} corresponding to this\n * query and an `unsubscribe` callback.\n */\n subscribe(\n name: string,\n args?: Record<string, Value>,\n options?: SubscribeOptions,\n ): { queryToken: QueryToken; unsubscribe: () => void } {\n const argsObject = parseArgs(args);\n\n const { modification, queryToken, unsubscribe } = this.state.subscribe(\n name,\n argsObject,\n options?.journal,\n options?.componentPath,\n );\n if (modification !== null) {\n this.webSocketManager.sendMessage(modification);\n }\n return {\n queryToken,\n unsubscribe: () => {\n const modification = unsubscribe();\n if (modification) {\n this.webSocketManager.sendMessage(modification);\n }\n },\n };\n }\n\n /**\n * A query result based only on the current, local state.\n *\n * The only way this will return a value is if we're already subscribed to the\n * query or its value has been set optimistically.\n */\n localQueryResult(\n udfPath: string,\n args?: Record<string, Value>,\n ): Value | undefined {\n const argsObject = parseArgs(args);\n const queryToken = serializePathAndArgs(udfPath, argsObject);\n return this.optimisticQueryResults.queryResult(queryToken);\n }\n\n /**\n * Get query result by query token based on current, local state\n *\n * The only way this will return a value is if we're already subscribed to the\n * query or its value has been set optimistically.\n *\n * @internal\n */\n localQueryResultByToken(queryToken: QueryToken): Value | undefined {\n return this.optimisticQueryResults.queryResult(queryToken);\n }\n\n /**\n * Whether local query result is available for a token.\n *\n * This method does not throw if the result is an error.\n *\n * @internal\n */\n hasLocalQueryResultByToken(queryToken: QueryToken): boolean {\n return this.optimisticQueryResults.hasQueryResult(queryToken);\n }\n\n /**\n * @internal\n */\n localQueryLogs(\n udfPath: string,\n args?: Record<string, Value>,\n ): string[] | undefined {\n const argsObject = parseArgs(args);\n const queryToken = serializePathAndArgs(udfPath, argsObject);\n return this.optimisticQueryResults.queryLogs(queryToken);\n }\n\n /**\n * Retrieve the current {@link QueryJournal} for this query function.\n *\n * If we have not yet received a result for this query, this will be `undefined`.\n *\n * @param name - The name of the query.\n * @param args - The arguments object for this query.\n * @returns The query's {@link QueryJournal} or `undefined`.\n */\n queryJournal(\n name: string,\n args?: Record<string, Value>,\n ): QueryJournal | undefined {\n const argsObject = parseArgs(args);\n const queryToken = serializePathAndArgs(name, argsObject);\n return this.state.queryJournal(queryToken);\n }\n\n /**\n * Get the current {@link ConnectionState} between the client and the Convex\n * backend.\n *\n * @returns The {@link ConnectionState} with the Convex backend.\n */\n connectionState(): ConnectionState {\n const wsConnectionState = this.webSocketManager.connectionState();\n return {\n hasInflightRequests: this.requestManager.hasInflightRequests(),\n isWebSocketConnected: wsConnectionState.isConnected,\n hasEverConnected: wsConnectionState.hasEverConnected,\n connectionCount: wsConnectionState.connectionCount,\n connectionRetries: wsConnectionState.connectionRetries,\n timeOfOldestInflightRequest:\n this.requestManager.timeOfOldestInflightRequest(),\n inflightMutations: this.requestManager.inflightMutations(),\n inflightActions: this.requestManager.inflightActions(),\n };\n }\n\n /**\n * Call this whenever the connection state may have changed in a way that could\n * require publishing it. Schedules a possibly update.\n */\n private markConnectionStateDirty = () => {\n void Promise.resolve().then(() => {\n const curConnectionState = this.connectionState();\n if (\n JSON.stringify(curConnectionState) !==\n JSON.stringify(this._lastPublishedConnectionState)\n ) {\n this._lastPublishedConnectionState = curConnectionState;\n for (const cb of this.connectionStateSubscribers.values()) {\n // One of these callback throwing will prevent other callbacks\n // from running but will not leave the client in a undefined state.\n cb(curConnectionState);\n }\n }\n });\n };\n\n /**\n * Subscribe to the {@link ConnectionState} between the client and the Convex\n * backend, calling a callback each time it changes.\n *\n * Subscribed callbacks will be called when any part of ConnectionState changes.\n * ConnectionState may grow in future versions (e.g. to provide a array of\n * inflight requests) in which case callbacks would be called more frequently.\n *\n * @returns An unsubscribe function to stop listening.\n */\n subscribeToConnectionState(\n cb: (connectionState: ConnectionState) => void,\n ): () => void {\n const id = this.nextConnectionStateSubscriberId++;\n this.connectionStateSubscribers.set(id, cb);\n return () => {\n this.connectionStateSubscribers.delete(id);\n };\n }\n\n /**\n * Execute a mutation function.\n *\n * @param name - The name of the mutation.\n * @param args - An arguments object for the mutation. If this is omitted,\n * the arguments will be `{}`.\n * @param options - A {@link MutationOptions} options object for this mutation.\n\n * @returns - A promise of the mutation's result.\n */\n async mutation(\n name: string,\n args?: Record<string, Value>,\n options?: MutationOptions,\n ): Promise<any> {\n const result = await this.mutationInternal(name, args, options);\n if (!result.success) {\n if (result.errorData !== undefined) {\n throw forwardData(\n result,\n new ConvexError(\n createHybridErrorStacktrace(\"mutation\", name, result),\n ),\n );\n }\n throw new Error(createHybridErrorStacktrace(\"mutation\", name, result));\n }\n return result.value;\n }\n\n /**\n * @internal\n */\n async mutationInternal(\n udfPath: string,\n args?: Record<string, Value>,\n options?: MutationOptions,\n componentPath?: string,\n ): Promise<FunctionResult> {\n const { mutationPromise } = this.enqueueMutation(\n udfPath,\n args,\n options,\n componentPath,\n );\n return mutationPromise;\n }\n\n /**\n * @internal\n */\n enqueueMutation(\n udfPath: string,\n args?: Record<string, Value>,\n options?: MutationOptions,\n componentPath?: string,\n ): { requestId: RequestId; mutationPromise: Promise<FunctionResult> } {\n const mutationArgs = parseArgs(args);\n this.tryReportLongDisconnect();\n const requestId = this.nextRequestId;\n this._nextRequestId++;\n\n if (options !== undefined) {\n const optimisticUpdate = options.optimisticUpdate;\n if (optimisticUpdate !== undefined) {\n const wrappedUpdate = (localQueryStore: OptimisticLocalStore) => {\n const result: unknown = optimisticUpdate(\n localQueryStore,\n mutationArgs,\n );\n if (result instanceof Promise) {\n this.logger.warn(\n \"Optimistic update handler returned a Promise. Optimistic updates should be synchronous.\",\n );\n }\n };\n\n const changedQueryTokens =\n this.optimisticQueryResults.applyOptimisticUpdate(\n wrappedUpdate,\n requestId,\n );\n\n const changedQueries = changedQueryTokens.map((token) => {\n const localResult = this.localQueryResultByToken(token);\n return {\n token,\n modification: {\n kind: \"Updated\" as const,\n result:\n localResult === undefined\n ? undefined\n : {\n success: true as const,\n value: localResult,\n logLines: [],\n },\n },\n };\n });\n this.handleTransition({\n queries: changedQueries,\n reflectedMutations: [],\n timestamp: this.remoteQuerySet.timestamp(),\n });\n }\n }\n\n const message: MutationRequest = {\n type: \"Mutation\",\n requestId,\n udfPath,\n componentPath,\n args: [convexToJson(mutationArgs)],\n };\n const mightBeSent = this.webSocketManager.sendMessage(message);\n const mutationPromise = this.requestManager.request(message, mightBeSent);\n return {\n requestId,\n mutationPromise,\n };\n }\n\n /**\n * Execute an action function.\n *\n * @param name - The name of the action.\n * @param args - An arguments object for the action. If this is omitted,\n * the arguments will be `{}`.\n * @returns A promise of the action's result.\n */\n async action(name: string, args?: Record<string, Value>): Promise<any> {\n const result = await this.actionInternal(name, args);\n if (!result.success) {\n if (result.errorData !== undefined) {\n throw forwardData(\n result,\n new ConvexError(createHybridErrorStacktrace(\"action\", name, result)),\n );\n }\n throw new Error(createHybridErrorStacktrace(\"action\", name, result));\n }\n return result.value;\n }\n\n /**\n * @internal\n */\n async actionInternal(\n udfPath: string,\n args?: Record<string, Value>,\n componentPath?: string,\n ): Promise<FunctionResult> {\n const actionArgs = parseArgs(args);\n const requestId = this.nextRequestId;\n this._nextRequestId++;\n this.tryReportLongDisconnect();\n\n const message: ActionRequest = {\n type: \"Action\",\n requestId,\n udfPath,\n componentPath,\n args: [convexToJson(actionArgs)],\n };\n\n const mightBeSent = this.webSocketManager.sendMessage(message);\n return this.requestManager.request(message, mightBeSent);\n }\n\n /**\n * Close any network handles associated with this client and stop all subscriptions.\n *\n * Call this method when you're done with an {@link BaseConvexClient} to\n * dispose of its sockets and resources.\n *\n * @returns A `Promise` fulfilled when the connection has been completely closed.\n */\n async close(): Promise<void> {\n this.authenticationManager.stop();\n return this.webSocketManager.terminate();\n }\n\n /**\n * Return the address for this client, useful for creating a new client.\n *\n * Not guaranteed to match the address with which this client was constructed:\n * it may be canonicalized.\n */\n get url() {\n return this.address;\n }\n\n /**\n * @internal\n */\n get nextRequestId() {\n return this._nextRequestId;\n }\n\n /**\n * @internal\n */\n get sessionId() {\n return this._sessionId;\n }\n\n // Instance property so that `mark()` doesn't need to be called as a method.\n private mark = (name: MarkName) => {\n if (this.debug) {\n mark(name, this.sessionId);\n }\n };\n\n /**\n * Reports performance marks to the server. This should only be called when\n * we have a functional websocket.\n */\n private reportMarks() {\n if (this.debug) {\n const report = getMarksReport(this.sessionId);\n this.webSocketManager.sendMessage({\n type: \"Event\",\n eventType: \"ClientConnect\",\n event: report,\n });\n }\n }\n\n private tryReportLongDisconnect() {\n if (!this.debug) {\n return;\n }\n const timeOfOldestRequest =\n this.connectionState().timeOfOldestInflightRequest;\n if (\n timeOfOldestRequest === null ||\n Date.now() - timeOfOldestRequest.getTime() <= 60 * 1000\n ) {\n return;\n }\n const endpoint = `${this.address}/api/debug_event`;\n fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Convex-Client\": `npm-${version}`,\n },\n body: JSON.stringify({ event: \"LongWebsocketDisconnect\" }),\n })\n .then((response) => {\n if (!response.ok) {\n this.logger.warn(\n \"Analytics request failed with response:\",\n response.body,\n );\n }\n })\n .catch((error) => {\n this.logger.warn(\"Analytics response failed with error:\", error);\n });\n }\n}\n\n/**\n * The public API of {@link BaseConvexClient}.\n *\n * @internal\n */\nexport interface BaseConvexClientInterface {\n addOnTransitionHandler(fn: (transition: Transition) => void): () => void;\n\n setAuth(\n fetchToken: AuthTokenFetcher,\n onChange: (isAuthenticated: boolean) => void,\n onRefreshChange?: (isRefreshing: boolean) => void,\n ): void;\n\n /** @internal */\n setAdminAuth(value: string, fakeUserIdentity?: UserIdentityAttributes): void;\n\n clearAuth(): void;\n\n subscribe(\n name: string,\n args?: Record<string, Value>,\n options?: SubscribeOptions,\n ): { queryToken: QueryToken; unsubscribe: () => void };\n\n localQueryResult(\n udfPath: string,\n args?: Record<string, Value>,\n ): Value | undefined;\n\n localQueryResultByToken(queryToken: QueryToken): Value | undefined;\n\n hasLocalQueryResultByToken(queryToken: QueryToken): boolean;\n\n localQueryLogs(\n udfPath: string,\n args?: Record<string, Value>,\n ): string[] | undefined;\n\n queryJournal(\n name: string,\n args?: Record<string, Value>,\n ): QueryJournal | undefined;\n\n connectionState(): ConnectionState;\n\n subscribeToConnectionState(\n cb: (connectionState: ConnectionState) => void,\n ): () => void;\n\n mutation(\n name: string,\n args?: Record<string, Value>,\n options?: MutationOptions,\n ): Promise<any>;\n\n action(name: string, args?: Record<string, Value>): Promise<any>;\n\n close(): Promise<void>;\n\n readonly url: string;\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": ["modification"]
|
|
7
7
|
}
|
|
@@ -251,8 +251,11 @@ class PaginatedQueryClient {
|
|
|
251
251
|
if (ongoingSplits.has(pageKey)) {
|
|
252
252
|
continue;
|
|
253
253
|
}
|
|
254
|
-
const
|
|
255
|
-
|
|
254
|
+
const pageEntry = pageKeyToQuery.get(pageKey);
|
|
255
|
+
if (!pageEntry) {
|
|
256
|
+
throw new Error(`No page query for active pageKey ${pageKey}`);
|
|
257
|
+
}
|
|
258
|
+
const pageResult = getResult(pageEntry.queryToken);
|
|
256
259
|
if (!pageResult) {
|
|
257
260
|
continue;
|
|
258
261
|
}
|
|
@@ -263,6 +266,7 @@ class PaginatedQueryClient {
|
|
|
263
266
|
this.splitPaginatedQueryPage(
|
|
264
267
|
paginatedQuery,
|
|
265
268
|
pageKey,
|
|
269
|
+
pageEntry.cursor,
|
|
266
270
|
result.splitCursor,
|
|
267
271
|
// we just checked
|
|
268
272
|
result.continueCursor
|
|
@@ -271,11 +275,10 @@ class PaginatedQueryClient {
|
|
|
271
275
|
}
|
|
272
276
|
}
|
|
273
277
|
}
|
|
274
|
-
splitPaginatedQueryPage(paginatedQuery, pageKey, splitCursor, continueCursor) {
|
|
278
|
+
splitPaginatedQueryPage(paginatedQuery, pageKey, startCursor, splitCursor, continueCursor) {
|
|
275
279
|
const splitKey1 = paginatedQuery.nextPageKey++;
|
|
276
280
|
const splitKey2 = paginatedQuery.nextPageKey++;
|
|
277
281
|
const paginationOpts = {
|
|
278
|
-
cursor: continueCursor,
|
|
279
282
|
numItems: paginatedQuery.options.initialNumItems,
|
|
280
283
|
id: paginatedQuery.id
|
|
281
284
|
};
|
|
@@ -285,13 +288,15 @@ class PaginatedQueryClient {
|
|
|
285
288
|
...paginatedQuery.args,
|
|
286
289
|
paginationOpts: {
|
|
287
290
|
...paginationOpts,
|
|
288
|
-
cursor:
|
|
289
|
-
// Start from beginning for first split
|
|
291
|
+
cursor: startCursor,
|
|
290
292
|
endCursor: splitCursor
|
|
291
293
|
}
|
|
292
294
|
}
|
|
293
295
|
);
|
|
294
|
-
paginatedQuery.pageKeyToQuery.set(splitKey1,
|
|
296
|
+
paginatedQuery.pageKeyToQuery.set(splitKey1, {
|
|
297
|
+
...firstSubscription,
|
|
298
|
+
cursor: startCursor
|
|
299
|
+
});
|
|
295
300
|
const secondSubscription = this.client.subscribe(
|
|
296
301
|
paginatedQuery.canonicalizedUdfPath,
|
|
297
302
|
{
|
|
@@ -303,7 +308,10 @@ class PaginatedQueryClient {
|
|
|
303
308
|
}
|
|
304
309
|
}
|
|
305
310
|
);
|
|
306
|
-
paginatedQuery.pageKeyToQuery.set(splitKey2,
|
|
311
|
+
paginatedQuery.pageKeyToQuery.set(splitKey2, {
|
|
312
|
+
...secondSubscription,
|
|
313
|
+
cursor: splitCursor
|
|
314
|
+
});
|
|
307
315
|
paginatedQuery.ongoingSplits.set(pageKey, [splitKey1, splitKey2]);
|
|
308
316
|
}
|
|
309
317
|
/**
|
|
@@ -326,7 +334,10 @@ class PaginatedQueryClient {
|
|
|
326
334
|
pageArgs
|
|
327
335
|
);
|
|
328
336
|
paginatedQuery.pageKeys.push(pageKey);
|
|
329
|
-
paginatedQuery.pageKeyToQuery.set(pageKey,
|
|
337
|
+
paginatedQuery.pageKeyToQuery.set(pageKey, {
|
|
338
|
+
...subscription,
|
|
339
|
+
cursor: continueCursor
|
|
340
|
+
});
|
|
330
341
|
return subscription;
|
|
331
342
|
}
|
|
332
343
|
removePaginatedQuerySubscriber(token) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/browser/sync/paginated_query_client.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * PaginatedQueryClient maps subscriptions to paginated queries to the\n * individual page queries and handles page splits.\n *\n * In order to process all modified queries, paginated and normal, in the same\n * synchronous call the PaginatedQueryClient transition should be used exclusively.\n *\n * Like the BaseConvexClient, this client is not Convex Function type-aware: it deals\n * with queries as functions that return Value, not the specific value.\n * Use a higher-level library to get types.\n */\n\nimport { Value } from \"../../values/index.js\";\nimport {\n PaginatedQueryToken,\n QueryToken,\n serializePaginatedPathAndArgs,\n canonicalizeUdfPath,\n} from \"./udf_path_utils.js\";\nimport { BaseConvexClient, Transition } from \"./client.js\";\nimport {\n PaginatedQueryResult,\n PaginationStatus,\n asPaginationResult,\n} from \"./pagination.js\";\nimport { TS } from \"./protocol.js\";\nimport { Long } from \"../../vendor/long.js\";\n\ntype QueryPageKey = number;\n\n/**\n * Represents a paginated query subscription with multiple pages.\n *\n * To know the order of pages it's necessary to consult `pageKeys`.\n * The pages in this array are active, they constitute a gapless sequence of results.\n * Some pages are not in this array: they may be waiting for data for a page split.\n */\ntype LocalPaginatedQuery = {\n token: PaginatedQueryToken;\n canonicalizedUdfPath: string;\n args: Record<string, Value>; // WITHOUT paginationOpts\n numSubscribers: number;\n options: { initialNumItems: number };\n nextPageKey: QueryPageKey;\n pageKeys: QueryPageKey[]; // These pages make up the active page queries.\n // Map page keys to their query subscriptions\n pageKeyToQuery: Map<\n QueryPageKey,\n { queryToken: QueryToken; unsubscribe: () => void }\n >;\n ongoingSplits: Map<QueryPageKey, [QueryPageKey, QueryPageKey]>;\n skip: boolean;\n\n // Give separate uses of the query separate identities,\n // which may be removed in the future to improve caching.\n id: number;\n};\n\nexport interface SubscribeToPaginatedQueryOptions {\n initialNumItems: number;\n id: number;\n}\n\ntype AnyPaginatedQueryResult = PaginatedQueryResult<Value>;\n\nexport type PaginatedQueryModification =\n | { kind: \"Updated\"; result: AnyPaginatedQueryResult | undefined }\n | { kind: \"Removed\" };\n\nexport type ExtendedTransition = Transition & {\n paginatedQueries: Array<{\n token: PaginatedQueryToken;\n modification: PaginatedQueryModification;\n }>;\n};\n\nexport class PaginatedQueryClient {\n private paginatedQuerySet: Map<PaginatedQueryToken, LocalPaginatedQuery> =\n new Map();\n // hold onto a real Transition so we can construct synthetic ones with that timestamp\n private lastTransitionTs: TS;\n\n constructor(\n private client: BaseConvexClient,\n private onTransition: (transition: ExtendedTransition) => void,\n ) {\n // Nonsense initial value to construct synthetic Transitions\n this.lastTransitionTs = Long.fromNumber(0);\n this.client.addOnTransitionHandler((transition: Transition) =>\n this.onBaseTransition(transition),\n );\n }\n\n /**\n * Subscribe to a paginated query.\n *\n * @param name - The name of the paginated query function\n * @param args - Arguments for the query (excluding paginationOpts)\n * @param options - Pagination options including initialNumItems\n * @returns Object with paginatedQueryToken and unsubscribe function\n */\n subscribe(\n name: string,\n args: Record<string, Value>,\n options: SubscribeToPaginatedQueryOptions,\n ): {\n paginatedQueryToken: PaginatedQueryToken;\n unsubscribe: () => void;\n } {\n const canonicalizedUdfPath = canonicalizeUdfPath(name);\n // Note that only the expected options are included in the serialization.\n const token = serializePaginatedPathAndArgs(\n canonicalizedUdfPath,\n args,\n options,\n );\n\n const unsubscribe = () => this.removePaginatedQuerySubscriber(token);\n\n const existingEntry = this.paginatedQuerySet.get(token);\n if (existingEntry) {\n existingEntry.numSubscribers += 1;\n return {\n paginatedQueryToken: token,\n unsubscribe,\n };\n }\n\n // Create new paginated query\n this.paginatedQuerySet.set(token, {\n token,\n canonicalizedUdfPath,\n args,\n numSubscribers: 1,\n options: { initialNumItems: options.initialNumItems },\n nextPageKey: 0,\n pageKeys: [],\n pageKeyToQuery: new Map(),\n ongoingSplits: new Map(),\n skip: false,\n id: options.id,\n });\n\n this.addPageToPaginatedQuery(token, null, options.initialNumItems);\n\n return {\n paginatedQueryToken: token,\n unsubscribe,\n };\n }\n\n /**\n * Get current results for a paginated query based on local state.\n *\n * Throws an error when one of the pages has errored.\n */\n localQueryResult(\n name: string,\n args: Record<string, Value>,\n options: { initialNumItems: number; id: number },\n ): AnyPaginatedQueryResult | undefined {\n const canonicalizedUdfPath = canonicalizeUdfPath(name);\n const token = serializePaginatedPathAndArgs(\n canonicalizedUdfPath,\n args,\n options,\n );\n return this.localQueryResultByToken(token);\n }\n\n /**\n * @internal\n */\n localQueryResultByToken(\n token: PaginatedQueryToken,\n ): AnyPaginatedQueryResult | undefined {\n // undefined is probably the wrong value! Should be a real paginated query result for loading!\n // Butit's confusing why we'd ever get this, I guess some flows call localQueryResult before\n // subscribing? That's proabbly fair but is it consistent with the normal client?\n // What is the invariant here, will a token always exist? Or can a lookup occur at any time?\n const paginatedQuery = this.paginatedQuerySet.get(token);\n if (!paginatedQuery) {\n return undefined;\n }\n\n const activePages = this.activePageQueryTokens(paginatedQuery);\n if (activePages.length === 0) {\n return {\n results: [],\n status: \"LoadingFirstPage\",\n loadMore: (numItems: number) => {\n return this.loadMoreOfPaginatedQuery(token, numItems);\n },\n };\n }\n\n let allResults: Value[] = [];\n\n // Some page is loading (this isn't supposed to happen to any page but the last)\n let hasUndefined = false;\n let isDone = false;\n\n for (const pageToken of activePages) {\n // This throws, don't catch it, it should bubble up.\n // It might be a InvalidCursor Error. If it is, this query\n // should be reset (for now, use a new ID to ensure new state).\n //\n // In the future this might be caught and dealt with here but\n // an ID-based solution won't work here, ID is an intrinsic property\n // of this paginated query.\n const result = this.client.localQueryResultByToken(pageToken);\n\n if (result === undefined) {\n hasUndefined = true;\n isDone = false;\n continue;\n }\n\n const paginationResult = asPaginationResult(result);\n allResults = allResults.concat(paginationResult.page);\n // logic only relevant to the last page, we just happen to run it each time\n isDone = !!paginationResult.isDone;\n }\n\n let status: PaginationStatus;\n if (hasUndefined) {\n status = allResults.length === 0 ? \"LoadingFirstPage\" : \"LoadingMore\";\n } else if (isDone) {\n status = \"Exhausted\";\n } else {\n status = \"CanLoadMore\";\n }\n\n return {\n results: allResults,\n status,\n loadMore: (numItems: number) => {\n return this.loadMoreOfPaginatedQuery(token, numItems);\n },\n };\n }\n\n private onBaseTransition(transition: Transition) {\n const changedBaseTokens = transition.queries.map((q) => q.token);\n const changed = this.queriesContainingTokens(changedBaseTokens);\n\n let paginatedQueries: Array<{\n token: PaginatedQueryToken;\n modification: PaginatedQueryModification;\n }> = [];\n\n if (changed.length > 0) {\n this.processPaginatedQuerySplits(changed, (token) =>\n this.client.localQueryResultByToken(token),\n );\n\n paginatedQueries = changed.map((token) => ({\n token,\n modification: {\n kind: \"Updated\" as const,\n result: this.localQueryResultByToken(token),\n },\n }));\n }\n\n const extendedTransition: ExtendedTransition = {\n ...transition,\n paginatedQueries,\n };\n\n this.onTransition(extendedTransition);\n }\n\n /**\n * Load more items for a paginated query.\n *\n * This *always* causes a transition, the status of the query\n * has probably changed from \"CanLoadMore\" to \"LoadingMore\".\n * Data might have changed too: maybe a subscription to this page\n * query already exists (unlikely but possible) or this page query\n * has an optimistic update providing some initial data.\n *\n * @internal\n */\n private loadMoreOfPaginatedQuery(\n token: PaginatedQueryToken,\n numItems: number,\n ): boolean {\n this.mustGetPaginatedQuery(token);\n\n const lastPageToken = this.queryTokenForLastPageOfPaginatedQuery(token);\n const lastPageResult = this.client.localQueryResultByToken(lastPageToken);\n\n if (!lastPageResult) {\n // Still loading a page and concurrent loads are not allowed\n return false;\n }\n\n const paginationResult = asPaginationResult(lastPageResult);\n if (paginationResult.isDone) {\n // No more pages available\n return false;\n }\n\n this.addPageToPaginatedQuery(\n token,\n paginationResult.continueCursor,\n numItems,\n );\n\n const loadMoreTransition: ExtendedTransition = {\n timestamp: this.lastTransitionTs,\n reflectedMutations: [],\n queries: [],\n paginatedQueries: [\n {\n token,\n modification: {\n kind: \"Updated\" as const,\n result: this.localQueryResultByToken(token),\n },\n },\n ],\n };\n this.onTransition(loadMoreTransition);\n\n return true;\n }\n\n /**\n * @internal\n */\n private queriesContainingTokens(\n queryTokens: QueryToken[],\n ): PaginatedQueryToken[] {\n if (queryTokens.length === 0) {\n return [];\n }\n\n const changed: PaginatedQueryToken[] = [];\n const queryTokenSet = new Set(queryTokens);\n\n for (const [paginatedToken, paginatedQuery] of this.paginatedQuerySet) {\n for (const pageToken of this.allQueryTokens(paginatedQuery)) {\n if (queryTokenSet.has(pageToken)) {\n changed.push(paginatedToken);\n break;\n }\n }\n }\n\n return changed;\n }\n\n /**\n * @internal\n */\n private processPaginatedQuerySplits(\n changed: PaginatedQueryToken[],\n getResult: (token: QueryToken) => Value | undefined,\n ): void {\n for (const paginatedQueryToken of changed) {\n const paginatedQuery = this.mustGetPaginatedQuery(paginatedQueryToken);\n\n // These properties are all mutable, the destructure here is optional.\n const { ongoingSplits, pageKeyToQuery, pageKeys } = paginatedQuery;\n\n // Check for any completed splits\n for (const [pageKey, [splitKey1, splitKey2]] of ongoingSplits) {\n const bothNewPagesLoaded =\n getResult(pageKeyToQuery.get(splitKey1)!.queryToken) !== undefined &&\n getResult(pageKeyToQuery.get(splitKey2)!.queryToken) !== undefined;\n\n if (bothNewPagesLoaded) {\n this.completePaginatedQuerySplit(\n paginatedQuery,\n pageKey,\n splitKey1,\n splitKey2,\n );\n }\n }\n\n // Check each active page for splits needed\n for (const pageKey of pageKeys) {\n if (ongoingSplits.has(pageKey)) {\n continue; // Already splitting\n }\n\n const pageToken = pageKeyToQuery.get(pageKey)!.queryToken;\n const pageResult = getResult(pageToken);\n if (!pageResult) {\n continue;\n }\n const result = asPaginationResult(pageResult);\n\n // Check if this page needs splitting\n const shouldSplit =\n result.splitCursor &&\n (result.pageStatus === \"SplitRecommended\" ||\n result.pageStatus === \"SplitRequired\" ||\n // This client-driven page splitting condition will change in the future.\n result.page.length > paginatedQuery.options.initialNumItems * 2);\n\n if (shouldSplit) {\n this.splitPaginatedQueryPage(\n paginatedQuery,\n pageKey,\n result.splitCursor!, // we just checked\n result.continueCursor,\n );\n }\n }\n }\n }\n\n private splitPaginatedQueryPage(\n paginatedQuery: LocalPaginatedQuery,\n pageKey: QueryPageKey,\n splitCursor: string,\n continueCursor: string | null,\n ): void {\n const splitKey1 = paginatedQuery.nextPageKey++;\n const splitKey2 = paginatedQuery.nextPageKey++;\n\n const paginationOpts: Value = {\n cursor: continueCursor,\n numItems: paginatedQuery.options.initialNumItems,\n id: paginatedQuery.id,\n };\n\n // First split page: same cursor as original, but add endCursor at splitCursor\n const firstSubscription = this.client.subscribe(\n paginatedQuery.canonicalizedUdfPath,\n {\n ...paginatedQuery.args,\n paginationOpts: {\n ...paginationOpts,\n cursor: null, // Start from beginning for first split\n endCursor: splitCursor,\n },\n },\n );\n paginatedQuery.pageKeyToQuery.set(splitKey1, firstSubscription);\n\n // Second split page: cursor starts at splitCursor, endCursor is the original continueCursor\n const secondSubscription = this.client.subscribe(\n paginatedQuery.canonicalizedUdfPath,\n {\n ...paginatedQuery.args,\n paginationOpts: {\n ...paginationOpts,\n cursor: splitCursor,\n endCursor: continueCursor,\n },\n },\n );\n paginatedQuery.pageKeyToQuery.set(splitKey2, secondSubscription);\n\n paginatedQuery.ongoingSplits.set(pageKey, [splitKey1, splitKey2]);\n }\n\n /**\n * @internal\n */\n private addPageToPaginatedQuery(\n token: PaginatedQueryToken,\n continueCursor: string | null,\n numItems: number,\n ): { queryToken: QueryToken; unsubscribe: () => void } {\n const paginatedQuery = this.mustGetPaginatedQuery(token);\n const pageKey = paginatedQuery.nextPageKey++;\n\n const paginationOpts: Value = {\n cursor: continueCursor,\n numItems,\n id: paginatedQuery.id,\n };\n\n const pageArgs = {\n ...paginatedQuery.args,\n paginationOpts,\n };\n\n const subscription = this.client.subscribe(\n paginatedQuery.canonicalizedUdfPath,\n pageArgs,\n );\n\n paginatedQuery.pageKeys.push(pageKey);\n paginatedQuery.pageKeyToQuery.set(pageKey, subscription);\n return subscription;\n }\n\n private removePaginatedQuerySubscriber(token: PaginatedQueryToken): void {\n const paginatedQuery = this.paginatedQuerySet.get(token);\n if (!paginatedQuery) {\n return;\n }\n\n paginatedQuery.numSubscribers -= 1;\n if (paginatedQuery.numSubscribers > 0) {\n return;\n }\n\n // Remove all page subscriptions by calling their unsubscribe callbacks\n for (const subscription of paginatedQuery.pageKeyToQuery.values()) {\n subscription.unsubscribe();\n }\n\n this.paginatedQuerySet.delete(token);\n }\n\n private completePaginatedQuerySplit(\n paginatedQuery: LocalPaginatedQuery,\n pageKey: QueryPageKey,\n splitKey1: QueryPageKey,\n splitKey2: QueryPageKey,\n ): void {\n const originalQuery = paginatedQuery.pageKeyToQuery.get(pageKey)!;\n paginatedQuery.pageKeyToQuery.delete(pageKey);\n const pageIndex = paginatedQuery.pageKeys.indexOf(pageKey);\n paginatedQuery.pageKeys.splice(pageIndex, 1, splitKey1, splitKey2);\n paginatedQuery.ongoingSplits.delete(pageKey);\n originalQuery.unsubscribe();\n }\n\n /** The query tokens for all active pages, in result order */\n private activePageQueryTokens(\n paginatedQuery: LocalPaginatedQuery,\n ): QueryToken[] {\n return paginatedQuery.pageKeys.map(\n (pageKey) => paginatedQuery.pageKeyToQuery.get(pageKey)!.queryToken,\n );\n }\n\n private allQueryTokens(paginatedQuery: LocalPaginatedQuery): QueryToken[] {\n return Array.from(paginatedQuery.pageKeyToQuery.values()).map(\n (sub) => sub.queryToken,\n );\n }\n\n private queryTokenForLastPageOfPaginatedQuery(\n token: PaginatedQueryToken,\n ): QueryToken {\n const paginatedQuery = this.mustGetPaginatedQuery(token);\n const lastPageKey =\n paginatedQuery.pageKeys[paginatedQuery.pageKeys.length - 1];\n if (lastPageKey === undefined) {\n throw new Error(`No pages for paginated query ${token}`);\n }\n return paginatedQuery.pageKeyToQuery.get(lastPageKey)!.queryToken;\n }\n\n private mustGetPaginatedQuery(\n token: PaginatedQueryToken,\n ): LocalPaginatedQuery {\n const paginatedQuery = this.paginatedQuerySet.get(token);\n if (!paginatedQuery) {\n throw new Error(\"paginated query no longer exists for token \" + token);\n }\n return paginatedQuery;\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAaA,4BAKO;AAEP,wBAIO;AAEP,kBAAqB;
|
|
4
|
+
"sourcesContent": ["/**\n * PaginatedQueryClient maps subscriptions to paginated queries to the\n * individual page queries and handles page splits.\n *\n * In order to process all modified queries, paginated and normal, in the same\n * synchronous call the PaginatedQueryClient transition should be used exclusively.\n *\n * Like the BaseConvexClient, this client is not Convex Function type-aware: it deals\n * with queries as functions that return Value, not the specific value.\n * Use a higher-level library to get types.\n */\n\nimport { Value } from \"../../values/index.js\";\nimport {\n PaginatedQueryToken,\n QueryToken,\n serializePaginatedPathAndArgs,\n canonicalizeUdfPath,\n} from \"./udf_path_utils.js\";\nimport { BaseConvexClientInterface, Transition } from \"./client.js\";\nimport {\n PaginatedQueryResult,\n PaginationStatus,\n asPaginationResult,\n} from \"./pagination.js\";\nimport { TS } from \"./protocol.js\";\nimport { Long } from \"../../vendor/long.js\";\n\ntype QueryPageKey = number;\n\n/**\n * Represents a paginated query subscription with multiple pages.\n *\n * To know the order of pages it's necessary to consult `pageKeys`.\n * The pages in this array are active, they constitute a gapless sequence of results.\n * Some pages are not in this array: they may be waiting for data for a page split.\n */\ntype LocalPaginatedQuery = {\n token: PaginatedQueryToken;\n canonicalizedUdfPath: string;\n args: Record<string, Value>; // WITHOUT paginationOpts\n numSubscribers: number;\n options: { initialNumItems: number };\n nextPageKey: QueryPageKey;\n pageKeys: QueryPageKey[]; // These pages make up the active page queries.\n // Map page keys to their query subscriptions\n pageKeyToQuery: Map<\n QueryPageKey,\n { queryToken: QueryToken; unsubscribe: () => void; cursor: string | null }\n >;\n ongoingSplits: Map<QueryPageKey, [QueryPageKey, QueryPageKey]>;\n skip: boolean;\n\n // Give separate uses of the query separate identities,\n // which may be removed in the future to improve caching.\n id: number;\n};\n\nexport interface SubscribeToPaginatedQueryOptions {\n initialNumItems: number;\n id: number;\n}\n\ntype AnyPaginatedQueryResult = PaginatedQueryResult<Value>;\n\nexport type PaginatedQueryModification =\n | { kind: \"Updated\"; result: AnyPaginatedQueryResult | undefined }\n | { kind: \"Removed\" };\n\nexport type ExtendedTransition = Transition & {\n paginatedQueries: Array<{\n token: PaginatedQueryToken;\n modification: PaginatedQueryModification;\n }>;\n};\n\n/** @internal */\nexport class PaginatedQueryClient {\n private paginatedQuerySet: Map<PaginatedQueryToken, LocalPaginatedQuery> =\n new Map();\n // hold onto a real Transition so we can construct synthetic ones with that timestamp\n private lastTransitionTs: TS;\n\n constructor(\n private client: BaseConvexClientInterface,\n private onTransition: (transition: ExtendedTransition) => void,\n ) {\n // Nonsense initial value to construct synthetic Transitions\n this.lastTransitionTs = Long.fromNumber(0);\n this.client.addOnTransitionHandler((transition: Transition) =>\n this.onBaseTransition(transition),\n );\n }\n\n /**\n * Subscribe to a paginated query.\n *\n * @param name - The name of the paginated query function\n * @param args - Arguments for the query (excluding paginationOpts)\n * @param options - Pagination options including initialNumItems\n * @returns Object with paginatedQueryToken and unsubscribe function\n */\n subscribe(\n name: string,\n args: Record<string, Value>,\n options: SubscribeToPaginatedQueryOptions,\n ): {\n paginatedQueryToken: PaginatedQueryToken;\n unsubscribe: () => void;\n } {\n const canonicalizedUdfPath = canonicalizeUdfPath(name);\n // Note that only the expected options are included in the serialization.\n const token = serializePaginatedPathAndArgs(\n canonicalizedUdfPath,\n args,\n options,\n );\n\n const unsubscribe = () => this.removePaginatedQuerySubscriber(token);\n\n const existingEntry = this.paginatedQuerySet.get(token);\n if (existingEntry) {\n existingEntry.numSubscribers += 1;\n return {\n paginatedQueryToken: token,\n unsubscribe,\n };\n }\n\n // Create new paginated query\n this.paginatedQuerySet.set(token, {\n token,\n canonicalizedUdfPath,\n args,\n numSubscribers: 1,\n options: { initialNumItems: options.initialNumItems },\n nextPageKey: 0,\n pageKeys: [],\n pageKeyToQuery: new Map(),\n ongoingSplits: new Map(),\n skip: false,\n id: options.id,\n });\n\n this.addPageToPaginatedQuery(token, null, options.initialNumItems);\n\n return {\n paginatedQueryToken: token,\n unsubscribe,\n };\n }\n\n /**\n * Get current results for a paginated query based on local state.\n *\n * Throws an error when one of the pages has errored.\n */\n localQueryResult(\n name: string,\n args: Record<string, Value>,\n options: { initialNumItems: number; id: number },\n ): AnyPaginatedQueryResult | undefined {\n const canonicalizedUdfPath = canonicalizeUdfPath(name);\n const token = serializePaginatedPathAndArgs(\n canonicalizedUdfPath,\n args,\n options,\n );\n return this.localQueryResultByToken(token);\n }\n\n /**\n * @internal\n */\n localQueryResultByToken(\n token: PaginatedQueryToken,\n ): AnyPaginatedQueryResult | undefined {\n // undefined is probably the wrong value! Should be a real paginated query result for loading!\n // Butit's confusing why we'd ever get this, I guess some flows call localQueryResult before\n // subscribing? That's proabbly fair but is it consistent with the normal client?\n // What is the invariant here, will a token always exist? Or can a lookup occur at any time?\n const paginatedQuery = this.paginatedQuerySet.get(token);\n if (!paginatedQuery) {\n return undefined;\n }\n\n const activePages = this.activePageQueryTokens(paginatedQuery);\n if (activePages.length === 0) {\n return {\n results: [],\n status: \"LoadingFirstPage\",\n loadMore: (numItems: number) => {\n return this.loadMoreOfPaginatedQuery(token, numItems);\n },\n };\n }\n\n let allResults: Value[] = [];\n\n // Some page is loading (this isn't supposed to happen to any page but the last)\n let hasUndefined = false;\n let isDone = false;\n\n for (const pageToken of activePages) {\n // This throws, don't catch it, it should bubble up.\n // It might be a InvalidCursor Error. If it is, this query\n // should be reset (for now, use a new ID to ensure new state).\n //\n // In the future this might be caught and dealt with here but\n // an ID-based solution won't work here, ID is an intrinsic property\n // of this paginated query.\n const result = this.client.localQueryResultByToken(pageToken);\n\n if (result === undefined) {\n hasUndefined = true;\n isDone = false;\n continue;\n }\n\n const paginationResult = asPaginationResult(result);\n allResults = allResults.concat(paginationResult.page);\n // logic only relevant to the last page, we just happen to run it each time\n isDone = !!paginationResult.isDone;\n }\n\n let status: PaginationStatus;\n if (hasUndefined) {\n status = allResults.length === 0 ? \"LoadingFirstPage\" : \"LoadingMore\";\n } else if (isDone) {\n status = \"Exhausted\";\n } else {\n status = \"CanLoadMore\";\n }\n\n return {\n results: allResults,\n status,\n loadMore: (numItems: number) => {\n return this.loadMoreOfPaginatedQuery(token, numItems);\n },\n };\n }\n\n private onBaseTransition(transition: Transition) {\n const changedBaseTokens = transition.queries.map((q) => q.token);\n const changed = this.queriesContainingTokens(changedBaseTokens);\n\n let paginatedQueries: Array<{\n token: PaginatedQueryToken;\n modification: PaginatedQueryModification;\n }> = [];\n\n if (changed.length > 0) {\n this.processPaginatedQuerySplits(changed, (token) =>\n this.client.localQueryResultByToken(token),\n );\n\n paginatedQueries = changed.map((token) => ({\n token,\n modification: {\n kind: \"Updated\" as const,\n result: this.localQueryResultByToken(token),\n },\n }));\n }\n\n const extendedTransition: ExtendedTransition = {\n ...transition,\n paginatedQueries,\n };\n\n this.onTransition(extendedTransition);\n }\n\n /**\n * Load more items for a paginated query.\n *\n * This *always* causes a transition, the status of the query\n * has probably changed from \"CanLoadMore\" to \"LoadingMore\".\n * Data might have changed too: maybe a subscription to this page\n * query already exists (unlikely but possible) or this page query\n * has an optimistic update providing some initial data.\n *\n * @internal\n */\n private loadMoreOfPaginatedQuery(\n token: PaginatedQueryToken,\n numItems: number,\n ): boolean {\n this.mustGetPaginatedQuery(token);\n\n const lastPageToken = this.queryTokenForLastPageOfPaginatedQuery(token);\n const lastPageResult = this.client.localQueryResultByToken(lastPageToken);\n\n if (!lastPageResult) {\n // Still loading a page and concurrent loads are not allowed\n return false;\n }\n\n const paginationResult = asPaginationResult(lastPageResult);\n if (paginationResult.isDone) {\n // No more pages available\n return false;\n }\n\n this.addPageToPaginatedQuery(\n token,\n paginationResult.continueCursor,\n numItems,\n );\n\n const loadMoreTransition: ExtendedTransition = {\n timestamp: this.lastTransitionTs,\n reflectedMutations: [],\n queries: [],\n paginatedQueries: [\n {\n token,\n modification: {\n kind: \"Updated\" as const,\n result: this.localQueryResultByToken(token),\n },\n },\n ],\n };\n this.onTransition(loadMoreTransition);\n\n return true;\n }\n\n /**\n * @internal\n */\n private queriesContainingTokens(\n queryTokens: QueryToken[],\n ): PaginatedQueryToken[] {\n if (queryTokens.length === 0) {\n return [];\n }\n\n const changed: PaginatedQueryToken[] = [];\n const queryTokenSet = new Set(queryTokens);\n\n for (const [paginatedToken, paginatedQuery] of this.paginatedQuerySet) {\n for (const pageToken of this.allQueryTokens(paginatedQuery)) {\n if (queryTokenSet.has(pageToken)) {\n changed.push(paginatedToken);\n break;\n }\n }\n }\n\n return changed;\n }\n\n /**\n * @internal\n */\n private processPaginatedQuerySplits(\n changed: PaginatedQueryToken[],\n getResult: (token: QueryToken) => Value | undefined,\n ): void {\n for (const paginatedQueryToken of changed) {\n const paginatedQuery = this.mustGetPaginatedQuery(paginatedQueryToken);\n\n // These properties are all mutable, the destructure here is optional.\n const { ongoingSplits, pageKeyToQuery, pageKeys } = paginatedQuery;\n\n // Check for any completed splits\n for (const [pageKey, [splitKey1, splitKey2]] of ongoingSplits) {\n const bothNewPagesLoaded =\n getResult(pageKeyToQuery.get(splitKey1)!.queryToken) !== undefined &&\n getResult(pageKeyToQuery.get(splitKey2)!.queryToken) !== undefined;\n\n if (bothNewPagesLoaded) {\n this.completePaginatedQuerySplit(\n paginatedQuery,\n pageKey,\n splitKey1,\n splitKey2,\n );\n }\n }\n\n // Check each active page for splits needed\n for (const pageKey of pageKeys) {\n if (ongoingSplits.has(pageKey)) {\n continue; // Already splitting\n }\n\n const pageEntry = pageKeyToQuery.get(pageKey);\n if (!pageEntry) {\n throw new Error(`No page query for active pageKey ${pageKey}`);\n }\n const pageResult = getResult(pageEntry.queryToken);\n if (!pageResult) {\n continue;\n }\n const result = asPaginationResult(pageResult);\n\n // Check if this page needs splitting\n const shouldSplit =\n result.splitCursor &&\n (result.pageStatus === \"SplitRecommended\" ||\n result.pageStatus === \"SplitRequired\" ||\n // This client-driven page splitting condition will change in the future.\n result.page.length > paginatedQuery.options.initialNumItems * 2);\n\n if (shouldSplit) {\n this.splitPaginatedQueryPage(\n paginatedQuery,\n pageKey,\n pageEntry.cursor,\n result.splitCursor!, // we just checked\n result.continueCursor,\n );\n }\n }\n }\n }\n\n private splitPaginatedQueryPage(\n paginatedQuery: LocalPaginatedQuery,\n pageKey: QueryPageKey,\n // Where the page being split started.\n startCursor: string | null,\n // The split position - will be end of the first split page and the start\n // of the second.\n splitCursor: string,\n // Where the page being split ended.\n continueCursor: string | null,\n ): void {\n const splitKey1 = paginatedQuery.nextPageKey++;\n const splitKey2 = paginatedQuery.nextPageKey++;\n\n const paginationOpts: Value = {\n numItems: paginatedQuery.options.initialNumItems,\n id: paginatedQuery.id,\n };\n\n // First split page: same cursor as the original page, ending at splitCursor\n const firstSubscription = this.client.subscribe(\n paginatedQuery.canonicalizedUdfPath,\n {\n ...paginatedQuery.args,\n paginationOpts: {\n ...paginationOpts,\n cursor: startCursor,\n endCursor: splitCursor,\n },\n },\n );\n paginatedQuery.pageKeyToQuery.set(splitKey1, {\n ...firstSubscription,\n cursor: startCursor,\n });\n\n // Second split page: cursor starts at splitCursor, endCursor is the original continueCursor\n const secondSubscription = this.client.subscribe(\n paginatedQuery.canonicalizedUdfPath,\n {\n ...paginatedQuery.args,\n paginationOpts: {\n ...paginationOpts,\n cursor: splitCursor,\n endCursor: continueCursor,\n },\n },\n );\n paginatedQuery.pageKeyToQuery.set(splitKey2, {\n ...secondSubscription,\n cursor: splitCursor,\n });\n\n paginatedQuery.ongoingSplits.set(pageKey, [splitKey1, splitKey2]);\n }\n\n /**\n * @internal\n */\n private addPageToPaginatedQuery(\n token: PaginatedQueryToken,\n continueCursor: string | null,\n numItems: number,\n ): { queryToken: QueryToken; unsubscribe: () => void } {\n const paginatedQuery = this.mustGetPaginatedQuery(token);\n const pageKey = paginatedQuery.nextPageKey++;\n\n const paginationOpts: Value = {\n cursor: continueCursor,\n numItems,\n id: paginatedQuery.id,\n };\n\n const pageArgs = {\n ...paginatedQuery.args,\n paginationOpts,\n };\n\n const subscription = this.client.subscribe(\n paginatedQuery.canonicalizedUdfPath,\n pageArgs,\n );\n\n paginatedQuery.pageKeys.push(pageKey);\n paginatedQuery.pageKeyToQuery.set(pageKey, {\n ...subscription,\n cursor: continueCursor,\n });\n return subscription;\n }\n\n private removePaginatedQuerySubscriber(token: PaginatedQueryToken): void {\n const paginatedQuery = this.paginatedQuerySet.get(token);\n if (!paginatedQuery) {\n return;\n }\n\n paginatedQuery.numSubscribers -= 1;\n if (paginatedQuery.numSubscribers > 0) {\n return;\n }\n\n // Remove all page subscriptions by calling their unsubscribe callbacks\n for (const subscription of paginatedQuery.pageKeyToQuery.values()) {\n subscription.unsubscribe();\n }\n\n this.paginatedQuerySet.delete(token);\n }\n\n private completePaginatedQuerySplit(\n paginatedQuery: LocalPaginatedQuery,\n pageKey: QueryPageKey,\n splitKey1: QueryPageKey,\n splitKey2: QueryPageKey,\n ): void {\n const originalQuery = paginatedQuery.pageKeyToQuery.get(pageKey)!;\n paginatedQuery.pageKeyToQuery.delete(pageKey);\n const pageIndex = paginatedQuery.pageKeys.indexOf(pageKey);\n paginatedQuery.pageKeys.splice(pageIndex, 1, splitKey1, splitKey2);\n paginatedQuery.ongoingSplits.delete(pageKey);\n originalQuery.unsubscribe();\n }\n\n /** The query tokens for all active pages, in result order */\n private activePageQueryTokens(\n paginatedQuery: LocalPaginatedQuery,\n ): QueryToken[] {\n return paginatedQuery.pageKeys.map(\n (pageKey) => paginatedQuery.pageKeyToQuery.get(pageKey)!.queryToken,\n );\n }\n\n private allQueryTokens(paginatedQuery: LocalPaginatedQuery): QueryToken[] {\n return Array.from(paginatedQuery.pageKeyToQuery.values()).map(\n (sub) => sub.queryToken,\n );\n }\n\n private queryTokenForLastPageOfPaginatedQuery(\n token: PaginatedQueryToken,\n ): QueryToken {\n const paginatedQuery = this.mustGetPaginatedQuery(token);\n const lastPageKey =\n paginatedQuery.pageKeys[paginatedQuery.pageKeys.length - 1];\n if (lastPageKey === undefined) {\n throw new Error(`No pages for paginated query ${token}`);\n }\n return paginatedQuery.pageKeyToQuery.get(lastPageKey)!.queryToken;\n }\n\n private mustGetPaginatedQuery(\n token: PaginatedQueryToken,\n ): LocalPaginatedQuery {\n const paginatedQuery = this.paginatedQuerySet.get(token);\n if (!paginatedQuery) {\n throw new Error(\"paginated query no longer exists for token \" + token);\n }\n return paginatedQuery;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -303,7 +303,7 @@ async function* codegenStaticApiObjectsTS(ctx, analysis) {
|
|
|
303
303
|
}
|
|
304
304
|
async function* codegenDynamicApiObjects(ctx, componentDirectory) {
|
|
305
305
|
const absModulePaths = await (0, import_bundler.entryPoints)(ctx, componentDirectory.path);
|
|
306
|
-
const modulePaths = absModulePaths.map((p) => import_path.default.relative(componentDirectory.path, p)).sort();
|
|
306
|
+
const modulePaths = absModulePaths.map((p) => import_path.default.relative(componentDirectory.path, p)).sort(import_common.compareModulePaths);
|
|
307
307
|
for (const modulePath of modulePaths) {
|
|
308
308
|
const ident = (0, import_api.moduleIdentifier)(modulePath);
|
|
309
309
|
const path2 = (0, import_api.importPath)(modulePath);
|
|
@@ -333,7 +333,7 @@ async function* codegenDynamicApiObjects(ctx, componentDirectory) {
|
|
|
333
333
|
}
|
|
334
334
|
async function* codegenDynamicApiObjectsTS(ctx, componentDirectory) {
|
|
335
335
|
const absModulePaths = await (0, import_bundler.entryPoints)(ctx, componentDirectory.path);
|
|
336
|
-
const modulePaths = absModulePaths.map((p) => import_path.default.relative(componentDirectory.path, p)).sort();
|
|
336
|
+
const modulePaths = absModulePaths.map((p) => import_path.default.relative(componentDirectory.path, p)).sort(import_common.compareModulePaths);
|
|
337
337
|
for (const modulePath of modulePaths) {
|
|
338
338
|
const ident = (0, import_api.moduleIdentifier)(modulePath);
|
|
339
339
|
const path2 = (0, import_api.importPath)(modulePath);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/cli/codegen_templates/component_api.ts"],
|
|
4
|
-
"sourcesContent": ["import path from \"path\";\nimport { Context } from \"../../bundler/context.js\";\nimport { entryPoints } from \"../../bundler/index.js\";\nimport {\n ComponentDirectory,\n toAbsolutePath,\n toComponentDefinitionPath,\n ComponentDefinitionPath,\n} from \"../lib/components/definition/directoryStructure.js\";\nimport { StartPushResponse } from \"../lib/deployApi/startPush.js\";\nimport { importPath, moduleIdentifier } from \"./api.js\";\nimport { apiComment, compareStrings, header } from \"./common.js\";\nimport {\n ComponentExports,\n EvaluatedComponentDefinition,\n} from \"../lib/deployApi/componentDefinition.js\";\nimport { Identifier, Reference } from \"../lib/deployApi/types.js\";\nimport { CanonicalizedModulePath } from \"../lib/deployApi/paths.js\";\nimport {\n AnalyzedFunction,\n AnalyzedModule,\n Visibility,\n} from \"../lib/deployApi/modules.js\";\nimport { parseValidator, validatorToType } from \"./validator_helpers.js\";\n\nexport function componentApiJs() {\n const lines = [];\n lines.push(header(\"Generated `api` utility.\"));\n lines.push(`\n import { anyApi, componentsGeneric } from \"convex/server\";\n\n ${apiComment(\"api\", undefined)}\n export const api = anyApi;\n export const internal = anyApi;\n export const components = componentsGeneric();\n `);\n return lines.join(\"\\n\");\n}\n\nexport function rootComponentApiCJS() {\n const lines = [];\n lines.push(header(\"Generated `api` utility.\"));\n lines.push(`const { anyApi } = require(\"convex/server\");`);\n lines.push(`module.exports = {\n api: anyApi,\n internal: anyApi,\n };`);\n return lines.join(\"\\n\");\n}\n\nexport function componentApiStubDTS() {\n const lines = [];\n lines.push(header(\"Generated `api` utility.\"));\n lines.push(`import type { AnyApi, AnyComponents } from \"convex/server\";`);\n lines.push(`\n export declare const api: AnyApi;\n export declare const internal: AnyApi;\n export declare const components: AnyComponents;\n `);\n\n return lines.join(\"\\n\");\n}\n\n// This is also used for root components\nexport function componentApiStubTS() {\n const lines = [];\n lines.push(header(\"Generated `api` utility.\"));\n lines.push(`\n import type { AnyApi, AnyComponents } from \"convex/server\";\n import { anyApi, componentsGeneric } from \"convex/server\";\n\n export const api: AnyApi = anyApi;\n export const internal: AnyApi = anyApi;\n export const components: AnyComponents = componentsGeneric();\n `);\n return lines.join(\"\\n\");\n}\n\nexport async function componentApiDTS(\n ctx: Context,\n startPush: StartPushResponse,\n rootComponent: ComponentDirectory,\n componentDirectory: ComponentDirectory,\n componentsMap: Map<string, ComponentDirectory>,\n opts: { staticApi: boolean; useComponentApiImports: boolean },\n) {\n const definitionPath = toComponentDefinitionPath(\n rootComponent,\n componentDirectory,\n );\n\n const analysis = startPush.analysis[definitionPath];\n if (!analysis) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `No analysis found for component ${definitionPath} orig: ${definitionPath}\\nin\\n${Object.keys(startPush.analysis).toString()}`,\n });\n }\n\n const lines = [];\n lines.push(header(\"Generated `api` utility.\"));\n let apiLines: AsyncGenerator<string>;\n if (opts.staticApi) {\n apiLines = codegenStaticApiObjects(ctx, analysis);\n } else {\n apiLines = codegenDynamicApiObjects(ctx, componentDirectory);\n }\n for await (const line of apiLines) {\n lines.push(line);\n }\n\n lines.push(`\n export declare const components: {`);\n lines.push(\n ...(await componentApiLines(\n ctx,\n startPush,\n analysis,\n rootComponent,\n componentsMap,\n opts,\n )),\n );\n lines.push(\"};\");\n return lines.join(\"\\n\");\n}\n\nasync function componentApiLines(\n ctx: Context,\n startPush: StartPushResponse,\n analysis: EvaluatedComponentDefinition,\n rootComponent: ComponentDirectory,\n componentsMap: Map<string, ComponentDirectory>,\n opts: { useComponentApiImports: boolean },\n): Promise<string[]> {\n const lines: string[] = [];\n for (const childComponent of analysis.definition.childComponents) {\n if (opts.useComponentApiImports) {\n const absolutePath = toAbsolutePath(\n rootComponent,\n childComponent.path as ComponentDefinitionPath,\n );\n\n let childComponentWithRelativePath = componentsMap?.get(absolutePath);\n if (!childComponentWithRelativePath) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Invalid child component directory: ${childComponent.path}`,\n });\n }\n\n // If the user uses a different import specifier than the absolute path of the child component, use the import specifier.\n const importSpecifier = childComponentWithRelativePath.importSpecifier;\n let importPath;\n // Don't trust relative specifiers\n if (\n importSpecifier &&\n !importSpecifier.startsWith(\".\") &&\n importSpecifier !== childComponent.path\n ) {\n importPath = importSpecifier;\n } else {\n importPath = `../${childComponent.path}`;\n }\n lines.push(\n ` \"${childComponent.name}\": import(\"${importPath}/_generated/component.js\").ComponentApi<\"${childComponent.name}\">,`,\n );\n } else {\n const childComponentAnalysis = startPush.analysis[childComponent.path];\n if (!childComponentAnalysis) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `No analysis found for child component ${childComponent.path}`,\n });\n }\n for await (const line of codegenExports(\n ctx,\n childComponent.name,\n childComponentAnalysis,\n )) {\n lines.push(line);\n }\n }\n }\n return lines;\n}\n\nexport async function componentTS(\n ctx: Context,\n startPush: StartPushResponse,\n rootComponent: ComponentDirectory,\n componentDirectory: ComponentDirectory,\n) {\n const definitionPath = toComponentDefinitionPath(\n rootComponent,\n componentDirectory,\n );\n const analysis = startPush.analysis[definitionPath];\n if (!analysis) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `No analysis found for component ${definitionPath} orig: ${definitionPath}\\nin\\n${Object.keys(startPush.analysis).toString()}`,\n });\n }\n\n const lines = [];\n lines.push(header(\"Generated `ComponentApi` utility.\"));\n lines.push(`\n import type { FunctionReference } from \"convex/server\";\n\n /**\n * A utility for referencing a Convex component's exposed API.\n *\n * Useful when expecting a parameter like \\`components.myComponent\\`.\n * Usage:\n * \\`\\`\\`ts\n * async function myFunction(ctx: QueryCtx, component: ComponentApi) {\n * return ctx.runQuery(component.someFile.someQuery, { ...args });\n * }\n * \\`\\`\\`\n */`);\n lines.push(\n `export type ComponentApi<Name extends string | undefined = string | undefined> = `,\n );\n for await (const line of codegenExport(\n ctx,\n analysis,\n analysis.definition.exports,\n \"Name\",\n )) {\n lines.push(line);\n }\n lines.push(`;`);\n return lines.join(\"\\n\");\n}\n\nexport async function componentApiTSWithTypes(\n ctx: Context,\n startPush: StartPushResponse,\n rootComponent: ComponentDirectory,\n componentDirectory: ComponentDirectory,\n componentsMap: Map<string, ComponentDirectory>,\n opts: { staticApi: boolean; useComponentApiImports: boolean },\n) {\n const definitionPath = toComponentDefinitionPath(\n rootComponent,\n componentDirectory,\n );\n\n const analysis = startPush.analysis[definitionPath];\n if (!analysis) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `No analysis found for component ${definitionPath} orig: ${definitionPath}\\nin\\n${Object.keys(startPush.analysis).toString()}`,\n });\n }\n\n const lines = [];\n lines.push(header(\"Generated `api` utility.\"));\n let apiLines: AsyncGenerator<string>;\n if (opts.staticApi) {\n apiLines = codegenStaticApiObjectsTS(ctx, analysis);\n } else {\n apiLines = codegenDynamicApiObjectsTS(ctx, componentDirectory);\n }\n for await (const line of apiLines) {\n lines.push(line);\n }\n\n // Generate components section\n lines.push(`\n export const components = componentsGeneric() as unknown as {`);\n lines.push(\n ...(await componentApiLines(\n ctx,\n startPush,\n analysis,\n rootComponent,\n componentsMap,\n opts,\n )),\n );\n lines.push(\"};\");\n\n return lines.join(\"\\n\");\n}\n\nasync function* codegenStaticApiObjects(\n ctx: Context,\n analysis: EvaluatedComponentDefinition,\n) {\n yield `import type { FunctionReference } from \"convex/server\";`;\n yield `import type { GenericId as Id } from \"convex/values\";`;\n\n const apiTree = await buildApiTree(ctx, analysis.functions, {\n kind: \"public\",\n });\n yield apiComment(\"api\", \"public\");\n yield `export declare const api:`;\n yield* codegenApiTree(ctx, apiTree);\n yield \";\";\n\n yield apiComment(\"internal\", \"internal\");\n const internalTree = await buildApiTree(ctx, analysis.functions, {\n kind: \"internal\",\n });\n yield `export declare const internal:`;\n yield* codegenApiTree(ctx, internalTree);\n yield \";\";\n}\n\nasync function* codegenStaticApiObjectsTS(\n ctx: Context,\n analysis: EvaluatedComponentDefinition,\n) {\n yield `import type { FunctionReference } from \"convex/server\";`;\n yield `import type { GenericId as Id } from \"convex/values\";`;\n yield `import { anyApi, componentsGeneric } from \"convex/server\";`;\n\n const apiTree = await buildApiTree(ctx, analysis.functions, {\n kind: \"public\",\n });\n yield apiComment(\"api\", \"public\");\n yield `export const api:`;\n yield* codegenApiTree(ctx, apiTree);\n yield \"= anyApi as any;\";\n\n yield apiComment(\"internal\", \"internal\");\n const internalTree = await buildApiTree(ctx, analysis.functions, {\n kind: \"internal\",\n });\n yield `export const internal:`;\n yield* codegenApiTree(ctx, internalTree);\n yield \"= anyApi as any;\";\n}\n\nasync function* codegenDynamicApiObjects(\n ctx: Context,\n componentDirectory: ComponentDirectory,\n) {\n const absModulePaths = await entryPoints(ctx, componentDirectory.path);\n const modulePaths = absModulePaths\n .map((p) => path.relative(componentDirectory.path, p))\n .sort();\n for (const modulePath of modulePaths) {\n const ident = moduleIdentifier(modulePath);\n const path = importPath(modulePath);\n yield `import type * as ${ident} from \"../${path}.js\";`;\n }\n yield `\n import type {\n ApiFromModules,\n FilterApi,\n FunctionReference,\n } from \"convex/server\";\n\n declare const fullApi: ApiFromModules<{\n `;\n for (const modulePath of modulePaths) {\n const ident = moduleIdentifier(modulePath);\n const path = importPath(modulePath);\n yield ` \"${path}\": typeof ${ident},`;\n }\n yield `}>;`;\n yield `\n ${apiComment(\"api\", \"public\")}\n export declare const api: FilterApi<typeof fullApi, FunctionReference<any, \"public\">>;\n ${apiComment(\"internal\", \"internal\")}\n export declare const internal: FilterApi<typeof fullApi, FunctionReference<any, \"internal\">>;\n `;\n}\n\nasync function* codegenDynamicApiObjectsTS(\n ctx: Context,\n componentDirectory: ComponentDirectory,\n) {\n const absModulePaths = await entryPoints(ctx, componentDirectory.path);\n const modulePaths = absModulePaths\n .map((p) => path.relative(componentDirectory.path, p))\n .sort();\n for (const modulePath of modulePaths) {\n const ident = moduleIdentifier(modulePath);\n const path = importPath(modulePath);\n yield `import type * as ${ident} from \"../${path}.js\";`;\n }\n yield `\n import type {\n ApiFromModules,\n FilterApi,\n FunctionReference,\n } from \"convex/server\";\n import { anyApi, componentsGeneric } from \"convex/server\";\n\n const fullApi: ApiFromModules<{\n `;\n for (const modulePath of modulePaths) {\n const ident = moduleIdentifier(modulePath);\n const path = importPath(modulePath);\n yield ` \"${path}\": typeof ${ident},`;\n }\n yield `}> = anyApi as any;`;\n yield `\n ${apiComment(\"api\", \"public\")}\n export const api: FilterApi<typeof fullApi, FunctionReference<any, \"public\">> = anyApi as any;\n ${apiComment(\"internal\", \"internal\")}\n export const internal: FilterApi<typeof fullApi, FunctionReference<any, \"internal\">> = anyApi as any;\n `;\n}\n\ninterface ApiTree {\n [identifier: string]:\n | { type: \"branch\"; branch: ApiTree }\n | { type: \"leaf\"; leaf: AnalyzedFunction };\n}\n\nasync function buildApiTree(\n ctx: Context,\n functions: Record<CanonicalizedModulePath, AnalyzedModule>,\n visibility: Visibility,\n): Promise<ApiTree> {\n const root: ApiTree = {};\n for (const [modulePath, module] of Object.entries(functions)) {\n const p = importPath(modulePath);\n if (p.startsWith(\"_deps/\")) {\n continue;\n }\n for (const f of module.functions) {\n if (f.visibility?.kind !== visibility.kind) {\n continue;\n }\n let current = root;\n for (const pathComponent of p.split(\"/\")) {\n let next = current[pathComponent];\n if (!next) {\n next = { type: \"branch\", branch: {} };\n current[pathComponent] = next;\n }\n if (next.type === \"leaf\") {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Ambiguous function name: ${f.name} in ${modulePath}`,\n });\n }\n current = next.branch;\n }\n if (current[f.name]) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Duplicate function name: ${f.name} in ${modulePath}`,\n });\n }\n current[f.name] = { type: \"leaf\", leaf: f };\n }\n }\n return root;\n}\n\nasync function* codegenApiTree(\n ctx: Context,\n tree: ApiTree,\n): AsyncGenerator<string> {\n yield \"{\";\n // Sort entries alphabetically for stable output\n const sortedEntries = Object.entries(tree).sort(([a], [b]) =>\n compareStrings(a, b),\n );\n for (const [identifier, subtree] of sortedEntries) {\n if (subtree.type === \"branch\") {\n yield `\"${identifier}\":`;\n yield* codegenApiTree(ctx, subtree.branch);\n yield \",\";\n } else {\n const visibility = subtree.leaf.visibility?.kind;\n if (!visibility) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Function ${subtree.leaf.name} has no visibility`,\n });\n }\n const ref = await codegenFunctionReference(\n ctx,\n subtree.leaf,\n visibility,\n true,\n undefined,\n );\n yield `\"${identifier}\": ${ref},`;\n }\n }\n yield \"}\";\n}\n\nasync function* codegenExports(\n ctx: Context,\n name: Identifier,\n analysis: EvaluatedComponentDefinition,\n): AsyncGenerator<string> {\n yield `${name}: {`;\n const exports = analysis.definition.exports.branch;\n const entries = Array.from(exports).sort(([a], [b]) => compareStrings(a, b));\n for (const [name, componentExport] of entries) {\n yield `${name}:`;\n yield* codegenExport(ctx, analysis, componentExport, undefined);\n yield \",\";\n }\n yield \"},\";\n}\n\nasync function* codegenExport(\n ctx: Context,\n analysis: EvaluatedComponentDefinition,\n componentExport: ComponentExports,\n componentPath: string | undefined,\n): AsyncGenerator<string> {\n if (componentExport.type === \"leaf\") {\n yield await resolveFunctionReference(\n ctx,\n analysis,\n componentExport.leaf,\n \"internal\",\n componentPath,\n );\n } else if (componentExport.type === \"branch\") {\n yield \"{\";\n const entries = Array.from(componentExport.branch).sort(([a], [b]) =>\n compareStrings(a, b),\n );\n for (const [name, childExport] of entries) {\n yield `${name}:`;\n yield* codegenExport(ctx, analysis, childExport, componentPath);\n yield \",\";\n }\n yield \"}\";\n }\n}\n\nexport async function resolveFunctionReference(\n ctx: Context,\n analysis: EvaluatedComponentDefinition,\n reference: Reference,\n visibility: \"public\" | \"internal\",\n componentPath: string | undefined,\n) {\n if (!reference.startsWith(\"_reference/function/\")) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Invalid function reference: ${reference}`,\n });\n }\n const udfPath = reference.slice(\"_reference/function/\".length);\n\n const [modulePath, functionName] = udfPath.split(\":\");\n const canonicalizedModulePath = canonicalizeModulePath(modulePath);\n\n const analyzedModule = analysis.functions[canonicalizedModulePath];\n if (!analyzedModule) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Module not found: ${modulePath}`,\n });\n }\n const analyzedFunction = analyzedModule.functions.find(\n (f) => f.name === functionName,\n );\n if (!analyzedFunction) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Function not found: ${functionName}`,\n });\n }\n return await codegenFunctionReference(\n ctx,\n analyzedFunction,\n visibility,\n false,\n componentPath,\n );\n}\n\nasync function codegenFunctionReference(\n ctx: Context,\n analyzedFunction: AnalyzedFunction,\n visibility: \"public\" | \"internal\",\n useIdType: boolean,\n componentPath: string | undefined,\n): Promise<string> {\n // The server sends down `udfType` capitalized.\n const udfType = analyzedFunction.udfType.toLowerCase();\n\n let argsType = \"any\";\n try {\n const argsValidator = parseValidator(analyzedFunction.args);\n if (argsValidator) {\n if (argsValidator.type === \"object\" || argsValidator.type === \"any\") {\n argsType = validatorToType(argsValidator, useIdType);\n } else {\n // eslint-disable-next-line no-restricted-syntax\n throw new Error(\n `Unexpected argument validator type: ${argsValidator.type}`,\n );\n }\n }\n } catch (e) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Invalid function args: ${analyzedFunction.args}`,\n errForSentry: e,\n });\n }\n\n let returnsType = \"any\";\n try {\n const returnsValidator = parseValidator(analyzedFunction.returns);\n if (returnsValidator) {\n returnsType = validatorToType(returnsValidator, useIdType);\n }\n } catch (e) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Invalid function returns: ${analyzedFunction.returns}`,\n errForSentry: e,\n });\n }\n\n return `FunctionReference<\"${udfType}\", \"${visibility}\", ${argsType}, ${returnsType}${componentPath ? `, ${componentPath}` : \"\"}>`;\n}\n\nfunction canonicalizeModulePath(modulePath: string): CanonicalizedModulePath {\n if (!modulePath.endsWith(\".js\")) {\n return modulePath + \".js\";\n }\n return modulePath;\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAiB;AAEjB,qBAA4B;AAC5B,gCAKO;AAEP,iBAA6C;AAC7C,
|
|
4
|
+
"sourcesContent": ["import path from \"path\";\nimport { Context } from \"../../bundler/context.js\";\nimport { entryPoints } from \"../../bundler/index.js\";\nimport {\n ComponentDirectory,\n toAbsolutePath,\n toComponentDefinitionPath,\n ComponentDefinitionPath,\n} from \"../lib/components/definition/directoryStructure.js\";\nimport { StartPushResponse } from \"../lib/deployApi/startPush.js\";\nimport { importPath, moduleIdentifier } from \"./api.js\";\nimport {\n apiComment,\n compareModulePaths,\n compareStrings,\n header,\n} from \"./common.js\";\nimport {\n ComponentExports,\n EvaluatedComponentDefinition,\n} from \"../lib/deployApi/componentDefinition.js\";\nimport { Identifier, Reference } from \"../lib/deployApi/types.js\";\nimport { CanonicalizedModulePath } from \"../lib/deployApi/paths.js\";\nimport {\n AnalyzedFunction,\n AnalyzedModule,\n Visibility,\n} from \"../lib/deployApi/modules.js\";\nimport { parseValidator, validatorToType } from \"./validator_helpers.js\";\n\nexport function componentApiJs() {\n const lines = [];\n lines.push(header(\"Generated `api` utility.\"));\n lines.push(`\n import { anyApi, componentsGeneric } from \"convex/server\";\n\n ${apiComment(\"api\", undefined)}\n export const api = anyApi;\n export const internal = anyApi;\n export const components = componentsGeneric();\n `);\n return lines.join(\"\\n\");\n}\n\nexport function rootComponentApiCJS() {\n const lines = [];\n lines.push(header(\"Generated `api` utility.\"));\n lines.push(`const { anyApi } = require(\"convex/server\");`);\n lines.push(`module.exports = {\n api: anyApi,\n internal: anyApi,\n };`);\n return lines.join(\"\\n\");\n}\n\nexport function componentApiStubDTS() {\n const lines = [];\n lines.push(header(\"Generated `api` utility.\"));\n lines.push(`import type { AnyApi, AnyComponents } from \"convex/server\";`);\n lines.push(`\n export declare const api: AnyApi;\n export declare const internal: AnyApi;\n export declare const components: AnyComponents;\n `);\n\n return lines.join(\"\\n\");\n}\n\n// This is also used for root components\nexport function componentApiStubTS() {\n const lines = [];\n lines.push(header(\"Generated `api` utility.\"));\n lines.push(`\n import type { AnyApi, AnyComponents } from \"convex/server\";\n import { anyApi, componentsGeneric } from \"convex/server\";\n\n export const api: AnyApi = anyApi;\n export const internal: AnyApi = anyApi;\n export const components: AnyComponents = componentsGeneric();\n `);\n return lines.join(\"\\n\");\n}\n\nexport async function componentApiDTS(\n ctx: Context,\n startPush: StartPushResponse,\n rootComponent: ComponentDirectory,\n componentDirectory: ComponentDirectory,\n componentsMap: Map<string, ComponentDirectory>,\n opts: { staticApi: boolean; useComponentApiImports: boolean },\n) {\n const definitionPath = toComponentDefinitionPath(\n rootComponent,\n componentDirectory,\n );\n\n const analysis = startPush.analysis[definitionPath];\n if (!analysis) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `No analysis found for component ${definitionPath} orig: ${definitionPath}\\nin\\n${Object.keys(startPush.analysis).toString()}`,\n });\n }\n\n const lines = [];\n lines.push(header(\"Generated `api` utility.\"));\n let apiLines: AsyncGenerator<string>;\n if (opts.staticApi) {\n apiLines = codegenStaticApiObjects(ctx, analysis);\n } else {\n apiLines = codegenDynamicApiObjects(ctx, componentDirectory);\n }\n for await (const line of apiLines) {\n lines.push(line);\n }\n\n lines.push(`\n export declare const components: {`);\n lines.push(\n ...(await componentApiLines(\n ctx,\n startPush,\n analysis,\n rootComponent,\n componentsMap,\n opts,\n )),\n );\n lines.push(\"};\");\n return lines.join(\"\\n\");\n}\n\nasync function componentApiLines(\n ctx: Context,\n startPush: StartPushResponse,\n analysis: EvaluatedComponentDefinition,\n rootComponent: ComponentDirectory,\n componentsMap: Map<string, ComponentDirectory>,\n opts: { useComponentApiImports: boolean },\n): Promise<string[]> {\n const lines: string[] = [];\n for (const childComponent of analysis.definition.childComponents) {\n if (opts.useComponentApiImports) {\n const absolutePath = toAbsolutePath(\n rootComponent,\n childComponent.path as ComponentDefinitionPath,\n );\n\n let childComponentWithRelativePath = componentsMap?.get(absolutePath);\n if (!childComponentWithRelativePath) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Invalid child component directory: ${childComponent.path}`,\n });\n }\n\n // If the user uses a different import specifier than the absolute path of the child component, use the import specifier.\n const importSpecifier = childComponentWithRelativePath.importSpecifier;\n let importPath;\n // Don't trust relative specifiers\n if (\n importSpecifier &&\n !importSpecifier.startsWith(\".\") &&\n importSpecifier !== childComponent.path\n ) {\n importPath = importSpecifier;\n } else {\n importPath = `../${childComponent.path}`;\n }\n lines.push(\n ` \"${childComponent.name}\": import(\"${importPath}/_generated/component.js\").ComponentApi<\"${childComponent.name}\">,`,\n );\n } else {\n const childComponentAnalysis = startPush.analysis[childComponent.path];\n if (!childComponentAnalysis) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `No analysis found for child component ${childComponent.path}`,\n });\n }\n for await (const line of codegenExports(\n ctx,\n childComponent.name,\n childComponentAnalysis,\n )) {\n lines.push(line);\n }\n }\n }\n return lines;\n}\n\nexport async function componentTS(\n ctx: Context,\n startPush: StartPushResponse,\n rootComponent: ComponentDirectory,\n componentDirectory: ComponentDirectory,\n) {\n const definitionPath = toComponentDefinitionPath(\n rootComponent,\n componentDirectory,\n );\n const analysis = startPush.analysis[definitionPath];\n if (!analysis) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `No analysis found for component ${definitionPath} orig: ${definitionPath}\\nin\\n${Object.keys(startPush.analysis).toString()}`,\n });\n }\n\n const lines = [];\n lines.push(header(\"Generated `ComponentApi` utility.\"));\n lines.push(`\n import type { FunctionReference } from \"convex/server\";\n\n /**\n * A utility for referencing a Convex component's exposed API.\n *\n * Useful when expecting a parameter like \\`components.myComponent\\`.\n * Usage:\n * \\`\\`\\`ts\n * async function myFunction(ctx: QueryCtx, component: ComponentApi) {\n * return ctx.runQuery(component.someFile.someQuery, { ...args });\n * }\n * \\`\\`\\`\n */`);\n lines.push(\n `export type ComponentApi<Name extends string | undefined = string | undefined> = `,\n );\n for await (const line of codegenExport(\n ctx,\n analysis,\n analysis.definition.exports,\n \"Name\",\n )) {\n lines.push(line);\n }\n lines.push(`;`);\n return lines.join(\"\\n\");\n}\n\nexport async function componentApiTSWithTypes(\n ctx: Context,\n startPush: StartPushResponse,\n rootComponent: ComponentDirectory,\n componentDirectory: ComponentDirectory,\n componentsMap: Map<string, ComponentDirectory>,\n opts: { staticApi: boolean; useComponentApiImports: boolean },\n) {\n const definitionPath = toComponentDefinitionPath(\n rootComponent,\n componentDirectory,\n );\n\n const analysis = startPush.analysis[definitionPath];\n if (!analysis) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `No analysis found for component ${definitionPath} orig: ${definitionPath}\\nin\\n${Object.keys(startPush.analysis).toString()}`,\n });\n }\n\n const lines = [];\n lines.push(header(\"Generated `api` utility.\"));\n let apiLines: AsyncGenerator<string>;\n if (opts.staticApi) {\n apiLines = codegenStaticApiObjectsTS(ctx, analysis);\n } else {\n apiLines = codegenDynamicApiObjectsTS(ctx, componentDirectory);\n }\n for await (const line of apiLines) {\n lines.push(line);\n }\n\n // Generate components section\n lines.push(`\n export const components = componentsGeneric() as unknown as {`);\n lines.push(\n ...(await componentApiLines(\n ctx,\n startPush,\n analysis,\n rootComponent,\n componentsMap,\n opts,\n )),\n );\n lines.push(\"};\");\n\n return lines.join(\"\\n\");\n}\n\nasync function* codegenStaticApiObjects(\n ctx: Context,\n analysis: EvaluatedComponentDefinition,\n) {\n yield `import type { FunctionReference } from \"convex/server\";`;\n yield `import type { GenericId as Id } from \"convex/values\";`;\n\n const apiTree = await buildApiTree(ctx, analysis.functions, {\n kind: \"public\",\n });\n yield apiComment(\"api\", \"public\");\n yield `export declare const api:`;\n yield* codegenApiTree(ctx, apiTree);\n yield \";\";\n\n yield apiComment(\"internal\", \"internal\");\n const internalTree = await buildApiTree(ctx, analysis.functions, {\n kind: \"internal\",\n });\n yield `export declare const internal:`;\n yield* codegenApiTree(ctx, internalTree);\n yield \";\";\n}\n\nasync function* codegenStaticApiObjectsTS(\n ctx: Context,\n analysis: EvaluatedComponentDefinition,\n) {\n yield `import type { FunctionReference } from \"convex/server\";`;\n yield `import type { GenericId as Id } from \"convex/values\";`;\n yield `import { anyApi, componentsGeneric } from \"convex/server\";`;\n\n const apiTree = await buildApiTree(ctx, analysis.functions, {\n kind: \"public\",\n });\n yield apiComment(\"api\", \"public\");\n yield `export const api:`;\n yield* codegenApiTree(ctx, apiTree);\n yield \"= anyApi as any;\";\n\n yield apiComment(\"internal\", \"internal\");\n const internalTree = await buildApiTree(ctx, analysis.functions, {\n kind: \"internal\",\n });\n yield `export const internal:`;\n yield* codegenApiTree(ctx, internalTree);\n yield \"= anyApi as any;\";\n}\n\nasync function* codegenDynamicApiObjects(\n ctx: Context,\n componentDirectory: ComponentDirectory,\n) {\n const absModulePaths = await entryPoints(ctx, componentDirectory.path);\n const modulePaths = absModulePaths\n .map((p) => path.relative(componentDirectory.path, p))\n .sort(compareModulePaths);\n for (const modulePath of modulePaths) {\n const ident = moduleIdentifier(modulePath);\n const path = importPath(modulePath);\n yield `import type * as ${ident} from \"../${path}.js\";`;\n }\n yield `\n import type {\n ApiFromModules,\n FilterApi,\n FunctionReference,\n } from \"convex/server\";\n\n declare const fullApi: ApiFromModules<{\n `;\n for (const modulePath of modulePaths) {\n const ident = moduleIdentifier(modulePath);\n const path = importPath(modulePath);\n yield ` \"${path}\": typeof ${ident},`;\n }\n yield `}>;`;\n yield `\n ${apiComment(\"api\", \"public\")}\n export declare const api: FilterApi<typeof fullApi, FunctionReference<any, \"public\">>;\n ${apiComment(\"internal\", \"internal\")}\n export declare const internal: FilterApi<typeof fullApi, FunctionReference<any, \"internal\">>;\n `;\n}\n\nasync function* codegenDynamicApiObjectsTS(\n ctx: Context,\n componentDirectory: ComponentDirectory,\n) {\n const absModulePaths = await entryPoints(ctx, componentDirectory.path);\n const modulePaths = absModulePaths\n .map((p) => path.relative(componentDirectory.path, p))\n .sort(compareModulePaths);\n for (const modulePath of modulePaths) {\n const ident = moduleIdentifier(modulePath);\n const path = importPath(modulePath);\n yield `import type * as ${ident} from \"../${path}.js\";`;\n }\n yield `\n import type {\n ApiFromModules,\n FilterApi,\n FunctionReference,\n } from \"convex/server\";\n import { anyApi, componentsGeneric } from \"convex/server\";\n\n const fullApi: ApiFromModules<{\n `;\n for (const modulePath of modulePaths) {\n const ident = moduleIdentifier(modulePath);\n const path = importPath(modulePath);\n yield ` \"${path}\": typeof ${ident},`;\n }\n yield `}> = anyApi as any;`;\n yield `\n ${apiComment(\"api\", \"public\")}\n export const api: FilterApi<typeof fullApi, FunctionReference<any, \"public\">> = anyApi as any;\n ${apiComment(\"internal\", \"internal\")}\n export const internal: FilterApi<typeof fullApi, FunctionReference<any, \"internal\">> = anyApi as any;\n `;\n}\n\ninterface ApiTree {\n [identifier: string]:\n | { type: \"branch\"; branch: ApiTree }\n | { type: \"leaf\"; leaf: AnalyzedFunction };\n}\n\nasync function buildApiTree(\n ctx: Context,\n functions: Record<CanonicalizedModulePath, AnalyzedModule>,\n visibility: Visibility,\n): Promise<ApiTree> {\n const root: ApiTree = {};\n for (const [modulePath, module] of Object.entries(functions)) {\n const p = importPath(modulePath);\n if (p.startsWith(\"_deps/\")) {\n continue;\n }\n for (const f of module.functions) {\n if (f.visibility?.kind !== visibility.kind) {\n continue;\n }\n let current = root;\n for (const pathComponent of p.split(\"/\")) {\n let next = current[pathComponent];\n if (!next) {\n next = { type: \"branch\", branch: {} };\n current[pathComponent] = next;\n }\n if (next.type === \"leaf\") {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Ambiguous function name: ${f.name} in ${modulePath}`,\n });\n }\n current = next.branch;\n }\n if (current[f.name]) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Duplicate function name: ${f.name} in ${modulePath}`,\n });\n }\n current[f.name] = { type: \"leaf\", leaf: f };\n }\n }\n return root;\n}\n\nasync function* codegenApiTree(\n ctx: Context,\n tree: ApiTree,\n): AsyncGenerator<string> {\n yield \"{\";\n // Sort entries alphabetically for stable output\n const sortedEntries = Object.entries(tree).sort(([a], [b]) =>\n compareStrings(a, b),\n );\n for (const [identifier, subtree] of sortedEntries) {\n if (subtree.type === \"branch\") {\n yield `\"${identifier}\":`;\n yield* codegenApiTree(ctx, subtree.branch);\n yield \",\";\n } else {\n const visibility = subtree.leaf.visibility?.kind;\n if (!visibility) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Function ${subtree.leaf.name} has no visibility`,\n });\n }\n const ref = await codegenFunctionReference(\n ctx,\n subtree.leaf,\n visibility,\n true,\n undefined,\n );\n yield `\"${identifier}\": ${ref},`;\n }\n }\n yield \"}\";\n}\n\nasync function* codegenExports(\n ctx: Context,\n name: Identifier,\n analysis: EvaluatedComponentDefinition,\n): AsyncGenerator<string> {\n yield `${name}: {`;\n const exports = analysis.definition.exports.branch;\n const entries = Array.from(exports).sort(([a], [b]) => compareStrings(a, b));\n for (const [name, componentExport] of entries) {\n yield `${name}:`;\n yield* codegenExport(ctx, analysis, componentExport, undefined);\n yield \",\";\n }\n yield \"},\";\n}\n\nasync function* codegenExport(\n ctx: Context,\n analysis: EvaluatedComponentDefinition,\n componentExport: ComponentExports,\n componentPath: string | undefined,\n): AsyncGenerator<string> {\n if (componentExport.type === \"leaf\") {\n yield await resolveFunctionReference(\n ctx,\n analysis,\n componentExport.leaf,\n \"internal\",\n componentPath,\n );\n } else if (componentExport.type === \"branch\") {\n yield \"{\";\n const entries = Array.from(componentExport.branch).sort(([a], [b]) =>\n compareStrings(a, b),\n );\n for (const [name, childExport] of entries) {\n yield `${name}:`;\n yield* codegenExport(ctx, analysis, childExport, componentPath);\n yield \",\";\n }\n yield \"}\";\n }\n}\n\nexport async function resolveFunctionReference(\n ctx: Context,\n analysis: EvaluatedComponentDefinition,\n reference: Reference,\n visibility: \"public\" | \"internal\",\n componentPath: string | undefined,\n) {\n if (!reference.startsWith(\"_reference/function/\")) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Invalid function reference: ${reference}`,\n });\n }\n const udfPath = reference.slice(\"_reference/function/\".length);\n\n const [modulePath, functionName] = udfPath.split(\":\");\n const canonicalizedModulePath = canonicalizeModulePath(modulePath);\n\n const analyzedModule = analysis.functions[canonicalizedModulePath];\n if (!analyzedModule) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Module not found: ${modulePath}`,\n });\n }\n const analyzedFunction = analyzedModule.functions.find(\n (f) => f.name === functionName,\n );\n if (!analyzedFunction) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Function not found: ${functionName}`,\n });\n }\n return await codegenFunctionReference(\n ctx,\n analyzedFunction,\n visibility,\n false,\n componentPath,\n );\n}\n\nasync function codegenFunctionReference(\n ctx: Context,\n analyzedFunction: AnalyzedFunction,\n visibility: \"public\" | \"internal\",\n useIdType: boolean,\n componentPath: string | undefined,\n): Promise<string> {\n // The server sends down `udfType` capitalized.\n const udfType = analyzedFunction.udfType.toLowerCase();\n\n let argsType = \"any\";\n try {\n const argsValidator = parseValidator(analyzedFunction.args);\n if (argsValidator) {\n if (argsValidator.type === \"object\" || argsValidator.type === \"any\") {\n argsType = validatorToType(argsValidator, useIdType);\n } else {\n // eslint-disable-next-line no-restricted-syntax\n throw new Error(\n `Unexpected argument validator type: ${argsValidator.type}`,\n );\n }\n }\n } catch (e) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Invalid function args: ${analyzedFunction.args}`,\n errForSentry: e,\n });\n }\n\n let returnsType = \"any\";\n try {\n const returnsValidator = parseValidator(analyzedFunction.returns);\n if (returnsValidator) {\n returnsType = validatorToType(returnsValidator, useIdType);\n }\n } catch (e) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Invalid function returns: ${analyzedFunction.returns}`,\n errForSentry: e,\n });\n }\n\n return `FunctionReference<\"${udfType}\", \"${visibility}\", ${argsType}, ${returnsType}${componentPath ? `, ${componentPath}` : \"\"}>`;\n}\n\nfunction canonicalizeModulePath(modulePath: string): CanonicalizedModulePath {\n if (!modulePath.endsWith(\".js\")) {\n return modulePath + \".js\";\n }\n return modulePath;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["importPath", "path", "module", "name"]
|
|
7
7
|
}
|
|
@@ -36,6 +36,8 @@ function validatorToType(validator, useIdType) {
|
|
|
36
36
|
return "number";
|
|
37
37
|
} else if (validator.type === "bigint") {
|
|
38
38
|
return "bigint";
|
|
39
|
+
} else if (validator.type === "commitTs") {
|
|
40
|
+
return 'bigint | import("convex/values").CommitTsPlaceholder';
|
|
39
41
|
} else if (validator.type === "boolean") {
|
|
40
42
|
return "boolean";
|
|
41
43
|
} else if (validator.type === "string") {
|