convex 1.42.2 → 1.43.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/CHANGELOG.md +8 -1
  2. package/bin/main-dev +6 -1
  3. package/dist/browser.bundle.js +49 -10
  4. package/dist/browser.bundle.js.map +2 -2
  5. package/dist/cjs/browser/index.js.map +2 -2
  6. package/dist/cjs/browser/simple_client-node.js +35 -35
  7. package/dist/cjs/browser/simple_client-node.js.map +2 -2
  8. package/dist/cjs/browser/sync/client.js.map +1 -1
  9. package/dist/cjs/browser/sync/paginated_query_client.js +20 -9
  10. package/dist/cjs/browser/sync/paginated_query_client.js.map +2 -2
  11. package/dist/cjs/cli/codegen_templates/component_api.js +2 -2
  12. package/dist/cjs/cli/codegen_templates/component_api.js.map +2 -2
  13. package/dist/cjs/cli/codegen_templates/validator_helpers.js +2 -0
  14. package/dist/cjs/cli/codegen_templates/validator_helpers.js.map +2 -2
  15. package/dist/cjs/cli/deployment.js +2 -1
  16. package/dist/cjs/cli/deployment.js.map +2 -2
  17. package/dist/cjs/cli/deploymentCreate.js.map +2 -2
  18. package/dist/cjs/cli/generatedApi.js.map +1 -1
  19. package/dist/cjs/cli/lib/deployApi/validator.js +1 -0
  20. package/dist/cjs/cli/lib/deployApi/validator.js.map +2 -2
  21. package/dist/cjs/cli/lib/deploymentSelection.js.map +2 -2
  22. package/dist/cjs/cli/lib/generateDocs.js +1 -1
  23. package/dist/cjs/cli/lib/generateDocs.js.map +2 -2
  24. package/dist/cjs/cli/lib/generatedFunctionLogsApi.js.map +1 -1
  25. package/dist/cjs/cli/lib/localDeployment/projectMismatch.js.map +2 -2
  26. package/dist/cjs/cli/lib/login.js +23 -23
  27. package/dist/cjs/cli/lib/login.js.map +2 -2
  28. package/dist/cjs/cli/lib/typecheck.js +28 -25
  29. package/dist/cjs/cli/lib/typecheck.js.map +2 -2
  30. package/dist/cjs/cli/lib/usageLimits.js +164 -0
  31. package/dist/cjs/cli/lib/usageLimits.js.map +7 -0
  32. package/dist/cjs/cli/usageLimits.js +326 -0
  33. package/dist/cjs/cli/usageLimits.js.map +7 -0
  34. package/dist/cjs/index.js +1 -1
  35. package/dist/cjs/index.js.map +1 -1
  36. package/dist/cjs/react/client.js.map +2 -2
  37. package/dist/cjs/server/components/index.js.map +2 -2
  38. package/dist/cjs/server/cron.js +33 -29
  39. package/dist/cjs/server/cron.js.map +2 -2
  40. package/dist/cjs/server/database.js.map +1 -1
  41. package/dist/cjs/server/impl/database_impl.js +3 -0
  42. package/dist/cjs/server/impl/database_impl.js.map +2 -2
  43. package/dist/cjs/server/log.js +2 -1
  44. package/dist/cjs/server/log.js.map +2 -2
  45. package/dist/cjs/server/logVars.js +12 -1
  46. package/dist/cjs/server/logVars.js.map +2 -2
  47. package/dist/cjs/server/meta.js.map +1 -1
  48. package/dist/cjs/values/compare.js +5 -1
  49. package/dist/cjs/values/compare.js.map +2 -2
  50. package/dist/cjs/values/index.js +1 -0
  51. package/dist/cjs/values/index.js.map +2 -2
  52. package/dist/cjs/values/size.js +4 -0
  53. package/dist/cjs/values/size.js.map +2 -2
  54. package/dist/cjs/values/validator.js +11 -0
  55. package/dist/cjs/values/validator.js.map +2 -2
  56. package/dist/cjs/values/validators.js +20 -0
  57. package/dist/cjs/values/validators.js.map +2 -2
  58. package/dist/cjs/values/value.js +31 -0
  59. package/dist/cjs/values/value.js.map +2 -2
  60. package/dist/cjs-types/browser/index.d.ts.map +1 -1
  61. package/dist/cjs-types/browser/sync/paginated_query_client.d.ts +1 -38
  62. package/dist/cjs-types/browser/sync/paginated_query_client.d.ts.map +1 -1
  63. package/dist/cjs-types/cli/codegen_templates/component_api.d.ts.map +1 -1
  64. package/dist/cjs-types/cli/codegen_templates/validator_helpers.d.ts.map +1 -1
  65. package/dist/cjs-types/cli/codegen_templates/validator_helpers.test.d.ts +2 -0
  66. package/dist/cjs-types/cli/codegen_templates/validator_helpers.test.d.ts.map +1 -0
  67. package/dist/cjs-types/cli/deployment.d.ts.map +1 -1
  68. package/dist/cjs-types/cli/deploymentCreate.d.ts +2 -2
  69. package/dist/cjs-types/cli/generatedApi.d.ts +43 -43
  70. package/dist/cjs-types/cli/generatedApi.d.ts.map +1 -1
  71. package/dist/cjs-types/cli/lib/deployApi/validator.d.ts +6 -0
  72. package/dist/cjs-types/cli/lib/deployApi/validator.d.ts.map +1 -1
  73. package/dist/cjs-types/cli/lib/deploymentSelection.d.ts +2 -2
  74. package/dist/cjs-types/cli/lib/deploymentSelection.d.ts.map +1 -1
  75. package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts +45 -45
  76. package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
  77. package/dist/cjs-types/cli/lib/localDeployment/projectMismatch.d.ts +2 -2
  78. package/dist/cjs-types/cli/lib/localDeployment/projectMismatch.d.ts.map +1 -1
  79. package/dist/cjs-types/cli/lib/login.d.ts.map +1 -1
  80. package/dist/cjs-types/cli/lib/mcp/tools/insights.d.ts +24 -24
  81. package/dist/cjs-types/cli/lib/typecheck.d.ts +2 -0
  82. package/dist/cjs-types/cli/lib/typecheck.d.ts.map +1 -1
  83. package/dist/cjs-types/cli/lib/typecheck.test.d.ts +2 -0
  84. package/dist/cjs-types/cli/lib/typecheck.test.d.ts.map +1 -0
  85. package/dist/cjs-types/cli/lib/usageLimits.d.ts +46 -0
  86. package/dist/cjs-types/cli/lib/usageLimits.d.ts.map +1 -0
  87. package/dist/cjs-types/cli/usageLimits.d.ts +22 -0
  88. package/dist/cjs-types/cli/usageLimits.d.ts.map +1 -0
  89. package/dist/cjs-types/index.d.ts +1 -1
  90. package/dist/cjs-types/index.d.ts.map +1 -1
  91. package/dist/cjs-types/react/client.d.ts.map +1 -1
  92. package/dist/cjs-types/server/components/index.d.ts +26 -8
  93. package/dist/cjs-types/server/components/index.d.ts.map +1 -1
  94. package/dist/cjs-types/server/cron.d.ts +20 -11
  95. package/dist/cjs-types/server/cron.d.ts.map +1 -1
  96. package/dist/cjs-types/server/cron.test.d.ts +2 -0
  97. package/dist/cjs-types/server/cron.test.d.ts.map +1 -0
  98. package/dist/cjs-types/server/database.d.ts.map +1 -1
  99. package/dist/cjs-types/server/impl/database_impl.d.ts.map +1 -1
  100. package/dist/cjs-types/server/log.d.ts.map +1 -1
  101. package/dist/cjs-types/server/logVars.d.ts +14 -1
  102. package/dist/cjs-types/server/logVars.d.ts.map +1 -1
  103. package/dist/cjs-types/server/meta.d.ts +2 -2
  104. package/dist/cjs-types/server/meta.d.ts.map +1 -1
  105. package/dist/cjs-types/values/compare.d.ts.map +1 -1
  106. package/dist/cjs-types/values/index.d.ts +2 -2
  107. package/dist/cjs-types/values/index.d.ts.map +1 -1
  108. package/dist/cjs-types/values/size.d.ts +1 -1
  109. package/dist/cjs-types/values/size.d.ts.map +1 -1
  110. package/dist/cjs-types/values/validator.d.ts +10 -1
  111. package/dist/cjs-types/values/validator.d.ts.map +1 -1
  112. package/dist/cjs-types/values/validators.d.ts +14 -3
  113. package/dist/cjs-types/values/validators.d.ts.map +1 -1
  114. package/dist/cjs-types/values/value.d.ts +18 -1
  115. package/dist/cjs-types/values/value.d.ts.map +1 -1
  116. package/dist/cli.bundle.cjs +2327 -1866
  117. package/dist/cli.bundle.cjs.map +4 -4
  118. package/dist/esm/browser/index.js.map +2 -2
  119. package/dist/esm/browser/simple_client-node.js +35 -35
  120. package/dist/esm/browser/simple_client-node.js.map +1 -1
  121. package/dist/esm/browser/sync/client.js.map +1 -1
  122. package/dist/esm/browser/sync/paginated_query_client.js +20 -9
  123. package/dist/esm/browser/sync/paginated_query_client.js.map +2 -2
  124. package/dist/esm/cli/codegen_templates/component_api.js +8 -3
  125. package/dist/esm/cli/codegen_templates/component_api.js.map +2 -2
  126. package/dist/esm/cli/codegen_templates/validator_helpers.js +2 -0
  127. package/dist/esm/cli/codegen_templates/validator_helpers.js.map +2 -2
  128. package/dist/esm/cli/deployment.js +2 -1
  129. package/dist/esm/cli/deployment.js.map +2 -2
  130. package/dist/esm/cli/deploymentCreate.js.map +2 -2
  131. package/dist/esm/cli/lib/deployApi/validator.js +1 -0
  132. package/dist/esm/cli/lib/deployApi/validator.js.map +2 -2
  133. package/dist/esm/cli/lib/deploymentSelection.js.map +2 -2
  134. package/dist/esm/cli/lib/generateDocs.js +1 -1
  135. package/dist/esm/cli/lib/generateDocs.js.map +2 -2
  136. package/dist/esm/cli/lib/localDeployment/projectMismatch.js.map +2 -2
  137. package/dist/esm/cli/lib/login.js +23 -23
  138. package/dist/esm/cli/lib/login.js.map +2 -2
  139. package/dist/esm/cli/lib/typecheck.js +27 -25
  140. package/dist/esm/cli/lib/typecheck.js.map +2 -2
  141. package/dist/esm/cli/lib/usageLimits.js +129 -0
  142. package/dist/esm/cli/lib/usageLimits.js.map +7 -0
  143. package/dist/esm/cli/usageLimits.js +316 -0
  144. package/dist/esm/cli/usageLimits.js.map +7 -0
  145. package/dist/esm/index.js +1 -1
  146. package/dist/esm/index.js.map +1 -1
  147. package/dist/esm/react/client.js.map +2 -2
  148. package/dist/esm/server/components/index.js.map +2 -2
  149. package/dist/esm/server/cron.js +33 -29
  150. package/dist/esm/server/cron.js.map +2 -2
  151. package/dist/esm/server/impl/database_impl.js +4 -1
  152. package/dist/esm/server/impl/database_impl.js.map +2 -2
  153. package/dist/esm/server/log.js +3 -2
  154. package/dist/esm/server/log.js.map +2 -2
  155. package/dist/esm/server/logVars.js +11 -1
  156. package/dist/esm/server/logVars.js.map +2 -2
  157. package/dist/esm/values/compare.js +5 -1
  158. package/dist/esm/values/compare.js.map +2 -2
  159. package/dist/esm/values/index.js +1 -1
  160. package/dist/esm/values/index.js.map +2 -2
  161. package/dist/esm/values/size.js +4 -0
  162. package/dist/esm/values/size.js.map +2 -2
  163. package/dist/esm/values/validator.js +12 -0
  164. package/dist/esm/values/validator.js.map +2 -2
  165. package/dist/esm/values/validators.js +19 -0
  166. package/dist/esm/values/validators.js.map +2 -2
  167. package/dist/esm/values/value.js +29 -1
  168. package/dist/esm/values/value.js.map +2 -2
  169. package/dist/esm-types/browser/index.d.ts.map +1 -1
  170. package/dist/esm-types/browser/sync/paginated_query_client.d.ts +1 -38
  171. package/dist/esm-types/browser/sync/paginated_query_client.d.ts.map +1 -1
  172. package/dist/esm-types/cli/codegen_templates/component_api.d.ts.map +1 -1
  173. package/dist/esm-types/cli/codegen_templates/validator_helpers.d.ts.map +1 -1
  174. package/dist/esm-types/cli/codegen_templates/validator_helpers.test.d.ts +2 -0
  175. package/dist/esm-types/cli/codegen_templates/validator_helpers.test.d.ts.map +1 -0
  176. package/dist/esm-types/cli/deployment.d.ts.map +1 -1
  177. package/dist/esm-types/cli/deploymentCreate.d.ts +2 -2
  178. package/dist/esm-types/cli/generatedApi.d.ts +43 -43
  179. package/dist/esm-types/cli/generatedApi.d.ts.map +1 -1
  180. package/dist/esm-types/cli/lib/deployApi/validator.d.ts +6 -0
  181. package/dist/esm-types/cli/lib/deployApi/validator.d.ts.map +1 -1
  182. package/dist/esm-types/cli/lib/deploymentSelection.d.ts +2 -2
  183. package/dist/esm-types/cli/lib/deploymentSelection.d.ts.map +1 -1
  184. package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts +45 -45
  185. package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
  186. package/dist/esm-types/cli/lib/localDeployment/projectMismatch.d.ts +2 -2
  187. package/dist/esm-types/cli/lib/localDeployment/projectMismatch.d.ts.map +1 -1
  188. package/dist/esm-types/cli/lib/login.d.ts.map +1 -1
  189. package/dist/esm-types/cli/lib/mcp/tools/insights.d.ts +24 -24
  190. package/dist/esm-types/cli/lib/typecheck.d.ts +2 -0
  191. package/dist/esm-types/cli/lib/typecheck.d.ts.map +1 -1
  192. package/dist/esm-types/cli/lib/typecheck.test.d.ts +2 -0
  193. package/dist/esm-types/cli/lib/typecheck.test.d.ts.map +1 -0
  194. package/dist/esm-types/cli/lib/usageLimits.d.ts +46 -0
  195. package/dist/esm-types/cli/lib/usageLimits.d.ts.map +1 -0
  196. package/dist/esm-types/cli/usageLimits.d.ts +22 -0
  197. package/dist/esm-types/cli/usageLimits.d.ts.map +1 -0
  198. package/dist/esm-types/index.d.ts +1 -1
  199. package/dist/esm-types/index.d.ts.map +1 -1
  200. package/dist/esm-types/react/client.d.ts.map +1 -1
  201. package/dist/esm-types/server/components/index.d.ts +26 -8
  202. package/dist/esm-types/server/components/index.d.ts.map +1 -1
  203. package/dist/esm-types/server/cron.d.ts +20 -11
  204. package/dist/esm-types/server/cron.d.ts.map +1 -1
  205. package/dist/esm-types/server/cron.test.d.ts +2 -0
  206. package/dist/esm-types/server/cron.test.d.ts.map +1 -0
  207. package/dist/esm-types/server/database.d.ts.map +1 -1
  208. package/dist/esm-types/server/impl/database_impl.d.ts.map +1 -1
  209. package/dist/esm-types/server/log.d.ts.map +1 -1
  210. package/dist/esm-types/server/logVars.d.ts +14 -1
  211. package/dist/esm-types/server/logVars.d.ts.map +1 -1
  212. package/dist/esm-types/server/meta.d.ts +2 -2
  213. package/dist/esm-types/server/meta.d.ts.map +1 -1
  214. package/dist/esm-types/values/compare.d.ts.map +1 -1
  215. package/dist/esm-types/values/index.d.ts +2 -2
  216. package/dist/esm-types/values/index.d.ts.map +1 -1
  217. package/dist/esm-types/values/size.d.ts +1 -1
  218. package/dist/esm-types/values/size.d.ts.map +1 -1
  219. package/dist/esm-types/values/validator.d.ts +10 -1
  220. package/dist/esm-types/values/validator.d.ts.map +1 -1
  221. package/dist/esm-types/values/validators.d.ts +14 -3
  222. package/dist/esm-types/values/validators.d.ts.map +1 -1
  223. package/dist/esm-types/values/value.d.ts +18 -1
  224. package/dist/esm-types/values/value.d.ts.map +1 -1
  225. package/dist/react.bundle.js +53 -11
  226. package/dist/react.bundle.js.map +2 -2
  227. package/package.json +8 -8
  228. package/schemas/convex.schema.json +2 -1
  229. package/src/browser/index.ts +2 -0
  230. package/src/browser/sync/client.test.ts +8 -1
  231. package/src/browser/sync/client.ts +63 -0
  232. package/src/browser/sync/paginated_query_client.test.ts +209 -1
  233. package/src/browser/sync/paginated_query_client.ts +29 -11
  234. package/src/cli/codegen_templates/component_api.ts +8 -3
  235. package/src/cli/codegen_templates/validator_helpers.test.ts +31 -0
  236. package/src/cli/codegen_templates/validator_helpers.ts +6 -0
  237. package/src/cli/deployment.ts +4 -1
  238. package/src/cli/deploymentCreate.test.ts +2 -2
  239. package/src/cli/deploymentCreate.ts +2 -2
  240. package/src/cli/generatedApi.ts +43 -43
  241. package/src/cli/lib/deployApi/validator.ts +1 -0
  242. package/src/cli/lib/deploymentSelection.ts +2 -2
  243. package/src/cli/lib/generateDocs.test.ts +1 -1
  244. package/src/cli/lib/generateDocs.ts +3 -3
  245. package/src/cli/lib/generatedFunctionLogsApi.ts +45 -45
  246. package/src/cli/lib/localDeployment/projectMismatch.ts +2 -2
  247. package/src/cli/lib/login.ts +33 -30
  248. package/src/cli/lib/typecheck.test.ts +71 -0
  249. package/src/cli/lib/typecheck.ts +33 -27
  250. package/src/cli/lib/usageLimits.ts +242 -0
  251. package/src/cli/usageLimits.ts +420 -0
  252. package/src/index.ts +1 -1
  253. package/src/react/ConvexAuthState.test.tsx +24 -8
  254. package/src/react/auth_websocket.test.tsx +39 -29
  255. package/src/react/client.ts +3 -2
  256. package/src/react/client_provided_base_client.test.tsx +154 -36
  257. package/src/server/audit_logging.test.ts +2 -0
  258. package/src/server/components/index.test.ts +33 -1
  259. package/src/server/components/index.ts +41 -14
  260. package/src/server/cron.test.ts +58 -0
  261. package/src/server/cron.ts +82 -21
  262. package/src/server/database.ts +17 -0
  263. package/src/server/impl/database_impl.ts +4 -1
  264. package/src/server/log.ts +4 -1
  265. package/src/server/logVars.ts +17 -1
  266. package/src/server/meta.ts +2 -2
  267. package/src/server/schema.test.ts +6 -1
  268. package/src/values/compare.ts +5 -2
  269. package/src/values/index.ts +2 -1
  270. package/src/values/size.test.ts +5 -0
  271. package/src/values/size.ts +5 -1
  272. package/src/values/validator.test.ts +16 -1
  273. package/src/values/validator.ts +13 -0
  274. package/src/values/validators.ts +29 -1
  275. package/src/values/value.test.ts +35 -0
  276. 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.2",
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"
@@ -24,6 +24,8 @@ export type {
24
24
  ConnectionState,
25
25
  AuthTokenFetcher,
26
26
  } from "./sync/client.js";
