@prisma-next/cli 0.4.1 → 0.4.3

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.
Files changed (170) hide show
  1. package/README.md +56 -26
  2. package/dist/agent-skill-mongo.md +63 -31
  3. package/dist/agent-skill-postgres.md +1 -1
  4. package/dist/cli-errors-By1iVE3z.mjs +34 -0
  5. package/dist/cli-errors-By1iVE3z.mjs.map +1 -0
  6. package/dist/cli-errors-DDeVsP2Y.d.mts +5 -0
  7. package/dist/cli.mjs +131 -15
  8. package/dist/cli.mjs.map +1 -1
  9. package/dist/{client-DGKrciLM.mjs → client-1JqqkiC7.mjs} +45 -20
  10. package/dist/client-1JqqkiC7.mjs.map +1 -0
  11. package/dist/commands/contract-emit.d.mts.map +1 -1
  12. package/dist/commands/contract-emit.mjs +7 -2
  13. package/dist/commands/contract-infer.d.mts.map +1 -1
  14. package/dist/commands/contract-infer.mjs +8 -2
  15. package/dist/commands/db-init.d.mts.map +1 -1
  16. package/dist/commands/db-init.mjs +11 -9
  17. package/dist/commands/db-init.mjs.map +1 -1
  18. package/dist/commands/db-schema.mjs +8 -5
  19. package/dist/commands/db-schema.mjs.map +1 -1
  20. package/dist/commands/db-sign.mjs +8 -7
  21. package/dist/commands/db-sign.mjs.map +1 -1
  22. package/dist/commands/db-update.mjs +10 -9
  23. package/dist/commands/db-update.mjs.map +1 -1
  24. package/dist/commands/db-verify.mjs +10 -9
  25. package/dist/commands/db-verify.mjs.map +1 -1
  26. package/dist/commands/migration-apply.d.mts +5 -2
  27. package/dist/commands/migration-apply.d.mts.map +1 -1
  28. package/dist/commands/migration-apply.mjs +57 -57
  29. package/dist/commands/migration-apply.mjs.map +1 -1
  30. package/dist/commands/migration-new.d.mts.map +1 -1
  31. package/dist/commands/migration-new.mjs +26 -32
  32. package/dist/commands/migration-new.mjs.map +1 -1
  33. package/dist/commands/migration-plan.d.mts +14 -5
  34. package/dist/commands/migration-plan.d.mts.map +1 -1
  35. package/dist/commands/migration-plan.mjs +45 -48
  36. package/dist/commands/migration-plan.mjs.map +1 -1
  37. package/dist/commands/migration-ref.d.mts +6 -4
  38. package/dist/commands/migration-ref.d.mts.map +1 -1
  39. package/dist/commands/migration-ref.mjs +31 -40
  40. package/dist/commands/migration-ref.mjs.map +1 -1
  41. package/dist/commands/migration-show.d.mts +13 -7
  42. package/dist/commands/migration-show.d.mts.map +1 -1
  43. package/dist/commands/migration-show.mjs +28 -29
  44. package/dist/commands/migration-show.mjs.map +1 -1
  45. package/dist/commands/migration-status.d.mts +23 -5
  46. package/dist/commands/migration-status.d.mts.map +1 -1
  47. package/dist/commands/migration-status.mjs +8 -3
  48. package/dist/{config-loader-_xQZsw0i.mjs → config-loader-ih8ViDb_.mjs} +2 -2
  49. package/dist/config-loader-ih8ViDb_.mjs.map +1 -0
  50. package/dist/config-loader.mjs +1 -1
  51. package/dist/contract-emit-DS5NzZh2.mjs +6 -0
  52. package/dist/contract-emit-RZBWzkop.mjs +329 -0
  53. package/dist/contract-emit-RZBWzkop.mjs.map +1 -0
  54. package/dist/contract-emit-rt_Nmdwq.mjs +150 -0
  55. package/dist/contract-emit-rt_Nmdwq.mjs.map +1 -0
  56. package/dist/{contract-enrichment-BV4KpbNW.mjs → contract-enrichment-4Ptgw3Pe.mjs} +1 -1
  57. package/dist/{contract-enrichment-BV4KpbNW.mjs.map → contract-enrichment-4Ptgw3Pe.mjs.map} +1 -1
  58. package/dist/{contract-infer-CUbiWGX0.mjs → contract-infer-Cf5J2wVg.mjs} +11 -19
  59. package/dist/contract-infer-Cf5J2wVg.mjs.map +1 -0
  60. package/dist/exports/control-api.d.mts +86 -21
  61. package/dist/exports/control-api.d.mts.map +1 -1
  62. package/dist/exports/control-api.mjs +7 -5
  63. package/dist/exports/index.mjs +8 -3
  64. package/dist/exports/index.mjs.map +1 -1
  65. package/dist/exports/init-output.d.mts +39 -0
  66. package/dist/exports/init-output.d.mts.map +1 -0
  67. package/dist/exports/init-output.mjs +3 -0
  68. package/dist/{framework-components-B__p--vT.mjs → framework-components-Bgcre3Z6.mjs} +2 -2
  69. package/dist/{framework-components-B__p--vT.mjs.map → framework-components-Bgcre3Z6.mjs.map} +1 -1
  70. package/dist/init-DAbQMxIR.mjs +2062 -0
  71. package/dist/init-DAbQMxIR.mjs.map +1 -0
  72. package/dist/{inspect-live-schema-wIYBTdL3.mjs → inspect-live-schema-LWtXfxm_.mjs} +9 -9
  73. package/dist/inspect-live-schema-LWtXfxm_.mjs.map +1 -0
  74. package/dist/migration-cli.d.mts +80 -0
  75. package/dist/migration-cli.d.mts.map +1 -0
  76. package/dist/migration-cli.mjs +408 -0
  77. package/dist/migration-cli.mjs.map +1 -0
  78. package/dist/{migration-command-scaffold-BC73xQSo.mjs → migration-command-scaffold-CU452v9h.mjs} +7 -7
  79. package/dist/{migration-command-scaffold-BC73xQSo.mjs.map → migration-command-scaffold-CU452v9h.mjs.map} +1 -1
  80. package/dist/{migration-status-CXBbScH5.mjs → migration-status-DoPrFIOQ.mjs} +119 -64
  81. package/dist/migration-status-DoPrFIOQ.mjs.map +1 -0
  82. package/dist/{migrations-DYRAjiVh.mjs → migrations-MEoKMiV5.mjs} +42 -21
  83. package/dist/migrations-MEoKMiV5.mjs.map +1 -0
  84. package/dist/output-BpcQrnnq.mjs +103 -0
  85. package/dist/output-BpcQrnnq.mjs.map +1 -0
  86. package/dist/{progress-adapter-Bwouy73-.mjs → progress-adapter-DgRGldpT.mjs} +1 -1
  87. package/dist/{progress-adapter-Bwouy73-.mjs.map → progress-adapter-DgRGldpT.mjs.map} +1 -1
  88. package/dist/quick-reference-mongo.md +34 -13
  89. package/dist/quick-reference-postgres.md +11 -9
  90. package/dist/{result-handler-CGohaH1o.mjs → result-handler-Ch6hVnOo.mjs} +36 -94
  91. package/dist/result-handler-Ch6hVnOo.mjs.map +1 -0
  92. package/dist/{terminal-ui-BuPXVRFY.mjs → terminal-ui-u2YgKghu.mjs} +76 -2
  93. package/dist/terminal-ui-u2YgKghu.mjs.map +1 -0
  94. package/dist/{verify-Cm2UFuZA.mjs → verify-BT9tgCOH.mjs} +2 -2
  95. package/dist/{verify-Cm2UFuZA.mjs.map → verify-BT9tgCOH.mjs.map} +1 -1
  96. package/package.json +27 -17
  97. package/src/cli.ts +32 -6
  98. package/src/commands/contract-emit.ts +67 -163
  99. package/src/commands/contract-infer.ts +7 -20
  100. package/src/commands/db-init.ts +1 -0
  101. package/src/commands/db-update.ts +1 -1
  102. package/src/commands/init/detect-pnpm-catalog.ts +141 -0
  103. package/src/commands/init/errors.ts +254 -0
  104. package/src/commands/init/exit-codes.ts +62 -0
  105. package/src/commands/init/hygiene-gitattributes.ts +97 -0
  106. package/src/commands/init/hygiene-gitignore.ts +48 -0
  107. package/src/commands/init/hygiene-package-scripts.ts +91 -0
  108. package/src/commands/init/index.ts +112 -7
  109. package/src/commands/init/init.ts +766 -144
  110. package/src/commands/init/inputs.ts +421 -0
  111. package/src/commands/init/output.ts +147 -0
  112. package/src/commands/init/probe-db.ts +308 -0
  113. package/src/commands/init/reinit-cleanup.ts +83 -0
  114. package/src/commands/init/templates/agent-skill-mongo.md +63 -31
  115. package/src/commands/init/templates/agent-skill-postgres.md +1 -1
  116. package/src/commands/init/templates/agent-skill.ts +25 -3
  117. package/src/commands/init/templates/code-templates.ts +125 -32
  118. package/src/commands/init/templates/env.ts +80 -0
  119. package/src/commands/init/templates/quick-reference-mongo.md +34 -13
  120. package/src/commands/init/templates/quick-reference-postgres.md +11 -9
  121. package/src/commands/init/templates/quick-reference.ts +42 -3
  122. package/src/commands/init/templates/tsconfig.ts +167 -5
  123. package/src/commands/inspect-live-schema.ts +10 -5
  124. package/src/commands/migration-apply.ts +86 -65
  125. package/src/commands/migration-new.ts +28 -34
  126. package/src/commands/migration-plan.ts +80 -56
  127. package/src/commands/migration-ref.ts +40 -54
  128. package/src/commands/migration-show.ts +53 -36
  129. package/src/commands/migration-status.ts +202 -71
  130. package/src/config-path-validation.ts +0 -1
  131. package/src/control-api/client.ts +21 -0
  132. package/src/control-api/operations/contract-emit.ts +198 -115
  133. package/src/control-api/operations/db-init.ts +10 -6
  134. package/src/control-api/operations/db-update.ts +10 -6
  135. package/src/control-api/operations/migration-apply.ts +30 -9
  136. package/src/control-api/types.ts +69 -7
  137. package/src/exports/control-api.ts +2 -1
  138. package/src/exports/init-output.ts +10 -0
  139. package/src/migration-cli.ts +577 -0
  140. package/src/utils/cli-errors.ts +50 -2
  141. package/src/utils/command-helpers.ts +48 -26
  142. package/src/utils/emit-queue.ts +26 -0
  143. package/src/utils/formatters/graph-migration-mapper.ts +7 -3
  144. package/src/utils/formatters/migrations.ts +62 -26
  145. package/src/utils/publish-contract-artifact-pair.ts +134 -0
  146. package/dist/cli-errors-CznZA5-d.mjs +0 -5
  147. package/dist/cli-errors-z37sV3eR.d.mts +0 -4
  148. package/dist/client-DGKrciLM.mjs.map +0 -1
  149. package/dist/config-loader-_xQZsw0i.mjs.map +0 -1
  150. package/dist/contract-emit-304WZtZJ.mjs +0 -4
  151. package/dist/contract-emit-DgeWdonT.mjs +0 -122
  152. package/dist/contract-emit-DgeWdonT.mjs.map +0 -1
  153. package/dist/contract-emit-mU1_B_m9.mjs +0 -195
  154. package/dist/contract-emit-mU1_B_m9.mjs.map +0 -1
  155. package/dist/contract-infer-CUbiWGX0.mjs.map +0 -1
  156. package/dist/extract-operation-statements-DWWFz1PK.mjs +0 -13
  157. package/dist/extract-operation-statements-DWWFz1PK.mjs.map +0 -1
  158. package/dist/extract-sql-ddl-7zn_AFS8.mjs +0 -26
  159. package/dist/extract-sql-ddl-7zn_AFS8.mjs.map +0 -1
  160. package/dist/init-DRquYpPa.mjs +0 -430
  161. package/dist/init-DRquYpPa.mjs.map +0 -1
  162. package/dist/inspect-live-schema-wIYBTdL3.mjs.map +0 -1
  163. package/dist/migration-status-CXBbScH5.mjs.map +0 -1
  164. package/dist/migrations-DYRAjiVh.mjs.map +0 -1
  165. package/dist/result-handler-CGohaH1o.mjs.map +0 -1
  166. package/dist/terminal-ui-BuPXVRFY.mjs.map +0 -1
  167. package/dist/validate-contract-deps-DZqv9m7H.mjs +0 -37
  168. package/dist/validate-contract-deps-DZqv9m7H.mjs.map +0 -1
  169. package/src/control-api/operations/extract-operation-statements.ts +0 -14
  170. package/src/control-api/operations/extract-sql-ddl.ts +0 -47
