@prisma-next/target-postgres 0.14.0-dev.39 → 0.14.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 (106) hide show
  1. package/dist/{authoring-CZmSQriw.mjs → authoring-BX1WHIdN.mjs} +2 -2
  2. package/dist/authoring-BX1WHIdN.mjs.map +1 -0
  3. package/dist/contract-free.mjs +2 -2
  4. package/dist/control.d.mts.map +1 -1
  5. package/dist/control.mjs +633 -16
  6. package/dist/control.mjs.map +1 -1
  7. package/dist/{ddl-CGf6UABz.mjs → ddl-BmLIe1z_.mjs} +2 -2
  8. package/dist/{ddl-CGf6UABz.mjs.map → ddl-BmLIe1z_.mjs.map} +1 -1
  9. package/dist/{descriptor-meta-Du6olWQI.mjs → descriptor-meta-tjatyhtZ.mjs} +3 -3
  10. package/dist/{descriptor-meta-Du6olWQI.mjs.map → descriptor-meta-tjatyhtZ.mjs.map} +1 -1
  11. package/dist/{issue-planner-C-CwZd8D.mjs → issue-planner-DbFvaAgu.mjs} +5 -5
  12. package/dist/issue-planner-DbFvaAgu.mjs.map +1 -0
  13. package/dist/issue-planner.d.mts +1 -1
  14. package/dist/issue-planner.mjs +1 -1
  15. package/dist/migration.d.mts +2 -2
  16. package/dist/migration.mjs +2 -2
  17. package/dist/{op-factory-call-DPJR7tJc.d.mts → op-factory-call-CJWUxyUW.d.mts} +2 -2
  18. package/dist/{op-factory-call-DPJR7tJc.d.mts.map → op-factory-call-CJWUxyUW.d.mts.map} +1 -1
  19. package/dist/{op-factory-call-DArx_1HM.mjs → op-factory-call-t65c4jyY.mjs} +3 -3
  20. package/dist/op-factory-call-t65c4jyY.mjs.map +1 -0
  21. package/dist/op-factory-call.d.mts +1 -1
  22. package/dist/op-factory-call.mjs +1 -1
  23. package/dist/pack.d.mts +2 -2
  24. package/dist/pack.mjs +1 -1
  25. package/dist/{planner-BQ7lSqbm.mjs → planner-CG2pLHk-.mjs} +310 -126
  26. package/dist/planner-CG2pLHk-.mjs.map +1 -0
  27. package/dist/{planner-produced-postgres-migration-3ppG0t3i.mjs → planner-produced-postgres-migration-BolD0Mvc.mjs} +2 -2
  28. package/dist/{planner-produced-postgres-migration-3ppG0t3i.mjs.map → planner-produced-postgres-migration-BolD0Mvc.mjs.map} +1 -1
  29. package/dist/{planner-produced-postgres-migration-BauTSigX.d.mts → planner-produced-postgres-migration-Cz9--XOT.d.mts} +2 -2
  30. package/dist/{planner-produced-postgres-migration-BauTSigX.d.mts.map → planner-produced-postgres-migration-Cz9--XOT.d.mts.map} +1 -1
  31. package/dist/planner-produced-postgres-migration.d.mts +1 -1
  32. package/dist/planner-produced-postgres-migration.mjs +1 -1
  33. package/dist/{planner-sql-checks-CyjaoKhm.mjs → planner-sql-checks-DE9c4519.mjs} +2 -2
  34. package/dist/{planner-sql-checks-CyjaoKhm.mjs.map → planner-sql-checks-DE9c4519.mjs.map} +1 -1
  35. package/dist/planner-sql-checks.mjs +1 -1
  36. package/dist/planner.d.mts +73 -22
  37. package/dist/planner.d.mts.map +1 -1
  38. package/dist/planner.mjs +2 -2
  39. package/dist/{postgres-contract-view-CoRZewAp.mjs → postgres-contract-view-BegduLr6.mjs} +3 -3
  40. package/dist/{postgres-contract-view-CoRZewAp.mjs.map → postgres-contract-view-BegduLr6.mjs.map} +1 -1
  41. package/dist/postgres-database-schema-node-os8ogEap.d.mts +183 -0
  42. package/dist/postgres-database-schema-node-os8ogEap.d.mts.map +1 -0
  43. package/dist/{postgres-migration-BVjq0iFW.mjs → postgres-migration-BfMmK6tv.mjs} +3 -3
  44. package/dist/{postgres-migration-BVjq0iFW.mjs.map → postgres-migration-BfMmK6tv.mjs.map} +1 -1
  45. package/dist/{postgres-migration-UtGCjw5j.d.mts → postgres-migration-D7kNVWVG.d.mts} +2 -2
  46. package/dist/{postgres-migration-UtGCjw5j.d.mts.map → postgres-migration-D7kNVWVG.d.mts.map} +1 -1
  47. package/dist/{postgres-rls-policy-12wAEQhs.d.mts → postgres-rls-policy--CSddvMR.d.mts} +8 -16
  48. package/dist/postgres-rls-policy--CSddvMR.d.mts.map +1 -0
  49. package/dist/{postgres-schema-DLcc3W2-.mjs → postgres-schema-BLIHBEfX.mjs} +34 -64
  50. package/dist/postgres-schema-BLIHBEfX.mjs.map +1 -0
  51. package/dist/{postgres-schema-DJUEkWg-.d.mts → postgres-schema-BywoeKlt.d.mts} +9 -10
  52. package/dist/postgres-schema-BywoeKlt.d.mts.map +1 -0
  53. package/dist/postgres-table-schema-node-1nNc8XDT.mjs +248 -0
  54. package/dist/postgres-table-schema-node-1nNc8XDT.mjs.map +1 -0
  55. package/dist/resolve-ddl-schema-CL1RR1mx.mjs +20 -0
  56. package/dist/resolve-ddl-schema-CL1RR1mx.mjs.map +1 -0
  57. package/dist/runtime.mjs +1 -1
  58. package/dist/schema-ir-annotations.d.mts +9 -3
  59. package/dist/schema-ir-annotations.d.mts.map +1 -1
  60. package/dist/schema-ir-annotations.mjs +1 -1
  61. package/dist/types.d.mts +4 -4
  62. package/dist/types.mjs +3 -3
  63. package/package.json +20 -19
  64. package/src/core/authoring.ts +2 -2
  65. package/src/core/entity-kinds.ts +2 -2
  66. package/src/core/migrations/contract-to-postgres-database-schema-node.ts +125 -0
  67. package/src/core/migrations/diff-database-schema.ts +191 -0
  68. package/src/core/migrations/issue-planner.ts +1 -1
  69. package/src/core/migrations/op-factory-call.ts +1 -1
  70. package/src/core/migrations/operations/rls.ts +1 -1
  71. package/src/core/migrations/planner.ts +117 -68
  72. package/src/core/{schema-ir/postgres-schema-ir-annotations.ts → migrations/resolve-ddl-schema.ts} +8 -6
  73. package/src/core/migrations/runner.ts +13 -16
  74. package/src/core/migrations/verify-postgres-namespaces.ts +22 -14
  75. package/src/core/{schema-ir/postgres-rls-policy.ts → postgres-rls-policy.ts} +6 -43
  76. package/src/core/{schema-ir/postgres-role.ts → postgres-role.ts} +6 -21
  77. package/src/core/postgres-schema.ts +2 -2
  78. package/src/core/psl-infer/infer-psl-contract.ts +834 -0
  79. package/src/core/psl-infer/postgres-default-mapping.ts +16 -0
  80. package/src/core/psl-infer/postgres-type-map.ts +157 -0
  81. package/src/core/schema-ir/postgres-database-schema-node.ts +63 -0
  82. package/src/core/schema-ir/postgres-namespace-schema-node.ts +61 -0
  83. package/src/core/schema-ir/postgres-policy-schema-node.ts +93 -0
  84. package/src/core/schema-ir/postgres-role-schema-node.ts +66 -0
  85. package/src/core/schema-ir/{postgres-table-ir.ts → postgres-table-schema-node.ts} +29 -18
  86. package/src/core/schema-ir/schema-node-kinds.ts +28 -0
  87. package/src/exports/control.ts +30 -2
  88. package/src/exports/planner.ts +2 -5
  89. package/src/exports/schema-ir-annotations.ts +1 -1
  90. package/src/exports/types.ts +23 -15
  91. package/dist/authoring-CZmSQriw.mjs.map +0 -1
  92. package/dist/issue-planner-C-CwZd8D.mjs.map +0 -1
  93. package/dist/op-factory-call-DArx_1HM.mjs.map +0 -1
  94. package/dist/planner-BQ7lSqbm.mjs.map +0 -1
  95. package/dist/postgres-rls-policy-12wAEQhs.d.mts.map +0 -1
  96. package/dist/postgres-schema-DJUEkWg-.d.mts.map +0 -1
  97. package/dist/postgres-schema-DLcc3W2-.mjs.map +0 -1
  98. package/dist/postgres-schema-ir-BJl_a3J7.d.mts +0 -102
  99. package/dist/postgres-schema-ir-BJl_a3J7.d.mts.map +0 -1
  100. package/dist/postgres-schema-ir-I_FiUndw.mjs +0 -133
  101. package/dist/postgres-schema-ir-I_FiUndw.mjs.map +0 -1
  102. package/dist/postgres-schema-ir-annotations-BbDSiwx2.mjs +0 -14
  103. package/dist/postgres-schema-ir-annotations-BbDSiwx2.mjs.map +0 -1
  104. package/src/core/migrations/contract-to-postgres-schema-ir.ts +0 -91
  105. package/src/core/migrations/diff-postgres-schema.ts +0 -55
  106. package/src/core/schema-ir/postgres-schema-ir.ts +0 -138
