@prisma-next/cli 0.3.0-pr.99.6 → 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.
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 +261 -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 +9 -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 +10 -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 +126 -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 +56 -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 +137 -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 +123 -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 +323 -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 +245 -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 +9 -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-Zm_sd1wQ.mjs +112 -0
  74. package/dist/contract-emit-Zm_sd1wQ.mjs.map +1 -0
  75. package/dist/contract-emit-kN-IkKTE.mjs +6 -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 +8 -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 +142 -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-6Pvm_esG.mjs +430 -0
  96. package/dist/init-6Pvm_esG.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
@@ -1,48 +1,45 @@
1
1
  import { readFile } from 'node:fs/promises';
2
- import { relative, resolve } from 'node:path';
3
2
  import type {
4
3
  SignDatabaseResult,
5
4
  VerifyDatabaseSchemaResult,
6
- } from '@prisma-next/core-control-plane/types';
5
+ } from '@prisma-next/framework-components/control';
7
6
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
8
7
  import { Command } from 'commander';
8
+ import { relative, resolve } from 'pathe';
9
9
  import { loadConfig } from '../config-loader';
10
10
  import { createControlClient } from '../control-api/client';
11
+ import { ContractValidationError } from '../control-api/errors';
11
12
  import {
12
13
  CliStructuredError,
13
14
  errorContractValidationFailed,
14
15
  errorDatabaseConnectionRequired,
15
16
  errorDriverRequired,
16
17
  errorFileNotFound,
17
- errorJsonFormatNotSupported,
18
18
  errorUnexpected,
19
19
  } from '../utils/cli-errors';
20
- import { setCommandDescriptions } from '../utils/command-helpers';
21
- import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
22
20
  import {
23
- formatCommandHelp,
21
+ addGlobalOptions,
22
+ maskConnectionUrl,
23
+ resolveContractPath,
24
+ setCommandDescriptions,
25
+ setCommandExamples,
26
+ } from '../utils/command-helpers';
27
+ import { formatStyledHeader } from '../utils/formatters/styled';
28
+ import {
24
29
  formatSchemaVerifyJson,
25
30
  formatSchemaVerifyOutput,
26
31
  formatSignJson,
27
32
  formatSignOutput,
28
- formatStyledHeader,
29
- } from '../utils/output';
33
+ } from '../utils/formatters/verify';
34
+ import type { CommonCommandOptions } from '../utils/global-flags';
35
+ import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
30
36
  import { createProgressAdapter } from '../utils/progress-adapter';
31
37
  import { handleResult } from '../utils/result-handler';
38
+ import { TerminalUI } from '../utils/terminal-ui';
32
39
 
