@prisma-next/target-postgres 0.14.0-dev.2 → 0.14.0-dev.20
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/canonicalize-Bn3BM1Cn.mjs +46 -0
- package/dist/canonicalize-Bn3BM1Cn.mjs.map +1 -0
- package/dist/canonicalize-CAbXtVtB.d.mts +37 -0
- package/dist/canonicalize-CAbXtVtB.d.mts.map +1 -0
- package/dist/codecs.mjs.map +1 -1
- package/dist/contract-free.d.mts +8 -3
- package/dist/contract-free.d.mts.map +1 -1
- package/dist/contract-free.mjs +3 -3
- package/dist/control.d.mts +1 -1
- package/dist/control.mjs +6 -6
- package/dist/data-transform-BOWpliq8.mjs.map +1 -1
- package/dist/{data-transform-DDgWdB5o.d.mts → data-transform-qKy1U5V9.d.mts} +2 -2
- package/dist/{data-transform-DDgWdB5o.d.mts.map → data-transform-qKy1U5V9.d.mts.map} +1 -1
- package/dist/data-transform.d.mts +1 -1
- package/dist/{ddl-QDyOSeLc.mjs → ddl-D1K9soOg.mjs} +62 -5
- package/dist/ddl-D1K9soOg.mjs.map +1 -0
- package/dist/ddl.d.mts +3 -2
- package/dist/ddl.mjs +2 -2
- package/dist/{default-normalizer-DyyCHQWs.mjs → default-normalizer-CzV8-QSu.mjs} +58 -2
- package/dist/default-normalizer-CzV8-QSu.mjs.map +1 -0
- package/dist/default-normalizer.d.mts +1 -1
- package/dist/default-normalizer.d.mts.map +1 -1
- package/dist/default-normalizer.mjs +1 -1
- package/dist/descriptor-meta-b_SzXwen.mjs +239 -0
- package/dist/descriptor-meta-b_SzXwen.mjs.map +1 -0
- package/dist/{issue-planner-DL6g3CmE.mjs → issue-planner-CUeQ7D83.mjs} +11 -9
- package/dist/issue-planner-CUeQ7D83.mjs.map +1 -0
- package/dist/issue-planner.d.mts +3 -3
- package/dist/issue-planner.d.mts.map +1 -1
- package/dist/issue-planner.mjs +1 -1
- package/dist/migration.d.mts +3 -3
- package/dist/migration.mjs +2 -2
- package/dist/nodes-CSkPX3rI.d.mts +120 -0
- package/dist/nodes-CSkPX3rI.d.mts.map +1 -0
- package/dist/{nodes-Bbhs2rwj.mjs → nodes-Dh__NS58.mjs} +56 -2
- package/dist/nodes-Dh__NS58.mjs.map +1 -0
- package/dist/{op-factory-call-DmQEc3XV.d.mts → op-factory-call-CQ4P40QC.d.mts} +44 -13
- package/dist/op-factory-call-CQ4P40QC.d.mts.map +1 -0
- package/dist/{op-factory-call-D_p5vxwt.mjs → op-factory-call-CyqPjAwf.mjs} +199 -28
- package/dist/op-factory-call-CyqPjAwf.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 +74 -1
- package/dist/pack.d.mts.map +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/{planner-Bs_baQax.mjs → planner-D5zY__R3.mjs} +115 -23
- package/dist/planner-D5zY__R3.mjs.map +1 -0
- package/dist/{planner-ddl-builders-B2wOwLqI.mjs → planner-ddl-builders-B8Nn6nHN.mjs} +12 -27
- package/dist/planner-ddl-builders-B8Nn6nHN.mjs.map +1 -0
- package/dist/planner-ddl-builders.d.mts +2 -4
- package/dist/planner-ddl-builders.d.mts.map +1 -1
- package/dist/planner-ddl-builders.mjs +2 -2
- package/dist/{planner-produced-postgres-migration-QqHa2C2l.d.mts → planner-produced-postgres-migration-CNTSCMl7.d.mts} +3 -3
- package/dist/{planner-produced-postgres-migration-QqHa2C2l.d.mts.map → planner-produced-postgres-migration-CNTSCMl7.d.mts.map} +1 -1
- package/dist/{planner-produced-postgres-migration-Cji5vxUf.mjs → planner-produced-postgres-migration-D8JHnu5j.mjs} +2 -2
- package/dist/{planner-produced-postgres-migration-Cji5vxUf.mjs.map → planner-produced-postgres-migration-D8JHnu5j.mjs.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-jqUUGyQR.mjs → planner-sql-checks-CI6Z21mm.mjs} +2 -2
- package/dist/{planner-sql-checks-jqUUGyQR.mjs.map → planner-sql-checks-CI6Z21mm.mjs.map} +1 -1
- package/dist/planner-sql-checks.mjs +1 -1
- package/dist/{planner-target-details-CIY6tLeo.d.mts → planner-target-details-HkP4RrRO.d.mts} +2 -2
- package/dist/planner-target-details-HkP4RrRO.d.mts.map +1 -0
- package/dist/planner-target-details.d.mts +1 -1
- package/dist/planner.d.mts +35 -3
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +2 -2
- package/dist/{postgres-contract-serializer-k3TAcPMY.mjs → postgres-contract-serializer-k5eg5ZUZ.mjs} +29 -16
- package/dist/postgres-contract-serializer-k5eg5ZUZ.mjs.map +1 -0
- package/dist/{postgres-migration-B5jKrXv3.mjs → postgres-migration-BoqzWSkj.mjs} +2 -2
- package/dist/{postgres-migration-B5jKrXv3.mjs.map → postgres-migration-BoqzWSkj.mjs.map} +1 -1
- package/dist/{postgres-migration-Y4YBJqkS.d.mts → postgres-migration-DJNJHcdP.d.mts} +4 -4
- package/dist/{postgres-migration-Y4YBJqkS.d.mts.map → postgres-migration-DJNJHcdP.d.mts.map} +1 -1
- package/dist/postgres-rls-policy-D_z1osEq.d.mts +60 -0
- package/dist/postgres-rls-policy-D_z1osEq.d.mts.map +1 -0
- package/dist/postgres-role-_CCuyPCQ.d.mts +33 -0
- package/dist/postgres-role-_CCuyPCQ.d.mts.map +1 -0
- package/dist/{postgres-schema-COGZ1ark.mjs → postgres-schema-BVDr_61a.mjs} +139 -3
- package/dist/postgres-schema-BVDr_61a.mjs.map +1 -0
- package/dist/postgres-schema-D0h4lsw2.d.mts +158 -0
- package/dist/postgres-schema-D0h4lsw2.d.mts.map +1 -0
- package/dist/postgres-schema-ir-BF0X8jit.mjs +66 -0
- package/dist/postgres-schema-ir-BF0X8jit.mjs.map +1 -0
- package/dist/postgres-schema-ir-BQ3RpZMz.d.mts +60 -0
- package/dist/postgres-schema-ir-BQ3RpZMz.d.mts.map +1 -0
- package/dist/postgres-schema-ir-annotations-El62Sywa.mjs +14 -0
- package/dist/postgres-schema-ir-annotations-El62Sywa.mjs.map +1 -0
- package/dist/render-ops.d.mts +1 -1
- package/dist/rls-canonicalize.d.mts +2 -0
- package/dist/rls-canonicalize.mjs +2 -0
- package/dist/runtime.d.mts +3 -2
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +2 -2
- package/dist/schema-ir-annotations.d.mts +8 -0
- package/dist/schema-ir-annotations.d.mts.map +1 -0
- package/dist/schema-ir-annotations.mjs +2 -0
- package/dist/types.d.mts +5 -140
- package/dist/types.mjs +3 -2
- package/package.json +21 -17
- package/src/contract-free/checks.ts +73 -0
- package/src/contract-free/ddl.ts +45 -1
- package/src/core/authoring.ts +115 -1
- package/src/core/codecs.ts +1 -1
- package/src/core/ddl/nodes.ts +94 -2
- package/src/core/default-normalizer.ts +60 -2
- package/src/core/descriptor-meta.ts +4 -0
- package/src/core/entity-kinds.ts +16 -0
- package/src/core/migrations/bound-schema.ts +10 -0
- package/src/core/migrations/control-policy.ts +3 -0
- package/src/core/migrations/issue-planner.ts +8 -0
- package/src/core/migrations/op-factory-call.ts +106 -21
- package/src/core/migrations/operations/columns.ts +24 -12
- package/src/core/migrations/operations/data-transform.ts +6 -0
- package/src/core/migrations/operations/rls.ts +106 -0
- package/src/core/migrations/operations/shared.ts +0 -30
- package/src/core/migrations/planner-ddl-builders.ts +15 -56
- package/src/core/migrations/planner-recipes.ts +31 -13
- package/src/core/migrations/planner-strategies.ts +3 -11
- package/src/core/migrations/planner-target-details.ts +3 -1
- package/src/core/migrations/planner.ts +99 -1
- package/src/core/migrations/verify-postgres-namespaces.ts +12 -15
- package/src/core/migrations/verify-postgres-rls-policies.ts +62 -0
- package/src/core/postgres-contract-serializer.ts +43 -19
- package/src/core/postgres-rls-policy.ts +103 -0
- package/src/core/postgres-role.ts +53 -0
- package/src/core/postgres-schema-ir-annotations.ts +22 -0
- package/src/core/postgres-schema-ir.ts +101 -0
- package/src/core/postgres-schema.ts +24 -4
- package/src/core/postgres-validators.ts +20 -0
- package/src/core/rls/canonicalize.ts +48 -0
- package/src/exports/contract-free.ts +7 -1
- package/src/exports/ddl.ts +4 -0
- package/src/exports/planner-ddl-builders.ts +0 -2
- package/src/exports/planner.ts +1 -0
- package/src/exports/rls-canonicalize.ts +6 -0
- package/src/exports/schema-ir-annotations.ts +1 -0
- package/src/exports/types.ts +12 -0
- package/dist/ddl-QDyOSeLc.mjs.map +0 -1
- package/dist/default-normalizer-DyyCHQWs.mjs.map +0 -1
- package/dist/descriptor-meta-CpGygXpI.mjs +0 -140
- package/dist/descriptor-meta-CpGygXpI.mjs.map +0 -1
- package/dist/issue-planner-DL6g3CmE.mjs.map +0 -1
- package/dist/nodes-Bbhs2rwj.mjs.map +0 -1
- package/dist/nodes-pLeLgdis.d.mts +0 -67
- package/dist/nodes-pLeLgdis.d.mts.map +0 -1
- package/dist/op-factory-call-D_p5vxwt.mjs.map +0 -1
- package/dist/op-factory-call-DmQEc3XV.d.mts.map +0 -1
- package/dist/planner-Bs_baQax.mjs.map +0 -1
- package/dist/planner-ddl-builders-B2wOwLqI.mjs.map +0 -1
- package/dist/planner-target-details-CIY6tLeo.d.mts.map +0 -1
- package/dist/postgres-contract-serializer-k3TAcPMY.mjs.map +0 -1
- package/dist/postgres-schema-COGZ1ark.mjs.map +0 -1
- package/dist/types.d.mts.map +0 -1
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as assertPostgresRlsPolicy, r as isPostgresSchema } from "./postgres-schema-BVDr_61a.mjs";
|
|
2
|
+
import { t as parsePostgresDefault } from "./default-normalizer-CzV8-QSu.mjs";
|
|
2
3
|
import { t as normalizeSchemaNativeType } from "./native-type-normalizer-Bc9XJzWC.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { n as isPostgresSchemaIR } from "./postgres-schema-ir-BF0X8jit.mjs";
|
|
5
|
+
import { t as resolveDdlSchemaForNamespaceStorage } from "./postgres-schema-ir-annotations-El62Sywa.mjs";
|
|
6
|
+
import { C as EnableRowLevelSecurityCall, d as CreatePostgresRlsPolicyCall, x as DropPostgresRlsPolicyCall } from "./op-factory-call-CyqPjAwf.mjs";
|
|
7
|
+
import { n as postgresPlannerStrategies, t as planIssues } from "./issue-planner-CUeQ7D83.mjs";
|
|
8
|
+
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-D8JHnu5j.mjs";
|
|
6
9
|
import { blindCast } from "@prisma-next/utils/casts";
|
|
7
10
|
import { extractCodecControlHooks, partitionCallsByControlPolicy, partitionIssuesByControlPolicy, planFieldEventOperations, plannerFailure } from "@prisma-next/family-sql/control";
|
|
8
11
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
9
|
-
import { verifySqlSchema } from "@prisma-next/family-sql/schema-verify";
|
|
10
12
|
import { UNBOUND_NAMESPACE_ID, entityAt } from "@prisma-next/framework-components/ir";
|
|
13
|
+
import { verifySqlSchema } from "@prisma-next/family-sql/schema-verify";
|
|
14
|
+
import { diffNodes } from "@prisma-next/framework-components/control";
|
|
11
15
|
//#region src/core/migrations/control-policy.ts
|
|
12
16
|
/**
|
|
13
17
|
* Factory calls that create a whole, previously-absent top-level storage
|
|
@@ -20,7 +24,12 @@ import { UNBOUND_NAMESPACE_ID, entityAt } from "@prisma-next/framework-component
|
|
|
20
24
|
* extension-contributed factories — is treated as NOT object-creation, so it
|
|
21
25
|
* is suppressed under `tolerated` rather than permissively emitted.
|
|
22
26
|
*/
|
|
23
|
-
const OBJECT_CREATION_FACTORIES = new Set([
|
|
27
|
+
const OBJECT_CREATION_FACTORIES = new Set([
|
|
28
|
+
"createTable",
|
|
29
|
+
"createSchema",
|
|
30
|
+
"createRlsPolicy",
|
|
31
|
+
"enableRowLevelSecurity"
|
|
32
|
+
]);
|
|
24
33
|
function createsNewTopLevelObject(call) {
|
|
25
34
|
return OBJECT_CREATION_FACTORIES.has(call.factoryName);
|
|
26
35
|
}
|
|
@@ -95,7 +104,8 @@ function resolvePostgresCallControlPolicySubject(call, contract) {
|
|
|
95
104
|
*/
|
|
96
105
|
const POSTGRES_ISSUE_CREATION_FACTORY = Object.freeze({
|
|
97
106
|
missing_schema: "createSchema",
|
|
98
|
-
missing_table: "createTable"
|
|
107
|
+
missing_table: "createTable",
|
|
108
|
+
missing_rls_policy: "createRlsPolicy"
|
|
99
109
|
});
|
|
100
110
|
function resolvePostgresIssueCreationFactoryName(issue) {
|
|
101
111
|
return POSTGRES_ISSUE_CREATION_FACTORY[issue.kind];
|
|
@@ -152,22 +162,20 @@ function resolveDdlSchemaName(storage, namespaceId) {
|
|
|
152
162
|
return namespaceId;
|
|
153
163
|
}
|
|
154
164
|
/**
|
|
155
|
-
* Reads the introspected list of schema names from
|
|
156
|
-
*
|
|
157
|
-
* `
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
165
|
+
* Reads the introspected list of schema names from a `PostgresSchemaIR`.
|
|
166
|
+
* Defaults to the always-present `public` schema when the schema IR is not a
|
|
167
|
+
* `PostgresSchemaIR` — a fresh Postgres database always carries `public`
|
|
168
|
+
* (unless an operator dropped it manually), so any verifier path that runs
|
|
169
|
+
* without an enriched introspection still suppresses the redundant
|
|
170
|
+
* `CREATE SCHEMA "public"`.
|
|
161
171
|
*
|
|
162
172
|
* Production introspection (`PostgresControlAdapter.introspect`) is the
|
|
163
|
-
* authoritative source: it queries `pg_namespace` and
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
* explicitly via the schema IR.
|
|
173
|
+
* authoritative source: it queries `pg_namespace` and sets `existingSchemas`
|
|
174
|
+
* on the returned `PostgresSchemaIR`. Tests that want to assert against a
|
|
175
|
+
* richer initial state construct a `PostgresSchemaIR` explicitly.
|
|
167
176
|
*/
|
|
168
177
|
function existingSchemasFromSchema(schema) {
|
|
169
|
-
|
|
170
|
-
if (Array.isArray(slot)) return slot.filter((s) => typeof s === "string");
|
|
178
|
+
if (isPostgresSchemaIR(schema)) return schema.existingSchemas;
|
|
171
179
|
return ["public"];
|
|
172
180
|
}
|
|
173
181
|
/**
|
|
@@ -207,6 +215,40 @@ function verifyPostgresNamespacePresence(input) {
|
|
|
207
215
|
return issues;
|
|
208
216
|
}
|
|
209
217
|
//#endregion
|
|
218
|
+
//#region src/core/migrations/verify-postgres-rls-policies.ts
|
|
219
|
+
const POSTGRES_DEFAULT_NAMESPACE_ID = "public";
|
|
220
|
+
function resolveNamespaceId(namespaceId) {
|
|
221
|
+
return namespaceId === UNBOUND_NAMESPACE_ID ? POSTGRES_DEFAULT_NAMESPACE_ID : namespaceId;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Computes RLS policy drift between the contract and the live DB schema using
|
|
225
|
+
* the generic {@link diffNodes} differ. Returns `SchemaDiffIssue[]` keyed by
|
|
226
|
+
* the full `EntityCoordinate` (plane + namespaceId + entityKind + entityName).
|
|
227
|
+
*
|
|
228
|
+
* Both sides supply `PostgresRlsPolicy` nodes that implement `DiffableNode`
|
|
229
|
+
* with a concrete namespace coordinate — the contract carries explicit
|
|
230
|
+
* `namespaceId` set during lowering (e.g. `'public'`); the introspected schema
|
|
231
|
+
* carries the resolved DDL schema name. The differ matches purely on coordinate
|
|
232
|
+
* identity; no namespace interpretation happens here.
|
|
233
|
+
*
|
|
234
|
+
* The diff is scoped to the namespaces the verified contract owns. The live
|
|
235
|
+
* schema introspection returns every policy across all DB schemas, but a policy
|
|
236
|
+
* in a namespace this contract does not own belongs to another contract space
|
|
237
|
+
* (or is external) and must not be reported as extra. Without this scope a space
|
|
238
|
+
* that owns only `auth`/`storage` (e.g. the supabase extension space) would flag
|
|
239
|
+
* the application space's `public.*` policies as extra during verify.
|
|
240
|
+
*
|
|
241
|
+
* Ownership is the set of namespaces the contract declares (its
|
|
242
|
+
* `storage.namespaces` slot keys), with the late-bound `__unbound__` slot
|
|
243
|
+
* resolved to the Postgres default `public` so a single-namespace contract still
|
|
244
|
+
* owns — and diffs — its `public` policies.
|
|
245
|
+
*/
|
|
246
|
+
function diffPostgresRlsPolicies(input) {
|
|
247
|
+
const { contract, schema } = input;
|
|
248
|
+
const ownedNamespaceIds = new Set(Object.keys(contract.storage.namespaces).map(resolveNamespaceId));
|
|
249
|
+
return diffNodes(Object.values(contract.storage.namespaces).flatMap((ns) => isPostgresSchema(ns) ? Object.values(ns.policy) : []), schema.rlsPolicies.filter((policy) => ownedNamespaceIds.has(resolveNamespaceId(policy.namespaceId))));
|
|
250
|
+
}
|
|
251
|
+
//#endregion
|
|
210
252
|
//#region src/core/migrations/planner.ts
|
|
211
253
|
function createPostgresMigrationPlanner(lowerer) {
|
|
212
254
|
return new PostgresMigrationPlanner(lowerer);
|
|
@@ -268,6 +310,21 @@ var PostgresMigrationPlanner = class {
|
|
|
268
310
|
strategies: postgresPlannerStrategies
|
|
269
311
|
});
|
|
270
312
|
if (!result.ok) return plannerFailure(result.failure);
|
|
313
|
+
if (!isPostgresSchemaIR(options.schema)) {
|
|
314
|
+
const policyNames = [];
|
|
315
|
+
for (const ns of Object.values(options.contract.storage.namespaces)) if (isPostgresSchema(ns)) for (const [policyName, policy] of Object.entries(ns.policy)) policyNames.push(`${policy.tableName}.${policyName}`);
|
|
316
|
+
if (policyNames.length > 0) return plannerFailure([{
|
|
317
|
+
kind: "unsupportedOperation",
|
|
318
|
+
summary: `migration plan does not yet emit RLS policies (the offline contract→schema derivation cannot carry them). Author RLS via 'db update' / 'db init' against a live database. Tracked for a follow-up slice (extension migration participation seam). Affected policies: ${policyNames.join(", ")}`
|
|
319
|
+
}]);
|
|
320
|
+
}
|
|
321
|
+
const rlsPartition = partitionCallsByControlPolicy({
|
|
322
|
+
calls: this.planRlsDiff(options),
|
|
323
|
+
contract: options.contract,
|
|
324
|
+
resolveControlPolicySubject: (call) => resolvePostgresCallControlPolicySubject(call, options.contract),
|
|
325
|
+
resolveFactoryName: (call) => call.factoryName,
|
|
326
|
+
formatSubjectLabel: (factoryName, subject) => formatPostgresControlPolicySubjectLabel(factoryName, subject, options.contract)
|
|
327
|
+
});
|
|
271
328
|
const fieldEventPartition = partitionCallsByControlPolicy({
|
|
272
329
|
calls: blindCast(planFieldEventOperations({
|
|
273
330
|
priorContract: options.fromContract,
|
|
@@ -279,8 +336,16 @@ var PostgresMigrationPlanner = class {
|
|
|
279
336
|
resolveFactoryName: (call) => call.factoryName,
|
|
280
337
|
formatSubjectLabel: (factoryName, subject) => formatPostgresControlPolicySubjectLabel(factoryName, subject, options.contract)
|
|
281
338
|
});
|
|
282
|
-
const calls = [
|
|
283
|
-
|
|
339
|
+
const calls = [
|
|
340
|
+
...result.value.calls,
|
|
341
|
+
...rlsPartition.kept,
|
|
342
|
+
...fieldEventPartition.kept
|
|
343
|
+
];
|
|
344
|
+
const warnings = [
|
|
345
|
+
...issuePartition.warnings,
|
|
346
|
+
...rlsPartition.warnings,
|
|
347
|
+
...fieldEventPartition.warnings
|
|
348
|
+
];
|
|
284
349
|
return Object.freeze({
|
|
285
350
|
kind: "success",
|
|
286
351
|
plan: new TypeScriptRenderablePostgresMigration(calls, {
|
|
@@ -290,6 +355,33 @@ var PostgresMigrationPlanner = class {
|
|
|
290
355
|
...warnings.length > 0 ? { warnings: Object.freeze(warnings) } : {}
|
|
291
356
|
});
|
|
292
357
|
}
|
|
358
|
+
planRlsDiff(options) {
|
|
359
|
+
if (!isPostgresSchemaIR(options.schema)) return [];
|
|
360
|
+
const diffIssues = diffPostgresRlsPolicies({
|
|
361
|
+
contract: options.contract,
|
|
362
|
+
schema: options.schema
|
|
363
|
+
});
|
|
364
|
+
const allowsDestructive = options.policy.allowedOperationClasses.includes("destructive");
|
|
365
|
+
const calls = [];
|
|
366
|
+
const seenEnableTables = /* @__PURE__ */ new Set();
|
|
367
|
+
for (const issue of diffIssues) {
|
|
368
|
+
const { namespaceId, entityName: policyName } = issue.coordinate;
|
|
369
|
+
const schemaForTable = resolveDdlSchemaForNamespaceStorage(options.contract.storage, namespaceId, options.schema);
|
|
370
|
+
if (issue.outcome === "missing") {
|
|
371
|
+
assertPostgresRlsPolicy(issue.expected);
|
|
372
|
+
const tableKey = `${schemaForTable}.${issue.expected.tableName}`;
|
|
373
|
+
if (!seenEnableTables.has(tableKey)) {
|
|
374
|
+
seenEnableTables.add(tableKey);
|
|
375
|
+
calls.push(new EnableRowLevelSecurityCall(schemaForTable, issue.expected.tableName));
|
|
376
|
+
}
|
|
377
|
+
calls.push(new CreatePostgresRlsPolicyCall(schemaForTable, issue.expected.tableName, issue.expected));
|
|
378
|
+
} else if (issue.outcome === "extra" && allowsDestructive) {
|
|
379
|
+
assertPostgresRlsPolicy(issue.actual);
|
|
380
|
+
calls.push(new DropPostgresRlsPolicyCall(schemaForTable, issue.actual.tableName, policyName));
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return calls;
|
|
384
|
+
}
|
|
293
385
|
ensureAdditivePolicy(policy) {
|
|
294
386
|
if (!policy.allowedOperationClasses.includes("additive")) return plannerFailure([{
|
|
295
387
|
kind: "unsupportedOperation",
|
|
@@ -319,6 +411,6 @@ var PostgresMigrationPlanner = class {
|
|
|
319
411
|
}
|
|
320
412
|
};
|
|
321
413
|
//#endregion
|
|
322
|
-
export { createPostgresMigrationPlanner as t };
|
|
414
|
+
export { diffPostgresRlsPolicies as n, createPostgresMigrationPlanner as t };
|
|
323
415
|
|
|
324
|
-
//# sourceMappingURL=planner-
|
|
416
|
+
//# sourceMappingURL=planner-D5zY__R3.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner-D5zY__R3.mjs","names":["#lowerer"],"sources":["../src/core/migrations/control-policy.ts","../src/core/migrations/verify-postgres-namespaces.ts","../src/core/migrations/verify-postgres-rls-policies.ts","../src/core/migrations/planner.ts"],"sourcesContent":["import type { Contract } from '@prisma-next/contract/types';\nimport type { ControlPolicySubject } from '@prisma-next/family-sql/control';\nimport type { SchemaIssue } from '@prisma-next/framework-components/control';\nimport { entityAt, UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';\nimport type { SqlStorage, StorageTable } from '@prisma-next/sql-contract/types';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { isPostgresSchema } from '../postgres-schema';\nimport type { PostgresOpFactoryCall } from './op-factory-call';\n\n/**\n * Factory calls that create a whole, previously-absent top-level storage\n * object. Used to decide whether `tolerated` permits a call (it only allows\n * creating absent objects, never modifying existing ones).\n *\n * Deliberately an explicit, closed set rather than a `factoryName`\n * create/alter/drop classification: it answers exactly one yes/no question\n * and is fail-closed. Any call not listed here — including future or\n * extension-contributed factories — is treated as NOT object-creation, so it\n * is suppressed under `tolerated` rather than permissively emitted.\n */\nconst OBJECT_CREATION_FACTORIES: ReadonlySet<string> = new Set<string>([\n 'createTable',\n 'createSchema',\n 'createRlsPolicy',\n 'enableRowLevelSecurity',\n]);\n\nfunction createsNewTopLevelObject(call: PostgresOpFactoryCall): boolean {\n return OBJECT_CREATION_FACTORIES.has(call.factoryName);\n}\n\nfunction ddlSchemaNameForNamespace(contract: Contract<SqlStorage>, namespaceId: string): string {\n const namespace = contract.storage.namespaces[namespaceId];\n return isPostgresSchema(namespace) ? namespace.ddlSchemaName(contract.storage) : namespaceId;\n}\n\nfunction resolveNamespaceIdForTable(\n contract: Contract<SqlStorage>,\n tableName: string,\n ddlSchemaName: string | undefined,\n): string {\n for (const namespaceId of Object.keys(contract.storage.namespaces)) {\n const table = entityAt<StorageTable>(contract.storage, {\n namespaceId,\n entityKind: 'table',\n entityName: tableName,\n });\n if (!table) continue;\n if (\n ddlSchemaName === undefined ||\n ddlSchemaNameForNamespace(contract, namespaceId) === ddlSchemaName\n ) {\n return namespaceId;\n }\n }\n return UNBOUND_NAMESPACE_ID;\n}\n\nfunction resolveNamespaceIdForDdlSchema(\n contract: Contract<SqlStorage>,\n ddlSchemaName: string,\n): string {\n for (const namespaceId of Object.keys(contract.storage.namespaces)) {\n const ns = contract.storage.namespaces[namespaceId];\n if (isPostgresSchema(ns) && ns.ddlSchemaName(contract.storage) === ddlSchemaName) {\n return namespaceId;\n }\n if (namespaceId === ddlSchemaName) {\n return namespaceId;\n }\n }\n return UNBOUND_NAMESPACE_ID;\n}\n\ninterface PostgresCallFields {\n readonly schemaName?: string;\n readonly tableName?: string;\n readonly columnName?: string;\n}\n\nfunction postgresCallFields(call: PostgresOpFactoryCall): PostgresCallFields {\n return {\n ...ifDefined('schemaName', 'schemaName' in call ? call.schemaName : undefined),\n ...ifDefined('tableName', 'tableName' in call ? call.tableName : undefined),\n ...ifDefined('columnName', 'columnName' in call ? call.columnName : undefined),\n };\n}\n\nexport function formatPostgresControlPolicySubjectLabel(\n factoryName: string,\n subject: ControlPolicySubject | undefined,\n contract: Contract<SqlStorage>,\n): string {\n if (subject?.table) {\n const ddlSchema = ddlSchemaNameForNamespace(contract, subject.namespaceId);\n return `${factoryName}(${ddlSchema}.${subject.table})`;\n }\n return factoryName;\n}\n\nexport function resolvePostgresCallControlPolicySubject(\n call: PostgresOpFactoryCall,\n contract: Contract<SqlStorage>,\n): ControlPolicySubject | undefined {\n const callFields = postgresCallFields(call);\n const createsNewObject = createsNewTopLevelObject(call);\n\n if (call.factoryName === 'createSchema' && callFields.schemaName) {\n return {\n namespaceId: resolveNamespaceIdForDdlSchema(contract, callFields.schemaName),\n createsNewObject,\n };\n }\n\n if (callFields.tableName) {\n const namespaceId = resolveNamespaceIdForTable(\n contract,\n callFields.tableName,\n callFields.schemaName,\n );\n const tableControlPolicy = entityAt<StorageTable>(contract.storage, {\n namespaceId,\n entityKind: 'table',\n entityName: callFields.tableName,\n })?.control;\n return {\n namespaceId,\n ...ifDefined('explicitNodeControlPolicy', tableControlPolicy),\n table: callFields.tableName,\n ...ifDefined('column', callFields.columnName),\n createsNewObject,\n };\n }\n\n if (callFields.schemaName) {\n return {\n namespaceId: resolveNamespaceIdForDdlSchema(contract, callFields.schemaName),\n createsNewObject,\n };\n }\n\n return undefined;\n}\n\n/**\n * Issue kinds that describe the absence of a whole, top-level Postgres\n * object — the same kinds `createsNewTopLevelObject` recognises for calls.\n * Used by {@link resolvePostgresIssueCreationFactoryName} to decide whether\n * a `tolerated` subject permits the issue to flow into the planner\n * (create-if-absent) and to seed the suppressed-subject warning's\n * `factoryName` when the planner is skipped.\n */\nconst POSTGRES_ISSUE_CREATION_FACTORY: Readonly<Record<string, string>> = Object.freeze({\n missing_schema: 'createSchema',\n missing_table: 'createTable',\n missing_rls_policy: 'createRlsPolicy',\n});\n\nexport function resolvePostgresIssueCreationFactoryName(issue: SchemaIssue): string | undefined {\n return POSTGRES_ISSUE_CREATION_FACTORY[issue.kind];\n}\n\n/**\n * Resolve the control-policy subject coordinate for a single\n * {@link SchemaIssue}. Mirrors the resolution `resolvePostgresCallControlPolicySubject`\n * performs for a generated DDL call, but works *off the issue* — so the\n * planner can partition issues by effective policy before the diff engine\n * runs. `createsNewObject` is derived from the issue's kind: schema/table/\n * type-missing issues describe a brand-new top-level object; everything else\n * touches an existing object.\n *\n * An `extra_table` issue carries no contract namespace coordinate (the table\n * isn't in any contract namespace), so the subject's `namespaceId` falls\n * back to {@link UNBOUND_NAMESPACE_ID}; the call-side resolver does the same\n * for the `DropTableCall` it produces.\n */\nexport function resolvePostgresIssueControlPolicySubject(\n issue: SchemaIssue,\n contract: Contract<SqlStorage>,\n): ControlPolicySubject | undefined {\n const createsNewObject = POSTGRES_ISSUE_CREATION_FACTORY[issue.kind] !== undefined;\n\n if (issue.kind === 'missing_schema' && issue.namespaceId) {\n return { namespaceId: issue.namespaceId, createsNewObject };\n }\n\n if ('table' in issue && issue.table) {\n const namespaceId =\n 'namespaceId' in issue && issue.namespaceId\n ? issue.namespaceId\n : resolveNamespaceIdForTable(contract, issue.table, undefined);\n const table = entityAt<StorageTable>(contract.storage, {\n namespaceId,\n entityKind: 'table',\n entityName: issue.table,\n });\n return {\n namespaceId,\n ...ifDefined('explicitNodeControlPolicy', table?.control),\n table: issue.table,\n ...ifDefined('column', 'column' in issue ? issue.column : undefined),\n createsNewObject,\n };\n }\n\n return undefined;\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type { SchemaIssue } from '@prisma-next/framework-components/control';\nimport { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';\nimport { isPostgresSchema } from '../postgres-schema';\nimport { isPostgresSchemaIR } from '../postgres-schema-ir';\n\n/**\n * Resolves the live-database schema name for a given namespace\n * coordinate. Mirrors `resolveDdlSchemaForNamespace` in\n * `planner-strategies.ts` so the verifier's projection and the\n * planner's projection always agree — Postgres-aware namespaces (the\n * production path) dispatch to `ddlSchemaName(storage)`, and bare\n * object payloads (used by some tests) fall back to the coordinate\n * itself.\n */\nfunction resolveDdlSchemaName(storage: SqlStorage, namespaceId: string): string {\n const namespace = storage.namespaces[namespaceId];\n if (isPostgresSchema(namespace)) {\n return namespace.ddlSchemaName(storage);\n }\n return namespaceId;\n}\n\n/**\n * Reads the introspected list of schema names from a `PostgresSchemaIR`.\n * Defaults to the always-present `public` schema when the schema IR is not a\n * `PostgresSchemaIR` — a fresh Postgres database always carries `public`\n * (unless an operator dropped it manually), so any verifier path that runs\n * without an enriched introspection still suppresses the redundant\n * `CREATE SCHEMA \"public\"`.\n *\n * Production introspection (`PostgresControlAdapter.introspect`) is the\n * authoritative source: it queries `pg_namespace` and sets `existingSchemas`\n * on the returned `PostgresSchemaIR`. Tests that want to assert against a\n * richer initial state construct a `PostgresSchemaIR` explicitly.\n */\nfunction existingSchemasFromSchema(schema: SqlSchemaIR): readonly string[] {\n if (isPostgresSchemaIR(schema)) {\n return schema.existingSchemas;\n }\n return ['public'];\n}\n\n/**\n * Emits a `missing_schema` issue for every contract-declared Postgres\n * namespace whose live container does not yet exist.\n *\n * A namespace's live container is the schema returned by its\n * polymorphic `ddlSchemaName(storage)` method — named schemas resolve\n * to their own id; the unbound singleton returns `UNBOUND_NAMESPACE_ID`\n * and is skipped explicitly (late-bound namespaces have no fixed DDL\n * schema). Issues are emitted only when the resolved name is a real,\n * creatable schema (not the unbound sentinel) and is missing from the\n * introspected list. `public` is suppressed implicitly because the\n * introspection (or its sensible default) always carries it.\n *\n * Each emitted issue stamps `namespaceId` with the contract namespace\n * coordinate so the downstream `mapIssueToCall` re-resolves the DDL\n * schema name through the same polymorphic path — keeping the\n * coordinate, not the resolved name, as the issue's stable identity.\n */\nexport function verifyPostgresNamespacePresence(input: {\n readonly contract: Contract<SqlStorage>;\n readonly schema: SqlSchemaIR;\n}): readonly SchemaIssue[] {\n const { contract, schema } = input;\n const existing = new Set(existingSchemasFromSchema(schema));\n const issues: SchemaIssue[] = [];\n const namespaceIds = Object.keys(contract.storage.namespaces).sort();\n for (const namespaceId of namespaceIds) {\n if (namespaceId === UNBOUND_NAMESPACE_ID) continue;\n const ddlName = resolveDdlSchemaName(contract.storage, namespaceId);\n if (ddlName === UNBOUND_NAMESPACE_ID) continue;\n if (existing.has(ddlName)) continue;\n issues.push({\n kind: 'missing_schema',\n namespaceId,\n message: `Schema \"${ddlName}\" is missing from database`,\n });\n }\n return issues;\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type { SchemaDiffIssue } from '@prisma-next/framework-components/control';\nimport { diffNodes } from '@prisma-next/framework-components/control';\nimport { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport { isPostgresSchema } from '../postgres-schema';\nimport type { PostgresSchemaIR } from '../postgres-schema-ir';\n\n// Postgres binds the late-bound (`__unbound__`) namespace to the `public`\n// schema, so an unbound contract owns `public` in the live database. Both the\n// contract slot ids and the introspected policy coordinates can carry either\n// form depending on how the schema was lowered, so normalize both sides to the\n// same id before comparing ownership.\nconst POSTGRES_DEFAULT_NAMESPACE_ID = 'public';\n\nfunction resolveNamespaceId(namespaceId: string): string {\n return namespaceId === UNBOUND_NAMESPACE_ID ? POSTGRES_DEFAULT_NAMESPACE_ID : namespaceId;\n}\n\n/**\n * Computes RLS policy drift between the contract and the live DB schema using\n * the generic {@link diffNodes} differ. Returns `SchemaDiffIssue[]` keyed by\n * the full `EntityCoordinate` (plane + namespaceId + entityKind + entityName).\n *\n * Both sides supply `PostgresRlsPolicy` nodes that implement `DiffableNode`\n * with a concrete namespace coordinate — the contract carries explicit\n * `namespaceId` set during lowering (e.g. `'public'`); the introspected schema\n * carries the resolved DDL schema name. The differ matches purely on coordinate\n * identity; no namespace interpretation happens here.\n *\n * The diff is scoped to the namespaces the verified contract owns. The live\n * schema introspection returns every policy across all DB schemas, but a policy\n * in a namespace this contract does not own belongs to another contract space\n * (or is external) and must not be reported as extra. Without this scope a space\n * that owns only `auth`/`storage` (e.g. the supabase extension space) would flag\n * the application space's `public.*` policies as extra during verify.\n *\n * Ownership is the set of namespaces the contract declares (its\n * `storage.namespaces` slot keys), with the late-bound `__unbound__` slot\n * resolved to the Postgres default `public` so a single-namespace contract still\n * owns — and diffs — its `public` policies.\n */\nexport function diffPostgresRlsPolicies(input: {\n readonly contract: Contract<SqlStorage>;\n readonly schema: PostgresSchemaIR;\n}): readonly SchemaDiffIssue[] {\n const { contract, schema } = input;\n\n const ownedNamespaceIds = new Set(\n Object.keys(contract.storage.namespaces).map(resolveNamespaceId),\n );\n\n const expected = Object.values(contract.storage.namespaces).flatMap((ns) =>\n isPostgresSchema(ns) ? Object.values(ns.policy) : [],\n );\n\n const actual = schema.rlsPolicies.filter((policy) =>\n ownedNamespaceIds.has(resolveNamespaceId(policy.namespaceId)),\n );\n\n return diffNodes(expected, actual);\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type {\n MigrationOperationPolicy,\n SqlMigrationPlannerPlanOptions,\n SqlPlannerConflict,\n SqlPlannerFailureResult,\n} from '@prisma-next/family-sql/control';\nimport {\n extractCodecControlHooks,\n partitionCallsByControlPolicy,\n partitionIssuesByControlPolicy,\n planFieldEventOperations,\n plannerFailure,\n} from '@prisma-next/family-sql/control';\nimport type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';\nimport { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n MigrationPlanner,\n MigrationPlanWithAuthoringSurface,\n MigrationScaffoldContext,\n SchemaIssue,\n} from '@prisma-next/framework-components/control';\nimport { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';\nimport { blindCast } from '@prisma-next/utils/casts';\nimport { parsePostgresDefault } from '../default-normalizer';\nimport { normalizeSchemaNativeType } from '../native-type-normalizer';\nimport { assertPostgresRlsPolicy } from '../postgres-rls-policy';\nimport { isPostgresSchema } from '../postgres-schema';\nimport { isPostgresSchemaIR } from '../postgres-schema-ir';\nimport { resolveDdlSchemaForNamespaceStorage } from '../postgres-schema-ir-annotations';\nimport {\n formatPostgresControlPolicySubjectLabel,\n resolvePostgresCallControlPolicySubject,\n resolvePostgresIssueControlPolicySubject,\n resolvePostgresIssueCreationFactoryName,\n} from './control-policy';\nimport { planIssues } from './issue-planner';\nimport type { PostgresOpFactoryCall } from './op-factory-call';\nimport {\n CreatePostgresRlsPolicyCall,\n DropPostgresRlsPolicyCall,\n EnableRowLevelSecurityCall,\n} from './op-factory-call';\nimport { TypeScriptRenderablePostgresMigration } from './planner-produced-postgres-migration';\nimport { postgresPlannerStrategies } from './planner-strategies';\nimport { verifyPostgresNamespacePresence } from './verify-postgres-namespaces';\nimport { diffPostgresRlsPolicies } from './verify-postgres-rls-policies';\n\ntype PlannerFrameworkComponents = SqlMigrationPlannerPlanOptions extends {\n readonly frameworkComponents: infer T;\n}\n ? T\n : ReadonlyArray<unknown>;\n\ntype PlannerOptionsWithComponents = SqlMigrationPlannerPlanOptions & {\n readonly frameworkComponents: PlannerFrameworkComponents;\n};\n\ntype VerifySqlSchemaOptionsWithComponents = Parameters<typeof verifySqlSchema>[0] & {\n readonly frameworkComponents: PlannerFrameworkComponents;\n};\n\nexport function createPostgresMigrationPlanner(\n lowerer: ExecuteRequestLowerer,\n): PostgresMigrationPlanner {\n return new PostgresMigrationPlanner(lowerer);\n}\n\n/**\n * Result of `PostgresMigrationPlanner.plan()`. A discriminated union whose\n * success variant carries a `TypeScriptRenderablePostgresMigration` — a\n * migration object that both the CLI (via `renderTypeScript()`) and the\n * SQL-typed callers (via `operations`, `describe()`, etc.) consume\n * uniformly.\n */\nexport type PostgresPlanResult =\n | {\n readonly kind: 'success';\n readonly plan: TypeScriptRenderablePostgresMigration;\n readonly warnings?: readonly SqlPlannerConflict[];\n }\n | SqlPlannerFailureResult;\n\n/**\n * Postgres migration planner — a thin wrapper over `planIssues`.\n *\n * `plan()` verifies the live schema against the target contract (producing\n * `SchemaIssue[]`) and delegates to `planIssues` with the unified\n * `postgresPlannerStrategies` list: enum-change, NOT-NULL backfill,\n * type-change, nullable-tightening, codec-hook storage types,\n * component-declared dependency installs, and shared-temp-default /\n * empty-table-guarded NOT-NULL add-column. The same strategy list runs for\n * `migration plan`, `db update`, and `db init`; behavior diverges purely on\n * `policy.allowedOperationClasses` (the data-safe strategies short-circuit\n * when `'data'` is excluded). The issue planner applies operation-class\n * policy gates and emits a single `PostgresOpFactoryCall[]` that drives both\n * the runtime-ops view (via `renderOps`) and the `renderTypeScript()`\n * authoring surface.\n */\nexport class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgres'> {\n readonly #lowerer: ExecuteRequestLowerer | undefined;\n\n constructor(lowerer?: ExecuteRequestLowerer) {\n this.#lowerer = lowerer;\n }\n\n plan(options: {\n readonly contract: unknown;\n readonly schema: unknown;\n readonly policy: MigrationOperationPolicy;\n /**\n * The \"from\" contract (state the planner assumes the database starts\n * at), or `null` for reconciliation flows. Only `migration plan` ever\n * supplies a non-null value; `db update` / `db init` reconcile against\n * the live schema and pass `null`. When present alongside the\n * `'data'` operation class, strategies that need from/to column-shape\n * comparisons (unsafe type change, nullability tightening) activate.\n *\n * Typed as the framework `Contract | null` to satisfy the\n * `MigrationPlanner` interface contract; `planSql` narrows to the SQL\n * shape via `SqlMigrationPlannerPlanOptions`. Used to populate\n * `describe().from` on the produced plan as\n * `fromContract?.storage.storageHash ?? null`.\n */\n readonly fromContract: Contract | null;\n readonly schemaName?: string;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;\n /**\n * Contract space this plan applies to. Stamped onto the produced\n * {@link TypeScriptRenderablePostgresMigration.spaceId} so the runner keys\n * the marker row by the right space.\n */\n readonly spaceId: string;\n }): PostgresPlanResult {\n return this.planSql(options as SqlMigrationPlannerPlanOptions);\n }\n\n emptyMigration(\n context: MigrationScaffoldContext,\n spaceId: string,\n ): MigrationPlanWithAuthoringSurface {\n return new TypeScriptRenderablePostgresMigration(\n [],\n {\n from: context.fromHash,\n to: context.toHash,\n },\n spaceId,\n this.#lowerer,\n );\n }\n\n private planSql(options: SqlMigrationPlannerPlanOptions): PostgresPlanResult {\n const schemaName =\n options.schemaName ??\n Object.keys(options.contract.storage.namespaces).find((id) => id !== UNBOUND_NAMESPACE_ID) ??\n UNBOUND_NAMESPACE_ID;\n const policyResult = this.ensureAdditivePolicy(options.policy);\n if (policyResult) {\n return policyResult;\n }\n\n const schemaIssues = this.collectSchemaIssues(options);\n const codecHooks = extractCodecControlHooks(options.frameworkComponents);\n const storageTypes = options.contract.storage.types ?? {};\n\n // Input-side control-policy partition. `external` / `observed` subjects\n // — and non-creation issues for `tolerated` subjects — are dropped from\n // the planner's input entirely; the planner never observes them, never\n // diffs them, never generates DDL for them. Suppression warnings are\n // built directly from the suppressed partition (one per subject), so the\n // user-visible message survives even when the planner would have failed\n // to model the subject's live shape.\n const issuePartition = partitionIssuesByControlPolicy({\n issues: schemaIssues,\n contract: options.contract,\n resolveControlPolicySubject: (issue) =>\n resolvePostgresIssueControlPolicySubject(issue, options.contract),\n resolveCreationFactoryName: resolvePostgresIssueCreationFactoryName,\n formatSubjectLabel: (factoryName, subject) =>\n formatPostgresControlPolicySubjectLabel(factoryName, subject, options.contract),\n });\n\n const result = planIssues({\n issues: issuePartition.plannable,\n toContract: options.contract,\n // `fromContract` is only supplied by `migration plan`. It is `null` for\n // `db update` / `db init`, which means data-safety strategies needing\n // from/to comparisons (unsafe type change, nullable tightening) are\n // inapplicable there — reconciliation falls through to\n // `mapIssueToCall`'s direct destructive handlers.\n fromContract: options.fromContract,\n schemaName,\n codecHooks,\n storageTypes,\n schema: options.schema,\n policy: options.policy,\n frameworkComponents: options.frameworkComponents,\n strategies: postgresPlannerStrategies,\n });\n\n if (!result.ok) {\n return plannerFailure(result.failure);\n }\n\n // Translate RLS diff issues to DDL calls and run through control-policy\n // partition. This runs AFTER the structural planIssues pass so the RLS\n // calls can refer to tables that may have been created in this plan.\n //\n // Fail loud when the contract declares RLS policies but the schema is\n // contract-derived (migration plan path). Without a live-introspected\n // PostgresSchemaIR there is no baseline to reconcile against, so\n // silently emitting no RLS DDL would be wrong. The user must run\n // `db update` / `db init` against a live database to emit RLS.\n if (!isPostgresSchemaIR(options.schema)) {\n const policyNames: string[] = [];\n for (const ns of Object.values(options.contract.storage.namespaces)) {\n if (isPostgresSchema(ns)) {\n for (const [policyName, policy] of Object.entries(ns.policy)) {\n policyNames.push(`${policy.tableName}.${policyName}`);\n }\n }\n }\n if (policyNames.length > 0) {\n return plannerFailure([\n {\n kind: 'unsupportedOperation',\n summary: `migration plan does not yet emit RLS policies (the offline contract→schema derivation cannot carry them). Author RLS via 'db update' / 'db init' against a live database. Tracked for a follow-up slice (extension migration participation seam). Affected policies: ${policyNames.join(', ')}`,\n },\n ]);\n }\n }\n const rlsCalls = this.planRlsDiff(options);\n const rlsPartition = partitionCallsByControlPolicy({\n calls: rlsCalls,\n contract: options.contract,\n resolveControlPolicySubject: (call) =>\n resolvePostgresCallControlPolicySubject(call, options.contract),\n resolveFactoryName: (call) => call.factoryName,\n formatSubjectLabel: (factoryName, subject) =>\n formatPostgresControlPolicySubjectLabel(factoryName, subject, options.contract),\n });\n\n // Inline `onFieldEvent`-emitted ops after structural DDL. The fixed\n // ordering is `structural → added → dropped → altered`, with\n // within-group sorting by `(tableName, fieldName)` so re-emits are\n // byte-stable. The hook fires only at the application emitter —\n // extension-space planning never reaches this helper.\n const fieldEventOps = planFieldEventOperations({\n priorContract: options.fromContract,\n newContract: options.contract,\n codecHooks,\n });\n // Codec hook ops are target-agnostic `OpFactoryCall`; Postgres planning\n // lifts them at this integration boundary (see field-event-planner JSDoc).\n const fieldEventPostgresCalls = blindCast<\n readonly PostgresOpFactoryCall[],\n 'Codec hook ops conform to PostgresOpFactoryCall at the app emitter boundary'\n >(fieldEventOps);\n const fieldEventPartition = partitionCallsByControlPolicy({\n calls: fieldEventPostgresCalls,\n contract: options.contract,\n resolveControlPolicySubject: (call) =>\n resolvePostgresCallControlPolicySubject(call, options.contract),\n resolveFactoryName: (call) => call.factoryName,\n formatSubjectLabel: (factoryName, subject) =>\n formatPostgresControlPolicySubjectLabel(factoryName, subject, options.contract),\n });\n const calls = [...result.value.calls, ...rlsPartition.kept, ...fieldEventPartition.kept];\n const warnings: SqlPlannerConflict[] = [\n ...issuePartition.warnings,\n ...rlsPartition.warnings,\n ...fieldEventPartition.warnings,\n ];\n\n return Object.freeze({\n kind: 'success' as const,\n plan: new TypeScriptRenderablePostgresMigration(\n calls,\n {\n from: options.fromContract?.storage.storageHash ?? null,\n to: options.contract.storage.storageHash,\n },\n options.spaceId,\n this.#lowerer,\n ),\n ...(warnings.length > 0 ? { warnings: Object.freeze(warnings) } : {}),\n });\n }\n\n private planRlsDiff(options: PlannerOptionsWithComponents): readonly PostgresOpFactoryCall[] {\n if (!isPostgresSchemaIR(options.schema)) {\n // Non-PostgresSchemaIR (migration plan path) reaches here only when the\n // contract declares NO policies — `planSql` already returned a failure\n // for the has-policies case. This [] is the genuine no-RLS case.\n return [];\n }\n const diffIssues = diffPostgresRlsPolicies({\n contract: options.contract,\n schema: options.schema,\n });\n\n const allowsDestructive = options.policy.allowedOperationClasses.includes('destructive');\n const calls: PostgresOpFactoryCall[] = [];\n const seenEnableTables = new Set<string>();\n\n for (const issue of diffIssues) {\n const { namespaceId, entityName: policyName } = issue.coordinate;\n const schemaForTable = resolveDdlSchemaForNamespaceStorage(\n options.contract.storage,\n namespaceId,\n options.schema,\n );\n\n // 'mismatch' is unreachable for content-addressed policies: the wire name\n // encodes the body hash, so two policies sharing a coordinate (same name)\n // are always equal and isEqualTo never returns false.\n if (issue.outcome === 'missing') {\n assertPostgresRlsPolicy(issue.expected);\n const tableKey = `${schemaForTable}.${issue.expected.tableName}`;\n if (!seenEnableTables.has(tableKey)) {\n seenEnableTables.add(tableKey);\n calls.push(new EnableRowLevelSecurityCall(schemaForTable, issue.expected.tableName));\n }\n calls.push(\n new CreatePostgresRlsPolicyCall(schemaForTable, issue.expected.tableName, issue.expected),\n );\n } else if (issue.outcome === 'extra' && allowsDestructive) {\n assertPostgresRlsPolicy(issue.actual);\n calls.push(\n new DropPostgresRlsPolicyCall(schemaForTable, issue.actual.tableName, policyName),\n );\n }\n }\n\n return calls;\n }\n\n private ensureAdditivePolicy(policy: MigrationOperationPolicy) {\n if (!policy.allowedOperationClasses.includes('additive')) {\n return plannerFailure([\n {\n kind: 'unsupportedOperation',\n summary: 'Migration planner requires additive operations be allowed',\n why: 'The planner requires the \"additive\" operation class to be allowed in the policy.',\n },\n ]);\n }\n return null;\n }\n\n private collectSchemaIssues(options: PlannerOptionsWithComponents): readonly SchemaIssue[] {\n // `db init` uses additive-only policy and intentionally ignores extra\n // schema objects. Any reconciliation-capable policy (widening or\n // destructive) must inspect extras to reconcile strict equality.\n const allowed = options.policy.allowedOperationClasses;\n const strict = allowed.includes('widening') || allowed.includes('destructive');\n const verifyOptions: VerifySqlSchemaOptionsWithComponents = {\n contract: options.contract,\n schema: options.schema,\n strict,\n typeMetadataRegistry: new Map(),\n frameworkComponents: options.frameworkComponents,\n normalizeDefault: parsePostgresDefault,\n normalizeNativeType: normalizeSchemaNativeType,\n };\n const verifyResult = verifySqlSchema(verifyOptions);\n // Schema presence is a Postgres-specific concern (no equivalent in\n // SQLite / Mongo), so the issue emission lives in the target layer\n // rather than in the family verifier. Stitch it in here so a single\n // `SchemaIssue[]` flows through `planIssues` and the planner emits\n // CREATE SCHEMA in the dep bucket before any CreateTableCall.\n // RLS policy drift is handled separately via diffPostgresRlsPolicies → planRlsDiff.\n const namespaceIssues = verifyPostgresNamespacePresence({\n contract: options.contract,\n schema: options.schema,\n });\n if (namespaceIssues.length === 0) {\n return verifyResult.schema.issues;\n }\n return [...namespaceIssues, ...verifyResult.schema.issues];\n }\n}\n"],"mappings":"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"}
|
|
@@ -2,19 +2,6 @@ import { i as quoteIdentifier, n as escapeLiteral } from "./sql-utils-DcfMz4MQ.m
|
|
|
2
2
|
import { t as resolveColumnTypeMetadata } from "./planner-type-resolution-Bt2f_q-F.mjs";
|
|
3
3
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
4
4
|
//#region src/core/migrations/planner-ddl-builders.ts
|
|
5
|
-
function buildCreateTableSql(qualifiedTableName, table, codecHooks, storageTypes = {}) {
|
|
6
|
-
const columnDefinitions = Object.entries(table.columns).map(([columnName, column]) => {
|
|
7
|
-
return [
|
|
8
|
-
quoteIdentifier(columnName),
|
|
9
|
-
buildColumnTypeSql(column, codecHooks, storageTypes),
|
|
10
|
-
buildColumnDefaultSql(column.default, column),
|
|
11
|
-
column.nullable ? "" : "NOT NULL"
|
|
12
|
-
].filter(Boolean).join(" ");
|
|
13
|
-
});
|
|
14
|
-
const constraintDefinitions = [];
|
|
15
|
-
if (table.primaryKey) constraintDefinitions.push(`PRIMARY KEY (${table.primaryKey.columns.map(quoteIdentifier).join(", ")})`);
|
|
16
|
-
return `CREATE TABLE ${qualifiedTableName} (\n ${[...columnDefinitions, ...constraintDefinitions].join(",\n ")}\n)`;
|
|
17
|
-
}
|
|
18
5
|
/**
|
|
19
6
|
* Pattern for safe PostgreSQL type names.
|
|
20
7
|
* Allows letters, digits, underscores, spaces (for "double precision", "character varying"),
|
|
@@ -50,10 +37,13 @@ function buildColumnTypeSql(column, codecHooks, storageTypes = {}, allowPseudoTy
|
|
|
50
37
|
}
|
|
51
38
|
}
|
|
52
39
|
const expanded = expandParameterizedTypeSql(resolved, codecHooks);
|
|
53
|
-
if (expanded !== null) return expanded;
|
|
54
|
-
if (column.typeRef)
|
|
40
|
+
if (expanded !== null) return column.many ? `${expanded}[]` : expanded;
|
|
41
|
+
if (column.typeRef) {
|
|
42
|
+
const base = quoteIdentifier(resolved.nativeType);
|
|
43
|
+
return column.many ? `${base}[]` : base;
|
|
44
|
+
}
|
|
55
45
|
assertSafeNativeType(resolved.nativeType);
|
|
56
|
-
return resolved.nativeType;
|
|
46
|
+
return column.many ? `${resolved.nativeType}[]` : resolved.nativeType;
|
|
57
47
|
}
|
|
58
48
|
function expandParameterizedTypeSql(column, codecHooks) {
|
|
59
49
|
if (!column.typeParams || Object.keys(column.typeParams).length === 0) return null;
|
|
@@ -84,6 +74,7 @@ function buildColumnDefaultSql(columnDefault, column) {
|
|
|
84
74
|
}
|
|
85
75
|
function renderDefaultLiteral(value, column) {
|
|
86
76
|
const isJsonColumn = column?.nativeType === "json" || column?.nativeType === "jsonb";
|
|
77
|
+
if (column?.many && Array.isArray(value)) return renderArrayLiteralDefault(value);
|
|
87
78
|
if (value instanceof Date) return `'${escapeLiteral(value.toISOString())}'`;
|
|
88
79
|
if (typeof value === "string") return `'${escapeLiteral(value)}'`;
|
|
89
80
|
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
@@ -92,17 +83,11 @@ function renderDefaultLiteral(value, column) {
|
|
|
92
83
|
if (isJsonColumn) return `'${escapeLiteral(json)}'::${column.nativeType}`;
|
|
93
84
|
return `'${escapeLiteral(json)}'`;
|
|
94
85
|
}
|
|
95
|
-
function
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return [
|
|
99
|
-
`ALTER TABLE ${qualifiedTableName}`,
|
|
100
|
-
`ADD COLUMN ${quoteIdentifier(columnName)} ${typeSql}`,
|
|
101
|
-
defaultSql,
|
|
102
|
-
column.nullable ? "" : "NOT NULL"
|
|
103
|
-
].filter(Boolean).join(" ");
|
|
86
|
+
function renderArrayLiteralDefault(elements) {
|
|
87
|
+
if (elements.length === 0) return "'{}'";
|
|
88
|
+
return `ARRAY[${elements.map((el) => renderDefaultLiteral(el)).join(", ")}]`;
|
|
104
89
|
}
|
|
105
90
|
//#endregion
|
|
106
|
-
export {
|
|
91
|
+
export { buildColumnTypeSql as n, renderDefaultLiteral as r, buildColumnDefaultSql as t };
|
|
107
92
|
|
|
108
|
-
//# sourceMappingURL=planner-ddl-builders-
|
|
93
|
+
//# sourceMappingURL=planner-ddl-builders-B8Nn6nHN.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner-ddl-builders-B8Nn6nHN.mjs","names":[],"sources":["../src/core/migrations/planner-ddl-builders.ts"],"sourcesContent":["import type { CodecControlHooks } from '@prisma-next/family-sql/control';\nimport type { StorageColumn, StorageTypeInstance } from '@prisma-next/sql-contract/types';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { escapeLiteral, quoteIdentifier } from '../sql-utils';\nimport type { PostgresColumnDefault } from '../types';\nimport { resolveColumnTypeMetadata } from './planner-type-resolution';\n\n/**\n * Pattern for safe PostgreSQL type names.\n * Allows letters, digits, underscores, spaces (for \"double precision\", \"character varying\"),\n * and trailing [] for array types.\n */\nconst SAFE_NATIVE_TYPE_PATTERN = /^[a-zA-Z][a-zA-Z0-9_ ]*(\\[\\])?$/;\n\nfunction assertSafeNativeType(nativeType: string): void {\n if (!SAFE_NATIVE_TYPE_PATTERN.test(nativeType)) {\n throw new Error(\n `Unsafe native type name in contract: \"${nativeType}\". ` +\n 'Native type names must match /^[a-zA-Z][a-zA-Z0-9_ ]*(\\\\[\\\\])?$/',\n );\n }\n}\n\n/**\n * Sanity check against accidental SQL injection from malformed contract files.\n * Rejects semicolons, SQL comment tokens, and dollar-quoting.\n * Not a comprehensive security boundary — the contract is developer-authored.\n */\nfunction assertSafeDefaultExpression(expression: string): void {\n if (expression.includes(';') || /--|\\/\\*|\\$\\$|\\bSELECT\\b/i.test(expression)) {\n throw new Error(\n `Unsafe default expression in contract: \"${expression}\". ` +\n 'Default expressions must not contain semicolons, SQL comment tokens, dollar-quoting, or subqueries.',\n );\n }\n}\n\n/**\n * Renders the SQL type for a column in DDL context.\n *\n * @param allowPseudoTypes - When true (default), autoincrement integer columns\n * produce SERIAL/BIGSERIAL/SMALLSERIAL pseudo-types. Set to false for contexts\n * like ALTER COLUMN TYPE where pseudo-types are invalid.\n */\nexport function buildColumnTypeSql(\n column: StorageColumn,\n codecHooks: ReadonlyMap<string, CodecControlHooks>,\n storageTypes: Record<string, StorageTypeInstance> = {},\n allowPseudoTypes = true,\n): string {\n const resolved = resolveColumnTypeMetadata(column, storageTypes);\n\n if (allowPseudoTypes) {\n const columnDefault = column.default;\n if (columnDefault?.kind === 'function' && columnDefault.expression === 'autoincrement()') {\n if (resolved.nativeType === 'int4' || resolved.nativeType === 'integer') {\n return 'SERIAL';\n }\n if (resolved.nativeType === 'int8' || resolved.nativeType === 'bigint') {\n return 'BIGSERIAL';\n }\n if (resolved.nativeType === 'int2' || resolved.nativeType === 'smallint') {\n return 'SMALLSERIAL';\n }\n }\n }\n\n const expanded = expandParameterizedTypeSql(resolved, codecHooks);\n if (expanded !== null) {\n return column.many ? `${expanded}[]` : expanded;\n }\n\n if (column.typeRef) {\n const base = quoteIdentifier(resolved.nativeType);\n return column.many ? `${base}[]` : base;\n }\n\n assertSafeNativeType(resolved.nativeType);\n return column.many ? `${resolved.nativeType}[]` : resolved.nativeType;\n}\n\nfunction expandParameterizedTypeSql(\n column: Pick<StorageColumn, 'nativeType' | 'codecId' | 'typeParams'>,\n codecHooks: ReadonlyMap<string, CodecControlHooks>,\n): string | null {\n if (!column.typeParams || Object.keys(column.typeParams).length === 0) {\n return null;\n }\n\n if (!column.codecId) {\n throw new Error(\n `Column declares typeParams for nativeType \"${column.nativeType}\" but has no codecId. ` +\n 'Ensure the column is associated with a codec.',\n );\n }\n\n const hooks = codecHooks.get(column.codecId);\n if (!hooks?.expandNativeType) {\n if (hooks?.planTypeOperations) {\n return null;\n }\n throw new Error(\n `Column declares typeParams for nativeType \"${column.nativeType}\" ` +\n `but no expandNativeType hook is registered for codecId \"${column.codecId}\". ` +\n 'Ensure the extension providing this codec is included in extensionPacks.',\n );\n }\n\n const expanded = hooks.expandNativeType({\n nativeType: column.nativeType,\n codecId: column.codecId,\n ...ifDefined('typeParams', column.typeParams),\n });\n\n return expanded !== column.nativeType ? expanded : null;\n}\n\n/** Autoincrement columns use SERIAL types, so this returns empty for them. */\nexport function buildColumnDefaultSql(\n columnDefault: PostgresColumnDefault | undefined,\n column?: StorageColumn,\n): string {\n if (!columnDefault) {\n return '';\n }\n\n switch (columnDefault.kind) {\n case 'literal':\n return `DEFAULT ${renderDefaultLiteral(columnDefault.value, column)}`;\n case 'function': {\n if (columnDefault.expression === 'autoincrement()') {\n return '';\n }\n assertSafeDefaultExpression(columnDefault.expression);\n return `DEFAULT (${columnDefault.expression})`;\n }\n case 'sequence':\n return `DEFAULT nextval('${escapeLiteral(quoteIdentifier(columnDefault.name))}'::regclass)`;\n }\n}\n\nexport function renderDefaultLiteral(value: unknown, column?: StorageColumn): string {\n const isJsonColumn = column?.nativeType === 'json' || column?.nativeType === 'jsonb';\n\n if (column?.many && Array.isArray(value)) {\n return renderArrayLiteralDefault(value);\n }\n\n if (value instanceof Date) {\n return `'${escapeLiteral(value.toISOString())}'`;\n }\n if (typeof value === 'string') {\n return `'${escapeLiteral(value)}'`;\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n if (value === null) {\n return 'NULL';\n }\n const json = JSON.stringify(value);\n if (isJsonColumn) {\n return `'${escapeLiteral(json)}'::${column.nativeType}`;\n }\n return `'${escapeLiteral(json)}'`;\n}\n\nfunction renderArrayLiteralDefault(elements: unknown[]): string {\n if (elements.length === 0) {\n return \"'{}'\";\n }\n const rendered = elements.map((el) => renderDefaultLiteral(el)).join(', ');\n return `ARRAY[${rendered}]`;\n}\n"],"mappings":";;;;;;;;;AAYA,MAAM,2BAA2B;AAEjC,SAAS,qBAAqB,YAA0B;CACtD,IAAI,CAAC,yBAAyB,KAAK,UAAU,GAC3C,MAAM,IAAI,MACR,yCAAyC,WAAW,qEAEtD;AAEJ;;;;;;AAOA,SAAS,4BAA4B,YAA0B;CAC7D,IAAI,WAAW,SAAS,GAAG,KAAK,2BAA2B,KAAK,UAAU,GACxE,MAAM,IAAI,MACR,2CAA2C,WAAW,uGAExD;AAEJ;;;;;;;;AASA,SAAgB,mBACd,QACA,YACA,eAAoD,CAAC,GACrD,mBAAmB,MACX;CACR,MAAM,WAAW,0BAA0B,QAAQ,YAAY;CAE/D,IAAI,kBAAkB;EACpB,MAAM,gBAAgB,OAAO;EAC7B,IAAI,eAAe,SAAS,cAAc,cAAc,eAAe,mBAAmB;GACxF,IAAI,SAAS,eAAe,UAAU,SAAS,eAAe,WAC5D,OAAO;GAET,IAAI,SAAS,eAAe,UAAU,SAAS,eAAe,UAC5D,OAAO;GAET,IAAI,SAAS,eAAe,UAAU,SAAS,eAAe,YAC5D,OAAO;EAEX;CACF;CAEA,MAAM,WAAW,2BAA2B,UAAU,UAAU;CAChE,IAAI,aAAa,MACf,OAAO,OAAO,OAAO,GAAG,SAAS,MAAM;CAGzC,IAAI,OAAO,SAAS;EAClB,MAAM,OAAO,gBAAgB,SAAS,UAAU;EAChD,OAAO,OAAO,OAAO,GAAG,KAAK,MAAM;CACrC;CAEA,qBAAqB,SAAS,UAAU;CACxC,OAAO,OAAO,OAAO,GAAG,SAAS,WAAW,MAAM,SAAS;AAC7D;AAEA,SAAS,2BACP,QACA,YACe;CACf,IAAI,CAAC,OAAO,cAAc,OAAO,KAAK,OAAO,UAAU,CAAC,CAAC,WAAW,GAClE,OAAO;CAGT,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,MACR,8CAA8C,OAAO,WAAW,oEAElE;CAGF,MAAM,QAAQ,WAAW,IAAI,OAAO,OAAO;CAC3C,IAAI,CAAC,OAAO,kBAAkB;EAC5B,IAAI,OAAO,oBACT,OAAO;EAET,MAAM,IAAI,MACR,8CAA8C,OAAO,WAAW,4DACH,OAAO,QAAQ,4EAE9E;CACF;CAEA,MAAM,WAAW,MAAM,iBAAiB;EACtC,YAAY,OAAO;EACnB,SAAS,OAAO;EAChB,GAAG,UAAU,cAAc,OAAO,UAAU;CAC9C,CAAC;CAED,OAAO,aAAa,OAAO,aAAa,WAAW;AACrD;;AAGA,SAAgB,sBACd,eACA,QACQ;CACR,IAAI,CAAC,eACH,OAAO;CAGT,QAAQ,cAAc,MAAtB;EACE,KAAK,WACH,OAAO,WAAW,qBAAqB,cAAc,OAAO,MAAM;EACpE,KAAK;GACH,IAAI,cAAc,eAAe,mBAC/B,OAAO;GAET,4BAA4B,cAAc,UAAU;GACpD,OAAO,YAAY,cAAc,WAAW;EAE9C,KAAK,YACH,OAAO,oBAAoB,cAAc,gBAAgB,cAAc,IAAI,CAAC,EAAE;CAClF;AACF;AAEA,SAAgB,qBAAqB,OAAgB,QAAgC;CACnF,MAAM,eAAe,QAAQ,eAAe,UAAU,QAAQ,eAAe;CAE7E,IAAI,QAAQ,QAAQ,MAAM,QAAQ,KAAK,GACrC,OAAO,0BAA0B,KAAK;CAGxC,IAAI,iBAAiB,MACnB,OAAO,IAAI,cAAc,MAAM,YAAY,CAAC,EAAE;CAEhD,IAAI,OAAO,UAAU,UACnB,OAAO,IAAI,cAAc,KAAK,EAAE;CAElC,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAChD,OAAO,OAAO,KAAK;CAErB,IAAI,UAAU,MACZ,OAAO;CAET,MAAM,OAAO,KAAK,UAAU,KAAK;CACjC,IAAI,cACF,OAAO,IAAI,cAAc,IAAI,EAAE,KAAK,OAAO;CAE7C,OAAO,IAAI,cAAc,IAAI,EAAE;AACjC;AAEA,SAAS,0BAA0B,UAA6B;CAC9D,IAAI,SAAS,WAAW,GACtB,OAAO;CAGT,OAAO,SADU,SAAS,KAAK,OAAO,qBAAqB,EAAE,CAAC,CAAC,CAAC,KAAK,IAC9C,EAAE;AAC3B"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { t as PostgresColumnDefault } from "./types-BDKkx8MA.mjs";
|
|
2
2
|
import { CodecControlHooks } from "@prisma-next/family-sql/control";
|
|
3
|
-
import { StorageColumn,
|
|
3
|
+
import { StorageColumn, StorageTypeInstance } from "@prisma-next/sql-contract/types";
|
|
4
4
|
|
|
5
5
|
//#region src/core/migrations/planner-ddl-builders.d.ts
|
|
6
|
-
declare function buildCreateTableSql(qualifiedTableName: string, table: StorageTable, codecHooks: ReadonlyMap<string, CodecControlHooks>, storageTypes?: Record<string, StorageTypeInstance>): string;
|
|
7
6
|
/**
|
|
8
7
|
* Renders the SQL type for a column in DDL context.
|
|
9
8
|
*
|
|
@@ -15,7 +14,6 @@ declare function buildColumnTypeSql(column: StorageColumn, codecHooks: ReadonlyM
|
|
|
15
14
|
/** Autoincrement columns use SERIAL types, so this returns empty for them. */
|
|
16
15
|
declare function buildColumnDefaultSql(columnDefault: PostgresColumnDefault | undefined, column?: StorageColumn): string;
|
|
17
16
|
declare function renderDefaultLiteral(value: unknown, column?: StorageColumn): string;
|
|
18
|
-
declare function buildAddColumnSql(qualifiedTableName: string, columnName: string, column: StorageColumn, codecHooks: ReadonlyMap<string, CodecControlHooks>, temporaryDefault?: string | null, storageTypes?: Record<string, StorageTypeInstance>): string;
|
|
19
17
|
//#endregion
|
|
20
|
-
export {
|
|
18
|
+
export { buildColumnDefaultSql, buildColumnTypeSql, renderDefaultLiteral };
|
|
21
19
|
//# sourceMappingURL=planner-ddl-builders.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-ddl-builders.d.mts","names":[],"sources":["../src/core/migrations/planner-ddl-builders.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"planner-ddl-builders.d.mts","names":[],"sources":["../src/core/migrations/planner-ddl-builders.ts"],"mappings":";;;;;;;AA4CA;;;;;iBAAgB,kBAAA,CACd,MAAA,EAAQ,aAAA,EACR,UAAA,EAAY,WAAA,SAAoB,iBAAA,GAChC,YAAA,GAAc,MAAA,SAAe,mBAAA,GAC7B,gBAAA;;iBAsEc,qBAAA,CACd,aAAA,EAAe,qBAAA,cACf,MAAA,GAAS,aAAa;AAAA,iBAqBR,oBAAA,CAAqB,KAAA,WAAgB,MAAA,GAAS,aAAa"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { n as buildColumnTypeSql, r as renderDefaultLiteral, t as buildColumnDefaultSql } from "./planner-ddl-builders-B8Nn6nHN.mjs";
|
|
2
|
+
export { buildColumnDefaultSql, buildColumnTypeSql, renderDefaultLiteral };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as PostgresPlanTargetDetails } from "./planner-target-details-
|
|
2
|
-
import { t as PostgresMigration } from "./postgres-migration-
|
|
1
|
+
import { t as PostgresPlanTargetDetails } from "./planner-target-details-HkP4RrRO.mjs";
|
|
2
|
+
import { t as PostgresMigration } from "./postgres-migration-DJNJHcdP.mjs";
|
|
3
3
|
import { SqlMigrationPlanOperation } from "@prisma-next/family-sql/control";
|
|
4
4
|
import { MigrationPlanWithAuthoringSurface, OpFactoryCall } from "@prisma-next/framework-components/control";
|
|
5
5
|
import { ExecuteRequestLowerer } from "@prisma-next/family-sql/control-adapter";
|
|
@@ -21,4 +21,4 @@ declare class TypeScriptRenderablePostgresMigration extends PostgresMigration im
|
|
|
21
21
|
}
|
|
22
22
|
//#endregion
|
|
23
23
|
export { TypeScriptRenderablePostgresMigration as t };
|
|
24
|
-
//# sourceMappingURL=planner-produced-postgres-migration-
|
|
24
|
+
//# sourceMappingURL=planner-produced-postgres-migration-CNTSCMl7.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-produced-postgres-migration-
|
|
1
|
+
{"version":3,"file":"planner-produced-postgres-migration-CNTSCMl7.d.mts","names":[],"sources":["../src/core/migrations/planner-produced-postgres-migration.ts"],"mappings":";;;;;;;;cAqCa,qCAAA,SACH,iBAAA,YACG,iCAAA;EAAA;cAcT,KAAA,WAAgB,aAAA,IAChB,IAAA,EAAM,aAAA,EACN,OAAA,UACA,OAAA,GAAU,qBAAA;EAAA,IASC,UAAA,cACT,yBAAA,CAA0B,yBAAA,IAC1B,OAAA,CAAQ,yBAAA,CAA0B,yBAAA;EAM7B,QAAA,IAAY,aAAA;EARR;;;;;EAAA,IAiBT,OAAA;EAIJ,gBAAA;AAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as PostgresMigration } from "./postgres-migration-
|
|
1
|
+
import { t as PostgresMigration } from "./postgres-migration-BoqzWSkj.mjs";
|
|
2
2
|
import { t as renderOps } from "./render-ops-BREh1kHe.mjs";
|
|
3
3
|
import { t as renderCallsToTypeScript } from "./render-typescript-KMgosran.mjs";
|
|
4
4
|
//#region src/core/migrations/planner-produced-postgres-migration.ts
|
|
@@ -40,4 +40,4 @@ var TypeScriptRenderablePostgresMigration = class extends PostgresMigration {
|
|
|
40
40
|
//#endregion
|
|
41
41
|
export { TypeScriptRenderablePostgresMigration as t };
|
|
42
42
|
|
|
43
|
-
//# sourceMappingURL=planner-produced-postgres-migration-
|
|
43
|
+
//# sourceMappingURL=planner-produced-postgres-migration-D8JHnu5j.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-produced-postgres-migration-
|
|
1
|
+
{"version":3,"file":"planner-produced-postgres-migration-D8JHnu5j.mjs","names":["#calls","#meta","#spaceId","#lowerer","#operationsCache"],"sources":["../src/core/migrations/planner-produced-postgres-migration.ts"],"sourcesContent":["/**\n * Planner-produced Postgres migration.\n *\n * Returned by `PostgresMigrationPlanner.plan(...)` and `emptyMigration(...)`.\n * Holds the migration IR (`PostgresOpFactoryCall[]`) alongside\n * `MigrationMeta` and exposes both the runtime-ops view (`get operations`)\n * and the TypeScript authoring view (`renderTypeScript()`). Satisfies\n * `MigrationPlanWithAuthoringSurface` so the CLI can uniformly serialize any\n * planner result back to `migration.ts`.\n *\n * Extends the family-level `SqlMigration` alias rather than the target-local\n * migration base directly — mirrors Mongo's `PlannerProducedMongoMigration`\n * shape and keeps CLI wiring one step removed from target internals.\n *\n * Placeholder-bearing plans: `renderTypeScript()` always succeeds and embeds\n * `() => placeholder(\"slot\")` at each stub. `operations`, in contrast, is\n * _not safe to enumerate_ on a stub-bearing plan — `DataTransformCall.toOp()`\n * throws `PN-MIG-2001` because a planner-stubbed closure cannot be lowered\n * to a runtime op. Callers that know a plan may carry stubs must render to\n * `migration.ts`, let the user fill the slots, and re-load the edited\n * migration before enumerating ops. The walk-schema planner does not emit\n * `DataTransformCall`s today, so this asymmetry is invisible until the\n * issue-planner integration lands in Phase 2.\n */\n\nimport type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';\nimport type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';\nimport type {\n MigrationPlanWithAuthoringSurface,\n OpFactoryCall,\n} from '@prisma-next/framework-components/control';\nimport type { MigrationMeta } from '@prisma-next/migration-tools/migration';\nimport type { PostgresPlanTargetDetails } from './planner-target-details';\nimport { PostgresMigration } from './postgres-migration';\nimport { renderOps } from './render-ops';\nimport { renderCallsToTypeScript } from './render-typescript';\n\nexport class TypeScriptRenderablePostgresMigration\n extends PostgresMigration\n implements MigrationPlanWithAuthoringSurface\n{\n readonly #calls: readonly OpFactoryCall[];\n readonly #meta: MigrationMeta;\n readonly #spaceId: string;\n readonly #lowerer: ExecuteRequestLowerer | undefined;\n #operationsCache:\n | readonly (\n | SqlMigrationPlanOperation<PostgresPlanTargetDetails>\n | Promise<SqlMigrationPlanOperation<PostgresPlanTargetDetails>>\n )[]\n | undefined;\n\n constructor(\n calls: readonly OpFactoryCall[],\n meta: MigrationMeta,\n spaceId: string,\n lowerer?: ExecuteRequestLowerer,\n ) {\n super();\n this.#calls = calls;\n this.#meta = meta;\n this.#spaceId = spaceId;\n this.#lowerer = lowerer;\n }\n\n override get operations(): readonly (\n | SqlMigrationPlanOperation<PostgresPlanTargetDetails>\n | Promise<SqlMigrationPlanOperation<PostgresPlanTargetDetails>>\n )[] {\n this.#operationsCache ??= renderOps(this.#calls, this.#lowerer);\n return this.#operationsCache;\n }\n\n override describe(): MigrationMeta {\n return this.#meta;\n }\n\n /**\n * Contract space this planner-produced plan applies to. Threaded\n * from the planner options so the runner keys the marker row by\n * the right space when executing the plan.\n */\n get spaceId(): string {\n return this.#spaceId;\n }\n\n renderTypeScript(): string {\n return renderCallsToTypeScript(this.#calls, { from: this.#meta.from, to: this.#meta.to });\n }\n}\n"],"mappings":";;;;AAqCA,IAAa,wCAAb,cACU,kBAEV;CACE;CACA;CACA;CACA;CACA;CAOA,YACE,OACA,MACA,SACA,SACA;EACA,MAAM;EACN,KAAKA,SAAS;EACd,KAAKC,QAAQ;EACb,KAAKC,WAAW;EAChB,KAAKC,WAAW;CAClB;CAEA,IAAa,aAGT;EACF,KAAKC,qBAAqB,UAAU,KAAKJ,QAAQ,KAAKG,QAAQ;EAC9D,OAAO,KAAKC;CACd;CAEA,WAAmC;EACjC,OAAO,KAAKH;CACd;;;;;;CAOA,IAAI,UAAkB;EACpB,OAAO,KAAKC;CACd;CAEA,mBAA2B;EACzB,OAAO,wBAAwB,KAAKF,QAAQ;GAAE,MAAM,KAAKC,MAAM;GAAM,IAAI,KAAKA,MAAM;EAAG,CAAC;CAC1F;AACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-
|
|
1
|
+
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-CNTSCMl7.mjs";
|
|
2
2
|
export { TypeScriptRenderablePostgresMigration };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-
|
|
1
|
+
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-D8JHnu5j.mjs";
|
|
2
2
|
export { TypeScriptRenderablePostgresMigration };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as postgresCreateNamespace } from "./postgres-schema-
|
|
1
|
+
import { i as postgresCreateNamespace } from "./postgres-schema-BVDr_61a.mjs";
|
|
2
2
|
import { i as quoteIdentifier } from "./sql-utils-DcfMz4MQ.mjs";
|
|
3
3
|
import { t as resolveColumnTypeMetadata } from "./planner-type-resolution-Bt2f_q-F.mjs";
|
|
4
4
|
//#region src/core/migrations/planner-sql-checks.ts
|
|
@@ -149,4 +149,4 @@ function buildExpectedFormatType(column, codecHooks, storageTypes = {}) {
|
|
|
149
149
|
//#endregion
|
|
150
150
|
export { qualifyTableName as n, buildExpectedFormatType as t };
|
|
151
151
|
|
|
152
|
-
//# sourceMappingURL=planner-sql-checks-
|
|
152
|
+
//# sourceMappingURL=planner-sql-checks-CI6Z21mm.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-sql-checks-
|
|
1
|
+
{"version":3,"file":"planner-sql-checks-CI6Z21mm.mjs","names":[],"sources":["../src/core/migrations/planner-sql-checks.ts"],"sourcesContent":["import type { CodecControlHooks } from '@prisma-next/family-sql/control';\nimport type { StorageColumn, StorageTypeInstance } from '@prisma-next/sql-contract/types';\nimport { postgresCreateNamespace } from '../postgres-schema';\nimport { quoteIdentifier } from '../sql-utils';\nimport { resolveColumnTypeMetadata } from './planner-type-resolution';\n\n/**\n * String-keyed entry points the migration ops use to render\n * schema-qualified DDL and catalog checks. The `schema` argument is\n * interpreted as a namespace coordinate: the framework `__unbound__`\n * sentinel resolves to the late-bound `PostgresUnboundSchema` singleton\n * (which elides the qualifier so `search_path` decides at runtime); any\n * other id materialises a `PostgresSchema(id)` whose qualifier is the\n * named schema. Helpers route through these `Namespace` concretions so\n * the unbound branch lives in the polymorphic override, not the call\n * site.\n */\nexport function qualifyTableName(schema: string, table: string): string {\n return postgresCreateNamespace({ id: schema, entries: { table: {} } }).qualifyTable(table);\n}\n\nconst FORMAT_TYPE_DISPLAY: ReadonlyMap<string, string> = new Map([\n ['int2', 'smallint'],\n ['int4', 'integer'],\n ['int8', 'bigint'],\n ['float4', 'real'],\n ['float8', 'double precision'],\n ['bool', 'boolean'],\n ['timestamp', 'timestamp without time zone'],\n ['timestamptz', 'timestamp with time zone'],\n ['time', 'time without time zone'],\n ['timetz', 'time with time zone'],\n]);\n\nconst UNQUOTED_POSTGRES_IDENTIFIER_PATTERN = /^[a-z_][a-z0-9_$]*$/;\n\nconst POSTGRES_RESERVED_IDENTIFIER_WORDS = new Set([\n 'all',\n 'analyse',\n 'analyze',\n 'and',\n 'any',\n 'array',\n 'as',\n 'asc',\n 'asymmetric',\n 'authorization',\n 'between',\n 'binary',\n 'both',\n 'case',\n 'cast',\n 'check',\n 'collate',\n 'column',\n 'constraint',\n 'create',\n 'current_catalog',\n 'current_date',\n 'current_role',\n 'current_time',\n 'current_timestamp',\n 'current_user',\n 'default',\n 'deferrable',\n 'desc',\n 'distinct',\n 'do',\n 'else',\n 'end',\n 'except',\n 'false',\n 'fetch',\n 'for',\n 'foreign',\n 'freeze',\n 'from',\n 'full',\n 'grant',\n 'group',\n 'having',\n 'ilike',\n 'in',\n 'initially',\n 'inner',\n 'intersect',\n 'into',\n 'is',\n 'isnull',\n 'join',\n 'lateral',\n 'leading',\n 'left',\n 'like',\n 'limit',\n 'localtime',\n 'localtimestamp',\n 'natural',\n 'not',\n 'notnull',\n 'null',\n 'offset',\n 'on',\n 'only',\n 'or',\n 'order',\n 'outer',\n 'overlaps',\n 'placing',\n 'primary',\n 'references',\n 'right',\n 'select',\n 'session_user',\n 'similar',\n 'some',\n 'symmetric',\n 'table',\n 'then',\n 'to',\n 'trailing',\n 'true',\n 'union',\n 'unique',\n 'user',\n 'using',\n 'variadic',\n 'verbose',\n 'when',\n 'where',\n 'window',\n 'with',\n]);\n\nfunction formatUserDefinedTypeName(identifier: string): string {\n if (\n UNQUOTED_POSTGRES_IDENTIFIER_PATTERN.test(identifier) &&\n !POSTGRES_RESERVED_IDENTIFIER_WORDS.has(identifier)\n ) {\n return identifier;\n }\n\n return quoteIdentifier(identifier);\n}\n\nexport function buildExpectedFormatType(\n column: StorageColumn,\n codecHooks: Map<string, CodecControlHooks>,\n storageTypes: Record<string, StorageTypeInstance> = {},\n): string {\n const resolved = resolveColumnTypeMetadata(column, storageTypes);\n\n if (resolved.typeParams && resolved.codecId) {\n const hooks = codecHooks.get(resolved.codecId);\n if (hooks?.expandNativeType) {\n return hooks.expandNativeType({\n nativeType: resolved.nativeType,\n codecId: resolved.codecId,\n typeParams: resolved.typeParams,\n });\n }\n }\n\n if (column.typeRef) {\n return formatUserDefinedTypeName(resolved.nativeType);\n }\n\n return FORMAT_TYPE_DISPLAY.get(resolved.nativeType) ?? resolved.nativeType;\n}\n"],"mappings":";;;;;;;;;;;;;;;AAiBA,SAAgB,iBAAiB,QAAgB,OAAuB;CACtE,OAAO,wBAAwB;EAAE,IAAI;EAAQ,SAAS,EAAE,OAAO,CAAC,EAAE;CAAE,CAAC,CAAC,CAAC,aAAa,KAAK;AAC3F;AAEA,MAAM,sBAAmD,IAAI,IAAI;CAC/D,CAAC,QAAQ,UAAU;CACnB,CAAC,QAAQ,SAAS;CAClB,CAAC,QAAQ,QAAQ;CACjB,CAAC,UAAU,MAAM;CACjB,CAAC,UAAU,kBAAkB;CAC7B,CAAC,QAAQ,SAAS;CAClB,CAAC,aAAa,6BAA6B;CAC3C,CAAC,eAAe,0BAA0B;CAC1C,CAAC,QAAQ,wBAAwB;CACjC,CAAC,UAAU,qBAAqB;AAClC,CAAC;AAED,MAAM,uCAAuC;AAE7C,MAAM,qCAAqC,IAAI,IAAI;CACjD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAS,0BAA0B,YAA4B;CAC7D,IACE,qCAAqC,KAAK,UAAU,KACpD,CAAC,mCAAmC,IAAI,UAAU,GAElD,OAAO;CAGT,OAAO,gBAAgB,UAAU;AACnC;AAEA,SAAgB,wBACd,QACA,YACA,eAAoD,CAAC,GAC7C;CACR,MAAM,WAAW,0BAA0B,QAAQ,YAAY;CAE/D,IAAI,SAAS,cAAc,SAAS,SAAS;EAC3C,MAAM,QAAQ,WAAW,IAAI,SAAS,OAAO;EAC7C,IAAI,OAAO,kBACT,OAAO,MAAM,iBAAiB;GAC5B,YAAY,SAAS;GACrB,SAAS,SAAS;GAClB,YAAY,SAAS;EACvB,CAAC;CAEL;CAEA,IAAI,OAAO,SACT,OAAO,0BAA0B,SAAS,UAAU;CAGtD,OAAO,oBAAoB,IAAI,SAAS,UAAU,KAAK,SAAS;AAClE"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as qualifyTableName, t as buildExpectedFormatType } from "./planner-sql-checks-
|
|
1
|
+
import { n as qualifyTableName, t as buildExpectedFormatType } from "./planner-sql-checks-CI6Z21mm.mjs";
|
|
2
2
|
export { buildExpectedFormatType, qualifyTableName };
|
package/dist/{planner-target-details-CIY6tLeo.d.mts → planner-target-details-HkP4RrRO.d.mts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/core/migrations/planner-target-details.d.ts
|
|
2
|
-
type OperationClass = 'dependency' | 'type' | 'table' | 'column' | 'primaryKey' | 'unique' | 'index' | 'foreignKey' | 'checkConstraint';
|
|
2
|
+
type OperationClass = 'dependency' | 'type' | 'table' | 'column' | 'primaryKey' | 'unique' | 'index' | 'foreignKey' | 'checkConstraint' | 'rlsPolicy' | 'rowLevelSecurity';
|
|
3
3
|
interface PostgresPlanTargetDetails {
|
|
4
4
|
readonly schema: string;
|
|
5
5
|
readonly objectType: OperationClass;
|
|
@@ -8,4 +8,4 @@ interface PostgresPlanTargetDetails {
|
|
|
8
8
|
}
|
|
9
9
|
//#endregion
|
|
10
10
|
export { PostgresPlanTargetDetails as t };
|
|
11
|
-
//# sourceMappingURL=planner-target-details-
|
|
11
|
+
//# sourceMappingURL=planner-target-details-HkP4RrRO.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner-target-details-HkP4RrRO.d.mts","names":[],"sources":["../src/core/migrations/planner-target-details.ts"],"mappings":";KAEY,cAAA;AAAA,UAaK,yBAAA;EAAA,SACN,MAAA;EAAA,SACA,UAAA,EAAY,cAAc;EAAA,SAC1B,IAAA;EAAA,SACA,KAAA;AAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as PostgresPlanTargetDetails } from "./planner-target-details-
|
|
1
|
+
import { t as PostgresPlanTargetDetails } from "./planner-target-details-HkP4RrRO.mjs";
|
|
2
2
|
export type { PostgresPlanTargetDetails };
|