convex 1.36.1 → 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.
Files changed (219) hide show
  1. package/CHANGELOG.md +47 -25
  2. package/dist/browser.bundle.js +1 -1
  3. package/dist/browser.bundle.js.map +1 -1
  4. package/dist/cjs/cli/codegen_templates/agentsmd.js +8 -2
  5. package/dist/cjs/cli/codegen_templates/agentsmd.js.map +2 -2
  6. package/dist/cjs/cli/codegen_templates/claudemd.js +2 -0
  7. package/dist/cjs/cli/codegen_templates/claudemd.js.map +2 -2
  8. package/dist/cjs/cli/configure.js +0 -8
  9. package/dist/cjs/cli/configure.js.map +2 -2
  10. package/dist/cjs/cli/deployment.js +2 -1
  11. package/dist/cjs/cli/deployment.js.map +2 -2
  12. package/dist/cjs/cli/deploymentToken.js +30 -0
  13. package/dist/cjs/cli/deploymentToken.js.map +7 -0
  14. package/dist/cjs/cli/deploymentTokenCreate.js +109 -0
  15. package/dist/cjs/cli/deploymentTokenCreate.js.map +7 -0
  16. package/dist/cjs/cli/deploymentTokenDelete.js +87 -0
  17. package/dist/cjs/cli/deploymentTokenDelete.js.map +7 -0
  18. package/dist/cjs/cli/envDefault.js +1 -1
  19. package/dist/cjs/cli/envDefault.js.map +1 -1
  20. package/dist/cjs/cli/generatedApi.js.map +1 -1
  21. package/dist/cjs/cli/lib/generatedFunctionLogsApi.js.map +1 -1
  22. package/dist/cjs/cli/lib/usage.js +13 -6
  23. package/dist/cjs/cli/lib/usage.js.map +2 -2
  24. package/dist/cjs/cli/lib/workos/environmentApi.js +6 -12
  25. package/dist/cjs/cli/lib/workos/environmentApi.js.map +3 -3
  26. package/dist/cjs/index.js +1 -1
  27. package/dist/cjs/index.js.map +1 -1
  28. package/dist/cjs/react/client.js +40 -42
  29. package/dist/cjs/react/client.js.map +2 -2
  30. package/dist/cjs/react/index.js +1 -0
  31. package/dist/cjs/react/index.js.map +2 -2
  32. package/dist/cjs/react/use_paginated_query.js +5 -46
  33. package/dist/cjs/react/use_paginated_query.js.map +2 -2
  34. package/dist/cjs/react/use_paginated_query2.js.map +2 -2
  35. package/dist/cjs/server/audit_logging.js +67 -0
  36. package/dist/cjs/server/audit_logging.js.map +7 -0
  37. package/dist/cjs/server/impl/meta_impl.js +27 -3
  38. package/dist/cjs/server/impl/meta_impl.js.map +2 -2
  39. package/dist/cjs/server/impl/registration_impl.js +2 -0
  40. package/dist/cjs/server/impl/registration_impl.js.map +2 -2
  41. package/dist/cjs/server/index.js +2 -0
  42. package/dist/cjs/server/index.js.map +2 -2
  43. package/dist/cjs/server/log.js +30 -0
  44. package/dist/cjs/server/log.js.map +7 -0
  45. package/dist/cjs/server/logVars.js +48 -0
  46. package/dist/cjs/server/logVars.js.map +7 -0
  47. package/dist/cjs/server/meta.js.map +1 -1
  48. package/dist/cjs/server/registration.js.map +1 -1
  49. package/dist/cjs-types/cli/codegen_templates/agentsmd.d.ts.map +1 -1
  50. package/dist/cjs-types/cli/codegen_templates/claudemd.d.ts.map +1 -1
  51. package/dist/cjs-types/cli/configure.d.ts.map +1 -1
  52. package/dist/cjs-types/cli/deployment.d.ts.map +1 -1
  53. package/dist/cjs-types/cli/deploymentToken.d.ts +3 -0
  54. package/dist/cjs-types/cli/deploymentToken.d.ts.map +1 -0
  55. package/dist/cjs-types/cli/deploymentToken.test.d.ts +2 -0
  56. package/dist/cjs-types/cli/deploymentToken.test.d.ts.map +1 -0
  57. package/dist/cjs-types/cli/deploymentTokenCreate.d.ts +13 -0
  58. package/dist/cjs-types/cli/deploymentTokenCreate.d.ts.map +1 -0
  59. package/dist/cjs-types/cli/deploymentTokenDelete.d.ts +11 -0
  60. package/dist/cjs-types/cli/deploymentTokenDelete.d.ts.map +1 -0
  61. package/dist/cjs-types/cli/generatedApi.d.ts +1 -1
  62. package/dist/cjs-types/cli/generatedApi.d.ts.map +1 -1
  63. package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts +1 -0
  64. package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
  65. package/dist/cjs-types/cli/lib/usage.d.ts.map +1 -1
  66. package/dist/cjs-types/cli/lib/workos/environmentApi.d.ts.map +1 -1
  67. package/dist/cjs-types/cli/lib/workos/environmentApi.test.d.ts +2 -0
  68. package/dist/cjs-types/cli/lib/workos/environmentApi.test.d.ts.map +1 -0
  69. package/dist/cjs-types/index.d.ts +1 -1
  70. package/dist/cjs-types/react/client.d.ts +52 -0
  71. package/dist/cjs-types/react/client.d.ts.map +1 -1
  72. package/dist/cjs-types/react/index.d.ts +2 -2
  73. package/dist/cjs-types/react/index.d.ts.map +1 -1
  74. package/dist/cjs-types/react/use_paginated_query.d.ts.map +1 -1
  75. package/dist/cjs-types/react/use_paginated_query2.d.ts +63 -1
  76. package/dist/cjs-types/react/use_paginated_query2.d.ts.map +1 -1
  77. package/dist/cjs-types/server/api.intersect.test.d.ts +2 -0
  78. package/dist/cjs-types/server/api.intersect.test.d.ts.map +1 -0
  79. package/dist/cjs-types/server/audit_logging.d.ts +19 -0
  80. package/dist/cjs-types/server/audit_logging.d.ts.map +1 -0
  81. package/dist/cjs-types/server/audit_logging.test.d.ts +2 -0
  82. package/dist/cjs-types/server/audit_logging.test.d.ts.map +1 -0
  83. package/dist/cjs-types/server/impl/meta_impl.d.ts.map +1 -1
  84. package/dist/cjs-types/server/impl/registration_impl.d.ts.map +1 -1
  85. package/dist/cjs-types/server/index.d.ts +2 -2
  86. package/dist/cjs-types/server/index.d.ts.map +1 -1
  87. package/dist/cjs-types/server/log.d.ts +2 -0
  88. package/dist/cjs-types/server/log.d.ts.map +1 -0
  89. package/dist/cjs-types/server/logVars.d.ts +20 -0
  90. package/dist/cjs-types/server/logVars.d.ts.map +1 -0
  91. package/dist/cjs-types/server/meta.d.ts +40 -0
  92. package/dist/cjs-types/server/meta.d.ts.map +1 -1
  93. package/dist/cjs-types/server/registration.d.ts +5 -2
  94. package/dist/cjs-types/server/registration.d.ts.map +1 -1
  95. package/dist/cli.bundle.cjs +184 -34
  96. package/dist/cli.bundle.cjs.map +4 -4
  97. package/dist/esm/cli/codegen_templates/agentsmd.js +8 -2
  98. package/dist/esm/cli/codegen_templates/agentsmd.js.map +2 -2
  99. package/dist/esm/cli/codegen_templates/claudemd.js +2 -0
  100. package/dist/esm/cli/codegen_templates/claudemd.js.map +2 -2
  101. package/dist/esm/cli/configure.js +0 -8
  102. package/dist/esm/cli/configure.js.map +2 -2
  103. package/dist/esm/cli/deployment.js +2 -1
  104. package/dist/esm/cli/deployment.js.map +2 -2
  105. package/dist/esm/cli/deploymentToken.js +8 -0
  106. package/dist/esm/cli/deploymentToken.js.map +7 -0
  107. package/dist/esm/cli/deploymentTokenCreate.js +91 -0
  108. package/dist/esm/cli/deploymentTokenCreate.js.map +7 -0
  109. package/dist/esm/cli/deploymentTokenDelete.js +68 -0
  110. package/dist/esm/cli/deploymentTokenDelete.js.map +7 -0
  111. package/dist/esm/cli/envDefault.js +1 -1
  112. package/dist/esm/cli/envDefault.js.map +1 -1
  113. package/dist/esm/cli/lib/usage.js +15 -8
  114. package/dist/esm/cli/lib/usage.js.map +2 -2
  115. package/dist/esm/cli/lib/workos/environmentApi.js +6 -12
  116. package/dist/esm/cli/lib/workos/environmentApi.js.map +3 -3
  117. package/dist/esm/index.js +1 -1
  118. package/dist/esm/index.js.map +1 -1
  119. package/dist/esm/react/client.js +38 -41
  120. package/dist/esm/react/client.js.map +2 -2
  121. package/dist/esm/react/index.js +4 -1
  122. package/dist/esm/react/index.js.map +2 -2
  123. package/dist/esm/react/use_paginated_query.js +5 -46
  124. package/dist/esm/react/use_paginated_query.js.map +2 -2
  125. package/dist/esm/react/use_paginated_query2.js.map +2 -2
  126. package/dist/esm/server/audit_logging.js +44 -0
  127. package/dist/esm/server/audit_logging.js.map +7 -0
  128. package/dist/esm/server/impl/meta_impl.js +27 -3
  129. package/dist/esm/server/impl/meta_impl.js.map +2 -2
  130. package/dist/esm/server/impl/registration_impl.js +2 -0
  131. package/dist/esm/server/impl/registration_impl.js.map +2 -2
  132. package/dist/esm/server/index.js +1 -0
  133. package/dist/esm/server/index.js.map +2 -2
  134. package/dist/esm/server/log.js +8 -0
  135. package/dist/esm/server/log.js.map +7 -0
  136. package/dist/esm/server/logVars.js +25 -0
  137. package/dist/esm/server/logVars.js.map +7 -0
  138. package/dist/esm-types/cli/codegen_templates/agentsmd.d.ts.map +1 -1
  139. package/dist/esm-types/cli/codegen_templates/claudemd.d.ts.map +1 -1
  140. package/dist/esm-types/cli/configure.d.ts.map +1 -1
  141. package/dist/esm-types/cli/deployment.d.ts.map +1 -1
  142. package/dist/esm-types/cli/deploymentToken.d.ts +3 -0
  143. package/dist/esm-types/cli/deploymentToken.d.ts.map +1 -0
  144. package/dist/esm-types/cli/deploymentToken.test.d.ts +2 -0
  145. package/dist/esm-types/cli/deploymentToken.test.d.ts.map +1 -0
  146. package/dist/esm-types/cli/deploymentTokenCreate.d.ts +13 -0
  147. package/dist/esm-types/cli/deploymentTokenCreate.d.ts.map +1 -0
  148. package/dist/esm-types/cli/deploymentTokenDelete.d.ts +11 -0
  149. package/dist/esm-types/cli/deploymentTokenDelete.d.ts.map +1 -0
  150. package/dist/esm-types/cli/generatedApi.d.ts +1 -1
  151. package/dist/esm-types/cli/generatedApi.d.ts.map +1 -1
  152. package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts +1 -0
  153. package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
  154. package/dist/esm-types/cli/lib/usage.d.ts.map +1 -1
  155. package/dist/esm-types/cli/lib/workos/environmentApi.d.ts.map +1 -1
  156. package/dist/esm-types/cli/lib/workos/environmentApi.test.d.ts +2 -0
  157. package/dist/esm-types/cli/lib/workos/environmentApi.test.d.ts.map +1 -0
  158. package/dist/esm-types/index.d.ts +1 -1
  159. package/dist/esm-types/react/client.d.ts +52 -0
  160. package/dist/esm-types/react/client.d.ts.map +1 -1
  161. package/dist/esm-types/react/index.d.ts +2 -2
  162. package/dist/esm-types/react/index.d.ts.map +1 -1
  163. package/dist/esm-types/react/use_paginated_query.d.ts.map +1 -1
  164. package/dist/esm-types/react/use_paginated_query2.d.ts +63 -1
  165. package/dist/esm-types/react/use_paginated_query2.d.ts.map +1 -1
  166. package/dist/esm-types/server/api.intersect.test.d.ts +2 -0
  167. package/dist/esm-types/server/api.intersect.test.d.ts.map +1 -0
  168. package/dist/esm-types/server/audit_logging.d.ts +19 -0
  169. package/dist/esm-types/server/audit_logging.d.ts.map +1 -0
  170. package/dist/esm-types/server/audit_logging.test.d.ts +2 -0
  171. package/dist/esm-types/server/audit_logging.test.d.ts.map +1 -0
  172. package/dist/esm-types/server/impl/meta_impl.d.ts.map +1 -1
  173. package/dist/esm-types/server/impl/registration_impl.d.ts.map +1 -1
  174. package/dist/esm-types/server/index.d.ts +2 -2
  175. package/dist/esm-types/server/index.d.ts.map +1 -1
  176. package/dist/esm-types/server/log.d.ts +2 -0
  177. package/dist/esm-types/server/log.d.ts.map +1 -0
  178. package/dist/esm-types/server/logVars.d.ts +20 -0
  179. package/dist/esm-types/server/logVars.d.ts.map +1 -0
  180. package/dist/esm-types/server/meta.d.ts +40 -0
  181. package/dist/esm-types/server/meta.d.ts.map +1 -1
  182. package/dist/esm-types/server/registration.d.ts +5 -2
  183. package/dist/esm-types/server/registration.d.ts.map +1 -1
  184. package/dist/react.bundle.js +45 -88
  185. package/dist/react.bundle.js.map +2 -2
  186. package/package.json +1 -1
  187. package/src/cli/codegen_templates/agentsmd.ts +8 -2
  188. package/src/cli/codegen_templates/claudemd.ts +2 -0
  189. package/src/cli/configure.ts +0 -9
  190. package/src/cli/deployment.ts +3 -1
  191. package/src/cli/deploymentToken.test.ts +372 -0
  192. package/src/cli/deploymentToken.ts +11 -0
  193. package/src/cli/deploymentTokenCreate.ts +113 -0
  194. package/src/cli/deploymentTokenDelete.ts +91 -0
  195. package/src/cli/envDefault.ts +1 -1
  196. package/src/cli/generatedApi.ts +1 -1
  197. package/src/cli/lib/generatedFunctionLogsApi.ts +1 -0
  198. package/src/cli/lib/usage.ts +18 -8
  199. package/src/cli/lib/workos/environmentApi.test.ts +107 -0
  200. package/src/cli/lib/workos/environmentApi.ts +12 -19
  201. package/src/index.ts +1 -1
  202. package/src/react/client.test.tsx +10 -8
  203. package/src/react/client.ts +88 -96
  204. package/src/react/index.ts +6 -1
  205. package/src/react/use_paginated_query.test.tsx +215 -132
  206. package/src/react/use_paginated_query.ts +8 -142
  207. package/src/react/use_paginated_query2.ts +78 -5
  208. package/src/react/use_query_object_options.test.ts +8 -7
  209. package/src/react/use_query_result.test.ts +40 -7
  210. package/src/server/api.intersect.test.ts +109 -0
  211. package/src/server/audit_logging.test.ts +129 -0
  212. package/src/server/audit_logging.ts +75 -0
  213. package/src/server/impl/meta_impl.ts +28 -0
  214. package/src/server/impl/registration_impl.ts +2 -0
  215. package/src/server/index.ts +12 -0
  216. package/src/server/log.ts +16 -0
  217. package/src/server/logVars.ts +34 -0
  218. package/src/server/meta.ts +53 -1
  219. package/src/server/registration.ts +10 -8
