@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
@@ -1,586 +1,267 @@
1
- import type { ExtensionPackRef, TargetPackRef } from '@prisma-next/contract/framework-components';
1
+ import type { ForeignKeyDefaultsState } from '@prisma-next/contract-authoring';
2
+ import type { CodecLookup } from '@prisma-next/framework-components/codec';
2
3
  import type {
3
- ColumnBuilderState,
4
- ModelBuilderState,
5
- RelationDefinition,
6
- TableBuilderState,
7
- } from '@prisma-next/contract-authoring';
4
+ ExtensionPackRef,
5
+ FamilyPackRef,
6
+ TargetPackRef,
7
+ } from '@prisma-next/framework-components/components';
8
+ import type { StorageTypeInstance } from '@prisma-next/sql-contract/types';
9
+ import { buildSqlContractFromDefinition } from './build-contract';
8
10
  import {
9
- type BuildModels,
10
- type BuildRelations,
11
- type BuildStorageColumn,
12
- ContractBuilder,
13
- createTable,
14
- type ExtractColumns,
15
- type ExtractPrimaryKey,
16
- ModelBuilder,
17
- type Mutable,
18
- TableBuilder,
19
- } from '@prisma-next/contract-authoring';
20
- import type {
21
- ModelDefinition,
22
- ModelField,
23
- SqlContract,
24
- SqlMappings,
25
- SqlStorage,
26
- } from '@prisma-next/sql-contract/types';
27
- import { computeMappings } from './contract';
28
-
29
- /**
30
- * Type-level mappings structure for contracts built via `defineContract()`.
31
- *
32
- * Compile-time type helper (not a runtime object) that ensures mappings match what the builder
33
- * produces. `codecTypes` uses the generic `CodecTypes` parameter; `operationTypes` is always
34
- * empty since operations are added via extensions at runtime.
35
- *
36
- * **Difference from RuntimeContext**: This is a compile-time type for contract construction.
37
- * `RuntimeContext` is a runtime object with populated registries for query execution.
38
- *
39
- * @template C - The `CodecTypes` generic parameter passed to `defineContract<CodecTypes>()`
40
- */
41
- type ContractBuilderMappings<C extends Record<string, { output: unknown }>> = Omit<
42
- SqlMappings,
43
- 'codecTypes' | 'operationTypes'
44
- > & {
45
- readonly codecTypes: C;
46
- readonly operationTypes: Record<string, never>;
47
- };
48
-
49
- type BuildStorageTable<
50
- _TableName extends string,
51
- Columns extends Record<string, ColumnBuilderState<string, boolean, string>>,
52
- PK extends readonly string[] | undefined,
53
- > = {
54
- readonly columns: {
55
- readonly [K in keyof Columns]: Columns[K] extends ColumnBuilderState<
56
- string,
57
- infer Null,
58
- infer TType
59
- >
60
- ? BuildStorageColumn<Null & boolean, TType>
61
- : never;
11
+ type ComposedAuthoringHelpers,
12
+ createComposedAuthoringHelpers,
13
+ } from './composed-authoring-helpers';
14
+ import {
15
+ type ContractInput,
16
+ type ContractModelBuilder,
17
+ field,
18
+ isContractInput,
19
+ type ModelAttributesSpec,
20
+ model,
21
+ type RelationBuilder,
22
+ type RelationState,
23
+ rel,
24
+ type ScalarFieldBuilder,
25
+ type SqlStageSpec,
26
+ } from './contract-dsl';
27
+ import { buildContractDefinition } from './contract-lowering';
28
+ import type { SqlContractResult } from './contract-types';
29
+
30
+ export { buildSqlContractFromDefinition } from './build-contract';
31
+
32
+ type ModelLike = {
33
+ readonly stageOne: {
34
+ readonly modelName?: string;
35
+ readonly fields: Record<string, ScalarFieldBuilder>;
36
+ readonly relations: Record<string, RelationBuilder<RelationState>>;
62
37
  };
63
- readonly uniques: ReadonlyArray<{ readonly columns: readonly string[]; readonly name?: string }>;
64
- readonly indexes: ReadonlyArray<{ readonly columns: readonly string[]; readonly name?: string }>;
65
- readonly foreignKeys: ReadonlyArray<{
66
- readonly columns: readonly string[];
67
- readonly references: { readonly table: string; readonly columns: readonly string[] };
68
- readonly name?: string;
69
- }>;
70
- } & (PK extends readonly string[]
71
- ? { readonly primaryKey: { readonly columns: PK; readonly name?: string } }
72
- : Record<string, never>);
38
+ readonly __attributes: ModelAttributesSpec | undefined;
39
+ readonly __sql: SqlStageSpec | undefined;
40
+ buildAttributesSpec(): ModelAttributesSpec | undefined;
41
+ buildSqlSpec(): SqlStageSpec | undefined;
42
+ };
73
43
 
