@prisma-next/cli 0.16.0-dev.2 → 0.16.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 +31 -32
- package/dist/cli.mjs +13 -13
- package/dist/{client-KuBQftxz.mjs → client-DeJvLD2h.mjs} +4 -5
- package/dist/{client-KuBQftxz.mjs.map → client-DeJvLD2h.mjs.map} +1 -1
- package/dist/{command-helpers-Cpq44aVa.mjs → command-helpers-CVn3U9Uz.mjs} +12 -4
- package/dist/command-helpers-CVn3U9Uz.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 +5 -4
- 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 +8 -10
- 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 +11 -13
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts.map +1 -1
- package/dist/commands/migrate.mjs +9 -9
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-check.d.mts +3 -1
- package/dist/commands/migration-check.d.mts.map +1 -1
- package/dist/commands/migration-check.mjs +1 -1
- package/dist/commands/migration-graph.mjs +4 -4
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +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 +15 -39
- 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 +4 -4
- package/dist/commands/migration-show.mjs.map +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.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-C-l4-JEY.mjs +36 -0
- package/dist/contract-at-errors-C-l4-JEY.mjs.map +1 -0
- package/dist/{contract-emit-CgnXSANN.mjs → contract-emit-BMJiefhy.mjs} +3 -3
- package/dist/{contract-emit-CgnXSANN.mjs.map → contract-emit-BMJiefhy.mjs.map} +1 -1
- package/dist/{contract-emit-cJQBlvGb.mjs → contract-emit-DJ8SRI7L.mjs} +3 -4
- package/dist/{contract-emit-cJQBlvGb.mjs.map → contract-emit-DJ8SRI7L.mjs.map} +1 -1
- package/dist/{contract-infer-Cg8evbeg.mjs → contract-infer-_tstfy3k.mjs} +3 -3
- package/dist/{contract-infer-Cg8evbeg.mjs.map → contract-infer-_tstfy3k.mjs.map} +1 -1
- package/dist/{contract-space-aggregate-loader-D_3VeHVb.mjs → contract-space-aggregate-loader-hPVymNLw.mjs} +2 -2
- package/dist/{contract-space-aggregate-loader-D_3VeHVb.mjs.map → contract-space-aggregate-loader-hPVymNLw.mjs.map} +1 -1
- package/dist/{db-verify-BEclUzki.mjs → db-verify-3WM8y323.mjs} +4 -4
- package/dist/{db-verify-BEclUzki.mjs.map → db-verify-3WM8y323.mjs.map} +1 -1
- package/dist/exports/control-api.mjs +2 -2
- package/dist/exports/index.mjs +2 -2
- package/dist/exports/init-output.d.mts.map +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{format-DAUAZqPt.mjs → format-CFLZv_zU.mjs} +6 -5
- package/dist/format-CFLZv_zU.mjs.map +1 -0
- package/dist/{framework-components-CnbUbiJw.mjs → framework-components-VMQJbAwl.mjs} +2 -2
- package/dist/{framework-components-CnbUbiJw.mjs.map → framework-components-VMQJbAwl.mjs.map} +1 -1
- package/dist/{init-C3JhkHg-.mjs → init-XycYREvA.mjs} +55 -47
- package/dist/init-XycYREvA.mjs.map +1 -0
- package/dist/{inspect-live-schema-CX7EA5C3.mjs → inspect-live-schema-xS6VqjHE.mjs} +3 -3
- package/dist/{inspect-live-schema-CX7EA5C3.mjs.map → inspect-live-schema-xS6VqjHE.mjs.map} +1 -1
- package/dist/{migration-check-VqN-iTcx.mjs → migration-check-C_4gozCn.mjs} +53 -26
- package/dist/migration-check-C_4gozCn.mjs.map +1 -0
- package/dist/{migration-command-scaffold-CRyYrHZ9.mjs → migration-command-scaffold-D9JkLq_M.mjs} +3 -3
- package/dist/{migration-command-scaffold-CRyYrHZ9.mjs.map → migration-command-scaffold-D9JkLq_M.mjs.map} +1 -1
- package/dist/{migration-graph-command-render-B83xrnNy.mjs → migration-graph-command-render-DgQg5App.mjs} +3 -3
- package/dist/migration-graph-command-render-DgQg5App.mjs.map +1 -0
- package/dist/{migration-list-BAKujUjh.mjs → migration-list-BwEwRv25.mjs} +6 -6
- package/dist/migration-list-BwEwRv25.mjs.map +1 -0
- package/dist/{migration-log-C_xam2HZ.mjs → migration-log-C3RMIsUI.mjs} +4 -4
- package/dist/{migration-log-C_xam2HZ.mjs.map → migration-log-C3RMIsUI.mjs.map} +1 -1
- package/dist/{migration-path-target-DpPTsRCg.mjs → migration-path-target-C0TQuV5n.mjs} +2 -2
- package/dist/{migration-path-target-DpPTsRCg.mjs.map → migration-path-target-C0TQuV5n.mjs.map} +1 -1
- package/dist/{migration-plan-CreSVtWZ.mjs → migration-plan-BzxmK_do.mjs} +58 -68
- package/dist/migration-plan-BzxmK_do.mjs.map +1 -0
- package/dist/{migration-status-DYErXpyF.mjs → migration-status-ChQKA6Jf.mjs} +11 -11
- package/dist/migration-status-ChQKA6Jf.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-BEyNMfco.mjs} +2 -2
- package/dist/{telemetry-DVr6RFnq.mjs.map → telemetry-BEyNMfco.mjs.map} +1 -1
- package/dist/{verify-CgDVZjhc.mjs → verify-C4RazDE1.mjs} +2 -2
- package/dist/{verify-CgDVZjhc.mjs.map → verify-C4RazDE1.mjs.map} +1 -1
- package/package.json +21 -21
- package/src/commands/db-init.ts +1 -0
- package/src/commands/db-sign.ts +6 -4
- package/src/commands/db-update.ts +14 -6
- 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/reinit-cleanup.ts +16 -17
- package/src/commands/migrate.ts +2 -1
- package/src/commands/migration-check.ts +71 -33
- package/src/commands/migration-list.ts +1 -5
- package/src/commands/migration-new.ts +16 -40
- package/src/commands/migration-plan.ts +59 -83
- package/src/commands/migration-show.ts +1 -1
- package/src/commands/migration-status.ts +5 -6
- package/src/commands/ref.ts +19 -14
- package/src/control-api/client.ts +0 -1
- package/src/control-api/operations/contract-emit.ts +0 -1
- package/src/control-api/operations/format.ts +4 -3
- package/src/utils/cli-errors.ts +13 -5
- package/src/utils/contract-at-errors.ts +13 -22
- package/src/utils/contract-space-seed-phase.ts +8 -6
- package/src/utils/formatters/migration-graph-labels.ts +1 -2
- package/src/utils/formatters/migration-list-data-column.ts +1 -2
- package/src/utils/plan-resolution.ts +7 -10
- package/src/utils/ref-advancement.ts +22 -3
- 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/format-DAUAZqPt.mjs.map +0 -1
- package/dist/init-C3JhkHg-.mjs.map +0 -1
- package/dist/migration-check-VqN-iTcx.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-DYErXpyF.mjs.map +0 -1
- package/dist/output-mEQ74_nd.mjs.map +0 -1
- package/dist/ref-advancement-BkXlikCA.mjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { loadConfig } from '@prisma-next/config-loader';
|
|
3
3
|
import type { Contract } from '@prisma-next/contract/types';
|
|
4
4
|
import { getEmittedArtifactPaths } from '@prisma-next/emitter';
|
|
@@ -9,15 +9,14 @@ import {
|
|
|
9
9
|
type OperationPreview,
|
|
10
10
|
type SchemaOwnership,
|
|
11
11
|
} from '@prisma-next/framework-components/control';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
snapshotsImportPathFrom,
|
|
14
|
+
writeContractSnapshot,
|
|
15
|
+
} from '@prisma-next/migration-tools/contract-snapshot-store';
|
|
13
16
|
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
14
17
|
import { computeMigrationHash } from '@prisma-next/migration-tools/hash';
|
|
15
18
|
import { deriveProvidedInvariants } from '@prisma-next/migration-tools/invariants';
|
|
16
|
-
import {
|
|
17
|
-
copyFilesWithRename,
|
|
18
|
-
formatMigrationDirName,
|
|
19
|
-
writeMigrationPackage,
|
|
20
|
-
} from '@prisma-next/migration-tools/io';
|
|
19
|
+
import { formatMigrationDirName, writeMigrationPackage } from '@prisma-next/migration-tools/io';
|
|
21
20
|
import type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';
|
|
22
21
|
import { writeMigrationTs } from '@prisma-next/migration-tools/migration-ts';
|
|
23
22
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
@@ -62,27 +61,6 @@ interface MigrationPlanOptions extends CommonCommandOptions {
|
|
|
62
61
|
readonly to?: string;
|
|
63
62
|
}
|
|
64
63
|
|
|
65
|
-
async function writeSnapshotContractArtifacts(
|
|
66
|
-
packageDir: string,
|
|
67
|
-
contractJson: unknown,
|
|
68
|
-
contractDts: string,
|
|
69
|
-
artifactBasename: 'start-contract' | 'end-contract',
|
|
70
|
-
): Promise<void> {
|
|
71
|
-
await mkdir(packageDir, { recursive: true });
|
|
72
|
-
const jsonContent = `${canonicalizeJson(contractJson)}\n`;
|
|
73
|
-
const dtsContent = contractDts.endsWith('\n') ? contractDts : `${contractDts}\n`;
|
|
74
|
-
await writeFile(join(packageDir, `${artifactBasename}.json`), jsonContent);
|
|
75
|
-
await writeFile(join(packageDir, `${artifactBasename}.d.ts`), dtsContent);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async function writeSnapshotStartContract(
|
|
79
|
-
packageDir: string,
|
|
80
|
-
contractJson: unknown,
|
|
81
|
-
contractDts: string,
|
|
82
|
-
): Promise<void> {
|
|
83
|
-
await writeSnapshotContractArtifacts(packageDir, contractJson, contractDts, 'start-contract');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
64
|
type PlannerSuccess = {
|
|
87
65
|
readonly plannedOps: readonly MigrationPlanOperation[];
|
|
88
66
|
readonly migrationTsContent: string;
|
|
@@ -99,6 +77,7 @@ async function runPlannerLeg(
|
|
|
99
77
|
fromContract: Contract | null,
|
|
100
78
|
spaceId: string,
|
|
101
79
|
ownership: SchemaOwnership,
|
|
80
|
+
snapshotsImportPath: string,
|
|
102
81
|
): Promise<Result<PlannerSuccess, CliStructuredError>> {
|
|
103
82
|
const fromSchema = migrations.contractToSchema(fromContract, frameworkComponents);
|
|
104
83
|
const plannerResult = planner.plan({
|
|
@@ -114,6 +93,7 @@ async function runPlannerLeg(
|
|
|
114
93
|
// space in the aggregate, so it stays a genuine drop; a table another
|
|
115
94
|
// space owns is never dropped.
|
|
116
95
|
ownership,
|
|
96
|
+
snapshotsImportPath,
|
|
117
97
|
});
|
|
118
98
|
if (plannerResult.kind === 'failure') {
|
|
119
99
|
return notOk(
|
|
@@ -309,14 +289,18 @@ async function executeMigrationPlanCommand(
|
|
|
309
289
|
let toStorageHash: string = rawStorageHash;
|
|
310
290
|
|
|
311
291
|
// When `--to <ref>` resolves a non-default destination, these carry its raw
|
|
312
|
-
// artifacts so the planned package's
|
|
313
|
-
// resolved target rather than copied from the emitted
|
|
292
|
+
// artifacts so the planned package's destination snapshot store entry is
|
|
293
|
+
// written from the resolved target rather than copied from the emitted
|
|
294
|
+
// `contract.json`.
|
|
314
295
|
let toArtifacts: { contractJson: unknown; contractDts: string } | null = null;
|
|
315
296
|
|
|
316
297
|
let fromContract: Contract | null = null;
|
|
317
298
|
let fromHash: string | null = null;
|
|
318
|
-
let
|
|
319
|
-
|
|
299
|
+
let snapshotStartContract: {
|
|
300
|
+
readonly fromHash: string;
|
|
301
|
+
readonly contractJson: unknown;
|
|
302
|
+
readonly contractDts: string;
|
|
303
|
+
} | null = null;
|
|
320
304
|
let isAutoBaseline = false;
|
|
321
305
|
|
|
322
306
|
const tolerantAggregateResult = await loadContractSpaceAggregateForCli({
|
|
@@ -346,12 +330,12 @@ async function executeMigrationPlanCommand(
|
|
|
346
330
|
case 'graph-node':
|
|
347
331
|
fromHash = resolutionResult.value.fromHash;
|
|
348
332
|
fromContract = resolutionResult.value.fromContract;
|
|
349
|
-
fromContractSourceDir = resolutionResult.value.sourceDir;
|
|
350
333
|
break;
|
|
351
|
-
case '
|
|
334
|
+
case 'ref':
|
|
352
335
|
fromHash = resolutionResult.value.fromHash;
|
|
353
336
|
fromContract = resolutionResult.value.fromContract;
|
|
354
337
|
snapshotStartContract = {
|
|
338
|
+
fromHash: resolutionResult.value.fromHash,
|
|
355
339
|
contractJson: resolutionResult.value.contractJson,
|
|
356
340
|
contractDts: resolutionResult.value.contractDts,
|
|
357
341
|
};
|
|
@@ -360,6 +344,7 @@ async function executeMigrationPlanCommand(
|
|
|
360
344
|
fromHash = resolutionResult.value.fromHash;
|
|
361
345
|
fromContract = resolutionResult.value.fromContract;
|
|
362
346
|
snapshotStartContract = {
|
|
347
|
+
fromHash: resolutionResult.value.fromHash,
|
|
363
348
|
contractJson: resolutionResult.value.contractJson,
|
|
364
349
|
contractDts: resolutionResult.value.contractDts,
|
|
365
350
|
};
|
|
@@ -369,7 +354,7 @@ async function executeMigrationPlanCommand(
|
|
|
369
354
|
|
|
370
355
|
// `--to <ref>` swaps the planner destination to an arbitrary resolved
|
|
371
356
|
// contract (e.g. an ancestor / rollback target). The from-side resolution
|
|
372
|
-
// above is untouched; only the destination + its
|
|
357
|
+
// above is untouched; only the destination + its snapshot store entry
|
|
373
358
|
// change.
|
|
374
359
|
if (options.to !== undefined) {
|
|
375
360
|
const toResolution = await resolveToForPlan(options.to, {
|
|
@@ -386,11 +371,11 @@ async function executeMigrationPlanCommand(
|
|
|
386
371
|
};
|
|
387
372
|
}
|
|
388
373
|
|
|
389
|
-
// Phase 1 — seed: unconditionally re-emit per-space pinned
|
|
374
|
+
// Phase 1 — seed: unconditionally re-emit per-space pinned artifacts
|
|
390
375
|
// (contract.json / contract.d.ts / refs/head.json) and materialise any
|
|
391
376
|
// descriptor-shipped migration packages not yet on disk. Runs before
|
|
392
377
|
// the no-op check so that an extension bump alone (with no structural
|
|
393
|
-
// app-space change) still re-pins extension
|
|
378
|
+
// app-space change) still re-pins extension artifacts on disk.
|
|
394
379
|
const canonicalExtensionInputs = toExtensionInputs(config.extensionPacks ?? []);
|
|
395
380
|
const seedResult = await runContractSpaceSeedPhase({
|
|
396
381
|
migrationsDir,
|
|
@@ -463,24 +448,26 @@ async function executeMigrationPlanCommand(
|
|
|
463
448
|
[config.target, config.adapter, ...(config.extensionPacks ?? [])],
|
|
464
449
|
);
|
|
465
450
|
|
|
466
|
-
// Write the planned package's destination
|
|
467
|
-
// resolved target's raw artifacts are written; otherwise
|
|
468
|
-
// `contract.json` / `contract.d.ts` are
|
|
469
|
-
async function
|
|
451
|
+
// Write the planned package's destination contract into the snapshot store.
|
|
452
|
+
// With `--to`, the resolved target's raw artifacts are written; otherwise
|
|
453
|
+
// the emitted `contract.json` / `contract.d.ts` are read from disk.
|
|
454
|
+
async function writeDestinationSnapshot(destHash: string): Promise<void> {
|
|
470
455
|
if (toArtifacts !== null) {
|
|
471
|
-
await
|
|
472
|
-
|
|
473
|
-
toArtifacts.
|
|
474
|
-
|
|
475
|
-
'end-contract',
|
|
476
|
-
);
|
|
456
|
+
await writeContractSnapshot(migrationsDir, destHash, {
|
|
457
|
+
contractJson: toArtifacts.contractJson,
|
|
458
|
+
contractDts: toArtifacts.contractDts,
|
|
459
|
+
});
|
|
477
460
|
return;
|
|
478
461
|
}
|
|
479
462
|
const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);
|
|
480
|
-
await
|
|
481
|
-
|
|
482
|
-
|
|
463
|
+
const [contractJsonRaw, contractDts] = await Promise.all([
|
|
464
|
+
readFile(destinationArtifacts.jsonPath, 'utf-8'),
|
|
465
|
+
readFile(destinationArtifacts.dtsPath, 'utf-8'),
|
|
483
466
|
]);
|
|
467
|
+
await writeContractSnapshot(migrationsDir, destHash, {
|
|
468
|
+
contractJson: JSON.parse(contractJsonRaw) as unknown,
|
|
469
|
+
contractDts,
|
|
470
|
+
});
|
|
484
471
|
}
|
|
485
472
|
|
|
486
473
|
try {
|
|
@@ -507,6 +494,7 @@ async function executeMigrationPlanCommand(
|
|
|
507
494
|
null,
|
|
508
495
|
aggregate.app.spaceId,
|
|
509
496
|
aggregate,
|
|
497
|
+
snapshotsImportPathFrom(baselinePackageDir, migrationsDir),
|
|
510
498
|
);
|
|
511
499
|
if (!baselineLeg.ok) {
|
|
512
500
|
return notOk(baselineLeg.failure);
|
|
@@ -519,12 +507,10 @@ async function executeMigrationPlanCommand(
|
|
|
519
507
|
baselineTimestamp,
|
|
520
508
|
baselineLeg.value,
|
|
521
509
|
);
|
|
522
|
-
await
|
|
523
|
-
|
|
524
|
-
snapshotStartContract.
|
|
525
|
-
|
|
526
|
-
'end-contract',
|
|
527
|
-
);
|
|
510
|
+
await writeContractSnapshot(migrationsDir, fromHash, {
|
|
511
|
+
contractJson: snapshotStartContract.contractJson,
|
|
512
|
+
contractDts: snapshotStartContract.contractDts,
|
|
513
|
+
});
|
|
528
514
|
|
|
529
515
|
if (fromHash === toStorageHash) {
|
|
530
516
|
const baselineOps = baselineLeg.value.hasPlaceholders ? [] : baselineLeg.value.plannedOps;
|
|
@@ -577,6 +563,7 @@ async function executeMigrationPlanCommand(
|
|
|
577
563
|
fromContract,
|
|
578
564
|
aggregate.app.spaceId,
|
|
579
565
|
aggregate,
|
|
566
|
+
snapshotsImportPathFrom(deltaPackageDir, migrationsDir),
|
|
580
567
|
);
|
|
581
568
|
if (!deltaLeg.ok) {
|
|
582
569
|
return notOk(deltaLeg.failure);
|
|
@@ -589,12 +576,11 @@ async function executeMigrationPlanCommand(
|
|
|
589
576
|
deltaTimestamp,
|
|
590
577
|
deltaLeg.value,
|
|
591
578
|
);
|
|
592
|
-
await
|
|
593
|
-
await
|
|
594
|
-
|
|
595
|
-
snapshotStartContract.
|
|
596
|
-
|
|
597
|
-
);
|
|
579
|
+
await writeDestinationSnapshot(toStorageHash);
|
|
580
|
+
await writeContractSnapshot(migrationsDir, fromHash, {
|
|
581
|
+
contractJson: snapshotStartContract.contractJson,
|
|
582
|
+
contractDts: snapshotStartContract.contractDts,
|
|
583
|
+
});
|
|
598
584
|
|
|
599
585
|
const deltaOps = deltaLeg.value.hasPlaceholders ? [] : deltaLeg.value.plannedOps;
|
|
600
586
|
if (deltaLeg.value.hasPlaceholders) {
|
|
@@ -651,6 +637,7 @@ async function executeMigrationPlanCommand(
|
|
|
651
637
|
fromContract,
|
|
652
638
|
aggregate.app.spaceId,
|
|
653
639
|
aggregate,
|
|
640
|
+
snapshotsImportPathFrom(packageDir, migrationsDir),
|
|
654
641
|
);
|
|
655
642
|
if (!deltaLeg.ok) {
|
|
656
643
|
return notOk(deltaLeg.failure);
|
|
@@ -663,21 +650,12 @@ async function executeMigrationPlanCommand(
|
|
|
663
650
|
timestamp,
|
|
664
651
|
deltaLeg.value,
|
|
665
652
|
);
|
|
666
|
-
await
|
|
667
|
-
if (
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
{ sourcePath: sourceArtifacts.jsonPath, destName: 'start-contract.json' },
|
|
673
|
-
{ sourcePath: sourceArtifacts.dtsPath, destName: 'start-contract.d.ts' },
|
|
674
|
-
]);
|
|
675
|
-
} else if (snapshotStartContract !== null) {
|
|
676
|
-
await writeSnapshotStartContract(
|
|
677
|
-
packageDir,
|
|
678
|
-
snapshotStartContract.contractJson,
|
|
679
|
-
snapshotStartContract.contractDts,
|
|
680
|
-
);
|
|
653
|
+
await writeDestinationSnapshot(toStorageHash);
|
|
654
|
+
if (snapshotStartContract !== null) {
|
|
655
|
+
await writeContractSnapshot(migrationsDir, snapshotStartContract.fromHash, {
|
|
656
|
+
contractJson: snapshotStartContract.contractJson,
|
|
657
|
+
contractDts: snapshotStartContract.contractDts,
|
|
658
|
+
});
|
|
681
659
|
}
|
|
682
660
|
|
|
683
661
|
if (deltaLeg.value.hasPlaceholders) {
|
|
@@ -953,10 +931,9 @@ export type PrefixResolutionFailure =
|
|
|
953
931
|
*
|
|
954
932
|
* Note: matches `metadata.to` (the contract hash this migration produces),
|
|
955
933
|
* not `metadata.migrationHash` (the package's content-addressed identity).
|
|
956
|
-
* Tries exact match first, then prefix match
|
|
957
|
-
*
|
|
958
|
-
*
|
|
959
|
-
* not found.
|
|
934
|
+
* Tries exact match first, then prefix match. Returns the matched package on
|
|
935
|
+
* success, or a discriminated failure indicating whether the prefix was
|
|
936
|
+
* ambiguous or simply not found.
|
|
960
937
|
*
|
|
961
938
|
* @internal Exported for testing only.
|
|
962
939
|
*/
|
|
@@ -967,8 +944,7 @@ export function resolveBundleByPrefix<T extends { metadata: { to: string } }>(
|
|
|
967
944
|
const exact = bundles.find((p) => p.metadata.to === needle);
|
|
968
945
|
if (exact) return ok(exact);
|
|
969
946
|
|
|
970
|
-
const
|
|
971
|
-
const candidates = bundles.filter((p) => p.metadata.to.startsWith(prefixWithScheme));
|
|
947
|
+
const candidates = bundles.filter((p) => p.metadata.to.startsWith(needle));
|
|
972
948
|
|
|
973
949
|
if (candidates.length === 1) return ok(candidates[0]!);
|
|
974
950
|
if (candidates.length > 1) return notOk({ reason: 'ambiguous', count: candidates.length });
|
|
@@ -228,7 +228,7 @@ export function createMigrationShowCommand(): Command {
|
|
|
228
228
|
);
|
|
229
229
|
setCommandExamples(command, [
|
|
230
230
|
'prisma-next migration show 20260101_100000_add_user',
|
|
231
|
-
'prisma-next migration show
|
|
231
|
+
'prisma-next migration show a1b2c3',
|
|
232
232
|
'prisma-next migration show 20260101_100000_add_user --json',
|
|
233
233
|
]);
|
|
234
234
|
setCommandSeeAlso(command, [
|
|
@@ -111,8 +111,7 @@ interface MigrationStatusCommandResult {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
function shortDisplayHash(hash: string): string {
|
|
114
|
-
|
|
115
|
-
return stripped.slice(0, 12);
|
|
114
|
+
return hash.slice(0, 12);
|
|
116
115
|
}
|
|
117
116
|
|
|
118
117
|
function resolveTarget(contractHash: string, activeRefHash: string | undefined): string {
|
|
@@ -660,10 +659,10 @@ export function createMigrationStatusCommand(): Command {
|
|
|
660
659
|
setCommandExamples(command, [
|
|
661
660
|
'prisma-next migration status --db $DATABASE_URL',
|
|
662
661
|
'prisma-next migration status --to production --db $DATABASE_URL',
|
|
663
|
-
'prisma-next migration status --from
|
|
664
|
-
'prisma-next migration status --from
|
|
665
|
-
'prisma-next migration status --ascii --from
|
|
666
|
-
'prisma-next migration status --legend --from
|
|
662
|
+
'prisma-next migration status --from abc123 --to production',
|
|
663
|
+
'prisma-next migration status --from abc123 --to production --json',
|
|
664
|
+
'prisma-next migration status --ascii --from abc123 --to production',
|
|
665
|
+
'prisma-next migration status --legend --from abc123 --to production',
|
|
667
666
|
]);
|
|
668
667
|
setCommandSeeAlso(command, [
|
|
669
668
|
{ verb: 'migration log', oneLiner: 'Show executed migration history' },
|
package/src/commands/ref.ts
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
1
|
import { loadConfig } from '@prisma-next/config-loader';
|
|
3
2
|
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
3
|
+
import {
|
|
4
|
+
contractSnapshotDir,
|
|
5
|
+
readContractSnapshotJson,
|
|
6
|
+
} from '@prisma-next/migration-tools/contract-snapshot-store';
|
|
4
7
|
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
5
8
|
import { findLatestMigration, isGraphNode } from '@prisma-next/migration-tools/migration-graph';
|
|
6
9
|
import { parseContractRef } from '@prisma-next/migration-tools/ref-resolution';
|
|
7
10
|
import type { RefEntry } from '@prisma-next/migration-tools/refs';
|
|
8
11
|
import {
|
|
9
|
-
|
|
12
|
+
deleteRef,
|
|
10
13
|
readRefs,
|
|
11
14
|
validateRefName,
|
|
12
15
|
validateRefValue,
|
|
13
|
-
|
|
16
|
+
writeRef,
|
|
14
17
|
} from '@prisma-next/migration-tools/refs';
|
|
15
18
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
16
19
|
import { Command } from 'commander';
|
|
@@ -34,7 +37,6 @@ import {
|
|
|
34
37
|
import { buildReadAggregate } from '../utils/contract-space-aggregate-loader';
|
|
35
38
|
import { formatCommandHelp } from '../utils/formatters/help';
|
|
36
39
|
import { parseGlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
|
|
37
|
-
import { readContractIR } from '../utils/ref-advancement';
|
|
38
40
|
import { handleResult } from '../utils/result-handler';
|
|
39
41
|
import { createTerminalUI } from '../utils/terminal-ui';
|
|
40
42
|
|
|
@@ -114,26 +116,29 @@ export async function executeRefSetCommand(
|
|
|
114
116
|
return notOk(errorRefSetBundleNotFound(resolvedHash));
|
|
115
117
|
}
|
|
116
118
|
|
|
117
|
-
const contractJsonPath = join(
|
|
118
|
-
|
|
119
|
+
const contractJsonPath = join(
|
|
120
|
+
contractSnapshotDir(migrationsDir, resolvedHash),
|
|
121
|
+
'contract.json',
|
|
122
|
+
);
|
|
119
123
|
try {
|
|
120
|
-
|
|
121
|
-
contractJson = JSON.parse(raw) as Record<string, unknown>;
|
|
124
|
+
await readContractSnapshotJson(migrationsDir, resolvedHash);
|
|
122
125
|
} catch (readError) {
|
|
123
|
-
if (
|
|
126
|
+
if (
|
|
127
|
+
MigrationToolsError.is(readError) &&
|
|
128
|
+
readError.code === 'MIGRATION.CONTRACT_SNAPSHOT_MISSING'
|
|
129
|
+
) {
|
|
124
130
|
return notOk(
|
|
125
131
|
errorFileNotFound(contractJsonPath, {
|
|
126
|
-
why: `Migration bundle for hash ${resolvedHash} is missing its
|
|
127
|
-
fix: '
|
|
132
|
+
why: `Migration bundle for hash ${resolvedHash} is missing its contract snapshot at ${contractJsonPath}`,
|
|
133
|
+
fix: 'Restore migrations/snapshots/ from version control, or re-run the command that produced this migration to regenerate its snapshot.',
|
|
128
134
|
}),
|
|
129
135
|
);
|
|
130
136
|
}
|
|
131
137
|
throw readError;
|
|
132
138
|
}
|
|
133
139
|
|
|
134
|
-
const contractIR = await readContractIR(contractJson, contractJsonPath);
|
|
135
140
|
const entry: RefEntry = { hash: resolvedHash, invariants: [] };
|
|
136
|
-
await
|
|
141
|
+
await writeRef(refsDir, name, entry);
|
|
137
142
|
return ok({ ok: true as const, ref: name, hash: resolvedHash, invariants: [] });
|
|
138
143
|
} catch (error) {
|
|
139
144
|
if (error instanceof CliStructuredError) return notOk(error);
|
|
@@ -148,7 +153,7 @@ export async function executeRefDeleteCommand(
|
|
|
148
153
|
try {
|
|
149
154
|
const config = await loadConfig(options.config);
|
|
150
155
|
const { refsDir } = resolveMigrationPaths(options.config, config);
|
|
151
|
-
await
|
|
156
|
+
await deleteRef(refsDir, name);
|
|
152
157
|
return ok({ ok: true as const, ref: name, deleted: true as const });
|
|
153
158
|
} catch (error) {
|
|
154
159
|
if (error instanceof CliStructuredError) return notOk(error);
|
|
@@ -609,7 +609,6 @@ class ControlClientImpl implements ControlClient {
|
|
|
609
609
|
const sourceContext = {
|
|
610
610
|
composedExtensionPacks: stack.extensionPacks.map((p) => p.id),
|
|
611
611
|
composedExtensionContracts: stack.extensionContracts,
|
|
612
|
-
scalarTypeDescriptors: stack.scalarTypeDescriptors,
|
|
613
612
|
authoringContributions: stack.authoringContributions,
|
|
614
613
|
codecLookup: stack.codecLookup,
|
|
615
614
|
controlMutationDefaults: stack.controlMutationDefaults,
|
|
@@ -197,7 +197,6 @@ export async function executeContractEmit(
|
|
|
197
197
|
const sourceContext = {
|
|
198
198
|
composedExtensionPacks: stack.extensionPacks.map((p) => p.id),
|
|
199
199
|
composedExtensionContracts: stack.extensionContracts,
|
|
200
|
-
scalarTypeDescriptors: stack.scalarTypeDescriptors,
|
|
201
200
|
authoringContributions: stack.authoringContributions,
|
|
202
201
|
codecLookup: stack.codecLookup,
|
|
203
202
|
controlMutationDefaults: stack.controlMutationDefaults,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { EOL } from 'node:os';
|
|
3
3
|
import { loadConfig } from '@prisma-next/config-loader';
|
|
4
|
-
import { type FormatOptions, format
|
|
4
|
+
import { type FormatOptions, format } from '@prisma-next/psl-parser/format';
|
|
5
5
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
6
|
+
import { isStructuredError } from '@prisma-next/utils/structured-error';
|
|
6
7
|
import { CliStructuredError, errorRuntime, errorUnexpected } from '../../utils/cli-errors';
|
|
7
8
|
|
|
8
9
|
export interface FormatOperationOptions {
|
|
@@ -71,12 +72,12 @@ export async function executeFormat(
|
|
|
71
72
|
try {
|
|
72
73
|
formatted = format(contents, formatOptions);
|
|
73
74
|
} catch (error) {
|
|
74
|
-
if (error
|
|
75
|
+
if (isStructuredError(error) && error.code === 'PSL.PARSE_FAILED') {
|
|
75
76
|
return notOk(
|
|
76
77
|
errorRuntime('Cannot format PSL with parse errors', {
|
|
77
78
|
why: error.message,
|
|
78
79
|
fix: 'Fix the parse errors in your schema and try again.',
|
|
79
|
-
meta: { diagnostics: error.diagnostics },
|
|
80
|
+
meta: { diagnostics: error.meta?.['diagnostics'] },
|
|
80
81
|
}),
|
|
81
82
|
);
|
|
82
83
|
}
|
package/src/utils/cli-errors.ts
CHANGED
|
@@ -215,22 +215,30 @@ export function errorPlanForgotTheFlag(
|
|
|
215
215
|
});
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
/**
|
|
219
|
+
* `viaRef: true` (the default) mirrors migration-tools' `errorRefNotResolvable`:
|
|
220
|
+
* a ref name with no pointer file, where the fallback hash isn't a graph
|
|
221
|
+
* node either — there's nothing to materialize a contract from.
|
|
222
|
+
* `viaRef: false` is a distinct, ref-independent case: an explicit `--from
|
|
223
|
+
* <hash>` that doesn't name a ref, on an empty migration graph, so there is
|
|
224
|
+
* no graph node and no ref to resolve a contract through.
|
|
225
|
+
*/
|
|
218
226
|
export function errorSnapshotMissing(
|
|
219
227
|
identifier: string,
|
|
220
228
|
options?: { readonly viaRef?: boolean },
|
|
221
229
|
): CliStructuredError {
|
|
222
230
|
const viaRef = options?.viaRef !== false;
|
|
223
231
|
const fix = viaRef
|
|
224
|
-
? `
|
|
225
|
-
: `No contract source exists for hash "${identifier}" on an empty migration graph. Use --from with a ref name
|
|
232
|
+
? `Create the ref with "prisma-next ref set ${identifier} <hash>" (or advance it via "prisma-next db update --advance-ref ${identifier}"), or pass a hash that is a node in the migration graph.`
|
|
233
|
+
: `No contract source exists for hash "${identifier}" on an empty migration graph. Use --from with a ref name (its contract resolves through the snapshot store), or run db update first.`;
|
|
226
234
|
return errorRuntime(
|
|
227
235
|
viaRef
|
|
228
|
-
? `Ref "${identifier}"
|
|
236
|
+
? `Ref "${identifier}" is not resolvable`
|
|
229
237
|
: `No contract source for from-hash "${identifier}"`,
|
|
230
238
|
{
|
|
231
239
|
why: viaRef
|
|
232
|
-
? `Ref "${identifier}"
|
|
233
|
-
: `Hash "${identifier}" is not a graph
|
|
240
|
+
? `Ref "${identifier}" has no pointer file, and the hash being resolved is not a node in the migration graph either.`
|
|
241
|
+
: `Hash "${identifier}" is not a node in the migration graph (the graph is empty), and it does not name a ref either.`,
|
|
234
242
|
fix,
|
|
235
243
|
meta: {
|
|
236
244
|
code: 'MIGRATION.SNAPSHOT_MISSING',
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
2
2
|
import { notOk, type Result } from '@prisma-next/utils/result';
|
|
3
|
-
import { join } from 'pathe';
|
|
4
3
|
import {
|
|
5
4
|
CliStructuredError,
|
|
6
5
|
errorContractValidationFailed,
|
|
@@ -16,7 +15,7 @@ export function mapContractAtError(
|
|
|
16
15
|
): Result<never, CliStructuredError> {
|
|
17
16
|
if (MigrationToolsError.is(error)) {
|
|
18
17
|
switch (error.code) {
|
|
19
|
-
case 'MIGRATION.
|
|
18
|
+
case 'MIGRATION.REF_NOT_RESOLVABLE': {
|
|
20
19
|
const refName =
|
|
21
20
|
typeof error.details?.['refName'] === 'string'
|
|
22
21
|
? error.details['refName']
|
|
@@ -27,18 +26,13 @@ export function mapContractAtError(
|
|
|
27
26
|
}
|
|
28
27
|
case 'MIGRATION.CONTRACT_DESERIALIZATION_FAILED': {
|
|
29
28
|
const filePath =
|
|
30
|
-
typeof error.details?.['filePath'] === 'string'
|
|
31
|
-
? error.details['filePath']
|
|
32
|
-
: 'ref-snapshot';
|
|
29
|
+
typeof error.details?.['filePath'] === 'string' ? error.details['filePath'] : 'unknown';
|
|
33
30
|
const message =
|
|
34
31
|
typeof error.details?.['message'] === 'string' ? error.details['message'] : error.message;
|
|
35
|
-
const isRefSnapshot = filePath.endsWith('.contract.json');
|
|
36
32
|
return notOk(
|
|
37
33
|
errorContractValidationFailed(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
: `Predecessor contract at ${filePath} failed to deserialize: ${message}`,
|
|
41
|
-
{ where: { path: isRefSnapshot ? 'ref-snapshot' : filePath } },
|
|
34
|
+
`Predecessor contract at ${filePath} failed to deserialize: ${message}`,
|
|
35
|
+
{ where: { path: filePath } },
|
|
42
36
|
),
|
|
43
37
|
);
|
|
44
38
|
}
|
|
@@ -66,22 +60,19 @@ export function mapContractAtError(
|
|
|
66
60
|
fix: error.fix,
|
|
67
61
|
}),
|
|
68
62
|
);
|
|
69
|
-
case 'MIGRATION.
|
|
70
|
-
const
|
|
71
|
-
typeof error.details?.['
|
|
72
|
-
|
|
73
|
-
|
|
63
|
+
case 'MIGRATION.CONTRACT_SNAPSHOT_MISSING': {
|
|
64
|
+
const expectedPath =
|
|
65
|
+
typeof error.details?.['expectedPath'] === 'string'
|
|
66
|
+
? error.details['expectedPath']
|
|
67
|
+
: 'migrations/snapshots/';
|
|
74
68
|
const role = options?.artifactRole ?? 'from';
|
|
75
69
|
return notOk(
|
|
76
|
-
errorFileNotFound(
|
|
70
|
+
errorFileNotFound(expectedPath, {
|
|
77
71
|
why:
|
|
78
72
|
role === 'to'
|
|
79
|
-
? `Target migration is missing its
|
|
80
|
-
: `Predecessor migration is missing its
|
|
81
|
-
fix:
|
|
82
|
-
role === 'to'
|
|
83
|
-
? 'Re-emit the target migration so its sibling `end-contract.json` / `end-contract.d.ts` are restored, then re-run this command.'
|
|
84
|
-
: '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.',
|
|
73
|
+
? `Target migration is missing its contract snapshot at ${expectedPath}`
|
|
74
|
+
: `Predecessor migration is missing its contract snapshot at ${expectedPath}`,
|
|
75
|
+
fix: 'Restore migrations/snapshots/ from version control, or re-run the command that produced this migration to regenerate its snapshot.',
|
|
85
76
|
}),
|
|
86
77
|
);
|
|
87
78
|
}
|
|
@@ -2,7 +2,7 @@ import { materialiseExtensionMigrationPackageIfMissing } from '@prisma-next/migr
|
|
|
2
2
|
import type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';
|
|
3
3
|
import type { MigrationOps } from '@prisma-next/migration-tools/package';
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
emitContractSpaceArtifacts,
|
|
6
6
|
planAllSpaces,
|
|
7
7
|
readContractSpaceHeadRef,
|
|
8
8
|
type SpacePlanOutput,
|
|
@@ -53,7 +53,7 @@ export interface ContractSpaceSeedPhaseInputs {
|
|
|
53
53
|
* - `action: 'unchanged'` — the on-disk head already matched the
|
|
54
54
|
* descriptor and no new migration packages needed to be written.
|
|
55
55
|
*
|
|
56
|
-
* Either way, the
|
|
56
|
+
* Either way, the artifacts (`contract.json`, `contract.d.ts`,
|
|
57
57
|
* `refs/head.json`) are re-emitted: the framework owns those files and
|
|
58
58
|
* makes the re-emit observably idempotent at the byte level.
|
|
59
59
|
*/
|
|
@@ -75,9 +75,11 @@ export interface ContractSpaceSeedPhaseResult {
|
|
|
75
75
|
* For every extension that exposes a `contractSpace`:
|
|
76
76
|
*
|
|
77
77
|
* 1. Read the on-disk head ref (returns `null` on first emit).
|
|
78
|
-
* 2.
|
|
79
|
-
*
|
|
80
|
-
*
|
|
78
|
+
* 2. Write the head contract into the migrations-root snapshot store
|
|
79
|
+
* (write-if-absent, keyed by hash) and unconditionally re-emit
|
|
80
|
+
* `refs/head.json`, via {@link emitContractSpaceArtifacts}. The
|
|
81
|
+
* framework owns `refs/head.json`; re-emit is the contract for that
|
|
82
|
+
* file. The snapshot itself is only written once per distinct hash.
|
|
81
83
|
* 3. Materialise any descriptor-shipped migration packages not yet on
|
|
82
84
|
* disk via {@link materialiseExtensionMigrationPackageIfMissing}.
|
|
83
85
|
* Existing packages are left untouched (by-existence skip).
|
|
@@ -144,7 +146,7 @@ export async function runContractSpaceSeedPhase(
|
|
|
144
146
|
const onDiskHeadRef = await readContractSpaceHeadRef(inputs.migrationsDir, space.spaceId);
|
|
145
147
|
const priorHash = onDiskHeadRef?.hash ?? null;
|
|
146
148
|
|
|
147
|
-
await
|
|
149
|
+
await emitContractSpaceArtifacts(inputs.migrationsDir, space.spaceId, {
|
|
148
150
|
contract: descriptor.contractJson,
|
|
149
151
|
contractDts: buildPlaceholderContractDts(space.spaceId),
|
|
150
152
|
headRef: { hash: descriptor.headRef.hash, invariants: descriptor.headRef.invariants },
|
|
@@ -104,8 +104,7 @@ function abbreviateHash(hash: string, hashLength: number, emptySource: string):
|
|
|
104
104
|
if (hash === EMPTY_CONTRACT_HASH) {
|
|
105
105
|
return emptySource;
|
|
106
106
|
}
|
|
107
|
-
|
|
108
|
-
return stripped.slice(0, hashLength);
|
|
107
|
+
return hash.slice(0, hashLength);
|
|
109
108
|
}
|
|
110
109
|
|
|
111
110
|
interface ContractOverlayNames {
|
|
@@ -18,8 +18,7 @@ export function migrationListEmptySource(glyphMode: GlyphMode): string {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export function abbreviateContractHash(hash: string): string {
|
|
21
|
-
|
|
22
|
-
return stripped.slice(0, MIGRATION_LIST_HASH_WIDTH);
|
|
21
|
+
return hash.slice(0, MIGRATION_LIST_HASH_WIDTH);
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
export function padFromHashColumn(text: string, width: number): string {
|