@prisma-next/target-postgres 0.5.0-dev.2 → 0.5.0-dev.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/codec-types.d.mts +1 -1
- package/dist/{codecs-D-F2KJqt.d.mts → codecs-BQEm9_oo.d.mts} +31 -11
- package/dist/codecs-BQEm9_oo.d.mts.map +1 -0
- package/dist/codecs.d.mts +1 -1
- package/dist/control.mjs +7 -6
- package/dist/control.mjs.map +1 -1
- package/dist/{data-transform-VfEGzXWt.mjs → data-transform-C83dy0vk.mjs} +3 -1
- package/dist/data-transform-C83dy0vk.mjs.map +1 -0
- package/dist/{data-transform-CxFRBIUp.d.mts → data-transform-D8x5m1YV.d.mts} +7 -1
- package/dist/data-transform-D8x5m1YV.d.mts.map +1 -0
- package/dist/data-transform.d.mts +1 -1
- package/dist/data-transform.mjs +1 -1
- package/dist/migration.d.mts +2 -2
- package/dist/migration.mjs +2 -2
- package/dist/pack.d.mts +1 -1
- package/dist/{planner-CLUvVhUN.mjs → planner-VgGu_r8x.mjs} +2 -2
- package/dist/{planner-CLUvVhUN.mjs.map → planner-VgGu_r8x.mjs.map} +1 -1
- package/dist/{planner-produced-postgres-migration-DSSPq8QS.mjs → planner-produced-postgres-migration-CVD4g__D.mjs} +2 -2
- package/dist/{planner-produced-postgres-migration-DSSPq8QS.mjs.map → planner-produced-postgres-migration-CVD4g__D.mjs.map} +1 -1
- package/dist/{planner-produced-postgres-migration-CRRTno6Z.d.mts → planner-produced-postgres-migration-Dw_mPMKt.d.mts} +2 -2
- package/dist/planner-produced-postgres-migration-Dw_mPMKt.d.mts.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 +2 -2
- package/dist/planner.mjs +1 -1
- package/dist/{postgres-migration-BjA3Zmts.d.mts → postgres-migration-DcfWGqhe.d.mts} +2 -2
- package/dist/{postgres-migration-BjA3Zmts.d.mts.map → postgres-migration-DcfWGqhe.d.mts.map} +1 -1
- package/dist/{postgres-migration-qtmtbONe.mjs → postgres-migration-EGSlO4jO.mjs} +2 -2
- package/dist/{postgres-migration-qtmtbONe.mjs.map → postgres-migration-EGSlO4jO.mjs.map} +1 -1
- package/dist/{statement-builders-BPnmt6wx.mjs → statement-builders-CHqCtSfe.mjs} +13 -8
- package/dist/statement-builders-CHqCtSfe.mjs.map +1 -0
- package/dist/statement-builders.d.mts +10 -3
- package/dist/statement-builders.d.mts.map +1 -1
- package/dist/statement-builders.mjs +2 -2
- package/package.json +18 -15
- package/src/core/migrations/operations/data-transform.ts +8 -0
- package/src/core/migrations/runner.ts +7 -4
- package/src/core/migrations/statement-builders.ts +22 -6
- package/src/exports/statement-builders.ts +1 -1
- package/dist/codecs-D-F2KJqt.d.mts.map +0 -1
- package/dist/data-transform-CxFRBIUp.d.mts.map +0 -1
- package/dist/data-transform-VfEGzXWt.mjs.map +0 -1
- package/dist/planner-produced-postgres-migration-CRRTno6Z.d.mts.map +0 -1
- package/dist/statement-builders-BPnmt6wx.mjs.map +0 -1
package/dist/codec-types.d.mts
CHANGED
|
@@ -33,8 +33,12 @@ declare const codecs: _prisma_next_sql_relational_core_ast0.CodecDefBuilder<{
|
|
|
33
33
|
"bit varying": Codec<"pg/varbit@1", readonly ["equality", "order"], string, string, Record<string, unknown>, unknown>;
|
|
34
34
|
interval: Codec<"pg/interval@1", readonly ["equality", "order"], string | Record<string, unknown>, string, Record<string, unknown>, unknown>;
|
|
35
35
|
enum: Codec<"pg/enum@1", readonly ["equality", "order"], string, string, Record<string, unknown>, unknown>;
|
|
36
|
-
json: Codec<"pg/json@1", readonly [], string,
|
|
37
|
-
|
|
36
|
+
json: Codec<"pg/json@1", readonly [], string, string | number | boolean | {
|
|
37
|
+
readonly [key: string]: JsonValue;
|
|
38
|
+
} | readonly JsonValue[] | null, Record<string, unknown>, unknown>;
|
|
39
|
+
} & Record<"jsonb", Codec<"pg/jsonb@1", readonly ["equality"], string, string | number | boolean | {
|
|
40
|
+
readonly [key: string]: JsonValue;
|
|
41
|
+
} | readonly JsonValue[] | null, Record<string, unknown>, unknown>>>;
|
|
38
42
|
declare const codecDefinitions: {
|
|
39
43
|
readonly char: {
|
|
40
44
|
readonly typeId: "sql/char@1";
|
|
@@ -249,18 +253,34 @@ declare const codecDefinitions: {
|
|
|
249
253
|
readonly json: {
|
|
250
254
|
readonly typeId: "pg/json@1";
|
|
251
255
|
readonly scalar: "json";
|
|
252
|
-
readonly codec: Codec<"pg/json@1", readonly [], string,
|
|
253
|
-
|
|
254
|
-
readonly
|
|
255
|
-
readonly
|
|
256
|
+
readonly codec: Codec<"pg/json@1", readonly [], string, string | number | boolean | {
|
|
257
|
+
readonly [key: string]: JsonValue;
|
|
258
|
+
} | readonly JsonValue[] | null, Record<string, unknown>, unknown>;
|
|
259
|
+
readonly input: string | number | boolean | {
|
|
260
|
+
readonly [key: string]: JsonValue;
|
|
261
|
+
} | readonly JsonValue[] | null;
|
|
262
|
+
readonly output: string | number | boolean | {
|
|
263
|
+
readonly [key: string]: JsonValue;
|
|
264
|
+
} | readonly JsonValue[] | null;
|
|
265
|
+
readonly jsType: string | number | boolean | {
|
|
266
|
+
readonly [key: string]: JsonValue;
|
|
267
|
+
} | readonly JsonValue[] | null;
|
|
256
268
|
};
|
|
257
269
|
readonly jsonb: {
|
|
258
270
|
readonly typeId: "pg/jsonb@1";
|
|
259
271
|
readonly scalar: "jsonb";
|
|
260
|
-
readonly codec: Codec<"pg/jsonb@1", readonly ["equality"], string,
|
|
261
|
-
|
|
262
|
-
readonly
|
|
263
|
-
readonly
|
|
272
|
+
readonly codec: Codec<"pg/jsonb@1", readonly ["equality"], string, string | number | boolean | {
|
|
273
|
+
readonly [key: string]: JsonValue;
|
|
274
|
+
} | readonly JsonValue[] | null, Record<string, unknown>, unknown>;
|
|
275
|
+
readonly input: string | number | boolean | {
|
|
276
|
+
readonly [key: string]: JsonValue;
|
|
277
|
+
} | readonly JsonValue[] | null;
|
|
278
|
+
readonly output: string | number | boolean | {
|
|
279
|
+
readonly [key: string]: JsonValue;
|
|
280
|
+
} | readonly JsonValue[] | null;
|
|
281
|
+
readonly jsType: string | number | boolean | {
|
|
282
|
+
readonly [key: string]: JsonValue;
|
|
283
|
+
} | readonly JsonValue[] | null;
|
|
264
284
|
};
|
|
265
285
|
};
|
|
266
286
|
declare const dataTypes: {
|
|
@@ -296,4 +316,4 @@ declare const dataTypes: {
|
|
|
296
316
|
type CodecTypes = typeof codecs.CodecTypes;
|
|
297
317
|
//#endregion
|
|
298
318
|
export { codecDefinitions as n, dataTypes as r, CodecTypes as t };
|
|
299
|
-
//# sourceMappingURL=codecs-
|
|
319
|
+
//# sourceMappingURL=codecs-BQEm9_oo.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codecs-BQEm9_oo.d.mts","names":[],"sources":["../src/core/codecs.ts"],"sourcesContent":[],"mappings":";;;;;;cAkmBM,8CAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA+BC;;;;;;;;;;;;;;;;;;;;;;;IA/BD,SAAA,MAAA,EAAA,MAAA;EA+BC,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IACA,SAA4B,MAAA,EAAA,WAAA;IAE7B,SAAU,MAAA,EAAA,MAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAFnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAED,UAAA,UAAoB,MAAA,CAAO"}
|
package/dist/codecs.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as codecDefinitions, r as dataTypes, t as CodecTypes } from "./codecs-
|
|
1
|
+
import { n as codecDefinitions, r as dataTypes, t as CodecTypes } from "./codecs-BQEm9_oo.mjs";
|
|
2
2
|
export { type CodecTypes, codecDefinitions, dataTypes };
|
package/dist/control.mjs
CHANGED
|
@@ -3,13 +3,12 @@ import { t as parsePostgresDefault } from "./default-normalizer-DNOpRoOF.mjs";
|
|
|
3
3
|
import { t as normalizeSchemaNativeType } from "./native-type-normalizer-CInai_oY.mjs";
|
|
4
4
|
import { o as renderDefaultLiteral } from "./planner-ddl-builders-Dxvw1LHw.mjs";
|
|
5
5
|
import "./issue-planner-CFjB0_oO.mjs";
|
|
6
|
-
import { t as createPostgresMigrationPlanner } from "./planner-
|
|
7
|
-
import { a as ensurePrismaContractSchemaStatement, i as ensureMarkerTableStatement, n as
|
|
6
|
+
import { t as createPostgresMigrationPlanner } from "./planner-VgGu_r8x.mjs";
|
|
7
|
+
import { a as ensurePrismaContractSchemaStatement, i as ensureMarkerTableStatement, n as buildMergeMarkerStatements, r as ensureLedgerTableStatement, t as buildLedgerInsertStatement } from "./statement-builders-CHqCtSfe.mjs";
|
|
8
8
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
9
9
|
import { contractToSchemaIR, extractCodecControlHooks, runnerFailure, runnerSuccess } from "@prisma-next/family-sql/control";
|
|
10
10
|
import { verifySqlSchema } from "@prisma-next/family-sql/schema-verify";
|
|
11
11
|
import { ok, okVoid } from "@prisma-next/utils/result";
|
|
12
|
-
import { readMarker } from "@prisma-next/family-sql/verify";
|
|
13
12
|
import { SqlQueryError } from "@prisma-next/sql-errors";
|
|
14
13
|
|
|
15
14
|
//#region src/core/migrations/runner.ts
|
|
@@ -54,7 +53,7 @@ var PostgresMigrationRunner = class {
|
|
|
54
53
|
try {
|
|
55
54
|
await this.acquireLock(driver, lockKey);
|
|
56
55
|
await this.ensureControlTables(driver);
|
|
57
|
-
const existingMarker = await readMarker(driver);
|
|
56
|
+
const existingMarker = await this.family.readMarker({ driver });
|
|
58
57
|
const markerCheck = this.ensureMarkerCompatibility(existingMarker, options.plan);
|
|
59
58
|
if (!markerCheck.ok) return markerCheck;
|
|
60
59
|
const skipOperations = this.markerMatchesDestination(existingMarker, options.plan) && options.plan.origin != null;
|
|
@@ -308,12 +307,14 @@ var PostgresMigrationRunner = class {
|
|
|
308
307
|
return okVoid();
|
|
309
308
|
}
|
|
310
309
|
async upsertMarker(driver, options, existingMarker) {
|
|
311
|
-
const
|
|
310
|
+
const incomingInvariants = Array.from(new Set(options.invariants ?? [])).sort();
|
|
311
|
+
const writeStatements = buildMergeMarkerStatements({
|
|
312
312
|
storageHash: options.plan.destination.storageHash,
|
|
313
313
|
profileHash: options.plan.destination.profileHash ?? options.destinationContract.profileHash ?? options.plan.destination.storageHash,
|
|
314
314
|
contractJson: options.destinationContract,
|
|
315
315
|
canonicalVersion: null,
|
|
316
|
-
meta: {}
|
|
316
|
+
meta: {},
|
|
317
|
+
invariants: incomingInvariants
|
|
317
318
|
});
|
|
318
319
|
const statement = existingMarker ? writeStatements.update : writeStatements.insert;
|
|
319
320
|
await this.executeStatement(driver, statement);
|
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 { readMarker } from '@prisma-next/family-sql/verify';\nimport type { DataTransformOperation } from '@prisma-next/framework-components/control';\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 buildWriteMarkerStatements,\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\nfunction isDataTransformOperation(op: unknown): op is DataTransformOperation {\n return (\n typeof op === 'object' &&\n op !== null &&\n 'operationClass' in op &&\n (op as { operationClass: string }).operationClass === 'data' &&\n 'name' in op &&\n 'check' in op &&\n 'run' in op\n );\n}\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 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) skips if marker matches.\n const markerAtDestination = this.markerMatchesDestination(existingMarker, options.plan);\n const skipOperations = markerAtDestination && options.plan.origin != null;\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 // Record marker and ledger entries\n await this.upsertMarker(driver, options, existingMarker);\n await this.recordLedgerEntry(driver, options, existingMarker, applyValue.executedOperations);\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 // Data transform operations have a different execution lifecycle\n if (operation.operationClass === 'data' && isDataTransformOperation(operation)) {\n const dtResult = await this.executeDataTransform(driver, operation, {\n runIdempotency,\n });\n if (!dtResult.ok) {\n return dtResult;\n }\n executedOperations.push(operation);\n operationsExecuted += 1;\n continue;\n }\n\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 /**\n * Executes a data transform operation with the check → (skip or run) → check lifecycle.\n *\n * 1. If check is a query AST: render to SQL, execute. Empty result = already applied (skip).\n * 2. If check is `true`: always skip. If `false`: always run.\n * 3. Execute run ASTs (rendered to SQL) sequentially.\n * 4. Re-execute check as post-run validation. If violations remain, fail.\n */\n private async executeDataTransform(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n op: DataTransformOperation,\n options: { runIdempotency: boolean },\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n // Step 1: Check (skip guard)\n if (op.check === true) {\n // Always skip, regardless of idempotency setting\n return okVoid();\n }\n if (options.runIdempotency && op.check !== null && op.check !== false) {\n const checkResult = await driver.query(op.check.sql, op.check.params);\n if (checkResult.rows.length === 0) {\n // No violations — already applied, skip\n return okVoid();\n }\n }\n\n // Step 2: Execute run steps\n if (op.run) {\n for (const plan of op.run) {\n try {\n await driver.query(plan.sql, plan.params);\n } catch (error: unknown) {\n if (SqlQueryError.is(error)) {\n return runnerFailure(\n 'EXECUTION_FAILED',\n `Data transform \"${op.name}\" failed: ${error.message}`,\n {\n why: error.message,\n meta: {\n operationId: op.id,\n dataTransformName: op.name,\n sql: plan.sql,\n sqlState: error.sqlState,\n },\n },\n );\n }\n throw error;\n }\n }\n }\n\n // Step 3: Post-run validation (check again)\n if (op.check !== null && op.check !== false) {\n const checkResult = await driver.query(op.check.sql, op.check.params);\n if (checkResult.rows.length > 0) {\n return runnerFailure(\n 'POSTCHECK_FAILED',\n `Data transform \"${op.name}\" did not resolve all violations (${checkResult.rows.length} remaining)`,\n {\n why: `After executing the data transform, the check query still returns ${checkResult.rows.length} violation(s).`,\n meta: {\n operationId: op.id,\n dataTransformName: op.name,\n remainingViolations: checkResult.rows.length,\n },\n },\n );\n }\n }\n\n return okVoid();\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);\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);\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);\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 writeStatements = buildWriteMarkerStatements({\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 });\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 type { DataTransformOperation } from '@prisma-next/framework-components/control';\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\nfunction isDataTransformOperation(op: unknown): op is DataTransformOperation {\n return (\n typeof op === 'object' &&\n op !== null &&\n 'operationClass' in op &&\n (op as { operationClass: string }).operationClass === 'data' &&\n 'name' in op &&\n 'check' in op &&\n 'run' in op\n );\n}\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) skips if marker matches.\n const markerAtDestination = this.markerMatchesDestination(existingMarker, options.plan);\n const skipOperations = markerAtDestination && options.plan.origin != null;\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 // Record marker and ledger entries\n await this.upsertMarker(driver, options, existingMarker);\n await this.recordLedgerEntry(driver, options, existingMarker, applyValue.executedOperations);\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 // Data transform operations have a different execution lifecycle\n if (operation.operationClass === 'data' && isDataTransformOperation(operation)) {\n const dtResult = await this.executeDataTransform(driver, operation, {\n runIdempotency,\n });\n if (!dtResult.ok) {\n return dtResult;\n }\n executedOperations.push(operation);\n operationsExecuted += 1;\n continue;\n }\n\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 /**\n * Executes a data transform operation with the check → (skip or run) → check lifecycle.\n *\n * 1. If check is a query AST: render to SQL, execute. Empty result = already applied (skip).\n * 2. If check is `true`: always skip. If `false`: always run.\n * 3. Execute run ASTs (rendered to SQL) sequentially.\n * 4. Re-execute check as post-run validation. If violations remain, fail.\n */\n private async executeDataTransform(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n op: DataTransformOperation,\n options: { runIdempotency: boolean },\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n // Step 1: Check (skip guard)\n if (op.check === true) {\n // Always skip, regardless of idempotency setting\n return okVoid();\n }\n if (options.runIdempotency && op.check !== null && op.check !== false) {\n const checkResult = await driver.query(op.check.sql, op.check.params);\n if (checkResult.rows.length === 0) {\n // No violations — already applied, skip\n return okVoid();\n }\n }\n\n // Step 2: Execute run steps\n if (op.run) {\n for (const plan of op.run) {\n try {\n await driver.query(plan.sql, plan.params);\n } catch (error: unknown) {\n if (SqlQueryError.is(error)) {\n return runnerFailure(\n 'EXECUTION_FAILED',\n `Data transform \"${op.name}\" failed: ${error.message}`,\n {\n why: error.message,\n meta: {\n operationId: op.id,\n dataTransformName: op.name,\n sql: plan.sql,\n sqlState: error.sqlState,\n },\n },\n );\n }\n throw error;\n }\n }\n }\n\n // Step 3: Post-run validation (check again)\n if (op.check !== null && op.check !== false) {\n const checkResult = await driver.query(op.check.sql, op.check.params);\n if (checkResult.rows.length > 0) {\n return runnerFailure(\n 'POSTCHECK_FAILED',\n `Data transform \"${op.name}\" did not resolve all violations (${checkResult.rows.length} remaining)`,\n {\n why: `After executing the data transform, the check query still returns ${checkResult.rows.length} violation(s).`,\n meta: {\n operationId: op.id,\n dataTransformName: op.name,\n remainingViolations: checkResult.rows.length,\n },\n },\n );\n }\n }\n\n return okVoid();\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);\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);\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);\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 // Sort + dedupe so the INSERT path writes a stable initial value.\n // UPDATE merges server-side, so no client-side union is needed.\n const incomingInvariants = Array.from(new Set(options.invariants ?? [])).sort();\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,3 +1,4 @@
|
|
|
1
|
+
import { ifDefined } from "@prisma-next/utils/defined";
|
|
1
2
|
import { errorDataTransformContractMismatch } from "@prisma-next/errors/migration";
|
|
2
3
|
|
|
3
4
|
//#region src/core/migrations/operations/data-transform.ts
|
|
@@ -8,6 +9,7 @@ function dataTransform(contract, name, options, adapter) {
|
|
|
8
9
|
label: `Data transform: ${name}`,
|
|
9
10
|
operationClass: "data",
|
|
10
11
|
name,
|
|
12
|
+
...ifDefined("invariantId", options.invariantId),
|
|
11
13
|
source: "migration.ts",
|
|
12
14
|
check: options.check ? invokeAndLower(options.check, contract, adapter, name) : null,
|
|
13
15
|
run: runClosures.map((closure) => invokeAndLower(closure, contract, adapter, name))
|
|
@@ -36,4 +38,4 @@ function assertContractMatches(plan, contract, name) {
|
|
|
36
38
|
|
|
37
39
|
//#endregion
|
|
38
40
|
export { dataTransform as t };
|
|
39
|
-
//# sourceMappingURL=data-transform-
|
|
41
|
+
//# sourceMappingURL=data-transform-C83dy0vk.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-transform-C83dy0vk.mjs","names":["runClosures: readonly DataTransformClosure[]"],"sources":["../src/core/migrations/operations/data-transform.ts"],"sourcesContent":["/**\n * User-facing `dataTransform` factory for the Postgres migration authoring\n * surface. Invoked directly inside a `migration.ts` file via the\n * `PostgresMigration` instance method (`this.dataTransform(...)`), which\n * supplies the control adapter from the migration's injected stack:\n *\n * ```ts\n * import endContract from './end-contract.json' with { type: 'json' };\n *\n * class M extends Migration {\n * override get operations() {\n * return [\n * this.dataTransform(endContract, 'backfill emails', {\n * check: () => db.users.count().where(({ email }) => email.isNull()),\n * run: () => db.users.update({ email: '' }).where(({ email }) => email.isNull()),\n * }),\n * ];\n * }\n * }\n * ```\n *\n * The factory accepts lazy closures (`() => SqlQueryPlan | Buildable`),\n * invokes each one, asserts that its `meta.storageHash` matches the\n * `contract` it was handed (→ `PN-MIG-2005` on mismatch), and lowers the\n * plan via the supplied control adapter to a serialized `{sql, params}`\n * payload for `ops.json`. The free factory remains usable standalone\n * (tests, ad-hoc tooling, non-class contexts) by passing the adapter\n * explicitly as the fourth argument.\n */\n\nimport type { Contract } from '@prisma-next/contract/types';\nimport { errorDataTransformContractMismatch } from '@prisma-next/errors/migration';\nimport type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';\nimport type {\n DataTransformOperation,\n SerializedQueryPlan,\n} from '@prisma-next/framework-components/control';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';\nimport { ifDefined } from '@prisma-next/utils/defined';\n\ninterface Buildable<R = unknown> {\n build(): SqlQueryPlan<R>;\n}\n\n/**\n * A single-closure producer of a SQL query plan. Shared between\n * `check` and each `run` entry.\n */\nexport type DataTransformClosure = () => SqlQueryPlan | Buildable;\n\nexport interface DataTransformOptions {\n /**\n * Optional opt-in routing identity. Presence opts the transform into\n * invariant-aware routing; absence means it is path-dependent and\n * not referenceable from refs.\n */\n readonly invariantId?: string;\n /** Optional pre-flight query. `undefined` means \"no check\". */\n readonly check?: DataTransformClosure;\n /** One or more mutation queries to execute. */\n readonly run: DataTransformClosure | readonly DataTransformClosure[];\n}\n\n/**\n * Concrete Postgres flavor of `DataTransformOperation`, re-exported so the\n * `PostgresMigration.dataTransform` instance method can name it without\n * leaking the framework-components symbol into call sites.\n */\nexport type PostgresDataTransformOperation = DataTransformOperation;\n\nexport function dataTransform<TContract extends Contract<SqlStorage>>(\n contract: TContract,\n name: string,\n options: DataTransformOptions,\n adapter: SqlControlAdapter<'postgres'>,\n): DataTransformOperation {\n const runClosures: readonly DataTransformClosure[] = Array.isArray(options.run)\n ? options.run\n : [options.run as DataTransformClosure];\n return {\n id: `data_migration.${name}`,\n label: `Data transform: ${name}`,\n operationClass: 'data',\n name,\n ...ifDefined('invariantId', options.invariantId),\n source: 'migration.ts',\n check: options.check ? invokeAndLower(options.check, contract, adapter, name) : null,\n run: runClosures.map((closure) => invokeAndLower(closure, contract, adapter, name)),\n };\n}\n\nfunction invokeAndLower(\n closure: DataTransformClosure,\n contract: Contract<SqlStorage>,\n adapter: SqlControlAdapter<'postgres'>,\n name: string,\n): SerializedQueryPlan {\n const result = closure();\n const plan = isBuildable(result) ? result.build() : result;\n assertContractMatches(plan, contract, name);\n const lowered = adapter.lower(plan.ast, { contract });\n return { sql: lowered.sql, params: lowered.params };\n}\n\nfunction isBuildable(value: unknown): value is Buildable {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'build' in value &&\n typeof (value as { build: unknown }).build === 'function'\n );\n}\n\nfunction assertContractMatches(\n plan: SqlQueryPlan,\n contract: Contract<SqlStorage>,\n name: string,\n): void {\n if (plan.meta.storageHash !== contract.storage.storageHash) {\n throw errorDataTransformContractMismatch({\n dataTransformName: name,\n expected: contract.storage.storageHash,\n actual: plan.meta.storageHash,\n });\n }\n}\n"],"mappings":";;;;AAuEA,SAAgB,cACd,UACA,MACA,SACA,SACwB;CACxB,MAAMA,cAA+C,MAAM,QAAQ,QAAQ,IAAI,GAC3E,QAAQ,MACR,CAAC,QAAQ,IAA4B;AACzC,QAAO;EACL,IAAI,kBAAkB;EACtB,OAAO,mBAAmB;EAC1B,gBAAgB;EAChB;EACA,GAAG,UAAU,eAAe,QAAQ,YAAY;EAChD,QAAQ;EACR,OAAO,QAAQ,QAAQ,eAAe,QAAQ,OAAO,UAAU,SAAS,KAAK,GAAG;EAChF,KAAK,YAAY,KAAK,YAAY,eAAe,SAAS,UAAU,SAAS,KAAK,CAAC;EACpF;;AAGH,SAAS,eACP,SACA,UACA,SACA,MACqB;CACrB,MAAM,SAAS,SAAS;CACxB,MAAM,OAAO,YAAY,OAAO,GAAG,OAAO,OAAO,GAAG;AACpD,uBAAsB,MAAM,UAAU,KAAK;CAC3C,MAAM,UAAU,QAAQ,MAAM,KAAK,KAAK,EAAE,UAAU,CAAC;AACrD,QAAO;EAAE,KAAK,QAAQ;EAAK,QAAQ,QAAQ;EAAQ;;AAGrD,SAAS,YAAY,OAAoC;AACvD,QACE,OAAO,UAAU,YACjB,UAAU,QACV,WAAW,SACX,OAAQ,MAA6B,UAAU;;AAInD,SAAS,sBACP,MACA,UACA,MACM;AACN,KAAI,KAAK,KAAK,gBAAgB,SAAS,QAAQ,YAC7C,OAAM,mCAAmC;EACvC,mBAAmB;EACnB,UAAU,SAAS,QAAQ;EAC3B,QAAQ,KAAK,KAAK;EACnB,CAAC"}
|
|
@@ -15,6 +15,12 @@ interface Buildable<R = unknown> {
|
|
|
15
15
|
*/
|
|
16
16
|
type DataTransformClosure = () => SqlQueryPlan | Buildable;
|
|
17
17
|
interface DataTransformOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Optional opt-in routing identity. Presence opts the transform into
|
|
20
|
+
* invariant-aware routing; absence means it is path-dependent and
|
|
21
|
+
* not referenceable from refs.
|
|
22
|
+
*/
|
|
23
|
+
readonly invariantId?: string;
|
|
18
24
|
/** Optional pre-flight query. `undefined` means "no check". */
|
|
19
25
|
readonly check?: DataTransformClosure;
|
|
20
26
|
/** One or more mutation queries to execute. */
|
|
@@ -29,4 +35,4 @@ type PostgresDataTransformOperation = DataTransformOperation;
|
|
|
29
35
|
declare function dataTransform<TContract extends Contract<SqlStorage>>(contract: TContract, name: string, options: DataTransformOptions, adapter: SqlControlAdapter<'postgres'>): DataTransformOperation;
|
|
30
36
|
//#endregion
|
|
31
37
|
export { dataTransform as i, DataTransformOptions as n, PostgresDataTransformOperation as r, DataTransformClosure as t };
|
|
32
|
-
//# sourceMappingURL=data-transform-
|
|
38
|
+
//# sourceMappingURL=data-transform-D8x5m1YV.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-transform-D8x5m1YV.d.mts","names":[],"sources":["../src/core/migrations/operations/data-transform.ts"],"sourcesContent":[],"mappings":";;;;;;;;UAyCU;WACC,aAAa;;;;;;KAOZ,oBAAA,SAA6B,eAAe;UAEvC,oBAAA;;;;;;;;mBAQE;;gBAEH,gCAAgC;;;;;;;KAQpC,8BAAA,GAAiC;iBAE7B,gCAAgC,SAAS,uBAC7C,kCAED,+BACA,gCACR"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as dataTransform } from "./data-transform-
|
|
1
|
+
import { i as dataTransform } from "./data-transform-D8x5m1YV.mjs";
|
|
2
2
|
export { dataTransform };
|
package/dist/data-transform.mjs
CHANGED
package/dist/migration.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { i as dataTransform, n as DataTransformOptions, t as DataTransformClosure } from "./data-transform-
|
|
1
|
+
import { i as dataTransform, n as DataTransformOptions, t as DataTransformClosure } from "./data-transform-D8x5m1YV.mjs";
|
|
2
2
|
import { n as ForeignKeySpec, r as Op, t as ColumnSpec } from "./shared-Bxkt8pNO.mjs";
|
|
3
|
-
import { t as PostgresMigration } from "./postgres-migration-
|
|
3
|
+
import { t as PostgresMigration } from "./postgres-migration-DcfWGqhe.mjs";
|
|
4
4
|
import { placeholder } from "@prisma-next/errors/migration";
|
|
5
5
|
import { MigrationCLI } from "@prisma-next/cli/migration-cli";
|
|
6
6
|
|
package/dist/migration.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _ as dropColumn, a as addEnumValues, b as setDefault, c as renameType, d as addForeignKey, f as addPrimaryKey, g as alterColumnType, h as addColumn, i as dropIndex, l as createExtension, m as dropConstraint, n as dropTable, o as createEnumType, p as addUnique, r as createIndex, s as dropEnumType, t as createTable, u as createSchema, v as dropDefault, x as setNotNull, y as dropNotNull } from "./tables-BmdW_FWO.mjs";
|
|
2
|
-
import { t as dataTransform } from "./data-transform-
|
|
3
|
-
import { t as PostgresMigration } from "./postgres-migration-
|
|
2
|
+
import { t as dataTransform } from "./data-transform-C83dy0vk.mjs";
|
|
3
|
+
import { t as PostgresMigration } from "./postgres-migration-EGSlO4jO.mjs";
|
|
4
4
|
import { placeholder } from "@prisma-next/errors/migration";
|
|
5
5
|
import { MigrationCLI } from "@prisma-next/cli/migration-cli";
|
|
6
6
|
|
package/dist/pack.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as parsePostgresDefault } from "./default-normalizer-DNOpRoOF.mjs";
|
|
2
2
|
import { t as normalizeSchemaNativeType } from "./native-type-normalizer-CInai_oY.mjs";
|
|
3
3
|
import { n as postgresPlannerStrategies, t as planIssues } from "./issue-planner-CFjB0_oO.mjs";
|
|
4
|
-
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-
|
|
4
|
+
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-CVD4g__D.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
|
|
|
@@ -95,4 +95,4 @@ var PostgresMigrationPlanner = class {
|
|
|
95
95
|
|
|
96
96
|
//#endregion
|
|
97
97
|
export { createPostgresMigrationPlanner as t };
|
|
98
|
-
//# sourceMappingURL=planner-
|
|
98
|
+
//# sourceMappingURL=planner-VgGu_r8x.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-
|
|
1
|
+
{"version":3,"file":"planner-VgGu_r8x.mjs","names":["DEFAULT_PLANNER_CONFIG: PlannerConfig","config: PlannerConfig"],"sources":["../src/core/migrations/planner.ts"],"sourcesContent":["import 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 readonly fromHash?: string;\n /**\n * The \"from\" contract (state the planner assumes the database starts\n * at). Only `migration plan` supplies this; `db update` / `db init`\n * reconcile against the live schema with no old contract. When present\n * alongside the `'data'` operation class, strategies that need from/to\n * column shape comparisons (unsafe type change, nullability tightening)\n * activate.\n */\n readonly fromContract?: unknown;\n readonly schemaName?: string;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;\n }): PostgresPlanResult {\n return this.planSql(options as SqlMigrationPlannerPlanOptions, options.fromHash ?? '');\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, fromHash: string): 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 ?? null,\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: fromHash,\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":";;;;;;;;AAsCA,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,SAgBkB;AACrB,SAAO,KAAK,QAAQ,SAA2C,QAAQ,YAAY,GAAG;;CAGxF,eAAe,SAAsE;AACnF,SAAO,IAAI,sCAAsC,EAAE,EAAE;GACnD,MAAM,QAAQ;GACd,IAAI,QAAQ;GACb,CAAC;;CAGJ,AAAQ,QAAQ,SAAyC,UAAsC;EAC7F,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,gBAAgB;GACtC;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;IACN,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,4 +1,4 @@
|
|
|
1
|
-
import { t as PostgresMigration } from "./postgres-migration-
|
|
1
|
+
import { t as PostgresMigration } from "./postgres-migration-EGSlO4jO.mjs";
|
|
2
2
|
import { t as renderOps } from "./render-ops-D6_DHdOK.mjs";
|
|
3
3
|
import { t as renderCallsToTypeScript } from "./render-typescript-1rF_SB4g.mjs";
|
|
4
4
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
@@ -30,4 +30,4 @@ var TypeScriptRenderablePostgresMigration = class extends PostgresMigration {
|
|
|
30
30
|
|
|
31
31
|
//#endregion
|
|
32
32
|
export { TypeScriptRenderablePostgresMigration as t };
|
|
33
|
-
//# sourceMappingURL=planner-produced-postgres-migration-
|
|
33
|
+
//# sourceMappingURL=planner-produced-postgres-migration-CVD4g__D.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-produced-postgres-migration-
|
|
1
|
+
{"version":3,"file":"planner-produced-postgres-migration-CVD4g__D.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('kind', this.#meta.kind),\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,QAAQ,MAAKA,KAAM,KAAK;GACrC,GAAG,UAAU,UAAU,MAAKA,KAAM,OAAO;GAC1C,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as PostgresPlanTargetDetails } from "./planner-target-details-DH-azLu-.mjs";
|
|
2
2
|
import { b as PostgresOpFactoryCall } from "./op-factory-call-C3bWXKSP.mjs";
|
|
3
|
-
import { t as PostgresMigration } from "./postgres-migration-
|
|
3
|
+
import { t as PostgresMigration } from "./postgres-migration-DcfWGqhe.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";
|
|
@@ -17,4 +17,4 @@ declare class TypeScriptRenderablePostgresMigration extends PostgresMigration im
|
|
|
17
17
|
}
|
|
18
18
|
//#endregion
|
|
19
19
|
export { TypeScriptRenderablePostgresMigration as t };
|
|
20
|
-
//# sourceMappingURL=planner-produced-postgres-migration-
|
|
20
|
+
//# sourceMappingURL=planner-produced-postgres-migration-Dw_mPMKt.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner-produced-postgres-migration-Dw_mPMKt.d.mts","names":[],"sources":["../src/core/migrations/planner-produced-postgres-migration.ts"],"sourcesContent":[],"mappings":";;;;;;;;;KAmCK,EAAA,GAAK,0BAA0B;cAEvB,qCAAA,SACH,iBAAA,YACG;;8BAKiB,+BAA+B;6BAMvB;cAIf"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "./shared-Bxkt8pNO.mjs";
|
|
2
2
|
import "./op-factory-call-C3bWXKSP.mjs";
|
|
3
|
-
import "./postgres-migration-
|
|
4
|
-
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-
|
|
3
|
+
import "./postgres-migration-DcfWGqhe.mjs";
|
|
4
|
+
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-Dw_mPMKt.mjs";
|
|
5
5
|
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-CVD4g__D.mjs";
|
|
2
2
|
|
|
3
3
|
export { TypeScriptRenderablePostgresMigration };
|
package/dist/planner.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "./shared-Bxkt8pNO.mjs";
|
|
2
2
|
import "./op-factory-call-C3bWXKSP.mjs";
|
|
3
|
-
import "./postgres-migration-
|
|
4
|
-
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-
|
|
3
|
+
import "./postgres-migration-DcfWGqhe.mjs";
|
|
4
|
+
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-Dw_mPMKt.mjs";
|
|
5
5
|
import { MigrationOperationPolicy, SqlPlannerFailureResult } from "@prisma-next/family-sql/control";
|
|
6
6
|
import { MigrationPlanWithAuthoringSurface, MigrationPlanner, MigrationScaffoldContext } from "@prisma-next/framework-components/control";
|
|
7
7
|
import { TargetBoundComponentDescriptor } from "@prisma-next/framework-components/components";
|
package/dist/planner.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as PostgresPlanTargetDetails } from "./planner-target-details-DH-azLu-.mjs";
|
|
2
|
-
import { n as DataTransformOptions, r as PostgresDataTransformOperation } from "./data-transform-
|
|
2
|
+
import { n as DataTransformOptions, r as PostgresDataTransformOperation } from "./data-transform-D8x5m1YV.mjs";
|
|
3
3
|
import { Migration } from "@prisma-next/family-sql/migration";
|
|
4
4
|
import { Contract } from "@prisma-next/contract/types";
|
|
5
5
|
import { SqlStorage } from "@prisma-next/sql-contract/types";
|
|
@@ -47,4 +47,4 @@ declare abstract class PostgresMigration extends Migration<PostgresPlanTargetDet
|
|
|
47
47
|
}
|
|
48
48
|
//#endregion
|
|
49
49
|
export { PostgresMigration as t };
|
|
50
|
-
//# sourceMappingURL=postgres-migration-
|
|
50
|
+
//# sourceMappingURL=postgres-migration-DcfWGqhe.d.mts.map
|
package/dist/{postgres-migration-BjA3Zmts.d.mts.map → postgres-migration-DcfWGqhe.d.mts.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-migration-
|
|
1
|
+
{"version":3,"file":"postgres-migration-DcfWGqhe.d.mts","names":[],"sources":["../src/core/migrations/postgres-migration.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;AAiCA;;;;;;;;;;;;;;;;;;uBAAsB,iBAAA,SAA0B,UAC9C;;;;;;;;qCAWmC;sBAEf;;;;;;4CAesB,SAAS,uBACvC,kCAED,uBACR"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as errorPostgresMigrationStackMissing } from "./errors-AFvEPZ1R.mjs";
|
|
2
|
-
import { t as dataTransform } from "./data-transform-
|
|
2
|
+
import { t as dataTransform } from "./data-transform-C83dy0vk.mjs";
|
|
3
3
|
import { Migration } from "@prisma-next/family-sql/migration";
|
|
4
4
|
|
|
5
5
|
//#region src/core/migrations/postgres-migration.ts
|
|
@@ -49,4 +49,4 @@ var PostgresMigration = class extends Migration {
|
|
|
49
49
|
|
|
50
50
|
//#endregion
|
|
51
51
|
export { PostgresMigration as t };
|
|
52
|
-
//# sourceMappingURL=postgres-migration-
|
|
52
|
+
//# sourceMappingURL=postgres-migration-EGSlO4jO.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-migration-
|
|
1
|
+
{"version":3,"file":"postgres-migration-EGSlO4jO.mjs","names":["SqlMigration"],"sources":["../src/core/migrations/postgres-migration.ts"],"sourcesContent":["import type { Contract } from '@prisma-next/contract/types';\nimport type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';\nimport { Migration as SqlMigration } from '@prisma-next/family-sql/migration';\nimport type { ControlStack } from '@prisma-next/framework-components/control';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport { errorPostgresMigrationStackMissing } from '../errors';\nimport {\n type DataTransformOptions,\n dataTransform,\n type PostgresDataTransformOperation,\n} from './operations/data-transform';\nimport type { PostgresPlanTargetDetails } from './planner-target-details';\n\n/**\n * Target-owned base class for Postgres migrations.\n *\n * Fixes the `SqlMigration` generic to `PostgresPlanTargetDetails` and the\n * abstract `targetId` to the Postgres target-id string literal, so both\n * user-authored migrations and renderer-generated scaffolds (the output of\n * `renderCallsToTypeScript`) can extend `PostgresMigration` directly without\n * redeclaring target-local identity.\n *\n * Mirrors `MongoMigration` in `@prisma-next/family-mongo`: the renderer\n * emits `extends Migration` against a target-specific re-export of this\n * class from `@prisma-next/target-postgres/migration`, keeping the\n * authoring surface target-scoped rather than family-scoped.\n *\n * The constructor materializes a single Postgres `SqlControlAdapter` from\n * `stack.adapter.create(stack)` and stores it; the protected `dataTransform`\n * instance method forwards to the free `dataTransform` factory with that\n * stored adapter, so user migrations can write `this.dataTransform(...)`\n * without threading the adapter through every call.\n */\nexport abstract class PostgresMigration extends SqlMigration<\n PostgresPlanTargetDetails,\n 'postgres'\n> {\n readonly targetId = 'postgres' as const;\n\n /**\n * Materialized Postgres control adapter, created once per migration\n * instance from the injected stack. `undefined` only when the migration\n * was instantiated without a stack (test fixtures); `dataTransform`\n * throws in that case to surface the misuse.\n */\n protected readonly controlAdapter: SqlControlAdapter<'postgres'> | undefined;\n\n constructor(stack?: ControlStack<'sql', 'postgres'>) {\n super(stack);\n // The descriptor `create()` is typed as the wider `ControlAdapterInstance`;\n // the Postgres descriptor concretely returns a `SqlControlAdapter<'postgres'>`,\n // so the cast holds for any Postgres-target stack assembled at runtime.\n this.controlAdapter = stack?.adapter\n ? (stack.adapter.create(stack) as SqlControlAdapter<'postgres'>)\n : undefined;\n }\n\n /**\n * Instance-method wrapper around the free `dataTransform` factory that\n * supplies the stored control adapter. Authors call this from inside\n * `get operations()`; the adapter argument is hidden from the call site.\n */\n protected dataTransform<TContract extends Contract<SqlStorage>>(\n contract: TContract,\n name: string,\n options: DataTransformOptions,\n ): PostgresDataTransformOperation {\n if (!this.controlAdapter) {\n throw errorPostgresMigrationStackMissing();\n }\n return dataTransform(contract, name, options, this.controlAdapter);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,IAAsB,oBAAtB,cAAgDA,UAG9C;CACA,AAAS,WAAW;;;;;;;CAQpB,AAAmB;CAEnB,YAAY,OAAyC;AACnD,QAAM,MAAM;AAIZ,OAAK,iBAAiB,OAAO,UACxB,MAAM,QAAQ,OAAO,MAAM,GAC5B;;;;;;;CAQN,AAAU,cACR,UACA,MACA,SACgC;AAChC,MAAI,CAAC,KAAK,eACR,OAAM,oCAAoC;AAE5C,SAAO,cAAc,UAAU,MAAM,SAAS,KAAK,eAAe"}
|
|
@@ -12,7 +12,8 @@ const ensureMarkerTableStatement = {
|
|
|
12
12
|
canonical_version int,
|
|
13
13
|
updated_at timestamptz not null default now(),
|
|
14
14
|
app_tag text,
|
|
15
|
-
meta jsonb not null default '{}'
|
|
15
|
+
meta jsonb not null default '{}',
|
|
16
|
+
invariants text[] not null default '{}'
|
|
16
17
|
)`,
|
|
17
18
|
params: []
|
|
18
19
|
};
|
|
@@ -30,7 +31,7 @@ const ensureLedgerTableStatement = {
|
|
|
30
31
|
)`,
|
|
31
32
|
params: []
|
|
32
33
|
};
|
|
33
|
-
function
|
|
34
|
+
function buildMergeMarkerStatements(input) {
|
|
34
35
|
const params = [
|
|
35
36
|
1,
|
|
36
37
|
input.storageHash,
|
|
@@ -38,7 +39,8 @@ function buildWriteMarkerStatements(input) {
|
|
|
38
39
|
jsonParam(input.contractJson),
|
|
39
40
|
input.canonicalVersion ?? null,
|
|
40
41
|
input.appTag ?? null,
|
|
41
|
-
jsonParam(input.meta ?? {})
|
|
42
|
+
jsonParam(input.meta ?? {}),
|
|
43
|
+
input.invariants
|
|
42
44
|
];
|
|
43
45
|
return {
|
|
44
46
|
insert: {
|
|
@@ -50,7 +52,8 @@ function buildWriteMarkerStatements(input) {
|
|
|
50
52
|
canonical_version,
|
|
51
53
|
updated_at,
|
|
52
54
|
app_tag,
|
|
53
|
-
meta
|
|
55
|
+
meta,
|
|
56
|
+
invariants
|
|
54
57
|
) values (
|
|
55
58
|
$1,
|
|
56
59
|
$2,
|
|
@@ -59,7 +62,8 @@ function buildWriteMarkerStatements(input) {
|
|
|
59
62
|
$5,
|
|
60
63
|
now(),
|
|
61
64
|
$6,
|
|
62
|
-
$7::jsonb
|
|
65
|
+
$7::jsonb,
|
|
66
|
+
$8::text[]
|
|
63
67
|
)`,
|
|
64
68
|
params
|
|
65
69
|
},
|
|
@@ -71,7 +75,8 @@ function buildWriteMarkerStatements(input) {
|
|
|
71
75
|
canonical_version = $5,
|
|
72
76
|
updated_at = now(),
|
|
73
77
|
app_tag = $6,
|
|
74
|
-
meta = $7::jsonb
|
|
78
|
+
meta = $7::jsonb,
|
|
79
|
+
invariants = array(select distinct unnest(invariants || $8::text[]) order by 1)
|
|
75
80
|
where id = $1`,
|
|
76
81
|
params
|
|
77
82
|
}
|
|
@@ -112,5 +117,5 @@ function jsonParam(value) {
|
|
|
112
117
|
}
|
|
113
118
|
|
|
114
119
|
//#endregion
|
|
115
|
-
export { ensurePrismaContractSchemaStatement as a, ensureMarkerTableStatement as i,
|
|
116
|
-
//# sourceMappingURL=statement-builders-
|
|
120
|
+
export { ensurePrismaContractSchemaStatement as a, ensureMarkerTableStatement as i, buildMergeMarkerStatements as n, ensureLedgerTableStatement as r, buildLedgerInsertStatement as t };
|
|
121
|
+
//# sourceMappingURL=statement-builders-CHqCtSfe.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statement-builders-CHqCtSfe.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"}
|
|
@@ -6,18 +6,25 @@ interface SqlStatement {
|
|
|
6
6
|
declare const ensurePrismaContractSchemaStatement: SqlStatement;
|
|
7
7
|
declare const ensureMarkerTableStatement: SqlStatement;
|
|
8
8
|
declare const ensureLedgerTableStatement: SqlStatement;
|
|
9
|
-
interface
|
|
9
|
+
interface MergeMarkerInput {
|
|
10
10
|
readonly storageHash: string;
|
|
11
11
|
readonly profileHash: string;
|
|
12
12
|
readonly contractJson?: unknown;
|
|
13
13
|
readonly canonicalVersion?: number | null;
|
|
14
14
|
readonly appTag?: string | null;
|
|
15
15
|
readonly meta?: Record<string, unknown>;
|
|
16
|
+
/**
|
|
17
|
+
* Invariants to merge into `marker.invariants`. INSERT writes them as
|
|
18
|
+
* the initial value (callers are expected to pass a sorted, deduped
|
|
19
|
+
* array). UPDATE merges them with the existing column server-side via
|
|
20
|
+
* a single atomic SQL expression.
|
|
21
|
+
*/
|
|
22
|
+
readonly invariants: readonly string[];
|
|
16
23
|
}
|
|
17
|
-
declare function
|
|
24
|
+
declare function buildMergeMarkerStatements(input: MergeMarkerInput): {
|
|
18
25
|
readonly insert: SqlStatement;
|
|
19
26
|
readonly update: SqlStatement;
|
|
20
27
|
};
|
|
21
28
|
//#endregion
|
|
22
|
-
export { type SqlStatement,
|
|
29
|
+
export { type SqlStatement, buildMergeMarkerStatements, ensureLedgerTableStatement, ensureMarkerTableStatement, ensurePrismaContractSchemaStatement };
|
|
23
30
|
//# 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":";UAAiB,YAAA;EAAA,SAAA,GAAA,EAAA,MAAY;EAKhB,SAAA,MAAA,EAAA,SAAA,OAAA,EAAA;AAKb;
|
|
1
|
+
{"version":3,"file":"statement-builders.d.mts","names":[],"sources":["../src/core/migrations/statement-builders.ts"],"sourcesContent":[],"mappings":";UAAiB,YAAA;EAAA,SAAA,GAAA,EAAA,MAAY;EAKhB,SAAA,MAAA,EAAA,SAAA,OAAA,EAAA;AAKb;AAea,cApBA,mCAoB4B,EApBS,YAiCjD;AAEgB,cA9BJ,0BAoCW,EApCiB,YAoCjB;AAUR,cA/BH,0BA+B6B,EA/BD,YA+BC;AAAQ,UAhBjC,gBAAA,CAgBiC;EAC/B,SAAA,WAAA,EAAA,MAAA;EACA,SAAA,WAAA,EAAA,MAAA;EAAY,SAAA,YAAA,CAAA,EAAA,OAAA;;;kBAZb;;;;;;;;;iBAUF,0BAAA,QAAkC;mBAC/B;mBACA"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as ensurePrismaContractSchemaStatement, i as ensureMarkerTableStatement, n as
|
|
1
|
+
import { a as ensurePrismaContractSchemaStatement, i as ensureMarkerTableStatement, n as buildMergeMarkerStatements, r as ensureLedgerTableStatement } from "./statement-builders-CHqCtSfe.mjs";
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { buildMergeMarkerStatements, ensureLedgerTableStatement, ensureMarkerTableStatement, ensurePrismaContractSchemaStatement };
|
package/package.json
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/target-postgres",
|
|
3
|
-
"version": "0.5.0-dev.
|
|
3
|
+
"version": "0.5.0-dev.20",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "Postgres target pack for Prisma Next",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"arktype": "^2.0.0",
|
|
9
9
|
"pathe": "^2.0.3",
|
|
10
|
-
"@prisma-next/cli": "0.5.0-dev.
|
|
11
|
-
"@prisma-next/contract": "0.5.0-dev.
|
|
12
|
-
"@prisma-next/
|
|
13
|
-
"@prisma-next/
|
|
14
|
-
"@prisma-next/migration-tools": "0.5.0-dev.
|
|
15
|
-
"@prisma-next/ts-render": "0.5.0-dev.
|
|
16
|
-
"@prisma-next/
|
|
17
|
-
"@prisma-next/sql-
|
|
18
|
-
"@prisma-next/
|
|
19
|
-
"@prisma-next/sql-
|
|
20
|
-
"@prisma-next/
|
|
21
|
-
"@prisma-next/sql-
|
|
22
|
-
"@prisma-next/
|
|
10
|
+
"@prisma-next/cli": "0.5.0-dev.20",
|
|
11
|
+
"@prisma-next/contract": "0.5.0-dev.20",
|
|
12
|
+
"@prisma-next/framework-components": "0.5.0-dev.20",
|
|
13
|
+
"@prisma-next/family-sql": "0.5.0-dev.20",
|
|
14
|
+
"@prisma-next/migration-tools": "0.5.0-dev.20",
|
|
15
|
+
"@prisma-next/ts-render": "0.5.0-dev.20",
|
|
16
|
+
"@prisma-next/errors": "0.5.0-dev.20",
|
|
17
|
+
"@prisma-next/sql-errors": "0.5.0-dev.20",
|
|
18
|
+
"@prisma-next/sql-contract": "0.5.0-dev.20",
|
|
19
|
+
"@prisma-next/sql-operations": "0.5.0-dev.20",
|
|
20
|
+
"@prisma-next/utils": "0.5.0-dev.20",
|
|
21
|
+
"@prisma-next/sql-relational-core": "0.5.0-dev.20",
|
|
22
|
+
"@prisma-next/sql-schema-ir": "0.5.0-dev.20"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"tsdown": "0.18.4",
|
|
26
26
|
"typescript": "5.9.3",
|
|
27
27
|
"vitest": "4.0.17",
|
|
28
|
-
"@prisma-next/tsconfig": "0.0.0",
|
|
29
28
|
"@prisma-next/test-utils": "0.0.1",
|
|
29
|
+
"@prisma-next/tsconfig": "0.0.0",
|
|
30
30
|
"@prisma-next/tsdown": "0.0.0"
|
|
31
31
|
},
|
|
32
32
|
"files": [
|
|
@@ -67,6 +67,9 @@
|
|
|
67
67
|
"url": "https://github.com/prisma/prisma-next.git",
|
|
68
68
|
"directory": "packages/3-targets/3-targets/postgres"
|
|
69
69
|
},
|
|
70
|
+
"prismaNext": {
|
|
71
|
+
"minServerVersion": "14"
|
|
72
|
+
},
|
|
70
73
|
"scripts": {
|
|
71
74
|
"build": "tsdown",
|
|
72
75
|
"test": "vitest run --passWithNoTests",
|
|
@@ -37,6 +37,7 @@ import type {
|
|
|
37
37
|
} from '@prisma-next/framework-components/control';
|
|
38
38
|
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
39
39
|
import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
|
|
40
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
40
41
|
|
|
41
42
|
interface Buildable<R = unknown> {
|
|
42
43
|
build(): SqlQueryPlan<R>;
|
|
@@ -49,6 +50,12 @@ interface Buildable<R = unknown> {
|
|
|
49
50
|
export type DataTransformClosure = () => SqlQueryPlan | Buildable;
|
|
50
51
|
|
|
51
52
|
export interface DataTransformOptions {
|
|
53
|
+
/**
|
|
54
|
+
* Optional opt-in routing identity. Presence opts the transform into
|
|
55
|
+
* invariant-aware routing; absence means it is path-dependent and
|
|
56
|
+
* not referenceable from refs.
|
|
57
|
+
*/
|
|
58
|
+
readonly invariantId?: string;
|
|
52
59
|
/** Optional pre-flight query. `undefined` means "no check". */
|
|
53
60
|
readonly check?: DataTransformClosure;
|
|
54
61
|
/** One or more mutation queries to execute. */
|
|
@@ -76,6 +83,7 @@ export function dataTransform<TContract extends Contract<SqlStorage>>(
|
|
|
76
83
|
label: `Data transform: ${name}`,
|
|
77
84
|
operationClass: 'data',
|
|
78
85
|
name,
|
|
86
|
+
...ifDefined('invariantId', options.invariantId),
|
|
79
87
|
source: 'migration.ts',
|
|
80
88
|
check: options.check ? invokeAndLower(options.check, contract, adapter, name) : null,
|
|
81
89
|
run: runClosures.map((closure) => invokeAndLower(closure, contract, adapter, name)),
|
|
@@ -12,7 +12,6 @@ import type {
|
|
|
12
12
|
} from '@prisma-next/family-sql/control';
|
|
13
13
|
import { runnerFailure, runnerSuccess } from '@prisma-next/family-sql/control';
|
|
14
14
|
import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
|
|
15
|
-
import { readMarker } from '@prisma-next/family-sql/verify';
|
|
16
15
|
import type { DataTransformOperation } from '@prisma-next/framework-components/control';
|
|
17
16
|
import { SqlQueryError } from '@prisma-next/sql-errors';
|
|
18
17
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
@@ -23,7 +22,7 @@ import { normalizeSchemaNativeType } from '../native-type-normalizer';
|
|
|
23
22
|
import type { PostgresPlanTargetDetails } from './planner-target-details';
|
|
24
23
|
import {
|
|
25
24
|
buildLedgerInsertStatement,
|
|
26
|
-
|
|
25
|
+
buildMergeMarkerStatements,
|
|
27
26
|
ensureLedgerTableStatement,
|
|
28
27
|
ensureMarkerTableStatement,
|
|
29
28
|
ensurePrismaContractSchemaStatement,
|
|
@@ -120,7 +119,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
120
119
|
try {
|
|
121
120
|
await this.acquireLock(driver, lockKey);
|
|
122
121
|
await this.ensureControlTables(driver);
|
|
123
|
-
const existingMarker = await readMarker(driver);
|
|
122
|
+
const existingMarker = await this.family.readMarker({ driver });
|
|
124
123
|
|
|
125
124
|
// Validate plan origin matches existing marker (needs marker from DB)
|
|
126
125
|
const markerCheck = this.ensureMarkerCompatibility(existingMarker, options.plan);
|
|
@@ -617,7 +616,10 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
617
616
|
options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,
|
|
618
617
|
existingMarker: ContractMarkerRecord | null,
|
|
619
618
|
): Promise<void> {
|
|
620
|
-
|
|
619
|
+
// Sort + dedupe so the INSERT path writes a stable initial value.
|
|
620
|
+
// UPDATE merges server-side, so no client-side union is needed.
|
|
621
|
+
const incomingInvariants = Array.from(new Set(options.invariants ?? [])).sort();
|
|
622
|
+
const writeStatements = buildMergeMarkerStatements({
|
|
621
623
|
storageHash: options.plan.destination.storageHash,
|
|
622
624
|
profileHash:
|
|
623
625
|
options.plan.destination.profileHash ??
|
|
@@ -626,6 +628,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
626
628
|
contractJson: options.destinationContract,
|
|
627
629
|
canonicalVersion: null,
|
|
628
630
|
meta: {},
|
|
631
|
+
invariants: incomingInvariants,
|
|
629
632
|
});
|
|
630
633
|
const statement = existingMarker ? writeStatements.update : writeStatements.insert;
|
|
631
634
|
await this.executeStatement(driver, statement);
|
|
@@ -17,7 +17,8 @@ export const ensureMarkerTableStatement: SqlStatement = {
|
|
|
17
17
|
canonical_version int,
|
|
18
18
|
updated_at timestamptz not null default now(),
|
|
19
19
|
app_tag text,
|
|
20
|
-
meta jsonb not null default '{}'
|
|
20
|
+
meta jsonb not null default '{}',
|
|
21
|
+
invariants text[] not null default '{}'
|
|
21
22
|
)`,
|
|
22
23
|
params: [],
|
|
23
24
|
};
|
|
@@ -37,16 +38,23 @@ export const ensureLedgerTableStatement: SqlStatement = {
|
|
|
37
38
|
params: [],
|
|
38
39
|
};
|
|
39
40
|
|
|
40
|
-
export interface
|
|
41
|
+
export interface MergeMarkerInput {
|
|
41
42
|
readonly storageHash: string;
|
|
42
43
|
readonly profileHash: string;
|
|
43
44
|
readonly contractJson?: unknown;
|
|
44
45
|
readonly canonicalVersion?: number | null;
|
|
45
46
|
readonly appTag?: string | null;
|
|
46
47
|
readonly meta?: Record<string, unknown>;
|
|
48
|
+
/**
|
|
49
|
+
* Invariants to merge into `marker.invariants`. INSERT writes them as
|
|
50
|
+
* the initial value (callers are expected to pass a sorted, deduped
|
|
51
|
+
* array). UPDATE merges them with the existing column server-side via
|
|
52
|
+
* a single atomic SQL expression.
|
|
53
|
+
*/
|
|
54
|
+
readonly invariants: readonly string[];
|
|
47
55
|
}
|
|
48
56
|
|
|
49
|
-
export function
|
|
57
|
+
export function buildMergeMarkerStatements(input: MergeMarkerInput): {
|
|
50
58
|
readonly insert: SqlStatement;
|
|
51
59
|
readonly update: SqlStatement;
|
|
52
60
|
} {
|
|
@@ -58,6 +66,7 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
|
|
|
58
66
|
input.canonicalVersion ?? null,
|
|
59
67
|
input.appTag ?? null,
|
|
60
68
|
jsonParam(input.meta ?? {}),
|
|
69
|
+
input.invariants,
|
|
61
70
|
];
|
|
62
71
|
|
|
63
72
|
return {
|
|
@@ -70,7 +79,8 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
|
|
|
70
79
|
canonical_version,
|
|
71
80
|
updated_at,
|
|
72
81
|
app_tag,
|
|
73
|
-
meta
|
|
82
|
+
meta,
|
|
83
|
+
invariants
|
|
74
84
|
) values (
|
|
75
85
|
$1,
|
|
76
86
|
$2,
|
|
@@ -79,11 +89,16 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
|
|
|
79
89
|
$5,
|
|
80
90
|
now(),
|
|
81
91
|
$6,
|
|
82
|
-
$7::jsonb
|
|
92
|
+
$7::jsonb,
|
|
93
|
+
$8::text[]
|
|
83
94
|
)`,
|
|
84
95
|
params,
|
|
85
96
|
},
|
|
86
97
|
update: {
|
|
98
|
+
// `invariants = array(select distinct unnest(invariants || $8::text[]) order by 1)`
|
|
99
|
+
// reads the current column value under the UPDATE's row lock, unions
|
|
100
|
+
// with the incoming array, dedupes, and sorts ascending — single
|
|
101
|
+
// statement, atomic, no read-then-write window.
|
|
87
102
|
sql: `update prisma_contract.marker set
|
|
88
103
|
core_hash = $2,
|
|
89
104
|
profile_hash = $3,
|
|
@@ -91,7 +106,8 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
|
|
|
91
106
|
canonical_version = $5,
|
|
92
107
|
updated_at = now(),
|
|
93
108
|
app_tag = $6,
|
|
94
|
-
meta = $7::jsonb
|
|
109
|
+
meta = $7::jsonb,
|
|
110
|
+
invariants = array(select distinct unnest(invariants || $8::text[]) order by 1)
|
|
95
111
|
where id = $1`,
|
|
96
112
|
params,
|
|
97
113
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"codecs-D-F2KJqt.d.mts","names":[],"sources":["../src/core/codecs.ts"],"sourcesContent":[],"mappings":";;;;;;cAkmBM,8CAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA+BC;;;;;;;;;;;;;;;;;;;;;;;;;EA/BD,SAAA,KAAA,EAAA;IA+BC,SAAA,MAA0C,EAAA,aAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAC1C,SAA4B,KAAA,OAAA,CAAA,WAAA,EAAA,SAAA,CAAA,UAAA,EAAA,OAAA,EAAA,SAAA,CAAA,EAAA,MAAA,EAAA,MAAA,QAAA,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,OAAA,CAAA;IAE7B,SAAU,KAAA,EAAA,MAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAFnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAED,UAAA,UAAoB,MAAA,CAAO"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"data-transform-CxFRBIUp.d.mts","names":[],"sources":["../src/core/migrations/operations/data-transform.ts"],"sourcesContent":[],"mappings":";;;;;;;;UAwCU;WACC,aAAa;;;;;;KAOZ,oBAAA,SAA6B,eAAe;UAEvC,oBAAA;;mBAEE;;gBAEH,gCAAgC;;;;;;;KAQpC,8BAAA,GAAiC;iBAE7B,gCAAgC,SAAS,uBAC7C,kCAED,+BACA,gCACR"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"data-transform-VfEGzXWt.mjs","names":["runClosures: readonly DataTransformClosure[]"],"sources":["../src/core/migrations/operations/data-transform.ts"],"sourcesContent":["/**\n * User-facing `dataTransform` factory for the Postgres migration authoring\n * surface. Invoked directly inside a `migration.ts` file via the\n * `PostgresMigration` instance method (`this.dataTransform(...)`), which\n * supplies the control adapter from the migration's injected stack:\n *\n * ```ts\n * import endContract from './end-contract.json' with { type: 'json' };\n *\n * class M extends Migration {\n * override get operations() {\n * return [\n * this.dataTransform(endContract, 'backfill emails', {\n * check: () => db.users.count().where(({ email }) => email.isNull()),\n * run: () => db.users.update({ email: '' }).where(({ email }) => email.isNull()),\n * }),\n * ];\n * }\n * }\n * ```\n *\n * The factory accepts lazy closures (`() => SqlQueryPlan | Buildable`),\n * invokes each one, asserts that its `meta.storageHash` matches the\n * `contract` it was handed (→ `PN-MIG-2005` on mismatch), and lowers the\n * plan via the supplied control adapter to a serialized `{sql, params}`\n * payload for `ops.json`. The free factory remains usable standalone\n * (tests, ad-hoc tooling, non-class contexts) by passing the adapter\n * explicitly as the fourth argument.\n */\n\nimport type { Contract } from '@prisma-next/contract/types';\nimport { errorDataTransformContractMismatch } from '@prisma-next/errors/migration';\nimport type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';\nimport type {\n DataTransformOperation,\n SerializedQueryPlan,\n} from '@prisma-next/framework-components/control';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';\n\ninterface Buildable<R = unknown> {\n build(): SqlQueryPlan<R>;\n}\n\n/**\n * A single-closure producer of a SQL query plan. Shared between\n * `check` and each `run` entry.\n */\nexport type DataTransformClosure = () => SqlQueryPlan | Buildable;\n\nexport interface DataTransformOptions {\n /** Optional pre-flight query. `undefined` means \"no check\". */\n readonly check?: DataTransformClosure;\n /** One or more mutation queries to execute. */\n readonly run: DataTransformClosure | readonly DataTransformClosure[];\n}\n\n/**\n * Concrete Postgres flavor of `DataTransformOperation`, re-exported so the\n * `PostgresMigration.dataTransform` instance method can name it without\n * leaking the framework-components symbol into call sites.\n */\nexport type PostgresDataTransformOperation = DataTransformOperation;\n\nexport function dataTransform<TContract extends Contract<SqlStorage>>(\n contract: TContract,\n name: string,\n options: DataTransformOptions,\n adapter: SqlControlAdapter<'postgres'>,\n): DataTransformOperation {\n const runClosures: readonly DataTransformClosure[] = Array.isArray(options.run)\n ? options.run\n : [options.run as DataTransformClosure];\n return {\n id: `data_migration.${name}`,\n label: `Data transform: ${name}`,\n operationClass: 'data',\n name,\n source: 'migration.ts',\n check: options.check ? invokeAndLower(options.check, contract, adapter, name) : null,\n run: runClosures.map((closure) => invokeAndLower(closure, contract, adapter, name)),\n };\n}\n\nfunction invokeAndLower(\n closure: DataTransformClosure,\n contract: Contract<SqlStorage>,\n adapter: SqlControlAdapter<'postgres'>,\n name: string,\n): SerializedQueryPlan {\n const result = closure();\n const plan = isBuildable(result) ? result.build() : result;\n assertContractMatches(plan, contract, name);\n const lowered = adapter.lower(plan.ast, { contract });\n return { sql: lowered.sql, params: lowered.params };\n}\n\nfunction isBuildable(value: unknown): value is Buildable {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'build' in value &&\n typeof (value as { build: unknown }).build === 'function'\n );\n}\n\nfunction assertContractMatches(\n plan: SqlQueryPlan,\n contract: Contract<SqlStorage>,\n name: string,\n): void {\n if (plan.meta.storageHash !== contract.storage.storageHash) {\n throw errorDataTransformContractMismatch({\n dataTransformName: name,\n expected: contract.storage.storageHash,\n actual: plan.meta.storageHash,\n });\n }\n}\n"],"mappings":";;;AAgEA,SAAgB,cACd,UACA,MACA,SACA,SACwB;CACxB,MAAMA,cAA+C,MAAM,QAAQ,QAAQ,IAAI,GAC3E,QAAQ,MACR,CAAC,QAAQ,IAA4B;AACzC,QAAO;EACL,IAAI,kBAAkB;EACtB,OAAO,mBAAmB;EAC1B,gBAAgB;EAChB;EACA,QAAQ;EACR,OAAO,QAAQ,QAAQ,eAAe,QAAQ,OAAO,UAAU,SAAS,KAAK,GAAG;EAChF,KAAK,YAAY,KAAK,YAAY,eAAe,SAAS,UAAU,SAAS,KAAK,CAAC;EACpF;;AAGH,SAAS,eACP,SACA,UACA,SACA,MACqB;CACrB,MAAM,SAAS,SAAS;CACxB,MAAM,OAAO,YAAY,OAAO,GAAG,OAAO,OAAO,GAAG;AACpD,uBAAsB,MAAM,UAAU,KAAK;CAC3C,MAAM,UAAU,QAAQ,MAAM,KAAK,KAAK,EAAE,UAAU,CAAC;AACrD,QAAO;EAAE,KAAK,QAAQ;EAAK,QAAQ,QAAQ;EAAQ;;AAGrD,SAAS,YAAY,OAAoC;AACvD,QACE,OAAO,UAAU,YACjB,UAAU,QACV,WAAW,SACX,OAAQ,MAA6B,UAAU;;AAInD,SAAS,sBACP,MACA,UACA,MACM;AACN,KAAI,KAAK,KAAK,gBAAgB,SAAS,QAAQ,YAC7C,OAAM,mCAAmC;EACvC,mBAAmB;EACnB,UAAU,SAAS,QAAQ;EAC3B,QAAQ,KAAK,KAAK;EACnB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"planner-produced-postgres-migration-CRRTno6Z.d.mts","names":[],"sources":["../src/core/migrations/planner-produced-postgres-migration.ts"],"sourcesContent":[],"mappings":";;;;;;;;;KAmCK,EAAA,GAAK,0BAA0B;cAEvB,qCAAA,SACH,iBAAA,YACG;;8BAKiB,+BAA+B;6BAMvB;cAIf"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"statement-builders-BPnmt6wx.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 )`,\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 WriteMarkerInput {\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\nexport function buildWriteMarkerStatements(input: WriteMarkerInput): {\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 ];\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 ) values (\n $1,\n $2,\n $3,\n $4::jsonb,\n $5,\n now(),\n $6,\n $7::jsonb\n )`,\n params,\n },\n update: {\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 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;;;;;;;;;;CAUL,QAAQ,EAAE;CACX;AAED,MAAaC,6BAA2C;CACtD,KAAK;;;;;;;;;;;CAWL,QAAQ,EAAE;CACX;AAWD,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;EAC5B;AAED,QAAO;EACL,QAAQ;GACN,KAAK;;;;;;;;;;;;;;;;;;;GAmBL;GACD;EACD,QAAQ;GACN,KAAK;;;;;;;;;GASL;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"}
|