@prisma-next/target-sqlite 0.5.0-dev.62 → 0.5.0-dev.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/control.mjs +21 -6
  2. package/dist/control.mjs.map +1 -1
  3. package/dist/migration.d.mts +1 -1
  4. package/dist/migration.d.mts.map +1 -1
  5. package/dist/{planner-CuchCrpN.mjs → planner-DmOFOhoq.mjs} +6 -6
  6. package/dist/{planner-CuchCrpN.mjs.map → planner-DmOFOhoq.mjs.map} +1 -1
  7. package/dist/{planner-produced-sqlite-migration-C3AAaQoW.mjs → planner-produced-sqlite-migration-BANB82Pw.mjs} +12 -2
  8. package/dist/planner-produced-sqlite-migration-BANB82Pw.mjs.map +1 -0
  9. package/dist/{planner-produced-sqlite-migration-BfIIUtxe.d.mts → planner-produced-sqlite-migration-CJr8ATRH.d.mts} +8 -2
  10. package/dist/planner-produced-sqlite-migration-CJr8ATRH.d.mts.map +1 -0
  11. package/dist/planner-produced-sqlite-migration.d.mts +1 -1
  12. package/dist/planner-produced-sqlite-migration.mjs +1 -1
  13. package/dist/planner.d.mts +9 -3
  14. package/dist/planner.d.mts.map +1 -1
  15. package/dist/planner.mjs +1 -1
  16. package/dist/{statement-builders-B3OGOp7n.mjs → statement-builders-DobaAWnW.mjs} +21 -10
  17. package/dist/statement-builders-DobaAWnW.mjs.map +1 -0
  18. package/dist/statement-builders.d.mts +22 -2
  19. package/dist/statement-builders.d.mts.map +1 -1
  20. package/dist/statement-builders.mjs +2 -2
  21. package/package.json +17 -17
  22. package/src/core/migrations/planner-produced-sqlite-migration.ts +12 -0
  23. package/src/core/migrations/planner.ts +19 -5
  24. package/src/core/migrations/runner.ts +46 -4
  25. package/src/core/migrations/statement-builders.ts +30 -8
  26. package/src/exports/statement-builders.ts +1 -0
  27. package/dist/planner-produced-sqlite-migration-BfIIUtxe.d.mts.map +0 -1
  28. package/dist/planner-produced-sqlite-migration-C3AAaQoW.mjs.map +0 -1
  29. package/dist/statement-builders-B3OGOp7n.mjs.map +0 -1
package/dist/control.mjs CHANGED
@@ -2,8 +2,8 @@ import { t as sqliteTargetDescriptorMeta } from "./descriptor-meta-BA2YAFQq.mjs"
2
2
  import { t as parseSqliteDefault } from "./default-normalizer-R-sQXAYt.mjs";
3
3
  import { t as normalizeSqliteNativeType } from "./native-type-normalizer-BMovohPm.mjs";
4
4
  import { d as renderDefaultLiteral } from "./tables-sKIg_lWE.mjs";
5
- import { n as createSqliteMigrationPlanner } from "./planner-CuchCrpN.mjs";
6
- import { a as buildWriteMarkerStatements, c as readMarkerStatement, i as buildLedgerInsertStatement, o as ensureLedgerTableStatement, s as ensureMarkerTableStatement } from "./statement-builders-B3OGOp7n.mjs";
5
+ import { n as createSqliteMigrationPlanner } from "./planner-DmOFOhoq.mjs";
6
+ import { a as buildLedgerInsertStatement, c as ensureMarkerTableStatement, i as MARKER_TABLE_NAME, l as readMarkerStatement, o as buildWriteMarkerStatements, s as ensureLedgerTableStatement } from "./statement-builders-DobaAWnW.mjs";
7
7
  import { contractToSchemaIR, runnerFailure, runnerSuccess } from "@prisma-next/family-sql/control";
8
8
  import { verifySqlSchema } from "@prisma-next/family-sql/schema-verify";
9
9
  import { ok, okVoid } from "@prisma-next/utils/result";
@@ -30,8 +30,9 @@ var SqliteMigrationRunner = class {
30
30
  await this.beginExclusiveTransaction(driver);
31
31
  let committed = false;
32
32
  try {
33
- await this.ensureControlTables(driver);
34
- const existingMarker = await this.readMarker(driver);
33
+ const ensureResult = await this.ensureControlTables(driver);
34
+ if (!ensureResult.ok) return ensureResult;
35
+ const existingMarker = await this.readMarker(driver, options.plan.spaceId);
35
36
  const markerCheck = this.ensureMarkerCompatibility(existingMarker, options.plan);
36
37
  if (!markerCheck.ok) return markerCheck;
37
38
  const markerAtDestination = this.markerMatchesDestination(existingMarker, options.plan);
@@ -139,11 +140,24 @@ var SqliteMigrationRunner = class {
139
140
  });
140
141
  }
141
142
  async ensureControlTables(driver) {
143
+ const legacyDetection = await this.detectLegacyMarkerShape(driver);
144
+ if (!legacyDetection.ok) return legacyDetection;
142
145
  await this.executeStatement(driver, ensureMarkerTableStatement);
143
146
  await this.executeStatement(driver, ensureLedgerTableStatement);
147
+ return okVoid();
148
+ }
149
+ async detectLegacyMarkerShape(driver) {
150
+ const tableInfo = await driver.query(`PRAGMA table_info("${MARKER_TABLE_NAME}")`);
151
+ if (tableInfo.rows.length === 0) return okVoid();
152
+ const columns = new Set(tableInfo.rows.map((row) => row.name));
153
+ if (columns.has("space")) return okVoid();
154
+ return runnerFailure("LEGACY_MARKER_SHAPE", `Legacy marker-table shape detected on ${MARKER_TABLE_NAME} (no \`space\` column). Prisma Next is in pre-1.0; the previous transitional auto-migration to the per-space-row schema has been removed. Drop \`${MARKER_TABLE_NAME}\` and re-run \`dbInit\` to reinitialise from a clean baseline.`, { meta: {
155
+ table: MARKER_TABLE_NAME,
156
+ columns: [...columns].sort()
157
+ } });
144
158
  }
