convex 1.42.3 → 1.43.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/bin/main-dev +6 -1
  2. package/dist/browser.bundle.js +49 -10
  3. package/dist/browser.bundle.js.map +2 -2
  4. package/dist/cjs/browser/simple_client-node.js +35 -35
  5. package/dist/cjs/browser/simple_client-node.js.map +2 -2
  6. package/dist/cjs/browser/sync/paginated_query_client.js +20 -9
  7. package/dist/cjs/browser/sync/paginated_query_client.js.map +2 -2
  8. package/dist/cjs/cli/codegen_templates/validator_helpers.js +2 -0
  9. package/dist/cjs/cli/codegen_templates/validator_helpers.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/deploymentCreate.js.map +2 -2
  13. package/dist/cjs/cli/generatedApi.js.map +1 -1
  14. package/dist/cjs/cli/lib/deployApi/validator.js +1 -0
  15. package/dist/cjs/cli/lib/deployApi/validator.js.map +2 -2
  16. package/dist/cjs/cli/lib/deploymentSelection.js.map +2 -2
  17. package/dist/cjs/cli/lib/generateDocs.js +1 -1
  18. package/dist/cjs/cli/lib/generateDocs.js.map +2 -2
  19. package/dist/cjs/cli/lib/generatedFunctionLogsApi.js.map +1 -1
  20. package/dist/cjs/cli/lib/localDeployment/projectMismatch.js.map +2 -2
  21. package/dist/cjs/cli/lib/login.js +23 -23
  22. package/dist/cjs/cli/lib/login.js.map +2 -2
  23. package/dist/cjs/cli/lib/typecheck.js +28 -25
  24. package/dist/cjs/cli/lib/typecheck.js.map +2 -2
  25. package/dist/cjs/cli/lib/usageLimits.js +164 -0
  26. package/dist/cjs/cli/lib/usageLimits.js.map +7 -0
  27. package/dist/cjs/cli/usageLimits.js +326 -0
  28. package/dist/cjs/cli/usageLimits.js.map +7 -0
  29. package/dist/cjs/index.js +1 -1
  30. package/dist/cjs/index.js.map +1 -1
  31. package/dist/cjs/server/components/index.js.map +2 -2
  32. package/dist/cjs/server/cron.js +33 -29
  33. package/dist/cjs/server/cron.js.map +2 -2
  34. package/dist/cjs/server/database.js.map +1 -1
  35. package/dist/cjs/server/impl/database_impl.js +3 -0
  36. package/dist/cjs/server/impl/database_impl.js.map +2 -2
  37. package/dist/cjs/server/log.js +2 -1
  38. package/dist/cjs/server/log.js.map +2 -2
  39. package/dist/cjs/server/logVars.js +12 -1
  40. package/dist/cjs/server/logVars.js.map +2 -2
  41. package/dist/cjs/values/compare.js +5 -1
  42. package/dist/cjs/values/compare.js.map +2 -2
  43. package/dist/cjs/values/index.js +1 -0
  44. package/dist/cjs/values/index.js.map +2 -2
  45. package/dist/cjs/values/size.js +4 -0
  46. package/dist/cjs/values/size.js.map +2 -2
  47. package/dist/cjs/values/validator.js +11 -0
  48. package/dist/cjs/values/validator.js.map +2 -2
  49. package/dist/cjs/values/validators.js +20 -0
  50. package/dist/cjs/values/validators.js.map +2 -2
  51. package/dist/cjs/values/value.js +31 -0
  52. package/dist/cjs/values/value.js.map +2 -2
  53. package/dist/cjs-types/cli/codegen_templates/validator_helpers.d.ts.map +1 -1
  54. package/dist/cjs-types/cli/codegen_templates/validator_helpers.test.d.ts +2 -0
  55. package/dist/cjs-types/cli/codegen_templates/validator_helpers.test.d.ts.map +1 -0
  56. package/dist/cjs-types/cli/deployment.d.ts.map +1 -1
  57. package/dist/cjs-types/cli/deploymentCreate.d.ts +2 -2
  58. package/dist/cjs-types/cli/generatedApi.d.ts +43 -43
  59. package/dist/cjs-types/cli/generatedApi.d.ts.map +1 -1
  60. package/dist/cjs-types/cli/lib/deployApi/validator.d.ts +6 -0
  61. package/dist/cjs-types/cli/lib/deployApi/validator.d.ts.map +1 -1
  62. package/dist/cjs-types/cli/lib/deploymentSelection.d.ts +2 -2
  63. package/dist/cjs-types/cli/lib/deploymentSelection.d.ts.map +1 -1
  64. package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts +45 -45
  65. package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
  66. package/dist/cjs-types/cli/lib/localDeployment/projectMismatch.d.ts +2 -2
  67. package/dist/cjs-types/cli/lib/localDeployment/projectMismatch.d.ts.map +1 -1
  68. package/dist/cjs-types/cli/lib/login.d.ts.map +1 -1
  69. package/dist/cjs-types/cli/lib/mcp/tools/insights.d.ts +24 -24
  70. package/dist/cjs-types/cli/lib/typecheck.d.ts +2 -0
  71. package/dist/cjs-types/cli/lib/typecheck.d.ts.map +1 -1
  72. package/dist/cjs-types/cli/lib/typecheck.test.d.ts +2 -0
  73. package/dist/cjs-types/cli/lib/typecheck.test.d.ts.map +1 -0
  74. package/dist/cjs-types/cli/lib/usageLimits.d.ts +46 -0
  75. package/dist/cjs-types/cli/lib/usageLimits.d.ts.map +1 -0
  76. package/dist/cjs-types/cli/usageLimits.d.ts +22 -0
  77. package/dist/cjs-types/cli/usageLimits.d.ts.map +1 -0
  78. package/dist/cjs-types/index.d.ts +1 -1
  79. package/dist/cjs-types/index.d.ts.map +1 -1
  80. package/dist/cjs-types/server/components/index.d.ts +26 -8
  81. package/dist/cjs-types/server/components/index.d.ts.map +1 -1
  82. package/dist/cjs-types/server/cron.d.ts +20 -11
  83. package/dist/cjs-types/server/cron.d.ts.map +1 -1
  84. package/dist/cjs-types/server/cron.test.d.ts +2 -0
  85. package/dist/cjs-types/server/cron.test.d.ts.map +1 -0
  86. package/dist/cjs-types/server/database.d.ts.map +1 -1
  87. package/dist/cjs-types/server/impl/database_impl.d.ts.map +1 -1
  88. package/dist/cjs-types/server/log.d.ts.map +1 -1
  89. package/dist/cjs-types/server/logVars.d.ts +14 -1
  90. package/dist/cjs-types/server/logVars.d.ts.map +1 -1
  91. package/dist/cjs-types/values/compare.d.ts.map +1 -1
  92. package/dist/cjs-types/values/index.d.ts +2 -2
  93. package/dist/cjs-types/values/index.d.ts.map +1 -1
  94. package/dist/cjs-types/values/size.d.ts +1 -1
  95. package/dist/cjs-types/values/size.d.ts.map +1 -1
  96. package/dist/cjs-types/values/validator.d.ts +10 -1
  97. package/dist/cjs-types/values/validator.d.ts.map +1 -1
  98. package/dist/cjs-types/values/validators.d.ts +14 -3
  99. package/dist/cjs-types/values/validators.d.ts.map +1 -1
  100. package/dist/cjs-types/values/value.d.ts +18 -1
  101. package/dist/cjs-types/values/value.d.ts.map +1 -1
  102. package/dist/cli.bundle.cjs +2325 -1864
  103. package/dist/cli.bundle.cjs.map +4 -4
  104. package/dist/esm/browser/simple_client-node.js +35 -35
  105. package/dist/esm/browser/simple_client-node.js.map +1 -1
  106. package/dist/esm/browser/sync/paginated_query_client.js +20 -9
  107. package/dist/esm/browser/sync/paginated_query_client.js.map +2 -2
  108. package/dist/esm/cli/codegen_templates/validator_helpers.js +2 -0
  109. package/dist/esm/cli/codegen_templates/validator_helpers.js.map +2 -2
  110. package/dist/esm/cli/deployment.js +2 -1
  111. package/dist/esm/cli/deployment.js.map +2 -2
  112. package/dist/esm/cli/deploymentCreate.js.map +2 -2
  113. package/dist/esm/cli/lib/deployApi/validator.js +1 -0
  114. package/dist/esm/cli/lib/deployApi/validator.js.map +2 -2
  115. package/dist/esm/cli/lib/deploymentSelection.js.map +2 -2
  116. package/dist/esm/cli/lib/generateDocs.js +1 -1
  117. package/dist/esm/cli/lib/generateDocs.js.map +2 -2
  118. package/dist/esm/cli/lib/localDeployment/projectMismatch.js.map +2 -2
  119. package/dist/esm/cli/lib/login.js +23 -23
  120. package/dist/esm/cli/lib/login.js.map +2 -2
  121. package/dist/esm/cli/lib/typecheck.js +27 -25
  122. package/dist/esm/cli/lib/typecheck.js.map +2 -2
  123. package/dist/esm/cli/lib/usageLimits.js +129 -0
  124. package/dist/esm/cli/lib/usageLimits.js.map +7 -0
  125. package/dist/esm/cli/usageLimits.js +316 -0
  126. package/dist/esm/cli/usageLimits.js.map +7 -0
  127. package/dist/esm/index.js +1 -1
  128. package/dist/esm/index.js.map +1 -1
  129. package/dist/esm/server/components/index.js.map +2 -2
  130. package/dist/esm/server/cron.js +33 -29
  131. package/dist/esm/server/cron.js.map +2 -2
  132. package/dist/esm/server/impl/database_impl.js +4 -1
  133. package/dist/esm/server/impl/database_impl.js.map +2 -2
  134. package/dist/esm/server/log.js +3 -2
  135. package/dist/esm/server/log.js.map +2 -2
  136. package/dist/esm/server/logVars.js +11 -1
  137. package/dist/esm/server/logVars.js.map +2 -2
  138. package/dist/esm/values/compare.js +5 -1
  139. package/dist/esm/values/compare.js.map +2 -2
  140. package/dist/esm/values/index.js +1 -1
  141. package/dist/esm/values/index.js.map +2 -2
  142. package/dist/esm/values/size.js +4 -0
  143. package/dist/esm/values/size.js.map +2 -2
  144. package/dist/esm/values/validator.js +12 -0
  145. package/dist/esm/values/validator.js.map +2 -2
  146. package/dist/esm/values/validators.js +19 -0
  147. package/dist/esm/values/validators.js.map +2 -2
  148. package/dist/esm/values/value.js +29 -1
  149. package/dist/esm/values/value.js.map +2 -2
  150. package/dist/esm-types/cli/codegen_templates/validator_helpers.d.ts.map +1 -1
  151. package/dist/esm-types/cli/codegen_templates/validator_helpers.test.d.ts +2 -0
  152. package/dist/esm-types/cli/codegen_templates/validator_helpers.test.d.ts.map +1 -0
  153. package/dist/esm-types/cli/deployment.d.ts.map +1 -1
  154. package/dist/esm-types/cli/deploymentCreate.d.ts +2 -2
  155. package/dist/esm-types/cli/generatedApi.d.ts +43 -43
  156. package/dist/esm-types/cli/generatedApi.d.ts.map +1 -1
  157. package/dist/esm-types/cli/lib/deployApi/validator.d.ts +6 -0
  158. package/dist/esm-types/cli/lib/deployApi/validator.d.ts.map +1 -1
  159. package/dist/esm-types/cli/lib/deploymentSelection.d.ts +2 -2
  160. package/dist/esm-types/cli/lib/deploymentSelection.d.ts.map +1 -1
  161. package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts +45 -45
  162. package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
  163. package/dist/esm-types/cli/lib/localDeployment/projectMismatch.d.ts +2 -2
  164. package/dist/esm-types/cli/lib/localDeployment/projectMismatch.d.ts.map +1 -1
  165. package/dist/esm-types/cli/lib/login.d.ts.map +1 -1
  166. package/dist/esm-types/cli/lib/mcp/tools/insights.d.ts +24 -24
  167. package/dist/esm-types/cli/lib/typecheck.d.ts +2 -0
  168. package/dist/esm-types/cli/lib/typecheck.d.ts.map +1 -1
  169. package/dist/esm-types/cli/lib/typecheck.test.d.ts +2 -0
  170. package/dist/esm-types/cli/lib/typecheck.test.d.ts.map +1 -0
  171. package/dist/esm-types/cli/lib/usageLimits.d.ts +46 -0
  172. package/dist/esm-types/cli/lib/usageLimits.d.ts.map +1 -0
  173. package/dist/esm-types/cli/usageLimits.d.ts +22 -0
  174. package/dist/esm-types/cli/usageLimits.d.ts.map +1 -0
  175. package/dist/esm-types/index.d.ts +1 -1
  176. package/dist/esm-types/index.d.ts.map +1 -1
  177. package/dist/esm-types/server/components/index.d.ts +26 -8
  178. package/dist/esm-types/server/components/index.d.ts.map +1 -1
  179. package/dist/esm-types/server/cron.d.ts +20 -11
  180. package/dist/esm-types/server/cron.d.ts.map +1 -1
  181. package/dist/esm-types/server/cron.test.d.ts +2 -0
  182. package/dist/esm-types/server/cron.test.d.ts.map +1 -0
  183. package/dist/esm-types/server/database.d.ts.map +1 -1
  184. package/dist/esm-types/server/impl/database_impl.d.ts.map +1 -1
  185. package/dist/esm-types/server/log.d.ts.map +1 -1
  186. package/dist/esm-types/server/logVars.d.ts +14 -1
  187. package/dist/esm-types/server/logVars.d.ts.map +1 -1
  188. package/dist/esm-types/values/compare.d.ts.map +1 -1
  189. package/dist/esm-types/values/index.d.ts +2 -2
  190. package/dist/esm-types/values/index.d.ts.map +1 -1
  191. package/dist/esm-types/values/size.d.ts +1 -1
  192. package/dist/esm-types/values/size.d.ts.map +1 -1
  193. package/dist/esm-types/values/validator.d.ts +10 -1
  194. package/dist/esm-types/values/validator.d.ts.map +1 -1
  195. package/dist/esm-types/values/validators.d.ts +14 -3
  196. package/dist/esm-types/values/validators.d.ts.map +1 -1
  197. package/dist/esm-types/values/value.d.ts +18 -1
  198. package/dist/esm-types/values/value.d.ts.map +1 -1
  199. package/dist/react.bundle.js +53 -11
  200. package/dist/react.bundle.js.map +2 -2
  201. package/package.json +8 -8
  202. package/schemas/convex.schema.json +2 -1
  203. package/src/browser/sync/paginated_query_client.test.ts +209 -1
  204. package/src/browser/sync/paginated_query_client.ts +26 -9
  205. package/src/cli/codegen_templates/validator_helpers.test.ts +31 -0
  206. package/src/cli/codegen_templates/validator_helpers.ts +6 -0
  207. package/src/cli/deployment.ts +4 -1
  208. package/src/cli/deploymentCreate.test.ts +2 -2
  209. package/src/cli/deploymentCreate.ts +2 -2
  210. package/src/cli/generatedApi.ts +43 -43
  211. package/src/cli/lib/deployApi/validator.ts +1 -0
  212. package/src/cli/lib/deploymentSelection.ts +2 -2
  213. package/src/cli/lib/generateDocs.test.ts +1 -1
  214. package/src/cli/lib/generateDocs.ts +3 -3
  215. package/src/cli/lib/generatedFunctionLogsApi.ts +45 -45
  216. package/src/cli/lib/localDeployment/projectMismatch.ts +2 -2
  217. package/src/cli/lib/login.ts +33 -30
  218. package/src/cli/lib/typecheck.test.ts +71 -0
  219. package/src/cli/lib/typecheck.ts +33 -27
  220. package/src/cli/lib/usageLimits.ts +242 -0
  221. package/src/cli/usageLimits.ts +420 -0
  222. package/src/index.ts +1 -1
  223. package/src/server/audit_logging.test.ts +2 -0
  224. package/src/server/components/index.test.ts +33 -1
  225. package/src/server/components/index.ts +41 -14
  226. package/src/server/cron.test.ts +58 -0
  227. package/src/server/cron.ts +82 -21
  228. package/src/server/database.ts +17 -0
  229. package/src/server/impl/database_impl.ts +4 -1
  230. package/src/server/log.ts +4 -1
  231. package/src/server/logVars.ts +17 -1
  232. package/src/server/schema.test.ts +6 -1
  233. package/src/values/compare.ts +5 -2
  234. package/src/values/index.ts +2 -1
  235. package/src/values/size.test.ts +5 -0
  236. package/src/values/size.ts +5 -1
  237. package/src/values/validator.test.ts +16 -1
  238. package/src/values/validator.ts +13 -0
  239. package/src/values/validators.ts +29 -1
  240. package/src/values/value.test.ts +35 -0
  241. package/src/values/value.ts +56 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "convex",
