@prisma-next/target-postgres 0.5.0-dev.61 → 0.5.0-dev.63
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/control.mjs +25 -4
- package/dist/control.mjs.map +1 -1
- package/dist/issue-planner.d.mts +1 -1
- package/dist/migration.d.mts +1 -1
- package/dist/op-factory-call-SFMIf-Cz.d.mts.map +1 -1
- package/dist/{planner-BMtFbKfn.mjs → planner-Cgr3Ttsi.mjs} +5 -5
- package/dist/planner-Cgr3Ttsi.mjs.map +1 -0
- package/dist/{planner-produced-postgres-migration-CyzRgqsq.d.mts → planner-produced-postgres-migration-CEI1d_Rq.d.mts} +9 -3
- package/dist/{planner-produced-postgres-migration-CyzRgqsq.d.mts.map → planner-produced-postgres-migration-CEI1d_Rq.d.mts.map} +1 -1
- package/dist/{planner-produced-postgres-migration-C0vaAvA8.mjs → planner-produced-postgres-migration-YjS7RsWc.mjs} +12 -2
- package/dist/planner-produced-postgres-migration-YjS7RsWc.mjs.map +1 -0
- package/dist/planner-produced-postgres-migration.d.mts +2 -2
- package/dist/planner-produced-postgres-migration.mjs +1 -1
- package/dist/planner.d.mts +10 -4
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +1 -1
- package/dist/{postgres-migration-Dzxr5BCy.d.mts → postgres-migration-RXzk7OX7.d.mts} +2 -2
- package/dist/{postgres-migration-Dzxr5BCy.d.mts.map → postgres-migration-RXzk7OX7.d.mts.map} +1 -1
- package/dist/{statement-builders-Ckkq4ryf.mjs → statement-builders-sOpc5QM-.mjs} +17 -6
- package/dist/statement-builders-sOpc5QM-.mjs.map +1 -0
- package/dist/statement-builders.d.mts +22 -1
- package/dist/statement-builders.d.mts.map +1 -1
- package/dist/statement-builders.mjs +2 -2
- package/package.json +15 -15
- package/src/core/migrations/planner-produced-postgres-migration.ts +12 -1
- package/src/core/migrations/planner.ts +26 -9
- package/src/core/migrations/runner.ts +49 -3
- package/src/core/migrations/statement-builders.ts +27 -4
- package/src/exports/statement-builders.ts +1 -0
- package/dist/planner-BMtFbKfn.mjs.map +0 -1
- package/dist/planner-produced-postgres-migration-C0vaAvA8.mjs.map +0 -1
- package/dist/statement-builders-Ckkq4ryf.mjs.map +0 -1
package/dist/control.mjs
CHANGED
|
@@ -3,8 +3,8 @@ import { t as parsePostgresDefault } from "./default-normalizer-D4RoM0i6.mjs";
|
|
|
3
3
|
import { t as normalizeSchemaNativeType } from "./native-type-normalizer-i4IFPL5F.mjs";
|
|
4
4
|
import { o as renderDefaultLiteral } from "./planner-ddl-builders-B6VK92UF.mjs";
|
|
5
5
|
import "./issue-planner-DooWabc2.mjs";
|
|
6
|
-
import { t as createPostgresMigrationPlanner } from "./planner-
|
|
7
|
-
import { a as
|
|
6
|
+
import { t as createPostgresMigrationPlanner } from "./planner-Cgr3Ttsi.mjs";
|
|
7
|
+
import { a as ensureMarkerTableStatement, i as ensureLedgerTableStatement, n as buildLedgerInsertStatement, o as ensurePrismaContractSchemaStatement, r as buildMergeMarkerStatements } from "./statement-builders-sOpc5QM-.mjs";
|
|
8
8
|
import { contractToSchemaIR, extractCodecControlHooks, runnerFailure, runnerSuccess } from "@prisma-next/family-sql/control";
|
|
9
9
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
10
10
|
import { verifySqlSchema } from "@prisma-next/family-sql/schema-verify";
|
|
@@ -49,8 +49,12 @@ var PostgresMigrationRunner = class {
|
|
|
49
49
|
let committed = false;
|
|
50
50
|
try {
|
|
51
51
|
await this.acquireLock(driver, lockKey);
|
|
52
|
-
await this.ensureControlTables(driver);
|
|
53
|
-
|
|
52
|
+
const ensureResult = await this.ensureControlTables(driver);
|
|
53
|
+
if (!ensureResult.ok) return ensureResult;
|
|
54
|
+
const existingMarker = await this.family.readMarker({
|
|
55
|
+
driver,
|
|
56
|
+
space: options.plan.spaceId
|
|
57
|
+
});
|
|
54
58
|
const markerCheck = this.ensureMarkerCompatibility(existingMarker, options.plan);
|
|
55
59
|
if (!markerCheck.ok) return markerCheck;
|
|
56
60
|
const markerAtDestination = this.markerMatchesDestination(existingMarker, options.plan);
|
|
@@ -140,8 +144,24 @@ var PostgresMigrationRunner = class {
|
|
|
140
144
|
}
|
|
141
145
|
async ensureControlTables(driver) {
|
|
142
146
|
await this.executeStatement(driver, ensurePrismaContractSchemaStatement);
|
|
147
|
+
const legacyDetection = await this.detectLegacyMarkerShape(driver);
|
|
148
|
+
if (!legacyDetection.ok) return legacyDetection;
|
|
143
149
|
await this.executeStatement(driver, ensureMarkerTableStatement);
|
|
144
150
|
await this.executeStatement(driver, ensureLedgerTableStatement);
|
|
151
|
+
return okVoid();
|
|
152
|
+
}
|
|
153
|
+
async detectLegacyMarkerShape(driver) {
|
|
154
|
+
const result = await driver.query(`select column_name
|
|
155
|
+
from information_schema.columns
|
|
156
|
+
where table_schema = 'prisma_contract'
|
|
157
|
+
and table_name = 'marker'`);
|
|
158
|
+
if (result.rows.length === 0) return okVoid();
|
|
159
|
+
const columns = new Set(result.rows.map((row) => row.column_name));
|
|
160
|
+
if (columns.has("space")) return okVoid();
|
|
161
|
+
return runnerFailure("LEGACY_MARKER_SHAPE", "Legacy marker-table shape detected on prisma_contract.marker (no `space` column). Prisma Next is in pre-1.0; the previous transitional auto-migration to the per-space-row schema has been removed. Drop `prisma_contract.marker` and re-run `dbInit` to reinitialise from a clean baseline.", { meta: {
|
|
162
|
+
table: "prisma_contract.marker",
|
|
163
|
+
columns: [...columns].sort()
|
|
164
|
+
} });
|
|
145
165
|
}
|
|
146
166
|
async runExpectationSteps(driver, steps, operation, phase) {
|
|
147
167
|
for (const step of steps) {
|
|
@@ -266,6 +286,7 @@ var PostgresMigrationRunner = class {
|
|
|
266
286
|
async upsertMarker(driver, options, existingMarker) {
|
|
267
287
|
const incomingInvariants = options.plan.providedInvariants ?? [];
|
|
268
288
|
const writeStatements = buildMergeMarkerStatements({
|
|
289
|
+
space: options.plan.spaceId,
|
|
269
290
|
storageHash: options.plan.destination.storageHash,
|
|
270
291
|
profileHash: options.plan.destination.profileHash ?? options.destinationContract.profileHash ?? options.plan.destination.storageHash,
|
|
271
292
|
contractJson: options.destinationContract,
|
package/dist/control.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control.mjs","names":["DEFAULT_CONFIG: RunnerConfig","cloned: Record<string, unknown>","family: SqlControlFamilyInstance","config: RunnerConfig","applyValue: ApplyPlanSuccessValue","executedOperations: Array<SqlMigrationPlanOperation<PostgresPlanTargetDetails>>","error: unknown","postgresTargetDescriptor: SqlControlTargetDescriptor<'postgres', PostgresPlanTargetDetails>"],"sources":["../src/core/migrations/runner.ts","../src/exports/control.ts"],"sourcesContent":["import type { ContractMarkerRecord } from '@prisma-next/contract/types';\nimport type {\n MigrationOperationPolicy,\n SqlControlFamilyInstance,\n SqlMigrationPlanContractInfo,\n SqlMigrationPlanOperation,\n SqlMigrationPlanOperationStep,\n SqlMigrationRunner,\n SqlMigrationRunnerExecuteOptions,\n SqlMigrationRunnerFailure,\n SqlMigrationRunnerResult,\n} from '@prisma-next/family-sql/control';\nimport { runnerFailure, runnerSuccess } from '@prisma-next/family-sql/control';\nimport { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';\nimport { SqlQueryError } from '@prisma-next/sql-errors';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { Result } from '@prisma-next/utils/result';\nimport { ok, okVoid } from '@prisma-next/utils/result';\nimport { parsePostgresDefault } from '../default-normalizer';\nimport { normalizeSchemaNativeType } from '../native-type-normalizer';\nimport type { PostgresPlanTargetDetails } from './planner-target-details';\nimport {\n buildLedgerInsertStatement,\n buildMergeMarkerStatements,\n ensureLedgerTableStatement,\n ensureMarkerTableStatement,\n ensurePrismaContractSchemaStatement,\n type SqlStatement,\n} from './statement-builders';\n\ninterface RunnerConfig {\n readonly defaultSchema: string;\n}\n\ninterface ApplyPlanSuccessValue {\n readonly operationsExecuted: number;\n readonly executedOperations: readonly SqlMigrationPlanOperation<PostgresPlanTargetDetails>[];\n}\n\nconst DEFAULT_CONFIG: RunnerConfig = {\n defaultSchema: 'public',\n};\n\nconst LOCK_DOMAIN = 'prisma_next.contract.marker';\n\n/**\n * Deep clones and freezes a record object to prevent mutation.\n * Recursively clones nested objects and arrays to ensure complete isolation.\n */\nfunction cloneAndFreezeRecord<T extends Record<string, unknown>>(value: T): T {\n const cloned: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value)) {\n if (val === null || val === undefined) {\n cloned[key] = val;\n } else if (Array.isArray(val)) {\n // Clone array (shallow clone of array elements)\n cloned[key] = Object.freeze([...val]);\n } else if (typeof val === 'object') {\n // Recursively clone nested objects\n cloned[key] = cloneAndFreezeRecord(val as Record<string, unknown>);\n } else {\n // Primitives are copied as-is\n cloned[key] = val;\n }\n }\n return Object.freeze(cloned) as T;\n}\n\nexport function createPostgresMigrationRunner(\n family: SqlControlFamilyInstance,\n config: Partial<RunnerConfig> = {},\n): SqlMigrationRunner<PostgresPlanTargetDetails> {\n return new PostgresMigrationRunner(family, { ...DEFAULT_CONFIG, ...config });\n}\n\nclass PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDetails> {\n constructor(\n private readonly family: SqlControlFamilyInstance,\n private readonly config: RunnerConfig,\n ) {}\n\n async execute(\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n ): Promise<SqlMigrationRunnerResult> {\n const schema = options.schemaName ?? this.config.defaultSchema;\n const driver = options.driver;\n const lockKey = `${LOCK_DOMAIN}:${schema}`;\n\n // Static checks - fail fast before transaction\n const destinationCheck = this.ensurePlanMatchesDestinationContract(\n options.plan.destination,\n options.destinationContract,\n );\n if (!destinationCheck.ok) {\n return destinationCheck;\n }\n\n const policyCheck = this.enforcePolicyCompatibility(options.policy, options.plan.operations);\n if (!policyCheck.ok) {\n return policyCheck;\n }\n\n // Begin transaction for DB operations\n await this.beginTransaction(driver);\n let committed = false;\n try {\n await this.acquireLock(driver, lockKey);\n await this.ensureControlTables(driver);\n const existingMarker = await this.family.readMarker({ driver });\n\n // Validate plan origin matches existing marker (needs marker from DB)\n const markerCheck = this.ensureMarkerCompatibility(existingMarker, options.plan);\n if (!markerCheck.ok) {\n return markerCheck;\n }\n\n // db update (origin: null) always applies; migration-apply (origin set,\n // origin !== destination) skips if marker already matches destination.\n // Self-edges (origin === destination) intentionally bypass the skip:\n // the migration is data-only, and the data transform's own check\n // decides whether `run` fires.\n const markerAtDestination = this.markerMatchesDestination(existingMarker, options.plan);\n const isSelfEdge = options.plan.origin?.storageHash === options.plan.destination.storageHash;\n const skipOperations = markerAtDestination && options.plan.origin != null && !isSelfEdge;\n let applyValue: ApplyPlanSuccessValue;\n\n if (skipOperations) {\n applyValue = { operationsExecuted: 0, executedOperations: [] };\n } else {\n const applyResult = await this.applyPlan(driver, options);\n if (!applyResult.ok) {\n return applyResult;\n }\n applyValue = applyResult.value;\n }\n\n // Verify resulting schema matches contract\n // Step 1: Introspect live schema (DB I/O, family-owned)\n const schemaIR = await this.family.introspect({\n driver,\n contract: options.destinationContract,\n });\n\n // Step 2: Pure verification (no DB I/O)\n const schemaVerifyResult = verifySqlSchema({\n contract: options.destinationContract,\n schema: schemaIR,\n strict: options.strictVerification ?? true,\n context: options.context ?? {},\n typeMetadataRegistry: this.family.typeMetadataRegistry,\n frameworkComponents: options.frameworkComponents,\n normalizeDefault: parsePostgresDefault,\n normalizeNativeType: normalizeSchemaNativeType,\n });\n if (!schemaVerifyResult.ok) {\n return runnerFailure('SCHEMA_VERIFY_FAILED', schemaVerifyResult.summary, {\n why: 'The resulting database schema does not satisfy the destination contract.',\n meta: {\n issues: schemaVerifyResult.schema.issues,\n },\n });\n }\n\n // Self-edge no-op detection: a self-edge migration whose ops all\n // self-skipped (every op pre-satisfied) and that brings no new\n // invariants produced no observable change. Skip the marker +\n // ledger writes so an idempotent re-apply of a self-edge data\n // transform doesn't churn updatedAt or pile up empty ledger\n // entries. With data transforms now flowing through the unified\n // op loop, a pre-satisfied DT lands in the\n // `postcheckAlreadySatisfied` skip path which does not increment\n // `operationsExecuted`, so the check below correctly distinguishes\n // \"every op self-skipped\" from \"the plan had ops that ran\". `db\n // update` no-ops still write a ledger entry as audit trail.\n const incomingInvariants = options.plan.providedInvariants ?? [];\n const existingInvariants = new Set(existingMarker?.invariants ?? []);\n const incomingIsSubsetOfExisting = incomingInvariants.every((id) =>\n existingInvariants.has(id),\n );\n const isSelfEdgeNoOp =\n isSelfEdge && applyValue.operationsExecuted === 0 && incomingIsSubsetOfExisting;\n\n if (!isSelfEdgeNoOp) {\n await this.upsertMarker(driver, options, existingMarker);\n await this.recordLedgerEntry(\n driver,\n options,\n existingMarker,\n applyValue.executedOperations,\n );\n }\n\n await this.commitTransaction(driver);\n committed = true;\n return runnerSuccess({\n operationsPlanned: options.plan.operations.length,\n operationsExecuted: applyValue.operationsExecuted,\n });\n } finally {\n if (!committed) {\n await this.rollbackTransaction(driver);\n }\n }\n }\n\n private async applyPlan(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n ): Promise<Result<ApplyPlanSuccessValue, SqlMigrationRunnerFailure>> {\n const checks = options.executionChecks;\n const runPrechecks = checks?.prechecks !== false; // Default true\n const runPostchecks = checks?.postchecks !== false; // Default true\n const runIdempotency = checks?.idempotencyChecks !== false; // Default true\n\n let operationsExecuted = 0;\n const executedOperations: Array<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> = [];\n for (const operation of options.plan.operations) {\n options.callbacks?.onOperationStart?.(operation);\n try {\n // Idempotency probe: only run if both postchecks and idempotency checks are enabled\n if (runPostchecks && runIdempotency) {\n const postcheckAlreadySatisfied = await this.expectationsAreSatisfied(\n driver,\n operation.postcheck,\n );\n if (postcheckAlreadySatisfied) {\n executedOperations.push(this.createPostcheckPreSatisfiedSkipRecord(operation));\n continue;\n }\n }\n\n // Prechecks: only run if enabled\n if (runPrechecks) {\n const precheckResult = await this.runExpectationSteps(\n driver,\n operation.precheck,\n operation,\n 'precheck',\n );\n if (!precheckResult.ok) {\n return precheckResult;\n }\n }\n\n const executeResult = await this.runExecuteSteps(driver, operation.execute, operation);\n if (!executeResult.ok) {\n return executeResult;\n }\n\n // Postchecks: only run if enabled\n if (runPostchecks) {\n const postcheckResult = await this.runExpectationSteps(\n driver,\n operation.postcheck,\n operation,\n 'postcheck',\n );\n if (!postcheckResult.ok) {\n return postcheckResult;\n }\n }\n\n executedOperations.push(operation);\n operationsExecuted += 1;\n } finally {\n options.callbacks?.onOperationComplete?.(operation);\n }\n }\n return ok({ operationsExecuted, executedOperations });\n }\n\n private async ensureControlTables(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<void> {\n await this.executeStatement(driver, ensurePrismaContractSchemaStatement);\n await this.executeStatement(driver, ensureMarkerTableStatement);\n await this.executeStatement(driver, ensureLedgerTableStatement);\n }\n\n private async runExpectationSteps(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n operation: SqlMigrationPlanOperation<PostgresPlanTargetDetails>,\n phase: 'precheck' | 'postcheck',\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n for (const step of steps) {\n const result = await driver.query(step.sql, step.params ?? []);\n if (!this.stepResultIsTrue(result.rows)) {\n const code = phase === 'precheck' ? 'PRECHECK_FAILED' : 'POSTCHECK_FAILED';\n return runnerFailure(\n code,\n `Operation ${operation.id} failed during ${phase}: ${step.description}`,\n {\n meta: {\n operationId: operation.id,\n phase,\n stepDescription: step.description,\n },\n },\n );\n }\n }\n return okVoid();\n }\n\n private async runExecuteSteps(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n operation: SqlMigrationPlanOperation<PostgresPlanTargetDetails>,\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n for (const step of steps) {\n try {\n await driver.query(step.sql, step.params ?? []);\n } catch (error: unknown) {\n // Catch SqlQueryError and include normalized metadata\n if (SqlQueryError.is(error)) {\n return runnerFailure(\n 'EXECUTION_FAILED',\n `Operation ${operation.id} failed during execution: ${step.description}`,\n {\n why: error.message,\n meta: {\n operationId: operation.id,\n stepDescription: step.description,\n sql: step.sql,\n sqlState: error.sqlState,\n constraint: error.constraint,\n table: error.table,\n column: error.column,\n detail: error.detail,\n },\n },\n );\n }\n // Let SqlConnectionError and other errors propagate (fail-fast)\n throw error;\n }\n }\n return okVoid();\n }\n\n private stepResultIsTrue(rows: readonly Record<string, unknown>[]): boolean {\n if (!rows || rows.length === 0) {\n return false;\n }\n const firstRow = rows[0];\n const firstValue = firstRow ? Object.values(firstRow)[0] : undefined;\n if (typeof firstValue === 'boolean') {\n return firstValue;\n }\n if (typeof firstValue === 'number') {\n return firstValue !== 0;\n }\n if (typeof firstValue === 'string') {\n const lower = firstValue.toLowerCase();\n // PostgreSQL boolean representations: 't'/'f', 'true'/'false', '1'/'0'\n if (lower === 't' || lower === 'true' || lower === '1') {\n return true;\n }\n if (lower === 'f' || lower === 'false' || lower === '0') {\n return false;\n }\n // For other strings, non-empty is truthy (though this case shouldn't occur for boolean checks)\n return firstValue.length > 0;\n }\n return Boolean(firstValue);\n }\n\n private async expectationsAreSatisfied(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n ): Promise<boolean> {\n if (steps.length === 0) {\n return false;\n }\n for (const step of steps) {\n const result = await driver.query(step.sql, step.params ?? []);\n if (!this.stepResultIsTrue(result.rows)) {\n return false;\n }\n }\n return true;\n }\n\n private createPostcheckPreSatisfiedSkipRecord(\n operation: SqlMigrationPlanOperation<PostgresPlanTargetDetails>,\n ): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {\n // Clone and freeze existing meta if present\n const clonedMeta = operation.meta ? cloneAndFreezeRecord(operation.meta) : undefined;\n\n // Create frozen runner metadata\n const runnerMeta = Object.freeze({\n skipped: true,\n reason: 'postcheck_pre_satisfied',\n });\n\n // Merge and freeze the combined meta\n const mergedMeta = Object.freeze({\n ...(clonedMeta ?? {}),\n runner: runnerMeta,\n });\n\n // Clone and freeze arrays to prevent mutation\n const frozenPostcheck = Object.freeze([...operation.postcheck]);\n\n return Object.freeze({\n id: operation.id,\n label: operation.label,\n ...ifDefined('summary', operation.summary),\n operationClass: operation.operationClass,\n target: operation.target, // Already frozen from plan creation\n precheck: Object.freeze([]),\n execute: Object.freeze([]),\n postcheck: frozenPostcheck,\n ...ifDefined('meta', operation.meta || mergedMeta ? mergedMeta : undefined),\n });\n }\n\n private markerMatchesDestination(\n marker: ContractMarkerRecord | null,\n plan: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['plan'],\n ): boolean {\n if (!marker) {\n return false;\n }\n if (marker.storageHash !== plan.destination.storageHash) {\n return false;\n }\n if (plan.destination.profileHash && marker.profileHash !== plan.destination.profileHash) {\n return false;\n }\n return true;\n }\n\n private enforcePolicyCompatibility(\n policy: MigrationOperationPolicy,\n operations: readonly SqlMigrationPlanOperation<PostgresPlanTargetDetails>[],\n ): Result<void, SqlMigrationRunnerFailure> {\n const allowedClasses = new Set(policy.allowedOperationClasses);\n for (const operation of operations) {\n if (!allowedClasses.has(operation.operationClass)) {\n return runnerFailure(\n 'POLICY_VIOLATION',\n `Operation ${operation.id} has class \"${operation.operationClass}\" which is not allowed by policy.`,\n {\n why: `Policy only allows: ${policy.allowedOperationClasses.join(', ')}.`,\n meta: {\n operationId: operation.id,\n operationClass: operation.operationClass,\n allowedClasses: policy.allowedOperationClasses,\n },\n },\n );\n }\n }\n return okVoid();\n }\n\n private ensureMarkerCompatibility(\n marker: ContractMarkerRecord | null,\n plan: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['plan'],\n ): Result<void, SqlMigrationRunnerFailure> {\n const origin = plan.origin ?? null;\n if (!origin) {\n // No origin assertion on the plan — the caller does not want origin validation.\n // This is the case for `db update`, which introspects the live schema and does not\n // rely on marker continuity. `db init` handles its own marker checks before the runner.\n return okVoid();\n }\n\n if (!marker) {\n return runnerFailure(\n 'MARKER_ORIGIN_MISMATCH',\n `Missing contract marker: expected origin storage hash ${origin.storageHash}.`,\n {\n meta: {\n expectedOriginStorageHash: origin.storageHash,\n },\n },\n );\n }\n if (marker.storageHash !== origin.storageHash) {\n return runnerFailure(\n 'MARKER_ORIGIN_MISMATCH',\n `Existing contract marker (${marker.storageHash}) does not match plan origin (${origin.storageHash}).`,\n {\n meta: {\n markerStorageHash: marker.storageHash,\n expectedOriginStorageHash: origin.storageHash,\n },\n },\n );\n }\n if (origin.profileHash && marker.profileHash !== origin.profileHash) {\n return runnerFailure(\n 'MARKER_ORIGIN_MISMATCH',\n `Existing contract marker profile hash (${marker.profileHash}) does not match plan origin profile hash (${origin.profileHash}).`,\n {\n meta: {\n markerProfileHash: marker.profileHash,\n expectedOriginProfileHash: origin.profileHash,\n },\n },\n );\n }\n return okVoid();\n }\n\n private ensurePlanMatchesDestinationContract(\n destination: SqlMigrationPlanContractInfo,\n contract: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['destinationContract'],\n ): Result<void, SqlMigrationRunnerFailure> {\n if (destination.storageHash !== contract.storage.storageHash) {\n return runnerFailure(\n 'DESTINATION_CONTRACT_MISMATCH',\n `Plan destination storage hash (${destination.storageHash}) does not match provided contract storage hash (${contract.storage.storageHash}).`,\n {\n meta: {\n planStorageHash: destination.storageHash,\n contractStorageHash: contract.storage.storageHash,\n },\n },\n );\n }\n if (\n destination.profileHash &&\n contract.profileHash &&\n destination.profileHash !== contract.profileHash\n ) {\n return runnerFailure(\n 'DESTINATION_CONTRACT_MISMATCH',\n `Plan destination profile hash (${destination.profileHash}) does not match provided contract profile hash (${contract.profileHash}).`,\n {\n meta: {\n planProfileHash: destination.profileHash,\n contractProfileHash: contract.profileHash,\n },\n },\n );\n }\n return okVoid();\n }\n\n private async upsertMarker(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n existingMarker: ContractMarkerRecord | null,\n ): Promise<void> {\n const incomingInvariants = options.plan.providedInvariants ?? [];\n const writeStatements = buildMergeMarkerStatements({\n storageHash: options.plan.destination.storageHash,\n profileHash:\n options.plan.destination.profileHash ??\n options.destinationContract.profileHash ??\n options.plan.destination.storageHash,\n contractJson: options.destinationContract,\n canonicalVersion: null,\n meta: {},\n invariants: incomingInvariants,\n });\n const statement = existingMarker ? writeStatements.update : writeStatements.insert;\n await this.executeStatement(driver, statement);\n }\n\n private async recordLedgerEntry(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n existingMarker: ContractMarkerRecord | null,\n executedOperations: readonly SqlMigrationPlanOperation<PostgresPlanTargetDetails>[],\n ): Promise<void> {\n const ledgerStatement = buildLedgerInsertStatement({\n originStorageHash: existingMarker?.storageHash ?? null,\n originProfileHash: existingMarker?.profileHash ?? null,\n destinationStorageHash: options.plan.destination.storageHash,\n destinationProfileHash:\n options.plan.destination.profileHash ??\n options.destinationContract.profileHash ??\n options.plan.destination.storageHash,\n contractJsonBefore: existingMarker?.contractJson ?? null,\n contractJsonAfter: options.destinationContract,\n operations: executedOperations,\n });\n await this.executeStatement(driver, ledgerStatement);\n }\n\n private async acquireLock(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n key: string,\n ): Promise<void> {\n await driver.query('select pg_advisory_xact_lock(hashtext($1))', [key]);\n }\n\n private async beginTransaction(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('BEGIN');\n }\n\n private async commitTransaction(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('COMMIT');\n }\n\n private async rollbackTransaction(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('ROLLBACK');\n }\n\n private async executeStatement(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n statement: SqlStatement,\n ): Promise<void> {\n if (statement.params.length > 0) {\n await driver.query(statement.sql, statement.params);\n return;\n }\n await driver.query(statement.sql);\n }\n}\n","import type { ColumnDefault, Contract } from '@prisma-next/contract/types';\nimport type {\n SqlControlFamilyInstance,\n SqlControlTargetDescriptor,\n} from '@prisma-next/family-sql/control';\nimport { contractToSchemaIR, extractCodecControlHooks } from '@prisma-next/family-sql/control';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n ControlTargetInstance,\n MigrationRunner,\n} from '@prisma-next/framework-components/control';\nimport type { SqlStorage, StorageColumn } from '@prisma-next/sql-contract/types';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { postgresTargetDescriptorMeta } from '../core/descriptor-meta';\nimport { createPostgresMigrationPlanner } from '../core/migrations/planner';\nimport { renderDefaultLiteral } from '../core/migrations/planner-ddl-builders';\nimport type { PostgresPlanTargetDetails } from '../core/migrations/planner-target-details';\nimport { createPostgresMigrationRunner } from '../core/migrations/runner';\n\nfunction buildNativeTypeExpander(\n frameworkComponents?: ReadonlyArray<TargetBoundComponentDescriptor<'sql', 'postgres'>>,\n) {\n if (!frameworkComponents) {\n return undefined;\n }\n const codecHooks = extractCodecControlHooks(frameworkComponents);\n return (input: {\n readonly nativeType: string;\n readonly codecId?: string;\n readonly typeParams?: Record<string, unknown>;\n }) => {\n if (!input.typeParams) return input.nativeType;\n // Mirror `renderExpectedNativeType` in verify-sql-schema: when a codec\n // has no `expandNativeType` hook (e.g. `pg/enum@1`, whose typeParams\n // describe the value set rather than a DDL suffix), fall back to the\n // bare native type rather than throwing. Throwing here would reject\n // every plan involving an enum-/values-typed column as soon as its\n // `typeRef` resolved to a `StorageTypeInstance` carrying typeParams.\n if (!input.codecId) return input.nativeType;\n const hooks = codecHooks.get(input.codecId);\n if (!hooks?.expandNativeType) return input.nativeType;\n return hooks.expandNativeType(input);\n };\n}\n\nexport function postgresRenderDefault(def: ColumnDefault, column: StorageColumn): string {\n if (def.kind === 'function') {\n return def.expression;\n }\n return renderDefaultLiteral(def.value, column);\n}\n\nconst postgresTargetDescriptor: SqlControlTargetDescriptor<'postgres', PostgresPlanTargetDetails> =\n {\n ...postgresTargetDescriptorMeta,\n migrations: {\n createPlanner(_family: SqlControlFamilyInstance) {\n return createPostgresMigrationPlanner();\n },\n createRunner(family) {\n return createPostgresMigrationRunner(family) as MigrationRunner<'sql', 'postgres'>;\n },\n contractToSchema(contract, frameworkComponents) {\n const expander = buildNativeTypeExpander(frameworkComponents);\n return contractToSchemaIR(contract as Contract<SqlStorage> | null, {\n annotationNamespace: 'pg',\n ...ifDefined('expandNativeType', expander),\n renderDefault: postgresRenderDefault,\n frameworkComponents: frameworkComponents ?? [],\n });\n },\n },\n create(): ControlTargetInstance<'sql', 'postgres'> {\n return {\n familyId: 'sql',\n targetId: 'postgres',\n };\n },\n /**\n * Direct method for SQL-specific usage.\n * @deprecated Use migrations.createPlanner() for CLI compatibility.\n */\n createPlanner(_family: SqlControlFamilyInstance) {\n return createPostgresMigrationPlanner();\n },\n /**\n * Direct method for SQL-specific usage.\n * @deprecated Use migrations.createRunner() for CLI compatibility.\n */\n createRunner(family) {\n return createPostgresMigrationRunner(family);\n },\n };\n\nexport default postgresTargetDescriptor;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"control.mjs","names":["DEFAULT_CONFIG: RunnerConfig","cloned: Record<string, unknown>","family: SqlControlFamilyInstance","config: RunnerConfig","applyValue: ApplyPlanSuccessValue","executedOperations: Array<SqlMigrationPlanOperation<PostgresPlanTargetDetails>>","error: unknown","postgresTargetDescriptor: SqlControlTargetDescriptor<'postgres', PostgresPlanTargetDetails>"],"sources":["../src/core/migrations/runner.ts","../src/exports/control.ts"],"sourcesContent":["import type { ContractMarkerRecord } from '@prisma-next/contract/types';\nimport type {\n MigrationOperationPolicy,\n SqlControlFamilyInstance,\n SqlMigrationPlanContractInfo,\n SqlMigrationPlanOperation,\n SqlMigrationPlanOperationStep,\n SqlMigrationRunner,\n SqlMigrationRunnerExecuteOptions,\n SqlMigrationRunnerFailure,\n SqlMigrationRunnerResult,\n} from '@prisma-next/family-sql/control';\nimport { runnerFailure, runnerSuccess } from '@prisma-next/family-sql/control';\nimport { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';\nimport { SqlQueryError } from '@prisma-next/sql-errors';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { Result } from '@prisma-next/utils/result';\nimport { ok, okVoid } from '@prisma-next/utils/result';\nimport { parsePostgresDefault } from '../default-normalizer';\nimport { normalizeSchemaNativeType } from '../native-type-normalizer';\nimport type { PostgresPlanTargetDetails } from './planner-target-details';\nimport {\n buildLedgerInsertStatement,\n buildMergeMarkerStatements,\n ensureLedgerTableStatement,\n ensureMarkerTableStatement,\n ensurePrismaContractSchemaStatement,\n type SqlStatement,\n} from './statement-builders';\n\ninterface RunnerConfig {\n readonly defaultSchema: string;\n}\n\ninterface ApplyPlanSuccessValue {\n readonly operationsExecuted: number;\n readonly executedOperations: readonly SqlMigrationPlanOperation<PostgresPlanTargetDetails>[];\n}\n\nconst DEFAULT_CONFIG: RunnerConfig = {\n defaultSchema: 'public',\n};\n\nconst LOCK_DOMAIN = 'prisma_next.contract.marker';\n\n/**\n * Deep clones and freezes a record object to prevent mutation.\n * Recursively clones nested objects and arrays to ensure complete isolation.\n */\nfunction cloneAndFreezeRecord<T extends Record<string, unknown>>(value: T): T {\n const cloned: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value)) {\n if (val === null || val === undefined) {\n cloned[key] = val;\n } else if (Array.isArray(val)) {\n // Clone array (shallow clone of array elements)\n cloned[key] = Object.freeze([...val]);\n } else if (typeof val === 'object') {\n // Recursively clone nested objects\n cloned[key] = cloneAndFreezeRecord(val as Record<string, unknown>);\n } else {\n // Primitives are copied as-is\n cloned[key] = val;\n }\n }\n return Object.freeze(cloned) as T;\n}\n\nexport function createPostgresMigrationRunner(\n family: SqlControlFamilyInstance,\n config: Partial<RunnerConfig> = {},\n): SqlMigrationRunner<PostgresPlanTargetDetails> {\n return new PostgresMigrationRunner(family, { ...DEFAULT_CONFIG, ...config });\n}\n\nclass PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDetails> {\n constructor(\n private readonly family: SqlControlFamilyInstance,\n private readonly config: RunnerConfig,\n ) {}\n\n async execute(\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n ): Promise<SqlMigrationRunnerResult> {\n const schema = options.schemaName ?? this.config.defaultSchema;\n const driver = options.driver;\n const lockKey = `${LOCK_DOMAIN}:${schema}`;\n\n // Static checks - fail fast before transaction\n const destinationCheck = this.ensurePlanMatchesDestinationContract(\n options.plan.destination,\n options.destinationContract,\n );\n if (!destinationCheck.ok) {\n return destinationCheck;\n }\n\n const policyCheck = this.enforcePolicyCompatibility(options.policy, options.plan.operations);\n if (!policyCheck.ok) {\n return policyCheck;\n }\n\n // Begin transaction for DB operations\n await this.beginTransaction(driver);\n let committed = false;\n try {\n await this.acquireLock(driver, lockKey);\n const ensureResult = await this.ensureControlTables(driver);\n if (!ensureResult.ok) {\n return ensureResult;\n }\n const existingMarker = await this.family.readMarker({\n driver,\n space: options.plan.spaceId,\n });\n\n // Validate plan origin matches existing marker (needs marker from DB)\n const markerCheck = this.ensureMarkerCompatibility(existingMarker, options.plan);\n if (!markerCheck.ok) {\n return markerCheck;\n }\n\n // db update (origin: null) always applies; migration-apply (origin set,\n // origin !== destination) skips if marker already matches destination.\n // Self-edges (origin === destination) intentionally bypass the skip:\n // the migration is data-only, and the data transform's own check\n // decides whether `run` fires.\n const markerAtDestination = this.markerMatchesDestination(existingMarker, options.plan);\n const isSelfEdge = options.plan.origin?.storageHash === options.plan.destination.storageHash;\n const skipOperations = markerAtDestination && options.plan.origin != null && !isSelfEdge;\n let applyValue: ApplyPlanSuccessValue;\n\n if (skipOperations) {\n applyValue = { operationsExecuted: 0, executedOperations: [] };\n } else {\n const applyResult = await this.applyPlan(driver, options);\n if (!applyResult.ok) {\n return applyResult;\n }\n applyValue = applyResult.value;\n }\n\n // Verify resulting schema matches contract\n // Step 1: Introspect live schema (DB I/O, family-owned)\n const schemaIR = await this.family.introspect({\n driver,\n contract: options.destinationContract,\n });\n\n // Step 2: Pure verification (no DB I/O)\n const schemaVerifyResult = verifySqlSchema({\n contract: options.destinationContract,\n schema: schemaIR,\n strict: options.strictVerification ?? true,\n context: options.context ?? {},\n typeMetadataRegistry: this.family.typeMetadataRegistry,\n frameworkComponents: options.frameworkComponents,\n normalizeDefault: parsePostgresDefault,\n normalizeNativeType: normalizeSchemaNativeType,\n });\n if (!schemaVerifyResult.ok) {\n return runnerFailure('SCHEMA_VERIFY_FAILED', schemaVerifyResult.summary, {\n why: 'The resulting database schema does not satisfy the destination contract.',\n meta: {\n issues: schemaVerifyResult.schema.issues,\n },\n });\n }\n\n // Self-edge no-op detection: a self-edge migration whose ops all\n // self-skipped (every op pre-satisfied) and that brings no new\n // invariants produced no observable change. Skip the marker +\n // ledger writes so an idempotent re-apply of a self-edge data\n // transform doesn't churn updatedAt or pile up empty ledger\n // entries. With data transforms now flowing through the unified\n // op loop, a pre-satisfied DT lands in the\n // `postcheckAlreadySatisfied` skip path which does not increment\n // `operationsExecuted`, so the check below correctly distinguishes\n // \"every op self-skipped\" from \"the plan had ops that ran\". `db\n // update` no-ops still write a ledger entry as audit trail.\n const incomingInvariants = options.plan.providedInvariants ?? [];\n const existingInvariants = new Set(existingMarker?.invariants ?? []);\n const incomingIsSubsetOfExisting = incomingInvariants.every((id) =>\n existingInvariants.has(id),\n );\n const isSelfEdgeNoOp =\n isSelfEdge && applyValue.operationsExecuted === 0 && incomingIsSubsetOfExisting;\n\n if (!isSelfEdgeNoOp) {\n await this.upsertMarker(driver, options, existingMarker);\n await this.recordLedgerEntry(\n driver,\n options,\n existingMarker,\n applyValue.executedOperations,\n );\n }\n\n await this.commitTransaction(driver);\n committed = true;\n return runnerSuccess({\n operationsPlanned: options.plan.operations.length,\n operationsExecuted: applyValue.operationsExecuted,\n });\n } finally {\n if (!committed) {\n await this.rollbackTransaction(driver);\n }\n }\n }\n\n private async applyPlan(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n ): Promise<Result<ApplyPlanSuccessValue, SqlMigrationRunnerFailure>> {\n const checks = options.executionChecks;\n const runPrechecks = checks?.prechecks !== false; // Default true\n const runPostchecks = checks?.postchecks !== false; // Default true\n const runIdempotency = checks?.idempotencyChecks !== false; // Default true\n\n let operationsExecuted = 0;\n const executedOperations: Array<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> = [];\n for (const operation of options.plan.operations) {\n options.callbacks?.onOperationStart?.(operation);\n try {\n // Idempotency probe: only run if both postchecks and idempotency checks are enabled\n if (runPostchecks && runIdempotency) {\n const postcheckAlreadySatisfied = await this.expectationsAreSatisfied(\n driver,\n operation.postcheck,\n );\n if (postcheckAlreadySatisfied) {\n executedOperations.push(this.createPostcheckPreSatisfiedSkipRecord(operation));\n continue;\n }\n }\n\n // Prechecks: only run if enabled\n if (runPrechecks) {\n const precheckResult = await this.runExpectationSteps(\n driver,\n operation.precheck,\n operation,\n 'precheck',\n );\n if (!precheckResult.ok) {\n return precheckResult;\n }\n }\n\n const executeResult = await this.runExecuteSteps(driver, operation.execute, operation);\n if (!executeResult.ok) {\n return executeResult;\n }\n\n // Postchecks: only run if enabled\n if (runPostchecks) {\n const postcheckResult = await this.runExpectationSteps(\n driver,\n operation.postcheck,\n operation,\n 'postcheck',\n );\n if (!postcheckResult.ok) {\n return postcheckResult;\n }\n }\n\n executedOperations.push(operation);\n operationsExecuted += 1;\n } finally {\n options.callbacks?.onOperationComplete?.(operation);\n }\n }\n return ok({ operationsExecuted, executedOperations });\n }\n\n private async ensureControlTables(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n await this.executeStatement(driver, ensurePrismaContractSchemaStatement);\n // Pre-1.0 zero-range guardrail: detect a pre-cleanup single-row\n // marker table (no `space` column) and surface a structured failure\n // rather than silently auto-migrating it to the per-space shape.\n // See `specs/framework-mechanism.spec.md § 2`.\n const legacyDetection = await this.detectLegacyMarkerShape(driver);\n if (!legacyDetection.ok) {\n return legacyDetection;\n }\n await this.executeStatement(driver, ensureMarkerTableStatement);\n await this.executeStatement(driver, ensureLedgerTableStatement);\n return okVoid();\n }\n\n private async detectLegacyMarkerShape(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n const result = await driver.query<{ column_name: string }>(\n `select column_name\n from information_schema.columns\n where table_schema = 'prisma_contract'\n and table_name = 'marker'`,\n );\n if (result.rows.length === 0) {\n return okVoid();\n }\n const columns = new Set(result.rows.map((row) => row.column_name));\n if (columns.has('space')) {\n return okVoid();\n }\n return runnerFailure(\n 'LEGACY_MARKER_SHAPE',\n 'Legacy marker-table shape detected on prisma_contract.marker (no `space` column). ' +\n 'Prisma Next is in pre-1.0; the previous transitional auto-migration to the per-space-row schema has been removed. ' +\n 'Drop `prisma_contract.marker` and re-run `dbInit` to reinitialise from a clean baseline.',\n {\n meta: {\n table: 'prisma_contract.marker',\n columns: [...columns].sort(),\n },\n },\n );\n }\n\n private async runExpectationSteps(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n operation: SqlMigrationPlanOperation<PostgresPlanTargetDetails>,\n phase: 'precheck' | 'postcheck',\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n for (const step of steps) {\n const result = await driver.query(step.sql, step.params ?? []);\n if (!this.stepResultIsTrue(result.rows)) {\n const code = phase === 'precheck' ? 'PRECHECK_FAILED' : 'POSTCHECK_FAILED';\n return runnerFailure(\n code,\n `Operation ${operation.id} failed during ${phase}: ${step.description}`,\n {\n meta: {\n operationId: operation.id,\n phase,\n stepDescription: step.description,\n },\n },\n );\n }\n }\n return okVoid();\n }\n\n private async runExecuteSteps(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n operation: SqlMigrationPlanOperation<PostgresPlanTargetDetails>,\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n for (const step of steps) {\n try {\n await driver.query(step.sql, step.params ?? []);\n } catch (error: unknown) {\n // Catch SqlQueryError and include normalized metadata\n if (SqlQueryError.is(error)) {\n return runnerFailure(\n 'EXECUTION_FAILED',\n `Operation ${operation.id} failed during execution: ${step.description}`,\n {\n why: error.message,\n meta: {\n operationId: operation.id,\n stepDescription: step.description,\n sql: step.sql,\n sqlState: error.sqlState,\n constraint: error.constraint,\n table: error.table,\n column: error.column,\n detail: error.detail,\n },\n },\n );\n }\n // Let SqlConnectionError and other errors propagate (fail-fast)\n throw error;\n }\n }\n return okVoid();\n }\n\n private stepResultIsTrue(rows: readonly Record<string, unknown>[]): boolean {\n if (!rows || rows.length === 0) {\n return false;\n }\n const firstRow = rows[0];\n const firstValue = firstRow ? Object.values(firstRow)[0] : undefined;\n if (typeof firstValue === 'boolean') {\n return firstValue;\n }\n if (typeof firstValue === 'number') {\n return firstValue !== 0;\n }\n if (typeof firstValue === 'string') {\n const lower = firstValue.toLowerCase();\n // PostgreSQL boolean representations: 't'/'f', 'true'/'false', '1'/'0'\n if (lower === 't' || lower === 'true' || lower === '1') {\n return true;\n }\n if (lower === 'f' || lower === 'false' || lower === '0') {\n return false;\n }\n // For other strings, non-empty is truthy (though this case shouldn't occur for boolean checks)\n return firstValue.length > 0;\n }\n return Boolean(firstValue);\n }\n\n private async expectationsAreSatisfied(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n ): Promise<boolean> {\n if (steps.length === 0) {\n return false;\n }\n for (const step of steps) {\n const result = await driver.query(step.sql, step.params ?? []);\n if (!this.stepResultIsTrue(result.rows)) {\n return false;\n }\n }\n return true;\n }\n\n private createPostcheckPreSatisfiedSkipRecord(\n operation: SqlMigrationPlanOperation<PostgresPlanTargetDetails>,\n ): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {\n // Clone and freeze existing meta if present\n const clonedMeta = operation.meta ? cloneAndFreezeRecord(operation.meta) : undefined;\n\n // Create frozen runner metadata\n const runnerMeta = Object.freeze({\n skipped: true,\n reason: 'postcheck_pre_satisfied',\n });\n\n // Merge and freeze the combined meta\n const mergedMeta = Object.freeze({\n ...(clonedMeta ?? {}),\n runner: runnerMeta,\n });\n\n // Clone and freeze arrays to prevent mutation\n const frozenPostcheck = Object.freeze([...operation.postcheck]);\n\n return Object.freeze({\n id: operation.id,\n label: operation.label,\n ...ifDefined('summary', operation.summary),\n operationClass: operation.operationClass,\n target: operation.target, // Already frozen from plan creation\n precheck: Object.freeze([]),\n execute: Object.freeze([]),\n postcheck: frozenPostcheck,\n ...ifDefined('meta', operation.meta || mergedMeta ? mergedMeta : undefined),\n });\n }\n\n private markerMatchesDestination(\n marker: ContractMarkerRecord | null,\n plan: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['plan'],\n ): boolean {\n if (!marker) {\n return false;\n }\n if (marker.storageHash !== plan.destination.storageHash) {\n return false;\n }\n if (plan.destination.profileHash && marker.profileHash !== plan.destination.profileHash) {\n return false;\n }\n return true;\n }\n\n private enforcePolicyCompatibility(\n policy: MigrationOperationPolicy,\n operations: readonly SqlMigrationPlanOperation<PostgresPlanTargetDetails>[],\n ): Result<void, SqlMigrationRunnerFailure> {\n const allowedClasses = new Set(policy.allowedOperationClasses);\n for (const operation of operations) {\n if (!allowedClasses.has(operation.operationClass)) {\n return runnerFailure(\n 'POLICY_VIOLATION',\n `Operation ${operation.id} has class \"${operation.operationClass}\" which is not allowed by policy.`,\n {\n why: `Policy only allows: ${policy.allowedOperationClasses.join(', ')}.`,\n meta: {\n operationId: operation.id,\n operationClass: operation.operationClass,\n allowedClasses: policy.allowedOperationClasses,\n },\n },\n );\n }\n }\n return okVoid();\n }\n\n private ensureMarkerCompatibility(\n marker: ContractMarkerRecord | null,\n plan: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['plan'],\n ): Result<void, SqlMigrationRunnerFailure> {\n const origin = plan.origin ?? null;\n if (!origin) {\n // No origin assertion on the plan — the caller does not want origin validation.\n // This is the case for `db update`, which introspects the live schema and does not\n // rely on marker continuity. `db init` handles its own marker checks before the runner.\n return okVoid();\n }\n\n if (!marker) {\n return runnerFailure(\n 'MARKER_ORIGIN_MISMATCH',\n `Missing contract marker: expected origin storage hash ${origin.storageHash}.`,\n {\n meta: {\n expectedOriginStorageHash: origin.storageHash,\n },\n },\n );\n }\n if (marker.storageHash !== origin.storageHash) {\n return runnerFailure(\n 'MARKER_ORIGIN_MISMATCH',\n `Existing contract marker (${marker.storageHash}) does not match plan origin (${origin.storageHash}).`,\n {\n meta: {\n markerStorageHash: marker.storageHash,\n expectedOriginStorageHash: origin.storageHash,\n },\n },\n );\n }\n if (origin.profileHash && marker.profileHash !== origin.profileHash) {\n return runnerFailure(\n 'MARKER_ORIGIN_MISMATCH',\n `Existing contract marker profile hash (${marker.profileHash}) does not match plan origin profile hash (${origin.profileHash}).`,\n {\n meta: {\n markerProfileHash: marker.profileHash,\n expectedOriginProfileHash: origin.profileHash,\n },\n },\n );\n }\n return okVoid();\n }\n\n private ensurePlanMatchesDestinationContract(\n destination: SqlMigrationPlanContractInfo,\n contract: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['destinationContract'],\n ): Result<void, SqlMigrationRunnerFailure> {\n if (destination.storageHash !== contract.storage.storageHash) {\n return runnerFailure(\n 'DESTINATION_CONTRACT_MISMATCH',\n `Plan destination storage hash (${destination.storageHash}) does not match provided contract storage hash (${contract.storage.storageHash}).`,\n {\n meta: {\n planStorageHash: destination.storageHash,\n contractStorageHash: contract.storage.storageHash,\n },\n },\n );\n }\n if (\n destination.profileHash &&\n contract.profileHash &&\n destination.profileHash !== contract.profileHash\n ) {\n return runnerFailure(\n 'DESTINATION_CONTRACT_MISMATCH',\n `Plan destination profile hash (${destination.profileHash}) does not match provided contract profile hash (${contract.profileHash}).`,\n {\n meta: {\n planProfileHash: destination.profileHash,\n contractProfileHash: contract.profileHash,\n },\n },\n );\n }\n return okVoid();\n }\n\n private async upsertMarker(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n existingMarker: ContractMarkerRecord | null,\n ): Promise<void> {\n const incomingInvariants = options.plan.providedInvariants ?? [];\n const writeStatements = buildMergeMarkerStatements({\n space: options.plan.spaceId,\n storageHash: options.plan.destination.storageHash,\n profileHash:\n options.plan.destination.profileHash ??\n options.destinationContract.profileHash ??\n options.plan.destination.storageHash,\n contractJson: options.destinationContract,\n canonicalVersion: null,\n meta: {},\n invariants: incomingInvariants,\n });\n const statement = existingMarker ? writeStatements.update : writeStatements.insert;\n await this.executeStatement(driver, statement);\n }\n\n private async recordLedgerEntry(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n existingMarker: ContractMarkerRecord | null,\n executedOperations: readonly SqlMigrationPlanOperation<PostgresPlanTargetDetails>[],\n ): Promise<void> {\n const ledgerStatement = buildLedgerInsertStatement({\n originStorageHash: existingMarker?.storageHash ?? null,\n originProfileHash: existingMarker?.profileHash ?? null,\n destinationStorageHash: options.plan.destination.storageHash,\n destinationProfileHash:\n options.plan.destination.profileHash ??\n options.destinationContract.profileHash ??\n options.plan.destination.storageHash,\n contractJsonBefore: existingMarker?.contractJson ?? null,\n contractJsonAfter: options.destinationContract,\n operations: executedOperations,\n });\n await this.executeStatement(driver, ledgerStatement);\n }\n\n private async acquireLock(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n key: string,\n ): Promise<void> {\n await driver.query('select pg_advisory_xact_lock(hashtext($1))', [key]);\n }\n\n private async beginTransaction(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('BEGIN');\n }\n\n private async commitTransaction(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('COMMIT');\n }\n\n private async rollbackTransaction(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('ROLLBACK');\n }\n\n private async executeStatement(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n statement: SqlStatement,\n ): Promise<void> {\n if (statement.params.length > 0) {\n await driver.query(statement.sql, statement.params);\n return;\n }\n await driver.query(statement.sql);\n }\n}\n","import type { ColumnDefault, Contract } from '@prisma-next/contract/types';\nimport type {\n SqlControlFamilyInstance,\n SqlControlTargetDescriptor,\n} from '@prisma-next/family-sql/control';\nimport { contractToSchemaIR, extractCodecControlHooks } from '@prisma-next/family-sql/control';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n ControlTargetInstance,\n MigrationRunner,\n} from '@prisma-next/framework-components/control';\nimport type { SqlStorage, StorageColumn } from '@prisma-next/sql-contract/types';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { postgresTargetDescriptorMeta } from '../core/descriptor-meta';\nimport { createPostgresMigrationPlanner } from '../core/migrations/planner';\nimport { renderDefaultLiteral } from '../core/migrations/planner-ddl-builders';\nimport type { PostgresPlanTargetDetails } from '../core/migrations/planner-target-details';\nimport { createPostgresMigrationRunner } from '../core/migrations/runner';\n\nfunction buildNativeTypeExpander(\n frameworkComponents?: ReadonlyArray<TargetBoundComponentDescriptor<'sql', 'postgres'>>,\n) {\n if (!frameworkComponents) {\n return undefined;\n }\n const codecHooks = extractCodecControlHooks(frameworkComponents);\n return (input: {\n readonly nativeType: string;\n readonly codecId?: string;\n readonly typeParams?: Record<string, unknown>;\n }) => {\n if (!input.typeParams) return input.nativeType;\n // Mirror `renderExpectedNativeType` in verify-sql-schema: when a codec\n // has no `expandNativeType` hook (e.g. `pg/enum@1`, whose typeParams\n // describe the value set rather than a DDL suffix), fall back to the\n // bare native type rather than throwing. Throwing here would reject\n // every plan involving an enum-/values-typed column as soon as its\n // `typeRef` resolved to a `StorageTypeInstance` carrying typeParams.\n if (!input.codecId) return input.nativeType;\n const hooks = codecHooks.get(input.codecId);\n if (!hooks?.expandNativeType) return input.nativeType;\n return hooks.expandNativeType(input);\n };\n}\n\nexport function postgresRenderDefault(def: ColumnDefault, column: StorageColumn): string {\n if (def.kind === 'function') {\n return def.expression;\n }\n return renderDefaultLiteral(def.value, column);\n}\n\nconst postgresTargetDescriptor: SqlControlTargetDescriptor<'postgres', PostgresPlanTargetDetails> =\n {\n ...postgresTargetDescriptorMeta,\n migrations: {\n createPlanner(_family: SqlControlFamilyInstance) {\n return createPostgresMigrationPlanner();\n },\n createRunner(family) {\n return createPostgresMigrationRunner(family) as MigrationRunner<'sql', 'postgres'>;\n },\n contractToSchema(contract, frameworkComponents) {\n const expander = buildNativeTypeExpander(frameworkComponents);\n return contractToSchemaIR(contract as Contract<SqlStorage> | null, {\n annotationNamespace: 'pg',\n ...ifDefined('expandNativeType', expander),\n renderDefault: postgresRenderDefault,\n frameworkComponents: frameworkComponents ?? [],\n });\n },\n },\n create(): ControlTargetInstance<'sql', 'postgres'> {\n return {\n familyId: 'sql',\n targetId: 'postgres',\n };\n },\n /**\n * Direct method for SQL-specific usage.\n * @deprecated Use migrations.createPlanner() for CLI compatibility.\n */\n createPlanner(_family: SqlControlFamilyInstance) {\n return createPostgresMigrationPlanner();\n },\n /**\n * Direct method for SQL-specific usage.\n * @deprecated Use migrations.createRunner() for CLI compatibility.\n */\n createRunner(family) {\n return createPostgresMigrationRunner(family);\n },\n };\n\nexport default postgresTargetDescriptor;\n"],"mappings":"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"}
|
package/dist/issue-planner.d.mts
CHANGED
|
@@ -2,10 +2,10 @@ import "./shared-DSVRy4AX.mjs";
|
|
|
2
2
|
import { b as PostgresOpFactoryCall } from "./op-factory-call-SFMIf-Cz.mjs";
|
|
3
3
|
import { CodecControlHooks, MigrationOperationPolicy, SqlPlannerConflict } from "@prisma-next/family-sql/control";
|
|
4
4
|
import { Result } from "@prisma-next/utils/result";
|
|
5
|
+
import { SchemaIssue } from "@prisma-next/framework-components/control";
|
|
5
6
|
import { Contract } from "@prisma-next/contract/types";
|
|
6
7
|
import { SqlStorage, StorageTypeInstance } from "@prisma-next/sql-contract/types";
|
|
7
8
|
import { TargetBoundComponentDescriptor } from "@prisma-next/framework-components/components";
|
|
8
|
-
import { SchemaIssue } from "@prisma-next/framework-components/control";
|
|
9
9
|
import { SqlSchemaIR } from "@prisma-next/sql-schema-ir/types";
|
|
10
10
|
|
|
11
11
|
//#region src/core/migrations/planner-strategies.d.ts
|
package/dist/migration.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as DataTransformOptions, r as dataTransform, t as DataTransformClosure } from "./data-transform-T71mQkVW.mjs";
|
|
2
2
|
import { n as ForeignKeySpec, r as Op, t as ColumnSpec } from "./shared-DSVRy4AX.mjs";
|
|
3
|
-
import { t as PostgresMigration } from "./postgres-migration-
|
|
3
|
+
import { t as PostgresMigration } from "./postgres-migration-RXzk7OX7.mjs";
|
|
4
4
|
import { placeholder } from "@prisma-next/errors/migration";
|
|
5
5
|
import { MigrationCLI } from "@prisma-next/cli/migration-cli";
|
|
6
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"op-factory-call-SFMIf-Cz.d.mts","names":[],"sources":["../src/core/migrations/op-factory-call.ts"],"sourcesContent":[],"mappings":";;;;;;;;KA8CK,EAAA,GAAK,yBAmGS,CAnGiB,yBAmGjB,CAAA;uBA/FJ,yBAAA,SAAkC,YAAA,YAAwB,aAkGZ,CAAA;EASnD,kBAAA,WAAA,EAAA,MAAA;EAjByB,kBAAA,cAAA,EAxFC,uBAwFD;EAAyB,kBAAA,KAAA,EAAA,MAAA;EA0B/C,SAAA,IAAA,CAAA,CAAA,EAhHM,EAgHS;EA0BX,kBAAA,CAAA,CAAA,EAAA,SAxIgB,iBAwIM,EAAA;EAO1B,UAAA,MAAA,CAAA,CAAA,EAAA,IAAoB;;AAapB,UA/II,qBAAA,CA+IJ;EAWH,SAAA,OAAA,EAAA,SAAA,MAAA,EAAA;;AAxBwD,cA9HrD,eAAA,SAAwB,yBAAA,CA8H6B;EAiCrD,SAAA,WAAe,EAAA,aAAQ;EA0BvB,SAAA,cAAgB,EAAA,UAAQ;EA0BxB,SAAA,UAAe,EAAA,MAAA;EAkDf,SAAA,SAAgB,EAAA,MAAA;EA8BhB,SAAA,OAAA,EAAA,SA9RgB,UA8RU,EAAA;EAiC1B,SAAA,UAAc,EA9TJ,qBA8TY,GAAA,SAAA;EAiCtB,SAAA,KAAA,EAAA,MAAkB;EAKhB,WAAA,CAAA,UAAA,EAAA,MAAA,EAAA,SAAA,EAAA,MAAA,EAAA,OAAA,EAAA,SA9VO,UA8VP,EAAA,EAAA,UAAA,CAAA,EA7VE,qBA6VF;EAG0C,IAAA,CAAA,CAAA,EArV/C,EAqV+C;EAS/C,gBAAA,CAAA,CAAA,EAAA,MAAA;;AAjBsD,cA9TnD,aAAA,SAAsB,yBAAA,CA8T6B;EA0BnD,SAAA,WAAA,EAAmB,
|
|
1
|
+
{"version":3,"file":"op-factory-call-SFMIf-Cz.d.mts","names":[],"sources":["../src/core/migrations/op-factory-call.ts"],"sourcesContent":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as parsePostgresDefault } from "./default-normalizer-D4RoM0i6.mjs";
|
|
2
2
|
import { t as normalizeSchemaNativeType } from "./native-type-normalizer-i4IFPL5F.mjs";
|
|
3
3
|
import { n as postgresPlannerStrategies, t as planIssues } from "./issue-planner-DooWabc2.mjs";
|
|
4
|
-
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-
|
|
4
|
+
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-YjS7RsWc.mjs";
|
|
5
5
|
import { extractCodecControlHooks, plannerFailure } from "@prisma-next/family-sql/control";
|
|
6
6
|
import { verifySqlSchema } from "@prisma-next/family-sql/schema-verify";
|
|
7
7
|
|
|
@@ -36,11 +36,11 @@ var PostgresMigrationPlanner = class {
|
|
|
36
36
|
plan(options) {
|
|
37
37
|
return this.planSql(options);
|
|
38
38
|
}
|
|
39
|
-
emptyMigration(context) {
|
|
39
|
+
emptyMigration(context, spaceId) {
|
|
40
40
|
return new TypeScriptRenderablePostgresMigration([], {
|
|
41
41
|
from: context.fromHash,
|
|
42
42
|
to: context.toHash
|
|
43
|
-
});
|
|
43
|
+
}, spaceId);
|
|
44
44
|
}
|
|
45
45
|
planSql(options) {
|
|
46
46
|
const schemaName = options.schemaName ?? this.config.defaultSchema;
|
|
@@ -67,7 +67,7 @@ var PostgresMigrationPlanner = class {
|
|
|
67
67
|
plan: new TypeScriptRenderablePostgresMigration(result.value.calls, {
|
|
68
68
|
from: options.fromContract?.storage.storageHash ?? null,
|
|
69
69
|
to: options.contract.storage.storageHash
|
|
70
|
-
})
|
|
70
|
+
}, options.spaceId)
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
ensureAdditivePolicy(policy) {
|
|
@@ -95,4 +95,4 @@ var PostgresMigrationPlanner = class {
|
|
|
95
95
|
|
|
96
96
|
//#endregion
|
|
97
97
|
export { createPostgresMigrationPlanner as t };
|
|
98
|
-
//# sourceMappingURL=planner-
|
|
98
|
+
//# sourceMappingURL=planner-Cgr3Ttsi.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner-Cgr3Ttsi.mjs","names":["DEFAULT_PLANNER_CONFIG: PlannerConfig","config: PlannerConfig"],"sources":["../src/core/migrations/planner.ts"],"sourcesContent":["import type { Contract } from '@prisma-next/contract/types';\nimport type {\n MigrationOperationPolicy,\n SqlMigrationPlannerPlanOptions,\n SqlPlannerFailureResult,\n} from '@prisma-next/family-sql/control';\nimport { extractCodecControlHooks, plannerFailure } from '@prisma-next/family-sql/control';\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 { parsePostgresDefault } from '../default-normalizer';\nimport { normalizeSchemaNativeType } from '../native-type-normalizer';\nimport { planIssues } from './issue-planner';\nimport { TypeScriptRenderablePostgresMigration } from './planner-produced-postgres-migration';\nimport { postgresPlannerStrategies } from './planner-strategies';\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\ninterface PlannerConfig {\n readonly defaultSchema: string;\n}\n\nconst DEFAULT_PLANNER_CONFIG: PlannerConfig = {\n defaultSchema: 'public',\n};\n\nexport function createPostgresMigrationPlanner(\n config: Partial<PlannerConfig> = {},\n): PostgresMigrationPlanner {\n return new PostgresMigrationPlanner({\n ...DEFAULT_PLANNER_CONFIG,\n ...config,\n });\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 | { readonly kind: 'success'; readonly plan: TypeScriptRenderablePostgresMigration }\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 constructor(private readonly config: PlannerConfig) {}\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 );\n }\n\n private planSql(options: SqlMigrationPlannerPlanOptions): PostgresPlanResult {\n const schemaName = options.schemaName ?? this.config.defaultSchema;\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 const result = planIssues({\n issues: schemaIssues,\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 return Object.freeze({\n kind: 'success' as const,\n plan: new TypeScriptRenderablePostgresMigration(\n result.value.calls,\n {\n from: options.fromContract?.storage.storageHash ?? null,\n to: options.contract.storage.storageHash,\n },\n options.spaceId,\n ),\n });\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 return verifyResult.schema.issues;\n }\n}\n"],"mappings":";;;;;;;;AAuCA,MAAMA,yBAAwC,EAC5C,eAAe,UAChB;AAED,SAAgB,+BACd,SAAiC,EAAE,EACT;AAC1B,QAAO,IAAI,yBAAyB;EAClC,GAAG;EACH,GAAG;EACJ,CAAC;;;;;;;;;;;;;;;;;;AA8BJ,IAAa,2BAAb,MAAqF;CACnF,YAAY,AAAiBC,QAAuB;EAAvB;;CAE7B,KAAK,SA2BkB;AACrB,SAAO,KAAK,QAAQ,QAA0C;;CAGhE,eACE,SACA,SACmC;AACnC,SAAO,IAAI,sCACT,EAAE,EACF;GACE,MAAM,QAAQ;GACd,IAAI,QAAQ;GACb,EACD,QACD;;CAGH,AAAQ,QAAQ,SAA6D;EAC3E,MAAM,aAAa,QAAQ,cAAc,KAAK,OAAO;EACrD,MAAM,eAAe,KAAK,qBAAqB,QAAQ,OAAO;AAC9D,MAAI,aACF,QAAO;EAGT,MAAM,eAAe,KAAK,oBAAoB,QAAQ;EACtD,MAAM,aAAa,yBAAyB,QAAQ,oBAAoB;EACxE,MAAM,eAAe,QAAQ,SAAS,QAAQ,SAAS,EAAE;EAEzD,MAAM,SAAS,WAAW;GACxB,QAAQ;GACR,YAAY,QAAQ;GAMpB,cAAc,QAAQ;GACtB;GACA;GACA;GACA,QAAQ,QAAQ;GAChB,QAAQ,QAAQ;GAChB,qBAAqB,QAAQ;GAC7B,YAAY;GACb,CAAC;AAEF,MAAI,CAAC,OAAO,GACV,QAAO,eAAe,OAAO,QAAQ;AAGvC,SAAO,OAAO,OAAO;GACnB,MAAM;GACN,MAAM,IAAI,sCACR,OAAO,MAAM,OACb;IACE,MAAM,QAAQ,cAAc,QAAQ,eAAe;IACnD,IAAI,QAAQ,SAAS,QAAQ;IAC9B,EACD,QAAQ,QACT;GACF,CAAC;;CAGJ,AAAQ,qBAAqB,QAAkC;AAC7D,MAAI,CAAC,OAAO,wBAAwB,SAAS,WAAW,CACtD,QAAO,eAAe,CACpB;GACE,MAAM;GACN,SAAS;GACT,KAAK;GACN,CACF,CAAC;AAEJ,SAAO;;CAGT,AAAQ,oBAAoB,SAA+D;EAIzF,MAAM,UAAU,QAAQ,OAAO;EAC/B,MAAM,SAAS,QAAQ,SAAS,WAAW,IAAI,QAAQ,SAAS,cAAc;AAW9E,SADqB,gBATuC;GAC1D,UAAU,QAAQ;GAClB,QAAQ,QAAQ;GAChB;GACA,sCAAsB,IAAI,KAAK;GAC/B,qBAAqB,QAAQ;GAC7B,kBAAkB;GAClB,qBAAqB;GACtB,CACkD,CAC/B,OAAO"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as PostgresPlanTargetDetails } from "./planner-target-details-COAiKZjW.mjs";
|
|
2
2
|
import { b as PostgresOpFactoryCall } from "./op-factory-call-SFMIf-Cz.mjs";
|
|
3
|
-
import { t as PostgresMigration } from "./postgres-migration-
|
|
3
|
+
import { t as PostgresMigration } from "./postgres-migration-RXzk7OX7.mjs";
|
|
4
4
|
import { SqlMigrationPlanOperation } from "@prisma-next/family-sql/control";
|
|
5
5
|
import { MigrationPlanWithAuthoringSurface } from "@prisma-next/framework-components/control";
|
|
6
6
|
import { MigrationMeta } from "@prisma-next/migration-tools/migration";
|
|
@@ -10,11 +10,17 @@ import { MigrationMeta } from "@prisma-next/migration-tools/migration";
|
|
|
10
10
|
type Op = SqlMigrationPlanOperation<PostgresPlanTargetDetails>;
|
|
11
11
|
declare class TypeScriptRenderablePostgresMigration extends PostgresMigration implements MigrationPlanWithAuthoringSurface {
|
|
12
12
|
#private;
|
|
13
|
-
constructor(calls: readonly PostgresOpFactoryCall[], meta: MigrationMeta);
|
|
13
|
+
constructor(calls: readonly PostgresOpFactoryCall[], meta: MigrationMeta, spaceId: string);
|
|
14
14
|
get operations(): readonly Op[];
|
|
15
15
|
describe(): MigrationMeta;
|
|
16
|
+
/**
|
|
17
|
+
* Contract space this planner-produced plan applies to. Threaded
|
|
18
|
+
* from the planner options so the runner keys the marker row by
|
|
19
|
+
* the right space when executing the plan.
|
|
20
|
+
*/
|
|
21
|
+
get spaceId(): string;
|
|
16
22
|
renderTypeScript(): string;
|
|
17
23
|
}
|
|
18
24
|
//#endregion
|
|
19
25
|
export { TypeScriptRenderablePostgresMigration as t };
|
|
20
|
-
//# sourceMappingURL=planner-produced-postgres-migration-
|
|
26
|
+
//# sourceMappingURL=planner-produced-postgres-migration-CEI1d_Rq.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-produced-postgres-migration-
|
|
1
|
+
{"version":3,"file":"planner-produced-postgres-migration-CEI1d_Rq.d.mts","names":[],"sources":["../src/core/migrations/planner-produced-postgres-migration.ts"],"sourcesContent":[],"mappings":";;;;;;;;;KAmCK,EAAA,GAAK,0BAA0B;cAEvB,qCAAA,SACH,iBAAA,YACG;;8BAMiB,+BAA+B;6BAOvB;cAIf"}
|
|
@@ -7,10 +7,12 @@ import { ifDefined } from "@prisma-next/utils/defined";
|
|
|
7
7
|
var TypeScriptRenderablePostgresMigration = class extends PostgresMigration {
|
|
8
8
|
#calls;
|
|
9
9
|
#meta;
|
|
10
|
-
|
|
10
|
+
#spaceId;
|
|
11
|
+
constructor(calls, meta, spaceId) {
|
|
11
12
|
super();
|
|
12
13
|
this.#calls = calls;
|
|
13
14
|
this.#meta = meta;
|
|
15
|
+
this.#spaceId = spaceId;
|
|
14
16
|
}
|
|
15
17
|
get operations() {
|
|
16
18
|
return renderOps(this.#calls);
|
|
@@ -18,6 +20,14 @@ var TypeScriptRenderablePostgresMigration = class extends PostgresMigration {
|
|
|
18
20
|
describe() {
|
|
19
21
|
return this.#meta;
|
|
20
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Contract space this planner-produced plan applies to. Threaded
|
|
25
|
+
* from the planner options so the runner keys the marker row by
|
|
26
|
+
* the right space when executing the plan.
|
|
27
|
+
*/
|
|
28
|
+
get spaceId() {
|
|
29
|
+
return this.#spaceId;
|
|
30
|
+
}
|
|
21
31
|
renderTypeScript() {
|
|
22
32
|
return renderCallsToTypeScript(this.#calls, {
|
|
23
33
|
from: this.#meta.from,
|
|
@@ -29,4 +39,4 @@ var TypeScriptRenderablePostgresMigration = class extends PostgresMigration {
|
|
|
29
39
|
|
|
30
40
|
//#endregion
|
|
31
41
|
export { TypeScriptRenderablePostgresMigration as t };
|
|
32
|
-
//# sourceMappingURL=planner-produced-postgres-migration-
|
|
42
|
+
//# sourceMappingURL=planner-produced-postgres-migration-YjS7RsWc.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner-produced-postgres-migration-YjS7RsWc.mjs","names":["#calls","#meta","#spaceId"],"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 { MigrationPlanWithAuthoringSurface } from '@prisma-next/framework-components/control';\nimport type { MigrationMeta } from '@prisma-next/migration-tools/migration';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { PostgresOpFactoryCall } from './op-factory-call';\nimport type { PostgresPlanTargetDetails } from './planner-target-details';\nimport { PostgresMigration } from './postgres-migration';\nimport { renderOps } from './render-ops';\nimport { renderCallsToTypeScript } from './render-typescript';\n\ntype Op = SqlMigrationPlanOperation<PostgresPlanTargetDetails>;\n\nexport class TypeScriptRenderablePostgresMigration\n extends PostgresMigration\n implements MigrationPlanWithAuthoringSurface\n{\n readonly #calls: readonly PostgresOpFactoryCall[];\n readonly #meta: MigrationMeta;\n readonly #spaceId: string;\n\n constructor(calls: readonly PostgresOpFactoryCall[], meta: MigrationMeta, spaceId: string) {\n super();\n this.#calls = calls;\n this.#meta = meta;\n this.#spaceId = spaceId;\n }\n\n override get operations(): readonly Op[] {\n return renderOps(this.#calls);\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, {\n from: this.#meta.from,\n to: this.#meta.to,\n ...ifDefined('labels', this.#meta.labels),\n });\n }\n}\n"],"mappings":";;;;;;AAqCA,IAAa,wCAAb,cACU,kBAEV;CACE,CAASA;CACT,CAASC;CACT,CAASC;CAET,YAAY,OAAyC,MAAqB,SAAiB;AACzF,SAAO;AACP,QAAKF,QAAS;AACd,QAAKC,OAAQ;AACb,QAAKC,UAAW;;CAGlB,IAAa,aAA4B;AACvC,SAAO,UAAU,MAAKF,MAAO;;CAG/B,AAAS,WAA0B;AACjC,SAAO,MAAKC;;;;;;;CAQd,IAAI,UAAkB;AACpB,SAAO,MAAKC;;CAGd,mBAA2B;AACzB,SAAO,wBAAwB,MAAKF,OAAQ;GAC1C,MAAM,MAAKC,KAAM;GACjB,IAAI,MAAKA,KAAM;GACf,GAAG,UAAU,UAAU,MAAKA,KAAM,OAAO;GAC1C,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./data-transform-T71mQkVW.mjs";
|
|
2
2
|
import "./shared-DSVRy4AX.mjs";
|
|
3
3
|
import "./op-factory-call-SFMIf-Cz.mjs";
|
|
4
|
-
import "./postgres-migration-
|
|
5
|
-
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-
|
|
4
|
+
import "./postgres-migration-RXzk7OX7.mjs";
|
|
5
|
+
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-CEI1d_Rq.mjs";
|
|
6
6
|
export { TypeScriptRenderablePostgresMigration };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-
|
|
1
|
+
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-YjS7RsWc.mjs";
|
|
2
2
|
|
|
3
3
|
export { TypeScriptRenderablePostgresMigration };
|
package/dist/planner.d.mts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import "./data-transform-T71mQkVW.mjs";
|
|
2
2
|
import "./shared-DSVRy4AX.mjs";
|
|
3
3
|
import "./op-factory-call-SFMIf-Cz.mjs";
|
|
4
|
-
import "./postgres-migration-
|
|
5
|
-
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-
|
|
4
|
+
import "./postgres-migration-RXzk7OX7.mjs";
|
|
5
|
+
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-CEI1d_Rq.mjs";
|
|
6
6
|
import { MigrationOperationPolicy, SqlPlannerFailureResult } from "@prisma-next/family-sql/control";
|
|
7
|
+
import { MigrationPlanWithAuthoringSurface, MigrationPlanner, MigrationScaffoldContext } from "@prisma-next/framework-components/control";
|
|
7
8
|
import { Contract } from "@prisma-next/contract/types";
|
|
8
9
|
import { TargetBoundComponentDescriptor } from "@prisma-next/framework-components/components";
|
|
9
|
-
import { MigrationPlanWithAuthoringSurface, MigrationPlanner, MigrationScaffoldContext } from "@prisma-next/framework-components/control";
|
|
10
10
|
|
|
11
11
|
//#region src/core/migrations/planner.d.ts
|
|
12
12
|
interface PlannerConfig {
|
|
@@ -64,8 +64,14 @@ declare class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postg
|
|
|
64
64
|
readonly fromContract: Contract | null;
|
|
65
65
|
readonly schemaName?: string;
|
|
66
66
|
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
|
|
67
|
+
/**
|
|
68
|
+
* Contract space this plan applies to. Stamped onto the produced
|
|
69
|
+
* {@link TypeScriptRenderablePostgresMigration.spaceId} so the runner keys
|
|
70
|
+
* the marker row by the right space.
|
|
71
|
+
*/
|
|
72
|
+
readonly spaceId: string;
|
|
67
73
|
}): PostgresPlanResult;
|
|
68
|
-
emptyMigration(context: MigrationScaffoldContext): MigrationPlanWithAuthoringSurface;
|
|
74
|
+
emptyMigration(context: MigrationScaffoldContext, spaceId: string): MigrationPlanWithAuthoringSurface;
|
|
69
75
|
private planSql;
|
|
70
76
|
private ensureAdditivePolicy;
|
|
71
77
|
private collectSchemaIssues;
|
package/dist/planner.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner.d.mts","names":[],"sources":["../src/core/migrations/planner.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;UAmCU,aAAA;;;iBAQM,8BAAA,UACN,QAAQ,iBACf;;;AA3B2F;AAyB9F;;;;AAE2B,KAcf,kBAAA,GAde;EAcf,SAAA,IAAA,EAAA,SAAkB;EAoBjB,SAAA,IAAA,EAnBkC,qCAmBT;CACC,GAnBnC,uBAmBmC;;;;;;;;;;;;;;;;;cAD1B,wBAAA,YAAoC;;sBACV;;;;qBAKlB;;;;;;;;;;;;;;;2BAeM;;kCAEO,cAAc;
|
|
1
|
+
{"version":3,"file":"planner.d.mts","names":[],"sources":["../src/core/migrations/planner.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;UAmCU,aAAA;;;iBAQM,8BAAA,UACN,QAAQ,iBACf;;;AA3B2F;AAyB9F;;;;AAE2B,KAcf,kBAAA,GAde;EAcf,SAAA,IAAA,EAAA,SAAkB;EAoBjB,SAAA,IAAA,EAnBkC,qCAmBT;CACC,GAnBnC,uBAmBmC;;;;;;;;;;;;;;;;;cAD1B,wBAAA,YAAoC;;sBACV;;;;qBAKlB;;;;;;;;;;;;;;;2BAeM;;kCAEO,cAAc;;;;;;;MAO1C;0BAKO,4CAER"}
|
package/dist/planner.mjs
CHANGED
|
@@ -2,10 +2,10 @@ import { t as PostgresPlanTargetDetails } from "./planner-target-details-COAiKZj
|
|
|
2
2
|
import { n as DataTransformOptions } from "./data-transform-T71mQkVW.mjs";
|
|
3
3
|
import { SqlMigrationPlanOperation } from "@prisma-next/family-sql/control";
|
|
4
4
|
import { Migration } from "@prisma-next/family-sql/migration";
|
|
5
|
+
import { ControlStack } from "@prisma-next/framework-components/control";
|
|
5
6
|
import { Contract } from "@prisma-next/contract/types";
|
|
6
7
|
import { SqlStorage } from "@prisma-next/sql-contract/types";
|
|
7
8
|
import { SqlControlAdapter } from "@prisma-next/family-sql/control-adapter";
|
|
8
|
-
import { ControlStack } from "@prisma-next/framework-components/control";
|
|
9
9
|
|
|
10
10
|
//#region src/core/migrations/postgres-migration.d.ts
|
|
11
11
|
|
|
@@ -48,4 +48,4 @@ declare abstract class PostgresMigration extends Migration<PostgresPlanTargetDet
|
|
|
48
48
|
}
|
|
49
49
|
//#endregion
|
|
50
50
|
export { PostgresMigration as t };
|
|
51
|
-
//# sourceMappingURL=postgres-migration-
|
|
51
|
+
//# sourceMappingURL=postgres-migration-RXzk7OX7.d.mts.map
|
package/dist/{postgres-migration-Dzxr5BCy.d.mts.map → postgres-migration-RXzk7OX7.d.mts.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-migration-
|
|
1
|
+
{"version":3,"file":"postgres-migration-RXzk7OX7.d.mts","names":[],"sources":["../src/core/migrations/postgres-migration.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;AA8BA;;;;;;;;;;;;;;;;;;uBAAsB,iBAAA,SAA0B,UAC9C;;;;;;;;qCAWmC;sBAEf;;;;;;4CAesB,SAAS,uBACvC,kCAED,uBACR,0BAA0B"}
|
|
@@ -1,11 +1,22 @@
|
|
|
1
|
+
import { APP_SPACE_ID } from "@prisma-next/framework-components/control";
|
|
2
|
+
|
|
1
3
|
//#region src/core/migrations/statement-builders.ts
|
|
2
4
|
const ensurePrismaContractSchemaStatement = {
|
|
3
5
|
sql: "create schema if not exists prisma_contract",
|
|
4
6
|
params: []
|
|
5
7
|
};
|
|
8
|
+
/**
|
|
9
|
+
* Schema for `prisma_contract.marker`. The `space text` primary key
|
|
10
|
+
* supports one row per loaded contract space (`'app'`,
|
|
11
|
+
* `'<extension-id>'`, …); on a brand-new database `CREATE TABLE IF NOT
|
|
12
|
+
* EXISTS` produces this shape directly. The migration runner detects
|
|
13
|
+
* pre-1.0 single-row markers (no `space` column) at boot and fails with
|
|
14
|
+
* a structured `LEGACY_MARKER_SHAPE` error rather than auto-migrating —
|
|
15
|
+
* see `specs/framework-mechanism.spec.md § 2`.
|
|
16
|
+
*/
|
|
6
17
|
const ensureMarkerTableStatement = {
|
|
7
18
|
sql: `create table if not exists prisma_contract.marker (
|
|
8
|
-
|
|
19
|
+
space text not null primary key default '${APP_SPACE_ID}',
|
|
9
20
|
core_hash text not null,
|
|
10
21
|
profile_hash text not null,
|
|
11
22
|
contract_json jsonb,
|
|
@@ -33,7 +44,7 @@ const ensureLedgerTableStatement = {
|
|
|
33
44
|
};
|
|
34
45
|
function buildMergeMarkerStatements(input) {
|
|
35
46
|
const params = [
|
|
36
|
-
|
|
47
|
+
input.space,
|
|
37
48
|
input.storageHash,
|
|
38
49
|
input.profileHash,
|
|
39
50
|
jsonParam(input.contractJson),
|
|
@@ -45,7 +56,7 @@ function buildMergeMarkerStatements(input) {
|
|
|
45
56
|
return {
|
|
46
57
|
insert: {
|
|
47
58
|
sql: `insert into prisma_contract.marker (
|
|
48
|
-
|
|
59
|
+
space,
|
|
49
60
|
core_hash,
|
|
50
61
|
profile_hash,
|
|
51
62
|
contract_json,
|
|
@@ -77,7 +88,7 @@ function buildMergeMarkerStatements(input) {
|
|
|
77
88
|
app_tag = $6,
|
|
78
89
|
meta = $7::jsonb,
|
|
79
90
|
invariants = array(select distinct unnest(invariants || $8::text[]) order by 1)
|
|
80
|
-
where
|
|
91
|
+
where space = $1`,
|
|
81
92
|
params
|
|
82
93
|
}
|
|
83
94
|
};
|
|
@@ -117,5 +128,5 @@ function jsonParam(value) {
|
|
|
117
128
|
}
|
|
118
129
|
|
|
119
130
|
//#endregion
|
|
120
|
-
export {
|
|
121
|
-
//# sourceMappingURL=statement-builders-
|
|
131
|
+
export { ensureMarkerTableStatement as a, ensureLedgerTableStatement as i, buildLedgerInsertStatement as n, ensurePrismaContractSchemaStatement as o, buildMergeMarkerStatements as r, APP_SPACE_ID as t };
|
|
132
|
+
//# sourceMappingURL=statement-builders-sOpc5QM-.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statement-builders-sOpc5QM-.mjs","names":["ensurePrismaContractSchemaStatement: SqlStatement","ensureMarkerTableStatement: SqlStatement","ensureLedgerTableStatement: SqlStatement","params: readonly unknown[]"],"sources":["../src/core/migrations/statement-builders.ts"],"sourcesContent":["import { APP_SPACE_ID } from '@prisma-next/framework-components/control';\n\nexport { APP_SPACE_ID };\n\nexport interface SqlStatement {\n readonly sql: string;\n readonly params: readonly unknown[];\n}\n\nexport const ensurePrismaContractSchemaStatement: SqlStatement = {\n sql: 'create schema if not exists prisma_contract',\n params: [],\n};\n\n/**\n * Schema for `prisma_contract.marker`. The `space text` primary key\n * supports one row per loaded contract space (`'app'`,\n * `'<extension-id>'`, …); on a brand-new database `CREATE TABLE IF NOT\n * EXISTS` produces this shape directly. The migration runner detects\n * pre-1.0 single-row markers (no `space` column) at boot and fails with\n * a structured `LEGACY_MARKER_SHAPE` error rather than auto-migrating —\n * see `specs/framework-mechanism.spec.md § 2`.\n */\nexport const ensureMarkerTableStatement: SqlStatement = {\n sql: `create table if not exists prisma_contract.marker (\n space text not null primary key default '${APP_SPACE_ID}',\n core_hash text not null,\n profile_hash text not null,\n contract_json jsonb,\n canonical_version int,\n updated_at timestamptz not null default now(),\n app_tag text,\n meta jsonb not null default '{}',\n invariants text[] not null default '{}'\n )`,\n params: [],\n};\n\nexport const ensureLedgerTableStatement: SqlStatement = {\n sql: `create table if not exists prisma_contract.ledger (\n id bigserial primary key,\n created_at timestamptz not null default now(),\n origin_core_hash text,\n origin_profile_hash text,\n destination_core_hash text not null,\n destination_profile_hash text,\n contract_json_before jsonb,\n contract_json_after jsonb,\n operations jsonb not null\n )`,\n params: [],\n};\n\nexport interface MergeMarkerInput {\n /**\n * Logical space identifier for this marker row. Required at every\n * call site so the type system surfaces every place that needs to\n * thread the value (rather than letting an `?? APP_SPACE_ID`\n * fall-through silently collapse multi-space markers onto the\n * `'app'` row). App-plan callers pass {@link APP_SPACE_ID}\n * (`'app'`); per-extension callers (planner / runner / verifier\n * extensions over contract spaces) pass the extension's space id.\n */\n readonly space: string;\n readonly storageHash: string;\n readonly profileHash: string;\n readonly contractJson?: unknown;\n readonly canonicalVersion?: number | null;\n readonly appTag?: string | null;\n readonly meta?: Record<string, unknown>;\n /**\n * Invariants to merge into `marker.invariants`. INSERT writes them as\n * the initial value (callers are expected to pass a sorted, deduped\n * array). UPDATE merges them with the existing column server-side via\n * a single atomic SQL expression.\n */\n readonly invariants: readonly string[];\n}\n\nexport function buildMergeMarkerStatements(input: MergeMarkerInput): {\n readonly insert: SqlStatement;\n readonly update: SqlStatement;\n} {\n const params: readonly unknown[] = [\n input.space,\n input.storageHash,\n input.profileHash,\n jsonParam(input.contractJson),\n input.canonicalVersion ?? null,\n input.appTag ?? null,\n jsonParam(input.meta ?? {}),\n input.invariants,\n ];\n\n return {\n insert: {\n sql: `insert into prisma_contract.marker (\n space,\n core_hash,\n profile_hash,\n contract_json,\n canonical_version,\n updated_at,\n app_tag,\n meta,\n invariants\n ) values (\n $1,\n $2,\n $3,\n $4::jsonb,\n $5,\n now(),\n $6,\n $7::jsonb,\n $8::text[]\n )`,\n params,\n },\n update: {\n // `invariants = array(select distinct unnest(invariants || $8::text[]) order by 1)`\n // reads the current column value under the UPDATE's row lock, unions\n // with the incoming array, dedupes, and sorts ascending — single\n // statement, atomic, no read-then-write window.\n sql: `update prisma_contract.marker set\n core_hash = $2,\n profile_hash = $3,\n contract_json = $4::jsonb,\n canonical_version = $5,\n updated_at = now(),\n app_tag = $6,\n meta = $7::jsonb,\n invariants = array(select distinct unnest(invariants || $8::text[]) order by 1)\n where space = $1`,\n params,\n },\n };\n}\n\nexport interface LedgerInsertInput {\n readonly originStorageHash?: string | null;\n readonly originProfileHash?: string | null;\n readonly destinationStorageHash: string;\n readonly destinationProfileHash?: string | null;\n readonly contractJsonBefore?: unknown;\n readonly contractJsonAfter?: unknown;\n readonly operations: unknown;\n}\n\nexport function buildLedgerInsertStatement(input: LedgerInsertInput): SqlStatement {\n return {\n sql: `insert into prisma_contract.ledger (\n origin_core_hash,\n origin_profile_hash,\n destination_core_hash,\n destination_profile_hash,\n contract_json_before,\n contract_json_after,\n operations\n ) values (\n $1,\n $2,\n $3,\n $4,\n $5::jsonb,\n $6::jsonb,\n $7::jsonb\n )`,\n params: [\n input.originStorageHash ?? null,\n input.originProfileHash ?? null,\n input.destinationStorageHash,\n input.destinationProfileHash ?? null,\n jsonParam(input.contractJsonBefore),\n jsonParam(input.contractJsonAfter),\n jsonParam(input.operations),\n ],\n };\n}\n\nfunction jsonParam(value: unknown): string {\n return JSON.stringify(value ?? null);\n}\n"],"mappings":";;;AASA,MAAaA,sCAAoD;CAC/D,KAAK;CACL,QAAQ,EAAE;CACX;;;;;;;;;;AAWD,MAAaC,6BAA2C;CACtD,KAAK;+CACwC,aAAa;;;;;;;;;;CAU1D,QAAQ,EAAE;CACX;AAED,MAAaC,6BAA2C;CACtD,KAAK;;;;;;;;;;;CAWL,QAAQ,EAAE;CACX;AA4BD,SAAgB,2BAA2B,OAGzC;CACA,MAAMC,SAA6B;EACjC,MAAM;EACN,MAAM;EACN,MAAM;EACN,UAAU,MAAM,aAAa;EAC7B,MAAM,oBAAoB;EAC1B,MAAM,UAAU;EAChB,UAAU,MAAM,QAAQ,EAAE,CAAC;EAC3B,MAAM;EACP;AAED,QAAO;EACL,QAAQ;GACN,KAAK;;;;;;;;;;;;;;;;;;;;;GAqBL;GACD;EACD,QAAQ;GAKN,KAAK;;;;;;;;;;GAUL;GACD;EACF;;AAaH,SAAgB,2BAA2B,OAAwC;AACjF,QAAO;EACL,KAAK;;;;;;;;;;;;;;;;;EAiBL,QAAQ;GACN,MAAM,qBAAqB;GAC3B,MAAM,qBAAqB;GAC3B,MAAM;GACN,MAAM,0BAA0B;GAChC,UAAU,MAAM,mBAAmB;GACnC,UAAU,MAAM,kBAAkB;GAClC,UAAU,MAAM,WAAW;GAC5B;EACF;;AAGH,SAAS,UAAU,OAAwB;AACzC,QAAO,KAAK,UAAU,SAAS,KAAK"}
|
|
@@ -1,12 +1,33 @@
|
|
|
1
|
+
import { APP_SPACE_ID } from "@prisma-next/framework-components/control";
|
|
2
|
+
|
|
1
3
|
//#region src/core/migrations/statement-builders.d.ts
|
|
2
4
|
interface SqlStatement {
|
|
3
5
|
readonly sql: string;
|
|
4
6
|
readonly params: readonly unknown[];
|
|
5
7
|
}
|
|
6
8
|
declare const ensurePrismaContractSchemaStatement: SqlStatement;
|
|
9
|
+
/**
|
|
10
|
+
* Schema for `prisma_contract.marker`. The `space text` primary key
|
|
11
|
+
* supports one row per loaded contract space (`'app'`,
|
|
12
|
+
* `'<extension-id>'`, …); on a brand-new database `CREATE TABLE IF NOT
|
|
13
|
+
* EXISTS` produces this shape directly. The migration runner detects
|
|
14
|
+
* pre-1.0 single-row markers (no `space` column) at boot and fails with
|
|
15
|
+
* a structured `LEGACY_MARKER_SHAPE` error rather than auto-migrating —
|
|
16
|
+
* see `specs/framework-mechanism.spec.md § 2`.
|
|
17
|
+
*/
|
|
7
18
|
declare const ensureMarkerTableStatement: SqlStatement;
|
|
8
19
|
declare const ensureLedgerTableStatement: SqlStatement;
|
|
9
20
|
interface MergeMarkerInput {
|
|
21
|
+
/**
|
|
22
|
+
* Logical space identifier for this marker row. Required at every
|
|
23
|
+
* call site so the type system surfaces every place that needs to
|
|
24
|
+
* thread the value (rather than letting an `?? APP_SPACE_ID`
|
|
25
|
+
* fall-through silently collapse multi-space markers onto the
|
|
26
|
+
* `'app'` row). App-plan callers pass {@link APP_SPACE_ID}
|
|
27
|
+
* (`'app'`); per-extension callers (planner / runner / verifier
|
|
28
|
+
* extensions over contract spaces) pass the extension's space id.
|
|
29
|
+
*/
|
|
30
|
+
readonly space: string;
|
|
10
31
|
readonly storageHash: string;
|
|
11
32
|
readonly profileHash: string;
|
|
12
33
|
readonly contractJson?: unknown;
|
|
@@ -26,5 +47,5 @@ declare function buildMergeMarkerStatements(input: MergeMarkerInput): {
|
|
|
26
47
|
readonly update: SqlStatement;
|
|
27
48
|
};
|
|
28
49
|
//#endregion
|
|
29
|
-
export { type SqlStatement, buildMergeMarkerStatements, ensureLedgerTableStatement, ensureMarkerTableStatement, ensurePrismaContractSchemaStatement };
|
|
50
|
+
export { APP_SPACE_ID, type SqlStatement, buildMergeMarkerStatements, ensureLedgerTableStatement, ensureMarkerTableStatement, ensurePrismaContractSchemaStatement };
|
|
30
51
|
//# sourceMappingURL=statement-builders.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statement-builders.d.mts","names":[],"sources":["../src/core/migrations/statement-builders.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"statement-builders.d.mts","names":[],"sources":["../src/core/migrations/statement-builders.ts"],"sourcesContent":[],"mappings":";;;UAIiB,YAAA;EAAA,SAAA,GAAA,EAAA,MAAY;EAKhB,SAAA,MAAA,EAAA,SAAA,OAAA,EAAA;AAcb;AAea,cA7BA,mCA6B4B,EA7BS,YA0CjD;AAED;AA0BA;;;;;;;;cAxDa,4BAA4B;cAe5B,4BAA4B;UAexB,gBAAA;;;;;;;;;;;;;;;;kBAgBC;;;;;;;;;iBAUF,0BAAA,QAAkC;mBAC/B;mBACA"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as ensureMarkerTableStatement, i as ensureLedgerTableStatement, o as ensurePrismaContractSchemaStatement, r as buildMergeMarkerStatements, t as APP_SPACE_ID } from "./statement-builders-sOpc5QM-.mjs";
|
|
2
2
|
|
|
3
|
-
export { buildMergeMarkerStatements, ensureLedgerTableStatement, ensureMarkerTableStatement, ensurePrismaContractSchemaStatement };
|
|
3
|
+
export { APP_SPACE_ID, buildMergeMarkerStatements, ensureLedgerTableStatement, ensureMarkerTableStatement, ensurePrismaContractSchemaStatement };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/target-postgres",
|
|
3
|
-
"version": "0.5.0-dev.
|
|
3
|
+
"version": "0.5.0-dev.63",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -9,26 +9,26 @@
|
|
|
9
9
|
"@standard-schema/spec": "^1.1.0",
|
|
10
10
|
"arktype": "^2.0.0",
|
|
11
11
|
"pathe": "^2.0.3",
|
|
12
|
-
"@prisma-next/cli": "0.5.0-dev.
|
|
13
|
-
"@prisma-next/
|
|
14
|
-
"@prisma-next/
|
|
15
|
-
"@prisma-next/family-sql": "0.5.0-dev.
|
|
16
|
-
"@prisma-next/framework-components": "0.5.0-dev.
|
|
17
|
-
"@prisma-next/migration-tools": "0.5.0-dev.
|
|
18
|
-
"@prisma-next/
|
|
19
|
-
"@prisma-next/sql-contract": "0.5.0-dev.
|
|
20
|
-
"@prisma-next/sql-
|
|
21
|
-
"@prisma-next/
|
|
22
|
-
"@prisma-next/sql-
|
|
23
|
-
"@prisma-next/
|
|
24
|
-
"@prisma-next/
|
|
12
|
+
"@prisma-next/cli": "0.5.0-dev.63",
|
|
13
|
+
"@prisma-next/contract": "0.5.0-dev.63",
|
|
14
|
+
"@prisma-next/errors": "0.5.0-dev.63",
|
|
15
|
+
"@prisma-next/family-sql": "0.5.0-dev.63",
|
|
16
|
+
"@prisma-next/framework-components": "0.5.0-dev.63",
|
|
17
|
+
"@prisma-next/migration-tools": "0.5.0-dev.63",
|
|
18
|
+
"@prisma-next/ts-render": "0.5.0-dev.63",
|
|
19
|
+
"@prisma-next/sql-contract": "0.5.0-dev.63",
|
|
20
|
+
"@prisma-next/sql-errors": "0.5.0-dev.63",
|
|
21
|
+
"@prisma-next/sql-operations": "0.5.0-dev.63",
|
|
22
|
+
"@prisma-next/sql-schema-ir": "0.5.0-dev.63",
|
|
23
|
+
"@prisma-next/utils": "0.5.0-dev.63",
|
|
24
|
+
"@prisma-next/sql-relational-core": "0.5.0-dev.63"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"tsdown": "0.18.4",
|
|
28
28
|
"typescript": "5.9.3",
|
|
29
29
|
"vitest": "4.0.17",
|
|
30
|
-
"@prisma-next/tsconfig": "0.0.0",
|
|
31
30
|
"@prisma-next/test-utils": "0.0.1",
|
|
31
|
+
"@prisma-next/tsconfig": "0.0.0",
|
|
32
32
|
"@prisma-next/tsdown": "0.0.0"
|
|
33
33
|
},
|
|
34
34
|
"files": [
|
|
@@ -41,11 +41,13 @@ export class TypeScriptRenderablePostgresMigration
|
|
|
41
41
|
{
|
|
42
42
|
readonly #calls: readonly PostgresOpFactoryCall[];
|
|
43
43
|
readonly #meta: MigrationMeta;
|
|
44
|
+
readonly #spaceId: string;
|
|
44
45
|
|
|
45
|
-
constructor(calls: readonly PostgresOpFactoryCall[], meta: MigrationMeta) {
|
|
46
|
+
constructor(calls: readonly PostgresOpFactoryCall[], meta: MigrationMeta, spaceId: string) {
|
|
46
47
|
super();
|
|
47
48
|
this.#calls = calls;
|
|
48
49
|
this.#meta = meta;
|
|
50
|
+
this.#spaceId = spaceId;
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
override get operations(): readonly Op[] {
|
|
@@ -56,6 +58,15 @@ export class TypeScriptRenderablePostgresMigration
|
|
|
56
58
|
return this.#meta;
|
|
57
59
|
}
|
|
58
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Contract space this planner-produced plan applies to. Threaded
|
|
63
|
+
* from the planner options so the runner keys the marker row by
|
|
64
|
+
* the right space when executing the plan.
|
|
65
|
+
*/
|
|
66
|
+
get spaceId(): string {
|
|
67
|
+
return this.#spaceId;
|
|
68
|
+
}
|
|
69
|
+
|
|
59
70
|
renderTypeScript(): string {
|
|
60
71
|
return renderCallsToTypeScript(this.#calls, {
|
|
61
72
|
from: this.#meta.from,
|
|
@@ -101,15 +101,28 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
101
101
|
readonly fromContract: Contract | null;
|
|
102
102
|
readonly schemaName?: string;
|
|
103
103
|
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
|
|
104
|
+
/**
|
|
105
|
+
* Contract space this plan applies to. Stamped onto the produced
|
|
106
|
+
* {@link TypeScriptRenderablePostgresMigration.spaceId} so the runner keys
|
|
107
|
+
* the marker row by the right space.
|
|
108
|
+
*/
|
|
109
|
+
readonly spaceId: string;
|
|
104
110
|
}): PostgresPlanResult {
|
|
105
111
|
return this.planSql(options as SqlMigrationPlannerPlanOptions);
|
|
106
112
|
}
|
|
107
113
|
|
|
108
|
-
emptyMigration(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
114
|
+
emptyMigration(
|
|
115
|
+
context: MigrationScaffoldContext,
|
|
116
|
+
spaceId: string,
|
|
117
|
+
): MigrationPlanWithAuthoringSurface {
|
|
118
|
+
return new TypeScriptRenderablePostgresMigration(
|
|
119
|
+
[],
|
|
120
|
+
{
|
|
121
|
+
from: context.fromHash,
|
|
122
|
+
to: context.toHash,
|
|
123
|
+
},
|
|
124
|
+
spaceId,
|
|
125
|
+
);
|
|
113
126
|
}
|
|
114
127
|
|
|
115
128
|
private planSql(options: SqlMigrationPlannerPlanOptions): PostgresPlanResult {
|
|
@@ -147,10 +160,14 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
147
160
|
|
|
148
161
|
return Object.freeze({
|
|
149
162
|
kind: 'success' as const,
|
|
150
|
-
plan: new TypeScriptRenderablePostgresMigration(
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
163
|
+
plan: new TypeScriptRenderablePostgresMigration(
|
|
164
|
+
result.value.calls,
|
|
165
|
+
{
|
|
166
|
+
from: options.fromContract?.storage.storageHash ?? null,
|
|
167
|
+
to: options.contract.storage.storageHash,
|
|
168
|
+
},
|
|
169
|
+
options.spaceId,
|
|
170
|
+
),
|
|
154
171
|
});
|
|
155
172
|
}
|
|
156
173
|
|
|
@@ -105,8 +105,14 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
105
105
|
let committed = false;
|
|
106
106
|
try {
|
|
107
107
|
await this.acquireLock(driver, lockKey);
|
|
108
|
-
await this.ensureControlTables(driver);
|
|
109
|
-
|
|
108
|
+
const ensureResult = await this.ensureControlTables(driver);
|
|
109
|
+
if (!ensureResult.ok) {
|
|
110
|
+
return ensureResult;
|
|
111
|
+
}
|
|
112
|
+
const existingMarker = await this.family.readMarker({
|
|
113
|
+
driver,
|
|
114
|
+
space: options.plan.spaceId,
|
|
115
|
+
});
|
|
110
116
|
|
|
111
117
|
// Validate plan origin matches existing marker (needs marker from DB)
|
|
112
118
|
const markerCheck = this.ensureMarkerCompatibility(existingMarker, options.plan);
|
|
@@ -271,10 +277,49 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
271
277
|
|
|
272
278
|
private async ensureControlTables(
|
|
273
279
|
driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],
|
|
274
|
-
): Promise<void
|
|
280
|
+
): Promise<Result<void, SqlMigrationRunnerFailure>> {
|
|
275
281
|
await this.executeStatement(driver, ensurePrismaContractSchemaStatement);
|
|
282
|
+
// Pre-1.0 zero-range guardrail: detect a pre-cleanup single-row
|
|
283
|
+
// marker table (no `space` column) and surface a structured failure
|
|
284
|
+
// rather than silently auto-migrating it to the per-space shape.
|
|
285
|
+
// See `specs/framework-mechanism.spec.md § 2`.
|
|
286
|
+
const legacyDetection = await this.detectLegacyMarkerShape(driver);
|
|
287
|
+
if (!legacyDetection.ok) {
|
|
288
|
+
return legacyDetection;
|
|
289
|
+
}
|
|
276
290
|
await this.executeStatement(driver, ensureMarkerTableStatement);
|
|
277
291
|
await this.executeStatement(driver, ensureLedgerTableStatement);
|
|
292
|
+
return okVoid();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private async detectLegacyMarkerShape(
|
|
296
|
+
driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],
|
|
297
|
+
): Promise<Result<void, SqlMigrationRunnerFailure>> {
|
|
298
|
+
const result = await driver.query<{ column_name: string }>(
|
|
299
|
+
`select column_name
|
|
300
|
+
from information_schema.columns
|
|
301
|
+
where table_schema = 'prisma_contract'
|
|
302
|
+
and table_name = 'marker'`,
|
|
303
|
+
);
|
|
304
|
+
if (result.rows.length === 0) {
|
|
305
|
+
return okVoid();
|
|
306
|
+
}
|
|
307
|
+
const columns = new Set(result.rows.map((row) => row.column_name));
|
|
308
|
+
if (columns.has('space')) {
|
|
309
|
+
return okVoid();
|
|
310
|
+
}
|
|
311
|
+
return runnerFailure(
|
|
312
|
+
'LEGACY_MARKER_SHAPE',
|
|
313
|
+
'Legacy marker-table shape detected on prisma_contract.marker (no `space` column). ' +
|
|
314
|
+
'Prisma Next is in pre-1.0; the previous transitional auto-migration to the per-space-row schema has been removed. ' +
|
|
315
|
+
'Drop `prisma_contract.marker` and re-run `dbInit` to reinitialise from a clean baseline.',
|
|
316
|
+
{
|
|
317
|
+
meta: {
|
|
318
|
+
table: 'prisma_contract.marker',
|
|
319
|
+
columns: [...columns].sort(),
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
);
|
|
278
323
|
}
|
|
279
324
|
|
|
280
325
|
private async runExpectationSteps(
|
|
@@ -548,6 +593,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
548
593
|
): Promise<void> {
|
|
549
594
|
const incomingInvariants = options.plan.providedInvariants ?? [];
|
|
550
595
|
const writeStatements = buildMergeMarkerStatements({
|
|
596
|
+
space: options.plan.spaceId,
|
|
551
597
|
storageHash: options.plan.destination.storageHash,
|
|
552
598
|
profileHash:
|
|
553
599
|
options.plan.destination.profileHash ??
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { APP_SPACE_ID } from '@prisma-next/framework-components/control';
|
|
2
|
+
|
|
3
|
+
export { APP_SPACE_ID };
|
|
4
|
+
|
|
1
5
|
export interface SqlStatement {
|
|
2
6
|
readonly sql: string;
|
|
3
7
|
readonly params: readonly unknown[];
|
|
@@ -8,9 +12,18 @@ export const ensurePrismaContractSchemaStatement: SqlStatement = {
|
|
|
8
12
|
params: [],
|
|
9
13
|
};
|
|
10
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Schema for `prisma_contract.marker`. The `space text` primary key
|
|
17
|
+
* supports one row per loaded contract space (`'app'`,
|
|
18
|
+
* `'<extension-id>'`, …); on a brand-new database `CREATE TABLE IF NOT
|
|
19
|
+
* EXISTS` produces this shape directly. The migration runner detects
|
|
20
|
+
* pre-1.0 single-row markers (no `space` column) at boot and fails with
|
|
21
|
+
* a structured `LEGACY_MARKER_SHAPE` error rather than auto-migrating —
|
|
22
|
+
* see `specs/framework-mechanism.spec.md § 2`.
|
|
23
|
+
*/
|
|
11
24
|
export const ensureMarkerTableStatement: SqlStatement = {
|
|
12
25
|
sql: `create table if not exists prisma_contract.marker (
|
|
13
|
-
|
|
26
|
+
space text not null primary key default '${APP_SPACE_ID}',
|
|
14
27
|
core_hash text not null,
|
|
15
28
|
profile_hash text not null,
|
|
16
29
|
contract_json jsonb,
|
|
@@ -39,6 +52,16 @@ export const ensureLedgerTableStatement: SqlStatement = {
|
|
|
39
52
|
};
|
|
40
53
|
|
|
41
54
|
export interface MergeMarkerInput {
|
|
55
|
+
/**
|
|
56
|
+
* Logical space identifier for this marker row. Required at every
|
|
57
|
+
* call site so the type system surfaces every place that needs to
|
|
58
|
+
* thread the value (rather than letting an `?? APP_SPACE_ID`
|
|
59
|
+
* fall-through silently collapse multi-space markers onto the
|
|
60
|
+
* `'app'` row). App-plan callers pass {@link APP_SPACE_ID}
|
|
61
|
+
* (`'app'`); per-extension callers (planner / runner / verifier
|
|
62
|
+
* extensions over contract spaces) pass the extension's space id.
|
|
63
|
+
*/
|
|
64
|
+
readonly space: string;
|
|
42
65
|
readonly storageHash: string;
|
|
43
66
|
readonly profileHash: string;
|
|
44
67
|
readonly contractJson?: unknown;
|
|
@@ -59,7 +82,7 @@ export function buildMergeMarkerStatements(input: MergeMarkerInput): {
|
|
|
59
82
|
readonly update: SqlStatement;
|
|
60
83
|
} {
|
|
61
84
|
const params: readonly unknown[] = [
|
|
62
|
-
|
|
85
|
+
input.space,
|
|
63
86
|
input.storageHash,
|
|
64
87
|
input.profileHash,
|
|
65
88
|
jsonParam(input.contractJson),
|
|
@@ -72,7 +95,7 @@ export function buildMergeMarkerStatements(input: MergeMarkerInput): {
|
|
|
72
95
|
return {
|
|
73
96
|
insert: {
|
|
74
97
|
sql: `insert into prisma_contract.marker (
|
|
75
|
-
|
|
98
|
+
space,
|
|
76
99
|
core_hash,
|
|
77
100
|
profile_hash,
|
|
78
101
|
contract_json,
|
|
@@ -108,7 +131,7 @@ export function buildMergeMarkerStatements(input: MergeMarkerInput): {
|
|
|
108
131
|
app_tag = $6,
|
|
109
132
|
meta = $7::jsonb,
|
|
110
133
|
invariants = array(select distinct unnest(invariants || $8::text[]) order by 1)
|
|
111
|
-
where
|
|
134
|
+
where space = $1`,
|
|
112
135
|
params,
|
|
113
136
|
},
|
|
114
137
|
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"planner-BMtFbKfn.mjs","names":["DEFAULT_PLANNER_CONFIG: PlannerConfig","config: PlannerConfig"],"sources":["../src/core/migrations/planner.ts"],"sourcesContent":["import type { Contract } from '@prisma-next/contract/types';\nimport type {\n MigrationOperationPolicy,\n SqlMigrationPlannerPlanOptions,\n SqlPlannerFailureResult,\n} from '@prisma-next/family-sql/control';\nimport { extractCodecControlHooks, plannerFailure } from '@prisma-next/family-sql/control';\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 { parsePostgresDefault } from '../default-normalizer';\nimport { normalizeSchemaNativeType } from '../native-type-normalizer';\nimport { planIssues } from './issue-planner';\nimport { TypeScriptRenderablePostgresMigration } from './planner-produced-postgres-migration';\nimport { postgresPlannerStrategies } from './planner-strategies';\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\ninterface PlannerConfig {\n readonly defaultSchema: string;\n}\n\nconst DEFAULT_PLANNER_CONFIG: PlannerConfig = {\n defaultSchema: 'public',\n};\n\nexport function createPostgresMigrationPlanner(\n config: Partial<PlannerConfig> = {},\n): PostgresMigrationPlanner {\n return new PostgresMigrationPlanner({\n ...DEFAULT_PLANNER_CONFIG,\n ...config,\n });\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 | { readonly kind: 'success'; readonly plan: TypeScriptRenderablePostgresMigration }\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 constructor(private readonly config: PlannerConfig) {}\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 }): PostgresPlanResult {\n return this.planSql(options as SqlMigrationPlannerPlanOptions);\n }\n\n emptyMigration(context: MigrationScaffoldContext): MigrationPlanWithAuthoringSurface {\n return new TypeScriptRenderablePostgresMigration([], {\n from: context.fromHash,\n to: context.toHash,\n });\n }\n\n private planSql(options: SqlMigrationPlannerPlanOptions): PostgresPlanResult {\n const schemaName = options.schemaName ?? this.config.defaultSchema;\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 const result = planIssues({\n issues: schemaIssues,\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 return Object.freeze({\n kind: 'success' as const,\n plan: new TypeScriptRenderablePostgresMigration(result.value.calls, {\n from: options.fromContract?.storage.storageHash ?? null,\n to: options.contract.storage.storageHash,\n }),\n });\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 return verifyResult.schema.issues;\n }\n}\n"],"mappings":";;;;;;;;AAuCA,MAAMA,yBAAwC,EAC5C,eAAe,UAChB;AAED,SAAgB,+BACd,SAAiC,EAAE,EACT;AAC1B,QAAO,IAAI,yBAAyB;EAClC,GAAG;EACH,GAAG;EACJ,CAAC;;;;;;;;;;;;;;;;;;AA8BJ,IAAa,2BAAb,MAAqF;CACnF,YAAY,AAAiBC,QAAuB;EAAvB;;CAE7B,KAAK,SAqBkB;AACrB,SAAO,KAAK,QAAQ,QAA0C;;CAGhE,eAAe,SAAsE;AACnF,SAAO,IAAI,sCAAsC,EAAE,EAAE;GACnD,MAAM,QAAQ;GACd,IAAI,QAAQ;GACb,CAAC;;CAGJ,AAAQ,QAAQ,SAA6D;EAC3E,MAAM,aAAa,QAAQ,cAAc,KAAK,OAAO;EACrD,MAAM,eAAe,KAAK,qBAAqB,QAAQ,OAAO;AAC9D,MAAI,aACF,QAAO;EAGT,MAAM,eAAe,KAAK,oBAAoB,QAAQ;EACtD,MAAM,aAAa,yBAAyB,QAAQ,oBAAoB;EACxE,MAAM,eAAe,QAAQ,SAAS,QAAQ,SAAS,EAAE;EAEzD,MAAM,SAAS,WAAW;GACxB,QAAQ;GACR,YAAY,QAAQ;GAMpB,cAAc,QAAQ;GACtB;GACA;GACA;GACA,QAAQ,QAAQ;GAChB,QAAQ,QAAQ;GAChB,qBAAqB,QAAQ;GAC7B,YAAY;GACb,CAAC;AAEF,MAAI,CAAC,OAAO,GACV,QAAO,eAAe,OAAO,QAAQ;AAGvC,SAAO,OAAO,OAAO;GACnB,MAAM;GACN,MAAM,IAAI,sCAAsC,OAAO,MAAM,OAAO;IAClE,MAAM,QAAQ,cAAc,QAAQ,eAAe;IACnD,IAAI,QAAQ,SAAS,QAAQ;IAC9B,CAAC;GACH,CAAC;;CAGJ,AAAQ,qBAAqB,QAAkC;AAC7D,MAAI,CAAC,OAAO,wBAAwB,SAAS,WAAW,CACtD,QAAO,eAAe,CACpB;GACE,MAAM;GACN,SAAS;GACT,KAAK;GACN,CACF,CAAC;AAEJ,SAAO;;CAGT,AAAQ,oBAAoB,SAA+D;EAIzF,MAAM,UAAU,QAAQ,OAAO;EAC/B,MAAM,SAAS,QAAQ,SAAS,WAAW,IAAI,QAAQ,SAAS,cAAc;AAW9E,SADqB,gBATuC;GAC1D,UAAU,QAAQ;GAClB,QAAQ,QAAQ;GAChB;GACA,sCAAsB,IAAI,KAAK;GAC/B,qBAAqB,QAAQ;GAC7B,kBAAkB;GAClB,qBAAqB;GACtB,CACkD,CAC/B,OAAO"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"planner-produced-postgres-migration-C0vaAvA8.mjs","names":["#calls","#meta"],"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 { MigrationPlanWithAuthoringSurface } from '@prisma-next/framework-components/control';\nimport type { MigrationMeta } from '@prisma-next/migration-tools/migration';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { PostgresOpFactoryCall } from './op-factory-call';\nimport type { PostgresPlanTargetDetails } from './planner-target-details';\nimport { PostgresMigration } from './postgres-migration';\nimport { renderOps } from './render-ops';\nimport { renderCallsToTypeScript } from './render-typescript';\n\ntype Op = SqlMigrationPlanOperation<PostgresPlanTargetDetails>;\n\nexport class TypeScriptRenderablePostgresMigration\n extends PostgresMigration\n implements MigrationPlanWithAuthoringSurface\n{\n readonly #calls: readonly PostgresOpFactoryCall[];\n readonly #meta: MigrationMeta;\n\n constructor(calls: readonly PostgresOpFactoryCall[], meta: MigrationMeta) {\n super();\n this.#calls = calls;\n this.#meta = meta;\n }\n\n override get operations(): readonly Op[] {\n return renderOps(this.#calls);\n }\n\n override describe(): MigrationMeta {\n return this.#meta;\n }\n\n renderTypeScript(): string {\n return renderCallsToTypeScript(this.#calls, {\n from: this.#meta.from,\n to: this.#meta.to,\n ...ifDefined('labels', this.#meta.labels),\n });\n }\n}\n"],"mappings":";;;;;;AAqCA,IAAa,wCAAb,cACU,kBAEV;CACE,CAASA;CACT,CAASC;CAET,YAAY,OAAyC,MAAqB;AACxE,SAAO;AACP,QAAKD,QAAS;AACd,QAAKC,OAAQ;;CAGf,IAAa,aAA4B;AACvC,SAAO,UAAU,MAAKD,MAAO;;CAG/B,AAAS,WAA0B;AACjC,SAAO,MAAKC;;CAGd,mBAA2B;AACzB,SAAO,wBAAwB,MAAKD,OAAQ;GAC1C,MAAM,MAAKC,KAAM;GACjB,IAAI,MAAKA,KAAM;GACf,GAAG,UAAU,UAAU,MAAKA,KAAM,OAAO;GAC1C,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"statement-builders-Ckkq4ryf.mjs","names":["ensurePrismaContractSchemaStatement: SqlStatement","ensureMarkerTableStatement: SqlStatement","ensureLedgerTableStatement: SqlStatement","params: readonly unknown[]"],"sources":["../src/core/migrations/statement-builders.ts"],"sourcesContent":["export interface SqlStatement {\n readonly sql: string;\n readonly params: readonly unknown[];\n}\n\nexport const ensurePrismaContractSchemaStatement: SqlStatement = {\n sql: 'create schema if not exists prisma_contract',\n params: [],\n};\n\nexport const ensureMarkerTableStatement: SqlStatement = {\n sql: `create table if not exists prisma_contract.marker (\n id smallint primary key default 1,\n core_hash text not null,\n profile_hash text not null,\n contract_json jsonb,\n canonical_version int,\n updated_at timestamptz not null default now(),\n app_tag text,\n meta jsonb not null default '{}',\n invariants text[] not null default '{}'\n )`,\n params: [],\n};\n\nexport const ensureLedgerTableStatement: SqlStatement = {\n sql: `create table if not exists prisma_contract.ledger (\n id bigserial primary key,\n created_at timestamptz not null default now(),\n origin_core_hash text,\n origin_profile_hash text,\n destination_core_hash text not null,\n destination_profile_hash text,\n contract_json_before jsonb,\n contract_json_after jsonb,\n operations jsonb not null\n )`,\n params: [],\n};\n\nexport interface MergeMarkerInput {\n readonly storageHash: string;\n readonly profileHash: string;\n readonly contractJson?: unknown;\n readonly canonicalVersion?: number | null;\n readonly appTag?: string | null;\n readonly meta?: Record<string, unknown>;\n /**\n * Invariants to merge into `marker.invariants`. INSERT writes them as\n * the initial value (callers are expected to pass a sorted, deduped\n * array). UPDATE merges them with the existing column server-side via\n * a single atomic SQL expression.\n */\n readonly invariants: readonly string[];\n}\n\nexport function buildMergeMarkerStatements(input: MergeMarkerInput): {\n readonly insert: SqlStatement;\n readonly update: SqlStatement;\n} {\n const params: readonly unknown[] = [\n 1,\n input.storageHash,\n input.profileHash,\n jsonParam(input.contractJson),\n input.canonicalVersion ?? null,\n input.appTag ?? null,\n jsonParam(input.meta ?? {}),\n input.invariants,\n ];\n\n return {\n insert: {\n sql: `insert into prisma_contract.marker (\n id,\n core_hash,\n profile_hash,\n contract_json,\n canonical_version,\n updated_at,\n app_tag,\n meta,\n invariants\n ) values (\n $1,\n $2,\n $3,\n $4::jsonb,\n $5,\n now(),\n $6,\n $7::jsonb,\n $8::text[]\n )`,\n params,\n },\n update: {\n // `invariants = array(select distinct unnest(invariants || $8::text[]) order by 1)`\n // reads the current column value under the UPDATE's row lock, unions\n // with the incoming array, dedupes, and sorts ascending — single\n // statement, atomic, no read-then-write window.\n sql: `update prisma_contract.marker set\n core_hash = $2,\n profile_hash = $3,\n contract_json = $4::jsonb,\n canonical_version = $5,\n updated_at = now(),\n app_tag = $6,\n meta = $7::jsonb,\n invariants = array(select distinct unnest(invariants || $8::text[]) order by 1)\n where id = $1`,\n params,\n },\n };\n}\n\nexport interface LedgerInsertInput {\n readonly originStorageHash?: string | null;\n readonly originProfileHash?: string | null;\n readonly destinationStorageHash: string;\n readonly destinationProfileHash?: string | null;\n readonly contractJsonBefore?: unknown;\n readonly contractJsonAfter?: unknown;\n readonly operations: unknown;\n}\n\nexport function buildLedgerInsertStatement(input: LedgerInsertInput): SqlStatement {\n return {\n sql: `insert into prisma_contract.ledger (\n origin_core_hash,\n origin_profile_hash,\n destination_core_hash,\n destination_profile_hash,\n contract_json_before,\n contract_json_after,\n operations\n ) values (\n $1,\n $2,\n $3,\n $4,\n $5::jsonb,\n $6::jsonb,\n $7::jsonb\n )`,\n params: [\n input.originStorageHash ?? null,\n input.originProfileHash ?? null,\n input.destinationStorageHash,\n input.destinationProfileHash ?? null,\n jsonParam(input.contractJsonBefore),\n jsonParam(input.contractJsonAfter),\n jsonParam(input.operations),\n ],\n };\n}\n\nfunction jsonParam(value: unknown): string {\n return JSON.stringify(value ?? null);\n}\n"],"mappings":";AAKA,MAAaA,sCAAoD;CAC/D,KAAK;CACL,QAAQ,EAAE;CACX;AAED,MAAaC,6BAA2C;CACtD,KAAK;;;;;;;;;;;CAWL,QAAQ,EAAE;CACX;AAED,MAAaC,6BAA2C;CACtD,KAAK;;;;;;;;;;;CAWL,QAAQ,EAAE;CACX;AAkBD,SAAgB,2BAA2B,OAGzC;CACA,MAAMC,SAA6B;EACjC;EACA,MAAM;EACN,MAAM;EACN,UAAU,MAAM,aAAa;EAC7B,MAAM,oBAAoB;EAC1B,MAAM,UAAU;EAChB,UAAU,MAAM,QAAQ,EAAE,CAAC;EAC3B,MAAM;EACP;AAED,QAAO;EACL,QAAQ;GACN,KAAK;;;;;;;;;;;;;;;;;;;;;GAqBL;GACD;EACD,QAAQ;GAKN,KAAK;;;;;;;;;;GAUL;GACD;EACF;;AAaH,SAAgB,2BAA2B,OAAwC;AACjF,QAAO;EACL,KAAK;;;;;;;;;;;;;;;;;EAiBL,QAAQ;GACN,MAAM,qBAAqB;GAC3B,MAAM,qBAAqB;GAC3B,MAAM;GACN,MAAM,0BAA0B;GAChC,UAAU,MAAM,mBAAmB;GACnC,UAAU,MAAM,kBAAkB;GAClC,UAAU,MAAM,WAAW;GAC5B;EACF;;AAGH,SAAS,UAAU,OAAwB;AACzC,QAAO,KAAK,UAAU,SAAS,KAAK"}
|