@gaganref/confect-core 10.3.1-fork.9 → 10.3.1-fork.91

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 (94) hide show
  1. package/dist/ActionCall.d.ts +18 -0
  2. package/dist/ActionCall.d.ts.map +1 -0
  3. package/dist/ActionCall.js +21 -0
  4. package/dist/ActionCall.js.map +1 -0
  5. package/dist/App.d.ts +38 -93
  6. package/dist/App.d.ts.map +1 -1
  7. package/dist/App.js +30 -38
  8. package/dist/App.js.map +1 -1
  9. package/dist/Auth.d.ts +33 -9
  10. package/dist/Auth.d.ts.map +1 -1
  11. package/dist/Auth.js +17 -2
  12. package/dist/Auth.js.map +1 -1
  13. package/dist/Callback.d.ts +7 -2
  14. package/dist/Callback.d.ts.map +1 -1
  15. package/dist/Callback.js +11 -6
  16. package/dist/Callback.js.map +1 -1
  17. package/dist/CommitTs.d.ts +18 -0
  18. package/dist/CommitTs.d.ts.map +1 -0
  19. package/dist/CommitTs.js +20 -0
  20. package/dist/CommitTs.js.map +1 -0
  21. package/dist/Component.d.ts +106 -2
  22. package/dist/Component.d.ts.map +1 -1
  23. package/dist/Component.js +44 -18
  24. package/dist/Component.js.map +1 -1
  25. package/dist/FunctionSpec.d.ts +361 -2
  26. package/dist/FunctionSpec.d.ts.map +1 -1
  27. package/dist/FunctionSpec.js +68 -3
  28. package/dist/FunctionSpec.js.map +1 -1
  29. package/dist/GenericId.js +2 -2
  30. package/dist/GroupSpec.js +1 -1
  31. package/dist/Http.d.ts +4 -8
  32. package/dist/Http.d.ts.map +1 -1
  33. package/dist/Http.js +15 -22
  34. package/dist/Http.js.map +1 -1
  35. package/dist/ManifestSection.d.ts +101 -0
  36. package/dist/ManifestSection.d.ts.map +1 -0
  37. package/dist/ManifestSection.js +7 -0
  38. package/dist/ManifestSection.js.map +1 -0
  39. package/dist/Mount.d.ts +200 -0
  40. package/dist/Mount.d.ts.map +1 -0
  41. package/dist/Mount.js +116 -0
  42. package/dist/Mount.js.map +1 -0
  43. package/dist/PaginationError.js +1 -1
  44. package/dist/Profile.d.ts +95 -0
  45. package/dist/Profile.d.ts.map +1 -0
  46. package/dist/Profile.js +15 -0
  47. package/dist/Profile.js.map +1 -0
  48. package/dist/Ref.d.ts +4 -3
  49. package/dist/Ref.d.ts.map +1 -1
  50. package/dist/Ref.js +13 -19
  51. package/dist/Ref.js.map +1 -1
  52. package/dist/RefInternal.d.ts +3 -0
  53. package/dist/RefInternal.d.ts.map +1 -0
  54. package/dist/RefInternal.js +16 -0
  55. package/dist/RefInternal.js.map +1 -0
  56. package/dist/SchemaToValidator.d.ts +20 -6
  57. package/dist/SchemaToValidator.d.ts.map +1 -1
  58. package/dist/SchemaToValidator.js +45 -14
  59. package/dist/SchemaToValidator.js.map +1 -1
  60. package/dist/SystemFields.js +3 -3
  61. package/dist/Table.d.ts +48 -9
  62. package/dist/Table.d.ts.map +1 -1
  63. package/dist/Table.js +41 -13
  64. package/dist/Table.js.map +1 -1
  65. package/dist/index.d.ts +5 -0
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +11 -6
  68. package/dist/internal/annotations.d.ts +2 -0
  69. package/dist/internal/annotations.d.ts.map +1 -0
  70. package/dist/internal/annotations.js +6 -0
  71. package/dist/internal/annotations.js.map +1 -0
  72. package/dist/internal/scalarDocs.d.ts +5 -0
  73. package/dist/internal/scalarDocs.d.ts.map +1 -0
  74. package/dist/internal/scalarDocs.js +43 -0
  75. package/dist/internal/scalarDocs.js.map +1 -0
  76. package/package.json +3 -3
  77. package/src/ActionCall.ts +25 -0
  78. package/src/App.ts +45 -136
  79. package/src/Auth.ts +54 -11
  80. package/src/Callback.ts +17 -4
  81. package/src/CommitTs.ts +26 -0
  82. package/src/Component.ts +204 -11
  83. package/src/FunctionSpec.ts +562 -5
  84. package/src/Http.ts +15 -37
  85. package/src/ManifestSection.ts +108 -0
  86. package/src/Mount.ts +401 -0
  87. package/src/Profile.ts +141 -0
  88. package/src/Ref.ts +19 -26
  89. package/src/RefInternal.ts +26 -0
  90. package/src/SchemaToValidator.ts +97 -22
  91. package/src/Table.ts +157 -23
  92. package/src/index.ts +5 -0
  93. package/src/internal/annotations.ts +1 -0
  94. package/src/internal/scalarDocs.ts +72 -0