@@ -67,24 +67,14 @@ const completeSplitQuery = (key) => (prevState) => {
67
67
  ongoingSplits
68
68
  };
69
69
  };
70
- export function usePaginatedQuery(queryOrOptions, args, options) {
71
- const isObjectOptions = typeof queryOrOptions === "object" && queryOrOptions !== null && "query" in queryOrOptions;
72
- const query = isObjectOptions ? queryOrOptions.query : queryOrOptions;
73
- const queryArgs = isObjectOptions ? queryOrOptions.args : args;
74
- const throwOnError = isObjectOptions ? queryOrOptions.throwOnError ?? false : true;
75
- const initialOptions = isObjectOptions ? { initialNumItems: queryOrOptions.initialNumItems } : options;
70
+ export function usePaginatedQuery(query, args, options) {
76
71
  const { user: positionalResult } = usePaginatedQueryInternal(
77
72
  query,
78
- queryArgs,
79
- initialOptions,
80
- throwOnError
81
- );
82
- if (!isObjectOptions) {
83
- return positionalResult;
84
- }
85
- return reshapeToObjectForm(
86
- positionalResult
73
+ args,
74
+ options,
75
+ true
87
76
  );
77
+ return positionalResult;
88
78
  }
89
79
  export const includePage = Symbol("includePageKeys");
90
80
  export const page = Symbol("page");
@@ -273,37 +263,6 @@ export function usePaginatedQueryInternal(query, args, options, throwOnError = t
273
263
  internal: { state: currState }
274
264
  };
275
265
  }
