@prisma-next/cli 0.11.0-dev.5 → 0.11.0-dev.51
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/dist/cli-errors-DFF1LlfU.mjs +215 -0
- package/dist/cli-errors-DFF1LlfU.mjs.map +1 -0
- package/dist/cli.mjs +9 -10
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-oXO2WCPD.mjs → client-5uvDppD8.mjs} +23 -21
- package/dist/client-5uvDppD8.mjs.map +1 -0
- package/dist/{command-helpers-DtavI0wJ.mjs → command-helpers-4UNsRRc4.mjs} +427 -9
- package/dist/command-helpers-4UNsRRc4.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 +33 -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 +6 -7
- 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 +36 -8
- 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 +5 -1
- package/dist/commands/migrate.d.mts.map +1 -1
- package/dist/commands/migrate.mjs +79 -39
- 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 +1 -1
- package/dist/commands/migration-graph.d.mts.map +1 -1
- package/dist/commands/migration-graph.mjs +3 -4
- package/dist/commands/migration-graph.mjs.map +1 -1
- package/dist/commands/migration-list.d.mts +63 -12
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +2 -103
- package/dist/commands/migration-log.d.mts.map +1 -1
- package/dist/commands/migration-log.mjs +3 -4
- 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 +33 -38
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +2 -1
- 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 +62 -153
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +5 -40
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +93 -67
- 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 +34 -9
- 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-emit-o-8VmdQX.mjs → contract-emit-C-CFGZsI.mjs} +9 -6
- package/dist/{contract-emit-o-8VmdQX.mjs.map → contract-emit-C-CFGZsI.mjs.map} +1 -1
- package/dist/{contract-emit-CmsklifJ.mjs → contract-emit-CuUzzM46.mjs} +5 -6
- package/dist/{contract-emit-CmsklifJ.mjs.map → contract-emit-CuUzzM46.mjs.map} +1 -1
- package/dist/{contract-enrichment-Dani0mMW.mjs → contract-enrichment-XmUPhmsS.mjs} +4 -25
- package/dist/contract-enrichment-XmUPhmsS.mjs.map +1 -0
- package/dist/{contract-infer-pKkiCt7C.mjs → contract-infer-C98ZaRhp.mjs} +3 -4
- package/dist/{contract-infer-pKkiCt7C.mjs.map → contract-infer-C98ZaRhp.mjs.map} +1 -1
- package/dist/contract-space-aggregate-loader-CVHGuA35.mjs +170 -0
- package/dist/contract-space-aggregate-loader-CVHGuA35.mjs.map +1 -0
- package/dist/{db-verify-AoIUriL4.mjs → db-verify-BWl1Yxi-.mjs} +6 -7
- package/dist/{db-verify-AoIUriL4.mjs.map → db-verify-BWl1Yxi-.mjs.map} +1 -1
- package/dist/exports/control-api.d.mts +1 -1
- 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-BiY86HbQ.mjs +62 -0
- package/dist/extension-pack-inputs-BiY86HbQ.mjs.map +1 -0
- package/dist/{framework-components-65gOHkHB.mjs → framework-components-DTcjouhS.mjs} +2 -2
- package/dist/{framework-components-65gOHkHB.mjs.map → framework-components-DTcjouhS.mjs.map} +1 -1
- package/dist/{global-flags-CdE7M0d9.d.mts → global-flags-DWsQ6SSI.d.mts} +1 -1
- package/dist/global-flags-DWsQ6SSI.d.mts.map +1 -0
- package/dist/glyph-mode-CBB4emzO.d.mts +5 -0
- package/dist/glyph-mode-CBB4emzO.d.mts.map +1 -0
- package/dist/{graph-render-DJVv0_uf.mjs → graph-render-D2FnLpuK.mjs} +1 -1
- package/dist/{graph-render-DJVv0_uf.mjs.map → graph-render-D2FnLpuK.mjs.map} +1 -1
- package/dist/{init-Db5Itt5r.mjs → init-C7PvN163.mjs} +5 -5
- package/dist/{init-Db5Itt5r.mjs.map → init-C7PvN163.mjs.map} +1 -1
- package/dist/{inspect-live-schema-LeWvkZVz.mjs → inspect-live-schema-BRCWQ-Sr.mjs} +5 -5
- package/dist/{inspect-live-schema-LeWvkZVz.mjs.map → inspect-live-schema-BRCWQ-Sr.mjs.map} +1 -1
- package/dist/migration-check-DoskM1nB.mjs +341 -0
- package/dist/migration-check-DoskM1nB.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-BtkunvFQ.mjs → migration-command-scaffold-CXLkoIJx.mjs} +5 -5
- package/dist/{migration-command-scaffold-BtkunvFQ.mjs.map → migration-command-scaffold-CXLkoIJx.mjs.map} +1 -1
- package/dist/migration-list-B2-iQ5Jd.mjs +646 -0
- package/dist/migration-list-B2-iQ5Jd.mjs.map +1 -0
- package/dist/{migration-plan-C2jeH1J5.mjs → migration-plan-BqmIKQpZ.mjs} +341 -88
- package/dist/migration-plan-BqmIKQpZ.mjs.map +1 -0
- package/dist/{migration-types-BXWvz12q.d.mts → migration-types-q64xAI_J.d.mts} +1 -1
- package/dist/{migration-types-BXWvz12q.d.mts.map → migration-types-q64xAI_J.d.mts.map} +1 -1
- package/dist/{migrations-CwZMa1Ck.mjs → migrations-BcVTutso.mjs} +12 -13
- package/dist/migrations-BcVTutso.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-xASh41wr.mjs} +1 -1
- package/dist/{progress-adapter-DFfvZcYL.mjs.map → progress-adapter-xASh41wr.mjs.map} +1 -1
- package/dist/ref-advancement-DRh5Nquq.mjs +50 -0
- package/dist/ref-advancement-DRh5Nquq.mjs.map +1 -0
- package/dist/{types-C9FfXb1l.d.mts → types-CEtm6v6a.d.mts} +5 -11
- package/dist/types-CEtm6v6a.d.mts.map +1 -0
- package/dist/{verify-Bom75OYI.mjs → verify-DOHbbrub.mjs} +2 -2
- package/dist/{verify-Bom75OYI.mjs.map → verify-DOHbbrub.mjs.map} +1 -1
- package/package.json +20 -20
- package/src/commands/db-init.ts +48 -2
- package/src/commands/db-update.ts +45 -0
- package/src/commands/migrate.ts +120 -40
- package/src/commands/migration-check.ts +43 -83
- package/src/commands/migration-list.ts +173 -74
- package/src/commands/migration-new.ts +44 -48
- package/src/commands/migration-plan.ts +359 -128
- package/src/commands/migration-show.ts +65 -284
- package/src/commands/migration-status.ts +131 -99
- package/src/commands/ref.ts +46 -6
- package/src/control-api/client.ts +0 -1
- package/src/control-api/contract-enrichment.ts +6 -42
- package/src/control-api/operations/contract-emit.ts +7 -2
- package/src/control-api/operations/db-verify.ts +9 -5
- package/src/control-api/operations/migration-apply.ts +11 -19
- package/src/control-api/types.ts +0 -7
- package/src/migration-cli.ts +4 -4
- package/src/utils/cli-errors.ts +224 -0
- package/src/utils/command-helpers.ts +9 -4
- package/src/utils/contract-space-aggregate-loader.ts +221 -117
- package/src/utils/formatters/migration-list-data-column.ts +115 -0
- package/src/utils/formatters/migration-list-graph-layout.ts +268 -0
- package/src/utils/formatters/migration-list-graph-render.ts +314 -0
- package/src/utils/formatters/migration-list-render.ts +194 -0
- package/src/utils/formatters/migration-list-styler.ts +61 -0
- package/src/utils/formatters/migrations.ts +29 -38
- 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 +257 -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-DtavI0wJ.mjs.map +0 -1
- package/dist/commands/migration-check.mjs.map +0 -1
- package/dist/commands/migration-list.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.map +0 -1
- package/dist/migration-plan-C2jeH1J5.mjs.map +0 -1
- package/dist/migrations-CwZMa1Ck.mjs.map +0 -1
- package/dist/terminal-ui-BiB_8KNo.mjs +0 -379
- package/dist/terminal-ui-BiB_8KNo.mjs.map +0 -1
- package/dist/types-C9FfXb1l.d.mts.map +0 -1
|
@@ -1,23 +1,37 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
1
|
+
import { enumerateMigrationSpaces } from '@prisma-next/migration-tools/enumerate-migration-spaces';
|
|
3
2
|
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
4
|
-
import {
|
|
3
|
+
import type {
|
|
4
|
+
MigrationListResult,
|
|
5
|
+
MigrationSpaceListEntry,
|
|
6
|
+
} from '@prisma-next/migration-tools/migration-list-types';
|
|
7
|
+
import { APP_SPACE_ID, isValidSpaceId } from '@prisma-next/migration-tools/spaces';
|
|
8
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
5
9
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
6
10
|
import { Command } from 'commander';
|
|
7
11
|
import { loadConfig } from '../config-loader';
|
|
8
12
|
import {
|
|
9
13
|
type CliStructuredError,
|
|
14
|
+
errorInvalidSpaceId,
|
|
15
|
+
errorSpaceNotFound,
|
|
10
16
|
errorUnexpected,
|
|
11
17
|
mapMigrationToolsError,
|
|
12
18
|
} from '../utils/cli-errors';
|
|
13
19
|
import {
|
|
14
20
|
addGlobalOptions,
|
|
15
|
-
loadMigrationPackages,
|
|
16
21
|
resolveMigrationPaths,
|
|
17
22
|
setCommandDescriptions,
|
|
18
23
|
setCommandExamples,
|
|
19
24
|
setCommandSeeAlso,
|
|
20
25
|
} from '../utils/command-helpers';
|
|
26
|
+
import {
|
|
27
|
+
type GlyphMode,
|
|
28
|
+
renderMigrationListGraphResult,
|
|
29
|
+
} from '../utils/formatters/migration-list-graph-render';
|
|
30
|
+
import {
|
|
31
|
+
buildMigrationListTopologyBySpace,
|
|
32
|
+
renderMigrationListWithStyle,
|
|
33
|
+
} from '../utils/formatters/migration-list-render';
|
|
34
|
+
import { createAnsiMigrationListStyler } from '../utils/formatters/migration-list-styler';
|
|
21
35
|
import { formatStyledHeader } from '../utils/formatters/styled';
|
|
22
36
|
import type { CommonCommandOptions } from '../utils/global-flags';
|
|
23
37
|
import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
|
|
@@ -26,30 +40,141 @@ import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
|
|
|
26
40
|
|
|
27
41
|
interface MigrationListOptions extends CommonCommandOptions {
|
|
28
42
|
readonly config?: string;
|
|
43
|
+
readonly space?: string;
|
|
44
|
+
readonly graph?: boolean;
|
|
45
|
+
readonly ascii?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface MigrationListHumanRenderOptions {
|
|
49
|
+
readonly graph: boolean;
|
|
50
|
+
readonly glyphMode: GlyphMode;
|
|
51
|
+
readonly useColor: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function renderMigrationListHumanOutput(
|
|
55
|
+
result: MigrationListResult,
|
|
56
|
+
options: MigrationListHumanRenderOptions,
|
|
57
|
+
): string {
|
|
58
|
+
const styler = createAnsiMigrationListStyler({ useColor: options.useColor });
|
|
59
|
+
const topologyBySpaceId = buildMigrationListTopologyBySpace(result);
|
|
60
|
+
if (options.graph) {
|
|
61
|
+
return renderMigrationListGraphResult(result, styler, options.glyphMode, topologyBySpaceId);
|
|
62
|
+
}
|
|
63
|
+
return renderMigrationListWithStyle(result, styler, options.glyphMode, topologyBySpaceId);
|
|
29
64
|
}
|
|
30
65
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Inputs for {@link runMigrationList} — the pure-ish data-and-policy core
|
|
68
|
+
* of `migration list` that tests exercise directly.
|
|
69
|
+
*
|
|
70
|
+
* The core depends only on the filesystem rooted at `migrationsDir`. It
|
|
71
|
+
* does NOT call `loadConfig`, parse CLI flags, render a styled header,
|
|
72
|
+
* or write to any stream. Output rendering is the caller's concern (the
|
|
73
|
+
* CLI shell renders via {@link renderMigrationList}; JSON callers
|
|
74
|
+
* serialize the {@link MigrationListResult} directly).
|
|
75
|
+
*/
|
|
76
|
+
export interface RunMigrationListInputs {
|
|
77
|
+
/** Absolute path to the project's `migrations/` directory. */
|
|
78
|
+
readonly migrationsDir: string;
|
|
79
|
+
/**
|
|
80
|
+
* Optional contract-space id to narrow the result to a single space.
|
|
81
|
+
* Same validation rules as {@link isValidSpaceId}. When absent, every
|
|
82
|
+
* on-disk space contributes.
|
|
83
|
+
*/
|
|
84
|
+
readonly spaceFilter?: string;
|
|
38
85
|
}
|
|
39
86
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
87
|
+
function computeSummary(spaces: readonly MigrationSpaceListEntry[]): string {
|
|
88
|
+
const totalMigrations = spaces.reduce((count, space) => count + space.migrations.length, 0);
|
|
89
|
+
if (spaces.length <= 1) {
|
|
90
|
+
return `${totalMigrations} migration(s) on disk`;
|
|
91
|
+
}
|
|
92
|
+
return `${totalMigrations} migration(s) across ${spaces.length} contract space(s)`;
|
|
44
93
|
}
|
|
45
94
|
|
|
95
|
+
/**
|
|
96
|
+
* The unit-testable core of `migration list`. Given an absolute
|
|
97
|
+
* `migrationsDir` and an optional `spaceFilter`, enumerates every
|
|
98
|
+
* on-disk migration (via {@link enumerateMigrationSpaces}), narrows to
|
|
99
|
+
* the requested space if any, and assembles a {@link MigrationListResult}
|
|
100
|
+
* ready for the renderer or JSON serializer.
|
|
101
|
+
*
|
|
102
|
+
* The enumerator is the single source of truth for "what is a contract
|
|
103
|
+
* space": existence, the `--space` candidate-suggestion list, and
|
|
104
|
+
* scoping are all derived from one {@link enumerateMigrationSpaces}
|
|
105
|
+
* traversal. This means the reserved-name exclusion the enumerator
|
|
106
|
+
* applies (e.g. the per-space `refs/` subdirectory) is honoured here for
|
|
107
|
+
* free — a `--space refs` request resolves to `SPACE_NOT_FOUND`, not a
|
|
108
|
+
* synthesized empty-state.
|
|
109
|
+
*
|
|
110
|
+
* Distinct empty-state paths:
|
|
111
|
+
*
|
|
112
|
+
* - `migrations/` missing or contains no valid space directories →
|
|
113
|
+
* synthesizes `[{ spaceId: APP_SPACE_ID, migrations: [] }]` so the
|
|
114
|
+
* renderer's empty-state path can name a directory (spec § Empty-state +
|
|
115
|
+
* the `migrations/` missing edge case).
|
|
116
|
+
* - `--space <id>` on an existing-but-empty space dir → the enumerator
|
|
117
|
+
* surfaces `{ spaceId, migrations: [] }`; `<id>` is in the set, so it
|
|
118
|
+
* scopes to that entry and renders the empty-state the same way.
|
|
119
|
+
* - `--space <id>` on a non-existent (or reserved) space → structured
|
|
120
|
+
* `MIGRATION.SPACE_NOT_FOUND` error (NOT empty-state).
|
|
121
|
+
*
|
|
122
|
+
* Errors caught here:
|
|
123
|
+
*
|
|
124
|
+
* - {@link MigrationToolsError} from the enumerator → mapped through
|
|
125
|
+
* {@link mapMigrationToolsError} so callers see the catalogue code.
|
|
126
|
+
* - Anything else (filesystem etc.) → wrapped via {@link errorUnexpected}.
|
|
127
|
+
*/
|
|
128
|
+
export async function runMigrationList(
|
|
129
|
+
inputs: RunMigrationListInputs,
|
|
130
|
+
): Promise<Result<MigrationListResult, CliStructuredError>> {
|
|
131
|
+
const { migrationsDir, spaceFilter } = inputs;
|
|
132
|
+
|
|
133
|
+
if (spaceFilter !== undefined && !isValidSpaceId(spaceFilter)) {
|
|
134
|
+
return notOk(errorInvalidSpaceId(spaceFilter));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let spaces: readonly MigrationSpaceListEntry[];
|
|
138
|
+
try {
|
|
139
|
+
spaces = await enumerateMigrationSpaces({ projectMigrationsDir: migrationsDir });
|
|
140
|
+
} catch (error) {
|
|
141
|
+
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
142
|
+
return notOk(
|
|
143
|
+
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
144
|
+
why: `Failed to enumerate migrations: ${error instanceof Error ? error.message : String(error)}`,
|
|
145
|
+
}),
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (spaceFilter !== undefined && !spaces.some((s) => s.spaceId === spaceFilter)) {
|
|
150
|
+
return notOk(errorSpaceNotFound(spaceFilter, spaces.map((s) => s.spaceId).sort()));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const scopedSpaces =
|
|
154
|
+
spaceFilter !== undefined ? spaces.filter((s) => s.spaceId === spaceFilter) : spaces;
|
|
155
|
+
|
|
156
|
+
const resultSpaces: readonly MigrationSpaceListEntry[] =
|
|
157
|
+
scopedSpaces.length === 0 ? [{ spaceId: APP_SPACE_ID, migrations: [] }] : scopedSpaces;
|
|
158
|
+
|
|
159
|
+
return ok({
|
|
160
|
+
ok: true,
|
|
161
|
+
spaces: resultSpaces,
|
|
162
|
+
summary: computeSummary(resultSpaces),
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* CLI shell: loads config, resolves paths, prints the styled header on
|
|
168
|
+
* stderr (interactive mode only), and delegates to {@link runMigrationList}.
|
|
169
|
+
* Kept intentionally thin so the unit-testable surface lives in the core.
|
|
170
|
+
*/
|
|
46
171
|
async function executeMigrationListCommand(
|
|
47
172
|
options: MigrationListOptions,
|
|
48
173
|
flags: GlobalFlags,
|
|
49
174
|
ui: TerminalUI,
|
|
50
175
|
): Promise<Result<MigrationListResult, CliStructuredError>> {
|
|
51
176
|
const config = await loadConfig(options.config);
|
|
52
|
-
const { configPath,
|
|
177
|
+
const { configPath, migrationsDir, migrationsRelative } = resolveMigrationPaths(
|
|
53
178
|
options.config,
|
|
54
179
|
config,
|
|
55
180
|
);
|
|
@@ -57,58 +182,20 @@ async function executeMigrationListCommand(
|
|
|
57
182
|
if (!flags.json && !flags.quiet) {
|
|
58
183
|
const header = formatStyledHeader({
|
|
59
184
|
command: 'migration list',
|
|
60
|
-
description: 'List on-disk migrations
|
|
185
|
+
description: 'List on-disk migrations, latest first, per contract space',
|
|
61
186
|
details: [
|
|
62
187
|
{ label: 'config', value: configPath },
|
|
63
|
-
{ label: 'migrations', value:
|
|
188
|
+
{ label: 'migrations', value: migrationsRelative },
|
|
189
|
+
...(options.space !== undefined ? [{ label: 'space', value: options.space }] : []),
|
|
64
190
|
],
|
|
65
191
|
flags,
|
|
66
192
|
});
|
|
67
193
|
ui.stderr(header);
|
|
68
194
|
}
|
|
69
195
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
({ bundles, graph } = await loadMigrationPackages(appMigrationsDir));
|
|
74
|
-
} catch (error) {
|
|
75
|
-
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
76
|
-
return notOk(
|
|
77
|
-
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
78
|
-
why: `Failed to read migrations: ${error instanceof Error ? error.message : String(error)}`,
|
|
79
|
-
}),
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (bundles.length === 0) {
|
|
84
|
-
return ok({ ok: true, migrations: [], summary: 'No migrations found' });
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const leaves = [...graph.nodes].filter(
|
|
88
|
-
(n) => !graph.forwardChain.has(n) || graph.forwardChain.get(n)!.length === 0,
|
|
89
|
-
);
|
|
90
|
-
const targetHash =
|
|
91
|
-
leaves.length === 1 ? leaves[0]! : ([...graph.nodes].values().next().value as string);
|
|
92
|
-
const chain = findPath(graph, EMPTY_CONTRACT_HASH, targetHash) ?? [];
|
|
93
|
-
|
|
94
|
-
const pkgByDirName = new Map(bundles.map((p) => [p.dirName, p]));
|
|
95
|
-
const entries: MigrationListEntry[] = chain.map((edge) => {
|
|
96
|
-
const pkg = pkgByDirName.get(edge.dirName);
|
|
97
|
-
const ops = (pkg?.ops ?? []) as readonly MigrationPlanOperation[];
|
|
98
|
-
return {
|
|
99
|
-
dirName: edge.dirName,
|
|
100
|
-
from: edge.from,
|
|
101
|
-
to: edge.to,
|
|
102
|
-
migrationHash: edge.migrationHash,
|
|
103
|
-
operationCount: ops.length,
|
|
104
|
-
createdAt: edge.createdAt,
|
|
105
|
-
};
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
return ok({
|
|
109
|
-
ok: true,
|
|
110
|
-
migrations: entries,
|
|
111
|
-
summary: `${entries.length} migration(s) on disk`,
|
|
196
|
+
return runMigrationList({
|
|
197
|
+
migrationsDir,
|
|
198
|
+
...ifDefined('spaceFilter', options.space),
|
|
112
199
|
});
|
|
113
200
|
}
|
|
114
201
|
|
|
@@ -116,11 +203,23 @@ export function createMigrationListCommand(): Command {
|
|
|
116
203
|
const command = new Command('list');
|
|
117
204
|
setCommandDescriptions(
|
|
118
205
|
command,
|
|
119
|
-
'List on-disk migrations
|
|
120
|
-
'Enumerates
|
|
121
|
-
'
|
|
206
|
+
'List on-disk migrations, latest first, per contract space',
|
|
207
|
+
'Enumerates every on-disk migration under migrations/<space>/ for every\n' +
|
|
208
|
+
'contract space found on disk, latest first. Offline — does not consult\n' +
|
|
209
|
+
'the database. Each row leads with a kind glyph (* forward, ↩ rollback,\n' +
|
|
210
|
+
'⟲ self), then dirName, then source → destination contract hashes\n' +
|
|
211
|
+
'(7-char git-style). Self-edges show a single hash. Invariants render as\n' +
|
|
212
|
+
'{...}; refs on the destination as (production, db). Pass --space <id>\n' +
|
|
213
|
+
'to narrow to one contract space. --graph draws the forward spine with\n' +
|
|
214
|
+
'lane gutters; --ascii forces ASCII glyphs (orthogonal to --no-color).',
|
|
122
215
|
);
|
|
123
|
-
setCommandExamples(command, [
|
|
216
|
+
setCommandExamples(command, [
|
|
217
|
+
'prisma-next migration list',
|
|
218
|
+
'prisma-next migration list --graph',
|
|
219
|
+
'prisma-next migration list --space app',
|
|
220
|
+
'prisma-next migration list --graph --ascii',
|
|
221
|
+
'prisma-next migration list --json',
|
|
222
|
+
]);
|
|
124
223
|
setCommandSeeAlso(command, [
|
|
125
224
|
{ verb: 'migration status', oneLiner: 'Show migration path and pending status' },
|
|
126
225
|
{ verb: 'migration log', oneLiner: 'Show executed migration history' },
|
|
@@ -129,6 +228,9 @@ export function createMigrationListCommand(): Command {
|
|
|
129
228
|
]);
|
|
130
229
|
addGlobalOptions(command)
|
|
131
230
|
.option('--config <path>', 'Path to prisma-next.config.ts')
|
|
231
|
+
.option('--space <id>', 'Narrow output to a single contract space')
|
|
232
|
+
.option('--graph', 'Draw migration relationships as an annotated tree')
|
|
233
|
+
.option('--ascii', 'Use ASCII glyphs for --graph (pipe-friendly)')
|
|
132
234
|
.action(async (options: MigrationListOptions) => {
|
|
133
235
|
const flags = parseGlobalFlagsOrExit(options);
|
|
134
236
|
const ui = createTerminalUI(flags);
|
|
@@ -137,16 +239,13 @@ export function createMigrationListCommand(): Command {
|
|
|
137
239
|
if (flags.json) {
|
|
138
240
|
ui.output(JSON.stringify(listResult, null, 2));
|
|
139
241
|
} else if (!flags.quiet) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
ui.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
ui.log(`\n${listResult.summary}`);
|
|
149
|
-
}
|
|
242
|
+
ui.output(
|
|
243
|
+
renderMigrationListHumanOutput(listResult, {
|
|
244
|
+
graph: options.graph === true,
|
|
245
|
+
glyphMode: ui.resolveGlyphMode(options.ascii === true),
|
|
246
|
+
useColor: ui.useColor,
|
|
247
|
+
}),
|
|
248
|
+
);
|
|
150
249
|
}
|
|
151
250
|
});
|
|
152
251
|
process.exit(exitCode);
|
|
@@ -12,19 +12,15 @@ import { readFile } from 'node:fs/promises';
|
|
|
12
12
|
import type { Contract } from '@prisma-next/contract/types';
|
|
13
13
|
import { getEmittedArtifactPaths } from '@prisma-next/emitter';
|
|
14
14
|
import { APP_SPACE_ID, createControlStack } from '@prisma-next/framework-components/control';
|
|
15
|
-
import {
|
|
15
|
+
import { loadContractSpaceAggregate } from '@prisma-next/migration-tools/aggregate';
|
|
16
16
|
import { computeMigrationHash } from '@prisma-next/migration-tools/hash';
|
|
17
17
|
import {
|
|
18
18
|
copyFilesWithRename,
|
|
19
19
|
formatMigrationDirName,
|
|
20
|
-
readMigrationsDir,
|
|
21
20
|
writeMigrationPackage,
|
|
22
21
|
} from '@prisma-next/migration-tools/io';
|
|
23
22
|
import type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';
|
|
24
|
-
import {
|
|
25
|
-
findLatestMigration,
|
|
26
|
-
reconstructGraph,
|
|
27
|
-
} from '@prisma-next/migration-tools/migration-graph';
|
|
23
|
+
import { findLatestMigration } from '@prisma-next/migration-tools/migration-graph';
|
|
28
24
|
import { writeMigrationTs } from '@prisma-next/migration-tools/migration-ts';
|
|
29
25
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
30
26
|
import { Command } from 'commander';
|
|
@@ -36,7 +32,6 @@ import {
|
|
|
36
32
|
errorRuntime,
|
|
37
33
|
errorTargetMigrationNotSupported,
|
|
38
34
|
errorUnexpected,
|
|
39
|
-
mapMigrationToolsError,
|
|
40
35
|
} from '../utils/cli-errors';
|
|
41
36
|
import {
|
|
42
37
|
addGlobalOptions,
|
|
@@ -46,6 +41,7 @@ import {
|
|
|
46
41
|
setCommandDescriptions,
|
|
47
42
|
setCommandExamples,
|
|
48
43
|
} from '../utils/command-helpers';
|
|
44
|
+
import { refusePackageCorruptionOnAggregate } from '../utils/contract-space-aggregate-loader';
|
|
49
45
|
import { formatStyledHeader } from '../utils/formatters/styled';
|
|
50
46
|
import { assertFrameworkComponentsCompatible } from '../utils/framework-components';
|
|
51
47
|
import type { CommonCommandOptions } from '../utils/global-flags';
|
|
@@ -71,7 +67,10 @@ async function executeMigrationNewCommand(
|
|
|
71
67
|
options: MigrationNewOptions,
|
|
72
68
|
): Promise<Result<MigrationNewResult, CliStructuredError>> {
|
|
73
69
|
const config = await loadConfig(options.config);
|
|
74
|
-
const { appMigrationsDir, appMigrationsRelative } = resolveMigrationPaths(
|
|
70
|
+
const { migrationsDir, appMigrationsDir, appMigrationsRelative } = resolveMigrationPaths(
|
|
71
|
+
options.config,
|
|
72
|
+
config,
|
|
73
|
+
);
|
|
75
74
|
|
|
76
75
|
// Construct the family instance up-front so the on-disk contract read
|
|
77
76
|
// below crosses the serializer seam (`familyInstance.deserializeContract`)
|
|
@@ -98,7 +97,8 @@ async function executeMigrationNewCommand(
|
|
|
98
97
|
|
|
99
98
|
let toContract: Contract;
|
|
100
99
|
try {
|
|
101
|
-
|
|
100
|
+
const parsedContract: unknown = JSON.parse(contractJsonContent);
|
|
101
|
+
toContract = familyInstance.deserializeContract(parsedContract);
|
|
102
102
|
} catch (error) {
|
|
103
103
|
return notOk(
|
|
104
104
|
errorRuntime('Contract JSON is invalid', {
|
|
@@ -118,45 +118,47 @@ async function executeMigrationNewCommand(
|
|
|
118
118
|
);
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
const aggregate = await loadContractSpaceAggregate({
|
|
122
|
+
migrationsDir,
|
|
123
|
+
deserializeContract: (json) => familyInstance.deserializeContract(json),
|
|
124
|
+
appContract: toContract,
|
|
125
|
+
});
|
|
126
|
+
const packageCorruptionFailure = refusePackageCorruptionOnAggregate(aggregate);
|
|
127
|
+
if (packageCorruptionFailure) {
|
|
128
|
+
return notOk(packageCorruptionFailure);
|
|
129
|
+
}
|
|
123
130
|
|
|
124
|
-
|
|
125
|
-
|
|
131
|
+
const packages = aggregate.app.packages;
|
|
132
|
+
const graph = aggregate.app.graph();
|
|
126
133
|
|
|
127
|
-
|
|
128
|
-
|
|
134
|
+
let fromHash: string | null = null;
|
|
135
|
+
let fromContractSourceDir: string | null = null;
|
|
129
136
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
137
|
+
if (packages.length > 0) {
|
|
138
|
+
if (options.from) {
|
|
139
|
+
const match = packages.find((p) => p.metadata.to.startsWith(options.from!));
|
|
140
|
+
if (!match) {
|
|
141
|
+
return notOk(
|
|
142
|
+
errorRuntime('Starting contract not found', {
|
|
143
|
+
why: `No migration with to hash matching "${options.from}" exists in ${appMigrationsRelative}`,
|
|
144
|
+
fix: 'Check that the --from hash matches a known migration target hash.',
|
|
145
|
+
}),
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
fromHash = match.metadata.to;
|
|
149
|
+
fromContractSourceDir = match.dirPath;
|
|
150
|
+
} else {
|
|
151
|
+
const latestMigration = findLatestMigration(graph);
|
|
152
|
+
if (latestMigration) {
|
|
153
|
+
fromHash = latestMigration.to;
|
|
154
|
+
const leafPkg = packages.find(
|
|
155
|
+
(p) => p.metadata.migrationHash === latestMigration.migrationHash,
|
|
156
|
+
);
|
|
157
|
+
if (leafPkg) {
|
|
158
|
+
fromContractSourceDir = leafPkg.dirPath;
|
|
152
159
|
}
|
|
153
160
|
}
|
|
154
161
|
}
|
|
155
|
-
} catch (error) {
|
|
156
|
-
if (MigrationToolsError.is(error)) {
|
|
157
|
-
return notOk(mapMigrationToolsError(error));
|
|
158
|
-
}
|
|
159
|
-
throw error;
|
|
160
162
|
}
|
|
161
163
|
|
|
162
164
|
if (fromHash === toStorageHash && !options.from) {
|
|
@@ -180,12 +182,6 @@ async function executeMigrationNewCommand(
|
|
|
180
182
|
const baseMetadata: Omit<MigrationMetadata, 'migrationHash'> = {
|
|
181
183
|
from: fromHash,
|
|
182
184
|
to: toStorageHash,
|
|
183
|
-
hints: {
|
|
184
|
-
used: [],
|
|
185
|
-
applied: [],
|
|
186
|
-
plannerVersion: '1.0.0',
|
|
187
|
-
},
|
|
188
|
-
labels: [],
|
|
189
185
|
providedInvariants: [],
|
|
190
186
|
createdAt: timestamp.toISOString(),
|
|
191
187
|
};
|