@prisma-next/cli 0.12.0-dev.21 → 0.12.0-dev.23

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 (46) hide show
  1. package/dist/cli.mjs +5 -5
  2. package/dist/{client-Cdxcme1x.mjs → client-BHe8szOW.mjs} +4 -4
  3. package/dist/{client-Cdxcme1x.mjs.map → client-BHe8szOW.mjs.map} +1 -1
  4. package/dist/commands/contract-infer.mjs +1 -1
  5. package/dist/commands/db-init.mjs +2 -2
  6. package/dist/commands/db-schema.mjs +1 -1
  7. package/dist/commands/db-sign.mjs +1 -1
  8. package/dist/commands/db-update.mjs +2 -2
  9. package/dist/commands/db-verify.mjs +1 -1
  10. package/dist/commands/migrate.mjs +1 -1
  11. package/dist/commands/migration-graph.mjs +164 -1
  12. package/dist/commands/migration-graph.mjs.map +1 -0
  13. package/dist/commands/migration-list.d.mts.map +1 -1
  14. package/dist/commands/migration-list.mjs +4 -6
  15. package/dist/commands/migration-list.mjs.map +1 -1
  16. package/dist/commands/migration-log.d.mts +4 -16
  17. package/dist/commands/migration-log.d.mts.map +1 -1
  18. package/dist/commands/migration-log.mjs +1 -137
  19. package/dist/commands/migration-show.mjs +1 -1
  20. package/dist/commands/migration-status.mjs +1 -1
  21. package/dist/{contract-infer-DaFPNrZH.mjs → contract-infer-OCn12Zvn.mjs} +2 -2
  22. package/dist/{contract-infer-DaFPNrZH.mjs.map → contract-infer-OCn12Zvn.mjs.map} +1 -1
  23. package/dist/{db-verify-BSA1a_W_.mjs → db-verify-DJxengYP.mjs} +2 -2
  24. package/dist/{db-verify-BSA1a_W_.mjs.map → db-verify-DJxengYP.mjs.map} +1 -1
  25. package/dist/exports/control-api.mjs +1 -1
  26. package/dist/{inspect-live-schema-Dn56wDhG.mjs → inspect-live-schema-DVZlDlnF.mjs} +2 -2
  27. package/dist/{inspect-live-schema-Dn56wDhG.mjs.map → inspect-live-schema-DVZlDlnF.mjs.map} +1 -1
  28. package/dist/{migration-command-scaffold-V52dV2Tv.mjs → migration-command-scaffold-Cs7Ky-m5.mjs} +2 -2
  29. package/dist/{migration-command-scaffold-V52dV2Tv.mjs.map → migration-command-scaffold-Cs7Ky-m5.mjs.map} +1 -1
  30. package/dist/{migration-graph-DKl_IYsF.mjs → migration-graph-tree-render-BQdhKBO8.mjs} +405 -165
  31. package/dist/migration-graph-tree-render-BQdhKBO8.mjs.map +1 -0
  32. package/dist/migration-log-BzPmks3c.mjs +203 -0
  33. package/dist/migration-log-BzPmks3c.mjs.map +1 -0
  34. package/package.json +18 -18
  35. package/src/commands/migration-list.ts +11 -15
  36. package/src/commands/migration-log.ts +23 -89
  37. package/src/control-api/client.ts +3 -3
  38. package/src/utils/formatters/migration-graph-tree-render.ts +31 -1
  39. package/src/utils/formatters/migration-list-data-column.ts +0 -93
  40. package/src/utils/formatters/migration-list-graph-topology.ts +1 -7
  41. package/src/utils/formatters/migration-list-render.ts +102 -70
  42. package/src/utils/formatters/migration-log-table.ts +190 -0
  43. package/dist/commands/migration-log.mjs.map +0 -1
  44. package/dist/migration-graph-DKl_IYsF.mjs.map +0 -1
  45. package/dist/migration-list-styler-COQbZmXk.mjs +0 -414
  46. package/dist/migration-list-styler-COQbZmXk.mjs.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma-next/cli",
