@prisma-next/cli 0.3.0-pr.99.6 → 0.4.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/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 +254 -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 +4 -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 +4 -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 +125 -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 +53 -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 +136 -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 +122 -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 +322 -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 +244 -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 +4 -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-D9WOShFz.mjs +4 -0
- package/dist/contract-emit-Zm_sd1wQ.mjs +112 -0
- package/dist/contract-emit-Zm_sd1wQ.mjs.map +1 -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 +6 -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 +137 -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-DQ8auNB4.mjs +430 -0
- package/dist/init-DQ8auNB4.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,346 @@
|
|
|
1
|
+
import { green, yellow } from 'colorette';
|
|
2
|
+
|
|
3
|
+
import type { GlobalFlags } from '../global-flags';
|
|
4
|
+
import { createColorFormatter, formatDim, isVerbose } from './helpers';
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Migration Command Output Formatters (shared by db init and db update)
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Shared CLI output type for migration commands (db init, db update).
|
|
12
|
+
*/
|
|
13
|
+
export interface MigrationCommandResult {
|
|
14
|
+
readonly ok: true;
|
|
15
|
+
readonly mode: 'plan' | 'apply';
|
|
16
|
+
readonly plan: {
|
|
17
|
+
readonly targetId: string;
|
|
18
|
+
readonly destination: {
|
|
19
|
+
readonly storageHash: string;
|
|
20
|
+
readonly profileHash?: string;
|
|
21
|
+
};
|
|
22
|
+
readonly operations: readonly {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly label: string;
|
|
25
|
+
readonly operationClass: string;
|
|
26
|
+
}[];
|
|
27
|
+
readonly sql?: readonly string[];
|
|
28
|
+
};
|
|
29
|
+
readonly execution?: {
|
|
30
|
+
readonly operationsPlanned: number;
|
|
31
|
+
readonly operationsExecuted: number;
|
|
32
|
+
};
|
|
33
|
+
readonly marker?: {
|
|
34
|
+
readonly storageHash: string;
|
|
35
|
+
readonly profileHash?: string;
|
|
36
|
+
};
|
|
37
|
+
readonly summary: string;
|
|
38
|
+
readonly timings: {
|
|
39
|
+
readonly total: number;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Formats human-readable output for migration commands (db init, db update) in plan mode.
|
|
45
|
+
*/
|
|
46
|
+
export function formatMigrationPlanOutput(
|
|
47
|
+
result: MigrationCommandResult,
|
|
48
|
+
flags: GlobalFlags,
|
|
49
|
+
): string {
|
|
50
|
+
if (flags.quiet) {
|
|
51
|
+
return '';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const lines: string[] = [];
|
|
55
|
+
|
|
56
|
+
const useColor = flags.color !== false;
|
|
57
|
+
const formatGreen = createColorFormatter(useColor, green);
|
|
58
|
+
const formatDimText = (text: string) => formatDim(useColor, text);
|
|
59
|
+
|
|
60
|
+
// Plan summary
|
|
61
|
+
const operationCount = result.plan?.operations.length ?? 0;
|
|
62
|
+
lines.push(`${formatGreen('✔')} Planned ${operationCount} operation(s)`);
|
|
63
|
+
|
|
64
|
+
// Show operations tree
|
|
65
|
+
if (result.plan?.operations && result.plan.operations.length > 0) {
|
|
66
|
+
const formatYellow = createColorFormatter(useColor, yellow);
|
|
67
|
+
lines.push(`${formatDimText('│')}`);
|
|
68
|
+
for (let i = 0; i < result.plan.operations.length; i++) {
|
|
69
|
+
const op = result.plan.operations[i];
|
|
70
|
+
if (!op) continue;
|
|
71
|
+
const isLast = i === result.plan.operations.length - 1;
|
|
72
|
+
const treeChar = isLast ? '└' : '├';
|
|
73
|
+
const opClassLabel =
|
|
74
|
+
op.operationClass === 'destructive'
|
|
75
|
+
? formatYellow(`[${op.operationClass}]`)
|
|
76
|
+
: formatDimText(`[${op.operationClass}]`);
|
|
77
|
+
lines.push(`${formatDimText(treeChar)}─ ${op.label} ${opClassLabel}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const hasDestructive = result.plan.operations.some((op) => op.operationClass === 'destructive');
|
|
81
|
+
if (hasDestructive) {
|
|
82
|
+
lines.push('');
|
|
83
|
+
lines.push(
|
|
84
|
+
`${formatYellow('⚠')} This migration contains destructive operations that may cause data loss.`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Destination hash
|
|
90
|
+
if (result.plan?.destination) {
|
|
91
|
+
lines.push('');
|
|
92
|
+
lines.push(`${formatDimText(`Destination hash: ${result.plan.destination.storageHash}`)}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// SQL DDL preview (SQL family only)
|
|
96
|
+
const planSql = result.plan?.sql;
|
|
97
|
+
if (planSql) {
|
|
98
|
+
lines.push('');
|
|
99
|
+
lines.push(`${formatDimText('DDL preview')}`);
|
|
100
|
+
if (planSql.length === 0) {
|
|
101
|
+
lines.push(`${formatDimText('No DDL operations.')}`);
|
|
102
|
+
} else {
|
|
103
|
+
lines.push('');
|
|
104
|
+
for (const statement of planSql) {
|
|
105
|
+
const trimmed = statement.trim();
|
|
106
|
+
if (!trimmed) continue;
|
|
107
|
+
const line = trimmed.endsWith(';') ? trimmed : `${trimmed};`;
|
|
108
|
+
lines.push(`${line}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Timings in verbose mode
|
|
114
|
+
if (isVerbose(flags, 1)) {
|
|
115
|
+
lines.push(`${formatDimText(`Total time: ${result.timings.total}ms`)}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Note about dry run
|
|
119
|
+
lines.push('');
|
|
120
|
+
lines.push(`${formatDimText('This is a dry run. No changes were applied.')}`);
|
|
121
|
+
lines.push(`${formatDimText('Run without --dry-run to apply changes.')}`);
|
|
122
|
+
|
|
123
|
+
return lines.join('\n');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface MigrationApplyCommandOutputResult {
|
|
127
|
+
readonly migrationsApplied: number;
|
|
128
|
+
readonly markerHash: string;
|
|
129
|
+
readonly applied: readonly {
|
|
130
|
+
readonly dirName: string;
|
|
131
|
+
readonly operationsExecuted: number;
|
|
132
|
+
}[];
|
|
133
|
+
readonly summary: string;
|
|
134
|
+
readonly timings?: {
|
|
135
|
+
readonly total: number;
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface MigrationVerifyCommandOutputResult {
|
|
140
|
+
readonly status: 'verified' | 'attested';
|
|
141
|
+
readonly migrationId?: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function formatMigrationApplyCommandOutput(
|
|
145
|
+
result: MigrationApplyCommandOutputResult,
|
|
146
|
+
flags: GlobalFlags,
|
|
147
|
+
): string {
|
|
148
|
+
if (flags.quiet) {
|
|
149
|
+
return '';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const lines: string[] = [];
|
|
153
|
+
const useColor = flags.color !== false;
|
|
154
|
+
const formatGreen = createColorFormatter(useColor, green);
|
|
155
|
+
const formatDimText = (text: string) => formatDim(useColor, text);
|
|
156
|
+
|
|
157
|
+
if (result.migrationsApplied === 0) {
|
|
158
|
+
lines.push(`${formatGreen('✔')} ${result.summary}`);
|
|
159
|
+
lines.push(formatDimText(` marker: ${result.markerHash}`));
|
|
160
|
+
return lines.join('\n');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
lines.push(`${formatGreen('✔')} ${result.summary}`);
|
|
164
|
+
lines.push('');
|
|
165
|
+
|
|
166
|
+
for (let i = 0; i < result.applied.length; i++) {
|
|
167
|
+
const migration = result.applied[i]!;
|
|
168
|
+
const isLast = i === result.applied.length - 1;
|
|
169
|
+
const treeChar = isLast ? '└' : '├';
|
|
170
|
+
lines.push(
|
|
171
|
+
`${formatDimText(treeChar)}─ ${migration.dirName} ${formatDimText(`[${migration.operationsExecuted} op(s)]`)}`,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
lines.push('');
|
|
176
|
+
lines.push(formatDimText(`marker: ${result.markerHash}`));
|
|
177
|
+
|
|
178
|
+
if (isVerbose(flags, 1) && result.timings) {
|
|
179
|
+
lines.push('');
|
|
180
|
+
lines.push(formatDimText(`Total time: ${result.timings.total}ms`));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return lines.join('\n');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function formatMigrationVerifyCommandOutput(
|
|
187
|
+
result: MigrationVerifyCommandOutputResult,
|
|
188
|
+
flags: GlobalFlags,
|
|
189
|
+
): string {
|
|
190
|
+
if (flags.quiet) {
|
|
191
|
+
return '';
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const lines: string[] = [];
|
|
195
|
+
const useColor = flags.color !== false;
|
|
196
|
+
const formatGreen = createColorFormatter(useColor, green);
|
|
197
|
+
const formatYellow = createColorFormatter(useColor, yellow);
|
|
198
|
+
const formatDimText = (text: string) => formatDim(useColor, text);
|
|
199
|
+
|
|
200
|
+
switch (result.status) {
|
|
201
|
+
case 'verified':
|
|
202
|
+
lines.push(`${formatGreen('✔')} Migration verified`);
|
|
203
|
+
if (result.migrationId) {
|
|
204
|
+
lines.push(formatDimText(` migrationId: ${result.migrationId}`));
|
|
205
|
+
}
|
|
206
|
+
break;
|
|
207
|
+
case 'attested':
|
|
208
|
+
lines.push(`${formatYellow('◉')} Draft migration attested`);
|
|
209
|
+
if (result.migrationId) {
|
|
210
|
+
lines.push(formatDimText(` migrationId: ${result.migrationId}`));
|
|
211
|
+
}
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return lines.join('\n');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
interface MigrationShowResult {
|
|
219
|
+
readonly dirName: string;
|
|
220
|
+
readonly dirPath: string;
|
|
221
|
+
readonly from: string;
|
|
222
|
+
readonly to: string;
|
|
223
|
+
readonly migrationId: string | null;
|
|
224
|
+
readonly kind: string;
|
|
225
|
+
readonly createdAt: string;
|
|
226
|
+
readonly operations: readonly {
|
|
227
|
+
readonly id: string;
|
|
228
|
+
readonly label: string;
|
|
229
|
+
readonly operationClass: string;
|
|
230
|
+
}[];
|
|
231
|
+
readonly sql: readonly string[];
|
|
232
|
+
readonly summary: string;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function formatMigrationShowOutput(result: MigrationShowResult, flags: GlobalFlags): string {
|
|
236
|
+
if (flags.quiet) {
|
|
237
|
+
return '';
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const lines: string[] = [];
|
|
241
|
+
|
|
242
|
+
const useColor = flags.color !== false;
|
|
243
|
+
const formatGreen = createColorFormatter(useColor, green);
|
|
244
|
+
const formatYellow = createColorFormatter(useColor, yellow);
|
|
245
|
+
const formatDimText = (text: string) => formatDim(useColor, text);
|
|
246
|
+
|
|
247
|
+
lines.push(`${formatGreen('✔')} ${result.dirName}`);
|
|
248
|
+
lines.push(`${formatDimText(` kind: ${result.kind}`)}`);
|
|
249
|
+
lines.push(`${formatDimText(` from: ${result.from}`)}`);
|
|
250
|
+
lines.push(`${formatDimText(` to: ${result.to}`)}`);
|
|
251
|
+
if (result.migrationId) {
|
|
252
|
+
lines.push(`${formatDimText(` migrationId: ${result.migrationId}`)}`);
|
|
253
|
+
} else {
|
|
254
|
+
lines.push(`${formatYellow(' migrationId: (draft — not yet attested)')}`);
|
|
255
|
+
}
|
|
256
|
+
lines.push(`${formatDimText(` created: ${result.createdAt}`)}`);
|
|
257
|
+
|
|
258
|
+
lines.push('');
|
|
259
|
+
lines.push(`${result.operations.length} operation(s)`);
|
|
260
|
+
|
|
261
|
+
if (result.operations.length > 0) {
|
|
262
|
+
lines.push(`${formatDimText('│')}`);
|
|
263
|
+
for (let i = 0; i < result.operations.length; i++) {
|
|
264
|
+
const op = result.operations[i]!;
|
|
265
|
+
const isLast = i === result.operations.length - 1;
|
|
266
|
+
const treeChar = isLast ? '└' : '├';
|
|
267
|
+
const opClassLabel =
|
|
268
|
+
op.operationClass === 'destructive'
|
|
269
|
+
? formatYellow(`[${op.operationClass}]`)
|
|
270
|
+
: formatDimText(`[${op.operationClass}]`);
|
|
271
|
+
lines.push(`${formatDimText(treeChar)}─ ${op.label} ${opClassLabel}`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const hasDestructive = result.operations.some((op) => op.operationClass === 'destructive');
|
|
275
|
+
if (hasDestructive) {
|
|
276
|
+
lines.push('');
|
|
277
|
+
lines.push(
|
|
278
|
+
`${formatYellow('⚠')} This migration contains destructive operations that may cause data loss.`,
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (result.sql.length > 0) {
|
|
284
|
+
lines.push('');
|
|
285
|
+
lines.push(`${formatDimText('DDL preview')}`);
|
|
286
|
+
lines.push('');
|
|
287
|
+
for (const statement of result.sql) {
|
|
288
|
+
const trimmed = statement.trim();
|
|
289
|
+
if (!trimmed) continue;
|
|
290
|
+
const line = trimmed.endsWith(';') ? trimmed : `${trimmed};`;
|
|
291
|
+
lines.push(`${line}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return lines.join('\n');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Formats human-readable output for migration commands (db init, db update) in apply mode.
|
|
300
|
+
*/
|
|
301
|
+
export function formatMigrationApplyOutput(
|
|
302
|
+
result: MigrationCommandResult,
|
|
303
|
+
flags: GlobalFlags,
|
|
304
|
+
): string {
|
|
305
|
+
if (flags.quiet) {
|
|
306
|
+
return '';
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const lines: string[] = [];
|
|
310
|
+
|
|
311
|
+
const useColor = flags.color !== false;
|
|
312
|
+
const formatGreen = createColorFormatter(useColor, green);
|
|
313
|
+
const formatDimText = (text: string) => formatDim(useColor, text);
|
|
314
|
+
|
|
315
|
+
if (result.ok) {
|
|
316
|
+
// Success summary
|
|
317
|
+
const executed = result.execution?.operationsExecuted ?? 0;
|
|
318
|
+
if (executed === 0) {
|
|
319
|
+
lines.push(`${formatGreen('✔')} Database already matches contract`);
|
|
320
|
+
} else {
|
|
321
|
+
lines.push(`${formatGreen('✔')} Applied ${executed} operation(s)`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Marker info
|
|
325
|
+
if (result.marker) {
|
|
326
|
+
lines.push(`${formatDimText(` Signature: ${result.marker.storageHash}`)}`);
|
|
327
|
+
if (result.marker.profileHash) {
|
|
328
|
+
lines.push(`${formatDimText(` Profile hash: ${result.marker.profileHash}`)}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Timings in verbose mode
|
|
333
|
+
if (isVerbose(flags, 1)) {
|
|
334
|
+
lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return lines.join('\n');
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Formats JSON output for migration commands (db init, db update).
|
|
343
|
+
*/
|
|
344
|
+
export function formatMigrationJson(result: MigrationCommandResult): string {
|
|
345
|
+
return JSON.stringify(result, null, 2);
|
|
346
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { blue, bold, cyan, green } from 'colorette';
|
|
2
|
+
import type { Command } from 'commander';
|
|
3
|
+
import stringWidth from 'string-width';
|
|
4
|
+
import stripAnsi from 'strip-ansi';
|
|
5
|
+
|
|
6
|
+
import type { GlobalFlags } from '../global-flags';
|
|
7
|
+
import { createColorFormatter, formatDim } from './helpers';
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Styled Output Formatters
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Fixed width for left column in help output.
|
|
15
|
+
*/
|
|
16
|
+
const LEFT_COLUMN_WIDTH = 20;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates an arrow segment badge with green background and white text.
|
|
20
|
+
* Body: green background with white "prisma-next" text
|
|
21
|
+
* Tip: dark grey arrow pointing right (Powerline separator)
|
|
22
|
+
*/
|
|
23
|
+
function createPrismaNextBadge(useColor: boolean): string {
|
|
24
|
+
if (!useColor) {
|
|
25
|
+
return 'prisma-next';
|
|
26
|
+
}
|
|
27
|
+
return bold('prisma-next');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Creates a padding function.
|
|
32
|
+
*/
|
|
33
|
+
function createPadFunction(): (s: string, w: number) => string {
|
|
34
|
+
return (s: string, w: number) => s + ' '.repeat(Math.max(0, w - s.length));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Formats a header line: brand + operation + intent
|
|
39
|
+
*/
|
|
40
|
+
function formatHeaderLine(options: {
|
|
41
|
+
readonly brand: string;
|
|
42
|
+
readonly operation: string;
|
|
43
|
+
readonly intent: string;
|
|
44
|
+
}): string {
|
|
45
|
+
if (options.operation) {
|
|
46
|
+
return `${options.brand} ${options.operation} → ${options.intent}`;
|
|
47
|
+
}
|
|
48
|
+
return `${options.brand} ${options.intent}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Formats a "Read more" URL line.
|
|
53
|
+
* The "Read more" label is in default color (not cyan), and the URL is blue.
|
|
54
|
+
*/
|
|
55
|
+
function formatReadMoreLine(options: {
|
|
56
|
+
readonly url: string;
|
|
57
|
+
readonly maxLabelWidth: number;
|
|
58
|
+
readonly useColor: boolean;
|
|
59
|
+
readonly formatDimText: (text: string) => string;
|
|
60
|
+
}): string {
|
|
61
|
+
const pad = createPadFunction();
|
|
62
|
+
const labelPadded = pad('Read more', options.maxLabelWidth);
|
|
63
|
+
// Label is default color (not cyan)
|
|
64
|
+
const valueColored = options.useColor ? blue(options.url) : options.url;
|
|
65
|
+
return `${options.formatDimText('│')} ${labelPadded} ${valueColored}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Pads text to a fixed width, accounting for ANSI escape codes.
|
|
70
|
+
* Uses string-width to measure the actual display width.
|
|
71
|
+
*/
|
|
72
|
+
export function padToFixedWidth(text: string, width: number): string {
|
|
73
|
+
const actualWidth = stringWidth(text);
|
|
74
|
+
const padding = Math.max(0, width - actualWidth);
|
|
75
|
+
return text + ' '.repeat(padding);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Renders a command tree structure.
|
|
80
|
+
* Handles both single-level (subcommands of a command) and multi-level (top-level commands with subcommands) trees.
|
|
81
|
+
*/
|
|
82
|
+
export function renderCommandTree(options: {
|
|
83
|
+
readonly commands: readonly Command[];
|
|
84
|
+
readonly useColor: boolean;
|
|
85
|
+
readonly formatDimText: (text: string) => string;
|
|
86
|
+
readonly hasItemsAfter: boolean;
|
|
87
|
+
readonly continuationPrefix?: string;
|
|
88
|
+
}): string[] {
|
|
89
|
+
const { commands, useColor, formatDimText, hasItemsAfter, continuationPrefix } = options;
|
|
90
|
+
const lines: string[] = [];
|
|
91
|
+
|
|
92
|
+
if (commands.length === 0) {
|
|
93
|
+
return lines;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Format each command
|
|
97
|
+
for (let i = 0; i < commands.length; i++) {
|
|
98
|
+
const cmd = commands[i];
|
|
99
|
+
if (!cmd) continue;
|
|
100
|
+
|
|
101
|
+
const subcommands = cmd.commands.filter((subcmd) => !subcmd.name().startsWith('_'));
|
|
102
|
+
const isLastCommand = i === commands.length - 1;
|
|
103
|
+
|
|
104
|
+
if (subcommands.length > 0) {
|
|
105
|
+
// Command with subcommands - show command name, then tree-structured subcommands
|
|
106
|
+
const treeChar = isLastCommand && !hasItemsAfter ? formatDimText('└') : formatDimText('├');
|
|
107
|
+
// For top-level command, pad name to fixed width (accounting for "| |-- " = 5 chars)
|
|
108
|
+
const treePrefix = `${treeChar}─ `;
|
|
109
|
+
const treePrefixWidth = stringWidth(stripAnsi(treePrefix));
|
|
110
|
+
const remainingWidth = LEFT_COLUMN_WIDTH - treePrefixWidth;
|
|
111
|
+
const commandNamePadded = padToFixedWidth(cmd.name(), remainingWidth);
|
|
112
|
+
const commandNameColored = useColor ? cyan(commandNamePadded) : commandNamePadded;
|
|
113
|
+
lines.push(`${formatDimText('│')} ${treePrefix}${commandNameColored}`);
|
|
114
|
+
|
|
115
|
+
for (let j = 0; j < subcommands.length; j++) {
|
|
116
|
+
const subcmd = subcommands[j];
|
|
117
|
+
if (!subcmd) continue;
|
|
118
|
+
|
|
119
|
+
const isLastSubcommand = j === subcommands.length - 1;
|
|
120
|
+
const shortDescription = subcmd.description() || '';
|
|
121
|
+
|
|
122
|
+
// Use tree characters: -- for last subcommand, |-- for others
|
|
123
|
+
const treeChar = isLastSubcommand ? '└' : '├';
|
|
124
|
+
const continuation =
|
|
125
|
+
continuationPrefix ??
|
|
126
|
+
(isLastCommand && isLastSubcommand && !hasItemsAfter ? ' ' : formatDimText('│'));
|
|
127
|
+
// For subcommands, account for "| | -- " = 7 chars (or "| -- " = 6 chars if continuation is space)
|
|
128
|
+
const continuationStr = continuation === ' ' ? ' ' : continuation;
|
|
129
|
+
const subTreePrefix = `${continuationStr} ${formatDimText(treeChar)}─ `;
|
|
130
|
+
const subTreePrefixWidth = stringWidth(stripAnsi(subTreePrefix));
|
|
131
|
+
const subRemainingWidth = LEFT_COLUMN_WIDTH - subTreePrefixWidth;
|
|
132
|
+
const subcommandNamePadded = padToFixedWidth(subcmd.name(), subRemainingWidth);
|
|
133
|
+
const subcommandNameColored = useColor ? cyan(subcommandNamePadded) : subcommandNamePadded;
|
|
134
|
+
lines.push(
|
|
135
|
+
`${formatDimText('│')} ${subTreePrefix}${subcommandNameColored} ${shortDescription}`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
// Standalone command - show command name and description on same line
|
|
140
|
+
const treeChar = isLastCommand && !hasItemsAfter ? formatDimText('└') : formatDimText('├');
|
|
141
|
+
const treePrefix = `${treeChar}─ `;
|
|
142
|
+
const treePrefixWidth = stringWidth(stripAnsi(treePrefix));
|
|
143
|
+
const remainingWidth = LEFT_COLUMN_WIDTH - treePrefixWidth;
|
|
144
|
+
const commandNamePadded = padToFixedWidth(cmd.name(), remainingWidth);
|
|
145
|
+
const commandNameColored = useColor ? cyan(commandNamePadded) : commandNamePadded;
|
|
146
|
+
const shortDescription = cmd.description() || '';
|
|
147
|
+
lines.push(`${formatDimText('│')} ${treePrefix}${commandNameColored} ${shortDescription}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return lines;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Formats the header in the new experimental visual style.
|
|
156
|
+
* This header appears at the start of command output, showing the operation,
|
|
157
|
+
* intent, documentation link, and parameters.
|
|
158
|
+
*/
|
|
159
|
+
export function formatStyledHeader(options: {
|
|
160
|
+
readonly command: string;
|
|
161
|
+
readonly description: string;
|
|
162
|
+
readonly url?: string;
|
|
163
|
+
readonly details: ReadonlyArray<{ readonly label: string; readonly value: string }>;
|
|
164
|
+
readonly flags: GlobalFlags;
|
|
165
|
+
}): string {
|
|
166
|
+
const lines: string[] = [];
|
|
167
|
+
const useColor = options.flags.color !== false;
|
|
168
|
+
const formatDimText = (text: string) => formatDim(useColor, text);
|
|
169
|
+
|
|
170
|
+
// Header: arrow + operation badge + intent
|
|
171
|
+
const brand = createPrismaNextBadge(useColor);
|
|
172
|
+
// Use full command path (e.g., "contract emit" not just "emit")
|
|
173
|
+
const operation = useColor ? bold(options.command) : options.command;
|
|
174
|
+
const intent = formatDimText(options.description);
|
|
175
|
+
lines.push(formatHeaderLine({ brand, operation, intent }));
|
|
176
|
+
lines.push(formatDimText('│')); // Vertical line separator between command and params
|
|
177
|
+
|
|
178
|
+
// Format details using fixed left column width (same style as help text options)
|
|
179
|
+
for (const detail of options.details) {
|
|
180
|
+
// Add colon to label, then pad to fixed width using padToFixedWidth for ANSI-aware padding
|
|
181
|
+
const labelWithColon = `${detail.label}:`;
|
|
182
|
+
const labelPadded = padToFixedWidth(labelWithColon, LEFT_COLUMN_WIDTH);
|
|
183
|
+
const labelColored = useColor ? cyan(labelPadded) : labelPadded;
|
|
184
|
+
lines.push(`${formatDimText('│')} ${labelColored} ${detail.value}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Add "Read more" URL if present (same style as help text)
|
|
188
|
+
if (options.url) {
|
|
189
|
+
lines.push(formatDimText('│')); // Separator line before "Read more"
|
|
190
|
+
lines.push(
|
|
191
|
+
formatReadMoreLine({
|
|
192
|
+
url: options.url,
|
|
193
|
+
maxLabelWidth: LEFT_COLUMN_WIDTH,
|
|
194
|
+
useColor,
|
|
195
|
+
formatDimText,
|
|
196
|
+
}),
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
lines.push(formatDimText('└'));
|
|
201
|
+
|
|
202
|
+
return `${lines.join('\n')}\n`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Formats a success message in the styled output format.
|
|
207
|
+
*/
|
|
208
|
+
export function formatSuccessMessage(flags: GlobalFlags): string {
|
|
209
|
+
const useColor = flags.color !== false;
|
|
210
|
+
const formatGreen = createColorFormatter(useColor, green);
|
|
211
|
+
return `${formatGreen('✔')} Success`;
|
|
212
|
+
}
|