@@ -1,65 +1,225 @@
1
- import { d as isPostgresRlsPolicy, l as PostgresRlsPolicy, r as isPostgresSchema, u as assertPostgresRlsPolicy } from "./postgres-schema-DLcc3W2-.mjs";
2
- import { a as PostgresTableIR, i as isPostgresSchemaIR, n as assertPostgresSchemaIR, r as ensurePostgresSchemaIR, t as PostgresSchemaIR } from "./postgres-schema-ir-I_FiUndw.mjs";
3
- import { t as resolveDdlSchemaForNamespaceStorage } from "./postgres-schema-ir-annotations-BbDSiwx2.mjs";
1
+ import { d as PostgresRlsPolicy, r as isPostgresSchema } from "./postgres-schema-BLIHBEfX.mjs";
2
+ import { a as PostgresDatabaseSchemaNode, i as PostgresNamespaceSchemaNode, n as PostgresRoleSchemaNode, r as PostgresPolicySchemaNode, t as PostgresTableSchemaNode } from "./postgres-table-schema-node-1nNc8XDT.mjs";
3
+ import { t as resolveDdlSchemaForNamespaceStorage } from "./resolve-ddl-schema-CL1RR1mx.mjs";
4
4
  import { t as parsePostgresDefault } from "./default-normalizer-Dug8Fez9.mjs";
