@prisma-next/sql-contract-ts 0.3.0-pr.99.6 → 0.4.0-dev.1

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 (41) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +206 -73
  3. package/dist/config-types.d.mts +8 -0
  4. package/dist/config-types.d.mts.map +1 -0
  5. package/dist/config-types.mjs +14 -0
  6. package/dist/config-types.mjs.map +1 -0
  7. package/dist/contract-builder.d.mts +769 -0
  8. package/dist/contract-builder.d.mts.map +1 -0
  9. package/dist/contract-builder.mjs +1288 -0
  10. package/dist/contract-builder.mjs.map +1 -0
  11. package/package.json +19 -16
  12. package/schemas/data-contract-sql-v1.json +189 -23
  13. package/src/authoring-helper-runtime.ts +139 -0
  14. package/src/authoring-type-utils.ts +168 -0
  15. package/src/build-contract.ts +463 -0
  16. package/src/composed-authoring-helpers.ts +256 -0
  17. package/src/config-types.ts +11 -0
  18. package/src/contract-builder.ts +232 -551
  19. package/src/contract-definition.ts +103 -0
  20. package/src/contract-dsl.ts +1492 -0
  21. package/src/contract-lowering.ts +703 -0
  22. package/src/contract-types.ts +534 -0
  23. package/src/contract-warnings.ts +242 -0
  24. package/src/exports/config-types.ts +2 -0
  25. package/src/exports/contract-builder.ts +23 -2
  26. package/dist/chunk-HTNUNGA2.js +0 -346
  27. package/dist/chunk-HTNUNGA2.js.map +0 -1
  28. package/dist/contract-builder.d.ts +0 -101
  29. package/dist/contract-builder.d.ts.map +0 -1
  30. package/dist/contract.d.ts +0 -50
  31. package/dist/contract.d.ts.map +0 -1
  32. package/dist/exports/contract-builder.d.ts +0 -3
  33. package/dist/exports/contract-builder.d.ts.map +0 -1
  34. package/dist/exports/contract-builder.js +0 -231
  35. package/dist/exports/contract-builder.js.map +0 -1
  36. package/dist/exports/contract.d.ts +0 -2
  37. package/dist/exports/contract.d.ts.map +0 -1
  38. package/dist/exports/contract.js +0 -9
  39. package/dist/exports/contract.js.map +0 -1
  40. package/src/contract.ts +0 -582
  41. package/src/exports/contract.ts +0 -1
@@ -24,10 +24,10 @@
24
24
  "enum": ["sql"],
25
25
  "description": "Target family classification"
26
26
  },
27
- "coreHash": {
27
+ "storageHash": {
28
28
  "type": "string",
29
29
  "pattern": "^sha256:[a-f0-9]{64}$",
30
- "description": "SHA-256 hash of the core schema structure (models, fields, relations, storage layout)"
30
+ "description": "SHA-256 hash of the storage section (DB-satisfied expectations)"
31
31
  },
32
32
  "profileHash": {
33
33
  "type": "string",
@@ -44,7 +44,7 @@
44
44
  }
45
45
  }
46
46
  },
47
- "extensions": {
47
+ "extensionPacks": {
48
48
  "type": "object",
49
49
  "description": "Extension packs and their configuration",
50
50
  "additionalProperties": true
@@ -61,6 +61,13 @@
61
61
  "$ref": "#/$defs/Source"
62
62
  }
63
63
  },
64
+ "roots": {
65
+ "type": "object",
66
+ "description": "Mapping of root model names to their identifiers",
67
+ "additionalProperties": {
68
+ "type": "string"
69
+ }
70
+ },
64
71
  "models": {
65
72
  "type": "object",
66
73
  "description": "Model definitions mapping application-level models to storage tables",
@@ -89,9 +96,41 @@
89
96
  }
90
97
  },
91
98
  "required": ["tables"]