package/src/Profile.ts ADDED
@@ -0,0 +1,141 @@
1
+ import type * as Schema from "effect/Schema";
2
+ import type * as FunctionProvenance from "./FunctionProvenance.js";
3
+ import type * as MiddlewareSpec from "./MiddlewareSpec.js";
4
+
5
+ /**
6
+ * A type-level function from a function's name and args fields to the fields a
7
+ * profile's builders require. Read `this["Name"]` and `this["Args"]` directly
8
+ * in `Type`, passing them to a type alias; a nested object type cannot name
9
+ * `this`.
10
+ */
11
+ export interface Fields {
12
+ readonly Name: string;
13
+ // `unknown`, not `ArgsFields`: an index signature here would leak into
14
+ // `keyof this["Args"]`.
15
+ readonly Args: unknown;
16
+ readonly Type: object;
17
+ }
18
+
19
+ export interface NoFields extends Fields {
20
+ readonly Type: {};
21
+ }
22
+
23
+ export type Apply<
24
+ Fields_ extends Fields,
25
+ Name_ extends string,
26
+ Args_,
27
+ > = (Fields_ & { readonly Name: Name_; readonly Args: Args_ })["Type"];
28
+
29
+ /**
30
+ * A base profile's fields and its extension's, for one function.
31
+ */
32
+ export interface Both<
33
+ Base extends Fields,
34
+ Extension extends Fields,
35
+ > extends Fields {
36
+ readonly Type: Apply<Base, this["Name"], this["Args"]> &
37
+ Apply<Extension, this["Name"], this["Args"]>;
38
+ }
39
+
40
+ /**
41
+ * What a profile callback sees. `args` stays a thunk.
42
+ */
43
+ export interface Info<FieldsType> {
44
+ readonly name: string;
45
+ readonly description?: string;
46
+ readonly args: () => FunctionProvenance.ArgsFields;
47
+ readonly fields: "description" extends keyof FieldsType
48
+ ? Omit<FieldsType, "description">
49
+ : FieldsType;
50
+ }
51
+
52
+ export interface Attachment<
53
+ MiddlewareSpec_ extends MiddlewareSpec.AnyMiddlewareSpec,
54
+ FieldsType,
55
+ > {
56
+ readonly spec: MiddlewareSpec_;
57
+ readonly options: (
58
+ info: Info<FieldsType>,
59
+ ) => MiddlewareSpec.Options<MiddlewareSpec_>;
60
+ }
61
+
62
+ /**
63
+ * A middleware without options has none to compute and is attached at most
64
+ * once, so it joins a profile's list directly.
65
+ */
66
+ type TakesOptions<MiddlewareSpec_ extends MiddlewareSpec.AnyMiddlewareSpec> = [
67
+ MiddlewareSpec.WithoutOptions<MiddlewareSpec_>,
68
+ ] extends [never]
69
+ ? unknown
70
+ : MiddlewareSpec.AttachmentError<`Middleware "${MiddlewareSpec.Key<MiddlewareSpec_>}" takes no options, so list it without Profile.attach`>;
71
+
72
+ /**
73
+ * Attach a middleware that takes options to every function a profile builds,
74
+ * with options computed from each function's fields.
75
+ */
76
+ export const attach = <
77
+ MiddlewareSpec_ extends MiddlewareSpec.AnyMiddlewareSpec,
78
+ FieldsType,
79
+ >(
80
+ spec: MiddlewareSpec_ & TakesOptions<MiddlewareSpec_>,
81
+ options: (
82
+ info: Info<FieldsType>,
83
+ ) => MiddlewareSpec.Options<NoInfer<MiddlewareSpec_>>,
84
+ ): Attachment<MiddlewareSpec_, FieldsType> => ({ spec, options });
85
+
86
+ /**
87
+ * A type function for a schema wrapper, applied to each declared codec.
88
+ */
89
+ export interface SchemaTransform {
90
+ readonly Input: Schema.Codec<unknown, unknown>;
91
+ readonly Type: Schema.Codec<any, any>;
92
+ }
93
+
94
+ /**
95
+ * Args wrappers transform the declared field map, before Struct composition.
96
+ */
97
+ export interface ArgsTransform {
98
+ readonly Input: unknown;
99
+ readonly Type: FunctionProvenance.ArgsFields;
100
+ }
101
+
102
+ export interface Transforms {
103
+ readonly args?: ArgsTransform;
104
+ readonly returns?: SchemaTransform;
105
+ readonly item?: SchemaTransform;
106
+ readonly error?: SchemaTransform;
107
+ }
108
+
109
+ export type Transform<T, Input> = (T & { readonly Input: Input })["Type" &
110
+ keyof T];
111
+
112
+ /**
113
+ * Error wrappers opt into Schema.Never when no error thunk was declared.
114
+ */
115
+ export type Wrappers<T extends Transforms, FieldsType> = {
116
+ readonly [K in keyof T]-?: K extends keyof Transforms
117
+ ? <
118
+ Input extends (K extends "args"
119
+ ? FunctionProvenance.ArgsFields
120
+ : Schema.Codec<any, any>),
121
+ >(
122
+ thunk: () => Input,
123
+ info: Info<FieldsType>,
124
+ ) => () => Transform<T[K], Input>
125
+ : never;
126
+ } & { readonly [K in Exclude<keyof Transforms, keyof T>]?: never };
127
+
128
+ type NonemptyError<T extends Transforms> = T extends {
129
+ readonly error: infer E extends SchemaTransform;
130
+ }
131
+ ? [Transform<E, typeof Schema.Never>["Type"]] extends [never]
132
+ ? MiddlewareSpec.AttachmentError<"Profile error wrapper must add an error to Schema.Never">
133
+ : unknown
134
+ : unknown;
135
+
136
+ export type SchemaConfig<T extends Transforms, FieldsType> = ([
137
+ keyof T,
138
+ ] extends [never]
139
+ ? { readonly schemas?: never }
140
+ : { readonly schemas: Wrappers<T, FieldsType> }) &
141
+ NonemptyError<T>;
package/src/Ref.ts CHANGED
@@ -6,7 +6,7 @@ import type {
6
6
  PaginationOptions,
7
7
  PaginationResult,
8
8
  } from "convex/server";
