prisma-next 0.12.0-dev.4 → 0.12.0-dev.40
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.mjs +180 -163
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-KgJorIvG.mjs → client-BNdG504y.mjs} +80 -56
- package/dist/client-BNdG504y.mjs.map +1 -0
- package/dist/{command-helpers-Bbw1GbwL.mjs → command-helpers-xvg9oq4T.mjs} +301 -23
- package/dist/command-helpers-xvg9oq4T.mjs.map +1 -0
- package/dist/commands/contract-emit.mjs +1 -1
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.mjs +4 -5
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.mjs +3 -3
- package/dist/commands/db-sign.mjs +4 -4
- package/dist/commands/db-update.d.mts.map +1 -1
- package/dist/commands/db-update.mjs +10 -7
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +2 -2
- package/dist/commands/migrate.d.mts.map +1 -1
- package/dist/commands/migrate.mjs +6 -8
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-check.d.mts +55 -1
- package/dist/commands/migration-check.d.mts.map +1 -1
- package/dist/commands/migration-check.mjs +2 -2
- package/dist/commands/migration-graph.d.mts +25 -7
- package/dist/commands/migration-graph.d.mts.map +1 -1
- package/dist/commands/migration-graph.mjs +170 -2
- package/dist/commands/migration-graph.mjs.map +1 -0
- package/dist/commands/migration-list.d.mts +24 -26
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +2 -190
- package/dist/commands/migration-log.d.mts +20 -15
- package/dist/commands/migration-log.d.mts.map +1 -1
- package/dist/commands/migration-log.mjs +1 -137
- package/dist/commands/migration-new.mjs +3 -3
- package/dist/commands/migration-plan.d.mts +1 -1
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-show.d.mts +1 -4
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +13 -25
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +41 -141
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +2 -759
- package/dist/commands/ref.d.mts +1 -1
- package/dist/commands/ref.mjs +3 -3
- package/dist/commands/telemetry/index.d.mts +7 -0
- package/dist/commands/telemetry/index.d.mts.map +1 -0
- package/dist/commands/telemetry/index.mjs +2 -0
- package/dist/{contract-at-errors-BxP-TOMl.mjs → contract-at-errors-Wj3u4Xco.mjs} +2 -2
- package/dist/{contract-at-errors-BxP-TOMl.mjs.map → contract-at-errors-Wj3u4Xco.mjs.map} +1 -1
- package/dist/{contract-emit-DxcGl4Uq.mjs → contract-emit-COg18szA.mjs} +3 -3
- package/dist/{contract-emit-DxcGl4Uq.mjs.map → contract-emit-COg18szA.mjs.map} +1 -1
- package/dist/{contract-emit-D-4jrNve.mjs → contract-emit-KyJNQK5-.mjs} +3 -3
- package/dist/{contract-emit-D-4jrNve.mjs.map → contract-emit-KyJNQK5-.mjs.map} +1 -1
- package/dist/{contract-infer-D8uEbJuu.mjs → contract-infer-IEp0227u.mjs} +3 -3
- package/dist/{contract-infer-D8uEbJuu.mjs.map → contract-infer-IEp0227u.mjs.map} +1 -1
- package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs → contract-space-aggregate-loader-BdRPfM3Q.mjs} +63 -5
- package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs.map → contract-space-aggregate-loader-BdRPfM3Q.mjs.map} +1 -1
- package/dist/{db-verify-v_vUKXTU.mjs → db-verify-C9k5KAyI.mjs} +4 -4
- package/dist/{db-verify-v_vUKXTU.mjs.map → db-verify-C9k5KAyI.mjs.map} +1 -1
- package/dist/exports/control-api.d.mts +2 -2
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +2 -2
- package/dist/exports/index.mjs +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{framework-components-fYXjz_in.mjs → framework-components-Be4inY3I.mjs} +2 -2
- package/dist/{framework-components-fYXjz_in.mjs.map → framework-components-Be4inY3I.mjs.map} +1 -1
- package/dist/{global-flags-DEHjV8_s.d.mts → global-flags-DG4uY5tV.d.mts} +1 -1
- package/dist/{global-flags-DEHjV8_s.d.mts.map → global-flags-DG4uY5tV.d.mts.map} +1 -1
- package/dist/{init-Cv9UzWL5.mjs → init-BIxw3l7t.mjs} +5 -58
- package/dist/init-BIxw3l7t.mjs.map +1 -0
- package/dist/{inspect-live-schema-C6ohV_oQ.mjs → inspect-live-schema-DXUFGQDe.mjs} +3 -3
- package/dist/{inspect-live-schema-C6ohV_oQ.mjs.map → inspect-live-schema-DXUFGQDe.mjs.map} +1 -1
- package/dist/{migration-check-BiBJoYYW.mjs → migration-check-CUavU7U9.mjs} +236 -88
- package/dist/migration-check-CUavU7U9.mjs.map +1 -0
- package/dist/{migration-command-scaffold-CjvwO6at.mjs → migration-command-scaffold-omgKpt3K.mjs} +3 -3
- package/dist/{migration-command-scaffold-CjvwO6at.mjs.map → migration-command-scaffold-omgKpt3K.mjs.map} +1 -1
- package/dist/migration-graph-space-render-ByJ83gxp.mjs +1966 -0
- package/dist/migration-graph-space-render-ByJ83gxp.mjs.map +1 -0
- package/dist/migration-list-jK6QeczE.mjs +228 -0
- package/dist/migration-list-jK6QeczE.mjs.map +1 -0
- package/dist/migration-list-types-DS63IdFd.d.mts +23 -0
- package/dist/migration-list-types-DS63IdFd.d.mts.map +1 -0
- package/dist/migration-log-CW0EjxSr.mjs +215 -0
- package/dist/migration-log-CW0EjxSr.mjs.map +1 -0
- package/dist/migration-path-target-DqcrbOis.mjs +24 -0
- package/dist/migration-path-target-DqcrbOis.mjs.map +1 -0
- package/dist/{migration-plan-9DJ7q7_z.mjs → migration-plan-NHdlUwPG.mjs} +5 -6
- package/dist/{migration-plan-9DJ7q7_z.mjs.map → migration-plan-NHdlUwPG.mjs.map} +1 -1
- package/dist/migration-status-GZ6XfbWs.mjs +439 -0
- package/dist/migration-status-GZ6XfbWs.mjs.map +1 -0
- package/dist/{output-B60Gw5fu.mjs → output-CF_hqzI-.mjs} +1 -1
- package/dist/{output-B60Gw5fu.mjs.map → output-CF_hqzI-.mjs.map} +1 -1
- package/dist/{ref-advancement-DUZqsue6.mjs → ref-advancement-CJY9zOv7.mjs} +1 -1
- package/dist/{ref-advancement-DUZqsue6.mjs.map → ref-advancement-CJY9zOv7.mjs.map} +1 -1
- package/dist/telemetry-DQP0BvKv.mjs +122 -0
- package/dist/telemetry-DQP0BvKv.mjs.map +1 -0
- package/dist/{types-Dt_SfqFm.d.mts → types-Cculk5KV.d.mts} +44 -31
- package/dist/types-Cculk5KV.d.mts.map +1 -0
- package/dist/{verify-DCA9Sldu.mjs → verify-tvHRBBVP.mjs} +2 -2
- package/dist/{verify-DCA9Sldu.mjs.map → verify-tvHRBBVP.mjs.map} +1 -1
- package/package.json +11 -12
- package/dist/client-KgJorIvG.mjs.map +0 -1
- package/dist/command-helpers-Bbw1GbwL.mjs.map +0 -1
- package/dist/commands/migration-list.mjs.map +0 -1
- package/dist/commands/migration-log.mjs.map +0 -1
- package/dist/commands/migration-status.mjs.map +0 -1
- package/dist/extension-pack-inputs-IDvjRCi3.mjs +0 -62
- package/dist/extension-pack-inputs-IDvjRCi3.mjs.map +0 -1
- package/dist/graph-render-rFAqZujX.mjs +0 -1081
- package/dist/graph-render-rFAqZujX.mjs.map +0 -1
- package/dist/init-Cv9UzWL5.mjs.map +0 -1
- package/dist/migration-check-BiBJoYYW.mjs.map +0 -1
- package/dist/migration-graph-D7DVUElV.mjs +0 -1232
- package/dist/migration-graph-D7DVUElV.mjs.map +0 -1
- package/dist/migration-list-styler-BRwF4-gy.mjs +0 -399
- package/dist/migration-list-styler-BRwF4-gy.mjs.map +0 -1
- package/dist/migration-types-D2FW63pr.d.mts +0 -15
- package/dist/migration-types-D2FW63pr.d.mts.map +0 -1
- package/dist/migrations-Cv2jxNNK.mjs +0 -228
- package/dist/migrations-Cv2jxNNK.mjs.map +0 -1
- package/dist/types-Dt_SfqFm.d.mts.map +0 -1
|
@@ -1,759 +1,2 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
|
|
3
|
-
import { t as createControlClient } from "../client-KgJorIvG.mjs";
|
|
4
|
-
import { t as toDeclaredExtensionsFromRaw } from "../extension-pack-inputs-IDvjRCi3.mjs";
|
|
5
|
-
import { i as loadContractRawSafely, o as refuseContractSpaceIntegrity, s as refusePackageCorruptionOnAggregate, t as appContractStandInFromIdentity } from "../contract-space-aggregate-loader-DvZwdkrr.mjs";
|
|
6
|
-
import { i as migrationGraphToRenderInput, n as graphRenderer, r as isLinearGraph, t as extractRelevantSubgraph } from "../graph-render-rFAqZujX.mjs";
|
|
7
|
-
import { Command } from "commander";
|
|
8
|
-
import { ifDefined } from "@prisma-next/utils/defined";
|
|
9
|
-
import { notOk, ok } from "@prisma-next/utils/result";
|
|
10
|
-
import { createControlStack } from "@prisma-next/framework-components/control";
|
|
11
|
-
import { cyan, dim, magenta, yellow } from "colorette";
|
|
12
|
-
import { graphWalkStrategy, loadContractSpaceAggregate, requireHeadRef } from "@prisma-next/migration-tools/aggregate";
|
|
13
|
-
import { EMPTY_CONTRACT_HASH } from "@prisma-next/migration-tools/constants";
|
|
14
|
-
import { MigrationToolsError, errorNoInvariantPath, errorUnknownInvariant } from "@prisma-next/migration-tools/errors";
|
|
15
|
-
import { findPath, findPathWithDecision, findReachableLeaves } from "@prisma-next/migration-tools/migration-graph";
|
|
16
|
-
import { readRefs } from "@prisma-next/migration-tools/refs";
|
|
17
|
-
import { parseContractRef } from "@prisma-next/migration-tools/ref-resolution";
|
|
18
|
-
//#region src/commands/migration-status.ts
|
|
19
|
-
/**
|
|
20
|
-
* Sum per-space `pendingCount` into a cross-space total, but only when
|
|
21
|
-
* every loaded space reports a defined `pendingCount`. Returns
|
|
22
|
-
* `undefined` if any space is on the marker-unknown / offline path
|
|
23
|
-
* (where `pendingCount` is intentionally absent), so JSON consumers can
|
|
24
|
-
* distinguish "no pending" from "unknown".
|
|
25
|
-
*/
|
|
26
|
-
function computeTotalPendingAcrossSpaces(spaces) {
|
|
27
|
-
if (spaces.length === 0) return void 0;
|
|
28
|
-
let total = 0;
|
|
29
|
-
for (const s of spaces) {
|
|
30
|
-
if (s.pendingCount === void 0) return void 0;
|
|
31
|
-
total += s.pendingCount;
|
|
32
|
-
}
|
|
33
|
-
return total;
|
|
34
|
-
}
|
|
35
|
-
function summarizeOps(ops) {
|
|
36
|
-
if (ops.length === 0) return {
|
|
37
|
-
summary: "0 ops",
|
|
38
|
-
hasDestructive: false
|
|
39
|
-
};
|
|
40
|
-
const classes = /* @__PURE__ */ new Map();
|
|
41
|
-
for (const op of ops) classes.set(op.operationClass, (classes.get(op.operationClass) ?? 0) + 1);
|
|
42
|
-
const hasDestructive = classes.has("destructive");
|
|
43
|
-
const count = ops.length;
|
|
44
|
-
const noun = count === 1 ? "op" : "ops";
|
|
45
|
-
if (classes.size === 1) return {
|
|
46
|
-
summary: `${count} ${noun} (all ${[...classes.keys()][0]})`,
|
|
47
|
-
hasDestructive
|
|
48
|
-
};
|
|
49
|
-
const destructiveCount = classes.get("destructive");
|
|
50
|
-
if (destructiveCount) return {
|
|
51
|
-
summary: `${count} ${noun} (${destructiveCount} destructive)`,
|
|
52
|
-
hasDestructive
|
|
53
|
-
};
|
|
54
|
-
return {
|
|
55
|
-
summary: `${count} ${noun} (${[...classes.entries()].map(([cls, n]) => `${n} ${cls}`).join(", ")})`,
|
|
56
|
-
hasDestructive
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Derive per-edge status across the full graph using path analysis.
|
|
61
|
-
*
|
|
62
|
-
* - **applied**: edge is on the path from root to the DB marker
|
|
63
|
-
* - **pending**: edge is on the path from the DB marker to the target
|
|
64
|
-
* (and the marker is reachable from root, i.e. it's on the same branch)
|
|
65
|
-
* - **unreachable**: edge is on the path from root to the target but the DB
|
|
66
|
-
* marker is on a different branch — `apply` can't reach these edges
|
|
67
|
-
* without the DB first moving to this branch
|
|
68
|
-
*
|
|
69
|
-
* Returns statuses only for edges that have a known status (skips offline
|
|
70
|
-
* and edges not on any relevant path).
|
|
71
|
-
*
|
|
72
|
-
* @internal Exported for testing only.
|
|
73
|
-
*/
|
|
74
|
-
function deriveEdgeStatuses(graph, targetHash, contractHash, markerHash, mode) {
|
|
75
|
-
if (mode === "offline") return [];
|
|
76
|
-
const edgeKey = (e) => `${e.from}\0${e.to}`;
|
|
77
|
-
const effectiveMarker = markerHash ?? EMPTY_CONTRACT_HASH;
|
|
78
|
-
const appliedPath = markerHash !== void 0 ? findPath(graph, EMPTY_CONTRACT_HASH, markerHash) : null;
|
|
79
|
-
const pendingPath = findPath(graph, effectiveMarker, targetHash);
|
|
80
|
-
const targetPath = findPath(graph, EMPTY_CONTRACT_HASH, targetHash);
|
|
81
|
-
const statuses = [];
|
|
82
|
-
const assignedKeys = /* @__PURE__ */ new Set();
|
|
83
|
-
if (appliedPath) for (const e of appliedPath) {
|
|
84
|
-
assignedKeys.add(edgeKey(e));
|
|
85
|
-
statuses.push({
|
|
86
|
-
dirName: e.dirName,
|
|
87
|
-
status: "applied"
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
if (pendingPath) for (const e of pendingPath) {
|
|
91
|
-
assignedKeys.add(edgeKey(e));
|
|
92
|
-
statuses.push({
|
|
93
|
-
dirName: e.dirName,
|
|
94
|
-
status: "pending"
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
if (contractHash !== EMPTY_CONTRACT_HASH && contractHash !== targetHash && graph.nodes.has(contractHash)) {
|
|
98
|
-
const beyondTarget = findPath(graph, targetHash, contractHash);
|
|
99
|
-
if (beyondTarget) {
|
|
100
|
-
for (const e of beyondTarget) if (!assignedKeys.has(edgeKey(e))) {
|
|
101
|
-
assignedKeys.add(edgeKey(e));
|
|
102
|
-
statuses.push({
|
|
103
|
-
dirName: e.dirName,
|
|
104
|
-
status: "pending"
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
if (targetPath) {
|
|
110
|
-
for (const e of targetPath) if (!assignedKeys.has(edgeKey(e))) statuses.push({
|
|
111
|
-
dirName: e.dirName,
|
|
112
|
-
status: "unreachable"
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
return statuses;
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* @param mode — 'online' if we connected to the database, 'offline' otherwise
|
|
119
|
-
* @param markerHash — the marker hash from the database, or undefined if no marker row / offline
|
|
120
|
-
*/
|
|
121
|
-
function buildMigrationEntries(chain, packages, mode, markerHash, edgeStatuses) {
|
|
122
|
-
const pkgByDirName = new Map(packages.map((p) => [p.dirName, p]));
|
|
123
|
-
const statusByDirName = edgeStatuses ? new Map(edgeStatuses.map((e) => [e.dirName, e.status])) : void 0;
|
|
124
|
-
const markerInChain = markerHash === void 0 || chain.some((e) => e.to === markerHash);
|
|
125
|
-
const entries = [];
|
|
126
|
-
let reachedMarker = mode === "online" && markerHash === void 0;
|
|
127
|
-
for (const migration of chain) {
|
|
128
|
-
const ops = pkgByDirName.get(migration.dirName)?.ops ?? [];
|
|
129
|
-
const { summary, hasDestructive } = summarizeOps(ops);
|
|
130
|
-
let status;
|
|
131
|
-
const edgeStatus = statusByDirName?.get(migration.dirName);
|
|
132
|
-
if (edgeStatus) status = edgeStatus;
|
|
133
|
-
else if (mode === "offline" || !markerInChain) status = "unknown";
|
|
134
|
-
else if (reachedMarker) status = "pending";
|
|
135
|
-
else status = "applied";
|
|
136
|
-
entries.push({
|
|
137
|
-
dirName: migration.dirName,
|
|
138
|
-
from: migration.from,
|
|
139
|
-
to: migration.to,
|
|
140
|
-
migrationHash: migration.migrationHash,
|
|
141
|
-
operationCount: ops.length,
|
|
142
|
-
operationSummary: summary,
|
|
143
|
-
hasDestructive,
|
|
144
|
-
status
|
|
145
|
-
});
|
|
146
|
-
if (!reachedMarker && migration.to === markerHash) reachedMarker = true;
|
|
147
|
-
}
|
|
148
|
-
return entries;
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Resolve the migration chain to display in status output.
|
|
152
|
-
*
|
|
153
|
-
* When offline or the marker is at EMPTY, the chain is simply the shortest
|
|
154
|
-
* path from EMPTY to the target — all structural paths are equivalent per
|
|
155
|
-
* the spec, so the deterministic shortest path is the canonical display.
|
|
156
|
-
*
|
|
157
|
-
* When online with a non-empty marker, the chain routes *through* the marker:
|
|
158
|
-
* EMPTY→marker (applied history) + marker→target (pending edges). This ensures
|
|
159
|
-
* the displayed chain includes the marker node so applied/pending status is
|
|
160
|
-
* correct. Without this, BFS from EMPTY to target could pick a shortest path
|
|
161
|
-
* that bypasses the marker entirely (e.g. in a diamond graph), causing the
|
|
162
|
-
* marker to appear "diverged" when it isn't.
|
|
163
|
-
*/
|
|
164
|
-
function resolveDisplayChain(graph, targetHash, markerHash) {
|
|
165
|
-
if (markerHash === void 0) return findPath(graph, EMPTY_CONTRACT_HASH, targetHash);
|
|
166
|
-
const toMarker = findPath(graph, EMPTY_CONTRACT_HASH, markerHash);
|
|
167
|
-
if (!toMarker) return findPath(graph, EMPTY_CONTRACT_HASH, targetHash);
|
|
168
|
-
if (markerHash === targetHash) return toMarker;
|
|
169
|
-
const fromMarker = findPath(graph, markerHash, targetHash);
|
|
170
|
-
if (fromMarker) return [...toMarker, ...fromMarker];
|
|
171
|
-
const toTarget = findPath(graph, EMPTY_CONTRACT_HASH, targetHash);
|
|
172
|
-
if (!toTarget) return null;
|
|
173
|
-
const targetToMarker = findPath(graph, targetHash, markerHash);
|
|
174
|
-
if (targetToMarker) return [...toTarget, ...targetToMarker];
|
|
175
|
-
return toTarget;
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Build the aggregate enumeration of contract spaces for the status
|
|
179
|
-
* output. Loads the aggregate from disk (lossy on failure — extension
|
|
180
|
-
* spaces are simply omitted, the app member's output keeps working),
|
|
181
|
-
* reads per-space marker rows when online, and uses
|
|
182
|
-
* {@link graphWalkStrategy} to compute each space's pending count.
|
|
183
|
-
*
|
|
184
|
-
* The aggregate-walking status reports per-space marker + pending
|
|
185
|
-
* state alongside the cross-space totals.
|
|
186
|
-
*/
|
|
187
|
-
async function loadAggregateStatusSpaces(args) {
|
|
188
|
-
const declaredExtensions = toDeclaredExtensionsFromRaw(args.extensionPacks);
|
|
189
|
-
if (refuseContractSpaceIntegrity(args.aggregate, {
|
|
190
|
-
declaredExtensions,
|
|
191
|
-
checkContracts: true
|
|
192
|
-
})) return [];
|
|
193
|
-
const aggregate = args.aggregate;
|
|
194
|
-
const orderedMembers = [...aggregate.extensions, aggregate.app];
|
|
195
|
-
const rows = [];
|
|
196
|
-
for (const member of orderedMembers) {
|
|
197
|
-
const liveMarker = args.markersBySpace?.get(member.spaceId) ?? null;
|
|
198
|
-
const isApp = member.spaceId === aggregate.app.spaceId;
|
|
199
|
-
const headRef = requireHeadRef(member);
|
|
200
|
-
if (member.graph().nodes.size === 0) {
|
|
201
|
-
rows.push({
|
|
202
|
-
spaceId: member.spaceId,
|
|
203
|
-
kind: isApp ? "app" : "extension",
|
|
204
|
-
headHash: headRef.hash,
|
|
205
|
-
...args.markersBySpace !== null ? {
|
|
206
|
-
markerHash: liveMarker?.storageHash ?? null,
|
|
207
|
-
status: headRef.hash === EMPTY_CONTRACT_HASH ? "up-to-date" : "never-planned",
|
|
208
|
-
pendingCount: 0
|
|
209
|
-
} : {}
|
|
210
|
-
});
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
if (args.markersBySpace === null) {
|
|
214
|
-
rows.push({
|
|
215
|
-
spaceId: member.spaceId,
|
|
216
|
-
kind: isApp ? "app" : "extension",
|
|
217
|
-
headHash: headRef.hash
|
|
218
|
-
});
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
const walked = graphWalkStrategy({
|
|
222
|
-
aggregateTargetId: aggregate.targetId,
|
|
223
|
-
member,
|
|
224
|
-
currentMarker: liveMarker
|
|
225
|
-
});
|
|
226
|
-
let pendingCount = 0;
|
|
227
|
-
let status;
|
|
228
|
-
if (walked.kind === "ok") {
|
|
229
|
-
pendingCount = walked.result.migrationEdges?.length ?? 0;
|
|
230
|
-
if (liveMarker === null) status = pendingCount === 0 ? "no-marker" : "pending";
|
|
231
|
-
else status = pendingCount === 0 ? "up-to-date" : "pending";
|
|
232
|
-
} else status = "unreachable";
|
|
233
|
-
rows.push({
|
|
234
|
-
spaceId: member.spaceId,
|
|
235
|
-
kind: isApp ? "app" : "extension",
|
|
236
|
-
headHash: headRef.hash,
|
|
237
|
-
markerHash: liveMarker?.storageHash ?? null,
|
|
238
|
-
pendingCount,
|
|
239
|
-
...status ? { status } : {}
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
return rows;
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* Read the raw contract.json bytes from disk for the aggregate
|
|
246
|
-
* loader. Returns `null` if the file is missing or unparseable —
|
|
247
|
-
* the existing `readContractEnvelope` path will report the same
|
|
248
|
-
* problem via a status diagnostic, no need to double-surface.
|
|
249
|
-
*/
|
|
250
|
-
async function validateOnlineMarkerRead(config, dbConnection) {
|
|
251
|
-
const driver = config.driver;
|
|
252
|
-
if (!driver) return ok(void 0);
|
|
253
|
-
const client = createControlClient({
|
|
254
|
-
family: config.family,
|
|
255
|
-
target: config.target,
|
|
256
|
-
adapter: config.adapter,
|
|
257
|
-
driver,
|
|
258
|
-
extensionPacks: config.extensionPacks ?? []
|
|
259
|
-
});
|
|
260
|
-
try {
|
|
261
|
-
await client.connect(dbConnection);
|
|
262
|
-
await client.readMarker();
|
|
263
|
-
return ok(void 0);
|
|
264
|
-
} catch (error) {
|
|
265
|
-
if (CliStructuredError.is(error)) return notOk(error);
|
|
266
|
-
return notOk(errorUnexpected(error instanceof Error ? error.message : String(error), { why: `Failed to read database marker: ${error instanceof Error ? error.message : String(error)}` }));
|
|
267
|
-
} finally {
|
|
268
|
-
await client.close();
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
async function executeMigrationStatusCommand(options, flags, ui) {
|
|
272
|
-
const config = await loadConfig(options.config);
|
|
273
|
-
const { configPath, appMigrationsRelative, migrationsDir, refsDir } = resolveMigrationPaths(options.config, config);
|
|
274
|
-
const dbConnection = options.db ?? config.db?.connection;
|
|
275
|
-
const hasDriver = !!config.driver;
|
|
276
|
-
let activeRefName;
|
|
277
|
-
let activeRefHash;
|
|
278
|
-
let activeRefEntry;
|
|
279
|
-
let allRefs = {};
|
|
280
|
-
try {
|
|
281
|
-
allRefs = await readRefs(refsDir);
|
|
282
|
-
} catch (error) {
|
|
283
|
-
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
284
|
-
throw error;
|
|
285
|
-
}
|
|
286
|
-
const diagnostics = [];
|
|
287
|
-
let contractHash = EMPTY_CONTRACT_HASH;
|
|
288
|
-
try {
|
|
289
|
-
contractHash = (await readContractEnvelope(config)).storageHash;
|
|
290
|
-
} catch (error) {
|
|
291
|
-
diagnostics.push({
|
|
292
|
-
code: "CONTRACT.UNREADABLE",
|
|
293
|
-
severity: "warn",
|
|
294
|
-
message: `Could not read contract: ${error instanceof Error ? error.message : "unknown error"}`,
|
|
295
|
-
hints: ["Run 'prisma-next contract emit' to generate a valid contract"]
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
const contractRawForAggregate = await loadContractRawSafely(config);
|
|
299
|
-
const stack = createControlStack(config);
|
|
300
|
-
const familyInstance = config.family.create(stack);
|
|
301
|
-
const deserializeContract = (json) => familyInstance.deserializeContract(json);
|
|
302
|
-
let appContractForLoad = appContractStandInFromIdentity({
|
|
303
|
-
contractHash,
|
|
304
|
-
targetId: config.target.id,
|
|
305
|
-
targetFamily: config.target.familyId
|
|
306
|
-
});
|
|
307
|
-
if (contractRawForAggregate !== null) try {
|
|
308
|
-
appContractForLoad = deserializeContract(contractRawForAggregate);
|
|
309
|
-
} catch (error) {
|
|
310
|
-
diagnostics.push({
|
|
311
|
-
code: "CONTRACT.UNREADABLE",
|
|
312
|
-
severity: "warn",
|
|
313
|
-
message: `Could not deserialize contract: ${error instanceof Error ? error.message : "unknown error"}`,
|
|
314
|
-
hints: ["Run 'prisma-next contract emit' to generate a valid contract"]
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
let aggregate;
|
|
318
|
-
try {
|
|
319
|
-
aggregate = await loadContractSpaceAggregate({
|
|
320
|
-
migrationsDir,
|
|
321
|
-
deserializeContract,
|
|
322
|
-
appContract: appContractForLoad
|
|
323
|
-
});
|
|
324
|
-
} catch (error) {
|
|
325
|
-
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
326
|
-
return notOk(errorUnexpected(error instanceof Error ? error.message : String(error), { why: `Failed to read migrations directory: ${error instanceof Error ? error.message : String(error)}` }));
|
|
327
|
-
}
|
|
328
|
-
if (contractRawForAggregate !== null) {
|
|
329
|
-
const corruptionFailure = refusePackageCorruptionOnAggregate(aggregate);
|
|
330
|
-
if (corruptionFailure) return notOk(corruptionFailure);
|
|
331
|
-
}
|
|
332
|
-
const appGraph = aggregate.app.graph();
|
|
333
|
-
let fromOverrideHash;
|
|
334
|
-
if (options.to || options.from) {
|
|
335
|
-
if (options.to) {
|
|
336
|
-
const refResult = parseContractRef(options.to, {
|
|
337
|
-
graph: appGraph,
|
|
338
|
-
refs: allRefs
|
|
339
|
-
});
|
|
340
|
-
if (!refResult.ok) return notOk(mapRefResolutionError(refResult.failure));
|
|
341
|
-
activeRefHash = refResult.value.hash;
|
|
342
|
-
if (refResult.value.provenance.kind === "ref") {
|
|
343
|
-
const resolvedRefName = refResult.value.provenance.refName;
|
|
344
|
-
activeRefName = resolvedRefName;
|
|
345
|
-
activeRefEntry = allRefs[resolvedRefName];
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
if (options.from) {
|
|
349
|
-
const fromResult = parseContractRef(options.from, {
|
|
350
|
-
graph: appGraph,
|
|
351
|
-
refs: allRefs
|
|
352
|
-
});
|
|
353
|
-
if (!fromResult.ok) return notOk(mapRefResolutionError(fromResult.failure));
|
|
354
|
-
fromOverrideHash = fromResult.value.hash;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
const requiredInvariants = [...activeRefEntry?.invariants ?? []].sort();
|
|
358
|
-
const statusRefs = Object.entries(allRefs).map(([name, entry]) => ({
|
|
359
|
-
name,
|
|
360
|
-
hash: entry.hash,
|
|
361
|
-
active: name === activeRefName
|
|
362
|
-
}));
|
|
363
|
-
if (!flags.json && !flags.quiet) {
|
|
364
|
-
const details = [{
|
|
365
|
-
label: "config",
|
|
366
|
-
value: configPath
|
|
367
|
-
}, {
|
|
368
|
-
label: "migrations",
|
|
369
|
-
value: appMigrationsRelative
|
|
370
|
-
}];
|
|
371
|
-
if (dbConnection && hasDriver) details.push({
|
|
372
|
-
label: "database",
|
|
373
|
-
value: maskConnectionUrl(String(dbConnection))
|
|
374
|
-
});
|
|
375
|
-
if (activeRefName) details.push({
|
|
376
|
-
label: "ref",
|
|
377
|
-
value: activeRefName
|
|
378
|
-
});
|
|
379
|
-
if (options.from) details.push({
|
|
380
|
-
label: "from",
|
|
381
|
-
value: options.from
|
|
382
|
-
});
|
|
383
|
-
if (activeRefEntry && activeRefEntry.invariants.length > 0) details.push({
|
|
384
|
-
label: "required",
|
|
385
|
-
value: formatInvariantList(activeRefEntry.invariants)
|
|
386
|
-
});
|
|
387
|
-
const header = formatStyledHeader({
|
|
388
|
-
command: "migration status",
|
|
389
|
-
description: "Show migration history and applied status",
|
|
390
|
-
details,
|
|
391
|
-
flags
|
|
392
|
-
});
|
|
393
|
-
ui.stderr(header);
|
|
394
|
-
}
|
|
395
|
-
const bundles = aggregate.app.packages;
|
|
396
|
-
const graph = appGraph;
|
|
397
|
-
if (bundles.length === 0) {
|
|
398
|
-
if (dbConnection && hasDriver) {
|
|
399
|
-
const markerProbe = await validateOnlineMarkerRead(config, dbConnection);
|
|
400
|
-
if (!markerProbe.ok) return markerProbe;
|
|
401
|
-
}
|
|
402
|
-
if (contractHash !== EMPTY_CONTRACT_HASH) diagnostics.push({
|
|
403
|
-
code: "CONTRACT.AHEAD",
|
|
404
|
-
severity: "warn",
|
|
405
|
-
message: "No migration exists for the current contract",
|
|
406
|
-
hints: ["Run 'prisma-next migration plan' to generate a migration for the current contract"]
|
|
407
|
-
});
|
|
408
|
-
return ok({
|
|
409
|
-
ok: true,
|
|
410
|
-
mode: dbConnection && hasDriver ? "online" : "offline",
|
|
411
|
-
migrations: [],
|
|
412
|
-
targetHash: EMPTY_CONTRACT_HASH,
|
|
413
|
-
contractHash,
|
|
414
|
-
summary: "No migrations found",
|
|
415
|
-
diagnostics,
|
|
416
|
-
requiredInvariants
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
let targetHash;
|
|
420
|
-
if (activeRefHash) targetHash = activeRefHash;
|
|
421
|
-
else if (graph.nodes.has(contractHash)) targetHash = contractHash;
|
|
422
|
-
else {
|
|
423
|
-
const leaves = findReachableLeaves(graph, EMPTY_CONTRACT_HASH);
|
|
424
|
-
if (leaves.length === 1) targetHash = leaves[0];
|
|
425
|
-
else diagnostics.push({
|
|
426
|
-
code: "MIGRATION.DIVERGED",
|
|
427
|
-
severity: "warn",
|
|
428
|
-
message: "There are multiple valid migration paths — you must select a target",
|
|
429
|
-
hints: ["Use '--to <contract>' to select a target", "Or 'prisma-next ref set <name> <hash>' to create one"]
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
let markerHash;
|
|
433
|
-
let markerInvariants = [];
|
|
434
|
-
let mode = "offline";
|
|
435
|
-
let allMarkers = null;
|
|
436
|
-
if (dbConnection && hasDriver) {
|
|
437
|
-
const client = createControlClient({
|
|
438
|
-
family: config.family,
|
|
439
|
-
target: config.target,
|
|
440
|
-
adapter: config.adapter,
|
|
441
|
-
driver: config.driver,
|
|
442
|
-
extensionPacks: config.extensionPacks ?? []
|
|
443
|
-
});
|
|
444
|
-
try {
|
|
445
|
-
await client.connect(dbConnection);
|
|
446
|
-
const marker = await client.readMarker();
|
|
447
|
-
markerHash = marker?.storageHash;
|
|
448
|
-
markerInvariants = marker?.invariants ?? [];
|
|
449
|
-
mode = "online";
|
|
450
|
-
if (typeof client.readAllMarkers === "function") allMarkers = await client.readAllMarkers();
|
|
451
|
-
else allMarkers = null;
|
|
452
|
-
} catch (error) {
|
|
453
|
-
if (CliStructuredError.is(error)) return notOk(error);
|
|
454
|
-
if (!flags.json && !flags.quiet) ui.warn("Could not connect to database — showing offline status");
|
|
455
|
-
} finally {
|
|
456
|
-
await client.close();
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
if (fromOverrideHash !== void 0) {
|
|
460
|
-
markerHash = fromOverrideHash;
|
|
461
|
-
mode = "offline";
|
|
462
|
-
allMarkers = null;
|
|
463
|
-
}
|
|
464
|
-
let aggregateSpaces = [];
|
|
465
|
-
if (contractRawForAggregate !== null) try {
|
|
466
|
-
aggregateSpaces = await loadAggregateStatusSpaces({
|
|
467
|
-
aggregate,
|
|
468
|
-
extensionPacks: config.extensionPacks ?? [],
|
|
469
|
-
markersBySpace: allMarkers
|
|
470
|
-
});
|
|
471
|
-
} catch {
|
|
472
|
-
aggregateSpaces = [];
|
|
473
|
-
}
|
|
474
|
-
const totalPendingAcrossSpaces = computeTotalPendingAcrossSpaces(aggregateSpaces);
|
|
475
|
-
if (activeRefEntry && activeRefEntry.invariants.length > 0) {
|
|
476
|
-
const declared = collectDeclaredInvariants(graph);
|
|
477
|
-
const known = new Set(declared);
|
|
478
|
-
if (mode === "online") for (const id of markerInvariants) known.add(id);
|
|
479
|
-
const unknown = activeRefEntry.invariants.filter((id) => !known.has(id));
|
|
480
|
-
if (unknown.length > 0) return notOk(mapMigrationToolsError(errorUnknownInvariant({
|
|
481
|
-
...ifDefined("refName", activeRefName),
|
|
482
|
-
unknown,
|
|
483
|
-
declared: [...declared].sort()
|
|
484
|
-
})));
|
|
485
|
-
}
|
|
486
|
-
if (mode === "online" && markerHash !== void 0 && !graph.nodes.has(markerHash) && markerHash !== contractHash) {
|
|
487
|
-
const hints = [];
|
|
488
|
-
if (graph.nodes.has(contractHash)) hints.push("Run 'prisma-next db sign' to overwrite the marker if the database already matches the contract", "Run 'prisma-next db update' to push the current contract to the database", "Run 'prisma-next contract infer' to make your contract match the database", "Run 'prisma-next db verify' to inspect the database state");
|
|
489
|
-
else hints.push("Run 'prisma-next db update' to push the current contract to the database", "Run 'prisma-next contract infer' to make your contract match the database", "Run 'prisma-next db verify' to inspect the database state");
|
|
490
|
-
diagnostics.push({
|
|
491
|
-
code: "MIGRATION.MARKER_NOT_IN_HISTORY",
|
|
492
|
-
severity: "warn",
|
|
493
|
-
message: "Database was updated outside the migration system (marker does not match any migration)",
|
|
494
|
-
hints
|
|
495
|
-
});
|
|
496
|
-
return ok({
|
|
497
|
-
ok: true,
|
|
498
|
-
mode,
|
|
499
|
-
migrations: [],
|
|
500
|
-
targetHash: EMPTY_CONTRACT_HASH,
|
|
501
|
-
contractHash,
|
|
502
|
-
summary: `${bundles.length} migration(s) on disk`,
|
|
503
|
-
diagnostics,
|
|
504
|
-
markerHash,
|
|
505
|
-
requiredInvariants,
|
|
506
|
-
...statusRefs.length > 0 ? { refs: statusRefs } : {}
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
if (mode === "online" && markerHash === void 0) diagnostics.push({
|
|
510
|
-
code: "MIGRATION.NO_MARKER",
|
|
511
|
-
severity: "warn",
|
|
512
|
-
message: "Database has not been initialized — no migration marker found",
|
|
513
|
-
hints: ["Run 'prisma-next migrate' to apply pending migrations"]
|
|
514
|
-
});
|
|
515
|
-
if (targetHash && contractHash !== EMPTY_CONTRACT_HASH && !graph.nodes.has(contractHash) && markerHash !== contractHash) diagnostics.push({
|
|
516
|
-
code: "CONTRACT.AHEAD",
|
|
517
|
-
severity: "warn",
|
|
518
|
-
message: "Contract has changed since the last migration was planned",
|
|
519
|
-
hints: ["Run 'prisma-next migration plan' to generate a migration for the current contract"]
|
|
520
|
-
});
|
|
521
|
-
if (!targetHash) return ok({
|
|
522
|
-
ok: true,
|
|
523
|
-
mode,
|
|
524
|
-
migrations: [],
|
|
525
|
-
targetHash: EMPTY_CONTRACT_HASH,
|
|
526
|
-
contractHash,
|
|
527
|
-
summary: `${bundles.length} migration(s) on disk`,
|
|
528
|
-
diagnostics,
|
|
529
|
-
...ifDefined("markerHash", markerHash),
|
|
530
|
-
requiredInvariants,
|
|
531
|
-
...statusRefs.length > 0 ? { refs: statusRefs } : {},
|
|
532
|
-
graph,
|
|
533
|
-
bundles,
|
|
534
|
-
diverged: true
|
|
535
|
-
});
|
|
536
|
-
const chain = resolveDisplayChain(graph, targetHash, markerHash);
|
|
537
|
-
if (!chain) return notOk(errorRuntime("Cannot reconstruct migration history", {
|
|
538
|
-
why: `No path from ${EMPTY_CONTRACT_HASH} to target ${targetHash}`,
|
|
539
|
-
fix: "The migration history may have gaps. Check the migrations directory for missing or corrupted packages."
|
|
540
|
-
}));
|
|
541
|
-
const edgeStatuses = deriveEdgeStatuses(graph, targetHash, contractHash, markerHash, mode);
|
|
542
|
-
const entries = buildMigrationEntries(chain, bundles, mode, markerHash, edgeStatuses);
|
|
543
|
-
const pendingCount = edgeStatuses.filter((e) => e.status === "pending").length;
|
|
544
|
-
const appliedCount = edgeStatuses.filter((e) => e.status === "applied").length;
|
|
545
|
-
let appliedInvariants;
|
|
546
|
-
let missingInvariants;
|
|
547
|
-
let effectiveRequired = /* @__PURE__ */ new Set();
|
|
548
|
-
if (mode === "online") {
|
|
549
|
-
const markerSet = new Set(markerInvariants);
|
|
550
|
-
effectiveRequired = new Set(requiredInvariants.filter((id) => !markerSet.has(id)));
|
|
551
|
-
appliedInvariants = requiredInvariants.filter((id) => markerSet.has(id));
|
|
552
|
-
missingInvariants = [...effectiveRequired].sort();
|
|
553
|
-
}
|
|
554
|
-
const hasInvariantWork = effectiveRequired.size > 0;
|
|
555
|
-
const missingList = [...effectiveRequired].sort().join(", ");
|
|
556
|
-
let summary;
|
|
557
|
-
if (mode === "online") if (markerHash !== void 0 && !graph.nodes.has(markerHash) && markerHash === contractHash) summary = `${bundles.length} migration(s) on disk`;
|
|
558
|
-
else if (activeRefHash && activeRefName && markerHash !== void 0) {
|
|
559
|
-
const distance = summarizeRefDistance(graph, markerHash, activeRefHash, activeRefName);
|
|
560
|
-
summary = hasInvariantWork ? `${distance} — missing invariant(s): ${missingList}` : distance;
|
|
561
|
-
} else if (pendingCount === 0 && !hasInvariantWork) summary = `Database is up to date (${appliedCount} migration${appliedCount !== 1 ? "s" : ""} applied)`;
|
|
562
|
-
else if (pendingCount === 0 && hasInvariantWork) summary = `Missing invariant(s): ${missingList} — run 'prisma-next migrate --to ${activeRefName ?? "<ref>"}' to apply`;
|
|
563
|
-
else if (markerHash === void 0) summary = `${pendingCount} pending migration(s) — database has no marker`;
|
|
564
|
-
else summary = `${pendingCount} pending migration(s) — run 'prisma-next migrate' to apply`;
|
|
565
|
-
else summary = `${entries.length} migration(s) on disk`;
|
|
566
|
-
let pathDecision;
|
|
567
|
-
let routingUnreachable = false;
|
|
568
|
-
if (mode === "online") {
|
|
569
|
-
const outcome = findPathWithDecision(graph, markerHash ?? EMPTY_CONTRACT_HASH, targetHash, {
|
|
570
|
-
...ifDefined("refName", activeRefName),
|
|
571
|
-
required: effectiveRequired
|
|
572
|
-
});
|
|
573
|
-
if (outcome.kind === "ok") pathDecision = toPathDecisionResult(outcome.decision);
|
|
574
|
-
else if (outcome.kind === "unsatisfiable") return notOk(mapMigrationToolsError(errorNoInvariantPath({
|
|
575
|
-
...ifDefined("refName", activeRefName),
|
|
576
|
-
required: [...effectiveRequired].sort(),
|
|
577
|
-
missing: outcome.missing,
|
|
578
|
-
structuralPath: outcome.structuralPath.map(toStructuralEdge)
|
|
579
|
-
})));
|
|
580
|
-
else routingUnreachable = true;
|
|
581
|
-
}
|
|
582
|
-
if (mode === "online") {
|
|
583
|
-
if (markerHash !== void 0 && !graph.nodes.has(markerHash) && markerHash === contractHash) diagnostics.push({
|
|
584
|
-
code: "MIGRATION.MARKER_NOT_IN_HISTORY",
|
|
585
|
-
severity: "warn",
|
|
586
|
-
message: "Database matches the current contract but was updated directly (not via migrate)",
|
|
587
|
-
hints: ["Run 'prisma-next migration plan' to plan a migration to your current contract"]
|
|
588
|
-
});
|
|
589
|
-
else if (pendingCount > 0) diagnostics.push({
|
|
590
|
-
code: "MIGRATION.DATABASE_BEHIND",
|
|
591
|
-
severity: "info",
|
|
592
|
-
message: `${pendingCount} migration(s) pending`,
|
|
593
|
-
hints: ["Run 'prisma-next migrate' to apply pending migrations"]
|
|
594
|
-
});
|
|
595
|
-
else if (hasInvariantWork) diagnostics.push({
|
|
596
|
-
code: "MIGRATION.INVARIANTS_PENDING",
|
|
597
|
-
severity: "info",
|
|
598
|
-
message: `Missing required invariant(s): ${missingList}`,
|
|
599
|
-
hints: [`Run 'prisma-next migrate --to ${activeRefName ?? "<ref>"}' to apply a path that covers the required invariants`]
|
|
600
|
-
});
|
|
601
|
-
else if (!routingUnreachable) diagnostics.push({
|
|
602
|
-
code: "MIGRATION.UP_TO_DATE",
|
|
603
|
-
severity: "info",
|
|
604
|
-
message: "Database is up to date",
|
|
605
|
-
hints: []
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
return ok({
|
|
609
|
-
ok: true,
|
|
610
|
-
mode,
|
|
611
|
-
migrations: entries,
|
|
612
|
-
targetHash,
|
|
613
|
-
contractHash,
|
|
614
|
-
summary,
|
|
615
|
-
diagnostics,
|
|
616
|
-
...ifDefined("markerHash", markerHash),
|
|
617
|
-
requiredInvariants,
|
|
618
|
-
...ifDefined("appliedInvariants", appliedInvariants),
|
|
619
|
-
...ifDefined("missingInvariants", missingInvariants),
|
|
620
|
-
...statusRefs.length > 0 ? { refs: statusRefs } : {},
|
|
621
|
-
...ifDefined("pathDecision", pathDecision),
|
|
622
|
-
graph,
|
|
623
|
-
bundles,
|
|
624
|
-
edgeStatuses,
|
|
625
|
-
...ifDefined("activeRefHash", activeRefHash),
|
|
626
|
-
...ifDefined("activeRefName", activeRefName),
|
|
627
|
-
spaces: aggregateSpaces,
|
|
628
|
-
...ifDefined("totalPendingAcrossSpaces", totalPendingAcrossSpaces)
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
function createMigrationStatusCommand() {
|
|
632
|
-
const command = new Command("status");
|
|
633
|
-
setCommandDescriptions(command, "Show migration path and pending status", "Shows which migrations are pending between the database marker and\nthe target contract. Requires a database connection for live status.\nUse `migration graph` for topology, `migration log` for history,\nand `migration list` for on-disk enumeration.");
|
|
634
|
-
setCommandExamples(command, ["prisma-next migration status --db $DATABASE_URL", "prisma-next migration status --to production --db $DATABASE_URL"]);
|
|
635
|
-
setCommandSeeAlso(command, [
|
|
636
|
-
{
|
|
637
|
-
verb: "migration log",
|
|
638
|
-
oneLiner: "Show executed migration history"
|
|
639
|
-
},
|
|
640
|
-
{
|
|
641
|
-
verb: "migration list",
|
|
642
|
-
oneLiner: "List on-disk migrations"
|
|
643
|
-
},
|
|
644
|
-
{
|
|
645
|
-
verb: "migration graph",
|
|
646
|
-
oneLiner: "Show the migration graph topology"
|
|
647
|
-
},
|
|
648
|
-
{
|
|
649
|
-
verb: "migration show",
|
|
650
|
-
oneLiner: "Display migration package contents"
|
|
651
|
-
}
|
|
652
|
-
]);
|
|
653
|
-
addGlobalOptions(command).option("--db <url>", "Database connection string").option("--config <path>", "Path to prisma-next.config.ts").option("--to <contract>", "Target contract reference (hash, prefix, ref name, migration dir name, <dir>^, or ./path)").option("--from <contract>", "Origin contract reference; same grammar as --to. Supplying --from switches to offline path computation.").action(async (options) => {
|
|
654
|
-
const flags = parseGlobalFlagsOrExit(options);
|
|
655
|
-
const ui = createTerminalUI(flags);
|
|
656
|
-
const exitCode = handleResult(await executeMigrationStatusCommand(options, flags, ui), flags, ui, (statusResult) => {
|
|
657
|
-
if (flags.json) {
|
|
658
|
-
const { graph: _graph, bundles: _bundles, edgeStatuses: _edgeStatuses, activeRefHash: _activeRefHash, activeRefName: _activeRefName, diverged: _diverged, ...jsonResult } = statusResult;
|
|
659
|
-
ui.output(JSON.stringify(jsonResult, null, 2));
|
|
660
|
-
} else if (!flags.quiet) {
|
|
661
|
-
const colorize = flags.color !== false;
|
|
662
|
-
if (statusResult.graph) {
|
|
663
|
-
const renderInput = migrationGraphToRenderInput({
|
|
664
|
-
graph: statusResult.graph,
|
|
665
|
-
mode: statusResult.mode,
|
|
666
|
-
markerHash: statusResult.markerHash,
|
|
667
|
-
contractHash: statusResult.contractHash,
|
|
668
|
-
refs: statusResult.refs,
|
|
669
|
-
activeRefHash: statusResult.activeRefHash,
|
|
670
|
-
activeRefName: statusResult.activeRefName,
|
|
671
|
-
edgeStatuses: statusResult.edgeStatuses
|
|
672
|
-
});
|
|
673
|
-
const graphToRender = statusResult.diverged ? renderInput.graph : extractRelevantSubgraph(renderInput.graph, renderInput.relevantPaths);
|
|
674
|
-
const dagreOptions = isLinearGraph(graphToRender) ? { ranksep: 1 } : void 0;
|
|
675
|
-
const renderOptions = {
|
|
676
|
-
...renderInput.options,
|
|
677
|
-
colorize,
|
|
678
|
-
...ifDefined("dagreOptions", dagreOptions)
|
|
679
|
-
};
|
|
680
|
-
const graphOutput = graphRenderer.render(graphToRender, renderOptions);
|
|
681
|
-
ui.log(graphOutput);
|
|
682
|
-
if (statusResult.mode === "online") ui.log(formatLegend(colorize));
|
|
683
|
-
}
|
|
684
|
-
ui.log("");
|
|
685
|
-
ui.log(formatStatusSummary(statusResult, colorize));
|
|
686
|
-
}
|
|
687
|
-
});
|
|
688
|
-
process.exit(exitCode);
|
|
689
|
-
});
|
|
690
|
-
return command;
|
|
691
|
-
}
|
|
692
|
-
function formatLegend(colorize) {
|
|
693
|
-
const c = (fn, s) => colorize ? fn(s) : s;
|
|
694
|
-
return c(dim, [
|
|
695
|
-
`${c(cyan, "✓")} applied`,
|
|
696
|
-
`${c(yellow, "⧗")} pending`,
|
|
697
|
-
`${c(magenta, "✗")} unreachable`
|
|
698
|
-
].join(" "));
|
|
699
|
-
}
|
|
700
|
-
function formatStatusSummary(result, colorize) {
|
|
701
|
-
const c = (fn, s) => colorize ? fn(s) : s;
|
|
702
|
-
const lines = [];
|
|
703
|
-
const hasUnknown = result.migrations.some((e) => e.status === "unknown");
|
|
704
|
-
const pendingCount = result.migrations.filter((e) => e.status === "pending").length;
|
|
705
|
-
const hasWarnings = result.diagnostics?.some((d) => d.severity === "warn") ?? false;
|
|
706
|
-
const hasInvariantPending = result.diagnostics?.some((d) => d.code === "MIGRATION.INVARIANTS_PENDING") ?? false;
|
|
707
|
-
if (result.mode === "online") if (hasUnknown || hasWarnings) lines.push(`${c(yellow, "⚠")} ${result.summary}`);
|
|
708
|
-
else if (pendingCount === 0 && !hasInvariantPending) lines.push(`${c(cyan, "✔")} ${result.summary}`);
|
|
709
|
-
else lines.push(`${c(yellow, "⧗")} ${result.summary}`);
|
|
710
|
-
else lines.push(result.summary);
|
|
711
|
-
if (result.requiredInvariants.length > 0) if (result.appliedInvariants !== void 0 && result.missingInvariants !== void 0) {
|
|
712
|
-
lines.push(`${c(dim, "applied ")}${formatInvariantList(result.appliedInvariants)}`);
|
|
713
|
-
lines.push(`${c(dim, "missing ")}${formatInvariantList(result.missingInvariants)}`);
|
|
714
|
-
} else lines.push(`${c(dim, "applied ")}(unknown — connect a database to evaluate)`);
|
|
715
|
-
const warnings = result.diagnostics?.filter((d) => d.severity === "warn") ?? [];
|
|
716
|
-
for (const diag of warnings) {
|
|
717
|
-
lines.push(`${c(yellow, "⚠")} ${diag.message}`);
|
|
718
|
-
for (const hint of diag.hints) lines.push(` ${c(dim, hint)}`);
|
|
719
|
-
}
|
|
720
|
-
if (result.spaces?.some((s) => s.kind === "extension")) {
|
|
721
|
-
const total = result.totalPendingAcrossSpaces ?? 0;
|
|
722
|
-
lines.push("");
|
|
723
|
-
lines.push(c(dim, "spaces"));
|
|
724
|
-
for (const space of result.spaces) lines.push(formatSpaceLine(space, c));
|
|
725
|
-
if (total > 0) {
|
|
726
|
-
lines.push("");
|
|
727
|
-
lines.push(`${c(yellow, "⧗")} ${total} pending migration(s) across ${result.spaces.length} space(s) — run 'prisma-next migrate' to apply`);
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
return lines.join("\n");
|
|
731
|
-
}
|
|
732
|
-
function formatSpaceLine(space, c) {
|
|
733
|
-
const glyph = (() => {
|
|
734
|
-
if (space.status === "up-to-date" || space.status === "no-marker") return c(cyan, "✓");
|
|
735
|
-
if (space.status === "pending") return c(yellow, "⧗");
|
|
736
|
-
if (space.status === "unreachable" || space.status === "never-planned") return c(magenta, "✗");
|
|
737
|
-
return " ";
|
|
738
|
-
})();
|
|
739
|
-
const tag = space.kind === "app" ? "[app]" : "[ext]";
|
|
740
|
-
const head = space.headHash.slice(0, 8);
|
|
741
|
-
const marker = space.markerHash === void 0 ? "(unknown)" : space.markerHash === null ? "(no marker)" : space.markerHash.slice(0, 8);
|
|
742
|
-
const pending = space.pendingCount === void 0 ? "" : space.pendingCount === 0 ? c(dim, " (up to date)") : c(yellow, ` (${space.pendingCount} pending)`);
|
|
743
|
-
return ` ${glyph} ${c(dim, tag)} ${space.spaceId} → head ${c(dim, head)}, marker ${c(dim, marker)}${pending}`;
|
|
744
|
-
}
|
|
745
|
-
function formatInvariantList(ids) {
|
|
746
|
-
return ids.length === 0 ? "(none)" : ids.join(", ");
|
|
747
|
-
}
|
|
748
|
-
function summarizeRefDistance(graph, markerHash, refHash, refName) {
|
|
749
|
-
if (markerHash === refHash) return `At ref "${refName}" target`;
|
|
750
|
-
const pathToRef = findPath(graph, markerHash, refHash);
|
|
751
|
-
if (pathToRef) return `${pathToRef.length} migration(s) behind ref "${refName}"`;
|
|
752
|
-
const pathFromRef = findPath(graph, refHash, markerHash);
|
|
753
|
-
if (pathFromRef) return `${pathFromRef.length} migration(s) ahead of ref "${refName}"`;
|
|
754
|
-
return `No path between database marker and ref "${refName}" target`;
|
|
755
|
-
}
|
|
756
|
-
//#endregion
|
|
757
|
-
export { computeTotalPendingAcrossSpaces, createMigrationStatusCommand, deriveEdgeStatuses, formatStatusSummary, loadAggregateStatusSpaces };
|
|
758
|
-
|
|
759
|
-
//# sourceMappingURL=migration-status.mjs.map
|
|
1
|
+
import { a as formatStatusSummary, i as formatStatusHumanOutput, n as buildStatusHeadline, r as createMigrationStatusCommand, t as buildNoPathSummary } from "../migration-status-GZ6XfbWs.mjs";
|
|
2
|
+
export { buildNoPathSummary, buildStatusHeadline, createMigrationStatusCommand, formatStatusHumanOutput, formatStatusSummary };
|