5
5
  import { t as normalizeSchemaNativeType } from "./native-type-normalizer-Bc9XJzWC.mjs";
6
- import { C as EnableRowLevelSecurityCall, d as CreatePostgresRlsPolicyCall, x as DropPostgresRlsPolicyCall } from "./op-factory-call-DArx_1HM.mjs";
7
- import { n as postgresPlannerStrategies, t as planIssues } from "./issue-planner-C-CwZd8D.mjs";
8
- import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-3ppG0t3i.mjs";
6
+ import { C as EnableRowLevelSecurityCall, d as CreatePostgresRlsPolicyCall, x as DropPostgresRlsPolicyCall } from "./op-factory-call-t65c4jyY.mjs";
7
+ import { n as postgresPlannerStrategies, t as planIssues } from "./issue-planner-DbFvaAgu.mjs";
8
+ import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-BolD0Mvc.mjs";
9
9
  import { blindCast } from "@prisma-next/utils/casts";
10
- import { contractToSchemaIR, extractCodecControlHooks, partitionCallsByControlPolicy, partitionIssuesByControlPolicy, planFieldEventOperations, plannerFailure } from "@prisma-next/family-sql/control";
10
+ import { contractNamespaceToSchemaIR, extractCodecControlHooks, partitionCallsByControlPolicy, partitionIssuesByControlPolicy, planFieldEventOperations, plannerFailure } from "@prisma-next/family-sql/control";
11
11
  import { ifDefined } from "@prisma-next/utils/defined";
12
12
  import { UNBOUND_NAMESPACE_ID, entityAt } from "@prisma-next/framework-components/ir";
