@prisma-next/target-sqlite 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/control.d.mts +42 -1
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +53 -2
- package/dist/control.mjs.map +1 -1
- package/dist/{planner-CKzXAj4i.mjs → planner-B1bodAgY.mjs} +50 -18
- package/dist/planner-B1bodAgY.mjs.map +1 -0
- package/dist/planner.mjs +1 -1
- package/package.json +18 -18
- package/src/core/migrations/issue-planner.ts +16 -15
- package/src/core/migrations/planner-strategies.ts +48 -7
- package/src/core/sqlite-unbound-database.ts +64 -0
- package/src/exports/control.ts +4 -0
- package/dist/planner-CKzXAj4i.mjs.map +0 -1
package/dist/control.d.mts
CHANGED
|
@@ -1,8 +1,49 @@
|
|
|
1
1
|
import { t as SqlitePlanTargetDetails } from "./planner-target-details-vhvZDWK1.mjs";
|
|
2
2
|
import { SqlControlTargetDescriptor } from "@prisma-next/family-sql/control";
|
|
3
|
+
import { StorageTable } from "@prisma-next/sql-contract/types";
|
|
4
|
+
import { NamespaceBase } from "@prisma-next/framework-components/ir";
|
|
3
5
|
|
|
4
6
|
//#region src/core/control-target.d.ts
|
|
5
7
|
declare const sqliteControlTargetDescriptor: SqlControlTargetDescriptor<'sqlite', SqlitePlanTargetDetails>;
|
|
6
8
|
//#endregion
|
|
7
|
-
|
|
9
|
+
//#region src/core/sqlite-unbound-database.d.ts
|
|
10
|
+
/**
|
|
11
|
+
* SQLite target `Namespace` concretion. SQLite has no schema or
|
|
12
|
+
* database-namespacing concept at the SQL level — there is exactly one
|
|
13
|
+
* effective namespace per connection, so the target ships a single
|
|
14
|
+
* singleton bound to the framework's `UNBOUND_NAMESPACE_ID` slot.
|
|
15
|
+
*
|
|
16
|
+
* Qualifier emission elides the prefix entirely: rendered DDL and
|
|
17
|
+
* queries look unqualified (`CREATE TABLE "users" (...)`), matching
|
|
18
|
+
* SQLite's native dialect. Call sites stay polymorphic — they ask the
|
|
19
|
+
* namespace for its qualifier and consume the empty/unqualified result
|
|
20
|
+
* the same way Postgres consumes a `"schema"` prefix.
|
|
21
|
+
*
|
|
22
|
+
* The SQLite PSL interpreter rejects every explicit `namespace { … }`
|
|
23
|
+
* block with a diagnostic naming SQLite; only the implicit
|
|
24
|
+
* `__unspecified__` AST bucket reaches the SQLite interpreter, which
|
|
25
|
+
* lowers it to this singleton.
|
|
26
|
+
*/
|
|
27
|
+
declare class SqliteUnboundDatabase extends NamespaceBase {
|
|
28
|
+
static readonly instance: SqliteUnboundDatabase;
|
|
29
|
+
readonly kind: "database";
|
|
30
|
+
readonly id: "__unbound__";
|
|
31
|
+
readonly tables: Readonly<Record<string, StorageTable>>;
|
|
32
|
+
private constructor();
|
|
33
|
+
qualifier(): string;
|
|
34
|
+
qualifyTable(tableName: string): string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Target-supplied `Namespace` factory the SQLite target plumbs through
|
|
38
|
+
* `defineContract({ createNamespace })`. SQLite has only one
|
|
39
|
+
* effective namespace slot — the framework `UNBOUND_NAMESPACE_ID`
|
|
40
|
+
* sentinel — so the factory always returns the singleton and rejects
|
|
41
|
+
* any other coordinate. The SQL family's defensive validation in
|
|
42
|
+
* `defineContract` already rejects user-declared SQLite namespaces, so
|
|
43
|
+
* this throw is a structural safety net rather than a user-facing
|
|
44
|
+
* surface.
|
|
45
|
+
*/
|
|
46
|
+
declare function sqliteCreateNamespace(id: string): SqliteUnboundDatabase;
|
|
47
|
+
//#endregion
|
|
48
|
+
export { type SqlitePlanTargetDetails, SqliteUnboundDatabase, sqliteControlTargetDescriptor as default, sqliteCreateNamespace };
|
|
8
49
|
//# sourceMappingURL=control.d.mts.map
|
package/dist/control.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/control-target.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/control-target.ts","../src/core/sqlite-unbound-database.ts"],"mappings":";;;;;;cAkCM,6BAAA,EAA+B,0BAAA,WAAqC,uBAAA;;;;;;;AAnBS;;;;;;;;ACSnF;;;;;cAAa,qBAAA,SAA8B,aAAA;EAAA,gBACzB,QAAA,EAAU,qBAAA;EAAA,SAEjB,IAAA;EAAA,SACA,EAAA;EAAA,SACA,MAAA,EAAQ,QAAA,CAAS,MAAA,SAAe,YAAA;EAAA,QAElC,WAAA,CAAA;EAMP,SAAA,CAAA;EAIA,YAAA,CAAa,SAAA;AAAA;;;;;;;;;;;iBAeC,qBAAA,CAAsB,EAAA,WAAa,qBAAA"}
|
package/dist/control.mjs
CHANGED
|
@@ -2,13 +2,14 @@ import { t as sqliteTargetDescriptorMeta } from "./descriptor-meta-CE2Kbn9b.mjs"
|
|
|
2
2
|
import { t as parseSqliteDefault } from "./default-normalizer-3Fccw7yw.mjs";
|
|
3
3
|
import { t as normalizeSqliteNativeType } from "./native-type-normalizer-BlN5XfD-.mjs";
|
|
4
4
|
import { d as renderDefaultLiteral } from "./tables-DGRRJasz.mjs";
|
|
5
|
-
import { n as createSqliteMigrationPlanner } from "./planner-
|
|
5
|
+
import { n as createSqliteMigrationPlanner } from "./planner-B1bodAgY.mjs";
|
|
6
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-Dne-LkAV.mjs";
|
|
7
7
|
import { contractToSchemaIR, runnerFailure, runnerSuccess } from "@prisma-next/family-sql/control";
|
|
8
8
|
import { SqlStorage } from "@prisma-next/sql-contract/types";
|
|
9
9
|
import { verifySqlSchema } from "@prisma-next/family-sql/schema-verify";
|
|
10
10
|
import { notOk, ok, okVoid } from "@prisma-next/utils/result";
|
|
11
11
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
12
|
+
import { NamespaceBase, UNBOUND_NAMESPACE_ID, freezeNode } from "@prisma-next/framework-components/ir";
|
|
12
13
|
import { APP_SPACE_ID } from "@prisma-next/framework-components/control";
|
|
13
14
|
import { parseContractMarkerRow } from "@prisma-next/family-sql/verify";
|
|
14
15
|
import { SqlContractSerializerBase, SqlSchemaVerifierBase } from "@prisma-next/family-sql/ir";
|
|
@@ -475,6 +476,56 @@ const sqliteControlTargetDescriptor = {
|
|
|
475
476
|
}
|
|
476
477
|
};
|
|
477
478
|
//#endregion
|
|
478
|
-
|
|
479
|
+
//#region src/core/sqlite-unbound-database.ts
|
|
480
|
+
/**
|
|
481
|
+
* SQLite target `Namespace` concretion. SQLite has no schema or
|
|
482
|
+
* database-namespacing concept at the SQL level — there is exactly one
|
|
483
|
+
* effective namespace per connection, so the target ships a single
|
|
484
|
+
* singleton bound to the framework's `UNBOUND_NAMESPACE_ID` slot.
|
|
485
|
+
*
|
|
486
|
+
* Qualifier emission elides the prefix entirely: rendered DDL and
|
|
487
|
+
* queries look unqualified (`CREATE TABLE "users" (...)`), matching
|
|
488
|
+
* SQLite's native dialect. Call sites stay polymorphic — they ask the
|
|
489
|
+
* namespace for its qualifier and consume the empty/unqualified result
|
|
490
|
+
* the same way Postgres consumes a `"schema"` prefix.
|
|
491
|
+
*
|
|
492
|
+
* The SQLite PSL interpreter rejects every explicit `namespace { … }`
|
|
493
|
+
* block with a diagnostic naming SQLite; only the implicit
|
|
494
|
+
* `__unspecified__` AST bucket reaches the SQLite interpreter, which
|
|
495
|
+
* lowers it to this singleton.
|
|
496
|
+
*/
|
|
497
|
+
var SqliteUnboundDatabase = class SqliteUnboundDatabase extends NamespaceBase {
|
|
498
|
+
static instance = new SqliteUnboundDatabase();
|
|
499
|
+
kind = "database";
|
|
500
|
+
id = UNBOUND_NAMESPACE_ID;
|
|
501
|
+
tables;
|
|
502
|
+
constructor() {
|
|
503
|
+
super();
|
|
504
|
+
this.tables = Object.freeze({});
|
|
505
|
+
freezeNode(this);
|
|
506
|
+
}
|
|
507
|
+
qualifier() {
|
|
508
|
+
return "";
|
|
509
|
+
}
|
|
510
|
+
qualifyTable(tableName) {
|
|
511
|
+
return `"${tableName}"`;
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
/**
|
|
515
|
+
* Target-supplied `Namespace` factory the SQLite target plumbs through
|
|
516
|
+
* `defineContract({ createNamespace })`. SQLite has only one
|
|
517
|
+
* effective namespace slot — the framework `UNBOUND_NAMESPACE_ID`
|
|
518
|
+
* sentinel — so the factory always returns the singleton and rejects
|
|
519
|
+
* any other coordinate. The SQL family's defensive validation in
|
|
520
|
+
* `defineContract` already rejects user-declared SQLite namespaces, so
|
|
521
|
+
* this throw is a structural safety net rather than a user-facing
|
|
522
|
+
* surface.
|
|
523
|
+
*/
|
|
524
|
+
function sqliteCreateNamespace(id) {
|
|
525
|
+
if (id === UNBOUND_NAMESPACE_ID) return SqliteUnboundDatabase.instance;
|
|
526
|
+
throw new Error(`sqliteCreateNamespace: SQLite has no schema concept; the only valid namespace id is "${UNBOUND_NAMESPACE_ID}" (received "${id}").`);
|
|
527
|
+
}
|
|
528
|
+
//#endregion
|
|
529
|
+
export { SqliteUnboundDatabase, sqliteControlTargetDescriptor as default, sqliteCreateNamespace };
|
|
479
530
|
|
|
480
531
|
//# sourceMappingURL=control.mjs.map
|
package/dist/control.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control.mjs","names":[],"sources":["../src/core/migrations/runner.ts","../src/core/sqlite-contract-serializer.ts","../src/core/sqlite-schema-verifier.ts","../src/core/control-target.ts"],"sourcesContent":["import type { ContractMarkerRecord } from '@prisma-next/contract/types';\nimport type {\n MigrationOperationPolicy,\n MultiSpaceRunnerResult,\n SqlControlFamilyInstance,\n SqlMigrationPlanContractInfo,\n SqlMigrationPlanOperation,\n SqlMigrationPlanOperationStep,\n SqlMigrationRunner,\n SqlMigrationRunnerExecuteOptions,\n SqlMigrationRunnerFailure,\n SqlMigrationRunnerResult,\n SqlMigrationRunnerSuccessValue,\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 type { ControlDriverInstance } from '@prisma-next/framework-components/control';\nimport { APP_SPACE_ID } from '@prisma-next/framework-components/control';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { Result } from '@prisma-next/utils/result';\nimport { notOk, 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) return destinationCheck;\n\n const policyCheck = this.enforcePolicyCompatibility(options.policy, options.plan.operations);\n if (!policyCheck.ok) return policyCheck;\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 result = await this.executeOnConnection(options);\n if (!result.ok) return result;\n\n if (fkWasEnabled) {\n const fkIntegrityCheck = await this.verifyForeignKeyIntegrity(driver);\n if (!fkIntegrityCheck.ok) return fkIntegrityCheck;\n }\n\n await this.commitTransaction(driver);\n committed = true;\n return result;\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 /**\n * Apply the plan against an already-open connection without managing\n * the transaction lifecycle. The caller owns BEGIN/COMMIT/ROLLBACK\n * and any connection-level setup (FK pragma toggle, FK integrity\n * check). Used by the per-space runner orchestration to fan out\n * across contract spaces inside one outer transaction.\n */\n async executeOnConnection(\n options: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>,\n ): Promise<SqlMigrationRunnerResult> {\n const driver = options.driver;\n if (options.space !== undefined && options.space !== options.plan.spaceId) {\n throw new Error(\n `SqlMigrationRunner: options.space (${options.space}) does not match plan.spaceId (${options.plan.spaceId})`,\n );\n }\n const space = options.plan.spaceId;\n\n const destinationCheck = this.ensurePlanMatchesDestinationContract(\n options.plan.destination,\n options.destinationContract,\n );\n if (!destinationCheck.ok) return destinationCheck;\n\n const policyCheck = this.enforcePolicyCompatibility(options.policy, options.plan.operations);\n if (!policyCheck.ok) return policyCheck;\n\n const ensureResult = await this.ensureControlTables(driver);\n if (!ensureResult.ok) return ensureResult;\n const existingMarker = await this.readMarker(driver, space);\n\n const markerCheck = this.ensureMarkerCompatibility(existingMarker, options.plan);\n if (!markerCheck.ok) return markerCheck;\n\n const markerAtDestination = this.markerMatchesDestination(existingMarker, options.plan);\n const isSelfEdge = options.plan.origin?.storageHash === options.plan.destination.storageHash;\n const skipOperations = markerAtDestination && options.plan.origin != null && !isSelfEdge;\n\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) return applyResult;\n operationsExecuted = applyResult.value.operationsExecuted;\n executedOperations = applyResult.value.executedOperations;\n }\n\n if (space === APP_SPACE_ID) {\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: { issues: schemaVerifyResult.schema.issues },\n });\n }\n }\n\n // Self-edge no-op detection: see Postgres runner for the rationale\n // (kept symmetric across both targets).\n const incomingInvariants = options.plan.providedInvariants;\n const existingInvariants = new Set(existingMarker?.invariants ?? []);\n const incomingIsSubsetOfExisting = incomingInvariants.every((id) => existingInvariants.has(id));\n const isSelfEdgeNoOp = isSelfEdge && operationsExecuted === 0 && incomingIsSubsetOfExisting;\n\n if (!isSelfEdgeNoOp) {\n await this.upsertMarker(driver, options, existingMarker, space);\n await this.recordLedgerEntry(driver, options, existingMarker, executedOperations);\n }\n\n return runnerSuccess({\n operationsPlanned: options.plan.operations.length,\n operationsExecuted,\n });\n }\n\n async executeAcrossSpaces(options: {\n readonly driver: ControlDriverInstance<'sql', string>;\n readonly perSpaceOptions: ReadonlyArray<\n SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>\n >;\n }): Promise<MultiSpaceRunnerResult> {\n const driver = options.driver;\n const perSpaceOptions = options.perSpaceOptions;\n\n if (perSpaceOptions.length === 0) {\n return ok({ perSpaceResults: [] });\n }\n\n // FK pragma toggle and the FK integrity check both span the outer\n // transaction — see `execute(...)` for the full rationale.\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 perSpaceResults: Array<{\n space: string;\n value: SqlMigrationRunnerSuccessValue;\n }> = [];\n let lastProcessedSpace: string | undefined;\n for (const spaceOptions of perSpaceOptions) {\n const space = spaceOptions.space ?? spaceOptions.plan.spaceId;\n const result = await this.executeOnConnection({ ...spaceOptions, driver, space });\n if (!result.ok) {\n return notOk({ ...result.failure, failingSpace: space });\n }\n perSpaceResults.push({ space, value: result.value });\n lastProcessedSpace = space;\n }\n\n if (fkWasEnabled) {\n const fkIntegrityCheck = await this.verifyForeignKeyIntegrity(driver);\n if (!fkIntegrityCheck.ok) {\n // Post-loop integrity violations cannot be attributed to a\n // single per-space step (the cumulative effect of all\n // applied plans was needed to reveal the broken\n // reference). Surface the last successfully-applied space\n // so operators can investigate from the most recent\n // migration first.\n return notOk({\n ...fkIntegrityCheck.failure,\n failingSpace: lastProcessedSpace ?? APP_SPACE_ID,\n });\n }\n }\n\n await this.commitTransaction(driver);\n committed = true;\n return ok({ perSpaceResults });\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 space: string,\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,\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 { Contract } from '@prisma-next/contract/types';\nimport { SqlContractSerializerBase } from '@prisma-next/family-sql/ir';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\n\n/**\n * SQLite target `ContractSerializer` concretion. Mirrors the Postgres\n * shape: inherits the full SQL-family deserialization pipeline. Today's\n * SQLite contract shape is the family-shared shape; no target-specific\n * polymorphic `storage.types` factories are registered yet.\n *\n * `serializeContract` falls through to the family-base default —\n * SQLite's contract is JSON-clean today. Once target-only fields land\n * (e.g. per-target derived storage fields) this is the home for\n * stripping them from the persisted envelope.\n */\nexport class SqliteContractSerializer extends SqlContractSerializerBase<Contract<SqlStorage>> {\n constructor() {\n super(new Map());\n }\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport { SqlSchemaVerifierBase } from '@prisma-next/family-sql/ir';\nimport type { SchemaIssue, SchemaVerifyOptions } from '@prisma-next/framework-components/control';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';\n\n/**\n * SQLite target `SchemaVerifier` concretion. Mirrors the Postgres\n * shape: hooks return the empty list pending the call-site migration\n * that routes the existing verifier behaviour through the SPI.\n */\nexport class SqliteSchemaVerifier extends SqlSchemaVerifierBase<Contract<SqlStorage>, SqlSchemaIR> {\n protected verifyCommonSqlSchema(\n _options: SchemaVerifyOptions<Contract<SqlStorage>, SqlSchemaIR>,\n ): readonly SchemaIssue[] {\n return [];\n }\n\n protected verifyTargetExtensions(\n _options: SchemaVerifyOptions<Contract<SqlStorage>, SqlSchemaIR>,\n ): readonly SchemaIssue[] {\n return [];\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 { SqlStorage, type 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';\nimport { SqliteContractSerializer } from './sqlite-contract-serializer';\nimport { SqliteSchemaVerifier } from './sqlite-schema-verifier';\n\nfunction isSqlContract(contract: Contract | null): contract is Contract<SqlStorage> | null {\n return contract === null || contract.storage instanceof SqlStorage;\n}\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 contractSerializer: new SqliteContractSerializer(),\n schemaVerifier: new SqliteSchemaVerifier(),\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 // The framework SPI types `contract` as the generic\n // `Contract | null`. Any contract reaching the sqlite\n // target descriptor is SQL-family by construction (the\n // family contract resolver would have refused to bind a\n // sqlite target otherwise); the `isSqlContract` predicate\n // encodes that invariant at runtime + narrows the generic\n // to `Contract<SqlStorage>` without a blind cast.\n if (!isSqlContract(contract)) {\n throw new Error(\n 'sqliteControlTargetDescriptor.contractToSchema received a non-SQL contract; expected Contract<SqlStorage>',\n );\n }\n return contractToSchemaIR(contract, {\n annotationNamespace: 'sqlite',\n renderDefault: sqliteRenderDefault,\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":[],"sources":["../src/core/migrations/runner.ts","../src/core/sqlite-contract-serializer.ts","../src/core/sqlite-schema-verifier.ts","../src/core/control-target.ts","../src/core/sqlite-unbound-database.ts"],"sourcesContent":["import type { ContractMarkerRecord } from '@prisma-next/contract/types';\nimport type {\n MigrationOperationPolicy,\n MultiSpaceRunnerResult,\n SqlControlFamilyInstance,\n SqlMigrationPlanContractInfo,\n SqlMigrationPlanOperation,\n SqlMigrationPlanOperationStep,\n SqlMigrationRunner,\n SqlMigrationRunnerExecuteOptions,\n SqlMigrationRunnerFailure,\n SqlMigrationRunnerResult,\n SqlMigrationRunnerSuccessValue,\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 type { ControlDriverInstance } from '@prisma-next/framework-components/control';\nimport { APP_SPACE_ID } from '@prisma-next/framework-components/control';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { Result } from '@prisma-next/utils/result';\nimport { notOk, 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) return destinationCheck;\n\n const policyCheck = this.enforcePolicyCompatibility(options.policy, options.plan.operations);\n if (!policyCheck.ok) return policyCheck;\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 result = await this.executeOnConnection(options);\n if (!result.ok) return result;\n\n if (fkWasEnabled) {\n const fkIntegrityCheck = await this.verifyForeignKeyIntegrity(driver);\n if (!fkIntegrityCheck.ok) return fkIntegrityCheck;\n }\n\n await this.commitTransaction(driver);\n committed = true;\n return result;\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 /**\n * Apply the plan against an already-open connection without managing\n * the transaction lifecycle. The caller owns BEGIN/COMMIT/ROLLBACK\n * and any connection-level setup (FK pragma toggle, FK integrity\n * check). Used by the per-space runner orchestration to fan out\n * across contract spaces inside one outer transaction.\n */\n async executeOnConnection(\n options: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>,\n ): Promise<SqlMigrationRunnerResult> {\n const driver = options.driver;\n if (options.space !== undefined && options.space !== options.plan.spaceId) {\n throw new Error(\n `SqlMigrationRunner: options.space (${options.space}) does not match plan.spaceId (${options.plan.spaceId})`,\n );\n }\n const space = options.plan.spaceId;\n\n const destinationCheck = this.ensurePlanMatchesDestinationContract(\n options.plan.destination,\n options.destinationContract,\n );\n if (!destinationCheck.ok) return destinationCheck;\n\n const policyCheck = this.enforcePolicyCompatibility(options.policy, options.plan.operations);\n if (!policyCheck.ok) return policyCheck;\n\n const ensureResult = await this.ensureControlTables(driver);\n if (!ensureResult.ok) return ensureResult;\n const existingMarker = await this.readMarker(driver, space);\n\n const markerCheck = this.ensureMarkerCompatibility(existingMarker, options.plan);\n if (!markerCheck.ok) return markerCheck;\n\n const markerAtDestination = this.markerMatchesDestination(existingMarker, options.plan);\n const isSelfEdge = options.plan.origin?.storageHash === options.plan.destination.storageHash;\n const skipOperations = markerAtDestination && options.plan.origin != null && !isSelfEdge;\n\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) return applyResult;\n operationsExecuted = applyResult.value.operationsExecuted;\n executedOperations = applyResult.value.executedOperations;\n }\n\n if (space === APP_SPACE_ID) {\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: { issues: schemaVerifyResult.schema.issues },\n });\n }\n }\n\n // Self-edge no-op detection: see Postgres runner for the rationale\n // (kept symmetric across both targets).\n const incomingInvariants = options.plan.providedInvariants;\n const existingInvariants = new Set(existingMarker?.invariants ?? []);\n const incomingIsSubsetOfExisting = incomingInvariants.every((id) => existingInvariants.has(id));\n const isSelfEdgeNoOp = isSelfEdge && operationsExecuted === 0 && incomingIsSubsetOfExisting;\n\n if (!isSelfEdgeNoOp) {\n await this.upsertMarker(driver, options, existingMarker, space);\n await this.recordLedgerEntry(driver, options, existingMarker, executedOperations);\n }\n\n return runnerSuccess({\n operationsPlanned: options.plan.operations.length,\n operationsExecuted,\n });\n }\n\n async executeAcrossSpaces(options: {\n readonly driver: ControlDriverInstance<'sql', string>;\n readonly perSpaceOptions: ReadonlyArray<\n SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>\n >;\n }): Promise<MultiSpaceRunnerResult> {\n const driver = options.driver;\n const perSpaceOptions = options.perSpaceOptions;\n\n if (perSpaceOptions.length === 0) {\n return ok({ perSpaceResults: [] });\n }\n\n // FK pragma toggle and the FK integrity check both span the outer\n // transaction — see `execute(...)` for the full rationale.\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 perSpaceResults: Array<{\n space: string;\n value: SqlMigrationRunnerSuccessValue;\n }> = [];\n let lastProcessedSpace: string | undefined;\n for (const spaceOptions of perSpaceOptions) {\n const space = spaceOptions.space ?? spaceOptions.plan.spaceId;\n const result = await this.executeOnConnection({ ...spaceOptions, driver, space });\n if (!result.ok) {\n return notOk({ ...result.failure, failingSpace: space });\n }\n perSpaceResults.push({ space, value: result.value });\n lastProcessedSpace = space;\n }\n\n if (fkWasEnabled) {\n const fkIntegrityCheck = await this.verifyForeignKeyIntegrity(driver);\n if (!fkIntegrityCheck.ok) {\n // Post-loop integrity violations cannot be attributed to a\n // single per-space step (the cumulative effect of all\n // applied plans was needed to reveal the broken\n // reference). Surface the last successfully-applied space\n // so operators can investigate from the most recent\n // migration first.\n return notOk({\n ...fkIntegrityCheck.failure,\n failingSpace: lastProcessedSpace ?? APP_SPACE_ID,\n });\n }\n }\n\n await this.commitTransaction(driver);\n committed = true;\n return ok({ perSpaceResults });\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 space: string,\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,\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 { Contract } from '@prisma-next/contract/types';\nimport { SqlContractSerializerBase } from '@prisma-next/family-sql/ir';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\n\n/**\n * SQLite target `ContractSerializer` concretion. Mirrors the Postgres\n * shape: inherits the full SQL-family deserialization pipeline. Today's\n * SQLite contract shape is the family-shared shape; no target-specific\n * polymorphic `storage.types` factories are registered yet.\n *\n * `serializeContract` falls through to the family-base default —\n * SQLite's contract is JSON-clean today. Once target-only fields land\n * (e.g. per-target derived storage fields) this is the home for\n * stripping them from the persisted envelope.\n */\nexport class SqliteContractSerializer extends SqlContractSerializerBase<Contract<SqlStorage>> {\n constructor() {\n super(new Map());\n }\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport { SqlSchemaVerifierBase } from '@prisma-next/family-sql/ir';\nimport type { SchemaIssue, SchemaVerifyOptions } from '@prisma-next/framework-components/control';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';\n\n/**\n * SQLite target `SchemaVerifier` concretion. Mirrors the Postgres\n * shape: hooks return the empty list pending the call-site migration\n * that routes the existing verifier behaviour through the SPI.\n */\nexport class SqliteSchemaVerifier extends SqlSchemaVerifierBase<Contract<SqlStorage>, SqlSchemaIR> {\n protected verifyCommonSqlSchema(\n _options: SchemaVerifyOptions<Contract<SqlStorage>, SqlSchemaIR>,\n ): readonly SchemaIssue[] {\n return [];\n }\n\n protected verifyTargetExtensions(\n _options: SchemaVerifyOptions<Contract<SqlStorage>, SqlSchemaIR>,\n ): readonly SchemaIssue[] {\n return [];\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 { SqlStorage, type 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';\nimport { SqliteContractSerializer } from './sqlite-contract-serializer';\nimport { SqliteSchemaVerifier } from './sqlite-schema-verifier';\n\nfunction isSqlContract(contract: Contract | null): contract is Contract<SqlStorage> | null {\n return contract === null || contract.storage instanceof SqlStorage;\n}\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 contractSerializer: new SqliteContractSerializer(),\n schemaVerifier: new SqliteSchemaVerifier(),\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 // The framework SPI types `contract` as the generic\n // `Contract | null`. Any contract reaching the sqlite\n // target descriptor is SQL-family by construction (the\n // family contract resolver would have refused to bind a\n // sqlite target otherwise); the `isSqlContract` predicate\n // encodes that invariant at runtime + narrows the generic\n // to `Contract<SqlStorage>` without a blind cast.\n if (!isSqlContract(contract)) {\n throw new Error(\n 'sqliteControlTargetDescriptor.contractToSchema received a non-SQL contract; expected Contract<SqlStorage>',\n );\n }\n return contractToSchemaIR(contract, {\n annotationNamespace: 'sqlite',\n renderDefault: sqliteRenderDefault,\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","import {\n freezeNode,\n NamespaceBase,\n UNBOUND_NAMESPACE_ID,\n} from '@prisma-next/framework-components/ir';\nimport type { StorageTable } from '@prisma-next/sql-contract/types';\n\n/**\n * SQLite target `Namespace` concretion. SQLite has no schema or\n * database-namespacing concept at the SQL level — there is exactly one\n * effective namespace per connection, so the target ships a single\n * singleton bound to the framework's `UNBOUND_NAMESPACE_ID` slot.\n *\n * Qualifier emission elides the prefix entirely: rendered DDL and\n * queries look unqualified (`CREATE TABLE \"users\" (...)`), matching\n * SQLite's native dialect. Call sites stay polymorphic — they ask the\n * namespace for its qualifier and consume the empty/unqualified result\n * the same way Postgres consumes a `\"schema\"` prefix.\n *\n * The SQLite PSL interpreter rejects every explicit `namespace { … }`\n * block with a diagnostic naming SQLite; only the implicit\n * `__unspecified__` AST bucket reaches the SQLite interpreter, which\n * lowers it to this singleton.\n */\nexport class SqliteUnboundDatabase extends NamespaceBase {\n static readonly instance: SqliteUnboundDatabase = new SqliteUnboundDatabase();\n\n readonly kind = 'database' as const;\n readonly id = UNBOUND_NAMESPACE_ID;\n readonly tables: Readonly<Record<string, StorageTable>>;\n\n private constructor() {\n super();\n this.tables = Object.freeze({});\n freezeNode(this);\n }\n\n qualifier(): string {\n return '';\n }\n\n qualifyTable(tableName: string): string {\n return `\"${tableName}\"`;\n }\n}\n\n/**\n * Target-supplied `Namespace` factory the SQLite target plumbs through\n * `defineContract({ createNamespace })`. SQLite has only one\n * effective namespace slot — the framework `UNBOUND_NAMESPACE_ID`\n * sentinel — so the factory always returns the singleton and rejects\n * any other coordinate. The SQL family's defensive validation in\n * `defineContract` already rejects user-declared SQLite namespaces, so\n * this throw is a structural safety net rather than a user-facing\n * surface.\n */\nexport function sqliteCreateNamespace(id: string): SqliteUnboundDatabase {\n if (id === UNBOUND_NAMESPACE_ID) {\n return SqliteUnboundDatabase.instance;\n }\n throw new Error(\n `sqliteCreateNamespace: SQLite has no schema concept; the only valid namespace id is \"${UNBOUND_NAMESPACE_ID}\" (received \"${id}\").`,\n );\n}\n"],"mappings":"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"}
|
|
@@ -8,7 +8,33 @@ import { extractCodecControlHooks, planFieldEventOperations, plannerFailure } fr
|
|
|
8
8
|
import { verifySqlSchema } from "@prisma-next/family-sql/schema-verify";
|
|
9
9
|
import { defaultIndexName } from "@prisma-next/sql-schema-ir/naming";
|
|
10
10
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
11
|
+
import { UNBOUND_NAMESPACE_ID } from "@prisma-next/framework-components/ir";
|
|
11
12
|
//#region src/core/migrations/planner-strategies.ts
|
|
13
|
+
/**
|
|
14
|
+
* Look up a storage table by its explicit namespace coordinate. Returns
|
|
15
|
+
* `undefined` when the namespace has no table by that name (or no such
|
|
16
|
+
* namespace exists). Callers that get `undefined` MUST treat it as an
|
|
17
|
+
* explicit conflict rather than silently falling back to a name-only
|
|
18
|
+
* walk across namespaces — the SQLite target currently has a single
|
|
19
|
+
* namespace, but this helper enforces the explicit-coordinate
|
|
20
|
+
* discipline so a future multi-namespace SQLite shape inherits the
|
|
21
|
+
* conflict-on-stale-coordinate behaviour the Postgres planner already
|
|
22
|
+
* has.
|
|
23
|
+
*/
|
|
24
|
+
function tableAt(storage, namespaceId, tableName) {
|
|
25
|
+
return storage.namespaces[namespaceId]?.tables[tableName];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Default namespace coordinate for an issue that does not carry one
|
|
29
|
+
* explicitly. Hand-crafted unit-test issues fall back to `__unbound__`,
|
|
30
|
+
* the only namespace any single-namespace contract carries —
|
|
31
|
+
* verifier-emitted issues for legacy single-namespace contracts already
|
|
32
|
+
* stamp this id explicitly. Typed structurally so issue variants
|
|
33
|
+
* without a `namespaceId` slot flow through to the same fallback.
|
|
34
|
+
*/
|
|
35
|
+
function resolveNamespaceIdForIssue(issue) {
|
|
36
|
+
return issue.namespaceId ?? UNBOUND_NAMESPACE_ID;
|
|
37
|
+
}
|
|
12
38
|
const WIDENING_ISSUE_KINDS = new Set(["default_mismatch", "default_missing"]);
|
|
13
39
|
const DESTRUCTIVE_ISSUE_KINDS = new Set([
|
|
14
40
|
"extra_default",
|
|
@@ -49,14 +75,15 @@ const recreateTableStrategy = (issues, ctx) => {
|
|
|
49
75
|
if (cls === "destructive") entry.hasDestructive = true;
|
|
50
76
|
} else byTable.set(table, {
|
|
51
77
|
issues: [issue],
|
|
52
|
-
hasDestructive: cls === "destructive"
|
|
78
|
+
hasDestructive: cls === "destructive",
|
|
79
|
+
namespaceId: resolveNamespaceIdForIssue(issue)
|
|
53
80
|
});
|
|
54
81
|
consumed.add(issue);
|
|
55
82
|
}
|
|
56
83
|
if (byTable.size === 0) return { kind: "no_match" };
|
|
57
84
|
const calls = [];
|
|
58
85
|
for (const [tableName, entry] of byTable) {
|
|
59
|
-
const contractTable = ctx.toContract.storage.
|
|
86
|
+
const contractTable = tableAt(ctx.toContract.storage, entry.namespaceId, tableName);
|
|
60
87
|
const schemaTable = ctx.schema.tables[tableName];
|
|
61
88
|
if (!contractTable || !schemaTable) continue;
|
|
62
89
|
const operationClass = entry.hasDestructive ? "destructive" : "widening";
|
|
@@ -74,12 +101,12 @@ const recreateTableStrategy = (issues, ctx) => {
|
|
|
74
101
|
}
|
|
75
102
|
for (const fk of contractTable.foreignKeys) {
|
|
76
103
|
if (fk.index === false) continue;
|
|
77
|
-
const key = fk.columns.join(",");
|
|
104
|
+
const key = fk.source.columns.join(",");
|
|
78
105
|
if (seenIndexColumnKeys.has(key)) continue;
|
|
79
106
|
seenIndexColumnKeys.add(key);
|
|
80
107
|
indexes.push({
|
|
81
|
-
name: defaultIndexName(tableName, fk.columns),
|
|
82
|
-
columns: fk.columns
|
|
108
|
+
name: defaultIndexName(tableName, fk.source.columns),
|
|
109
|
+
columns: fk.source.columns
|
|
83
110
|
});
|
|
84
111
|
}
|
|
85
112
|
calls.push(new RecreateTableCall({
|
|
@@ -126,7 +153,8 @@ const nullabilityTighteningBackfillStrategy = (issues, ctx) => {
|
|
|
126
153
|
if (issue.kind !== "nullability_mismatch") continue;
|
|
127
154
|
if (!issue.table || !issue.column) continue;
|
|
128
155
|
if (issue.expected === "true") continue;
|
|
129
|
-
const
|
|
156
|
+
const namespaceId = resolveNamespaceIdForIssue(issue);
|
|
157
|
+
const column = tableAt(ctx.toContract.storage, namespaceId, issue.table)?.columns[issue.column];
|
|
130
158
|
if (!column || column.nullable === true) continue;
|
|
131
159
|
calls.push(new DataTransformCall(`data_migration.backfill-${issue.table}-${issue.column}`, `Backfill NULLs in "${issue.table}"."${issue.column}" before NOT NULL tightening`, issue.table, issue.column));
|
|
132
160
|
}
|
|
@@ -252,10 +280,10 @@ function toTableSpec(table, storageTypes) {
|
|
|
252
280
|
...u.name !== void 0 ? { name: u.name } : {}
|
|
253
281
|
}));
|
|
254
282
|
const foreignKeys = table.foreignKeys.map((fk) => ({
|
|
255
|
-
columns: fk.columns,
|
|
283
|
+
columns: fk.source.columns,
|
|
256
284
|
references: {
|
|
257
|
-
table: fk.
|
|
258
|
-
columns: fk.
|
|
285
|
+
table: fk.target.tableName,
|
|
286
|
+
columns: fk.target.columns
|
|
259
287
|
},
|
|
260
288
|
constraint: fk.constraint !== false,
|
|
261
289
|
...fk.name !== void 0 ? { name: fk.name } : {},
|
|
@@ -282,8 +310,9 @@ function mapIssueToCall(issue, ctx) {
|
|
|
282
310
|
switch (issue.kind) {
|
|
283
311
|
case "missing_table": {
|
|
284
312
|
if (!issue.table) return notOk(issueConflict("unsupportedOperation", "Missing table issue has no table name"));
|
|
285
|
-
const
|
|
286
|
-
|
|
313
|
+
const namespaceId = resolveNamespaceIdForIssue(issue);
|
|
314
|
+
const contractTable = tableAt(ctx.toContract.storage, namespaceId, issue.table);
|
|
315
|
+
if (!contractTable) return notOk(issueConflict("unsupportedOperation", `Table "${issue.table}" in namespace "${namespaceId}" reported missing but not found in destination contract`));
|
|
287
316
|
const tableSpec = toTableSpec(contractTable, ctx.storageTypes);
|
|
288
317
|
const calls = [new CreateTableCall(issue.table, tableSpec)];
|
|
289
318
|
const declaredIndexColumnKeys = /* @__PURE__ */ new Set();
|
|
@@ -294,25 +323,28 @@ function mapIssueToCall(issue, ctx) {
|
|
|
294
323
|
}
|
|
295
324
|
for (const fk of contractTable.foreignKeys) {
|
|
296
325
|
if (fk.index === false) continue;
|
|
297
|
-
if (declaredIndexColumnKeys.has(fk.columns.join(","))) continue;
|
|
298
|
-
const indexName = defaultIndexName(issue.table, fk.columns);
|
|
299
|
-
calls.push(new CreateIndexCall(issue.table, indexName, fk.columns));
|
|
326
|
+
if (declaredIndexColumnKeys.has(fk.source.columns.join(","))) continue;
|
|
327
|
+
const indexName = defaultIndexName(issue.table, fk.source.columns);
|
|
328
|
+
calls.push(new CreateIndexCall(issue.table, indexName, fk.source.columns));
|
|
300
329
|
}
|
|
301
330
|
return ok(calls);
|
|
302
331
|
}
|
|
303
332
|
case "missing_column": {
|
|
304
333
|
if (!issue.table || !issue.column) return notOk(issueConflict("unsupportedOperation", "Missing column issue has no table/column name"));
|
|
305
|
-
const
|
|
334
|
+
const namespaceId = resolveNamespaceIdForIssue(issue);
|
|
335
|
+
const contractTable2 = tableAt(ctx.toContract.storage, namespaceId, issue.table);
|
|
336
|
+
const column = contractTable2?.columns[issue.column];
|
|
306
337
|
if (!column) return notOk(issueConflict("unsupportedOperation", `Column "${issue.table}"."${issue.column}" not in destination contract`));
|
|
307
|
-
const contractTable =
|
|
338
|
+
const contractTable = contractTable2;
|
|
308
339
|
const columnSpec = toColumnSpec(issue.column, column, ctx.storageTypes, contractTable ? isInlineAutoincrementPrimaryKey(contractTable, issue.column) : false);
|
|
309
340
|
return ok([new AddColumnCall(issue.table, columnSpec)]);
|
|
310
341
|
}
|
|
311
342
|
case "index_mismatch": {
|
|
312
343
|
if (!issue.table) return notOk(issueConflict("indexIncompatible", "Index issue has no table name"));
|
|
313
344
|
if (!isMissing(issue) || !issue.expected) return notOk(issueConflict("indexIncompatible", `Index on "${issue.table}" differs (expected: ${issue.expected}, actual: ${issue.actual})`, { table: issue.table }));
|
|
345
|
+
const namespaceId = resolveNamespaceIdForIssue(issue);
|
|
314
346
|
const columns = issue.expected.split(", ");
|
|
315
|
-
const contractTable = ctx.toContract.storage
|
|
347
|
+
const contractTable = tableAt(ctx.toContract.storage, namespaceId, issue.table);
|
|
316
348
|
if (!contractTable) return notOk(issueConflict("unsupportedOperation", `Table "${issue.table}" not found in destination contract`));
|
|
317
349
|
const indexName = contractTable.indexes.find((idx) => idx.columns.join(",") === columns.join(","))?.name ?? defaultIndexName(issue.table, columns);
|
|
318
350
|
return ok([new CreateIndexCall(issue.table, indexName, columns)]);
|
|
@@ -519,4 +551,4 @@ var SqliteMigrationPlanner = class {
|
|
|
519
551
|
//#endregion
|
|
520
552
|
export { createSqliteMigrationPlanner as n, SqliteMigrationPlanner as t };
|
|
521
553
|
|
|
522
|
-
//# sourceMappingURL=planner-
|
|
554
|
+
//# sourceMappingURL=planner-B1bodAgY.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner-B1bodAgY.mjs","names":[],"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 { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';\nimport type {\n PostgresEnumStorageEntry,\n SqlStorage,\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 { 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 | PostgresEnumStorageEntry>>;\n readonly schema: SqlSchemaIR;\n readonly policy: MigrationOperationPolicy;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;\n}\n\n/**\n * Look up a storage table by its explicit namespace coordinate. Returns\n * `undefined` when the namespace has no table by that name (or no such\n * namespace exists). Callers that get `undefined` MUST treat it as an\n * explicit conflict rather than silently falling back to a name-only\n * walk across namespaces — the SQLite target currently has a single\n * namespace, but this helper enforces the explicit-coordinate\n * discipline so a future multi-namespace SQLite shape inherits the\n * conflict-on-stale-coordinate behaviour the Postgres planner already\n * has.\n */\nexport function tableAt(\n storage: SqlStorage,\n namespaceId: string,\n tableName: string,\n): StorageTable | undefined {\n return storage.namespaces[namespaceId]?.tables[tableName] as StorageTable | undefined;\n}\n\n/**\n * Default namespace coordinate for an issue that does not carry one\n * explicitly. Hand-crafted unit-test issues fall back to `__unbound__`,\n * the only namespace any single-namespace contract carries —\n * verifier-emitted issues for legacy single-namespace contracts already\n * stamp this id explicitly. Typed structurally so issue variants\n * without a `namespaceId` slot flow through to the same fallback.\n */\nexport function resolveNamespaceIdForIssue(issue: { readonly namespaceId?: string }): string {\n return issue.namespaceId ?? UNBOUND_NAMESPACE_ID;\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<\n string,\n { issues: SchemaIssue[]; hasDestructive: boolean; namespaceId: string }\n >();\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, {\n issues: [issue],\n hasDestructive: cls === 'destructive',\n namespaceId: resolveNamespaceIdForIssue(issue),\n });\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 = tableAt(ctx.toContract.storage, entry.namespaceId, 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.source.columns.join(',');\n if (seenIndexColumnKeys.has(key)) continue;\n seenIndexColumnKeys.add(key);\n indexes.push({\n name: defaultIndexName(tableName, fk.source.columns),\n columns: fk.source.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 namespaceId = resolveNamespaceIdForIssue(issue);\n const column = tableAt(ctx.toContract.storage, namespaceId, 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 PostgresEnumStorageEntry,\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 resolveNamespaceIdForIssue,\n type StrategyContext,\n sqlitePlannerStrategies,\n tableAt,\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 | PostgresEnumStorageEntry>>,\n inlineAutoincrementPrimaryKey = false,\n): SqliteColumnSpec {\n const typeSql = buildColumnTypeSql(\n column,\n storageTypes as Record<string, StorageTypeInstance | PostgresEnumStorageEntry>,\n );\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 | PostgresEnumStorageEntry>>,\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.source.columns,\n references: { table: fk.target.tableName, columns: fk.target.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 | PostgresEnumStorageEntry>>;\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: {} };\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 namespaceId = resolveNamespaceIdForIssue(issue);\n const contractTable = tableAt(ctx.toContract.storage, namespaceId, issue.table);\n if (!contractTable) {\n return notOk(\n issueConflict(\n 'unsupportedOperation',\n `Table \"${issue.table}\" in namespace \"${namespaceId}\" 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.source.columns.join(','))) continue;\n const indexName = defaultIndexName(issue.table, fk.source.columns);\n calls.push(new CreateIndexCall(issue.table, indexName, fk.source.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 namespaceId = resolveNamespaceIdForIssue(issue);\n const contractTable2 = tableAt(ctx.toContract.storage, namespaceId, issue.table);\n const column = contractTable2?.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 = contractTable2;\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 namespaceId = resolveNamespaceIdForIssue(issue);\n const columns = issue.expected.split(', ');\n const contractTable = tableAt(ctx.toContract.storage, namespaceId, 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 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 {\n extractCodecControlHooks,\n planFieldEventOperations,\n plannerFailure,\n} 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 // Codec lifecycle hook (T2.2): inline `onFieldEvent`-emitted ops after\n // structural DDL. Sub-spec § 5 fixes the ordering as\n // `structural → added → dropped → altered`, with within-group sorting by\n // `(tableName, fieldName)` deterministic for byte-stable re-emits.\n // Hook fires only at the application emitter — extension-space planning\n // (M2 R2) never reaches this helper.\n const fieldEventOps = planFieldEventOperations({\n priorContract: options.fromContract,\n newContract: options.contract,\n codecHooks,\n });\n // Codec-emitted calls already conform to `OpFactoryCall` — render +\n // toOp + importRequirements ride directly through the same emit path\n // as structural ops, no `RawSqlCall` wrap.\n const calls = [...result.value.calls, ...fieldEventOps];\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 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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"}
|
package/dist/planner.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as createSqliteMigrationPlanner, t as SqliteMigrationPlanner } from "./planner-
|
|
1
|
+
import { n as createSqliteMigrationPlanner, t as SqliteMigrationPlanner } from "./planner-B1bodAgY.mjs";
|
|
2
2
|
export { SqliteMigrationPlanner, createSqliteMigrationPlanner };
|
package/package.json
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/target-sqlite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@prisma-next/cli": "0.
|
|
9
|
-
"@prisma-next/contract": "0.
|
|
10
|
-
"@prisma-next/errors": "0.
|
|
11
|
-
"@prisma-next/family-sql": "0.
|
|
12
|
-
"@prisma-next/framework-components": "0.
|
|
13
|
-
"@prisma-next/migration-tools": "0.
|
|
14
|
-
"@prisma-next/sql-contract": "0.
|
|
15
|
-
"@prisma-next/sql-errors": "0.
|
|
16
|
-
"@prisma-next/sql-relational-core": "0.
|
|
17
|
-
"@prisma-next/sql-runtime": "0.
|
|
18
|
-
"@prisma-next/sql-schema-ir": "0.
|
|
19
|
-
"@prisma-next/ts-render": "0.
|
|
20
|
-
"@prisma-next/utils": "0.
|
|
8
|
+
"@prisma-next/cli": "0.10.0",
|
|
9
|
+
"@prisma-next/contract": "0.10.0",
|
|
10
|
+
"@prisma-next/errors": "0.10.0",
|
|
11
|
+
"@prisma-next/family-sql": "0.10.0",
|
|
12
|
+
"@prisma-next/framework-components": "0.10.0",
|
|
13
|
+
"@prisma-next/migration-tools": "0.10.0",
|
|
14
|
+
"@prisma-next/sql-contract": "0.10.0",
|
|
15
|
+
"@prisma-next/sql-errors": "0.10.0",
|
|
16
|
+
"@prisma-next/sql-relational-core": "0.10.0",
|
|
17
|
+
"@prisma-next/sql-runtime": "0.10.0",
|
|
18
|
+
"@prisma-next/sql-schema-ir": "0.10.0",
|
|
19
|
+
"@prisma-next/ts-render": "0.10.0",
|
|
20
|
+
"@prisma-next/utils": "0.10.0",
|
|
21
21
|
"@standard-schema/spec": "1.1.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@prisma-next/driver-sqlite": "0.
|
|
25
|
-
"@prisma-next/test-utils": "0.
|
|
26
|
-
"@prisma-next/tsconfig": "0.
|
|
27
|
-
"@prisma-next/tsdown": "0.
|
|
24
|
+
"@prisma-next/driver-sqlite": "0.10.0",
|
|
25
|
+
"@prisma-next/test-utils": "0.10.0",
|
|
26
|
+
"@prisma-next/tsconfig": "0.10.0",
|
|
27
|
+
"@prisma-next/tsdown": "0.10.0",
|
|
28
28
|
"tsdown": "0.22.0",
|
|
29
29
|
"typescript": "5.9.3",
|
|
30
30
|
"vitest": "4.1.6"
|
|
@@ -50,8 +50,10 @@ import {
|
|
|
50
50
|
} from './planner-ddl-builders';
|
|
51
51
|
import {
|
|
52
52
|
type CallMigrationStrategy,
|
|
53
|
+
resolveNamespaceIdForIssue,
|
|
53
54
|
type StrategyContext,
|
|
54
55
|
sqlitePlannerStrategies,
|
|
56
|
+
tableAt,
|
|
55
57
|
} from './planner-strategies';
|
|
56
58
|
import { CONTROL_TABLE_NAMES } from './statement-builders';
|
|
57
59
|
|
|
@@ -239,8 +241,8 @@ export function toTableSpec(
|
|
|
239
241
|
...(u.name !== undefined ? { name: u.name } : {}),
|
|
240
242
|
}));
|
|
241
243
|
const foreignKeys: SqliteForeignKeySpec[] = table.foreignKeys.map((fk) => ({
|
|
242
|
-
columns: fk.columns,
|
|
243
|
-
references: { table: fk.
|
|
244
|
+
columns: fk.source.columns,
|
|
245
|
+
references: { table: fk.target.tableName, columns: fk.target.columns },
|
|
244
246
|
constraint: fk.constraint !== false,
|
|
245
247
|
...(fk.name !== undefined ? { name: fk.name } : {}),
|
|
246
248
|
...(fk.onDelete !== undefined ? { onDelete: fk.onDelete } : {}),
|
|
@@ -297,12 +299,13 @@ function mapIssueToCall(
|
|
|
297
299
|
issueConflict('unsupportedOperation', 'Missing table issue has no table name'),
|
|
298
300
|
);
|
|
299
301
|
}
|
|
300
|
-
const
|
|
302
|
+
const namespaceId = resolveNamespaceIdForIssue(issue);
|
|
303
|
+
const contractTable = tableAt(ctx.toContract.storage, namespaceId, issue.table);
|
|
301
304
|
if (!contractTable) {
|
|
302
305
|
return notOk(
|
|
303
306
|
issueConflict(
|
|
304
307
|
'unsupportedOperation',
|
|
305
|
-
`Table "${issue.table}" reported missing but not found in destination contract`,
|
|
308
|
+
`Table "${issue.table}" in namespace "${namespaceId}" reported missing but not found in destination contract`,
|
|
306
309
|
),
|
|
307
310
|
);
|
|
308
311
|
}
|
|
@@ -316,9 +319,9 @@ function mapIssueToCall(
|
|
|
316
319
|
}
|
|
317
320
|
for (const fk of contractTable.foreignKeys) {
|
|
318
321
|
if (fk.index === false) continue;
|
|
319
|
-
if (declaredIndexColumnKeys.has(fk.columns.join(','))) continue;
|
|
320
|
-
const indexName = defaultIndexName(issue.table, fk.columns);
|
|
321
|
-
calls.push(new CreateIndexCall(issue.table, indexName, fk.columns));
|
|
322
|
+
if (declaredIndexColumnKeys.has(fk.source.columns.join(','))) continue;
|
|
323
|
+
const indexName = defaultIndexName(issue.table, fk.source.columns);
|
|
324
|
+
calls.push(new CreateIndexCall(issue.table, indexName, fk.source.columns));
|
|
322
325
|
}
|
|
323
326
|
return ok(calls);
|
|
324
327
|
}
|
|
@@ -329,7 +332,9 @@ function mapIssueToCall(
|
|
|
329
332
|
issueConflict('unsupportedOperation', 'Missing column issue has no table/column name'),
|
|
330
333
|
);
|
|
331
334
|
}
|
|
332
|
-
const
|
|
335
|
+
const namespaceId = resolveNamespaceIdForIssue(issue);
|
|
336
|
+
const contractTable2 = tableAt(ctx.toContract.storage, namespaceId, issue.table);
|
|
337
|
+
const column = contractTable2?.columns[issue.column];
|
|
333
338
|
if (!column) {
|
|
334
339
|
return notOk(
|
|
335
340
|
issueConflict(
|
|
@@ -338,7 +343,7 @@ function mapIssueToCall(
|
|
|
338
343
|
),
|
|
339
344
|
);
|
|
340
345
|
}
|
|
341
|
-
const contractTable =
|
|
346
|
+
const contractTable = contractTable2;
|
|
342
347
|
const columnSpec = toColumnSpec(
|
|
343
348
|
issue.column,
|
|
344
349
|
column,
|
|
@@ -361,8 +366,9 @@ function mapIssueToCall(
|
|
|
361
366
|
),
|
|
362
367
|
);
|
|
363
368
|
}
|
|
369
|
+
const namespaceId = resolveNamespaceIdForIssue(issue);
|
|
364
370
|
const columns = issue.expected.split(', ');
|
|
365
|
-
const contractTable = ctx.toContract.storage
|
|
371
|
+
const contractTable = tableAt(ctx.toContract.storage, namespaceId, issue.table);
|
|
366
372
|
if (!contractTable) {
|
|
367
373
|
return notOk(
|
|
368
374
|
issueConflict(
|
|
@@ -371,11 +377,6 @@ function mapIssueToCall(
|
|
|
371
377
|
),
|
|
372
378
|
);
|
|
373
379
|
}
|
|
374
|
-
// Use the explicit-index name if one is declared for these columns;
|
|
375
|
-
// otherwise fall back to `defaultIndexName` (which is also what
|
|
376
|
-
// `verifySqlSchema` synthesizes for FK-backing indexes). Whether the
|
|
377
|
-
// missing index originates from `contractTable.indexes` or from an FK
|
|
378
|
-
// with `index: true` doesn't change the emitted DDL.
|
|
379
380
|
const explicitIndex = contractTable.indexes.find(
|
|
380
381
|
(idx) => idx.columns.join(',') === columns.join(','),
|
|
381
382
|
);
|
|
@@ -21,9 +21,11 @@ import type {
|
|
|
21
21
|
} from '@prisma-next/family-sql/control';
|
|
22
22
|
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
23
23
|
import type { SchemaIssue } from '@prisma-next/framework-components/control';
|
|
24
|
+
import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
24
25
|
import type {
|
|
25
26
|
PostgresEnumStorageEntry,
|
|
26
27
|
SqlStorage,
|
|
28
|
+
StorageTable,
|
|
27
29
|
StorageTypeInstance,
|
|
28
30
|
} from '@prisma-next/sql-contract/types';
|
|
29
31
|
import { defaultIndexName } from '@prisma-next/sql-schema-ir/naming';
|
|
@@ -43,6 +45,37 @@ export interface StrategyContext {
|
|
|
43
45
|
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
|
|
44
46
|
}
|
|
45
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Look up a storage table by its explicit namespace coordinate. Returns
|
|
50
|
+
* `undefined` when the namespace has no table by that name (or no such
|
|
51
|
+
* namespace exists). Callers that get `undefined` MUST treat it as an
|
|
52
|
+
* explicit conflict rather than silently falling back to a name-only
|
|
53
|
+
* walk across namespaces — the SQLite target currently has a single
|
|
54
|
+
* namespace, but this helper enforces the explicit-coordinate
|
|
55
|
+
* discipline so a future multi-namespace SQLite shape inherits the
|
|
56
|
+
* conflict-on-stale-coordinate behaviour the Postgres planner already
|
|
57
|
+
* has.
|
|
58
|
+
*/
|
|
59
|
+
export function tableAt(
|
|
60
|
+
storage: SqlStorage,
|
|
61
|
+
namespaceId: string,
|
|
62
|
+
tableName: string,
|
|
63
|
+
): StorageTable | undefined {
|
|
64
|
+
return storage.namespaces[namespaceId]?.tables[tableName] as StorageTable | undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Default namespace coordinate for an issue that does not carry one
|
|
69
|
+
* explicitly. Hand-crafted unit-test issues fall back to `__unbound__`,
|
|
70
|
+
* the only namespace any single-namespace contract carries —
|
|
71
|
+
* verifier-emitted issues for legacy single-namespace contracts already
|
|
72
|
+
* stamp this id explicitly. Typed structurally so issue variants
|
|
73
|
+
* without a `namespaceId` slot flow through to the same fallback.
|
|
74
|
+
*/
|
|
75
|
+
export function resolveNamespaceIdForIssue(issue: { readonly namespaceId?: string }): string {
|
|
76
|
+
return issue.namespaceId ?? UNBOUND_NAMESPACE_ID;
|
|
77
|
+
}
|
|
78
|
+
|
|
46
79
|
export type CallMigrationStrategy = (
|
|
47
80
|
issues: readonly SchemaIssue[],
|
|
48
81
|
context: StrategyContext,
|
|
@@ -91,7 +124,10 @@ function classifyIssue(issue: SchemaIssue): 'widening' | 'destructive' | null {
|
|
|
91
124
|
* consumed are removed so `mapIssueToCall` doesn't double-handle them.
|
|
92
125
|
*/
|
|
93
126
|
export const recreateTableStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
94
|
-
const byTable = new Map<
|
|
127
|
+
const byTable = new Map<
|
|
128
|
+
string,
|
|
129
|
+
{ issues: SchemaIssue[]; hasDestructive: boolean; namespaceId: string }
|
|
130
|
+
>();
|
|
95
131
|
const consumed = new Set<SchemaIssue>();
|
|
96
132
|
|
|
97
133
|
for (const issue of issues) {
|
|
@@ -105,7 +141,11 @@ export const recreateTableStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
|
105
141
|
entry.issues.push(issue);
|
|
106
142
|
if (cls === 'destructive') entry.hasDestructive = true;
|
|
107
143
|
} else {
|
|
108
|
-
byTable.set(table, {
|
|
144
|
+
byTable.set(table, {
|
|
145
|
+
issues: [issue],
|
|
146
|
+
hasDestructive: cls === 'destructive',
|
|
147
|
+
namespaceId: resolveNamespaceIdForIssue(issue),
|
|
148
|
+
});
|
|
109
149
|
}
|
|
110
150
|
consumed.add(issue);
|
|
111
151
|
}
|
|
@@ -114,7 +154,7 @@ export const recreateTableStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
|
114
154
|
|
|
115
155
|
const calls: SqliteOpFactoryCall[] = [];
|
|
116
156
|
for (const [tableName, entry] of byTable) {
|
|
117
|
-
const contractTable = ctx.toContract.storage.
|
|
157
|
+
const contractTable = tableAt(ctx.toContract.storage, entry.namespaceId, tableName);
|
|
118
158
|
const schemaTable = ctx.schema.tables[tableName];
|
|
119
159
|
if (!contractTable || !schemaTable) continue;
|
|
120
160
|
const operationClass: MigrationOperationClass = entry.hasDestructive
|
|
@@ -138,12 +178,12 @@ export const recreateTableStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
|
138
178
|
}
|
|
139
179
|
for (const fk of contractTable.foreignKeys) {
|
|
140
180
|
if (fk.index === false) continue;
|
|
141
|
-
const key = fk.columns.join(',');
|
|
181
|
+
const key = fk.source.columns.join(',');
|
|
142
182
|
if (seenIndexColumnKeys.has(key)) continue;
|
|
143
183
|
seenIndexColumnKeys.add(key);
|
|
144
184
|
indexes.push({
|
|
145
|
-
name: defaultIndexName(tableName, fk.columns),
|
|
146
|
-
columns: fk.columns,
|
|
185
|
+
name: defaultIndexName(tableName, fk.source.columns),
|
|
186
|
+
columns: fk.source.columns,
|
|
147
187
|
});
|
|
148
188
|
}
|
|
149
189
|
|
|
@@ -206,7 +246,8 @@ export const nullabilityTighteningBackfillStrategy: CallMigrationStrategy = (iss
|
|
|
206
246
|
// needs no backfill.
|
|
207
247
|
if (issue.expected === 'true') continue;
|
|
208
248
|
|
|
209
|
-
const
|
|
249
|
+
const namespaceId = resolveNamespaceIdForIssue(issue);
|
|
250
|
+
const column = tableAt(ctx.toContract.storage, namespaceId, issue.table)?.columns[issue.column];
|
|
210
251
|
if (!column || column.nullable === true) continue;
|
|
211
252
|
|
|
212
253
|
calls.push(
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
freezeNode,
|
|
3
|
+
NamespaceBase,
|
|
4
|
+
UNBOUND_NAMESPACE_ID,
|
|
5
|
+
} from '@prisma-next/framework-components/ir';
|
|
6
|
+
import type { StorageTable } from '@prisma-next/sql-contract/types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* SQLite target `Namespace` concretion. SQLite has no schema or
|
|
10
|
+
* database-namespacing concept at the SQL level — there is exactly one
|
|
11
|
+
* effective namespace per connection, so the target ships a single
|
|
12
|
+
* singleton bound to the framework's `UNBOUND_NAMESPACE_ID` slot.
|
|
13
|
+
*
|
|
14
|
+
* Qualifier emission elides the prefix entirely: rendered DDL and
|
|
15
|
+
* queries look unqualified (`CREATE TABLE "users" (...)`), matching
|
|
16
|
+
* SQLite's native dialect. Call sites stay polymorphic — they ask the
|
|
17
|
+
* namespace for its qualifier and consume the empty/unqualified result
|
|
18
|
+
* the same way Postgres consumes a `"schema"` prefix.
|
|
19
|
+
*
|
|
20
|
+
* The SQLite PSL interpreter rejects every explicit `namespace { … }`
|
|
21
|
+
* block with a diagnostic naming SQLite; only the implicit
|
|
22
|
+
* `__unspecified__` AST bucket reaches the SQLite interpreter, which
|
|
23
|
+
* lowers it to this singleton.
|
|
24
|
+
*/
|
|
25
|
+
export class SqliteUnboundDatabase extends NamespaceBase {
|
|
26
|
+
static readonly instance: SqliteUnboundDatabase = new SqliteUnboundDatabase();
|
|
27
|
+
|
|
28
|
+
readonly kind = 'database' as const;
|
|
29
|
+
readonly id = UNBOUND_NAMESPACE_ID;
|
|
30
|
+
readonly tables: Readonly<Record<string, StorageTable>>;
|
|
31
|
+
|
|
32
|
+
private constructor() {
|
|
33
|
+
super();
|
|
34
|
+
this.tables = Object.freeze({});
|
|
35
|
+
freezeNode(this);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
qualifier(): string {
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
qualifyTable(tableName: string): string {
|
|
43
|
+
return `"${tableName}"`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Target-supplied `Namespace` factory the SQLite target plumbs through
|
|
49
|
+
* `defineContract({ createNamespace })`. SQLite has only one
|
|
50
|
+
* effective namespace slot — the framework `UNBOUND_NAMESPACE_ID`
|
|
51
|
+
* sentinel — so the factory always returns the singleton and rejects
|
|
52
|
+
* any other coordinate. The SQL family's defensive validation in
|
|
53
|
+
* `defineContract` already rejects user-declared SQLite namespaces, so
|
|
54
|
+
* this throw is a structural safety net rather than a user-facing
|
|
55
|
+
* surface.
|
|
56
|
+
*/
|
|
57
|
+
export function sqliteCreateNamespace(id: string): SqliteUnboundDatabase {
|
|
58
|
+
if (id === UNBOUND_NAMESPACE_ID) {
|
|
59
|
+
return SqliteUnboundDatabase.instance;
|
|
60
|
+
}
|
|
61
|
+
throw new Error(
|
|
62
|
+
`sqliteCreateNamespace: SQLite has no schema concept; the only valid namespace id is "${UNBOUND_NAMESPACE_ID}" (received "${id}").`,
|
|
63
|
+
);
|
|
64
|
+
}
|
package/src/exports/control.ts
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"planner-CKzXAj4i.mjs","names":[],"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 {\n PostgresEnumStorageEntry,\n SqlStorage,\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 { 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 | PostgresEnumStorageEntry>>;\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 PostgresEnumStorageEntry,\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 | PostgresEnumStorageEntry>>,\n inlineAutoincrementPrimaryKey = false,\n): SqliteColumnSpec {\n const typeSql = buildColumnTypeSql(\n column,\n storageTypes as Record<string, StorageTypeInstance | PostgresEnumStorageEntry>,\n );\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 | PostgresEnumStorageEntry>>,\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 | PostgresEnumStorageEntry>>;\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: {} };\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 {\n extractCodecControlHooks,\n planFieldEventOperations,\n plannerFailure,\n} 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 // Codec lifecycle hook (T2.2): inline `onFieldEvent`-emitted ops after\n // structural DDL. Sub-spec § 5 fixes the ordering as\n // `structural → added → dropped → altered`, with within-group sorting by\n // `(tableName, fieldName)` deterministic for byte-stable re-emits.\n // Hook fires only at the application emitter — extension-space planning\n // (M2 R2) never reaches this helper.\n const fieldEventOps = planFieldEventOperations({\n priorContract: options.fromContract,\n newContract: options.contract,\n codecHooks,\n });\n // Codec-emitted calls already conform to `OpFactoryCall` — render +\n // toOp + importRequirements ride directly through the same emit path\n // as structural ops, no `RawSqlCall` wrap.\n const calls = [...result.value.calls, ...fieldEventOps];\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 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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"}
|