@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.
Files changed (257) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +381 -128
  3. package/dist/agent-skill-mongo.md +106 -0
  4. package/dist/agent-skill-postgres.md +106 -0
  5. package/dist/cli-errors-BDCYR5ap.mjs +4 -0
  6. package/dist/cli-errors-DStABy9d.d.mts +3 -0
  7. package/dist/cli.d.mts +1 -0
  8. package/dist/cli.js +1 -2910
  9. package/dist/cli.mjs +254 -0
  10. package/dist/cli.mjs.map +1 -0
  11. package/dist/client-DiUkJAeN.mjs +987 -0
  12. package/dist/client-DiUkJAeN.mjs.map +1 -0
  13. package/dist/commands/contract-emit.d.mts +7 -0
  14. package/dist/commands/contract-emit.d.mts.map +1 -0
  15. package/dist/commands/contract-emit.mjs +4 -0
  16. package/dist/commands/contract-infer.d.mts +7 -0
  17. package/dist/commands/contract-infer.d.mts.map +1 -0
  18. package/dist/commands/contract-infer.mjs +4 -0
  19. package/dist/commands/db-init.d.mts +7 -0
  20. package/dist/commands/db-init.d.mts.map +1 -0
  21. package/dist/commands/db-init.mjs +125 -0
  22. package/dist/commands/db-init.mjs.map +1 -0
  23. package/dist/commands/db-schema.d.mts +7 -0
  24. package/dist/commands/db-schema.d.mts.map +1 -0
  25. package/dist/commands/db-schema.mjs +53 -0
  26. package/dist/commands/db-schema.mjs.map +1 -0
  27. package/dist/commands/db-sign.d.mts +7 -0
  28. package/dist/commands/db-sign.d.mts.map +1 -0
  29. package/dist/commands/db-sign.mjs +136 -0
  30. package/dist/commands/db-sign.mjs.map +1 -0
  31. package/dist/commands/db-update.d.mts +7 -0
  32. package/dist/commands/db-update.d.mts.map +1 -0
  33. package/dist/commands/db-update.mjs +122 -0
  34. package/dist/commands/db-update.mjs.map +1 -0
  35. package/dist/commands/db-verify.d.mts +7 -0
  36. package/dist/commands/db-verify.d.mts.map +1 -0
  37. package/dist/commands/db-verify.mjs +322 -0
  38. package/dist/commands/db-verify.mjs.map +1 -0
  39. package/dist/commands/migration-apply.d.mts +36 -0
  40. package/dist/commands/migration-apply.d.mts.map +1 -0
  41. package/dist/commands/migration-apply.mjs +244 -0
  42. package/dist/commands/migration-apply.mjs.map +1 -0
  43. package/dist/commands/migration-new.d.mts +8 -0
  44. package/dist/commands/migration-new.d.mts.map +1 -0
  45. package/dist/commands/migration-new.mjs +152 -0
  46. package/dist/commands/migration-new.mjs.map +1 -0
  47. package/dist/commands/migration-plan.d.mts +47 -0
  48. package/dist/commands/migration-plan.d.mts.map +1 -0
  49. package/dist/commands/migration-plan.mjs +313 -0
  50. package/dist/commands/migration-plan.mjs.map +1 -0
  51. package/dist/commands/migration-ref.d.mts +43 -0
  52. package/dist/commands/migration-ref.d.mts.map +1 -0
  53. package/dist/commands/migration-ref.mjs +195 -0
  54. package/dist/commands/migration-ref.mjs.map +1 -0
  55. package/dist/commands/migration-show.d.mts +28 -0
  56. package/dist/commands/migration-show.d.mts.map +1 -0
  57. package/dist/commands/migration-show.mjs +140 -0
  58. package/dist/commands/migration-show.mjs.map +1 -0
  59. package/dist/commands/migration-status.d.mts +86 -0
  60. package/dist/commands/migration-status.d.mts.map +1 -0
  61. package/dist/commands/migration-status.mjs +4 -0
  62. package/dist/commands/migration-verify.d.mts +16 -0
  63. package/dist/commands/migration-verify.d.mts.map +1 -0
  64. package/dist/commands/migration-verify.mjs +110 -0
  65. package/dist/commands/migration-verify.mjs.map +1 -0
  66. package/dist/config-loader-C4VXKl8f.mjs +43 -0
  67. package/dist/config-loader-C4VXKl8f.mjs.map +1 -0
  68. package/dist/{config-loader.d.ts → config-loader.d.mts} +8 -3
  69. package/dist/config-loader.d.mts.map +1 -0
  70. package/dist/config-loader.mjs +3 -0
  71. package/dist/contract-emit-D2wDXfyo.mjs +191 -0
  72. package/dist/contract-emit-D2wDXfyo.mjs.map +1 -0
  73. package/dist/contract-emit-D9WOShFz.mjs +4 -0
  74. package/dist/contract-emit-Zm_sd1wQ.mjs +112 -0
  75. package/dist/contract-emit-Zm_sd1wQ.mjs.map +1 -0
  76. package/dist/contract-enrichment-CGW6mm-E.mjs +79 -0
  77. package/dist/contract-enrichment-CGW6mm-E.mjs.map +1 -0
  78. package/dist/contract-infer-DozZT511.mjs +90 -0
  79. package/dist/contract-infer-DozZT511.mjs.map +1 -0
  80. package/dist/exports/config-types.d.mts +2 -0
  81. package/dist/exports/config-types.mjs +3 -0
  82. package/dist/exports/control-api.d.mts +624 -0
  83. package/dist/exports/control-api.d.mts.map +1 -0
  84. package/dist/exports/control-api.mjs +6 -0
  85. package/dist/{load-ts-contract.d.ts → exports/index.d.mts} +12 -7
  86. package/dist/exports/index.d.mts.map +1 -0
  87. package/dist/exports/index.mjs +137 -0
  88. package/dist/exports/index.mjs.map +1 -0
  89. package/dist/extract-operation-statements-DZUJNmL3.mjs +13 -0
  90. package/dist/extract-operation-statements-DZUJNmL3.mjs.map +1 -0
  91. package/dist/extract-sql-ddl-DDMX-9mz.mjs +26 -0
  92. package/dist/extract-sql-ddl-DDMX-9mz.mjs.map +1 -0
  93. package/dist/framework-components-BAsliT4V.mjs +59 -0
  94. package/dist/framework-components-BAsliT4V.mjs.map +1 -0
  95. package/dist/init-DQ8auNB4.mjs +430 -0
  96. package/dist/init-DQ8auNB4.mjs.map +1 -0
  97. package/dist/inspect-live-schema-BYnhztxZ.mjs +91 -0
  98. package/dist/inspect-live-schema-BYnhztxZ.mjs.map +1 -0
  99. package/dist/migration-command-scaffold-CntCcntR.mjs +105 -0
  100. package/dist/migration-command-scaffold-CntCcntR.mjs.map +1 -0
  101. package/dist/migration-status-CJANY4yr.mjs +1583 -0
  102. package/dist/migration-status-CJANY4yr.mjs.map +1 -0
  103. package/dist/migrations-DTZBYXm1.mjs +173 -0
  104. package/dist/migrations-DTZBYXm1.mjs.map +1 -0
  105. package/dist/progress-adapter-B-YvmcDu.mjs +43 -0
  106. package/dist/progress-adapter-B-YvmcDu.mjs.map +1 -0
  107. package/dist/quick-reference-mongo.md +93 -0
  108. package/dist/quick-reference-postgres.md +91 -0
  109. package/dist/result-handler-oK_vA-Fn.mjs +697 -0
  110. package/dist/result-handler-oK_vA-Fn.mjs.map +1 -0
  111. package/dist/terminal-ui-C5k88MmW.mjs +274 -0
  112. package/dist/terminal-ui-C5k88MmW.mjs.map +1 -0
  113. package/dist/validate-contract-deps-esa-VQ0h.mjs +37 -0
  114. package/dist/validate-contract-deps-esa-VQ0h.mjs.map +1 -0
  115. package/dist/verify-DlFQ2FOw.mjs +385 -0
  116. package/dist/verify-DlFQ2FOw.mjs.map +1 -0
  117. package/package.json +87 -40
  118. package/src/cli.ts +118 -58
  119. package/src/commands/contract-emit.ts +101 -78
  120. package/src/commands/contract-infer-paths.ts +32 -0
  121. package/src/commands/contract-infer.ts +143 -0
  122. package/src/commands/db-init.ts +97 -219
  123. package/src/commands/db-schema.ts +77 -0
  124. package/src/commands/db-sign.ts +46 -73
  125. package/src/commands/db-update.ts +236 -0
  126. package/src/commands/db-verify.ts +409 -119
  127. package/src/commands/init/detect-package-manager.ts +47 -0
  128. package/src/commands/init/index.ts +21 -0
  129. package/src/commands/init/init.ts +203 -0
  130. package/src/commands/init/templates/agent-skill-mongo.md +106 -0
  131. package/src/commands/init/templates/agent-skill-postgres.md +106 -0
  132. package/src/commands/init/templates/agent-skill.ts +19 -0
  133. package/src/commands/init/templates/code-templates.ts +168 -0
  134. package/src/commands/init/templates/quick-reference-mongo.md +93 -0
  135. package/src/commands/init/templates/quick-reference-postgres.md +91 -0
  136. package/src/commands/init/templates/quick-reference.ts +19 -0
  137. package/src/commands/init/templates/render.ts +20 -0
  138. package/src/commands/init/templates/tsconfig.ts +35 -0
  139. package/src/commands/inspect-live-schema.ts +170 -0
  140. package/src/commands/migration-apply.ts +427 -0
  141. package/src/commands/migration-new.ts +260 -0
  142. package/src/commands/migration-plan.ts +519 -0
  143. package/src/commands/migration-ref.ts +305 -0
  144. package/src/commands/migration-show.ts +246 -0
  145. package/src/commands/migration-status.ts +864 -0
  146. package/src/commands/migration-verify.ts +180 -0
  147. package/src/config-loader.ts +13 -3
  148. package/src/control-api/client.ts +205 -183
  149. package/src/control-api/contract-enrichment.ts +119 -0
  150. package/src/control-api/errors.ts +9 -0
  151. package/src/control-api/operations/contract-emit.ts +181 -0
  152. package/src/control-api/operations/db-init.ts +53 -49
  153. package/src/control-api/operations/db-update.ts +220 -0
  154. package/src/control-api/operations/extract-operation-statements.ts +14 -0
  155. package/src/control-api/operations/extract-sql-ddl.ts +47 -0
  156. package/src/control-api/operations/migration-apply.ts +191 -0
  157. package/src/control-api/operations/migration-helpers.ts +49 -0
  158. package/src/control-api/types.ts +274 -52
  159. package/src/exports/config-types.ts +4 -3
  160. package/src/exports/control-api.ts +15 -5
  161. package/src/load-ts-contract.ts +30 -19
  162. package/src/utils/cli-errors.ts +14 -8
  163. package/src/utils/command-helpers.ts +302 -3
  164. package/src/utils/formatters/emit.ts +67 -0
  165. package/src/utils/formatters/errors.ts +82 -0
  166. package/src/utils/formatters/graph-migration-mapper.ts +240 -0
  167. package/src/utils/formatters/graph-render.ts +1323 -0
  168. package/src/utils/formatters/graph-types.ts +120 -0
  169. package/src/utils/formatters/help.ts +380 -0
  170. package/src/utils/formatters/helpers.ts +28 -0
  171. package/src/utils/formatters/migrations.ts +346 -0
  172. package/src/utils/formatters/styled.ts +212 -0
  173. package/src/utils/formatters/verify.ts +621 -0
  174. package/src/utils/framework-components.ts +13 -10
  175. package/src/utils/global-flags.ts +41 -23
  176. package/src/utils/migration-command-scaffold.ts +184 -0
  177. package/src/utils/migration-types.ts +12 -0
  178. package/src/utils/progress-adapter.ts +18 -29
  179. package/src/utils/result-handler.ts +12 -13
  180. package/src/utils/shutdown.ts +92 -0
  181. package/src/utils/suggest-command.ts +31 -0
  182. package/src/utils/terminal-ui.ts +276 -0
  183. package/src/utils/validate-contract-deps.ts +49 -0
  184. package/dist/chunk-AGOTG4L3.js +0 -965
  185. package/dist/chunk-AGOTG4L3.js.map +0 -1
  186. package/dist/chunk-HLLI4YL7.js +0 -180
  187. package/dist/chunk-HLLI4YL7.js.map +0 -1
  188. package/dist/chunk-HWYQOCAJ.js +0 -47
  189. package/dist/chunk-HWYQOCAJ.js.map +0 -1
  190. package/dist/chunk-VG2R7DGF.js +0 -735
  191. package/dist/chunk-VG2R7DGF.js.map +0 -1
  192. package/dist/cli.d.ts +0 -2
  193. package/dist/cli.d.ts.map +0 -1
  194. package/dist/cli.js.map +0 -1
  195. package/dist/commands/contract-emit.d.ts +0 -3
  196. package/dist/commands/contract-emit.d.ts.map +0 -1
  197. package/dist/commands/contract-emit.js +0 -10
  198. package/dist/commands/contract-emit.js.map +0 -1
  199. package/dist/commands/db-init.d.ts +0 -3
  200. package/dist/commands/db-init.d.ts.map +0 -1
  201. package/dist/commands/db-init.js +0 -257
  202. package/dist/commands/db-init.js.map +0 -1
  203. package/dist/commands/db-introspect.d.ts +0 -3
  204. package/dist/commands/db-introspect.d.ts.map +0 -1
  205. package/dist/commands/db-introspect.js +0 -155
  206. package/dist/commands/db-introspect.js.map +0 -1
  207. package/dist/commands/db-schema-verify.d.ts +0 -3
  208. package/dist/commands/db-schema-verify.d.ts.map +0 -1
  209. package/dist/commands/db-schema-verify.js +0 -171
  210. package/dist/commands/db-schema-verify.js.map +0 -1
  211. package/dist/commands/db-sign.d.ts +0 -3
  212. package/dist/commands/db-sign.d.ts.map +0 -1
  213. package/dist/commands/db-sign.js +0 -195
  214. package/dist/commands/db-sign.js.map +0 -1
  215. package/dist/commands/db-verify.d.ts +0 -3
  216. package/dist/commands/db-verify.d.ts.map +0 -1
  217. package/dist/commands/db-verify.js +0 -193
  218. package/dist/commands/db-verify.js.map +0 -1
  219. package/dist/config-loader.d.ts.map +0 -1
  220. package/dist/config-loader.js +0 -7
  221. package/dist/config-loader.js.map +0 -1
  222. package/dist/control-api/client.d.ts +0 -13
  223. package/dist/control-api/client.d.ts.map +0 -1
  224. package/dist/control-api/operations/db-init.d.ts +0 -29
  225. package/dist/control-api/operations/db-init.d.ts.map +0 -1
  226. package/dist/control-api/types.d.ts +0 -387
  227. package/dist/control-api/types.d.ts.map +0 -1
  228. package/dist/exports/config-types.d.ts +0 -3
  229. package/dist/exports/config-types.d.ts.map +0 -1
  230. package/dist/exports/config-types.js +0 -6
  231. package/dist/exports/config-types.js.map +0 -1
  232. package/dist/exports/control-api.d.ts +0 -13
  233. package/dist/exports/control-api.d.ts.map +0 -1
  234. package/dist/exports/control-api.js +0 -7
  235. package/dist/exports/control-api.js.map +0 -1
  236. package/dist/exports/index.d.ts +0 -4
  237. package/dist/exports/index.d.ts.map +0 -1
  238. package/dist/exports/index.js +0 -176
  239. package/dist/exports/index.js.map +0 -1
  240. package/dist/load-ts-contract.d.ts.map +0 -1
  241. package/dist/utils/cli-errors.d.ts +0 -7
  242. package/dist/utils/cli-errors.d.ts.map +0 -1
  243. package/dist/utils/command-helpers.d.ts +0 -12
  244. package/dist/utils/command-helpers.d.ts.map +0 -1
  245. package/dist/utils/framework-components.d.ts +0 -70
  246. package/dist/utils/framework-components.d.ts.map +0 -1
  247. package/dist/utils/global-flags.d.ts +0 -25
  248. package/dist/utils/global-flags.d.ts.map +0 -1
  249. package/dist/utils/output.d.ts +0 -142
  250. package/dist/utils/output.d.ts.map +0 -1
  251. package/dist/utils/progress-adapter.d.ts +0 -26
  252. package/dist/utils/progress-adapter.d.ts.map +0 -1
  253. package/dist/utils/result-handler.d.ts +0 -15
  254. package/dist/utils/result-handler.d.ts.map +0 -1
  255. package/src/commands/db-introspect.ts +0 -227
  256. package/src/commands/db-schema-verify.ts +0 -238
  257. 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
+ }