@prisma-next/target-sqlite 0.14.0 → 0.15.0-dev.1
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/codecs-BGeJavlQ.d.mts.map +1 -1
- package/dist/codecs.mjs +220 -2
- package/dist/codecs.mjs.map +1 -1
- package/dist/contract-free.mjs +1 -1
- package/dist/{control-tables-7KwMyJ6i.mjs → control-tables-D_o8dKRB.mjs} +2 -2
- package/dist/{control-tables-7KwMyJ6i.mjs.map → control-tables-D_o8dKRB.mjs.map} +1 -1
- package/dist/control-tables.mjs +1 -1
- package/dist/control.d.mts +3 -4
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +17 -28
- package/dist/control.mjs.map +1 -1
- package/dist/{descriptor-meta-DxmEeTJ-.mjs → descriptor-meta-D1fap-5t.mjs} +4 -11
- package/dist/descriptor-meta-D1fap-5t.mjs.map +1 -0
- package/dist/descriptor-meta-runtime-BkXK3OjD.mjs +12 -0
- package/dist/descriptor-meta-runtime-BkXK3OjD.mjs.map +1 -0
- package/dist/migration.d.mts +2 -2
- package/dist/migration.mjs +2 -2
- package/dist/native-type-normalizer.mjs +13 -1
- package/dist/native-type-normalizer.mjs.map +1 -0
- package/dist/{op-factory-call-DmdfD1yd.mjs → op-factory-call-CAAyaPyg.mjs} +133 -117
- package/dist/op-factory-call-CAAyaPyg.mjs.map +1 -0
- package/dist/op-factory-call.d.mts +2 -2
- package/dist/op-factory-call.d.mts.map +1 -1
- package/dist/op-factory-call.mjs +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/planner-DNvbcExu.mjs +854 -0
- package/dist/planner-DNvbcExu.mjs.map +1 -0
- package/dist/{planner-produced-sqlite-migration-CpgsY-M9.d.mts → planner-produced-sqlite-migration-DKopa0rd.d.mts} +3 -3
- package/dist/{planner-produced-sqlite-migration-CpgsY-M9.d.mts.map → planner-produced-sqlite-migration-DKopa0rd.d.mts.map} +1 -1
- package/dist/{planner-produced-sqlite-migration-0xPEm3R1.mjs → planner-produced-sqlite-migration-DhAaGkSM.mjs} +46 -17
- package/dist/planner-produced-sqlite-migration-DhAaGkSM.mjs.map +1 -0
- package/dist/planner-produced-sqlite-migration.d.mts +1 -1
- package/dist/planner-produced-sqlite-migration.mjs +1 -1
- package/dist/planner.d.mts +47 -11
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +1 -1
- package/dist/runtime.d.mts +6 -4
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +3 -3
- package/dist/{shared-Dhc8mLK1.d.mts → shared-CaiRyjA0.d.mts} +2 -3
- package/dist/{shared-Dhc8mLK1.d.mts.map → shared-CaiRyjA0.d.mts.map} +1 -1
- package/dist/{sqlite-contract-serializer--iaDgC8e.mjs → sqlite-contract-view-B8qeCczc.mjs} +35 -10
- package/dist/sqlite-contract-view-B8qeCczc.mjs.map +1 -0
- package/dist/sqlite-contract-view-J32K82Gi.d.mts +29 -0
- package/dist/sqlite-contract-view-J32K82Gi.d.mts.map +1 -0
- package/dist/sqlite-migration-DQq045KG.mjs +123 -0
- package/dist/sqlite-migration-DQq045KG.mjs.map +1 -0
- package/dist/{sqlite-migration-DVfhQwN_.d.mts → sqlite-migration-Dzz9p0Gw.d.mts} +35 -6
- package/dist/sqlite-migration-Dzz9p0Gw.d.mts.map +1 -0
- package/package.json +20 -20
- package/src/core/codecs.ts +10 -3
- package/src/core/control-target.ts +13 -18
- package/src/core/errors.ts +14 -12
- package/src/core/migrations/column-ddl-rendering.ts +177 -0
- package/src/core/migrations/diff-database-schema.ts +230 -0
- package/src/core/migrations/issue-planner.ts +366 -488
- package/src/core/migrations/op-factory-call.ts +5 -0
- package/src/core/migrations/operations/shared.ts +3 -5
- package/src/core/migrations/operations/tables.ts +107 -68
- package/src/core/migrations/planner-strategies.ts +118 -137
- package/src/core/migrations/planner.ts +117 -30
- package/src/core/migrations/render-typescript.ts +42 -15
- package/src/core/migrations/runner.ts +4 -10
- package/src/core/migrations/sqlite-migration.ts +69 -31
- package/src/core/sqlite-contract-serializer.ts +29 -14
- package/src/core/sqlite-contract-view.ts +41 -0
- package/src/core/sqlite-schema-verifier.ts +6 -3
- package/src/core/sqlite-unbound-database.ts +8 -8
- package/src/exports/runtime.ts +1 -0
- package/dist/codecs-DsC4OGmU.mjs +0 -220
- package/dist/codecs-DsC4OGmU.mjs.map +0 -1
- package/dist/descriptor-meta-DxmEeTJ-.mjs.map +0 -1
- package/dist/native-type-normalizer-CiSyVmMP.mjs +0 -14
- package/dist/native-type-normalizer-CiSyVmMP.mjs.map +0 -1
- package/dist/op-factory-call-DmdfD1yd.mjs.map +0 -1
- package/dist/planner-Ciq8p_dL.mjs +0 -622
- package/dist/planner-Ciq8p_dL.mjs.map +0 -1
- package/dist/planner-produced-sqlite-migration-0xPEm3R1.mjs.map +0 -1
- package/dist/sqlite-contract-serializer--iaDgC8e.mjs.map +0 -1
- package/dist/sqlite-migration-A0rwqPOG.mjs +0 -92
- package/dist/sqlite-migration-A0rwqPOG.mjs.map +0 -1
- package/dist/sqlite-migration-DVfhQwN_.d.mts.map +0 -1
package/dist/control.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control.mjs","names":[],"sources":["../src/core/migrations/runner.ts","../src/core/sqlite-schema-verifier.ts","../src/core/control-target.ts"],"sourcesContent":["import type { Contract, ContractMarkerRecord } from '@prisma-next/contract/types';\nimport type {\n MigrationOperationPolicy,\n SqlControlFamilyInstance,\n SqlMigrationPlanContractInfo,\n SqlMigrationPlanOperation,\n SqlMigrationPlanOperationStep,\n SqlMigrationRunner,\n SqlMigrationRunnerExecuteOptions,\n SqlMigrationRunnerFailure,\n SqlMigrationRunnerResult,\n 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 { MigrationRunnerResult } from '@prisma-next/framework-components/control';\nimport { APP_SPACE_ID } from '@prisma-next/framework-components/control';\nimport type { SqlControlDriverInstance, SqlStorage } from '@prisma-next/sql-contract/types';\nimport type { SqlExecuteRequest } from '@prisma-next/sql-relational-core/ast';\nimport { blindCast } from '@prisma-next/utils/casts';\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 { MARKER_TABLE_NAME } from '../control-tables';\nimport { parseSqliteDefault } from '../default-normalizer';\nimport { normalizeSqliteNativeType } from '../native-type-normalizer';\nimport type { SqlitePlanTargetDetails } from './planner-target-details';\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 /**\n * Apply the plan against an already-open connection without managing\n * the transaction lifecycle. The caller ({@link SqliteMigrationRunner.execute})\n * owns BEGIN/COMMIT/ROLLBACK and any connection-level setup (FK pragma\n * toggle, FK integrity check).\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 // Materialize any async ops before running checks or executing.\n const planOps = blindCast<\n readonly SqlMigrationPlanOperation<SqlitePlanTargetDetails>[],\n 'ops were produced by the SQLite planner and are SqlMigrationPlanOperation<SqlitePlanTargetDetails>; MigrationPlan.operations uses the wider framework type to accommodate Promise covariance'\n >(await Promise.all(options.plan.operations));\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, planOps);\n if (!policyCheck.ok) return policyCheck;\n\n const ensureResult = await this.ensureControlTables(driver, options.destinationContract);\n if (!ensureResult.ok) return ensureResult;\n const existingMarker = await this.family.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, planOps);\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 const markerResult = await this.upsertMarker(driver, options, existingMarker, space);\n if (!markerResult.ok) return markerResult;\n await this.recordLedgerEntries(driver, options, executedOperations);\n }\n\n return runnerSuccess({\n operationsPlanned: planOps.length,\n operationsExecuted,\n });\n }\n\n async execute(options: {\n readonly driver: SqlControlDriverInstance<string>;\n readonly perSpaceOptions: ReadonlyArray<\n SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>\n >;\n }): Promise<MigrationRunnerResult> {\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: PRAGMA foreign_keys is a no-op inside a transaction, so the\n // toggle has to wrap 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 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 ops: readonly SqlMigrationPlanOperation<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 ops) {\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 contract: Contract<SqlStorage>,\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n const legacyDetection = await this.detectLegacyMarkerShape(driver);\n if (!legacyDetection.ok) {\n return legacyDetection;\n }\n const lowererContext = { contract };\n for (const query of this.family.bootstrapControlTableQueries()) {\n await this.executeStatement(driver, await this.family.lowerAst(query, lowererContext));\n }\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 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<Result<void, SqlMigrationRunnerFailure>> {\n // Pass the plan's incoming invariants verbatim; `updateMarker` unions them\n // with the stored set (TS-side, dialect-uniform) under the runner's\n // BEGIN EXCLUSIVE — no client-side pre-merge here, so there is no\n // double-merge with the SPI's internal accumulate-dedupe.\n const destination = {\n storageHash: options.plan.destination.storageHash,\n profileHash:\n options.plan.destination.profileHash ??\n options.destinationContract.profileHash ??\n options.plan.destination.storageHash,\n invariants: options.plan.providedInvariants ?? [],\n };\n if (!existingMarker) {\n await this.family.initMarker({ driver, space, destination });\n return okVoid();\n }\n const updated = await this.family.updateMarker({\n driver,\n space,\n expectedFrom: existingMarker.storageHash,\n destination,\n });\n if (!updated) {\n return runnerFailure(\n 'MARKER_CAS_FAILURE',\n 'Marker was modified by another process during migration execution.',\n {\n meta: {\n space,\n expectedStorageHash: existingMarker.storageHash,\n destinationStorageHash: options.plan.destination.storageHash,\n },\n },\n );\n }\n return okVoid();\n }\n\n private async recordLedgerEntries(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>,\n executedOperations: readonly SqlMigrationPlanOperation<SqlitePlanTargetDetails>[],\n ): Promise<void> {\n const plan = options.plan;\n const space = plan.spaceId;\n const edges = options.migrationEdges;\n const totalEdgeOps = edges.reduce((sum, edge) => sum + edge.operationCount, 0);\n if (totalEdgeOps !== plan.operations.length) {\n throw new Error(\n `Ledger write: plan.operations length (${plan.operations.length}) does not match sum of migrationEdges operationCount (${totalEdgeOps})`,\n );\n }\n // The ledger records the operations as executed — idempotency-skipped ops\n // are substituted with skip records (empty `execute`) by `applyPlan`, so the\n // journal reflects what actually ran rather than the raw plan.\n let offset = 0;\n for (const edge of edges) {\n const edgeOps = executedOperations.slice(offset, offset + edge.operationCount);\n offset += edge.operationCount;\n await this.family.writeLedgerEntry({\n driver,\n space,\n entry: {\n edgeId: `${edge.from}->${edge.to}`,\n from: edge.from,\n to: edge.to,\n migrationName: edge.dirName,\n migrationHash: edge.migrationHash,\n operations: edgeOps,\n },\n });\n }\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: SqlExecuteRequest,\n ): Promise<void> {\n await driver.query(statement.sql, statement.params);\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 { SqlControlTargetDescriptor } from '@prisma-next/family-sql/control';\nimport { contractToSchemaIR } from '@prisma-next/family-sql/control';\nimport type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';\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(adapter: SqlControlAdapter<'sqlite'>): MigrationPlanner<'sql', 'sqlite'> {\n return createSqliteMigrationPlanner(adapter);\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(adapter: SqlControlAdapter<'sqlite'>) {\n return createSqliteMigrationPlanner(adapter);\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-schema-verifier.ts","../src/core/control-target.ts"],"sourcesContent":["import type { Contract, ContractMarkerRecord } from '@prisma-next/contract/types';\nimport type {\n MigrationOperationPolicy,\n SqlControlFamilyInstance,\n SqlMigrationPlanContractInfo,\n SqlMigrationPlanOperation,\n SqlMigrationPlanOperationStep,\n SqlMigrationRunner,\n SqlMigrationRunnerExecuteOptions,\n SqlMigrationRunnerFailure,\n SqlMigrationRunnerResult,\n SqlMigrationRunnerSuccessValue,\n} from '@prisma-next/family-sql/control';\nimport { runnerFailure, runnerSuccess } from '@prisma-next/family-sql/control';\nimport type { MigrationRunnerResult } from '@prisma-next/framework-components/control';\nimport { APP_SPACE_ID } from '@prisma-next/framework-components/control';\nimport type { SqlControlDriverInstance, SqlStorage } from '@prisma-next/sql-contract/types';\nimport type { SqlExecuteRequest } from '@prisma-next/sql-relational-core/ast';\nimport { blindCast } from '@prisma-next/utils/casts';\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 { MARKER_TABLE_NAME } from '../control-tables';\nimport { verifySqliteDatabaseSchema } from './diff-database-schema';\nimport type { SqlitePlanTargetDetails } from './planner-target-details';\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 /**\n * Apply the plan against an already-open connection without managing\n * the transaction lifecycle. The caller ({@link SqliteMigrationRunner.execute})\n * owns BEGIN/COMMIT/ROLLBACK and any connection-level setup (FK pragma\n * toggle, FK integrity check).\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 // Materialize any async ops before running checks or executing.\n const planOps = blindCast<\n readonly SqlMigrationPlanOperation<SqlitePlanTargetDetails>[],\n 'ops were produced by the SQLite planner and are SqlMigrationPlanOperation<SqlitePlanTargetDetails>; MigrationPlan.operations uses the wider framework type to accommodate Promise covariance'\n >(await Promise.all(options.plan.operations));\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, planOps);\n if (!policyCheck.ok) return policyCheck;\n\n const ensureResult = await this.ensureControlTables(driver, options.destinationContract);\n if (!ensureResult.ok) return ensureResult;\n const existingMarker = await this.family.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, planOps);\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 schemaNode = await this.family.introspect({\n driver,\n contract: options.destinationContract,\n });\n const schemaVerifyResult = verifySqliteDatabaseSchema({\n contract: options.destinationContract,\n actualSchema: schemaNode,\n strict: options.strictVerification ?? true,\n typeMetadataRegistry: this.family.typeMetadataRegistry,\n frameworkComponents: options.frameworkComponents,\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 const markerResult = await this.upsertMarker(driver, options, existingMarker, space);\n if (!markerResult.ok) return markerResult;\n await this.recordLedgerEntries(driver, options, executedOperations);\n }\n\n return runnerSuccess({\n operationsPlanned: planOps.length,\n operationsExecuted,\n });\n }\n\n async execute(options: {\n readonly driver: SqlControlDriverInstance<string>;\n readonly perSpaceOptions: ReadonlyArray<\n SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>\n >;\n }): Promise<MigrationRunnerResult> {\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: PRAGMA foreign_keys is a no-op inside a transaction, so the\n // toggle has to wrap 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 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 ops: readonly SqlMigrationPlanOperation<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 ops) {\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 contract: Contract<SqlStorage>,\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n const legacyDetection = await this.detectLegacyMarkerShape(driver);\n if (!legacyDetection.ok) {\n return legacyDetection;\n }\n const lowererContext = { contract };\n for (const query of this.family.bootstrapControlTableQueries()) {\n await this.executeStatement(driver, await this.family.lowerAst(query, lowererContext));\n }\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 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<Result<void, SqlMigrationRunnerFailure>> {\n // Pass the plan's incoming invariants verbatim; `updateMarker` unions them\n // with the stored set (TS-side, dialect-uniform) under the runner's\n // BEGIN EXCLUSIVE — no client-side pre-merge here, so there is no\n // double-merge with the SPI's internal accumulate-dedupe.\n const destination = {\n storageHash: options.plan.destination.storageHash,\n profileHash:\n options.plan.destination.profileHash ??\n options.destinationContract.profileHash ??\n options.plan.destination.storageHash,\n invariants: options.plan.providedInvariants ?? [],\n };\n if (!existingMarker) {\n await this.family.initMarker({ driver, space, destination });\n return okVoid();\n }\n const updated = await this.family.updateMarker({\n driver,\n space,\n expectedFrom: existingMarker.storageHash,\n destination,\n });\n if (!updated) {\n return runnerFailure(\n 'MARKER_CAS_FAILURE',\n 'Marker was modified by another process during migration execution.',\n {\n meta: {\n space,\n expectedStorageHash: existingMarker.storageHash,\n destinationStorageHash: options.plan.destination.storageHash,\n },\n },\n );\n }\n return okVoid();\n }\n\n private async recordLedgerEntries(\n driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>,\n executedOperations: readonly SqlMigrationPlanOperation<SqlitePlanTargetDetails>[],\n ): Promise<void> {\n const plan = options.plan;\n const space = plan.spaceId;\n const edges = options.migrationEdges;\n const totalEdgeOps = edges.reduce((sum, edge) => sum + edge.operationCount, 0);\n if (totalEdgeOps !== plan.operations.length) {\n throw new Error(\n `Ledger write: plan.operations length (${plan.operations.length}) does not match sum of migrationEdges operationCount (${totalEdgeOps})`,\n );\n }\n // The ledger records the operations as executed — idempotency-skipped ops\n // are substituted with skip records (empty `execute`) by `applyPlan`, so the\n // journal reflects what actually ran rather than the raw plan.\n let offset = 0;\n for (const edge of edges) {\n const edgeOps = executedOperations.slice(offset, offset + edge.operationCount);\n offset += edge.operationCount;\n await this.family.writeLedgerEntry({\n driver,\n space,\n entry: {\n edgeId: `${edge.from}->${edge.to}`,\n from: edge.from,\n to: edge.to,\n migrationName: edge.dirName,\n migrationHash: edge.migrationHash,\n operations: edgeOps,\n },\n });\n }\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: SqlExecuteRequest,\n ): Promise<void> {\n await driver.query(statement.sql, statement.params);\n }\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport { SqlSchemaVerifierBase } from '@prisma-next/family-sql/ir';\nimport type {\n SchemaDiffIssue,\n SchemaVerifyOptions,\n} 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 SchemaDiffIssue[] {\n return [];\n }\n\n protected verifyTargetExtensions(\n _options: SchemaVerifyOptions<Contract<SqlStorage>, SqlSchemaIR>,\n ): readonly SchemaDiffIssue[] {\n return [];\n }\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type { SqlControlTargetDescriptor } from '@prisma-next/family-sql/control';\nimport type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';\nimport type {\n ControlTargetInstance,\n MigrationPlanner,\n MigrationRunner,\n} from '@prisma-next/framework-components/control';\nimport { SqlStorage } from '@prisma-next/sql-contract/types';\nimport {\n relationalNodeEntityKind,\n relationalNodeGranularity,\n} from '@prisma-next/sql-schema-ir/types';\nimport { sqliteTargetDescriptorMeta } from './descriptor-meta';\nimport { diffSqliteSchema, sqliteContractToSchema } from './migrations/diff-database-schema';\nimport { createSqliteMigrationPlanner } from './migrations/planner';\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\nconst sqliteControlTargetDescriptor: SqlControlTargetDescriptor<'sqlite', SqlitePlanTargetDetails> =\n {\n ...sqliteTargetDescriptorMeta,\n contractSerializer: new SqliteContractSerializer(),\n schemaVerifier: new SqliteSchemaVerifier(),\n diffSchema(input) {\n return diffSqliteSchema(input);\n },\n classifySubjectGranularity: relationalNodeGranularity,\n classifyEntityKind: relationalNodeEntityKind,\n migrations: {\n createPlanner(adapter: SqlControlAdapter<'sqlite'>): MigrationPlanner<'sql', 'sqlite'> {\n return createSqliteMigrationPlanner(adapter);\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 sqliteContractToSchema(contract);\n },\n },\n create(): ControlTargetInstance<'sql', 'sqlite'> {\n return {\n familyId: 'sql',\n targetId: 'sqlite',\n };\n },\n createPlanner(adapter: SqlControlAdapter<'sqlite'>) {\n return createSqliteMigrationPlanner(adapter);\n },\n createRunner(family) {\n return createSqliteMigrationRunner(family);\n },\n };\n\nexport default sqliteControlTargetDescriptor;\n"],"mappings":"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"}
|
|
@@ -1,24 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as sqliteTargetDescriptorMetaRuntime } from "./descriptor-meta-runtime-BkXK3OjD.mjs";
|
|
2
2
|
import { UNBOUND_NAMESPACE_ID } from "@prisma-next/framework-components/ir";
|
|
3
|
+
import { temporalAuthoringPresets } from "@prisma-next/family-sql/control";
|
|
3
4
|
//#region src/core/authoring.ts
|
|
4
5
|
const sqliteAuthoringFieldPresets = { temporal: /* @__PURE__ */ temporalAuthoringPresets({
|
|
5
6
|
codecId: "sqlite/datetime@1",
|
|
6
7
|
nativeType: "text"
|
|
7
8
|
}) };
|
|
8
|
-
const sqliteTargetDescriptorMetaRuntime = {
|
|
9
|
-
kind: "target",
|
|
10
|
-
familyId: "sql",
|
|
11
|
-
targetId: "sqlite",
|
|
12
|
-
id: "sqlite",
|
|
13
|
-
version: "0.0.1",
|
|
14
|
-
capabilities: {}
|
|
15
|
-
};
|
|
16
9
|
const sqliteTargetDescriptorMeta = {
|
|
17
10
|
...sqliteTargetDescriptorMetaRuntime,
|
|
18
11
|
defaultNamespaceId: UNBOUND_NAMESPACE_ID,
|
|
19
12
|
authoring: { field: sqliteAuthoringFieldPresets }
|
|
20
13
|
};
|
|
21
14
|
//#endregion
|
|
22
|
-
export {
|
|
15
|
+
export { sqliteTargetDescriptorMeta as t };
|
|
23
16
|
|
|
24
|
-
//# sourceMappingURL=descriptor-meta-
|
|
17
|
+
//# sourceMappingURL=descriptor-meta-D1fap-5t.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptor-meta-D1fap-5t.mjs","names":[],"sources":["../src/core/authoring.ts","../src/core/descriptor-meta.ts"],"sourcesContent":["import { temporalAuthoringPresets } from '@prisma-next/family-sql/control';\nimport type { AuthoringFieldNamespace } from '@prisma-next/framework-components/authoring';\n\nexport const sqliteAuthoringFieldPresets = {\n temporal: /* @__PURE__ */ temporalAuthoringPresets({\n codecId: 'sqlite/datetime@1',\n nativeType: 'text',\n }),\n} as const satisfies AuthoringFieldNamespace;\n","import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';\nimport type { CodecTypes } from '../exports/codec-types';\nimport { sqliteAuthoringFieldPresets } from './authoring';\nimport { sqliteTargetDescriptorMetaRuntime } from './descriptor-meta-runtime';\n\nconst sqliteTargetDescriptorMetaBase = {\n ...sqliteTargetDescriptorMetaRuntime,\n defaultNamespaceId: UNBOUND_NAMESPACE_ID,\n authoring: {\n field: sqliteAuthoringFieldPresets,\n },\n} as const;\n\nexport const sqliteTargetDescriptorMeta: typeof sqliteTargetDescriptorMetaBase & {\n readonly __codecTypes?: CodecTypes;\n} = sqliteTargetDescriptorMetaBase;\n"],"mappings":";;;;AAGA,MAAa,8BAA8B,EACzC,UAA0B,yCAAyB;CACjD,SAAS;CACT,YAAY;AACd,CAAC,EACH;ACKA,MAAa,6BAET;CATF,GAAG;CACH,oBAAoB;CACpB,WAAW,EACT,OAAO,4BACT;AAKE"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const sqliteTargetDescriptorMetaRuntime = {
|
|
2
|
+
kind: "target",
|
|
3
|
+
familyId: "sql",
|
|
4
|
+
targetId: "sqlite",
|
|
5
|
+
id: "sqlite",
|
|
6
|
+
version: "0.0.1",
|
|
7
|
+
capabilities: {}
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { sqliteTargetDescriptorMetaRuntime as t };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=descriptor-meta-runtime-BkXK3OjD.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptor-meta-runtime-BkXK3OjD.mjs","names":[],"sources":["../src/core/descriptor-meta-runtime.ts"],"sourcesContent":["// Runtime-safe slice of the sqlite target descriptor metadata.\n//\n// This file exists separately from ./descriptor-meta on purpose: the runtime\n// plane reads only `kind/familyId/targetId/id/version/capabilities` (plus the\n// `__codecTypes` phantom). The `authoring` slot lives on the pack/control\n// descriptor only, because authoring contributions are consumed at\n// contract-construction time by `assembleAuthoringContributions` (control\n// plane) and the PSL interpreter — never at runtime.\n//\n// Keeping the runtime closure free of the `./authoring` import is what lets\n// the bundler tree-shake `@prisma-next/family-sql/control` (and its\n// transitive `verify-sql-schema` chunk) out of the runtime entry. Do not\n// add an `authoring` field here — if you need to, the pack/control meta in\n// `./descriptor-meta` is the right place. See TML-2766 for context.\nimport type { CodecTypes } from '../exports/codec-types';\n\nconst sqliteTargetDescriptorMetaRuntimeBase = {\n kind: 'target',\n familyId: 'sql',\n targetId: 'sqlite',\n id: 'sqlite',\n version: '0.0.1',\n capabilities: {},\n} as const;\n\nexport const sqliteTargetDescriptorMetaRuntime: typeof sqliteTargetDescriptorMetaRuntimeBase & {\n readonly __codecTypes?: CodecTypes;\n} = sqliteTargetDescriptorMetaRuntimeBase;\n"],"mappings":"AAyBA,MAAa,oCAET;CAVF,MAAM;CACN,UAAU;CACV,UAAU;CACV,IAAI;CACJ,SAAS;CACT,cAAc,CAAC;AAKb"}
|
package/dist/migration.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as Op } from "./shared-
|
|
2
|
-
import { t as SqliteMigration } from "./sqlite-migration-
|
|
1
|
+
import { t as Op } from "./shared-CaiRyjA0.mjs";
|
|
2
|
+
import { t as SqliteMigration } from "./sqlite-migration-Dzz9p0Gw.mjs";
|
|
3
3
|
import { placeholder } from "@prisma-next/errors/migration";
|
|
4
4
|
import { col, fn, foreignKey, lit, primaryKey, unique } from "@prisma-next/sql-relational-core/contract-free";
|
|
5
5
|
import { MigrationCLI } from "@prisma-next/cli/migration-cli";
|
package/dist/migration.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { f as step } from "./op-factory-call-CAAyaPyg.mjs";
|
|
1
2
|
import { t as buildTargetDetails } from "./planner-target-details-H8z9TFDg.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { t as SqliteMigration } from "./sqlite-migration-A0rwqPOG.mjs";
|
|
3
|
+
import { t as SqliteMigration } from "./sqlite-migration-DQq045KG.mjs";
|
|
4
4
|
import { placeholder } from "@prisma-next/errors/migration";
|
|
5
5
|
import { col, fn, foreignKey, lit, primaryKey, unique } from "@prisma-next/sql-relational-core/contract-free";
|
|
6
6
|
import { MigrationCLI } from "@prisma-next/cli/migration-cli";
|
|
@@ -1,2 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/core/native-type-normalizer.ts
|
|
2
|
+
/**
|
|
3
|
+
* Canonicalizes SQLite native-type tokens for verifier comparison.
|
|
4
|
+
* Lives target-side so the planner / runner / adapter all share the same
|
|
5
|
+
* normalization without crossing the `target-sqlite` ↔ `adapter-sqlite`
|
|
6
|
+
* boundary.
|
|
7
|
+
*/
|
|
8
|
+
function normalizeSqliteNativeType(nativeType) {
|
|
9
|
+
return nativeType.trim().toLowerCase();
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
2
12
|
export { normalizeSqliteNativeType };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=native-type-normalizer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-type-normalizer.mjs","names":[],"sources":["../src/core/native-type-normalizer.ts"],"sourcesContent":["/**\n * Canonicalizes SQLite native-type tokens for verifier comparison.\n * Lives target-side so the planner / runner / adapter all share the same\n * normalization without crossing the `target-sqlite` ↔ `adapter-sqlite`\n * boundary.\n */\nexport function normalizeSqliteNativeType(nativeType: string): string {\n return nativeType.trim().toLowerCase();\n}\n"],"mappings":";;;;;;;AAMA,SAAgB,0BAA0B,YAA4B;CACpE,OAAO,WAAW,KAAK,CAAC,CAAC,YAAY;AACvC"}
|
|
@@ -1,61 +1,13 @@
|
|
|
1
|
-
import { n as stripOuterParens } from "./default-normalizer-DuoHj9-O.mjs";
|
|
2
|
-
import { i as tableExistsAst, n as columnExistsAst, r as indexExistsAst, t as createTable } from "./ddl-DrtjQMFK.mjs";
|
|
3
1
|
import { n as escapeLiteral, r as quoteIdentifier } from "./sql-utils-CV8Bdgtc.mjs";
|
|
2
|
+
import { i as tableExistsAst, n as columnExistsAst, r as indexExistsAst, t as createTable } from "./ddl-DrtjQMFK.mjs";
|
|
4
3
|
import { t as buildTargetDetails } from "./planner-target-details-H8z9TFDg.mjs";
|
|
4
|
+
import { n as stripOuterParens } from "./default-normalizer-DuoHj9-O.mjs";
|
|
5
|
+
import { RelationalSchemaNodeKind } from "@prisma-next/sql-schema-ir/types";
|
|
6
|
+
import { blindCast } from "@prisma-next/utils/casts";
|
|
5
7
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
6
8
|
import { errorUnfilledPlaceholder } from "@prisma-next/errors/migration";
|
|
7
9
|
import { TsExpression, jsonToTsSource } from "@prisma-next/ts-render";
|
|
8
10
|
import { REFERENTIAL_ACTION_SQL } from "@prisma-next/sql-contract/referential-action-sql";
|
|
9
|
-
//#region src/core/migrations/operations/shared.ts
|
|
10
|
-
function step(description, sql, params) {
|
|
11
|
-
return {
|
|
12
|
-
description,
|
|
13
|
-
sql,
|
|
14
|
-
...ifDefined("params", params)
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Renders a single column's inline DDL fragment within a `CREATE TABLE`
|
|
19
|
-
* statement. Honours the `inlineAutoincrementPrimaryKey` flag — SQLite
|
|
20
|
-
* treats `INTEGER PRIMARY KEY AUTOINCREMENT` as a special form that aliases
|
|
21
|
-
* `rowid`, and the column must not carry a `DEFAULT` or repeat `NOT NULL`.
|
|
22
|
-
*/
|
|
23
|
-
function renderColumnDefinition(column) {
|
|
24
|
-
const parts = [quoteIdentifier(column.name), column.typeSql];
|
|
25
|
-
if (column.inlineAutoincrementPrimaryKey) parts.push("PRIMARY KEY AUTOINCREMENT");
|
|
26
|
-
else {
|
|
27
|
-
if (column.defaultSql) parts.push(column.defaultSql);
|
|
28
|
-
if (!column.nullable) parts.push("NOT NULL");
|
|
29
|
-
}
|
|
30
|
-
return parts.join(" ");
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Renders an inline FOREIGN KEY constraint clause for a `CREATE TABLE`
|
|
34
|
-
* body. Returns the empty string when `constraint` is false (the FK is
|
|
35
|
-
* tracked at the contract level for index-creation purposes only and must
|
|
36
|
-
* not produce DDL).
|
|
37
|
-
*/
|
|
38
|
-
function renderForeignKeyClause(fk) {
|
|
39
|
-
if (!fk.constraint) return "";
|
|
40
|
-
let sql = `${fk.name ? `CONSTRAINT ${quoteIdentifier(fk.name)} ` : ""}FOREIGN KEY (${fk.columns.map(quoteIdentifier).join(", ")}) REFERENCES ${quoteIdentifier(fk.references.table)} (${fk.references.columns.map(quoteIdentifier).join(", ")})`;
|
|
41
|
-
if (fk.onDelete !== void 0) sql += ` ON DELETE ${REFERENTIAL_ACTION_SQL[fk.onDelete]}`;
|
|
42
|
-
if (fk.onUpdate !== void 0) sql += ` ON UPDATE ${REFERENTIAL_ACTION_SQL[fk.onUpdate]}`;
|
|
43
|
-
return sql;
|
|
44
|
-
}
|
|
45
|
-
//#endregion
|
|
46
|
-
//#region src/core/migrations/operations/columns.ts
|
|
47
|
-
function addColumnExecuteSql(tableName, column) {
|
|
48
|
-
return [
|
|
49
|
-
`ALTER TABLE ${quoteIdentifier(tableName)}`,
|
|
50
|
-
`ADD COLUMN ${quoteIdentifier(column.name)} ${column.typeSql}`,
|
|
51
|
-
column.defaultSql,
|
|
52
|
-
column.nullable ? "" : "NOT NULL"
|
|
53
|
-
].filter(Boolean).join(" ");
|
|
54
|
-
}
|
|
55
|
-
function dropColumnExecuteSql(tableName, columnName) {
|
|
56
|
-
return `ALTER TABLE ${quoteIdentifier(tableName)} DROP COLUMN ${quoteIdentifier(columnName)}`;
|
|
57
|
-
}
|
|
58
|
-
//#endregion
|
|
59
11
|
//#region src/core/migrations/planner-ddl-builders.ts
|
|
60
12
|
const SAFE_NATIVE_TYPE_PATTERN = /^[a-zA-Z][a-zA-Z0-9_ ]*$/;
|
|
61
13
|
function assertSafeNativeType(nativeType) {
|
|
@@ -105,19 +57,6 @@ function buildCreateIndexSql(tableName, indexName, columns, unique = false) {
|
|
|
105
57
|
function buildDropIndexSql(indexName) {
|
|
106
58
|
return `DROP INDEX IF EXISTS ${quoteIdentifier(indexName)}`;
|
|
107
59
|
}
|
|
108
|
-
/**
|
|
109
|
-
* True when the column is rendered inline as `INTEGER PRIMARY KEY
|
|
110
|
-
* AUTOINCREMENT`. Requires the column's default to be `autoincrement()` and
|
|
111
|
-
* the column to be the sole member of the table's primary key — anything
|
|
112
|
-
* else falls back to a separate PRIMARY KEY constraint with a default
|
|
113
|
-
* AUTOINCREMENT semantics expressed elsewhere.
|
|
114
|
-
*/
|
|
115
|
-
function isInlineAutoincrementPrimaryKey(table, columnName) {
|
|
116
|
-
if (table.primaryKey?.columns.length !== 1) return false;
|
|
117
|
-
if (table.primaryKey.columns[0] !== columnName) return false;
|
|
118
|
-
const column = table.columns[columnName];
|
|
119
|
-
return column?.default?.kind === "function" && column.default.expression === "autoincrement()";
|
|
120
|
-
}
|
|
121
60
|
function resolveColumnTypeMetadata(column, storageTypes) {
|
|
122
61
|
if (!column.typeRef) return column;
|
|
123
62
|
const referencedType = storageTypes[column.typeRef];
|
|
@@ -129,6 +68,55 @@ function resolveColumnTypeMetadata(column, storageTypes) {
|
|
|
129
68
|
};
|
|
130
69
|
}
|
|
131
70
|
//#endregion
|
|
71
|
+
//#region src/core/migrations/operations/shared.ts
|
|
72
|
+
function step(description, sql, params) {
|
|
73
|
+
return {
|
|
74
|
+
description,
|
|
75
|
+
sql,
|
|
76
|
+
...ifDefined("params", params)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Renders a single column's inline DDL fragment within a `CREATE TABLE`
|
|
81
|
+
* statement. Honours the `inlineAutoincrementPrimaryKey` flag — SQLite
|
|
82
|
+
* treats `INTEGER PRIMARY KEY AUTOINCREMENT` as a special form that aliases
|
|
83
|
+
* `rowid`, and the column must not carry a `DEFAULT` or repeat `NOT NULL`.
|
|
84
|
+
*/
|
|
85
|
+
function renderColumnDefinition(column) {
|
|
86
|
+
const parts = [quoteIdentifier(column.name), column.typeSql];
|
|
87
|
+
if (column.inlineAutoincrementPrimaryKey) parts.push("PRIMARY KEY AUTOINCREMENT");
|
|
88
|
+
else {
|
|
89
|
+
if (column.defaultSql) parts.push(column.defaultSql);
|
|
90
|
+
if (!column.nullable) parts.push("NOT NULL");
|
|
91
|
+
}
|
|
92
|
+
return parts.join(" ");
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Renders an inline FOREIGN KEY constraint clause for a `CREATE TABLE`
|
|
96
|
+
* body. Every `SqliteForeignKeySpec` is constraint-bearing by construction
|
|
97
|
+
* (`tableSpecFromNode` only ever builds specs from schema-IR FK nodes, which
|
|
98
|
+
* are themselves constraint-only — a non-constraint FK contributes no node).
|
|
99
|
+
*/
|
|
100
|
+
function renderForeignKeyClause(fk) {
|
|
101
|
+
let sql = `${fk.name ? `CONSTRAINT ${quoteIdentifier(fk.name)} ` : ""}FOREIGN KEY (${fk.columns.map(quoteIdentifier).join(", ")}) REFERENCES ${quoteIdentifier(fk.references.table)} (${fk.references.columns.map(quoteIdentifier).join(", ")})`;
|
|
102
|
+
if (fk.onDelete !== void 0) sql += ` ON DELETE ${REFERENTIAL_ACTION_SQL[fk.onDelete]}`;
|
|
103
|
+
if (fk.onUpdate !== void 0) sql += ` ON UPDATE ${REFERENTIAL_ACTION_SQL[fk.onUpdate]}`;
|
|
104
|
+
return sql;
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region src/core/migrations/operations/columns.ts
|
|
108
|
+
function addColumnExecuteSql(tableName, column) {
|
|
109
|
+
return [
|
|
110
|
+
`ALTER TABLE ${quoteIdentifier(tableName)}`,
|
|
111
|
+
`ADD COLUMN ${quoteIdentifier(column.name)} ${column.typeSql}`,
|
|
112
|
+
column.defaultSql,
|
|
113
|
+
column.nullable ? "" : "NOT NULL"
|
|
114
|
+
].filter(Boolean).join(" ");
|
|
115
|
+
}
|
|
116
|
+
function dropColumnExecuteSql(tableName, columnName) {
|
|
117
|
+
return `ALTER TABLE ${quoteIdentifier(tableName)} DROP COLUMN ${quoteIdentifier(columnName)}`;
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
132
120
|
//#region src/core/migrations/operations/tables.ts
|
|
133
121
|
async function tableExistsSteps(lowerer, tableName) {
|
|
134
122
|
const checks = tableExistsAst(tableName);
|
|
@@ -197,25 +185,26 @@ async function recreateTable(args, lowerer) {
|
|
|
197
185
|
};
|
|
198
186
|
}
|
|
199
187
|
/**
|
|
200
|
-
* Build a one-line summary of a recreate-table operation from the schema
|
|
188
|
+
* Build a one-line summary of a recreate-table operation from the schema-diff
|
|
201
189
|
* issues that triggered it. Lives next to `recreateTable` so the planner
|
|
202
190
|
* (which has the issues) can produce the same description the factory
|
|
203
191
|
* used to build inline. Keeping the formatting target-side keeps
|
|
204
|
-
* `RecreateTableCall` issue-free at the IR layer.
|
|
192
|
+
* `RecreateTableCall` issue-free at the IR layer. `SchemaDiffIssue` carries
|
|
193
|
+
* no rendered message, so this renders one from each issue's path.
|
|
205
194
|
*/
|
|
206
195
|
function buildRecreateSummary(tableName, issues) {
|
|
207
|
-
return `Recreates table ${tableName} to apply schema changes: ${issues.map((i) => i.
|
|
196
|
+
return `Recreates table ${tableName} to apply schema changes: ${issues.map((i) => i.path.join("/")).join("; ")}`;
|
|
197
|
+
}
|
|
198
|
+
function nodeKindOf(issue) {
|
|
199
|
+
const node = issue.expected ?? issue.actual;
|
|
200
|
+
if (node === void 0) return void 0;
|
|
201
|
+
return blindCast(node).nodeKind;
|
|
202
|
+
}
|
|
203
|
+
/** Mirrors `SqlColumnIR.isEqualTo`'s type comparison, isolated so a `not-equal` column issue's postcheck can target type drift and nullability drift independently. */
|
|
204
|
+
function columnTypeChanged(expected, actual) {
|
|
205
|
+
if (expected.resolvedNativeType !== void 0 && actual.resolvedNativeType !== void 0) return expected.resolvedNativeType !== actual.resolvedNativeType;
|
|
206
|
+
return expected.nativeType !== actual.nativeType || Boolean(expected.many) !== Boolean(actual.many);
|
|
208
207
|
}
|
|
209
|
-
const COLUMN_LEVEL_ISSUE_KINDS = new Set([
|
|
210
|
-
"nullability_mismatch",
|
|
211
|
-
"default_mismatch",
|
|
212
|
-
"default_missing",
|
|
213
|
-
"extra_default",
|
|
214
|
-
"type_mismatch"
|
|
215
|
-
]);
|
|
216
|
-
const PK_ISSUE_KINDS = new Set(["primary_key_mismatch", "extra_primary_key"]);
|
|
217
|
-
const UNIQUE_ISSUE_KINDS = new Set(["unique_constraint_mismatch", "extra_unique_constraint"]);
|
|
218
|
-
const FK_ISSUE_KINDS = new Set(["foreign_key_mismatch", "extra_foreign_key"]);
|
|
219
208
|
/**
|
|
220
209
|
* Returns the columns the contract expects as the table's primary key. Picks
|
|
221
210
|
* up SQLite's inline `INTEGER PRIMARY KEY AUTOINCREMENT` form when no
|
|
@@ -229,60 +218,86 @@ function expectedPrimaryKeyColumns(spec) {
|
|
|
229
218
|
function quoteSqlList(values) {
|
|
230
219
|
return values.map((v) => `'${escapeLiteral(v)}'`).join(", ");
|
|
231
220
|
}
|
|
221
|
+
function columnNameFromNode(issue) {
|
|
222
|
+
const node = issue.expected ?? issue.actual;
|
|
223
|
+
if (node === void 0) return void 0;
|
|
224
|
+
return blindCast(node).name;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* A column-default issue's own node has no back-reference to its owning
|
|
228
|
+
* column — it's a transient child built by `SqlColumnIR.children()`. The
|
|
229
|
+
* column's id (`column:<name>`) is always the diff path's second-to-last
|
|
230
|
+
* segment for a default issue (`[..., tableId, columnId, 'default']`), so
|
|
231
|
+
* the name is recovered from the path rather than the node.
|
|
232
|
+
*/
|
|
233
|
+
function columnNameFromDefaultIssuePath(issue) {
|
|
234
|
+
const columnId = issue.path[issue.path.length - 2];
|
|
235
|
+
if (columnId === void 0) return void 0;
|
|
236
|
+
return columnId.startsWith("column:") ? columnId.slice(7) : columnId;
|
|
237
|
+
}
|
|
232
238
|
/**
|
|
233
239
|
* Per-issue postchecks verifying the recreated table's shape against the
|
|
234
|
-
*
|
|
235
|
-
* `
|
|
236
|
-
* issues (`
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
* `RecreateTableCall` doesn't have to carry `SchemaIssue` objects through
|
|
240
|
+
* expected spec. Column-level issues (`sql-column` `not-equal`,
|
|
241
|
+
* `sql-column-default` any reason) emit one targeted check each;
|
|
242
|
+
* constraint-level issues (`sql-primary-key`, `sql-unique`, `sql-foreign-key`,
|
|
243
|
+
* any reason) emit one `pragma_*`-driven check per declared constraint in the
|
|
244
|
+
* expected spec, so a recreated table with the right columns but the wrong
|
|
245
|
+
* PK / unique / FK shape fails the postcheck instead of passing silently.
|
|
246
|
+
* Exported so the planner can pre-build the list at construction time and
|
|
247
|
+
* `RecreateTableCall` doesn't have to carry `SchemaDiffIssue` objects through
|
|
243
248
|
* to render time.
|
|
244
249
|
*/
|
|
245
250
|
function buildRecreatePostchecks(tableName, issues, spec) {
|
|
246
251
|
const checks = [];
|
|
247
252
|
const t = escapeLiteral(tableName);
|
|
248
253
|
const byName = new Map(spec.columns.map((c) => [c.name, c]));
|
|
254
|
+
let hasPkIssue = false;
|
|
255
|
+
let hasUniqueIssue = false;
|
|
256
|
+
let hasFkIssue = false;
|
|
249
257
|
for (const issue of issues) {
|
|
250
|
-
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
sql: `SELECT COUNT(*) > 0 FROM pragma_table_info('${t}') WHERE name = '${c}' AND "notnull" = ${wantNotNull ? 1 : 0}`
|
|
258
|
+
const nodeKind = nodeKindOf(issue);
|
|
259
|
+
if (nodeKind === RelationalSchemaNodeKind.column && issue.reason === "not-equal") {
|
|
260
|
+
const columnName = columnNameFromNode(issue);
|
|
261
|
+
if (columnName === void 0) continue;
|
|
262
|
+
const c = escapeLiteral(columnName);
|
|
263
|
+
const expected = blindCast(issue.expected);
|
|
264
|
+
const actual = blindCast(issue.actual);
|
|
265
|
+
if (expected.nullable !== actual.nullable) checks.push({
|
|
266
|
+
description: `verify "${columnName}" nullability on "${tableName}"`,
|
|
267
|
+
sql: `SELECT COUNT(*) > 0 FROM pragma_table_info('${t}') WHERE name = '${c}' AND "notnull" = ${expected.nullable ? 0 : 1}`
|
|
261
268
|
});
|
|
269
|
+
if (columnTypeChanged(expected, actual)) {
|
|
270
|
+
const colSpec = byName.get(columnName);
|
|
271
|
+
if (colSpec) checks.push({
|
|
272
|
+
description: `verify "${columnName}" type on "${tableName}"`,
|
|
273
|
+
sql: `SELECT COUNT(*) > 0 FROM pragma_table_info('${t}') WHERE name = '${c}' AND LOWER(type) = '${escapeLiteral(colSpec.typeSql.toLowerCase())}'`
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
continue;
|
|
262
277
|
}
|
|
263
|
-
if (
|
|
264
|
-
const
|
|
278
|
+
if (nodeKind === RelationalSchemaNodeKind.columnDefault) {
|
|
279
|
+
const columnName = columnNameFromDefaultIssuePath(issue);
|
|
280
|
+
if (columnName === void 0) continue;
|
|
281
|
+
const c = escapeLiteral(columnName);
|
|
282
|
+
if (issue.reason === "not-expected") {
|
|
283
|
+
checks.push({
|
|
284
|
+
description: `verify "${columnName}" has no default on "${tableName}"`,
|
|
285
|
+
sql: `SELECT COUNT(*) > 0 FROM pragma_table_info('${t}') WHERE name = '${c}' AND dflt_value IS NULL`
|
|
286
|
+
});
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
const colSpec = byName.get(columnName);
|
|
265
290
|
const expectedRaw = colSpec?.defaultSql.startsWith("DEFAULT ") ? stripOuterParens(colSpec.defaultSql.slice(8)) : null;
|
|
266
291
|
if (expectedRaw) checks.push({
|
|
267
|
-
description: `verify "${
|
|
292
|
+
description: `verify "${columnName}" default on "${tableName}"`,
|
|
268
293
|
sql: `SELECT COUNT(*) > 0 FROM pragma_table_info('${t}') WHERE name = '${c}' AND dflt_value = '${escapeLiteral(expectedRaw)}'`
|
|
269
294
|
});
|
|
295
|
+
continue;
|
|
270
296
|
}
|
|
271
|
-
if (
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
description: `verify "${issue.column}" type on "${tableName}"`,
|
|
275
|
-
sql: `SELECT COUNT(*) > 0 FROM pragma_table_info('${t}') WHERE name = '${c}' AND LOWER(type) = '${escapeLiteral(colSpec.typeSql.toLowerCase())}'`
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
if (issue.kind === "extra_default") checks.push({
|
|
279
|
-
description: `verify "${issue.column}" has no default on "${tableName}"`,
|
|
280
|
-
sql: `SELECT COUNT(*) > 0 FROM pragma_table_info('${t}') WHERE name = '${c}' AND dflt_value IS NULL`
|
|
281
|
-
});
|
|
297
|
+
if (nodeKind === RelationalSchemaNodeKind.primaryKey) hasPkIssue = true;
|
|
298
|
+
if (nodeKind === RelationalSchemaNodeKind.unique) hasUniqueIssue = true;
|
|
299
|
+
if (nodeKind === RelationalSchemaNodeKind.foreignKey) hasFkIssue = true;
|
|
282
300
|
}
|
|
283
|
-
const hasPkIssue = issues.some((i) => PK_ISSUE_KINDS.has(i.kind));
|
|
284
|
-
const hasUniqueIssue = issues.some((i) => UNIQUE_ISSUE_KINDS.has(i.kind));
|
|
285
|
-
const hasFkIssue = issues.some((i) => FK_ISSUE_KINDS.has(i.kind));
|
|
286
301
|
if (hasPkIssue) {
|
|
287
302
|
const pkColumns = expectedPrimaryKeyColumns(spec);
|
|
288
303
|
const colCount = pkColumns.length;
|
|
@@ -373,6 +388,7 @@ function renderDdlConstraintAsTsCall(constraint) {
|
|
|
373
388
|
const nameOpt = constraint.name ? `, { name: ${jsonToTsSource(constraint.name)} }` : "";
|
|
374
389
|
return `unique(${jsonToTsSource(constraint.columns)}${nameOpt})`;
|
|
375
390
|
}
|
|
391
|
+
case "check-expression": throw new Error(`SQLite does not support expression CHECK constraints (constraint "${constraint.name}"). Scalar-array columns and their element-non-null checks are Postgres-only.`);
|
|
376
392
|
}
|
|
377
393
|
}
|
|
378
394
|
function constraintImportSymbols(constraints) {
|
|
@@ -789,6 +805,6 @@ var RawSqlCall = class extends SqliteOpFactoryCallNode {
|
|
|
789
805
|
}
|
|
790
806
|
};
|
|
791
807
|
//#endregion
|
|
792
|
-
export {
|
|
808
|
+
export { DropColumnCall as a, RawSqlCall as c, buildRecreateSummary as d, step as f, renderDefaultLiteral as h, DataTransformCall as i, RecreateTableCall as l, buildColumnTypeSql as m, CreateIndexCall as n, DropIndexCall as o, buildColumnDefaultSql as p, CreateTableCall as r, DropTableCall as s, AddColumnCall as t, buildRecreatePostchecks as u };
|
|
793
809
|
|
|
794
|
-
//# sourceMappingURL=op-factory-call-
|
|
810
|
+
//# sourceMappingURL=op-factory-call-CAAyaPyg.mjs.map
|