@prisma-next/cli 0.16.0-dev.4 → 0.16.0-dev.5
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/README.md +3 -4
- package/dist/cli.mjs +6 -6
- package/dist/commands/db-sign.d.mts.map +1 -1
- package/dist/commands/db-sign.mjs +4 -6
- 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 +5 -8
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/migrate.mjs +2 -2
- package/dist/commands/migration-check.d.mts +3 -1
- package/dist/commands/migration-check.d.mts.map +1 -1
- package/dist/commands/migration-check.mjs +1 -1
- package/dist/commands/migration-graph.mjs +1 -1
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +1 -1
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +12 -36
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-status.mjs +1 -1
- package/dist/commands/ref.d.mts.map +1 -1
- package/dist/commands/ref.mjs +7 -9
- package/dist/commands/ref.mjs.map +1 -1
- package/dist/{contract-at-errors-D9gEjYtg.mjs → contract-at-errors-CTtzKR6q.mjs} +6 -10
- package/dist/contract-at-errors-CTtzKR6q.mjs.map +1 -0
- package/dist/exports/init-output.d.mts.map +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{init-C3JhkHg-.mjs → init-CgKEi_t5.mjs} +53 -45
- package/dist/init-CgKEi_t5.mjs.map +1 -0
- package/dist/{migration-check-MHT3w7_v.mjs → migration-check-CcuayFB2.mjs} +49 -22
- package/dist/migration-check-CcuayFB2.mjs.map +1 -0
- package/dist/{migration-list-BAKujUjh.mjs → migration-list-6z6vuXHa.mjs} +3 -3
- package/dist/migration-list-6z6vuXHa.mjs.map +1 -0
- package/dist/{migration-plan-CreSVtWZ.mjs → migration-plan-DuMEUejG.mjs} +46 -54
- package/dist/migration-plan-DuMEUejG.mjs.map +1 -0
- package/dist/{migration-status-YgEvXf-u.mjs → migration-status-B61YOaMl.mjs} +3 -2
- package/dist/{migration-status-YgEvXf-u.mjs.map → migration-status-B61YOaMl.mjs.map} +1 -1
- package/dist/{output-mEQ74_nd.mjs → output-DWNYqZGc.mjs} +5 -6
- package/dist/output-DWNYqZGc.mjs.map +1 -0
- package/package.json +21 -21
- package/src/commands/db-sign.ts +6 -4
- package/src/commands/db-update.ts +13 -6
- package/src/commands/init/hygiene-gitattributes.ts +37 -21
- package/src/commands/init/init.ts +6 -6
- package/src/commands/init/output.ts +4 -5
- package/src/commands/init/reinit-cleanup.ts +16 -17
- package/src/commands/migration-check.ts +69 -32
- package/src/commands/migration-list.ts +1 -5
- package/src/commands/migration-new.ts +15 -39
- package/src/commands/migration-plan.ts +54 -76
- package/src/commands/ref.ts +18 -7
- package/src/utils/contract-at-errors.ts +9 -13
- package/src/utils/contract-space-seed-phase.ts +8 -6
- package/src/utils/plan-resolution.ts +1 -4
- package/dist/contract-at-errors-D9gEjYtg.mjs.map +0 -1
- package/dist/init-C3JhkHg-.mjs.map +0 -1
- package/dist/migration-check-MHT3w7_v.mjs.map +0 -1
- package/dist/migration-list-BAKujUjh.mjs.map +0 -1
- package/dist/migration-plan-CreSVtWZ.mjs.map +0 -1
- package/dist/output-mEQ74_nd.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-status-YgEvXf-u.mjs","names":[],"sources":["../src/commands/migration-status-overlay.ts","../src/commands/migration-status.ts"],"sourcesContent":["import type { MigrationGraph } from '@prisma-next/migration-tools/graph';\nimport { findPath } from '@prisma-next/migration-tools/migration-graph';\nimport type { MigrationEdgeAnnotation } from '../utils/formatters/migration-graph-labels';\n\nexport interface DeriveStatusEdgeAnnotationsInput {\n readonly graph: MigrationGraph;\n readonly targetHash: string;\n readonly originHash: string;\n readonly appliedMigrationHashes: ReadonlySet<string>;\n readonly showAppliedOverlay: boolean;\n}\n\nexport function deriveStatusEdgeAnnotations(\n input: DeriveStatusEdgeAnnotationsInput,\n): ReadonlyMap<string, MigrationEdgeAnnotation> {\n const annotations = new Map<string, MigrationEdgeAnnotation>();\n\n if (input.showAppliedOverlay) {\n for (const edge of input.graph.migrationByHash.values()) {\n if (input.appliedMigrationHashes.has(edge.migrationHash)) {\n annotations.set(edge.migrationHash, { status: 'applied' });\n }\n }\n }\n\n if (!input.graph.nodes.has(input.originHash)) {\n return annotations;\n }\n\n const pendingPath = findPath(input.graph, input.originHash, input.targetHash);\n if (!pendingPath) {\n return annotations;\n }\n\n for (const edge of pendingPath) {\n if (input.appliedMigrationHashes.has(edge.migrationHash)) {\n continue;\n }\n const existing = annotations.get(edge.migrationHash);\n if (existing?.status === 'applied') {\n continue;\n }\n annotations.set(edge.migrationHash, { status: 'pending' });\n }\n\n return annotations;\n}\n\nexport function appliedHashesFromLedger(\n ledgerEntries: ReadonlyArray<{ readonly migrationHash: string }>,\n): ReadonlySet<string> {\n return new Set(ledgerEntries.map((entry) => entry.migrationHash));\n}\n\nexport function statusForMigrationHash(\n migrationHash: string,\n annotations: ReadonlyMap<string, MigrationEdgeAnnotation>,\n): 'applied' | 'pending' | null {\n const status = annotations.get(migrationHash)?.status;\n return status ?? null;\n}\n","import { loadConfig } from '@prisma-next/config-loader';\nimport type { LedgerEntryRecord } from '@prisma-next/contract/types';\nimport type {\n AggregateContractSpace,\n ContractMarkerRecordLike,\n} from '@prisma-next/migration-tools/aggregate';\nimport { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';\nimport {\n errorNoInvariantPath,\n errorUnknownInvariant,\n MigrationToolsError,\n} from '@prisma-next/migration-tools/errors';\nimport { findPath, findPathWithDecision } from '@prisma-next/migration-tools/migration-graph';\nimport { parseContractRef } from '@prisma-next/migration-tools/ref-resolution';\nimport type { RefEntry, Refs } from '@prisma-next/migration-tools/refs';\nimport { readRefs } from '@prisma-next/migration-tools/refs';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { dim, yellow } from 'colorette';\nimport { Command } from 'commander';\nimport { createControlClient } from '../control-api/client';\nimport {\n CliStructuredError,\n errorUnexpected,\n mapMigrationToolsError,\n mapRefResolutionError,\n requireLiveDatabase,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n collectDeclaredInvariants,\n maskConnectionUrl,\n readContractEnvelope,\n resolveMigrationPaths,\n setCommandDescriptions,\n setCommandExamples,\n setCommandSeeAlso,\n toStructuralEdge,\n} from '../utils/command-helpers';\nimport {\n buildReadAggregate,\n loadContractRawSafely,\n refusePackageCorruptionOnAggregate,\n} from '../utils/contract-space-aggregate-loader';\nimport {\n type MigrationEdgeAnnotation,\n renderMigrationGraphLegend,\n} from '../utils/formatters/migration-graph-labels';\nimport {\n computeGlobalMaxDirNameWidth,\n computeGlobalMaxEdgeTreePrefixWidth,\n indentMigrationGraphTreeBlock,\n renderMigrationGraphSpaceTree,\n} from '../utils/formatters/migration-graph-space-render';\nimport type { MigrationListEntry } from '../utils/formatters/migration-list-types';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';\nimport { shouldShowLegend, validateLegendOptions } from '../utils/legend';\nimport { handleResult } from '../utils/result-handler';\nimport { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';\nimport type {\n MigrationStatusEntry,\n MigrationStatusResult,\n MigrationStatusSpace,\n StatusDiagnosticJson,\n} from './json/schemas';\nimport { migrationStatusJsonResultSchema } from './json/schemas';\nimport {\n listRefsByContractHash,\n migrationSpaceListEntriesFromAggregate,\n runMigrationList,\n} from './migration-list';\nimport {\n appliedHashesFromLedger,\n deriveStatusEdgeAnnotations,\n statusForMigrationHash,\n} from './migration-status-overlay';\n\nexport type { StatusRef } from '../utils/migration-types';\nexport type {\n MigrationStatusEntry,\n MigrationStatusResult,\n MigrationStatusSpace,\n StatusDiagnosticJson,\n};\nexport { migrationStatusJsonResultSchema };\n\nexport interface MigrationStatusOptions extends CommonCommandOptions {\n readonly db?: string;\n readonly config?: string;\n readonly to?: string;\n readonly from?: string;\n readonly space?: string;\n readonly legend?: boolean;\n readonly ascii?: boolean;\n}\n\nexport interface MigrationStatusTreeSection {\n readonly space: string;\n readonly tree: string;\n readonly showHeading: boolean;\n}\n\ninterface MigrationStatusCommandResult {\n readonly ok: true;\n readonly spaces: readonly MigrationStatusSpace[];\n readonly summary: string;\n readonly diagnostics: readonly StatusDiagnosticJson[];\n readonly treeSections: readonly MigrationStatusTreeSection[];\n}\n\nfunction shortDisplayHash(hash: string): string {\n const stripped = hash.startsWith('sha256:') ? hash.slice(7) : hash;\n return stripped.slice(0, 12);\n}\n\nfunction resolveTarget(contractHash: string, activeRefHash: string | undefined): string {\n return activeRefHash ?? contractHash;\n}\n\nfunction buildStatusMigrations(\n listMigrations: readonly MigrationListEntry[],\n annotations: ReadonlyMap<string, MigrationEdgeAnnotation>,\n): readonly MigrationStatusEntry[] {\n return listMigrations.map((migration) => ({\n ...migration,\n status: statusForMigrationHash(migration.hash, annotations),\n }));\n}\n\nfunction renderSpaceTree(args: {\n readonly space: AggregateContractSpace;\n readonly liveContractHash: string;\n readonly migrations: readonly MigrationListEntry[];\n readonly markerHash: string | undefined;\n readonly showDbMarker: boolean;\n readonly statusOverlay: ReadonlyMap<string, MigrationEdgeAnnotation>;\n readonly colorize: boolean;\n readonly glyphMode: 'unicode' | 'ascii';\n readonly isAppSpace: boolean;\n readonly globalMaxEdgeTreePrefixWidth?: number;\n readonly globalMaxDirNameWidth?: number;\n}): string {\n const graph = args.space.graph();\n if (graph.nodes.size === 0) {\n return '';\n }\n return renderMigrationGraphSpaceTree({\n graph,\n migrations: args.migrations,\n liveContractHash: args.liveContractHash,\n refsByHash: listRefsByContractHash(args.space),\n statusOverlayByHash: args.statusOverlay,\n colorize: args.colorize,\n glyphMode: args.glyphMode,\n isAppSpace: args.isAppSpace,\n ...(args.showDbMarker && args.markerHash !== undefined ? { dbHash: args.markerHash } : {}),\n ...(args.globalMaxEdgeTreePrefixWidth !== undefined\n ? { globalMaxEdgeTreePrefixWidth: args.globalMaxEdgeTreePrefixWidth }\n : {}),\n ...(args.globalMaxDirNameWidth !== undefined\n ? { globalMaxDirNameWidth: args.globalMaxDirNameWidth }\n : {}),\n });\n}\n\nfunction countPending(migrations: readonly MigrationStatusEntry[]): number {\n return migrations.filter((m) => m.status === 'pending').length;\n}\n\nexport function buildNoPathSummary(args: {\n readonly markerHash: string | undefined;\n readonly targetHash: string;\n readonly explicitTarget: boolean;\n readonly refName: string | undefined;\n}): string {\n const markerPart =\n args.markerHash !== undefined\n ? `the database state (${shortDisplayHash(args.markerHash)})`\n : 'the database state';\n const targetShort = shortDisplayHash(args.targetHash);\n if (!args.explicitTarget) {\n return `No migration path from ${markerPart} to the application's contract (${targetShort}). Run \\`prisma-next migration plan --name <name>\\` to author one.`;\n }\n const targetLabel =\n args.refName !== undefined\n ? `the target (${targetShort} via \\`${args.refName}\\`)`\n : `the target (${targetShort})`;\n return `No migration path from ${markerPart} to ${targetLabel}. Run \\`prisma-next migration plan --name <name>\\` to author one, or pass \\`--to <contract>\\` to pick a reachable target.`;\n}\n\nexport function buildStatusHeadline(args: {\n readonly pendingCount: number;\n readonly targetHash: string;\n readonly markerDiverged: boolean;\n readonly markerHash: string | undefined;\n}): string {\n if (args.markerDiverged && args.markerHash !== undefined) {\n return `Database marker ${shortDisplayHash(args.markerHash)} is not in the on-disk migration graph`;\n }\n if (args.pendingCount === 0) {\n return 'Up to date';\n }\n return `${args.pendingCount} pending — run \\`prisma-next migrate --to ${shortDisplayHash(args.targetHash)}\\``;\n}\n\nexport function formatStatusSummary(\n result: MigrationStatusCommandResult,\n colorize: boolean,\n): string {\n const c = (fn: (s: string) => string, s: string) => (colorize ? fn(s) : s);\n const lines: string[] = [];\n const pendingTotal = result.spaces.reduce(\n (sum, space) => sum + countPending(space.migrations),\n 0,\n );\n const hasDivergence = result.diagnostics.some(\n (d) => d.code === 'MIGRATION.MARKER_NOT_IN_HISTORY',\n );\n if (hasDivergence || pendingTotal > 0) {\n lines.push(c(yellow, result.summary));\n } else {\n lines.push(result.summary);\n }\n const missingInvariantsDiagnostic = result.diagnostics.find(\n (d) => d.code === 'MIGRATION.MISSING_INVARIANTS',\n );\n if (missingInvariantsDiagnostic !== undefined) {\n lines.push(c(dim, missingInvariantsDiagnostic.message));\n }\n return lines.join('\\n');\n}\n\nexport function formatStatusHumanOutput(\n result: MigrationStatusCommandResult,\n colorize: boolean,\n): string {\n const sections: string[] = [];\n for (const section of result.treeSections) {\n if (section.showHeading) {\n sections.push(`${section.space}:`);\n }\n if (section.tree.length > 0) {\n sections.push(section.tree);\n } else {\n sections.push('(no migrations)');\n }\n sections.push('');\n }\n sections.push(formatStatusSummary(result, colorize));\n return sections.join('\\n').trimEnd();\n}\n\nasync function readMarkersAndLedgers(args: {\n readonly client: ReturnType<typeof createControlClient>;\n readonly spaceIds: readonly string[];\n}): Promise<{\n readonly markersBySpace: ReadonlyMap<string, ContractMarkerRecordLike>;\n readonly ledgersBySpace: ReadonlyMap<string, readonly LedgerEntryRecord[]>;\n}> {\n const markersBySpace = new Map<string, ContractMarkerRecordLike>();\n const all = await args.client.readAllMarkers();\n for (const [spaceId, marker] of all) {\n markersBySpace.set(spaceId, marker);\n }\n const ledgersBySpace = new Map<string, readonly LedgerEntryRecord[]>();\n for (const spaceId of args.spaceIds) {\n ledgersBySpace.set(spaceId, await args.client.readLedger(spaceId));\n }\n return { markersBySpace, ledgersBySpace };\n}\n\nexport async function executeMigrationStatusCommand(\n options: MigrationStatusOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n): Promise<Result<MigrationStatusCommandResult, CliStructuredError>> {\n const config = await loadConfig(options.config);\n const { configPath, migrationsDir, migrationsRelative, refsDir } = resolveMigrationPaths(\n options.config,\n config,\n );\n\n const dbConnection = options.db ?? config.db?.connection;\n const hasDriver = !!config.driver;\n const usingFromOverride = options.from !== undefined;\n\n if (!usingFromOverride) {\n const missingDb = requireLiveDatabase({\n dbConnection,\n hasDriver,\n why: 'migration status needs a database connection to read the marker and ledger (or pass --from for offline path preview)',\n retryCommand: 'prisma-next migration status --from <contract>',\n });\n if (missingDb) {\n return notOk(missingDb);\n }\n }\n\n let allRefs: Refs = {};\n try {\n allRefs = await readRefs(refsDir);\n } catch (error) {\n if (MigrationToolsError.is(error)) {\n return notOk(mapMigrationToolsError(error));\n }\n throw error;\n }\n\n const diagnostics: StatusDiagnosticJson[] = [];\n let contractHash: string = EMPTY_CONTRACT_HASH;\n try {\n const envelope = await readContractEnvelope(config);\n contractHash = envelope.storageHash;\n } catch (error) {\n diagnostics.push({\n code: 'CONTRACT.UNREADABLE',\n severity: 'warn',\n message: `Could not read contract: ${error instanceof Error ? error.message : 'unknown error'}`,\n hints: [\"Run 'prisma-next contract emit' to generate a valid contract\"],\n });\n }\n\n const loaded = await buildReadAggregate(config, { migrationsDir });\n if (!loaded.ok) {\n return notOk(loaded.failure);\n }\n\n const { aggregate } = loaded.value;\n const contractRawForAggregate = await loadContractRawSafely(config);\n if (contractRawForAggregate !== null) {\n const corruptionFailure = refusePackageCorruptionOnAggregate(aggregate);\n if (corruptionFailure) {\n return notOk(corruptionFailure);\n }\n }\n const appGraph = aggregate.app.graph();\n\n let activeRefHash: string | undefined;\n let activeRefName: string | undefined;\n let activeRefEntry: RefEntry | undefined;\n let fromOverrideHash: string | undefined;\n\n if (options.to) {\n const refResult = parseContractRef(options.to, { graph: appGraph, refs: allRefs });\n if (!refResult.ok) {\n return notOk(mapRefResolutionError(refResult.failure));\n }\n activeRefHash = refResult.value.hash;\n if (refResult.value.provenance.kind === 'ref') {\n activeRefName = refResult.value.provenance.refName;\n activeRefEntry = allRefs[activeRefName];\n }\n }\n\n if (options.from) {\n const fromResult = parseContractRef(options.from, { graph: appGraph, refs: allRefs });\n if (!fromResult.ok) {\n return notOk(mapRefResolutionError(fromResult.failure));\n }\n fromOverrideHash = fromResult.value.hash;\n }\n\n const requiredInvariants: readonly string[] = [...(activeRefEntry?.invariants ?? [])].sort();\n\n if (!flags.json && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n { label: 'migrations', value: migrationsRelative },\n ];\n if (dbConnection && hasDriver) {\n details.push({ label: 'database', value: maskConnectionUrl(String(dbConnection)) });\n }\n if (activeRefName) {\n details.push({ label: 'ref', value: activeRefName });\n }\n if (options.from) {\n details.push({ label: 'from', value: options.from });\n }\n if (options.space) {\n details.push({ label: 'space', value: options.space });\n }\n const header = formatStyledHeader({\n command: 'migration status',\n description: 'Show migration history and applied status',\n details,\n flags,\n });\n ui.stderr(header);\n if (shouldShowLegend(options, flags)) {\n ui.stderr(\n renderMigrationGraphLegend({\n colorize: flags.color !== false,\n glyphMode: ui.resolveGlyphMode(options.ascii === true),\n }),\n );\n ui.stderr('');\n }\n }\n\n const listSpaces = await migrationSpaceListEntriesFromAggregate(aggregate, migrationsDir);\n const listResult = runMigrationList({\n spaces: listSpaces,\n ...ifDefined('spaceFilter', options.space),\n });\n if (!listResult.ok) {\n return listResult;\n }\n\n const scopedSpaces = listResult.value.spaces;\n const showSpaceHeadings = scopedSpaces.length > 1;\n\n let markersBySpace = new Map<string, ContractMarkerRecordLike>();\n let ledgersBySpace = new Map<string, readonly LedgerEntryRecord[]>();\n let connected = false;\n\n if (dbConnection && hasDriver && !usingFromOverride) {\n const client = createControlClient({\n family: config.family,\n target: config.target,\n adapter: config.adapter,\n driver: config.driver,\n extensionPacks: config.extensionPacks ?? [],\n });\n try {\n await client.connect(dbConnection);\n connected = true;\n const read = await readMarkersAndLedgers({\n client,\n spaceIds: scopedSpaces.map((s) => s.space),\n });\n markersBySpace = new Map(read.markersBySpace);\n ledgersBySpace = new Map(read.ledgersBySpace);\n } catch (error) {\n if (CliStructuredError.is(error)) {\n return notOk(error);\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to read database state: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n } finally {\n await client.close();\n }\n }\n\n if (activeRefEntry && activeRefEntry.invariants.length > 0 && connected) {\n const declared = collectDeclaredInvariants(appGraph);\n const markerInvariants = markersBySpace.get(aggregate.app.spaceId)?.invariants ?? [];\n const known = new Set<string>(declared);\n for (const id of markerInvariants) known.add(id);\n const unknown = activeRefEntry.invariants.filter((id) => !known.has(id));\n if (unknown.length > 0) {\n return notOk(\n mapMigrationToolsError(\n errorUnknownInvariant({\n ...ifDefined('refName', activeRefName),\n unknown,\n declared: [...declared].sort(),\n }),\n ),\n );\n }\n }\n\n const showAppliedOverlay = connected && !usingFromOverride;\n const showDbMarker = connected && !usingFromOverride;\n const glyphMode = ui.resolveGlyphMode(options.ascii === true);\n const colorize = flags.color !== false;\n\n const statusSpaces: MigrationStatusSpace[] = [];\n const treeSections: MigrationStatusTreeSection[] = [];\n let markerDiverged = false;\n let markerCannotReachTarget = false;\n let headlineTargetHash = activeRefHash ?? contractHash;\n let totalPending = 0;\n\n const globalLayoutInputs = showSpaceHeadings\n ? scopedSpaces\n .filter((spaceEntry) => spaceEntry.migrations.length > 0)\n .map((spaceEntry) => ({\n graph: aggregate.space(spaceEntry.space)!.graph(),\n liveContractHash: contractHash,\n }))\n : [];\n const globalMaxEdgeTreePrefixWidth =\n globalLayoutInputs.length > 0\n ? computeGlobalMaxEdgeTreePrefixWidth(globalLayoutInputs)\n : undefined;\n const globalMaxDirNameWidth =\n globalLayoutInputs.length > 0 ? computeGlobalMaxDirNameWidth(globalLayoutInputs) : undefined;\n\n for (const spaceEntry of scopedSpaces) {\n const space = aggregate.space(spaceEntry.space);\n if (space === undefined) {\n continue;\n }\n const graph = space.graph();\n const spaceContractHash = space.contract().storage.storageHash;\n const targetHash = resolveTarget(spaceContractHash, activeRefHash);\n if (spaceEntry.space === aggregate.app.spaceId) {\n headlineTargetHash = targetHash;\n }\n\n const markerRecord = markersBySpace.get(spaceEntry.space);\n const markerHash = usingFromOverride\n ? fromOverrideHash\n : (markerRecord?.storageHash ?? undefined);\n const originHash = markerHash ?? EMPTY_CONTRACT_HASH;\n const markerInGraph =\n markerHash === undefined || graph.nodes.has(markerHash) || markerHash === spaceContractHash;\n if (\n connected &&\n !usingFromOverride &&\n markerInGraph &&\n originHash !== targetHash &&\n findPath(graph, originHash, targetHash) === null\n ) {\n markerCannotReachTarget = true;\n }\n\n if (connected && !usingFromOverride && markerHash !== undefined && !markerInGraph) {\n markerDiverged = true;\n diagnostics.push({\n code: 'MIGRATION.MARKER_NOT_IN_HISTORY',\n severity: 'warn',\n message:\n 'Database was updated outside the migration system (marker does not match any migration)',\n hints: [\n \"Run 'prisma-next db sign' to overwrite the marker if the database already matches the contract\",\n \"Run 'prisma-next db update' to push the current contract to the database\",\n ],\n });\n }\n\n const ledger = ledgersBySpace.get(spaceEntry.space) ?? [];\n const appliedHashes = showAppliedOverlay ? appliedHashesFromLedger(ledger) : new Set<string>();\n\n const annotations = deriveStatusEdgeAnnotations({\n graph,\n targetHash,\n originHash,\n appliedMigrationHashes: appliedHashes,\n showAppliedOverlay,\n });\n const isAppSpace = spaceEntry.space === aggregate.app.spaceId;\n const tree = renderSpaceTree({\n space,\n liveContractHash: contractHash,\n migrations: spaceEntry.migrations,\n markerHash,\n showDbMarker,\n statusOverlay: annotations,\n colorize,\n glyphMode,\n isAppSpace,\n ...(globalMaxEdgeTreePrefixWidth !== undefined ? { globalMaxEdgeTreePrefixWidth } : {}),\n ...(globalMaxDirNameWidth !== undefined ? { globalMaxDirNameWidth } : {}),\n });\n const migrations = buildStatusMigrations(spaceEntry.migrations, annotations);\n const pending = countPending(migrations);\n totalPending += pending;\n\n statusSpaces.push({\n space: spaceEntry.space,\n currentContract: markerHash ?? null,\n targetContract: targetHash,\n migrations: [...migrations],\n });\n const displayTree =\n showSpaceHeadings && tree.length > 0 ? indentMigrationGraphTreeBlock(tree, ' ') : tree;\n treeSections.push({\n space: spaceEntry.space,\n tree: displayTree,\n showHeading: showSpaceHeadings,\n });\n }\n\n if (connected && requiredInvariants.length > 0) {\n const markerInvariants = markersBySpace.get(aggregate.app.spaceId)?.invariants ?? [];\n const markerSet = new Set(markerInvariants);\n const missing = requiredInvariants.filter((id) => !markerSet.has(id));\n if (missing.length > 0) {\n diagnostics.push({\n code: 'MIGRATION.MISSING_INVARIANTS',\n ...ifDefined('ref', activeRefName),\n invariants: missing,\n message: `missing invariant(s): ${missing.join(', ')}`,\n });\n if (activeRefHash !== undefined) {\n const originHash =\n markersBySpace.get(aggregate.app.spaceId)?.storageHash ?? EMPTY_CONTRACT_HASH;\n const outcome = findPathWithDecision(appGraph, originHash, activeRefHash, {\n ...ifDefined('refName', activeRefName),\n required: new Set(missing),\n });\n if (outcome.kind === 'unsatisfiable') {\n return notOk(\n mapMigrationToolsError(\n errorNoInvariantPath({\n ...ifDefined('refName', activeRefName),\n required: [...missing].sort(),\n missing: outcome.missing,\n structuralPath: outcome.structuralPath.map(toStructuralEdge),\n }),\n ),\n );\n }\n }\n }\n }\n\n const appMarkerHash = markersBySpace.get(aggregate.app.spaceId)?.storageHash;\n const summary = markerCannotReachTarget\n ? buildNoPathSummary({\n markerHash: appMarkerHash,\n targetHash: headlineTargetHash,\n explicitTarget: options.to !== undefined,\n refName: activeRefName,\n })\n : buildStatusHeadline({\n pendingCount: totalPending,\n targetHash: headlineTargetHash,\n markerDiverged,\n markerHash: appMarkerHash,\n });\n\n if (scopedSpaces.every((s) => s.migrations.length === 0)) {\n return ok({\n ok: true,\n spaces: statusSpaces,\n summary: 'No migrations found',\n diagnostics,\n treeSections,\n });\n }\n\n return ok({\n ok: true,\n spaces: statusSpaces,\n summary,\n diagnostics,\n treeSections,\n });\n}\n\nexport function createMigrationStatusCommand(): Command {\n const command = new Command('status');\n setCommandDescriptions(\n command,\n 'Show migration path and pending status',\n 'Shows which migrations are pending between the database marker and\\n' +\n 'the target contract. Requires a database connection.\\n' +\n 'Pass --from for an offline path preview without a database.\\n' +\n 'Use `migration graph` for topology, `migration log` for history,\\n' +\n 'and `migration list` for on-disk enumeration.',\n );\n setCommandExamples(command, [\n 'prisma-next migration status --db $DATABASE_URL',\n 'prisma-next migration status --to production --db $DATABASE_URL',\n 'prisma-next migration status --from sha256:abc --to production',\n 'prisma-next migration status --from sha256:abc --to production --json',\n 'prisma-next migration status --ascii --from sha256:abc --to production',\n 'prisma-next migration status --legend --from sha256:abc --to production',\n ]);\n setCommandSeeAlso(command, [\n { verb: 'migration log', oneLiner: 'Show executed migration history' },\n { verb: 'migration list', oneLiner: 'List on-disk migrations' },\n { verb: 'migration graph', oneLiner: 'Show the migration graph topology' },\n { verb: 'migration show', oneLiner: 'Display migration package contents' },\n ]);\n addGlobalOptions(command)\n .option('--db <url>', 'Database connection string')\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .option('--space <id>', 'Narrow output to a single contract space')\n .option(\n '--to <contract>',\n 'Target contract reference (hash, prefix, ref name, migration dir name, <dir>^, or ./path)',\n )\n .option(\n '--from <contract>',\n 'Origin contract reference; same grammar as --to. Supplying --from switches to offline path computation.',\n )\n .option('--legend', 'Print a key for the tree glyphs and lane colors')\n .option('--ascii', 'Use ASCII glyphs (pipe-friendly)')\n .action(async (options: MigrationStatusOptions) => {\n const flags = parseGlobalFlagsOrExit(options);\n const ui = createTerminalUI(flags);\n\n const legendValidation = validateLegendOptions(options, flags);\n if (!legendValidation.ok) {\n process.exit(handleResult(legendValidation, flags, ui));\n }\n\n const result = await executeMigrationStatusCommand(options, flags, ui);\n\n const exitCode = handleResult(result, flags, ui, (statusResult) => {\n if (flags.json) {\n const jsonResult: MigrationStatusResult = {\n ok: true,\n spaces: [...statusResult.spaces],\n summary: statusResult.summary,\n diagnostics: [...statusResult.diagnostics],\n };\n ui.output(JSON.stringify(jsonResult, null, 2));\n } else if (!flags.quiet) {\n ui.output(formatStatusHumanOutput(statusResult, flags.color !== false));\n }\n });\n\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"migration-status-B61YOaMl.mjs","names":[],"sources":["../src/commands/migration-status-overlay.ts","../src/commands/migration-status.ts"],"sourcesContent":["import type { MigrationGraph } from '@prisma-next/migration-tools/graph';\nimport { findPath } from '@prisma-next/migration-tools/migration-graph';\nimport type { MigrationEdgeAnnotation } from '../utils/formatters/migration-graph-labels';\n\nexport interface DeriveStatusEdgeAnnotationsInput {\n readonly graph: MigrationGraph;\n readonly targetHash: string;\n readonly originHash: string;\n readonly appliedMigrationHashes: ReadonlySet<string>;\n readonly showAppliedOverlay: boolean;\n}\n\nexport function deriveStatusEdgeAnnotations(\n input: DeriveStatusEdgeAnnotationsInput,\n): ReadonlyMap<string, MigrationEdgeAnnotation> {\n const annotations = new Map<string, MigrationEdgeAnnotation>();\n\n if (input.showAppliedOverlay) {\n for (const edge of input.graph.migrationByHash.values()) {\n if (input.appliedMigrationHashes.has(edge.migrationHash)) {\n annotations.set(edge.migrationHash, { status: 'applied' });\n }\n }\n }\n\n if (!input.graph.nodes.has(input.originHash)) {\n return annotations;\n }\n\n const pendingPath = findPath(input.graph, input.originHash, input.targetHash);\n if (!pendingPath) {\n return annotations;\n }\n\n for (const edge of pendingPath) {\n if (input.appliedMigrationHashes.has(edge.migrationHash)) {\n continue;\n }\n const existing = annotations.get(edge.migrationHash);\n if (existing?.status === 'applied') {\n continue;\n }\n annotations.set(edge.migrationHash, { status: 'pending' });\n }\n\n return annotations;\n}\n\nexport function appliedHashesFromLedger(\n ledgerEntries: ReadonlyArray<{ readonly migrationHash: string }>,\n): ReadonlySet<string> {\n return new Set(ledgerEntries.map((entry) => entry.migrationHash));\n}\n\nexport function statusForMigrationHash(\n migrationHash: string,\n annotations: ReadonlyMap<string, MigrationEdgeAnnotation>,\n): 'applied' | 'pending' | null {\n const status = annotations.get(migrationHash)?.status;\n return status ?? null;\n}\n","import { loadConfig } from '@prisma-next/config-loader';\nimport type { LedgerEntryRecord } from '@prisma-next/contract/types';\nimport type {\n AggregateContractSpace,\n ContractMarkerRecordLike,\n} from '@prisma-next/migration-tools/aggregate';\nimport { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';\nimport {\n errorNoInvariantPath,\n errorUnknownInvariant,\n MigrationToolsError,\n} from '@prisma-next/migration-tools/errors';\nimport { findPath, findPathWithDecision } from '@prisma-next/migration-tools/migration-graph';\nimport { parseContractRef } from '@prisma-next/migration-tools/ref-resolution';\nimport type { RefEntry, Refs } from '@prisma-next/migration-tools/refs';\nimport { readRefs } from '@prisma-next/migration-tools/refs';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { dim, yellow } from 'colorette';\nimport { Command } from 'commander';\nimport { createControlClient } from '../control-api/client';\nimport {\n CliStructuredError,\n errorUnexpected,\n mapMigrationToolsError,\n mapRefResolutionError,\n requireLiveDatabase,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n collectDeclaredInvariants,\n maskConnectionUrl,\n readContractEnvelope,\n resolveMigrationPaths,\n setCommandDescriptions,\n setCommandExamples,\n setCommandSeeAlso,\n toStructuralEdge,\n} from '../utils/command-helpers';\nimport {\n buildReadAggregate,\n loadContractRawSafely,\n refusePackageCorruptionOnAggregate,\n} from '../utils/contract-space-aggregate-loader';\nimport {\n type MigrationEdgeAnnotation,\n renderMigrationGraphLegend,\n} from '../utils/formatters/migration-graph-labels';\nimport {\n computeGlobalMaxDirNameWidth,\n computeGlobalMaxEdgeTreePrefixWidth,\n indentMigrationGraphTreeBlock,\n renderMigrationGraphSpaceTree,\n} from '../utils/formatters/migration-graph-space-render';\nimport type { MigrationListEntry } from '../utils/formatters/migration-list-types';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';\nimport { shouldShowLegend, validateLegendOptions } from '../utils/legend';\nimport { handleResult } from '../utils/result-handler';\nimport { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';\nimport type {\n MigrationStatusEntry,\n MigrationStatusResult,\n MigrationStatusSpace,\n StatusDiagnosticJson,\n} from './json/schemas';\nimport { migrationStatusJsonResultSchema } from './json/schemas';\nimport {\n listRefsByContractHash,\n migrationSpaceListEntriesFromAggregate,\n runMigrationList,\n} from './migration-list';\nimport {\n appliedHashesFromLedger,\n deriveStatusEdgeAnnotations,\n statusForMigrationHash,\n} from './migration-status-overlay';\n\nexport type { StatusRef } from '../utils/migration-types';\nexport type {\n MigrationStatusEntry,\n MigrationStatusResult,\n MigrationStatusSpace,\n StatusDiagnosticJson,\n};\nexport { migrationStatusJsonResultSchema };\n\nexport interface MigrationStatusOptions extends CommonCommandOptions {\n readonly db?: string;\n readonly config?: string;\n readonly to?: string;\n readonly from?: string;\n readonly space?: string;\n readonly legend?: boolean;\n readonly ascii?: boolean;\n}\n\nexport interface MigrationStatusTreeSection {\n readonly space: string;\n readonly tree: string;\n readonly showHeading: boolean;\n}\n\ninterface MigrationStatusCommandResult {\n readonly ok: true;\n readonly spaces: readonly MigrationStatusSpace[];\n readonly summary: string;\n readonly diagnostics: readonly StatusDiagnosticJson[];\n readonly treeSections: readonly MigrationStatusTreeSection[];\n}\n\nfunction shortDisplayHash(hash: string): string {\n const stripped = hash.startsWith('sha256:') ? hash.slice(7) : hash;\n return stripped.slice(0, 12);\n}\n\nfunction resolveTarget(contractHash: string, activeRefHash: string | undefined): string {\n return activeRefHash ?? contractHash;\n}\n\nfunction buildStatusMigrations(\n listMigrations: readonly MigrationListEntry[],\n annotations: ReadonlyMap<string, MigrationEdgeAnnotation>,\n): readonly MigrationStatusEntry[] {\n return listMigrations.map((migration) => ({\n ...migration,\n status: statusForMigrationHash(migration.hash, annotations),\n }));\n}\n\nfunction renderSpaceTree(args: {\n readonly space: AggregateContractSpace;\n readonly liveContractHash: string;\n readonly migrations: readonly MigrationListEntry[];\n readonly markerHash: string | undefined;\n readonly showDbMarker: boolean;\n readonly statusOverlay: ReadonlyMap<string, MigrationEdgeAnnotation>;\n readonly colorize: boolean;\n readonly glyphMode: 'unicode' | 'ascii';\n readonly isAppSpace: boolean;\n readonly globalMaxEdgeTreePrefixWidth?: number;\n readonly globalMaxDirNameWidth?: number;\n}): string {\n const graph = args.space.graph();\n if (graph.nodes.size === 0) {\n return '';\n }\n return renderMigrationGraphSpaceTree({\n graph,\n migrations: args.migrations,\n liveContractHash: args.liveContractHash,\n refsByHash: listRefsByContractHash(args.space),\n statusOverlayByHash: args.statusOverlay,\n colorize: args.colorize,\n glyphMode: args.glyphMode,\n isAppSpace: args.isAppSpace,\n ...(args.showDbMarker && args.markerHash !== undefined ? { dbHash: args.markerHash } : {}),\n ...(args.globalMaxEdgeTreePrefixWidth !== undefined\n ? { globalMaxEdgeTreePrefixWidth: args.globalMaxEdgeTreePrefixWidth }\n : {}),\n ...(args.globalMaxDirNameWidth !== undefined\n ? { globalMaxDirNameWidth: args.globalMaxDirNameWidth }\n : {}),\n });\n}\n\nfunction countPending(migrations: readonly MigrationStatusEntry[]): number {\n return migrations.filter((m) => m.status === 'pending').length;\n}\n\nexport function buildNoPathSummary(args: {\n readonly markerHash: string | undefined;\n readonly targetHash: string;\n readonly explicitTarget: boolean;\n readonly refName: string | undefined;\n}): string {\n const markerPart =\n args.markerHash !== undefined\n ? `the database state (${shortDisplayHash(args.markerHash)})`\n : 'the database state';\n const targetShort = shortDisplayHash(args.targetHash);\n if (!args.explicitTarget) {\n return `No migration path from ${markerPart} to the application's contract (${targetShort}). Run \\`prisma-next migration plan --name <name>\\` to author one.`;\n }\n const targetLabel =\n args.refName !== undefined\n ? `the target (${targetShort} via \\`${args.refName}\\`)`\n : `the target (${targetShort})`;\n return `No migration path from ${markerPart} to ${targetLabel}. Run \\`prisma-next migration plan --name <name>\\` to author one, or pass \\`--to <contract>\\` to pick a reachable target.`;\n}\n\nexport function buildStatusHeadline(args: {\n readonly pendingCount: number;\n readonly targetHash: string;\n readonly markerDiverged: boolean;\n readonly markerHash: string | undefined;\n}): string {\n if (args.markerDiverged && args.markerHash !== undefined) {\n return `Database marker ${shortDisplayHash(args.markerHash)} is not in the on-disk migration graph`;\n }\n if (args.pendingCount === 0) {\n return 'Up to date';\n }\n return `${args.pendingCount} pending — run \\`prisma-next migrate --to ${shortDisplayHash(args.targetHash)}\\``;\n}\n\nexport function formatStatusSummary(\n result: MigrationStatusCommandResult,\n colorize: boolean,\n): string {\n const c = (fn: (s: string) => string, s: string) => (colorize ? fn(s) : s);\n const lines: string[] = [];\n const pendingTotal = result.spaces.reduce(\n (sum, space) => sum + countPending(space.migrations),\n 0,\n );\n const hasDivergence = result.diagnostics.some(\n (d) => d.code === 'MIGRATION.MARKER_NOT_IN_HISTORY',\n );\n if (hasDivergence || pendingTotal > 0) {\n lines.push(c(yellow, result.summary));\n } else {\n lines.push(result.summary);\n }\n const missingInvariantsDiagnostic = result.diagnostics.find(\n (d) => d.code === 'MIGRATION.MISSING_INVARIANTS',\n );\n if (missingInvariantsDiagnostic !== undefined) {\n lines.push(c(dim, missingInvariantsDiagnostic.message));\n }\n return lines.join('\\n');\n}\n\nexport function formatStatusHumanOutput(\n result: MigrationStatusCommandResult,\n colorize: boolean,\n): string {\n const sections: string[] = [];\n for (const section of result.treeSections) {\n if (section.showHeading) {\n sections.push(`${section.space}:`);\n }\n if (section.tree.length > 0) {\n sections.push(section.tree);\n } else {\n sections.push('(no migrations)');\n }\n sections.push('');\n }\n sections.push(formatStatusSummary(result, colorize));\n return sections.join('\\n').trimEnd();\n}\n\nasync function readMarkersAndLedgers(args: {\n readonly client: ReturnType<typeof createControlClient>;\n readonly spaceIds: readonly string[];\n}): Promise<{\n readonly markersBySpace: ReadonlyMap<string, ContractMarkerRecordLike>;\n readonly ledgersBySpace: ReadonlyMap<string, readonly LedgerEntryRecord[]>;\n}> {\n const markersBySpace = new Map<string, ContractMarkerRecordLike>();\n const all = await args.client.readAllMarkers();\n for (const [spaceId, marker] of all) {\n markersBySpace.set(spaceId, marker);\n }\n const ledgersBySpace = new Map<string, readonly LedgerEntryRecord[]>();\n for (const spaceId of args.spaceIds) {\n ledgersBySpace.set(spaceId, await args.client.readLedger(spaceId));\n }\n return { markersBySpace, ledgersBySpace };\n}\n\nexport async function executeMigrationStatusCommand(\n options: MigrationStatusOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n): Promise<Result<MigrationStatusCommandResult, CliStructuredError>> {\n const config = await loadConfig(options.config);\n const { configPath, migrationsDir, migrationsRelative, refsDir } = resolveMigrationPaths(\n options.config,\n config,\n );\n\n const dbConnection = options.db ?? config.db?.connection;\n const hasDriver = !!config.driver;\n const usingFromOverride = options.from !== undefined;\n\n if (!usingFromOverride) {\n const missingDb = requireLiveDatabase({\n dbConnection,\n hasDriver,\n why: 'migration status needs a database connection to read the marker and ledger (or pass --from for offline path preview)',\n retryCommand: 'prisma-next migration status --from <contract>',\n });\n if (missingDb) {\n return notOk(missingDb);\n }\n }\n\n let allRefs: Refs = {};\n try {\n allRefs = await readRefs(refsDir);\n } catch (error) {\n if (MigrationToolsError.is(error)) {\n return notOk(mapMigrationToolsError(error));\n }\n throw error;\n }\n\n const diagnostics: StatusDiagnosticJson[] = [];\n let contractHash: string = EMPTY_CONTRACT_HASH;\n try {\n const envelope = await readContractEnvelope(config);\n contractHash = envelope.storageHash;\n } catch (error) {\n diagnostics.push({\n code: 'CONTRACT.UNREADABLE',\n severity: 'warn',\n message: `Could not read contract: ${error instanceof Error ? error.message : 'unknown error'}`,\n hints: [\"Run 'prisma-next contract emit' to generate a valid contract\"],\n });\n }\n\n const loaded = await buildReadAggregate(config, { migrationsDir });\n if (!loaded.ok) {\n return notOk(loaded.failure);\n }\n\n const { aggregate } = loaded.value;\n const contractRawForAggregate = await loadContractRawSafely(config);\n if (contractRawForAggregate !== null) {\n const corruptionFailure = refusePackageCorruptionOnAggregate(aggregate);\n if (corruptionFailure) {\n return notOk(corruptionFailure);\n }\n }\n const appGraph = aggregate.app.graph();\n\n let activeRefHash: string | undefined;\n let activeRefName: string | undefined;\n let activeRefEntry: RefEntry | undefined;\n let fromOverrideHash: string | undefined;\n\n if (options.to) {\n const refResult = parseContractRef(options.to, { graph: appGraph, refs: allRefs });\n if (!refResult.ok) {\n return notOk(mapRefResolutionError(refResult.failure));\n }\n activeRefHash = refResult.value.hash;\n if (refResult.value.provenance.kind === 'ref') {\n activeRefName = refResult.value.provenance.refName;\n activeRefEntry = allRefs[activeRefName];\n }\n }\n\n if (options.from) {\n const fromResult = parseContractRef(options.from, { graph: appGraph, refs: allRefs });\n if (!fromResult.ok) {\n return notOk(mapRefResolutionError(fromResult.failure));\n }\n fromOverrideHash = fromResult.value.hash;\n }\n\n const requiredInvariants: readonly string[] = [...(activeRefEntry?.invariants ?? [])].sort();\n\n if (!flags.json && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n { label: 'migrations', value: migrationsRelative },\n ];\n if (dbConnection && hasDriver) {\n details.push({ label: 'database', value: maskConnectionUrl(String(dbConnection)) });\n }\n if (activeRefName) {\n details.push({ label: 'ref', value: activeRefName });\n }\n if (options.from) {\n details.push({ label: 'from', value: options.from });\n }\n if (options.space) {\n details.push({ label: 'space', value: options.space });\n }\n const header = formatStyledHeader({\n command: 'migration status',\n description: 'Show migration history and applied status',\n details,\n flags,\n });\n ui.stderr(header);\n if (shouldShowLegend(options, flags)) {\n ui.stderr(\n renderMigrationGraphLegend({\n colorize: flags.color !== false,\n glyphMode: ui.resolveGlyphMode(options.ascii === true),\n }),\n );\n ui.stderr('');\n }\n }\n\n const listSpaces = await migrationSpaceListEntriesFromAggregate(aggregate, migrationsDir);\n const listResult = runMigrationList({\n spaces: listSpaces,\n ...ifDefined('spaceFilter', options.space),\n });\n if (!listResult.ok) {\n return listResult;\n }\n\n const scopedSpaces = listResult.value.spaces;\n const showSpaceHeadings = scopedSpaces.length > 1;\n\n let markersBySpace = new Map<string, ContractMarkerRecordLike>();\n let ledgersBySpace = new Map<string, readonly LedgerEntryRecord[]>();\n let connected = false;\n\n if (dbConnection && hasDriver && !usingFromOverride) {\n const client = createControlClient({\n family: config.family,\n target: config.target,\n adapter: config.adapter,\n driver: config.driver,\n extensionPacks: config.extensionPacks ?? [],\n });\n try {\n await client.connect(dbConnection);\n connected = true;\n const read = await readMarkersAndLedgers({\n client,\n spaceIds: scopedSpaces.map((s) => s.space),\n });\n markersBySpace = new Map(read.markersBySpace);\n ledgersBySpace = new Map(read.ledgersBySpace);\n } catch (error) {\n if (CliStructuredError.is(error)) {\n return notOk(error);\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to read database state: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n } finally {\n await client.close();\n }\n }\n\n if (activeRefEntry && activeRefEntry.invariants.length > 0 && connected) {\n const declared = collectDeclaredInvariants(appGraph);\n const markerInvariants = markersBySpace.get(aggregate.app.spaceId)?.invariants ?? [];\n const known = new Set<string>(declared);\n for (const id of markerInvariants) known.add(id);\n const unknown = activeRefEntry.invariants.filter((id) => !known.has(id));\n if (unknown.length > 0) {\n return notOk(\n mapMigrationToolsError(\n errorUnknownInvariant({\n ...ifDefined('refName', activeRefName),\n unknown,\n declared: [...declared].sort(),\n }),\n ),\n );\n }\n }\n\n const showAppliedOverlay = connected && !usingFromOverride;\n const showDbMarker = connected && !usingFromOverride;\n const glyphMode = ui.resolveGlyphMode(options.ascii === true);\n const colorize = flags.color !== false;\n\n const statusSpaces: MigrationStatusSpace[] = [];\n const treeSections: MigrationStatusTreeSection[] = [];\n let markerDiverged = false;\n let markerCannotReachTarget = false;\n let headlineTargetHash = activeRefHash ?? contractHash;\n let totalPending = 0;\n\n const globalLayoutInputs = showSpaceHeadings\n ? scopedSpaces\n .filter((spaceEntry) => spaceEntry.migrations.length > 0)\n .map((spaceEntry) => ({\n graph: aggregate.space(spaceEntry.space)!.graph(),\n liveContractHash: contractHash,\n }))\n : [];\n const globalMaxEdgeTreePrefixWidth =\n globalLayoutInputs.length > 0\n ? computeGlobalMaxEdgeTreePrefixWidth(globalLayoutInputs)\n : undefined;\n const globalMaxDirNameWidth =\n globalLayoutInputs.length > 0 ? computeGlobalMaxDirNameWidth(globalLayoutInputs) : undefined;\n\n for (const spaceEntry of scopedSpaces) {\n const space = aggregate.space(spaceEntry.space);\n if (space === undefined) {\n continue;\n }\n const graph = space.graph();\n const spaceContractHash = space.contract().storage.storageHash;\n const targetHash = resolveTarget(spaceContractHash, activeRefHash);\n if (spaceEntry.space === aggregate.app.spaceId) {\n headlineTargetHash = targetHash;\n }\n\n const markerRecord = markersBySpace.get(spaceEntry.space);\n const markerHash = usingFromOverride\n ? fromOverrideHash\n : (markerRecord?.storageHash ?? undefined);\n const originHash = markerHash ?? EMPTY_CONTRACT_HASH;\n const markerInGraph =\n markerHash === undefined || graph.nodes.has(markerHash) || markerHash === spaceContractHash;\n if (\n connected &&\n !usingFromOverride &&\n markerInGraph &&\n originHash !== targetHash &&\n findPath(graph, originHash, targetHash) === null\n ) {\n markerCannotReachTarget = true;\n }\n\n if (connected && !usingFromOverride && markerHash !== undefined && !markerInGraph) {\n markerDiverged = true;\n diagnostics.push({\n code: 'MIGRATION.MARKER_NOT_IN_HISTORY',\n severity: 'warn',\n message:\n 'Database was updated outside the migration system (marker does not match any migration)',\n hints: [\n \"Run 'prisma-next db sign' to overwrite the marker if the database already matches the contract\",\n \"Run 'prisma-next db update' to push the current contract to the database\",\n ],\n });\n }\n\n const ledger = ledgersBySpace.get(spaceEntry.space) ?? [];\n const appliedHashes = showAppliedOverlay ? appliedHashesFromLedger(ledger) : new Set<string>();\n\n const annotations = deriveStatusEdgeAnnotations({\n graph,\n targetHash,\n originHash,\n appliedMigrationHashes: appliedHashes,\n showAppliedOverlay,\n });\n const isAppSpace = spaceEntry.space === aggregate.app.spaceId;\n const tree = renderSpaceTree({\n space,\n liveContractHash: contractHash,\n migrations: spaceEntry.migrations,\n markerHash,\n showDbMarker,\n statusOverlay: annotations,\n colorize,\n glyphMode,\n isAppSpace,\n ...(globalMaxEdgeTreePrefixWidth !== undefined ? { globalMaxEdgeTreePrefixWidth } : {}),\n ...(globalMaxDirNameWidth !== undefined ? { globalMaxDirNameWidth } : {}),\n });\n const migrations = buildStatusMigrations(spaceEntry.migrations, annotations);\n const pending = countPending(migrations);\n totalPending += pending;\n\n statusSpaces.push({\n space: spaceEntry.space,\n currentContract: markerHash ?? null,\n targetContract: targetHash,\n migrations: [...migrations],\n });\n const displayTree =\n showSpaceHeadings && tree.length > 0 ? indentMigrationGraphTreeBlock(tree, ' ') : tree;\n treeSections.push({\n space: spaceEntry.space,\n tree: displayTree,\n showHeading: showSpaceHeadings,\n });\n }\n\n if (connected && requiredInvariants.length > 0) {\n const markerInvariants = markersBySpace.get(aggregate.app.spaceId)?.invariants ?? [];\n const markerSet = new Set(markerInvariants);\n const missing = requiredInvariants.filter((id) => !markerSet.has(id));\n if (missing.length > 0) {\n diagnostics.push({\n code: 'MIGRATION.MISSING_INVARIANTS',\n ...ifDefined('ref', activeRefName),\n invariants: missing,\n message: `missing invariant(s): ${missing.join(', ')}`,\n });\n if (activeRefHash !== undefined) {\n const originHash =\n markersBySpace.get(aggregate.app.spaceId)?.storageHash ?? EMPTY_CONTRACT_HASH;\n const outcome = findPathWithDecision(appGraph, originHash, activeRefHash, {\n ...ifDefined('refName', activeRefName),\n required: new Set(missing),\n });\n if (outcome.kind === 'unsatisfiable') {\n return notOk(\n mapMigrationToolsError(\n errorNoInvariantPath({\n ...ifDefined('refName', activeRefName),\n required: [...missing].sort(),\n missing: outcome.missing,\n structuralPath: outcome.structuralPath.map(toStructuralEdge),\n }),\n ),\n );\n }\n }\n }\n }\n\n const appMarkerHash = markersBySpace.get(aggregate.app.spaceId)?.storageHash;\n const summary = markerCannotReachTarget\n ? buildNoPathSummary({\n markerHash: appMarkerHash,\n targetHash: headlineTargetHash,\n explicitTarget: options.to !== undefined,\n refName: activeRefName,\n })\n : buildStatusHeadline({\n pendingCount: totalPending,\n targetHash: headlineTargetHash,\n markerDiverged,\n markerHash: appMarkerHash,\n });\n\n if (scopedSpaces.every((s) => s.migrations.length === 0)) {\n return ok({\n ok: true,\n spaces: statusSpaces,\n summary: 'No migrations found',\n diagnostics,\n treeSections,\n });\n }\n\n return ok({\n ok: true,\n spaces: statusSpaces,\n summary,\n diagnostics,\n treeSections,\n });\n}\n\nexport function createMigrationStatusCommand(): Command {\n const command = new Command('status');\n setCommandDescriptions(\n command,\n 'Show migration path and pending status',\n 'Shows which migrations are pending between the database marker and\\n' +\n 'the target contract. Requires a database connection.\\n' +\n 'Pass --from for an offline path preview without a database.\\n' +\n 'Use `migration graph` for topology, `migration log` for history,\\n' +\n 'and `migration list` for on-disk enumeration.',\n );\n setCommandExamples(command, [\n 'prisma-next migration status --db $DATABASE_URL',\n 'prisma-next migration status --to production --db $DATABASE_URL',\n 'prisma-next migration status --from sha256:abc --to production',\n 'prisma-next migration status --from sha256:abc --to production --json',\n 'prisma-next migration status --ascii --from sha256:abc --to production',\n 'prisma-next migration status --legend --from sha256:abc --to production',\n ]);\n setCommandSeeAlso(command, [\n { verb: 'migration log', oneLiner: 'Show executed migration history' },\n { verb: 'migration list', oneLiner: 'List on-disk migrations' },\n { verb: 'migration graph', oneLiner: 'Show the migration graph topology' },\n { verb: 'migration show', oneLiner: 'Display migration package contents' },\n ]);\n addGlobalOptions(command)\n .option('--db <url>', 'Database connection string')\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .option('--space <id>', 'Narrow output to a single contract space')\n .option(\n '--to <contract>',\n 'Target contract reference (hash, prefix, ref name, migration dir name, <dir>^, or ./path)',\n )\n .option(\n '--from <contract>',\n 'Origin contract reference; same grammar as --to. Supplying --from switches to offline path computation.',\n )\n .option('--legend', 'Print a key for the tree glyphs and lane colors')\n .option('--ascii', 'Use ASCII glyphs (pipe-friendly)')\n .action(async (options: MigrationStatusOptions) => {\n const flags = parseGlobalFlagsOrExit(options);\n const ui = createTerminalUI(flags);\n\n const legendValidation = validateLegendOptions(options, flags);\n if (!legendValidation.ok) {\n process.exit(handleResult(legendValidation, flags, ui));\n }\n\n const result = await executeMigrationStatusCommand(options, flags, ui);\n\n const exitCode = handleResult(result, flags, ui, (statusResult) => {\n if (flags.json) {\n const jsonResult: MigrationStatusResult = {\n ok: true,\n spaces: [...statusResult.spaces],\n summary: statusResult.summary,\n diagnostics: [...statusResult.diagnostics],\n };\n ui.output(JSON.stringify(jsonResult, null, 2));\n } else if (!flags.quiet) {\n ui.output(formatStatusHumanOutput(statusResult, flags.color !== false));\n }\n });\n\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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"}
|
|
@@ -22,10 +22,9 @@ const InitOutputSchema = type({
|
|
|
22
22
|
filesWritten: "string[]",
|
|
23
23
|
/**
|
|
24
24
|
* FR9.1 — files removed from disk during this run. Populated only on
|
|
25
|
-
* re-init when previously-emitted contract
|
|
26
|
-
* `contract.d.ts`, `
|
|
27
|
-
*
|
|
28
|
-
* green-field init.
|
|
25
|
+
* re-init when previously-emitted contract artifacts (`contract.json`,
|
|
26
|
+
* `contract.d.ts`, `ops.json`, `migration.json`) were left behind by an
|
|
27
|
+
* earlier run. Empty on a green-field init.
|
|
29
28
|
*/
|
|
30
29
|
filesDeleted: "string[]",
|
|
31
30
|
packagesInstalled: {
|
|
@@ -65,7 +64,7 @@ function renderInitOutro(ui, output, flags) {
|
|
|
65
64
|
for (const file of output.filesWritten) lines.push(` • ${file}`);
|
|
66
65
|
if (output.filesDeleted.length > 0) {
|
|
67
66
|
lines.push("");
|
|
68
|
-
lines.push("Files deleted (stale contract
|
|
67
|
+
lines.push("Files deleted (stale contract artifacts):");
|
|
69
68
|
for (const file of output.filesDeleted) lines.push(` • ${file}`);
|
|
70
69
|
}
|
|
71
70
|
if (!output.packagesInstalled.skipped) {
|
|
@@ -107,4 +106,4 @@ function buildNextSteps(options) {
|
|
|
107
106
|
//#endregion
|
|
108
107
|
export { renderInitOutro as i, buildNextSteps as n, formatInitJson as r, InitOutputSchema as t };
|
|
109
108
|
|
|
110
|
-
//# sourceMappingURL=output-
|
|
109
|
+
//# sourceMappingURL=output-DWNYqZGc.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-DWNYqZGc.mjs","names":[],"sources":["../src/commands/init/output.ts"],"sourcesContent":["import { type } from 'arktype';\nimport type { GlobalFlags } from '../../utils/global-flags';\nimport type { TerminalUI } from '../../utils/terminal-ui';\n\n/**\n * arktype schema for the structured success document `init --json` writes\n * to stdout (FR1.5). The same shape backs the human-readable outro\n * renderer (FR10), so the two output modes carry identical information.\n *\n * `target` is normalised to the user-facing flag value (`mongodb` rather\n * than the internal `mongo`) so consumers can round-trip the document\n * straight into a follow-up `--target` invocation.\n *\n * The `ok: true` literal is the documented success/error discriminator —\n * see [Style Guide § JSON Semantics](../../../../../../../docs/CLI%20Style%20Guide.md#json-semantics).\n * Error envelopes (`CliErrorEnvelope`) carry `ok: false` so consumers can\n * branch with `if (doc.ok)` without inspecting the rest of the structure.\n */\nexport const InitOutputSchema = type({\n ok: 'true',\n target: \"'postgres'|'mongodb'\",\n authoring: \"'psl'|'typescript'\",\n schemaPath: 'string',\n filesWritten: 'string[]',\n /**\n * FR9.1 — files removed from disk during this run. Populated only on\n * re-init when previously-emitted contract artifacts (`contract.json`,\n * `contract.d.ts`, `ops.json`, `migration.json`) were left behind by an\n * earlier run. Empty on a green-field init.\n */\n filesDeleted: 'string[]',\n packagesInstalled: {\n skipped: 'boolean',\n deps: 'string[]',\n devDeps: 'string[]',\n },\n contractEmitted: 'boolean',\n nextSteps: 'string[]',\n warnings: 'string[]',\n});\n\nexport type InitOutput = typeof InitOutputSchema.infer;\n\n/**\n * Serialises the output document for `--json`. Sorted keys are not enforced\n * — `JSON.stringify` preserves insertion order, and the schema field order\n * is the documented order, which matches what users will see when they\n * `jq .` the result.\n */\nexport function formatInitJson(output: InitOutput): string {\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Renders the human-readable outro on stderr (FR10.1). Re-uses the same\n * data structure as the JSON output so the two stay in lock-step.\n *\n * Warnings come before \"Next steps\" because they describe state the user\n * needs to be aware of before acting on the next-steps list.\n */\nexport function renderInitOutro(ui: TerminalUI, output: InitOutput, flags: GlobalFlags): void {\n if (flags.quiet || flags.json) {\n return;\n }\n\n for (const warning of output.warnings) {\n ui.warn(warning);\n }\n\n const lines: string[] = [];\n lines.push(`Target: ${output.target}`);\n lines.push(`Authoring: ${output.authoring}`);\n lines.push(`Schema: ${output.schemaPath}`);\n lines.push('');\n lines.push('Files written:');\n for (const file of output.filesWritten) {\n lines.push(` • ${file}`);\n }\n\n if (output.filesDeleted.length > 0) {\n lines.push('');\n lines.push('Files deleted (stale contract artifacts):');\n for (const file of output.filesDeleted) {\n lines.push(` • ${file}`);\n }\n }\n\n if (!output.packagesInstalled.skipped) {\n lines.push('');\n lines.push('Packages installed:');\n for (const dep of output.packagesInstalled.deps) {\n lines.push(` • ${dep}`);\n }\n for (const dep of output.packagesInstalled.devDeps) {\n lines.push(` • ${dep} (dev)`);\n }\n }\n\n lines.push('');\n lines.push('Next steps:');\n for (const step of output.nextSteps) {\n lines.push(` ${step}`);\n }\n\n ui.note(lines.join('\\n'), 'Done');\n}\n\n/**\n * Builds the `nextSteps` array from the resolved scaffold state. Steps are\n * ordered by the workflow a user needs to follow: configure connection →\n * (emit if not yet done) → run a starter query → docs / agent skill.\n *\n * The strings are stable and human-readable; agents wanting to act on them\n * should match on substrings (e.g. \"DATABASE_URL\") rather than exact text,\n * since copy may evolve.\n */\nexport function buildNextSteps(options: {\n readonly target: 'postgres' | 'mongodb';\n readonly contractEmitted: boolean;\n readonly emitCommand: string;\n readonly schemaPath: string;\n /**\n * Whether the project-level Prisma Next skills install actually ran\n * and succeeded during this `init`. When false (the user passed\n * `--no-skill`, so the install was skipped), the\n * \"registered with your agent runtime\" step is omitted — the skip is\n * already surfaced in the warnings array with a manual-install hint.\n */\n readonly skillRegistered: boolean;\n}): string[] {\n const steps: string[] = [];\n let stepNumber = 1;\n const push = (text: string): void => {\n steps.push(`${stepNumber}. ${text}`);\n stepNumber += 1;\n };\n push('Set DATABASE_URL in your environment (export it or add it to .env).');\n if (!options.contractEmitted) {\n push(`Emit the contract: \\`${options.emitCommand}\\``);\n push(`Edit your schema at ${options.schemaPath}, then re-run the emit command.`);\n } else {\n push(`Edit your schema at ${options.schemaPath}, then re-run \\`${options.emitCommand}\\`.`);\n }\n push('Open prisma-next.md for a quick reference on how to write your first typed query.');\n if (options.skillRegistered) {\n push(\n 'Prisma Next skills are registered with your agent runtime — open the project in your IDE and ask the agent to add a model, run a query, or plan a migration.',\n );\n }\n return steps;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAkBA,MAAa,mBAAmB,KAAK;CACnC,IAAI;CACJ,QAAQ;CACR,WAAW;CACX,YAAY;CACZ,cAAc;;;;;;;CAOd,cAAc;CACd,mBAAmB;EACjB,SAAS;EACT,MAAM;EACN,SAAS;CACX;CACA,iBAAiB;CACjB,WAAW;CACX,UAAU;AACZ,CAAC;;;;;;;AAUD,SAAgB,eAAe,QAA4B;CACzD,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;AACvC;;;;;;;;AASA,SAAgB,gBAAgB,IAAgB,QAAoB,OAA0B;CAC5F,IAAI,MAAM,SAAS,MAAM,MACvB;CAGF,KAAK,MAAM,WAAW,OAAO,UAC3B,GAAG,KAAK,OAAO;CAGjB,MAAM,QAAkB,CAAC;CACzB,MAAM,KAAK,cAAc,OAAO,QAAQ;CACxC,MAAM,KAAK,cAAc,OAAO,WAAW;CAC3C,MAAM,KAAK,cAAc,OAAO,YAAY;CAC5C,MAAM,KAAK,EAAE;CACb,MAAM,KAAK,gBAAgB;CAC3B,KAAK,MAAM,QAAQ,OAAO,cACxB,MAAM,KAAK,OAAO,MAAM;CAG1B,IAAI,OAAO,aAAa,SAAS,GAAG;EAClC,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,2CAA2C;EACtD,KAAK,MAAM,QAAQ,OAAO,cACxB,MAAM,KAAK,OAAO,MAAM;CAE5B;CAEA,IAAI,CAAC,OAAO,kBAAkB,SAAS;EACrC,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,qBAAqB;EAChC,KAAK,MAAM,OAAO,OAAO,kBAAkB,MACzC,MAAM,KAAK,OAAO,KAAK;EAEzB,KAAK,MAAM,OAAO,OAAO,kBAAkB,SACzC,MAAM,KAAK,OAAO,IAAI,OAAO;CAEjC;CAEA,MAAM,KAAK,EAAE;CACb,MAAM,KAAK,aAAa;CACxB,KAAK,MAAM,QAAQ,OAAO,WACxB,MAAM,KAAK,KAAK,MAAM;CAGxB,GAAG,KAAK,MAAM,KAAK,IAAI,GAAG,MAAM;AAClC;;;;;;;;;;AAWA,SAAgB,eAAe,SAalB;CACX,MAAM,QAAkB,CAAC;CACzB,IAAI,aAAa;CACjB,MAAM,QAAQ,SAAuB;EACnC,MAAM,KAAK,GAAG,WAAW,IAAI,MAAM;EACnC,cAAc;CAChB;CACA,KAAK,qEAAqE;CAC1E,IAAI,CAAC,QAAQ,iBAAiB;EAC5B,KAAK,wBAAwB,QAAQ,YAAY,GAAG;EACpD,KAAK,uBAAuB,QAAQ,WAAW,gCAAgC;CACjF,OACE,KAAK,uBAAuB,QAAQ,WAAW,kBAAkB,QAAQ,YAAY,IAAI;CAE3F,KAAK,mFAAmF;CACxF,IAAI,QAAQ,iBACV,KACE,8JACF;CAEF,OAAO;AACT"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/cli",
|
|
3
|
-
"version": "0.16.0-dev.
|
|
3
|
+
"version": "0.16.0-dev.5",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -9,18 +9,18 @@
|
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@clack/prompts": "^1.7.0",
|
|
12
|
-
"@prisma-next/config": "0.16.0-dev.
|
|
13
|
-
"@prisma-next/config-loader": "0.16.0-dev.
|
|
14
|
-
"@prisma-next/contract": "0.16.0-dev.
|
|
15
|
-
"@prisma-next/emitter": "0.16.0-dev.
|
|
16
|
-
"@prisma-next/errors": "0.16.0-dev.
|
|
17
|
-
"@prisma-next/framework-components": "0.16.0-dev.
|
|
18
|
-
"@prisma-next/language-server": "0.16.0-dev.
|
|
19
|
-
"@prisma-next/migration-tools": "0.16.0-dev.
|
|
20
|
-
"@prisma-next/psl-parser": "0.16.0-dev.
|
|
21
|
-
"@prisma-next/psl-printer": "0.16.0-dev.
|
|
22
|
-
"@prisma-next/cli-telemetry": "0.16.0-dev.
|
|
23
|
-
"@prisma-next/utils": "0.16.0-dev.
|
|
12
|
+
"@prisma-next/config": "0.16.0-dev.5",
|
|
13
|
+
"@prisma-next/config-loader": "0.16.0-dev.5",
|
|
14
|
+
"@prisma-next/contract": "0.16.0-dev.5",
|
|
15
|
+
"@prisma-next/emitter": "0.16.0-dev.5",
|
|
16
|
+
"@prisma-next/errors": "0.16.0-dev.5",
|
|
17
|
+
"@prisma-next/framework-components": "0.16.0-dev.5",
|
|
18
|
+
"@prisma-next/language-server": "0.16.0-dev.5",
|
|
19
|
+
"@prisma-next/migration-tools": "0.16.0-dev.5",
|
|
20
|
+
"@prisma-next/psl-parser": "0.16.0-dev.5",
|
|
21
|
+
"@prisma-next/psl-printer": "0.16.0-dev.5",
|
|
22
|
+
"@prisma-next/cli-telemetry": "0.16.0-dev.5",
|
|
23
|
+
"@prisma-next/utils": "0.16.0-dev.5",
|
|
24
24
|
"arktype": "^2.2.2",
|
|
25
25
|
"ci-info": "^4.3.1",
|
|
26
26
|
"clipanion": "4.0.0-rc.4",
|
|
@@ -36,14 +36,14 @@
|
|
|
36
36
|
"wrap-ansi": "^10.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@prisma-next/sql-contract": "0.16.0-dev.
|
|
40
|
-
"@prisma-next/sql-contract-emitter": "0.16.0-dev.
|
|
41
|
-
"@prisma-next/sql-contract-ts": "0.16.0-dev.
|
|
42
|
-
"@prisma-next/sql-operations": "0.16.0-dev.
|
|
43
|
-
"@prisma-next/sql-runtime": "0.16.0-dev.
|
|
44
|
-
"@prisma-next/test-utils": "0.16.0-dev.
|
|
45
|
-
"@prisma-next/tsconfig": "0.16.0-dev.
|
|
46
|
-
"@prisma-next/tsdown": "0.16.0-dev.
|
|
39
|
+
"@prisma-next/sql-contract": "0.16.0-dev.5",
|
|
40
|
+
"@prisma-next/sql-contract-emitter": "0.16.0-dev.5",
|
|
41
|
+
"@prisma-next/sql-contract-ts": "0.16.0-dev.5",
|
|
42
|
+
"@prisma-next/sql-operations": "0.16.0-dev.5",
|
|
43
|
+
"@prisma-next/sql-runtime": "0.16.0-dev.5",
|
|
44
|
+
"@prisma-next/test-utils": "0.16.0-dev.5",
|
|
45
|
+
"@prisma-next/tsconfig": "0.16.0-dev.5",
|
|
46
|
+
"@prisma-next/tsdown": "0.16.0-dev.5",
|
|
47
47
|
"@types/node": "25.9.4",
|
|
48
48
|
"tsdown": "0.22.8",
|
|
49
49
|
"typescript": "5.9.3",
|
package/src/commands/db-sign.ts
CHANGED
|
@@ -4,11 +4,12 @@ import type {
|
|
|
4
4
|
SignDatabaseResult,
|
|
5
5
|
VerifyDatabaseSchemaResult,
|
|
6
6
|
} from '@prisma-next/framework-components/control';
|
|
7
|
+
import { readContractSnapshotJson } from '@prisma-next/migration-tools/contract-snapshot-store';
|
|
7
8
|
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
8
9
|
import { parseContractRef } from '@prisma-next/migration-tools/ref-resolution';
|
|
9
10
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
10
11
|
import { Command } from 'commander';
|
|
11
|
-
import {
|
|
12
|
+
import { relative, resolve } from 'pathe';
|
|
12
13
|
import { createControlClient } from '../control-api/client';
|
|
13
14
|
import { ContractValidationError } from '../control-api/errors';
|
|
14
15
|
import {
|
|
@@ -113,9 +114,10 @@ async function executeDbSignCommand(
|
|
|
113
114
|
const targetHash = refResult.value.hash;
|
|
114
115
|
const matchingBundle = bundles.find((p) => p.metadata.to === targetHash);
|
|
115
116
|
if (matchingBundle) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
contractJson = (await readContractSnapshotJson(migrationsDir, targetHash)) as Record<
|
|
118
|
+
string,
|
|
119
|
+
unknown
|
|
120
|
+
>;
|
|
119
121
|
} else {
|
|
120
122
|
const defaultRaw = await readFile(contractPathAbsolute, 'utf-8');
|
|
121
123
|
const defaultContract = JSON.parse(defaultRaw) as Record<string, unknown>;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
contractSnapshotDir,
|
|
3
|
+
readContractSnapshotJson,
|
|
4
|
+
} from '@prisma-next/migration-tools/contract-snapshot-store';
|
|
2
5
|
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
3
6
|
import { parseContractRef } from '@prisma-next/migration-tools/ref-resolution';
|
|
4
7
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
@@ -135,16 +138,20 @@ async function executeDbUpdateCommand(
|
|
|
135
138
|
errorUnexpected(
|
|
136
139
|
`No migration bundle found for --to "${options.to}" (resolved hash: ${targetHash})`,
|
|
137
140
|
{
|
|
138
|
-
why: `The ref resolved successfully but no on-disk migration package has
|
|
141
|
+
why: `The ref resolved successfully but no on-disk migration package has a destination (\`to\`) hash matching ${targetHash}.`,
|
|
139
142
|
fix: 'Provide a ref or hash that corresponds to an existing migration package, or run `migration list` to see available migrations.',
|
|
140
143
|
},
|
|
141
144
|
),
|
|
142
145
|
);
|
|
143
146
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
147
|
+
contractJson = (await readContractSnapshotJson(migrationsDir, targetHash)) as Record<
|
|
148
|
+
string,
|
|
149
|
+
unknown
|
|
150
|
+
>;
|
|
151
|
+
contractJsonPathForSnapshot = join(
|
|
152
|
+
contractSnapshotDir(migrationsDir, targetHash),
|
|
153
|
+
'contract.json',
|
|
154
|
+
);
|
|
148
155
|
} catch (error) {
|
|
149
156
|
if (MigrationToolsError.is(error)) {
|
|
150
157
|
return notOk(mapMigrationToolsError(error));
|
|
@@ -1,47 +1,60 @@
|
|
|
1
1
|
import type { TargetId } from './templates/code-templates';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* The `.gitattributes` entries written for a freshly initialised project
|
|
5
|
+
* (FR3.4). Mirrors the relevant subset of the repo-root
|
|
6
|
+
* [`.gitattributes`](../../../../../../../../.gitattributes):
|
|
7
7
|
*
|
|
8
8
|
* - **Today**: `contract.json`, `contract.d.ts` are emitted on every
|
|
9
9
|
* `prisma-next contract emit`. Marking them `linguist-generated`
|
|
10
10
|
* keeps GitHub's diff stats honest and collapses the file in code
|
|
11
11
|
* review by default.
|
|
12
|
-
* - **Forward-looking**: `
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* - **Forward-looking**: `ops.json`, `migration.json` are not yet emitted
|
|
13
|
+
* by `init` flows but will be produced by adjacent commands (lower /
|
|
14
|
+
* migration tooling). Adding them now matches Decision 5
|
|
15
|
+
* (forward-looking subset) so the file does not need to be amended
|
|
16
|
+
* every time a new artifact lands.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
* who runs `init --schema-path db/contract.prisma`
|
|
20
|
-
* `db/contract.json linguist-generated` — not the workspace-glob
|
|
21
|
-
* `<glob>/contract.json` (which would over-match any unrelated
|
|
18
|
+
* `ARTIFACT_FILENAMES` entries are written relative to the schema
|
|
19
|
+
* directory so a user who runs `init --schema-path db/contract.prisma`
|
|
20
|
+
* gets `db/contract.json linguist-generated` — not the workspace-glob
|
|
21
|
+
* form `<glob>/contract.json` (which would over-match any unrelated
|
|
22
22
|
* `contract.json` the user has elsewhere) and not the absolute
|
|
23
|
-
* `DEFAULT_CONTRACT_SOURCE_DIR/contract.json` (which would silently
|
|
24
|
-
* schema path).
|
|
23
|
+
* `DEFAULT_CONTRACT_SOURCE_DIR/contract.json` (which would silently
|
|
24
|
+
* break for a non-default schema path).
|
|
25
|
+
*
|
|
26
|
+
* The migration contract snapshot store (`migrations/snapshots/<hex>/`)
|
|
27
|
+
* is anchored to the migrations root instead, not the schema directory:
|
|
28
|
+
* migration package depth under `migrations/` varies (`app/<pkg>`,
|
|
29
|
+
* `<space>/<pkg>`, or a bare `<pkg>` in extension source repos), so no
|
|
30
|
+
* single schema-dir-relative pattern can reach every snapshot. See
|
|
31
|
+
* `STORE_GITATTRIBUTES_LINES` below.
|
|
25
32
|
*/
|
|
26
|
-
const
|
|
33
|
+
const ARTIFACT_FILENAMES: readonly string[] = [
|
|
27
34
|
'contract.json',
|
|
28
35
|
'contract.d.ts',
|
|
29
|
-
'end-contract.json',
|
|
30
|
-
'end-contract.d.ts',
|
|
31
|
-
'start-contract.json',
|
|
32
|
-
'start-contract.d.ts',
|
|
33
36
|
'ops.json',
|
|
34
37
|
'migration.json',
|
|
35
38
|
];
|
|
36
39
|
|
|
37
40
|
const ATTRIBUTE = 'linguist-generated';
|
|
38
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Full `.gitattributes` lines for the migration contract snapshot store,
|
|
44
|
+
* already anchored to the migrations root — unlike `ARTIFACT_FILENAMES`,
|
|
45
|
+
* these are not combined with the schema-relative prefix.
|
|
46
|
+
*/
|
|
47
|
+
const STORE_GITATTRIBUTES_LINES: readonly string[] = [
|
|
48
|
+
`migrations/snapshots/**/contract.json ${ATTRIBUTE}`,
|
|
49
|
+
`migrations/snapshots/**/contract.d.ts ${ATTRIBUTE}`,
|
|
50
|
+
];
|
|
51
|
+
|
|
39
52
|
/**
|
|
40
53
|
* Computes the `.gitattributes` lines this scaffold expects to own. Each
|
|
41
54
|
* line has the shape `<path> linguist-generated`. The `target` parameter
|
|
42
55
|
* is currently unused but accepted for symmetry with the other hygiene
|
|
43
56
|
* helpers and to leave room for target-specific entries (e.g. a future
|
|
44
|
-
*
|
|
57
|
+
* family-specific artifact) without a signature break.
|
|
45
58
|
*/
|
|
46
59
|
export function requiredGitattributesLines(
|
|
47
60
|
schemaDir: string,
|
|
@@ -49,7 +62,10 @@ export function requiredGitattributesLines(
|
|
|
49
62
|
): readonly string[] {
|
|
50
63
|
const dir = schemaDir === '.' ? '' : schemaDir.replace(/\/+$/, '');
|
|
51
64
|
const prefix = dir === '' ? '' : `${dir}/`;
|
|
52
|
-
return
|
|
65
|
+
return [
|
|
66
|
+
...ARTIFACT_FILENAMES.map((file) => `${prefix}${file} ${ATTRIBUTE}`),
|
|
67
|
+
...STORE_GITATTRIBUTES_LINES,
|
|
68
|
+
];
|
|
53
69
|
}
|
|
54
70
|
|
|
55
71
|
/**
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
renderInitOutro,
|
|
49
49
|
} from './output';
|
|
50
50
|
import { type ProbeOutcome, type ProbeOverrides, probeServerVersion } from './probe-db';
|
|
51
|
-
import {
|
|
51
|
+
import { findStaleArtifacts, removeDependency } from './reinit-cleanup';
|
|
52
52
|
import {
|
|
53
53
|
DEFAULT_SKILL_SOURCES,
|
|
54
54
|
formatSkillInstallCommand,
|
|
@@ -175,14 +175,14 @@ export async function runInit(
|
|
|
175
175
|
{ path: '.env.example', content: envExampleContent(inputs.target) },
|
|
176
176
|
];
|
|
177
177
|
|
|
178
|
-
// FR9.1 — on re-init, queue the previously-emitted contract
|
|
178
|
+
// FR9.1 — on re-init, queue the previously-emitted contract artifacts
|
|
179
179
|
// for deletion so a target switch (or schema-shape change) does not
|
|
180
180
|
// leave a stale `contract.json` / `contract.d.ts` next to the new
|
|
181
181
|
// schema source. Detection is filesystem-only (no parsing of the
|
|
182
182
|
// previous config) so the cleanup is safe to run before the write
|
|
183
183
|
// phase: each path is checked for existence in the precondition,
|
|
184
184
|
// and missing-on-disk-at-write-time is tolerated.
|
|
185
|
-
const filesToDelete: string[] = inputs.reinit ? [...
|
|
185
|
+
const filesToDelete: string[] = inputs.reinit ? [...findStaleArtifacts(baseDir, schemaDir)] : [];
|
|
186
186
|
|
|
187
187
|
// `init` delegates the skill to `npx skills add prisma/prisma-next#v<version>`,
|
|
188
188
|
// so a hand-rolled `.agents/skills/prisma-next/SKILL.md` in the project
|
|
@@ -245,7 +245,7 @@ export async function runInit(
|
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
// FR3.4: idempotent .gitattributes — linguist-generated entries for
|
|
248
|
-
// the emitted
|
|
248
|
+
// the emitted artifacts so GitHub diff stats / code review collapse
|
|
249
249
|
// them by default.
|
|
250
250
|
const gitattributesPath = join(baseDir, '.gitattributes');
|
|
251
251
|
const existingGitattributes = existsSync(gitattributesPath)
|
|
@@ -373,7 +373,7 @@ export async function runInit(
|
|
|
373
373
|
}
|
|
374
374
|
}
|
|
375
375
|
|
|
376
|
-
// FR9.1 — delete stale
|
|
376
|
+
// FR9.1 — delete stale artifacts after the new templates are written.
|
|
377
377
|
// Order is intentional: the names do not collide with `filesToWrite`
|
|
378
378
|
// (we never write `contract.json` from this command — that's `contract
|
|
379
379
|
// emit`'s job), so deletion *after* the writes guarantees we never
|
|
@@ -847,7 +847,7 @@ function formatCatalogWarning(
|
|
|
847
847
|
/**
|
|
848
848
|
* Recognised pnpm error signatures that justify a fallback to npm.
|
|
849
849
|
*
|
|
850
|
-
* These patterns indicate the published
|
|
850
|
+
* These patterns indicate the published artifact itself is at fault
|
|
851
851
|
* (a leaked `workspace:*` or `catalog:` specifier), not the user's
|
|
852
852
|
* environment — pnpm is faithfully reporting "I cannot resolve this
|
|
853
853
|
* registry version", and npm is willing to install it because npm
|
|
@@ -24,10 +24,9 @@ export const InitOutputSchema = type({
|
|
|
24
24
|
filesWritten: 'string[]',
|
|
25
25
|
/**
|
|
26
26
|
* FR9.1 — files removed from disk during this run. Populated only on
|
|
27
|
-
* re-init when previously-emitted contract
|
|
28
|
-
* `contract.d.ts`, `
|
|
29
|
-
*
|
|
30
|
-
* green-field init.
|
|
27
|
+
* re-init when previously-emitted contract artifacts (`contract.json`,
|
|
28
|
+
* `contract.d.ts`, `ops.json`, `migration.json`) were left behind by an
|
|
29
|
+
* earlier run. Empty on a green-field init.
|
|
31
30
|
*/
|
|
32
31
|
filesDeleted: 'string[]',
|
|
33
32
|
packagesInstalled: {
|
|
@@ -80,7 +79,7 @@ export function renderInitOutro(ui: TerminalUI, output: InitOutput, flags: Globa
|
|
|
80
79
|
|
|
81
80
|
if (output.filesDeleted.length > 0) {
|
|
82
81
|
lines.push('');
|
|
83
|
-
lines.push('Files deleted (stale contract
|
|
82
|
+
lines.push('Files deleted (stale contract artifacts):');
|
|
84
83
|
for (const file of output.filesDeleted) {
|
|
85
84
|
lines.push(` • ${file}`);
|
|
86
85
|
}
|
|
@@ -4,41 +4,40 @@ import { join } from 'pathe';
|
|
|
4
4
|
/**
|
|
5
5
|
* Filenames the contract pipeline emits next to the user's schema source
|
|
6
6
|
* (`<schemaDir>/contract.json`, `<schemaDir>/contract.d.ts`, …). Mirrors
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* the schema-dir-relative `ARTIFACT_FILENAMES` in `hygiene-gitattributes.ts`
|
|
8
|
+
* (not that file's migrations-root-anchored store lines, which are outside
|
|
9
|
+
* this command's scope — reinit only ever touches `schemaDir`); kept as a
|
|
10
|
+
* separate constant here because the cleanup contract is target-agnostic
|
|
11
|
+
* and we deliberately do not want a stale artifact from a previous target
|
|
12
|
+
* lingering after a re-init.
|
|
11
13
|
*
|
|
12
|
-
* If a future emit pipeline produces an additional
|
|
13
|
-
* **and** to
|
|
14
|
-
*
|
|
15
|
-
* `init` is willing to delete on
|
|
14
|
+
* If a future emit pipeline produces an additional schema-dir artifact,
|
|
15
|
+
* add it here **and** to `ARTIFACT_FILENAMES` in `hygiene-gitattributes.ts`
|
|
16
|
+
* — the two stay in lockstep so the file `init` advertises as
|
|
17
|
+
* `linguist-generated` is exactly the file `init` is willing to delete on
|
|
18
|
+
* re-init.
|
|
16
19
|
*/
|
|
17
|
-
const
|
|
20
|
+
const ARTIFACT_FILENAMES: readonly string[] = [
|
|
18
21
|
'contract.json',
|
|
19
22
|
'contract.d.ts',
|
|
20
|
-
'end-contract.json',
|
|
21
|
-
'end-contract.d.ts',
|
|
22
|
-
'start-contract.json',
|
|
23
|
-
'start-contract.d.ts',
|
|
24
23
|
'ops.json',
|
|
25
24
|
'migration.json',
|
|
26
25
|
];
|
|
27
26
|
|
|
28
27
|
/**
|
|
29
|
-
* Returns the schema-relative paths of stale contract
|
|
28
|
+
* Returns the schema-relative paths of stale contract artifacts the
|
|
30
29
|
* previous `init` run (or a `contract emit`) left behind in `schemaDir`.
|
|
31
30
|
* Paths are returned relative to `baseDir` so the caller can plumb them
|
|
32
31
|
* into `filesWritten`-style logging without re-deriving the path.
|
|
33
32
|
*
|
|
34
33
|
* Pure function: no filesystem mutation. Used by `runInit`'s precondition
|
|
35
34
|
* phase (FR6.2 / NFR3 atomicity) so a downstream parse failure leaves
|
|
36
|
-
* the
|
|
35
|
+
* the artifacts on disk and the project byte-identical to its pre-init
|
|
37
36
|
* state.
|
|
38
37
|
*/
|
|
39
|
-
export function
|
|
38
|
+
export function findStaleArtifacts(baseDir: string, schemaDir: string): readonly string[] {
|
|
40
39
|
const result: string[] = [];
|
|
41
|
-
for (const filename of
|
|
40
|
+
for (const filename of ARTIFACT_FILENAMES) {
|
|
42
41
|
const rel = join(schemaDir, filename);
|
|
43
42
|
if (existsSync(join(baseDir, rel))) {
|
|
44
43
|
result.push(rel);
|