@confect/core 10.0.0-next.8 → 10.0.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 +966 -46
- package/README.md +3 -1
- package/dist/FunctionProvenance.d.ts +166 -68
- package/dist/FunctionProvenance.d.ts.map +1 -1
- package/dist/FunctionProvenance.js +55 -13
- package/dist/FunctionProvenance.js.map +1 -1
- package/dist/FunctionSpec.d.ts +320 -138
- package/dist/FunctionSpec.d.ts.map +1 -1
- package/dist/FunctionSpec.js +56 -13
- package/dist/FunctionSpec.js.map +1 -1
- package/dist/GenericId.d.ts.map +1 -1
- package/dist/GenericId.js +1 -1
- package/dist/GroupPath.d.ts +6 -10
- package/dist/GroupPath.d.ts.map +1 -1
- package/dist/GroupPath.js.map +1 -1
- package/dist/GroupSpec.d.ts +25 -12
- package/dist/GroupSpec.d.ts.map +1 -1
- package/dist/GroupSpec.js +51 -14
- package/dist/GroupSpec.js.map +1 -1
- package/dist/Identifier.d.ts +6 -5
- package/dist/Identifier.d.ts.map +1 -1
- package/dist/Identifier.js +6 -5
- package/dist/Identifier.js.map +1 -1
- package/dist/Lazy.d.ts +11 -14
- package/dist/Lazy.d.ts.map +1 -1
- package/dist/Lazy.js +11 -14
- package/dist/Lazy.js.map +1 -1
- package/dist/MiddlewareAttachment.d.ts +171 -0
- package/dist/MiddlewareAttachment.d.ts.map +1 -0
- package/dist/MiddlewareAttachment.js +48 -0
- package/dist/MiddlewareAttachment.js.map +1 -0
- package/dist/MiddlewareSpec.d.ts +198 -0
- package/dist/MiddlewareSpec.d.ts.map +1 -0
- package/dist/MiddlewareSpec.js +60 -0
- package/dist/MiddlewareSpec.js.map +1 -0
- package/dist/PaginationError.d.ts +20 -0
- package/dist/PaginationError.d.ts.map +1 -0
- package/dist/PaginationError.js +31 -0
- package/dist/PaginationError.js.map +1 -0
- package/dist/PaginationOptions.d.ts +23 -0
- package/dist/PaginationOptions.d.ts.map +1 -0
- package/dist/PaginationOptions.js +29 -0
- package/dist/PaginationOptions.js.map +1 -0
- package/dist/PaginationResult.d.ts +1 -0
- package/dist/PaginationResult.d.ts.map +1 -1
- package/dist/PaginationResult.js.map +1 -1
- package/dist/Ref.d.ts +135 -47
- package/dist/Ref.d.ts.map +1 -1
- package/dist/Ref.js +104 -46
- package/dist/Ref.js.map +1 -1
- package/dist/Refs.d.ts +10 -5
- package/dist/Refs.d.ts.map +1 -1
- package/dist/Refs.js +5 -4
- package/dist/Refs.js.map +1 -1
- package/dist/SchemaToValidator.d.ts +99 -0
- package/dist/SchemaToValidator.d.ts.map +1 -0
- package/dist/SchemaToValidator.js +158 -0
- package/dist/SchemaToValidator.js.map +1 -0
- package/dist/Spec.d.ts +16 -12
- package/dist/Spec.d.ts.map +1 -1
- package/dist/Spec.js +14 -9
- package/dist/Spec.js.map +1 -1
- package/dist/SystemFields.d.ts +16 -15
- package/dist/SystemFields.d.ts.map +1 -1
- package/dist/SystemFields.js +16 -15
- package/dist/SystemFields.js.map +1 -1
- package/dist/Table.d.ts +75 -0
- package/dist/Table.d.ts.map +1 -0
- package/dist/Table.js +77 -0
- package/dist/Table.js.map +1 -0
- package/dist/Types.d.ts +8 -2
- package/dist/Types.d.ts.map +1 -1
- package/dist/Types.js.map +1 -1
- package/dist/UserIdentity.d.ts.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/package.json +17 -15
- package/src/FunctionProvenance.ts +197 -34
- package/src/FunctionSpec.ts +332 -86
- package/src/GroupPath.ts +6 -12
- package/src/GroupSpec.ts +184 -25
- package/src/Identifier.ts +6 -5
- package/src/Lazy.ts +11 -14
- package/src/MiddlewareAttachment.ts +106 -0
- package/src/MiddlewareSpec.ts +392 -0
- package/src/PaginationError.ts +78 -0
- package/src/PaginationOptions.ts +23 -0
- package/src/PaginationResult.ts +4 -0
- package/src/Ref.ts +468 -236
- package/src/Refs.ts +43 -18
- package/src/SchemaToValidator.ts +612 -0
- package/src/Spec.ts +50 -39
- package/src/SystemFields.ts +32 -36
- package/src/Table.ts +427 -0
- package/src/Types.ts +9 -3
- package/src/index.ts +6 -1
- package/dist/Registry.d.ts +0 -24
- package/dist/Registry.d.ts.map +0 -1
- package/dist/Registry.js +0 -19
- package/dist/Registry.js.map +0 -1
- package/dist/tsconfig.src.tsbuildinfo +0 -1
- package/src/Registry.ts +0 -30
package/src/Spec.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as Predicate from "effect/Predicate";
|
|
2
2
|
import * as Record from "effect/Record";
|
|
3
|
+
import * as Struct from "effect/Struct";
|
|
3
4
|
import * as GroupSpec from "./GroupSpec";
|
|
4
5
|
|
|
5
|
-
export const TypeId = "
|
|
6
|
+
export const TypeId = "~@confect/core/Spec";
|
|
6
7
|
export type TypeId = typeof TypeId;
|
|
7
8
|
|
|
8
9
|
export const isSpec = (u: unknown): u is AnyWithProps =>
|
|
@@ -10,66 +11,76 @@ export const isSpec = (u: unknown): u is AnyWithProps =>
|
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* A Confect spec: a flat container of function groups. Groups may be of any
|
|
13
|
-
* runtime
|
|
14
|
+
* runtime—a group built with `GroupSpec.makeNode()` (a Node action group) sits
|
|
14
15
|
* alongside `GroupSpec.make()` groups in the same namespace. The runtime of a
|
|
15
16
|
* group lives on the group itself (`GroupSpec.runtime`) and on each function's
|
|
16
17
|
* `RuntimeAndFunctionType`; the spec does not carry a runtime of its own.
|
|
17
18
|
*/
|
|
18
|
-
export interface Spec<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
Groups_,
|
|
23
|
-
GroupName
|
|
24
|
-
>;
|
|
25
|
-
};
|
|
19
|
+
export interface Spec<
|
|
20
|
+
Groups_ extends Record.ReadonlyRecord<string, GroupSpec.AnyWithProps> = {},
|
|
21
|
+
> {
|
|
22
|
+
readonly [TypeId]: Readonly<Groups_>;
|
|
26
23
|
|
|
27
24
|
add<Group extends GroupSpec.AnyWithProps>(
|
|
28
25
|
group: Group,
|
|
29
|
-
): Spec<
|
|
26
|
+
): Spec<
|
|
27
|
+
Struct.Assign<Groups_, Record.ReadonlyRecord<GroupSpec.Name<Group>, Group>>
|
|
28
|
+
>;
|
|
30
29
|
|
|
31
30
|
addAt<const Name extends string, Group extends GroupSpec.AnyWithProps>(
|
|
32
31
|
name: Name,
|
|
33
32
|
group: Group,
|
|
34
|
-
): Spec<
|
|
33
|
+
): Spec<
|
|
34
|
+
Struct.Assign<
|
|
35
|
+
Groups_,
|
|
36
|
+
Record.ReadonlyRecord<Name, GroupSpec.NamedAt<Group, Name>>
|
|
37
|
+
>
|
|
38
|
+
>;
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
export interface Any {
|
|
38
|
-
readonly [TypeId]:
|
|
42
|
+
readonly [TypeId]: unknown;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
export interface AnyWithProps extends
|
|
45
|
+
export interface AnyWithProps extends Any {
|
|
46
|
+
readonly [TypeId]: Record.ReadonlyRecord<string, GroupSpec.AnyWithProps>;
|
|
47
|
+
}
|
|
42
48
|
|
|
43
49
|
export type Groups<Spec_ extends AnyWithProps> =
|
|
44
|
-
Spec_[
|
|
50
|
+
Spec_[TypeId][keyof Spec_[TypeId]];
|
|
45
51
|
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
/**
|
|
53
|
+
* The group record stored by a spec, with its precise member types.
|
|
54
|
+
*/
|
|
55
|
+
export const groups = <Spec_ extends AnyWithProps>(
|
|
56
|
+
self: Spec_,
|
|
57
|
+
): Spec_[TypeId] => self[TypeId];
|
|
48
58
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
const makeRecord = <
|
|
60
|
+
Groups_ extends Record.ReadonlyRecord<string, GroupSpec.AnyWithProps>,
|
|
61
|
+
>(
|
|
62
|
+
record: Groups_,
|
|
63
|
+
): Spec<Groups_> => ({
|
|
64
|
+
[TypeId]: record,
|
|
65
|
+
add<Group extends GroupSpec.AnyWithProps>(group: Group) {
|
|
66
|
+
return makeRecord(
|
|
67
|
+
Struct.assign(
|
|
68
|
+
record,
|
|
69
|
+
Record.singleton<GroupSpec.Name<Group>, Group>(group.name, group),
|
|
70
|
+
),
|
|
71
|
+
);
|
|
53
72
|
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
this: AnyWithProps,
|
|
57
|
-
name: string,
|
|
73
|
+
addAt<const Name extends string, Group extends GroupSpec.AnyWithProps>(
|
|
74
|
+
name: Name,
|
|
58
75
|
group: Group,
|
|
59
76
|
) {
|
|
60
|
-
return
|
|
61
|
-
|
|
62
|
-
|
|
77
|
+
return makeRecord(
|
|
78
|
+
Struct.assign(
|
|
79
|
+
record,
|
|
80
|
+
Record.singleton(name, GroupSpec.withName(name, group)),
|
|
81
|
+
),
|
|
82
|
+
);
|
|
63
83
|
},
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const makeProto = <Groups_ extends GroupSpec.AnyWithProps>({
|
|
67
|
-
groups,
|
|
68
|
-
}: {
|
|
69
|
-
groups: Record.ReadonlyRecord<string, Groups_>;
|
|
70
|
-
}): Spec<Groups_> =>
|
|
71
|
-
Object.assign(Object.create(Proto), {
|
|
72
|
-
groups,
|
|
73
|
-
});
|
|
84
|
+
});
|
|
74
85
|
|
|
75
|
-
export const make = (): Spec =>
|
|
86
|
+
export const make = (): Spec => makeRecord({});
|
package/src/SystemFields.ts
CHANGED
|
@@ -13,12 +13,13 @@ import * as Struct from "effect/Struct";
|
|
|
13
13
|
import * as GenericId from "./GenericId";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* Produces a schema for Convex system fields. In Confect, system fields include
|
|
16
|
+
* Produces a schema for Convex system fields. In Confect, system fields include
|
|
17
|
+
* `_id`.
|
|
17
18
|
*/
|
|
18
19
|
export const SystemFields = <TableName extends string>(tableName: TableName) =>
|
|
19
20
|
Schema.Struct({
|
|
20
21
|
_id: GenericId.GenericId(tableName),
|
|
21
|
-
_creationTime: Schema.
|
|
22
|
+
_creationTime: Schema.Finite,
|
|
22
23
|
});
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -58,23 +59,21 @@ const splitSystemFields = (input: { [key: PropertyKey]: unknown }) => {
|
|
|
58
59
|
* output. Transformations construct fresh objects, so without this the system
|
|
59
60
|
* fields would be dropped at every transformation step.
|
|
60
61
|
*/
|
|
61
|
-
const wrapGetter = (
|
|
62
|
-
getter: SchemaGetter.Getter<any, any,
|
|
63
|
-
): SchemaGetter.Getter<any, any,
|
|
64
|
-
|
|
62
|
+
const wrapGetter = <R>(
|
|
63
|
+
getter: SchemaGetter.Getter<any, any, R>,
|
|
64
|
+
): SchemaGetter.Getter<any, any, R> =>
|
|
65
|
+
SchemaGetter.transformOptionalEffect((input, options) => {
|
|
65
66
|
if (Option.isNone(input) || !Predicate.isObject(input.value)) {
|
|
66
|
-
return
|
|
67
|
+
return SchemaGetter.run(getter, input, options);
|
|
67
68
|
}
|
|
68
69
|
const { system, rest } = splitSystemFields(input.value);
|
|
69
|
-
return getter
|
|
70
|
-
.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
Option.map((output) =>
|
|
74
|
-
Predicate.isObject(output) ? Struct.assign(output, system) : output,
|
|
75
|
-
),
|
|
70
|
+
return SchemaGetter.run(getter, Option.some(rest), options).pipe(
|
|
71
|
+
Effect.map(
|
|
72
|
+
Option.map((output) =>
|
|
73
|
+
Predicate.isObject(output) ? Struct.assign(output, system) : output,
|
|
76
74
|
),
|
|
77
|
-
)
|
|
75
|
+
),
|
|
76
|
+
);
|
|
78
77
|
});
|
|
79
78
|
|
|
80
79
|
const wrapTransformation = (
|
|
@@ -93,8 +92,8 @@ const wrapTransformation = (
|
|
|
93
92
|
|
|
94
93
|
/**
|
|
95
94
|
* Builds an AST walker that adds the given system-field property signatures to
|
|
96
|
-
* every object node of a schema's AST
|
|
97
|
-
* chain
|
|
95
|
+
* every object node of a schema's AST—including each step of its encoding
|
|
96
|
+
* chain—wrapping transformations so the fields survive them.
|
|
98
97
|
*/
|
|
99
98
|
const makeExtendAst = (
|
|
100
99
|
systemPropertySignatures: ReadonlyArray<SchemaAST.PropertySignature>,
|
|
@@ -113,7 +112,7 @@ const makeExtendAst = (
|
|
|
113
112
|
if (SchemaAST.isUnion(ast)) {
|
|
114
113
|
return new SchemaAST.Union(
|
|
115
114
|
Array.map(ast.types, extendAst),
|
|
116
|
-
ast.
|
|
115
|
+
ast.options,
|
|
117
116
|
ast.annotations,
|
|
118
117
|
ast.checks,
|
|
119
118
|
ast.encoding === undefined ? undefined : extendEncoding(ast.encoding),
|
|
@@ -154,11 +153,11 @@ const makeExtendAst = (
|
|
|
154
153
|
/**
|
|
155
154
|
* Extend a table schema with Convex system fields.
|
|
156
155
|
*
|
|
157
|
-
* A plain `Struct` gains the fields via `Schema.fieldsAssign` and a `Union`
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* `Schema.decodeTo`/`Schema.encodeKeys`, a branded struct, a suspended
|
|
161
|
-
* —
|
|
156
|
+
* A plain `Struct` gains the fields via `Schema.fieldsAssign` and a `Union` has
|
|
157
|
+
* them distributed across its members, preserving the schema's `Struct`/`Union`
|
|
158
|
+
* structure. Any other object-shaped schema—one built with
|
|
159
|
+
* `Schema.decodeTo`/`Schema.encodeKeys`, a branded struct, a suspended
|
|
160
|
+
* schema—is extended at the AST level: every object node in its encoding chain
|
|
162
161
|
* gains the system fields, and each transformation is wrapped so the fields
|
|
163
162
|
* bypass the user-defined getters. Schemas that do not resolve to an object
|
|
164
163
|
* shape at every step (such as `Schema.Class`, whose decoded values are class
|
|
@@ -191,9 +190,7 @@ export const extendWithSystemFields = <
|
|
|
191
190
|
if (
|
|
192
191
|
s.ast.encoding === undefined &&
|
|
193
192
|
SchemaAST.isUnion(s.ast) &&
|
|
194
|
-
Array.isArray(
|
|
195
|
-
(s as Partial<Schema.Union<ReadonlyArray<Schema.Top>>>).members,
|
|
196
|
-
)
|
|
193
|
+
Array.isArray((s as { readonly members?: unknown }).members)
|
|
197
194
|
) {
|
|
198
195
|
return Schema.Union(
|
|
199
196
|
Array.map(
|
|
@@ -205,8 +202,7 @@ export const extendWithSystemFields = <
|
|
|
205
202
|
if (
|
|
206
203
|
s.ast.encoding === undefined &&
|
|
207
204
|
SchemaAST.isObjects(s.ast) &&
|
|
208
|
-
typeof (s as
|
|
209
|
-
"function"
|
|
205
|
+
typeof (s as { readonly mapFields?: unknown }).mapFields === "function"
|
|
210
206
|
) {
|
|
211
207
|
return Schema.fieldsAssign(system)(
|
|
212
208
|
s as Schema.Struct<Schema.Struct.Fields>,
|
|
@@ -222,9 +218,9 @@ export const extendWithSystemFields = <
|
|
|
222
218
|
};
|
|
223
219
|
|
|
224
220
|
/**
|
|
225
|
-
* Applies the system fields to a single struct, mirroring
|
|
226
|
-
* Any other (already-extended or opaque) schema falls
|
|
227
|
-
* the system-field document shape.
|
|
221
|
+
* Applies the system fields to a single struct, mirroring
|
|
222
|
+
* `Schema.fieldsAssign`. Any other (already-extended or opaque) schema falls
|
|
223
|
+
* back to a `Codec` carrying the system-field document shape.
|
|
228
224
|
*/
|
|
229
225
|
type ApplySystemFields<TableName extends string, S> =
|
|
230
226
|
S extends Schema.Struct<infer Fields extends Schema.Struct.Fields>
|
|
@@ -244,10 +240,10 @@ type ApplySystemFields<TableName extends string, S> =
|
|
|
244
240
|
* This mirrors the runtime {@link extendWithSystemFields}: a `Struct` gains the
|
|
245
241
|
* system fields directly (matching `Schema.fieldsAssign`), and a `Union` has
|
|
246
242
|
* them distributed across its members (matching
|
|
247
|
-
* `union.mapMembers(Tuple.map(Schema.fieldsAssign(...)))`). The
|
|
248
|
-
* structure is preserved rather than collapsed to a bare
|
|
249
|
-
* schema falls back to a `Codec` carrying the system-field
|
|
250
|
-
* matching the runtime's AST-level extension.
|
|
243
|
+
* `union.mapMembers(Tuple.map(Schema.fieldsAssign(...)))`). The
|
|
244
|
+
* `Struct`/`Union` structure is preserved rather than collapsed to a bare
|
|
245
|
+
* `Codec`; any other schema falls back to a `Codec` carrying the system-field
|
|
246
|
+
* document shape, matching the runtime's AST-level extension.
|
|
251
247
|
*/
|
|
252
248
|
export type ExtendWithSystemFields<
|
|
253
249
|
TableName extends string,
|
|
@@ -267,7 +263,7 @@ export type ExtendWithSystemFields<
|
|
|
267
263
|
|
|
268
264
|
/**
|
|
269
265
|
* The decoded/encoded document shape: a table's fields plus Convex's system
|
|
270
|
-
* fields. Deliberately a bare intersection (no `Expand`/`Simplify`)
|
|
266
|
+
* fields. Deliberately a bare intersection (no `Expand`/`Simplify`)—flattening
|
|
271
267
|
* it with a homomorphic mapped type collapses to `{ [x: string]: any }` when
|
|
272
268
|
* `Document` is still an unresolved generic, which breaks structural
|
|
273
269
|
* comparability in the database reader/writer plumbing.
|
package/src/Table.ts
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import * as Lazy from "./Lazy";
|
|
2
|
+
import * as SystemFields from "./SystemFields";
|
|
3
|
+
import type { TableSchemaToTableValidator } from "./SchemaToValidator";
|
|
4
|
+
import type {
|
|
5
|
+
SystemFields as ConvexSystemFields,
|
|
6
|
+
Expand,
|
|
7
|
+
GenericTableIndexes,
|
|
8
|
+
GenericTableSearchIndexes,
|
|
9
|
+
GenericTableVectorIndexes,
|
|
10
|
+
IndexTiebreakerField,
|
|
11
|
+
SearchIndexConfig,
|
|
12
|
+
VectorIndexConfig,
|
|
13
|
+
} from "convex/server";
|
|
14
|
+
import type { GenericValidator, Validator } from "convex/values";
|
|
15
|
+
import * as Predicate from "effect/Predicate";
|
|
16
|
+
import type * as Schema from "effect/Schema";
|
|
17
|
+
|
|
18
|
+
export const TypeId = "~@confect/core/Table";
|
|
19
|
+
export type TypeId = typeof TypeId;
|
|
20
|
+
|
|
21
|
+
// -----------------------------------------------------------------------------
|
|
22
|
+
// Predicates
|
|
23
|
+
// -----------------------------------------------------------------------------
|
|
24
|
+
//
|
|
25
|
+
// Both bound `Table`s and `UnnamedTable` callables share the same `[TypeId]`
|
|
26
|
+
// brand. They disambiguate by whether a `tableName` property is set: bound
|
|
27
|
+
// tables have one, unnamed callables do not.
|
|
28
|
+
//
|
|
29
|
+
// The discriminator is `tableName` (not `name`) so it does not collide with
|
|
30
|
+
// the built-in `Function.prototype.name` that every JS function carries.
|
|
31
|
+
|
|
32
|
+
export const isTable = (u: unknown): u is Any =>
|
|
33
|
+
Predicate.hasProperty(u, TypeId) && Predicate.hasProperty(u, "tableName");
|
|
34
|
+
|
|
35
|
+
export const isUnnamedTable = (u: unknown): u is UnnamedAny =>
|
|
36
|
+
Predicate.hasProperty(u, TypeId) && !Predicate.hasProperty(u, "tableName");
|
|
37
|
+
|
|
38
|
+
// -----------------------------------------------------------------------------
|
|
39
|
+
// Bound Table
|
|
40
|
+
// -----------------------------------------------------------------------------
|
|
41
|
+
//
|
|
42
|
+
// A bound `Table` is the client-safe document schema: `Fields`, `Doc`, and
|
|
43
|
+
// the index metadata records. The Convex deploy `tableDefinition` (which
|
|
44
|
+
// value-imports `convex/server`) is produced separately by
|
|
45
|
+
// `@confect/server/Table.tableDefinition`.
|
|
46
|
+
|
|
47
|
+
export interface Table<
|
|
48
|
+
Name_ extends string,
|
|
49
|
+
TableSchema_ extends Schema.Codec<any, any>,
|
|
50
|
+
TableValidator_ extends GenericValidator =
|
|
51
|
+
TableSchemaToTableValidator<TableSchema_>,
|
|
52
|
+
Indexes_ extends GenericTableIndexes = {},
|
|
53
|
+
SearchIndexes_ extends GenericTableSearchIndexes = {},
|
|
54
|
+
VectorIndexes_ extends GenericTableVectorIndexes = {},
|
|
55
|
+
> {
|
|
56
|
+
readonly [TypeId]: TypeId;
|
|
57
|
+
readonly tableName: Name_;
|
|
58
|
+
readonly Fields: TableSchema_;
|
|
59
|
+
readonly Doc: SystemFields.ExtendWithSystemFields<Name_, TableSchema_>;
|
|
60
|
+
readonly indexes: Indexes_;
|
|
61
|
+
readonly searchIndexes: SearchIndexes_;
|
|
62
|
+
readonly vectorIndexes: VectorIndexes_;
|
|
63
|
+
readonly "~TableValidator": TableValidator_;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface Any {
|
|
67
|
+
readonly [TypeId]: TypeId;
|
|
68
|
+
readonly tableName: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type AnyWithProps = Table<
|
|
72
|
+
any,
|
|
73
|
+
Schema.Codec<any, any>,
|
|
74
|
+
GenericValidator,
|
|
75
|
+
GenericTableIndexes,
|
|
76
|
+
GenericTableSearchIndexes,
|
|
77
|
+
GenericTableVectorIndexes
|
|
78
|
+
>;
|
|
79
|
+
|
|
80
|
+
// -----------------------------------------------------------------------------
|
|
81
|
+
// UnnamedTable (callable)
|
|
82
|
+
// -----------------------------------------------------------------------------
|
|
83
|
+
//
|
|
84
|
+
// `Table.make(lazyFields)` returns an `UnnamedTable`: a callable that
|
|
85
|
+
// produces a fully bound `Table` when invoked with a name. Chaining methods
|
|
86
|
+
// (`.index`, `.searchIndex`, `.vectorIndex`) live here and return new
|
|
87
|
+
// `UnnamedTable`s, accumulating plain index metadata records. The
|
|
88
|
+
// user-supplied `lazyFields` callback is just carried through until first
|
|
89
|
+
// access of `Fields`/`Doc` on a bound table. The codegen pipeline emits a
|
|
90
|
+
// wrapper file per user-authored table that simply invokes the unnamed
|
|
91
|
+
// callable with the filename basename.
|
|
92
|
+
|
|
93
|
+
export interface UnnamedTable<
|
|
94
|
+
TableSchema_ extends Schema.Codec<any, any>,
|
|
95
|
+
TableValidator_ extends GenericValidator =
|
|
96
|
+
TableSchemaToTableValidator<TableSchema_>,
|
|
97
|
+
Indexes_ extends GenericTableIndexes = {},
|
|
98
|
+
SearchIndexes_ extends GenericTableSearchIndexes = {},
|
|
99
|
+
VectorIndexes_ extends GenericTableVectorIndexes = {},
|
|
100
|
+
> {
|
|
101
|
+
<const Name_ extends string>(
|
|
102
|
+
tableName: Name_,
|
|
103
|
+
): Table<
|
|
104
|
+
Name_,
|
|
105
|
+
TableSchema_,
|
|
106
|
+
TableValidator_,
|
|
107
|
+
Indexes_,
|
|
108
|
+
SearchIndexes_,
|
|
109
|
+
VectorIndexes_
|
|
110
|
+
>;
|
|
111
|
+
|
|
112
|
+
readonly [TypeId]: TypeId;
|
|
113
|
+
readonly indexes: Indexes_;
|
|
114
|
+
readonly searchIndexes: SearchIndexes_;
|
|
115
|
+
readonly vectorIndexes: VectorIndexes_;
|
|
116
|
+
readonly "~TableValidator": TableValidator_;
|
|
117
|
+
|
|
118
|
+
index<
|
|
119
|
+
IndexName extends string,
|
|
120
|
+
FirstFieldPath extends ExtractFieldPaths<TableValidator_>,
|
|
121
|
+
RestFieldPaths extends ExtractFieldPaths<TableValidator_>[],
|
|
122
|
+
>(
|
|
123
|
+
name: IndexName,
|
|
124
|
+
fields: [FirstFieldPath, ...RestFieldPaths],
|
|
125
|
+
): UnnamedTable<
|
|
126
|
+
TableSchema_,
|
|
127
|
+
TableValidator_,
|
|
128
|
+
Expand<
|
|
129
|
+
Indexes_ &
|
|
130
|
+
Record<
|
|
131
|
+
IndexName,
|
|
132
|
+
[FirstFieldPath, ...RestFieldPaths, IndexTiebreakerField]
|
|
133
|
+
>
|
|
134
|
+
>,
|
|
135
|
+
SearchIndexes_,
|
|
136
|
+
VectorIndexes_
|
|
137
|
+
>;
|
|
138
|
+
|
|
139
|
+
searchIndex<
|
|
140
|
+
IndexName extends string,
|
|
141
|
+
SearchField extends ExtractFieldPaths<TableValidator_>,
|
|
142
|
+
FilterFields extends ExtractFieldPaths<TableValidator_> = never,
|
|
143
|
+
>(
|
|
144
|
+
name: IndexName,
|
|
145
|
+
indexConfig: Expand<SearchIndexConfig<SearchField, FilterFields>>,
|
|
146
|
+
): UnnamedTable<
|
|
147
|
+
TableSchema_,
|
|
148
|
+
TableValidator_,
|
|
149
|
+
Indexes_,
|
|
150
|
+
Expand<
|
|
151
|
+
SearchIndexes_ &
|
|
152
|
+
Record<
|
|
153
|
+
IndexName,
|
|
154
|
+
{
|
|
155
|
+
searchField: SearchField;
|
|
156
|
+
filterFields: FilterFields;
|
|
157
|
+
}
|
|
158
|
+
>
|
|
159
|
+
>,
|
|
160
|
+
VectorIndexes_
|
|
161
|
+
>;
|
|
162
|
+
|
|
163
|
+
vectorIndex<
|
|
164
|
+
IndexName extends string,
|
|
165
|
+
VectorField extends ExtractFieldPaths<TableValidator_>,
|
|
166
|
+
FilterFields extends ExtractFieldPaths<TableValidator_> = never,
|
|
167
|
+
>(
|
|
168
|
+
name: IndexName,
|
|
169
|
+
indexConfig: Expand<VectorIndexConfig<VectorField, FilterFields>>,
|
|
170
|
+
): UnnamedTable<
|
|
171
|
+
TableSchema_,
|
|
172
|
+
TableValidator_,
|
|
173
|
+
Indexes_,
|
|
174
|
+
SearchIndexes_,
|
|
175
|
+
Expand<
|
|
176
|
+
VectorIndexes_ &
|
|
177
|
+
Record<
|
|
178
|
+
IndexName,
|
|
179
|
+
{
|
|
180
|
+
vectorField: VectorField;
|
|
181
|
+
dimensions: number;
|
|
182
|
+
filterFields: FilterFields;
|
|
183
|
+
}
|
|
184
|
+
>
|
|
185
|
+
>
|
|
186
|
+
>;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export interface UnnamedAny {
|
|
190
|
+
readonly [TypeId]: TypeId;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export type UnnamedAnyWithProps = UnnamedTable<
|
|
194
|
+
Schema.Codec<any, any>,
|
|
195
|
+
GenericValidator,
|
|
196
|
+
GenericTableIndexes,
|
|
197
|
+
GenericTableSearchIndexes,
|
|
198
|
+
GenericTableVectorIndexes
|
|
199
|
+
>;
|
|
200
|
+
|
|
201
|
+
// -----------------------------------------------------------------------------
|
|
202
|
+
// Type extractors
|
|
203
|
+
// -----------------------------------------------------------------------------
|
|
204
|
+
|
|
205
|
+
export type Name<TableDef extends AnyWithProps> = TableDef["tableName"] &
|
|
206
|
+
string;
|
|
207
|
+
|
|
208
|
+
export type TableSchema<TableDef extends AnyWithProps> = TableDef["Fields"];
|
|
209
|
+
|
|
210
|
+
export type TableValidator<TableDef extends AnyWithProps> =
|
|
211
|
+
TableDef["~TableValidator"];
|
|
212
|
+
|
|
213
|
+
export type Indexes<TableDef extends AnyWithProps> = TableDef["indexes"];
|
|
214
|
+
|
|
215
|
+
export type SearchIndexes<TableDef extends AnyWithProps> =
|
|
216
|
+
TableDef["searchIndexes"];
|
|
217
|
+
|
|
218
|
+
export type VectorIndexes<TableDef extends AnyWithProps> =
|
|
219
|
+
TableDef["vectorIndexes"];
|
|
220
|
+
|
|
221
|
+
export type Doc<TableDef extends AnyWithProps> = TableDef["Doc"];
|
|
222
|
+
|
|
223
|
+
export type Fields<TableDef extends AnyWithProps> = TableDef["Fields"];
|
|
224
|
+
|
|
225
|
+
export type WithName<
|
|
226
|
+
TableDef extends AnyWithProps,
|
|
227
|
+
Name_ extends string,
|
|
228
|
+
> = TableDef extends { readonly tableName: Name_ } ? TableDef : never;
|
|
229
|
+
|
|
230
|
+
export type TablesRecord<Tables extends AnyWithProps> = {
|
|
231
|
+
readonly [TableName_ in Name<Tables>]: WithName<Tables, TableName_>;
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// -----------------------------------------------------------------------------
|
|
235
|
+
// Construction
|
|
236
|
+
// -----------------------------------------------------------------------------
|
|
237
|
+
//
|
|
238
|
+
// `make` only stores the user-supplied `lazyFields` callback alongside any
|
|
239
|
+
// chained index metadata. Neither `Fields` nor `Doc` is constructed until
|
|
240
|
+
// first access on a bound `Table`. Each chain step is O(1) (plain object
|
|
241
|
+
// spread of the metadata records) and never invokes the callback. Binding
|
|
242
|
+
// via `unnamed(tableName)` installs lazy memoised getters for `Fields` and
|
|
243
|
+
// `Doc` via `Lazy.defineProperty`, so the first access materialises the
|
|
244
|
+
// value and replaces the getter with a plain data property—second-and-
|
|
245
|
+
// subsequent accesses are observably indistinguishable from a plain
|
|
246
|
+
// property and avoid all function-call overhead.
|
|
247
|
+
|
|
248
|
+
interface UnnamedState<
|
|
249
|
+
TableSchema_ extends Schema.Codec<any, any>,
|
|
250
|
+
Indexes_ extends GenericTableIndexes,
|
|
251
|
+
SearchIndexes_ extends GenericTableSearchIndexes,
|
|
252
|
+
VectorIndexes_ extends GenericTableVectorIndexes,
|
|
253
|
+
> {
|
|
254
|
+
readonly lazyFields: () => TableSchema_;
|
|
255
|
+
readonly indexes: Indexes_;
|
|
256
|
+
readonly searchIndexes: SearchIndexes_;
|
|
257
|
+
readonly vectorIndexes: VectorIndexes_;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const makeBound = <
|
|
261
|
+
Name_ extends string,
|
|
262
|
+
TableSchema_ extends Schema.Codec<any, any>,
|
|
263
|
+
TableValidator_ extends Validator<any, any, any>,
|
|
264
|
+
Indexes_ extends GenericTableIndexes,
|
|
265
|
+
SearchIndexes_ extends GenericTableSearchIndexes,
|
|
266
|
+
VectorIndexes_ extends GenericTableVectorIndexes,
|
|
267
|
+
>(
|
|
268
|
+
tableName: Name_,
|
|
269
|
+
state: UnnamedState<TableSchema_, Indexes_, SearchIndexes_, VectorIndexes_>,
|
|
270
|
+
): Table<
|
|
271
|
+
Name_,
|
|
272
|
+
TableSchema_,
|
|
273
|
+
TableValidator_,
|
|
274
|
+
Indexes_,
|
|
275
|
+
SearchIndexes_,
|
|
276
|
+
VectorIndexes_
|
|
277
|
+
> => {
|
|
278
|
+
const bound = {
|
|
279
|
+
[TypeId]: TypeId as TypeId,
|
|
280
|
+
tableName,
|
|
281
|
+
indexes: state.indexes,
|
|
282
|
+
searchIndexes: state.searchIndexes,
|
|
283
|
+
vectorIndexes: state.vectorIndexes,
|
|
284
|
+
} as Table<
|
|
285
|
+
Name_,
|
|
286
|
+
TableSchema_,
|
|
287
|
+
TableValidator_,
|
|
288
|
+
Indexes_,
|
|
289
|
+
SearchIndexes_,
|
|
290
|
+
VectorIndexes_
|
|
291
|
+
>;
|
|
292
|
+
|
|
293
|
+
Lazy.defineProperty(bound, "Fields", () => state.lazyFields());
|
|
294
|
+
|
|
295
|
+
Lazy.defineProperty(bound, "Doc", () =>
|
|
296
|
+
SystemFields.extendWithSystemFields(
|
|
297
|
+
tableName,
|
|
298
|
+
(bound as { Fields: TableSchema_ }).Fields,
|
|
299
|
+
),
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
return bound;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const makeUnnamed = <
|
|
306
|
+
TableSchema_ extends Schema.Codec<any, any>,
|
|
307
|
+
TableValidator_ extends Validator<any, any, any>,
|
|
308
|
+
Indexes_ extends GenericTableIndexes,
|
|
309
|
+
SearchIndexes_ extends GenericTableSearchIndexes,
|
|
310
|
+
VectorIndexes_ extends GenericTableVectorIndexes,
|
|
311
|
+
>(
|
|
312
|
+
state: UnnamedState<TableSchema_, Indexes_, SearchIndexes_, VectorIndexes_>,
|
|
313
|
+
): UnnamedTable<
|
|
314
|
+
TableSchema_,
|
|
315
|
+
TableValidator_,
|
|
316
|
+
Indexes_,
|
|
317
|
+
SearchIndexes_,
|
|
318
|
+
VectorIndexes_
|
|
319
|
+
> => {
|
|
320
|
+
type UnnamedTable_ = UnnamedTable<
|
|
321
|
+
TableSchema_,
|
|
322
|
+
TableValidator_,
|
|
323
|
+
Indexes_,
|
|
324
|
+
SearchIndexes_,
|
|
325
|
+
VectorIndexes_
|
|
326
|
+
>;
|
|
327
|
+
|
|
328
|
+
type UnnamedTableFunction<FunctionName extends keyof UnnamedTable_> =
|
|
329
|
+
UnnamedTable_[FunctionName];
|
|
330
|
+
|
|
331
|
+
const bind = <const Name_ extends string>(
|
|
332
|
+
tableName: Name_,
|
|
333
|
+
): Table<
|
|
334
|
+
Name_,
|
|
335
|
+
TableSchema_,
|
|
336
|
+
TableValidator_,
|
|
337
|
+
Indexes_,
|
|
338
|
+
SearchIndexes_,
|
|
339
|
+
VectorIndexes_
|
|
340
|
+
> =>
|
|
341
|
+
makeBound<
|
|
342
|
+
Name_,
|
|
343
|
+
TableSchema_,
|
|
344
|
+
TableValidator_,
|
|
345
|
+
Indexes_,
|
|
346
|
+
SearchIndexes_,
|
|
347
|
+
VectorIndexes_
|
|
348
|
+
>(tableName, state);
|
|
349
|
+
|
|
350
|
+
const index: UnnamedTableFunction<"index"> = (name, fields) =>
|
|
351
|
+
makeUnnamed({
|
|
352
|
+
lazyFields: state.lazyFields,
|
|
353
|
+
indexes: {
|
|
354
|
+
...state.indexes,
|
|
355
|
+
[name]: fields,
|
|
356
|
+
} as any,
|
|
357
|
+
searchIndexes: state.searchIndexes,
|
|
358
|
+
vectorIndexes: state.vectorIndexes,
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
const searchIndex: UnnamedTableFunction<"searchIndex"> = (
|
|
362
|
+
name,
|
|
363
|
+
indexConfig,
|
|
364
|
+
) =>
|
|
365
|
+
makeUnnamed({
|
|
366
|
+
lazyFields: state.lazyFields,
|
|
367
|
+
indexes: state.indexes,
|
|
368
|
+
searchIndexes: {
|
|
369
|
+
...state.searchIndexes,
|
|
370
|
+
[name]: indexConfig,
|
|
371
|
+
} as any,
|
|
372
|
+
vectorIndexes: state.vectorIndexes,
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
const vectorIndex: UnnamedTableFunction<"vectorIndex"> = (
|
|
376
|
+
name,
|
|
377
|
+
indexConfig,
|
|
378
|
+
) =>
|
|
379
|
+
makeUnnamed({
|
|
380
|
+
lazyFields: state.lazyFields,
|
|
381
|
+
indexes: state.indexes,
|
|
382
|
+
searchIndexes: state.searchIndexes,
|
|
383
|
+
vectorIndexes: {
|
|
384
|
+
...state.vectorIndexes,
|
|
385
|
+
[name]: indexConfig,
|
|
386
|
+
} as any,
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
return Object.assign(bind, {
|
|
390
|
+
[TypeId]: TypeId as TypeId,
|
|
391
|
+
indexes: state.indexes,
|
|
392
|
+
searchIndexes: state.searchIndexes,
|
|
393
|
+
vectorIndexes: state.vectorIndexes,
|
|
394
|
+
index,
|
|
395
|
+
searchIndex,
|
|
396
|
+
vectorIndex,
|
|
397
|
+
}) as UnnamedTable_;
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
export const make = <const TableSchema_ extends Schema.Codec<any, any>>(
|
|
401
|
+
lazyFields: () => TableSchema_,
|
|
402
|
+
): UnnamedTable<TableSchema_, TableSchemaToTableValidator<TableSchema_>> => {
|
|
403
|
+
type TableValidator_ = TableSchemaToTableValidator<TableSchema_>;
|
|
404
|
+
type UnnamedTable_ = UnnamedTable<TableSchema_, TableValidator_>;
|
|
405
|
+
|
|
406
|
+
return makeUnnamed<TableSchema_, TableValidator_, {}, {}, {}>({
|
|
407
|
+
lazyFields,
|
|
408
|
+
indexes: {},
|
|
409
|
+
searchIndexes: {},
|
|
410
|
+
vectorIndexes: {},
|
|
411
|
+
}) as UnnamedTable_;
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
// Vendored types from convex-js, partially modified. Ideally we could use these directly. See https://github.com/get-convex/convex-js/pull/14
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Extract all of the index field paths within a Convex `Validator`.
|
|
418
|
+
*
|
|
419
|
+
* This is used when declaring table indexes.
|
|
420
|
+
*
|
|
421
|
+
* @public
|
|
422
|
+
*/
|
|
423
|
+
type ExtractFieldPaths<T extends Validator<any, any, any>> =
|
|
424
|
+
// Add in the system fields available in index definitions.
|
|
425
|
+
// This should be everything except for `_id` because thats added to indexes
|
|
426
|
+
// automatically.
|
|
427
|
+
T["fieldPaths"] | keyof ConvexSystemFields;
|