@prisma-next/cli 0.5.0-dev.9 → 0.5.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 +61 -26
- package/dist/cli-errors-B9OBbled.d.mts +3 -0
- package/dist/cli-errors-D3_sMh2K.mjs +33 -0
- package/dist/cli-errors-D3_sMh2K.mjs.map +1 -0
- package/dist/cli.mjs +16 -78
- package/dist/cli.mjs.map +1 -1
- package/dist/client-BCnP7cHo.mjs +1485 -0
- package/dist/client-BCnP7cHo.mjs.map +1 -0
- package/dist/{result-handler-Ba3zWQsI.mjs → command-helpers-BeZHkxV8.mjs} +70 -47
- package/dist/command-helpers-BeZHkxV8.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts.map +1 -1
- package/dist/commands/contract-emit.mjs +2 -4
- package/dist/commands/contract-infer.d.mts.map +1 -1
- package/dist/commands/contract-infer.mjs +2 -4
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +16 -13
- 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 +6 -7
- 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 +9 -9
- 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 +15 -13
- 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 -321
- package/dist/commands/migration-apply.d.mts +28 -13
- package/dist/commands/migration-apply.d.mts.map +1 -1
- package/dist/commands/migration-apply.mjs +55 -151
- package/dist/commands/migration-apply.mjs.map +1 -1
- package/dist/commands/migration-new.d.mts +0 -1
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +34 -40
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +33 -6
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +2 -348
- package/dist/commands/migration-ref.d.mts +1 -1
- package/dist/commands/migration-ref.d.mts.map +1 -1
- package/dist/commands/migration-ref.mjs +8 -12
- package/dist/commands/migration-ref.mjs.map +1 -1
- package/dist/commands/migration-show.d.mts +64 -10
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +166 -60
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +126 -5
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +2 -4
- package/dist/{config-loader-C25b63rJ.mjs → config-loader-B6sJjXTv.mjs} +3 -5
- package/dist/config-loader-B6sJjXTv.mjs.map +1 -0
- package/dist/config-loader.d.mts +0 -1
- package/dist/config-loader.d.mts.map +1 -1
- package/dist/config-loader.mjs +2 -3
- package/dist/contract-emit-9DBda5Ou.mjs +150 -0
- package/dist/contract-emit-9DBda5Ou.mjs.map +1 -0
- package/dist/contract-emit-B77TsJqf.mjs +327 -0
- package/dist/contract-emit-B77TsJqf.mjs.map +1 -0
- package/dist/{contract-enrichment-CAOELa-H.mjs → contract-enrichment-Dani0mMW.mjs} +4 -6
- package/dist/contract-enrichment-Dani0mMW.mjs.map +1 -0
- package/dist/{contract-infer-D9cC3rJm.mjs → contract-infer-ByxhPjpW.mjs} +13 -22
- package/dist/contract-infer-ByxhPjpW.mjs.map +1 -0
- package/dist/contract-space-aggregate-loader-BrwKK6Q6.mjs +160 -0
- package/dist/contract-space-aggregate-loader-BrwKK6Q6.mjs.map +1 -0
- package/dist/db-verify-Czm5T-J4.mjs +404 -0
- package/dist/db-verify-Czm5T-J4.mjs.map +1 -0
- package/dist/exports/config-types.mjs +1 -2
- package/dist/exports/control-api.d.mts +101 -586
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +4 -6
- package/dist/exports/index.d.mts.map +1 -1
- package/dist/exports/index.mjs +28 -30
- package/dist/exports/index.mjs.map +1 -1
- package/dist/exports/init-output.d.mts +2 -4
- package/dist/exports/init-output.d.mts.map +1 -1
- package/dist/exports/init-output.mjs +2 -3
- package/dist/{framework-components-Cr--XBKy.mjs → framework-components-ChqVUxR-.mjs} +3 -4
- package/dist/{framework-components-Cr--XBKy.mjs.map → framework-components-ChqVUxR-.mjs.map} +1 -1
- package/dist/global-flags-Icqpxk23.d.mts +12 -0
- package/dist/global-flags-Icqpxk23.d.mts.map +1 -0
- package/dist/helpers-eqdN8tH6.mjs +25 -0
- package/dist/helpers-eqdN8tH6.mjs.map +1 -0
- package/dist/{init-C5220SY9.mjs → init-DETSgw3h.mjs} +40 -49
- package/dist/init-DETSgw3h.mjs.map +1 -0
- package/dist/{inspect-live-schema-yrHAvG71.mjs → inspect-live-schema-DxdBd4Er.mjs} +10 -11
- package/dist/inspect-live-schema-DxdBd4Er.mjs.map +1 -0
- package/dist/migration-cli.d.mts +41 -12
- package/dist/migration-cli.d.mts.map +1 -1
- package/dist/migration-cli.mjs +309 -86
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-B3B09et6.mjs → migration-command-scaffold-BdV8JYXV.mjs} +8 -9
- package/dist/migration-command-scaffold-BdV8JYXV.mjs.map +1 -0
- package/dist/migration-plan-mRu5K81L.mjs +494 -0
- package/dist/migration-plan-mRu5K81L.mjs.map +1 -0
- package/dist/{migration-status-DUMiH8_G.mjs → migration-status-By9G5p2H.mjs} +270 -65
- package/dist/migration-status-By9G5p2H.mjs.map +1 -0
- package/dist/migrations-CTsyBXCA.mjs +229 -0
- package/dist/migrations-CTsyBXCA.mjs.map +1 -0
- package/dist/{output-BpcQrnnq.mjs → output-B16Kefzx.mjs} +9 -3
- package/dist/output-B16Kefzx.mjs.map +1 -0
- package/dist/{progress-adapter-DvQWB1nK.mjs → progress-adapter-DFfvZcYL.mjs} +2 -2
- package/dist/{progress-adapter-DvQWB1nK.mjs.map → progress-adapter-DFfvZcYL.mjs.map} +1 -1
- package/dist/result-handler-rmPVKIP2.mjs +25 -0
- package/dist/result-handler-rmPVKIP2.mjs.map +1 -0
- package/dist/rolldown-runtime-twds-ZHy.mjs +14 -0
- package/dist/{terminal-ui-C3ZLwQxK.mjs → terminal-ui-C_hFNbAn.mjs} +4 -28
- package/dist/terminal-ui-C_hFNbAn.mjs.map +1 -0
- package/dist/types-LItU7E4l.d.mts +856 -0
- package/dist/types-LItU7E4l.d.mts.map +1 -0
- package/dist/{verify-Bkycc-Tf.mjs → verify-CiwNWM9N.mjs} +3 -4
- package/dist/verify-CiwNWM9N.mjs.map +1 -0
- package/package.json +28 -26
- package/src/cli.ts +32 -6
- package/src/commands/contract-emit.ts +67 -163
- package/src/commands/contract-infer.ts +7 -20
- package/src/commands/db-init.ts +15 -3
- package/src/commands/db-update.ts +9 -4
- package/src/commands/db-verify.ts +47 -15
- package/src/commands/init/index.ts +1 -1
- package/src/commands/init/init.ts +2 -2
- package/src/commands/init/templates/code-templates.ts +26 -18
- package/src/commands/inspect-live-schema.ts +10 -5
- package/src/commands/migration-apply.ts +114 -212
- package/src/commands/migration-new.ts +42 -45
- package/src/commands/migration-plan.ts +213 -75
- package/src/commands/migration-ref.ts +8 -7
- package/src/commands/migration-show.ts +274 -70
- package/src/commands/migration-status.ts +491 -64
- package/src/config-path-validation.ts +0 -1
- package/src/control-api/client.ts +85 -5
- package/src/control-api/contract-enrichment.ts +6 -4
- package/src/control-api/operations/apply-aggregate.ts +290 -0
- package/src/control-api/operations/contract-emit.ts +198 -115
- package/src/control-api/operations/db-apply-aggregate.ts +399 -0
- package/src/control-api/operations/db-init.ts +51 -253
- package/src/control-api/operations/db-update.ts +66 -183
- package/src/control-api/operations/db-verify.ts +342 -0
- package/src/control-api/operations/migration-apply.ts +430 -131
- package/src/control-api/types.ts +278 -29
- package/src/exports/control-api.ts +15 -3
- package/src/load-ts-contract.ts +28 -26
- package/src/migration-cli.ts +445 -122
- package/src/utils/cli-errors.ts +49 -2
- package/src/utils/combine-schema-results.ts +84 -0
- package/src/utils/command-helpers.ts +69 -25
- package/src/utils/contract-space-aggregate-loader.ts +177 -0
- package/src/utils/contract-space-seed-phase.ts +201 -0
- package/src/utils/emit-queue.ts +26 -0
- package/src/utils/extension-pack-inputs.ts +162 -0
- package/src/utils/formatters/graph-migration-mapper.ts +7 -3
- package/src/utils/formatters/migrations.ts +255 -77
- package/src/utils/publish-contract-artifact-pair.ts +134 -0
- package/dist/cli-errors-BFYgBH3L.d.mts +0 -4
- package/dist/cli-errors-Cd79vmTH.mjs +0 -5
- package/dist/client-CrsnY58k.mjs +0 -997
- package/dist/client-CrsnY58k.mjs.map +0 -1
- package/dist/commands/db-verify.mjs.map +0 -1
- package/dist/commands/migration-plan.mjs.map +0 -1
- package/dist/config-loader-C25b63rJ.mjs.map +0 -1
- package/dist/contract-emit--feXyNd7.mjs +0 -4
- package/dist/contract-emit-NJ01hiiv.mjs +0 -195
- package/dist/contract-emit-NJ01hiiv.mjs.map +0 -1
- package/dist/contract-emit-V5SSitUT.mjs +0 -122
- package/dist/contract-emit-V5SSitUT.mjs.map +0 -1
- package/dist/contract-enrichment-CAOELa-H.mjs.map +0 -1
- package/dist/contract-infer-D9cC3rJm.mjs.map +0 -1
- package/dist/extract-operation-statements-DsFfxXVZ.mjs +0 -13
- package/dist/extract-operation-statements-DsFfxXVZ.mjs.map +0 -1
- package/dist/extract-sql-ddl-D9UbZDyz.mjs +0 -26
- package/dist/extract-sql-ddl-D9UbZDyz.mjs.map +0 -1
- package/dist/init-C5220SY9.mjs.map +0 -1
- package/dist/inspect-live-schema-yrHAvG71.mjs.map +0 -1
- package/dist/migration-command-scaffold-B3B09et6.mjs.map +0 -1
- package/dist/migration-status-DUMiH8_G.mjs.map +0 -1
- package/dist/migrations-Bo5WtTla.mjs +0 -153
- package/dist/migrations-Bo5WtTla.mjs.map +0 -1
- package/dist/output-BpcQrnnq.mjs.map +0 -1
- package/dist/result-handler-Ba3zWQsI.mjs.map +0 -1
- package/dist/terminal-ui-C3ZLwQxK.mjs.map +0 -1
- package/dist/validate-contract-deps-B_Cs29TL.mjs +0 -37
- package/dist/validate-contract-deps-B_Cs29TL.mjs.map +0 -1
- package/dist/verify-Bkycc-Tf.mjs.map +0 -1
- package/src/control-api/operations/extract-operation-statements.ts +0 -14
- package/src/control-api/operations/extract-sql-ddl.ts +0 -47
|
@@ -1,8 +1,43 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { OperationPreview } from '@prisma-next/framework-components/control';
|
|
2
|
+
import { cyan, green, yellow } from 'colorette';
|
|
2
3
|
|
|
4
|
+
import type { AggregatePerSpaceExecutionEntry } from '../../control-api/types';
|
|
3
5
|
import type { GlobalFlags } from '../global-flags';
|
|
4
6
|
import { createColorFormatter, formatDim, isVerbose } from './helpers';
|
|
5
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Render a single statement of an `OperationPreview` for the human-readable
|
|
10
|
+
* preview block. SQL statements get a trailing `;` if missing so the rendered
|
|
11
|
+
* preview is byte-identical to the legacy `string[]`-based renderer for SQL
|
|
12
|
+
* targets. Other languages (`'mongodb-shell'`) render verbatim.
|
|
13
|
+
*/
|
|
14
|
+
function renderPreviewStatement(text: string, language: string): string | undefined {
|
|
15
|
+
const trimmed = text.trim();
|
|
16
|
+
if (!trimmed) return undefined;
|
|
17
|
+
if (language === 'sql') {
|
|
18
|
+
return trimmed.endsWith(';') ? trimmed : `${trimmed};`;
|
|
19
|
+
}
|
|
20
|
+
return trimmed;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Choose the header label for a preview block. SQL-only previews keep the
|
|
25
|
+
* legacy `DDL preview` label so the rendered output is byte-identical to the
|
|
26
|
+
* pre-aggregate SQL CLI; previews from any other family — or a mix that
|
|
27
|
+
* includes any non-SQL language — use the family-agnostic `Operation preview`
|
|
28
|
+
* label.
|
|
29
|
+
*
|
|
30
|
+
* An empty `statements` array deliberately renders as `Operation preview`
|
|
31
|
+
* rather than `DDL preview`: `Array.prototype.every` is vacuously true for
|
|
32
|
+
* empty arrays, but we have no evidence the preview is SQL-only when no
|
|
33
|
+
* statements are present, so the family-agnostic label is the safer default.
|
|
34
|
+
*/
|
|
35
|
+
export function previewBlockHeader(preview: OperationPreview): string {
|
|
36
|
+
const allSql =
|
|
37
|
+
preview.statements.length > 0 && preview.statements.every((s) => s.language === 'sql');
|
|
38
|
+
return allSql ? 'DDL preview' : 'Operation preview';
|
|
39
|
+
}
|
|
40
|
+
|
|
6
41
|
// ============================================================================
|
|
7
42
|
// Migration Command Output Formatters (shared by db init and db update)
|
|
8
43
|
// ============================================================================
|
|
@@ -24,7 +59,12 @@ export interface MigrationCommandResult {
|
|
|
24
59
|
readonly label: string;
|
|
25
60
|
readonly operationClass: string;
|
|
26
61
|
}[];
|
|
27
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Family-agnostic textual preview of the planned operations. Replaces the
|
|
64
|
+
* previous `sql?: readonly string[]`. Consumers should read
|
|
65
|
+
* `plan.preview?.statements`.
|
|
66
|
+
*/
|
|
67
|
+
readonly preview?: OperationPreview;
|
|
28
68
|
};
|
|
29
69
|
readonly execution?: {
|
|
30
70
|
readonly operationsPlanned: number;
|
|
@@ -34,12 +74,67 @@ export interface MigrationCommandResult {
|
|
|
34
74
|
readonly storageHash: string;
|
|
35
75
|
readonly profileHash?: string;
|
|
36
76
|
};
|
|
77
|
+
/**
|
|
78
|
+
* Per-space execution breakdown in canonical schedule order
|
|
79
|
+
* (extensions alphabetically, then app). Surfaces per-space markers
|
|
80
|
+
* and the ops grouped by space, so the CLI summary can name which
|
|
81
|
+
* space each op and marker belongs to instead of flattening them
|
|
82
|
+
* into a single ambiguous list. See {@link AggregatePerSpaceExecutionEntry}.
|
|
83
|
+
*/
|
|
84
|
+
readonly perSpace?: ReadonlyArray<AggregatePerSpaceExecutionEntry>;
|
|
37
85
|
readonly summary: string;
|
|
38
86
|
readonly timings: {
|
|
39
87
|
readonly total: number;
|
|
40
88
|
};
|
|
41
89
|
}
|
|
42
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Render the shared per-space execution block consumed by the `db init`
|
|
93
|
+
* / `db update` / `migration apply` summaries. Always shows: space
|
|
94
|
+
* label (`Extension space: <id>` or `App space`) → per-op lines under
|
|
95
|
+
* each space → per-space marker hash (when known).
|
|
96
|
+
*
|
|
97
|
+
* `mode` controls the marker label phrasing — `'apply'` shows
|
|
98
|
+
* `marker → <hash>` (post-apply), `'plan'` omits the marker line
|
|
99
|
+
* entirely (no marker has been written yet).
|
|
100
|
+
*/
|
|
101
|
+
export function formatPerSpaceBlock(
|
|
102
|
+
perSpace: ReadonlyArray<AggregatePerSpaceExecutionEntry>,
|
|
103
|
+
mode: 'plan' | 'apply',
|
|
104
|
+
useColor: boolean,
|
|
105
|
+
): readonly string[] {
|
|
106
|
+
const formatYellow = createColorFormatter(useColor, yellow);
|
|
107
|
+
const formatCyan = createColorFormatter(useColor, cyan);
|
|
108
|
+
const formatDimText = (text: string) => formatDim(useColor, text);
|
|
109
|
+
|
|
110
|
+
const lines: string[] = [];
|
|
111
|
+
for (let s = 0; s < perSpace.length; s++) {
|
|
112
|
+
const space = perSpace[s]!;
|
|
113
|
+
if (s > 0) lines.push('');
|
|
114
|
+
const header =
|
|
115
|
+
space.kind === 'app'
|
|
116
|
+
? formatCyan('App space')
|
|
117
|
+
: formatCyan(`Extension space: ${space.spaceId}`);
|
|
118
|
+
lines.push(header);
|
|
119
|
+
if (space.operations.length === 0) {
|
|
120
|
+
lines.push(` ${formatDimText('(no operations)')}`);
|
|
121
|
+
} else {
|
|
122
|
+
for (let i = 0; i < space.operations.length; i++) {
|
|
123
|
+
const op = space.operations[i]!;
|
|
124
|
+
const isLast = i === space.operations.length - 1;
|
|
125
|
+
const treeChar = isLast ? '└' : '├';
|
|
126
|
+
const destructiveMarker =
|
|
127
|
+
op.operationClass === 'destructive' ? ` ${formatYellow('(destructive)')}` : '';
|
|
128
|
+
lines.push(` ${formatDimText(treeChar)}─ ${op.label}${destructiveMarker}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (mode === 'apply' && space.marker) {
|
|
132
|
+
lines.push(` ${formatDimText(`marker: ${space.marker.storageHash}`)}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return lines;
|
|
136
|
+
}
|
|
137
|
+
|
|
43
138
|
/**
|
|
44
139
|
* Formats human-readable output for migration commands (db init, db update) in plan mode.
|
|
45
140
|
*/
|
|
@@ -59,22 +154,43 @@ export function formatMigrationPlanOutput(
|
|
|
59
154
|
|
|
60
155
|
// Plan summary
|
|
61
156
|
const operationCount = result.plan?.operations.length ?? 0;
|
|
62
|
-
|
|
157
|
+
const spaceCount = result.perSpace?.length ?? 0;
|
|
158
|
+
if (spaceCount > 0) {
|
|
159
|
+
lines.push(
|
|
160
|
+
`${formatGreen('✔')} Planned ${operationCount} operation(s) across ${spaceCount} contract space${spaceCount === 1 ? '' : 's'}`,
|
|
161
|
+
);
|
|
162
|
+
} else {
|
|
163
|
+
lines.push(`${formatGreen('✔')} Planned ${operationCount} operation(s)`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const formatYellow = createColorFormatter(useColor, yellow);
|
|
63
167
|
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
168
|
+
// Per-space breakdown takes precedence over the flat ops tree when
|
|
169
|
+
// the aggregate flow surfaced one.
|
|
170
|
+
if (result.perSpace && result.perSpace.length > 0) {
|
|
171
|
+
lines.push('');
|
|
172
|
+
lines.push(...formatPerSpaceBlock(result.perSpace, 'plan', useColor));
|
|
173
|
+
const hasDestructive = result.perSpace.some((s) =>
|
|
174
|
+
s.operations.some((op) => op.operationClass === 'destructive'),
|
|
175
|
+
);
|
|
176
|
+
if (hasDestructive) {
|
|
177
|
+
lines.push('');
|
|
178
|
+
lines.push(
|
|
179
|
+
`${formatYellow('⚠')} This migration contains destructive operations that may cause data loss.`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
} else if (result.plan?.operations && result.plan.operations.length > 0) {
|
|
183
|
+
// Single-space fallback (no aggregate breakdown). Same flat tree
|
|
184
|
+
// we've always rendered.
|
|
67
185
|
lines.push(`${formatDimText('│')}`);
|
|
68
186
|
for (let i = 0; i < result.plan.operations.length; i++) {
|
|
69
187
|
const op = result.plan.operations[i];
|
|
70
188
|
if (!op) continue;
|
|
71
189
|
const isLast = i === result.plan.operations.length - 1;
|
|
72
190
|
const treeChar = isLast ? '└' : '├';
|
|
73
|
-
const
|
|
74
|
-
op.operationClass === 'destructive'
|
|
75
|
-
|
|
76
|
-
: formatDimText(`[${op.operationClass}]`);
|
|
77
|
-
lines.push(`${formatDimText(treeChar)}─ ${op.label} ${opClassLabel}`);
|
|
191
|
+
const destructiveMarker =
|
|
192
|
+
op.operationClass === 'destructive' ? ` ${formatYellow('(destructive)')}` : '';
|
|
193
|
+
lines.push(`${formatDimText(treeChar)}─ ${op.label}${destructiveMarker}`);
|
|
78
194
|
}
|
|
79
195
|
|
|
80
196
|
const hasDestructive = result.plan.operations.some((op) => op.operationClass === 'destructive');
|
|
@@ -92,20 +208,20 @@ export function formatMigrationPlanOutput(
|
|
|
92
208
|
lines.push(`${formatDimText(`Destination hash: ${result.plan.destination.storageHash}`)}`);
|
|
93
209
|
}
|
|
94
210
|
|
|
95
|
-
//
|
|
96
|
-
const
|
|
97
|
-
if (
|
|
211
|
+
// Statement preview (any family that implements OperationPreviewCapable)
|
|
212
|
+
const preview = result.plan?.preview;
|
|
213
|
+
if (preview) {
|
|
98
214
|
lines.push('');
|
|
99
|
-
lines.push(`${formatDimText(
|
|
100
|
-
if (
|
|
101
|
-
lines.push(`${formatDimText('No
|
|
215
|
+
lines.push(`${formatDimText(previewBlockHeader(preview))}`);
|
|
216
|
+
if (preview.statements.length === 0) {
|
|
217
|
+
lines.push(`${formatDimText('No operations.')}`);
|
|
102
218
|
} else {
|
|
103
219
|
lines.push('');
|
|
104
|
-
for (const statement of
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
107
|
-
|
|
108
|
-
|
|
220
|
+
for (const statement of preview.statements) {
|
|
221
|
+
const rendered = renderPreviewStatement(statement.text, statement.language);
|
|
222
|
+
if (rendered) {
|
|
223
|
+
lines.push(rendered);
|
|
224
|
+
}
|
|
109
225
|
}
|
|
110
226
|
}
|
|
111
227
|
}
|
|
@@ -127,10 +243,20 @@ export interface MigrationApplyCommandOutputResult {
|
|
|
127
243
|
readonly migrationsApplied: number;
|
|
128
244
|
readonly markerHash: string;
|
|
129
245
|
readonly applied: readonly {
|
|
130
|
-
readonly
|
|
246
|
+
readonly spaceId: string;
|
|
247
|
+
readonly dirName?: string;
|
|
248
|
+
readonly migrationHash?: string;
|
|
249
|
+
readonly from?: string;
|
|
250
|
+
readonly to?: string;
|
|
131
251
|
readonly operationsExecuted: number;
|
|
132
252
|
}[];
|
|
133
253
|
readonly summary: string;
|
|
254
|
+
/**
|
|
255
|
+
* Per-space breakdown in canonical schedule order (extensions
|
|
256
|
+
* alphabetically, then app). Always present for the aggregate-walking
|
|
257
|
+
* `migration apply` command.
|
|
258
|
+
*/
|
|
259
|
+
readonly perSpace: readonly AggregatePerSpaceExecutionEntry[];
|
|
134
260
|
readonly timings?: {
|
|
135
261
|
readonly total: number;
|
|
136
262
|
};
|
|
@@ -149,26 +275,17 @@ export function formatMigrationApplyCommandOutput(
|
|
|
149
275
|
const formatGreen = createColorFormatter(useColor, green);
|
|
150
276
|
const formatDimText = (text: string) => formatDim(useColor, text);
|
|
151
277
|
|
|
152
|
-
if (result.migrationsApplied === 0) {
|
|
153
|
-
lines.push(`${formatGreen('✔')} ${result.summary}`);
|
|
154
|
-
lines.push(formatDimText(` marker: ${result.markerHash}`));
|
|
155
|
-
return lines.join('\n');
|
|
156
|
-
}
|
|
157
|
-
|
|
158
278
|
lines.push(`${formatGreen('✔')} ${result.summary}`);
|
|
159
|
-
lines.push('');
|
|
160
279
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
`${formatDimText(treeChar)}─ ${migration.dirName} ${formatDimText(`[${migration.operationsExecuted} op(s)]`)}`,
|
|
167
|
-
);
|
|
280
|
+
if (result.perSpace.length > 0) {
|
|
281
|
+
lines.push('');
|
|
282
|
+
for (const line of formatPerSpaceBlock(result.perSpace, 'apply', useColor)) {
|
|
283
|
+
lines.push(line);
|
|
284
|
+
}
|
|
168
285
|
}
|
|
169
286
|
|
|
170
287
|
lines.push('');
|
|
171
|
-
lines.push(formatDimText(
|
|
288
|
+
lines.push(formatDimText('Next: prisma-next migration status'));
|
|
172
289
|
|
|
173
290
|
if (isVerbose(flags, 1) && result.timings) {
|
|
174
291
|
lines.push('');
|
|
@@ -178,59 +295,66 @@ export function formatMigrationApplyCommandOutput(
|
|
|
178
295
|
return lines.join('\n');
|
|
179
296
|
}
|
|
180
297
|
|
|
181
|
-
interface
|
|
298
|
+
interface MigrationShowSpacePresent {
|
|
299
|
+
readonly kind: 'present';
|
|
300
|
+
readonly spaceId: string;
|
|
182
301
|
readonly dirName: string;
|
|
183
302
|
readonly dirPath: string;
|
|
184
|
-
readonly from: string;
|
|
303
|
+
readonly from: string | null;
|
|
185
304
|
readonly to: string;
|
|
186
|
-
readonly
|
|
187
|
-
readonly kind: string;
|
|
305
|
+
readonly migrationHash: string;
|
|
188
306
|
readonly createdAt: string;
|
|
189
307
|
readonly operations: readonly {
|
|
190
308
|
readonly id: string;
|
|
191
309
|
readonly label: string;
|
|
192
310
|
readonly operationClass: string;
|
|
193
311
|
}[];
|
|
194
|
-
readonly
|
|
312
|
+
readonly preview: OperationPreview;
|
|
195
313
|
readonly summary: string;
|
|
196
314
|
}
|
|
197
315
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
316
|
+
interface MigrationShowSpaceMissing {
|
|
317
|
+
readonly kind: 'missing';
|
|
318
|
+
readonly spaceId: string;
|
|
319
|
+
readonly summary: string;
|
|
320
|
+
}
|
|
202
321
|
|
|
203
|
-
|
|
322
|
+
type MigrationShowSpaceResult = MigrationShowSpacePresent | MigrationShowSpaceMissing;
|
|
204
323
|
|
|
205
|
-
|
|
324
|
+
interface MigrationShowResult {
|
|
325
|
+
readonly spaces: readonly MigrationShowSpaceResult[];
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function formatSpaceShowBlock(
|
|
329
|
+
space: MigrationShowSpacePresent,
|
|
330
|
+
useColor: boolean,
|
|
331
|
+
): readonly string[] {
|
|
206
332
|
const formatGreen = createColorFormatter(useColor, green);
|
|
207
333
|
const formatYellow = createColorFormatter(useColor, yellow);
|
|
208
334
|
const formatDimText = (text: string) => formatDim(useColor, text);
|
|
209
335
|
|
|
210
|
-
lines
|
|
211
|
-
lines.push(`${
|
|
212
|
-
lines.push(`${formatDimText(` from: ${
|
|
213
|
-
lines.push(`${formatDimText(` to: ${
|
|
214
|
-
lines.push(`${formatDimText(`
|
|
215
|
-
lines.push(`${formatDimText(` created: ${
|
|
336
|
+
const lines: string[] = [];
|
|
337
|
+
lines.push(`${formatGreen('✔')} ${space.dirName}`);
|
|
338
|
+
lines.push(`${formatDimText(` from: ${space.from ?? '(baseline)'}`)}`);
|
|
339
|
+
lines.push(`${formatDimText(` to: ${space.to}`)}`);
|
|
340
|
+
lines.push(`${formatDimText(` migrationHash: ${space.migrationHash}`)}`);
|
|
341
|
+
lines.push(`${formatDimText(` created: ${space.createdAt}`)}`);
|
|
216
342
|
|
|
217
343
|
lines.push('');
|
|
218
|
-
lines.push(`${
|
|
344
|
+
lines.push(`${space.operations.length} operation(s)`);
|
|
219
345
|
|
|
220
|
-
if (
|
|
346
|
+
if (space.operations.length > 0) {
|
|
221
347
|
lines.push(`${formatDimText('│')}`);
|
|
222
|
-
for (let i = 0; i <
|
|
223
|
-
const op =
|
|
224
|
-
const isLast = i ===
|
|
348
|
+
for (let i = 0; i < space.operations.length; i++) {
|
|
349
|
+
const op = space.operations[i]!;
|
|
350
|
+
const isLast = i === space.operations.length - 1;
|
|
225
351
|
const treeChar = isLast ? '└' : '├';
|
|
226
|
-
const
|
|
227
|
-
op.operationClass === 'destructive'
|
|
228
|
-
|
|
229
|
-
: formatDimText(`[${op.operationClass}]`);
|
|
230
|
-
lines.push(`${formatDimText(treeChar)}─ ${op.label} ${opClassLabel}`);
|
|
352
|
+
const destructiveMarker =
|
|
353
|
+
op.operationClass === 'destructive' ? ` ${formatYellow('(destructive)')}` : '';
|
|
354
|
+
lines.push(`${formatDimText(treeChar)}─ ${op.label}${destructiveMarker}`);
|
|
231
355
|
}
|
|
232
356
|
|
|
233
|
-
const hasDestructive =
|
|
357
|
+
const hasDestructive = space.operations.some((op) => op.operationClass === 'destructive');
|
|
234
358
|
if (hasDestructive) {
|
|
235
359
|
lines.push('');
|
|
236
360
|
lines.push(
|
|
@@ -239,15 +363,45 @@ export function formatMigrationShowOutput(result: MigrationShowResult, flags: Gl
|
|
|
239
363
|
}
|
|
240
364
|
}
|
|
241
365
|
|
|
242
|
-
if (
|
|
366
|
+
if (space.preview.statements.length > 0) {
|
|
243
367
|
lines.push('');
|
|
244
|
-
lines.push(`${formatDimText(
|
|
368
|
+
lines.push(`${formatDimText(previewBlockHeader(space.preview))}`);
|
|
245
369
|
lines.push('');
|
|
246
|
-
for (const statement of
|
|
247
|
-
const
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
-
|
|
370
|
+
for (const statement of space.preview.statements) {
|
|
371
|
+
const rendered = renderPreviewStatement(statement.text, statement.language);
|
|
372
|
+
if (rendered) {
|
|
373
|
+
lines.push(rendered);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return lines;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export function formatMigrationShowOutput(result: MigrationShowResult, flags: GlobalFlags): string {
|
|
382
|
+
if (flags.quiet) {
|
|
383
|
+
return '';
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const useColor = flags.color !== false;
|
|
387
|
+
const formatDimText = (text: string) => formatDim(useColor, text);
|
|
388
|
+
const multipleSpaces = result.spaces.length > 1;
|
|
389
|
+
const lines: string[] = [];
|
|
390
|
+
|
|
391
|
+
for (let i = 0; i < result.spaces.length; i++) {
|
|
392
|
+
const space = result.spaces[i]!;
|
|
393
|
+
if (multipleSpaces) {
|
|
394
|
+
lines.push(formatDimText(`── ${space.spaceId} ──`));
|
|
395
|
+
}
|
|
396
|
+
if (space.kind === 'missing') {
|
|
397
|
+
lines.push(formatDimText(` ${space.summary}`));
|
|
398
|
+
} else {
|
|
399
|
+
for (const line of formatSpaceShowBlock(space, useColor)) {
|
|
400
|
+
lines.push(line);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
if (i < result.spaces.length - 1) {
|
|
404
|
+
lines.push('');
|
|
251
405
|
}
|
|
252
406
|
}
|
|
253
407
|
|
|
@@ -274,15 +428,39 @@ export function formatMigrationApplyOutput(
|
|
|
274
428
|
if (result.ok) {
|
|
275
429
|
// Success summary
|
|
276
430
|
const executed = result.execution?.operationsExecuted ?? 0;
|
|
431
|
+
const spaceCount = result.perSpace?.length ?? 0;
|
|
432
|
+
|
|
277
433
|
if (executed === 0) {
|
|
278
|
-
|
|
434
|
+
const acrossClause =
|
|
435
|
+
spaceCount > 0 ? ` across ${spaceCount} contract space${spaceCount === 1 ? '' : 's'}` : '';
|
|
436
|
+
lines.push(`${formatGreen('✔')} Database already matches contract${acrossClause}`);
|
|
437
|
+
} else if (spaceCount > 0) {
|
|
438
|
+
lines.push(
|
|
439
|
+
`${formatGreen('✔')} Applied ${executed} operation(s) across ${spaceCount} contract space${spaceCount === 1 ? '' : 's'}`,
|
|
440
|
+
);
|
|
279
441
|
} else {
|
|
280
442
|
lines.push(`${formatGreen('✔')} Applied ${executed} operation(s)`);
|
|
281
443
|
}
|
|
282
444
|
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
|
|
445
|
+
// Per-space breakdown — replaces the single ambiguous `Signature:`
|
|
446
|
+
// line with a per-space marker + ops listing.
|
|
447
|
+
if (result.perSpace && result.perSpace.length > 0) {
|
|
448
|
+
lines.push('');
|
|
449
|
+
lines.push(...formatPerSpaceBlock(result.perSpace, 'apply', useColor));
|
|
450
|
+
lines.push('');
|
|
451
|
+
lines.push(
|
|
452
|
+
formatDimText(
|
|
453
|
+
`Run 'prisma-next migration status' to confirm ${
|
|
454
|
+
spaceCount === 1 ? 'the space is' : 'all spaces are'
|
|
455
|
+
} up to date.`,
|
|
456
|
+
),
|
|
457
|
+
);
|
|
458
|
+
} else if (result.marker) {
|
|
459
|
+
// Single-space fallback (no aggregate breakdown surfaced — e.g.
|
|
460
|
+
// older callers / non-aggregate code paths). The label is
|
|
461
|
+
// `App-space marker` (not `Signature`) so that when only one
|
|
462
|
+
// marker is observable we still name what it covers explicitly.
|
|
463
|
+
lines.push(`${formatDimText(` App-space marker: ${result.marker.storageHash}`)}`);
|
|
286
464
|
if (result.marker.profileHash) {
|
|
287
465
|
lines.push(`${formatDimText(` Profile hash: ${result.marker.profileHash}`)}`);
|
|
288
466
|
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { basename, dirname, join } from 'pathe';
|
|
3
|
+
|
|
4
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
5
|
+
return typeof value === 'object' && value !== null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function createTempArtifactPath(path: string, publicationToken: string, phase: string): string {
|
|
9
|
+
return join(dirname(path), `.${basename(path)}.${process.pid}.${publicationToken}.${phase}.tmp`);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type PreviousArtifact = { readonly content: string } | 'remove';
|
|
13
|
+
|
|
14
|
+
async function readExistingArtifact(path: string): Promise<PreviousArtifact> {
|
|
15
|
+
try {
|
|
16
|
+
return { content: await readFile(path, 'utf-8') };
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (isRecord(error) && error['code'] === 'ENOENT') {
|
|
19
|
+
return 'remove';
|
|
20
|
+
}
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function restoreArtifact(
|
|
26
|
+
path: string,
|
|
27
|
+
previous: PreviousArtifact,
|
|
28
|
+
publicationToken: string,
|
|
29
|
+
): Promise<void> {
|
|
30
|
+
if (previous === 'remove') {
|
|
31
|
+
await rm(path, { force: true });
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const restorePath = createTempArtifactPath(path, publicationToken, 'rollback');
|
|
36
|
+
await writeFile(restorePath, previous.content, 'utf-8');
|
|
37
|
+
try {
|
|
38
|
+
await rename(restorePath, path);
|
|
39
|
+
} finally {
|
|
40
|
+
await rm(restorePath, { force: true });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface PublishEntry {
|
|
45
|
+
readonly tempPath: string;
|
|
46
|
+
readonly outputPath: string;
|
|
47
|
+
readonly previous: PreviousArtifact;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function withRollbackFailureCause(error: unknown, rollbackFailures: readonly unknown[]): Error {
|
|
51
|
+
const rollbackCause = new AggregateError(
|
|
52
|
+
rollbackFailures,
|
|
53
|
+
'Failed to restore published artifacts',
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
if (error instanceof Error) {
|
|
57
|
+
Object.defineProperty(error, 'cause', {
|
|
58
|
+
value: rollbackCause,
|
|
59
|
+
configurable: true,
|
|
60
|
+
writable: true,
|
|
61
|
+
});
|
|
62
|
+
return error;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return new Error(String(error), { cause: rollbackCause });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function publishPairWithRollback(
|
|
69
|
+
entries: readonly PublishEntry[],
|
|
70
|
+
publicationToken: string,
|
|
71
|
+
): Promise<void> {
|
|
72
|
+
const replaced: PublishEntry[] = [];
|
|
73
|
+
try {
|
|
74
|
+
for (const entry of entries) {
|
|
75
|
+
await rename(entry.tempPath, entry.outputPath);
|
|
76
|
+
replaced.push(entry);
|
|
77
|
+
}
|
|
78
|
+
} catch (error) {
|
|
79
|
+
const rollbackResults = await Promise.allSettled(
|
|
80
|
+
replaced.map((entry) => restoreArtifact(entry.outputPath, entry.previous, publicationToken)),
|
|
81
|
+
);
|
|
82
|
+
const rollbackFailures = rollbackResults.flatMap((result) =>
|
|
83
|
+
result.status === 'rejected' ? [result.reason] : [],
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (rollbackFailures.length > 0) {
|
|
87
|
+
throw withRollbackFailureCause(error, rollbackFailures);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export async function publishContractArtifactPair({
|
|
95
|
+
outputJsonPath,
|
|
96
|
+
outputDtsPath,
|
|
97
|
+
contractJson,
|
|
98
|
+
contractDts,
|
|
99
|
+
publicationToken,
|
|
100
|
+
beforePublish,
|
|
101
|
+
}: {
|
|
102
|
+
readonly outputJsonPath: string;
|
|
103
|
+
readonly outputDtsPath: string;
|
|
104
|
+
readonly contractJson: string;
|
|
105
|
+
readonly contractDts: string;
|
|
106
|
+
readonly publicationToken: string;
|
|
107
|
+
readonly beforePublish?: () => Promise<boolean> | boolean;
|
|
108
|
+
}): Promise<boolean> {
|
|
109
|
+
const tempJsonPath = createTempArtifactPath(outputJsonPath, publicationToken, 'next');
|
|
110
|
+
const tempDtsPath = createTempArtifactPath(outputDtsPath, publicationToken, 'next');
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
await writeFile(tempJsonPath, contractJson, 'utf-8');
|
|
114
|
+
await writeFile(tempDtsPath, contractDts, 'utf-8');
|
|
115
|
+
|
|
116
|
+
if ((await beforePublish?.()) === false) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const previousJson = await readExistingArtifact(outputJsonPath);
|
|
121
|
+
const previousDts = await readExistingArtifact(outputDtsPath);
|
|
122
|
+
|
|
123
|
+
await publishPairWithRollback(
|
|
124
|
+
[
|
|
125
|
+
{ tempPath: tempDtsPath, outputPath: outputDtsPath, previous: previousDts },
|
|
126
|
+
{ tempPath: tempJsonPath, outputPath: outputJsonPath, previous: previousJson },
|
|
127
|
+
],
|
|
128
|
+
publicationToken,
|
|
129
|
+
);
|
|
130
|
+
return true;
|
|
131
|
+
} finally {
|
|
132
|
+
await Promise.allSettled([rm(tempJsonPath, { force: true }), rm(tempDtsPath, { force: true })]);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { CliStructuredError as CliStructuredError$1, errorConfigValidation as errorConfigValidation$1, errorContractConfigMissing as errorContractConfigMissing$1, errorContractValidationFailed, errorDatabaseConnectionRequired, errorDriverRequired, errorFileNotFound, errorMigrationPlanningFailed, errorTargetMigrationNotSupported, errorUnexpected as errorUnexpected$1 } from "@prisma-next/errors/control";
|
|
2
|
-
import { ERROR_CODE_DESTRUCTIVE_CHANGES, errorDestructiveChanges, errorHashMismatch, errorMarkerMissing, errorRunnerFailed, errorRuntime as errorRuntime$1, errorTargetMismatch } from "@prisma-next/errors/execution";
|
|
3
|
-
import "@prisma-next/errors/migration";
|
|
4
|
-
|
|
5
|
-
export { errorUnexpected$1 as _, errorContractValidationFailed as a, errorDriverRequired as c, errorMarkerMissing as d, errorMigrationPlanningFailed as f, errorTargetMismatch as g, errorTargetMigrationNotSupported as h, errorContractConfigMissing$1 as i, errorFileNotFound as l, errorRuntime$1 as m, ERROR_CODE_DESTRUCTIVE_CHANGES as n, errorDatabaseConnectionRequired as o, errorRunnerFailed as p, errorConfigValidation$1 as r, errorDestructiveChanges as s, CliStructuredError$1 as t, errorHashMismatch as u };
|