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.
- package/bin/main-dev +6 -1
- package/dist/browser.bundle.js +49 -10
- package/dist/browser.bundle.js.map +2 -2
- package/dist/cjs/browser/simple_client-node.js +35 -35
- package/dist/cjs/browser/simple_client-node.js.map +2 -2
- package/dist/cjs/browser/sync/paginated_query_client.js +20 -9
- package/dist/cjs/browser/sync/paginated_query_client.js.map +2 -2
- package/dist/cjs/cli/codegen_templates/validator_helpers.js +2 -0
- package/dist/cjs/cli/codegen_templates/validator_helpers.js.map +2 -2
- package/dist/cjs/cli/deployment.js +2 -1
- package/dist/cjs/cli/deployment.js.map +2 -2
- package/dist/cjs/cli/deploymentCreate.js.map +2 -2
- package/dist/cjs/cli/generatedApi.js.map +1 -1
- package/dist/cjs/cli/lib/deployApi/validator.js +1 -0
- package/dist/cjs/cli/lib/deployApi/validator.js.map +2 -2
- package/dist/cjs/cli/lib/deploymentSelection.js.map +2 -2
- package/dist/cjs/cli/lib/generateDocs.js +1 -1
- package/dist/cjs/cli/lib/generateDocs.js.map +2 -2
- package/dist/cjs/cli/lib/generatedFunctionLogsApi.js.map +1 -1
- package/dist/cjs/cli/lib/localDeployment/projectMismatch.js.map +2 -2
- package/dist/cjs/cli/lib/login.js +23 -23
- package/dist/cjs/cli/lib/login.js.map +2 -2
- package/dist/cjs/cli/lib/typecheck.js +28 -25
- package/dist/cjs/cli/lib/typecheck.js.map +2 -2
- package/dist/cjs/cli/lib/usageLimits.js +164 -0
- package/dist/cjs/cli/lib/usageLimits.js.map +7 -0
- package/dist/cjs/cli/usageLimits.js +326 -0
- package/dist/cjs/cli/usageLimits.js.map +7 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/server/components/index.js.map +2 -2
- package/dist/cjs/server/cron.js +33 -29
- package/dist/cjs/server/cron.js.map +2 -2
- package/dist/cjs/server/database.js.map +1 -1
- package/dist/cjs/server/impl/database_impl.js +3 -0
- package/dist/cjs/server/impl/database_impl.js.map +2 -2
- package/dist/cjs/server/log.js +2 -1
- package/dist/cjs/server/log.js.map +2 -2
- package/dist/cjs/server/logVars.js +12 -1
- package/dist/cjs/server/logVars.js.map +2 -2
- package/dist/cjs/values/compare.js +5 -1
- package/dist/cjs/values/compare.js.map +2 -2
- package/dist/cjs/values/index.js +1 -0
- package/dist/cjs/values/index.js.map +2 -2
- package/dist/cjs/values/size.js +4 -0
- package/dist/cjs/values/size.js.map +2 -2
- package/dist/cjs/values/validator.js +11 -0
- package/dist/cjs/values/validator.js.map +2 -2
- package/dist/cjs/values/validators.js +20 -0
- package/dist/cjs/values/validators.js.map +2 -2
- package/dist/cjs/values/value.js +31 -0
- package/dist/cjs/values/value.js.map +2 -2
- package/dist/cjs-types/cli/codegen_templates/validator_helpers.d.ts.map +1 -1
- package/dist/cjs-types/cli/codegen_templates/validator_helpers.test.d.ts +2 -0
- package/dist/cjs-types/cli/codegen_templates/validator_helpers.test.d.ts.map +1 -0
- package/dist/cjs-types/cli/deployment.d.ts.map +1 -1
- package/dist/cjs-types/cli/deploymentCreate.d.ts +2 -2
- package/dist/cjs-types/cli/generatedApi.d.ts +43 -43
- package/dist/cjs-types/cli/generatedApi.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/deployApi/validator.d.ts +6 -0
- package/dist/cjs-types/cli/lib/deployApi/validator.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/deploymentSelection.d.ts +2 -2
- package/dist/cjs-types/cli/lib/deploymentSelection.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts +45 -45
- package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/localDeployment/projectMismatch.d.ts +2 -2
- package/dist/cjs-types/cli/lib/localDeployment/projectMismatch.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/login.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/mcp/tools/insights.d.ts +24 -24
- package/dist/cjs-types/cli/lib/typecheck.d.ts +2 -0
- package/dist/cjs-types/cli/lib/typecheck.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/typecheck.test.d.ts +2 -0
- package/dist/cjs-types/cli/lib/typecheck.test.d.ts.map +1 -0
- package/dist/cjs-types/cli/lib/usageLimits.d.ts +46 -0
- package/dist/cjs-types/cli/lib/usageLimits.d.ts.map +1 -0
- package/dist/cjs-types/cli/usageLimits.d.ts +22 -0
- package/dist/cjs-types/cli/usageLimits.d.ts.map +1 -0
- package/dist/cjs-types/index.d.ts +1 -1
- package/dist/cjs-types/index.d.ts.map +1 -1
- package/dist/cjs-types/server/components/index.d.ts +26 -8
- package/dist/cjs-types/server/components/index.d.ts.map +1 -1
- package/dist/cjs-types/server/cron.d.ts +20 -11
- package/dist/cjs-types/server/cron.d.ts.map +1 -1
- package/dist/cjs-types/server/cron.test.d.ts +2 -0
- package/dist/cjs-types/server/cron.test.d.ts.map +1 -0
- package/dist/cjs-types/server/database.d.ts.map +1 -1
- package/dist/cjs-types/server/impl/database_impl.d.ts.map +1 -1
- package/dist/cjs-types/server/log.d.ts.map +1 -1
- package/dist/cjs-types/server/logVars.d.ts +14 -1
- package/dist/cjs-types/server/logVars.d.ts.map +1 -1
- package/dist/cjs-types/values/compare.d.ts.map +1 -1
- package/dist/cjs-types/values/index.d.ts +2 -2
- package/dist/cjs-types/values/index.d.ts.map +1 -1
- package/dist/cjs-types/values/size.d.ts +1 -1
- package/dist/cjs-types/values/size.d.ts.map +1 -1
- package/dist/cjs-types/values/validator.d.ts +10 -1
- package/dist/cjs-types/values/validator.d.ts.map +1 -1
- package/dist/cjs-types/values/validators.d.ts +14 -3
- package/dist/cjs-types/values/validators.d.ts.map +1 -1
- package/dist/cjs-types/values/value.d.ts +18 -1
- package/dist/cjs-types/values/value.d.ts.map +1 -1
- package/dist/cli.bundle.cjs +2325 -1864
- package/dist/cli.bundle.cjs.map +4 -4
- package/dist/esm/browser/simple_client-node.js +35 -35
- package/dist/esm/browser/simple_client-node.js.map +1 -1
- package/dist/esm/browser/sync/paginated_query_client.js +20 -9
- package/dist/esm/browser/sync/paginated_query_client.js.map +2 -2
- package/dist/esm/cli/codegen_templates/validator_helpers.js +2 -0
- package/dist/esm/cli/codegen_templates/validator_helpers.js.map +2 -2
- package/dist/esm/cli/deployment.js +2 -1
- package/dist/esm/cli/deployment.js.map +2 -2
- package/dist/esm/cli/deploymentCreate.js.map +2 -2
- package/dist/esm/cli/lib/deployApi/validator.js +1 -0
- package/dist/esm/cli/lib/deployApi/validator.js.map +2 -2
- package/dist/esm/cli/lib/deploymentSelection.js.map +2 -2
- package/dist/esm/cli/lib/generateDocs.js +1 -1
- package/dist/esm/cli/lib/generateDocs.js.map +2 -2
- package/dist/esm/cli/lib/localDeployment/projectMismatch.js.map +2 -2
- package/dist/esm/cli/lib/login.js +23 -23
- package/dist/esm/cli/lib/login.js.map +2 -2
- package/dist/esm/cli/lib/typecheck.js +27 -25
- package/dist/esm/cli/lib/typecheck.js.map +2 -2
- package/dist/esm/cli/lib/usageLimits.js +129 -0
- package/dist/esm/cli/lib/usageLimits.js.map +7 -0
- package/dist/esm/cli/usageLimits.js +316 -0
- package/dist/esm/cli/usageLimits.js.map +7 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/server/components/index.js.map +2 -2
- package/dist/esm/server/cron.js +33 -29
- package/dist/esm/server/cron.js.map +2 -2
- package/dist/esm/server/impl/database_impl.js +4 -1
- package/dist/esm/server/impl/database_impl.js.map +2 -2
- package/dist/esm/server/log.js +3 -2
- package/dist/esm/server/log.js.map +2 -2
- package/dist/esm/server/logVars.js +11 -1
- package/dist/esm/server/logVars.js.map +2 -2
- package/dist/esm/values/compare.js +5 -1
- package/dist/esm/values/compare.js.map +2 -2
- package/dist/esm/values/index.js +1 -1
- package/dist/esm/values/index.js.map +2 -2
- package/dist/esm/values/size.js +4 -0
- package/dist/esm/values/size.js.map +2 -2
- package/dist/esm/values/validator.js +12 -0
- package/dist/esm/values/validator.js.map +2 -2
- package/dist/esm/values/validators.js +19 -0
- package/dist/esm/values/validators.js.map +2 -2
- package/dist/esm/values/value.js +29 -1
- package/dist/esm/values/value.js.map +2 -2
- package/dist/esm-types/cli/codegen_templates/validator_helpers.d.ts.map +1 -1
- package/dist/esm-types/cli/codegen_templates/validator_helpers.test.d.ts +2 -0
- package/dist/esm-types/cli/codegen_templates/validator_helpers.test.d.ts.map +1 -0
- package/dist/esm-types/cli/deployment.d.ts.map +1 -1
- package/dist/esm-types/cli/deploymentCreate.d.ts +2 -2
- package/dist/esm-types/cli/generatedApi.d.ts +43 -43
- package/dist/esm-types/cli/generatedApi.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/deployApi/validator.d.ts +6 -0
- package/dist/esm-types/cli/lib/deployApi/validator.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/deploymentSelection.d.ts +2 -2
- package/dist/esm-types/cli/lib/deploymentSelection.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts +45 -45
- package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/localDeployment/projectMismatch.d.ts +2 -2
- package/dist/esm-types/cli/lib/localDeployment/projectMismatch.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/login.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/mcp/tools/insights.d.ts +24 -24
- package/dist/esm-types/cli/lib/typecheck.d.ts +2 -0
- package/dist/esm-types/cli/lib/typecheck.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/typecheck.test.d.ts +2 -0
- package/dist/esm-types/cli/lib/typecheck.test.d.ts.map +1 -0
- package/dist/esm-types/cli/lib/usageLimits.d.ts +46 -0
- package/dist/esm-types/cli/lib/usageLimits.d.ts.map +1 -0
- package/dist/esm-types/cli/usageLimits.d.ts +22 -0
- package/dist/esm-types/cli/usageLimits.d.ts.map +1 -0
- package/dist/esm-types/index.d.ts +1 -1
- package/dist/esm-types/index.d.ts.map +1 -1
- package/dist/esm-types/server/components/index.d.ts +26 -8
- package/dist/esm-types/server/components/index.d.ts.map +1 -1
- package/dist/esm-types/server/cron.d.ts +20 -11
- package/dist/esm-types/server/cron.d.ts.map +1 -1
- package/dist/esm-types/server/cron.test.d.ts +2 -0
- package/dist/esm-types/server/cron.test.d.ts.map +1 -0
- package/dist/esm-types/server/database.d.ts.map +1 -1
- package/dist/esm-types/server/impl/database_impl.d.ts.map +1 -1
- package/dist/esm-types/server/log.d.ts.map +1 -1
- package/dist/esm-types/server/logVars.d.ts +14 -1
- package/dist/esm-types/server/logVars.d.ts.map +1 -1
- package/dist/esm-types/values/compare.d.ts.map +1 -1
- package/dist/esm-types/values/index.d.ts +2 -2
- package/dist/esm-types/values/index.d.ts.map +1 -1
- package/dist/esm-types/values/size.d.ts +1 -1
- package/dist/esm-types/values/size.d.ts.map +1 -1
- package/dist/esm-types/values/validator.d.ts +10 -1
- package/dist/esm-types/values/validator.d.ts.map +1 -1
- package/dist/esm-types/values/validators.d.ts +14 -3
- package/dist/esm-types/values/validators.d.ts.map +1 -1
- package/dist/esm-types/values/value.d.ts +18 -1
- package/dist/esm-types/values/value.d.ts.map +1 -1
- package/dist/react.bundle.js +53 -11
- package/dist/react.bundle.js.map +2 -2
- package/package.json +8 -8
- package/schemas/convex.schema.json +2 -1
- package/src/browser/sync/paginated_query_client.test.ts +209 -1
- package/src/browser/sync/paginated_query_client.ts +26 -9
- package/src/cli/codegen_templates/validator_helpers.test.ts +31 -0
- package/src/cli/codegen_templates/validator_helpers.ts +6 -0
- package/src/cli/deployment.ts +4 -1
- package/src/cli/deploymentCreate.test.ts +2 -2
- package/src/cli/deploymentCreate.ts +2 -2
- package/src/cli/generatedApi.ts +43 -43
- package/src/cli/lib/deployApi/validator.ts +1 -0
- package/src/cli/lib/deploymentSelection.ts +2 -2
- package/src/cli/lib/generateDocs.test.ts +1 -1
- package/src/cli/lib/generateDocs.ts +3 -3
- package/src/cli/lib/generatedFunctionLogsApi.ts +45 -45
- package/src/cli/lib/localDeployment/projectMismatch.ts +2 -2
- package/src/cli/lib/login.ts +33 -30
- package/src/cli/lib/typecheck.test.ts +71 -0
- package/src/cli/lib/typecheck.ts +33 -27
- package/src/cli/lib/usageLimits.ts +242 -0
- package/src/cli/usageLimits.ts +420 -0
- package/src/index.ts +1 -1
- package/src/server/audit_logging.test.ts +2 -0
- package/src/server/components/index.test.ts +33 -1
- package/src/server/components/index.ts +41 -14
- package/src/server/cron.test.ts +58 -0
- package/src/server/cron.ts +82 -21
- package/src/server/database.ts +17 -0
- package/src/server/impl/database_impl.ts +4 -1
- package/src/server/log.ts +4 -1
- package/src/server/logVars.ts +17 -1
- package/src/server/schema.test.ts +6 -1
- package/src/values/compare.ts +5 -2
- package/src/values/index.ts +2 -1
- package/src/values/size.test.ts +5 -0
- package/src/values/size.ts +5 -1
- package/src/values/validator.test.ts +16 -1
- package/src/values/validator.ts +13 -0
- package/src/values/validators.ts +29 -1
- package/src/values/value.test.ts +35 -0
- package/src/values/value.ts +56 -1
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.
|
|
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.
|
|
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": "
|
|
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 {
|
|
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
|
|
392
|
-
|
|
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,
|
|
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:
|
|
448
|
+
cursor: startCursor,
|
|
441
449
|
endCursor: splitCursor,
|
|
442
450
|
},
|
|
443
451
|
},
|
|
444
452
|
);
|
|
445
|
-
paginatedQuery.pageKeyToQuery.set(splitKey1,
|
|
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,
|
|
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,
|
|
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") {
|
package/src/cli/deployment.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
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 {
|
|
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<
|
|
713
|
+
): Promise<ProjectResponse> {
|
|
714
714
|
switch (deploymentSelection.kind) {
|
|
715
715
|
case "existingDeployment": {
|
|
716
716
|
const { deploymentFields } = deploymentSelection.deploymentToActOn;
|