@confect/core 10.0.0-next.21 → 10.0.0-next.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/CHANGELOG.md +168 -81
  2. package/dist/FunctionProvenance.d.ts +34 -26
  3. package/dist/FunctionProvenance.d.ts.map +1 -1
  4. package/dist/FunctionProvenance.js +14 -13
  5. package/dist/FunctionProvenance.js.map +1 -1
  6. package/dist/FunctionSpec.d.ts +15 -9
  7. package/dist/FunctionSpec.d.ts.map +1 -1
  8. package/dist/FunctionSpec.js +12 -6
  9. package/dist/FunctionSpec.js.map +1 -1
  10. package/dist/GroupPath.d.ts +5 -6
  11. package/dist/GroupPath.d.ts.map +1 -1
  12. package/dist/GroupPath.js.map +1 -1
  13. package/dist/GroupSpec.d.ts +10 -3
  14. package/dist/GroupSpec.d.ts.map +1 -1
  15. package/dist/GroupSpec.js +33 -19
  16. package/dist/GroupSpec.js.map +1 -1
  17. package/dist/Identifier.d.ts +6 -5
  18. package/dist/Identifier.d.ts.map +1 -1
  19. package/dist/Identifier.js +6 -5
  20. package/dist/Identifier.js.map +1 -1
  21. package/dist/Lazy.d.ts +11 -13
  22. package/dist/Lazy.d.ts.map +1 -1
  23. package/dist/Lazy.js +11 -13
  24. package/dist/Lazy.js.map +1 -1
  25. package/dist/MiddlewareAttachment.d.ts +171 -0
  26. package/dist/MiddlewareAttachment.d.ts.map +1 -0
  27. package/dist/MiddlewareAttachment.js +48 -0
  28. package/dist/MiddlewareAttachment.js.map +1 -0
  29. package/dist/MiddlewareSpec.d.ts +59 -45
  30. package/dist/MiddlewareSpec.d.ts.map +1 -1
  31. package/dist/MiddlewareSpec.js +9 -4
  32. package/dist/MiddlewareSpec.js.map +1 -1
  33. package/dist/PaginationError.d.ts +9 -3
  34. package/dist/PaginationError.d.ts.map +1 -1
  35. package/dist/PaginationError.js +9 -3
  36. package/dist/PaginationError.js.map +1 -1
  37. package/dist/PaginationOptions.d.ts +3 -3
  38. package/dist/PaginationOptions.js +3 -3
  39. package/dist/PaginationOptions.js.map +1 -1
  40. package/dist/Ref.d.ts +42 -35
  41. package/dist/Ref.d.ts.map +1 -1
  42. package/dist/Ref.js +32 -30
  43. package/dist/Ref.js.map +1 -1
  44. package/dist/Refs.d.ts +1 -1
  45. package/dist/Refs.d.ts.map +1 -1
  46. package/dist/Refs.js +3 -2
  47. package/dist/Refs.js.map +1 -1
  48. package/dist/SchemaToValidator.d.ts.map +1 -1
  49. package/dist/SchemaToValidator.js +4 -2
  50. package/dist/SchemaToValidator.js.map +1 -1
  51. package/dist/Spec.d.ts +15 -12
  52. package/dist/Spec.d.ts.map +1 -1
  53. package/dist/Spec.js +13 -8
  54. package/dist/Spec.js.map +1 -1
  55. package/dist/SystemFields.d.ts +15 -14
  56. package/dist/SystemFields.d.ts.map +1 -1
  57. package/dist/SystemFields.js +11 -10
  58. package/dist/SystemFields.js.map +1 -1
  59. package/dist/Table.d.ts +1 -0
  60. package/dist/Table.d.ts.map +1 -1
  61. package/dist/Table.js.map +1 -1
  62. package/dist/index.d.ts +1 -0
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +3 -2
  65. package/package.json +8 -7
  66. package/src/FunctionProvenance.ts +35 -27
  67. package/src/FunctionSpec.ts +33 -15
  68. package/src/GroupPath.ts +5 -6
  69. package/src/GroupSpec.ts +83 -31
  70. package/src/Identifier.ts +6 -5
  71. package/src/Lazy.ts +11 -13
  72. package/src/MiddlewareAttachment.ts +106 -0
  73. package/src/MiddlewareSpec.ts +79 -43
  74. package/src/PaginationError.ts +9 -3
  75. package/src/PaginationOptions.ts +3 -3
  76. package/src/Ref.ts +76 -65
  77. package/src/Refs.ts +3 -3
  78. package/src/SchemaToValidator.ts +53 -44
  79. package/src/Spec.ts +50 -39
  80. package/src/SystemFields.ts +18 -17
  81. package/src/Table.ts +3 -2
  82. package/src/index.ts +1 -0
