@prisma-next/cli 0.11.0-dev.9 → 0.12.0-dev.1
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 +13 -9
- package/dist/cli.mjs +9 -10
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-UnIveZxZ.mjs → client-KgJorIvG.mjs} +72 -60
- package/dist/client-KgJorIvG.mjs.map +1 -0
- package/dist/{command-helpers-CRfjbZRz.mjs → command-helpers-Bbw1GbwL.mjs} +642 -45
- package/dist/command-helpers-Bbw1GbwL.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts.map +1 -1
- package/dist/commands/contract-emit.mjs +1 -1
- package/dist/commands/contract-infer.d.mts.map +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 -7
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.d.mts.map +1 -1
- package/dist/commands/db-schema.mjs +3 -4
- package/dist/commands/db-schema.mjs.map +1 -1
- package/dist/commands/db-sign.d.mts.map +1 -1
- package/dist/commands/db-sign.mjs +12 -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 +12 -11
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.d.mts.map +1 -1
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +2 -2
- package/dist/commands/migrate.d.mts.map +1 -1
- package/dist/commands/migrate.mjs +68 -69
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-check.d.mts +4 -3
- package/dist/commands/migration-check.d.mts.map +1 -1
- package/dist/commands/migration-check.mjs +1 -280
- package/dist/commands/migration-graph.d.mts +13 -2
- package/dist/commands/migration-graph.d.mts.map +1 -1
- package/dist/commands/migration-graph.mjs +2 -137
- package/dist/commands/migration-list.d.mts +64 -4
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +143 -56
- package/dist/commands/migration-list.mjs.map +1 -1
- package/dist/commands/migration-log.d.mts +10 -1
- package/dist/commands/migration-log.d.mts.map +1 -1
- package/dist/commands/migration-log.mjs +10 -15
- package/dist/commands/migration-log.mjs.map +1 -1
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +32 -38
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +2 -2
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-show.d.mts +4 -55
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +61 -153
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +12 -49
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +84 -80
- package/dist/commands/migration-status.mjs.map +1 -1
- package/dist/commands/ref.d.mts +1 -1
- package/dist/commands/ref.d.mts.map +1 -1
- package/dist/commands/ref.mjs +10 -8
- package/dist/commands/ref.mjs.map +1 -1
- package/dist/config-loader-B6sJjXTv.mjs.map +1 -1
- package/dist/config-loader.d.mts.map +1 -1
- package/dist/contract-at-errors-BxP-TOMl.mjs +42 -0
- package/dist/contract-at-errors-BxP-TOMl.mjs.map +1 -0
- package/dist/{contract-emit-C6rlsljO.mjs → contract-emit-D-4jrNve.mjs} +21 -7
- package/dist/{contract-emit-C6rlsljO.mjs.map → contract-emit-D-4jrNve.mjs.map} +1 -1
- package/dist/{contract-emit-mqXmapxB.mjs → contract-emit-DxcGl4Uq.mjs} +4 -6
- package/dist/{contract-emit-mqXmapxB.mjs.map → contract-emit-DxcGl4Uq.mjs.map} +1 -1
- package/dist/{contract-enrichment-Dani0mMW.mjs → contract-enrichment-a0V5Y_mL.mjs} +4 -25
- package/dist/contract-enrichment-a0V5Y_mL.mjs.map +1 -0
- package/dist/{contract-infer-C4jxc1aZ.mjs → contract-infer-D8uEbJuu.mjs} +3 -4
- package/dist/{contract-infer-C4jxc1aZ.mjs.map → contract-infer-D8uEbJuu.mjs.map} +1 -1
- package/dist/contract-space-aggregate-loader-DvZwdkrr.mjs +247 -0
- package/dist/contract-space-aggregate-loader-DvZwdkrr.mjs.map +1 -0
- package/dist/{db-verify-1d8tDoFN.mjs → db-verify-v_vUKXTU.mjs} +5 -7
- package/dist/{db-verify-1d8tDoFN.mjs.map → db-verify-v_vUKXTU.mjs.map} +1 -1
- package/dist/exports/control-api.d.mts +3 -3
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +3 -3
- package/dist/exports/index.d.mts.map +1 -1
- package/dist/exports/index.mjs +1 -1
- 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/extension-pack-inputs-IDvjRCi3.mjs +62 -0
- package/dist/extension-pack-inputs-IDvjRCi3.mjs.map +1 -0
- package/dist/{framework-components-Bexd0f4E.mjs → framework-components-fYXjz_in.mjs} +2 -2
- package/dist/{framework-components-Bexd0f4E.mjs.map → framework-components-fYXjz_in.mjs.map} +1 -1
- package/dist/global-flags-DEHjV8_s.d.mts +34 -0
- package/dist/global-flags-DEHjV8_s.d.mts.map +1 -0
- package/dist/{graph-render-BE8vmJ_7.mjs → graph-render-rFAqZujX.mjs} +2 -2
- package/dist/{graph-render-BE8vmJ_7.mjs.map → graph-render-rFAqZujX.mjs.map} +1 -1
- package/dist/{init-ByoeQphC.mjs → init-Cv9UzWL5.mjs} +17 -19
- package/dist/init-Cv9UzWL5.mjs.map +1 -0
- package/dist/{inspect-live-schema-B1Q49RF0.mjs → inspect-live-schema-C6ohV_oQ.mjs} +4 -5
- package/dist/{inspect-live-schema-B1Q49RF0.mjs.map → inspect-live-schema-C6ohV_oQ.mjs.map} +1 -1
- package/dist/migration-check-BiBJoYYW.mjs +341 -0
- package/dist/migration-check-BiBJoYYW.mjs.map +1 -0
- package/dist/migration-cli.d.mts.map +1 -1
- package/dist/migration-cli.mjs +4 -4
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-oY4P1Qto.mjs → migration-command-scaffold-CjvwO6at.mjs} +4 -5
- package/dist/{migration-command-scaffold-oY4P1Qto.mjs.map → migration-command-scaffold-CjvwO6at.mjs.map} +1 -1
- package/dist/migration-graph-D7DVUElV.mjs +1232 -0
- package/dist/migration-graph-D7DVUElV.mjs.map +1 -0
- package/dist/migration-list-styler-BRwF4-gy.mjs +399 -0
- package/dist/migration-list-styler-BRwF4-gy.mjs.map +1 -0
- package/dist/{migration-plan-jdAHg_gK.mjs → migration-plan-9DJ7q7_z.mjs} +169 -189
- package/dist/migration-plan-9DJ7q7_z.mjs.map +1 -0
- package/dist/{migration-types-BXWvz12q.d.mts → migration-types-D2FW63pr.d.mts} +1 -1
- package/dist/{migration-types-BXWvz12q.d.mts.map → migration-types-D2FW63pr.d.mts.map} +1 -1
- package/dist/{migrations-B7n518mT.mjs → migrations-Cv2jxNNK.mjs} +3 -13
- package/dist/migrations-Cv2jxNNK.mjs.map +1 -0
- package/dist/{output-CUIdfYo5.mjs → output-B60Gw5fu.mjs} +1 -1
- package/dist/{output-CUIdfYo5.mjs.map → output-B60Gw5fu.mjs.map} +1 -1
- package/dist/{progress-adapter-DFfvZcYL.mjs → progress-adapter-C644QK8l.mjs} +1 -1
- package/dist/{progress-adapter-DFfvZcYL.mjs.map → progress-adapter-C644QK8l.mjs.map} +1 -1
- package/dist/{ref-advancement-DRh5Nquq.mjs → ref-advancement-DUZqsue6.mjs} +1 -1
- package/dist/{ref-advancement-DRh5Nquq.mjs.map → ref-advancement-DUZqsue6.mjs.map} +1 -1
- package/dist/terminal-ui-5Y6mrg93.d.mts +133 -0
- package/dist/terminal-ui-5Y6mrg93.d.mts.map +1 -0
- package/dist/{types-UWB2-rrw.d.mts → types-Dt_SfqFm.d.mts} +18 -26
- package/dist/types-Dt_SfqFm.d.mts.map +1 -0
- package/dist/{verify-C5UvbrF1.mjs → verify-DCA9Sldu.mjs} +2 -2
- package/dist/{verify-C5UvbrF1.mjs.map → verify-DCA9Sldu.mjs.map} +1 -1
- package/package.json +34 -23
- package/src/commands/db-sign.ts +9 -5
- package/src/commands/db-update.ts +9 -8
- package/src/commands/init/templates/env.ts +13 -14
- package/src/commands/migrate.ts +87 -76
- package/src/commands/migration-check.ts +43 -83
- package/src/commands/migration-graph.ts +75 -60
- package/src/commands/migration-list.ts +220 -74
- package/src/commands/migration-log.ts +8 -14
- package/src/commands/migration-new.ts +44 -48
- package/src/commands/migration-plan.ts +107 -129
- package/src/commands/migration-show.ts +65 -284
- package/src/commands/migration-status.ts +127 -124
- package/src/commands/ref.ts +9 -4
- package/src/control-api/client.ts +0 -1
- package/src/control-api/contract-enrichment.ts +6 -42
- package/src/control-api/operations/{apply-aggregate.ts → apply.ts} +44 -75
- package/src/control-api/operations/contract-emit.ts +7 -2
- package/src/control-api/operations/{db-apply-aggregate.ts → db-apply.ts} +19 -19
- package/src/control-api/operations/db-init.ts +4 -4
- package/src/control-api/operations/db-update.ts +4 -4
- package/src/control-api/operations/db-verify.ts +15 -11
- package/src/control-api/operations/migration-apply.ts +56 -47
- package/src/control-api/types.ts +19 -27
- package/src/migration-cli.ts +4 -4
- package/src/utils/cli-errors.ts +73 -12
- package/src/utils/command-helpers.ts +1 -20
- package/src/utils/contract-at-errors.ts +96 -0
- package/src/utils/contract-space-aggregate-loader.ts +336 -117
- package/src/utils/formatters/migration-graph-layout.ts +1119 -0
- package/src/utils/formatters/migration-graph-rows.ts +336 -0
- package/src/utils/formatters/migration-graph-tree-render.ts +459 -0
- package/src/utils/formatters/migration-list-data-column.ts +115 -0
- package/src/utils/formatters/migration-list-graph-topology.ts +368 -0
- package/src/utils/formatters/migration-list-render.ts +191 -0
- package/src/utils/formatters/migration-list-styler.ts +63 -0
- package/src/utils/formatters/migration-list-types.ts +21 -0
- package/src/utils/formatters/migrations.ts +12 -46
- package/src/utils/glyph-mode.ts +22 -0
- package/src/utils/integrity-violation-to-check-failure.ts +130 -0
- package/src/utils/plan-resolution.ts +134 -133
- package/src/utils/terminal-ui.ts +42 -1
- package/dist/cli-errors-Bw2GlweY.mjs +0 -175
- package/dist/cli-errors-Bw2GlweY.mjs.map +0 -1
- package/dist/client-UnIveZxZ.mjs.map +0 -1
- package/dist/command-helpers-CRfjbZRz.mjs.map +0 -1
- package/dist/commands/migration-check.mjs.map +0 -1
- package/dist/commands/migration-graph.mjs.map +0 -1
- package/dist/contract-enrichment-Dani0mMW.mjs.map +0 -1
- package/dist/contract-space-aggregate-loader-CGakRlKM.mjs +0 -160
- package/dist/contract-space-aggregate-loader-CGakRlKM.mjs.map +0 -1
- package/dist/global-flags-CdE7M0d9.d.mts +0 -15
- package/dist/global-flags-CdE7M0d9.d.mts.map +0 -1
- package/dist/init-ByoeQphC.mjs.map +0 -1
- package/dist/migration-plan-jdAHg_gK.mjs.map +0 -1
- package/dist/migrations-B7n518mT.mjs.map +0 -1
- package/dist/rolldown-runtime-twds-ZHy.mjs +0 -14
- package/dist/terminal-ui-BiB_8KNo.mjs +0 -379
- package/dist/terminal-ui-BiB_8KNo.mjs.map +0 -1
- package/dist/types-UWB2-rrw.d.mts.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { OperationPreview } from '@prisma-next/framework-components/control';
|
|
2
2
|
import { cyan, green, yellow } from 'colorette';
|
|
3
3
|
|
|
4
|
-
import type {
|
|
4
|
+
import type { PerSpaceExecutionEntry } from '../../control-api/types';
|
|
5
5
|
import type { GlobalFlags } from '../global-flags';
|
|
6
6
|
import { createColorFormatter, formatDim, isVerbose } from './helpers';
|
|
7
7
|
|
|
@@ -79,9 +79,9 @@ export interface MigrationCommandResult {
|
|
|
79
79
|
* (extensions alphabetically, then app). Surfaces per-space markers
|
|
80
80
|
* and the ops grouped by space, so the CLI summary can name which
|
|
81
81
|
* space each op and marker belongs to instead of flattening them
|
|
82
|
-
* into a single ambiguous list. See {@link
|
|
82
|
+
* into a single ambiguous list. See {@link PerSpaceExecutionEntry}.
|
|
83
83
|
*/
|
|
84
|
-
readonly perSpace?: ReadonlyArray<
|
|
84
|
+
readonly perSpace?: ReadonlyArray<PerSpaceExecutionEntry>;
|
|
85
85
|
readonly advancedRef?: { readonly name: string; readonly hash: string } | null;
|
|
86
86
|
readonly plannedAdvanceRef?: { readonly name: string; readonly hash: string } | null;
|
|
87
87
|
readonly summary: string;
|
|
@@ -101,7 +101,7 @@ export interface MigrationCommandResult {
|
|
|
101
101
|
* entirely (no marker has been written yet).
|
|
102
102
|
*/
|
|
103
103
|
export function formatPerSpaceBlock(
|
|
104
|
-
perSpace: ReadonlyArray<
|
|
104
|
+
perSpace: ReadonlyArray<PerSpaceExecutionEntry>,
|
|
105
105
|
mode: 'plan' | 'apply',
|
|
106
106
|
useColor: boolean,
|
|
107
107
|
): readonly string[] {
|
|
@@ -182,7 +182,7 @@ export function formatMigrationPlanOutput(
|
|
|
182
182
|
);
|
|
183
183
|
}
|
|
184
184
|
} else if (result.plan?.operations && result.plan.operations.length > 0) {
|
|
185
|
-
//
|
|
185
|
+
// App-only / no-aggregate-breakdown fallback. Same flat tree
|
|
186
186
|
// we've always rendered.
|
|
187
187
|
lines.push(`${formatDimText('│')}`);
|
|
188
188
|
for (let i = 0; i < result.plan.operations.length; i++) {
|
|
@@ -267,7 +267,7 @@ export interface MigrationApplyCommandOutputResult {
|
|
|
267
267
|
* alphabetically, then app). Always present for the aggregate-walking
|
|
268
268
|
* `migrate` command.
|
|
269
269
|
*/
|
|
270
|
-
readonly perSpace: readonly
|
|
270
|
+
readonly perSpace: readonly PerSpaceExecutionEntry[];
|
|
271
271
|
readonly timings?: {
|
|
272
272
|
readonly total: number;
|
|
273
273
|
};
|
|
@@ -314,9 +314,7 @@ export function formatMigrationApplyCommandOutput(
|
|
|
314
314
|
return lines.join('\n');
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
-
interface
|
|
318
|
-
readonly kind: 'present';
|
|
319
|
-
readonly spaceId: string;
|
|
317
|
+
interface MigrationShowPresent {
|
|
320
318
|
readonly dirName: string;
|
|
321
319
|
readonly dirPath: string;
|
|
322
320
|
readonly from: string | null;
|
|
@@ -332,22 +330,11 @@ interface MigrationShowSpacePresent {
|
|
|
332
330
|
readonly summary: string;
|
|
333
331
|
}
|
|
334
332
|
|
|
335
|
-
interface MigrationShowSpaceMissing {
|
|
336
|
-
readonly kind: 'missing';
|
|
337
|
-
readonly spaceId: string;
|
|
338
|
-
readonly summary: string;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
type MigrationShowSpaceResult = MigrationShowSpacePresent | MigrationShowSpaceMissing;
|
|
342
|
-
|
|
343
333
|
interface MigrationShowResult {
|
|
344
|
-
readonly
|
|
334
|
+
readonly migration: MigrationShowPresent;
|
|
345
335
|
}
|
|
346
336
|
|
|
347
|
-
function formatSpaceShowBlock(
|
|
348
|
-
space: MigrationShowSpacePresent,
|
|
349
|
-
useColor: boolean,
|
|
350
|
-
): readonly string[] {
|
|
337
|
+
function formatSpaceShowBlock(space: MigrationShowPresent, useColor: boolean): readonly string[] {
|
|
351
338
|
const formatGreen = createColorFormatter(useColor, green);
|
|
352
339
|
const formatYellow = createColorFormatter(useColor, yellow);
|
|
353
340
|
const formatDimText = (text: string) => formatDim(useColor, text);
|
|
@@ -403,28 +390,7 @@ export function formatMigrationShowOutput(result: MigrationShowResult, flags: Gl
|
|
|
403
390
|
}
|
|
404
391
|
|
|
405
392
|
const useColor = flags.color !== false;
|
|
406
|
-
|
|
407
|
-
const multipleSpaces = result.spaces.length > 1;
|
|
408
|
-
const lines: string[] = [];
|
|
409
|
-
|
|
410
|
-
for (let i = 0; i < result.spaces.length; i++) {
|
|
411
|
-
const space = result.spaces[i]!;
|
|
412
|
-
if (multipleSpaces) {
|
|
413
|
-
lines.push(formatDimText(`── ${space.spaceId} ──`));
|
|
414
|
-
}
|
|
415
|
-
if (space.kind === 'missing') {
|
|
416
|
-
lines.push(formatDimText(` ${space.summary}`));
|
|
417
|
-
} else {
|
|
418
|
-
for (const line of formatSpaceShowBlock(space, useColor)) {
|
|
419
|
-
lines.push(line);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
if (i < result.spaces.length - 1) {
|
|
423
|
-
lines.push('');
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
return lines.join('\n');
|
|
393
|
+
return formatSpaceShowBlock(result.migration, useColor).join('\n');
|
|
428
394
|
}
|
|
429
395
|
|
|
430
396
|
/**
|
|
@@ -475,8 +441,8 @@ export function formatMigrationApplyOutput(
|
|
|
475
441
|
),
|
|
476
442
|
);
|
|
477
443
|
} else if (result.marker) {
|
|
478
|
-
//
|
|
479
|
-
//
|
|
444
|
+
// App-only / no-aggregate-breakdown fallback (e.g. older callers
|
|
445
|
+
// / non-aggregate code paths). The label is
|
|
480
446
|
// `App-space marker` (not `Signature`) so that when only one
|
|
481
447
|
// marker is observable we still name what it covers explicitly.
|
|
482
448
|
lines.push(`${formatDimText(` App-space marker: ${result.marker.storageHash}`)}`);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type GlyphMode = 'unicode' | 'ascii';
|
|
2
|
+
|
|
3
|
+
export interface GlyphModeInput {
|
|
4
|
+
readonly isTTY: boolean;
|
|
5
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function localeString(env: Readonly<Record<string, string | undefined>>): string {
|
|
9
|
+
return env['LC_ALL'] ?? env['LC_CTYPE'] ?? env['LANG'] ?? '';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function isUtf8Locale(env: Readonly<Record<string, string | undefined>>): boolean {
|
|
13
|
+
const locale = localeString(env);
|
|
14
|
+
if (locale.length === 0) return false;
|
|
15
|
+
return /UTF-8|utf8/i.test(locale);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function detectGlyphMode(input: GlyphModeInput): GlyphMode {
|
|
19
|
+
if (!input.isTTY) return 'ascii';
|
|
20
|
+
if (!isUtf8Locale(input.env)) return 'ascii';
|
|
21
|
+
return 'unicode';
|
|
22
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { IntegrityViolation } from '@prisma-next/migration-tools/aggregate';
|
|
2
|
+
import { join, relative } from 'pathe';
|
|
3
|
+
|
|
4
|
+
export interface CheckFailure {
|
|
5
|
+
readonly pnCode: string;
|
|
6
|
+
readonly where: string;
|
|
7
|
+
readonly why: string;
|
|
8
|
+
readonly fix: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function migrationPathRelative(dirPath: string): string {
|
|
12
|
+
return relative(process.cwd(), dirPath);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function migrationFileRelative(dirPath: string, fileName: string): string {
|
|
16
|
+
return join(migrationPathRelative(dirPath), fileName);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Map one {@link IntegrityViolation} onto a `migration check` failure row.
|
|
21
|
+
* Sole catalogue mapping from integrity violations to `PN-MIG-CHECK-*`.
|
|
22
|
+
*/
|
|
23
|
+
export function integrityViolationToCheckFailure(
|
|
24
|
+
violation: IntegrityViolation,
|
|
25
|
+
migrationsDir: string,
|
|
26
|
+
): CheckFailure {
|
|
27
|
+
const spaceRelative = (spaceId: string): string =>
|
|
28
|
+
migrationPathRelative(join(migrationsDir, spaceId));
|
|
29
|
+
const packageRelative = (spaceId: string, dirName: string): string =>
|
|
30
|
+
migrationPathRelative(join(migrationsDir, spaceId, dirName));
|
|
31
|
+
const refRelative = (spaceId: string, refName: string): string =>
|
|
32
|
+
migrationPathRelative(join(migrationsDir, spaceId, 'refs', `${refName}.json`));
|
|
33
|
+
|
|
34
|
+
switch (violation.kind) {
|
|
35
|
+
case 'hashMismatch':
|
|
36
|
+
return {
|
|
37
|
+
pnCode: 'PN-MIG-CHECK-001',
|
|
38
|
+
where: migrationFileRelative(
|
|
39
|
+
join(migrationsDir, violation.spaceId, violation.dirName),
|
|
40
|
+
'migration.json',
|
|
41
|
+
),
|
|
42
|
+
why: `Stored hash ${violation.stored} does not match recomputed hash ${violation.computed}`,
|
|
43
|
+
fix: 'Re-emit the migration package or restore from version control.',
|
|
44
|
+
};
|
|
45
|
+
case 'providedInvariantsMismatch':
|
|
46
|
+
return {
|
|
47
|
+
pnCode: 'PN-MIG-CHECK-002',
|
|
48
|
+
where: packageRelative(violation.spaceId, violation.dirName),
|
|
49
|
+
why: `Migration "${violation.dirName}" providedInvariants in migration.json disagrees with ops.json.`,
|
|
50
|
+
fix: 'Re-emit the migration package so migration.json and ops.json agree.',
|
|
51
|
+
};
|
|
52
|
+
case 'packageUnloadable':
|
|
53
|
+
return {
|
|
54
|
+
pnCode: 'PN-MIG-CHECK-002',
|
|
55
|
+
where: packageRelative(violation.spaceId, violation.dirName),
|
|
56
|
+
why: `Migration "${violation.dirName}" could not be loaded: ${violation.detail}`,
|
|
57
|
+
fix: 'Re-emit the migration package or restore from version control.',
|
|
58
|
+
};
|
|
59
|
+
case 'sameSourceAndTarget':
|
|
60
|
+
return {
|
|
61
|
+
pnCode: 'PN-MIG-CHECK-007',
|
|
62
|
+
where: packageRelative(violation.spaceId, violation.dirName),
|
|
63
|
+
why: `Migration "${violation.dirName}" in space "${violation.spaceId}" has source equal to target (${violation.hash}) with no data invariant — a true no-op self-edge.`,
|
|
64
|
+
fix: 'Add a data operation if this self-edge was meant to carry a data invariant, or delete the migration if it is a true no-op.',
|
|
65
|
+
};
|
|
66
|
+
case 'orphanSpaceDir':
|
|
67
|
+
return {
|
|
68
|
+
pnCode: 'PN-MIG-CHECK-008',
|
|
69
|
+
where: spaceRelative(violation.spaceId),
|
|
70
|
+
why: `Contract-space directory "${violation.spaceId}" exists on disk but no extension declares it.`,
|
|
71
|
+
fix: 'Remove the orphan directory, or declare the extension in `extensionPacks`.',
|
|
72
|
+
};
|
|
73
|
+
case 'declaredButUnmigrated':
|
|
74
|
+
return {
|
|
75
|
+
pnCode: 'PN-MIG-CHECK-009',
|
|
76
|
+
where: spaceRelative(violation.spaceId),
|
|
77
|
+
why: `Extension "${violation.spaceId}" is declared in \`extensionPacks\` but has no on-disk migrations directory.`,
|
|
78
|
+
fix: 'Re-emit the extension contract-space artefacts with `prisma-next contract emit` and migration planning, or remove the extension from `extensionPacks` if it is unused.',
|
|
79
|
+
};
|
|
80
|
+
case 'headRefMissing':
|
|
81
|
+
return {
|
|
82
|
+
pnCode: 'PN-MIG-CHECK-010',
|
|
83
|
+
where: refRelative(violation.spaceId, 'head'),
|
|
84
|
+
why: `Head ref \`refs/head.json\` is missing for contract space "${violation.spaceId}".`,
|
|
85
|
+
fix: 'Re-emit the contract-space migrations and head ref artefacts, or restore `refs/head.json` from version control.',
|
|
86
|
+
};
|
|
87
|
+
case 'headRefNotInGraph':
|
|
88
|
+
return {
|
|
89
|
+
pnCode: 'PN-MIG-CHECK-011',
|
|
90
|
+
where: refRelative(violation.spaceId, 'head'),
|
|
91
|
+
why: `Head ref ${violation.hash} for contract space "${violation.spaceId}" is not present in its migration graph.`,
|
|
92
|
+
fix: 'Re-emit the contract space migrations, or restore the missing migration package.',
|
|
93
|
+
};
|
|
94
|
+
case 'refUnreadable':
|
|
95
|
+
return {
|
|
96
|
+
pnCode: 'PN-MIG-CHECK-012',
|
|
97
|
+
where: refRelative(violation.spaceId, violation.refName),
|
|
98
|
+
why: `Ref "${violation.refName}" for contract space "${violation.spaceId}" is unreadable: ${violation.detail}`,
|
|
99
|
+
fix: 'Repair or remove the corrupt ref file.',
|
|
100
|
+
};
|
|
101
|
+
case 'targetMismatch':
|
|
102
|
+
return {
|
|
103
|
+
pnCode: 'PN-MIG-CHECK-013',
|
|
104
|
+
where: spaceRelative(violation.spaceId),
|
|
105
|
+
why: `Contract space "${violation.spaceId}" targets "${violation.actual}" but the project targets "${violation.expected}".`,
|
|
106
|
+
fix: 'Update the extension to target the configured database, or change the project target.',
|
|
107
|
+
};
|
|
108
|
+
case 'disjointness':
|
|
109
|
+
return {
|
|
110
|
+
pnCode: 'PN-MIG-CHECK-014',
|
|
111
|
+
where: migrationPathRelative(migrationsDir),
|
|
112
|
+
why: `Storage element "${violation.element}" is claimed by multiple contract spaces: ${violation.claimedBy.join(', ')}.`,
|
|
113
|
+
fix: 'Update the contracts so each storage element is owned by exactly one contract space.',
|
|
114
|
+
};
|
|
115
|
+
case 'contractUnreadable':
|
|
116
|
+
return {
|
|
117
|
+
pnCode: 'PN-MIG-CHECK-015',
|
|
118
|
+
where: migrationFileRelative(join(migrationsDir, violation.spaceId), 'contract.json'),
|
|
119
|
+
why: `Contract for space "${violation.spaceId}" is unreadable: ${violation.detail}`,
|
|
120
|
+
fix: 'Re-emit the extension contract artefacts, or fix the descriptor producing the invalid contract.',
|
|
121
|
+
};
|
|
122
|
+
case 'duplicateMigrationHash':
|
|
123
|
+
return {
|
|
124
|
+
pnCode: 'PN-MIG-CHECK-016',
|
|
125
|
+
where: spaceRelative(violation.spaceId),
|
|
126
|
+
why: `Multiple migrations in space "${violation.spaceId}" share migrationHash "${violation.migrationHash}" (${violation.dirNames.join(', ')}).`,
|
|
127
|
+
fix: 'Re-emit one of the conflicting packages so each migrationHash is unique.',
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Contract } from '@prisma-next/contract/types';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ContractSpaceMember } from '@prisma-next/migration-tools/aggregate';
|
|
3
3
|
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
4
4
|
import type { MigrationGraph } from '@prisma-next/migration-tools/graph';
|
|
5
5
|
import {
|
|
@@ -7,20 +7,17 @@ import {
|
|
|
7
7
|
findLatestMigration,
|
|
8
8
|
isGraphNode,
|
|
9
9
|
} from '@prisma-next/migration-tools/migration-graph';
|
|
10
|
-
import type {
|
|
10
|
+
import type { ContractRef } from '@prisma-next/migration-tools/ref-resolution';
|
|
11
11
|
import { parseContractRef } from '@prisma-next/migration-tools/ref-resolution';
|
|
12
12
|
import type { Refs } from '@prisma-next/migration-tools/refs';
|
|
13
|
-
import { readRefSnapshot, readRefs } from '@prisma-next/migration-tools/refs';
|
|
14
13
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
15
14
|
import {
|
|
16
15
|
CliStructuredError,
|
|
17
|
-
errorContractValidationFailed,
|
|
18
16
|
errorPlanForgotTheFlag,
|
|
19
17
|
errorSnapshotMissing,
|
|
20
|
-
errorUnexpected,
|
|
21
|
-
mapMigrationToolsError,
|
|
22
18
|
mapRefResolutionError,
|
|
23
19
|
} from './cli-errors';
|
|
20
|
+
import { mapContractAtError } from './contract-at-errors';
|
|
24
21
|
|
|
25
22
|
const FULL_HASH_PATTERN = /^sha256:([0-9a-f]{64}|empty)$/;
|
|
26
23
|
|
|
@@ -48,15 +45,11 @@ export type FromResolution =
|
|
|
48
45
|
|
|
49
46
|
export interface ResolveFromForPlanInput {
|
|
50
47
|
readonly optionsFrom?: string | undefined;
|
|
51
|
-
readonly
|
|
52
|
-
readonly bundles: readonly OnDiskMigrationPackage[];
|
|
53
|
-
readonly graph: MigrationGraph;
|
|
54
|
-
readonly familyInstance: ControlFamilyInstance<string, unknown>;
|
|
55
|
-
readonly readBundleEndContract: (migrationDir: string) => Promise<Contract>;
|
|
48
|
+
readonly member: ContractSpaceMember;
|
|
56
49
|
}
|
|
57
50
|
|
|
58
|
-
function graphIsEmpty(
|
|
59
|
-
return
|
|
51
|
+
function graphIsEmpty(member: ContractSpaceMember): boolean {
|
|
52
|
+
return member.packages.length === 0;
|
|
60
53
|
}
|
|
61
54
|
|
|
62
55
|
function getReachableRefs(
|
|
@@ -86,158 +79,136 @@ export function assertFromIsGraphNode(
|
|
|
86
79
|
}
|
|
87
80
|
}
|
|
88
81
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (CliStructuredError.is(error)) {
|
|
97
|
-
return notOk(error);
|
|
82
|
+
type RefContractResolution =
|
|
83
|
+
| {
|
|
84
|
+
kind: 'snapshot';
|
|
85
|
+
hash: string;
|
|
86
|
+
contract: Contract;
|
|
87
|
+
contractJson: unknown;
|
|
88
|
+
contractDts: string;
|
|
98
89
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
90
|
+
| {
|
|
91
|
+
kind: 'graph-node';
|
|
92
|
+
hash: string;
|
|
93
|
+
contract: Contract;
|
|
94
|
+
contractJson: unknown;
|
|
95
|
+
contractDts: string;
|
|
96
|
+
sourceDir: string;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
async function resolveContractRef(
|
|
100
|
+
parsed: ContractRef,
|
|
101
|
+
member: ContractSpaceMember,
|
|
102
|
+
options?: { readonly explicitLabel?: string; readonly artifactRole?: 'from' | 'to' },
|
|
103
|
+
): Promise<Result<RefContractResolution, CliStructuredError>> {
|
|
104
|
+
const { hash, provenance } = parsed;
|
|
105
|
+
const refName = provenance.kind === 'ref' ? provenance.refName : undefined;
|
|
107
106
|
|
|
108
|
-
async function resolveGraphNodeFromBundle(
|
|
109
|
-
fromHash: string,
|
|
110
|
-
bundles: readonly OnDiskMigrationPackage[],
|
|
111
|
-
readBundleEndContract: (migrationDir: string) => Promise<Contract>,
|
|
112
|
-
explicitFromLabel?: string,
|
|
113
|
-
): Promise<Result<Extract<FromResolution, { kind: 'graph-node' }>, CliStructuredError>> {
|
|
114
|
-
const matchingBundle = bundles.find((pkg) => pkg.metadata.to === fromHash);
|
|
115
|
-
if (!matchingBundle) {
|
|
116
|
-
return notOk(
|
|
117
|
-
errorUnexpected(
|
|
118
|
-
explicitFromLabel
|
|
119
|
-
? `No migration bundle found for --from "${explicitFromLabel}" (resolved hash: ${fromHash})`
|
|
120
|
-
: `No migration bundle found for graph node ${fromHash}`,
|
|
121
|
-
{
|
|
122
|
-
why: `The hash ${fromHash} is a graph node but no on-disk migration package has an end-contract hash matching it.`,
|
|
123
|
-
fix: 'Provide a ref or hash that corresponds to an existing migration package, or run `migration list` to see available migrations.',
|
|
124
|
-
},
|
|
125
|
-
),
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
107
|
try {
|
|
129
|
-
const
|
|
108
|
+
const at = await member.contractAt(hash, refName !== undefined ? { refName } : undefined);
|
|
109
|
+
|
|
110
|
+
if (at.provenance === 'snapshot') {
|
|
111
|
+
return ok({
|
|
112
|
+
kind: 'snapshot',
|
|
113
|
+
hash: at.hash,
|
|
114
|
+
contract: at.contract,
|
|
115
|
+
contractJson: at.contractJson,
|
|
116
|
+
contractDts: at.contractDts,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
130
120
|
return ok({
|
|
131
121
|
kind: 'graph-node',
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
122
|
+
hash: at.hash,
|
|
123
|
+
contract: at.contract,
|
|
124
|
+
contractJson: at.contractJson,
|
|
125
|
+
contractDts: at.contractDts,
|
|
126
|
+
sourceDir: at.sourceDir,
|
|
135
127
|
});
|
|
136
128
|
} catch (error) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
129
|
+
return mapContractAtError(
|
|
130
|
+
error,
|
|
131
|
+
options?.artifactRole !== undefined ? { artifactRole: options.artifactRole } : undefined,
|
|
132
|
+
);
|
|
141
133
|
}
|
|
142
134
|
}
|
|
143
135
|
|
|
144
|
-
async function
|
|
145
|
-
|
|
146
|
-
fromHash: string,
|
|
136
|
+
async function resolveFromPolicy(
|
|
137
|
+
parsed: ContractRef,
|
|
147
138
|
input: ResolveFromForPlanInput,
|
|
148
139
|
refs: Refs,
|
|
140
|
+
explicitFromLabel?: string,
|
|
149
141
|
): Promise<Result<FromResolution, CliStructuredError>> {
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
snapshot = await readRefSnapshot(refsDir, refName);
|
|
157
|
-
} catch (error) {
|
|
158
|
-
if (MigrationToolsError.is(error)) {
|
|
159
|
-
return notOk(mapMigrationToolsError(error));
|
|
160
|
-
}
|
|
161
|
-
throw error;
|
|
142
|
+
const resolution = await resolveContractRef(parsed, input.member, {
|
|
143
|
+
...(explicitFromLabel !== undefined ? { explicitLabel: explicitFromLabel } : {}),
|
|
144
|
+
artifactRole: 'from',
|
|
145
|
+
});
|
|
146
|
+
if (!resolution.ok) {
|
|
147
|
+
return resolution;
|
|
162
148
|
}
|
|
163
149
|
|
|
164
|
-
if (
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (empty) {
|
|
172
|
-
return ok({ kind: 'auto-baseline', fromHash, fromContract, contractDts, contractJson });
|
|
173
|
-
}
|
|
174
|
-
try {
|
|
175
|
-
assertFromIsGraphNode(fromHash, graph, refs, graphTip);
|
|
176
|
-
} catch (error) {
|
|
177
|
-
if (CliStructuredError.is(error)) {
|
|
178
|
-
return notOk(error);
|
|
179
|
-
}
|
|
180
|
-
throw error;
|
|
181
|
-
}
|
|
182
|
-
return ok({ kind: 'snapshot', fromHash, fromContract, contractDts, contractJson });
|
|
150
|
+
if (resolution.value.kind === 'graph-node') {
|
|
151
|
+
return ok({
|
|
152
|
+
kind: 'graph-node',
|
|
153
|
+
fromHash: resolution.value.hash,
|
|
154
|
+
fromContract: resolution.value.contract,
|
|
155
|
+
sourceDir: resolution.value.sourceDir,
|
|
156
|
+
});
|
|
183
157
|
}
|
|
184
158
|
|
|
185
|
-
|
|
186
|
-
|
|
159
|
+
const { hash, contract, contractJson, contractDts } = resolution.value;
|
|
160
|
+
if (graphIsEmpty(input.member)) {
|
|
161
|
+
return ok({
|
|
162
|
+
kind: 'auto-baseline',
|
|
163
|
+
fromHash: hash,
|
|
164
|
+
fromContract: contract,
|
|
165
|
+
contractDts,
|
|
166
|
+
contractJson,
|
|
167
|
+
});
|
|
187
168
|
}
|
|
188
169
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const { bundles, graph } = input;
|
|
199
|
-
|
|
200
|
-
if (isGraphNode(fromHash, graph)) {
|
|
201
|
-
return resolveGraphNodeFromBundle(
|
|
202
|
-
fromHash,
|
|
203
|
-
bundles,
|
|
204
|
-
input.readBundleEndContract,
|
|
205
|
-
explicitFromLabel,
|
|
206
|
-
);
|
|
170
|
+
const graph = input.member.graph();
|
|
171
|
+
const graphTip = findLatestMigration(graph)?.to ?? null;
|
|
172
|
+
try {
|
|
173
|
+
assertFromIsGraphNode(hash, graph, refs, graphTip);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
if (CliStructuredError.is(error)) {
|
|
176
|
+
return notOk(error);
|
|
177
|
+
}
|
|
178
|
+
throw error;
|
|
207
179
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
180
|
+
return ok({
|
|
181
|
+
kind: 'snapshot',
|
|
182
|
+
fromHash: hash,
|
|
183
|
+
fromContract: contract,
|
|
184
|
+
contractDts,
|
|
185
|
+
contractJson,
|
|
186
|
+
});
|
|
212
187
|
}
|
|
213
188
|
|
|
214
189
|
export async function resolveFromForPlan(
|
|
215
190
|
input: ResolveFromForPlanInput,
|
|
216
191
|
): Promise<Result<FromResolution, CliStructuredError>> {
|
|
217
|
-
const { optionsFrom,
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
try {
|
|
221
|
-
refs = await readRefs(refsDir);
|
|
222
|
-
} catch (error) {
|
|
223
|
-
if (MigrationToolsError.is(error)) {
|
|
224
|
-
return notOk(mapMigrationToolsError(error));
|
|
225
|
-
}
|
|
226
|
-
throw error;
|
|
227
|
-
}
|
|
192
|
+
const { optionsFrom, member } = input;
|
|
193
|
+
const graph = member.graph();
|
|
194
|
+
const refs = member.refs;
|
|
228
195
|
|
|
229
196
|
if (optionsFrom === undefined) {
|
|
230
197
|
const dbRef = refs['db'];
|
|
231
198
|
if (!dbRef) {
|
|
232
199
|
return ok({ kind: 'greenfield', fromHash: null, fromContract: null });
|
|
233
200
|
}
|
|
234
|
-
return
|
|
201
|
+
return resolveFromPolicy(
|
|
202
|
+
{ hash: dbRef.hash, provenance: { kind: 'ref', refName: 'db' } },
|
|
203
|
+
input,
|
|
204
|
+
refs,
|
|
205
|
+
);
|
|
235
206
|
}
|
|
236
207
|
|
|
237
208
|
const refResult = parseContractRef(optionsFrom, { graph, refs });
|
|
238
209
|
if (!refResult.ok) {
|
|
239
210
|
if (looksLikeFullHash(optionsFrom)) {
|
|
240
|
-
const empty = graphIsEmpty(
|
|
211
|
+
const empty = graphIsEmpty(member);
|
|
241
212
|
const graphTip = findLatestMigration(graph)?.to ?? null;
|
|
242
213
|
if (empty) {
|
|
243
214
|
return notOk(errorSnapshotMissing(optionsFrom, { viaRef: false }));
|
|
@@ -247,11 +218,41 @@ export async function resolveFromForPlan(
|
|
|
247
218
|
return notOk(mapRefResolutionError(refResult.failure));
|
|
248
219
|
}
|
|
249
220
|
|
|
250
|
-
|
|
221
|
+
return resolveFromPolicy(refResult.value, input, refs, optionsFrom);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export interface ResolveToForPlanInput {
|
|
225
|
+
readonly member: ContractSpaceMember;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export interface ResolvedContractRef {
|
|
229
|
+
readonly hash: string;
|
|
230
|
+
readonly contract: Contract;
|
|
231
|
+
readonly contractJson: unknown;
|
|
232
|
+
readonly contractDts: string;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export async function resolveToForPlan(
|
|
236
|
+
optionsTo: string,
|
|
237
|
+
input: ResolveToForPlanInput,
|
|
238
|
+
): Promise<Result<ResolvedContractRef, CliStructuredError>> {
|
|
239
|
+
const { member } = input;
|
|
240
|
+
const graph = member.graph();
|
|
241
|
+
const refs = member.refs;
|
|
242
|
+
|
|
243
|
+
const refResult = parseContractRef(optionsTo, { graph, refs });
|
|
244
|
+
if (!refResult.ok) {
|
|
245
|
+
return notOk(mapRefResolutionError(refResult.failure));
|
|
246
|
+
}
|
|
251
247
|
|
|
252
|
-
|
|
253
|
-
|
|
248
|
+
const resolution = await resolveContractRef(refResult.value, member, {
|
|
249
|
+
explicitLabel: optionsTo,
|
|
250
|
+
artifactRole: 'to',
|
|
251
|
+
});
|
|
252
|
+
if (!resolution.ok) {
|
|
253
|
+
return resolution;
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
|
|
256
|
+
const { hash, contract, contractJson, contractDts } = resolution.value;
|
|
257
|
+
return ok({ hash, contract, contractJson, contractDts });
|
|
257
258
|
}
|