convex 1.36.0 → 1.37.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 +50 -27
- package/dist/browser.bundle.js +1 -1
- package/dist/browser.bundle.js.map +1 -1
- package/dist/cjs/cli/codegen_templates/agentsmd.js +8 -2
- package/dist/cjs/cli/codegen_templates/agentsmd.js.map +2 -2
- package/dist/cjs/cli/codegen_templates/claudemd.js +2 -0
- package/dist/cjs/cli/codegen_templates/claudemd.js.map +2 -2
- package/dist/cjs/cli/configure.js +0 -8
- package/dist/cjs/cli/configure.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/deploymentToken.js +30 -0
- package/dist/cjs/cli/deploymentToken.js.map +7 -0
- package/dist/cjs/cli/deploymentTokenCreate.js +109 -0
- package/dist/cjs/cli/deploymentTokenCreate.js.map +7 -0
- package/dist/cjs/cli/deploymentTokenDelete.js +87 -0
- package/dist/cjs/cli/deploymentTokenDelete.js.map +7 -0
- package/dist/cjs/cli/envDefault.js +130 -41
- package/dist/cjs/cli/envDefault.js.map +3 -3
- package/dist/cjs/cli/generatedApi.js.map +1 -1
- package/dist/cjs/cli/lib/command.js +1 -1
- package/dist/cjs/cli/lib/command.js.map +1 -1
- package/dist/cjs/cli/lib/generatedFunctionLogsApi.js.map +1 -1
- package/dist/cjs/cli/lib/login.js +51 -0
- package/dist/cjs/cli/lib/login.js.map +3 -3
- package/dist/cjs/cli/lib/usage.js +13 -6
- package/dist/cjs/cli/lib/usage.js.map +2 -2
- package/dist/cjs/cli/lib/workos/environmentApi.js +6 -12
- package/dist/cjs/cli/lib/workos/environmentApi.js.map +3 -3
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react/client.js +40 -42
- package/dist/cjs/react/client.js.map +2 -2
- package/dist/cjs/react/index.js +1 -0
- package/dist/cjs/react/index.js.map +2 -2
- package/dist/cjs/react/use_paginated_query.js +5 -46
- package/dist/cjs/react/use_paginated_query.js.map +2 -2
- package/dist/cjs/react/use_paginated_query2.js.map +2 -2
- package/dist/cjs/server/audit_logging.js +67 -0
- package/dist/cjs/server/audit_logging.js.map +7 -0
- package/dist/cjs/server/impl/meta_impl.js +27 -3
- package/dist/cjs/server/impl/meta_impl.js.map +2 -2
- package/dist/cjs/server/impl/registration_impl.js +2 -0
- package/dist/cjs/server/impl/registration_impl.js.map +2 -2
- package/dist/cjs/server/index.js +2 -0
- package/dist/cjs/server/index.js.map +2 -2
- package/dist/cjs/server/log.js +30 -0
- package/dist/cjs/server/log.js.map +7 -0
- package/dist/cjs/server/logVars.js +48 -0
- package/dist/cjs/server/logVars.js.map +7 -0
- package/dist/cjs/server/meta.js.map +1 -1
- package/dist/cjs/server/registration.js.map +1 -1
- package/dist/cjs-types/cli/codegen_templates/agentsmd.d.ts.map +1 -1
- package/dist/cjs-types/cli/codegen_templates/claudemd.d.ts.map +1 -1
- package/dist/cjs-types/cli/configure.d.ts.map +1 -1
- package/dist/cjs-types/cli/deployment.d.ts.map +1 -1
- package/dist/cjs-types/cli/deploymentToken.d.ts +3 -0
- package/dist/cjs-types/cli/deploymentToken.d.ts.map +1 -0
- package/dist/cjs-types/cli/deploymentToken.test.d.ts +2 -0
- package/dist/cjs-types/cli/deploymentToken.test.d.ts.map +1 -0
- package/dist/cjs-types/cli/deploymentTokenCreate.d.ts +13 -0
- package/dist/cjs-types/cli/deploymentTokenCreate.d.ts.map +1 -0
- package/dist/cjs-types/cli/deploymentTokenDelete.d.ts +11 -0
- package/dist/cjs-types/cli/deploymentTokenDelete.d.ts.map +1 -0
- package/dist/cjs-types/cli/envDefault.d.ts +2 -2
- package/dist/cjs-types/cli/envDefault.d.ts.map +1 -1
- package/dist/cjs-types/cli/envDefault.test.d.ts +2 -0
- package/dist/cjs-types/cli/envDefault.test.d.ts.map +1 -0
- package/dist/cjs-types/cli/generatedApi.d.ts +1 -1
- package/dist/cjs-types/cli/generatedApi.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts +1 -0
- package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/login.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/usage.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/workos/environmentApi.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/workos/environmentApi.test.d.ts +2 -0
- package/dist/cjs-types/cli/lib/workos/environmentApi.test.d.ts.map +1 -0
- package/dist/cjs-types/index.d.ts +1 -1
- package/dist/cjs-types/react/client.d.ts +52 -0
- package/dist/cjs-types/react/client.d.ts.map +1 -1
- package/dist/cjs-types/react/index.d.ts +2 -2
- package/dist/cjs-types/react/index.d.ts.map +1 -1
- package/dist/cjs-types/react/use_paginated_query.d.ts.map +1 -1
- package/dist/cjs-types/react/use_paginated_query2.d.ts +63 -1
- package/dist/cjs-types/react/use_paginated_query2.d.ts.map +1 -1
- package/dist/cjs-types/server/api.intersect.test.d.ts +2 -0
- package/dist/cjs-types/server/api.intersect.test.d.ts.map +1 -0
- package/dist/cjs-types/server/audit_logging.d.ts +19 -0
- package/dist/cjs-types/server/audit_logging.d.ts.map +1 -0
- package/dist/cjs-types/server/audit_logging.test.d.ts +2 -0
- package/dist/cjs-types/server/audit_logging.test.d.ts.map +1 -0
- package/dist/cjs-types/server/impl/meta_impl.d.ts.map +1 -1
- package/dist/cjs-types/server/impl/registration_impl.d.ts.map +1 -1
- package/dist/cjs-types/server/index.d.ts +2 -2
- package/dist/cjs-types/server/index.d.ts.map +1 -1
- package/dist/cjs-types/server/log.d.ts +2 -0
- package/dist/cjs-types/server/log.d.ts.map +1 -0
- package/dist/cjs-types/server/logVars.d.ts +20 -0
- package/dist/cjs-types/server/logVars.d.ts.map +1 -0
- package/dist/cjs-types/server/meta.d.ts +40 -0
- package/dist/cjs-types/server/meta.d.ts.map +1 -1
- package/dist/cjs-types/server/registration.d.ts +5 -2
- package/dist/cjs-types/server/registration.d.ts.map +1 -1
- package/dist/cli.bundle.cjs +362 -74
- package/dist/cli.bundle.cjs.map +4 -4
- package/dist/esm/cli/codegen_templates/agentsmd.js +8 -2
- package/dist/esm/cli/codegen_templates/agentsmd.js.map +2 -2
- package/dist/esm/cli/codegen_templates/claudemd.js +2 -0
- package/dist/esm/cli/codegen_templates/claudemd.js.map +2 -2
- package/dist/esm/cli/configure.js +0 -8
- package/dist/esm/cli/configure.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/deploymentToken.js +8 -0
- package/dist/esm/cli/deploymentToken.js.map +7 -0
- package/dist/esm/cli/deploymentTokenCreate.js +91 -0
- package/dist/esm/cli/deploymentTokenCreate.js.map +7 -0
- package/dist/esm/cli/deploymentTokenDelete.js +68 -0
- package/dist/esm/cli/deploymentTokenDelete.js.map +7 -0
- package/dist/esm/cli/envDefault.js +131 -42
- package/dist/esm/cli/envDefault.js.map +3 -3
- package/dist/esm/cli/lib/command.js +1 -1
- package/dist/esm/cli/lib/command.js.map +1 -1
- package/dist/esm/cli/lib/login.js +52 -0
- package/dist/esm/cli/lib/login.js.map +3 -3
- package/dist/esm/cli/lib/usage.js +15 -8
- package/dist/esm/cli/lib/usage.js.map +2 -2
- package/dist/esm/cli/lib/workos/environmentApi.js +6 -12
- package/dist/esm/cli/lib/workos/environmentApi.js.map +3 -3
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/client.js +38 -41
- package/dist/esm/react/client.js.map +2 -2
- package/dist/esm/react/index.js +4 -1
- package/dist/esm/react/index.js.map +2 -2
- package/dist/esm/react/use_paginated_query.js +5 -46
- package/dist/esm/react/use_paginated_query.js.map +2 -2
- package/dist/esm/react/use_paginated_query2.js.map +2 -2
- package/dist/esm/server/audit_logging.js +44 -0
- package/dist/esm/server/audit_logging.js.map +7 -0
- package/dist/esm/server/impl/meta_impl.js +27 -3
- package/dist/esm/server/impl/meta_impl.js.map +2 -2
- package/dist/esm/server/impl/registration_impl.js +2 -0
- package/dist/esm/server/impl/registration_impl.js.map +2 -2
- package/dist/esm/server/index.js +1 -0
- package/dist/esm/server/index.js.map +2 -2
- package/dist/esm/server/log.js +8 -0
- package/dist/esm/server/log.js.map +7 -0
- package/dist/esm/server/logVars.js +25 -0
- package/dist/esm/server/logVars.js.map +7 -0
- package/dist/esm-types/cli/codegen_templates/agentsmd.d.ts.map +1 -1
- package/dist/esm-types/cli/codegen_templates/claudemd.d.ts.map +1 -1
- package/dist/esm-types/cli/configure.d.ts.map +1 -1
- package/dist/esm-types/cli/deployment.d.ts.map +1 -1
- package/dist/esm-types/cli/deploymentToken.d.ts +3 -0
- package/dist/esm-types/cli/deploymentToken.d.ts.map +1 -0
- package/dist/esm-types/cli/deploymentToken.test.d.ts +2 -0
- package/dist/esm-types/cli/deploymentToken.test.d.ts.map +1 -0
- package/dist/esm-types/cli/deploymentTokenCreate.d.ts +13 -0
- package/dist/esm-types/cli/deploymentTokenCreate.d.ts.map +1 -0
- package/dist/esm-types/cli/deploymentTokenDelete.d.ts +11 -0
- package/dist/esm-types/cli/deploymentTokenDelete.d.ts.map +1 -0
- package/dist/esm-types/cli/envDefault.d.ts +2 -2
- package/dist/esm-types/cli/envDefault.d.ts.map +1 -1
- package/dist/esm-types/cli/envDefault.test.d.ts +2 -0
- package/dist/esm-types/cli/envDefault.test.d.ts.map +1 -0
- package/dist/esm-types/cli/generatedApi.d.ts +1 -1
- package/dist/esm-types/cli/generatedApi.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts +1 -0
- package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/login.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/usage.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/workos/environmentApi.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/workos/environmentApi.test.d.ts +2 -0
- package/dist/esm-types/cli/lib/workos/environmentApi.test.d.ts.map +1 -0
- package/dist/esm-types/index.d.ts +1 -1
- package/dist/esm-types/react/client.d.ts +52 -0
- package/dist/esm-types/react/client.d.ts.map +1 -1
- package/dist/esm-types/react/index.d.ts +2 -2
- package/dist/esm-types/react/index.d.ts.map +1 -1
- package/dist/esm-types/react/use_paginated_query.d.ts.map +1 -1
- package/dist/esm-types/react/use_paginated_query2.d.ts +63 -1
- package/dist/esm-types/react/use_paginated_query2.d.ts.map +1 -1
- package/dist/esm-types/server/api.intersect.test.d.ts +2 -0
- package/dist/esm-types/server/api.intersect.test.d.ts.map +1 -0
- package/dist/esm-types/server/audit_logging.d.ts +19 -0
- package/dist/esm-types/server/audit_logging.d.ts.map +1 -0
- package/dist/esm-types/server/audit_logging.test.d.ts +2 -0
- package/dist/esm-types/server/audit_logging.test.d.ts.map +1 -0
- package/dist/esm-types/server/impl/meta_impl.d.ts.map +1 -1
- package/dist/esm-types/server/impl/registration_impl.d.ts.map +1 -1
- package/dist/esm-types/server/index.d.ts +2 -2
- package/dist/esm-types/server/index.d.ts.map +1 -1
- package/dist/esm-types/server/log.d.ts +2 -0
- package/dist/esm-types/server/log.d.ts.map +1 -0
- package/dist/esm-types/server/logVars.d.ts +20 -0
- package/dist/esm-types/server/logVars.d.ts.map +1 -0
- package/dist/esm-types/server/meta.d.ts +40 -0
- package/dist/esm-types/server/meta.d.ts.map +1 -1
- package/dist/esm-types/server/registration.d.ts +5 -2
- package/dist/esm-types/server/registration.d.ts.map +1 -1
- package/dist/react.bundle.js +45 -88
- package/dist/react.bundle.js.map +2 -2
- package/package.json +4 -4
- package/src/cli/codegen_templates/agentsmd.ts +8 -2
- package/src/cli/codegen_templates/claudemd.ts +2 -0
- package/src/cli/configure.ts +0 -9
- package/src/cli/deployment.ts +3 -1
- package/src/cli/deploymentToken.test.ts +372 -0
- package/src/cli/deploymentToken.ts +11 -0
- package/src/cli/deploymentTokenCreate.ts +113 -0
- package/src/cli/deploymentTokenDelete.ts +91 -0
- package/src/cli/envDefault.test.ts +495 -0
- package/src/cli/envDefault.ts +222 -107
- package/src/cli/generatedApi.ts +1 -1
- package/src/cli/lib/command.ts +1 -1
- package/src/cli/lib/generatedFunctionLogsApi.ts +1 -0
- package/src/cli/lib/login.ts +67 -0
- package/src/cli/lib/usage.ts +18 -8
- package/src/cli/lib/workos/environmentApi.test.ts +107 -0
- package/src/cli/lib/workos/environmentApi.ts +12 -19
- package/src/index.ts +1 -1
- package/src/react/client.test.tsx +10 -8
- package/src/react/client.ts +88 -96
- package/src/react/index.ts +6 -1
- package/src/react/use_paginated_query.test.tsx +215 -132
- package/src/react/use_paginated_query.ts +8 -142
- package/src/react/use_paginated_query2.ts +78 -5
- package/src/react/use_query_object_options.test.ts +8 -7
- package/src/react/use_query_result.test.ts +40 -7
- package/src/server/api.intersect.test.ts +109 -0
- package/src/server/audit_logging.test.ts +129 -0
- package/src/server/audit_logging.ts +75 -0
- package/src/server/impl/meta_impl.ts +28 -0
- package/src/server/impl/registration_impl.ts +2 -0
- package/src/server/index.ts +12 -0
- package/src/server/log.ts +16 -0
- package/src/server/logVars.ts +34 -0
- package/src/server/meta.ts +53 -1
- package/src/server/registration.ts +10 -8
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/react/client.ts"],
|
|
4
|
-
"sourcesContent": ["import { BaseConvexClient } from \"../browser/index.js\";\nimport type {\n OptimisticUpdate,\n PaginatedQueryToken,\n QueryToken,\n PaginationStatus,\n} from \"../browser/index.js\";\nimport React, { useCallback, useContext, useMemo } from \"react\";\nimport { convexToJson, Value } from \"../values/index.js\";\nimport { QueryJournal } from \"../browser/sync/protocol.js\";\nimport {\n AuthTokenFetcher,\n BaseConvexClientOptions,\n ConnectionState,\n} from \"../browser/sync/client.js\";\nimport type { UserIdentityAttributes } from \"../browser/sync/protocol.js\";\nimport { RequestForQueries, useQueries } from \"./use_queries.js\";\nimport { useSubscription } from \"./use_subscription.js\";\nimport { parseArgs } from \"../common/index.js\";\nimport {\n ArgsAndOptions,\n FunctionArgs,\n FunctionReference,\n FunctionReturnType,\n OptionalRestArgs,\n getFunctionName,\n makeFunctionReference,\n} from \"../server/api.js\";\nimport { EmptyObject } from \"../server/registration.js\";\nimport {\n instantiateDefaultLogger,\n instantiateNoopLogger,\n Logger,\n} from \"../browser/logging.js\";\nimport type { QueryOptions } from \"../browser/query_options.js\";\nimport { LoadMoreOfPaginatedQuery } from \"../browser/sync/pagination.js\";\nimport {\n PaginatedQueryClient,\n ExtendedTransition,\n} from \"../browser/sync/paginated_query_client.js\";\n\n// When no arguments are passed, extend subscriptions (for APIs that do this by default)\n// for this amount after the subscription would otherwise be dropped.\nconst DEFAULT_EXTEND_SUBSCRIPTION_FOR = 5_000;\n\nif (typeof React === \"undefined\") {\n throw new Error(\"Required dependency 'react' not found\");\n}\n\n// TODO Typedoc doesn't generate documentation for the comment below perhaps\n// because it's a callable interface.\n/**\n * An interface to execute a Convex mutation function on the server.\n *\n * @public\n */\nexport interface ReactMutation<Mutation extends FunctionReference<\"mutation\">> {\n /**\n * Execute the mutation on the server, returning a `Promise` of its return value.\n *\n * @param args - Arguments for the mutation to pass up to the server.\n * @returns The return value of the server-side function call.\n */\n (...args: OptionalRestArgs<Mutation>): Promise<FunctionReturnType<Mutation>>;\n\n /**\n * Define an optimistic update to apply as part of this mutation.\n *\n * This is a temporary update to the local query results to facilitate a\n * fast, interactive UI. It enables query results to update before a mutation\n * executed on the server.\n *\n * When the mutation is invoked, the optimistic update will be applied.\n *\n * Optimistic updates can also be used to temporarily remove queries from the\n * client and create loading experiences until a mutation completes and the\n * new query results are synced.\n *\n * The update will be automatically rolled back when the mutation is fully\n * completed and queries have been updated.\n *\n * @param optimisticUpdate - The optimistic update to apply.\n * @returns A new `ReactMutation` with the update configured.\n *\n * @public\n */\n withOptimisticUpdate<T extends OptimisticUpdate<FunctionArgs<Mutation>>>(\n optimisticUpdate: T &\n (ReturnType<T> extends Promise<any>\n ? \"Optimistic update handlers must be synchronous\"\n : {}),\n ): ReactMutation<Mutation>;\n}\n\n// Exported only for testing.\nexport function createMutation(\n mutationReference: FunctionReference<\"mutation\">,\n client: ConvexReactClient,\n update?: OptimisticUpdate<any>,\n): ReactMutation<any> {\n function mutation(args?: Record<string, Value>): Promise<unknown> {\n assertNotAccidentalArgument(args);\n\n return client.mutation(mutationReference, args, {\n optimisticUpdate: update,\n });\n }\n mutation.withOptimisticUpdate = function withOptimisticUpdate(\n optimisticUpdate: OptimisticUpdate<any>,\n ): ReactMutation<any> {\n if (update !== undefined) {\n throw new Error(\n `Already specified optimistic update for mutation ${getFunctionName(\n mutationReference,\n )}`,\n );\n }\n return createMutation(mutationReference, client, optimisticUpdate);\n };\n return mutation as ReactMutation<any>;\n}\n\n/**\n * An interface to execute a Convex action on the server.\n *\n * @public\n */\nexport interface ReactAction<Action extends FunctionReference<\"action\">> {\n /**\n * Execute the function on the server, returning a `Promise` of its return value.\n *\n * @param args - Arguments for the function to pass up to the server.\n * @returns The return value of the server-side function call.\n * @public\n */\n (...args: OptionalRestArgs<Action>): Promise<FunctionReturnType<Action>>;\n}\n\nfunction createAction(\n actionReference: FunctionReference<\"action\">,\n client: ConvexReactClient,\n): ReactAction<any> {\n return function (args?: Record<string, Value>): Promise<unknown> {\n return client.action(actionReference, args);\n } as ReactAction<any>;\n}\n\n// Watches should be stateless: in QueriesObserver we create a watch just to get\n// the current value.\n/**\n * A watch on the output of a Convex query function.\n *\n * @public\n */\nexport interface Watch<T> {\n /**\n * Initiate a watch on the output of a query.\n *\n * This will subscribe to this query and call\n * the callback whenever the query result changes.\n *\n * **Important: If the client is already subscribed to this query with the\n * same arguments this callback will not be invoked until the query result is\n * updated.** To get the current, local result call\n * {@link react.Watch.localQueryResult}.\n *\n * @param callback - Function that is called whenever the query result changes.\n * @returns - A function that disposes of the subscription.\n */\n onUpdate(callback: () => void): () => void;\n\n /**\n * Get the current result of a query.\n *\n * This will only return a result if we're already subscribed to the query\n * and have received a result from the server or the query value has been set\n * optimistically.\n *\n * @returns The result of the query or `undefined` if it isn't known.\n * @throws An error if the query encountered an error on the server.\n */\n localQueryResult(): T | undefined;\n\n /**\n * @internal\n */\n localQueryLogs(): string[] | undefined;\n\n /**\n * Get the current {@link browser.QueryJournal} for this query.\n *\n * If we have not yet received a result for this query, this will be `undefined`.\n */\n journal(): QueryJournal | undefined;\n}\n\n/**\n * A watch on the output of a paginated Convex query function.\n *\n * @public\n */\nexport interface PaginatedWatch<T> {\n /**\n * Initiate a watch on the output of a paginated query.\n *\n * This will subscribe to this query and call\n * the callback whenever the query result changes.\n *\n * @param callback - Function that is called whenever the query result changes.\n * @returns - A function that disposes of the subscription.\n */\n onUpdate(callback: () => void): () => void;\n\n /**\n * Get the current result of a paginated query.\n *\n * @returns The current results, status, and loadMore function, or `undefined` if not loaded.\n */\n localQueryResult():\n | {\n results: T[];\n status: PaginationStatus;\n loadMore: LoadMoreOfPaginatedQuery;\n }\n | undefined;\n}\n\n/**\n * Options for {@link ConvexReactClient.watchQuery}.\n *\n * @public\n */\nexport interface WatchQueryOptions {\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;\n}\n\n/**\n * Options for {@link ConvexReactClient.watchPaginatedQuery}.\n *\n * @internal\n */\nexport interface WatchPaginatedQueryOptions {\n /**\n * The initial number of items to load.\n */\n initialNumItems: number;\n\n // We may be able to remove this in the future, but to preserve the existing behavior of\n // usePaginatedQuery() it's still here.\n id: number;\n\n /**\n * @internal\n */\n componentPath?: string;\n}\n\n/**\n * Options for {@link ConvexReactClient.mutation}.\n *\n * @public\n */\nexport interface MutationOptions<Args extends Record<string, Value>> {\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<Args> | undefined;\n}\n\n/**\n * Options for {@link ConvexReactClient}.\n *\n * @public\n */\nexport interface ConvexReactClientOptions extends BaseConvexClientOptions {}\n\n/**\n * A Convex client for use within React.\n *\n * This loads reactive queries and executes mutations over a WebSocket.\n *\n * @public\n */\nexport class ConvexReactClient {\n private address: string;\n private cachedSync?: BaseConvexClient | undefined;\n private cachedPaginatedQueryClient?: PaginatedQueryClient | undefined;\n private listeners: Map<QueryToken | PaginatedQueryToken, Set<() => void>>;\n private options: ConvexReactClientOptions;\n // \"closed\" means this client is done, not just that the underlying WS connection is closed.\n private closed = false;\n private _logger: Logger;\n\n private adminAuth?: string;\n private fakeUserIdentity?: UserIdentityAttributes | 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 options - See {@link ConvexReactClientOptions} for a full description.\n */\n constructor(address: string, options?: ConvexReactClientOptions) {\n // Validate address immediately since validation by the lazily-instantiated\n // internal client does not occur synchronously.\n if (address === undefined) {\n throw new Error(\n \"No address provided to ConvexReactClient.\\n\" +\n \"If trying to deploy to production, make sure to follow all the instructions found at https://docs.convex.dev/production/hosting/\\n\" +\n \"If running locally, make sure to run `convex dev` and ensure the .env.local file is populated.\",\n );\n }\n if (typeof address !== \"string\") {\n throw new Error(\n `ConvexReactClient requires a URL like 'https://happy-otter-123.convex.cloud', received something of type ${typeof address} instead.`,\n );\n }\n if (!address.includes(\"://\")) {\n throw new Error(\"Provided address was not an absolute URL.\");\n }\n this.address = address;\n this.listeners = new Map();\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 this.options = { ...options, logger: this._logger };\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 * Lazily instantiate the `BaseConvexClient` so we don't create the WebSocket\n * when server-side rendering.\n *\n * @internal\n */\n get sync() {\n if (this.closed) {\n throw new Error(\"ConvexReactClient has already been closed.\");\n }\n if (this.cachedSync) {\n return this.cachedSync;\n }\n // BaseConvexClient and paginated query client are always created together.\n this.cachedSync = new BaseConvexClient(\n this.address,\n () => {}, // Use the PaginatedQueryClient's transition instead.\n this.options,\n );\n if (this.adminAuth) {\n this.cachedSync.setAdminAuth(this.adminAuth, this.fakeUserIdentity);\n }\n this.cachedPaginatedQueryClient = new PaginatedQueryClient(\n this.cachedSync,\n (transition) => this.handleTransition(transition),\n );\n return this.cachedSync;\n }\n\n /**\n * Lazily instantiate the `PaginatedQueryClient` so we don't create it\n * when server-side rendering.\n *\n * @internal\n */\n get paginatedQueryClient() {\n // access sync to instantiate the clients\n this.sync;\n if (this.cachedPaginatedQueryClient) {\n return this.cachedPaginatedQueryClient;\n }\n throw new Error(\"Should already be instantiated\");\n }\n\n /**\n * Set the authentication token to be used for subsequent queries and mutations.\n * `fetchToken` will be called automatically again if a token expires.\n * `fetchToken` should return `null` if the token cannot be retrieved, for example\n * when the user's rights were permanently revoked.\n * @param fetchToken - an async function returning the JWT-encoded OpenID Connect Identity Token\n * @param onChange - a callback that will be called when the authentication status changes\n */\n setAuth(\n fetchToken: AuthTokenFetcher,\n onChange?: (isAuthenticated: boolean) => void,\n ) {\n if (typeof fetchToken === \"string\") {\n throw new Error(\n \"Passing a string to ConvexReactClient.setAuth is no longer supported, \" +\n \"please upgrade to passing in an async function to handle reauthentication.\",\n );\n }\n this.sync.setAuth(\n fetchToken,\n onChange ??\n (() => {\n // Do nothing\n }),\n );\n }\n\n /**\n * Clear the current authentication token if set.\n */\n clearAuth() {\n this.sync.clearAuth();\n }\n\n /**\n * @internal\n */\n setAdminAuth(token: string, identity?: UserIdentityAttributes) {\n this.adminAuth = token;\n this.fakeUserIdentity = identity;\n if (this.closed) {\n throw new Error(\"ConvexReactClient has already been closed.\");\n }\n if (this.cachedSync) {\n this.sync.setAdminAuth(token, identity);\n }\n }\n\n /**\n * Construct a new {@link Watch} on a Convex query function.\n *\n * **Most application code should not call this method directly. Instead use\n * the {@link useQuery} hook.**\n *\n * The act of creating a watch does nothing, a Watch is stateless.\n *\n * @param query - A {@link server.FunctionReference} for the public query to run.\n * @param args - An arguments object for the query. If this is omitted,\n * the arguments will be `{}`.\n * @param options - A {@link WatchQueryOptions} options object for this query.\n *\n * @returns The {@link Watch} object.\n */\n watchQuery<Query extends FunctionReference<\"query\">>(\n query: Query,\n ...argsAndOptions: ArgsAndOptions<Query, WatchQueryOptions>\n ): Watch<FunctionReturnType<Query>> {\n const [args, options] = argsAndOptions;\n const name = getFunctionName(query);\n\n return {\n onUpdate: (callback) => {\n const { queryToken, unsubscribe } = this.sync.subscribe(\n name as string,\n args,\n options,\n );\n\n const currentListeners = this.listeners.get(queryToken);\n if (currentListeners !== undefined) {\n currentListeners.add(callback);\n } else {\n this.listeners.set(queryToken, new Set([callback]));\n }\n\n return () => {\n if (this.closed) {\n return;\n }\n\n const currentListeners = this.listeners.get(queryToken)!;\n currentListeners.delete(callback);\n if (currentListeners.size === 0) {\n this.listeners.delete(queryToken);\n }\n unsubscribe();\n };\n },\n\n localQueryResult: () => {\n // Use the cached client because we can't have a query result if we don't\n // even have a client yet!\n if (this.cachedSync) {\n return this.cachedSync.localQueryResult(name, args);\n }\n return undefined;\n },\n\n localQueryLogs: () => {\n if (this.cachedSync) {\n return this.cachedSync.localQueryLogs(name, args);\n }\n return undefined;\n },\n\n journal: () => {\n if (this.cachedSync) {\n return this.cachedSync.queryJournal(name, args);\n }\n return undefined;\n },\n };\n }\n\n // Let's try out a queryOptions-style API.\n // This method is similar to the React Query API `queryClient.prefetchQuery()`.\n // In the future an ensureQueryData(): Promise<Data> method could exist.\n /**\n * Indicates likely future interest in a query subscription.\n *\n * The implementation currently immediately subscribes to a query. In the future this method\n * may prioritize some queries over others, fetch the query result without subscribing, or\n * do nothing in slow network connections or high load scenarios.\n *\n * To use this in a React component, call useQuery() and ignore the return value.\n *\n * @param queryOptions - A query (function reference from an api object) and its args, plus\n * an optional extendSubscriptionFor for how long to subscribe to the query.\n */\n prewarmQuery<Query extends FunctionReference<\"query\">>(\n queryOptions: QueryOptions<Query> & { extendSubscriptionFor?: number },\n ) {\n const extendSubscriptionFor =\n queryOptions.extendSubscriptionFor ?? DEFAULT_EXTEND_SUBSCRIPTION_FOR;\n const watch = this.watchQuery(queryOptions.query, queryOptions.args || {});\n const unsubscribe = watch.onUpdate(() => {});\n setTimeout(unsubscribe, extendSubscriptionFor);\n }\n\n /**\n * Construct a new {@link PaginatedWatch} on a Convex paginated query function.\n *\n * **Most application code should not call this method directly. Instead use\n * the {@link usePaginatedQuery} hook.**\n *\n * The act of creating a watch does nothing, a Watch is stateless.\n *\n * @param query - A {@link server.FunctionReference} for the public query to run.\n * @param args - An arguments object for the query. If this is omitted,\n * the arguments will be `{}`.\n * @param options - A {@link WatchPaginatedQueryOptions} options object for this query.\n *\n * @returns The {@link PaginatedWatch} object.\n *\n * @internal\n */\n watchPaginatedQuery<Query extends FunctionReference<\"query\">>(\n query: Query,\n args: Query[\"_args\"],\n options: WatchPaginatedQueryOptions,\n ): PaginatedWatch<FunctionReturnType<Query>> {\n const name = getFunctionName(query);\n\n return {\n onUpdate: (callback) => {\n const { paginatedQueryToken, unsubscribe } =\n this.paginatedQueryClient.subscribe(name, args || {}, options);\n\n const currentListeners = this.listeners.get(paginatedQueryToken);\n if (currentListeners !== undefined) {\n currentListeners.add(callback);\n } else {\n this.listeners.set(paginatedQueryToken, new Set([callback]));\n }\n\n return () => {\n if (this.closed) {\n return;\n }\n\n const currentListeners = this.listeners.get(paginatedQueryToken)!;\n currentListeners.delete(callback);\n if (currentListeners.size === 0) {\n this.listeners.delete(paginatedQueryToken);\n }\n unsubscribe();\n };\n },\n\n localQueryResult: () => {\n // Use our new paginated query client\n return this.paginatedQueryClient.localQueryResult(name, args, options);\n },\n };\n }\n\n /**\n * Execute a mutation function.\n *\n * @param mutation - A {@link server.FunctionReference} for the public mutation\n * to run.\n * @param args - An arguments object for the mutation. If this is omitted,\n * the arguments will be `{}`.\n * @param options - A {@link MutationOptions} options object for the mutation.\n * @returns A promise of the mutation's result.\n */\n mutation<Mutation extends FunctionReference<\"mutation\">>(\n mutation: Mutation,\n ...argsAndOptions: ArgsAndOptions<\n Mutation,\n MutationOptions<FunctionArgs<Mutation>>\n >\n ): Promise<FunctionReturnType<Mutation>> {\n const [args, options] = argsAndOptions;\n const name = getFunctionName(mutation);\n return this.sync.mutation(name, args, options);\n }\n\n /**\n * Execute an action function.\n *\n * @param action - A {@link server.FunctionReference} for the public action\n * to run.\n * @param args - An arguments object for the action. If this is omitted,\n * the arguments will be `{}`.\n * @returns A promise of the action's result.\n */\n action<Action extends FunctionReference<\"action\">>(\n action: Action,\n ...args: OptionalRestArgs<Action>\n ): Promise<FunctionReturnType<Action>> {\n const name = getFunctionName(action);\n return this.sync.action(name, ...args);\n }\n\n /**\n * Fetch a query result once.\n *\n * **Most application code should subscribe to queries instead, using\n * the {@link useQuery} hook.**\n *\n * @param query - A {@link server.FunctionReference} for the public query\n * to run.\n * @param args - An arguments object for the query. If this is omitted,\n * the arguments will be `{}`.\n * @returns A promise of the query's result.\n */\n query<Query extends FunctionReference<\"query\">>(\n query: Query,\n ...args: OptionalRestArgs<Query>\n ): Promise<FunctionReturnType<Query>> {\n const watch = this.watchQuery(query, ...args);\n const existingResult = watch.localQueryResult();\n if (existingResult !== undefined) {\n return Promise.resolve(existingResult);\n }\n return new Promise((resolve, reject) => {\n const unsubscribe = watch.onUpdate(() => {\n unsubscribe();\n try {\n resolve(watch.localQueryResult());\n } catch (e) {\n reject(e);\n }\n });\n });\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 return this.sync.connectionState();\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 * ConnectionState may also *lose* properties in future versions as we figure\n * out what information is most useful. As such this API is considered unstable.\n *\n * @returns An unsubscribe function to stop listening.\n */\n subscribeToConnectionState(\n cb: (connectionState: ConnectionState) => void,\n ): () => void {\n return this.sync.subscribeToConnectionState(cb);\n }\n\n /**\n * Get the logger for this client.\n *\n * @returns The {@link Logger} for this client.\n */\n get logger(): Logger {\n return this._logger;\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 a {@link ConvexReactClient} 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.closed = true;\n // Prevent outstanding React batched updates from invoking listeners.\n this.listeners = new Map();\n if (this.cachedPaginatedQueryClient) {\n this.cachedPaginatedQueryClient = undefined;\n }\n if (this.cachedSync) {\n const sync = this.cachedSync;\n this.cachedSync = undefined;\n await sync.close();\n }\n }\n\n /**\n * Handle transitions from both base client and paginated client.\n * This ensures all transitions are processed synchronously and in order.\n */\n private handleTransition(transition: ExtendedTransition) {\n const simple = transition.queries.map((q) => q.token);\n const paginated = transition.paginatedQueries.map((q) => q.token);\n this.transition([...simple, ...paginated]);\n }\n\n private transition(updatedQueries: (QueryToken | PaginatedQueryToken)[]) {\n for (const queryToken of updatedQueries) {\n const callbacks = this.listeners.get(queryToken);\n if (callbacks) {\n for (const callback of callbacks) {\n callback();\n }\n }\n }\n }\n}\n\nconst ConvexContext = React.createContext<ConvexReactClient>(\n undefined as unknown as ConvexReactClient, // in the future this will be a mocked client for testing\n);\n\n/**\n * Get the {@link ConvexReactClient} within a React component.\n *\n * This relies on the {@link ConvexProvider} being above in the React component tree.\n *\n * @returns The active {@link ConvexReactClient} object, or `undefined`.\n *\n * @public\n */\nexport function useConvex(): ConvexReactClient {\n return useContext(ConvexContext);\n}\n\n/**\n * Provides an active Convex {@link ConvexReactClient} to descendants of this component.\n *\n * Wrap your app in this component to use Convex hooks `useQuery`,\n * `useMutation`, and `useConvex`.\n *\n * @param props - an object with a `client` property that refers to a {@link ConvexReactClient}.\n *\n * @public\n */\nexport const ConvexProvider: React.FC<{\n client: ConvexReactClient;\n children?: React.ReactNode;\n}> = ({ client, children }) => {\n return React.createElement(\n ConvexContext.Provider,\n { value: client },\n children,\n );\n};\n\nexport type OptionalRestArgsOrSkip<FuncRef extends FunctionReference<any>> =\n FuncRef[\"_args\"] extends EmptyObject\n ? [args?: EmptyObject | \"skip\"]\n : [args: FuncRef[\"_args\"] | \"skip\"];\n\n/**\n * Result returned by object-form {@link useQuery}.\n *\n * @internal\n */\nexport type UseQueryResult<QueryResult> =\n | {\n data: QueryResult;\n error: undefined;\n status: \"success\";\n }\n | {\n data: undefined;\n error: Error;\n status: \"error\";\n }\n | {\n data: undefined;\n error: undefined;\n status: \"pending\";\n };\n\ntype UseQueryOptions<Query extends FunctionReference<\"query\">> = {\n query: Query;\n args: FunctionArgs<Query> | \"skip\";\n throwOnError?: boolean;\n};\n\n/**\n * Load a reactive query within a React component.\n *\n * This React hook subscribes to a Convex query and causes a rerender whenever\n * the query result changes. The subscription is managed automatically --\n * it starts when the component mounts and stops when it unmounts.\n *\n * Throws an error if not used under {@link ConvexProvider}.\n *\n * @example\n * ```tsx\n * import { useQuery } from \"convex/react\";\n * import { api } from \"../convex/_generated/api\";\n *\n * function TaskList() {\n * // Reactively loads tasks, re-renders when data changes:\n * const tasks = useQuery(api.tasks.list, { completed: false });\n *\n * // Returns `undefined` while loading:\n * if (tasks === undefined) return <div>Loading...</div>;\n *\n * return tasks.map((task) => <div key={task._id}>{task.text}</div>);\n * }\n *\n * // Pass \"skip\" to conditionally disable the query:\n * function MaybeProfile({ userId }: { userId?: Id<\"users\"> }) {\n * const profile = useQuery(\n * api.users.get,\n * userId ? { userId } : \"skip\",\n * );\n * // ...\n * }\n * ```\n *\n * @param query - a {@link server.FunctionReference} for the public query to run\n * like `api.dir1.dir2.filename.func`.\n * @param args - The arguments to the query function or the string `\"skip\"` if the\n * query should not be loaded.\n * @returns the result of the query. Returns `undefined` while loading.\n *\n * @see https://docs.convex.dev/client/react#fetching-data\n * @public\n */\nexport function useQuery<Query extends FunctionReference<\"query\">>(\n query: Query,\n ...args: OptionalRestArgsOrSkip<Query>\n): Query[\"_returnType\"] | undefined;\n\n/**\n * Load a reactive query within a React component using an options object.\n *\n * This is an alternative form of {@link useQuery} that accepts a single\n * {@link UseQueryOptions} object instead of positional arguments.\n * Errors are returned in the result object unless `throwOnError` is set.\n *\n * @example\n * ```tsx\n * import { useQuery } from \"convex/react\";\n * import { api } from \"../convex/_generated/api\";\n *\n * function TaskList() {\n * const state = useQuery({ query: api.tasks.list, args: { completed: false } });\n *\n * if (state.status === \"pending\") return <div>Loading...</div>;\n * if (state.status === \"error\") return <div>Error: {state.error.message}</div>;\n * return state.data.map((task) => <div key={task._id}>{task.text}</div>);\n * }\n * ```\n *\n * @param options - Query options. Pass `args: \"skip\"` to disable the query.\n * @returns the current query state as a {@link UseQueryResult} object.\n *\n * @see https://docs.convex.dev/client/react#fetching-data\n * @internal\n */\nexport function useQuery<Query extends FunctionReference<\"query\">>(\n options: UseQueryOptions<Query>,\n): UseQueryResult<Query[\"_returnType\"]>;\n\nexport function useQuery<Query extends FunctionReference<\"query\">>(\n queryOrOptions: Query | UseQueryOptions<Query>,\n ...args: OptionalRestArgsOrSkip<Query>\n): Query[\"_returnType\"] | undefined | UseQueryResult<Query[\"_returnType\"]> {\n const isObjectOptions =\n typeof queryOrOptions === \"object\" &&\n queryOrOptions !== null &&\n \"query\" in queryOrOptions;\n const throwOnError = isObjectOptions\n ? (queryOrOptions.throwOnError ?? false)\n : true;\n\n let queryReference: Query | undefined;\n let argsObject: Record<string, Value> = {};\n\n if (isObjectOptions) {\n const query = queryOrOptions.query;\n queryReference =\n typeof query === \"string\"\n ? (makeFunctionReference<\"query\", any, any>(query) as Query)\n : query;\n if (queryOrOptions.args !== \"skip\") {\n argsObject = parseArgs(queryOrOptions.args as Record<string, Value>);\n }\n } else {\n const query = queryOrOptions;\n queryReference =\n typeof query === \"string\"\n ? (makeFunctionReference<\"query\", any, any>(query) as Query)\n : query;\n argsObject = args[0] === \"skip\" ? {} : parseArgs(args[0] as Query[\"_args\"]);\n }\n\n const queryName = queryReference\n ? getFunctionName(queryReference)\n : undefined;\n const skip =\n (isObjectOptions && queryOrOptions.args === \"skip\") ||\n (!isObjectOptions && args[0] === \"skip\");\n\n const queries = useMemo(\n () =>\n skip || !queryReference\n ? ({} as RequestForQueries)\n : { query: { query: queryReference, args: argsObject } },\n // Stringify args so args that are semantically the same don't trigger a\n // rerender. Saves developers from adding `useMemo` on every args usage.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [JSON.stringify(convexToJson(argsObject)), queryName, skip],\n );\n\n const results = useQueries(queries);\n const result = results[\"query\"];\n\n if (isObjectOptions) {\n if (result instanceof Error) {\n if (throwOnError) {\n throw result;\n }\n return {\n data: undefined,\n error: result,\n status: \"error\",\n };\n }\n\n if (result === undefined) {\n return {\n data: undefined,\n error: undefined,\n status: \"pending\",\n };\n }\n\n return {\n data: result,\n error: undefined,\n status: \"success\",\n };\n }\n\n if (result instanceof Error) {\n throw result;\n }\n return result;\n}\n\n/**\n * Construct a new {@link ReactMutation}.\n *\n * Returns a function that you can call to execute a Convex mutation. The\n * returned function is stable across renders (same reference identity), so\n * it can be safely used in dependency arrays and memoization.\n *\n * Mutations can optionally be configured with\n * [optimistic updates](https://docs.convex.dev/client/react/optimistic-updates)\n * for instant UI feedback.\n *\n * Throws an error if not used under {@link ConvexProvider}.\n *\n * @example\n * ```tsx\n * import { useMutation } from \"convex/react\";\n * import { api } from \"../convex/_generated/api\";\n *\n * function CreateTask() {\n * const createTask = useMutation(api.tasks.create);\n *\n * const handleClick = async () => {\n * await createTask({ text: \"New task\" });\n * };\n *\n * return <button onClick={handleClick}>Add Task</button>;\n * }\n * ```\n *\n * @param mutation - A {@link server.FunctionReference} for the public mutation\n * to run like `api.dir1.dir2.filename.func`.\n * @returns The {@link ReactMutation} object with that name.\n *\n * @see https://docs.convex.dev/client/react#editing-data\n * @public\n */\nexport function useMutation<Mutation extends FunctionReference<\"mutation\">>(\n mutation: Mutation,\n): ReactMutation<Mutation> {\n const mutationReference =\n typeof mutation === \"string\"\n ? makeFunctionReference<\"mutation\", any, any>(mutation)\n : mutation;\n\n const convex = useContext(ConvexContext);\n if (convex === undefined) {\n throw new Error(\n \"Could not find Convex client! `useMutation` must be used in the React component \" +\n \"tree under `ConvexProvider`. Did you forget it? \" +\n \"See https://docs.convex.dev/quick-start#set-up-convex-in-your-react-app\",\n );\n }\n return useMemo(\n () => createMutation(mutationReference, convex),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [convex, getFunctionName(mutationReference)],\n );\n}\n\n/**\n * Construct a new {@link ReactAction}.\n *\n * Returns a function that you can call to execute a Convex action. Actions\n * can call third-party APIs and perform side effects. The returned function\n * is stable across renders (same reference identity).\n *\n * **Error handling:** Actions can fail (e.g., if an external API is down).\n * Always wrap action calls in try/catch or handle the rejected promise.\n *\n * **Note:** In most cases, calling an action directly from a client is an\n * anti-pattern. Prefer having the client call a mutation that captures the\n * user's intent (by writing to the database) and then schedules the action\n * via `ctx.scheduler.runAfter`. This ensures the intent is durably recorded\n * even if the client disconnects.\n *\n * Throws an error if not used under {@link ConvexProvider}.\n *\n * @example\n * ```tsx\n * import { useAction } from \"convex/react\";\n * import { api } from \"../convex/_generated/api\";\n *\n * function GenerateSummary() {\n * const generate = useAction(api.ai.generateSummary);\n *\n * const handleClick = async () => {\n * try {\n * const summary = await generate({ text: \"Some long text...\" });\n * console.log(summary);\n * } catch (error) {\n * console.error(\"Action failed:\", error);\n * }\n * };\n *\n * return <button onClick={handleClick}>Generate</button>;\n * }\n * ```\n *\n * @param action - A {@link server.FunctionReference} for the public action\n * to run like `api.dir1.dir2.filename.func`.\n * @returns The {@link ReactAction} object with that name.\n *\n * @see https://docs.convex.dev/functions/actions#calling-actions-from-clients\n * @public\n */\nexport function useAction<Action extends FunctionReference<\"action\">>(\n action: Action,\n): ReactAction<Action> {\n const convex = useContext(ConvexContext);\n const actionReference =\n typeof action === \"string\"\n ? makeFunctionReference<\"action\", any, any>(action)\n : action;\n\n if (convex === undefined) {\n throw new Error(\n \"Could not find Convex client! `useAction` must be used in the React component \" +\n \"tree under `ConvexProvider`. Did you forget it? \" +\n \"See https://docs.convex.dev/quick-start#set-up-convex-in-your-react-app\",\n );\n }\n return useMemo(\n () => createAction(actionReference, convex),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [convex, getFunctionName(actionReference)],\n );\n}\n\n/**\n * React hook to get the current {@link ConnectionState} and subscribe to changes.\n *\n * This hook returns the current connection state and automatically rerenders\n * when any part of the connection state changes (e.g., when going online/offline,\n * when requests start/complete, etc.).\n *\n * The shape of ConnectionState may change in the future which may cause this\n * hook to rerender more frequently.\n *\n * Throws an error if not used under {@link ConvexProvider}.\n *\n * @returns The current {@link ConnectionState} with the Convex backend.\n *\n * @public\n */\nexport function useConvexConnectionState(): ConnectionState {\n const convex = useContext(ConvexContext);\n if (convex === undefined) {\n throw new Error(\n \"Could not find Convex client! `useConvexConnectionState` must be used in the React component \" +\n \"tree under `ConvexProvider`. Did you forget it? \" +\n \"See https://docs.convex.dev/quick-start#set-up-convex-in-your-react-app\",\n );\n }\n\n const getCurrentValue = useCallback(() => {\n return convex.connectionState();\n }, [convex]);\n\n const subscribe = useCallback(\n (callback: () => void) => {\n return convex.subscribeToConnectionState(() => {\n callback();\n });\n },\n [convex],\n );\n\n return useSubscription({ getCurrentValue, subscribe });\n}\n\n// When a function is called with a single argument that looks like a\n// React SyntheticEvent it was likely called as an event handler.\nfunction assertNotAccidentalArgument(value: any) {\n // these are properties of a React.SyntheticEvent\n // https://reactjs.org/docs/events.html\n if (\n typeof value === \"object\" &&\n value !== null &&\n \"bubbles\" in value &&\n \"persist\" in value &&\n \"isDefaultPrevented\" in value\n ) {\n throw new Error(\n `Convex function called with SyntheticEvent object. Did you use a Convex function as an event handler directly? Event handlers like onClick receive an event object as their first argument. These SyntheticEvent objects are not valid Convex values. Try wrapping the function like \\`const handler = () => myMutation();\\` and using \\`handler\\` in the event handler.`,\n );\n }\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { BaseConvexClient } from \"../browser/index.js\";\nimport type {\n OptimisticUpdate,\n PaginatedQueryToken,\n QueryToken,\n PaginationStatus,\n} from \"../browser/index.js\";\nimport React, { useCallback, useContext, useMemo } from \"react\";\nimport { convexToJson, Value } from \"../values/index.js\";\nimport { QueryJournal } from \"../browser/sync/protocol.js\";\nimport {\n AuthTokenFetcher,\n BaseConvexClientOptions,\n ConnectionState,\n} from \"../browser/sync/client.js\";\nimport type { UserIdentityAttributes } from \"../browser/sync/protocol.js\";\nimport { RequestForQueries, useQueries } from \"./use_queries.js\";\nimport { useSubscription } from \"./use_subscription.js\";\nimport { parseArgs } from \"../common/index.js\";\nimport {\n ArgsAndOptions,\n FunctionArgs,\n FunctionReference,\n FunctionReturnType,\n OptionalRestArgs,\n getFunctionName,\n makeFunctionReference,\n} from \"../server/api.js\";\nimport { EmptyObject } from \"../server/registration.js\";\nimport {\n instantiateDefaultLogger,\n instantiateNoopLogger,\n Logger,\n} from \"../browser/logging.js\";\nimport type { QueryOptions } from \"../browser/query_options.js\";\nimport { LoadMoreOfPaginatedQuery } from \"../browser/sync/pagination.js\";\nimport {\n PaginatedQueryClient,\n ExtendedTransition,\n} from \"../browser/sync/paginated_query_client.js\";\n\n// When no arguments are passed, extend subscriptions (for APIs that do this by default)\n// for this amount after the subscription would otherwise be dropped.\nconst DEFAULT_EXTEND_SUBSCRIPTION_FOR = 5_000;\n\nif (typeof React === \"undefined\") {\n throw new Error(\"Required dependency 'react' not found\");\n}\n\n// TODO Typedoc doesn't generate documentation for the comment below perhaps\n// because it's a callable interface.\n/**\n * An interface to execute a Convex mutation function on the server.\n *\n * @public\n */\nexport interface ReactMutation<Mutation extends FunctionReference<\"mutation\">> {\n /**\n * Execute the mutation on the server, returning a `Promise` of its return value.\n *\n * @param args - Arguments for the mutation to pass up to the server.\n * @returns The return value of the server-side function call.\n */\n (...args: OptionalRestArgs<Mutation>): Promise<FunctionReturnType<Mutation>>;\n\n /**\n * Define an optimistic update to apply as part of this mutation.\n *\n * This is a temporary update to the local query results to facilitate a\n * fast, interactive UI. It enables query results to update before a mutation\n * executed on the server.\n *\n * When the mutation is invoked, the optimistic update will be applied.\n *\n * Optimistic updates can also be used to temporarily remove queries from the\n * client and create loading experiences until a mutation completes and the\n * new query results are synced.\n *\n * The update will be automatically rolled back when the mutation is fully\n * completed and queries have been updated.\n *\n * @param optimisticUpdate - The optimistic update to apply.\n * @returns A new `ReactMutation` with the update configured.\n *\n * @public\n */\n withOptimisticUpdate<T extends OptimisticUpdate<FunctionArgs<Mutation>>>(\n optimisticUpdate: T &\n (ReturnType<T> extends Promise<any>\n ? \"Optimistic update handlers must be synchronous\"\n : {}),\n ): ReactMutation<Mutation>;\n}\n\n// Exported only for testing.\nexport function createMutation(\n mutationReference: FunctionReference<\"mutation\">,\n client: ConvexReactClient,\n update?: OptimisticUpdate<any>,\n): ReactMutation<any> {\n function mutation(args?: Record<string, Value>): Promise<unknown> {\n assertNotAccidentalArgument(args);\n\n return client.mutation(mutationReference, args, {\n optimisticUpdate: update,\n });\n }\n mutation.withOptimisticUpdate = function withOptimisticUpdate(\n optimisticUpdate: OptimisticUpdate<any>,\n ): ReactMutation<any> {\n if (update !== undefined) {\n throw new Error(\n `Already specified optimistic update for mutation ${getFunctionName(\n mutationReference,\n )}`,\n );\n }\n return createMutation(mutationReference, client, optimisticUpdate);\n };\n return mutation as ReactMutation<any>;\n}\n\n/**\n * An interface to execute a Convex action on the server.\n *\n * @public\n */\nexport interface ReactAction<Action extends FunctionReference<\"action\">> {\n /**\n * Execute the function on the server, returning a `Promise` of its return value.\n *\n * @param args - Arguments for the function to pass up to the server.\n * @returns The return value of the server-side function call.\n * @public\n */\n (...args: OptionalRestArgs<Action>): Promise<FunctionReturnType<Action>>;\n}\n\nfunction createAction(\n actionReference: FunctionReference<\"action\">,\n client: ConvexReactClient,\n): ReactAction<any> {\n return function (args?: Record<string, Value>): Promise<unknown> {\n return client.action(actionReference, args);\n } as ReactAction<any>;\n}\n\n// Watches should be stateless: in QueriesObserver we create a watch just to get\n// the current value.\n/**\n * A watch on the output of a Convex query function.\n *\n * @public\n */\nexport interface Watch<T> {\n /**\n * Initiate a watch on the output of a query.\n *\n * This will subscribe to this query and call\n * the callback whenever the query result changes.\n *\n * **Important: If the client is already subscribed to this query with the\n * same arguments this callback will not be invoked until the query result is\n * updated.** To get the current, local result call\n * {@link react.Watch.localQueryResult}.\n *\n * @param callback - Function that is called whenever the query result changes.\n * @returns - A function that disposes of the subscription.\n */\n onUpdate(callback: () => void): () => void;\n\n /**\n * Get the current result of a query.\n *\n * This will only return a result if we're already subscribed to the query\n * and have received a result from the server or the query value has been set\n * optimistically.\n *\n * @returns The result of the query or `undefined` if it isn't known.\n * @throws An error if the query encountered an error on the server.\n */\n localQueryResult(): T | undefined;\n\n /**\n * @internal\n */\n localQueryLogs(): string[] | undefined;\n\n /**\n * Get the current {@link browser.QueryJournal} for this query.\n *\n * If we have not yet received a result for this query, this will be `undefined`.\n */\n journal(): QueryJournal | undefined;\n}\n\n/**\n * A watch on the output of a paginated Convex query function.\n *\n * @public\n */\nexport interface PaginatedWatch<T> {\n /**\n * Initiate a watch on the output of a paginated query.\n *\n * This will subscribe to this query and call\n * the callback whenever the query result changes.\n *\n * @param callback - Function that is called whenever the query result changes.\n * @returns - A function that disposes of the subscription.\n */\n onUpdate(callback: () => void): () => void;\n\n /**\n * Get the current result of a paginated query.\n *\n * @returns The current results, status, and loadMore function, or `undefined` if not loaded.\n */\n localQueryResult():\n | {\n results: T[];\n status: PaginationStatus;\n loadMore: LoadMoreOfPaginatedQuery;\n }\n | undefined;\n}\n\n/**\n * Options for {@link ConvexReactClient.watchQuery}.\n *\n * @public\n */\nexport interface WatchQueryOptions {\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;\n}\n\n/**\n * Options for {@link ConvexReactClient.watchPaginatedQuery}.\n *\n * @internal\n */\nexport interface WatchPaginatedQueryOptions {\n /**\n * The initial number of items to load.\n */\n initialNumItems: number;\n\n // We may be able to remove this in the future, but to preserve the existing behavior of\n // usePaginatedQuery() it's still here.\n id: number;\n\n /**\n * @internal\n */\n componentPath?: string;\n}\n\n/**\n * Options for {@link ConvexReactClient.mutation}.\n *\n * @public\n */\nexport interface MutationOptions<Args extends Record<string, Value>> {\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<Args> | undefined;\n}\n\n/**\n * Options for {@link ConvexReactClient}.\n *\n * @public\n */\nexport interface ConvexReactClientOptions extends BaseConvexClientOptions {}\n\n/**\n * A Convex client for use within React.\n *\n * This loads reactive queries and executes mutations over a WebSocket.\n *\n * @public\n */\nexport class ConvexReactClient {\n private address: string;\n private cachedSync?: BaseConvexClient | undefined;\n private cachedPaginatedQueryClient?: PaginatedQueryClient | undefined;\n private listeners: Map<QueryToken | PaginatedQueryToken, Set<() => void>>;\n private options: ConvexReactClientOptions;\n // \"closed\" means this client is done, not just that the underlying WS connection is closed.\n private closed = false;\n private _logger: Logger;\n\n private adminAuth?: string;\n private fakeUserIdentity?: UserIdentityAttributes | 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 options - See {@link ConvexReactClientOptions} for a full description.\n */\n constructor(address: string, options?: ConvexReactClientOptions) {\n // Validate address immediately since validation by the lazily-instantiated\n // internal client does not occur synchronously.\n if (address === undefined) {\n throw new Error(\n \"No address provided to ConvexReactClient.\\n\" +\n \"If trying to deploy to production, make sure to follow all the instructions found at https://docs.convex.dev/production/hosting/\\n\" +\n \"If running locally, make sure to run `convex dev` and ensure the .env.local file is populated.\",\n );\n }\n if (typeof address !== \"string\") {\n throw new Error(\n `ConvexReactClient requires a URL like 'https://happy-otter-123.convex.cloud', received something of type ${typeof address} instead.`,\n );\n }\n if (!address.includes(\"://\")) {\n throw new Error(\"Provided address was not an absolute URL.\");\n }\n this.address = address;\n this.listeners = new Map();\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 this.options = { ...options, logger: this._logger };\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 * Lazily instantiate the `BaseConvexClient` so we don't create the WebSocket\n * when server-side rendering.\n *\n * @internal\n */\n get sync() {\n if (this.closed) {\n throw new Error(\"ConvexReactClient has already been closed.\");\n }\n if (this.cachedSync) {\n return this.cachedSync;\n }\n // BaseConvexClient and paginated query client are always created together.\n this.cachedSync = new BaseConvexClient(\n this.address,\n () => {}, // Use the PaginatedQueryClient's transition instead.\n this.options,\n );\n if (this.adminAuth) {\n this.cachedSync.setAdminAuth(this.adminAuth, this.fakeUserIdentity);\n }\n this.cachedPaginatedQueryClient = new PaginatedQueryClient(\n this.cachedSync,\n (transition) => this.handleTransition(transition),\n );\n return this.cachedSync;\n }\n\n /**\n * Lazily instantiate the `PaginatedQueryClient` so we don't create it\n * when server-side rendering.\n *\n * @internal\n */\n get paginatedQueryClient() {\n // access sync to instantiate the clients\n this.sync;\n if (this.cachedPaginatedQueryClient) {\n return this.cachedPaginatedQueryClient;\n }\n throw new Error(\"Should already be instantiated\");\n }\n\n /**\n * Set the authentication token to be used for subsequent queries and mutations.\n * `fetchToken` will be called automatically again if a token expires.\n * `fetchToken` should return `null` if the token cannot be retrieved, for example\n * when the user's rights were permanently revoked.\n * @param fetchToken - an async function returning the JWT-encoded OpenID Connect Identity Token\n * @param onChange - a callback that will be called when the authentication status changes\n */\n setAuth(\n fetchToken: AuthTokenFetcher,\n onChange?: (isAuthenticated: boolean) => void,\n ) {\n if (typeof fetchToken === \"string\") {\n throw new Error(\n \"Passing a string to ConvexReactClient.setAuth is no longer supported, \" +\n \"please upgrade to passing in an async function to handle reauthentication.\",\n );\n }\n this.sync.setAuth(\n fetchToken,\n onChange ??\n (() => {\n // Do nothing\n }),\n );\n }\n\n /**\n * Clear the current authentication token if set.\n */\n clearAuth() {\n this.sync.clearAuth();\n }\n\n /**\n * @internal\n */\n setAdminAuth(token: string, identity?: UserIdentityAttributes) {\n this.adminAuth = token;\n this.fakeUserIdentity = identity;\n if (this.closed) {\n throw new Error(\"ConvexReactClient has already been closed.\");\n }\n if (this.cachedSync) {\n this.sync.setAdminAuth(token, identity);\n }\n }\n\n /**\n * Construct a new {@link Watch} on a Convex query function.\n *\n * **Most application code should not call this method directly. Instead use\n * the {@link useQuery} hook.**\n *\n * The act of creating a watch does nothing, a Watch is stateless.\n *\n * @param query - A {@link server.FunctionReference} for the public query to run.\n * @param args - An arguments object for the query. If this is omitted,\n * the arguments will be `{}`.\n * @param options - A {@link WatchQueryOptions} options object for this query.\n *\n * @returns The {@link Watch} object.\n */\n watchQuery<Query extends FunctionReference<\"query\">>(\n query: Query,\n ...argsAndOptions: ArgsAndOptions<Query, WatchQueryOptions>\n ): Watch<FunctionReturnType<Query>> {\n const [args, options] = argsAndOptions;\n const name = getFunctionName(query);\n\n return {\n onUpdate: (callback) => {\n const { queryToken, unsubscribe } = this.sync.subscribe(\n name as string,\n args,\n options,\n );\n\n const currentListeners = this.listeners.get(queryToken);\n if (currentListeners !== undefined) {\n currentListeners.add(callback);\n } else {\n this.listeners.set(queryToken, new Set([callback]));\n }\n\n return () => {\n if (this.closed) {\n return;\n }\n\n const currentListeners = this.listeners.get(queryToken)!;\n currentListeners.delete(callback);\n if (currentListeners.size === 0) {\n this.listeners.delete(queryToken);\n }\n unsubscribe();\n };\n },\n\n localQueryResult: () => {\n // Use the cached client because we can't have a query result if we don't\n // even have a client yet!\n if (this.cachedSync) {\n return this.cachedSync.localQueryResult(name, args);\n }\n return undefined;\n },\n\n localQueryLogs: () => {\n if (this.cachedSync) {\n return this.cachedSync.localQueryLogs(name, args);\n }\n return undefined;\n },\n\n journal: () => {\n if (this.cachedSync) {\n return this.cachedSync.queryJournal(name, args);\n }\n return undefined;\n },\n };\n }\n\n // Let's try out a queryOptions-style API.\n // This method is similar to the React Query API `queryClient.prefetchQuery()`.\n // In the future an ensureQueryData(): Promise<Data> method could exist.\n /**\n * Indicates likely future interest in a query subscription.\n *\n * The implementation currently immediately subscribes to a query. In the future this method\n * may prioritize some queries over others, fetch the query result without subscribing, or\n * do nothing in slow network connections or high load scenarios.\n *\n * To use this in a React component, call useQuery() and ignore the return value.\n *\n * @param queryOptions - A query (function reference from an api object) and its args, plus\n * an optional extendSubscriptionFor for how long to subscribe to the query.\n */\n prewarmQuery<Query extends FunctionReference<\"query\">>(\n queryOptions: QueryOptions<Query> & { extendSubscriptionFor?: number },\n ) {\n const extendSubscriptionFor =\n queryOptions.extendSubscriptionFor ?? DEFAULT_EXTEND_SUBSCRIPTION_FOR;\n const watch = this.watchQuery(queryOptions.query, queryOptions.args || {});\n const unsubscribe = watch.onUpdate(() => {});\n setTimeout(unsubscribe, extendSubscriptionFor);\n }\n\n /**\n * Construct a new {@link PaginatedWatch} on a Convex paginated query function.\n *\n * **Most application code should not call this method directly. Instead use\n * the {@link usePaginatedQuery} hook.**\n *\n * The act of creating a watch does nothing, a Watch is stateless.\n *\n * @param query - A {@link server.FunctionReference} for the public query to run.\n * @param args - An arguments object for the query. If this is omitted,\n * the arguments will be `{}`.\n * @param options - A {@link WatchPaginatedQueryOptions} options object for this query.\n *\n * @returns The {@link PaginatedWatch} object.\n *\n * @internal\n */\n watchPaginatedQuery<Query extends FunctionReference<\"query\">>(\n query: Query,\n args: Query[\"_args\"],\n options: WatchPaginatedQueryOptions,\n ): PaginatedWatch<FunctionReturnType<Query>> {\n const name = getFunctionName(query);\n\n return {\n onUpdate: (callback) => {\n const { paginatedQueryToken, unsubscribe } =\n this.paginatedQueryClient.subscribe(name, args || {}, options);\n\n const currentListeners = this.listeners.get(paginatedQueryToken);\n if (currentListeners !== undefined) {\n currentListeners.add(callback);\n } else {\n this.listeners.set(paginatedQueryToken, new Set([callback]));\n }\n\n return () => {\n if (this.closed) {\n return;\n }\n\n const currentListeners = this.listeners.get(paginatedQueryToken)!;\n currentListeners.delete(callback);\n if (currentListeners.size === 0) {\n this.listeners.delete(paginatedQueryToken);\n }\n unsubscribe();\n };\n },\n\n localQueryResult: () => {\n // Use our new paginated query client\n return this.paginatedQueryClient.localQueryResult(name, args, options);\n },\n };\n }\n\n /**\n * Execute a mutation function.\n *\n * @param mutation - A {@link server.FunctionReference} for the public mutation\n * to run.\n * @param args - An arguments object for the mutation. If this is omitted,\n * the arguments will be `{}`.\n * @param options - A {@link MutationOptions} options object for the mutation.\n * @returns A promise of the mutation's result.\n */\n mutation<Mutation extends FunctionReference<\"mutation\">>(\n mutation: Mutation,\n ...argsAndOptions: ArgsAndOptions<\n Mutation,\n MutationOptions<FunctionArgs<Mutation>>\n >\n ): Promise<FunctionReturnType<Mutation>> {\n const [args, options] = argsAndOptions;\n const name = getFunctionName(mutation);\n return this.sync.mutation(name, args, options);\n }\n\n /**\n * Execute an action function.\n *\n * @param action - A {@link server.FunctionReference} for the public action\n * to run.\n * @param args - An arguments object for the action. If this is omitted,\n * the arguments will be `{}`.\n * @returns A promise of the action's result.\n */\n action<Action extends FunctionReference<\"action\">>(\n action: Action,\n ...args: OptionalRestArgs<Action>\n ): Promise<FunctionReturnType<Action>> {\n const name = getFunctionName(action);\n return this.sync.action(name, ...args);\n }\n\n /**\n * Fetch a query result once.\n *\n * **Most application code should subscribe to queries instead, using\n * the {@link useQuery} hook.**\n *\n * @param query - A {@link server.FunctionReference} for the public query\n * to run.\n * @param args - An arguments object for the query. If this is omitted,\n * the arguments will be `{}`.\n * @returns A promise of the query's result.\n */\n query<Query extends FunctionReference<\"query\">>(\n query: Query,\n ...args: OptionalRestArgs<Query>\n ): Promise<FunctionReturnType<Query>> {\n const watch = this.watchQuery(query, ...args);\n const existingResult = watch.localQueryResult();\n if (existingResult !== undefined) {\n return Promise.resolve(existingResult);\n }\n return new Promise((resolve, reject) => {\n const unsubscribe = watch.onUpdate(() => {\n unsubscribe();\n try {\n resolve(watch.localQueryResult());\n } catch (e) {\n reject(e);\n }\n });\n });\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 return this.sync.connectionState();\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 * ConnectionState may also *lose* properties in future versions as we figure\n * out what information is most useful. As such this API is considered unstable.\n *\n * @returns An unsubscribe function to stop listening.\n */\n subscribeToConnectionState(\n cb: (connectionState: ConnectionState) => void,\n ): () => void {\n return this.sync.subscribeToConnectionState(cb);\n }\n\n /**\n * Get the logger for this client.\n *\n * @returns The {@link Logger} for this client.\n */\n get logger(): Logger {\n return this._logger;\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 a {@link ConvexReactClient} 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.closed = true;\n // Prevent outstanding React batched updates from invoking listeners.\n this.listeners = new Map();\n if (this.cachedPaginatedQueryClient) {\n this.cachedPaginatedQueryClient = undefined;\n }\n if (this.cachedSync) {\n const sync = this.cachedSync;\n this.cachedSync = undefined;\n await sync.close();\n }\n }\n\n /**\n * Handle transitions from both base client and paginated client.\n * This ensures all transitions are processed synchronously and in order.\n */\n private handleTransition(transition: ExtendedTransition) {\n const simple = transition.queries.map((q) => q.token);\n const paginated = transition.paginatedQueries.map((q) => q.token);\n this.transition([...simple, ...paginated]);\n }\n\n private transition(updatedQueries: (QueryToken | PaginatedQueryToken)[]) {\n for (const queryToken of updatedQueries) {\n const callbacks = this.listeners.get(queryToken);\n if (callbacks) {\n for (const callback of callbacks) {\n callback();\n }\n }\n }\n }\n}\n\nconst ConvexContext = React.createContext<ConvexReactClient>(\n undefined as unknown as ConvexReactClient, // in the future this will be a mocked client for testing\n);\n\n/**\n * Get the {@link ConvexReactClient} within a React component.\n *\n * This relies on the {@link ConvexProvider} being above in the React component tree.\n *\n * @returns The active {@link ConvexReactClient} object, or `undefined`.\n *\n * @public\n */\nexport function useConvex(): ConvexReactClient {\n return useContext(ConvexContext);\n}\n\n/**\n * Provides an active Convex {@link ConvexReactClient} to descendants of this component.\n *\n * Wrap your app in this component to use Convex hooks `useQuery`,\n * `useMutation`, and `useConvex`.\n *\n * @param props - an object with a `client` property that refers to a {@link ConvexReactClient}.\n *\n * @public\n */\nexport const ConvexProvider: React.FC<{\n client: ConvexReactClient;\n children?: React.ReactNode;\n}> = ({ client, children }) => {\n return React.createElement(\n ConvexContext.Provider,\n { value: client },\n children,\n );\n};\n\nexport type OptionalRestArgsOrSkip<FuncRef extends FunctionReference<any>> =\n FuncRef[\"_args\"] extends EmptyObject\n ? [args?: EmptyObject | \"skip\"]\n : [args: FuncRef[\"_args\"] | \"skip\"];\n\n/**\n * Result returned by object-form {@link useQuery_experimental}.\n *\n * @public\n */\nexport type UseQueryResult<QueryResult, ThrowOnError extends boolean = false> =\n | { status: \"pending\" }\n | { status: \"success\"; data: QueryResult }\n | (ThrowOnError extends true ? never : { status: \"error\"; error: Error });\n\ntype UseQueryOptions<\n Query extends FunctionReference<\"query\">,\n ThrowOnError extends boolean,\n> = {\n query: Query;\n args: FunctionArgs<Query> | \"skip\";\n throwOnError?: ThrowOnError;\n};\n\n/**\n * Load a reactive query within a React component.\n *\n * This React hook subscribes to a Convex query and causes a rerender whenever\n * the query result changes. The subscription is managed automatically --\n * it starts when the component mounts and stops when it unmounts.\n *\n * Throws an error if not used under {@link ConvexProvider}.\n *\n * @example\n * ```tsx\n * import { useQuery } from \"convex/react\";\n * import { api } from \"../convex/_generated/api\";\n *\n * function TaskList() {\n * // Reactively loads tasks, re-renders when data changes:\n * const tasks = useQuery(api.tasks.list, { completed: false });\n *\n * // Returns `undefined` while loading:\n * if (tasks === undefined) return <div>Loading...</div>;\n *\n * return tasks.map((task) => <div key={task._id}>{task.text}</div>);\n * }\n *\n * // Pass \"skip\" to conditionally disable the query:\n * function MaybeProfile({ userId }: { userId?: Id<\"users\"> }) {\n * const profile = useQuery(\n * api.users.get,\n * userId ? { userId } : \"skip\",\n * );\n * // ...\n * }\n * ```\n *\n * @param query - a {@link server.FunctionReference} for the public query to run\n * like `api.dir1.dir2.filename.func`.\n * @param args - The arguments to the query function or the string `\"skip\"` if the\n * query should not be loaded.\n * @returns the result of the query. Returns `undefined` while loading.\n *\n * @see https://docs.convex.dev/client/react#fetching-data\n * @public\n */\nexport function useQuery<Query extends FunctionReference<\"query\">>(\n query: Query,\n ...args: OptionalRestArgsOrSkip<Query>\n): Query[\"_returnType\"] | undefined {\n const skip = args[0] === \"skip\";\n const argsObject = args[0] === \"skip\" ? {} : parseArgs(args[0]);\n const queryReference =\n typeof query === \"string\"\n ? makeFunctionReference<\"query\", any, any>(query)\n : query;\n\n const queryName = getFunctionName(queryReference);\n\n const queries = useMemo(\n () =>\n skip\n ? ({} as RequestForQueries)\n : { query: { query: queryReference, args: argsObject } },\n // Stringify args so args that are semantically the same don't trigger a\n // rerender. Saves developers from adding `useMemo` on every args usage.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [JSON.stringify(convexToJson(argsObject)), queryName, skip],\n );\n\n const results = useQueries(queries);\n const result = results[\"query\"];\n\n if (result instanceof Error) {\n throw result;\n }\n return result;\n}\n\n/**\n * Load a reactive query within a React component using an options object.\n *\n * This is an experimental form of {@link useQuery} that accepts a single\n * {@link UseQueryOptions} object instead of positional arguments.\n *\n * Consumers are expected to check the returned object `status` field to\n * make proper use of the result. If an error occurs, it will be present\n * in the result object unless `throwOnError` is `true`, in which case\n * the error will be thrown instead.\n *\n * @example\n * ```tsx\n * import { useQuery_experimental as useQuery } from \"convex/react\";\n * import { api } from \"../convex/_generated/api\";\n *\n * function TaskList() {\n * const state = useQuery({ query: api.tasks.list, args: { completed: false } });\n *\n * if (state.status === \"pending\") return <div>Loading...</div>;\n * if (state.status === \"error\") return <div>Error: {state.error.message}</div>;\n * return state.data.map((task) => <div key={task._id}>{task.text}</div>);\n * }\n * ```\n *\n * @param options - Query options. Pass `args: \"skip\"` to disable the query.\n * @returns the current query state as a {@link UseQueryResult} object.\n *\n * @see https://docs.convex.dev/client/react#fetching-data\n * @public\n */\nexport function useQuery_experimental<\n Query extends FunctionReference<\"query\">,\n ThrowOnError extends boolean = false,\n>(\n options: UseQueryOptions<Query, ThrowOnError>,\n): UseQueryResult<Query[\"_returnType\"], ThrowOnError>;\n\nexport function useQuery_experimental<\n Query extends FunctionReference<\"query\">,\n ThrowOnError extends boolean = false,\n>(\n options: UseQueryOptions<Query, ThrowOnError>,\n): UseQueryResult<Query[\"_returnType\"], false> {\n const throwOnError = options.throwOnError ?? false;\n const queryReference =\n typeof options.query === \"string\"\n ? (makeFunctionReference<\"query\", any, any>(options.query) as Query)\n : options.query;\n const skip = options.args === \"skip\";\n const argsObject = !skip\n ? parseArgs(options.args as Record<string, Value>)\n : {};\n\n const queryName = getFunctionName(queryReference);\n const queries = useMemo(\n () =>\n skip\n ? ({} as RequestForQueries)\n : { query: { query: queryReference, args: argsObject } },\n // Stringify args so args that are semantically the same don't trigger a\n // rerender. Saves developers from adding `useMemo` on every args usage.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [JSON.stringify(convexToJson(argsObject)), queryName, skip],\n );\n\n const results = useQueries(queries);\n const result = results[\"query\"];\n\n if (result instanceof Error) {\n if (throwOnError) {\n throw result;\n }\n return {\n error: result,\n status: \"error\",\n };\n }\n\n if (result === undefined) {\n return {\n status: \"pending\",\n };\n }\n\n return {\n data: result,\n status: \"success\",\n };\n}\n\n/**\n * Construct a new {@link ReactMutation}.\n *\n * Returns a function that you can call to execute a Convex mutation. The\n * returned function is stable across renders (same reference identity), so\n * it can be safely used in dependency arrays and memoization.\n *\n * Mutations can optionally be configured with\n * [optimistic updates](https://docs.convex.dev/client/react/optimistic-updates)\n * for instant UI feedback.\n *\n * Throws an error if not used under {@link ConvexProvider}.\n *\n * @example\n * ```tsx\n * import { useMutation } from \"convex/react\";\n * import { api } from \"../convex/_generated/api\";\n *\n * function CreateTask() {\n * const createTask = useMutation(api.tasks.create);\n *\n * const handleClick = async () => {\n * await createTask({ text: \"New task\" });\n * };\n *\n * return <button onClick={handleClick}>Add Task</button>;\n * }\n * ```\n *\n * @param mutation - A {@link server.FunctionReference} for the public mutation\n * to run like `api.dir1.dir2.filename.func`.\n * @returns The {@link ReactMutation} object with that name.\n *\n * @see https://docs.convex.dev/client/react#editing-data\n * @public\n */\nexport function useMutation<Mutation extends FunctionReference<\"mutation\">>(\n mutation: Mutation,\n): ReactMutation<Mutation> {\n const mutationReference =\n typeof mutation === \"string\"\n ? makeFunctionReference<\"mutation\", any, any>(mutation)\n : mutation;\n\n const convex = useContext(ConvexContext);\n if (convex === undefined) {\n throw new Error(\n \"Could not find Convex client! `useMutation` must be used in the React component \" +\n \"tree under `ConvexProvider`. Did you forget it? \" +\n \"See https://docs.convex.dev/quick-start#set-up-convex-in-your-react-app\",\n );\n }\n return useMemo(\n () => createMutation(mutationReference, convex),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [convex, getFunctionName(mutationReference)],\n );\n}\n\n/**\n * Construct a new {@link ReactAction}.\n *\n * Returns a function that you can call to execute a Convex action. Actions\n * can call third-party APIs and perform side effects. The returned function\n * is stable across renders (same reference identity).\n *\n * **Error handling:** Actions can fail (e.g., if an external API is down).\n * Always wrap action calls in try/catch or handle the rejected promise.\n *\n * **Note:** In most cases, calling an action directly from a client is an\n * anti-pattern. Prefer having the client call a mutation that captures the\n * user's intent (by writing to the database) and then schedules the action\n * via `ctx.scheduler.runAfter`. This ensures the intent is durably recorded\n * even if the client disconnects.\n *\n * Throws an error if not used under {@link ConvexProvider}.\n *\n * @example\n * ```tsx\n * import { useAction } from \"convex/react\";\n * import { api } from \"../convex/_generated/api\";\n *\n * function GenerateSummary() {\n * const generate = useAction(api.ai.generateSummary);\n *\n * const handleClick = async () => {\n * try {\n * const summary = await generate({ text: \"Some long text...\" });\n * console.log(summary);\n * } catch (error) {\n * console.error(\"Action failed:\", error);\n * }\n * };\n *\n * return <button onClick={handleClick}>Generate</button>;\n * }\n * ```\n *\n * @param action - A {@link server.FunctionReference} for the public action\n * to run like `api.dir1.dir2.filename.func`.\n * @returns The {@link ReactAction} object with that name.\n *\n * @see https://docs.convex.dev/functions/actions#calling-actions-from-clients\n * @public\n */\nexport function useAction<Action extends FunctionReference<\"action\">>(\n action: Action,\n): ReactAction<Action> {\n const convex = useContext(ConvexContext);\n const actionReference =\n typeof action === \"string\"\n ? makeFunctionReference<\"action\", any, any>(action)\n : action;\n\n if (convex === undefined) {\n throw new Error(\n \"Could not find Convex client! `useAction` must be used in the React component \" +\n \"tree under `ConvexProvider`. Did you forget it? \" +\n \"See https://docs.convex.dev/quick-start#set-up-convex-in-your-react-app\",\n );\n }\n return useMemo(\n () => createAction(actionReference, convex),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [convex, getFunctionName(actionReference)],\n );\n}\n\n/**\n * React hook to get the current {@link ConnectionState} and subscribe to changes.\n *\n * This hook returns the current connection state and automatically rerenders\n * when any part of the connection state changes (e.g., when going online/offline,\n * when requests start/complete, etc.).\n *\n * The shape of ConnectionState may change in the future which may cause this\n * hook to rerender more frequently.\n *\n * Throws an error if not used under {@link ConvexProvider}.\n *\n * @returns The current {@link ConnectionState} with the Convex backend.\n *\n * @public\n */\nexport function useConvexConnectionState(): ConnectionState {\n const convex = useContext(ConvexContext);\n if (convex === undefined) {\n throw new Error(\n \"Could not find Convex client! `useConvexConnectionState` must be used in the React component \" +\n \"tree under `ConvexProvider`. Did you forget it? \" +\n \"See https://docs.convex.dev/quick-start#set-up-convex-in-your-react-app\",\n );\n }\n\n const getCurrentValue = useCallback(() => {\n return convex.connectionState();\n }, [convex]);\n\n const subscribe = useCallback(\n (callback: () => void) => {\n return convex.subscribeToConnectionState(() => {\n callback();\n });\n },\n [convex],\n );\n\n return useSubscription({ getCurrentValue, subscribe });\n}\n\n// When a function is called with a single argument that looks like a\n// React SyntheticEvent it was likely called as an event handler.\nfunction assertNotAccidentalArgument(value: any) {\n // these are properties of a React.SyntheticEvent\n // https://reactjs.org/docs/events.html\n if (\n typeof value === \"object\" &&\n value !== null &&\n \"bubbles\" in value &&\n \"persist\" in value &&\n \"isDefaultPrevented\" in value\n ) {\n throw new Error(\n `Convex function called with SyntheticEvent object. Did you use a Convex function as an event handler directly? Event handlers like onClick receive an event object as their first argument. These SyntheticEvent objects are not valid Convex values. Try wrapping the function like \\`const handler = () => myMutation();\\` and using \\`handler\\` in the event handler.`,\n );\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["React", "currentListeners"]
|
|
7
7
|
}
|
package/dist/cjs/react/index.js
CHANGED
|
@@ -30,6 +30,7 @@ __export(react_exports, {
|
|
|
30
30
|
usePaginatedQuery_experimental: () => import_use_paginated_query2.usePaginatedQuery_experimental,
|
|
31
31
|
useQueries: () => import_use_queries.useQueries,
|
|
32
32
|
useQuery: () => import_client.useQuery,
|
|
33
|
+
useQuery_experimental: () => import_client.useQuery_experimental,
|
|
33
34
|
useSubscription: () => import_use_subscription.useSubscription
|
|
34
35
|
});
|
|
35
36
|
module.exports = __toCommonJS(react_exports);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/react/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Tools to integrate Convex into React applications.\n *\n * This module contains:\n * 1. {@link ConvexReactClient}, a client for using Convex in React.\n * 2. {@link ConvexProvider}, a component that stores this client in React context.\n * 3. {@link Authenticated}, {@link Unauthenticated} and {@link AuthLoading} helper auth components.\n * 4. Hooks {@link useQuery}, {@link useMutation}, {@link useAction} and more for accessing this\n * client from your React components.\n *\n * ## Usage\n *\n * ### Creating the client\n *\n * ```typescript\n * import { ConvexReactClient } from \"convex/react\";\n *\n * // typically loaded from an environment variable\n * const address = \"https://small-mouse-123.convex.cloud\"\n * const convex = new ConvexReactClient(address);\n * ```\n *\n * ### Storing the client in React Context\n *\n * ```typescript\n * import { ConvexProvider } from \"convex/react\";\n *\n * <ConvexProvider client={convex}>\n * <App />\n * </ConvexProvider>\n * ```\n *\n * ### Using the auth helpers\n *\n * ```typescript\n * import { Authenticated, Unauthenticated, AuthLoading } from \"convex/react\";\n *\n * <Authenticated>\n * Logged in\n * </Authenticated>\n * <Unauthenticated>\n * Logged out\n * </Unauthenticated>\n * <AuthLoading>\n * Still loading\n * </AuthLoading>\n * ```\n *\n * ### Using React hooks\n *\n * ```typescript\n * import { useQuery, useMutation } from \"convex/react\";\n * import { api } from \"../convex/_generated/api\";\n *\n * function App() {\n * const counter = useQuery(api.getCounter.default);\n * const increment = useMutation(api.incrementCounter.default);\n * // Your component here!\n * }\n * ```\n * @module\n */\nexport * from \"./use_paginated_query.js\";\nexport {
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8DA,0BAAc,qCA9Dd;AA+DA,
|
|
4
|
+
"sourcesContent": ["/**\n * Tools to integrate Convex into React applications.\n *\n * This module contains:\n * 1. {@link ConvexReactClient}, a client for using Convex in React.\n * 2. {@link ConvexProvider}, a component that stores this client in React context.\n * 3. {@link Authenticated}, {@link Unauthenticated} and {@link AuthLoading} helper auth components.\n * 4. Hooks {@link useQuery}, {@link useMutation}, {@link useAction} and more for accessing this\n * client from your React components.\n *\n * ## Usage\n *\n * ### Creating the client\n *\n * ```typescript\n * import { ConvexReactClient } from \"convex/react\";\n *\n * // typically loaded from an environment variable\n * const address = \"https://small-mouse-123.convex.cloud\"\n * const convex = new ConvexReactClient(address);\n * ```\n *\n * ### Storing the client in React Context\n *\n * ```typescript\n * import { ConvexProvider } from \"convex/react\";\n *\n * <ConvexProvider client={convex}>\n * <App />\n * </ConvexProvider>\n * ```\n *\n * ### Using the auth helpers\n *\n * ```typescript\n * import { Authenticated, Unauthenticated, AuthLoading } from \"convex/react\";\n *\n * <Authenticated>\n * Logged in\n * </Authenticated>\n * <Unauthenticated>\n * Logged out\n * </Unauthenticated>\n * <AuthLoading>\n * Still loading\n * </AuthLoading>\n * ```\n *\n * ### Using React hooks\n *\n * ```typescript\n * import { useQuery, useMutation } from \"convex/react\";\n * import { api } from \"../convex/_generated/api\";\n *\n * function App() {\n * const counter = useQuery(api.getCounter.default);\n * const increment = useMutation(api.incrementCounter.default);\n * // Your component here!\n * }\n * ```\n * @module\n */\nexport * from \"./use_paginated_query.js\";\nexport {\n usePaginatedQuery_experimental,\n type UsePaginatedQueryOptions,\n type UsePaginatedQueryObjectReturnType,\n} from \"./use_paginated_query2.js\";\nexport { usePaginatedQuery } from \"./use_paginated_query.js\";\nexport { useQueries, type RequestForQueries } from \"./use_queries.js\";\nexport type { AuthTokenFetcher } from \"../browser/sync/client.js\";\nexport * from \"./auth_helpers.js\";\nexport * from \"./ConvexAuthState.js\";\nexport * from \"./hydration.js\";\n/* @internal */\nexport { useSubscription } from \"./use_subscription.js\";\nexport {\n type ReactMutation,\n type ReactAction,\n type Watch,\n type WatchQueryOptions,\n type MutationOptions,\n type ConvexReactClientOptions,\n type OptionalRestArgsOrSkip,\n ConvexReactClient,\n useConvex,\n ConvexProvider,\n useQuery,\n useQuery_experimental,\n useMutation,\n useAction,\n useConvexConnectionState,\n} from \"./client.js\";\n/** @internal */\nexport type { UseQueryResult } from \"./client.js\";\n/** @internal */\nexport { convexQueryOptions } from \"../browser/query_options.js\";\nexport type { QueryOptions } from \"../browser/query_options.js\";\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8DA,0BAAc,qCA9Dd;AA+DA,kCAIO;AACP,iCAAkC;AAClC,yBAAmD;AAEnD,0BAAc,8BAvEd;AAwEA,0BAAc,iCAxEd;AAyEA,0BAAc,2BAzEd;AA2EA,8BAAgC;AAChC,oBAgBO;AAIP,2BAAmC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -95,24 +95,14 @@ const completeSplitQuery = (key) => (prevState) => {
|
|
|
95
95
|
ongoingSplits
|
|
96
96
|
};
|
|
97
97
|
};
|
|
98
|
-
function usePaginatedQuery(
|
|
99
|
-
const isObjectOptions = typeof queryOrOptions === "object" && queryOrOptions !== null && "query" in queryOrOptions;
|
|
100
|
-
const query = isObjectOptions ? queryOrOptions.query : queryOrOptions;
|
|
101
|
-
const queryArgs = isObjectOptions ? queryOrOptions.args : args;
|
|
102
|
-
const throwOnError = isObjectOptions ? queryOrOptions.throwOnError ?? false : true;
|
|
103
|
-
const initialOptions = isObjectOptions ? { initialNumItems: queryOrOptions.initialNumItems } : options;
|
|
98
|
+
function usePaginatedQuery(query, args, options) {
|
|
104
99
|
const { user: positionalResult } = usePaginatedQueryInternal(
|
|
105
100
|
query,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
);
|
|
110
|
-
if (!isObjectOptions) {
|
|
111
|
-
return positionalResult;
|
|
112
|
-
}
|
|
113
|
-
return reshapeToObjectForm(
|
|
114
|
-
positionalResult
|
|
101
|
+
args,
|
|
102
|
+
options,
|
|
103
|
+
true
|
|
115
104
|
);
|
|
105
|
+
return positionalResult;
|
|
116
106
|
}
|
|
117
107
|
const includePage = Symbol("includePageKeys");
|
|
118
108
|
const page = Symbol("page");
|
|
@@ -301,37 +291,6 @@ function usePaginatedQueryInternal(query, args, options, throwOnError = true) {
|
|
|
301
291
|
internal: { state: currState }
|
|
302
292
|
};
|
|
303
293
|
}
|
|
304
|
-
function reshapeToObjectForm(internal) {
|
|
305
|
-
const { results, loadMore } = internal;
|
|
306
|
-
if (internal.status === "Error" && "error" in internal) {
|
|
307
|
-
return {
|
|
308
|
-
data: results,
|
|
309
|
-
status: "error",
|
|
310
|
-
canLoadMore: false,
|
|
311
|
-
isLoading: false,
|
|
312
|
-
error: internal.error,
|
|
313
|
-
loadMore
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
if (internal.status === "LoadingFirstPage" || internal.status === "LoadingMore") {
|
|
317
|
-
return {
|
|
318
|
-
data: internal.status === "LoadingFirstPage" ? void 0 : results,
|
|
319
|
-
status: "pending",
|
|
320
|
-
canLoadMore: false,
|
|
321
|
-
isLoading: true,
|
|
322
|
-
error: void 0,
|
|
323
|
-
loadMore
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
return {
|
|
327
|
-
data: results,
|
|
328
|
-
status: "success",
|
|
329
|
-
canLoadMore: internal.status === "CanLoadMore",
|
|
330
|
-
isLoading: false,
|
|
331
|
-
error: void 0,
|
|
332
|
-
loadMore
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
294
|
let paginationId = 0;
|
|
336
295
|
function nextPaginationId() {
|
|
337
296
|
paginationId++;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/react/use_paginated_query.ts"],
|
|
4
|
-
"sourcesContent": ["import { useMemo, useState } from \"react\";\n\nimport { OptimisticLocalStore } from \"../browser/index.js\";\nimport {\n FunctionReturnType,\n PaginationOptions,\n paginationOptsValidator,\n PaginationResult,\n} from \"../server/index.js\";\nimport { ConvexError, convexToJson, Infer, Value } from \"../values/index.js\";\nimport { useQueries } from \"./use_queries.js\";\nimport {\n FunctionArgs,\n FunctionReference,\n getFunctionName,\n} from \"../server/api.js\";\nimport { BetterOmit, Expand } from \"../type_utils.js\";\nimport { useConvex } from \"./client.js\";\nimport { compareValues } from \"../values/compare.js\";\n\n/**\n * A {@link server.FunctionReference} that is usable with {@link usePaginatedQuery}.\n *\n * This function reference must:\n * - Refer to a public query\n * - Have an argument named \"paginationOpts\" of type {@link server.PaginationOptions}\n * - Have a return type of {@link server.PaginationResult}.\n *\n * @public\n */\nexport type PaginatedQueryReference = FunctionReference<\n \"query\",\n \"public\",\n { paginationOpts: PaginationOptions },\n PaginationResult<any>\n>;\n\n/**\n * Options for object-form {@link usePaginatedQuery}.\n *\n * @internal\n */\nexport type UsePaginatedQueryOptions<Query extends PaginatedQueryReference> = {\n query: Query;\n args: PaginatedQueryArgs<Query> | \"skip\";\n initialNumItems: number;\n /**\n * When `true` (default for positional form), errors are thrown and caught\n * by an error boundary. When `false` (default for object form), errors are\n * returned as `{ status: \"Error\", error: Error }` instead of being thrown.\n */\n throwOnError?: boolean;\n};\n\n// Incrementing integer for each page queried in the usePaginatedQuery hook.\ntype QueryPageKey = number;\n\ntype UsePaginatedQueryState = {\n query: FunctionReference<\"query\">;\n args: Record<string, Value>;\n id: number;\n nextPageKey: QueryPageKey;\n pageKeys: QueryPageKey[];\n queries: Record<\n QueryPageKey,\n {\n query: FunctionReference<\"query\">;\n // Use the validator type as a test that it matches the args\n // we generate.\n args: { paginationOpts: Infer<typeof paginationOptsValidator> };\n }\n >;\n ongoingSplits: Record<QueryPageKey, [QueryPageKey, QueryPageKey]>;\n skip: boolean;\n};\n\nconst splitQuery =\n (key: QueryPageKey, splitCursor: string, continueCursor: string) =>\n (prevState: UsePaginatedQueryState) => {\n const queries = { ...prevState.queries };\n const splitKey1 = prevState.nextPageKey;\n const splitKey2 = prevState.nextPageKey + 1;\n const nextPageKey = prevState.nextPageKey + 2;\n queries[splitKey1] = {\n query: prevState.query,\n args: {\n ...prevState.args,\n paginationOpts: {\n ...prevState.queries[key].args.paginationOpts,\n endCursor: splitCursor,\n },\n },\n };\n queries[splitKey2] = {\n query: prevState.query,\n args: {\n ...prevState.args,\n paginationOpts: {\n ...prevState.queries[key].args.paginationOpts,\n cursor: splitCursor,\n endCursor: continueCursor,\n },\n },\n };\n const ongoingSplits = { ...prevState.ongoingSplits };\n ongoingSplits[key] = [splitKey1, splitKey2];\n return {\n ...prevState,\n nextPageKey,\n queries,\n ongoingSplits,\n };\n };\n\nconst completeSplitQuery =\n (key: QueryPageKey) => (prevState: UsePaginatedQueryState) => {\n const completedSplit = prevState.ongoingSplits[key];\n if (completedSplit === undefined) {\n return prevState;\n }\n const queries = { ...prevState.queries };\n delete queries[key];\n const ongoingSplits = { ...prevState.ongoingSplits };\n delete ongoingSplits[key];\n let pageKeys = prevState.pageKeys.slice();\n const pageIndex = prevState.pageKeys.findIndex((v) => v === key);\n if (pageIndex >= 0) {\n pageKeys = [\n ...prevState.pageKeys.slice(0, pageIndex),\n ...completedSplit,\n ...prevState.pageKeys.slice(pageIndex + 1),\n ];\n }\n return {\n ...prevState,\n queries,\n pageKeys,\n ongoingSplits,\n };\n };\n\n/**\n * Load data reactively from a paginated query to a create a growing list.\n *\n * This can be used to power \"infinite scroll\" UIs.\n *\n * This hook must be used with public query references that match\n * {@link PaginatedQueryReference}.\n *\n * `usePaginatedQuery` concatenates all the pages of results into a single list\n * and manages the continuation cursors when requesting more items.\n *\n * Example usage:\n * ```typescript\n * const { results, status, isLoading, loadMore } = usePaginatedQuery(\n * api.messages.list,\n * { channel: \"#general\" },\n * { initialNumItems: 5 }\n * );\n * ```\n *\n * If the query reference or arguments change, the pagination state will be reset\n * to the first page. Similarly, if any of the pages result in an InvalidCursor\n * error or an error associated with too much data, the pagination state will also\n * reset to the first page.\n *\n * To learn more about pagination, see [Paginated Queries](https://docs.convex.dev/database/pagination).\n *\n * @param query - A FunctionReference to the public query function to run.\n * @param args - The arguments object for the query function, excluding\n * the `paginationOpts` property. That property is injected by this hook.\n * @param options - An object specifying the `initialNumItems` to be loaded in\n * the first page.\n * @returns A {@link UsePaginatedQueryResult} that includes the currently loaded\n * items, the status of the pagination, and a `loadMore` function.\n *\n * @public\n */\nexport function usePaginatedQuery<Query extends PaginatedQueryReference>(\n query: Query,\n args: PaginatedQueryArgs<Query> | \"skip\",\n options: { initialNumItems: number },\n): UsePaginatedQueryReturnType<Query>;\n\n/**\n * Load data reactively from a paginated query using an options object.\n *\n * @param options - Object-form options for the paginated query.\n * @returns An object with `data`, `status`, `canLoadMore`, `isLoading`,\n * `error`, and `loadMore`. `status` is `\"pending\"` while loading,\n * `\"success\"` when data is available, or `\"error\"` if the query threw.\n * `canLoadMore` is `true` only when idle and more pages exist.\n *\n * @internal\n */\nexport function usePaginatedQuery<Query extends PaginatedQueryReference>(\n options: UsePaginatedQueryOptions<Query>,\n): UsePaginatedQueryObjectReturnType<Query>;\n\nexport function usePaginatedQuery<Query extends PaginatedQueryReference>(\n queryOrOptions: Query | UsePaginatedQueryOptions<Query>,\n args?: PaginatedQueryArgs<Query> | \"skip\",\n options?: { initialNumItems: number },\n):\n | UsePaginatedQueryReturnType<Query>\n | UsePaginatedQueryObjectReturnType<Query> {\n const isObjectOptions =\n typeof queryOrOptions === \"object\" &&\n queryOrOptions !== null &&\n \"query\" in queryOrOptions;\n\n const query = isObjectOptions ? queryOrOptions.query : queryOrOptions;\n const queryArgs = isObjectOptions ? queryOrOptions.args : args;\n const throwOnError = isObjectOptions\n ? (queryOrOptions.throwOnError ?? false)\n : true;\n const initialOptions = isObjectOptions\n ? { initialNumItems: queryOrOptions.initialNumItems }\n : options;\n\n const { user: positionalResult } = usePaginatedQueryInternal(\n query,\n queryArgs as PaginatedQueryArgs<Query> | \"skip\",\n initialOptions as { initialNumItems: number },\n throwOnError,\n );\n\n if (!isObjectOptions) {\n return positionalResult as unknown as UsePaginatedQueryReturnType<Query>;\n }\n\n return reshapeToObjectForm(\n positionalResult,\n ) as unknown as UsePaginatedQueryObjectReturnType<Query>;\n}\n\n/** @internal */\nexport const includePage = Symbol(\"includePageKeys\");\n\n/** @internal */\nexport const page = Symbol(\"page\");\n\n/**\n * @internal\n */\nexport function usePaginatedQueryInternal<\n Query extends PaginatedQueryReference,\n>(\n query: Query,\n args: PaginatedQueryArgs<Query> | \"skip\",\n options: {\n initialNumItems: number;\n [includePage]?: boolean;\n },\n throwOnError: boolean = true,\n): {\n user: UsePaginatedQueryInternalResult<PaginatedQueryItem<Query>>;\n internal: { state: UsePaginatedQueryState };\n} {\n if (\n typeof options?.initialNumItems !== \"number\" ||\n options.initialNumItems < 0\n ) {\n throw new Error(\n `\\`options.initialNumItems\\` must be a positive number. Received \\`${options?.initialNumItems}\\`.`,\n );\n }\n const skip = args === \"skip\";\n const argsObject = skip ? {} : args;\n const queryName = getFunctionName(query);\n const createInitialState = useMemo(() => {\n return () => {\n const id = nextPaginationId();\n return {\n query,\n args: argsObject as Record<string, Value>,\n id,\n nextPageKey: 1,\n pageKeys: skip ? [] : [0],\n queries: skip\n ? ({} as UsePaginatedQueryState[\"queries\"])\n : {\n 0: {\n query,\n args: {\n ...argsObject,\n paginationOpts: {\n numItems: options.initialNumItems,\n cursor: null,\n id,\n },\n },\n },\n },\n ongoingSplits: {},\n skip,\n };\n };\n // ESLint doesn't like that we're stringifying the args. We do this because\n // we want to avoid rerendering if the args are a different\n // object that serializes to the same result.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n // eslint-disable-next-line react-hooks/exhaustive-deps\n JSON.stringify(convexToJson(argsObject as Value)),\n queryName,\n options.initialNumItems,\n skip,\n ]);\n\n const [state, setState] =\n useState<UsePaginatedQueryState>(createInitialState);\n\n // `currState` is the state that we'll render based on.\n let currState = state;\n if (\n getFunctionName(query) !== getFunctionName(state.query) ||\n JSON.stringify(convexToJson(argsObject as Value)) !==\n JSON.stringify(convexToJson(state.args)) ||\n skip !== state.skip\n ) {\n currState = createInitialState();\n setState(currState);\n }\n const convexClient = useConvex();\n const logger = convexClient.logger;\n\n const resultsObject = useQueries(currState.queries);\n\n const isIncludingPageKeys = options[includePage] ?? false;\n const [results, maybeLastResult, maybeError]: [\n Value[],\n undefined | PaginationResult<Value>,\n undefined | Error,\n ] = useMemo(() => {\n let currResult = undefined;\n\n const allItems = [];\n for (const pageKey of currState.pageKeys) {\n currResult = resultsObject[pageKey];\n if (currResult === undefined) {\n break;\n }\n\n if (currResult instanceof Error) {\n if (\n currResult.message.includes(\"InvalidCursor\") ||\n (currResult instanceof ConvexError &&\n typeof currResult.data === \"object\" &&\n currResult.data?.isConvexSystemError === true &&\n currResult.data?.paginationError === \"InvalidCursor\")\n ) {\n // - InvalidCursor: If the cursor is invalid, probably the paginated\n // database query was data-dependent and changed underneath us. The\n // cursor in the params or journal no longer matches the current\n // database query.\n\n // In all cases, we want to restart pagination to throw away all our\n // existing cursors.\n logger.warn(\n \"usePaginatedQuery hit error, resetting pagination state: \" +\n currResult.message,\n );\n setState(createInitialState);\n return [[], undefined, undefined];\n } else {\n if (throwOnError) {\n throw currResult;\n }\n return [allItems, undefined, currResult];\n }\n }\n const ongoingSplit = currState.ongoingSplits[pageKey];\n if (ongoingSplit !== undefined) {\n if (\n resultsObject[ongoingSplit[0]] !== undefined &&\n resultsObject[ongoingSplit[1]] !== undefined\n ) {\n // Both pages of the split have results now. Swap them in.\n setState(completeSplitQuery(pageKey));\n }\n } else if (\n currResult.splitCursor &&\n (currResult.pageStatus === \"SplitRecommended\" ||\n currResult.pageStatus === \"SplitRequired\" ||\n currResult.page.length > options.initialNumItems * 2)\n ) {\n // If a single page has more than double the expected number of items,\n // or if the server requests a split, split the page into two.\n setState(\n splitQuery(\n pageKey,\n currResult.splitCursor,\n currResult.continueCursor,\n ),\n );\n }\n if (currResult.pageStatus === \"SplitRequired\") {\n // If pageStatus is 'SplitRequired', it means the server was not able to\n // fetch the full page. So we stop results before the incomplete\n // page and return 'LoadingMore' while the page is splitting.\n return [allItems, undefined, undefined];\n }\n allItems.push(\n ...(isIncludingPageKeys\n ? currResult.page.map((i: any) => ({\n ...i,\n [page]: pageKey.toString(),\n }))\n : currResult.page),\n );\n }\n return [allItems, currResult, undefined];\n }, [\n resultsObject,\n currState.pageKeys,\n currState.ongoingSplits,\n options.initialNumItems,\n createInitialState,\n logger,\n isIncludingPageKeys,\n throwOnError,\n ]);\n\n const statusObject = useMemo(() => {\n if (maybeError !== undefined) {\n return {\n status: \"Error\",\n isLoading: false,\n error: maybeError,\n loadMore: () => {\n // Intentional noop.\n },\n } as const;\n }\n if (maybeLastResult === undefined) {\n if (currState.nextPageKey === 1) {\n return {\n status: \"LoadingFirstPage\",\n isLoading: true,\n loadMore: () => {\n // Intentional noop.\n },\n } as const;\n } else {\n return {\n status: \"LoadingMore\",\n isLoading: true,\n loadMore: (_numItems: number) => {\n // Intentional noop.\n },\n } as const;\n }\n }\n if (maybeLastResult.isDone) {\n return {\n status: \"Exhausted\",\n isLoading: false,\n loadMore: (_numItems: number) => {\n // Intentional noop.\n },\n } as const;\n }\n const continueCursor = maybeLastResult.continueCursor;\n let alreadyLoadingMore = false;\n return {\n status: \"CanLoadMore\",\n isLoading: false,\n loadMore: (numItems: number) => {\n if (!alreadyLoadingMore) {\n alreadyLoadingMore = true;\n setState((prevState) => {\n const pageKeys = [...prevState.pageKeys, prevState.nextPageKey];\n const queries = { ...prevState.queries };\n queries[prevState.nextPageKey] = {\n query: prevState.query,\n args: {\n ...prevState.args,\n paginationOpts: {\n numItems,\n cursor: continueCursor,\n id: prevState.id,\n },\n },\n };\n return {\n ...prevState,\n nextPageKey: prevState.nextPageKey + 1,\n pageKeys,\n queries,\n };\n });\n }\n },\n } as const;\n }, [maybeError, maybeLastResult, currState.nextPageKey]);\n\n return {\n user: {\n results,\n ...statusObject,\n },\n internal: { state: currState },\n };\n}\n\n/**\n * Reshape the internal TitleCase pagination result into the object-form\n * return type with lowercase `status`, `canLoadMore`, and `data`.\n */\nfunction reshapeToObjectForm<Item>(\n internal: UsePaginatedQueryInternalResult<Item>,\n) {\n const { results, loadMore } = internal;\n if (internal.status === \"Error\" && \"error\" in internal) {\n return {\n data: results,\n status: \"error\" as const,\n canLoadMore: false as const,\n isLoading: false as const,\n error: internal.error,\n loadMore,\n };\n }\n if (\n internal.status === \"LoadingFirstPage\" ||\n internal.status === \"LoadingMore\"\n ) {\n return {\n data: internal.status === \"LoadingFirstPage\" ? undefined : results,\n status: \"pending\" as const,\n canLoadMore: false as const,\n isLoading: true as const,\n error: undefined,\n loadMore,\n };\n }\n // CanLoadMore or Exhausted\n return {\n data: results,\n status: \"success\" as const,\n canLoadMore: internal.status === \"CanLoadMore\",\n isLoading: false as const,\n error: undefined,\n loadMore,\n };\n}\n\nlet paginationId = 0;\n/**\n * Generate a new, unique ID for a pagination session.\n *\n * Every usage of {@link usePaginatedQuery} puts a unique ID into the\n * query function arguments as a \"cache-buster\". This serves two purposes:\n *\n * 1. All calls to {@link usePaginatedQuery} have independent query\n * journals.\n *\n * Every time we start a new pagination session, we'll load the first page of\n * results and receive a fresh journal. Without the ID, we might instead reuse\n * a query subscription already present in our client. This isn't desirable\n * because the existing query function result may have grown or shrunk from the\n * requested `initialNumItems`.\n *\n * 2. We can restart the pagination session on some types of errors.\n *\n * Sometimes we want to restart pagination from the beginning if we hit an error.\n * Similar to (1), we'd like to ensure that this new session actually requests\n * its first page from the server and doesn't reuse a query result already\n * present in the client that may have hit the error.\n *\n * @returns The pagination ID.\n */\nfunction nextPaginationId(): number {\n paginationId++;\n return paginationId;\n}\n\n/**\n * Reset pagination id for tests only, so tests know what it is.\n */\nexport function resetPaginationId() {\n paginationId = 0;\n}\n\n/**\n * The result of calling the {@link usePaginatedQuery} hook.\n *\n * This includes:\n * - `results` - An array of the currently loaded results.\n * - `isLoading` - Whether the hook is currently loading results.\n * - `status` - The status of the pagination. The possible statuses are:\n * - \"LoadingFirstPage\": The hook is loading the first page of results.\n * - \"CanLoadMore\": This query may have more items to fetch. Call `loadMore` to\n * fetch another page.\n * - \"LoadingMore\": We're currently loading another page of results.\n * - \"Exhausted\": We've paginated to the end of the list.\n * - `loadMore(n)` A callback to fetch more results. This will only fetch more\n * results if the status is \"CanLoadMore\".\n *\n * @public\n */\nexport type UsePaginatedQueryResult<Item> = {\n results: Item[];\n loadMore: (numItems: number) => void;\n} & (\n | {\n status: \"LoadingFirstPage\";\n isLoading: true;\n }\n | {\n status: \"CanLoadMore\";\n isLoading: false;\n }\n | {\n status: \"LoadingMore\";\n isLoading: true;\n }\n | {\n status: \"Exhausted\";\n isLoading: false;\n }\n);\n\n/**\n * @internal\n */\nexport type UsePaginatedQueryInternalResult<Item> =\n | UsePaginatedQueryResult<Item>\n | {\n results: Item[];\n status: \"Error\";\n isLoading: false;\n error: Error;\n loadMore: (numItems: number) => void;\n };\n\n/**\n * The possible pagination statuses in {@link UsePaginatedQueryResult}.\n *\n * This is a union of string literal types.\n * @public\n */\nexport type PaginationStatus = UsePaginatedQueryResult<any>[\"status\"];\n\n/**\n * Given a {@link PaginatedQueryReference}, get the type of the arguments\n * object for the query, excluding the `paginationOpts` argument.\n *\n * @public\n */\nexport type PaginatedQueryArgs<Query extends PaginatedQueryReference> = Expand<\n BetterOmit<FunctionArgs<Query>, \"paginationOpts\">\n>;\n\n/**\n * Given a {@link PaginatedQueryReference}, get the type of the item being\n * paginated over.\n * @public\n */\nexport type PaginatedQueryItem<Query extends PaginatedQueryReference> =\n FunctionReturnType<Query>[\"page\"][number];\n\n/**\n * The return type of {@link usePaginatedQuery}.\n *\n * @public\n */\nexport type UsePaginatedQueryReturnType<Query extends PaginatedQueryReference> =\n UsePaginatedQueryResult<PaginatedQueryItem<Query>>;\n\n/**\n * Return type of the object-form {@link usePaginatedQuery} overload.\n *\n * Uses lowercase query status (`\"pending\" | \"success\" | \"error\"`) and a\n * `canLoadMore` boolean instead of the TitleCase pagination status strings\n * used by the positional form.\n *\n * @internal\n */\nexport type UsePaginatedQueryObjectReturnType<\n Query extends PaginatedQueryReference,\n> =\n | {\n data: PaginatedQueryItem<Query>[] | undefined;\n status: \"pending\";\n canLoadMore: false;\n isLoading: true;\n error: undefined;\n loadMore: (numItems: number) => void;\n }\n | {\n data: PaginatedQueryItem<Query>[];\n status: \"success\";\n canLoadMore: boolean;\n isLoading: false;\n error: undefined;\n loadMore: (numItems: number) => void;\n }\n | {\n data: PaginatedQueryItem<Query>[];\n status: \"error\";\n canLoadMore: false;\n isLoading: false;\n error: Error;\n loadMore: (numItems: number) => void;\n };\n\n/**\n * Optimistically update the values in a paginated list.\n *\n * This optimistic update is designed to be used to update data loaded with\n * {@link usePaginatedQuery}. It updates the list by applying\n * `updateValue` to each element of the list across all of the loaded pages.\n *\n * This will only apply to queries with a matching names and arguments.\n *\n * Example usage:\n * ```ts\n * const myMutation = useMutation(api.myModule.myMutation)\n * .withOptimisticUpdate((localStore, mutationArg) => {\n *\n * // Optimistically update the document with ID `mutationArg`\n * // to have an additional property.\n *\n * optimisticallyUpdateValueInPaginatedQuery(\n * localStore,\n * api.myModule.paginatedQuery\n * {},\n * currentValue => {\n * if (mutationArg === currentValue._id) {\n * return {\n * ...currentValue,\n * \"newProperty\": \"newValue\",\n * };\n * }\n * return currentValue;\n * }\n * );\n *\n * });\n * ```\n *\n * @param localStore - An {@link OptimisticLocalStore} to update.\n * @param query - A {@link FunctionReference} for the paginated query to update.\n * @param args - The arguments object to the query function, excluding the\n * `paginationOpts` property.\n * @param updateValue - A function to produce the new values.\n *\n * @public\n */\nexport function optimisticallyUpdateValueInPaginatedQuery<\n Query extends PaginatedQueryReference,\n>(\n localStore: OptimisticLocalStore,\n query: Query,\n args: PaginatedQueryArgs<Query>,\n updateValue: (\n currentValue: PaginatedQueryItem<Query>,\n ) => PaginatedQueryItem<Query>,\n): void {\n const expectedArgs = JSON.stringify(convexToJson(args as Value));\n\n for (const queryResult of localStore.getAllQueries(query)) {\n if (queryResult.value !== undefined) {\n const { paginationOpts: _, ...innerArgs } = queryResult.args as {\n paginationOpts: PaginationOptions;\n };\n if (JSON.stringify(convexToJson(innerArgs as Value)) === expectedArgs) {\n const value = queryResult.value;\n if (\n typeof value === \"object\" &&\n value !== null &&\n Array.isArray(value.page)\n ) {\n localStore.setQuery(query, queryResult.args, {\n ...value,\n page: value.page.map(updateValue),\n });\n }\n }\n }\n }\n}\n\n/**\n * Updates a paginated query to insert an element at the top of the list.\n *\n * This is regardless of the sort order, so if the list is in descending order,\n * the inserted element will be treated as the \"biggest\" element, but if it's\n * ascending, it'll be treated as the \"smallest\".\n *\n * Example:\n * ```ts\n * const createTask = useMutation(api.tasks.create)\n * .withOptimisticUpdate((localStore, mutationArgs) => {\n * insertAtTop({\n * paginatedQuery: api.tasks.list,\n * argsToMatch: { listId: mutationArgs.listId },\n * localQueryStore: localStore,\n * item: { _id: crypto.randomUUID() as Id<\"tasks\">, title: mutationArgs.title, completed: false },\n * });\n * });\n * ```\n *\n * @param options.paginatedQuery - A function reference to the paginated query.\n * @param options.argsToMatch - Optional arguments that must be in each relevant paginated query.\n * This is useful if you use the same query function with different arguments to load\n * different lists.\n * @param options.localQueryStore\n * @param options.item The item to insert.\n * @returns\n */\nexport function insertAtTop<Query extends PaginatedQueryReference>(options: {\n paginatedQuery: Query;\n argsToMatch?: Partial<PaginatedQueryArgs<Query>>;\n localQueryStore: OptimisticLocalStore;\n item: PaginatedQueryItem<Query>;\n}) {\n const { paginatedQuery, argsToMatch, localQueryStore, item } = options;\n const queries = localQueryStore.getAllQueries(paginatedQuery);\n const queriesThatMatch = queries.filter((q) => {\n if (argsToMatch === undefined) {\n return true;\n }\n return Object.keys(argsToMatch).every(\n // @ts-expect-error -- This should be safe since both should be plain objects\n (k) => compareValues(argsToMatch[k], q.args[k]) === 0,\n );\n });\n const firstPage = queriesThatMatch.find(\n (q) => q.args.paginationOpts.cursor === null,\n );\n if (firstPage === undefined || firstPage.value === undefined) {\n // first page is not loaded, so don't update it until it loads\n return;\n }\n localQueryStore.setQuery(paginatedQuery, firstPage.args, {\n ...firstPage.value,\n page: [item, ...firstPage.value.page],\n });\n}\n\n/**\n * Updates a paginated query to insert an element at the bottom of the list.\n *\n * This is regardless of the sort order, so if the list is in descending order,\n * the inserted element will be treated as the \"smallest\" element, but if it's\n * ascending, it'll be treated as the \"biggest\".\n *\n * This only has an effect if the last page is loaded, since otherwise it would result\n * in the element being inserted at the end of whatever is loaded (which is the middle of the list)\n * and then popping out once the optimistic update is over.\n *\n * @param options.paginatedQuery - A function reference to the paginated query.\n * @param options.argsToMatch - Optional arguments that must be in each relevant paginated query.\n * This is useful if you use the same query function with different arguments to load\n * different lists.\n * @param options.localQueryStore\n * @param options.element The element to insert.\n * @returns\n */\nexport function insertAtBottomIfLoaded<\n Query extends PaginatedQueryReference,\n>(options: {\n paginatedQuery: Query;\n argsToMatch?: Partial<PaginatedQueryArgs<Query>>;\n localQueryStore: OptimisticLocalStore;\n item: PaginatedQueryItem<Query>;\n}) {\n const { paginatedQuery, localQueryStore, item, argsToMatch } = options;\n const queries = localQueryStore.getAllQueries(paginatedQuery);\n const queriesThatMatch = queries.filter((q) => {\n if (argsToMatch === undefined) {\n return true;\n }\n return Object.keys(argsToMatch).every(\n // @ts-expect-error -- This should be safe since both should be plain objects\n (k) => compareValues(argsToMatch[k], q.args[k]) === 0,\n );\n });\n const lastPage = queriesThatMatch.find(\n (q) => q.value !== undefined && q.value.isDone,\n );\n if (lastPage === undefined) {\n // last page is not loaded, so don't update it since the item would immediately pop out\n // when the server updates\n return;\n }\n localQueryStore.setQuery(paginatedQuery, lastPage.args, {\n ...lastPage.value!,\n page: [...lastPage.value!.page, item],\n });\n}\n\ntype LocalQueryResult<Query extends FunctionReference<\"query\">> = {\n args: FunctionArgs<Query>;\n value: undefined | FunctionReturnType<Query>;\n};\n\ntype LoadedResult<Query extends FunctionReference<\"query\">> = {\n args: FunctionArgs<Query>;\n value: FunctionReturnType<Query>;\n};\n\n/**\n * This is a helper function for inserting an item at a specific position in a paginated query.\n *\n * You must provide the sortOrder and a function for deriving the sort key (an array of values) from an item in the list.\n *\n * This will only work if the server query uses the same sort order and sort key as the optimistic update.\n *\n * Example:\n * ```ts\n * const createTask = useMutation(api.tasks.create)\n * .withOptimisticUpdate((localStore, mutationArgs) => {\n * insertAtPosition({\n * paginatedQuery: api.tasks.listByPriority,\n * argsToMatch: { listId: mutationArgs.listId },\n * sortOrder: \"asc\",\n * sortKeyFromItem: (item) => [item.priority, item._creationTime],\n * localQueryStore: localStore,\n * item: {\n * _id: crypto.randomUUID() as Id<\"tasks\">,\n * _creationTime: Date.now(),\n * title: mutationArgs.title,\n * completed: false,\n * priority: mutationArgs.priority,\n * },\n * });\n * });\n * ```\n * @param options.paginatedQuery - A function reference to the paginated query.\n * @param options.argsToMatch - Optional arguments that must be in each relevant paginated query.\n * This is useful if you use the same query function with different arguments to load\n * different lists.\n * @param options.sortOrder - The sort order of the paginated query (\"asc\" or \"desc\").\n * @param options.sortKeyFromItem - A function for deriving the sort key (an array of values) from an element in the list.\n * Including a tie-breaker field like `_creationTime` is recommended.\n * @param options.localQueryStore\n * @param options.item - The item to insert.\n * @returns\n */\nexport function insertAtPosition<\n Query extends PaginatedQueryReference,\n>(options: {\n paginatedQuery: Query;\n argsToMatch?: Partial<PaginatedQueryArgs<Query>>;\n sortOrder: \"asc\" | \"desc\";\n sortKeyFromItem: (element: PaginatedQueryItem<Query>) => Value | Value[];\n localQueryStore: OptimisticLocalStore;\n item: PaginatedQueryItem<Query>;\n}) {\n const {\n paginatedQuery,\n sortOrder,\n sortKeyFromItem,\n localQueryStore,\n item,\n argsToMatch,\n } = options;\n\n const queries: LocalQueryResult<Query>[] =\n localQueryStore.getAllQueries(paginatedQuery);\n // Group into sets of pages for the same usePaginatedQuery. Grouping is by all\n // args except paginationOpts, but including paginationOpts.id.\n const queryGroups: Record<string, LocalQueryResult<Query>[]> = {};\n for (const query of queries) {\n if (\n argsToMatch !== undefined &&\n !Object.keys(argsToMatch).every(\n (k) =>\n // @ts-ignore why is this not working?\n argsToMatch[k] === query.args[k],\n )\n ) {\n continue;\n }\n const key = JSON.stringify(\n Object.fromEntries(\n Object.entries(query.args).map(([k, v]) => [\n k,\n k === \"paginationOpts\" ? (v as any).id : v,\n ]),\n ),\n );\n queryGroups[key] ??= [];\n queryGroups[key].push(query);\n }\n for (const pageQueries of Object.values(queryGroups)) {\n insertAtPositionInPages({\n pageQueries,\n paginatedQuery,\n sortOrder,\n sortKeyFromItem,\n localQueryStore,\n item,\n });\n }\n}\n\nfunction insertAtPositionInPages<\n Query extends PaginatedQueryReference,\n>(options: {\n pageQueries: LocalQueryResult<Query>[];\n paginatedQuery: Query;\n sortOrder: \"asc\" | \"desc\";\n sortKeyFromItem: (element: PaginatedQueryItem<Query>) => Value | Value[];\n localQueryStore: OptimisticLocalStore;\n item: PaginatedQueryItem<Query>;\n}) {\n const {\n pageQueries,\n sortOrder,\n sortKeyFromItem,\n localQueryStore,\n item,\n paginatedQuery,\n } = options;\n const insertedKey = sortKeyFromItem(item);\n const loadedPages: LoadedResult<Query>[] = pageQueries.filter(\n (q): q is LoadedResult<Query> =>\n q.value !== undefined && q.value.page.length > 0,\n );\n const sortedPages = loadedPages.sort((a, b) => {\n const aKey = sortKeyFromItem(a.value.page[0]);\n const bKey = sortKeyFromItem(b.value.page[0]);\n if (sortOrder === \"asc\") {\n return compareValues(aKey, bKey);\n } else {\n return compareValues(bKey, aKey);\n }\n });\n\n // check if the inserted element is before the first page\n const firstLoadedPage = sortedPages[0];\n if (firstLoadedPage === undefined) {\n // no pages, so don't update until they load\n return;\n }\n const firstPageKey = sortKeyFromItem(firstLoadedPage.value.page[0]);\n const isBeforeFirstPage =\n sortOrder === \"asc\"\n ? compareValues(insertedKey, firstPageKey) <= 0\n : compareValues(insertedKey, firstPageKey) >= 0;\n if (isBeforeFirstPage) {\n if (firstLoadedPage.args.paginationOpts.cursor === null) {\n localQueryStore.setQuery(paginatedQuery, firstLoadedPage.args, {\n ...firstLoadedPage.value,\n page: [item, ...firstLoadedPage.value.page],\n });\n } else {\n // if the very first page is not loaded\n return;\n }\n return;\n }\n\n const lastLoadedPage = sortedPages[sortedPages.length - 1];\n if (lastLoadedPage === undefined) {\n // no pages, so don't update until they load\n return;\n }\n const lastPageKey = sortKeyFromItem(\n lastLoadedPage.value.page[lastLoadedPage.value.page.length - 1],\n );\n const isAfterLastPage =\n sortOrder === \"asc\"\n ? compareValues(insertedKey, lastPageKey) >= 0\n : compareValues(insertedKey, lastPageKey) <= 0;\n if (isAfterLastPage) {\n // Only update if the last page is done loading, otherwise it will pop out\n // when the server updates the query\n if (lastLoadedPage.value.isDone) {\n localQueryStore.setQuery(paginatedQuery, lastLoadedPage.args, {\n ...lastLoadedPage.value,\n page: [...lastLoadedPage.value.page, item],\n });\n }\n return;\n }\n\n // if sorted in ascending order, find the first page that starts with a key greater than the inserted element,\n // and update the page before it\n // if sorted in descending order, find the first page that starts with a key less than the inserted element,\n // and update the page before it\n\n const successorPageIndex = sortedPages.findIndex((p) =>\n sortOrder === \"asc\"\n ? compareValues(sortKeyFromItem(p.value.page[0]), insertedKey) > 0\n : compareValues(sortKeyFromItem(p.value.page[0]), insertedKey) < 0,\n );\n const pageToUpdate =\n successorPageIndex === -1\n ? sortedPages[sortedPages.length - 1]\n : sortedPages[successorPageIndex - 1];\n if (pageToUpdate === undefined) {\n // no pages, so don't update until they load\n return;\n }\n // If ascending, find the first element that is greater than or equal to the inserted element\n // If descending, find the first element that is less than or equal to the inserted element\n const indexWithinPage = pageToUpdate.value.page.findIndex((e) =>\n sortOrder === \"asc\"\n ? compareValues(sortKeyFromItem(e), insertedKey) >= 0\n : compareValues(sortKeyFromItem(e), insertedKey) <= 0,\n );\n const newPage =\n indexWithinPage === -1\n ? [...pageToUpdate.value.page, item]\n : [\n ...pageToUpdate.value.page.slice(0, indexWithinPage),\n item,\n ...pageToUpdate.value.page.slice(indexWithinPage),\n ];\n localQueryStore.setQuery(paginatedQuery, pageToUpdate.args, {\n ...pageToUpdate.value,\n page: newPage,\n });\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { useMemo, useState } from \"react\";\n\nimport { OptimisticLocalStore } from \"../browser/index.js\";\nimport {\n FunctionReturnType,\n PaginationOptions,\n paginationOptsValidator,\n PaginationResult,\n} from \"../server/index.js\";\nimport { ConvexError, convexToJson, Infer, Value } from \"../values/index.js\";\nimport { useQueries } from \"./use_queries.js\";\nimport {\n FunctionArgs,\n FunctionReference,\n getFunctionName,\n} from \"../server/api.js\";\nimport { BetterOmit, Expand } from \"../type_utils.js\";\nimport { useConvex } from \"./client.js\";\nimport { compareValues } from \"../values/compare.js\";\n\n/**\n * A {@link server.FunctionReference} that is usable with {@link usePaginatedQuery}.\n *\n * This function reference must:\n * - Refer to a public query\n * - Have an argument named \"paginationOpts\" of type {@link server.PaginationOptions}\n * - Have a return type of {@link server.PaginationResult}.\n *\n * @public\n */\nexport type PaginatedQueryReference = FunctionReference<\n \"query\",\n \"public\",\n { paginationOpts: PaginationOptions },\n PaginationResult<any>\n>;\n\n// Incrementing integer for each page queried in the usePaginatedQuery hook.\ntype QueryPageKey = number;\n\ntype UsePaginatedQueryState = {\n query: FunctionReference<\"query\">;\n args: Record<string, Value>;\n id: number;\n nextPageKey: QueryPageKey;\n pageKeys: QueryPageKey[];\n queries: Record<\n QueryPageKey,\n {\n query: FunctionReference<\"query\">;\n // Use the validator type as a test that it matches the args\n // we generate.\n args: { paginationOpts: Infer<typeof paginationOptsValidator> };\n }\n >;\n ongoingSplits: Record<QueryPageKey, [QueryPageKey, QueryPageKey]>;\n skip: boolean;\n};\n\nconst splitQuery =\n (key: QueryPageKey, splitCursor: string, continueCursor: string) =>\n (prevState: UsePaginatedQueryState) => {\n const queries = { ...prevState.queries };\n const splitKey1 = prevState.nextPageKey;\n const splitKey2 = prevState.nextPageKey + 1;\n const nextPageKey = prevState.nextPageKey + 2;\n queries[splitKey1] = {\n query: prevState.query,\n args: {\n ...prevState.args,\n paginationOpts: {\n ...prevState.queries[key].args.paginationOpts,\n endCursor: splitCursor,\n },\n },\n };\n queries[splitKey2] = {\n query: prevState.query,\n args: {\n ...prevState.args,\n paginationOpts: {\n ...prevState.queries[key].args.paginationOpts,\n cursor: splitCursor,\n endCursor: continueCursor,\n },\n },\n };\n const ongoingSplits = { ...prevState.ongoingSplits };\n ongoingSplits[key] = [splitKey1, splitKey2];\n return {\n ...prevState,\n nextPageKey,\n queries,\n ongoingSplits,\n };\n };\n\nconst completeSplitQuery =\n (key: QueryPageKey) => (prevState: UsePaginatedQueryState) => {\n const completedSplit = prevState.ongoingSplits[key];\n if (completedSplit === undefined) {\n return prevState;\n }\n const queries = { ...prevState.queries };\n delete queries[key];\n const ongoingSplits = { ...prevState.ongoingSplits };\n delete ongoingSplits[key];\n let pageKeys = prevState.pageKeys.slice();\n const pageIndex = prevState.pageKeys.findIndex((v) => v === key);\n if (pageIndex >= 0) {\n pageKeys = [\n ...prevState.pageKeys.slice(0, pageIndex),\n ...completedSplit,\n ...prevState.pageKeys.slice(pageIndex + 1),\n ];\n }\n return {\n ...prevState,\n queries,\n pageKeys,\n ongoingSplits,\n };\n };\n\n/**\n * Load data reactively from a paginated query to a create a growing list.\n *\n * This can be used to power \"infinite scroll\" UIs.\n *\n * This hook must be used with public query references that match\n * {@link PaginatedQueryReference}.\n *\n * `usePaginatedQuery` concatenates all the pages of results into a single list\n * and manages the continuation cursors when requesting more items.\n *\n * Example usage:\n * ```typescript\n * const { results, status, isLoading, loadMore } = usePaginatedQuery(\n * api.messages.list,\n * { channel: \"#general\" },\n * { initialNumItems: 5 }\n * );\n * ```\n *\n * If the query reference or arguments change, the pagination state will be reset\n * to the first page. Similarly, if any of the pages result in an InvalidCursor\n * error or an error associated with too much data, the pagination state will also\n * reset to the first page.\n *\n * To learn more about pagination, see [Paginated Queries](https://docs.convex.dev/database/pagination).\n *\n * @param query - A FunctionReference to the public query function to run.\n * @param args - The arguments object for the query function, excluding\n * the `paginationOpts` property. That property is injected by this hook.\n * @param options - An object specifying the `initialNumItems` to be loaded in\n * the first page.\n * @returns A {@link UsePaginatedQueryResult} that includes the currently loaded\n * items, the status of the pagination, and a `loadMore` function.\n *\n * @public\n */\nexport function usePaginatedQuery<Query extends PaginatedQueryReference>(\n query: Query,\n args: PaginatedQueryArgs<Query> | \"skip\",\n options: { initialNumItems: number },\n): UsePaginatedQueryReturnType<Query>;\n\nexport function usePaginatedQuery<Query extends PaginatedQueryReference>(\n query: Query,\n args: PaginatedQueryArgs<Query> | \"skip\",\n options: { initialNumItems: number },\n): UsePaginatedQueryReturnType<Query> {\n const { user: positionalResult } = usePaginatedQueryInternal(\n query,\n args,\n options,\n true,\n );\n return positionalResult as unknown as UsePaginatedQueryReturnType<Query>;\n}\n\n/** @internal */\nexport const includePage = Symbol(\"includePageKeys\");\n\n/** @internal */\nexport const page = Symbol(\"page\");\n\n/**\n * @internal\n */\nexport function usePaginatedQueryInternal<\n Query extends PaginatedQueryReference,\n>(\n query: Query,\n args: PaginatedQueryArgs<Query> | \"skip\",\n options: {\n initialNumItems: number;\n [includePage]?: boolean;\n },\n throwOnError: boolean = true,\n): {\n user: UsePaginatedQueryInternalResult<PaginatedQueryItem<Query>>;\n internal: { state: UsePaginatedQueryState };\n} {\n if (\n typeof options?.initialNumItems !== \"number\" ||\n options.initialNumItems < 0\n ) {\n throw new Error(\n `\\`options.initialNumItems\\` must be a positive number. Received \\`${options?.initialNumItems}\\`.`,\n );\n }\n const skip = args === \"skip\";\n const argsObject = skip ? {} : args;\n const queryName = getFunctionName(query);\n const createInitialState = useMemo(() => {\n return () => {\n const id = nextPaginationId();\n return {\n query,\n args: argsObject as Record<string, Value>,\n id,\n nextPageKey: 1,\n pageKeys: skip ? [] : [0],\n queries: skip\n ? ({} as UsePaginatedQueryState[\"queries\"])\n : {\n 0: {\n query,\n args: {\n ...argsObject,\n paginationOpts: {\n numItems: options.initialNumItems,\n cursor: null,\n id,\n },\n },\n },\n },\n ongoingSplits: {},\n skip,\n };\n };\n // ESLint doesn't like that we're stringifying the args. We do this because\n // we want to avoid rerendering if the args are a different\n // object that serializes to the same result.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n // eslint-disable-next-line react-hooks/exhaustive-deps\n JSON.stringify(convexToJson(argsObject as Value)),\n queryName,\n options.initialNumItems,\n skip,\n ]);\n\n const [state, setState] =\n useState<UsePaginatedQueryState>(createInitialState);\n\n // `currState` is the state that we'll render based on.\n let currState = state;\n if (\n getFunctionName(query) !== getFunctionName(state.query) ||\n JSON.stringify(convexToJson(argsObject as Value)) !==\n JSON.stringify(convexToJson(state.args)) ||\n skip !== state.skip\n ) {\n currState = createInitialState();\n setState(currState);\n }\n const convexClient = useConvex();\n const logger = convexClient.logger;\n\n const resultsObject = useQueries(currState.queries);\n\n const isIncludingPageKeys = options[includePage] ?? false;\n const [results, maybeLastResult, maybeError]: [\n Value[],\n undefined | PaginationResult<Value>,\n undefined | Error,\n ] = useMemo(() => {\n let currResult = undefined;\n\n const allItems = [];\n for (const pageKey of currState.pageKeys) {\n currResult = resultsObject[pageKey];\n if (currResult === undefined) {\n break;\n }\n\n if (currResult instanceof Error) {\n if (\n currResult.message.includes(\"InvalidCursor\") ||\n (currResult instanceof ConvexError &&\n typeof currResult.data === \"object\" &&\n currResult.data?.isConvexSystemError === true &&\n currResult.data?.paginationError === \"InvalidCursor\")\n ) {\n // - InvalidCursor: If the cursor is invalid, probably the paginated\n // database query was data-dependent and changed underneath us. The\n // cursor in the params or journal no longer matches the current\n // database query.\n\n // In all cases, we want to restart pagination to throw away all our\n // existing cursors.\n logger.warn(\n \"usePaginatedQuery hit error, resetting pagination state: \" +\n currResult.message,\n );\n setState(createInitialState);\n return [[], undefined, undefined];\n } else {\n if (throwOnError) {\n throw currResult;\n }\n return [allItems, undefined, currResult];\n }\n }\n const ongoingSplit = currState.ongoingSplits[pageKey];\n if (ongoingSplit !== undefined) {\n if (\n resultsObject[ongoingSplit[0]] !== undefined &&\n resultsObject[ongoingSplit[1]] !== undefined\n ) {\n // Both pages of the split have results now. Swap them in.\n setState(completeSplitQuery(pageKey));\n }\n } else if (\n currResult.splitCursor &&\n (currResult.pageStatus === \"SplitRecommended\" ||\n currResult.pageStatus === \"SplitRequired\" ||\n currResult.page.length > options.initialNumItems * 2)\n ) {\n // If a single page has more than double the expected number of items,\n // or if the server requests a split, split the page into two.\n setState(\n splitQuery(\n pageKey,\n currResult.splitCursor,\n currResult.continueCursor,\n ),\n );\n }\n if (currResult.pageStatus === \"SplitRequired\") {\n // If pageStatus is 'SplitRequired', it means the server was not able to\n // fetch the full page. So we stop results before the incomplete\n // page and return 'LoadingMore' while the page is splitting.\n return [allItems, undefined, undefined];\n }\n allItems.push(\n ...(isIncludingPageKeys\n ? currResult.page.map((i: any) => ({\n ...i,\n [page]: pageKey.toString(),\n }))\n : currResult.page),\n );\n }\n return [allItems, currResult, undefined];\n }, [\n resultsObject,\n currState.pageKeys,\n currState.ongoingSplits,\n options.initialNumItems,\n createInitialState,\n logger,\n isIncludingPageKeys,\n throwOnError,\n ]);\n\n const statusObject = useMemo(() => {\n if (maybeError !== undefined) {\n return {\n status: \"Error\",\n isLoading: false,\n error: maybeError,\n loadMore: () => {\n // Intentional noop.\n },\n } as const;\n }\n if (maybeLastResult === undefined) {\n if (currState.nextPageKey === 1) {\n return {\n status: \"LoadingFirstPage\",\n isLoading: true,\n loadMore: () => {\n // Intentional noop.\n },\n } as const;\n } else {\n return {\n status: \"LoadingMore\",\n isLoading: true,\n loadMore: (_numItems: number) => {\n // Intentional noop.\n },\n } as const;\n }\n }\n if (maybeLastResult.isDone) {\n return {\n status: \"Exhausted\",\n isLoading: false,\n loadMore: (_numItems: number) => {\n // Intentional noop.\n },\n } as const;\n }\n const continueCursor = maybeLastResult.continueCursor;\n let alreadyLoadingMore = false;\n return {\n status: \"CanLoadMore\",\n isLoading: false,\n loadMore: (numItems: number) => {\n if (!alreadyLoadingMore) {\n alreadyLoadingMore = true;\n setState((prevState) => {\n const pageKeys = [...prevState.pageKeys, prevState.nextPageKey];\n const queries = { ...prevState.queries };\n queries[prevState.nextPageKey] = {\n query: prevState.query,\n args: {\n ...prevState.args,\n paginationOpts: {\n numItems,\n cursor: continueCursor,\n id: prevState.id,\n },\n },\n };\n return {\n ...prevState,\n nextPageKey: prevState.nextPageKey + 1,\n pageKeys,\n queries,\n };\n });\n }\n },\n } as const;\n }, [maybeError, maybeLastResult, currState.nextPageKey]);\n\n return {\n user: {\n results,\n ...statusObject,\n },\n internal: { state: currState },\n };\n}\n\nlet paginationId = 0;\n/**\n * Generate a new, unique ID for a pagination session.\n *\n * Every usage of {@link usePaginatedQuery} puts a unique ID into the\n * query function arguments as a \"cache-buster\". This serves two purposes:\n *\n * 1. All calls to {@link usePaginatedQuery} have independent query\n * journals.\n *\n * Every time we start a new pagination session, we'll load the first page of\n * results and receive a fresh journal. Without the ID, we might instead reuse\n * a query subscription already present in our client. This isn't desirable\n * because the existing query function result may have grown or shrunk from the\n * requested `initialNumItems`.\n *\n * 2. We can restart the pagination session on some types of errors.\n *\n * Sometimes we want to restart pagination from the beginning if we hit an error.\n * Similar to (1), we'd like to ensure that this new session actually requests\n * its first page from the server and doesn't reuse a query result already\n * present in the client that may have hit the error.\n *\n * @returns The pagination ID.\n */\nfunction nextPaginationId(): number {\n paginationId++;\n return paginationId;\n}\n\n/**\n * Reset pagination id for tests only, so tests know what it is.\n */\nexport function resetPaginationId() {\n paginationId = 0;\n}\n\n/**\n * The result of calling the {@link usePaginatedQuery} hook.\n *\n * This includes:\n * - `results` - An array of the currently loaded results.\n * - `isLoading` - Whether the hook is currently loading results.\n * - `status` - The status of the pagination. The possible statuses are:\n * - \"LoadingFirstPage\": The hook is loading the first page of results.\n * - \"CanLoadMore\": This query may have more items to fetch. Call `loadMore` to\n * fetch another page.\n * - \"LoadingMore\": We're currently loading another page of results.\n * - \"Exhausted\": We've paginated to the end of the list.\n * - `loadMore(n)` A callback to fetch more results. This will only fetch more\n * results if the status is \"CanLoadMore\".\n *\n * @public\n */\nexport type UsePaginatedQueryResult<Item> = {\n results: Item[];\n loadMore: (numItems: number) => void;\n} & (\n | {\n status: \"LoadingFirstPage\";\n isLoading: true;\n }\n | {\n status: \"CanLoadMore\";\n isLoading: false;\n }\n | {\n status: \"LoadingMore\";\n isLoading: true;\n }\n | {\n status: \"Exhausted\";\n isLoading: false;\n }\n);\n\n/**\n * @internal\n */\nexport type UsePaginatedQueryInternalResult<Item> =\n | UsePaginatedQueryResult<Item>\n | {\n results: Item[];\n status: \"Error\";\n isLoading: false;\n error: Error;\n loadMore: (numItems: number) => void;\n };\n\n/**\n * The possible pagination statuses in {@link UsePaginatedQueryResult}.\n *\n * This is a union of string literal types.\n * @public\n */\nexport type PaginationStatus = UsePaginatedQueryResult<any>[\"status\"];\n\n/**\n * Given a {@link PaginatedQueryReference}, get the type of the arguments\n * object for the query, excluding the `paginationOpts` argument.\n *\n * @public\n */\nexport type PaginatedQueryArgs<Query extends PaginatedQueryReference> = Expand<\n BetterOmit<FunctionArgs<Query>, \"paginationOpts\">\n>;\n\n/**\n * Given a {@link PaginatedQueryReference}, get the type of the item being\n * paginated over.\n * @public\n */\nexport type PaginatedQueryItem<Query extends PaginatedQueryReference> =\n FunctionReturnType<Query>[\"page\"][number];\n\n/**\n * The return type of {@link usePaginatedQuery}.\n *\n * @public\n */\nexport type UsePaginatedQueryReturnType<Query extends PaginatedQueryReference> =\n UsePaginatedQueryResult<PaginatedQueryItem<Query>>;\n\n/**\n * Optimistically update the values in a paginated list.\n *\n * This optimistic update is designed to be used to update data loaded with\n * {@link usePaginatedQuery}. It updates the list by applying\n * `updateValue` to each element of the list across all of the loaded pages.\n *\n * This will only apply to queries with a matching names and arguments.\n *\n * Example usage:\n * ```ts\n * const myMutation = useMutation(api.myModule.myMutation)\n * .withOptimisticUpdate((localStore, mutationArg) => {\n *\n * // Optimistically update the document with ID `mutationArg`\n * // to have an additional property.\n *\n * optimisticallyUpdateValueInPaginatedQuery(\n * localStore,\n * api.myModule.paginatedQuery\n * {},\n * currentValue => {\n * if (mutationArg === currentValue._id) {\n * return {\n * ...currentValue,\n * \"newProperty\": \"newValue\",\n * };\n * }\n * return currentValue;\n * }\n * );\n *\n * });\n * ```\n *\n * @param localStore - An {@link OptimisticLocalStore} to update.\n * @param query - A {@link FunctionReference} for the paginated query to update.\n * @param args - The arguments object to the query function, excluding the\n * `paginationOpts` property.\n * @param updateValue - A function to produce the new values.\n *\n * @public\n */\nexport function optimisticallyUpdateValueInPaginatedQuery<\n Query extends PaginatedQueryReference,\n>(\n localStore: OptimisticLocalStore,\n query: Query,\n args: PaginatedQueryArgs<Query>,\n updateValue: (\n currentValue: PaginatedQueryItem<Query>,\n ) => PaginatedQueryItem<Query>,\n): void {\n const expectedArgs = JSON.stringify(convexToJson(args as Value));\n\n for (const queryResult of localStore.getAllQueries(query)) {\n if (queryResult.value !== undefined) {\n const { paginationOpts: _, ...innerArgs } = queryResult.args as {\n paginationOpts: PaginationOptions;\n };\n if (JSON.stringify(convexToJson(innerArgs as Value)) === expectedArgs) {\n const value = queryResult.value;\n if (\n typeof value === \"object\" &&\n value !== null &&\n Array.isArray(value.page)\n ) {\n localStore.setQuery(query, queryResult.args, {\n ...value,\n page: value.page.map(updateValue),\n });\n }\n }\n }\n }\n}\n\n/**\n * Updates a paginated query to insert an element at the top of the list.\n *\n * This is regardless of the sort order, so if the list is in descending order,\n * the inserted element will be treated as the \"biggest\" element, but if it's\n * ascending, it'll be treated as the \"smallest\".\n *\n * Example:\n * ```ts\n * const createTask = useMutation(api.tasks.create)\n * .withOptimisticUpdate((localStore, mutationArgs) => {\n * insertAtTop({\n * paginatedQuery: api.tasks.list,\n * argsToMatch: { listId: mutationArgs.listId },\n * localQueryStore: localStore,\n * item: { _id: crypto.randomUUID() as Id<\"tasks\">, title: mutationArgs.title, completed: false },\n * });\n * });\n * ```\n *\n * @param options.paginatedQuery - A function reference to the paginated query.\n * @param options.argsToMatch - Optional arguments that must be in each relevant paginated query.\n * This is useful if you use the same query function with different arguments to load\n * different lists.\n * @param options.localQueryStore\n * @param options.item The item to insert.\n * @returns\n */\nexport function insertAtTop<Query extends PaginatedQueryReference>(options: {\n paginatedQuery: Query;\n argsToMatch?: Partial<PaginatedQueryArgs<Query>>;\n localQueryStore: OptimisticLocalStore;\n item: PaginatedQueryItem<Query>;\n}) {\n const { paginatedQuery, argsToMatch, localQueryStore, item } = options;\n const queries = localQueryStore.getAllQueries(paginatedQuery);\n const queriesThatMatch = queries.filter((q) => {\n if (argsToMatch === undefined) {\n return true;\n }\n return Object.keys(argsToMatch).every(\n // @ts-expect-error -- This should be safe since both should be plain objects\n (k) => compareValues(argsToMatch[k], q.args[k]) === 0,\n );\n });\n const firstPage = queriesThatMatch.find(\n (q) => q.args.paginationOpts.cursor === null,\n );\n if (firstPage === undefined || firstPage.value === undefined) {\n // first page is not loaded, so don't update it until it loads\n return;\n }\n localQueryStore.setQuery(paginatedQuery, firstPage.args, {\n ...firstPage.value,\n page: [item, ...firstPage.value.page],\n });\n}\n\n/**\n * Updates a paginated query to insert an element at the bottom of the list.\n *\n * This is regardless of the sort order, so if the list is in descending order,\n * the inserted element will be treated as the \"smallest\" element, but if it's\n * ascending, it'll be treated as the \"biggest\".\n *\n * This only has an effect if the last page is loaded, since otherwise it would result\n * in the element being inserted at the end of whatever is loaded (which is the middle of the list)\n * and then popping out once the optimistic update is over.\n *\n * @param options.paginatedQuery - A function reference to the paginated query.\n * @param options.argsToMatch - Optional arguments that must be in each relevant paginated query.\n * This is useful if you use the same query function with different arguments to load\n * different lists.\n * @param options.localQueryStore\n * @param options.element The element to insert.\n * @returns\n */\nexport function insertAtBottomIfLoaded<\n Query extends PaginatedQueryReference,\n>(options: {\n paginatedQuery: Query;\n argsToMatch?: Partial<PaginatedQueryArgs<Query>>;\n localQueryStore: OptimisticLocalStore;\n item: PaginatedQueryItem<Query>;\n}) {\n const { paginatedQuery, localQueryStore, item, argsToMatch } = options;\n const queries = localQueryStore.getAllQueries(paginatedQuery);\n const queriesThatMatch = queries.filter((q) => {\n if (argsToMatch === undefined) {\n return true;\n }\n return Object.keys(argsToMatch).every(\n // @ts-expect-error -- This should be safe since both should be plain objects\n (k) => compareValues(argsToMatch[k], q.args[k]) === 0,\n );\n });\n const lastPage = queriesThatMatch.find(\n (q) => q.value !== undefined && q.value.isDone,\n );\n if (lastPage === undefined) {\n // last page is not loaded, so don't update it since the item would immediately pop out\n // when the server updates\n return;\n }\n localQueryStore.setQuery(paginatedQuery, lastPage.args, {\n ...lastPage.value!,\n page: [...lastPage.value!.page, item],\n });\n}\n\ntype LocalQueryResult<Query extends FunctionReference<\"query\">> = {\n args: FunctionArgs<Query>;\n value: undefined | FunctionReturnType<Query>;\n};\n\ntype LoadedResult<Query extends FunctionReference<\"query\">> = {\n args: FunctionArgs<Query>;\n value: FunctionReturnType<Query>;\n};\n\n/**\n * This is a helper function for inserting an item at a specific position in a paginated query.\n *\n * You must provide the sortOrder and a function for deriving the sort key (an array of values) from an item in the list.\n *\n * This will only work if the server query uses the same sort order and sort key as the optimistic update.\n *\n * Example:\n * ```ts\n * const createTask = useMutation(api.tasks.create)\n * .withOptimisticUpdate((localStore, mutationArgs) => {\n * insertAtPosition({\n * paginatedQuery: api.tasks.listByPriority,\n * argsToMatch: { listId: mutationArgs.listId },\n * sortOrder: \"asc\",\n * sortKeyFromItem: (item) => [item.priority, item._creationTime],\n * localQueryStore: localStore,\n * item: {\n * _id: crypto.randomUUID() as Id<\"tasks\">,\n * _creationTime: Date.now(),\n * title: mutationArgs.title,\n * completed: false,\n * priority: mutationArgs.priority,\n * },\n * });\n * });\n * ```\n * @param options.paginatedQuery - A function reference to the paginated query.\n * @param options.argsToMatch - Optional arguments that must be in each relevant paginated query.\n * This is useful if you use the same query function with different arguments to load\n * different lists.\n * @param options.sortOrder - The sort order of the paginated query (\"asc\" or \"desc\").\n * @param options.sortKeyFromItem - A function for deriving the sort key (an array of values) from an element in the list.\n * Including a tie-breaker field like `_creationTime` is recommended.\n * @param options.localQueryStore\n * @param options.item - The item to insert.\n * @returns\n */\nexport function insertAtPosition<\n Query extends PaginatedQueryReference,\n>(options: {\n paginatedQuery: Query;\n argsToMatch?: Partial<PaginatedQueryArgs<Query>>;\n sortOrder: \"asc\" | \"desc\";\n sortKeyFromItem: (element: PaginatedQueryItem<Query>) => Value | Value[];\n localQueryStore: OptimisticLocalStore;\n item: PaginatedQueryItem<Query>;\n}) {\n const {\n paginatedQuery,\n sortOrder,\n sortKeyFromItem,\n localQueryStore,\n item,\n argsToMatch,\n } = options;\n\n const queries: LocalQueryResult<Query>[] =\n localQueryStore.getAllQueries(paginatedQuery);\n // Group into sets of pages for the same usePaginatedQuery. Grouping is by all\n // args except paginationOpts, but including paginationOpts.id.\n const queryGroups: Record<string, LocalQueryResult<Query>[]> = {};\n for (const query of queries) {\n if (\n argsToMatch !== undefined &&\n !Object.keys(argsToMatch).every(\n (k) =>\n // @ts-ignore why is this not working?\n argsToMatch[k] === query.args[k],\n )\n ) {\n continue;\n }\n const key = JSON.stringify(\n Object.fromEntries(\n Object.entries(query.args).map(([k, v]) => [\n k,\n k === \"paginationOpts\" ? (v as any).id : v,\n ]),\n ),\n );\n queryGroups[key] ??= [];\n queryGroups[key].push(query);\n }\n for (const pageQueries of Object.values(queryGroups)) {\n insertAtPositionInPages({\n pageQueries,\n paginatedQuery,\n sortOrder,\n sortKeyFromItem,\n localQueryStore,\n item,\n });\n }\n}\n\nfunction insertAtPositionInPages<\n Query extends PaginatedQueryReference,\n>(options: {\n pageQueries: LocalQueryResult<Query>[];\n paginatedQuery: Query;\n sortOrder: \"asc\" | \"desc\";\n sortKeyFromItem: (element: PaginatedQueryItem<Query>) => Value | Value[];\n localQueryStore: OptimisticLocalStore;\n item: PaginatedQueryItem<Query>;\n}) {\n const {\n pageQueries,\n sortOrder,\n sortKeyFromItem,\n localQueryStore,\n item,\n paginatedQuery,\n } = options;\n const insertedKey = sortKeyFromItem(item);\n const loadedPages: LoadedResult<Query>[] = pageQueries.filter(\n (q): q is LoadedResult<Query> =>\n q.value !== undefined && q.value.page.length > 0,\n );\n const sortedPages = loadedPages.sort((a, b) => {\n const aKey = sortKeyFromItem(a.value.page[0]);\n const bKey = sortKeyFromItem(b.value.page[0]);\n if (sortOrder === \"asc\") {\n return compareValues(aKey, bKey);\n } else {\n return compareValues(bKey, aKey);\n }\n });\n\n // check if the inserted element is before the first page\n const firstLoadedPage = sortedPages[0];\n if (firstLoadedPage === undefined) {\n // no pages, so don't update until they load\n return;\n }\n const firstPageKey = sortKeyFromItem(firstLoadedPage.value.page[0]);\n const isBeforeFirstPage =\n sortOrder === \"asc\"\n ? compareValues(insertedKey, firstPageKey) <= 0\n : compareValues(insertedKey, firstPageKey) >= 0;\n if (isBeforeFirstPage) {\n if (firstLoadedPage.args.paginationOpts.cursor === null) {\n localQueryStore.setQuery(paginatedQuery, firstLoadedPage.args, {\n ...firstLoadedPage.value,\n page: [item, ...firstLoadedPage.value.page],\n });\n } else {\n // if the very first page is not loaded\n return;\n }\n return;\n }\n\n const lastLoadedPage = sortedPages[sortedPages.length - 1];\n if (lastLoadedPage === undefined) {\n // no pages, so don't update until they load\n return;\n }\n const lastPageKey = sortKeyFromItem(\n lastLoadedPage.value.page[lastLoadedPage.value.page.length - 1],\n );\n const isAfterLastPage =\n sortOrder === \"asc\"\n ? compareValues(insertedKey, lastPageKey) >= 0\n : compareValues(insertedKey, lastPageKey) <= 0;\n if (isAfterLastPage) {\n // Only update if the last page is done loading, otherwise it will pop out\n // when the server updates the query\n if (lastLoadedPage.value.isDone) {\n localQueryStore.setQuery(paginatedQuery, lastLoadedPage.args, {\n ...lastLoadedPage.value,\n page: [...lastLoadedPage.value.page, item],\n });\n }\n return;\n }\n\n // if sorted in ascending order, find the first page that starts with a key greater than the inserted element,\n // and update the page before it\n // if sorted in descending order, find the first page that starts with a key less than the inserted element,\n // and update the page before it\n\n const successorPageIndex = sortedPages.findIndex((p) =>\n sortOrder === \"asc\"\n ? compareValues(sortKeyFromItem(p.value.page[0]), insertedKey) > 0\n : compareValues(sortKeyFromItem(p.value.page[0]), insertedKey) < 0,\n );\n const pageToUpdate =\n successorPageIndex === -1\n ? sortedPages[sortedPages.length - 1]\n : sortedPages[successorPageIndex - 1];\n if (pageToUpdate === undefined) {\n // no pages, so don't update until they load\n return;\n }\n // If ascending, find the first element that is greater than or equal to the inserted element\n // If descending, find the first element that is less than or equal to the inserted element\n const indexWithinPage = pageToUpdate.value.page.findIndex((e) =>\n sortOrder === \"asc\"\n ? compareValues(sortKeyFromItem(e), insertedKey) >= 0\n : compareValues(sortKeyFromItem(e), insertedKey) <= 0,\n );\n const newPage =\n indexWithinPage === -1\n ? [...pageToUpdate.value.page, item]\n : [\n ...pageToUpdate.value.page.slice(0, indexWithinPage),\n item,\n ...pageToUpdate.value.page.slice(indexWithinPage),\n ];\n localQueryStore.setQuery(paginatedQuery, pageToUpdate.args, {\n ...pageToUpdate.value,\n page: newPage,\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|