@prisma-next/sql-contract-ts 0.3.0-dev.9 → 0.3.0-dev.90

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.
@@ -1,6 +1,17 @@
1
1
  import type { ExtensionPackRef, TargetPackRef } from '@prisma-next/contract/framework-components';
2
+ import type {
3
+ ColumnDefault,
4
+ ColumnDefaultLiteralInputValue,
5
+ ColumnDefaultLiteralValue,
6
+ ExecutionMutationDefault,
7
+ ExecutionMutationDefaultValue,
8
+ TaggedRaw,
9
+ } from '@prisma-next/contract/types';
2
10
  import type {
3
11
  ColumnBuilderState,
12
+ ColumnTypeDescriptor,
13
+ ContractBuilderState,
14
+ ForeignKeyDefaultsState,
4
15
  ModelBuilderState,
5
16
  RelationDefinition,
6
17
  TableBuilderState,
@@ -10,41 +21,137 @@ import {
10
21
  type BuildRelations,
11
22
  type BuildStorageColumn,
12
23
  ContractBuilder,
24
+ createTable,
13
25
  type ExtractColumns,
14
26
  type ExtractPrimaryKey,
15
27
  ModelBuilder,
16
28
  type Mutable,
17
29
  TableBuilder,
18
30
  } from '@prisma-next/contract-authoring';
19
- import type {
20
- ModelDefinition,
21
- ModelField,
22
- SqlContract,
23
- SqlMappings,
24
- SqlStorage,
31
+ import {
32
+ applyFkDefaults,
33
+ type ContractWithTypeMaps,
34
+ type Index,
35
+ type ModelDefinition,
36
+ type ModelField,
37
+ type ReferentialAction,
38
+ type SqlContract,
39
+ type SqlMappings,
40
+ type SqlStorage,
41
+ type StorageTypeInstance,
42
+ type TypeMaps,
25
43
  } from '@prisma-next/sql-contract/types';
44
+ import { ifDefined } from '@prisma-next/utils/defined';
26
45
  import { computeMappings } from './contract';
27
46
 
28
- /**
29
- * Type-level mappings structure for contracts built via `defineContract()`.
30
- *
31
- * Compile-time type helper (not a runtime object) that ensures mappings match what the builder
32
- * produces. `codecTypes` uses the generic `CodecTypes` parameter; `operationTypes` is always
33
- * empty since operations are added via extensions at runtime.
34
- *
35
- * **Difference from RuntimeContext**: This is a compile-time type for contract construction.
36
- * `RuntimeContext` is a runtime object with populated registries for query execution.
37
- *
38
- * @template C - The `CodecTypes` generic parameter passed to `defineContract<CodecTypes>()`
39
- */
40
- type ContractBuilderMappings<C extends Record<string, { output: unknown }>> = Omit<
41
- SqlMappings,
42
- 'codecTypes' | 'operationTypes'
43
- > & {
44
- readonly codecTypes: C;
45
- readonly operationTypes: Record<string, never>;
47
+ type ColumnDefaultForCodec<
48
+ CodecTypes extends Record<string, { output: unknown }>,
49
+ CodecId extends string,
50
+ > =
51
+ | {
52
+ readonly kind: 'literal';
53
+ readonly value: CodecId extends keyof CodecTypes ? CodecTypes[CodecId]['output'] : unknown;
54
+ }
55
+ | { readonly kind: 'function'; readonly expression: string };
56
+
57
+ type SqlNullableColumnOptions<
58
+ Descriptor extends ColumnTypeDescriptor,
59
+ CodecTypes extends Record<string, { output: unknown }>,
60
+ > = {
61
+ readonly type: Descriptor;
62
+ readonly nullable: true;
63
+ readonly typeParams?: Record<string, unknown>;
64
+ readonly default?: ColumnDefaultForCodec<CodecTypes, Descriptor['codecId']>;
65
+ };
66
+
67
+ type SqlNonNullableColumnOptions<
68
+ Descriptor extends ColumnTypeDescriptor,
69
+ CodecTypes extends Record<string, { output: unknown }>,
70
+ > = {
71
+ readonly type: Descriptor;
72
+ readonly nullable?: false;
73
+ readonly typeParams?: Record<string, unknown>;
74
+ readonly default?: ColumnDefaultForCodec<CodecTypes, Descriptor['codecId']>;
75
+ };
76
+
77
+ type SqlGeneratedColumnOptions<
78
+ Descriptor extends ColumnTypeDescriptor,
79
+ CodecTypes extends Record<string, { output: unknown }>,
80
+ > = Omit<SqlNonNullableColumnOptions<Descriptor, CodecTypes>, 'default' | 'nullable'> & {
81
+ readonly nullable?: false;
82
+ readonly generated: ExecutionMutationDefaultValue;
46
83
  };
47
84
 
85
+ type SqlColumnOptions<
86
+ Descriptor extends ColumnTypeDescriptor,
87
+ CodecTypes extends Record<string, { output: unknown }>,
88
+ > =
89
+ | SqlNullableColumnOptions<Descriptor, CodecTypes>
90
+ | SqlNonNullableColumnOptions<Descriptor, CodecTypes>;
91
+
92
+ export interface SqlTableBuilder<
93
+ Name extends string,
94
+ CodecTypes extends Record<string, { output: unknown }>,
95
+ Columns extends Record<string, ColumnBuilderState<string, boolean, string>> = Record<
96
+ never,
97
+ ColumnBuilderState<string, boolean, string>
98
+ >,
99
+ PrimaryKey extends readonly string[] | undefined = undefined,
100
+ > extends Omit<TableBuilder<Name, Columns, PrimaryKey>, 'column' | 'generated'> {
101
+ column<ColName extends string, Descriptor extends ColumnTypeDescriptor>(
102
+ name: ColName,
103
+ options: SqlNullableColumnOptions<Descriptor, CodecTypes>,
104
+ ): TableBuilder<
105
+ Name,
106
+ Columns & Record<ColName, ColumnBuilderState<ColName, true, Descriptor['codecId']>>,
107
+ PrimaryKey
108
+ >;
109
+ column<ColName extends string, Descriptor extends ColumnTypeDescriptor>(
110
+ name: ColName,
111
+ options: SqlNonNullableColumnOptions<Descriptor, CodecTypes>,
112
+ ): TableBuilder<
113
+ Name,
114
+ Columns & Record<ColName, ColumnBuilderState<ColName, false, Descriptor['codecId']>>,
115
+ PrimaryKey
116
+ >;
117
+ column<ColName extends string, Descriptor extends ColumnTypeDescriptor>(
118
+ name: ColName,
119
+ options: SqlColumnOptions<Descriptor, CodecTypes>,
120
+ ): TableBuilder<
121
+ Name,
122
+ Columns & Record<ColName, ColumnBuilderState<ColName, boolean, Descriptor['codecId']>>,
123
+ PrimaryKey
124
+ >;
125
+ generated<ColName extends string, Descriptor extends ColumnTypeDescriptor>(
126
+ name: ColName,
127
+ options: SqlGeneratedColumnOptions<Descriptor, CodecTypes>,
128
+ ): TableBuilder<
129
+ Name,
130
+ Columns & Record<ColName, ColumnBuilderState<ColName, false, Descriptor['codecId']>>,
131
+ PrimaryKey
132
+ >;
133
+ }
134
+
135
+ type ContractBuilderMappings = SqlMappings;
136
+
137
+ type ExtractCodecTypesFromPack<P> = P extends { __codecTypes?: infer C }
138
+ ? C extends Record<string, { output: unknown }>
139
+ ? C
140
+ : Record<string, never>
141
+ : Record<string, never>;
142
+
143
+ type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (
144
+ k: infer I,
145
+ ) => void
146
+ ? I
147
+ : never;
148
+
149
+ type MergeExtensionCodecTypes<Packs extends Record<string, unknown>> = UnionToIntersection<
150
+ {
151
+ [K in keyof Packs]: ExtractCodecTypesFromPack<Packs[K]>;
152
+ }[keyof Packs]
153
+ >;
154
+
48
155
  type BuildStorageTable<
49
156
  _TableName extends string,
50
157
  Columns extends Record<string, ColumnBuilderState<string, boolean, string>>,
@@ -59,11 +166,19 @@ type BuildStorageTable<
59
166
  ? BuildStorageColumn<Null & boolean, TType>
60
167
  : never;
61
168
  };
62
- readonly uniques: ReadonlyArray<never>;
63
- readonly indexes: ReadonlyArray<never>;
64
- readonly foreignKeys: ReadonlyArray<never>;
169
+ readonly uniques: ReadonlyArray<{ readonly columns: readonly string[]; readonly name?: string }>;
170
+ readonly indexes: ReadonlyArray<Index>;
171
+ readonly foreignKeys: ReadonlyArray<{
172
+ readonly columns: readonly string[];
173
+ readonly references: { readonly table: string; readonly columns: readonly string[] };
174
+ readonly name?: string;
175
+ readonly onDelete?: ReferentialAction;
176
+ readonly onUpdate?: ReferentialAction;
177
+ readonly constraint: boolean;
178
+ readonly index: boolean;
179
+ }>;
65
180
  } & (PK extends readonly string[]
66
- ? { readonly primaryKey: { readonly columns: PK } }
181
+ ? { readonly primaryKey: { readonly columns: PK; readonly name?: string } }
67
182
  : Record<string, never>);
68
183
 
69
184
  type BuildStorage<
@@ -75,6 +190,7 @@ type BuildStorage<
75
190
  readonly string[] | undefined
76
191
  >
77
192
  >,
193
+ Types extends Record<string, StorageTypeInstance>,
78
194
  > = {
79
195
  readonly tables: {
80
196
  readonly [K in keyof Tables]: BuildStorageTable<
@@ -83,6 +199,7 @@ type BuildStorage<
83
199
  ExtractPrimaryKey<Tables[K]>
84
200
  >;
85
201
  };
202
+ readonly types: Types;
86
203
  };
87
204
 
88
205
  type BuildStorageTables<
@@ -108,6 +225,52 @@ export interface ColumnBuilder<Name extends string, Nullable extends boolean, Ty
108
225
  build(): ColumnBuilderState<Name, Nullable, Type>;
109
226
  }
