@prisma-next/cli 0.11.0-dev.5 → 0.11.0-dev.51

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/dist/cli-errors-DFF1LlfU.mjs +215 -0
  2. package/dist/cli-errors-DFF1LlfU.mjs.map +1 -0
  3. package/dist/cli.mjs +9 -10
  4. package/dist/cli.mjs.map +1 -1
  5. package/dist/{client-oXO2WCPD.mjs → client-5uvDppD8.mjs} +23 -21
  6. package/dist/client-5uvDppD8.mjs.map +1 -0
  7. package/dist/{command-helpers-DtavI0wJ.mjs → command-helpers-4UNsRRc4.mjs} +427 -9
  8. package/dist/command-helpers-4UNsRRc4.mjs.map +1 -0
  9. package/dist/commands/contract-emit.d.mts.map +1 -1
  10. package/dist/commands/contract-emit.mjs +1 -1
  11. package/dist/commands/contract-infer.d.mts.map +1 -1
  12. package/dist/commands/contract-infer.mjs +1 -1
  13. package/dist/commands/db-init.d.mts.map +1 -1
  14. package/dist/commands/db-init.mjs +33 -7
  15. package/dist/commands/db-init.mjs.map +1 -1
  16. package/dist/commands/db-schema.d.mts.map +1 -1
  17. package/dist/commands/db-schema.mjs +3 -4
  18. package/dist/commands/db-schema.mjs.map +1 -1
  19. package/dist/commands/db-sign.d.mts.map +1 -1
  20. package/dist/commands/db-sign.mjs +6 -7
  21. package/dist/commands/db-sign.mjs.map +1 -1
  22. package/dist/commands/db-update.d.mts.map +1 -1
  23. package/dist/commands/db-update.mjs +36 -8
  24. package/dist/commands/db-update.mjs.map +1 -1
  25. package/dist/commands/db-verify.d.mts.map +1 -1
  26. package/dist/commands/db-verify.mjs +1 -1
  27. package/dist/commands/migrate.d.mts +5 -1
  28. package/dist/commands/migrate.d.mts.map +1 -1
  29. package/dist/commands/migrate.mjs +79 -39
  30. package/dist/commands/migrate.mjs.map +1 -1
  31. package/dist/commands/migration-check.d.mts +4 -3
  32. package/dist/commands/migration-check.d.mts.map +1 -1
  33. package/dist/commands/migration-check.mjs +1 -280
  34. package/dist/commands/migration-graph.d.mts +1 -1
  35. package/dist/commands/migration-graph.d.mts.map +1 -1
  36. package/dist/commands/migration-graph.mjs +3 -4
  37. package/dist/commands/migration-graph.mjs.map +1 -1
  38. package/dist/commands/migration-list.d.mts +63 -12
  39. package/dist/commands/migration-list.d.mts.map +1 -1
  40. package/dist/commands/migration-list.mjs +2 -103
  41. package/dist/commands/migration-log.d.mts.map +1 -1
  42. package/dist/commands/migration-log.mjs +3 -4
  43. package/dist/commands/migration-log.mjs.map +1 -1
  44. package/dist/commands/migration-new.d.mts.map +1 -1
  45. package/dist/commands/migration-new.mjs +33 -38
  46. package/dist/commands/migration-new.mjs.map +1 -1
  47. package/dist/commands/migration-plan.d.mts +2 -1
  48. package/dist/commands/migration-plan.d.mts.map +1 -1
  49. package/dist/commands/migration-plan.mjs +1 -1
  50. package/dist/commands/migration-show.d.mts +4 -55
  51. package/dist/commands/migration-show.d.mts.map +1 -1
  52. package/dist/commands/migration-show.mjs +62 -153
  53. package/dist/commands/migration-show.mjs.map +1 -1
  54. package/dist/commands/migration-status.d.mts +5 -40
  55. package/dist/commands/migration-status.d.mts.map +1 -1
  56. package/dist/commands/migration-status.mjs +93 -67
  57. package/dist/commands/migration-status.mjs.map +1 -1
  58. package/dist/commands/ref.d.mts +1 -1
  59. package/dist/commands/ref.d.mts.map +1 -1
  60. package/dist/commands/ref.mjs +34 -9
  61. package/dist/commands/ref.mjs.map +1 -1
  62. package/dist/config-loader-B6sJjXTv.mjs.map +1 -1
  63. package/dist/config-loader.d.mts.map +1 -1
  64. package/dist/{contract-emit-o-8VmdQX.mjs → contract-emit-C-CFGZsI.mjs} +9 -6
  65. package/dist/{contract-emit-o-8VmdQX.mjs.map → contract-emit-C-CFGZsI.mjs.map} +1 -1
  66. package/dist/{contract-emit-CmsklifJ.mjs → contract-emit-CuUzzM46.mjs} +5 -6
  67. package/dist/{contract-emit-CmsklifJ.mjs.map → contract-emit-CuUzzM46.mjs.map} +1 -1
  68. package/dist/{contract-enrichment-Dani0mMW.mjs → contract-enrichment-XmUPhmsS.mjs} +4 -25
  69. package/dist/contract-enrichment-XmUPhmsS.mjs.map +1 -0
  70. package/dist/{contract-infer-pKkiCt7C.mjs → contract-infer-C98ZaRhp.mjs} +3 -4
  71. package/dist/{contract-infer-pKkiCt7C.mjs.map → contract-infer-C98ZaRhp.mjs.map} +1 -1
  72. package/dist/contract-space-aggregate-loader-CVHGuA35.mjs +170 -0
  73. package/dist/contract-space-aggregate-loader-CVHGuA35.mjs.map +1 -0
  74. package/dist/{db-verify-AoIUriL4.mjs → db-verify-BWl1Yxi-.mjs} +6 -7
  75. package/dist/{db-verify-AoIUriL4.mjs.map → db-verify-BWl1Yxi-.mjs.map} +1 -1
  76. package/dist/exports/control-api.d.mts +1 -1
  77. package/dist/exports/control-api.d.mts.map +1 -1
  78. package/dist/exports/control-api.mjs +3 -3
  79. package/dist/exports/index.d.mts.map +1 -1
  80. package/dist/exports/index.mjs +1 -1
  81. package/dist/exports/index.mjs.map +1 -1
  82. package/dist/exports/init-output.d.mts.map +1 -1
  83. package/dist/exports/init-output.mjs +1 -1
  84. package/dist/extension-pack-inputs-BiY86HbQ.mjs +62 -0
  85. package/dist/extension-pack-inputs-BiY86HbQ.mjs.map +1 -0
  86. package/dist/{framework-components-65gOHkHB.mjs → framework-components-DTcjouhS.mjs} +2 -2
  87. package/dist/{framework-components-65gOHkHB.mjs.map → framework-components-DTcjouhS.mjs.map} +1 -1
  88. package/dist/{global-flags-CdE7M0d9.d.mts → global-flags-DWsQ6SSI.d.mts} +1 -1
  89. package/dist/global-flags-DWsQ6SSI.d.mts.map +1 -0
  90. package/dist/glyph-mode-CBB4emzO.d.mts +5 -0
  91. package/dist/glyph-mode-CBB4emzO.d.mts.map +1 -0
  92. package/dist/{graph-render-DJVv0_uf.mjs → graph-render-D2FnLpuK.mjs} +1 -1
  93. package/dist/{graph-render-DJVv0_uf.mjs.map → graph-render-D2FnLpuK.mjs.map} +1 -1
  94. package/dist/{init-Db5Itt5r.mjs → init-C7PvN163.mjs} +5 -5
  95. package/dist/{init-Db5Itt5r.mjs.map → init-C7PvN163.mjs.map} +1 -1
  96. package/dist/{inspect-live-schema-LeWvkZVz.mjs → inspect-live-schema-BRCWQ-Sr.mjs} +5 -5
  97. package/dist/{inspect-live-schema-LeWvkZVz.mjs.map → inspect-live-schema-BRCWQ-Sr.mjs.map} +1 -1
  98. package/dist/migration-check-DoskM1nB.mjs +341 -0
  99. package/dist/migration-check-DoskM1nB.mjs.map +1 -0
  100. package/dist/migration-cli.d.mts.map +1 -1
  101. package/dist/migration-cli.mjs +4 -4
  102. package/dist/migration-cli.mjs.map +1 -1
  103. package/dist/{migration-command-scaffold-BtkunvFQ.mjs → migration-command-scaffold-CXLkoIJx.mjs} +5 -5
  104. package/dist/{migration-command-scaffold-BtkunvFQ.mjs.map → migration-command-scaffold-CXLkoIJx.mjs.map} +1 -1
  105. package/dist/migration-list-B2-iQ5Jd.mjs +646 -0
  106. package/dist/migration-list-B2-iQ5Jd.mjs.map +1 -0
  107. package/dist/{migration-plan-C2jeH1J5.mjs → migration-plan-BqmIKQpZ.mjs} +341 -88
  108. package/dist/migration-plan-BqmIKQpZ.mjs.map +1 -0
  109. package/dist/{migration-types-BXWvz12q.d.mts → migration-types-q64xAI_J.d.mts} +1 -1
  110. package/dist/{migration-types-BXWvz12q.d.mts.map → migration-types-q64xAI_J.d.mts.map} +1 -1
  111. package/dist/{migrations-CwZMa1Ck.mjs → migrations-BcVTutso.mjs} +12 -13
  112. package/dist/migrations-BcVTutso.mjs.map +1 -0
  113. package/dist/{output-BlsrGMEF.mjs → output-B60Gw5fu.mjs} +1 -1
  114. package/dist/{output-BlsrGMEF.mjs.map → output-B60Gw5fu.mjs.map} +1 -1
  115. package/dist/{progress-adapter-DFfvZcYL.mjs → progress-adapter-xASh41wr.mjs} +1 -1
  116. package/dist/{progress-adapter-DFfvZcYL.mjs.map → progress-adapter-xASh41wr.mjs.map} +1 -1
  117. package/dist/ref-advancement-DRh5Nquq.mjs +50 -0
  118. package/dist/ref-advancement-DRh5Nquq.mjs.map +1 -0
  119. package/dist/{types-C9FfXb1l.d.mts → types-CEtm6v6a.d.mts} +5 -11
  120. package/dist/types-CEtm6v6a.d.mts.map +1 -0
  121. package/dist/{verify-Bom75OYI.mjs → verify-DOHbbrub.mjs} +2 -2
  122. package/dist/{verify-Bom75OYI.mjs.map → verify-DOHbbrub.mjs.map} +1 -1
  123. package/package.json +20 -20
  124. package/src/commands/db-init.ts +48 -2
  125. package/src/commands/db-update.ts +45 -0
  126. package/src/commands/migrate.ts +120 -40
  127. package/src/commands/migration-check.ts +43 -83
  128. package/src/commands/migration-list.ts +173 -74
  129. package/src/commands/migration-new.ts +44 -48
  130. package/src/commands/migration-plan.ts +359 -128
  131. package/src/commands/migration-show.ts +65 -284
  132. package/src/commands/migration-status.ts +131 -99
  133. package/src/commands/ref.ts +46 -6
  134. package/src/control-api/client.ts +0 -1
  135. package/src/control-api/contract-enrichment.ts +6 -42
  136. package/src/control-api/operations/contract-emit.ts +7 -2
  137. package/src/control-api/operations/db-verify.ts +9 -5
  138. package/src/control-api/operations/migration-apply.ts +11 -19
  139. package/src/control-api/types.ts +0 -7
  140. package/src/migration-cli.ts +4 -4
  141. package/src/utils/cli-errors.ts +224 -0
  142. package/src/utils/command-helpers.ts +9 -4
  143. package/src/utils/contract-space-aggregate-loader.ts +221 -117
  144. package/src/utils/formatters/migration-list-data-column.ts +115 -0
  145. package/src/utils/formatters/migration-list-graph-layout.ts +268 -0
  146. package/src/utils/formatters/migration-list-graph-render.ts +314 -0
  147. package/src/utils/formatters/migration-list-render.ts +194 -0
  148. package/src/utils/formatters/migration-list-styler.ts +61 -0
  149. package/src/utils/formatters/migrations.ts +29 -38
  150. package/src/utils/glyph-mode.ts +22 -0
  151. package/src/utils/integrity-violation-to-check-failure.ts +130 -0
  152. package/src/utils/plan-resolution.ts +257 -0
  153. package/src/utils/ref-advancement.ts +68 -0
  154. package/src/utils/terminal-ui.ts +42 -1
  155. package/dist/cli-errors-Czmx92Zy.d.mts +0 -3
  156. package/dist/cli-errors-Djtz98Vm.mjs +0 -71
  157. package/dist/cli-errors-Djtz98Vm.mjs.map +0 -1
  158. package/dist/client-oXO2WCPD.mjs.map +0 -1
  159. package/dist/command-helpers-DtavI0wJ.mjs.map +0 -1
  160. package/dist/commands/migration-check.mjs.map +0 -1
  161. package/dist/commands/migration-list.mjs.map +0 -1
  162. package/dist/contract-enrichment-Dani0mMW.mjs.map +0 -1
  163. package/dist/contract-space-aggregate-loader-BmNQwlws.mjs +0 -160
  164. package/dist/contract-space-aggregate-loader-BmNQwlws.mjs.map +0 -1
  165. package/dist/global-flags-CdE7M0d9.d.mts.map +0 -1
  166. package/dist/migration-plan-C2jeH1J5.mjs.map +0 -1
  167. package/dist/migrations-CwZMa1Ck.mjs.map +0 -1
  168. package/dist/terminal-ui-BiB_8KNo.mjs +0 -379
  169. package/dist/terminal-ui-BiB_8KNo.mjs.map +0 -1
  170. package/dist/types-C9FfXb1l.d.mts.map +0 -1
