@prisma-next/cli 0.16.0-dev.3 → 0.16.0-dev.31
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 +44 -45
- package/dist/cli.mjs +13 -13
- package/dist/{client-KuBQftxz.mjs → client-B1UjQc_x.mjs} +50 -58
- package/dist/client-B1UjQc_x.mjs.map +1 -0
- package/dist/{command-helpers-Cpq44aVa.mjs → command-helpers-D-jBl0Qt.mjs} +16 -8
- package/dist/command-helpers-D-jBl0Qt.mjs.map +1 -0
- package/dist/commands/contract-emit.mjs +1 -1
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +8 -6
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.mjs +3 -3
- package/dist/commands/db-sign.d.mts.map +1 -1
- package/dist/commands/db-sign.mjs +11 -12
- package/dist/commands/db-sign.mjs.map +1 -1
- package/dist/commands/db-update.d.mts.map +1 -1
- package/dist/commands/db-update.mjs +14 -15
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +1 -1
- package/dist/commands/migrate.d.mts.map +1 -1
- package/dist/commands/migrate.mjs +13 -13
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-check.d.mts +4 -2
- package/dist/commands/migration-check.d.mts.map +1 -1
- package/dist/commands/migration-check.mjs +1 -1
- package/dist/commands/migration-graph.d.mts +1 -1
- package/dist/commands/migration-graph.mjs +4 -4
- package/dist/commands/migration-list.d.mts +1 -1
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +1 -1
- package/dist/commands/migration-log.d.mts +1 -1
- package/dist/commands/migration-log.mjs +1 -1
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +16 -40
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +3 -4
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-show.mjs +5 -5
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +1 -1
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +1 -1
- package/dist/commands/ref.d.mts +1 -1
- package/dist/commands/ref.d.mts.map +1 -1
- package/dist/commands/ref.mjs +13 -18
- package/dist/commands/ref.mjs.map +1 -1
- package/dist/commands/telemetry/index.mjs +1 -1
- package/dist/contract-at-errors-DvZ_DzJK.mjs +36 -0
- package/dist/contract-at-errors-DvZ_DzJK.mjs.map +1 -0
- package/dist/{contract-emit-CgnXSANN.mjs → contract-emit-BT46Mu8Q.mjs} +3 -3
- package/dist/{contract-emit-CgnXSANN.mjs.map → contract-emit-BT46Mu8Q.mjs.map} +1 -1
- package/dist/{contract-emit-cJQBlvGb.mjs → contract-emit-hVdlaF2U.mjs} +6 -7
- package/dist/{contract-emit-cJQBlvGb.mjs.map → contract-emit-hVdlaF2U.mjs.map} +1 -1
- package/dist/{contract-enrichment-gn9sWbPw.mjs → contract-enrichment-JUhLv-BX.mjs} +8 -8
- package/dist/{contract-enrichment-gn9sWbPw.mjs.map → contract-enrichment-JUhLv-BX.mjs.map} +1 -1
- package/dist/{contract-infer-Cg8evbeg.mjs → contract-infer-DpqTQECL.mjs} +3 -3
- package/dist/{contract-infer-Cg8evbeg.mjs.map → contract-infer-DpqTQECL.mjs.map} +1 -1
- package/dist/{contract-space-aggregate-loader-D_3VeHVb.mjs → contract-space-aggregate-loader-CNd-ZB6C.mjs} +14 -14
- package/dist/contract-space-aggregate-loader-CNd-ZB6C.mjs.map +1 -0
- package/dist/{db-verify-BEclUzki.mjs → db-verify-FEN6k1m7.mjs} +10 -8
- package/dist/db-verify-FEN6k1m7.mjs.map +1 -0
- package/dist/exports/control-api.d.mts +4 -4
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +3 -3
- package/dist/exports/index.d.mts +2 -1
- package/dist/exports/index.d.mts.map +1 -1
- package/dist/exports/index.mjs +61 -21
- package/dist/exports/index.mjs.map +1 -1
- package/dist/exports/init-output.d.mts.map +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{format-DAUAZqPt.mjs → format-DI5qVr22.mjs} +8 -7
- package/dist/format-DI5qVr22.mjs.map +1 -0
- package/dist/{framework-components-CnbUbiJw.mjs → framework-components-C4XACYNB.mjs} +3 -3
- package/dist/framework-components-C4XACYNB.mjs.map +1 -0
- package/dist/{init-C3JhkHg-.mjs → init-ClrRkbwd.mjs} +77 -67
- package/dist/init-ClrRkbwd.mjs.map +1 -0
- package/dist/{inspect-live-schema-CX7EA5C3.mjs → inspect-live-schema-C0Rgdtw_.mjs} +4 -4
- package/dist/{inspect-live-schema-CX7EA5C3.mjs.map → inspect-live-schema-C0Rgdtw_.mjs.map} +1 -1
- package/dist/{migration-check-MHT3w7_v.mjs → migration-check-zEkHpPDT.mjs} +58 -30
- package/dist/migration-check-zEkHpPDT.mjs.map +1 -0
- package/dist/{migration-command-scaffold-CRyYrHZ9.mjs → migration-command-scaffold-CkNsp9g1.mjs} +4 -4
- package/dist/{migration-command-scaffold-CRyYrHZ9.mjs.map → migration-command-scaffold-CkNsp9g1.mjs.map} +1 -1
- package/dist/{migration-graph-command-render-B83xrnNy.mjs → migration-graph-command-render-DEwFpx-w.mjs} +5 -4
- package/dist/migration-graph-command-render-DEwFpx-w.mjs.map +1 -0
- package/dist/{migration-list-BAKujUjh.mjs → migration-list-BkafFVXS.mjs} +6 -6
- package/dist/migration-list-BkafFVXS.mjs.map +1 -0
- package/dist/{migration-log-C_xam2HZ.mjs → migration-log-BxhAtbhA.mjs} +5 -5
- package/dist/{migration-log-C_xam2HZ.mjs.map → migration-log-BxhAtbhA.mjs.map} +1 -1
- package/dist/{migration-path-target-DpPTsRCg.mjs → migration-path-target-B5HkjSTO.mjs} +2 -2
- package/dist/{migration-path-target-DpPTsRCg.mjs.map → migration-path-target-B5HkjSTO.mjs.map} +1 -1
- package/dist/{migration-plan-CreSVtWZ.mjs → migration-plan-Bk2pbPQH.mjs} +64 -74
- package/dist/migration-plan-Bk2pbPQH.mjs.map +1 -0
- package/dist/{migration-status-YgEvXf-u.mjs → migration-status-BzlARseq.mjs} +13 -12
- package/dist/migration-status-BzlARseq.mjs.map +1 -0
- package/dist/{output-mEQ74_nd.mjs → output-DWNYqZGc.mjs} +5 -6
- package/dist/output-DWNYqZGc.mjs.map +1 -0
- package/dist/{ref-advancement-BkXlikCA.mjs → ref-advancement-BCz7X7qJ.mjs} +13 -6
- package/dist/ref-advancement-BCz7X7qJ.mjs.map +1 -0
- package/dist/{telemetry-DVr6RFnq.mjs → telemetry-BbniBUbP.mjs} +2 -2
- package/dist/{telemetry-DVr6RFnq.mjs.map → telemetry-BbniBUbP.mjs.map} +1 -1
- package/dist/{types-D4r_HI9-.d.mts → types-DgXprE3w.d.mts} +3 -3
- package/dist/types-DgXprE3w.d.mts.map +1 -0
- package/dist/{verify-CgDVZjhc.mjs → verify-BSaNeaSL.mjs} +2 -2
- package/dist/{verify-CgDVZjhc.mjs.map → verify-BSaNeaSL.mjs.map} +1 -1
- package/package.json +21 -21
- package/src/commands/db-init.ts +5 -3
- package/src/commands/db-sign.ts +9 -7
- package/src/commands/db-update.ts +18 -9
- package/src/commands/db-verify.ts +4 -4
- package/src/commands/format.ts +1 -1
- package/src/commands/init/hygiene-gitattributes.ts +37 -21
- package/src/commands/init/init.ts +6 -6
- package/src/commands/init/output.ts +4 -5
- package/src/commands/init/probe-db.ts +9 -2
- package/src/commands/init/reinit-cleanup.ts +16 -17
- package/src/commands/init/templates/render.ts +2 -1
- package/src/commands/inspect-live-schema.ts +1 -1
- package/src/commands/migrate.ts +6 -5
- package/src/commands/migration-check.ts +72 -34
- package/src/commands/migration-list.ts +1 -5
- package/src/commands/migration-log.ts +1 -1
- package/src/commands/migration-new.ts +17 -41
- package/src/commands/migration-plan.ts +64 -88
- package/src/commands/migration-show.ts +2 -2
- package/src/commands/migration-status.ts +6 -7
- package/src/commands/ref.ts +19 -14
- package/src/control-api/client.ts +43 -25
- package/src/control-api/contract-enrichment.ts +7 -7
- package/src/control-api/operations/contract-emit.ts +2 -3
- package/src/control-api/operations/db-init.ts +3 -3
- package/src/control-api/operations/db-run.ts +8 -7
- package/src/control-api/operations/db-update.ts +2 -2
- package/src/control-api/operations/db-verify.ts +4 -4
- package/src/control-api/operations/format.ts +5 -4
- package/src/control-api/operations/migrate.ts +6 -5
- package/src/control-api/operations/run-migration.ts +2 -1
- package/src/control-api/types.ts +1 -1
- package/src/load-ts-contract.ts +81 -16
- package/src/utils/cli-errors.ts +15 -7
- package/src/utils/combine-verify-results.ts +2 -1
- package/src/utils/command-helpers.ts +15 -5
- package/src/utils/contract-at-errors.ts +13 -22
- package/src/utils/contract-space-aggregate-loader.ts +8 -8
- package/src/utils/contract-space-seed-phase.ts +11 -9
- package/src/utils/extension-pack-inputs.ts +8 -8
- package/src/utils/formatters/migration-graph-grid-layout.ts +2 -1
- package/src/utils/formatters/migration-graph-labels.ts +1 -2
- package/src/utils/formatters/migration-list-data-column.ts +1 -2
- package/src/utils/framework-components.ts +9 -9
- package/src/utils/integrity-violation-to-check-failure.ts +3 -3
- package/src/utils/migration-command-scaffold.ts +1 -1
- package/src/utils/plan-resolution.ts +7 -10
- package/src/utils/ref-advancement.ts +22 -3
- package/dist/client-KuBQftxz.mjs.map +0 -1
- package/dist/command-helpers-Cpq44aVa.mjs.map +0 -1
- package/dist/contract-at-errors-D9gEjYtg.mjs +0 -42
- package/dist/contract-at-errors-D9gEjYtg.mjs.map +0 -1
- package/dist/contract-space-aggregate-loader-D_3VeHVb.mjs.map +0 -1
- package/dist/db-verify-BEclUzki.mjs.map +0 -1
- package/dist/format-DAUAZqPt.mjs.map +0 -1
- package/dist/framework-components-CnbUbiJw.mjs.map +0 -1
- package/dist/init-C3JhkHg-.mjs.map +0 -1
- package/dist/migration-check-MHT3w7_v.mjs.map +0 -1
- package/dist/migration-graph-command-render-B83xrnNy.mjs.map +0 -1
- package/dist/migration-list-BAKujUjh.mjs.map +0 -1
- package/dist/migration-plan-CreSVtWZ.mjs.map +0 -1
- package/dist/migration-status-YgEvXf-u.mjs.map +0 -1
- package/dist/output-mEQ74_nd.mjs.map +0 -1
- package/dist/ref-advancement-BkXlikCA.mjs.map +0 -1
- package/dist/types-D4r_HI9-.d.mts.map +0 -1
- package/src/control-api/errors.ts +0 -9
|
@@ -4,41 +4,40 @@ import { join } from 'pathe';
|
|
|
4
4
|
/**
|
|
5
5
|
* Filenames the contract pipeline emits next to the user's schema source
|
|
6
6
|
* (`<schemaDir>/contract.json`, `<schemaDir>/contract.d.ts`, …). Mirrors
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* the schema-dir-relative `ARTIFACT_FILENAMES` in `hygiene-gitattributes.ts`
|
|
8
|
+
* (not that file's migrations-root-anchored store lines, which are outside
|
|
9
|
+
* this command's scope — reinit only ever touches `schemaDir`); kept as a
|
|
10
|
+
* separate constant here because the cleanup contract is target-agnostic
|
|
11
|
+
* and we deliberately do not want a stale artifact from a previous target
|
|
12
|
+
* lingering after a re-init.
|
|
11
13
|
*
|
|
12
|
-
* If a future emit pipeline produces an additional
|
|
13
|
-
* **and** to
|
|
14
|
-
*
|
|
15
|
-
* `init` is willing to delete on
|
|
14
|
+
* If a future emit pipeline produces an additional schema-dir artifact,
|
|
15
|
+
* add it here **and** to `ARTIFACT_FILENAMES` in `hygiene-gitattributes.ts`
|
|
16
|
+
* — the two stay in lockstep so the file `init` advertises as
|
|
17
|
+
* `linguist-generated` is exactly the file `init` is willing to delete on
|
|
18
|
+
* re-init.
|
|
16
19
|
*/
|
|
17
|
-
const
|
|
20
|
+
const ARTIFACT_FILENAMES: readonly string[] = [
|
|
18
21
|
'contract.json',
|
|
19
22
|
'contract.d.ts',
|
|
20
|
-
'end-contract.json',
|
|
21
|
-
'end-contract.d.ts',
|
|
22
|
-
'start-contract.json',
|
|
23
|
-
'start-contract.d.ts',
|
|
24
23
|
'ops.json',
|
|
25
24
|
'migration.json',
|
|
26
25
|
];
|
|
27
26
|
|
|
28
27
|
/**
|
|
29
|
-
* Returns the schema-relative paths of stale contract
|
|
28
|
+
* Returns the schema-relative paths of stale contract artifacts the
|
|
30
29
|
* previous `init` run (or a `contract emit`) left behind in `schemaDir`.
|
|
31
30
|
* Paths are returned relative to `baseDir` so the caller can plumb them
|
|
32
31
|
* into `filesWritten`-style logging without re-deriving the path.
|
|
33
32
|
*
|
|
34
33
|
* Pure function: no filesystem mutation. Used by `runInit`'s precondition
|
|
35
34
|
* phase (FR6.2 / NFR3 atomicity) so a downstream parse failure leaves
|
|
36
|
-
* the
|
|
35
|
+
* the artifacts on disk and the project byte-identical to its pre-init
|
|
37
36
|
* state.
|
|
38
37
|
*/
|
|
39
|
-
export function
|
|
38
|
+
export function findStaleArtifacts(baseDir: string, schemaDir: string): readonly string[] {
|
|
40
39
|
const result: string[] = [];
|
|
41
|
-
for (const filename of
|
|
40
|
+
for (const filename of ARTIFACT_FILENAMES) {
|
|
42
41
|
const rel = join(schemaDir, filename);
|
|
43
42
|
if (existsSync(join(baseDir, rel))) {
|
|
44
43
|
result.push(rel);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { InternalError } from '@prisma-next/utils/internal-error';
|
|
2
3
|
import { join } from 'pathe';
|
|
3
4
|
|
|
4
5
|
export function renderTemplate(
|
|
@@ -12,7 +13,7 @@ export function renderTemplate(
|
|
|
12
13
|
for (const key of variableNames) {
|
|
13
14
|
const value = vars[key];
|
|
14
15
|
if (value === undefined) {
|
|
15
|
-
throw new
|
|
16
|
+
throw new InternalError(`Template variable '${key}' is not defined`);
|
|
16
17
|
}
|
|
17
18
|
result = result.replaceAll(`{{${key}}}`, value);
|
|
18
19
|
}
|
|
@@ -131,7 +131,7 @@ export async function inspectLiveSchema(
|
|
|
131
131
|
target: config.target,
|
|
132
132
|
adapter: config.adapter,
|
|
133
133
|
driver: config.driver,
|
|
134
|
-
|
|
134
|
+
extensions: config.extensions ?? [],
|
|
135
135
|
});
|
|
136
136
|
const onProgress = createProgressAdapter({ ui, flags });
|
|
137
137
|
|
package/src/commands/migrate.ts
CHANGED
|
@@ -330,7 +330,7 @@ async function executeMigrateShowCommand(
|
|
|
330
330
|
target: config.target,
|
|
331
331
|
adapter: config.adapter,
|
|
332
332
|
driver: config.driver!,
|
|
333
|
-
|
|
333
|
+
extensions: config.extensions ?? [],
|
|
334
334
|
});
|
|
335
335
|
try {
|
|
336
336
|
await client.connect(dbConnection);
|
|
@@ -675,7 +675,7 @@ async function executeMigrateCommand(
|
|
|
675
675
|
targetId: config.target.targetId,
|
|
676
676
|
migrationsDir,
|
|
677
677
|
appContract: contractRaw,
|
|
678
|
-
|
|
678
|
+
extensions: config.extensions ?? [],
|
|
679
679
|
deserializeContract: (json) => familyInstance.deserializeContract(json),
|
|
680
680
|
});
|
|
681
681
|
if (!loadedAggregate.ok) {
|
|
@@ -684,7 +684,7 @@ async function executeMigrateCommand(
|
|
|
684
684
|
const aggregate = loadedAggregate.value;
|
|
685
685
|
const integrityFailure = refuseContractSpaceIntegrity(aggregate, {
|
|
686
686
|
declaredExtensions: toDeclaredExtensionsFromRaw(
|
|
687
|
-
(config.
|
|
687
|
+
(config.extensions ?? []) as ReadonlyArray<unknown>,
|
|
688
688
|
),
|
|
689
689
|
checkContracts: true,
|
|
690
690
|
});
|
|
@@ -740,7 +740,7 @@ async function executeMigrateCommand(
|
|
|
740
740
|
target: config.target,
|
|
741
741
|
adapter: config.adapter,
|
|
742
742
|
driver: config.driver,
|
|
743
|
-
|
|
743
|
+
extensions: config.extensions ?? [],
|
|
744
744
|
});
|
|
745
745
|
|
|
746
746
|
try {
|
|
@@ -831,6 +831,7 @@ async function executeMigrateCommand(
|
|
|
831
831
|
: await readContractIR(snapshotContractJson, contractPathAbsolute);
|
|
832
832
|
advancedRef = await executeRefAdvancement(
|
|
833
833
|
refsDir,
|
|
834
|
+
migrationsDir,
|
|
834
835
|
options.advanceRef,
|
|
835
836
|
value.markerHash,
|
|
836
837
|
contractIR,
|
|
@@ -886,7 +887,7 @@ export function createMigrateCommand(): Command {
|
|
|
886
887
|
setCommandExamples(command, [
|
|
887
888
|
'prisma-next migrate --db $DATABASE_URL',
|
|
888
889
|
'prisma-next migrate --to production --db $DATABASE_URL',
|
|
889
|
-
'prisma-next migrate --to
|
|
890
|
+
'prisma-next migrate --to abc123 --db $DATABASE_URL',
|
|
890
891
|
'prisma-next migrate --show --db $DATABASE_URL',
|
|
891
892
|
'prisma-next migrate --show --from @contract --to production',
|
|
892
893
|
]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readdirSync,
|
|
1
|
+
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { loadConfig } from '@prisma-next/config-loader';
|
|
4
4
|
import { createControlStack } from '@prisma-next/framework-components/control';
|
|
@@ -7,6 +7,12 @@ import type {
|
|
|
7
7
|
IntegrityViolation,
|
|
8
8
|
} from '@prisma-next/migration-tools/aggregate';
|
|
9
9
|
import { loadContractSpaceAggregate } from '@prisma-next/migration-tools/aggregate';
|
|
10
|
+
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
11
|
+
import {
|
|
12
|
+
contractSnapshotDir,
|
|
13
|
+
readContractSnapshotJson,
|
|
14
|
+
} from '@prisma-next/migration-tools/contract-snapshot-store';
|
|
15
|
+
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
10
16
|
import type { MigrationGraph } from '@prisma-next/migration-tools/graph';
|
|
11
17
|
import { verifyMigrationHash } from '@prisma-next/migration-tools/hash';
|
|
12
18
|
import type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';
|
|
@@ -18,7 +24,6 @@ import type { Refs } from '@prisma-next/migration-tools/refs';
|
|
|
18
24
|
import {
|
|
19
25
|
isValidSpaceId,
|
|
20
26
|
listContractSpaceDirectories,
|
|
21
|
-
RESERVED_SPACE_SUBDIR_NAMES,
|
|
22
27
|
spaceMigrationDirectory,
|
|
23
28
|
spaceRefsDirectory,
|
|
24
29
|
} from '@prisma-next/migration-tools/spaces';
|
|
@@ -92,32 +97,57 @@ function checkFileExists(
|
|
|
92
97
|
return null;
|
|
93
98
|
}
|
|
94
99
|
|
|
95
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Snapshot-store consistency check for one migration package (D6.5):
|
|
102
|
+
* absent store entry is not an issue (runner independence, ADR 199 — a
|
|
103
|
+
* package dir with only `migration.json` + `ops.json` is legitimate); a
|
|
104
|
+
* present entry whose inner `storage.storageHash` disagrees with
|
|
105
|
+
* `pkg.metadata.to` is `MIGRATION.CHECK_SNAPSHOT_HASH_MISMATCH`; an unparseable store
|
|
106
|
+
* entry (or a malformed `to`) is `MIGRATION.CHECK_SNAPSHOT_UNPARSEABLE`.
|
|
107
|
+
*/
|
|
108
|
+
async function checkSnapshotConsistency(
|
|
96
109
|
spaceId: string,
|
|
97
110
|
pkg: OnDiskMigrationPackage,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
111
|
+
migrationsDir: string,
|
|
112
|
+
): Promise<CheckFailure | null> {
|
|
113
|
+
let snapshotDir: string;
|
|
101
114
|
try {
|
|
102
|
-
|
|
103
|
-
const storage = raw['storage'] as Record<string, unknown> | undefined;
|
|
104
|
-
const snapshotHash = storage?.['storageHash'];
|
|
105
|
-
if (typeof snapshotHash === 'string' && snapshotHash !== pkg.metadata.to) {
|
|
106
|
-
return {
|
|
107
|
-
space: spaceId,
|
|
108
|
-
code: 'MIGRATION.CHECK_SNAPSHOT_HASH_MISMATCH',
|
|
109
|
-
where: migrationPathRelative(pkg.dirPath),
|
|
110
|
-
why: `Migration "${pkg.dirName}" declares to=${pkg.metadata.to} but end-contract.json has storageHash=${snapshotHash}`,
|
|
111
|
-
fix: 'Re-emit the migration package so migration.json and end-contract.json agree.',
|
|
112
|
-
};
|
|
113
|
-
}
|
|
115
|
+
snapshotDir = contractSnapshotDir(migrationsDir, pkg.metadata.to);
|
|
114
116
|
} catch {
|
|
115
117
|
return {
|
|
116
118
|
space: spaceId,
|
|
117
119
|
code: 'MIGRATION.CHECK_SNAPSHOT_UNPARSEABLE',
|
|
118
120
|
where: migrationPathRelative(pkg.dirPath),
|
|
119
|
-
why: `Migration "${pkg.dirName}"
|
|
120
|
-
fix: 'Re-emit the migration package
|
|
121
|
+
why: `Migration "${pkg.dirName}" declares to="${pkg.metadata.to}", which is not a well-formed contract snapshot hash.`,
|
|
122
|
+
fix: 'Re-emit the migration package so migration.json declares a valid 64-hex to-hash.',
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let raw: unknown;
|
|
127
|
+
try {
|
|
128
|
+
raw = await readContractSnapshotJson(migrationsDir, pkg.metadata.to);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
if (MigrationToolsError.is(error) && error.code === 'MIGRATION.CONTRACT_SNAPSHOT_MISSING') {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
space: spaceId,
|
|
135
|
+
code: 'MIGRATION.CHECK_SNAPSHOT_UNPARSEABLE',
|
|
136
|
+
where: migrationPathRelative(pkg.dirPath),
|
|
137
|
+
why: `Migration "${pkg.dirName}" has an unparseable contract snapshot at ${snapshotDir}/contract.json.`,
|
|
138
|
+
fix: 'Restore migrations/snapshots/ from version control, or re-run the command that produced this migration to regenerate its snapshot.',
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const record = raw !== null && typeof raw === 'object' ? (raw as Record<string, unknown>) : {};
|
|
142
|
+
const storage = record['storage'] as Record<string, unknown> | undefined;
|
|
143
|
+
const snapshotHash = storage?.['storageHash'];
|
|
144
|
+
if (typeof snapshotHash === 'string' && snapshotHash !== pkg.metadata.to) {
|
|
145
|
+
return {
|
|
146
|
+
space: spaceId,
|
|
147
|
+
code: 'MIGRATION.CHECK_SNAPSHOT_HASH_MISMATCH',
|
|
148
|
+
where: migrationPathRelative(pkg.dirPath),
|
|
149
|
+
why: `Migration "${pkg.dirName}" declares to=${pkg.metadata.to} but its contract snapshot has storageHash=${snapshotHash}`,
|
|
150
|
+
fix: 'Re-emit the migration package so migration.json and its contract snapshot agree.',
|
|
121
151
|
};
|
|
122
152
|
}
|
|
123
153
|
return null;
|
|
@@ -137,6 +167,8 @@ export interface CheckSpace {
|
|
|
137
167
|
readonly graph: MigrationGraph;
|
|
138
168
|
readonly migrationsDir: string;
|
|
139
169
|
readonly refsDir: string;
|
|
170
|
+
/** Migrations root the contract-snapshot store lives under — shared by every space. */
|
|
171
|
+
readonly projectMigrationsDir: string;
|
|
140
172
|
}
|
|
141
173
|
|
|
142
174
|
/**
|
|
@@ -152,9 +184,7 @@ export async function enumerateCheckSpaces(
|
|
|
152
184
|
projectMigrationsDir: string,
|
|
153
185
|
): Promise<readonly CheckSpace[]> {
|
|
154
186
|
const candidateDirs = await listContractSpaceDirectories(projectMigrationsDir);
|
|
155
|
-
const onDiskSpaceIds = new Set(
|
|
156
|
-
candidateDirs.filter((name) => !RESERVED_SPACE_SUBDIR_NAMES.has(name)).filter(isValidSpaceId),
|
|
157
|
-
);
|
|
187
|
+
const onDiskSpaceIds = new Set(candidateDirs.filter(isValidSpaceId));
|
|
158
188
|
const spaces: CheckSpace[] = [];
|
|
159
189
|
for (const space of aggregate.spaces()) {
|
|
160
190
|
const spaceId = space.spaceId;
|
|
@@ -168,6 +198,7 @@ export async function enumerateCheckSpaces(
|
|
|
168
198
|
graph: space.graph(),
|
|
169
199
|
migrationsDir,
|
|
170
200
|
refsDir: spaceRefsDirectory(migrationsDir),
|
|
201
|
+
projectMigrationsDir,
|
|
171
202
|
});
|
|
172
203
|
}
|
|
173
204
|
return spaces;
|
|
@@ -208,7 +239,7 @@ function checkReachability(space: CheckSpace): readonly CheckFailure[] {
|
|
|
208
239
|
const isReachable =
|
|
209
240
|
pkg.metadata.from === null ||
|
|
210
241
|
allToHashes.has(pkg.metadata.from) ||
|
|
211
|
-
pkg.metadata.from ===
|
|
242
|
+
pkg.metadata.from === EMPTY_CONTRACT_HASH;
|
|
212
243
|
if (!isReachable) {
|
|
213
244
|
failures.push({
|
|
214
245
|
space: space.spaceId,
|
|
@@ -238,12 +269,15 @@ function checkDanglingRefs(space: CheckSpace): readonly CheckFailure[] {
|
|
|
238
269
|
return failures;
|
|
239
270
|
}
|
|
240
271
|
|
|
241
|
-
function checkSpace(space: CheckSpace): readonly CheckFailure[] {
|
|
272
|
+
async function checkSpace(space: CheckSpace): Promise<readonly CheckFailure[]> {
|
|
273
|
+
const snapshotFailures = await Promise.all(
|
|
274
|
+
space.packages.map((pkg) =>
|
|
275
|
+
checkSnapshotConsistency(space.spaceId, pkg, space.projectMigrationsDir),
|
|
276
|
+
),
|
|
277
|
+
);
|
|
242
278
|
return [
|
|
243
279
|
...checkManifestFilesPresent(space),
|
|
244
|
-
...
|
|
245
|
-
.map((pkg) => checkSnapshotConsistency(space.spaceId, pkg))
|
|
246
|
-
.filter((f): f is CheckFailure => f !== null),
|
|
280
|
+
...snapshotFailures.filter((f): f is CheckFailure => f !== null),
|
|
247
281
|
...checkReachability(space),
|
|
248
282
|
...checkDanglingRefs(space),
|
|
249
283
|
];
|
|
@@ -272,9 +306,9 @@ export interface RunMigrationCheckInputs {
|
|
|
272
306
|
* Aggregate-integrity violations (which already span every space) are folded
|
|
273
307
|
* in by the caller, not here.
|
|
274
308
|
*/
|
|
275
|
-
export function runMigrationCheck(
|
|
309
|
+
export async function runMigrationCheck(
|
|
276
310
|
inputs: RunMigrationCheckInputs,
|
|
277
|
-
): Result<MigrationCheckResult, CliStructuredError
|
|
311
|
+
): Promise<Result<MigrationCheckResult, CliStructuredError>> {
|
|
278
312
|
const { spaces, spaceFilter } = inputs;
|
|
279
313
|
|
|
280
314
|
if (spaceFilter !== undefined && !isValidSpaceId(spaceFilter)) {
|
|
@@ -287,7 +321,7 @@ export function runMigrationCheck(
|
|
|
287
321
|
const scopedSpaces =
|
|
288
322
|
spaceFilter !== undefined ? spaces.filter((s) => s.spaceId === spaceFilter) : spaces;
|
|
289
323
|
|
|
290
|
-
const failures = scopedSpaces.
|
|
324
|
+
const failures = (await Promise.all(scopedSpaces.map(checkSpace))).flat();
|
|
291
325
|
if (failures.length === 0) {
|
|
292
326
|
return ok({ ok: true, failures: [], summary: 'All checks passed' });
|
|
293
327
|
}
|
|
@@ -301,7 +335,7 @@ async function loadAggregateIntegrityViolations(
|
|
|
301
335
|
try {
|
|
302
336
|
const contractJsonContent = await readFile(resolveContractPath(config), 'utf-8');
|
|
303
337
|
const familyInstance = config.family.create(createControlStack(config));
|
|
304
|
-
const declaredExtensions = toDeclaredExtensionsFromRaw(config.
|
|
338
|
+
const declaredExtensions = toDeclaredExtensionsFromRaw(config.extensions ?? []);
|
|
305
339
|
|
|
306
340
|
const parsedAppContract: unknown = JSON.parse(contractJsonContent);
|
|
307
341
|
const aggregate = await loadContractSpaceAggregate({
|
|
@@ -365,7 +399,7 @@ async function executeMigrationCheckCommand(
|
|
|
365
399
|
});
|
|
366
400
|
}
|
|
367
401
|
|
|
368
|
-
const checkResult = runMigrationCheck({
|
|
402
|
+
const checkResult = await runMigrationCheck({
|
|
369
403
|
spaces,
|
|
370
404
|
...(options.space !== undefined ? { spaceFilter: options.space } : {}),
|
|
371
405
|
});
|
|
@@ -558,7 +592,11 @@ async function checkSingleTarget(
|
|
|
558
592
|
});
|
|
559
593
|
}
|
|
560
594
|
|
|
561
|
-
const snapshotFailure = checkSnapshotConsistency(
|
|
595
|
+
const snapshotFailure = await checkSnapshotConsistency(
|
|
596
|
+
matchedSpace.spaceId,
|
|
597
|
+
matchedPkg,
|
|
598
|
+
matchedSpace.projectMigrationsDir,
|
|
599
|
+
);
|
|
562
600
|
if (snapshotFailure) failures.push(snapshotFailure);
|
|
563
601
|
|
|
564
602
|
const resolvedSpaceId = matchedSpace.spaceId !== 'app' ? matchedSpace.spaceId : undefined;
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
APP_SPACE_ID,
|
|
10
10
|
isValidSpaceId,
|
|
11
11
|
listContractSpaceDirectories,
|
|
12
|
-
RESERVED_SPACE_SUBDIR_NAMES,
|
|
13
12
|
} from '@prisma-next/migration-tools/spaces';
|
|
14
13
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
15
14
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
@@ -81,10 +80,7 @@ export function listRefsByContractHash(
|
|
|
81
80
|
|
|
82
81
|
async function orderedOnDiskSpaceIds(projectMigrationsDir: string): Promise<readonly string[]> {
|
|
83
82
|
const candidateDirs = await listContractSpaceDirectories(projectMigrationsDir);
|
|
84
|
-
return candidateDirs
|
|
85
|
-
.filter((name) => !RESERVED_SPACE_SUBDIR_NAMES.has(name))
|
|
86
|
-
.filter(isValidSpaceId)
|
|
87
|
-
.sort(compareSpaceIds);
|
|
83
|
+
return candidateDirs.filter(isValidSpaceId).sort(compareSpaceIds);
|
|
88
84
|
}
|
|
89
85
|
|
|
90
86
|
/**
|
|
@@ -14,12 +14,13 @@ import type { Contract } from '@prisma-next/contract/types';
|
|
|
14
14
|
import { getEmittedArtifactPaths } from '@prisma-next/emitter';
|
|
15
15
|
import { APP_SPACE_ID, createControlStack } from '@prisma-next/framework-components/control';
|
|
16
16
|
import { loadContractSpaceAggregate } from '@prisma-next/migration-tools/aggregate';
|
|
17
|
-
import { computeMigrationHash } from '@prisma-next/migration-tools/hash';
|
|
18
17
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
} from '@prisma-next/migration-tools/
|
|
18
|
+
contractSnapshotDir,
|
|
19
|
+
snapshotsImportPathFrom,
|
|
20
|
+
writeContractSnapshot,
|
|
21
|
+
} from '@prisma-next/migration-tools/contract-snapshot-store';
|
|
22
|
+
import { computeMigrationHash } from '@prisma-next/migration-tools/hash';
|
|
23
|
+
import { formatMigrationDirName, writeMigrationPackage } from '@prisma-next/migration-tools/io';
|
|
23
24
|
import type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';
|
|
24
25
|
import { findLatestMigration } from '@prisma-next/migration-tools/migration-graph';
|
|
25
26
|
import { writeMigrationTs } from '@prisma-next/migration-tools/migration-ts';
|
|
@@ -28,7 +29,6 @@ import { Command } from 'commander';
|
|
|
28
29
|
import { join, relative } from 'pathe';
|
|
29
30
|
import {
|
|
30
31
|
CliStructuredError,
|
|
31
|
-
errorFileNotFound,
|
|
32
32
|
errorRuntime,
|
|
33
33
|
errorTargetMigrationNotSupported,
|
|
34
34
|
errorUnexpected,
|
|
@@ -133,7 +133,6 @@ async function executeMigrationNewCommand(
|
|
|
133
133
|
const graph = aggregate.app.graph();
|
|
134
134
|
|
|
135
135
|
let fromHash: string | null = null;
|
|
136
|
-
let fromContractSourceDir: string | null = null;
|
|
137
136
|
|
|
138
137
|
if (packages.length > 0) {
|
|
139
138
|
if (options.from) {
|
|
@@ -147,17 +146,10 @@ async function executeMigrationNewCommand(
|
|
|
147
146
|
);
|
|
148
147
|
}
|
|
149
148
|
fromHash = match.metadata.to;
|
|
150
|
-
fromContractSourceDir = match.dirPath;
|
|
151
149
|
} else {
|
|
152
150
|
const latestMigration = findLatestMigration(graph);
|
|
153
151
|
if (latestMigration) {
|
|
154
152
|
fromHash = latestMigration.to;
|
|
155
|
-
const leafPkg = packages.find(
|
|
156
|
-
(p) => p.metadata.migrationHash === latestMigration.migrationHash,
|
|
157
|
-
);
|
|
158
|
-
if (leafPkg) {
|
|
159
|
-
fromContractSourceDir = leafPkg.dirPath;
|
|
160
|
-
}
|
|
161
153
|
}
|
|
162
154
|
}
|
|
163
155
|
}
|
|
@@ -204,44 +196,28 @@ async function executeMigrationNewCommand(
|
|
|
204
196
|
assertFrameworkComponentsCompatible(config.family.familyId, config.target.targetId, [
|
|
205
197
|
config.target,
|
|
206
198
|
config.adapter,
|
|
207
|
-
...(config.
|
|
199
|
+
...(config.extensions ?? []),
|
|
208
200
|
]);
|
|
209
201
|
|
|
210
202
|
await writeMigrationPackage(packageDir, metadata, []);
|
|
211
203
|
const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);
|
|
212
|
-
await
|
|
213
|
-
|
|
214
|
-
|
|
204
|
+
const [contractJsonRaw, contractDts] = await Promise.all([
|
|
205
|
+
readFile(destinationArtifacts.jsonPath, 'utf-8'),
|
|
206
|
+
readFile(destinationArtifacts.dtsPath, 'utf-8'),
|
|
215
207
|
]);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
try {
|
|
221
|
-
await copyFilesWithRename(packageDir, [
|
|
222
|
-
{ sourcePath: sourceArtifacts.jsonPath, destName: 'start-contract.json' },
|
|
223
|
-
{ sourcePath: sourceArtifacts.dtsPath, destName: 'start-contract.d.ts' },
|
|
224
|
-
]);
|
|
225
|
-
} catch (error) {
|
|
226
|
-
if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {
|
|
227
|
-
return notOk(
|
|
228
|
-
errorFileNotFound(sourceArtifacts.jsonPath, {
|
|
229
|
-
why: `Predecessor migration is missing its destination contract snapshot at ${sourceArtifacts.jsonPath}`,
|
|
230
|
-
fix: 'Re-emit the predecessor migration (`prisma-next migration plan` from its source) so its sibling `end-contract.json` is restored, then re-run this command.',
|
|
231
|
-
}),
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
throw error;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
208
|
+
await writeContractSnapshot(migrationsDir, toStorageHash, {
|
|
209
|
+
contractJson: JSON.parse(contractJsonRaw) as unknown,
|
|
210
|
+
contractDts,
|
|
211
|
+
});
|
|
237
212
|
|
|
238
213
|
const planner = migrations.createPlanner(controlAdapter);
|
|
239
214
|
const emptyPlan = planner.emptyMigration(
|
|
240
215
|
{
|
|
241
216
|
packageDir,
|
|
242
|
-
contractJsonPath: join(
|
|
217
|
+
contractJsonPath: join(contractSnapshotDir(migrationsDir, toStorageHash), 'contract.json'),
|
|
243
218
|
fromHash,
|
|
244
219
|
toHash: toStorageHash,
|
|
220
|
+
snapshotsImportPath: snapshotsImportPathFrom(packageDir, migrationsDir),
|
|
245
221
|
},
|
|
246
222
|
APP_SPACE_ID,
|
|
247
223
|
);
|
|
@@ -277,7 +253,7 @@ export function createMigrationNewCommand(): Command {
|
|
|
277
253
|
);
|
|
278
254
|
setCommandExamples(command, [
|
|
279
255
|
'prisma-next migration new --name split-name',
|
|
280
|
-
'prisma-next migration new --name custom-fk --from
|
|
256
|
+
'prisma-next migration new --name custom-fk --from abc123...',
|
|
281
257
|
]);
|
|
282
258
|
addGlobalOptions(command)
|
|
283
259
|
.option('--name <slug>', 'Migration name (used in directory name)')
|