@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
@@ -0,0 +1,143 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { errorRuntime } from '@prisma-next/errors/execution';
3
+ import { printPsl, validatePrintableSqlSchemaIR } from '@prisma-next/psl-printer';
4
+ import {
5
+ createPostgresDefaultMapping,
6
+ createPostgresTypeMap,
7
+ extractEnumInfo,
8
+ parseRawDefault,
9
+ } from '@prisma-next/psl-printer/postgres';
10
+ import { notOk, ok, type Result } from '@prisma-next/utils/result';
11
+ import { Command } from 'commander';
12
+ import { dirname, relative } from 'pathe';
13
+ import type { CliStructuredError } from '../utils/cli-errors';
14
+ import {
15
+ addGlobalOptions,
16
+ setCommandDescriptions,
17
+ setCommandExamples,
18
+ } from '../utils/command-helpers';
19
+ import { parseGlobalFlags } from '../utils/global-flags';
20
+ import { handleResult } from '../utils/result-handler';
21
+ import { TerminalUI } from '../utils/terminal-ui';
22
+ import { resolveContractInferOutputPath } from './contract-infer-paths';
23
+ import {
24
+ type InspectLiveSchemaOptions,
25
+ type InspectLiveSchemaResult,
26
+ inspectLiveSchema,
27
+ } from './inspect-live-schema';
28
+
29
+ interface ContractInferOptions extends InspectLiveSchemaOptions {
30
+ readonly output?: string;
31
+ }
32
+
33
+ interface ContractInferSuccessResult {
34
+ readonly ok: true;
35
+ readonly summary: string;
36
+ readonly target: InspectLiveSchemaResult['target'];
37
+ readonly psl: {
38
+ readonly path: string;
39
+ };
40
+ readonly meta: InspectLiveSchemaResult['meta'];
41
+ readonly timings: {
42
+ readonly total: number;
43
+ };
44
+ }
45
+
46
+ async function executeContractInferCommand(
47
+ options: ContractInferOptions,
48
+ ui: TerminalUI,
49
+ startTime: number,
50
+ ): Promise<Result<ContractInferSuccessResult, CliStructuredError>> {
51
+ const flags = parseGlobalFlags(options);
52
+ const inspectResult = await inspectLiveSchema(options, flags, ui, startTime, {
53
+ commandName: 'contract infer',
54
+ description: 'Infer a PSL contract from the live database schema',
55
+ url: 'https://pris.ly/contract-infer',
56
+ });
57
+
58
+ if (!inspectResult.ok) {
59
+ return inspectResult;
60
+ }
61
+
62
+ const { config, target, meta } = inspectResult.value;
63
+
64
+ if (target.familyId !== 'sql') {
65
+ return notOk(
66
+ errorRuntime(`contract infer is not supported for family "${target.familyId}"`, {
67
+ why: 'contract infer currently supports SQL targets only',
68
+ fix: 'Use an SQL target (e.g. Postgres) with this command',
69
+ }),
70
+ );
71
+ }
72
+
73
+ const schema = validatePrintableSqlSchemaIR(inspectResult.value.schema);
74
+ const outputPath = resolveContractInferOutputPath(options, config.contract?.output);
75
+ const enumInfo = extractEnumInfo(schema.annotations);
76
+ const pslContent = printPsl(schema, {
77
+ defaultMapping: createPostgresDefaultMapping(),
78
+ typeMap: createPostgresTypeMap(enumInfo.typeNames),
79
+ enumInfo,
80
+ parseRawDefault,
81
+ });
82
+
83
+ if (existsSync(outputPath) && !flags.json && !flags.quiet) {
84
+ ui.stderr(`\u26A0 Overwriting existing file: ${relative(process.cwd(), outputPath)}`);
85
+ }
86
+
87
+ mkdirSync(dirname(outputPath), { recursive: true });
88
+ writeFileSync(outputPath, pslContent, 'utf-8');
89
+
90
+ const pslPath = relative(process.cwd(), outputPath);
91
+ if (!flags.json && !flags.quiet) {
92
+ ui.stderr(`\u2714 Contract written to ${pslPath}`);
93
+ }
94
+
95
+ return ok({
96
+ ok: true,
97
+ summary: 'Contract inferred successfully',
98
+ target,
99
+ psl: {
100
+ path: pslPath,
101
+ },
102
+ meta,
103
+ timings: {
104
+ total: Date.now() - startTime,
105
+ },
106
+ });
107
+ }
108
+
109
+ export function createContractInferCommand(): Command {
110
+ const command = new Command('infer');
111
+ setCommandDescriptions(
112
+ command,
113
+ 'Infer a PSL contract from the live database schema',
114
+ 'Reads the live database schema and writes an inferred PSL contract to disk.\n' +
115
+ 'This command stops at `contract.prisma`; follow it with `contract emit` and\n' +
116
+ '`db sign` as separate steps.',
117
+ );
118
+ setCommandExamples(command, [
119
+ 'prisma-next contract infer --db $DATABASE_URL',
120
+ 'prisma-next contract infer --db $DATABASE_URL --output ./prisma/contract.prisma',
121
+ 'prisma-next contract infer --db $DATABASE_URL --json',
122
+ ]);
123
+ addGlobalOptions(command)
124
+ .option('--db <url>', 'Database connection string')
125
+ .option('--config <path>', 'Path to prisma-next.config.ts')
126
+ .option('--output <path>', 'Write the inferred PSL contract to the specified path')
127
+ .action(async (options: ContractInferOptions) => {
128
+ const flags = parseGlobalFlags(options);
129
+ const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
130
+ const startTime = Date.now();
131
+
132
+ const result = await executeContractInferCommand(options, ui, startTime);
133
+ const exitCode = handleResult(result, flags, ui, (value) => {
134
+ if (flags.json) {
135
+ ui.output(JSON.stringify(value, null, 2));
136
+ }
137
+ });
138
+
139
+ process.exit(exitCode);
140
+ });
141
+
142
+ return command;
143
+ }
@@ -1,50 +1,37 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { relative, resolve } from 'node:path';
1
+ import { ifDefined } from '@prisma-next/utils/defined';
3
2
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
4
3
  import { Command } from 'commander';
