@prisma-next/cli 0.5.0-dev.4 → 0.5.0-dev.6

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 (99) hide show
  1. package/dist/agent-skill-mongo.md +63 -31
  2. package/dist/agent-skill-postgres.md +1 -1
  3. package/dist/cli.mjs +119 -13
  4. package/dist/cli.mjs.map +1 -1
  5. package/dist/{client-TG7rbCWT.mjs → client-CrsnY58k.mjs} +4 -4
  6. package/dist/{client-TG7rbCWT.mjs.map → client-CrsnY58k.mjs.map} +1 -1
  7. package/dist/commands/contract-emit.mjs +2 -2
  8. package/dist/commands/contract-infer.mjs +2 -2
  9. package/dist/commands/db-init.mjs +7 -7
  10. package/dist/commands/db-schema.mjs +5 -5
  11. package/dist/commands/db-sign.mjs +7 -7
  12. package/dist/commands/db-update.mjs +7 -7
  13. package/dist/commands/db-verify.mjs +7 -7
  14. package/dist/commands/migration-apply.mjs +6 -6
  15. package/dist/commands/migration-new.mjs +5 -5
  16. package/dist/commands/migration-plan.mjs +6 -6
  17. package/dist/commands/migration-ref.d.mts +1 -1
  18. package/dist/commands/migration-ref.mjs +4 -4
  19. package/dist/commands/migration-show.d.mts +1 -1
  20. package/dist/commands/migration-show.mjs +6 -6
  21. package/dist/commands/migration-status.mjs +2 -2
  22. package/dist/{config-loader-_W4T21X1.mjs → config-loader-C25b63rJ.mjs} +1 -1
  23. package/dist/{config-loader-_W4T21X1.mjs.map → config-loader-C25b63rJ.mjs.map} +1 -1
  24. package/dist/config-loader.mjs +1 -1
  25. package/dist/contract-emit--feXyNd7.mjs +4 -0
  26. package/dist/{contract-emit-DpPjuFy-.mjs → contract-emit-NJ01hiiv.mjs} +8 -8
  27. package/dist/{contract-emit-DpPjuFy-.mjs.map → contract-emit-NJ01hiiv.mjs.map} +1 -1
  28. package/dist/{contract-emit-CQfj7xJn.mjs → contract-emit-V5SSitUT.mjs} +6 -6
  29. package/dist/{contract-emit-CQfj7xJn.mjs.map → contract-emit-V5SSitUT.mjs.map} +1 -1
  30. package/dist/{contract-enrichment-CGW6mm-E.mjs → contract-enrichment-CAOELa-H.mjs} +1 -1
  31. package/dist/{contract-enrichment-CGW6mm-E.mjs.map → contract-enrichment-CAOELa-H.mjs.map} +1 -1
  32. package/dist/{contract-infer-BS4kIX9c.mjs → contract-infer-D9cC3rJm.mjs} +4 -4
  33. package/dist/{contract-infer-BS4kIX9c.mjs.map → contract-infer-D9cC3rJm.mjs.map} +1 -1
  34. package/dist/exports/control-api.mjs +4 -4
  35. package/dist/exports/index.mjs +2 -2
  36. package/dist/exports/init-output.d.mts +39 -0
  37. package/dist/exports/init-output.d.mts.map +1 -0
  38. package/dist/exports/init-output.mjs +3 -0
  39. package/dist/{extract-operation-statements-DZUJNmL3.mjs → extract-operation-statements-DsFfxXVZ.mjs} +2 -2
  40. package/dist/{extract-operation-statements-DZUJNmL3.mjs.map → extract-operation-statements-DsFfxXVZ.mjs.map} +1 -1
  41. package/dist/{extract-sql-ddl-DDMX-9mz.mjs → extract-sql-ddl-D9UbZDyz.mjs} +1 -1
  42. package/dist/{extract-sql-ddl-DDMX-9mz.mjs.map → extract-sql-ddl-D9UbZDyz.mjs.map} +1 -1
  43. package/dist/{framework-components-DfZKQBQ2.mjs → framework-components-Cr--XBKy.mjs} +2 -2
  44. package/dist/{framework-components-DfZKQBQ2.mjs.map → framework-components-Cr--XBKy.mjs.map} +1 -1
  45. package/dist/init-C5220SY9.mjs +2062 -0
  46. package/dist/init-C5220SY9.mjs.map +1 -0
  47. package/dist/{inspect-live-schema-BsoFVoS1.mjs → inspect-live-schema-yrHAvG71.mjs} +6 -6
  48. package/dist/{inspect-live-schema-BsoFVoS1.mjs.map → inspect-live-schema-yrHAvG71.mjs.map} +1 -1
  49. package/dist/migration-cli.mjs +1 -1
  50. package/dist/{migration-command-scaffold-DOXnheFa.mjs → migration-command-scaffold-B3B09et6.mjs} +6 -6
  51. package/dist/{migration-command-scaffold-DOXnheFa.mjs.map → migration-command-scaffold-B3B09et6.mjs.map} +1 -1
  52. package/dist/{migration-status-Ry3TnEya.mjs → migration-status-DUMiH8_G.mjs} +6 -6
  53. package/dist/{migration-status-Ry3TnEya.mjs.map → migration-status-DUMiH8_G.mjs.map} +1 -1
  54. package/dist/{migrations-fU0xoKjS.mjs → migrations-Bo5WtTla.mjs} +2 -2
  55. package/dist/{migrations-fU0xoKjS.mjs.map → migrations-Bo5WtTla.mjs.map} +1 -1
  56. package/dist/output-BpcQrnnq.mjs +103 -0
  57. package/dist/output-BpcQrnnq.mjs.map +1 -0
  58. package/dist/{progress-adapter-B-YvmcDu.mjs → progress-adapter-DvQWB1nK.mjs} +1 -1
  59. package/dist/{progress-adapter-B-YvmcDu.mjs.map → progress-adapter-DvQWB1nK.mjs.map} +1 -1
  60. package/dist/quick-reference-mongo.md +34 -13
  61. package/dist/quick-reference-postgres.md +11 -9
  62. package/dist/{result-handler-BJwA7ufw.mjs → result-handler-Ba3zWQsI.mjs} +4 -77
  63. package/dist/result-handler-Ba3zWQsI.mjs.map +1 -0
  64. package/dist/{terminal-ui-C5k88MmW.mjs → terminal-ui-C3ZLwQxK.mjs} +76 -2
  65. package/dist/terminal-ui-C3ZLwQxK.mjs.map +1 -0
  66. package/dist/{validate-contract-deps-esa-VQ0h.mjs → validate-contract-deps-B_Cs29TL.mjs} +1 -1
  67. package/dist/{validate-contract-deps-esa-VQ0h.mjs.map → validate-contract-deps-B_Cs29TL.mjs.map} +1 -1
  68. package/dist/{verify-bl__PkXk.mjs → verify-Bkycc-Tf.mjs} +2 -2
  69. package/dist/{verify-bl__PkXk.mjs.map → verify-Bkycc-Tf.mjs.map} +1 -1
  70. package/package.json +21 -16
  71. package/src/commands/init/detect-pnpm-catalog.ts +141 -0
  72. package/src/commands/init/errors.ts +254 -0
  73. package/src/commands/init/exit-codes.ts +62 -0
  74. package/src/commands/init/hygiene-gitattributes.ts +97 -0
  75. package/src/commands/init/hygiene-gitignore.ts +48 -0
  76. package/src/commands/init/hygiene-package-scripts.ts +91 -0
  77. package/src/commands/init/index.ts +112 -7
  78. package/src/commands/init/init.ts +766 -144
  79. package/src/commands/init/inputs.ts +421 -0
  80. package/src/commands/init/output.ts +147 -0
  81. package/src/commands/init/probe-db.ts +308 -0
  82. package/src/commands/init/reinit-cleanup.ts +83 -0
  83. package/src/commands/init/templates/agent-skill-mongo.md +63 -31
  84. package/src/commands/init/templates/agent-skill-postgres.md +1 -1
  85. package/src/commands/init/templates/agent-skill.ts +25 -3
  86. package/src/commands/init/templates/code-templates.ts +125 -32
  87. package/src/commands/init/templates/env.ts +80 -0
  88. package/src/commands/init/templates/quick-reference-mongo.md +34 -13
  89. package/src/commands/init/templates/quick-reference-postgres.md +11 -9
  90. package/src/commands/init/templates/quick-reference.ts +42 -3
  91. package/src/commands/init/templates/tsconfig.ts +167 -5
  92. package/src/exports/init-output.ts +10 -0
  93. package/dist/contract-emit-fhNwwhkQ.mjs +0 -4
  94. package/dist/init-CQfo_4Ro.mjs +0 -430
  95. package/dist/init-CQfo_4Ro.mjs.map +0 -1
  96. package/dist/result-handler-BJwA7ufw.mjs.map +0 -1
  97. package/dist/terminal-ui-C5k88MmW.mjs.map +0 -1
  98. /package/dist/{cli-errors-C0JhVj0c.d.mts → cli-errors-BFYgBH3L.d.mts} +0 -0
  99. /package/dist/{cli-errors-DHq6GQGu.mjs → cli-errors-Cd79vmTH.mjs} +0 -0
@@ -1,6 +1,80 @@
1
1
  import { bold, cyan, dim, green, red, yellow } from "colorette";
2
2
  import * as clack from "@clack/prompts";
3
3
 
4
+ //#region src/utils/formatters/helpers.ts
5
+ /**
6
+ * Checks if verbose output is enabled at the specified level.
7
+ */
8
+ function isVerbose(flags, level) {
9
+ return (flags.verbose ?? 0) >= level;
10
+ }
11
+ /**
12
+ * Creates a color-aware formatter function.
13
+ * Returns a function that applies the color only if colors are enabled.
14
+ */
15
+ function createColorFormatter(useColor, colorFn) {
16
+ return useColor ? colorFn : (text) => text;
17
+ }
18
+ /**
19
+ * Formats text with dim styling if colors are enabled.
20
+ */
21
+ function formatDim(useColor, text) {
22
+ return useColor ? dim(text) : text;
23
+ }
24
+
25
+ //#endregion
26
+ //#region src/utils/formatters/errors.ts
27
+ /**
28
+ * Formats error output for human-readable display.
29
+ */
30
+ function formatErrorOutput(error, flags) {
31
+ const lines = [];
32
+ const useColor = flags.color !== false;
33
+ const formatRed = createColorFormatter(useColor, red);
34
+ const formatDimText = (text) => formatDim(useColor, text);
35
+ lines.push(`${formatRed("✖")} ${error.summary} (${error.code})`);
36
+ if (error.why) lines.push(`${formatDimText(` Why: ${error.why}`)}`);
37
+ if (error.fix) lines.push(`${formatDimText(` Fix: ${error.fix}`)}`);
38
+ if (error.where?.path) {
39
+ const whereLine = error.where.line ? `${error.where.path}:${error.where.line}` : error.where.path;
40
+ lines.push(`${formatDimText(` Where: ${whereLine}`)}`);
41
+ }
42
+ if (error.meta?.["conflicts"]) {
43
+ const conflicts = error.meta["conflicts"];
44
+ if (conflicts.length > 0) {
45
+ const maxToShow = isVerbose(flags, 1) ? conflicts.length : Math.min(3, conflicts.length);
46
+ const header = isVerbose(flags, 1) ? " Conflicts:" : ` Conflicts (showing ${maxToShow} of ${conflicts.length}):`;
47
+ lines.push(`${formatDimText(header)}`);
48
+ for (const conflict of conflicts.slice(0, maxToShow)) lines.push(`${formatDimText(` - [${conflict.kind}] ${conflict.summary}`)}`);
49
+ if (!isVerbose(flags, 1) && conflicts.length > maxToShow) lines.push(`${formatDimText(" Re-run with -v/--verbose to see all conflicts")}`);
50
+ }
51
+ }
52
+ if (error.meta?.["issues"]) {
53
+ const issues = error.meta["issues"];
54
+ if (issues.length > 0) {
55
+ const maxToShow = isVerbose(flags, 1) ? issues.length : Math.min(3, issues.length);
56
+ const header = isVerbose(flags, 1) ? " Issues:" : ` Issues (showing ${maxToShow} of ${issues.length}):`;
57
+ lines.push(`${formatDimText(header)}`);
58
+ for (const issue of issues.slice(0, maxToShow)) {
59
+ const kind = issue.kind ?? "issue";
60
+ const message = issue.message ?? "";
61
+ lines.push(`${formatDimText(` - [${kind}] ${message}`)}`);
62
+ }
63
+ if (!isVerbose(flags, 1) && issues.length > maxToShow) lines.push(`${formatDimText(" Re-run with -v/--verbose to see all issues")}`);
64
+ }
65
+ }
66
+ if (error.docsUrl && isVerbose(flags, 1)) lines.push(formatDimText(error.docsUrl));
67
+ if (isVerbose(flags, 2) && error.meta) lines.push(`${formatDimText(` Meta: ${JSON.stringify(error.meta, null, 2)}`)}`);
68
+ return lines.join("\n");
69
+ }
70
+ /**
71
+ * Formats error output as JSON.
72
+ */
73
+ function formatErrorJson(error) {
74
+ return JSON.stringify(error, null, 2);
75
+ }
76
+
77
+ //#endregion
4
78
  //#region src/utils/shutdown.ts
