@prisma-next/family-sql 0.12.0-dev.4 → 0.12.0-dev.41

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 (47) hide show
  1. package/dist/control-adapter-uYnmu04p.d.mts +181 -0
  2. package/dist/control-adapter-uYnmu04p.d.mts.map +1 -0
  3. package/dist/control-adapter.d.mts +2 -109
  4. package/dist/control.d.mts +118 -4
  5. package/dist/control.d.mts.map +1 -1
  6. package/dist/control.mjs +226 -40
  7. package/dist/control.mjs.map +1 -1
  8. package/dist/ir.d.mts +2 -2
  9. package/dist/ir.d.mts.map +1 -1
  10. package/dist/ir.mjs +1 -1
  11. package/dist/migration.d.mts +1 -1
  12. package/dist/runtime.d.mts +4 -2
  13. package/dist/runtime.d.mts.map +1 -1
  14. package/dist/runtime.mjs +3 -1
  15. package/dist/runtime.mjs.map +1 -1
  16. package/dist/schema-verify.d.mts +2 -1
  17. package/dist/schema-verify.d.mts.map +1 -1
  18. package/dist/schema-verify.mjs +1 -1
  19. package/dist/{sql-contract-serializer-8axtK4lg.mjs → sql-contract-serializer-nNw1yk9P.mjs} +14 -35
  20. package/dist/sql-contract-serializer-nNw1yk9P.mjs.map +1 -0
  21. package/dist/{types-CeeCStqw.d.mts → types-DxNgxFkF.d.mts} +61 -7
  22. package/dist/types-DxNgxFkF.d.mts.map +1 -0
  23. package/dist/{verify-sql-schema-CN7pPoTC.d.mts → verify-sql-schema-Cj3c2t5Z.d.mts} +8 -2
  24. package/dist/verify-sql-schema-Cj3c2t5Z.d.mts.map +1 -0
  25. package/dist/{verify-sql-schema-CYLsGCFO.mjs → verify-sql-schema-DV_d2XCG.mjs} +410 -323
  26. package/dist/verify-sql-schema-DV_d2XCG.mjs.map +1 -0
  27. package/package.json +21 -21
  28. package/src/core/control-adapter.ts +112 -3
  29. package/src/core/control-instance.ts +143 -56
  30. package/src/core/default-namespace.ts +9 -0
  31. package/src/core/ir/sql-contract-serializer-base.ts +42 -57
  32. package/src/core/migrations/contract-to-schema-ir.ts +12 -8
  33. package/src/core/migrations/control-policy.ts +322 -0
  34. package/src/core/migrations/field-event-planner.ts +2 -2
  35. package/src/core/migrations/plan-helpers.ts +16 -0
  36. package/src/core/migrations/types.ts +12 -2
  37. package/src/core/schema-verify/control-verify-emit.ts +46 -0
  38. package/src/core/schema-verify/verifier-disposition.ts +53 -0
  39. package/src/core/schema-verify/verify-helpers.ts +151 -110
  40. package/src/core/schema-verify/verify-sql-schema.ts +305 -178
  41. package/src/exports/control.ts +6 -0
  42. package/src/exports/runtime.ts +7 -0
  43. package/dist/control-adapter.d.mts.map +0 -1
  44. package/dist/sql-contract-serializer-8axtK4lg.mjs.map +0 -1
  45. package/dist/types-CeeCStqw.d.mts.map +0 -1
  46. package/dist/verify-sql-schema-CN7pPoTC.d.mts.map +0 -1
  47. package/dist/verify-sql-schema-CYLsGCFO.mjs.map +0 -1
package/dist/control.mjs CHANGED
@@ -1,18 +1,19 @@
1
1
  import { n as sqlFamilyAuthoringFieldPresets, t as sqlFamilyAuthoringTypes } from "./authoring-type-constructors-F4JpCJl7.mjs";
2
- import { t as SqlContractSerializer } from "./sql-contract-serializer-8axtK4lg.mjs";
3
- import { a as extractCodecControlHooks, t as verifySqlSchema } from "./verify-sql-schema-CYLsGCFO.mjs";
2
+ import { t as SqlContractSerializer } from "./sql-contract-serializer-nNw1yk9P.mjs";
3
+ import { a as extractCodecControlHooks, t as verifySqlSchema } from "./verify-sql-schema-DV_d2XCG.mjs";
4
4
  import { t as collectSupportedCodecTypeIds } from "./verify-Crewz6hG.mjs";
