@prisma-next/sql-contract-ts 0.3.0-dev.135 → 0.3.0-dev.146

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,9 +1,274 @@
1
- import { ContractBuilder, ModelBuilder, TableBuilder, createTable } from "@prisma-next/contract-authoring";
2
- import { instantiateAuthoringFieldPreset, instantiateAuthoringTypeConstructor, isAuthoringFieldPresetDescriptor, isAuthoringTypeConstructorDescriptor, validateAuthoringHelperArguments } from "@prisma-next/contract/framework-components";
3
- import { ifDefined } from "@prisma-next/utils/defined";
1
+ import { computeExecutionHash, computeProfileHash, computeStorageHash } from "@prisma-next/contract/hashing";
2
+ import { coreHash } from "@prisma-next/contract/types";
4
3
  import { applyFkDefaults } from "@prisma-next/sql-contract/types";
5
4
  import { validateStorageSemantics } from "@prisma-next/sql-contract/validators";
5
+ import { ifDefined } from "@prisma-next/utils/defined";
6
+ import { instantiateAuthoringFieldPreset, instantiateAuthoringTypeConstructor, isAuthoringFieldPresetDescriptor, isAuthoringTypeConstructorDescriptor, validateAuthoringHelperArguments } from "@prisma-next/framework-components/authoring";
6
7
 