5
79
  function createShutdownHandler(options) {
6
80
  const exit = options?.exit ?? ((code) => process.exit(code));
@@ -270,5 +344,5 @@ var TerminalUI = class TerminalUI {
270
344
  };
271
345
 
272
346
  //#endregion
273
- export { installShutdownHandlers as n, TerminalUI as t };
274
- //# sourceMappingURL=terminal-ui-C5k88MmW.mjs.map
347
+ export { createColorFormatter as a, formatErrorOutput as i, installShutdownHandlers as n, formatDim as o, formatErrorJson as r, isVerbose as s, TerminalUI as t };
348
+ //# sourceMappingURL=terminal-ui-C3ZLwQxK.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-ui-C3ZLwQxK.mjs","names":["lines: string[]","graceTimer: ReturnType<typeof setTimeout> | undefined","shutdownSignal: AbortSignal","noop: clack.SpinnerResult","inner: clack.SpinnerResult | undefined","timer: ReturnType<typeof setTimeout> | undefined","pendingMsg: string | undefined"],"sources":["../src/utils/formatters/helpers.ts","../src/utils/formatters/errors.ts","../src/utils/shutdown.ts","../src/utils/terminal-ui.ts"],"sourcesContent":["import { dim } from 'colorette';\n\nimport type { GlobalFlags } from '../global-flags';\n\n/**\n * Checks if verbose output is enabled at the specified level.\n */\nexport function isVerbose(flags: GlobalFlags, level: 1 | 2): boolean {\n return (flags.verbose ?? 0) >= level;\n}\n\n/**\n * Creates a color-aware formatter function.\n * Returns a function that applies the color only if colors are enabled.\n */\nexport function createColorFormatter<T extends (text: string) => string>(\n useColor: boolean,\n colorFn: T,\n): (text: string) => string {\n return useColor ? colorFn : (text: string) => text;\n}\n\n/**\n * Formats text with dim styling if colors are enabled.\n */\nexport function formatDim(useColor: boolean, text: string): string {\n return useColor ? dim(text) : text;\n}\n","import { red } from 'colorette';\n\nimport type { CliErrorConflict, CliErrorEnvelope } from '../cli-errors';\nimport type { GlobalFlags } from '../global-flags';\nimport { createColorFormatter, formatDim, isVerbose } from './helpers';\n\n/**\n * Formats error output for human-readable display.\n */\nexport function formatErrorOutput(error: CliErrorEnvelope, flags: GlobalFlags): string {\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatRed = createColorFormatter(useColor, red);\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n lines.push(`${formatRed('✖')} ${error.summary} (${error.code})`);\n\n if (error.why) {\n lines.push(`${formatDimText(` Why: ${error.why}`)}`);\n }\n if (error.fix) {\n lines.push(`${formatDimText(` Fix: ${error.fix}`)}`);\n }\n if (error.where?.path) {\n const whereLine = error.where.line\n ? `${error.where.path}:${error.where.line}`\n : error.where.path;\n lines.push(`${formatDimText(` Where: ${whereLine}`)}`);\n }\n // Show conflicts list if present (always show a short list; show full list when verbose)\n if (error.meta?.['conflicts']) {\n const conflicts = error.meta['conflicts'] as readonly CliErrorConflict[];\n if (conflicts.length > 0) {\n const maxToShow = isVerbose(flags, 1) ? conflicts.length : Math.min(3, conflicts.length);\n const header = isVerbose(flags, 1)\n ? ' Conflicts:'\n : ` Conflicts (showing ${maxToShow} of ${conflicts.length}):`;\n lines.push(`${formatDimText(header)}`);\n for (const conflict of conflicts.slice(0, maxToShow)) {\n lines.push(`${formatDimText(` - [${conflict.kind}] ${conflict.summary}`)}`);\n }\n if (!isVerbose(flags, 1) && conflicts.length > maxToShow) {\n lines.push(`${formatDimText(' Re-run with -v/--verbose to see all conflicts')}`);\n }\n }\n }\n // Show issues list if present (always show a short list; show full list when verbose)\n if (error.meta?.['issues']) {\n const issues = error.meta['issues'] as readonly { kind?: string; message?: string }[];\n if (issues.length > 0) {\n const maxToShow = isVerbose(flags, 1) ? issues.length : Math.min(3, issues.length);\n const header = isVerbose(flags, 1)\n ? ' Issues:'\n : ` Issues (showing ${maxToShow} of ${issues.length}):`;\n lines.push(`${formatDimText(header)}`);\n for (const issue of issues.slice(0, maxToShow)) {\n const kind = issue.kind ?? 'issue';\n const message = issue.message ?? '';\n lines.push(`${formatDimText(` - [${kind}] ${message}`)}`);\n }\n if (!isVerbose(flags, 1) && issues.length > maxToShow) {\n lines.push(`${formatDimText(' Re-run with -v/--verbose to see all issues')}`);\n }\n }\n }\n if (error.docsUrl && isVerbose(flags, 1)) {\n lines.push(formatDimText(error.docsUrl));\n }\n if (isVerbose(flags, 2) && error.meta) {\n lines.push(`${formatDimText(` Meta: ${JSON.stringify(error.meta, null, 2)}`)}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats error output as JSON.\n */\nexport function formatErrorJson(error: CliErrorEnvelope): string {\n return JSON.stringify(error, null, 2);\n}\n","/**\n * Global shutdown controller for graceful SIGINT/SIGTERM handling.\n *\n * The CLI installs signal handlers once at startup. When a signal fires:\n * 1. The AbortController is aborted — in-flight async work (DB queries, emit) can check `signal.aborted`.\n * 2. A 3-second grace timer starts — gives `finally` blocks time to close connections.\n * 3. If the process hasn't exited by then, force-exit with code 130 (128 + SIGINT).\n * 4. A second signal during the grace period force-exits immediately.\n */\n\n/**\n * Creates a shutdown handler with its own AbortController.\n * Exposed for testing — production code uses the singleton below.\n */\nexport interface ShutdownHandler {\n readonly signal: AbortSignal;\n readonly isShuttingDown: () => boolean;\n readonly onSignal: () => void;\n readonly clearGraceTimer: () => void;\n}\n\nexport function createShutdownHandler(options?: {\n readonly exit?: (code: number) => void;\n readonly gracePeriodMs?: number;\n}): ShutdownHandler {\n const exit = options?.exit ?? ((code: number) => process.exit(code));\n const gracePeriodMs = options?.gracePeriodMs ?? 3000;\n\n const controller = new AbortController();\n let shuttingDown = false;\n let graceTimer: ReturnType<typeof setTimeout> | undefined;\n\n const onSignal = () => {\n if (shuttingDown) {\n // Second signal — force exit\n exit(130);\n return;\n }\n shuttingDown = true;\n controller.abort();\n\n // Give finally blocks time to clean up, then force-exit\n graceTimer = setTimeout(() => exit(130), gracePeriodMs);\n graceTimer.unref();\n };\n\n return {\n signal: controller.signal,\n isShuttingDown: () => shuttingDown,\n onSignal,\n clearGraceTimer: () => {\n if (graceTimer !== undefined) {\n clearTimeout(graceTimer);\n graceTimer = undefined;\n }\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Singleton for production use\n// ---------------------------------------------------------------------------\n\nconst globalHandler = createShutdownHandler();\n\n/**\n * The global AbortSignal. Pass this to any async operation that should\n * be cancellable on Ctrl+C (e.g., DB queries, long-running emit).\n */\nexport const shutdownSignal: AbortSignal = globalHandler.signal;\n\n/**\n * Whether a shutdown has been initiated.\n */\nexport function isShuttingDown(): boolean {\n return globalHandler.isShuttingDown();\n}\n\n/**\n * Installs SIGINT and SIGTERM handlers. Call once at CLI startup.\n *\n * - First signal: aborts the controller, starts a 3s grace timer.\n * - Second signal: force-exits immediately.\n */\nlet installed = false;\n\nexport function installShutdownHandlers(): void {\n if (installed) return;\n installed = true;\n process.on('SIGINT', globalHandler.onSignal);\n process.on('SIGTERM', globalHandler.onSignal);\n}\n","import * as clack from '@clack/prompts';\nimport { bold, cyan, dim, green, red, yellow } from 'colorette';\nimport { shutdownSignal } from './shutdown';\n\n/**\n * Composable CLI output abstraction.\n *\n * Follows the Unix convention of separating data from decoration:\n * - **stdout** — data output only (`ui.output()`). This is what scripts and pipes capture.\n * - **stderr** — all decoration (spinners, logs, notes, intro/outro). Visible in terminal, invisible in pipes.\n *\n * Rules:\n * 1. All methods except `output()` and `error()` write to stderr only in interactive mode.\n * 2. `output(data)` always writes to stdout — if a command calls it, there is data to emit.\n * 3. `error()` always writes to stderr — errors matter even when piped.\n * 4. All other decoration is suppressed when piped — `isInteractive` gates every other decoration method.\n * 5. Never write data to stderr — decoration methods are for human context only.\n * 6. Never write decoration to stdout — it breaks pipes, `$(...)` captures, and `> file` redirects.\n */\nexport class TerminalUI {\n /**\n * True when stdout is a TTY (interactive terminal).\n * False when piped (e.g., `prisma-next db verify | jq`).\n */\n readonly isInteractive: boolean;\n\n /**\n * Whether color output is enabled.\n */\n readonly useColor: boolean;\n\n private static readonly stderrOpts = { output: process.stderr } as const;\n\n constructor(options?: {\n readonly color?: boolean | undefined;\n readonly interactive?: boolean | undefined;\n }) {\n // --interactive/--no-interactive override TTY detection\n this.isInteractive = options?.interactive ?? !!process.stdout.isTTY;\n this.useColor = options?.color ?? this.isInteractive;\n }\n\n // ---------------------------------------------------------------------------\n // Decoration → stderr (only in interactive mode)\n // ---------------------------------------------------------------------------\n\n /**\n * Log a message line to stderr. No-op when piped.\n */\n log(message: string): void {\n if (!this.isInteractive) return;\n clack.log.message(message, TerminalUI.stderrOpts);\n }\n\n /**\n * Log a success message to stderr. No-op when piped.\n */\n success(message: string): void {\n if (!this.isInteractive) return;\n clack.log.success(message, TerminalUI.stderrOpts);\n }\n\n /**\n * Log a warning message to stderr. No-op when piped.\n */\n warn(message: string): void {\n if (!this.isInteractive) return;\n clack.log.warn(message, TerminalUI.stderrOpts);\n }\n\n /**\n * Log an error message to stderr. Always writes (errors matter even in pipes).\n */\n error(message: string): void {\n clack.log.error(message, TerminalUI.stderrOpts);\n }\n\n /**\n * Log an info message to stderr. No-op when piped.\n */\n info(message: string): void {\n if (!this.isInteractive) return;\n clack.log.info(message, TerminalUI.stderrOpts);\n }\n\n /**\n * Log a step message to stderr. No-op when piped.\n */\n step(message: string): void {\n if (!this.isInteractive) return;\n clack.log.step(message, TerminalUI.stderrOpts);\n }\n\n /**\n * Display a note box on stderr. No-op when piped.\n */\n note(message: string, title?: string): void {\n if (!this.isInteractive) return;\n clack.note(message, title, TerminalUI.stderrOpts);\n }\n\n /**\n * Display intro banner on stderr. No-op when piped.\n */\n intro(title?: string): void {\n if (!this.isInteractive) return;\n clack.intro(title, TerminalUI.stderrOpts);\n }\n\n /**\n * Display outro banner on stderr. No-op when piped.\n */\n outro(message?: string): void {\n if (!this.isInteractive) return;\n clack.outro(message, TerminalUI.stderrOpts);\n }\n\n /**\n * Create a Clack spinner on stderr with a 100ms delay threshold.\n * The spinner only appears if the operation takes longer than the threshold,\n * avoiding flicker for fast operations. Returns a no-op spinner when not interactive.\n */\n spinner(delayMs = 100): clack.SpinnerResult {\n const noop: clack.SpinnerResult = {\n start: () => {},\n stop: () => {},\n cancel: () => {},\n error: () => {},\n message: () => {},\n clear: () => {},\n get isCancelled() {\n return false;\n },\n };\n\n if (!this.isInteractive) {\n return noop;\n }\n\n // Wrap the real spinner with a delay: only show it after `delayMs`\n let inner: clack.SpinnerResult | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let pendingMsg: string | undefined;\n let settled = false;\n\n const ensureCleared = () => {\n if (timer !== undefined) {\n clearTimeout(timer);\n timer = undefined;\n }\n };\n\n // Cancel the spinner if a shutdown signal fires\n const onAbort = () => {\n if (!settled) {\n settled = true;\n ensureCleared();\n if (inner) {\n inner.cancel('Interrupted');\n }\n }\n };\n if (!shutdownSignal.aborted) {\n shutdownSignal.addEventListener('abort', onAbort, { once: true });\n }\n\n return {\n start(msg?: string) {\n pendingMsg = msg;\n timer = setTimeout(() => {\n if (!settled) {\n inner = clack.spinner(TerminalUI.stderrOpts);\n inner.start(pendingMsg);\n }\n }, delayMs);\n },\n stop(msg?: string) {\n settled = true;\n ensureCleared();\n if (inner) {\n inner.stop(msg);\n }\n },\n cancel(msg?: string) {\n settled = true;\n ensureCleared();\n if (inner) {\n inner.cancel(msg);\n }\n },\n error(msg?: string) {\n settled = true;\n ensureCleared();\n if (inner) {\n inner.error(msg);\n }\n },\n message(msg?: string) {\n pendingMsg = msg;\n if (inner) {\n inner.message(msg);\n }\n },\n clear() {\n settled = true;\n ensureCleared();\n if (inner) {\n inner.clear();\n }\n },\n get isCancelled() {\n return inner?.isCancelled ?? false;\n },\n };\n }\n\n /**\n * Prompt for yes/no confirmation on stderr. Returns true if confirmed.\n * In non-interactive mode or when cancelled (Ctrl-C), returns false.\n */\n async confirm(message: string): Promise<boolean> {\n if (!this.isInteractive) return false;\n const result = await clack.confirm({\n message,\n ...TerminalUI.stderrOpts,\n });\n if (clack.isCancel(result)) return false;\n return result;\n }\n\n /**\n * Write a raw line to stderr. No-op when piped.\n * Use for decoration that doesn't fit Clack's log format (e.g. styled headers).\n */\n stderr(message: string): void {\n if (!this.isInteractive) return;\n process.stderr.write(`${message}\\n`);\n }\n\n // ---------------------------------------------------------------------------\n // Data → stdout (only when piped)\n // ---------------------------------------------------------------------------\n\n /**\n * Write machine-readable data to stdout.\n * Always writes — if a command calls output(), there is data to emit.\n *\n * This is what scripts and pipes capture: `prisma-next db verify --json | jq .ok`\n */\n output(data: string): void {\n process.stdout.write(`${data}\\n`);\n }\n\n // ---------------------------------------------------------------------------\n // Color helpers\n // ---------------------------------------------------------------------------\n\n green(text: string): string {\n return this.useColor ? green(text) : text;\n }\n red(text: string): string {\n return this.useColor ? red(text) : text;\n }\n cyan(text: string): string {\n return this.useColor ? cyan(text) : text;\n }\n dim(text: string): string {\n return this.useColor ? dim(text) : text;\n }\n bold(text: string): string {\n return this.useColor ? bold(text) : text;\n }\n yellow(text: string): string {\n return this.useColor ? yellow(text) : text;\n }\n}\n"],"mappings":";;;;;;;AAOA,SAAgB,UAAU,OAAoB,OAAuB;AACnE,SAAQ,MAAM,WAAW,MAAM;;;;;;AAOjC,SAAgB,qBACd,UACA,SAC0B;AAC1B,QAAO,WAAW,WAAW,SAAiB;;;;;AAMhD,SAAgB,UAAU,UAAmB,MAAsB;AACjE,QAAO,WAAW,IAAI,KAAK,GAAG;;;;;;;;ACjBhC,SAAgB,kBAAkB,OAAyB,OAA4B;CACrF,MAAMA,QAAkB,EAAE;CAE1B,MAAM,WAAW,MAAM,UAAU;CACjC,MAAM,YAAY,qBAAqB,UAAU,IAAI;CACrD,MAAM,iBAAiB,SAAiB,UAAU,UAAU,KAAK;AAEjE,OAAM,KAAK,GAAG,UAAU,IAAI,CAAC,GAAG,MAAM,QAAQ,IAAI,MAAM,KAAK,GAAG;AAEhE,KAAI,MAAM,IACR,OAAM,KAAK,GAAG,cAAc,UAAU,MAAM,MAAM,GAAG;AAEvD,KAAI,MAAM,IACR,OAAM,KAAK,GAAG,cAAc,UAAU,MAAM,MAAM,GAAG;AAEvD,KAAI,MAAM,OAAO,MAAM;EACrB,MAAM,YAAY,MAAM,MAAM,OAC1B,GAAG,MAAM,MAAM,KAAK,GAAG,MAAM,MAAM,SACnC,MAAM,MAAM;AAChB,QAAM,KAAK,GAAG,cAAc,YAAY,YAAY,GAAG;;AAGzD,KAAI,MAAM,OAAO,cAAc;EAC7B,MAAM,YAAY,MAAM,KAAK;AAC7B,MAAI,UAAU,SAAS,GAAG;GACxB,MAAM,YAAY,UAAU,OAAO,EAAE,GAAG,UAAU,SAAS,KAAK,IAAI,GAAG,UAAU,OAAO;GACxF,MAAM,SAAS,UAAU,OAAO,EAAE,GAC9B,iBACA,wBAAwB,UAAU,MAAM,UAAU,OAAO;AAC7D,SAAM,KAAK,GAAG,cAAc,OAAO,GAAG;AACtC,QAAK,MAAM,YAAY,UAAU,MAAM,GAAG,UAAU,CAClD,OAAM,KAAK,GAAG,cAAc,UAAU,SAAS,KAAK,IAAI,SAAS,UAAU,GAAG;AAEhF,OAAI,CAAC,UAAU,OAAO,EAAE,IAAI,UAAU,SAAS,UAC7C,OAAM,KAAK,GAAG,cAAc,kDAAkD,GAAG;;;AAKvF,KAAI,MAAM,OAAO,WAAW;EAC1B,MAAM,SAAS,MAAM,KAAK;AAC1B,MAAI,OAAO,SAAS,GAAG;GACrB,MAAM,YAAY,UAAU,OAAO,EAAE,GAAG,OAAO,SAAS,KAAK,IAAI,GAAG,OAAO,OAAO;GAClF,MAAM,SAAS,UAAU,OAAO,EAAE,GAC9B,cACA,qBAAqB,UAAU,MAAM,OAAO,OAAO;AACvD,SAAM,KAAK,GAAG,cAAc,OAAO,GAAG;AACtC,QAAK,MAAM,SAAS,OAAO,MAAM,GAAG,UAAU,EAAE;IAC9C,MAAM,OAAO,MAAM,QAAQ;IAC3B,MAAM,UAAU,MAAM,WAAW;AACjC,UAAM,KAAK,GAAG,cAAc,UAAU,KAAK,IAAI,UAAU,GAAG;;AAE9D,OAAI,CAAC,UAAU,OAAO,EAAE,IAAI,OAAO,SAAS,UAC1C,OAAM,KAAK,GAAG,cAAc,+CAA+C,GAAG;;;AAIpF,KAAI,MAAM,WAAW,UAAU,OAAO,EAAE,CACtC,OAAM,KAAK,cAAc,MAAM,QAAQ,CAAC;AAE1C,KAAI,UAAU,OAAO,EAAE,IAAI,MAAM,KAC/B,OAAM,KAAK,GAAG,cAAc,WAAW,KAAK,UAAU,MAAM,MAAM,MAAM,EAAE,GAAG,GAAG;AAGlF,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAgB,gBAAgB,OAAiC;AAC/D,QAAO,KAAK,UAAU,OAAO,MAAM,EAAE;;;;;AC3DvC,SAAgB,sBAAsB,SAGlB;CAClB,MAAM,OAAO,SAAS,UAAU,SAAiB,QAAQ,KAAK,KAAK;CACnE,MAAM,gBAAgB,SAAS,iBAAiB;CAEhD,MAAM,aAAa,IAAI,iBAAiB;CACxC,IAAI,eAAe;CACnB,IAAIC;CAEJ,MAAM,iBAAiB;AACrB,MAAI,cAAc;AAEhB,QAAK,IAAI;AACT;;AAEF,iBAAe;AACf,aAAW,OAAO;AAGlB,eAAa,iBAAiB,KAAK,IAAI,EAAE,cAAc;AACvD,aAAW,OAAO;;AAGpB,QAAO;EACL,QAAQ,WAAW;EACnB,sBAAsB;EACtB;EACA,uBAAuB;AACrB,OAAI,eAAe,QAAW;AAC5B,iBAAa,WAAW;AACxB,iBAAa;;;EAGlB;;AAOH,MAAM,gBAAgB,uBAAuB;;;;;AAM7C,MAAaC,iBAA8B,cAAc;;;;;;;AAezD,IAAI,YAAY;AAEhB,SAAgB,0BAAgC;AAC9C,KAAI,UAAW;AACf,aAAY;AACZ,SAAQ,GAAG,UAAU,cAAc,SAAS;AAC5C,SAAQ,GAAG,WAAW,cAAc,SAAS;;;;;;;;;;;;;;;;;;;;ACvE/C,IAAa,aAAb,MAAa,WAAW;;;;;CAKtB,AAAS;;;;CAKT,AAAS;CAET,OAAwB,aAAa,EAAE,QAAQ,QAAQ,QAAQ;CAE/D,YAAY,SAGT;AAED,OAAK,gBAAgB,SAAS,eAAe,CAAC,CAAC,QAAQ,OAAO;AAC9D,OAAK,WAAW,SAAS,SAAS,KAAK;;;;;CAUzC,IAAI,SAAuB;AACzB,MAAI,CAAC,KAAK,cAAe;AACzB,QAAM,IAAI,QAAQ,SAAS,WAAW,WAAW;;;;;CAMnD,QAAQ,SAAuB;AAC7B,MAAI,CAAC,KAAK,cAAe;AACzB,QAAM,IAAI,QAAQ,SAAS,WAAW,WAAW;;;;;CAMnD,KAAK,SAAuB;AAC1B,MAAI,CAAC,KAAK,cAAe;AACzB,QAAM,IAAI,KAAK,SAAS,WAAW,WAAW;;;;;CAMhD,MAAM,SAAuB;AAC3B,QAAM,IAAI,MAAM,SAAS,WAAW,WAAW;;;;;CAMjD,KAAK,SAAuB;AAC1B,MAAI,CAAC,KAAK,cAAe;AACzB,QAAM,IAAI,KAAK,SAAS,WAAW,WAAW;;;;;CAMhD,KAAK,SAAuB;AAC1B,MAAI,CAAC,KAAK,cAAe;AACzB,QAAM,IAAI,KAAK,SAAS,WAAW,WAAW;;;;;CAMhD,KAAK,SAAiB,OAAsB;AAC1C,MAAI,CAAC,KAAK,cAAe;AACzB,QAAM,KAAK,SAAS,OAAO,WAAW,WAAW;;;;;CAMnD,MAAM,OAAsB;AAC1B,MAAI,CAAC,KAAK,cAAe;AACzB,QAAM,MAAM,OAAO,WAAW,WAAW;;;;;CAM3C,MAAM,SAAwB;AAC5B,MAAI,CAAC,KAAK,cAAe;AACzB,QAAM,MAAM,SAAS,WAAW,WAAW;;;;;;;CAQ7C,QAAQ,UAAU,KAA0B;EAC1C,MAAMC,OAA4B;GAChC,aAAa;GACb,YAAY;GACZ,cAAc;GACd,aAAa;GACb,eAAe;GACf,aAAa;GACb,IAAI,cAAc;AAChB,WAAO;;GAEV;AAED,MAAI,CAAC,KAAK,cACR,QAAO;EAIT,IAAIC;EACJ,IAAIC;EACJ,IAAIC;EACJ,IAAI,UAAU;EAEd,MAAM,sBAAsB;AAC1B,OAAI,UAAU,QAAW;AACvB,iBAAa,MAAM;AACnB,YAAQ;;;EAKZ,MAAM,gBAAgB;AACpB,OAAI,CAAC,SAAS;AACZ,cAAU;AACV,mBAAe;AACf,QAAI,MACF,OAAM,OAAO,cAAc;;;AAIjC,MAAI,CAAC,eAAe,QAClB,gBAAe,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;AAGnE,SAAO;GACL,MAAM,KAAc;AAClB,iBAAa;AACb,YAAQ,iBAAiB;AACvB,SAAI,CAAC,SAAS;AACZ,cAAQ,MAAM,QAAQ,WAAW,WAAW;AAC5C,YAAM,MAAM,WAAW;;OAExB,QAAQ;;GAEb,KAAK,KAAc;AACjB,cAAU;AACV,mBAAe;AACf,QAAI,MACF,OAAM,KAAK,IAAI;;GAGnB,OAAO,KAAc;AACnB,cAAU;AACV,mBAAe;AACf,QAAI,MACF,OAAM,OAAO,IAAI;;GAGrB,MAAM,KAAc;AAClB,cAAU;AACV,mBAAe;AACf,QAAI,MACF,OAAM,MAAM,IAAI;;GAGpB,QAAQ,KAAc;AACpB,iBAAa;AACb,QAAI,MACF,OAAM,QAAQ,IAAI;;GAGtB,QAAQ;AACN,cAAU;AACV,mBAAe;AACf,QAAI,MACF,OAAM,OAAO;;GAGjB,IAAI,cAAc;AAChB,WAAO,OAAO,eAAe;;GAEhC;;;;;;CAOH,MAAM,QAAQ,SAAmC;AAC/C,MAAI,CAAC,KAAK,cAAe,QAAO;EAChC,MAAM,SAAS,MAAM,MAAM,QAAQ;GACjC;GACA,GAAG,WAAW;GACf,CAAC;AACF,MAAI,MAAM,SAAS,OAAO,CAAE,QAAO;AACnC,SAAO;;;;;;CAOT,OAAO,SAAuB;AAC5B,MAAI,CAAC,KAAK,cAAe;AACzB,UAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;;;;;;;;CAatC,OAAO,MAAoB;AACzB,UAAQ,OAAO,MAAM,GAAG,KAAK,IAAI;;CAOnC,MAAM,MAAsB;AAC1B,SAAO,KAAK,WAAW,MAAM,KAAK,GAAG;;CAEvC,IAAI,MAAsB;AACxB,SAAO,KAAK,WAAW,IAAI,KAAK,GAAG;;CAErC,KAAK,MAAsB;AACzB,SAAO,KAAK,WAAW,KAAK,KAAK,GAAG;;CAEtC,IAAI,MAAsB;AACxB,SAAO,KAAK,WAAW,IAAI,KAAK,GAAG;;CAErC,KAAK,MAAsB;AACzB,SAAO,KAAK,WAAW,KAAK,KAAK,GAAG;;CAEtC,OAAO,MAAsB;AAC3B,SAAO,KAAK,WAAW,OAAO,KAAK,GAAG"}
@@ -34,4 +34,4 @@ function validateContractDeps(dtsContent, projectRoot) {
34
34
 
35
35
  //#endregion
36
36
  export { validateContractDeps };
37
- //# sourceMappingURL=validate-contract-deps-esa-VQ0h.mjs.map
37
+ //# sourceMappingURL=validate-contract-deps-B_Cs29TL.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate-contract-deps-esa-VQ0h.mjs","names":["missing: string[]"],"sources":["../src/utils/validate-contract-deps.ts"],"sourcesContent":["import { createRequire } from 'node:module';\n\nconst IMPORT_PATTERN = /import\\s+type\\s+.*?\\s+from\\s+['\"](@[^/]+\\/[^/'\"]+)/g;\n\nexport function extractPackageSpecifiers(dtsContent: string): string[] {\n const packages = new Set<string>();\n for (const match of dtsContent.matchAll(IMPORT_PATTERN)) {\n const pkg = match[1];\n if (pkg) packages.add(pkg);\n }\n return [...packages];\n}\n\nexport interface ContractDepsValidation {\n readonly missing: readonly string[];\n readonly warning?: string;\n}\n\nexport function validateContractDeps(\n dtsContent: string,\n projectRoot: string,\n): ContractDepsValidation {\n const packages = extractPackageSpecifiers(dtsContent);\n const resolve = createRequire(`${projectRoot}/package.json`);\n\n const missing: string[] = [];\n for (const pkg of packages) {\n try {\n resolve.resolve(`${pkg}/package.json`);\n } catch {\n missing.push(pkg);\n }\n }\n\n if (missing.length === 0) {\n return { missing };\n }\n\n const list = missing.map((p) => ` - ${p}`).join('\\n');\n const warning = [\n 'contract.d.ts imports types from packages that are not installed:',\n list,\n '',\n 'Install them with your package manager:',\n ...missing.map((p) => ` ${p}`),\n ].join('\\n');\n\n return { missing, warning };\n}\n"],"mappings":";;;AAEA,MAAM,iBAAiB;AAEvB,SAAgB,yBAAyB,YAA8B;CACrE,MAAM,2BAAW,IAAI,KAAa;AAClC,MAAK,MAAM,SAAS,WAAW,SAAS,eAAe,EAAE;EACvD,MAAM,MAAM,MAAM;AAClB,MAAI,IAAK,UAAS,IAAI,IAAI;;AAE5B,QAAO,CAAC,GAAG,SAAS;;AAQtB,SAAgB,qBACd,YACA,aACwB;CACxB,MAAM,WAAW,yBAAyB,WAAW;CACrD,MAAM,UAAU,cAAc,GAAG,YAAY,eAAe;CAE5D,MAAMA,UAAoB,EAAE;AAC5B,MAAK,MAAM,OAAO,SAChB,KAAI;AACF,UAAQ,QAAQ,GAAG,IAAI,eAAe;SAChC;AACN,UAAQ,KAAK,IAAI;;AAIrB,KAAI,QAAQ,WAAW,EACrB,QAAO,EAAE,SAAS;AAYpB,QAAO;EAAE;EAAS,SARF;GACd;GAFW,QAAQ,KAAK,MAAM,OAAO,IAAI,CAAC,KAAK,KAAK;GAIpD;GACA;GACA,GAAG,QAAQ,KAAK,MAAM,KAAK,IAAI;GAChC,CAAC,KAAK,KAAK;EAEe"}
1
+ {"version":3,"file":"validate-contract-deps-B_Cs29TL.mjs","names":["missing: string[]"],"sources":["../src/utils/validate-contract-deps.ts"],"sourcesContent":["import { createRequire } from 'node:module';\n\nconst IMPORT_PATTERN = /import\\s+type\\s+.*?\\s+from\\s+['\"](@[^/]+\\/[^/'\"]+)/g;\n\nexport function extractPackageSpecifiers(dtsContent: string): string[] {\n const packages = new Set<string>();\n for (const match of dtsContent.matchAll(IMPORT_PATTERN)) {\n const pkg = match[1];\n if (pkg) packages.add(pkg);\n }\n return [...packages];\n}\n\nexport interface ContractDepsValidation {\n readonly missing: readonly string[];\n readonly warning?: string;\n}\n\nexport function validateContractDeps(\n dtsContent: string,\n projectRoot: string,\n): ContractDepsValidation {\n const packages = extractPackageSpecifiers(dtsContent);\n const resolve = createRequire(`${projectRoot}/package.json`);\n\n const missing: string[] = [];\n for (const pkg of packages) {\n try {\n resolve.resolve(`${pkg}/package.json`);\n } catch {\n missing.push(pkg);\n }\n }\n\n if (missing.length === 0) {\n return { missing };\n }\n\n const list = missing.map((p) => ` - ${p}`).join('\\n');\n const warning = [\n 'contract.d.ts imports types from packages that are not installed:',\n list,\n '',\n 'Install them with your package manager:',\n ...missing.map((p) => ` ${p}`),\n ].join('\\n');\n\n return { missing, warning };\n}\n"],"mappings":";;;AAEA,MAAM,iBAAiB;AAEvB,SAAgB,yBAAyB,YAA8B;CACrE,MAAM,2BAAW,IAAI,KAAa;AAClC,MAAK,MAAM,SAAS,WAAW,SAAS,eAAe,EAAE;EACvD,MAAM,MAAM,MAAM;AAClB,MAAI,IAAK,UAAS,IAAI,IAAI;;AAE5B,QAAO,CAAC,GAAG,SAAS;;AAQtB,SAAgB,qBACd,YACA,aACwB;CACxB,MAAM,WAAW,yBAAyB,WAAW;CACrD,MAAM,UAAU,cAAc,GAAG,YAAY,eAAe;CAE5D,MAAMA,UAAoB,EAAE;AAC5B,MAAK,MAAM,OAAO,SAChB,KAAI;AACF,UAAQ,QAAQ,GAAG,IAAI,eAAe;SAChC;AACN,UAAQ,KAAK,IAAI;;AAIrB,KAAI,QAAQ,WAAW,EACrB,QAAO,EAAE,SAAS;AAYpB,QAAO;EAAE;EAAS,SARF;GACd;GAFW,QAAQ,KAAK,MAAM,OAAO,IAAI,CAAC,KAAK,KAAK;GAIpD;GACA;GACA,GAAG,QAAQ,KAAK,MAAM,KAAK,IAAI;GAChC,CAAC,KAAK,KAAK;EAEe"}
@@ -1,4 +1,4 @@
1
- import { b as formatDim, x as isVerbose, y as createColorFormatter } from "./result-handler-BJwA7ufw.mjs";
1
+ import { a as createColorFormatter, o as formatDim, s as isVerbose } from "./terminal-ui-C3ZLwQxK.mjs";
2
2
  import { ifDefined } from "@prisma-next/utils/defined";
3
3
  import { bold, cyan, dim, green, magenta, red, yellow } from "colorette";
4
4
 
@@ -382,4 +382,4 @@ function formatSignJson(result) {
382
382
 
383
383
  //#endregion
384
384
  export { formatSignJson as a, formatVerifyOutput as c, formatSchemaVerifyOutput as i, formatIntrospectOutput as n, formatSignOutput as o, formatSchemaVerifyJson as r, formatVerifyJson as s, formatIntrospectJson as t };
385
- //# sourceMappingURL=verify-bl__PkXk.mjs.map
385
+ //# sourceMappingURL=verify-Bkycc-Tf.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"verify-bl__PkXk.mjs","names":["lines: string[]","formattedLabel: string","statusColor: (text: string) => string","labelColor: (text: string) => string"],"sources":["../src/utils/formatters/verify.ts"],"sourcesContent":["import type {\n CoreSchemaView,\n IntrospectSchemaResult,\n SchemaTreeNode,\n SchemaVerificationNode,\n SignDatabaseResult,\n VerifyDatabaseResult,\n VerifyDatabaseSchemaResult,\n} from '@prisma-next/framework-components/control';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { bold, cyan, dim, green, magenta, red, yellow } from 'colorette';\nimport type { GlobalFlags } from '../global-flags';\nimport { createColorFormatter, formatDim, isVerbose } from './helpers';\n\n// ============================================================================\n// Verify Output Formatters\n// ============================================================================\n\nexport interface DbVerifyCommandSuccessResult {\n readonly ok: true;\n readonly mode: 'full' | 'marker-only';\n readonly summary: string;\n readonly contract: VerifyDatabaseResult['contract'];\n readonly marker?: VerifyDatabaseResult['marker'];\n readonly target: VerifyDatabaseResult['target'];\n readonly missingCodecs?: VerifyDatabaseResult['missingCodecs'];\n readonly codecCoverageSkipped?: VerifyDatabaseResult['codecCoverageSkipped'];\n readonly schema?: {\n readonly summary: string;\n readonly counts: VerifyDatabaseSchemaResult['schema']['counts'];\n readonly strict: boolean;\n };\n readonly warning?: string;\n readonly meta?:\n | (NonNullable<VerifyDatabaseResult['meta']> & {\n readonly schemaVerification: 'performed' | 'skipped';\n })\n | {\n readonly schemaVerification: 'performed' | 'skipped';\n };\n readonly timings: {\n readonly total: number;\n };\n}\n\n/**\n * Formats human-readable output for database verify.\n */\nexport function formatVerifyOutput(\n result: DbVerifyCommandSuccessResult,\n flags: GlobalFlags,\n): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatGreen = createColorFormatter(useColor, green);\n const formatYellow = createColorFormatter(useColor, yellow);\n const formatDimText = (text: string) => formatDim(useColor, text);\n const verificationMode =\n result.mode === 'full'\n ? `marker + schema${result.schema?.strict ? ' (strict)' : ' (tolerant)'}`\n : 'marker only (--marker-only)';\n\n lines.push(`${formatGreen('✔')} ${result.summary}`);\n lines.push(`${formatDimText(` verification: ${verificationMode}`)}`);\n lines.push(`${formatDimText(` storageHash: ${result.contract.storageHash}`)}`);\n if (result.contract.profileHash) {\n lines.push(`${formatDimText(` profileHash: ${result.contract.profileHash}`)}`);\n }\n if (result.mode === 'full' && result.schema && isVerbose(flags, 1)) {\n lines.push(\n `${formatDimText(` schema: pass=${result.schema.counts.pass} warn=${result.schema.counts.warn} fail=${result.schema.counts.fail}`)}`,\n );\n }\n if (result.warning) {\n lines.push('');\n lines.push(`${formatYellow('⚠')} ${result.warning}`);\n }\n\n if (isVerbose(flags, 1)) {\n if (result.codecCoverageSkipped) {\n lines.push(\n `${formatDimText(' Codec coverage check skipped (helper returned no supported types)')}`,\n );\n }\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for database verify.\n */\nexport function formatVerifyJson(result: DbVerifyCommandSuccessResult): string {\n const output = {\n ok: result.ok,\n summary: result.summary,\n mode: result.mode,\n contract: result.contract,\n ...ifDefined('marker', result.marker),\n target: result.target,\n ...ifDefined('missingCodecs', result.missingCodecs),\n ...ifDefined('codecCoverageSkipped', result.codecCoverageSkipped),\n ...ifDefined('schema', result.schema),\n ...ifDefined('warning', result.warning),\n ...ifDefined('meta', result.meta),\n timings: result.timings,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Formats JSON output for database introspection.\n */\nexport function formatIntrospectJson(result: IntrospectSchemaResult<unknown>): string {\n return JSON.stringify(result, null, 2);\n}\n\n/**\n * Renders a schema tree structure from CoreSchemaView.\n * Matches the style of renderSchemaVerificationTree for consistency.\n */\nfunction renderSchemaTree(\n node: SchemaTreeNode,\n flags: GlobalFlags,\n options: {\n readonly isLast: boolean;\n readonly prefix: string;\n readonly useColor: boolean;\n readonly formatDimText: (text: string) => string;\n readonly isRoot?: boolean;\n },\n): string[] {\n const { isLast, prefix, useColor, formatDimText, isRoot = false } = options;\n const lines: string[] = [];\n\n // Format node label with color based on kind (matching schema-verify style)\n let formattedLabel: string = node.label;\n\n if (useColor) {\n switch (node.kind) {\n case 'root':\n formattedLabel = bold(node.label);\n break;\n case 'entity': {\n // Parse \"table tableName\" format - color \"table\" dim, tableName cyan\n const tableMatch = node.label.match(/^table\\s+(.+)$/);\n if (tableMatch?.[1]) {\n const tableName = tableMatch[1];\n formattedLabel = `${dim('table')} ${cyan(tableName)}`;\n } else {\n // Fallback: color entire label with cyan\n formattedLabel = cyan(node.label);\n }\n break;\n }\n case 'collection': {\n // \"columns\" grouping node - dim the label\n formattedLabel = dim(node.label);\n break;\n }\n case 'field': {\n // Parse column name format: \"columnName: typeDisplay (nullability)\"\n // Color code: column name (cyan), type (default), nullability (dim)\n const columnMatch = node.label.match(/^([^:]+):\\s*(.+)$/);\n if (columnMatch?.[1] && columnMatch[2]) {\n const columnName = columnMatch[1];\n const rest = columnMatch[2];\n // Parse rest: \"typeDisplay (nullability)\"\n const typeMatch = rest.match(/^([^\\s(]+)\\s*(\\([^)]+\\))$/);\n if (typeMatch?.[1] && typeMatch[2]) {\n const typeDisplay = typeMatch[1];\n const nullability = typeMatch[2];\n formattedLabel = `${cyan(columnName)}: ${typeDisplay} ${dim(nullability)}`;\n } else {\n // Fallback if format doesn't match\n formattedLabel = `${cyan(columnName)}: ${rest}`;\n }\n } else {\n formattedLabel = node.label;\n }\n break;\n }\n case 'index': {\n // Parse index/unique constraint/primary key formats\n // \"primary key: columnName\" -> dim \"primary key\", cyan columnName\n const pkMatch = node.label.match(/^primary key:\\s*(.+)$/);\n if (pkMatch?.[1]) {\n const columnNames = pkMatch[1];\n formattedLabel = `${dim('primary key')}: ${cyan(columnNames)}`;\n } else {\n // \"unique name\" -> dim \"unique\", cyan \"name\"\n const uniqueMatch = node.label.match(/^unique\\s+(.+)$/);\n if (uniqueMatch?.[1]) {\n const name = uniqueMatch[1];\n formattedLabel = `${dim('unique')} ${cyan(name)}`;\n } else {\n // \"index name\" or \"unique index name\" -> dim label prefix, cyan name\n const indexMatch = node.label.match(/^(unique\\s+)?index\\s+(.+)$/);\n if (indexMatch?.[2]) {\n const indexPrefix = indexMatch[1] ? `${dim('unique')} ` : '';\n const name = indexMatch[2];\n formattedLabel = `${indexPrefix}${dim('index')} ${cyan(name)}`;\n } else {\n formattedLabel = dim(node.label);\n }\n }\n }\n break;\n }\n case 'dependency': {\n // Parse extension message formats similar to schema-verify\n // \"extensionName extension is enabled\" -> cyan extensionName, dim rest\n const extMatch = node.label.match(/^([^\\s]+)\\s+(extension is enabled)$/);\n if (extMatch?.[1] && extMatch[2]) {\n const extName = extMatch[1];\n const rest = extMatch[2];\n formattedLabel = `${cyan(extName)} ${dim(rest)}`;\n } else {\n // Fallback: color entire label with magenta\n formattedLabel = magenta(node.label);\n }\n break;\n }\n default:\n formattedLabel = node.label;\n break;\n }\n }\n\n // Root node renders without tree characters or prefix\n if (isRoot) {\n lines.push(formattedLabel);\n } else {\n const treeChar = isLast ? '└' : '├';\n const treePrefix = `${formatDimText(treeChar)}─ `;\n lines.push(`${prefix}${treePrefix}${formattedLabel}`);\n }\n\n // Render children if present\n if (node.children && node.children.length > 0) {\n const childPrefix = isRoot ? '' : `${prefix}${isLast ? ' ' : `${formatDimText('│')} `}`;\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n if (!child) continue;\n const isLastChild = i === node.children.length - 1;\n const childLines = renderSchemaTree(child, flags, {\n isLast: isLastChild,\n prefix: childPrefix,\n useColor,\n formatDimText,\n isRoot: false,\n });\n lines.push(...childLines);\n }\n }\n\n return lines;\n}\n\n/**\n * Formats human-readable output for database introspection.\n */\nexport function formatIntrospectOutput(\n result: IntrospectSchemaResult<unknown>,\n schemaView: CoreSchemaView | undefined,\n flags: GlobalFlags,\n): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n if (schemaView) {\n // Render tree structure - root node is special (no tree characters)\n const treeLines = renderSchemaTree(schemaView.root, flags, {\n isLast: true,\n prefix: '',\n useColor,\n formatDimText,\n isRoot: true,\n });\n lines.push(...treeLines);\n } else {\n // Fallback: print summary when toSchemaView is not available\n lines.push(`✔ ${result.summary}`);\n if (isVerbose(flags, 1)) {\n lines.push(` Target: ${result.target.familyId}/${result.target.id}`);\n if (result.meta?.dbUrl) {\n lines.push(` Database: ${result.meta.dbUrl}`);\n }\n }\n }\n\n // Add timings in verbose mode\n if (isVerbose(flags, 1)) {\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Renders a schema verification tree structure from SchemaVerificationNode.\n * Similar to renderSchemaTree but for verification nodes with status-based colors and glyphs.\n */\nfunction renderSchemaVerificationTree(\n node: SchemaVerificationNode,\n flags: GlobalFlags,\n options: {\n readonly isLast: boolean;\n readonly prefix: string;\n readonly useColor: boolean;\n readonly formatDimText: (text: string) => string;\n readonly isRoot?: boolean;\n },\n): string[] {\n const { isLast, prefix, useColor, formatDimText, isRoot = false } = options;\n const lines: string[] = [];\n\n // Format status glyph and color based on status\n let statusGlyph = '';\n let statusColor: (text: string) => string = (text) => text;\n if (useColor) {\n switch (node.status) {\n case 'pass':\n statusGlyph = '✔';\n statusColor = green;\n break;\n case 'warn':\n statusGlyph = '⚠';\n statusColor = (text) => (useColor ? yellow(text) : text);\n break;\n case 'fail':\n statusGlyph = '✖';\n statusColor = red;\n break;\n }\n } else {\n switch (node.status) {\n case 'pass':\n statusGlyph = '✔';\n break;\n case 'warn':\n statusGlyph = '⚠';\n break;\n case 'fail':\n statusGlyph = '✖';\n break;\n }\n }\n\n // Format node label with color based on kind\n // For column nodes, we need to parse the name to color code different parts\n let labelColor: (text: string) => string = (text) => text;\n let formattedLabel: string = node.name;\n\n if (useColor) {\n switch (node.kind) {\n case 'contract':\n case 'schema':\n labelColor = bold;\n formattedLabel = labelColor(node.name);\n break;\n case 'table': {\n // Parse \"table tableName\" format - color \"table\" dim, tableName cyan\n const tableMatch = node.name.match(/^table\\s+(.+)$/);\n if (tableMatch?.[1]) {\n const tableName = tableMatch[1];\n formattedLabel = `${dim('table')} ${cyan(tableName)}`;\n } else {\n formattedLabel = dim(node.name);\n }\n break;\n }\n case 'columns':\n labelColor = dim;\n formattedLabel = labelColor(node.name);\n break;\n case 'column': {\n // Parse column name format: \"columnName: contractType -> nativeType (nullability)\"\n // Color code: column name (cyan), contract type (default), native type (dim), nullability (dim)\n const columnMatch = node.name.match(/^([^:]+):\\s*(.+)$/);\n if (columnMatch?.[1] && columnMatch[2]) {\n const columnName = columnMatch[1];\n const rest = columnMatch[2];\n // Parse rest: \"contractType -> nativeType (nullability)\"\n // Match contract type (can contain /, @, etc.), arrow, native type, then nullability in parentheses\n const typeMatch = rest.match(/^([^\\s→]+)\\s*→\\s*([^\\s(]+)\\s*(\\([^)]+\\))$/);\n if (typeMatch?.[1] && typeMatch[2] && typeMatch[3]) {\n const contractType = typeMatch[1];\n const nativeType = typeMatch[2];\n const nullability = typeMatch[3];\n formattedLabel = `${cyan(columnName)}: ${contractType} → ${dim(nativeType)} ${dim(nullability)}`;\n } else {\n // Fallback if format doesn't match (e.g., no native type or no nullability)\n formattedLabel = `${cyan(columnName)}: ${rest}`;\n }\n } else {\n formattedLabel = node.name;\n }\n break;\n }\n case 'type':\n case 'nullability':\n labelColor = (text) => text; // Default color\n formattedLabel = labelColor(node.name);\n break;\n case 'primaryKey': {\n // Parse \"primary key: columnName\" format - color \"primary key\" dim, columnName cyan\n const pkMatch = node.name.match(/^primary key:\\s*(.+)$/);\n if (pkMatch?.[1]) {\n const columnNames = pkMatch[1];\n formattedLabel = `${dim('primary key')}: ${cyan(columnNames)}`;\n } else {\n formattedLabel = dim(node.name);\n }\n break;\n }\n case 'foreignKey':\n case 'unique':\n case 'index':\n labelColor = dim;\n formattedLabel = labelColor(node.name);\n break;\n case 'dependency': {\n // Parse specific extension message formats\n // \"database is postgres\" -> dim \"database is\", cyan \"postgres\"\n const dbMatch = node.name.match(/^database is\\s+(.+)$/);\n if (dbMatch?.[1]) {\n const dbName = dbMatch[1];\n formattedLabel = `${dim('database is')} ${cyan(dbName)}`;\n } else {\n // \"vector extension is enabled\" -> dim everything except extension name\n // Match pattern: \"extensionName extension is enabled\"\n const extMatch = node.name.match(/^([^\\s]+)\\s+(extension is enabled)$/);\n if (extMatch?.[1] && extMatch[2]) {\n const extName = extMatch[1];\n const rest = extMatch[2];\n formattedLabel = `${cyan(extName)} ${dim(rest)}`;\n } else {\n // Fallback: color entire name with magenta\n labelColor = magenta;\n formattedLabel = labelColor(node.name);\n }\n }\n break;\n }\n default:\n formattedLabel = node.name;\n break;\n }\n } else {\n formattedLabel = node.name;\n }\n\n const statusGlyphColored = statusColor(statusGlyph);\n\n // Build the label with optional message for failure/warn nodes\n let nodeLabel = formattedLabel;\n if (\n (node.status === 'fail' || node.status === 'warn') &&\n node.message &&\n node.message.length > 0\n ) {\n // Always show message for failure/warn nodes - it provides crucial context\n // For parent nodes, the message summarizes child failures\n // For leaf nodes, the message explains the specific issue\n const messageText = formatDimText(`(${node.message})`);\n nodeLabel = `${formattedLabel} ${messageText}`;\n }\n\n // Root node renders without tree characters or | prefix\n // Root node renders without tree characters or prefix\n if (isRoot) {\n lines.push(`${statusGlyphColored} ${nodeLabel}`);\n } else {\n const treeChar = isLast ? '└' : '├';\n const treePrefix = `${formatDimText(treeChar)}─ `;\n lines.push(`${prefix}${treePrefix}${statusGlyphColored} ${nodeLabel}`);\n }\n\n // Render children if present\n if (node.children && node.children.length > 0) {\n const childPrefix = isRoot ? '' : `${prefix}${isLast ? ' ' : `${formatDimText('│')} `}`;\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n if (!child) continue;\n const isLastChild = i === node.children.length - 1;\n const childLines = renderSchemaVerificationTree(child, flags, {\n isLast: isLastChild,\n prefix: childPrefix,\n useColor,\n formatDimText,\n isRoot: false,\n });\n lines.push(...childLines);\n }\n }\n\n return lines;\n}\n\n/**\n * Formats human-readable output for database schema verification.\n */\nexport function formatSchemaVerifyOutput(\n result: VerifyDatabaseSchemaResult,\n flags: GlobalFlags,\n): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatGreen = createColorFormatter(useColor, green);\n const formatRed = createColorFormatter(useColor, red);\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n // Render verification tree first\n const treeLines = renderSchemaVerificationTree(result.schema.root, flags, {\n isLast: true,\n prefix: '',\n useColor,\n formatDimText,\n isRoot: true,\n });\n lines.push(...treeLines);\n\n // Add counts and timings in verbose mode\n if (isVerbose(flags, 1)) {\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n lines.push(\n `${formatDimText(` pass=${result.schema.counts.pass} warn=${result.schema.counts.warn} fail=${result.schema.counts.fail}`)}`,\n );\n }\n\n // Blank line before summary\n lines.push('');\n\n // Summary line at the end: summary with status glyph\n if (result.ok) {\n lines.push(`${formatGreen('✔')} ${result.summary}`);\n } else {\n const codeText = result.code ? ` (${result.code})` : '';\n lines.push(`${formatRed('✖')} ${result.summary}${codeText}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for database schema verification.\n */\nexport function formatSchemaVerifyJson(result: VerifyDatabaseSchemaResult): string {\n return JSON.stringify(result, null, 2);\n}\n\n// ============================================================================\n// Sign Output Formatters\n// ============================================================================\n\n/**\n * Formats human-readable output for database sign.\n */\nexport function formatSignOutput(result: SignDatabaseResult, flags: GlobalFlags): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatGreen = createColorFormatter(useColor, green);\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n if (result.ok) {\n // Main success message in white (not dimmed)\n lines.push(`${formatGreen('✔')} Database signed`);\n\n // Show from -> to hashes with clear labels\n const previousHash = result.marker.previous?.storageHash ?? 'none';\n const currentHash = result.contract.storageHash;\n\n lines.push(`${formatDimText(` from: ${previousHash}`)}`);\n lines.push(`${formatDimText(` to: ${currentHash}`)}`);\n\n if (isVerbose(flags, 1)) {\n if (result.contract.profileHash) {\n lines.push(`${formatDimText(` profileHash: ${result.contract.profileHash}`)}`);\n }\n if (result.marker.previous?.profileHash) {\n lines.push(\n `${formatDimText(` previous profileHash: ${result.marker.previous.profileHash}`)}`,\n );\n }\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for database sign.\n */\nexport function formatSignJson(result: SignDatabaseResult): string {\n return JSON.stringify(result, null, 2);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"verify-Bkycc-Tf.mjs","names":["lines: string[]","formattedLabel: string","statusColor: (text: string) => string","labelColor: (text: string) => string"],"sources":["../src/utils/formatters/verify.ts"],"sourcesContent":["import type {\n CoreSchemaView,\n IntrospectSchemaResult,\n SchemaTreeNode,\n SchemaVerificationNode,\n SignDatabaseResult,\n VerifyDatabaseResult,\n VerifyDatabaseSchemaResult,\n} from '@prisma-next/framework-components/control';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { bold, cyan, dim, green, magenta, red, yellow } from 'colorette';\nimport type { GlobalFlags } from '../global-flags';\nimport { createColorFormatter, formatDim, isVerbose } from './helpers';\n\n// ============================================================================\n// Verify Output Formatters\n// ============================================================================\n\nexport interface DbVerifyCommandSuccessResult {\n readonly ok: true;\n readonly mode: 'full' | 'marker-only';\n readonly summary: string;\n readonly contract: VerifyDatabaseResult['contract'];\n readonly marker?: VerifyDatabaseResult['marker'];\n readonly target: VerifyDatabaseResult['target'];\n readonly missingCodecs?: VerifyDatabaseResult['missingCodecs'];\n readonly codecCoverageSkipped?: VerifyDatabaseResult['codecCoverageSkipped'];\n readonly schema?: {\n readonly summary: string;\n readonly counts: VerifyDatabaseSchemaResult['schema']['counts'];\n readonly strict: boolean;\n };\n readonly warning?: string;\n readonly meta?:\n | (NonNullable<VerifyDatabaseResult['meta']> & {\n readonly schemaVerification: 'performed' | 'skipped';\n })\n | {\n readonly schemaVerification: 'performed' | 'skipped';\n };\n readonly timings: {\n readonly total: number;\n };\n}\n\n/**\n * Formats human-readable output for database verify.\n */\nexport function formatVerifyOutput(\n result: DbVerifyCommandSuccessResult,\n flags: GlobalFlags,\n): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatGreen = createColorFormatter(useColor, green);\n const formatYellow = createColorFormatter(useColor, yellow);\n const formatDimText = (text: string) => formatDim(useColor, text);\n const verificationMode =\n result.mode === 'full'\n ? `marker + schema${result.schema?.strict ? ' (strict)' : ' (tolerant)'}`\n : 'marker only (--marker-only)';\n\n lines.push(`${formatGreen('✔')} ${result.summary}`);\n lines.push(`${formatDimText(` verification: ${verificationMode}`)}`);\n lines.push(`${formatDimText(` storageHash: ${result.contract.storageHash}`)}`);\n if (result.contract.profileHash) {\n lines.push(`${formatDimText(` profileHash: ${result.contract.profileHash}`)}`);\n }\n if (result.mode === 'full' && result.schema && isVerbose(flags, 1)) {\n lines.push(\n `${formatDimText(` schema: pass=${result.schema.counts.pass} warn=${result.schema.counts.warn} fail=${result.schema.counts.fail}`)}`,\n );\n }\n if (result.warning) {\n lines.push('');\n lines.push(`${formatYellow('⚠')} ${result.warning}`);\n }\n\n if (isVerbose(flags, 1)) {\n if (result.codecCoverageSkipped) {\n lines.push(\n `${formatDimText(' Codec coverage check skipped (helper returned no supported types)')}`,\n );\n }\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for database verify.\n */\nexport function formatVerifyJson(result: DbVerifyCommandSuccessResult): string {\n const output = {\n ok: result.ok,\n summary: result.summary,\n mode: result.mode,\n contract: result.contract,\n ...ifDefined('marker', result.marker),\n target: result.target,\n ...ifDefined('missingCodecs', result.missingCodecs),\n ...ifDefined('codecCoverageSkipped', result.codecCoverageSkipped),\n ...ifDefined('schema', result.schema),\n ...ifDefined('warning', result.warning),\n ...ifDefined('meta', result.meta),\n timings: result.timings,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Formats JSON output for database introspection.\n */\nexport function formatIntrospectJson(result: IntrospectSchemaResult<unknown>): string {\n return JSON.stringify(result, null, 2);\n}\n\n/**\n * Renders a schema tree structure from CoreSchemaView.\n * Matches the style of renderSchemaVerificationTree for consistency.\n */\nfunction renderSchemaTree(\n node: SchemaTreeNode,\n flags: GlobalFlags,\n options: {\n readonly isLast: boolean;\n readonly prefix: string;\n readonly useColor: boolean;\n readonly formatDimText: (text: string) => string;\n readonly isRoot?: boolean;\n },\n): string[] {\n const { isLast, prefix, useColor, formatDimText, isRoot = false } = options;\n const lines: string[] = [];\n\n // Format node label with color based on kind (matching schema-verify style)\n let formattedLabel: string = node.label;\n\n if (useColor) {\n switch (node.kind) {\n case 'root':\n formattedLabel = bold(node.label);\n break;\n case 'entity': {\n // Parse \"table tableName\" format - color \"table\" dim, tableName cyan\n const tableMatch = node.label.match(/^table\\s+(.+)$/);\n if (tableMatch?.[1]) {\n const tableName = tableMatch[1];\n formattedLabel = `${dim('table')} ${cyan(tableName)}`;\n } else {\n // Fallback: color entire label with cyan\n formattedLabel = cyan(node.label);\n }\n break;\n }\n case 'collection': {\n // \"columns\" grouping node - dim the label\n formattedLabel = dim(node.label);\n break;\n }\n case 'field': {\n // Parse column name format: \"columnName: typeDisplay (nullability)\"\n // Color code: column name (cyan), type (default), nullability (dim)\n const columnMatch = node.label.match(/^([^:]+):\\s*(.+)$/);\n if (columnMatch?.[1] && columnMatch[2]) {\n const columnName = columnMatch[1];\n const rest = columnMatch[2];\n // Parse rest: \"typeDisplay (nullability)\"\n const typeMatch = rest.match(/^([^\\s(]+)\\s*(\\([^)]+\\))$/);\n if (typeMatch?.[1] && typeMatch[2]) {\n const typeDisplay = typeMatch[1];\n const nullability = typeMatch[2];\n formattedLabel = `${cyan(columnName)}: ${typeDisplay} ${dim(nullability)}`;\n } else {\n // Fallback if format doesn't match\n formattedLabel = `${cyan(columnName)}: ${rest}`;\n }\n } else {\n formattedLabel = node.label;\n }\n break;\n }\n case 'index': {\n // Parse index/unique constraint/primary key formats\n // \"primary key: columnName\" -> dim \"primary key\", cyan columnName\n const pkMatch = node.label.match(/^primary key:\\s*(.+)$/);\n if (pkMatch?.[1]) {\n const columnNames = pkMatch[1];\n formattedLabel = `${dim('primary key')}: ${cyan(columnNames)}`;\n } else {\n // \"unique name\" -> dim \"unique\", cyan \"name\"\n const uniqueMatch = node.label.match(/^unique\\s+(.+)$/);\n if (uniqueMatch?.[1]) {\n const name = uniqueMatch[1];\n formattedLabel = `${dim('unique')} ${cyan(name)}`;\n } else {\n // \"index name\" or \"unique index name\" -> dim label prefix, cyan name\n const indexMatch = node.label.match(/^(unique\\s+)?index\\s+(.+)$/);\n if (indexMatch?.[2]) {\n const indexPrefix = indexMatch[1] ? `${dim('unique')} ` : '';\n const name = indexMatch[2];\n formattedLabel = `${indexPrefix}${dim('index')} ${cyan(name)}`;\n } else {\n formattedLabel = dim(node.label);\n }\n }\n }\n break;\n }\n case 'dependency': {\n // Parse extension message formats similar to schema-verify\n // \"extensionName extension is enabled\" -> cyan extensionName, dim rest\n const extMatch = node.label.match(/^([^\\s]+)\\s+(extension is enabled)$/);\n if (extMatch?.[1] && extMatch[2]) {\n const extName = extMatch[1];\n const rest = extMatch[2];\n formattedLabel = `${cyan(extName)} ${dim(rest)}`;\n } else {\n // Fallback: color entire label with magenta\n formattedLabel = magenta(node.label);\n }\n break;\n }\n default:\n formattedLabel = node.label;\n break;\n }\n }\n\n // Root node renders without tree characters or prefix\n if (isRoot) {\n lines.push(formattedLabel);\n } else {\n const treeChar = isLast ? '└' : '├';\n const treePrefix = `${formatDimText(treeChar)}─ `;\n lines.push(`${prefix}${treePrefix}${formattedLabel}`);\n }\n\n // Render children if present\n if (node.children && node.children.length > 0) {\n const childPrefix = isRoot ? '' : `${prefix}${isLast ? ' ' : `${formatDimText('│')} `}`;\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n if (!child) continue;\n const isLastChild = i === node.children.length - 1;\n const childLines = renderSchemaTree(child, flags, {\n isLast: isLastChild,\n prefix: childPrefix,\n useColor,\n formatDimText,\n isRoot: false,\n });\n lines.push(...childLines);\n }\n }\n\n return lines;\n}\n\n/**\n * Formats human-readable output for database introspection.\n */\nexport function formatIntrospectOutput(\n result: IntrospectSchemaResult<unknown>,\n schemaView: CoreSchemaView | undefined,\n flags: GlobalFlags,\n): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n if (schemaView) {\n // Render tree structure - root node is special (no tree characters)\n const treeLines = renderSchemaTree(schemaView.root, flags, {\n isLast: true,\n prefix: '',\n useColor,\n formatDimText,\n isRoot: true,\n });\n lines.push(...treeLines);\n } else {\n // Fallback: print summary when toSchemaView is not available\n lines.push(`✔ ${result.summary}`);\n if (isVerbose(flags, 1)) {\n lines.push(` Target: ${result.target.familyId}/${result.target.id}`);\n if (result.meta?.dbUrl) {\n lines.push(` Database: ${result.meta.dbUrl}`);\n }\n }\n }\n\n // Add timings in verbose mode\n if (isVerbose(flags, 1)) {\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Renders a schema verification tree structure from SchemaVerificationNode.\n * Similar to renderSchemaTree but for verification nodes with status-based colors and glyphs.\n */\nfunction renderSchemaVerificationTree(\n node: SchemaVerificationNode,\n flags: GlobalFlags,\n options: {\n readonly isLast: boolean;\n readonly prefix: string;\n readonly useColor: boolean;\n readonly formatDimText: (text: string) => string;\n readonly isRoot?: boolean;\n },\n): string[] {\n const { isLast, prefix, useColor, formatDimText, isRoot = false } = options;\n const lines: string[] = [];\n\n // Format status glyph and color based on status\n let statusGlyph = '';\n let statusColor: (text: string) => string = (text) => text;\n if (useColor) {\n switch (node.status) {\n case 'pass':\n statusGlyph = '✔';\n statusColor = green;\n break;\n case 'warn':\n statusGlyph = '⚠';\n statusColor = (text) => (useColor ? yellow(text) : text);\n break;\n case 'fail':\n statusGlyph = '✖';\n statusColor = red;\n break;\n }\n } else {\n switch (node.status) {\n case 'pass':\n statusGlyph = '✔';\n break;\n case 'warn':\n statusGlyph = '⚠';\n break;\n case 'fail':\n statusGlyph = '✖';\n break;\n }\n }\n\n // Format node label with color based on kind\n // For column nodes, we need to parse the name to color code different parts\n let labelColor: (text: string) => string = (text) => text;\n let formattedLabel: string = node.name;\n\n if (useColor) {\n switch (node.kind) {\n case 'contract':\n case 'schema':\n labelColor = bold;\n formattedLabel = labelColor(node.name);\n break;\n case 'table': {\n // Parse \"table tableName\" format - color \"table\" dim, tableName cyan\n const tableMatch = node.name.match(/^table\\s+(.+)$/);\n if (tableMatch?.[1]) {\n const tableName = tableMatch[1];\n formattedLabel = `${dim('table')} ${cyan(tableName)}`;\n } else {\n formattedLabel = dim(node.name);\n }\n break;\n }\n case 'columns':\n labelColor = dim;\n formattedLabel = labelColor(node.name);\n break;\n case 'column': {\n // Parse column name format: \"columnName: contractType -> nativeType (nullability)\"\n // Color code: column name (cyan), contract type (default), native type (dim), nullability (dim)\n const columnMatch = node.name.match(/^([^:]+):\\s*(.+)$/);\n if (columnMatch?.[1] && columnMatch[2]) {\n const columnName = columnMatch[1];\n const rest = columnMatch[2];\n // Parse rest: \"contractType -> nativeType (nullability)\"\n // Match contract type (can contain /, @, etc.), arrow, native type, then nullability in parentheses\n const typeMatch = rest.match(/^([^\\s→]+)\\s*→\\s*([^\\s(]+)\\s*(\\([^)]+\\))$/);\n if (typeMatch?.[1] && typeMatch[2] && typeMatch[3]) {\n const contractType = typeMatch[1];\n const nativeType = typeMatch[2];\n const nullability = typeMatch[3];\n formattedLabel = `${cyan(columnName)}: ${contractType} → ${dim(nativeType)} ${dim(nullability)}`;\n } else {\n // Fallback if format doesn't match (e.g., no native type or no nullability)\n formattedLabel = `${cyan(columnName)}: ${rest}`;\n }\n } else {\n formattedLabel = node.name;\n }\n break;\n }\n case 'type':\n case 'nullability':\n labelColor = (text) => text; // Default color\n formattedLabel = labelColor(node.name);\n break;\n case 'primaryKey': {\n // Parse \"primary key: columnName\" format - color \"primary key\" dim, columnName cyan\n const pkMatch = node.name.match(/^primary key:\\s*(.+)$/);\n if (pkMatch?.[1]) {\n const columnNames = pkMatch[1];\n formattedLabel = `${dim('primary key')}: ${cyan(columnNames)}`;\n } else {\n formattedLabel = dim(node.name);\n }\n break;\n }\n case 'foreignKey':\n case 'unique':\n case 'index':\n labelColor = dim;\n formattedLabel = labelColor(node.name);\n break;\n case 'dependency': {\n // Parse specific extension message formats\n // \"database is postgres\" -> dim \"database is\", cyan \"postgres\"\n const dbMatch = node.name.match(/^database is\\s+(.+)$/);\n if (dbMatch?.[1]) {\n const dbName = dbMatch[1];\n formattedLabel = `${dim('database is')} ${cyan(dbName)}`;\n } else {\n // \"vector extension is enabled\" -> dim everything except extension name\n // Match pattern: \"extensionName extension is enabled\"\n const extMatch = node.name.match(/^([^\\s]+)\\s+(extension is enabled)$/);\n if (extMatch?.[1] && extMatch[2]) {\n const extName = extMatch[1];\n const rest = extMatch[2];\n formattedLabel = `${cyan(extName)} ${dim(rest)}`;\n } else {\n // Fallback: color entire name with magenta\n labelColor = magenta;\n formattedLabel = labelColor(node.name);\n }\n }\n break;\n }\n default:\n formattedLabel = node.name;\n break;\n }\n } else {\n formattedLabel = node.name;\n }\n\n const statusGlyphColored = statusColor(statusGlyph);\n\n // Build the label with optional message for failure/warn nodes\n let nodeLabel = formattedLabel;\n if (\n (node.status === 'fail' || node.status === 'warn') &&\n node.message &&\n node.message.length > 0\n ) {\n // Always show message for failure/warn nodes - it provides crucial context\n // For parent nodes, the message summarizes child failures\n // For leaf nodes, the message explains the specific issue\n const messageText = formatDimText(`(${node.message})`);\n nodeLabel = `${formattedLabel} ${messageText}`;\n }\n\n // Root node renders without tree characters or | prefix\n // Root node renders without tree characters or prefix\n if (isRoot) {\n lines.push(`${statusGlyphColored} ${nodeLabel}`);\n } else {\n const treeChar = isLast ? '└' : '├';\n const treePrefix = `${formatDimText(treeChar)}─ `;\n lines.push(`${prefix}${treePrefix}${statusGlyphColored} ${nodeLabel}`);\n }\n\n // Render children if present\n if (node.children && node.children.length > 0) {\n const childPrefix = isRoot ? '' : `${prefix}${isLast ? ' ' : `${formatDimText('│')} `}`;\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n if (!child) continue;\n const isLastChild = i === node.children.length - 1;\n const childLines = renderSchemaVerificationTree(child, flags, {\n isLast: isLastChild,\n prefix: childPrefix,\n useColor,\n formatDimText,\n isRoot: false,\n });\n lines.push(...childLines);\n }\n }\n\n return lines;\n}\n\n/**\n * Formats human-readable output for database schema verification.\n */\nexport function formatSchemaVerifyOutput(\n result: VerifyDatabaseSchemaResult,\n flags: GlobalFlags,\n): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatGreen = createColorFormatter(useColor, green);\n const formatRed = createColorFormatter(useColor, red);\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n // Render verification tree first\n const treeLines = renderSchemaVerificationTree(result.schema.root, flags, {\n isLast: true,\n prefix: '',\n useColor,\n formatDimText,\n isRoot: true,\n });\n lines.push(...treeLines);\n\n // Add counts and timings in verbose mode\n if (isVerbose(flags, 1)) {\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n lines.push(\n `${formatDimText(` pass=${result.schema.counts.pass} warn=${result.schema.counts.warn} fail=${result.schema.counts.fail}`)}`,\n );\n }\n\n // Blank line before summary\n lines.push('');\n\n // Summary line at the end: summary with status glyph\n if (result.ok) {\n lines.push(`${formatGreen('✔')} ${result.summary}`);\n } else {\n const codeText = result.code ? ` (${result.code})` : '';\n lines.push(`${formatRed('✖')} ${result.summary}${codeText}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for database schema verification.\n */\nexport function formatSchemaVerifyJson(result: VerifyDatabaseSchemaResult): string {\n return JSON.stringify(result, null, 2);\n}\n\n// ============================================================================\n// Sign Output Formatters\n// ============================================================================\n\n/**\n * Formats human-readable output for database sign.\n */\nexport function formatSignOutput(result: SignDatabaseResult, flags: GlobalFlags): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatGreen = createColorFormatter(useColor, green);\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n if (result.ok) {\n // Main success message in white (not dimmed)\n lines.push(`${formatGreen('✔')} Database signed`);\n\n // Show from -> to hashes with clear labels\n const previousHash = result.marker.previous?.storageHash ?? 'none';\n const currentHash = result.contract.storageHash;\n\n lines.push(`${formatDimText(` from: ${previousHash}`)}`);\n lines.push(`${formatDimText(` to: ${currentHash}`)}`);\n\n if (isVerbose(flags, 1)) {\n if (result.contract.profileHash) {\n lines.push(`${formatDimText(` profileHash: ${result.contract.profileHash}`)}`);\n }\n if (result.marker.previous?.profileHash) {\n lines.push(\n `${formatDimText(` previous profileHash: ${result.marker.previous.profileHash}`)}`,\n );\n }\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for database sign.\n */\nexport function formatSignJson(result: SignDatabaseResult): string {\n return JSON.stringify(result, null, 2);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma-next/cli",
3
- "version": "0.5.0-dev.4",
3
+ "version": "0.5.0-dev.6",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "files": [
@@ -19,33 +19,34 @@
19
19
  "colorette": "^2.0.20",
20
20
  "commander": "^12.0.0",
21
21
  "esbuild": "^0.25.11",
22
+ "jsonc-parser": "^3.3.1",
22
23
  "package-manager-detector": "^1.6.0",
23
24
  "pathe": "^2.0.3",
24
25
  "string-width": "^8.2.0",
25
26
  "strip-ansi": "^7.1.2",
26
27
  "wrap-ansi": "^10.0.0",
27
- "@prisma-next/config": "0.5.0-dev.4",
28
- "@prisma-next/contract": "0.5.0-dev.4",
29
- "@prisma-next/emitter": "0.5.0-dev.4",
30
- "@prisma-next/errors": "0.5.0-dev.4",
31
- "@prisma-next/psl-printer": "0.5.0-dev.4",
32
- "@prisma-next/migration-tools": "0.5.0-dev.4",
33
- "@prisma-next/framework-components": "0.5.0-dev.4",
34
- "@prisma-next/utils": "0.5.0-dev.4"
28
+ "@prisma-next/emitter": "0.5.0-dev.6",
29
+ "@prisma-next/config": "0.5.0-dev.6",
30
+ "@prisma-next/contract": "0.5.0-dev.6",
31
+ "@prisma-next/errors": "0.5.0-dev.6",
32
+ "@prisma-next/framework-components": "0.5.0-dev.6",
33
+ "@prisma-next/migration-tools": "0.5.0-dev.6",
34
+ "@prisma-next/psl-printer": "0.5.0-dev.6",
35
+ "@prisma-next/utils": "0.5.0-dev.6"
35
36
  },
36
37
  "devDependencies": {
37
38
  "@types/node": "24.10.4",
38
39
  "tsdown": "0.18.4",
39
40
  "typescript": "5.9.3",
40
41
  "vitest": "4.0.17",
41
- "@prisma-next/sql-contract": "0.5.0-dev.4",
42
- "@prisma-next/sql-contract-emitter": "0.5.0-dev.4",
43
- "@prisma-next/sql-contract-ts": "0.5.0-dev.4",
44
- "@prisma-next/sql-operations": "0.5.0-dev.4",
42
+ "@prisma-next/sql-contract": "0.5.0-dev.6",
43
+ "@prisma-next/sql-contract-ts": "0.5.0-dev.6",
44
+ "@prisma-next/sql-runtime": "0.5.0-dev.6",
45
+ "@prisma-next/sql-contract-emitter": "0.5.0-dev.6",
46
+ "@prisma-next/tsdown": "0.0.0",
47
+ "@prisma-next/sql-operations": "0.5.0-dev.6",
45
48
  "@prisma-next/test-utils": "0.0.1",
46
- "@prisma-next/sql-runtime": "0.5.0-dev.4",
47
- "@prisma-next/tsconfig": "0.0.0",
48
- "@prisma-next/tsdown": "0.0.0"
49
+ "@prisma-next/tsconfig": "0.0.0"
49
50
  },
50
51
  "exports": {
51
52
  ".": {
@@ -56,6 +57,10 @@
56
57
  "types": "./dist/exports/config-types.d.mts",
57
58
  "import": "./dist/exports/config-types.mjs"
58
59
  },
60
+ "./init-output": {
61
+ "types": "./dist/exports/init-output.d.mts",
62
+ "import": "./dist/exports/init-output.mjs"
63
+ },
59
64
  "./commands/db-init": {
60
65
  "types": "./dist/commands/db-init.d.mts",
61
66
  "import": "./dist/commands/db-init.mjs"
@@ -0,0 +1,141 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { dirname, join } from 'pathe';
3
+
4
+ /**
5
+ * Catalog entry detected in a `pnpm-workspace.yaml` that overrides one of
6
+ * the packages `init` installs. The `version` is the raw value as written
7
+ * in the workspace file (no normalisation), so the warning surfaces the
8
+ * exact text the user can search for if they want to find the override.
9
+ */
10
+ export interface PnpmCatalogOverride {
11
+ readonly name: string;
12
+ readonly version: string;
13
+ }
14
+
15
+ /**
16
+ * Result of scanning for a pnpm workspace catalog that overrides any of
17
+ * the packages `init` is about to install. Returns `null` when no
18
+ * `pnpm-workspace.yaml` is found in `baseDir` or any ancestor; an empty
19
+ * `entries` array means a workspace exists but contains none of our
20
+ * packages.
21
+ */
22
+ export interface PnpmCatalogScanResult {
23
+ /** Absolute path of the `pnpm-workspace.yaml` that was consulted. */
24
+ readonly workspaceFile: string;
25
+ readonly entries: readonly PnpmCatalogOverride[];
26
+ }
27
+
28
+ /**
29
+ * Walks up from `baseDir` looking for `pnpm-workspace.yaml`, then scans
30
+ * its top-level `catalog:` block for entries that match any of `packages`.
31
+ *
32
+ * Implements FR7.3 / Spec Decision 8 (honour-and-warn): when `init` runs
33
+ * inside a pnpm workspace whose catalog overrides one of the packages it
34
+ * installs, surface a structured warning so the user knows the catalog
35
+ * version (not the published `latest`) is what ended up in their
36
+ * `node_modules`. pnpm itself does this silently; the warning closes the
37
+ * "looks fine, must be wrong version six months later" gap.
38
+ *
39
+ * Notes / scope:
40
+ *
41
+ * - We only inspect the unnamed top-level `catalog:` block. pnpm also
42
+ * supports `catalogs:` (plural — *named* catalogs referenced via
43
+ * `catalog:foo` specifiers); those don't apply to a vanilla
44
+ * `pnpm add prisma-next` invocation, so we skip them.
45
+ * - We don't validate YAML syntax exhaustively. The file format pnpm
46
+ * ships is line-oriented and well-known; a minimal regex is more
47
+ * robust than depending on a YAML parser for one warning.
48
+ * - We don't compare against the registry's `latest` — pnpm uses the
49
+ * catalog version regardless, so the warning fires whenever a match
50
+ * exists. The user-facing copy explains how to opt out.
51
+ */
52
+ export function detectPnpmCatalogOverrides(
53
+ baseDir: string,
54
+ packages: readonly string[],
55
+ ): PnpmCatalogScanResult | null {
56
+ const workspaceFile = findNearestPnpmWorkspaceFile(baseDir);
57
+ if (workspaceFile === null) {
58
+ return null;
59
+ }
60
+
61
+ const contents = readFileSync(workspaceFile, 'utf-8');
62
+ const catalog = extractCatalogBlock(contents);
63
+ if (catalog === null) {
64
+ return { workspaceFile, entries: [] };
65
+ }
66
+
67
+ const wanted = new Set(packages);
68
+ const entries: PnpmCatalogOverride[] = [];
69
+ for (const [name, version] of catalog) {
70
+ if (wanted.has(name)) {
71
+ entries.push({ name, version });
72
+ }
73
+ }
74
+ return { workspaceFile, entries };
75
+ }
76
+
77
+ function findNearestPnpmWorkspaceFile(baseDir: string): string | null {
78
+ let dir = baseDir;
79
+ let prev = '';
80
+ while (dir !== prev) {
81
+ const candidate = join(dir, 'pnpm-workspace.yaml');
82
+ if (existsSync(candidate)) {
83
+ return candidate;
84
+ }
85
+ prev = dir;
86
+ dir = dirname(dir);
87
+ }
88
+ return null;
89
+ }
90
+
91
+ /**
92
+ * Returns the entries inside the top-level `catalog:` block as `[name, version]`
93
+ * pairs in document order, or `null` when no `catalog:` block exists.
94
+ *
95
+ * The parser is intentionally minimal: it reads line-by-line, locates the
96
+ * top-level `catalog:` line (no leading whitespace), then collects every
97
+ * subsequent indented line of the form `<key>: <value>` until the next
98
+ * top-level key (or end of file). Quotes around `<key>` and `<value>`
99
+ * are stripped; comments (`#…`) are ignored.
100
+ */
101
+ function extractCatalogBlock(contents: string): Array<[string, string]> | null {
102
+ const lines = contents.split(/\r?\n/);
103
+ const startIdx = lines.findIndex((line) => /^catalog\s*:\s*$/.test(line));
104
+ if (startIdx === -1) {
105
+ return null;
106
+ }
107
+
108
+ const entries: Array<[string, string]> = [];
109
+ for (let i = startIdx + 1; i < lines.length; i++) {
110
+ const raw = lines[i] ?? '';
111
+ if (raw.trim() === '' || /^\s*#/.test(raw)) {
112
+ continue;
113
+ }
114
+ if (!/^\s/.test(raw)) {
115
+ // Hit the next top-level key — catalog block ended.
116
+ break;
117
+ }
118
+ const match = raw.match(/^\s+(?:'([^']+)'|"([^"]+)"|([^:\s'"]+))\s*:\s*(.*?)\s*(?:#.*)?$/);
119
+ if (!match) {
120
+ continue;
121
+ }
122
+ const name = match[1] ?? match[2] ?? match[3];
123
+ if (name === undefined) continue;
124
+ const rawValue = match[4] ?? '';
125
+ const version = stripQuotes(rawValue.trim());
126
+ if (version === '') continue;
127
+ entries.push([name, version]);
128
+ }
129
+ return entries;
130
+ }
131
+
132
+ function stripQuotes(value: string): string {
133
+ if (value.length >= 2) {
134
+ const first = value[0];
135
+ const last = value[value.length - 1];
136
+ if ((first === '"' && last === '"') || (first === "'" && last === "'")) {
137
+ return value.slice(1, -1);
138
+ }
139
+ }
140
+ return value;
141
+ }