package/src/Ref.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type * as MiddlewareAttachment from "./MiddlewareAttachment";
1
2
  import type {
2
3
  DefaultFunctionArgs,
3
4
  FunctionReference as ConvexFunctionReference,
@@ -35,8 +36,8 @@ export interface Base<
35
36
 
36
37
  /**
37
38
  * A reference to a single Convex function, as callers see it: the wire name
38
- * plus the data needed to encode args, decode returns, and decode typed
39
- * errors. A ref is one of two shapes, keyed by the spec's provenance.
39
+ * plus the data needed to encode args, decode returns, and decode typed errors.
40
+ * A ref is one of two shapes, keyed by the spec's provenance.
40
41
  */
41
42
  export type Ref<
42
43
  RuntimeAndFunctionType_ extends RuntimeAndFunctionType.RuntimeAndFunctionType,
@@ -80,8 +81,8 @@ export type ConfectRef<
80
81
  /**
81
82
  * Internal form used while mapping a spec to a ref. Supplying the already
82
83
  * derived value types avoids repeatedly expanding `Schema.Struct` for every
83
- * leaf in a generated ref tree; the public `ConfectRef` alias still derives
84
- * the same values from its fields and schemas.
84
+ * leaf in a generated ref tree; the public `ConfectRef` alias still derives the
85
+ * same values from its fields and schemas.
85
86
  */
86
87
  interface ConfectRefWithTypes<
87
88
  RuntimeAndFunctionType_ extends RuntimeAndFunctionType.RuntimeAndFunctionType,
@@ -102,6 +103,7 @@ interface ConfectRefWithTypes<
102
103
  readonly returns: Schema.Codec<any, any>;
103
104
  readonly kind: FunctionProvenance.ConfectKind;
104
105
  readonly middlewareSpecs: ReadonlyArray<MiddlewareSpec.AnyMiddlewareSpec>;
106
+ readonly middlewareAttachments: ReadonlyArray<MiddlewareAttachment.MiddlewareAttachment>;
105
107
  readonly error?: Schema.Codec<any, any>;
106
108
  }
107
109
 
@@ -123,7 +125,9 @@ export interface ConvexRef<
123
125
 
124
126
  export type Any = Ref<any, any, any, any, any>;
125
127
 
126
- /** Erased Confect-provenance ref used by provenance-specific APIs. */
128
+ /**
129
+ * Erased Confect-provenance ref used by provenance-specific APIs.
130
+ */
127
131
  export type AnyConfect = Extract<Any, { readonly _tag: "Confect" }>;
128
132
 
129
133
  export type AnyInternal = Ref<any, "internal", any, any, any>;
@@ -226,7 +230,9 @@ export type Args<Ref_> = Ref_ extends { readonly "~Args": infer Args_ }
226
230
  ? Args_
227
231
  : never;
228
232
 