9
- import { getFunctionAddress, makeFunctionReference } from "convex/server";
9
+ import { getFunctionAddress } from "convex/server";
10
10
  import type { Value } from "convex/values";
11
11
  import { ConvexError } from "convex/values";
12
12
  import * as Effect from "effect/Effect";
@@ -18,6 +18,7 @@ import type * as FunctionProvenance from "./FunctionProvenance.js";
18
18
  import type * as FunctionSpec from "./FunctionSpec.js";
19
19
  import * as Lazy from "./Lazy.js";
20
20
  import * as MiddlewareSpec from "./MiddlewareSpec.js";
21
+ import { getFunctionReference } from "./RefInternal.js";
21
22
  import type * as RuntimeAndFunctionType from "./RuntimeAndFunctionType.js";
22
23
 
23
24
  export const TypeId = "~@confect/core/Ref";
@@ -41,7 +42,9 @@ export interface Base<
41
42
  : (args: Args_) => void;
42
43
  readonly "~Returns": Returns_;
43
44
  readonly "~Error": Error_;
45
+ readonly functionType?: RuntimeAndFunctionType_["functionType"];
44
46
  readonly convexFunctionName: string;
47
+ readonly reservedArgument?: string;
45
48
  /**
46
49
  * Preserved for component references and function handles, which have no
47
50
  * local name.
@@ -447,14 +450,19 @@ export const make = <
447
450
  ({
448
451
  [TypeId]: TypeId,
449
452
  _tag: "Convex",
453
+ functionType: functionSpec.runtimeAndFunctionType.functionType,
450
454
  convexFunctionName,
451
455
  }) as Any,
452
456
  ),
453
457
  Match.tag("Confect", (provenance): Any =>
454
- fromProvenance(convexFunctionName, provenance, [
455
- ...groupMiddlewareAttachments,
456
- ...functionSpec.middlewareAttachments,
457
- ]),
458
+ fromProvenance(
459
+ convexFunctionName,
460
+ provenance,
461
+ [...groupMiddlewareAttachments, ...functionSpec.middlewareAttachments],
462
+ undefined,
463
+ functionSpec.runtimeAndFunctionType.functionType,
464
+ functionSpec.reservedArgument,
465
+ ),
458
466
  ),
459
467
  Match.exhaustive,
460
468
  ) as FromFunctionSpec<
@@ -476,11 +484,15 @@ export const fromProvenance = (
476
484
  provenance: FunctionProvenance.AnyConfect,
477
485
  middlewareAttachments: ReadonlyArray<MiddlewareAttachment.MiddlewareAttachment>,
478
486
  functionReference?: ConvexFunctionReference<any, any>,
487
+ functionType?: "query" | "mutation" | "action",
488
+ reservedArgument?: string,
479
489
  ): AnyConfect => {
480
490
  const ref = {
481
491
  [TypeId]: TypeId as TypeId,
482
492
  _tag: "Confect" as const,
493
+ ...(functionType === undefined ? {} : { functionType }),
483
494
  convexFunctionName,
495
+ ...(reservedArgument === undefined ? {} : { reservedArgument }),
484
496
  ...(functionReference === undefined ? {} : { functionReference }),
485
497
  kind: provenance.kind,
486
498
  middlewareAttachments,
@@ -501,27 +513,6 @@ export const fromProvenance = (
501
513
  export const getConvexFunctionName = (ref: Any): string =>
502
514
  ref.convexFunctionName;
503
515
 
504
- const functionReferenceCache = new Map<string, FunctionReference<Any>>();
505
-
506
- export const getFunctionReference = <Ref_ extends Any>(
507
- ref: Ref_,
508
- ): FunctionReference<Ref_> => {
509
- if (ref.functionReference !== undefined) {
510
- return ref.functionReference as FunctionReference<Ref_>;
511
- }
512
- const convexFunctionName = getConvexFunctionName(ref);
513
-
514
- const cached = functionReferenceCache.get(convexFunctionName);
515
- if (cached !== undefined) {
516
- return cached as FunctionReference<Ref_>;
517
- }
518
-
519
- const functionReference = makeFunctionReference(convexFunctionName);
520
- functionReferenceCache.set(convexFunctionName, functionReference);
521
-
522
- return functionReference as FunctionReference<Ref_>;
523
- };
524
-
525
516
  /**
526
517
  * Adapt a native reference without assigning Confect codecs or typed errors.
527
518
  */