5
5
  import { n as temporalAuthoringPresets, r as timestampNowControlDescriptor } from "./timestamp-now-generator-BkjCQIde.mjs";
6
6
  import { sqlEmission } from "@prisma-next/sql-contract-emitter";
7
7
  import { APP_SPACE_ID, SchemaTreeNode, VERIFY_CODE_HASH_MISMATCH, VERIFY_CODE_MARKER_MISSING, VERIFY_CODE_TARGET_MISMATCH, assembleAuthoringContributions } from "@prisma-next/framework-components/control";
8
8
  import { assertDescriptorSelfConsistency } from "@prisma-next/migration-tools/spaces";
9
9
  import { sqlContractCanonicalizationHooks } from "@prisma-next/sql-contract/canonicalization-hooks";
10
- import { ensureSchemaStatement, ensureTableStatement, writeContractMarker } from "@prisma-next/sql-runtime";
11
10
  import { defaultIndexName } from "@prisma-next/sql-schema-ir/naming";
12
11
  import { ifDefined } from "@prisma-next/utils/defined";
13
12
  import { UNBOUND_NAMESPACE_ID } from "@prisma-next/framework-components/ir";
14
13
  import { StorageTable, isPostgresEnumStorageEntry, isStorageTypeInstance, toStorageTypeInstance } from "@prisma-next/sql-contract/types";
14
+ import { blindCast } from "@prisma-next/utils/casts";
15
15
  import { UNSPECIFIED_PSL_NAMESPACE_ID } from "@prisma-next/framework-components/psl-ast";
16
+ import { effectiveControlPolicy } from "@prisma-next/contract/types";
16
17
  import { notOk, ok } from "@prisma-next/utils/result";
17
18
  //#region src/core/operation-preview.ts