99
+ },
100
+ "execution": {
101
+ "type": "object",
102
+ "description": "Execution-only behavior not satisfied by the database",
103
+ "additionalProperties": false,
104
+ "properties": {
105
+ "executionHash": {
106
+ "type": "string",
107
+ "pattern": "^sha256:[a-f0-9]{64}$",
108
+ "description": "SHA-256 hash of the execution section (client-side behavior)"
109
+ },
110
+ "mutations": {
111
+ "type": "object",
112
+ "additionalProperties": false,
113
+ "properties": {
114
+ "defaults": {
115
+ "type": "array",
116
+ "items": { "$ref": "#/$defs/ExecutionMutationDefault" }
117
+ }
118
+ },
119
+ "required": ["defaults"]
120
+ }
121
+ },
122
+ "required": ["executionHash", "mutations"]
92
123
  }
93
124
  },
94
- "required": ["schemaVersion", "target", "targetFamily", "coreHash", "models", "storage"],
125
+ "required": [
126
+ "schemaVersion",
127
+ "target",
128
+ "targetFamily",
129
+ "storageHash",
130
+ "models",
131
+ "storage",
132
+ "roots"
133
+ ],
95
134
  "$defs": {
96
135
  "StorageTable": {
97
136
  "type": "object",
@@ -133,6 +172,88 @@
133
172
  },
134
173
  "required": ["columns"]
135
174
  },
175
+ "ColumnDefault": {
176
+ "description": "Column default value. Can be a literal value or a database function expression.",
177
+ "oneOf": [
178
+ {
179
+ "type": "object",
180
+ "description": "Static literal value",
181
+ "additionalProperties": false,
182
+ "properties": {
183
+ "kind": {
184
+ "type": "string",
185
+ "enum": ["literal"]
186
+ },
187
+ "value": {
188
+ "description": "JSON literal value (or tagged bigint) for the default value",
189
+ "oneOf": [
190
+ { "type": "string" },
191
+ { "type": "number" },
192
+ { "type": "boolean" },
193
+ { "type": "null" },
194
+ { "type": "array" },
195
+ { "type": "object" }
196
+ ]
197
+ }
198
+ },
199
+ "required": ["kind", "value"]
200
+ },
201
+ {
202
+ "type": "object",
203
+ "description": "Database function expression (e.g., 'autoincrement()', 'now()', 'gen_random_uuid()')",
204
+ "additionalProperties": false,
205
+ "properties": {
206
+ "kind": {
207
+ "type": "string",
208
+ "enum": ["function"]
209
+ },
210
+ "expression": {
211
+ "type": "string",
212
+ "description": "The function expression (e.g., 'autoincrement()', 'now()', 'gen_random_uuid()')"
213
+ }
214
+ },
215
+ "required": ["kind", "expression"]
216
+ }
217
+ ]
218
+ },
219
+ "ExecutionMutationDefaultValue": {
220
+ "type": "object",
221
+ "description": "Execution-time default value applied before a write",
222
+ "additionalProperties": false,
223
+ "properties": {
224
+ "kind": {
225
+ "type": "string",
226
+ "enum": ["generator"]
227
+ },
228
+ "id": {
229
+ "type": "string",
230
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]*$"
231
+ },
232
+ "params": {
233
+ "type": "object",
234
+ "additionalProperties": true
235
+ }
236
+ },
237
+ "required": ["kind", "id"]
238
+ },
239
+ "ExecutionMutationDefault": {
240
+ "type": "object",
241
+ "additionalProperties": false,
242
+ "properties": {
243
+ "ref": {
244
+ "type": "object",
245
+ "additionalProperties": false,
246
+ "properties": {
247
+ "table": { "type": "string" },
248
+ "column": { "type": "string" }
249
+ },
250
+ "required": ["table", "column"]
251
+ },
252
+ "onCreate": { "$ref": "#/$defs/ExecutionMutationDefaultValue" },
253
+ "onUpdate": { "$ref": "#/$defs/ExecutionMutationDefaultValue" }
254
+ },
255
+ "required": ["ref"]
256
+ },
136
257
  "StorageColumn": {
137
258
  "type": "object",
138
259
  "description": "Column definition with type, nullability, and optional parameterized type info",
@@ -159,6 +280,10 @@
159
280
  "typeRef": {
160
281
  "type": "string",
161
282
  "description": "Reference to a named type instance in storage.types"
283
+ },
284
+ "default": {
285
+ "$ref": "#/$defs/ColumnDefault",
286
+ "description": "Default value for the column"
162
287
  }
163
288
  },
