@prisma-next/sql-contract-ts 0.3.0-dev.34 → 0.3.0-dev.37
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/README.md +28 -11
- package/dist/contract-builder.d.mts +108 -0
- package/dist/contract-builder.d.mts.map +1 -0
- package/dist/contract-builder.mjs +355 -0
- package/dist/contract-builder.mjs.map +1 -0
- package/package.json +22 -14
- package/schemas/data-contract-sql-v1.json +115 -4
- package/src/contract-builder.ts +179 -32
- package/src/contract.ts +54 -83
- package/dist/chunk-HTNUNGA2.js +0 -346
- package/dist/chunk-HTNUNGA2.js.map +0 -1
- package/dist/contract-builder.d.ts +0 -101
- 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 -232
- 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/exports/contract.ts +0 -1
package/src/contract-builder.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ExtensionPackRef, TargetPackRef } from '@prisma-next/contract/framework-components';
|
|
2
|
+
import type { ExecutionMutationDefault } from '@prisma-next/contract/types';
|
|
2
3
|
import type {
|
|
3
4
|
ColumnBuilderState,
|
|
5
|
+
ContractBuilderState,
|
|
6
|
+
ForeignKeyDefaultsState,
|
|
4
7
|
ModelBuilderState,
|
|
5
8
|
RelationDefinition,
|
|
6
9
|
TableBuilderState,
|
|
@@ -17,13 +20,16 @@ import {
|
|
|
17
20
|
type Mutable,
|
|
18
21
|
TableBuilder,
|
|
19
22
|
} from '@prisma-next/contract-authoring';
|
|
20
|
-
import
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
import {
|
|
24
|
+
applyFkDefaults,
|
|
25
|
+
type ModelDefinition,
|
|
26
|
+
type ModelField,
|
|
27
|
+
type SqlContract,
|
|
28
|
+
type SqlMappings,
|
|
29
|
+
type SqlStorage,
|
|
30
|
+
type StorageTypeInstance,
|
|
26
31
|
} from '@prisma-next/sql-contract/types';
|
|
32
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
27
33
|
import { computeMappings } from './contract';
|
|
28
34
|
|
|
29
35
|
/**
|
|
@@ -33,8 +39,8 @@ import { computeMappings } from './contract';
|
|
|
33
39
|
* produces. `codecTypes` uses the generic `CodecTypes` parameter; `operationTypes` is always
|
|
34
40
|
* empty since operations are added via extensions at runtime.
|
|
35
41
|
*
|
|
36
|
-
* **Difference from
|
|
37
|
-
* `
|
|
42
|
+
* **Difference from ExecutionContext**: This is a compile-time type for contract construction.
|
|
43
|
+
* `ExecutionContext` is a runtime object with populated registries for query execution.
|
|
38
44
|
*
|
|
39
45
|
* @template C - The `CodecTypes` generic parameter passed to `defineContract<CodecTypes>()`
|
|
40
46
|
*/
|
|
@@ -66,6 +72,8 @@ type BuildStorageTable<
|
|
|
66
72
|
readonly columns: readonly string[];
|
|
67
73
|
readonly references: { readonly table: string; readonly columns: readonly string[] };
|
|
68
74
|
readonly name?: string;
|
|
75
|
+
readonly constraint: boolean;
|
|
76
|
+
readonly index: boolean;
|
|
69
77
|
}>;
|
|
70
78
|
} & (PK extends readonly string[]
|
|
71
79
|
? { readonly primaryKey: { readonly columns: PK; readonly name?: string } }
|
|
@@ -80,6 +88,7 @@ type BuildStorage<
|
|
|
80
88
|
readonly string[] | undefined
|
|
81
89
|
>
|
|
82
90
|
>,
|
|
91
|
+
Types extends Record<string, StorageTypeInstance>,
|
|
83
92
|
> = {
|
|
84
93
|
readonly tables: {
|
|
85
94
|
readonly [K in keyof Tables]: BuildStorageTable<
|
|
@@ -88,6 +97,7 @@ type BuildStorage<
|
|
|
88
97
|
ExtractPrimaryKey<Tables[K]>
|
|
89
98
|
>;
|
|
90
99
|
};
|
|
100
|
+
readonly types: Types;
|
|
91
101
|
};
|
|
92
102
|
|
|
93
103
|
type BuildStorageTables<
|
|
@@ -128,10 +138,21 @@ class SqlContractBuilder<
|
|
|
128
138
|
string,
|
|
129
139
|
ModelBuilderState<string, string, Record<string, string>, Record<string, RelationDefinition>>
|
|
130
140
|
> = Record<never, never>,
|
|
131
|
-
|
|
141
|
+
Types extends Record<string, StorageTypeInstance> = Record<never, never>,
|
|
142
|
+
StorageHash extends string | undefined = undefined,
|
|
132
143
|
ExtensionPacks extends Record<string, unknown> | undefined = undefined,
|
|
133
144
|
Capabilities extends Record<string, Record<string, boolean>> | undefined = undefined,
|
|
134
|
-
> extends ContractBuilder<Target, Tables, Models,
|
|
145
|
+
> extends ContractBuilder<Target, Tables, Models, StorageHash, ExtensionPacks, Capabilities> {
|
|
146
|
+
protected declare readonly state: ContractBuilderState<
|
|
147
|
+
Target,
|
|
148
|
+
Tables,
|
|
149
|
+
Models,
|
|
150
|
+
StorageHash,
|
|
151
|
+
ExtensionPacks,
|
|
152
|
+
Capabilities
|
|
153
|
+
> & {
|
|
154
|
+
readonly storageTypes?: Types;
|
|
155
|
+
};
|
|
135
156
|
/**
|
|
136
157
|
* This method is responsible for normalizing the contract IR by setting default values
|
|
137
158
|
* for all required fields:
|
|
@@ -152,7 +173,7 @@ class SqlContractBuilder<
|
|
|
152
173
|
*/
|
|
153
174
|
build(): Target extends string
|
|
154
175
|
? SqlContract<
|
|
155
|
-
BuildStorage<Tables>,
|
|
176
|
+
BuildStorage<Tables, Types>,
|
|
156
177
|
BuildModels<Models>,
|
|
157
178
|
BuildRelations<Models>,
|
|
158
179
|
ContractBuilderMappings<CodecTypes>
|
|
@@ -160,7 +181,7 @@ class SqlContractBuilder<
|
|
|
160
181
|
readonly schemaVersion: '1';
|
|
161
182
|
readonly target: Target;
|
|
162
183
|
readonly targetFamily: 'sql';
|
|
163
|
-
readonly
|
|
184
|
+
readonly storageHash: StorageHash extends string ? StorageHash : string;
|
|
164
185
|
} & (ExtensionPacks extends Record<string, unknown>
|
|
165
186
|
? { readonly extensionPacks: ExtensionPacks }
|
|
166
187
|
: Record<string, never>) &
|
|
@@ -171,7 +192,7 @@ class SqlContractBuilder<
|
|
|
171
192
|
// Type helper to ensure literal types are preserved in return type
|
|
172
193
|
type BuiltContract = Target extends string
|
|
173
194
|
? SqlContract<
|
|
174
|
-
BuildStorage<Tables>,
|
|
195
|
+
BuildStorage<Tables, Types>,
|
|
175
196
|
BuildModels<Models>,
|
|
176
197
|
BuildRelations<Models>,
|
|
177
198
|
ContractBuilderMappings<CodecTypes>
|
|
@@ -179,7 +200,7 @@ class SqlContractBuilder<
|
|
|
179
200
|
readonly schemaVersion: '1';
|
|
180
201
|
readonly target: Target;
|
|
181
202
|
readonly targetFamily: 'sql';
|
|
182
|
-
readonly
|
|
203
|
+
readonly storageHash: StorageHash extends string ? StorageHash : string;
|
|
183
204
|
} & (ExtensionPacks extends Record<string, unknown>
|
|
184
205
|
? { readonly extensionPacks: ExtensionPacks }
|
|
185
206
|
: Record<string, never>) &
|
|
@@ -194,6 +215,7 @@ class SqlContractBuilder<
|
|
|
194
215
|
const target = this.state.target as Target & string;
|
|
195
216
|
|
|
196
217
|
const storageTables = {} as Partial<Mutable<BuildStorageTables<Tables>>>;
|
|
218
|
+
const executionDefaults: ExecutionMutationDefault[] = [];
|
|
197
219
|
|
|
198
220
|
for (const tableName of Object.keys(this.state.tables) as Array<keyof Tables & string>) {
|
|
199
221
|
const tableState = this.state.tables[tableName];
|
|
@@ -215,17 +237,27 @@ class SqlContractBuilder<
|
|
|
215
237
|
if (!columnState) continue;
|
|
216
238
|
const codecId = columnState.type;
|
|
217
239
|
const nativeType = columnState.nativeType;
|
|
240
|
+
const typeRef = columnState.typeRef;
|
|
218
241
|
|
|
219
242
|
columns[columnName as keyof ColumnDefs] = {
|
|
220
243
|
nativeType,
|
|
221
244
|
codecId,
|
|
222
245
|
nullable: (columnState.nullable ?? false) as ColumnDefs[keyof ColumnDefs]['nullable'] &
|
|
223
246
|
boolean,
|
|
224
|
-
...(
|
|
247
|
+
...ifDefined('typeParams', columnState.typeParams),
|
|
248
|
+
...ifDefined('default', columnState.default),
|
|
249
|
+
...ifDefined('typeRef', typeRef),
|
|
225
250
|
} as BuildStorageColumn<
|
|
226
251
|
ColumnDefs[keyof ColumnDefs]['nullable'] & boolean,
|
|
227
252
|
ColumnDefs[keyof ColumnDefs]['type']
|
|
228
253
|
>;
|
|
254
|
+
|
|
255
|
+
if ('executionDefault' in columnState && columnState.executionDefault) {
|
|
256
|
+
executionDefaults.push({
|
|
257
|
+
ref: { table: tableName, column: columnName },
|
|
258
|
+
onCreate: columnState.executionDefault,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
229
261
|
}
|
|
230
262
|
|
|
231
263
|
// Build uniques from table state
|
|
@@ -240,10 +272,11 @@ class SqlContractBuilder<
|
|
|
240
272
|
...(i.name ? { name: i.name } : {}),
|
|
241
273
|
}));
|
|
242
274
|
|
|
243
|
-
// Build foreign keys from table state
|
|
275
|
+
// Build foreign keys from table state, materializing defaults
|
|
244
276
|
const foreignKeys = (tableState.foreignKeys ?? []).map((fk) => ({
|
|
245
277
|
columns: fk.columns,
|
|
246
278
|
references: fk.references,
|
|
279
|
+
...applyFkDefaults(fk, this.state.foreignKeyDefaults),
|
|
247
280
|
...(fk.name ? { name: fk.name } : {}),
|
|
248
281
|
}));
|
|
249
282
|
|
|
@@ -270,7 +303,26 @@ class SqlContractBuilder<
|
|
|
270
303
|
(storageTables as Mutable<BuildStorageTables<Tables>>)[tableName] = table;
|
|
271
304
|
}
|
|
272
305
|
|
|
273
|
-
const
|
|
306
|
+
const storageTypes = (this.state.storageTypes ?? {}) as Types;
|
|
307
|
+
const storage: BuildStorage<Tables, Types> = {
|
|
308
|
+
tables: storageTables as BuildStorageTables<Tables>,
|
|
309
|
+
types: storageTypes,
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
const execution =
|
|
313
|
+
executionDefaults.length > 0
|
|
314
|
+
? {
|
|
315
|
+
mutations: {
|
|
316
|
+
defaults: executionDefaults.sort((a, b) => {
|
|
317
|
+
const tableCompare = a.ref.table.localeCompare(b.ref.table);
|
|
318
|
+
if (tableCompare !== 0) {
|
|
319
|
+
return tableCompare;
|
|
320
|
+
}
|
|
321
|
+
return a.ref.column.localeCompare(b.ref.column);
|
|
322
|
+
}),
|
|
323
|
+
},
|
|
324
|
+
}
|
|
325
|
+
: undefined;
|
|
274
326
|
|
|
275
327
|
// Build models - construct as partial first, then assert full type
|
|
276
328
|
const modelsPartial: Partial<BuildModels<Models>> = {};
|
|
@@ -374,11 +426,12 @@ class SqlContractBuilder<
|
|
|
374
426
|
schemaVersion: '1' as const,
|
|
375
427
|
target,
|
|
376
428
|
targetFamily: 'sql' as const,
|
|
377
|
-
|
|
429
|
+
storageHash: this.state.storageHash || 'sha256:ts-builder-placeholder',
|
|
378
430
|
models,
|
|
379
431
|
relations: relationsPartial,
|
|
380
432
|
storage,
|
|
381
433
|
mappings,
|
|
434
|
+
...(execution ? { execution } : {}),
|
|
382
435
|
extensionPacks,
|
|
383
436
|
capabilities: this.state.capabilities || {},
|
|
384
437
|
meta: {},
|
|
@@ -391,7 +444,8 @@ class SqlContractBuilder<
|
|
|
391
444
|
Target,
|
|
392
445
|
Tables,
|
|
393
446
|
Models,
|
|
394
|
-
|
|
447
|
+
Types,
|
|
448
|
+
StorageHash,
|
|
395
449
|
ExtensionPacks,
|
|
396
450
|
Capabilities
|
|
397
451
|
>['build']
|
|
@@ -400,13 +454,23 @@ class SqlContractBuilder<
|
|
|
400
454
|
|
|
401
455
|
override target<T extends string>(
|
|
402
456
|
packRef: TargetPackRef<'sql', T>,
|
|
403
|
-
): SqlContractBuilder<
|
|
457
|
+
): SqlContractBuilder<
|
|
458
|
+
CodecTypes,
|
|
459
|
+
T,
|
|
460
|
+
Tables,
|
|
461
|
+
Models,
|
|
462
|
+
Types,
|
|
463
|
+
StorageHash,
|
|
464
|
+
ExtensionPacks,
|
|
465
|
+
Capabilities
|
|
466
|
+
> {
|
|
404
467
|
return new SqlContractBuilder<
|
|
405
468
|
CodecTypes,
|
|
406
469
|
T,
|
|
407
470
|
Tables,
|
|
408
471
|
Models,
|
|
409
|
-
|
|
472
|
+
Types,
|
|
473
|
+
StorageHash,
|
|
410
474
|
ExtensionPacks,
|
|
411
475
|
Capabilities
|
|
412
476
|
>({
|
|
@@ -422,7 +486,8 @@ class SqlContractBuilder<
|
|
|
422
486
|
Target,
|
|
423
487
|
Tables,
|
|
424
488
|
Models,
|
|
425
|
-
|
|
489
|
+
Types,
|
|
490
|
+
StorageHash,
|
|
426
491
|
ExtensionPacks,
|
|
427
492
|
Capabilities
|
|
428
493
|
> {
|
|
@@ -461,7 +526,8 @@ class SqlContractBuilder<
|
|
|
461
526
|
Target,
|
|
462
527
|
Tables,
|
|
463
528
|
Models,
|
|
464
|
-
|
|
529
|
+
Types,
|
|
530
|
+
StorageHash,
|
|
465
531
|
ExtensionPacks,
|
|
466
532
|
Capabilities
|
|
467
533
|
>({
|
|
@@ -472,27 +538,46 @@ class SqlContractBuilder<
|
|
|
472
538
|
|
|
473
539
|
override capabilities<C extends Record<string, Record<string, boolean>>>(
|
|
474
540
|
capabilities: C,
|
|
475
|
-
): SqlContractBuilder<CodecTypes, Target, Tables, Models,
|
|
476
|
-
return new SqlContractBuilder<
|
|
541
|
+
): SqlContractBuilder<CodecTypes, Target, Tables, Models, Types, StorageHash, ExtensionPacks, C> {
|
|
542
|
+
return new SqlContractBuilder<
|
|
543
|
+
CodecTypes,
|
|
544
|
+
Target,
|
|
545
|
+
Tables,
|
|
546
|
+
Models,
|
|
547
|
+
Types,
|
|
548
|
+
StorageHash,
|
|
549
|
+
ExtensionPacks,
|
|
550
|
+
C
|
|
551
|
+
>({
|
|
477
552
|
...this.state,
|
|
478
553
|
capabilities,
|
|
479
554
|
});
|
|
480
555
|
}
|
|
481
556
|
|
|
482
|
-
override
|
|
557
|
+
override storageHash<H extends string>(
|
|
483
558
|
hash: H,
|
|
484
|
-
): SqlContractBuilder<
|
|
559
|
+
): SqlContractBuilder<
|
|
560
|
+
CodecTypes,
|
|
561
|
+
Target,
|
|
562
|
+
Tables,
|
|
563
|
+
Models,
|
|
564
|
+
Types,
|
|
565
|
+
H,
|
|
566
|
+
ExtensionPacks,
|
|
567
|
+
Capabilities
|
|
568
|
+
> {
|
|
485
569
|
return new SqlContractBuilder<
|
|
486
570
|
CodecTypes,
|
|
487
571
|
Target,
|
|
488
572
|
Tables,
|
|
489
573
|
Models,
|
|
574
|
+
Types,
|
|
490
575
|
H,
|
|
491
576
|
ExtensionPacks,
|
|
492
577
|
Capabilities
|
|
493
578
|
>({
|
|
494
579
|
...this.state,
|
|
495
|
-
|
|
580
|
+
storageHash: hash,
|
|
496
581
|
});
|
|
497
582
|
}
|
|
498
583
|
|
|
@@ -511,7 +596,8 @@ class SqlContractBuilder<
|
|
|
511
596
|
Target,
|
|
512
597
|
Tables & Record<TableName, ReturnType<T['build']>>,
|
|
513
598
|
Models,
|
|
514
|
-
|
|
599
|
+
Types,
|
|
600
|
+
StorageHash,
|
|
515
601
|
ExtensionPacks,
|
|
516
602
|
Capabilities
|
|
517
603
|
> {
|
|
@@ -525,7 +611,8 @@ class SqlContractBuilder<
|
|
|
525
611
|
Target,
|
|
526
612
|
Tables & Record<TableName, ReturnType<T['build']>>,
|
|
527
613
|
Models,
|
|
528
|
-
|
|
614
|
+
Types,
|
|
615
|
+
StorageHash,
|
|
529
616
|
ExtensionPacks,
|
|
530
617
|
Capabilities
|
|
531
618
|
>({
|
|
@@ -555,7 +642,8 @@ class SqlContractBuilder<
|
|
|
555
642
|
Target,
|
|
556
643
|
Tables,
|
|
557
644
|
Models & Record<ModelName, ReturnType<M['build']>>,
|
|
558
|
-
|
|
645
|
+
Types,
|
|
646
|
+
StorageHash,
|
|
559
647
|
ExtensionPacks,
|
|
560
648
|
Capabilities
|
|
561
649
|
> {
|
|
@@ -569,7 +657,8 @@ class SqlContractBuilder<
|
|
|
569
657
|
Target,
|
|
570
658
|
Tables,
|
|
571
659
|
Models & Record<ModelName, ReturnType<M['build']>>,
|
|
572
|
-
|
|
660
|
+
Types,
|
|
661
|
+
StorageHash,
|
|
573
662
|
ExtensionPacks,
|
|
574
663
|
Capabilities
|
|
575
664
|
>({
|
|
@@ -578,6 +667,64 @@ class SqlContractBuilder<
|
|
|
578
667
|
Record<ModelName, ReturnType<M['build']>>,
|
|
579
668
|
});
|
|
580
669
|
}
|
|
670
|
+
|
|
671
|
+
override foreignKeyDefaults(
|
|
672
|
+
config: ForeignKeyDefaultsState,
|
|
673
|
+
): SqlContractBuilder<
|
|
674
|
+
CodecTypes,
|
|
675
|
+
Target,
|
|
676
|
+
Tables,
|
|
677
|
+
Models,
|
|
678
|
+
Types,
|
|
679
|
+
StorageHash,
|
|
680
|
+
ExtensionPacks,
|
|
681
|
+
Capabilities
|
|
682
|
+
> {
|
|
683
|
+
return new SqlContractBuilder<
|
|
684
|
+
CodecTypes,
|
|
685
|
+
Target,
|
|
686
|
+
Tables,
|
|
687
|
+
Models,
|
|
688
|
+
Types,
|
|
689
|
+
StorageHash,
|
|
690
|
+
ExtensionPacks,
|
|
691
|
+
Capabilities
|
|
692
|
+
>({
|
|
693
|
+
...this.state,
|
|
694
|
+
foreignKeyDefaults: config,
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
storageType<Name extends string, Type extends StorageTypeInstance>(
|
|
699
|
+
name: Name,
|
|
700
|
+
typeInstance: Type,
|
|
701
|
+
): SqlContractBuilder<
|
|
702
|
+
CodecTypes,
|
|
703
|
+
Target,
|
|
704
|
+
Tables,
|
|
705
|
+
Models,
|
|
706
|
+
Types & Record<Name, Type>,
|
|
707
|
+
StorageHash,
|
|
708
|
+
ExtensionPacks,
|
|
709
|
+
Capabilities
|
|
710
|
+
> {
|
|
711
|
+
return new SqlContractBuilder<
|
|
712
|
+
CodecTypes,
|
|
713
|
+
Target,
|
|
714
|
+
Tables,
|
|
715
|
+
Models,
|
|
716
|
+
Types & Record<Name, Type>,
|
|
717
|
+
StorageHash,
|
|
718
|
+
ExtensionPacks,
|
|
719
|
+
Capabilities
|
|
720
|
+
>({
|
|
721
|
+
...this.state,
|
|
722
|
+
storageTypes: {
|
|
723
|
+
...(this.state.storageTypes ?? {}),
|
|
724
|
+
[name]: typeInstance,
|
|
725
|
+
},
|
|
726
|
+
});
|
|
727
|
+
}
|
|
581
728
|
}
|
|
582
729
|
|
|
583
730
|
export function defineContract<
|
package/src/contract.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
StorageTypeInstance,
|
|
15
15
|
UniqueConstraint,
|
|
16
16
|
} from '@prisma-next/sql-contract/types';
|
|
17
|
+
import { ColumnDefaultSchema } from '@prisma-next/sql-contract/validators';
|
|
17
18
|
import { type } from 'arktype';
|
|
18
19
|
import type { O } from 'ts-toolbelt';
|
|
19
20
|
|
|
@@ -21,12 +22,14 @@ import type { O } from 'ts-toolbelt';
|
|
|
21
22
|
* Structural validation schema for SqlContract using Arktype.
|
|
22
23
|
* This validates the shape and types of the contract structure.
|
|
23
24
|
*/
|
|
25
|
+
|
|
24
26
|
const StorageColumnSchema = type.declare<StorageColumn>().type({
|
|
25
27
|
nativeType: 'string',
|
|
26
28
|
codecId: 'string',
|
|
27
29
|
nullable: 'boolean',
|
|
28
30
|
'typeParams?': 'Record<string, unknown>',
|
|
29
31
|
'typeRef?': 'string',
|
|
32
|
+
'default?': ColumnDefaultSchema,
|
|
30
33
|
});
|
|
31
34
|
|
|
32
35
|
const StorageTypeInstanceSchema = type.declare<StorageTypeInstance>().type({
|
|
@@ -59,6 +62,8 @@ const ForeignKeySchema = type.declare<ForeignKey>().type({
|
|
|
59
62
|
columns: type.string.array().readonly(),
|
|
60
63
|
references: ForeignKeyReferencesSchema,
|
|
61
64
|
'name?': 'string',
|
|
65
|
+
constraint: 'boolean',
|
|
66
|
+
index: 'boolean',
|
|
62
67
|
});
|
|
63
68
|
|
|
64
69
|
const StorageTableSchema = type.declare<StorageTable>().type({
|
|
@@ -88,6 +93,27 @@ const ModelSchema = type.declare<ModelDefinition>().type({
|
|
|
88
93
|
relations: type({ '[string]': 'unknown' }),
|
|
89
94
|
});
|
|
90
95
|
|
|
96
|
+
const ExecutionMutationDefaultValueSchema = type({
|
|
97
|
+
kind: "'generator'",
|
|
98
|
+
id: "'ulid' | 'nanoid' | 'uuidv7' | 'uuidv4' | 'cuid2' | 'ksuid'",
|
|
99
|
+
'params?': 'Record<string, unknown>',
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const ExecutionMutationDefaultSchema = type({
|
|
103
|
+
ref: {
|
|
104
|
+
table: 'string',
|
|
105
|
+
column: 'string',
|
|
106
|
+
},
|
|
107
|
+
'onCreate?': ExecutionMutationDefaultValueSchema,
|
|
108
|
+
'onUpdate?': ExecutionMutationDefaultValueSchema,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const ExecutionSchema = type({
|
|
112
|
+
mutations: {
|
|
113
|
+
defaults: ExecutionMutationDefaultSchema.array().readonly(),
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
91
117
|
/**
|
|
92
118
|
* Complete SqlContract schema for structural validation.
|
|
93
119
|
* This validates the entire contract structure at once.
|
|
@@ -96,7 +122,8 @@ const SqlContractSchema = type({
|
|
|
96
122
|
'schemaVersion?': "'1'",
|
|
97
123
|
target: 'string',
|
|
98
124
|
targetFamily: "'sql'",
|
|
99
|
-
|
|
125
|
+
storageHash: 'string',
|
|
126
|
+
'executionHash?': 'string',
|
|
100
127
|
'profileHash?': 'string',
|
|
101
128
|
'capabilities?': 'Record<string, Record<string, boolean>>',
|
|
102
129
|
'extensionPacks?': 'Record<string, unknown>',
|
|
@@ -104,6 +131,7 @@ const SqlContractSchema = type({
|
|
|
104
131
|
'sources?': 'Record<string, unknown>',
|
|
105
132
|
models: type({ '[string]': ModelSchema }),
|
|
106
133
|
storage: StorageSchema,
|
|
134
|
+
'execution?': ExecutionSchema,
|
|
107
135
|
});
|
|
108
136
|
|
|
109
137
|
/**
|
|
@@ -198,13 +226,15 @@ export function computeMappings(
|
|
|
198
226
|
* This checks that references (e.g., foreign keys, primary keys, uniques) point to storage objects that already exist.
|
|
199
227
|
* Structural validation is expected to have already completed before this helper runs.
|
|
200
228
|
*
|
|
229
|
+
* Rule: keep this focused on structural consistency only; capability/feature
|
|
230
|
+
* gating (e.g., defaults.*) belongs in migration/runtime verification, not here.
|
|
231
|
+
*
|
|
201
232
|
* @param structurallyValidatedContract - The contract whose structure has already been validated
|
|
202
233
|
* @throws Error if logical validation fails
|
|
203
234
|
*/
|
|
204
235
|
function validateContractLogic(structurallyValidatedContract: SqlContract<SqlStorage>): void {
|
|
205
236
|
const { storage, models } = structurallyValidatedContract;
|
|
206
237
|
const tableNames = new Set(Object.keys(storage.tables));
|
|
207
|
-
const typeInstanceNames = new Set(Object.keys(storage.types ?? {}));
|
|
208
238
|
|
|
209
239
|
// Validate storage.types if present
|
|
210
240
|
if (storage.types) {
|
|
@@ -235,11 +265,26 @@ function validateContractLogic(structurallyValidatedContract: SqlContract<SqlSto
|
|
|
235
265
|
);
|
|
236
266
|
}
|
|
237
267
|
|
|
238
|
-
// Validate typeRef points to an existing storage.types key
|
|
239
|
-
if (column.typeRef !== undefined
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
268
|
+
// Validate typeRef points to an existing storage.types key and matches codecId/nativeType
|
|
269
|
+
if (column.typeRef !== undefined) {
|
|
270
|
+
const referencedType = storage.types?.[column.typeRef];
|
|
271
|
+
if (!referencedType) {
|
|
272
|
+
throw new Error(
|
|
273
|
+
`Column "${columnName}" in table "${tableName}" references non-existent type instance "${column.typeRef}" (not found in storage.types)`,
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (column.codecId !== referencedType.codecId) {
|
|
278
|
+
throw new Error(
|
|
279
|
+
`Column "${columnName}" in table "${tableName}" has codecId "${column.codecId}" but references type instance "${column.typeRef}" with codecId "${referencedType.codecId}"`,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (column.nativeType !== referencedType.nativeType) {
|
|
284
|
+
throw new Error(
|
|
285
|
+
`Column "${columnName}" in table "${tableName}" has nativeType "${column.nativeType}" but references type instance "${column.typeRef}" with nativeType "${referencedType.nativeType}"`,
|
|
286
|
+
);
|
|
287
|
+
}
|
|
243
288
|
}
|
|
244
289
|
}
|
|
245
290
|
}
|
|
@@ -431,82 +476,8 @@ function validateContractLogic(structurallyValidatedContract: SqlContract<SqlSto
|
|
|
431
476
|
}
|
|
432
477
|
}
|
|
433
478
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
// Only normalize if storage exists (validation will catch if it's missing)
|
|
438
|
-
let normalizedStorage = contractObj['storage'];
|
|
439
|
-
if (normalizedStorage && typeof normalizedStorage === 'object' && normalizedStorage !== null) {
|
|
440
|
-
const storage = normalizedStorage as Record<string, unknown>;
|
|
441
|
-
const tables = storage['tables'] as Record<string, unknown> | undefined;
|
|
442
|
-
|
|
443
|
-
if (tables) {
|
|
444
|
-
// Normalize storage tables
|
|
445
|
-
const normalizedTables: Record<string, unknown> = {};
|
|
446
|
-
for (const [tableName, table] of Object.entries(tables)) {
|
|
447
|
-
const tableObj = table as Record<string, unknown>;
|
|
448
|
-
const columns = tableObj['columns'] as Record<string, unknown> | undefined;
|
|
449
|
-
|
|
450
|
-
if (columns) {
|
|
451
|
-
// Normalize columns: add nullable: false if missing
|
|
452
|
-
const normalizedColumns: Record<string, unknown> = {};
|
|
453
|
-
for (const [columnName, column] of Object.entries(columns)) {
|
|
454
|
-
const columnObj = column as Record<string, unknown>;
|
|
455
|
-
const normalizedColumn: Record<string, unknown> = {
|
|
456
|
-
...columnObj,
|
|
457
|
-
nullable: columnObj['nullable'] ?? false,
|
|
458
|
-
};
|
|
459
|
-
|
|
460
|
-
normalizedColumns[columnName] = normalizedColumn;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// Normalize table arrays: add empty arrays if missing
|
|
464
|
-
normalizedTables[tableName] = {
|
|
465
|
-
...tableObj,
|
|
466
|
-
columns: normalizedColumns,
|
|
467
|
-
uniques: tableObj['uniques'] ?? [],
|
|
468
|
-
indexes: tableObj['indexes'] ?? [],
|
|
469
|
-
foreignKeys: tableObj['foreignKeys'] ?? [],
|
|
470
|
-
};
|
|
471
|
-
} else {
|
|
472
|
-
normalizedTables[tableName] = tableObj;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
normalizedStorage = {
|
|
477
|
-
...storage,
|
|
478
|
-
tables: normalizedTables,
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// Only normalize if models exists (validation will catch if it's missing)
|
|
484
|
-
let normalizedModels = contractObj['models'];
|
|
485
|
-
if (normalizedModels && typeof normalizedModels === 'object' && normalizedModels !== null) {
|
|
486
|
-
const models = normalizedModels as Record<string, unknown>;
|
|
487
|
-
const normalizedModelsObj: Record<string, unknown> = {};
|
|
488
|
-
for (const [modelName, model] of Object.entries(models)) {
|
|
489
|
-
const modelObj = model as Record<string, unknown>;
|
|
490
|
-
normalizedModelsObj[modelName] = {
|
|
491
|
-
...modelObj,
|
|
492
|
-
relations: modelObj['relations'] ?? {},
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
normalizedModels = normalizedModelsObj;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// Normalize top-level fields: add empty objects if missing
|
|
499
|
-
return {
|
|
500
|
-
...contractObj,
|
|
501
|
-
models: normalizedModels,
|
|
502
|
-
relations: contractObj['relations'] ?? {},
|
|
503
|
-
storage: normalizedStorage,
|
|
504
|
-
extensionPacks: contractObj['extensionPacks'] ?? {},
|
|
505
|
-
capabilities: contractObj['capabilities'] ?? {},
|
|
506
|
-
meta: contractObj['meta'] ?? {},
|
|
507
|
-
sources: contractObj['sources'] ?? {},
|
|
508
|
-
} as SqlContract<SqlStorage>;
|
|
509
|
-
}
|
|
479
|
+
import { normalizeContract } from '@prisma-next/sql-contract/validate';
|
|
480
|
+
export { normalizeContract };
|
|
510
481
|
|
|
511
482
|
/**
|
|
512
483
|
* Validates that a JSON import conforms to the SqlContract structure
|