@prisma-next/cli 0.11.0 → 0.12.0
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 +259 -12
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-oXO2WCPD.mjs → client-KgJorIvG.mjs} +72 -60
- package/dist/client-KgJorIvG.mjs.map +1 -0
- package/dist/{command-helpers-BSb0tRC8.mjs → command-helpers-Bbw1GbwL.mjs} +646 -46
- 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 +32 -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 +41 -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 +6 -2
- package/dist/commands/migrate.d.mts.map +1 -1
- package/dist/commands/migrate.mjs +75 -40
- 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 +3 -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 +85 -81
- 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 +38 -10
- 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-bcrpT-wD.mjs → contract-emit-D-4jrNve.mjs} +25 -10
- package/dist/contract-emit-D-4jrNve.mjs.map +1 -0
- package/dist/{contract-emit-r4y8Zhf1.mjs → contract-emit-DxcGl4Uq.mjs} +19 -14
- package/dist/contract-emit-DxcGl4Uq.mjs.map +1 -0
- 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-BmySmqVT.mjs → contract-infer-D8uEbJuu.mjs} +4 -5
- package/dist/{contract-infer-BmySmqVT.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-BClPs3ph.mjs → db-verify-v_vUKXTU.mjs} +5 -7
- package/dist/{db-verify-BClPs3ph.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-65gOHkHB.mjs → framework-components-fYXjz_in.mjs} +2 -2
- package/dist/{framework-components-65gOHkHB.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-DJVv0_uf.mjs → graph-render-rFAqZujX.mjs} +2 -2
- package/dist/{graph-render-DJVv0_uf.mjs.map → graph-render-rFAqZujX.mjs.map} +1 -1
- package/dist/{init-BCJZPWE1.mjs → init-Cv9UzWL5.mjs} +20 -269
- package/dist/init-Cv9UzWL5.mjs.map +1 -0
- package/dist/{inspect-live-schema-DSRbFoOL.mjs → inspect-live-schema-C6ohV_oQ.mjs} +4 -5
- package/dist/{inspect-live-schema-DSRbFoOL.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-Bzd9La5c.mjs → migration-command-scaffold-CjvwO6at.mjs} +4 -5
- package/dist/{migration-command-scaffold-Bzd9La5c.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-CFwqw3Gk.mjs → migration-plan-9DJ7q7_z.mjs} +372 -133
- 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-CwZMa1Ck.mjs → migrations-Cv2jxNNK.mjs} +12 -13
- package/dist/migrations-Cv2jxNNK.mjs.map +1 -0
- package/dist/{output-BlsrGMEF.mjs → output-B60Gw5fu.mjs} +1 -1
- package/dist/{output-BlsrGMEF.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-DUZqsue6.mjs +50 -0
- package/dist/ref-advancement-DUZqsue6.mjs.map +1 -0
- package/dist/terminal-ui-5Y6mrg93.d.mts +133 -0
- package/dist/terminal-ui-5Y6mrg93.d.mts.map +1 -0
- package/dist/{types--CqjMdk0.d.mts → types-Dt_SfqFm.d.mts} +28 -28
- package/dist/types-Dt_SfqFm.d.mts.map +1 -0
- package/dist/{verify-Bom75OYI.mjs → verify-DCA9Sldu.mjs} +2 -2
- package/dist/{verify-Bom75OYI.mjs.map → verify-DCA9Sldu.mjs.map} +1 -1
- package/package.json +35 -24
- package/src/commands/contract-emit.ts +19 -7
- package/src/commands/contract-infer.ts +1 -1
- package/src/commands/db-init.ts +48 -2
- package/src/commands/db-sign.ts +9 -5
- package/src/commands/db-update.ts +54 -8
- package/src/commands/init/hygiene-gitattributes.ts +2 -2
- package/src/commands/init/index.ts +2 -1
- package/src/commands/init/templates/code-templates.ts +4 -2
- package/src/commands/init/templates/env.ts +13 -14
- package/src/commands/migrate.ts +125 -44
- 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 +412 -197
- package/src/commands/migration-show.ts +65 -284
- package/src/commands/migration-status.ts +127 -124
- package/src/commands/ref.ts +53 -8
- 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 +14 -6
- 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 +26 -27
- package/src/migration-cli.ts +4 -4
- package/src/utils/cli-errors.ts +234 -0
- package/src/utils/command-helpers.ts +9 -24
- 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 +37 -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 +258 -0
- package/src/utils/ref-advancement.ts +68 -0
- package/src/utils/terminal-ui.ts +42 -1
- package/dist/cli-errors-Czmx92Zy.d.mts +0 -3
- package/dist/cli-errors-Djtz98Vm.mjs +0 -71
- package/dist/cli-errors-Djtz98Vm.mjs.map +0 -1
- package/dist/client-oXO2WCPD.mjs.map +0 -1
- package/dist/command-helpers-BSb0tRC8.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-emit-bcrpT-wD.mjs.map +0 -1
- package/dist/contract-emit-r4y8Zhf1.mjs.map +0 -1
- package/dist/contract-enrichment-Dani0mMW.mjs.map +0 -1
- package/dist/contract-space-aggregate-loader-BmNQwlws.mjs +0 -160
- package/dist/contract-space-aggregate-loader-BmNQwlws.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-BCJZPWE1.mjs.map +0 -1
- package/dist/migration-plan-CFwqw3Gk.mjs.map +0 -1
- package/dist/migrations-CwZMa1Ck.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--CqjMdk0.d.mts.map +0 -1
|
@@ -6,16 +6,10 @@ import {
|
|
|
6
6
|
type MigrationPlanOperation,
|
|
7
7
|
type OperationPreview,
|
|
8
8
|
} from '@prisma-next/framework-components/control';
|
|
9
|
-
import {
|
|
10
|
-
import { readMigrationPackage, readMigrationsDir } from '@prisma-next/migration-tools/io';
|
|
11
|
-
import {
|
|
12
|
-
findLatestMigration,
|
|
13
|
-
reconstructGraph,
|
|
14
|
-
} from '@prisma-next/migration-tools/migration-graph';
|
|
9
|
+
import { loadContractSpaceAggregate } from '@prisma-next/migration-tools/aggregate';
|
|
15
10
|
import type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';
|
|
16
11
|
import { parseMigrationRef } from '@prisma-next/migration-tools/ref-resolution';
|
|
17
|
-
import {
|
|
18
|
-
import { spaceMigrationDirectory } from '@prisma-next/migration-tools/spaces';
|
|
12
|
+
import { castAs } from '@prisma-next/utils/casts';
|
|
19
13
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
20
14
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
21
15
|
import { Command } from 'commander';
|
|
@@ -28,7 +22,6 @@ import {
|
|
|
28
22
|
errorFileNotFound,
|
|
29
23
|
errorRuntime,
|
|
30
24
|
errorUnexpected,
|
|
31
|
-
mapMigrationToolsError,
|
|
32
25
|
mapRefResolutionError,
|
|
33
26
|
} from '../utils/cli-errors';
|
|
34
27
|
import {
|
|
@@ -39,7 +32,6 @@ import {
|
|
|
39
32
|
setCommandExamples,
|
|
40
33
|
setCommandSeeAlso,
|
|
41
34
|
} from '../utils/command-helpers';
|
|
42
|
-
import { buildContractSpaceAggregate } from '../utils/contract-space-aggregate-loader';
|
|
43
35
|
import { formatMigrationShowOutput } from '../utils/formatters/migrations';
|
|
44
36
|
import { formatStyledHeader } from '../utils/formatters/styled';
|
|
45
37
|
import type { CommonCommandOptions } from '../utils/global-flags';
|
|
@@ -51,11 +43,7 @@ interface MigrationShowOptions extends CommonCommandOptions {
|
|
|
51
43
|
readonly config?: string;
|
|
52
44
|
}
|
|
53
45
|
|
|
54
|
-
|
|
55
|
-
* Details of one space's latest (or targeted) migration package.
|
|
56
|
-
*/
|
|
57
|
-
export interface MigrationShowSpacePresent {
|
|
58
|
-
readonly kind: 'present';
|
|
46
|
+
export interface MigrationShowPresent {
|
|
59
47
|
readonly spaceId: string;
|
|
60
48
|
readonly dirName: string;
|
|
61
49
|
readonly dirPath: string;
|
|
@@ -68,56 +56,19 @@ export interface MigrationShowSpacePresent {
|
|
|
68
56
|
readonly label: string;
|
|
69
57
|
readonly operationClass: string;
|
|
70
58
|
}[];
|
|
71
|
-
/**
|
|
72
|
-
* Family-agnostic textual preview of the migration's operations. Always
|
|
73
|
-
* defined; statements is empty for a no-op migration or a family that does
|
|
74
|
-
* not implement the `OperationPreviewCapable` capability.
|
|
75
|
-
*/
|
|
76
59
|
readonly preview: OperationPreview;
|
|
77
60
|
readonly summary: string;
|
|
78
61
|
}
|
|
79
62
|
|
|
80
|
-
/**
|
|
81
|
-
* Placeholder for a loaded contract space that has no on-disk migration
|
|
82
|
-
* package — the extension descriptor declared the space but no migrations
|
|
83
|
-
* directory has been materialised for it yet. Surfaces the space in the
|
|
84
|
-
* response so JSON consumers see every loaded extension instead of having
|
|
85
|
-
* silently-skipped entries.
|
|
86
|
-
*/
|
|
87
|
-
export interface MigrationShowSpaceMissing {
|
|
88
|
-
readonly kind: 'missing';
|
|
89
|
-
readonly spaceId: string;
|
|
90
|
-
readonly summary: string;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export type MigrationShowSpaceResult = MigrationShowSpacePresent | MigrationShowSpaceMissing;
|
|
94
|
-
|
|
95
63
|
export interface MigrationShowResult {
|
|
96
64
|
readonly ok: true;
|
|
97
|
-
|
|
98
|
-
* Per-space results, ordered: app first, then extensions alphabetically
|
|
99
|
-
* (matching the aggregate's canonical ordering).
|
|
100
|
-
*/
|
|
101
|
-
readonly spaces: readonly MigrationShowSpaceResult[];
|
|
65
|
+
readonly migration: MigrationShowPresent;
|
|
102
66
|
}
|
|
103
67
|
|
|
104
68
|
function looksLikePath(target: string): boolean {
|
|
105
69
|
return target.includes('/') || target.includes('\\');
|
|
106
70
|
}
|
|
107
71
|
|
|
108
|
-
/**
|
|
109
|
-
* Validate that a path-like `migration show` target resolves inside the app
|
|
110
|
-
* migrations directory. The returned result is always emitted under
|
|
111
|
-
* `aggregate.app.spaceId`, so accepting an extension-space (or otherwise
|
|
112
|
-
* external) path here would silently mislabel the result. Returns the
|
|
113
|
-
* resolved absolute path on success.
|
|
114
|
-
*
|
|
115
|
-
* `pathe.relative` can return an absolute path when the target cannot be
|
|
116
|
-
* expressed relative to the base (e.g. on Windows when `target` is on a
|
|
117
|
-
* different drive than `appMigrationsDir`). That case does not start with
|
|
118
|
-
* `..`, so the absolute-check below is required to reject cross-drive
|
|
119
|
-
* targets rather than mislabeling them as app-space.
|
|
120
|
-
*/
|
|
121
72
|
export function resolveAppTargetPath(
|
|
122
73
|
target: string,
|
|
123
74
|
appMigrationsDir: string,
|
|
@@ -141,83 +92,14 @@ export function resolveAppTargetPath(
|
|
|
141
92
|
return ok(targetPath);
|
|
142
93
|
}
|
|
143
94
|
|
|
144
|
-
|
|
145
|
-
packages: readonly OnDiskMigrationPackage[],
|
|
146
|
-
prefix: string,
|
|
147
|
-
): Result<OnDiskMigrationPackage, CliStructuredError> {
|
|
148
|
-
const normalizedPrefix = prefix.startsWith('sha256:') ? prefix : `sha256:${prefix}`;
|
|
149
|
-
const matches = packages.filter((p) => p.metadata.migrationHash.startsWith(normalizedPrefix));
|
|
150
|
-
|
|
151
|
-
if (matches.length === 1) {
|
|
152
|
-
return ok(matches[0]!);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (matches.length === 0) {
|
|
156
|
-
return notOk(
|
|
157
|
-
errorRuntime('No migration found matching prefix', {
|
|
158
|
-
why: `No migration has a migrationHash starting with "${normalizedPrefix}"`,
|
|
159
|
-
fix: 'Run `prisma-next migration show` (no argument) to see the latest migration, or check the migrations directory for available packages.',
|
|
160
|
-
}),
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const candidates = matches.map((p) => ` ${p.dirName} ${p.metadata.migrationHash}`).join('\n');
|
|
165
|
-
return notOk(
|
|
166
|
-
errorRuntime('Ambiguous hash prefix', {
|
|
167
|
-
why: `Multiple migrations match prefix "${normalizedPrefix}":\n${candidates}`,
|
|
168
|
-
fix: 'Provide a longer prefix to uniquely identify the migration.',
|
|
169
|
-
}),
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Resolve the latest migration from a space directory.
|
|
175
|
-
*
|
|
176
|
-
* Returns `ok(null)` only when the directory is empty or absent (ENOENT is
|
|
177
|
-
* absorbed by `readMigrationsDir`). If `readMigrationsDir` returned packages
|
|
178
|
-
* but `findLatestMigration` cannot pick a leaf, the on-disk history is
|
|
179
|
-
* corrupt — return a runtime error rather than collapsing it to a `missing`
|
|
180
|
-
* placeholder, which would hide the corruption from the caller.
|
|
181
|
-
*/
|
|
182
|
-
export async function resolveLatestFromDir(
|
|
183
|
-
spaceDir: string,
|
|
184
|
-
): Promise<Result<OnDiskMigrationPackage | null, CliStructuredError>> {
|
|
185
|
-
try {
|
|
186
|
-
const allPackages = await readMigrationsDir(spaceDir);
|
|
187
|
-
if (allPackages.length === 0) return ok(null);
|
|
188
|
-
const graph = reconstructGraph(allPackages);
|
|
189
|
-
const latestMigration = findLatestMigration(graph);
|
|
190
|
-
if (!latestMigration) {
|
|
191
|
-
return notOk(
|
|
192
|
-
errorRuntime('Could not resolve latest migration', {
|
|
193
|
-
why: `No latest migration found in ${relative(process.cwd(), spaceDir)}`,
|
|
194
|
-
fix: 'The migrations directory may be corrupted. Inspect the migration.json files.',
|
|
195
|
-
}),
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
const leafPkg = allPackages.find(
|
|
199
|
-
(p) => p.metadata.migrationHash === latestMigration.migrationHash,
|
|
200
|
-
);
|
|
201
|
-
return ok(leafPkg ?? null);
|
|
202
|
-
} catch (error) {
|
|
203
|
-
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
204
|
-
return notOk(
|
|
205
|
-
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
206
|
-
why: `Failed to read migrations: ${error instanceof Error ? error.message : String(error)}`,
|
|
207
|
-
}),
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function pkgToSpaceResult(
|
|
95
|
+
function pkgToPresent(
|
|
213
96
|
spaceId: string,
|
|
214
97
|
pkg: OnDiskMigrationPackage,
|
|
215
98
|
client: ReturnType<typeof createControlClient>,
|
|
216
|
-
):
|
|
217
|
-
const ops =
|
|
99
|
+
): MigrationShowPresent {
|
|
100
|
+
const ops = castAs<readonly MigrationPlanOperation[]>(pkg.ops);
|
|
218
101
|
const preview: OperationPreview = client.toOperationPreview(ops) ?? { statements: [] };
|
|
219
102
|
return {
|
|
220
|
-
kind: 'present',
|
|
221
103
|
spaceId,
|
|
222
104
|
dirName: pkg.dirName,
|
|
223
105
|
dirPath: relative(process.cwd(), pkg.dirPath),
|
|
@@ -235,40 +117,42 @@ function pkgToSpaceResult(
|
|
|
235
117
|
};
|
|
236
118
|
}
|
|
237
119
|
|
|
120
|
+
function findPackageByDirPath(
|
|
121
|
+
packages: readonly OnDiskMigrationPackage[],
|
|
122
|
+
resolvedDirPath: string,
|
|
123
|
+
): OnDiskMigrationPackage | undefined {
|
|
124
|
+
const normalized = resolve(resolvedDirPath);
|
|
125
|
+
return packages.find((p) => resolve(p.dirPath) === normalized);
|
|
126
|
+
}
|
|
127
|
+
|
|
238
128
|
async function executeMigrationShowCommand(
|
|
239
|
-
target: string
|
|
129
|
+
target: string,
|
|
240
130
|
options: MigrationShowOptions,
|
|
241
131
|
flags: GlobalFlags,
|
|
242
132
|
ui: TerminalUI,
|
|
243
133
|
): Promise<Result<MigrationShowResult, CliStructuredError>> {
|
|
244
134
|
const config = await loadConfig(options.config);
|
|
245
|
-
const { configPath, migrationsDir, appMigrationsDir, appMigrationsRelative
|
|
135
|
+
const { configPath, migrationsDir, appMigrationsDir, appMigrationsRelative } =
|
|
246
136
|
resolveMigrationPaths(options.config, config);
|
|
247
137
|
|
|
248
138
|
const contractPathAbsolute = resolveContractPath(config);
|
|
249
139
|
const contractPath = relative(process.cwd(), contractPathAbsolute);
|
|
250
140
|
|
|
251
141
|
if (!flags.json && !flags.quiet) {
|
|
252
|
-
const details: Array<{ label: string; value: string }> = [
|
|
253
|
-
{ label: 'config', value: configPath },
|
|
254
|
-
{ label: 'contract', value: contractPath },
|
|
255
|
-
{ label: 'migrations', value: appMigrationsRelative },
|
|
256
|
-
];
|
|
257
|
-
if (target) {
|
|
258
|
-
details.push({ label: 'target', value: target });
|
|
259
|
-
}
|
|
260
142
|
const header = formatStyledHeader({
|
|
261
143
|
command: 'migration show',
|
|
262
144
|
description: 'Display migration package contents',
|
|
263
|
-
details
|
|
145
|
+
details: [
|
|
146
|
+
{ label: 'config', value: configPath },
|
|
147
|
+
{ label: 'contract', value: contractPath },
|
|
148
|
+
{ label: 'migrations', value: appMigrationsRelative },
|
|
149
|
+
{ label: 'target', value: target },
|
|
150
|
+
],
|
|
264
151
|
flags,
|
|
265
152
|
});
|
|
266
153
|
ui.stderr(header);
|
|
267
154
|
}
|
|
268
155
|
|
|
269
|
-
// `migration show` is an offline command; the control client is constructed
|
|
270
|
-
// purely to dispatch the family-specific `toOperationPreview` capability and
|
|
271
|
-
// is not connected to a database.
|
|
272
156
|
const client = createControlClient({
|
|
273
157
|
family: config.family,
|
|
274
158
|
target: config.target,
|
|
@@ -277,85 +161,11 @@ async function executeMigrationShowCommand(
|
|
|
277
161
|
extensionPacks: config.extensionPacks ?? [],
|
|
278
162
|
});
|
|
279
163
|
|
|
280
|
-
// Explicit-target path. Read the app-space migrations directory directly
|
|
281
|
-
// and resolve `target` against the app graph. We deliberately skip
|
|
282
|
-
// `buildContractSpaceAggregate` here for two reasons:
|
|
283
|
-
//
|
|
284
|
-
// 1. Functional: the user asked about ONE specific migration. They don't
|
|
285
|
-
// need extension-space enumeration; resolving + rendering the named
|
|
286
|
-
// package is enough.
|
|
287
|
-
// 2. UX: the aggregate's layout-integrity check (PN-MIG-5001) fires when
|
|
288
|
-
// an extension is declared but its migrations directory hasn't been
|
|
289
|
-
// materialised. Gating an offline read-only inspect command on that
|
|
290
|
-
// check forces users to run `migrate` against a database before they
|
|
291
|
-
// can see what a migration contains — which contradicts what an
|
|
292
|
-
// offline read-only verb should require.
|
|
293
|
-
//
|
|
294
|
-
// Same pattern as `migration list`, `migration graph`, `migration check`:
|
|
295
|
-
// those verbs read `appMigrationsDir` directly without ever consulting
|
|
296
|
-
// the aggregate.
|
|
297
|
-
if (target) {
|
|
298
|
-
try {
|
|
299
|
-
let appPkg: OnDiskMigrationPackage;
|
|
300
|
-
if (looksLikePath(target)) {
|
|
301
|
-
const resolved = resolveAppTargetPath(target, appMigrationsDir, appMigrationsRelative);
|
|
302
|
-
if (!resolved.ok) return resolved;
|
|
303
|
-
appPkg = await readMigrationPackage(resolved.value);
|
|
304
|
-
} else {
|
|
305
|
-
const allPackages = await readMigrationsDir(appMigrationsDir);
|
|
306
|
-
if (allPackages.length === 0) {
|
|
307
|
-
return notOk(
|
|
308
|
-
errorRuntime('No migrations found', {
|
|
309
|
-
why: `No migration packages found in ${appMigrationsRelative}`,
|
|
310
|
-
fix: 'Run `prisma-next migration plan` to create a migration first.',
|
|
311
|
-
}),
|
|
312
|
-
);
|
|
313
|
-
}
|
|
314
|
-
const graph = reconstructGraph(allPackages);
|
|
315
|
-
const refs = await readRefs(refsDir);
|
|
316
|
-
const migResult = parseMigrationRef(target, { graph, refs });
|
|
317
|
-
if (!migResult.ok) {
|
|
318
|
-
return notOk(mapRefResolutionError(migResult.failure));
|
|
319
|
-
}
|
|
320
|
-
const matchedPkg = allPackages.find(
|
|
321
|
-
(p) => p.metadata.migrationHash === migResult.value.migrationHash,
|
|
322
|
-
);
|
|
323
|
-
if (!matchedPkg) {
|
|
324
|
-
return notOk(
|
|
325
|
-
errorRuntime('Migration package not found', {
|
|
326
|
-
why: `Resolved migration "${migResult.value.dirName}" but the package was not loaded`,
|
|
327
|
-
fix: 'The migrations directory may be corrupted. Inspect the migration.json files.',
|
|
328
|
-
}),
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
appPkg = matchedPkg;
|
|
332
|
-
}
|
|
333
|
-
return ok({
|
|
334
|
-
ok: true,
|
|
335
|
-
spaces: [pkgToSpaceResult(APP_SPACE_ID, appPkg, client)],
|
|
336
|
-
});
|
|
337
|
-
} catch (error) {
|
|
338
|
-
if (MigrationToolsError.is(error)) {
|
|
339
|
-
return notOk(mapMigrationToolsError(error));
|
|
340
|
-
}
|
|
341
|
-
return notOk(
|
|
342
|
-
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
343
|
-
why: `Failed to read app-space migration: ${error instanceof Error ? error.message : String(error)}`,
|
|
344
|
-
}),
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// No-target path. Enumerate the latest migration per space (app +
|
|
350
|
-
// extensions). The aggregate-loader is needed here because we need to
|
|
351
|
-
// know which extension spaces are declared; its layout-integrity check
|
|
352
|
-
// is appropriate at this entry point because the user is asking the
|
|
353
|
-
// system to report on every loaded space.
|
|
354
164
|
let contractJsonContent: string;
|
|
355
165
|
try {
|
|
356
166
|
contractJsonContent = await readFile(contractPathAbsolute, 'utf-8');
|
|
357
167
|
} catch (error) {
|
|
358
|
-
if (error instanceof Error && (error as
|
|
168
|
+
if (error instanceof Error && (error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
359
169
|
return notOk(
|
|
360
170
|
errorFileNotFound(contractPathAbsolute, {
|
|
361
171
|
why: `Contract file not found at ${contractPathAbsolute}`,
|
|
@@ -370,15 +180,14 @@ async function executeMigrationShowCommand(
|
|
|
370
180
|
);
|
|
371
181
|
}
|
|
372
182
|
|
|
373
|
-
// Construct the family instance up-front so the on-disk app contract
|
|
374
|
-
// read crosses the serializer seam (`familyInstance.deserializeContract`)
|
|
375
|
-
// at the read site. See TML-2536.
|
|
376
183
|
const stack = createControlStack(config);
|
|
377
184
|
const familyInstance = config.family.create(stack);
|
|
378
185
|
|
|
379
186
|
let appContract: Contract;
|
|
380
187
|
try {
|
|
381
|
-
appContract = familyInstance.deserializeContract(
|
|
188
|
+
appContract = familyInstance.deserializeContract(
|
|
189
|
+
castAs<unknown>(JSON.parse(contractJsonContent)),
|
|
190
|
+
);
|
|
382
191
|
} catch (error) {
|
|
383
192
|
return notOk(
|
|
384
193
|
errorContractValidationFailed(
|
|
@@ -388,85 +197,61 @@ async function executeMigrationShowCommand(
|
|
|
388
197
|
);
|
|
389
198
|
}
|
|
390
199
|
|
|
391
|
-
const
|
|
392
|
-
targetId: config.target.targetId,
|
|
200
|
+
const aggregate = await loadContractSpaceAggregate({
|
|
393
201
|
migrationsDir,
|
|
394
202
|
appContract,
|
|
395
|
-
extensionPacks: config.extensionPacks ?? [],
|
|
396
203
|
deserializeContract: (json: unknown) => familyInstance.deserializeContract(json),
|
|
397
204
|
});
|
|
398
|
-
if (!aggregateResult.ok) {
|
|
399
|
-
return notOk(aggregateResult.failure);
|
|
400
|
-
}
|
|
401
|
-
const aggregate = aggregateResult.value;
|
|
402
205
|
|
|
403
|
-
const
|
|
206
|
+
const packages = aggregate.app.packages;
|
|
207
|
+
const graph = aggregate.app.graph();
|
|
208
|
+
const refs = aggregate.app.refs;
|
|
404
209
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
const
|
|
408
|
-
if (
|
|
210
|
+
let appPkg: OnDiskMigrationPackage;
|
|
211
|
+
if (looksLikePath(target)) {
|
|
212
|
+
const resolved = resolveAppTargetPath(target, appMigrationsDir, appMigrationsRelative);
|
|
213
|
+
if (!resolved.ok) return resolved;
|
|
214
|
+
const matched = findPackageByDirPath(packages, resolved.value);
|
|
215
|
+
if (!matched) {
|
|
409
216
|
return notOk(
|
|
410
|
-
errorRuntime('
|
|
411
|
-
why: `No migration
|
|
412
|
-
fix: '
|
|
217
|
+
errorRuntime('Migration package not found', {
|
|
218
|
+
why: `No loaded migration package at ${relative(process.cwd(), resolved.value)}`,
|
|
219
|
+
fix: 'Pass a directory name, hash prefix, or path to an on-disk app-space migration package.',
|
|
413
220
|
}),
|
|
414
221
|
);
|
|
415
222
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
if (
|
|
223
|
+
appPkg = matched;
|
|
224
|
+
} else {
|
|
225
|
+
if (packages.length === 0) {
|
|
419
226
|
return notOk(
|
|
420
|
-
errorRuntime('
|
|
421
|
-
why:
|
|
422
|
-
fix: '
|
|
227
|
+
errorRuntime('No migrations found', {
|
|
228
|
+
why: `No migration packages found in ${appMigrationsRelative}`,
|
|
229
|
+
fix: 'Run `prisma-next migration plan` to create a migration first.',
|
|
423
230
|
}),
|
|
424
231
|
);
|
|
425
232
|
}
|
|
426
|
-
const
|
|
427
|
-
|
|
233
|
+
const migResult = parseMigrationRef(target, { graph, refs });
|
|
234
|
+
if (!migResult.ok) {
|
|
235
|
+
return notOk(mapRefResolutionError(migResult.failure));
|
|
236
|
+
}
|
|
237
|
+
const matchedPkg = packages.find(
|
|
238
|
+
(p) => p.metadata.migrationHash === migResult.value.migrationHash,
|
|
428
239
|
);
|
|
429
|
-
if (!
|
|
240
|
+
if (!matchedPkg) {
|
|
430
241
|
return notOk(
|
|
431
|
-
errorRuntime('
|
|
432
|
-
why: `
|
|
242
|
+
errorRuntime('Migration package not found', {
|
|
243
|
+
why: `Resolved migration "${migResult.value.dirName}" but the package was not loaded`,
|
|
433
244
|
fix: 'The migrations directory may be corrupted. Inspect the migration.json files.',
|
|
434
245
|
}),
|
|
435
246
|
);
|
|
436
247
|
}
|
|
437
|
-
|
|
438
|
-
} catch (error) {
|
|
439
|
-
if (MigrationToolsError.is(error)) {
|
|
440
|
-
return notOk(mapMigrationToolsError(error));
|
|
441
|
-
}
|
|
442
|
-
return notOk(
|
|
443
|
-
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
444
|
-
why: `Failed to read app-space migration: ${error instanceof Error ? error.message : String(error)}`,
|
|
445
|
-
}),
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
// Extension spaces: always emit one entry per loaded extension so the
|
|
450
|
-
// response enumerates every space the aggregate knows about. Spaces
|
|
451
|
-
// with no on-disk migration package yet (e.g. an extension was declared
|
|
452
|
-
// but never `migrate`d) become `kind: 'missing'` placeholders instead
|
|
453
|
-
// of being silently skipped.
|
|
454
|
-
for (const ext of aggregate.extensions) {
|
|
455
|
-
const extSpaceDir = spaceMigrationDirectory(migrationsDir, ext.spaceId);
|
|
456
|
-
const extPkgResult = await resolveLatestFromDir(extSpaceDir);
|
|
457
|
-
if (!extPkgResult.ok) return extPkgResult;
|
|
458
|
-
if (extPkgResult.value !== null) {
|
|
459
|
-
spaces.push(pkgToSpaceResult(ext.spaceId, extPkgResult.value, client));
|
|
460
|
-
} else {
|
|
461
|
-
spaces.push({
|
|
462
|
-
kind: 'missing',
|
|
463
|
-
spaceId: ext.spaceId,
|
|
464
|
-
summary: 'No on-disk migration package for this space',
|
|
465
|
-
});
|
|
466
|
-
}
|
|
248
|
+
appPkg = matchedPkg;
|
|
467
249
|
}
|
|
468
250
|
|
|
469
|
-
return ok({
|
|
251
|
+
return ok({
|
|
252
|
+
ok: true,
|
|
253
|
+
migration: pkgToPresent(APP_SPACE_ID, appPkg, client),
|
|
254
|
+
});
|
|
470
255
|
}
|
|
471
256
|
|
|
472
257
|
export function createMigrationShowCommand(): Command {
|
|
@@ -474,12 +259,11 @@ export function createMigrationShowCommand(): Command {
|
|
|
474
259
|
setCommandDescriptions(
|
|
475
260
|
command,
|
|
476
261
|
'Display migration package contents',
|
|
477
|
-
'Shows the operations, statement preview, and metadata for
|
|
478
|
-
'
|
|
479
|
-
'specific app-space migration; defaults to the latest per space.',
|
|
262
|
+
'Shows the operations, statement preview, and metadata for one app-space migration.\n' +
|
|
263
|
+
'Accepts a directory path, directory name, or hash prefix.',
|
|
480
264
|
);
|
|
481
265
|
setCommandExamples(command, [
|
|
482
|
-
'prisma-next migration show',
|
|
266
|
+
'prisma-next migration show 20260101_100000_add_user',
|
|
483
267
|
'prisma-next migration show sha256:a1b2c3',
|
|
484
268
|
]);
|
|
485
269
|
setCommandSeeAlso(command, [
|
|
@@ -489,12 +273,9 @@ export function createMigrationShowCommand(): Command {
|
|
|
489
273
|
{ verb: 'migration graph', oneLiner: 'Show the migration graph topology' },
|
|
490
274
|
]);
|
|
491
275
|
addGlobalOptions(command)
|
|
492
|
-
.argument(
|
|
493
|
-
'[target]',
|
|
494
|
-
'Migration reference: directory name, hash/prefix, or path (defaults to latest)',
|
|
495
|
-
)
|
|
276
|
+
.argument('<target>', 'Migration reference: directory name, hash/prefix, or path')
|
|
496
277
|
.option('--config <path>', 'Path to prisma-next.config.ts')
|
|
497
|
-
.action(async (target: string
|
|
278
|
+
.action(async (target: string, options: MigrationShowOptions) => {
|
|
498
279
|
const flags = parseGlobalFlagsOrExit(options);
|
|
499
280
|
|
|
500
281
|
const ui = createTerminalUI(flags);
|