@prisma-next/target-postgres 0.11.0-dev.9 → 0.12.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/codec-types-CRlHq7Cz.d.mts.map +1 -1
- package/dist/codecs-Dud5KDNk.d.mts.map +1 -1
- package/dist/codecs.d.mts.map +1 -1
- package/dist/codecs.mjs.map +1 -1
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +16 -39
- package/dist/control.mjs.map +1 -1
- package/dist/data-transform-CdtGUWp2.mjs.map +1 -1
- package/dist/data-transform-bmOKkygi.d.mts.map +1 -1
- package/dist/{default-normalizer-DHCsbfjc.mjs → default-normalizer-CRscvhS5.mjs} +1 -1
- package/dist/{default-normalizer-DHCsbfjc.mjs.map → default-normalizer-CRscvhS5.mjs.map} +1 -1
- package/dist/default-normalizer.d.mts.map +1 -1
- package/dist/default-normalizer.mjs +1 -1
- package/dist/descriptor-meta-DLA2xV6B.mjs.map +1 -1
- package/dist/enum-planning-Dz0Ye3Lb.mjs +0 -0
- package/dist/enum-planning-Dz0Ye3Lb.mjs.map +1 -0
- package/dist/enum-planning.d.mts +41 -3
- package/dist/enum-planning.d.mts.map +1 -1
- package/dist/enum-planning.mjs +2 -2
- package/dist/errors--zafB5_n.mjs.map +1 -1
- package/dist/errors.d.mts.map +1 -1
- package/dist/{issue-planner-Ct9xNqbr.mjs → issue-planner-ByQhUzS4.mjs} +111 -54
- package/dist/issue-planner-ByQhUzS4.mjs.map +1 -0
- package/dist/issue-planner.d.mts.map +1 -1
- package/dist/issue-planner.mjs +1 -1
- package/dist/migration.d.mts.map +1 -1
- package/dist/migration.mjs +1 -1
- package/dist/migration.mjs.map +1 -1
- package/dist/{native-type-normalizer-DMikJJ1V.mjs → native-type-normalizer-ClNPq__-.mjs} +1 -1
- package/dist/{native-type-normalizer-DMikJJ1V.mjs.map → native-type-normalizer-ClNPq__-.mjs.map} +1 -1
- package/dist/native-type-normalizer.d.mts.map +1 -1
- package/dist/native-type-normalizer.mjs +1 -1
- package/dist/{op-factory-call-CB6tPJ3f.mjs → op-factory-call-B0WNg30h.mjs} +2 -2
- package/dist/{op-factory-call-CB6tPJ3f.mjs.map → op-factory-call-B0WNg30h.mjs.map} +1 -1
- package/dist/op-factory-call-Drccm_JD.d.mts.map +1 -1
- package/dist/op-factory-call.mjs +1 -1
- package/dist/pack.d.mts.map +1 -1
- package/dist/{planner-DlhK35aV.mjs → planner-ClF0y0YR.mjs} +17 -25
- package/dist/planner-ClF0y0YR.mjs.map +1 -0
- package/dist/{planner-ddl-builders--0TmW6Ey.mjs → planner-ddl-builders-BxRCSn_b.mjs} +3 -3
- package/dist/{planner-ddl-builders--0TmW6Ey.mjs.map → planner-ddl-builders-BxRCSn_b.mjs.map} +1 -1
- package/dist/planner-ddl-builders.d.mts.map +1 -1
- package/dist/planner-ddl-builders.mjs +1 -1
- package/dist/planner-identity-values-ojX-6cPV.mjs.map +1 -1
- package/dist/planner-identity-values.d.mts.map +1 -1
- package/dist/{planner-produced-postgres-migration-TJWH2m_x.mjs → planner-produced-postgres-migration-N1yqYg20.mjs} +3 -5
- package/dist/planner-produced-postgres-migration-N1yqYg20.mjs.map +1 -0
- package/dist/planner-produced-postgres-migration-p-VKkCia.d.mts.map +1 -1
- package/dist/planner-produced-postgres-migration.mjs +1 -1
- package/dist/planner-schema-lookup-BGyukuzG.mjs.map +1 -1
- package/dist/planner-schema-lookup.d.mts.map +1 -1
- package/dist/{planner-sql-checks-BDtJQ9WD.mjs → planner-sql-checks-D3H-xOO1.mjs} +3 -3
- package/dist/{planner-sql-checks-BDtJQ9WD.mjs.map → planner-sql-checks-D3H-xOO1.mjs.map} +1 -1
- package/dist/planner-sql-checks.d.mts.map +1 -1
- package/dist/planner-sql-checks.mjs +1 -1
- package/dist/planner-target-details-CIj61DUj.d.mts.map +1 -1
- package/dist/planner.d.mts +1 -6
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +1 -1
- package/dist/{postgres-contract-serializer-CYct4Y7O.mjs → postgres-contract-serializer-YJvjKrmo.mjs} +2 -2
- package/dist/postgres-contract-serializer-YJvjKrmo.mjs.map +1 -0
- package/dist/postgres-enum-type-CNhPTDhy.d.mts.map +1 -1
- package/dist/postgres-enum-type-DS-KLVRH.mjs.map +1 -1
- package/dist/postgres-migration-uADmx0dW.mjs.map +1 -1
- package/dist/{postgres-schema-BL0RAvew.mjs → postgres-schema-Bm7vjlOv.mjs} +12 -19
- package/dist/postgres-schema-Bm7vjlOv.mjs.map +1 -0
- package/dist/render-ops-BC2PtCkj.mjs.map +1 -1
- package/dist/render-ops.d.mts.map +1 -1
- package/dist/{render-typescript-CI1wbgUc.mjs → render-typescript-CPk7hhWH.mjs} +2 -3
- package/dist/render-typescript-CPk7hhWH.mjs.map +1 -0
- package/dist/render-typescript.d.mts +0 -1
- package/dist/render-typescript.d.mts.map +1 -1
- package/dist/render-typescript.mjs +1 -1
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +1 -1
- package/dist/runtime.mjs.map +1 -1
- package/dist/shared-ByhSooBS.d.mts.map +1 -1
- package/dist/{sql-utils-BewXAnsG.mjs → sql-utils-B_ruBD-M.mjs} +1 -1
- package/dist/{sql-utils-BewXAnsG.mjs.map → sql-utils-B_ruBD-M.mjs.map} +1 -1
- package/dist/sql-utils.d.mts.map +1 -1
- package/dist/sql-utils.mjs +1 -1
- package/dist/statement-builders-vImtdfmM.mjs.map +1 -1
- package/dist/statement-builders.d.mts +1 -1
- package/dist/statement-builders.d.mts.map +1 -1
- package/dist/{tables-Vhh4k5yz.mjs → tables-Dcb2q9zV.mjs} +3 -3
- package/dist/{tables-Vhh4k5yz.mjs.map → tables-Dcb2q9zV.mjs.map} +1 -1
- package/dist/types-D-XIpzHA.d.mts.map +1 -1
- package/dist/types.d.mts +10 -14
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +1 -1
- package/package.json +29 -18
- package/src/core/migrations/enum-planning.ts +134 -14
- package/src/core/migrations/issue-planner.ts +20 -11
- package/src/core/migrations/planner-produced-postgres-migration.ts +0 -2
- package/src/core/migrations/planner-strategies.ts +138 -40
- package/src/core/migrations/planner.ts +9 -21
- package/src/core/migrations/render-typescript.ts +1 -5
- package/src/core/migrations/runner.ts +20 -61
- package/src/core/migrations/statement-builders.ts +1 -1
- package/src/core/migrations/verify-postgres-namespaces.ts +6 -6
- package/src/core/postgres-contract-serializer.ts +4 -4
- package/src/core/postgres-schema.ts +10 -20
- package/src/exports/control.ts +16 -0
- package/src/exports/enum-planning.ts +5 -0
- package/dist/enum-planning-Bqp96iIw.mjs +0 -63
- package/dist/enum-planning-Bqp96iIw.mjs.map +0 -1
- package/dist/issue-planner-Ct9xNqbr.mjs.map +0 -1
- package/dist/planner-DlhK35aV.mjs.map +0 -1
- package/dist/planner-produced-postgres-migration-TJWH2m_x.mjs.map +0 -1
- package/dist/postgres-contract-serializer-CYct4Y7O.mjs.map +0 -1
- package/dist/postgres-schema-BL0RAvew.mjs.map +0 -1
- package/dist/render-typescript-CI1wbgUc.mjs.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/postgres-schema-verifier.ts","../src/exports/control.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 { ControlDriverInstance } from '@prisma-next/framework-components/control';\nimport { APP_SPACE_ID } from '@prisma-next/framework-components/control';\nimport { SqlQueryError } from '@prisma-next/sql-errors';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { Result } from '@prisma-next/utils/result';\nimport { notOk, ok, okVoid } from '@prisma-next/utils/result';\nimport { parsePostgresDefault } from '../default-normalizer';\nimport { normalizeSchemaNativeType } from '../native-type-normalizer';\nimport { readExistingEnumValues } from './enum-planning';\nimport type { PostgresPlanTargetDetails } from './planner-target-details';\nimport {\n buildLedgerInsertStatement,\n buildMergeMarkerStatements,\n ensureLedgerTableStatement,\n ensureMarkerTableStatement,\n ensurePrismaContractSchemaStatement,\n type SqlStatement,\n} from './statement-builders';\n\ninterface RunnerConfig {\n readonly defaultSchema: string;\n}\n\ninterface ApplyPlanSuccessValue {\n readonly operationsExecuted: number;\n readonly executedOperations: readonly SqlMigrationPlanOperation<PostgresPlanTargetDetails>[];\n}\n\nconst DEFAULT_CONFIG: RunnerConfig = {\n defaultSchema: 'public',\n};\n\nconst LOCK_DOMAIN = 'prisma_next.contract.marker';\n\n/**\n * Deep clones and freezes a record object to prevent mutation.\n * Recursively clones nested objects and arrays to ensure complete isolation.\n */\nfunction cloneAndFreezeRecord<T extends Record<string, unknown>>(value: T): T {\n const cloned: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value)) {\n if (val === null || val === undefined) {\n cloned[key] = val;\n } else if (Array.isArray(val)) {\n // Clone array (shallow clone of array elements)\n cloned[key] = Object.freeze([...val]);\n } else if (typeof val === 'object') {\n // Recursively clone nested objects\n cloned[key] = cloneAndFreezeRecord(val as Record<string, unknown>);\n } else {\n // Primitives are copied as-is\n cloned[key] = val;\n }\n }\n return Object.freeze(cloned) as T;\n}\n\nexport function createPostgresMigrationRunner(\n family: SqlControlFamilyInstance,\n config: Partial<RunnerConfig> = {},\n): SqlMigrationRunner<PostgresPlanTargetDetails> {\n return new PostgresMigrationRunner(family, { ...DEFAULT_CONFIG, ...config });\n}\n\nclass PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDetails> {\n constructor(\n private readonly family: SqlControlFamilyInstance,\n private readonly config: RunnerConfig,\n ) {}\n\n async execute(\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n ): Promise<SqlMigrationRunnerResult> {\n const driver = options.driver;\n\n // Static checks fail fast before any transaction work — no point\n // burning a BEGIN/ROLLBACK round-trip on a destination-contract\n // mismatch the caller can fix locally.\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 await this.beginTransaction(driver);\n let committed = false;\n try {\n const result = await this.executeOnConnection(options);\n if (!result.ok) {\n return result;\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 }\n\n /**\n * Body of the migration runner without transaction management. The\n * caller (single-space `execute(...)` above, or the multi-space\n * outer-tx orchestrator at the SQL family level) owns the\n * `BEGIN`/`COMMIT`/`ROLLBACK` lifecycle.\n */\n async executeOnConnection(\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n ): Promise<SqlMigrationRunnerResult> {\n const schema = options.schemaName ?? this.config.defaultSchema;\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 const lockKey = `${LOCK_DOMAIN}:${schema}:${space}`;\n\n // Static checks (idempotent — safe to run again when the caller is\n // `execute(...)` because the cost is a single object comparison).\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 await this.acquireLock(driver, lockKey);\n const ensureResult = await this.ensureControlTables(driver);\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 let applyValue: ApplyPlanSuccessValue;\n\n if (skipOperations) {\n applyValue = { operationsExecuted: 0, executedOperations: [] };\n } else {\n const applyResult = await this.applyPlan(driver, options);\n if (!applyResult.ok) return applyResult;\n applyValue = applyResult.value;\n }\n\n // Schema verification on app-space only — extension spaces don't\n // own user-facing tables in the live schema, and `verifySqlSchema`\n // matches the destination contract against the database, which\n // would flag every app-space table as \"extra\" when called against\n // an extension contract.\n if (space === APP_SPACE_ID) {\n const schemaIR = await this.family.introspect({\n driver,\n contract: options.destinationContract,\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: parsePostgresDefault,\n normalizeNativeType: normalizeSchemaNativeType,\n resolveExistingEnumValues: (schema, enumType) =>\n readExistingEnumValues(schema, enumType.nativeType),\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 const incomingInvariants = options.plan.providedInvariants ?? [];\n const existingInvariants = new Set(existingMarker?.invariants ?? []);\n const incomingIsSubsetOfExisting = incomingInvariants.every((id) => existingInvariants.has(id));\n const isSelfEdgeNoOp =\n isSelfEdge && applyValue.operationsExecuted === 0 && incomingIsSubsetOfExisting;\n\n if (!isSelfEdgeNoOp) {\n await this.upsertMarker(driver, options, existingMarker, space);\n await this.recordLedgerEntry(driver, options, existingMarker, applyValue.executedOperations);\n }\n\n return runnerSuccess({\n operationsPlanned: options.plan.operations.length,\n operationsExecuted: applyValue.operationsExecuted,\n });\n }\n\n async executeAcrossSpaces(options: {\n readonly driver: ControlDriverInstance<'sql', string>;\n readonly perSpaceOptions: ReadonlyArray<\n SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>\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 await this.beginTransaction(driver);\n let committed = false;\n try {\n const perSpaceResults: Array<{\n space: string;\n value: SqlMigrationRunnerSuccessValue;\n }> = [];\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 }\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 }\n\n private async applyPlan(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n ): Promise<Result<ApplyPlanSuccessValue, SqlMigrationRunnerFailure>> {\n const checks = options.executionChecks;\n const runPrechecks = checks?.prechecks !== false; // Default true\n const runPostchecks = checks?.postchecks !== false; // Default true\n const runIdempotency = checks?.idempotencyChecks !== false; // Default true\n\n let operationsExecuted = 0;\n const executedOperations: Array<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> = [];\n for (const operation of options.plan.operations) {\n options.callbacks?.onOperationStart?.(operation);\n try {\n // Idempotency probe: only run if both postchecks and idempotency checks are enabled\n if (runPostchecks && runIdempotency) {\n const postcheckAlreadySatisfied = await this.expectationsAreSatisfied(\n driver,\n operation.postcheck,\n );\n if (postcheckAlreadySatisfied) {\n executedOperations.push(this.createPostcheckPreSatisfiedSkipRecord(operation));\n continue;\n }\n }\n\n // Prechecks: only run if enabled\n if (runPrechecks) {\n const precheckResult = await this.runExpectationSteps(\n driver,\n operation.precheck,\n operation,\n 'precheck',\n );\n if (!precheckResult.ok) {\n return precheckResult;\n }\n }\n\n const executeResult = await this.runExecuteSteps(driver, operation.execute, operation);\n if (!executeResult.ok) {\n return executeResult;\n }\n\n // Postchecks: only run if enabled\n if (runPostchecks) {\n const postcheckResult = await this.runExpectationSteps(\n driver,\n operation.postcheck,\n operation,\n 'postcheck',\n );\n if (!postcheckResult.ok) {\n return postcheckResult;\n }\n }\n\n executedOperations.push(operation);\n operationsExecuted += 1;\n } finally {\n options.callbacks?.onOperationComplete?.(operation);\n }\n }\n return ok({ operationsExecuted, executedOperations });\n }\n\n private async ensureControlTables(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n await this.executeStatement(driver, ensurePrismaContractSchemaStatement);\n // Pre-1.0 zero-range guardrail: detect a pre-cleanup single-row\n // marker table (no `space` column) and surface a structured failure\n // rather than silently auto-migrating it to the per-space shape.\n // See `specs/framework-mechanism.spec.md § 2`.\n const legacyDetection = await this.detectLegacyMarkerShape(driver);\n if (!legacyDetection.ok) {\n return legacyDetection;\n }\n await this.executeStatement(driver, ensureMarkerTableStatement);\n await this.executeStatement(driver, ensureLedgerTableStatement);\n return okVoid();\n }\n\n private async detectLegacyMarkerShape(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n const result = await driver.query<{ column_name: string }>(\n `select column_name\n from information_schema.columns\n where table_schema = 'prisma_contract'\n and table_name = 'marker'`,\n );\n if (result.rows.length === 0) {\n return okVoid();\n }\n const columns = new Set(result.rows.map((row) => row.column_name));\n if (columns.has('space')) {\n return okVoid();\n }\n return runnerFailure(\n 'LEGACY_MARKER_SHAPE',\n 'Legacy marker-table shape detected on prisma_contract.marker (no `space` column). ' +\n 'Prisma Next is in pre-1.0; the previous transitional auto-migration to the per-space-row schema has been removed. ' +\n 'Drop `prisma_contract.marker` and re-run `dbInit` to reinitialise from a clean baseline.',\n {\n meta: {\n table: 'prisma_contract.marker',\n columns: [...columns].sort(),\n },\n },\n );\n }\n\n private async runExpectationSteps(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n operation: SqlMigrationPlanOperation<PostgresPlanTargetDetails>,\n phase: 'precheck' | 'postcheck',\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n for (const step of steps) {\n const result = await driver.query(step.sql, step.params ?? []);\n if (!this.stepResultIsTrue(result.rows)) {\n const code = phase === 'precheck' ? 'PRECHECK_FAILED' : 'POSTCHECK_FAILED';\n return runnerFailure(\n code,\n `Operation ${operation.id} failed during ${phase}: ${step.description}`,\n {\n meta: {\n operationId: operation.id,\n phase,\n stepDescription: step.description,\n },\n },\n );\n }\n }\n return okVoid();\n }\n\n private async runExecuteSteps(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n operation: SqlMigrationPlanOperation<PostgresPlanTargetDetails>,\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n for (const step of steps) {\n try {\n await driver.query(step.sql, step.params ?? []);\n } catch (error: unknown) {\n if (SqlQueryError.is(error)) {\n return runnerFailure(\n 'EXECUTION_FAILED',\n `Operation ${operation.id} failed during execution: ${step.description}`,\n {\n why: error.message,\n meta: {\n operationId: operation.id,\n stepDescription: step.description,\n sql: step.sql,\n sqlState: error.sqlState,\n constraint: error.constraint,\n table: error.table,\n column: error.column,\n detail: error.detail,\n },\n },\n );\n }\n throw error;\n }\n }\n return okVoid();\n }\n\n private stepResultIsTrue(rows: readonly Record<string, unknown>[]): boolean {\n if (!rows || rows.length === 0) {\n return false;\n }\n const firstRow = rows[0];\n const firstValue = firstRow ? Object.values(firstRow)[0] : undefined;\n if (typeof firstValue === 'boolean') {\n return firstValue;\n }\n if (typeof firstValue === 'number') {\n return firstValue !== 0;\n }\n if (typeof firstValue === 'string') {\n const lower = firstValue.toLowerCase();\n // PostgreSQL boolean representations: 't'/'f', 'true'/'false', '1'/'0'\n if (lower === 't' || lower === 'true' || lower === '1') {\n return true;\n }\n if (lower === 'f' || lower === 'false' || lower === '0') {\n return false;\n }\n // For other strings, non-empty is truthy (though this case shouldn't occur for boolean checks)\n return firstValue.length > 0;\n }\n return Boolean(firstValue);\n }\n\n private async expectationsAreSatisfied(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n ): Promise<boolean> {\n if (steps.length === 0) {\n return false;\n }\n for (const step of steps) {\n const result = await driver.query(step.sql, step.params ?? []);\n if (!this.stepResultIsTrue(result.rows)) {\n return false;\n }\n }\n return true;\n }\n\n private createPostcheckPreSatisfiedSkipRecord(\n operation: SqlMigrationPlanOperation<PostgresPlanTargetDetails>,\n ): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {\n // Clone and freeze existing meta if present\n const clonedMeta = operation.meta ? cloneAndFreezeRecord(operation.meta) : undefined;\n\n // Create frozen runner metadata\n const runnerMeta = Object.freeze({\n skipped: true,\n reason: 'postcheck_pre_satisfied',\n });\n\n // Merge and freeze the combined meta\n const mergedMeta = Object.freeze({\n ...(clonedMeta ?? {}),\n runner: runnerMeta,\n });\n\n // Clone and freeze arrays to prevent mutation\n const frozenPostcheck = Object.freeze([...operation.postcheck]);\n\n return Object.freeze({\n id: operation.id,\n label: operation.label,\n ...ifDefined('summary', operation.summary),\n operationClass: operation.operationClass,\n target: operation.target, // Already frozen from plan creation\n precheck: Object.freeze([]),\n execute: Object.freeze([]),\n postcheck: frozenPostcheck,\n ...ifDefined('meta', operation.meta || mergedMeta ? mergedMeta : undefined),\n });\n }\n\n private markerMatchesDestination(\n marker: ContractMarkerRecord | null,\n plan: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['plan'],\n ): boolean {\n if (!marker) {\n return false;\n }\n if (marker.storageHash !== plan.destination.storageHash) {\n return false;\n }\n if (plan.destination.profileHash && marker.profileHash !== plan.destination.profileHash) {\n return false;\n }\n return true;\n }\n\n private enforcePolicyCompatibility(\n policy: MigrationOperationPolicy,\n operations: readonly SqlMigrationPlanOperation<PostgresPlanTargetDetails>[],\n ): Result<void, SqlMigrationRunnerFailure> {\n const allowedClasses = new Set(policy.allowedOperationClasses);\n for (const operation of operations) {\n if (!allowedClasses.has(operation.operationClass)) {\n return runnerFailure(\n 'POLICY_VIOLATION',\n `Operation ${operation.id} has class \"${operation.operationClass}\" which is not allowed by policy.`,\n {\n why: `Policy only allows: ${policy.allowedOperationClasses.join(', ')}.`,\n meta: {\n operationId: operation.id,\n operationClass: operation.operationClass,\n allowedClasses: policy.allowedOperationClasses,\n },\n },\n );\n }\n }\n return okVoid();\n }\n\n private ensureMarkerCompatibility(\n marker: ContractMarkerRecord | null,\n plan: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['plan'],\n ): Result<void, SqlMigrationRunnerFailure> {\n const origin = plan.origin ?? null;\n if (!origin) {\n // No origin assertion on the plan — the caller does not want origin validation.\n // This is the case for `db update`, which introspects the live schema and does not\n // rely on marker continuity. `db init` handles its own marker checks before the runner.\n return okVoid();\n }\n\n if (!marker) {\n return runnerFailure(\n 'MARKER_ORIGIN_MISMATCH',\n `Missing contract marker: expected origin storage hash ${origin.storageHash}.`,\n {\n meta: {\n expectedOriginStorageHash: origin.storageHash,\n },\n },\n );\n }\n if (marker.storageHash !== origin.storageHash) {\n return runnerFailure(\n 'MARKER_ORIGIN_MISMATCH',\n `Existing contract marker (${marker.storageHash}) does not match plan origin (${origin.storageHash}).`,\n {\n meta: {\n markerStorageHash: marker.storageHash,\n expectedOriginStorageHash: origin.storageHash,\n },\n },\n );\n }\n if (origin.profileHash && marker.profileHash !== origin.profileHash) {\n return runnerFailure(\n 'MARKER_ORIGIN_MISMATCH',\n `Existing contract marker profile hash (${marker.profileHash}) does not match plan origin profile hash (${origin.profileHash}).`,\n {\n meta: {\n markerProfileHash: marker.profileHash,\n expectedOriginProfileHash: origin.profileHash,\n },\n },\n );\n }\n return okVoid();\n }\n\n private ensurePlanMatchesDestinationContract(\n destination: SqlMigrationPlanContractInfo,\n contract: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['destinationContract'],\n ): Result<void, SqlMigrationRunnerFailure> {\n if (destination.storageHash !== contract.storage.storageHash) {\n return runnerFailure(\n 'DESTINATION_CONTRACT_MISMATCH',\n `Plan destination storage hash (${destination.storageHash}) does not match provided contract storage hash (${contract.storage.storageHash}).`,\n {\n meta: {\n planStorageHash: destination.storageHash,\n contractStorageHash: contract.storage.storageHash,\n },\n },\n );\n }\n if (\n destination.profileHash &&\n contract.profileHash &&\n destination.profileHash !== contract.profileHash\n ) {\n return runnerFailure(\n 'DESTINATION_CONTRACT_MISMATCH',\n `Plan destination profile hash (${destination.profileHash}) does not match provided contract profile hash (${contract.profileHash}).`,\n {\n meta: {\n planProfileHash: destination.profileHash,\n contractProfileHash: contract.profileHash,\n },\n },\n );\n }\n return okVoid();\n }\n\n private async upsertMarker(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n existingMarker: ContractMarkerRecord | null,\n space: string,\n ): Promise<void> {\n const incomingInvariants = options.plan.providedInvariants ?? [];\n const writeStatements = buildMergeMarkerStatements({\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: incomingInvariants,\n });\n const statement = existingMarker ? writeStatements.update : writeStatements.insert;\n await this.executeStatement(driver, statement);\n }\n\n private async recordLedgerEntry(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n existingMarker: ContractMarkerRecord | null,\n executedOperations: readonly SqlMigrationPlanOperation<PostgresPlanTargetDetails>[],\n ): Promise<void> {\n const ledgerStatement = buildLedgerInsertStatement({\n originStorageHash: existingMarker?.storageHash ?? null,\n originProfileHash: existingMarker?.profileHash ?? null,\n destinationStorageHash: options.plan.destination.storageHash,\n destinationProfileHash:\n options.plan.destination.profileHash ??\n options.destinationContract.profileHash ??\n options.plan.destination.storageHash,\n contractJsonBefore: existingMarker?.contractJson ?? null,\n contractJsonAfter: options.destinationContract,\n operations: executedOperations,\n });\n await this.executeStatement(driver, ledgerStatement);\n }\n\n private async acquireLock(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n key: string,\n ): Promise<void> {\n await driver.query('select pg_advisory_xact_lock(hashtext($1))', [key]);\n }\n\n private async beginTransaction(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('BEGIN');\n }\n\n private async commitTransaction(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('COMMIT');\n }\n\n private async rollbackTransaction(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('ROLLBACK');\n }\n\n private async executeStatement(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n statement: SqlStatement,\n ): Promise<void> {\n if (statement.params.length > 0) {\n await driver.query(statement.sql, statement.params);\n return;\n }\n await driver.query(statement.sql);\n }\n}\n","import type { 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 * Postgres target `SchemaVerifier` concretion. Plugs into the\n * SQL-shared verification surface; production verification today still\n * routes through the legacy `verifySqlSchema` entry point, which\n * carries options (codec hooks, normalizers, framework components)\n * that the framework-level `SchemaVerifyOptions` shape does not yet\n * surface.\n *\n * The hooks return the empty list pending the call-site migration that\n * routes the existing verifier behaviour through the SPI — at that\n * point `verifyCommonSqlSchema` will likely lift onto the family base\n * (mirroring `verifyCommonMongoSchema`) and `verifyTargetExtensions`\n * will house Postgres-only kinds (functions, RLS policies in a future\n * project).\n */\nexport class PostgresSchemaVerifier extends SqlSchemaVerifierBase<\n Contract<SqlStorage>,\n SqlSchemaIR\n> {\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, extractCodecControlHooks } from '@prisma-next/family-sql/control';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n ControlTargetInstance,\n MigrationRunner,\n} from '@prisma-next/framework-components/control';\nimport type { SqlStorage, StorageColumn } from '@prisma-next/sql-contract/types';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { postgresTargetDescriptorMeta } from '../core/descriptor-meta';\nimport { createPostgresMigrationPlanner } from '../core/migrations/planner';\nimport { renderDefaultLiteral } from '../core/migrations/planner-ddl-builders';\nimport type { PostgresPlanTargetDetails } from '../core/migrations/planner-target-details';\nimport { createPostgresMigrationRunner } from '../core/migrations/runner';\nimport { PostgresContractSerializer } from '../core/postgres-contract-serializer';\nimport { PostgresSchemaVerifier } from '../core/postgres-schema-verifier';\n\nfunction buildNativeTypeExpander(\n frameworkComponents?: ReadonlyArray<TargetBoundComponentDescriptor<'sql', 'postgres'>>,\n) {\n if (!frameworkComponents) {\n return undefined;\n }\n const codecHooks = extractCodecControlHooks(frameworkComponents);\n return (input: {\n readonly nativeType: string;\n readonly codecId?: string;\n readonly typeParams?: Record<string, unknown>;\n }) => {\n if (!input.typeParams) return input.nativeType;\n // Mirror `renderExpectedNativeType` in verify-sql-schema: when a codec\n // has no `expandNativeType` hook (e.g. `pg/enum@1`, whose typeParams\n // describe the value set rather than a DDL suffix), fall back to the\n // bare native type rather than throwing. Throwing here would reject\n // every plan involving an enum-/values-typed column as soon as its\n // `typeRef` resolved to a `StorageTypeInstance` carrying typeParams.\n if (!input.codecId) return input.nativeType;\n const hooks = codecHooks.get(input.codecId);\n if (!hooks?.expandNativeType) return input.nativeType;\n return hooks.expandNativeType(input);\n };\n}\n\nexport function postgresRenderDefault(def: ColumnDefault, column: StorageColumn): string {\n if (def.kind === 'function') {\n return def.expression;\n }\n return renderDefaultLiteral(def.value, column);\n}\n\nconst postgresTargetDescriptor: SqlControlTargetDescriptor<'postgres', PostgresPlanTargetDetails> =\n {\n ...postgresTargetDescriptorMeta,\n contractSerializer: new PostgresContractSerializer(),\n schemaVerifier: new PostgresSchemaVerifier(),\n migrations: {\n createPlanner(_family: SqlControlFamilyInstance) {\n return createPostgresMigrationPlanner();\n },\n createRunner(family) {\n return createPostgresMigrationRunner(family) as MigrationRunner<'sql', 'postgres'>;\n },\n contractToSchema(contract, frameworkComponents) {\n const expander = buildNativeTypeExpander(frameworkComponents);\n // Blind cast: the framework SPI signature\n // (`control-migration-types.ts § contractToSchema`) types\n // `contract` as the generic `Contract | null`. Inside the\n // postgres target descriptor we know any contract reaching\n // this method is SQL-family — the family contract resolver\n // would have refused to construct a postgres target binding\n // otherwise — so we narrow the generic to\n // `Contract<SqlStorage>` for the lowering call.\n return contractToSchemaIR(contract as unknown as Contract<SqlStorage> | null, {\n annotationNamespace: 'pg',\n ...ifDefined('expandNativeType', expander),\n renderDefault: postgresRenderDefault,\n });\n },\n },\n create(): ControlTargetInstance<'sql', 'postgres'> {\n return {\n familyId: 'sql',\n targetId: 'postgres',\n };\n },\n /**\n * Direct method for SQL-specific usage.\n * @deprecated Use migrations.createPlanner() for CLI compatibility.\n */\n createPlanner(_family: SqlControlFamilyInstance) {\n return createPostgresMigrationPlanner();\n },\n /**\n * Direct method for SQL-specific usage.\n * @deprecated Use migrations.createRunner() for CLI compatibility.\n */\n createRunner(family) {\n return createPostgresMigrationRunner(family);\n },\n };\n\nexport default postgresTargetDescriptor;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"control.mjs","names":[],"sources":["../src/core/migrations/runner.ts","../src/core/postgres-schema-verifier.ts","../src/exports/control.ts"],"sourcesContent":["import type { ContractMarkerRecord } from '@prisma-next/contract/types';\nimport type {\n MigrationOperationPolicy,\n SqlControlFamilyInstance,\n SqlMigrationPlanContractInfo,\n SqlMigrationPlanOperation,\n SqlMigrationPlanOperationStep,\n SqlMigrationRunner,\n SqlMigrationRunnerExecuteOptions,\n SqlMigrationRunnerFailure,\n SqlMigrationRunnerResult,\n 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 {\n ControlDriverInstance,\n MigrationRunnerResult,\n} from '@prisma-next/framework-components/control';\nimport { APP_SPACE_ID } from '@prisma-next/framework-components/control';\nimport { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';\nimport { SqlQueryError } from '@prisma-next/sql-errors';\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 { parsePostgresDefault } from '../default-normalizer';\nimport { normalizeSchemaNativeType } from '../native-type-normalizer';\nimport { createResolveExistingEnumValues } from './enum-planning';\nimport type { PostgresPlanTargetDetails } from './planner-target-details';\nimport {\n buildLedgerInsertStatement,\n buildMergeMarkerStatements,\n ensureLedgerTableStatement,\n ensureMarkerTableStatement,\n ensurePrismaContractSchemaStatement,\n type SqlStatement,\n} from './statement-builders';\n\ninterface ApplyPlanSuccessValue {\n readonly operationsExecuted: number;\n readonly executedOperations: readonly SqlMigrationPlanOperation<PostgresPlanTargetDetails>[];\n}\n\nconst LOCK_DOMAIN = 'prisma_next.contract.marker';\n\n/**\n * Deep clones and freezes a record object to prevent mutation.\n * Recursively clones nested objects and arrays to ensure complete isolation.\n */\nfunction cloneAndFreezeRecord<T extends Record<string, unknown>>(value: T): T {\n const cloned: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value)) {\n if (val === null || val === undefined) {\n cloned[key] = val;\n } else if (Array.isArray(val)) {\n cloned[key] = Object.freeze([...val]);\n } else if (typeof val === 'object') {\n cloned[key] = cloneAndFreezeRecord(val as Record<string, unknown>);\n } else {\n cloned[key] = val;\n }\n }\n return Object.freeze(cloned) as T;\n}\n\nexport function createPostgresMigrationRunner(\n family: SqlControlFamilyInstance,\n): SqlMigrationRunner<PostgresPlanTargetDetails> {\n return new PostgresMigrationRunner(family);\n}\n\nclass PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDetails> {\n constructor(private readonly family: SqlControlFamilyInstance) {}\n\n /**\n * Body of the migration runner without transaction management. The\n * caller ({@link PostgresMigrationRunner.execute}) owns the\n * `BEGIN`/`COMMIT`/`ROLLBACK` lifecycle.\n */\n async executeOnConnection(\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n ): Promise<SqlMigrationRunnerResult> {\n const schema =\n options.schemaName ??\n Object.keys(options.destinationContract.storage.namespaces).find(\n (id) => id !== UNBOUND_NAMESPACE_ID,\n ) ??\n UNBOUND_NAMESPACE_ID;\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 const lockKey = `${LOCK_DOMAIN}:${schema}:${space}`;\n\n // Static checks (idempotent — safe to run again when the caller is\n // `execute(...)` because the cost is a single object comparison).\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 await this.acquireLock(driver, lockKey);\n const ensureResult = await this.ensureControlTables(driver);\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 let applyValue: ApplyPlanSuccessValue;\n\n if (skipOperations) {\n applyValue = { operationsExecuted: 0, executedOperations: [] };\n } else {\n const applyResult = await this.applyPlan(driver, options);\n if (!applyResult.ok) return applyResult;\n applyValue = applyResult.value;\n }\n\n // Schema verification on app-space only — extension spaces don't\n // own user-facing tables in the live schema, and `verifySqlSchema`\n // matches the destination contract against the database, which\n // would flag every app-space table as \"extra\" when called against\n // an extension contract.\n if (space === APP_SPACE_ID) {\n const schemaIR = await this.family.introspect({\n driver,\n contract: options.destinationContract,\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: parsePostgresDefault,\n normalizeNativeType: normalizeSchemaNativeType,\n resolveExistingEnumValues: createResolveExistingEnumValues(\n options.destinationContract.storage,\n ),\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 const incomingInvariants = options.plan.providedInvariants ?? [];\n const existingInvariants = new Set(existingMarker?.invariants ?? []);\n const incomingIsSubsetOfExisting = incomingInvariants.every((id) => existingInvariants.has(id));\n const isSelfEdgeNoOp =\n isSelfEdge && applyValue.operationsExecuted === 0 && incomingIsSubsetOfExisting;\n\n if (!isSelfEdgeNoOp) {\n await this.upsertMarker(driver, options, existingMarker, space);\n await this.recordLedgerEntry(driver, options, existingMarker, applyValue.executedOperations);\n }\n\n return runnerSuccess({\n operationsPlanned: options.plan.operations.length,\n operationsExecuted: applyValue.operationsExecuted,\n });\n }\n\n async execute(options: {\n readonly driver: ControlDriverInstance<'sql', string>;\n readonly perSpaceOptions: ReadonlyArray<\n SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>\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 await this.beginTransaction(driver);\n let committed = false;\n try {\n const perSpaceResults: Array<{\n space: string;\n value: SqlMigrationRunnerSuccessValue;\n }> = [];\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 }\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 }\n\n private async applyPlan(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n ): Promise<Result<ApplyPlanSuccessValue, SqlMigrationRunnerFailure>> {\n const checks = options.executionChecks;\n const runPrechecks = checks?.prechecks !== false; // Default true\n const runPostchecks = checks?.postchecks !== false; // Default true\n const runIdempotency = checks?.idempotencyChecks !== false; // Default true\n\n let operationsExecuted = 0;\n const executedOperations: Array<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> = [];\n for (const operation of options.plan.operations) {\n options.callbacks?.onOperationStart?.(operation);\n try {\n // Idempotency probe: only run if both postchecks and idempotency checks are enabled\n if (runPostchecks && runIdempotency) {\n const postcheckAlreadySatisfied = await this.expectationsAreSatisfied(\n driver,\n operation.postcheck,\n );\n if (postcheckAlreadySatisfied) {\n executedOperations.push(this.createPostcheckPreSatisfiedSkipRecord(operation));\n continue;\n }\n }\n\n // Prechecks: only run if enabled\n if (runPrechecks) {\n const precheckResult = await this.runExpectationSteps(\n driver,\n operation.precheck,\n operation,\n 'precheck',\n );\n if (!precheckResult.ok) {\n return precheckResult;\n }\n }\n\n const executeResult = await this.runExecuteSteps(driver, operation.execute, operation);\n if (!executeResult.ok) {\n return executeResult;\n }\n\n // Postchecks: only run if enabled\n if (runPostchecks) {\n const postcheckResult = await this.runExpectationSteps(\n driver,\n operation.postcheck,\n operation,\n 'postcheck',\n );\n if (!postcheckResult.ok) {\n return postcheckResult;\n }\n }\n\n executedOperations.push(operation);\n operationsExecuted += 1;\n } finally {\n options.callbacks?.onOperationComplete?.(operation);\n }\n }\n return ok({ operationsExecuted, executedOperations });\n }\n\n private async ensureControlTables(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n await this.executeStatement(driver, ensurePrismaContractSchemaStatement);\n // Pre-1.0 zero-range guardrail: detect a pre-cleanup single-row\n // marker table (no `space` column) and surface a structured failure\n // rather than silently auto-migrating it to the per-space shape.\n // See `specs/framework-mechanism.spec.md § 2`.\n const legacyDetection = await this.detectLegacyMarkerShape(driver);\n if (!legacyDetection.ok) {\n return legacyDetection;\n }\n await this.executeStatement(driver, ensureMarkerTableStatement);\n await this.executeStatement(driver, ensureLedgerTableStatement);\n return okVoid();\n }\n\n private async detectLegacyMarkerShape(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n const result = await driver.query<{ column_name: string }>(\n `select column_name\n from information_schema.columns\n where table_schema = 'prisma_contract'\n and table_name = 'marker'`,\n );\n if (result.rows.length === 0) {\n return okVoid();\n }\n const columns = new Set(result.rows.map((row) => row.column_name));\n if (columns.has('space')) {\n return okVoid();\n }\n return runnerFailure(\n 'LEGACY_MARKER_SHAPE',\n 'Legacy marker-table shape detected on prisma_contract.marker (no `space` column). ' +\n 'Prisma Next is in pre-1.0; the previous transitional auto-migration to the per-space-row schema has been removed. ' +\n 'Drop `prisma_contract.marker` and re-run `dbInit` to reinitialise from a clean baseline.',\n {\n meta: {\n table: 'prisma_contract.marker',\n columns: [...columns].sort(),\n },\n },\n );\n }\n\n private async runExpectationSteps(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n operation: SqlMigrationPlanOperation<PostgresPlanTargetDetails>,\n phase: 'precheck' | 'postcheck',\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n for (const step of steps) {\n const result = await driver.query(step.sql, step.params ?? []);\n if (!this.stepResultIsTrue(result.rows)) {\n const code = phase === 'precheck' ? 'PRECHECK_FAILED' : 'POSTCHECK_FAILED';\n return runnerFailure(\n code,\n `Operation ${operation.id} failed during ${phase}: ${step.description}`,\n {\n meta: {\n operationId: operation.id,\n phase,\n stepDescription: step.description,\n },\n },\n );\n }\n }\n return okVoid();\n }\n\n private async runExecuteSteps(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n operation: SqlMigrationPlanOperation<PostgresPlanTargetDetails>,\n ): Promise<Result<void, SqlMigrationRunnerFailure>> {\n for (const step of steps) {\n try {\n await driver.query(step.sql, step.params ?? []);\n } catch (error: unknown) {\n if (SqlQueryError.is(error)) {\n return runnerFailure(\n 'EXECUTION_FAILED',\n `Operation ${operation.id} failed during execution: ${step.description}`,\n {\n why: error.message,\n meta: {\n operationId: operation.id,\n stepDescription: step.description,\n sql: step.sql,\n sqlState: error.sqlState,\n constraint: error.constraint,\n table: error.table,\n column: error.column,\n detail: error.detail,\n },\n },\n );\n }\n throw error;\n }\n }\n return okVoid();\n }\n\n private stepResultIsTrue(rows: readonly Record<string, unknown>[]): boolean {\n if (!rows || rows.length === 0) {\n return false;\n }\n const firstRow = rows[0];\n const firstValue = firstRow ? Object.values(firstRow)[0] : undefined;\n if (typeof firstValue === 'boolean') {\n return firstValue;\n }\n if (typeof firstValue === 'number') {\n return firstValue !== 0;\n }\n if (typeof firstValue === 'string') {\n const lower = firstValue.toLowerCase();\n // PostgreSQL boolean representations: 't'/'f', 'true'/'false', '1'/'0'\n if (lower === 't' || lower === 'true' || lower === '1') {\n return true;\n }\n if (lower === 'f' || lower === 'false' || lower === '0') {\n return false;\n }\n // For other strings, non-empty is truthy (though this case shouldn't occur for boolean checks)\n return firstValue.length > 0;\n }\n return Boolean(firstValue);\n }\n\n private async expectationsAreSatisfied(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n steps: readonly SqlMigrationPlanOperationStep[],\n ): Promise<boolean> {\n if (steps.length === 0) {\n return false;\n }\n for (const step of steps) {\n const result = await driver.query(step.sql, step.params ?? []);\n if (!this.stepResultIsTrue(result.rows)) {\n return false;\n }\n }\n return true;\n }\n\n private createPostcheckPreSatisfiedSkipRecord(\n operation: SqlMigrationPlanOperation<PostgresPlanTargetDetails>,\n ): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {\n // Clone and freeze existing meta if present\n const clonedMeta = operation.meta ? cloneAndFreezeRecord(operation.meta) : undefined;\n\n // Create frozen runner metadata\n const runnerMeta = Object.freeze({\n skipped: true,\n reason: 'postcheck_pre_satisfied',\n });\n\n // Merge and freeze the combined meta\n const mergedMeta = Object.freeze({\n ...(clonedMeta ?? {}),\n runner: runnerMeta,\n });\n\n // Clone and freeze arrays to prevent mutation\n const frozenPostcheck = Object.freeze([...operation.postcheck]);\n\n return Object.freeze({\n id: operation.id,\n label: operation.label,\n ...ifDefined('summary', operation.summary),\n operationClass: operation.operationClass,\n target: operation.target, // Already frozen from plan creation\n precheck: Object.freeze([]),\n execute: Object.freeze([]),\n postcheck: frozenPostcheck,\n ...ifDefined('meta', operation.meta || mergedMeta ? mergedMeta : undefined),\n });\n }\n\n private markerMatchesDestination(\n marker: ContractMarkerRecord | null,\n plan: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['plan'],\n ): boolean {\n if (!marker) {\n return false;\n }\n if (marker.storageHash !== plan.destination.storageHash) {\n return false;\n }\n if (plan.destination.profileHash && marker.profileHash !== plan.destination.profileHash) {\n return false;\n }\n return true;\n }\n\n private enforcePolicyCompatibility(\n policy: MigrationOperationPolicy,\n operations: readonly SqlMigrationPlanOperation<PostgresPlanTargetDetails>[],\n ): Result<void, SqlMigrationRunnerFailure> {\n const allowedClasses = new Set(policy.allowedOperationClasses);\n for (const operation of operations) {\n if (!allowedClasses.has(operation.operationClass)) {\n return runnerFailure(\n 'POLICY_VIOLATION',\n `Operation ${operation.id} has class \"${operation.operationClass}\" which is not allowed by policy.`,\n {\n why: `Policy only allows: ${policy.allowedOperationClasses.join(', ')}.`,\n meta: {\n operationId: operation.id,\n operationClass: operation.operationClass,\n allowedClasses: policy.allowedOperationClasses,\n },\n },\n );\n }\n }\n return okVoid();\n }\n\n private ensureMarkerCompatibility(\n marker: ContractMarkerRecord | null,\n plan: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['plan'],\n ): Result<void, SqlMigrationRunnerFailure> {\n const origin = plan.origin ?? null;\n if (!origin) {\n // No origin assertion on the plan — the caller does not want origin validation.\n // This is the case for `db update`, which introspects the live schema and does not\n // rely on marker continuity. `db init` handles its own marker checks before the runner.\n return okVoid();\n }\n\n if (!marker) {\n return runnerFailure(\n 'MARKER_ORIGIN_MISMATCH',\n `Missing contract marker: expected origin storage hash ${origin.storageHash}.`,\n {\n meta: {\n expectedOriginStorageHash: origin.storageHash,\n },\n },\n );\n }\n if (marker.storageHash !== origin.storageHash) {\n return runnerFailure(\n 'MARKER_ORIGIN_MISMATCH',\n `Existing contract marker (${marker.storageHash}) does not match plan origin (${origin.storageHash}).`,\n {\n meta: {\n markerStorageHash: marker.storageHash,\n expectedOriginStorageHash: origin.storageHash,\n },\n },\n );\n }\n if (origin.profileHash && marker.profileHash !== origin.profileHash) {\n return runnerFailure(\n 'MARKER_ORIGIN_MISMATCH',\n `Existing contract marker profile hash (${marker.profileHash}) does not match plan origin profile hash (${origin.profileHash}).`,\n {\n meta: {\n markerProfileHash: marker.profileHash,\n expectedOriginProfileHash: origin.profileHash,\n },\n },\n );\n }\n return okVoid();\n }\n\n private ensurePlanMatchesDestinationContract(\n destination: SqlMigrationPlanContractInfo,\n contract: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['destinationContract'],\n ): Result<void, SqlMigrationRunnerFailure> {\n if (destination.storageHash !== contract.storage.storageHash) {\n return runnerFailure(\n 'DESTINATION_CONTRACT_MISMATCH',\n `Plan destination storage hash (${destination.storageHash}) does not match provided contract storage hash (${contract.storage.storageHash}).`,\n {\n meta: {\n planStorageHash: destination.storageHash,\n contractStorageHash: contract.storage.storageHash,\n },\n },\n );\n }\n if (\n destination.profileHash &&\n contract.profileHash &&\n destination.profileHash !== contract.profileHash\n ) {\n return runnerFailure(\n 'DESTINATION_CONTRACT_MISMATCH',\n `Plan destination profile hash (${destination.profileHash}) does not match provided contract profile hash (${contract.profileHash}).`,\n {\n meta: {\n planProfileHash: destination.profileHash,\n contractProfileHash: contract.profileHash,\n },\n },\n );\n }\n return okVoid();\n }\n\n private async upsertMarker(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n existingMarker: ContractMarkerRecord | null,\n space: string,\n ): Promise<void> {\n const incomingInvariants = options.plan.providedInvariants ?? [];\n const writeStatements = buildMergeMarkerStatements({\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: incomingInvariants,\n });\n const statement = existingMarker ? writeStatements.update : writeStatements.insert;\n await this.executeStatement(driver, statement);\n }\n\n private async recordLedgerEntry(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,\n existingMarker: ContractMarkerRecord | null,\n executedOperations: readonly SqlMigrationPlanOperation<PostgresPlanTargetDetails>[],\n ): Promise<void> {\n const ledgerStatement = buildLedgerInsertStatement({\n originStorageHash: existingMarker?.storageHash ?? null,\n originProfileHash: existingMarker?.profileHash ?? null,\n destinationStorageHash: options.plan.destination.storageHash,\n destinationProfileHash:\n options.plan.destination.profileHash ??\n options.destinationContract.profileHash ??\n options.plan.destination.storageHash,\n contractJsonBefore: existingMarker?.contractJson ?? null,\n contractJsonAfter: options.destinationContract,\n operations: executedOperations,\n });\n await this.executeStatement(driver, ledgerStatement);\n }\n\n private async acquireLock(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n key: string,\n ): Promise<void> {\n await driver.query('select pg_advisory_xact_lock(hashtext($1))', [key]);\n }\n\n private async beginTransaction(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('BEGIN');\n }\n\n private async commitTransaction(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('COMMIT');\n }\n\n private async rollbackTransaction(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n ): Promise<void> {\n await driver.query('ROLLBACK');\n }\n\n private async executeStatement(\n driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],\n statement: SqlStatement,\n ): Promise<void> {\n if (statement.params.length > 0) {\n await driver.query(statement.sql, statement.params);\n return;\n }\n await driver.query(statement.sql);\n }\n}\n","import type { 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 * Postgres target `SchemaVerifier` concretion. Plugs into the\n * SQL-shared verification surface; production verification today still\n * routes through the legacy `verifySqlSchema` entry point, which\n * carries options (codec hooks, normalizers, framework components)\n * that the framework-level `SchemaVerifyOptions` shape does not yet\n * surface.\n *\n * The hooks return the empty list pending the call-site migration that\n * routes the existing verifier behaviour through the SPI — at that\n * point `verifyCommonSqlSchema` will likely lift onto the family base\n * (mirroring `verifyCommonMongoSchema`) and `verifyTargetExtensions`\n * will house Postgres-only kinds (functions, RLS policies in a future\n * project).\n */\nexport class PostgresSchemaVerifier extends SqlSchemaVerifierBase<\n Contract<SqlStorage>,\n SqlSchemaIR\n> {\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, extractCodecControlHooks } from '@prisma-next/family-sql/control';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n ControlTargetInstance,\n MigrationRunner,\n} from '@prisma-next/framework-components/control';\nimport type { SqlStorage, StorageColumn } from '@prisma-next/sql-contract/types';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { postgresTargetDescriptorMeta } from '../core/descriptor-meta';\nimport {\n enumStorageCompoundKey,\n resolveDdlSchemaForNamespaceStorage,\n} from '../core/migrations/enum-planning';\nimport { createPostgresMigrationPlanner } from '../core/migrations/planner';\nimport { renderDefaultLiteral } from '../core/migrations/planner-ddl-builders';\nimport type { PostgresPlanTargetDetails } from '../core/migrations/planner-target-details';\nimport { createPostgresMigrationRunner } from '../core/migrations/runner';\nimport { PostgresContractSerializer } from '../core/postgres-contract-serializer';\nimport { PostgresSchemaVerifier } from '../core/postgres-schema-verifier';\n\nfunction buildNativeTypeExpander(\n frameworkComponents?: ReadonlyArray<TargetBoundComponentDescriptor<'sql', 'postgres'>>,\n) {\n if (!frameworkComponents) {\n return undefined;\n }\n const codecHooks = extractCodecControlHooks(frameworkComponents);\n return (input: {\n readonly nativeType: string;\n readonly codecId?: string;\n readonly typeParams?: Record<string, unknown>;\n }) => {\n if (!input.typeParams) return input.nativeType;\n // Mirror `renderExpectedNativeType` in verify-sql-schema: when a codec\n // has no `expandNativeType` hook (e.g. `pg/enum@1`, whose typeParams\n // describe the value set rather than a DDL suffix), fall back to the\n // bare native type rather than throwing. Throwing here would reject\n // every plan involving an enum-/values-typed column as soon as its\n // `typeRef` resolved to a `StorageTypeInstance` carrying typeParams.\n if (!input.codecId) return input.nativeType;\n const hooks = codecHooks.get(input.codecId);\n if (!hooks?.expandNativeType) return input.nativeType;\n return hooks.expandNativeType(input);\n };\n}\n\nexport function postgresRenderDefault(def: ColumnDefault, column: StorageColumn): string {\n if (def.kind === 'function') {\n return def.expression;\n }\n return renderDefaultLiteral(def.value, column);\n}\n\nconst postgresTargetDescriptor: SqlControlTargetDescriptor<'postgres', PostgresPlanTargetDetails> =\n {\n ...postgresTargetDescriptorMeta,\n contractSerializer: new PostgresContractSerializer(),\n schemaVerifier: new PostgresSchemaVerifier(),\n migrations: {\n createPlanner(_family: SqlControlFamilyInstance) {\n return createPostgresMigrationPlanner();\n },\n createRunner(family) {\n return createPostgresMigrationRunner(family) as MigrationRunner<'sql', 'postgres'>;\n },\n contractToSchema(contract, frameworkComponents) {\n const expander = buildNativeTypeExpander(frameworkComponents);\n // Blind cast: the framework SPI signature\n // (`control-migration-types.ts § contractToSchema`) types\n // `contract` as the generic `Contract | null`. Inside the\n // postgres target descriptor we know any contract reaching\n // this method is SQL-family — the family contract resolver\n // would have refused to construct a postgres target binding\n // otherwise — so we narrow the generic to\n // `Contract<SqlStorage>` for the lowering call.\n return contractToSchemaIR(contract as unknown as Contract<SqlStorage> | null, {\n annotationNamespace: 'pg',\n ...ifDefined('expandNativeType', expander),\n renderDefault: postgresRenderDefault,\n // Schema-qualify enum annotation keys so the projected \"from\" IR's\n // `storageTypes` match `readExistingEnumValues` on the read side\n // (the contract-to-contract `migration plan` path). The DDL-schema\n // resolution + compound-key format stay here in the target layer;\n // the family projector treats the returned string as opaque.\n // `undefined` schema IR ⇒ the unbound coordinate resolves to the\n // default `public` landing schema, matching the read-side fallback.\n resolveEnumStorageKey: (storage, namespaceId, nativeType) =>\n enumStorageCompoundKey(\n resolveDdlSchemaForNamespaceStorage(storage, namespaceId, undefined),\n nativeType,\n ),\n });\n },\n },\n create(): ControlTargetInstance<'sql', 'postgres'> {\n return {\n familyId: 'sql',\n targetId: 'postgres',\n };\n },\n /**\n * Direct method for SQL-specific usage.\n * @deprecated Use migrations.createPlanner() for CLI compatibility.\n */\n createPlanner(_family: SqlControlFamilyInstance) {\n return createPostgresMigrationPlanner();\n },\n /**\n * Direct method for SQL-specific usage.\n * @deprecated Use migrations.createRunner() for CLI compatibility.\n */\n createRunner(family) {\n return createPostgresMigrationRunner(family);\n },\n };\n\nexport default postgresTargetDescriptor;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-transform-CdtGUWp2.mjs","names":[],"sources":["../src/core/migrations/operations/data-transform.ts"],"sourcesContent":["/**\n * User-facing `dataTransform` factory for the Postgres migration authoring\n * surface. Invoked directly inside a `migration.ts` file via the\n * `PostgresMigration` instance method (`this.dataTransform(...)`), which\n * supplies the control adapter from the migration's injected stack:\n *\n * ```ts\n * import endContract from './end-contract.json' with { type: 'json' };\n *\n * class M extends Migration {\n * override get operations() {\n * return [\n * this.dataTransform(endContract, 'backfill emails', {\n * check: () => db.users.select('id').where(({ email }) => email.isNull()).limit(1),\n * run: () => db.users.update({ email: '' }).where(({ email }) => email.isNull()),\n * }),\n * ];\n * }\n * }\n * ```\n *\n * The factory accepts lazy closures (`() => SqlQueryPlan | Buildable`),\n * invokes each one, asserts that its `meta.storageHash` matches the\n * `contract` it was handed (→ `PN-MIG-2005` on mismatch), and lowers the\n * plan via the supplied control adapter to a serialized `{sql, params}`\n * payload.\n *\n * The factory then lowers the data transform to the unified migration-op\n * shape `{ precheck, execute, postcheck }`. The user's `check` plan is\n * wrapped twice with opposite truth values:\n *\n * - precheck `SELECT EXISTS (<check>) AS ok` asserts there is work to do\n * (precheck is short-circuited by the runner's pre-satisfied-skip path\n * when nothing remains to backfill).\n * - postcheck `SELECT NOT EXISTS (<check>) AS ok` asserts the work is\n * complete after the run steps execute.\n *\n * The `check` plan is therefore expected to be a **rowset query whose\n * presence of any row signals \"work remains\"** — typically `select('id')\n * .where(<violation predicate>).limit(1)`. Scalar/aggregate shapes\n * (`count(*)`, `bool_and(...)`) do not work under this contract: they\n * always return exactly one row, so `EXISTS` is always true and\n * `NOT EXISTS` is always false. (This is the same row-presence contract\n * the pre-unification runner relied on; the wrapping is just lifting it\n * into SQL.)\n *\n * Each `run` plan becomes an execute step. Because the `Step.params`\n * field threads through `driver.query(sql, params)`, the user's bound\n * values flow through the driver's parameter binder rather than being\n * inlined into the SQL text.\n *\n * The free factory remains usable standalone (tests, ad-hoc tooling,\n * non-class contexts) by passing the adapter explicitly as the fourth\n * argument.\n */\n\nimport type { Contract } from '@prisma-next/contract/types';\nimport { errorDataTransformContractMismatch } from '@prisma-next/errors/migration';\nimport type {\n SqlMigrationPlanOperation,\n SqlMigrationPlanOperationStep,\n} from '@prisma-next/family-sql/control';\nimport type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';\nimport type { SerializedQueryPlan } from '@prisma-next/framework-components/control';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { PostgresPlanTargetDetails } from '../planner-target-details';\n\ninterface Buildable<R = unknown> {\n build(): SqlQueryPlan<R>;\n}\n\n/**\n * A single-closure producer of a SQL query plan. Shared between\n * `check` and each `run` entry.\n */\nexport type DataTransformClosure = () => SqlQueryPlan | Buildable;\n\nexport interface DataTransformOptions {\n /**\n * Optional opt-in routing identity. Presence opts the transform into\n * invariant-aware routing; absence means it is path-dependent and\n * not referenceable from refs.\n */\n readonly invariantId?: string;\n /**\n * Optional pre-flight query. `undefined` means \"no check\". When\n * supplied, the closure must return a **rowset query** whose\n * presence of any row signals \"violations remain\". Conventional\n * shape: `db.<table>.select('id').where(<violation>).limit(1)`.\n * Scalar/aggregate shapes do not satisfy this contract.\n */\n readonly check?: DataTransformClosure;\n /** One or more mutation queries to execute. */\n readonly run: DataTransformClosure | readonly DataTransformClosure[];\n}\n\nexport function dataTransform<TContract extends Contract<SqlStorage>>(\n contract: TContract,\n name: string,\n options: DataTransformOptions,\n adapter: SqlControlAdapter<'postgres'>,\n): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {\n const runClosures: readonly DataTransformClosure[] = Array.isArray(options.run)\n ? options.run\n : [options.run as DataTransformClosure];\n\n const checkPlan = options.check ? invokeAndLower(options.check, contract, adapter, name) : null;\n const runPlans = runClosures.map((closure) => invokeAndLower(closure, contract, adapter, name));\n\n const precheck: readonly SqlMigrationPlanOperationStep[] = checkPlan\n ? [\n {\n description: `Check ${name} has work to do`,\n sql: `SELECT EXISTS (${checkPlan.sql}) AS ok`,\n params: checkPlan.params,\n },\n ]\n : [];\n\n const execute: readonly SqlMigrationPlanOperationStep[] = runPlans.map((plan) => ({\n description: `Run ${name}`,\n sql: plan.sql,\n params: plan.params,\n }));\n\n const postcheck: readonly SqlMigrationPlanOperationStep[] = checkPlan\n ? [\n {\n description: `Verify ${name} resolved all violations`,\n sql: `SELECT NOT EXISTS (${checkPlan.sql}) AS ok`,\n params: checkPlan.params,\n },\n ]\n : [];\n\n return {\n id: `data_migration.${name}`,\n label: `Data transform: ${name}`,\n operationClass: 'data',\n ...ifDefined('invariantId', options.invariantId),\n target: { id: 'postgres' },\n precheck,\n execute,\n postcheck,\n };\n}\n\nfunction invokeAndLower(\n closure: DataTransformClosure,\n contract: Contract<SqlStorage>,\n adapter: SqlControlAdapter<'postgres'>,\n name: string,\n): SerializedQueryPlan {\n const result = closure();\n const plan = isBuildable(result) ? result.build() : result;\n assertContractMatches(plan, contract, name);\n const lowered = adapter.lower(plan.ast, { contract });\n const params = lowered.params.map((slot) => {\n if (slot.kind === 'literal') return slot.value;\n throw new Error(\n `data-transform: bind-site slot '${slot.name}' is not allowed in migration plans`,\n );\n });\n return { sql: lowered.sql, params };\n}\n\nfunction isBuildable(value: unknown): value is Buildable {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'build' in value &&\n typeof (value as { build: unknown }).build === 'function'\n );\n}\n\nfunction assertContractMatches(\n plan: SqlQueryPlan,\n contract: Contract<SqlStorage>,\n name: string,\n): void {\n if (plan.meta.storageHash !== contract.storage.storageHash) {\n throw errorDataTransformContractMismatch({\n dataTransformName: name,\n expected: contract.storage.storageHash,\n actual: plan.meta.storageHash,\n });\n }\n}\n"],"mappings":";;;AAkGA,SAAgB,cACd,UACA,MACA,SACA,SACsD;CACtD,MAAM,cAA+C,MAAM,QAAQ,QAAQ,
|
|
1
|
+
{"version":3,"file":"data-transform-CdtGUWp2.mjs","names":[],"sources":["../src/core/migrations/operations/data-transform.ts"],"sourcesContent":["/**\n * User-facing `dataTransform` factory for the Postgres migration authoring\n * surface. Invoked directly inside a `migration.ts` file via the\n * `PostgresMigration` instance method (`this.dataTransform(...)`), which\n * supplies the control adapter from the migration's injected stack:\n *\n * ```ts\n * import endContract from './end-contract.json' with { type: 'json' };\n *\n * class M extends Migration {\n * override get operations() {\n * return [\n * this.dataTransform(endContract, 'backfill emails', {\n * check: () => db.users.select('id').where(({ email }) => email.isNull()).limit(1),\n * run: () => db.users.update({ email: '' }).where(({ email }) => email.isNull()),\n * }),\n * ];\n * }\n * }\n * ```\n *\n * The factory accepts lazy closures (`() => SqlQueryPlan | Buildable`),\n * invokes each one, asserts that its `meta.storageHash` matches the\n * `contract` it was handed (→ `PN-MIG-2005` on mismatch), and lowers the\n * plan via the supplied control adapter to a serialized `{sql, params}`\n * payload.\n *\n * The factory then lowers the data transform to the unified migration-op\n * shape `{ precheck, execute, postcheck }`. The user's `check` plan is\n * wrapped twice with opposite truth values:\n *\n * - precheck `SELECT EXISTS (<check>) AS ok` asserts there is work to do\n * (precheck is short-circuited by the runner's pre-satisfied-skip path\n * when nothing remains to backfill).\n * - postcheck `SELECT NOT EXISTS (<check>) AS ok` asserts the work is\n * complete after the run steps execute.\n *\n * The `check` plan is therefore expected to be a **rowset query whose\n * presence of any row signals \"work remains\"** — typically `select('id')\n * .where(<violation predicate>).limit(1)`. Scalar/aggregate shapes\n * (`count(*)`, `bool_and(...)`) do not work under this contract: they\n * always return exactly one row, so `EXISTS` is always true and\n * `NOT EXISTS` is always false. (This is the same row-presence contract\n * the pre-unification runner relied on; the wrapping is just lifting it\n * into SQL.)\n *\n * Each `run` plan becomes an execute step. Because the `Step.params`\n * field threads through `driver.query(sql, params)`, the user's bound\n * values flow through the driver's parameter binder rather than being\n * inlined into the SQL text.\n *\n * The free factory remains usable standalone (tests, ad-hoc tooling,\n * non-class contexts) by passing the adapter explicitly as the fourth\n * argument.\n */\n\nimport type { Contract } from '@prisma-next/contract/types';\nimport { errorDataTransformContractMismatch } from '@prisma-next/errors/migration';\nimport type {\n SqlMigrationPlanOperation,\n SqlMigrationPlanOperationStep,\n} from '@prisma-next/family-sql/control';\nimport type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';\nimport type { SerializedQueryPlan } from '@prisma-next/framework-components/control';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { PostgresPlanTargetDetails } from '../planner-target-details';\n\ninterface Buildable<R = unknown> {\n build(): SqlQueryPlan<R>;\n}\n\n/**\n * A single-closure producer of a SQL query plan. Shared between\n * `check` and each `run` entry.\n */\nexport type DataTransformClosure = () => SqlQueryPlan | Buildable;\n\nexport interface DataTransformOptions {\n /**\n * Optional opt-in routing identity. Presence opts the transform into\n * invariant-aware routing; absence means it is path-dependent and\n * not referenceable from refs.\n */\n readonly invariantId?: string;\n /**\n * Optional pre-flight query. `undefined` means \"no check\". When\n * supplied, the closure must return a **rowset query** whose\n * presence of any row signals \"violations remain\". Conventional\n * shape: `db.<table>.select('id').where(<violation>).limit(1)`.\n * Scalar/aggregate shapes do not satisfy this contract.\n */\n readonly check?: DataTransformClosure;\n /** One or more mutation queries to execute. */\n readonly run: DataTransformClosure | readonly DataTransformClosure[];\n}\n\nexport function dataTransform<TContract extends Contract<SqlStorage>>(\n contract: TContract,\n name: string,\n options: DataTransformOptions,\n adapter: SqlControlAdapter<'postgres'>,\n): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {\n const runClosures: readonly DataTransformClosure[] = Array.isArray(options.run)\n ? options.run\n : [options.run as DataTransformClosure];\n\n const checkPlan = options.check ? invokeAndLower(options.check, contract, adapter, name) : null;\n const runPlans = runClosures.map((closure) => invokeAndLower(closure, contract, adapter, name));\n\n const precheck: readonly SqlMigrationPlanOperationStep[] = checkPlan\n ? [\n {\n description: `Check ${name} has work to do`,\n sql: `SELECT EXISTS (${checkPlan.sql}) AS ok`,\n params: checkPlan.params,\n },\n ]\n : [];\n\n const execute: readonly SqlMigrationPlanOperationStep[] = runPlans.map((plan) => ({\n description: `Run ${name}`,\n sql: plan.sql,\n params: plan.params,\n }));\n\n const postcheck: readonly SqlMigrationPlanOperationStep[] = checkPlan\n ? [\n {\n description: `Verify ${name} resolved all violations`,\n sql: `SELECT NOT EXISTS (${checkPlan.sql}) AS ok`,\n params: checkPlan.params,\n },\n ]\n : [];\n\n return {\n id: `data_migration.${name}`,\n label: `Data transform: ${name}`,\n operationClass: 'data',\n ...ifDefined('invariantId', options.invariantId),\n target: { id: 'postgres' },\n precheck,\n execute,\n postcheck,\n };\n}\n\nfunction invokeAndLower(\n closure: DataTransformClosure,\n contract: Contract<SqlStorage>,\n adapter: SqlControlAdapter<'postgres'>,\n name: string,\n): SerializedQueryPlan {\n const result = closure();\n const plan = isBuildable(result) ? result.build() : result;\n assertContractMatches(plan, contract, name);\n const lowered = adapter.lower(plan.ast, { contract });\n const params = lowered.params.map((slot) => {\n if (slot.kind === 'literal') return slot.value;\n throw new Error(\n `data-transform: bind-site slot '${slot.name}' is not allowed in migration plans`,\n );\n });\n return { sql: lowered.sql, params };\n}\n\nfunction isBuildable(value: unknown): value is Buildable {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'build' in value &&\n typeof (value as { build: unknown }).build === 'function'\n );\n}\n\nfunction assertContractMatches(\n plan: SqlQueryPlan,\n contract: Contract<SqlStorage>,\n name: string,\n): void {\n if (plan.meta.storageHash !== contract.storage.storageHash) {\n throw errorDataTransformContractMismatch({\n dataTransformName: name,\n expected: contract.storage.storageHash,\n actual: plan.meta.storageHash,\n });\n }\n}\n"],"mappings":";;;AAkGA,SAAgB,cACd,UACA,MACA,SACA,SACsD;CACtD,MAAM,cAA+C,MAAM,QAAQ,QAAQ,GAAG,IAC1E,QAAQ,MACR,CAAC,QAAQ,GAA2B;CAExC,MAAM,YAAY,QAAQ,QAAQ,eAAe,QAAQ,OAAO,UAAU,SAAS,IAAI,IAAI;CAC3F,MAAM,WAAW,YAAY,KAAK,YAAY,eAAe,SAAS,UAAU,SAAS,IAAI,CAAC;CAE9F,MAAM,WAAqD,YACvD,CACE;EACE,aAAa,SAAS,KAAK;EAC3B,KAAK,kBAAkB,UAAU,IAAI;EACrC,QAAQ,UAAU;CACpB,CACF,IACA,CAAC;CAEL,MAAM,UAAoD,SAAS,KAAK,UAAU;EAChF,aAAa,OAAO;EACpB,KAAK,KAAK;EACV,QAAQ,KAAK;CACf,EAAE;CAEF,MAAM,YAAsD,YACxD,CACE;EACE,aAAa,UAAU,KAAK;EAC5B,KAAK,sBAAsB,UAAU,IAAI;EACzC,QAAQ,UAAU;CACpB,CACF,IACA,CAAC;CAEL,OAAO;EACL,IAAI,kBAAkB;EACtB,OAAO,mBAAmB;EAC1B,gBAAgB;EAChB,GAAG,UAAU,eAAe,QAAQ,WAAW;EAC/C,QAAQ,EAAE,IAAI,WAAW;EACzB;EACA;EACA;CACF;AACF;AAEA,SAAS,eACP,SACA,UACA,SACA,MACqB;CACrB,MAAM,SAAS,QAAQ;CACvB,MAAM,OAAO,YAAY,MAAM,IAAI,OAAO,MAAM,IAAI;CACpD,sBAAsB,MAAM,UAAU,IAAI;CAC1C,MAAM,UAAU,QAAQ,MAAM,KAAK,KAAK,EAAE,SAAS,CAAC;CACpD,MAAM,SAAS,QAAQ,OAAO,KAAK,SAAS;EAC1C,IAAI,KAAK,SAAS,WAAW,OAAO,KAAK;EACzC,MAAM,IAAI,MACR,mCAAmC,KAAK,KAAK,oCAC/C;CACF,CAAC;CACD,OAAO;EAAE,KAAK,QAAQ;EAAK;CAAO;AACpC;AAEA,SAAS,YAAY,OAAoC;CACvD,OACE,OAAO,UAAU,YACjB,UAAU,QACV,WAAW,SACX,OAAQ,MAA6B,UAAU;AAEnD;AAEA,SAAS,sBACP,MACA,UACA,MACM;CACN,IAAI,KAAK,KAAK,gBAAgB,SAAS,QAAQ,aAC7C,MAAM,mCAAmC;EACvC,mBAAmB;EACnB,UAAU,SAAS,QAAQ;EAC3B,QAAQ,KAAK,KAAK;CACpB,CAAC;AAEL"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-transform-bmOKkygi.d.mts","names":[],"sources":["../src/core/migrations/operations/data-transform.ts"],"mappings":";;;;;;;;UAqEU,SAAA;EACR,KAAA,IAAS,
|
|
1
|
+
{"version":3,"file":"data-transform-bmOKkygi.d.mts","names":[],"sources":["../src/core/migrations/operations/data-transform.ts"],"mappings":";;;;;;;;UAqEU,SAAA;EACR,KAAA,IAAS,YAAY,CAAC,CAAA;AAAA;AAiC8B;;;;AAAA,KA1B1C,oBAAA,SAA6B,YAAA,GAAe,SAAS;AAAA,UAEhD,oBAAA;;;;;;WAMN,WAAA;;;;;;;;WAQA,KAAA,GAAQ,oBAAA;;WAER,GAAA,EAAK,oBAAA,YAAgC,oBAAA;AAAA;AAAA,iBAGhC,aAAA,mBAAgC,QAAA,CAAS,UAAA,EAAA,CACvD,QAAA,EAAU,SAAA,EACV,IAAA,UACA,OAAA,EAAS,oBAAA,EACT,OAAA,EAAS,iBAAA,eACR,yBAAA,CAA0B,yBAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-normalizer-
|
|
1
|
+
{"version":3,"file":"default-normalizer-CRscvhS5.mjs","names":[],"sources":["../src/core/default-normalizer.ts"],"sourcesContent":["import type { ColumnDefault } from '@prisma-next/contract/types';\n\n/**\n * Pre-compiled regex patterns for performance.\n * These are compiled once at module load time rather than on each function call.\n */\nconst NEXTVAL_PATTERN = /^nextval\\s*\\(/i;\nconst NOW_FUNCTION_PATTERN = /^(now\\s*\\(\\s*\\)|CURRENT_TIMESTAMP)$/i;\nconst CLOCK_TIMESTAMP_PATTERN = /^clock_timestamp\\s*\\(\\s*\\)$/i;\nconst TIMESTAMP_CAST_SUFFIX = /::timestamp(?:tz|\\s+(?:with|without)\\s+time\\s+zone)?$/i;\nconst TEXT_CAST_SUFFIX = /::text$/i;\nconst NOW_LITERAL_PATTERN = /^'now'$/i;\nconst UUID_PATTERN = /^gen_random_uuid\\s*\\(\\s*\\)$/i;\nconst UUID_OSSP_PATTERN = /^uuid_generate_v4\\s*\\(\\s*\\)$/i;\nconst NULL_PATTERN = /^NULL(?:::.+)?$/i;\nconst TRUE_PATTERN = /^true$/i;\nconst FALSE_PATTERN = /^false$/i;\nconst NUMERIC_PATTERN = /^-?\\d+(\\.\\d+)?$/;\nconst STRING_LITERAL_PATTERN = /^'((?:[^']|'')*)'(?:::(?:\"[^\"]+\"|[\\w\\s]+)(?:\\(\\d+\\))?)?$/;\n\n/**\n * Returns the canonical expression for a timestamp default function, or undefined\n * if the expression is not a recognized timestamp default.\n *\n * Keeps now()/CURRENT_TIMESTAMP and clock_timestamp() distinct:\n * - now(), CURRENT_TIMESTAMP, ('now'::text)::timestamp... → 'now()'\n * - clock_timestamp(), clock_timestamp()::timestamptz → 'clock_timestamp()'\n *\n * These are semantically different in Postgres: now() returns the transaction\n * start time (constant within a transaction), while clock_timestamp() returns\n * the actual wall-clock time (can differ across rows in a single INSERT).\n */\nfunction canonicalizeTimestampDefault(expr: string): string | undefined {\n if (NOW_FUNCTION_PATTERN.test(expr)) return 'now()';\n if (CLOCK_TIMESTAMP_PATTERN.test(expr)) return 'clock_timestamp()';\n\n if (!TIMESTAMP_CAST_SUFFIX.test(expr)) return undefined;\n\n let inner = expr.replace(TIMESTAMP_CAST_SUFFIX, '').trim();\n\n if (inner.startsWith('(') && inner.endsWith(')')) {\n inner = inner.slice(1, -1).trim();\n }\n\n if (NOW_FUNCTION_PATTERN.test(inner)) return 'now()';\n if (CLOCK_TIMESTAMP_PATTERN.test(inner)) return 'clock_timestamp()';\n\n inner = inner.replace(TEXT_CAST_SUFFIX, '').trim();\n if (NOW_LITERAL_PATTERN.test(inner)) return 'now()';\n\n return undefined;\n}\n\n/**\n * Parses a raw Postgres column default expression into a normalized ColumnDefault.\n * This enables semantic comparison between contract defaults and introspected schema defaults.\n *\n * Used by the migration diff layer to normalize raw database defaults during comparison,\n * keeping the introspection layer focused on faithful data capture.\n *\n * @param rawDefault - Raw default expression from information_schema.columns.column_default\n * @param nativeType - Native column type, used for type-aware parsing (bigint tagging, JSON detection)\n * @returns Normalized ColumnDefault or undefined if the expression cannot be parsed\n */\nexport function parsePostgresDefault(\n rawDefault: string,\n nativeType?: string,\n): ColumnDefault | undefined {\n const trimmed = rawDefault.trim();\n const normalizedType = nativeType?.toLowerCase();\n const isBigInt = normalizedType === 'bigint' || normalizedType === 'int8';\n\n if (NEXTVAL_PATTERN.test(trimmed)) {\n return { kind: 'function', expression: 'autoincrement()' };\n }\n\n const canonicalTimestamp = canonicalizeTimestampDefault(trimmed);\n if (canonicalTimestamp) {\n return { kind: 'function', expression: canonicalTimestamp };\n }\n\n if (UUID_PATTERN.test(trimmed)) {\n return { kind: 'function', expression: 'gen_random_uuid()' };\n }\n\n if (UUID_OSSP_PATTERN.test(trimmed)) {\n return { kind: 'function', expression: 'gen_random_uuid()' };\n }\n\n if (NULL_PATTERN.test(trimmed)) {\n return { kind: 'literal', value: null };\n }\n\n if (TRUE_PATTERN.test(trimmed)) {\n return { kind: 'literal', value: true };\n }\n if (FALSE_PATTERN.test(trimmed)) {\n return { kind: 'literal', value: false };\n }\n\n if (NUMERIC_PATTERN.test(trimmed)) {\n const num = Number(trimmed);\n if (!Number.isFinite(num)) return undefined;\n if (isBigInt && !Number.isSafeInteger(num)) {\n return { kind: 'literal', value: trimmed };\n }\n return { kind: 'literal', value: num };\n }\n\n const stringMatch = trimmed.match(STRING_LITERAL_PATTERN);\n if (stringMatch?.[1] !== undefined) {\n const unescaped = stringMatch[1].replace(/''/g, \"'\");\n if (normalizedType === 'json' || normalizedType === 'jsonb') {\n try {\n return { kind: 'literal', value: JSON.parse(unescaped) };\n } catch {\n // Keep legacy behavior for malformed/non-JSON string content.\n }\n }\n if (isBigInt && NUMERIC_PATTERN.test(unescaped)) {\n const num = Number(unescaped);\n if (Number.isSafeInteger(num)) {\n return { kind: 'literal', value: num };\n }\n return { kind: 'literal', value: unescaped };\n }\n return { kind: 'literal', value: unescaped };\n }\n\n return { kind: 'function', expression: trimmed };\n}\n"],"mappings":";;;;;AAMA,MAAM,kBAAkB;AACxB,MAAM,uBAAuB;AAC7B,MAAM,0BAA0B;AAChC,MAAM,wBAAwB;AAC9B,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;AAC5B,MAAM,eAAe;AACrB,MAAM,oBAAoB;AAC1B,MAAM,eAAe;AACrB,MAAM,eAAe;AACrB,MAAM,gBAAgB;AACtB,MAAM,kBAAkB;AACxB,MAAM,yBAAyB;;;;;;;;;;;;;AAc/B,SAAS,6BAA6B,MAAkC;CACtE,IAAI,qBAAqB,KAAK,IAAI,GAAG,OAAO;CAC5C,IAAI,wBAAwB,KAAK,IAAI,GAAG,OAAO;CAE/C,IAAI,CAAC,sBAAsB,KAAK,IAAI,GAAG,OAAO,KAAA;CAE9C,IAAI,QAAQ,KAAK,QAAQ,uBAAuB,EAAE,EAAE,KAAK;CAEzD,IAAI,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,GAC7C,QAAQ,MAAM,MAAM,GAAG,EAAE,EAAE,KAAK;CAGlC,IAAI,qBAAqB,KAAK,KAAK,GAAG,OAAO;CAC7C,IAAI,wBAAwB,KAAK,KAAK,GAAG,OAAO;CAEhD,QAAQ,MAAM,QAAQ,kBAAkB,EAAE,EAAE,KAAK;CACjD,IAAI,oBAAoB,KAAK,KAAK,GAAG,OAAO;AAG9C;;;;;;;;;;;;AAaA,SAAgB,qBACd,YACA,YAC2B;CAC3B,MAAM,UAAU,WAAW,KAAK;CAChC,MAAM,iBAAiB,YAAY,YAAY;CAC/C,MAAM,WAAW,mBAAmB,YAAY,mBAAmB;CAEnE,IAAI,gBAAgB,KAAK,OAAO,GAC9B,OAAO;EAAE,MAAM;EAAY,YAAY;CAAkB;CAG3D,MAAM,qBAAqB,6BAA6B,OAAO;CAC/D,IAAI,oBACF,OAAO;EAAE,MAAM;EAAY,YAAY;CAAmB;CAG5D,IAAI,aAAa,KAAK,OAAO,GAC3B,OAAO;EAAE,MAAM;EAAY,YAAY;CAAoB;CAG7D,IAAI,kBAAkB,KAAK,OAAO,GAChC,OAAO;EAAE,MAAM;EAAY,YAAY;CAAoB;CAG7D,IAAI,aAAa,KAAK,OAAO,GAC3B,OAAO;EAAE,MAAM;EAAW,OAAO;CAAK;CAGxC,IAAI,aAAa,KAAK,OAAO,GAC3B,OAAO;EAAE,MAAM;EAAW,OAAO;CAAK;CAExC,IAAI,cAAc,KAAK,OAAO,GAC5B,OAAO;EAAE,MAAM;EAAW,OAAO;CAAM;CAGzC,IAAI,gBAAgB,KAAK,OAAO,GAAG;EACjC,MAAM,MAAM,OAAO,OAAO;EAC1B,IAAI,CAAC,OAAO,SAAS,GAAG,GAAG,OAAO,KAAA;EAClC,IAAI,YAAY,CAAC,OAAO,cAAc,GAAG,GACvC,OAAO;GAAE,MAAM;GAAW,OAAO;EAAQ;EAE3C,OAAO;GAAE,MAAM;GAAW,OAAO;EAAI;CACvC;CAEA,MAAM,cAAc,QAAQ,MAAM,sBAAsB;CACxD,IAAI,cAAc,OAAO,KAAA,GAAW;EAClC,MAAM,YAAY,YAAY,GAAG,QAAQ,OAAO,GAAG;EACnD,IAAI,mBAAmB,UAAU,mBAAmB,SAClD,IAAI;GACF,OAAO;IAAE,MAAM;IAAW,OAAO,KAAK,MAAM,SAAS;GAAE;EACzD,QAAQ,CAER;EAEF,IAAI,YAAY,gBAAgB,KAAK,SAAS,GAAG;GAC/C,MAAM,MAAM,OAAO,SAAS;GAC5B,IAAI,OAAO,cAAc,GAAG,GAC1B,OAAO;IAAE,MAAM;IAAW,OAAO;GAAI;GAEvC,OAAO;IAAE,MAAM;IAAW,OAAO;GAAU;EAC7C;EACA,OAAO;GAAE,MAAM;GAAW,OAAO;EAAU;CAC7C;CAEA,OAAO;EAAE,MAAM;EAAY,YAAY;CAAQ;AACjD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-normalizer.d.mts","names":[],"sources":["../src/core/default-normalizer.ts"],"mappings":";;;;;AAgEA
|
|
1
|
+
{"version":3,"file":"default-normalizer.d.mts","names":[],"sources":["../src/core/default-normalizer.ts"],"mappings":";;;;;AAgEA;;;;;;;;AAGgB;iBAHA,oBAAA,CACd,UAAA,UACA,UAAA,YACC,aAAa"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as parsePostgresDefault } from "./default-normalizer-
|
|
1
|
+
import { t as parsePostgresDefault } from "./default-normalizer-CRscvhS5.mjs";
|
|
2
2
|
export { parsePostgresDefault };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"descriptor-meta-DLA2xV6B.mjs","names":[],"sources":["../src/core/authoring.ts","../src/core/descriptor-meta.ts"],"sourcesContent":["import { temporalAuthoringPresets } from '@prisma-next/family-sql/control';\nimport type {\n AuthoringEntityTypeNamespace,\n AuthoringFieldNamespace,\n AuthoringTypeNamespace,\n} from '@prisma-next/framework-components/authoring';\nimport type { PostgresEnumStorageEntry } from '@prisma-next/sql-contract/types';\nimport { PostgresEnumTypeSchema } from '@prisma-next/sql-contract/validators';\nimport { PostgresEnumType, type PostgresEnumTypeInput } from './postgres-enum-type';\n\nexport const postgresAuthoringTypes = {} as const satisfies AuthoringTypeNamespace;\n\n/**\n * Entity type contributions surface as top-level helpers on the\n * composed-helpers shape (e.g. `helpers.enum({...})`), flattened\n * alongside the built-in `model` / `rel` helpers. Pack contributions\n * still ship via the contribution data structure\n * `authoring.entityTypes.<name>`; the composed-helpers template\n * performs the rename in the type system.\n *\n * `enum` is the first real consumer of the entities-namespace mechanism:\n * the factory constructs a `PostgresEnumType` IR-class instance from\n * the user-supplied input. Both authoring runtimes (TS DSL and PSL)\n * dispatch through this single contribution — PSL `enum Status { … }`\n * declarations are lowered by the interpreter into a factory call\n * with the parsed name + value list; TS DSL `helpers.enum({...})`\n * resolves through the same path. Removing this contribution makes\n * both surfaces fail with a \"no entity helper named `enum`\" type\n * error at the contract-definition site.\n */\n/**\n * The factory constructs a `PostgresEnumType` instance natively — the\n * `SqlStorage.types` slot accepts polymorphic IR (the framework\n * `StorageType` alphabet), so no cast is needed at the contribution\n * surface. The declared return type is the structural\n * `PostgresEnumStorageEntry` so the inferred contract type stays\n * portable (it names a type exported from\n * `@prisma-next/sql-contract/types`, a public surface every consumer\n * already imports). Sharpening the inferred contract type to surface\n * enum-specific narrowing through `EntityHelperFunction` is a\n * separable refinement and lives outside this PR.\n */\nexport const postgresAuthoringEntityTypes = {\n enum: {\n kind: 'entity',\n discriminator: 'postgres-enum',\n validatorSchema: PostgresEnumTypeSchema,\n output: {\n factory: (input: PostgresEnumTypeInput): PostgresEnumStorageEntry =>\n new PostgresEnumType(input),\n },\n },\n} as const satisfies AuthoringEntityTypeNamespace;\n\n/**\n * Field presets contributed by the Postgres target pack.\n *\n * These mirror the PSL scalar-to-codec mapping used by the Postgres adapter\n * (see `createPostgresPslScalarTypeDescriptors`), so that authoring a field\n * via the TS callback surface (e.g. `field.int()`) and via the PSL scalar\n * surface (e.g. `Int`) lowers to byte-identical contracts.\n */\nexport const postgresAuthoringFieldPresets = {\n text: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/text@1',\n nativeType: 'text',\n },\n },\n int: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/int4@1',\n nativeType: 'int4',\n },\n },\n bigint: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/int8@1',\n nativeType: 'int8',\n },\n },\n float: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/float8@1',\n nativeType: 'float8',\n },\n },\n decimal: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/numeric@1',\n nativeType: 'numeric',\n },\n },\n boolean: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/bool@1',\n nativeType: 'bool',\n },\n },\n json: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/jsonb@1',\n nativeType: 'jsonb',\n },\n },\n bytes: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/bytea@1',\n nativeType: 'bytea',\n },\n },\n dateTime: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/timestamptz@1',\n nativeType: 'timestamptz',\n },\n },\n temporal: temporalAuthoringPresets({\n codecId: 'pg/timestamptz@1',\n nativeType: 'timestamptz',\n }),\n} as const satisfies AuthoringFieldNamespace;\n","import type { CodecTypes } from '../exports/codec-types';\nimport {\n postgresAuthoringEntityTypes,\n postgresAuthoringFieldPresets,\n postgresAuthoringTypes,\n} from './authoring';\n\nconst postgresTargetDescriptorMetaBase = {\n kind: 'target',\n familyId: 'sql',\n targetId: 'postgres',\n id: 'postgres',\n version: '0.0.1',\n capabilities: {},\n authoring: {\n type: postgresAuthoringTypes,\n field: postgresAuthoringFieldPresets,\n entityTypes: postgresAuthoringEntityTypes,\n },\n} as const;\n\nexport const postgresTargetDescriptorMeta: typeof postgresTargetDescriptorMetaBase & {\n readonly __codecTypes?: CodecTypes;\n} = postgresTargetDescriptorMetaBase;\n"],"mappings":";;;;AAUA,MAAa,yBAAyB,
|
|
1
|
+
{"version":3,"file":"descriptor-meta-DLA2xV6B.mjs","names":[],"sources":["../src/core/authoring.ts","../src/core/descriptor-meta.ts"],"sourcesContent":["import { temporalAuthoringPresets } from '@prisma-next/family-sql/control';\nimport type {\n AuthoringEntityTypeNamespace,\n AuthoringFieldNamespace,\n AuthoringTypeNamespace,\n} from '@prisma-next/framework-components/authoring';\nimport type { PostgresEnumStorageEntry } from '@prisma-next/sql-contract/types';\nimport { PostgresEnumTypeSchema } from '@prisma-next/sql-contract/validators';\nimport { PostgresEnumType, type PostgresEnumTypeInput } from './postgres-enum-type';\n\nexport const postgresAuthoringTypes = {} as const satisfies AuthoringTypeNamespace;\n\n/**\n * Entity type contributions surface as top-level helpers on the\n * composed-helpers shape (e.g. `helpers.enum({...})`), flattened\n * alongside the built-in `model` / `rel` helpers. Pack contributions\n * still ship via the contribution data structure\n * `authoring.entityTypes.<name>`; the composed-helpers template\n * performs the rename in the type system.\n *\n * `enum` is the first real consumer of the entities-namespace mechanism:\n * the factory constructs a `PostgresEnumType` IR-class instance from\n * the user-supplied input. Both authoring runtimes (TS DSL and PSL)\n * dispatch through this single contribution — PSL `enum Status { … }`\n * declarations are lowered by the interpreter into a factory call\n * with the parsed name + value list; TS DSL `helpers.enum({...})`\n * resolves through the same path. Removing this contribution makes\n * both surfaces fail with a \"no entity helper named `enum`\" type\n * error at the contract-definition site.\n */\n/**\n * The factory constructs a `PostgresEnumType` instance natively — the\n * `SqlStorage.types` slot accepts polymorphic IR (the framework\n * `StorageType` alphabet), so no cast is needed at the contribution\n * surface. The declared return type is the structural\n * `PostgresEnumStorageEntry` so the inferred contract type stays\n * portable (it names a type exported from\n * `@prisma-next/sql-contract/types`, a public surface every consumer\n * already imports). Sharpening the inferred contract type to surface\n * enum-specific narrowing through `EntityHelperFunction` is a\n * separable refinement and lives outside this PR.\n */\nexport const postgresAuthoringEntityTypes = {\n enum: {\n kind: 'entity',\n discriminator: 'postgres-enum',\n validatorSchema: PostgresEnumTypeSchema,\n output: {\n factory: (input: PostgresEnumTypeInput): PostgresEnumStorageEntry =>\n new PostgresEnumType(input),\n },\n },\n} as const satisfies AuthoringEntityTypeNamespace;\n\n/**\n * Field presets contributed by the Postgres target pack.\n *\n * These mirror the PSL scalar-to-codec mapping used by the Postgres adapter\n * (see `createPostgresPslScalarTypeDescriptors`), so that authoring a field\n * via the TS callback surface (e.g. `field.int()`) and via the PSL scalar\n * surface (e.g. `Int`) lowers to byte-identical contracts.\n */\nexport const postgresAuthoringFieldPresets = {\n text: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/text@1',\n nativeType: 'text',\n },\n },\n int: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/int4@1',\n nativeType: 'int4',\n },\n },\n bigint: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/int8@1',\n nativeType: 'int8',\n },\n },\n float: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/float8@1',\n nativeType: 'float8',\n },\n },\n decimal: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/numeric@1',\n nativeType: 'numeric',\n },\n },\n boolean: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/bool@1',\n nativeType: 'bool',\n },\n },\n json: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/jsonb@1',\n nativeType: 'jsonb',\n },\n },\n bytes: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/bytea@1',\n nativeType: 'bytea',\n },\n },\n dateTime: {\n kind: 'fieldPreset',\n output: {\n codecId: 'pg/timestamptz@1',\n nativeType: 'timestamptz',\n },\n },\n temporal: temporalAuthoringPresets({\n codecId: 'pg/timestamptz@1',\n nativeType: 'timestamptz',\n }),\n} as const satisfies AuthoringFieldNamespace;\n","import type { CodecTypes } from '../exports/codec-types';\nimport {\n postgresAuthoringEntityTypes,\n postgresAuthoringFieldPresets,\n postgresAuthoringTypes,\n} from './authoring';\n\nconst postgresTargetDescriptorMetaBase = {\n kind: 'target',\n familyId: 'sql',\n targetId: 'postgres',\n id: 'postgres',\n version: '0.0.1',\n capabilities: {},\n authoring: {\n type: postgresAuthoringTypes,\n field: postgresAuthoringFieldPresets,\n entityTypes: postgresAuthoringEntityTypes,\n },\n} as const;\n\nexport const postgresTargetDescriptorMeta: typeof postgresTargetDescriptorMetaBase & {\n readonly __codecTypes?: CodecTypes;\n} = postgresTargetDescriptorMetaBase;\n"],"mappings":";;;;AAUA,MAAa,yBAAyB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCvC,MAAa,+BAA+B,EAC1C,MAAM;CACJ,MAAM;CACN,eAAe;CACf,iBAAiB;CACjB,QAAQ,EACN,UAAU,UACR,IAAI,iBAAiB,KAAK,EAC9B;AACF,EACF;AC/BA,MAAa,+BAET;CAfF,MAAM;CACN,UAAU;CACV,UAAU;CACV,IAAI;CACJ,SAAS;CACT,cAAc,CAAC;CACf,WAAW;EACT,MAAM;EACN,OAAO;GD+CT,MAAM;IACJ,MAAM;IACN,QAAQ;KACN,SAAS;KACT,YAAY;IACd;GACF;GACA,KAAK;IACH,MAAM;IACN,QAAQ;KACN,SAAS;KACT,YAAY;IACd;GACF;GACA,QAAQ;IACN,MAAM;IACN,QAAQ;KACN,SAAS;KACT,YAAY;IACd;GACF;GACA,OAAO;IACL,MAAM;IACN,QAAQ;KACN,SAAS;KACT,YAAY;IACd;GACF;GACA,SAAS;IACP,MAAM;IACN,QAAQ;KACN,SAAS;KACT,YAAY;IACd;GACF;GACA,SAAS;IACP,MAAM;IACN,QAAQ;KACN,SAAS;KACT,YAAY;IACd;GACF;GACA,MAAM;IACJ,MAAM;IACN,QAAQ;KACN,SAAS;KACT,YAAY;IACd;GACF;GACA,OAAO;IACL,MAAM;IACN,QAAQ;KACN,SAAS;KACT,YAAY;IACd;GACF;GACA,UAAU;IACR,MAAM;IACN,QAAQ;KACN,SAAS;KACT,YAAY;IACd;GACF;GACA,UAAU,yBAAyB;IACjC,SAAS;IACT,YAAY;GACd,CAAC;ECjHQ;EACP,aAAa;CACf;AAKE"}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enum-planning-Dz0Ye3Lb.mjs","names":[],"sources":["../src/core/migrations/enum-planning.ts"],"sourcesContent":["/**\n * Pure planning helpers for Postgres enum types: the diff/rebuild logic\n * that the verifier and planner use to walk `PostgresEnumType` instances\n * natively. Op builders live in `./operations/enums.ts`.\n */\n\nimport { arraysEqual } from '@prisma-next/family-sql/schema-verify';\nimport { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';\nimport type { PostgresEnumStorageEntry, SqlStorage } from '@prisma-next/sql-contract/types';\nimport type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';\nimport { PG_ENUM_CODEC_ID } from '../codec-ids';\nimport type { PostgresEnumType } from '../postgres-enum-type';\nimport { isPostgresSchema } from '../postgres-schema';\n\n/**\n * Codec-typed enum entry shape stored under\n * `schema.annotations.pg.storageTypes[(schemaName, nativeType)]`.\n */\ninterface PgStorageTypeEntry {\n readonly codecId?: string;\n readonly typeParams?: { readonly values?: unknown };\n}\n\n/** Postgres-specific subtree on family `SqlSchemaIR.annotations`. */\nexport interface PostgresSchemaIrAnnotations {\n readonly schema?: string;\n readonly storageTypes?: Readonly<Record<string, PgStorageTypeEntry>>;\n}\n\nfunction readOptionalString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction readPgStorageTypeEntry(value: unknown): PgStorageTypeEntry | undefined {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n return undefined;\n }\n const codecId = Reflect.get(value, 'codecId');\n const typeParamsRaw = Reflect.get(value, 'typeParams');\n const typeParams =\n typeParamsRaw !== undefined &&\n typeParamsRaw !== null &&\n typeof typeParamsRaw === 'object' &&\n !Array.isArray(typeParamsRaw)\n ? { values: Reflect.get(typeParamsRaw, 'values') }\n : undefined;\n return {\n ...(typeof codecId === 'string' ? { codecId } : {}),\n ...(typeParams !== undefined ? { typeParams } : {}),\n };\n}\n\nfunction readPgStorageTypesMap(\n value: unknown,\n): Readonly<Record<string, PgStorageTypeEntry>> | undefined {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n return undefined;\n }\n const entries: Record<string, PgStorageTypeEntry> = {};\n for (const [key, entryValue] of Object.entries(value)) {\n const entry = readPgStorageTypeEntry(entryValue);\n if (entry !== undefined) {\n entries[key] = entry;\n }\n }\n return Object.keys(entries).length > 0 ? entries : undefined;\n}\n\n/**\n * Reads the Postgres annotation envelope (`schema.annotations.pg`) from\n * family Schema IR. `SqlAnnotations` is an open target-pack extensibility\n * map (`Record<string, unknown>`); this accessor narrows the `pg` slot at\n * runtime so Postgres code can read introspection fields without casts.\n */\nexport function readPostgresSchemaIrAnnotations(schema: SqlSchemaIR): PostgresSchemaIrAnnotations {\n const raw = schema.annotations?.['pg'];\n if (raw === undefined || raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n return {};\n }\n const schemaField = readOptionalString(Reflect.get(raw, 'schema'));\n const storageTypes = readPgStorageTypesMap(Reflect.get(raw, 'storageTypes'));\n return {\n ...(schemaField !== undefined ? { schema: schemaField } : {}),\n ...(storageTypes !== undefined ? { storageTypes } : {}),\n };\n}\n\n/**\n * Separator for `(schemaName, nativeType)` keys in introspected\n * `schema.annotations.pg.storageTypes`. NUL cannot appear in Postgres\n * identifiers, so the pair is unambiguous.\n */\nexport const ENUM_STORAGE_KEY_SEP = '\\u0000';\n\n/** Builds the schema-qualified storageTypes map key for a live Postgres enum. */\nexport function enumStorageCompoundKey(schemaName: string, nativeType: string): string {\n return `${schemaName}${ENUM_STORAGE_KEY_SEP}${nativeType}`;\n}\n\n/**\n * Resolves the live-schema name a namespace's enums are introspected under,\n * for keying `readExistingEnumValues` lookups. The unbound namespace's\n * `ddlSchemaName` is a planner-emit sentinel (`__unbound__`) that never names a\n * real schema, so for the unbound coordinate we read the *introspected* schema\n * recorded on `annotations.pg.schema` (the live `current_schema()` the adapter\n * walked) — that is the schema the enum's `storageTypes` entry is keyed under.\n * Named namespaces resolve to their own DDL schema, which matches the\n * per-schema introspection key directly.\n */\nexport function resolveDdlSchemaForNamespaceStorage(\n storage: SqlStorage,\n namespaceId: string,\n schemaIr?: SqlSchemaIR,\n): string {\n if (namespaceId === UNBOUND_NAMESPACE_ID) {\n return (schemaIr ? readPostgresSchemaIrAnnotations(schemaIr).schema : undefined) ?? 'public';\n }\n const namespace = storage.namespaces[namespaceId];\n if (namespace && isPostgresSchema(namespace)) {\n return namespace.ddlSchemaName(storage);\n }\n return namespaceId;\n}\n\n/** Contract-scoped bridge for the family verifier's enum value resolver. */\nexport function createResolveExistingEnumValues(\n storage: SqlStorage,\n): (\n schema: SqlSchemaIR,\n enumType: PostgresEnumStorageEntry,\n namespaceId: string,\n) => readonly string[] | null {\n return (schema, enumType, namespaceId) =>\n readExistingEnumValues(\n schema,\n resolveDdlSchemaForNamespaceStorage(storage, namespaceId, schema),\n enumType.nativeType,\n );\n}\n\n/**\n * Categorisation of how an existing enum type's values relate to the\n * desired set in the contract.\n */\nexport type EnumDiff =\n | { readonly kind: 'unchanged' }\n | { readonly kind: 'add_values'; readonly values: readonly string[] }\n | { readonly kind: 'rebuild'; readonly removedValues: readonly string[] };\n\n/**\n * Reads existing enum values for `(schemaName, nativeType)` from the\n * Postgres-introspected `schema.annotations.pg.storageTypes` map.\n *\n * Schema IR's `storageTypes` slots are always codec-typed\n * (`{codecId: PG_ENUM_CODEC_ID, typeParams.values}`): the introspector\n * writes that shape, and the Contract→Schema IR projector resolves\n * `PostgresEnumType` instances down to the same codec-typed triple before\n * they ever land in Schema IR. There is no second on-disk shape to\n * accept here.\n *\n * Returns `null` when no enum entry exists for the given native type.\n */\nexport function readExistingEnumValues(\n schema: SqlSchemaIR,\n schemaName: string,\n nativeType: string,\n): readonly string[] | null {\n const storageTypes = readPostgresSchemaIrAnnotations(schema).storageTypes;\n const existing = storageTypes?.[enumStorageCompoundKey(schemaName, nativeType)];\n if (!existing || existing.codecId !== PG_ENUM_CODEC_ID) {\n return null;\n }\n const enumValues = existing.typeParams?.values;\n if (!Array.isArray(enumValues) || !enumValues.every((v) => typeof v === 'string')) {\n return null;\n }\n return enumValues;\n}\n\n/**\n * Determines what changes are needed to transform existing enum values to\n * desired values.\n *\n * Postgres enums can only have values added (not removed or reordered)\n * without a full type rebuild involving temp type creation and column\n * migration; `'rebuild'` covers the value-removal and reorder cases.\n */\nexport function determineEnumDiff(\n existing: readonly string[],\n desired: readonly string[],\n): EnumDiff {\n if (arraysEqual(existing, desired)) {\n return { kind: 'unchanged' };\n }\n const existingSet = new Set(existing);\n const desiredSet = new Set(desired);\n const missingValues = desired.filter((value) => !existingSet.has(value));\n const removedValues = existing.filter((value) => !desiredSet.has(value));\n const orderMismatch =\n missingValues.length === 0 && removedValues.length === 0 && !arraysEqual(existing, desired);\n if (removedValues.length > 0 || orderMismatch) {\n return { kind: 'rebuild', removedValues };\n }\n return { kind: 'add_values', values: missingValues };\n}\n\n/**\n * Convenience accessor — returns the enum's desired values from a\n * `PostgresEnumType` IR instance.\n */\nexport function getDesiredEnumValues(typeInstance: PostgresEnumType): readonly string[] {\n return typeInstance.values;\n}\n"],"mappings":"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"}
|
package/dist/enum-planning.d.mts
CHANGED
|
@@ -1,7 +1,45 @@
|
|
|
1
1
|
import { t as PostgresEnumType } from "./postgres-enum-type-CNhPTDhy.mjs";
|
|
2
|
+
import { PostgresEnumStorageEntry, SqlStorage } from "@prisma-next/sql-contract/types";
|
|
2
3
|
import { SqlSchemaIR } from "@prisma-next/sql-schema-ir/types";
|
|
3
4
|
|
|
4
5
|
//#region src/core/migrations/enum-planning.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Codec-typed enum entry shape stored under
|
|
8
|
+
* `schema.annotations.pg.storageTypes[(schemaName, nativeType)]`.
|
|
9
|
+
*/
|
|
10
|
+
interface PgStorageTypeEntry {
|
|
11
|
+
readonly codecId?: string;
|
|
12
|
+
readonly typeParams?: {
|
|
13
|
+
readonly values?: unknown;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/** Postgres-specific subtree on family `SqlSchemaIR.annotations`. */
|
|
17
|
+
interface PostgresSchemaIrAnnotations {
|
|
18
|
+
readonly schema?: string;
|
|
19
|
+
readonly storageTypes?: Readonly<Record<string, PgStorageTypeEntry>>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Reads the Postgres annotation envelope (`schema.annotations.pg`) from
|
|
23
|
+
* family Schema IR. `SqlAnnotations` is an open target-pack extensibility
|
|
24
|
+
* map (`Record<string, unknown>`); this accessor narrows the `pg` slot at
|
|
25
|
+
* runtime so Postgres code can read introspection fields without casts.
|
|
26
|
+
*/
|
|
27
|
+
declare function readPostgresSchemaIrAnnotations(schema: SqlSchemaIR): PostgresSchemaIrAnnotations;
|
|
28
|
+
/** Builds the schema-qualified storageTypes map key for a live Postgres enum. */
|
|
29
|
+
declare function enumStorageCompoundKey(schemaName: string, nativeType: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Resolves the live-schema name a namespace's enums are introspected under,
|
|
32
|
+
* for keying `readExistingEnumValues` lookups. The unbound namespace's
|
|
33
|
+
* `ddlSchemaName` is a planner-emit sentinel (`__unbound__`) that never names a
|
|
34
|
+
* real schema, so for the unbound coordinate we read the *introspected* schema
|
|
35
|
+
* recorded on `annotations.pg.schema` (the live `current_schema()` the adapter
|
|
36
|
+
* walked) — that is the schema the enum's `storageTypes` entry is keyed under.
|
|
37
|
+
* Named namespaces resolve to their own DDL schema, which matches the
|
|
38
|
+
* per-schema introspection key directly.
|
|
39
|
+
*/
|
|
40
|
+
declare function resolveDdlSchemaForNamespaceStorage(storage: SqlStorage, namespaceId: string, schemaIr?: SqlSchemaIR): string;
|
|
41
|
+
/** Contract-scoped bridge for the family verifier's enum value resolver. */
|
|
42
|
+
declare function createResolveExistingEnumValues(storage: SqlStorage): (schema: SqlSchemaIR, enumType: PostgresEnumStorageEntry, namespaceId: string) => readonly string[] | null;
|
|
5
43
|
/**
|
|
6
44
|
* Categorisation of how an existing enum type's values relate to the
|
|
7
45
|
* desired set in the contract.
|
|
@@ -16,7 +54,7 @@ type EnumDiff = {
|
|
|
16
54
|
readonly removedValues: readonly string[];
|
|
17
55
|
};
|
|
18
56
|
/**
|
|
19
|
-
* Reads existing enum values for `nativeType` from the
|
|
57
|
+
* Reads existing enum values for `(schemaName, nativeType)` from the
|
|
20
58
|
* Postgres-introspected `schema.annotations.pg.storageTypes` map.
|
|
21
59
|
*
|
|
22
60
|
* Schema IR's `storageTypes` slots are always codec-typed
|
|
@@ -28,7 +66,7 @@ type EnumDiff = {
|
|
|
28
66
|
*
|
|
29
67
|
* Returns `null` when no enum entry exists for the given native type.
|
|
30
68
|
*/
|
|
31
|
-
declare function readExistingEnumValues(schema: SqlSchemaIR, nativeType: string): readonly string[] | null;
|
|
69
|
+
declare function readExistingEnumValues(schema: SqlSchemaIR, schemaName: string, nativeType: string): readonly string[] | null;
|
|
32
70
|
/**
|
|
33
71
|
* Determines what changes are needed to transform existing enum values to
|
|
34
72
|
* desired values.
|
|
@@ -44,5 +82,5 @@ declare function determineEnumDiff(existing: readonly string[], desired: readonl
|
|
|
44
82
|
*/
|
|
45
83
|
declare function getDesiredEnumValues(typeInstance: PostgresEnumType): readonly string[];
|
|
46
84
|
//#endregion
|
|
47
|
-
export { type EnumDiff, determineEnumDiff, getDesiredEnumValues, readExistingEnumValues };
|
|
85
|
+
export { type EnumDiff, type PostgresSchemaIrAnnotations, createResolveExistingEnumValues, determineEnumDiff, enumStorageCompoundKey, getDesiredEnumValues, readExistingEnumValues, readPostgresSchemaIrAnnotations, resolveDdlSchemaForNamespaceStorage };
|
|
48
86
|
//# sourceMappingURL=enum-planning.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enum-planning.d.mts","names":[],"sources":["../src/core/migrations/enum-planning.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"enum-planning.d.mts","names":[],"sources":["../src/core/migrations/enum-planning.ts"],"mappings":";;;;;;;;;UAkBU,kBAAA;EAAA,SACC,OAAA;EAAA,SACA,UAAA;IAAA,SAAwB,MAAA;EAAA;AAAA;;UAIlB,2BAAA;EAAA,SACN,MAAA;EAAA,SACA,YAAA,GAAe,QAAA,CAAS,MAAA,SAAe,kBAAA;AAAA;;;;;;AAAkB;iBAgDpD,+BAAA,CAAgC,MAAA,EAAQ,WAAA,GAAc,2BAA2B;;iBAqBjF,sBAAA,CAAuB,UAAA,UAAoB,UAAkB;AAA7E;;;;AAA6E;AAc7E;;;;;AAdA,iBAcgB,mCAAA,CACd,OAAA,EAAS,UAAA,EACT,WAAA,UACA,QAAA,GAAW,WAAW;;iBAaR,+BAAA,CACd,OAAA,EAAS,UAAA,IAET,MAAA,EAAQ,WAAA,EACR,QAAA,EAAU,wBAAA,EACV,WAAA;;;AAlBsB;AAaxB;KAmBY,QAAA;EAAA,SACG,IAAA;AAAA;EAAA,SACA,IAAA;EAAA,SAA6B,MAAA;AAAA;EAAA,SAC7B,IAAA;EAAA,SAA0B,aAAA;AAAA;;;;;;;AAjBpB;AAcrB;;;;;;iBAkBgB,sBAAA,CACd,MAAA,EAAQ,WAAW,EACnB,UAAA,UACA,UAAA;;;;AAlBoD;AAetD;;;;iBAyBgB,iBAAA,CACd,QAAA,qBACA,OAAA,sBACC,QAAQ;;;;;iBAoBK,oBAAA,CAAqB,YAA8B,EAAhB,gBAAgB"}
|
package/dist/enum-planning.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { determineEnumDiff, getDesiredEnumValues, readExistingEnumValues };
|
|
1
|
+
import { a as readExistingEnumValues, i as getDesiredEnumValues, n as determineEnumDiff, o as readPostgresSchemaIrAnnotations, r as enumStorageCompoundKey, s as resolveDdlSchemaForNamespaceStorage, t as createResolveExistingEnumValues } from "./enum-planning-Dz0Ye3Lb.mjs";
|
|
2
|
+
export { createResolveExistingEnumValues, determineEnumDiff, enumStorageCompoundKey, getDesiredEnumValues, readExistingEnumValues, readPostgresSchemaIrAnnotations, resolveDdlSchemaForNamespaceStorage };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors--zafB5_n.mjs","names":[],"sources":["../src/core/errors.ts"],"sourcesContent":["import { CliStructuredError } from '@prisma-next/errors/control';\n\n/**\n * A `PostgresMigration` instance method that needs the materialized control\n * adapter (currently only `this.dataTransform(...)`) was invoked, but the\n * migration was constructed without a `ControlStack`. Concrete authoring\n * usage always goes through the migration CLI entrypoint, which assembles\n * a stack from the loaded `prisma-next.config.ts`; reaching this error\n * means a test fixture or ad-hoc consumer instantiated `PostgresMigration`\n * with the no-arg form (legal for `operations` / `describe` introspection\n * only).\n *\n * Distinct from `PN-MIG-2001` (placeholder not filled) and `PN-MIG-2005`\n * (data-transform query plan against wrong contract) because the missing\n * input is the stack itself, not the per-operation contract.\n *\n * Lives in `@prisma-next/target-postgres/errors` rather than the shared\n * framework migration errors module because the failure is target-specific:\n * the contract it talks about (`PostgresMigration`, the Postgres control\n * adapter, the Postgres-target stack) only exists in this package.\n */\nexport function errorPostgresMigrationStackMissing(): CliStructuredError {\n return new CliStructuredError(\n '2007',\n 'PostgresMigration.dataTransform requires a control adapter',\n {\n domain: 'MIG',\n why: 'PostgresMigration.dataTransform was invoked on an instance constructed without a ControlStack. The stored controlAdapter is undefined, so dataTransform cannot lower its query plan.',\n fix: 'Construct the migration via the migration CLI entrypoint (which assembles a ControlStack from the loaded prisma-next.config.ts), or pass a ControlStack containing a Postgres adapter to the migration constructor in test fixtures.',\n meta: {},\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,qCAAyD;CACvE,OAAO,IAAI,mBACT,QACA,8DACA;EACE,QAAQ;EACR,KAAK;EACL,KAAK;EACL,MAAM,
|
|
1
|
+
{"version":3,"file":"errors--zafB5_n.mjs","names":[],"sources":["../src/core/errors.ts"],"sourcesContent":["import { CliStructuredError } from '@prisma-next/errors/control';\n\n/**\n * A `PostgresMigration` instance method that needs the materialized control\n * adapter (currently only `this.dataTransform(...)`) was invoked, but the\n * migration was constructed without a `ControlStack`. Concrete authoring\n * usage always goes through the migration CLI entrypoint, which assembles\n * a stack from the loaded `prisma-next.config.ts`; reaching this error\n * means a test fixture or ad-hoc consumer instantiated `PostgresMigration`\n * with the no-arg form (legal for `operations` / `describe` introspection\n * only).\n *\n * Distinct from `PN-MIG-2001` (placeholder not filled) and `PN-MIG-2005`\n * (data-transform query plan against wrong contract) because the missing\n * input is the stack itself, not the per-operation contract.\n *\n * Lives in `@prisma-next/target-postgres/errors` rather than the shared\n * framework migration errors module because the failure is target-specific:\n * the contract it talks about (`PostgresMigration`, the Postgres control\n * adapter, the Postgres-target stack) only exists in this package.\n */\nexport function errorPostgresMigrationStackMissing(): CliStructuredError {\n return new CliStructuredError(\n '2007',\n 'PostgresMigration.dataTransform requires a control adapter',\n {\n domain: 'MIG',\n why: 'PostgresMigration.dataTransform was invoked on an instance constructed without a ControlStack. The stored controlAdapter is undefined, so dataTransform cannot lower its query plan.',\n fix: 'Construct the migration via the migration CLI entrypoint (which assembles a ControlStack from the loaded prisma-next.config.ts), or pass a ControlStack containing a Postgres adapter to the migration constructor in test fixtures.',\n meta: {},\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,qCAAyD;CACvE,OAAO,IAAI,mBACT,QACA,8DACA;EACE,QAAQ;EACR,KAAK;EACL,KAAK;EACL,MAAM,CAAC;CACT,CACF;AACF"}
|
package/dist/errors.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.mts","names":[],"sources":["../src/core/errors.ts"],"mappings":";;;;;AAqBA
|
|
1
|
+
{"version":3,"file":"errors.d.mts","names":[],"sources":["../src/core/errors.ts"],"mappings":";;;;;AAqBA;;;;AAAwE;;;;;;;;;;;;;iBAAxD,kCAAA,CAAA,GAAsC,kBAAkB"}
|