@confect/core 10.0.0-next.20 → 10.0.0-next.22

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 (66) hide show
  1. package/CHANGELOG.md +162 -80
  2. package/dist/FunctionProvenance.d.ts +5 -6
  3. package/dist/FunctionProvenance.d.ts.map +1 -1
  4. package/dist/FunctionProvenance.js +3 -4
  5. package/dist/FunctionProvenance.js.map +1 -1
  6. package/dist/FunctionSpec.d.ts +3 -1
  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/GroupSpec.d.ts +5 -1
  11. package/dist/GroupSpec.d.ts.map +1 -1
  12. package/dist/GroupSpec.js +31 -17
  13. package/dist/GroupSpec.js.map +1 -1
  14. package/dist/Identifier.d.ts +1 -1
  15. package/dist/Identifier.js +1 -1
  16. package/dist/Identifier.js.map +1 -1
  17. package/dist/Lazy.d.ts +4 -5
  18. package/dist/Lazy.d.ts.map +1 -1
  19. package/dist/Lazy.js +4 -5
  20. package/dist/Lazy.js.map +1 -1
  21. package/dist/MiddlewareAttachment.d.ts +171 -0
  22. package/dist/MiddlewareAttachment.d.ts.map +1 -0
  23. package/dist/MiddlewareAttachment.js +48 -0
  24. package/dist/MiddlewareAttachment.js.map +1 -0
  25. package/dist/MiddlewareSpec.d.ts +35 -23
  26. package/dist/MiddlewareSpec.d.ts.map +1 -1
  27. package/dist/MiddlewareSpec.js +1 -0
  28. package/dist/MiddlewareSpec.js.map +1 -1
  29. package/dist/PaginationOptions.d.ts +1 -1
  30. package/dist/PaginationOptions.js +1 -1
  31. package/dist/PaginationOptions.js.map +1 -1
  32. package/dist/Ref.d.ts +8 -7
  33. package/dist/Ref.d.ts.map +1 -1
  34. package/dist/Ref.js +11 -9
  35. package/dist/Ref.js.map +1 -1
  36. package/dist/Refs.js +1 -1
  37. package/dist/Refs.js.map +1 -1
  38. package/dist/SchemaToValidator.d.ts.map +1 -1
  39. package/dist/SchemaToValidator.js +4 -2
  40. package/dist/SchemaToValidator.js.map +1 -1
  41. package/dist/Spec.d.ts +1 -1
  42. package/dist/Spec.js.map +1 -1
  43. package/dist/SystemFields.d.ts +3 -4
  44. package/dist/SystemFields.d.ts.map +1 -1
  45. package/dist/SystemFields.js +5 -6
  46. package/dist/SystemFields.js.map +1 -1
  47. package/dist/Table.js.map +1 -1
  48. package/dist/index.d.ts +1 -0
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +2 -1
  51. package/package.json +10 -10
  52. package/src/FunctionProvenance.ts +6 -8
  53. package/src/FunctionSpec.ts +21 -7
  54. package/src/GroupSpec.ts +74 -26
  55. package/src/Identifier.ts +1 -1
  56. package/src/Lazy.ts +4 -5
  57. package/src/MiddlewareAttachment.ts +106 -0
  58. package/src/MiddlewareSpec.ts +55 -21
  59. package/src/PaginationOptions.ts +1 -1
  60. package/src/Ref.ts +42 -37
  61. package/src/Refs.ts +1 -1
  62. package/src/SchemaToValidator.ts +56 -45
  63. package/src/Spec.ts +1 -1
  64. package/src/SystemFields.ts +9 -10
  65. package/src/Table.ts +2 -2
  66. 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,
@@ -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
 