18
19
  function isDdlStatement(sqlStatement) {
@@ -1023,7 +1024,7 @@ function extractCodecTypeIdsFromContract(contract) {
1023
1024
  if (typeof contract === "object" && contract !== null && "storage" in contract && typeof contract.storage === "object" && contract.storage !== null && "namespaces" in contract.storage && typeof contract.storage.namespaces === "object" && contract.storage.namespaces !== null) {
1024
1025
  const namespaces = contract.storage.namespaces;
1025
1026
  for (const ns of Object.values(namespaces)) {
1026
- const tbls = ns.tables;
1027
+ const tbls = ns.entries.table;
1027
1028
  if (typeof tbls !== "object" || tbls === null) continue;
1028
1029
  for (const table of Object.values(tbls)) if (typeof table === "object" && table !== null && "columns" in table && typeof table.columns === "object" && table.columns !== null) {
1029
1030
  const columns = table.columns;
@@ -1057,9 +1058,6 @@ function createVerifyResult(options) {
1057
1058
  if (options.codecCoverageSkipped) result.codecCoverageSkipped = options.codecCoverageSkipped;
1058
1059
  return result;
1059
1060
  }
1060
- function isSqlControlAdapter(value) {
1061
- return typeof value === "object" && value !== null && "introspect" in value && typeof value.introspect === "function" && "readMarker" in value && typeof value.readMarker === "function" && "readAllMarkers" in value && typeof value.readAllMarkers === "function" && "lower" in value && typeof value.lower === "function";
1062
- }
1063
1061
  function buildSqlTypeMetadataRegistry(options) {
1064
1062
  const { target, adapter, extensionPacks: extensions } = options;
1065
1063
  const registry = /* @__PURE__ */ new Map();
@@ -1103,11 +1101,7 @@ function createSqlFamilyInstance(stack) {
1103
1101
  adapter,
1104
1102
  extensionPacks: extensions
1105
1103
  });
1106
- const getControlAdapter = () => {
1107
- const controlAdapter = adapter.create(stack);
1108
- if (!isSqlControlAdapter(controlAdapter)) throw new Error("Adapter does not implement SqlControlAdapter (missing introspect, readMarker, or readAllMarkers)");
1109
- return controlAdapter;
1110
- };
1104
+ const getControlAdapter = () => adapter.create(stack);
1111
1105
  const targetSerializer = target.contractSerializer;
1112
1106
  const deserializeWithTargetSerializer = (contractJson) => {
1113
1107
  return (targetSerializer ?? new SqlContractSerializer()).deserializeContract(contractJson);
@@ -1223,6 +1217,7 @@ function createSqlFamilyInstance(stack) {
1223
1217
  frameworkComponents: options.frameworkComponents,
1224
1218
  ...ifDefined("normalizeDefault", controlAdapter.normalizeDefault),
1225
1219
  ...ifDefined("normalizeNativeType", controlAdapter.normalizeNativeType),
1220
+ ...ifDefined("columnsCompatible", controlAdapter.columnsCompatible),
1226
1221
  ...ifDefined("resolveExistingEnumValues", resolveExistingEnumValues)
1227
1222
  });
1228
1223
  },
@@ -1233,21 +1228,21 @@ function createSqlFamilyInstance(stack) {
1233
1228
  const contractStorageHash = contract.storage.storageHash;
1234
1229
  const contractProfileHash = "profileHash" in contract && typeof contract.profileHash === "string" ? contract.profileHash : contractStorageHash;
1235
1230
  const contractTarget = contract.target;
1236
- await driver.query(ensureSchemaStatement.sql, ensureSchemaStatement.params);
1237
- await driver.query(ensureTableStatement.sql, ensureTableStatement.params);
1238
- const existingMarker = await getControlAdapter().readMarker(driver, APP_SPACE_ID);
1231
+ const controlAdapter = getControlAdapter();
1232
+ const lowererContext = { contract };
1233
+ for (const query of controlAdapter.bootstrapSignMarkerQueries()) {
1234
+ const lowered = controlAdapter.lower(query, lowererContext);
1235
+ await driver.query(lowered.sql, lowered.params);
1236
+ }
1237
+ const existingMarker = await controlAdapter.readMarker(driver, APP_SPACE_ID);
1239
1238
  let markerCreated = false;
1240
1239
  let markerUpdated = false;
1241
1240
  let previousHashes;
1242
1241
  if (!existingMarker) {
1243
- const write = writeContractMarker({
1244
- space: APP_SPACE_ID,
1242
+ await controlAdapter.insertMarker(driver, APP_SPACE_ID, {
1245
1243
  storageHash: contractStorageHash,
1246
- profileHash: contractProfileHash,
1247
- contractJson: contractInput,
1248
- canonicalVersion: 1
1244
+ profileHash: contractProfileHash
1249
1245
  });
1250
- await driver.query(write.insert.sql, write.insert.params);
1251
1246
  markerCreated = true;
1252
1247
  } else {
1253
1248
  const existingStorageHash = existingMarker.storageHash;
@@ -1257,14 +1252,10 @@ function createSqlFamilyInstance(stack) {
1257
1252
  storageHash: existingStorageHash,
1258
1253
  profileHash: existingProfileHash
1259
1254
  };
1260
- const write = writeContractMarker({
1261
- space: APP_SPACE_ID,
1255
+ if (!await controlAdapter.updateMarker(driver, APP_SPACE_ID, existingStorageHash, {
1262
1256
  storageHash: contractStorageHash,
1263
- profileHash: contractProfileHash,
1264
- contractJson: contractInput,
1265
- canonicalVersion: existingMarker.canonicalVersion ?? 1
1266
- });
1267
- await driver.query(write.update.sql, write.update.params);
1257
+ profileHash: contractProfileHash
1258
+ })) throw new Error("CAS conflict: marker was modified by another process during sign");
1268
1259
  markerUpdated = true;
1269
1260
  }
1270
1261
  }
@@ -1302,6 +1293,18 @@ function createSqlFamilyInstance(stack) {
1302
1293
  async readAllMarkers(options) {
1303
1294
  return getControlAdapter().readAllMarkers(options.driver);
1304
1295
  },
1296
+ async readLedger(options) {
1297
+ return getControlAdapter().readLedger(options.driver, options.space);
1298
+ },
1299
+ async initMarker(options) {
1300
+ return getControlAdapter().initMarker(options.driver, options.space, options.destination);
1301
+ },
1302
+ async updateMarker(options) {
1303
+ return getControlAdapter().updateMarker(options.driver, options.space, options.expectedFrom, options.destination);
1304
+ },
1305
+ async writeLedgerEntry(options) {
1306
+ return getControlAdapter().writeLedgerEntry(options.driver, options.space, options.entry);
1307
+ },
1305
1308
  async introspect(options) {
1306
1309
  return getControlAdapter().introspect(options.driver, options.contract);
1307
1310
  },
@@ -1311,6 +1314,12 @@ function createSqlFamilyInstance(stack) {
1311
1314
  lowerAst(ast, context) {
1312
1315
  return getControlAdapter().lower(ast, context);
1313
1316
  },
1317
+ bootstrapControlTableQueries() {
1318
+ return getControlAdapter().bootstrapControlTableQueries();
1319
+ },
1320
+ bootstrapSignMarkerQueries() {
1321
+ return getControlAdapter().bootstrapSignMarkerQueries();
1322
+ },
1314
1323
  toOperationPreview(operations) {
1315
1324
  return sqlOperationsToPreview(operations);
1316
1325
  },
@@ -1520,10 +1529,10 @@ function detectDestructiveChanges(from, to) {
1520
1529
  for (const namespaceId of namespaceIds) {
1521
1530
  const fromNs = from.namespaces[namespaceId];
1522
1531
  const toNs = to.namespaces[namespaceId];
1523
- const fromTables = fromNs?.tables;
1532
+ const fromTables = fromNs?.entries.table;
1524
1533
  if (!fromTables) continue;
1525
1534
  for (const tableName of Object.keys(fromTables)) {
1526
- const toTableRaw = toNs?.tables[tableName];
1535
+ const toTableRaw = toNs?.entries.table[tableName];
1527
1536
  if (!(toTableRaw instanceof StorageTable)) {
1528
1537
  conflicts.push({
1529
1538
  kind: "tableRemoved",
@@ -1563,13 +1572,13 @@ function contractToSchemaIR(contract, options) {
1563
1572
  const storage = contract.storage;
1564
1573
  const allTypes = { ...storage.types ?? {} };
1565
1574
  for (const ns of Object.values(storage.namespaces)) {
1566
- const nsEnums = ns.enum;
1567
- if (nsEnums) for (const [k, v] of Object.entries(nsEnums)) allTypes[k] = v;
1575
+ const nsEnums = ns.entries["type"];
1576
+ if (nsEnums) for (const [k, v] of Object.entries(nsEnums)) allTypes[k] = blindCast(v);
1568
1577
  }
1569
1578
  const storageTypes = allTypes;
1570
1579
  const tables = {};
1571
- for (const ns of Object.values(storage.namespaces)) for (const [tableName, tableDefRaw] of Object.entries(ns.tables)) {
1572
- if (!(tableDefRaw instanceof StorageTable)) throw new Error(`contractToSchemaIR: expected StorageTable at namespaces.${ns.id}.tables.${tableName}`);
1580
+ for (const ns of Object.values(storage.namespaces)) for (const [tableName, tableDefRaw] of Object.entries(ns.entries.table)) {
1581
+ if (!(tableDefRaw instanceof StorageTable)) throw new Error(`contractToSchemaIR: expected StorageTable at namespaces.${ns.id}.entries.table.${tableName}`);
1573
1582
  const tableDef = tableDefRaw;
1574
1583
  if (tables[tableName] !== void 0) throw new Error(`contractToSchemaIR: duplicate SQL table name "${tableName}" across namespaces (ambiguous for flat SqlSchemaIR.tables).`);
1575
1584
  tables[tableName] = convertTable(tableName, tableDef, storageTypes, options.expandNativeType, options.renderDefault);
@@ -1604,7 +1613,7 @@ function deriveAnnotations(storage, annotationNamespace, resolveEnumStorageKey)
1604
1613
  if (isStorageTypeInstance(typeInstance)) storageTypes[typeInstance.nativeType] = typeInstance;
1605
1614
  }
1606
1615
  for (const [namespaceId, ns] of Object.entries(storage.namespaces)) {
1607
- const nsEnums = ns.enum;
1616
+ const nsEnums = ns.entries["type"];
1608
1617
  if (!nsEnums) continue;
1609
1618
  for (const entry of Object.values(nsEnums)) {
1610
1619
  if (!isPostgresEnumStorageEntry(entry)) continue;
@@ -1616,6 +1625,176 @@ function deriveAnnotations(storage, annotationNamespace, resolveEnumStorageKey)
1616
1625
  return { [annotationNamespace]: { storageTypes } };
1617
1626
  }
1618
1627
  //#endregion
1628
+ //#region src/core/migrations/control-policy.ts
1629
+ /**
1630
+ * The control policy that governs a single call. The `external` default is an
1631
+ * un-overridable namespace floor: when the contract default is `external`, no
1632
+ * per-object `managed` override can escalate DDL above the floor, so the
1633
+ * policy is forced to `external` regardless of the node's own declaration.
1634
+ * Every other default defers to the node's effective control policy.
1635
+ */
1636
+ function controlPolicyForCall(subject, defaultControlPolicy) {
1637
+ if (defaultControlPolicy === "external") return "external";
1638
+ return effectiveControlPolicy(subject?.explicitNodeControlPolicy, defaultControlPolicy);
1639
+ }
1640
+ /**
1641
+ * Whether a call is allowed to emit under a given control policy.
1642
+ *
1643
+ * - `managed` — full lifecycle, every op allowed.
1644
+ * - `tolerated` — create-if-absent only: allowed iff the call creates a whole
1645
+ * new top-level object (and its subject was positively resolved). Anything
1646
+ * that modifies an existing object, and anything whose subject could not be
1647
+ * resolved, is suppressed.
1648
+ * - `external` / `observed` — no DDL at all.
1649
+ */
1650
+ function callAllowedUnderControlPolicy(policy, subject) {
1651
+ switch (policy) {
1652
+ case "managed": return true;
1653
+ case "tolerated": return subject?.createsNewObject === true;
1654
+ case "external":
1655
+ case "observed": return false;
1656
+ }
1657
+ }
1658
+ function defaultSubjectLabel(factoryName, subject) {
1659
+ if (subject?.table) return `${factoryName}(${subject.table})`;
1660
+ if (subject?.typeName) return `${factoryName}(${subject.typeName})`;
1661
+ return factoryName;
1662
+ }
1663
+ function suppressionSummary(subjectLabel, subject, effectivePolicy) {
1664
+ const namespace = subject?.namespaceId ?? "unknown";
1665
+ const declared = subject?.explicitNodeControlPolicy;
1666
+ if (effectivePolicy === "external" && declared === "managed") return `control policy suppressed: ${subjectLabel} — namespace '${namespace}' has effective control 'external' but table declared 'managed'`;
1667
+ return `control policy suppressed: ${subjectLabel} — namespace '${namespace}' has effective control '${effectivePolicy}'${declared ? ` but table declared '${declared}'` : ""}`;
1668
+ }
1669
+ function buildSubjectSuppressionWarning(subject, effectivePolicy, factoryName, formatSubjectLabel) {
1670
+ return {
1671
+ kind: "controlPolicySuppressedCall",
1672
+ summary: suppressionSummary(formatSubjectLabel(factoryName, subject), subject, effectivePolicy),
1673
+ location: {
1674
+ ...ifDefined("namespace", subject?.namespaceId),
1675
+ ...ifDefined("table", subject?.table),
1676
+ ...ifDefined("column", subject?.column),
1677
+ ...ifDefined("type", subject?.typeName)
1678
+ },
1679
+ meta: {
1680
+ controlPolicy: effectivePolicy,
1681
+ factoryName,
1682
+ ...ifDefined("declaredControlPolicy", subject?.explicitNodeControlPolicy)
1683
+ }
1684
+ };
1685
+ }
1686
+ function defaultModificationFactoryNameForSubject(subject) {
1687
+ if (subject.table) return "alterTable";
1688
+ if (subject.typeName) return "alterType";
1689
+ return "alterSchema";
1690
+ }
1691
+ /**
1692
+ * Partition the calls produced for a single set of subjects into those the
1693
+ * effective control policy permits (`kept`) and a list of
1694
+ * {@link SqlPlannerConflict} warnings describing the suppressed calls.
1695
+ *
1696
+ * **Prefer {@link partitionIssuesByControlPolicy}** for the schema-issue
1697
+ * pipeline: it filters subjects out of the planner's *input* so the planner
1698
+ * never has to reason about un-modeled state on `external`/`observed`
1699
+ * subjects. This call-level helper remains for paths that bypass the issue
1700
+ * pipeline — currently the codec-emitted field-event ops, which originate
1701
+ * from declared contract fields rather than from introspected schema state
1702
+ * and therefore cannot trip the diff engine.
1703
+ */
1704
+ function partitionCallsByControlPolicy(options) {
1705
+ const defaultControlPolicy = options.contract.defaultControlPolicy;
1706
+ const formatSubjectLabel = options.formatSubjectLabel ?? defaultSubjectLabel;
1707
+ const kept = [];
1708
+ const warnings = [];
1709
+ for (const call of options.calls) {
1710
+ const subject = options.resolveControlPolicySubject(call);
1711
+ const policy = controlPolicyForCall(subject, defaultControlPolicy);
1712
+ if (callAllowedUnderControlPolicy(policy, subject)) kept.push(call);
1713
+ else {
1714
+ const factoryName = options.resolveFactoryName(call);
1715
+ warnings.push(buildSubjectSuppressionWarning(subject, policy, factoryName, formatSubjectLabel));
1716
+ }
1717
+ }
1718
+ return Object.freeze({
1719
+ kept: Object.freeze(kept),
1720
+ warnings: Object.freeze(warnings)
1721
+ });
1722
+ }
1723
+ /**
1724
+ * Partition a list of schema-issue-shaped inputs by the effective control
1725
+ * policy of each issue's subject *before* the planner is invoked.
1726
+ *
1727
+ * `plannable` is the list of issues whose subject's effective policy permits
1728
+ * the planner to act on them (`managed`, or `tolerated` for whole-object
1729
+ * creation issues only). Issues for `external`/`observed` subjects, and
1730
+ * non-creation issues for `tolerated` subjects, are dropped from the planner's
1731
+ * input entirely — they never enter introspection-driven planning, never feed
1732
+ * the diff engine, and never produce DDL calls that would have to be
1733
+ * post-filtered. This sidesteps a class of failure where the diff engine
1734
+ * cannot reason about the live shape of a subject the user marked as
1735
+ * out-of-scope (`external`).
1736
+ *
1737
+ * `warnings` is one {@link SqlPlannerConflict} per suppressed subject (not per
1738
+ * suppressed issue). `factoryName` is inferred from the subject's issue mix:
1739
+ * if any of the subject's issues is whole-object creation, the warning takes
1740
+ * the corresponding creation factoryName (e.g. `createTable`,
1741
+ * `createEnumType`, `createSchema`); otherwise it falls back to
1742
+ * `defaultModificationFactoryName(subject)` — a synthetic label that names
1743
+ * the *kind* of mutation that would have run, since no concrete DDL call was
1744
+ * generated.
1745
+ *
1746
+ * Unresolved-subject issues (`resolveControlPolicySubject` returns
1747
+ * `undefined`) emit one warning each; they cannot be deduplicated because
1748
+ * they carry no subject coordinate.
1749
+ */
1750
+ function partitionIssuesByControlPolicy(options) {
1751
+ const defaultControlPolicy = options.contract.defaultControlPolicy;
1752
+ const formatSubjectLabel = options.formatSubjectLabel ?? defaultSubjectLabel;
1753
+ const inferModificationFactoryName = options.defaultModificationFactoryName ?? defaultModificationFactoryNameForSubject;
1754
+ const plannable = [];
1755
+ const suppressedSubjects = /* @__PURE__ */ new Map();
1756
+ const unresolvedSuppressions = [];
1757
+ for (const issue of options.issues) {
1758
+ const subject = options.resolveControlPolicySubject(issue);
1759
+ const policy = controlPolicyForCall(subject, defaultControlPolicy);
1760
+ const creationFactoryName = options.resolveCreationFactoryName(issue);
1761
+ if (policy === "managed") {
1762
+ plannable.push(issue);
1763
+ continue;
1764
+ }
1765
+ if (policy === "tolerated" && subject !== void 0 && creationFactoryName !== void 0 && subject.createsNewObject) {
1766
+ plannable.push(issue);
1767
+ continue;
1768
+ }
1769
+ if (subject === void 0) {
1770
+ const factoryName = creationFactoryName ?? "unknown";
1771
+ unresolvedSuppressions.push(buildSubjectSuppressionWarning(void 0, policy, factoryName, formatSubjectLabel));
1772
+ continue;
1773
+ }
1774
+ const key = subjectKey(subject);
1775
+ const existing = suppressedSubjects.get(key);
1776
+ if (existing) {
1777
+ if (existing.creationFactoryName === void 0 && creationFactoryName !== void 0) existing.creationFactoryName = creationFactoryName;
1778
+ } else suppressedSubjects.set(key, {
1779
+ subject,
1780
+ policy,
1781
+ ...ifDefined("creationFactoryName", creationFactoryName)
1782
+ });
1783
+ }
1784
+ const warnings = [...unresolvedSuppressions];
1785
+ for (const entry of suppressedSubjects.values()) {
1786
+ const factoryName = entry.creationFactoryName ?? inferModificationFactoryName(entry.subject);
1787
+ warnings.push(buildSubjectSuppressionWarning(entry.subject, entry.policy, factoryName, formatSubjectLabel));
1788
+ }
1789
+ return Object.freeze({
1790
+ plannable: Object.freeze(plannable),
1791
+ warnings: Object.freeze(warnings)
1792
+ });
1793
+ }
1794
+ function subjectKey(subject) {
1795
+ return `${subject.namespaceId}\u0000${subject.table ?? ""}\u0000${subject.typeName ?? ""}`;
1796
+ }
1797
+ //#endregion
1619
1798
  //#region src/core/migrations/field-event-planner.ts
1620
1799
  function planFieldEventOperations(options) {
1621
1800
  const priorContract = options.priorContract;
@@ -1627,8 +1806,8 @@ function planFieldEventOperations(options) {
1627
1806
  for (const namespaceId of namespaceIds) {
1628
1807
  const priorNs = priorContract?.storage.namespaces[namespaceId];
1629
1808
  const newNs = newContract.storage.namespaces[namespaceId];
1630
- const priorTables = priorNs?.tables;
1631
- const newTables = newNs?.tables;
1809
+ const priorTables = priorNs?.entries.table;
1810
+ const newTables = newNs?.entries.table;
1632
1811
  const tableNames = unionSorted(priorTables ? Object.keys(priorTables) : [], newTables ? Object.keys(newTables) : []);
1633
1812
  for (const tableName of tableNames) {
1634
1813
  const priorTableRaw = priorTables?.[tableName];
@@ -1796,10 +1975,17 @@ function createMigrationPlan(options) {
1796
1975
  ...options.meta ? { meta: cloneRecord(options.meta) } : {}
1797
1976
  });
1798
1977
  }
1799
- function plannerSuccess(plan) {
1978
+ function plannerSuccess(plan, warnings) {
1800
1979
  return Object.freeze({
1801
1980
  kind: "success",
1802
- plan
1981
+ plan,
1982
+ ...warnings && warnings.length > 0 ? { warnings: Object.freeze(warnings.map((conflict) => Object.freeze({
1983
+ kind: conflict.kind,
1984
+ summary: conflict.summary,
1985
+ ...conflict.why ? { why: conflict.why } : {},
1986
+ ...conflict.location ? { location: Object.freeze({ ...conflict.location }) } : {},
1987
+ ...conflict.meta ? { meta: cloneRecord(conflict.meta) } : {}
1988
+ }))) } : {}
1803
1989
  });
1804
1990
  }
1805
1991
  function plannerFailure(conflicts) {
@@ -1844,6 +2030,6 @@ const INIT_ADDITIVE_POLICY = Object.freeze({ allowedOperationClasses: Object.fre
1844
2030
  //#region src/exports/control.ts
1845
2031
  var control_default = new SqlFamilyDescriptor();
1846
2032
  //#endregion
1847
- export { INIT_ADDITIVE_POLICY, assembleAuthoringContributions, contractToSchemaIR, createMigrationPlan, control_default as default, detectDestructiveChanges, extractCodecControlHooks, planFieldEventOperations, plannerFailure, plannerSuccess, runnerFailure, runnerSuccess, temporalAuthoringPresets, timestampNowControlDescriptor };
2033
+ export { INIT_ADDITIVE_POLICY, assembleAuthoringContributions, contractToSchemaIR, controlPolicyForCall, createMigrationPlan, control_default as default, detectDestructiveChanges, extractCodecControlHooks, partitionCallsByControlPolicy, partitionIssuesByControlPolicy, planFieldEventOperations, plannerFailure, plannerSuccess, runnerFailure, runnerSuccess, temporalAuthoringPresets, timestampNowControlDescriptor };
1848
2034
 
1849
2035
  //# sourceMappingURL=control.mjs.map