@prisma-next/sql-contract-ts 0.12.0 → 0.13.0-dev.10

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,9 @@
1
- import { t as buildSqlContractFromDefinition } from "./build-contract-BCYW3_wE.mjs";
1
+ import { t as buildSqlContractFromDefinition } from "./build-contract-HP3IjjLv.mjs";
2
2
  import { blindCast } from "@prisma-next/utils/casts";
3
+ import { ifDefined } from "@prisma-next/utils/defined";
3
4
  import { isColumnDefault } from "@prisma-next/contract/types";
4
5
  import { createEntityHelpersFromNamespace } from "@prisma-next/contract-authoring";
5
6
  import { isPostgresEnumStorageEntry, toStorageTypeInstance } from "@prisma-next/sql-contract/types";
6
- import { ifDefined } from "@prisma-next/utils/defined";
7
7
  import { assertNoCrossRegistryCollisions, instantiateAuthoringFieldPreset, instantiateAuthoringTypeConstructor, isAuthoringEntityTypeDescriptor, isAuthoringFieldPresetDescriptor, isAuthoringTypeConstructorDescriptor, mergeAuthoringNamespaces, validateAuthoringHelperArguments } from "@prisma-next/framework-components/authoring";
8
8
  //#region src/authoring-helper-runtime.ts
9
9
  function isNamedConstraintOptionsLike(value) {
@@ -79,6 +79,71 @@ function createFieldHelpersFromNamespace(namespace, createLeafHelper, path = [])
79
79
  return helpers;
80
80
  }
81
81
  //#endregion
82
+ //#region src/enum-type.ts
83
+ function member(name, value) {
84
+ return {
85
+ name,
86
+ value: blindCast(value ?? name)
87
+ };
88
+ }
89
+ /**
90
+ * Internal brand that identifies an EnumTypeHandle in the lowering pipeline.
91
+ * Not exported — callers only interact with `EnumTypeHandle`.
92
+ */
93
+ const ENUM_TYPE_HANDLE_BRAND = Symbol("EnumTypeHandle");
94
+ function enumType(name, codec, ...members) {
95
+ if (members.length === 0) throw new Error(`enumType("${name}"): must have at least one member.`);
96
+ const seenNames = /* @__PURE__ */ new Set();
97
+ const seenValues = /* @__PURE__ */ new Set();
98
+ for (const m of members) {
99
+ if (seenNames.has(m.name)) throw new Error(`enumType("${name}"): duplicate member name "${m.name}". Member names must be unique.`);
100
+ seenNames.add(m.name);
101
+ const loweredValue = String(m.value);
102
+ if (seenValues.has(loweredValue)) throw new Error(`enumType("${name}"): duplicate member value "${loweredValue}". Member values must be unique.`);
103
+ seenValues.add(loweredValue);
104
+ }
105
+ const values = Object.freeze(members.map((m) => m.value));
106
+ const names = Object.freeze(members.map((m) => m.name));
107
+ const enumMembers = Object.freeze(members.map((m) => ({
108
+ name: m.name,
109
+ value: m.value
110
+ })));
111
+ const membersAccessor = Object.freeze(Object.fromEntries(members.map((m) => [m.name, m.value])));
112
+ const valueSet = new Set(values);
113
+ const valueToName = new Map(members.map((m) => [m.value, m.name]));
114
+ const valueToOrdinal = new Map(values.map((v, i) => [v, i]));
115
+ return {
116
+ [ENUM_TYPE_HANDLE_BRAND]: true,
117
+ enumName: name,
118
+ codecId: codec.codecId,
119
+ nativeType: codec.nativeType,
120
+ enumMembers,
121
+ values,
122
+ names,
123
+ members: membersAccessor,
124
+ has: (v) => valueSet.has(v),
125
+ nameOf: (v) => valueToName.get(v),
126
+ ordinalOf: (v) => valueToOrdinal.get(v) ?? -1
127
+ };
128
+ }
129
+ /**
130
+ * Bind `enumType` to a target's codec typemap. The returned function is the
131
+ * same runtime `enumType`, retyped so member values are constrained to the
132
+ * codec's input type. Target packages call this with their pack's
133
+ * `ExtractCodecTypesFromPack<Pack>` to expose a codec-aware `enumType`.
134
+ */
135
+ function bindEnumType() {
136
+ return enumType;
137
+ }
138
+ /**
139
+ * Returns true when the value is an `EnumTypeHandle` produced by
140
+ * `enumType()`. Used in the lowering pipeline to detect enum handles
141
+ * in field state without importing the BRAND symbol at every call site.
142
+ */
143
+ function isEnumTypeHandle(value) {
144
+ return typeof value === "object" && value !== null && Reflect.get(value, ENUM_TYPE_HANDLE_BRAND) === true;
145
+ }
146
+ //#endregion
82
147
  //#region src/contract-dsl.ts