74
- type BuildStorage<
75
- Tables extends Record<
76
- string,
77
- TableBuilderState<
78
- string,
79
- Record<string, ColumnBuilderState<string, boolean, string>>,
80
- readonly string[] | undefined
81
- >
82
- >,
44
+ type ContractDefinition<
45
+ Family extends FamilyPackRef<string>,
46
+ Target extends TargetPackRef<'sql', string>,
47
+ Types extends Record<string, StorageTypeInstance>,
48
+ Models extends Record<string, ModelLike>,
49
+ ExtensionPacks extends Record<string, ExtensionPackRef<'sql', string>> | undefined,
50
+ Capabilities extends Record<string, Record<string, boolean>> | undefined,
51
+ Naming extends ContractInput['naming'] | undefined,
52
+ StorageHash extends string | undefined,
53
+ ForeignKeyDefaults extends ForeignKeyDefaultsState | undefined,
83
54
  > = {
84
- readonly tables: {
85
- readonly [K in keyof Tables]: BuildStorageTable<
86
- K & string,
87
- ExtractColumns<Tables[K]>,
88
- ExtractPrimaryKey<Tables[K]>
89
- >;
90
- };
55
+ readonly family: Family;
56
+ readonly target: Target;
57
+ readonly extensionPacks?: ExtensionPacks;
58
+ readonly naming?: Naming;
59
+ readonly storageHash?: StorageHash;
60
+ readonly foreignKeyDefaults?: ForeignKeyDefaults;
61
+ readonly capabilities?: Capabilities;
62
+ readonly types?: Types;
63
+ readonly models?: Models;
64
+ readonly codecLookup?: CodecLookup;
91
65
  };
92
66
 
93
- type BuildStorageTables<
94
- Tables extends Record<
95
- string,
96
- TableBuilderState<
97
- string,
98
- Record<string, ColumnBuilderState<string, boolean, string>>,
99
- readonly string[] | undefined
100
- >
101
- >,
67
+ type ContractScaffold<
68
+ Family extends FamilyPackRef<string>,
69
+ Target extends TargetPackRef<'sql', string>,
70
+ ExtensionPacks extends Record<string, ExtensionPackRef<'sql', string>> | undefined,
71
+ Capabilities extends Record<string, Record<string, boolean>> | undefined,
72
+ Naming extends ContractInput['naming'] | undefined,
73
+ StorageHash extends string | undefined,
74
+ ForeignKeyDefaults extends ForeignKeyDefaultsState | undefined,
102
75
  > = {
103
- readonly [K in keyof Tables]: BuildStorageTable<
104
- K & string,
105
- ExtractColumns<Tables[K]>,
106
- ExtractPrimaryKey<Tables[K]>
107
- >;
76
+ readonly family: Family;
77
+ readonly target: Target;
78
+ readonly extensionPacks?: ExtensionPacks;
79
+ readonly naming?: Naming;
80
+ readonly storageHash?: StorageHash;
81
+ readonly foreignKeyDefaults?: ForeignKeyDefaults;
82
+ readonly capabilities?: Capabilities;
83
+ readonly codecLookup?: CodecLookup;
108
84
  };
109
85
 