110
227
 
228
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
229
+ if (typeof value !== 'object' || value === null) return false;
230
+ const proto = Object.getPrototypeOf(value);
231
+ return proto === Object.prototype || proto === null;
232
+ }
233
+
234
+ function isJsonValue(value: unknown): value is ColumnDefaultLiteralValue {
235
+ if (value === null) return true;
236
+ const valueType = typeof value;
237
+ if (valueType === 'string' || valueType === 'number' || valueType === 'boolean') return true;
238
+ if (Array.isArray(value)) {
239
+ return value.every((item) => isJsonValue(item));
240
+ }
241
+ if (isPlainObject(value)) {
242
+ return Object.values(value).every((item) => isJsonValue(item));
243
+ }
244
+ return false;
245
+ }
246
+
247
+ function encodeDefaultLiteralValue(
248
+ value: ColumnDefaultLiteralInputValue,
249
+ ): ColumnDefaultLiteralValue {
250
+ if (typeof value === 'bigint') {
251
+ return { $type: 'bigint', value: value.toString() };
252
+ }
253
+ if (value instanceof Date) {
254
+ return value.toISOString();
255
+ }
256
+ if (isJsonValue(value)) {
257
+ if (isPlainObject(value) && '$type' in value) {
258
+ return { $type: 'raw', value } satisfies TaggedRaw;
259
+ }
260
+ return value;
261
+ }
262
+ throw new Error(
263
+ 'Unsupported column default literal value: expected JSON-safe value, bigint, or Date.',
264
+ );
265
+ }
266
+
267
+ function encodeColumnDefault(defaultInput: ColumnDefault): ColumnDefault {
268
+ if (defaultInput.kind === 'function') {
269
+ return { kind: 'function', expression: defaultInput.expression };
270
+ }
271
+ return { kind: 'literal', value: encodeDefaultLiteralValue(defaultInput.value) };
272
+ }
273
+
111
274
  class SqlContractBuilder<