5
- import { loadConfig } from '../config-loader';
6
- import { createControlClient } from '../control-api/client';
4
+ import { ContractValidationError } from '../control-api/errors';
7
5
  import type { DbInitFailure } from '../control-api/types';
8
6
  import {
9
7
  CliStructuredError,
10
8
  errorContractValidationFailed,
11
- errorDatabaseConnectionRequired,
12
- errorDriverRequired,
13
- errorFileNotFound,
14
- errorJsonFormatNotSupported,
15
9
  errorMigrationPlanningFailed,
10
+ errorRunnerFailed,
16
11
  errorRuntime,
17
- errorTargetMigrationNotSupported,
18
12
  errorUnexpected,
19
13
  } from '../utils/cli-errors';
20
- import { setCommandDescriptions } from '../utils/command-helpers';
14
+ import type { MigrationCommandOptions } from '../utils/command-helpers';
15
+ import {
16
+ sanitizeErrorMessage,
17
+ setCommandDescriptions,
18
+ setCommandExamples,
19
+ } from '../utils/command-helpers';
20
+ import {
21
+ formatMigrationApplyOutput,
22
+ formatMigrationJson,
23
+ formatMigrationPlanOutput,
24
+ type MigrationCommandResult,
25
+ } from '../utils/formatters/migrations';
21
26
  import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