@@ -0,0 +1,194 @@
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
+ import type { GlyphMode } from '../glyph-mode';
11
+ import {
12
+ computeMigrationDirNameWidth,
13
+ formatMigrationDataColumn,
14
+ migrationListEmptySource,
15
+ migrationListForwardArrow,
16
+ migrationListKindGlyph,
17
+ } from './migration-list-data-column';
18
+
19
+ export type { MigrationEdgeKind } from '@prisma-next/migration-tools/migration-list-graph-topology';
20
+ export type {
21
+ MigrationListEntry,
22
+ MigrationListResult,
23
+ MigrationSpaceListEntry,
24
+ } from '@prisma-next/migration-tools/migration-list-types';
25
+ export type { GlyphMode } from '../glyph-mode';
26
+
27
+ /**
28
+ * Semantic styler for `migration list` output tokens. Token-typed so
29
+ * the renderer composes presentation-neutral fragments and the styler
30
+ * decides how each token kind is decorated (ANSI codes, plain text,
31
+ * etc.). The renderer pads with raw spaces *outside* styled tokens so
32
+ * visible column widths stay stable regardless of what the styler
33
+ * emits — adding ANSI escape sequences never disturbs alignment.
34
+ *
35
+ * `invariants` and `refs` receive the underlying string arrays rather
36
+ * than a pre-joined string so per-element styling (e.g. distinguishing
37
+ * the live-DB `db` marker from user-named refs) is possible without
38
+ * having to re-parse a joined block.
39
+ */
40
+ export interface MigrationListStyler {
41
+ kind(text: string): string;
42
+ dirName(text: string): string;
43
+ sourceHash(text: string): string;
44
+ destHash(text: string): string;
45
+ glyph(text: string): string;
46
+ lane(text: string): string;
47
+ invariants(ids: readonly string[]): string;
48
+ refs(names: readonly string[]): string;
49
+ spaceHeading(text: string): string;
50
+ summary(text: string): string;
51
+ emptyState(text: string): string;
52
+ }
53
+
54
+ export const IDENTITY_MIGRATION_LIST_STYLER: MigrationListStyler = {
55
+ kind: (text) => text,
56
+ dirName: (text) => text,
57
+ sourceHash: (text) => text,
58
+ destHash: (text) => text,
59
+ glyph: (text) => text,
60
+ lane: (text) => text,
61
+ invariants: (ids) => `{${ids.join(', ')}}`,
62
+ refs: (names) => `(${names.join(', ')})`,
63
+ spaceHeading: (text) => text,
64
+ summary: (text) => text,
65
+ emptyState: (text) => text,
66
+ };
67
+
68
+ function resolveEdgeKind(
69
+ migrationHash: string,
70
+ kindByMigrationHash: ReadonlyMap<string, MigrationEdgeKind>,
71
+ ): MigrationEdgeKind {
72
+ return kindByMigrationHash.get(migrationHash) ?? 'forward';
73
+ }
74
+
75
+ function formatMigrationRow(
76
+ migration: MigrationListEntry,
77
+ dirNameWidth: number,
78
+ edgeKind: MigrationEdgeKind,
79
+ glyphMode: GlyphMode,
80
+ style: MigrationListStyler,
81
+ ): string {
82
+ const kindColumn = `${style.kind(migrationListKindGlyph(glyphMode, edgeKind))} `;
83
+ const data = formatMigrationDataColumn(migration, {
84
+ dirNameWidth,
85
+ edgeKind,
86
+ style,
87
+ forwardArrow: migrationListForwardArrow(glyphMode),
88
+ emptySource: migrationListEmptySource(glyphMode),
89
+ });
90
+ return `${kindColumn}${data}`;
91
+ }
92
+
93
+ function formatEmptyStateLine(spaceId: string, style: MigrationListStyler): string {
94
+ return style.emptyState(`There are no migrations in migrations/${spaceId}/ yet`);
95
+ }
96
+
97
+ function renderSpaceBlock(
98
+ spaceId: string,
99
+ migrations: readonly MigrationListEntry[],
100
+ multiSpace: boolean,
101
+ glyphMode: GlyphMode,
102
+ kindByMigrationHash: ReadonlyMap<string, MigrationEdgeKind>,
103
+ style: MigrationListStyler,
104
+ ): readonly string[] {
105
+ if (migrations.length === 0) {
106
+ const emptyLine = formatEmptyStateLine(spaceId, style);
107
+ if (!multiSpace) {
108
+ return [emptyLine];
109
+ }
110
+ return [style.spaceHeading(`${spaceId}:`), ` ${emptyLine}`];
111
+ }
112
+
113
+ const dirNameWidth = computeMigrationDirNameWidth(migrations);
114
+ const rows = migrations.map((entry) =>
115
+ formatMigrationRow(
116
+ entry,
117
+ dirNameWidth,
118
+ resolveEdgeKind(entry.migrationHash, kindByMigrationHash),
119
+ glyphMode,
120
+ style,
121
+ ),
122
+ );
123
+ if (!multiSpace) {
124
+ return rows;
125
+ }
126
+ return [style.spaceHeading(`${spaceId}:`), ...rows.map((row) => ` ${row}`)];
127
+ }
128
+
129
+ export function buildMigrationListTopologyBySpace(
130
+ result: MigrationListResult,
131
+ ): ReadonlyMap<string, MigrationListGraphTopology> {
132
+ const topologyBySpaceId = new Map<string, MigrationListGraphTopology>();
133
+ for (const space of result.spaces) {
134
+ topologyBySpaceId.set(space.spaceId, classifyMigrationListGraphTopology(space.migrations));
135
+ }
136
+ return topologyBySpaceId;
137
+ }
138
+
139
+ /**
140
+ * Compose the styled `migration list` output. The renderer is
141
+ * presentation-neutral — every token passes through `style` before
142
+ * landing in the output, so the same composition serves the pure-text
143
+ * path ({@link renderMigrationList} via
144
+ * {@link IDENTITY_MIGRATION_LIST_STYLER}) and the ANSI-styled CLI path
145
+ * (via the ANSI styler the CLI shell wires up).
146
+ */
147
+ export function renderMigrationListWithStyle(
148
+ result: MigrationListResult,
149
+ style: MigrationListStyler,
150
+ glyphMode: GlyphMode = 'unicode',
151
+ topologyBySpaceId: ReadonlyMap<
152
+ string,
153
+ MigrationListGraphTopology
154
+ > = buildMigrationListTopologyBySpace(result),
155
+ ): string {
156
+ const multiSpace = result.spaces.length > 1;
157
+ const lines: string[] = [];
158
+
159
+ for (let index = 0; index < result.spaces.length; index++) {
160
+ const space = result.spaces[index]!;
161
+ if (index > 0) {
162
+ lines.push('');
163
+ }
164
+ const topology = topologyBySpaceId.get(space.spaceId);
165
+ const kindByMigrationHash =
166
+ topology?.kindByMigrationHash ??
167
+ classifyMigrationListGraphTopology(space.migrations).kindByMigrationHash;
168
+ lines.push(
169
+ ...renderSpaceBlock(
170
+ space.spaceId,
171
+ space.migrations,
172
+ multiSpace,
173
+ glyphMode,
174
+ kindByMigrationHash,
175
+ style,
176
+ ),
177
+ );
178
+ }
179
+
180
+ const totalMigrations = result.spaces.reduce(
181
+ (count, space) => count + space.migrations.length,
182
+ 0,
183
+ );
184
+ if (totalMigrations > 0) {
185
+ lines.push('');
186
+ lines.push(style.summary(result.summary));
187
+ }
188
+
189
+ return lines.join('\n');
190
+ }
191
+
192
+ export function renderMigrationList(result: MigrationListResult): string {
193
+ return renderMigrationListWithStyle(result, IDENTITY_MIGRATION_LIST_STYLER);
194
+ }
@@ -0,0 +1,61 @@
1
+ import { bold, cyan, cyanBright, dim, green, greenBright, yellow } from 'colorette';
2
+ import { IDENTITY_MIGRATION_LIST_STYLER, type MigrationListStyler } from './migration-list-render';
3
+
4
+ /**
5
+ * The reserved ref name for the live-database marker. Treated as a
6
+ * structurally distinct token from user-named refs so the styler can
7
+ * make it visually pop in `(refs)` decorations.
8
+ */
9
+ const DB_REF_NAME = 'db';
10
+
11
+ function styleRefName(name: string): string {
12
+ return name === DB_REF_NAME ? bold(greenBright(name)) : green(name);
13
+ }
14
+
15
+ /**
16
+ * Build a {@link MigrationListStyler} that decorates `migration list`
17
+ * tokens with ANSI SGR codes. When `useColor` is `false` (non-TTY,
18
+ * `--no-color`, `NO_COLOR=1`, piped output) the function returns the
19
+ * shared identity styler so callers get plain text with zero ANSI
20
+ * bytes — pipe-friendly by construction.
21
+ *
22
+ * Palette:
23
+ *
24
+ * - `dirName`: bold
25
+ * - `sourceHash`: dim cyan
26
+ * - `destHash`: bright cyan
27
+ * - `kind` (`*` / `↩` / `⟲`): bright — the signal; lanes and arrows dim
28
+ * - `glyph` (`→` / `⟲` / `∅`): dim
29
+ * - `lane` (graph gutter lines `│` and fan/join connectors `├─┐` / `├─┘`): dim
30
+ * - `invariants` (`{...}`): yellow
31
+ * - `refs` (`(...)`): green; the live-DB `db` marker inside is green-bold
32
+ * - `spaceHeading` (`<spaceId>:`): bold
33
+ * - `summary`: dim
34
+ * - `emptyState`: dim
35
+ */
36
+ export function createAnsiMigrationListStyler(opts: {
37
+ readonly useColor: boolean;
38
+ }): MigrationListStyler {
39
+ if (!opts.useColor) {
40
+ return IDENTITY_MIGRATION_LIST_STYLER;
41
+ }
42
+ return {
43
+ // Kind glyphs stay bright in both flat and graph views; lanes carry the dim gutter.
44
+ kind: (text) => text,
45
+ dirName: (text) => bold(text),
46
+ sourceHash: (text) => dim(cyan(text)),
47
+ destHash: (text) => cyanBright(text),
48
+ glyph: (text) => dim(text),
49
+ lane: (text) => dim(text),
50
+ invariants: (ids) => yellow(`{${ids.join(', ')}}`),
51
+ refs: (names) => {
52
+ const open = green('(');
53
+ const close = green(')');
54
+ const separator = green(', ');
55
+ return open + names.map(styleRefName).join(separator) + close;
56
+ },
57
+ spaceHeading: (text) => bold(text),
58
+ summary: (text) => dim(text),
59
+ emptyState: (text) => dim(text),
60
+ };
61
+ }
@@ -82,6 +82,8 @@ export interface MigrationCommandResult {
82
82
  * into a single ambiguous list. See {@link AggregatePerSpaceExecutionEntry}.
83
83
  */
84
84
  readonly perSpace?: ReadonlyArray<AggregatePerSpaceExecutionEntry>;
85
+ readonly advancedRef?: { readonly name: string; readonly hash: string } | null;
86
+ readonly plannedAdvanceRef?: { readonly name: string; readonly hash: string } | null;
85
87
  readonly summary: string;
86
88
  readonly timings: {
87
89
  readonly total: number;
@@ -208,6 +210,15 @@ export function formatMigrationPlanOutput(
208
210
  lines.push(`${formatDimText(`Destination hash: ${result.plan.destination.storageHash}`)}`);
209
211
  }
210
212
 
213
+ if (result.plannedAdvanceRef) {
214
+ lines.push('');
215
+ lines.push(
216
+ formatDimText(
217
+ `Would advance ref "${result.plannedAdvanceRef.name}" → ${result.plannedAdvanceRef.hash}`,
218
+ ),
219
+ );
220
+ }
221
+
211
222
  // Statement preview (any family that implements OperationPreviewCapable)
212
223
  const preview = result.plan?.preview;
213
224
  if (preview) {
@@ -260,6 +271,7 @@ export interface MigrationApplyCommandOutputResult {
260
271
  readonly timings?: {
261
272
  readonly total: number;
262
273
  };
274
+ readonly advancedRef?: { readonly name: string; readonly hash: string } | null;
263
275
  }
264
276
 
265
277
  export function formatMigrationApplyCommandOutput(
@@ -284,6 +296,13 @@ export function formatMigrationApplyCommandOutput(
284
296
  }
285
297
  }
286
298
 
299
+ if (result.advancedRef) {
300
+ lines.push('');
301
+ lines.push(
302
+ formatDimText(`Advanced ref "${result.advancedRef.name}" → ${result.advancedRef.hash}`),
303
+ );
304
+ }
305
+
287
306
  lines.push('');
288
307
  lines.push(formatDimText('Next: prisma-next migration status'));
289
308
 
@@ -295,9 +314,7 @@ export function formatMigrationApplyCommandOutput(
295
314
  return lines.join('\n');
296
315
  }
297
316
 
298
- interface MigrationShowSpacePresent {
299
- readonly kind: 'present';
300
- readonly spaceId: string;
317
+ interface MigrationShowPresent {
301
318
  readonly dirName: string;
302
319
  readonly dirPath: string;
303
320
  readonly from: string | null;
@@ -313,22 +330,11 @@ interface MigrationShowSpacePresent {
313
330
  readonly summary: string;
314
331
  }
315
332
 
316
- interface MigrationShowSpaceMissing {
317
- readonly kind: 'missing';
318
- readonly spaceId: string;
319
- readonly summary: string;
320
- }
321
-
322
- type MigrationShowSpaceResult = MigrationShowSpacePresent | MigrationShowSpaceMissing;
323
-
324
333
  interface MigrationShowResult {
325
- readonly spaces: readonly MigrationShowSpaceResult[];
334
+ readonly migration: MigrationShowPresent;
326
335
  }
327
336
 
328
- function formatSpaceShowBlock(
329
- space: MigrationShowSpacePresent,
330
- useColor: boolean,
331
- ): readonly string[] {
337
+ function formatSpaceShowBlock(space: MigrationShowPresent, useColor: boolean): readonly string[] {
332
338
  const formatGreen = createColorFormatter(useColor, green);
333
339
  const formatYellow = createColorFormatter(useColor, yellow);
334
340
  const formatDimText = (text: string) => formatDim(useColor, text);
@@ -384,28 +390,7 @@ export function formatMigrationShowOutput(result: MigrationShowResult, flags: Gl
384
390
  }
385
391
 
386
392
  const useColor = flags.color !== false;
387
- const formatDimText = (text: string) => formatDim(useColor, text);
388
- const multipleSpaces = result.spaces.length > 1;
389
- const lines: string[] = [];
390
-
391
- for (let i = 0; i < result.spaces.length; i++) {
392
- const space = result.spaces[i]!;
393
- if (multipleSpaces) {
394
- lines.push(formatDimText(`── ${space.spaceId} ──`));
395
- }
396
- if (space.kind === 'missing') {
397
- lines.push(formatDimText(` ${space.summary}`));
398
- } else {
399
- for (const line of formatSpaceShowBlock(space, useColor)) {
400
- lines.push(line);
401
- }
402
- }
403
- if (i < result.spaces.length - 1) {
404
- lines.push('');
405
- }
406
- }
407
-
408
- return lines.join('\n');
393
+ return formatSpaceShowBlock(result.migration, useColor).join('\n');
409
394
  }
410
395
 
411
396
  /**
@@ -470,6 +455,12 @@ export function formatMigrationApplyOutput(
470
455
  if (isVerbose(flags, 1)) {
471
456
  lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);
472
457
  }
458
+
459
+ if (result.advancedRef) {
460
+ lines.push(
461
+ formatDimText(`Advanced ref "${result.advancedRef.name}" → ${result.advancedRef.hash}`),
462
+ );
463
+ }
473
464
  }
474
465
 
475
466
  return lines.join('\n');
@@ -0,0 +1,22 @@
1
+ export type GlyphMode = 'unicode' | 'ascii';
2
+
3
+ export interface GlyphModeInput {
4
+ readonly isTTY: boolean;
5
+ readonly env: Readonly<Record<string, string | undefined>>;
6
+ }
7
+
8
+ function localeString(env: Readonly<Record<string, string | undefined>>): string {
9
+ return env['LC_ALL'] ?? env['LC_CTYPE'] ?? env['LANG'] ?? '';
10
+ }
11
+
12
+ function isUtf8Locale(env: Readonly<Record<string, string | undefined>>): boolean {
13
+ const locale = localeString(env);
14
+ if (locale.length === 0) return false;
15
+ return /UTF-8|utf8/i.test(locale);
16
+ }
17
+
18
+ export function detectGlyphMode(input: GlyphModeInput): GlyphMode {
19
+ if (!input.isTTY) return 'ascii';
20
+ if (!isUtf8Locale(input.env)) return 'ascii';
21
+ return 'unicode';
22
+ }
@@ -0,0 +1,130 @@
1
+ import type { IntegrityViolation } from '@prisma-next/migration-tools/aggregate';
2
+ import { join, relative } from 'pathe';
3
+
4
+ export interface CheckFailure {
5
+ readonly pnCode: string;
6
+ readonly where: string;
7
+ readonly why: string;
8
+ readonly fix: string;
9
+ }
10
+
11
+ function migrationPathRelative(dirPath: string): string {
12
+ return relative(process.cwd(), dirPath);
13
+ }
14
+
15
+ function migrationFileRelative(dirPath: string, fileName: string): string {
16
+ return join(migrationPathRelative(dirPath), fileName);
17
+ }
18
+
19
+ /**
20
+ * Map one {@link IntegrityViolation} onto a `migration check` failure row.
21
+ * Sole catalogue mapping from integrity violations to `PN-MIG-CHECK-*`.
22
+ */
23
+ export function integrityViolationToCheckFailure(
24
+ violation: IntegrityViolation,
25
+ migrationsDir: string,
26
+ ): CheckFailure {
27
+ const spaceRelative = (spaceId: string): string =>
28
+ migrationPathRelative(join(migrationsDir, spaceId));
29
+ const packageRelative = (spaceId: string, dirName: string): string =>
30
+ migrationPathRelative(join(migrationsDir, spaceId, dirName));
31
+ const refRelative = (spaceId: string, refName: string): string =>
32
+ migrationPathRelative(join(migrationsDir, spaceId, 'refs', `${refName}.json`));
33
+
34
+ switch (violation.kind) {
35
+ case 'hashMismatch':
36
+ return {
37
+ pnCode: 'PN-MIG-CHECK-001',
38
+ where: migrationFileRelative(
39
+ join(migrationsDir, violation.spaceId, violation.dirName),
40
+ 'migration.json',
41
+ ),
42
+ why: `Stored hash ${violation.stored} does not match recomputed hash ${violation.computed}`,
43
+ fix: 'Re-emit the migration package or restore from version control.',
44
+ };
45
+ case 'providedInvariantsMismatch':
46
+ return {
47
+ pnCode: 'PN-MIG-CHECK-002',
48
+ where: packageRelative(violation.spaceId, violation.dirName),
49
+ why: `Migration "${violation.dirName}" providedInvariants in migration.json disagrees with ops.json.`,
50
+ fix: 'Re-emit the migration package so migration.json and ops.json agree.',
51
+ };
52
+ case 'packageUnloadable':
53
+ return {
54
+ pnCode: 'PN-MIG-CHECK-002',
55
+ where: packageRelative(violation.spaceId, violation.dirName),
56
+ why: `Migration "${violation.dirName}" could not be loaded: ${violation.detail}`,
57
+ fix: 'Re-emit the migration package or restore from version control.',
58
+ };
59
+ case 'sameSourceAndTarget':
60
+ return {
61
+ pnCode: 'PN-MIG-CHECK-007',
62
+ where: packageRelative(violation.spaceId, violation.dirName),
63
+ why: `Migration "${violation.dirName}" in space "${violation.spaceId}" has source equal to target (${violation.hash}) with no data invariant — a true no-op self-edge.`,
64
+ fix: 'Add a data operation if this self-edge was meant to carry a data invariant, or delete the migration if it is a true no-op.',
65
+ };
66
+ case 'orphanSpaceDir':
67
+ return {
68
+ pnCode: 'PN-MIG-CHECK-008',
69
+ where: spaceRelative(violation.spaceId),
70
+ why: `Contract-space directory "${violation.spaceId}" exists on disk but no extension declares it.`,
71
+ fix: 'Remove the orphan directory, or declare the extension in `extensionPacks`.',
72
+ };
73
+ case 'declaredButUnmigrated':
74
+ return {
75
+ pnCode: 'PN-MIG-CHECK-009',
76
+ where: spaceRelative(violation.spaceId),
77
+ why: `Extension "${violation.spaceId}" is declared in \`extensionPacks\` but has no on-disk migrations directory.`,
78
+ fix: 'Re-emit the extension contract-space artefacts with `prisma-next contract emit` and migration planning, or remove the extension from `extensionPacks` if it is unused.',
79
+ };
80
+ case 'headRefMissing':
81
+ return {
82
+ pnCode: 'PN-MIG-CHECK-010',
83
+ where: refRelative(violation.spaceId, 'head'),
84
+ why: `Head ref \`refs/head.json\` is missing for contract space "${violation.spaceId}".`,
85
+ fix: 'Re-emit the contract-space migrations and head ref artefacts, or restore `refs/head.json` from version control.',
86
+ };
87
+ case 'headRefNotInGraph':
88
+ return {
89
+ pnCode: 'PN-MIG-CHECK-011',
90
+ where: refRelative(violation.spaceId, 'head'),
91
+ why: `Head ref ${violation.hash} for contract space "${violation.spaceId}" is not present in its migration graph.`,
92
+ fix: 'Re-emit the contract space migrations, or restore the missing migration package.',
93
+ };
94
+ case 'refUnreadable':
95
+ return {
96
+ pnCode: 'PN-MIG-CHECK-012',
97
+ where: refRelative(violation.spaceId, violation.refName),
98
+ why: `Ref "${violation.refName}" for contract space "${violation.spaceId}" is unreadable: ${violation.detail}`,
99
+ fix: 'Repair or remove the corrupt ref file.',
100
+ };
101
+ case 'targetMismatch':
102
+ return {
103
+ pnCode: 'PN-MIG-CHECK-013',
104
+ where: spaceRelative(violation.spaceId),
105
+ why: `Contract space "${violation.spaceId}" targets "${violation.actual}" but the project targets "${violation.expected}".`,
106
+ fix: 'Update the extension to target the configured database, or change the project target.',
107
+ };
108
+ case 'disjointness':
109
+ return {
110
+ pnCode: 'PN-MIG-CHECK-014',
111
+ where: migrationPathRelative(migrationsDir),
112
+ why: `Storage element "${violation.element}" is claimed by multiple contract spaces: ${violation.claimedBy.join(', ')}.`,
113
+ fix: 'Update the contracts so each storage element is owned by exactly one contract space.',
114
+ };
115
+ case 'contractUnreadable':
116
+ return {
117
+ pnCode: 'PN-MIG-CHECK-015',
118
+ where: migrationFileRelative(join(migrationsDir, violation.spaceId), 'contract.json'),
119
+ why: `Contract for space "${violation.spaceId}" is unreadable: ${violation.detail}`,
120
+ fix: 'Re-emit the extension contract artefacts, or fix the descriptor producing the invalid contract.',
121
+ };
122
+ case 'duplicateMigrationHash':
123
+ return {
124
+ pnCode: 'PN-MIG-CHECK-016',
125
+ where: spaceRelative(violation.spaceId),
126
+ why: `Multiple migrations in space "${violation.spaceId}" share migrationHash "${violation.migrationHash}" (${violation.dirNames.join(', ')}).`,
127
+ fix: 'Re-emit one of the conflicting packages so each migrationHash is unique.',
128
+ };
129
+ }
130
+ }