@prisma-next/cli 0.11.0-dev.59 → 0.11.0-dev.60

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 (128) hide show
  1. package/dist/cli.mjs +9 -9
  2. package/dist/{client-Ls2SAhrZ.mjs → client-CD3om3R0.mjs} +7 -7
  3. package/dist/{client-Ls2SAhrZ.mjs.map → client-CD3om3R0.mjs.map} +1 -1
  4. package/dist/{command-helpers-DTpEJCgI.mjs → command-helpers-CoceqqMl.mjs} +221 -42
  5. package/dist/command-helpers-CoceqqMl.mjs.map +1 -0
  6. package/dist/commands/contract-emit.mjs +1 -1
  7. package/dist/commands/contract-infer.mjs +1 -1
  8. package/dist/commands/db-init.mjs +5 -6
  9. package/dist/commands/db-init.mjs.map +1 -1
  10. package/dist/commands/db-schema.mjs +3 -3
  11. package/dist/commands/db-sign.d.mts.map +1 -1
  12. package/dist/commands/db-sign.mjs +12 -9
  13. package/dist/commands/db-sign.mjs.map +1 -1
  14. package/dist/commands/db-update.d.mts.map +1 -1
  15. package/dist/commands/db-update.mjs +12 -10
  16. package/dist/commands/db-update.mjs.map +1 -1
  17. package/dist/commands/db-verify.mjs +1 -1
  18. package/dist/commands/migrate.d.mts +1 -1
  19. package/dist/commands/migrate.mjs +8 -9
  20. package/dist/commands/migrate.mjs.map +1 -1
  21. package/dist/commands/migration-check.mjs +1 -1
  22. package/dist/commands/migration-graph.d.mts +11 -2
  23. package/dist/commands/migration-graph.d.mts.map +1 -1
  24. package/dist/commands/migration-graph.mjs +15 -29
  25. package/dist/commands/migration-graph.mjs.map +1 -1
  26. package/dist/commands/migration-list.d.mts +59 -48
  27. package/dist/commands/migration-list.d.mts.map +1 -1
  28. package/dist/commands/migration-list.mjs +2 -2
  29. package/dist/commands/migration-log.d.mts +10 -1
  30. package/dist/commands/migration-log.d.mts.map +1 -1
  31. package/dist/commands/migration-log.mjs +10 -14
  32. package/dist/commands/migration-log.mjs.map +1 -1
  33. package/dist/commands/migration-new.mjs +5 -6
  34. package/dist/commands/migration-new.mjs.map +1 -1
  35. package/dist/commands/migration-plan.d.mts +1 -1
  36. package/dist/commands/migration-plan.mjs +1 -1
  37. package/dist/commands/migration-show.d.mts +1 -1
  38. package/dist/commands/migration-show.mjs +3 -4
  39. package/dist/commands/migration-show.mjs.map +1 -1
  40. package/dist/commands/migration-status.d.mts +1 -1
  41. package/dist/commands/migration-status.d.mts.map +1 -1
  42. package/dist/commands/migration-status.mjs +7 -28
  43. package/dist/commands/migration-status.mjs.map +1 -1
  44. package/dist/commands/ref.d.mts +1 -1
  45. package/dist/commands/ref.d.mts.map +1 -1
  46. package/dist/commands/ref.mjs +10 -7
  47. package/dist/commands/ref.mjs.map +1 -1
  48. package/dist/{contract-at-errors-B98TC1wK.mjs → contract-at-errors-Bhf2jnkp.mjs} +2 -2
  49. package/dist/{contract-at-errors-B98TC1wK.mjs.map → contract-at-errors-Bhf2jnkp.mjs.map} +1 -1
  50. package/dist/{contract-emit-CS3vF-w9.mjs → contract-emit-C47r1loe.mjs} +4 -5
  51. package/dist/{contract-emit-CS3vF-w9.mjs.map → contract-emit-C47r1loe.mjs.map} +1 -1
  52. package/dist/{contract-emit-BWLCn2PH.mjs → contract-emit-DxEfEc-M.mjs} +16 -5
  53. package/dist/{contract-emit-BWLCn2PH.mjs.map → contract-emit-DxEfEc-M.mjs.map} +1 -1
  54. package/dist/{contract-enrichment-XmUPhmsS.mjs → contract-enrichment-a0V5Y_mL.mjs} +1 -1
  55. package/dist/{contract-enrichment-XmUPhmsS.mjs.map → contract-enrichment-a0V5Y_mL.mjs.map} +1 -1
  56. package/dist/{contract-infer-BtefFYF-.mjs → contract-infer-B5EhTqdR.mjs} +3 -3
  57. package/dist/{contract-infer-BtefFYF-.mjs.map → contract-infer-B5EhTqdR.mjs.map} +1 -1
  58. package/dist/{contract-space-aggregate-loader-DX_1n2SA.mjs → contract-space-aggregate-loader-lafgkTwG.mjs} +81 -4
  59. package/dist/contract-space-aggregate-loader-lafgkTwG.mjs.map +1 -0
  60. package/dist/{db-verify-aHw2nzH2.mjs → db-verify-B7fbkGnp.mjs} +5 -6
  61. package/dist/{db-verify-aHw2nzH2.mjs.map → db-verify-B7fbkGnp.mjs.map} +1 -1
  62. package/dist/exports/control-api.d.mts +1 -1
  63. package/dist/exports/control-api.mjs +3 -3
  64. package/dist/exports/index.mjs +1 -1
  65. package/dist/exports/init-output.mjs +1 -1
  66. package/dist/{extension-pack-inputs-BiY86HbQ.mjs → extension-pack-inputs-IDvjRCi3.mjs} +1 -1
  67. package/dist/{extension-pack-inputs-BiY86HbQ.mjs.map → extension-pack-inputs-IDvjRCi3.mjs.map} +1 -1
  68. package/dist/{framework-components-BwuEBcyk.mjs → framework-components-R_O3y5IW.mjs} +2 -2
  69. package/dist/{framework-components-BwuEBcyk.mjs.map → framework-components-R_O3y5IW.mjs.map} +1 -1
  70. package/dist/global-flags-2SPgqWma.d.mts +34 -0
  71. package/dist/global-flags-2SPgqWma.d.mts.map +1 -0
  72. package/dist/{graph-render-eJDcLWny.mjs → graph-render-rFAqZujX.mjs} +2 -2
  73. package/dist/{graph-render-eJDcLWny.mjs.map → graph-render-rFAqZujX.mjs.map} +1 -1
  74. package/dist/{init-DOE4Q9YK.mjs → init-BQNpPozW.mjs} +4 -5
  75. package/dist/{init-DOE4Q9YK.mjs.map → init-BQNpPozW.mjs.map} +1 -1
  76. package/dist/{inspect-live-schema-IS8jWaJy.mjs → inspect-live-schema-CQJnuPgD.mjs} +4 -5
  77. package/dist/{inspect-live-schema-IS8jWaJy.mjs.map → inspect-live-schema-CQJnuPgD.mjs.map} +1 -1
  78. package/dist/{migration-check-BFdael8w.mjs → migration-check-CKfQlAWR.mjs} +5 -5
  79. package/dist/{migration-check-BFdael8w.mjs.map → migration-check-CKfQlAWR.mjs.map} +1 -1
  80. package/dist/{migration-command-scaffold-DojkenVv.mjs → migration-command-scaffold-CE931d1-.mjs} +4 -5
  81. package/dist/{migration-command-scaffold-DojkenVv.mjs.map → migration-command-scaffold-CE931d1-.mjs.map} +1 -1
  82. package/dist/{migration-list-hj86sCtZ.mjs → migration-list-A3bJ4j5e.mjs} +181 -47
  83. package/dist/migration-list-A3bJ4j5e.mjs.map +1 -0
  84. package/dist/{migration-plan-Bt6wxUIv.mjs → migration-plan-ZZm8C0s-.mjs} +8 -9
  85. package/dist/{migration-plan-Bt6wxUIv.mjs.map → migration-plan-ZZm8C0s-.mjs.map} +1 -1
  86. package/dist/{migration-types-D2FW63pr.d.mts → migration-types-q64xAI_J.d.mts} +1 -1
  87. package/dist/{migration-types-D2FW63pr.d.mts.map → migration-types-q64xAI_J.d.mts.map} +1 -1
  88. package/dist/{migrations-CVLh0Kv4.mjs → migrations-CjO1DsYe.mjs} +2 -2
  89. package/dist/{migrations-CVLh0Kv4.mjs.map → migrations-CjO1DsYe.mjs.map} +1 -1
  90. package/dist/{output-CF_hqzI-.mjs → output-DEg3SSnJ.mjs} +1 -1
  91. package/dist/{output-CF_hqzI-.mjs.map → output-DEg3SSnJ.mjs.map} +1 -1
  92. package/dist/{progress-adapter-xASh41wr.mjs → progress-adapter-C644QK8l.mjs} +1 -1
  93. package/dist/{progress-adapter-xASh41wr.mjs.map → progress-adapter-C644QK8l.mjs.map} +1 -1
  94. package/dist/{ref-advancement-DRh5Nquq.mjs → ref-advancement-DUZqsue6.mjs} +1 -1
  95. package/dist/{ref-advancement-DRh5Nquq.mjs.map → ref-advancement-DUZqsue6.mjs.map} +1 -1
  96. package/dist/terminal-ui-sLZt2cxc.d.mts +133 -0
  97. package/dist/terminal-ui-sLZt2cxc.d.mts.map +1 -0
  98. package/dist/{types-BuatV9YW.d.mts → types-DK-ge7eR.d.mts} +1 -1
  99. package/dist/{types-BuatV9YW.d.mts.map → types-DK-ge7eR.d.mts.map} +1 -1
  100. package/dist/{verify-BiWm4XwD.mjs → verify-vl983Ed-.mjs} +2 -2
  101. package/dist/{verify-BiWm4XwD.mjs.map → verify-vl983Ed-.mjs.map} +1 -1
  102. package/package.json +18 -18
  103. package/src/commands/db-sign.ts +9 -5
  104. package/src/commands/db-update.ts +9 -8
  105. package/src/commands/migration-graph.ts +15 -41
  106. package/src/commands/migration-list.ts +126 -68
  107. package/src/commands/migration-log.ts +8 -14
  108. package/src/commands/migration-status.ts +4 -30
  109. package/src/commands/ref.ts +9 -4
  110. package/src/control-api/types.ts +3 -3
  111. package/src/utils/command-helpers.ts +0 -24
  112. package/src/utils/contract-space-aggregate-loader.ts +116 -1
  113. package/src/utils/formatters/migration-list-data-column.ts +2 -2
  114. package/src/utils/formatters/migration-list-graph-layout.ts +2 -2
  115. package/src/utils/formatters/migration-list-graph-render.ts +2 -5
  116. package/src/utils/formatters/migration-list-graph-topology.ts +158 -0
  117. package/src/utils/formatters/migration-list-render.ts +9 -12
  118. package/src/utils/formatters/migration-list-types.ts +21 -0
  119. package/dist/cli-errors-DQY629C7.mjs +0 -220
  120. package/dist/cli-errors-DQY629C7.mjs.map +0 -1
  121. package/dist/command-helpers-DTpEJCgI.mjs.map +0 -1
  122. package/dist/contract-space-aggregate-loader-DX_1n2SA.mjs.map +0 -1
  123. package/dist/global-flags-Bo6nCRUS.d.mts +0 -15
  124. package/dist/global-flags-Bo6nCRUS.d.mts.map +0 -1
  125. package/dist/glyph-mode-VIjULGFF.d.mts +0 -5
  126. package/dist/glyph-mode-VIjULGFF.d.mts.map +0 -1
  127. package/dist/migration-list-hj86sCtZ.mjs.map +0 -1
  128. package/dist/rolldown-runtime-twds-ZHy.mjs +0 -14
