@prisma-next/cli 0.3.0-pr.99.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +381 -128
- package/dist/agent-skill-mongo.md +106 -0
- package/dist/agent-skill-postgres.md +106 -0
- package/dist/cli-errors-BDCYR5ap.mjs +4 -0
- package/dist/cli-errors-DStABy9d.d.mts +3 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.js +1 -2910
- package/dist/cli.mjs +261 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/client-DiUkJAeN.mjs +987 -0
- package/dist/client-DiUkJAeN.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts +7 -0
- package/dist/commands/contract-emit.d.mts.map +1 -0
- package/dist/commands/contract-emit.mjs +9 -0
- package/dist/commands/contract-infer.d.mts +7 -0
- package/dist/commands/contract-infer.d.mts.map +1 -0
- package/dist/commands/contract-infer.mjs +10 -0
- package/dist/commands/db-init.d.mts +7 -0
- package/dist/commands/db-init.d.mts.map +1 -0
- package/dist/commands/db-init.mjs +126 -0
- package/dist/commands/db-init.mjs.map +1 -0
- package/dist/commands/db-schema.d.mts +7 -0
- package/dist/commands/db-schema.d.mts.map +1 -0
- package/dist/commands/db-schema.mjs +56 -0
- package/dist/commands/db-schema.mjs.map +1 -0
- package/dist/commands/db-sign.d.mts +7 -0
- package/dist/commands/db-sign.d.mts.map +1 -0
- package/dist/commands/db-sign.mjs +137 -0
- package/dist/commands/db-sign.mjs.map +1 -0
- package/dist/commands/db-update.d.mts +7 -0
- package/dist/commands/db-update.d.mts.map +1 -0
- package/dist/commands/db-update.mjs +123 -0
- package/dist/commands/db-update.mjs.map +1 -0
- package/dist/commands/db-verify.d.mts +7 -0
- package/dist/commands/db-verify.d.mts.map +1 -0
- package/dist/commands/db-verify.mjs +323 -0
- package/dist/commands/db-verify.mjs.map +1 -0
- package/dist/commands/migration-apply.d.mts +36 -0
- package/dist/commands/migration-apply.d.mts.map +1 -0
- package/dist/commands/migration-apply.mjs +245 -0
- package/dist/commands/migration-apply.mjs.map +1 -0
- package/dist/commands/migration-new.d.mts +8 -0
- package/dist/commands/migration-new.d.mts.map +1 -0
- package/dist/commands/migration-new.mjs +152 -0
- package/dist/commands/migration-new.mjs.map +1 -0
- package/dist/commands/migration-plan.d.mts +47 -0
- package/dist/commands/migration-plan.d.mts.map +1 -0
- package/dist/commands/migration-plan.mjs +313 -0
- package/dist/commands/migration-plan.mjs.map +1 -0
- package/dist/commands/migration-ref.d.mts +43 -0
- package/dist/commands/migration-ref.d.mts.map +1 -0
- package/dist/commands/migration-ref.mjs +195 -0
- package/dist/commands/migration-ref.mjs.map +1 -0
- package/dist/commands/migration-show.d.mts +28 -0
- package/dist/commands/migration-show.d.mts.map +1 -0
- package/dist/commands/migration-show.mjs +140 -0
- package/dist/commands/migration-show.mjs.map +1 -0
- package/dist/commands/migration-status.d.mts +86 -0
- package/dist/commands/migration-status.d.mts.map +1 -0
- package/dist/commands/migration-status.mjs +9 -0
- package/dist/commands/migration-verify.d.mts +16 -0
- package/dist/commands/migration-verify.d.mts.map +1 -0
- package/dist/commands/migration-verify.mjs +110 -0
- package/dist/commands/migration-verify.mjs.map +1 -0
- package/dist/config-loader-C4VXKl8f.mjs +43 -0
- package/dist/config-loader-C4VXKl8f.mjs.map +1 -0
- package/dist/{config-loader.d.ts → config-loader.d.mts} +8 -3
- package/dist/config-loader.d.mts.map +1 -0
- package/dist/config-loader.mjs +3 -0
- package/dist/contract-emit-D2wDXfyo.mjs +191 -0
- package/dist/contract-emit-D2wDXfyo.mjs.map +1 -0
- package/dist/contract-emit-Zm_sd1wQ.mjs +112 -0
- package/dist/contract-emit-Zm_sd1wQ.mjs.map +1 -0
- package/dist/contract-emit-kN-IkKTE.mjs +6 -0
- package/dist/contract-enrichment-CGW6mm-E.mjs +79 -0
- package/dist/contract-enrichment-CGW6mm-E.mjs.map +1 -0
- package/dist/contract-infer-DozZT511.mjs +90 -0
- package/dist/contract-infer-DozZT511.mjs.map +1 -0
- package/dist/exports/config-types.d.mts +2 -0
- package/dist/exports/config-types.mjs +3 -0
- package/dist/exports/control-api.d.mts +624 -0
- package/dist/exports/control-api.d.mts.map +1 -0
- package/dist/exports/control-api.mjs +8 -0
- package/dist/{load-ts-contract.d.ts → exports/index.d.mts} +12 -7
- package/dist/exports/index.d.mts.map +1 -0
- package/dist/exports/index.mjs +142 -0
- package/dist/exports/index.mjs.map +1 -0
- package/dist/extract-operation-statements-DZUJNmL3.mjs +13 -0
- package/dist/extract-operation-statements-DZUJNmL3.mjs.map +1 -0
- package/dist/extract-sql-ddl-DDMX-9mz.mjs +26 -0
- package/dist/extract-sql-ddl-DDMX-9mz.mjs.map +1 -0
- package/dist/framework-components-BAsliT4V.mjs +59 -0
- package/dist/framework-components-BAsliT4V.mjs.map +1 -0
- package/dist/init-6Pvm_esG.mjs +430 -0
- package/dist/init-6Pvm_esG.mjs.map +1 -0
- package/dist/inspect-live-schema-BYnhztxZ.mjs +91 -0
- package/dist/inspect-live-schema-BYnhztxZ.mjs.map +1 -0
- package/dist/migration-command-scaffold-CntCcntR.mjs +105 -0
- package/dist/migration-command-scaffold-CntCcntR.mjs.map +1 -0
- package/dist/migration-status-CJANY4yr.mjs +1583 -0
- package/dist/migration-status-CJANY4yr.mjs.map +1 -0
- package/dist/migrations-DTZBYXm1.mjs +173 -0
- package/dist/migrations-DTZBYXm1.mjs.map +1 -0
- package/dist/progress-adapter-B-YvmcDu.mjs +43 -0
- package/dist/progress-adapter-B-YvmcDu.mjs.map +1 -0
- package/dist/quick-reference-mongo.md +93 -0
- package/dist/quick-reference-postgres.md +91 -0
- package/dist/result-handler-oK_vA-Fn.mjs +697 -0
- package/dist/result-handler-oK_vA-Fn.mjs.map +1 -0
- package/dist/terminal-ui-C5k88MmW.mjs +274 -0
- package/dist/terminal-ui-C5k88MmW.mjs.map +1 -0
- package/dist/validate-contract-deps-esa-VQ0h.mjs +37 -0
- package/dist/validate-contract-deps-esa-VQ0h.mjs.map +1 -0
- package/dist/verify-DlFQ2FOw.mjs +385 -0
- package/dist/verify-DlFQ2FOw.mjs.map +1 -0
- package/package.json +87 -40
- package/src/cli.ts +118 -58
- package/src/commands/contract-emit.ts +101 -78
- package/src/commands/contract-infer-paths.ts +32 -0
- package/src/commands/contract-infer.ts +143 -0
- package/src/commands/db-init.ts +97 -219
- package/src/commands/db-schema.ts +77 -0
- package/src/commands/db-sign.ts +46 -73
- package/src/commands/db-update.ts +236 -0
- package/src/commands/db-verify.ts +409 -119
- package/src/commands/init/detect-package-manager.ts +47 -0
- package/src/commands/init/index.ts +21 -0
- package/src/commands/init/init.ts +203 -0
- package/src/commands/init/templates/agent-skill-mongo.md +106 -0
- package/src/commands/init/templates/agent-skill-postgres.md +106 -0
- package/src/commands/init/templates/agent-skill.ts +19 -0
- package/src/commands/init/templates/code-templates.ts +168 -0
- package/src/commands/init/templates/quick-reference-mongo.md +93 -0
- package/src/commands/init/templates/quick-reference-postgres.md +91 -0
- package/src/commands/init/templates/quick-reference.ts +19 -0
- package/src/commands/init/templates/render.ts +20 -0
- package/src/commands/init/templates/tsconfig.ts +35 -0
- package/src/commands/inspect-live-schema.ts +170 -0
- package/src/commands/migration-apply.ts +427 -0
- package/src/commands/migration-new.ts +260 -0
- package/src/commands/migration-plan.ts +519 -0
- package/src/commands/migration-ref.ts +305 -0
- package/src/commands/migration-show.ts +246 -0
- package/src/commands/migration-status.ts +864 -0
- package/src/commands/migration-verify.ts +180 -0
- package/src/config-loader.ts +13 -3
- package/src/control-api/client.ts +205 -183
- package/src/control-api/contract-enrichment.ts +119 -0
- package/src/control-api/errors.ts +9 -0
- package/src/control-api/operations/contract-emit.ts +181 -0
- package/src/control-api/operations/db-init.ts +53 -49
- package/src/control-api/operations/db-update.ts +220 -0
- package/src/control-api/operations/extract-operation-statements.ts +14 -0
- package/src/control-api/operations/extract-sql-ddl.ts +47 -0
- package/src/control-api/operations/migration-apply.ts +191 -0
- package/src/control-api/operations/migration-helpers.ts +49 -0
- package/src/control-api/types.ts +274 -52
- package/src/exports/config-types.ts +4 -3
- package/src/exports/control-api.ts +15 -5
- package/src/load-ts-contract.ts +30 -19
- package/src/utils/cli-errors.ts +14 -8
- package/src/utils/command-helpers.ts +302 -3
- package/src/utils/formatters/emit.ts +67 -0
- package/src/utils/formatters/errors.ts +82 -0
- package/src/utils/formatters/graph-migration-mapper.ts +240 -0
- package/src/utils/formatters/graph-render.ts +1323 -0
- package/src/utils/formatters/graph-types.ts +120 -0
- package/src/utils/formatters/help.ts +380 -0
- package/src/utils/formatters/helpers.ts +28 -0
- package/src/utils/formatters/migrations.ts +346 -0
- package/src/utils/formatters/styled.ts +212 -0
- package/src/utils/formatters/verify.ts +621 -0
- package/src/utils/framework-components.ts +13 -10
- package/src/utils/global-flags.ts +41 -23
- package/src/utils/migration-command-scaffold.ts +184 -0
- package/src/utils/migration-types.ts +12 -0
- package/src/utils/progress-adapter.ts +18 -29
- package/src/utils/result-handler.ts +12 -13
- package/src/utils/shutdown.ts +92 -0
- package/src/utils/suggest-command.ts +31 -0
- package/src/utils/terminal-ui.ts +276 -0
- package/src/utils/validate-contract-deps.ts +49 -0
- package/dist/chunk-AGOTG4L3.js +0 -965
- package/dist/chunk-AGOTG4L3.js.map +0 -1
- package/dist/chunk-HLLI4YL7.js +0 -180
- package/dist/chunk-HLLI4YL7.js.map +0 -1
- package/dist/chunk-HWYQOCAJ.js +0 -47
- package/dist/chunk-HWYQOCAJ.js.map +0 -1
- package/dist/chunk-VG2R7DGF.js +0 -735
- package/dist/chunk-VG2R7DGF.js.map +0 -1
- package/dist/cli.d.ts +0 -2
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/contract-emit.d.ts +0 -3
- package/dist/commands/contract-emit.d.ts.map +0 -1
- package/dist/commands/contract-emit.js +0 -10
- package/dist/commands/contract-emit.js.map +0 -1
- package/dist/commands/db-init.d.ts +0 -3
- package/dist/commands/db-init.d.ts.map +0 -1
- package/dist/commands/db-init.js +0 -257
- package/dist/commands/db-init.js.map +0 -1
- package/dist/commands/db-introspect.d.ts +0 -3
- package/dist/commands/db-introspect.d.ts.map +0 -1
- package/dist/commands/db-introspect.js +0 -155
- package/dist/commands/db-introspect.js.map +0 -1
- package/dist/commands/db-schema-verify.d.ts +0 -3
- package/dist/commands/db-schema-verify.d.ts.map +0 -1
- package/dist/commands/db-schema-verify.js +0 -171
- package/dist/commands/db-schema-verify.js.map +0 -1
- package/dist/commands/db-sign.d.ts +0 -3
- package/dist/commands/db-sign.d.ts.map +0 -1
- package/dist/commands/db-sign.js +0 -195
- package/dist/commands/db-sign.js.map +0 -1
- package/dist/commands/db-verify.d.ts +0 -3
- package/dist/commands/db-verify.d.ts.map +0 -1
- package/dist/commands/db-verify.js +0 -193
- package/dist/commands/db-verify.js.map +0 -1
- package/dist/config-loader.d.ts.map +0 -1
- package/dist/config-loader.js +0 -7
- package/dist/config-loader.js.map +0 -1
- package/dist/control-api/client.d.ts +0 -13
- package/dist/control-api/client.d.ts.map +0 -1
- package/dist/control-api/operations/db-init.d.ts +0 -29
- package/dist/control-api/operations/db-init.d.ts.map +0 -1
- package/dist/control-api/types.d.ts +0 -387
- package/dist/control-api/types.d.ts.map +0 -1
- package/dist/exports/config-types.d.ts +0 -3
- package/dist/exports/config-types.d.ts.map +0 -1
- package/dist/exports/config-types.js +0 -6
- package/dist/exports/config-types.js.map +0 -1
- package/dist/exports/control-api.d.ts +0 -13
- package/dist/exports/control-api.d.ts.map +0 -1
- package/dist/exports/control-api.js +0 -7
- package/dist/exports/control-api.js.map +0 -1
- package/dist/exports/index.d.ts +0 -4
- package/dist/exports/index.d.ts.map +0 -1
- package/dist/exports/index.js +0 -176
- package/dist/exports/index.js.map +0 -1
- package/dist/load-ts-contract.d.ts.map +0 -1
- package/dist/utils/cli-errors.d.ts +0 -7
- package/dist/utils/cli-errors.d.ts.map +0 -1
- package/dist/utils/command-helpers.d.ts +0 -12
- package/dist/utils/command-helpers.d.ts.map +0 -1
- package/dist/utils/framework-components.d.ts +0 -70
- package/dist/utils/framework-components.d.ts.map +0 -1
- package/dist/utils/global-flags.d.ts +0 -25
- package/dist/utils/global-flags.d.ts.map +0 -1
- package/dist/utils/output.d.ts +0 -142
- package/dist/utils/output.d.ts.map +0 -1
- package/dist/utils/progress-adapter.d.ts +0 -26
- package/dist/utils/progress-adapter.d.ts.map +0 -1
- package/dist/utils/result-handler.d.ts +0 -15
- package/dist/utils/result-handler.d.ts.map +0 -1
- package/src/commands/db-introspect.ts +0 -227
- package/src/commands/db-schema-verify.ts +0 -238
- package/src/utils/output.ts +0 -1471
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Generic graph renderer types — domain-agnostic
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
/** A typed marker attached to a node. Different marker kinds get different visual treatment. */
|
|
6
|
+
export type NodeMarker =
|
|
7
|
+
| { readonly kind: 'db' }
|
|
8
|
+
| { readonly kind: 'ref'; readonly name: string; readonly active?: boolean }
|
|
9
|
+
| { readonly kind: 'contract'; readonly planned: boolean }
|
|
10
|
+
| { readonly kind: 'custom'; readonly label: string };
|
|
11
|
+
|
|
12
|
+
/** A node in the graph. Rendered as ○ with optional typed markers. */
|
|
13
|
+
export interface GraphNode {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
/** Typed markers rendered inline on the node row */
|
|
16
|
+
readonly markers?: readonly NodeMarker[] | undefined;
|
|
17
|
+
/** Detached nodes use a dashed connector (e.g. unplanned contract) */
|
|
18
|
+
readonly style?: 'normal' | 'detached' | undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** A directed edge between two nodes. Carries an optional label rendered on the edge line. */
|
|
22
|
+
export interface GraphEdge {
|
|
23
|
+
readonly from: string;
|
|
24
|
+
readonly to: string;
|
|
25
|
+
/** Edge line text, e.g. "20260101_init ✓ abc12.." */
|
|
26
|
+
readonly label?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Visual color hint for the edge. Overrides the default role-based
|
|
29
|
+
* coloring (spine/branch/backward) when set.
|
|
30
|
+
*
|
|
31
|
+
* - `'applied'` — cyan (CVD-safe: completed/done)
|
|
32
|
+
* - `'pending'` — yellow (CVD-safe: waiting/upcoming)
|
|
33
|
+
* - `'unreachable'` — magenta (CVD-safe: DB on a different branch)
|
|
34
|
+
*/
|
|
35
|
+
readonly colorHint?: 'applied' | 'pending' | 'unreachable';
|
|
36
|
+
/**
|
|
37
|
+
* Edge rendering style.
|
|
38
|
+
* - `'solid'` (default) — normal edge with `│` connector
|
|
39
|
+
* - `'dashed'` — draft/phantom edge with `┊` connector, excluded from path resolution
|
|
40
|
+
*/
|
|
41
|
+
readonly style?: 'solid' | 'dashed';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Immutable directed graph with adjacency-list indexing.
|
|
46
|
+
*
|
|
47
|
+
* Built once from flat arrays of nodes and edges, then passed around as
|
|
48
|
+
* the primary graph representation for rendering, traversal, truncation,
|
|
49
|
+
* and subgraph extraction.
|
|
50
|
+
*/
|
|
51
|
+
export class RenderGraph {
|
|
52
|
+
readonly nodes: readonly GraphNode[];
|
|
53
|
+
readonly edges: readonly GraphEdge[];
|
|
54
|
+
|
|
55
|
+
/** Forward adjacency: node id → outgoing edges. */
|
|
56
|
+
readonly forward: ReadonlyMap<string, readonly GraphEdge[]>;
|
|
57
|
+
/** Set of node ids that have at least one incoming edge. */
|
|
58
|
+
readonly incomingNodes: ReadonlySet<string>;
|
|
59
|
+
/** Node lookup by id. */
|
|
60
|
+
readonly nodeById: ReadonlyMap<string, GraphNode>;
|
|
61
|
+
|
|
62
|
+
constructor(nodes: readonly GraphNode[], edges: readonly GraphEdge[]) {
|
|
63
|
+
this.nodes = nodes;
|
|
64
|
+
this.edges = edges;
|
|
65
|
+
|
|
66
|
+
const fwd = new Map<string, GraphEdge[]>();
|
|
67
|
+
const inc = new Set<string>();
|
|
68
|
+
const byId = new Map<string, GraphNode>();
|
|
69
|
+
|
|
70
|
+
for (const n of nodes) {
|
|
71
|
+
byId.set(n.id, n);
|
|
72
|
+
}
|
|
73
|
+
for (const e of edges) {
|
|
74
|
+
const list = fwd.get(e.from);
|
|
75
|
+
if (list) list.push(e);
|
|
76
|
+
else fwd.set(e.from, [e]);
|
|
77
|
+
inc.add(e.to);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this.forward = fwd;
|
|
81
|
+
this.incomingNodes = inc;
|
|
82
|
+
this.nodeById = byId;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Outgoing edges from a node (empty array if none). */
|
|
86
|
+
outgoing(nodeId: string): readonly GraphEdge[] {
|
|
87
|
+
return this.forward.get(nodeId) ?? [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Options controlling graph rendering. */
|
|
92
|
+
export interface GraphRenderOptions {
|
|
93
|
+
/** Node id for the spine endpoint */
|
|
94
|
+
readonly spineTarget: string;
|
|
95
|
+
/** Root node id. Defaults to the node with no incoming edges. */
|
|
96
|
+
readonly rootId?: string;
|
|
97
|
+
/** Enable ANSI colour output */
|
|
98
|
+
readonly colorize?: boolean;
|
|
99
|
+
/** Terminal width. Default 80. */
|
|
100
|
+
readonly maxWidth?: number;
|
|
101
|
+
/**
|
|
102
|
+
* Truncate to show at most this many spine edges. `undefined` = no truncation.
|
|
103
|
+
* The effective limit expands to include any DB or contract markers on the spine.
|
|
104
|
+
*/
|
|
105
|
+
readonly limit?: number | undefined;
|
|
106
|
+
/**
|
|
107
|
+
* Override dagre layout parameters. Merged over defaults:
|
|
108
|
+
* `{ ranksep: 4, nodesep: 6, marginx: 2, marginy: 1 }`.
|
|
109
|
+
*
|
|
110
|
+
* When `ranksep` is ≤ 1, labels are placed inline on the arrowhead row
|
|
111
|
+
* instead of in a separate label-placement pass (there isn't enough
|
|
112
|
+
* vertical space for a dedicated label row).
|
|
113
|
+
*/
|
|
114
|
+
readonly dagreOptions?: {
|
|
115
|
+
readonly ranksep?: number;
|
|
116
|
+
readonly nodesep?: number;
|
|
117
|
+
readonly marginx?: number;
|
|
118
|
+
readonly marginy?: number;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import { blue, bold, cyan, dim, green, magenta } from 'colorette';
|
|
2
|
+
import type { Command } from 'commander';
|
|
3
|
+
import wrapAnsi from 'wrap-ansi';
|
|
4
|
+
|
|
5
|
+
import { getCommandExamples, getLongDescription } from '../command-helpers';
|
|
6
|
+
import type { GlobalFlags } from '../global-flags';
|
|
7
|
+
import { formatDim } from './helpers';
|
|
8
|
+
import { padToFixedWidth, renderCommandTree } from './styled';
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Help Output Formatters
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Fixed width for left column in help output.
|
|
16
|
+
* Must match the value in styled.ts.
|
|
17
|
+
*/
|
|
18
|
+
const LEFT_COLUMN_WIDTH = 20;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Minimum width for right column wrapping in help output.
|
|
22
|
+
*/
|
|
23
|
+
const RIGHT_COLUMN_MIN_WIDTH = 40;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Maximum width for right column wrapping in help output (when terminal is wide enough).
|
|
27
|
+
*/
|
|
28
|
+
const RIGHT_COLUMN_MAX_WIDTH = 90;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Gets the terminal width, or returns a default if not available.
|
|
32
|
+
*/
|
|
33
|
+
function getTerminalWidth(): number {
|
|
34
|
+
// Help text goes to stderr, so prefer stderr columns. Fall back to stdout, then CLI_WIDTH env.
|
|
35
|
+
const terminalWidth = process.stderr.columns || process.stdout.columns;
|
|
36
|
+
const envWidth = Number.parseInt(process.env['CLI_WIDTH'] || '', 10);
|
|
37
|
+
return terminalWidth || (Number.isFinite(envWidth) ? envWidth : 80);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Calculates the available width for the right column based on terminal width.
|
|
42
|
+
*/
|
|
43
|
+
function calculateRightColumnWidth(): number {
|
|
44
|
+
const terminalWidth = getTerminalWidth();
|
|
45
|
+
const availableWidth = terminalWidth - 2 - LEFT_COLUMN_WIDTH - 2;
|
|
46
|
+
return Math.max(RIGHT_COLUMN_MIN_WIDTH, Math.min(availableWidth, RIGHT_COLUMN_MAX_WIDTH));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Creates the CLI brand badge.
|
|
51
|
+
*/
|
|
52
|
+
function createPrismaNextBadge(useColor: boolean): string {
|
|
53
|
+
return useColor ? bold('prisma-next') : 'prisma-next';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Formats a header line: brand + operation + intent
|
|
58
|
+
*/
|
|
59
|
+
function formatHeaderLine(options: {
|
|
60
|
+
readonly brand: string;
|
|
61
|
+
readonly operation: string;
|
|
62
|
+
readonly intent: string;
|
|
63
|
+
}): string {
|
|
64
|
+
if (options.operation) {
|
|
65
|
+
return `${options.brand} ${options.operation} → ${options.intent}`;
|
|
66
|
+
}
|
|
67
|
+
return `${options.brand} ${options.intent}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Wraps text to fit within a specified width using wrap-ansi.
|
|
72
|
+
*/
|
|
73
|
+
function wrapTextAnsi(text: string, width: number): string[] {
|
|
74
|
+
const wrapped = wrapAnsi(text, width, { hard: false, trim: true });
|
|
75
|
+
return wrapped.split('\n');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Formats a default value as "default: <value>" with dimming.
|
|
80
|
+
*/
|
|
81
|
+
function formatDefaultValue(value: unknown, useColor: boolean): string {
|
|
82
|
+
const valueStr = String(value);
|
|
83
|
+
const defaultText = `default: ${valueStr}`;
|
|
84
|
+
return useColor ? dim(defaultText) : defaultText;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Formats a "Read more" URL line.
|
|
89
|
+
*/
|
|
90
|
+
function formatReadMoreLine(options: {
|
|
91
|
+
readonly url: string;
|
|
92
|
+
readonly maxLabelWidth: number;
|
|
93
|
+
readonly useColor: boolean;
|
|
94
|
+
readonly formatDimText: (text: string) => string;
|
|
95
|
+
}): string {
|
|
96
|
+
const labelPadded = `Read more${' '.repeat(Math.max(0, options.maxLabelWidth - 'Read more'.length))}`;
|
|
97
|
+
const valueColored = options.useColor ? blue(options.url) : options.url;
|
|
98
|
+
return `${options.formatDimText('│')} ${labelPadded} ${valueColored}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Formats multiline description with "Prisma Next" in green.
|
|
103
|
+
*/
|
|
104
|
+
function formatMultilineDescription(options: {
|
|
105
|
+
readonly descriptionLines: readonly string[];
|
|
106
|
+
readonly useColor: boolean;
|
|
107
|
+
readonly formatDimText: (text: string) => string;
|
|
108
|
+
}): string[] {
|
|
109
|
+
const lines: string[] = [];
|
|
110
|
+
const formatGreen = (text: string) => (options.useColor ? green(text) : text);
|
|
111
|
+
|
|
112
|
+
const rightColumnWidth = calculateRightColumnWidth();
|
|
113
|
+
const totalWidth = 2 + LEFT_COLUMN_WIDTH + 2 + rightColumnWidth;
|
|
114
|
+
const wrapWidth = totalWidth - 2;
|
|
115
|
+
|
|
116
|
+
for (const descLine of options.descriptionLines) {
|
|
117
|
+
const formattedLine = descLine.replace(/Prisma Next/g, (match) => formatGreen(match));
|
|
118
|
+
const wrappedLines = wrapTextAnsi(formattedLine, wrapWidth);
|
|
119
|
+
for (const wrappedLine of wrappedLines) {
|
|
120
|
+
lines.push(`${options.formatDimText('│')} ${wrappedLine}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return lines;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Maps command paths to their documentation URLs.
|
|
128
|
+
*/
|
|
129
|
+
function getCommandDocsUrl(commandPath: string): string | undefined {
|
|
130
|
+
const docsMap: Record<string, string> = {
|
|
131
|
+
'contract emit': 'https://pris.ly/contract-emit',
|
|
132
|
+
'contract infer': 'https://pris.ly/contract-infer',
|
|
133
|
+
'db schema': 'https://pris.ly/db-schema',
|
|
134
|
+
'db verify': 'https://pris.ly/db-verify',
|
|
135
|
+
'db update': 'https://pris.ly/db-update',
|
|
136
|
+
'migration plan': 'https://pris.ly/migration-plan',
|
|
137
|
+
'migration apply': 'https://pris.ly/migration-apply',
|
|
138
|
+
'migration show': 'https://pris.ly/migration-show',
|
|
139
|
+
'migration status': 'https://pris.ly/migration-status',
|
|
140
|
+
'migration verify': 'https://pris.ly/migration-verify',
|
|
141
|
+
};
|
|
142
|
+
return docsMap[commandPath];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Builds the full command path from a command and its parents.
|
|
147
|
+
*/
|
|
148
|
+
function buildCommandPath(command: Command): string {
|
|
149
|
+
const parts: string[] = [];
|
|
150
|
+
let current: Command | undefined = command;
|
|
151
|
+
while (current && current.name() !== 'prisma-next') {
|
|
152
|
+
parts.unshift(current.name());
|
|
153
|
+
current = current.parent ?? undefined;
|
|
154
|
+
}
|
|
155
|
+
return parts.join(' ');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Formats help output for a command using the styled format.
|
|
160
|
+
*/
|
|
161
|
+
export function formatCommandHelp(options: {
|
|
162
|
+
readonly command: Command;
|
|
163
|
+
readonly flags: GlobalFlags;
|
|
164
|
+
}): string {
|
|
165
|
+
const { command, flags } = options;
|
|
166
|
+
const lines: string[] = [];
|
|
167
|
+
const useColor = flags.color !== false;
|
|
168
|
+
const formatDimText = (text: string) => formatDim(useColor, text);
|
|
169
|
+
|
|
170
|
+
// Build full command path (e.g., "db verify")
|
|
171
|
+
const commandPath = buildCommandPath(command);
|
|
172
|
+
const shortDescription = command.description() || '';
|
|
173
|
+
const longDescription = getLongDescription(command);
|
|
174
|
+
|
|
175
|
+
// Include positional arguments in the header line
|
|
176
|
+
const argsSuffix = command.registeredArguments
|
|
177
|
+
.map((arg) => (arg.required ? `<${arg.name()}>` : `[${arg.name()}]`))
|
|
178
|
+
.join(' ');
|
|
179
|
+
const brand = createPrismaNextBadge(useColor);
|
|
180
|
+
const commandWithArgs = argsSuffix ? `${commandPath} ${argsSuffix}` : commandPath;
|
|
181
|
+
const operation = useColor ? bold(commandWithArgs) : commandWithArgs;
|
|
182
|
+
const intent = formatDimText(shortDescription);
|
|
183
|
+
lines.push(formatHeaderLine({ brand, operation, intent }));
|
|
184
|
+
lines.push(formatDimText('│'));
|
|
185
|
+
|
|
186
|
+
// Extract options and format them
|
|
187
|
+
const optionsList = command.options.map((opt) => {
|
|
188
|
+
const description = opt.description || '';
|
|
189
|
+
// Commander.js stores default value in defaultValue property
|
|
190
|
+
const defaultValue = (opt as { defaultValue?: unknown }).defaultValue;
|
|
191
|
+
return { flags: opt.flags, description, defaultValue };
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Extract subcommands if any
|
|
195
|
+
const subcommands = command.commands.filter((cmd) => !cmd.name().startsWith('_'));
|
|
196
|
+
|
|
197
|
+
// Format subcommands as a tree if present
|
|
198
|
+
if (subcommands.length > 0) {
|
|
199
|
+
const hasItemsAfter = optionsList.length > 0;
|
|
200
|
+
const treeLines = renderCommandTree({
|
|
201
|
+
commands: subcommands,
|
|
202
|
+
useColor,
|
|
203
|
+
formatDimText,
|
|
204
|
+
hasItemsAfter,
|
|
205
|
+
});
|
|
206
|
+
lines.push(...treeLines);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Add separator between subcommands and options if both exist
|
|
210
|
+
if (subcommands.length > 0 && optionsList.length > 0) {
|
|
211
|
+
lines.push(formatDimText('│'));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Format options with fixed width, wrapping, and default values
|
|
215
|
+
if (optionsList.length > 0) {
|
|
216
|
+
for (const opt of optionsList) {
|
|
217
|
+
// Format flag with fixed 30-char width
|
|
218
|
+
const flagsPadded = padToFixedWidth(opt.flags, LEFT_COLUMN_WIDTH);
|
|
219
|
+
let flagsColored = flagsPadded;
|
|
220
|
+
if (useColor) {
|
|
221
|
+
// Color placeholders in magenta, then wrap in cyan
|
|
222
|
+
flagsColored = flagsPadded.replace(/(<[^>]+>)/g, (match: string) => magenta(match));
|
|
223
|
+
flagsColored = cyan(flagsColored);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Wrap description based on terminal width
|
|
227
|
+
const rightColumnWidth = calculateRightColumnWidth();
|
|
228
|
+
const wrappedDescription = wrapTextAnsi(opt.description, rightColumnWidth);
|
|
229
|
+
|
|
230
|
+
// First line: flag + first line of description
|
|
231
|
+
lines.push(`${formatDimText('│')} ${flagsColored} ${wrappedDescription[0] || ''}`);
|
|
232
|
+
|
|
233
|
+
// Continuation lines: empty label (30 spaces) + wrapped lines
|
|
234
|
+
for (let i = 1; i < wrappedDescription.length; i++) {
|
|
235
|
+
const emptyLabel = ' '.repeat(LEFT_COLUMN_WIDTH);
|
|
236
|
+
lines.push(`${formatDimText('│')} ${emptyLabel} ${wrappedDescription[i] || ''}`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Default value line (if present)
|
|
240
|
+
if (opt.defaultValue !== undefined) {
|
|
241
|
+
const emptyLabel = ' '.repeat(LEFT_COLUMN_WIDTH);
|
|
242
|
+
const defaultText = formatDefaultValue(opt.defaultValue, useColor);
|
|
243
|
+
lines.push(`${formatDimText('│')} ${emptyLabel} ${defaultText}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Add docs URL if available (with separator line before it)
|
|
249
|
+
const docsUrl = getCommandDocsUrl(commandPath);
|
|
250
|
+
if (docsUrl) {
|
|
251
|
+
lines.push(formatDimText('│')); // Separator line between params and docs
|
|
252
|
+
lines.push(
|
|
253
|
+
formatReadMoreLine({
|
|
254
|
+
url: docsUrl,
|
|
255
|
+
maxLabelWidth: LEFT_COLUMN_WIDTH,
|
|
256
|
+
useColor,
|
|
257
|
+
formatDimText,
|
|
258
|
+
}),
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Examples (copy-pastable)
|
|
263
|
+
const examples = getCommandExamples(command);
|
|
264
|
+
if (examples && examples.length > 0) {
|
|
265
|
+
lines.push(formatDimText('│'));
|
|
266
|
+
lines.push(`${formatDimText('│')} ${formatDimText('Examples:')}`);
|
|
267
|
+
for (const example of examples) {
|
|
268
|
+
lines.push(`${formatDimText('│')} ${useColor ? dim('$') : '$'} ${example}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Multi-line description (if present) - shown after all other content
|
|
273
|
+
if (longDescription) {
|
|
274
|
+
lines.push(formatDimText('│'));
|
|
275
|
+
const descriptionLines = longDescription.split('\n').filter((line) => line.trim().length > 0);
|
|
276
|
+
lines.push(...formatMultilineDescription({ descriptionLines, useColor, formatDimText }));
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
lines.push(formatDimText('└'));
|
|
280
|
+
|
|
281
|
+
return `${lines.join('\n')}\n`;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Formats help output for the root program using the styled format.
|
|
286
|
+
*/
|
|
287
|
+
export function formatRootHelp(options: {
|
|
288
|
+
readonly program: Command;
|
|
289
|
+
readonly flags: GlobalFlags;
|
|
290
|
+
}): string {
|
|
291
|
+
const { program, flags } = options;
|
|
292
|
+
const lines: string[] = [];
|
|
293
|
+
const useColor = flags.color !== false;
|
|
294
|
+
const formatDimText = (text: string) => formatDim(useColor, text);
|
|
295
|
+
|
|
296
|
+
// Header: "prisma-next -> Manage your data layer"
|
|
297
|
+
const brand = createPrismaNextBadge(useColor);
|
|
298
|
+
const shortDescription = 'Manage your data layer';
|
|
299
|
+
const intent = formatDimText(shortDescription);
|
|
300
|
+
lines.push(formatHeaderLine({ brand, operation: '', intent }));
|
|
301
|
+
lines.push(formatDimText('│')); // Vertical line separator after header
|
|
302
|
+
|
|
303
|
+
// Extract top-level commands (exclude hidden commands starting with '_' and the 'help' command)
|
|
304
|
+
const topLevelCommands = program.commands.filter(
|
|
305
|
+
(cmd) => !cmd.name().startsWith('_') && cmd.name() !== 'help',
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
// Extract global options (needed to determine if last command)
|
|
309
|
+
const globalOptions = program.options.map((opt) => {
|
|
310
|
+
const description = opt.description || '';
|
|
311
|
+
// Commander.js stores default value in defaultValue property
|
|
312
|
+
const defaultValue = (opt as { defaultValue?: unknown }).defaultValue;
|
|
313
|
+
return { flags: opt.flags, description, defaultValue };
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// Build command tree
|
|
317
|
+
if (topLevelCommands.length > 0) {
|
|
318
|
+
const hasItemsAfter = globalOptions.length > 0;
|
|
319
|
+
const treeLines = renderCommandTree({
|
|
320
|
+
commands: topLevelCommands,
|
|
321
|
+
useColor,
|
|
322
|
+
formatDimText,
|
|
323
|
+
hasItemsAfter,
|
|
324
|
+
});
|
|
325
|
+
lines.push(...treeLines);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Add separator between commands and options if both exist
|
|
329
|
+
if (topLevelCommands.length > 0 && globalOptions.length > 0) {
|
|
330
|
+
lines.push(formatDimText('│'));
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Format global options with fixed width, wrapping, and default values
|
|
334
|
+
if (globalOptions.length > 0) {
|
|
335
|
+
for (const opt of globalOptions) {
|
|
336
|
+
// Format flag with fixed 30-char width
|
|
337
|
+
const flagsPadded = padToFixedWidth(opt.flags, LEFT_COLUMN_WIDTH);
|
|
338
|
+
let flagsColored = flagsPadded;
|
|
339
|
+
if (useColor) {
|
|
340
|
+
// Color placeholders in magenta, then wrap in cyan
|
|
341
|
+
flagsColored = flagsPadded.replace(/(<[^>]+>)/g, (match: string) => magenta(match));
|
|
342
|
+
flagsColored = cyan(flagsColored);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Wrap description based on terminal width
|
|
346
|
+
const rightColumnWidth = calculateRightColumnWidth();
|
|
347
|
+
const wrappedDescription = wrapTextAnsi(opt.description, rightColumnWidth);
|
|
348
|
+
|
|
349
|
+
// First line: flag + first line of description
|
|
350
|
+
lines.push(`${formatDimText('│')} ${flagsColored} ${wrappedDescription[0] || ''}`);
|
|
351
|
+
|
|
352
|
+
// Continuation lines: empty label (30 spaces) + wrapped lines
|
|
353
|
+
for (let i = 1; i < wrappedDescription.length; i++) {
|
|
354
|
+
const emptyLabel = ' '.repeat(LEFT_COLUMN_WIDTH);
|
|
355
|
+
lines.push(`${formatDimText('│')} ${emptyLabel} ${wrappedDescription[i] || ''}`);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Default value line (if present)
|
|
359
|
+
if (opt.defaultValue !== undefined) {
|
|
360
|
+
const emptyLabel = ' '.repeat(LEFT_COLUMN_WIDTH);
|
|
361
|
+
const defaultText = formatDefaultValue(opt.defaultValue, useColor);
|
|
362
|
+
lines.push(`${formatDimText('│')} ${emptyLabel} ${defaultText}`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Multi-line description (white, not dimmed, with "Prisma Next" in green) - shown at bottom
|
|
368
|
+
const formatGreen = (text: string) => (useColor ? green(text) : text);
|
|
369
|
+
const descriptionLines = [
|
|
370
|
+
`Use ${formatGreen('Prisma Next')} to define your data layer as a contract. Sign your database and application with the same contract to guarantee compatibility. Plan and apply migrations to safely evolve your schema.`,
|
|
371
|
+
];
|
|
372
|
+
if (descriptionLines.length > 0) {
|
|
373
|
+
lines.push(formatDimText('│')); // Separator line before description
|
|
374
|
+
lines.push(...formatMultilineDescription({ descriptionLines, useColor, formatDimText }));
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
lines.push(formatDimText('└'));
|
|
378
|
+
|
|
379
|
+
return `${lines.join('\n')}\n`;
|
|
380
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { dim } from 'colorette';
|
|
2
|
+
|
|
3
|
+
import type { GlobalFlags } from '../global-flags';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Checks if verbose output is enabled at the specified level.
|
|
7
|
+
*/
|
|
8
|
+
export function isVerbose(flags: GlobalFlags, level: 1 | 2): boolean {
|
|
9
|
+
return (flags.verbose ?? 0) >= level;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates a color-aware formatter function.
|
|
14
|
+
* Returns a function that applies the color only if colors are enabled.
|
|
15
|
+
*/
|
|
16
|
+
export function createColorFormatter<T extends (text: string) => string>(
|
|
17
|
+
useColor: boolean,
|
|
18
|
+
colorFn: T,
|
|
19
|
+
): (text: string) => string {
|
|
20
|
+
return useColor ? colorFn : (text: string) => text;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Formats text with dim styling if colors are enabled.
|
|
25
|
+
*/
|
|
26
|
+
export function formatDim(useColor: boolean, text: string): string {
|
|
27
|
+
return useColor ? dim(text) : text;
|
|
28
|
+
}
|