3
3
  "description": "Client for the Convex Cloud",
4
- "version": "1.42.3",
4
+ "version": "1.43.0-alpha.0",
5
5
  "author": "Convex, Inc. <no-reply@convex.dev>",
6
6
  "homepage": "https://convex.dev",
7
7
  "repository": {
@@ -204,10 +204,15 @@
204
204
  "@babel/types": "^7.27.1",
205
205
  "@clerk/react": "^6.4.3",
206
206
  "@commander-js/extra-typings": "^14.0.0",
207
- "@convex-dev/platform": "0.1.9",
207
+ "@convex-dev/platform": "0.1.10",
208
208
  "@eslint/compat": "~2.0.0",
209
209
  "@eslint/eslintrc": "^3",
210
210
  "@eslint/js": "~9.39.0",
211
+ "@inquirer/confirm": "^6.0.4",
212
+ "@inquirer/input": "^5.0.4",
213
+ "@inquirer/search": "^4.1.0",
214
+ "@inquirer/select": "^5.0.4",
215
+ "@inquirer/testing": "^3.3.0",
211
216
  "@modelcontextprotocol/sdk": "^1.25.2",
212
217
  "@octokit/openapi-types": "~27.0.0",
213
218
  "@sentry/node": "^7.23.0",
@@ -217,11 +222,6 @@
217
222
  "@types/adm-zip": "^0.5.7",
218
223
  "@types/babel__code-frame": "^7.0.6",
219
224
  "@types/deep-equal": "1.0.4",
220
- "@inquirer/confirm": "^6.0.4",
221
- "@inquirer/input": "^5.0.4",
222
- "@inquirer/search": "^4.1.0",
223
- "@inquirer/select": "^5.0.4",
224
- "@inquirer/testing": "^3.3.0",
225
225
  "@types/node": "^18.17.0",
226
226
  "@types/react": "^18.0.0",
227
227
  "@types/react-dom": "^18.0.0",
@@ -271,8 +271,8 @@
271
271
  "typedoc": "^0.24.6",
272
272
  "typescript": "~5.0.3",
273
273
  "typescript-eslint": "~8.57.0",
274
- "vitest": "^4.1.0",
275
274
  "undici": "7.28.0",
275
+ "vitest": "^4.1.0",
276
276
  "wait-for-expect": "~4.0.0",
277
277
  "zod": "^3.24.0",
278
278
  "zod-to-json-schema": "^3.24.0"
@@ -180,7 +180,8 @@
180
180
  "typescriptCompiler": {
181
181
  "type": "string",
182
182
  "enum": ["tsc", "tsgo"],
183
- "description": "TypeScript compiler to use for typechecking (`@typescript/native-preview` must be installed to use `tsgo`)"
183
+ "description": "Deprecated. Set to `tsgo` to use the TypeScript Native Preview compiler (`@typescript/native-preview`). TypeScript 7 uses the native compiler without needing this setting to be set.",
184
+ "deprecated": true
184
185
  },
185
186
  "authKit": {
186
187
  "$ref": "#/definitions/authKitConfig"
@@ -1,17 +1,150 @@
1
1
  import { test, expect, describe } from "vitest";
2
2
 
3
3
  import { BaseConvexClient } from "./client.js";
4
- import { QuerySetModification } from "./protocol.js";
4
+ import {
5
+ ClientMessage,
6
+ QuerySetModification,
7
+ WireServerMessage,
8
+ } from "./protocol.js";
5
9
  import {
6
10
  nodeWebSocket,
7
11
  UpdateQueue,
8
12
  withInMemoryWebSocket,
9
13
  } from "./client_node_test_helpers.js";
10
14
  import { PaginationOptions } from "../../server/pagination.js";
15
+ import { JSONValue } from "../../values/index.js";
11
16
  import { Long } from "../../vendor/long.js";
12
17
  import { anyApi } from "../../server/index.js";
13
18
  import { PaginatedQueryClient } from "./paginated_query_client.js";
14
19
 
20
+ /**
21
+ * A faithful in-memory pagination backend for the split test below. It answers
22
+ * each page query by slicing `data` according to the `cursor`/`endCursor` it
23
+ * actually receives, so the test can assert on the pages the client ends up
24
+ * with rather than on the arguments the client happened to send. Cursors are
25
+ * opaque to callers: internally they encode an offset into `data`.
26
+ */
27
+ class FakePaginationServer {
28
+ // The client only requires each transition's startVersion to equal the
29
+ // previous endVersion; the querySet/ts numbers are otherwise unconstrained.
30
+ private querySetVersion = 0;
31
+ private ts = 0;
32
+
33
+ constructor(
34
+ private io: {
35
+ receive: () => Promise<ClientMessage>;
36
+ send: (message: WireServerMessage) => void;
37
+ },
38
+ private data: JSONValue[],
39
+ // A freshly loaded page (one requested without an endCursor) comes back with
40
+ // a `splitCursor` and `SplitRecommended` once it reaches this many items.
41
+ // Split halves carry an endCursor and are never re-split, so the server
42
+ // converges. Omit to never recommend a split.
43
+ private options: { splitThreshold?: number } = {},
44
+ ) {}
45
+
46
+ private toOffset(cursor: string | null | undefined): number {
47
+ return cursor === null || cursor === undefined
48
+ ? 0
49
+ : parseInt(cursor.slice(1));
50
+ }
51
+
52
+ private toCursor(offset: number): string {
53
+ return `c${offset}`;
54
+ }
55
+
56
+ private pageResult(paginationOpts: {
57
+ cursor: string | null;
58
+ endCursor?: string | null;
59
+ numItems: number;
60
+ }): JSONValue {
61
+ const start = this.toOffset(paginationOpts.cursor);
62
+ const hasEndCursor =
63
+ paginationOpts.endCursor !== null &&
64
+ paginationOpts.endCursor !== undefined;
65
+ const end = hasEndCursor
66
+ ? this.toOffset(paginationOpts.endCursor)
67
+ : Math.min(start + paginationOpts.numItems, this.data.length);
68
+ const page = this.data.slice(start, end);
69
+ const recommendSplit =
70
+ !hasEndCursor &&
71
+ this.options.splitThreshold !== undefined &&
72
+ page.length >= this.options.splitThreshold;
73
+ return {
74
+ page,
75
+ isDone: end >= this.data.length,
76
+ continueCursor: this.toCursor(end),
77
+ splitCursor: recommendSplit
78
+ ? this.toCursor(start + Math.floor(page.length / 2))
79
+ : null,
80
+ pageStatus: recommendSplit ? "SplitRecommended" : null,
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Consume the connection handshake: the client's `Connect` message and the
86
+ * (empty) query set it sends on connecting.
87
+ */
88
+ async awaitConnect(): Promise<void> {
89
+ const connect = await this.io.receive();
90
+ if (connect.type !== "Connect") {
91
+ throw new Error(`Expected Connect, received ${connect.type}`);
92
+ }
93
+ const initialQuerySet = await this.io.receive();
94
+ if (initialQuerySet.type !== "ModifyQuerySet") {
95
+ throw new Error(
96
+ `Expected initial ModifyQuerySet, received ${initialQuerySet.type}`,
97
+ );
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Receive query subscriptions until `count` new pages (Add modifications)
103
+ * have been requested, then answer them all in a single transition.
104
+ */
105
+ async answerNextQueries({ count }: { count: number }): Promise<void> {
106
+ const adds: { queryId: number; args: JSONValue[] }[] = [];
107
+ while (adds.length < count) {
108
+ const message = await this.io.receive();
109
+ if (message.type !== "ModifyQuerySet") {
110
+ throw new Error(`Expected ModifyQuerySet, received ${message.type}`);
111
+ }
112
+ for (const modification of message.modifications) {
113
+ if (modification.type === "Add") {
114
+ adds.push({ queryId: modification.queryId, args: modification.args });
115
+ }
116
+ }
117
+ }
118
+
119
+ const startVersion = {
120
+ querySet: this.querySetVersion,
121
+ identity: 0,
122
+ ts: Long.fromNumber(this.ts),
123
+ };
124
+ this.querySetVersion += 1;
125
+ this.ts += 1;
126
+ this.io.send({
127
+ type: "Transition",
128
+ startVersion,
129
+ endVersion: {
130
+ querySet: this.querySetVersion,
131
+ identity: 0,
132
+ ts: Long.fromNumber(this.ts),
133
+ },
134
+ modifications: adds.map((add) => ({
135
+ type: "QueryUpdated" as const,
136
+ queryId: add.queryId,
137
+ value: this.pageResult(
138
+ (add.args[0] as unknown as { paginationOpts: PaginationOptions })
139
+ .paginationOpts,
140
+ ),
141
+ logLines: [],
142
+ journal: null,
143
+ })),
144
+ });
145
+ }
146
+ }
147
+
15
148
  describe("BaseConvexClient paginated queries with server mocked at ws level", () => {
16
149
  test("Subscribing and adding a page", async () => {
17
150
  await withInMemoryWebSocket(async ({ address, receive, send }) => {
@@ -231,6 +364,81 @@ describe("BaseConvexClient paginated queries with server mocked at ws level", ()
231
364
  await client.close();
232
365
  });
233
366
  });
367
+
368
+ test("Splitting a non-first page yields the correct concatenated pages", async () => {
369
+ await withInMemoryWebSocket(async ({ address, receive, send }) => {
370
+ const DATA = ["a", "b", "c", "d", "e", "f", "g", "h"];
371
+ // Recommend splitting any full 5-item page, so the oversized second page
372
+ // (loaded with numItems 5) splits while the 3-item first page does not.
373
+ const server = new FakePaginationServer({ receive, send }, DATA, {
374
+ splitThreshold: 5,
375
+ });
376
+
377
+ const q = new UpdateQueue();
378
+ const client = new BaseConvexClient(
379
+ address,
380
+ () => {}, // use paginated query client for all transitions
381
+ {
382
+ webSocketConstructor: nodeWebSocket,
383
+ unsavedChangesWarning: false,
384
+ },
385
+ );
386
+
387
+ const paginatedClient: PaginatedQueryClient = new PaginatedQueryClient(
388
+ client,
389
+ ({ queries, paginatedQueries }) =>
390
+ q.onTransition(
391
+ client,
392
+ paginatedClient,
393
+ )([
394
+ ...queries.map((t) => t.token),
395
+ ...paginatedQueries.map((t) => t.token),
396
+ ]),
397
+ );
398
+
399
+ const getResult = () =>
400
+ paginatedClient.localQueryResult(
401
+ "myQuery",
402
+ { channel: "general" },
403
+ { initialNumItems: 3, id: 1 },
404
+ );
405
+
406
+ await server.awaitConnect();
407
+
408
+ paginatedClient.subscribe(
409
+ "myQuery",
410
+ { channel: "general" },
411
+ { initialNumItems: 3, id: 1 },
412
+ );
413
+
414
+ let i = 0;
415
+
416
+ // Load the first page: DATA[0..3) = [a, b, c].
417
+ await server.answerNextQueries({ count: 1 });
418
+ await q.awaitPromiseAtIndexWithTimeout(i++);
419
+ expect(getResult()?.results).toEqual(["a", "b", "c"]);
420
+ expect(getResult()?.status).toEqual("CanLoadMore");
421
+
422
+ // Load a second page, which starts at the first page's continueCursor and
423
+ // comes back oversized so the server recommends a split.
424
+ getResult()!.loadMore(5);
425
+ await q.awaitPromiseAtIndexWithTimeout(i++);
426
+ await server.answerNextQueries({ count: 1 });
427
+ await q.awaitPromiseAtIndexWithTimeout(i++);
428
+
429
+ // The split subscribes two new page queries. Answer both together so the
430
+ // split completes in a single transition.
431
+ await server.answerNextQueries({ count: 2 });
432
+ await q.awaitPromiseAtIndexWithTimeout(i++);
433
+
434
+ // The visible pages must be the dataset exactly once, in order — no rows
435
+ // duplicated from re-reading the start of the dataset in a split half.
436
+ expect(getResult()?.results).toEqual(DATA);
437
+ expect(getResult()?.status).toEqual("Exhausted");
438
+
439
+ await client.close();
440
+ });
441
+ });
234
442
  });
235
443
 
236
444
  describe("BaseConvexClient paginated queries without connecting", () => {
@@ -46,7 +46,7 @@ type LocalPaginatedQuery = {
46
46
  // Map page keys to their query subscriptions
47
47
  pageKeyToQuery: Map<
48
48
  QueryPageKey,
49
- { queryToken: QueryToken; unsubscribe: () => void }
49
+ { queryToken: QueryToken; unsubscribe: () => void; cursor: string | null }
50
50
  >;
51
51
  ongoingSplits: Map<QueryPageKey, [QueryPageKey, QueryPageKey]>;
52
52
  skip: boolean;
@@ -388,8 +388,11 @@ export class PaginatedQueryClient {
388
388
  continue; // Already splitting
389
389
  }
390
390
 
391
- const pageToken = pageKeyToQuery.get(pageKey)!.queryToken;
392
- const pageResult = getResult(pageToken);
391
+ const pageEntry = pageKeyToQuery.get(pageKey);
392
+ if (!pageEntry) {
393
+ throw new Error(`No page query for active pageKey ${pageKey}`);
394
+ }
395
+ const pageResult = getResult(pageEntry.queryToken);
393
396
  if (!pageResult) {
394
397
  continue;
395
398
  }
@@ -407,6 +410,7 @@ export class PaginatedQueryClient {
407
410
  this.splitPaginatedQueryPage(
408
411
  paginatedQuery,
409
412
  pageKey,
413
+ pageEntry.cursor,
410
414
  result.splitCursor!, // we just checked
411
415
  result.continueCursor,
412
416
  );
@@ -418,31 +422,38 @@ export class PaginatedQueryClient {
418
422
  private splitPaginatedQueryPage(
419
423
  paginatedQuery: LocalPaginatedQuery,
420
424
  pageKey: QueryPageKey,
425
+ // Where the page being split started.
426
+ startCursor: string | null,
427
+ // The split position - will be end of the first split page and the start
428
+ // of the second.
421
429
  splitCursor: string,
430
+ // Where the page being split ended.
422
431
  continueCursor: string | null,
423
432
  ): void {
424
433
  const splitKey1 = paginatedQuery.nextPageKey++;
425
434
  const splitKey2 = paginatedQuery.nextPageKey++;
426
435
 
427
436
  const paginationOpts: Value = {
428
- cursor: continueCursor,
429
437
  numItems: paginatedQuery.options.initialNumItems,
430
438
  id: paginatedQuery.id,
431
439
  };
432
440
 
433
- // First split page: same cursor as original, but add endCursor at splitCursor
441
+ // First split page: same cursor as the original page, ending at splitCursor
434
442
  const firstSubscription = this.client.subscribe(
435
443
  paginatedQuery.canonicalizedUdfPath,
436
444
  {
437
445
  ...paginatedQuery.args,
438
446
  paginationOpts: {
439
447
  ...paginationOpts,
440
- cursor: null, // Start from beginning for first split
448
+ cursor: startCursor,
441
449
  endCursor: splitCursor,
442
450
  },
443
451
  },
444
452
  );
445
- paginatedQuery.pageKeyToQuery.set(splitKey1, firstSubscription);
453
+ paginatedQuery.pageKeyToQuery.set(splitKey1, {
454
+ ...firstSubscription,
455
+ cursor: startCursor,
456
+ });
446
457
 
447
458
  // Second split page: cursor starts at splitCursor, endCursor is the original continueCursor
448
459
  const secondSubscription = this.client.subscribe(
@@ -456,7 +467,10 @@ export class PaginatedQueryClient {
456
467
  },
457
468
  },
458
469
  );
459
- paginatedQuery.pageKeyToQuery.set(splitKey2, secondSubscription);
470
+ paginatedQuery.pageKeyToQuery.set(splitKey2, {
471
+ ...secondSubscription,
472
+ cursor: splitCursor,
473
+ });
460
474
 
461
475
  paginatedQuery.ongoingSplits.set(pageKey, [splitKey1, splitKey2]);
462
476
  }
@@ -489,7 +503,10 @@ export class PaginatedQueryClient {
489
503
  );
490
504
 
491
505
  paginatedQuery.pageKeys.push(pageKey);
492
- paginatedQuery.pageKeyToQuery.set(pageKey, subscription);
506
+ paginatedQuery.pageKeyToQuery.set(pageKey, {
507
+ ...subscription,
508
+ cursor: continueCursor,
509
+ });
493
510
  return subscription;
494
511
  }
495
512
 
@@ -0,0 +1,31 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import { validatorToType } from "./validator_helpers.js";
3
+
4
+ describe("validatorToType", () => {
5
+ test("commitTs matches Infer<v.commitTs()>, valid in any generated file", () => {
6
+ // Writes go through `db.vars.commitTs` (a `CommitTsPlaceholder`), so a
7
+ // bigint-only type would reject the intended insert/patch value.
8
+ expect(validatorToType({ type: "commitTs" }, false)).toEqual(
9
+ 'bigint | import("convex/values").CommitTsPlaceholder',
10
+ );
11
+ });
12
+
13
+ test("commitTs composes into container types", () => {
14
+ expect(
15
+ validatorToType(
16
+ {
17
+ type: "object",
18
+ value: {
19
+ commitTs: { fieldType: { type: "commitTs" }, optional: false },
20
+ },
21
+ },
22
+ false,
23
+ ),
24
+ ).toEqual(
25
+ '{ commitTs: bigint | import("convex/values").CommitTsPlaceholder }',
26
+ );
27
+ expect(
28
+ validatorToType({ type: "array", value: { type: "commitTs" } }, false),
29
+ ).toEqual('Array<bigint | import("convex/values").CommitTsPlaceholder>');
30
+ });
31
+ });
@@ -21,6 +21,12 @@ export function validatorToType(
21
21
  return "number";
22
22
  } else if (validator.type === "bigint") {
23
23
  return "bigint";
24
+ } else if (validator.type === "commitTs") {
25
+ // Match `Infer<v.commitTs()>`: the field holds a bigint once committed,
26
+ // but writes go through `db.vars.commitTs` (a `CommitTsPlaceholder`) and
27
+ // same-mutation read-backs yield the placeholder. The inline import keeps
28
+ // this valid in every generated file regardless of its header imports.
29
+ return 'bigint | import("convex/values").CommitTsPlaceholder';
24
30
  } else if (validator.type === "boolean") {
25
31
  return "boolean";
26
32
  } else if (validator.type === "string") {
@@ -2,10 +2,13 @@ import { Command } from "@commander-js/extra-typings";
2
2
  import { deploymentSelect } from "./deploymentSelect.js";
3
3
  import { deploymentCreate } from "./deploymentCreate.js";
4
4
  import { deploymentToken } from "./deploymentToken.js";
5
+ import { usage, usageLimits } from "./usageLimits.js";
5
6
 
6
7
  export const deployment = new Command("deployment")
7
8
  .summary("Manage deployments")
8
9
  .description("Manage deployments in your project.")
9
10
  .addCommand(deploymentSelect)
10
11
  .addCommand(deploymentCreate)
11
- .addCommand(deploymentToken);
12
+ .addCommand(deploymentToken)
13
+ .addCommand(usage)
14
+ .addCommand(usageLimits);
@@ -9,7 +9,7 @@ import {
9
9
  selectRegion,
10
10
  logNoDefaultRegionMessage,
11
11
  } from "./lib/utils/utils.js";
12
- import { PlatformProjectDetails } from "@convex-dev/platform/managementApi";
12
+ import { ProjectResponse } from "@convex-dev/platform/managementApi";
13
13
  import {
14
14
  DeploymentSelection,
15
15
  getDeploymentSelection,
@@ -190,7 +190,7 @@ const fakeProject = {
190
190
  createTime: 0,
191
191
  name: "My Project",
192
192
  teamSlug: "my-team",
193
- } satisfies PlatformProjectDetails;
193
+ } satisfies ProjectResponse;
194
194
 
195
195
  const createdDeployment = {
196
196
  kind: "cloud" as const,
@@ -20,7 +20,7 @@ import {
20
20
  typedBigBrainClient,
21
21
  typedPlatformClient,
22
22
  } from "./lib/utils/utils.js";
23
- import { PlatformProjectDetails } from "@convex-dev/platform/managementApi";
23
+ import { ProjectResponse } from "@convex-dev/platform/managementApi";
24
24
  import { getTeamAndProjectFromPreviewAdminKey } from "./lib/deployment.js";
25
25
  import { saveSelectedDeployment } from "./deploymentSelect.js";
26
26
  import { promptOptions, promptString } from "./lib/utils/prompts.js";
@@ -710,7 +710,7 @@ function parseLocalCreateTarget(
710
710
  async function resolveProject(
711
711
  ctx: Context,
712
712
  deploymentSelection: DeploymentSelection,
713
- ): Promise<PlatformProjectDetails> {
713
+ ): Promise<ProjectResponse> {
714
714
  switch (deploymentSelection.kind) {
715
715
  case "existingDeployment": {
716
716
  const { deploymentFields } = deploymentSelection.deploymentToActOn;