prisma-next 0.11.0-dev.69 → 0.11.0-dev.70
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.mjs +5 -5
- package/dist/commands/migrate.d.mts +1 -1
- package/dist/commands/migration-graph.d.mts +6 -4
- package/dist/commands/migration-graph.d.mts.map +1 -1
- package/dist/commands/migration-graph.mjs +1 -121
- package/dist/commands/migration-list.d.mts +4 -3
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +1 -1
- package/dist/commands/migration-log.d.mts +3 -3
- package/dist/commands/migration-plan.d.mts +1 -1
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-show.d.mts +1 -1
- package/dist/commands/migration-status.d.mts +1 -1
- package/dist/commands/ref.d.mts +1 -1
- package/dist/exports/control-api.d.mts +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{global-flags-2SPgqWma.d.mts → global-flags-DG4uY5tV.d.mts} +1 -1
- package/dist/{global-flags-2SPgqWma.d.mts.map → global-flags-DG4uY5tV.d.mts.map} +1 -1
- package/dist/{init-BQNpPozW.mjs → init-DE-phHWK.mjs} +2 -2
- package/dist/{init-BQNpPozW.mjs.map → init-DE-phHWK.mjs.map} +1 -1
- package/dist/migration-graph-kPluRdF2.mjs +1232 -0
- package/dist/migration-graph-kPluRdF2.mjs.map +1 -0
- package/dist/{migration-list-A3bJ4j5e.mjs → migration-list-CE35R5Ag.mjs} +2 -277
- package/dist/migration-list-CE35R5Ag.mjs.map +1 -0
- package/dist/migration-list-styler-DeAwACt3.mjs +402 -0
- package/dist/migration-list-styler-DeAwACt3.mjs.map +1 -0
- package/dist/{migration-plan-ZZm8C0s-.mjs → migration-plan-DHLa2Khm.mjs} +1 -1
- package/dist/{migration-plan-ZZm8C0s-.mjs.map → migration-plan-DHLa2Khm.mjs.map} +1 -1
- package/dist/{migration-types-q64xAI_J.d.mts → migration-types-CAQ-0TEE.d.mts} +1 -1
- package/dist/{migration-types-q64xAI_J.d.mts.map → migration-types-CAQ-0TEE.d.mts.map} +1 -1
- package/dist/{output-DEg3SSnJ.mjs → output-CF_hqzI-.mjs} +1 -1
- package/dist/{output-DEg3SSnJ.mjs.map → output-CF_hqzI-.mjs.map} +1 -1
- package/dist/{terminal-ui-sLZt2cxc.d.mts → terminal-ui-BbtqsQYY.d.mts} +1 -1
- package/dist/{terminal-ui-sLZt2cxc.d.mts.map → terminal-ui-BbtqsQYY.d.mts.map} +1 -1
- package/dist/{types-BNqTw3ek.d.mts → types-Ci7TndCS.d.mts} +1 -1
- package/dist/{types-BNqTw3ek.d.mts.map → types-Ci7TndCS.d.mts.map} +1 -1
- package/package.json +11 -11
- package/dist/commands/migration-graph.mjs.map +0 -1
- package/dist/migration-list-A3bJ4j5e.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-list-CE35R5Ag.mjs","names":[],"sources":["../src/utils/formatters/migration-list-graph-layout.ts","../src/utils/formatters/migration-list-graph-render.ts","../src/commands/migration-list.ts"],"sourcesContent":["import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';\nimport {\n classifyMigrationListGraphTopology,\n type MigrationEdgeKind,\n type MigrationListGraphTopology,\n} from './migration-list-graph-topology';\nimport type { MigrationListEntry } from './migration-list-types';\n\nexport type ConnectorKind = 'fanBelow' | 'joinAbove';\n\nexport interface MigrationLayoutRow {\n readonly kind: 'migration';\n readonly entry: MigrationListEntry;\n readonly edgeKind: MigrationEdgeKind;\n readonly laneIndex: number;\n readonly passThroughLanes: readonly number[];\n readonly woven: boolean;\n}\n\nexport interface NodeLineLayoutRow {\n readonly kind: 'nodeLine';\n readonly contractHash: string;\n readonly laneIndex: number;\n}\n\nexport interface ConnectorLayoutRow {\n readonly kind: 'connector';\n readonly connectorKind: ConnectorKind;\n readonly contractHash: string;\n readonly startLane: number;\n readonly endLane: number;\n readonly branchCount: number;\n}\n\nexport type LayoutRow = MigrationLayoutRow | NodeLineLayoutRow | ConnectorLayoutRow;\n\nexport interface MigrationListGraphLayout {\n readonly rows: readonly LayoutRow[];\n}\n\ninterface LaneState {\n want: string;\n active: boolean;\n}\n\nfunction canonicalFrom(from: string | null): string {\n return from ?? EMPTY_CONTRACT_HASH;\n}\n\nfunction forwardInDegree(topology: MigrationListGraphTopology, hash: string): number {\n return topology.forwardInDegree.get(hash) ?? 0;\n}\n\nfunction forwardOutDegree(topology: MigrationListGraphTopology, hash: string): number {\n return topology.forwardOutDegree.get(hash) ?? 0;\n}\n\nfunction buildForwardProducersByTo(\n entries: readonly MigrationListEntry[],\n kindByMigrationHash: ReadonlyMap<string, MigrationEdgeKind>,\n): Map<string, MigrationListEntry[]> {\n const byTo = new Map<string, MigrationListEntry[]>();\n for (const entry of entries) {\n if (kindByMigrationHash.get(entry.migrationHash) !== 'forward') continue;\n const bucket = byTo.get(entry.to);\n if (bucket) bucket.push(entry);\n else byTo.set(entry.to, [entry]);\n }\n return byTo;\n}\n\nfunction countForwardProducersTo(\n forwardProducersByTo: Map<string, MigrationListEntry[]>,\n contract: string,\n): number {\n return forwardProducersByTo.get(contract)?.length ?? 0;\n}\n\nfunction hasLaterForwardDepartingFrom(\n entries: readonly MigrationListEntry[],\n startIndex: number,\n contract: string,\n kindByMigrationHash: ReadonlyMap<string, MigrationEdgeKind>,\n): boolean {\n for (let index = startIndex + 1; index < entries.length; index++) {\n const later = entries[index];\n if (later === undefined) continue;\n if (kindByMigrationHash.get(later.migrationHash) !== 'forward') continue;\n if (canonicalFrom(later.from) === contract) return true;\n }\n return false;\n}\n\nexport function computeMigrationListGraphLayout(\n entries: readonly MigrationListEntry[],\n topology: MigrationListGraphTopology = classifyMigrationListGraphTopology(entries),\n): MigrationListGraphLayout {\n const { kindByMigrationHash } = topology;\n const forwardProducersByTo = buildForwardProducersByTo(entries, kindByMigrationHash);\n const convergencesEmitted = new Set<string>();\n const producerLaneByHash = new Map<string, number>();\n const lanes: LaneState[] = [];\n const rows: LayoutRow[] = [];\n\n function emitNodeLine(contractHash: string): void {\n rows.push({ kind: 'nodeLine', contractHash, laneIndex: 0 });\n }\n\n function emitConnector(\n connectorKind: ConnectorKind,\n contractHash: string,\n startLane: number,\n endLane: number,\n branchCount: number,\n ): void {\n rows.push({\n kind: 'connector',\n connectorKind,\n contractHash,\n startLane,\n endLane,\n branchCount,\n });\n }\n\n function activeLaneIndices(): number[] {\n const indices: number[] = [];\n for (let index = 0; index < lanes.length; index++) {\n if (lanes[index]?.active) indices.push(index);\n }\n return indices;\n }\n\n function lanesWanting(contract: string): number[] {\n const indices: number[] = [];\n for (let index = 0; index < lanes.length; index++) {\n const lane = lanes[index];\n if (lane?.active && lane.want === contract) indices.push(index);\n }\n return indices;\n }\n\n function ensureLane(index: number): void {\n while (lanes.length <= index) {\n lanes.push({ want: '', active: false });\n }\n }\n\n function openLaneAtRight(want: string): number {\n const index = lanes.length;\n lanes.push({ want, active: true });\n return index;\n }\n\n function closeLane(index: number): void {\n ensureLane(index);\n const lane = lanes[index];\n if (lane) lane.active = false;\n }\n\n function emitJoinAbove(contractHash: string, laneIndices: readonly number[]): void {\n if (laneIndices.length < 2) return;\n const startLane = Math.min(...laneIndices);\n const endLane = Math.max(...laneIndices);\n emitConnector('joinAbove', contractHash, startLane, endLane, laneIndices.length);\n for (const index of laneIndices) {\n if (index !== startLane) closeLane(index);\n }\n }\n\n function emitConvergencePreamble(contract: string): void {\n if (convergencesEmitted.has(contract)) return;\n if (forwardInDegree(topology, contract) < 2) return;\n\n const consumersWanting = lanesWanting(contract);\n if (forwardOutDegree(topology, contract) >= 2 && consumersWanting.length >= 2) {\n emitJoinAbove(contract, consumersWanting);\n }\n\n emitNodeLine(contract);\n const producers = forwardProducersByTo.get(contract) ?? [];\n if (producers.length >= 2) {\n emitConnector('fanBelow', contract, 0, producers.length - 1, producers.length);\n }\n\n for (const [producerIndex, producer] of producers.entries()) {\n ensureLane(producerIndex);\n lanes[producerIndex] = { want: canonicalFrom(producer.from), active: true };\n producerLaneByHash.set(producer.migrationHash, producerIndex);\n }\n\n convergencesEmitted.add(contract);\n }\n\n function placeWoven(\n entry: MigrationListEntry,\n edgeKind: MigrationEdgeKind,\n laneIndex: number,\n ): void {\n const passThroughLanes = activeLaneIndices().filter((index) => index !== laneIndex);\n rows.push({\n kind: 'migration',\n entry,\n edgeKind,\n laneIndex,\n passThroughLanes,\n woven: true,\n });\n ensureLane(laneIndex);\n lanes[laneIndex] = { want: canonicalFrom(entry.from), active: true };\n }\n\n function placeUnwoven(entry: MigrationListEntry, edgeKind: MigrationEdgeKind): void {\n const passThroughLanes = activeLaneIndices();\n const laneIndex = passThroughLanes.length === 0 ? 0 : Math.max(...passThroughLanes) + 1;\n rows.push({\n kind: 'migration',\n entry,\n edgeKind,\n laneIndex,\n passThroughLanes,\n woven: false,\n });\n }\n\n for (let entryIndex = 0; entryIndex < entries.length; entryIndex++) {\n const entry = entries[entryIndex]!;\n const edgeKind = kindByMigrationHash.get(entry.migrationHash) ?? 'forward';\n const to = entry.to;\n\n if (edgeKind !== 'forward') {\n placeUnwoven(entry, edgeKind);\n continue;\n }\n\n if (forwardInDegree(topology, to) >= 2 && !convergencesEmitted.has(to)) {\n emitConvergencePreamble(to);\n }\n\n const presetLane = producerLaneByHash.get(entry.migrationHash);\n const wantingTo = lanesWanting(to);\n\n if (wantingTo.length >= 2 && countForwardProducersTo(forwardProducersByTo, to) === 1) {\n emitJoinAbove(to, wantingTo);\n }\n\n if (presetLane !== undefined) {\n placeWoven(entry, edgeKind, presetLane);\n continue;\n }\n\n const firstWanting = wantingTo[0];\n if (firstWanting !== undefined) {\n placeWoven(entry, edgeKind, firstWanting);\n continue;\n }\n\n if (hasLaterForwardDepartingFrom(entries, entryIndex, to, kindByMigrationHash)) {\n placeUnwoven(entry, edgeKind);\n continue;\n }\n\n const tipLaneIndex = openLaneAtRight(canonicalFrom(entry.from));\n placeWoven(entry, edgeKind, tipLaneIndex);\n }\n\n return { rows };\n}\n","import type { GlyphMode } from '../glyph-mode';\nimport {\n abbreviateContractHash,\n computeMigrationDirNameWidth,\n formatMigrationDataColumn,\n formatNodeLineDataColumn,\n MIGRATION_LIST_ASCII_KIND_GLYPH,\n MIGRATION_LIST_UNICODE_KIND_GLYPH,\n migrationListEmptySource,\n migrationListForwardArrow,\n} from './migration-list-data-column';\nimport type {\n ConnectorLayoutRow,\n LayoutRow,\n MigrationLayoutRow,\n MigrationListGraphLayout,\n NodeLineLayoutRow,\n} from './migration-list-graph-layout';\nimport { computeMigrationListGraphLayout } from './migration-list-graph-layout';\nimport type { MigrationListGraphTopology } from './migration-list-graph-topology';\nimport type { MigrationListStyler } from './migration-list-render';\nimport type { MigrationListEntry, MigrationListResult } from './migration-list-types';\n\nexport type { GlyphMode } from '../glyph-mode';\n\ninterface GlyphPalette {\n readonly lane: string;\n readonly node: string;\n readonly forwardArrow: string;\n readonly emptySource: string;\n readonly kind: typeof MIGRATION_LIST_UNICODE_KIND_GLYPH;\n readonly fanBelow: (branchCount: number) => string;\n readonly joinAbove: (branchCount: number) => string;\n}\n\nconst UNICODE_PALETTE: GlyphPalette = {\n lane: '│',\n node: 'o',\n forwardArrow: migrationListForwardArrow('unicode'),\n emptySource: migrationListEmptySource('unicode'),\n kind: MIGRATION_LIST_UNICODE_KIND_GLYPH,\n fanBelow: (branchCount) => (branchCount === 2 ? '├─┐' : '├─┬─┐'),\n joinAbove: (branchCount) => (branchCount === 2 ? '├─┘' : '└─┴─┘'),\n};\n\nconst ASCII_PALETTE: GlyphPalette = {\n lane: '|',\n node: 'o',\n forwardArrow: migrationListForwardArrow('ascii'),\n emptySource: migrationListEmptySource('ascii'),\n kind: MIGRATION_LIST_ASCII_KIND_GLYPH,\n fanBelow: (branchCount) => (branchCount === 2 ? '+-\\\\' : '+-|-\\\\'),\n joinAbove: (branchCount) => (branchCount === 2 ? '+-/' : '/-+-/'),\n};\n\nfunction paletteFor(mode: GlyphMode): GlyphPalette {\n return mode === 'ascii' ? ASCII_PALETTE : UNICODE_PALETTE;\n}\n\nfunction migrationEntries(layout: MigrationListGraphLayout): MigrationListEntry[] {\n const entries: MigrationListEntry[] = [];\n for (const row of layout.rows) {\n if (row.kind === 'migration') entries.push(row.entry);\n }\n return entries;\n}\n\nfunction layoutMaxLaneIndex(layout: MigrationListGraphLayout): number {\n let max = 0;\n for (const row of layout.rows) {\n if (row.kind === 'migration') {\n max = Math.max(max, row.laneIndex, ...row.passThroughLanes);\n } else if (row.kind === 'connector') {\n max = Math.max(max, row.endLane);\n } else {\n max = Math.max(max, row.laneIndex);\n }\n }\n return max;\n}\n\nfunction laneCell(glyph: string): string {\n return `${glyph} `;\n}\n\nfunction emptyLaneCell(): string {\n return ' ';\n}\n\nfunction renderMigrationGutter(\n row: MigrationLayoutRow,\n maxLane: number,\n palette: GlyphPalette,\n style: MigrationListStyler,\n): string {\n const cells: string[] = [];\n for (let lane = 0; lane <= maxLane; lane++) {\n if (lane === row.laneIndex) {\n cells.push(laneCell(style.kind(palette.kind[row.edgeKind])));\n } else if (row.passThroughLanes.includes(lane)) {\n cells.push(laneCell(style.lane(palette.lane)));\n } else {\n cells.push(emptyLaneCell());\n }\n }\n return cells.join('');\n}\n\nfunction renderNodeLineGutter(\n row: NodeLineLayoutRow,\n openLanes: ReadonlySet<number>,\n maxLane: number,\n palette: GlyphPalette,\n style: MigrationListStyler,\n): string {\n const cells: string[] = [];\n for (let lane = 0; lane <= maxLane; lane++) {\n if (lane === row.laneIndex) {\n cells.push(laneCell(palette.node));\n } else if (openLanes.has(lane)) {\n cells.push(laneCell(style.lane(palette.lane)));\n } else {\n cells.push(emptyLaneCell());\n }\n }\n return cells.join('');\n}\n\nfunction renderConnectorGutter(\n row: ConnectorLayoutRow,\n openLanes: ReadonlySet<number>,\n maxLane: number,\n palette: GlyphPalette,\n style: MigrationListStyler,\n): string {\n const spanLaneCount = row.endLane - row.startLane + 1;\n const spanWidth = spanLaneCount * 2;\n let spanGlyph = (\n row.connectorKind === 'fanBelow'\n ? palette.fanBelow(row.branchCount)\n : palette.joinAbove(row.branchCount)\n )\n .padEnd(spanWidth, ' ')\n .slice(0, spanWidth);\n\n const hasOutsideOpen = [...openLanes].some((lane) => lane < row.startLane || lane > row.endLane);\n if (!hasOutsideOpen && spanGlyph.endsWith(' ')) {\n spanGlyph = spanGlyph.slice(0, -1);\n }\n\n let gutter = '';\n for (let lane = 0; lane < row.startLane; lane++) {\n gutter += openLanes.has(lane) ? laneCell(style.lane(palette.lane)) : emptyLaneCell();\n }\n gutter += style.lane(spanGlyph);\n for (let lane = row.endLane + 1; lane <= maxLane; lane++) {\n gutter += openLanes.has(lane) ? laneCell(style.lane(palette.lane)) : emptyLaneCell();\n }\n return gutter;\n}\n\nfunction advanceOpenLanes(row: LayoutRow, openLanes: ReadonlySet<number>): ReadonlySet<number> {\n if (row.kind === 'migration') {\n return new Set([row.laneIndex, ...row.passThroughLanes]);\n }\n if (row.kind === 'connector') {\n if (row.connectorKind === 'fanBelow') {\n const next = new Set(openLanes);\n for (let lane = row.startLane; lane <= row.endLane; lane++) {\n next.add(lane);\n }\n return next;\n }\n const next = new Set(openLanes);\n for (let lane = row.startLane + 1; lane <= row.endLane; lane++) {\n next.delete(lane);\n }\n return next;\n }\n return openLanes;\n}\n\nexport function renderMigrationListGraphWithStyle(\n layout: MigrationListGraphLayout,\n style: MigrationListStyler,\n glyphMode: GlyphMode,\n): string {\n const palette = paletteFor(glyphMode);\n const migrations = migrationEntries(layout);\n const layoutMaxLane = layoutMaxLaneIndex(layout);\n const dirNameWidth = computeMigrationDirNameWidth(migrations);\n const gutterMaxLane = layoutMaxLane;\n const blockDataStart = (layoutMaxLane + 1) * 2;\n // Migration and node-line gutters always occupy a fixed, ANSI-free visible\n // width of two columns per lane. Padding is computed from this width rather\n // than the rendered string length so dimmed lanes (which carry zero-width\n // SGR bytes) stay column-aligned with the data that follows.\n const gutterVisibleWidth = (gutterMaxLane + 1) * 2;\n const lines: string[] = [];\n let openLanes: ReadonlySet<number> = new Set();\n\n function padToDataColumn(gutter: string, dataStart: number): string {\n return gutter + ' '.repeat(Math.max(0, dataStart - gutterVisibleWidth));\n }\n\n for (const row of layout.rows) {\n if (row.kind === 'migration') {\n const gutter = renderMigrationGutter(row, gutterMaxLane, palette, style);\n const data = formatMigrationDataColumn(row.entry, {\n dirNameWidth,\n edgeKind: row.edgeKind,\n style,\n forwardArrow: palette.forwardArrow,\n emptySource: palette.emptySource,\n });\n lines.push(`${padToDataColumn(gutter, blockDataStart)}${data}`);\n } else if (row.kind === 'nodeLine') {\n const gutter = renderNodeLineGutter(row, openLanes, gutterMaxLane, palette, style);\n const data = formatNodeLineDataColumn(row.contractHash, style);\n lines.push(`${padToDataColumn(gutter, blockDataStart)}${data}`);\n } else {\n lines.push(renderConnectorGutter(row, openLanes, gutterMaxLane, palette, style));\n }\n openLanes = advanceOpenLanes(row, openLanes);\n }\n\n return lines.map((line) => line.trimEnd()).join('\\n');\n}\n\nexport function renderMigrationListGraph(\n layout: MigrationListGraphLayout,\n style: MigrationListStyler,\n glyphMode: GlyphMode,\n): string {\n return renderMigrationListGraphWithStyle(layout, style, glyphMode);\n}\n\nexport function formatGraphNodeLineHash(contractHash: string, style: MigrationListStyler): string {\n return style.sourceHash(abbreviateContractHash(contractHash));\n}\n\nfunction formatGraphEmptyStateLine(spaceId: string, style: MigrationListStyler): string {\n return style.emptyState(`There are no migrations in migrations/${spaceId}/ yet`);\n}\n\nfunction renderGraphSpaceBlock(\n spaceId: string,\n migrations: readonly MigrationListEntry[],\n multiSpace: boolean,\n style: MigrationListStyler,\n glyphMode: GlyphMode,\n topology: MigrationListGraphTopology,\n): readonly string[] {\n if (migrations.length === 0) {\n const emptyLine = formatGraphEmptyStateLine(spaceId, style);\n if (!multiSpace) {\n return [emptyLine];\n }\n return [style.spaceHeading(`${spaceId}:`), ` ${emptyLine}`];\n }\n\n const layout = computeMigrationListGraphLayout(migrations, topology);\n const graphBody = renderMigrationListGraphWithStyle(layout, style, glyphMode);\n const rows = graphBody.split('\\n');\n if (!multiSpace) {\n return rows;\n }\n return [style.spaceHeading(`${spaceId}:`), ...rows.map((row) => ` ${row}`)];\n}\n\nexport function renderMigrationListGraphResult(\n result: MigrationListResult,\n style: MigrationListStyler,\n glyphMode: GlyphMode,\n topologyBySpaceId: ReadonlyMap<string, MigrationListGraphTopology>,\n): string {\n const multiSpace = result.spaces.length > 1;\n const lines: string[] = [];\n\n for (let index = 0; index < result.spaces.length; index++) {\n const space = result.spaces[index]!;\n if (index > 0) {\n lines.push('');\n }\n const topology = topologyBySpaceId.get(space.spaceId);\n if (topology === undefined) {\n throw new Error(`missing topology for space ${space.spaceId}`);\n }\n lines.push(\n ...renderGraphSpaceBlock(\n space.spaceId,\n space.migrations,\n multiSpace,\n style,\n glyphMode,\n topology,\n ),\n );\n }\n\n const totalMigrations = result.spaces.reduce(\n (count, space) => count + space.migrations.length,\n 0,\n );\n if (totalMigrations > 0) {\n lines.push('');\n lines.push(style.summary(result.summary));\n }\n\n return lines.join('\\n');\n}\n","import type {\n ContractSpaceAggregate,\n ContractSpaceMember,\n} from '@prisma-next/migration-tools/aggregate';\nimport { HEAD_REF_NAME, refsByContractHash } from '@prisma-next/migration-tools/refs';\nimport {\n APP_SPACE_ID,\n isValidSpaceId,\n listContractSpaceDirectories,\n RESERVED_SPACE_SUBDIR_NAMES,\n} from '@prisma-next/migration-tools/spaces';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport { loadConfig } from '../config-loader';\nimport {\n type CliStructuredError,\n errorInvalidSpaceId,\n errorSpaceNotFound,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n resolveMigrationPaths,\n setCommandDescriptions,\n setCommandExamples,\n setCommandSeeAlso,\n} from '../utils/command-helpers';\nimport { buildReadAggregate } from '../utils/contract-space-aggregate-loader';\nimport {\n type GlyphMode,\n renderMigrationListGraphResult,\n} from '../utils/formatters/migration-list-graph-render';\nimport {\n buildMigrationListTopologyBySpace,\n renderMigrationListWithStyle,\n} from '../utils/formatters/migration-list-render';\nimport { createAnsiMigrationListStyler } from '../utils/formatters/migration-list-styler';\nimport type {\n MigrationListEntry,\n MigrationListResult,\n MigrationSpaceListEntry,\n} 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 { handleResult } from '../utils/result-handler';\nimport { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';\n\nfunction compareSpaceIds(a: string, b: string): number {\n if (a === APP_SPACE_ID) return b === APP_SPACE_ID ? 0 : -1;\n if (b === APP_SPACE_ID) return 1;\n if (a < b) return -1;\n if (a > b) return 1;\n return 0;\n}\n\nfunction compareDirNamesDescending(a: MigrationListEntry, b: MigrationListEntry): number {\n if (a.dirName < b.dirName) return 1;\n if (a.dirName > b.dirName) return -1;\n return 0;\n}\n\n/**\n * Ref names decorating a space's destination contract hashes. The\n * tolerant `member.refs` deliberately omits the structural `head.json`;\n * for extension spaces the old enumerator surfaced it as a `head`\n * decoration on the tip migration, so fold `member.headRef` back in to\n * keep that output. The app space synthesises its head, so it carries\n * no on-disk `head` ref to restore.\n */\nfunction listRefsByContractHash(\n member: ContractSpaceMember,\n): ReadonlyMap<string, readonly string[]> {\n const byHash = new Map(refsByContractHash(member.refs));\n if (member.spaceId !== APP_SPACE_ID && member.headRef !== null) {\n const hash = member.headRef.hash;\n const bucket = byHash.get(hash) ?? [];\n if (!bucket.includes(HEAD_REF_NAME)) {\n byHash.set(hash, [...bucket, HEAD_REF_NAME].sort());\n }\n }\n return byHash;\n}\n\nasync function orderedOnDiskSpaceIds(projectMigrationsDir: string): Promise<readonly string[]> {\n const candidateDirs = await listContractSpaceDirectories(projectMigrationsDir);\n return candidateDirs\n .filter((name) => !RESERVED_SPACE_SUBDIR_NAMES.has(name))\n .filter(isValidSpaceId)\n .sort(compareSpaceIds);\n}\n\n/**\n * Project the loaded {@link ContractSpaceAggregate} into the render-ready\n * {@link MigrationSpaceListEntry} rows `migration list` displays.\n *\n * Space membership matches the on-disk contract-space directories (not the\n * aggregate's always-present synthesized app member when `migrations/app/`\n * is absent); package and ref data come from `aggregate.space(id)`.\n */\nexport async function migrationSpaceListEntriesFromAggregate(\n aggregate: ContractSpaceAggregate,\n projectMigrationsDir: string,\n): Promise<readonly MigrationSpaceListEntry[]> {\n const spaceIds = await orderedOnDiskSpaceIds(projectMigrationsDir);\n const spaces: MigrationSpaceListEntry[] = [];\n\n for (const spaceId of spaceIds) {\n const member = aggregate.space(spaceId);\n if (member === undefined) {\n continue;\n }\n const refsByHash = listRefsByContractHash(member);\n const migrations: MigrationListEntry[] = member.packages\n .map((pkg) => ({\n dirName: pkg.dirName,\n from: pkg.metadata.from,\n to: pkg.metadata.to,\n migrationHash: pkg.metadata.migrationHash,\n operationCount: pkg.ops.length,\n createdAt: pkg.metadata.createdAt,\n refs: refsByHash.get(pkg.metadata.to) ?? [],\n providedInvariants: pkg.metadata.providedInvariants,\n }))\n .sort(compareDirNamesDescending);\n\n spaces.push({ spaceId, migrations });\n }\n\n return spaces;\n}\n\ninterface MigrationListOptions extends CommonCommandOptions {\n readonly config?: string;\n readonly space?: string;\n readonly graph?: boolean;\n readonly ascii?: boolean;\n}\n\nexport interface MigrationListHumanRenderOptions {\n readonly graph: boolean;\n readonly glyphMode: GlyphMode;\n readonly useColor: boolean;\n}\n\nexport function renderMigrationListHumanOutput(\n result: MigrationListResult,\n options: MigrationListHumanRenderOptions,\n): string {\n const styler = createAnsiMigrationListStyler({ useColor: options.useColor });\n const topologyBySpaceId = buildMigrationListTopologyBySpace(result);\n if (options.graph) {\n return renderMigrationListGraphResult(result, styler, options.glyphMode, topologyBySpaceId);\n }\n return renderMigrationListWithStyle(result, styler, options.glyphMode, topologyBySpaceId);\n}\n\n/**\n * Inputs for {@link runMigrationList} — the policy core of `migration list`\n * that tests exercise directly.\n *\n * The core does not call `loadConfig`, parse CLI flags, render a styled\n * header, or write to any stream. Enumeration is supplied by the caller\n * (the CLI shell builds it from {@link migrationSpaceListEntriesFromAggregate}).\n */\nexport interface RunMigrationListInputs {\n readonly spaces: readonly MigrationSpaceListEntry[];\n readonly spaceFilter?: string;\n}\n\nfunction computeSummary(spaces: readonly MigrationSpaceListEntry[]): string {\n const totalMigrations = spaces.reduce((count, space) => count + space.migrations.length, 0);\n if (spaces.length <= 1) {\n return `${totalMigrations} migration(s) on disk`;\n }\n return `${totalMigrations} migration(s) across ${spaces.length} contract space(s)`;\n}\n\n/**\n * Policy core of `migration list`: validates `--space`, narrows the\n * pre-enumerated spaces, and assembles a {@link MigrationListResult}.\n *\n * - `migrations/` missing or contains no valid space directories →\n * caller passes `spaces: []`; this synthesizes `[{ spaceId: APP_SPACE_ID, migrations: [] }]`.\n * - `--space <id>` on an existing-but-empty space → `{ spaceId, migrations: [] }` in the input.\n * - `--space <id>` on a non-existent (or reserved) space → `SPACE_NOT_FOUND`.\n */\nexport function runMigrationList(\n inputs: RunMigrationListInputs,\n): Result<MigrationListResult, CliStructuredError> {\n const { spaces, spaceFilter } = inputs;\n\n if (spaceFilter !== undefined && !isValidSpaceId(spaceFilter)) {\n return notOk(errorInvalidSpaceId(spaceFilter));\n }\n\n if (spaceFilter !== undefined && !spaces.some((s) => s.spaceId === spaceFilter)) {\n return notOk(errorSpaceNotFound(spaceFilter, spaces.map((s) => s.spaceId).sort()));\n }\n\n const scopedSpaces =\n spaceFilter !== undefined ? spaces.filter((s) => s.spaceId === spaceFilter) : spaces;\n\n const resultSpaces: readonly MigrationSpaceListEntry[] =\n scopedSpaces.length === 0 ? [{ spaceId: APP_SPACE_ID, migrations: [] }] : scopedSpaces;\n\n return ok({\n ok: true,\n spaces: resultSpaces,\n summary: computeSummary(resultSpaces),\n });\n}\n\n/**\n * CLI shell: loads config, resolves paths, prints the styled header on\n * stderr (interactive mode only), and delegates to {@link runMigrationList}.\n * Kept intentionally thin so the unit-testable surface lives in the core.\n */\nexport async function executeMigrationListCommand(\n options: MigrationListOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n): Promise<Result<MigrationListResult, CliStructuredError>> {\n const config = await loadConfig(options.config);\n const { configPath, migrationsDir, migrationsRelative } = resolveMigrationPaths(\n options.config,\n config,\n );\n\n if (!flags.json && !flags.quiet) {\n const header = formatStyledHeader({\n command: 'migration list',\n description: 'List on-disk migrations, latest first, per contract space',\n details: [\n { label: 'config', value: configPath },\n { label: 'migrations', value: migrationsRelative },\n ...(options.space !== undefined ? [{ label: 'space', value: options.space }] : []),\n ],\n flags,\n });\n ui.stderr(header);\n }\n\n const loaded = await buildReadAggregate(config, { migrationsDir });\n if (!loaded.ok) {\n return notOk(loaded.failure);\n }\n\n const spaces = await migrationSpaceListEntriesFromAggregate(\n loaded.value.aggregate,\n migrationsDir,\n );\n\n return runMigrationList({\n spaces,\n ...ifDefined('spaceFilter', options.space),\n });\n}\n\nexport function createMigrationListCommand(): Command {\n const command = new Command('list');\n setCommandDescriptions(\n command,\n 'List on-disk migrations, latest first, per contract space',\n 'Enumerates every on-disk migration under migrations/<space>/ for every\\n' +\n 'contract space found on disk, latest first. Offline — does not consult\\n' +\n 'the database. Each row leads with a kind glyph (* forward, ↩ rollback,\\n' +\n '⟲ self), then dirName, then source → destination contract hashes\\n' +\n '(7-char git-style). Self-edges show a single hash. Invariants render as\\n' +\n '{...}; refs on the destination as (production, db). Pass --space <id>\\n' +\n 'to narrow to one contract space. --graph draws the forward spine with\\n' +\n 'lane gutters; --ascii forces ASCII glyphs (orthogonal to --no-color).',\n );\n setCommandExamples(command, [\n 'prisma-next migration list',\n 'prisma-next migration list --graph',\n 'prisma-next migration list --space app',\n 'prisma-next migration list --graph --ascii',\n 'prisma-next migration list --json',\n ]);\n setCommandSeeAlso(command, [\n { verb: 'migration status', oneLiner: 'Show migration path and pending status' },\n { verb: 'migration log', oneLiner: 'Show executed migration history' },\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('--config <path>', 'Path to prisma-next.config.ts')\n .option('--space <id>', 'Narrow output to a single contract space')\n .option('--graph', 'Draw migration relationships as an annotated tree')\n .option('--ascii', 'Use ASCII glyphs for --graph (pipe-friendly)')\n .action(async (options: MigrationListOptions) => {\n const flags = parseGlobalFlagsOrExit(options);\n const ui = createTerminalUI(flags);\n const result = await executeMigrationListCommand(options, flags, ui);\n const exitCode = handleResult(result, flags, ui, (listResult) => {\n if (flags.json) {\n ui.output(JSON.stringify(listResult, null, 2));\n } else if (!flags.quiet) {\n ui.output(\n renderMigrationListHumanOutput(listResult, {\n graph: options.graph === true,\n glyphMode: ui.resolveGlyphMode(options.ascii === true),\n useColor: ui.useColor,\n }),\n );\n }\n });\n process.exit(exitCode);\n });\n return command;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import { bold, cyan, cyanBright, dim, green, yellow } from "colorette";
|
|
2
|
+
import { EMPTY_CONTRACT_HASH } from "@prisma-next/migration-tools/constants";
|
|
3
|
+
//#region src/utils/formatters/migration-list-graph-topology.ts
|
|
4
|
+
function compareDirNameDesc(a, b) {
|
|
5
|
+
return b.dirName.localeCompare(a.dirName);
|
|
6
|
+
}
|
|
7
|
+
function bumpDegree(map, key) {
|
|
8
|
+
map.set(key, (map.get(key) ?? 0) + 1);
|
|
9
|
+
}
|
|
10
|
+
function forwardRootsForDepth(nodes, candidates) {
|
|
11
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
12
|
+
for (const node of nodes) inDegree.set(node, 0);
|
|
13
|
+
for (const edge of candidates) bumpDegree(inDegree, edge.to);
|
|
14
|
+
const roots = [];
|
|
15
|
+
for (const node of nodes) if ((inDegree.get(node) ?? 0) === 0) roots.push(node);
|
|
16
|
+
roots.sort((a, b) => {
|
|
17
|
+
if (a === EMPTY_CONTRACT_HASH) return -1;
|
|
18
|
+
if (b === EMPTY_CONTRACT_HASH) return 1;
|
|
19
|
+
return a.localeCompare(b);
|
|
20
|
+
});
|
|
21
|
+
if (roots.length > 0) return roots;
|
|
22
|
+
return [...nodes].sort((a, b) => {
|
|
23
|
+
if (a === EMPTY_CONTRACT_HASH) return -1;
|
|
24
|
+
if (b === EMPTY_CONTRACT_HASH) return 1;
|
|
25
|
+
return a.localeCompare(b);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function longestPathDepths(nodes, candidates) {
|
|
29
|
+
const depth = /* @__PURE__ */ new Map();
|
|
30
|
+
for (const root of forwardRootsForDepth(nodes, candidates)) depth.set(root, 0);
|
|
31
|
+
const maxPasses = nodes.size;
|
|
32
|
+
for (let pass = 0; pass < maxPasses; pass++) {
|
|
33
|
+
let changed = false;
|
|
34
|
+
for (const edge of candidates) {
|
|
35
|
+
const base = depth.get(edge.from);
|
|
36
|
+
if (base === void 0) continue;
|
|
37
|
+
const next = base + 1;
|
|
38
|
+
if (next > (depth.get(edge.to) ?? -1)) {
|
|
39
|
+
depth.set(edge.to, next);
|
|
40
|
+
changed = true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (!changed) break;
|
|
44
|
+
}
|
|
45
|
+
for (const node of nodes) if (!depth.has(node)) depth.set(node, 0);
|
|
46
|
+
return depth;
|
|
47
|
+
}
|
|
48
|
+
function canReachForward(start, goal, candidates) {
|
|
49
|
+
if (start === goal) return true;
|
|
50
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
51
|
+
for (const edge of candidates) {
|
|
52
|
+
const bucket = outgoing.get(edge.from);
|
|
53
|
+
if (bucket) bucket.push(edge.to);
|
|
54
|
+
else outgoing.set(edge.from, [edge.to]);
|
|
55
|
+
}
|
|
56
|
+
const visited = new Set([start]);
|
|
57
|
+
const queue = [start];
|
|
58
|
+
while (queue.length > 0) {
|
|
59
|
+
const node = queue.shift();
|
|
60
|
+
if (node === void 0) continue;
|
|
61
|
+
for (const next of outgoing.get(node) ?? []) {
|
|
62
|
+
if (next === goal) return true;
|
|
63
|
+
if (!visited.has(next)) {
|
|
64
|
+
visited.add(next);
|
|
65
|
+
queue.push(next);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
function isMarginalForwardEdge(nodes, candidates, edge) {
|
|
72
|
+
const depthWithout = longestPathDepths(nodes, candidates.filter((candidate) => candidate !== edge));
|
|
73
|
+
const depthWith = longestPathDepths(nodes, candidates);
|
|
74
|
+
const fromDepth = depthWithout.get(edge.from) ?? 0;
|
|
75
|
+
return (depthWith.get(edge.to) ?? 0) > fromDepth;
|
|
76
|
+
}
|
|
77
|
+
function shouldPeelForwardEdge(nodes, candidates, edge) {
|
|
78
|
+
const without = candidates.filter((candidate) => candidate !== edge);
|
|
79
|
+
const depthWithout = longestPathDepths(nodes, without);
|
|
80
|
+
const fromDepth = depthWithout.get(edge.from) ?? 0;
|
|
81
|
+
const toWithout = depthWithout.get(edge.to) ?? 0;
|
|
82
|
+
if (canReachForward(edge.to, edge.from, without) && fromDepth > toWithout + 1) return true;
|
|
83
|
+
return !isMarginalForwardEdge(nodes, candidates, edge);
|
|
84
|
+
}
|
|
85
|
+
function peelNonMarginalForwardEdges(nodes, kindByMigrationHash, nonSelf) {
|
|
86
|
+
let candidates = nonSelf.filter((edge) => kindByMigrationHash.get(edge.hash) === "forward");
|
|
87
|
+
while (candidates.length > 0) {
|
|
88
|
+
const rollbackCandidates = candidates.filter((edge) => shouldPeelForwardEdge(nodes, candidates, edge));
|
|
89
|
+
if (rollbackCandidates.length === 0) break;
|
|
90
|
+
rollbackCandidates.sort(compareDirNameDesc);
|
|
91
|
+
const rollback = rollbackCandidates[0];
|
|
92
|
+
if (rollback === void 0) break;
|
|
93
|
+
kindByMigrationHash.set(rollback.hash, "rollback");
|
|
94
|
+
candidates = candidates.filter((edge) => edge !== rollback);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* DFS with dirName-descending traversal. A GRAY target is a rollback only when it
|
|
99
|
+
* is the immediate DFS parent of the source — cross-links to other GRAY nodes
|
|
100
|
+
* stay forward. A follow-up peel pass drops node-skipping rollbacks (target can
|
|
101
|
+
* reach the source on the forward subgraph and sits more than one rank below).
|
|
102
|
+
*/
|
|
103
|
+
function classifyNormalizedEdges(edges) {
|
|
104
|
+
const nodes = /* @__PURE__ */ new Set();
|
|
105
|
+
const kindByMigrationHash = /* @__PURE__ */ new Map();
|
|
106
|
+
const outgoingByFrom = /* @__PURE__ */ new Map();
|
|
107
|
+
const nonSelf = [];
|
|
108
|
+
for (const edge of edges) {
|
|
109
|
+
nodes.add(edge.from);
|
|
110
|
+
nodes.add(edge.to);
|
|
111
|
+
if (edge.from === edge.to) {
|
|
112
|
+
kindByMigrationHash.set(edge.hash, "self");
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
nonSelf.push(edge);
|
|
116
|
+
const bucket = outgoingByFrom.get(edge.from);
|
|
117
|
+
if (bucket) bucket.push(edge);
|
|
118
|
+
else outgoingByFrom.set(edge.from, [edge]);
|
|
119
|
+
}
|
|
120
|
+
for (const bucket of outgoingByFrom.values()) bucket.sort(compareDirNameDesc);
|
|
121
|
+
const nonSelfInDegree = /* @__PURE__ */ new Map();
|
|
122
|
+
for (const node of nodes) nonSelfInDegree.set(node, 0);
|
|
123
|
+
for (const bucket of outgoingByFrom.values()) for (const edge of bucket) bumpDegree(nonSelfInDegree, edge.to);
|
|
124
|
+
const dfsRoots = [];
|
|
125
|
+
for (const node of nodes) if ((nonSelfInDegree.get(node) ?? 0) === 0) dfsRoots.push(node);
|
|
126
|
+
dfsRoots.sort((a, b) => {
|
|
127
|
+
if (a === EMPTY_CONTRACT_HASH) return -1;
|
|
128
|
+
if (b === EMPTY_CONTRACT_HASH) return 1;
|
|
129
|
+
return a.localeCompare(b);
|
|
130
|
+
});
|
|
131
|
+
if (dfsRoots.length === 0) dfsRoots.push(...[...nodes].sort((a, b) => a.localeCompare(b)));
|
|
132
|
+
const WHITE = 0;
|
|
133
|
+
const GRAY = 1;
|
|
134
|
+
const BLACK = 2;
|
|
135
|
+
const color = /* @__PURE__ */ new Map();
|
|
136
|
+
const dfsParent = /* @__PURE__ */ new Map();
|
|
137
|
+
for (const node of nodes) color.set(node, WHITE);
|
|
138
|
+
const stack = [];
|
|
139
|
+
function isImmediateDfsParent(ancestor, node) {
|
|
140
|
+
return dfsParent.get(node) === ancestor;
|
|
141
|
+
}
|
|
142
|
+
function pushFrame(node, parent) {
|
|
143
|
+
color.set(node, GRAY);
|
|
144
|
+
dfsParent.set(node, parent);
|
|
145
|
+
stack.push({
|
|
146
|
+
node,
|
|
147
|
+
outgoing: outgoingByFrom.get(node) ?? [],
|
|
148
|
+
index: 0
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function runDfsFrom(root) {
|
|
152
|
+
if (color.get(root) !== WHITE) return;
|
|
153
|
+
pushFrame(root, void 0);
|
|
154
|
+
while (stack.length > 0) {
|
|
155
|
+
const frame = stack[stack.length - 1];
|
|
156
|
+
if (frame === void 0) break;
|
|
157
|
+
if (frame.index >= frame.outgoing.length) {
|
|
158
|
+
color.set(frame.node, BLACK);
|
|
159
|
+
stack.pop();
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const edge = frame.outgoing[frame.index];
|
|
163
|
+
frame.index += 1;
|
|
164
|
+
if (edge === void 0) continue;
|
|
165
|
+
const v = edge.to;
|
|
166
|
+
const vColor = color.get(v);
|
|
167
|
+
if (vColor === GRAY && isImmediateDfsParent(v, frame.node)) kindByMigrationHash.set(edge.hash, "rollback");
|
|
168
|
+
else {
|
|
169
|
+
kindByMigrationHash.set(edge.hash, "forward");
|
|
170
|
+
if (vColor === WHITE) pushFrame(v, frame.node);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
for (const root of dfsRoots) runDfsFrom(root);
|
|
175
|
+
const remainingWhite = [...nodes].filter((node) => color.get(node) === WHITE);
|
|
176
|
+
remainingWhite.sort((a, b) => a.localeCompare(b));
|
|
177
|
+
for (const root of remainingWhite) runDfsFrom(root);
|
|
178
|
+
peelNonMarginalForwardEdges(nodes, kindByMigrationHash, nonSelf);
|
|
179
|
+
const forwardInDegree = /* @__PURE__ */ new Map();
|
|
180
|
+
const forwardOutDegree = /* @__PURE__ */ new Map();
|
|
181
|
+
for (const edge of edges) {
|
|
182
|
+
if (kindByMigrationHash.get(edge.hash) !== "forward") continue;
|
|
183
|
+
bumpDegree(forwardOutDegree, edge.from);
|
|
184
|
+
bumpDegree(forwardInDegree, edge.to);
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
kindByMigrationHash,
|
|
188
|
+
forwardInDegree,
|
|
189
|
+
forwardOutDegree
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function canonicalFrom(from) {
|
|
193
|
+
return from ?? EMPTY_CONTRACT_HASH;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Classify forward/rollback/self for a Tier-2 `MigrationListEntry[]` edge set.
|
|
197
|
+
* Returns the kind of each migration plus the forward in/out degree of each
|
|
198
|
+
* contract node. This is the established Tier-2 surface; its behaviour is
|
|
199
|
+
* unchanged — only its implementation now delegates to the shared classifier.
|
|
200
|
+
*/
|
|
201
|
+
function classifyMigrationListGraphTopology(entries) {
|
|
202
|
+
return classifyNormalizedEdges(entries.map((entry) => ({
|
|
203
|
+
hash: entry.migrationHash,
|
|
204
|
+
from: canonicalFrom(entry.from),
|
|
205
|
+
to: entry.to,
|
|
206
|
+
dirName: entry.dirName
|
|
207
|
+
})));
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Classify forward/rollback/self for a `MigrationGraph` edge set (Tier-3).
|
|
211
|
+
* Delegates to the same shared classifier as `classifyMigrationListGraphTopology`
|
|
212
|
+
* so both tiers agree on forward/rollback/self without duplicating logic.
|
|
213
|
+
*/
|
|
214
|
+
function classifyMigrationGraphTopology(graph) {
|
|
215
|
+
const normalized = [];
|
|
216
|
+
for (const edges of graph.forwardChain.values()) for (const edge of edges) normalized.push({
|
|
217
|
+
hash: edge.migrationHash,
|
|
218
|
+
from: edge.from,
|
|
219
|
+
to: edge.to,
|
|
220
|
+
dirName: edge.dirName
|
|
221
|
+
});
|
|
222
|
+
return classifyNormalizedEdges(normalized);
|
|
223
|
+
}
|
|
224
|
+
const MIGRATION_LIST_UNICODE_KIND_GLYPH = {
|
|
225
|
+
forward: "*",
|
|
226
|
+
rollback: "↩",
|
|
227
|
+
self: "⟲"
|
|
228
|
+
};
|
|
229
|
+
const MIGRATION_LIST_ASCII_KIND_GLYPH = {
|
|
230
|
+
forward: "*",
|
|
231
|
+
rollback: "<",
|
|
232
|
+
self: "~"
|
|
233
|
+
};
|
|
234
|
+
function migrationListKindGlyph(glyphMode, edgeKind) {
|
|
235
|
+
return glyphMode === "ascii" ? MIGRATION_LIST_ASCII_KIND_GLYPH[edgeKind] : MIGRATION_LIST_UNICODE_KIND_GLYPH[edgeKind];
|
|
236
|
+
}
|
|
237
|
+
function migrationListForwardArrow(glyphMode) {
|
|
238
|
+
return glyphMode === "ascii" ? "->" : "→";
|
|
239
|
+
}
|
|
240
|
+
function migrationListEmptySource(glyphMode) {
|
|
241
|
+
return glyphMode === "ascii" ? "-" : "∅";
|
|
242
|
+
}
|
|
243
|
+
function abbreviateContractHash(hash) {
|
|
244
|
+
return (hash.startsWith("sha256:") ? hash.slice(7) : hash).slice(0, 7);
|
|
245
|
+
}
|
|
246
|
+
function computeMigrationDirNameWidth(migrations) {
|
|
247
|
+
if (migrations.length === 0) return 0;
|
|
248
|
+
return Math.max(...migrations.map((entry) => entry.dirName.length)) + 2;
|
|
249
|
+
}
|
|
250
|
+
function formatSourceColumn(from, style, emptySource) {
|
|
251
|
+
if (from === null) return style.glyph(emptySource) + " ".repeat(7 - emptySource.length);
|
|
252
|
+
return style.sourceHash(abbreviateContractHash(from));
|
|
253
|
+
}
|
|
254
|
+
function formatDecorations(providedInvariants, refs, style) {
|
|
255
|
+
const blocks = [];
|
|
256
|
+
if (providedInvariants.length > 0) blocks.push(style.invariants(providedInvariants));
|
|
257
|
+
if (refs.length > 0) blocks.push(style.refs(refs));
|
|
258
|
+
if (blocks.length === 0) return "";
|
|
259
|
+
return ` ${blocks.join(" ")}`;
|
|
260
|
+
}
|
|
261
|
+
function formatMigrationDataColumn(migration, options) {
|
|
262
|
+
const { dirNameWidth, edgeKind, style, forwardArrow = "→", emptySource = "∅" } = options;
|
|
263
|
+
const dirNamePadding = " ".repeat(Math.max(0, dirNameWidth - migration.dirName.length));
|
|
264
|
+
const dirName = `${style.dirName(migration.dirName)}${dirNamePadding}`;
|
|
265
|
+
const decorations = formatDecorations(migration.providedInvariants, migration.refs, style);
|
|
266
|
+
if (edgeKind === "self") {
|
|
267
|
+
const contractHash = migration.from ?? migration.to;
|
|
268
|
+
return `${dirName}${style.sourceHash(abbreviateContractHash(contractHash))}${decorations}`;
|
|
269
|
+
}
|
|
270
|
+
return `${dirName}${formatSourceColumn(migration.from, style, emptySource)} ${style.glyph(forwardArrow)} ${style.destHash(abbreviateContractHash(migration.to))}${decorations}`;
|
|
271
|
+
}
|
|
272
|
+
function formatNodeLineDataColumn(contractHash, style) {
|
|
273
|
+
return style.sourceHash(abbreviateContractHash(contractHash));
|
|
274
|
+
}
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/utils/formatters/migration-list-render.ts
|
|
277
|
+
const IDENTITY_MIGRATION_LIST_STYLER = {
|
|
278
|
+
kind: (text) => text,
|
|
279
|
+
dirName: (text) => text,
|
|
280
|
+
sourceHash: (text) => text,
|
|
281
|
+
destHash: (text) => text,
|
|
282
|
+
glyph: (text) => text,
|
|
283
|
+
lane: (text) => text,
|
|
284
|
+
invariants: (ids) => `{${ids.join(", ")}}`,
|
|
285
|
+
refs: (names) => `(${names.join(", ")})`,
|
|
286
|
+
spaceHeading: (text) => text,
|
|
287
|
+
summary: (text) => text,
|
|
288
|
+
emptyState: (text) => text
|
|
289
|
+
};
|
|
290
|
+
function resolveEdgeKind(migrationHash, kindByMigrationHash) {
|
|
291
|
+
return kindByMigrationHash.get(migrationHash) ?? "forward";
|
|
292
|
+
}
|
|
293
|
+
function formatMigrationRow(migration, dirNameWidth, edgeKind, glyphMode, style) {
|
|
294
|
+
return `${`${style.kind(migrationListKindGlyph(glyphMode, edgeKind))} `}${formatMigrationDataColumn(migration, {
|
|
295
|
+
dirNameWidth,
|
|
296
|
+
edgeKind,
|
|
297
|
+
style,
|
|
298
|
+
forwardArrow: migrationListForwardArrow(glyphMode),
|
|
299
|
+
emptySource: migrationListEmptySource(glyphMode)
|
|
300
|
+
})}`;
|
|
301
|
+
}
|
|
302
|
+
function formatEmptyStateLine(spaceId, style) {
|
|
303
|
+
return style.emptyState(`There are no migrations in migrations/${spaceId}/ yet`);
|
|
304
|
+
}
|
|
305
|
+
function renderSpaceBlock(spaceId, migrations, multiSpace, glyphMode, kindByMigrationHash, style) {
|
|
306
|
+
if (migrations.length === 0) {
|
|
307
|
+
const emptyLine = formatEmptyStateLine(spaceId, style);
|
|
308
|
+
if (!multiSpace) return [emptyLine];
|
|
309
|
+
return [style.spaceHeading(`${spaceId}:`), ` ${emptyLine}`];
|
|
310
|
+
}
|
|
311
|
+
const dirNameWidth = computeMigrationDirNameWidth(migrations);
|
|
312
|
+
const rows = migrations.map((entry) => formatMigrationRow(entry, dirNameWidth, resolveEdgeKind(entry.migrationHash, kindByMigrationHash), glyphMode, style));
|
|
313
|
+
if (!multiSpace) return rows;
|
|
314
|
+
return [style.spaceHeading(`${spaceId}:`), ...rows.map((row) => ` ${row}`)];
|
|
315
|
+
}
|
|
316
|
+
function buildMigrationListTopologyBySpace(result) {
|
|
317
|
+
const topologyBySpaceId = /* @__PURE__ */ new Map();
|
|
318
|
+
for (const space of result.spaces) topologyBySpaceId.set(space.spaceId, classifyMigrationListGraphTopology(space.migrations));
|
|
319
|
+
return topologyBySpaceId;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Compose the styled `migration list` output. The renderer is
|
|
323
|
+
* presentation-neutral — every token passes through `style` before
|
|
324
|
+
* landing in the output, so the same composition serves the pure-text
|
|
325
|
+
* path ({@link renderMigrationList} via
|
|
326
|
+
* {@link IDENTITY_MIGRATION_LIST_STYLER}) and the ANSI-styled CLI path
|
|
327
|
+
* (via the ANSI styler the CLI shell wires up).
|
|
328
|
+
*/
|
|
329
|
+
function renderMigrationListWithStyle(result, style, glyphMode = "unicode", topologyBySpaceId = buildMigrationListTopologyBySpace(result)) {
|
|
330
|
+
const multiSpace = result.spaces.length > 1;
|
|
331
|
+
const lines = [];
|
|
332
|
+
for (let index = 0; index < result.spaces.length; index++) {
|
|
333
|
+
const space = result.spaces[index];
|
|
334
|
+
if (index > 0) lines.push("");
|
|
335
|
+
const kindByMigrationHash = topologyBySpaceId.get(space.spaceId)?.kindByMigrationHash ?? classifyMigrationListGraphTopology(space.migrations).kindByMigrationHash;
|
|
336
|
+
lines.push(...renderSpaceBlock(space.spaceId, space.migrations, multiSpace, glyphMode, kindByMigrationHash, style));
|
|
337
|
+
}
|
|
338
|
+
if (result.spaces.reduce((count, space) => count + space.migrations.length, 0) > 0) {
|
|
339
|
+
lines.push("");
|
|
340
|
+
lines.push(style.summary(result.summary));
|
|
341
|
+
}
|
|
342
|
+
return lines.join("\n");
|
|
343
|
+
}
|
|
344
|
+
//#endregion
|
|
345
|
+
//#region src/utils/formatters/migration-list-styler.ts
|
|
346
|
+
/**
|
|
347
|
+
* The current contract overlay marker. Unlike user refs, this names the user's
|
|
348
|
+
* declared desired state — the implicit base/target for `plan` / `migrate` —
|
|
349
|
+
* not a stored label. It is emphasized (bold) so it stands out from plain refs
|
|
350
|
+
* (including the live-database `db` marker, which is just another ref).
|
|
351
|
+
*/
|
|
352
|
+
const CONTRACT_MARKER_NAME = "contract";
|
|
353
|
+
function styleRefName(name) {
|
|
354
|
+
return name === "contract" ? bold(green(name)) : green(name);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Build a {@link MigrationListStyler} that decorates `migration list`
|
|
358
|
+
* tokens with ANSI SGR codes. When `useColor` is `false` (non-TTY,
|
|
359
|
+
* `--no-color`, `NO_COLOR=1`, piped output) the function returns the
|
|
360
|
+
* shared identity styler so callers get plain text with zero ANSI
|
|
361
|
+
* bytes — pipe-friendly by construction.
|
|
362
|
+
*
|
|
363
|
+
* Palette:
|
|
364
|
+
*
|
|
365
|
+
* - `dirName`: bold
|
|
366
|
+
* - `sourceHash`: dim cyan
|
|
367
|
+
* - `destHash`: bright cyan
|
|
368
|
+
* - `kind` (`*` / `↩` / `⟲`): bright — the signal; lanes and arrows dim
|
|
369
|
+
* - `glyph` (`→` / `⟲` / `∅`): dim
|
|
370
|
+
* - `lane` (graph gutter lines `│` and fan/join connectors `├─┐` / `├─┘`): dim
|
|
371
|
+
* - `invariants` (`{...}`): yellow
|
|
372
|
+
* - `refs` (`(...)`): green; the `contract` desired-state marker inside is
|
|
373
|
+
* green-bold (the active ref is bolded separately by the tree styler)
|
|
374
|
+
* - `spaceHeading` (`<spaceId>:`): bold
|
|
375
|
+
* - `summary`: dim
|
|
376
|
+
* - `emptyState`: dim
|
|
377
|
+
*/
|
|
378
|
+
function createAnsiMigrationListStyler(opts) {
|
|
379
|
+
if (!opts.useColor) return IDENTITY_MIGRATION_LIST_STYLER;
|
|
380
|
+
return {
|
|
381
|
+
kind: (text) => text,
|
|
382
|
+
dirName: (text) => bold(text),
|
|
383
|
+
sourceHash: (text) => dim(cyan(text)),
|
|
384
|
+
destHash: (text) => cyanBright(text),
|
|
385
|
+
glyph: (text) => dim(text),
|
|
386
|
+
lane: (text) => dim(text),
|
|
387
|
+
invariants: (ids) => yellow(`{${ids.join(", ")}}`),
|
|
388
|
+
refs: (names) => {
|
|
389
|
+
const open = green("(");
|
|
390
|
+
const close = green(")");
|
|
391
|
+
const separator = green(", ");
|
|
392
|
+
return open + names.map(styleRefName).join(separator) + close;
|
|
393
|
+
},
|
|
394
|
+
spaceHeading: (text) => bold(text),
|
|
395
|
+
summary: (text) => dim(text),
|
|
396
|
+
emptyState: (text) => dim(text)
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
//#endregion
|
|
400
|
+
export { MIGRATION_LIST_ASCII_KIND_GLYPH as a, formatMigrationDataColumn as c, migrationListForwardArrow as d, classifyMigrationGraphTopology as f, renderMigrationListWithStyle as i, formatNodeLineDataColumn as l, createAnsiMigrationListStyler as n, MIGRATION_LIST_UNICODE_KIND_GLYPH as o, classifyMigrationListGraphTopology as p, buildMigrationListTopologyBySpace as r, computeMigrationDirNameWidth as s, CONTRACT_MARKER_NAME as t, migrationListEmptySource as u };
|
|
401
|
+
|
|
402
|
+
//# sourceMappingURL=migration-list-styler-DeAwACt3.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-list-styler-DeAwACt3.mjs","names":[],"sources":["../src/utils/formatters/migration-list-graph-topology.ts","../src/utils/formatters/migration-list-data-column.ts","../src/utils/formatters/migration-list-render.ts","../src/utils/formatters/migration-list-styler.ts"],"sourcesContent":["import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';\nimport type { MigrationGraph } from '@prisma-next/migration-tools/graph';\nimport type { MigrationListEntry } from './migration-list-types';\n\nexport type MigrationEdgeKind = 'forward' | 'rollback' | 'self';\n\nexport interface MigrationListGraphTopology {\n readonly kindByMigrationHash: ReadonlyMap<string, MigrationEdgeKind>;\n readonly forwardInDegree: ReadonlyMap<string, number>;\n readonly forwardOutDegree: ReadonlyMap<string, number>;\n}\n\n// ---------------------------------------------------------------------------\n// Shared classifier — operates on a normalized edge shape common to both\n// MigrationListEntry (Tier-2) and MigrationEdge / MigrationGraph (Tier-3).\n// ---------------------------------------------------------------------------\n\ninterface NormalizedEdge {\n readonly hash: string;\n readonly from: string;\n readonly to: string;\n readonly dirName: string;\n}\n\nfunction compareDirNameDesc(a: NormalizedEdge, b: NormalizedEdge): number {\n return b.dirName.localeCompare(a.dirName);\n}\n\nfunction bumpDegree(map: Map<string, number>, key: string): void {\n map.set(key, (map.get(key) ?? 0) + 1);\n}\n\nfunction forwardRootsForDepth(\n nodes: ReadonlySet<string>,\n candidates: readonly NormalizedEdge[],\n): readonly string[] {\n const inDegree = new Map<string, number>();\n for (const node of nodes) {\n inDegree.set(node, 0);\n }\n for (const edge of candidates) {\n bumpDegree(inDegree, edge.to);\n }\n\n const roots: string[] = [];\n for (const node of nodes) {\n if ((inDegree.get(node) ?? 0) === 0) {\n roots.push(node);\n }\n }\n roots.sort((a, b) => {\n if (a === EMPTY_CONTRACT_HASH) return -1;\n if (b === EMPTY_CONTRACT_HASH) return 1;\n return a.localeCompare(b);\n });\n if (roots.length > 0) return roots;\n\n return [...nodes].sort((a, b) => {\n if (a === EMPTY_CONTRACT_HASH) return -1;\n if (b === EMPTY_CONTRACT_HASH) return 1;\n return a.localeCompare(b);\n });\n}\n\nfunction longestPathDepths(\n nodes: ReadonlySet<string>,\n candidates: readonly NormalizedEdge[],\n): Map<string, number> {\n const depth = new Map<string, number>();\n for (const root of forwardRootsForDepth(nodes, candidates)) {\n depth.set(root, 0);\n }\n\n const maxPasses = nodes.size;\n for (let pass = 0; pass < maxPasses; pass++) {\n let changed = false;\n for (const edge of candidates) {\n const base = depth.get(edge.from);\n if (base === undefined) continue;\n const next = base + 1;\n if (next > (depth.get(edge.to) ?? -1)) {\n depth.set(edge.to, next);\n changed = true;\n }\n }\n if (!changed) break;\n }\n\n for (const node of nodes) {\n if (!depth.has(node)) {\n depth.set(node, 0);\n }\n }\n\n return depth;\n}\n\nfunction canReachForward(\n start: string,\n goal: string,\n candidates: readonly NormalizedEdge[],\n): boolean {\n if (start === goal) return true;\n\n const outgoing = new Map<string, string[]>();\n for (const edge of candidates) {\n const bucket = outgoing.get(edge.from);\n if (bucket) bucket.push(edge.to);\n else outgoing.set(edge.from, [edge.to]);\n }\n\n const visited = new Set<string>([start]);\n const queue = [start];\n while (queue.length > 0) {\n const node = queue.shift();\n if (node === undefined) continue;\n for (const next of outgoing.get(node) ?? []) {\n if (next === goal) return true;\n if (!visited.has(next)) {\n visited.add(next);\n queue.push(next);\n }\n }\n }\n\n return false;\n}\n\nfunction isMarginalForwardEdge(\n nodes: ReadonlySet<string>,\n candidates: readonly NormalizedEdge[],\n edge: NormalizedEdge,\n): boolean {\n const without = candidates.filter((candidate) => candidate !== edge);\n const depthWithout = longestPathDepths(nodes, without);\n const depthWith = longestPathDepths(nodes, candidates);\n const fromDepth = depthWithout.get(edge.from) ?? 0;\n const toWith = depthWith.get(edge.to) ?? 0;\n return toWith > fromDepth;\n}\n\n// The first branch is the load-bearing one: a forward edge `from → to` is a\n// disguised node-skipping rollback when, after removing it, `to` can still\n// reach `from` and `from` sits strictly deeper than `to + 1` (a longer path\n// already connects them). This branch fires on every cycle-closing edge, and\n// the caller peels exactly one edge (dirName-max) per iteration before\n// recomputing — so cycles are broken deterministically regardless of edge\n// order. `isMarginalForwardEdge` is only a fallback for the residual case and\n// is reached only while the candidate set is still cyclic.\nfunction shouldPeelForwardEdge(\n nodes: ReadonlySet<string>,\n candidates: readonly NormalizedEdge[],\n edge: NormalizedEdge,\n): boolean {\n const without = candidates.filter((candidate) => candidate !== edge);\n const depthWithout = longestPathDepths(nodes, without);\n const fromDepth = depthWithout.get(edge.from) ?? 0;\n const toWithout = depthWithout.get(edge.to) ?? 0;\n\n if (canReachForward(edge.to, edge.from, without) && fromDepth > toWithout + 1) {\n return true;\n }\n\n return !isMarginalForwardEdge(nodes, candidates, edge);\n}\n\nfunction peelNonMarginalForwardEdges(\n nodes: ReadonlySet<string>,\n kindByMigrationHash: Map<string, MigrationEdgeKind>,\n nonSelf: readonly NormalizedEdge[],\n): void {\n let candidates = nonSelf.filter((edge) => kindByMigrationHash.get(edge.hash) === 'forward');\n\n while (candidates.length > 0) {\n const rollbackCandidates = candidates.filter((edge) =>\n shouldPeelForwardEdge(nodes, candidates, edge),\n );\n if (rollbackCandidates.length === 0) break;\n\n rollbackCandidates.sort(compareDirNameDesc);\n const rollback = rollbackCandidates[0];\n if (rollback === undefined) break;\n\n kindByMigrationHash.set(rollback.hash, 'rollback');\n candidates = candidates.filter((edge) => edge !== rollback);\n }\n}\n\n/**\n * DFS with dirName-descending traversal. A GRAY target is a rollback only when it\n * is the immediate DFS parent of the source — cross-links to other GRAY nodes\n * stay forward. A follow-up peel pass drops node-skipping rollbacks (target can\n * reach the source on the forward subgraph and sits more than one rank below).\n */\nfunction classifyNormalizedEdges(edges: readonly NormalizedEdge[]): MigrationListGraphTopology {\n const nodes = new Set<string>();\n const kindByMigrationHash = new Map<string, MigrationEdgeKind>();\n const outgoingByFrom = new Map<string, NormalizedEdge[]>();\n const nonSelf: NormalizedEdge[] = [];\n\n for (const edge of edges) {\n nodes.add(edge.from);\n nodes.add(edge.to);\n\n if (edge.from === edge.to) {\n kindByMigrationHash.set(edge.hash, 'self');\n continue;\n }\n\n nonSelf.push(edge);\n const bucket = outgoingByFrom.get(edge.from);\n if (bucket) bucket.push(edge);\n else outgoingByFrom.set(edge.from, [edge]);\n }\n\n for (const bucket of outgoingByFrom.values()) {\n bucket.sort(compareDirNameDesc);\n }\n\n const nonSelfInDegree = new Map<string, number>();\n for (const node of nodes) {\n nonSelfInDegree.set(node, 0);\n }\n for (const bucket of outgoingByFrom.values()) {\n for (const edge of bucket) {\n bumpDegree(nonSelfInDegree, edge.to);\n }\n }\n\n const dfsRoots: string[] = [];\n for (const node of nodes) {\n if ((nonSelfInDegree.get(node) ?? 0) === 0) {\n dfsRoots.push(node);\n }\n }\n dfsRoots.sort((a, b) => {\n if (a === EMPTY_CONTRACT_HASH) return -1;\n if (b === EMPTY_CONTRACT_HASH) return 1;\n return a.localeCompare(b);\n });\n if (dfsRoots.length === 0) {\n dfsRoots.push(...[...nodes].sort((a, b) => a.localeCompare(b)));\n }\n\n const WHITE = 0;\n const GRAY = 1;\n const BLACK = 2;\n const color = new Map<string, number>();\n const dfsParent = new Map<string, string | undefined>();\n for (const node of nodes) {\n color.set(node, WHITE);\n }\n\n interface Frame {\n node: string;\n outgoing: readonly NormalizedEdge[];\n index: number;\n }\n const stack: Frame[] = [];\n\n function isImmediateDfsParent(ancestor: string, node: string): boolean {\n return dfsParent.get(node) === ancestor;\n }\n\n function pushFrame(node: string, parent: string | undefined): void {\n color.set(node, GRAY);\n dfsParent.set(node, parent);\n stack.push({ node, outgoing: outgoingByFrom.get(node) ?? [], index: 0 });\n }\n\n function runDfsFrom(root: string): void {\n if (color.get(root) !== WHITE) return;\n pushFrame(root, undefined);\n\n while (stack.length > 0) {\n const frame = stack[stack.length - 1];\n if (frame === undefined) break;\n if (frame.index >= frame.outgoing.length) {\n color.set(frame.node, BLACK);\n stack.pop();\n continue;\n }\n\n const edge = frame.outgoing[frame.index];\n frame.index += 1;\n if (edge === undefined) continue;\n\n const v = edge.to;\n const vColor = color.get(v);\n if (vColor === GRAY && isImmediateDfsParent(v, frame.node)) {\n kindByMigrationHash.set(edge.hash, 'rollback');\n } else {\n kindByMigrationHash.set(edge.hash, 'forward');\n if (vColor === WHITE) {\n pushFrame(v, frame.node);\n }\n }\n }\n }\n\n for (const root of dfsRoots) {\n runDfsFrom(root);\n }\n const remainingWhite = [...nodes].filter((node) => color.get(node) === WHITE);\n remainingWhite.sort((a, b) => a.localeCompare(b));\n for (const root of remainingWhite) {\n runDfsFrom(root);\n }\n\n peelNonMarginalForwardEdges(nodes, kindByMigrationHash, nonSelf);\n\n const forwardInDegree = new Map<string, number>();\n const forwardOutDegree = new Map<string, number>();\n\n for (const edge of edges) {\n if (kindByMigrationHash.get(edge.hash) !== 'forward') continue;\n bumpDegree(forwardOutDegree, edge.from);\n bumpDegree(forwardInDegree, edge.to);\n }\n\n return {\n kindByMigrationHash,\n forwardInDegree,\n forwardOutDegree,\n };\n}\n\nfunction canonicalFrom(from: string | null): string {\n return from ?? EMPTY_CONTRACT_HASH;\n}\n\n/**\n * Classify forward/rollback/self for a Tier-2 `MigrationListEntry[]` edge set.\n * Returns the kind of each migration plus the forward in/out degree of each\n * contract node. This is the established Tier-2 surface; its behaviour is\n * unchanged — only its implementation now delegates to the shared classifier.\n */\nexport function classifyMigrationListGraphTopology(\n entries: readonly MigrationListEntry[],\n): MigrationListGraphTopology {\n const normalized: NormalizedEdge[] = entries.map((entry) => ({\n hash: entry.migrationHash,\n from: canonicalFrom(entry.from),\n to: entry.to,\n dirName: entry.dirName,\n }));\n return classifyNormalizedEdges(normalized);\n}\n\n/**\n * Classify forward/rollback/self for a `MigrationGraph` edge set (Tier-3).\n * Delegates to the same shared classifier as `classifyMigrationListGraphTopology`\n * so both tiers agree on forward/rollback/self without duplicating logic.\n */\nexport function classifyMigrationGraphTopology(graph: MigrationGraph): MigrationListGraphTopology {\n const normalized: NormalizedEdge[] = [];\n for (const edges of graph.forwardChain.values()) {\n for (const edge of edges) {\n normalized.push({\n hash: edge.migrationHash,\n from: edge.from,\n to: edge.to,\n dirName: edge.dirName,\n });\n }\n }\n return classifyNormalizedEdges(normalized);\n}\n","import type { GlyphMode } from '../glyph-mode';\nimport type { MigrationEdgeKind } from './migration-list-graph-topology';\nimport type { MigrationListStyler } from './migration-list-render';\nimport type { MigrationListEntry } from './migration-list-types';\n\nexport const MIGRATION_LIST_HASH_WIDTH = 7;\nexport const MIGRATION_LIST_EMPTY_SOURCE = '∅';\nexport const MIGRATION_LIST_ASCII_EMPTY_SOURCE = '-';\nexport const MIGRATION_LIST_FORWARD_EDGE_GLYPH = '→';\nexport const MIGRATION_LIST_ASCII_FORWARD_EDGE_GLYPH = '->';\nexport const MIGRATION_LIST_DECORATION_PREFIX = ' ';\n\nexport const MIGRATION_LIST_UNICODE_KIND_GLYPH: Record<MigrationEdgeKind, string> = {\n forward: '*',\n rollback: '↩',\n self: '⟲',\n};\n\nexport const MIGRATION_LIST_ASCII_KIND_GLYPH: Record<MigrationEdgeKind, string> = {\n forward: '*',\n rollback: '<',\n self: '~',\n};\n\nexport function migrationListKindGlyph(glyphMode: GlyphMode, edgeKind: MigrationEdgeKind): string {\n return glyphMode === 'ascii'\n ? MIGRATION_LIST_ASCII_KIND_GLYPH[edgeKind]\n : MIGRATION_LIST_UNICODE_KIND_GLYPH[edgeKind];\n}\n\nexport function migrationListForwardArrow(glyphMode: GlyphMode): string {\n return glyphMode === 'ascii'\n ? MIGRATION_LIST_ASCII_FORWARD_EDGE_GLYPH\n : MIGRATION_LIST_FORWARD_EDGE_GLYPH;\n}\n\nexport function migrationListEmptySource(glyphMode: GlyphMode): string {\n return glyphMode === 'ascii' ? MIGRATION_LIST_ASCII_EMPTY_SOURCE : MIGRATION_LIST_EMPTY_SOURCE;\n}\n\nexport function abbreviateContractHash(hash: string): string {\n const stripped = hash.startsWith('sha256:') ? hash.slice(7) : hash;\n return stripped.slice(0, MIGRATION_LIST_HASH_WIDTH);\n}\n\nexport function computeMigrationDirNameWidth(migrations: readonly MigrationListEntry[]): number {\n if (migrations.length === 0) return 0;\n return Math.max(...migrations.map((entry) => entry.dirName.length)) + 2;\n}\n\nfunction formatSourceColumn(\n from: string | null,\n style: MigrationListStyler,\n emptySource: string,\n): string {\n if (from === null) {\n return style.glyph(emptySource) + ' '.repeat(MIGRATION_LIST_HASH_WIDTH - emptySource.length);\n }\n return style.sourceHash(abbreviateContractHash(from));\n}\n\nexport function formatDecorations(\n providedInvariants: readonly string[],\n refs: readonly string[],\n style: MigrationListStyler,\n): string {\n const blocks: string[] = [];\n if (providedInvariants.length > 0) {\n blocks.push(style.invariants(providedInvariants));\n }\n if (refs.length > 0) {\n blocks.push(style.refs(refs));\n }\n if (blocks.length === 0) return '';\n return `${MIGRATION_LIST_DECORATION_PREFIX}${blocks.join(' ')}`;\n}\n\nexport interface MigrationDataColumnOptions {\n readonly dirNameWidth: number;\n readonly edgeKind: MigrationEdgeKind;\n readonly style: MigrationListStyler;\n readonly forwardArrow?: string;\n readonly emptySource?: string;\n}\n\nexport function formatMigrationDataColumn(\n migration: MigrationListEntry,\n options: MigrationDataColumnOptions,\n): string {\n const {\n dirNameWidth,\n edgeKind,\n style,\n forwardArrow = MIGRATION_LIST_FORWARD_EDGE_GLYPH,\n emptySource = MIGRATION_LIST_EMPTY_SOURCE,\n } = options;\n const dirNamePadding = ' '.repeat(Math.max(0, dirNameWidth - migration.dirName.length));\n const dirName = `${style.dirName(migration.dirName)}${dirNamePadding}`;\n const decorations = formatDecorations(migration.providedInvariants, migration.refs, style);\n\n if (edgeKind === 'self') {\n const contractHash = migration.from ?? migration.to;\n const hash = style.sourceHash(abbreviateContractHash(contractHash));\n return `${dirName}${hash}${decorations}`;\n }\n\n const source = formatSourceColumn(migration.from, style, emptySource);\n const arrow = style.glyph(forwardArrow);\n const dest = style.destHash(abbreviateContractHash(migration.to));\n return `${dirName}${source} ${arrow} ${dest}${decorations}`;\n}\n\nexport function formatNodeLineDataColumn(contractHash: string, style: MigrationListStyler): string {\n return style.sourceHash(abbreviateContractHash(contractHash));\n}\n","import type { GlyphMode } from '../glyph-mode';\nimport {\n computeMigrationDirNameWidth,\n formatMigrationDataColumn,\n migrationListEmptySource,\n migrationListForwardArrow,\n migrationListKindGlyph,\n} from './migration-list-data-column';\nimport {\n classifyMigrationListGraphTopology,\n type MigrationEdgeKind,\n type MigrationListGraphTopology,\n} from './migration-list-graph-topology';\nimport type { MigrationListEntry, MigrationListResult } from './migration-list-types';\n\nexport type { GlyphMode } from '../glyph-mode';\nexport type { MigrationEdgeKind } from './migration-list-graph-topology';\nexport type {\n MigrationListEntry,\n MigrationListResult,\n MigrationSpaceListEntry,\n} from './migration-list-types';\n\n/**\n * Semantic styler for `migration list` output tokens. Token-typed so\n * the renderer composes presentation-neutral fragments and the styler\n * decides how each token kind is decorated (ANSI codes, plain text,\n * etc.). The renderer pads with raw spaces *outside* styled tokens so\n * visible column widths stay stable regardless of what the styler\n * emits — adding ANSI escape sequences never disturbs alignment.\n *\n * `invariants` and `refs` receive the underlying string arrays rather\n * than a pre-joined string so per-element styling (e.g. distinguishing\n * the live-DB `db` marker from user-named refs) is possible without\n * having to re-parse a joined block.\n */\nexport interface MigrationListStyler {\n kind(text: string): string;\n dirName(text: string): string;\n sourceHash(text: string): string;\n destHash(text: string): string;\n glyph(text: string): string;\n lane(text: string): string;\n invariants(ids: readonly string[]): string;\n refs(names: readonly string[]): string;\n spaceHeading(text: string): string;\n summary(text: string): string;\n emptyState(text: string): string;\n}\n\nexport const IDENTITY_MIGRATION_LIST_STYLER: MigrationListStyler = {\n kind: (text) => text,\n dirName: (text) => text,\n sourceHash: (text) => text,\n destHash: (text) => text,\n glyph: (text) => text,\n lane: (text) => text,\n invariants: (ids) => `{${ids.join(', ')}}`,\n refs: (names) => `(${names.join(', ')})`,\n spaceHeading: (text) => text,\n summary: (text) => text,\n emptyState: (text) => text,\n};\n\nfunction resolveEdgeKind(\n migrationHash: string,\n kindByMigrationHash: ReadonlyMap<string, MigrationEdgeKind>,\n): MigrationEdgeKind {\n return kindByMigrationHash.get(migrationHash) ?? 'forward';\n}\n\nfunction formatMigrationRow(\n migration: MigrationListEntry,\n dirNameWidth: number,\n edgeKind: MigrationEdgeKind,\n glyphMode: GlyphMode,\n style: MigrationListStyler,\n): string {\n const kindColumn = `${style.kind(migrationListKindGlyph(glyphMode, edgeKind))} `;\n const data = formatMigrationDataColumn(migration, {\n dirNameWidth,\n edgeKind,\n style,\n forwardArrow: migrationListForwardArrow(glyphMode),\n emptySource: migrationListEmptySource(glyphMode),\n });\n return `${kindColumn}${data}`;\n}\n\nfunction formatEmptyStateLine(spaceId: string, style: MigrationListStyler): string {\n return style.emptyState(`There are no migrations in migrations/${spaceId}/ yet`);\n}\n\nfunction renderSpaceBlock(\n spaceId: string,\n migrations: readonly MigrationListEntry[],\n multiSpace: boolean,\n glyphMode: GlyphMode,\n kindByMigrationHash: ReadonlyMap<string, MigrationEdgeKind>,\n style: MigrationListStyler,\n): readonly string[] {\n if (migrations.length === 0) {\n const emptyLine = formatEmptyStateLine(spaceId, style);\n if (!multiSpace) {\n return [emptyLine];\n }\n return [style.spaceHeading(`${spaceId}:`), ` ${emptyLine}`];\n }\n\n const dirNameWidth = computeMigrationDirNameWidth(migrations);\n const rows = migrations.map((entry) =>\n formatMigrationRow(\n entry,\n dirNameWidth,\n resolveEdgeKind(entry.migrationHash, kindByMigrationHash),\n glyphMode,\n style,\n ),\n );\n if (!multiSpace) {\n return rows;\n }\n return [style.spaceHeading(`${spaceId}:`), ...rows.map((row) => ` ${row}`)];\n}\n\nexport function buildMigrationListTopologyBySpace(\n result: MigrationListResult,\n): ReadonlyMap<string, MigrationListGraphTopology> {\n const topologyBySpaceId = new Map<string, MigrationListGraphTopology>();\n for (const space of result.spaces) {\n topologyBySpaceId.set(space.spaceId, classifyMigrationListGraphTopology(space.migrations));\n }\n return topologyBySpaceId;\n}\n\n/**\n * Compose the styled `migration list` output. The renderer is\n * presentation-neutral — every token passes through `style` before\n * landing in the output, so the same composition serves the pure-text\n * path ({@link renderMigrationList} via\n * {@link IDENTITY_MIGRATION_LIST_STYLER}) and the ANSI-styled CLI path\n * (via the ANSI styler the CLI shell wires up).\n */\nexport function renderMigrationListWithStyle(\n result: MigrationListResult,\n style: MigrationListStyler,\n glyphMode: GlyphMode = 'unicode',\n topologyBySpaceId: ReadonlyMap<\n string,\n MigrationListGraphTopology\n > = buildMigrationListTopologyBySpace(result),\n): string {\n const multiSpace = result.spaces.length > 1;\n const lines: string[] = [];\n\n for (let index = 0; index < result.spaces.length; index++) {\n const space = result.spaces[index]!;\n if (index > 0) {\n lines.push('');\n }\n const topology = topologyBySpaceId.get(space.spaceId);\n const kindByMigrationHash =\n topology?.kindByMigrationHash ??\n classifyMigrationListGraphTopology(space.migrations).kindByMigrationHash;\n lines.push(\n ...renderSpaceBlock(\n space.spaceId,\n space.migrations,\n multiSpace,\n glyphMode,\n kindByMigrationHash,\n style,\n ),\n );\n }\n\n const totalMigrations = result.spaces.reduce(\n (count, space) => count + space.migrations.length,\n 0,\n );\n if (totalMigrations > 0) {\n lines.push('');\n lines.push(style.summary(result.summary));\n }\n\n return lines.join('\\n');\n}\n\nexport function renderMigrationList(result: MigrationListResult): string {\n return renderMigrationListWithStyle(result, IDENTITY_MIGRATION_LIST_STYLER);\n}\n","import { bold, cyan, cyanBright, dim, green, yellow } from 'colorette';\nimport { IDENTITY_MIGRATION_LIST_STYLER, type MigrationListStyler } from './migration-list-render';\n\n/**\n * The current contract overlay marker. Unlike user refs, this names the user's\n * declared desired state — the implicit base/target for `plan` / `migrate` —\n * not a stored label. It is emphasized (bold) so it stands out from plain refs\n * (including the live-database `db` marker, which is just another ref).\n */\nexport const CONTRACT_MARKER_NAME = 'contract';\n\nfunction styleRefName(name: string): string {\n return name === CONTRACT_MARKER_NAME ? bold(green(name)) : green(name);\n}\n\n/**\n * Build a {@link MigrationListStyler} that decorates `migration list`\n * tokens with ANSI SGR codes. When `useColor` is `false` (non-TTY,\n * `--no-color`, `NO_COLOR=1`, piped output) the function returns the\n * shared identity styler so callers get plain text with zero ANSI\n * bytes — pipe-friendly by construction.\n *\n * Palette:\n *\n * - `dirName`: bold\n * - `sourceHash`: dim cyan\n * - `destHash`: bright cyan\n * - `kind` (`*` / `↩` / `⟲`): bright — the signal; lanes and arrows dim\n * - `glyph` (`→` / `⟲` / `∅`): dim\n * - `lane` (graph gutter lines `│` and fan/join connectors `├─┐` / `├─┘`): dim\n * - `invariants` (`{...}`): yellow\n * - `refs` (`(...)`): green; the `contract` desired-state marker inside is\n * green-bold (the active ref is bolded separately by the tree styler)\n * - `spaceHeading` (`<spaceId>:`): bold\n * - `summary`: dim\n * - `emptyState`: dim\n */\nexport function createAnsiMigrationListStyler(opts: {\n readonly useColor: boolean;\n}): MigrationListStyler {\n if (!opts.useColor) {\n return IDENTITY_MIGRATION_LIST_STYLER;\n }\n return {\n // Kind glyphs stay bright in both flat and graph views; lanes carry the dim gutter.\n kind: (text) => text,\n dirName: (text) => bold(text),\n sourceHash: (text) => dim(cyan(text)),\n destHash: (text) => cyanBright(text),\n glyph: (text) => dim(text),\n lane: (text) => dim(text),\n invariants: (ids) => yellow(`{${ids.join(', ')}}`),\n refs: (names) => {\n const open = green('(');\n const close = green(')');\n const separator = green(', ');\n return open + names.map(styleRefName).join(separator) + close;\n },\n spaceHeading: (text) => bold(text),\n summary: (text) => dim(text),\n emptyState: (text) => dim(text),\n };\n}\n"],"mappings":"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"}
|
|
@@ -765,4 +765,4 @@ function resolveBundleByPrefix(bundles, needle) {
|
|
|
765
765
|
//#endregion
|
|
766
766
|
export { formatMigrationPlanOutput as n, resolveBundleByPrefix as r, createMigrationPlanCommand as t };
|
|
767
767
|
|
|
768
|
-
//# sourceMappingURL=migration-plan-
|
|
768
|
+
//# sourceMappingURL=migration-plan-DHLa2Khm.mjs.map
|