3
- "version": "0.12.0-dev.21",
3
+ "version": "0.12.0-dev.23",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -10,15 +10,15 @@
10
10
  "dependencies": {
11
11
  "@clack/prompts": "^1.4.0",
12
12
  "@dagrejs/dagre": "^3.0.0",
13
- "@prisma-next/config": "0.12.0-dev.21",
14
- "@prisma-next/contract": "0.12.0-dev.21",
15
- "@prisma-next/emitter": "0.12.0-dev.21",
16
- "@prisma-next/errors": "0.12.0-dev.21",
17
- "@prisma-next/framework-components": "0.12.0-dev.21",
18
- "@prisma-next/migration-tools": "0.12.0-dev.21",
19
- "@prisma-next/psl-printer": "0.12.0-dev.21",
20
- "@prisma-next/cli-telemetry": "0.12.0-dev.21",
21
- "@prisma-next/utils": "0.12.0-dev.21",
13
+ "@prisma-next/config": "0.12.0-dev.23",
14
+ "@prisma-next/contract": "0.12.0-dev.23",
15
+ "@prisma-next/emitter": "0.12.0-dev.23",
16
+ "@prisma-next/errors": "0.12.0-dev.23",
17
+ "@prisma-next/framework-components": "0.12.0-dev.23",
18
+ "@prisma-next/migration-tools": "0.12.0-dev.23",
19
+ "@prisma-next/psl-printer": "0.12.0-dev.23",
20
+ "@prisma-next/cli-telemetry": "0.12.0-dev.23",
21
+ "@prisma-next/utils": "0.12.0-dev.23",
22
22
  "arktype": "^2.2.0",
23
23
  "c12": "^3.3.4",
24
24
  "ci-info": "^4.3.1",
@@ -35,14 +35,14 @@
35
35
  "wrap-ansi": "^10.0.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@prisma-next/sql-contract": "0.12.0-dev.21",
39
- "@prisma-next/sql-contract-emitter": "0.12.0-dev.21",
40
- "@prisma-next/sql-contract-ts": "0.12.0-dev.21",
41
- "@prisma-next/sql-operations": "0.12.0-dev.21",
42
- "@prisma-next/sql-runtime": "0.12.0-dev.21",
43
- "@prisma-next/test-utils": "0.12.0-dev.21",
44
- "@prisma-next/tsconfig": "0.12.0-dev.21",
45
- "@prisma-next/tsdown": "0.12.0-dev.21",
38
+ "@prisma-next/sql-contract": "0.12.0-dev.23",
39
+ "@prisma-next/sql-contract-emitter": "0.12.0-dev.23",
40
+ "@prisma-next/sql-contract-ts": "0.12.0-dev.23",
41
+ "@prisma-next/sql-operations": "0.12.0-dev.23",
42
+ "@prisma-next/sql-runtime": "0.12.0-dev.23",
43
+ "@prisma-next/test-utils": "0.12.0-dev.23",
44
+ "@prisma-next/tsconfig": "0.12.0-dev.23",
45
+ "@prisma-next/tsdown": "0.12.0-dev.23",
46
46
  "@types/node": "25.6.0",
47
47
  "tsdown": "0.22.0",
48
48
  "typescript": "5.9.3",
@@ -26,10 +26,7 @@ import {
26
26
  setCommandSeeAlso,
27
27
  } from '../utils/command-helpers';
28
28
  import { buildReadAggregate } from '../utils/contract-space-aggregate-loader';
29
- import {
30
- buildMigrationListTopologyBySpace,
31
- renderMigrationListWithStyle,
32
- } from '../utils/formatters/migration-list-render';
29
+ import { renderMigrationListWithStyle } from '../utils/formatters/migration-list-render';
33
30
  import { createAnsiMigrationListStyler } from '../utils/formatters/migration-list-styler';
34
31
  import type {
35
32
  MigrationListEntry,
@@ -143,8 +140,9 @@ export function renderMigrationListHumanOutput(
143
140
  options: MigrationListHumanRenderOptions,
144
141
  ): string {
145
142
  const styler = createAnsiMigrationListStyler({ useColor: options.useColor });
146
- const topologyBySpaceId = buildMigrationListTopologyBySpace(result);
147
- return renderMigrationListWithStyle(result, styler, options.glyphMode, topologyBySpaceId);
143
+ return renderMigrationListWithStyle(result, styler, options.glyphMode, {
144
+ colorize: options.useColor,
145
+ });
148
146
  }
149
147
 
150
148
  /**
@@ -222,7 +220,7 @@ export async function executeMigrationListCommand(
222
220
  if (!flags.json && !flags.quiet) {
223
221
  const header = formatStyledHeader({
224
222
  command: 'migration list',
225
- description: 'List on-disk migrations, latest first, per contract space',
223
+ description: 'List on-disk migrations per contract space',
226
224
  details: [
227
225
  { label: 'config', value: configPath },
228
226
  { label: 'migrations', value: migrationsRelative },
@@ -253,15 +251,13 @@ export function createMigrationListCommand(): Command {
253
251
  const command = new Command('list');
254
252
  setCommandDescriptions(
255
253
  command,
256
- 'List on-disk migrations, latest first, per contract space',
254
+ 'List on-disk migrations per contract space',
257
255
  'Enumerates every on-disk migration under migrations/<space>/ for every\n' +
258
- 'contract space found on disk, latest first. Offline — does not consult\n' +
259
- 'the database. Each row leads with a kind glyph (* forward, ↩ rollback,\n' +
260
- ' self), then dirName, then source destination contract hashes\n' +
261
- '(7-char git-style). Self-edges show a single hash. Invariants render as\n' +
262
- '{...}; refs on the destination as (production, db). Pass --space <id>\n' +
263
- 'to narrow to one contract space. --ascii forces ASCII kind glyphs\n' +
264
- '(orthogonal to --no-color).',
256
+ 'contract space found on disk. Offline — does not consult the database.\n' +
257
+ 'Human output draws the shared migration graph tree with operation counts,\n' +
258
+ 'invariants on each migration row, and refs on destination contract nodes.\n' +
259
+ 'Pass --space <id> to narrow to one contract space. --ascii forces ASCII\n' +
260
+ 'tree glyphs (orthogonal to --no-color).',
265
261
  );
266
262
  setCommandExamples(command, [
267
263
  'prisma-next migration list',
@@ -1,9 +1,6 @@
1
- import type { MigrationPlanOperation } from '@prisma-next/framework-components/control';
2
- import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
1
+ import type { LedgerEntryRecord } from '@prisma-next/contract/types';
3
2
  import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
4
- import { findPath } from '@prisma-next/migration-tools/migration-graph';
5
3
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
6
- import { cyan, dim } from 'colorette';
7
4
  import { Command } from 'commander';
8
5
  import { loadConfig } from '../config-loader';
9
6
  import { createControlClient } from '../control-api/client';
@@ -23,7 +20,12 @@ import {
23
20
  setCommandSeeAlso,
24
21
  targetSupportsMigrations,
25
22
  } from '../utils/command-helpers';
26
- import { buildReadAggregate } from '../utils/contract-space-aggregate-loader';
23
+ import { createAnsiMigrationListStyler } from '../utils/formatters/migration-list-styler';
24
+ import {
25
+ MIGRATION_LOG_EMPTY_MESSAGE,
26
+ renderMigrationLogTable,
27
+ serializeLedgerEntriesForJson,
28
+ } from '../utils/formatters/migration-log-table';
27
29
  import { formatStyledHeader } from '../utils/formatters/styled';
28
30
  import type { CommonCommandOptions } from '../utils/global-flags';
29
31
  import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
@@ -33,34 +35,16 @@ import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
33
35
  interface MigrationLogOptions extends CommonCommandOptions {
34
36
  readonly db?: string;
35
37
  readonly config?: string;
36
- }
37
-
38
- export interface MigrationLogEntry {
39
- readonly dirName: string;
40
- readonly from: string;
41
- readonly to: string;
42
- readonly migrationHash: string;
43
- readonly operationCount: number;
44
- readonly createdAt: string;
45
- }
46
-
47
- export interface MigrationLogResult {
48
- readonly ok: true;
49
- readonly markerHash: string | null;
50
- readonly applied: readonly MigrationLogEntry[];
51
- readonly summary: string;
38
+ readonly utc?: boolean;
52
39
  }
53
40
 
54
41
  export async function executeMigrationLogCommand(
55
42
  options: MigrationLogOptions,
56
43
  flags: GlobalFlags,
57
44
  ui: TerminalUI,
58
- ): Promise<Result<MigrationLogResult, CliStructuredError>> {
45
+ ): Promise<Result<readonly LedgerEntryRecord[], CliStructuredError>> {
59
46
  const config = await loadConfig(options.config);
60
- const { configPath, appMigrationsRelative, migrationsDir } = resolveMigrationPaths(
61
- options.config,
62
- config,
63
- );
47
+ const { configPath } = resolveMigrationPaths(options.config, config);
64
48
 
65
49
  const dbConnection = options.db ?? config.db?.connection;
66
50
  if (!dbConnection) {
@@ -81,10 +65,9 @@ export async function executeMigrationLogCommand(
81
65
  if (!flags.json && !flags.quiet) {
82
66
  const header = formatStyledHeader({
83
67
  command: 'migration log',
84
- description: 'Show executed migration history',
68
+ description: 'Show executed migration history from the database ledger',
85
69
  details: [
86
70
  { label: 'config', value: configPath },
87
- { label: 'migrations', value: appMigrationsRelative },
88
71
  ...(typeof dbConnection === 'string'
89
72
  ? [{ label: 'database', value: maskConnectionUrl(dbConnection) }]
90
73
  : []),
@@ -94,13 +77,6 @@ export async function executeMigrationLogCommand(
94
77
  ui.stderr(header);
95
78
  }
96
79
 
97
- const loaded = await buildReadAggregate(config, { migrationsDir });
98
- if (!loaded.ok) {
99
- return loaded;
100
- }
101
- const graph = loaded.value.aggregate.app.graph();
102
- const bundles = loaded.value.aggregate.app.packages;
103
-
104
80
  const client = createControlClient({
105
81
  family: config.family,
106
82
  target: config.target,
@@ -111,47 +87,8 @@ export async function executeMigrationLogCommand(
111
87
 
112
88
  try {
113
89
  await client.connect(dbConnection);
114
- const marker = await client.readMarker();
115
- const markerHash = marker?.storageHash ?? null;
116
-
117
- if (!markerHash) {
118
- return ok({
119
- ok: true,
120
- markerHash: null,
121
- applied: [],
122
- summary: 'No migrations applied (database has no marker)',
123
- });
124
- }
125
-
126
- const appliedPath = findPath(graph, EMPTY_CONTRACT_HASH, markerHash);
127
- if (appliedPath === null) {
128
- return notOk(
129
- errorUnexpected('Database marker is not reachable from migration history', {
130
- why: `Marker hash ${markerHash} is not reachable from the root of the on-disk migration graph.`,
131
- fix: 'The database may have been migrated outside this project. Use `migration status` to inspect the current state.',
132
- }),
133
- );
134
- }
135
- const pkgByDirName = new Map(bundles.map((p) => [p.dirName, p]));
136
- const entries: MigrationLogEntry[] = appliedPath.map((edge) => {
137
- const pkg = pkgByDirName.get(edge.dirName);
138
- const ops = (pkg?.ops ?? []) as readonly MigrationPlanOperation[];
139
- return {
140
- dirName: edge.dirName,
141
- from: edge.from,
142
- to: edge.to,
143
- migrationHash: edge.migrationHash,
144
- operationCount: ops.length,
145
- createdAt: edge.createdAt,
146
- };
147
- });
148
-
149
- return ok({
150
- ok: true,
151
- markerHash,
152
- applied: entries,
153
- summary: `${entries.length} migration(s) applied`,
154
- });
90
+ const ledger = await client.readLedger();
91
+ return ok(ledger);
155
92
  } catch (error) {
156
93
  if (CliStructuredError.is(error)) return notOk(error);
157
94
  if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
@@ -170,11 +107,12 @@ export function createMigrationLogCommand(): Command {
170
107
  setCommandDescriptions(
171
108
  command,
172
109
  'Show executed migration history',
173
- 'Reads the database marker and displays the applied migration chain\n' +
174
- 'from the initial state to the current marker position.',
110
+ 'Reads the database ledger and displays every applied migration edge\n' +
111
+ 'in chronological order, including rollbacks and re-applies.',
175
112
  );
176
113
  setCommandExamples(command, [
177
114
  'prisma-next migration log --db $DATABASE_URL',
115
+ 'prisma-next migration log --utc --db $DATABASE_URL',
178
116
  'prisma-next migration log --json --db $DATABASE_URL',
179
117
  ]);
180
118
  setCommandSeeAlso(command, [
@@ -186,24 +124,20 @@ export function createMigrationLogCommand(): Command {
186
124
  addGlobalOptions(command)
187
125
  .option('--db <url>', 'Database connection string')
188
126
  .option('--config <path>', 'Path to prisma-next.config.ts')
127
+ .option('--utc', 'Render human timestamps in UTC instead of local time')
189
128
  .action(async (options: MigrationLogOptions) => {
190
129
  const flags = parseGlobalFlagsOrExit(options);
191
130
  const ui = createTerminalUI(flags);
192
131
  const result = await executeMigrationLogCommand(options, flags, ui);
193
- const exitCode = handleResult(result, flags, ui, (logResult) => {
132
+ const exitCode = handleResult(result, flags, ui, (entries) => {
194
133
  if (flags.json) {
195
- ui.output(JSON.stringify(logResult, null, 2));
134
+ ui.output(JSON.stringify(serializeLedgerEntriesForJson(entries), null, 2));
196
135
  } else if (!flags.quiet) {
197
- const c = (fn: (s: string) => string, s: string) => (flags.color !== false ? fn(s) : s);
198
- if (logResult.applied.length === 0) {
199
- ui.log(logResult.summary);
136
+ if (entries.length === 0) {
137
+ ui.output(MIGRATION_LOG_EMPTY_MESSAGE);
200
138
  } else {
201
- for (const entry of logResult.applied) {
202
- ui.log(
203
- `${c(cyan, '✓')} ${entry.dirName} ${c(dim, entry.migrationHash.slice(0, 16) + '…')} ${entry.operationCount} op(s)`,
204
- );
205
- }
206
- ui.log(`\n${logResult.summary}`);
139
+ const styler = createAnsiMigrationListStyler({ useColor: ui.useColor });
140
+ ui.output(renderMigrationLogTable(entries, { utc: options.utc === true, styler }));
207
141
  }
208
142
  }
209
143
  });
@@ -451,10 +451,10 @@ class ControlClientImpl implements ControlClient {
451
451
  return familyInstance.readAllMarkers({ driver });
452
452
  }
453
453
 
454
- /** Reads the per-migration journal for `space` (defaults to the app contract space). */
455
- async readLedger(space = APP_SPACE_ID): Promise<readonly LedgerEntryRecord[]> {
454
+ /** Reads the per-migration journal; omit `space` to return every space. */
455
+ async readLedger(space?: string): Promise<readonly LedgerEntryRecord[]> {
456
456
  const { driver, familyInstance } = await this.ensureConnected();
457
- return familyInstance.readLedger({ driver, space });
457
+ return familyInstance.readLedger({ driver, ...ifDefined('space', space) });
458
458
  }
459
459
 
460
460
  async migrationApply(options: MigrationApplyOptions): Promise<MigrationApplyResult> {
@@ -27,8 +27,15 @@ const LABEL_GAP = 2;
27
27
  */
28
28
  const DB_MARKER_NAME = 'db';
29
29
 
30
+ export interface MigrationEdgeAnnotation {
31
+ readonly status?: 'applied' | 'pending';
32
+ readonly operationCount?: number;
33
+ readonly invariants?: readonly string[];
34
+ }
35
+
30
36
  export interface RenderMigrationGraphTreeOptions {
31
37
  readonly refsByHash?: ReadonlyMap<string, readonly string[]>;
38
+ readonly edgeAnnotationsByHash?: ReadonlyMap<string, MigrationEdgeAnnotation>;
32
39
  readonly dbHash?: string;
33
40
  readonly contractHash?: string;
34
41
  readonly activeRefName?: string;
@@ -571,6 +578,28 @@ function createTreeStyler(opts: RenderMigrationGraphTreeOptions): MigrationListS
571
578
  };
572
579
  }
573
580
 
581
+ function formatEdgeAnnotationSuffix(
582
+ migrationHash: string,
583
+ opts: RenderMigrationGraphTreeOptions,
584
+ style: MigrationListStyler,
585
+ ): string {
586
+ const annotation = opts.edgeAnnotationsByHash?.get(migrationHash);
587
+ if (annotation === undefined) {
588
+ return '';
589
+ }
590
+ const segments: string[] = [];
591
+ if (annotation.operationCount !== undefined) {
592
+ segments.push(`${annotation.operationCount} ops`);
593
+ }
594
+ if (annotation.invariants !== undefined && annotation.invariants.length > 0) {
595
+ segments.push(style.invariants(annotation.invariants));
596
+ }
597
+ if (segments.length === 0) {
598
+ return '';
599
+ }
600
+ return ` ${segments.join(' ')}`;
601
+ }
602
+
574
603
  function formatEdgeHashColumn(
575
604
  edge: ClassifiedEdge,
576
605
  style: MigrationListStyler,
@@ -784,7 +813,8 @@ export function renderMigrationGraphTree(
784
813
  : style.dirName;
785
814
  const dirName = `${dirNameStyler(edge.dirName)}${dirNamePadding}`;
786
815
  const hashColumn = formatEdgeHashColumn(edge, style, hashLength, palette);
787
- lines.push(trimTrailingWhitespace(`${gutterPad}${dirName}${hashColumn}`));
816
+ const annotationSuffix = formatEdgeAnnotationSuffix(edge.migrationHash, opts, style);
817
+ lines.push(trimTrailingWhitespace(`${gutterPad}${dirName}${hashColumn}${annotationSuffix}`));
788
818
  }
789
819
 
790
820
  return lines.join('\n');
@@ -1,32 +1,10 @@
1
1
  import type { GlyphMode } from '../glyph-mode';
2
- import type { MigrationEdgeKind } from './migration-list-graph-topology';
3
- import type { MigrationListStyler } from './migration-list-render';
4
- import type { MigrationListEntry } from './migration-list-types';
5
2
 
6
3
  export const MIGRATION_LIST_HASH_WIDTH = 7;
7
4
  export const MIGRATION_LIST_EMPTY_SOURCE = '∅';
8
5
  export const MIGRATION_LIST_ASCII_EMPTY_SOURCE = '-';
9
6
  export const MIGRATION_LIST_FORWARD_EDGE_GLYPH = '→';
10
7
  export const MIGRATION_LIST_ASCII_FORWARD_EDGE_GLYPH = '->';
11
- export const MIGRATION_LIST_DECORATION_PREFIX = ' ';
12
-
13
- export const MIGRATION_LIST_UNICODE_KIND_GLYPH: Record<MigrationEdgeKind, string> = {
14
- forward: '*',
15
- rollback: '↩',
16
- self: '⟲',
17
- };
18
-
19
- export const MIGRATION_LIST_ASCII_KIND_GLYPH: Record<MigrationEdgeKind, string> = {
20
- forward: '*',
21
- rollback: '<',
22
- self: '~',
23
- };
24
-
25
- export function migrationListKindGlyph(glyphMode: GlyphMode, edgeKind: MigrationEdgeKind): string {
26
- return glyphMode === 'ascii'
27
- ? MIGRATION_LIST_ASCII_KIND_GLYPH[edgeKind]
28
- : MIGRATION_LIST_UNICODE_KIND_GLYPH[edgeKind];
29
- }
30
8
 
31
9
  export function migrationListForwardArrow(glyphMode: GlyphMode): string {
32
10
  return glyphMode === 'ascii'
@@ -42,74 +20,3 @@ export function abbreviateContractHash(hash: string): string {
42
20
  const stripped = hash.startsWith('sha256:') ? hash.slice(7) : hash;
43
21
  return stripped.slice(0, MIGRATION_LIST_HASH_WIDTH);
44
22
  }
45
-
46
- export function computeMigrationDirNameWidth(migrations: readonly MigrationListEntry[]): number {
47
- if (migrations.length === 0) return 0;
48
- return Math.max(...migrations.map((entry) => entry.dirName.length)) + 2;
49
- }
50
-
51
- function formatSourceColumn(
52
- from: string | null,
53
- style: MigrationListStyler,
54
- emptySource: string,
55
- ): string {
56
- if (from === null) {
57
- return style.glyph(emptySource) + ' '.repeat(MIGRATION_LIST_HASH_WIDTH - emptySource.length);
58
- }
59
- return style.sourceHash(abbreviateContractHash(from));
60
- }
61
-
62
- export function formatDecorations(
63
- providedInvariants: readonly string[],
64
- refs: readonly string[],
65
- style: MigrationListStyler,
66
- ): string {
67
- const blocks: string[] = [];
68
- if (providedInvariants.length > 0) {
69
- blocks.push(style.invariants(providedInvariants));
70
- }
71
- if (refs.length > 0) {
72
- blocks.push(style.refs(refs));
73
- }
74
- if (blocks.length === 0) return '';
75
- return `${MIGRATION_LIST_DECORATION_PREFIX}${blocks.join(' ')}`;
76
- }
77
-
78
- export interface MigrationDataColumnOptions {
79
- readonly dirNameWidth: number;
80
- readonly edgeKind: MigrationEdgeKind;
81
- readonly style: MigrationListStyler;
82
- readonly forwardArrow?: string;
83
- readonly emptySource?: string;
84
- }
85
-
86
- export function formatMigrationDataColumn(
87
- migration: MigrationListEntry,
88
- options: MigrationDataColumnOptions,
89
- ): string {
90
- const {
91
- dirNameWidth,
92
- edgeKind,
93
- style,
94
- forwardArrow = MIGRATION_LIST_FORWARD_EDGE_GLYPH,
95
- emptySource = MIGRATION_LIST_EMPTY_SOURCE,
96
- } = options;
97
- const dirNamePadding = ' '.repeat(Math.max(0, dirNameWidth - migration.dirName.length));
98
- const dirName = `${style.dirName(migration.dirName)}${dirNamePadding}`;
99
- const decorations = formatDecorations(migration.providedInvariants, migration.refs, style);
100
-
101
- if (edgeKind === 'self') {
102
- const contractHash = migration.from ?? migration.to;
103
- const hash = style.sourceHash(abbreviateContractHash(contractHash));
104
- return `${dirName}${hash}${decorations}`;
105
- }
106
-
107
- const source = formatSourceColumn(migration.from, style, emptySource);
108
- const arrow = style.glyph(forwardArrow);
109
- const dest = style.destHash(abbreviateContractHash(migration.to));
110
- return `${dirName}${source} ${arrow} ${dest}${decorations}`;
111
- }
112
-
113
- export function formatNodeLineDataColumn(contractHash: string, style: MigrationListStyler): string {
114
- return style.sourceHash(abbreviateContractHash(contractHash));
115
- }
@@ -11,8 +11,7 @@ export interface MigrationListGraphTopology {
11
11
  }
12
12
 
13
13
  // ---------------------------------------------------------------------------
14
- // Shared classifier — operates on a normalized edge shape common to both
15
- // MigrationListEntry (Tier-2) and MigrationEdge / MigrationGraph (Tier-3).
14
+ // Shared classifier — operates on a normalized edge shape for MigrationGraph.
16
15
  // ---------------------------------------------------------------------------
17
16
 
18
17
  interface NormalizedEdge {
@@ -315,9 +314,6 @@ function canonicalFrom(from: string | null): string {
315
314
 
316
315
  /**
317
316
  * Classify forward/rollback/self for a Tier-2 `MigrationListEntry[]` edge set.
318
- * Returns the kind of each migration plus the forward in/out degree of each
319
- * contract node. This is the established Tier-2 surface; its behaviour is
320
- * unchanged — only its implementation now delegates to the shared classifier.
321
317
  */
322
318
  export function classifyMigrationListGraphTopology(
323
319
  entries: readonly MigrationListEntry[],
@@ -333,8 +329,6 @@ export function classifyMigrationListGraphTopology(
333
329
 
334
330
  /**
335
331
  * Classify forward/rollback/self for a `MigrationGraph` edge set (Tier-3).
336
- * Delegates to the same shared classifier as `classifyMigrationListGraphTopology`
337
- * so both tiers agree on forward/rollback/self without duplicating logic.
338
332
  */
339
333
  export function classifyMigrationGraphTopology(graph: MigrationGraph): MigrationListGraphTopology {
340
334
  const normalized: NormalizedEdge[] = [];