@prisma-next/cli 0.4.0-dev.9 → 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 +26 -18
- 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-Cd79vmTH.mjs +5 -0
- package/dist/cli.mjs +127 -25
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-CJxHfhze.mjs → client-CrsnY58k.mjs} +9 -8
- package/dist/{client-CJxHfhze.mjs.map → client-CrsnY58k.mjs.map} +1 -1
- package/dist/commands/contract-emit.d.mts.map +1 -1
- package/dist/commands/contract-emit.mjs +7 -7
- package/dist/commands/contract-infer.mjs +8 -8
- package/dist/commands/db-init.mjs +8 -8
- package/dist/commands/db-schema.mjs +8 -8
- package/dist/commands/db-sign.mjs +8 -8
- package/dist/commands/db-update.mjs +8 -8
- package/dist/commands/db-verify.mjs +9 -9
- package/dist/commands/migration-apply.d.mts +1 -1
- package/dist/commands/migration-apply.d.mts.map +1 -1
- package/dist/commands/migration-apply.mjs +37 -28
- package/dist/commands/migration-apply.mjs.map +1 -1
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +48 -23
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +6 -1
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +94 -71
- package/dist/commands/migration-plan.mjs.map +1 -1
- 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 +2 -2
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +11 -16
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +4 -5
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +7 -7
- package/dist/config-loader-C25b63rJ.mjs +90 -0
- package/dist/config-loader-C25b63rJ.mjs.map +1 -0
- package/dist/config-loader.d.mts.map +1 -1
- package/dist/config-loader.mjs +1 -1
- package/dist/contract-emit-DxgyXrqV.mjs +6 -0
- package/dist/{contract-emit-gpJNLGs7.mjs → contract-emit-NJ01hiiv.mjs} +20 -16
- package/dist/contract-emit-NJ01hiiv.mjs.map +1 -0
- package/dist/{contract-emit-CKig_Lra.mjs → contract-emit-V5SSitUT.mjs} +25 -21
- package/dist/contract-emit-V5SSitUT.mjs.map +1 -0
- package/dist/{contract-enrichment-CGW6mm-E.mjs → contract-enrichment-CAOELa-H.mjs} +1 -1
- package/dist/{contract-enrichment-CGW6mm-E.mjs.map → contract-enrichment-CAOELa-H.mjs.map} +1 -1
- package/dist/{contract-infer-BDJgg7Xb.mjs → contract-infer-D9cC3rJm.mjs} +4 -4
- package/dist/{contract-infer-BDJgg7Xb.mjs.map → contract-infer-D9cC3rJm.mjs.map} +1 -1
- package/dist/exports/control-api.d.mts +2 -2
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +6 -6
- package/dist/exports/index.mjs +7 -7
- 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-DZUJNmL3.mjs → extract-operation-statements-DsFfxXVZ.mjs} +2 -2
- package/dist/{extract-operation-statements-DZUJNmL3.mjs.map → extract-operation-statements-DsFfxXVZ.mjs.map} +1 -1
- package/dist/{extract-sql-ddl-DDMX-9mz.mjs → extract-sql-ddl-D9UbZDyz.mjs} +1 -1
- package/dist/{extract-sql-ddl-DDMX-9mz.mjs.map → extract-sql-ddl-D9UbZDyz.mjs.map} +1 -1
- package/dist/{framework-components-Bsr1GaIj.mjs → framework-components-Cr--XBKy.mjs} +2 -2
- package/dist/{framework-components-Bsr1GaIj.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-ChqrALmw.mjs → inspect-live-schema-yrHAvG71.mjs} +6 -6
- package/dist/{inspect-live-schema-ChqrALmw.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-B0oH_hyB.mjs → migration-command-scaffold-B3B09et6.mjs} +7 -7
- package/dist/{migration-command-scaffold-B0oH_hyB.mjs.map → migration-command-scaffold-B3B09et6.mjs.map} +1 -1
- package/dist/{migration-status-CPamfEPj.mjs → migration-status-DUMiH8_G.mjs} +25 -43
- package/dist/migration-status-DUMiH8_G.mjs.map +1 -0
- package/dist/{migrations-BIsjFjSV.mjs → migrations-Bo5WtTla.mjs} +4 -15
- package/dist/migrations-Bo5WtTla.mjs.map +1 -0
- package/dist/output-BpcQrnnq.mjs +103 -0
- package/dist/output-BpcQrnnq.mjs.map +1 -0
- package/dist/{progress-adapter-B-YvmcDu.mjs → progress-adapter-DvQWB1nK.mjs} +1 -1
- package/dist/{progress-adapter-B-YvmcDu.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-AFK4hxyX.mjs → result-handler-Ba3zWQsI.mjs} +26 -88
- package/dist/result-handler-Ba3zWQsI.mjs.map +1 -0
- package/dist/{terminal-ui-C5k88MmW.mjs → terminal-ui-C3ZLwQxK.mjs} +76 -2
- package/dist/terminal-ui-C3ZLwQxK.mjs.map +1 -0
- package/dist/{validate-contract-deps-DBH6iTAD.mjs → validate-contract-deps-B_Cs29TL.mjs} +1 -1
- package/dist/{validate-contract-deps-DBH6iTAD.mjs.map → validate-contract-deps-B_Cs29TL.mjs.map} +1 -1
- package/dist/{verify-C56CuQc7.mjs → verify-Bkycc-Tf.mjs} +2 -2
- package/dist/{verify-C56CuQc7.mjs.map → verify-Bkycc-Tf.mjs.map} +1 -1
- package/package.json +24 -19
- package/src/cli.ts +1 -5
- package/src/commands/contract-emit.ts +9 -10
- 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 +37 -26
- package/src/commands/migration-new.ts +39 -17
- package/src/commands/migration-plan.ts +119 -104
- package/src/commands/migration-ref.ts +32 -47
- package/src/commands/migration-show.ts +6 -16
- package/src/commands/migration-status.ts +30 -55
- package/src/config-loader.ts +35 -29
- package/src/config-path-validation.ts +75 -0
- package/src/control-api/client.ts +2 -1
- package/src/control-api/operations/contract-emit.ts +24 -23
- package/src/control-api/types.ts +1 -1
- 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 +18 -22
- package/src/utils/formatters/graph-migration-mapper.ts +5 -14
- package/src/utils/formatters/help.ts +0 -1
- package/src/utils/formatters/migrations.ts +2 -29
- package/dist/cli-errors-BUuJr6py.mjs +0 -5
- package/dist/cli-errors-Dic2eADK.d.mts +0 -4
- package/dist/commands/migration-emit.d.mts +0 -38
- package/dist/commands/migration-emit.d.mts.map +0 -1
- package/dist/commands/migration-emit.mjs +0 -81
- package/dist/commands/migration-emit.mjs.map +0 -1
- package/dist/config-loader-C4VXKl8f.mjs +0 -43
- package/dist/config-loader-C4VXKl8f.mjs.map +0 -1
- package/dist/contract-emit-CKig_Lra.mjs.map +0 -1
- package/dist/contract-emit-CU-SYNe4.mjs +0 -6
- package/dist/contract-emit-gpJNLGs7.mjs.map +0 -1
- package/dist/init-DZWvhEP0.mjs +0 -430
- package/dist/init-DZWvhEP0.mjs.map +0 -1
- package/dist/migration-emit-Du4DBMqz.mjs +0 -125
- package/dist/migration-emit-Du4DBMqz.mjs.map +0 -1
- package/dist/migration-status-CPamfEPj.mjs.map +0 -1
- package/dist/migrations-BIsjFjSV.mjs.map +0 -1
- package/dist/result-handler-AFK4hxyX.mjs.map +0 -1
- package/dist/terminal-ui-C5k88MmW.mjs.map +0 -1
- package/src/commands/migration-emit.ts +0 -134
- package/src/lib/migration-emit.ts +0 -125
- package/src/lib/migration-strategy.ts +0 -49
|
@@ -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({
|
|
@@ -2,7 +2,7 @@ import type { MigrationPlanOperation } from '@prisma-next/framework-components/c
|
|
|
2
2
|
import { findLatestMigration, reconstructGraph } from '@prisma-next/migration-tools/dag';
|
|
3
3
|
import { readMigrationPackage, readMigrationsDir } from '@prisma-next/migration-tools/io';
|
|
4
4
|
import type { MigrationBundle } from '@prisma-next/migration-tools/types';
|
|
5
|
-
import {
|
|
5
|
+
import { MigrationToolsError } from '@prisma-next/migration-tools/types';
|
|
6
6
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
7
7
|
import { Command } from 'commander';
|
|
8
8
|
import { relative, resolve } from 'pathe';
|
|
@@ -31,7 +31,7 @@ export interface MigrationShowResult {
|
|
|
31
31
|
readonly dirPath: string;
|
|
32
32
|
readonly from: string;
|
|
33
33
|
readonly to: string;
|
|
34
|
-
readonly migrationId: string
|
|
34
|
+
readonly migrationId: string;
|
|
35
35
|
readonly kind: string;
|
|
36
36
|
readonly createdAt: string;
|
|
37
37
|
readonly operations: readonly {
|
|
@@ -52,8 +52,7 @@ export function resolveByHashPrefix(
|
|
|
52
52
|
prefix: string,
|
|
53
53
|
): Result<MigrationBundle, CliStructuredError> {
|
|
54
54
|
const normalizedPrefix = prefix.startsWith('sha256:') ? prefix : `sha256:${prefix}`;
|
|
55
|
-
const
|
|
56
|
-
const matches = attested.filter((p) => p.manifest.migrationId!.startsWith(normalizedPrefix));
|
|
55
|
+
const matches = packages.filter((p) => p.manifest.migrationId.startsWith(normalizedPrefix));
|
|
57
56
|
|
|
58
57
|
if (matches.length === 1) {
|
|
59
58
|
return ok(matches[0]!);
|
|
@@ -62,7 +61,7 @@ export function resolveByHashPrefix(
|
|
|
62
61
|
if (matches.length === 0) {
|
|
63
62
|
return notOk(
|
|
64
63
|
errorRuntime('No migration found matching prefix', {
|
|
65
|
-
why: `No
|
|
64
|
+
why: `No migration has a migrationId starting with "${normalizedPrefix}"`,
|
|
66
65
|
fix: 'Run `prisma-next migration show` (no argument) to see the latest migration, or check the migrations directory for available packages.',
|
|
67
66
|
}),
|
|
68
67
|
);
|
|
@@ -132,16 +131,7 @@ async function executeMigrationShowCommand(
|
|
|
132
131
|
if (!resolved.ok) return resolved;
|
|
133
132
|
pkg = resolved.value;
|
|
134
133
|
} else {
|
|
135
|
-
const
|
|
136
|
-
if (attested.length === 0) {
|
|
137
|
-
return notOk(
|
|
138
|
-
errorRuntime('No attested migrations found', {
|
|
139
|
-
why: `All migrations in ${migrationsRelative} are drafts (migrationId: null)`,
|
|
140
|
-
fix: 'Run `prisma-next migration emit --dir <path>` to attest a draft migration.',
|
|
141
|
-
}),
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
const graph = reconstructGraph(attested);
|
|
134
|
+
const graph = reconstructGraph(allPackages);
|
|
145
135
|
const latestMigration = findLatestMigration(graph);
|
|
146
136
|
if (!latestMigration) {
|
|
147
137
|
return notOk(
|
|
@@ -151,7 +141,7 @@ async function executeMigrationShowCommand(
|
|
|
151
141
|
}),
|
|
152
142
|
);
|
|
153
143
|
}
|
|
154
|
-
const leafPkg =
|
|
144
|
+
const leafPkg = allPackages.find(
|
|
155
145
|
(p) => p.manifest.migrationId === latestMigration.migrationId,
|
|
156
146
|
);
|
|
157
147
|
if (!leafPkg) {
|
|
@@ -8,8 +8,7 @@ import {
|
|
|
8
8
|
import type { Refs } from '@prisma-next/migration-tools/refs';
|
|
9
9
|
import { readRefs, resolveRef } from '@prisma-next/migration-tools/refs';
|
|
10
10
|
import type {
|
|
11
|
-
|
|
12
|
-
DraftMigrationBundle,
|
|
11
|
+
MigrationBundle,
|
|
13
12
|
MigrationChainEntry,
|
|
14
13
|
MigrationGraph,
|
|
15
14
|
} from '@prisma-next/migration-tools/types';
|
|
@@ -62,7 +61,7 @@ export interface MigrationStatusEntry {
|
|
|
62
61
|
readonly dirName: string;
|
|
63
62
|
readonly from: string;
|
|
64
63
|
readonly to: string;
|
|
65
|
-
readonly migrationId: string
|
|
64
|
+
readonly migrationId: string;
|
|
66
65
|
readonly operationCount: number;
|
|
67
66
|
readonly operationSummary: string;
|
|
68
67
|
readonly hasDestructive: boolean;
|
|
@@ -87,7 +86,7 @@ export interface MigrationStatusResult {
|
|
|
87
86
|
readonly refName?: string;
|
|
88
87
|
readonly selectedPath: readonly {
|
|
89
88
|
readonly dirName: string;
|
|
90
|
-
readonly migrationId: string
|
|
89
|
+
readonly migrationId: string;
|
|
91
90
|
readonly from: string;
|
|
92
91
|
readonly to: string;
|
|
93
92
|
}[];
|
|
@@ -95,8 +94,7 @@ export interface MigrationStatusResult {
|
|
|
95
94
|
readonly summary: string;
|
|
96
95
|
readonly diagnostics: readonly StatusDiagnostic[];
|
|
97
96
|
readonly graph?: MigrationGraph;
|
|
98
|
-
readonly bundles?: readonly
|
|
99
|
-
readonly drafts?: readonly DraftMigrationBundle[];
|
|
97
|
+
readonly bundles?: readonly MigrationBundle[];
|
|
100
98
|
readonly edgeStatuses?: readonly EdgeStatus[];
|
|
101
99
|
readonly activeRefHash?: string;
|
|
102
100
|
readonly activeRefName?: string;
|
|
@@ -227,7 +225,7 @@ export function deriveEdgeStatuses(
|
|
|
227
225
|
*/
|
|
228
226
|
function buildMigrationEntries(
|
|
229
227
|
chain: readonly MigrationChainEntry[],
|
|
230
|
-
packages: readonly
|
|
228
|
+
packages: readonly MigrationBundle[],
|
|
231
229
|
mode: 'online' | 'offline',
|
|
232
230
|
markerHash: string | undefined,
|
|
233
231
|
edgeStatuses?: readonly EdgeStatus[],
|
|
@@ -347,7 +345,7 @@ async function executeMigrationStatusCommand(
|
|
|
347
345
|
ui: TerminalUI,
|
|
348
346
|
): Promise<Result<MigrationStatusResult, CliStructuredError>> {
|
|
349
347
|
const config = await loadConfig(options.config);
|
|
350
|
-
const { configPath, migrationsDir, migrationsRelative,
|
|
348
|
+
const { configPath, migrationsDir, migrationsRelative, refsDir } = resolveMigrationPaths(
|
|
351
349
|
options.config,
|
|
352
350
|
config,
|
|
353
351
|
);
|
|
@@ -359,7 +357,7 @@ async function executeMigrationStatusCommand(
|
|
|
359
357
|
let activeRefHash: string | undefined;
|
|
360
358
|
let allRefs: Refs = {};
|
|
361
359
|
try {
|
|
362
|
-
allRefs = await readRefs(
|
|
360
|
+
allRefs = await readRefs(refsDir);
|
|
363
361
|
} catch (error) {
|
|
364
362
|
if (MigrationToolsError.is(error)) {
|
|
365
363
|
return notOk(
|
|
@@ -375,30 +373,25 @@ async function executeMigrationStatusCommand(
|
|
|
375
373
|
|
|
376
374
|
if (options.ref) {
|
|
377
375
|
activeRefName = options.ref;
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
fix: error.fix,
|
|
390
|
-
meta: { code: error.code },
|
|
391
|
-
}),
|
|
392
|
-
);
|
|
393
|
-
}
|
|
394
|
-
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
|
+
);
|
|
395
387
|
}
|
|
388
|
+
throw error;
|
|
396
389
|
}
|
|
397
390
|
}
|
|
398
391
|
|
|
399
|
-
const statusRefs: StatusRef[] = Object.entries(allRefs).map(([name,
|
|
392
|
+
const statusRefs: StatusRef[] = Object.entries(allRefs).map(([name, entry]) => ({
|
|
400
393
|
name,
|
|
401
|
-
hash,
|
|
394
|
+
hash: entry.hash,
|
|
402
395
|
active: name === activeRefName,
|
|
403
396
|
}));
|
|
404
397
|
|
|
@@ -436,11 +429,10 @@ async function executeMigrationStatusCommand(
|
|
|
436
429
|
});
|
|
437
430
|
}
|
|
438
431
|
|
|
439
|
-
let
|
|
440
|
-
let drafts: readonly DraftMigrationBundle[];
|
|
432
|
+
let bundles: readonly MigrationBundle[];
|
|
441
433
|
let graph: MigrationGraph;
|
|
442
434
|
try {
|
|
443
|
-
({
|
|
435
|
+
({ bundles, graph } = await loadAllBundles(migrationsDir));
|
|
444
436
|
} catch (error) {
|
|
445
437
|
if (MigrationToolsError.is(error)) {
|
|
446
438
|
return notOk(
|
|
@@ -454,18 +446,7 @@ async function executeMigrationStatusCommand(
|
|
|
454
446
|
);
|
|
455
447
|
}
|
|
456
448
|
|
|
457
|
-
if (
|
|
458
|
-
diagnostics.push({
|
|
459
|
-
code: 'MIGRATION.DRAFTS',
|
|
460
|
-
severity: 'warn',
|
|
461
|
-
message: `${drafts.length} draft migration(s) found: ${drafts.map((d) => d.dirName).join(', ')}`,
|
|
462
|
-
hints: [
|
|
463
|
-
"Run 'prisma-next migration emit --dir <path>' to attest draft migrations before applying",
|
|
464
|
-
],
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
if (attested.length === 0) {
|
|
449
|
+
if (bundles.length === 0) {
|
|
469
450
|
if (contractHash !== EMPTY_CONTRACT_HASH) {
|
|
470
451
|
diagnostics.push({
|
|
471
452
|
code: 'CONTRACT.AHEAD',
|
|
@@ -576,7 +557,7 @@ async function executeMigrationStatusCommand(
|
|
|
576
557
|
migrations: [],
|
|
577
558
|
targetHash: EMPTY_CONTRACT_HASH,
|
|
578
559
|
contractHash,
|
|
579
|
-
summary: `${
|
|
560
|
+
summary: `${bundles.length} migration(s) on disk`,
|
|
580
561
|
diagnostics,
|
|
581
562
|
markerHash,
|
|
582
563
|
...(statusRefs.length > 0 ? { refs: statusRefs } : {}),
|
|
@@ -616,12 +597,12 @@ async function executeMigrationStatusCommand(
|
|
|
616
597
|
migrations: [],
|
|
617
598
|
targetHash: EMPTY_CONTRACT_HASH,
|
|
618
599
|
contractHash,
|
|
619
|
-
summary: `${
|
|
600
|
+
summary: `${bundles.length} migration(s) on disk`,
|
|
620
601
|
diagnostics,
|
|
621
602
|
...ifDefined('markerHash', markerHash),
|
|
622
603
|
...(statusRefs.length > 0 ? { refs: statusRefs } : {}),
|
|
623
604
|
graph,
|
|
624
|
-
bundles
|
|
605
|
+
bundles,
|
|
625
606
|
diverged: true,
|
|
626
607
|
});
|
|
627
608
|
}
|
|
@@ -638,7 +619,7 @@ async function executeMigrationStatusCommand(
|
|
|
638
619
|
}
|
|
639
620
|
|
|
640
621
|
const edgeStatuses = deriveEdgeStatuses(graph, targetHash, contractHash, markerHash, mode);
|
|
641
|
-
const entries = buildMigrationEntries(chain,
|
|
622
|
+
const entries = buildMigrationEntries(chain, bundles, mode, markerHash, edgeStatuses);
|
|
642
623
|
|
|
643
624
|
const pendingCount = edgeStatuses.filter((e) => e.status === 'pending').length;
|
|
644
625
|
const appliedCount = edgeStatuses.filter((e) => e.status === 'applied').length;
|
|
@@ -646,7 +627,7 @@ async function executeMigrationStatusCommand(
|
|
|
646
627
|
let summary: string;
|
|
647
628
|
if (mode === 'online') {
|
|
648
629
|
if (markerHash !== undefined && !graph.nodes.has(markerHash) && markerHash === contractHash) {
|
|
649
|
-
summary = `${
|
|
630
|
+
summary = `${bundles.length} migration(s) on disk`;
|
|
650
631
|
} else if (activeRefHash && markerHash !== undefined) {
|
|
651
632
|
summary = summarizeRefDistance(graph, markerHash, activeRefHash, activeRefName!);
|
|
652
633
|
} else if (pendingCount === 0) {
|
|
@@ -706,8 +687,7 @@ async function executeMigrationStatusCommand(
|
|
|
706
687
|
...(statusRefs.length > 0 ? { refs: statusRefs } : {}),
|
|
707
688
|
...ifDefined('pathDecision', pathDecision),
|
|
708
689
|
graph,
|
|
709
|
-
bundles
|
|
710
|
-
...(drafts.length > 0 ? { drafts } : {}),
|
|
690
|
+
bundles,
|
|
711
691
|
edgeStatuses,
|
|
712
692
|
...ifDefined('activeRefHash', activeRefHash),
|
|
713
693
|
...ifDefined('activeRefName', activeRefName),
|
|
@@ -768,11 +748,6 @@ export function createMigrationStatusCommand(): Command {
|
|
|
768
748
|
activeRefHash: statusResult.activeRefHash,
|
|
769
749
|
activeRefName: statusResult.activeRefName,
|
|
770
750
|
edgeStatuses: statusResult.edgeStatuses,
|
|
771
|
-
draftEdges: statusResult.drafts?.map((d) => ({
|
|
772
|
-
from: d.manifest.from,
|
|
773
|
-
to: d.manifest.to,
|
|
774
|
-
dirName: d.dirName,
|
|
775
|
-
})),
|
|
776
751
|
});
|
|
777
752
|
|
|
778
753
|
const graphToRender =
|
package/src/config-loader.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { dirname, resolve } from 'node:path';
|
|
2
1
|
import type { PrismaNextConfig } from '@prisma-next/config/config-types';
|
|
3
2
|
import { ConfigValidationError, validateConfig } from '@prisma-next/config/config-validation';
|
|
4
3
|
import {
|
|
@@ -6,7 +5,41 @@ import {
|
|
|
6
5
|
errorConfigValidation,
|
|
7
6
|
errorUnexpected,
|
|
8
7
|
} from '@prisma-next/errors/control';
|
|
8
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
9
9
|
import { loadConfig as loadConfigC12 } from 'c12';
|
|
10
|
+
import { dirname, resolve } from 'pathe';
|
|
11
|
+
import { finalizeConfig } from './config-path-validation';
|
|
12
|
+
|
|
13
|
+
async function loadValidatedConfig(configPath?: string): Promise<PrismaNextConfig> {
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
const resolvedConfigPath = configPath ? resolve(cwd, configPath) : undefined;
|
|
16
|
+
const configCwd = resolvedConfigPath ? dirname(resolvedConfigPath) : cwd;
|
|
17
|
+
|
|
18
|
+
const result = await loadConfigC12<PrismaNextConfig>({
|
|
19
|
+
name: 'prisma-next',
|
|
20
|
+
...ifDefined('configFile', resolvedConfigPath),
|
|
21
|
+
cwd: configCwd,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// When a specific config file was requested, verify it was actually loaded
|
|
25
|
+
// (c12 falls back to searching by name if the specified file doesn't exist)
|
|
26
|
+
if (resolvedConfigPath && result.configFile !== resolvedConfigPath) {
|
|
27
|
+
throw errorConfigFileNotFound(resolvedConfigPath);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Check if config is missing or empty (c12 may return empty object when file doesn't exist)
|
|
31
|
+
if (!result.config || Object.keys(result.config).length === 0) {
|
|
32
|
+
// Use c12's configFile if available, otherwise use explicit configPath, otherwise omit path
|
|
33
|
+
const displayPath = result.configFile || resolvedConfigPath || configPath;
|
|
34
|
+
throw errorConfigFileNotFound(displayPath);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Validate config structure
|
|
38
|
+
validateConfig(result.config);
|
|
39
|
+
|
|
40
|
+
const loadedConfigDir = result.configFile ? dirname(result.configFile) : configCwd;
|
|
41
|
+
return finalizeConfig(result.config, loadedConfigDir);
|
|
42
|
+
}
|
|
10
43
|
|
|
11
44
|
/**
|
|
12
45
|
* Loads the Prisma Next config from a TypeScript file.
|
|
@@ -19,34 +52,7 @@ import { loadConfig as loadConfigC12 } from 'c12';
|
|
|
19
52
|
*/
|
|
20
53
|
export async function loadConfig(configPath?: string): Promise<PrismaNextConfig> {
|
|
21
54
|
try {
|
|
22
|
-
|
|
23
|
-
// Resolve config path to absolute path and set cwd to config directory when path is provided
|
|
24
|
-
const resolvedConfigPath = configPath ? resolve(cwd, configPath) : undefined;
|
|
25
|
-
const configCwd = resolvedConfigPath ? dirname(resolvedConfigPath) : cwd;
|
|
26
|
-
|
|
27
|
-
const result = await loadConfigC12<PrismaNextConfig>({
|
|
28
|
-
name: 'prisma-next',
|
|
29
|
-
...(resolvedConfigPath ? { configFile: resolvedConfigPath } : {}),
|
|
30
|
-
cwd: configCwd,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
// When a specific config file was requested, verify it was actually loaded
|
|
34
|
-
// (c12 falls back to searching by name if the specified file doesn't exist)
|
|
35
|
-
if (resolvedConfigPath && result.configFile !== resolvedConfigPath) {
|
|
36
|
-
throw errorConfigFileNotFound(resolvedConfigPath);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Check if config is missing or empty (c12 may return empty object when file doesn't exist)
|
|
40
|
-
if (!result.config || Object.keys(result.config).length === 0) {
|
|
41
|
-
// Use c12's configFile if available, otherwise use explicit configPath, otherwise omit path
|
|
42
|
-
const displayPath = result.configFile || resolvedConfigPath || configPath;
|
|
43
|
-
throw errorConfigFileNotFound(displayPath);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Validate config structure
|
|
47
|
-
validateConfig(result.config);
|
|
48
|
-
|
|
49
|
-
return result.config;
|
|
55
|
+
return await loadValidatedConfig(configPath);
|
|
50
56
|
} catch (error) {
|
|
51
57
|
if (error instanceof ConfigValidationError) {
|
|
52
58
|
throw errorConfigValidation(error.field, {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ContractSourceProvider,
|
|
3
|
+
normalizeContractConfig,
|
|
4
|
+
type PrismaNextConfig,
|
|
5
|
+
} from '@prisma-next/config/config-types';
|
|
6
|
+
import { ConfigValidationError } from '@prisma-next/config/config-validation';
|
|
7
|
+
import { getEmittedArtifactPaths } from '@prisma-next/emitter';
|
|
8
|
+
import { resolve } from 'pathe';
|
|
9
|
+
|
|
10
|
+
function throwValidation(field: string, why: string): never {
|
|
11
|
+
throw new ConfigValidationError(field, why);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function finalizeContractSource(
|
|
15
|
+
source: ContractSourceProvider,
|
|
16
|
+
configDir: string,
|
|
17
|
+
): ContractSourceProvider {
|
|
18
|
+
const resolvedInputs = source.inputs?.map((input) => resolve(configDir, input));
|
|
19
|
+
if (resolvedInputs === undefined) {
|
|
20
|
+
return source;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
...source,
|
|
25
|
+
inputs: resolvedInputs,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function validateNoOutputsAreInputs(
|
|
30
|
+
inputs: readonly string[] | undefined,
|
|
31
|
+
output: string | undefined,
|
|
32
|
+
): void {
|
|
33
|
+
if (inputs === undefined || output === undefined) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let emittedArtifactPaths: ReturnType<typeof getEmittedArtifactPaths>;
|
|
38
|
+
try {
|
|
39
|
+
emittedArtifactPaths = getEmittedArtifactPaths(output);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
throwValidation('contract.output', error instanceof Error ? error.message : String(error));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const emittedPaths = new Set([emittedArtifactPaths.jsonPath, emittedArtifactPaths.dtsPath]);
|
|
45
|
+
|
|
46
|
+
for (const input of inputs) {
|
|
47
|
+
if (emittedPaths.has(input)) {
|
|
48
|
+
throwValidation(
|
|
49
|
+
'contract.source.inputs[]',
|
|
50
|
+
'Config.contract.source.inputs must not include emitted artifact paths derived from contract.output',
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function finalizeConfig(config: PrismaNextConfig, configDir: string): PrismaNextConfig {
|
|
57
|
+
if (!config.contract) {
|
|
58
|
+
return config;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const contract = normalizeContractConfig(config.contract);
|
|
62
|
+
const source = finalizeContractSource(contract.source, configDir);
|
|
63
|
+
const output = resolve(configDir, contract.output);
|
|
64
|
+
|
|
65
|
+
validateNoOutputsAreInputs(source.inputs, output);
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
...config,
|
|
69
|
+
contract: {
|
|
70
|
+
...contract,
|
|
71
|
+
source,
|
|
72
|
+
output,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -496,8 +496,9 @@ class ControlClientImpl implements ControlClient {
|
|
|
496
496
|
authoringContributions: stack.authoringContributions,
|
|
497
497
|
codecLookup: stack.codecLookup,
|
|
498
498
|
controlMutationDefaults: stack.controlMutationDefaults,
|
|
499
|
+
resolvedInputs: contractConfig.source.inputs ?? [],
|
|
499
500
|
};
|
|
500
|
-
const providerResult = await contractConfig.
|
|
501
|
+
const providerResult = await contractConfig.source.load(sourceContext);
|
|
501
502
|
if (!providerResult.ok) {
|
|
502
503
|
onProgress?.({
|
|
503
504
|
action: 'emit',
|