@@ -3,10 +3,7 @@ import type { ControlTargetDescriptor } from '@prisma-next/framework-components/
3
3
  import { hasMigrations } from '@prisma-next/framework-components/control';
4
4
  import type { NoInvariantPathStructuralEdge } from '@prisma-next/migration-tools/errors';
5
5
  import type { MigrationEdge, MigrationGraph } from '@prisma-next/migration-tools/graph';
6
- import { readMigrationsDir } from '@prisma-next/migration-tools/io';
7
6
  import type { PathDecision } from '@prisma-next/migration-tools/migration-graph';
8
- import { reconstructGraph } from '@prisma-next/migration-tools/migration-graph';
9
- import type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';
10
7
  import { APP_SPACE_ID, spaceMigrationDirectory } from '@prisma-next/migration-tools/spaces';
11
8
  import { ifDefined } from '@prisma-next/utils/defined';
12
9
  import type { Command } from 'commander';
@@ -231,27 +228,6 @@ export function getTargetMigrations(target: ControlTargetDescriptor<string, stri
231
228
  return hasMigrations(target) ? target.migrations : undefined;
232
229
  }
233
230
 
234
- /**
235
- * Reads the migrations directory and builds the migration graph from all
236
- * packages. Throws on I/O or graph errors — callers handle error mapping.
237
- *
238
- * Every on-disk package is content-addressed (`migrationHash` is always a
239
- * string); there is no draft state to filter out.
240
- */
241
- export async function loadMigrationPackages(migrationsDir: string): Promise<{
242
- bundles: readonly OnDiskMigrationPackage[];
243
- graph: MigrationGraph;
244
- }> {
245
- // `readMigrationsDir` is tolerant: it retains hash-/invariant-mismatched
246
- // packages and omits unparseable ones, reporting both via `problems`.
247
- // This helper preserves its historical `{ bundles, graph }` contract by
248
- // exposing the retained packages; callers that need to surface load
249
- // problems read them from the tolerant primitive directly.
250
- const { packages } = await readMigrationsDir(migrationsDir);
251
- const graph = reconstructGraph(packages);
252
- return { bundles: packages, graph };
253
- }
254
-
255
231
  /**
256
232
  * The subset of the emitted contract.json that the framework layer can
257
233
  * safely type. The emitter adds these fields on top of the family-specific
@@ -1,5 +1,8 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import type { PrismaNextConfig } from '@prisma-next/config/config-types';
1
3
  import type { Contract } from '@prisma-next/contract/types';
2
4
  import type { ControlExtensionDescriptor } from '@prisma-next/framework-components/control';
5
+ import { createControlStack } from '@prisma-next/framework-components/control';
3
6
  import type {
4
7
  ContractSpaceAggregate,
5
8
  DeclaredExtensionEntry,
@@ -7,8 +10,12 @@ import type {
7
10
  IntegrityViolation,
8
11
  } from '@prisma-next/migration-tools/aggregate';
9
12
  import { loadContractSpaceAggregate } from '@prisma-next/migration-tools/aggregate';
13
+ import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
14
+ import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
15
+ import { blindCast } from '@prisma-next/utils/casts';
10
16
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
11
- import { CliStructuredError } from './cli-errors';
17
+ import { CliStructuredError, errorUnexpected, mapMigrationToolsError } from './cli-errors';
18
+ import { readContractEnvelope, resolveContractPath } from './command-helpers';
12
19
  import { toDeclaredExtensionsFromRaw } from './extension-pack-inputs';
13
20
 
14
21
  const CONTRACT_SPACES_DOCS_URL = 'https://pris.ly/contract-spaces';
@@ -279,3 +286,111 @@ export async function buildContractSpaceAggregate<
279
286
  }
280
287
  return ok(loaded.value);
281
288
  }
289
+
290
+ /**
291
+ * Build a minimal app {@link Contract} carrying only the project's
292
+ * contract-identity (`storage.storageHash` + `target` / `targetFamily`)
293
+ * when the real `contract.json` is absent or undeserializable.
294
+ *
295
+ * `loadContractSpaceAggregate` requires an `appContract` to synthesise the
296
+ * app space's head ref from its storage hash. Read commands query only that
297
+ * hash and the target — never `models` — so an empty-`models` stand-in is
298
+ * sufficient for them. It is *not* a valid contract for any consumer that
299
+ * reads schema, which is why this is confined to the read-aggregate path.
300
+ */
301
+ export function appContractStandInFromIdentity(args: {
302
+ readonly contractHash: string;
303
+ readonly targetId: string;
304
+ readonly targetFamily: string;
305
+ }): Contract {
306
+ return blindCast<
307
+ Contract,
308
+ 'read-aggregate consumers query only storage.storageHash and target; empty models stand in for an unreadable contract.json'
309
+ >({
310
+ storage: { storageHash: args.contractHash },
311
+ schemaVersion: '0.0.0',
312
+ target: args.targetId,
313
+ targetFamily: args.targetFamily,
314
+ models: {},
315
+ profileHash: EMPTY_CONTRACT_HASH,
316
+ });
317
+ }
318
+
319
+ export async function loadContractRawSafely(config: {
320
+ contract?: { output?: string };
321
+ }): Promise<unknown | null> {
322
+ try {
323
+ const path = resolveContractPath(config);
324
+ const raw = await readFile(path, 'utf-8');
325
+ return JSON.parse(raw);
326
+ } catch {
327
+ return null;
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Tolerant {@link ContractSpaceAggregate} assembly for read-only CLI
333
+ * commands. No integrity gate — callers query `aggregate.app` (or other
334
+ * facets) without re-reading `migrations/`. When `contract.json` is absent
335
+ * or undeserializable, the app contract falls back to an identity-only
336
+ * stand-in ({@link appContractStandInFromIdentity}), so these commands
337
+ * load without requiring a readable contract.
338
+ */
339
+ export async function buildReadAggregate(
340
+ config: PrismaNextConfig,
341
+ options: { readonly migrationsDir: string },
342
+ ): Promise<
343
+ Result<
344
+ { readonly aggregate: ContractSpaceAggregate; readonly contractHash: string },
345
+ CliStructuredError
346
+ >
347
+ > {
348
+ let contractHash: string = EMPTY_CONTRACT_HASH;
349
+ try {
350
+ const envelope = await readContractEnvelope(config);
351
+ contractHash = envelope.storageHash;
352
+ } catch {
353
+ // Contract unreadable — marker uses EMPTY_CONTRACT_HASH
354
+ }
355
+
356
+ try {
357
+ const contractRawForAggregate = await loadContractRawSafely(config);
358
+ const stack = createControlStack(config);
359
+ const familyInstance = config.family.create(stack);
360
+ const deserializeContract = (json: unknown): Contract =>
361
+ familyInstance.deserializeContract(json);
362
+ let appContractForLoad: Contract = appContractStandInFromIdentity({
363
+ contractHash,
364
+ targetId: config.target.id,
365
+ targetFamily: config.target.familyId,
366
+ });
367
+ if (contractRawForAggregate !== null) {
368
+ try {
369
+ appContractForLoad = deserializeContract(contractRawForAggregate);
370
+ } catch {
371
+ // Deserialization failed — identity-only stand-in fallback
372
+ }
373
+ }
374
+
375
+ const loaded = await loadContractSpaceAggregateForCli({
376
+ targetId: config.target.id,
377
+ migrationsDir: options.migrationsDir,
378
+ appContract: appContractForLoad,
379
+ extensionPacks: config.extensionPacks ?? [],
380
+ deserializeContract,
381
+ });
382
+ if (!loaded.ok) {
383
+ return loaded;
384
+ }
385
+ return ok({ aggregate: loaded.value, contractHash });
386
+ } catch (error) {
387
+ if (MigrationToolsError.is(error)) {
388
+ return notOk(mapMigrationToolsError(error));
389
+ }
390
+ return notOk(
391
+ errorUnexpected(error instanceof Error ? error.message : String(error), {
392
+ why: `Failed to read migrations directory: ${error instanceof Error ? error.message : String(error)}`,
393
+ }),
394
+ );
395
+ }
396
+ }
@@ -1,7 +1,7 @@
1
- import type { MigrationEdgeKind } from '@prisma-next/migration-tools/migration-list-graph-topology';
2
- import type { MigrationListEntry } from '@prisma-next/migration-tools/migration-list-types';
3
1
  import type { GlyphMode } from '../glyph-mode';
2
+ import type { MigrationEdgeKind } from './migration-list-graph-topology';
4
3
  import type { MigrationListStyler } from './migration-list-render';
4
+ import type { MigrationListEntry } from './migration-list-types';
5
5
 
6
6
  export const MIGRATION_LIST_HASH_WIDTH = 7;
7
7
  export const MIGRATION_LIST_EMPTY_SOURCE = '∅';
@@ -3,8 +3,8 @@ import {
3
3
  classifyMigrationListGraphTopology,
4
4
  type MigrationEdgeKind,
5
5
  type MigrationListGraphTopology,
6
- } from '@prisma-next/migration-tools/migration-list-graph-topology';
7
- import type { MigrationListEntry } from '@prisma-next/migration-tools/migration-list-types';
6
+ } from './migration-list-graph-topology';
7
+ import type { MigrationListEntry } from './migration-list-types';
8
8
 
9
9
  export type ConnectorKind = 'fanBelow' | 'joinAbove';
10
10
 
@@ -1,8 +1,3 @@
1
- import type { MigrationListGraphTopology } from '@prisma-next/migration-tools/migration-list-graph-topology';
2
- import type {
3
- MigrationListEntry,
4
- MigrationListResult,
5
- } from '@prisma-next/migration-tools/migration-list-types';
6
1
  import type { GlyphMode } from '../glyph-mode';
7
2
  import {
8
3
  abbreviateContractHash,
@@ -22,7 +17,9 @@ import type {
22
17
  NodeLineLayoutRow,
23
18
  } from './migration-list-graph-layout';
24
19
  import { computeMigrationListGraphLayout } from './migration-list-graph-layout';
20
+ import type { MigrationListGraphTopology } from './migration-list-graph-topology';
25
21
  import type { MigrationListStyler } from './migration-list-render';
22
+ import type { MigrationListEntry, MigrationListResult } from './migration-list-types';
26
23
 
27
24
  export type { GlyphMode } from '../glyph-mode';
28
25
 
@@ -0,0 +1,158 @@
1
+ import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
2
+ import type { MigrationListEntry } from './migration-list-types';
3
+
4
+ export type MigrationEdgeKind = 'forward' | 'rollback' | 'self';
5
+
6
+ export interface MigrationListGraphTopology {
7
+ readonly kindByMigrationHash: ReadonlyMap<string, MigrationEdgeKind>;
8
+ readonly forwardInDegree: ReadonlyMap<string, number>;
9
+ readonly forwardOutDegree: ReadonlyMap<string, number>;
10
+ }
11
+
12
+ interface NonSelfEdge {
13
+ readonly entry: MigrationListEntry;
14
+ readonly from: string;
15
+ readonly to: string;
16
+ }
17
+
18
+ function canonicalFrom(from: string | null): string {
19
+ return from ?? EMPTY_CONTRACT_HASH;
20
+ }
21
+
22
+ function compareDirNameDesc(a: MigrationListEntry, b: MigrationListEntry): number {
23
+ return b.dirName.localeCompare(a.dirName);
24
+ }
25
+
26
+ function bumpDegree(map: Map<string, number>, key: string): void {
27
+ map.set(key, (map.get(key) ?? 0) + 1);
28
+ }
29
+
30
+ export function classifyMigrationListGraphTopology(
31
+ entries: readonly MigrationListEntry[],
32
+ ): MigrationListGraphTopology {
33
+ const nodes = new Set<string>();
34
+ const kindByMigrationHash = new Map<string, MigrationEdgeKind>();
35
+ const outgoingByFrom = new Map<string, NonSelfEdge[]>();
36
+
37
+ for (const entry of entries) {
38
+ const from = canonicalFrom(entry.from);
39
+ const to = entry.to;
40
+ nodes.add(from);
41
+ nodes.add(to);
42
+
43
+ if (from === to) {
44
+ kindByMigrationHash.set(entry.migrationHash, 'self');
45
+ continue;
46
+ }
47
+
48
+ const bucket = outgoingByFrom.get(from);
49
+ const edge: NonSelfEdge = { entry, from, to };
50
+ if (bucket) bucket.push(edge);
51
+ else outgoingByFrom.set(from, [edge]);
52
+ }
53
+
54
+ for (const bucket of outgoingByFrom.values()) {
55
+ bucket.sort((a, b) => compareDirNameDesc(a.entry, b.entry));
56
+ }
57
+
58
+ const nonSelfInDegree = new Map<string, number>();
59
+ for (const node of nodes) {
60
+ nonSelfInDegree.set(node, 0);
61
+ }
62
+ for (const bucket of outgoingByFrom.values()) {
63
+ for (const edge of bucket) {
64
+ bumpDegree(nonSelfInDegree, edge.to);
65
+ }
66
+ }
67
+
68
+ const dfsRoots: string[] = [];
69
+ for (const node of nodes) {
70
+ if ((nonSelfInDegree.get(node) ?? 0) === 0) {
71
+ dfsRoots.push(node);
72
+ }
73
+ }
74
+ dfsRoots.sort((a, b) => {
75
+ if (a === EMPTY_CONTRACT_HASH) return -1;
76
+ if (b === EMPTY_CONTRACT_HASH) return 1;
77
+ return a.localeCompare(b);
78
+ });
79
+ if (dfsRoots.length === 0) {
80
+ dfsRoots.push(...[...nodes].sort((a, b) => a.localeCompare(b)));
81
+ }
82
+
83
+ const WHITE = 0;
84
+ const GRAY = 1;
85
+ const BLACK = 2;
86
+ const color = new Map<string, number>();
87
+ for (const node of nodes) {
88
+ color.set(node, WHITE);
89
+ }
90
+
91
+ interface Frame {
92
+ node: string;
93
+ outgoing: readonly NonSelfEdge[];
94
+ index: number;
95
+ }
96
+ const stack: Frame[] = [];
97
+
98
+ function pushFrame(node: string): void {
99
+ color.set(node, GRAY);
100
+ stack.push({ node, outgoing: outgoingByFrom.get(node) ?? [], index: 0 });
101
+ }
102
+
103
+ function runDfsFrom(root: string): void {
104
+ if (color.get(root) !== WHITE) return;
105
+ pushFrame(root);
106
+
107
+ while (stack.length > 0) {
108
+ const frame = stack[stack.length - 1];
109
+ if (frame === undefined) break;
110
+ if (frame.index >= frame.outgoing.length) {
111
+ color.set(frame.node, BLACK);
112
+ stack.pop();
113
+ continue;
114
+ }
115
+
116
+ const edge = frame.outgoing[frame.index];
117
+ frame.index += 1;
118
+ if (edge === undefined) continue;
119
+
120
+ const v = edge.to;
121
+ const vColor = color.get(v);
122
+ if (vColor === GRAY) {
123
+ kindByMigrationHash.set(edge.entry.migrationHash, 'rollback');
124
+ } else {
125
+ kindByMigrationHash.set(edge.entry.migrationHash, 'forward');
126
+ if (vColor === WHITE) {
127
+ pushFrame(v);
128
+ }
129
+ }
130
+ }
131
+ }
132
+
133
+ for (const root of dfsRoots) {
134
+ runDfsFrom(root);
135
+ }
136
+ const remainingWhite = [...nodes].filter((node) => color.get(node) === WHITE);
137
+ remainingWhite.sort((a, b) => a.localeCompare(b));
138
+ for (const root of remainingWhite) {
139
+ runDfsFrom(root);
140
+ }
141
+
142
+ const forwardInDegree = new Map<string, number>();
143
+ const forwardOutDegree = new Map<string, number>();
144
+
145
+ for (const entry of entries) {
146
+ if (kindByMigrationHash.get(entry.migrationHash) !== 'forward') continue;
147
+ const from = canonicalFrom(entry.from);
148
+ const to = entry.to;
149
+ bumpDegree(forwardOutDegree, from);
150
+ bumpDegree(forwardInDegree, to);
151
+ }
152
+
153
+ return {
154
+ kindByMigrationHash,
155
+ forwardInDegree,
156
+ forwardOutDegree,
157
+ };
158
+ }
@@ -1,12 +1,3 @@
1
- import {
2
- classifyMigrationListGraphTopology,
3
- type MigrationEdgeKind,
4
- type MigrationListGraphTopology,
5
- } from '@prisma-next/migration-tools/migration-list-graph-topology';
6
- import type {
7
- MigrationListEntry,
8
- MigrationListResult,
9
- } from '@prisma-next/migration-tools/migration-list-types';
10
1
  import type { GlyphMode } from '../glyph-mode';
11
2
  import {
12
3
  computeMigrationDirNameWidth,
@@ -15,14 +6,20 @@ import {
15
6
  migrationListForwardArrow,
16
7
  migrationListKindGlyph,
17
8
  } from './migration-list-data-column';
9
+ import {
10
+ classifyMigrationListGraphTopology,
11
+ type MigrationEdgeKind,
12
+ type MigrationListGraphTopology,
13
+ } from './migration-list-graph-topology';
14
+ import type { MigrationListEntry, MigrationListResult } from './migration-list-types';
18
15
 
19
- export type { MigrationEdgeKind } from '@prisma-next/migration-tools/migration-list-graph-topology';
16
+ export type { GlyphMode } from '../glyph-mode';
17
+ export type { MigrationEdgeKind } from './migration-list-graph-topology';
20
18
  export type {
21
19
  MigrationListEntry,
22
20
  MigrationListResult,
23
21
  MigrationSpaceListEntry,
24
- } from '@prisma-next/migration-tools/migration-list-types';
25
- export type { GlyphMode } from '../glyph-mode';
22
+ } from './migration-list-types';
26
23
 
27
24
  /**
28
25
  * Semantic styler for `migration list` output tokens. Token-typed so
@@ -0,0 +1,21 @@
1
+ export interface MigrationListEntry {
2
+ readonly dirName: string;
3
+ readonly from: string | null;
4
+ readonly to: string;
5
+ readonly migrationHash: string;
6
+ readonly operationCount: number;
7
+ readonly createdAt: string;
8
+ readonly refs: readonly string[];
9
+ readonly providedInvariants: readonly string[];
10
+ }
11
+
12
+ export interface MigrationSpaceListEntry {
13
+ readonly spaceId: string;
14
+ readonly migrations: readonly MigrationListEntry[];
15
+ }
16
+
17
+ export interface MigrationListResult {
18
+ readonly ok: true;
19
+ readonly spaces: readonly MigrationSpaceListEntry[];
20
+ readonly summary: string;
21
+ }
@@ -1,220 +0,0 @@
1
- import { CliStructuredError as CliStructuredError$1, errorConfigValidation as errorConfigValidation$1, errorContractConfigMissing as errorContractConfigMissing$1, errorContractValidationFailed, errorDatabaseConnectionRequired, errorDriverRequired, errorFileNotFound, errorInvalidOutputFormat, errorMigrationPlanningFailed, errorOutputFormatMutex, errorTargetMigrationNotSupported, errorUnexpected as errorUnexpected$1 } from "@prisma-next/errors/control";
2
- import { ERROR_CODE_DESTRUCTIVE_CHANGES, errorDestructiveChanges, errorHashMismatch, errorMarkerMissing, errorRunnerFailed, errorRuntime, errorRuntime as errorRuntime$1, errorTargetMismatch } from "@prisma-next/errors/execution";
3
- import "@prisma-next/errors/migration";
4
- //#region src/utils/cli-errors.ts
5
- function errorRefSetHashNotInGraph(resolvedHash, reachableHashes, graphTipHash) {
6
- const reachableList = reachableHashes.length > 0 ? reachableHashes.join(", ") : "(none — migration graph is empty)";
7
- const fix = reachableHashes.length > 0 ? graphTipHash !== null ? `Set the ref to a graph-node hash such as ${graphTipHash}, or run \`prisma-next migration plan\` to extend the graph.` : "Set the ref to a hash that appears in the migration graph." : "Run `prisma-next migration plan` first.";
8
- return errorRuntime(`Resolved contract hash is not in the migration graph: ${resolvedHash}`, {
9
- why: reachableHashes.length > 0 ? `The migration graph reaches ${reachableList}; resolved ${resolvedHash} isn't a graph node.` : "The migration graph is empty — no hashes reachable.",
10
- fix,
11
- meta: {
12
- code: "MIGRATION.HASH_NOT_IN_GRAPH",
13
- resolvedHash,
14
- reachableHashes: [...reachableHashes],
15
- ...graphTipHash !== null ? { graphTipHash } : {}
16
- }
17
- });
18
- }
19
- function errorRefSetEmptySentinel(hash) {
20
- return errorRuntime(`Cannot set ref to the empty-database sentinel: ${hash}`, {
21
- why: "The empty-database sentinel is a planner internal; it is not a valid ref target.",
22
- fix: "Set the ref to a contract hash from the migration graph, or use another ref name.",
23
- meta: {
24
- code: "MIGRATION.REF_SET_EMPTY_SENTINEL",
25
- hash
26
- }
27
- });
28
- }
29
- /**
30
- * `--space <id>` was given a value that doesn't satisfy the contract-space
31
- * naming rule (`[a-z][a-z0-9_-]{0,63}` per `isValidSpaceId`). Fires before
32
- * any fs work — the input is syntactically rejected the same way an on-disk
33
- * directory with that name would be skipped by the enumerator.
34
- */
35
- function errorInvalidSpaceId(spaceId) {
36
- return errorRuntime(`Invalid contract space id: ${spaceId}`, {
37
- why: "Contract space ids must match [a-z][a-z0-9_-]{0,63} (lowercase, starts with a letter, max 64 characters — the rule applied to every on-disk space directory).",
38
- fix: "Pass a space id that matches the directory naming rule, or omit --space to list every space.",
39
- meta: {
40
- code: "MIGRATION.INVALID_SPACE_ID",
41
- spaceId
42
- }
43
- });
44
- }
45
- /**
46
- * `migration list --space <id>` was given a contract-space id that has no
47
- * directory under `migrations/`. Distinct from "the space exists but is
48
- * empty" — that path renders the empty-state line and exits 0 per the
49
- * slice spec § Empty-state. This error fires only when `<projectMigrationsDir>/<spaceId>`
50
- * does not exist on disk.
51
- *
52
- * `availableSpaces` lists the contract-space directory names actually
53
- * present, sorted lex-asc, so the diagnostic can suggest a near match
54
- * without making the user reach for `ls`.
55
- */
56
- function errorSpaceNotFound(spaceId, availableSpaces) {
57
- const availableList = availableSpaces.length > 0 ? availableSpaces.join(", ") : "(none — no contract spaces on disk yet)";
58
- const fix = availableSpaces.length > 0 ? `Pick one of: ${availableList}. Run \`prisma-next migration list\` (no --space) to see every space's migrations.` : "Author a migration with `prisma-next migration new` to create the first contract-space directory.";
59
- return errorRuntime(`Unknown contract space: ${spaceId}`, {
60
- why: `No directory named "${spaceId}" exists under the migrations root.`,
61
- fix,
62
- meta: {
63
- code: "MIGRATION.SPACE_NOT_FOUND",
64
- spaceId,
65
- availableSpaces: [...availableSpaces]
66
- }
67
- });
68
- }
69
- function errorRefSetBundleNotFound(hash) {
70
- return errorRuntime(`No migration bundle matches graph-node hash ${hash}`, {
71
- why: `The hash is a graph node but no on-disk bundle has metadata.to = ${hash}.`,
72
- fix: "Run `pnpm fixtures:check`, or re-emit the migration that produces this hash so its bundle is restored.",
73
- meta: {
74
- code: "MIGRATION.REF_SET_BUNDLE_NOT_FOUND",
75
- hash
76
- }
77
- });
78
- }
79
- function errorPlanForgotTheFlag(resolvedHash, reachableRefs, graphTipHash) {
80
- const reachableList = reachableRefs.length > 0 ? reachableRefs.map((r) => `${r.name} (${r.hash})`).join(", ") : "(none)";
81
- const refFix = reachableRefs.length > 0 ? `Run migration plan with ${reachableRefs.map((r) => `--from ${r.name}`).join(" or ")}.` : graphTipHash !== null ? `Run migration plan --from ${graphTipHash}.` : "Commit pending migrations first, then run migration plan.";
82
- return errorRuntime(`Resolved from-hash is not in the migration graph: ${resolvedHash}`, {
83
- why: `The migration graph reaches ${reachableList}; resolved ${resolvedHash} isn't a graph node.`,
84
- fix: refFix,
85
- meta: {
86
- code: "MIGRATION.HASH_NOT_IN_GRAPH",
87
- resolvedHash,
88
- reachableRefs: reachableRefs.map((r) => r.name),
89
- ...graphTipHash !== null ? { graphTipHash } : {}
90
- }
91
- });
92
- }
93
- function errorSnapshotMissing(identifier, options) {
94
- const viaRef = options?.viaRef !== false;
95
- const fix = viaRef ? `Run "prisma-next db update --advance-ref ${identifier}" to repopulate the snapshot, or "prisma-next ref delete ${identifier}" to clear the orphan pointer.` : `No contract source exists for hash "${identifier}" on an empty migration graph. Use --from with a ref name that has a paired snapshot, or run db update first.`;
96
- return errorRuntime(viaRef ? `Ref "${identifier}" has no paired contract snapshot` : `No contract source for from-hash "${identifier}"`, {
97
- why: viaRef ? `Ref "${identifier}" exists but its paired snapshot files are missing.` : `Hash "${identifier}" is not a graph node and no paired ref snapshot supplies a contract.`,
98
- fix,
99
- meta: {
100
- code: "MIGRATION.SNAPSHOT_MISSING",
101
- identifier,
102
- viaRef
103
- }
104
- });
105
- }
106
- function errorMarkerMismatch(markerHash, reachableHashes, graphTip) {
107
- const reachableList = reachableHashes.length > 0 ? reachableHashes.join(", ") : "(none — migration graph is empty)";
108
- const planFromFix = graphTip !== null ? `Run \`prisma-next migration plan --from ${graphTip}\` if the live marker is canonical and the on-disk graph needs catching up.` : "Run `prisma-next migration plan` if the live marker is canonical and the on-disk graph needs catching up.";
109
- return errorRuntime("Database marker is not reachable in the on-disk migration graph", {
110
- why: `DB marker is ${markerHash}, but the on-disk migration graph reaches: ${reachableList}.`,
111
- fix: [
112
- planFromFix,
113
- `Run \`prisma-next ref set db ${markerHash}\` if the on-disk graph is canonical and the local \`db\` ref drifted.`,
114
- "Investigate whether the database was migrated by an out-of-band process."
115
- ].join("\n"),
116
- meta: {
117
- code: "MIGRATION.MARKER_MISMATCH",
118
- markerHash,
119
- reachableHashes: [...reachableHashes],
120
- ...graphTip !== null ? { graphTip } : {}
121
- }
122
- });
123
- }
124
- function errorPathUnreachable(failure) {
125
- const meta = failure.meta ?? {};
126
- const fromHashMeta = typeof meta["fromHash"] === "string" ? meta["fromHash"] : null;
127
- const planFromHash = fromHashMeta === "<empty>" ? null : fromHashMeta;
128
- const targetHash = typeof meta["targetHash"] === "string" ? meta["targetHash"] : typeof meta["target"] === "string" ? meta["target"] : null;
129
- const deadEnds = meta["deadEnds"];
130
- const deadEndsSuffix = Array.isArray(deadEnds) && deadEnds.length > 0 ? ` Dead-ends: ${deadEnds.map(String).join(", ")}.` : "";
131
- const planCommand = (() => {
132
- if (planFromHash !== null && targetHash !== null) return `prisma-next migration plan --from ${planFromHash} --to ${targetHash} --name <slug>`;
133
- if (targetHash !== null) return `prisma-next migration plan --to ${targetHash} --name <slug>`;
134
- if (planFromHash !== null) return `prisma-next migration plan --from ${planFromHash} --name <slug>`;
135
- return "prisma-next migration plan";
136
- })();
137
- const applyCommand = targetHash !== null ? `prisma-next migrate --to ${targetHash}` : "prisma-next migrate";
138
- return errorRuntime(failure.summary, {
139
- why: failure.why ?? `Cannot reach target "${targetHash ?? "<unknown>"}" from current marker "${fromHashMeta ?? "<unknown>"}".${deadEndsSuffix}`,
140
- fix: [
141
- "Plan the missing edge, then apply it:",
142
- ` 1. ${planCommand}`,
143
- ` 2. ${applyCommand}`,
144
- "A rollback (reverse) plan is expected to contain destructive (DROP) operations — review them before applying.",
145
- "Narrower cases (rename inference, re-adding a NOT NULL column without a safe default, or a type change that needs data) may additionally need a hint in the planned migration.",
146
- "Inspect the on-disk graph with `prisma-next migration list`, or `prisma-next migration show <bundle>` for any bundle in the path you expected."
147
- ].join("\n"),
148
- meta: {
149
- ...meta,
150
- code: "MIGRATION.PATH_UNREACHABLE"
151
- }
152
- });
153
- }
154
- /**
155
- * Maps a `MigrationToolsError` raised by the migration-tools loader/graph
156
- * surface (`readMigrationPackage`, `readMigrationsDir`, `readRefs`,
157
- * `resolveRef`, `reconstructGraph`, ...) into a CLI `errorRuntime` envelope.
158
- *
159
- * The full `error.details` payload is forwarded into `meta` so machine
160
- * consumers (`--json`) see structural fields like `dir`, `storedHash`,
161
- * `computedHash` (for `MIGRATION.HASH_MISMATCH`) alongside the stable
162
- * `code`. The user-visible `summary`/`why`/`fix` text is unchanged.
163
- *
164
- * Callers are expected to gate on `MigrationToolsError.is(error)` first
165
- * (mirroring the original inline pattern); non-`MigrationToolsError`
166
- * values are caller-classified (rethrow, wrap with command-specific
167
- * `errorUnexpected`, etc.).
168
- */
169
- function mapMigrationToolsError(error) {
170
- return errorRuntime(error.message, {
171
- why: error.why,
172
- fix: error.fix,
173
- meta: {
174
- code: error.code,
175
- ...error.details ?? {}
176
- }
177
- });
178
- }
179
- /**
180
- * Maps a `RefResolutionError` from the contract/migration reference
181
- * resolver into a CLI structured error envelope.
182
- */
183
- function mapRefResolutionError(error) {
184
- switch (error.kind) {
185
- case "not-found": return errorRuntime(`Not a known ${error.grammar} reference: "${error.input}"`, {
186
- why: `No ${error.grammar} matching "${error.input}" exists in the migration graph or refs index.`,
187
- fix: error.grammar === "contract" ? "Provide a valid contract hash, ref name, or migration directory name." : "Provide a valid migration directory name or migration hash.",
188
- meta: {
189
- input: error.input,
190
- grammar: error.grammar
191
- }
192
- });
193
- case "ambiguous": return errorRuntime(`Ambiguous ${error.grammar} reference: "${error.input}"`, {
194
- why: `"${error.input}" matches multiple ${error.grammar}s: ${error.candidates.join(", ")}`,
195
- fix: "Provide a longer prefix or use the full hash to disambiguate.",
196
- meta: {
197
- input: error.input,
198
- candidates: error.candidates,
199
- grammar: error.grammar
200
- }
201
- });
202
- case "wrong-grammar": return errorRuntime(error.message, {
203
- why: error.message,
204
- fix: error.fix,
205
- meta: {
206
- input: error.input,
207
- expectedGrammar: error.expectedGrammar
208
- }
209
- });
210
- case "invalid-format": return errorRuntime(`Invalid reference format: "${error.input}"`, {
211
- why: error.reason,
212
- fix: "Provide a valid contract hash, ref name, or migration directory name.",
213
- meta: { input: error.input }
214
- });
215
- }
216
- }
217
- //#endregion
218
- export { mapRefResolutionError as A, errorRuntime$1 as C, errorTargetMismatch as D, errorTargetMigrationNotSupported as E, errorUnexpected$1 as O, errorRunnerFailed as S, errorSpaceNotFound as T, errorPathUnreachable as _, errorContractValidationFailed as a, errorRefSetEmptySentinel as b, errorDriverRequired as c, errorInvalidOutputFormat as d, errorInvalidSpaceId as f, errorOutputFormatMutex as g, errorMigrationPlanningFailed as h, errorContractConfigMissing$1 as i, mapMigrationToolsError as k, errorFileNotFound as l, errorMarkerMissing as m, ERROR_CODE_DESTRUCTIVE_CHANGES as n, errorDatabaseConnectionRequired as o, errorMarkerMismatch as p, errorConfigValidation$1 as r, errorDestructiveChanges as s, CliStructuredError$1 as t, errorHashMismatch as u, errorPlanForgotTheFlag as v, errorSnapshotMissing as w, errorRefSetHashNotInGraph as x, errorRefSetBundleNotFound as y };
219
-
220
- //# sourceMappingURL=cli-errors-DQY629C7.mjs.map