110
- export interface ColumnBuilder<Name extends string, Nullable extends boolean, Type extends string> {
111
- nullable<Value extends boolean>(value?: Value): ColumnBuilder<Name, Value, Type>;
112
- type<Id extends string>(id: Id): ColumnBuilder<Name, Nullable, Id>;
113
- build(): ColumnBuilderState<Name, Nullable, Type>;
114
- }
115
-
116
- class SqlContractBuilder<
117
- CodecTypes extends Record<string, { output: unknown }> = Record<string, never>,
118
- Target extends string | undefined = undefined,
119
- Tables extends Record<
120
- string,
121
- TableBuilderState<
122
- string,
123
- Record<string, ColumnBuilderState<string, boolean, string>>,
124
- readonly string[] | undefined
125
- >
126
- > = Record<never, never>,
127
- Models extends Record<
128
- string,
129
- ModelBuilderState<string, string, Record<string, string>, Record<string, RelationDefinition>>
130
- > = Record<never, never>,
131
- CoreHash extends string | undefined = undefined,
132
- ExtensionPacks extends Record<string, unknown> | undefined = undefined,
133
- Capabilities extends Record<string, Record<string, boolean>> | undefined = undefined,
134
- > extends ContractBuilder<Target, Tables, Models, CoreHash, ExtensionPacks, Capabilities> {
135
- /**
136
- * This method is responsible for normalizing the contract IR by setting default values
137
- * for all required fields:
138
- * - `nullable`: defaults to `false` if not provided
139
- * - `uniques`: defaults to `[]` (empty array)
140
- * - `indexes`: defaults to `[]` (empty array)
141
- * - `foreignKeys`: defaults to `[]` (empty array)
142
- * - `relations`: defaults to `{}` (empty object) for both model-level and contract-level
143
- * - `nativeType`: required field set from column type descriptor when columns are defined
144
- *
145
- * The contract builder is the **only** place where normalization should occur.
146
- * Validators, parsers, and emitters should assume the contract is already normalized.
147
- *
148
- * **Required**: Use column type descriptors (e.g., `int4Column`, `textColumn`) when defining columns.
149
- * This ensures `nativeType` is set correctly at build time.
150
- *
151
- * @returns A normalized SqlContract with all required fields present
152
- */
153
- build(): Target extends string
154
- ? SqlContract<
155
- BuildStorage<Tables>,
156
- BuildModels<Models>,
157
- BuildRelations<Models>,
158
- ContractBuilderMappings<CodecTypes>
159
- > & {
160
- readonly schemaVersion: '1';
161
- readonly target: Target;
162
- readonly targetFamily: 'sql';
163
- readonly coreHash: CoreHash extends string ? CoreHash : string;
164
- } & (ExtensionPacks extends Record<string, unknown>
165
- ? { readonly extensionPacks: ExtensionPacks }
166
- : Record<string, never>) &
167
- (Capabilities extends Record<string, Record<string, boolean>>
168
- ? { readonly capabilities: Capabilities }
169
- : Record<string, never>)
170
- : never {
171
- // Type helper to ensure literal types are preserved in return type
172
- type BuiltContract = Target extends string
173
- ? SqlContract<
174
- BuildStorage<Tables>,
175
- BuildModels<Models>,
176
- BuildRelations<Models>,
177
- ContractBuilderMappings<CodecTypes>
178
- > & {
179
- readonly schemaVersion: '1';
180
- readonly target: Target;
181
- readonly targetFamily: 'sql';
182
- readonly coreHash: CoreHash extends string ? CoreHash : string;
183
- } & (ExtensionPacks extends Record<string, unknown>
184
- ? { readonly extensionPacks: ExtensionPacks }
185
- : Record<string, never>) &
186
- (Capabilities extends Record<string, Record<string, boolean>>
187
- ? { readonly capabilities: Capabilities }
188
- : Record<string, never>)
189
- : never;
190
- if (!this.state.target) {
191
- throw new Error('target is required. Call .target() before .build()');
192
- }
193
-
194
- const target = this.state.target as Target & string;
195
-
196
- const storageTables = {} as Partial<Mutable<BuildStorageTables<Tables>>>;
197
-
198
- for (const tableName of Object.keys(this.state.tables) as Array<keyof Tables & string>) {
199
- const tableState = this.state.tables[tableName];
200
- if (!tableState) continue;
201
-
202
- type TableKey = typeof tableName;
203
- type ColumnDefs = ExtractColumns<Tables[TableKey]>;
204
- type PrimaryKey = ExtractPrimaryKey<Tables[TableKey]>;
205
-
206
- const columns = {} as Partial<{
207
- [K in keyof ColumnDefs]: BuildStorageColumn<
208
- ColumnDefs[K]['nullable'] & boolean,
209
- ColumnDefs[K]['type']
210
- >;
211
- }>;
212
-
213
- for (const columnName in tableState.columns) {
214
- const columnState = tableState.columns[columnName];
215
- if (!columnState) continue;
216
- const codecId = columnState.type;
217
- const nativeType = columnState.nativeType;
218
-
219
- columns[columnName as keyof ColumnDefs] = {
220
- nativeType,
221
- codecId,
222
- nullable: (columnState.nullable ?? false) as ColumnDefs[keyof ColumnDefs]['nullable'] &
223
- boolean,
224
- } as BuildStorageColumn<
225
- ColumnDefs[keyof ColumnDefs]['nullable'] & boolean,
226
- ColumnDefs[keyof ColumnDefs]['type']
227
- >;
228
- }
229
-
230
- // Build uniques from table state
231
- const uniques = (tableState.uniques ?? []).map((u) => ({
232
- columns: u.columns,
233
- ...(u.name ? { name: u.name } : {}),
234
- }));
86
+ type ContractFactory<
87
+ Family extends FamilyPackRef<string>,
88
+ Target extends TargetPackRef<'sql', string>,
89
+ Types extends Record<string, StorageTypeInstance>,
90
+ Models extends Record<string, ModelLike>,
91
+ ExtensionPacks extends Record<string, ExtensionPackRef<'sql', string>> | undefined,
92
+ > = (helpers: ComposedAuthoringHelpers<Family, Target, ExtensionPacks>) => {
93
+ readonly types?: Types;
94
+ readonly models?: Models;
95
+ };
235
96
 
236
- // Build indexes from table state
237
- const indexes = (tableState.indexes ?? []).map((i) => ({
238
- columns: i.columns,
239
- ...(i.name ? { name: i.name } : {}),
240
- }));
97
+ function validateTargetPackRef(
98
+ family: FamilyPackRef<string>,
99
+ target: TargetPackRef<'sql', string>,
100
+ ): void {
101
+ if (family.familyId !== 'sql') {
102
+ throw new Error(
103
+ `defineContract only accepts SQL family packs. Received family "${family.familyId}".`,
104
+ );
105
+ }
241
106
 
242
- // Build foreign keys from table state
243
- const foreignKeys = (tableState.foreignKeys ?? []).map((fk) => ({
244
- columns: fk.columns,
245
- references: fk.references,
246
- ...(fk.name ? { name: fk.name } : {}),
247
- }));
107
+ if (target.familyId !== family.familyId) {
108
+ throw new Error(
109
+ `target pack "${target.id}" targets family "${target.familyId}" but contract family is "${family.familyId}".`,
110
+ );
111
+ }
112
+ }
248
113
 
249
- const table = {
250
- columns: columns as {
251
- [K in keyof ColumnDefs]: BuildStorageColumn<
252
- ColumnDefs[K]['nullable'] & boolean,
253
- ColumnDefs[K]['type']
254
- >;
255
- },
256
- uniques,
257
- indexes,
258
- foreignKeys,
259
- ...(tableState.primaryKey
260
- ? {
261
- primaryKey: {
262
- columns: tableState.primaryKey,
263
- ...(tableState.primaryKeyName ? { name: tableState.primaryKeyName } : {}),
264
- },
265
- }
266
- : {}),
267
- } as unknown as BuildStorageTable<TableKey & string, ColumnDefs, PrimaryKey>;
114
+ function validateExtensionPackRefs(
115
+ target: TargetPackRef<'sql', string>,
116
+ extensionPacks?: Record<string, ExtensionPackRef<'sql', string>>,
117
+ ): void {
118
+ if (!extensionPacks) {
119
+ return;
120
+ }
268
121
 
269
- (storageTables as Mutable<BuildStorageTables<Tables>>)[tableName] = table;
122
+ for (const packRef of Object.values(extensionPacks)) {
123
+ if (packRef.kind !== 'extension') {
124
+ throw new Error(
125
+ `defineContract only accepts extension pack refs in extensionPacks. Received kind "${packRef.kind}".`,
126
+ );
270
127
  }
271
128
 
272
- const storage = { tables: storageTables as BuildStorageTables<Tables> } as BuildStorage<Tables>;
273
-
274
- // Build models - construct as partial first, then assert full type
275
- const modelsPartial: Partial<BuildModels<Models>> = {};
276
-
277
- // Iterate over models - TypeScript will see keys as string, but type assertion preserves literals
278
- for (const modelName in this.state.models) {
279
- const modelState = this.state.models[modelName];
280
- if (!modelState) continue;
281
-
282
- const modelStateTyped = modelState as unknown as {
283
- name: string;
284
- table: string;
285
- fields: Record<string, string>;
286
- };
287
-
288
- // Build fields object
289
- const fields: Partial<Record<string, ModelField>> = {};
290
-
291
- // Iterate over fields
292
- for (const fieldName in modelStateTyped.fields) {
293
- const columnName = modelStateTyped.fields[fieldName];
294
- if (columnName) {
295
- fields[fieldName] = {
296
- column: columnName,
297
- };
298
- }
299
- }
300
-
301
- // Assign to models - type assertion preserves literal keys
302
- (modelsPartial as unknown as Record<string, ModelDefinition>)[modelName] = {
303
- storage: {
304
- table: modelStateTyped.table,
305
- },
306
- fields: fields as Record<string, ModelField>,
307
- relations: {},
308
- };
129
+ if (packRef.familyId !== target.familyId) {
130
+ throw new Error(
131
+ `extension pack "${packRef.id}" targets family "${packRef.familyId}" but contract target family is "${target.familyId}".`,
132
+ );
309
133
  }
310
134
 
311
- // Build relations object - organized by table name
312
- const relationsPartial: Partial<Record<string, Record<string, RelationDefinition>>> = {};
313
-
314
- // Iterate over models to collect relations
315
- for (const modelName in this.state.models) {
316
- const modelState = this.state.models[modelName];
317
- if (!modelState) continue;
318
-
319
- const modelStateTyped = modelState as unknown as {
320
- name: string;
321
- table: string;
322
- fields: Record<string, string>;
323
- relations: Record<string, RelationDefinition>;
324
- };
325
-
326
- const tableName = modelStateTyped.table;
327
- if (!tableName) continue;
328
-
329
- // Only initialize relations object for this table if it has relations
330
- if (modelStateTyped.relations && Object.keys(modelStateTyped.relations).length > 0) {
331
- if (!relationsPartial[tableName]) {
332
- relationsPartial[tableName] = {};
333
- }
334
-
335
- // Add relations from this model to the table's relations
336
- const tableRelations = relationsPartial[tableName];
337
- if (tableRelations) {
338
- for (const relationName in modelStateTyped.relations) {
339
- const relation = modelStateTyped.relations[relationName];
340
- if (relation) {
341
- tableRelations[relationName] = relation;
342
- }
343
- }
344
- }
345
- }
346
- }
347
-
348
- const models = modelsPartial as unknown as BuildModels<Models>;
349
-
350
- const baseMappings = computeMappings(
351
- models as unknown as Record<string, ModelDefinition>,
352
- storage as SqlStorage,
353
- );
354
-
355
- const mappings = {
356
- ...baseMappings,
357
- codecTypes: {} as CodecTypes,
358
- operationTypes: {} as Record<string, never>,
359
- } as ContractBuilderMappings<CodecTypes>;
360
-
361
- const extensionNamespaces = this.state.extensionNamespaces ?? [];
362
- const extensionPacks: Record<string, unknown> = { ...(this.state.extensionPacks || {}) };
363
- for (const namespace of extensionNamespaces) {
364
- if (!Object.hasOwn(extensionPacks, namespace)) {
365
- extensionPacks[namespace] = {};
366
- }
135
+ if (packRef.targetId && packRef.targetId !== target.targetId) {
136
+ throw new Error(
137
+ `extension pack "${packRef.id}" targets "${packRef.targetId}" but contract target is "${target.targetId}".`,
138
+ );
367
139
  }
140
+ }
141
+ }
368
142
 
369
- // Construct contract with explicit type that matches the generic parameters
370
- // This ensures TypeScript infers literal types from the generics, not runtime values
371
- // Always include relations, even if empty (normalized to empty object)
372
- const contract = {
373
- schemaVersion: '1' as const,
374
- target,
375
- targetFamily: 'sql' as const,
376
- coreHash: this.state.coreHash || 'sha256:ts-builder-placeholder',
377
- models,
378
- relations: relationsPartial,
379
- storage,
380
- mappings,
381
- extensionPacks,
382
- capabilities: this.state.capabilities || {},
383
- meta: {},
384
- sources: {},
385
- } as unknown as BuiltContract;
143
+ function buildContractFromDsl<Definition extends ContractInput>(
144
+ definition: Definition,
145
+ ): SqlContractResult<Definition>;
386
146
 
387
- return contract as unknown as ReturnType<
388
- SqlContractBuilder<
389
- CodecTypes,
390
- Target,
391
- Tables,
392
- Models,
393
- CoreHash,
394
- ExtensionPacks,
395
- Capabilities
396
- >['build']
397
- >;
398
- }
147
+ function buildContractFromDsl(
148
+ definition: ContractInput,
149
+ ): ReturnType<typeof buildSqlContractFromDefinition> {
150
+ validateTargetPackRef(definition.family, definition.target);
151
+ validateExtensionPackRefs(definition.target, definition.extensionPacks);
399
152
 
400
- override target<T extends string>(
401
- packRef: TargetPackRef<'sql', T>,
402
- ): SqlContractBuilder<CodecTypes, T, Tables, Models, CoreHash, ExtensionPacks, Capabilities> {
403
- return new SqlContractBuilder<
404
- CodecTypes,
405
- T,
406
- Tables,
407
- Models,
408
- CoreHash,
409
- ExtensionPacks,
410
- Capabilities
411
- >({
412
- ...this.state,
413
- target: packRef.targetId,
414
- });
415
- }
153
+ return buildSqlContractFromDefinition(
154
+ buildContractDefinition(definition),
155
+ definition.codecLookup,
156
+ );
157
+ }
416
158
 
417
- extensionPacks(
418
- packs: Record<string, ExtensionPackRef<'sql', string>>,
419
- ): SqlContractBuilder<
420
- CodecTypes,
159
+ export function defineContract<
160
+ const Family extends FamilyPackRef<string>,
161
+ const Target extends TargetPackRef<'sql', string>,
162
+ const Types extends Record<string, StorageTypeInstance> = Record<never, never>,
163
+ const Models extends Record<string, ModelLike> = Record<never, never>,
164
+ const ExtensionPacks extends
165
+ | Record<string, ExtensionPackRef<'sql', string>>
166
+ | undefined = undefined,
167
+ const Capabilities extends Record<string, Record<string, boolean>> | undefined = undefined,
168
+ const Naming extends ContractInput['naming'] | undefined = undefined,
169
+ const StorageHash extends string | undefined = undefined,
170
+ const ForeignKeyDefaults extends ForeignKeyDefaultsState | undefined = undefined,
171
+ >(
172
+ definition: ContractDefinition<
173
+ Family,
421
174
  Target,
422
- Tables,
175
+ Types,
423
176
  Models,
424
- CoreHash,
425
177
  ExtensionPacks,
426
- Capabilities
427
- > {
428
- if (!this.state.target) {
429
- throw new Error('extensionPacks() requires target() to be called first');
430
- }
431
-
432
- const namespaces = new Set(this.state.extensionNamespaces ?? []);
433
-
434
- for (const packRef of Object.values(packs)) {
435
- if (!packRef) continue;
436
-
437
- if (packRef.kind !== 'extension') {
438
- throw new Error(
439
- `extensionPacks() only accepts extension pack refs. Received kind "${packRef.kind}".`,
440
- );
441
- }
442
-
443
- if (packRef.familyId !== 'sql') {
444
- throw new Error(
445
- `extension pack "${packRef.id}" targets family "${packRef.familyId}" but this builder targets "sql".`,
446
- );
447
- }
448
-
449
- if (packRef.targetId && packRef.targetId !== this.state.target) {
450
- throw new Error(
451
- `extension pack "${packRef.id}" targets "${packRef.targetId}" but builder target is "${this.state.target}".`,
452
- );
453
- }
454
-
455
- namespaces.add(packRef.id);
456
- }
457
-
458
- return new SqlContractBuilder<
459
- CodecTypes,
460
- Target,
461
- Tables,
462
- Models,
463
- CoreHash,
464
- ExtensionPacks,
465
- Capabilities
466
- >({
467
- ...this.state,
468
- extensionNamespaces: [...namespaces],
469
- });
470
- }
471
-
472
- override capabilities<C extends Record<string, Record<string, boolean>>>(
473
- capabilities: C,
474
- ): SqlContractBuilder<CodecTypes, Target, Tables, Models, CoreHash, ExtensionPacks, C> {
475
- return new SqlContractBuilder<CodecTypes, Target, Tables, Models, CoreHash, ExtensionPacks, C>({
476
- ...this.state,
477
- capabilities,
478
- });
479
- }
480
-
481
- override coreHash<H extends string>(
482
- hash: H,
483
- ): SqlContractBuilder<CodecTypes, Target, Tables, Models, H, ExtensionPacks, Capabilities> {
484
- return new SqlContractBuilder<
485
- CodecTypes,
486
- Target,
487
- Tables,
488
- Models,
489
- H,
490
- ExtensionPacks,
491
- Capabilities
492
- >({
493
- ...this.state,
494
- coreHash: hash,
495
- });
496
- }
497
-
498
- override table<
499
- TableName extends string,
500
- T extends TableBuilder<
501
- TableName,
502
- Record<string, ColumnBuilderState<string, boolean, string>>,
503
- readonly string[] | undefined
504
- >,
505
- >(
506
- name: TableName,
507
- callback: (t: TableBuilder<TableName>) => T | undefined,
508
- ): SqlContractBuilder<
509
- CodecTypes,
178
+ Capabilities,
179
+ Naming,
180
+ StorageHash,
181
+ ForeignKeyDefaults
182
+ >,
183
+ ): SqlContractResult<
184
+ ContractDefinition<
185
+ Family,
510
186
  Target,
511
- Tables & Record<TableName, ReturnType<T['build']>>,
187
+ Types,
512
188
  Models,
513
- CoreHash,
514
189
  ExtensionPacks,
515
- Capabilities
516
- > {
517
- const tableBuilder = createTable(name);
518
- const result = callback(tableBuilder);
519
- const finalBuilder = result instanceof TableBuilder ? result : tableBuilder;
520
- const tableState = finalBuilder.build();
521
-
522
- return new SqlContractBuilder<
523
- CodecTypes,
524
- Target,
525
- Tables & Record<TableName, ReturnType<T['build']>>,
526
- Models,
527
- CoreHash,
528
- ExtensionPacks,
529
- Capabilities
530
- >({
531
- ...this.state,
532
- tables: { ...this.state.tables, [name]: tableState } as Tables &
533
- Record<TableName, ReturnType<T['build']>>,
534
- });
535
- }
536
-
537
- override model<
538
- ModelName extends string,
539
- TableName extends string,
540
- M extends ModelBuilder<
541
- ModelName,
542
- TableName,
543
- Record<string, string>,
544
- Record<string, RelationDefinition>
545
- >,
546
- >(
547
- name: ModelName,
548
- table: TableName,
549
- callback: (
550
- m: ModelBuilder<ModelName, TableName, Record<string, string>, Record<never, never>>,
551
- ) => M | undefined,
552
- ): SqlContractBuilder<
553
- CodecTypes,
190
+ Capabilities,
191
+ Naming,
192
+ StorageHash,
193
+ ForeignKeyDefaults
194
+ >
195
+ >;
196
+ export function defineContract<
197
+ const Family extends FamilyPackRef<string>,
198
+ const Target extends TargetPackRef<'sql', string>,
199
+ const Types extends Record<string, StorageTypeInstance> = Record<never, never>,
200
+ const Models extends Record<string, ModelLike> = Record<never, never>,
201
+ const ExtensionPacks extends
202
+ | Record<string, ExtensionPackRef<'sql', string>>
203
+ | undefined = undefined,
204
+ const Capabilities extends Record<string, Record<string, boolean>> | undefined = undefined,
205
+ const Naming extends ContractInput['naming'] | undefined = undefined,
206
+ const StorageHash extends string | undefined = undefined,
207
+ const ForeignKeyDefaults extends ForeignKeyDefaultsState | undefined = undefined,
208
+ >(
209
+ definition: ContractScaffold<
210
+ Family,
211
+ Target,
212
+ ExtensionPacks,
213
+ Capabilities,
214
+ Naming,
215
+ StorageHash,
216
+ ForeignKeyDefaults
217
+ >,
218
+ factory: ContractFactory<Family, Target, Types, Models, ExtensionPacks>,
219
+ ): SqlContractResult<
220
+ ContractDefinition<
221
+ Family,
554
222
  Target,
555
- Tables,
556
- Models & Record<ModelName, ReturnType<M['build']>>,
557
- CoreHash,
223
+ Types,
224
+ Models,
558
225
  ExtensionPacks,
559
- Capabilities
560
- > {
561
- const modelBuilder = new ModelBuilder<ModelName, TableName>(name, table);
562
- const result = callback(modelBuilder);
563
- const finalBuilder = result instanceof ModelBuilder ? result : modelBuilder;
564
- const modelState = finalBuilder.build();
226
+ Capabilities,
227
+ Naming,
228
+ StorageHash,
229
+ ForeignKeyDefaults
230
+ >
231
+ >;
232
+ export function defineContract(
233
+ definition: ContractInput,
234
+ factory?: ContractFactory<
235
+ FamilyPackRef<string>,
236
+ TargetPackRef<'sql', string>,
237
+ Record<string, StorageTypeInstance>,
238
+ Record<string, ModelLike>,
239
+ Record<string, ExtensionPackRef<'sql', string>> | undefined
240
+ >,
241
+ ): SqlContractResult<ContractInput> {
242
+ if (!isContractInput(definition)) {
243
+ throw new TypeError(
244
+ 'defineContract expects a contract definition object. Define your contract with defineContract({ family, target, models, ... }).',
245
+ );
246
+ }
565
247
 
566
- return new SqlContractBuilder<
567
- CodecTypes,
568
- Target,
569
- Tables,
570
- Models & Record<ModelName, ReturnType<M['build']>>,
571
- CoreHash,
572
- ExtensionPacks,
573
- Capabilities
574
- >({
575
- ...this.state,
576
- models: { ...this.state.models, [name]: modelState } as Models &
577
- Record<ModelName, ReturnType<M['build']>>,
578
- });
248
+ if (!factory) {
249
+ return buildContractFromDsl(definition);
579
250
  }
580
- }
581
251
 
582
- export function defineContract<
583
- CodecTypes extends Record<string, { output: unknown }> = Record<string, never>,
584
- >(): SqlContractBuilder<CodecTypes> {
585
- return new SqlContractBuilder<CodecTypes>();
252
+ const builtDefinition = {
253
+ ...definition,
254
+ ...factory(
255
+ createComposedAuthoringHelpers({
256
+ family: definition.family,
257
+ target: definition.target,
258
+ extensionPacks: definition.extensionPacks,
259
+ }),
260
+ ),
261
+ };
262
+
263
+ return buildContractFromDsl(builtDefinition);
586
264
  }
265
+
266
+ export { field, model, rel };
267
+ export type { ComposedAuthoringHelpers, ContractInput, ContractModelBuilder, ScalarFieldBuilder };