276
- function reshapeToObjectForm(internal) {
277
- const { results, loadMore } = internal;
278
- if (internal.status === "Error" && "error" in internal) {
279
- return {
280
- data: results,
281
- status: "error",
282
- canLoadMore: false,
283
- isLoading: false,
284
- error: internal.error,
285
- loadMore
286
- };
287
- }
288
- if (internal.status === "LoadingFirstPage" || internal.status === "LoadingMore") {
289
- return {
290
- data: internal.status === "LoadingFirstPage" ? void 0 : results,
291
- status: "pending",
292
- canLoadMore: false,
293
- isLoading: true,
294
- error: void 0,
295
- loadMore
296
- };
297
- }
298
- return {
299
- data: results,
300
- status: "success",
301
- canLoadMore: internal.status === "CanLoadMore",
302
- isLoading: false,
303
- error: void 0,
304
- loadMore
305
- };
306
- }
307
266
  let paginationId = 0;
308
267
  function nextPaginationId() {
309
268
  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
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/react/use_paginated_query2.ts"],
4
- "sourcesContent": ["import { useState } from \"react\";\n\nimport { FunctionReference, getFunctionName } from \"../server/api.js\";\nimport {\n PaginatedQueryReference,\n PaginatedQueryArgs,\n UsePaginatedQueryOptions,\n UsePaginatedQueryReturnType,\n UsePaginatedQueryObjectReturnType,\n} from \"./use_paginated_query.js\";\nimport { convexToJson, Value } from \"../values/value.js\";\nimport { useQueries } from \"./use_queries.js\";\nimport { PaginatedQueryResult } from \"../browser/sync/pagination.js\";\nimport { SubscribeToPaginatedQueryOptions } from \"../browser/sync/paginated_query_client.js\";\nimport { ConvexError } from \"../values/errors.js\";\nimport { useConvex } from \"./client.js\";\n\ntype UsePaginatedQueryState = {\n query: FunctionReference<\"query\">;\n args: Record<string, Value>;\n id: number;\n queries: {\n paginatedQuery?: {\n query: FunctionReference<\"query\">;\n args: Record<string, Value>;\n paginationOptions: SubscribeToPaginatedQueryOptions;\n };\n };\n skip: boolean;\n};\n\n/**\n * Experimental new usePaginatedQuery implementation that will replace the current one\n * in the future.\n *\n * Load data reactively from a paginated query to a create a growing list.\n *\n * This is an alternate implementation that relies on new client pagination logic.\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_experimental<\n Query extends PaginatedQueryReference,\n>(\n query: Query,\n args: PaginatedQueryArgs<Query> | \"skip\",\n // Future options this hook might accept:\n // - maximumRowsRead\n // - maximumBytesRead\n // - a cursor for where to start? although probably no endCursor\n options: { initialNumItems: number },\n): UsePaginatedQueryReturnType<Query>;\n\n/** @internal */\nexport function usePaginatedQuery_experimental<\n Query extends PaginatedQueryReference,\n>(\n options: UsePaginatedQueryOptions<Query>,\n): UsePaginatedQueryObjectReturnType<Query>;\n\nexport function usePaginatedQuery_experimental<\n Query extends PaginatedQueryReference,\n>(\n queryOrOptions: Query | UsePaginatedQueryOptions<Query>,\n args?: PaginatedQueryArgs<Query> | \"skip\",\n // Future options this hook might accept:\n // - maximumRowsRead\n // - maximumBytesRead\n // - a cursor for where to start? although probably no endCursor\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 if (\n typeof initialOptions?.initialNumItems !== \"number\" ||\n initialOptions.initialNumItems < 0\n ) {\n throw new Error(\n `\\`options.initialNumItems\\` must be a positive number. Received \\`${initialOptions?.initialNumItems}\\`.`,\n );\n }\n const skip = queryArgs === \"skip\";\n const argsObject = skip ? {} : queryArgs;\n\n const convexClient = useConvex();\n const logger = convexClient.logger;\n\n // The identity of createInitialState changes each time!\n const createInitialState: () => UsePaginatedQueryState = () => {\n const id = nextPaginationId();\n return {\n query,\n args: argsObject as Record<string, Value>,\n id,\n // Queries will contain zero or one queries forever.\n queries: skip\n ? ({} as UsePaginatedQueryState[\"queries\"])\n : {\n paginatedQuery: {\n query,\n args: {\n ...argsObject,\n },\n paginationOptions: {\n initialNumItems: initialOptions.initialNumItems,\n id,\n },\n },\n },\n skip,\n };\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 // New function, args, or skip? New paginated query!\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 // currState.queries is just a single query; we use useQueries\n // because it's the lower-level ook sthat supports pagination options.\n const resultsObject = useQueries(currState.queries);\n\n // skip\n if (!(\"paginatedQuery\" in resultsObject)) {\n if (!skip) {\n throw new Error(\"Why is it missing?\");\n }\n const internalResult = {\n results: [] as Query[\"_returnType\"][\"page\"],\n status: \"LoadingFirstPage\" as const,\n isLoading: true as const,\n loadMore: function skipNOP(_numItems: number) {\n return false;\n },\n };\n if (isObjectOptions) {\n return reshapeToObjectForm2(\n internalResult,\n ) as unknown as UsePaginatedQueryObjectReturnType<Query>;\n }\n return internalResult as unknown as UsePaginatedQueryReturnType<Query>;\n }\n const result = resultsObject.paginatedQuery as\n | PaginatedQueryResult<Query[\"_returnType\"][\"page\"][number]>\n | Error;\n\n // TODO this is a weird mix of responsibilities:\n // - is it the hook's job to render the initial loading state?\n // - or is it the paginated query's job to render the approproate loading state?\n // It comes back to why we'd ever get undefined when asking about a query; have we not yet called subscribe for it?\n if (result === undefined) {\n const internalResult = {\n results: [] as Query[\"_returnType\"][\"page\"],\n loadMore: () => false,\n isLoading: true as const,\n status: \"LoadingFirstPage\" as const,\n };\n if (isObjectOptions) {\n return reshapeToObjectForm2(\n internalResult,\n ) as unknown as UsePaginatedQueryObjectReturnType<Query>;\n }\n return internalResult as unknown as UsePaginatedQueryReturnType<Query>;\n }\n\n if (result instanceof Error) {\n if (\n result.message.includes(\"InvalidCursor\") ||\n (result instanceof ConvexError &&\n typeof result.data === \"object\" &&\n result.data?.isConvexSystemError === true &&\n result.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 result.message,\n );\n setState(createInitialState);\n const internalResult = {\n results: [] as Query[\"_returnType\"][\"page\"],\n loadMore: () => false,\n isLoading: true as const,\n status: \"LoadingFirstPage\" as const,\n };\n if (isObjectOptions) {\n return reshapeToObjectForm2(\n internalResult,\n ) as unknown as UsePaginatedQueryObjectReturnType<Query>;\n }\n return internalResult as unknown as UsePaginatedQueryReturnType<Query>;\n } else {\n if (throwOnError) {\n throw result;\n }\n const internalResult = {\n results: [] as Query[\"_returnType\"][\"page\"],\n loadMore: () => false,\n isLoading: false as const,\n status: \"Error\" as const,\n error: result,\n };\n if (isObjectOptions) {\n return reshapeToObjectForm2(\n internalResult,\n ) as unknown as UsePaginatedQueryObjectReturnType<Query>;\n }\n return internalResult as unknown as UsePaginatedQueryReturnType<Query>;\n }\n }\n\n const internalResult = {\n ...result,\n loadMore: (num: number) => {\n return result.loadMore(num);\n },\n isLoading:\n result.status === \"LoadingFirstPage\"\n ? (true as const)\n : result.status === \"LoadingMore\"\n ? (true as const)\n : (false as const),\n };\n if (isObjectOptions) {\n return reshapeToObjectForm2(\n internalResult,\n ) as unknown as UsePaginatedQueryObjectReturnType<Query>;\n }\n return internalResult as unknown as UsePaginatedQueryReturnType<Query>;\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 reshapeToObjectForm2<Item>(internal: {\n results: Item[];\n status: string;\n isLoading: boolean;\n loadMore: (...args: any[]) => any;\n error?: Error;\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 * See ./use_paginated_query for the purpose, but we may be able to get rid of this soon.\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"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { useState } from \"react\";\n\nimport { FunctionReference, getFunctionName } from \"../server/api.js\";\nimport {\n PaginatedQueryReference,\n PaginatedQueryArgs,\n PaginatedQueryItem,\n UsePaginatedQueryReturnType,\n} from \"./use_paginated_query.js\";\nimport { convexToJson, Value } from \"../values/value.js\";\nimport { useQueries } from \"./use_queries.js\";\nimport { PaginatedQueryResult } from \"../browser/sync/pagination.js\";\nimport { SubscribeToPaginatedQueryOptions } from \"../browser/sync/paginated_query_client.js\";\nimport { ConvexError } from \"../values/errors.js\";\nimport { useConvex } from \"./client.js\";\n\n/**\n * Options for object-form {@link usePaginatedQuery_experimental}.\n *\n * @public\n */\nexport type UsePaginatedQueryOptions<\n Query extends PaginatedQueryReference,\n ThrowOnError extends boolean = false,\n> = {\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?: ThrowOnError;\n};\n\n/**\n * Return type of the object-form {@link usePaginatedQuery_experimental} 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 * @public\n */\nexport type UsePaginatedQueryObjectReturnType<\n Query extends PaginatedQueryReference,\n ThrowOnError extends boolean = false,\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 | (ThrowOnError extends true\n ? never\n : {\n data: PaginatedQueryItem<Query>[];\n status: \"error\";\n canLoadMore: false;\n isLoading: false;\n error: Error;\n loadMore: (numItems: number) => void;\n });\n\ntype UsePaginatedQueryState = {\n query: FunctionReference<\"query\">;\n args: Record<string, Value>;\n id: number;\n queries: {\n paginatedQuery?: {\n query: FunctionReference<\"query\">;\n args: Record<string, Value>;\n paginationOptions: SubscribeToPaginatedQueryOptions;\n };\n };\n skip: boolean;\n};\n\n/**\n * Experimental new usePaginatedQuery implementation that will replace the current one\n * in the future.\n *\n * Load data reactively from a paginated query to a create a growing list.\n *\n * This is an alternate implementation that relies on new client pagination logic.\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_experimental<\n Query extends PaginatedQueryReference,\n>(\n query: Query,\n args: PaginatedQueryArgs<Query> | \"skip\",\n // Future options this hook might accept:\n // - maximumRowsRead\n // - maximumBytesRead\n // - a cursor for where to start? although probably no endCursor\n options: { initialNumItems: number },\n): UsePaginatedQueryReturnType<Query>;\n\n/**\n * Experimental new usePaginatedQuery implementation that accepts an options object\n * rather than positional arguments.\n *\n * @param options - A {@link UsePaginatedQueryOptions} object including `query` and `args`.\n * @returns A {@link UsePaginatedQueryObjectReturnType} object with `data`, `status`,\n * `canLoadMore`, `isLoading`, `error`, and `loadMore`. `status` is `\"pending\"` while\n * loading, `\"success\"` when data is available, or `\"error\"` if the query threw.\n * When `throwOnError` is `true`, the `\"error\"` status is excluded from the return\n * type since errors will be thrown instead.\n * `canLoadMore` is `true` only when idle and more pages exist.\n *\n * @public\n */\nexport function usePaginatedQuery_experimental<\n Query extends PaginatedQueryReference,\n ThrowOnError extends boolean = false,\n>(\n options: UsePaginatedQueryOptions<Query, ThrowOnError>,\n): UsePaginatedQueryObjectReturnType<Query, ThrowOnError>;\n\nexport function usePaginatedQuery_experimental<\n Query extends PaginatedQueryReference,\n>(\n queryOrOptions: Query | UsePaginatedQueryOptions<Query>,\n args?: PaginatedQueryArgs<Query> | \"skip\",\n // Future options this hook might accept:\n // - maximumRowsRead\n // - maximumBytesRead\n // - a cursor for where to start? although probably no endCursor\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 if (\n typeof initialOptions?.initialNumItems !== \"number\" ||\n initialOptions.initialNumItems < 0\n ) {\n throw new Error(\n `\\`options.initialNumItems\\` must be a positive number. Received \\`${initialOptions?.initialNumItems}\\`.`,\n );\n }\n const skip = queryArgs === \"skip\";\n const argsObject = skip ? {} : queryArgs;\n\n const convexClient = useConvex();\n const logger = convexClient.logger;\n\n // The identity of createInitialState changes each time!\n const createInitialState: () => UsePaginatedQueryState = () => {\n const id = nextPaginationId();\n return {\n query,\n args: argsObject as Record<string, Value>,\n id,\n // Queries will contain zero or one queries forever.\n queries: skip\n ? ({} as UsePaginatedQueryState[\"queries\"])\n : {\n paginatedQuery: {\n query,\n args: {\n ...argsObject,\n },\n paginationOptions: {\n initialNumItems: initialOptions.initialNumItems,\n id,\n },\n },\n },\n skip,\n };\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 // New function, args, or skip? New paginated query!\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 // currState.queries is just a single query; we use useQueries\n // because it's the lower-level ook sthat supports pagination options.\n const resultsObject = useQueries(currState.queries);\n\n // skip\n if (!(\"paginatedQuery\" in resultsObject)) {\n if (!skip) {\n throw new Error(\"Why is it missing?\");\n }\n const internalResult = {\n results: [] as Query[\"_returnType\"][\"page\"],\n status: \"LoadingFirstPage\" as const,\n isLoading: true as const,\n loadMore: function skipNOP(_numItems: number) {\n return false;\n },\n };\n if (isObjectOptions) {\n return reshapeToObjectForm2(\n internalResult,\n ) as unknown as UsePaginatedQueryObjectReturnType<Query>;\n }\n return internalResult as unknown as UsePaginatedQueryReturnType<Query>;\n }\n const result = resultsObject.paginatedQuery as\n | PaginatedQueryResult<Query[\"_returnType\"][\"page\"][number]>\n | Error;\n\n // TODO this is a weird mix of responsibilities:\n // - is it the hook's job to render the initial loading state?\n // - or is it the paginated query's job to render the approproate loading state?\n // It comes back to why we'd ever get undefined when asking about a query; have we not yet called subscribe for it?\n if (result === undefined) {\n const internalResult = {\n results: [] as Query[\"_returnType\"][\"page\"],\n loadMore: () => false,\n isLoading: true as const,\n status: \"LoadingFirstPage\" as const,\n };\n if (isObjectOptions) {\n return reshapeToObjectForm2(\n internalResult,\n ) as unknown as UsePaginatedQueryObjectReturnType<Query>;\n }\n return internalResult as unknown as UsePaginatedQueryReturnType<Query>;\n }\n\n if (result instanceof Error) {\n if (\n result.message.includes(\"InvalidCursor\") ||\n (result instanceof ConvexError &&\n typeof result.data === \"object\" &&\n result.data?.isConvexSystemError === true &&\n result.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 result.message,\n );\n setState(createInitialState);\n const internalResult = {\n results: [] as Query[\"_returnType\"][\"page\"],\n loadMore: () => false,\n isLoading: true as const,\n status: \"LoadingFirstPage\" as const,\n };\n if (isObjectOptions) {\n return reshapeToObjectForm2(\n internalResult,\n ) as unknown as UsePaginatedQueryObjectReturnType<Query>;\n }\n return internalResult as unknown as UsePaginatedQueryReturnType<Query>;\n } else {\n if (throwOnError) {\n throw result;\n }\n const internalResult = {\n results: [] as Query[\"_returnType\"][\"page\"],\n loadMore: () => false,\n isLoading: false as const,\n status: \"Error\" as const,\n error: result,\n };\n if (isObjectOptions) {\n return reshapeToObjectForm2(\n internalResult,\n ) as unknown as UsePaginatedQueryObjectReturnType<Query>;\n }\n return internalResult as unknown as UsePaginatedQueryReturnType<Query>;\n }\n }\n\n const internalResult = {\n ...result,\n loadMore: (num: number) => {\n return result.loadMore(num);\n },\n isLoading:\n result.status === \"LoadingFirstPage\"\n ? (true as const)\n : result.status === \"LoadingMore\"\n ? (true as const)\n : (false as const),\n };\n if (isObjectOptions) {\n return reshapeToObjectForm2(\n internalResult,\n ) as unknown as UsePaginatedQueryObjectReturnType<Query>;\n }\n return internalResult as unknown as UsePaginatedQueryReturnType<Query>;\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 reshapeToObjectForm2<Item>(internal: {\n results: Item[];\n status: string;\n isLoading: boolean;\n loadMore: (...args: any[]) => any;\n error?: Error;\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 * See ./use_paginated_query for the purpose, but we may be able to get rid of this soon.\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"],
5
+ "mappings": "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",
6
6
  "names": ["internalResult"]
7
7
  }
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ import { performAsyncSyscall } from "./impl/syscall.js";
3
+ import { varNames } from "./logVars.js";
4
+ function validateKey(key) {
5
+ if (key.startsWith("$")) {
6
+ throw new Error(`Audit log body keys must not start with "$": "${key}"`);
7
+ }
8
+ }
9
+ function cloneValue(value) {
10
+ if (typeof value === "symbol") {
11
+ if (!(value in varNames)) {
12
+ throw new Error(
13
+ `Unknown audit var symbol: ${String(value)}. Use one of log.var.requestId, log.var.ip, log.var.userAgent, or log.var.now.`
14
+ );
15
+ }
16
+ return { $var: varNames[value] };
17
+ }
18
+ if (value === null || value === void 0 || typeof value !== "object") {
19
+ return value;
20
+ }
21
+ if (Array.isArray(value)) {
22
+ return value.map(cloneValue);
23
+ }
24
+ const result = {};
25
+ for (const [key, val] of Object.entries(value)) {
26
+ validateKey(key);
27
+ result[key] = cloneValue(val);
28
+ }
29
+ return result;
30
+ }
31
+ export function cloneWithSentinels(body) {
32
+ const result = {};
33
+ for (const [key, val] of Object.entries(body)) {
34
+ validateKey(key);
35
+ result[key] = cloneValue(val);
36
+ }
37
+ return result;
38
+ }
39
+ export const audit = async (body) => {
40
+ await performAsyncSyscall("1.0/auditLog", {
41
+ body: cloneWithSentinels(body)
42
+ });
43
+ };
44
+ //# sourceMappingURL=audit_logging.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/server/audit_logging.ts"],
4
+ "sourcesContent": ["import { performAsyncSyscall } from \"./impl/syscall.js\";\nimport { LogVar, varNames } from \"./logVars.js\";\n\nexport type AuditLogBody = { [key: string]: AuditLogValue };\nexport type AuditLogValue =\n | null\n | undefined\n | boolean\n | number\n | string\n | LogVar\n | AuditLogValue[]\n | { [key: string]: AuditLogValue };\n\ntype JsonValue =\n | null\n | undefined\n | boolean\n | number\n | string\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nfunction validateKey(key: string) {\n if (key.startsWith(\"$\")) {\n throw new Error(`Audit log body keys must not start with \"$\": \"${key}\"`);\n }\n}\n\nfunction cloneValue(value: AuditLogValue): JsonValue {\n if (typeof value === \"symbol\") {\n if (!(value in varNames)) {\n throw new Error(\n `Unknown audit var symbol: ${String(value)}. Use one of log.var.requestId, log.var.ip, log.var.userAgent, or log.var.now.`,\n );\n }\n return { $var: varNames[value] };\n }\n if (value === null || value === undefined || typeof value !== \"object\") {\n return value;\n }\n if (Array.isArray(value)) {\n return value.map(cloneValue);\n }\n const result: { [key: string]: JsonValue } = {};\n for (const [key, val] of Object.entries(value)) {\n validateKey(key);\n result[key] = cloneValue(val);\n }\n return result;\n}\n\n/**\n * Deep-clone the body, replacing audit var symbols with sentinel objects\n * like `{ $var: \"ip\" }`.\n */\nexport function cloneWithSentinels(body: AuditLogBody): {\n [key: string]: JsonValue;\n} {\n const result: { [key: string]: JsonValue } = {};\n for (const [key, val] of Object.entries(body)) {\n validateKey(key);\n result[key] = cloneValue(val);\n }\n return result;\n}\n\n/**\n * @internal\n */\nexport const audit = async (body: AuditLogBody): Promise<void> => {\n await performAsyncSyscall(\"1.0/auditLog\", {\n body: cloneWithSentinels(body),\n });\n};\n"],
5
+ "mappings": ";AAAA,SAAS,2BAA2B;AACpC,SAAiB,gBAAgB;AAsBjC,SAAS,YAAY,KAAa;AAChC,MAAI,IAAI,WAAW,GAAG,GAAG;AACvB,UAAM,IAAI,MAAM,iDAAiD,GAAG,GAAG;AAAA,EACzE;AACF;AAEA,SAAS,WAAW,OAAiC;AACnD,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,EAAE,SAAS,WAAW;AACxB,YAAM,IAAI;AAAA,QACR,6BAA6B,OAAO,KAAK,CAAC;AAAA,MAC5C;AAAA,IACF;AACA,WAAO,EAAE,MAAM,SAAS,KAAK,EAAE;AAAA,EACjC;AACA,MAAI,UAAU,QAAQ,UAAU,UAAa,OAAO,UAAU,UAAU;AACtE,WAAO;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,UAAU;AAAA,EAC7B;AACA,QAAM,SAAuC,CAAC;AAC9C,aAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC9C,gBAAY,GAAG;AACf,WAAO,GAAG,IAAI,WAAW,GAAG;AAAA,EAC9B;AACA,SAAO;AACT;AAMO,gBAAS,mBAAmB,MAEjC;AACA,QAAM,SAAuC,CAAC;AAC9C,aAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC7C,gBAAY,GAAG;AACf,WAAO,GAAG,IAAI,WAAW,GAAG;AAAA,EAC9B;AACA,SAAO;AACT;AAKO,aAAM,QAAQ,OAAO,SAAsC;AAChE,QAAM,oBAAoB,gBAAgB;AAAA,IACxC,MAAM,mBAAmB,IAAI;AAAA,EAC/B,CAAC;AACH;",
6
+ "names": []
7
+ }
@@ -22,6 +22,25 @@ async function getFunctionMetadata() {
22
22
  );