22
27
  import {
23
- type DbInitResult,
24
- formatCommandHelp,
25
- formatDbInitApplyOutput,
26
- formatDbInitJson,
27
- formatDbInitPlanOutput,
28
- formatStyledHeader,
29
- } from '../utils/output';
30
- import { createProgressAdapter } from '../utils/progress-adapter';
28
+ addMigrationCommandOptions,
29
+ prepareMigrationContext,
30
+ } from '../utils/migration-command-scaffold';
31
31
  import { handleResult } from '../utils/result-handler';
32
+ import { TerminalUI } from '../utils/terminal-ui';
32
33
 
33
- interface DbInitOptions {
34
- readonly db?: string;
35
- readonly config?: string;
36
- readonly plan?: boolean;
37
- readonly json?: string | boolean;
38
- readonly quiet?: boolean;
39
- readonly q?: boolean;
40
- readonly verbose?: boolean;
41
- readonly v?: boolean;
42
- readonly vv?: boolean;
43
- readonly trace?: boolean;
44
- readonly timestamps?: boolean;
45
- readonly color?: boolean;
46
- readonly 'no-color'?: boolean;
47
- }
34
+ type DbInitOptions = MigrationCommandOptions;
48
35
 
49
36
  /**
50
37
  * Maps a DbInitFailure to a CliStructuredError for consistent error handling.
@@ -57,12 +44,12 @@ function mapDbInitFailure(failure: DbInitFailure): CliStructuredError {
57
44
  if (failure.code === 'MARKER_ORIGIN_MISMATCH') {
58
45
  const mismatchParts: string[] = [];
59
46
  if (
60
- failure.marker?.coreHash !== failure.destination?.coreHash &&
61
- failure.marker?.coreHash &&
62
- failure.destination?.coreHash
47
+ failure.marker?.storageHash !== failure.destination?.storageHash &&
48
+ failure.marker?.storageHash &&
49
+ failure.destination?.storageHash
63
50
  ) {
64
51
  mismatchParts.push(
65
- `coreHash (marker: ${failure.marker.coreHash}, destination: ${failure.destination.coreHash})`,
52
+ `storageHash (marker: ${failure.marker.storageHash}, destination: ${failure.destination.storageHash})`,
66
53
  );
67
54
  }
68
55
  if (
@@ -76,33 +63,28 @@ function mapDbInitFailure(failure: DbInitFailure): CliStructuredError {
76
63
  }
77
64
 
78
65
  return errorRuntime(
79
- `Existing contract marker does not match plan destination.${mismatchParts.length > 0 ? ` Mismatch in ${mismatchParts.join(' and ')}.` : ''}`,
66
+ `Existing database signature does not match plan destination.${mismatchParts.length > 0 ? ` Mismatch in ${mismatchParts.join(' and ')}.` : ''}`,
80
67
  {
81
- why: 'Database has an existing contract marker that does not match the target contract',
68
+ why: 'Database has an existing signature (marker) that does not match the target contract',
82
69
  fix: 'If bootstrapping, drop/reset the database then re-run `prisma-next db init`; otherwise reconcile schema/marker using your migration workflow',
83
70
  meta: {
84
71
  code: 'MARKER_ORIGIN_MISMATCH',
85
- ...(failure.marker?.coreHash ? { markerCoreHash: failure.marker.coreHash } : {}),
86
- ...(failure.destination?.coreHash
87
- ? { destinationCoreHash: failure.destination.coreHash }
88
- : {}),
89
- ...(failure.marker?.profileHash ? { markerProfileHash: failure.marker.profileHash } : {}),
90
- ...(failure.destination?.profileHash
91
- ? { destinationProfileHash: failure.destination.profileHash }
92
- : {}),
72
+ ...ifDefined('markerStorageHash', failure.marker?.storageHash),
73
+ ...ifDefined('destinationStorageHash', failure.destination?.storageHash),
74
+ ...ifDefined('markerProfileHash', failure.marker?.profileHash),
75
+ ...ifDefined('destinationProfileHash', failure.destination?.profileHash),
93
76
  },
94
77
  },
95
78
  );
96
79
  }
97
80
 
98
81
  if (failure.code === 'RUNNER_FAILED') {
99
- return errorRuntime(failure.summary, {
82
+ return errorRunnerFailed(failure.summary, {
100
83
  why: failure.why ?? 'Migration runner failed',
101
84
  fix: 'Fix the schema mismatch (db init is additive-only), or drop/reset the database and re-run `prisma-next db init`',
102
- meta: {
103
- code: 'RUNNER_FAILED',
104
- ...(failure.meta ?? {}),
105
- },
85
+ ...(failure.meta
86
+ ? { meta: { code: 'RUNNER_FAILED', ...failure.meta } }
87
+ : { meta: { code: 'RUNNER_FAILED' } }),
106
88
  });
107
89
  }
108
90
 
@@ -117,114 +99,25 @@ function mapDbInitFailure(failure: DbInitFailure): CliStructuredError {
117
99
  async function executeDbInitCommand(
118
100
  options: DbInitOptions,
119
101
  flags: GlobalFlags,
102
+ ui: TerminalUI,
120
103
  startTime: number,
121
- ): Promise<Result<DbInitResult, CliStructuredError>> {
122
- // Load config
123
- const config = await loadConfig(options.config);
124
- const configPath = options.config
125
- ? relative(process.cwd(), resolve(options.config))
126
- : 'prisma-next.config.ts';
127
- const contractPathAbsolute = config.contract?.output
128
- ? resolve(config.contract.output)
129
- : resolve('src/prisma/contract.json');
130
- const contractPath = relative(process.cwd(), contractPathAbsolute);
131
-
132
- // Output header
133
- if (flags.json !== 'object' && !flags.quiet) {
134
- const details: Array<{ label: string; value: string }> = [
135
- { label: 'config', value: configPath },
136
- { label: 'contract', value: contractPath },
137
- ];
138
- if (options.db) {
139
- details.push({ label: 'database', value: options.db });
140
- }
141
- if (options.plan) {
142
- details.push({ label: 'mode', value: 'plan (dry run)' });
143
- }
144
- const header = formatStyledHeader({
145
- command: 'db init',
146
- description: 'Bootstrap a database to match the current contract',
147
- url: 'https://pris.ly/db-init',
148
- details,
149
- flags,
150
- });
151
- console.log(header);
152
- }
153
-
154
- // Load contract file
155
- let contractJsonContent: string;
156
- try {
157
- contractJsonContent = await readFile(contractPathAbsolute, 'utf-8');
158
- } catch (error) {
159
- if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {
160
- return notOk(
161
- errorFileNotFound(contractPathAbsolute, {
162
- why: `Contract file not found at ${contractPathAbsolute}`,
163
- fix: `Run \`prisma-next contract emit\` to generate ${contractPath}, or update \`config.contract.output\` in ${configPath}`,
164
- }),
165
- );
166
- }
167
- return notOk(
168
- errorUnexpected(error instanceof Error ? error.message : String(error), {
169
- why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`,
170
- }),
171
- );
172
- }
173
-
174
- let contractJson: Record<string, unknown>;
175
- try {
176
- contractJson = JSON.parse(contractJsonContent) as Record<string, unknown>;
177
- } catch (error) {
178
- return notOk(
179
- errorContractValidationFailed(
180
- `Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`,
181
- { where: { path: contractPathAbsolute } },
182
- ),
183
- );
184
- }
185
-
186
- // Resolve database connection (--db flag or config.db.connection)
187
- const dbConnection = options.db ?? config.db?.connection;
188
- if (!dbConnection) {
189
- return notOk(
190
- errorDatabaseConnectionRequired({
191
- why: `Database connection is required for db init (set db.connection in ${configPath}, or pass --db <url>)`,
192
- }),
193
- );
194
- }
195
-
196
- // Check for driver
197
- if (!config.driver) {
198
- return notOk(errorDriverRequired({ why: 'Config.driver is required for db init' }));
199
- }
200
-
201
- // Check target supports migrations via the migrations capability
202
- if (!config.target.migrations) {
203
- return notOk(
204
- errorTargetMigrationNotSupported({
205
- why: `Target "${config.target.id}" does not support migrations`,
206
- }),
207
- );
208
- }
209
-
210
- // Create control client
211
- const client = createControlClient({
212
- family: config.family,
213
- target: config.target,
214
- adapter: config.adapter,
215
- driver: config.driver,
216
- extensionPacks: config.extensionPacks ?? [],
104
+ ): Promise<Result<MigrationCommandResult, CliStructuredError>> {
105
+ // Prepare shared migration context (config, contract, connection, client)
106
+ const ctxResult = await prepareMigrationContext(options, flags, ui, {
107
+ commandName: 'db init',
108
+ description: 'Bootstrap a database to match the current contract',
109
+ url: 'https://pris.ly/db-init',
217
110
  });
218
-
219
- // Create progress adapter
220
- const onProgress = createProgressAdapter({ flags });
111
+ if (!ctxResult.ok) {
112
+ return ctxResult;
113
+ }
114
+ const { client, contractJson, dbConnection, onProgress, contractPathAbsolute } = ctxResult.value;
221
115
 
222
116
  try {
223
117
  // Call dbInit with connection and progress callback
224
- // Connection happens inside dbInit with a 'connect' progress span
225
118
  const result = await client.dbInit({
226
- contractIR: contractJson,
227
- mode: options.plan ? 'plan' : 'apply',
119
+ contract: contractJson,
120
+ mode: options.dryRun ? 'plan' : 'apply',
228
121
  connection: dbConnection,
229
122
  onProgress,
230
123
  });
@@ -235,15 +128,14 @@ async function executeDbInitCommand(
235
128
  }
236
129
 
237
130
  // Convert success result to CLI output format
238
- const profileHash = result.value.marker?.profileHash;
239
- const dbInitResult: DbInitResult = {
131
+ const dbInitResult: MigrationCommandResult = {
240
132
  ok: true,
241
133
  mode: result.value.mode,
242
134
  plan: {
243
- targetId: config.target.targetId,
135
+ targetId: ctxResult.value.config.target.targetId,
244
136
  destination: {
245
- coreHash: result.value.marker?.coreHash ?? '',
246
- ...(profileHash ? { profileHash } : {}),
137
+ storageHash: result.value.destination.storageHash,
138
+ ...ifDefined('profileHash', result.value.destination.profileHash),
247
139
  },
248
140
  operations: result.value.plan.operations.map((op) => ({
249
141
  id: op.id,
@@ -262,10 +154,8 @@ async function executeDbInitCommand(
262
154
  ...(result.value.marker
263
155
  ? {
264
156
  marker: {
265
- coreHash: result.value.marker.coreHash,
266
- ...(result.value.marker.profileHash
267
- ? { profileHash: result.value.marker.profileHash }
268
- : {}),
157
+ storageHash: result.value.marker.storageHash,
158
+ ...ifDefined('profileHash', result.value.marker.profileHash),
269
159
  },
270
160
  }
271
161
  : {}),
@@ -276,15 +166,26 @@ async function executeDbInitCommand(
276
166
  return ok(dbInitResult);
277
167
  } catch (error) {
278
168
  // Driver already throws CliStructuredError for connection failures
279
- // Use static type guard to work across module boundaries
280
169
  if (CliStructuredError.is(error)) {
281
170
  return notOk(error);
282
171
  }
283
172
 
284
- // Wrap unexpected errors
173
+ if (error instanceof ContractValidationError) {
174
+ return notOk(
175
+ errorContractValidationFailed(`Contract validation failed: ${error.message}`, {
176
+ where: { path: contractPathAbsolute },
177
+ }),
178
+ );
179
+ }
180
+
181
+ const rawMessage = error instanceof Error ? error.message : String(error);
182
+ const safeMessage = sanitizeErrorMessage(
183
+ rawMessage,
184
+ typeof dbConnection === 'string' ? dbConnection : undefined,
185
+ );
285
186
  return notOk(
286
- errorUnexpected(error instanceof Error ? error.message : String(error), {
287
- why: `Unexpected error during db init: ${error instanceof Error ? error.message : String(error)}`,
187
+ errorUnexpected(safeMessage, {
188
+ why: `Unexpected error during db init: ${safeMessage}`,
288
189
  }),
289
190
  );
290
191
  } finally {
@@ -296,65 +197,42 @@ export function createDbInitCommand(): Command {
296
197
  const command = new Command('init');
297
198
  setCommandDescriptions(
298
199
  command,
299
- 'Bootstrap a database to match the current contract and write the contract marker',
200
+ 'Bootstrap a database to match the current contract and sign it',
300
201
  'Initializes a database to match your emitted contract using additive-only operations.\n' +
301
202
  'Creates any missing tables, columns, indexes, and constraints defined in your contract.\n' +
302
203
  'Leaves existing compatible structures in place, surfaces conflicts when destructive changes\n' +
303
- 'would be required, and writes a contract marker to track the database state. Use --plan to\n' +
204
+ 'would be required, and signs the database to track contract state. Use --dry-run to\n' +
304
205
  'preview changes without applying.',
305
206
  );
306
- command
307
- .configureHelp({
308
- formatHelp: (cmd) => {
309
- const flags = parseGlobalFlags({});
310
- return formatCommandHelp({ command: cmd, flags });
311
- },
312
- })
313
- .option('--db <url>', 'Database connection string')
314
- .option('--config <path>', 'Path to prisma-next.config.ts')
315
- .option('--plan', 'Preview planned operations without applying', false)
316
- .option('--json [format]', 'Output as JSON (object)', false)
317
- .option('-q, --quiet', 'Quiet mode: errors only')
318
- .option('-v, --verbose', 'Verbose output: debug info, timings')
319
- .option('-vv, --trace', 'Trace output: deep internals, stack traces')
320
- .option('--timestamps', 'Add timestamps to output')
321
- .option('--color', 'Force color output')
322
- .option('--no-color', 'Disable color output')
323
- .action(async (options: DbInitOptions) => {
324
- const flags = parseGlobalFlags(options);
325
- const startTime = Date.now();
326
-
327
- // Validate JSON format option
328
- if (flags.json === 'ndjson') {
329
- const result = notOk(
330
- errorJsonFormatNotSupported({
331
- command: 'db init',
332
- format: 'ndjson',
333
- supportedFormats: ['object'],
334
- }),
335
- );
336
- const exitCode = handleResult(result, flags);
337
- process.exit(exitCode);
338
- }
339
-
340
- const result = await executeDbInitCommand(options, flags, startTime);
341
-
342
- const exitCode = handleResult(result, flags, (dbInitResult) => {
343
- if (flags.json === 'object') {
344
- console.log(formatDbInitJson(dbInitResult));
345
- } else {
346
- const output =
347
- dbInitResult.mode === 'plan'
348
- ? formatDbInitPlanOutput(dbInitResult, flags)
349
- : formatDbInitApplyOutput(dbInitResult, flags);
350
- if (output) {
351
- console.log(output);
352
- }
207
+ setCommandExamples(command, [
208
+ 'prisma-next db init --db $DATABASE_URL',
209
+ 'prisma-next db init --db $DATABASE_URL --dry-run',
210
+ ]);
211
+ addMigrationCommandOptions(command);
212
+ command.action(async (options: DbInitOptions) => {
213
+ const flags = parseGlobalFlags(options);
214
+ const startTime = Date.now();
215
+
216
+ const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
217
+
218
+ const result = await executeDbInitCommand(options, flags, ui, startTime);
219
+
220
+ const exitCode = handleResult(result, flags, ui, (dbInitResult) => {
221
+ if (flags.json) {
222
+ ui.output(formatMigrationJson(dbInitResult));
223
+ } else {
224
+ const output =
225
+ dbInitResult.mode === 'plan'
226
+ ? formatMigrationPlanOutput(dbInitResult, flags)
227
+ : formatMigrationApplyOutput(dbInitResult, flags);
228
+ if (output) {
229
+ ui.log(output);
353
230
  }
354
- });
355
-
356
- process.exit(exitCode);
231
+ }
357
232
  });
358
233
 
234
+ process.exit(exitCode);
235
+ });
236
+
359
237
  return command;
360
238
  }
@@ -0,0 +1,77 @@
1
+ import type { IntrospectSchemaResult } from '@prisma-next/framework-components/control';
2
+ import { Command } from 'commander';
3
+ import {
4
+ addGlobalOptions,
5
+ setCommandDescriptions,
6
+ setCommandExamples,
7
+ } from '../utils/command-helpers';
8
+ import { formatIntrospectJson, formatIntrospectOutput } from '../utils/formatters/verify';
9
+ import { parseGlobalFlags } from '../utils/global-flags';
10
+ import { handleResult } from '../utils/result-handler';
11
+ import { TerminalUI } from '../utils/terminal-ui';
12
+ import {
13
+ type InspectLiveSchemaOptions,
14
+ type InspectLiveSchemaResult,
15
+ inspectLiveSchema,
16
+ } from './inspect-live-schema';
17
+
18
+ function toIntrospectSchemaResult(
19
+ result: InspectLiveSchemaResult,
20
+ ): IntrospectSchemaResult<unknown> {
21
+ return {
22
+ ok: true,
23
+ summary: 'Schema read successfully',
24
+ target: result.target,
25
+ schema: result.schema,
26
+ meta: result.meta,
27
+ timings: result.timings,
28
+ };
29
+ }
30
+
31
+ export function createDbSchemaCommand(): Command {
32
+ const command = new Command('schema');
33
+ setCommandDescriptions(
34
+ command,
35
+ 'Inspect the live database schema',
36
+ 'Reads the live database schema and prints it as a tree by default or as JSON with\n' +
37
+ '--json. This command is always read-only and never writes files. To save machine-\n' +
38
+ 'readable output, use shell redirection, for example `prisma-next db schema --json > schema.json`.',
39
+ );
40
+ setCommandExamples(command, [
41
+ 'prisma-next db schema --db $DATABASE_URL',
42
+ 'prisma-next db schema --db $DATABASE_URL --json',
43
+ 'prisma-next db schema --db $DATABASE_URL --json > schema.json',
44
+ ]);
45
+ addGlobalOptions(command)
46
+ .option('--db <url>', 'Database connection string')
47
+ .option('--config <path>', 'Path to prisma-next.config.ts')
48
+ .action(async (options: InspectLiveSchemaOptions) => {
49
+ const flags = parseGlobalFlags(options);
50
+ const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
51
+ const startTime = Date.now();
52
+
53
+ const result = await inspectLiveSchema(options, flags, ui, startTime, {
54
+ commandName: 'db schema',
55
+ description: 'Inspect the live database schema',
56
+ url: 'https://pris.ly/db-schema',
57
+ });
58
+
59
+ const exitCode = handleResult(result, flags, ui, (value) => {
60
+ const introspectResult = toIntrospectSchemaResult(value);
61
+
62
+ if (flags.json) {
63
+ ui.output(formatIntrospectJson(introspectResult));
64
+ return;
65
+ }
66
+
67
+ const output = formatIntrospectOutput(introspectResult, value.schemaView, flags);
68
+ if (output) {
69
+ ui.log(output);
70
+ }
71
+ });
72
+
73
+ process.exit(exitCode);
74
+ });
75
+
76
+ return command;
77
+ }