145
- async readMarker(driver) {
146
- const stmt = readMarkerStatement();
159
+ async readMarker(driver, space) {
160
+ const stmt = readMarkerStatement(space);
147
161
  try {
148
162
  const row = (await driver.query(stmt.sql, stmt.params)).rows[0];
149
163
  if (!row) return null;
@@ -273,6 +287,7 @@ var SqliteMigrationRunner = class {
273
287
  for (const inv of options.plan.providedInvariants) merged.add(inv);
274
288
  const invariants = Array.from(merged).sort();
275
289
  const writeStatements = buildWriteMarkerStatements({
290
+ space: options.plan.spaceId,
276
291
  storageHash: options.plan.destination.storageHash,
277
292
  profileHash: options.plan.destination.profileHash ?? options.destinationContract.profileHash ?? options.plan.destination.storageHash,
278
293
  contractJson: options.destinationContract,
@@ -1 +1 @@
1
- {"version":3,"file":"control.mjs","names":["family: SqlControlFamilyInstance","operationsExecuted: number","executedOperations: readonly SqlMigrationPlanOperation<SqlitePlanTargetDetails>[]","executedOperations: Array<SqlMigrationPlanOperation<SqlitePlanTargetDetails>>","error: unknown","sqliteControlTargetDescriptor: SqlControlTargetDescriptor<'sqlite', SqlitePlanTargetDetails>"],"sources":["../src/core/migrations/runner.ts","../src/core/control-target.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 ContractMarkerRow, parseContractMarkerRow } from '@prisma-next/family-sql/verify';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { Result } from '@prisma-next/utils/result';\nimport { ok, okVoid } from '@prisma-next/utils/result';\nimport { parseSqliteDefault } from '../default-normalizer';\nimport { normalizeSqliteNativeType } from '../native-type-normalizer';\nimport type { SqlitePlanTargetDetails } from './planner-target-details';\nimport {\n buildLedgerInsertStatement,\n buildWriteMarkerStatements,\n ensureLedgerTableStatement,\n ensureMarkerTableStatement,\n readMarkerStatement,\n type SqlStatement,\n} from './statement-builders';\n\nexport function createSqliteMigrationRunner(\n family: SqlControlFamilyInstance,\n): SqlMigrationRunner<SqlitePlanTargetDetails> {\n return new SqliteMigrationRunner(family);\n}\n\nclass SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetails> {\n constructor(private readonly family: SqlControlFamilyInstance) {}\n\n async execute(\n options: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>,\n ): Promise<SqlMigrationRunnerResult> {\n const driver = options.driver;\n\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 // SQLite recreate-table drops and rebuilds the table. If foreign_keys is ON,\n // dropping a referenced parent cascade-deletes child rows; we must disable FK\n // enforcement for the duration of the migration and validate integrity before\n // committing. PRAGMA foreign_keys is a no-op inside a transaction, so toggle\n // around BEGIN/COMMIT.\n const fkWasEnabled = await this.readForeignKeysEnabled(driver);\n if (fkWasEnabled) {\n await driver.query('PRAGMA foreign_keys = OFF');\n }\n\n try {\n await this.beginExclusiveTransaction(driver);\n let committed = false;\n try {\n await this.ensureControlTables(driver);\n const existingMarker = await this.readMarker(driver);\n\n const markerCheck = this.ensureMarkerCompatibility(existingMarker, options.plan);\n if (!markerCheck.ok) {\n return markerCheck;\n }\n\n const markerAtDestination = this.markerMatchesDestination(existingMarker, options.plan);\n const isSelfEdge =\n options.plan.origin?.storageHash === options.plan.destination.storageHash;\n const skipOperations = markerAtDestination && options.plan.origin != null && !isSelfEdge;\n\n let operationsExecuted: number;\n let executedOperations: readonly SqlMigrationPlanOperation<SqlitePlanTargetDetails>[];\n\n if (skipOperations) {\n operationsExecuted = 0;\n executedOperations = [];\n } else {\n const applyResult = await this.applyPlan(driver, options);\n if (!applyResult.ok) {\n return applyResult;\n }\n operationsExecuted = applyResult.value.operationsExecuted;\n executedOperations = applyResult.value.executedOperations;\n }\n\n const schemaIR = await this.family.introspect({\n driver,\n contract: options.destinationContract,\n });\n\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: parseSqliteDefault,\n normalizeNativeType: normalizeSqliteNativeType,\n });\n if (!schemaVerifyResult.ok) {\n return runnerFailure('SCHEMA_VERIFY_FAILED', schemaVerifyResult.summary, {\n why: 'The resulting database schema does not satisfy the destination contract.',\n meta: {\n issues: schemaVerifyResult.schema.issues,\n },\n });\n }\n\n // Self-edge no-op detection: a self-edge migration whose ops had no\n // ops to begin with and brings no new invariants produced no\n // observable change. Skip the marker + ledger writes so an idempotent\n // re-apply of a self-edge data transform doesn't churn updatedAt or\n // pile up empty ledger entries. db update no-ops still write a\n // ledger entry as audit trail.\n const incomingInvariants = options.plan.providedInvariants;\n const existingInvariants = new Set(existingMarker?.invariants ?? []);\n const incomingIsSubsetOfExisting = incomingInvariants.every((id) =>\n existingInvariants.has(id),\n );\n const isSelfEdgeNoOp = isSelfEdge && operationsExecuted === 0 && incomingIsSubsetOfExisting;\n\n if (!isSelfEdgeNoOp) {\n await this.upsertMarker(driver, options, existingMarker);\n await this.recordLedgerEntry(driver, options, existingMarker, executedOperations);\n }\n\n if (fkWasEnabled) {\n const fkIntegrityCheck = await this.verifyForeignKeyIntegrity(driver);\n if (!fkIntegrityCheck.ok) {\n return fkIntegrityCheck;\n }\n }\n\n await this.commitTransaction(driver);\n committed = true;\n return runnerSuccess({\n operationsPlanned: options.plan.operations.length,\n operationsExecuted,\n });\n } finally {\n if (!committed) {\n await this.rollbackTransaction(driver);\n }\n }\n } finally {\n if (fkWasEnabled) {\n await driver.query('PRAGMA foreign_keys = ON');\n }\n }\n }\n\n private async readForeignKeysEnabled(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n ): Promise<boolean> {\n const result = await driver.query<{ foreign_keys: number }>('PRAGMA foreign_keys');\n const row = result.rows[0];\n return row?.foreign_keys === 1;\n }\n\n private async verifyForeignKeyIntegrity(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n const result = await driver.query<Record<string, unknown>>('PRAGMA foreign_key_check');\n if (result.rows.length === 0) {\n return okVoid();\n }\n return runnerFailure(\n 'FOREIGN_KEY_VIOLATION',\n `Foreign key integrity check failed after migration: ${result.rows.length} violation(s).`,\n {\n why: 'PRAGMA foreign_key_check reported violations after applying recreate-table operations.',\n meta: { violations: result.rows },\n },\n );\n }\n\n private async applyPlan(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>,\n ): Promise<\n Result<\n {\n readonly operationsExecuted: number;\n readonly executedOperations: readonly SqlMigrationPlanOperation<SqlitePlanTargetDetails>[];\n },\n SqlMigrationRunnerFailure\n >\n > {\n const checks = options.executionChecks;\n const runPrechecks = checks?.prechecks !== false;\n const runPostchecks = checks?.postchecks !== false;\n const runIdempotency = checks?.idempotencyChecks !== false;\n\n let operationsExecuted = 0;\n const executedOperations: Array<SqlMigrationPlanOperation<SqlitePlanTargetDetails>> = [];\n\n for (const operation of options.plan.operations) {\n options.callbacks?.onOperationStart?.(operation);\n try {\n if (runPostchecks && runIdempotency) {\n const postcheckAlreadySatisfied = await this.expectationsAreSatisfied(\n driver,\n operation.postcheck,\n );\n if (postcheckAlreadySatisfied) {\n executedOperations.push(this.createSkipRecord(operation));\n continue;\n }\n }\n\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 if (runPostchecks) {\n const postcheckResult = await this.runExpectationSteps(\n driver,\n operation.postcheck,\n operation,\n 'postcheck',\n );\n if (!postcheckResult.ok) {\n return postcheckResult;\n }\n }\n\n executedOperations.push(operation);\n operationsExecuted += 1;\n } finally {\n options.callbacks?.onOperationComplete?.(operation);\n }\n }\n return ok({ operationsExecuted, executedOperations });\n }\n\n private async ensureControlTables(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n ): Promise<void> {\n await this.executeStatement(driver, ensureMarkerTableStatement);\n await this.executeStatement(driver, ensureLedgerTableStatement);\n }\n\n private async readMarker(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n ): Promise<ContractMarkerRecord | null> {\n const stmt = readMarkerStatement();\n try {\n const result = await driver.query<ContractMarkerRow>(stmt.sql, stmt.params);\n const row = result.rows[0];\n if (!row) return null;\n // SQLite stores arrays as JSON-encoded TEXT (no native array type), so\n // the driver returns `invariants` as a string. Decode before delegating\n // to the shared row schema, which expects `string[]`.\n const invariants =\n typeof row.invariants === 'string'\n ? (JSON.parse(row.invariants) as unknown)\n : row.invariants;\n return parseContractMarkerRow({ ...row, invariants });\n } catch (error) {\n // Table might not exist yet\n if (error instanceof Error && error.message.includes('no such table')) {\n return null;\n }\n throw error;\n }\n }\n\n private async runExpectationSteps(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n operation: SqlMigrationPlanOperation<SqlitePlanTargetDetails>,\n phase: 'precheck' | 'postcheck',\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n for (const step of steps) {\n const result = await driver.query(step.sql, step.params ?? []);\n if (!this.stepResultIsTrue(result.rows)) {\n const code = phase === 'precheck' ? 'PRECHECK_FAILED' : 'POSTCHECK_FAILED';\n return runnerFailure(\n code,\n `Operation ${operation.id} failed during ${phase}: ${step.description}`,\n {\n meta: {\n operationId: operation.id,\n phase,\n stepDescription: step.description,\n },\n },\n );\n }\n }\n return okVoid();\n }\n\n private async runExecuteSteps(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n operation: SqlMigrationPlanOperation<SqlitePlanTargetDetails>,\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n for (const step of steps) {\n try {\n await driver.query(step.sql, step.params ?? []);\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return runnerFailure(\n 'EXECUTION_FAILED',\n `Operation ${operation.id} failed during execution: ${step.description}`,\n {\n why: message,\n meta: {\n operationId: operation.id,\n stepDescription: step.description,\n sql: step.sql,\n },\n },\n );\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 === 'number') {\n return firstValue !== 0;\n }\n if (typeof firstValue === 'boolean') {\n return firstValue;\n }\n if (typeof firstValue === 'string') {\n const lower = firstValue.toLowerCase();\n if (lower === 'true' || lower === '1') return true;\n if (lower === 'false' || lower === '0') return false;\n return firstValue.length > 0;\n }\n return Boolean(firstValue);\n }\n\n private async expectationsAreSatisfied(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n ): Promise<boolean> {\n if (steps.length === 0) {\n return false;\n }\n for (const step of steps) {\n const result = await driver.query(step.sql, step.params ?? []);\n if (!this.stepResultIsTrue(result.rows)) {\n return false;\n }\n }\n return true;\n }\n\n private createSkipRecord(\n operation: SqlMigrationPlanOperation<SqlitePlanTargetDetails>,\n ): SqlMigrationPlanOperation<SqlitePlanTargetDetails> {\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,\n precheck: Object.freeze([]),\n execute: Object.freeze([]),\n postcheck: Object.freeze([...operation.postcheck]),\n meta: Object.freeze({\n ...(operation.meta ?? {}),\n runner: Object.freeze({ skipped: true, reason: 'postcheck_pre_satisfied' }),\n }),\n });\n }\n\n private markerMatchesDestination(\n marker: ContractMarkerRecord | null,\n plan: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['plan'],\n ): boolean {\n if (!marker) return false;\n if (marker.storageHash !== plan.destination.storageHash) return false;\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<SqlitePlanTargetDetails>[],\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<SqlitePlanTargetDetails>['plan'],\n ): Result<void, SqlMigrationRunnerFailure> {\n const origin = plan.origin ?? null;\n if (!origin) {\n return okVoid();\n }\n if (!marker) {\n return runnerFailure(\n 'MARKER_ORIGIN_MISMATCH',\n `Missing contract marker: expected origin storage hash ${origin.storageHash}.`,\n { meta: { expectedOriginStorageHash: origin.storageHash } },\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<SqlitePlanTargetDetails>['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<SqlitePlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>,\n existingMarker: ContractMarkerRecord | null,\n ): Promise<void> {\n // SQLite has no native array type, so we can't merge invariants in SQL\n // the way Postgres does. Merge client-side under the runner's\n // BEGIN EXCLUSIVE — sort + dedupe so the JSON-encoded value is stable.\n const merged = new Set<string>(existingMarker?.invariants ?? []);\n for (const inv of options.plan.providedInvariants) merged.add(inv);\n const invariants = Array.from(merged).sort();\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 invariants,\n });\n const statement = existingMarker ? writeStatements.update : writeStatements.insert;\n await this.executeStatement(driver, statement);\n }\n\n private async recordLedgerEntry(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>,\n existingMarker: ContractMarkerRecord | null,\n executedOperations: readonly SqlMigrationPlanOperation<SqlitePlanTargetDetails>[],\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 beginExclusiveTransaction(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('BEGIN EXCLUSIVE');\n }\n\n private async commitTransaction(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('COMMIT');\n }\n\n private async rollbackTransaction(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('ROLLBACK');\n }\n\n private async executeStatement(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['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 } from '@prisma-next/family-sql/control';\nimport type {\n ControlTargetInstance,\n MigrationPlanner,\n MigrationRunner,\n} from '@prisma-next/framework-components/control';\nimport type { SqlStorage, StorageColumn } from '@prisma-next/sql-contract/types';\nimport { sqliteTargetDescriptorMeta } from './descriptor-meta';\nimport { createSqliteMigrationPlanner } from './migrations/planner';\nimport { renderDefaultLiteral } from './migrations/planner-ddl-builders';\nimport type { SqlitePlanTargetDetails } from './migrations/planner-target-details';\nimport { createSqliteMigrationRunner } from './migrations/runner';\n\nfunction sqliteRenderDefault(def: ColumnDefault, _column: StorageColumn): string {\n if (def.kind === 'function') {\n if (def.expression === 'now()') {\n return \"datetime('now')\";\n }\n return def.expression;\n }\n return renderDefaultLiteral(def.value);\n}\n\nconst sqliteControlTargetDescriptor: SqlControlTargetDescriptor<'sqlite', SqlitePlanTargetDetails> =\n {\n ...sqliteTargetDescriptorMeta,\n migrations: {\n createPlanner(_family: SqlControlFamilyInstance): MigrationPlanner<'sql', 'sqlite'> {\n return createSqliteMigrationPlanner();\n },\n createRunner(family) {\n return createSqliteMigrationRunner(family) as MigrationRunner<'sql', 'sqlite'>;\n },\n contractToSchema(contract, frameworkComponents) {\n return contractToSchemaIR(contract as Contract<SqlStorage> | null, {\n annotationNamespace: 'sqlite',\n renderDefault: sqliteRenderDefault,\n frameworkComponents: frameworkComponents ?? [],\n });\n },\n },\n create(): ControlTargetInstance<'sql', 'sqlite'> {\n return {\n familyId: 'sql',\n targetId: 'sqlite',\n };\n },\n createPlanner(_family: SqlControlFamilyInstance) {\n return createSqliteMigrationPlanner();\n },\n createRunner(family) {\n return createSqliteMigrationRunner(family);\n },\n };\n\nexport default sqliteControlTargetDescriptor;\n"],"mappings":"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"}
1
+ {"version":3,"file":"control.mjs","names":["family: SqlControlFamilyInstance","operationsExecuted: number","executedOperations: readonly SqlMigrationPlanOperation<SqlitePlanTargetDetails>[]","executedOperations: Array<SqlMigrationPlanOperation<SqlitePlanTargetDetails>>","error: unknown","sqliteControlTargetDescriptor: SqlControlTargetDescriptor<'sqlite', SqlitePlanTargetDetails>"],"sources":["../src/core/migrations/runner.ts","../src/core/control-target.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 ContractMarkerRow, parseContractMarkerRow } from '@prisma-next/family-sql/verify';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { Result } from '@prisma-next/utils/result';\nimport { ok, okVoid } from '@prisma-next/utils/result';\nimport { parseSqliteDefault } from '../default-normalizer';\nimport { normalizeSqliteNativeType } from '../native-type-normalizer';\nimport type { SqlitePlanTargetDetails } from './planner-target-details';\nimport {\n buildLedgerInsertStatement,\n buildWriteMarkerStatements,\n ensureLedgerTableStatement,\n ensureMarkerTableStatement,\n MARKER_TABLE_NAME,\n readMarkerStatement,\n type SqlStatement,\n} from './statement-builders';\n\nexport function createSqliteMigrationRunner(\n family: SqlControlFamilyInstance,\n): SqlMigrationRunner<SqlitePlanTargetDetails> {\n return new SqliteMigrationRunner(family);\n}\n\nclass SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetails> {\n constructor(private readonly family: SqlControlFamilyInstance) {}\n\n async execute(\n options: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>,\n ): Promise<SqlMigrationRunnerResult> {\n const driver = options.driver;\n\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 // SQLite recreate-table drops and rebuilds the table. If foreign_keys is ON,\n // dropping a referenced parent cascade-deletes child rows; we must disable FK\n // enforcement for the duration of the migration and validate integrity before\n // committing. PRAGMA foreign_keys is a no-op inside a transaction, so toggle\n // around BEGIN/COMMIT.\n const fkWasEnabled = await this.readForeignKeysEnabled(driver);\n if (fkWasEnabled) {\n await driver.query('PRAGMA foreign_keys = OFF');\n }\n\n try {\n await this.beginExclusiveTransaction(driver);\n let committed = false;\n try {\n const ensureResult = await this.ensureControlTables(driver);\n if (!ensureResult.ok) {\n return ensureResult;\n }\n const existingMarker = await this.readMarker(driver, options.plan.spaceId);\n\n const markerCheck = this.ensureMarkerCompatibility(existingMarker, options.plan);\n if (!markerCheck.ok) {\n return markerCheck;\n }\n\n const markerAtDestination = this.markerMatchesDestination(existingMarker, options.plan);\n const isSelfEdge =\n options.plan.origin?.storageHash === options.plan.destination.storageHash;\n const skipOperations = markerAtDestination && options.plan.origin != null && !isSelfEdge;\n\n let operationsExecuted: number;\n let executedOperations: readonly SqlMigrationPlanOperation<SqlitePlanTargetDetails>[];\n\n if (skipOperations) {\n operationsExecuted = 0;\n executedOperations = [];\n } else {\n const applyResult = await this.applyPlan(driver, options);\n if (!applyResult.ok) {\n return applyResult;\n }\n operationsExecuted = applyResult.value.operationsExecuted;\n executedOperations = applyResult.value.executedOperations;\n }\n\n const schemaIR = await this.family.introspect({\n driver,\n contract: options.destinationContract,\n });\n\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: parseSqliteDefault,\n normalizeNativeType: normalizeSqliteNativeType,\n });\n if (!schemaVerifyResult.ok) {\n return runnerFailure('SCHEMA_VERIFY_FAILED', schemaVerifyResult.summary, {\n why: 'The resulting database schema does not satisfy the destination contract.',\n meta: {\n issues: schemaVerifyResult.schema.issues,\n },\n });\n }\n\n // Self-edge no-op detection: a self-edge migration whose ops had no\n // ops to begin with and brings no new invariants produced no\n // observable change. Skip the marker + ledger writes so an idempotent\n // re-apply of a self-edge data transform doesn't churn updatedAt or\n // pile up empty ledger entries. db update no-ops still write a\n // ledger entry as audit trail.\n const incomingInvariants = options.plan.providedInvariants;\n const existingInvariants = new Set(existingMarker?.invariants ?? []);\n const incomingIsSubsetOfExisting = incomingInvariants.every((id) =>\n existingInvariants.has(id),\n );\n const isSelfEdgeNoOp = isSelfEdge && operationsExecuted === 0 && incomingIsSubsetOfExisting;\n\n if (!isSelfEdgeNoOp) {\n await this.upsertMarker(driver, options, existingMarker);\n await this.recordLedgerEntry(driver, options, existingMarker, executedOperations);\n }\n\n if (fkWasEnabled) {\n const fkIntegrityCheck = await this.verifyForeignKeyIntegrity(driver);\n if (!fkIntegrityCheck.ok) {\n return fkIntegrityCheck;\n }\n }\n\n await this.commitTransaction(driver);\n committed = true;\n return runnerSuccess({\n operationsPlanned: options.plan.operations.length,\n operationsExecuted,\n });\n } finally {\n if (!committed) {\n await this.rollbackTransaction(driver);\n }\n }\n } finally {\n if (fkWasEnabled) {\n await driver.query('PRAGMA foreign_keys = ON');\n }\n }\n }\n\n private async readForeignKeysEnabled(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n ): Promise<boolean> {\n const result = await driver.query<{ foreign_keys: number }>('PRAGMA foreign_keys');\n const row = result.rows[0];\n return row?.foreign_keys === 1;\n }\n\n private async verifyForeignKeyIntegrity(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n const result = await driver.query<Record<string, unknown>>('PRAGMA foreign_key_check');\n if (result.rows.length === 0) {\n return okVoid();\n }\n return runnerFailure(\n 'FOREIGN_KEY_VIOLATION',\n `Foreign key integrity check failed after migration: ${result.rows.length} violation(s).`,\n {\n why: 'PRAGMA foreign_key_check reported violations after applying recreate-table operations.',\n meta: { violations: result.rows },\n },\n );\n }\n\n private async applyPlan(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>,\n ): Promise<\n Result<\n {\n readonly operationsExecuted: number;\n readonly executedOperations: readonly SqlMigrationPlanOperation<SqlitePlanTargetDetails>[];\n },\n SqlMigrationRunnerFailure\n >\n > {\n const checks = options.executionChecks;\n const runPrechecks = checks?.prechecks !== false;\n const runPostchecks = checks?.postchecks !== false;\n const runIdempotency = checks?.idempotencyChecks !== false;\n\n let operationsExecuted = 0;\n const executedOperations: Array<SqlMigrationPlanOperation<SqlitePlanTargetDetails>> = [];\n\n for (const operation of options.plan.operations) {\n options.callbacks?.onOperationStart?.(operation);\n try {\n if (runPostchecks && runIdempotency) {\n const postcheckAlreadySatisfied = await this.expectationsAreSatisfied(\n driver,\n operation.postcheck,\n );\n if (postcheckAlreadySatisfied) {\n executedOperations.push(this.createSkipRecord(operation));\n continue;\n }\n }\n\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 if (runPostchecks) {\n const postcheckResult = await this.runExpectationSteps(\n driver,\n operation.postcheck,\n operation,\n 'postcheck',\n );\n if (!postcheckResult.ok) {\n return postcheckResult;\n }\n }\n\n executedOperations.push(operation);\n operationsExecuted += 1;\n } finally {\n options.callbacks?.onOperationComplete?.(operation);\n }\n }\n return ok({ operationsExecuted, executedOperations });\n }\n\n private async ensureControlTables(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n // Pre-1.0 zero-range guardrail: detect a pre-cleanup single-row\n // marker table (no `space` column) and surface a structured failure\n // rather than silently rebuilding the table into the per-space\n // shape. See `specs/framework-mechanism.spec.md § 2`.\n const legacyDetection = await this.detectLegacyMarkerShape(driver);\n if (!legacyDetection.ok) {\n return legacyDetection;\n }\n await this.executeStatement(driver, ensureMarkerTableStatement);\n await this.executeStatement(driver, ensureLedgerTableStatement);\n return okVoid();\n }\n\n private async detectLegacyMarkerShape(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n const tableInfo = await driver.query<{ name: string }>(\n `PRAGMA table_info(\"${MARKER_TABLE_NAME}\")`,\n );\n if (tableInfo.rows.length === 0) {\n return okVoid();\n }\n const columns = new Set(tableInfo.rows.map((row) => row.name));\n if (columns.has('space')) {\n return okVoid();\n }\n return runnerFailure(\n 'LEGACY_MARKER_SHAPE',\n `Legacy marker-table shape detected on ${MARKER_TABLE_NAME} (no \\`space\\` column). ` +\n 'Prisma Next is in pre-1.0; the previous transitional auto-migration to the per-space-row schema has been removed. ' +\n `Drop \\`${MARKER_TABLE_NAME}\\` and re-run \\`dbInit\\` to reinitialise from a clean baseline.`,\n {\n meta: {\n table: MARKER_TABLE_NAME,\n columns: [...columns].sort(),\n },\n },\n );\n }\n\n private async readMarker(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n space: string,\n ): Promise<ContractMarkerRecord | null> {\n const stmt = readMarkerStatement(space);\n try {\n const result = await driver.query<ContractMarkerRow>(stmt.sql, stmt.params);\n const row = result.rows[0];\n if (!row) return null;\n // SQLite stores arrays as JSON-encoded TEXT (no native array type), so\n // the driver returns `invariants` as a string. Decode before delegating\n // to the shared row schema, which expects `string[]`.\n const invariants =\n typeof row.invariants === 'string'\n ? (JSON.parse(row.invariants) as unknown)\n : row.invariants;\n return parseContractMarkerRow({ ...row, invariants });\n } catch (error) {\n // Table might not exist yet\n if (error instanceof Error && error.message.includes('no such table')) {\n return null;\n }\n throw error;\n }\n }\n\n private async runExpectationSteps(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n operation: SqlMigrationPlanOperation<SqlitePlanTargetDetails>,\n phase: 'precheck' | 'postcheck',\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n for (const step of steps) {\n const result = await driver.query(step.sql, step.params ?? []);\n if (!this.stepResultIsTrue(result.rows)) {\n const code = phase === 'precheck' ? 'PRECHECK_FAILED' : 'POSTCHECK_FAILED';\n return runnerFailure(\n code,\n `Operation ${operation.id} failed during ${phase}: ${step.description}`,\n {\n meta: {\n operationId: operation.id,\n phase,\n stepDescription: step.description,\n },\n },\n );\n }\n }\n return okVoid();\n }\n\n private async runExecuteSteps(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n operation: SqlMigrationPlanOperation<SqlitePlanTargetDetails>,\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n for (const step of steps) {\n try {\n await driver.query(step.sql, step.params ?? []);\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return runnerFailure(\n 'EXECUTION_FAILED',\n `Operation ${operation.id} failed during execution: ${step.description}`,\n {\n why: message,\n meta: {\n operationId: operation.id,\n stepDescription: step.description,\n sql: step.sql,\n },\n },\n );\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 === 'number') {\n return firstValue !== 0;\n }\n if (typeof firstValue === 'boolean') {\n return firstValue;\n }\n if (typeof firstValue === 'string') {\n const lower = firstValue.toLowerCase();\n if (lower === 'true' || lower === '1') return true;\n if (lower === 'false' || lower === '0') return false;\n return firstValue.length > 0;\n }\n return Boolean(firstValue);\n }\n\n private async expectationsAreSatisfied(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n ): Promise<boolean> {\n if (steps.length === 0) {\n return false;\n }\n for (const step of steps) {\n const result = await driver.query(step.sql, step.params ?? []);\n if (!this.stepResultIsTrue(result.rows)) {\n return false;\n }\n }\n return true;\n }\n\n private createSkipRecord(\n operation: SqlMigrationPlanOperation<SqlitePlanTargetDetails>,\n ): SqlMigrationPlanOperation<SqlitePlanTargetDetails> {\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,\n precheck: Object.freeze([]),\n execute: Object.freeze([]),\n postcheck: Object.freeze([...operation.postcheck]),\n meta: Object.freeze({\n ...(operation.meta ?? {}),\n runner: Object.freeze({ skipped: true, reason: 'postcheck_pre_satisfied' }),\n }),\n });\n }\n\n private markerMatchesDestination(\n marker: ContractMarkerRecord | null,\n plan: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['plan'],\n ): boolean {\n if (!marker) return false;\n if (marker.storageHash !== plan.destination.storageHash) return false;\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<SqlitePlanTargetDetails>[],\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<SqlitePlanTargetDetails>['plan'],\n ): Result<void, SqlMigrationRunnerFailure> {\n const origin = plan.origin ?? null;\n if (!origin) {\n return okVoid();\n }\n if (!marker) {\n return runnerFailure(\n 'MARKER_ORIGIN_MISMATCH',\n `Missing contract marker: expected origin storage hash ${origin.storageHash}.`,\n { meta: { expectedOriginStorageHash: origin.storageHash } },\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<SqlitePlanTargetDetails>['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<SqlitePlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>,\n existingMarker: ContractMarkerRecord | null,\n ): Promise<void> {\n // SQLite has no native array type, so we can't merge invariants in SQL\n // the way Postgres does. Merge client-side under the runner's\n // BEGIN EXCLUSIVE — sort + dedupe so the JSON-encoded value is stable.\n const merged = new Set<string>(existingMarker?.invariants ?? []);\n for (const inv of options.plan.providedInvariants) merged.add(inv);\n const invariants = Array.from(merged).sort();\n const writeStatements = buildWriteMarkerStatements({\n space: options.plan.spaceId,\n storageHash: options.plan.destination.storageHash,\n profileHash:\n options.plan.destination.profileHash ??\n options.destinationContract.profileHash ??\n options.plan.destination.storageHash,\n contractJson: options.destinationContract,\n canonicalVersion: null,\n meta: {},\n invariants,\n });\n const statement = existingMarker ? writeStatements.update : writeStatements.insert;\n await this.executeStatement(driver, statement);\n }\n\n private async recordLedgerEntry(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>,\n existingMarker: ContractMarkerRecord | null,\n executedOperations: readonly SqlMigrationPlanOperation<SqlitePlanTargetDetails>[],\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 beginExclusiveTransaction(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('BEGIN EXCLUSIVE');\n }\n\n private async commitTransaction(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('COMMIT');\n }\n\n private async rollbackTransaction(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('ROLLBACK');\n }\n\n private async executeStatement(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['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 } from '@prisma-next/family-sql/control';\nimport type {\n ControlTargetInstance,\n MigrationPlanner,\n MigrationRunner,\n} from '@prisma-next/framework-components/control';\nimport type { SqlStorage, StorageColumn } from '@prisma-next/sql-contract/types';\nimport { sqliteTargetDescriptorMeta } from './descriptor-meta';\nimport { createSqliteMigrationPlanner } from './migrations/planner';\nimport { renderDefaultLiteral } from './migrations/planner-ddl-builders';\nimport type { SqlitePlanTargetDetails } from './migrations/planner-target-details';\nimport { createSqliteMigrationRunner } from './migrations/runner';\n\nfunction sqliteRenderDefault(def: ColumnDefault, _column: StorageColumn): string {\n if (def.kind === 'function') {\n if (def.expression === 'now()') {\n return \"datetime('now')\";\n }\n return def.expression;\n }\n return renderDefaultLiteral(def.value);\n}\n\nconst sqliteControlTargetDescriptor: SqlControlTargetDescriptor<'sqlite', SqlitePlanTargetDetails> =\n {\n ...sqliteTargetDescriptorMeta,\n migrations: {\n createPlanner(_family: SqlControlFamilyInstance): MigrationPlanner<'sql', 'sqlite'> {\n return createSqliteMigrationPlanner();\n },\n createRunner(family) {\n return createSqliteMigrationRunner(family) as MigrationRunner<'sql', 'sqlite'>;\n },\n contractToSchema(contract, frameworkComponents) {\n return contractToSchemaIR(contract as Contract<SqlStorage> | null, {\n annotationNamespace: 'sqlite',\n renderDefault: sqliteRenderDefault,\n frameworkComponents: frameworkComponents ?? [],\n });\n },\n },\n create(): ControlTargetInstance<'sql', 'sqlite'> {\n return {\n familyId: 'sql',\n targetId: 'sqlite',\n };\n },\n createPlanner(_family: SqlControlFamilyInstance) {\n return createSqliteMigrationPlanner();\n },\n createRunner(family) {\n return createSqliteMigrationRunner(family);\n },\n };\n\nexport default sqliteControlTargetDescriptor;\n"],"mappings":"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"}
@@ -2,6 +2,7 @@ import { i as SqliteTableSpec, n as SqliteColumnSpec, r as SqliteIndexSpec, t as
2
2
  import { t as SqliteMigration } from "./sqlite-migration-BeR1cikr.mjs";
3
3
  import { MigrationOperationClass } from "@prisma-next/family-sql/control";
4
4
  import { placeholder } from "@prisma-next/errors/migration";
5
+ import "@prisma-next/framework-components/control";
5
6
  import { MigrationCLI } from "@prisma-next/cli/migration-cli";
6
7
 
7
8
  //#region src/core/migrations/operations/columns.d.ts
@@ -9,7 +10,6 @@ declare function addColumn(tableName: string, column: SqliteColumnSpec): Op;
9
10
  declare function dropColumn(tableName: string, columnName: string): Op;
10
11
  //#endregion
11
12
  //#region src/core/migrations/operations/data-transform.d.ts
12
-
13
13
  interface DataTransformOptions {
14
14
  /** Stable id used in the ledger / for runner idempotency tracking. */
15
15
  readonly id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"migration.d.mts","names":[],"sources":["../src/core/migrations/operations/columns.ts","../src/core/migrations/operations/data-transform.ts","../src/core/migrations/operations/indexes.ts","../src/core/migrations/operations/tables.ts"],"sourcesContent":[],"mappings":";;;;;;;iBAIgB,SAAA,4BAAqC,mBAAmB;iBA+BxD,UAAA,yCAAmD;;;;ACInD,UAvBC,oBAAA,CAuBmB;;;;EClCpB,SAAA,KAAW,EAAA,MAAA;EAyBX;;;;ACchB;AAuBA;EAuBiB,SAAA,WAAiB,EAAA,MAAA;EAGR;;;;AAwB1B;;;;;;iBF9EgB,aAAA,OAAoB,uBAAuB;;;iBClC3C,WAAA,oEAA+E;iBAyB/E,SAAA,wCAAiD;;;iBCcjD,WAAA,0BAAqC,kBAAkB;iBAuBvD,SAAA,qBAA8B;UAuB7B,iBAAA;;EHtFD;EA+BA,SAAA,aAAU,EG0DA,eH1D2C;;;;ACnBrE;AAuBA;;;;AClCA;AAyBA;;6BC2E6B;;EA7Db,SAAA,OAAW,EAAA,MAAA;EAuBX;AAuBhB;;;;EAwBkD,SAAA,UAAA,EAAA,SAAA;IAGlC,SAAA,WAAa,EAAA,MAAO;;;2BAHT;;iBAGX,aAAA,OAAoB,oBAAoB"}
1
+ {"version":3,"file":"migration.d.mts","names":[],"sources":["../src/core/migrations/operations/columns.ts","../src/core/migrations/operations/data-transform.ts","../src/core/migrations/operations/indexes.ts","../src/core/migrations/operations/tables.ts"],"sourcesContent":[],"mappings":";;;;;;;;iBAIgB,SAAA,4BAAqC,mBAAmB;iBA+BxD,UAAA,yCAAmD;;;ACnBlD,UAAA,oBAAA,CAAoB;EAuBrB;;;;EClCA;EAyBA,SAAA,KAAS,EAAA,MAAA;;;;ACczB;EAuBgB,SAAA,WAAS,EAAA,MAAuB;EAuB/B;;;;;AA2BjB;;;;;iBF9EgB,aAAA,OAAoB,uBAAuB;;;iBClC3C,WAAA,oEAA+E;iBAyB/E,SAAA,wCAAiD;;;iBCcjD,WAAA,0BAAqC,kBAAkB;iBAuBvD,SAAA,qBAA8B;UAuB7B,iBAAA;;;EHtFD,SAAA,aAAS,EGyFC,eHzF2B;EA+BrC;;;;ACnBhB;AAuBA;;;;AClCA;AAyBA;6BC2E6B;;;EA7Db;AAuBhB;AAuBA;;;EAwB2B,SAAA,UAAA,EAAA,SAAA;IAAuB,SAAA,WAAA,EAAA,MAAA;IAGlC,SAAA,GAAa,EAAA,MAAA;;2BAHF;;iBAGX,aAAA,OAAoB,oBAAoB"}
@@ -2,8 +2,8 @@ import { t as parseSqliteDefault } from "./default-normalizer-R-sQXAYt.mjs";
2
2
  import { t as normalizeSqliteNativeType } from "./native-type-normalizer-BMovohPm.mjs";
3
3
  import { c as buildColumnDefaultSql, l as buildColumnTypeSql, n as buildRecreateSummary, t as buildRecreatePostchecks, u as isInlineAutoincrementPrimaryKey } from "./tables-sKIg_lWE.mjs";
4
4
  import { a as DropColumnCall, c as RecreateTableCall, i as DataTransformCall, n as CreateIndexCall, o as DropIndexCall, r as CreateTableCall, s as DropTableCall, t as AddColumnCall } from "./op-factory-call-BUVV-W9F.mjs";
5
- import { t as CONTROL_TABLE_NAMES } from "./statement-builders-B3OGOp7n.mjs";
6
- import { t as TypeScriptRenderableSqliteMigration } from "./planner-produced-sqlite-migration-C3AAaQoW.mjs";
5
+ import { n as CONTROL_TABLE_NAMES } from "./statement-builders-DobaAWnW.mjs";
6
+ import { t as TypeScriptRenderableSqliteMigration } from "./planner-produced-sqlite-migration-BANB82Pw.mjs";
7
7
  import { extractCodecControlHooks, plannerFailure } from "@prisma-next/family-sql/control";
8
8
  import { verifySqlSchema } from "@prisma-next/family-sql/schema-verify";
9
9
  import { defaultIndexName } from "@prisma-next/sql-schema-ir/naming";
@@ -458,11 +458,11 @@ var SqliteMigrationPlanner = class {
458
458
  plan(options) {
459
459
  return this.planSql(options);
460
460
  }
461
- emptyMigration(context) {
461
+ emptyMigration(context, spaceId) {
462
462
  return new TypeScriptRenderableSqliteMigration([], {
463
463
  from: context.fromHash,
464
464
  to: context.toHash
465
- });
465
+ }, spaceId);
466
466
  }
467
467
  planSql(options) {
468
468
  const policyResult = this.ensureAdditivePolicy(options.policy);
@@ -491,7 +491,7 @@ var SqliteMigrationPlanner = class {
491
491
  plan: new TypeScriptRenderableSqliteMigration(result.value.calls, {
492
492
  from: options.fromContract?.storage.storageHash ?? null,
493
493
  to: options.contract.storage.storageHash
494
- }, destination)
494
+ }, options.spaceId, destination)
495
495
  };
496
496
  }
497
497
  ensureAdditivePolicy(policy) {
@@ -519,4 +519,4 @@ var SqliteMigrationPlanner = class {
519
519
 
520
520
  //#endregion
521
521
  export { createSqliteMigrationPlanner as n, SqliteMigrationPlanner as t };
522
- //# sourceMappingURL=planner-CuchCrpN.mjs.map
522
+ //# sourceMappingURL=planner-DmOFOhoq.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"planner-CuchCrpN.mjs","names":["recreateTableStrategy: CallMigrationStrategy","calls: SqliteOpFactoryCall[]","operationClass: MigrationOperationClass","indexes: SqliteIndexSpec[]","nullabilityTighteningBackfillStrategy: CallMigrationStrategy","sqlitePlannerStrategies: readonly CallMigrationStrategy[]","ISSUE_KIND_ORDER: Record<string, number>","location: {\n table?: string;\n column?: string;\n constraint?: string;\n }","location: { table?: string; column?: string; index?: string }","columns: SqliteColumnSpec[]","uniques: SqliteUniqueSpec[]","foreignKeys: SqliteForeignKeySpec[]","DEFAULT_POLICY: MigrationOperationPolicy","calls: SqliteOpFactoryCall[]","context: StrategyContext","recipeCalls: SqliteOpFactoryCall[]","bucketableCalls: SqliteOpFactoryCall[]","defaultCalls: SqliteOpFactoryCall[]","conflicts: SqlPlannerConflict[]","gatedRecipeBucket: SqliteOpFactoryCall[]","gatedBucketableBucket: SqliteOpFactoryCall[]","gatedDefaultBucket: SqliteOpFactoryCall[]","destination: SqliteMigrationDestinationInfo"],"sources":["../src/core/migrations/planner-strategies.ts","../src/core/migrations/issue-planner.ts","../src/core/migrations/planner.ts"],"sourcesContent":["/**\n * SQLite migration strategies.\n *\n * Each strategy examines the issue list, consumes issues it handles, and\n * returns the `SqliteOpFactoryCall[]` to address them. The issue planner\n * runs each strategy in order and routes whatever's left through\n * `mapIssueToCall`.\n *\n * SQLite has no enums, no data-safe backfill, and no component-declared\n * database dependencies. The only recipe that needs strategy-level\n * multi-issue consumption is `recreateTable` (added in a later phase), which\n * absorbs type/nullability/default/constraint mismatches for a given table\n * into a single recreate operation.\n */\n\nimport type { Contract } from '@prisma-next/contract/types';\nimport type {\n CodecControlHooks,\n MigrationOperationClass,\n MigrationOperationPolicy,\n} from '@prisma-next/family-sql/control';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type { SchemaIssue } from '@prisma-next/framework-components/control';\nimport type { SqlStorage, StorageTypeInstance } from '@prisma-next/sql-contract/types';\nimport { defaultIndexName } from '@prisma-next/sql-schema-ir/naming';\nimport type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';\nimport { toTableSpec } from './issue-planner';\nimport { DataTransformCall, RecreateTableCall, type SqliteOpFactoryCall } from './op-factory-call';\nimport type { SqliteIndexSpec } from './operations/shared';\nimport { buildRecreatePostchecks, buildRecreateSummary } from './operations/tables';\n\nexport interface StrategyContext {\n readonly toContract: Contract<SqlStorage>;\n readonly fromContract: Contract<SqlStorage> | null;\n readonly codecHooks: ReadonlyMap<string, CodecControlHooks>;\n readonly storageTypes: Readonly<Record<string, StorageTypeInstance>>;\n readonly schema: SqlSchemaIR;\n readonly policy: MigrationOperationPolicy;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;\n}\n\nexport type CallMigrationStrategy = (\n issues: readonly SchemaIssue[],\n context: StrategyContext,\n) =>\n | {\n kind: 'match';\n issues: readonly SchemaIssue[];\n calls: readonly SqliteOpFactoryCall[];\n recipe?: boolean;\n }\n | { kind: 'no_match' };\n\n// ============================================================================\n// Recreate-table strategy\n// ============================================================================\n\nconst WIDENING_ISSUE_KINDS = new Set<SchemaIssue['kind']>(['default_mismatch', 'default_missing']);\n\nconst DESTRUCTIVE_ISSUE_KINDS = new Set<SchemaIssue['kind']>([\n 'extra_default',\n 'type_mismatch',\n 'primary_key_mismatch',\n 'foreign_key_mismatch',\n 'unique_constraint_mismatch',\n 'extra_foreign_key',\n 'extra_unique_constraint',\n 'extra_primary_key',\n]);\n\nfunction classifyIssue(issue: SchemaIssue): 'widening' | 'destructive' | null {\n if (issue.kind === 'enum_values_changed') return null;\n if (!issue.table) return null;\n if (issue.kind === 'nullability_mismatch') {\n // Relaxing (NOT NULL → nullable) is widening; tightening is destructive.\n return issue.expected === 'true' ? 'widening' : 'destructive';\n }\n if (WIDENING_ISSUE_KINDS.has(issue.kind)) return 'widening';\n if (DESTRUCTIVE_ISSUE_KINDS.has(issue.kind)) return 'destructive';\n return null;\n}\n\n/**\n * Groups recreate-eligible issues by table, decides per-table operation class\n * (destructive wins over widening), and emits one `RecreateTableCall` per\n * table. Returns unchanged-or-smaller issue list — issues the strategy\n * consumed are removed so `mapIssueToCall` doesn't double-handle them.\n */\nexport const recreateTableStrategy: CallMigrationStrategy = (issues, ctx) => {\n const byTable = new Map<string, { issues: SchemaIssue[]; hasDestructive: boolean }>();\n const consumed = new Set<SchemaIssue>();\n\n for (const issue of issues) {\n const cls = classifyIssue(issue);\n if (!cls) continue;\n if (issue.kind === 'enum_values_changed') continue;\n if (!issue.table) continue;\n const table = issue.table;\n const entry = byTable.get(table);\n if (entry) {\n entry.issues.push(issue);\n if (cls === 'destructive') entry.hasDestructive = true;\n } else {\n byTable.set(table, { issues: [issue], hasDestructive: cls === 'destructive' });\n }\n consumed.add(issue);\n }\n\n if (byTable.size === 0) return { kind: 'no_match' };\n\n const calls: SqliteOpFactoryCall[] = [];\n for (const [tableName, entry] of byTable) {\n const contractTable = ctx.toContract.storage.tables[tableName];\n const schemaTable = ctx.schema.tables[tableName];\n if (!contractTable || !schemaTable) continue;\n const operationClass: MigrationOperationClass = entry.hasDestructive\n ? 'destructive'\n : 'widening';\n\n // Flatten the contract table to a self-contained spec — the Call holds\n // pre-rendered SQL fragments only, no `StorageColumn` or `storageTypes`.\n const tableSpec = toTableSpec(contractTable, ctx.storageTypes);\n\n const seenIndexColumnKeys = new Set<string>();\n const indexes: SqliteIndexSpec[] = [];\n for (const idx of contractTable.indexes) {\n const key = idx.columns.join(',');\n if (seenIndexColumnKeys.has(key)) continue;\n seenIndexColumnKeys.add(key);\n indexes.push({\n name: idx.name ?? defaultIndexName(tableName, idx.columns),\n columns: idx.columns,\n });\n }\n for (const fk of contractTable.foreignKeys) {\n if (fk.index === false) continue;\n const key = fk.columns.join(',');\n if (seenIndexColumnKeys.has(key)) continue;\n seenIndexColumnKeys.add(key);\n indexes.push({\n name: defaultIndexName(tableName, fk.columns),\n columns: fk.columns,\n });\n }\n\n calls.push(\n new RecreateTableCall({\n tableName,\n contractTable: tableSpec,\n schemaColumnNames: Object.keys(schemaTable.columns),\n indexes,\n summary: buildRecreateSummary(tableName, entry.issues),\n postchecks: buildRecreatePostchecks(tableName, entry.issues, tableSpec),\n operationClass,\n }),\n );\n }\n\n return {\n kind: 'match',\n issues: issues.filter((i) => !consumed.has(i)),\n calls,\n recipe: true,\n };\n};\n\n// ============================================================================\n// Nullability-tightening backfill strategy\n// ============================================================================\n\n/**\n * When the policy allows `'data'` and the contract tightens one or more\n * columns from nullable to NOT NULL, emit a `DataTransformCall` stub per\n * tightened column. The user fills the backfill `UPDATE` in the rendered\n * `migration.ts` before the subsequent `RecreateTableCall` copies data into\n * the tightened schema (whose `INSERT INTO temp SELECT … FROM old` would\n * otherwise fail at runtime if any `NULL`s remain).\n *\n * Does NOT consume the tightening issue — `recreateTableStrategy` still\n * needs it to produce the actual recreate that enforces the NOT NULL at\n * the schema level. The backfill op and the recreate op end up in the\n * recipe slot in strategy order (backfill first, recreate second), which\n * matches the required execution order.\n *\n * Mirrors Postgres's `nullableTighteningCallStrategy` / `'data'`-class\n * gating. When `'data'` is not in the policy (the default `db update` /\n * `db init` path), the strategy short-circuits and the recreate alone\n * runs with its current destructive-class gating — preserving today's\n * behavior where a tightening blows up at runtime if NULLs are present.\n */\nexport const nullabilityTighteningBackfillStrategy: CallMigrationStrategy = (issues, ctx) => {\n if (!ctx.policy.allowedOperationClasses.includes('data')) {\n return { kind: 'no_match' };\n }\n\n const calls: SqliteOpFactoryCall[] = [];\n for (const issue of issues) {\n if (issue.kind !== 'nullability_mismatch') continue;\n if (!issue.table || !issue.column) continue;\n // Tightening only: `expected === 'true'` means the contract wants the\n // column nullable (relaxing from NOT NULL → nullable), which is safe and\n // needs no backfill.\n if (issue.expected === 'true') continue;\n\n const column = ctx.toContract.storage.tables[issue.table]?.columns[issue.column];\n if (!column || column.nullable === true) continue;\n\n calls.push(\n new DataTransformCall(\n `data_migration.backfill-${issue.table}-${issue.column}`,\n `Backfill NULLs in \"${issue.table}\".\"${issue.column}\" before NOT NULL tightening`,\n issue.table,\n issue.column,\n ),\n );\n }\n\n if (calls.length === 0) return { kind: 'no_match' };\n\n return {\n kind: 'match',\n issues,\n calls,\n recipe: true,\n };\n};\n\nexport const sqlitePlannerStrategies: readonly CallMigrationStrategy[] = [\n nullabilityTighteningBackfillStrategy,\n recreateTableStrategy,\n];\n","/**\n * SQLite migration issue planner.\n *\n * Takes schema issues (from `verifySqlSchema`) and emits migration IR\n * (`SqliteOpFactoryCall[]`). Strategies consume issues they recognize and\n * produce specialized call sequences (e.g. recreateTableStrategy absorbs\n * type/nullability/default/constraint mismatches into a single recreate op);\n * remaining issues flow through `mapIssueToCall` for the default case.\n */\n\nimport type { Contract } from '@prisma-next/contract/types';\nimport type {\n CodecControlHooks,\n MigrationOperationPolicy,\n SqlPlannerConflict,\n SqlPlannerConflictLocation,\n} from '@prisma-next/family-sql/control';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type { SchemaIssue } from '@prisma-next/framework-components/control';\nimport type {\n SqlStorage,\n StorageColumn,\n StorageTable,\n StorageTypeInstance,\n} from '@prisma-next/sql-contract/types';\nimport { defaultIndexName } from '@prisma-next/sql-schema-ir/naming';\nimport type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';\nimport type { Result } from '@prisma-next/utils/result';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport {\n AddColumnCall,\n CreateIndexCall,\n CreateTableCall,\n DropColumnCall,\n DropIndexCall,\n DropTableCall,\n type SqliteOpFactoryCall,\n} from './op-factory-call';\nimport type {\n SqliteColumnSpec,\n SqliteForeignKeySpec,\n SqliteTableSpec,\n SqliteUniqueSpec,\n} from './operations/shared';\nimport {\n buildColumnDefaultSql,\n buildColumnTypeSql,\n isInlineAutoincrementPrimaryKey,\n} from './planner-ddl-builders';\nimport {\n type CallMigrationStrategy,\n type StrategyContext,\n sqlitePlannerStrategies,\n} from './planner-strategies';\nimport { CONTROL_TABLE_NAMES } from './statement-builders';\n\nexport type { CallMigrationStrategy, StrategyContext };\n\n// ============================================================================\n// Issue kind ordering (dependency order)\n// ============================================================================\n\nconst ISSUE_KIND_ORDER: Record<string, number> = {\n // Drops (reconciliation — clear the way for creates)\n extra_foreign_key: 10,\n extra_unique_constraint: 11,\n extra_primary_key: 12,\n extra_index: 13,\n extra_default: 14,\n extra_column: 15,\n extra_table: 16,\n\n // Tables before columns\n missing_table: 20,\n\n // Columns before constraints\n missing_column: 30,\n\n // Reconciliation alters (on existing objects)\n type_mismatch: 40,\n nullability_mismatch: 41,\n default_missing: 42,\n default_mismatch: 43,\n\n // Constraints after columns exist\n primary_key_mismatch: 50,\n unique_constraint_mismatch: 51,\n index_mismatch: 52,\n foreign_key_mismatch: 60,\n};\n\nfunction issueOrder(issue: SchemaIssue): number {\n return ISSUE_KIND_ORDER[issue.kind] ?? 99;\n}\n\nfunction issueKey(issue: SchemaIssue): string {\n const table = 'table' in issue && typeof issue.table === 'string' ? issue.table : '';\n const column = 'column' in issue && typeof issue.column === 'string' ? issue.column : '';\n const name =\n 'indexOrConstraint' in issue && typeof issue.indexOrConstraint === 'string'\n ? issue.indexOrConstraint\n : '';\n return `${table}\\u0000${column}\\u0000${name}`;\n}\n\n// ============================================================================\n// Conflict helpers\n// ============================================================================\n\nfunction issueConflict(\n kind: SqlPlannerConflict['kind'],\n summary: string,\n location?: SqlPlannerConflict['location'],\n): SqlPlannerConflict {\n return {\n kind,\n summary,\n why: 'Use `migration new` to author a custom migration for this change.',\n ...(location ? { location } : {}),\n };\n}\n\nfunction conflictKindForIssue(issue: SchemaIssue): SqlPlannerConflict['kind'] {\n switch (issue.kind) {\n case 'type_mismatch':\n return 'typeMismatch';\n case 'nullability_mismatch':\n return 'nullabilityConflict';\n case 'primary_key_mismatch':\n case 'unique_constraint_mismatch':\n case 'index_mismatch':\n case 'extra_primary_key':\n case 'extra_unique_constraint':\n return 'indexIncompatible';\n case 'foreign_key_mismatch':\n case 'extra_foreign_key':\n return 'foreignKeyConflict';\n default:\n return 'missingButNonAdditive';\n }\n}\n\nfunction issueLocation(issue: SchemaIssue): SqlPlannerConflictLocation | undefined {\n if (issue.kind === 'enum_values_changed') return undefined;\n const location: {\n table?: string;\n column?: string;\n constraint?: string;\n } = {};\n if (issue.table) location.table = issue.table;\n if (issue.column) location.column = issue.column;\n if (issue.indexOrConstraint) location.constraint = issue.indexOrConstraint;\n return Object.keys(location).length > 0 ? (location as SqlPlannerConflictLocation) : undefined;\n}\n\nfunction conflictForDisallowedCall(\n call: SqliteOpFactoryCall,\n allowed: readonly string[],\n): SqlPlannerConflict {\n const summary = `Operation \"${call.label}\" requires class \"${call.operationClass}\", but policy allows only: ${allowed.join(', ')}`;\n const location = locationForCall(call);\n return {\n kind: conflictKindForCall(call),\n summary,\n why: 'Use `migration new` to author a custom migration for this change.',\n ...(location ? { location } : {}),\n };\n}\n\nfunction conflictKindForCall(call: SqliteOpFactoryCall): SqlPlannerConflict['kind'] {\n switch (call.factoryName) {\n case 'createIndex':\n case 'dropIndex':\n return 'indexIncompatible';\n default:\n return 'missingButNonAdditive';\n }\n}\n\nfunction locationForCall(call: SqliteOpFactoryCall): SqlPlannerConflictLocation | undefined {\n const location: { table?: string; column?: string; index?: string } = {};\n if ('tableName' in call) location.table = call.tableName;\n if ('columnName' in call) location.column = call.columnName;\n if ('indexName' in call) location.index = call.indexName;\n return Object.keys(location).length > 0 ? (location as SqlPlannerConflictLocation) : undefined;\n}\n\nfunction isMissing(issue: SchemaIssue): boolean {\n if (issue.kind === 'enum_values_changed') return false;\n return issue.actual === undefined;\n}\n\n// ============================================================================\n// StorageTable / StorageColumn → flat SqliteTableSpec\n// ============================================================================\n\n/**\n * Resolves codec / `typeRef` / default rendering into a flat\n * `SqliteColumnSpec`. Mirrors Postgres's `toColumnSpec`. Once a column is\n * flattened, downstream Calls and operation factories never see\n * `StorageColumn` again — they deal in pre-rendered SQL fragments.\n */\nexport function toColumnSpec(\n name: string,\n column: StorageColumn,\n storageTypes: Readonly<Record<string, StorageTypeInstance>>,\n inlineAutoincrementPrimaryKey = false,\n): SqliteColumnSpec {\n const typeSql = buildColumnTypeSql(column, storageTypes as Record<string, StorageTypeInstance>);\n const defaultSql = buildColumnDefaultSql(column.default);\n return {\n name,\n typeSql,\n defaultSql,\n nullable: column.nullable,\n ...(inlineAutoincrementPrimaryKey ? { inlineAutoincrementPrimaryKey: true } : {}),\n };\n}\n\n/**\n * Flattens a `StorageTable` into a `SqliteTableSpec` ready for\n * `CreateTableCall` / `RecreateTableCall`. Sole-column AUTOINCREMENT\n * primary keys are detected here and marked on the column spec so the\n * renderer emits `INTEGER PRIMARY KEY AUTOINCREMENT` inline.\n */\nexport function toTableSpec(\n table: StorageTable,\n storageTypes: Readonly<Record<string, StorageTypeInstance>>,\n): SqliteTableSpec {\n const columns: SqliteColumnSpec[] = Object.entries(table.columns).map(([name, column]) =>\n toColumnSpec(name, column, storageTypes, isInlineAutoincrementPrimaryKey(table, name)),\n );\n const uniques: SqliteUniqueSpec[] = table.uniques.map((u) => ({\n columns: u.columns,\n ...(u.name !== undefined ? { name: u.name } : {}),\n }));\n const foreignKeys: SqliteForeignKeySpec[] = table.foreignKeys.map((fk) => ({\n columns: fk.columns,\n references: { table: fk.references.table, columns: fk.references.columns },\n constraint: fk.constraint !== false,\n ...(fk.name !== undefined ? { name: fk.name } : {}),\n ...(fk.onDelete !== undefined ? { onDelete: fk.onDelete } : {}),\n ...(fk.onUpdate !== undefined ? { onUpdate: fk.onUpdate } : {}),\n }));\n return {\n columns,\n ...(table.primaryKey ? { primaryKey: { columns: table.primaryKey.columns } } : {}),\n uniques,\n foreignKeys,\n };\n}\n\n// ============================================================================\n// Issue planner\n// ============================================================================\n\nexport interface IssuePlannerOptions {\n readonly issues: readonly SchemaIssue[];\n readonly toContract: Contract<SqlStorage>;\n readonly fromContract: Contract<SqlStorage> | null;\n readonly codecHooks: ReadonlyMap<string, CodecControlHooks>;\n readonly storageTypes: Readonly<Record<string, StorageTypeInstance>>;\n readonly schema?: SqlSchemaIR;\n readonly policy?: MigrationOperationPolicy;\n readonly frameworkComponents?: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;\n readonly strategies?: readonly CallMigrationStrategy[];\n}\n\nexport interface IssuePlannerValue {\n readonly calls: readonly SqliteOpFactoryCall[];\n}\n\nconst DEFAULT_POLICY: MigrationOperationPolicy = {\n allowedOperationClasses: ['additive', 'widening', 'destructive', 'data'],\n};\n\nfunction emptySchemaIR(): SqlSchemaIR {\n return { tables: {}, dependencies: [] };\n}\n\n// ============================================================================\n// Issue → Call mapping (per-issue default path)\n// ============================================================================\n\nfunction mapIssueToCall(\n issue: SchemaIssue,\n ctx: StrategyContext,\n): Result<readonly SqliteOpFactoryCall[], SqlPlannerConflict> {\n switch (issue.kind) {\n case 'missing_table': {\n if (!issue.table) {\n return notOk(\n issueConflict('unsupportedOperation', 'Missing table issue has no table name'),\n );\n }\n const contractTable = ctx.toContract.storage.tables[issue.table];\n if (!contractTable) {\n return notOk(\n issueConflict(\n 'unsupportedOperation',\n `Table \"${issue.table}\" reported missing but not found in destination contract`,\n ),\n );\n }\n const tableSpec = toTableSpec(contractTable, ctx.storageTypes);\n const calls: SqliteOpFactoryCall[] = [new CreateTableCall(issue.table, tableSpec)];\n const declaredIndexColumnKeys = new Set<string>();\n for (const index of contractTable.indexes) {\n const indexName = index.name ?? defaultIndexName(issue.table, index.columns);\n declaredIndexColumnKeys.add(index.columns.join(','));\n calls.push(new CreateIndexCall(issue.table, indexName, index.columns));\n }\n for (const fk of contractTable.foreignKeys) {\n if (fk.index === false) continue;\n if (declaredIndexColumnKeys.has(fk.columns.join(','))) continue;\n const indexName = defaultIndexName(issue.table, fk.columns);\n calls.push(new CreateIndexCall(issue.table, indexName, fk.columns));\n }\n return ok(calls);\n }\n\n case 'missing_column': {\n if (!issue.table || !issue.column) {\n return notOk(\n issueConflict('unsupportedOperation', 'Missing column issue has no table/column name'),\n );\n }\n const column = ctx.toContract.storage.tables[issue.table]?.columns[issue.column];\n if (!column) {\n return notOk(\n issueConflict(\n 'unsupportedOperation',\n `Column \"${issue.table}\".\"${issue.column}\" not in destination contract`,\n ),\n );\n }\n const contractTable = ctx.toContract.storage.tables[issue.table];\n const columnSpec = toColumnSpec(\n issue.column,\n column,\n ctx.storageTypes,\n contractTable ? isInlineAutoincrementPrimaryKey(contractTable, issue.column) : false,\n );\n return ok([new AddColumnCall(issue.table, columnSpec)]);\n }\n\n case 'index_mismatch': {\n if (!issue.table) {\n return notOk(issueConflict('indexIncompatible', 'Index issue has no table name'));\n }\n if (!isMissing(issue) || !issue.expected) {\n return notOk(\n issueConflict(\n 'indexIncompatible',\n `Index on \"${issue.table}\" differs (expected: ${issue.expected}, actual: ${issue.actual})`,\n { table: issue.table },\n ),\n );\n }\n const columns = issue.expected.split(', ');\n const contractTable = ctx.toContract.storage.tables[issue.table];\n if (!contractTable) {\n return notOk(\n issueConflict(\n 'unsupportedOperation',\n `Table \"${issue.table}\" not found in destination contract`,\n ),\n );\n }\n // Use the explicit-index name if one is declared for these columns;\n // otherwise fall back to `defaultIndexName` (which is also what\n // `verifySqlSchema` synthesizes for FK-backing indexes). Whether the\n // missing index originates from `contractTable.indexes` or from an FK\n // with `index: true` doesn't change the emitted DDL.\n const explicitIndex = contractTable.indexes.find(\n (idx) => idx.columns.join(',') === columns.join(','),\n );\n const indexName = explicitIndex?.name ?? defaultIndexName(issue.table, columns);\n return ok([new CreateIndexCall(issue.table, indexName, columns)]);\n }\n\n case 'extra_table': {\n if (!issue.table) {\n return notOk(issueConflict('unsupportedOperation', 'Extra table issue has no table name'));\n }\n // Runner-owned control tables must never be dropped.\n if (CONTROL_TABLE_NAMES.has(issue.table)) return ok([]);\n return ok([new DropTableCall(issue.table)]);\n }\n\n case 'extra_column': {\n if (!issue.table || !issue.column) {\n return notOk(\n issueConflict('unsupportedOperation', 'Extra column issue has no table/column name'),\n );\n }\n return ok([new DropColumnCall(issue.table, issue.column)]);\n }\n\n case 'extra_index': {\n if (!issue.table || !issue.indexOrConstraint) {\n return notOk(\n issueConflict('unsupportedOperation', 'Extra index issue has no table/index name'),\n );\n }\n return ok([new DropIndexCall(issue.table, issue.indexOrConstraint)]);\n }\n\n // SQLite has no enum types (capability `sql.enums: false`). The verifier\n // should never emit `enum_values_changed` against a SQLite schema, so if\n // we receive one it is a verifier bug — surface it as an explicit\n // conflict rather than silently dropping it.\n case 'enum_values_changed':\n return notOk(\n issueConflict(\n 'unsupportedOperation',\n 'Received enum_values_changed against a SQLite schema (sql.enums: false) — verifier bug',\n ),\n );\n\n // Everything below is absorbed by recreateTableStrategy. If it falls\n // through here, policy or context didn't allow the recreate — surface as\n // a conflict.\n case 'type_mismatch':\n case 'nullability_mismatch':\n case 'default_mismatch':\n case 'default_missing':\n case 'extra_default':\n case 'primary_key_mismatch':\n case 'unique_constraint_mismatch':\n case 'foreign_key_mismatch':\n case 'extra_foreign_key':\n case 'extra_unique_constraint':\n case 'extra_primary_key':\n return notOk(issueConflict(conflictKindForIssue(issue), issue.message, issueLocation(issue)));\n\n default:\n return notOk(\n issueConflict(\n 'unsupportedOperation',\n `Unhandled issue kind: ${(issue as SchemaIssue).kind}`,\n ),\n );\n }\n}\n\n// ============================================================================\n// Call categorization for final emission order\n// ============================================================================\n\ntype CallCategory =\n | 'drop-column'\n | 'drop-index'\n | 'drop-table'\n | 'create-table'\n | 'add-column'\n | 'create-index';\n\nfunction classifyCall(call: SqliteOpFactoryCall): CallCategory | null {\n switch (call.factoryName) {\n case 'createTable':\n return 'create-table';\n case 'addColumn':\n return 'add-column';\n case 'createIndex':\n return 'create-index';\n case 'dropColumn':\n return 'drop-column';\n case 'dropIndex':\n return 'drop-index';\n case 'dropTable':\n return 'drop-table';\n // recreateTable goes into the recipe slot; return null for bucketable.\n case 'recreateTable':\n return null;\n default:\n return null;\n }\n}\n\n// ============================================================================\n// Top-level planIssues\n// ============================================================================\n\nexport function planIssues(\n options: IssuePlannerOptions,\n): Result<IssuePlannerValue, readonly SqlPlannerConflict[]> {\n const policyProvided = options.policy !== undefined;\n const policy = options.policy ?? DEFAULT_POLICY;\n const schema = options.schema ?? emptySchemaIR();\n const frameworkComponents = options.frameworkComponents ?? [];\n\n const context: StrategyContext = {\n toContract: options.toContract,\n fromContract: options.fromContract,\n codecHooks: options.codecHooks,\n storageTypes: options.storageTypes,\n schema,\n policy,\n frameworkComponents,\n };\n\n const strategies = options.strategies ?? sqlitePlannerStrategies;\n\n let remaining = options.issues;\n const recipeCalls: SqliteOpFactoryCall[] = [];\n const bucketableCalls: SqliteOpFactoryCall[] = [];\n\n for (const strategy of strategies) {\n const result = strategy(remaining, context);\n if (result.kind === 'match') {\n remaining = result.issues;\n if (result.recipe) {\n recipeCalls.push(...result.calls);\n } else {\n bucketableCalls.push(...result.calls);\n }\n }\n }\n\n const sorted = [...remaining].sort((a, b) => {\n const kindDelta = issueOrder(a) - issueOrder(b);\n if (kindDelta !== 0) return kindDelta;\n const keyA = issueKey(a);\n const keyB = issueKey(b);\n return keyA < keyB ? -1 : keyA > keyB ? 1 : 0;\n });\n\n const defaultCalls: SqliteOpFactoryCall[] = [];\n const conflicts: SqlPlannerConflict[] = [];\n\n for (const issue of sorted) {\n const result = mapIssueToCall(issue, context);\n if (result.ok) {\n defaultCalls.push(...result.value);\n } else {\n conflicts.push(result.failure);\n }\n }\n\n // Policy gating for recipe + bucketable. Default-mapped calls for disallowed\n // classes never get here (they're surfaced as per-issue conflicts above).\n const allowed = policy.allowedOperationClasses;\n let gatedRecipe = recipeCalls;\n let gatedBucketable = bucketableCalls;\n let gatedDefault = defaultCalls;\n if (policyProvided) {\n const sink = (acc: SqliteOpFactoryCall[]) => (call: SqliteOpFactoryCall) => {\n if (allowed.includes(call.operationClass)) {\n acc.push(call);\n return;\n }\n conflicts.push(conflictForDisallowedCall(call, allowed));\n };\n const gatedRecipeBucket: SqliteOpFactoryCall[] = [];\n const gatedBucketableBucket: SqliteOpFactoryCall[] = [];\n const gatedDefaultBucket: SqliteOpFactoryCall[] = [];\n recipeCalls.forEach(sink(gatedRecipeBucket));\n bucketableCalls.forEach(sink(gatedBucketableBucket));\n defaultCalls.forEach(sink(gatedDefaultBucket));\n gatedRecipe = gatedRecipeBucket;\n gatedBucketable = gatedBucketableBucket;\n gatedDefault = gatedDefaultBucket;\n }\n\n if (conflicts.length > 0) {\n return notOk(conflicts);\n }\n\n // Final emission order matches the current monolithic planner:\n // create-table → add-column → create-index → recreate → drop-column → drop-index → drop-table\n const combined = [...gatedDefault, ...gatedBucketable];\n const byCategory = (cat: CallCategory) => combined.filter((c) => classifyCall(c) === cat);\n\n const calls: SqliteOpFactoryCall[] = [\n ...byCategory('create-table'),\n ...byCategory('add-column'),\n ...byCategory('create-index'),\n ...gatedRecipe,\n ...byCategory('drop-column'),\n ...byCategory('drop-index'),\n ...byCategory('drop-table'),\n ];\n\n return ok({ calls });\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type {\n MigrationOperationPolicy,\n SqlMigrationPlanner,\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 MigrationScaffoldContext,\n SchemaIssue,\n} from '@prisma-next/framework-components/control';\nimport { parseSqliteDefault } from '../default-normalizer';\nimport { normalizeSqliteNativeType } from '../native-type-normalizer';\nimport { planIssues } from './issue-planner';\nimport {\n type SqliteMigrationDestinationInfo,\n TypeScriptRenderableSqliteMigration,\n} from './planner-produced-sqlite-migration';\nimport { sqlitePlannerStrategies } from './planner-strategies';\nimport type { SqlitePlanTargetDetails } from './planner-target-details';\n\nexport function createSqliteMigrationPlanner(): SqliteMigrationPlanner {\n return new SqliteMigrationPlanner();\n}\n\nexport type SqlitePlanResult =\n | { readonly kind: 'success'; readonly plan: TypeScriptRenderableSqliteMigration }\n | SqlPlannerFailureResult;\n\n/**\n * SQLite 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 registered\n * strategies. Strategies absorb groups of related issues into composite\n * recipes (e.g. recreating a table to apply type/nullability/default/\n * constraint changes at once); anything not absorbed by a strategy flows\n * through `mapIssueToCall` in the issue planner as a one-off call.\n *\n * FK-backing indexes are surfaced by `verifySqlSchema`'s index expansion\n * (see `verify-sql-schema.ts:459-469`), so `mapIssueToCall` handles them\n * uniformly alongside user-declared indexes.\n */\nexport class SqliteMigrationPlanner\n implements SqlMigrationPlanner<SqlitePlanTargetDetails>, MigrationPlanner<'sql', 'sqlite'>\n{\n plan(options: {\n readonly contract: unknown;\n readonly schema: unknown;\n readonly policy: MigrationOperationPolicy;\n /**\n * The \"from\" contract (state the planner assumes the database starts at),\n * or `null` for reconciliation flows.\n *\n * Typed as the framework `Contract | null` to satisfy the\n * `MigrationPlanner` interface contract; `planSql` narrows to the SQL\n * shape via `SqlMigrationPlannerPlanOptions`. Used to populate\n * `describe().from` on the produced plan as\n * `fromContract?.storage.storageHash ?? null`.\n */\n readonly fromContract: Contract | null;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;\n }): SqlitePlanResult {\n return this.planSql(options as SqlMigrationPlannerPlanOptions);\n }\n\n emptyMigration(context: MigrationScaffoldContext): TypeScriptRenderableSqliteMigration {\n return new TypeScriptRenderableSqliteMigration([], {\n from: context.fromHash,\n to: context.toHash,\n });\n }\n\n private planSql(options: SqlMigrationPlannerPlanOptions): SqlitePlanResult {\n const policyResult = this.ensureAdditivePolicy(options.policy);\n if (policyResult) return policyResult;\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: options.fromContract,\n codecHooks,\n storageTypes,\n schema: options.schema,\n policy: options.policy,\n frameworkComponents: options.frameworkComponents,\n strategies: sqlitePlannerStrategies,\n });\n\n if (!result.ok) {\n return plannerFailure(result.failure);\n }\n\n const destination: SqliteMigrationDestinationInfo = {\n storageHash: options.contract.storage.storageHash,\n ...(options.contract.profileHash !== undefined\n ? { profileHash: options.contract.profileHash }\n : {}),\n };\n\n return {\n kind: 'success' as const,\n plan: new TypeScriptRenderableSqliteMigration(\n result.value.calls,\n {\n from: options.fromContract?.storage.storageHash ?? null,\n to: options.contract.storage.storageHash,\n },\n destination,\n ),\n };\n }\n\n private ensureAdditivePolicy(policy: MigrationOperationPolicy): SqlPlannerFailureResult | null {\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: SqlMigrationPlannerPlanOptions): readonly SchemaIssue[] {\n const allowed = options.policy.allowedOperationClasses;\n const strict = allowed.includes('widening') || allowed.includes('destructive');\n const verifyResult = verifySqlSchema({\n contract: options.contract,\n schema: options.schema,\n strict,\n typeMetadataRegistry: new Map(),\n frameworkComponents: options.frameworkComponents,\n normalizeDefault: parseSqliteDefault,\n normalizeNativeType: normalizeSqliteNativeType,\n });\n return verifyResult.schema.issues;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"planner-DmOFOhoq.mjs","names":["recreateTableStrategy: CallMigrationStrategy","calls: SqliteOpFactoryCall[]","operationClass: MigrationOperationClass","indexes: SqliteIndexSpec[]","nullabilityTighteningBackfillStrategy: CallMigrationStrategy","sqlitePlannerStrategies: readonly CallMigrationStrategy[]","ISSUE_KIND_ORDER: Record<string, number>","location: {\n table?: string;\n column?: string;\n constraint?: string;\n }","location: { table?: string; column?: string; index?: string }","columns: SqliteColumnSpec[]","uniques: SqliteUniqueSpec[]","foreignKeys: SqliteForeignKeySpec[]","DEFAULT_POLICY: MigrationOperationPolicy","calls: SqliteOpFactoryCall[]","context: StrategyContext","recipeCalls: SqliteOpFactoryCall[]","bucketableCalls: SqliteOpFactoryCall[]","defaultCalls: SqliteOpFactoryCall[]","conflicts: SqlPlannerConflict[]","gatedRecipeBucket: SqliteOpFactoryCall[]","gatedBucketableBucket: SqliteOpFactoryCall[]","gatedDefaultBucket: SqliteOpFactoryCall[]","destination: SqliteMigrationDestinationInfo"],"sources":["../src/core/migrations/planner-strategies.ts","../src/core/migrations/issue-planner.ts","../src/core/migrations/planner.ts"],"sourcesContent":["/**\n * SQLite migration strategies.\n *\n * Each strategy examines the issue list, consumes issues it handles, and\n * returns the `SqliteOpFactoryCall[]` to address them. The issue planner\n * runs each strategy in order and routes whatever's left through\n * `mapIssueToCall`.\n *\n * SQLite has no enums, no data-safe backfill, and no component-declared\n * database dependencies. The only recipe that needs strategy-level\n * multi-issue consumption is `recreateTable` (added in a later phase), which\n * absorbs type/nullability/default/constraint mismatches for a given table\n * into a single recreate operation.\n */\n\nimport type { Contract } from '@prisma-next/contract/types';\nimport type {\n CodecControlHooks,\n MigrationOperationClass,\n MigrationOperationPolicy,\n} from '@prisma-next/family-sql/control';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type { SchemaIssue } from '@prisma-next/framework-components/control';\nimport type { SqlStorage, StorageTypeInstance } from '@prisma-next/sql-contract/types';\nimport { defaultIndexName } from '@prisma-next/sql-schema-ir/naming';\nimport type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';\nimport { toTableSpec } from './issue-planner';\nimport { DataTransformCall, RecreateTableCall, type SqliteOpFactoryCall } from './op-factory-call';\nimport type { SqliteIndexSpec } from './operations/shared';\nimport { buildRecreatePostchecks, buildRecreateSummary } from './operations/tables';\n\nexport interface StrategyContext {\n readonly toContract: Contract<SqlStorage>;\n readonly fromContract: Contract<SqlStorage> | null;\n readonly codecHooks: ReadonlyMap<string, CodecControlHooks>;\n readonly storageTypes: Readonly<Record<string, StorageTypeInstance>>;\n readonly schema: SqlSchemaIR;\n readonly policy: MigrationOperationPolicy;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;\n}\n\nexport type CallMigrationStrategy = (\n issues: readonly SchemaIssue[],\n context: StrategyContext,\n) =>\n | {\n kind: 'match';\n issues: readonly SchemaIssue[];\n calls: readonly SqliteOpFactoryCall[];\n recipe?: boolean;\n }\n | { kind: 'no_match' };\n\n// ============================================================================\n// Recreate-table strategy\n// ============================================================================\n\nconst WIDENING_ISSUE_KINDS = new Set<SchemaIssue['kind']>(['default_mismatch', 'default_missing']);\n\nconst DESTRUCTIVE_ISSUE_KINDS = new Set<SchemaIssue['kind']>([\n 'extra_default',\n 'type_mismatch',\n 'primary_key_mismatch',\n 'foreign_key_mismatch',\n 'unique_constraint_mismatch',\n 'extra_foreign_key',\n 'extra_unique_constraint',\n 'extra_primary_key',\n]);\n\nfunction classifyIssue(issue: SchemaIssue): 'widening' | 'destructive' | null {\n if (issue.kind === 'enum_values_changed') return null;\n if (!issue.table) return null;\n if (issue.kind === 'nullability_mismatch') {\n // Relaxing (NOT NULL → nullable) is widening; tightening is destructive.\n return issue.expected === 'true' ? 'widening' : 'destructive';\n }\n if (WIDENING_ISSUE_KINDS.has(issue.kind)) return 'widening';\n if (DESTRUCTIVE_ISSUE_KINDS.has(issue.kind)) return 'destructive';\n return null;\n}\n\n/**\n * Groups recreate-eligible issues by table, decides per-table operation class\n * (destructive wins over widening), and emits one `RecreateTableCall` per\n * table. Returns unchanged-or-smaller issue list — issues the strategy\n * consumed are removed so `mapIssueToCall` doesn't double-handle them.\n */\nexport const recreateTableStrategy: CallMigrationStrategy = (issues, ctx) => {\n const byTable = new Map<string, { issues: SchemaIssue[]; hasDestructive: boolean }>();\n const consumed = new Set<SchemaIssue>();\n\n for (const issue of issues) {\n const cls = classifyIssue(issue);\n if (!cls) continue;\n if (issue.kind === 'enum_values_changed') continue;\n if (!issue.table) continue;\n const table = issue.table;\n const entry = byTable.get(table);\n if (entry) {\n entry.issues.push(issue);\n if (cls === 'destructive') entry.hasDestructive = true;\n } else {\n byTable.set(table, { issues: [issue], hasDestructive: cls === 'destructive' });\n }\n consumed.add(issue);\n }\n\n if (byTable.size === 0) return { kind: 'no_match' };\n\n const calls: SqliteOpFactoryCall[] = [];\n for (const [tableName, entry] of byTable) {\n const contractTable = ctx.toContract.storage.tables[tableName];\n const schemaTable = ctx.schema.tables[tableName];\n if (!contractTable || !schemaTable) continue;\n const operationClass: MigrationOperationClass = entry.hasDestructive\n ? 'destructive'\n : 'widening';\n\n // Flatten the contract table to a self-contained spec — the Call holds\n // pre-rendered SQL fragments only, no `StorageColumn` or `storageTypes`.\n const tableSpec = toTableSpec(contractTable, ctx.storageTypes);\n\n const seenIndexColumnKeys = new Set<string>();\n const indexes: SqliteIndexSpec[] = [];\n for (const idx of contractTable.indexes) {\n const key = idx.columns.join(',');\n if (seenIndexColumnKeys.has(key)) continue;\n seenIndexColumnKeys.add(key);\n indexes.push({\n name: idx.name ?? defaultIndexName(tableName, idx.columns),\n columns: idx.columns,\n });\n }\n for (const fk of contractTable.foreignKeys) {\n if (fk.index === false) continue;\n const key = fk.columns.join(',');\n if (seenIndexColumnKeys.has(key)) continue;\n seenIndexColumnKeys.add(key);\n indexes.push({\n name: defaultIndexName(tableName, fk.columns),\n columns: fk.columns,\n });\n }\n\n calls.push(\n new RecreateTableCall({\n tableName,\n contractTable: tableSpec,\n schemaColumnNames: Object.keys(schemaTable.columns),\n indexes,\n summary: buildRecreateSummary(tableName, entry.issues),\n postchecks: buildRecreatePostchecks(tableName, entry.issues, tableSpec),\n operationClass,\n }),\n );\n }\n\n return {\n kind: 'match',\n issues: issues.filter((i) => !consumed.has(i)),\n calls,\n recipe: true,\n };\n};\n\n// ============================================================================\n// Nullability-tightening backfill strategy\n// ============================================================================\n\n/**\n * When the policy allows `'data'` and the contract tightens one or more\n * columns from nullable to NOT NULL, emit a `DataTransformCall` stub per\n * tightened column. The user fills the backfill `UPDATE` in the rendered\n * `migration.ts` before the subsequent `RecreateTableCall` copies data into\n * the tightened schema (whose `INSERT INTO temp SELECT … FROM old` would\n * otherwise fail at runtime if any `NULL`s remain).\n *\n * Does NOT consume the tightening issue — `recreateTableStrategy` still\n * needs it to produce the actual recreate that enforces the NOT NULL at\n * the schema level. The backfill op and the recreate op end up in the\n * recipe slot in strategy order (backfill first, recreate second), which\n * matches the required execution order.\n *\n * Mirrors Postgres's `nullableTighteningCallStrategy` / `'data'`-class\n * gating. When `'data'` is not in the policy (the default `db update` /\n * `db init` path), the strategy short-circuits and the recreate alone\n * runs with its current destructive-class gating — preserving today's\n * behavior where a tightening blows up at runtime if NULLs are present.\n */\nexport const nullabilityTighteningBackfillStrategy: CallMigrationStrategy = (issues, ctx) => {\n if (!ctx.policy.allowedOperationClasses.includes('data')) {\n return { kind: 'no_match' };\n }\n\n const calls: SqliteOpFactoryCall[] = [];\n for (const issue of issues) {\n if (issue.kind !== 'nullability_mismatch') continue;\n if (!issue.table || !issue.column) continue;\n // Tightening only: `expected === 'true'` means the contract wants the\n // column nullable (relaxing from NOT NULL → nullable), which is safe and\n // needs no backfill.\n if (issue.expected === 'true') continue;\n\n const column = ctx.toContract.storage.tables[issue.table]?.columns[issue.column];\n if (!column || column.nullable === true) continue;\n\n calls.push(\n new DataTransformCall(\n `data_migration.backfill-${issue.table}-${issue.column}`,\n `Backfill NULLs in \"${issue.table}\".\"${issue.column}\" before NOT NULL tightening`,\n issue.table,\n issue.column,\n ),\n );\n }\n\n if (calls.length === 0) return { kind: 'no_match' };\n\n return {\n kind: 'match',\n issues,\n calls,\n recipe: true,\n };\n};\n\nexport const sqlitePlannerStrategies: readonly CallMigrationStrategy[] = [\n nullabilityTighteningBackfillStrategy,\n recreateTableStrategy,\n];\n","/**\n * SQLite migration issue planner.\n *\n * Takes schema issues (from `verifySqlSchema`) and emits migration IR\n * (`SqliteOpFactoryCall[]`). Strategies consume issues they recognize and\n * produce specialized call sequences (e.g. recreateTableStrategy absorbs\n * type/nullability/default/constraint mismatches into a single recreate op);\n * remaining issues flow through `mapIssueToCall` for the default case.\n */\n\nimport type { Contract } from '@prisma-next/contract/types';\nimport type {\n CodecControlHooks,\n MigrationOperationPolicy,\n SqlPlannerConflict,\n SqlPlannerConflictLocation,\n} from '@prisma-next/family-sql/control';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type { SchemaIssue } from '@prisma-next/framework-components/control';\nimport type {\n SqlStorage,\n StorageColumn,\n StorageTable,\n StorageTypeInstance,\n} from '@prisma-next/sql-contract/types';\nimport { defaultIndexName } from '@prisma-next/sql-schema-ir/naming';\nimport type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';\nimport type { Result } from '@prisma-next/utils/result';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport {\n AddColumnCall,\n CreateIndexCall,\n CreateTableCall,\n DropColumnCall,\n DropIndexCall,\n DropTableCall,\n type SqliteOpFactoryCall,\n} from './op-factory-call';\nimport type {\n SqliteColumnSpec,\n SqliteForeignKeySpec,\n SqliteTableSpec,\n SqliteUniqueSpec,\n} from './operations/shared';\nimport {\n buildColumnDefaultSql,\n buildColumnTypeSql,\n isInlineAutoincrementPrimaryKey,\n} from './planner-ddl-builders';\nimport {\n type CallMigrationStrategy,\n type StrategyContext,\n sqlitePlannerStrategies,\n} from './planner-strategies';\nimport { CONTROL_TABLE_NAMES } from './statement-builders';\n\nexport type { CallMigrationStrategy, StrategyContext };\n\n// ============================================================================\n// Issue kind ordering (dependency order)\n// ============================================================================\n\nconst ISSUE_KIND_ORDER: Record<string, number> = {\n // Drops (reconciliation — clear the way for creates)\n extra_foreign_key: 10,\n extra_unique_constraint: 11,\n extra_primary_key: 12,\n extra_index: 13,\n extra_default: 14,\n extra_column: 15,\n extra_table: 16,\n\n // Tables before columns\n missing_table: 20,\n\n // Columns before constraints\n missing_column: 30,\n\n // Reconciliation alters (on existing objects)\n type_mismatch: 40,\n nullability_mismatch: 41,\n default_missing: 42,\n default_mismatch: 43,\n\n // Constraints after columns exist\n primary_key_mismatch: 50,\n unique_constraint_mismatch: 51,\n index_mismatch: 52,\n foreign_key_mismatch: 60,\n};\n\nfunction issueOrder(issue: SchemaIssue): number {\n return ISSUE_KIND_ORDER[issue.kind] ?? 99;\n}\n\nfunction issueKey(issue: SchemaIssue): string {\n const table = 'table' in issue && typeof issue.table === 'string' ? issue.table : '';\n const column = 'column' in issue && typeof issue.column === 'string' ? issue.column : '';\n const name =\n 'indexOrConstraint' in issue && typeof issue.indexOrConstraint === 'string'\n ? issue.indexOrConstraint\n : '';\n return `${table}\\u0000${column}\\u0000${name}`;\n}\n\n// ============================================================================\n// Conflict helpers\n// ============================================================================\n\nfunction issueConflict(\n kind: SqlPlannerConflict['kind'],\n summary: string,\n location?: SqlPlannerConflict['location'],\n): SqlPlannerConflict {\n return {\n kind,\n summary,\n why: 'Use `migration new` to author a custom migration for this change.',\n ...(location ? { location } : {}),\n };\n}\n\nfunction conflictKindForIssue(issue: SchemaIssue): SqlPlannerConflict['kind'] {\n switch (issue.kind) {\n case 'type_mismatch':\n return 'typeMismatch';\n case 'nullability_mismatch':\n return 'nullabilityConflict';\n case 'primary_key_mismatch':\n case 'unique_constraint_mismatch':\n case 'index_mismatch':\n case 'extra_primary_key':\n case 'extra_unique_constraint':\n return 'indexIncompatible';\n case 'foreign_key_mismatch':\n case 'extra_foreign_key':\n return 'foreignKeyConflict';\n default:\n return 'missingButNonAdditive';\n }\n}\n\nfunction issueLocation(issue: SchemaIssue): SqlPlannerConflictLocation | undefined {\n if (issue.kind === 'enum_values_changed') return undefined;\n const location: {\n table?: string;\n column?: string;\n constraint?: string;\n } = {};\n if (issue.table) location.table = issue.table;\n if (issue.column) location.column = issue.column;\n if (issue.indexOrConstraint) location.constraint = issue.indexOrConstraint;\n return Object.keys(location).length > 0 ? (location as SqlPlannerConflictLocation) : undefined;\n}\n\nfunction conflictForDisallowedCall(\n call: SqliteOpFactoryCall,\n allowed: readonly string[],\n): SqlPlannerConflict {\n const summary = `Operation \"${call.label}\" requires class \"${call.operationClass}\", but policy allows only: ${allowed.join(', ')}`;\n const location = locationForCall(call);\n return {\n kind: conflictKindForCall(call),\n summary,\n why: 'Use `migration new` to author a custom migration for this change.',\n ...(location ? { location } : {}),\n };\n}\n\nfunction conflictKindForCall(call: SqliteOpFactoryCall): SqlPlannerConflict['kind'] {\n switch (call.factoryName) {\n case 'createIndex':\n case 'dropIndex':\n return 'indexIncompatible';\n default:\n return 'missingButNonAdditive';\n }\n}\n\nfunction locationForCall(call: SqliteOpFactoryCall): SqlPlannerConflictLocation | undefined {\n const location: { table?: string; column?: string; index?: string } = {};\n if ('tableName' in call) location.table = call.tableName;\n if ('columnName' in call) location.column = call.columnName;\n if ('indexName' in call) location.index = call.indexName;\n return Object.keys(location).length > 0 ? (location as SqlPlannerConflictLocation) : undefined;\n}\n\nfunction isMissing(issue: SchemaIssue): boolean {\n if (issue.kind === 'enum_values_changed') return false;\n return issue.actual === undefined;\n}\n\n// ============================================================================\n// StorageTable / StorageColumn → flat SqliteTableSpec\n// ============================================================================\n\n/**\n * Resolves codec / `typeRef` / default rendering into a flat\n * `SqliteColumnSpec`. Mirrors Postgres's `toColumnSpec`. Once a column is\n * flattened, downstream Calls and operation factories never see\n * `StorageColumn` again — they deal in pre-rendered SQL fragments.\n */\nexport function toColumnSpec(\n name: string,\n column: StorageColumn,\n storageTypes: Readonly<Record<string, StorageTypeInstance>>,\n inlineAutoincrementPrimaryKey = false,\n): SqliteColumnSpec {\n const typeSql = buildColumnTypeSql(column, storageTypes as Record<string, StorageTypeInstance>);\n const defaultSql = buildColumnDefaultSql(column.default);\n return {\n name,\n typeSql,\n defaultSql,\n nullable: column.nullable,\n ...(inlineAutoincrementPrimaryKey ? { inlineAutoincrementPrimaryKey: true } : {}),\n };\n}\n\n/**\n * Flattens a `StorageTable` into a `SqliteTableSpec` ready for\n * `CreateTableCall` / `RecreateTableCall`. Sole-column AUTOINCREMENT\n * primary keys are detected here and marked on the column spec so the\n * renderer emits `INTEGER PRIMARY KEY AUTOINCREMENT` inline.\n */\nexport function toTableSpec(\n table: StorageTable,\n storageTypes: Readonly<Record<string, StorageTypeInstance>>,\n): SqliteTableSpec {\n const columns: SqliteColumnSpec[] = Object.entries(table.columns).map(([name, column]) =>\n toColumnSpec(name, column, storageTypes, isInlineAutoincrementPrimaryKey(table, name)),\n );\n const uniques: SqliteUniqueSpec[] = table.uniques.map((u) => ({\n columns: u.columns,\n ...(u.name !== undefined ? { name: u.name } : {}),\n }));\n const foreignKeys: SqliteForeignKeySpec[] = table.foreignKeys.map((fk) => ({\n columns: fk.columns,\n references: { table: fk.references.table, columns: fk.references.columns },\n constraint: fk.constraint !== false,\n ...(fk.name !== undefined ? { name: fk.name } : {}),\n ...(fk.onDelete !== undefined ? { onDelete: fk.onDelete } : {}),\n ...(fk.onUpdate !== undefined ? { onUpdate: fk.onUpdate } : {}),\n }));\n return {\n columns,\n ...(table.primaryKey ? { primaryKey: { columns: table.primaryKey.columns } } : {}),\n uniques,\n foreignKeys,\n };\n}\n\n// ============================================================================\n// Issue planner\n// ============================================================================\n\nexport interface IssuePlannerOptions {\n readonly issues: readonly SchemaIssue[];\n readonly toContract: Contract<SqlStorage>;\n readonly fromContract: Contract<SqlStorage> | null;\n readonly codecHooks: ReadonlyMap<string, CodecControlHooks>;\n readonly storageTypes: Readonly<Record<string, StorageTypeInstance>>;\n readonly schema?: SqlSchemaIR;\n readonly policy?: MigrationOperationPolicy;\n readonly frameworkComponents?: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;\n readonly strategies?: readonly CallMigrationStrategy[];\n}\n\nexport interface IssuePlannerValue {\n readonly calls: readonly SqliteOpFactoryCall[];\n}\n\nconst DEFAULT_POLICY: MigrationOperationPolicy = {\n allowedOperationClasses: ['additive', 'widening', 'destructive', 'data'],\n};\n\nfunction emptySchemaIR(): SqlSchemaIR {\n return { tables: {}, dependencies: [] };\n}\n\n// ============================================================================\n// Issue → Call mapping (per-issue default path)\n// ============================================================================\n\nfunction mapIssueToCall(\n issue: SchemaIssue,\n ctx: StrategyContext,\n): Result<readonly SqliteOpFactoryCall[], SqlPlannerConflict> {\n switch (issue.kind) {\n case 'missing_table': {\n if (!issue.table) {\n return notOk(\n issueConflict('unsupportedOperation', 'Missing table issue has no table name'),\n );\n }\n const contractTable = ctx.toContract.storage.tables[issue.table];\n if (!contractTable) {\n return notOk(\n issueConflict(\n 'unsupportedOperation',\n `Table \"${issue.table}\" reported missing but not found in destination contract`,\n ),\n );\n }\n const tableSpec = toTableSpec(contractTable, ctx.storageTypes);\n const calls: SqliteOpFactoryCall[] = [new CreateTableCall(issue.table, tableSpec)];\n const declaredIndexColumnKeys = new Set<string>();\n for (const index of contractTable.indexes) {\n const indexName = index.name ?? defaultIndexName(issue.table, index.columns);\n declaredIndexColumnKeys.add(index.columns.join(','));\n calls.push(new CreateIndexCall(issue.table, indexName, index.columns));\n }\n for (const fk of contractTable.foreignKeys) {\n if (fk.index === false) continue;\n if (declaredIndexColumnKeys.has(fk.columns.join(','))) continue;\n const indexName = defaultIndexName(issue.table, fk.columns);\n calls.push(new CreateIndexCall(issue.table, indexName, fk.columns));\n }\n return ok(calls);\n }\n\n case 'missing_column': {\n if (!issue.table || !issue.column) {\n return notOk(\n issueConflict('unsupportedOperation', 'Missing column issue has no table/column name'),\n );\n }\n const column = ctx.toContract.storage.tables[issue.table]?.columns[issue.column];\n if (!column) {\n return notOk(\n issueConflict(\n 'unsupportedOperation',\n `Column \"${issue.table}\".\"${issue.column}\" not in destination contract`,\n ),\n );\n }\n const contractTable = ctx.toContract.storage.tables[issue.table];\n const columnSpec = toColumnSpec(\n issue.column,\n column,\n ctx.storageTypes,\n contractTable ? isInlineAutoincrementPrimaryKey(contractTable, issue.column) : false,\n );\n return ok([new AddColumnCall(issue.table, columnSpec)]);\n }\n\n case 'index_mismatch': {\n if (!issue.table) {\n return notOk(issueConflict('indexIncompatible', 'Index issue has no table name'));\n }\n if (!isMissing(issue) || !issue.expected) {\n return notOk(\n issueConflict(\n 'indexIncompatible',\n `Index on \"${issue.table}\" differs (expected: ${issue.expected}, actual: ${issue.actual})`,\n { table: issue.table },\n ),\n );\n }\n const columns = issue.expected.split(', ');\n const contractTable = ctx.toContract.storage.tables[issue.table];\n if (!contractTable) {\n return notOk(\n issueConflict(\n 'unsupportedOperation',\n `Table \"${issue.table}\" not found in destination contract`,\n ),\n );\n }\n // Use the explicit-index name if one is declared for these columns;\n // otherwise fall back to `defaultIndexName` (which is also what\n // `verifySqlSchema` synthesizes for FK-backing indexes). Whether the\n // missing index originates from `contractTable.indexes` or from an FK\n // with `index: true` doesn't change the emitted DDL.\n const explicitIndex = contractTable.indexes.find(\n (idx) => idx.columns.join(',') === columns.join(','),\n );\n const indexName = explicitIndex?.name ?? defaultIndexName(issue.table, columns);\n return ok([new CreateIndexCall(issue.table, indexName, columns)]);\n }\n\n case 'extra_table': {\n if (!issue.table) {\n return notOk(issueConflict('unsupportedOperation', 'Extra table issue has no table name'));\n }\n // Runner-owned control tables must never be dropped.\n if (CONTROL_TABLE_NAMES.has(issue.table)) return ok([]);\n return ok([new DropTableCall(issue.table)]);\n }\n\n case 'extra_column': {\n if (!issue.table || !issue.column) {\n return notOk(\n issueConflict('unsupportedOperation', 'Extra column issue has no table/column name'),\n );\n }\n return ok([new DropColumnCall(issue.table, issue.column)]);\n }\n\n case 'extra_index': {\n if (!issue.table || !issue.indexOrConstraint) {\n return notOk(\n issueConflict('unsupportedOperation', 'Extra index issue has no table/index name'),\n );\n }\n return ok([new DropIndexCall(issue.table, issue.indexOrConstraint)]);\n }\n\n // SQLite has no enum types (capability `sql.enums: false`). The verifier\n // should never emit `enum_values_changed` against a SQLite schema, so if\n // we receive one it is a verifier bug — surface it as an explicit\n // conflict rather than silently dropping it.\n case 'enum_values_changed':\n return notOk(\n issueConflict(\n 'unsupportedOperation',\n 'Received enum_values_changed against a SQLite schema (sql.enums: false) — verifier bug',\n ),\n );\n\n // Everything below is absorbed by recreateTableStrategy. If it falls\n // through here, policy or context didn't allow the recreate — surface as\n // a conflict.\n case 'type_mismatch':\n case 'nullability_mismatch':\n case 'default_mismatch':\n case 'default_missing':\n case 'extra_default':\n case 'primary_key_mismatch':\n case 'unique_constraint_mismatch':\n case 'foreign_key_mismatch':\n case 'extra_foreign_key':\n case 'extra_unique_constraint':\n case 'extra_primary_key':\n return notOk(issueConflict(conflictKindForIssue(issue), issue.message, issueLocation(issue)));\n\n default:\n return notOk(\n issueConflict(\n 'unsupportedOperation',\n `Unhandled issue kind: ${(issue as SchemaIssue).kind}`,\n ),\n );\n }\n}\n\n// ============================================================================\n// Call categorization for final emission order\n// ============================================================================\n\ntype CallCategory =\n | 'drop-column'\n | 'drop-index'\n | 'drop-table'\n | 'create-table'\n | 'add-column'\n | 'create-index';\n\nfunction classifyCall(call: SqliteOpFactoryCall): CallCategory | null {\n switch (call.factoryName) {\n case 'createTable':\n return 'create-table';\n case 'addColumn':\n return 'add-column';\n case 'createIndex':\n return 'create-index';\n case 'dropColumn':\n return 'drop-column';\n case 'dropIndex':\n return 'drop-index';\n case 'dropTable':\n return 'drop-table';\n // recreateTable goes into the recipe slot; return null for bucketable.\n case 'recreateTable':\n return null;\n default:\n return null;\n }\n}\n\n// ============================================================================\n// Top-level planIssues\n// ============================================================================\n\nexport function planIssues(\n options: IssuePlannerOptions,\n): Result<IssuePlannerValue, readonly SqlPlannerConflict[]> {\n const policyProvided = options.policy !== undefined;\n const policy = options.policy ?? DEFAULT_POLICY;\n const schema = options.schema ?? emptySchemaIR();\n const frameworkComponents = options.frameworkComponents ?? [];\n\n const context: StrategyContext = {\n toContract: options.toContract,\n fromContract: options.fromContract,\n codecHooks: options.codecHooks,\n storageTypes: options.storageTypes,\n schema,\n policy,\n frameworkComponents,\n };\n\n const strategies = options.strategies ?? sqlitePlannerStrategies;\n\n let remaining = options.issues;\n const recipeCalls: SqliteOpFactoryCall[] = [];\n const bucketableCalls: SqliteOpFactoryCall[] = [];\n\n for (const strategy of strategies) {\n const result = strategy(remaining, context);\n if (result.kind === 'match') {\n remaining = result.issues;\n if (result.recipe) {\n recipeCalls.push(...result.calls);\n } else {\n bucketableCalls.push(...result.calls);\n }\n }\n }\n\n const sorted = [...remaining].sort((a, b) => {\n const kindDelta = issueOrder(a) - issueOrder(b);\n if (kindDelta !== 0) return kindDelta;\n const keyA = issueKey(a);\n const keyB = issueKey(b);\n return keyA < keyB ? -1 : keyA > keyB ? 1 : 0;\n });\n\n const defaultCalls: SqliteOpFactoryCall[] = [];\n const conflicts: SqlPlannerConflict[] = [];\n\n for (const issue of sorted) {\n const result = mapIssueToCall(issue, context);\n if (result.ok) {\n defaultCalls.push(...result.value);\n } else {\n conflicts.push(result.failure);\n }\n }\n\n // Policy gating for recipe + bucketable. Default-mapped calls for disallowed\n // classes never get here (they're surfaced as per-issue conflicts above).\n const allowed = policy.allowedOperationClasses;\n let gatedRecipe = recipeCalls;\n let gatedBucketable = bucketableCalls;\n let gatedDefault = defaultCalls;\n if (policyProvided) {\n const sink = (acc: SqliteOpFactoryCall[]) => (call: SqliteOpFactoryCall) => {\n if (allowed.includes(call.operationClass)) {\n acc.push(call);\n return;\n }\n conflicts.push(conflictForDisallowedCall(call, allowed));\n };\n const gatedRecipeBucket: SqliteOpFactoryCall[] = [];\n const gatedBucketableBucket: SqliteOpFactoryCall[] = [];\n const gatedDefaultBucket: SqliteOpFactoryCall[] = [];\n recipeCalls.forEach(sink(gatedRecipeBucket));\n bucketableCalls.forEach(sink(gatedBucketableBucket));\n defaultCalls.forEach(sink(gatedDefaultBucket));\n gatedRecipe = gatedRecipeBucket;\n gatedBucketable = gatedBucketableBucket;\n gatedDefault = gatedDefaultBucket;\n }\n\n if (conflicts.length > 0) {\n return notOk(conflicts);\n }\n\n // Final emission order matches the current monolithic planner:\n // create-table → add-column → create-index → recreate → drop-column → drop-index → drop-table\n const combined = [...gatedDefault, ...gatedBucketable];\n const byCategory = (cat: CallCategory) => combined.filter((c) => classifyCall(c) === cat);\n\n const calls: SqliteOpFactoryCall[] = [\n ...byCategory('create-table'),\n ...byCategory('add-column'),\n ...byCategory('create-index'),\n ...gatedRecipe,\n ...byCategory('drop-column'),\n ...byCategory('drop-index'),\n ...byCategory('drop-table'),\n ];\n\n return ok({ calls });\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type {\n MigrationOperationPolicy,\n SqlMigrationPlanner,\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 MigrationScaffoldContext,\n SchemaIssue,\n} from '@prisma-next/framework-components/control';\nimport { parseSqliteDefault } from '../default-normalizer';\nimport { normalizeSqliteNativeType } from '../native-type-normalizer';\nimport { planIssues } from './issue-planner';\nimport {\n type SqliteMigrationDestinationInfo,\n TypeScriptRenderableSqliteMigration,\n} from './planner-produced-sqlite-migration';\nimport { sqlitePlannerStrategies } from './planner-strategies';\nimport type { SqlitePlanTargetDetails } from './planner-target-details';\n\nexport function createSqliteMigrationPlanner(): SqliteMigrationPlanner {\n return new SqliteMigrationPlanner();\n}\n\nexport type SqlitePlanResult =\n | { readonly kind: 'success'; readonly plan: TypeScriptRenderableSqliteMigration }\n | SqlPlannerFailureResult;\n\n/**\n * SQLite 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 registered\n * strategies. Strategies absorb groups of related issues into composite\n * recipes (e.g. recreating a table to apply type/nullability/default/\n * constraint changes at once); anything not absorbed by a strategy flows\n * through `mapIssueToCall` in the issue planner as a one-off call.\n *\n * FK-backing indexes are surfaced by `verifySqlSchema`'s index expansion\n * (see `verify-sql-schema.ts:459-469`), so `mapIssueToCall` handles them\n * uniformly alongside user-declared indexes.\n */\nexport class SqliteMigrationPlanner\n implements SqlMigrationPlanner<SqlitePlanTargetDetails>, MigrationPlanner<'sql', 'sqlite'>\n{\n plan(options: {\n readonly contract: unknown;\n readonly schema: unknown;\n readonly policy: MigrationOperationPolicy;\n /**\n * The \"from\" contract (state the planner assumes the database starts at),\n * or `null` for reconciliation flows.\n *\n * Typed as the framework `Contract | null` to satisfy the\n * `MigrationPlanner` interface contract; `planSql` narrows to the SQL\n * shape via `SqlMigrationPlannerPlanOptions`. Used to populate\n * `describe().from` on the produced plan as\n * `fromContract?.storage.storageHash ?? null`.\n */\n readonly fromContract: Contract | null;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;\n /**\n * Contract space this plan applies to. Stamped onto the produced\n * {@link TypeScriptRenderableSqliteMigration.spaceId} so the runner keys\n * the marker row by the right space.\n */\n readonly spaceId: string;\n }): SqlitePlanResult {\n return this.planSql(options as SqlMigrationPlannerPlanOptions);\n }\n\n emptyMigration(\n context: MigrationScaffoldContext,\n spaceId: string,\n ): TypeScriptRenderableSqliteMigration {\n return new TypeScriptRenderableSqliteMigration(\n [],\n {\n from: context.fromHash,\n to: context.toHash,\n },\n spaceId,\n );\n }\n\n private planSql(options: SqlMigrationPlannerPlanOptions): SqlitePlanResult {\n const policyResult = this.ensureAdditivePolicy(options.policy);\n if (policyResult) return policyResult;\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: options.fromContract,\n codecHooks,\n storageTypes,\n schema: options.schema,\n policy: options.policy,\n frameworkComponents: options.frameworkComponents,\n strategies: sqlitePlannerStrategies,\n });\n\n if (!result.ok) {\n return plannerFailure(result.failure);\n }\n\n const destination: SqliteMigrationDestinationInfo = {\n storageHash: options.contract.storage.storageHash,\n ...(options.contract.profileHash !== undefined\n ? { profileHash: options.contract.profileHash }\n : {}),\n };\n\n return {\n kind: 'success' as const,\n plan: new TypeScriptRenderableSqliteMigration(\n result.value.calls,\n {\n from: options.fromContract?.storage.storageHash ?? null,\n to: options.contract.storage.storageHash,\n },\n options.spaceId,\n destination,\n ),\n };\n }\n\n private ensureAdditivePolicy(policy: MigrationOperationPolicy): SqlPlannerFailureResult | null {\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: SqlMigrationPlannerPlanOptions): readonly SchemaIssue[] {\n const allowed = options.policy.allowedOperationClasses;\n const strict = allowed.includes('widening') || allowed.includes('destructive');\n const verifyResult = verifySqlSchema({\n contract: options.contract,\n schema: options.schema,\n strict,\n typeMetadataRegistry: new Map(),\n frameworkComponents: options.frameworkComponents,\n normalizeDefault: parseSqliteDefault,\n normalizeNativeType: normalizeSqliteNativeType,\n });\n return verifyResult.schema.issues;\n }\n}\n"],"mappings":"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"}
@@ -78,10 +78,12 @@ var TypeScriptRenderableSqliteMigration = class extends SqliteMigration {
78
78
  #calls;
79
79
  #meta;
80
80
  #destination;
81
- constructor(calls, meta, destination) {
81
+ #spaceId;
82
+ constructor(calls, meta, spaceId, destination) {
82
83
  super();
83
84
  this.#calls = calls;
84
85
  this.#meta = meta;
86
+ this.#spaceId = spaceId;
85
87
  this.#destination = destination ?? { storageHash: meta.to };
86
88
  }
87
89
  get operations() {
@@ -93,6 +95,14 @@ var TypeScriptRenderableSqliteMigration = class extends SqliteMigration {
93
95
  get destination() {
94
96
  return this.#destination;
95
97
  }
98
+ /**
99
+ * Contract space this planner-produced plan applies to. Threaded
100
+ * from {@link SqlMigrationPlannerPlanOptions.spaceId} so the runner
101
+ * keys the marker row by the right space when executing the plan.
102
+ */
103
+ get spaceId() {
104
+ return this.#spaceId;
105
+ }
96
106
  renderTypeScript() {
97
107
  return renderCallsToTypeScript(this.#calls, {
98
108
  from: this.#meta.from,
@@ -104,4 +114,4 @@ var TypeScriptRenderableSqliteMigration = class extends SqliteMigration {
104
114
 
105
115
  //#endregion
106
116
  export { TypeScriptRenderableSqliteMigration as t };
107
- //# sourceMappingURL=planner-produced-sqlite-migration-C3AAaQoW.mjs.map
117
+ //# sourceMappingURL=planner-produced-sqlite-migration-BANB82Pw.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planner-produced-sqlite-migration-BANB82Pw.mjs","names":["BASE_IMPORTS: readonly ImportRequirement[]","requirements: ImportRequirement[]","lines: string[]","#calls","#meta","#destination","#spaceId"],"sources":["../src/core/migrations/render-typescript.ts","../src/core/migrations/planner-produced-sqlite-migration.ts"],"sourcesContent":["/**\n * Polymorphic TypeScript emitter for the SQLite migration IR. Mirrors the\n * Postgres `render-typescript.ts` — different base-class + factory module\n * specifier, same overall shape.\n */\n\nimport { detectScaffoldRuntime, shebangLineFor } from '@prisma-next/migration-tools/migration-ts';\nimport { type ImportRequirement, jsonToTsSource, renderImports } from '@prisma-next/ts-render';\nimport type { SqliteOpFactoryCall } from './op-factory-call';\n\nexport interface RenderMigrationMeta {\n readonly from: string | null;\n readonly to: string;\n readonly labels?: readonly string[];\n}\n\n/**\n * Always-present base imports for the rendered scaffold. Both come from\n * `@prisma-next/target-sqlite/migration` so an authored SQLite\n * `migration.ts` only needs a single dependency for its base class and\n * its CLI entrypoint. Mirrors Postgres's `BASE_IMPORTS`.\n *\n * - `Migration` — the target-owned re-export fixes the `SqlMigration`\n * generic to `SqlitePlanTargetDetails` and the abstract `targetId` to\n * `'sqlite'`.\n * - `MigrationCLI` — the migration-file CLI entrypoint, re-exported from\n * `@prisma-next/cli/migration-cli`. Loads `prisma-next.config.ts`,\n * assembles a `ControlStack`, and instantiates the migration class.\n */\nconst BASE_IMPORTS: readonly ImportRequirement[] = [\n { moduleSpecifier: '@prisma-next/target-sqlite/migration', symbol: 'Migration' },\n { moduleSpecifier: '@prisma-next/target-sqlite/migration', symbol: 'MigrationCLI' },\n];\n\nexport function renderCallsToTypeScript(\n calls: ReadonlyArray<SqliteOpFactoryCall>,\n meta: RenderMigrationMeta,\n): string {\n const imports = buildImports(calls);\n const operationsBody = calls.map((c) => c.renderTypeScript()).join(',\\n');\n\n return [\n shebangLineFor(detectScaffoldRuntime()),\n imports,\n '',\n 'export default class M extends Migration {',\n buildDescribeMethod(meta),\n ' override get operations() {',\n ' return [',\n indent(operationsBody, 6),\n ' ];',\n ' }',\n '}',\n '',\n 'MigrationCLI.run(import.meta.url, M);',\n '',\n ].join('\\n');\n}\n\nfunction buildImports(calls: ReadonlyArray<SqliteOpFactoryCall>): string {\n const requirements: ImportRequirement[] = [...BASE_IMPORTS];\n for (const call of calls) {\n for (const req of call.importRequirements()) {\n requirements.push(req);\n }\n }\n return renderImports(requirements);\n}\n\nfunction buildDescribeMethod(meta: RenderMigrationMeta): string {\n const lines: string[] = [];\n lines.push(' override describe() {');\n lines.push(' return {');\n lines.push(` from: ${JSON.stringify(meta.from)},`);\n lines.push(` to: ${JSON.stringify(meta.to)},`);\n if (meta.labels && meta.labels.length > 0) {\n lines.push(` labels: ${jsonToTsSource(meta.labels)},`);\n }\n lines.push(' };');\n lines.push(' }');\n lines.push('');\n return lines.join('\\n');\n}\n\nfunction indent(text: string, spaces: number): string {\n const pad = ' '.repeat(spaces);\n return text\n .split('\\n')\n .map((line) => (line.trim() ? `${pad}${line}` : line))\n .join('\\n');\n}\n","import 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 { SqliteOpFactoryCall } from './op-factory-call';\nimport type { SqlitePlanTargetDetails } from './planner-target-details';\nimport { renderOps } from './render-ops';\nimport { renderCallsToTypeScript } from './render-typescript';\nimport { SqliteMigration } from './sqlite-migration';\n\ntype Op = SqlMigrationPlanOperation<SqlitePlanTargetDetails>;\n\nexport interface SqliteMigrationDestinationInfo {\n readonly storageHash: string;\n readonly profileHash?: string;\n}\n\nexport class TypeScriptRenderableSqliteMigration\n extends SqliteMigration\n implements MigrationPlanWithAuthoringSurface\n{\n readonly #calls: readonly SqliteOpFactoryCall[];\n readonly #meta: MigrationMeta;\n readonly #destination: SqliteMigrationDestinationInfo;\n readonly #spaceId: string;\n\n constructor(\n calls: readonly SqliteOpFactoryCall[],\n meta: MigrationMeta,\n spaceId: string,\n destination?: SqliteMigrationDestinationInfo,\n ) {\n super();\n this.#calls = calls;\n this.#meta = meta;\n this.#spaceId = spaceId;\n this.#destination = destination ?? { storageHash: meta.to };\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 override get destination(): SqliteMigrationDestinationInfo {\n return this.#destination;\n }\n\n /**\n * Contract space this planner-produced plan applies to. Threaded\n * from {@link SqlMigrationPlannerPlanOptions.spaceId} so the runner\n * keys the marker row by the right space when executing the plan.\n */\n get spaceId(): string {\n return this.#spaceId;\n }\n\n renderTypeScript(): string {\n return renderCallsToTypeScript(this.#calls, {\n from: this.#meta.from,\n to: this.#meta.to,\n ...ifDefined('labels', this.#meta.labels),\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAMA,eAA6C,CACjD;CAAE,iBAAiB;CAAwC,QAAQ;CAAa,EAChF;CAAE,iBAAiB;CAAwC,QAAQ;CAAgB,CACpF;AAED,SAAgB,wBACd,OACA,MACQ;CACR,MAAM,UAAU,aAAa,MAAM;CACnC,MAAM,iBAAiB,MAAM,KAAK,MAAM,EAAE,kBAAkB,CAAC,CAAC,KAAK,MAAM;AAEzE,QAAO;EACL,eAAe,uBAAuB,CAAC;EACvC;EACA;EACA;EACA,oBAAoB,KAAK;EACzB;EACA;EACA,OAAO,gBAAgB,EAAE;EACzB;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,KAAK,KAAK;;AAGd,SAAS,aAAa,OAAmD;CACvE,MAAMC,eAAoC,CAAC,GAAG,aAAa;AAC3D,MAAK,MAAM,QAAQ,MACjB,MAAK,MAAM,OAAO,KAAK,oBAAoB,CACzC,cAAa,KAAK,IAAI;AAG1B,QAAO,cAAc,aAAa;;AAGpC,SAAS,oBAAoB,MAAmC;CAC9D,MAAMC,QAAkB,EAAE;AAC1B,OAAM,KAAK,0BAA0B;AACrC,OAAM,KAAK,eAAe;AAC1B,OAAM,KAAK,eAAe,KAAK,UAAU,KAAK,KAAK,CAAC,GAAG;AACvD,OAAM,KAAK,aAAa,KAAK,UAAU,KAAK,GAAG,CAAC,GAAG;AACnD,KAAI,KAAK,UAAU,KAAK,OAAO,SAAS,EACtC,OAAM,KAAK,iBAAiB,eAAe,KAAK,OAAO,CAAC,GAAG;AAE7D,OAAM,KAAK,SAAS;AACpB,OAAM,KAAK,MAAM;AACjB,OAAM,KAAK,GAAG;AACd,QAAO,MAAM,KAAK,KAAK;;AAGzB,SAAS,OAAO,MAAc,QAAwB;CACpD,MAAM,MAAM,IAAI,OAAO,OAAO;AAC9B,QAAO,KACJ,MAAM,KAAK,CACX,KAAK,SAAU,KAAK,MAAM,GAAG,GAAG,MAAM,SAAS,KAAM,CACrD,KAAK,KAAK;;;;;ACxEf,IAAa,sCAAb,cACU,gBAEV;CACE,CAASC;CACT,CAASC;CACT,CAASC;CACT,CAASC;CAET,YACE,OACA,MACA,SACA,aACA;AACA,SAAO;AACP,QAAKH,QAAS;AACd,QAAKC,OAAQ;AACb,QAAKE,UAAW;AAChB,QAAKD,cAAe,eAAe,EAAE,aAAa,KAAK,IAAI;;CAG7D,IAAa,aAA4B;AACvC,SAAO,UAAU,MAAKF,MAAO;;CAG/B,AAAS,WAA0B;AACjC,SAAO,MAAKC;;CAGd,IAAa,cAA8C;AACzD,SAAO,MAAKC;;;;;;;CAQd,IAAI,UAAkB;AACpB,SAAO,MAAKC;;CAGd,mBAA2B;AACzB,SAAO,wBAAwB,MAAKH,OAAQ;GAC1C,MAAM,MAAKC,KAAM;GACjB,IAAI,MAAKA,KAAM;GACf,GAAG,UAAU,UAAU,MAAKA,KAAM,OAAO;GAC1C,CAAC"}
@@ -13,12 +13,18 @@ interface SqliteMigrationDestinationInfo {
13
13
  }
14
14
  declare class TypeScriptRenderableSqliteMigration extends SqliteMigration implements MigrationPlanWithAuthoringSurface {
15
15
  #private;
16
- constructor(calls: readonly SqliteOpFactoryCall[], meta: MigrationMeta, destination?: SqliteMigrationDestinationInfo);
16
+ constructor(calls: readonly SqliteOpFactoryCall[], meta: MigrationMeta, spaceId: string, destination?: SqliteMigrationDestinationInfo);
17
17
  get operations(): readonly Op[];
18
18
  describe(): MigrationMeta;
19
19
  get destination(): SqliteMigrationDestinationInfo;
20
+ /**
21
+ * Contract space this planner-produced plan applies to. Threaded
22
+ * from {@link SqlMigrationPlannerPlanOptions.spaceId} so the runner
23
+ * keys the marker row by the right space when executing the plan.
24
+ */
25
+ get spaceId(): string;
20
26
  renderTypeScript(): string;
21
27
  }
22
28
  //#endregion
23
29
  export { TypeScriptRenderableSqliteMigration as n, SqliteMigrationDestinationInfo as t };
24
- //# sourceMappingURL=planner-produced-sqlite-migration-BfIIUtxe.d.mts.map
30
+ //# sourceMappingURL=planner-produced-sqlite-migration-CJr8ATRH.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planner-produced-sqlite-migration-CJr8ATRH.d.mts","names":[],"sources":["../src/core/migrations/planner-produced-sqlite-migration.ts"],"sourcesContent":[],"mappings":";;;;;;;;KAUK,EAAA,GAAK,0BAA0B;UAEnB,8BAAA;EAFZ,SAAE,WAA6B,EAAA,MAAA;EAEnB,SAAA,WAAA,CAAA,EAAA,MAAA;AAKjB;AAUoB,cAVP,mCAAA,SACH,eAAA,YACG,iCAQO,CAAA;EACV,CAAA,OAAA;EAEQ,WAAA,CAAA,KAAA,EAAA,SAHE,mBAGF,EAAA,EAAA,IAAA,EAFR,aAEQ,EAAA,OAAA,EAAA,MAAA,EAAA,WAAA,CAAA,EAAA,8BAAA;EASoB,IAAA,UAAA,CAAA,CAAA,EAAA,SAAA,EAAA,EAAA;EAIf,QAAA,CAAA,CAAA,EAAA,aAAA;EAIO,IAAA,WAAA,CAAA,CAAA,EAAA,8BAAA;EA7BpB"}
@@ -1,5 +1,5 @@
1
1
  import "./shared-D_1fFqLf.mjs";
2
2
  import "./sqlite-migration-BeR1cikr.mjs";
3
3
  import "./op-factory-call-dUIOao68.mjs";
4
- import { n as TypeScriptRenderableSqliteMigration, t as SqliteMigrationDestinationInfo } from "./planner-produced-sqlite-migration-BfIIUtxe.mjs";
4
+ import { n as TypeScriptRenderableSqliteMigration, t as SqliteMigrationDestinationInfo } from "./planner-produced-sqlite-migration-CJr8ATRH.mjs";
5
5
  export { type SqliteMigrationDestinationInfo, TypeScriptRenderableSqliteMigration };
@@ -1,3 +1,3 @@
1
- import { t as TypeScriptRenderableSqliteMigration } from "./planner-produced-sqlite-migration-C3AAaQoW.mjs";
1
+ import { t as TypeScriptRenderableSqliteMigration } from "./planner-produced-sqlite-migration-BANB82Pw.mjs";
2
2
 
3
3
  export { TypeScriptRenderableSqliteMigration };
@@ -2,10 +2,10 @@ import { t as SqlitePlanTargetDetails } from "./planner-target-details-CtWRvse0.
2
2
  import "./shared-D_1fFqLf.mjs";
3
3
  import "./sqlite-migration-BeR1cikr.mjs";
4
4
  import "./op-factory-call-dUIOao68.mjs";
5
- import { n as TypeScriptRenderableSqliteMigration } from "./planner-produced-sqlite-migration-BfIIUtxe.mjs";
5
+ import { n as TypeScriptRenderableSqliteMigration } from "./planner-produced-sqlite-migration-CJr8ATRH.mjs";
6
6
  import { MigrationOperationPolicy, SqlMigrationPlanner, SqlPlannerFailureResult } from "@prisma-next/family-sql/control";
7
- import { Contract } from "@prisma-next/contract/types";
8
7
  import { MigrationPlanner, MigrationScaffoldContext } from "@prisma-next/framework-components/control";
8
+ import { Contract } from "@prisma-next/contract/types";
9
9
  import { TargetBoundComponentDescriptor } from "@prisma-next/framework-components/components";
10
10
 
11
11
  //#region src/core/migrations/planner.d.ts
@@ -45,8 +45,14 @@ declare class SqliteMigrationPlanner implements SqlMigrationPlanner<SqlitePlanTa
45
45
  */
46
46
  readonly fromContract: Contract | null;
47
47
  readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
48
+ /**
49
+ * Contract space this plan applies to. Stamped onto the produced
50
+ * {@link TypeScriptRenderableSqliteMigration.spaceId} so the runner keys
51
+ * the marker row by the right space.
52
+ */
53
+ readonly spaceId: string;
48
54
  }): SqlitePlanResult;
49
- emptyMigration(context: MigrationScaffoldContext): TypeScriptRenderableSqliteMigration;
55
+ emptyMigration(context: MigrationScaffoldContext, spaceId: string): TypeScriptRenderableSqliteMigration;
50
56
  private planSql;
51
57
  private ensureAdditivePolicy;
52
58
  private collectSchemaIssues;
@@ -1 +1 @@
1
- {"version":3,"file":"planner.d.mts","names":[],"sources":["../src/core/migrations/planner.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;iBAyBgB,4BAAA,CAAA,GAAgC;KAIpC,gBAAA;;iBACmC;IAC3C;AANJ;AAIA;AAkBA;;;;;;;;;;;;cAAa,sBAAA,YACA,oBAAoB,0BAA0B;;;;qBAKtC;;;;;;;;;;;2BAWM;kCACO,cAAc;MAC1C;0BAIoB,2BAA2B"}
1
+ {"version":3,"file":"planner.d.mts","names":[],"sources":["../src/core/migrations/planner.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;iBAyBgB,4BAAA,CAAA,GAAgC;KAIpC,gBAAA;;iBACmC;IAC3C;AANJ;AAIA;AAkBA;;;;;;;;;;;;cAAa,sBAAA,YACA,oBAAoB,0BAA0B;;;;qBAKtC;;;;;;;;;;;2BAWM;kCACO,cAAc;;;;;;;MAO1C;0BAKO,4CAER"}
package/dist/planner.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { n as createSqliteMigrationPlanner, t as SqliteMigrationPlanner } from "./planner-CuchCrpN.mjs";
1
+ import { n as createSqliteMigrationPlanner, t as SqliteMigrationPlanner } from "./planner-DmOFOhoq.mjs";
2
2
 
3
3
  export { SqliteMigrationPlanner, createSqliteMigrationPlanner };
@@ -1,3 +1,5 @@
1
+ import { APP_SPACE_ID } from "@prisma-next/framework-components/control";
2
+
1
3
  //#region src/core/migrations/statement-builders.ts
2
4
  const MARKER_TABLE_NAME = "_prisma_marker";
3
5
  const LEDGER_TABLE_NAME = "_prisma_ledger";
@@ -6,9 +8,18 @@ const LEDGER_TABLE_NAME = "_prisma_ledger";
6
8
  * when reconciling "extra" tables against the contract.
7
9
  */
8
10
  const CONTROL_TABLE_NAMES = new Set([MARKER_TABLE_NAME, LEDGER_TABLE_NAME]);
11
+ /**
12
+ * Schema for `_prisma_marker`. The `space TEXT PRIMARY KEY` shape
13
+ * supports one row per loaded contract space (`'app'`,
14
+ * `'<extension-id>'`, …); brand-new databases create this shape
15
+ * directly. The migration runner detects pre-1.0 single-row markers
16
+ * (no `space` column) at boot and fails with a structured
17
+ * `LEGACY_MARKER_SHAPE` error rather than auto-rebuilding the table —
18
+ * see `specs/framework-mechanism.spec.md § 2`.
19
+ */
9
20
  const ensureMarkerTableStatement = {
10
21
  sql: `CREATE TABLE IF NOT EXISTS _prisma_marker (
11
- id INTEGER PRIMARY KEY CHECK (id = 1),
22
+ space TEXT NOT NULL PRIMARY KEY DEFAULT '${APP_SPACE_ID}',
12
23
  core_hash TEXT NOT NULL,
13
24
  profile_hash TEXT NOT NULL,
14
25
  contract_json TEXT,
@@ -34,7 +45,7 @@ const ensureLedgerTableStatement = {
34
45
  )`,
35
46
  params: []
36
47
  };
37
- function readMarkerStatement() {
48
+ function readMarkerStatement(space) {
38
49
  return {
39
50
  sql: `SELECT
40
51
  core_hash,
@@ -46,15 +57,15 @@ function readMarkerStatement() {
46
57
  meta,
47
58
  invariants
48
59
  FROM _prisma_marker
49
- WHERE id = ?`,
50
- params: [1]
60
+ WHERE space = ?`,
61
+ params: [space]
51
62
  };
52
63
  }
53
64
  function buildWriteMarkerStatements(input) {
54
65
  return {
55
66
  insert: {
56
67
  sql: `INSERT INTO _prisma_marker (
57
- id,
68
+ space,
58
69
  core_hash,
59
70
  profile_hash,
60
71
  contract_json,
@@ -75,7 +86,7 @@ function buildWriteMarkerStatements(input) {
75
86
  ?
76
87
  )`,
77
88
  params: [
78
- 1,
89
+ input.space,
79
90
  input.storageHash,
80
91
  input.profileHash,
81
92
  jsonParam(input.contractJson),
@@ -95,7 +106,7 @@ function buildWriteMarkerStatements(input) {
95
106
  app_tag = ?,
96
107
  meta = ?,
97
108
  invariants = ?
98
- WHERE id = ?`,
109
+ WHERE space = ?`,
99
110
  params: [
100
111
  input.storageHash,
101
112
  input.profileHash,
@@ -104,7 +115,7 @@ function buildWriteMarkerStatements(input) {
104
115
  input.appTag ?? null,
105
116
  jsonParam(input.meta ?? {}),
106
117
  jsonParam(input.invariants),
107
- 1
118
+ input.space
108
119
  ]
109
120
  }
110
121
  };
@@ -144,5 +155,5 @@ function jsonParam(value) {
144
155
  }
145
156
 
146
157
  //#endregion
147
- export { buildWriteMarkerStatements as a, readMarkerStatement as c, buildLedgerInsertStatement as i, LEDGER_TABLE_NAME as n, ensureLedgerTableStatement as o, MARKER_TABLE_NAME as r, ensureMarkerTableStatement as s, CONTROL_TABLE_NAMES as t };
148
- //# sourceMappingURL=statement-builders-B3OGOp7n.mjs.map
158
+ export { buildLedgerInsertStatement as a, ensureMarkerTableStatement as c, MARKER_TABLE_NAME as i, readMarkerStatement as l, CONTROL_TABLE_NAMES as n, buildWriteMarkerStatements as o, LEDGER_TABLE_NAME as r, ensureLedgerTableStatement as s, APP_SPACE_ID as t };
159
+ //# sourceMappingURL=statement-builders-DobaAWnW.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statement-builders-DobaAWnW.mjs","names":["CONTROL_TABLE_NAMES: ReadonlySet<string>","ensureMarkerTableStatement: SqlStatement","ensureLedgerTableStatement: SqlStatement"],"sources":["../src/core/migrations/statement-builders.ts"],"sourcesContent":["import { APP_SPACE_ID } from '@prisma-next/framework-components/control';\n\nexport { APP_SPACE_ID };\n\nexport interface SqlStatement {\n readonly sql: string;\n readonly params: readonly unknown[];\n}\n\nexport const MARKER_TABLE_NAME = '_prisma_marker';\nexport const LEDGER_TABLE_NAME = '_prisma_ledger';\n\n/**\n * Control tables the runner creates/manages. The planner must not drop these\n * when reconciling \"extra\" tables against the contract.\n */\nexport const CONTROL_TABLE_NAMES: ReadonlySet<string> = new Set([\n MARKER_TABLE_NAME,\n LEDGER_TABLE_NAME,\n]);\n\n/**\n * Schema for `_prisma_marker`. The `space TEXT PRIMARY KEY` shape\n * supports one row per loaded contract space (`'app'`,\n * `'<extension-id>'`, …); brand-new databases create this shape\n * directly. The migration runner detects pre-1.0 single-row markers\n * (no `space` column) at boot and fails with a structured\n * `LEGACY_MARKER_SHAPE` error rather than auto-rebuilding the table —\n * see `specs/framework-mechanism.spec.md § 2`.\n */\nexport const ensureMarkerTableStatement: SqlStatement = {\n sql: `CREATE TABLE IF NOT EXISTS _prisma_marker (\n space TEXT NOT NULL PRIMARY KEY DEFAULT '${APP_SPACE_ID}',\n core_hash TEXT NOT NULL,\n profile_hash TEXT NOT NULL,\n contract_json TEXT,\n canonical_version INTEGER,\n updated_at TEXT NOT NULL DEFAULT (datetime('now')),\n app_tag TEXT,\n meta TEXT 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_ledger (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n created_at TEXT NOT NULL DEFAULT (datetime('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 TEXT,\n contract_json_after TEXT,\n operations TEXT NOT NULL\n )`,\n params: [],\n};\n\nexport function readMarkerStatement(space: string): SqlStatement {\n return {\n sql: `SELECT\n core_hash,\n profile_hash,\n contract_json,\n canonical_version,\n updated_at,\n app_tag,\n meta,\n invariants\n FROM _prisma_marker\n WHERE space = ?`,\n params: [space],\n };\n}\n\nexport interface WriteMarkerInput {\n /**\n * Logical space identifier for this marker row. Required at every\n * call site so the type system surfaces every place that needs to\n * thread the value (rather than letting an `?? APP_SPACE_ID`\n * fall-through silently collapse multi-space markers onto the\n * `'app'` row). App-plan callers pass {@link APP_SPACE_ID}\n * (`'app'`); per-extension callers pass the extension's space id.\n */\n readonly space: string;\n readonly storageHash: string;\n readonly profileHash: string;\n readonly contractJson?: unknown;\n readonly canonicalVersion?: number | null;\n readonly appTag?: string | null;\n readonly meta?: Record<string, unknown>;\n /**\n * Invariants to write into `marker.invariants`. Stored as a JSON-encoded\n * TEXT array — SQLite has no native array type. The runner is responsible\n * for merging with the existing column (no SQL-side merge here, unlike\n * Postgres) before passing them in: BEGIN EXCLUSIVE on the migration\n * transaction makes the read-then-merge-then-write sequence safe.\n */\n readonly invariants: readonly string[];\n}\n\nexport function buildWriteMarkerStatements(input: WriteMarkerInput): {\n readonly insert: SqlStatement;\n readonly update: SqlStatement;\n} {\n const params: readonly unknown[] = [\n input.space,\n input.storageHash,\n input.profileHash,\n jsonParam(input.contractJson),\n input.canonicalVersion ?? null,\n input.appTag ?? null,\n jsonParam(input.meta ?? {}),\n jsonParam(input.invariants),\n ];\n\n return {\n insert: {\n sql: `INSERT INTO _prisma_marker (\n space,\n core_hash,\n profile_hash,\n contract_json,\n canonical_version,\n updated_at,\n app_tag,\n meta,\n invariants\n ) VALUES (\n ?,\n ?,\n ?,\n ?,\n ?,\n datetime('now'),\n ?,\n ?,\n ?\n )`,\n params,\n },\n update: {\n sql: `UPDATE _prisma_marker SET\n core_hash = ?,\n profile_hash = ?,\n contract_json = ?,\n canonical_version = ?,\n updated_at = datetime('now'),\n app_tag = ?,\n meta = ?,\n invariants = ?\n WHERE space = ?`,\n params: [\n input.storageHash,\n input.profileHash,\n jsonParam(input.contractJson),\n input.canonicalVersion ?? null,\n input.appTag ?? null,\n jsonParam(input.meta ?? {}),\n jsonParam(input.invariants),\n input.space,\n ],\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_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 ?,\n ?,\n ?,\n ?,\n ?,\n ?,\n ?\n )`,\n params: [\n input.originStorageHash ?? null,\n input.originProfileHash ?? null,\n input.destinationStorageHash,\n input.destinationProfileHash ?? null,\n jsonParam(input.contractJsonBefore),\n jsonParam(input.contractJsonAfter),\n jsonParam(input.operations),\n ],\n };\n}\n\nfunction jsonParam(value: unknown): string {\n return JSON.stringify(value ?? null);\n}\n"],"mappings":";;;AASA,MAAa,oBAAoB;AACjC,MAAa,oBAAoB;;;;;AAMjC,MAAaA,sBAA2C,IAAI,IAAI,CAC9D,mBACA,kBACD,CAAC;;;;;;;;;;AAWF,MAAaC,6BAA2C;CACtD,KAAK;+CACwC,aAAa;;;;;;;;;;CAU1D,QAAQ,EAAE;CACX;AAED,MAAaC,6BAA2C;CACtD,KAAK;;;;;;;;;;;CAWL,QAAQ,EAAE;CACX;AAED,SAAgB,oBAAoB,OAA6B;AAC/D,QAAO;EACL,KAAK;;;;;;;;;;;EAWL,QAAQ,CAAC,MAAM;EAChB;;AA6BH,SAAgB,2BAA2B,OAGzC;AAYA,QAAO;EACL,QAAQ;GACN,KAAK;;;;;;;;;;;;;;;;;;;;;GAqBL,QAlC+B;IACjC,MAAM;IACN,MAAM;IACN,MAAM;IACN,UAAU,MAAM,aAAa;IAC7B,MAAM,oBAAoB;IAC1B,MAAM,UAAU;IAChB,UAAU,MAAM,QAAQ,EAAE,CAAC;IAC3B,UAAU,MAAM,WAAW;IAC5B;GA0BE;EACD,QAAQ;GACN,KAAK;;;;;;;;;;GAUL,QAAQ;IACN,MAAM;IACN,MAAM;IACN,UAAU,MAAM,aAAa;IAC7B,MAAM,oBAAoB;IAC1B,MAAM,UAAU;IAChB,UAAU,MAAM,QAAQ,EAAE,CAAC;IAC3B,UAAU,MAAM,WAAW;IAC3B,MAAM;IACP;GACF;EACF;;AAaH,SAAgB,2BAA2B,OAAwC;AACjF,QAAO;EACL,KAAK;;;;;;;;;;;;;;;;;EAiBL,QAAQ;GACN,MAAM,qBAAqB;GAC3B,MAAM,qBAAqB;GAC3B,MAAM;GACN,MAAM,0BAA0B;GAChC,UAAU,MAAM,mBAAmB;GACnC,UAAU,MAAM,kBAAkB;GAClC,UAAU,MAAM,WAAW;GAC5B;EACF;;AAGH,SAAS,UAAU,OAAwB;AACzC,QAAO,KAAK,UAAU,SAAS,KAAK"}
@@ -1,3 +1,5 @@
1
+ import { APP_SPACE_ID } from "@prisma-next/framework-components/control";
2
+
1
3
  //#region src/core/migrations/statement-builders.d.ts
2
4
  interface SqlStatement {
3
5
  readonly sql: string;
@@ -10,10 +12,28 @@ declare const LEDGER_TABLE_NAME = "_prisma_ledger";
10
12
  * when reconciling "extra" tables against the contract.
11
13
  */
12
14
  declare const CONTROL_TABLE_NAMES: ReadonlySet<string>;
15
+ /**
16
+ * Schema for `_prisma_marker`. The `space TEXT PRIMARY KEY` shape
17
+ * supports one row per loaded contract space (`'app'`,
18
+ * `'<extension-id>'`, …); brand-new databases create this shape
19
+ * directly. The migration runner detects pre-1.0 single-row markers
20
+ * (no `space` column) at boot and fails with a structured
21
+ * `LEGACY_MARKER_SHAPE` error rather than auto-rebuilding the table —
22
+ * see `specs/framework-mechanism.spec.md § 2`.
23
+ */
13
24
  declare const ensureMarkerTableStatement: SqlStatement;
14
25
  declare const ensureLedgerTableStatement: SqlStatement;
15
- declare function readMarkerStatement(): SqlStatement;
26
+ declare function readMarkerStatement(space: string): SqlStatement;
16
27
  interface WriteMarkerInput {
28
+ /**
29
+ * Logical space identifier for this marker row. Required at every
30
+ * call site so the type system surfaces every place that needs to
31
+ * thread the value (rather than letting an `?? APP_SPACE_ID`
32
+ * fall-through silently collapse multi-space markers onto the
33
+ * `'app'` row). App-plan callers pass {@link APP_SPACE_ID}
34
+ * (`'app'`); per-extension callers pass the extension's space id.
35
+ */
36
+ readonly space: string;
17
37
  readonly storageHash: string;
18
38
  readonly profileHash: string;
19
39
  readonly contractJson?: unknown;
@@ -44,5 +64,5 @@ interface LedgerInsertInput {
44
64
  }
45
65
  declare function buildLedgerInsertStatement(input: LedgerInsertInput): SqlStatement;
46
66
  //#endregion
47
- export { CONTROL_TABLE_NAMES, LEDGER_TABLE_NAME, MARKER_TABLE_NAME, type SqlStatement, buildLedgerInsertStatement, buildWriteMarkerStatements, ensureLedgerTableStatement, ensureMarkerTableStatement, readMarkerStatement };
67
+ export { APP_SPACE_ID, CONTROL_TABLE_NAMES, LEDGER_TABLE_NAME, MARKER_TABLE_NAME, type SqlStatement, buildLedgerInsertStatement, buildWriteMarkerStatements, ensureLedgerTableStatement, ensureMarkerTableStatement, readMarkerStatement };
48
68
  //# 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,SAAiB,OAAA,EAAA;AAC9B;AAMa,cAPA,iBAAA,GAOqB,gBAAW;AAKhC,cAXA,iBAAA,GAwBZ,gBAbwC;AAezC;AAeA;AAiBA;AAiBA;AAAkD,cArErC,mBAqEqC,EArEhB,WAqEgB,CAAA,MAAA,CAAA;AAC/B,cAjEN,0BAiEM,EAjEsB,YAiEtB;AACA,cAnDN,0BAmDM,EAnDsB,YAmDtB;AAAY,iBApCf,mBAAA,CAAA,CAoCe,EApCQ,YAoCR;AA+Dd,UAlFA,gBAAA,CAkFiB;EAUlB,SAAA,WAAA,EAAA,MAAA;;;;;kBAtFE;;;;;;;;;;iBAWF,0BAAA,QAAkC;mBAC/B;mBACA;;UA+DF,iBAAA;;;;;;;;;iBAUD,0BAAA,QAAkC,oBAAoB"}
1
+ {"version":3,"file":"statement-builders.d.mts","names":[],"sources":["../src/core/migrations/statement-builders.ts"],"sourcesContent":[],"mappings":";;;UAIiB,YAAA;EAAA,SAAA,GAAA,EAAA,MAAY;EAKhB,SAAA,MAAA,EAAA,SAAiB,OAAA,EAAA;AAC9B;AAMa,cAPA,iBAAA,GAOqB,gBAAW;AAchC,cApBA,iBAAA,GAiCZ,gBAbwC;AAezC;AAeA;AAiBA;AA0BA;AAAkD,cAvFrC,mBAuFqC,EAvFhB,WAuFgB,CAAA,MAAA,CAAA;;;;AAiElD;AAUA;;;;;cApJa,4BAA4B;cAe5B,4BAA4B;iBAezB,mBAAA,iBAAoC;UAiBnC,gBAAA;;;;;;;;;;;;;;;kBAeC;;;;;;;;;;iBAWF,0BAAA,QAAkC;mBAC/B;mBACA;;UA+DF,iBAAA;;;;;;;;;iBAUD,0BAAA,QAAkC,oBAAoB"}
@@ -1,3 +1,3 @@
1
- import { a as buildWriteMarkerStatements, c as readMarkerStatement, i as buildLedgerInsertStatement, n as LEDGER_TABLE_NAME, o as ensureLedgerTableStatement, r as MARKER_TABLE_NAME, s as ensureMarkerTableStatement, t as CONTROL_TABLE_NAMES } from "./statement-builders-B3OGOp7n.mjs";
1
+ import { a as buildLedgerInsertStatement, c as ensureMarkerTableStatement, i as MARKER_TABLE_NAME, l as readMarkerStatement, n as CONTROL_TABLE_NAMES, o as buildWriteMarkerStatements, r as LEDGER_TABLE_NAME, s as ensureLedgerTableStatement, t as APP_SPACE_ID } from "./statement-builders-DobaAWnW.mjs";
2
2
 
3
- export { CONTROL_TABLE_NAMES, LEDGER_TABLE_NAME, MARKER_TABLE_NAME, buildLedgerInsertStatement, buildWriteMarkerStatements, ensureLedgerTableStatement, ensureMarkerTableStatement, readMarkerStatement };
3
+ export { APP_SPACE_ID, CONTROL_TABLE_NAMES, LEDGER_TABLE_NAME, MARKER_TABLE_NAME, buildLedgerInsertStatement, buildWriteMarkerStatements, ensureLedgerTableStatement, ensureMarkerTableStatement, readMarkerStatement };
package/package.json CHANGED
@@ -1,32 +1,32 @@
1
1
  {
2
2
  "name": "@prisma-next/target-sqlite",
3
- "version": "0.5.0-dev.62",
3
+ "version": "0.5.0-dev.63",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "dependencies": {
8
- "@prisma-next/cli": "0.5.0-dev.62",
9
- "@prisma-next/contract": "0.5.0-dev.62",
10
- "@prisma-next/errors": "0.5.0-dev.62",
11
- "@prisma-next/family-sql": "0.5.0-dev.62",
12
- "@prisma-next/framework-components": "0.5.0-dev.62",
13
- "@prisma-next/migration-tools": "0.5.0-dev.62",
14
- "@prisma-next/sql-contract": "0.5.0-dev.62",
15
- "@prisma-next/sql-errors": "0.5.0-dev.62",
16
- "@prisma-next/sql-relational-core": "0.5.0-dev.62",
17
- "@prisma-next/sql-runtime": "0.5.0-dev.62",
18
- "@prisma-next/sql-schema-ir": "0.5.0-dev.62",
19
- "@prisma-next/ts-render": "0.5.0-dev.62",
20
- "@prisma-next/utils": "0.5.0-dev.62"
8
+ "@prisma-next/cli": "0.5.0-dev.63",
9
+ "@prisma-next/contract": "0.5.0-dev.63",
10
+ "@prisma-next/errors": "0.5.0-dev.63",
11
+ "@prisma-next/framework-components": "0.5.0-dev.63",
12
+ "@prisma-next/family-sql": "0.5.0-dev.63",
13
+ "@prisma-next/migration-tools": "0.5.0-dev.63",
14
+ "@prisma-next/sql-contract": "0.5.0-dev.63",
15
+ "@prisma-next/sql-runtime": "0.5.0-dev.63",
16
+ "@prisma-next/sql-schema-ir": "0.5.0-dev.63",
17
+ "@prisma-next/sql-relational-core": "0.5.0-dev.63",
18
+ "@prisma-next/ts-render": "0.5.0-dev.63",
19
+ "@prisma-next/sql-errors": "0.5.0-dev.63",
20
+ "@prisma-next/utils": "0.5.0-dev.63"
21
21
  },
22
22
  "devDependencies": {
23
23
  "tsdown": "0.18.4",
24
24
  "typescript": "5.9.3",
25
25
  "vitest": "4.0.17",
26
- "@prisma-next/test-utils": "0.0.1",
27
26
  "@prisma-next/tsconfig": "0.0.0",
28
- "@prisma-next/tsdown": "0.0.0",
29
- "@prisma-next/driver-sqlite": "0.5.0-dev.62"
27
+ "@prisma-next/test-utils": "0.0.1",
28
+ "@prisma-next/driver-sqlite": "0.5.0-dev.63",
29
+ "@prisma-next/tsdown": "0.0.0"
30
30
  },
31
31
  "files": [
32
32
  "dist",
@@ -22,15 +22,18 @@ export class TypeScriptRenderableSqliteMigration
22
22
  readonly #calls: readonly SqliteOpFactoryCall[];
23
23
  readonly #meta: MigrationMeta;
24
24
  readonly #destination: SqliteMigrationDestinationInfo;
25
+ readonly #spaceId: string;
25
26
 
26
27
  constructor(
27
28
  calls: readonly SqliteOpFactoryCall[],
28
29
  meta: MigrationMeta,
30
+ spaceId: string,
29
31
  destination?: SqliteMigrationDestinationInfo,
30
32
  ) {
31
33
  super();
32
34
  this.#calls = calls;
33
35
  this.#meta = meta;
36
+ this.#spaceId = spaceId;
34
37
  this.#destination = destination ?? { storageHash: meta.to };
35
38
  }
36
39
 
@@ -46,6 +49,15 @@ export class TypeScriptRenderableSqliteMigration
46
49
  return this.#destination;
47
50
  }
48
51
 
52
+ /**
53
+ * Contract space this planner-produced plan applies to. Threaded
54
+ * from {@link SqlMigrationPlannerPlanOptions.spaceId} so the runner
55
+ * keys the marker row by the right space when executing the plan.
56
+ */
57
+ get spaceId(): string {
58
+ return this.#spaceId;
59
+ }
60
+
49
61
  renderTypeScript(): string {
50
62
  return renderCallsToTypeScript(this.#calls, {
51
63
  from: this.#meta.from,
@@ -64,15 +64,28 @@ export class SqliteMigrationPlanner
64
64
  */
65
65
  readonly fromContract: Contract | null;
66
66
  readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
67
+ /**
68
+ * Contract space this plan applies to. Stamped onto the produced
69
+ * {@link TypeScriptRenderableSqliteMigration.spaceId} so the runner keys
70
+ * the marker row by the right space.
71
+ */
72
+ readonly spaceId: string;
67
73
  }): SqlitePlanResult {
68
74
  return this.planSql(options as SqlMigrationPlannerPlanOptions);
69
75
  }
70
76
 
71
- emptyMigration(context: MigrationScaffoldContext): TypeScriptRenderableSqliteMigration {
72
- return new TypeScriptRenderableSqliteMigration([], {
73
- from: context.fromHash,
74
- to: context.toHash,
75
- });
77
+ emptyMigration(
78
+ context: MigrationScaffoldContext,
79
+ spaceId: string,
80
+ ): TypeScriptRenderableSqliteMigration {
81
+ return new TypeScriptRenderableSqliteMigration(
82
+ [],
83
+ {
84
+ from: context.fromHash,
85
+ to: context.toHash,
86
+ },
87
+ spaceId,
88
+ );
76
89
  }
77
90
 
78
91
  private planSql(options: SqlMigrationPlannerPlanOptions): SqlitePlanResult {
@@ -114,6 +127,7 @@ export class SqliteMigrationPlanner
114
127
  from: options.fromContract?.storage.storageHash ?? null,
115
128
  to: options.contract.storage.storageHash,
116
129
  },
130
+ options.spaceId,
117
131
  destination,
118
132
  ),
119
133
  };
@@ -24,6 +24,7 @@ import {
24
24
  buildWriteMarkerStatements,
25
25
  ensureLedgerTableStatement,
26
26
  ensureMarkerTableStatement,
27
+ MARKER_TABLE_NAME,
27
28
  readMarkerStatement,
28
29
  type SqlStatement,
29
30
  } from './statement-builders';
@@ -69,8 +70,11 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
69
70
  await this.beginExclusiveTransaction(driver);
70
71
  let committed = false;
71
72
  try {
72
- await this.ensureControlTables(driver);
73
- const existingMarker = await this.readMarker(driver);
73
+ const ensureResult = await this.ensureControlTables(driver);
74
+ if (!ensureResult.ok) {
75
+ return ensureResult;
76
+ }
77
+ const existingMarker = await this.readMarker(driver, options.plan.spaceId);
74
78
 
75
79
  const markerCheck = this.ensureMarkerCompatibility(existingMarker, options.plan);
76
80
  if (!markerCheck.ok) {
@@ -263,15 +267,52 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
263
267
 
264
268
  private async ensureControlTables(
265
269
  driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],
266
- ): Promise<void> {
270
+ ): Promise<Result<void, SqlMigrationRunnerFailure>> {
271
+ // Pre-1.0 zero-range guardrail: detect a pre-cleanup single-row
272
+ // marker table (no `space` column) and surface a structured failure
273
+ // rather than silently rebuilding the table into the per-space
274
+ // shape. See `specs/framework-mechanism.spec.md § 2`.
275
+ const legacyDetection = await this.detectLegacyMarkerShape(driver);
276
+ if (!legacyDetection.ok) {
277
+ return legacyDetection;
278
+ }
267
279
  await this.executeStatement(driver, ensureMarkerTableStatement);
268
280
  await this.executeStatement(driver, ensureLedgerTableStatement);
281
+ return okVoid();
282
+ }
283
+
284
+ private async detectLegacyMarkerShape(
285
+ driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],
286
+ ): Promise<Result<void, SqlMigrationRunnerFailure>> {
287
+ const tableInfo = await driver.query<{ name: string }>(
288
+ `PRAGMA table_info("${MARKER_TABLE_NAME}")`,
289
+ );
290
+ if (tableInfo.rows.length === 0) {
291
+ return okVoid();
292
+ }
293
+ const columns = new Set(tableInfo.rows.map((row) => row.name));
294
+ if (columns.has('space')) {
295
+ return okVoid();
296
+ }
297
+ return runnerFailure(
298
+ 'LEGACY_MARKER_SHAPE',
299
+ `Legacy marker-table shape detected on ${MARKER_TABLE_NAME} (no \`space\` column). ` +
300
+ 'Prisma Next is in pre-1.0; the previous transitional auto-migration to the per-space-row schema has been removed. ' +
301
+ `Drop \`${MARKER_TABLE_NAME}\` and re-run \`dbInit\` to reinitialise from a clean baseline.`,
302
+ {
303
+ meta: {
304
+ table: MARKER_TABLE_NAME,
305
+ columns: [...columns].sort(),
306
+ },
307
+ },
308
+ );
269
309
  }
270
310
 
271
311
  private async readMarker(
272
312
  driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],
313
+ space: string,
273
314
  ): Promise<ContractMarkerRecord | null> {
274
- const stmt = readMarkerStatement();
315
+ const stmt = readMarkerStatement(space);
275
316
  try {
276
317
  const result = await driver.query<ContractMarkerRow>(stmt.sql, stmt.params);
277
318
  const row = result.rows[0];
@@ -527,6 +568,7 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
527
568
  for (const inv of options.plan.providedInvariants) merged.add(inv);
528
569
  const invariants = Array.from(merged).sort();
529
570
  const writeStatements = buildWriteMarkerStatements({
571
+ space: options.plan.spaceId,
530
572
  storageHash: options.plan.destination.storageHash,
531
573
  profileHash:
532
574
  options.plan.destination.profileHash ??
@@ -1,3 +1,7 @@
1
+ import { APP_SPACE_ID } from '@prisma-next/framework-components/control';
2
+
3
+ export { APP_SPACE_ID };
4
+
1
5
  export interface SqlStatement {
2
6
  readonly sql: string;
3
7
  readonly params: readonly unknown[];
@@ -15,9 +19,18 @@ export const CONTROL_TABLE_NAMES: ReadonlySet<string> = new Set([
15
19
  LEDGER_TABLE_NAME,
16
20
  ]);
17
21
 
22
+ /**
23
+ * Schema for `_prisma_marker`. The `space TEXT PRIMARY KEY` shape
24
+ * supports one row per loaded contract space (`'app'`,
25
+ * `'<extension-id>'`, …); brand-new databases create this shape
26
+ * directly. The migration runner detects pre-1.0 single-row markers
27
+ * (no `space` column) at boot and fails with a structured
28
+ * `LEGACY_MARKER_SHAPE` error rather than auto-rebuilding the table —
29
+ * see `specs/framework-mechanism.spec.md § 2`.
30
+ */
18
31
  export const ensureMarkerTableStatement: SqlStatement = {
19
32
  sql: `CREATE TABLE IF NOT EXISTS _prisma_marker (
20
- id INTEGER PRIMARY KEY CHECK (id = 1),
33
+ space TEXT NOT NULL PRIMARY KEY DEFAULT '${APP_SPACE_ID}',
21
34
  core_hash TEXT NOT NULL,
22
35
  profile_hash TEXT NOT NULL,
23
36
  contract_json TEXT,
@@ -45,7 +58,7 @@ export const ensureLedgerTableStatement: SqlStatement = {
45
58
  params: [],
46
59
  };
47
60
 
48
- export function readMarkerStatement(): SqlStatement {
61
+ export function readMarkerStatement(space: string): SqlStatement {
49
62
  return {
50
63
  sql: `SELECT
51
64
  core_hash,
@@ -57,12 +70,21 @@ export function readMarkerStatement(): SqlStatement {
57
70
  meta,
58
71
  invariants
59
72
  FROM _prisma_marker
60
- WHERE id = ?`,
61
- params: [1],
73
+ WHERE space = ?`,
74
+ params: [space],
62
75
  };
63
76
  }
64
77
 
65
78
  export interface WriteMarkerInput {
79
+ /**
80
+ * Logical space identifier for this marker row. Required at every
81
+ * call site so the type system surfaces every place that needs to
82
+ * thread the value (rather than letting an `?? APP_SPACE_ID`
83
+ * fall-through silently collapse multi-space markers onto the
84
+ * `'app'` row). App-plan callers pass {@link APP_SPACE_ID}
85
+ * (`'app'`); per-extension callers pass the extension's space id.
86
+ */
87
+ readonly space: string;
66
88
  readonly storageHash: string;
67
89
  readonly profileHash: string;
68
90
  readonly contractJson?: unknown;
@@ -84,7 +106,7 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
84
106
  readonly update: SqlStatement;
85
107
  } {
86
108
  const params: readonly unknown[] = [
87
- 1,
109
+ input.space,
88
110
  input.storageHash,
89
111
  input.profileHash,
90
112
  jsonParam(input.contractJson),
@@ -97,7 +119,7 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
97
119
  return {
98
120
  insert: {
99
121
  sql: `INSERT INTO _prisma_marker (
100
- id,
122
+ space,
101
123
  core_hash,
102
124
  profile_hash,
103
125
  contract_json,
@@ -129,7 +151,7 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
129
151
  app_tag = ?,
130
152
  meta = ?,
131
153
  invariants = ?
132
- WHERE id = ?`,
154
+ WHERE space = ?`,
133
155
  params: [
134
156
  input.storageHash,
135
157
  input.profileHash,
@@ -138,7 +160,7 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
138
160
  input.appTag ?? null,
139
161
  jsonParam(input.meta ?? {}),
140
162
  jsonParam(input.invariants),
141
- 1,
163
+ input.space,
142
164
  ],
143
165
  },
144
166
  };
@@ -1,4 +1,5 @@
1
1
  export {
2
+ APP_SPACE_ID,
2
3
  buildLedgerInsertStatement,
3
4
  buildWriteMarkerStatements,
4
5
  CONTROL_TABLE_NAMES,
@@ -1 +0,0 @@
1
- {"version":3,"file":"planner-produced-sqlite-migration-BfIIUtxe.d.mts","names":[],"sources":["../src/core/migrations/planner-produced-sqlite-migration.ts"],"sourcesContent":[],"mappings":";;;;;;;;KAUK,EAAA,GAAK,0BAA0B;UAEnB,8BAAA;EAFZ,SAAE,WAA6B,EAAA,MAAA;EAEnB,SAAA,WAAA,CAAA,EAAA,MAAA;AAKjB;AASoB,cATP,mCAAA,SACH,eAAA,YACG,iCAOO,CAAA;EACV,CAAA,OAAA;EACQ,WAAA,CAAA,KAAA,EAAA,SAFE,mBAEF,EAAA,EAAA,IAAA,EADR,aACQ,EAAA,WAAA,CAAA,EAAA,8BAAA;EAQoB,IAAA,UAAA,CAAA,CAAA,EAAA,SAAA,EAAA,EAAA;EAIf,QAAA,CAAA,CAAA,EAAA,aAAA;EAIO,IAAA,WAAA,CAAA,CAAA,EAAA,8BAAA;EA1BpB,gBAAA,CAAA,CAAA,EAAA,MAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"planner-produced-sqlite-migration-C3AAaQoW.mjs","names":["BASE_IMPORTS: readonly ImportRequirement[]","requirements: ImportRequirement[]","lines: string[]","#calls","#meta","#destination"],"sources":["../src/core/migrations/render-typescript.ts","../src/core/migrations/planner-produced-sqlite-migration.ts"],"sourcesContent":["/**\n * Polymorphic TypeScript emitter for the SQLite migration IR. Mirrors the\n * Postgres `render-typescript.ts` — different base-class + factory module\n * specifier, same overall shape.\n */\n\nimport { detectScaffoldRuntime, shebangLineFor } from '@prisma-next/migration-tools/migration-ts';\nimport { type ImportRequirement, jsonToTsSource, renderImports } from '@prisma-next/ts-render';\nimport type { SqliteOpFactoryCall } from './op-factory-call';\n\nexport interface RenderMigrationMeta {\n readonly from: string | null;\n readonly to: string;\n readonly labels?: readonly string[];\n}\n\n/**\n * Always-present base imports for the rendered scaffold. Both come from\n * `@prisma-next/target-sqlite/migration` so an authored SQLite\n * `migration.ts` only needs a single dependency for its base class and\n * its CLI entrypoint. Mirrors Postgres's `BASE_IMPORTS`.\n *\n * - `Migration` — the target-owned re-export fixes the `SqlMigration`\n * generic to `SqlitePlanTargetDetails` and the abstract `targetId` to\n * `'sqlite'`.\n * - `MigrationCLI` — the migration-file CLI entrypoint, re-exported from\n * `@prisma-next/cli/migration-cli`. Loads `prisma-next.config.ts`,\n * assembles a `ControlStack`, and instantiates the migration class.\n */\nconst BASE_IMPORTS: readonly ImportRequirement[] = [\n { moduleSpecifier: '@prisma-next/target-sqlite/migration', symbol: 'Migration' },\n { moduleSpecifier: '@prisma-next/target-sqlite/migration', symbol: 'MigrationCLI' },\n];\n\nexport function renderCallsToTypeScript(\n calls: ReadonlyArray<SqliteOpFactoryCall>,\n meta: RenderMigrationMeta,\n): string {\n const imports = buildImports(calls);\n const operationsBody = calls.map((c) => c.renderTypeScript()).join(',\\n');\n\n return [\n shebangLineFor(detectScaffoldRuntime()),\n imports,\n '',\n 'export default class M extends Migration {',\n buildDescribeMethod(meta),\n ' override get operations() {',\n ' return [',\n indent(operationsBody, 6),\n ' ];',\n ' }',\n '}',\n '',\n 'MigrationCLI.run(import.meta.url, M);',\n '',\n ].join('\\n');\n}\n\nfunction buildImports(calls: ReadonlyArray<SqliteOpFactoryCall>): string {\n const requirements: ImportRequirement[] = [...BASE_IMPORTS];\n for (const call of calls) {\n for (const req of call.importRequirements()) {\n requirements.push(req);\n }\n }\n return renderImports(requirements);\n}\n\nfunction buildDescribeMethod(meta: RenderMigrationMeta): string {\n const lines: string[] = [];\n lines.push(' override describe() {');\n lines.push(' return {');\n lines.push(` from: ${JSON.stringify(meta.from)},`);\n lines.push(` to: ${JSON.stringify(meta.to)},`);\n if (meta.labels && meta.labels.length > 0) {\n lines.push(` labels: ${jsonToTsSource(meta.labels)},`);\n }\n lines.push(' };');\n lines.push(' }');\n lines.push('');\n return lines.join('\\n');\n}\n\nfunction indent(text: string, spaces: number): string {\n const pad = ' '.repeat(spaces);\n return text\n .split('\\n')\n .map((line) => (line.trim() ? `${pad}${line}` : line))\n .join('\\n');\n}\n","import 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 { SqliteOpFactoryCall } from './op-factory-call';\nimport type { SqlitePlanTargetDetails } from './planner-target-details';\nimport { renderOps } from './render-ops';\nimport { renderCallsToTypeScript } from './render-typescript';\nimport { SqliteMigration } from './sqlite-migration';\n\ntype Op = SqlMigrationPlanOperation<SqlitePlanTargetDetails>;\n\nexport interface SqliteMigrationDestinationInfo {\n readonly storageHash: string;\n readonly profileHash?: string;\n}\n\nexport class TypeScriptRenderableSqliteMigration\n extends SqliteMigration\n implements MigrationPlanWithAuthoringSurface\n{\n readonly #calls: readonly SqliteOpFactoryCall[];\n readonly #meta: MigrationMeta;\n readonly #destination: SqliteMigrationDestinationInfo;\n\n constructor(\n calls: readonly SqliteOpFactoryCall[],\n meta: MigrationMeta,\n destination?: SqliteMigrationDestinationInfo,\n ) {\n super();\n this.#calls = calls;\n this.#meta = meta;\n this.#destination = destination ?? { storageHash: meta.to };\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 override get destination(): SqliteMigrationDestinationInfo {\n return this.#destination;\n }\n\n renderTypeScript(): string {\n return renderCallsToTypeScript(this.#calls, {\n from: this.#meta.from,\n to: this.#meta.to,\n ...ifDefined('labels', this.#meta.labels),\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAMA,eAA6C,CACjD;CAAE,iBAAiB;CAAwC,QAAQ;CAAa,EAChF;CAAE,iBAAiB;CAAwC,QAAQ;CAAgB,CACpF;AAED,SAAgB,wBACd,OACA,MACQ;CACR,MAAM,UAAU,aAAa,MAAM;CACnC,MAAM,iBAAiB,MAAM,KAAK,MAAM,EAAE,kBAAkB,CAAC,CAAC,KAAK,MAAM;AAEzE,QAAO;EACL,eAAe,uBAAuB,CAAC;EACvC;EACA;EACA;EACA,oBAAoB,KAAK;EACzB;EACA;EACA,OAAO,gBAAgB,EAAE;EACzB;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,KAAK,KAAK;;AAGd,SAAS,aAAa,OAAmD;CACvE,MAAMC,eAAoC,CAAC,GAAG,aAAa;AAC3D,MAAK,MAAM,QAAQ,MACjB,MAAK,MAAM,OAAO,KAAK,oBAAoB,CACzC,cAAa,KAAK,IAAI;AAG1B,QAAO,cAAc,aAAa;;AAGpC,SAAS,oBAAoB,MAAmC;CAC9D,MAAMC,QAAkB,EAAE;AAC1B,OAAM,KAAK,0BAA0B;AACrC,OAAM,KAAK,eAAe;AAC1B,OAAM,KAAK,eAAe,KAAK,UAAU,KAAK,KAAK,CAAC,GAAG;AACvD,OAAM,KAAK,aAAa,KAAK,UAAU,KAAK,GAAG,CAAC,GAAG;AACnD,KAAI,KAAK,UAAU,KAAK,OAAO,SAAS,EACtC,OAAM,KAAK,iBAAiB,eAAe,KAAK,OAAO,CAAC,GAAG;AAE7D,OAAM,KAAK,SAAS;AACpB,OAAM,KAAK,MAAM;AACjB,OAAM,KAAK,GAAG;AACd,QAAO,MAAM,KAAK,KAAK;;AAGzB,SAAS,OAAO,MAAc,QAAwB;CACpD,MAAM,MAAM,IAAI,OAAO,OAAO;AAC9B,QAAO,KACJ,MAAM,KAAK,CACX,KAAK,SAAU,KAAK,MAAM,GAAG,GAAG,MAAM,SAAS,KAAM,CACrD,KAAK,KAAK;;;;;ACxEf,IAAa,sCAAb,cACU,gBAEV;CACE,CAASC;CACT,CAASC;CACT,CAASC;CAET,YACE,OACA,MACA,aACA;AACA,SAAO;AACP,QAAKF,QAAS;AACd,QAAKC,OAAQ;AACb,QAAKC,cAAe,eAAe,EAAE,aAAa,KAAK,IAAI;;CAG7D,IAAa,aAA4B;AACvC,SAAO,UAAU,MAAKF,MAAO;;CAG/B,AAAS,WAA0B;AACjC,SAAO,MAAKC;;CAGd,IAAa,cAA8C;AACzD,SAAO,MAAKC;;CAGd,mBAA2B;AACzB,SAAO,wBAAwB,MAAKF,OAAQ;GAC1C,MAAM,MAAKC,KAAM;GACjB,IAAI,MAAKA,KAAM;GACf,GAAG,UAAU,UAAU,MAAKA,KAAM,OAAO;GAC1C,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"statement-builders-B3OGOp7n.mjs","names":["CONTROL_TABLE_NAMES: ReadonlySet<string>","ensureMarkerTableStatement: SqlStatement","ensureLedgerTableStatement: SqlStatement"],"sources":["../src/core/migrations/statement-builders.ts"],"sourcesContent":["export interface SqlStatement {\n readonly sql: string;\n readonly params: readonly unknown[];\n}\n\nexport const MARKER_TABLE_NAME = '_prisma_marker';\nexport const LEDGER_TABLE_NAME = '_prisma_ledger';\n\n/**\n * Control tables the runner creates/manages. The planner must not drop these\n * when reconciling \"extra\" tables against the contract.\n */\nexport const CONTROL_TABLE_NAMES: ReadonlySet<string> = new Set([\n MARKER_TABLE_NAME,\n LEDGER_TABLE_NAME,\n]);\n\nexport const ensureMarkerTableStatement: SqlStatement = {\n sql: `CREATE TABLE IF NOT EXISTS _prisma_marker (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n core_hash TEXT NOT NULL,\n profile_hash TEXT NOT NULL,\n contract_json TEXT,\n canonical_version INTEGER,\n updated_at TEXT NOT NULL DEFAULT (datetime('now')),\n app_tag TEXT,\n meta TEXT 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_ledger (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n created_at TEXT NOT NULL DEFAULT (datetime('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 TEXT,\n contract_json_after TEXT,\n operations TEXT NOT NULL\n )`,\n params: [],\n};\n\nexport function readMarkerStatement(): SqlStatement {\n return {\n sql: `SELECT\n core_hash,\n profile_hash,\n contract_json,\n canonical_version,\n updated_at,\n app_tag,\n meta,\n invariants\n FROM _prisma_marker\n WHERE id = ?`,\n params: [1],\n };\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 * Invariants to write into `marker.invariants`. Stored as a JSON-encoded\n * TEXT array — SQLite has no native array type. The runner is responsible\n * for merging with the existing column (no SQL-side merge here, unlike\n * Postgres) before passing them in: BEGIN EXCLUSIVE on the migration\n * transaction makes the read-then-merge-then-write sequence safe.\n */\n readonly invariants: readonly string[];\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 jsonParam(input.invariants),\n ];\n\n return {\n insert: {\n sql: `INSERT INTO _prisma_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 ?,\n ?,\n ?,\n ?,\n ?,\n datetime('now'),\n ?,\n ?,\n ?\n )`,\n params,\n },\n update: {\n sql: `UPDATE _prisma_marker SET\n core_hash = ?,\n profile_hash = ?,\n contract_json = ?,\n canonical_version = ?,\n updated_at = datetime('now'),\n app_tag = ?,\n meta = ?,\n invariants = ?\n WHERE id = ?`,\n params: [\n input.storageHash,\n input.profileHash,\n jsonParam(input.contractJson),\n input.canonicalVersion ?? null,\n input.appTag ?? null,\n jsonParam(input.meta ?? {}),\n jsonParam(input.invariants),\n 1,\n ],\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_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 ?,\n ?,\n ?,\n ?,\n ?,\n ?,\n ?\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,MAAa,oBAAoB;AACjC,MAAa,oBAAoB;;;;;AAMjC,MAAaA,sBAA2C,IAAI,IAAI,CAC9D,mBACA,kBACD,CAAC;AAEF,MAAaC,6BAA2C;CACtD,KAAK;;;;;;;;;;;CAWL,QAAQ,EAAE;CACX;AAED,MAAaC,6BAA2C;CACtD,KAAK;;;;;;;;;;;CAWL,QAAQ,EAAE;CACX;AAED,SAAgB,sBAAoC;AAClD,QAAO;EACL,KAAK;;;;;;;;;;;EAWL,QAAQ,CAAC,EAAE;EACZ;;AAoBH,SAAgB,2BAA2B,OAGzC;AAYA,QAAO;EACL,QAAQ;GACN,KAAK;;;;;;;;;;;;;;;;;;;;;GAqBL,QAlC+B;IACjC;IACA,MAAM;IACN,MAAM;IACN,UAAU,MAAM,aAAa;IAC7B,MAAM,oBAAoB;IAC1B,MAAM,UAAU;IAChB,UAAU,MAAM,QAAQ,EAAE,CAAC;IAC3B,UAAU,MAAM,WAAW;IAC5B;GA0BE;EACD,QAAQ;GACN,KAAK;;;;;;;;;;GAUL,QAAQ;IACN,MAAM;IACN,MAAM;IACN,UAAU,MAAM,aAAa;IAC7B,MAAM,oBAAoB;IAC1B,MAAM,UAAU;IAChB,UAAU,MAAM,QAAQ,EAAE,CAAC;IAC3B,UAAU,MAAM,WAAW;IAC3B;IACD;GACF;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"}