229
- /** The field map captured by a Confect-provenance ref. */
233
+ /**
234
+ * The field map captured by a Confect-provenance ref.
235
+ */
230
236
  export type ArgsFields<Ref_> = Ref_ extends {
231
237
  readonly _tag: "Confect";
232
238
  readonly args: {
@@ -236,7 +242,9 @@ export type ArgsFields<Ref_> = Ref_ extends {
236
242
  ? ArgsFields_
237
243
  : never;
238
244
 
239
- /** The assembled args schema carried by a Confect-provenance ref. */
245
+ /**
246
+ * The assembled args schema carried by a Confect-provenance ref.
247
+ */
240
248
  export type ArgsSchema<Ref_> = Ref_ extends {
241
249
  readonly _tag: "Confect";
242
250
  readonly args: infer ArgsSchema_ extends FunctionProvenance.AnyArgsSchema;
@@ -322,7 +330,7 @@ export const make = <FunctionSpec_ extends FunctionSpec.AnyWithProps>(
322
330
  */
323
331
  convexFunctionNamespace: string,
324
332
  functionSpec: FunctionSpec_,
325
- groupMiddlewareSpecs: ReadonlyArray<MiddlewareSpec.AnyMiddlewareSpec> = [],
333
+ groupMiddlewareAttachments: ReadonlyArray<MiddlewareAttachment.MiddlewareAttachment> = [],
326
334
  ): FromFunctionSpec<FunctionSpec_> => {
327
335
  const convexFunctionName = `${convexFunctionNamespace}:${functionSpec.name}`;
328
336
 
@@ -340,10 +348,13 @@ export const make = <FunctionSpec_ extends FunctionSpec.AnyWithProps>(
340
348
  _tag: "Confect" as const,
341
349
  convexFunctionName,
342
350
  kind: provenance.kind,
343
- middlewareSpecs: [
344
- ...groupMiddlewareSpecs,
345
- ...functionSpec.middlewareSpecs,
351
+ middlewareAttachments: [
352
+ ...groupMiddlewareAttachments,
353
+ ...functionSpec.middlewareAttachments,
346
354
  ],
355
+ get middlewareSpecs() {
356
+ return this.middlewareAttachments.map(({ spec }) => spec);
357
+ },
347
358
  };
348
359
 
349
360
  Lazy.defineProperty(ref, "args", () => provenance.args);
@@ -476,11 +487,11 @@ export const isConvexError = (error: unknown): error is ConvexError<Value> =>
476
487
  /**
477
488
  * Build a callback-style handler that decodes the ref's typed error from a
478
489
  * caught `ConvexError`, or else forwards the value to `mapUnknownError`. The
479
- * fallback is also invoked when the input *is* a `ConvexError` but the ref
480
- * doesn't declare a typed-error schema—by definition such a value falls
481
- * outside the ref's error contract. Useful when adapting non-Effect APIs (e.g.
482
- * emitter callbacks for streamed subscriptions) to the same error semantics
483
- * that `runWithCodec` provides.
490
+ * fallback is also invoked when the input _is_ a `ConvexError` but the ref
491
+ * doesn't declare a typed-error schema—by definition such a value falls outside
492
+ * the ref's error contract. Useful when adapting non-Effect APIs (e.g. emitter
493
+ * callbacks for streamed subscriptions) to the same error semantics that
494
+ * `runWithCodec` provides.
484
495
  */
485
496
  export const decodeErrorOrElse =
486
497
  <Ref_ extends Any, E>(ref: Ref_, mapUnknownError: (error: unknown) => E) =>
@@ -514,13 +525,13 @@ const errorSchemaOf = (ref: Any): Option.Option<Schema.Codec<any, any>> =>
514
525
  );
515
526
 
516
527
  /**
517
- * Decode `encodedError` against the ref's error schema — the function's
518
- * declared `error` schema unioned with its covering middlewares' error
519
- * schemas. Returns `None` if the ref declares no typed error at all (Confect
520
- * ref without an `error` schema and without failing middleware, or a
521
- * Convex-provenance ref)—by definition there's nothing to decode the value
522
- * into, and the caller is responsible for deciding what to do (typically:
523
- * surface the original value as a defect).
528
+ * Decode `encodedError` against the ref's error schema—the function's declared
529
+ * `error` schema unioned with its covering middlewares' error schemas. Returns
530
+ * `None` if the ref declares no typed error at all (Confect ref without an
531
+ * `error` schema and without failing middleware, or a Convex-provenance ref)—by
532
+ * definition there's nothing to decode the value into, and the caller is
533
+ * responsible for deciding what to do (typically: surface the original value as
534
+ * a defect).
524
535
  */
525
536
  export const decodeError = <Ref_ extends Any>(
526
537
  ref: Ref_,
@@ -529,26 +540,24 @@ export const decodeError = <Ref_ extends Any>(
529
540
  Option.match(errorSchemaOf(ref), {
530
541
  onNone: () => Effect.succeed(Option.none<Error<Ref_>>()),
531
542
  onSome: (schema) =>
532
- Effect.map(
543
+ Effect.asSome(
533
544
  Schema.decodeUnknownEffect(schema)(encodedError),
534
- Option.some,
535
545
  ) as Effect.Effect<Option.Option<Error<Ref_>>, Schema.SchemaError>,
536
546
  });
537
547
 
538
548
  /**
539
549
  * Synchronous counterpart to `decodeError`. Returns `None` when the value is
540
- * not this ref's typed error — either because the ref declares no `error`
541
- * schema, or because `encodedError` doesn't match the one it declares.
550
+ * not this ref's typed error—either because the ref declares no `error` schema,
551
+ * or because `encodedError` doesn't match the one it declares.
542
552
  *
543
553
  * The second case is reachable in normal operation: Convex raises its own
544
- * `ConvexError`s (an `InvalidCursor` pagination error, for instance), and
545
- * those never match a user-declared error schema. Callers pair this with a
546
- * fallback that surfaces the original error, so failing to decode must not
547
- * throw — a `ParseError` here would replace the real error with an opaque one
548
- * and lose the only useful diagnostic. Hence the `Option` suffix rather than
549
- * `Sync`, matching `Schema.decodeUnknownOption`: the sibling `*Sync` helpers
550
- * in this module all throw on a parse failure, and this one deliberately
551
- * doesn't.
554
+ * `ConvexError`s (an `InvalidCursor` pagination error, for instance), and those
555
+ * never match a user-declared error schema. Callers pair this with a fallback
556
+ * that surfaces the original error, so failing to decode must not throw—a
557
+ * `ParseError` here would replace the real error with an opaque one and lose
558
+ * the only useful diagnostic. Hence the `Option` suffix rather than `Sync`,
559
+ * matching `Schema.decodeUnknownOption`: the sibling `*Sync` helpers in this
560
+ * module all throw on a parse failure, and this one deliberately doesn't.
552
561
  */
553
562
  export const decodeErrorOption = <Ref_ extends Any>(
554
563
  ref: Ref_,
@@ -585,11 +594,11 @@ const paginatedKind = (ref: AnyConfect): FunctionProvenance.Paginated =>
585
594
  );
586
595
 
587
596
  /**
588
- * Encode the args of a paginated query ref via its user-args schema —
589
- * `paginationOpts` is excluded, since the pagination protocol fields are
597
+ * Encode the args of a paginated query ref via its user-args
598
+ * schema—`paginationOpts` is excluded, since the pagination protocol fields are
590
599
  * managed by the client (e.g. `usePaginatedQuery` from `convex/react`), not by
591
- * the caller. Requires a ref built with `FunctionSpec.publicPaginatedQuery`
592
- * (or `internalPaginatedQuery`).
600
+ * the caller. Requires a ref built with `FunctionSpec.publicPaginatedQuery` (or
601
+ * `internalPaginatedQuery`).
593
602
  */
594
603
  export const encodePaginatedQueryArgsSync = <
595
604
  Ref_ extends AnyPublicPaginatedQuery,
@@ -630,7 +639,10 @@ export const decodePaginationPageSync = <Ref_ extends AnyPublicPaginatedQuery>(
630
639
  * `mapUnknownError` to be turned into a typed `E`, or surfaced as a defect when
631
640
  * no handler is provided.
632
641
  */
633
- export const runWithCodec = <Ref_ extends Any, E = never>(
642
+ export const runWithCodec = Effect.fnUntraced(function* <
643
+ Ref_ extends Any,
644
+ E = never,
645
+ >(
634
646
  ref: Ref_,
635
647
  args: Args<Ref_>,
636
648
  call: (
@@ -638,29 +650,28 @@ export const runWithCodec = <Ref_ extends Any, E = never>(
638
650
  encodedArgs: unknown,
639
651
  ) => PromiseLike<unknown>,
640
652
  mapUnknownError?: (error: unknown) => E,
641
- ): Effect.Effect<Returns<Ref_>, E | Error<Ref_> | Schema.SchemaError> =>
642
- Effect.gen(function* () {
643
- const functionReference = getFunctionReference(ref);
644
- const invoke = (
645
- encodedArgs: unknown,
646
- ): Effect.Effect<unknown, Error<Ref_> | E> =>
647
- Effect.tryPromise({
648
- try: () => Promise.resolve(call(functionReference, encodedArgs)),
649
- catch: (error): Error<Ref_> | E => {
650
- if (isConvexError(error)) {
651
- const decoded = decodeErrorOption(ref, error.data);
652
- if (Option.isSome(decoded)) {
653
- return decoded.value;
654
- }
655
- }
656
- if (mapUnknownError !== undefined) {
657
- return mapUnknownError(error);
653
+ ): Effect.fn.Return<Returns<Ref_>, E | Error<Ref_> | Schema.SchemaError> {
654
+ const functionReference = getFunctionReference(ref);
655
+ const invoke = (
656
+ encodedArgs: unknown,
657
+ ): Effect.Effect<unknown, Error<Ref_> | E> =>
658
+ Effect.tryPromise({
659
+ try: () => Promise.resolve(call(functionReference, encodedArgs)),
660
+ catch: (error): Error<Ref_> | E => {
661
+ if (isConvexError(error)) {
662
+ const decoded = decodeErrorOption(ref, error.data);
663
+ if (Option.isSome(decoded)) {
664
+ return decoded.value;
658
665
  }
659
- throw error;
660
- },
661
- });
662
-
663
- const encodedArgs = yield* encodeArgs(ref, args);
664
- const encodedReturns = yield* invoke(encodedArgs);
665
- return yield* decodeReturns(ref, encodedReturns);
666
- });
666
+ }
667
+ if (mapUnknownError !== undefined) {
668
+ return mapUnknownError(error);
669
+ }
670
+ throw error;
671
+ },
672
+ });
673
+
674
+ const encodedArgs = yield* encodeArgs(ref, args);
675
+ const encodedReturns = yield* invoke(encodedArgs);
676
+ return yield* decodeReturns(ref, encodedReturns);
677
+ });
package/src/Refs.ts CHANGED
@@ -6,7 +6,7 @@ import type * as FunctionSpec from "./FunctionSpec";
6
6
  import type * as GroupSpec from "./GroupSpec";
7
7
  import type * as MiddlewareSpec from "./MiddlewareSpec";
8
8
  import * as Ref from "./Ref";
9
- import type * as Spec from "./Spec";
9
+ import * as Spec from "./Spec";
10
10
 
11
11
  export type Refs<
12
12
  Spec_ extends Spec.AnyWithProps,
@@ -98,7 +98,7 @@ type Any =
98
98
  export const make = <Spec_ extends Spec.AnyWithProps>(
99
99
  spec: Spec_,
100
100
  ): FromSpec<Spec_> => {
101
- const refs = makeHelper(spec.groups);
101
+ const refs = makeHelper(Spec.groups(spec));
102
102
  return {
103
103
  public: refs as Refs<Spec_, Ref.AnyPublic>,
104
104
  internal: refs as Refs<Spec_, Ref.AnyInternal>,
@@ -126,7 +126,7 @@ const makeHelper = (
126
126
  Ref.make(
127
127
  currentConvexFunctionNamespace,
128
128
  function_,
129
- group.middlewareSpecs,
129
+ group.middlewareAttachments,
130
130
  ),
131
131
  ),
132
132
  (_subGroup, _function) => {
@@ -384,23 +384,27 @@ export const compileAst = (
384
384
  const handleUnion = (
385
385
  { types }: SchemaAST.Union,
386
386
  isOptionalPropertyOfTypeLiteral: boolean,
387
- ) =>
388
- Effect.gen(function* () {
389
- const members = isOptionalPropertyOfTypeLiteral
390
- ? Array.filter(types, Predicate.not(SchemaAST.isUndefined))
391
- : types;
392
-
393
- const [firstValidator, secondValidator, ...restValidators] =
394
- yield* Effect.all(Array.map(members, (type) => compileAst(type)));
395
-
396
- if (firstValidator === undefined) {
397
- return yield* new EmptyUnionIsNotSupportedError();
398
- } else if (secondValidator === undefined) {
399
- return firstValidator;
400
- } else {
401
- return v.union(firstValidator, secondValidator, ...restValidators);
402
- }
403
- });
387
+ ) => handleUnionTypes(types, isOptionalPropertyOfTypeLiteral);
388
+
389
+ const handleUnionTypes = Effect.fnUntraced(function* (
390
+ types: SchemaAST.Union["types"],
391
+ isOptionalPropertyOfTypeLiteral: boolean,
392
+ ) {
393
+ const members = isOptionalPropertyOfTypeLiteral
394
+ ? Array.filter(types, Predicate.not(SchemaAST.isUndefined))
395
+ : types;
396
+
397
+ const [firstValidator, secondValidator, ...restValidators] =
398
+ yield* Effect.all(Array.map(members, (type) => compileAst(type)));
399
+
400
+ if (firstValidator === undefined) {
401
+ return yield* new EmptyUnionIsNotSupportedError();
402
+ } else if (secondValidator === undefined) {
403
+ return firstValidator;
404
+ } else {
405
+ return v.union(firstValidator, secondValidator, ...restValidators);
406
+ }
407
+ });
404
408
 
405
409
  const handleObjects = (objectsAst: SchemaAST.Objects) =>
406
410
  pipe(
@@ -431,35 +435,40 @@ const handleObjects = (objectsAst: SchemaAST.Objects) =>
431
435
  );
432
436
 
433
437
  const handleArrays = ({ elements, rest }: SchemaAST.Arrays) =>
434
- Effect.gen(function* () {
435
- const [f, s, ...r] = elements;
438
+ handleArrayElements(elements, rest);
436
439
 
437
- const elementToValidator = (element: SchemaAST.AST) =>
438
- SchemaAST.isOptional(element)
439
- ? Effect.fail(new OptionalTupleElementsAreNotSupportedError())
440
- : compileAst(element);
440
+ const handleArrayElements = Effect.fnUntraced(function* (
441
+ elements: SchemaAST.Arrays["elements"],
442
+ rest: SchemaAST.Arrays["rest"],
443
+ ) {
444
+ const [f, s, ...r] = elements;
445
+
446
+ const elementToValidator = (element: SchemaAST.AST) =>
447
+ SchemaAST.isOptional(element)
448
+ ? Effect.fail(new OptionalTupleElementsAreNotSupportedError())
449
+ : compileAst(element);
450
+
451
+ const arrayItemsValidator = yield* f === undefined
452
+ ? pipe(
453
+ rest,
454
+ Array.head,
455
+ Option.match({
456
+ onNone: () => Effect.fail(new EmptyTupleIsNotSupportedError()),
457
+ onSome: (type) => compileAst(type),
458
+ }),
459
+ )
460
+ : s === undefined
461
+ ? elementToValidator(f)
462
+ : Effect.gen(function* () {
463
+ const firstValidator = yield* elementToValidator(f);
464
+ const secondValidator = yield* elementToValidator(s);
465
+ const restValidators = yield* Effect.forEach(r, elementToValidator);
466
+
467
+ return v.union(firstValidator, secondValidator, ...restValidators);
468
+ });
441
469
 
442
- const arrayItemsValidator = yield* f === undefined
443
- ? pipe(
444
- rest,
445
- Array.head,
446
- Option.match({
447
- onNone: () => Effect.fail(new EmptyTupleIsNotSupportedError()),
448
- onSome: (type) => compileAst(type),
449
- }),
450
- )
451
- : s === undefined
452
- ? elementToValidator(f)
453
- : Effect.gen(function* () {
454
- const firstValidator = yield* elementToValidator(f);
455
- const secondValidator = yield* elementToValidator(s);
456
- const restValidators = yield* Effect.forEach(r, elementToValidator);
457
-
458
- return v.union(firstValidator, secondValidator, ...restValidators);
459
- });
460
-
461
- return v.array(arrayItemsValidator);
462
- });
470
+ return v.array(arrayItemsValidator);
471
+ });
463
472
 
464
473
  const handlePropertySignatures = (objectsAst: SchemaAST.Objects) =>
465
474
  pipe(
package/src/Spec.ts CHANGED
@@ -1,5 +1,6 @@
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
6
  export const TypeId = "~@confect/core/Spec";
@@ -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 — a group built with `GroupSpec.makeNode()` (a Node action group) sits
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<Groups_ extends GroupSpec.AnyWithProps = never> {
19
- readonly [TypeId]: TypeId;
20
- readonly groups: {
21
- [GroupName in GroupSpec.Name<Groups_>]: GroupSpec.WithName<
22
- Groups_,
23
- GroupName
24
- >;
25
- };
26
- readonly "~Groups": Groups_;
19
+ export interface Spec<
20
+ Groups_ extends Record.ReadonlyRecord<string, GroupSpec.AnyWithProps> = {},
21
+ > {
22
+ readonly [TypeId]: Readonly<Groups_>;
27
23
 
28
24
  add<Group extends GroupSpec.AnyWithProps>(
29
25
  group: Group,
30
- ): Spec<Groups_ | Group>;
26
+ ): Spec<
27
+ Struct.Assign<Groups_, Record.ReadonlyRecord<GroupSpec.Name<Group>, Group>>
28
+ >;
31
29
 
32
30
  addAt<const Name extends string, Group extends GroupSpec.AnyWithProps>(
33
31
  name: Name,
34
32
  group: Group,
35
- ): Spec<Groups_ | GroupSpec.NamedAt<Group, Name>>;
33
+ ): Spec<
34
+ Struct.Assign<
35
+ Groups_,
36
+ Record.ReadonlyRecord<Name, GroupSpec.NamedAt<Group, Name>>
37
+ >
38
+ >;
36
39
  }
37
40
 
38
41
  export interface Any {
39
- readonly [TypeId]: TypeId;
42
+ readonly [TypeId]: unknown;
40
43
  }
41
44
 
42
- export interface AnyWithProps extends Spec<GroupSpec.AnyWithProps> {}
45
+ export interface AnyWithProps extends Any {
46
+ readonly [TypeId]: Record.ReadonlyRecord<string, GroupSpec.AnyWithProps>;
47
+ }
43
48
 
44
- export type Groups<Spec_ extends AnyWithProps> = Spec_["~Groups"];
49
+ export type Groups<Spec_ extends AnyWithProps> =
50
+ Spec_[TypeId][keyof Spec_[TypeId]];
45
51
 
46
- const Proto = {
47
- [TypeId]: TypeId,
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
- add<Group extends GroupSpec.AnyWithProps>(this: AnyWithProps, group: Group) {
50
- return makeProto({
51
- groups: Record.set(this.groups, group.name, group),
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
- addAt<Group extends GroupSpec.AnyWithProps>(
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 makeProto({
61
- groups: Record.set(this.groups, name, GroupSpec.withName(name, group)),
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
- }) as Spec<Groups_>;
84
+ });
74
85
 
75
- export const make = (): Spec => makeProto({ groups: {} });
86
+ export const make = (): Spec => makeRecord({});
@@ -13,7 +13,8 @@ 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 `_id`.
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({
@@ -93,8 +94,8 @@ const wrapTransformation = (
93
94
 
94
95
  /**
95
96
  * 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.
97
+ * every object node of a schema's AST—including each step of its encoding
98
+ * chain—wrapping transformations so the fields survive them.
98
99
  */
99
100
  const makeExtendAst = (
100
101
  systemPropertySignatures: ReadonlyArray<SchemaAST.PropertySignature>,
@@ -113,7 +114,7 @@ const makeExtendAst = (
113
114
  if (SchemaAST.isUnion(ast)) {
114
115
  return new SchemaAST.Union(
115
116
  Array.map(ast.types, extendAst),
116
- ast.mode,
117
+ ast.options,
117
118
  ast.annotations,
118
119
  ast.checks,
119
120
  ast.encoding === undefined ? undefined : extendEncoding(ast.encoding),
@@ -154,11 +155,11 @@ const makeExtendAst = (
154
155
  /**
155
156
  * Extend a table schema with Convex system fields.
156
157
  *
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
158
+ * A plain `Struct` gains the fields via `Schema.fieldsAssign` and a `Union` has
159
+ * them distributed across its members, preserving the schema's `Struct`/`Union`
160
+ * structure. Any other object-shaped schema—one built with
161
+ * `Schema.decodeTo`/`Schema.encodeKeys`, a branded struct, a suspended
162
+ * schema—is extended at the AST level: every object node in its encoding chain
162
163
  * gains the system fields, and each transformation is wrapped so the fields
163
164
  * bypass the user-defined getters. Schemas that do not resolve to an object
164
165
  * shape at every step (such as `Schema.Class`, whose decoded values are class
@@ -219,9 +220,9 @@ export const extendWithSystemFields = <
219
220
  };
220
221
 
221
222
  /**
222
- * Applies the system fields to a single struct, mirroring `Schema.fieldsAssign`.
223
- * Any other (already-extended or opaque) schema falls back to a `Codec` carrying
224
- * the system-field document shape.
223
+ * Applies the system fields to a single struct, mirroring
224
+ * `Schema.fieldsAssign`. Any other (already-extended or opaque) schema falls
225
+ * back to a `Codec` carrying the system-field document shape.
225
226
  */
226
227
  type ApplySystemFields<TableName extends string, S> =
227
228
  S extends Schema.Struct<infer Fields extends Schema.Struct.Fields>
@@ -241,10 +242,10 @@ type ApplySystemFields<TableName extends string, S> =
241
242
  * This mirrors the runtime {@link extendWithSystemFields}: a `Struct` gains the
242
243
  * system fields directly (matching `Schema.fieldsAssign`), and a `Union` has
243
244
  * them distributed across its members (matching
244
- * `union.mapMembers(Tuple.map(Schema.fieldsAssign(...)))`). The `Struct`/`Union`
245
- * structure is preserved rather than collapsed to a bare `Codec`; any other
246
- * schema falls back to a `Codec` carrying the system-field document shape,
247
- * matching the runtime's AST-level extension.
245
+ * `union.mapMembers(Tuple.map(Schema.fieldsAssign(...)))`). The
246
+ * `Struct`/`Union` structure is preserved rather than collapsed to a bare
247
+ * `Codec`; any other schema falls back to a `Codec` carrying the system-field
248
+ * document shape, matching the runtime's AST-level extension.
248
249
  */
249
250
  export type ExtendWithSystemFields<
250
251
  TableName extends string,
@@ -264,7 +265,7 @@ export type ExtendWithSystemFields<
264
265
 
265
266
  /**
266
267
  * The decoded/encoded document shape: a table's fields plus Convex's system
267
- * fields. Deliberately a bare intersection (no `Expand`/`Simplify`) — flattening
268
+ * fields. Deliberately a bare intersection (no `Expand`/`Simplify`)—flattening
268
269
  * it with a homomorphic mapped type collapses to `{ [x: string]: any }` when
269
270
  * `Document` is still an unresolved generic, which breaks structural
270
271
  * comparability in the database reader/writer plumbing.
package/src/Table.ts CHANGED
@@ -86,7 +86,7 @@ export type AnyWithProps = Table<
86
86
  // (`.index`, `.searchIndex`, `.vectorIndex`) live here and return new
87
87
  // `UnnamedTable`s, accumulating plain index metadata records. The
88
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
89
+ // access of `Fields`/`Doc` on a bound table. The codegen pipeline emits a
90
90
  // wrapper file per user-authored table that simply invokes the unnamed
91
91
  // callable with the filename basename.
92
92
 
@@ -241,7 +241,7 @@ export type TablesRecord<Tables extends AnyWithProps> = {
241
241
  // spread of the metadata records) and never invokes the callback. Binding
242
242
  // via `unnamed(tableName)` installs lazy memoised getters for `Fields` and
243
243
  // `Doc` via `Lazy.defineProperty`, so the first access materialises the
244
- // value and replaces the getter with a plain data property — second-and-
244
+ // value and replaces the getter with a plain data property—second-and-
245
245
  // subsequent accesses are observably indistinguishable from a plain
246
246
  // property and avoid all function-call overhead.
247
247
 
@@ -417,6 +417,7 @@ export const make = <const TableSchema_ extends Schema.Codec<any, any>>(
417
417
  * Extract all of the index field paths within a Convex `Validator`.
418
418
  *
419
419
  * This is used when declaring table indexes.
420
+ *
420
421
  * @public
421
422
  */
422
423
  type ExtractFieldPaths<T extends Validator<any, any, any>> =
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ export * as GroupPath from "./GroupPath";
5
5
  export * as GroupSpec from "./GroupSpec";
6
6
  export * as Identifier from "./Identifier";
7
7
  export * as Lazy from "./Lazy";
8
+ export * as MiddlewareAttachment from "./MiddlewareAttachment";
8
9
  export * as MiddlewareSpec from "./MiddlewareSpec";
9
10
  export * as PaginationError from "./PaginationError";
10
11
  export * as PaginationOptions from "./PaginationOptions";