27
+ /** @internal */
28
+ export type { BaseConvexClientInterface } from "./sync/client.js";
27
29
  export type { PaginationStatus } from "./sync/pagination.js";
28
30
  export type { ConvexClientOptions } from "./simple_client.js";
29
31
  export { ConvexClient } from "./simple_client.js";
@@ -4,9 +4,16 @@
4
4
 
5
5
  import { test, expect } from "vitest";
6
6
 
7
- import { BaseConvexClient } from "./client.js";
7
+ import { BaseConvexClient, BaseConvexClientInterface } from "./client.js";
8
8
  import { anyApi } from "../../server/api.js";
9
9
 
10
+ // Compile-time assertion that `BaseConvexClient` satisfies
11
+ // `BaseConvexClientInterface`. We can't use an `implements` clause on the class
12
+ // because `BaseConvexClientInterface` is `@internal`: that would leak the
13
+ // interface name into the public `.d.ts` where it has been stripped.
14
+ const _assertBaseConvexClientImplementsInterface: BaseConvexClientInterface =
15
+ undefined as unknown as BaseConvexClient;
16
+
10
17
  test("localQueryResult reflects optimistic results", async () => {
11
18
  const client = new BaseConvexClient("http://127.0.0.1:8000", () => {
12
19
  // ignore updates.
@@ -1126,3 +1126,66 @@ export class BaseConvexClient {
1126
1126
  });
1127
1127
  }
1128
1128
  }
1129
+
1130
+ /**
1131
+ * The public API of {@link BaseConvexClient}.
1132
+ *
1133
+ * @internal
1134
+ */
1135
+ export interface BaseConvexClientInterface {
1136
+ addOnTransitionHandler(fn: (transition: Transition) => void): () => void;
1137
+
1138
+ setAuth(
1139
+ fetchToken: AuthTokenFetcher,
1140
+ onChange: (isAuthenticated: boolean) => void,
1141
+ onRefreshChange?: (isRefreshing: boolean) => void,
1142
+ ): void;
1143
+
1144
+ /** @internal */
1145
+ setAdminAuth(value: string, fakeUserIdentity?: UserIdentityAttributes): void;
1146
+
1147
+ clearAuth(): void;
1148
+
1149
+ subscribe(
1150
+ name: string,
1151
+ args?: Record<string, Value>,
1152
+ options?: SubscribeOptions,
1153
+ ): { queryToken: QueryToken; unsubscribe: () => void };
1154
+
1155
+ localQueryResult(
1156
+ udfPath: string,
1157
+ args?: Record<string, Value>,
1158
+ ): Value | undefined;
1159
+
1160
+ localQueryResultByToken(queryToken: QueryToken): Value | undefined;
1161
+
1162
+ hasLocalQueryResultByToken(queryToken: QueryToken): boolean;
1163
+
1164
+ localQueryLogs(
1165
+ udfPath: string,
1166
+ args?: Record<string, Value>,
1167
+ ): string[] | undefined;
1168
+
1169
+ queryJournal(
1170
+ name: string,
1171
+ args?: Record<string, Value>,
1172
+ ): QueryJournal | undefined;
1173
+
1174
+ connectionState(): ConnectionState;
1175
+
1176
+ subscribeToConnectionState(
1177
+ cb: (connectionState: ConnectionState) => void,
1178
+ ): () => void;
1179
+
1180
+ mutation(
1181
+ name: string,
1182
+ args?: Record<string, Value>,
1183
+ options?: MutationOptions,
1184
+ ): Promise<any>;
1185
+
1186
+ action(name: string, args?: Record<string, Value>): Promise<any>;
1187
+
1188
+ close(): Promise<void>;
1189
+
1190
+ readonly url: string;
1191
+ }
@@ -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", () => {
@@ -17,7 +17,7 @@ import {
17
17
  serializePaginatedPathAndArgs,
18
18
  canonicalizeUdfPath,
19
19
  } from "./udf_path_utils.js";
20
- import { BaseConvexClient, Transition } from "./client.js";
20
+ import { BaseConvexClientInterface, Transition } from "./client.js";
21
21
  import {
22
22
  PaginatedQueryResult,
23
23
  PaginationStatus,
@@ -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;
@@ -74,6 +74,7 @@ export type ExtendedTransition = Transition & {
74
74
  }>;
75
75
  };
76
76
 
77
+ /** @internal */
77
78
  export class PaginatedQueryClient {
78
79
  private paginatedQuerySet: Map<PaginatedQueryToken, LocalPaginatedQuery> =
79
80
  new Map();
@@ -81,7 +82,7 @@ export class PaginatedQueryClient {
81
82
  private lastTransitionTs: TS;
82
83
 
83
84
  constructor(
84
- private client: BaseConvexClient,
85
+ private client: BaseConvexClientInterface,
85
86
  private onTransition: (transition: ExtendedTransition) => void,
86
87
  ) {
87
88
  // Nonsense initial value to construct synthetic Transitions
@@ -387,8 +388,11 @@ export class PaginatedQueryClient {
387
388
  continue; // Already splitting
388
389
  }
389
390
 
390
- const pageToken = pageKeyToQuery.get(pageKey)!.queryToken;
391
- 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);
392
396
  if (!pageResult) {
393
397
  continue;
394
398
  }
@@ -406,6 +410,7 @@ export class PaginatedQueryClient {
406
410
  this.splitPaginatedQueryPage(
407
411
  paginatedQuery,
408
412
  pageKey,
413
+ pageEntry.cursor,
409
414
  result.splitCursor!, // we just checked
410
415
  result.continueCursor,
411
416
  );
@@ -417,31 +422,38 @@ export class PaginatedQueryClient {
417
422
  private splitPaginatedQueryPage(
418
423
  paginatedQuery: LocalPaginatedQuery,
419
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.
420
429
  splitCursor: string,
430
+ // Where the page being split ended.
421
431
  continueCursor: string | null,
422
432
  ): void {
423
433
  const splitKey1 = paginatedQuery.nextPageKey++;
424
434
  const splitKey2 = paginatedQuery.nextPageKey++;
425
435
 
426
436
  const paginationOpts: Value = {
427
- cursor: continueCursor,
428
437
  numItems: paginatedQuery.options.initialNumItems,
429
438
  id: paginatedQuery.id,
430
439
  };
431
440
 
432
- // 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
433
442
  const firstSubscription = this.client.subscribe(
434
443
  paginatedQuery.canonicalizedUdfPath,
435
444
  {
436
445
  ...paginatedQuery.args,
437
446
  paginationOpts: {
438
447
  ...paginationOpts,
439
- cursor: null, // Start from beginning for first split
448
+ cursor: startCursor,
440
449
  endCursor: splitCursor,
441
450
  },
442
451
  },
443
452
  );
444
- paginatedQuery.pageKeyToQuery.set(splitKey1, firstSubscription);
453
+ paginatedQuery.pageKeyToQuery.set(splitKey1, {
454
+ ...firstSubscription,
455
+ cursor: startCursor,
456
+ });
445
457
 
446
458
  // Second split page: cursor starts at splitCursor, endCursor is the original continueCursor
447
459
  const secondSubscription = this.client.subscribe(
@@ -455,7 +467,10 @@ export class PaginatedQueryClient {
455
467
  },
456
468
  },
457
469
  );
458
- paginatedQuery.pageKeyToQuery.set(splitKey2, secondSubscription);
470
+ paginatedQuery.pageKeyToQuery.set(splitKey2, {
471
+ ...secondSubscription,
472
+ cursor: splitCursor,
473
+ });
459
474
 
460
475
  paginatedQuery.ongoingSplits.set(pageKey, [splitKey1, splitKey2]);
461
476
  }
@@ -488,7 +503,10 @@ export class PaginatedQueryClient {
488
503
  );
489
504
 
490
505
  paginatedQuery.pageKeys.push(pageKey);
491
- paginatedQuery.pageKeyToQuery.set(pageKey, subscription);
506
+ paginatedQuery.pageKeyToQuery.set(pageKey, {
507
+ ...subscription,
508
+ cursor: continueCursor,
509
+ });
492
510
  return subscription;
493
511
  }
494
512
 
@@ -9,7 +9,12 @@ import {
9
9
  } from "../lib/components/definition/directoryStructure.js";
10
10
  import { StartPushResponse } from "../lib/deployApi/startPush.js";
11
11
  import { importPath, moduleIdentifier } from "./api.js";
12
- import { apiComment, compareStrings, header } from "./common.js";
12
+ import {
13
+ apiComment,
14
+ compareModulePaths,
15
+ compareStrings,
16
+ header,
17
+ } from "./common.js";
13
18
  import {
14
19
  ComponentExports,
15
20
  EvaluatedComponentDefinition,
@@ -346,7 +351,7 @@ async function* codegenDynamicApiObjects(
346
351
  const absModulePaths = await entryPoints(ctx, componentDirectory.path);
347
352
  const modulePaths = absModulePaths
348
353
  .map((p) => path.relative(componentDirectory.path, p))
349
- .sort();
354
+ .sort(compareModulePaths);
350
355
  for (const modulePath of modulePaths) {
351
356
  const ident = moduleIdentifier(modulePath);
352
357
  const path = importPath(modulePath);
@@ -382,7 +387,7 @@ async function* codegenDynamicApiObjectsTS(
382
387
  const absModulePaths = await entryPoints(ctx, componentDirectory.path);
383
388
  const modulePaths = absModulePaths
384
389
  .map((p) => path.relative(componentDirectory.path, p))
385
- .sort();
390
+ .sort(compareModulePaths);
386
391
  for (const modulePath of modulePaths) {
387
392
  const ident = moduleIdentifier(modulePath);
388
393
  const path = importPath(modulePath);
@@ -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;