convex 1.43.0 → 1.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +130 -113
- package/dist/browser.bundle.js +1 -1
- package/dist/browser.bundle.js.map +1 -1
- package/dist/cjs/cli/codegen_templates/component_server.js +4 -3
- package/dist/cjs/cli/codegen_templates/component_server.js.map +2 -2
- package/dist/cjs/cli/codegen_templates/server.js +24 -12
- package/dist/cjs/cli/codegen_templates/server.js.map +2 -2
- package/dist/cjs/cli/lib/login.js +12 -8
- package/dist/cjs/cli/lib/login.js.map +2 -2
- package/dist/cjs/cli/login.js +52 -7
- package/dist/cjs/cli/login.js.map +2 -2
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react/ConvexAuthState.js +30 -32
- package/dist/cjs/react/ConvexAuthState.js.map +2 -2
- package/dist/cjs/server/impl/actions_impl.js +12 -0
- package/dist/cjs/server/impl/actions_impl.js.map +2 -2
- package/dist/cjs/server/index.js +3 -0
- package/dist/cjs/server/index.js.map +2 -2
- package/dist/cjs/server/schema.js +69 -1
- package/dist/cjs/server/schema.js.map +2 -2
- package/dist/cjs-types/cli/codegen_templates/component_server.d.ts.map +1 -1
- package/dist/cjs-types/cli/codegen_templates/server.d.ts +1 -0
- package/dist/cjs-types/cli/codegen_templates/server.d.ts.map +1 -1
- package/dist/cjs-types/cli/lib/login.d.ts +5 -0
- package/dist/cjs-types/cli/lib/login.d.ts.map +1 -1
- package/dist/cjs-types/cli/login.d.ts.map +1 -1
- package/dist/cjs-types/cli/login.test.d.ts +2 -0
- package/dist/cjs-types/cli/login.test.d.ts.map +1 -0
- package/dist/cjs-types/index.d.ts +1 -1
- package/dist/cjs-types/react/ConvexAuthState.d.ts.map +1 -1
- package/dist/cjs-types/server/impl/actions_impl.d.ts.map +1 -1
- package/dist/cjs-types/server/impl/actions_impl.test.d.ts +2 -0
- package/dist/cjs-types/server/impl/actions_impl.test.d.ts.map +1 -0
- package/dist/cjs-types/server/index.d.ts +2 -2
- package/dist/cjs-types/server/index.d.ts.map +1 -1
- package/dist/cjs-types/server/schema.d.ts +83 -5
- package/dist/cjs-types/server/schema.d.ts.map +1 -1
- package/dist/cli.bundle.cjs +1277 -570
- package/dist/cli.bundle.cjs.map +4 -4
- package/dist/esm/cli/codegen_templates/component_server.js +8 -4
- package/dist/esm/cli/codegen_templates/component_server.js.map +2 -2
- package/dist/esm/cli/codegen_templates/server.js +22 -11
- package/dist/esm/cli/codegen_templates/server.js.map +2 -2
- package/dist/esm/cli/lib/login.js +11 -8
- package/dist/esm/cli/lib/login.js.map +2 -2
- package/dist/esm/cli/login.js +54 -10
- package/dist/esm/cli/login.js.map +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/ConvexAuthState.js +31 -32
- package/dist/esm/react/ConvexAuthState.js.map +2 -2
- package/dist/esm/server/impl/actions_impl.js +11 -0
- package/dist/esm/server/impl/actions_impl.js.map +2 -2
- package/dist/esm/server/index.js +2 -1
- package/dist/esm/server/index.js.map +2 -2
- package/dist/esm/server/schema.js +67 -0
- package/dist/esm/server/schema.js.map +2 -2
- package/dist/esm-types/cli/codegen_templates/component_server.d.ts.map +1 -1
- package/dist/esm-types/cli/codegen_templates/server.d.ts +1 -0
- package/dist/esm-types/cli/codegen_templates/server.d.ts.map +1 -1
- package/dist/esm-types/cli/lib/login.d.ts +5 -0
- package/dist/esm-types/cli/lib/login.d.ts.map +1 -1
- package/dist/esm-types/cli/login.d.ts.map +1 -1
- package/dist/esm-types/cli/login.test.d.ts +2 -0
- package/dist/esm-types/cli/login.test.d.ts.map +1 -0
- package/dist/esm-types/index.d.ts +1 -1
- package/dist/esm-types/react/ConvexAuthState.d.ts.map +1 -1
- package/dist/esm-types/server/impl/actions_impl.d.ts.map +1 -1
- package/dist/esm-types/server/impl/actions_impl.test.d.ts +2 -0
- package/dist/esm-types/server/impl/actions_impl.test.d.ts.map +1 -0
- package/dist/esm-types/server/index.d.ts +2 -2
- package/dist/esm-types/server/index.d.ts.map +1 -1
- package/dist/esm-types/server/schema.d.ts +83 -5
- package/dist/esm-types/server/schema.d.ts.map +1 -1
- package/dist/react.bundle.js +31 -33
- package/dist/react.bundle.js.map +2 -2
- package/package.json +3 -4
- package/src/cli/codegen_templates/component_server.ts +12 -6
- package/src/cli/codegen_templates/server.ts +36 -19
- package/src/cli/lib/login.ts +20 -9
- package/src/cli/login.test.ts +261 -0
- package/src/cli/login.ts +88 -13
- package/src/index.ts +1 -1
- package/src/react/ConvexAuthState.tsx +15 -7
- package/src/server/impl/actions_impl.test.ts +59 -0
- package/src/server/impl/actions_impl.ts +23 -0
- package/src/server/index.ts +7 -1
- package/src/server/schema.test.ts +98 -0
- package/src/server/schema.ts +178 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { version } from "../../index.js";
|
|
3
|
+
import * as syscall from "./syscall.js";
|
|
4
|
+
import { getServiceToken } from "./actions_impl.js";
|
|
5
|
+
|
|
6
|
+
vi.mock("./syscall.js", () => ({
|
|
7
|
+
performAsyncSyscall: vi.fn(),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
vi.clearAllMocks();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe("getServiceToken", () => {
|
|
15
|
+
test("requires a service", async () => {
|
|
16
|
+
await expect(
|
|
17
|
+
getServiceToken(undefined as unknown as "ai-gateway"),
|
|
18
|
+
).rejects.toThrow("Must provide arg 1 `service` to `getServiceToken`");
|
|
19
|
+
expect(syscall.performAsyncSyscall).not.toHaveBeenCalled();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("requests an LLM token for the current action", async () => {
|
|
23
|
+
vi.mocked(syscall.performAsyncSyscall).mockResolvedValue("jwt");
|
|
24
|
+
|
|
25
|
+
const token = await getServiceToken("ai-gateway");
|
|
26
|
+
|
|
27
|
+
expect(token).toBe("jwt");
|
|
28
|
+
expect(syscall.performAsyncSyscall).toHaveBeenCalledWith(
|
|
29
|
+
"1.0/createServiceToken",
|
|
30
|
+
{
|
|
31
|
+
service: "ai-gateway",
|
|
32
|
+
version,
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("rejects the former service spelling", async () => {
|
|
38
|
+
await expect(getServiceToken("ai" as "ai-gateway")).rejects.toThrow(
|
|
39
|
+
'Unsupported service "ai"',
|
|
40
|
+
);
|
|
41
|
+
expect(syscall.performAsyncSyscall).not.toHaveBeenCalled();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("rejects unsupported services before issuing a syscall", async () => {
|
|
45
|
+
await expect(getServiceToken("other" as "ai-gateway")).rejects.toThrow(
|
|
46
|
+
'Unsupported service "other"',
|
|
47
|
+
);
|
|
48
|
+
expect(syscall.performAsyncSyscall).not.toHaveBeenCalled();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("preserves syscall errors", async () => {
|
|
52
|
+
const error = new Error(
|
|
53
|
+
"Unknown async operation 1.0/createServiceToken with args {}",
|
|
54
|
+
);
|
|
55
|
+
vi.mocked(syscall.performAsyncSyscall).mockRejectedValue(error);
|
|
56
|
+
|
|
57
|
+
await expect(getServiceToken("ai-gateway")).rejects.toBe(error);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -4,6 +4,7 @@ import { performAsyncSyscall } from "./syscall.js";
|
|
|
4
4
|
import { parseArgs } from "../../common/index.js";
|
|
5
5
|
import { FunctionReference } from "../../server/api.js";
|
|
6
6
|
import { getFunctionAddress } from "../components/paths.js";
|
|
7
|
+
import { validateArg } from "./validate.js";
|
|
7
8
|
|
|
8
9
|
function syscallArgs(
|
|
9
10
|
requestId: string,
|
|
@@ -53,3 +54,25 @@ export function setupActionCalls(requestId: string) {
|
|
|
53
54
|
},
|
|
54
55
|
};
|
|
55
56
|
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get a short-lived credential for calling a Convex-managed service.
|
|
60
|
+
*
|
|
61
|
+
* This function can only be called while an action is running. The credential
|
|
62
|
+
* is scoped to the current deployment and should be sent as a bearer token.
|
|
63
|
+
* Repeated calls in the same action reuse one token; a failed mint is not
|
|
64
|
+
* cached, so a later call retries.
|
|
65
|
+
*
|
|
66
|
+
* @param service - The service the credential may access.
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
export async function getServiceToken(service: "ai-gateway"): Promise<string> {
|
|
70
|
+
validateArg(service, 1, "getServiceToken", "service");
|
|
71
|
+
if (service !== "ai-gateway") {
|
|
72
|
+
throw new Error(`Unsupported service "${String(service)}"`);
|
|
73
|
+
}
|
|
74
|
+
return await performAsyncSyscall("1.0/createServiceToken", {
|
|
75
|
+
service,
|
|
76
|
+
version,
|
|
77
|
+
});
|
|
78
|
+
}
|
package/src/server/index.ts
CHANGED
|
@@ -103,6 +103,10 @@ export {
|
|
|
103
103
|
internalMutationGeneric,
|
|
104
104
|
internalQueryGeneric,
|
|
105
105
|
} from "./impl/registration_impl.js";
|
|
106
|
+
/**
|
|
107
|
+
* @internal
|
|
108
|
+
*/
|
|
109
|
+
export { getServiceToken } from "./impl/actions_impl.js";
|
|
106
110
|
export type {
|
|
107
111
|
TransactionMetric,
|
|
108
112
|
TransactionMetrics,
|
|
@@ -223,8 +227,10 @@ export type {
|
|
|
223
227
|
DataModelFromSchemaDefinition,
|
|
224
228
|
SystemDataModel,
|
|
225
229
|
SystemTableNames,
|
|
230
|
+
DocValidator,
|
|
231
|
+
SystemFieldValidators,
|
|
226
232
|
} from "./schema.js";
|
|
227
|
-
export { defineTable, defineSchema } from "./schema.js";
|
|
233
|
+
export { defineTable, defineSchema, docValidator } from "./schema.js";
|
|
228
234
|
|
|
229
235
|
export type {
|
|
230
236
|
VectorSearch,
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
DataModelFromSchemaDefinition,
|
|
10
10
|
GenericSchema,
|
|
11
11
|
SchemaDefinition,
|
|
12
|
+
docValidator,
|
|
12
13
|
} from "./schema.js";
|
|
13
14
|
import { v, Infer } from "../values/validator.js";
|
|
14
15
|
import {
|
|
@@ -903,6 +904,103 @@ describe("defineSchema/defineTable expose table validators", () => {
|
|
|
903
904
|
});
|
|
904
905
|
});
|
|
905
906
|
|
|
907
|
+
describe("docValidator", () => {
|
|
908
|
+
const objectSchema = defineSchema({
|
|
909
|
+
messages: defineTable({ body: v.string(), author: v.id("users") }),
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
const unionSchema = defineSchema({
|
|
913
|
+
results: defineTable(
|
|
914
|
+
v.union(
|
|
915
|
+
v.object({ kind: v.literal("error"), message: v.string() }),
|
|
916
|
+
v.object({ kind: v.literal("success"), value: v.number() }),
|
|
917
|
+
),
|
|
918
|
+
),
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
test("adds system fields to an object table", () => {
|
|
922
|
+
const doc = docValidator("messages", objectSchema.tables.messages);
|
|
923
|
+
const expected = v.object({
|
|
924
|
+
_id: v.id("messages"),
|
|
925
|
+
_creationTime: v.number(),
|
|
926
|
+
body: v.string(),
|
|
927
|
+
author: v.id("users"),
|
|
928
|
+
});
|
|
929
|
+
expect(doc.json).toEqual(expected.json);
|
|
930
|
+
assert<Equals<Infer<typeof doc>, Infer<typeof expected>>>();
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
test("adds system fields to each member of a union table", () => {
|
|
934
|
+
const doc = docValidator("results", unionSchema.tables.results);
|
|
935
|
+
const systemFields = {
|
|
936
|
+
_id: v.id("results"),
|
|
937
|
+
_creationTime: v.number(),
|
|
938
|
+
};
|
|
939
|
+
const expected = v.union(
|
|
940
|
+
v.object({
|
|
941
|
+
...systemFields,
|
|
942
|
+
kind: v.literal("error"),
|
|
943
|
+
message: v.string(),
|
|
944
|
+
}),
|
|
945
|
+
v.object({
|
|
946
|
+
...systemFields,
|
|
947
|
+
kind: v.literal("success"),
|
|
948
|
+
value: v.number(),
|
|
949
|
+
}),
|
|
950
|
+
);
|
|
951
|
+
expect(doc.json).toEqual(expected.json);
|
|
952
|
+
assert<Equals<Infer<typeof doc>, Infer<typeof expected>>>();
|
|
953
|
+
// The members stay individually typed, so they can be narrowed.
|
|
954
|
+
assert<
|
|
955
|
+
Equals<
|
|
956
|
+
Infer<(typeof doc.members)[0]>,
|
|
957
|
+
Infer<(typeof expected.members)[0]>
|
|
958
|
+
>
|
|
959
|
+
>();
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
test("passes through v.any()", () => {
|
|
963
|
+
const anySchema = defineSchema({ anything: defineTable(v.any()) });
|
|
964
|
+
const doc = docValidator("anything", anySchema.tables.anything);
|
|
965
|
+
expect(doc.json).toEqual(v.any().json);
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
test("rejects a table whose documents are not objects", () => {
|
|
969
|
+
const table = defineTable(v.array(v.string()) as any);
|
|
970
|
+
expect(() => docValidator("bad", table)).toThrow(
|
|
971
|
+
'Invalid validator for table "bad"',
|
|
972
|
+
);
|
|
973
|
+
});
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
describe("SchemaDefinition.doc/.id", () => {
|
|
977
|
+
const schema = defineSchema({
|
|
978
|
+
messages: defineTable({ body: v.string() }),
|
|
979
|
+
users: defineTable({ name: v.string() }),
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
test("doc matches docValidator", () => {
|
|
983
|
+
const actual = schema.doc("messages");
|
|
984
|
+
const expected = docValidator("messages", schema.tables.messages);
|
|
985
|
+
expect(actual.json).toEqual(expected.json);
|
|
986
|
+
assert<Equals<typeof actual, typeof expected>>();
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
test("id is typed to the table", () => {
|
|
990
|
+
const actual = schema.id("users");
|
|
991
|
+
const expected = v.id("users");
|
|
992
|
+
expect(actual.json).toEqual(expected.json);
|
|
993
|
+
assert<Equals<typeof actual, typeof expected>>();
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
test("only accepts tables in the schema", () => {
|
|
997
|
+
// @ts-expect-error "nonexistent" is not a table in the schema.
|
|
998
|
+
expect(() => schema.doc("nonexistent")).toThrow("is not in this schema");
|
|
999
|
+
// @ts-expect-error "nonexistent" is not a table in the schema.
|
|
1000
|
+
expect(() => schema.id("nonexistent")).toThrow("is not in this schema");
|
|
1001
|
+
});
|
|
1002
|
+
});
|
|
1003
|
+
|
|
906
1004
|
describe("GenericSchema assignability", () => {
|
|
907
1005
|
test("DataModelFromSchemaDefinition<GenericSchema> extends GenericDataModel", () => {
|
|
908
1006
|
type GenericDM = DataModelFromSchemaDefinition<
|
package/src/server/schema.ts
CHANGED
|
@@ -42,13 +42,20 @@ import {
|
|
|
42
42
|
SystemIndexes,
|
|
43
43
|
} from "../server/system_fields.js";
|
|
44
44
|
import { Expand } from "../type_utils.js";
|
|
45
|
+
import { GenericId } from "../values/index.js";
|
|
45
46
|
import {
|
|
46
47
|
GenericValidator,
|
|
47
48
|
ObjectType,
|
|
48
49
|
isValidator,
|
|
49
50
|
v,
|
|
50
51
|
} from "../values/validator.js";
|
|
51
|
-
import {
|
|
52
|
+
import {
|
|
53
|
+
VFloat64,
|
|
54
|
+
VId,
|
|
55
|
+
VObject,
|
|
56
|
+
VUnion,
|
|
57
|
+
Validator,
|
|
58
|
+
} from "../values/validators.js";
|
|
52
59
|
|
|
53
60
|
/**
|
|
54
61
|
* Extract all of the index field paths within a {@link Validator}.
|
|
@@ -657,6 +664,121 @@ export function defineTable<
|
|
|
657
664
|
}
|
|
658
665
|
}
|
|
659
666
|
|
|
667
|
+
/**
|
|
668
|
+
* The validators for the system fields Convex adds to every document.
|
|
669
|
+
*
|
|
670
|
+
* @public
|
|
671
|
+
*/
|
|
672
|
+
export type SystemFieldValidators<TableName extends string> = {
|
|
673
|
+
_id: VId<GenericId<TableName>>;
|
|
674
|
+
_creationTime: VFloat64<number>;
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Add the system field validators to an object validator, leaving any other
|
|
679
|
+
* validator untouched.
|
|
680
|
+
*/
|
|
681
|
+
type WithSystemFieldValidators<
|
|
682
|
+
TableName extends string,
|
|
683
|
+
DocumentType extends Validator<any, any, any>,
|
|
684
|
+
> =
|
|
685
|
+
DocumentType extends VObject<infer Type, infer Fields, any, any>
|
|
686
|
+
? VObject<
|
|
687
|
+
Expand<IdField<TableName> & SystemFields & Type>,
|
|
688
|
+
Expand<SystemFieldValidators<TableName> & Fields>
|
|
689
|
+
>
|
|
690
|
+
: DocumentType;
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* The validator for whole documents of a table: the table's own validator with
|
|
694
|
+
* the `_id` and `_creationTime` system fields added.
|
|
695
|
+
*
|
|
696
|
+
* For a table defined with a union, the system fields are added to each member
|
|
697
|
+
* of the union.
|
|
698
|
+
*
|
|
699
|
+
* @public
|
|
700
|
+
*/
|
|
701
|
+
export type DocValidator<
|
|
702
|
+
TableName extends string,
|
|
703
|
+
DocumentType extends Validator<any, any, any>,
|
|
704
|
+
> =
|
|
705
|
+
DocumentType extends VUnion<any, infer Members, any, any>
|
|
706
|
+
? {
|
|
707
|
+
[Index in keyof Members]: WithSystemFieldValidators<
|
|
708
|
+
TableName,
|
|
709
|
+
Members[Index]
|
|
710
|
+
>;
|
|
711
|
+
} extends infer NewMembers extends Validator<any, "required", any>[]
|
|
712
|
+
? VUnion<
|
|
713
|
+
WithSystemFieldValidators<TableName, Members[number]>["type"],
|
|
714
|
+
NewMembers
|
|
715
|
+
>
|
|
716
|
+
: never
|
|
717
|
+
: WithSystemFieldValidators<TableName, DocumentType>;
|
|
718
|
+
|
|
719
|
+
function addSystemFields(
|
|
720
|
+
tableName: string,
|
|
721
|
+
validator: GenericValidator,
|
|
722
|
+
): GenericValidator {
|
|
723
|
+
switch (validator.kind) {
|
|
724
|
+
case "object":
|
|
725
|
+
return validator.extend({
|
|
726
|
+
_id: v.id(tableName),
|
|
727
|
+
_creationTime: v.number(),
|
|
728
|
+
});
|
|
729
|
+
case "union":
|
|
730
|
+
return v.union(
|
|
731
|
+
...validator.members.map((member) =>
|
|
732
|
+
addSystemFields(tableName, member),
|
|
733
|
+
),
|
|
734
|
+
);
|
|
735
|
+
// `v.any()` already permits the system fields.
|
|
736
|
+
case "any":
|
|
737
|
+
return validator;
|
|
738
|
+
default:
|
|
739
|
+
throw new Error(
|
|
740
|
+
`Invalid validator for table "${tableName}": a table's documents must be objects, or a union of objects (see https://docs.convex.dev/database/schemas)`,
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* Build the validator for whole documents of a table, by adding the `_id` and
|
|
747
|
+
* `_creationTime` system fields to the table's own validator.
|
|
748
|
+
*
|
|
749
|
+
* Prefer {@link SchemaDefinition.doc} when you have the schema in hand: it
|
|
750
|
+
* checks the table name against the schema.
|
|
751
|
+
*
|
|
752
|
+
* @example
|
|
753
|
+
* ```ts
|
|
754
|
+
* const messageDoc = docValidator("messages", schema.tables.messages);
|
|
755
|
+
*
|
|
756
|
+
* export const get = query({
|
|
757
|
+
* args: { id: v.id("messages") },
|
|
758
|
+
* returns: v.union(messageDoc, v.null()),
|
|
759
|
+
* handler: (ctx, args) => ctx.db.get(args.id),
|
|
760
|
+
* });
|
|
761
|
+
* ```
|
|
762
|
+
*
|
|
763
|
+
* @param tableName - The name of the table, used for the `_id` validator.
|
|
764
|
+
* @param table - The {@link TableDefinition} for that table.
|
|
765
|
+
* @returns A validator matching documents of the table.
|
|
766
|
+
*
|
|
767
|
+
* @public
|
|
768
|
+
*/
|
|
769
|
+
export function docValidator<
|
|
770
|
+
TableName extends string,
|
|
771
|
+
Table extends TableDefinition<any, any, any, any>,
|
|
772
|
+
>(
|
|
773
|
+
tableName: TableName,
|
|
774
|
+
table: Table,
|
|
775
|
+
): DocValidator<TableName, Table["validator"]> {
|
|
776
|
+
return addSystemFields(tableName, table.validator) as DocValidator<
|
|
777
|
+
TableName,
|
|
778
|
+
Table["validator"]
|
|
779
|
+
>;
|
|
780
|
+
}
|
|
781
|
+
|
|
660
782
|
/**
|
|
661
783
|
* A type describing the schema of a Convex project.
|
|
662
784
|
*
|
|
@@ -666,6 +788,24 @@ export function defineTable<
|
|
|
666
788
|
*/
|
|
667
789
|
export type GenericSchema = Record<string, TableDefinition>;
|
|
668
790
|
|
|
791
|
+
function tableInSchema<
|
|
792
|
+
Schema extends GenericSchema,
|
|
793
|
+
TableName extends keyof Schema & string,
|
|
794
|
+
>(
|
|
795
|
+
schema: SchemaDefinition<Schema, boolean>,
|
|
796
|
+
tableName: TableName,
|
|
797
|
+
): Schema[TableName] {
|
|
798
|
+
const table = schema.tables[tableName];
|
|
799
|
+
if (table === undefined) {
|
|
800
|
+
throw new Error(
|
|
801
|
+
`Table "${tableName}" is not in this schema. Tables in this schema: ${Object.keys(
|
|
802
|
+
schema.tables,
|
|
803
|
+
).join(", ")}`,
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
return table;
|
|
807
|
+
}
|
|
808
|
+
|
|
669
809
|
/**
|
|
670
810
|
*
|
|
671
811
|
* The definition of a Convex project schema.
|
|
@@ -690,6 +830,43 @@ export class SchemaDefinition<
|
|
|
690
830
|
options?.schemaValidation === undefined ? true : options.schemaValidation;
|
|
691
831
|
}
|
|
692
832
|
|
|
833
|
+
/**
|
|
834
|
+
* The validator for whole documents of a table in this schema: the table's
|
|
835
|
+
* own validator with the `_id` and `_creationTime` system fields added.
|
|
836
|
+
*
|
|
837
|
+
* @example
|
|
838
|
+
* ```ts
|
|
839
|
+
* export const get = query({
|
|
840
|
+
* args: { id: schema.id("messages") },
|
|
841
|
+
* returns: v.union(schema.doc("messages"), v.null()),
|
|
842
|
+
* handler: (ctx, args) => ctx.db.get(args.id),
|
|
843
|
+
* });
|
|
844
|
+
* ```
|
|
845
|
+
*
|
|
846
|
+
* @param tableName - The name of a table in this schema.
|
|
847
|
+
* @returns A validator matching documents of that table.
|
|
848
|
+
*/
|
|
849
|
+
doc<TableName extends keyof Schema & string>(
|
|
850
|
+
tableName: TableName,
|
|
851
|
+
): DocValidator<TableName, Schema[TableName]["validator"]> {
|
|
852
|
+
return docValidator(tableName, tableInSchema(this, tableName));
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* The validator for IDs of a table in this schema.
|
|
857
|
+
*
|
|
858
|
+
* Same as `v.id(tableName)`, but only accepts tables in this schema.
|
|
859
|
+
*
|
|
860
|
+
* @param tableName - The name of a table in this schema.
|
|
861
|
+
* @returns A validator matching IDs of that table.
|
|
862
|
+
*/
|
|
863
|
+
id<TableName extends keyof Schema & string>(
|
|
864
|
+
tableName: TableName,
|
|
865
|
+
): VId<GenericId<TableName>> {
|
|
866
|
+
tableInSchema(this, tableName);
|
|
867
|
+
return v.id(tableName);
|
|
868
|
+
}
|
|
869
|
+
|
|
693
870
|
/**
|
|
694
871
|
* Export the contents of this definition.
|
|
695
872
|
*
|