13
- import { verifySqlSchema } from "@prisma-next/family-sql/schema-verify";
14
- import { diffSchemas } from "@prisma-next/framework-components/control";
15
- //#region src/core/migrations/contract-to-postgres-schema-ir.ts
16
- function contractToPostgresSchemaIR(contract, options) {
17
- const sqlIr = contractToSchemaIR(contract, options);
18
- const ownedSchemas = contract === null ? [] : Object.values(contract.storage.namespaces).filter((ns) => isPostgresSchema(ns)).map((ns) => resolveDdlSchemaForNamespaceStorage(contract.storage, ns.id));
19
- const policiesByTable = /* @__PURE__ */ new Map();
20
- if (contract !== null) for (const ns of Object.values(contract.storage.namespaces)) {
13
+ import { verifySqlSchemaTree } from "@prisma-next/family-sql/diff";
14
+ import { SchemaDiff, diffSchemas } from "@prisma-next/framework-components/control";
15
+ //#region src/core/migrations/contract-to-postgres-database-schema-node.ts
16
+ function toPolicyNode(policy, namespaceId) {
17
+ return new PostgresPolicySchemaNode({
18
+ name: policy.name,
19
+ prefix: policy.prefix,
20
+ tableName: policy.tableName,
21
+ namespaceId,
22
+ operation: policy.operation,
23
+ roles: [...policy.roles],
24
+ ...ifDefined("using", policy.using),
25
+ ...ifDefined("withCheck", policy.withCheck),
26
+ permissive: policy.permissive
27
+ });
28
+ }
29
+ /**
30
+ * Projects a Postgres contract into the expected schema-diff tree: a
31
+ * `PostgresDatabaseSchemaNode` root holding one `PostgresNamespaceSchemaNode`
32
+ * per Postgres namespace, each holding its `PostgresTableSchemaNode`s with
33
+ * their `PostgresPolicySchemaNode`s, plus the database roles on the root.
34
+ *
35
+ * Not a duplicate of the family's `contractToSchemaIR`: that builds a flat,
36
+ * single `{ tables }` map (and throws on cross-namespace name collisions, with
37
+ * no RLS/role concept) for SQLite's single-schema world. This is the
38
+ * Postgres-specific *tree* shape — multi-schema, RLS-policy-aware, role-aware.
39
+ * It reuses the family's per-namespace table conversion (`contractNamespaceToSchemaIR`)
40
+ * for column/FK/index building and only adds the Postgres tree/policy/role shape.
41
+ *
42
+ * Tables are grouped by their owning namespace (resolved DDL schema name) so
43
+ * the tree mirrors Postgres's object hierarchy. The DDL schema name is
44
+ * resolved once per namespace.
45
+ *
46
+ * A policy that references a table absent from its namespace is a malformed
47
+ * contract — the loop throws rather than fabricating a stub table.
48
+ */
49
+ function contractToPostgresDatabaseSchemaNode(contract, options) {
50
+ if (contract === null) return new PostgresDatabaseSchemaNode({
51
+ namespaces: {},
52
+ roles: [],
53
+ existingSchemas: [],
54
+ pgVersion: ""
55
+ });
56
+ const namespaces = {};
57
+ const roles = [];
58
+ const ownedSchemas = [];
59
+ for (const ns of Object.values(contract.storage.namespaces)) {
21
60
  if (!isPostgresSchema(ns)) continue;
22
- const resolvedSchema = resolveDdlSchemaForNamespaceStorage(contract.storage, ns.id);
61
+ const ddlSchema = resolveDdlSchemaForNamespaceStorage(contract.storage, ns.id);
62
+ ownedSchemas.push(ddlSchema);
63
+ const sqlTables = contractNamespaceToSchemaIR(contract.storage, ns.id, options).tables;
64
+ const policiesByTable = /* @__PURE__ */ new Map();
23
65
  for (const policy of Object.values(ns.policy)) {
24
- const resolved = resolvedSchema === policy.namespaceId ? policy : new PostgresRlsPolicy({
25
- name: policy.name,
26
- prefix: policy.prefix,
27
- tableName: policy.tableName,
28
- namespaceId: resolvedSchema,
29
- operation: policy.operation,
30
- roles: [...policy.roles],
31
- ...ifDefined("using", policy.using),
32
- ...ifDefined("withCheck", policy.withCheck),
33
- permissive: policy.permissive
34
- });
35
66
  const list = policiesByTable.get(policy.tableName) ?? [];
36
- list.push(resolved);
67
+ list.push(toPolicyNode(policy, ddlSchema));
37
68
  policiesByTable.set(policy.tableName, list);
38
69
  }
70
+ const tables = {};
71
+ for (const tableName of Object.keys(ns.table)) {
72
+ const sqlTable = sqlTables[tableName];
73
+ if (sqlTable === void 0) continue;
74
+ tables[tableName] = new PostgresTableSchemaNode({
75
+ name: sqlTable.name,
76
+ columns: sqlTable.columns,
77
+ foreignKeys: sqlTable.foreignKeys,
78
+ uniques: sqlTable.uniques,
79
+ indexes: sqlTable.indexes,
80
+ ...ifDefined("primaryKey", sqlTable.primaryKey),
81
+ ...ifDefined("annotations", sqlTable.annotations),
82
+ ...ifDefined("checks", sqlTable.checks),
83
+ policies: policiesByTable.get(tableName) ?? []
84
+ });
85
+ }
86
+ for (const [tableName, tablePolicies] of policiesByTable) if (!(tableName in tables)) {
87
+ const policyName = tablePolicies[0]?.name ?? "(unknown)";
88
+ throw new Error(`contract-to-postgres-database-schema-node: policy "${policyName}" references table "${tableName}" not present in namespace "${ddlSchema}"`);
89
+ }
90
+ namespaces[ddlSchema] = new PostgresNamespaceSchemaNode({
91
+ schemaName: ddlSchema,
92
+ tables,
93
+ nativeEnumTypeNames: []
94
+ });
95
+ for (const role of Object.values(ns.role)) roles.push(new PostgresRoleSchemaNode({
96
+ name: role.name,
97
+ namespaceId: role.namespaceId
98
+ }));
39
99
  }
40
- const tables = {};
41
- for (const [tableName, sqlTable] of Object.entries(sqlIr.tables)) tables[tableName] = new PostgresTableIR({
42
- name: sqlTable.name,
43
- columns: sqlTable.columns,
44
- foreignKeys: sqlTable.foreignKeys,
45
- uniques: sqlTable.uniques,
46
- indexes: sqlTable.indexes,
47
- ...sqlTable.primaryKey !== void 0 ? { primaryKey: sqlTable.primaryKey } : {},
48
- ...sqlTable.annotations !== void 0 ? { annotations: sqlTable.annotations } : {},
49
- ...sqlTable.checks !== void 0 ? { checks: sqlTable.checks } : {},
50
- rlsPolicies: policiesByTable.get(tableName) ?? []
51
- });
52
- for (const [tableName, policies] of policiesByTable) if (!(tableName in tables)) {
53
- const policyName = policies[0]?.name ?? "(unknown)";
54
- throw new Error(`contract-to-postgres-schema-ir: policy "${policyName}" references table "${tableName}" not present in the schema`);
55
- }
56
- return new PostgresSchemaIR({
57
- tables,
58
- roles: contract === null ? [] : Object.values(contract.storage.namespaces).flatMap((ns) => isPostgresSchema(ns) ? Object.values(ns.role) : []),
59
- pgSchemaName: "public",
60
- pgVersion: "",
100
+ return new PostgresDatabaseSchemaNode({
101
+ namespaces,
102
+ roles,
61
103
  existingSchemas: ownedSchemas,
62
- nativeEnumTypeNames: []
104
+ pgVersion: ""
105
+ });
106
+ }
107
+ //#endregion
108
+ //#region src/core/migrations/diff-database-schema.ts
109
+ /**
110
+ * The single combined database-schema comparison — the one computation the
111
+ * migration planner and the family schema verify both consume. Composes,
112
+ * once each:
113
+ *
114
+ * - the per-namespace-paired relational diff (`verifySqlSchemaTree`) → table /
115
+ * column / constraint findings as framework `SchemaIssue`s (with the
116
+ * verification-tree `root` and pass/warn/fail counts);
117
+ * - the policy diff (`diffPostgresSchema` over the two trees) → RLS policy
118
+ * presence as `SchemaDiffIssue`s, ownership-filtered to the contract's owned
119
+ * schemas.
120
+ *
121
+ * `diffPostgresDatabaseSchema` and `verifyPostgresDatabaseSchema` both read
122
+ * this single result, so the relational walk runs once per caller — never
123
+ * once for the diff and again for the verify tree.
124
+ */
125
+ function computePostgresSchemaComparison(input) {
126
+ const postgresContract = blindCast(input.contract);
127
+ const relational = verifySqlSchemaTree({
128
+ contract: input.contract,
129
+ actualSchema: input.actualSchema,
130
+ buildExpectedSchema: (scopedContract) => contractToPostgresDatabaseSchemaNode(blindCast(scopedContract), { annotationNamespace: "pg" }),
131
+ strict: input.strict,
132
+ typeMetadataRegistry: input.typeMetadataRegistry,
133
+ frameworkComponents: input.frameworkComponents,
134
+ normalizeDefault: parsePostgresDefault,
135
+ normalizeNativeType: normalizeSchemaNativeType
136
+ });
137
+ PostgresDatabaseSchemaNode.assert(input.actualSchema);
138
+ const expected = contractToPostgresDatabaseSchemaNode(postgresContract, { annotationNamespace: "pg" });
139
+ return {
140
+ relational,
141
+ schemaDiffIssues: filterIssuesByOwnership(diffPostgresSchema(expected, input.actualSchema), ownedSchemaNames(expected))
142
+ };
143
+ }
144
+ /**
145
+ * The `SchemaDiffer` for Postgres: the target's black-box comparison,
146
+ * projected to the two issue lists. Namespace presence (`missing_schema` →
147
+ * `CREATE SCHEMA`) is intentionally NOT composed here: it is a planner-only
148
+ * op-generation concern (verify rejects on the relational `missing_table` a
149
+ * missing schema already produces), so the planner stitches it in around this
150
+ * diff. Control-policy suppression of the policy issues is likewise a
151
+ * per-consumer post-step (verify filters the issues; the planner filters the
152
+ * calls).
153
+ */
154
+ function diffPostgresDatabaseSchema(input) {
155
+ const { relational, schemaDiffIssues } = computePostgresSchemaComparison(input);
156
+ return new SchemaDiff(relational.schema.issues, schemaDiffIssues);
157
+ }
158
+ /**
159
+ * The same combined comparison as {@link diffPostgresDatabaseSchema}, wrapped
160
+ * in the verify envelope (`ok`/`summary`/`code`/`target`/`timings`) plus the
161
+ * pass/warn/fail tree the CLI renders — i.e. exactly the existing verify-result
162
+ * schema shape, so nothing downstream changes.
163
+ */
164
+ function verifyPostgresDatabaseSchema(input) {
165
+ const { relational, schemaDiffIssues } = computePostgresSchemaComparison(input);
166
+ return {
167
+ ...relational,
168
+ schema: {
169
+ ...relational.schema,
170
+ schemaDiffIssues
171
+ }
172
+ };
173
+ }
174
+ function ownedSchemaNames(expected) {
175
+ const policyNamespaces = Object.values(expected.namespaces).flatMap((ns) => Object.values(ns.tables).flatMap((t) => t.policies.map((p) => p.namespaceId)));
176
+ return new Set([...policyNamespaces, ...expected.existingSchemas]);
177
+ }
178
+ function renderPostgresPolicyReference(policy) {
179
+ return `policy "${policy.name}" on "${policy.namespaceId}"."${policy.tableName}"`;
180
+ }
181
+ /**
182
+ * The policy node-diff — the structural half of the combined comparison above.
183
+ * Computes RLS-policy drift between two derived schema trees:
184
+ *
185
+ * 1. Runs the framework total diff over the two `PostgresDatabaseSchemaNode`
186
+ * roots (database → namespace → table → policy).
187
+ * 2. Filters to policy-subject issues only — this is transitional: the generic
188
+ * differ walks the whole tree, but the legacy relational verifier still owns
189
+ * table/column drift, so non-policy issues are dropped here.
190
+ * 3. Remaps the message to a human-readable policy reference.
191
+ *
192
+ * Both trees are `PostgresDatabaseSchemaNode`s, so every issue node is a
193
+ * `SqlSchemaDiffNode` — narrow the framework's `SchemaDiffIssue<DiffableNode>`
194
+ * output once here (the single boundary cast), so every downstream consumer
195
+ * (the ownership filter, the planner) reads the concrete node with no cast.
196
+ *
197
+ * Ownership filtering (dropping `extra` issues in namespaces a contract doesn't
198
+ * own) is the caller's responsibility — use `filterIssuesByOwnership`.
199
+ */
200
+ function diffPostgresSchema(expected, actual) {
201
+ return blindCast(diffSchemas(expected, actual)).filter((i) => {
202
+ const node = i.expected ?? i.actual;
203
+ return node !== void 0 && PostgresPolicySchemaNode.is(node);
204
+ }).map((i) => {
205
+ const node = i.expected ?? i.actual;
206
+ if (node === void 0 || !PostgresPolicySchemaNode.is(node)) return i;
207
+ return {
208
+ ...i,
209
+ message: `${i.outcome}: ${renderPostgresPolicyReference(node)}`
210
+ };
211
+ });
212
+ }
213
+ /**
214
+ * Filters `extra` policy issues to those in owned namespaces. Call after
215
+ * `diffPostgresSchema` with the union of namespace ids from the expected tree's
216
+ * policies and its `existingSchemas`.
217
+ */
218
+ function filterIssuesByOwnership(issues, ownedSchemaNameSet) {
219
+ return issues.filter((i) => {
220
+ if (i.outcome !== "extra") return true;
221
+ if (i.actual === void 0) return false;
222
+ return PostgresPolicySchemaNode.is(i.actual) && ownedSchemaNameSet.has(i.actual.namespaceId);
63
223
  });
64
224
  }
65
225
  //#endregion
@@ -197,41 +357,6 @@ function resolvePostgresIssueControlPolicySubject(issue, contract) {
197
357
  }
198
358
  }
199
359
  //#endregion
200
- //#region src/core/migrations/diff-postgres-schema.ts
201
- function renderPostgresPolicyReference(policy) {
202
- return `policy "${policy.name}" on "${policy.namespaceId}"."${policy.tableName}"`;
203
- }
204
- /**
205
- * Computes schema drift between two derived schema IRs.
206
- *
207
- * 1. Runs the framework total diff.
208
- * 2. Filters to policy-subject issues only — this is transitional: the generic
209
- * differ walks the whole tree, but the legacy relational verifier still owns
210
- * table/column drift, so non-policy issues are dropped here.
211
- * 3. Remaps the message to a human-readable policy reference.
212
- *
213
- * Ownership filtering (dropping `extra` issues in namespaces a contract doesn't
214
- * own) is the caller's responsibility — use `filterIssuesByOwnership`.
215
- */
216
- function diffPostgresSchema(expected, actual) {
217
- return diffSchemas(expected, ensurePostgresSchemaIR(actual)).filter((i) => isPostgresRlsPolicy(i.expected ?? i.actual)).map((i) => {
218
- const policy = i.expected ?? i.actual;
219
- if (!isPostgresRlsPolicy(policy)) return i;
220
- return {
221
- ...i,
222
- message: `${i.outcome}: ${renderPostgresPolicyReference(policy)}`
223
- };
224
- });
225
- }
226
- /**
227
- * Filters `extra` policy issues to those in owned namespaces. Call after
228
- * `diffPostgresSchema` with the union of namespace ids from the expected IR's
229
- * policies and its `existingSchemas`.
230
- */
231
- function filterIssuesByOwnership(issues, ownedSchemaNames) {
232
- return issues.filter((i) => i.outcome !== "extra" || isPostgresRlsPolicy(i.actual) && ownedSchemaNames.has(i.actual.namespaceId));
233
- }
234
- //#endregion
235
360
  //#region src/core/migrations/verify-postgres-namespaces.ts
236
361
  /**
237
362
  * Resolves the live-database schema name for a given namespace
@@ -248,25 +373,32 @@ function resolveDdlSchemaName(storage, namespaceId) {
248
373
  return namespaceId;
249
374
  }
250
375
  /**
251
- * Reads the introspected list of schema names from a `PostgresSchemaIR`.
252
- * Defaults to the always-present `public` schema when the schema IR is not a
253
- * `PostgresSchemaIR` — a fresh Postgres database always carries `public`
254
- * (unless an operator dropped it manually), so any verifier path that runs
255
- * without an enriched introspection still suppresses the redundant
256
- * `CREATE SCHEMA "public"`.
376
+ * Reads the introspected list of schema names from the database-root schema
377
+ * node. `existingSchemas` is database-level, so it lives on the
378
+ * `PostgresDatabaseSchemaNode` root not on the per-schema namespace nodes.
379
+ *
380
+ * Defaults to the always-present `public` schema when the node is not the
381
+ * database root — a fresh Postgres database always carries `public` (unless an
382
+ * operator dropped it manually), so any verifier path that runs without an
383
+ * enriched introspection still suppresses the redundant `CREATE SCHEMA
384
+ * "public"`.
257
385
  *
258
386
  * Production introspection (`PostgresControlAdapter.introspect`) is the
259
387
  * authoritative source: it queries `pg_namespace` and sets `existingSchemas`
260
- * on the returned `PostgresSchemaIR`. Tests that want to assert against a
261
- * richer initial state construct a `PostgresSchemaIR` explicitly.
388
+ * on the returned root. Tests that want to assert against a richer initial
389
+ * state construct a `PostgresDatabaseSchemaNode` explicitly.
262
390
  */
263
391
  function existingSchemasFromSchema(schema) {
264
- if (isPostgresSchemaIR(schema)) return schema.existingSchemas;
392
+ if (PostgresDatabaseSchemaNode.is(schema)) return schema.existingSchemas;
265
393
  return ["public"];
266
394
  }
267
395
  /**
268
396
  * Emits a `missing_schema` issue for every contract-declared Postgres
269
- * namespace whose live container does not yet exist.
397
+ * namespace whose live container does not yet exist. The planner's
398
+ * `collectSchemaIssues` prepends these to the relational findings so a
399
+ * multi-schema plan emits `CREATE SCHEMA` before the tables that need it —
400
+ * a planner-only concern (verify already rejects via the `missing_table` a
401
+ * missing schema produces), so this is not part of the shared diff.
270
402
  *
271
403
  * A namespace's live container is the schema returned by its
272
404
  * polymorphic `ddlSchemaName(storage)` method — named schemas resolve
@@ -294,6 +426,7 @@ function verifyPostgresNamespacePresence(input) {
294
426
  if (existing.has(ddlName)) continue;
295
427
  issues.push({
296
428
  kind: "missing_schema",
429
+ reason: "not-found",
297
430
  namespaceId,
298
431
  message: `Schema "${ddlName}" is missing from database`
299
432
  });
@@ -339,9 +472,19 @@ var PostgresMigrationPlanner = class {
339
472
  const schemaName = options.schemaName ?? Object.keys(options.contract.storage.namespaces).find((id) => id !== UNBOUND_NAMESPACE_ID) ?? UNBOUND_NAMESPACE_ID;
340
473
  const policyResult = this.ensureAdditivePolicy(options.policy);
341
474
  if (policyResult) return policyResult;
342
- const schemaIssues = this.collectSchemaIssues(options);
475
+ PostgresDatabaseSchemaNode.assert(options.schema);
476
+ const rawDiff = diffPostgresDatabaseSchema({
477
+ contract: options.contract,
478
+ actualSchema: options.schema,
479
+ strict: options.policy.allowedOperationClasses.includes("widening") || options.policy.allowedOperationClasses.includes("destructive"),
480
+ typeMetadataRegistry: /* @__PURE__ */ new Map(),
481
+ frameworkComponents: options.frameworkComponents
482
+ });
483
+ const databaseDiff = options.keepDiffIssue ? rawDiff.filter(options.keepDiffIssue) : rawDiff;
484
+ const schemaIssues = this.collectSchemaIssues(options, databaseDiff.issues);
343
485
  const codecHooks = extractCodecControlHooks(options.frameworkComponents);
344
486
  const storageTypes = options.contract.storage.types ?? {};
487
+ const relationalSchema = relationalNamespaceNode(options.schema, schemaName);
345
488
  const issuePartition = partitionIssuesByControlPolicy({
346
489
  issues: schemaIssues,
347
490
  contract: options.contract,
@@ -356,14 +499,14 @@ var PostgresMigrationPlanner = class {
356
499
  schemaName,
357
500
  codecHooks,
358
501
  storageTypes,
359
- schema: options.schema,
502
+ ...ifDefined("schema", relationalSchema),
360
503
  policy: options.policy,
361
504
  frameworkComponents: options.frameworkComponents,
362
505
  strategies: postgresPlannerStrategies
363
506
  });
364
507
  if (!result.ok) return plannerFailure(result.failure);
365
508
  const schemaDiffPartition = partitionCallsByControlPolicy({
366
- calls: this.planPostgresSchemaDiff(options),
509
+ calls: this.planPostgresSchemaDiff(options, databaseDiff.schemaDiffIssues),
367
510
  contract: options.contract,
368
511
  resolveControlPolicySubject: (call) => resolvePostgresCallControlPolicySubject(call, options.contract),
369
512
  resolveFactoryName: (call) => call.factoryName,
@@ -399,26 +542,31 @@ var PostgresMigrationPlanner = class {
399
542
  ...warnings.length > 0 ? { warnings: Object.freeze(warnings) } : {}
400
543
  });
401
544
  }
402
- planPostgresSchemaDiff(options) {
403
- assertPostgresSchemaIR(options.schema);
404
- const expected = contractToPostgresSchemaIR(blindCast(options.contract), { annotationNamespace: "pg" });
405
- const filteredDiffIssues = filterIssuesByOwnership(diffPostgresSchema(expected, ensurePostgresSchemaIR(options.schema)), new Set([...expected.rlsPolicies.map((p) => p.namespaceId), ...expected.existingSchemas]));
545
+ /**
546
+ * Maps the RLS policy presence findings of the shared
547
+ * `diffPostgresDatabaseSchema` (already ownership-filtered) into
548
+ * `ENABLE RLS` / `CREATE POLICY` / `DROP POLICY` ops. It no longer re-diffs —
549
+ * it consumes the `schemaDiffIssues` of the one combined diff.
550
+ */
551
+ planPostgresSchemaDiff(options, filteredDiffIssues) {
406
552
  const allowsDestructive = options.policy.allowedOperationClasses.includes("destructive");
407
553
  const calls = [];
408
554
  const seenEnableTables = /* @__PURE__ */ new Set();
409
555
  for (const issue of filteredDiffIssues) if (issue.outcome === "missing") {
410
- assertPostgresRlsPolicy(issue.expected);
411
- const schemaForTable = resolveDdlSchemaForNamespaceStorage(options.contract.storage, issue.expected.namespaceId, options.schema);
412
- const tableKey = `${schemaForTable}.${issue.expected.tableName}`;
556
+ const expected = issue.expected;
557
+ PostgresPolicySchemaNode.assert(expected);
558
+ const schemaForTable = resolveDdlSchemaForNamespaceStorage(options.contract.storage, expected.namespaceId);
559
+ const tableKey = `${schemaForTable}.${expected.tableName}`;
413
560
  if (!seenEnableTables.has(tableKey)) {
414
561
  seenEnableTables.add(tableKey);
415
- calls.push(new EnableRowLevelSecurityCall(schemaForTable, issue.expected.tableName));
562
+ calls.push(new EnableRowLevelSecurityCall(schemaForTable, expected.tableName));
416
563
  }
417
- calls.push(new CreatePostgresRlsPolicyCall(schemaForTable, issue.expected.tableName, issue.expected));
564
+ calls.push(new CreatePostgresRlsPolicyCall(schemaForTable, expected.tableName, policyNodeToContractPolicy(expected)));
418
565
  } else if (issue.outcome === "extra" && allowsDestructive) {
419
- assertPostgresRlsPolicy(issue.actual);
420
- const schemaForTable = resolveDdlSchemaForNamespaceStorage(options.contract.storage, issue.actual.namespaceId, options.schema);
421
- calls.push(new DropPostgresRlsPolicyCall(schemaForTable, issue.actual.tableName, issue.actual.name));
566
+ const actual = issue.actual;
567
+ PostgresPolicySchemaNode.assert(actual);
568
+ const schemaForTable = resolveDdlSchemaForNamespaceStorage(options.contract.storage, actual.namespaceId);
569
+ calls.push(new DropPostgresRlsPolicyCall(schemaForTable, actual.tableName, actual.name));
422
570
  }
423
571
  return calls;
424
572
  }
@@ -430,27 +578,63 @@ var PostgresMigrationPlanner = class {
430
578
  }]);
431
579
  return null;
432
580
  }
