prisma-next 0.8.0 → 0.9.0-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{cli-errors-D3_sMh2K.mjs → cli-errors-CF60g2cG.mjs} +40 -2
- package/dist/cli-errors-CF60g2cG.mjs.map +1 -0
- package/dist/cli.mjs +67 -19
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-BCnP7cHo.mjs → client-Brv4qlfB.mjs} +28 -30
- package/dist/client-Brv4qlfB.mjs.map +1 -0
- package/dist/{command-helpers-BeZHkxV8.mjs → command-helpers-D3vL5yi8.mjs} +29 -6
- package/dist/command-helpers-D3vL5yi8.mjs.map +1 -0
- package/dist/commands/contract-emit.mjs +1 -1
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.mjs +7 -7
- package/dist/commands/db-schema.mjs +5 -5
- package/dist/commands/db-sign.d.mts.map +1 -1
- package/dist/commands/db-sign.mjs +67 -25
- 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 +37 -9
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.d.mts.map +1 -1
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +28 -0
- package/dist/commands/migrate.d.mts.map +1 -0
- package/dist/commands/{migration-apply.mjs → migrate.mjs} +65 -39
- package/dist/commands/migrate.mjs.map +1 -0
- package/dist/commands/migration-check.d.mts +18 -0
- package/dist/commands/migration-check.d.mts.map +1 -0
- package/dist/commands/migration-check.mjs +284 -0
- package/dist/commands/migration-check.mjs.map +1 -0
- package/dist/commands/migration-graph.d.mts +16 -0
- package/dist/commands/migration-graph.d.mts.map +1 -0
- package/dist/commands/migration-graph.mjs +141 -0
- package/dist/commands/migration-graph.mjs.map +1 -0
- package/dist/commands/migration-list.d.mts +20 -0
- package/dist/commands/migration-list.d.mts.map +1 -0
- package/dist/commands/migration-list.mjs +107 -0
- package/dist/commands/migration-list.mjs.map +1 -0
- package/dist/commands/migration-log.d.mts +21 -0
- package/dist/commands/migration-log.d.mts.map +1 -0
- package/dist/commands/migration-log.mjs +146 -0
- package/dist/commands/migration-log.mjs.map +1 -0
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +30 -29
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +2 -2
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-show.d.mts +1 -1
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +90 -52
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +5 -17
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +732 -1
- package/dist/commands/migration-status.mjs.map +1 -0
- package/dist/commands/ref.d.mts +34 -0
- package/dist/commands/ref.d.mts.map +1 -0
- package/dist/commands/{migration-ref.mjs → ref.mjs} +28 -57
- package/dist/commands/ref.mjs.map +1 -0
- package/dist/{contract-emit-9DBda5Ou.mjs → contract-emit-C3STUIBg.mjs} +6 -6
- package/dist/{contract-emit-9DBda5Ou.mjs.map → contract-emit-C3STUIBg.mjs.map} +1 -1
- package/dist/{contract-emit-B77TsJqf.mjs → contract-emit-iynA3BCA.mjs} +9 -5
- package/dist/contract-emit-iynA3BCA.mjs.map +1 -0
- package/dist/{contract-infer-ByxhPjpW.mjs → contract-infer-Cnj8G1E2.mjs} +5 -5
- package/dist/{contract-infer-ByxhPjpW.mjs.map → contract-infer-Cnj8G1E2.mjs.map} +1 -1
- package/dist/{contract-space-aggregate-loader-BrwKK6Q6.mjs → contract-space-aggregate-loader-pAc8CDfY.mjs} +4 -4
- package/dist/{contract-space-aggregate-loader-BrwKK6Q6.mjs.map → contract-space-aggregate-loader-pAc8CDfY.mjs.map} +1 -1
- package/dist/{db-verify-Czm5T-J4.mjs → db-verify-D7cyH_zz.mjs} +12 -9
- package/dist/db-verify-D7cyH_zz.mjs.map +1 -0
- package/dist/errors-Cw6kyTyV.mjs +56 -0
- package/dist/errors-Cw6kyTyV.mjs.map +1 -0
- package/dist/exports/control-api.d.mts +1 -1
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +2 -2
- package/dist/exports/index.mjs +1 -1
- package/dist/exports/index.mjs.map +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{framework-components-ChqVUxR-.mjs → framework-components-xFLFpZUO.mjs} +2 -2
- package/dist/{framework-components-ChqVUxR-.mjs.map → framework-components-xFLFpZUO.mjs.map} +1 -1
- package/dist/{global-flags-Icqpxk23.d.mts → global-flags-DGmw6Kqg.d.mts} +1 -1
- package/dist/{global-flags-Icqpxk23.d.mts.map → global-flags-DGmw6Kqg.d.mts.map} +1 -1
- package/dist/{migration-status-By9G5p2H.mjs → graph-render-eJDcLWny.mjs} +3 -692
- package/dist/graph-render-eJDcLWny.mjs.map +1 -0
- package/dist/{init-B-k3a1Qw.mjs → init-Bqg5JWg7.mjs} +133 -61
- package/dist/init-Bqg5JWg7.mjs.map +1 -0
- package/dist/{inspect-live-schema-DxdBd4Er.mjs → inspect-live-schema-CWLK_lgs.mjs} +4 -4
- package/dist/{inspect-live-schema-DxdBd4Er.mjs.map → inspect-live-schema-CWLK_lgs.mjs.map} +1 -1
- package/dist/migration-cli.mjs +1 -1
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-BdV8JYXV.mjs → migration-command-scaffold-CmXXC1UZ.mjs} +4 -4
- package/dist/{migration-command-scaffold-BdV8JYXV.mjs.map → migration-command-scaffold-CmXXC1UZ.mjs.map} +1 -1
- package/dist/{migration-plan-mRu5K81L.mjs → migration-plan-CHyUlBV0.mjs} +76 -37
- package/dist/migration-plan-CHyUlBV0.mjs.map +1 -0
- package/dist/migration-types-D2FW63pr.d.mts +15 -0
- package/dist/migration-types-D2FW63pr.d.mts.map +1 -0
- package/dist/{migrations-CTsyBXCA.mjs → migrations-DyUf5lTt.mjs} +2 -2
- package/dist/migrations-DyUf5lTt.mjs.map +1 -0
- package/dist/{output-BVj6a971.mjs → output-B60Gw5fu.mjs} +12 -11
- package/dist/{output-BVj6a971.mjs.map → output-B60Gw5fu.mjs.map} +1 -1
- package/dist/{result-handler-rmPVKIP2.mjs → result-handler-Bm_6dDYg.mjs} +2 -2
- package/dist/{result-handler-rmPVKIP2.mjs.map → result-handler-Bm_6dDYg.mjs.map} +1 -1
- package/dist/{terminal-ui-C_hFNbAn.mjs → terminal-ui-XtOQsqe9.mjs} +2 -54
- package/dist/terminal-ui-XtOQsqe9.mjs.map +1 -0
- package/dist/{types-LItU7E4l.d.mts → types-0aS865QN.d.mts} +14 -8
- package/dist/types-0aS865QN.d.mts.map +1 -0
- package/dist/{verify-CiwNWM9N.mjs → verify-D7ypCCe6.mjs} +1 -1
- package/dist/{verify-CiwNWM9N.mjs.map → verify-D7ypCCe6.mjs.map} +1 -1
- package/package.json +10 -10
- package/dist/cli-errors-D3_sMh2K.mjs.map +0 -1
- package/dist/client-BCnP7cHo.mjs.map +0 -1
- package/dist/command-helpers-BeZHkxV8.mjs.map +0 -1
- package/dist/commands/migration-apply.d.mts +0 -51
- package/dist/commands/migration-apply.d.mts.map +0 -1
- package/dist/commands/migration-apply.mjs.map +0 -1
- package/dist/commands/migration-ref.d.mts +0 -45
- package/dist/commands/migration-ref.d.mts.map +0 -1
- package/dist/commands/migration-ref.mjs.map +0 -1
- package/dist/contract-emit-B77TsJqf.mjs.map +0 -1
- package/dist/db-verify-Czm5T-J4.mjs.map +0 -1
- package/dist/init-B-k3a1Qw.mjs.map +0 -1
- package/dist/migration-plan-mRu5K81L.mjs.map +0 -1
- package/dist/migration-status-By9G5p2H.mjs.map +0 -1
- package/dist/migrations-CTsyBXCA.mjs.map +0 -1
- package/dist/terminal-ui-C_hFNbAn.mjs.map +0 -1
- package/dist/types-LItU7E4l.d.mts.map +0 -1
- /package/dist/{cli-errors-B9OBbled.d.mts → cli-errors-DdcjVLJV.d.mts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-render-eJDcLWny.mjs","names":[],"sources":["../src/utils/formatters/graph-types.ts","../src/utils/formatters/graph-migration-mapper.ts","../src/utils/formatters/graph-render.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Generic graph renderer types — domain-agnostic\n// ---------------------------------------------------------------------------\n\n/** A typed marker attached to a node. Different marker kinds get different visual treatment. */\nexport type NodeMarker =\n | { readonly kind: 'db' }\n | { readonly kind: 'ref'; readonly name: string; readonly active?: boolean }\n | { readonly kind: 'contract'; readonly planned: boolean }\n | { readonly kind: 'custom'; readonly label: string };\n\n/** A node in the graph. Rendered as ○ with optional typed markers. */\nexport interface GraphNode {\n readonly id: string;\n /** Typed markers rendered inline on the node row */\n readonly markers?: readonly NodeMarker[] | undefined;\n /** Detached nodes use a dashed connector (e.g. unplanned contract) */\n readonly style?: 'normal' | 'detached' | undefined;\n}\n\n/** A directed edge between two nodes. Carries an optional label rendered on the edge line. */\nexport interface GraphEdge {\n readonly from: string;\n readonly to: string;\n /** Edge line text, e.g. \"20260101_init ✓ abc12..\" */\n readonly label?: string;\n /**\n * Visual color hint for the edge. Overrides the default role-based\n * coloring (spine/branch/backward) when set.\n *\n * - `'applied'` — cyan (CVD-safe: completed/done)\n * - `'pending'` — yellow (CVD-safe: waiting/upcoming)\n * - `'unreachable'` — magenta (CVD-safe: DB on a different branch)\n */\n readonly colorHint?: 'applied' | 'pending' | 'unreachable';\n /**\n * Edge rendering style.\n * - `'solid'` (default) — normal edge with `│` connector\n * - `'dashed'` — draft/phantom edge with `┊` connector, excluded from path resolution\n */\n readonly style?: 'solid' | 'dashed';\n}\n\n/**\n * Immutable directed graph with adjacency-list indexing.\n *\n * Built once from flat arrays of nodes and edges, then passed around as\n * the primary graph representation for rendering, traversal, truncation,\n * and subgraph extraction.\n */\nexport class RenderGraph {\n readonly nodes: readonly GraphNode[];\n readonly edges: readonly GraphEdge[];\n\n /** Forward adjacency: node id → outgoing edges. */\n readonly forward: ReadonlyMap<string, readonly GraphEdge[]>;\n /** Set of node ids that have at least one incoming edge. */\n readonly incomingNodes: ReadonlySet<string>;\n /** Node lookup by id. */\n readonly nodeById: ReadonlyMap<string, GraphNode>;\n\n constructor(nodes: readonly GraphNode[], edges: readonly GraphEdge[]) {\n this.nodes = nodes;\n this.edges = edges;\n\n const fwd = new Map<string, GraphEdge[]>();\n const inc = new Set<string>();\n const byId = new Map<string, GraphNode>();\n\n for (const n of nodes) {\n byId.set(n.id, n);\n }\n for (const e of edges) {\n const list = fwd.get(e.from);\n if (list) list.push(e);\n else fwd.set(e.from, [e]);\n inc.add(e.to);\n }\n\n this.forward = fwd;\n this.incomingNodes = inc;\n this.nodeById = byId;\n }\n\n /** Outgoing edges from a node (empty array if none). */\n outgoing(nodeId: string): readonly GraphEdge[] {\n return this.forward.get(nodeId) ?? [];\n }\n}\n\n/** Options controlling graph rendering. */\nexport interface GraphRenderOptions {\n /** Node id for the spine endpoint */\n readonly spineTarget: string;\n /** Root node id. Defaults to the node with no incoming edges. */\n readonly rootId?: string;\n /** Enable ANSI colour output */\n readonly colorize?: boolean;\n /** Terminal width. Default 80. */\n readonly maxWidth?: number;\n /**\n * Truncate to show at most this many spine edges. `undefined` = no truncation.\n * The effective limit expands to include any DB or contract markers on the spine.\n */\n readonly limit?: number | undefined;\n /**\n * Override dagre layout parameters. Merged over defaults:\n * `{ ranksep: 4, nodesep: 6, marginx: 2, marginy: 1 }`.\n *\n * When `ranksep` is ≤ 1, labels are placed inline on the arrowhead row\n * instead of in a separate label-placement pass (there isn't enough\n * vertical space for a dedicated label row).\n */\n readonly dagreOptions?: {\n readonly ranksep?: number;\n readonly nodesep?: number;\n readonly marginx?: number;\n readonly marginy?: number;\n };\n}\n","/**\n * Maps MigrationGraph + status info to the generic graph renderer types.\n */\nimport { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';\nimport type { MigrationGraph } from '@prisma-next/migration-tools/graph';\nimport { findPath } from '@prisma-next/migration-tools/migration-graph';\nimport { ifDefined } from '@prisma-next/utils/defined';\n\nimport type { StatusRef } from '../migration-types';\nimport {\n type GraphEdge,\n type GraphNode,\n type GraphRenderOptions,\n type NodeMarker,\n RenderGraph,\n} from './graph-types';\n\nexport type EdgeStatusKind = 'applied' | 'pending' | 'unreachable';\n\nconst STATUS_ICON: Record<EdgeStatusKind, string> = {\n applied: ' ✓',\n pending: ' ⧗',\n unreachable: ' ✗',\n};\n\n/** Shorten a contract hash for display: strip sha256: prefix, take 7 chars. */\nfunction shortHash(hash: string): string {\n const stripped = hash.startsWith('sha256:') ? hash.slice(7) : hash;\n return stripped.slice(0, 7);\n}\n\nfunction toShortId(hash: string): string {\n return hash === EMPTY_CONTRACT_HASH ? '∅' : shortHash(hash);\n}\n\n/** Minimal per-edge status from the CLI's status result. */\nexport interface EdgeStatus {\n readonly dirName: string;\n readonly status: EdgeStatusKind;\n}\n\nexport interface MigrationGraphInput {\n readonly graph: MigrationGraph;\n readonly mode: 'online' | 'offline';\n readonly markerHash?: string | undefined;\n readonly contractHash: string;\n readonly refs?: readonly StatusRef[] | undefined;\n readonly activeRefHash?: string | undefined;\n readonly activeRefName?: string | undefined;\n /**\n * Per-edge applied/pending status from the ledger. When provided, status\n * icons (✓/⧗) are baked into edge labels. Undefined in offline mode.\n */\n readonly edgeStatuses?: readonly EdgeStatus[] | undefined;\n}\n\nexport interface MigrationRenderInput {\n readonly graph: RenderGraph;\n readonly options: GraphRenderOptions;\n /** All relevant paths (root→contract, root→marker, root→ref). */\n readonly relevantPaths: readonly (readonly string[])[];\n}\n\n/**\n * Convert a MigrationGraph + status info into the generic graph renderer types.\n */\nexport function migrationGraphToRenderInput(input: MigrationGraphInput): MigrationRenderInput {\n const { graph, mode, markerHash, contractHash, refs, activeRefHash, edgeStatuses } = input;\n\n const statusByDirName = new Map(edgeStatuses?.map((e) => [e.dirName, e.status]));\n\n // Build nodes\n const nodeList: GraphNode[] = [];\n for (const nodeId of graph.nodes) {\n const markers: NodeMarker[] = [];\n\n // DB marker\n if (mode === 'online' && markerHash === nodeId) {\n markers.push({ kind: 'db' });\n }\n\n // Ref markers\n if (refs) {\n for (const ref of refs) {\n if (ref.hash === nodeId) {\n markers.push({ kind: 'ref', name: ref.name, active: ref.active });\n }\n }\n }\n\n // Contract marker\n if (contractHash === nodeId && contractHash !== EMPTY_CONTRACT_HASH) {\n markers.push({ kind: 'contract', planned: true });\n }\n\n nodeList.push({\n id: toShortId(nodeId),\n markers: markers.length > 0 ? markers : undefined,\n });\n }\n\n // Build edges\n const edgeList: GraphEdge[] = [];\n\n for (const [, entries] of graph.forwardChain) {\n for (const entry of entries) {\n const status = statusByDirName.get(entry.dirName);\n const icon = status ? STATUS_ICON[status] : '';\n const invariantsSuffix =\n entry.invariants.length > 0\n ? ` provides [${entry.invariants.map((id) => JSON.stringify(id)).join(', ')}]`\n : '';\n const label = `${entry.dirName}${icon}${invariantsSuffix}`;\n\n edgeList.push({\n from: toShortId(entry.from),\n to: toShortId(entry.to),\n label,\n ...ifDefined('colorHint', status),\n });\n }\n }\n\n // Compute paths to all interesting targets so the default view shows the\n // minimal subgraph that covers everything relevant: contract, DB marker, ref.\n const relevantPaths: string[][] = [];\n const rootId = EMPTY_CONTRACT_HASH;\n\n function addPathFromRoot(targetHash: string): void {\n if (!graph.nodes.has(targetHash)) return;\n const raw = findPath(graph, rootId, targetHash);\n if (raw && raw.length > 0) {\n relevantPaths.push([toShortId(rootId), ...raw.map((e) => toShortId(e.to))]);\n }\n }\n\n function addPathBetween(fromHash: string, toHash: string): void {\n /* v8 ignore next -- @preserve */\n if (!graph.nodes.has(fromHash) || !graph.nodes.has(toHash)) return;\n const raw = findPath(graph, fromHash, toHash);\n if (raw && raw.length > 0) {\n relevantPaths.push([toShortId(fromHash), ...raw.map((e) => toShortId(e.to))]);\n }\n }\n\n // 1. Path to the DB marker\n if (mode === 'online' && markerHash) {\n addPathFromRoot(markerHash);\n }\n\n // 2. Path to the ref\n if (activeRefHash && activeRefHash !== markerHash) {\n addPathFromRoot(activeRefHash);\n }\n\n // 3. Path(s) to the contract — prefer continuing from marker and/or ref\n // rather than an independent root→contract (which BFS may route through\n // an unrelated branch). When both marker and ref exist, try both paths\n // so that the diamond (two branches converging at the contract) is visible.\n if (contractHash !== EMPTY_CONTRACT_HASH) {\n let contractReached = false;\n\n if (markerHash && markerHash !== contractHash) {\n const markerReaches = findPath(graph, markerHash, contractHash);\n if (markerReaches) {\n addPathBetween(markerHash, contractHash);\n contractReached = true;\n }\n }\n\n if (activeRefHash && activeRefHash !== markerHash && activeRefHash !== contractHash) {\n const refReaches = findPath(graph, activeRefHash, contractHash);\n if (refReaches) {\n addPathBetween(activeRefHash, contractHash);\n contractReached = true;\n }\n }\n\n if (!contractReached && contractHash !== (markerHash ?? activeRefHash)) {\n addPathFromRoot(contractHash);\n }\n }\n\n // Fall back: if no paths were found, try the tip of the forward chain.\n if (relevantPaths.length === 0) {\n const lastEdge = [...graph.forwardChain.values()].flat().pop();\n const fallbackHash = lastEdge?.to ?? EMPTY_CONTRACT_HASH;\n addPathFromRoot(fallbackHash);\n }\n\n // Spine target for rendering (edge coloring, detached node alignment).\n let spineTargetHash: string;\n\n if (activeRefHash && graph.nodes.has(activeRefHash)) {\n spineTargetHash = activeRefHash;\n } else if (contractHash !== EMPTY_CONTRACT_HASH && graph.nodes.has(contractHash)) {\n spineTargetHash = contractHash;\n } else {\n const lastEdge = [...graph.forwardChain.values()].flat().pop();\n spineTargetHash = lastEdge?.to ?? EMPTY_CONTRACT_HASH;\n }\n\n // Contract not in the migration graph — connect from spine target with a\n // dashed edge so the user can see the gap (contract has changed but no\n // migration has been planned yet).\n if (contractHash !== EMPTY_CONTRACT_HASH && !graph.nodes.has(contractHash)) {\n const contractMarkers: NodeMarker[] = [];\n if (mode === 'online' && markerHash === contractHash) {\n contractMarkers.push({ kind: 'db' });\n }\n contractMarkers.push({ kind: 'contract', planned: false });\n nodeList.push({\n id: shortHash(contractHash),\n markers: contractMarkers,\n });\n\n if (graph.nodes.has(spineTargetHash) || spineTargetHash === EMPTY_CONTRACT_HASH) {\n edgeList.push({\n from: toShortId(spineTargetHash),\n to: shortHash(contractHash),\n style: 'dashed',\n });\n }\n }\n\n return {\n graph: new RenderGraph(nodeList, edgeList),\n options: {\n spineTarget: toShortId(spineTargetHash),\n rootId: '∅',\n colorize: true,\n },\n relevantPaths,\n };\n}\n","/**\n * Terminal graph renderer.\n *\n * Renders directed graphs as ASCII/box-drawing art for terminal output. Uses\n * dagre for automatic layout (rank assignment + coordinate placement), then\n * stamps the result onto a {@link CharGrid} — a sparse character canvas that\n * resolves box-drawing junctions, color priority, and label placement.\n *\n * ## Rendering pipeline\n *\n * 1. **Layout** — dagre assigns (x, y) coordinates to nodes and polyline\n * control points to edges. We use `rankdir: 'TB'` (top-to-bottom).\n * 2. **Orthogonalization** — dagre's polylines may contain diagonal segments.\n * {@link selectBestVariant} resolves each diagonal into an L-shaped bend,\n * enumerating all 2^N combinations and picking the variant with fewest\n * corners and shortest total length.\n * 3. **Edge stamping** — orthogonal segments are stamped onto the CharGrid as\n * directional bitmasks. The grid resolves overlapping directions into the\n * correct box-drawing character (│, ─, ┌, ┼, etc.).\n * 4. **Label placement** — edge labels are placed adjacent to their polyline\n * segments, preferring horizontal (branch-specific) segments over shared\n * vertical trunks to avoid ambiguity.\n * 5. **Arrowheads** — ▾ ▴ ◂ ▸ placed one cell before the terminal point.\n * 6. **Node stamping** — `○ nodeId` with inline marker tags (db, contract,\n * ref names).\n * 7. **Elided indicator** — when truncation is active, `┊ (N earlier\n * migrations)` is stamped above the visible root.\n * 8. **Detached nodes** — rendered below the graph with `◇` and a dotted\n * connector.\n *\n * ## Graph filtering\n *\n * The caller controls what graph is rendered: the full graph, or a subgraph\n * extracted via {@link extractRelevantSubgraph} (union of relevant paths).\n * The renderer itself is agnostic — it renders whatever graph it receives.\n *\n * Truncation is supported via `options.limit`.\n *\n * ## Color accessibility\n *\n * Uses a CVD-safe palette — no red/green contrast. Shape and icon always\n * carry meaning; color only reinforces.\n */\nimport dagre from '@dagrejs/dagre';\nimport { bold, cyan, dim, magenta, yellow } from 'colorette';\nimport {\n type GraphEdge,\n type GraphNode,\n type GraphRenderOptions,\n type NodeMarker,\n RenderGraph,\n} from './graph-types';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** A 2D point on the character grid (integer coordinates). */\ninterface Point {\n x: number;\n y: number;\n}\n\n/** An orthogonal line segment between two points on the character grid. */\ninterface Segment {\n readonly from: Point;\n readonly to: Point;\n}\n\nfunction segment(from: Point, to: Point): Segment {\n return { from, to };\n}\n\nfunction isVertical(seg: Segment): boolean {\n return seg.from.x === seg.to.x;\n}\n\nfunction manhattanLength(seg: Segment): number {\n return Math.abs(seg.to.x - seg.from.x) + Math.abs(seg.to.y - seg.from.y);\n}\n\n/** A function that wraps a string with an ANSI color escape sequence. */\ntype ColorFn = (s: string) => string;\n\n// ---------------------------------------------------------------------------\n// CVD-safe color palette\n//\n// No red/green contrast. Shape/icon always carries meaning; color reinforces.\n// ---------------------------------------------------------------------------\n\n/** Color functions for each semantic role in the graph. */\ninterface GraphColors {\n spine: ColorFn;\n branch: ColorFn;\n backward: ColorFn;\n applied: ColorFn;\n pending: ColorFn;\n unreachable: ColorFn;\n node: ColorFn;\n label: ColorFn;\n marker: ColorFn;\n /** Rotating color for ref markers — cycles through the palette by index. */\n ref: (index: number) => ColorFn;\n}\n\n/** Rotating palette for ref marker names, cycling through these for each ref. */\nconst REF_COLORS: ColorFn[] = [yellow, magenta, bold, cyan];\n\n/** Build the color palette, respecting the `colorize` flag. When false, all color functions become identity. */\nfunction buildColors(colorize: boolean): GraphColors {\n const c = (fn: ColorFn): ColorFn => (colorize ? fn : (s) => s);\n return {\n spine: c(cyan),\n branch: c(dim),\n backward: c(magenta),\n applied: c(cyan),\n pending: c(yellow),\n unreachable: c(magenta),\n node: c(cyan),\n label: c(dim),\n marker: c(bold),\n ref: (index: number) => c(REF_COLORS[index % REF_COLORS.length]!),\n };\n}\n\n/** Map a `colorHint` value to its color function, or `undefined` for no hint. */\nfunction resolveHintColor(hint: GraphEdge['colorHint'], colors: GraphColors): ColorFn | undefined {\n if (hint === 'applied') return colors.applied;\n if (hint === 'pending') return colors.pending;\n if (hint === 'unreachable') return colors.unreachable;\n return undefined;\n}\n\n/**\n * Edge drawing priorities — higher priority wins when edges overlap on the\n * same grid cell. Backward edges are drawn on top so rollback paths remain\n * visible over spine and branch edges.\n */\nconst PRIORITY = {\n branch: 1,\n spine: 2,\n backward: 3,\n} as const;\n\n// ---------------------------------------------------------------------------\n// Direction bitmask → box-drawing character\n//\n// Each grid cell accumulates a bitmask of connected directions (UP, DOWN,\n// LEFT, RIGHT). The bitmask is then mapped to the appropriate Unicode\n// box-drawing character. For example, UP|RIGHT → └, all four → ┼.\n// ---------------------------------------------------------------------------\n\nconst DIR = {\n up: 1,\n down: 2,\n left: 4,\n right: 8,\n dashed: 16,\n} as const;\n\n/** Arrow characters for edge termination (one cell before the target node). */\nconst ARROW = { up: '▴', down: '▾', left: '◂', right: '▸' };\n\n/** Maps a direction bitmask to its box-drawing character. */\nconst BOX_CHAR: Record<number, string> = {\n 0: ' ',\n [DIR.up]: '│',\n [DIR.down]: '│',\n [DIR.up | DIR.down]: '│',\n [DIR.left]: '─',\n [DIR.right]: '─',\n [DIR.left | DIR.right]: '─',\n [DIR.down | DIR.right]: '┌',\n [DIR.down | DIR.left]: '┐',\n [DIR.up | DIR.right]: '└',\n [DIR.up | DIR.left]: '┘',\n [DIR.up | DIR.down | DIR.right]: '├',\n [DIR.up | DIR.down | DIR.left]: '┤',\n [DIR.left | DIR.right | DIR.down]: '┬',\n [DIR.left | DIR.right | DIR.up]: '┴',\n [DIR.up | DIR.down | DIR.left | DIR.right]: '┼',\n // Dashed variants — straight segments only, corners fall back to solid\n [DIR.up | DIR.dashed]: '┊',\n [DIR.down | DIR.dashed]: '┊',\n [DIR.up | DIR.down | DIR.dashed]: '┊',\n [DIR.left | DIR.dashed]: '┈',\n [DIR.right | DIR.dashed]: '┈',\n [DIR.left | DIR.right | DIR.dashed]: '┈',\n};\n\n// ---------------------------------------------------------------------------\n// Inline marker tags\n//\n// Markers (db, contract, ref, custom) are rendered inline after the node id:\n// ○ abc1234 ◆ db prod\n// ---------------------------------------------------------------------------\n\n/** A single rendered tag: the display text and the color function to apply. */\ninterface InlineTag {\n text: string;\n color: ColorFn;\n}\n\n/**\n * Convert a node's markers into renderable inline tags.\n *\n * - `db` → `◆ db`\n * - `contract` → `◆ contract` (applied) or `◇ contract` (planned)\n * - `ref` → the ref name, colored from the rotating {@link REF_COLORS} palette\n * - `custom` → the custom label\n */\nfunction buildInlineTags(markers: readonly NodeMarker[], colors: GraphColors): InlineTag[] {\n const tags: InlineTag[] = [];\n const refNames = markers\n .filter((m): m is NodeMarker & { kind: 'ref' } => m.kind === 'ref')\n .map((m) => m.name);\n\n for (const m of markers) {\n if (m.kind === 'db') {\n tags.push({ text: '◆ db', color: colors.marker });\n } else if (m.kind === 'contract') {\n tags.push({ text: m.planned ? '◆ contract' : '◇ contract', color: colors.marker });\n } else if (m.kind === 'ref') {\n tags.push({ text: m.name, color: colors.ref(refNames.indexOf(m.name)) });\n } else if (m.kind === 'custom') {\n tags.push({ text: m.label, color: colors.marker });\n }\n }\n return tags;\n}\n\n/** Total character width of inline tags including leading spaces (0 if no tags). */\nfunction inlineTagsWidth(tags: InlineTag[]): number {\n if (tags.length === 0) return 0;\n return tags.reduce((w, t) => w + 1 + t.text.length, 0);\n}\n\n// ---------------------------------------------------------------------------\n// Character grid with color priority\n//\n// The grid is the central rendering canvas. It supports two layers:\n//\n// 1. **Connections** — direction bitmasks at (x, y) cells, resolved to\n// box-drawing characters at render time. When multiple edges cross the\n// same cell, their direction bits are OR'd together (e.g. UP|RIGHT → └).\n// Color follows a priority system so higher-priority edges (backward >\n// spine > branch) visually dominate at intersections.\n//\n// 2. **Text stamps** — literal characters placed at (x, y), such as node\n// ids, labels, arrowheads, and markers. Text stamps override connections\n// at the same position.\n//\n// The grid also tracks **reserved areas** (node label regions) so that\n// label-placement heuristics can avoid overlapping node text.\n// ---------------------------------------------------------------------------\n\n/** Tracks the winning color for a grid cell based on edge priority. */\ninterface CellColor {\n color: ColorFn | undefined;\n priority: number;\n}\n\n/**\n * Sparse character canvas for terminal graph rendering.\n *\n * Coordinates are unbounded integers — the grid auto-expands as content is\n * added and trims to the bounding box on {@link render}.\n */\nclass CharGrid {\n private connections = new Map<string, number>();\n private cellColors = new Map<string, CellColor>();\n private chars = new Map<string, { ch: string; color: ColorFn | undefined }>();\n private reserved = new Set<string>();\n private minX = Number.POSITIVE_INFINITY;\n private maxX = Number.NEGATIVE_INFINITY;\n private minY = Number.POSITIVE_INFINITY;\n private maxY = Number.NEGATIVE_INFINITY;\n\n private key(x: number, y: number): string {\n return `${x},${y}`;\n }\n\n /** Expand the bounding box to include (x, y). */\n private touch(x: number, y: number): void {\n if (x < this.minX) this.minX = x;\n if (x > this.maxX) this.maxX = x;\n if (y < this.minY) this.minY = y;\n if (y > this.maxY) this.maxY = y;\n }\n\n /**\n * Add a directional connection at (x, y). Multiple calls at the same cell\n * are OR'd together — e.g. `addConnection(x, y, UP)` then\n * `addConnection(x, y, RIGHT)` produces a └ corner. Color follows\n * priority: higher-priority edges win at shared cells.\n */\n addConnection(\n x: number,\n y: number,\n dir: number,\n color?: ColorFn,\n priority: number = PRIORITY.branch,\n ): void {\n this.touch(x, y);\n const k = this.key(x, y);\n this.connections.set(k, (this.connections.get(k) ?? 0) | dir);\n const existing = this.cellColors.get(k);\n if (!existing || priority >= existing.priority) {\n this.cellColors.set(k, { color, priority });\n }\n }\n\n /** Stamp a horizontal edge segment from x1 to x2 at row y. */\n markHorizontal(\n y: number,\n x1: number,\n x2: number,\n color?: ColorFn,\n priority?: number,\n extraBits = 0,\n ): void {\n const lo = Math.min(x1, x2);\n const hi = Math.max(x1, x2);\n /* v8 ignore next -- @preserve */\n if (lo === hi) return;\n this.addConnection(lo, y, DIR.right | extraBits, color, priority);\n for (let x = lo + 1; x < hi; x++)\n this.addConnection(x, y, DIR.left | DIR.right | extraBits, color, priority);\n this.addConnection(hi, y, DIR.left | extraBits, color, priority);\n }\n\n /** Stamp a vertical edge segment from y1 to y2 at column x. */\n markVertical(\n x: number,\n y1: number,\n y2: number,\n color?: ColorFn,\n priority?: number,\n extraBits = 0,\n ): void {\n const lo = Math.min(y1, y2);\n const hi = Math.max(y1, y2);\n /* v8 ignore next -- @preserve */\n if (lo === hi) return;\n this.addConnection(x, lo, DIR.down | extraBits, color, priority);\n for (let y = lo + 1; y < hi; y++)\n this.addConnection(x, y, DIR.up | DIR.down | extraBits, color, priority);\n this.addConnection(x, hi, DIR.up | extraBits, color, priority);\n }\n\n /** Place literal text at (x, y). Each character occupies one cell. Text stamps override connections. */\n stampText(x: number, y: number, text: string, color?: ColorFn): void {\n for (let i = 0; i < text.length; i++) {\n const cx = x + i;\n this.touch(cx, y);\n this.chars.set(this.key(cx, y), { ch: text[i]!, color });\n }\n }\n\n /** True if (x, y) has stamped text or is in a reserved area (node labels). */\n hasLabel(x: number, y: number): boolean {\n return this.chars.has(this.key(x, y)) || this.reserved.has(this.key(x, y));\n }\n\n /** True if (x, y) has any directional connection (an edge passes through). */\n hasConnection(x: number, y: number): boolean {\n return (this.connections.get(this.key(x, y)) ?? 0) !== 0;\n }\n\n /** True if (x, y) has stamped text (not just a reserved area). */\n hasText(x: number, y: number): boolean {\n return this.chars.has(this.key(x, y));\n }\n\n /** Reserve a horizontal span so label placement avoids it. Used for node id + marker regions. */\n reserveArea(x: number, y: number, width: number): void {\n for (let i = 0; i < width; i++) this.reserved.add(this.key(x + i, y));\n }\n\n /** The largest y coordinate with content — used for positioning detached nodes below the graph. */\n getMaxY(): number {\n return this.maxY;\n }\n\n /**\n * Render the grid to a multi-line string.\n *\n * Iterates row by row over the bounding box, resolving each cell to either\n * its stamped text character or the box-drawing character for its\n * connection bitmask. Consecutive characters with the same color are\n * batched into a single ANSI-wrapped run for efficiency.\n */\n render(): string {\n /* v8 ignore next -- @preserve */\n if (this.minX === Number.POSITIVE_INFINITY) return '(empty)';\n\n const rows: string[] = [];\n for (let y = this.minY; y <= this.maxY; y++) {\n let row = '';\n let runChars = '';\n let runColor: ColorFn | undefined;\n\n const flush = () => {\n if (runChars.length === 0) return;\n row += runColor ? runColor(runChars) : runChars;\n runChars = '';\n };\n\n for (let x = this.minX; x <= this.maxX; x++) {\n const k = this.key(x, y);\n let ch: string;\n let color: ColorFn | undefined;\n\n const label = this.chars.get(k);\n if (label) {\n ch = label.ch;\n color = label.color;\n } else {\n const conn = this.connections.get(k) ?? 0;\n // Dashed corners don't exist — strip the bit and fall back to solid\n ch = BOX_CHAR[conn] ?? BOX_CHAR[conn & ~DIR.dashed] ?? ' ';\n color = conn === 0 ? undefined : this.cellColors.get(k)?.color;\n }\n\n if (color !== runColor) {\n flush();\n runColor = color;\n }\n runChars += ch;\n }\n flush();\n rows.push(row.trimEnd());\n }\n\n while (rows.length > 0 && rows[rows.length - 1] === '') rows.pop();\n return rows.join('\\n');\n }\n}\n\n// ---------------------------------------------------------------------------\n// Spine detection — BFS shortest path from root to target\n//\n// The renderer operates on generic GraphNode/GraphEdge, not MigrationGraph,\n// so it cannot use domain-specific pathfinding. These two BFS functions\n// re-derive the spine from the generic edge list.\n// ---------------------------------------------------------------------------\n\n/**\n * Find the set of edge keys (`\"from→to\"`) on the shortest path from\n * `rootId` to `targetId`. Used to color spine edges distinctly from\n * branch edges in the rendered output.\n *\n * Returns an empty set if no path exists.\n */\nfunction findSpineEdges(graph: RenderGraph, rootId: string, targetId: string): Set<string> {\n const visited = new Set([rootId]);\n const parent = new Map<string, GraphEdge>();\n const queue = [rootId];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n if (current === targetId) {\n const spineEdges = new Set<string>();\n let node = targetId;\n while (parent.has(node)) {\n const edge = parent.get(node)!;\n spineEdges.add(`${edge.from}→${edge.to}`);\n node = edge.from;\n }\n return spineEdges;\n }\n for (const edge of graph.outgoing(current)) {\n if (!visited.has(edge.to)) {\n visited.add(edge.to);\n parent.set(edge.to, edge);\n queue.push(edge.to);\n }\n }\n }\n return new Set();\n}\n\n// ---------------------------------------------------------------------------\n// Orthogonal polyline builder — variant-based\n//\n// Dagre produces polyline control points that may contain diagonal segments\n// (two consecutive points that differ in both x and y). Terminal rendering\n// requires strictly orthogonal segments (horizontal or vertical only).\n//\n// To resolve diagonals, we insert an L-shaped bend at each one. Each diagonal\n// has two possible resolutions (horizontal-first or vertical-first), so N\n// diagonals produce 2^N candidate polylines. We enumerate all variants and\n// pick the one with the fewest corners and shortest total length.\n// ---------------------------------------------------------------------------\n\n/**\n * Prepend `src` and append `tgt` to dagre's control points, round to\n * integers, and deduplicate consecutive identical points.\n */\nfunction prepareRawPoints(src: Point, dagrePoints: Point[], tgt: Point): Point[] {\n const raw = [src, ...dagrePoints, tgt];\n const rounded = raw.map((p) => ({ x: Math.round(p.x), y: Math.round(p.y) }));\n const deduped: Point[] = [rounded[0]!];\n for (let i = 1; i < rounded.length; i++) {\n const prev = deduped[deduped.length - 1]!;\n const curr = rounded[i]!;\n if (curr.x !== prev.x || curr.y !== prev.y) deduped.push(curr);\n }\n return deduped;\n}\n\nfunction countDiagonals(points: Point[]): number {\n let count = 0;\n for (let i = 1; i < points.length; i++) {\n const prev = points[i - 1]!;\n const curr = points[i]!;\n if (prev.x !== curr.x && prev.y !== curr.y) count++;\n }\n return count;\n}\n\ntype BendDirection = 'horizontal-first' | 'vertical-first';\n\n/**\n * Decode a bitmask into an array of bend directions.\n *\n * Each bit selects how one diagonal is converted to an orthogonal corner:\n * 0 → horizontal-first, 1 → vertical-first. Used by {@link selectBestVariant}\n * to enumerate all 2^N combinations.\n */\nfunction bitsToBends(bits: number, count: number): BendDirection[] {\n const bends: BendDirection[] = [];\n for (let k = 0; k < count; k++) {\n bends.push((bits >> k) & 1 ? 'vertical-first' : 'horizontal-first');\n }\n return bends;\n}\n\n/**\n * Build one polyline variant by resolving each diagonal with the given bend direction.\n *\n * Diagonals are detected inline: when the last emitted point and the next\n * input point differ in both x and y, the segment is diagonal and is\n * resolved using the next value from `bends`. The result is deduplicated\n * to remove zero-length segments created when a bend coincides with an\n * adjacent point.\n */\nfunction buildVariant(points: Point[], bends: BendDirection[]): Point[] {\n /* v8 ignore next -- @preserve */\n if (points.length < 2) return points;\n\n let bendIdx = 0;\n const result: Point[] = [points[0]!];\n for (let i = 1; i < points.length; i++) {\n const prev = result[result.length - 1]!;\n const curr = points[i]!;\n\n if (prev.x === curr.x || prev.y === curr.y) {\n result.push(curr);\n } else {\n const bend = bends[bendIdx++] ?? 'horizontal-first';\n if (bend === 'horizontal-first') {\n result.push({ x: curr.x, y: prev.y });\n } else {\n result.push({ x: prev.x, y: curr.y });\n }\n result.push(curr);\n }\n }\n\n const final: Point[] = [result[0]!];\n for (let i = 1; i < result.length; i++) {\n const prev = final[final.length - 1]!;\n const curr = result[i]!;\n if (curr.x !== prev.x || curr.y !== prev.y) final.push(curr);\n }\n return final;\n}\n\n/** Count the number of direction changes (corners) in an orthogonal polyline. */\nfunction countCorners(poly: Point[]): number {\n let corners = 0;\n for (let i = 1; i < poly.length - 1; i++) {\n const a = poly[i - 1]!;\n const b = poly[i]!;\n const c = poly[i + 1]!;\n const d1Vert = a.x === b.x;\n const d2Vert = b.x === c.x;\n if (d1Vert !== d2Vert) corners++;\n }\n return corners;\n}\n\n/** Manhattan length of a polyline (sum of absolute x and y deltas). */\nfunction polyLength(poly: Point[]): number {\n let len = 0;\n for (let i = 0; i < poly.length - 1; i++) {\n len += Math.abs(poly[i + 1]!.x - poly[i]!.x) + Math.abs(poly[i + 1]!.y - poly[i]!.y);\n }\n return len;\n}\n\n// ---------------------------------------------------------------------------\n// Label placement\n//\n// Edge labels (migration names) are placed adjacent to polyline segments.\n// The algorithm generates candidate positions along each segment, scores\n// them, and picks the best. Key heuristics:\n//\n// - **Horizontal segment preference**: when a polyline has both vertical and\n// horizontal segments, horizontal segments are boosted because they\n// uniquely identify a branch, while vertical segments often share column\n// space with the trunk. This prevents labels from \"jumping\" when node\n// widths change.\n// - **Source adjacency penalty**: positions within ±1 row of the source node\n// are penalized — labels there look like they belong to an incoming edge.\n// - **Whitespace bonus**: positions with clear space above and below score\n// higher for readability.\n// ---------------------------------------------------------------------------\n\n/**\n * Find the best (x, y) position to place an edge label adjacent to its\n * polyline. Returns null if no collision-free position exists.\n *\n * @param poly - The orthogonalized polyline for the edge.\n * @param label - The label text to place.\n * @param grid - The character grid (used for collision checks).\n * @param srcY - Y coordinate of the source node (for adjacency penalty).\n */\nfunction findLabelPlacement(\n poly: Point[],\n label: string,\n grid: CharGrid,\n srcY?: number,\n): Point | undefined {\n const segments = polyToSegments(poly);\n\n let best: (Point & { score: number }) | undefined;\n\n for (const seg of segments) {\n const candidates = segmentLabelCandidates(seg, label.length);\n for (const pos of candidates) {\n if (labelCollides(grid, pos.x, pos.y, label)) continue;\n const score = scoreLabelCandidate(pos, seg, segments, label, grid, srcY);\n if (!best || score > best.score) best = { x: pos.x, y: pos.y, score };\n }\n }\n\n return best;\n}\n\n/** Convert a polyline into non-zero-length segments. */\nfunction polyToSegments(poly: readonly Point[]): Segment[] {\n const segments: Segment[] = [];\n for (let i = 0; i < poly.length - 1; i++) {\n const seg = segment(poly[i]!, poly[i + 1]!);\n if (manhattanLength(seg) > 0) segments.push(seg);\n }\n return segments;\n}\n\n/**\n * Generate all candidate (x, y) positions for placing a label adjacent\n * to a single segment. Positions are perpendicular to the segment:\n *\n * - Vertical segments: left and right, at every y along the segment.\n * - Horizontal segments: above and below, at every x where the label fits.\n */\nfunction segmentLabelCandidates(seg: Segment, labelLen: number): Point[] {\n const candidates: Point[] = [];\n\n if (isVertical(seg)) {\n const minY = Math.min(seg.from.y, seg.to.y);\n const maxY = Math.max(seg.from.y, seg.to.y);\n for (const x of [seg.from.x + 2, seg.from.x - labelLen - 1]) {\n for (let y = minY; y <= maxY; y++) {\n candidates.push({ x, y });\n }\n }\n } else {\n const minX = Math.min(seg.from.x, seg.to.x);\n const maxX = Math.max(seg.from.x, seg.to.x);\n for (const dy of [-1, 1]) {\n const y = seg.from.y + dy;\n for (let x = minX; x <= maxX - labelLen + 1; x++) {\n candidates.push({ x, y });\n }\n }\n }\n\n return candidates;\n}\n\n/**\n * Score a candidate label position. Higher is better.\n *\n * Combines: segment length, surrounding whitespace, distance from\n * segment midpoint, source-node proximity penalty, and segment-position\n * bonus (horizontal/later segments preferred when the edge has bends).\n *\n * @param pos - Candidate position (top-left corner of the label text).\n * @param seg - The segment this candidate is adjacent to.\n * @param allSegments - All segments of the edge polyline (for segment-position bonus).\n * @param label - The label text (used for width and whitespace probing).\n * @param grid - The character grid (used for whitespace checks).\n * @param srcY - Y coordinate of the edge's source node (penalizes labels\n * that would appear to belong to the node rather than the edge).\n */\nfunction scoreLabelCandidate(\n pos: Point,\n seg: Segment,\n allSegments: readonly Segment[],\n label: string,\n grid: CharGrid,\n srcY?: number,\n): number {\n const len = manhattanLength(seg);\n const midX = Math.round((seg.from.x + seg.to.x) / 2);\n const midY = Math.round((seg.from.y + seg.to.y) / 2);\n\n let score = len;\n\n // Whitespace above/below the label improves readability.\n for (let dy = 1; dy <= 2; dy++) {\n if (!rowHasContent(grid, pos.x, pos.y - dy, label.length)) score += 3;\n if (!rowHasContent(grid, pos.x, pos.y + dy, label.length)) score += 3;\n }\n\n // Prefer positions near the segment midpoint.\n const labelCenterX = pos.x + Math.floor(label.length / 2);\n score -= (Math.abs(labelCenterX - midX) + Math.abs(pos.y - midY)) * 2;\n\n const labelCenterY = pos.y + Math.floor(label.length / 2);\n score -= (Math.abs(labelCenterY - midY) + Math.abs(pos.x - midX)) * 2;\n\n // Prefer labels to the right of a vertical segment\n if (isVertical(seg) && pos.x > seg.from.x) {\n score += 10;\n }\n\n // Penalize positions adjacent to the source node — labels there\n // look like they belong to the incoming edge above.\n if (srcY !== undefined && Math.abs(pos.y - srcY) <= 1) score -= 20;\n\n // Horizontal segments uniquely identify a branch, while the initial\n // vertical drop from the source often shares column space with the trunk.\n // Boost horizontal and later segments so labels land on the branch.\n const hasHorizontalSeg = allSegments.some((s) => !isVertical(s));\n if (hasHorizontalSeg) {\n if (!isVertical(seg)) score += 15;\n const segIndex = allSegments.indexOf(seg);\n score += (segIndex / allSegments.length) * 5;\n }\n\n return score;\n}\n\n/** True if any cell in the horizontal span [x, x+width) at row y has content. */\nfunction rowHasContent(grid: CharGrid, x: number, y: number, width: number): boolean {\n for (let i = 0; i < width; i++) {\n if (grid.hasLabel(x + i, y) || grid.hasConnection(x + i, y)) return true;\n }\n return false;\n}\n\n/**\n * Check if placing `text` at (x, y) would collide with existing content.\n * Checks one cell of padding on each side to keep labels visually separated.\n */\nfunction labelCollides(grid: CharGrid, x: number, y: number, text: string): boolean {\n for (let i = -1; i <= text.length; i++) {\n const cx = x + i;\n if (grid.hasLabel(cx, y)) return true;\n if (i >= 0 && i < text.length && grid.hasConnection(cx, y)) return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Joint variant selection\n//\n// Combines diagonal resolution with label placement in a single pass. For\n// each of the 2^N polyline variants, we compute a score based on corner\n// count, total length, and whether the label can be placed. The variant\n// with the lowest score wins.\n// ---------------------------------------------------------------------------\n\n/** A resolved polyline paired with its best label position (if any). */\ninterface PolyWithLabel {\n poly: Point[];\n labelPos: Point | undefined;\n}\n\n/**\n * Resolve dagre's polyline into the best orthogonal variant and find the\n * optimal label position in a single pass.\n *\n * Enumerates all 2^N diagonal resolutions, scores each by:\n * - `corners * 10` — fewer corners preferred\n * - `+ manhattan length` — shorter paths preferred\n * - `+ 100` penalty if the label couldn't be placed\n *\n * For edges with no diagonals, the polyline is used as-is.\n */\nfunction selectBestVariant(\n src: Point,\n dagrePoints: Point[],\n tgt: Point,\n label: string | undefined,\n grid: CharGrid,\n): PolyWithLabel {\n const rawPoints = prepareRawPoints(src, dagrePoints, tgt);\n const diagCount = countDiagonals(rawPoints);\n\n if (diagCount === 0) {\n const poly = buildVariant(rawPoints, []);\n const labelPos = label ? findLabelPlacement(poly, label, grid, src.y) : undefined;\n return { poly, labelPos };\n }\n\n // Each diagonal must be converted to an orthogonal corner — either\n // horizontal-first or vertical-first. With N diagonals that's 2^N\n // combinations, enumerated via bitmask: we count from 0 to 2^N - 1,\n // and every number represents a different combination of bend directions.\n const numVariants = 1 << diagCount;\n let bestPoly: Point[] | null = null;\n let bestLabel: Point | undefined;\n let bestScore = Number.POSITIVE_INFINITY;\n\n for (let bits = 0; bits < numVariants; bits++) {\n const bends = bitsToBends(bits, diagCount);\n const poly = buildVariant(rawPoints, bends);\n\n const corners = countCorners(poly);\n const len = polyLength(poly);\n const labelPos = label ? findLabelPlacement(poly, label, grid, src.y) : undefined;\n\n const labelPenalty = label && !labelPos ? 100 : 0;\n const score = corners * 10 + len + labelPenalty;\n\n if (score < bestScore) {\n bestScore = score;\n bestPoly = poly;\n bestLabel = labelPos;\n }\n }\n\n return {\n poly: bestPoly ?? buildVariant(rawPoints, bitsToBends(0, diagCount)),\n labelPos: bestLabel,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Subgraph extraction\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the subgraph containing only the nodes and forward-moving edges\n * along the given path.\n *\n * Backward (rollback) edges are excluded even if both endpoints are on the\n * path — only edges where `from` precedes `to` in path order are kept.\n */\nexport function extractSubgraph(graph: RenderGraph, path: readonly string[]): RenderGraph {\n const pathIndex = new Map(path.map((id, i) => [id, i]));\n const nodeSet = new Set(path);\n // Always keep dashed edges and their endpoints\n for (const e of graph.edges) {\n if (e.style === 'dashed') {\n nodeSet.add(e.from);\n nodeSet.add(e.to);\n }\n }\n const filteredNodes = graph.nodes.filter((n) => nodeSet.has(n.id));\n const filteredEdges = graph.edges.filter((e) => {\n if (e.style === 'dashed') return true;\n const fromIdx = pathIndex.get(e.from);\n const toIdx = pathIndex.get(e.to);\n return fromIdx !== undefined && toIdx !== undefined && fromIdx < toIdx;\n });\n return new RenderGraph(filteredNodes, filteredEdges);\n}\n\n/**\n * Extract the subgraph covering the union of multiple paths.\n *\n * Each path is an ordered list of node ids (root → target). The result\n * contains every node on any path plus every forward edge between\n * consecutive nodes on any path. Detached nodes are always included.\n *\n * When all paths overlap (the common case), the result is identical to\n * a single-path extract. When paths diverge (e.g. DB marker on a\n * different branch than the contract), the result naturally includes the\n * fork and both branches — exactly the minimal information needed.\n */\nexport function extractRelevantSubgraph(\n graph: RenderGraph,\n paths: readonly (readonly string[])[],\n): RenderGraph {\n const nodeSet = new Set<string>();\n const edgePairs = new Set<string>();\n\n for (const path of paths) {\n for (let i = 0; i < path.length; i++) {\n nodeSet.add(path[i]!);\n if (i > 0) {\n edgePairs.add(`${path[i - 1]!}\\0${path[i]!}`);\n }\n }\n }\n\n // Always keep dashed (draft) edges and their endpoints\n const dashedEdges = graph.edges.filter((e) => e.style === 'dashed');\n for (const e of dashedEdges) {\n nodeSet.add(e.from);\n nodeSet.add(e.to);\n }\n\n const filteredNodes = graph.nodes.filter((n) => nodeSet.has(n.id));\n const filteredEdges = graph.edges.filter(\n (e) => edgePairs.has(`${e.from}\\0${e.to}`) || e.style === 'dashed',\n );\n return new RenderGraph(filteredNodes, filteredEdges);\n}\n\n// ---------------------------------------------------------------------------\n// Truncation — keep last N spine edges, expand for markers\n// ---------------------------------------------------------------------------\n\n/** Result of {@link truncateGraph} — the visible subgraph plus truncation metadata. */\nexport interface TruncationResult {\n readonly graph: RenderGraph;\n /** Number of spine edges hidden by truncation (0 = nothing truncated). */\n readonly elidedCount: number;\n /** The visible portion of the spine (subset of the input spine). */\n readonly spine: readonly string[];\n}\n\n/**\n * Truncate a graph to the last `limit` spine edges from the spine target.\n * The window expands to include any node carrying a db or contract marker\n * so those are never truncated away.\n *\n * For the full graph: keeps all branches that fork from the visible spine window.\n * For the spine view: caller should call extractSubgraph first, then truncate.\n */\nexport function truncateGraph(\n graph: RenderGraph,\n spine: readonly string[],\n limit: number,\n): TruncationResult {\n if (spine.length <= 1 || limit >= spine.length - 1) {\n return { graph, elidedCount: 0, spine };\n }\n\n // Find the earliest spine node that has a db or contract marker\n let earliestMarkerIdx = spine.length;\n for (let i = 0; i < spine.length; i++) {\n const n = graph.nodeById.get(spine[i]!);\n if (n?.markers?.some((m) => m.kind === 'db' || m.kind === 'contract')) {\n earliestMarkerIdx = i;\n break;\n }\n }\n\n // Effective limit: expand to include markers\n // spine has N+1 nodes for N edges; we want the last `effectiveEdges` edges,\n // which means keeping the last `effectiveEdges + 1` nodes\n const markerDistance = spine.length - 1 - earliestMarkerIdx;\n const effectiveEdges = Math.max(limit, markerDistance);\n\n if (effectiveEdges >= spine.length - 1) {\n return { graph, elidedCount: 0, spine };\n }\n\n const keepFromIdx = spine.length - 1 - effectiveEdges;\n const truncatedSpine = spine.slice(keepFromIdx);\n const visibleSpineSet = new Set(truncatedSpine);\n\n // Include any node reachable from visible spine nodes\n // (branches that fork from visible portion)\n const reachable = new Set(visibleSpineSet);\n const queue = [...truncatedSpine];\n while (queue.length > 0) {\n const current = queue.shift()!;\n for (const edge of graph.outgoing(current)) {\n if (!reachable.has(edge.to)) {\n reachable.add(edge.to);\n queue.push(edge.to);\n }\n }\n }\n\n const truncatedNodes = graph.nodes.filter((n) => reachable.has(n.id));\n const truncatedEdges = graph.edges.filter((e) => reachable.has(e.from) && reachable.has(e.to));\n const elidedCount = spine.length - 1 - effectiveEdges;\n\n return {\n graph: new RenderGraph(truncatedNodes, truncatedEdges),\n elidedCount,\n spine: truncatedSpine,\n };\n}\n\n/**\n * After truncation the original root may not be in the visible graph.\n * Find the first node with no incoming edges as a fallback root.\n */\nfunction findVisibleRoot(graph: RenderGraph, layoutNodes: readonly GraphNode[]): string {\n return layoutNodes.find((n) => !graph.incomingNodes.has(n.id))?.id ?? layoutNodes[0]?.id ?? '∅';\n}\n\n// ---------------------------------------------------------------------------\n// Core layout + render pipeline\n// ---------------------------------------------------------------------------\n\n/**\n * The main rendering pipeline: dagre layout → edge stamping → label\n * placement → arrowheads → nodes → elided indicator → detached nodes.\n *\n * Called by {@link render} after optional truncation. Receives nodes/edges\n * and produces the final multi-line string.\n *\n * @param graph - The graph to render (may include detached nodes, which are\n * rendered below the main graph rather than laid out by dagre).\n * @param options - Render options (rootId, spineTarget, colorize).\n * @param elidedCount - If > 0, a `┊ (N earlier migrations)` indicator\n * is stamped above the visible root node.\n */\nfunction layoutAndRender(graph: RenderGraph, options: GraphRenderOptions, elidedCount = 0): string {\n const colorize = options.colorize ?? true;\n const colors = buildColors(colorize);\n\n const layoutNodes = graph.nodes;\n const layoutNodeIds = new Set(layoutNodes.map((n) => n.id));\n const requestedRoot = options.rootId ?? layoutNodes[0]?.id ?? '∅';\n const rootId = layoutNodeIds.has(requestedRoot)\n ? requestedRoot\n : findVisibleRoot(graph, layoutNodes);\n\n const spineEdgeKeys = findSpineEdges(graph, rootId, options.spineTarget);\n\n const g = new dagre.graphlib.Graph({ multigraph: true });\n const dagreDefaults = { ranksep: 4, nodesep: 6, marginx: 2, marginy: 1 };\n g.setGraph({ rankdir: 'TB', ...dagreDefaults, ...options.dagreOptions });\n g.setDefaultEdgeLabel(() => ({}));\n\n for (const node of layoutNodes) {\n const tags = buildInlineTags(node.markers ?? [], colors);\n const tagWidth = inlineTagsWidth(tags);\n g.setNode(node.id, { width: node.id.length + 6 + tagWidth, height: 1 });\n }\n\n const edgeNames: string[] = [];\n for (let i = 0; i < graph.edges.length; i++) {\n const edge = graph.edges[i]!;\n const name = `e${i}`;\n edgeNames.push(name);\n g.setEdge(edge.from, edge.to, { label: edge.label ?? '' }, name);\n }\n\n dagre.layout(g);\n\n const nodePos = new Map<string, Point>();\n for (const id of g.nodes()) {\n const n = g.node(id);\n nodePos.set(id, { x: Math.round(n.x), y: Math.round(n.y) });\n }\n\n const grid = new CharGrid();\n\n // Reserve node label areas so edges and labels avoid them\n for (const node of layoutNodes) {\n const pos = nodePos.get(node.id);\n /* v8 ignore next -- @preserve */\n if (!pos) continue;\n const tags = buildInlineTags(node.markers ?? [], colors);\n const tagWidth = inlineTagsWidth(tags);\n grid.reserveArea(pos.x - 1, pos.y, node.id.length + 4 + tagWidth);\n }\n\n // --- Prepare edge metadata ---\n type EdgeEntry = {\n idx: number;\n edge: GraphEdge;\n dagrePoints: Point[];\n src: Point;\n tgt: Point;\n role: 'spine' | 'branch' | 'backward';\n edgeColor: ColorFn;\n priority: number;\n };\n const edgeEntries: EdgeEntry[] = [];\n\n for (let i = 0; i < graph.edges.length; i++) {\n const edge = graph.edges[i]!;\n const name = edgeNames[i]!;\n if (!name || !nodePos.has(edge.from) || !nodePos.has(edge.to)) continue;\n\n const src = nodePos.get(edge.from)!;\n const tgt = nodePos.get(edge.to)!;\n const dagreEdge = g.edge({ v: edge.from, w: edge.to, name });\n const dagrePoints: Point[] = dagreEdge?.points ?? [];\n\n const isBackward = tgt.y < src.y;\n const isSpine = spineEdgeKeys.has(`${edge.from}→${edge.to}`);\n const role: EdgeEntry['role'] = isBackward ? 'backward' : isSpine ? 'spine' : 'branch';\n const hintColor = resolveHintColor(edge.colorHint, colors);\n const edgeColor =\n hintColor ??\n (role === 'backward' ? colors.backward : role === 'spine' ? colors.spine : colors.branch);\n const priority =\n role === 'backward' ? PRIORITY.backward : role === 'spine' ? PRIORITY.spine : PRIORITY.branch;\n\n edgeEntries.push({ idx: i, edge, dagrePoints, src, tgt, role, edgeColor, priority });\n }\n\n // --- Pass 1: Draw all edges ---\n type DrawnEdge = { edge: GraphEdge; poly: Point[]; role: EdgeEntry['role']; srcY: number };\n const drawnEdges: DrawnEdge[] = [];\n\n for (const entry of edgeEntries) {\n const { edge, dagrePoints, src, tgt, edgeColor, priority } = entry;\n\n const { poly } = selectBestVariant(src, dagrePoints, tgt, edge.label, grid);\n\n const dashedBit = edge.style === 'dashed' ? DIR.dashed : 0;\n for (let j = 0; j < poly.length - 1; j++) {\n const a = poly[j]!;\n const b = poly[j + 1]!;\n if (a.y === b.y) {\n grid.markHorizontal(a.y, a.x, b.x, edgeColor, priority, dashedBit);\n } else if (a.x === b.x) {\n grid.markVertical(a.x, a.y, b.y, edgeColor, priority, dashedBit);\n }\n }\n\n drawnEdges.push({ edge, poly, role: entry.role, srcY: src.y });\n }\n\n // --- Pass 2: Place labels (longest first) ---\n const labelOrder = [...drawnEdges]\n .map((de, i) => ({ ...de, i }))\n .filter((de) => de.edge.label)\n .sort((a, b) => (b.edge.label?.length ?? 0) - (a.edge.label?.length ?? 0));\n\n for (const { edge, poly, role, srcY } of labelOrder) {\n /* v8 ignore next -- @preserve */\n if (!edge.label) continue;\n const labelPos = findLabelPlacement(poly, edge.label, grid, srcY);\n if (labelPos) {\n const labelColor =\n resolveHintColor(edge.colorHint, colors) ??\n (role === 'backward' ? colors.backward : role === 'spine' ? colors.spine : colors.label);\n grid.stampText(labelPos.x, labelPos.y, edge.label, labelColor);\n }\n }\n\n // --- Pass 3: Arrowheads ---\n for (const { edge, poly, role } of drawnEdges) {\n /* v8 ignore next -- @preserve */\n if (poly.length < 2) continue;\n const last = poly[poly.length - 1]!;\n const prev = poly[poly.length - 2]!;\n\n const edgeColor =\n resolveHintColor(edge.colorHint, colors) ??\n (role === 'backward' ? colors.backward : role === 'spine' ? colors.spine : colors.branch);\n\n let ax: number | undefined;\n let ay: number | undefined;\n let arrow: string | undefined;\n\n if (prev.x === last.x) {\n if (last.y > prev.y) {\n ax = last.x;\n ay = last.y - 1;\n arrow = ARROW.down;\n } else {\n ax = last.x;\n ay = last.y + 1;\n arrow = ARROW.up;\n }\n } else {\n if (last.x > prev.x) {\n ax = last.x - 1;\n ay = last.y;\n arrow = ARROW.right;\n } else {\n ax = last.x + 1;\n ay = last.y;\n arrow = ARROW.left;\n }\n }\n\n if (ax !== undefined && ay !== undefined && arrow && !grid.hasText(ax, ay)) {\n grid.stampText(ax, ay, arrow, edgeColor);\n }\n }\n\n // --- Draw nodes ---\n const spineNodeIds = new Set<string>();\n for (const key of spineEdgeKeys) {\n const [from, to] = key.split('→');\n if (from) spineNodeIds.add(from);\n if (to) spineNodeIds.add(to);\n }\n\n for (const node of layoutNodes) {\n const pos = nodePos.get(node.id);\n if (!pos) continue;\n\n const isSpineNode = spineNodeIds.has(node.id);\n const nodeColor = isSpineNode ? colors.spine : colors.branch;\n\n grid.stampText(pos.x, pos.y, '○', nodeColor);\n grid.stampText(pos.x + 1, pos.y, ' ');\n const hasMarkers = node.markers && node.markers.length > 0;\n grid.stampText(pos.x + 2, pos.y, node.id, isSpineNode || hasMarkers ? bold : dim);\n\n const tags = buildInlineTags(node.markers ?? [], colors);\n if (tags.length > 0) {\n let bx = pos.x + 2 + node.id.length;\n for (const tag of tags) {\n grid.stampText(bx, pos.y, ' ');\n bx++;\n grid.stampText(bx, pos.y, tag.text, tag.color);\n bx += tag.text.length;\n }\n }\n }\n\n // --- Elided indicator above root ---\n if (elidedCount > 0) {\n const topNodeId =\n layoutNodes.find((n) => !graph.incomingNodes.has(n.id))?.id ?? layoutNodes[0]?.id;\n const rootPos = topNodeId ? nodePos.get(topNodeId) : undefined;\n if (rootPos) {\n const label = elidedCount === 1 ? '1 earlier migration' : `${elidedCount} earlier migrations`;\n const topY = rootPos.y - 3;\n grid.stampText(rootPos.x, topY, '┊', colors.label);\n grid.stampText(rootPos.x, topY + 1, '┊', colors.label);\n grid.stampText(rootPos.x + 2, topY + 1, `(${label})`, colors.label);\n grid.stampText(rootPos.x, topY + 2, '┊', colors.label);\n }\n }\n\n return grid.render();\n}\n\n// ---------------------------------------------------------------------------\n// GraphRenderer implementation\n// ---------------------------------------------------------------------------\n\n/**\n * BFS to find the ordered node path from `rootId` to `targetId`.\n * Used for truncation — the spine path determines which edges to keep.\n *\n * Returns `[rootId]` if no path exists.\n */\nfunction findSpinePath(graph: RenderGraph, rootId: string, targetId: string): string[] {\n const visited = new Set([rootId]);\n const parent = new Map<string, string>();\n const queue = [rootId];\n while (queue.length > 0) {\n const current = queue.shift()!;\n if (current === targetId) {\n const path: string[] = [];\n let node = targetId;\n while (node !== rootId) {\n path.unshift(node);\n node = parent.get(node)!;\n }\n path.unshift(rootId);\n return path;\n }\n for (const edge of graph.outgoing(current)) {\n if (!visited.has(edge.to)) {\n visited.add(edge.to);\n parent.set(edge.to, current);\n queue.push(edge.to);\n }\n }\n }\n return [rootId];\n}\n\n/**\n * Render a graph with optional truncation.\n *\n * The caller decides what to pass in: the full graph for `--graph`, or a\n * subgraph extracted via {@link extractRelevantSubgraph} for the default view.\n */\nfunction render(graph: RenderGraph, options: GraphRenderOptions): string {\n if (options.limit !== undefined) {\n const spine = findSpinePath(\n graph,\n options.rootId ?? graph.nodes[0]?.id ?? '∅',\n options.spineTarget,\n );\n const { graph: truncated, elidedCount } = truncateGraph(graph, spine, options.limit);\n return layoutAndRender(truncated, options, elidedCount);\n }\n return layoutAndRender(graph, options);\n}\n\nexport interface GraphRenderer {\n render(graph: RenderGraph, options: GraphRenderOptions): string;\n}\n\nexport const graphRenderer: GraphRenderer = {\n render,\n};\n\n/** True if the graph is a single linear chain (no branching), ignoring dashed edges. */\nexport function isLinearGraph(graph: RenderGraph): boolean {\n for (const node of graph.nodes) {\n const solidOutgoing = graph.outgoing(node.id).filter((e) => e.style !== 'dashed');\n if (solidOutgoing.length > 1) return false;\n }\n return true;\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { t as CliStructuredError } from "./cli-errors-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { t as CliStructuredError } from "./cli-errors-CF60g2cG.mjs";
|
|
2
|
+
import { n as formatErrorOutput, t as formatErrorJson } from "./errors-Cw6kyTyV.mjs";
|
|
3
|
+
import { t as TerminalUI } from "./terminal-ui-XtOQsqe9.mjs";
|
|
4
|
+
import { i as renderInitOutro, n as buildNextSteps, r as formatInitJson, t as InitOutputSchema } from "./output-B60Gw5fu.mjs";
|
|
4
5
|
import { createRequire } from "node:module";
|
|
5
6
|
import { basename, dirname, extname, isAbsolute, join, normalize } from "pathe";
|
|
6
7
|
import * as clack from "@clack/prompts";
|
|
@@ -9,6 +10,9 @@ import { execFile } from "node:child_process";
|
|
|
9
10
|
import { promisify } from "node:util";
|
|
10
11
|
import { detect, getUserAgent } from "package-manager-detector/detect";
|
|
11
12
|
import { applyEdits, modify, parse, printParseErrorCode } from "jsonc-parser";
|
|
13
|
+
//#region package.json
|
|
14
|
+
var version = "0.9.0-dev.1";
|
|
15
|
+
//#endregion
|
|
12
16
|
//#region src/commands/init/errors.ts
|
|
13
17
|
/**
|
|
14
18
|
* Re-init in non-interactive mode without `--force`. Distinct from the
|
|
@@ -213,7 +217,7 @@ function errorInitEmitFailed(options) {
|
|
|
213
217
|
}
|
|
214
218
|
/**
|
|
215
219
|
* The project-level agent-skill install (`npx skills add
|
|
216
|
-
*
|
|
220
|
+
* prisma/prisma-next#v<version>`) failed after a successful dependency
|
|
217
221
|
* install + emit. The project's scaffold remains on disk; the user
|
|
218
222
|
* can either fix the underlying issue (network, registry, PATH) and
|
|
219
223
|
* run the install command manually, or re-run `init --no-skill` to
|
|
@@ -223,7 +227,7 @@ function errorInitEmitFailed(options) {
|
|
|
223
227
|
* semantics. Maps to exit code `6 = SKILL_INSTALL_FAILED`.
|
|
224
228
|
*/
|
|
225
229
|
function errorInitSkillInstallFailed(options) {
|
|
226
|
-
return new CliStructuredError("5013", "Failed to install
|
|
230
|
+
return new CliStructuredError("5013", "Failed to install Prisma Next skills", {
|
|
227
231
|
domain: "CLI",
|
|
228
232
|
why: `\`${options.skillInstallCommand}\` exited with an error: ${options.cause}`,
|
|
229
233
|
fix: `Either:
|
|
@@ -240,43 +244,101 @@ function errorInitSkillInstallFailed(options) {
|
|
|
240
244
|
//#region src/commands/init/agent-skill-install.ts
|
|
241
245
|
const exec = promisify(execFile);
|
|
242
246
|
/**
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
247
|
+
* Default base for the GitHub-URL form `<owner>/<repo>` consumed by
|
|
248
|
+
* upstream `skills add`. Each `SkillSource` joins this base with its
|
|
249
|
+
* own subpath (and optional `#ref` for version-pinned clusters).
|
|
250
|
+
*/
|
|
251
|
+
const DEFAULT_AGENT_SKILL_BASE = "prisma/prisma-next";
|
|
252
|
+
const DEFAULT_AGENT_SKILL_SOURCES = [
|
|
253
|
+
{
|
|
254
|
+
subpath: "skills",
|
|
255
|
+
ref: "cli",
|
|
256
|
+
description: "usage skills (version-locked to installed Prisma Next)"
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
subpath: "skills/upgrade",
|
|
260
|
+
ref: null,
|
|
261
|
+
description: "upgrade skill (always tracks `main`)"
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
subpath: "skills/extension-author",
|
|
265
|
+
ref: null,
|
|
266
|
+
description: "extension-author skill (always tracks `main`)"
|
|
267
|
+
}
|
|
268
|
+
];
|
|
269
|
+
/**
|
|
270
|
+
* Test-only escape hatch for pinning the install base to a local
|
|
271
|
+
* checkout. Production runs leave this unset, so installs always use
|
|
272
|
+
* `DEFAULT_AGENT_SKILL_BASE`.
|
|
273
|
+
*
|
|
274
|
+
* When set to an absolute filesystem path (typical for tests), the
|
|
275
|
+
* `#ref` fragment is dropped — local-path mode in upstream's CLI does
|
|
276
|
+
* not accept refs, and the local clone has whatever content the test
|
|
277
|
+
* checked into it anyway. When set to anything else (e.g. a fork name
|
|
278
|
+
* `myuser/prisma-next`), the ref policy is preserved.
|
|
254
279
|
*/
|
|
255
|
-
|
|
280
|
+
function resolveAgentSkillBase() {
|
|
281
|
+
const override = process.env["PRISMA_NEXT_SKILLS_BASE"]?.trim();
|
|
282
|
+
return override && override.length > 0 ? override : DEFAULT_AGENT_SKILL_BASE;
|
|
283
|
+
}
|
|
284
|
+
function isLocalPath(base) {
|
|
285
|
+
return base.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(base);
|
|
286
|
+
}
|
|
256
287
|
/**
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
|
|
288
|
+
* Build the `<base>/<subpath>[#ref]` URL the `skills` CLI will
|
|
289
|
+
* resolve. Exported for unit tests so the per-source format can be
|
|
290
|
+
* asserted without going through the full install loop.
|
|
291
|
+
*/
|
|
292
|
+
function formatSkillSourceUrl(source) {
|
|
293
|
+
const base = resolveAgentSkillBase();
|
|
294
|
+
const url = `${base}/${source.subpath}`;
|
|
295
|
+
if (source.ref === null) return url;
|
|
296
|
+
if (isLocalPath(base)) return url;
|
|
297
|
+
if (source.ref === "cli") return `${url}#v${version}`;
|
|
298
|
+
return url;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* The skill-install command for one source, formatted for the
|
|
302
|
+
* project's detected package manager. `npx`/`pnpm dlx`/`bunx` are
|
|
303
|
+
* interchangeable to the user; we pick the variant that matches the
|
|
304
|
+
* rest of the install step so a single project consistently uses one
|
|
305
|
+
* runner.
|
|
261
306
|
*
|
|
262
307
|
* `--all` auto-selects every skill in the cluster and every detected
|
|
263
308
|
* agent runtime, skipping the multi-select prompts the `skills` CLI
|
|
264
309
|
* shows by default. A non-interactive scaffold step cannot present
|
|
265
|
-
* prompts
|
|
266
|
-
* router skill routes between the workflow-scoped siblings). Users who
|
|
267
|
-
* want a narrower install run `npx skills add @prisma-next/agent-skill`
|
|
268
|
-
* themselves after `init` with the flags they want.
|
|
310
|
+
* prompts.
|
|
269
311
|
*
|
|
270
312
|
* Exported for unit tests so the per-PM dispatch can be asserted
|
|
271
313
|
* without a live subprocess.
|
|
272
314
|
*/
|
|
273
|
-
function formatSkillInstallCommand(pm) {
|
|
274
|
-
|
|
275
|
-
"skills",
|
|
315
|
+
function formatSkillInstallCommand(pm, source) {
|
|
316
|
+
return formatPackageManagerCommand(pm, [
|
|
317
|
+
"skills@latest",
|
|
276
318
|
"add",
|
|
277
|
-
|
|
319
|
+
formatSkillSourceUrl(source),
|
|
278
320
|
"--all"
|
|
279
|
-
];
|
|
321
|
+
]);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* `skills add --all` should cover Claude Code, but upstream currently skips
|
|
325
|
+
* project-local Claude symlinks when `.claude/` does not already exist. Run
|
|
326
|
+
* the explicit Claude Code install as well so fresh projects get
|
|
327
|
+
* `.claude/skills` without asking users to create that folder first.
|
|
328
|
+
*/
|
|
329
|
+
function formatClaudeSkillInstallCommand(pm, source) {
|
|
330
|
+
return formatPackageManagerCommand(pm, [
|
|
331
|
+
"skills@latest",
|
|
332
|
+
"add",
|
|
333
|
+
formatSkillSourceUrl(source),
|
|
334
|
+
"--agent",
|
|
335
|
+
"claude-code",
|
|
336
|
+
"--skill",
|
|
337
|
+
"'*'",
|
|
338
|
+
"-y"
|
|
339
|
+
]);
|
|
340
|
+
}
|
|
341
|
+
function formatPackageManagerCommand(pm, args) {
|
|
280
342
|
switch (pm) {
|
|
281
343
|
case "pnpm": return `pnpm dlx ${args.join(" ")}`;
|
|
282
344
|
case "yarn": return `yarn dlx ${args.join(" ")}`;
|
|
@@ -290,39 +352,46 @@ function formatSkillInstallCommand(pm) {
|
|
|
290
352
|
* format-then-parse split keeps the user-facing command string the same
|
|
291
353
|
* as the surface the structured error advertises, so a user who copies
|
|
292
354
|
* the error's `fix` line gets the same invocation that init just
|
|
293
|
-
* attempted.
|
|
355
|
+
* attempted. Single quotes are preserved in the display form so `*` is
|
|
356
|
+
* safe to copy into a shell, then stripped before `execFile`.
|
|
294
357
|
*/
|
|
295
358
|
function commandToExec(command) {
|
|
296
|
-
const tokens = command.
|
|
359
|
+
const tokens = (command.match(/'[^']*'|\S+/g) ?? []).map((token) => token.startsWith("'") && token.endsWith("'") ? token.slice(1, -1) : token);
|
|
297
360
|
return {
|
|
298
361
|
file: tokens[0] ?? "npx",
|
|
299
362
|
args: tokens.slice(1)
|
|
300
363
|
};
|
|
301
364
|
}
|
|
302
365
|
/**
|
|
303
|
-
* Runs the project-level skill install
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
366
|
+
* Runs the project-level skill install for every source in
|
|
367
|
+
* `DEFAULT_AGENT_SKILL_SOURCES`, in order. Returns
|
|
368
|
+
* `{ ok: true, commands }` on success; throws a structured
|
|
369
|
+
* `errorInitSkillInstallFailed` on the first failure (subsequent
|
|
370
|
+
* sources are not attempted — the user opted into Prisma Next by
|
|
371
|
+
* running `init` and a partial install would leave the project in an
|
|
372
|
+
* ambiguous state). The throw is intentionally fatal — project-level
|
|
373
|
+
* skill install is unconditional (modulo `--no-skill`).
|
|
309
374
|
*/
|
|
310
375
|
async function runProjectLevelSkillInstall(ctx) {
|
|
311
|
-
const
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
command
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
}
|
|
376
|
+
const commands = [];
|
|
377
|
+
const installCommands = DEFAULT_AGENT_SKILL_SOURCES.flatMap((source) => [formatSkillInstallCommand(ctx.pm, source), formatClaudeSkillInstallCommand(ctx.pm, source)]);
|
|
378
|
+
for (const command of installCommands) {
|
|
379
|
+
const { file, args } = commandToExec(command);
|
|
380
|
+
try {
|
|
381
|
+
await exec(file, args, { cwd: ctx.baseDir });
|
|
382
|
+
commands.push(command);
|
|
383
|
+
} catch (err) {
|
|
384
|
+
throw errorInitSkillInstallFailed({
|
|
385
|
+
skillInstallCommand: command,
|
|
386
|
+
filesWritten: ctx.filesWritten,
|
|
387
|
+
cause: redactSecrets$1(readChildStderr$1(err)) || (err instanceof Error ? err.message : String(err))
|
|
388
|
+
});
|
|
389
|
+
}
|
|
325
390
|
}
|
|
391
|
+
return {
|
|
392
|
+
ok: true,
|
|
393
|
+
commands
|
|
394
|
+
};
|
|
326
395
|
}
|
|
327
396
|
function readChildStderr$1(err) {
|
|
328
397
|
if (err instanceof Error && "stderr" in err) return String(err.stderr ?? "");
|
|
@@ -345,7 +414,7 @@ function redactSecrets$1(stderr) {
|
|
|
345
414
|
/**
|
|
346
415
|
* Hand-rolled skill stub path that init must not leave behind. Removed
|
|
347
416
|
* on every init run so a project's `.agents/skills/prisma-next/` does
|
|
348
|
-
* not shadow the
|
|
417
|
+
* not shadow the installed Prisma Next skill cluster.
|
|
349
418
|
*/
|
|
350
419
|
const LEGACY_SKILL_FILE = ".agents/skills/prisma-next/SKILL.md";
|
|
351
420
|
//#endregion
|
|
@@ -1916,19 +1985,21 @@ async function runInit(baseDir, runOptions) {
|
|
|
1916
1985
|
filesWritten
|
|
1917
1986
|
}));
|
|
1918
1987
|
}
|
|
1919
|
-
const
|
|
1920
|
-
|
|
1921
|
-
|
|
1988
|
+
const manualProjectSkillSummary = DEFAULT_AGENT_SKILL_SOURCES.flatMap((source) => [formatSkillInstallCommand(install.effectivePm, source), formatClaudeSkillInstallCommand(install.effectivePm, source)]).map((c) => `\`${c}\``).join(" && ");
|
|
1989
|
+
let skillRegistered = false;
|
|
1990
|
+
if (!inputs.installProjectSkill) warnings.push(`Skipped Prisma Next agent-skill install (--no-skill). To install the skills later, run: ${manualProjectSkillSummary}`);
|
|
1991
|
+
else if (install.skipped) warnings.push(`Skipped Prisma Next agent-skill install because --no-install was passed. After you run install manually, install the skills with: ${manualProjectSkillSummary}`);
|
|
1922
1992
|
else {
|
|
1923
1993
|
const spinner = ui.spinner();
|
|
1924
|
-
spinner.start("Registering
|
|
1994
|
+
spinner.start("Registering Prisma Next skills with the agent runtime...");
|
|
1925
1995
|
try {
|
|
1926
1996
|
const project = await runProjectLevelSkillInstall({
|
|
1927
1997
|
baseDir,
|
|
1928
1998
|
pm: install.effectivePm,
|
|
1929
1999
|
filesWritten
|
|
1930
2000
|
});
|
|
1931
|
-
spinner.stop(`Registered
|
|
2001
|
+
spinner.stop(`Registered Prisma Next skills (project level) — ran ${project.commands.map((c) => `\`${c}\``).join(", ")}`);
|
|
2002
|
+
skillRegistered = true;
|
|
1932
2003
|
} catch (error) {
|
|
1933
2004
|
spinner.stop("Agent-skill install failed");
|
|
1934
2005
|
if (CliStructuredError.is(error)) return emitError(ui, flags, error);
|
|
@@ -1952,7 +2023,8 @@ async function runInit(baseDir, runOptions) {
|
|
|
1952
2023
|
target: inputs.target === "mongo" ? "mongodb" : "postgres",
|
|
1953
2024
|
contractEmitted,
|
|
1954
2025
|
emitCommand,
|
|
1955
|
-
schemaPath: inputs.schemaPath
|
|
2026
|
+
schemaPath: inputs.schemaPath,
|
|
2027
|
+
skillRegistered
|
|
1956
2028
|
}),
|
|
1957
2029
|
warnings
|
|
1958
2030
|
};
|
|
@@ -2229,7 +2301,7 @@ async function runEmit(ctx) {
|
|
|
2229
2301
|
const spinner = ctx.ui.spinner();
|
|
2230
2302
|
spinner.start("Emitting contract...");
|
|
2231
2303
|
try {
|
|
2232
|
-
const { executeContractEmit } = await import("./contract-emit-
|
|
2304
|
+
const { executeContractEmit } = await import("./contract-emit-iynA3BCA.mjs").then((n) => n.t);
|
|
2233
2305
|
await executeContractEmit({ configPath: join(ctx.baseDir, "prisma-next.config.ts") });
|
|
2234
2306
|
spinner.stop("Contract emitted");
|
|
2235
2307
|
} catch (err) {
|
|
@@ -2282,4 +2354,4 @@ function sanitisePackageName(raw) {
|
|
|
2282
2354
|
//#endregion
|
|
2283
2355
|
export { runInit };
|
|
2284
2356
|
|
|
2285
|
-
//# sourceMappingURL=init-
|
|
2357
|
+
//# sourceMappingURL=init-Bqg5JWg7.mjs.map
|