@prisma-next/cli 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -6
- package/dist/agent-skill-mongo.md +63 -31
- package/dist/agent-skill-postgres.md +1 -1
- package/dist/cli-errors-BFYgBH3L.d.mts +4 -0
- package/dist/{cli-errors-CznZA5-d.mjs → cli-errors-Cd79vmTH.mjs} +2 -2
- package/dist/cli.mjs +126 -13
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-DGKrciLM.mjs → client-CrsnY58k.mjs} +4 -4
- package/dist/{client-DGKrciLM.mjs.map → client-CrsnY58k.mjs.map} +1 -1
- package/dist/commands/contract-emit.mjs +7 -2
- package/dist/commands/contract-infer.mjs +8 -2
- package/dist/commands/db-init.mjs +8 -7
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.mjs +8 -5
- package/dist/commands/db-schema.mjs.map +1 -1
- package/dist/commands/db-sign.mjs +8 -7
- package/dist/commands/db-sign.mjs.map +1 -1
- package/dist/commands/db-update.mjs +8 -7
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.mjs +8 -7
- package/dist/commands/db-verify.mjs.map +1 -1
- package/dist/commands/migration-apply.mjs +9 -8
- package/dist/commands/migration-apply.mjs.map +1 -1
- package/dist/commands/migration-new.mjs +5 -5
- package/dist/commands/migration-plan.mjs +6 -6
- package/dist/commands/migration-ref.d.mts +6 -4
- package/dist/commands/migration-ref.d.mts.map +1 -1
- package/dist/commands/migration-ref.mjs +29 -34
- package/dist/commands/migration-ref.mjs.map +1 -1
- package/dist/commands/migration-show.d.mts +1 -1
- package/dist/commands/migration-show.mjs +6 -6
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +7 -2
- package/dist/{config-loader-_xQZsw0i.mjs → config-loader-C25b63rJ.mjs} +1 -1
- package/dist/{config-loader-_xQZsw0i.mjs.map → config-loader-C25b63rJ.mjs.map} +1 -1
- package/dist/config-loader.mjs +1 -1
- package/dist/contract-emit-DxgyXrqV.mjs +6 -0
- package/dist/{contract-emit-mU1_B_m9.mjs → contract-emit-NJ01hiiv.mjs} +10 -10
- package/dist/contract-emit-NJ01hiiv.mjs.map +1 -0
- package/dist/{contract-emit-DgeWdonT.mjs → contract-emit-V5SSitUT.mjs} +6 -6
- package/dist/{contract-emit-DgeWdonT.mjs.map → contract-emit-V5SSitUT.mjs.map} +1 -1
- package/dist/{contract-enrichment-BV4KpbNW.mjs → contract-enrichment-CAOELa-H.mjs} +1 -1
- package/dist/{contract-enrichment-BV4KpbNW.mjs.map → contract-enrichment-CAOELa-H.mjs.map} +1 -1
- package/dist/{contract-infer-CUbiWGX0.mjs → contract-infer-D9cC3rJm.mjs} +4 -4
- package/dist/{contract-infer-CUbiWGX0.mjs.map → contract-infer-D9cC3rJm.mjs.map} +1 -1
- package/dist/exports/control-api.mjs +6 -4
- package/dist/exports/index.mjs +7 -2
- package/dist/exports/index.mjs.map +1 -1
- package/dist/exports/init-output.d.mts +39 -0
- package/dist/exports/init-output.d.mts.map +1 -0
- package/dist/exports/init-output.mjs +3 -0
- package/dist/{extract-operation-statements-DWWFz1PK.mjs → extract-operation-statements-DsFfxXVZ.mjs} +2 -2
- package/dist/{extract-operation-statements-DWWFz1PK.mjs.map → extract-operation-statements-DsFfxXVZ.mjs.map} +1 -1
- package/dist/{extract-sql-ddl-7zn_AFS8.mjs → extract-sql-ddl-D9UbZDyz.mjs} +1 -1
- package/dist/{extract-sql-ddl-7zn_AFS8.mjs.map → extract-sql-ddl-D9UbZDyz.mjs.map} +1 -1
- package/dist/{framework-components-B__p--vT.mjs → framework-components-Cr--XBKy.mjs} +2 -2
- package/dist/{framework-components-B__p--vT.mjs.map → framework-components-Cr--XBKy.mjs.map} +1 -1
- package/dist/init-m8x0UoPY.mjs +2062 -0
- package/dist/init-m8x0UoPY.mjs.map +1 -0
- package/dist/{inspect-live-schema-wIYBTdL3.mjs → inspect-live-schema-yrHAvG71.mjs} +6 -6
- package/dist/{inspect-live-schema-wIYBTdL3.mjs.map → inspect-live-schema-yrHAvG71.mjs.map} +1 -1
- package/dist/migration-cli.d.mts +50 -0
- package/dist/migration-cli.d.mts.map +1 -0
- package/dist/migration-cli.mjs +184 -0
- package/dist/migration-cli.mjs.map +1 -0
- package/dist/{migration-command-scaffold-BC73xQSo.mjs → migration-command-scaffold-B3B09et6.mjs} +6 -6
- package/dist/{migration-command-scaffold-BC73xQSo.mjs.map → migration-command-scaffold-B3B09et6.mjs.map} +1 -1
- package/dist/{migration-status-CXBbScH5.mjs → migration-status-DUMiH8_G.mjs} +12 -14
- package/dist/{migration-status-CXBbScH5.mjs.map → migration-status-DUMiH8_G.mjs.map} +1 -1
- package/dist/{migrations-DYRAjiVh.mjs → migrations-Bo5WtTla.mjs} +2 -2
- package/dist/{migrations-DYRAjiVh.mjs.map → migrations-Bo5WtTla.mjs.map} +1 -1
- package/dist/output-BpcQrnnq.mjs +103 -0
- package/dist/output-BpcQrnnq.mjs.map +1 -0
- package/dist/{progress-adapter-Bwouy73-.mjs → progress-adapter-DvQWB1nK.mjs} +1 -1
- package/dist/{progress-adapter-Bwouy73-.mjs.map → progress-adapter-DvQWB1nK.mjs.map} +1 -1
- package/dist/quick-reference-mongo.md +34 -13
- package/dist/quick-reference-postgres.md +11 -9
- package/dist/{result-handler-CGohaH1o.mjs → result-handler-Ba3zWQsI.mjs} +5 -78
- package/dist/result-handler-Ba3zWQsI.mjs.map +1 -0
- package/dist/{terminal-ui-BuPXVRFY.mjs → terminal-ui-C3ZLwQxK.mjs} +76 -2
- package/dist/terminal-ui-C3ZLwQxK.mjs.map +1 -0
- package/dist/{validate-contract-deps-DZqv9m7H.mjs → validate-contract-deps-B_Cs29TL.mjs} +1 -1
- package/dist/{validate-contract-deps-DZqv9m7H.mjs.map → validate-contract-deps-B_Cs29TL.mjs.map} +1 -1
- package/dist/{verify-Cm2UFuZA.mjs → verify-Bkycc-Tf.mjs} +2 -2
- package/dist/{verify-Cm2UFuZA.mjs.map → verify-Bkycc-Tf.mjs.map} +1 -1
- package/package.json +25 -16
- package/src/commands/init/detect-pnpm-catalog.ts +141 -0
- package/src/commands/init/errors.ts +254 -0
- package/src/commands/init/exit-codes.ts +62 -0
- package/src/commands/init/hygiene-gitattributes.ts +97 -0
- package/src/commands/init/hygiene-gitignore.ts +48 -0
- package/src/commands/init/hygiene-package-scripts.ts +91 -0
- package/src/commands/init/index.ts +112 -7
- package/src/commands/init/init.ts +766 -144
- package/src/commands/init/inputs.ts +421 -0
- package/src/commands/init/output.ts +147 -0
- package/src/commands/init/probe-db.ts +308 -0
- package/src/commands/init/reinit-cleanup.ts +83 -0
- package/src/commands/init/templates/agent-skill-mongo.md +63 -31
- package/src/commands/init/templates/agent-skill-postgres.md +1 -1
- package/src/commands/init/templates/agent-skill.ts +25 -3
- package/src/commands/init/templates/code-templates.ts +125 -32
- package/src/commands/init/templates/env.ts +80 -0
- package/src/commands/init/templates/quick-reference-mongo.md +34 -13
- package/src/commands/init/templates/quick-reference-postgres.md +11 -9
- package/src/commands/init/templates/quick-reference.ts +42 -3
- package/src/commands/init/templates/tsconfig.ts +167 -5
- package/src/commands/migration-apply.ts +3 -3
- package/src/commands/migration-ref.ts +32 -47
- package/src/commands/migration-status.ts +16 -21
- package/src/exports/init-output.ts +10 -0
- package/src/migration-cli.ts +254 -0
- package/src/utils/cli-errors.ts +1 -0
- package/src/utils/command-helpers.ts +3 -3
- package/dist/cli-errors-z37sV3eR.d.mts +0 -4
- package/dist/contract-emit-304WZtZJ.mjs +0 -4
- package/dist/contract-emit-mU1_B_m9.mjs.map +0 -1
- package/dist/init-DRquYpPa.mjs +0 -430
- package/dist/init-DRquYpPa.mjs.map +0 -1
- package/dist/result-handler-CGohaH1o.mjs.map +0 -1
- package/dist/terminal-ui-BuPXVRFY.mjs.map +0 -1
|
@@ -1,17 +1,22 @@
|
|
|
1
|
+
import type { RefEntry } from '@prisma-next/migration-tools/refs';
|
|
1
2
|
import {
|
|
3
|
+
deleteRef,
|
|
4
|
+
readRef,
|
|
2
5
|
readRefs,
|
|
3
|
-
resolveRef,
|
|
4
6
|
validateRefName,
|
|
5
7
|
validateRefValue,
|
|
6
|
-
|
|
8
|
+
writeRef,
|
|
7
9
|
} from '@prisma-next/migration-tools/refs';
|
|
8
10
|
import { MigrationToolsError } from '@prisma-next/migration-tools/types';
|
|
9
11
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
10
12
|
import { Command } from 'commander';
|
|
11
|
-
import { resolve } from 'pathe';
|
|
12
13
|
import { loadConfig } from '../config-loader';
|
|
13
14
|
import { CliStructuredError, errorRuntime, errorUnexpected } from '../utils/cli-errors';
|
|
14
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
addGlobalOptions,
|
|
17
|
+
resolveMigrationPaths,
|
|
18
|
+
setCommandDescriptions,
|
|
19
|
+
} from '../utils/command-helpers';
|
|
15
20
|
import { formatCommandHelp } from '../utils/formatters/help';
|
|
16
21
|
import { parseGlobalFlags } from '../utils/global-flags';
|
|
17
22
|
import { handleResult } from '../utils/result-handler';
|
|
@@ -21,12 +26,14 @@ interface RefSetResult {
|
|
|
21
26
|
readonly ok: true;
|
|
22
27
|
readonly ref: string;
|
|
23
28
|
readonly hash: string;
|
|
29
|
+
readonly invariants: readonly string[];
|
|
24
30
|
}
|
|
25
31
|
|
|
26
32
|
interface RefGetResult {
|
|
27
33
|
readonly ok: true;
|
|
28
34
|
readonly ref: string;
|
|
29
35
|
readonly hash: string;
|
|
36
|
+
readonly invariants: readonly string[];
|
|
30
37
|
}
|
|
31
38
|
|
|
32
39
|
interface RefDeleteResult {
|
|
@@ -37,12 +44,7 @@ interface RefDeleteResult {
|
|
|
37
44
|
|
|
38
45
|
interface RefListResult {
|
|
39
46
|
readonly ok: true;
|
|
40
|
-
readonly refs: Record<string,
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function resolveRefsPath(configPath?: string, config?: { migrations?: { dir?: string } }): string {
|
|
44
|
-
const base = configPath ? resolve(configPath, '..') : process.cwd();
|
|
45
|
-
return resolve(base, config?.migrations?.dir ?? 'migrations', 'refs.json');
|
|
47
|
+
readonly refs: Record<string, RefEntry>;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
function mapError(error: unknown): CliStructuredError {
|
|
@@ -70,13 +72,6 @@ function cliErrorInvalidRefValue(hash: string): CliStructuredError {
|
|
|
70
72
|
});
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
function errorRefNotFound(name: string): CliStructuredError {
|
|
74
|
-
return errorRuntime(`Ref "${name}" does not exist`, {
|
|
75
|
-
why: `No ref named "${name}" found in refs.json`,
|
|
76
|
-
fix: `Run \`prisma-next migration ref list\` to see available refs, or \`prisma-next migration ref set ${name} <hash>\` to create it`,
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
75
|
async function executeRefSetCommand(
|
|
81
76
|
name: string,
|
|
82
77
|
hash: string,
|
|
@@ -91,11 +86,10 @@ async function executeRefSetCommand(
|
|
|
91
86
|
|
|
92
87
|
try {
|
|
93
88
|
const config = await loadConfig(options.config);
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return ok({ ok: true as const, ref: name, hash });
|
|
89
|
+
const { refsDir } = resolveMigrationPaths(options.config, config);
|
|
90
|
+
const entry: RefEntry = { hash, invariants: [] };
|
|
91
|
+
await writeRef(refsDir, name, entry);
|
|
92
|
+
return ok({ ok: true as const, ref: name, hash, invariants: [] });
|
|
99
93
|
} catch (error) {
|
|
100
94
|
if (error instanceof CliStructuredError) return notOk(error);
|
|
101
95
|
return notOk(mapError(error));
|
|
@@ -108,10 +102,9 @@ async function executeRefGetCommand(
|
|
|
108
102
|
): Promise<Result<RefGetResult, CliStructuredError>> {
|
|
109
103
|
try {
|
|
110
104
|
const config = await loadConfig(options.config);
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
const hash
|
|
114
|
-
return ok({ ok: true as const, ref: name, hash });
|
|
105
|
+
const { refsDir } = resolveMigrationPaths(options.config, config);
|
|
106
|
+
const entry = await readRef(refsDir, name);
|
|
107
|
+
return ok({ ok: true as const, ref: name, hash: entry.hash, invariants: entry.invariants });
|
|
115
108
|
} catch (error) {
|
|
116
109
|
if (error instanceof CliStructuredError) return notOk(error);
|
|
117
110
|
return notOk(mapError(error));
|
|
@@ -124,13 +117,8 @@ async function executeRefDeleteCommand(
|
|
|
124
117
|
): Promise<Result<RefDeleteResult, CliStructuredError>> {
|
|
125
118
|
try {
|
|
126
119
|
const config = await loadConfig(options.config);
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
if (!Object.hasOwn(refs, name)) {
|
|
130
|
-
return notOk(errorRefNotFound(name));
|
|
131
|
-
}
|
|
132
|
-
const { [name]: _, ...remaining } = refs;
|
|
133
|
-
await writeRefs(refsPath, remaining);
|
|
120
|
+
const { refsDir } = resolveMigrationPaths(options.config, config);
|
|
121
|
+
await deleteRef(refsDir, name);
|
|
134
122
|
return ok({ ok: true as const, ref: name, deleted: true as const });
|
|
135
123
|
} catch (error) {
|
|
136
124
|
if (error instanceof CliStructuredError) return notOk(error);
|
|
@@ -143,8 +131,8 @@ async function executeRefListCommand(options: {
|
|
|
143
131
|
}): Promise<Result<RefListResult, CliStructuredError>> {
|
|
144
132
|
try {
|
|
145
133
|
const config = await loadConfig(options.config);
|
|
146
|
-
const
|
|
147
|
-
const refs = await readRefs(
|
|
134
|
+
const { refsDir } = resolveMigrationPaths(options.config, config);
|
|
135
|
+
const refs = await readRefs(refsDir);
|
|
148
136
|
return ok({ ok: true as const, refs });
|
|
149
137
|
} catch (error) {
|
|
150
138
|
if (error instanceof CliStructuredError) return notOk(error);
|
|
@@ -157,7 +145,7 @@ function createRefSetCommand(): Command {
|
|
|
157
145
|
setCommandDescriptions(
|
|
158
146
|
command,
|
|
159
147
|
'Set a ref to a contract hash',
|
|
160
|
-
'Sets a named ref to point to a contract hash in migrations/refs
|
|
148
|
+
'Sets a named ref to point to a contract hash in migrations/refs/.',
|
|
161
149
|
);
|
|
162
150
|
addGlobalOptions(command)
|
|
163
151
|
.argument('<name>', 'Ref name (e.g., staging, production)')
|
|
@@ -190,7 +178,7 @@ function createRefGetCommand(): Command {
|
|
|
190
178
|
setCommandDescriptions(
|
|
191
179
|
command,
|
|
192
180
|
'Get the hash for a ref',
|
|
193
|
-
'Reads a named ref from migrations/refs
|
|
181
|
+
'Reads a named ref from migrations/refs/ and prints its contract hash.',
|
|
194
182
|
);
|
|
195
183
|
addGlobalOptions(command)
|
|
196
184
|
.argument('<name>', 'Ref name to look up')
|
|
@@ -218,7 +206,7 @@ function createRefGetCommand(): Command {
|
|
|
218
206
|
|
|
219
207
|
function createRefDeleteCommand(): Command {
|
|
220
208
|
const command = new Command('delete');
|
|
221
|
-
setCommandDescriptions(command, 'Delete a ref', 'Removes a named ref from migrations/refs
|
|
209
|
+
setCommandDescriptions(command, 'Delete a ref', 'Removes a named ref from migrations/refs/.');
|
|
222
210
|
addGlobalOptions(command)
|
|
223
211
|
.argument('<name>', 'Ref name to delete')
|
|
224
212
|
.option('--config <path>', 'Path to prisma-next.config.ts')
|
|
@@ -245,11 +233,7 @@ function createRefDeleteCommand(): Command {
|
|
|
245
233
|
|
|
246
234
|
function createRefListCommand(): Command {
|
|
247
235
|
const command = new Command('list');
|
|
248
|
-
setCommandDescriptions(
|
|
249
|
-
command,
|
|
250
|
-
'List all refs',
|
|
251
|
-
'Lists all named refs from migrations/refs.json.',
|
|
252
|
-
);
|
|
236
|
+
setCommandDescriptions(command, 'List all refs', 'Lists all named refs from migrations/refs/.');
|
|
253
237
|
addGlobalOptions(command)
|
|
254
238
|
.option('--config <path>', 'Path to prisma-next.config.ts')
|
|
255
239
|
.action(async (options: { config?: string; json?: string | boolean; quiet?: boolean }) => {
|
|
@@ -264,8 +248,10 @@ function createRefListCommand(): Command {
|
|
|
264
248
|
if (entries.length === 0) {
|
|
265
249
|
ui.output('No refs defined');
|
|
266
250
|
} else {
|
|
267
|
-
for (const [refName,
|
|
268
|
-
|
|
251
|
+
for (const [refName, entry] of entries) {
|
|
252
|
+
const invariantsSuffix =
|
|
253
|
+
entry.invariants.length > 0 ? ` [invariants: ${entry.invariants.join(', ')}]` : '';
|
|
254
|
+
ui.output(`${refName} → ${entry.hash}${invariantsSuffix}`);
|
|
269
255
|
}
|
|
270
256
|
}
|
|
271
257
|
}
|
|
@@ -282,7 +268,6 @@ export {
|
|
|
282
268
|
executeRefListCommand,
|
|
283
269
|
cliErrorInvalidRefName,
|
|
284
270
|
cliErrorInvalidRefValue,
|
|
285
|
-
errorRefNotFound,
|
|
286
271
|
};
|
|
287
272
|
|
|
288
273
|
export function createMigrationRefCommand(): Command {
|
|
@@ -290,7 +275,7 @@ export function createMigrationRefCommand(): Command {
|
|
|
290
275
|
setCommandDescriptions(
|
|
291
276
|
command,
|
|
292
277
|
'Manage migration refs',
|
|
293
|
-
'Manage named refs in migrations/refs
|
|
278
|
+
'Manage named refs in migrations/refs/. Refs map logical environment\n' +
|
|
294
279
|
'names (e.g., staging, production) to contract hashes.',
|
|
295
280
|
);
|
|
296
281
|
addGlobalOptions(command).configureHelp({
|
|
@@ -345,7 +345,7 @@ async function executeMigrationStatusCommand(
|
|
|
345
345
|
ui: TerminalUI,
|
|
346
346
|
): Promise<Result<MigrationStatusResult, CliStructuredError>> {
|
|
347
347
|
const config = await loadConfig(options.config);
|
|
348
|
-
const { configPath, migrationsDir, migrationsRelative,
|
|
348
|
+
const { configPath, migrationsDir, migrationsRelative, refsDir } = resolveMigrationPaths(
|
|
349
349
|
options.config,
|
|
350
350
|
config,
|
|
351
351
|
);
|
|
@@ -357,7 +357,7 @@ async function executeMigrationStatusCommand(
|
|
|
357
357
|
let activeRefHash: string | undefined;
|
|
358
358
|
let allRefs: Refs = {};
|
|
359
359
|
try {
|
|
360
|
-
allRefs = await readRefs(
|
|
360
|
+
allRefs = await readRefs(refsDir);
|
|
361
361
|
} catch (error) {
|
|
362
362
|
if (MigrationToolsError.is(error)) {
|
|
363
363
|
return notOk(
|
|
@@ -373,30 +373,25 @@ async function executeMigrationStatusCommand(
|
|
|
373
373
|
|
|
374
374
|
if (options.ref) {
|
|
375
375
|
activeRefName = options.ref;
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
fix: error.fix,
|
|
388
|
-
meta: { code: error.code },
|
|
389
|
-
}),
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
|
-
throw error;
|
|
376
|
+
try {
|
|
377
|
+
activeRefHash = resolveRef(allRefs, activeRefName).hash;
|
|
378
|
+
} catch (error) {
|
|
379
|
+
if (MigrationToolsError.is(error)) {
|
|
380
|
+
return notOk(
|
|
381
|
+
errorRuntime(error.message, {
|
|
382
|
+
why: error.why,
|
|
383
|
+
fix: error.fix,
|
|
384
|
+
meta: { code: error.code },
|
|
385
|
+
}),
|
|
386
|
+
);
|
|
393
387
|
}
|
|
388
|
+
throw error;
|
|
394
389
|
}
|
|
395
390
|
}
|
|
396
391
|
|
|
397
|
-
const statusRefs: StatusRef[] = Object.entries(allRefs).map(([name,
|
|
392
|
+
const statusRefs: StatusRef[] = Object.entries(allRefs).map(([name, entry]) => ({
|
|
398
393
|
name,
|
|
399
|
-
hash,
|
|
394
|
+
hash: entry.hash,
|
|
400
395
|
active: name === activeRefName,
|
|
401
396
|
}));
|
|
402
397
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public re-export of the `init --json` success-document schema (FR1.5).
|
|
3
|
+
*
|
|
4
|
+
* Imported as `@prisma-next/cli/init-output`. The shared error envelope is
|
|
5
|
+
* exported separately from `@prisma-next/errors`; consumers should branch
|
|
6
|
+
* on the `ok` discriminator (success documents carry `ok: true`, error
|
|
7
|
+
* envelopes carry `ok: false`) per the
|
|
8
|
+
* [Style Guide § JSON Semantics](../../../../../../../docs/CLI%20Style%20Guide.md#json-semantics).
|
|
9
|
+
*/
|
|
10
|
+
export { type InitOutput, InitOutputSchema } from '../commands/init/output';
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The migration-file CLI interface: the actor invoked when the author runs
|
|
3
|
+
* `node migration.ts` directly.
|
|
4
|
+
*
|
|
5
|
+
* Naming: this is *not* a "migration runner" in the apply-time sense. The
|
|
6
|
+
* apply-time runner is the thing `prisma-next migration apply` uses to
|
|
7
|
+
* execute migration JSON ops against a database. `MigrationCLI` is the
|
|
8
|
+
* tiny CLI surface owned by an authored `migration.ts` file: parse the
|
|
9
|
+
* file's argv, load the project's `prisma-next.config.ts`, assemble a
|
|
10
|
+
* `ControlStack`, instantiate the migration class, and serialize.
|
|
11
|
+
*
|
|
12
|
+
* The user authors a migration class, then calls
|
|
13
|
+
* `MigrationCLI.run(import.meta.url, MigrationClass)` at module scope
|
|
14
|
+
* after the class definition. When the file is invoked as a node
|
|
15
|
+
* entrypoint (`node migration.ts`), the CLI:
|
|
16
|
+
*
|
|
17
|
+
* 1. Detects whether the file is the direct entrypoint (no-op when imported).
|
|
18
|
+
* 2. Parses CLI args (`--help`, `--dry-run`, `--config <path>`).
|
|
19
|
+
* 3. Loads the project's `prisma-next.config.ts` via the same `loadConfig`
|
|
20
|
+
* the CLI commands use, walking up from the migration file's directory.
|
|
21
|
+
* 4. Probe-instantiates the migration class without a stack so it can read
|
|
22
|
+
* `targetId` and verify it matches `config.target.targetId`
|
|
23
|
+
* (`PN-MIG-2006` on mismatch) before any stack-driven adapter
|
|
24
|
+
* construction runs.
|
|
25
|
+
* 5. Assembles a `ControlStack` from the loaded config descriptors and
|
|
26
|
+
* constructs the migration with that stack.
|
|
27
|
+
* 6. Reads any previously-scaffolded `migration.json`, then calls
|
|
28
|
+
* `buildMigrationArtifacts` from `@prisma-next/migration-tools` to
|
|
29
|
+
* produce in-memory `ops.json` + `migration.json` content. Persists
|
|
30
|
+
* the result to disk (or prints in dry-run mode).
|
|
31
|
+
*
|
|
32
|
+
* File I/O lives here, in `@prisma-next/cli`: this is the only place
|
|
33
|
+
* that legitimately combines config loading, stack assembly, and
|
|
34
|
+
* on-disk persistence. `@prisma-next/migration-tools` owns the pure
|
|
35
|
+
* conversion from a `Migration` instance to artifact strings; `Migration`
|
|
36
|
+
* stays a pure abstract class.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
40
|
+
import { fileURLToPath } from 'node:url';
|
|
41
|
+
import { CliStructuredError, errorMigrationCliInvalidConfigArg } from '@prisma-next/errors/control';
|
|
42
|
+
import { errorMigrationTargetMismatch } from '@prisma-next/errors/migration';
|
|
43
|
+
import { createControlStack } from '@prisma-next/framework-components/control';
|
|
44
|
+
import {
|
|
45
|
+
buildMigrationArtifacts,
|
|
46
|
+
isDirectEntrypoint,
|
|
47
|
+
type Migration,
|
|
48
|
+
printMigrationHelp,
|
|
49
|
+
} from '@prisma-next/migration-tools/migration';
|
|
50
|
+
import type { MigrationManifest } from '@prisma-next/migration-tools/types';
|
|
51
|
+
import { dirname, join } from 'pathe';
|
|
52
|
+
import { loadConfig } from './config-loader';
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Constructor shape accepted by `MigrationCLI.run`. `Migration` subclasses
|
|
56
|
+
* accept an optional `ControlStack` in their constructor (each subclass
|
|
57
|
+
* narrows the stack to its own family/target generics); the CLI always
|
|
58
|
+
* passes one assembled from the loaded config. We use a rest-args `any[]`
|
|
59
|
+
* constructor signature so that subclass constructors with narrower
|
|
60
|
+
* parameter types remain assignable - constructor type compatibility in
|
|
61
|
+
* TS is contravariant in the parameter, and a wider `unknown` parameter
|
|
62
|
+
* on the alias side would reject any narrower subclass signature.
|
|
63
|
+
*
|
|
64
|
+
* The CLI only ever passes one argument (`new MigrationClass(stack)`);
|
|
65
|
+
* the rest-arity is purely a type-compatibility concession for subclass
|
|
66
|
+
* constructors that declare narrower parameter types, not an extension
|
|
67
|
+
* point for additional construction arguments.
|
|
68
|
+
*/
|
|
69
|
+
// biome-ignore lint/suspicious/noExplicitAny: see JSDoc - rest args with any are the idiomatic TS pattern for accepting arbitrary subclass constructor signatures
|
|
70
|
+
export type MigrationConstructor = new (...args: any[]) => Migration;
|
|
71
|
+
|
|
72
|
+
interface ParsedArgs {
|
|
73
|
+
readonly help: boolean;
|
|
74
|
+
readonly dryRun: boolean;
|
|
75
|
+
readonly configPath: string | undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Parse the subset of `process.argv` that `MigrationCLI.run` cares about.
|
|
80
|
+
* Recognised flags: `--help`, `--dry-run`, `--config <path>` /
|
|
81
|
+
* `--config=<path>`. Unknown flags are ignored to keep the surface
|
|
82
|
+
* forgiving for ad-hoc tooling that wraps a migration file.
|
|
83
|
+
*
|
|
84
|
+
* Throws `errorMigrationCliInvalidConfigArg` (`PN-CLI-4012`) when
|
|
85
|
+
* `--config` is missing its path argument or is followed by another flag
|
|
86
|
+
* (e.g. `--config --dry-run`); silently consuming the next flag would
|
|
87
|
+
* either drop dry-run handling or serialize against the wrong project.
|
|
88
|
+
*
|
|
89
|
+
* NOTE: this hand-rolled parser is a known wart, tracked separately by
|
|
90
|
+
* TML-2318 ("Migration CLI: replace handrolled arg parser with shared
|
|
91
|
+
* CLI library"). Until that lands the surface is intentionally tiny.
|
|
92
|
+
*/
|
|
93
|
+
function parseArgs(argv: readonly string[]): ParsedArgs {
|
|
94
|
+
let help = false;
|
|
95
|
+
let dryRun = false;
|
|
96
|
+
let configPath: string | undefined;
|
|
97
|
+
|
|
98
|
+
for (let i = 0; i < argv.length; i++) {
|
|
99
|
+
const arg = argv[i]!;
|
|
100
|
+
if (arg === '--help' || arg === '-h') {
|
|
101
|
+
help = true;
|
|
102
|
+
} else if (arg === '--dry-run') {
|
|
103
|
+
dryRun = true;
|
|
104
|
+
} else if (arg === '--config') {
|
|
105
|
+
const next = argv[i + 1];
|
|
106
|
+
if (next === undefined) {
|
|
107
|
+
throw errorMigrationCliInvalidConfigArg();
|
|
108
|
+
}
|
|
109
|
+
if (next.startsWith('-')) {
|
|
110
|
+
throw errorMigrationCliInvalidConfigArg({ nextToken: next });
|
|
111
|
+
}
|
|
112
|
+
configPath = next;
|
|
113
|
+
i++;
|
|
114
|
+
} else if (arg.startsWith('--config=')) {
|
|
115
|
+
configPath = arg.slice('--config='.length);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { help, dryRun, configPath };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The CLI surface invoked by an authored `migration.ts` file. Exposed as
|
|
124
|
+
* a class with a static `run` method (rather than a free function) to
|
|
125
|
+
* give the concept a stable identity in the ubiquitous language: this is
|
|
126
|
+
* the "migration-file CLI", distinct from the apply-time runner that
|
|
127
|
+
* executes migration JSON ops.
|
|
128
|
+
*
|
|
129
|
+
* Currently a single static method. Future surface (e.g. a programmatic
|
|
130
|
+
* `MigrationCLI.serializeOnly(...)` for tests, or extra subcommands) can
|
|
131
|
+
* land here without changing the import shape used by every authored
|
|
132
|
+
* migration.
|
|
133
|
+
*/
|
|
134
|
+
// biome-ignore lint/complexity/noStaticOnlyClass: see JSDoc - intentional class facade for the migration-file CLI surface; future methods will share state derived from argv/config.
|
|
135
|
+
export class MigrationCLI {
|
|
136
|
+
/**
|
|
137
|
+
* Orchestrates a class-flow `migration.ts` script run. Awaitable:
|
|
138
|
+
* callers may `await MigrationCLI.run(...)` to surface async failures
|
|
139
|
+
* from config loading, but the typical usage pattern (top-level call
|
|
140
|
+
* after the class definition) does not require awaiting because
|
|
141
|
+
* node's module evaluation keeps the promise alive until completion.
|
|
142
|
+
*
|
|
143
|
+
* Any throwable inside this function must surface through the internal
|
|
144
|
+
* try/catch — script callers do not await, so an unhandled rejection
|
|
145
|
+
* would silently exit 0. Treat the try/catch as load-bearing for the
|
|
146
|
+
* no-await usage pattern.
|
|
147
|
+
*/
|
|
148
|
+
static async run(importMetaUrl: string, MigrationClass: MigrationConstructor): Promise<void> {
|
|
149
|
+
if (!importMetaUrl) return;
|
|
150
|
+
if (!isDirectEntrypoint(importMetaUrl)) return;
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const args = parseArgs(process.argv.slice(2));
|
|
154
|
+
|
|
155
|
+
if (args.help) {
|
|
156
|
+
printMigrationHelp();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const migrationFile = fileURLToPath(importMetaUrl);
|
|
161
|
+
const migrationDir = dirname(migrationFile);
|
|
162
|
+
|
|
163
|
+
const config = await loadConfig(args.configPath);
|
|
164
|
+
|
|
165
|
+
// Probe-instantiate without a stack so we can read `targetId` before
|
|
166
|
+
// any target-specific constructor side effects (e.g.
|
|
167
|
+
// `PostgresMigration`'s `stack.adapter.create(stack)`) run. Concrete
|
|
168
|
+
// subclasses are required to accept the no-arg form; the abstract
|
|
169
|
+
// `Migration` constructor declares `stack?` and target subclasses
|
|
170
|
+
// (Postgres, Mongo) propagate that optionality. This makes the
|
|
171
|
+
// target-mismatch guard fail fast with `PN-MIG-2006` before any
|
|
172
|
+
// stack-driven adapter construction begins, even if the wrong-target
|
|
173
|
+
// adapter's `create` would otherwise succeed and silently misshapen
|
|
174
|
+
// the stored adapter cast.
|
|
175
|
+
const probe = new MigrationClass();
|
|
176
|
+
|
|
177
|
+
if (probe.targetId !== config.target.targetId) {
|
|
178
|
+
throw errorMigrationTargetMismatch({
|
|
179
|
+
migrationTargetId: probe.targetId,
|
|
180
|
+
configTargetId: config.target.targetId,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const stack = createControlStack(config);
|
|
185
|
+
const instance = new MigrationClass(stack);
|
|
186
|
+
|
|
187
|
+
serializeMigrationToDisk(instance, migrationDir, args.dryRun);
|
|
188
|
+
} catch (err) {
|
|
189
|
+
if (CliStructuredError.is(err)) {
|
|
190
|
+
process.stderr.write(`${err.message}: ${err.why}\n`);
|
|
191
|
+
} else {
|
|
192
|
+
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
193
|
+
}
|
|
194
|
+
process.exitCode = 1;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Read a previously-scaffolded `migration.json` from disk, returning
|
|
201
|
+
* `null` when the file is missing or unparseable. The CLI feeds this into
|
|
202
|
+
* `buildMigrationArtifacts` so the pure builder can preserve fields owned
|
|
203
|
+
* by `migration plan` (contract bookends, hints, labels, `createdAt`)
|
|
204
|
+
* across re-emits.
|
|
205
|
+
*/
|
|
206
|
+
function readExistingManifest(manifestPath: string): Partial<MigrationManifest> | null {
|
|
207
|
+
let raw: string;
|
|
208
|
+
try {
|
|
209
|
+
raw = readFileSync(manifestPath, 'utf-8');
|
|
210
|
+
} catch {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
return JSON.parse(raw) as Partial<MigrationManifest>;
|
|
215
|
+
} catch {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Persist a migration instance's artifacts to `migrationDir`. In
|
|
222
|
+
* `dryRun` mode the artifacts are printed to stdout (with the same
|
|
223
|
+
* `--- migration.json --- / --- ops.json ---` framing the legacy
|
|
224
|
+
* `serializeMigration` helper used) and no files are written. Otherwise
|
|
225
|
+
* `ops.json` and `migration.json` are written next to `migration.ts` and
|
|
226
|
+
* a confirmation line is printed.
|
|
227
|
+
*
|
|
228
|
+
* File I/O lives in the CLI rather than `@prisma-next/migration-tools`
|
|
229
|
+
* so the migration-tools package stays focused on the pure
|
|
230
|
+
* `Migration` → in-memory artifact conversion. The CLI is the only
|
|
231
|
+
* legitimate site for combining config loading, stack assembly, and
|
|
232
|
+
* filesystem persistence.
|
|
233
|
+
*/
|
|
234
|
+
function serializeMigrationToDisk(
|
|
235
|
+
instance: Migration,
|
|
236
|
+
migrationDir: string,
|
|
237
|
+
dryRun: boolean,
|
|
238
|
+
): void {
|
|
239
|
+
const manifestPath = join(migrationDir, 'migration.json');
|
|
240
|
+
const existing = readExistingManifest(manifestPath);
|
|
241
|
+
const { opsJson, manifestJson } = buildMigrationArtifacts(instance, existing);
|
|
242
|
+
|
|
243
|
+
if (dryRun) {
|
|
244
|
+
process.stdout.write(`--- migration.json ---\n${manifestJson}\n`);
|
|
245
|
+
process.stdout.write('--- ops.json ---\n');
|
|
246
|
+
process.stdout.write(`${opsJson}\n`);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
writeFileSync(join(migrationDir, 'ops.json'), opsJson);
|
|
251
|
+
writeFileSync(manifestPath, manifestJson);
|
|
252
|
+
|
|
253
|
+
process.stdout.write(`Wrote ops.json + migration.json to ${migrationDir}\n`);
|
|
254
|
+
}
|
package/src/utils/cli-errors.ts
CHANGED
|
@@ -85,7 +85,7 @@ export function resolveMigrationPaths(
|
|
|
85
85
|
configPath: string;
|
|
86
86
|
migrationsDir: string;
|
|
87
87
|
migrationsRelative: string;
|
|
88
|
-
|
|
88
|
+
refsDir: string;
|
|
89
89
|
} {
|
|
90
90
|
const configPath = configOption
|
|
91
91
|
? relative(process.cwd(), resolve(configOption))
|
|
@@ -95,8 +95,8 @@ export function resolveMigrationPaths(
|
|
|
95
95
|
config.migrations?.dir ?? 'migrations',
|
|
96
96
|
);
|
|
97
97
|
const migrationsRelative = relative(process.cwd(), migrationsDir);
|
|
98
|
-
const
|
|
99
|
-
return { configPath, migrationsDir, migrationsRelative,
|
|
98
|
+
const refsDir = resolve(migrationsDir, 'refs');
|
|
99
|
+
return { configPath, migrationsDir, migrationsRelative, refsDir };
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
/**
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"contract-emit-mU1_B_m9.mjs","names":["lines: string[]","exhaustive: never","config: Awaited<ReturnType<typeof loadConfig>>","errorUnexpected","outputPaths: ReturnType<typeof getEmittedArtifactPaths>"],"sources":["../src/utils/formatters/emit.ts","../src/commands/contract-emit.ts"],"sourcesContent":["import { ifDefined } from '@prisma-next/utils/defined';\nimport { relative } from 'pathe';\n\nimport type { GlobalFlags } from '../global-flags';\nimport { isVerbose } from './helpers';\n\n// EmitContractResult type for CLI output formatting (includes file paths)\nexport interface EmitContractResult {\n readonly storageHash: string;\n readonly executionHash?: string;\n readonly profileHash: string;\n readonly outDir: string;\n readonly files: {\n readonly json: string;\n readonly dts: string;\n };\n readonly timings: {\n readonly total: number;\n };\n}\n\n/**\n * Formats human-readable output for contract emit.\n */\nexport function formatEmitOutput(result: EmitContractResult, flags: GlobalFlags): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n // Convert absolute paths to relative paths from cwd\n const jsonPath = relative(process.cwd(), result.files.json);\n const dtsPath = relative(process.cwd(), result.files.dts);\n\n lines.push(`✔ Emitted contract.json → ${jsonPath}`);\n lines.push(`✔ Emitted contract.d.ts → ${dtsPath}`);\n lines.push(` storageHash: ${result.storageHash}`);\n if (result.executionHash) {\n lines.push(` executionHash: ${result.executionHash}`);\n }\n if (result.profileHash) {\n lines.push(` profileHash: ${result.profileHash}`);\n }\n if (isVerbose(flags, 1)) {\n lines.push(` Total time: ${result.timings.total}ms`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for contract emit.\n */\nexport function formatEmitJson(result: EmitContractResult): string {\n const output = {\n ok: true,\n storageHash: result.storageHash,\n ...ifDefined('executionHash', result.executionHash),\n ...(result.profileHash ? { profileHash: result.profileHash } : {}),\n outDir: result.outDir,\n files: result.files,\n timings: result.timings,\n };\n\n return JSON.stringify(output, null, 2);\n}\n","import { mkdirSync, writeFileSync } from 'node:fs';\nimport { getEmittedArtifactPaths } from '@prisma-next/emitter';\nimport { errorContractConfigMissing } from '@prisma-next/errors/control';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport { dirname, relative, resolve } from 'pathe';\nimport { loadConfig } from '../config-loader';\nimport { createControlClient } from '../control-api/client';\nimport type { EmitFailure } from '../control-api/types';\nimport {\n CliStructuredError,\n errorContractValidationFailed,\n errorRuntime,\n errorUnexpected,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n setCommandDescriptions,\n setCommandExamples,\n} from '../utils/command-helpers';\nimport {\n type EmitContractResult,\n formatEmitJson,\n formatEmitOutput,\n} from '../utils/formatters/emit';\nimport { formatStyledHeader, formatSuccessMessage } from '../utils/formatters/styled';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';\nimport { createProgressAdapter } from '../utils/progress-adapter';\nimport { handleResult } from '../utils/result-handler';\nimport { TerminalUI } from '../utils/terminal-ui';\n\ninterface ContractEmitOptions extends CommonCommandOptions {\n readonly config?: string;\n}\n\nfunction mapDiagnosticsToIssues(\n failure: EmitFailure,\n): ReadonlyArray<{ kind: string; message: string }> {\n const diagnostics = failure.diagnostics?.diagnostics ?? [];\n return diagnostics.map((diagnostic) => {\n const location =\n diagnostic.sourceId && diagnostic.span\n ? ` (${diagnostic.sourceId}:${diagnostic.span.start.line}:${diagnostic.span.start.column})`\n : diagnostic.sourceId\n ? ` (${diagnostic.sourceId})`\n : '';\n return {\n kind: diagnostic.code,\n message: `${diagnostic.message}${location}`,\n };\n });\n}\n\n/**\n * Maps an EmitFailure to a CliStructuredError for consistent error handling.\n */\nfunction mapEmitFailure(\n failure: EmitFailure,\n context?: { readonly configPath?: string },\n): CliStructuredError {\n if (failure.code === 'CONTRACT_SOURCE_INVALID') {\n const issues = mapDiagnosticsToIssues(failure);\n return errorRuntime(failure.summary, {\n why: failure.why ?? 'Contract source provider failed',\n fix: 'Check your contract source provider in prisma-next.config.ts and ensure it returns Result<Contract, Diagnostics>',\n ...(issues.length > 0 ? { meta: { issues } } : {}),\n });\n }\n\n if (failure.code === 'CONTRACT_VALIDATION_FAILED') {\n return errorContractValidationFailed(\n failure.why ?? 'Contract validation failed while emitting',\n context?.configPath ? { where: { path: context.configPath } } : undefined,\n );\n }\n\n if (failure.code === 'EMIT_FAILED') {\n return errorRuntime(failure.summary, {\n why: failure.why ?? 'Failed to emit contract',\n fix: 'Check your contract configuration and ensure the source is valid',\n });\n }\n\n // Exhaustive check - TypeScript will error if a new code is added but not handled\n const exhaustive: never = failure.code;\n throw new Error(`Unhandled EmitFailure code: ${exhaustive}`);\n}\n\n/**\n * Executes the contract emit command and returns a structured Result.\n */\nasync function executeContractEmitCommand(\n options: ContractEmitOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n startTime: number,\n): Promise<Result<EmitContractResult, CliStructuredError>> {\n // Load config\n let config: Awaited<ReturnType<typeof loadConfig>>;\n try {\n config = await loadConfig(options.config);\n } catch (error) {\n // Convert thrown CliStructuredError to Result\n if (error instanceof CliStructuredError) {\n return notOk(error);\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: 'Failed to load config',\n }),\n );\n }\n\n const configPath = options.config\n ? relative(process.cwd(), resolve(options.config))\n : 'prisma-next.config.ts';\n\n // Resolve contract from config\n if (!config.contract) {\n return notOk(\n errorContractConfigMissing({\n why: 'Config.contract is required for emit. Define it in your config: contract: { source: ..., output: ... }',\n }),\n );\n }\n\n // Contract config is already normalized by defineConfig() with defaults applied\n const contractConfig = config.contract;\n\n // Resolve artifact paths from config (already normalized by defineConfig() with defaults)\n if (!contractConfig.output) {\n return notOk(\n errorContractConfigMissing({\n why: 'Contract config must have output path. This should not happen if defineConfig() was used.',\n }),\n );\n }\n let outputPaths: ReturnType<typeof getEmittedArtifactPaths>;\n try {\n outputPaths = getEmittedArtifactPaths(contractConfig.output);\n } catch (error) {\n return notOk(\n errorContractConfigMissing({\n why: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n const { jsonPath: outputJsonPath, dtsPath: outputDtsPath } = outputPaths;\n\n // Output header to stderr (decoration)\n if (!flags.json && !flags.quiet) {\n const contractPath = relative(process.cwd(), outputJsonPath);\n const typesPath = relative(process.cwd(), outputDtsPath);\n const header = formatStyledHeader({\n command: 'contract emit',\n description: 'Emit your contract artifacts',\n url: 'https://pris.ly/contract-emit',\n details: [\n { label: 'config', value: configPath },\n { label: 'contract', value: contractPath },\n { label: 'types', value: typesPath },\n ],\n flags,\n });\n ui.stderr(header);\n }\n\n // Create control client (no driver needed for emit)\n const client = createControlClient({\n family: config.family,\n target: config.target,\n adapter: config.adapter,\n extensionPacks: config.extensionPacks ?? [],\n });\n\n // Create progress adapter\n const onProgress = createProgressAdapter({ ui, flags });\n\n try {\n // Call emit with progress callback\n const result = await client.emit({\n contractConfig: {\n source: contractConfig.source,\n output: outputJsonPath,\n },\n onProgress,\n });\n\n // Handle failures by mapping to CLI structured error\n if (!result.ok) {\n return notOk(mapEmitFailure(result.failure, { configPath }));\n }\n\n // Create directories if needed\n mkdirSync(dirname(outputJsonPath), { recursive: true });\n mkdirSync(dirname(outputDtsPath), { recursive: true });\n\n // Write the results to files\n writeFileSync(outputJsonPath, result.value.contractJson, 'utf-8');\n writeFileSync(outputDtsPath, result.value.contractDts, 'utf-8');\n\n // Validate that contract.d.ts type imports are resolvable\n const { validateContractDeps } = await import('../utils/validate-contract-deps');\n const depsValidation = validateContractDeps(result.value.contractDts, dirname(outputDtsPath));\n if (depsValidation.warning) {\n ui.warn(depsValidation.warning);\n }\n\n // Convert success result to CLI output format\n const emitResult: EmitContractResult = {\n storageHash: result.value.storageHash,\n ...(result.value.executionHash ? { executionHash: result.value.executionHash } : {}),\n profileHash: result.value.profileHash,\n outDir: dirname(outputJsonPath),\n files: {\n json: outputJsonPath,\n dts: outputDtsPath,\n },\n timings: { total: Date.now() - startTime },\n };\n\n return ok(emitResult);\n } catch (error) {\n // Use static type guard to work across module boundaries\n if (CliStructuredError.is(error)) {\n return notOk(error);\n }\n\n // Wrap unexpected errors\n return notOk(\n errorUnexpected('Unexpected error during contract emit', {\n why: error instanceof Error ? error.message : String(error),\n }),\n );\n } finally {\n await client.close();\n }\n}\n\nexport function createContractEmitCommand(): Command {\n const command = new Command('emit');\n setCommandDescriptions(\n command,\n 'Emit your contract artifacts',\n 'Reads your contract source (TypeScript or Prisma schema) and emits contract.json and\\n' +\n 'contract.d.ts. The contract.json contains the canonical contract structure, and\\n' +\n 'contract.d.ts provides TypeScript types for type-safe query building.',\n );\n setCommandExamples(command, [\n 'prisma-next contract emit',\n 'prisma-next contract emit --config ./custom-config.ts',\n ]);\n addGlobalOptions(command)\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .action(async (options: ContractEmitOptions) => {\n const flags = parseGlobalFlags(options);\n const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });\n const startTime = Date.now();\n\n const result = await executeContractEmitCommand(options, flags, ui, startTime);\n\n // Handle result - formats output and returns exit code\n const exitCode = handleResult(result, flags, ui, (emitResult) => {\n if (flags.json) {\n ui.output(formatEmitJson(emitResult));\n } else {\n const output = formatEmitOutput(emitResult, flags);\n if (output) {\n ui.log(output);\n }\n if (!flags.quiet) {\n ui.success(formatSuccessMessage(flags));\n }\n }\n });\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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"}
|