433
- collectSchemaIssues(options) {
434
- const allowed = options.policy.allowedOperationClasses;
435
- const strict = allowed.includes("widening") || allowed.includes("destructive");
436
- const verifyResult = verifySqlSchema({
437
- contract: options.contract,
438
- schema: options.schema,
439
- strict,
440
- typeMetadataRegistry: /* @__PURE__ */ new Map(),
441
- frameworkComponents: options.frameworkComponents,
442
- normalizeDefault: parsePostgresDefault,
443
- normalizeNativeType: normalizeSchemaNativeType
444
- });
581
+ /**
582
+ * The structural issue list `planIssues` consumes: the relational findings
583
+ * from the shared `diffPostgresDatabaseSchema` plus namespace presence.
584
+ *
585
+ * Schema presence (`missing_schema` → `CREATE SCHEMA`) is a planner-only
586
+ * op-generation concern, so it is stitched in here rather than inside the
587
+ * shared diff — verify never needs it (a missing schema already surfaces as
588
+ * `missing_table` in the relational findings). It reads `existingSchemas` off
589
+ * the database root, so it takes the whole tree. Policy drift is handled
590
+ * separately via `planPostgresSchemaDiff` from the same shared diff's
591
+ * `schemaDiffIssues`.
592
+ */
593
+ collectSchemaIssues(options, relationalIssues) {
445
594
  const namespaceIssues = verifyPostgresNamespacePresence({
446
595
  contract: options.contract,
447
596
  schema: options.schema
448
597
  });
449
- if (namespaceIssues.length === 0) return verifyResult.schema.issues;
450
- return [...namespaceIssues, ...verifyResult.schema.issues];
598
+ if (namespaceIssues.length === 0) return relationalIssues;
599
+ return [...namespaceIssues, ...relationalIssues];
451
600
  }
452
601
  };
