@confect/core 9.2.3 → 10.0.0-next.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 +15 -2
- package/dist/FunctionProvenance.d.ts +30 -30
- package/dist/FunctionProvenance.d.ts.map +1 -1
- package/dist/FunctionProvenance.js.map +1 -1
- package/dist/FunctionSpec.d.ts +14 -14
- package/dist/FunctionSpec.d.ts.map +1 -1
- package/dist/FunctionSpec.js.map +1 -1
- package/dist/GenericId.d.ts +2 -2
- package/dist/GenericId.d.ts.map +1 -1
- package/dist/GenericId.js +4 -3
- package/dist/GenericId.js.map +1 -1
- package/dist/PaginationResult.d.ts +6 -10
- package/dist/PaginationResult.d.ts.map +1 -1
- package/dist/PaginationResult.js +6 -2
- package/dist/PaginationResult.js.map +1 -1
- package/dist/Ref.d.ts +5 -5
- package/dist/Ref.d.ts.map +1 -1
- package/dist/Ref.js +10 -10
- package/dist/Ref.js.map +1 -1
- package/dist/Refs.js +1 -1
- package/dist/Registry.d.ts +1 -4
- package/dist/Registry.d.ts.map +1 -1
- package/dist/Registry.js +1 -1
- package/dist/Registry.js.map +1 -1
- package/dist/SystemFields.d.ts +51 -10
- package/dist/SystemFields.d.ts.map +1 -1
- package/dist/SystemFields.js +79 -2
- package/dist/SystemFields.js.map +1 -1
- package/dist/UserIdentity.d.ts +20 -54
- package/dist/UserIdentity.d.ts.map +1 -1
- package/dist/UserIdentity.js +17 -17
- package/dist/UserIdentity.js.map +1 -1
- package/dist/tsconfig.src.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/FunctionProvenance.ts +12 -12
- package/src/FunctionSpec.ts +9 -9
- package/src/GenericId.ts +7 -7
- package/src/PaginationResult.ts +5 -8
- package/src/Ref.ts +21 -16
- package/src/Registry.ts +3 -3
- package/src/SystemFields.ts +255 -26
- package/src/UserIdentity.ts +17 -17
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@confect/core",
|
|
3
3
|
"description": "Shared specs and schemas used by all Confect packages",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "10.0.0-next.0",
|
|
5
5
|
"author": "RJ Dellecese",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/rjdellecese/confect/issues"
|
|
8
8
|
},
|
|
9
9
|
"devDependencies": {
|
|
10
10
|
"@ark/attest": "0.56.0",
|
|
11
|
-
"@effect/vitest": "0.
|
|
11
|
+
"@effect/vitest": "4.0.0-beta.97",
|
|
12
12
|
"@types/node": "25.3.3",
|
|
13
|
-
"effect": "
|
|
13
|
+
"effect": "4.0.0-beta.97",
|
|
14
14
|
"tsdown": "0.20.3",
|
|
15
15
|
"tsx": "4.21.0",
|
|
16
16
|
"typescript": "5.9.3",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"module": "./dist/index.js",
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"convex": "^1.32.0",
|
|
52
|
-
"effect": "^
|
|
52
|
+
"effect": "^4.0.0-beta.97"
|
|
53
53
|
},
|
|
54
54
|
"repository": {
|
|
55
55
|
"type": "git",
|
|
@@ -5,9 +5,9 @@ import * as Lazy from "./Lazy";
|
|
|
5
5
|
|
|
6
6
|
export type FunctionProvenance = Data.TaggedEnum<{
|
|
7
7
|
Confect: {
|
|
8
|
-
args: Schema.
|
|
9
|
-
returns: Schema.
|
|
10
|
-
error?: Schema.
|
|
8
|
+
args: Schema.Codec<any, any>;
|
|
9
|
+
returns: Schema.Codec<any, any>;
|
|
10
|
+
error?: Schema.Codec<any, any>;
|
|
11
11
|
};
|
|
12
12
|
Convex: {
|
|
13
13
|
/** @internal */
|
|
@@ -18,9 +18,9 @@ export type FunctionProvenance = Data.TaggedEnum<{
|
|
|
18
18
|
}>;
|
|
19
19
|
|
|
20
20
|
export interface Confect<
|
|
21
|
-
Args extends Schema.
|
|
22
|
-
Returns extends Schema.
|
|
23
|
-
Error extends Schema.
|
|
21
|
+
Args extends Schema.Codec<any, any>,
|
|
22
|
+
Returns extends Schema.Codec<any, any>,
|
|
23
|
+
Error extends Schema.Codec<any, any> = never,
|
|
24
24
|
> {
|
|
25
25
|
readonly _tag: "Confect";
|
|
26
26
|
readonly args: Args;
|
|
@@ -29,9 +29,9 @@ export interface Confect<
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export interface AnyConfect extends Confect<
|
|
32
|
-
Schema.
|
|
33
|
-
Schema.
|
|
34
|
-
Schema.
|
|
32
|
+
Schema.Codec<any, any>,
|
|
33
|
+
Schema.Codec<any, any>,
|
|
34
|
+
Schema.Codec<any, any>
|
|
35
35
|
> {}
|
|
36
36
|
|
|
37
37
|
export interface Convex<Args extends DefaultFunctionArgs, Returns> {
|
|
@@ -61,9 +61,9 @@ export const FunctionProvenance = Data.taggedEnum<FunctionProvenance>();
|
|
|
61
61
|
* relies on `Data`'s structural `Equal`/`Hash` for provenance values.
|
|
62
62
|
*/
|
|
63
63
|
export const Confect = <
|
|
64
|
-
Args extends Schema.
|
|
65
|
-
Returns extends Schema.
|
|
66
|
-
Error extends Schema.
|
|
64
|
+
Args extends Schema.Codec<any, any>,
|
|
65
|
+
Returns extends Schema.Codec<any, any>,
|
|
66
|
+
Error extends Schema.Codec<any, any> = never,
|
|
67
67
|
>(
|
|
68
68
|
args: () => Args,
|
|
69
69
|
returns: () => Returns,
|
package/src/FunctionSpec.ts
CHANGED
|
@@ -93,7 +93,7 @@ export type Name<FunctionSpec_ extends AnyWithProps> = FunctionSpec_["name"];
|
|
|
93
93
|
export type Args<FunctionSpec_ extends AnyWithProps> = FunctionSpec_ extends {
|
|
94
94
|
functionProvenance: {
|
|
95
95
|
_tag: "Confect";
|
|
96
|
-
args: infer ArgsSchema_ extends Schema.
|
|
96
|
+
args: infer ArgsSchema_ extends Schema.Codec<any, any>;
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
99
|
? ArgsSchema_["Type"]
|
|
@@ -107,7 +107,7 @@ export type Returns<FunctionSpec_ extends AnyWithProps> =
|
|
|
107
107
|
FunctionSpec_ extends {
|
|
108
108
|
functionProvenance: {
|
|
109
109
|
_tag: "Confect";
|
|
110
|
-
returns: infer ReturnsSchema_ extends Schema.
|
|
110
|
+
returns: infer ReturnsSchema_ extends Schema.Codec<any, any>;
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
? ReturnsSchema_["Type"]
|
|
@@ -121,7 +121,7 @@ export type EncodedArgs<FunctionSpec_ extends AnyWithProps> =
|
|
|
121
121
|
FunctionSpec_ extends {
|
|
122
122
|
functionProvenance: {
|
|
123
123
|
_tag: "Confect";
|
|
124
|
-
args: infer ArgsSchema_ extends Schema.
|
|
124
|
+
args: infer ArgsSchema_ extends Schema.Codec<any, any>;
|
|
125
125
|
};
|
|
126
126
|
}
|
|
127
127
|
? ArgsSchema_["Encoded"]
|
|
@@ -135,7 +135,7 @@ export type EncodedReturns<FunctionSpec_ extends AnyWithProps> =
|
|
|
135
135
|
FunctionSpec_ extends {
|
|
136
136
|
functionProvenance: {
|
|
137
137
|
_tag: "Confect";
|
|
138
|
-
returns: infer ReturnsSchema_ extends Schema.
|
|
138
|
+
returns: infer ReturnsSchema_ extends Schema.Codec<any, any>;
|
|
139
139
|
};
|
|
140
140
|
}
|
|
141
141
|
? ReturnsSchema_["Encoded"]
|
|
@@ -149,7 +149,7 @@ export type Error<FunctionSpec_ extends AnyWithProps> = FunctionSpec_ extends {
|
|
|
149
149
|
functionProvenance: FunctionProvenance.Confect<
|
|
150
150
|
any,
|
|
151
151
|
any,
|
|
152
|
-
infer ErrorSchema_ extends Schema.
|
|
152
|
+
infer ErrorSchema_ extends Schema.Codec<any, any>
|
|
153
153
|
>;
|
|
154
154
|
}
|
|
155
155
|
? ErrorSchema_["Type"]
|
|
@@ -160,7 +160,7 @@ export type EncodedError<FunctionSpec_ extends AnyWithProps> =
|
|
|
160
160
|
functionProvenance: FunctionProvenance.Confect<
|
|
161
161
|
any,
|
|
162
162
|
any,
|
|
163
|
-
infer ErrorSchema_ extends Schema.
|
|
163
|
+
infer ErrorSchema_ extends Schema.Codec<any, any>
|
|
164
164
|
>;
|
|
165
165
|
}
|
|
166
166
|
? ErrorSchema_["Encoded"]
|
|
@@ -215,9 +215,9 @@ const make =
|
|
|
215
215
|
) =>
|
|
216
216
|
<
|
|
217
217
|
const Name_ extends string,
|
|
218
|
-
Args_ extends Schema.
|
|
219
|
-
Returns_ extends Schema.
|
|
220
|
-
Error_ extends Schema.
|
|
218
|
+
Args_ extends Schema.Codec<any, any>,
|
|
219
|
+
Returns_ extends Schema.Codec<any, any>,
|
|
220
|
+
Error_ extends Schema.Codec<any, any> = never,
|
|
221
221
|
>({
|
|
222
222
|
name,
|
|
223
223
|
args,
|
package/src/GenericId.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import type { GenericId as ConvexGenericId } from "convex/values";
|
|
2
|
-
import
|
|
2
|
+
import * as Option from "effect/Option";
|
|
3
3
|
import * as Schema from "effect/Schema";
|
|
4
4
|
import * as SchemaAST from "effect/SchemaAST";
|
|
5
5
|
|
|
6
|
-
const ConvexId =
|
|
6
|
+
const ConvexId = "~@confect/core/ConvexId";
|
|
7
7
|
|
|
8
8
|
export const GenericId = <TableName extends string>(
|
|
9
9
|
tableName: TableName,
|
|
10
|
-
): Schema.
|
|
11
|
-
Schema.String.
|
|
12
|
-
|
|
13
|
-
) as unknown as Schema.
|
|
10
|
+
): Schema.Codec<ConvexGenericId<TableName>> =>
|
|
11
|
+
Schema.String.annotate({
|
|
12
|
+
[ConvexId]: tableName,
|
|
13
|
+
}) as unknown as Schema.Codec<ConvexGenericId<TableName>>;
|
|
14
14
|
|
|
15
15
|
export type GenericId<TableName extends string> = ConvexGenericId<TableName>;
|
|
16
16
|
|
|
17
17
|
export const tableName = <TableName extends string>(
|
|
18
18
|
ast: SchemaAST.AST,
|
|
19
19
|
): Option.Option<TableName> =>
|
|
20
|
-
SchemaAST.
|
|
20
|
+
Option.fromNullishOr(SchemaAST.resolveAt<TableName>(ConvexId)(ast));
|
package/src/PaginationResult.ts
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
import * as Schema from "effect/Schema";
|
|
2
2
|
|
|
3
|
-
export const PaginationResult = <Doc extends Schema.
|
|
3
|
+
export const PaginationResult = <Doc extends Schema.Codec<any, any>>(
|
|
4
4
|
Doc: Doc,
|
|
5
5
|
) =>
|
|
6
6
|
Schema.Struct({
|
|
7
7
|
page: Schema.mutable(Schema.Array(Doc)),
|
|
8
8
|
isDone: Schema.Boolean,
|
|
9
9
|
continueCursor: Schema.String,
|
|
10
|
-
splitCursor: Schema.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
pageStatus: Schema.optionalWith(
|
|
14
|
-
Schema.Union(
|
|
10
|
+
splitCursor: Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])),
|
|
11
|
+
pageStatus: Schema.optionalKey(
|
|
12
|
+
Schema.Union([
|
|
15
13
|
Schema.Literal("SplitRecommended"),
|
|
16
14
|
Schema.Literal("SplitRequired"),
|
|
17
15
|
Schema.Null,
|
|
18
|
-
),
|
|
19
|
-
{ exact: true },
|
|
16
|
+
]),
|
|
20
17
|
),
|
|
21
18
|
});
|
package/src/Ref.ts
CHANGED
|
@@ -5,7 +5,6 @@ import type {
|
|
|
5
5
|
import { makeFunctionReference } from "convex/server";
|
|
6
6
|
import type { Value } from "convex/values";
|
|
7
7
|
import { ConvexError } from "convex/values";
|
|
8
|
-
import type { ParseResult } from "effect";
|
|
9
8
|
import * as Effect from "effect/Effect";
|
|
10
9
|
import * as Match from "effect/Match";
|
|
11
10
|
import * as Option from "effect/Option";
|
|
@@ -224,10 +223,10 @@ export const hasErrorSchema = (ref: Any): boolean =>
|
|
|
224
223
|
export const encodeArgs = <Ref_ extends Any>(
|
|
225
224
|
ref: Ref_,
|
|
226
225
|
args: Args<Ref_>,
|
|
227
|
-
): Effect.Effect<unknown,
|
|
226
|
+
): Effect.Effect<unknown, Schema.SchemaError> =>
|
|
228
227
|
Match.value(ref.functionSpec.functionProvenance).pipe(
|
|
229
228
|
Match.tag("Confect", (confectFunctionProvenance) =>
|
|
230
|
-
Schema.
|
|
229
|
+
Schema.encodeEffect(confectFunctionProvenance.args)(args),
|
|
231
230
|
),
|
|
232
231
|
Match.tag("Convex", () => Effect.succeed(args)),
|
|
233
232
|
Match.exhaustive,
|
|
@@ -236,10 +235,10 @@ export const encodeArgs = <Ref_ extends Any>(
|
|
|
236
235
|
export const decodeReturns = <Ref_ extends Any>(
|
|
237
236
|
ref: Ref_,
|
|
238
237
|
returns: unknown,
|
|
239
|
-
): Effect.Effect<Returns<Ref_>,
|
|
238
|
+
): Effect.Effect<Returns<Ref_>, Schema.SchemaError> =>
|
|
240
239
|
Match.value(ref.functionSpec.functionProvenance).pipe(
|
|
241
240
|
Match.tag("Confect", (confectFunctionProvenance) =>
|
|
242
|
-
Schema.
|
|
241
|
+
Schema.decodeUnknownEffect(confectFunctionProvenance.returns)(returns),
|
|
243
242
|
),
|
|
244
243
|
Match.tag("Convex", () => Effect.succeed(returns)),
|
|
245
244
|
Match.exhaustive,
|
|
@@ -263,7 +262,7 @@ export const decodeArgsSync = <Ref_ extends Any>(
|
|
|
263
262
|
): Args<Ref_> =>
|
|
264
263
|
Match.value(ref.functionSpec.functionProvenance).pipe(
|
|
265
264
|
Match.tag("Confect", (confectFunctionProvenance) =>
|
|
266
|
-
Schema.
|
|
265
|
+
Schema.decodeUnknownSync(confectFunctionProvenance.args)(encodedArgs),
|
|
267
266
|
),
|
|
268
267
|
Match.tag("Convex", () => encodedArgs),
|
|
269
268
|
Match.exhaustive,
|
|
@@ -287,7 +286,9 @@ export const decodeReturnsSync = <Ref_ extends Any>(
|
|
|
287
286
|
): Returns<Ref_> =>
|
|
288
287
|
Match.value(ref.functionSpec.functionProvenance).pipe(
|
|
289
288
|
Match.tag("Confect", (confectFunctionProvenance) =>
|
|
290
|
-
Schema.
|
|
289
|
+
Schema.decodeUnknownSync(confectFunctionProvenance.returns)(
|
|
290
|
+
encodedReturns,
|
|
291
|
+
),
|
|
291
292
|
),
|
|
292
293
|
Match.tag("Convex", () => encodedReturns),
|
|
293
294
|
Match.exhaustive,
|
|
@@ -332,12 +333,14 @@ export const decodeErrorOrElse =
|
|
|
332
333
|
export const decodeError = <Ref_ extends Any>(
|
|
333
334
|
ref: Ref_,
|
|
334
335
|
encodedError: unknown,
|
|
335
|
-
): Effect.Effect<Option.Option<Error<Ref_>>,
|
|
336
|
+
): Effect.Effect<Option.Option<Error<Ref_>>, Schema.SchemaError> =>
|
|
336
337
|
Match.value(ref.functionSpec.functionProvenance).pipe(
|
|
337
338
|
Match.tag("Confect", (confectFunctionProvenance) =>
|
|
338
339
|
"error" in confectFunctionProvenance
|
|
339
340
|
? Effect.map(
|
|
340
|
-
Schema.
|
|
341
|
+
Schema.decodeUnknownEffect(confectFunctionProvenance.error)(
|
|
342
|
+
encodedError,
|
|
343
|
+
),
|
|
341
344
|
Option.some,
|
|
342
345
|
)
|
|
343
346
|
: Effect.succeed(Option.none<Error<Ref_>>()),
|
|
@@ -358,7 +361,7 @@ export const decodeErrorSync = <Ref_ extends Any>(
|
|
|
358
361
|
Match.tag("Confect", (confectFunctionProvenance) =>
|
|
359
362
|
"error" in confectFunctionProvenance
|
|
360
363
|
? Option.some(
|
|
361
|
-
Schema.
|
|
364
|
+
Schema.decodeUnknownSync(confectFunctionProvenance.error)(
|
|
362
365
|
encodedError,
|
|
363
366
|
) as Error<Ref_>,
|
|
364
367
|
)
|
|
@@ -376,7 +379,9 @@ export const maybeDecodeErrorSync = <Ref_ extends Any>(
|
|
|
376
379
|
? Match.value(ref.functionSpec.functionProvenance).pipe(
|
|
377
380
|
Match.tag("Confect", (confectFunctionProvenance) =>
|
|
378
381
|
"error" in confectFunctionProvenance
|
|
379
|
-
? Schema.
|
|
382
|
+
? Schema.decodeUnknownSync(confectFunctionProvenance.error)(
|
|
383
|
+
error.data,
|
|
384
|
+
)
|
|
380
385
|
: error,
|
|
381
386
|
),
|
|
382
387
|
Match.tag("Convex", () => error),
|
|
@@ -400,7 +405,7 @@ export const runWithCodec = <Ref_ extends Any, E = never>(
|
|
|
400
405
|
encodedArgs: unknown,
|
|
401
406
|
) => PromiseLike<unknown>,
|
|
402
407
|
mapUnknownError?: (error: unknown) => E,
|
|
403
|
-
): Effect.Effect<Returns<Ref_>, E | Error<Ref_> |
|
|
408
|
+
): Effect.Effect<Returns<Ref_>, E | Error<Ref_> | Schema.SchemaError> =>
|
|
404
409
|
Effect.gen(function* () {
|
|
405
410
|
const functionReference = getFunctionReference(ref);
|
|
406
411
|
const functionProvenance = ref.functionSpec.functionProvenance;
|
|
@@ -425,13 +430,13 @@ export const runWithCodec = <Ref_ extends Any, E = never>(
|
|
|
425
430
|
return yield* Match.value(functionProvenance).pipe(
|
|
426
431
|
Match.tag("Confect", (confectFunctionProvenance) =>
|
|
427
432
|
Effect.gen(function* () {
|
|
428
|
-
const encodedArgs = yield* Schema.
|
|
433
|
+
const encodedArgs = yield* Schema.encodeEffect(
|
|
429
434
|
confectFunctionProvenance.args,
|
|
430
435
|
)(args);
|
|
431
436
|
const encodedReturns = yield* invoke(encodedArgs);
|
|
432
|
-
return yield* Schema.
|
|
433
|
-
|
|
434
|
-
);
|
|
437
|
+
return yield* Schema.decodeUnknownEffect(
|
|
438
|
+
confectFunctionProvenance.returns,
|
|
439
|
+
)(encodedReturns);
|
|
435
440
|
}),
|
|
436
441
|
),
|
|
437
442
|
Match.tag("Convex", () => invoke(args)),
|
package/src/Registry.ts
CHANGED
|
@@ -22,9 +22,9 @@ export interface RegistryItems {
|
|
|
22
22
|
* Effect's global default-value cache to align two separately-defined tags
|
|
23
23
|
* by string key.
|
|
24
24
|
*/
|
|
25
|
-
export
|
|
25
|
+
export const Registry = Context.Reference<Ref.Ref<RegistryItems>>(
|
|
26
26
|
"@confect/core/Registry",
|
|
27
27
|
{
|
|
28
|
-
defaultValue: () => Ref.
|
|
28
|
+
defaultValue: () => Ref.makeUnsafe<RegistryItems>({}),
|
|
29
29
|
},
|
|
30
|
-
)
|
|
30
|
+
);
|
package/src/SystemFields.ts
CHANGED
|
@@ -1,51 +1,280 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import type { IdField, SystemFields as NonIdSystemFields } from "convex/server";
|
|
2
|
+
import { pipe } from "effect/Function";
|
|
3
|
+
import * as Array from "effect/Array";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Option from "effect/Option";
|
|
6
|
+
import * as Predicate from "effect/Predicate";
|
|
7
|
+
import * as Record from "effect/Record";
|
|
6
8
|
import * as Schema from "effect/Schema";
|
|
9
|
+
import * as SchemaAST from "effect/SchemaAST";
|
|
10
|
+
import * as SchemaGetter from "effect/SchemaGetter";
|
|
11
|
+
import * as SchemaTransformation from "effect/SchemaTransformation";
|
|
12
|
+
import * as Struct from "effect/Struct";
|
|
7
13
|
import * as GenericId from "./GenericId";
|
|
8
14
|
|
|
9
|
-
type SystemFieldsSchema<TableName extends string> = Schema.Struct<{
|
|
10
|
-
_id: Schema.Schema<
|
|
11
|
-
GenericId.GenericId<TableName>,
|
|
12
|
-
GenericId.GenericId<TableName>,
|
|
13
|
-
never
|
|
14
|
-
>;
|
|
15
|
-
_creationTime: typeof Schema.Number;
|
|
16
|
-
}>;
|
|
17
|
-
|
|
18
15
|
/**
|
|
19
|
-
* Produces a schema for Convex system fields.
|
|
16
|
+
* Produces a schema for Convex system fields. In Confect, system fields include `_id`.
|
|
20
17
|
*/
|
|
21
|
-
export const SystemFields = <TableName extends string>(
|
|
22
|
-
tableName: TableName,
|
|
23
|
-
): SystemFieldsSchema<TableName> =>
|
|
18
|
+
export const SystemFields = <TableName extends string>(tableName: TableName) =>
|
|
24
19
|
Schema.Struct({
|
|
25
20
|
_id: GenericId.GenericId(tableName),
|
|
26
21
|
_creationTime: Schema.Number,
|
|
27
22
|
});
|
|
28
23
|
|
|
24
|
+
/**
|
|
25
|
+
* The field map added to a table schema, derived from {@link SystemFields} so it
|
|
26
|
+
* stays in lockstep with the runtime schema.
|
|
27
|
+
*/
|
|
28
|
+
type SystemFieldsFor<TableName extends string> = ReturnType<
|
|
29
|
+
typeof SystemFields<TableName>
|
|
30
|
+
>["fields"];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The system field names, derived from {@link SystemFields} so they stay in
|
|
34
|
+
* lockstep with the schema.
|
|
35
|
+
*/
|
|
36
|
+
const systemFieldNames: ReadonlyArray<string> = Record.keys(
|
|
37
|
+
SystemFields("").fields,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const isSystemFieldKey = (key: string | symbol): key is string =>
|
|
41
|
+
Predicate.isString(key) && Array.contains(systemFieldNames, key);
|
|
42
|
+
|
|
43
|
+
const splitSystemFields = (input: { [key: PropertyKey]: unknown }) => {
|
|
44
|
+
const keys = Reflect.ownKeys(input);
|
|
45
|
+
const pick = (pickedKeys: ReadonlyArray<string | symbol>) =>
|
|
46
|
+
Record.fromEntries(
|
|
47
|
+
Array.map(pickedKeys, (key) => [key, input[key]] as const),
|
|
48
|
+
);
|
|
49
|
+
return {
|
|
50
|
+
system: pick(Array.filter(keys, isSystemFieldKey)),
|
|
51
|
+
rest: pick(Array.filter(keys, Predicate.not(isSystemFieldKey))),
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Wraps a transformation getter so the system fields bypass it: they are split
|
|
57
|
+
* off the input before the user-defined getter runs and merged back into its
|
|
58
|
+
* output. Transformations construct fresh objects, so without this the system
|
|
59
|
+
* fields would be dropped at every transformation step.
|
|
60
|
+
*/
|
|
61
|
+
const wrapGetter = (
|
|
62
|
+
getter: SchemaGetter.Getter<any, any, any>,
|
|
63
|
+
): SchemaGetter.Getter<any, any, any> =>
|
|
64
|
+
new SchemaGetter.Getter((input, options) => {
|
|
65
|
+
if (Option.isNone(input) || !Predicate.isObject(input.value)) {
|
|
66
|
+
return getter.run(input, options);
|
|
67
|
+
}
|
|
68
|
+
const { system, rest } = splitSystemFields(input.value);
|
|
69
|
+
return getter
|
|
70
|
+
.run(Option.some(rest), options)
|
|
71
|
+
.pipe(
|
|
72
|
+
Effect.map(
|
|
73
|
+
Option.map((output) =>
|
|
74
|
+
Predicate.isObject(output) ? Struct.assign(output, system) : output,
|
|
75
|
+
),
|
|
76
|
+
),
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const wrapTransformation = (
|
|
81
|
+
transformation: SchemaAST.Link["transformation"],
|
|
82
|
+
): SchemaTransformation.Transformation<any, any, any, any> => {
|
|
83
|
+
if (!SchemaTransformation.isTransformation(transformation)) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
"Cannot extend a table schema that uses decoding/encoding middleware with system fields",
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return new SchemaTransformation.Transformation(
|
|
89
|
+
wrapGetter(transformation.decode),
|
|
90
|
+
wrapGetter(transformation.encode),
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Builds an AST walker that adds the given system-field property signatures to
|
|
96
|
+
* every object node of a schema's AST — including each step of its encoding
|
|
97
|
+
* chain — wrapping transformations so the fields survive them.
|
|
98
|
+
*/
|
|
99
|
+
const makeExtendAst = (
|
|
100
|
+
systemPropertySignatures: ReadonlyArray<SchemaAST.PropertySignature>,
|
|
101
|
+
) => {
|
|
102
|
+
const extendEncoding = (encoding: SchemaAST.Encoding): SchemaAST.Encoding =>
|
|
103
|
+
Array.map(
|
|
104
|
+
encoding,
|
|
105
|
+
(link) =>
|
|
106
|
+
new SchemaAST.Link(
|
|
107
|
+
extendAst(link.to),
|
|
108
|
+
wrapTransformation(link.transformation),
|
|
109
|
+
),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const extendAst = (ast: SchemaAST.AST): SchemaAST.AST => {
|
|
113
|
+
if (SchemaAST.isUnion(ast)) {
|
|
114
|
+
return new SchemaAST.Union(
|
|
115
|
+
Array.map(ast.types, extendAst),
|
|
116
|
+
ast.mode,
|
|
117
|
+
ast.annotations,
|
|
118
|
+
ast.checks,
|
|
119
|
+
ast.encoding === undefined ? undefined : extendEncoding(ast.encoding),
|
|
120
|
+
ast.context,
|
|
121
|
+
ast.encodingChecks,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
if (SchemaAST.isSuspend(ast)) {
|
|
125
|
+
return new SchemaAST.Suspend(
|
|
126
|
+
() => extendAst(ast.thunk()),
|
|
127
|
+
ast.annotations,
|
|
128
|
+
undefined,
|
|
129
|
+
ast.encoding === undefined ? undefined : extendEncoding(ast.encoding),
|
|
130
|
+
ast.context,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
if (SchemaAST.isObjects(ast)) {
|
|
134
|
+
return new SchemaAST.Objects(
|
|
135
|
+
Array.appendAll(ast.propertySignatures, systemPropertySignatures),
|
|
136
|
+
ast.indexSignatures,
|
|
137
|
+
ast.annotations,
|
|
138
|
+
ast.checks,
|
|
139
|
+
ast.encoding === undefined ? undefined : extendEncoding(ast.encoding),
|
|
140
|
+
ast.context,
|
|
141
|
+
ast.encodingChecks,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
throw new Error(
|
|
145
|
+
ast._tag === "Declaration"
|
|
146
|
+
? "Cannot extend a `Declaration` schema (such as a `Schema.Class`) with system fields: its decoded values are constructed instances, which cannot gain extra fields. Use a plain `Schema.Struct`, or transform to one with `Schema.decodeTo`."
|
|
147
|
+
: `Cannot extend a \`${ast._tag}\` schema node with system fields: a table schema must resolve to an object shape at every step of its encoding.`,
|
|
148
|
+
);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
return extendAst;
|
|
152
|
+
};
|
|
153
|
+
|
|
29
154
|
/**
|
|
30
155
|
* Extend a table schema with Convex system fields.
|
|
156
|
+
*
|
|
157
|
+
* A plain `Struct` gains the fields via `Schema.fieldsAssign` and a `Union`
|
|
158
|
+
* has them distributed across its members, preserving the schema's
|
|
159
|
+
* `Struct`/`Union` structure. Any other object-shaped schema — one built with
|
|
160
|
+
* `Schema.decodeTo`/`Schema.encodeKeys`, a branded struct, a suspended schema
|
|
161
|
+
* — is extended at the AST level: every object node in its encoding chain
|
|
162
|
+
* gains the system fields, and each transformation is wrapped so the fields
|
|
163
|
+
* bypass the user-defined getters. Schemas that do not resolve to an object
|
|
164
|
+
* shape at every step (such as `Schema.Class`, whose decoded values are class
|
|
165
|
+
* instances) are rejected with a descriptive error.
|
|
166
|
+
*
|
|
167
|
+
* Note that struct-level checks on an extended schema observe the value with
|
|
168
|
+
* the system fields attached, and encoding an extended schema emits the system
|
|
169
|
+
* fields alongside the table fields.
|
|
31
170
|
*/
|
|
32
171
|
export const extendWithSystemFields = <
|
|
33
172
|
TableName extends string,
|
|
34
|
-
TableSchema extends Schema.
|
|
173
|
+
TableSchema extends Schema.Codec<any, any>,
|
|
35
174
|
>(
|
|
36
175
|
tableName: TableName,
|
|
37
176
|
schema: TableSchema,
|
|
38
|
-
): ExtendWithSystemFields<TableName, TableSchema> =>
|
|
39
|
-
|
|
177
|
+
): ExtendWithSystemFields<TableName, TableSchema> => {
|
|
178
|
+
const system = SystemFields(tableName).fields;
|
|
179
|
+
|
|
180
|
+
const extendAst = makeExtendAst(
|
|
181
|
+
pipe(
|
|
182
|
+
system,
|
|
183
|
+
Record.toEntries,
|
|
184
|
+
Array.map(
|
|
185
|
+
([name, field]) => new SchemaAST.PropertySignature(name, field.ast),
|
|
186
|
+
),
|
|
187
|
+
),
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const extend = (s: Schema.Top): Schema.Top => {
|
|
191
|
+
if (
|
|
192
|
+
s.ast.encoding === undefined &&
|
|
193
|
+
SchemaAST.isUnion(s.ast) &&
|
|
194
|
+
Array.isArray(
|
|
195
|
+
(s as Partial<Schema.Union<ReadonlyArray<Schema.Top>>>).members,
|
|
196
|
+
)
|
|
197
|
+
) {
|
|
198
|
+
return Schema.Union(
|
|
199
|
+
Array.map(
|
|
200
|
+
(s as Schema.Union<ReadonlyArray<Schema.Top>>).members,
|
|
201
|
+
extend,
|
|
202
|
+
),
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
if (
|
|
206
|
+
s.ast.encoding === undefined &&
|
|
207
|
+
SchemaAST.isObjects(s.ast) &&
|
|
208
|
+
typeof (s as Partial<Schema.Struct<Schema.Struct.Fields>>).mapFields ===
|
|
209
|
+
"function"
|
|
210
|
+
) {
|
|
211
|
+
return Schema.fieldsAssign(system)(
|
|
212
|
+
s as Schema.Struct<Schema.Struct.Fields>,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
return Schema.make(extendAst(s.ast));
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
return extend(schema) as unknown as ExtendWithSystemFields<
|
|
219
|
+
TableName,
|
|
220
|
+
TableSchema
|
|
221
|
+
>;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Applies the system fields to a single struct, mirroring `Schema.fieldsAssign`.
|
|
226
|
+
* Any other (already-extended or opaque) schema falls back to a `Codec` carrying
|
|
227
|
+
* the system-field document shape.
|
|
228
|
+
*/
|
|
229
|
+
type ApplySystemFields<TableName extends string, S> =
|
|
230
|
+
S extends Schema.Struct<infer Fields extends Schema.Struct.Fields>
|
|
231
|
+
? Schema.Struct<
|
|
232
|
+
Struct.Simplify<Struct.Assign<Fields, SystemFieldsFor<TableName>>>
|
|
233
|
+
>
|
|
234
|
+
: S extends Schema.Codec<infer Type, infer Encoded>
|
|
235
|
+
? Schema.Codec<
|
|
236
|
+
WithSystemFields<TableName, Type>,
|
|
237
|
+
WithSystemFields<TableName, Encoded>
|
|
238
|
+
>
|
|
239
|
+
: never;
|
|
40
240
|
|
|
41
241
|
/**
|
|
42
242
|
* Extend a table schema with Convex system fields at the type level.
|
|
243
|
+
*
|
|
244
|
+
* This mirrors the runtime {@link extendWithSystemFields}: a `Struct` gains the
|
|
245
|
+
* system fields directly (matching `Schema.fieldsAssign`), and a `Union` has
|
|
246
|
+
* them distributed across its members (matching
|
|
247
|
+
* `union.mapMembers(Tuple.map(Schema.fieldsAssign(...)))`). The `Struct`/`Union`
|
|
248
|
+
* structure is preserved rather than collapsed to a bare `Codec`; any other
|
|
249
|
+
* schema falls back to a `Codec` carrying the system-field document shape,
|
|
250
|
+
* matching the runtime's AST-level extension.
|
|
43
251
|
*/
|
|
44
252
|
export type ExtendWithSystemFields<
|
|
45
253
|
TableName extends string,
|
|
46
|
-
TableSchema extends Schema.
|
|
47
|
-
> =
|
|
254
|
+
TableSchema extends Schema.Top,
|
|
255
|
+
> =
|
|
256
|
+
TableSchema extends Schema.Union<
|
|
257
|
+
infer Members extends ReadonlyArray<Schema.Top>
|
|
258
|
+
>
|
|
259
|
+
? Schema.Union<
|
|
260
|
+
Struct.Simplify<
|
|
261
|
+
Readonly<{
|
|
262
|
+
[K in keyof Members]: ExtendWithSystemFields<TableName, Members[K]>;
|
|
263
|
+
}>
|
|
264
|
+
>
|
|
265
|
+
>
|
|
266
|
+
: ApplySystemFields<TableName, TableSchema>;
|
|
48
267
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
268
|
+
/**
|
|
269
|
+
* The decoded/encoded document shape: a table's fields plus Convex's system
|
|
270
|
+
* fields. Deliberately a bare intersection (no `Expand`/`Simplify`) — flattening
|
|
271
|
+
* it with a homomorphic mapped type collapses to `{ [x: string]: any }` when
|
|
272
|
+
* `Document` is still an unresolved generic, which breaks structural
|
|
273
|
+
* comparability in the database reader/writer plumbing.
|
|
274
|
+
*/
|
|
275
|
+
export type WithSystemFields<
|
|
276
|
+
TableName extends string,
|
|
277
|
+
Document,
|
|
278
|
+
> = Document extends unknown
|
|
279
|
+
? IdField<TableName> & NonIdSystemFields & Document
|
|
280
|
+
: never;
|