@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.
- package/dist/{authoring-CZmSQriw.mjs → authoring-BX1WHIdN.mjs} +2 -2
- package/dist/authoring-BX1WHIdN.mjs.map +1 -0
- package/dist/contract-free.mjs +2 -2
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +633 -16
- package/dist/control.mjs.map +1 -1
- package/dist/{ddl-CGf6UABz.mjs → ddl-BmLIe1z_.mjs} +2 -2
- package/dist/{ddl-CGf6UABz.mjs.map → ddl-BmLIe1z_.mjs.map} +1 -1
- package/dist/{descriptor-meta-Du6olWQI.mjs → descriptor-meta-tjatyhtZ.mjs} +3 -3
- package/dist/{descriptor-meta-Du6olWQI.mjs.map → descriptor-meta-tjatyhtZ.mjs.map} +1 -1
- package/dist/{issue-planner-C-CwZd8D.mjs → issue-planner-DbFvaAgu.mjs} +5 -5
- package/dist/issue-planner-DbFvaAgu.mjs.map +1 -0
- package/dist/issue-planner.d.mts +1 -1
- package/dist/issue-planner.mjs +1 -1
- package/dist/migration.d.mts +2 -2
- package/dist/migration.mjs +2 -2
- package/dist/{op-factory-call-DPJR7tJc.d.mts → op-factory-call-CJWUxyUW.d.mts} +2 -2
- package/dist/{op-factory-call-DPJR7tJc.d.mts.map → op-factory-call-CJWUxyUW.d.mts.map} +1 -1
- package/dist/{op-factory-call-DArx_1HM.mjs → op-factory-call-t65c4jyY.mjs} +3 -3
- package/dist/op-factory-call-t65c4jyY.mjs.map +1 -0
- package/dist/op-factory-call.d.mts +1 -1
- package/dist/op-factory-call.mjs +1 -1
- package/dist/pack.d.mts +2 -2
- package/dist/pack.mjs +1 -1
- package/dist/{planner-BQ7lSqbm.mjs → planner-CG2pLHk-.mjs} +310 -126
- package/dist/planner-CG2pLHk-.mjs.map +1 -0
- package/dist/{planner-produced-postgres-migration-3ppG0t3i.mjs → planner-produced-postgres-migration-BolD0Mvc.mjs} +2 -2
- package/dist/{planner-produced-postgres-migration-3ppG0t3i.mjs.map → planner-produced-postgres-migration-BolD0Mvc.mjs.map} +1 -1
- package/dist/{planner-produced-postgres-migration-BauTSigX.d.mts → planner-produced-postgres-migration-Cz9--XOT.d.mts} +2 -2
- package/dist/{planner-produced-postgres-migration-BauTSigX.d.mts.map → planner-produced-postgres-migration-Cz9--XOT.d.mts.map} +1 -1
- package/dist/planner-produced-postgres-migration.d.mts +1 -1
- package/dist/planner-produced-postgres-migration.mjs +1 -1
- package/dist/{planner-sql-checks-CyjaoKhm.mjs → planner-sql-checks-DE9c4519.mjs} +2 -2
- package/dist/{planner-sql-checks-CyjaoKhm.mjs.map → planner-sql-checks-DE9c4519.mjs.map} +1 -1
- package/dist/planner-sql-checks.mjs +1 -1
- package/dist/planner.d.mts +73 -22
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +2 -2
- package/dist/{postgres-contract-view-CoRZewAp.mjs → postgres-contract-view-BegduLr6.mjs} +3 -3
- package/dist/{postgres-contract-view-CoRZewAp.mjs.map → postgres-contract-view-BegduLr6.mjs.map} +1 -1
- package/dist/postgres-database-schema-node-os8ogEap.d.mts +183 -0
- package/dist/postgres-database-schema-node-os8ogEap.d.mts.map +1 -0
- package/dist/{postgres-migration-BVjq0iFW.mjs → postgres-migration-BfMmK6tv.mjs} +3 -3
- package/dist/{postgres-migration-BVjq0iFW.mjs.map → postgres-migration-BfMmK6tv.mjs.map} +1 -1
- package/dist/{postgres-migration-UtGCjw5j.d.mts → postgres-migration-D7kNVWVG.d.mts} +2 -2
- package/dist/{postgres-migration-UtGCjw5j.d.mts.map → postgres-migration-D7kNVWVG.d.mts.map} +1 -1
- package/dist/{postgres-rls-policy-12wAEQhs.d.mts → postgres-rls-policy--CSddvMR.d.mts} +8 -16
- package/dist/postgres-rls-policy--CSddvMR.d.mts.map +1 -0
- package/dist/{postgres-schema-DLcc3W2-.mjs → postgres-schema-BLIHBEfX.mjs} +34 -64
- package/dist/postgres-schema-BLIHBEfX.mjs.map +1 -0
- package/dist/{postgres-schema-DJUEkWg-.d.mts → postgres-schema-BywoeKlt.d.mts} +9 -10
- package/dist/postgres-schema-BywoeKlt.d.mts.map +1 -0
- package/dist/postgres-table-schema-node-1nNc8XDT.mjs +248 -0
- package/dist/postgres-table-schema-node-1nNc8XDT.mjs.map +1 -0
- package/dist/resolve-ddl-schema-CL1RR1mx.mjs +20 -0
- package/dist/resolve-ddl-schema-CL1RR1mx.mjs.map +1 -0
- package/dist/runtime.mjs +1 -1
- package/dist/schema-ir-annotations.d.mts +9 -3
- package/dist/schema-ir-annotations.d.mts.map +1 -1
- package/dist/schema-ir-annotations.mjs +1 -1
- package/dist/types.d.mts +4 -4
- package/dist/types.mjs +3 -3
- package/package.json +20 -19
- package/src/core/authoring.ts +2 -2
- package/src/core/entity-kinds.ts +2 -2
- package/src/core/migrations/contract-to-postgres-database-schema-node.ts +125 -0
- package/src/core/migrations/diff-database-schema.ts +191 -0
- package/src/core/migrations/issue-planner.ts +1 -1
- package/src/core/migrations/op-factory-call.ts +1 -1
- package/src/core/migrations/operations/rls.ts +1 -1
- package/src/core/migrations/planner.ts +117 -68
- package/src/core/{schema-ir/postgres-schema-ir-annotations.ts → migrations/resolve-ddl-schema.ts} +8 -6
- package/src/core/migrations/runner.ts +13 -16
- package/src/core/migrations/verify-postgres-namespaces.ts +22 -14
- package/src/core/{schema-ir/postgres-rls-policy.ts → postgres-rls-policy.ts} +6 -43
- package/src/core/{schema-ir/postgres-role.ts → postgres-role.ts} +6 -21
- package/src/core/postgres-schema.ts +2 -2
- package/src/core/psl-infer/infer-psl-contract.ts +834 -0
- package/src/core/psl-infer/postgres-default-mapping.ts +16 -0
- package/src/core/psl-infer/postgres-type-map.ts +157 -0
- package/src/core/schema-ir/postgres-database-schema-node.ts +63 -0
- package/src/core/schema-ir/postgres-namespace-schema-node.ts +61 -0
- package/src/core/schema-ir/postgres-policy-schema-node.ts +93 -0
- package/src/core/schema-ir/postgres-role-schema-node.ts +66 -0
- package/src/core/schema-ir/{postgres-table-ir.ts → postgres-table-schema-node.ts} +29 -18
- package/src/core/schema-ir/schema-node-kinds.ts +28 -0
- package/src/exports/control.ts +30 -2
- package/src/exports/planner.ts +2 -5
- package/src/exports/schema-ir-annotations.ts +1 -1
- package/src/exports/types.ts +23 -15
- package/dist/authoring-CZmSQriw.mjs.map +0 -1
- package/dist/issue-planner-C-CwZd8D.mjs.map +0 -1
- package/dist/op-factory-call-DArx_1HM.mjs.map +0 -1
- package/dist/planner-BQ7lSqbm.mjs.map +0 -1
- package/dist/postgres-rls-policy-12wAEQhs.d.mts.map +0 -1
- package/dist/postgres-schema-DJUEkWg-.d.mts.map +0 -1
- package/dist/postgres-schema-DLcc3W2-.mjs.map +0 -1
- package/dist/postgres-schema-ir-BJl_a3J7.d.mts +0 -102
- package/dist/postgres-schema-ir-BJl_a3J7.d.mts.map +0 -1
- package/dist/postgres-schema-ir-I_FiUndw.mjs +0 -133
- package/dist/postgres-schema-ir-I_FiUndw.mjs.map +0 -1
- package/dist/postgres-schema-ir-annotations-BbDSiwx2.mjs +0 -14
- package/dist/postgres-schema-ir-annotations-BbDSiwx2.mjs.map +0 -1
- package/src/core/migrations/contract-to-postgres-schema-ir.ts +0 -91
- package/src/core/migrations/diff-postgres-schema.ts +0 -55
- package/src/core/schema-ir/postgres-schema-ir.ts +0 -138
|
@@ -1,65 +1,225 @@
|
|
|
1
|
-
import { d as
|
|
2
|
-
import { a as
|
|
3
|
-
import { t as resolveDdlSchemaForNamespaceStorage } from "./
|
|
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-
|
|
7
|
-
import { n as postgresPlannerStrategies, t as planIssues } from "./issue-planner-
|
|
8
|
-
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-
|
|
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 {
|
|
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 {
|
|
14
|
-
import { diffSchemas } from "@prisma-next/framework-components/control";
|
|
15
|
-
//#region src/core/migrations/contract-to-postgres-schema-
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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
|
|
252
|
-
*
|
|
253
|
-
* `
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
* `
|
|
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
|
|
261
|
-
*
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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
|
-
|
|
411
|
-
|
|
412
|
-
const
|
|
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,
|
|
562
|
+
calls.push(new EnableRowLevelSecurityCall(schemaForTable, expected.tableName));
|
|
416
563
|
}
|
|
417
|
-
calls.push(new CreatePostgresRlsPolicyCall(schemaForTable,
|
|
564
|
+
calls.push(new CreatePostgresRlsPolicyCall(schemaForTable, expected.tableName, policyNodeToContractPolicy(expected)));
|
|
418
565
|
} else if (issue.outcome === "extra" && allowsDestructive) {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
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
|
|
450
|
-
return [...namespaceIssues, ...
|
|
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 {
|
|
638
|
+
export { contractToPostgresDatabaseSchemaNode as i, diffPostgresDatabaseSchema as n, verifyPostgresDatabaseSchema as r, createPostgresMigrationPlanner as t };
|
|
455
639
|
|
|
456
|
-
//# sourceMappingURL=planner-
|
|
640
|
+
//# sourceMappingURL=planner-CG2pLHk-.mjs.map
|