@@ -1,16 +1,16 @@
1
- import { t as loadConfig } from "./config-loader-_xQZsw0i.mjs";
2
- import { _ as errorUnexpected, m as errorRuntime } from "./cli-errors-CznZA5-d.mjs";
3
- import { t as createControlClient } from "./client-DGKrciLM.mjs";
4
- import { _ as formatStyledHeader, a as maskConnectionUrl, c as resolveMigrationPaths, d as setCommandExamples, i as loadAllBundles, m as parseGlobalFlags, n as addGlobalOptions, o as readContractEnvelope, p as toPathDecisionResult, t as handleResult, u as setCommandDescriptions } from "./result-handler-CGohaH1o.mjs";
5
- import { t as TerminalUI } from "./terminal-ui-BuPXVRFY.mjs";
1
+ import { t as loadConfig } from "./config-loader-ih8ViDb_.mjs";
2
+ import { _ as errorUnexpected, m as errorRuntime, v as mapMigrationToolsError } from "./cli-errors-By1iVE3z.mjs";
3
+ import { t as TerminalUI } from "./terminal-ui-u2YgKghu.mjs";
4
+ import { a as loadMigrationPackages, d as setCommandDescriptions, f as setCommandExamples, g as parseGlobalFlags, h as toStructuralEdge, l as resolveMigrationPaths, m as toPathDecisionResult, n as addGlobalOptions, o as maskConnectionUrl, r as collectDeclaredInvariants, s as readContractEnvelope, t as handleResult, y as formatStyledHeader } from "./result-handler-Ch6hVnOo.mjs";
5
+ import { t as createControlClient } from "./client-1JqqkiC7.mjs";
6
6
  import { Command } from "commander";