602
+ /**
603
+ * Returns the one namespace node whose tables the relational strategy layer
604
+ * probes for live-table existence — the node matching the planner's resolved
605
+ * schema name, or the first namespace when none matches. `undefined` when the
606
+ * tree has no namespaces, so the strategy context uses its empty-schema default.
607
+ *
608
+ * The relational strategies key tables by bare name, so they can only probe one
609
+ * namespace at a time; probing across every namespace at once is future work.
610
+ */
611
+ function relationalNamespaceNode(schema, schemaName) {
612
+ const namespaceNodes = Object.values(schema.namespaces);
613
+ return namespaceNodes.find((node) => node.schemaName === schemaName) ?? namespaceNodes[0];
614
+ }
615
+ /**
616
+ * Rebuilds the `PostgresRlsPolicy` contract entity `CreatePostgresRlsPolicyCall`
617
+ * carries (its `renderTypeScript`/`createRlsPolicy` paths serialize the whole
618
+ * entity, `namespaceId` included). This reconstructs rather than looking the
619
+ * original up in the contract on purpose: the diff node's `namespaceId` is the
620
+ * *resolved DDL schema* (set when the expected tree was built), which is the
621
+ * value the emitted op must carry; the contract-stored entity holds the raw,
622
+ * pre-resolution coordinate, so a lookup would change the migration output.
623
+ */
624
+ function policyNodeToContractPolicy(node) {
625
+ return new PostgresRlsPolicy({
626
+ name: node.name,
627
+ prefix: node.prefix,
628
+ tableName: node.tableName,
629
+ namespaceId: node.namespaceId,
630
+ operation: node.operation,
631
+ roles: [...node.roles],
632
+ ...ifDefined("using", node.using),
633
+ ...ifDefined("withCheck", node.withCheck),
634
+ permissive: node.permissive
635
+ });
636
+ }
453
637
  //#endregion
454
- export { contractToPostgresSchemaIR as i, diffPostgresSchema as n, filterIssuesByOwnership as r, createPostgresMigrationPlanner as t };
638
+ export { contractToPostgresDatabaseSchemaNode as i, diffPostgresDatabaseSchema as n, verifyPostgresDatabaseSchema as r, createPostgresMigrationPlanner as t };
455
639
 
456
- //# sourceMappingURL=planner-BQ7lSqbm.mjs.map
640
+ //# sourceMappingURL=planner-CG2pLHk-.mjs.map