@prisma-next/cli 0.5.0-dev.2 → 0.5.0-dev.20
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 +54 -21
- package/dist/agent-skill-mongo.md +63 -31
- package/dist/agent-skill-postgres.md +1 -1
- package/dist/{cli-errors-C0JhVj0c.d.mts → cli-errors-BJLUczXT.d.mts} +1 -0
- package/dist/cli-errors-By1iVE3z.mjs +34 -0
- package/dist/cli-errors-By1iVE3z.mjs.map +1 -0
- package/dist/cli.mjs +127 -13
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-TG7rbCWT.mjs → client-enZIahga.mjs} +20 -5
- package/dist/client-enZIahga.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts.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 +9 -8
- 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 +9 -8
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.mjs +10 -9
- package/dist/commands/db-verify.mjs.map +1 -1
- 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 +15 -38
- 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 +24 -28
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +6 -3
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +38 -38
- 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 +31 -40
- package/dist/commands/migration-ref.mjs.map +1 -1
- package/dist/commands/migration-show.d.mts +4 -4
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +19 -26
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +5 -4
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +7 -2
- package/dist/{config-loader-_W4T21X1.mjs → config-loader-ih8ViDb_.mjs} +2 -2
- package/dist/config-loader-ih8ViDb_.mjs.map +1 -0
- package/dist/config-loader.mjs +1 -1
- package/dist/contract-emit-DS5NzZh2.mjs +6 -0
- package/dist/contract-emit-DWtGQYCD.mjs +150 -0
- package/dist/contract-emit-DWtGQYCD.mjs.map +1 -0
- package/dist/contract-emit-RZBWzkop.mjs +329 -0
- package/dist/contract-emit-RZBWzkop.mjs.map +1 -0
- package/dist/{contract-enrichment-CGW6mm-E.mjs → contract-enrichment-4Ptgw3Pe.mjs} +1 -1
- package/dist/{contract-enrichment-CGW6mm-E.mjs.map → contract-enrichment-4Ptgw3Pe.mjs.map} +1 -1
- package/dist/{contract-infer-BP3DrGgz.mjs → contract-infer-BjzkcwQt.mjs} +5 -5
- package/dist/{contract-infer-BP3DrGgz.mjs.map → contract-infer-BjzkcwQt.mjs.map} +1 -1
- package/dist/exports/control-api.d.mts +41 -16
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +7 -5
- package/dist/exports/index.mjs +8 -3
- 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-DZUJNmL3.mjs → extract-operation-statements-CU-Pp4-N.mjs} +2 -2
- package/dist/{extract-operation-statements-DZUJNmL3.mjs.map → extract-operation-statements-CU-Pp4-N.mjs.map} +1 -1
- package/dist/{extract-sql-ddl-DDMX-9mz.mjs → extract-sql-ddl-Bm0Mm0IT.mjs} +1 -1
- package/dist/{extract-sql-ddl-DDMX-9mz.mjs.map → extract-sql-ddl-Bm0Mm0IT.mjs.map} +1 -1
- package/dist/{framework-components-DfZKQBQ2.mjs → framework-components-Bgcre3Z6.mjs} +2 -2
- package/dist/{framework-components-DfZKQBQ2.mjs.map → framework-components-Bgcre3Z6.mjs.map} +1 -1
- package/dist/init-C-H-if1m.mjs +2062 -0
- package/dist/init-C-H-if1m.mjs.map +1 -0
- package/dist/{inspect-live-schema-DWzf4Q_m.mjs → inspect-live-schema-QklSDLt_.mjs} +6 -6
- package/dist/{inspect-live-schema-DWzf4Q_m.mjs.map → inspect-live-schema-QklSDLt_.mjs.map} +1 -1
- package/dist/migration-cli.mjs +15 -8
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-CLMD302g.mjs → migration-command-scaffold-BfloSWPZ.mjs} +7 -7
- package/dist/{migration-command-scaffold-CLMD302g.mjs.map → migration-command-scaffold-BfloSWPZ.mjs.map} +1 -1
- package/dist/{migration-status-B0HLF7So.mjs → migration-status-C5VYA5r9.mjs} +21 -35
- package/dist/migration-status-C5VYA5r9.mjs.map +1 -0
- package/dist/{migrations-B0dOQlk0.mjs → migrations-CSaDHNpB.mjs} +3 -3
- package/dist/migrations-CSaDHNpB.mjs.map +1 -0
- package/dist/output-BiO7kt87.mjs +103 -0
- package/dist/output-BiO7kt87.mjs.map +1 -0
- package/dist/{progress-adapter-B-YvmcDu.mjs → progress-adapter-DgRGldpT.mjs} +1 -1
- package/dist/{progress-adapter-B-YvmcDu.mjs.map → progress-adapter-DgRGldpT.mjs.map} +1 -1
- package/dist/quick-reference-mongo.md +34 -13
- package/dist/quick-reference-postgres.md +11 -9
- package/dist/{result-handler-CIyu0Pdt.mjs → result-handler-BmVh8AeV.mjs} +12 -93
- package/dist/result-handler-BmVh8AeV.mjs.map +1 -0
- package/dist/{terminal-ui-C5k88MmW.mjs → terminal-ui-u2YgKghu.mjs} +76 -2
- package/dist/terminal-ui-u2YgKghu.mjs.map +1 -0
- package/dist/{verify-BxiVp50b.mjs → verify-BumcH6Ry.mjs} +2 -2
- package/dist/{verify-BxiVp50b.mjs.map → verify-BumcH6Ry.mjs.map} +1 -1
- package/package.json +20 -15
- package/src/commands/contract-emit.ts +67 -163
- 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 +15 -50
- package/src/commands/migration-new.ts +24 -28
- package/src/commands/migration-plan.ts +58 -42
- package/src/commands/migration-ref.ts +40 -54
- package/src/commands/migration-show.ts +27 -28
- package/src/commands/migration-status.ts +33 -50
- package/src/config-path-validation.ts +0 -1
- package/src/control-api/operations/contract-emit.ts +198 -115
- package/src/control-api/operations/migration-apply.ts +15 -0
- package/src/control-api/types.ts +22 -3
- package/src/exports/control-api.ts +2 -1
- package/src/exports/init-output.ts +10 -0
- package/src/migration-cli.ts +16 -9
- package/src/utils/cli-errors.ts +45 -1
- package/src/utils/command-helpers.ts +13 -26
- package/src/utils/emit-queue.ts +26 -0
- package/src/utils/formatters/graph-migration-mapper.ts +2 -2
- package/src/utils/formatters/migrations.ts +2 -2
- package/src/utils/publish-contract-artifact-pair.ts +134 -0
- package/dist/cli-errors-DHq6GQGu.mjs +0 -5
- package/dist/client-TG7rbCWT.mjs.map +0 -1
- package/dist/config-loader-_W4T21X1.mjs.map +0 -1
- package/dist/contract-emit-CNYyzJwF.mjs +0 -195
- package/dist/contract-emit-CNYyzJwF.mjs.map +0 -1
- package/dist/contract-emit-CQfj7xJn.mjs +0 -122
- package/dist/contract-emit-CQfj7xJn.mjs.map +0 -1
- package/dist/contract-emit-fhNwwhkQ.mjs +0 -4
- package/dist/init-CQfo_4Ro.mjs +0 -430
- package/dist/init-CQfo_4Ro.mjs.map +0 -1
- package/dist/migration-status-B0HLF7So.mjs.map +0 -1
- package/dist/migrations-B0dOQlk0.mjs.map +0 -1
- package/dist/result-handler-CIyu0Pdt.mjs.map +0 -1
- package/dist/terminal-ui-C5k88MmW.mjs.map +0 -1
- package/dist/validate-contract-deps-esa-VQ0h.mjs +0 -37
- package/dist/validate-contract-deps-esa-VQ0h.mjs.map +0 -1
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { verifyMigrationBundle } from '@prisma-next/migration-tools/attestation';
|
|
2
1
|
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
3
|
-
import {
|
|
2
|
+
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
3
|
+
import { findPathWithDecision } from '@prisma-next/migration-tools/migration-graph';
|
|
4
|
+
import type { MigrationPackage } from '@prisma-next/migration-tools/package';
|
|
4
5
|
import { readRefs, resolveRef } from '@prisma-next/migration-tools/refs';
|
|
5
|
-
import type { MigrationBundle } from '@prisma-next/migration-tools/types';
|
|
6
|
-
import { MigrationToolsError } from '@prisma-next/migration-tools/types';
|
|
7
6
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
8
7
|
import { Command } from 'commander';
|
|
9
8
|
|
|
@@ -18,11 +17,11 @@ import {
|
|
|
18
17
|
errorRuntime,
|
|
19
18
|
errorTargetMigrationNotSupported,
|
|
20
19
|
errorUnexpected,
|
|
20
|
+
mapMigrationToolsError,
|
|
21
21
|
} from '../utils/cli-errors';
|
|
22
22
|
import {
|
|
23
23
|
addGlobalOptions,
|
|
24
|
-
|
|
25
|
-
type MigrationBundleSet,
|
|
24
|
+
loadMigrationPackages,
|
|
26
25
|
maskConnectionUrl,
|
|
27
26
|
readContractEnvelope,
|
|
28
27
|
resolveMigrationPaths,
|
|
@@ -64,7 +63,7 @@ export interface MigrationApplyResult {
|
|
|
64
63
|
readonly refName?: string;
|
|
65
64
|
readonly selectedPath: readonly {
|
|
66
65
|
readonly dirName: string;
|
|
67
|
-
readonly
|
|
66
|
+
readonly migrationHash: string;
|
|
68
67
|
readonly from: string;
|
|
69
68
|
readonly to: string;
|
|
70
69
|
}[];
|
|
@@ -74,19 +73,6 @@ export interface MigrationApplyResult {
|
|
|
74
73
|
};
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
function mapMigrationToolsError(error: unknown): CliStructuredErrorType {
|
|
78
|
-
if (MigrationToolsError.is(error)) {
|
|
79
|
-
return errorRuntime(error.message, {
|
|
80
|
-
why: error.why,
|
|
81
|
-
fix: error.fix,
|
|
82
|
-
meta: { code: error.code, ...(error.details ?? {}) },
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
return errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
86
|
-
why: `Unexpected error during migration apply: ${error instanceof Error ? error.message : String(error)}`,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
76
|
function mapApplyFailure(failure: MigrationApplyFailure): CliStructuredErrorType {
|
|
91
77
|
return errorRuntime(failure.summary, {
|
|
92
78
|
why: failure.why ?? 'Migration runner failed',
|
|
@@ -95,12 +81,12 @@ function mapApplyFailure(failure: MigrationApplyFailure): CliStructuredErrorType
|
|
|
95
81
|
});
|
|
96
82
|
}
|
|
97
83
|
|
|
98
|
-
function packageToStep(pkg:
|
|
84
|
+
function packageToStep(pkg: MigrationPackage): MigrationApplyStep {
|
|
99
85
|
return {
|
|
100
86
|
dirName: pkg.dirName,
|
|
101
|
-
from: pkg.
|
|
102
|
-
to: pkg.
|
|
103
|
-
toContract: pkg.
|
|
87
|
+
from: pkg.metadata.from,
|
|
88
|
+
to: pkg.metadata.to,
|
|
89
|
+
toContract: pkg.metadata.toContract,
|
|
104
90
|
operations: pkg.ops,
|
|
105
91
|
};
|
|
106
92
|
}
|
|
@@ -112,7 +98,7 @@ async function executeMigrationApplyCommand(
|
|
|
112
98
|
startTime: number,
|
|
113
99
|
): Promise<Result<MigrationApplyResult, CliStructuredErrorType>> {
|
|
114
100
|
const config = await loadConfig(options.config);
|
|
115
|
-
const { configPath, migrationsDir, migrationsRelative,
|
|
101
|
+
const { configPath, migrationsDir, migrationsRelative, refsDir } = resolveMigrationPaths(
|
|
116
102
|
options.config,
|
|
117
103
|
config,
|
|
118
104
|
);
|
|
@@ -149,8 +135,8 @@ async function executeMigrationApplyCommand(
|
|
|
149
135
|
if (options.ref) {
|
|
150
136
|
refName = options.ref;
|
|
151
137
|
try {
|
|
152
|
-
const refs = await readRefs(
|
|
153
|
-
destinationHash = resolveRef(refs, refName);
|
|
138
|
+
const refs = await readRefs(refsDir);
|
|
139
|
+
destinationHash = resolveRef(refs, refName).hash;
|
|
154
140
|
} catch (error) {
|
|
155
141
|
if (MigrationToolsError.is(error)) {
|
|
156
142
|
return notOk(mapMigrationToolsError(error));
|
|
@@ -196,9 +182,9 @@ async function executeMigrationApplyCommand(
|
|
|
196
182
|
}
|
|
197
183
|
|
|
198
184
|
// Read migrations and build migration chain model (offline — no DB needed)
|
|
199
|
-
let migrations:
|
|
185
|
+
let migrations: Awaited<ReturnType<typeof loadMigrationPackages>>;
|
|
200
186
|
try {
|
|
201
|
-
migrations = await
|
|
187
|
+
migrations = await loadMigrationPackages(migrationsDir);
|
|
202
188
|
} catch (error) {
|
|
203
189
|
if (MigrationToolsError.is(error)) {
|
|
204
190
|
return notOk(mapMigrationToolsError(error));
|
|
@@ -206,27 +192,6 @@ async function executeMigrationApplyCommand(
|
|
|
206
192
|
throw error;
|
|
207
193
|
}
|
|
208
194
|
|
|
209
|
-
// Defense in depth: re-hash every bundle and confirm the recorded
|
|
210
|
-
// `migrationId` matches the on-disk `(manifest, ops)`. Catches FS
|
|
211
|
-
// corruption, partial writes, and post-emit hand edits before we
|
|
212
|
-
// start touching the database.
|
|
213
|
-
for (const bundle of migrations.bundles) {
|
|
214
|
-
const verified = verifyMigrationBundle(bundle);
|
|
215
|
-
if (!verified.ok) {
|
|
216
|
-
return notOk(
|
|
217
|
-
errorRuntime(`Migration package is corrupt: ${bundle.dirName}`, {
|
|
218
|
-
why: `Stored migrationId "${verified.storedMigrationId}" does not match the recomputed hash "${verified.computedMigrationId}" for ${migrationsRelative}/${bundle.dirName}. The migration.json or ops.json has been edited or partially written since emit.`,
|
|
219
|
-
fix: `Re-emit the package by running \`node "${migrationsRelative}/${bundle.dirName}/migration.ts"\`, or restore the directory from version control.`,
|
|
220
|
-
meta: {
|
|
221
|
-
dirName: bundle.dirName,
|
|
222
|
-
storedMigrationId: verified.storedMigrationId,
|
|
223
|
-
computedMigrationId: verified.computedMigrationId,
|
|
224
|
-
},
|
|
225
|
-
}),
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
195
|
const client = createControlClient({
|
|
231
196
|
family: config.family,
|
|
232
197
|
target: config.target,
|
|
@@ -12,31 +12,36 @@ import { readFileSync } from 'node:fs';
|
|
|
12
12
|
import type { Contract } from '@prisma-next/contract/types';
|
|
13
13
|
import { getEmittedArtifactPaths } from '@prisma-next/emitter';
|
|
14
14
|
import { createControlStack } from '@prisma-next/framework-components/control';
|
|
15
|
-
import { computeMigrationId } from '@prisma-next/migration-tools/attestation';
|
|
16
15
|
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
17
|
-
import {
|
|
16
|
+
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
17
|
+
import { computeMigrationHash } from '@prisma-next/migration-tools/hash';
|
|
18
18
|
import {
|
|
19
19
|
copyFilesWithRename,
|
|
20
20
|
formatMigrationDirName,
|
|
21
21
|
readMigrationsDir,
|
|
22
22
|
writeMigrationPackage,
|
|
23
23
|
} from '@prisma-next/migration-tools/io';
|
|
24
|
+
import type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';
|
|
25
|
+
import {
|
|
26
|
+
findLatestMigration,
|
|
27
|
+
reconstructGraph,
|
|
28
|
+
} from '@prisma-next/migration-tools/migration-graph';
|
|
24
29
|
import { writeMigrationTs } from '@prisma-next/migration-tools/migration-ts';
|
|
25
|
-
import type { MigrationManifest } from '@prisma-next/migration-tools/types';
|
|
26
|
-
import { MigrationToolsError } from '@prisma-next/migration-tools/types';
|
|
27
30
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
28
31
|
import { Command } from 'commander';
|
|
29
|
-
import { join, relative
|
|
32
|
+
import { join, relative } from 'pathe';
|
|
30
33
|
import { loadConfig } from '../config-loader';
|
|
31
34
|
import {
|
|
32
35
|
CliStructuredError,
|
|
33
36
|
errorRuntime,
|
|
34
37
|
errorTargetMigrationNotSupported,
|
|
35
38
|
errorUnexpected,
|
|
39
|
+
mapMigrationToolsError,
|
|
36
40
|
} from '../utils/cli-errors';
|
|
37
41
|
import {
|
|
38
42
|
addGlobalOptions,
|
|
39
43
|
getTargetMigrations,
|
|
44
|
+
resolveContractPath,
|
|
40
45
|
resolveMigrationPaths,
|
|
41
46
|
setCommandDescriptions,
|
|
42
47
|
setCommandExamples,
|
|
@@ -68,11 +73,7 @@ async function executeMigrationNewCommand(
|
|
|
68
73
|
const config = await loadConfig(options.config);
|
|
69
74
|
const { migrationsDir, migrationsRelative } = resolveMigrationPaths(options.config, config);
|
|
70
75
|
|
|
71
|
-
const
|
|
72
|
-
const contractPathAbsolute = resolve(
|
|
73
|
-
options.config ? resolve(options.config, '..') : process.cwd(),
|
|
74
|
-
contractPath,
|
|
75
|
-
);
|
|
76
|
+
const contractPathAbsolute = resolveContractPath(config);
|
|
76
77
|
|
|
77
78
|
let contractJsonContent: string;
|
|
78
79
|
try {
|
|
@@ -126,7 +127,7 @@ async function executeMigrationNewCommand(
|
|
|
126
127
|
const graph = reconstructGraph(packages);
|
|
127
128
|
|
|
128
129
|
if (options.from) {
|
|
129
|
-
const match = packages.find((p) => p.
|
|
130
|
+
const match = packages.find((p) => p.metadata.to.startsWith(options.from!));
|
|
130
131
|
if (!match) {
|
|
131
132
|
return notOk(
|
|
132
133
|
errorRuntime('Starting contract not found', {
|
|
@@ -135,18 +136,18 @@ async function executeMigrationNewCommand(
|
|
|
135
136
|
}),
|
|
136
137
|
);
|
|
137
138
|
}
|
|
138
|
-
fromHash = match.
|
|
139
|
-
fromContract = match.
|
|
139
|
+
fromHash = match.metadata.to;
|
|
140
|
+
fromContract = match.metadata.toContract;
|
|
140
141
|
fromContractSourceDir = match.dirPath;
|
|
141
142
|
} else {
|
|
142
143
|
const latestMigration = findLatestMigration(graph);
|
|
143
144
|
if (latestMigration) {
|
|
144
145
|
fromHash = latestMigration.to;
|
|
145
146
|
const leafPkg = packages.find(
|
|
146
|
-
(p) => p.
|
|
147
|
+
(p) => p.metadata.migrationHash === latestMigration.migrationHash,
|
|
147
148
|
);
|
|
148
149
|
if (leafPkg) {
|
|
149
|
-
fromContract = leafPkg.
|
|
150
|
+
fromContract = leafPkg.metadata.toContract;
|
|
150
151
|
fromContractSourceDir = leafPkg.dirPath;
|
|
151
152
|
}
|
|
152
153
|
}
|
|
@@ -154,13 +155,7 @@ async function executeMigrationNewCommand(
|
|
|
154
155
|
}
|
|
155
156
|
} catch (error) {
|
|
156
157
|
if (MigrationToolsError.is(error)) {
|
|
157
|
-
return notOk(
|
|
158
|
-
errorRuntime(error.message, {
|
|
159
|
-
why: error.why,
|
|
160
|
-
fix: error.fix,
|
|
161
|
-
meta: { code: error.code },
|
|
162
|
-
}),
|
|
163
|
-
);
|
|
158
|
+
return notOk(mapMigrationToolsError(error));
|
|
164
159
|
}
|
|
165
160
|
throw error;
|
|
166
161
|
}
|
|
@@ -181,9 +176,9 @@ async function executeMigrationNewCommand(
|
|
|
181
176
|
|
|
182
177
|
// `migration new` scaffolds an empty `migration.ts` for the user to
|
|
183
178
|
// fill, so we attest over `ops: []`. Re-running self-emit after the
|
|
184
|
-
// user adds operations will produce a different `
|
|
179
|
+
// user adds operations will produce a different `migrationHash` (over
|
|
185
180
|
// the real ops). This is intentional — there is no on-disk draft.
|
|
186
|
-
const
|
|
181
|
+
const baseMetadata: Omit<MigrationMetadata, 'migrationHash'> = {
|
|
187
182
|
from: fromHash,
|
|
188
183
|
to: toStorageHash,
|
|
189
184
|
kind: 'regular',
|
|
@@ -195,11 +190,12 @@ async function executeMigrationNewCommand(
|
|
|
195
190
|
plannerVersion: '1.0.0',
|
|
196
191
|
},
|
|
197
192
|
labels: [],
|
|
193
|
+
providedInvariants: [],
|
|
198
194
|
createdAt: timestamp.toISOString(),
|
|
199
195
|
};
|
|
200
|
-
const
|
|
201
|
-
...
|
|
202
|
-
|
|
196
|
+
const metadata: MigrationMetadata = {
|
|
197
|
+
...baseMetadata,
|
|
198
|
+
migrationHash: computeMigrationHash(baseMetadata, []),
|
|
203
199
|
};
|
|
204
200
|
|
|
205
201
|
const migrations = getTargetMigrations(config.target);
|
|
@@ -218,7 +214,7 @@ async function executeMigrationNewCommand(
|
|
|
218
214
|
...(config.extensionPacks ?? []),
|
|
219
215
|
]);
|
|
220
216
|
|
|
221
|
-
await writeMigrationPackage(packageDir,
|
|
217
|
+
await writeMigrationPackage(packageDir, metadata, []);
|
|
222
218
|
const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);
|
|
223
219
|
await copyFilesWithRename(packageDir, [
|
|
224
220
|
{ sourcePath: destinationArtifacts.jsonPath, destName: 'end-contract.json' },
|
|
@@ -5,16 +5,18 @@ import {
|
|
|
5
5
|
createControlStack,
|
|
6
6
|
type MigrationPlanOperation,
|
|
7
7
|
} from '@prisma-next/framework-components/control';
|
|
8
|
-
import { computeMigrationId } from '@prisma-next/migration-tools/attestation';
|
|
9
8
|
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
10
|
-
import {
|
|
9
|
+
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
10
|
+
import { computeMigrationHash } from '@prisma-next/migration-tools/hash';
|
|
11
|
+
import { deriveProvidedInvariants } from '@prisma-next/migration-tools/invariants';
|
|
11
12
|
import {
|
|
12
13
|
copyFilesWithRename,
|
|
13
14
|
formatMigrationDirName,
|
|
14
15
|
writeMigrationPackage,
|
|
15
16
|
} from '@prisma-next/migration-tools/io';
|
|
17
|
+
import type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';
|
|
18
|
+
import { findLatestMigration } from '@prisma-next/migration-tools/migration-graph';
|
|
16
19
|
import { writeMigrationTs } from '@prisma-next/migration-tools/migration-ts';
|
|
17
|
-
import { type MigrationManifest, MigrationToolsError } from '@prisma-next/migration-tools/types';
|
|
18
20
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
19
21
|
import { Command } from 'commander';
|
|
20
22
|
import { join, relative } from 'pathe';
|
|
@@ -29,11 +31,12 @@ import {
|
|
|
29
31
|
errorRuntime,
|
|
30
32
|
errorTargetMigrationNotSupported,
|
|
31
33
|
errorUnexpected,
|
|
34
|
+
mapMigrationToolsError,
|
|
32
35
|
} from '../utils/cli-errors';
|
|
33
36
|
import {
|
|
34
37
|
addGlobalOptions,
|
|
35
38
|
getTargetMigrations,
|
|
36
|
-
|
|
39
|
+
loadMigrationPackages,
|
|
37
40
|
resolveContractPath,
|
|
38
41
|
resolveMigrationPaths,
|
|
39
42
|
setCommandDescriptions,
|
|
@@ -76,22 +79,6 @@ export interface MigrationPlanResult {
|
|
|
76
79
|
};
|
|
77
80
|
}
|
|
78
81
|
|
|
79
|
-
function mapMigrationToolsError(error: unknown): CliStructuredError {
|
|
80
|
-
if (CliStructuredError.is(error)) {
|
|
81
|
-
return error;
|
|
82
|
-
}
|
|
83
|
-
if (MigrationToolsError.is(error)) {
|
|
84
|
-
return errorRuntime(error.message, {
|
|
85
|
-
why: error.why,
|
|
86
|
-
fix: error.fix,
|
|
87
|
-
meta: { code: error.code, ...(error.details ?? {}) },
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
return errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
91
|
-
why: `Unexpected error during migration plan: ${error instanceof Error ? error.message : String(error)}`,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
82
|
async function executeMigrationPlanCommand(
|
|
96
83
|
options: MigrationPlanOptions,
|
|
97
84
|
flags: GlobalFlags,
|
|
@@ -177,7 +164,7 @@ async function executeMigrationPlanCommand(
|
|
|
177
164
|
let fromContractSourceDir: string | null = null;
|
|
178
165
|
|
|
179
166
|
try {
|
|
180
|
-
const { bundles, graph } = await
|
|
167
|
+
const { bundles, graph } = await loadMigrationPackages(migrationsDir);
|
|
181
168
|
|
|
182
169
|
if (options.from) {
|
|
183
170
|
const resolved = resolveBundleByPrefix(bundles, options.from);
|
|
@@ -195,16 +182,18 @@ async function executeMigrationPlanCommand(
|
|
|
195
182
|
}),
|
|
196
183
|
);
|
|
197
184
|
}
|
|
198
|
-
fromHash = resolved.value.
|
|
199
|
-
fromContract = resolved.value.
|
|
185
|
+
fromHash = resolved.value.metadata.to;
|
|
186
|
+
fromContract = resolved.value.metadata.toContract;
|
|
200
187
|
fromContractSourceDir = resolved.value.dirPath;
|
|
201
188
|
} else {
|
|
202
189
|
const latestMigration = findLatestMigration(graph);
|
|
203
190
|
if (latestMigration) {
|
|
204
191
|
fromHash = latestMigration.to;
|
|
205
|
-
const leafPkg = bundles.find(
|
|
192
|
+
const leafPkg = bundles.find(
|
|
193
|
+
(p) => p.metadata.migrationHash === latestMigration.migrationHash,
|
|
194
|
+
);
|
|
206
195
|
if (leafPkg) {
|
|
207
|
-
fromContract = leafPkg.
|
|
196
|
+
fromContract = leafPkg.metadata.toContract;
|
|
208
197
|
fromContractSourceDir = leafPkg.dirPath;
|
|
209
198
|
}
|
|
210
199
|
}
|
|
@@ -213,7 +202,16 @@ async function executeMigrationPlanCommand(
|
|
|
213
202
|
if (MigrationToolsError.is(error)) {
|
|
214
203
|
return notOk(mapMigrationToolsError(error));
|
|
215
204
|
}
|
|
216
|
-
|
|
205
|
+
// Wrap unexpected (non-MigrationToolsError) failures from the migration
|
|
206
|
+
// load phase in a structured CLI envelope. Letting them throw would
|
|
207
|
+
// bypass `handleResult()` and crash the command — see CLI structured-
|
|
208
|
+
// errors guideline (CliStructuredError + Result pattern).
|
|
209
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
210
|
+
return notOk(
|
|
211
|
+
errorUnexpected(message, {
|
|
212
|
+
why: `Unexpected error while loading migrations: ${message}`,
|
|
213
|
+
}),
|
|
214
|
+
);
|
|
217
215
|
}
|
|
218
216
|
|
|
219
217
|
// Check for no-op (same hash means no changes)
|
|
@@ -251,7 +249,7 @@ async function executeMigrationPlanCommand(
|
|
|
251
249
|
const dirName = formatMigrationDirName(timestamp, slug);
|
|
252
250
|
const packageDir = join(migrationsDir, dirName);
|
|
253
251
|
|
|
254
|
-
const
|
|
252
|
+
const baseMetadata: Omit<MigrationMetadata, 'migrationHash' | 'providedInvariants'> = {
|
|
255
253
|
from: fromHash,
|
|
256
254
|
to: toStorageHash,
|
|
257
255
|
kind: 'regular',
|
|
@@ -320,18 +318,22 @@ async function executeMigrationPlanCommand(
|
|
|
320
318
|
|
|
321
319
|
const migrationTsContent = plannerResult.plan.renderTypeScript();
|
|
322
320
|
|
|
323
|
-
// Always-attest: compute
|
|
324
|
-
// placeholders blocked lowering, ops is `[]` and the
|
|
325
|
-
// the empty list — re-emitting after the user fills the placeholder
|
|
326
|
-
// produces a different
|
|
321
|
+
// Always-attest: compute migrationHash over (metadata, ops). When
|
|
322
|
+
// placeholders blocked lowering, ops is `[]` and the hash is computed
|
|
323
|
+
// over the empty list — re-emitting after the user fills the placeholder
|
|
324
|
+
// produces a different hash (over the real ops). This is intentional;
|
|
327
325
|
// there is no on-disk "draft" state.
|
|
328
326
|
const opsForWrite = hasPlaceholders ? [] : plannedOps;
|
|
329
|
-
const
|
|
330
|
-
...
|
|
331
|
-
|
|
327
|
+
const metadataWithInvariants: Omit<MigrationMetadata, 'migrationHash'> = {
|
|
328
|
+
...baseMetadata,
|
|
329
|
+
providedInvariants: deriveProvidedInvariants(opsForWrite),
|
|
330
|
+
};
|
|
331
|
+
const metadata: MigrationMetadata = {
|
|
332
|
+
...metadataWithInvariants,
|
|
333
|
+
migrationHash: computeMigrationHash(metadataWithInvariants, opsForWrite),
|
|
332
334
|
};
|
|
333
335
|
|
|
334
|
-
await writeMigrationPackage(packageDir,
|
|
336
|
+
await writeMigrationPackage(packageDir, metadata, opsForWrite);
|
|
335
337
|
const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);
|
|
336
338
|
await copyFilesWithRename(packageDir, [
|
|
337
339
|
{ sourcePath: destinationArtifacts.jsonPath, destName: 'end-contract.json' },
|
|
@@ -382,7 +384,18 @@ async function executeMigrationPlanCommand(
|
|
|
382
384
|
};
|
|
383
385
|
return ok(result);
|
|
384
386
|
} catch (error) {
|
|
385
|
-
|
|
387
|
+
if (CliStructuredError.is(error)) {
|
|
388
|
+
return notOk(error);
|
|
389
|
+
}
|
|
390
|
+
if (MigrationToolsError.is(error)) {
|
|
391
|
+
return notOk(mapMigrationToolsError(error));
|
|
392
|
+
}
|
|
393
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
394
|
+
return notOk(
|
|
395
|
+
errorUnexpected(message, {
|
|
396
|
+
why: `Unexpected error during migration plan: ${message}`,
|
|
397
|
+
}),
|
|
398
|
+
);
|
|
386
399
|
}
|
|
387
400
|
}
|
|
388
401
|
|
|
@@ -489,7 +502,7 @@ function formatMigrationPlanOutput(result: MigrationPlanResult, flags: GlobalFla
|
|
|
489
502
|
|
|
490
503
|
lines.push('');
|
|
491
504
|
lines.push(
|
|
492
|
-
`Next: ${green_(
|
|
505
|
+
`Next: review ${green_(result.dir ?? '<dir>')} if needed, then run ${green_('prisma-next migration apply')}.`,
|
|
493
506
|
);
|
|
494
507
|
|
|
495
508
|
if (result.sql && result.sql.length > 0) {
|
|
@@ -517,24 +530,27 @@ export type PrefixResolutionFailure =
|
|
|
517
530
|
| { reason: 'not-found' };
|
|
518
531
|
|
|
519
532
|
/**
|
|
520
|
-
* Resolve a migration
|
|
533
|
+
* Resolve a migration package by **target contract hash** (`metadata.to`)
|
|
534
|
+
* using exact match or prefix match.
|
|
521
535
|
*
|
|
536
|
+
* Note: matches `metadata.to` (the contract hash this migration produces),
|
|
537
|
+
* not `metadata.migrationHash` (the package's content-addressed identity).
|
|
522
538
|
* Tries exact match first, then prefix match (auto-prepending `sha256:` when
|
|
523
|
-
* the needle omits the scheme). Returns the matched
|
|
539
|
+
* the needle omits the scheme). Returns the matched package on success, or a
|
|
524
540
|
* discriminated failure indicating whether the prefix was ambiguous or simply
|
|
525
541
|
* not found.
|
|
526
542
|
*
|
|
527
543
|
* @internal Exported for testing only.
|
|
528
544
|
*/
|
|
529
|
-
export function resolveBundleByPrefix<T extends {
|
|
545
|
+
export function resolveBundleByPrefix<T extends { metadata: { to: string } }>(
|
|
530
546
|
bundles: readonly T[],
|
|
531
547
|
needle: string,
|
|
532
548
|
): Result<T, PrefixResolutionFailure> {
|
|
533
|
-
const exact = bundles.find((p) => p.
|
|
549
|
+
const exact = bundles.find((p) => p.metadata.to === needle);
|
|
534
550
|
if (exact) return ok(exact);
|
|
535
551
|
|
|
536
552
|
const prefixWithScheme = needle.startsWith('sha256:') ? needle : `sha256:${needle}`;
|
|
537
|
-
const candidates = bundles.filter((p) => p.
|
|
553
|
+
const candidates = bundles.filter((p) => p.metadata.to.startsWith(prefixWithScheme));
|
|
538
554
|
|
|
539
555
|
if (candidates.length === 1) return ok(candidates[0]!);
|
|
540
556
|
if (candidates.length > 1) return notOk({ reason: 'ambiguous', count: candidates.length });
|