164
289
  "required": ["nativeType", "codecId", "nullable"]
@@ -276,9 +401,17 @@
276
401
  "name": {
277
402
  "type": "string",
278
403
  "description": "Foreign key constraint name"
404
+ },
405
+ "constraint": {
406
+ "type": "boolean",
407
+ "description": "Whether to emit FK constraint DDL (ALTER TABLE … ADD CONSTRAINT … FOREIGN KEY)"
408
+ },
409
+ "index": {
410
+ "type": "boolean",
411
+ "description": "Whether to emit a backing index for the FK columns"
279
412
  }
280
413
  },
281
- "required": ["columns", "references"]
414
+ "required": ["columns", "references", "constraint", "index"]
282
415
  },
283
416
  "FieldType": {
284
417
  "type": "object",
@@ -352,13 +485,20 @@
352
485
  "table": {
353
486
  "type": "string",
354
487
  "description": "Table name in storage.tables"
488
+ },
489
+ "fields": {
490
+ "type": "object",
491
+ "description": "Per-field storage mappings",
492
+ "additionalProperties": {
493
+ "$ref": "#/$defs/ModelStorageField"
494
+ }
355
495
  }
356
496
  },
357
497
  "required": ["table"]
358
498
  },
359
499
  "fields": {
360
500
  "type": "object",
361
- "description": "Field definitions mapping model fields to storage columns",
501
+ "description": "Domain field metadata (codecId, nullable)",
362
502
  "additionalProperties": {
363
503
  "$ref": "#/$defs/ModelField"
364
504
  }
@@ -369,26 +509,53 @@
369
509
  "additionalProperties": {
370
510
  "$ref": "#/$defs/ModelRelation"
371
511
  }
512
+ },
513
+ "owner": {
514
+ "type": "string",
515
+ "description": "Owner model name — declares this model belongs to another model's aggregate (per ADR 177)"
372
516
  }
373
517
  },
374
518
  "required": ["storage", "fields"]
375
519
  },
376
520
  "ModelField": {
377
521
  "type": "object",
378
- "description": "Model field definition mapping to storage column",
522
+ "description": "Domain field definition (codec and nullability; column mapping lives in model.storage.fields)",
523
+ "additionalProperties": false,
524
+ "properties": {
525
+ "codecId": {
526
+ "type": "string",
527
+ "description": "Codec identifier for the field"
528
+ },
529
+ "nullable": {
530
+ "type": "boolean",
531
+ "description": "Whether the field allows NULL values"
532
+ }
533
+ },
534
+ "required": ["codecId", "nullable"]
535
+ },
536
+ "ModelStorageField": {
537
+ "type": "object",
538
+ "description": "Per-field storage mapping",
379
539
  "additionalProperties": false,
380
540
  "properties": {
381
541
  "column": {
382
542
  "type": "string",
383
543
  "description": "Column name in the model's backing table"
544
+ },
545
+ "codecId": {
546
+ "type": "string",
547
+ "description": "Codec identifier for the field (derived from storage column)"
548
+ },
549
+ "nullable": {
550
+ "type": "boolean",
551
+ "description": "Whether the field allows NULL values (derived from storage column)"
384
552
  }
385
553
  },
386
554
  "required": ["column"]
387
555
  },