7
- import { notOk, ok } from "@prisma-next/utils/result";
8
7
  import { ifDefined } from "@prisma-next/utils/defined";
9
- import { EMPTY_CONTRACT_HASH } from "@prisma-next/migration-tools/constants";
10
- import { findPath, findPathWithDecision, findReachableLeaves } from "@prisma-next/migration-tools/dag";
8
+ import { notOk, ok } from "@prisma-next/utils/result";
9
+ import { findPath, findPathWithDecision, findReachableLeaves } from "@prisma-next/migration-tools/migration-graph";
11
10
  import { bold, cyan, dim, magenta, yellow } from "colorette";
11
+ import { EMPTY_CONTRACT_HASH } from "@prisma-next/migration-tools/constants";
12
+ import { MigrationToolsError, errorNoInvariantPath, errorUnknownInvariant } from "@prisma-next/migration-tools/errors";
12
13
  import { readRefs, resolveRef } from "@prisma-next/migration-tools/refs";
13
- import { MigrationToolsError } from "@prisma-next/migration-tools/types";
14
14
  import dagre from "@dagrejs/dagre";
15
15
 
16
16
  //#region src/utils/formatters/graph-types.ts
@@ -100,7 +100,8 @@ function migrationGraphToRenderInput(input) {
100
100
  for (const [, entries] of graph.forwardChain) for (const entry of entries) {
101
101
  const status = statusByDirName.get(entry.dirName);
102
102
  const icon = status ? STATUS_ICON[status] : "";
103
- const label = `${entry.dirName}${icon}`;
103
+ const invariantsSuffix = entry.invariants.length > 0 ? ` provides [${entry.invariants.map((id) => JSON.stringify(id)).join(", ")}]` : "";
104
+ const label = `${entry.dirName}${icon}${invariantsSuffix}`;
104
105
  edgeList.push({
105
106
  from: toShortId(entry.from),
106
107
  to: toShortId(entry.to),
@@ -1194,7 +1195,7 @@ function buildMigrationEntries(chain, packages, mode, markerHash, edgeStatuses)
1194
1195
  dirName: migration.dirName,
1195
1196
  from: migration.from,
1196
1197
  to: migration.to,
1197
- migrationId: migration.migrationId,
1198
+ migrationHash: migration.migrationHash,
1198
1199
  operationCount: ops.length,
1199
1200
  operationSummary: summary,
1200
1201
  hasDestructive,
@@ -1241,40 +1242,33 @@ function determineLimit(opts) {
1241
1242
  }
1242
1243
  async function executeMigrationStatusCommand(options, flags, ui) {
1243
1244
  const config = await loadConfig(options.config);
1244
- const { configPath, migrationsDir, migrationsRelative, refsPath } = resolveMigrationPaths(options.config, config);
1245
+ const { configPath, migrationsDir, migrationsRelative, refsDir } = resolveMigrationPaths(options.config, config);
1245
1246
  const dbConnection = options.db ?? config.db?.connection;
1246
1247
  const hasDriver = !!config.driver;
1247
1248
  let activeRefName;
1248
1249
  let activeRefHash;
1250
+ let activeRefEntry;
1249
1251
  let allRefs = {};
1250
1252
  try {
1251
- allRefs = await readRefs(refsPath);
1253
+ allRefs = await readRefs(refsDir);
1252
1254
  } catch (error) {
1253
- if (MigrationToolsError.is(error)) return notOk(errorRuntime(error.message, {
1254
- why: error.why,
1255
- fix: error.fix,
1256
- meta: { code: error.code }
1257
- }));
1255
+ if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
1258
1256
  throw error;
1259
1257
  }
1260
1258
  if (options.ref) {
1261
1259
  activeRefName = options.ref;
1262
- const refHash = allRefs[activeRefName];
1263
- if (refHash) activeRefHash = refHash;
1264
- else try {
1265
- activeRefHash = resolveRef(allRefs, activeRefName);
1260
+ try {
1261
+ activeRefEntry = resolveRef(allRefs, activeRefName);
1262
+ activeRefHash = activeRefEntry.hash;
1266
1263
  } catch (error) {
1267
- if (MigrationToolsError.is(error)) return notOk(errorRuntime(error.message, {
1268
- why: error.why,
1269
- fix: error.fix,
1270
- meta: { code: error.code }
1271
- }));
1264
+ if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
1272
1265
  throw error;
1273
1266
  }
1274
1267
  }
1275
- const statusRefs = Object.entries(allRefs).map(([name, hash]) => ({
1268
+ const requiredInvariants = [...activeRefEntry?.invariants ?? []].sort();
1269
+ const statusRefs = Object.entries(allRefs).map(([name, entry]) => ({
1276
1270
  name,
1277
- hash,
1271
+ hash: entry.hash,
1278
1272
  active: name === activeRefName
1279
1273
  }));
1280
1274
  if (!flags.json && !flags.quiet) {
@@ -1293,6 +1287,10 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1293
1287
  label: "ref",
1294
1288
  value: activeRefName
1295
1289
  });
1290
+ if (activeRefEntry && activeRefEntry.invariants.length > 0) details.push({
1291
+ label: "required",
1292
+ value: formatInvariantList(activeRefEntry.invariants)
1293
+ });
1296
1294
  const header = formatStyledHeader({
1297
1295
  command: "migration status",
1298
1296
  description: "Show migration history and applied status",
@@ -1316,13 +1314,9 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1316
1314
  let bundles;
1317
1315
  let graph;
1318
1316
  try {
1319
- ({bundles, graph} = await loadAllBundles(migrationsDir));
1317
+ ({bundles, graph} = await loadMigrationPackages(migrationsDir));
1320
1318
  } catch (error) {
1321
- if (MigrationToolsError.is(error)) return notOk(errorRuntime(error.message, {
1322
- why: error.why,
1323
- fix: error.fix,
1324
- meta: { code: error.code }
1325
- }));
1319
+ if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
1326
1320
  return notOk(errorUnexpected(error instanceof Error ? error.message : String(error), { why: `Failed to read migrations directory: ${error instanceof Error ? error.message : String(error)}` }));
1327
1321
  }
1328
1322
  if (bundles.length === 0) {
@@ -1339,7 +1333,8 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1339
1333
  targetHash: EMPTY_CONTRACT_HASH,
1340
1334
  contractHash,
1341
1335
  summary: "No migrations found",
1342
- diagnostics
1336
+ diagnostics,
1337
+ requiredInvariants
1343
1338
  });
1344
1339
  }
1345
1340
  let targetHash;
@@ -1356,6 +1351,7 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1356
1351
  });
1357
1352
  }
1358
1353
  let markerHash;
1354
+ let markerInvariants = [];
1359
1355
  let mode = "offline";
1360
1356
  if (dbConnection && hasDriver) {
1361
1357
  const client = createControlClient({
@@ -1367,7 +1363,9 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1367
1363
  });
1368
1364
  try {
1369
1365
  await client.connect(dbConnection);
1370
- markerHash = (await client.readMarker())?.storageHash;
1366
+ const marker = await client.readMarker();
1367
+ markerHash = marker?.storageHash;
1368
+ markerInvariants = marker?.invariants ?? [];
1371
1369
  mode = "online";
1372
1370
  } catch {
1373
1371
  if (!flags.json && !flags.quiet) ui.warn("Could not connect to database — showing offline status");
@@ -1375,6 +1373,17 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1375
1373
  await client.close();
1376
1374
  }
1377
1375
  }
1376
+ if (activeRefEntry && activeRefEntry.invariants.length > 0) {
1377
+ const declared = collectDeclaredInvariants(graph);
1378
+ const known = new Set(declared);
1379
+ if (mode === "online") for (const id of markerInvariants) known.add(id);
1380
+ const unknown = activeRefEntry.invariants.filter((id) => !known.has(id));
1381
+ if (unknown.length > 0) return notOk(mapMigrationToolsError(errorUnknownInvariant({
1382
+ ...ifDefined("refName", activeRefName),
1383
+ unknown,
1384
+ declared: [...declared].sort()
1385
+ })));
1386
+ }
1378
1387
  if (mode === "online" && markerHash !== void 0 && !graph.nodes.has(markerHash) && markerHash !== contractHash) {
1379
1388
  const hints = [];
1380
1389
  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");
@@ -1394,6 +1403,7 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1394
1403
  summary: `${bundles.length} migration(s) on disk`,
1395
1404
  diagnostics,
1396
1405
  markerHash,
1406
+ requiredInvariants,
1397
1407
  ...statusRefs.length > 0 ? { refs: statusRefs } : {}
1398
1408
  });
1399
1409
  }
@@ -1418,6 +1428,7 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1418
1428
  summary: `${bundles.length} migration(s) on disk`,
1419
1429
  diagnostics,
1420
1430
  ...ifDefined("markerHash", markerHash),
1431
+ requiredInvariants,
1421
1432
  ...statusRefs.length > 0 ? { refs: statusRefs } : {},
1422
1433
  graph,
1423
1434
  bundles,
@@ -1432,35 +1443,68 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1432
1443
  const entries = buildMigrationEntries(chain, bundles, mode, markerHash, edgeStatuses);
1433
1444
  const pendingCount = edgeStatuses.filter((e) => e.status === "pending").length;
1434
1445
  const appliedCount = edgeStatuses.filter((e) => e.status === "applied").length;
1446
+ let appliedInvariants;
1447
+ let missingInvariants;
1448
+ let effectiveRequired = /* @__PURE__ */ new Set();
1449
+ if (mode === "online") {
1450
+ const markerSet = new Set(markerInvariants);
1451
+ effectiveRequired = new Set(requiredInvariants.filter((id) => !markerSet.has(id)));
1452
+ appliedInvariants = requiredInvariants.filter((id) => markerSet.has(id));
1453
+ missingInvariants = [...effectiveRequired].sort();
1454
+ }
1455
+ const hasInvariantWork = effectiveRequired.size > 0;
1456
+ const missingList = [...effectiveRequired].sort().join(", ");
1435
1457
  let summary;
1436
1458
  if (mode === "online") if (markerHash !== void 0 && !graph.nodes.has(markerHash) && markerHash === contractHash) summary = `${bundles.length} migration(s) on disk`;
1437
- else if (activeRefHash && markerHash !== void 0) summary = summarizeRefDistance(graph, markerHash, activeRefHash, activeRefName);
1438
- else if (pendingCount === 0) summary = `Database is up to date (${appliedCount} migration${appliedCount !== 1 ? "s" : ""} applied)`;
1459
+ else if (activeRefHash && markerHash !== void 0) {
1460
+ const distance = summarizeRefDistance(graph, markerHash, activeRefHash, activeRefName);
1461
+ summary = hasInvariantWork ? `${distance} — missing invariant(s): ${missingList}` : distance;
1462
+ } else if (pendingCount === 0 && !hasInvariantWork) summary = `Database is up to date (${appliedCount} migration${appliedCount !== 1 ? "s" : ""} applied)`;
1463
+ else if (pendingCount === 0 && hasInvariantWork) summary = `Missing invariant(s): ${missingList} — run 'prisma-next migration apply --ref ${activeRefName ?? "<ref>"}' to apply`;
1439
1464
  else if (markerHash === void 0) summary = `${pendingCount} pending migration(s) — database has no marker`;
1440
1465
  else summary = `${pendingCount} pending migration(s) — run 'prisma-next migration apply' to apply`;
1441
1466
  else summary = `${entries.length} migration(s) on disk`;
1442
- if (mode === "online") if (markerHash !== void 0 && !graph.nodes.has(markerHash) && markerHash === contractHash) diagnostics.push({
1443
- code: "MIGRATION.MARKER_NOT_IN_HISTORY",
1444
- severity: "warn",
1445
- message: "Database matches the current contract but was updated directly (not via migration apply)",
1446
- hints: ["Run 'prisma-next migration plan' to plan a migration to your current contract"]
1447
- });
1448
- else if (pendingCount > 0) diagnostics.push({
1449
- code: "MIGRATION.DATABASE_BEHIND",
1450
- severity: "info",
1451
- message: `${pendingCount} migration(s) pending`,
1452
- hints: ["Run 'prisma-next migration apply' to apply pending migrations"]
1453
- });
1454
- else diagnostics.push({
1455
- code: "MIGRATION.UP_TO_DATE",
1456
- severity: "info",
1457
- message: "Database is up to date",
1458
- hints: []
1459
- });
1460
1467
  let pathDecision;
1461
- if (mode === "online" && markerHash !== void 0) {
1462
- const decision = findPathWithDecision(graph, markerHash, targetHash, activeRefName);
1463
- if (decision) pathDecision = toPathDecisionResult(decision);
1468
+ let routingUnreachable = false;
1469
+ if (mode === "online") {
1470
+ const outcome = findPathWithDecision(graph, markerHash ?? EMPTY_CONTRACT_HASH, targetHash, {
1471
+ ...ifDefined("refName", activeRefName),
1472
+ required: effectiveRequired
1473
+ });
1474
+ if (outcome.kind === "ok") pathDecision = toPathDecisionResult(outcome.decision);
1475
+ else if (outcome.kind === "unsatisfiable") return notOk(mapMigrationToolsError(errorNoInvariantPath({
1476
+ ...ifDefined("refName", activeRefName),
1477
+ required: [...effectiveRequired].sort(),
1478
+ missing: outcome.missing,
1479
+ structuralPath: outcome.structuralPath.map(toStructuralEdge)
1480
+ })));
1481
+ else routingUnreachable = true;
1482
+ }
1483
+ if (mode === "online") {
1484
+ if (markerHash !== void 0 && !graph.nodes.has(markerHash) && markerHash === contractHash) diagnostics.push({
1485
+ code: "MIGRATION.MARKER_NOT_IN_HISTORY",
1486
+ severity: "warn",
1487
+ message: "Database matches the current contract but was updated directly (not via migration apply)",
1488
+ hints: ["Run 'prisma-next migration plan' to plan a migration to your current contract"]
1489
+ });
1490
+ else if (pendingCount > 0) diagnostics.push({
1491
+ code: "MIGRATION.DATABASE_BEHIND",
1492
+ severity: "info",
1493
+ message: `${pendingCount} migration(s) pending`,
1494
+ hints: ["Run 'prisma-next migration apply' to apply pending migrations"]
1495
+ });
1496
+ else if (hasInvariantWork) diagnostics.push({
1497
+ code: "MIGRATION.INVARIANTS_PENDING",
1498
+ severity: "info",
1499
+ message: `Missing required invariant(s): ${missingList}`,
1500
+ hints: [`Run 'prisma-next migration apply --ref ${activeRefName ?? "<ref>"}' to apply a path that covers the required invariants`]
1501
+ });
1502
+ else if (!routingUnreachable) diagnostics.push({
1503
+ code: "MIGRATION.UP_TO_DATE",
1504
+ severity: "info",
1505
+ message: "Database is up to date",
1506
+ hints: []
1507
+ });
1464
1508
  }
1465
1509
  return ok({
1466
1510
  ok: true,
@@ -1471,6 +1515,9 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1471
1515
  summary,
1472
1516
  diagnostics,
1473
1517
  ...ifDefined("markerHash", markerHash),
1518
+ requiredInvariants,
1519
+ ...ifDefined("appliedInvariants", appliedInvariants),
1520
+ ...ifDefined("missingInvariants", missingInvariants),
1474
1521
  ...statusRefs.length > 0 ? { refs: statusRefs } : {},
1475
1522
  ...ifDefined("pathDecision", pathDecision),
1476
1523
  graph,
@@ -1492,7 +1539,7 @@ function createMigrationStatusCommand() {
1492
1539
  });
1493
1540
  const exitCode = handleResult(await executeMigrationStatusCommand(options, flags, ui), flags, ui, (statusResult) => {
1494
1541
  if (flags.json) {
1495
- const { graph: _g, bundles: _b, edgeStatuses: _es, activeRefHash: _arh, activeRefName: _arn, diverged: _d, ...jsonResult } = statusResult;
1542
+ const { graph: _graph, bundles: _bundles, edgeStatuses: _edgeStatuses, activeRefHash: _activeRefHash, activeRefName: _activeRefName, diverged: _diverged, ...jsonResult } = statusResult;
1496
1543
  ui.output(JSON.stringify(jsonResult, null, 2));
1497
1544
  } else if (!flags.quiet) {
1498
1545
  const colorize = flags.color !== false;
@@ -1542,10 +1589,15 @@ function formatStatusSummary(result, colorize) {
1542
1589
  const hasUnknown = result.migrations.some((e) => e.status === "unknown");
1543
1590
  const pendingCount = result.migrations.filter((e) => e.status === "pending").length;
1544
1591
  const hasWarnings = result.diagnostics?.some((d) => d.severity === "warn") ?? false;
1592
+ const hasInvariantPending = result.diagnostics?.some((d) => d.code === "MIGRATION.INVARIANTS_PENDING") ?? false;
1545
1593
  if (result.mode === "online") if (hasUnknown || hasWarnings) lines.push(`${c(yellow, "⚠")} ${result.summary}`);
1546
- else if (pendingCount === 0) lines.push(`${c(cyan, "✔")} ${result.summary}`);
1594
+ else if (pendingCount === 0 && !hasInvariantPending) lines.push(`${c(cyan, "✔")} ${result.summary}`);
1547
1595
  else lines.push(`${c(yellow, "⧗")} ${result.summary}`);
1548
1596
  else lines.push(result.summary);
1597
+ if (result.requiredInvariants.length > 0) if (result.appliedInvariants !== void 0 && result.missingInvariants !== void 0) {
1598
+ lines.push(`${c(dim, "applied ")}${formatInvariantList(result.appliedInvariants)}`);
1599
+ lines.push(`${c(dim, "missing ")}${formatInvariantList(result.missingInvariants)}`);
1600
+ } else lines.push(`${c(dim, "applied ")}(unknown — connect a database to evaluate)`);
1549
1601
  const warnings = result.diagnostics?.filter((d) => d.severity === "warn") ?? [];
1550
1602
  for (const diag of warnings) {
1551
1603
  lines.push(`${c(yellow, "⚠")} ${diag.message}`);
@@ -1553,6 +1605,9 @@ function formatStatusSummary(result, colorize) {
1553
1605
  }
1554
1606
  return lines.join("\n");
1555
1607
  }
1608
+ function formatInvariantList(ids) {
1609
+ return ids.length === 0 ? "(none)" : ids.join(", ");
1610
+ }
1556
1611
  function summarizeRefDistance(graph, markerHash, refHash, refName) {
1557
1612
  if (markerHash === refHash) return `At ref "${refName}" target`;
1558
1613
  const pathToRef = findPath(graph, markerHash, refHash);
@@ -1563,5 +1618,5 @@ function summarizeRefDistance(graph, markerHash, refHash, refName) {
1563
1618
  }
1564
1619
 
1565
1620
  //#endregion
1566
- export { deriveEdgeStatuses as n, createMigrationStatusCommand as t };
1567
- //# sourceMappingURL=migration-status-CXBbScH5.mjs.map
1621
+ export { deriveEdgeStatuses as n, formatStatusSummary as r, createMigrationStatusCommand as t };
1622
+ //# sourceMappingURL=migration-status-DoPrFIOQ.mjs.map