@prisma-next/cli 0.14.0-dev.49 → 0.14.0-dev.50
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 +13 -13
- package/dist/{client-CWTNqJdj.mjs → client-DL6KE_c0.mjs} +22 -37
- package/dist/client-DL6KE_c0.mjs.map +1 -0
- package/dist/{command-helpers-6cNJZ863.mjs → command-helpers-DMG0RsR1.mjs} +3 -3
- package/dist/command-helpers-DMG0RsR1.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 +3 -3
- package/dist/commands/db-schema.mjs +3 -3
- package/dist/commands/db-sign.mjs +4 -4
- package/dist/commands/db-update.mjs +4 -4
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +1 -1
- package/dist/commands/migrate.mjs +6 -6
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-check.d.mts +1 -1
- package/dist/commands/migration-check.mjs +1 -1
- package/dist/commands/migration-graph.d.mts +1 -1
- package/dist/commands/migration-graph.mjs +3 -3
- package/dist/commands/migration-list.d.mts +1 -1
- package/dist/commands/migration-list.mjs +1 -1
- package/dist/commands/migration-log.d.mts +1 -1
- package/dist/commands/migration-log.mjs +1 -1
- package/dist/commands/migration-new.mjs +3 -3
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-show.mjs +3 -3
- package/dist/commands/migration-status.d.mts +1 -1
- package/dist/commands/migration-status.mjs +1 -1
- package/dist/commands/ref.d.mts +1 -1
- package/dist/commands/ref.mjs +2 -2
- package/dist/commands/telemetry/index.mjs +1 -1
- package/dist/{contract-at-errors-DdxWRjMc.mjs → contract-at-errors-bF4CGDl0.mjs} +2 -2
- package/dist/{contract-at-errors-DdxWRjMc.mjs.map → contract-at-errors-bF4CGDl0.mjs.map} +1 -1
- package/dist/{contract-emit-CMwCj7_m.mjs → contract-emit-BM7oxYIG.mjs} +3 -3
- package/dist/{contract-emit-CMwCj7_m.mjs.map → contract-emit-BM7oxYIG.mjs.map} +1 -1
- package/dist/{contract-emit-klH7zzaV.mjs → contract-emit-DnCG2JaQ.mjs} +3 -3
- package/dist/{contract-emit-klH7zzaV.mjs.map → contract-emit-DnCG2JaQ.mjs.map} +1 -1
- package/dist/{contract-infer-B__56PGb.mjs → contract-infer-Dq9XNQCx.mjs} +3 -3
- package/dist/{contract-infer-B__56PGb.mjs.map → contract-infer-Dq9XNQCx.mjs.map} +1 -1
- package/dist/{contract-space-aggregate-loader-Bu2fjSWV.mjs → contract-space-aggregate-loader-DAa8u6bi.mjs} +2 -2
- package/dist/{contract-space-aggregate-loader-Bu2fjSWV.mjs.map → contract-space-aggregate-loader-DAa8u6bi.mjs.map} +1 -1
- package/dist/{db-verify-DlqHvkkA.mjs → db-verify-BpfJvC8O.mjs} +14 -38
- package/dist/db-verify-BpfJvC8O.mjs.map +1 -0
- package/dist/exports/control-api.d.mts +1 -1
- package/dist/exports/control-api.mjs +2 -2
- package/dist/exports/index.mjs +2 -2
- package/dist/{format-C3XdaDuA.mjs → format-DrUSkdqR.mjs} +2 -2
- package/dist/{format-C3XdaDuA.mjs.map → format-DrUSkdqR.mjs.map} +1 -1
- package/dist/{framework-components-5RljWT7Q.mjs → framework-components-BdmVlRSS.mjs} +2 -2
- package/dist/{framework-components-5RljWT7Q.mjs.map → framework-components-BdmVlRSS.mjs.map} +1 -1
- package/dist/{init-BF3s9NcB.mjs → init-C0YyVoGj.mjs} +3 -3
- package/dist/{init-BF3s9NcB.mjs.map → init-C0YyVoGj.mjs.map} +1 -1
- package/dist/{inspect-live-schema-Dd1Ft0dw.mjs → inspect-live-schema-Bs_0M6z8.mjs} +3 -3
- package/dist/{inspect-live-schema-Dd1Ft0dw.mjs.map → inspect-live-schema-Bs_0M6z8.mjs.map} +1 -1
- package/dist/{migration-check-C9F4RopI.mjs → migration-check-DYxrNfK-.mjs} +4 -5
- package/dist/{migration-check-C9F4RopI.mjs.map → migration-check-DYxrNfK-.mjs.map} +1 -1
- package/dist/{migration-command-scaffold-DW2BpjZW.mjs → migration-command-scaffold-DB5DQLvF.mjs} +3 -3
- package/dist/{migration-command-scaffold-DW2BpjZW.mjs.map → migration-command-scaffold-DB5DQLvF.mjs.map} +1 -1
- package/dist/{migration-list-BOzQzJK4.mjs → migration-list-BX5w9HAn.mjs} +3 -3
- package/dist/{migration-list-BOzQzJK4.mjs.map → migration-list-BX5w9HAn.mjs.map} +1 -1
- package/dist/{migration-log-q50rqMiU.mjs → migration-log-CB1KzVOG.mjs} +3 -3
- package/dist/{migration-log-q50rqMiU.mjs.map → migration-log-CB1KzVOG.mjs.map} +1 -1
- package/dist/{migration-path-target-zK6RG6JY.mjs → migration-path-target-CEFRK2Q7.mjs} +2 -2
- package/dist/{migration-path-target-zK6RG6JY.mjs.map → migration-path-target-CEFRK2Q7.mjs.map} +1 -1
- package/dist/{migration-plan-BwJLPkwV.mjs → migration-plan-DMKl_HnV.mjs} +11 -10
- package/dist/migration-plan-DMKl_HnV.mjs.map +1 -0
- package/dist/{migration-status-RrGUf39y.mjs → migration-status-BeNAmEju.mjs} +5 -6
- package/dist/{migration-status-RrGUf39y.mjs.map → migration-status-BeNAmEju.mjs.map} +1 -1
- package/dist/{telemetry-BckOSUWG.mjs → telemetry-_19qtfXp.mjs} +2 -2
- package/dist/{telemetry-BckOSUWG.mjs.map → telemetry-_19qtfXp.mjs.map} +1 -1
- package/dist/{types-CKgJmH02.d.mts → types-DFoqNIcQ.d.mts} +2 -2
- package/dist/{types-CKgJmH02.d.mts.map → types-DFoqNIcQ.d.mts.map} +1 -1
- package/dist/{verify-DC14_nPu.mjs → verify-Djo-Jep_.mjs} +36 -161
- package/dist/verify-Djo-Jep_.mjs.map +1 -0
- package/package.json +21 -21
- package/src/commands/db-verify.ts +1 -1
- package/src/commands/migrate.ts +4 -4
- package/src/commands/migration-plan.ts +11 -0
- package/src/control-api/operations/db-run.ts +13 -12
- package/src/control-api/operations/db-verify.ts +16 -13
- package/src/control-api/operations/migrate.ts +8 -8
- package/src/control-api/operations/run-migration.ts +5 -4
- package/src/control-api/types.ts +1 -1
- package/src/utils/combine-verify-results.ts +8 -27
- package/src/utils/formatters/errors.ts +12 -4
- package/src/utils/formatters/verify.ts +50 -230
- package/dist/client-CWTNqJdj.mjs.map +0 -1
- package/dist/command-helpers-6cNJZ863.mjs.map +0 -1
- package/dist/db-verify-DlqHvkkA.mjs.map +0 -1
- package/dist/migration-plan-BwJLPkwV.mjs.map +0 -1
- package/dist/verify-DC14_nPu.mjs.map +0 -1
|
@@ -1,7 +1,22 @@
|
|
|
1
|
-
import { M as createColorFormatter, N as formatDim, P as isVerbose } from "./command-helpers-
|
|
1
|
+
import { M as createColorFormatter, N as formatDim, P as isVerbose } from "./command-helpers-DMG0RsR1.mjs";
|
|
2
2
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
3
3
|
import { bold, cyan, dim, green, magenta, red, yellow } from "colorette";
|
|
4
4
|
//#region src/utils/formatters/verify.ts
|
|
5
|
+
/** Human-readable label for each failure reason, prefixed onto an issue's message for display. */
|
|
6
|
+
const REASON_LABEL = {
|
|
7
|
+
"not-found": "missing",
|
|
8
|
+
"not-expected": "extra",
|
|
9
|
+
"not-equal": "mismatch"
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* The issue's display text: its own message (the differ's `path`), prefixed
|
|
13
|
+
* with a human label for why it's flagged. Turning `reason` into a label is
|
|
14
|
+
* this formatter's job, not the differ's — the differ's issue is data
|
|
15
|
+
* (`path` + `reason` + nodes), not prose.
|
|
16
|
+
*/
|
|
17
|
+
function formatIssueMessage(issue) {
|
|
18
|
+
return `${REASON_LABEL[issue.reason]}: ${issue.message}`;
|
|
19
|
+
}
|
|
5
20
|
/**
|
|
6
21
|
* Formats human-readable output for database verify.
|
|
7
22
|
*/
|
|
@@ -17,7 +32,11 @@ function formatVerifyOutput(result, flags) {
|
|
|
17
32
|
lines.push(`${formatDimText(` verification: ${verificationMode}`)}`);
|
|
18
33
|
lines.push(`${formatDimText(` storageHash: ${result.contract.storageHash}`)}`);
|
|
19
34
|
if (result.contract.profileHash) lines.push(`${formatDimText(` profileHash: ${result.contract.profileHash}`)}`);
|
|
20
|
-
if (result.
|
|
35
|
+
if (result.schema?.warnings && result.schema.warnings.length > 0) {
|
|
36
|
+
lines.push("");
|
|
37
|
+
lines.push(formatYellow("Schema warnings:"));
|
|
38
|
+
for (const message of result.schema.warnings) lines.push(` ${formatYellow("⚠")} ${message}`);
|
|
39
|
+
}
|
|
21
40
|
if (result.unclaimed && result.unclaimed.length > 0) {
|
|
22
41
|
lines.push("");
|
|
23
42
|
lines.push(formatYellow("Unclaimed elements (declared by no contract):"));
|
|
@@ -62,7 +81,7 @@ function formatIntrospectJson(result) {
|
|
|
62
81
|
}
|
|
63
82
|
/**
|
|
64
83
|
* Renders a schema tree structure from CoreSchemaView.
|
|
65
|
-
*
|
|
84
|
+
* Status-glyph tree styling shared with the retired verification-tree renderer.
|
|
66
85
|
*/
|
|
67
86
|
function renderSchemaTree(node, flags, options) {
|
|
68
87
|
const { isLast, prefix, useColor, formatDimText, isRoot = false } = options;
|
|
@@ -181,145 +200,6 @@ function formatIntrospectOutput(result, schemaView, flags) {
|
|
|
181
200
|
return lines.join("\n");
|
|
182
201
|
}
|
|
183
202
|
/**
|
|
184
|
-
* Renders a schema verification tree structure from SchemaVerificationNode.
|
|
185
|
-
* Similar to renderSchemaTree but for verification nodes with status-based colors and glyphs.
|
|
186
|
-
*/
|
|
187
|
-
function renderSchemaVerificationTree(node, flags, options) {
|
|
188
|
-
const { isLast, prefix, useColor, formatDimText, isRoot = false } = options;
|
|
189
|
-
const lines = [];
|
|
190
|
-
let statusGlyph = "";
|
|
191
|
-
let statusColor = (text) => text;
|
|
192
|
-
if (useColor) switch (node.status) {
|
|
193
|
-
case "pass":
|
|
194
|
-
statusGlyph = "✔";
|
|
195
|
-
statusColor = green;
|
|
196
|
-
break;
|
|
197
|
-
case "warn":
|
|
198
|
-
statusGlyph = "⚠";
|
|
199
|
-
statusColor = (text) => useColor ? yellow(text) : text;
|
|
200
|
-
break;
|
|
201
|
-
case "fail":
|
|
202
|
-
statusGlyph = "✖";
|
|
203
|
-
statusColor = red;
|
|
204
|
-
break;
|
|
205
|
-
}
|
|
206
|
-
else switch (node.status) {
|
|
207
|
-
case "pass":
|
|
208
|
-
statusGlyph = "✔";
|
|
209
|
-
break;
|
|
210
|
-
case "warn":
|
|
211
|
-
statusGlyph = "⚠";
|
|
212
|
-
break;
|
|
213
|
-
case "fail":
|
|
214
|
-
statusGlyph = "✖";
|
|
215
|
-
break;
|
|
216
|
-
}
|
|
217
|
-
let labelColor = (text) => text;
|
|
218
|
-
let formattedLabel = node.name;
|
|
219
|
-
if (useColor) switch (node.kind) {
|
|
220
|
-
case "contract":
|
|
221
|
-
case "schema":
|
|
222
|
-
labelColor = bold;
|
|
223
|
-
formattedLabel = labelColor(node.name);
|
|
224
|
-
break;
|
|
225
|
-
case "table": {
|
|
226
|
-
const tableMatch = node.name.match(/^table\s+(.+)$/);
|
|
227
|
-
if (tableMatch?.[1]) {
|
|
228
|
-
const tableName = tableMatch[1];
|
|
229
|
-
formattedLabel = `${dim("table")} ${cyan(tableName)}`;
|
|
230
|
-
} else formattedLabel = dim(node.name);
|
|
231
|
-
break;
|
|
232
|
-
}
|
|
233
|
-
case "columns":
|
|
234
|
-
labelColor = dim;
|
|
235
|
-
formattedLabel = labelColor(node.name);
|
|
236
|
-
break;
|
|
237
|
-
case "column": {
|
|
238
|
-
const columnMatch = node.name.match(/^([^:]+):\s*(.+)$/);
|
|
239
|
-
if (columnMatch?.[1] && columnMatch[2]) {
|
|
240
|
-
const columnName = columnMatch[1];
|
|
241
|
-
const rest = columnMatch[2];
|
|
242
|
-
const typeMatch = rest.match(/^([^\s→]+)\s*→\s*([^\s(]+)\s*(\([^)]+\))$/);
|
|
243
|
-
if (typeMatch?.[1] && typeMatch[2] && typeMatch[3]) {
|
|
244
|
-
const contractType = typeMatch[1];
|
|
245
|
-
const nativeType = typeMatch[2];
|
|
246
|
-
const nullability = typeMatch[3];
|
|
247
|
-
formattedLabel = `${cyan(columnName)}: ${contractType} → ${dim(nativeType)} ${dim(nullability)}`;
|
|
248
|
-
} else formattedLabel = `${cyan(columnName)}: ${rest}`;
|
|
249
|
-
} else formattedLabel = node.name;
|
|
250
|
-
break;
|
|
251
|
-
}
|
|
252
|
-
case "type":
|
|
253
|
-
case "nullability":
|
|
254
|
-
labelColor = (text) => text;
|
|
255
|
-
formattedLabel = labelColor(node.name);
|
|
256
|
-
break;
|
|
257
|
-
case "primaryKey": {
|
|
258
|
-
const pkMatch = node.name.match(/^primary key:\s*(.+)$/);
|
|
259
|
-
if (pkMatch?.[1]) {
|
|
260
|
-
const columnNames = pkMatch[1];
|
|
261
|
-
formattedLabel = `${dim("primary key")}: ${cyan(columnNames)}`;
|
|
262
|
-
} else formattedLabel = dim(node.name);
|
|
263
|
-
break;
|
|
264
|
-
}
|
|
265
|
-
case "foreignKey":
|
|
266
|
-
case "unique":
|
|
267
|
-
case "index":
|
|
268
|
-
labelColor = dim;
|
|
269
|
-
formattedLabel = labelColor(node.name);
|
|
270
|
-
break;
|
|
271
|
-
case "dependency": {
|
|
272
|
-
const dbMatch = node.name.match(/^database is\s+(.+)$/);
|
|
273
|
-
if (dbMatch?.[1]) {
|
|
274
|
-
const dbName = dbMatch[1];
|
|
275
|
-
formattedLabel = `${dim("database is")} ${cyan(dbName)}`;
|
|
276
|
-
} else {
|
|
277
|
-
const extMatch = node.name.match(/^([^\s]+)\s+(extension is enabled)$/);
|
|
278
|
-
if (extMatch?.[1] && extMatch[2]) {
|
|
279
|
-
const extName = extMatch[1];
|
|
280
|
-
const rest = extMatch[2];
|
|
281
|
-
formattedLabel = `${cyan(extName)} ${dim(rest)}`;
|
|
282
|
-
} else {
|
|
283
|
-
labelColor = magenta;
|
|
284
|
-
formattedLabel = labelColor(node.name);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
break;
|
|
288
|
-
}
|
|
289
|
-
default:
|
|
290
|
-
formattedLabel = node.name;
|
|
291
|
-
break;
|
|
292
|
-
}
|
|
293
|
-
else formattedLabel = node.name;
|
|
294
|
-
const statusGlyphColored = statusColor(statusGlyph);
|
|
295
|
-
let nodeLabel = formattedLabel;
|
|
296
|
-
if ((node.status === "fail" || node.status === "warn") && node.message && node.message.length > 0) {
|
|
297
|
-
const messageText = formatDimText(`(${node.message})`);
|
|
298
|
-
nodeLabel = `${formattedLabel} ${messageText}`;
|
|
299
|
-
}
|
|
300
|
-
if (isRoot) lines.push(`${statusGlyphColored} ${nodeLabel}`);
|
|
301
|
-
else {
|
|
302
|
-
const treePrefix = `${formatDimText(isLast ? "└" : "├")}─ `;
|
|
303
|
-
lines.push(`${prefix}${treePrefix}${statusGlyphColored} ${nodeLabel}`);
|
|
304
|
-
}
|
|
305
|
-
if (node.children && node.children.length > 0) {
|
|
306
|
-
const childPrefix = isRoot ? "" : `${prefix}${isLast ? " " : `${formatDimText("│")} `}`;
|
|
307
|
-
for (let i = 0; i < node.children.length; i++) {
|
|
308
|
-
const child = node.children[i];
|
|
309
|
-
if (!child) continue;
|
|
310
|
-
const childLines = renderSchemaVerificationTree(child, flags, {
|
|
311
|
-
isLast: i === node.children.length - 1,
|
|
312
|
-
prefix: childPrefix,
|
|
313
|
-
useColor,
|
|
314
|
-
formatDimText,
|
|
315
|
-
isRoot: false
|
|
316
|
-
});
|
|
317
|
-
lines.push(...childLines);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
return lines;
|
|
321
|
-
}
|
|
322
|
-
/**
|
|
323
203
|
* Formats human-readable output for database schema verification.
|
|
324
204
|
*/
|
|
325
205
|
function formatSchemaVerifyOutput(result, flags, unclaimed = []) {
|
|
@@ -330,30 +210,25 @@ function formatSchemaVerifyOutput(result, flags, unclaimed = []) {
|
|
|
330
210
|
const formatRed = createColorFormatter(useColor, red);
|
|
331
211
|
const formatYellow = createColorFormatter(useColor, yellow);
|
|
332
212
|
const formatDimText = (text) => formatDim(useColor, text);
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
lines.push("");
|
|
343
|
-
lines.push(formatRed("Schema drift:"));
|
|
344
|
-
for (const issue of result.schema.schemaDiffIssues) lines.push(` ${formatRed("✖")} ${issue.message}`);
|
|
213
|
+
const issueMessages = result.schema.issues.map(formatIssueMessage);
|
|
214
|
+
if (issueMessages.length > 0) {
|
|
215
|
+
lines.push(formatRed("Schema issues:"));
|
|
216
|
+
for (const message of issueMessages) lines.push(` ${formatRed("✖")} ${message}`);
|
|
217
|
+
}
|
|
218
|
+
const warningMessages = (result.schema.warnings?.issues ?? []).map(formatIssueMessage);
|
|
219
|
+
if (warningMessages.length > 0) {
|
|
220
|
+
if (lines.length > 0) lines.push("");
|
|
221
|
+
lines.push(formatYellow("Schema warnings:"));
|
|
222
|
+
for (const message of warningMessages) lines.push(` ${formatYellow("⚠")} ${message}`);
|
|
345
223
|
}
|
|
346
224
|
if (unclaimed.length > 0) {
|
|
347
225
|
const strict = result.meta?.strict ?? false;
|
|
348
|
-
lines.push("");
|
|
226
|
+
if (lines.length > 0) lines.push("");
|
|
349
227
|
lines.push((strict ? formatRed : formatYellow)("Unclaimed elements (declared by no contract):"));
|
|
350
228
|
for (const name of unclaimed) lines.push(` ${(strict ? formatRed : formatYellow)(strict ? "✖" : "⚠")} ${name}`);
|
|
351
229
|
}
|
|
352
|
-
if (isVerbose(flags, 1)) {
|
|
353
|
-
|
|
354
|
-
lines.push(`${formatDimText(` pass=${result.schema.counts.pass} warn=${result.schema.counts.warn} fail=${result.schema.counts.fail}`)}`);
|
|
355
|
-
}
|
|
356
|
-
lines.push("");
|
|
230
|
+
if (isVerbose(flags, 1)) lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);
|
|
231
|
+
if (lines.length > 0) lines.push("");
|
|
357
232
|
if (result.ok) lines.push(`${formatGreen("✔")} ${result.summary}`);
|
|
358
233
|
else {
|
|
359
234
|
const codeText = result.code ? ` (${result.code})` : "";
|
|
@@ -404,4 +279,4 @@ function formatSignJson(result) {
|
|
|
404
279
|
//#endregion
|
|
405
280
|
export { formatSignJson as a, formatVerifyOutput as c, formatSchemaVerifyOutput as i, formatIntrospectOutput as n, formatSignOutput as o, formatSchemaVerifyJson as r, formatVerifyJson as s, formatIntrospectJson as t };
|
|
406
281
|
|
|
407
|
-
//# sourceMappingURL=verify-
|
|
282
|
+
//# sourceMappingURL=verify-Djo-Jep_.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-Djo-Jep_.mjs","names":[],"sources":["../src/utils/formatters/verify.ts"],"sourcesContent":["import type {\n CoreSchemaView,\n ExpectationFailureReason,\n IntrospectSchemaResult,\n SchemaDiffIssue,\n SchemaTreeNode,\n SignDatabaseResult,\n VerifyDatabaseResult,\n VerifyDatabaseSchemaResult,\n} from '@prisma-next/framework-components/control';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { bold, cyan, dim, green, magenta, red, yellow } from 'colorette';\nimport type { GlobalFlags } from '../global-flags';\nimport { createColorFormatter, formatDim, isVerbose } from './helpers';\n\n/** Human-readable label for each failure reason, prefixed onto an issue's message for display. */\nconst REASON_LABEL: Record<ExpectationFailureReason, string> = {\n 'not-found': 'missing',\n 'not-expected': 'extra',\n 'not-equal': 'mismatch',\n};\n\n/**\n * The issue's display text: its own message (the differ's `path`), prefixed\n * with a human label for why it's flagged. Turning `reason` into a label is\n * this formatter's job, not the differ's — the differ's issue is data\n * (`path` + `reason` + nodes), not prose.\n */\nfunction formatIssueMessage(issue: SchemaDiffIssue): string {\n return `${REASON_LABEL[issue.reason]}: ${issue.message}`;\n}\n\n// ============================================================================\n// Verify Output Formatters\n// ============================================================================\n\nexport interface DbVerifyCommandSuccessResult {\n readonly ok: true;\n readonly mode: 'full' | 'marker-only';\n readonly summary: string;\n readonly contract: VerifyDatabaseResult['contract'];\n readonly marker?: VerifyDatabaseResult['marker'];\n readonly target: VerifyDatabaseResult['target'];\n readonly missingCodecs?: VerifyDatabaseResult['missingCodecs'];\n readonly codecCoverageSkipped?: VerifyDatabaseResult['codecCoverageSkipped'];\n readonly schema?: {\n readonly summary: string;\n readonly strict: boolean;\n /**\n * Warn-graded finding messages (observed-policy drift). Informational —\n * present on a passing verify; the full-mode result summarizes them as a\n * flat message list.\n */\n readonly warnings?: readonly string[];\n };\n /**\n * Live element names no contract space declares. In full success this is\n * only ever non-empty in lenient mode — strict mode fails on it — and is\n * rendered informationally.\n */\n readonly unclaimed?: readonly string[];\n readonly warning?: string;\n readonly meta?:\n | (NonNullable<VerifyDatabaseResult['meta']> & {\n readonly schemaVerification: 'performed' | 'skipped';\n })\n | {\n readonly schemaVerification: 'performed' | 'skipped';\n };\n readonly timings: {\n readonly total: number;\n };\n}\n\n/**\n * Formats human-readable output for database verify.\n */\nexport function formatVerifyOutput(\n result: DbVerifyCommandSuccessResult,\n flags: GlobalFlags,\n): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatGreen = createColorFormatter(useColor, green);\n const formatYellow = createColorFormatter(useColor, yellow);\n const formatDimText = (text: string) => formatDim(useColor, text);\n const verificationMode =\n result.mode === 'full'\n ? `marker + schema${result.schema?.strict ? ' (strict)' : ' (tolerant)'}`\n : 'marker only (--marker-only)';\n\n lines.push(`${formatGreen('✔')} ${result.summary}`);\n lines.push(`${formatDimText(` verification: ${verificationMode}`)}`);\n lines.push(`${formatDimText(` storageHash: ${result.contract.storageHash}`)}`);\n if (result.contract.profileHash) {\n lines.push(`${formatDimText(` profileHash: ${result.contract.profileHash}`)}`);\n }\n if (result.schema?.warnings && result.schema.warnings.length > 0) {\n lines.push('');\n lines.push(formatYellow('Schema warnings:'));\n for (const message of result.schema.warnings) {\n lines.push(` ${formatYellow('⚠')} ${message}`);\n }\n }\n\n if (result.unclaimed && result.unclaimed.length > 0) {\n lines.push('');\n lines.push(formatYellow('Unclaimed elements (declared by no contract):'));\n for (const name of result.unclaimed) {\n lines.push(` ${formatYellow('⚠')} ${name}`);\n }\n }\n\n if (result.warning) {\n lines.push('');\n lines.push(`${formatYellow('⚠')} ${result.warning}`);\n }\n\n if (isVerbose(flags, 1)) {\n if (result.codecCoverageSkipped) {\n lines.push(\n `${formatDimText(' Codec coverage check skipped (helper returned no supported types)')}`,\n );\n }\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for database verify.\n */\nexport function formatVerifyJson(result: DbVerifyCommandSuccessResult): string {\n const output = {\n ok: result.ok,\n summary: result.summary,\n mode: result.mode,\n contract: result.contract,\n ...ifDefined('marker', result.marker),\n target: result.target,\n ...ifDefined('missingCodecs', result.missingCodecs),\n ...ifDefined('codecCoverageSkipped', result.codecCoverageSkipped),\n ...ifDefined('schema', result.schema),\n unclaimed: result.unclaimed ?? [],\n ...ifDefined('warning', result.warning),\n ...ifDefined('meta', result.meta),\n timings: result.timings,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Formats JSON output for database introspection.\n */\nexport function formatIntrospectJson(result: IntrospectSchemaResult<unknown>): string {\n return JSON.stringify(result, null, 2);\n}\n\n/**\n * Renders a schema tree structure from CoreSchemaView.\n * Status-glyph tree styling shared with the retired verification-tree renderer.\n */\nfunction renderSchemaTree(\n node: SchemaTreeNode,\n flags: GlobalFlags,\n options: {\n readonly isLast: boolean;\n readonly prefix: string;\n readonly useColor: boolean;\n readonly formatDimText: (text: string) => string;\n readonly isRoot?: boolean;\n },\n): string[] {\n const { isLast, prefix, useColor, formatDimText, isRoot = false } = options;\n const lines: string[] = [];\n\n // Format node label with color based on kind (matching schema-verify style)\n let formattedLabel: string = node.label;\n\n if (useColor) {\n switch (node.kind) {\n case 'root':\n formattedLabel = bold(node.label);\n break;\n case 'entity': {\n // Parse \"table tableName\" format - color \"table\" dim, tableName cyan\n const tableMatch = node.label.match(/^table\\s+(.+)$/);\n if (tableMatch?.[1]) {\n const tableName = tableMatch[1];\n formattedLabel = `${dim('table')} ${cyan(tableName)}`;\n } else {\n // Fallback: color entire label with cyan\n formattedLabel = cyan(node.label);\n }\n break;\n }\n case 'collection': {\n // \"columns\" grouping node - dim the label\n formattedLabel = dim(node.label);\n break;\n }\n case 'field': {\n // Parse column name format: \"columnName: typeDisplay (nullability)\"\n // Color code: column name (cyan), type (default), nullability (dim)\n const columnMatch = node.label.match(/^([^:]+):\\s*(.+)$/);\n if (columnMatch?.[1] && columnMatch[2]) {\n const columnName = columnMatch[1];\n const rest = columnMatch[2];\n // Parse rest: \"typeDisplay (nullability)\"\n const typeMatch = rest.match(/^([^\\s(]+)\\s*(\\([^)]+\\))$/);\n if (typeMatch?.[1] && typeMatch[2]) {\n const typeDisplay = typeMatch[1];\n const nullability = typeMatch[2];\n formattedLabel = `${cyan(columnName)}: ${typeDisplay} ${dim(nullability)}`;\n } else {\n // Fallback if format doesn't match\n formattedLabel = `${cyan(columnName)}: ${rest}`;\n }\n } else {\n formattedLabel = node.label;\n }\n break;\n }\n case 'index': {\n // Parse index/unique constraint/primary key formats\n // \"primary key: columnName\" -> dim \"primary key\", cyan columnName\n const pkMatch = node.label.match(/^primary key:\\s*(.+)$/);\n if (pkMatch?.[1]) {\n const columnNames = pkMatch[1];\n formattedLabel = `${dim('primary key')}: ${cyan(columnNames)}`;\n } else {\n // \"unique name\" -> dim \"unique\", cyan \"name\"\n const uniqueMatch = node.label.match(/^unique\\s+(.+)$/);\n if (uniqueMatch?.[1]) {\n const name = uniqueMatch[1];\n formattedLabel = `${dim('unique')} ${cyan(name)}`;\n } else {\n // \"index name\" or \"unique index name\" -> dim label prefix, cyan name\n const indexMatch = node.label.match(/^(unique\\s+)?index\\s+(.+)$/);\n if (indexMatch?.[2]) {\n const indexPrefix = indexMatch[1] ? `${dim('unique')} ` : '';\n const name = indexMatch[2];\n formattedLabel = `${indexPrefix}${dim('index')} ${cyan(name)}`;\n } else {\n formattedLabel = dim(node.label);\n }\n }\n }\n break;\n }\n case 'dependency': {\n // Parse extension message formats similar to schema-verify\n // \"extensionName extension is enabled\" -> cyan extensionName, dim rest\n const extMatch = node.label.match(/^([^\\s]+)\\s+(extension is enabled)$/);\n if (extMatch?.[1] && extMatch[2]) {\n const extName = extMatch[1];\n const rest = extMatch[2];\n formattedLabel = `${cyan(extName)} ${dim(rest)}`;\n } else {\n // Fallback: color entire label with magenta\n formattedLabel = magenta(node.label);\n }\n break;\n }\n default:\n formattedLabel = node.label;\n break;\n }\n }\n\n // Root node renders without tree characters or prefix\n if (isRoot) {\n lines.push(formattedLabel);\n } else {\n const treeChar = isLast ? '└' : '├';\n const treePrefix = `${formatDimText(treeChar)}─ `;\n lines.push(`${prefix}${treePrefix}${formattedLabel}`);\n }\n\n // Render children if present\n if (node.children && node.children.length > 0) {\n const childPrefix = isRoot ? '' : `${prefix}${isLast ? ' ' : `${formatDimText('│')} `}`;\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n if (!child) continue;\n const isLastChild = i === node.children.length - 1;\n const childLines = renderSchemaTree(child, flags, {\n isLast: isLastChild,\n prefix: childPrefix,\n useColor,\n formatDimText,\n isRoot: false,\n });\n lines.push(...childLines);\n }\n }\n\n return lines;\n}\n\n/**\n * Formats human-readable output for database introspection.\n */\nexport function formatIntrospectOutput(\n result: IntrospectSchemaResult<unknown>,\n schemaView: CoreSchemaView | undefined,\n flags: GlobalFlags,\n): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n if (schemaView) {\n // Render tree structure - root node is special (no tree characters)\n const treeLines = renderSchemaTree(schemaView.root, flags, {\n isLast: true,\n prefix: '',\n useColor,\n formatDimText,\n isRoot: true,\n });\n lines.push(...treeLines);\n } else {\n // Fallback: print summary when toSchemaView is not available\n lines.push(`✔ ${result.summary}`);\n if (isVerbose(flags, 1)) {\n lines.push(` Target: ${result.target.familyId}/${result.target.id}`);\n if (result.meta?.dbUrl) {\n lines.push(` Database: ${result.meta.dbUrl}`);\n }\n }\n }\n\n // Add timings in verbose mode\n if (isVerbose(flags, 1)) {\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats human-readable output for database schema verification.\n */\nexport function formatSchemaVerifyOutput(\n result: VerifyDatabaseSchemaResult,\n flags: GlobalFlags,\n unclaimed: readonly string[] = [],\n): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatGreen = createColorFormatter(useColor, green);\n const formatRed = createColorFormatter(useColor, red);\n const formatYellow = createColorFormatter(useColor, yellow);\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n const issueMessages = result.schema.issues.map(formatIssueMessage);\n if (issueMessages.length > 0) {\n lines.push(formatRed('Schema issues:'));\n for (const message of issueMessages) {\n lines.push(` ${formatRed('✖')} ${message}`);\n }\n }\n\n const warningMessages = (result.schema.warnings?.issues ?? []).map(formatIssueMessage);\n if (warningMessages.length > 0) {\n if (lines.length > 0) lines.push('');\n lines.push(formatYellow('Schema warnings:'));\n for (const message of warningMessages) {\n lines.push(` ${formatYellow('⚠')} ${message}`);\n }\n }\n\n if (unclaimed.length > 0) {\n const strict = result.meta?.strict ?? false;\n if (lines.length > 0) lines.push('');\n lines.push(\n (strict ? formatRed : formatYellow)('Unclaimed elements (declared by no contract):'),\n );\n for (const name of unclaimed) {\n lines.push(` ${(strict ? formatRed : formatYellow)(strict ? '✖' : '⚠')} ${name}`);\n }\n }\n\n if (isVerbose(flags, 1)) {\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n\n // Blank line before summary\n if (lines.length > 0) lines.push('');\n\n // Summary line at the end: verdict with status glyph\n if (result.ok) {\n lines.push(`${formatGreen('✔')} ${result.summary}`);\n } else {\n const codeText = result.code ? ` (${result.code})` : '';\n lines.push(`${formatRed('✖')} ${result.summary}${codeText}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for database schema verification. The unclaimed-elements\n * list is a top-level field alongside the combined result, reported once for\n * the whole database.\n */\nexport function formatSchemaVerifyJson(\n result: VerifyDatabaseSchemaResult,\n unclaimed: readonly string[] = [],\n): string {\n return JSON.stringify({ ...result, unclaimed }, null, 2);\n}\n\n// ============================================================================\n// Sign Output Formatters\n// ============================================================================\n\n/**\n * Formats human-readable output for database sign.\n */\nexport function formatSignOutput(result: SignDatabaseResult, flags: GlobalFlags): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatGreen = createColorFormatter(useColor, green);\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n if (result.ok) {\n // Main success message in white (not dimmed)\n lines.push(`${formatGreen('✔')} Database signed`);\n\n // Show from -> to hashes with clear labels\n const previousHash = result.marker.previous?.storageHash ?? 'none';\n const currentHash = result.contract.storageHash;\n\n lines.push(`${formatDimText(` from: ${previousHash}`)}`);\n lines.push(`${formatDimText(` to: ${currentHash}`)}`);\n\n if (isVerbose(flags, 1)) {\n if (result.contract.profileHash) {\n lines.push(`${formatDimText(` profileHash: ${result.contract.profileHash}`)}`);\n }\n if (result.marker.previous?.profileHash) {\n lines.push(\n `${formatDimText(` previous profileHash: ${result.marker.previous.profileHash}`)}`,\n );\n }\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for database sign.\n */\nexport function formatSignJson(result: SignDatabaseResult): string {\n return JSON.stringify(result, null, 2);\n}\n"],"mappings":";;;;;AAgBA,MAAM,eAAyD;CAC7D,aAAa;CACb,gBAAgB;CAChB,aAAa;AACf;;;;;;;AAQA,SAAS,mBAAmB,OAAgC;CAC1D,OAAO,GAAG,aAAa,MAAM,QAAQ,IAAI,MAAM;AACjD;;;;AA+CA,SAAgB,mBACd,QACA,OACQ;CACR,IAAI,MAAM,OACR,OAAO;CAGT,MAAM,QAAkB,CAAC;CAEzB,MAAM,WAAW,MAAM,UAAU;CACjC,MAAM,cAAc,qBAAqB,UAAU,KAAK;CACxD,MAAM,eAAe,qBAAqB,UAAU,MAAM;CAC1D,MAAM,iBAAiB,SAAiB,UAAU,UAAU,IAAI;CAChE,MAAM,mBACJ,OAAO,SAAS,SACZ,kBAAkB,OAAO,QAAQ,SAAS,cAAc,kBACxD;CAEN,MAAM,KAAK,GAAG,YAAY,GAAG,EAAE,GAAG,OAAO,SAAS;CAClD,MAAM,KAAK,GAAG,cAAc,mBAAmB,kBAAkB,GAAG;CACpE,MAAM,KAAK,GAAG,cAAc,kBAAkB,OAAO,SAAS,aAAa,GAAG;CAC9E,IAAI,OAAO,SAAS,aAClB,MAAM,KAAK,GAAG,cAAc,kBAAkB,OAAO,SAAS,aAAa,GAAG;CAEhF,IAAI,OAAO,QAAQ,YAAY,OAAO,OAAO,SAAS,SAAS,GAAG;EAChE,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,aAAa,kBAAkB,CAAC;EAC3C,KAAK,MAAM,WAAW,OAAO,OAAO,UAClC,MAAM,KAAK,KAAK,aAAa,GAAG,EAAE,GAAG,SAAS;CAElD;CAEA,IAAI,OAAO,aAAa,OAAO,UAAU,SAAS,GAAG;EACnD,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,aAAa,+CAA+C,CAAC;EACxE,KAAK,MAAM,QAAQ,OAAO,WACxB,MAAM,KAAK,KAAK,aAAa,GAAG,EAAE,GAAG,MAAM;CAE/C;CAEA,IAAI,OAAO,SAAS;EAClB,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,GAAG,aAAa,GAAG,EAAE,GAAG,OAAO,SAAS;CACrD;CAEA,IAAI,UAAU,OAAO,CAAC,GAAG;EACvB,IAAI,OAAO,sBACT,MAAM,KACJ,GAAG,cAAc,qEAAqE,GACxF;EAEF,MAAM,KAAK,GAAG,cAAc,iBAAiB,OAAO,QAAQ,MAAM,GAAG,GAAG;CAC1E;CAEA,OAAO,MAAM,KAAK,IAAI;AACxB;;;;AAKA,SAAgB,iBAAiB,QAA8C;CAC7E,MAAM,SAAS;EACb,IAAI,OAAO;EACX,SAAS,OAAO;EAChB,MAAM,OAAO;EACb,UAAU,OAAO;EACjB,GAAG,UAAU,UAAU,OAAO,MAAM;EACpC,QAAQ,OAAO;EACf,GAAG,UAAU,iBAAiB,OAAO,aAAa;EAClD,GAAG,UAAU,wBAAwB,OAAO,oBAAoB;EAChE,GAAG,UAAU,UAAU,OAAO,MAAM;EACpC,WAAW,OAAO,aAAa,CAAC;EAChC,GAAG,UAAU,WAAW,OAAO,OAAO;EACtC,GAAG,UAAU,QAAQ,OAAO,IAAI;EAChC,SAAS,OAAO;CAClB;CAEA,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;AACvC;;;;AAKA,SAAgB,qBAAqB,QAAiD;CACpF,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;AACvC;;;;;AAMA,SAAS,iBACP,MACA,OACA,SAOU;CACV,MAAM,EAAE,QAAQ,QAAQ,UAAU,eAAe,SAAS,UAAU;CACpE,MAAM,QAAkB,CAAC;CAGzB,IAAI,iBAAyB,KAAK;CAElC,IAAI,UACF,QAAQ,KAAK,MAAb;EACE,KAAK;GACH,iBAAiB,KAAK,KAAK,KAAK;GAChC;EACF,KAAK,UAAU;GAEb,MAAM,aAAa,KAAK,MAAM,MAAM,gBAAgB;GACpD,IAAI,aAAa,IAAI;IACnB,MAAM,YAAY,WAAW;IAC7B,iBAAiB,GAAG,IAAI,OAAO,EAAE,GAAG,KAAK,SAAS;GACpD,OAEE,iBAAiB,KAAK,KAAK,KAAK;GAElC;EACF;EACA,KAAK;GAEH,iBAAiB,IAAI,KAAK,KAAK;GAC/B;EAEF,KAAK,SAAS;GAGZ,MAAM,cAAc,KAAK,MAAM,MAAM,mBAAmB;GACxD,IAAI,cAAc,MAAM,YAAY,IAAI;IACtC,MAAM,aAAa,YAAY;IAC/B,MAAM,OAAO,YAAY;IAEzB,MAAM,YAAY,KAAK,MAAM,2BAA2B;IACxD,IAAI,YAAY,MAAM,UAAU,IAAI;KAClC,MAAM,cAAc,UAAU;KAC9B,MAAM,cAAc,UAAU;KAC9B,iBAAiB,GAAG,KAAK,UAAU,EAAE,IAAI,YAAY,GAAG,IAAI,WAAW;IACzE,OAEE,iBAAiB,GAAG,KAAK,UAAU,EAAE,IAAI;GAE7C,OACE,iBAAiB,KAAK;GAExB;EACF;EACA,KAAK,SAAS;GAGZ,MAAM,UAAU,KAAK,MAAM,MAAM,uBAAuB;GACxD,IAAI,UAAU,IAAI;IAChB,MAAM,cAAc,QAAQ;IAC5B,iBAAiB,GAAG,IAAI,aAAa,EAAE,IAAI,KAAK,WAAW;GAC7D,OAAO;IAEL,MAAM,cAAc,KAAK,MAAM,MAAM,iBAAiB;IACtD,IAAI,cAAc,IAAI;KACpB,MAAM,OAAO,YAAY;KACzB,iBAAiB,GAAG,IAAI,QAAQ,EAAE,GAAG,KAAK,IAAI;IAChD,OAAO;KAEL,MAAM,aAAa,KAAK,MAAM,MAAM,4BAA4B;KAChE,IAAI,aAAa,IAAI;MACnB,MAAM,cAAc,WAAW,KAAK,GAAG,IAAI,QAAQ,EAAE,KAAK;MAC1D,MAAM,OAAO,WAAW;MACxB,iBAAiB,GAAG,cAAc,IAAI,OAAO,EAAE,GAAG,KAAK,IAAI;KAC7D,OACE,iBAAiB,IAAI,KAAK,KAAK;IAEnC;GACF;GACA;EACF;EACA,KAAK,cAAc;GAGjB,MAAM,WAAW,KAAK,MAAM,MAAM,qCAAqC;GACvE,IAAI,WAAW,MAAM,SAAS,IAAI;IAChC,MAAM,UAAU,SAAS;IACzB,MAAM,OAAO,SAAS;IACtB,iBAAiB,GAAG,KAAK,OAAO,EAAE,GAAG,IAAI,IAAI;GAC/C,OAEE,iBAAiB,QAAQ,KAAK,KAAK;GAErC;EACF;EACA;GACE,iBAAiB,KAAK;GACtB;CACJ;CAIF,IAAI,QACF,MAAM,KAAK,cAAc;MACpB;EAEL,MAAM,aAAa,GAAG,cADL,SAAS,MAAM,GACY,EAAE;EAC9C,MAAM,KAAK,GAAG,SAAS,aAAa,gBAAgB;CACtD;CAGA,IAAI,KAAK,YAAY,KAAK,SAAS,SAAS,GAAG;EAC7C,MAAM,cAAc,SAAS,KAAK,GAAG,SAAS,SAAS,QAAQ,GAAG,cAAc,GAAG,EAAE;EACrF,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK;GAC7C,MAAM,QAAQ,KAAK,SAAS;GAC5B,IAAI,CAAC,OAAO;GAEZ,MAAM,aAAa,iBAAiB,OAAO,OAAO;IAChD,QAFkB,MAAM,KAAK,SAAS,SAAS;IAG/C,QAAQ;IACR;IACA;IACA,QAAQ;GACV,CAAC;GACD,MAAM,KAAK,GAAG,UAAU;EAC1B;CACF;CAEA,OAAO;AACT;;;;AAKA,SAAgB,uBACd,QACA,YACA,OACQ;CACR,IAAI,MAAM,OACR,OAAO;CAGT,MAAM,QAAkB,CAAC;CAEzB,MAAM,WAAW,MAAM,UAAU;CACjC,MAAM,iBAAiB,SAAiB,UAAU,UAAU,IAAI;CAEhE,IAAI,YAAY;EAEd,MAAM,YAAY,iBAAiB,WAAW,MAAM,OAAO;GACzD,QAAQ;GACR,QAAQ;GACR;GACA;GACA,QAAQ;EACV,CAAC;EACD,MAAM,KAAK,GAAG,SAAS;CACzB,OAAO;EAEL,MAAM,KAAK,KAAK,OAAO,SAAS;EAChC,IAAI,UAAU,OAAO,CAAC,GAAG;GACvB,MAAM,KAAK,aAAa,OAAO,OAAO,SAAS,GAAG,OAAO,OAAO,IAAI;GACpE,IAAI,OAAO,MAAM,OACf,MAAM,KAAK,eAAe,OAAO,KAAK,OAAO;EAEjD;CACF;CAGA,IAAI,UAAU,OAAO,CAAC,GACpB,MAAM,KAAK,GAAG,cAAc,iBAAiB,OAAO,QAAQ,MAAM,GAAG,GAAG;CAG1E,OAAO,MAAM,KAAK,IAAI;AACxB;;;;AAKA,SAAgB,yBACd,QACA,OACA,YAA+B,CAAC,GACxB;CACR,IAAI,MAAM,OACR,OAAO;CAGT,MAAM,QAAkB,CAAC;CAEzB,MAAM,WAAW,MAAM,UAAU;CACjC,MAAM,cAAc,qBAAqB,UAAU,KAAK;CACxD,MAAM,YAAY,qBAAqB,UAAU,GAAG;CACpD,MAAM,eAAe,qBAAqB,UAAU,MAAM;CAC1D,MAAM,iBAAiB,SAAiB,UAAU,UAAU,IAAI;CAEhE,MAAM,gBAAgB,OAAO,OAAO,OAAO,IAAI,kBAAkB;CACjE,IAAI,cAAc,SAAS,GAAG;EAC5B,MAAM,KAAK,UAAU,gBAAgB,CAAC;EACtC,KAAK,MAAM,WAAW,eACpB,MAAM,KAAK,KAAK,UAAU,GAAG,EAAE,GAAG,SAAS;CAE/C;CAEA,MAAM,mBAAmB,OAAO,OAAO,UAAU,UAAU,CAAC,EAAA,CAAG,IAAI,kBAAkB;CACrF,IAAI,gBAAgB,SAAS,GAAG;EAC9B,IAAI,MAAM,SAAS,GAAG,MAAM,KAAK,EAAE;EACnC,MAAM,KAAK,aAAa,kBAAkB,CAAC;EAC3C,KAAK,MAAM,WAAW,iBACpB,MAAM,KAAK,KAAK,aAAa,GAAG,EAAE,GAAG,SAAS;CAElD;CAEA,IAAI,UAAU,SAAS,GAAG;EACxB,MAAM,SAAS,OAAO,MAAM,UAAU;EACtC,IAAI,MAAM,SAAS,GAAG,MAAM,KAAK,EAAE;EACnC,MAAM,MACH,SAAS,YAAY,aAAA,CAAc,+CAA+C,CACrF;EACA,KAAK,MAAM,QAAQ,WACjB,MAAM,KAAK,MAAM,SAAS,YAAY,aAAA,CAAc,SAAS,MAAM,GAAG,EAAE,GAAG,MAAM;CAErF;CAEA,IAAI,UAAU,OAAO,CAAC,GACpB,MAAM,KAAK,GAAG,cAAc,iBAAiB,OAAO,QAAQ,MAAM,GAAG,GAAG;CAI1E,IAAI,MAAM,SAAS,GAAG,MAAM,KAAK,EAAE;CAGnC,IAAI,OAAO,IACT,MAAM,KAAK,GAAG,YAAY,GAAG,EAAE,GAAG,OAAO,SAAS;MAC7C;EACL,MAAM,WAAW,OAAO,OAAO,KAAK,OAAO,KAAK,KAAK;EACrD,MAAM,KAAK,GAAG,UAAU,GAAG,EAAE,GAAG,OAAO,UAAU,UAAU;CAC7D;CAEA,OAAO,MAAM,KAAK,IAAI;AACxB;;;;;;AAOA,SAAgB,uBACd,QACA,YAA+B,CAAC,GACxB;CACR,OAAO,KAAK,UAAU;EAAE,GAAG;EAAQ;CAAU,GAAG,MAAM,CAAC;AACzD;;;;AASA,SAAgB,iBAAiB,QAA4B,OAA4B;CACvF,IAAI,MAAM,OACR,OAAO;CAGT,MAAM,QAAkB,CAAC;CAEzB,MAAM,WAAW,MAAM,UAAU;CACjC,MAAM,cAAc,qBAAqB,UAAU,KAAK;CACxD,MAAM,iBAAiB,SAAiB,UAAU,UAAU,IAAI;CAEhE,IAAI,OAAO,IAAI;EAEb,MAAM,KAAK,GAAG,YAAY,GAAG,EAAE,iBAAiB;EAGhD,MAAM,eAAe,OAAO,OAAO,UAAU,eAAe;EAC5D,MAAM,cAAc,OAAO,SAAS;EAEpC,MAAM,KAAK,GAAG,cAAc,WAAW,cAAc,GAAG;EACxD,MAAM,KAAK,GAAG,cAAc,WAAW,aAAa,GAAG;EAEvD,IAAI,UAAU,OAAO,CAAC,GAAG;GACvB,IAAI,OAAO,SAAS,aAClB,MAAM,KAAK,GAAG,cAAc,kBAAkB,OAAO,SAAS,aAAa,GAAG;GAEhF,IAAI,OAAO,OAAO,UAAU,aAC1B,MAAM,KACJ,GAAG,cAAc,2BAA2B,OAAO,OAAO,SAAS,aAAa,GAClF;GAEF,MAAM,KAAK,GAAG,cAAc,iBAAiB,OAAO,QAAQ,MAAM,GAAG,GAAG;EAC1E;CACF;CAEA,OAAO,MAAM,KAAK,IAAI;AACxB;;;;AAKA,SAAgB,eAAe,QAAoC;CACjE,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;AACvC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/cli",
|
|
3
|
-
"version": "0.14.0-dev.
|
|
3
|
+
"version": "0.14.0-dev.50",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -9,18 +9,18 @@
|
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@clack/prompts": "^1.4.0",
|
|
12
|
-
"@prisma-next/config": "0.14.0-dev.
|
|
13
|
-
"@prisma-next/config-loader": "0.14.0-dev.
|
|
14
|
-
"@prisma-next/contract": "0.14.0-dev.
|
|
15
|
-
"@prisma-next/emitter": "0.14.0-dev.
|
|
16
|
-
"@prisma-next/errors": "0.14.0-dev.
|
|
17
|
-
"@prisma-next/framework-components": "0.14.0-dev.
|
|
18
|
-
"@prisma-next/language-server": "0.14.0-dev.
|
|
19
|
-
"@prisma-next/migration-tools": "0.14.0-dev.
|
|
20
|
-
"@prisma-next/psl-parser": "0.14.0-dev.
|
|
21
|
-
"@prisma-next/psl-printer": "0.14.0-dev.
|
|
22
|
-
"@prisma-next/cli-telemetry": "0.14.0-dev.
|
|
23
|
-
"@prisma-next/utils": "0.14.0-dev.
|
|
12
|
+
"@prisma-next/config": "0.14.0-dev.50",
|
|
13
|
+
"@prisma-next/config-loader": "0.14.0-dev.50",
|
|
14
|
+
"@prisma-next/contract": "0.14.0-dev.50",
|
|
15
|
+
"@prisma-next/emitter": "0.14.0-dev.50",
|
|
16
|
+
"@prisma-next/errors": "0.14.0-dev.50",
|
|
17
|
+
"@prisma-next/framework-components": "0.14.0-dev.50",
|
|
18
|
+
"@prisma-next/language-server": "0.14.0-dev.50",
|
|
19
|
+
"@prisma-next/migration-tools": "0.14.0-dev.50",
|
|
20
|
+
"@prisma-next/psl-parser": "0.14.0-dev.50",
|
|
21
|
+
"@prisma-next/psl-printer": "0.14.0-dev.50",
|
|
22
|
+
"@prisma-next/cli-telemetry": "0.14.0-dev.50",
|
|
23
|
+
"@prisma-next/utils": "0.14.0-dev.50",
|
|
24
24
|
"arktype": "^2.2.0",
|
|
25
25
|
"ci-info": "^4.3.1",
|
|
26
26
|
"clipanion": "4.0.0-rc.4",
|
|
@@ -36,14 +36,14 @@
|
|
|
36
36
|
"wrap-ansi": "^10.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@prisma-next/sql-contract": "0.14.0-dev.
|
|
40
|
-
"@prisma-next/sql-contract-emitter": "0.14.0-dev.
|
|
41
|
-
"@prisma-next/sql-contract-ts": "0.14.0-dev.
|
|
42
|
-
"@prisma-next/sql-operations": "0.14.0-dev.
|
|
43
|
-
"@prisma-next/sql-runtime": "0.14.0-dev.
|
|
44
|
-
"@prisma-next/test-utils": "0.14.0-dev.
|
|
45
|
-
"@prisma-next/tsconfig": "0.14.0-dev.
|
|
46
|
-
"@prisma-next/tsdown": "0.14.0-dev.
|
|
39
|
+
"@prisma-next/sql-contract": "0.14.0-dev.50",
|
|
40
|
+
"@prisma-next/sql-contract-emitter": "0.14.0-dev.50",
|
|
41
|
+
"@prisma-next/sql-contract-ts": "0.14.0-dev.50",
|
|
42
|
+
"@prisma-next/sql-operations": "0.14.0-dev.50",
|
|
43
|
+
"@prisma-next/sql-runtime": "0.14.0-dev.50",
|
|
44
|
+
"@prisma-next/test-utils": "0.14.0-dev.50",
|
|
45
|
+
"@prisma-next/tsconfig": "0.14.0-dev.50",
|
|
46
|
+
"@prisma-next/tsdown": "0.14.0-dev.50",
|
|
47
47
|
"@types/node": "25.9.1",
|
|
48
48
|
"tsdown": "0.22.1",
|
|
49
49
|
"typescript": "5.9.3",
|
|
@@ -437,8 +437,8 @@ async function executeDbVerifyCommand(
|
|
|
437
437
|
...ifDefined('codecCoverageSkipped', verifyResult.codecCoverageSkipped),
|
|
438
438
|
schema: {
|
|
439
439
|
summary: combined.result.summary,
|
|
440
|
-
counts: combined.result.schema.counts,
|
|
441
440
|
strict: combined.result.meta?.strict ?? false,
|
|
441
|
+
warnings: (combined.result.schema.warnings?.issues ?? []).map((issue) => issue.message),
|
|
442
442
|
},
|
|
443
443
|
unclaimed: combined.unclaimed,
|
|
444
444
|
meta: {
|
package/src/commands/migrate.ts
CHANGED
|
@@ -153,7 +153,7 @@ export interface MigrateResult {
|
|
|
153
153
|
* - `readAllMarkers()` for the from-state (when no `--from` is given), preserving
|
|
154
154
|
* the full marker including `invariants` (not just `storageHash`).
|
|
155
155
|
* - `planSpacePath()` (shared with `executeMigrate`) for per-space path selection,
|
|
156
|
-
* which feeds `
|
|
156
|
+
* which feeds `resolveRecordedPath()` with the same target hash, target invariants,
|
|
157
157
|
* and current marker as the real apply path uses.
|
|
158
158
|
*
|
|
159
159
|
* Returns BEFORE any write boundary (`runMigration` / marker / DDL). No
|
|
@@ -206,7 +206,7 @@ async function executeMigrateShowCommand(
|
|
|
206
206
|
const appGraph = aggregate.app.graph();
|
|
207
207
|
|
|
208
208
|
// Resolve the --to target (defaults to the on-disk contract, same as migrate).
|
|
209
|
-
// Also capture the ref's invariants so planSpacePath feeds
|
|
209
|
+
// Also capture the ref's invariants so planSpacePath feeds resolveRecordedPath the
|
|
210
210
|
// same target invariants that real migrate would use (refInvariants ?? headRef.invariants).
|
|
211
211
|
let targetHash: string = contractHash;
|
|
212
212
|
let refInvariants: readonly string[] | undefined;
|
|
@@ -262,7 +262,7 @@ async function executeMigrateShowCommand(
|
|
|
262
262
|
// - Omitted: read the live DB marker via readAllMarkers() — the same source migrate uses.
|
|
263
263
|
//
|
|
264
264
|
// Full marker records (storageHash + invariants) are preserved so planSpacePath
|
|
265
|
-
// can feed
|
|
265
|
+
// can feed resolveRecordedPath the complete currentMarker — exactly as executeMigrate
|
|
266
266
|
// does via familyInstance.readAllMarkers(). A stripped { storageHash, invariants: [] }
|
|
267
267
|
// marker would produce a different `required` set and a different (incorrect) path.
|
|
268
268
|
type LiveMarker = { readonly storageHash: string; readonly invariants: readonly string[] };
|
|
@@ -359,7 +359,7 @@ async function executeMigrateShowCommand(
|
|
|
359
359
|
}
|
|
360
360
|
|
|
361
361
|
// Walk the path via planSpacePath — the same helper executeMigrate uses.
|
|
362
|
-
// planSpacePath feeds
|
|
362
|
+
// planSpacePath feeds resolveRecordedPath identical inputs (targetHash, targetInvariants,
|
|
363
363
|
// currentMarker with full invariants), so the preview path is always the path migrate runs.
|
|
364
364
|
//
|
|
365
365
|
// Canonical schedule order: extensions alphabetically first, then app — mirroring the
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
hasOperationPreview,
|
|
8
8
|
type MigrationPlanOperation,
|
|
9
9
|
type OperationPreview,
|
|
10
|
+
type SchemaOwnership,
|
|
10
11
|
} from '@prisma-next/framework-components/control';
|
|
11
12
|
import { canonicalizeJson } from '@prisma-next/framework-components/utils';
|
|
12
13
|
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
@@ -97,6 +98,7 @@ async function runPlannerLeg(
|
|
|
97
98
|
contract: Contract,
|
|
98
99
|
fromContract: Contract | null,
|
|
99
100
|
spaceId: string,
|
|
101
|
+
ownership: SchemaOwnership,
|
|
100
102
|
): Promise<Result<PlannerSuccess, CliStructuredError>> {
|
|
101
103
|
const fromSchema = migrations.contractToSchema(fromContract, frameworkComponents);
|
|
102
104
|
const plannerResult = planner.plan({
|
|
@@ -106,6 +108,12 @@ async function runPlannerLeg(
|
|
|
106
108
|
fromContract,
|
|
107
109
|
frameworkComponents,
|
|
108
110
|
spaceId,
|
|
111
|
+
// Offline `migration plan` is the aggregate-of-(possibly one) degenerate
|
|
112
|
+
// case: the same ownership consultation the live aggregate flow uses. A
|
|
113
|
+
// from→to extra (a table removed from the contract) is not declared by any
|
|
114
|
+
// space in the aggregate, so it stays a genuine drop; a table another
|
|
115
|
+
// space owns is never dropped.
|
|
116
|
+
ownership,
|
|
109
117
|
});
|
|
110
118
|
if (plannerResult.kind === 'failure') {
|
|
111
119
|
return notOk(
|
|
@@ -498,6 +506,7 @@ async function executeMigrationPlanCommand(
|
|
|
498
506
|
fromContract,
|
|
499
507
|
null,
|
|
500
508
|
aggregate.app.spaceId,
|
|
509
|
+
aggregate,
|
|
501
510
|
);
|
|
502
511
|
if (!baselineLeg.ok) {
|
|
503
512
|
return notOk(baselineLeg.failure);
|
|
@@ -567,6 +576,7 @@ async function executeMigrationPlanCommand(
|
|
|
567
576
|
aggregate.app.contract(),
|
|
568
577
|
fromContract,
|
|
569
578
|
aggregate.app.spaceId,
|
|
579
|
+
aggregate,
|
|
570
580
|
);
|
|
571
581
|
if (!deltaLeg.ok) {
|
|
572
582
|
return notOk(deltaLeg.failure);
|
|
@@ -640,6 +650,7 @@ async function executeMigrationPlanCommand(
|
|
|
640
650
|
aggregate.app.contract(),
|
|
641
651
|
fromContract,
|
|
642
652
|
aggregate.app.spaceId,
|
|
653
|
+
aggregate,
|
|
643
654
|
);
|
|
644
655
|
if (!deltaLeg.ok) {
|
|
645
656
|
return notOk(deltaLeg.failure);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Backs `db init` / `db update`. Strategy: introspect → planMigration;
|
|
2
|
+
* Backs `db init` / `db update`. Strategy: introspect → planMigration; planFromDiff-for-app + resolveRecordedPath-extensions; plan-mode + orphan-marker preflight.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { Contract } from '@prisma-next/contract/types';
|
|
@@ -97,10 +97,11 @@ export interface ExecuteRunOptions<TFamilyId extends string, TTargetId extends s
|
|
|
97
97
|
* integrity violation short-circuits with a structured error.
|
|
98
98
|
* 2. **Read DB state**: marker rows (`familyInstance.readAllMarkers`)
|
|
99
99
|
* + introspected schema (`familyInstance.introspect`).
|
|
100
|
-
* 3. **Plan**: {@link planMigration} chooses
|
|
101
|
-
* space according to `callerPolicy.ignoreGraphFor`.
|
|
102
|
-
* is forced through
|
|
103
|
-
* extension space walks its on-disk graph
|
|
100
|
+
* 3. **Plan**: {@link planMigration} chooses `resolveRecordedPath` vs
|
|
101
|
+
* `planFromDiff` per space according to `callerPolicy.ignoreGraphFor`.
|
|
102
|
+
* The app space is forced through `planFromDiff` (today's daily-driver
|
|
103
|
+
* behaviour); every extension space walks its on-disk graph via
|
|
104
|
+
* `resolveRecordedPath`.
|
|
104
105
|
* 4. **Apply** (when `mode === 'apply'`): every per-space `MigrationPlan`
|
|
105
106
|
* feeds into the runner's `execute` — one outer
|
|
106
107
|
* transaction across every space; failure on any space rolls back
|
|
@@ -168,9 +169,9 @@ export async function executeRun<TFamilyId extends string, TTargetId extends str
|
|
|
168
169
|
});
|
|
169
170
|
onProgress?.({ action, kind: 'spanEnd', spanId: SPAN_IDS.introspect, outcome: 'ok' });
|
|
170
171
|
|
|
171
|
-
// 3. Plan via aggregate planner. App is forced through
|
|
172
|
-
// `db init` / `db update` daily-driver behaviour); extensions
|
|
173
|
-
// their on-disk migration graphs.
|
|
172
|
+
// 3. Plan via aggregate planner. App is forced through planFromDiff
|
|
173
|
+
// (today's `db init` / `db update` daily-driver behaviour); extensions
|
|
174
|
+
// walk their on-disk migration graphs via resolveRecordedPath.
|
|
174
175
|
onProgress?.({
|
|
175
176
|
action,
|
|
176
177
|
kind: 'spanStart',
|
|
@@ -228,9 +229,9 @@ export async function executeRun<TFamilyId extends string, TTargetId extends str
|
|
|
228
229
|
// 5. Run mode: hand off to the shared `runMigration` primitive.
|
|
229
230
|
// The runner-driving tail is identical for `db init` / `db update` /
|
|
230
231
|
// `migrate` — only how each caller produces `perSpacePlans`
|
|
231
|
-
// differs (
|
|
232
|
-
// only for migrate). Each caller produces
|
|
233
|
-
// this helper handles the shared run tail.
|
|
232
|
+
// differs (planFromDiff + resolveRecordedPath via planMigration here;
|
|
233
|
+
// resolveRecordedPath only for migrate). Each caller produces
|
|
234
|
+
// perSpacePlans differently; this helper handles the shared run tail.
|
|
234
235
|
const applied = await runMigration({
|
|
235
236
|
aggregate,
|
|
236
237
|
perSpacePlans: planResult.value.perSpace,
|
|
@@ -328,7 +329,7 @@ function detectOrphanMarkers(
|
|
|
328
329
|
}
|
|
329
330
|
|
|
330
331
|
function mapPlannerError(error: PlannerError): DbInitResult | DbUpdateResult {
|
|
331
|
-
if (error.kind === '
|
|
332
|
+
if (error.kind === 'planFromDiffFailed') {
|
|
332
333
|
const failure: DbInitFailure | DbUpdateFailure = {
|
|
333
334
|
code: 'PLANNING_FAILED',
|
|
334
335
|
summary: 'Migration planning failed due to conflicts',
|
|
@@ -4,8 +4,10 @@ import type {
|
|
|
4
4
|
ControlDriverInstance,
|
|
5
5
|
ControlExtensionDescriptor,
|
|
6
6
|
ControlFamilyInstance,
|
|
7
|
+
SchemaDiffIssue,
|
|
7
8
|
VerifyDatabaseSchemaResult,
|
|
8
9
|
} from '@prisma-next/framework-components/control';
|
|
10
|
+
import { hasSchemaSubjectClassifier } from '@prisma-next/framework-components/control';
|
|
9
11
|
import {
|
|
10
12
|
type AggregateContractSpace,
|
|
11
13
|
collectAggregateNamespaces,
|
|
@@ -14,6 +16,7 @@ import {
|
|
|
14
16
|
verifyMigration,
|
|
15
17
|
} from '@prisma-next/migration-tools/aggregate';
|
|
16
18
|
import { castAs } from '@prisma-next/utils/casts';
|
|
19
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
17
20
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
18
21
|
import { CliStructuredError } from '../../utils/cli-errors';
|
|
19
22
|
import {
|
|
@@ -115,6 +118,17 @@ export async function executeDbVerify<TFamilyId extends string, TTargetId extend
|
|
|
115
118
|
contract: collectAggregateNamespaces(aggregate),
|
|
116
119
|
});
|
|
117
120
|
|
|
121
|
+
// The subject-granularity + entity-kind classifiers are an injected
|
|
122
|
+
// capability — not every family provides one — detected the same way the
|
|
123
|
+
// other optional per-family capabilities are (`hasSchemaView`, …), never
|
|
124
|
+
// assumed.
|
|
125
|
+
const classifySubjectGranularity = hasSchemaSubjectClassifier(familyInstance)
|
|
126
|
+
? (issue: SchemaDiffIssue) => familyInstance.classifySubjectGranularity(issue)
|
|
127
|
+
: undefined;
|
|
128
|
+
const classifyEntityKind = hasSchemaSubjectClassifier(familyInstance)
|
|
129
|
+
? (issue: SchemaDiffIssue) => familyInstance.classifyEntityKind(issue)
|
|
130
|
+
: undefined;
|
|
131
|
+
|
|
118
132
|
emitVerifySpan(onProgress, 'spanStart');
|
|
119
133
|
const verifyResult = verifyMigration({
|
|
120
134
|
aggregate,
|
|
@@ -122,6 +136,8 @@ export async function executeDbVerify<TFamilyId extends string, TTargetId extend
|
|
|
122
136
|
schemaIntrospection,
|
|
123
137
|
mode: options.mode,
|
|
124
138
|
verifySchemaForSpace: createPerSpaceVerifier(options),
|
|
139
|
+
...ifDefined('classifySubjectGranularity', classifySubjectGranularity),
|
|
140
|
+
...ifDefined('classifyEntityKind', classifyEntityKind),
|
|
125
141
|
});
|
|
126
142
|
return finaliseVerifyResult({ verifyResult, aggregate, skipMarker, onProgress });
|
|
127
143
|
}
|
|
@@ -276,19 +292,6 @@ function buildSkippedSchemaResult(space: AggregateContractSpace): VerifyDatabase
|
|
|
276
292
|
target: { expected: contract.target },
|
|
277
293
|
schema: {
|
|
278
294
|
issues: [],
|
|
279
|
-
schemaDiffIssues: [],
|
|
280
|
-
root: {
|
|
281
|
-
status: 'pass',
|
|
282
|
-
kind: 'skipped',
|
|
283
|
-
name: space.spaceId,
|
|
284
|
-
contractPath: '',
|
|
285
|
-
code: 'SKIPPED',
|
|
286
|
-
message: 'Schema verification skipped',
|
|
287
|
-
expected: undefined,
|
|
288
|
-
actual: undefined,
|
|
289
|
-
children: [],
|
|
290
|
-
},
|
|
291
|
-
counts: { pass: 0, warn: 0, fail: 0, totalNodes: 0 },
|
|
292
295
|
},
|
|
293
296
|
timings: { total: 0 },
|
|
294
297
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Backs the `migrate` command.
|
|
2
|
+
* Backs the `migrate` command. Resolves the recorded path for every space, replay-only (no introspect/diff/planner).
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { Contract } from '@prisma-next/contract/types';
|
|
@@ -12,12 +12,12 @@ import type {
|
|
|
12
12
|
} from '@prisma-next/framework-components/control';
|
|
13
13
|
import {
|
|
14
14
|
type AggregateContractSpace,
|
|
15
|
-
|
|
15
|
+
buildFabricatedMigrationEdge,
|
|
16
16
|
type ContractMarkerRecordLike,
|
|
17
17
|
type ContractSpaceAggregate,
|
|
18
|
-
graphWalkStrategy,
|
|
19
18
|
type PerSpacePlan,
|
|
20
19
|
requireHeadRef,
|
|
20
|
+
resolveRecordedPath,
|
|
21
21
|
} from '@prisma-next/migration-tools/aggregate';
|
|
22
22
|
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
23
23
|
import { errorNoInvariantPath } from '@prisma-next/migration-tools/errors';
|
|
@@ -96,7 +96,7 @@ export interface ExecuteMigrateOptions<TFamilyId extends string, TTargetId exten
|
|
|
96
96
|
* 1. Load aggregate from disk (loader hydrates extension graphs;
|
|
97
97
|
* caller provides app-space packages).
|
|
98
98
|
* 2. Read live marker rows per space (`familyInstance.readAllMarkers`).
|
|
99
|
-
* 3. Per space: `
|
|
99
|
+
* 3. Per space: `resolveRecordedPath` plots the path from the live
|
|
100
100
|
* marker to `space.headRef.hash` (or `refHash` for the app
|
|
101
101
|
* space when provided). Empty-graph spaces fail loudly — a
|
|
102
102
|
* "never planned" space is a user-error condition for replay.
|
|
@@ -326,7 +326,7 @@ export async function executeMigrate<TFamilyId extends string, TTargetId extends
|
|
|
326
326
|
* Callers switch on `kind` and map to their own error representation:
|
|
327
327
|
* `executeMigrate` throws / returns `notOk`; `executeMigrateShowCommand`
|
|
328
328
|
* returns a CLI structured error. The shared discriminant guarantees both
|
|
329
|
-
* paths feed `
|
|
329
|
+
* paths feed `resolveRecordedPath` the same inputs.
|
|
330
330
|
*
|
|
331
331
|
* @internal Exported for `executeMigrateShowCommand` to call.
|
|
332
332
|
*/
|
|
@@ -407,7 +407,7 @@ export function planSpacePath({
|
|
|
407
407
|
? space
|
|
408
408
|
: { ...space, headRef: { hash: targetHash, invariants: targetInvariants } };
|
|
409
409
|
|
|
410
|
-
const walked =
|
|
410
|
+
const walked = resolveRecordedPath({
|
|
411
411
|
aggregateTargetId: aggregate.targetId,
|
|
412
412
|
space: targetSpace,
|
|
413
413
|
currentMarker: liveMarker,
|
|
@@ -458,9 +458,9 @@ function buildAtHeadResolution(args: {
|
|
|
458
458
|
},
|
|
459
459
|
displayOps: [],
|
|
460
460
|
destinationContract: space.contract(),
|
|
461
|
-
strategy: '
|
|
461
|
+
strategy: 'declared-state',
|
|
462
462
|
migrationEdges: [
|
|
463
|
-
|
|
463
|
+
buildFabricatedMigrationEdge({
|
|
464
464
|
currentMarkerStorageHash: liveMarker?.storageHash,
|
|
465
465
|
destinationStorageHash: targetHash,
|
|
466
466
|
operationCount: 0,
|