8
+ //#region src/build-contract.ts
9
+ function encodeDefaultLiteralValue(value, codecId, codecLookup) {
10
+ const codec = codecLookup?.get(codecId);
11
+ if (codec) return codec.encodeJson(value);
12
+ return value;
13
+ }
14
+ function encodeColumnDefault(defaultInput, codecId, codecLookup) {
15
+ if (defaultInput.kind === "function") return {
16
+ kind: "function",
17
+ expression: defaultInput.expression
18
+ };
19
+ return {
20
+ kind: "literal",
21
+ value: encodeDefaultLiteralValue(defaultInput.value, codecId, codecLookup)
22
+ };
23
+ }
24
+ function assertStorageSemantics(storage) {
25
+ const semanticErrors = validateStorageSemantics(storage);
26
+ if (semanticErrors.length > 0) throw new Error(`Contract semantic validation failed: ${semanticErrors.join("; ")}`);
27
+ }
28
+ function assertKnownTargetModel(modelsByName, sourceModelName, targetModelName, context) {
29
+ const targetModel = modelsByName.get(targetModelName);
30
+ if (!targetModel) throw new Error(`${context} on model "${sourceModelName}" references unknown model "${targetModelName}"`);
31
+ return targetModel;
32
+ }
33
+ function assertTargetTableMatches(sourceModelName, targetModel, referencedTableName, context) {
34
+ if (targetModel.tableName !== referencedTableName) throw new Error(`${context} on model "${sourceModelName}" references table "${referencedTableName}" but model "${targetModel.modelName}" maps to "${targetModel.tableName}"`);
35
+ }
36
+ function isValueObjectField(field$1) {
37
+ return "valueObjectName" in field$1;
38
+ }
39
+ const JSONB_CODEC_ID = "pg/jsonb@1";
40
+ const JSONB_NATIVE_TYPE = "jsonb";
41
+ function buildStorageColumn(field$1, codecLookup) {
42
+ if (isValueObjectField(field$1)) {
43
+ const encodedDefault$1 = field$1.default !== void 0 ? encodeColumnDefault(field$1.default, JSONB_CODEC_ID, codecLookup) : void 0;
44
+ return {
45
+ nativeType: JSONB_NATIVE_TYPE,
46
+ codecId: JSONB_CODEC_ID,
47
+ nullable: field$1.nullable,
48
+ ...ifDefined("default", encodedDefault$1)
49
+ };
50
+ }
51
+ if (field$1.many) return {
52
+ nativeType: JSONB_NATIVE_TYPE,
53
+ codecId: JSONB_CODEC_ID,
54
+ nullable: field$1.nullable
55
+ };
56
+ const codecId = field$1.descriptor.codecId;
57
+ const encodedDefault = field$1.default !== void 0 ? encodeColumnDefault(field$1.default, codecId, codecLookup) : void 0;
58
+ return {
59
+ nativeType: field$1.descriptor.nativeType,
60
+ codecId,
61
+ nullable: field$1.nullable,
62
+ ...ifDefined("typeParams", field$1.descriptor.typeParams),
63
+ ...ifDefined("default", encodedDefault),
64
+ ...ifDefined("typeRef", field$1.descriptor.typeRef)
65
+ };
66
+ }
67
+ function buildDomainField(field$1, column) {
68
+ if (isValueObjectField(field$1)) return {
69
+ type: {
70
+ kind: "valueObject",
71
+ name: field$1.valueObjectName
72
+ },
73
+ nullable: field$1.nullable,
74
+ ...field$1.many ? { many: true } : {}
75
+ };
76
+ return {
77
+ type: {
78
+ kind: "scalar",
79
+ codecId: column.codecId,
80
+ ...ifDefined("typeParams", column.typeParams)
81
+ },
82
+ nullable: column.nullable,
83
+ ...field$1.many ? { many: true } : {}
84
+ };
85
+ }
86
+ function buildSqlContractFromDefinition(definition, codecLookup) {
87
+ const target = definition.target.targetId;
88
+ const targetFamily = "sql";
89
+ const modelsByName = new Map(definition.models.map((m) => [m.modelName, m]));
90
+ const storageTables = {};
91
+ const executionDefaults = [];
92
+ const models = {};
93
+ const roots = {};
94
+ for (const semanticModel of definition.models) {
95
+ const tableName = semanticModel.tableName;
96
+ roots[tableName] = semanticModel.modelName;
97
+ const columns = {};
98
+ const fieldToColumn = {};
99
+ const domainFields = {};
100
+ const domainFieldRefs = {};
101
+ for (const field$1 of semanticModel.fields) {
102
+ if (field$1.executionDefault) {
103
+ if (field$1.default !== void 0) throw new Error(`Field "${semanticModel.modelName}.${field$1.fieldName}" cannot define both default and executionDefault.`);
104
+ if (field$1.nullable) throw new Error(`Field "${semanticModel.modelName}.${field$1.fieldName}" cannot be nullable when executionDefault is present.`);
105
+ }
106
+ const column = buildStorageColumn(field$1, codecLookup);
107
+ columns[field$1.columnName] = column;
108
+ fieldToColumn[field$1.fieldName] = field$1.columnName;
109
+ domainFields[field$1.fieldName] = buildDomainField(field$1, column);
110
+ if (isValueObjectField(field$1)) domainFieldRefs[field$1.fieldName] = {
111
+ kind: "valueObject",
112
+ name: field$1.valueObjectName,
113
+ ...field$1.many ? { many: true } : {}
114
+ };
115
+ else if (field$1.many) domainFieldRefs[field$1.fieldName] = {
116
+ kind: "scalar",
117
+ many: true
118
+ };
119
+ if ("executionDefault" in field$1 && field$1.executionDefault) executionDefaults.push({
120
+ ref: {
121
+ table: tableName,
122
+ column: field$1.columnName
123
+ },
124
+ onCreate: field$1.executionDefault
125
+ });
126
+ }
127
+ if (semanticModel.id) {
128
+ const fieldsByColumnName = new Map(semanticModel.fields.map((field$1) => [field$1.columnName, field$1]));
129
+ for (const columnName of semanticModel.id.columns) {
130
+ const field$1 = fieldsByColumnName.get(columnName);
131
+ if (field$1?.nullable) throw new Error(`Model "${semanticModel.modelName}" uses nullable field "${field$1.fieldName}" in its identity.`);
132
+ }
133
+ }
134
+ const foreignKeys = (semanticModel.foreignKeys ?? []).map((fk) => {
135
+ const targetModel = assertKnownTargetModel(modelsByName, semanticModel.modelName, fk.references.model, "Foreign key");
136
+ assertTargetTableMatches(semanticModel.modelName, targetModel, fk.references.table, "Foreign key");
137
+ return {
138
+ columns: fk.columns,
139
+ references: {
140
+ table: fk.references.table,
141
+ columns: fk.references.columns
142
+ },
143
+ ...applyFkDefaults({
144
+ ...ifDefined("constraint", fk.constraint),
145
+ ...ifDefined("index", fk.index)
146
+ }, definition.foreignKeyDefaults),
147
+ ...ifDefined("name", fk.name),
148
+ ...ifDefined("onDelete", fk.onDelete),
149
+ ...ifDefined("onUpdate", fk.onUpdate)
150
+ };
151
+ });
152
+ storageTables[tableName] = {
153
+ columns,
154
+ uniques: (semanticModel.uniques ?? []).map((u) => ({
155
+ columns: u.columns,
156
+ ...ifDefined("name", u.name)
157
+ })),
158
+ indexes: (semanticModel.indexes ?? []).map((i) => ({
159
+ columns: i.columns,
160
+ ...ifDefined("name", i.name),
161
+ ...ifDefined("using", i.using),
162
+ ...ifDefined("config", i.config)
163
+ })),
164
+ foreignKeys,
165
+ ...semanticModel.id ? { primaryKey: {
166
+ columns: semanticModel.id.columns,
167
+ ...ifDefined("name", semanticModel.id.name)
168
+ } } : {}
169
+ };
170
+ const storageFields = {};
171
+ for (const [fieldName, columnName] of Object.entries(fieldToColumn)) storageFields[fieldName] = { column: columnName };
172
+ const columnToField = new Map(Object.entries(fieldToColumn).map(([field$1, col]) => [col, field$1]));
173
+ const modelRelations = {};
174
+ for (const relation of semanticModel.relations ?? []) {
175
+ const targetModel = assertKnownTargetModel(modelsByName, semanticModel.modelName, relation.toModel, "Relation");
176
+ assertTargetTableMatches(semanticModel.modelName, targetModel, relation.toTable, "Relation");
177
+ if (relation.cardinality === "N:M" && !relation.through) throw new Error(`Relation "${semanticModel.modelName}.${relation.fieldName}" with cardinality "N:M" requires through metadata`);
178
+ const targetColumnToField = new Map(targetModel.fields.map((f) => [f.columnName, f.fieldName]));
179
+ modelRelations[relation.fieldName] = {
180
+ to: relation.toModel,
181
+ cardinality: relation.cardinality,
182
+ on: {
183
+ localFields: relation.on.parentColumns.map((col) => columnToField.get(col) ?? col),
184
+ targetFields: relation.on.childColumns.map((col) => targetColumnToField.get(col) ?? col)
185
+ },
186
+ ...relation.through ? { through: {
187
+ table: relation.through.table,
188
+ parentCols: relation.through.parentColumns,
189
+ childCols: relation.through.childColumns
190
+ } } : void 0
191
+ };
192
+ }
193
+ models[semanticModel.modelName] = {
194
+ storage: {
195
+ table: tableName,
196
+ fields: storageFields
197
+ },
198
+ fields: domainFields,
199
+ relations: modelRelations
200
+ };
201
+ }
202
+ const storageWithoutHash = {
203
+ tables: storageTables,
204
+ types: definition.storageTypes ?? {}
205
+ };
206
+ const storageHash = definition.storageHash ? coreHash(definition.storageHash) : computeStorageHash({
207
+ target,
208
+ targetFamily,
209
+ storage: storageWithoutHash
210
+ });
211
+ const storage = {
212
+ ...storageWithoutHash,
213
+ storageHash
214
+ };
215
+ const executionSection = executionDefaults.length > 0 ? { mutations: { defaults: executionDefaults.sort((a, b) => {
216
+ const tableCompare = a.ref.table.localeCompare(b.ref.table);
217
+ if (tableCompare !== 0) return tableCompare;
218
+ return a.ref.column.localeCompare(b.ref.column);
219
+ }) } } : void 0;
220
+ const extensionNamespaces = definition.extensionPacks ? Object.values(definition.extensionPacks).map((pack) => pack.id) : void 0;
221
+ const extensionPacks = { ...definition.extensionPacks || {} };
222
+ if (extensionNamespaces) {
223
+ for (const namespace of extensionNamespaces) if (!Object.hasOwn(extensionPacks, namespace)) extensionPacks[namespace] = {};
224
+ }
225
+ const capabilities = definition.capabilities || {};
226
+ const profileHash = computeProfileHash({
227
+ target,
228
+ targetFamily,
229
+ capabilities
230
+ });
231
+ const executionWithHash = executionSection ? {
232
+ ...executionSection,
233
+ executionHash: computeExecutionHash({
234
+ target,
235
+ targetFamily,
236
+ execution: executionSection
237
+ })
238
+ } : void 0;
239
+ const valueObjects = definition.valueObjects && definition.valueObjects.length > 0 ? Object.fromEntries(definition.valueObjects.map((vo) => [vo.name, { fields: Object.fromEntries(vo.fields.map((f) => [f.fieldName, isValueObjectField(f) ? {
240
+ type: {
241
+ kind: "valueObject",
242
+ name: f.valueObjectName
243
+ },
244
+ nullable: f.nullable,
245
+ ...f.many ? { many: true } : {}
246
+ } : {
247
+ type: {
248
+ kind: "scalar",
249
+ codecId: f.descriptor.codecId,
250
+ ...ifDefined("typeParams", f.descriptor.typeParams)
251
+ },
252
+ nullable: f.nullable
253
+ }])) }])) : void 0;
254
+ const contract = {
255
+ target,
256
+ targetFamily,
257
+ models,
258
+ roots,
259
+ storage,
260
+ ...executionWithHash ? { execution: executionWithHash } : {},
261
+ ...ifDefined("valueObjects", valueObjects),
262
+ extensionPacks,
263
+ capabilities,
264
+ profileHash,
265
+ meta: {}
266
+ };
267
+ assertStorageSemantics(contract.storage);
268
+ return contract;
269
+ }
270
+
271
+ //#endregion
7
272
  //#region src/authoring-helper-runtime.ts
