prisma-next 0.5.0-dev.8 → 0.5.0-dev.81
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/cli-errors-B9OBbled.d.mts +3 -0
- package/dist/cli-errors-D3_sMh2K.mjs +33 -0
- package/dist/cli-errors-D3_sMh2K.mjs.map +1 -0
- package/dist/cli.mjs +16 -78
- package/dist/cli.mjs.map +1 -1
- package/dist/client-qVH-rEgd.mjs +1595 -0
- package/dist/client-qVH-rEgd.mjs.map +1 -0
- package/dist/{result-handler-Ba3zWQsI.mjs → command-helpers-BeZHkxV8.mjs} +70 -47
- package/dist/command-helpers-BeZHkxV8.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts.map +1 -1
- package/dist/commands/contract-emit.mjs +2 -4
- package/dist/commands/contract-infer.d.mts.map +1 -1
- package/dist/commands/contract-infer.mjs +2 -4
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +16 -13
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.d.mts.map +1 -1
- package/dist/commands/db-schema.mjs +6 -7
- package/dist/commands/db-schema.mjs.map +1 -1
- package/dist/commands/db-sign.d.mts.map +1 -1
- package/dist/commands/db-sign.mjs +9 -9
- package/dist/commands/db-sign.mjs.map +1 -1
- package/dist/commands/db-update.d.mts.map +1 -1
- package/dist/commands/db-update.mjs +15 -13
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.d.mts.map +1 -1
- package/dist/commands/db-verify.mjs +1 -321
- package/dist/commands/migration-apply.d.mts +28 -13
- package/dist/commands/migration-apply.d.mts.map +1 -1
- package/dist/commands/migration-apply.mjs +55 -151
- package/dist/commands/migration-apply.mjs.map +1 -1
- package/dist/commands/migration-new.d.mts +0 -1
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +34 -40
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +33 -6
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +2 -348
- package/dist/commands/migration-ref.d.mts +1 -1
- package/dist/commands/migration-ref.d.mts.map +1 -1
- package/dist/commands/migration-ref.mjs +8 -12
- package/dist/commands/migration-ref.mjs.map +1 -1
- package/dist/commands/migration-show.d.mts +13 -7
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +35 -36
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +126 -5
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +2 -4
- package/dist/{config-loader-C25b63rJ.mjs → config-loader-B6sJjXTv.mjs} +3 -5
- package/dist/config-loader-B6sJjXTv.mjs.map +1 -0
- package/dist/config-loader.d.mts +0 -1
- package/dist/config-loader.d.mts.map +1 -1
- package/dist/config-loader.mjs +2 -3
- package/dist/contract-emit-9DBda5Ou.mjs +150 -0
- package/dist/contract-emit-9DBda5Ou.mjs.map +1 -0
- package/dist/contract-emit-B77TsJqf.mjs +327 -0
- package/dist/contract-emit-B77TsJqf.mjs.map +1 -0
- package/dist/{contract-enrichment-CAOELa-H.mjs → contract-enrichment-Dani0mMW.mjs} +4 -6
- package/dist/contract-enrichment-Dani0mMW.mjs.map +1 -0
- package/dist/{contract-infer-D9cC3rJm.mjs → contract-infer-BK9YFGEG.mjs} +13 -22
- package/dist/contract-infer-BK9YFGEG.mjs.map +1 -0
- package/dist/db-verify-C0y1PCO2.mjs +404 -0
- package/dist/db-verify-C0y1PCO2.mjs.map +1 -0
- package/dist/exports/config-types.mjs +1 -2
- package/dist/exports/control-api.d.mts +101 -586
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +4 -6
- package/dist/exports/index.d.mts.map +1 -1
- package/dist/exports/index.mjs +28 -30
- package/dist/exports/index.mjs.map +1 -1
- package/dist/exports/init-output.d.mts +2 -4
- package/dist/exports/init-output.d.mts.map +1 -1
- package/dist/exports/init-output.mjs +2 -3
- package/dist/extension-pack-inputs-C7xgE-vv.mjs +74 -0
- package/dist/extension-pack-inputs-C7xgE-vv.mjs.map +1 -0
- package/dist/{framework-components-Cr--XBKy.mjs → framework-components-ChqVUxR-.mjs} +3 -4
- package/dist/{framework-components-Cr--XBKy.mjs.map → framework-components-ChqVUxR-.mjs.map} +1 -1
- package/dist/global-flags-Icqpxk23.d.mts +12 -0
- package/dist/global-flags-Icqpxk23.d.mts.map +1 -0
- package/dist/helpers-eqdN8tH6.mjs +25 -0
- package/dist/helpers-eqdN8tH6.mjs.map +1 -0
- package/dist/{init-C5220SY9.mjs → init-CoDVPvQ4.mjs} +26 -35
- package/dist/init-CoDVPvQ4.mjs.map +1 -0
- package/dist/{inspect-live-schema-yrHAvG71.mjs → inspect-live-schema-CWYxGKlb.mjs} +10 -11
- package/dist/inspect-live-schema-CWYxGKlb.mjs.map +1 -0
- package/dist/migration-cli.d.mts +41 -12
- package/dist/migration-cli.d.mts.map +1 -1
- package/dist/migration-cli.mjs +309 -86
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-B3B09et6.mjs → migration-command-scaffold-B5dORFEv.mjs} +8 -9
- package/dist/migration-command-scaffold-B5dORFEv.mjs.map +1 -0
- package/dist/migration-plan-C6lVaHsO.mjs +554 -0
- package/dist/migration-plan-C6lVaHsO.mjs.map +1 -0
- package/dist/{migration-status-DUMiH8_G.mjs → migration-status-CZ-D5k7k.mjs} +272 -65
- package/dist/migration-status-CZ-D5k7k.mjs.map +1 -0
- package/dist/migrations-D_UJnpuW.mjs +216 -0
- package/dist/migrations-D_UJnpuW.mjs.map +1 -0
- package/dist/{output-BpcQrnnq.mjs → output-B16Kefzx.mjs} +9 -3
- package/dist/output-B16Kefzx.mjs.map +1 -0
- package/dist/{progress-adapter-DvQWB1nK.mjs → progress-adapter-DFfvZcYL.mjs} +2 -2
- package/dist/{progress-adapter-DvQWB1nK.mjs.map → progress-adapter-DFfvZcYL.mjs.map} +1 -1
- package/dist/result-handler-rmPVKIP2.mjs +25 -0
- package/dist/result-handler-rmPVKIP2.mjs.map +1 -0
- package/dist/rolldown-runtime-twds-ZHy.mjs +14 -0
- package/dist/{terminal-ui-C3ZLwQxK.mjs → terminal-ui-C_hFNbAn.mjs} +4 -28
- package/dist/terminal-ui-C_hFNbAn.mjs.map +1 -0
- package/dist/types-D7x-IFLO.d.mts +858 -0
- package/dist/types-D7x-IFLO.d.mts.map +1 -0
- package/dist/{verify-Bkycc-Tf.mjs → verify-CiwNWM9N.mjs} +3 -4
- package/dist/verify-CiwNWM9N.mjs.map +1 -0
- package/package.json +19 -17
- package/dist/cli-errors-BFYgBH3L.d.mts +0 -4
- package/dist/cli-errors-Cd79vmTH.mjs +0 -5
- package/dist/client-CrsnY58k.mjs +0 -997
- package/dist/client-CrsnY58k.mjs.map +0 -1
- package/dist/commands/db-verify.mjs.map +0 -1
- package/dist/commands/migration-plan.mjs.map +0 -1
- package/dist/config-loader-C25b63rJ.mjs.map +0 -1
- package/dist/contract-emit--feXyNd7.mjs +0 -4
- package/dist/contract-emit-NJ01hiiv.mjs +0 -195
- package/dist/contract-emit-NJ01hiiv.mjs.map +0 -1
- package/dist/contract-emit-V5SSitUT.mjs +0 -122
- package/dist/contract-emit-V5SSitUT.mjs.map +0 -1
- package/dist/contract-enrichment-CAOELa-H.mjs.map +0 -1
- package/dist/contract-infer-D9cC3rJm.mjs.map +0 -1
- package/dist/extract-operation-statements-DsFfxXVZ.mjs +0 -13
- package/dist/extract-operation-statements-DsFfxXVZ.mjs.map +0 -1
- package/dist/extract-sql-ddl-D9UbZDyz.mjs +0 -26
- package/dist/extract-sql-ddl-D9UbZDyz.mjs.map +0 -1
- package/dist/init-C5220SY9.mjs.map +0 -1
- package/dist/inspect-live-schema-yrHAvG71.mjs.map +0 -1
- package/dist/migration-command-scaffold-B3B09et6.mjs.map +0 -1
- package/dist/migration-status-DUMiH8_G.mjs.map +0 -1
- package/dist/migrations-Bo5WtTla.mjs +0 -153
- package/dist/migrations-Bo5WtTla.mjs.map +0 -1
- package/dist/output-BpcQrnnq.mjs.map +0 -1
- package/dist/result-handler-Ba3zWQsI.mjs.map +0 -1
- package/dist/terminal-ui-C3ZLwQxK.mjs.map +0 -1
- package/dist/validate-contract-deps-B_Cs29TL.mjs +0 -37
- package/dist/validate-contract-deps-B_Cs29TL.mjs.map +0 -1
- package/dist/verify-Bkycc-Tf.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-plan-C6lVaHsO.mjs","names":[],"sources":["../src/utils/contract-space-extension-migrations-pass.ts","../src/utils/contract-space-migrate-pass.ts","../src/commands/migration-plan.ts"],"sourcesContent":["import { materialiseExtensionMigrationPackageIfMissing } from '@prisma-next/migration-tools/io';\nimport type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';\nimport type { MigrationOps } from '@prisma-next/migration-tools/package';\nimport {\n planAllSpaces,\n type SpacePlanOutput,\n spaceMigrationDirectory,\n} from '@prisma-next/migration-tools/spaces';\n\n/**\n * In-memory authored migration package shipped by an extension descriptor.\n * Mirrors `MigrationPackage` from `@prisma-next/migration-tools/io`\n * (the on-disk shape minus `dirPath`); redeclared structurally here so\n * the CLI helper does not couple to the SQL family's `ExtensionMigrationPackage`\n * type — any family that ships pre-built migration packages can pass them\n * through unchanged.\n */\nexport interface DescriptorMigrationPackage {\n readonly dirName: string;\n readonly metadata: MigrationMetadata;\n readonly ops: MigrationOps;\n}\n\n/**\n * Minimal descriptor view consumed by the migration-materialisation pass.\n * Mirrors {@link import('./contract-space-migrate-pass').MigrateExtensionInput}\n * but adds the `migrations` field — the canonical set of pre-built\n * migration packages the extension ships.\n */\nexport interface ExtensionMigrationsExtensionInput {\n readonly id: string;\n readonly contractSpace?: {\n readonly contractJson: unknown;\n readonly migrations: readonly DescriptorMigrationPackage[];\n readonly headRef: { readonly hash: string; readonly invariants: readonly string[] };\n };\n}\n\nexport interface ContractSpaceExtensionMigrationsPassInputs {\n readonly migrationsDir: string;\n readonly extensionPacks: ReadonlyArray<ExtensionMigrationsExtensionInput>;\n}\n\nexport interface ContractSpaceExtensionMigrationsPassResult {\n readonly emitted: readonly { readonly spaceId: string; readonly dirName: string }[];\n readonly skipped: readonly { readonly spaceId: string; readonly dirName: string }[];\n}\n\n/**\n * Materialise an extension's pre-built migration packages onto disk\n * under `migrations/<spaceId>/<dirName>/` for every package that does\n * not yet exist there.\n *\n * Helper-location pattern — the per-space \"planner\" for extension\n * spaces is a no-op that just returns the descriptor's `migrations`\n * verbatim; the value `planAllSpaces` brings to this consumer site is\n * **deterministic ordering** (alphabetical by spaceId) and\n * **duplicate-spaceId detection**. The actual write is performed via\n * `materialiseMigrationPackage` per package.\n *\n * Idempotent: an existing `migrations/<spaceId>/<dirName>/` is left\n * untouched and reported in `result.skipped` — the helper never\n * overwrites authored migration content, ensuring re-running\n * `migrate` does not corrupt or churn extension migration packages.\n *\n * Per-space artefacts (`contract.json`, `contract.d.ts`,\n * `refs/head.json`) are emitted by\n * {@link import('./contract-space-migrate-pass').runContractSpaceMigratePass}\n * separately — they cover the head-pointer side of the ledger. This\n * helper covers the migration-graph side.\n */\nexport async function runContractSpaceExtensionMigrationsPass(\n inputs: ContractSpaceExtensionMigrationsPassInputs,\n): Promise<ContractSpaceExtensionMigrationsPassResult> {\n const planInputs = inputs.extensionPacks\n .filter(\n (\n pack,\n ): pack is ExtensionMigrationsExtensionInput & {\n contractSpace: NonNullable<ExtensionMigrationsExtensionInput['contractSpace']>;\n } => pack.contractSpace !== undefined,\n )\n .map((pack) => ({\n spaceId: pack.id,\n priorContract: null,\n newContract: pack.contractSpace.contractJson,\n __migrations: pack.contractSpace.migrations,\n }));\n\n // Threading the descriptor's pre-built migrations into the\n // `planAllSpaces` callback by piggybacking on the input shape.\n // The framework helper is generic over the per-space planner output;\n // here the \"planner\" is a no-op that returns the descriptor's\n // `migrations` array. The benefit of routing through `planAllSpaces`\n // is duplicate-spaceId detection + alphabetical ordering — failures\n // there throw `MIGRATION.DUPLICATE_SPACE_ID` before any write.\n const planned: readonly SpacePlanOutput<DescriptorMigrationPackage>[] = planAllSpaces(\n planInputs,\n (input) =>\n (input as typeof input & { readonly __migrations: readonly DescriptorMigrationPackage[] })\n .__migrations,\n );\n\n const emitted: { spaceId: string; dirName: string }[] = [];\n const skipped: { spaceId: string; dirName: string }[] = [];\n\n for (const space of planned) {\n const spaceDir = spaceMigrationDirectory(inputs.migrationsDir, space.spaceId);\n for (const pkg of space.migrationPackages) {\n const { written } = await materialiseExtensionMigrationPackageIfMissing(spaceDir, pkg);\n if (written) {\n emitted.push({ spaceId: space.spaceId, dirName: pkg.dirName });\n } else {\n skipped.push({ spaceId: space.spaceId, dirName: pkg.dirName });\n }\n }\n }\n\n return { emitted, skipped };\n}\n","import {\n detectSpaceContractDrift,\n emitContractSpaceArtefacts,\n readContractSpaceHeadRef,\n type SpaceContractDriftResult,\n} from '@prisma-next/migration-tools/spaces';\n\n/**\n * Minimal descriptor view consumed by the migrate-time per-space pass.\n *\n * The CLI receives descriptors typed against the SQL family (or any other\n * family in the future); this helper only needs the structural shape of\n * `contractSpace`, so it accepts an `unknown`-typed `contractJson` and\n * a structurally-typed `headRef`. SQL-family callers pass the same\n * `Contract<SqlStorage>` value through unchanged — `emitContractSpaceArtefacts`\n * already serialises through `canonicalizeJson` and is framework-neutral.\n *\n * @see specs/framework-mechanism.spec.md § 3 — Per-space helper location.\n */\nexport interface MigrateExtensionInput {\n readonly id: string;\n readonly contractSpace?: {\n readonly contractJson: unknown;\n readonly headRef: { readonly hash: string; readonly invariants: readonly string[] };\n };\n}\n\n/**\n * Inputs needed to compose the migrate-time per-space pass at the CLI\n * surface — typically called once after the app-space migration package\n * has been written, regardless of whether the app-space had structural\n * changes (an extension bump alone should still re-pin its artefacts).\n */\nexport interface ContractSpaceMigratePassInputs {\n readonly migrationsDir: string;\n readonly extensionPacks: ReadonlyArray<MigrateExtensionInput>;\n}\n\nexport interface ContractSpaceMigratePassResult {\n readonly drifts: readonly SpaceContractDriftResult[];\n readonly emittedSpaceIds: readonly string[];\n}\n\n/**\n * Run drift detection + on-disk artefact emission for every loaded\n * extension space at `migrate` time.\n *\n * Per sub-spec § 3:\n *\n * - For each declared extension that exposes a `contractSpace`:\n * - Read the on-disk head hash from `migrations/<spaceId>/refs/head.json`\n * (returns `null` on first emit).\n * - Compare against the descriptor's `headRef.hash` via\n * `detectSpaceContractDrift`. The `kind` discriminant decides whether\n * the user sees a warning (`drift`), a no-op silent emit (`firstEmit`,\n * `noDrift`), or nothing at all.\n * - Always re-emit the on-disk artefacts (`contract.json`, `contract.d.ts`,\n * `refs/head.json`). The framework owns these files and the helper is\n * idempotent.\n *\n * Drift warnings are returned to the caller for formatting (TerminalUI,\n * structured-output envelope, etc.) — the helper does not print directly,\n * keeping it framework-neutral and unit-testable.\n *\n * Extension migration packages (the descriptor's pre-canned `migrations`\n * array → `migrations/<spaceId>/<dirName>/`) are intentionally not\n * materialised here — that interaction will be wired in a follow-on round\n * once the runner-side single-tx slice (sub-spec § 6) is in place.\n * On-disk artefacts are sufficient to lock the drift-warning behaviour\n * and the always-on re-emit AC for R2.\n *\n * @see specs/framework-mechanism.spec.md § 3 — Drift detection (T1.9).\n */\nexport async function runContractSpaceMigratePass(\n inputs: ContractSpaceMigratePassInputs,\n): Promise<ContractSpaceMigratePassResult> {\n const drifts: SpaceContractDriftResult[] = [];\n const emittedSpaceIds: string[] = [];\n\n for (const pack of inputs.extensionPacks) {\n if (pack.contractSpace === undefined) continue;\n const { contractJson, headRef } = pack.contractSpace;\n\n const onDiskHeadRef = await readContractSpaceHeadRef(inputs.migrationsDir, pack.id);\n const drift = detectSpaceContractDrift(pack.id, {\n descriptorHash: headRef.hash,\n priorHeadHash: onDiskHeadRef?.hash ?? null,\n });\n drifts.push(drift);\n\n await emitContractSpaceArtefacts(inputs.migrationsDir, pack.id, {\n contract: contractJson,\n contractDts: buildPlaceholderContractDts(pack.id),\n headRef: { hash: headRef.hash, invariants: headRef.invariants },\n });\n emittedSpaceIds.push(pack.id);\n }\n\n return { drifts, emittedSpaceIds };\n}\n\n/**\n * Format the user-facing drift warning for a single space. Callers\n * funnel this through their preferred output channel (TerminalUI line,\n * structured-output envelope `warnings[]`, etc.).\n *\n * Locks AM7 — drift warning surfaces the extension name and the diff\n * direction (descriptor → on-disk head).\n */\nexport function formatContractSpaceDriftWarning(drift: SpaceContractDriftResult): string {\n if (drift.kind !== 'drift') {\n throw new Error(`formatContractSpaceDriftWarning called with non-drift result: ${drift.kind}`);\n }\n return (\n `Contract-space drift detected for \"${drift.spaceId}\": descriptor hash ` +\n `${drift.descriptorHash} differs from on-disk head hash ${drift.priorHeadHash ?? '<none>'}. ` +\n `The on-disk artefacts under migrations/${drift.spaceId}/ will be refreshed to match the descriptor.`\n );\n}\n\n/**\n * Placeholder `.d.ts` content for an extension space's on-disk mirror.\n *\n * Rendering a fully-typed `.d.ts` for an extension contract requires the\n * SQL-family renderer with the codec / typemap registry threaded\n * through; that integration is tracked under sub-spec Open Question 3\n * (see `projects/extension-contract-spaces/specs/framework-mechanism.spec.md`).\n *\n * Until that ships, the on-disk `.d.ts` is a `@ts-nocheck` stub. The\n * spec gap closing alongside the typed renderer is **AC2 / AC14**\n * (byte-equivalence of per-space artefacts under `migrate`):\n * a placeholder cannot be byte-equal to a fully-rendered `.d.ts` from\n * the same descriptor, so AC2 / AC14 are PARTIAL today and become\n * fully-PASS once OQ3 closes.\n *\n * Scheduled to close in **M3** (cipherstash editor tooling) — that's\n * the milestone where the typed renderer gets its first real\n * extension-space consumer and the byte-equivalence guarantee is\n * practically required.\n */\nfunction buildPlaceholderContractDts(spaceId: string): string {\n return [\n '// @ts-nocheck',\n '/**',\n ` * Placeholder \\`.d.ts\\` for extension space \"${spaceId}\".`,\n ' *',\n ' * The framework re-emits this file on every `migrate` run alongside',\n ' * `contract.json` and `refs/head.json`. A typed `.d.ts` rendering',\n \" * pass for extension contracts is tracked under the project's open\",\n ' * questions; until that ships, consumers should import',\n ' * `contract.json` directly with `validateContract<…>(…)`.',\n ' */',\n 'export {};',\n '',\n ].join('\\n');\n}\n","import { readFile } from 'node:fs/promises';\nimport type { Contract } from '@prisma-next/contract/types';\nimport { getEmittedArtifactPaths } from '@prisma-next/emitter';\nimport {\n APP_SPACE_ID,\n createControlStack,\n hasOperationPreview,\n type MigrationPlanOperation,\n type OperationPreview,\n} from '@prisma-next/framework-components/control';\nimport { MigrationToolsError } from '@prisma-next/migration-tools/errors';\nimport { computeMigrationHash } from '@prisma-next/migration-tools/hash';\nimport { deriveProvidedInvariants } from '@prisma-next/migration-tools/invariants';\nimport {\n copyFilesWithRename,\n formatMigrationDirName,\n writeMigrationPackage,\n} from '@prisma-next/migration-tools/io';\nimport type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';\nimport { findLatestMigration } from '@prisma-next/migration-tools/migration-graph';\nimport { writeMigrationTs } from '@prisma-next/migration-tools/migration-ts';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport { join, relative } from 'pathe';\nimport { loadConfig } from '../config-loader';\nimport {\n type CliErrorConflict,\n CliStructuredError,\n errorContractValidationFailed,\n errorFileNotFound,\n errorMigrationPlanningFailed,\n errorRuntime,\n errorTargetMigrationNotSupported,\n errorUnexpected,\n mapMigrationToolsError,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n getTargetMigrations,\n loadMigrationPackages,\n resolveContractPath,\n resolveMigrationPaths,\n setCommandDescriptions,\n setCommandExamples,\n} from '../utils/command-helpers';\nimport { runContractSpaceExtensionMigrationsPass } from '../utils/contract-space-extension-migrations-pass';\nimport {\n formatContractSpaceDriftWarning,\n runContractSpaceMigratePass,\n} from '../utils/contract-space-migrate-pass';\nimport {\n toExtensionInputs,\n toExtensionMigrationsInputs,\n toMigratePassInputs,\n} from '../utils/extension-pack-inputs';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport { assertFrameworkComponentsCompatible } from '../utils/framework-components';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';\nimport { handleResult } from '../utils/result-handler';\nimport { TerminalUI } from '../utils/terminal-ui';\n\ninterface MigrationPlanOptions extends CommonCommandOptions {\n readonly config?: string;\n readonly name?: string;\n readonly from?: string;\n}\n\nexport interface MigrationPlanResult {\n readonly ok: boolean;\n readonly noOp: boolean;\n readonly from: string | null;\n readonly to: string;\n readonly dir?: string;\n /**\n * Extension-space migration packages materialised onto disk during this\n * `plan` run. Each entry names a `migrations/<spaceId>/<dirName>/`\n * tree the framework wrote alongside the app-space migration directory.\n * Empty when the project has no extension packs declaring a contract\n * space, or when every extension-space package is already on disk.\n *\n * Surfacing these in the result (rather than only via `ui.step` log\n * lines) makes the cross-space side effect explicit to JSON consumers\n * and the success-summary renderer — the same multi-space side effect\n * that `migration apply` will replay.\n */\n readonly emittedExtensionDirs: readonly { readonly spaceId: string; readonly dirName: string }[];\n readonly operations: readonly {\n readonly id: string;\n readonly label: string;\n readonly operationClass: string;\n }[];\n /**\n * Family-agnostic textual preview of the migration plan operations.\n * Replaces the previous `sql?: readonly string[]` field; consumers should\n * read `result.preview?.statements`.\n */\n readonly preview?: OperationPreview;\n readonly summary: string;\n /**\n * When true, `migration.ts` was written but contains unfilled\n * `placeholder(...)` calls. The user must edit the file and then run\n * `node migration.ts` to self-emit `ops.json` / `migration.json`.\n */\n readonly pendingPlaceholders?: boolean;\n readonly timings: {\n readonly total: number;\n };\n}\n\nasync function executeMigrationPlanCommand(\n options: MigrationPlanOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n startTime: number,\n): Promise<Result<MigrationPlanResult, CliStructuredError>> {\n const config = await loadConfig(options.config);\n const { configPath, migrationsDir, appMigrationsDir, appMigrationsRelative } =\n resolveMigrationPaths(options.config, config);\n\n const contractPathAbsolute = resolveContractPath(config);\n const contractPath = relative(process.cwd(), contractPathAbsolute);\n\n if (!flags.json && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n { label: 'contract', value: contractPath },\n { label: 'migrations', value: appMigrationsRelative },\n ];\n if (options.from) {\n details.push({ label: 'from', value: options.from });\n }\n if (options.name) {\n details.push({ label: 'name', value: options.name });\n }\n const header = formatStyledHeader({\n command: 'migration plan',\n description: 'Plan a migration from contract changes',\n url: 'https://pris.ly/migration-plan',\n details,\n flags,\n });\n ui.stderr(header);\n }\n\n // Load contract file (the \"to\" contract)\n let contractJsonContent: string;\n try {\n contractJsonContent = await readFile(contractPathAbsolute, 'utf-8');\n } catch (error) {\n if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {\n return notOk(\n errorFileNotFound(contractPathAbsolute, {\n why: `Contract file not found at ${contractPathAbsolute}`,\n fix: `Run \\`prisma-next contract emit\\` to generate ${contractPath}, or update \\`config.contract.output\\` in ${configPath}`,\n }),\n );\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n }\n\n let toContractJson: Contract;\n try {\n toContractJson = JSON.parse(contractJsonContent) as Contract;\n } catch (error) {\n return notOk(\n errorContractValidationFailed(\n `Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`,\n { where: { path: contractPathAbsolute } },\n ),\n );\n }\n\n const rawStorageHash = toContractJson.storage?.storageHash;\n if (typeof rawStorageHash !== 'string') {\n return notOk(\n errorContractValidationFailed('Contract is missing storageHash', {\n where: { path: contractPathAbsolute },\n }),\n );\n }\n const toStorageHash = rawStorageHash;\n\n // Read existing migrations and determine \"from\" contract\n let fromContract: Contract | null = null;\n let fromHash: string | null = null;\n let fromContractSourceDir: string | null = null;\n\n try {\n const { bundles, graph } = await loadMigrationPackages(appMigrationsDir);\n\n if (options.from) {\n const resolved = resolveBundleByPrefix(bundles, options.from);\n if (!resolved.ok) {\n const f = resolved.failure;\n return notOk(\n f.reason === 'ambiguous'\n ? errorRuntime('Multiple matching migrations found', {\n why: `Prefix \"${options.from}\" matches ${f.count} migrations in ${appMigrationsRelative}`,\n fix: 'Provide a longer prefix to disambiguate, or omit --from to use the latest migration target.',\n })\n : errorRuntime('Starting contract not found', {\n why: `No migration with to hash matching \"${options.from}\" exists in ${appMigrationsRelative}`,\n fix: 'Check that the --from hash matches a known migration target hash, or omit --from to use the latest migration target.',\n }),\n );\n }\n fromHash = resolved.value.metadata.to;\n fromContract = resolved.value.metadata.toContract;\n fromContractSourceDir = resolved.value.dirPath;\n } else {\n const latestMigration = findLatestMigration(graph);\n if (latestMigration) {\n fromHash = latestMigration.to;\n const leafPkg = bundles.find(\n (p) => p.metadata.migrationHash === latestMigration.migrationHash,\n );\n if (leafPkg) {\n fromContract = leafPkg.metadata.toContract;\n fromContractSourceDir = leafPkg.dirPath;\n }\n }\n }\n } catch (error) {\n if (MigrationToolsError.is(error)) {\n return notOk(mapMigrationToolsError(error));\n }\n // Wrap unexpected (non-MigrationToolsError) failures from the migration\n // load phase in a structured CLI envelope. Letting them throw would\n // bypass `handleResult()` and crash the command — see CLI structured-\n // errors guideline (CliStructuredError + Result pattern).\n const message = error instanceof Error ? error.message : String(error);\n return notOk(\n errorUnexpected(message, {\n why: `Unexpected error while loading migrations: ${message}`,\n }),\n );\n }\n\n // Per-space migrate pass: drift detection + on-disk artefact emission for\n // every loaded extension that exposes a `contractSpace`. Runs *before*\n // the app-space no-op check so that an extension bump alone (with no\n // structural app-space change) still re-pins extension artefacts on\n // disk. Drift warnings are non-fatal — the on-disk artefacts are refreshed\n // and the user is notified that the bump is being captured.\n // Single descriptor-import boundary: every consumer of `extensionPacks`\n // goes through `toExtensionInputs` + a per-consumer adapter. AC11.\n const canonicalExtensionInputs = toExtensionInputs(config.extensionPacks ?? []);\n const migratePass = await runContractSpaceMigratePass({\n migrationsDir,\n extensionPacks: toMigratePassInputs(canonicalExtensionInputs),\n });\n if (!flags.json && !flags.quiet) {\n for (const drift of migratePass.drifts) {\n if (drift.kind === 'drift') {\n ui.stderr(formatContractSpaceDriftWarning(drift));\n }\n }\n }\n\n // Materialise descriptor-shipped migration packages onto disk under\n // `migrations/<spaceId>/<dirName>/` for any package not yet present.\n // Idempotent (existing dirs are left untouched).\n // Uses `planAllSpaces` for deterministic ordering + duplicate-spaceId\n // detection.\n const extensionMigrationsResult = await runContractSpaceExtensionMigrationsPass({\n migrationsDir,\n extensionPacks: toExtensionMigrationsInputs(canonicalExtensionInputs),\n });\n if (!flags.json && !flags.quiet) {\n for (const entry of extensionMigrationsResult.emitted) {\n ui.step(`Emitted ${entry.spaceId}/${entry.dirName}`);\n }\n }\n\n // Check for no-op (same hash means no changes)\n if (fromHash === toStorageHash) {\n const result: MigrationPlanResult = {\n ok: true,\n noOp: true,\n from: fromHash,\n to: toStorageHash,\n operations: [],\n emittedExtensionDirs: extensionMigrationsResult.emitted,\n summary: 'No changes detected between contracts',\n timings: { total: Date.now() - startTime },\n };\n return ok(result);\n }\n\n // Check target supports migrations\n const migrations = getTargetMigrations(config.target);\n if (!migrations) {\n return notOk(\n errorTargetMigrationNotSupported({\n why: `Target \"${config.target.id}\" does not support migrations`,\n }),\n );\n }\n const frameworkComponents = assertFrameworkComponentsCompatible(\n config.family.familyId,\n config.target.targetId,\n [config.target, config.adapter, ...(config.extensionPacks ?? [])],\n );\n\n // Build manifest and write migration package\n const timestamp = new Date();\n const slug = options.name ?? 'migration';\n const dirName = formatMigrationDirName(timestamp, slug);\n const packageDir = join(appMigrationsDir, dirName);\n\n const baseMetadata: Omit<MigrationMetadata, 'migrationHash' | 'providedInvariants'> = {\n from: fromHash,\n to: toStorageHash,\n fromContract,\n toContract: toContractJson,\n hints: {\n used: [],\n applied: [],\n plannerVersion: '2.0.0',\n },\n labels: [],\n createdAt: timestamp.toISOString(),\n };\n\n try {\n const stack = createControlStack(config);\n const familyInstance = config.family.create(stack);\n const planner = migrations.createPlanner(familyInstance);\n const fromSchema = migrations.contractToSchema(fromContract, frameworkComponents);\n const plannerResult = planner.plan({\n contract: toContractJson,\n schema: fromSchema,\n policy: { allowedOperationClasses: ['additive', 'widening', 'destructive', 'data'] },\n fromContract,\n frameworkComponents,\n spaceId: APP_SPACE_ID,\n });\n if (plannerResult.kind === 'failure') {\n return notOk(\n errorMigrationPlanningFailed({\n conflicts: plannerResult.conflicts as readonly CliErrorConflict[],\n }),\n );\n }\n\n // Accessing .operations triggers toOp() on each call. If any call\n // is a DataTransformCall with an unfilled placeholder stub, toOp()\n // throws PN-MIG-2001. We catch that here so the migration can still\n // be scaffolded with `ops: []`; the user fills the placeholder, then\n // re-runs `node migration.ts` to attest with the real ops.\n let plannedOps: readonly MigrationPlanOperation[] = [];\n let hasPlaceholders = false;\n try {\n plannedOps = plannerResult.plan.operations;\n if (plannedOps.length === 0) {\n return notOk(\n errorMigrationPlanningFailed({\n conflicts: [\n {\n kind: 'unsupportedChange',\n summary:\n 'Contract changed but planner produced no operations. ' +\n 'This indicates unsupported or ignored changes.',\n },\n ],\n }),\n );\n }\n } catch (e) {\n if (CliStructuredError.is(e) && e.domain === 'MIG' && e.code === '2001') {\n hasPlaceholders = true;\n } else {\n throw e;\n }\n }\n\n const migrationTsContent = plannerResult.plan.renderTypeScript();\n\n // Always-attest: compute migrationHash over (metadata, ops). When\n // placeholders blocked lowering, ops is `[]` and the hash is computed\n // over the empty list — re-emitting after the user fills the placeholder\n // produces a different hash (over the real ops). This is intentional;\n // there is no on-disk \"draft\" state.\n const opsForWrite = hasPlaceholders ? [] : plannedOps;\n const metadataWithInvariants: Omit<MigrationMetadata, 'migrationHash'> = {\n ...baseMetadata,\n providedInvariants: deriveProvidedInvariants(opsForWrite),\n };\n const metadata: MigrationMetadata = {\n ...metadataWithInvariants,\n migrationHash: computeMigrationHash(metadataWithInvariants, opsForWrite),\n };\n\n await writeMigrationPackage(packageDir, metadata, opsForWrite);\n const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);\n await copyFilesWithRename(packageDir, [\n { sourcePath: destinationArtifacts.jsonPath, destName: 'end-contract.json' },\n { sourcePath: destinationArtifacts.dtsPath, destName: 'end-contract.d.ts' },\n ]);\n if (fromContractSourceDir !== null) {\n const sourceArtifacts = getEmittedArtifactPaths(\n join(fromContractSourceDir, 'end-contract.json'),\n );\n await copyFilesWithRename(packageDir, [\n { sourcePath: sourceArtifacts.jsonPath, destName: 'start-contract.json' },\n { sourcePath: sourceArtifacts.dtsPath, destName: 'start-contract.d.ts' },\n ]);\n }\n await writeMigrationTs(packageDir, migrationTsContent);\n\n if (hasPlaceholders) {\n const result: MigrationPlanResult = {\n ok: true,\n noOp: false,\n from: fromHash,\n to: toStorageHash,\n dir: relative(process.cwd(), packageDir),\n operations: [],\n emittedExtensionDirs: extensionMigrationsResult.emitted,\n pendingPlaceholders: true,\n summary:\n 'Planned migration with placeholder(s) — edit migration.ts then run `node migration.ts` to self-emit',\n timings: { total: Date.now() - startTime },\n };\n return ok(result);\n }\n\n const preview = hasOperationPreview(familyInstance)\n ? familyInstance.toOperationPreview(plannedOps)\n : undefined;\n const result: MigrationPlanResult = {\n ok: true,\n noOp: false,\n from: fromHash,\n to: toStorageHash,\n dir: relative(process.cwd(), packageDir),\n operations: plannedOps.map((op) => ({\n id: op.id,\n label: op.label,\n operationClass: op.operationClass,\n })),\n emittedExtensionDirs: extensionMigrationsResult.emitted,\n ...(preview !== undefined ? { preview } : {}),\n summary: buildPlanSummary(plannedOps.length, extensionMigrationsResult.emitted.length),\n timings: { total: Date.now() - startTime },\n };\n return ok(result);\n } catch (error) {\n if (CliStructuredError.is(error)) {\n return notOk(error);\n }\n if (MigrationToolsError.is(error)) {\n return notOk(mapMigrationToolsError(error));\n }\n const message = error instanceof Error ? error.message : String(error);\n return notOk(\n errorUnexpected(message, {\n why: `Unexpected error during migration plan: ${message}`,\n }),\n );\n }\n}\n\nexport function createMigrationPlanCommand(): Command {\n const command = new Command('plan');\n setCommandDescriptions(\n command,\n 'Plan a migration from contract changes',\n 'Compares the emitted contract against the latest on-disk migration state and\\n' +\n 'produces a new migration package with the required operations. No database\\n' +\n 'connection is needed — this is a fully offline operation.',\n );\n setCommandExamples(command, [\n 'prisma-next migration plan',\n 'prisma-next migration plan --name add-users-table',\n ]);\n addGlobalOptions(command)\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .option('--name <slug>', 'Name slug for the migration directory', 'migration')\n .option('--from <hash>', 'Explicit starting contract hash (overrides latest migration target)')\n .action(async (options: MigrationPlanOptions) => {\n const flags = parseGlobalFlags(options);\n const startTime = Date.now();\n\n const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });\n const result = await executeMigrationPlanCommand(options, flags, ui, startTime);\n\n const exitCode = handleResult(result, flags, ui, (planResult) => {\n if (flags.json) {\n ui.output(JSON.stringify(planResult, null, 2));\n } else if (!flags.quiet) {\n ui.log(formatMigrationPlanOutput(planResult, flags));\n }\n });\n\n process.exit(exitCode);\n });\n\n return command;\n}\n\n/**\n * Compose the success-line summary so the cross-space side effect\n * (extension-space migration packages materialised on disk during\n * this `plan` run) is visible in the top line — not just in the\n * step log above it.\n *\n * Example outputs:\n * - `Planned 3 operation(s)` (app-space-only project)\n * - `Planned 3 operation(s); materialised 1 extension-space migration` (one extension)\n * - `Planned 3 operation(s); materialised 2 extension-space migrations` (two extensions)\n *\n * Locks AC3 at the summary-line level: a reader of the success line\n * can tell that something happened beyond the app space.\n */\nfunction buildPlanSummary(plannedOpsCount: number, emittedExtensionDirsCount: number): string {\n const base = `Planned ${plannedOpsCount} operation(s)`;\n if (emittedExtensionDirsCount === 0) return base;\n const noun =\n emittedExtensionDirsCount === 1 ? 'extension-space migration' : 'extension-space migrations';\n return `${base}; materialised ${emittedExtensionDirsCount} ${noun}`;\n}\n\nexport function formatMigrationPlanOutput(result: MigrationPlanResult, flags: GlobalFlags): string {\n const lines: string[] = [];\n const useColor = flags.color !== false;\n\n const green_ = useColor ? (s: string) => `\\x1b[32m${s}\\x1b[0m` : (s: string) => s;\n const yellow_ = useColor ? (s: string) => `\\x1b[33m${s}\\x1b[0m` : (s: string) => s;\n const dim_ = useColor ? (s: string) => `\\x1b[2m${s}\\x1b[0m` : (s: string) => s;\n\n // Renders the extension-space materialisation block + canonical apply-step\n // hint shared by the no-op, placeholder, and full-plan branches. The app\n // space short-circuits do not skip it: an extension-only bump emits new\n // `migrations/<spaceId>/<dirName>/` directories on disk that the user\n // still has to apply, so the success line must surface them.\n function appendEmittedExtensions(): void {\n if (result.emittedExtensionDirs.length === 0) return;\n lines.push('');\n lines.push(dim_('Emitted extension migrations:'));\n for (const entry of result.emittedExtensionDirs) {\n lines.push(dim_(` ${entry.spaceId} → migrations/${entry.spaceId}/${entry.dirName}`));\n }\n lines.push('');\n lines.push(\n `Next: review the extension migrations above, then run ${green_('prisma-next migration apply')}.`,\n );\n }\n\n if (result.noOp) {\n lines.push(`${green_('✔')} No changes detected`);\n lines.push(dim_(` from: ${result.from}`));\n lines.push(dim_(` to: ${result.to}`));\n appendEmittedExtensions();\n return lines.join('\\n');\n }\n\n if (result.pendingPlaceholders) {\n lines.push(`${yellow_('⚠')} ${result.summary}`);\n lines.push('');\n lines.push(dim_(`from: ${result.from}`));\n lines.push(dim_(`to: ${result.to}`));\n if (result.dir) {\n lines.push(dim_(`dir: ${result.dir}`));\n }\n lines.push('');\n lines.push(\n 'Open migration.ts and replace each `placeholder(...)` call with your actual query.',\n );\n lines.push(`Then run: ${green_(`node ${result.dir ?? '<dir>'}/migration.ts`)}`);\n appendEmittedExtensions();\n return lines.join('\\n');\n }\n\n lines.push(`${green_('✔')} ${result.summary}`);\n lines.push('');\n\n if (result.operations.length > 0) {\n lines.push(dim_('│'));\n for (let i = 0; i < result.operations.length; i++) {\n const op = result.operations[i]!;\n const isLast = i === result.operations.length - 1;\n const treeChar = isLast ? '└' : '├';\n // operationClass tag is intentionally NOT inlined per spec:\n // a destructive footer warning still surfaces below this list.\n const destructiveMarker =\n op.operationClass === 'destructive' ? ` ${yellow_('(destructive)')}` : '';\n lines.push(`${dim_(treeChar)}─ ${op.label}${destructiveMarker}`);\n }\n\n const hasDestructive = result.operations.some((op) => op.operationClass === 'destructive');\n if (hasDestructive) {\n lines.push('');\n lines.push(\n `${yellow_('⚠')} This migration contains destructive operations that may cause data loss.`,\n );\n }\n lines.push('');\n }\n\n lines.push(dim_(`from: ${result.from}`));\n lines.push(dim_(`to: ${result.to}`));\n if (result.dir) {\n lines.push(dim_(`App space → ${result.dir}`));\n }\n // Per-space block: surface the extension-space directories materialised\n // alongside the app-space migration. Without this block the cross-space\n // side effect is invisible in the success summary (e2e finding F1).\n for (const entry of result.emittedExtensionDirs) {\n lines.push(\n dim_(`Extension space ${entry.spaceId} → migrations/${entry.spaceId}/${entry.dirName}`),\n );\n }\n\n lines.push('');\n // The \"Next:\" hint always points at the canonical apply path\n // (`prisma-next migration apply`) regardless of how many spaces\n // were materialised — `db update` is a dev-time convenience, not\n // the canonical replay step.\n lines.push(\n `Next: review ${green_(result.dir ?? '<dir>')} if needed, then run ${green_('prisma-next migration apply')}.`,\n );\n\n if (result.preview && result.preview.statements.length > 0) {\n // The non-empty length is already guaranteed by the surrounding check, so\n // a plain `every` here is equivalent to the helper in formatters/migrations.ts.\n const allSql = result.preview.statements.every((s) => s.language === 'sql');\n lines.push('');\n lines.push(dim_(allSql ? 'DDL preview' : 'Operation preview'));\n lines.push('');\n for (const statement of result.preview.statements) {\n const trimmed = statement.text.trim();\n if (!trimmed) continue;\n const line = statement.language === 'sql' && !trimmed.endsWith(';') ? `${trimmed};` : trimmed;\n lines.push(line);\n }\n }\n\n if (flags.verbose && result.timings) {\n lines.push('');\n lines.push(dim_(`Total time: ${result.timings.total}ms`));\n }\n\n return lines.join('\\n');\n}\n\nexport type PrefixResolutionFailure =\n | { reason: 'ambiguous'; count: number }\n | { reason: 'not-found' };\n\n/**\n * Resolve a migration package by **target contract hash** (`metadata.to`)\n * using exact match or prefix match.\n *\n * Note: matches `metadata.to` (the contract hash this migration produces),\n * not `metadata.migrationHash` (the package's content-addressed identity).\n * Tries exact match first, then prefix match (auto-prepending `sha256:` when\n * the needle omits the scheme). Returns the matched package on success, or a\n * discriminated failure indicating whether the prefix was ambiguous or simply\n * not found.\n *\n * @internal Exported for testing only.\n */\nexport function resolveBundleByPrefix<T extends { metadata: { to: string } }>(\n bundles: readonly T[],\n needle: string,\n): Result<T, PrefixResolutionFailure> {\n const exact = bundles.find((p) => p.metadata.to === needle);\n if (exact) return ok(exact);\n\n const prefixWithScheme = needle.startsWith('sha256:') ? needle : `sha256:${needle}`;\n const candidates = bundles.filter((p) => p.metadata.to.startsWith(prefixWithScheme));\n\n if (candidates.length === 1) return ok(candidates[0]!);\n if (candidates.length > 1) return notOk({ reason: 'ambiguous', count: candidates.length });\n return notOk({ reason: 'not-found' });\n}\n"],"mappings":"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"}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import { t as loadConfig } from "./config-loader-
|
|
2
|
-
import { _ as errorUnexpected, m as errorRuntime } from "./cli-errors-
|
|
3
|
-
import { t as
|
|
4
|
-
import { t as TerminalUI } from "./terminal-ui-
|
|
5
|
-
import {
|
|
1
|
+
import { t as loadConfig } from "./config-loader-B6sJjXTv.mjs";
|
|
2
|
+
import { _ as errorUnexpected, m as errorRuntime, v as mapMigrationToolsError } from "./cli-errors-D3_sMh2K.mjs";
|
|
3
|
+
import { a as loadMigrationPackages, d as setCommandDescriptions, f as setCommandExamples, g as parseGlobalFlags, h as toStructuralEdge, l as resolveMigrationPaths, m as toPathDecisionResult, n as collectDeclaredInvariants, o as maskConnectionUrl, s as readContractEnvelope, t as addGlobalOptions, y as formatStyledHeader } from "./command-helpers-BeZHkxV8.mjs";
|
|
4
|
+
import { t as TerminalUI } from "./terminal-ui-C_hFNbAn.mjs";
|
|
5
|
+
import { t as handleResult } from "./result-handler-rmPVKIP2.mjs";
|
|
6
|
+
import { a as buildContractSpaceAggregate, t as createControlClient } from "./client-qVH-rEgd.mjs";
|
|
6
7
|
import { Command } from "commander";
|
|
7
|
-
import { notOk, ok } from "@prisma-next/utils/result";
|
|
8
8
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { notOk, ok } from "@prisma-next/utils/result";
|
|
10
|
+
import { createControlStack } from "@prisma-next/framework-components/control";
|
|
11
|
+
import { findPath, findPathWithDecision, findReachableLeaves } from "@prisma-next/migration-tools/migration-graph";
|
|
11
12
|
import { bold, cyan, dim, magenta, yellow } from "colorette";
|
|
13
|
+
import { graphWalkStrategy } from "@prisma-next/migration-tools/aggregate";
|
|
14
|
+
import { EMPTY_CONTRACT_HASH } from "@prisma-next/migration-tools/constants";
|
|
15
|
+
import { MigrationToolsError, errorNoInvariantPath, errorUnknownInvariant } from "@prisma-next/migration-tools/errors";
|
|
12
16
|
import { readRefs, resolveRef } from "@prisma-next/migration-tools/refs";
|
|
13
|
-
import { MigrationToolsError } from "@prisma-next/migration-tools/types";
|
|
14
17
|
import dagre from "@dagrejs/dagre";
|
|
15
|
-
|
|
16
18
|
//#region src/utils/formatters/graph-types.ts
|
|
17
19
|
/**
|
|
18
20
|
* Immutable directed graph with adjacency-list indexing.
|
|
@@ -52,7 +54,6 @@ var RenderGraph = class {
|
|
|
52
54
|
return this.forward.get(nodeId) ?? [];
|
|
53
55
|
}
|
|
54
56
|
};
|
|
55
|
-
|
|
56
57
|
//#endregion
|
|
57
58
|
//#region src/utils/formatters/graph-migration-mapper.ts
|
|
58
59
|
/**
|
|
@@ -100,7 +101,8 @@ function migrationGraphToRenderInput(input) {
|
|
|
100
101
|
for (const [, entries] of graph.forwardChain) for (const entry of entries) {
|
|
101
102
|
const status = statusByDirName.get(entry.dirName);
|
|
102
103
|
const icon = status ? STATUS_ICON[status] : "";
|
|
103
|
-
const
|
|
104
|
+
const invariantsSuffix = entry.invariants.length > 0 ? ` provides [${entry.invariants.map((id) => JSON.stringify(id)).join(", ")}]` : "";
|
|
105
|
+
const label = `${entry.dirName}${icon}${invariantsSuffix}`;
|
|
104
106
|
edgeList.push({
|
|
105
107
|
from: toShortId(entry.from),
|
|
106
108
|
to: toShortId(entry.to),
|
|
@@ -171,7 +173,6 @@ function migrationGraphToRenderInput(input) {
|
|
|
171
173
|
relevantPaths
|
|
172
174
|
};
|
|
173
175
|
}
|
|
174
|
-
|
|
175
176
|
//#endregion
|
|
176
177
|
//#region src/utils/formatters/graph-render.ts
|
|
177
178
|
/**
|
|
@@ -1086,9 +1087,24 @@ function isLinearGraph(graph) {
|
|
|
1086
1087
|
for (const node of graph.nodes) if (graph.outgoing(node.id).filter((e) => e.style !== "dashed").length > 1) return false;
|
|
1087
1088
|
return true;
|
|
1088
1089
|
}
|
|
1089
|
-
|
|
1090
1090
|
//#endregion
|
|
1091
1091
|
//#region src/commands/migration-status.ts
|
|
1092
|
+
/**
|
|
1093
|
+
* Sum per-space `pendingCount` into a cross-space total, but only when
|
|
1094
|
+
* every loaded space reports a defined `pendingCount`. Returns
|
|
1095
|
+
* `undefined` if any space is on the marker-unknown / offline path
|
|
1096
|
+
* (where `pendingCount` is intentionally absent), so JSON consumers can
|
|
1097
|
+
* distinguish "no pending" from "unknown".
|
|
1098
|
+
*/
|
|
1099
|
+
function computeTotalPendingAcrossSpaces(spaces) {
|
|
1100
|
+
if (spaces.length === 0) return void 0;
|
|
1101
|
+
let total = 0;
|
|
1102
|
+
for (const s of spaces) {
|
|
1103
|
+
if (s.pendingCount === void 0) return void 0;
|
|
1104
|
+
total += s.pendingCount;
|
|
1105
|
+
}
|
|
1106
|
+
return total;
|
|
1107
|
+
}
|
|
1092
1108
|
function summarizeOps(ops) {
|
|
1093
1109
|
if (ops.length === 0) return {
|
|
1094
1110
|
summary: "0 ops",
|
|
@@ -1194,7 +1210,7 @@ function buildMigrationEntries(chain, packages, mode, markerHash, edgeStatuses)
|
|
|
1194
1210
|
dirName: migration.dirName,
|
|
1195
1211
|
from: migration.from,
|
|
1196
1212
|
to: migration.to,
|
|
1197
|
-
|
|
1213
|
+
migrationHash: migration.migrationHash,
|
|
1198
1214
|
operationCount: ops.length,
|
|
1199
1215
|
operationSummary: summary,
|
|
1200
1216
|
hasDestructive,
|
|
@@ -1239,37 +1255,117 @@ function determineLimit(opts) {
|
|
|
1239
1255
|
if (Number.isNaN(parsed)) return DEFAULT_LIMIT;
|
|
1240
1256
|
return parsed;
|
|
1241
1257
|
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Build the aggregate enumeration of contract spaces for the status
|
|
1260
|
+
* output. Loads the aggregate from disk (lossy on failure — extension
|
|
1261
|
+
* spaces are simply omitted, the existing single-space app behaviour
|
|
1262
|
+
* keeps working), reads per-space marker rows when online, and uses
|
|
1263
|
+
* {@link graphWalkStrategy} to compute each space's pending count.
|
|
1264
|
+
*
|
|
1265
|
+
* Sub-spec § `migration status` semantics — the aggregate-walking
|
|
1266
|
+
* version reports per-space marker + pending state alongside the
|
|
1267
|
+
* cross-space totals.
|
|
1268
|
+
*/
|
|
1269
|
+
async function loadAggregateStatusSpaces(args) {
|
|
1270
|
+
const loaded = await buildContractSpaceAggregate({
|
|
1271
|
+
targetId: args.targetId,
|
|
1272
|
+
migrationsDir: args.migrationsDir,
|
|
1273
|
+
appContract: args.validateContract(args.appContractRaw),
|
|
1274
|
+
extensionPacks: args.extensionPacks,
|
|
1275
|
+
validateContract: args.validateContract
|
|
1276
|
+
});
|
|
1277
|
+
if (!loaded.ok) return [];
|
|
1278
|
+
const aggregate = loaded.value;
|
|
1279
|
+
const orderedMembers = [...aggregate.extensions, aggregate.app];
|
|
1280
|
+
const rows = [];
|
|
1281
|
+
for (const member of orderedMembers) {
|
|
1282
|
+
const liveMarker = args.markersBySpace?.get(member.spaceId) ?? null;
|
|
1283
|
+
const isApp = member.spaceId === aggregate.app.spaceId;
|
|
1284
|
+
if (member.migrations.graph.nodes.size === 0) {
|
|
1285
|
+
rows.push({
|
|
1286
|
+
spaceId: member.spaceId,
|
|
1287
|
+
kind: isApp ? "app" : "extension",
|
|
1288
|
+
headHash: member.headRef.hash,
|
|
1289
|
+
...args.markersBySpace !== null ? {
|
|
1290
|
+
markerHash: liveMarker?.storageHash ?? null,
|
|
1291
|
+
status: member.headRef.hash === EMPTY_CONTRACT_HASH ? "up-to-date" : "never-planned",
|
|
1292
|
+
pendingCount: 0
|
|
1293
|
+
} : {}
|
|
1294
|
+
});
|
|
1295
|
+
continue;
|
|
1296
|
+
}
|
|
1297
|
+
if (args.markersBySpace === null) {
|
|
1298
|
+
rows.push({
|
|
1299
|
+
spaceId: member.spaceId,
|
|
1300
|
+
kind: isApp ? "app" : "extension",
|
|
1301
|
+
headHash: member.headRef.hash
|
|
1302
|
+
});
|
|
1303
|
+
continue;
|
|
1304
|
+
}
|
|
1305
|
+
const walked = graphWalkStrategy({
|
|
1306
|
+
aggregateTargetId: aggregate.targetId,
|
|
1307
|
+
member,
|
|
1308
|
+
currentMarker: liveMarker
|
|
1309
|
+
});
|
|
1310
|
+
let pendingCount = 0;
|
|
1311
|
+
let status;
|
|
1312
|
+
if (walked.kind === "ok") {
|
|
1313
|
+
pendingCount = walked.result.plan.operations.length;
|
|
1314
|
+
if (liveMarker === null) status = pendingCount === 0 ? "no-marker" : "pending";
|
|
1315
|
+
else status = pendingCount === 0 ? "up-to-date" : "pending";
|
|
1316
|
+
} else status = "unreachable";
|
|
1317
|
+
rows.push({
|
|
1318
|
+
spaceId: member.spaceId,
|
|
1319
|
+
kind: isApp ? "app" : "extension",
|
|
1320
|
+
headHash: member.headRef.hash,
|
|
1321
|
+
markerHash: liveMarker?.storageHash ?? null,
|
|
1322
|
+
pendingCount,
|
|
1323
|
+
...status ? { status } : {}
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
return rows;
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Read the raw contract.json bytes from disk for the aggregate
|
|
1330
|
+
* loader. Returns `null` if the file is missing or unparseable —
|
|
1331
|
+
* the existing `readContractEnvelope` path will report the same
|
|
1332
|
+
* problem via a status diagnostic, no need to double-surface.
|
|
1333
|
+
*/
|
|
1334
|
+
async function loadContractRawSafely(config) {
|
|
1335
|
+
try {
|
|
1336
|
+
const path = (await import("./command-helpers-BeZHkxV8.mjs").then((n) => n.r)).resolveContractPath(config);
|
|
1337
|
+
const raw = await (await import("node:fs/promises")).readFile(path, "utf-8");
|
|
1338
|
+
return JSON.parse(raw);
|
|
1339
|
+
} catch {
|
|
1340
|
+
return null;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1242
1343
|
async function executeMigrationStatusCommand(options, flags, ui) {
|
|
1243
1344
|
const config = await loadConfig(options.config);
|
|
1244
|
-
const { configPath,
|
|
1345
|
+
const { configPath, appMigrationsDir, appMigrationsRelative, migrationsDir, refsDir } = resolveMigrationPaths(options.config, config);
|
|
1245
1346
|
const dbConnection = options.db ?? config.db?.connection;
|
|
1246
1347
|
const hasDriver = !!config.driver;
|
|
1247
1348
|
let activeRefName;
|
|
1248
1349
|
let activeRefHash;
|
|
1350
|
+
let activeRefEntry;
|
|
1249
1351
|
let allRefs = {};
|
|
1250
1352
|
try {
|
|
1251
1353
|
allRefs = await readRefs(refsDir);
|
|
1252
1354
|
} catch (error) {
|
|
1253
|
-
if (MigrationToolsError.is(error)) return notOk(
|
|
1254
|
-
why: error.why,
|
|
1255
|
-
fix: error.fix,
|
|
1256
|
-
meta: { code: error.code }
|
|
1257
|
-
}));
|
|
1355
|
+
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
1258
1356
|
throw error;
|
|
1259
1357
|
}
|
|
1260
1358
|
if (options.ref) {
|
|
1261
1359
|
activeRefName = options.ref;
|
|
1262
1360
|
try {
|
|
1263
|
-
|
|
1361
|
+
activeRefEntry = resolveRef(allRefs, activeRefName);
|
|
1362
|
+
activeRefHash = activeRefEntry.hash;
|
|
1264
1363
|
} catch (error) {
|
|
1265
|
-
if (MigrationToolsError.is(error)) return notOk(
|
|
1266
|
-
why: error.why,
|
|
1267
|
-
fix: error.fix,
|
|
1268
|
-
meta: { code: error.code }
|
|
1269
|
-
}));
|
|
1364
|
+
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
1270
1365
|
throw error;
|
|
1271
1366
|
}
|
|
1272
1367
|
}
|
|
1368
|
+
const requiredInvariants = [...activeRefEntry?.invariants ?? []].sort();
|
|
1273
1369
|
const statusRefs = Object.entries(allRefs).map(([name, entry]) => ({
|
|
1274
1370
|
name,
|
|
1275
1371
|
hash: entry.hash,
|
|
@@ -1281,7 +1377,7 @@ async function executeMigrationStatusCommand(options, flags, ui) {
|
|
|
1281
1377
|
value: configPath
|
|
1282
1378
|
}, {
|
|
1283
1379
|
label: "migrations",
|
|
1284
|
-
value:
|
|
1380
|
+
value: appMigrationsRelative
|
|
1285
1381
|
}];
|
|
1286
1382
|
if (dbConnection && hasDriver) details.push({
|
|
1287
1383
|
label: "database",
|
|
@@ -1291,6 +1387,10 @@ async function executeMigrationStatusCommand(options, flags, ui) {
|
|
|
1291
1387
|
label: "ref",
|
|
1292
1388
|
value: activeRefName
|
|
1293
1389
|
});
|
|
1390
|
+
if (activeRefEntry && activeRefEntry.invariants.length > 0) details.push({
|
|
1391
|
+
label: "required",
|
|
1392
|
+
value: formatInvariantList(activeRefEntry.invariants)
|
|
1393
|
+
});
|
|
1294
1394
|
const header = formatStyledHeader({
|
|
1295
1395
|
command: "migration status",
|
|
1296
1396
|
description: "Show migration history and applied status",
|
|
@@ -1314,13 +1414,9 @@ async function executeMigrationStatusCommand(options, flags, ui) {
|
|
|
1314
1414
|
let bundles;
|
|
1315
1415
|
let graph;
|
|
1316
1416
|
try {
|
|
1317
|
-
({bundles, graph} = await
|
|
1417
|
+
({bundles, graph} = await loadMigrationPackages(appMigrationsDir));
|
|
1318
1418
|
} catch (error) {
|
|
1319
|
-
if (MigrationToolsError.is(error)) return notOk(
|
|
1320
|
-
why: error.why,
|
|
1321
|
-
fix: error.fix,
|
|
1322
|
-
meta: { code: error.code }
|
|
1323
|
-
}));
|
|
1419
|
+
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
1324
1420
|
return notOk(errorUnexpected(error instanceof Error ? error.message : String(error), { why: `Failed to read migrations directory: ${error instanceof Error ? error.message : String(error)}` }));
|
|
1325
1421
|
}
|
|
1326
1422
|
if (bundles.length === 0) {
|
|
@@ -1337,7 +1433,8 @@ async function executeMigrationStatusCommand(options, flags, ui) {
|
|
|
1337
1433
|
targetHash: EMPTY_CONTRACT_HASH,
|
|
1338
1434
|
contractHash,
|
|
1339
1435
|
summary: "No migrations found",
|
|
1340
|
-
diagnostics
|
|
1436
|
+
diagnostics,
|
|
1437
|
+
requiredInvariants
|
|
1341
1438
|
});
|
|
1342
1439
|
}
|
|
1343
1440
|
let targetHash;
|
|
@@ -1354,7 +1451,9 @@ async function executeMigrationStatusCommand(options, flags, ui) {
|
|
|
1354
1451
|
});
|
|
1355
1452
|
}
|
|
1356
1453
|
let markerHash;
|
|
1454
|
+
let markerInvariants = [];
|
|
1357
1455
|
let mode = "offline";
|
|
1456
|
+
let allMarkers = null;
|
|
1358
1457
|
if (dbConnection && hasDriver) {
|
|
1359
1458
|
const client = createControlClient({
|
|
1360
1459
|
family: config.family,
|
|
@@ -1365,14 +1464,51 @@ async function executeMigrationStatusCommand(options, flags, ui) {
|
|
|
1365
1464
|
});
|
|
1366
1465
|
try {
|
|
1367
1466
|
await client.connect(dbConnection);
|
|
1368
|
-
|
|
1467
|
+
const marker = await client.readMarker();
|
|
1468
|
+
markerHash = marker?.storageHash;
|
|
1469
|
+
markerInvariants = marker?.invariants ?? [];
|
|
1369
1470
|
mode = "online";
|
|
1471
|
+
try {
|
|
1472
|
+
allMarkers = await client.readAllMarkers();
|
|
1473
|
+
} catch {
|
|
1474
|
+
allMarkers = null;
|
|
1475
|
+
}
|
|
1370
1476
|
} catch {
|
|
1371
1477
|
if (!flags.json && !flags.quiet) ui.warn("Could not connect to database — showing offline status");
|
|
1372
1478
|
} finally {
|
|
1373
1479
|
await client.close();
|
|
1374
1480
|
}
|
|
1375
1481
|
}
|
|
1482
|
+
const contractRawForAggregate = await loadContractRawSafely(config);
|
|
1483
|
+
let aggregateSpaces = [];
|
|
1484
|
+
if (contractRawForAggregate !== null) {
|
|
1485
|
+
const stack = createControlStack(config);
|
|
1486
|
+
const familyInstance = config.family.create(stack);
|
|
1487
|
+
try {
|
|
1488
|
+
aggregateSpaces = await loadAggregateStatusSpaces({
|
|
1489
|
+
targetId: config.target.targetId,
|
|
1490
|
+
migrationsDir,
|
|
1491
|
+
appContractRaw: contractRawForAggregate,
|
|
1492
|
+
extensionPacks: config.extensionPacks ?? [],
|
|
1493
|
+
validateContract: (json) => familyInstance.validateContract(json),
|
|
1494
|
+
markersBySpace: allMarkers
|
|
1495
|
+
});
|
|
1496
|
+
} catch {
|
|
1497
|
+
aggregateSpaces = [];
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
const totalPendingAcrossSpaces = computeTotalPendingAcrossSpaces(aggregateSpaces);
|
|
1501
|
+
if (activeRefEntry && activeRefEntry.invariants.length > 0) {
|
|
1502
|
+
const declared = collectDeclaredInvariants(graph);
|
|
1503
|
+
const known = new Set(declared);
|
|
1504
|
+
if (mode === "online") for (const id of markerInvariants) known.add(id);
|
|
1505
|
+
const unknown = activeRefEntry.invariants.filter((id) => !known.has(id));
|
|
1506
|
+
if (unknown.length > 0) return notOk(mapMigrationToolsError(errorUnknownInvariant({
|
|
1507
|
+
...ifDefined("refName", activeRefName),
|
|
1508
|
+
unknown,
|
|
1509
|
+
declared: [...declared].sort()
|
|
1510
|
+
})));
|
|
1511
|
+
}
|
|
1376
1512
|
if (mode === "online" && markerHash !== void 0 && !graph.nodes.has(markerHash) && markerHash !== contractHash) {
|
|
1377
1513
|
const hints = [];
|
|
1378
1514
|
if (graph.nodes.has(contractHash)) hints.push("Run 'prisma-next db sign' to overwrite the marker if the database already matches the contract", "Run 'prisma-next db update' to push the current contract to the database", "Run 'prisma-next contract infer' to make your contract match the database", "Run 'prisma-next db verify' to inspect the database state");
|
|
@@ -1392,6 +1528,7 @@ async function executeMigrationStatusCommand(options, flags, ui) {
|
|
|
1392
1528
|
summary: `${bundles.length} migration(s) on disk`,
|
|
1393
1529
|
diagnostics,
|
|
1394
1530
|
markerHash,
|
|
1531
|
+
requiredInvariants,
|
|
1395
1532
|
...statusRefs.length > 0 ? { refs: statusRefs } : {}
|
|
1396
1533
|
});
|
|
1397
1534
|
}
|
|
@@ -1416,6 +1553,7 @@ async function executeMigrationStatusCommand(options, flags, ui) {
|
|
|
1416
1553
|
summary: `${bundles.length} migration(s) on disk`,
|
|
1417
1554
|
diagnostics,
|
|
1418
1555
|
...ifDefined("markerHash", markerHash),
|
|
1556
|
+
requiredInvariants,
|
|
1419
1557
|
...statusRefs.length > 0 ? { refs: statusRefs } : {},
|
|
1420
1558
|
graph,
|
|
1421
1559
|
bundles,
|
|
@@ -1430,35 +1568,68 @@ async function executeMigrationStatusCommand(options, flags, ui) {
|
|
|
1430
1568
|
const entries = buildMigrationEntries(chain, bundles, mode, markerHash, edgeStatuses);
|
|
1431
1569
|
const pendingCount = edgeStatuses.filter((e) => e.status === "pending").length;
|
|
1432
1570
|
const appliedCount = edgeStatuses.filter((e) => e.status === "applied").length;
|
|
1571
|
+
let appliedInvariants;
|
|
1572
|
+
let missingInvariants;
|
|
1573
|
+
let effectiveRequired = /* @__PURE__ */ new Set();
|
|
1574
|
+
if (mode === "online") {
|
|
1575
|
+
const markerSet = new Set(markerInvariants);
|
|
1576
|
+
effectiveRequired = new Set(requiredInvariants.filter((id) => !markerSet.has(id)));
|
|
1577
|
+
appliedInvariants = requiredInvariants.filter((id) => markerSet.has(id));
|
|
1578
|
+
missingInvariants = [...effectiveRequired].sort();
|
|
1579
|
+
}
|
|
1580
|
+
const hasInvariantWork = effectiveRequired.size > 0;
|
|
1581
|
+
const missingList = [...effectiveRequired].sort().join(", ");
|
|
1433
1582
|
let summary;
|
|
1434
1583
|
if (mode === "online") if (markerHash !== void 0 && !graph.nodes.has(markerHash) && markerHash === contractHash) summary = `${bundles.length} migration(s) on disk`;
|
|
1435
|
-
else if (activeRefHash && markerHash !== void 0)
|
|
1436
|
-
|
|
1584
|
+
else if (activeRefHash && markerHash !== void 0) {
|
|
1585
|
+
const distance = summarizeRefDistance(graph, markerHash, activeRefHash, activeRefName);
|
|
1586
|
+
summary = hasInvariantWork ? `${distance} — missing invariant(s): ${missingList}` : distance;
|
|
1587
|
+
} else if (pendingCount === 0 && !hasInvariantWork) summary = `Database is up to date (${appliedCount} migration${appliedCount !== 1 ? "s" : ""} applied)`;
|
|
1588
|
+
else if (pendingCount === 0 && hasInvariantWork) summary = `Missing invariant(s): ${missingList} — run 'prisma-next migration apply --ref ${activeRefName ?? "<ref>"}' to apply`;
|
|
1437
1589
|
else if (markerHash === void 0) summary = `${pendingCount} pending migration(s) — database has no marker`;
|
|
1438
1590
|
else summary = `${pendingCount} pending migration(s) — run 'prisma-next migration apply' to apply`;
|
|
1439
1591
|
else summary = `${entries.length} migration(s) on disk`;
|
|
1440
|
-
if (mode === "online") if (markerHash !== void 0 && !graph.nodes.has(markerHash) && markerHash === contractHash) diagnostics.push({
|
|
1441
|
-
code: "MIGRATION.MARKER_NOT_IN_HISTORY",
|
|
1442
|
-
severity: "warn",
|
|
1443
|
-
message: "Database matches the current contract but was updated directly (not via migration apply)",
|
|
1444
|
-
hints: ["Run 'prisma-next migration plan' to plan a migration to your current contract"]
|
|
1445
|
-
});
|
|
1446
|
-
else if (pendingCount > 0) diagnostics.push({
|
|
1447
|
-
code: "MIGRATION.DATABASE_BEHIND",
|
|
1448
|
-
severity: "info",
|
|
1449
|
-
message: `${pendingCount} migration(s) pending`,
|
|
1450
|
-
hints: ["Run 'prisma-next migration apply' to apply pending migrations"]
|
|
1451
|
-
});
|
|
1452
|
-
else diagnostics.push({
|
|
1453
|
-
code: "MIGRATION.UP_TO_DATE",
|
|
1454
|
-
severity: "info",
|
|
1455
|
-
message: "Database is up to date",
|
|
1456
|
-
hints: []
|
|
1457
|
-
});
|
|
1458
1592
|
let pathDecision;
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1593
|
+
let routingUnreachable = false;
|
|
1594
|
+
if (mode === "online") {
|
|
1595
|
+
const outcome = findPathWithDecision(graph, markerHash ?? EMPTY_CONTRACT_HASH, targetHash, {
|
|
1596
|
+
...ifDefined("refName", activeRefName),
|
|
1597
|
+
required: effectiveRequired
|
|
1598
|
+
});
|
|
1599
|
+
if (outcome.kind === "ok") pathDecision = toPathDecisionResult(outcome.decision);
|
|
1600
|
+
else if (outcome.kind === "unsatisfiable") return notOk(mapMigrationToolsError(errorNoInvariantPath({
|
|
1601
|
+
...ifDefined("refName", activeRefName),
|
|
1602
|
+
required: [...effectiveRequired].sort(),
|
|
1603
|
+
missing: outcome.missing,
|
|
1604
|
+
structuralPath: outcome.structuralPath.map(toStructuralEdge)
|
|
1605
|
+
})));
|
|
1606
|
+
else routingUnreachable = true;
|
|
1607
|
+
}
|
|
1608
|
+
if (mode === "online") {
|
|
1609
|
+
if (markerHash !== void 0 && !graph.nodes.has(markerHash) && markerHash === contractHash) diagnostics.push({
|
|
1610
|
+
code: "MIGRATION.MARKER_NOT_IN_HISTORY",
|
|
1611
|
+
severity: "warn",
|
|
1612
|
+
message: "Database matches the current contract but was updated directly (not via migration apply)",
|
|
1613
|
+
hints: ["Run 'prisma-next migration plan' to plan a migration to your current contract"]
|
|
1614
|
+
});
|
|
1615
|
+
else if (pendingCount > 0) diagnostics.push({
|
|
1616
|
+
code: "MIGRATION.DATABASE_BEHIND",
|
|
1617
|
+
severity: "info",
|
|
1618
|
+
message: `${pendingCount} migration(s) pending`,
|
|
1619
|
+
hints: ["Run 'prisma-next migration apply' to apply pending migrations"]
|
|
1620
|
+
});
|
|
1621
|
+
else if (hasInvariantWork) diagnostics.push({
|
|
1622
|
+
code: "MIGRATION.INVARIANTS_PENDING",
|
|
1623
|
+
severity: "info",
|
|
1624
|
+
message: `Missing required invariant(s): ${missingList}`,
|
|
1625
|
+
hints: [`Run 'prisma-next migration apply --ref ${activeRefName ?? "<ref>"}' to apply a path that covers the required invariants`]
|
|
1626
|
+
});
|
|
1627
|
+
else if (!routingUnreachable) diagnostics.push({
|
|
1628
|
+
code: "MIGRATION.UP_TO_DATE",
|
|
1629
|
+
severity: "info",
|
|
1630
|
+
message: "Database is up to date",
|
|
1631
|
+
hints: []
|
|
1632
|
+
});
|
|
1462
1633
|
}
|
|
1463
1634
|
return ok({
|
|
1464
1635
|
ok: true,
|
|
@@ -1469,13 +1640,18 @@ async function executeMigrationStatusCommand(options, flags, ui) {
|
|
|
1469
1640
|
summary,
|
|
1470
1641
|
diagnostics,
|
|
1471
1642
|
...ifDefined("markerHash", markerHash),
|
|
1643
|
+
requiredInvariants,
|
|
1644
|
+
...ifDefined("appliedInvariants", appliedInvariants),
|
|
1645
|
+
...ifDefined("missingInvariants", missingInvariants),
|
|
1472
1646
|
...statusRefs.length > 0 ? { refs: statusRefs } : {},
|
|
1473
1647
|
...ifDefined("pathDecision", pathDecision),
|
|
1474
1648
|
graph,
|
|
1475
1649
|
bundles,
|
|
1476
1650
|
edgeStatuses,
|
|
1477
1651
|
...ifDefined("activeRefHash", activeRefHash),
|
|
1478
|
-
...ifDefined("activeRefName", activeRefName)
|
|
1652
|
+
...ifDefined("activeRefName", activeRefName),
|
|
1653
|
+
spaces: aggregateSpaces,
|
|
1654
|
+
...ifDefined("totalPendingAcrossSpaces", totalPendingAcrossSpaces)
|
|
1479
1655
|
});
|
|
1480
1656
|
}
|
|
1481
1657
|
function createMigrationStatusCommand() {
|
|
@@ -1490,7 +1666,7 @@ function createMigrationStatusCommand() {
|
|
|
1490
1666
|
});
|
|
1491
1667
|
const exitCode = handleResult(await executeMigrationStatusCommand(options, flags, ui), flags, ui, (statusResult) => {
|
|
1492
1668
|
if (flags.json) {
|
|
1493
|
-
const { graph:
|
|
1669
|
+
const { graph: _graph, bundles: _bundles, edgeStatuses: _edgeStatuses, activeRefHash: _activeRefHash, activeRefName: _activeRefName, diverged: _diverged, ...jsonResult } = statusResult;
|
|
1494
1670
|
ui.output(JSON.stringify(jsonResult, null, 2));
|
|
1495
1671
|
} else if (!flags.quiet) {
|
|
1496
1672
|
const colorize = flags.color !== false;
|
|
@@ -1540,17 +1716,48 @@ function formatStatusSummary(result, colorize) {
|
|
|
1540
1716
|
const hasUnknown = result.migrations.some((e) => e.status === "unknown");
|
|
1541
1717
|
const pendingCount = result.migrations.filter((e) => e.status === "pending").length;
|
|
1542
1718
|
const hasWarnings = result.diagnostics?.some((d) => d.severity === "warn") ?? false;
|
|
1719
|
+
const hasInvariantPending = result.diagnostics?.some((d) => d.code === "MIGRATION.INVARIANTS_PENDING") ?? false;
|
|
1543
1720
|
if (result.mode === "online") if (hasUnknown || hasWarnings) lines.push(`${c(yellow, "⚠")} ${result.summary}`);
|
|
1544
|
-
else if (pendingCount === 0) lines.push(`${c(cyan, "✔")} ${result.summary}`);
|
|
1721
|
+
else if (pendingCount === 0 && !hasInvariantPending) lines.push(`${c(cyan, "✔")} ${result.summary}`);
|
|
1545
1722
|
else lines.push(`${c(yellow, "⧗")} ${result.summary}`);
|
|
1546
1723
|
else lines.push(result.summary);
|
|
1724
|
+
if (result.requiredInvariants.length > 0) if (result.appliedInvariants !== void 0 && result.missingInvariants !== void 0) {
|
|
1725
|
+
lines.push(`${c(dim, "applied ")}${formatInvariantList(result.appliedInvariants)}`);
|
|
1726
|
+
lines.push(`${c(dim, "missing ")}${formatInvariantList(result.missingInvariants)}`);
|
|
1727
|
+
} else lines.push(`${c(dim, "applied ")}(unknown — connect a database to evaluate)`);
|
|
1547
1728
|
const warnings = result.diagnostics?.filter((d) => d.severity === "warn") ?? [];
|
|
1548
1729
|
for (const diag of warnings) {
|
|
1549
1730
|
lines.push(`${c(yellow, "⚠")} ${diag.message}`);
|
|
1550
1731
|
for (const hint of diag.hints) lines.push(` ${c(dim, hint)}`);
|
|
1551
1732
|
}
|
|
1733
|
+
if (result.spaces?.some((s) => s.kind === "extension")) {
|
|
1734
|
+
const total = result.totalPendingAcrossSpaces ?? 0;
|
|
1735
|
+
lines.push("");
|
|
1736
|
+
lines.push(c(dim, "spaces"));
|
|
1737
|
+
for (const space of result.spaces) lines.push(formatSpaceLine(space, c));
|
|
1738
|
+
if (total > 0) {
|
|
1739
|
+
lines.push("");
|
|
1740
|
+
lines.push(`${c(yellow, "⧗")} ${total} pending migration(s) across ${result.spaces.length} space(s) — run 'prisma-next migration apply' to apply`);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1552
1743
|
return lines.join("\n");
|
|
1553
1744
|
}
|
|
1745
|
+
function formatSpaceLine(space, c) {
|
|
1746
|
+
const glyph = (() => {
|
|
1747
|
+
if (space.status === "up-to-date" || space.status === "no-marker") return c(cyan, "✓");
|
|
1748
|
+
if (space.status === "pending") return c(yellow, "⧗");
|
|
1749
|
+
if (space.status === "unreachable" || space.status === "never-planned") return c(magenta, "✗");
|
|
1750
|
+
return " ";
|
|
1751
|
+
})();
|
|
1752
|
+
const tag = space.kind === "app" ? "[app]" : "[ext]";
|
|
1753
|
+
const head = space.headHash.slice(0, 8);
|
|
1754
|
+
const marker = space.markerHash === void 0 ? "(unknown)" : space.markerHash === null ? "(no marker)" : space.markerHash.slice(0, 8);
|
|
1755
|
+
const pending = space.pendingCount === void 0 ? "" : space.pendingCount === 0 ? c(dim, " (up to date)") : c(yellow, ` (${space.pendingCount} pending)`);
|
|
1756
|
+
return ` ${glyph} ${c(dim, tag)} ${space.spaceId} → head ${c(dim, head)}, marker ${c(dim, marker)}${pending}`;
|
|
1757
|
+
}
|
|
1758
|
+
function formatInvariantList(ids) {
|
|
1759
|
+
return ids.length === 0 ? "(none)" : ids.join(", ");
|
|
1760
|
+
}
|
|
1554
1761
|
function summarizeRefDistance(graph, markerHash, refHash, refName) {
|
|
1555
1762
|
if (markerHash === refHash) return `At ref "${refName}" target`;
|
|
1556
1763
|
const pathToRef = findPath(graph, markerHash, refHash);
|
|
@@ -1559,7 +1766,7 @@ function summarizeRefDistance(graph, markerHash, refHash, refName) {
|
|
|
1559
1766
|
if (pathFromRef) return `${pathFromRef.length} migration(s) ahead of ref "${refName}"`;
|
|
1560
1767
|
return `No path between database marker and ref "${refName}" target`;
|
|
1561
1768
|
}
|
|
1562
|
-
|
|
1563
1769
|
//#endregion
|
|
1564
|
-
export {
|
|
1565
|
-
|
|
1770
|
+
export { loadAggregateStatusSpaces as a, formatStatusSummary as i, createMigrationStatusCommand as n, deriveEdgeStatuses as r, computeTotalPendingAcrossSpaces as t };
|
|
1771
|
+
|
|
1772
|
+
//# sourceMappingURL=migration-status-CZ-D5k7k.mjs.map
|