83
148
  function toColumnDefault(value) {
84
149
  if (isColumnDefault(value)) return value;
@@ -92,6 +157,15 @@ var ScalarFieldBuilder = class ScalarFieldBuilder {
92
157
  constructor(state) {
93
158
  this.state = state;
94
159
  }
160
+ /**
161
+ * Returns the physical column name when `.column(name)` was called, or
162
+ * `undefined` when the field uses the default (logical field name) mapping.
163
+ * Used by cross-space FK lowering to stamp the physical column name onto
164
+ * `TargetFieldRef.columnName` so FK target columns are resolved correctly.
165
+ */
166
+ get physicalColumnName() {
167
+ return this.state.columnName;
168
+ }
95
169
  optional() {
96
170
  return new ScalarFieldBuilder({
97
171
  ...this.state,
@@ -166,6 +240,11 @@ function generatedField(spec) {
166
240
  });
167
241
  }
168
242
  function namedTypeField(typeRef) {
243
+ if (isEnumTypeHandle(typeRef)) return new ScalarFieldBuilder({
244
+ kind: "scalar",
245
+ typeRef,
246
+ nullable: false
247
+ });
169
248
  return new ScalarFieldBuilder({
170
249
  kind: "scalar",
171
250
  typeRef,
@@ -208,10 +287,16 @@ function normalizeTargetFieldRefInput(input) {
208
287
  const [first] = refs;
209
288
  if (!first) throw new Error("Expected at least one target ref");
210
289
  if (refs.some((ref) => ref.modelName !== first.modelName)) throw new Error("All target refs in a foreign key must point to the same model");
290
+ if (refs.some((ref) => ref.spaceId !== first.spaceId)) throw new Error(`All target refs in a compound foreign key must share the same spaceId (found mismatch: "${first.spaceId ?? "<local>"}" vs "${refs.find((r) => r.spaceId !== first.spaceId)?.spaceId ?? "<local>"}")`);
291
+ if (refs.some((ref) => ref.namespaceId !== first.namespaceId)) throw new Error("All target refs in a compound foreign key must share the same namespaceId (found mismatch)");
292
+ if (refs.some((ref) => ref.tableName !== first.tableName)) throw new Error("All target refs in a compound foreign key must share the same tableName (found mismatch)");
211
293
  return {
212
294
  modelName: first.modelName,
213
- fieldNames: refs.map((ref) => ref.fieldName),
214
- source: refs.some((ref) => ref.source === "string") ? "string" : "token"
295
+ fieldNames: refs.map((ref) => ref.columnName ?? ref.fieldName),
296
+ source: refs.some((ref) => ref.source === "string") ? "string" : "token",
297
+ spaceId: first.spaceId,
298
+ namespaceId: first.namespaceId,
299
+ tableName: first.tableName
215
300
  };
216
301
  }
217
302
  function createConstraintsDsl() {
@@ -254,6 +339,9 @@ function createConstraintsDsl() {
254
339
  targetModel: normalizedTarget.modelName,
255
340
  targetFields: normalizedTarget.fieldNames,
256
341
  targetSource: normalizedTarget.source,
342
+ ...normalizedTarget.spaceId !== void 0 ? { targetSpaceId: normalizedTarget.spaceId } : {},
343
+ ...normalizedTarget.namespaceId !== void 0 ? { targetNamespaceId: normalizedTarget.namespaceId } : {},
344
+ ...normalizedTarget.tableName !== void 0 ? { targetTableName: normalizedTarget.tableName } : {},
257
345
  ...options?.name ? { name: options.name } : {},
258
346
  ...options?.onDelete ? { onDelete: options.onDelete } : {},
259
347
  ...options?.onUpdate ? { onUpdate: options.onUpdate } : {},
@@ -277,14 +365,23 @@ function createFieldRefs(fields) {
277
365
  };
278
366
  return refs;
279
367
  }
280
- function createModelTokenRefs(modelName, fields) {
368
+ function createModelTokenRefs(modelName, fields, crossSpaceCoordinate) {
281
369
  const refs = {};
282
- for (const fieldName of Object.keys(fields)) refs[fieldName] = {
283
- kind: "targetFieldRef",
284
- source: "token",
285
- modelName,
286
- fieldName
287
- };
370
+ for (const [fieldName, fieldBuilder] of Object.entries(fields)) {
371
+ const physicalColumn = crossSpaceCoordinate !== void 0 ? fieldBuilder.physicalColumnName : void 0;
372
+ refs[fieldName] = {
373
+ kind: "targetFieldRef",
374
+ source: "token",
375
+ modelName,
376
+ fieldName,
377
+ ...crossSpaceCoordinate !== void 0 ? {
378
+ spaceId: crossSpaceCoordinate.spaceId,
379
+ ...crossSpaceCoordinate.namespaceId !== void 0 ? { namespaceId: crossSpaceCoordinate.namespaceId } : {},
380
+ ...crossSpaceCoordinate.tableName !== void 0 ? { tableName: crossSpaceCoordinate.tableName } : {},
381
+ ...physicalColumn !== void 0 ? { columnName: physicalColumn } : {}
382
+ } : {}
383
+ };
384
+ }
288
385
  return refs;
289
386
  }
290
387
  function buildStageSpec(stageInput, context) {
@@ -316,12 +413,21 @@ var ContractModelBuilder = class ContractModelBuilder {
316
413
  stageOne;
317
414
  attributesFactory;
318
415
  sqlFactory;
416
+ spaceId;
417
+ tableName;
319
418
  refs;
320
- constructor(stageOne, attributesFactory, sqlFactory) {
419
+ constructor(stageOne, attributesFactory, sqlFactory, spaceId, tableName) {
321
420
  this.stageOne = stageOne;
322
421
  this.attributesFactory = attributesFactory;
323
422
  this.sqlFactory = sqlFactory;
324
- this.refs = stageOne.modelName ? createModelTokenRefs(stageOne.modelName, stageOne.fields) : void 0;
423
+ this.spaceId = spaceId;
424
+ this.tableName = tableName;
425
+ const crossSpaceCoordinate = spaceId !== void 0 ? {
426
+ spaceId,
427
+ ...stageOne.namespace !== void 0 ? { namespaceId: stageOne.namespace } : {},
428
+ ...tableName !== void 0 ? { tableName } : {}
429
+ } : void 0;
430
+ this.refs = blindCast(stageOne.modelName ? createModelTokenRefs(stageOne.modelName, stageOne.fields, crossSpaceCoordinate) : void 0);
325
431
  }
326
432
  ref(fieldName) {
327
433
  const modelName = this.stageOne.modelName;
@@ -342,13 +448,14 @@ var ContractModelBuilder = class ContractModelBuilder {
342
448
  ...this.stageOne.relations,
343
449
  ...relations
344
450
  }
345
- }, this.attributesFactory, this.sqlFactory);
451
+ }, this.attributesFactory, this.sqlFactory, this.spaceId, this.tableName);
346
452
  }
347
453
  attributes(specOrFactory) {
348
- return new ContractModelBuilder(this.stageOne, specOrFactory, this.sqlFactory);
454
+ return new ContractModelBuilder(this.stageOne, specOrFactory, this.sqlFactory, this.spaceId, this.tableName);
349
455
  }
350
456
  sql(specOrFactory) {
351
- return new ContractModelBuilder(this.stageOne, this.attributesFactory, specOrFactory);
457
+ const nextTableName = typeof specOrFactory !== "function" ? specOrFactory.table : this.tableName;
458
+ return blindCast(new ContractModelBuilder(this.stageOne, this.attributesFactory, specOrFactory, this.spaceId, nextTableName));
352
459
  }
353
460
  buildAttributesSpec() {
354
461
  if (!this.attributesFactory) return;
@@ -400,12 +507,47 @@ function model(modelNameOrInput, maybeInput) {
400
507
  relations: input.relations ?? {}
401
508
  });
402
509
  }
510
+ /**
511
+ * Factory for building a standalone branded extension model handle.
512
+ *
513
+ * Use this instead of `new ContractModelBuilder(…)` when constructing handles
514
+ * for models that live in a foreign contract space (e.g. a Supabase extension
515
+ * model referenced by a user's contract). The `spaceId` brands the returned
516
+ * handle so `refs.<field>.spaceId` carries the foreign space identifier.
517
+ *
518
+ * @param name - The domain model name as declared in the foreign contract
519
+ * (e.g. `'AuthUser'`, not a bare table alias like `'User'`).
520
+ * @param input.namespace - The namespace within the foreign space (e.g. `'auth'`).
521
+ * @param input.fields - Field definitions (use `field.column(…)`).
522
+ * @param input.table - The physical table name in the foreign schema.
523
+ * @param spaceId - The extension space identifier (e.g. `'supabase'`).
524
+ */
525
+ function extensionModel(name, input, spaceId) {
526
+ return new ContractModelBuilder({
527
+ modelName: name,
528
+ namespace: input.namespace,
529
+ fields: input.fields,
530
+ relations: {}
531
+ }, void 0, void 0, spaceId, input.table);
532
+ }
533
+ function isCrossSpaceHandle(value) {
534
+ if (typeof value !== "object" || value === null) return false;
535
+ const rec = blindCast(value);
536
+ return typeof rec["spaceId"] === "string" && typeof rec["stageOne"] === "object" && rec["stageOne"] !== null;
537
+ }
403
538
  function belongsTo(toModel, options) {
539
+ const resolvedModel = typeof toModel === "function" ? toModel() : toModel;
540
+ const crossSpaceCoordinate = isCrossSpaceHandle(resolvedModel) ? {
541
+ spaceId: resolvedModel.spaceId,
542
+ ...resolvedModel.tableName !== void 0 ? { tableName: resolvedModel.tableName } : {},
543
+ ...resolvedModel.stageOne.namespace !== void 0 ? { namespaceId: resolvedModel.stageOne.namespace } : {}
544
+ } : void 0;
404
545
  return new RelationBuilder({
405
546
  kind: "belongsTo",
406
547
  toModel: normalizeRelationModelSource(toModel),
407
548
  from: options.from,
408
- to: options.to
549
+ to: options.to,
550
+ ...crossSpaceCoordinate !== void 0 ? crossSpaceCoordinate : {}
409
551
  });
410
552
  }
411
553
  function hasMany(toModel, options) {
@@ -677,6 +819,10 @@ function buildStorageTypeReverseLookup(storageTypes) {
677
819
  function resolveFieldDescriptor(modelName, fieldName, fieldState, storageTypes, storageTypeReverseLookup) {
678
820
  if ("descriptor" in fieldState && fieldState.descriptor) return fieldState.descriptor;
679
821
  if ("typeRef" in fieldState && fieldState.typeRef) {
822
+ if (isEnumTypeHandle(fieldState.typeRef)) return {
823
+ codecId: fieldState.typeRef.codecId,
824
+ nativeType: fieldState.typeRef.nativeType
825
+ };
680
826
  const typeRef = typeof fieldState.typeRef === "string" ? fieldState.typeRef : storageTypeReverseLookup.get(fieldState.typeRef);
681
827
  if (!typeRef) throw new Error(`Field "${modelName}.${fieldName}" references a storage type instance that is not present in definition.types`);
682
828
  const referencedType = storageTypes[typeRef];
@@ -751,6 +897,33 @@ function resolveRelationForeignKeys(spec, allSpecs) {
751
897
  const relation = relationBuilder.build();
752
898
  if (relation.kind !== "belongsTo" || !relation.sql?.fk) continue;
753
899
  const targetModelName = resolveRelationModelName(relation.toModel);
900
+ if (relation.spaceId !== void 0) {
901
+ const fields = normalizeRelationFieldNames(relation.from);
902
+ const targetFields = normalizeRelationFieldNames(relation.to);
903
+ assertRelationFieldArity({
904
+ modelName: spec.modelName,
905
+ relationName,
906
+ leftLabel: "source",
907
+ leftFields: fields,
908
+ rightLabel: "target",
909
+ rightFields: targetFields
910
+ });
911
+ foreignKeys.push({
912
+ kind: "fk",
913
+ fields,
914
+ targetModel: targetModelName,
915
+ targetFields,
916
+ targetSpaceId: relation.spaceId,
917
+ ...relation.namespaceId !== void 0 ? { targetNamespaceId: relation.namespaceId } : {},
918
+ ...relation.tableName !== void 0 ? { targetTableName: relation.tableName } : {},
919
+ ...relation.sql.fk.name ? { name: relation.sql.fk.name } : {},
920
+ ...relation.sql.fk.onDelete ? { onDelete: relation.sql.fk.onDelete } : {},
921
+ ...relation.sql.fk.onUpdate ? { onUpdate: relation.sql.fk.onUpdate } : {},
922
+ ...relation.sql.fk.constraint !== void 0 ? { constraint: relation.sql.fk.constraint } : {},
923
+ ...relation.sql.fk.index !== void 0 ? { index: relation.sql.fk.index } : {}
924
+ });
925
+ continue;
926
+ }
754
927
  if (!allSpecs.has(targetModelName)) throw new Error(`Relation "${spec.modelName}.${relationName}" references unknown model "${targetModelName}"`);
755
928
  const fields = normalizeRelationFieldNames(relation.from);
756
929
  const targetFields = normalizeRelationFieldNames(relation.to);
@@ -782,10 +955,8 @@ function resolveRelationAnchorFields(spec) {
782
955
  if ("id" in spec.fieldToColumn) return ["id"];
783
956
  throw new Error(`Model "${spec.modelName}" needs an explicit id or an "id" field to anchor non-owning relations`);
784
957
  }
785
- function lowerBelongsToRelation(relationName, relation, currentSpec, allSpecs) {
958
+ function lowerBelongsToRelation(relationName, relation, currentSpec, allSpecs, extensionPacks) {
786
959
  const targetModelName = resolveRelationModelName(relation.toModel);
787
- const targetSpec = allSpecs.get(targetModelName);
788
- if (!targetSpec) throw new Error(`Relation "${currentSpec.modelName}.${relationName}" references unknown model "${targetModelName}"`);
789
960
  const fromFields = normalizeRelationFieldNames(relation.from);
790
961
  const toFields = normalizeRelationFieldNames(relation.to);
791
962
  assertRelationFieldArity({
@@ -796,6 +967,27 @@ function lowerBelongsToRelation(relationName, relation, currentSpec, allSpecs) {
796
967
  rightLabel: "target",
797
968
  rightFields: toFields
798
969
  });
970
+ if (relation.spaceId !== void 0) {
971
+ assertKnownExtensionPack(extensionPacks, relation.spaceId, `Relation "${currentSpec.modelName}.${relationName}"`);
972
+ const targetTable = relation.tableName ?? targetModelName.toLowerCase();
973
+ const parentColumns = mapFieldNamesToColumnNames(currentSpec.modelName, fromFields, currentSpec.fieldToColumn);
974
+ return {
975
+ fieldName: relationName,
976
+ toModel: targetModelName,
977
+ toTable: targetTable,
978
+ cardinality: "N:1",
979
+ spaceId: relation.spaceId,
980
+ ...relation.namespaceId !== void 0 ? { namespaceId: relation.namespaceId } : {},
981
+ on: {
982
+ parentTable: currentSpec.tableName,
983
+ parentColumns,
984
+ childTable: targetTable,
985
+ childColumns: toFields
986
+ }
987
+ };
988
+ }
989
+ const targetSpec = allSpecs.get(targetModelName);
990
+ if (!targetSpec) throw new Error(`Relation "${currentSpec.modelName}.${relationName}" references unknown model "${targetModelName}"`);
799
991
  return {
800
992
  fieldName: relationName,
801
993
  toModel: targetModelName,
@@ -866,12 +1058,12 @@ function lowerManyToManyRelation(relationName, relation, currentSpec, allSpecs)
866
1058
  }
867
1059
  };
868
1060
  }
869
- function resolveRelationNode(relationName, relation, currentSpec, allSpecs) {
870
- if (relation.kind === "belongsTo") return lowerBelongsToRelation(relationName, relation, currentSpec, allSpecs);
1061
+ function resolveRelationNode(relationName, relation, currentSpec, allSpecs, extensionPacks) {
1062
+ if (relation.kind === "belongsTo") return lowerBelongsToRelation(relationName, relation, currentSpec, allSpecs, extensionPacks);
871
1063
  if (relation.kind === "hasMany" || relation.kind === "hasOne") return lowerHasOwnershipRelation(relationName, relation, currentSpec, allSpecs);
872
1064
  return lowerManyToManyRelation(relationName, relation, currentSpec, allSpecs);
873
1065
  }
874
- function lowerForeignKeyNode(spec, targetSpec, foreignKey) {
1066
+ function lowerLocalForeignKeyNode(spec, targetSpec, foreignKey) {
875
1067
  return {
876
1068
  columns: mapFieldNamesToColumnNames(spec.modelName, foreignKey.fields, spec.fieldToColumn),
877
1069
  references: {
@@ -886,33 +1078,70 @@ function lowerForeignKeyNode(spec, targetSpec, foreignKey) {
886
1078
  ...foreignKey.index !== void 0 ? { index: foreignKey.index } : {}
887
1079
  };
888
1080
  }
889
- function resolveForeignKeyNodes(spec, allSpecs) {
1081
+ function lowerCrossSpaceForeignKeyNode(spec, foreignKey) {
1082
+ return {
1083
+ columns: mapFieldNamesToColumnNames(spec.modelName, foreignKey.fields, spec.fieldToColumn),
1084
+ references: {
1085
+ model: foreignKey.targetModel,
1086
+ table: foreignKey.targetTableName ?? foreignKey.targetModel.toLowerCase(),
1087
+ columns: foreignKey.targetFields,
1088
+ ...foreignKey.targetNamespaceId !== void 0 ? { namespaceId: foreignKey.targetNamespaceId } : {},
1089
+ spaceId: foreignKey.targetSpaceId
1090
+ },
1091
+ ...foreignKey.name ? { name: foreignKey.name } : {},
1092
+ ...foreignKey.onDelete ? { onDelete: foreignKey.onDelete } : {},
1093
+ ...foreignKey.onUpdate ? { onUpdate: foreignKey.onUpdate } : {},
1094
+ ...foreignKey.constraint !== void 0 ? { constraint: foreignKey.constraint } : {},
1095
+ ...foreignKey.index !== void 0 ? { index: foreignKey.index } : {}
1096
+ };
1097
+ }
1098
+ function assertKnownExtensionPack(extensionPacks, spaceId, context) {
1099
+ if (extensionPacks !== void 0 && Object.hasOwn(extensionPacks, spaceId)) return;
1100
+ throw new Error(`${context} references contract space "${spaceId}" but "${spaceId}" is not declared in extensionPacks. Add the pack to extensionPacks.`);
1101
+ }
1102
+ function resolveForeignKeyNodes(spec, allSpecs, extensionPacks) {
890
1103
  const relationForeignKeys = resolveRelationForeignKeys(spec, allSpecs).map((foreignKey) => {
1104
+ if (foreignKey.targetSpaceId !== void 0) {
1105
+ assertKnownExtensionPack(extensionPacks, foreignKey.targetSpaceId, `Relation-derived foreign key on "${spec.modelName}"`);
1106
+ return lowerCrossSpaceForeignKeyNode(spec, {
1107
+ ...foreignKey,
1108
+ targetSpaceId: foreignKey.targetSpaceId
1109
+ });
1110
+ }
891
1111
  const targetSpec = allSpecs.get(foreignKey.targetModel);
892
1112
  if (!targetSpec) throw new Error(`Foreign key on "${spec.modelName}" references unknown model "${foreignKey.targetModel}"`);
893
- return lowerForeignKeyNode(spec, targetSpec, foreignKey);
1113
+ return lowerLocalForeignKeyNode(spec, targetSpec, foreignKey);
894
1114
  });
895
1115
  const sqlForeignKeys = (spec.sqlSpec?.foreignKeys ?? []).map((foreignKey) => {
1116
+ if (foreignKey.targetSpaceId !== void 0) {
1117
+ assertKnownExtensionPack(extensionPacks, foreignKey.targetSpaceId, `Foreign key on "${spec.modelName}"`);
1118
+ return lowerCrossSpaceForeignKeyNode(spec, {
1119
+ ...foreignKey,
1120
+ targetSpaceId: foreignKey.targetSpaceId
1121
+ });
1122
+ }
896
1123
  const targetSpec = allSpecs.get(foreignKey.targetModel);
897
1124
  if (!targetSpec) throw new Error(`Foreign key on "${spec.modelName}" references unknown model "${foreignKey.targetModel}"`);
898
- return lowerForeignKeyNode(spec, targetSpec, foreignKey);
1125
+ return lowerLocalForeignKeyNode(spec, targetSpec, foreignKey);
899
1126
  });
900
1127
  return [...relationForeignKeys, ...sqlForeignKeys];
901
1128
  }
902
- function resolveModelNode(spec, allSpecs, storageTypes, storageTypeReverseLookup) {
1129
+ function resolveModelNode(spec, allSpecs, storageTypes, storageTypeReverseLookup, extensionPacks) {
903
1130
  const fields = [];
904
1131
  for (const [fieldName, fieldBuilder] of Object.entries(spec.fieldBuilders)) {
905
1132
  const fieldState = fieldBuilder.build();
906
1133
  const descriptor = resolveFieldDescriptor(spec.modelName, fieldName, fieldState, storageTypes, storageTypeReverseLookup);
907
1134
  const columnName = spec.fieldToColumn[fieldName];
908
1135
  if (!columnName) throw new Error(`Column name resolution failed for "${spec.modelName}.${fieldName}"`);
1136
+ const enumHandle = "typeRef" in fieldState && isEnumTypeHandle(fieldState.typeRef) ? fieldState.typeRef : void 0;
909
1137
  fields.push({
910
1138
  fieldName,
911
1139
  columnName,
912
1140
  descriptor,
913
1141
  nullable: fieldState.nullable,
914
1142
  ...fieldState.default ? { default: fieldState.default } : {},
915
- ...fieldState.executionDefaults ? { executionDefaults: fieldState.executionDefaults } : {}
1143
+ ...fieldState.executionDefaults ? { executionDefaults: fieldState.executionDefaults } : {},
1144
+ ...enumHandle !== void 0 ? { enumTypeHandle: enumHandle } : {}
916
1145
  });
917
1146
  }
918
1147
  const { idConstraint } = spec;
@@ -926,8 +1155,8 @@ function resolveModelNode(spec, allSpecs, storageTypes, storageTypeReverseLookup
926
1155
  ...ifDefined("type", index.type),
927
1156
  ...ifDefined("options", index.options)
928
1157
  }));
929
- const foreignKeys = resolveForeignKeyNodes(spec, allSpecs);
930
- const relations = Object.entries(spec.relations).map(([relationName, relationBuilder]) => resolveRelationNode(relationName, relationBuilder.build(), spec, allSpecs));
1158
+ const foreignKeys = resolveForeignKeyNodes(spec, allSpecs, extensionPacks);
1159
+ const relations = Object.entries(spec.relations).map(([relationName, relationBuilder]) => resolveRelationNode(relationName, relationBuilder.build(), spec, allSpecs, extensionPacks));
931
1160
  return {
932
1161
  modelName: spec.modelName,
933
1162
  tableName: spec.tableName,
@@ -940,7 +1169,8 @@ function resolveModelNode(spec, allSpecs, storageTypes, storageTypeReverseLookup
940
1169
  ...uniques.length > 0 ? { uniques } : {},
941
1170
  ...indexes.length > 0 ? { indexes } : {},
942
1171
  ...foreignKeys.length > 0 ? { foreignKeys } : {},
943
- ...relations.length > 0 ? { relations } : {}
1172
+ ...relations.length > 0 ? { relations } : {},
1173
+ ...ifDefined("control", spec.sqlSpec?.control)
944
1174
  };
945
1175
  }
946
1176
  function collectRuntimeModelSpecs(definition) {
@@ -991,22 +1221,24 @@ function collectRuntimeModelSpecs(definition) {
991
1221
  modelSpecs
992
1222
  };
993
1223
  }
994
- function lowerModels(collection) {
1224
+ function lowerModels(collection, extensionPacks) {
995
1225
  emitTypedCrossModelFallbackWarnings(collection);
996
1226
  const storageTypeReverseLookup = buildStorageTypeReverseLookup(collection.storageTypes);
997
- return Array.from(collection.modelSpecs.values()).map((spec) => resolveModelNode(spec, collection.modelSpecs, collection.storageTypes, storageTypeReverseLookup));
1227
+ return Array.from(collection.modelSpecs.values()).map((spec) => resolveModelNode(spec, collection.modelSpecs, collection.storageTypes, storageTypeReverseLookup, extensionPacks));
998
1228
  }
999
1229
  function buildContractDefinition(definition) {
1000
1230
  const collection = collectRuntimeModelSpecs(definition);
1001
- const models = lowerModels(collection);
1231
+ const models = lowerModels(collection, definition.extensionPacks);
1002
1232
  return {
1003
1233
  target: definition.target,
1234
+ ...ifDefined("defaultControlPolicy", definition.defaultControlPolicy),
1004
1235
  ...definition.extensionPacks ? { extensionPacks: definition.extensionPacks } : {},
1005
1236
  ...definition.storageHash ? { storageHash: definition.storageHash } : {},
1006
1237
  ...definition.foreignKeyDefaults ? { foreignKeyDefaults: definition.foreignKeyDefaults } : {},
1007
1238
  ...Object.keys(collection.storageTypes).length > 0 ? { storageTypes: collection.storageTypes } : {},
1008
1239
  ...definition.namespaces ? { namespaces: definition.namespaces } : {},
1009
1240
  ...definition.createNamespace ? { createNamespace: definition.createNamespace } : {},
1241
+ ...definition.enums && Object.keys(definition.enums).length > 0 ? { enums: definition.enums } : {},
1010
1242
  models
1011
1243
  };
1012
1244
  }
@@ -1094,19 +1326,38 @@ function buildContractFromDsl(definition) {
1094
1326
  validatePerModelNamespaces(definition.target, definition.namespaces, definition.models ?? {});
1095
1327
  return blindCast(buildSqlContractFromDefinition(buildContractDefinition(definition), definition.codecLookup));
1096
1328
  }
1097
- function defineContract(definition, factory) {
1098
- if (!isContractInput(definition)) throw new TypeError("defineContract expects a contract definition object. Define your contract with defineContract({ family, target, models, ... }).");
1099
- if (!factory) return buildContractFromDsl(definition);
1100
- return buildContractFromDsl({
1329
+ /** Implementation. */
1330
+ function buildBoundContract(family, target, definition, factory) {
1331
+ const full = {
1101
1332
  ...definition,
1102
- ...factory(createComposedAuthoringHelpers({
1103
- family: definition.family,
1104
- target: definition.target,
1333
+ family,
1334
+ target
1335
+ };
1336
+ if (factory !== void 0) {
1337
+ const built = factory(createComposedAuthoringHelpers({
1338
+ family,
1339
+ target,
1105
1340
  extensionPacks: definition.extensionPacks
1106
- }))
1107
- });
1341
+ }));
1342
+ const mergedEnums = {
1343
+ ...definition.enums ?? {},
1344
+ ...built.enums
1345
+ };
1346
+ return buildContractFromDsl({
1347
+ ...full,
1348
+ ...ifDefined("types", built.types),
1349
+ ...ifDefined("models", built.models),
1350
+ ...ifDefined("enums", Object.keys(mergedEnums).length > 0 ? mergedEnums : void 0)
1351
+ });
1352
+ }
1353
+ return buildContractFromDsl(full);
1354
+ }
1355
+ function defineContract(definition, factory) {
1356
+ if (!isContractInput(definition)) throw new TypeError("defineContract expects a contract definition object. Define your contract with defineContract({ family, target, models, ... }).");
1357
+ if (factory !== void 0) return buildBoundContract(definition.family, definition.target, definition, factory);
1358
+ return buildBoundContract(definition.family, definition.target, definition);
1108
1359
  }
1109
1360
  //#endregion
1110
- export { buildSqlContractFromDefinition, defineContract, field, model, rel };
1361
+ export { bindEnumType, buildBoundContract, buildSqlContractFromDefinition, defineContract, enumType, extensionModel, field, member, model, rel };
1111
1362
 
1112
1363
  //# sourceMappingURL=contract-builder.mjs.map