8
273
  function isNamedConstraintOptionsLike(value) {
9
274
  if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
@@ -74,7 +339,7 @@ function createFieldHelpersFromNamespace(namespace, createLeafHelper, path = [])
74
339
  }
75
340
 
76
341
  //#endregion
77
- //#region src/staged-contract-dsl.ts
342
+ //#region src/contract-dsl.ts
78
343
  function isColumnDefault(value) {
79
344
  if (typeof value !== "object" || value === null) return false;
80
345
  const kind = value.kind;
@@ -310,7 +575,7 @@ function createColumnRefs(fields) {
310
575
  function findDuplicateRelationName(existingRelations, nextRelations) {
311
576
  return Object.keys(nextRelations).find((relationName) => Object.hasOwn(existingRelations, relationName));
312
577
  }
313
- var StagedModelBuilder = class StagedModelBuilder {
578
+ var ContractModelBuilder = class ContractModelBuilder {
314
579
  refs;
315
580
  constructor(stageOne, attributesFactory, sqlFactory) {
316
581
  this.stageOne = stageOne;
@@ -331,7 +596,7 @@ var StagedModelBuilder = class StagedModelBuilder {
331
596
  relations(relations) {
332
597
  const duplicateRelationName = findDuplicateRelationName(this.stageOne.relations, relations);
333
598
  if (duplicateRelationName) throw new Error(`Model "${this.stageOne.modelName ?? "<anonymous>"}" already defines relation "${duplicateRelationName}".`);
334
- return new StagedModelBuilder({
599
+ return new ContractModelBuilder({
335
600
  ...this.stageOne,
336
601
  relations: {
337
602
  ...this.stageOne.relations,
@@ -340,10 +605,10 @@ var StagedModelBuilder = class StagedModelBuilder {
340
605
  }, this.attributesFactory, this.sqlFactory);
341
606
  }
342
607
  attributes(specOrFactory) {
343
- return new StagedModelBuilder(this.stageOne, specOrFactory, this.sqlFactory);
608
+ return new ContractModelBuilder(this.stageOne, specOrFactory, this.sqlFactory);
344
609
  }
345
610
  sql(specOrFactory) {
346
- return new StagedModelBuilder(this.stageOne, this.attributesFactory, specOrFactory);
611
+ return new ContractModelBuilder(this.stageOne, this.attributesFactory, specOrFactory);
347
612
  }
348
613
  buildAttributesSpec() {
349
614
  if (!this.attributesFactory) return;
@@ -388,7 +653,7 @@ function normalizeRelationModelSource(target) {
388
653
  function model(modelNameOrInput, maybeInput) {
389
654
  const input = typeof modelNameOrInput === "string" ? maybeInput : modelNameOrInput;
390
655
  if (!input) throw new Error("model(\"ModelName\", ...) requires a model definition.");
391
- return new StagedModelBuilder({
656
+ return new ContractModelBuilder({
392
657
  ...typeof modelNameOrInput === "string" ? { modelName: modelNameOrInput } : {},
393
658
  fields: input.fields,
394
659
  relations: input.relations ?? {}
@@ -436,7 +701,7 @@ const field = {
436
701
  generated: generatedField,
437
702
  namedType: namedTypeField
438
703
  };
439
- function isStagedContractInput(value) {
704
+ function isContractInput(value) {
440
705
  if (typeof value !== "object" || value === null || !("target" in value) || !("family" in value)) return false;
441
706
  const target = value.target;
442
707
  const family = value.family;
@@ -550,291 +815,7 @@ function createComposedAuthoringHelpers(options) {
550
815
  }
551
816
 
552
817
  //#endregion
553
- //#region src/contract-ir-builder.ts
554
- function isPlainObject(value) {
555
- if (typeof value !== "object" || value === null) return false;
556
- const proto = Object.getPrototypeOf(value);
557
- return proto === Object.prototype || proto === null;
558
- }
559
- function isJsonValue(value) {
560
- if (value === null) return true;
561
- const valueType = typeof value;
562
- if (valueType === "string" || valueType === "number" || valueType === "boolean") return true;
563
- if (Array.isArray(value)) return value.every((item) => isJsonValue(item));
564
- if (isPlainObject(value)) return Object.values(value).every((item) => isJsonValue(item));
565
- return false;
566
- }
567
- function encodeDefaultLiteralValue(value) {
568
- if (typeof value === "bigint") return {
569
- $type: "bigint",
570
- value: value.toString()
571
- };
572
- if (value instanceof Date) return value.toISOString();
573
- if (isJsonValue(value)) {
574
- if (isPlainObject(value) && "$type" in value) return {
575
- $type: "raw",
576
- value
577
- };
578
- return value;
579
- }
580
- throw new Error("Unsupported column default literal value: expected JSON-safe value, bigint, or Date.");
581
- }
582
- function encodeColumnDefault(defaultInput) {
583
- if (defaultInput.kind === "function") return {
584
- kind: "function",
585
- expression: defaultInput.expression
586
- };
587
- return {
588
- kind: "literal",
589
- value: encodeDefaultLiteralValue(defaultInput.value)
590
- };
591
- }
592
- function assertStorageSemantics(storage) {
593
- const semanticErrors = validateStorageSemantics(storage);
594
- if (semanticErrors.length > 0) throw new Error(`Contract semantic validation failed: ${semanticErrors.join("; ")}`);
595
- }
596
- function buildContractIR(state) {
597
- if (!state.target) throw new Error("target is required. Call .target() before .build()");
598
- const target = state.target;
599
- const storageTables = {};
600
- const executionDefaults = [];
601
- for (const tableName of Object.keys(state.tables)) {
602
- const tableState = state.tables[tableName];
603
- if (!tableState) continue;
604
- const columns = {};
605
- for (const columnName in tableState.columns) {
606
- const columnState = tableState.columns[columnName];
607
- if (!columnState) continue;
608
- const encodedDefault = columnState.default !== void 0 ? encodeColumnDefault(columnState.default) : void 0;
609
- columns[columnName] = {
610
- nativeType: columnState.nativeType,
611
- codecId: columnState.type,
612
- nullable: columnState.nullable ?? false,
613
- ...ifDefined("typeParams", columnState.typeParams),
614
- ...ifDefined("default", encodedDefault),
615
- ...ifDefined("typeRef", columnState.typeRef)
616
- };
617
- if ("executionDefault" in columnState && columnState.executionDefault) executionDefaults.push({
618
- ref: {
619
- table: tableName,
620
- column: columnName
621
- },
622
- onCreate: columnState.executionDefault
623
- });
624
- }
625
- storageTables[tableName] = {
626
- columns,
627
- uniques: (tableState.uniques ?? []).map((u) => ({
628
- columns: u.columns,
629
- ...u.name ? { name: u.name } : {}
630
- })),
631
- indexes: (tableState.indexes ?? []).map((i) => ({
632
- columns: i.columns,
633
- ...i.name ? { name: i.name } : {},
634
- ...i.using ? { using: i.using } : {},
635
- ...i.config ? { config: i.config } : {}
636
- })),
637
- foreignKeys: (tableState.foreignKeys ?? []).map((fk) => ({
638
- columns: fk.columns,
639
- references: fk.references,
640
- ...applyFkDefaults(fk, state.foreignKeyDefaults),
641
- ...fk.name ? { name: fk.name } : {},
642
- ...fk.onDelete !== void 0 ? { onDelete: fk.onDelete } : {},
643
- ...fk.onUpdate !== void 0 ? { onUpdate: fk.onUpdate } : {}
644
- })),
645
- ...tableState.primaryKey ? { primaryKey: {
646
- columns: tableState.primaryKey,
647
- ...tableState.primaryKeyName ? { name: tableState.primaryKeyName } : {}
648
- } } : {}
649
- };
650
- }
651
- const storage = {
652
- tables: storageTables,
653
- types: state.storageTypes ?? {}
654
- };
655
- const execution = executionDefaults.length > 0 ? { mutations: { defaults: executionDefaults.sort((a, b) => {
656
- const tableCompare = a.ref.table.localeCompare(b.ref.table);
657
- if (tableCompare !== 0) return tableCompare;
658
- return a.ref.column.localeCompare(b.ref.column);
659
- }) } } : void 0;
660
- const models = {};
661
- for (const modelName in state.models) {
662
- const modelState = state.models[modelName];
663
- if (!modelState) continue;
664
- const tableName = modelState.table;
665
- const tableState = state.tables[tableName];
666
- const tableColumns = tableState ? tableState.columns : {};
667
- const storageFields = {};
668
- const domainFields = {};
669
- for (const fieldName in modelState.fields) {
670
- const columnName = modelState.fields[fieldName];
671
- if (!columnName) continue;
672
- storageFields[fieldName] = { column: columnName };
673
- const column = tableColumns[columnName];
674
- if (column) domainFields[fieldName] = {
675
- codecId: column.type,
676
- nullable: column.nullable ?? false
677
- };
678
- }
679
- const modelRelations = {};
680
- if (modelState.relations) for (const relName in modelState.relations) {
681
- const rel$1 = modelState.relations[relName];
682
- if (!rel$1) continue;
683
- modelRelations[relName] = {
684
- to: rel$1.to,
685
- cardinality: rel$1.cardinality,
686
- on: {
687
- localFields: rel$1.on.parentCols,
688
- targetFields: rel$1.on.childCols
689
- }
690
- };
691
- }
692
- models[modelName] = {
693
- storage: {
694
- table: tableName,
695
- fields: storageFields
696
- },
697
- fields: domainFields,
698
- relations: modelRelations
699
- };
700
- }
701
- const extensionNamespaces = state.extensionNamespaces ?? [];
702
- const extensionPacks = { ...state.extensionPacks || {} };
703
- for (const namespace of extensionNamespaces) if (!Object.hasOwn(extensionPacks, namespace)) extensionPacks[namespace] = {};
704
- const contract = {
705
- schemaVersion: "1",
706
- target,
707
- targetFamily: "sql",
708
- storageHash: state.storageHash || "sha256:ts-builder-placeholder",
709
- models,
710
- roots: {},
711
- storage,
712
- ...execution ? { execution } : {},
713
- extensionPacks,
714
- capabilities: state.capabilities || {},
715
- meta: {},
716
- sources: {}
717
- };
718
- assertStorageSemantics(contract.storage);
719
- return contract;
720
- }
721
- function assertKnownTargetModel(modelsByName, sourceModelName, targetModelName, context) {
722
- const targetModel = modelsByName.get(targetModelName);
723
- if (!targetModel) throw new Error(`${context} on model "${sourceModelName}" references unknown model "${targetModelName}"`);
724
- return targetModel;
725
- }
726
- function assertTargetTableMatches(sourceModelName, targetModel, referencedTableName, context) {
727
- if (targetModel.tableName !== referencedTableName) throw new Error(`${context} on model "${sourceModelName}" references table "${referencedTableName}" but model "${targetModel.modelName}" maps to "${targetModel.tableName}"`);
728
- }
729
- function buildSqlContractFromSemanticDefinition(definition) {
730
- const modelsByName = new Map(definition.models.map((m) => [m.modelName, m]));
731
- const tables = {};
732
- for (const model$1 of definition.models) {
733
- const columns = {};
734
- for (const field$1 of model$1.fields) {
735
- if (field$1.executionDefault) {
736
- if (field$1.default !== void 0) throw new Error(`Field "${model$1.modelName}.${field$1.fieldName}" cannot define both default and executionDefault.`);
737
- if (field$1.nullable) throw new Error(`Field "${model$1.modelName}.${field$1.fieldName}" cannot be nullable when executionDefault is present.`);
738
- columns[field$1.columnName] = {
739
- name: field$1.columnName,
740
- type: field$1.descriptor.codecId,
741
- nativeType: field$1.descriptor.nativeType,
742
- nullable: false,
743
- ...ifDefined("typeParams", field$1.descriptor.typeParams),
744
- ...ifDefined("typeRef", field$1.descriptor.typeRef),
745
- executionDefault: field$1.executionDefault
746
- };
747
- continue;
748
- }
749
- columns[field$1.columnName] = {
750
- name: field$1.columnName,
751
- type: field$1.descriptor.codecId,
752
- nativeType: field$1.descriptor.nativeType,
753
- nullable: field$1.nullable,
754
- ...ifDefined("typeParams", field$1.descriptor.typeParams),
755
- ...ifDefined("typeRef", field$1.descriptor.typeRef),
756
- ...ifDefined("default", field$1.default)
757
- };
758
- }
759
- if (model$1.id) {
760
- const fieldsByColumnName = new Map(model$1.fields.map((field$1) => [field$1.columnName, field$1]));
761
- for (const columnName of model$1.id.columns) {
762
- const field$1 = fieldsByColumnName.get(columnName);
763
- if (field$1?.nullable) throw new Error(`Model "${model$1.modelName}" uses nullable field "${field$1.fieldName}" in its identity.`);
764
- }
765
- }
766
- const foreignKeys = (model$1.foreignKeys ?? []).map((fk) => {
767
- const targetModel = assertKnownTargetModel(modelsByName, model$1.modelName, fk.references.model, "Foreign key");
768
- assertTargetTableMatches(model$1.modelName, targetModel, fk.references.table, "Foreign key");
769
- return {
770
- columns: fk.columns,
771
- references: {
772
- table: fk.references.table,
773
- columns: fk.references.columns
774
- },
775
- ...ifDefined("name", fk.name),
776
- ...ifDefined("onDelete", fk.onDelete),
777
- ...ifDefined("onUpdate", fk.onUpdate),
778
- ...ifDefined("constraint", fk.constraint),
779
- ...ifDefined("index", fk.index)
780
- };
781
- });
782
- tables[model$1.tableName] = {
783
- name: model$1.tableName,
784
- columns,
785
- ...model$1.id ? { primaryKey: model$1.id.columns } : {},
786
- ...model$1.id?.name ? { primaryKeyName: model$1.id.name } : {},
787
- uniques: model$1.uniques ?? [],
788
- indexes: model$1.indexes ?? [],
789
- foreignKeys
790
- };
791
- }
792
- const modelStates = {};
793
- for (const model$1 of definition.models) {
794
- const fields = {};
795
- for (const field$1 of model$1.fields) fields[field$1.fieldName] = field$1.columnName;
796
- const relations = {};
797
- for (const relation of model$1.relations ?? []) {
798
- const targetModel = assertKnownTargetModel(modelsByName, model$1.modelName, relation.toModel, "Relation");
799
- assertTargetTableMatches(model$1.modelName, targetModel, relation.toTable, "Relation");
800
- if (relation.cardinality === "N:M" && !relation.through) throw new Error(`Relation "${model$1.modelName}.${relation.fieldName}" with cardinality "N:M" requires through metadata`);
801
- relations[relation.fieldName] = {
802
- to: relation.toModel,
803
- cardinality: relation.cardinality,
804
- on: {
805
- parentCols: relation.on.parentColumns,
806
- childCols: relation.on.childColumns
807
- },
808
- ...relation.through ? { through: {
809
- table: relation.through.table,
810
- parentCols: relation.through.parentColumns,
811
- childCols: relation.through.childColumns
812
- } } : void 0
813
- };
814
- }
815
- modelStates[model$1.modelName] = {
816
- name: model$1.modelName,
817
- table: model$1.tableName,
818
- fields,
819
- relations
820
- };
821
- }
822
- const extensionNamespaces = definition.extensionPacks ? Object.values(definition.extensionPacks).map((pack) => pack.id) : void 0;
823
- return buildContractIR({
824
- target: definition.target.targetId,
825
- tables,
826
- models: modelStates,
827
- ...ifDefined("storageTypes", definition.storageTypes),
828
- ...ifDefined("storageHash", definition.storageHash),
829
- ...ifDefined("extensionPacks", definition.extensionPacks),
830
- ...ifDefined("capabilities", definition.capabilities),
831
- ...ifDefined("foreignKeyDefaults", definition.foreignKeyDefaults),
832
- ...ifDefined("extensionNamespaces", extensionNamespaces)
833
- });
834
- }
835
-
836
- //#endregion
837
- //#region src/staged-contract-warnings.ts
818
+ //#region src/contract-warnings.ts
838
819
  function hasNamedModelToken(models, modelName) {
839
820
  return models[modelName]?.stageOne.modelName === modelName;
840
821
  }
@@ -872,11 +853,11 @@ function flushWarnings(warnings) {
872
853
  for (const message of warnings) process.emitWarning(message, { code: "PN_CONTRACT_TYPED_FALLBACK_AVAILABLE" });
873
854
  return;
874
855
  }
875
- process.emitWarning(`${warnings.length} staged contract references use string fallbacks where typed alternatives are available. Use named model tokens and typed storage type refs for autocomplete and type safety.
856
+ process.emitWarning(`${warnings.length} contract references use string fallbacks where typed alternatives are available. Use named model tokens and typed storage type refs for autocomplete and type safety.
876
857
  ` + warnings.map((w) => ` - ${w}`).join("\n"), { code: "PN_CONTRACT_TYPED_FALLBACK_AVAILABLE" });
877
858
  }
878
859
  function formatFallbackWarning(location, current, suggested) {
879
- return `Staged contract ${location} uses ${current}. Use ${suggested} when the named model token is available in the same contract to keep typed relation targets and model refs.`;
860
+ return `Contract ${location} uses ${current}. Use ${suggested} when the named model token is available in the same contract to keep typed relation targets and model refs.`;
880
861
  }
881
862
  function emitTypedNamedTypeFallbackWarnings(models, storageTypes) {
882
863
  const warnings = [];
@@ -887,7 +868,7 @@ function emitTypedNamedTypeFallbackWarnings(models, storageTypes) {
887
868
  const warningKey = `${modelName}.${fieldName}`;
888
869
  if (warnedFields.has(warningKey)) continue;
889
870
  warnedFields.add(warningKey);
890
- warnings.push(`Staged contract field "${modelName}.${fieldName}" uses field.namedType('${fieldState.typeRef}'). Use field.namedType(types.${fieldState.typeRef}) when the storage type is declared in the same contract to keep autocomplete and typed local refs.`);
871
+ warnings.push(`Contract field "${modelName}.${fieldName}" uses field.namedType('${fieldState.typeRef}'). Use field.namedType(types.${fieldState.typeRef}) when the storage type is declared in the same contract to keep autocomplete and typed local refs.`);
891
872
  }
892
873
  flushWarnings(warnings);
893
874
  }
@@ -930,7 +911,7 @@ function emitTypedCrossModelFallbackWarnings(collection) {
930
911
  }
931
912
 
932
913
  //#endregion
933
- //#region src/staged-contract-lowering.ts
914
+ //#region src/contract-lowering.ts
934
915
  function buildStorageTypeReverseLookup(storageTypes) {
935
916
  const lookup = /* @__PURE__ */ new Map();
936
917
  for (const [key, instance] of Object.entries(storageTypes)) lookup.set(instance, key);
@@ -954,7 +935,7 @@ function resolveFieldDescriptor(modelName, fieldName, fieldState, storageTypes,
954
935
  function mapFieldNamesToColumnNames(modelName, fieldNames, fieldToColumn) {
955
936
  return fieldNames.map((fieldName) => {
956
937
  const columnName = fieldToColumn[fieldName];
957
- if (!columnName) throw new Error(`Unknown field "${modelName}.${fieldName}" in staged contract definition`);
938
+ if (!columnName) throw new Error(`Unknown field "${modelName}.${fieldName}" in contract definition`);
958
939
  return columnName;
959
940
  });
960
941
  }
@@ -1128,7 +1109,7 @@ function lowerManyToManyRelation(relationName, relation, currentSpec, allSpecs)
1128
1109
  }
1129
1110
  };
1130
1111
  }
1131
- function resolveSemanticRelationNode(relationName, relation, currentSpec, allSpecs) {
1112
+ function resolveRelationNode(relationName, relation, currentSpec, allSpecs) {
1132
1113
  if (relation.kind === "belongsTo") return lowerBelongsToRelation(relationName, relation, currentSpec, allSpecs);
1133
1114
  if (relation.kind === "hasMany" || relation.kind === "hasOne") return lowerHasOwnershipRelation(relationName, relation, currentSpec, allSpecs);
1134
1115
  return lowerManyToManyRelation(relationName, relation, currentSpec, allSpecs);
@@ -1148,7 +1129,7 @@ function lowerForeignKeyNode(spec, targetSpec, foreignKey) {
1148
1129
  ...foreignKey.index !== void 0 ? { index: foreignKey.index } : {}
1149
1130
  };
1150
1131
  }
1151
- function resolveSemanticForeignKeyNodes(spec, allSpecs) {
1132
+ function resolveForeignKeyNodes(spec, allSpecs) {
1152
1133
  const relationForeignKeys = resolveRelationForeignKeys(spec, allSpecs).map((foreignKey) => {
1153
1134
  const targetSpec = allSpecs.get(foreignKey.targetModel);
1154
1135
  if (!targetSpec) throw new Error(`Foreign key on "${spec.modelName}" references unknown model "${foreignKey.targetModel}"`);
@@ -1161,7 +1142,7 @@ function resolveSemanticForeignKeyNodes(spec, allSpecs) {
1161
1142
  });
1162
1143
  return [...relationForeignKeys, ...sqlForeignKeys];
1163
1144
  }
1164
- function resolveSemanticModelNode(spec, allSpecs, storageTypes, storageTypeReverseLookup) {
1145
+ function resolveModelNode(spec, allSpecs, storageTypes, storageTypeReverseLookup) {
1165
1146
  const fields = [];
1166
1147
  for (const [fieldName, fieldBuilder] of Object.entries(spec.fieldBuilders)) {
1167
1148
  const fieldState = fieldBuilder.build();
@@ -1188,8 +1169,8 @@ function resolveSemanticModelNode(spec, allSpecs, storageTypes, storageTypeRever
1188
1169
  ...index.using ? { using: index.using } : {},
1189
1170
  ...index.config ? { config: index.config } : {}
1190
1171
  }));
1191
- const foreignKeys = resolveSemanticForeignKeyNodes(spec, allSpecs);
1192
- const relations = Object.entries(spec.relations).map(([relationName, relationBuilder]) => resolveSemanticRelationNode(relationName, relationBuilder.build(), spec, allSpecs));
1172
+ const foreignKeys = resolveForeignKeyNodes(spec, allSpecs);
1173
+ const relations = Object.entries(spec.relations).map(([relationName, relationBuilder]) => resolveRelationNode(relationName, relationBuilder.build(), spec, allSpecs));
1193
1174
  return {
1194
1175
  modelName: spec.modelName,
1195
1176
  tableName: spec.tableName,
@@ -1251,14 +1232,14 @@ function collectRuntimeModelSpecs(definition) {
1251
1232
  modelSpecs
1252
1233
  };
1253
1234
  }
1254
- function lowerSemanticModels(collection) {
1235
+ function lowerModels(collection) {
1255
1236
  emitTypedCrossModelFallbackWarnings(collection);
1256
1237
  const storageTypeReverseLookup = buildStorageTypeReverseLookup(collection.storageTypes);
1257
- return Array.from(collection.modelSpecs.values()).map((spec) => resolveSemanticModelNode(spec, collection.modelSpecs, collection.storageTypes, storageTypeReverseLookup));
1238
+ return Array.from(collection.modelSpecs.values()).map((spec) => resolveModelNode(spec, collection.modelSpecs, collection.storageTypes, storageTypeReverseLookup));
1258
1239
  }
1259
- function buildStagedSemanticContractDefinition(definition) {
1240
+ function buildContractDefinition(definition) {
1260
1241
  const collection = collectRuntimeModelSpecs(definition);
1261
- const models = lowerSemanticModels(collection);
1242
+ const models = lowerModels(collection);
1262
1243
  return {
1263
1244
  target: definition.target,
1264
1245
  ...definition.extensionPacks ? { extensionPacks: definition.extensionPacks } : {},
@@ -1272,122 +1253,36 @@ function buildStagedSemanticContractDefinition(definition) {
1272
1253
 
1273
1254
  //#endregion
1274
1255
  //#region src/contract-builder.ts
1275
- function buildStagedContract(definition) {
1276
- return buildSqlContractFromSemanticDefinition(buildStagedSemanticContractDefinition(definition));
1277
- }
1278
- var SqlContractBuilder = class SqlContractBuilder extends ContractBuilder {
1279
- /**
1280
- * This method is responsible for normalizing the contract IR by setting default values
1281
- * for all required fields:
1282
- * - `nullable`: defaults to `false` if not provided
1283
- * - `uniques`: defaults to `[]` (empty array)
1284
- * - `indexes`: defaults to `[]` (empty array)
1285
- * - `foreignKeys`: defaults to `[]` (empty array)
1286
- * - model `relations`: defaults to `{}` (empty object) when not populated by the builder
1287
- * - `nativeType`: required field set from column type descriptor when columns are defined
1288
- *
1289
- * The contract builder is the **only** place where normalization should occur.
1290
- * Validators, parsers, and emitters should assume the contract is already normalized.
1291
- *
1292
- * **Required**: Use column type descriptors (e.g., `int4Column`, `textColumn`) when defining columns.
1293
- * This ensures `nativeType` is set correctly at build time.
1294
- *
1295
- * @returns A normalized SqlContract with all required fields present
1296
- */
1297
- build() {
1298
- return buildContractIR(this.state);
1256
+ function validateTargetPackRef(family, target) {
1257
+ if (family.familyId !== "sql") throw new Error(`defineContract only accepts SQL family packs. Received family "${family.familyId}".`);
1258
+ if (target.familyId !== family.familyId) throw new Error(`target pack "${target.id}" targets family "${target.familyId}" but contract family is "${family.familyId}".`);
1259
+ }
1260
+ function validateExtensionPackRefs(target, extensionPacks) {
1261
+ if (!extensionPacks) return;
1262
+ for (const packRef of Object.values(extensionPacks)) {
1263
+ if (packRef.kind !== "extension") throw new Error(`defineContract only accepts extension pack refs in extensionPacks. Received kind "${packRef.kind}".`);
1264
+ if (packRef.familyId !== target.familyId) throw new Error(`extension pack "${packRef.id}" targets family "${packRef.familyId}" but contract target family is "${target.familyId}".`);
1265
+ if (packRef.targetId && packRef.targetId !== target.targetId) throw new Error(`extension pack "${packRef.id}" targets "${packRef.targetId}" but contract target is "${target.targetId}".`);
1299
1266
  }
1300
- target(packRef) {
1301
- return new SqlContractBuilder({
1302
- ...this.state,
1303
- target: packRef.targetId
1304
- });
1305
- }
1306
- extensionPacks(packs) {
1307
- if (!this.state.target) throw new Error("extensionPacks() requires target() to be called first");
1308
- const namespaces = new Set(this.state.extensionNamespaces ?? []);
1309
- const nextExtensionPacks = { ...this.state.extensionPacks ?? {} };
1310
- for (const [name, packRef] of Object.entries(packs)) {
1311
- if (!packRef) continue;
1312
- if (packRef.kind !== "extension") throw new Error(`extensionPacks() only accepts extension pack refs. Received kind "${packRef.kind}".`);
1313
- if (packRef.familyId !== "sql") throw new Error(`extension pack "${packRef.id}" targets family "${packRef.familyId}" but this builder targets "sql".`);
1314
- if (packRef.targetId && packRef.targetId !== this.state.target) throw new Error(`extension pack "${packRef.id}" targets "${packRef.targetId}" but builder target is "${this.state.target}".`);
1315
- namespaces.add(packRef.id);
1316
- nextExtensionPacks[name] = packRef;
1317
- }
1318
- return new SqlContractBuilder({
1319
- ...this.state,
1320
- extensionPacks: nextExtensionPacks,
1321
- extensionNamespaces: [...namespaces]
1322
- });
1323
- }
1324
- capabilities(capabilities) {
1325
- return new SqlContractBuilder({
1326
- ...this.state,
1327
- capabilities
1328
- });
1329
- }
1330
- storageHash(hash) {
1331
- return new SqlContractBuilder({
1332
- ...this.state,
1333
- storageHash: hash
1334
- });
1335
- }
1336
- table(name, callback) {
1337
- const tableBuilder = createTable(name);
1338
- const result = callback(tableBuilder);
1339
- const tableState = (result instanceof TableBuilder ? result : tableBuilder).build();
1340
- return new SqlContractBuilder({
1341
- ...this.state,
1342
- tables: {
1343
- ...this.state.tables,
1344
- [name]: tableState
1345
- }
1346
- });
1347
- }
1348
- model(name, table, callback) {
1349
- const modelBuilder = new ModelBuilder(name, table);
1350
- const result = callback(modelBuilder);
1351
- const modelState = (result instanceof ModelBuilder ? result : modelBuilder).build();
1352
- return new SqlContractBuilder({
1353
- ...this.state,
1354
- models: {
1355
- ...this.state.models,
1356
- [name]: modelState
1357
- }
1358
- });
1359
- }
1360
- foreignKeyDefaults(config) {
1361
- return new SqlContractBuilder({
1362
- ...this.state,
1363
- foreignKeyDefaults: config
1364
- });
1365
- }
1366
- storageType(name, typeInstance) {
1367
- return new SqlContractBuilder({
1368
- ...this.state,
1369
- storageTypes: {
1370
- ...this.state.storageTypes ?? {},
1371
- [name]: typeInstance
1372
- }
1373
- });
1374
- }
1375
- };
1267
+ }
1268
+ function buildContractFromDsl(definition) {
1269
+ validateTargetPackRef(definition.family, definition.target);
1270
+ validateExtensionPackRefs(definition.target, definition.extensionPacks);
1271
+ return buildSqlContractFromDefinition(buildContractDefinition(definition), definition.codecLookup);
1272
+ }
1376
1273
  function defineContract(definition, factory) {
1377
- if (definition && isStagedContractInput(definition)) {
1378
- if (factory) return buildStagedContract({
1379
- ...definition,
1380
- ...factory(createComposedAuthoringHelpers({
1381
- family: definition.family,
1382
- target: definition.target,
1383
- extensionPacks: definition.extensionPacks
1384
- }))
1385
- });
1386
- return buildStagedContract(definition);
1387
- }
1388
- return new SqlContractBuilder();
1274
+ if (!isContractInput(definition)) throw new TypeError("defineContract expects a contract definition object. Define your contract with defineContract({ family, target, models, ... }).");
1275
+ if (!factory) return buildContractFromDsl(definition);
1276
+ return buildContractFromDsl({
1277
+ ...definition,
1278
+ ...factory(createComposedAuthoringHelpers({
1279
+ family: definition.family,
1280
+ target: definition.target,
1281
+ extensionPacks: definition.extensionPacks
1282
+ }))
1283
+ });
1389
1284
  }
1390
1285
 
1391
1286
  //#endregion
1392
- export { buildSqlContractFromSemanticDefinition, defineContract, field, model, rel };
1287
+ export { buildSqlContractFromDefinition, defineContract, field, model, rel };
1393
1288
  //# sourceMappingURL=contract-builder.mjs.map