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

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