@prisma-next/sql-contract-ts 0.3.0-dev.16 → 0.3.0-dev.162
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +209 -54
- package/dist/config-types.d.mts +8 -0
- package/dist/config-types.d.mts.map +1 -0
- package/dist/config-types.mjs +14 -0
- package/dist/config-types.mjs.map +1 -0
- package/dist/contract-builder.d.mts +769 -0
- package/dist/contract-builder.d.mts.map +1 -0
- package/dist/contract-builder.mjs +1288 -0
- package/dist/contract-builder.mjs.map +1 -0
- package/package.json +22 -19
- package/schemas/data-contract-sql-v1.json +235 -27
- package/src/authoring-helper-runtime.ts +139 -0
- package/src/authoring-type-utils.ts +168 -0
- package/src/build-contract.ts +463 -0
- package/src/composed-authoring-helpers.ts +256 -0
- package/src/config-types.ts +11 -0
- package/src/contract-builder.ts +232 -526
- package/src/contract-definition.ts +103 -0
- package/src/contract-dsl.ts +1492 -0
- package/src/contract-lowering.ts +703 -0
- package/src/contract-types.ts +534 -0
- package/src/contract-warnings.ts +242 -0
- package/src/exports/config-types.ts +2 -0
- package/src/exports/contract-builder.ts +23 -2
- package/dist/chunk-SEOX3AAQ.js +0 -309
- package/dist/chunk-SEOX3AAQ.js.map +0 -1
- package/dist/contract-builder.d.ts +0 -87
- package/dist/contract-builder.d.ts.map +0 -1
- package/dist/contract.d.ts +0 -50
- package/dist/contract.d.ts.map +0 -1
- package/dist/exports/contract-builder.d.ts +0 -3
- package/dist/exports/contract-builder.d.ts.map +0 -1
- package/dist/exports/contract-builder.js +0 -216
- package/dist/exports/contract-builder.js.map +0 -1
- package/dist/exports/contract.d.ts +0 -2
- package/dist/exports/contract.d.ts.map +0 -1
- package/dist/exports/contract.js +0 -9
- package/dist/exports/contract.js.map +0 -1
- package/src/contract.ts +0 -533
- package/src/exports/contract.ts +0 -1
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"enum": ["sql"],
|
|
25
25
|
"description": "Target family classification"
|
|
26
26
|
},
|
|
27
|
-
"
|
|
27
|
+
"storageHash": {
|
|
28
28
|
"type": "string",
|
|
29
29
|
"pattern": "^sha256:[a-f0-9]{64}$",
|
|
30
|
-
"description": "SHA-256 hash of the
|
|
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
|
-
"
|
|
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",
|
|
@@ -79,12 +86,51 @@
|
|
|
79
86
|
"additionalProperties": {
|
|
80
87
|
"$ref": "#/$defs/StorageTable"
|
|
81
88
|
}
|
|
89
|
+
},
|
|
90
|
+
"types": {
|
|
91
|
+
"type": "object",
|
|
92
|
+
"description": "Named type instances for parameterized/custom types (e.g., vectors, enums)",
|
|
93
|
+
"additionalProperties": {
|
|
94
|
+
"$ref": "#/$defs/StorageTypeInstance"
|
|
95
|
+
}
|
|
82
96
|
}
|
|
83
97
|
},
|
|
84
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"]
|
|
85
123
|
}
|
|
86
124
|
},
|
|
87
|
-
"required": [
|
|
125
|
+
"required": [
|
|
126
|
+
"schemaVersion",
|
|
127
|
+
"target",
|
|
128
|
+
"targetFamily",
|
|
129
|
+
"storageHash",
|
|
130
|
+
"models",
|
|
131
|
+
"storage",
|
|
132
|
+
"roots"
|
|
133
|
+
],
|
|
88
134
|
"$defs": {
|
|
89
135
|
"StorageTable": {
|
|
90
136
|
"type": "object",
|
|
@@ -126,21 +172,142 @@
|
|
|
126
172
|
},
|
|
127
173
|
"required": ["columns"]
|
|
128
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
|
+
},
|
|
129
257
|
"StorageColumn": {
|
|
130
258
|
"type": "object",
|
|
131
|
-
"description": "Column definition with type and
|
|
259
|
+
"description": "Column definition with type, nullability, and optional parameterized type info",
|
|
132
260
|
"additionalProperties": false,
|
|
133
261
|
"properties": {
|
|
134
|
-
"
|
|
262
|
+
"nativeType": {
|
|
263
|
+
"type": "string",
|
|
264
|
+
"description": "Database-native type (e.g., 'text', 'int4', 'timestamptz', 'vector(1536)')"
|
|
265
|
+
},
|
|
266
|
+
"codecId": {
|
|
135
267
|
"type": "string",
|
|
136
|
-
"description": "
|
|
268
|
+
"description": "Codec identifier for encoding/decoding (e.g., 'pg/text@1', 'pg/vector@1')"
|
|
137
269
|
},
|
|
138
270
|
"nullable": {
|
|
139
271
|
"type": "boolean",
|
|
140
272
|
"default": false,
|
|
141
273
|
"description": "Whether the column allows NULL values"
|
|
274
|
+
},
|
|
275
|
+
"typeParams": {
|
|
276
|
+
"type": "object",
|
|
277
|
+
"description": "Opaque, codec-owned JS/type parameters (e.g., { length: 1536 } for vectors)",
|
|
278
|
+
"additionalProperties": true
|
|
279
|
+
},
|
|
280
|
+
"typeRef": {
|
|
281
|
+
"type": "string",
|
|
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"
|
|
142
287
|
}
|
|
143
|
-
}
|
|
288
|
+
},
|
|
289
|
+
"required": ["nativeType", "codecId", "nullable"]
|
|
290
|
+
},
|
|
291
|
+
"StorageTypeInstance": {
|
|
292
|
+
"type": "object",
|
|
293
|
+
"description": "Named, parameterized type instance for reuse across columns",
|
|
294
|
+
"additionalProperties": false,
|
|
295
|
+
"properties": {
|
|
296
|
+
"codecId": {
|
|
297
|
+
"type": "string",
|
|
298
|
+
"description": "Codec identifier for encoding/decoding"
|
|
299
|
+
},
|
|
300
|
+
"nativeType": {
|
|
301
|
+
"type": "string",
|
|
302
|
+
"description": "Database-native type"
|
|
303
|
+
},
|
|
304
|
+
"typeParams": {
|
|
305
|
+
"type": "object",
|
|
306
|
+
"description": "Codec-owned type parameters",
|
|
307
|
+
"additionalProperties": true
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
"required": ["codecId", "nativeType", "typeParams"]
|
|
144
311
|
},
|
|
145
312
|
"PrimaryKey": {
|
|
146
313
|
"type": "object",
|
|
@@ -234,9 +401,17 @@
|
|
|
234
401
|
"name": {
|
|
235
402
|
"type": "string",
|
|
236
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"
|
|
237
412
|
}
|
|
238
413
|
},
|
|
239
|
-
"required": ["columns", "references"]
|
|
414
|
+
"required": ["columns", "references", "constraint", "index"]
|
|
240
415
|
},
|
|
241
416
|
"FieldType": {
|
|
242
417
|
"type": "object",
|
|
@@ -310,13 +485,20 @@
|
|
|
310
485
|
"table": {
|
|
311
486
|
"type": "string",
|
|
312
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
|
+
}
|
|
313
495
|
}
|
|
314
496
|
},
|
|
315
497
|
"required": ["table"]
|
|
316
498
|
},
|
|
317
499
|
"fields": {
|
|
318
500
|
"type": "object",
|
|
319
|
-
"description": "
|
|
501
|
+
"description": "Domain field metadata (codecId, nullable)",
|
|
320
502
|
"additionalProperties": {
|
|
321
503
|
"$ref": "#/$defs/ModelField"
|
|
322
504
|
}
|
|
@@ -327,26 +509,53 @@
|
|
|
327
509
|
"additionalProperties": {
|
|
328
510
|
"$ref": "#/$defs/ModelRelation"
|
|
329
511
|
}
|
|
512
|
+
},
|
|
513
|
+
"owner": {
|
|
514
|
+
"type": "string",
|
|
515
|
+
"description": "Owner model name — declares this model belongs to another model's aggregate (per ADR 177)"
|
|
330
516
|
}
|
|
331
517
|
},
|
|
332
518
|
"required": ["storage", "fields"]
|
|
333
519
|
},
|
|
334
520
|
"ModelField": {
|
|
335
521
|
"type": "object",
|
|
336
|
-
"description": "
|
|
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",
|
|
337
539
|
"additionalProperties": false,
|
|
338
540
|
"properties": {
|
|
339
541
|
"column": {
|
|
340
542
|
"type": "string",
|
|
341
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)"
|
|
342
552
|
}
|
|
343
553
|
},
|
|
344
554
|
"required": ["column"]
|
|
345
555
|
},
|
|
346
556
|
"ModelRelation": {
|
|
347
557
|
"type": "object",
|
|
348
|
-
"description": "Model relation definition",
|
|
349
|
-
"additionalProperties": false,
|
|
558
|
+
"description": "Model relation definition (domain format with localFields/targetFields)",
|
|
350
559
|
"properties": {
|
|
351
560
|
"to": {
|
|
352
561
|
"type": "string",
|
|
@@ -357,30 +566,29 @@
|
|
|
357
566
|
"enum": ["1:1", "1:N", "N:1", "N:M"],
|
|
358
567
|
"description": "Relation cardinality"
|
|
359
568
|
},
|
|
569
|
+
"strategy": {
|
|
570
|
+
"type": "string",
|
|
571
|
+
"enum": ["reference", "embed"],
|
|
572
|
+
"description": "Relation strategy"
|
|
573
|
+
},
|
|
360
574
|
"on": {
|
|
361
575
|
"type": "object",
|
|
362
576
|
"description": "Relation field mappings",
|
|
363
|
-
"additionalProperties": false,
|
|
364
577
|
"properties": {
|
|
365
|
-
"
|
|
578
|
+
"localFields": {
|
|
366
579
|
"type": "array",
|
|
367
|
-
"description": "
|
|
368
|
-
"items": {
|
|
369
|
-
"type": "string"
|
|
370
|
-
}
|
|
580
|
+
"description": "Local model fields",
|
|
581
|
+
"items": { "type": "string" }
|
|
371
582
|
},
|
|
372
|
-
"
|
|
583
|
+
"targetFields": {
|
|
373
584
|
"type": "array",
|
|
374
|
-
"description": "
|
|
375
|
-
"items": {
|
|
376
|
-
"type": "string"
|
|
377
|
-
}
|
|
585
|
+
"description": "Target model fields",
|
|
586
|
+
"items": { "type": "string" }
|
|
378
587
|
}
|
|
379
|
-
}
|
|
380
|
-
"required": ["parentCols", "childCols"]
|
|
588
|
+
}
|
|
381
589
|
}
|
|
382
590
|
},
|
|
383
|
-
"required": ["to", "cardinality"
|
|
591
|
+
"required": ["to", "cardinality"]
|
|
384
592
|
}
|
|
385
593
|
}
|
|
386
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
|
+
};
|