23
23
  return { name, componentPath };
24
24
  }
25
+ async function getDeploymentMetadata() {
26
+ const syscallJSON = await performAsyncSyscall(
27
+ "1.0/getDeploymentMetadata",
28
+ {}
29
+ );
30
+ const result = jsonToConvex(syscallJSON);
31
+ return {
32
+ name: result.name,
33
+ region: result.region ?? null,
34
+ class: result.class
35
+ };
36
+ }
37
+ async function getRequestMetadata() {
38
+ const { ip, userAgent, requestId } = await performAsyncSyscall(
39
+ "1.0/getRequestMetadata",
40
+ {}
41
+ );
42
+ return { ip, userAgent, requestId };
43
+ }
25
44
  export function setupQueryMeta(visibility) {
26
45
  return {
27
46
  getFunctionMetadata: async () => ({
@@ -29,7 +48,8 @@ export function setupQueryMeta(visibility) {
29
48
  type: "query",
30
49
  visibility
31
50
  }),
32
- getTransactionMetrics
51
+ getTransactionMetrics,
52
+ getDeploymentMetadata
33
53
  };
34
54
  }
35
55
  export function setupMutationMeta(visibility) {
@@ -39,7 +59,9 @@ export function setupMutationMeta(visibility) {
39
59
  type: "mutation",
40
60
  visibility
41
61
  }),
42
- getTransactionMetrics
62
+ getTransactionMetrics,
63
+ getDeploymentMetadata,
64
+ getRequestMetadata
43
65
  };