112
275
  CodecTypes extends Record<string, { output: unknown }> = Record<string, never>,
113
276
  Target extends string | undefined = undefined,
@@ -123,10 +286,21 @@ class SqlContractBuilder<
123
286
  string,
124
287
  ModelBuilderState<string, string, Record<string, string>, Record<string, RelationDefinition>>
125
288
  > = Record<never, never>,
126
- CoreHash extends string | undefined = undefined,
289
+ Types extends Record<string, StorageTypeInstance> = Record<never, never>,
290
+ StorageHash extends string | undefined = undefined,
127
291
  ExtensionPacks extends Record<string, unknown> | undefined = undefined,
128
292
  Capabilities extends Record<string, Record<string, boolean>> | undefined = undefined,
129
- > extends ContractBuilder<Target, Tables, Models, CoreHash, ExtensionPacks, Capabilities> {
293
+ > extends ContractBuilder<Target, Tables, Models, StorageHash, ExtensionPacks, Capabilities> {
294
+ protected declare readonly state: ContractBuilderState<
295
+ Target,
296
+ Tables,
297
+ Models,
298
+ StorageHash,
299
+ ExtensionPacks,
300
+ Capabilities
301
+ > & {
302
+ readonly storageTypes?: Types;
303
+ };
130
304
  /**
131
305
  * This method is responsible for normalizing the contract IR by setting default values
132
306
  * for all required fields:
@@ -146,41 +320,46 @@ class SqlContractBuilder<
146
320
  * @returns A normalized SqlContract with all required fields present
147
321
  */
148
322
  build(): Target extends string
149
- ? SqlContract<
150
- BuildStorage<Tables>,
151
- BuildModels<Models>,
152
- BuildRelations<Models>,
153
- ContractBuilderMappings<CodecTypes>
154
- > & {
155
- readonly schemaVersion: '1';
156
- readonly target: Target;
157
- readonly targetFamily: 'sql';
158
- readonly coreHash: CoreHash extends string ? CoreHash : string;
159
- } & (ExtensionPacks extends Record<string, unknown>
160
- ? { readonly extensionPacks: ExtensionPacks }
161
- : Record<string, never>) &
162
- (Capabilities extends Record<string, Record<string, boolean>>
163
- ? { readonly capabilities: Capabilities }
164
- : Record<string, never>)
165
- : never {
166
- // Type helper to ensure literal types are preserved in return type
167
- type BuiltContract = Target extends string
168
- ? SqlContract<
169
- BuildStorage<Tables>,
323
+ ? ContractWithTypeMaps<
324
+ SqlContract<
325
+ BuildStorage<Tables, Types>,
170
326
  BuildModels<Models>,
171
327
  BuildRelations<Models>,
172
- ContractBuilderMappings<CodecTypes>
328
+ ContractBuilderMappings
173
329
  > & {
174
330
  readonly schemaVersion: '1';
175
331
  readonly target: Target;
176
332
  readonly targetFamily: 'sql';
177
- readonly coreHash: CoreHash extends string ? CoreHash : string;
333
+ readonly storageHash: StorageHash extends string ? StorageHash : string;
178
334
  } & (ExtensionPacks extends Record<string, unknown>
179
335
  ? { readonly extensionPacks: ExtensionPacks }
180
336
  : Record<string, never>) &
181
337
  (Capabilities extends Record<string, Record<string, boolean>>
182
338
  ? { readonly capabilities: Capabilities }
183
- : Record<string, never>)
339
+ : Record<string, never>),
340
+ TypeMaps<CodecTypes, Record<string, never>>
341
+ >
342
+ : never {
343
+ type BuiltContract = Target extends string
344
+ ? ContractWithTypeMaps<
345
+ SqlContract<
346
+ BuildStorage<Tables, Types>,
347
+ BuildModels<Models>,
348
+ BuildRelations<Models>,
349
+ ContractBuilderMappings
350
+ > & {
351
+ readonly schemaVersion: '1';
352
+ readonly target: Target;
353
+ readonly targetFamily: 'sql';
354
+ readonly storageHash: StorageHash extends string ? StorageHash : string;
355
+ } & (ExtensionPacks extends Record<string, unknown>
356
+ ? { readonly extensionPacks: ExtensionPacks }
357
+ : Record<string, never>) &
358
+ (Capabilities extends Record<string, Record<string, boolean>>
359
+ ? { readonly capabilities: Capabilities }
360
+ : Record<string, never>),
361
+ TypeMaps<CodecTypes, Record<string, never>>
362
+ >
184
363
  : never;
185
364
  if (!this.state.target) {
186
365
  throw new Error('target is required. Call .target() before .build()');
@@ -189,6 +368,7 @@ class SqlContractBuilder<
189
368
  const target = this.state.target as Target & string;
190
369
 
191
370
  const storageTables = {} as Partial<Mutable<BuildStorageTables<Tables>>>;
371
+ const executionDefaults: ExecutionMutationDefault[] = [];
192
372
 
193
373
  for (const tableName of Object.keys(this.state.tables) as Array<keyof Tables & string>) {
194
374
  const tableState = this.state.tables[tableName];
@@ -210,18 +390,58 @@ class SqlContractBuilder<
210
390
  if (!columnState) continue;
211
391
  const codecId = columnState.type;
212
392
  const nativeType = columnState.nativeType;
393
+ const typeRef = columnState.typeRef;
394
+
395
+ const encodedDefault =
396
+ columnState.default !== undefined
397
+ ? encodeColumnDefault(columnState.default as ColumnDefault)
398
+ : undefined;
213
399
 
214
400
  columns[columnName as keyof ColumnDefs] = {
215
401
  nativeType,
216
402
  codecId,
217
403
  nullable: (columnState.nullable ?? false) as ColumnDefs[keyof ColumnDefs]['nullable'] &
218
404
  boolean,
405
+ ...ifDefined('typeParams', columnState.typeParams),
406
+ ...ifDefined('default', encodedDefault),
407
+ ...ifDefined('typeRef', typeRef),
219
408
  } as BuildStorageColumn<
220
409
  ColumnDefs[keyof ColumnDefs]['nullable'] & boolean,
221
410
  ColumnDefs[keyof ColumnDefs]['type']
222
411
  >;
412
+
413
+ if ('executionDefault' in columnState && columnState.executionDefault) {
414
+ executionDefaults.push({
415
+ ref: { table: tableName, column: columnName },
416
+ onCreate: columnState.executionDefault,
417
+ });
418
+ }
223
419
  }
224
420
 
421
+ // Build uniques from table state
422
+ const uniques = (tableState.uniques ?? []).map((u) => ({
423
+ columns: u.columns,
424
+ ...(u.name ? { name: u.name } : {}),
425
+ }));
426
+
427
+ // Build indexes from table state
428
+ const indexes = (tableState.indexes ?? []).map((i) => ({
429
+ columns: i.columns,
430
+ ...(i.name ? { name: i.name } : {}),
431
+ ...(i.using ? { using: i.using } : {}),
432
+ ...(i.config ? { config: i.config } : {}),
433
+ }));
434
+
435
+ // Build foreign keys from table state, materializing defaults
436
+ const foreignKeys = (tableState.foreignKeys ?? []).map((fk) => ({
437
+ columns: fk.columns,
438
+ references: fk.references,
439
+ ...applyFkDefaults(fk, this.state.foreignKeyDefaults),
440
+ ...(fk.name ? { name: fk.name } : {}),
441
+ ...(fk.onDelete !== undefined ? { onDelete: fk.onDelete } : {}),
442
+ ...(fk.onUpdate !== undefined ? { onUpdate: fk.onUpdate } : {}),
443
+ }));
444
+
225
445
  const table = {
226
446
  columns: columns as {
227
447
  [K in keyof ColumnDefs]: BuildStorageColumn<
@@ -229,13 +449,14 @@ class SqlContractBuilder<
229
449
  ColumnDefs[K]['type']
230
450
  >;
231
451
  },
232
- uniques: [],
233
- indexes: [],
234
- foreignKeys: [],
452
+ uniques,
453
+ indexes,
454
+ foreignKeys,
235
455
  ...(tableState.primaryKey
236
456
  ? {
237
457
  primaryKey: {
238
458
  columns: tableState.primaryKey,
459
+ ...(tableState.primaryKeyName ? { name: tableState.primaryKeyName } : {}),
239
460
  },
240
461
  }
241
462
  : {}),
@@ -244,7 +465,26 @@ class SqlContractBuilder<
244
465
  (storageTables as Mutable<BuildStorageTables<Tables>>)[tableName] = table;
245
466
  }
246
467
 
247
- const storage = { tables: storageTables as BuildStorageTables<Tables> } as BuildStorage<Tables>;
468
+ const storageTypes = (this.state.storageTypes ?? {}) as Types;
469
+ const storage: BuildStorage<Tables, Types> = {
470
+ tables: storageTables as BuildStorageTables<Tables>,
471
+ types: storageTypes,
472
+ };
473
+
474
+ const execution =
475
+ executionDefaults.length > 0
476
+ ? {
477
+ mutations: {
478
+ defaults: executionDefaults.sort((a, b) => {
479
+ const tableCompare = a.ref.table.localeCompare(b.ref.table);
480
+ if (tableCompare !== 0) {
481
+ return tableCompare;
482
+ }
483
+ return a.ref.column.localeCompare(b.ref.column);
484
+ }),
485
+ },
486
+ }
487
+ : undefined;
248
488
 
249
489
  // Build models - construct as partial first, then assert full type
250
490
  const modelsPartial: Partial<BuildModels<Models>> = {};
@@ -327,11 +567,7 @@ class SqlContractBuilder<
327
567
  storage as SqlStorage,
328
568
  );
329
569
 
330
- const mappings = {
331
- ...baseMappings,
332
- codecTypes: {} as CodecTypes,
333
- operationTypes: {} as Record<string, never>,
334
- } as ContractBuilderMappings<CodecTypes>;
570
+ const mappings = baseMappings as ContractBuilderMappings;
335
571
 
336
572
  const extensionNamespaces = this.state.extensionNamespaces ?? [];
337
573
  const extensionPacks: Record<string, unknown> = { ...(this.state.extensionPacks || {}) };
@@ -348,11 +584,12 @@ class SqlContractBuilder<
348
584
  schemaVersion: '1' as const,
349
585
  target,
350
586
  targetFamily: 'sql' as const,
351
- coreHash: this.state.coreHash || 'sha256:ts-builder-placeholder',
587
+ storageHash: this.state.storageHash || 'sha256:ts-builder-placeholder',
352
588
  models,
353
589
  relations: relationsPartial,
354
590
  storage,
355
591
  mappings,
592
+ ...(execution ? { execution } : {}),
356
593
  extensionPacks,
357
594
  capabilities: this.state.capabilities || {},
358
595
  meta: {},
@@ -365,38 +602,68 @@ class SqlContractBuilder<
365
602
  Target,
366
603
  Tables,
367
604
  Models,
368
- CoreHash,
605
+ Types,
606
+ StorageHash,
369
607
  ExtensionPacks,
370
608
  Capabilities
371
609
  >['build']
372
610
  >;
373
611
  }
374
612
 
375
- override target<T extends string>(
376
- packRef: TargetPackRef<'sql', T>,
377
- ): SqlContractBuilder<CodecTypes, T, Tables, Models, CoreHash, ExtensionPacks, Capabilities> {
613
+ override target<
614
+ T extends string,
615
+ TPack extends TargetPackRef<string, T> = TargetPackRef<string, T>,
616
+ >(
617
+ packRef: TPack & TargetPackRef<string, T>,
618
+ ): SqlContractBuilder<
619
+ ExtractCodecTypesFromPack<TPack> extends Record<string, never>
620
+ ? CodecTypes
621
+ : ExtractCodecTypesFromPack<TPack>,
622
+ T,
623
+ Tables,
624
+ Models,
625
+ Types,
626
+ StorageHash,
627
+ ExtensionPacks,
628
+ Capabilities
629
+ > {
378
630
  return new SqlContractBuilder<
379
- CodecTypes,
631
+ ExtractCodecTypesFromPack<TPack> extends Record<string, never>
632
+ ? CodecTypes
633
+ : ExtractCodecTypesFromPack<TPack>,
380
634
  T,
381
635
  Tables,
382
636
  Models,
383
- CoreHash,
637
+ Types,
638
+ StorageHash,
384
639
  ExtensionPacks,
385
640
  Capabilities
386
641
  >({
387
642
  ...this.state,
388
643
  target: packRef.targetId,
389
- });
644
+ }) as SqlContractBuilder<
645
+ ExtractCodecTypesFromPack<TPack> extends Record<string, never>
646
+ ? CodecTypes
647
+ : ExtractCodecTypesFromPack<TPack>,
648
+ T,
649
+ Tables,
650
+ Models,
651
+ Types,
652
+ StorageHash,
653
+ ExtensionPacks,
654
+ Capabilities
655
+ >;
390
656
  }
391
657
 
392
- extensionPacks(
393
- packs: Record<string, ExtensionPackRef<'sql', string>>,
658
+ extensionPacks<const Packs extends Record<string, ExtensionPackRef<'sql', string>>>(
659
+ packs: Packs,
394
660
  ): SqlContractBuilder<
395
- CodecTypes,
661
+ CodecTypes & MergeExtensionCodecTypes<Packs>,
396
662
  Target,
397
663
  Tables,
398
664
  Models,
399
- CoreHash,
665
+ Types,
666
+ StorageHash,
400
667
  ExtensionPacks,
401
668
  Capabilities
402
669
  > {
@@ -406,24 +673,24 @@ class SqlContractBuilder<
406
673
 
407
674
  const namespaces = new Set(this.state.extensionNamespaces ?? []);
408
675
 
409
- for (const packRef of Object.values(packs)) {
676
+ for (const packRef of Object.values(packs) as ExtensionPackRef<'sql', string>[]) {
410
677
  if (!packRef) continue;
411
678
 
412
679
  if (packRef.kind !== 'extension') {
413
680
  throw new Error(
414
- `extensionPacks() only accepts extension pack refs. Received kind \"${packRef.kind}\".`,
681
+ `extensionPacks() only accepts extension pack refs. Received kind "${packRef.kind}".`,
415
682
  );
416
683
  }
417
684
 
418
685
  if (packRef.familyId !== 'sql') {
419
686
  throw new Error(
420
- `extension pack \"${packRef.id}\" targets family \"${packRef.familyId}\" but this builder targets \"sql\".`,
687
+ `extension pack "${packRef.id}" targets family "${packRef.familyId}" but this builder targets "sql".`,
421
688
  );
422
689
  }
423
690
 
424
691
  if (packRef.targetId && packRef.targetId !== this.state.target) {
425
692
  throw new Error(
426
- `extension pack \"${packRef.id}\" targets \"${packRef.targetId}\" but builder target is \"${this.state.target}\".`,
693
+ `extension pack "${packRef.id}" targets "${packRef.targetId}" but builder target is "${this.state.target}".`,
427
694
  );
428
695
  }
429
696
 
@@ -431,11 +698,12 @@ class SqlContractBuilder<
431
698
  }
432
699
 
433
700
  return new SqlContractBuilder<
434
- CodecTypes,
701
+ CodecTypes & MergeExtensionCodecTypes<Packs>,
435
702
  Target,
436
703
  Tables,
437
704
  Models,
438
- CoreHash,
705
+ Types,
706
+ StorageHash,
439
707
  ExtensionPacks,
440
708
  Capabilities
441
709
  >({
@@ -446,27 +714,46 @@ class SqlContractBuilder<
446
714
 
447
715
  override capabilities<C extends Record<string, Record<string, boolean>>>(
448
716
  capabilities: C,
449
- ): SqlContractBuilder<CodecTypes, Target, Tables, Models, CoreHash, ExtensionPacks, C> {
450
- return new SqlContractBuilder<CodecTypes, Target, Tables, Models, CoreHash, ExtensionPacks, C>({
717
+ ): SqlContractBuilder<CodecTypes, Target, Tables, Models, Types, StorageHash, ExtensionPacks, C> {
718
+ return new SqlContractBuilder<
719
+ CodecTypes,
720
+ Target,
721
+ Tables,
722
+ Models,
723
+ Types,
724
+ StorageHash,
725
+ ExtensionPacks,
726
+ C
727
+ >({
451
728
  ...this.state,
452
729
  capabilities,
453
730
  });
454
731
  }
455
732
 
456
- override coreHash<H extends string>(
733
+ override storageHash<H extends string>(
457
734
  hash: H,
458
- ): SqlContractBuilder<CodecTypes, Target, Tables, Models, H, ExtensionPacks, Capabilities> {
735
+ ): SqlContractBuilder<
736
+ CodecTypes,
737
+ Target,
738
+ Tables,
739
+ Models,
740
+ Types,
741
+ H,
742
+ ExtensionPacks,
743
+ Capabilities
744
+ > {
459
745
  return new SqlContractBuilder<
460
746
  CodecTypes,
461
747
  Target,
462
748
  Tables,
463
749
  Models,
750
+ Types,
464
751
  H,
465
752
  ExtensionPacks,
466
753
  Capabilities
467
754
  >({
468
755
  ...this.state,
469
- coreHash: hash,
756
+ storageHash: hash,
470
757
  });
471
758
  }
472
759
 
@@ -485,12 +772,18 @@ class SqlContractBuilder<
485
772
  Target,
486
773
  Tables & Record<TableName, ReturnType<T['build']>>,
487
774
  Models,
488
- CoreHash,
775
+ Types,
776
+ StorageHash,
489
777
  ExtensionPacks,
490
778
  Capabilities
491
779
  > {
492
- const tableBuilder = new TableBuilder<TableName>(name);
493
- const result = callback(tableBuilder);
780
+ const tableBuilder = createTable(name);
781
+ const result = callback(
782
+ tableBuilder as unknown as SqlTableBuilder<
783
+ TableName,
784
+ CodecTypes
785
+ > as unknown as TableBuilder<TableName>,
786
+ );
494
787
  const finalBuilder = result instanceof TableBuilder ? result : tableBuilder;
495
788
  const tableState = finalBuilder.build();
496
789
 
@@ -499,7 +792,8 @@ class SqlContractBuilder<
499
792
  Target,
500
793
  Tables & Record<TableName, ReturnType<T['build']>>,
501
794
  Models,
502
- CoreHash,
795
+ Types,
796
+ StorageHash,
503
797
  ExtensionPacks,
504
798
  Capabilities
505
799
  >({
@@ -522,14 +816,15 @@ class SqlContractBuilder<
522
816
  name: ModelName,
523
817
  table: TableName,
524
818
  callback: (
525
- m: ModelBuilder<ModelName, TableName, Record<string, string>, Record<never, never>>,
819
+ m: ModelBuilder<ModelName, TableName, Record<never, never>, Record<never, never>>,
526
820
  ) => M | undefined,
527
821
  ): SqlContractBuilder<
528
822
  CodecTypes,
529
823
  Target,
530
824
  Tables,
531
825
  Models & Record<ModelName, ReturnType<M['build']>>,
532
- CoreHash,
826
+ Types,
827
+ StorageHash,
533
828
  ExtensionPacks,
534
829
  Capabilities
535
830
  > {
@@ -543,7 +838,8 @@ class SqlContractBuilder<
543
838
  Target,
544
839
  Tables,
545
840
  Models & Record<ModelName, ReturnType<M['build']>>,
546
- CoreHash,
841
+ Types,
842
+ StorageHash,
547
843
  ExtensionPacks,
548
844
  Capabilities
549
845
  >({
@@ -552,6 +848,64 @@ class SqlContractBuilder<
552
848
  Record<ModelName, ReturnType<M['build']>>,
553
849
  });
554
850
  }
851
+
852
+ override foreignKeyDefaults(
853
+ config: ForeignKeyDefaultsState,
854
+ ): SqlContractBuilder<
855
+ CodecTypes,
856
+ Target,
857
+ Tables,
858
+ Models,
859
+ Types,
860
+ StorageHash,
861
+ ExtensionPacks,
862
+ Capabilities
863
+ > {
864
+ return new SqlContractBuilder<
865
+ CodecTypes,
866
+ Target,
867
+ Tables,
868
+ Models,
869
+ Types,
870
+ StorageHash,
871
+ ExtensionPacks,
872
+ Capabilities
873
+ >({
874
+ ...this.state,
875
+ foreignKeyDefaults: config,
876
+ });
877
+ }
878
+
879
+ storageType<Name extends string, Type extends StorageTypeInstance>(
880
+ name: Name,
881
+ typeInstance: Type,
882
+ ): SqlContractBuilder<
883
+ CodecTypes,
884
+ Target,
885
+ Tables,
886
+ Models,
887
+ Types & Record<Name, Type>,
888
+ StorageHash,
889
+ ExtensionPacks,
890
+ Capabilities
891
+ > {
892
+ return new SqlContractBuilder<
893
+ CodecTypes,
894
+ Target,
895
+ Tables,
896
+ Models,
897
+ Types & Record<Name, Type>,
898
+ StorageHash,
899
+ ExtensionPacks,
900
+ Capabilities
901
+ >({
902
+ ...this.state,
903
+ storageTypes: {
904
+ ...(this.state.storageTypes ?? {}),
905
+ [name]: typeInstance,
906
+ },
907
+ });
908
+ }
555
909
  }
556
910
 
557
911
  export function defineContract<