388
556
  "ModelRelation": {
389
557
  "type": "object",
390
- "description": "Model relation definition",
391
- "additionalProperties": false,
558
+ "description": "Model relation definition (domain format with localFields/targetFields)",
392
559
  "properties": {
393
560
  "to": {
394
561
  "type": "string",
@@ -399,30 +566,29 @@
399
566
  "enum": ["1:1", "1:N", "N:1", "N:M"],
400
567
  "description": "Relation cardinality"
401
568
  },
569
+ "strategy": {
570
+ "type": "string",
571
+ "enum": ["reference", "embed"],
572
+ "description": "Relation strategy"
573
+ },
402
574
  "on": {
403
575
  "type": "object",
404
576
  "description": "Relation field mappings",
405
- "additionalProperties": false,
406
577
  "properties": {
407
- "parentCols": {
578
+ "localFields": {
408
579
  "type": "array",
409
- "description": "Parent table columns",
410
- "items": {
411
- "type": "string"
412
- }
580
+ "description": "Local model fields",
581
+ "items": { "type": "string" }
413
582
  },
414
- "childCols": {
583
+ "targetFields": {
415
584
  "type": "array",
416
- "description": "Child table columns",
417
- "items": {
418
- "type": "string"
419
- }
585
+ "description": "Target model fields",
586
+ "items": { "type": "string" }
420
587
  }
421
- },
422
- "required": ["parentCols", "childCols"]
588
+ }
423
589
  }
424
590
  },
425
- "required": ["to", "cardinality", "on"]
591
+ "required": ["to", "cardinality"]
426
592
  }
427
593
  }
428
594
  }