@@ -322,7 +324,7 @@ export const make = <FunctionSpec_ extends FunctionSpec.AnyWithProps>(
322
324
  */
323
325
  convexFunctionNamespace: string,
324
326
  functionSpec: FunctionSpec_,
325
- groupMiddlewareSpecs: ReadonlyArray<MiddlewareSpec.AnyMiddlewareSpec> = [],
327
+ groupMiddlewareAttachments: ReadonlyArray<MiddlewareAttachment.MiddlewareAttachment> = [],
326
328
  ): FromFunctionSpec<FunctionSpec_> => {
327
329
  const convexFunctionName = `${convexFunctionNamespace}:${functionSpec.name}`;
328
330
 
@@ -340,10 +342,13 @@ export const make = <FunctionSpec_ extends FunctionSpec.AnyWithProps>(
340
342
  _tag: "Confect" as const,
341
343
  convexFunctionName,
342
344
  kind: provenance.kind,
343
- middlewareSpecs: [
344
- ...groupMiddlewareSpecs,
345
- ...functionSpec.middlewareSpecs,
345
+ middlewareAttachments: [
346
+ ...groupMiddlewareAttachments,
347
+ ...functionSpec.middlewareAttachments,
346
348
  ],
349
+ get middlewareSpecs() {
350
+ return this.middlewareAttachments.map(({ spec }) => spec);
351
+ },
347
352
  };
348
353
 
349
354
  Lazy.defineProperty(ref, "args", () => provenance.args);
@@ -514,7 +519,7 @@ const errorSchemaOf = (ref: Any): Option.Option<Schema.Codec<any, any>> =>
514
519
  );
515
520
 
516
521
  /**
517
- * Decode `encodedError` against the ref's error schema — the function's
522
+ * Decode `encodedError` against the ref's error schema—the function's
518
523
  * declared `error` schema unioned with its covering middlewares' error
519
524
  * schemas. Returns `None` if the ref declares no typed error at all (Confect
520
525
  * ref without an `error` schema and without failing middleware, or a
@@ -529,22 +534,21 @@ export const decodeError = <Ref_ extends Any>(
529
534
  Option.match(errorSchemaOf(ref), {
530
535
  onNone: () => Effect.succeed(Option.none<Error<Ref_>>()),
531
536
  onSome: (schema) =>
532
- Effect.map(
537
+ Effect.asSome(
533
538
  Schema.decodeUnknownEffect(schema)(encodedError),
534
- Option.some,
535
539
  ) as Effect.Effect<Option.Option<Error<Ref_>>, Schema.SchemaError>,
536
540
  });
537
541
 
538
542
  /**
539
543
  * Synchronous counterpart to `decodeError`. Returns `None` when the value is
540
- * not this ref's typed error — either because the ref declares no `error`
544
+ * not this ref's typed error—either because the ref declares no `error`
541
545
  * schema, or because `encodedError` doesn't match the one it declares.
542
546
  *
543
547
  * The second case is reachable in normal operation: Convex raises its own
544
548
  * `ConvexError`s (an `InvalidCursor` pagination error, for instance), and
545
549
  * those never match a user-declared error schema. Callers pair this with a
546
550
  * 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
551
+ * throw—a `ParseError` here would replace the real error with an opaque one
548
552
  * and lose the only useful diagnostic. Hence the `Option` suffix rather than
549
553
  * `Sync`, matching `Schema.decodeUnknownOption`: the sibling `*Sync` helpers
550
554
  * in this module all throw on a parse failure, and this one deliberately
@@ -585,8 +589,7 @@ const paginatedKind = (ref: AnyConfect): FunctionProvenance.Paginated =>
585
589
  );
586
590
 
587
591
  /**
588
- * Encode the args of a paginated query ref via its user-args schema —
589
- * `paginationOpts` is excluded, since the pagination protocol fields are
592
+ * Encode the args of a paginated query ref via its user-args schema—`paginationOpts` is excluded, since the pagination protocol fields are
590
593
  * managed by the client (e.g. `usePaginatedQuery` from `convex/react`), not by
591
594
  * the caller. Requires a ref built with `FunctionSpec.publicPaginatedQuery`
592
595
  * (or `internalPaginatedQuery`).
@@ -630,7 +633,10 @@ export const decodePaginationPageSync = <Ref_ extends AnyPublicPaginatedQuery>(
630
633
  * `mapUnknownError` to be turned into a typed `E`, or surfaced as a defect when
631
634
  * no handler is provided.
632
635
  */
633
- export const runWithCodec = <Ref_ extends Any, E = never>(
636
+ export const runWithCodec = Effect.fnUntraced(function* <
637
+ Ref_ extends Any,
638
+ E = never,
639
+ >(
634
640
  ref: Ref_,
635
641
  args: Args<Ref_>,
636
642
  call: (
@@ -638,29 +644,28 @@ export const runWithCodec = <Ref_ extends Any, E = never>(
638
644
  encodedArgs: unknown,
639
645
  ) => PromiseLike<unknown>,
640
646
  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);
647
+ ): Effect.fn.Return<Returns<Ref_>, E | Error<Ref_> | Schema.SchemaError> {
648
+ const functionReference = getFunctionReference(ref);
649
+ const invoke = (
650
+ encodedArgs: unknown,
651
+ ): Effect.Effect<unknown, Error<Ref_> | E> =>
652
+ Effect.tryPromise({
653
+ try: () => Promise.resolve(call(functionReference, encodedArgs)),
654
+ catch: (error): Error<Ref_> | E => {
655
+ if (isConvexError(error)) {
656
+ const decoded = decodeErrorOption(ref, error.data);
657
+ if (Option.isSome(decoded)) {
658
+ return decoded.value;
658
659
  }
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
- });
660
+ }
661
+ if (mapUnknownError !== undefined) {
662
+ return mapUnknownError(error);
663
+ }
664
+ throw error;
665
+ },
666
+ });
667
+
668
+ const encodedArgs = yield* encodeArgs(ref, args);
669
+ const encodedReturns = yield* invoke(encodedArgs);
670
+ return yield* decodeReturns(ref, encodedReturns);
671
+ });
package/src/Refs.ts CHANGED
@@ -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) => {
@@ -91,7 +91,8 @@ export const compileTableSchema = <TableSchema extends Schema.Codec<any, any>>(
91
91
  Match.value,
92
92
  Match.tag("Objects", ({ indexSignatures }) =>
93
93
  Array.isReadonlyArrayEmpty(indexSignatures)
94
- ? (compileAst(ast) as Effect.Effect<any>)
94
+ ? // oxlint-disable-next-line effecttsgo/unsafe-effect-type-assertion -- The return type is derived from the input schema and cannot be recovered from the runtime AST.
95
+ (compileAst(ast) as Effect.Effect<any>)
95
96
  : Effect.fail(new IndexSignaturesAreNotSupportedError()),
96
97
  ),
97
98
  Match.tag("Union", (unionAst) => compileAst(unionAst)),
@@ -345,6 +346,7 @@ export const compileAst = (
345
346
  Match.tag("Unknown", "Any", () => Effect.succeed(v.any())),
346
347
  Match.tag("Declaration", (declaration) =>
347
348
  Effect.mapBoth(
349
+ // oxlint-disable-next-line effecttsgo/any-unknown-in-error-context -- Codec inputs are context-free, but Effect's SchemaAST.DeclarationRun erases their requirements to `any`.
348
350
  declaration.run(declaration.typeParameters)(
349
351
  new ArrayBuffer(0),
350
352
  declaration,
@@ -382,23 +384,27 @@ export const compileAst = (
382
384
  const handleUnion = (
383
385
  { types }: SchemaAST.Union,
384
386
  isOptionalPropertyOfTypeLiteral: boolean,
385
- ) =>
386
- Effect.gen(function* () {
387
- const members = isOptionalPropertyOfTypeLiteral
388
- ? Array.filter(types, Predicate.not(SchemaAST.isUndefined))
389
- : types;
390
-
391
- const [firstValidator, secondValidator, ...restValidators] =
392
- yield* Effect.all(Array.map(members, (type) => compileAst(type)));
393
-
394
- if (firstValidator === undefined) {
395
- return yield* new EmptyUnionIsNotSupportedError();
396
- } else if (secondValidator === undefined) {
397
- return firstValidator;
398
- } else {
399
- return v.union(firstValidator, secondValidator, ...restValidators);
400
- }
401
- });
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
+ });
402
408
 
403
409
  const handleObjects = (objectsAst: SchemaAST.Objects) =>
404
410
  pipe(
@@ -429,35 +435,40 @@ const handleObjects = (objectsAst: SchemaAST.Objects) =>
429
435
  );
430
436
 
431
437
  const handleArrays = ({ elements, rest }: SchemaAST.Arrays) =>
432
- Effect.gen(function* () {
433
- const [f, s, ...r] = elements;
438
+ handleArrayElements(elements, rest);
434
439
 
435
- const elementToValidator = (element: SchemaAST.AST) =>
436
- SchemaAST.isOptional(element)
437
- ? Effect.fail(new OptionalTupleElementsAreNotSupportedError())
438
- : 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
+ });
439
469
 
440
- const arrayItemsValidator = yield* f === undefined
441
- ? pipe(
442
- rest,
443
- Array.head,
444
- Option.match({
445
- onNone: () => Effect.fail(new EmptyTupleIsNotSupportedError()),
446
- onSome: (type) => compileAst(type),
447
- }),
448
- )
449
- : s === undefined
450
- ? elementToValidator(f)
451
- : Effect.gen(function* () {
452
- const firstValidator = yield* elementToValidator(f);
453
- const secondValidator = yield* elementToValidator(s);
454
- const restValidators = yield* Effect.forEach(r, elementToValidator);
455
-
456
- return v.union(firstValidator, secondValidator, ...restValidators);
457
- });
458
-
459
- return v.array(arrayItemsValidator);
460
- });
470
+ return v.array(arrayItemsValidator);
471
+ });
461
472
 
462
473
  const handlePropertySignatures = (objectsAst: SchemaAST.Objects) =>
463
474
  pipe(
package/src/Spec.ts CHANGED
@@ -10,7 +10,7 @@ export const isSpec = (u: unknown): u is AnyWithProps =>
10
10
 
11
11
  /**
12
12
  * 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
13
+ * runtime—a group built with `GroupSpec.makeNode()` (a Node action group) sits
14
14
  * alongside `GroupSpec.make()` groups in the same namespace. The runtime of a
15
15
  * group lives on the group itself (`GroupSpec.runtime`) and on each function's
16
16
  * `RuntimeAndFunctionType`; the spec does not carry a runtime of its own.
@@ -58,9 +58,9 @@ const splitSystemFields = (input: { [key: PropertyKey]: unknown }) => {
58
58
  * output. Transformations construct fresh objects, so without this the system
59
59
  * fields would be dropped at every transformation step.
60
60
  */
61
- const wrapGetter = (
62
- getter: SchemaGetter.Getter<any, any, any>,
63
- ): SchemaGetter.Getter<any, any, any> =>
61
+ const wrapGetter = <R>(
62
+ getter: SchemaGetter.Getter<any, any, R>,
63
+ ): SchemaGetter.Getter<any, any, R> =>
64
64
  new SchemaGetter.Getter((input, options) => {
65
65
  if (Option.isNone(input) || !Predicate.isObject(input.value)) {
66
66
  return getter.run(input, options);
@@ -93,8 +93,8 @@ const wrapTransformation = (
93
93
 
94
94
  /**
95
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.
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
98
  */
99
99
  const makeExtendAst = (
100
100
  systemPropertySignatures: ReadonlyArray<SchemaAST.PropertySignature>,
@@ -113,7 +113,7 @@ const makeExtendAst = (
113
113
  if (SchemaAST.isUnion(ast)) {
114
114
  return new SchemaAST.Union(
115
115
  Array.map(ast.types, extendAst),
116
- ast.mode,
116
+ ast.options,
117
117
  ast.annotations,
118
118
  ast.checks,
119
119
  ast.encoding === undefined ? undefined : extendEncoding(ast.encoding),
@@ -156,9 +156,8 @@ const makeExtendAst = (
156
156
  *
157
157
  * A plain `Struct` gains the fields via `Schema.fieldsAssign` and a `Union`
158
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
159
+ * `Struct`/`Union` structure. Any other object-shaped schema—one built with
160
+ * `Schema.decodeTo`/`Schema.encodeKeys`, a branded struct, a suspended 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
@@ -264,7 +263,7 @@ export type ExtendWithSystemFields<
264
263
 
265
264
  /**
266
265
  * The decoded/encoded document shape: a table's fields plus Convex's system
267
- * fields. Deliberately a bare intersection (no `Expand`/`Simplify`) — flattening
266
+ * fields. Deliberately a bare intersection (no `Expand`/`Simplify`)—flattening
268
267
  * it with a homomorphic mapped type collapses to `{ [x: string]: any }` when
269
268
  * `Document` is still an unresolved generic, which breaks structural
270
269
  * 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
 
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";