@@ -532,6 +523,7 @@ export const fromFunctionReference = <
532
523
  Returns_,
533
524
  >(
534
525
  functionReference: ConvexFunctionReference<Type, Visibility, Args_, Returns_>,
526
+ functionType?: NoInfer<Type>,
535
527
  ): ConvexRef<
536
528
  Extract<
537
529
  RuntimeAndFunctionType.RuntimeAndFunctionType,
@@ -545,6 +537,7 @@ export const fromFunctionReference = <
545
537
  return {
546
538
  [TypeId]: TypeId,
547
539
  _tag: "Convex",
540
+ ...(functionType === undefined ? {} : { functionType }),
548
541
  convexFunctionName:
549
542
  address.name ??
550
543
  address.reference ??
@@ -0,0 +1,26 @@
1
+ import { makeFunctionReference } from "convex/server";
2
+ import type * as Ref from "./Ref.js";
3
+
4
+ const functionReferenceCache = new Map<
5
+ string,
6
+ Ref.FunctionReference<Ref.Any>
7
+ >();
8
+
9
+ export const getFunctionReference = <Ref_ extends Ref.Any>(
10
+ ref: Ref_,
11
+ ): Ref.FunctionReference<Ref_> => {
12
+ if (ref.functionReference !== undefined) {
13
+ return ref.functionReference as Ref.FunctionReference<Ref_>;
14
+ }
15
+ const convexFunctionName = ref.convexFunctionName;
16
+
17
+ const cached = functionReferenceCache.get(convexFunctionName);
18
+ if (cached !== undefined) {
19
+ return cached as Ref.FunctionReference<Ref_>;
20
+ }
21
+
22
+ const functionReference = makeFunctionReference(convexFunctionName);
23
+ functionReferenceCache.set(convexFunctionName, functionReference);
24
+
25
+ return functionReference as Ref.FunctionReference<Ref_>;
26
+ };
@@ -1,5 +1,8 @@
1
1
  import * as IdScope from "./IdScope.js";
2
+ import type * as Callback from "./Callback.js";
3
+ import { CallbackContract } from "./internal/annotations.js";
2
4
  import type {
5
+ CommitTsPlaceholder,
3
6
  GenericId as ConvexGenericId,
4
7
  PropertyValidators,
5
8
  Validator,
@@ -7,6 +10,7 @@ import type {
7
10
  VArray,
8
11
  VBoolean,
9
12
  VBytes,
13
+ VCommitTs,
10
14
  VFloat64,
11
15
  VId,
12
16
  VInt64,
@@ -30,6 +34,7 @@ import * as Schema from "effect/Schema";
30
34
  import * as SchemaAST from "effect/SchemaAST";
31
35
  import * as String from "effect/String";
32
36
 
37
+ import * as CommitTs from "./CommitTs.js";
33
38
  import * as GenericId from "./GenericId.js";
34
39
  import type {
35
40
  IsAny,
@@ -46,10 +51,24 @@ import type {
46
51
  export const compileArgsSchema = <ConfectValue, ConvexValue>(
47
52
  argsSchema: Schema.Codec<ConfectValue, ConvexValue>,
48
53
  scope: IdScope.IdScope = IdScope.app,
54
+ reservedArgument?: string,
55
+ functionName?: string,
49
56
  ): PropertyValidators => {
50
57
  const ast = Schema.toEncoded(argsSchema).ast;
51
58
 
52
- return pipe(
59
+ const validators = Effect.runSync(compileArgsAst(ast, scope));
60
+ if (reservedArgument === undefined) return validators;
61
+ if (Object.hasOwn(validators, reservedArgument)) {
62
+ throw new ReservedArgumentCollision({
63
+ functionName,
64
+ argument: reservedArgument,
65
+ });
66
+ }
67
+ return { ...validators, [reservedArgument]: v.optional(v.any()) };
68
+ };
69
+
70
+ const compileArgsAst = (ast: SchemaAST.AST, scope: IdScope.IdScope) =>
71
+ pipe(
53
72
  ast,
54
73
  Match.value,
55
74
  Match.tag("Objects", (objectsAst) =>
@@ -58,9 +77,18 @@ export const compileArgsSchema = <ConfectValue, ConvexValue>(
58
77
  : Effect.fail(new IndexSignaturesAreNotSupportedError()),
59
78
  ),
60
79
  Match.orElse(() => Effect.fail(new TopLevelMustBeObjectError())),
61
- Effect.runSync,
62
80
  );
63
- };
81
+
82
+ export class ReservedArgumentCollision extends Data.TaggedError(
83
+ "ReservedArgumentCollision",
84
+ )<{
85
+ readonly functionName: string | undefined;
86
+ readonly argument: string;
87
+ }> {
88
+ override get message() {
89
+ return `${this.functionName} declares the argument "${this.argument}", which its profile reserves. Rename the argument.`;
90
+ }
91
+ }
64
92
 
65
93
  // Returns
66
94
 
@@ -113,6 +141,7 @@ export type ReadonlyValue =
113
141
  | bigint
114
142
  | boolean
115
143
  | ArrayBuffer
144
+ | CommitTsPlaceholder
116
145
  | ReadonlyArrayValue
117
146
  | ReadonlyRecordValue
118
147
  | null;
@@ -160,7 +189,9 @@ type ConvertValueToValidator<
160
189
  ? VBoolean<OptionalValue<boolean, Optionality>, Optionality>
161
190
  : VLiteral<OptionalValue<Vl, Optionality>, Optionality>
162
191
  : IsUnion<Vl> extends true
163
- ? UnionValueToValidator<Vl, Optionality>
192
+ ? IsCommitTs<Vl> extends true
193
+ ? VCommitTs<OptionalValue<Vl, Optionality>, Optionality>
194
+ : UnionValueToValidator<Vl, Optionality>
164
195
  : [Vl] extends [number]
165
196
  ? [number] extends [Vl]
166
197
  ? VFloat64<OptionalValue<number, Optionality>, Optionality>
@@ -173,17 +204,25 @@ type ConvertValueToValidator<
173
204
  ? StringValueToValidator<Vl, Optionality>
174
205
  : [Vl] extends [ArrayBuffer]
175
206
  ? VBytes<OptionalValue<ArrayBuffer, Optionality>, Optionality>
176
- : IsRecursive<Vl> extends true
177
- ? VAny<any, Optionality>
178
- : Vl extends ReadonlyArray<infer El>
179
- ? VArray<
180
- OptionalValue<MutableValue<El[]>, Optionality>,
181
- ConvertValueToValidator<El>,
182
- Optionality
183
- >
184
- : Vl extends object
185
- ? ObjectValueToValidator<Vl, Optionality>
186
- : never;
207
+ : IsCommitTs<Vl> extends true
208
+ ? VCommitTs<OptionalValue<Vl, Optionality>, Optionality>
209
+ : IsRecursive<Vl> extends true
210
+ ? VAny<any, Optionality>
211
+ : Vl extends ReadonlyArray<infer El>
212
+ ? VArray<
213
+ OptionalValue<MutableValue<El[]>, Optionality>,
214
+ ConvertValueToValidator<El>,
215
+ Optionality
216
+ >
217
+ : Vl extends object
218
+ ? ObjectValueToValidator<Vl, Optionality>
219
+ : never;
220
+
221
+ type IsCommitTs<Vl> = [Vl] extends [bigint | CommitTsPlaceholder]
222
+ ? [CommitTsPlaceholder] extends [Vl]
223
+ ? true
224
+ : false
225
+ : false;
187
226
 
188
227
  type StringValueToValidator<Vl, Optionality extends "required" | "optional"> =
189
228
  Vl extends ConvexGenericId<string>
@@ -239,11 +278,29 @@ export type UndefinedOrValueToValidator<Vl extends ReadonlyValue | undefined> =
239
278
  type UnionValueToValidator<Vl, Optionality extends "required" | "optional"> =
240
279
  IsRecursive<Vl> extends true
241
280
  ? VAny<any, Optionality>
242
- : VUnion<
243
- OptionalValue<MutableValue<Vl>, Optionality>,
244
- ValueTupleToValidatorTuple<UnionToTuple<Vl>>,
245
- Optionality
246
- >;
281
+ : [Extract<Vl, CommitTsPlaceholder>] extends [never]
282
+ ? VUnion<
283
+ OptionalValue<MutableValue<Vl>, Optionality>,
284
+ ValueTupleToValidatorTuple<UnionToTuple<Vl>>,
285
+ Optionality
286
+ >
287
+ : CommitTsUnionValueToValidator<Vl, Optionality>;
288
+
289
+ // A CommitTs is one validator at runtime, so its bigint and placeholder
290
+ // branches collapse into a single member, as true and false do into VBoolean.
291
+ // Only a union holding a placeholder comes here, because the infer that lets
292
+ // the spread tuple satisfy VUnion's constraint makes other unions far costlier.
293
+ type CommitTsUnionValueToValidator<
294
+ Vl,
295
+ Optionality extends "required" | "optional",
296
+ > = [
297
+ VCommitTs,
298
+ ...ValueTupleToValidatorTuple<
299
+ UnionToTuple<Exclude<Vl, bigint | CommitTsPlaceholder>>
300
+ >,
301
+ ] extends infer VdMembers extends Validator<any, "required", any>[]
302
+ ? VUnion<OptionalValue<MutableValue<Vl>, Optionality>, VdMembers, Optionality>
303
+ : never;
247
304
 
248
305
  type ValueTupleToValidatorTuple<
249
306
  VlTuple extends readonly unknown[],
@@ -313,6 +370,7 @@ export const compileAst = (
313
370
  scope: IdScope.IdScope = IdScope.app,
314
371
  ): Effect.Effect<
315
372
  Validator<any, any, any>,
373
+ | TopLevelMustBeObjectError
316
374
  | UnsupportedSchemaTypeError
317
375
  | UnsupportedPropertySignatureKeyTypeError
318
376
  | IndexSignaturesAreNotSupportedError
@@ -320,8 +378,24 @@ export const compileAst = (
320
378
  | OptionalTupleElementsAreNotSupportedError
321
379
  | EmptyTupleIsNotSupportedError
322
380
  | EmptyUnionIsNotSupportedError
323
- > =>
324
- isRecursive(ast)
381
+ > => {
382
+ const contract = SchemaAST.resolveAt<Callback.Any>(CallbackContract)(ast);
383
+ if (contract !== undefined) {
384
+ return Effect.gen(function* () {
385
+ const args = yield* compileArgsAst(
386
+ Schema.toEncoded(contract.functionProvenance.args).ast,
387
+ scope,
388
+ );
389
+ const returns = yield* compileAst(
390
+ Schema.toEncoded(contract.functionProvenance.returns).ast,
391
+ false,
392
+ scope,
393
+ );
394
+ return v.function({ fnType: contract.functionType, args, returns });
395
+ });
396
+ }
397
+ if (CommitTs.isCommitTs(ast)) return Effect.succeed(v.commitTs());
398
+ return isRecursive(ast)
325
399
  ? Effect.succeed(v.any())
326
400
  : pipe(
327
401
  ast,
@@ -400,6 +474,7 @@ export const compileAst = (
400
474
  ),
401
475
  Match.exhaustive,
402
476
  );
477
+ };
403
478
 
404
479
  const handleUnion = (
405
480
  { types }: SchemaAST.Union,