33
- interface DbSignOptions {
40
+ interface DbSignOptions extends CommonCommandOptions {
34
41
  readonly db?: string;
35
42
  readonly config?: string;
36
- readonly json?: string | boolean;
37
- readonly quiet?: boolean;
38
- readonly q?: boolean;
39
- readonly verbose?: boolean;
40
- readonly v?: boolean;
41
- readonly vv?: boolean;
42
- readonly trace?: boolean;
43
- readonly timestamps?: boolean;
44
- readonly color?: boolean;
45
- readonly 'no-color'?: boolean;
46
43
  }
47
44
 
48
45
  /**
@@ -59,25 +56,24 @@ type DbSignFailure = CliStructuredError | VerifyDatabaseSchemaResult;
59
56
  async function executeDbSignCommand(
60
57
  options: DbSignOptions,
61
58
  flags: GlobalFlags,
59
+ ui: TerminalUI,
62
60
  ): Promise<Result<SignDatabaseResult, DbSignFailure>> {
63
61
  // Load config
64
62
  const config = await loadConfig(options.config);
65
63
  const configPath = options.config
66
64
  ? relative(process.cwd(), resolve(options.config))
67
65
  : 'prisma-next.config.ts';
68
- const contractPathAbsolute = config.contract?.output
69
- ? resolve(config.contract.output)
70
- : resolve('src/prisma/contract.json');
66
+ const contractPathAbsolute = resolveContractPath(config);
71
67
  const contractPath = relative(process.cwd(), contractPathAbsolute);
72
68
 
73
69
  // Output header
74
- if (flags.json !== 'object' && !flags.quiet) {
70
+ if (!flags.json && !flags.quiet) {
75
71
  const details: Array<{ label: string; value: string }> = [
76
72
  { label: 'config', value: configPath },
77
73
  { label: 'contract', value: contractPath },
78
74
  ];
79
75
  if (options.db) {
80
- details.push({ label: 'database', value: options.db });
76
+ details.push({ label: 'database', value: maskConnectionUrl(options.db) });
81
77
  }
82
78
  const header = formatStyledHeader({
83
79
  command: 'db sign',
@@ -86,7 +82,7 @@ async function executeDbSignCommand(
86
82
  details,
87
83
  flags,
88
84
  });
89
- console.log(header);
85
+ ui.stderr(header);
90
86
  }
91
87
 
92
88
  // Load contract file
@@ -127,6 +123,7 @@ async function executeDbSignCommand(
127
123
  return notOk(
128
124
  errorDatabaseConnectionRequired({
129
125
  why: `Database connection is required for db sign (set db.connection in ${configPath}, or pass --db <url>)`,
126
+ commandName: 'db sign',
130
127
  }),
131
128
  );
132
129
  }
@@ -146,12 +143,12 @@ async function executeDbSignCommand(
146
143
  });
147
144
 
148
145
  // Create progress adapter
149
- const onProgress = createProgressAdapter({ flags });
146
+ const onProgress = createProgressAdapter({ ui, flags });
150
147
 
151
148
  try {
152
149
  // Step 1: Schema verification - connect here
153
150
  const schemaVerifyResult = await client.schemaVerify({
154
- contractIR: contractJson,
151
+ contract: contractJson,
155
152
  strict: false,
156
153
  connection: dbConnection,
157
154
  onProgress,
@@ -159,26 +156,17 @@ async function executeDbSignCommand(
159
156
 
160
157
  // If schema verification failed, return as failure
161
158
  if (!schemaVerifyResult.ok) {
162
- // Add blank line after all async operations if spinners were shown
163
- if (!flags.quiet && flags.json !== 'object' && process.stdout.isTTY) {
164
- console.log('');
165
- }
166
159
  return notOk(schemaVerifyResult);
167
160
  }
168
161
 
169
162
  // Step 2: Sign (already connected from schemaVerify)
170
163
  const signResult = await client.sign({
171
- contractIR: contractJson,
164
+ contract: contractJson,
172
165
  contractPath,
173
166
  configPath,
174
167
  onProgress,
175
168
  });
176
169
 
177
- // Add blank line after all async operations if spinners were shown
178
- if (!flags.quiet && flags.json !== 'object' && process.stdout.isTTY) {
179
- console.log('');
180
- }
181
-
182
170
  return ok(signResult);
183
171
  } catch (error) {
184
172
  // Driver already throws CliStructuredError for connection failures
@@ -186,6 +174,14 @@ async function executeDbSignCommand(
186
174
  return notOk(error);
187
175
  }
188
176
 
177
+ if (error instanceof ContractValidationError) {
178
+ return notOk(
179
+ errorContractValidationFailed(`Contract validation failed: ${error.message}`, {
180
+ where: { path: contractPathAbsolute },
181
+ }),
182
+ );
183
+ }
184
+
189
185
  // Wrap unexpected errors
190
186
  return notOk(
191
187
  errorUnexpected(error instanceof Error ? error.message : String(error), {
@@ -203,52 +199,29 @@ export function createDbSignCommand(): Command {
203
199
  command,
204
200
  'Sign the database with your contract so you can safely run queries',
205
201
  'Verifies that your database schema satisfies the emitted contract, and if so, writes or\n' +
206
- 'updates the contract marker in the database. This command is idempotent and safe to run\n' +
207
- 'in CI/deployment pipelines. The marker records that this database instance is aligned\n' +
202
+ 'updates the database signature. This command is idempotent and safe to run\n' +
203
+ 'in CI/deployment pipelines. The signature records that this database instance is aligned\n' +
208
204
  'with a specific contract version.',
209
205
  );
210
- command
211
- .configureHelp({
212
- formatHelp: (cmd) => {
213
- const flags = parseGlobalFlags({});
214
- return formatCommandHelp({ command: cmd, flags });
215
- },
216
- })
206
+ setCommandExamples(command, ['prisma-next db sign --db $DATABASE_URL']);
207
+ addGlobalOptions(command)
217
208
  .option('--db <url>', 'Database connection string')
218
209
  .option('--config <path>', 'Path to prisma-next.config.ts')
219
- .option('--json [format]', 'Output as JSON (object)', false)
220
- .option('-q, --quiet', 'Quiet mode: errors only')
221
- .option('-v, --verbose', 'Verbose output: debug info, timings')
222
- .option('-vv, --trace', 'Trace output: deep internals, stack traces')
223
- .option('--timestamps', 'Add timestamps to output')
224
- .option('--color', 'Force color output')
225
- .option('--no-color', 'Disable color output')
226
210
  .action(async (options: DbSignOptions) => {
227
211
  const flags = parseGlobalFlags(options);
228
212
 
229
- // Validate JSON format option
230
- if (flags.json === 'ndjson') {
231
- const result = notOk(
232
- errorJsonFormatNotSupported({
233
- command: 'db sign',
234
- format: 'ndjson',
235
- supportedFormats: ['object'],
236
- }),
237
- );
238
- const exitCode = handleResult(result, flags);
239
- process.exit(exitCode);
240
- }
213
+ const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
241
214
 
242
- const result = await executeDbSignCommand(options, flags);
215
+ const result = await executeDbSignCommand(options, flags, ui);
243
216
 
244
217
  if (result.ok) {
245
218
  // Success - format sign output
246
- if (flags.json === 'object') {
247
- console.log(formatSignJson(result.value));
219
+ if (flags.json) {
220
+ ui.output(formatSignJson(result.value));
248
221
  } else {
249
222
  const output = formatSignOutput(result.value, flags);
250
223
  if (output) {
251
- console.log(output);
224
+ ui.log(output);
252
225
  }
253
226
  }
254
227
  process.exit(0);
@@ -259,17 +232,17 @@ export function createDbSignCommand(): Command {
259
232
 
260
233
  if (failure instanceof CliStructuredError) {
261
234
  // Infrastructure error - use standard handler
262
- const exitCode = handleResult(result as Result<never, CliStructuredError>, flags);
235
+ const exitCode = handleResult(result as Result<never, CliStructuredError>, flags, ui);
263
236
  process.exit(exitCode);
264
237
  }
265
238
 
266
239
  // Schema verification failed - format and print schema verification output
267
- if (flags.json === 'object') {
268
- console.log(formatSchemaVerifyJson(failure));
240
+ if (flags.json) {
241
+ ui.output(formatSchemaVerifyJson(failure));
269
242
  } else {
270
243
  const output = formatSchemaVerifyOutput(failure, flags);
271
244
  if (output) {
272
- console.log(output);
245
+ ui.log(output);
273
246
  }
274
247
  }
275
248
  process.exit(1);
@@ -0,0 +1,236 @@
1
+ import { ifDefined } from '@prisma-next/utils/defined';
2
+ import { notOk, ok, type Result } from '@prisma-next/utils/result';
3
+ import { Command } from 'commander';
4
+ import { ContractValidationError } from '../control-api/errors';
5
+ import type { DbUpdateFailure } from '../control-api/types';
6
+ import {
7
+ CliStructuredError,
8
+ ERROR_CODE_DESTRUCTIVE_CHANGES,
9
+ errorContractValidationFailed,
10
+ errorDestructiveChanges,
11
+ errorMigrationPlanningFailed,
12
+ errorRunnerFailed,
13
+ errorUnexpected,
14
+ } from '../utils/cli-errors';
15
+ import type { MigrationCommandOptions } from '../utils/command-helpers';
16
+ import {
17
+ sanitizeErrorMessage,
18
+ setCommandDescriptions,
19
+ setCommandExamples,
20
+ } from '../utils/command-helpers';
21
+ import {
22
+ formatMigrationApplyOutput,
23
+ formatMigrationJson,
24
+ formatMigrationPlanOutput,
25
+ type MigrationCommandResult,
26
+ } from '../utils/formatters/migrations';
27
+ import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
28
+ import {
29
+ addMigrationCommandOptions,
30
+ prepareMigrationContext,
31
+ } from '../utils/migration-command-scaffold';
32
+ import { handleResult } from '../utils/result-handler';
33
+ import { TerminalUI } from '../utils/terminal-ui';
34
+
35
+ type DbUpdateOptions = MigrationCommandOptions;
36
+
37
+ /**
38
+ * Maps a DbUpdateFailure to a CliStructuredError for consistent error handling.
39
+ */
40
+ function mapDbUpdateFailure(failure: DbUpdateFailure): CliStructuredError {
41
+ if (failure.code === 'PLANNING_FAILED') {
42
+ return errorMigrationPlanningFailed({ conflicts: failure.conflicts ?? [] });
43
+ }
44
+
45
+ if (failure.code === 'RUNNER_FAILED') {
46
+ return errorRunnerFailed(failure.summary, {
47
+ why: failure.why ?? 'Migration runner failed',
48
+ fix: 'Inspect the reported conflict, reconcile schema drift if needed, then re-run `prisma-next db update`',
49
+ ...ifDefined('meta', failure.meta),
50
+ });
51
+ }
52
+
53
+ if (failure.code === 'DESTRUCTIVE_CHANGES') {
54
+ return errorDestructiveChanges(failure.summary, {
55
+ ...ifDefined('why', failure.why),
56
+ fix: 'Re-run with `-y` to apply destructive changes, or use `--dry-run` to preview first',
57
+ ...ifDefined('meta', failure.meta),
58
+ });
59
+ }
60
+
61
+ const exhaustive: never = failure.code;
62
+ throw new Error(`Unhandled DbUpdateFailure code: ${exhaustive}`);
63
+ }
64
+
65
+ /**
66
+ * Executes the db update command and returns a structured Result.
67
+ */
68
+ async function executeDbUpdateCommand(
69
+ options: DbUpdateOptions,
70
+ flags: GlobalFlags,
71
+ ui: TerminalUI,
72
+ startTime: number,
73
+ ): Promise<Result<MigrationCommandResult, CliStructuredError>> {
74
+ // Prepare shared migration context (config, contract, connection, client)
75
+ const ctxResult = await prepareMigrationContext(options, flags, ui, {
76
+ commandName: 'db update',
77
+ description: 'Update your database schema to match your contract',
78
+ url: 'https://pris.ly/db-update',
79
+ });
80
+ if (!ctxResult.ok) {
81
+ return ctxResult;
82
+ }
83
+ const { client, contractJson, dbConnection, onProgress, contractPathAbsolute } = ctxResult.value;
84
+
85
+ try {
86
+ // Call dbUpdate with connection and progress callback
87
+ const result = await client.dbUpdate({
88
+ contract: contractJson,
89
+ mode: options.dryRun ? 'plan' : 'apply',
90
+ connection: dbConnection,
91
+ ...(flags.yes ? { acceptDataLoss: true } : {}),
92
+ onProgress,
93
+ });
94
+
95
+ // Handle failures by mapping to CLI structured error
96
+ if (!result.ok) {
97
+ return notOk(mapDbUpdateFailure(result.failure));
98
+ }
99
+
100
+ // Convert success result to CLI output format
101
+ const dbUpdateResult: MigrationCommandResult = {
102
+ ok: true,
103
+ mode: result.value.mode,
104
+ plan: {
105
+ targetId: ctxResult.value.config.target.targetId,
106
+ destination: {
107
+ storageHash: result.value.destination.storageHash,
108
+ ...ifDefined('profileHash', result.value.destination.profileHash),
109
+ },
110
+ operations: result.value.plan.operations.map((op) => ({
111
+ id: op.id,
112
+ label: op.label,
113
+ operationClass: op.operationClass,
114
+ })),
115
+ ...ifDefined('sql', result.value.plan.sql),
116
+ },
117
+ ...ifDefined(
118
+ 'execution',
119
+ result.value.execution
120
+ ? {
121
+ operationsPlanned: result.value.execution.operationsPlanned,
122
+ operationsExecuted: result.value.execution.operationsExecuted,
123
+ }
124
+ : undefined,
125
+ ),
126
+ ...ifDefined(
127
+ 'marker',
128
+ result.value.marker
129
+ ? {
130
+ storageHash: result.value.marker.storageHash,
131
+ ...ifDefined('profileHash', result.value.marker.profileHash),
132
+ }
133
+ : undefined,
134
+ ),
135
+ summary: result.value.summary,
136
+ timings: { total: Date.now() - startTime },
137
+ };
138
+
139
+ return ok(dbUpdateResult);
140
+ } catch (error) {
141
+ if (CliStructuredError.is(error)) {
142
+ return notOk(error);
143
+ }
144
+
145
+ if (error instanceof ContractValidationError) {
146
+ return notOk(
147
+ errorContractValidationFailed(`Contract validation failed: ${error.message}`, {
148
+ where: { path: contractPathAbsolute },
149
+ }),
150
+ );
151
+ }
152
+
153
+ const rawMessage = error instanceof Error ? error.message : String(error);
154
+ const safeMessage = sanitizeErrorMessage(
155
+ rawMessage,
156
+ typeof dbConnection === 'string' ? dbConnection : undefined,
157
+ );
158
+ return notOk(
159
+ errorUnexpected(safeMessage, {
160
+ why: `Unexpected error during db update: ${safeMessage}`,
161
+ }),
162
+ );
163
+ } finally {
164
+ await client.close();
165
+ }
166
+ }
167
+
168
+ export function createDbUpdateCommand(): Command {
169
+ const command = new Command('update');
170
+ setCommandDescriptions(
171
+ command,
172
+ 'Update your database schema to match your contract',
173
+ 'Compares your database schema to the emitted contract and applies the necessary\n' +
174
+ 'changes. Works on any database, whether or not it has been initialized with `db init`.\n' +
175
+ 'Destructive operations prompt for confirmation in interactive mode. Use -y to\n' +
176
+ 'auto-accept or --dry-run to preview first.',
177
+ );
178
+ setCommandExamples(command, [
179
+ 'prisma-next db update --db $DATABASE_URL',
180
+ 'prisma-next db update --db $DATABASE_URL --dry-run',
181
+ ]);
182
+ addMigrationCommandOptions(command);
183
+ command.action(async (options: DbUpdateOptions) => {
184
+ const flags = parseGlobalFlags(options);
185
+ const startTime = Date.now();
186
+
187
+ const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
188
+
189
+ let result = await executeDbUpdateCommand(options, flags, ui, startTime);
190
+
191
+ // Interactive confirmation for destructive operations:
192
+ // When the control API rejects destructive changes, prompt the user instead of failing.
193
+ // In non-interactive mode (CI, piped, --no-interactive, --json), the error is returned as-is.
194
+ if (
195
+ !result.ok &&
196
+ result.failure.code === ERROR_CODE_DESTRUCTIVE_CHANGES &&
197
+ flags.interactive &&
198
+ !flags.json &&
199
+ !flags.yes
200
+ ) {
201
+ const meta = result.failure.meta as
202
+ | { destructiveOperations?: readonly { id: string; label: string }[] }
203
+ | undefined;
204
+ const destructiveOps = meta?.destructiveOperations ?? [];
205
+
206
+ if (destructiveOps.length > 0) {
207
+ ui.warn(
208
+ `${destructiveOps.length} destructive operation(s) that may cause data loss:\n${destructiveOps.map((op) => ` ${ui.yellow('▸')} ${op.label}`).join('\n')}`,
209
+ );
210
+ }
211
+
212
+ const confirmed = await ui.confirm('Apply destructive changes? This cannot be undone.');
213
+
214
+ if (confirmed) {
215
+ result = await executeDbUpdateCommand(options, { ...flags, yes: true }, ui, Date.now());
216
+ }
217
+ }
218
+
219
+ const exitCode = handleResult(result, flags, ui, (dbUpdateResult) => {
220
+ if (flags.json) {
221
+ ui.output(formatMigrationJson(dbUpdateResult));
222
+ } else {
223
+ const output =
224
+ dbUpdateResult.mode === 'plan'
225
+ ? formatMigrationPlanOutput(dbUpdateResult, flags)
226
+ : formatMigrationApplyOutput(dbUpdateResult, flags);
227
+ if (output) {
228
+ ui.log(output);
229
+ }
230
+ }
231
+ });
232
+ process.exit(exitCode);
233
+ });
234
+
235
+ return command;
236
+ }