@@ -0,0 +1,139 @@
1
+ import type {
2
+ AuthoringFieldNamespace,
3
+ AuthoringFieldPresetDescriptor,
4
+ AuthoringTypeNamespace,
5
+ } from '@prisma-next/framework-components/authoring';
6
+ import {
7
+ instantiateAuthoringTypeConstructor,
8
+ isAuthoringFieldPresetDescriptor,
9
+ isAuthoringTypeConstructorDescriptor,
10
+ validateAuthoringHelperArguments,
11
+ } from '@prisma-next/framework-components/authoring';
12
+ import type { StorageTypeInstance } from '@prisma-next/sql-contract/types';
13
+
14
+ export type RuntimeNamedConstraintSpec = {
15
+ readonly name?: string;
16
+ };
17
+
18
+ export function isNamedConstraintOptionsLike(value: unknown): value is RuntimeNamedConstraintSpec {
19
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
20
+ return false;
21
+ }
22
+
23
+ const keys = Object.keys(value as Record<string, unknown>);
24
+ if (keys.some((key) => key !== 'name')) {
25
+ return false;
26
+ }
27
+
28
+ const name = (value as { readonly name?: unknown }).name;
29
+ return name === undefined || typeof name === 'string';
30
+ }
31
+
32
+ const blockedSegments = new Set(['__proto__', 'constructor', 'prototype']);
33
+
34
+ function assertSafeHelperKey(key: string, path: readonly string[]): void {
35
+ if (blockedSegments.has(key)) {
36
+ throw new Error(
37
+ `Invalid authoring helper "${[...path, key].join('.')}". Helper path segments must not use "${key}".`,
38
+ );
39
+ }
40
+ }
41
+
42
+ export function createTypeHelpersFromNamespace(
43
+ namespace: AuthoringTypeNamespace,
44
+ path: readonly string[] = [],
45
+ ): Record<string, unknown> {
46
+ const helpers: Record<string, unknown> = {};
47
+
48
+ for (const [key, value] of Object.entries(namespace)) {
49
+ assertSafeHelperKey(key, path);
50
+ const currentPath = [...path, key];
51
+
52
+ if (isAuthoringTypeConstructorDescriptor(value)) {
53
+ const helperPath = currentPath.join('.');
54
+ helpers[key] = (...args: readonly unknown[]) => {
55
+ validateAuthoringHelperArguments(helperPath, value.args, args);
56
+ return instantiateAuthoringTypeConstructor(value, args) as StorageTypeInstance;
57
+ };
58
+ continue;
59
+ }
60
+
61
+ helpers[key] = createTypeHelpersFromNamespace(value as AuthoringTypeNamespace, currentPath);
62
+ }
63
+
64
+ return helpers;
65
+ }
66
+
67
+ export function createFieldPresetHelper<Result>(options: {
68
+ readonly helperPath: string;
69
+ readonly descriptor: AuthoringFieldPresetDescriptor;
70
+ readonly build: (options: {
71
+ readonly args: readonly unknown[];
72
+ readonly namedConstraintOptions?: RuntimeNamedConstraintSpec;
73
+ }) => Result;
74
+ }): (...rawArgs: readonly unknown[]) => Result {
75
+ return (...rawArgs: readonly unknown[]) => {
76
+ const acceptsNamedConstraintOptions =
77
+ options.descriptor.output.id === true || options.descriptor.output.unique === true;
78
+ const declaredArguments = options.descriptor.args ?? [];
79
+
80
+ if (acceptsNamedConstraintOptions && rawArgs.length > declaredArguments.length + 1) {
81
+ throw new Error(
82
+ `${options.helperPath} expects at most ${declaredArguments.length + 1} argument(s), received ${rawArgs.length}`,
83
+ );
84
+ }
85
+
86
+ let args = rawArgs;
87
+ let namedConstraintOptions: RuntimeNamedConstraintSpec | undefined;
88
+
89
+ if (acceptsNamedConstraintOptions && rawArgs.length === declaredArguments.length + 1) {
90
+ const maybeNamedConstraintOptions = rawArgs.at(-1);
91
+ if (!isNamedConstraintOptionsLike(maybeNamedConstraintOptions)) {
92
+ throw new Error(
93
+ `${options.helperPath} accepts an optional trailing { name?: string } constraint options object`,
94
+ );
95
+ }
96
+ namedConstraintOptions = maybeNamedConstraintOptions;
97
+ args = rawArgs.slice(0, -1);
98
+ }
99
+
100
+ validateAuthoringHelperArguments(options.helperPath, options.descriptor.args, args);
101
+
102
+ return options.build({
103
+ args,
104
+ ...(namedConstraintOptions ? { namedConstraintOptions } : {}),
105
+ });
106
+ };
107
+ }
108
+
109
+ export function createFieldHelpersFromNamespace(
110
+ namespace: AuthoringFieldNamespace,
111
+ createLeafHelper: (options: {
112
+ readonly helperPath: string;
113
+ readonly descriptor: AuthoringFieldPresetDescriptor;
114
+ }) => (...rawArgs: readonly unknown[]) => unknown,
115
+ path: readonly string[] = [],
116
+ ): Record<string, unknown> {
117
+ const helpers: Record<string, unknown> = {};
118
+
119
+ for (const [key, value] of Object.entries(namespace)) {
120
+ assertSafeHelperKey(key, path);
121
+ const currentPath = [...path, key];
122
+
123
+ if (isAuthoringFieldPresetDescriptor(value)) {
124
+ helpers[key] = createLeafHelper({
125
+ helperPath: currentPath.join('.'),
126
+ descriptor: value,
127
+ });
128
+ continue;
129
+ }
130
+
131
+ helpers[key] = createFieldHelpersFromNamespace(
132
+ value as AuthoringFieldNamespace,
133
+ createLeafHelper,
134
+ currentPath,
135
+ );
136
+ }
137
+
138
+ return helpers;
139
+ }
@@ -0,0 +1,168 @@
1
+ import type {
2
+ AuthoringArgumentDescriptor,
3
+ AuthoringFieldPresetDescriptor,
4
+ } from '@prisma-next/framework-components/authoring';
5
+ import type { ScalarFieldBuilder, ScalarFieldState } from './contract-dsl';
6
+
7
+ export type UnionToIntersection<U> = (U extends unknown ? (value: U) => void : never) extends (
8
+ value: infer I,
9
+ ) => void
10
+ ? I
11
+ : never;
12
+
13
+ export type NamedConstraintSpec<Name extends string | undefined = string | undefined> = {
14
+ readonly name?: Name;
15
+ };
16
+
17
+ export type NamedConstraintState<
18
+ Enabled extends boolean,
19
+ Name extends string | undefined = undefined,
20
+ > = Enabled extends true ? NamedConstraintSpec<Name> : undefined;
21
+
22
+ export type OptionalObjectArgumentKeys<
23
+ Properties extends Record<string, AuthoringArgumentDescriptor>,
24
+ > = {
25
+ readonly [K in keyof Properties]: Properties[K] extends { readonly optional: true } ? K : never;
26
+ }[keyof Properties];
27
+
28
+ export type ObjectArgumentType<Properties extends Record<string, AuthoringArgumentDescriptor>> = {
29
+ readonly [K in Exclude<
30
+ keyof Properties,
31
+ OptionalObjectArgumentKeys<Properties>
32
+ >]: ArgTypeFromDescriptor<Properties[K]>;
33
+ } & {
34
+ readonly [K in OptionalObjectArgumentKeys<Properties>]?: ArgTypeFromDescriptor<Properties[K]>;
35
+ };
36
+
37
+ export type ArgTypeFromDescriptor<Arg extends AuthoringArgumentDescriptor> = Arg extends {
38
+ readonly kind: 'string';
39
+ }
40
+ ? string
41
+ : Arg extends { readonly kind: 'number' }
42
+ ? number
43
+ : Arg extends { readonly kind: 'stringArray' }
44
+ ? readonly string[]
45
+ : Arg extends {
46
+ readonly kind: 'object';
47
+ readonly properties: infer Properties extends Record<
48
+ string,
49
+ AuthoringArgumentDescriptor
50
+ >;
51
+ }
52
+ ? ObjectArgumentType<Properties>
53
+ : never;
54
+
55
+ export type TupleFromArgumentDescriptors<Args extends readonly AuthoringArgumentDescriptor[]> = {
56
+ readonly [K in keyof Args]: Args[K] extends AuthoringArgumentDescriptor
57
+ ? ArgTypeFromDescriptor<Args[K]>
58
+ : never;
59
+ };
60
+
61
+ export type SupportsNamedConstraintOptions<Descriptor extends AuthoringFieldPresetDescriptor> =
62
+ Descriptor['output'] extends { readonly id: true }
63
+ ? true
64
+ : Descriptor['output'] extends { readonly unique: true }
65
+ ? true
66
+ : false;
67
+
68
+ export type ResolveTemplateValue<Template, Args extends readonly unknown[]> = Template extends {
69
+ readonly kind: 'arg';
70
+ readonly index: infer Index extends number;
71
+ readonly path?: infer Path extends readonly string[] | undefined;
72
+ readonly default?: infer Default;
73
+ }
74
+ ? ResolveTemplateArgValue<Args[Index], Path, Default, Args>
75
+ : Template extends readonly unknown[]
76
+ ? { readonly [K in keyof Template]: ResolveTemplateValue<Template[K], Args> }
77
+ : Template extends Record<string, unknown>
78
+ ? { readonly [K in keyof Template]: ResolveTemplateValue<Template[K], Args> }
79
+ : Template;
80
+
81
+ type ResolveTemplatePathValue<
82
+ Value,
83
+ Path extends readonly string[] | undefined,
84
+ > = Path extends readonly [infer Segment extends string, ...infer Rest extends readonly string[]]
85
+ ? Segment extends keyof NonNullable<Value>
86
+ ? ResolveTemplatePathValue<NonNullable<Value>[Segment], Rest>
87
+ : never
88
+ : Value;
89
+
90
+ type ResolveTemplateDefaultValue<
91
+ Value,
92
+ Default,
93
+ Args extends readonly unknown[],
94
+ > = Default extends undefined
95
+ ? Value
96
+ : [Value] extends [never]
97
+ ? ResolveTemplateValue<Default, Args>
98
+ : undefined extends Value
99
+ ? Exclude<Value, undefined> | ResolveTemplateValue<Default, Args>
100
+ : Value;
101
+
102
+ type ResolveTemplateArgValue<
103
+ Value,
104
+ Path extends readonly string[] | undefined,
105
+ Default,
106
+ Args extends readonly unknown[],
107
+ > = ResolveTemplateDefaultValue<ResolveTemplatePathValue<Value, Path>, Default, Args>;
108
+
109
+ export type FieldBuilderFromPresetDescriptor<
110
+ Descriptor extends AuthoringFieldPresetDescriptor,
111
+ Args extends readonly unknown[] = readonly [],
112
+ ConstraintName extends string | undefined = undefined,
113
+ > = ScalarFieldBuilder<
114
+ ScalarFieldState<
115
+ ResolveTemplateValue<Descriptor['output']['codecId'], Args> extends string
116
+ ? ResolveTemplateValue<Descriptor['output']['codecId'], Args>
117
+ : string,
118
+ undefined,
119
+ ResolveTemplateValue<Descriptor['output']['nullable'], Args> extends true ? true : false,
120
+ undefined,
121
+ NamedConstraintState<
122
+ ResolveTemplateValue<Descriptor['output']['id'], Args> extends true ? true : false,
123
+ ConstraintName
124
+ >,
125
+ NamedConstraintState<
126
+ ResolveTemplateValue<Descriptor['output']['unique'], Args> extends true ? true : false,
127
+ ConstraintName
128
+ >
129
+ >
130
+ >;
131
+
132
+ export type FieldHelperFunctionWithoutNamedConstraint<
133
+ Descriptor extends AuthoringFieldPresetDescriptor,
134
+ > = Descriptor extends {
135
+ readonly args: infer Args extends readonly AuthoringArgumentDescriptor[];
136
+ }
137
+ ? <const Params extends TupleFromArgumentDescriptors<Args>>(
138
+ ...args: Params
139
+ ) => FieldBuilderFromPresetDescriptor<Descriptor, Params>
140
+ : () => FieldBuilderFromPresetDescriptor<Descriptor, readonly []>;
141
+
142
+ export type FieldHelperFunctionWithNamedConstraint<
143
+ Descriptor extends AuthoringFieldPresetDescriptor,
144
+ > = Descriptor extends {
145
+ readonly args: infer Args extends readonly AuthoringArgumentDescriptor[];
146
+ }
147
+ ? <
148
+ const Params extends TupleFromArgumentDescriptors<Args>,
149
+ const Name extends string | undefined = undefined,
150
+ >(
151
+ ...args: [...params: Params, options?: NamedConstraintSpec<Name>]
152
+ ) => FieldBuilderFromPresetDescriptor<Descriptor, Params, Name>
153
+ : <const Name extends string | undefined = undefined>(
154
+ options?: NamedConstraintSpec<Name>,
155
+ ) => FieldBuilderFromPresetDescriptor<Descriptor, readonly [], Name>;
156
+
157
+ export type FieldHelperFunction<Descriptor extends AuthoringFieldPresetDescriptor> =
158
+ SupportsNamedConstraintOptions<Descriptor> extends true
159
+ ? FieldHelperFunctionWithNamedConstraint<Descriptor>
160
+ : FieldHelperFunctionWithoutNamedConstraint<Descriptor>;
161
+
162
+ export type FieldHelpersFromNamespace<Namespace> = {
163
+ readonly [K in keyof Namespace]: Namespace[K] extends AuthoringFieldPresetDescriptor
164
+ ? FieldHelperFunction<Namespace[K]>
165
+ : Namespace[K] extends Record<string, unknown>
166
+ ? FieldHelpersFromNamespace<Namespace[K]>
167
+ : never;
168
+ };