44
66
  }
45
67
  export function setupActionMeta(visibility) {
@@ -48,7 +70,9 @@ export function setupActionMeta(visibility) {
48
70
  ...await getFunctionMetadata(),
49
71
  type: "action",
50
72
  visibility
51
- })
73
+ }),
74
+ getDeploymentMetadata,
75
+ getRequestMetadata
52
76
  };
53
77
  }
54
78
  //# sourceMappingURL=meta_impl.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/server/impl/meta_impl.ts"],
4
- "sourcesContent": ["import { jsonToConvex } from \"../../values/index.js\";\nimport {\n ActionMeta,\n MutationMeta,\n QueryMeta,\n FunctionMetadata,\n TransactionMetrics,\n} from \"../meta.js\";\nimport { performAsyncSyscall } from \"./syscall.js\";\n\nasync function getTransactionMetrics(): Promise<TransactionMetrics> {\n let syscallJSON;\n try {\n syscallJSON = await performAsyncSyscall(\"1.0/getTransactionMetrics\", {});\n } catch (e: any) {\n if (e.message?.includes(\"Unknown async operation\")) {\n throw new Error(\n \"getTransactionMetrics() can only be called from a query or mutation. \" +\n \"It is not available in actions or outside of a Convex function.\",\n );\n }\n throw e;\n }\n return jsonToConvex(syscallJSON) as any;\n}\n\nasync function getFunctionMetadata(): Promise<{\n name: string;\n componentPath: string;\n}> {\n const { name, componentPath } = await performAsyncSyscall(\n \"1.0/getFunctionMetadata\",\n {},\n );\n return { name, componentPath };\n}\n\nexport function setupQueryMeta(\n visibility: FunctionMetadata[\"visibility\"],\n): QueryMeta {\n return {\n getFunctionMetadata: async () => ({\n ...(await getFunctionMetadata()),\n type: \"query\",\n visibility,\n }),\n getTransactionMetrics,\n };\n}\n\nexport function setupMutationMeta(\n visibility: FunctionMetadata[\"visibility\"],\n): MutationMeta {\n return {\n getFunctionMetadata: async () => ({\n ...(await getFunctionMetadata()),\n type: \"mutation\",\n visibility,\n }),\n getTransactionMetrics,\n };\n}\n\nexport function setupActionMeta(\n visibility: FunctionMetadata[\"visibility\"],\n): ActionMeta {\n return {\n getFunctionMetadata: async () => ({\n ...(await getFunctionMetadata()),\n type: \"action\",\n visibility,\n }),\n };\n}\n"],
5
- "mappings": ";AAAA,SAAS,oBAAoB;AAQ7B,SAAS,2BAA2B;AAEpC,eAAe,wBAAqD;AAClE,MAAI;AACJ,MAAI;AACF,kBAAc,MAAM,oBAAoB,6BAA6B,CAAC,CAAC;AAAA,EACzE,SAAS,GAAQ;AACf,QAAI,EAAE,SAAS,SAAS,yBAAyB,GAAG;AAClD,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACA,SAAO,aAAa,WAAW;AACjC;AAEA,eAAe,sBAGZ;AACD,QAAM,EAAE,MAAM,cAAc,IAAI,MAAM;AAAA,IACpC;AAAA,IACA,CAAC;AAAA,EACH;AACA,SAAO,EAAE,MAAM,cAAc;AAC/B;AAEO,gBAAS,eACd,YACW;AACX,SAAO;AAAA,IACL,qBAAqB,aAAa;AAAA,MAChC,GAAI,MAAM,oBAAoB;AAAA,MAC9B,MAAM;AAAA,MACN;AAAA,IACF;AAAA,IACA;AAAA,EACF;AACF;AAEO,gBAAS,kBACd,YACc;AACd,SAAO;AAAA,IACL,qBAAqB,aAAa;AAAA,MAChC,GAAI,MAAM,oBAAoB;AAAA,MAC9B,MAAM;AAAA,MACN;AAAA,IACF;AAAA,IACA;AAAA,EACF;AACF;AAEO,gBAAS,gBACd,YACY;AACZ,SAAO;AAAA,IACL,qBAAqB,aAAa;AAAA,MAChC,GAAI,MAAM,oBAAoB;AAAA,MAC9B,MAAM;AAAA,MACN;AAAA,IACF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { jsonToConvex } from \"../../values/index.js\";\nimport {\n ActionMeta,\n MutationMeta,\n QueryMeta,\n RequestMetadata,\n FunctionMetadata,\n TransactionMetrics,\n DeploymentMetadata,\n} from \"../meta.js\";\nimport { performAsyncSyscall } from \"./syscall.js\";\n\nasync function getTransactionMetrics(): Promise<TransactionMetrics> {\n let syscallJSON;\n try {\n syscallJSON = await performAsyncSyscall(\"1.0/getTransactionMetrics\", {});\n } catch (e: any) {\n if (e.message?.includes(\"Unknown async operation\")) {\n throw new Error(\n \"getTransactionMetrics() can only be called from a query or mutation. \" +\n \"It is not available in actions or outside of a Convex function.\",\n );\n }\n throw e;\n }\n return jsonToConvex(syscallJSON) as any;\n}\n\nasync function getFunctionMetadata(): Promise<{\n name: string;\n componentPath: string;\n}> {\n const { name, componentPath } = await performAsyncSyscall(\n \"1.0/getFunctionMetadata\",\n {},\n );\n return { name, componentPath };\n}\n\nasync function getDeploymentMetadata(): Promise<DeploymentMetadata> {\n const syscallJSON = await performAsyncSyscall(\n \"1.0/getDeploymentMetadata\",\n {},\n );\n const result = jsonToConvex(syscallJSON) as any;\n return {\n name: result.name,\n region: result.region ?? null,\n class: result.class,\n };\n}\n\nasync function getRequestMetadata(): Promise<RequestMetadata> {\n const { ip, userAgent, requestId } = await performAsyncSyscall(\n \"1.0/getRequestMetadata\",\n {},\n );\n return { ip, userAgent, requestId };\n}\n\nexport function setupQueryMeta(\n visibility: FunctionMetadata[\"visibility\"],\n): QueryMeta {\n return {\n getFunctionMetadata: async () => ({\n ...(await getFunctionMetadata()),\n type: \"query\",\n visibility,\n }),\n getTransactionMetrics,\n getDeploymentMetadata,\n };\n}\n\nexport function setupMutationMeta(\n visibility: FunctionMetadata[\"visibility\"],\n): MutationMeta {\n return {\n getFunctionMetadata: async () => ({\n ...(await getFunctionMetadata()),\n type: \"mutation\",\n visibility,\n }),\n getTransactionMetrics,\n getDeploymentMetadata,\n getRequestMetadata,\n };\n}\n\nexport function setupActionMeta(\n visibility: FunctionMetadata[\"visibility\"],\n): ActionMeta {\n return {\n getFunctionMetadata: async () => ({\n ...(await getFunctionMetadata()),\n type: \"action\",\n visibility,\n }),\n getDeploymentMetadata,\n getRequestMetadata,\n };\n}\n"],
5
+ "mappings": ";AAAA,SAAS,oBAAoB;AAU7B,SAAS,2BAA2B;AAEpC,eAAe,wBAAqD;AAClE,MAAI;AACJ,MAAI;AACF,kBAAc,MAAM,oBAAoB,6BAA6B,CAAC,CAAC;AAAA,EACzE,SAAS,GAAQ;AACf,QAAI,EAAE,SAAS,SAAS,yBAAyB,GAAG;AAClD,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACA,SAAO,aAAa,WAAW;AACjC;AAEA,eAAe,sBAGZ;AACD,QAAM,EAAE,MAAM,cAAc,IAAI,MAAM;AAAA,IACpC;AAAA,IACA,CAAC;AAAA,EACH;AACA,SAAO,EAAE,MAAM,cAAc;AAC/B;AAEA,eAAe,wBAAqD;AAClE,QAAM,cAAc,MAAM;AAAA,IACxB;AAAA,IACA,CAAC;AAAA,EACH;AACA,QAAM,SAAS,aAAa,WAAW;AACvC,SAAO;AAAA,IACL,MAAM,OAAO;AAAA,IACb,QAAQ,OAAO,UAAU;AAAA,IACzB,OAAO,OAAO;AAAA,EAChB;AACF;AAEA,eAAe,qBAA+C;AAC5D,QAAM,EAAE,IAAI,WAAW,UAAU,IAAI,MAAM;AAAA,IACzC;AAAA,IACA,CAAC;AAAA,EACH;AACA,SAAO,EAAE,IAAI,WAAW,UAAU;AACpC;AAEO,gBAAS,eACd,YACW;AACX,SAAO;AAAA,IACL,qBAAqB,aAAa;AAAA,MAChC,GAAI,MAAM,oBAAoB;AAAA,MAC9B,MAAM;AAAA,MACN;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEO,gBAAS,kBACd,YACc;AACd,SAAO;AAAA,IACL,qBAAqB,aAAa;AAAA,MAChC,GAAI,MAAM,oBAAoB;AAAA,MAC9B,MAAM;AAAA,MACN;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEO,gBAAS,gBACd,YACY;AACZ,SAAO;AAAA,IACL,qBAAqB,aAAa;AAAA,MAChC,GAAI,MAAM,oBAAoB;AAAA,MAC9B,MAAM;AAAA,MACN;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -183,6 +183,7 @@ export const internalQueryGeneric = ((functionDefinition) => {
183
183
  return func;
184
184
  });
185
185
  async function invokeAction(func, requestId, argsStr, visibility) {
186
+ globalThis.Convex?.setupPerformance?.();
186
187
  const args = jsonToConvex(JSON.parse(argsStr));
187
188
  const calls = setupActionCalls(requestId);
188
189
  const ctx = {
@@ -221,6 +222,7 @@ export const internalActionGeneric = ((functionDefinition) => {
221
222
  return func;
222
223
  });
223
224
  async function invokeHttpAction(func, request) {
225
+ globalThis.Convex?.setupPerformance?.();
224
226
  const requestId = "";
225
227
  const calls = setupActionCalls(requestId);
226
228
  const ctx = {