@prisma-next/cli 0.14.0-dev.4 → 0.14.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 (122) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.mjs +27 -13
  3. package/dist/cli.mjs.map +1 -1
  4. package/dist/{client-CJzuo5wX.mjs → client-EnAjSmZg.mjs} +4 -4
  5. package/dist/{client-CJzuo5wX.mjs.map → client-EnAjSmZg.mjs.map} +1 -1
  6. package/dist/{command-helpers-DGMvGBeX.mjs → command-helpers-6cNJZ863.mjs} +3 -3
  7. package/dist/{command-helpers-DGMvGBeX.mjs.map → command-helpers-6cNJZ863.mjs.map} +1 -1
  8. package/dist/commands/contract-emit.mjs +1 -1
  9. package/dist/commands/contract-infer.mjs +1 -1
  10. package/dist/commands/db-init.mjs +3 -3
  11. package/dist/commands/db-schema.mjs +3 -3
  12. package/dist/commands/db-sign.mjs +5 -5
  13. package/dist/commands/db-sign.mjs.map +1 -1
  14. package/dist/commands/db-update.mjs +4 -4
  15. package/dist/commands/db-verify.mjs +1 -1
  16. package/dist/commands/migrate.d.mts +1 -1
  17. package/dist/commands/migrate.mjs +6 -6
  18. package/dist/commands/migrate.mjs.map +1 -1
  19. package/dist/commands/migration-check.d.mts +1 -1
  20. package/dist/commands/migration-check.mjs +1 -1
  21. package/dist/commands/migration-graph.d.mts +1 -1
  22. package/dist/commands/migration-graph.mjs +4 -4
  23. package/dist/commands/migration-graph.mjs.map +1 -1
  24. package/dist/commands/migration-list.d.mts +1 -1
  25. package/dist/commands/migration-list.mjs +1 -1
  26. package/dist/commands/migration-log.d.mts +1 -1
  27. package/dist/commands/migration-log.mjs +1 -1
  28. package/dist/commands/migration-new.mjs +4 -4
  29. package/dist/commands/migration-new.mjs.map +1 -1
  30. package/dist/commands/migration-plan.mjs +1 -1
  31. package/dist/commands/migration-show.mjs +4 -4
  32. package/dist/commands/migration-show.mjs.map +1 -1
  33. package/dist/commands/migration-status.d.mts +1 -1
  34. package/dist/commands/migration-status.mjs +1 -1
  35. package/dist/commands/ref.d.mts +1 -1
  36. package/dist/commands/ref.mjs +3 -3
  37. package/dist/commands/ref.mjs.map +1 -1
  38. package/dist/commands/telemetry/index.mjs +1 -1
  39. package/dist/{contract-at-errors-CFXsstzm.mjs → contract-at-errors-DdxWRjMc.mjs} +2 -2
  40. package/dist/{contract-at-errors-CFXsstzm.mjs.map → contract-at-errors-DdxWRjMc.mjs.map} +1 -1
  41. package/dist/{contract-emit-B_qriF8B.mjs → contract-emit-D4xDDFkO.mjs} +6 -6
  42. package/dist/contract-emit-D4xDDFkO.mjs.map +1 -0
  43. package/dist/{contract-emit-C8HmtboH.mjs → contract-emit-fz6pDR2n.mjs} +4 -4
  44. package/dist/contract-emit-fz6pDR2n.mjs.map +1 -0
  45. package/dist/{contract-infer-BYT_ra_U.mjs → contract-infer-Ars4yjBv.mjs} +3 -3
  46. package/dist/{contract-infer-BYT_ra_U.mjs.map → contract-infer-Ars4yjBv.mjs.map} +1 -1
  47. package/dist/{contract-space-aggregate-loader-ClI1KN6d.mjs → contract-space-aggregate-loader-Bu2fjSWV.mjs} +2 -2
  48. package/dist/{contract-space-aggregate-loader-ClI1KN6d.mjs.map → contract-space-aggregate-loader-Bu2fjSWV.mjs.map} +1 -1
  49. package/dist/{db-verify-C24FKhb7.mjs → db-verify-CZxRV_er.mjs} +5 -5
  50. package/dist/db-verify-CZxRV_er.mjs.map +1 -0
  51. package/dist/exports/control-api.d.mts +1 -1
  52. package/dist/exports/control-api.mjs +2 -2
  53. package/dist/exports/index.mjs +2 -2
  54. package/dist/{format-C8yv6mXH.mjs → format-C3XdaDuA.mjs} +3 -3
  55. package/dist/format-C3XdaDuA.mjs.map +1 -0
  56. package/dist/{framework-components-YVQHhPH7.mjs → framework-components-5RljWT7Q.mjs} +2 -2
  57. package/dist/{framework-components-YVQHhPH7.mjs.map → framework-components-5RljWT7Q.mjs.map} +1 -1
  58. package/dist/{init-a3-ljrL3.mjs → init-C79Yb97u.mjs} +3 -3
  59. package/dist/{init-a3-ljrL3.mjs.map → init-C79Yb97u.mjs.map} +1 -1
  60. package/dist/{inspect-live-schema-DF6IwcDl.mjs → inspect-live-schema-DR8Eyt29.mjs} +4 -4
  61. package/dist/inspect-live-schema-DR8Eyt29.mjs.map +1 -0
  62. package/dist/{migration-check-VwM8xCZV.mjs → migration-check-CTcJfCC4.mjs} +5 -6
  63. package/dist/migration-check-CTcJfCC4.mjs.map +1 -0
  64. package/dist/migration-cli.mjs +1 -1
  65. package/dist/migration-cli.mjs.map +1 -1
  66. package/dist/{migration-command-scaffold-DA-Lhx6o.mjs → migration-command-scaffold-6OT_Q-3Y.mjs} +4 -4
  67. package/dist/migration-command-scaffold-6OT_Q-3Y.mjs.map +1 -0
  68. package/dist/{migration-list-DlJJ_38Z.mjs → migration-list-DVHVmMrf.mjs} +4 -4
  69. package/dist/migration-list-DVHVmMrf.mjs.map +1 -0
  70. package/dist/{migration-log-CG0qQAFm.mjs → migration-log-CQpc9JoL.mjs} +4 -4
  71. package/dist/migration-log-CQpc9JoL.mjs.map +1 -0
  72. package/dist/{migration-path-target-Ce6OZImp.mjs → migration-path-target-zK6RG6JY.mjs} +2 -2
  73. package/dist/{migration-path-target-Ce6OZImp.mjs.map → migration-path-target-zK6RG6JY.mjs.map} +1 -1
  74. package/dist/{migration-plan-W_E8FQOk.mjs → migration-plan-o2aqjnfd.mjs} +6 -6
  75. package/dist/migration-plan-o2aqjnfd.mjs.map +1 -0
  76. package/dist/{migration-status-CD-LC2Ip.mjs → migration-status-2aXGtYMW.mjs} +6 -7
  77. package/dist/migration-status-2aXGtYMW.mjs.map +1 -0
  78. package/dist/{telemetry-BIM4beEO.mjs → telemetry-BckOSUWG.mjs} +2 -2
  79. package/dist/{telemetry-BIM4beEO.mjs.map → telemetry-BckOSUWG.mjs.map} +1 -1
  80. package/dist/{types-C_tYiJYx.d.mts → types-qETCCNbe.d.mts} +2 -2
  81. package/dist/{types-C_tYiJYx.d.mts.map → types-qETCCNbe.d.mts.map} +1 -1
  82. package/dist/{verify-DcOYZ1tH.mjs → verify-DLqhL2LU.mjs} +2 -2
  83. package/dist/{verify-DcOYZ1tH.mjs.map → verify-DLqhL2LU.mjs.map} +1 -1
  84. package/package.json +21 -24
  85. package/src/cli.ts +3 -0
  86. package/src/commands/contract-emit.ts +1 -1
  87. package/src/commands/db-sign.ts +1 -1
  88. package/src/commands/db-verify.ts +1 -1
  89. package/src/commands/inspect-live-schema.ts +1 -1
  90. package/src/commands/lsp.ts +30 -0
  91. package/src/commands/migrate.ts +1 -1
  92. package/src/commands/migration-check.ts +1 -1
  93. package/src/commands/migration-graph.ts +1 -1
  94. package/src/commands/migration-list.ts +1 -1
  95. package/src/commands/migration-log.ts +1 -1
  96. package/src/commands/migration-new.ts +1 -1
  97. package/src/commands/migration-plan.ts +1 -1
  98. package/src/commands/migration-show.ts +1 -1
  99. package/src/commands/migration-status.ts +1 -1
  100. package/src/commands/ref.ts +1 -1
  101. package/src/control-api/operations/contract-emit.ts +1 -1
  102. package/src/control-api/operations/format.ts +1 -2
  103. package/src/migration-cli.ts +1 -1
  104. package/src/utils/migration-command-scaffold.ts +1 -1
  105. package/dist/config-loader-p9JMrekQ.mjs +0 -88
  106. package/dist/config-loader-p9JMrekQ.mjs.map +0 -1
  107. package/dist/config-loader.d.mts +0 -16
  108. package/dist/config-loader.d.mts.map +0 -1
  109. package/dist/config-loader.mjs +0 -2
  110. package/dist/contract-emit-B_qriF8B.mjs.map +0 -1
  111. package/dist/contract-emit-C8HmtboH.mjs.map +0 -1
  112. package/dist/db-verify-C24FKhb7.mjs.map +0 -1
  113. package/dist/format-C8yv6mXH.mjs.map +0 -1
  114. package/dist/inspect-live-schema-DF6IwcDl.mjs.map +0 -1
  115. package/dist/migration-check-VwM8xCZV.mjs.map +0 -1
  116. package/dist/migration-command-scaffold-DA-Lhx6o.mjs.map +0 -1
  117. package/dist/migration-list-DlJJ_38Z.mjs.map +0 -1
  118. package/dist/migration-log-CG0qQAFm.mjs.map +0 -1
  119. package/dist/migration-plan-W_E8FQOk.mjs.map +0 -1
  120. package/dist/migration-status-CD-LC2Ip.mjs.map +0 -1
  121. package/src/config-loader.ts +0 -92
  122. package/src/config-path-validation.ts +0 -74
@@ -1,7 +1,7 @@
1
- import { t as loadConfig } from "./config-loader-p9JMrekQ.mjs";
2
- import { A as formatStyledHeader, F as CliStructuredError, U as errorDriverRequired, V as errorDatabaseConnectionRequired, c as sanitizeErrorMessage, ct as errorUnexpected, i as maskConnectionUrl } from "./command-helpers-DGMvGBeX.mjs";
1
+ import { A as formatStyledHeader, F as CliStructuredError, U as errorDriverRequired, V as errorDatabaseConnectionRequired, c as sanitizeErrorMessage, ct as errorUnexpected, i as maskConnectionUrl } from "./command-helpers-6cNJZ863.mjs";
3
2
  import { t as createProgressAdapter } from "./progress-adapter-CjAeTxY_.mjs";
4
- import { t as createControlClient } from "./client-CJzuo5wX.mjs";
3
+ import { t as createControlClient } from "./client-EnAjSmZg.mjs";
4
+ import { loadConfig } from "@prisma-next/config-loader";
5
5
  import { notOk, ok } from "@prisma-next/utils/result";
6
6
  import { relative, resolve } from "pathe";
7
7
  //#region src/commands/inspect-live-schema.ts
@@ -88,4 +88,4 @@ async function inspectLiveSchema(options, flags, ui, startTime, context) {
88
88
  //#endregion
89
89
  export { inspectLiveSchema as t };
90
90
 
91
- //# sourceMappingURL=inspect-live-schema-DF6IwcDl.mjs.map
91
+ //# sourceMappingURL=inspect-live-schema-DR8Eyt29.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspect-live-schema-DR8Eyt29.mjs","names":[],"sources":["../src/commands/inspect-live-schema.ts"],"sourcesContent":["import { loadConfig } from '@prisma-next/config-loader';\nimport type { AuthoringPslBlockDescriptorNamespace } from '@prisma-next/framework-components/authoring';\nimport type { CoreSchemaView } from '@prisma-next/framework-components/control';\nimport type { PslDocumentAst } from '@prisma-next/framework-components/psl-ast';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { relative, resolve } from 'pathe';\nimport { createControlClient } from '../control-api/client';\nimport {\n CliStructuredError,\n errorDatabaseConnectionRequired,\n errorDriverRequired,\n errorUnexpected,\n} from '../utils/cli-errors';\nimport { maskConnectionUrl, sanitizeErrorMessage } from '../utils/command-helpers';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport type { CommonCommandOptions, GlobalFlags } from '../utils/global-flags';\nimport { createProgressAdapter } from '../utils/progress-adapter';\nimport type { TerminalUI } from '../utils/terminal-ui';\n\nexport interface InspectLiveSchemaOptions extends CommonCommandOptions {\n readonly db?: string;\n readonly config?: string;\n}\n\ninterface InspectLiveSchemaContext {\n readonly commandName: string;\n readonly description: string;\n readonly url: string;\n}\n\ntype LoadedCliConfig = Awaited<ReturnType<typeof loadConfig>>;\n\nexport interface InspectLiveSchemaResult {\n readonly config: LoadedCliConfig;\n readonly schema: unknown;\n readonly schemaView: CoreSchemaView | undefined;\n /**\n * PSL AST inferred from the introspected schema, when the configured family\n * implements `PslContractInferCapable`. `undefined` for families that do not\n * support inference (e.g. Mongo today).\n */\n readonly pslContractAst: PslDocumentAst | undefined;\n /**\n * The assembled PSL block descriptors from the control stack — the full set of\n * extension-contributed top-level block descriptors. Downstream commands pass\n * this through to `printPsl` so contributed-block AST nodes round-trip back to\n * source.\n */\n readonly pslBlockDescriptors: AuthoringPslBlockDescriptorNamespace;\n readonly target: {\n readonly familyId: string;\n readonly id: string;\n };\n readonly meta: {\n readonly configPath?: string;\n readonly dbUrl?: string;\n };\n readonly timings: {\n readonly total: number;\n };\n}\n\nexport async function inspectLiveSchema(\n options: InspectLiveSchemaOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n startTime: number,\n context: InspectLiveSchemaContext,\n): Promise<Result<InspectLiveSchemaResult, CliStructuredError>> {\n let config: LoadedCliConfig;\n try {\n config = await loadConfig(options.config);\n } catch (error) {\n if (CliStructuredError.is(error)) {\n return notOk(error);\n }\n\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: 'Failed to load config',\n }),\n );\n }\n\n const configPath = options.config\n ? relative(process.cwd(), resolve(options.config))\n : 'prisma-next.config.ts';\n\n if (!flags.json && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n ];\n\n if (options.db) {\n details.push({ label: 'database', value: maskConnectionUrl(options.db) });\n } else if (config.db?.connection && typeof config.db.connection === 'string') {\n details.push({ label: 'database', value: maskConnectionUrl(config.db.connection) });\n }\n\n ui.stderr(\n formatStyledHeader({\n command: context.commandName,\n description: context.description,\n url: context.url,\n details,\n flags,\n }),\n );\n }\n\n const dbConnection = options.db ?? config.db?.connection;\n if (!dbConnection) {\n return notOk(\n errorDatabaseConnectionRequired({\n why: `Database connection is required for ${context.commandName} (set db.connection in ${configPath}, or pass --db <url>)`,\n commandName: context.commandName,\n }),\n );\n }\n\n if (!config.driver) {\n return notOk(\n errorDriverRequired({\n why: `Config.driver is required for ${context.commandName}`,\n }),\n );\n }\n\n const client = createControlClient({\n family: config.family,\n target: config.target,\n adapter: config.adapter,\n driver: config.driver,\n extensionPacks: config.extensionPacks ?? [],\n });\n const onProgress = createProgressAdapter({ ui, flags });\n\n try {\n const schema = await client.introspect({\n connection: dbConnection,\n onProgress,\n });\n const schemaView = client.toSchemaView(schema);\n const pslContractAst = client.inferPslContract(schema);\n const pslBlockDescriptors = client.getPslBlockDescriptors();\n\n const dbUrl = typeof dbConnection === 'string' ? maskConnectionUrl(dbConnection) : undefined;\n\n return ok({\n config,\n schema,\n schemaView,\n pslContractAst,\n pslBlockDescriptors,\n target: {\n familyId: config.family.familyId,\n id: config.target.targetId,\n },\n meta: {\n configPath,\n ...(dbUrl ? { dbUrl } : {}),\n },\n timings: {\n total: Date.now() - startTime,\n },\n });\n } catch (error) {\n if (CliStructuredError.is(error)) {\n return notOk(error);\n }\n\n const rawMessage = error instanceof Error ? error.message : String(error);\n const safeMessage = sanitizeErrorMessage(\n rawMessage,\n typeof dbConnection === 'string' ? dbConnection : undefined,\n );\n return notOk(\n errorUnexpected(safeMessage, {\n why: `Unexpected error during ${context.commandName}: ${safeMessage}`,\n }),\n );\n } finally {\n await client.close();\n }\n}\n"],"mappings":";;;;;;;AA8DA,eAAsB,kBACpB,SACA,OACA,IACA,WACA,SAC8D;CAC9D,IAAI;CACJ,IAAI;EACF,SAAS,MAAM,WAAW,QAAQ,MAAM;CAC1C,SAAS,OAAO;EACd,IAAI,mBAAmB,GAAG,KAAK,GAC7B,OAAO,MAAM,KAAK;EAGpB,OAAO,MACL,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG,EACtE,KAAK,wBACP,CAAC,CACH;CACF;CAEA,MAAM,aAAa,QAAQ,SACvB,SAAS,QAAQ,IAAI,GAAG,QAAQ,QAAQ,MAAM,CAAC,IAC/C;CAEJ,IAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,OAAO;EAC/B,MAAM,UAAmD,CACvD;GAAE,OAAO;GAAU,OAAO;EAAW,CACvC;EAEA,IAAI,QAAQ,IACV,QAAQ,KAAK;GAAE,OAAO;GAAY,OAAO,kBAAkB,QAAQ,EAAE;EAAE,CAAC;OACnE,IAAI,OAAO,IAAI,cAAc,OAAO,OAAO,GAAG,eAAe,UAClE,QAAQ,KAAK;GAAE,OAAO;GAAY,OAAO,kBAAkB,OAAO,GAAG,UAAU;EAAE,CAAC;EAGpF,GAAG,OACD,mBAAmB;GACjB,SAAS,QAAQ;GACjB,aAAa,QAAQ;GACrB,KAAK,QAAQ;GACb;GACA;EACF,CAAC,CACH;CACF;CAEA,MAAM,eAAe,QAAQ,MAAM,OAAO,IAAI;CAC9C,IAAI,CAAC,cACH,OAAO,MACL,gCAAgC;EAC9B,KAAK,uCAAuC,QAAQ,YAAY,yBAAyB,WAAW;EACpG,aAAa,QAAQ;CACvB,CAAC,CACH;CAGF,IAAI,CAAC,OAAO,QACV,OAAO,MACL,oBAAoB,EAClB,KAAK,iCAAiC,QAAQ,cAChD,CAAC,CACH;CAGF,MAAM,SAAS,oBAAoB;EACjC,QAAQ,OAAO;EACf,QAAQ,OAAO;EACf,SAAS,OAAO;EAChB,QAAQ,OAAO;EACf,gBAAgB,OAAO,kBAAkB,CAAC;CAC5C,CAAC;CACD,MAAM,aAAa,sBAAsB;EAAE;EAAI;CAAM,CAAC;CAEtD,IAAI;EACF,MAAM,SAAS,MAAM,OAAO,WAAW;GACrC,YAAY;GACZ;EACF,CAAC;EACD,MAAM,aAAa,OAAO,aAAa,MAAM;EAC7C,MAAM,iBAAiB,OAAO,iBAAiB,MAAM;EACrD,MAAM,sBAAsB,OAAO,uBAAuB;EAE1D,MAAM,QAAQ,OAAO,iBAAiB,WAAW,kBAAkB,YAAY,IAAI,KAAA;EAEnF,OAAO,GAAG;GACR;GACA;GACA;GACA;GACA;GACA,QAAQ;IACN,UAAU,OAAO,OAAO;IACxB,IAAI,OAAO,OAAO;GACpB;GACA,MAAM;IACJ;IACA,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;GAC3B;GACA,SAAS,EACP,OAAO,KAAK,IAAI,IAAI,UACtB;EACF,CAAC;CACH,SAAS,OAAO;EACd,IAAI,mBAAmB,GAAG,KAAK,GAC7B,OAAO,MAAM,KAAK;EAIpB,MAAM,cAAc,qBADD,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAGtE,OAAO,iBAAiB,WAAW,eAAe,KAAA,CACpD;EACA,OAAO,MACL,gBAAgB,aAAa,EAC3B,KAAK,2BAA2B,QAAQ,YAAY,IAAI,cAC1D,CAAC,CACH;CACF,UAAU;EACR,MAAM,OAAO,MAAM;CACrB;AACF"}
@@ -1,10 +1,9 @@
1
- import { t as loadConfig } from "./config-loader-p9JMrekQ.mjs";
2
- import { A as formatStyledHeader, K as errorInvalidSpaceId, L as errorAmbiguousMigrationRef, _ as createTerminalUI, at as errorSpaceNotFound, b as formatErrorJson, d as setCommandSeeAlso, g as parseGlobalFlagsOrExit, l as setCommandDescriptions, o as resolveContractPath, s as resolveMigrationPaths, t as addGlobalOptions, u as setCommandExamples, ut as mapRefResolutionError, x as formatErrorOutput } from "./command-helpers-DGMvGBeX.mjs";
1
+ import { A as formatStyledHeader, K as errorInvalidSpaceId, L as errorAmbiguousMigrationRef, _ as createTerminalUI, at as errorSpaceNotFound, b as formatErrorJson, d as setCommandSeeAlso, g as parseGlobalFlagsOrExit, l as setCommandDescriptions, o as resolveContractPath, s as resolveMigrationPaths, t as addGlobalOptions, u as setCommandExamples, ut as mapRefResolutionError, x as formatErrorOutput } from "./command-helpers-6cNJZ863.mjs";
3
2
  import { t as toDeclaredExtensionsFromRaw } from "./extension-pack-inputs-1ySHqxKG.mjs";
4
- import { n as buildReadAggregate } from "./contract-space-aggregate-loader-ClI1KN6d.mjs";
5
- import { i as resolveTargetPathAcrossSpaces, n as looksLikePath, r as resolveAppTargetPath, t as findPackageByDirPath } from "./migration-path-target-Ce6OZImp.mjs";
6
- import "./schemas-KhXMzNA_.mjs";
3
+ import { n as buildReadAggregate } from "./contract-space-aggregate-loader-Bu2fjSWV.mjs";
4
+ import { i as resolveTargetPathAcrossSpaces, n as looksLikePath, r as resolveAppTargetPath, t as findPackageByDirPath } from "./migration-path-target-zK6RG6JY.mjs";
7
5
  import { Command } from "commander";
6
+ import { loadConfig } from "@prisma-next/config-loader";
8
7
  import { ifDefined } from "@prisma-next/utils/defined";
9
8
  import { notOk, ok } from "@prisma-next/utils/result";
10
9
  import { join, relative } from "pathe";
@@ -571,4 +570,4 @@ function createMigrationCheckCommand() {
571
570
  //#endregion
572
571
  export { enumerateCheckSpaces as n, runMigrationCheck as r, createMigrationCheckCommand as t };
573
572
 
574
- //# sourceMappingURL=migration-check-VwM8xCZV.mjs.map
573
+ //# sourceMappingURL=migration-check-CTcJfCC4.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-check-CTcJfCC4.mjs","names":["migrationPathRelative","migrationFileRelative"],"sources":["../src/utils/integrity-violation-to-check-failure.ts","../src/commands/migration-check.ts"],"sourcesContent":["import type { IntegrityViolation } from '@prisma-next/migration-tools/aggregate';\nimport { join, relative } from 'pathe';\nimport type { CheckFailure } from '../commands/json/schemas';\n\nexport type { CheckFailure } from '../commands/json/schemas';\n\nfunction migrationPathRelative(dirPath: string): string {\n return relative(process.cwd(), dirPath);\n}\n\nfunction migrationFileRelative(dirPath: string, fileName: string): string {\n return join(migrationPathRelative(dirPath), fileName);\n}\n\n/**\n * Map one {@link IntegrityViolation} onto a `migration check` failure row.\n * Sole catalogue mapping from integrity violations to `PN-MIG-CHECK-*`.\n */\nexport function integrityViolationToCheckFailure(\n violation: IntegrityViolation,\n migrationsDir: string,\n): CheckFailure {\n const spaceRelative = (spaceId: string): string =>\n migrationPathRelative(join(migrationsDir, spaceId));\n const packageRelative = (spaceId: string, dirName: string): string =>\n migrationPathRelative(join(migrationsDir, spaceId, dirName));\n const refRelative = (spaceId: string, refName: string): string =>\n migrationPathRelative(join(migrationsDir, spaceId, 'refs', `${refName}.json`));\n\n switch (violation.kind) {\n case 'hashMismatch':\n return {\n space: violation.spaceId,\n code: 'PN-MIG-CHECK-001',\n where: migrationFileRelative(\n join(migrationsDir, violation.spaceId, violation.dirName),\n 'migration.json',\n ),\n why: `Stored hash ${violation.stored} does not match recomputed hash ${violation.computed}`,\n fix: 'Re-emit the migration package or restore from version control.',\n };\n case 'providedInvariantsMismatch':\n return {\n space: violation.spaceId,\n code: 'PN-MIG-CHECK-002',\n where: packageRelative(violation.spaceId, violation.dirName),\n why: `Migration \"${violation.dirName}\" providedInvariants in migration.json disagrees with ops.json.`,\n fix: 'Re-emit the migration package so migration.json and ops.json agree.',\n };\n case 'packageUnloadable':\n return {\n space: violation.spaceId,\n code: 'PN-MIG-CHECK-002',\n where: packageRelative(violation.spaceId, violation.dirName),\n why: `Migration \"${violation.dirName}\" could not be loaded: ${violation.detail}`,\n fix: 'Re-emit the migration package or restore from version control.',\n };\n case 'sameSourceAndTarget':\n return {\n space: violation.spaceId,\n code: 'PN-MIG-CHECK-007',\n where: packageRelative(violation.spaceId, violation.dirName),\n 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.`,\n 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.',\n };\n case 'orphanSpaceDir':\n return {\n space: violation.spaceId,\n code: 'PN-MIG-CHECK-008',\n where: spaceRelative(violation.spaceId),\n why: `Contract-space directory \"${violation.spaceId}\" exists on disk but no extension declares it.`,\n fix: 'Remove the orphan directory, or declare the extension in `extensionPacks`.',\n };\n case 'declaredButUnmigrated':\n return {\n space: violation.spaceId,\n code: 'PN-MIG-CHECK-009',\n where: spaceRelative(violation.spaceId),\n why: `Extension \"${violation.spaceId}\" is declared in \\`extensionPacks\\` but has no on-disk migrations directory.`,\n 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.',\n };\n case 'headRefMissing':\n return {\n space: violation.spaceId,\n code: 'PN-MIG-CHECK-010',\n where: refRelative(violation.spaceId, 'head'),\n why: `Head ref \\`refs/head.json\\` is missing for contract space \"${violation.spaceId}\".`,\n fix: 'Re-emit the contract-space migrations and head ref artefacts, or restore `refs/head.json` from version control.',\n };\n case 'headRefNotInGraph':\n return {\n space: violation.spaceId,\n code: 'PN-MIG-CHECK-011',\n where: refRelative(violation.spaceId, 'head'),\n why: `Head ref ${violation.hash} for contract space \"${violation.spaceId}\" is not present in its migration graph.`,\n fix: 'Re-emit the contract space migrations, or restore the missing migration package.',\n };\n case 'refUnreadable':\n return {\n space: violation.spaceId,\n code: 'PN-MIG-CHECK-012',\n where: refRelative(violation.spaceId, violation.refName),\n why: `Ref \"${violation.refName}\" for contract space \"${violation.spaceId}\" is unreadable: ${violation.detail}`,\n fix: 'Repair or remove the corrupt ref file.',\n };\n case 'targetMismatch':\n return {\n space: violation.spaceId,\n code: 'PN-MIG-CHECK-013',\n where: spaceRelative(violation.spaceId),\n why: `Contract space \"${violation.spaceId}\" targets \"${violation.actual}\" but the project targets \"${violation.expected}\".`,\n fix: 'Update the extension to target the configured database, or change the project target.',\n };\n case 'disjointness':\n return {\n space: 'app',\n code: 'PN-MIG-CHECK-014',\n where: migrationPathRelative(migrationsDir),\n why: `Storage element \"${violation.element}\" is claimed by multiple contract spaces: ${violation.claimedBy.join(', ')}.`,\n fix: 'Update the contracts so each storage element is owned by exactly one contract space.',\n };\n case 'contractUnreadable':\n return {\n space: violation.spaceId,\n code: 'PN-MIG-CHECK-015',\n where: migrationFileRelative(join(migrationsDir, violation.spaceId), 'contract.json'),\n why: `Contract for space \"${violation.spaceId}\" is unreadable: ${violation.detail}`,\n fix: 'Re-emit the extension contract artefacts, or fix the descriptor producing the invalid contract.',\n };\n case 'duplicateMigrationHash':\n return {\n space: violation.spaceId,\n code: 'PN-MIG-CHECK-016',\n where: spaceRelative(violation.spaceId),\n why: `Multiple migrations in space \"${violation.spaceId}\" share migrationHash \"${violation.migrationHash}\" (${violation.dirNames.join(', ')}).`,\n fix: 'Re-emit one of the conflicting packages so each migrationHash is unique.',\n };\n }\n}\n","import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { loadConfig } from '@prisma-next/config-loader';\nimport { createControlStack } from '@prisma-next/framework-components/control';\nimport type {\n ContractSpaceAggregate,\n IntegrityViolation,\n} from '@prisma-next/migration-tools/aggregate';\nimport { loadContractSpaceAggregate } from '@prisma-next/migration-tools/aggregate';\nimport type { MigrationGraph } from '@prisma-next/migration-tools/graph';\nimport { verifyMigrationHash } from '@prisma-next/migration-tools/hash';\nimport type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';\nimport {\n parseMigrationRef,\n type RefResolutionError,\n} from '@prisma-next/migration-tools/ref-resolution';\nimport type { Refs } from '@prisma-next/migration-tools/refs';\nimport {\n isValidSpaceId,\n listContractSpaceDirectories,\n RESERVED_SPACE_SUBDIR_NAMES,\n spaceMigrationDirectory,\n spaceRefsDirectory,\n} from '@prisma-next/migration-tools/spaces';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport { join, relative } from 'pathe';\nimport {\n type CliStructuredError,\n errorAmbiguousMigrationRef,\n errorInvalidSpaceId,\n errorSpaceNotFound,\n mapRefResolutionError,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n resolveContractPath,\n resolveMigrationPaths,\n setCommandDescriptions,\n setCommandExamples,\n setCommandSeeAlso,\n} from '../utils/command-helpers';\nimport { buildReadAggregate } from '../utils/contract-space-aggregate-loader';\nimport { toDeclaredExtensionsFromRaw } from '../utils/extension-pack-inputs';\nimport { formatErrorJson, formatErrorOutput } from '../utils/formatters/errors';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';\nimport { integrityViolationToCheckFailure } from '../utils/integrity-violation-to-check-failure';\nimport {\n findPackageByDirPath,\n looksLikePath,\n resolveAppTargetPath,\n resolveTargetPathAcrossSpaces,\n} from '../utils/migration-path-target';\nimport { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';\nimport type { CheckFailure, MigrationCheckResult } from './json/schemas';\nimport { INTEGRITY_FAILED, OK, PRECONDITION } from './migration-check/exit-codes';\n\ninterface MigrationCheckOptions extends CommonCommandOptions {\n readonly config?: string;\n readonly space?: string;\n}\n\nexport type { CheckFailure, MigrationCheckResult } from './json/schemas';\nexport { migrationCheckResultSchema } from './json/schemas';\n\nfunction migrationPathRelative(dirPath: string): string {\n return relative(process.cwd(), dirPath);\n}\n\nfunction migrationFileRelative(dirPath: string, fileName: string): string {\n return join(migrationPathRelative(dirPath), fileName);\n}\n\nfunction checkFileExists(\n spaceId: string,\n dirPath: string,\n dirName: string,\n fileName: string,\n): CheckFailure | null {\n if (!existsSync(join(dirPath, fileName))) {\n return {\n space: spaceId,\n code: 'PN-MIG-CHECK-002',\n where: migrationFileRelative(dirPath, fileName),\n why: `${fileName} is missing from ${dirName}`,\n fix: 'Re-emit the migration package or restore from version control.',\n };\n }\n return null;\n}\n\nfunction checkSnapshotConsistency(\n spaceId: string,\n pkg: OnDiskMigrationPackage,\n): CheckFailure | null {\n const endContractPath = join(pkg.dirPath, 'end-contract.json');\n if (!existsSync(endContractPath)) return null;\n try {\n const raw = JSON.parse(readFileSync(endContractPath, 'utf-8')) as Record<string, unknown>;\n const storage = raw['storage'] as Record<string, unknown> | undefined;\n const snapshotHash = storage?.['storageHash'];\n if (typeof snapshotHash === 'string' && snapshotHash !== pkg.metadata.to) {\n return {\n space: spaceId,\n code: 'PN-MIG-CHECK-005',\n where: migrationPathRelative(pkg.dirPath),\n why: `Migration \"${pkg.dirName}\" declares to=${pkg.metadata.to} but end-contract.json has storageHash=${snapshotHash}`,\n fix: 'Re-emit the migration package so migration.json and end-contract.json agree.',\n };\n }\n } catch {\n return {\n space: spaceId,\n code: 'PN-MIG-CHECK-006',\n where: migrationPathRelative(pkg.dirPath),\n why: `Migration \"${pkg.dirName}\" has an unparseable end-contract.json.`,\n fix: 'Re-emit the migration package to repair the snapshot file.',\n };\n }\n return null;\n}\n\n/**\n * One contract space's on-disk state, resolved for the explicit graph\n * checks `runMigrationCheck` runs per space: the space's migration\n * packages, its user-authored refs, its induced graph, and the absolute\n * `migrations/<space>/` + `migrations/<space>/refs/` directories the\n * file-existence and dangling-ref `where` paths are derived from.\n */\nexport interface CheckSpace {\n readonly spaceId: string;\n readonly packages: readonly OnDiskMigrationPackage[];\n readonly refs: Refs;\n readonly graph: MigrationGraph;\n readonly migrationsDir: string;\n readonly refsDir: string;\n}\n\n/**\n * Project the loaded {@link ContractSpaceAggregate} into the\n * {@link CheckSpace} rows the multi-space check iterates — one per on-disk\n * contract-space directory, in the aggregate's `app`-first ordering. Mirrors\n * `migration list`'s `migrationSpaceListEntriesFromAggregate`: space\n * membership matches the on-disk directories, package / ref / graph data come\n * from `aggregate.space(id)`.\n */\nexport async function enumerateCheckSpaces(\n aggregate: ContractSpaceAggregate,\n projectMigrationsDir: string,\n): Promise<readonly CheckSpace[]> {\n const candidateDirs = await listContractSpaceDirectories(projectMigrationsDir);\n const onDiskSpaceIds = new Set(\n candidateDirs.filter((name) => !RESERVED_SPACE_SUBDIR_NAMES.has(name)).filter(isValidSpaceId),\n );\n const spaces: CheckSpace[] = [];\n for (const member of aggregate.spaces()) {\n const spaceId = member.spaceId;\n if (!isValidSpaceId(spaceId)) continue;\n if (!onDiskSpaceIds.has(spaceId)) continue;\n const migrationsDir = spaceMigrationDirectory(projectMigrationsDir, spaceId);\n spaces.push({\n spaceId,\n packages: member.packages,\n refs: member.refs,\n graph: member.graph(),\n migrationsDir,\n refsDir: spaceRefsDirectory(migrationsDir),\n });\n }\n return spaces;\n}\n\nfunction checkManifestFilesPresent(space: CheckSpace): readonly CheckFailure[] {\n if (!existsSync(space.migrationsDir)) return [];\n const loadedDirNames = new Set(space.packages.map((p) => p.dirName));\n const failures: CheckFailure[] = [];\n let entries: string[];\n try {\n entries = readdirSync(space.migrationsDir);\n } catch {\n return failures;\n }\n for (const entry of entries) {\n if (entry.startsWith('.') || entry.startsWith('_') || entry === 'refs') continue;\n const entryPath = join(space.migrationsDir, entry);\n try {\n if (!statSync(entryPath).isDirectory()) continue;\n } catch {\n continue;\n }\n if (!loadedDirNames.has(entry)) {\n for (const f of ['migration.json', 'ops.json']) {\n const fail = checkFileExists(space.spaceId, entryPath, entry, f);\n if (fail) failures.push(fail);\n }\n }\n }\n return failures;\n}\n\nfunction checkReachability(space: CheckSpace): readonly CheckFailure[] {\n const allToHashes = new Set(space.packages.map((p) => p.metadata.to));\n const failures: CheckFailure[] = [];\n for (const pkg of space.packages) {\n const isReachable =\n pkg.metadata.from === null ||\n allToHashes.has(pkg.metadata.from) ||\n pkg.metadata.from === 'sha256:empty';\n if (!isReachable) {\n failures.push({\n space: space.spaceId,\n code: 'PN-MIG-CHECK-003',\n where: migrationPathRelative(pkg.dirPath),\n why: `Migration \"${pkg.dirName}\" starts from ${pkg.metadata.from} which no other migration produces`,\n fix: 'This migration is unreachable in the graph. Delete it or re-emit a connecting migration.',\n });\n }\n }\n return failures;\n}\n\nfunction checkDanglingRefs(space: CheckSpace): readonly CheckFailure[] {\n const failures: CheckFailure[] = [];\n for (const [name, entry] of Object.entries(space.refs)) {\n if (!space.graph.nodes.has(entry.hash)) {\n failures.push({\n space: space.spaceId,\n code: 'PN-MIG-CHECK-004',\n where: relative(process.cwd(), join(space.refsDir, `${name}.json`)),\n why: `Ref \"${name}\" points at ${entry.hash} which does not exist in the migration graph`,\n fix: `Update the ref with \\`prisma-next ref set ${name} <valid-hash>\\` or delete it.`,\n });\n }\n }\n return failures;\n}\n\nfunction checkSpace(space: CheckSpace): readonly CheckFailure[] {\n return [\n ...checkManifestFilesPresent(space),\n ...space.packages\n .map((pkg) => checkSnapshotConsistency(space.spaceId, pkg))\n .filter((f): f is CheckFailure => f !== null),\n ...checkReachability(space),\n ...checkDanglingRefs(space),\n ];\n}\n\n/**\n * Inputs for {@link runMigrationCheck} — the multi-space policy core of\n * the holistic (no-arg) `migration check`. Enumeration is supplied by the\n * caller (the CLI shell builds it from {@link enumerateCheckSpaces}); the\n * core does not touch config, flags, or streams.\n */\nexport interface RunMigrationCheckInputs {\n readonly spaces: readonly CheckSpace[];\n readonly spaceFilter?: string;\n}\n\n/**\n * Policy core of the holistic `migration check`: validates `--space`,\n * narrows the pre-enumerated spaces, and runs the per-space explicit graph\n * checks (file-existence, snapshot consistency, reachability, dangling\n * refs), aggregating every failure into one {@link MigrationCheckResult}.\n *\n * `--space` validation mirrors `migration list`: an invalid id →\n * {@link errorInvalidSpaceId}; an id with no on-disk space →\n * {@link errorSpaceNotFound}. Both map to exit `PRECONDITION` at the shell.\n * Aggregate-integrity violations (which already span every space) are folded\n * in by the caller, not here.\n */\nexport function runMigrationCheck(\n inputs: RunMigrationCheckInputs,\n): Result<MigrationCheckResult, CliStructuredError> {\n const { spaces, spaceFilter } = inputs;\n\n if (spaceFilter !== undefined && !isValidSpaceId(spaceFilter)) {\n return notOk(errorInvalidSpaceId(spaceFilter));\n }\n if (spaceFilter !== undefined && !spaces.some((s) => s.spaceId === spaceFilter)) {\n return notOk(errorSpaceNotFound(spaceFilter, spaces.map((s) => s.spaceId).sort()));\n }\n\n const scopedSpaces =\n spaceFilter !== undefined ? spaces.filter((s) => s.spaceId === spaceFilter) : spaces;\n\n const failures = scopedSpaces.flatMap(checkSpace);\n if (failures.length === 0) {\n return ok({ ok: true, failures: [], summary: 'All checks passed' });\n }\n return ok({ ok: false, failures, summary: `${failures.length} integrity failure(s)` });\n}\n\nasync function loadAggregateIntegrityViolations(\n config: Awaited<ReturnType<typeof loadConfig>>,\n migrationsDir: string,\n): Promise<readonly IntegrityViolation[]> {\n try {\n const contractJsonContent = await readFile(resolveContractPath(config), 'utf-8');\n const familyInstance = config.family.create(createControlStack(config));\n const declaredExtensions = toDeclaredExtensionsFromRaw(config.extensionPacks ?? []);\n\n const parsedAppContract: unknown = JSON.parse(contractJsonContent);\n const aggregate = await loadContractSpaceAggregate({\n migrationsDir,\n deserializeContract: (json: unknown) => familyInstance.deserializeContract(json),\n appContract: familyInstance.deserializeContract(parsedAppContract),\n });\n return aggregate.checkIntegrity({ declaredExtensions, checkContracts: true });\n } catch {\n return [];\n }\n}\n\ninterface MigrationCheckOutcome {\n readonly result?: MigrationCheckResult;\n readonly error?: CliStructuredError;\n readonly exitCode: number;\n readonly resolvedSpaceId?: string;\n}\n\nasync function executeMigrationCheckCommand(\n target: string | undefined,\n options: MigrationCheckOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n): Promise<MigrationCheckOutcome> {\n const config = await loadConfig(options.config);\n const { configPath, migrationsDir, appMigrationsDir, appMigrationsRelative } =\n resolveMigrationPaths(options.config, config);\n\n if (!flags.json && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n { label: 'migrations', value: appMigrationsRelative },\n ];\n if (target) {\n details.push({ label: 'target', value: target });\n }\n const header = formatStyledHeader({\n command: 'migration check',\n description: 'Verify artifact and graph integrity',\n details,\n flags,\n });\n ui.stderr(header);\n }\n\n const loadedAggregate = await buildReadAggregate(config, { migrationsDir });\n if (!loadedAggregate.ok) {\n return { error: loadedAggregate.failure, exitCode: PRECONDITION };\n }\n\n const spaces = await enumerateCheckSpaces(loadedAggregate.value.aggregate, migrationsDir);\n\n if (target) {\n return await checkSingleTarget(target, {\n spaces,\n ...(options.space !== undefined ? { spaceFilter: options.space } : {}),\n appMigrationsDir,\n appMigrationsRelative,\n });\n }\n\n const checkResult = runMigrationCheck({\n spaces,\n ...(options.space !== undefined ? { spaceFilter: options.space } : {}),\n });\n if (!checkResult.ok) {\n return { error: checkResult.failure, exitCode: PRECONDITION };\n }\n\n const failures: CheckFailure[] = [...checkResult.value.failures];\n const allViolations = await loadAggregateIntegrityViolations(config, migrationsDir);\n const scopedViolations =\n options.space === undefined\n ? allViolations\n : allViolations.filter((v) => v.kind !== 'disjointness' && v.spaceId === options.space);\n for (const violation of scopedViolations) {\n failures.push(integrityViolationToCheckFailure(violation, migrationsDir));\n }\n\n if (failures.length === 0) {\n return {\n result: { ok: true, failures: [], summary: 'All checks passed' },\n exitCode: OK,\n };\n }\n\n return {\n result: { ok: false, failures, summary: `${failures.length} integrity failure(s)` },\n exitCode: INTEGRITY_FAILED,\n };\n}\n\ninterface SingleTargetInputs {\n readonly spaces: readonly CheckSpace[];\n readonly spaceFilter?: string;\n readonly appMigrationsDir: string;\n readonly appMigrationsRelative: string;\n}\n\n/**\n * Ranks ref-resolution failure kinds by how informative they are, so a\n * single-target check surfaces the most useful failure across spaces instead of\n * whichever space failed first. `not-found` (the input matched nothing here)\n * says the least; a malformed input, a wrong grammar, or an in-space ambiguity\n * all say more.\n */\nfunction refFailureSpecificity(error: RefResolutionError): number {\n switch (error.kind) {\n case 'wrong-grammar':\n return 3;\n case 'ambiguous':\n return 2;\n case 'invalid-format':\n return 1;\n case 'not-found':\n return 0;\n }\n}\n\n/**\n * Single-target (`check <ref/path>`) mode — resolves a migration reference\n * across all contract spaces (or the one space narrowed by `--space <id>`).\n *\n * Resolution:\n * - filesystem path → find the owning space by checking which space's\n * `migrationsDir` contains the resolved path; falls back to app-relative\n * validation when the path is outside every space dir.\n * - ref → `parseMigrationRef` against each in-scope space; collect every\n * (space, package) hit; 0 hits = not-found, 1 = check it, >1 = ambiguity\n * error (qualify with `--space`).\n *\n * `--space <id>` is validated the same way the holistic path does it:\n * invalid id → `errorInvalidSpaceId`; no on-disk space → `errorSpaceNotFound`.\n */\nasync function checkSingleTarget(\n target: string,\n inputs: SingleTargetInputs,\n): Promise<MigrationCheckOutcome> {\n const { spaces, spaceFilter, appMigrationsDir, appMigrationsRelative } = inputs;\n\n if (spaceFilter !== undefined && !isValidSpaceId(spaceFilter)) {\n return { error: errorInvalidSpaceId(spaceFilter), exitCode: PRECONDITION };\n }\n if (spaceFilter !== undefined && !spaces.some((s) => s.spaceId === spaceFilter)) {\n return {\n error: errorSpaceNotFound(spaceFilter, spaces.map((s) => s.spaceId).sort()),\n exitCode: PRECONDITION,\n };\n }\n\n const scopedSpaces =\n spaceFilter !== undefined ? spaces.filter((s) => s.spaceId === spaceFilter) : spaces;\n\n let matchedSpace: CheckSpace | undefined;\n let matchedPkg: OnDiskMigrationPackage | undefined;\n\n if (looksLikePath(target)) {\n const resolvedPath = resolveTargetPathAcrossSpaces(target, scopedSpaces);\n if (resolvedPath !== null) {\n for (const space of scopedSpaces) {\n const found = findPackageByDirPath(space.packages, resolvedPath);\n if (found) {\n matchedSpace = space;\n matchedPkg = found;\n break;\n }\n }\n } else {\n // Path outside every space dir — fall back to app-relative validation\n const resolved = resolveAppTargetPath(target, appMigrationsDir, appMigrationsRelative);\n if (!resolved.ok) {\n return { error: resolved.failure, exitCode: PRECONDITION };\n }\n const appSpace = scopedSpaces.find((s) => s.spaceId === 'app');\n if (appSpace) {\n matchedSpace = appSpace;\n matchedPkg = findPackageByDirPath(appSpace.packages, resolved.value);\n }\n }\n } else {\n // Ref resolution: try each in-scope space, collect all hits.\n const hits: Array<{ space: CheckSpace; pkg: OnDiskMigrationPackage }> = [];\n let bestParseFailure: RefResolutionError | undefined;\n for (const space of scopedSpaces) {\n const migResult = parseMigrationRef(target, { graph: space.graph, refs: space.refs });\n if (!migResult.ok) {\n // Keep scanning — a later space may hold a hit that must not be discarded.\n // When no space yields a hit, keep the most informative failure rather than\n // whichever space failed first (the kind is space-dependent).\n if (\n bestParseFailure === undefined ||\n refFailureSpecificity(migResult.failure) > refFailureSpecificity(bestParseFailure)\n ) {\n bestParseFailure = migResult.failure;\n }\n continue;\n }\n const pkg = space.packages.find(\n (p) => p.metadata.migrationHash === migResult.value.migrationHash,\n );\n if (pkg) {\n hits.push({ space, pkg });\n }\n }\n\n if (hits.length > 1) {\n const spaceIds = hits.map((h) => h.space.spaceId);\n return {\n error: errorAmbiguousMigrationRef(target, spaceIds),\n exitCode: PRECONDITION,\n };\n }\n\n if (hits.length === 1) {\n matchedSpace = hits[0]!.space;\n matchedPkg = hits[0]!.pkg;\n } else if (bestParseFailure !== undefined) {\n // The ref didn't resolve in any in-scope space — surface the most informative\n // parse failure through the shared ref-resolution envelope (PN-RUN-3000) the\n // earlier work established, rather than a bespoke string. (Ref-resolved-but-\n // no-package falls through to the \"not found on disk\" result below.)\n return { error: mapRefResolutionError(bestParseFailure), exitCode: PRECONDITION };\n }\n }\n\n if (!matchedPkg || !matchedSpace) {\n return {\n result: {\n ok: false,\n failures: [],\n summary: `Migration package for \"${target}\" not found on disk`,\n },\n exitCode: PRECONDITION,\n };\n }\n\n const failures: CheckFailure[] = [...checkManifestFilesPresent(matchedSpace)];\n\n for (const f of ['migration.json', 'ops.json']) {\n const fail = checkFileExists(matchedSpace.spaceId, matchedPkg.dirPath, matchedPkg.dirName, f);\n if (fail) failures.push(fail);\n }\n\n const verification = verifyMigrationHash(matchedPkg);\n if (!verification.ok) {\n failures.push({\n space: matchedSpace.spaceId,\n code: 'PN-MIG-CHECK-001',\n where: migrationFileRelative(matchedPkg.dirPath, 'migration.json'),\n why: `Stored hash ${verification.storedHash} does not match recomputed hash ${verification.computedHash}`,\n fix: 'Re-emit the migration package or restore from version control.',\n });\n }\n\n const snapshotFailure = checkSnapshotConsistency(matchedSpace.spaceId, matchedPkg);\n if (snapshotFailure) failures.push(snapshotFailure);\n\n const resolvedSpaceId = matchedSpace.spaceId !== 'app' ? matchedSpace.spaceId : undefined;\n\n if (failures.length === 0) {\n return {\n result: { ok: true, failures: [], summary: 'All checks passed' },\n exitCode: OK,\n ...ifDefined('resolvedSpaceId', resolvedSpaceId),\n };\n }\n return {\n result: { ok: false, failures, summary: `${failures.length} integrity failure(s)` },\n exitCode: INTEGRITY_FAILED,\n ...ifDefined('resolvedSpaceId', resolvedSpaceId),\n };\n}\n\nexport function createMigrationCheckCommand(): Command {\n const command = new Command('check');\n setCommandDescriptions(\n command,\n 'Verify artifact and graph integrity',\n 'Validates that on-disk migration packages are internally consistent\\n' +\n '(hashes match, manifests are complete) and that the graph is well-formed\\n' +\n '(edges connect, refs point at valid nodes). The whole-graph check spans\\n' +\n 'every contract space by default; pass --space <id> to narrow to one. A\\n' +\n 'migration reference checks a single package, resolved across all contract\\n' +\n 'spaces (narrow with --space; an ambiguous reference is a precondition failure).\\n' +\n 'Offline — does not consult the database.\\n' +\n 'Exit codes: 0 = all checks passed, 2 = precondition failed\\n' +\n '(unresolved target or unknown --space), 4 = integrity failure(s) found.',\n );\n setCommandExamples(command, [\n 'prisma-next migration check',\n 'prisma-next migration check --space app',\n 'prisma-next migration check 20260101-add-users',\n 'prisma-next migration check 20260101-add-users --space app',\n 'prisma-next migration check --json',\n ]);\n setCommandSeeAlso(command, [\n { verb: 'migration status', oneLiner: 'Show migration path and pending status' },\n { verb: 'migration list', oneLiner: 'List on-disk migrations' },\n { verb: 'migration graph', oneLiner: 'Show the migration graph topology' },\n { verb: 'migration show', oneLiner: 'Display migration package contents' },\n ]);\n command.exitOverride();\n addGlobalOptions(command)\n .argument('[target]', 'Migration reference: directory name, hash/prefix, ref, or path')\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .option('--space <id>', 'Narrow output to a single contract space')\n .action(async (target: string | undefined, options: MigrationCheckOptions) => {\n const flags = parseGlobalFlagsOrExit(options);\n const ui = createTerminalUI(flags);\n\n let outcome: MigrationCheckOutcome;\n try {\n outcome = await executeMigrationCheckCommand(target, options, flags, ui);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n outcome = {\n result: { ok: false, failures: [], summary: msg },\n exitCode: PRECONDITION,\n };\n }\n\n if (outcome.error) {\n const envelope = outcome.error.toEnvelope();\n if (flags.json) {\n ui.output(formatErrorJson(envelope));\n } else if (!flags.quiet) {\n ui.error(formatErrorOutput(envelope, flags));\n }\n process.exit(outcome.exitCode);\n }\n\n const result = outcome.result ?? {\n ok: false,\n failures: [],\n summary: 'No check result produced',\n };\n\n if (flags.json) {\n ui.output(JSON.stringify(result, null, 2));\n } else if (!flags.quiet) {\n if (result.ok) {\n const spaceSuffix =\n outcome.resolvedSpaceId !== undefined ? ` (space: ${outcome.resolvedSpaceId})` : '';\n ui.log(`✔ ${result.summary}${spaceSuffix}`);\n } else {\n for (const f of result.failures) {\n ui.log(`✗ [${f.code}] ${f.where}: ${f.why}`);\n ui.log(` fix: ${f.fix}`);\n }\n ui.log(`\\n${result.summary}`);\n }\n }\n\n process.exit(outcome.exitCode);\n });\n\n return command;\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { t as loadConfig } from "./config-loader-p9JMrekQ.mjs";
1
+ import { loadConfig } from "@prisma-next/config-loader";
2
2
  import { CliStructuredError, errorMigrationCliInvalidConfigArg, errorMigrationCliUnknownFlag } from "@prisma-next/errors/control";
3
3
  import { dirname, join } from "pathe";
4
4
  import { createControlStack } from "@prisma-next/framework-components/control";
@@ -1 +1 @@
1
- {"version":3,"file":"migration-cli.mjs","names":[],"sources":["../src/migration-cli.ts"],"sourcesContent":["/**\n * The migration-file CLI interface: the actor invoked when the author runs\n * `node migration.ts` directly.\n *\n * Naming: this is *not* a \"migration runner\" in the apply-time sense. The\n * apply-time runner is the thing `prisma-next migrate` uses to\n * execute migration JSON ops against a database. `MigrationCLI` is the\n * tiny CLI surface owned by an authored `migration.ts` file: parse the\n * file's argv, load the project's `prisma-next.config.ts`, assemble a\n * `ControlStack`, instantiate the migration class, and serialize.\n *\n * The user authors a migration class, then calls\n * `MigrationCLI.run(import.meta.url, MigrationClass)` at module scope\n * after the class definition. When the file is invoked as a node\n * entrypoint (`node migration.ts`), the CLI:\n *\n * 1. Detects whether the file is the direct entrypoint (no-op when imported).\n * 2. Parses CLI args (`--help`, `--dry-run`, `--config <path>`) via\n * [clipanion](https://github.com/arcanis/clipanion).\n * 3. Loads the project's `prisma-next.config.ts` via the same `loadConfig`\n * the CLI commands use, walking up from the migration file's directory.\n * 4. Probe-instantiates the migration class without a stack so it can read\n * `targetId` and verify it matches `config.target.targetId`\n * (`PN-MIG-2006` on mismatch) before any stack-driven adapter\n * construction runs.\n * 5. Assembles a `ControlStack` from the loaded config descriptors and\n * constructs the migration with that stack.\n * 6. Reads any previously-scaffolded `migration.json`, then calls\n * `buildMigrationArtifacts` from `@prisma-next/migration-tools` to\n * produce in-memory `ops.json` + `migration.json` content. Persists\n * the result to disk (or prints in dry-run mode).\n *\n * File I/O lives here, in `@prisma-next/cli`: this is the only place\n * that legitimately combines config loading, stack assembly, and\n * on-disk persistence. `@prisma-next/migration-tools` owns the pure\n * conversion from a `Migration` instance to artifact strings; `Migration`\n * stays a pure abstract class.\n *\n * Parser library: clipanion (chosen over Commander/citty/`node:util.parseArgs`\n * for its in-process testability and runtime-agnostic execution surface; see\n * `docs/architecture docs/research/commander-friction-points.md` for the\n * evaluation rubric and the durable rationale that drove the choice).\n */\n\nimport { readFileSync, realpathSync, writeFileSync } from 'node:fs';\nimport type { Writable } from 'node:stream';\nimport { fileURLToPath } from 'node:url';\nimport {\n CliStructuredError,\n errorMigrationCliInvalidConfigArg,\n errorMigrationCliUnknownFlag,\n} from '@prisma-next/errors/control';\nimport { errorMigrationTargetMismatch } from '@prisma-next/errors/migration';\nimport { createControlStack } from '@prisma-next/framework-components/control';\nimport { errorInvalidJson, MigrationToolsError } from '@prisma-next/migration-tools/errors';\nimport type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';\nimport { buildMigrationArtifacts, type Migration } from '@prisma-next/migration-tools/migration';\nimport { Cli, Command, Option, UsageError } from 'clipanion';\nimport { dirname, join } from 'pathe';\nimport { loadConfig } from './config-loader';\n\n/**\n * Constructor shape accepted by `MigrationCLI.run`. `Migration` subclasses\n * accept an optional `ControlStack` in their constructor (each subclass\n * narrows the stack to its own family/target generics); the CLI always\n * passes one assembled from the loaded config. We use a rest-args `any[]`\n * constructor signature so that subclass constructors with narrower\n * parameter types remain assignable - constructor type compatibility in\n * TS is contravariant in the parameter, and a wider `unknown` parameter\n * on the alias side would reject any narrower subclass signature.\n *\n * The CLI only ever passes one argument (`new MigrationClass(stack)`);\n * the rest-arity is purely a type-compatibility concession for subclass\n * constructors that declare narrower parameter types, not an extension\n * point for additional construction arguments.\n */\n// biome-ignore lint/suspicious/noExplicitAny: see JSDoc - rest args with any are the idiomatic TS pattern for accepting arbitrary subclass constructor signatures\nexport type MigrationConstructor = new (...args: any[]) => Migration;\n\n/**\n * Stream surface accepted by `MigrationCLI.run`'s `options.stdout` /\n * `options.stderr`. Aliases node's `Writable` because clipanion's\n * `BaseContext.stdout`/`stderr` are typed as `Writable`, and the CLI\n * forwards the injected streams into clipanion's context.\n *\n * `process.stdout` and `process.stderr` are `Writable`-shaped, so the\n * default-fallback path remains a no-op for existing two-argument\n * callers like `MigrationCLI.run(import.meta.url, MyMigration)`.\n *\n * Tests inject a `Writable` subclass that captures chunks for\n * assertions.\n */\nexport type MigrationCliWritable = Writable;\n\n/**\n * Flags exposed by the migration-file CLI.\n *\n * Must stay in sync with the `Option` declarations on\n * `MigrationFileCommand` below. This list is rendered in the\n * `errorMigrationCliUnknownFlag` envelope's `fix` text and `meta`,\n * so order matters for user-visible output (declaration order is the\n * order users see when they run `--help`).\n */\nconst KNOWN_FLAGS: readonly string[] = ['--help', '--dry-run', '--config'];\n\n/**\n * The clipanion command that owns the migration-file CLI's option\n * declarations. The class is internal — `MigrationCLI.run` is the\n * stable public surface. Adding a flag here automatically updates\n * `--help` rendering and the `KNOWN_FLAGS` list (the latter must be\n * updated in tandem).\n */\nclass MigrationFileCommand extends Command {\n static override paths = [Command.Default];\n\n static override usage = Command.Usage({\n description: 'Self-emit ops.json and migration.json from a class-flow migration',\n details: `\n Loads the project's prisma-next.config.ts, assembles a ControlStack\n from the configured target/adapter/extensions, and serializes the\n migration's operations + metadata next to this file.\n `,\n examples: [\n ['Self-emit ops.json + migration.json next to migration.ts', '$0'],\n ['Preview without writing files', '$0 --dry-run'],\n ['Use a non-default config path', '$0 --config ./custom.config.ts'],\n ],\n });\n\n dryRun = Option.Boolean('--dry-run', false, {\n description: 'Print operations to stdout without writing files',\n });\n\n config = Option.String('--config', {\n description: 'Path to prisma-next.config.ts',\n });\n\n /**\n * Unused: orchestration runs inside `MigrationCLI.run` so error\n * routing stays under our control (clipanion's `cli.run` writes\n * error output to `context.stdout`, but our contract requires\n * structured errors on stderr). `cli.process` is used to parse\n * argv into a populated `MigrationFileCommand` instance whose\n * fields drive the orchestration directly.\n */\n override async execute(): Promise<number> {\n return 0;\n }\n}\n\n/**\n * The CLI surface invoked by an authored `migration.ts` file. Exposed as\n * a class with a static `run` method (rather than a free function) to\n * give the concept a stable identity in the ubiquitous language: this is\n * the \"migration-file CLI\", distinct from the apply-time runner that\n * executes migration JSON ops.\n *\n * Currently a single static method. Future surface (e.g. a programmatic\n * `MigrationCLI.serializeOnly(...)` for tests, or extra subcommands) can\n * land here without changing the import shape used by every authored\n * migration.\n */\n// biome-ignore lint/complexity/noStaticOnlyClass: see JSDoc - intentional class facade for the migration-file CLI surface; future methods will share state derived from argv/config.\nexport class MigrationCLI {\n /**\n * Orchestrates a class-flow `migration.ts` script run.\n *\n * The third argument is the in-process testability surface: callers\n * (and tests) may inject `argv`, `stdout`, and `stderr` instead of\n * relying on `process.argv` / `process.stdout` / `process.stderr`.\n * Each option defaults to its `process` global when omitted, so\n * existing two-argument call sites\n * (`MigrationCLI.run(import.meta.url, MyMigration)`) continue to\n * compile and behave identically.\n *\n * Returns the exit code so the caller can branch on it. Also writes\n * the same code to `process.exitCode` so script-style callers that\n * don't await the return value still surface a non-zero exit when\n * something fails.\n *\n * Exit codes:\n * - 0 — success, or `--help`, or imported-not-entrypoint no-op.\n * - 1 — runtime/orchestration error (config not found, target\n * mismatch, etc.).\n * - 2 — usage error (unknown flag, malformed `--config`). Aligns\n * with `docs/CLI Style Guide.md` § Exit Codes.\n */\n static async run(\n importMetaUrl: string,\n MigrationClass: MigrationConstructor,\n options: {\n readonly argv?: readonly string[];\n readonly stdout?: MigrationCliWritable;\n readonly stderr?: MigrationCliWritable;\n } = {},\n ): Promise<number> {\n if (!importMetaUrl) {\n return 0;\n }\n\n const argv = options.argv ?? process.argv;\n const stdout = options.stdout ?? process.stdout;\n const stderr = options.stderr ?? process.stderr;\n\n if (!isDirectEntrypoint(importMetaUrl, argv)) {\n return 0;\n }\n\n const exitCode = await orchestrate(importMetaUrl, MigrationClass, {\n argv,\n stdout,\n stderr,\n });\n // Preserve any pre-existing non-zero `process.exitCode` set by code\n // running alongside `MigrationCLI.run` (an unhandled rejection\n // upstream, an explicit `process.exitCode = N` from another\n // module). Overwriting it with our success would mask the upstream\n // failure for script-style callers that don't await the return\n // value. Failures we return here are still surfaced — non-zero\n // codes always win over the prior status — but successes never\n // clear it.\n if (exitCode !== 0 || !process.exitCode) {\n process.exitCode = exitCode;\n }\n return exitCode;\n }\n}\n\n/**\n * Argv-aware variant of the entrypoint guard. The shared\n * `@prisma-next/migration-tools` helper of the same name reads\n * `process.argv[1]` directly, which doesn't compose with the new\n * in-process testability surface (tests inject `argv` without mutating\n * the process global). Inlined here so the migration-file CLI's check\n * follows the injected `argv[1]` consistently.\n */\nfunction isDirectEntrypoint(importMetaUrl: string, argv: readonly string[]): boolean {\n const argv1 = argv[1];\n if (!argv1) {\n return false;\n }\n try {\n return realpathSync(fileURLToPath(importMetaUrl)) === realpathSync(argv1);\n } catch {\n return false;\n }\n}\n\n/**\n * Argv-and-stream-driven orchestration body. Pulled out of the static\n * method so the entrypoint guard / process-default plumbing stays\n * separate from the parse + load + serialize steps.\n */\nasync function orchestrate(\n importMetaUrl: string,\n MigrationClass: MigrationConstructor,\n ctx: {\n readonly argv: readonly string[];\n readonly stdout: MigrationCliWritable;\n readonly stderr: MigrationCliWritable;\n },\n): Promise<number> {\n const cli = Cli.from([MigrationFileCommand], {\n binaryName: 'migration.ts',\n binaryLabel: 'Migration file CLI',\n });\n\n const input = ctx.argv.slice(2);\n\n // Pre-scan for malformed `--config` (no value, or value-shaped-as-flag)\n // before delegating to clipanion. The legacy parser surfaced both as\n // `errorMigrationCliInvalidConfigArg` (`PN-CLI-4012`); pre-scanning\n // here keeps that contract independent of how clipanion classifies\n // the error internally (it variably throws `UnknownSyntaxError` or\n // accepts the flag-shaped token as the value depending on what other\n // options are registered).\n const configError = detectInvalidConfig(input);\n if (configError) {\n writeStructuredError(ctx.stderr, configError);\n return 2;\n }\n\n let parsed: MigrationFileCommand;\n try {\n const command = cli.process({\n input: [...input],\n context: { stdout: ctx.stdout, stderr: ctx.stderr },\n });\n if (!(command instanceof MigrationFileCommand)) {\n // The only registered command class is `MigrationFileCommand`;\n // any other concrete type indicates clipanion emitted its\n // built-in `HelpCommand`. Render usage directly so we don't\n // depend on calling `cli.run` (which routes errors to stdout —\n // wrong stream for our contract).\n ctx.stdout.write(cli.usage(MigrationFileCommand, { detailed: true }));\n return 0;\n }\n parsed = command;\n } catch (err) {\n return renderParseError(err, input, ctx.stderr);\n }\n\n if (parsed.help) {\n ctx.stdout.write(cli.usage(MigrationFileCommand, { detailed: true }));\n return 0;\n }\n\n try {\n await runMigration(importMetaUrl, MigrationClass, parsed, ctx);\n return 0;\n } catch (err) {\n if (CliStructuredError.is(err)) {\n writeStructuredError(ctx.stderr, err);\n } else if (MigrationToolsError.is(err)) {\n // Migration-tools errors (e.g. `errorInvalidJson` thrown by\n // `readExistingMetadata` when migration.json is malformed) carry\n // their own `code`/`why`/`fix` shape. Render them with the same\n // visual structure as `CliStructuredError` so consumers grepping\n // for `MIGRATION.<CODE>` see consistent output across surfaces.\n const fix = err.fix ? `\\n${err.fix}` : '';\n ctx.stderr.write(`${err.code}: ${err.message}\\n${err.why}${fix}\\n`);\n } else {\n ctx.stderr.write(`${err instanceof Error ? err.message : String(err)}\\n`);\n }\n return 1;\n }\n}\n\n/**\n * Returns an `errorMigrationCliInvalidConfigArg` envelope when `input`\n * contains a malformed `--config`:\n *\n * - `--config` as the last token (no value follows).\n * - `--config <flag>` where `<flag>` starts with `-` (silently\n * consuming the next flag would either drop the flag or serialize\n * against the wrong project).\n * - `--config <empty>` where the value is the empty string. Shells\n * expand `--config \"\"` (or `--config \"$UNSET_VAR\"`) into a real\n * empty argv token; treating that as a usage error here surfaces\n * `PN-CLI-4012` instead of a less actionable loader error on an\n * empty path.\n * - `--config=` (the equals form with an empty value). Same shape as\n * the empty-string case above; the user expressed intent to override\n * the config path but the override is empty.\n *\n * `--config=<value>` and `--config <value>` with a non-empty value are\n * both valid (and the equals form's value is allowed to start with\n * `-` — the `=` makes the binding explicit).\n */\nfunction detectInvalidConfig(input: readonly string[]): CliStructuredError | null {\n for (let i = 0; i < input.length; i++) {\n const token = input[i];\n if (token === '--config') {\n const next = input[i + 1];\n if (next === undefined || next === '') {\n return errorMigrationCliInvalidConfigArg();\n }\n if (next.startsWith('-')) {\n return errorMigrationCliInvalidConfigArg({ nextToken: next });\n }\n continue;\n }\n if (token === '--config=') {\n return errorMigrationCliInvalidConfigArg();\n }\n }\n return null;\n}\n\n/**\n * Translate clipanion's parse-time errors into the project's structured\n * error envelopes.\n *\n * - `UnknownSyntaxError` covers both unknown flags (`--frobnicate`) and\n * the bare-trailing `--config` case (where arity-1 needs a value but\n * none was supplied). Distinguished by inspecting the input array.\n * - `UsageError` covers schema/validator failures from typanion. None\n * of the migration-file CLI's options have validators today, but we\n * still translate it as a usage error (exit 2) for forward-compat.\n * - Anything else re-throws — caller's outer catch will surface it as\n * exit 1 (runtime error).\n */\nfunction renderParseError(\n err: unknown,\n input: readonly string[],\n stderr: MigrationCliWritable,\n): number {\n if (isUnknownSyntaxError(err)) {\n const flag = findOffendingFlag(input);\n writeStructuredError(stderr, errorMigrationCliUnknownFlag({ flag, knownFlags: KNOWN_FLAGS }));\n return 2;\n }\n if (err instanceof UsageError) {\n // typanion validator failures and similar usage errors. None of\n // the migration-file CLI's options have validators today, so this\n // branch is forward-compat scaffolding — kept so that a future\n // option declaration with a validator routes through the same PN\n // envelope path rather than escaping as exit 1.\n writeStructuredError(stderr, errorMigrationCliInvalidConfigArg({ nextToken: err.message }));\n return 2;\n }\n throw err;\n}\n\n/**\n * Duck-type check for clipanion's `UnknownSyntaxError`: the class is\n * thrown by the parser but is not re-exported from the package's main\n * entry (only `UsageError` is — see clipanion's `advanced/index.d.ts`).\n * Identified by `name === 'UnknownSyntaxError'` and the\n * `clipanion.type === 'none'` discriminator that clipanion's\n * `ErrorWithMeta` interface guarantees.\n */\nfunction isUnknownSyntaxError(err: unknown): err is Error {\n if (!(err instanceof Error) || err.name !== 'UnknownSyntaxError') {\n return false;\n }\n // clipanion's `ErrorWithMeta` interface guarantees a `clipanion` field with\n // a `type` discriminator on every error it throws. Read it via a structural\n // shape rather than importing the class (it's not re-exported from the\n // package main).\n const meta = (err as { clipanion?: { type?: string } }).clipanion;\n return typeof meta === 'object' && meta !== null && meta.type === 'none';\n}\n\n/**\n * Best-effort: pull the first input token that doesn't match a known\n * flag. Falls back to the first token when we can't pinpoint it. The\n * returned name is rendered into the user-visible PN-CLI-4013 envelope\n * (`Unknown flag \\`<name>\\``) and round-tripped via `meta.flag` so\n * agent consumers can render their own \"did you mean\" suggestions.\n */\nfunction findOffendingFlag(input: readonly string[]): string {\n for (const token of input) {\n if (!token.startsWith('-')) {\n continue;\n }\n const head = token.split('=', 1)[0] ?? token;\n if (!KNOWN_FLAGS.includes(head) && head !== '-h') {\n return head;\n }\n }\n return input[0] ?? '';\n}\n\n/**\n * Write a `CliStructuredError` envelope to the given stream. Format\n * matches the legacy hand-rolled writer (`message: why`) so the rest of\n * the project's error rendering stays consistent across surfaces. The\n * full PN code (`PN-<domain>-<code>`) is included so consumers can\n * grep for stable identifiers.\n */\nfunction writeStructuredError(stream: MigrationCliWritable, err: CliStructuredError): void {\n const envelope = err.toEnvelope();\n const why = envelope.why ?? envelope.summary;\n const fix = envelope.fix ? `\\n${envelope.fix}` : '';\n stream.write(`${envelope.code}: ${envelope.summary}\\n${why}${fix}\\n`);\n}\n\n/**\n * Read a previously-scaffolded `migration.json` from disk, returning\n * `null` when the file is missing and throwing `MIGRATION.INVALID_JSON`\n * when the file is present but cannot be parsed as JSON. The CLI feeds\n * this into `buildMigrationArtifacts` so the pure builder can preserve\n * fields owned by `migration plan` (contract bookends, `createdAt`) across\n * re-emits.\n *\n * Author-time path: this loader still does not verify the manifest hash\n * or schema — that is the apply-time loader's job. Hash mismatch is the\n * *expected* outcome of a re-author (the developer's source changes\n * invalidate the prior hash by construction), and verification here\n * would block legitimate regenerations. Syntactic JSON-parse failure,\n * however, is now surfaced rather than swallowed: a malformed\n * `migration.json` indicates either a hand-edit gone wrong or partial\n * write, and silently rebuilding from `describe()` would discard the\n * user's on-disk content (preserved bookends, `createdAt`) without any\n * indication something was wrong on disk.\n * Apply-time consumers always route through the verifying\n * `readMigrationPackage` in `@prisma-next/migration-tools/io` instead.\n */\nfunction readExistingMetadata(metadataPath: string): Partial<MigrationMetadata> | null {\n let raw: string;\n try {\n raw = readFileSync(metadataPath, 'utf-8');\n } catch {\n return null;\n }\n try {\n return JSON.parse(raw) as Partial<MigrationMetadata>;\n } catch (e) {\n throw errorInvalidJson(metadataPath, e instanceof Error ? e.message : String(e));\n }\n}\n\n/**\n * Persist a migration instance's artifacts to `migrationDir`. In\n * `dryRun` mode the artifacts are printed to stdout (with the same\n * `--- migration.json --- / --- ops.json ---` framing the legacy\n * `serializeMigration` helper used) and no files are written. Otherwise\n * `ops.json` and `migration.json` are written next to `migration.ts` and\n * a confirmation line is printed.\n *\n * File I/O lives in the CLI rather than `@prisma-next/migration-tools`\n * so the migration-tools package stays focused on the pure\n * `Migration` → in-memory artifact conversion. The CLI is the only\n * legitimate site for combining config loading, stack assembly, and\n * filesystem persistence.\n */\nasync function serializeMigrationToDisk(\n instance: Migration,\n migrationDir: string,\n dryRun: boolean,\n stdout: MigrationCliWritable,\n): Promise<void> {\n const metadataPath = join(migrationDir, 'migration.json');\n const existing = readExistingMetadata(metadataPath);\n const { opsJson, metadataJson } = await buildMigrationArtifacts(instance, existing);\n\n if (dryRun) {\n stdout.write(`--- migration.json ---\\n${metadataJson}\\n`);\n stdout.write('--- ops.json ---\\n');\n stdout.write(`${opsJson}\\n`);\n return;\n }\n\n writeFileSync(join(migrationDir, 'ops.json'), opsJson);\n writeFileSync(metadataPath, metadataJson);\n\n stdout.write(`Wrote ops.json + migration.json to ${migrationDir}\\n`);\n}\n\n/**\n * Inner orchestration: load config, probe-construct the migration,\n * verify target, assemble the stack, construct with the stack, persist.\n *\n * Throws `CliStructuredError` for known failure modes (config not\n * found, target mismatch); the outer `orchestrate` translates those to\n * exit 1.\n */\nasync function runMigration(\n importMetaUrl: string,\n MigrationClass: MigrationConstructor,\n parsed: MigrationFileCommand,\n ctx: {\n readonly stdout: MigrationCliWritable;\n readonly stderr: MigrationCliWritable;\n },\n): Promise<void> {\n const migrationFile = fileURLToPath(importMetaUrl);\n const migrationDir = dirname(migrationFile);\n\n const config = await loadConfig(parsed.config);\n\n // Probe-instantiate without a stack so we can read `targetId` before\n // any target-specific constructor side effects (e.g.\n // `PostgresMigration`'s `stack.adapter.create(stack)`) run. Concrete\n // subclasses are required to accept the no-arg form; the abstract\n // `Migration` constructor declares `stack?` and target subclasses\n // (Postgres, Mongo) propagate that optionality. This makes the\n // target-mismatch guard fail fast with `PN-MIG-2006` before any\n // stack-driven adapter construction begins, even if the wrong-target\n // adapter's `create` would otherwise succeed and silently misshapen\n // the stored adapter cast.\n const probe = new MigrationClass();\n\n if (probe.targetId !== config.target.targetId) {\n throw errorMigrationTargetMismatch({\n migrationTargetId: probe.targetId,\n configTargetId: config.target.targetId,\n });\n }\n\n const stack = createControlStack(config);\n const instance = new MigrationClass(stack);\n\n await serializeMigrationToDisk(instance, migrationDir, parsed.dryRun, ctx.stdout);\n void ctx.stderr;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"migration-cli.mjs","names":[],"sources":["../src/migration-cli.ts"],"sourcesContent":["/**\n * The migration-file CLI interface: the actor invoked when the author runs\n * `node migration.ts` directly.\n *\n * Naming: this is *not* a \"migration runner\" in the apply-time sense. The\n * apply-time runner is the thing `prisma-next migrate` uses to\n * execute migration JSON ops against a database. `MigrationCLI` is the\n * tiny CLI surface owned by an authored `migration.ts` file: parse the\n * file's argv, load the project's `prisma-next.config.ts`, assemble a\n * `ControlStack`, instantiate the migration class, and serialize.\n *\n * The user authors a migration class, then calls\n * `MigrationCLI.run(import.meta.url, MigrationClass)` at module scope\n * after the class definition. When the file is invoked as a node\n * entrypoint (`node migration.ts`), the CLI:\n *\n * 1. Detects whether the file is the direct entrypoint (no-op when imported).\n * 2. Parses CLI args (`--help`, `--dry-run`, `--config <path>`) via\n * [clipanion](https://github.com/arcanis/clipanion).\n * 3. Loads the project's `prisma-next.config.ts` via the same `loadConfig`\n * the CLI commands use, walking up from the migration file's directory.\n * 4. Probe-instantiates the migration class without a stack so it can read\n * `targetId` and verify it matches `config.target.targetId`\n * (`PN-MIG-2006` on mismatch) before any stack-driven adapter\n * construction runs.\n * 5. Assembles a `ControlStack` from the loaded config descriptors and\n * constructs the migration with that stack.\n * 6. Reads any previously-scaffolded `migration.json`, then calls\n * `buildMigrationArtifacts` from `@prisma-next/migration-tools` to\n * produce in-memory `ops.json` + `migration.json` content. Persists\n * the result to disk (or prints in dry-run mode).\n *\n * File I/O lives here, in `@prisma-next/cli`: this is the only place\n * that legitimately combines config loading, stack assembly, and\n * on-disk persistence. `@prisma-next/migration-tools` owns the pure\n * conversion from a `Migration` instance to artifact strings; `Migration`\n * stays a pure abstract class.\n *\n * Parser library: clipanion (chosen over Commander/citty/`node:util.parseArgs`\n * for its in-process testability and runtime-agnostic execution surface; see\n * `docs/architecture docs/research/commander-friction-points.md` for the\n * evaluation rubric and the durable rationale that drove the choice).\n */\n\nimport { readFileSync, realpathSync, writeFileSync } from 'node:fs';\nimport type { Writable } from 'node:stream';\nimport { fileURLToPath } from 'node:url';\nimport { loadConfig } from '@prisma-next/config-loader';\nimport {\n CliStructuredError,\n errorMigrationCliInvalidConfigArg,\n errorMigrationCliUnknownFlag,\n} from '@prisma-next/errors/control';\nimport { errorMigrationTargetMismatch } from '@prisma-next/errors/migration';\nimport { createControlStack } from '@prisma-next/framework-components/control';\nimport { errorInvalidJson, MigrationToolsError } from '@prisma-next/migration-tools/errors';\nimport type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';\nimport { buildMigrationArtifacts, type Migration } from '@prisma-next/migration-tools/migration';\nimport { Cli, Command, Option, UsageError } from 'clipanion';\nimport { dirname, join } from 'pathe';\n\n/**\n * Constructor shape accepted by `MigrationCLI.run`. `Migration` subclasses\n * accept an optional `ControlStack` in their constructor (each subclass\n * narrows the stack to its own family/target generics); the CLI always\n * passes one assembled from the loaded config. We use a rest-args `any[]`\n * constructor signature so that subclass constructors with narrower\n * parameter types remain assignable - constructor type compatibility in\n * TS is contravariant in the parameter, and a wider `unknown` parameter\n * on the alias side would reject any narrower subclass signature.\n *\n * The CLI only ever passes one argument (`new MigrationClass(stack)`);\n * the rest-arity is purely a type-compatibility concession for subclass\n * constructors that declare narrower parameter types, not an extension\n * point for additional construction arguments.\n */\n// biome-ignore lint/suspicious/noExplicitAny: see JSDoc - rest args with any are the idiomatic TS pattern for accepting arbitrary subclass constructor signatures\nexport type MigrationConstructor = new (...args: any[]) => Migration;\n\n/**\n * Stream surface accepted by `MigrationCLI.run`'s `options.stdout` /\n * `options.stderr`. Aliases node's `Writable` because clipanion's\n * `BaseContext.stdout`/`stderr` are typed as `Writable`, and the CLI\n * forwards the injected streams into clipanion's context.\n *\n * `process.stdout` and `process.stderr` are `Writable`-shaped, so the\n * default-fallback path remains a no-op for existing two-argument\n * callers like `MigrationCLI.run(import.meta.url, MyMigration)`.\n *\n * Tests inject a `Writable` subclass that captures chunks for\n * assertions.\n */\nexport type MigrationCliWritable = Writable;\n\n/**\n * Flags exposed by the migration-file CLI.\n *\n * Must stay in sync with the `Option` declarations on\n * `MigrationFileCommand` below. This list is rendered in the\n * `errorMigrationCliUnknownFlag` envelope's `fix` text and `meta`,\n * so order matters for user-visible output (declaration order is the\n * order users see when they run `--help`).\n */\nconst KNOWN_FLAGS: readonly string[] = ['--help', '--dry-run', '--config'];\n\n/**\n * The clipanion command that owns the migration-file CLI's option\n * declarations. The class is internal — `MigrationCLI.run` is the\n * stable public surface. Adding a flag here automatically updates\n * `--help` rendering and the `KNOWN_FLAGS` list (the latter must be\n * updated in tandem).\n */\nclass MigrationFileCommand extends Command {\n static override paths = [Command.Default];\n\n static override usage = Command.Usage({\n description: 'Self-emit ops.json and migration.json from a class-flow migration',\n details: `\n Loads the project's prisma-next.config.ts, assembles a ControlStack\n from the configured target/adapter/extensions, and serializes the\n migration's operations + metadata next to this file.\n `,\n examples: [\n ['Self-emit ops.json + migration.json next to migration.ts', '$0'],\n ['Preview without writing files', '$0 --dry-run'],\n ['Use a non-default config path', '$0 --config ./custom.config.ts'],\n ],\n });\n\n dryRun = Option.Boolean('--dry-run', false, {\n description: 'Print operations to stdout without writing files',\n });\n\n config = Option.String('--config', {\n description: 'Path to prisma-next.config.ts',\n });\n\n /**\n * Unused: orchestration runs inside `MigrationCLI.run` so error\n * routing stays under our control (clipanion's `cli.run` writes\n * error output to `context.stdout`, but our contract requires\n * structured errors on stderr). `cli.process` is used to parse\n * argv into a populated `MigrationFileCommand` instance whose\n * fields drive the orchestration directly.\n */\n override async execute(): Promise<number> {\n return 0;\n }\n}\n\n/**\n * The CLI surface invoked by an authored `migration.ts` file. Exposed as\n * a class with a static `run` method (rather than a free function) to\n * give the concept a stable identity in the ubiquitous language: this is\n * the \"migration-file CLI\", distinct from the apply-time runner that\n * executes migration JSON ops.\n *\n * Currently a single static method. Future surface (e.g. a programmatic\n * `MigrationCLI.serializeOnly(...)` for tests, or extra subcommands) can\n * land here without changing the import shape used by every authored\n * migration.\n */\n// biome-ignore lint/complexity/noStaticOnlyClass: see JSDoc - intentional class facade for the migration-file CLI surface; future methods will share state derived from argv/config.\nexport class MigrationCLI {\n /**\n * Orchestrates a class-flow `migration.ts` script run.\n *\n * The third argument is the in-process testability surface: callers\n * (and tests) may inject `argv`, `stdout`, and `stderr` instead of\n * relying on `process.argv` / `process.stdout` / `process.stderr`.\n * Each option defaults to its `process` global when omitted, so\n * existing two-argument call sites\n * (`MigrationCLI.run(import.meta.url, MyMigration)`) continue to\n * compile and behave identically.\n *\n * Returns the exit code so the caller can branch on it. Also writes\n * the same code to `process.exitCode` so script-style callers that\n * don't await the return value still surface a non-zero exit when\n * something fails.\n *\n * Exit codes:\n * - 0 — success, or `--help`, or imported-not-entrypoint no-op.\n * - 1 — runtime/orchestration error (config not found, target\n * mismatch, etc.).\n * - 2 — usage error (unknown flag, malformed `--config`). Aligns\n * with `docs/CLI Style Guide.md` § Exit Codes.\n */\n static async run(\n importMetaUrl: string,\n MigrationClass: MigrationConstructor,\n options: {\n readonly argv?: readonly string[];\n readonly stdout?: MigrationCliWritable;\n readonly stderr?: MigrationCliWritable;\n } = {},\n ): Promise<number> {\n if (!importMetaUrl) {\n return 0;\n }\n\n const argv = options.argv ?? process.argv;\n const stdout = options.stdout ?? process.stdout;\n const stderr = options.stderr ?? process.stderr;\n\n if (!isDirectEntrypoint(importMetaUrl, argv)) {\n return 0;\n }\n\n const exitCode = await orchestrate(importMetaUrl, MigrationClass, {\n argv,\n stdout,\n stderr,\n });\n // Preserve any pre-existing non-zero `process.exitCode` set by code\n // running alongside `MigrationCLI.run` (an unhandled rejection\n // upstream, an explicit `process.exitCode = N` from another\n // module). Overwriting it with our success would mask the upstream\n // failure for script-style callers that don't await the return\n // value. Failures we return here are still surfaced — non-zero\n // codes always win over the prior status — but successes never\n // clear it.\n if (exitCode !== 0 || !process.exitCode) {\n process.exitCode = exitCode;\n }\n return exitCode;\n }\n}\n\n/**\n * Argv-aware variant of the entrypoint guard. The shared\n * `@prisma-next/migration-tools` helper of the same name reads\n * `process.argv[1]` directly, which doesn't compose with the new\n * in-process testability surface (tests inject `argv` without mutating\n * the process global). Inlined here so the migration-file CLI's check\n * follows the injected `argv[1]` consistently.\n */\nfunction isDirectEntrypoint(importMetaUrl: string, argv: readonly string[]): boolean {\n const argv1 = argv[1];\n if (!argv1) {\n return false;\n }\n try {\n return realpathSync(fileURLToPath(importMetaUrl)) === realpathSync(argv1);\n } catch {\n return false;\n }\n}\n\n/**\n * Argv-and-stream-driven orchestration body. Pulled out of the static\n * method so the entrypoint guard / process-default plumbing stays\n * separate from the parse + load + serialize steps.\n */\nasync function orchestrate(\n importMetaUrl: string,\n MigrationClass: MigrationConstructor,\n ctx: {\n readonly argv: readonly string[];\n readonly stdout: MigrationCliWritable;\n readonly stderr: MigrationCliWritable;\n },\n): Promise<number> {\n const cli = Cli.from([MigrationFileCommand], {\n binaryName: 'migration.ts',\n binaryLabel: 'Migration file CLI',\n });\n\n const input = ctx.argv.slice(2);\n\n // Pre-scan for malformed `--config` (no value, or value-shaped-as-flag)\n // before delegating to clipanion. The legacy parser surfaced both as\n // `errorMigrationCliInvalidConfigArg` (`PN-CLI-4012`); pre-scanning\n // here keeps that contract independent of how clipanion classifies\n // the error internally (it variably throws `UnknownSyntaxError` or\n // accepts the flag-shaped token as the value depending on what other\n // options are registered).\n const configError = detectInvalidConfig(input);\n if (configError) {\n writeStructuredError(ctx.stderr, configError);\n return 2;\n }\n\n let parsed: MigrationFileCommand;\n try {\n const command = cli.process({\n input: [...input],\n context: { stdout: ctx.stdout, stderr: ctx.stderr },\n });\n if (!(command instanceof MigrationFileCommand)) {\n // The only registered command class is `MigrationFileCommand`;\n // any other concrete type indicates clipanion emitted its\n // built-in `HelpCommand`. Render usage directly so we don't\n // depend on calling `cli.run` (which routes errors to stdout —\n // wrong stream for our contract).\n ctx.stdout.write(cli.usage(MigrationFileCommand, { detailed: true }));\n return 0;\n }\n parsed = command;\n } catch (err) {\n return renderParseError(err, input, ctx.stderr);\n }\n\n if (parsed.help) {\n ctx.stdout.write(cli.usage(MigrationFileCommand, { detailed: true }));\n return 0;\n }\n\n try {\n await runMigration(importMetaUrl, MigrationClass, parsed, ctx);\n return 0;\n } catch (err) {\n if (CliStructuredError.is(err)) {\n writeStructuredError(ctx.stderr, err);\n } else if (MigrationToolsError.is(err)) {\n // Migration-tools errors (e.g. `errorInvalidJson` thrown by\n // `readExistingMetadata` when migration.json is malformed) carry\n // their own `code`/`why`/`fix` shape. Render them with the same\n // visual structure as `CliStructuredError` so consumers grepping\n // for `MIGRATION.<CODE>` see consistent output across surfaces.\n const fix = err.fix ? `\\n${err.fix}` : '';\n ctx.stderr.write(`${err.code}: ${err.message}\\n${err.why}${fix}\\n`);\n } else {\n ctx.stderr.write(`${err instanceof Error ? err.message : String(err)}\\n`);\n }\n return 1;\n }\n}\n\n/**\n * Returns an `errorMigrationCliInvalidConfigArg` envelope when `input`\n * contains a malformed `--config`:\n *\n * - `--config` as the last token (no value follows).\n * - `--config <flag>` where `<flag>` starts with `-` (silently\n * consuming the next flag would either drop the flag or serialize\n * against the wrong project).\n * - `--config <empty>` where the value is the empty string. Shells\n * expand `--config \"\"` (or `--config \"$UNSET_VAR\"`) into a real\n * empty argv token; treating that as a usage error here surfaces\n * `PN-CLI-4012` instead of a less actionable loader error on an\n * empty path.\n * - `--config=` (the equals form with an empty value). Same shape as\n * the empty-string case above; the user expressed intent to override\n * the config path but the override is empty.\n *\n * `--config=<value>` and `--config <value>` with a non-empty value are\n * both valid (and the equals form's value is allowed to start with\n * `-` — the `=` makes the binding explicit).\n */\nfunction detectInvalidConfig(input: readonly string[]): CliStructuredError | null {\n for (let i = 0; i < input.length; i++) {\n const token = input[i];\n if (token === '--config') {\n const next = input[i + 1];\n if (next === undefined || next === '') {\n return errorMigrationCliInvalidConfigArg();\n }\n if (next.startsWith('-')) {\n return errorMigrationCliInvalidConfigArg({ nextToken: next });\n }\n continue;\n }\n if (token === '--config=') {\n return errorMigrationCliInvalidConfigArg();\n }\n }\n return null;\n}\n\n/**\n * Translate clipanion's parse-time errors into the project's structured\n * error envelopes.\n *\n * - `UnknownSyntaxError` covers both unknown flags (`--frobnicate`) and\n * the bare-trailing `--config` case (where arity-1 needs a value but\n * none was supplied). Distinguished by inspecting the input array.\n * - `UsageError` covers schema/validator failures from typanion. None\n * of the migration-file CLI's options have validators today, but we\n * still translate it as a usage error (exit 2) for forward-compat.\n * - Anything else re-throws — caller's outer catch will surface it as\n * exit 1 (runtime error).\n */\nfunction renderParseError(\n err: unknown,\n input: readonly string[],\n stderr: MigrationCliWritable,\n): number {\n if (isUnknownSyntaxError(err)) {\n const flag = findOffendingFlag(input);\n writeStructuredError(stderr, errorMigrationCliUnknownFlag({ flag, knownFlags: KNOWN_FLAGS }));\n return 2;\n }\n if (err instanceof UsageError) {\n // typanion validator failures and similar usage errors. None of\n // the migration-file CLI's options have validators today, so this\n // branch is forward-compat scaffolding — kept so that a future\n // option declaration with a validator routes through the same PN\n // envelope path rather than escaping as exit 1.\n writeStructuredError(stderr, errorMigrationCliInvalidConfigArg({ nextToken: err.message }));\n return 2;\n }\n throw err;\n}\n\n/**\n * Duck-type check for clipanion's `UnknownSyntaxError`: the class is\n * thrown by the parser but is not re-exported from the package's main\n * entry (only `UsageError` is — see clipanion's `advanced/index.d.ts`).\n * Identified by `name === 'UnknownSyntaxError'` and the\n * `clipanion.type === 'none'` discriminator that clipanion's\n * `ErrorWithMeta` interface guarantees.\n */\nfunction isUnknownSyntaxError(err: unknown): err is Error {\n if (!(err instanceof Error) || err.name !== 'UnknownSyntaxError') {\n return false;\n }\n // clipanion's `ErrorWithMeta` interface guarantees a `clipanion` field with\n // a `type` discriminator on every error it throws. Read it via a structural\n // shape rather than importing the class (it's not re-exported from the\n // package main).\n const meta = (err as { clipanion?: { type?: string } }).clipanion;\n return typeof meta === 'object' && meta !== null && meta.type === 'none';\n}\n\n/**\n * Best-effort: pull the first input token that doesn't match a known\n * flag. Falls back to the first token when we can't pinpoint it. The\n * returned name is rendered into the user-visible PN-CLI-4013 envelope\n * (`Unknown flag \\`<name>\\``) and round-tripped via `meta.flag` so\n * agent consumers can render their own \"did you mean\" suggestions.\n */\nfunction findOffendingFlag(input: readonly string[]): string {\n for (const token of input) {\n if (!token.startsWith('-')) {\n continue;\n }\n const head = token.split('=', 1)[0] ?? token;\n if (!KNOWN_FLAGS.includes(head) && head !== '-h') {\n return head;\n }\n }\n return input[0] ?? '';\n}\n\n/**\n * Write a `CliStructuredError` envelope to the given stream. Format\n * matches the legacy hand-rolled writer (`message: why`) so the rest of\n * the project's error rendering stays consistent across surfaces. The\n * full PN code (`PN-<domain>-<code>`) is included so consumers can\n * grep for stable identifiers.\n */\nfunction writeStructuredError(stream: MigrationCliWritable, err: CliStructuredError): void {\n const envelope = err.toEnvelope();\n const why = envelope.why ?? envelope.summary;\n const fix = envelope.fix ? `\\n${envelope.fix}` : '';\n stream.write(`${envelope.code}: ${envelope.summary}\\n${why}${fix}\\n`);\n}\n\n/**\n * Read a previously-scaffolded `migration.json` from disk, returning\n * `null` when the file is missing and throwing `MIGRATION.INVALID_JSON`\n * when the file is present but cannot be parsed as JSON. The CLI feeds\n * this into `buildMigrationArtifacts` so the pure builder can preserve\n * fields owned by `migration plan` (contract bookends, `createdAt`) across\n * re-emits.\n *\n * Author-time path: this loader still does not verify the manifest hash\n * or schema — that is the apply-time loader's job. Hash mismatch is the\n * *expected* outcome of a re-author (the developer's source changes\n * invalidate the prior hash by construction), and verification here\n * would block legitimate regenerations. Syntactic JSON-parse failure,\n * however, is now surfaced rather than swallowed: a malformed\n * `migration.json` indicates either a hand-edit gone wrong or partial\n * write, and silently rebuilding from `describe()` would discard the\n * user's on-disk content (preserved bookends, `createdAt`) without any\n * indication something was wrong on disk.\n * Apply-time consumers always route through the verifying\n * `readMigrationPackage` in `@prisma-next/migration-tools/io` instead.\n */\nfunction readExistingMetadata(metadataPath: string): Partial<MigrationMetadata> | null {\n let raw: string;\n try {\n raw = readFileSync(metadataPath, 'utf-8');\n } catch {\n return null;\n }\n try {\n return JSON.parse(raw) as Partial<MigrationMetadata>;\n } catch (e) {\n throw errorInvalidJson(metadataPath, e instanceof Error ? e.message : String(e));\n }\n}\n\n/**\n * Persist a migration instance's artifacts to `migrationDir`. In\n * `dryRun` mode the artifacts are printed to stdout (with the same\n * `--- migration.json --- / --- ops.json ---` framing the legacy\n * `serializeMigration` helper used) and no files are written. Otherwise\n * `ops.json` and `migration.json` are written next to `migration.ts` and\n * a confirmation line is printed.\n *\n * File I/O lives in the CLI rather than `@prisma-next/migration-tools`\n * so the migration-tools package stays focused on the pure\n * `Migration` → in-memory artifact conversion. The CLI is the only\n * legitimate site for combining config loading, stack assembly, and\n * filesystem persistence.\n */\nasync function serializeMigrationToDisk(\n instance: Migration,\n migrationDir: string,\n dryRun: boolean,\n stdout: MigrationCliWritable,\n): Promise<void> {\n const metadataPath = join(migrationDir, 'migration.json');\n const existing = readExistingMetadata(metadataPath);\n const { opsJson, metadataJson } = await buildMigrationArtifacts(instance, existing);\n\n if (dryRun) {\n stdout.write(`--- migration.json ---\\n${metadataJson}\\n`);\n stdout.write('--- ops.json ---\\n');\n stdout.write(`${opsJson}\\n`);\n return;\n }\n\n writeFileSync(join(migrationDir, 'ops.json'), opsJson);\n writeFileSync(metadataPath, metadataJson);\n\n stdout.write(`Wrote ops.json + migration.json to ${migrationDir}\\n`);\n}\n\n/**\n * Inner orchestration: load config, probe-construct the migration,\n * verify target, assemble the stack, construct with the stack, persist.\n *\n * Throws `CliStructuredError` for known failure modes (config not\n * found, target mismatch); the outer `orchestrate` translates those to\n * exit 1.\n */\nasync function runMigration(\n importMetaUrl: string,\n MigrationClass: MigrationConstructor,\n parsed: MigrationFileCommand,\n ctx: {\n readonly stdout: MigrationCliWritable;\n readonly stderr: MigrationCliWritable;\n },\n): Promise<void> {\n const migrationFile = fileURLToPath(importMetaUrl);\n const migrationDir = dirname(migrationFile);\n\n const config = await loadConfig(parsed.config);\n\n // Probe-instantiate without a stack so we can read `targetId` before\n // any target-specific constructor side effects (e.g.\n // `PostgresMigration`'s `stack.adapter.create(stack)`) run. Concrete\n // subclasses are required to accept the no-arg form; the abstract\n // `Migration` constructor declares `stack?` and target subclasses\n // (Postgres, Mongo) propagate that optionality. This makes the\n // target-mismatch guard fail fast with `PN-MIG-2006` before any\n // stack-driven adapter construction begins, even if the wrong-target\n // adapter's `create` would otherwise succeed and silently misshapen\n // the stored adapter cast.\n const probe = new MigrationClass();\n\n if (probe.targetId !== config.target.targetId) {\n throw errorMigrationTargetMismatch({\n migrationTargetId: probe.targetId,\n configTargetId: config.target.targetId,\n });\n }\n\n const stack = createControlStack(config);\n const instance = new MigrationClass(stack);\n\n await serializeMigrationToDisk(instance, migrationDir, parsed.dryRun, ctx.stdout);\n void ctx.stderr;\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import { t as loadConfig } from "./config-loader-p9JMrekQ.mjs";
2
- import { A as formatStyledHeader, B as errorContractValidationFailed, U as errorDriverRequired, V as errorDatabaseConnectionRequired, W as errorFileNotFound, ct as errorUnexpected, i as maskConnectionUrl, o as resolveContractPath, ot as errorTargetMigrationNotSupported, t as addGlobalOptions } from "./command-helpers-DGMvGBeX.mjs";
1
+ import { A as formatStyledHeader, B as errorContractValidationFailed, U as errorDriverRequired, V as errorDatabaseConnectionRequired, W as errorFileNotFound, ct as errorUnexpected, i as maskConnectionUrl, o as resolveContractPath, ot as errorTargetMigrationNotSupported, t as addGlobalOptions } from "./command-helpers-6cNJZ863.mjs";
3
2
  import { t as createProgressAdapter } from "./progress-adapter-CjAeTxY_.mjs";
4
- import { t as createControlClient } from "./client-CJzuo5wX.mjs";
3
+ import { t as createControlClient } from "./client-EnAjSmZg.mjs";
4
+ import { loadConfig } from "@prisma-next/config-loader";
5
5
  import { notOk, ok } from "@prisma-next/utils/result";
6
6
  import { readFile } from "node:fs/promises";
7
7
  import { hasMigrations } from "@prisma-next/framework-components/control";
@@ -100,4 +100,4 @@ function addMigrationCommandOptions(command) {
100
100
  //#endregion
101
101
  export { prepareMigrationContext as n, addMigrationCommandOptions as t };
102
102
 
103
- //# sourceMappingURL=migration-command-scaffold-DA-Lhx6o.mjs.map
103
+ //# sourceMappingURL=migration-command-scaffold-6OT_Q-3Y.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-command-scaffold-6OT_Q-3Y.mjs","names":[],"sources":["../src/utils/migration-command-scaffold.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { relative, resolve } from 'node:path';\nimport { loadConfig } from '@prisma-next/config-loader';\nimport { hasMigrations } from '@prisma-next/framework-components/control';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport type { Command } from 'commander';\nimport { createControlClient } from '../control-api/client';\nimport type { ControlClient } from '../control-api/types';\nimport {\n type CliStructuredError,\n errorContractValidationFailed,\n errorDatabaseConnectionRequired,\n errorDriverRequired,\n errorFileNotFound,\n errorTargetMigrationNotSupported,\n errorUnexpected,\n} from './cli-errors';\nimport { addGlobalOptions, maskConnectionUrl, resolveContractPath } from './command-helpers';\nimport { formatStyledHeader } from './formatters/styled';\nimport type { GlobalFlags } from './global-flags';\nimport { createProgressAdapter } from './progress-adapter';\nimport type { TerminalUI } from './terminal-ui';\n\n/**\n * Resolved context for a migration command.\n * Contains everything needed to invoke a control-api operation.\n */\nexport interface MigrationContext {\n readonly client: ControlClient;\n readonly contractJson: Record<string, unknown>;\n readonly dbConnection: unknown;\n readonly onProgress: ReturnType<typeof createProgressAdapter>;\n readonly configPath: string;\n readonly contractPath: string;\n readonly contractPathAbsolute: string;\n readonly config: Awaited<ReturnType<typeof loadConfig>>;\n}\n\n/**\n * Command-specific configuration for the shared scaffold.\n */\nexport interface MigrationCommandDescriptor {\n readonly commandName: string;\n readonly description: string;\n readonly url: string;\n}\n\n/**\n * Prepares the shared context for migration commands (db init, db update).\n *\n * Handles: config loading, contract file reading, JSON parsing, connection resolution,\n * driver/migration-support validation, client creation, and header output.\n *\n * Returns a Result with either the resolved context or a structured error.\n */\nexport async function prepareMigrationContext(\n options: { readonly db?: string; readonly config?: string; readonly dryRun?: boolean },\n flags: GlobalFlags,\n ui: TerminalUI,\n descriptor: MigrationCommandDescriptor,\n): Promise<Result<MigrationContext, CliStructuredError>> {\n // Load config\n const config = await loadConfig(options.config);\n const configPath = options.config\n ? relative(process.cwd(), resolve(options.config))\n : 'prisma-next.config.ts';\n const contractPathAbsolute = resolveContractPath(config);\n const contractPath = relative(process.cwd(), contractPathAbsolute);\n\n // Output header to stderr (decoration)\n if (!flags.json && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n { label: 'contract', value: contractPath },\n ];\n if (options.db) {\n details.push({ label: 'database', value: maskConnectionUrl(options.db) });\n }\n if (options.dryRun) {\n details.push({ label: 'mode', value: 'dry run' });\n }\n const header = formatStyledHeader({\n command: descriptor.commandName,\n description: descriptor.description,\n url: descriptor.url,\n details,\n flags,\n });\n ui.stderr(header);\n }\n\n // Load contract file\n let contractJsonContent: string;\n try {\n contractJsonContent = await readFile(contractPathAbsolute, 'utf-8');\n } catch (error) {\n if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {\n return notOk(\n errorFileNotFound(contractPathAbsolute, {\n why: `Contract file not found at ${contractPathAbsolute}`,\n fix: `Run \\`prisma-next contract emit\\` to generate ${contractPath}, or update \\`config.contract.output\\` in ${configPath}`,\n }),\n );\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n }\n\n // Parse contract JSON\n let contractJson: Record<string, unknown>;\n try {\n contractJson = JSON.parse(contractJsonContent) as Record<string, unknown>;\n } catch (error) {\n return notOk(\n errorContractValidationFailed(\n `Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`,\n { where: { path: contractPathAbsolute } },\n ),\n );\n }\n\n // Resolve database connection (--db flag or config.db.connection)\n const dbConnection = options.db ?? config.db?.connection;\n if (!dbConnection) {\n return notOk(\n errorDatabaseConnectionRequired({\n why: `Database connection is required for ${descriptor.commandName} (set db.connection in ${configPath}, or pass --db <url>)`,\n commandName: descriptor.commandName,\n }),\n );\n }\n\n // Check for driver\n if (!config.driver) {\n return notOk(\n errorDriverRequired({ why: `Config.driver is required for ${descriptor.commandName}` }),\n );\n }\n\n if (!hasMigrations(config.target)) {\n return notOk(\n errorTargetMigrationNotSupported({\n why: `Target \"${config.target.id}\" does not support migrations`,\n }),\n );\n }\n\n // Create control client\n const client = createControlClient({\n family: config.family,\n target: config.target,\n adapter: config.adapter,\n driver: config.driver,\n extensionPacks: config.extensionPacks ?? [],\n });\n\n // Create progress adapter\n const onProgress = createProgressAdapter({ ui, flags });\n\n return ok({\n client,\n contractJson,\n dbConnection,\n onProgress,\n configPath,\n contractPath,\n contractPathAbsolute,\n config,\n });\n}\n\n/**\n * Registers the shared CLI options for migration commands (db init, db update).\n */\nexport function addMigrationCommandOptions(command: Command): Command {\n addGlobalOptions(command);\n return command\n .option('--db <url>', 'Database connection string')\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .option('--dry-run', 'Preview planned operations without applying', false);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAuDA,eAAsB,wBACpB,SACA,OACA,IACA,YACuD;CAEvD,MAAM,SAAS,MAAM,WAAW,QAAQ,MAAM;CAC9C,MAAM,aAAa,QAAQ,SACvB,SAAS,QAAQ,IAAI,GAAG,QAAQ,QAAQ,MAAM,CAAC,IAC/C;CACJ,MAAM,uBAAuB,oBAAoB,MAAM;CACvD,MAAM,eAAe,SAAS,QAAQ,IAAI,GAAG,oBAAoB;CAGjE,IAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,OAAO;EAC/B,MAAM,UAAmD,CACvD;GAAE,OAAO;GAAU,OAAO;EAAW,GACrC;GAAE,OAAO;GAAY,OAAO;EAAa,CAC3C;EACA,IAAI,QAAQ,IACV,QAAQ,KAAK;GAAE,OAAO;GAAY,OAAO,kBAAkB,QAAQ,EAAE;EAAE,CAAC;EAE1E,IAAI,QAAQ,QACV,QAAQ,KAAK;GAAE,OAAO;GAAQ,OAAO;EAAU,CAAC;EAElD,MAAM,SAAS,mBAAmB;GAChC,SAAS,WAAW;GACpB,aAAa,WAAW;GACxB,KAAK,WAAW;GAChB;GACA;EACF,CAAC;EACD,GAAG,OAAO,MAAM;CAClB;CAGA,IAAI;CACJ,IAAI;EACF,sBAAsB,MAAM,SAAS,sBAAsB,OAAO;CACpE,SAAS,OAAO;EACd,IAAI,iBAAiB,SAAU,MAA4B,SAAS,UAClE,OAAO,MACL,kBAAkB,sBAAsB;GACtC,KAAK,8BAA8B;GACnC,KAAK,iDAAiD,aAAa,4CAA4C;EACjH,CAAC,CACH;EAEF,OAAO,MACL,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG,EACtE,KAAK,iCAAiC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,IAC7F,CAAC,CACH;CACF;CAGA,IAAI;CACJ,IAAI;EACF,eAAe,KAAK,MAAM,mBAAmB;CAC/C,SAAS,OAAO;EACd,OAAO,MACL,8BACE,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAClF,EAAE,OAAO,EAAE,MAAM,qBAAqB,EAAE,CAC1C,CACF;CACF;CAGA,MAAM,eAAe,QAAQ,MAAM,OAAO,IAAI;CAC9C,IAAI,CAAC,cACH,OAAO,MACL,gCAAgC;EAC9B,KAAK,uCAAuC,WAAW,YAAY,yBAAyB,WAAW;EACvG,aAAa,WAAW;CAC1B,CAAC,CACH;CAIF,IAAI,CAAC,OAAO,QACV,OAAO,MACL,oBAAoB,EAAE,KAAK,iCAAiC,WAAW,cAAc,CAAC,CACxF;CAGF,IAAI,CAAC,cAAc,OAAO,MAAM,GAC9B,OAAO,MACL,iCAAiC,EAC/B,KAAK,WAAW,OAAO,OAAO,GAAG,+BACnC,CAAC,CACH;CAIF,MAAM,SAAS,oBAAoB;EACjC,QAAQ,OAAO;EACf,QAAQ,OAAO;EACf,SAAS,OAAO;EAChB,QAAQ,OAAO;EACf,gBAAgB,OAAO,kBAAkB,CAAC;CAC5C,CAAC;CAGD,MAAM,aAAa,sBAAsB;EAAE;EAAI;CAAM,CAAC;CAEtD,OAAO,GAAG;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;AACH;;;;AAKA,SAAgB,2BAA2B,SAA2B;CACpE,iBAAiB,OAAO;CACxB,OAAO,QACJ,OAAO,cAAc,4BAA4B,CAAC,CAClD,OAAO,mBAAmB,+BAA+B,CAAC,CAC1D,OAAO,aAAa,+CAA+C,KAAK;AAC7E"}
@@ -1,8 +1,8 @@
1
- import { t as loadConfig } from "./config-loader-p9JMrekQ.mjs";
2
- import { A as formatStyledHeader, K as errorInvalidSpaceId, _ as createTerminalUI, at as errorSpaceNotFound, d as setCommandSeeAlso, g as parseGlobalFlagsOrExit, l as setCommandDescriptions, q as errorLegendHumanOnly, s as resolveMigrationPaths, t as addGlobalOptions, u as setCommandExamples, y as handleResult } from "./command-helpers-DGMvGBeX.mjs";
3
- import { n as buildReadAggregate } from "./contract-space-aggregate-loader-ClI1KN6d.mjs";
1
+ import { A as formatStyledHeader, K as errorInvalidSpaceId, _ as createTerminalUI, at as errorSpaceNotFound, d as setCommandSeeAlso, g as parseGlobalFlagsOrExit, l as setCommandDescriptions, q as errorLegendHumanOnly, s as resolveMigrationPaths, t as addGlobalOptions, u as setCommandExamples, y as handleResult } from "./command-helpers-6cNJZ863.mjs";
2
+ import { n as buildReadAggregate } from "./contract-space-aggregate-loader-Bu2fjSWV.mjs";
4
3
  import { a as renderMigrationGraphLegend, c as renderMigrationListWithStyle, o as createAnsiMigrationListStyler } from "./migration-graph-command-render-CEez7YUK.mjs";
5
4
  import { Command } from "commander";
5
+ import { loadConfig } from "@prisma-next/config-loader";
6
6
  import { ifDefined } from "@prisma-next/utils/defined";
7
7
  import { notOk, ok } from "@prisma-next/utils/result";
8
8
  import { APP_SPACE_ID, RESERVED_SPACE_SUBDIR_NAMES, isValidSpaceId, listContractSpaceDirectories } from "@prisma-next/migration-tools/spaces";
@@ -227,4 +227,4 @@ function createMigrationListCommand() {
227
227
  //#endregion
228
228
  export { renderMigrationListHumanOutput as a, validateLegendOptions as c, migrationSpaceListEntriesFromAggregate as i, executeMigrationListCommand as n, runMigrationList as o, listRefsByContractHash as r, shouldShowLegend as s, createMigrationListCommand as t };
229
229
 
230
- //# sourceMappingURL=migration-list-DlJJ_38Z.mjs.map
230
+ //# sourceMappingURL=migration-list-DVHVmMrf.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-list-DVHVmMrf.mjs","names":[],"sources":["../src/utils/legend.ts","../src/commands/migration-list.ts"],"sourcesContent":["import { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { type CliStructuredError, errorLegendHumanOnly } from './cli-errors';\nimport type { GlobalFlags } from './global-flags';\n\nexport interface LegendCliOptions {\n readonly legend?: boolean;\n readonly dot?: boolean;\n}\n\n/**\n * The legend is decoration printed alongside the command header on stderr, so\n * it is suppressed for the machine-readable / silent paths (`--json`, `--dot`,\n * `--quiet`) exactly as the header is.\n */\nexport function shouldShowLegend(options: LegendCliOptions, flags: GlobalFlags): boolean {\n return (\n options.legend === true && options.dot !== true && flags.json !== true && flags.quiet !== true\n );\n}\n\nexport function validateLegendOptions(\n options: LegendCliOptions,\n flags: GlobalFlags,\n): Result<void, CliStructuredError> {\n if (options.legend !== true) {\n return ok(undefined);\n }\n if (flags.json === true) {\n return notOk(errorLegendHumanOnly('--json'));\n }\n if (flags.quiet === true) {\n return notOk(errorLegendHumanOnly('--quiet'));\n }\n if (options.dot === true) {\n return notOk(errorLegendHumanOnly('--dot'));\n }\n return ok(undefined);\n}\n","import { loadConfig } from '@prisma-next/config-loader';\nimport type {\n ContractSpaceAggregate,\n ContractSpaceMember,\n} from '@prisma-next/migration-tools/aggregate';\nimport type { MigrationGraph } from '@prisma-next/migration-tools/graph';\nimport { HEAD_REF_NAME, refsByContractHash } from '@prisma-next/migration-tools/refs';\nimport {\n APP_SPACE_ID,\n isValidSpaceId,\n listContractSpaceDirectories,\n RESERVED_SPACE_SUBDIR_NAMES,\n} from '@prisma-next/migration-tools/spaces';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport {\n type CliStructuredError,\n errorInvalidSpaceId,\n errorSpaceNotFound,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n resolveMigrationPaths,\n setCommandDescriptions,\n setCommandExamples,\n setCommandSeeAlso,\n} from '../utils/command-helpers';\nimport { buildReadAggregate } from '../utils/contract-space-aggregate-loader';\nimport { renderMigrationGraphLegend } from '../utils/formatters/migration-graph-labels';\nimport { renderMigrationListWithStyle } from '../utils/formatters/migration-list-render';\nimport { createAnsiMigrationListStyler } from '../utils/formatters/migration-list-styler';\nimport type {\n MigrationListEntry,\n MigrationListResult,\n MigrationSpaceListEntry,\n} from '../utils/formatters/migration-list-types';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';\nimport type { GlyphMode } from '../utils/glyph-mode';\nimport { shouldShowLegend, validateLegendOptions } from '../utils/legend';\nimport { handleResult } from '../utils/result-handler';\nimport { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';\n\nfunction compareSpaceIds(a: string, b: string): number {\n if (a === APP_SPACE_ID) return b === APP_SPACE_ID ? 0 : -1;\n if (b === APP_SPACE_ID) return 1;\n if (a < b) return -1;\n if (a > b) return 1;\n return 0;\n}\n\nfunction compareDirNamesDescending(a: MigrationListEntry, b: MigrationListEntry): number {\n if (a.name < b.name) return 1;\n if (a.name > b.name) return -1;\n return 0;\n}\n\n/**\n * Ref names decorating a space's destination contract hashes. The\n * tolerant `member.refs` deliberately omits the structural `head.json`;\n * for extension spaces the old enumerator surfaced it as a `head`\n * decoration on the tip migration, so fold `member.headRef` back in to\n * keep that output. The app space synthesises its head, so it carries\n * no on-disk `head` ref to restore.\n */\nexport function listRefsByContractHash(\n member: ContractSpaceMember,\n): ReadonlyMap<string, readonly string[]> {\n const byHash = new Map(refsByContractHash(member.refs));\n if (member.spaceId !== APP_SPACE_ID && member.headRef !== null) {\n const hash = member.headRef.hash;\n const bucket = byHash.get(hash) ?? [];\n if (!bucket.includes(HEAD_REF_NAME)) {\n byHash.set(hash, [...bucket, HEAD_REF_NAME].sort());\n }\n }\n return byHash;\n}\n\nasync function orderedOnDiskSpaceIds(projectMigrationsDir: string): Promise<readonly string[]> {\n const candidateDirs = await listContractSpaceDirectories(projectMigrationsDir);\n return candidateDirs\n .filter((name) => !RESERVED_SPACE_SUBDIR_NAMES.has(name))\n .filter(isValidSpaceId)\n .sort(compareSpaceIds);\n}\n\n/**\n * Project the loaded {@link ContractSpaceAggregate} into the render-ready\n * {@link MigrationSpaceListEntry} rows `migration list` displays.\n *\n * Space membership matches the on-disk contract-space directories (not the\n * aggregate's always-present synthesized app member when `migrations/app/`\n * is absent); package and ref data come from `aggregate.space(id)`.\n */\nexport async function migrationSpaceListEntriesFromAggregate(\n aggregate: ContractSpaceAggregate,\n projectMigrationsDir: string,\n): Promise<readonly MigrationSpaceListEntry[]> {\n const spaceIds = await orderedOnDiskSpaceIds(projectMigrationsDir);\n const spaces: MigrationSpaceListEntry[] = [];\n\n for (const spaceId of spaceIds) {\n const member = aggregate.space(spaceId);\n if (member === undefined) {\n continue;\n }\n const refsByHash = listRefsByContractHash(member);\n const migrations: MigrationListEntry[] = member.packages\n .map((pkg) => ({\n name: pkg.dirName,\n hash: pkg.metadata.migrationHash,\n fromContract: pkg.metadata.from,\n toContract: pkg.metadata.to,\n operationCount: pkg.ops.length,\n createdAt: pkg.metadata.createdAt,\n refs: [...(refsByHash.get(pkg.metadata.to) ?? [])],\n providedInvariants: [...pkg.metadata.providedInvariants],\n }))\n .sort(compareDirNamesDescending);\n\n spaces.push({ space: spaceId, migrations });\n }\n\n return spaces;\n}\n\ninterface MigrationListOptions extends CommonCommandOptions {\n readonly config?: string;\n readonly space?: string;\n readonly ascii?: boolean;\n readonly legend?: boolean;\n}\n\nexport interface MigrationListExecuteResult {\n readonly list: MigrationListResult;\n readonly liveContractHash: string;\n readonly aggregate: ContractSpaceAggregate;\n}\n\nexport interface MigrationListHumanRenderOptions {\n readonly glyphMode: GlyphMode;\n readonly useColor: boolean;\n readonly liveContractHash: string;\n readonly graphForSpace: (spaceId: string) => MigrationGraph | undefined;\n readonly appSpaceId?: string;\n}\n\nexport function renderMigrationListHumanOutput(\n result: MigrationListResult,\n options: MigrationListHumanRenderOptions,\n): string {\n const styler = createAnsiMigrationListStyler({ useColor: options.useColor });\n return renderMigrationListWithStyle(result, styler, options.glyphMode, {\n colorize: options.useColor,\n liveContractHash: options.liveContractHash,\n graphForSpace: options.graphForSpace,\n ...(options.appSpaceId !== undefined ? { appSpaceId: options.appSpaceId } : {}),\n });\n}\n\n/**\n * Inputs for {@link runMigrationList} — the policy core of `migration list`\n * that tests exercise directly.\n *\n * The core does not call `loadConfig`, parse CLI flags, render a styled\n * header, or write to any stream. Enumeration is supplied by the caller\n * (the CLI shell builds it from {@link migrationSpaceListEntriesFromAggregate}).\n */\nexport interface RunMigrationListInputs {\n readonly spaces: readonly MigrationSpaceListEntry[];\n readonly spaceFilter?: string;\n}\n\nfunction computeSummary(spaces: readonly MigrationSpaceListEntry[]): string {\n const totalMigrations = spaces.reduce((count, space) => count + space.migrations.length, 0);\n if (spaces.length <= 1) {\n return `${totalMigrations} migration(s) on disk`;\n }\n return `${totalMigrations} migration(s) across ${spaces.length} contract space(s)`;\n}\n\n/**\n * Policy core of `migration list`: validates `--space`, narrows the\n * pre-enumerated spaces, and assembles a {@link MigrationListResult}.\n *\n * - `migrations/` missing or contains no valid space directories →\n * caller passes `spaces: []`; this synthesizes `[{ spaceId: APP_SPACE_ID, migrations: [] }]`.\n * - `--space <id>` on an existing-but-empty space → `{ spaceId, migrations: [] }` in the input.\n * - `--space <id>` on a non-existent (or reserved) space → `SPACE_NOT_FOUND`.\n */\nexport function runMigrationList(\n inputs: RunMigrationListInputs,\n): Result<MigrationListResult, CliStructuredError> {\n const { spaces, spaceFilter } = inputs;\n\n if (spaceFilter !== undefined && !isValidSpaceId(spaceFilter)) {\n return notOk(errorInvalidSpaceId(spaceFilter));\n }\n\n if (spaceFilter !== undefined && !spaces.some((s) => s.space === spaceFilter)) {\n return notOk(errorSpaceNotFound(spaceFilter, spaces.map((s) => s.space).sort()));\n }\n\n const scopedSpaces =\n spaceFilter !== undefined ? spaces.filter((s) => s.space === spaceFilter) : spaces;\n\n const resultSpaces: readonly MigrationSpaceListEntry[] =\n scopedSpaces.length === 0 ? [{ space: APP_SPACE_ID, migrations: [] }] : scopedSpaces;\n\n return ok({\n ok: true,\n spaces: [...resultSpaces],\n summary: computeSummary(resultSpaces),\n });\n}\n\n/**\n * CLI shell: loads config, resolves paths, prints the styled header on\n * stderr (interactive mode only), and delegates to {@link runMigrationList}.\n * Kept intentionally thin so the unit-testable surface lives in the core.\n */\nexport async function executeMigrationListCommand(\n options: MigrationListOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n): Promise<Result<MigrationListExecuteResult, CliStructuredError>> {\n const config = await loadConfig(options.config);\n const { configPath, migrationsDir, migrationsRelative } = resolveMigrationPaths(\n options.config,\n config,\n );\n\n if (!flags.json && !flags.quiet) {\n const header = formatStyledHeader({\n command: 'migration list',\n description: 'List on-disk migrations per contract space',\n details: [\n { label: 'config', value: configPath },\n { label: 'migrations', value: migrationsRelative },\n ...(options.space !== undefined ? [{ label: 'space', value: options.space }] : []),\n ],\n flags,\n });\n ui.stderr(header);\n if (shouldShowLegend(options, flags)) {\n ui.stderr(\n renderMigrationGraphLegend({\n colorize: flags.color !== false,\n glyphMode: ui.resolveGlyphMode(options.ascii === true),\n }),\n );\n ui.stderr('');\n }\n }\n\n const loaded = await buildReadAggregate(config, { migrationsDir });\n if (!loaded.ok) {\n return notOk(loaded.failure);\n }\n\n const { aggregate, contractHash: liveContractHash } = loaded.value;\n\n const spaces = await migrationSpaceListEntriesFromAggregate(aggregate, migrationsDir);\n\n const listResult = runMigrationList({\n spaces,\n ...ifDefined('spaceFilter', options.space),\n });\n if (!listResult.ok) {\n return listResult;\n }\n return ok({ list: listResult.value, liveContractHash, aggregate });\n}\n\nexport function createMigrationListCommand(): Command {\n const command = new Command('list');\n setCommandDescriptions(\n command,\n 'List on-disk migrations per contract space',\n 'Enumerates every on-disk migration under migrations/<space>/ for every\\n' +\n 'contract space found on disk. Offline — does not consult the database.\\n' +\n 'Human output draws the shared migration graph tree with operation counts,\\n' +\n 'invariants on each migration row, and refs on destination contract nodes.\\n' +\n 'Pass --space <id> to narrow to one contract space. --ascii forces ASCII\\n' +\n 'tree glyphs (orthogonal to --no-color).',\n );\n setCommandExamples(command, [\n 'prisma-next migration list',\n 'prisma-next migration list --space app',\n 'prisma-next migration list --ascii',\n 'prisma-next migration list --legend',\n 'prisma-next migration list --json',\n ]);\n setCommandSeeAlso(command, [\n { verb: 'migration status', oneLiner: 'Show migration path and pending status' },\n { verb: 'migration log', oneLiner: 'Show executed migration history' },\n { verb: 'migration graph', oneLiner: 'Show the migration graph topology' },\n { verb: 'migration show', oneLiner: 'Display migration package contents' },\n ]);\n addGlobalOptions(command)\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .option('--space <id>', 'Narrow output to a single contract space')\n .option('--ascii', 'Use ASCII kind glyphs (pipe-friendly)')\n .option('--legend', 'Print a key for the tree glyphs and lane colors')\n .action(async (options: MigrationListOptions) => {\n const flags = parseGlobalFlagsOrExit(options);\n const ui = createTerminalUI(flags);\n const legendValidation = validateLegendOptions(options, flags);\n if (!legendValidation.ok) {\n process.exit(handleResult(legendValidation, flags, ui));\n }\n const result = await executeMigrationListCommand(options, flags, ui);\n const exitCode = handleResult(result, flags, ui, ({ list, liveContractHash, aggregate }) => {\n if (flags.json) {\n ui.output(JSON.stringify(list, null, 2));\n } else if (!flags.quiet) {\n ui.output(\n renderMigrationListHumanOutput(list, {\n glyphMode: ui.resolveGlyphMode(options.ascii === true),\n useColor: ui.useColor,\n liveContractHash,\n graphForSpace: (spaceId) => aggregate.space(spaceId)?.graph(),\n appSpaceId: aggregate.app.spaceId,\n }),\n );\n }\n });\n process.exit(exitCode);\n });\n return command;\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
- import { t as loadConfig } from "./config-loader-p9JMrekQ.mjs";
2
- import { A as formatStyledHeader, F as CliStructuredError, _ as createTerminalUI, ct as errorUnexpected, d as setCommandSeeAlso, dt as requireLiveDatabase, f as targetSupportsMigrations, g as parseGlobalFlagsOrExit, i as maskConnectionUrl, l as setCommandDescriptions, lt as mapMigrationToolsError, s as resolveMigrationPaths, t as addGlobalOptions, u as setCommandExamples, y as handleResult } from "./command-helpers-DGMvGBeX.mjs";
3
- import { t as createControlClient } from "./client-CJzuo5wX.mjs";
1
+ import { A as formatStyledHeader, F as CliStructuredError, _ as createTerminalUI, ct as errorUnexpected, d as setCommandSeeAlso, dt as requireLiveDatabase, f as targetSupportsMigrations, g as parseGlobalFlagsOrExit, i as maskConnectionUrl, l as setCommandDescriptions, lt as mapMigrationToolsError, s as resolveMigrationPaths, t as addGlobalOptions, u as setCommandExamples, y as handleResult } from "./command-helpers-6cNJZ863.mjs";
2
+ import { t as createControlClient } from "./client-EnAjSmZg.mjs";
4
3
  import { _ as migrationListEmptySource, g as abbreviateContractHash, o as createAnsiMigrationListStyler, s as IDENTITY_MIGRATION_LIST_STYLER, v as migrationListForwardArrow } from "./migration-graph-command-render-CEez7YUK.mjs";
5
4
  import { Command } from "commander";
5
+ import { loadConfig } from "@prisma-next/config-loader";
6
6
  import { ifDefined } from "@prisma-next/utils/defined";
7
7
  import { notOk, ok } from "@prisma-next/utils/result";
8
8
  import stringWidth from "string-width";
@@ -219,4 +219,4 @@ function createMigrationLogCommand() {
219
219
  //#endregion
220
220
  export { executeMigrationLogCommand as n, createMigrationLogCommand as t };
221
221
 
222
- //# sourceMappingURL=migration-log-CG0qQAFm.mjs.map
222
+ //# sourceMappingURL=migration-log-CQpc9JoL.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-log-CQpc9JoL.mjs","names":[],"sources":["../src/utils/formatters/migration-log-table.ts","../src/commands/migration-log.ts"],"sourcesContent":["import type { LedgerEntryRecord } from '@prisma-next/contract/types';\nimport stringWidth from 'string-width';\nimport type { GlyphMode } from '../glyph-mode';\nimport {\n abbreviateContractHash,\n migrationListEmptySource,\n migrationListForwardArrow,\n} from './migration-list-data-column';\nimport { IDENTITY_MIGRATION_LIST_STYLER, type MigrationListStyler } from './migration-list-render';\n\nexport type LedgerTimestampMode = 'local' | 'utc' | 'iso';\n\nexport interface RenderMigrationLogTableOptions {\n readonly utc?: boolean;\n readonly styler?: MigrationListStyler;\n readonly glyphMode?: GlyphMode;\n}\n\nexport interface SerializedLedgerEntryRecord {\n readonly space: string;\n readonly name: string;\n readonly hash: string;\n readonly fromContract: string | null;\n readonly toContract: string;\n readonly appliedAt: string;\n readonly operationCount: number;\n}\n\nconst HEADING_APPLIED_AT = 'Applied at';\nconst HEADING_SPACE = 'Space';\nconst HEADING_MIGRATION = 'Migration';\nconst HEADING_CHANGE = 'Change';\nconst HEADING_OPS = 'Ops';\nconst COLUMN_SEPARATOR = ' ';\nconst DIVIDER_CHAR = '─';\nconst ASCII_DIVIDER_CHAR = '-';\n\nexport function sortLedgerEntries(entries: readonly LedgerEntryRecord[]): LedgerEntryRecord[] {\n return [...entries].sort((left, right) => {\n const timeDiff = left.appliedAt.getTime() - right.appliedAt.getTime();\n if (timeDiff !== 0) {\n return timeDiff;\n }\n const spaceDiff = left.space.localeCompare(right.space);\n if (spaceDiff !== 0) {\n return spaceDiff;\n }\n return left.migrationName.localeCompare(right.migrationName);\n });\n}\n\nfunction pad2(value: number): string {\n return String(value).padStart(2, '0');\n}\n\nexport function formatLedgerAppliedAt(date: Date, mode: LedgerTimestampMode): string {\n if (mode === 'iso') {\n return date.toISOString();\n }\n if (mode === 'utc') {\n return `${date.getUTCFullYear()}-${pad2(date.getUTCMonth() + 1)}-${pad2(date.getUTCDate())} ${pad2(date.getUTCHours())}:${pad2(date.getUTCMinutes())}:${pad2(date.getUTCSeconds())}Z`;\n }\n const offsetMinutes = -date.getTimezoneOffset();\n const sign = offsetMinutes >= 0 ? '+' : '-';\n const absoluteOffset = Math.abs(offsetMinutes);\n const offsetHours = pad2(Math.floor(absoluteOffset / 60));\n const offsetMins = pad2(absoluteOffset % 60);\n return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())} ${pad2(date.getHours())}:${pad2(date.getMinutes())}:${pad2(date.getSeconds())} ${sign}${offsetHours}:${offsetMins}`;\n}\n\nexport function formatHashEndpoint(hash: string | null, glyphMode: GlyphMode = 'unicode'): string {\n if (hash === null) {\n return migrationListEmptySource(glyphMode);\n }\n return abbreviateContractHash(hash);\n}\n\nexport function formatHashTransition(\n from: string | null,\n to: string,\n glyphMode: GlyphMode = 'unicode',\n): string {\n return `${formatHashEndpoint(from, glyphMode)} ${migrationListForwardArrow(glyphMode)} ${abbreviateContractHash(to)}`;\n}\n\nexport function styleHashTransition(\n from: string | null,\n to: string,\n styler: MigrationListStyler,\n glyphMode: GlyphMode = 'unicode',\n): string {\n const fromPart =\n from === null\n ? styler.glyph(migrationListEmptySource(glyphMode))\n : styler.sourceHash(abbreviateContractHash(from));\n const arrow = styler.glyph(migrationListForwardArrow(glyphMode));\n const dest = styler.destHash(abbreviateContractHash(to));\n return `${fromPart} ${arrow} ${dest}`;\n}\n\nfunction padVisible(text: string, targetWidth: number): string {\n const padding = Math.max(0, targetWidth - stringWidth(text));\n return text + ' '.repeat(padding);\n}\n\nfunction columnWidth(values: readonly string[]): number {\n return values.reduce((max, value) => Math.max(max, stringWidth(value)), 0);\n}\n\nfunction padDividerCell(valueWidth: number, dividerChar: string): string {\n return dividerChar.repeat(valueWidth + 2);\n}\n\nfunction padTextCell(value: string, valueWidth: number): string {\n return ` ${padVisible(value, valueWidth)} `;\n}\n\nfunction padOpsCell(value: string, valueWidth: number): string {\n const padding = Math.max(0, valueWidth - stringWidth(value));\n return ` ${' '.repeat(padding)}${value} `;\n}\n\nexport function renderMigrationLogTable(\n entries: readonly LedgerEntryRecord[],\n options: RenderMigrationLogTableOptions = {},\n): string {\n const sorted = sortLedgerEntries(entries);\n if (sorted.length === 0) {\n return '';\n }\n\n const styler = options.styler ?? IDENTITY_MIGRATION_LIST_STYLER;\n const glyphMode = options.glyphMode ?? 'unicode';\n const dividerChar = glyphMode === 'ascii' ? ASCII_DIVIDER_CHAR : DIVIDER_CHAR;\n const showSpace = new Set(sorted.map((entry) => entry.space)).size > 1;\n const timestampMode: LedgerTimestampMode = options.utc ? 'utc' : 'local';\n const rows = sorted.map((entry) => ({\n appliedAt: formatLedgerAppliedAt(entry.appliedAt, timestampMode),\n space: entry.space,\n migrationName: entry.migrationName,\n transition: formatHashTransition(entry.from, entry.to, glyphMode),\n ops: `${entry.operationCount} ops`,\n from: entry.from,\n to: entry.to,\n }));\n\n const appliedAtWidth = columnWidth([HEADING_APPLIED_AT, ...rows.map((row) => row.appliedAt)]);\n const spaceWidth = showSpace ? columnWidth([HEADING_SPACE, ...rows.map((row) => row.space)]) : 0;\n const nameWidth = columnWidth([HEADING_MIGRATION, ...rows.map((row) => row.migrationName)]);\n const transitionWidth = columnWidth([HEADING_CHANGE, ...rows.map((row) => row.transition)]);\n const opsWidth = columnWidth([HEADING_OPS, ...rows.map((row) => row.ops)]);\n\n const headingParts = [padTextCell(HEADING_APPLIED_AT, appliedAtWidth)];\n if (showSpace) {\n headingParts.push(padTextCell(HEADING_SPACE, spaceWidth));\n }\n headingParts.push(\n padTextCell(HEADING_MIGRATION, nameWidth),\n padTextCell(HEADING_CHANGE, transitionWidth),\n padOpsCell(HEADING_OPS, opsWidth),\n );\n const heading = headingParts.join(COLUMN_SEPARATOR);\n\n const dividerParts = [padDividerCell(appliedAtWidth, dividerChar)];\n if (showSpace) {\n dividerParts.push(padDividerCell(spaceWidth, dividerChar));\n }\n dividerParts.push(\n padDividerCell(nameWidth, dividerChar),\n padDividerCell(transitionWidth, dividerChar),\n padDividerCell(opsWidth, dividerChar),\n );\n const divider = dividerParts.map((cell) => styler.summary(cell)).join(COLUMN_SEPARATOR);\n\n const dataRows = rows.map((row) => {\n const parts = [padTextCell(row.appliedAt, appliedAtWidth)];\n if (showSpace) {\n parts.push(padTextCell(row.space, spaceWidth));\n }\n parts.push(\n padTextCell(styler.dirName(row.migrationName), nameWidth),\n padTextCell(styleHashTransition(row.from, row.to, styler, glyphMode), transitionWidth),\n padOpsCell(row.ops, opsWidth),\n );\n return parts.join(COLUMN_SEPARATOR);\n });\n\n return [heading, divider, ...dataRows].join('\\n');\n}\n\nexport function serializeLedgerEntriesForJson(\n entries: readonly LedgerEntryRecord[],\n): SerializedLedgerEntryRecord[] {\n return sortLedgerEntries(entries).map((entry) => ({\n space: entry.space,\n name: entry.migrationName,\n hash: entry.migrationHash,\n fromContract: entry.from,\n toContract: entry.to,\n appliedAt: formatLedgerAppliedAt(entry.appliedAt, 'iso'),\n operationCount: entry.operationCount,\n }));\n}\n\nexport const MIGRATION_LOG_EMPTY_MESSAGE = 'No migrations have been applied to this database.';\n","import { loadConfig } from '@prisma-next/config-loader';\nimport type { LedgerEntryRecord } from '@prisma-next/contract/types';\nimport { MigrationToolsError } from '@prisma-next/migration-tools/errors';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport { createControlClient } from '../control-api/client';\nimport {\n CliStructuredError,\n errorUnexpected,\n mapMigrationToolsError,\n requireLiveDatabase,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n maskConnectionUrl,\n resolveMigrationPaths,\n setCommandDescriptions,\n setCommandExamples,\n setCommandSeeAlso,\n targetSupportsMigrations,\n} from '../utils/command-helpers';\nimport { createAnsiMigrationListStyler } from '../utils/formatters/migration-list-styler';\nimport {\n MIGRATION_LOG_EMPTY_MESSAGE,\n renderMigrationLogTable,\n serializeLedgerEntriesForJson,\n} from '../utils/formatters/migration-log-table';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';\nimport { handleResult } from '../utils/result-handler';\nimport { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';\nimport type { MigrationLogResult } from './json/schemas';\n\nexport type { MigrationLogResult };\n\ninterface MigrationLogOptions extends CommonCommandOptions {\n readonly db?: string;\n readonly config?: string;\n readonly utc?: boolean;\n readonly ascii?: boolean;\n}\n\nexport async function executeMigrationLogCommand(\n options: MigrationLogOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n): Promise<Result<readonly LedgerEntryRecord[], CliStructuredError>> {\n const config = await loadConfig(options.config);\n const { configPath } = resolveMigrationPaths(options.config, config);\n\n const dbConnection = options.db ?? config.db?.connection;\n const missingDb = requireLiveDatabase({\n dbConnection,\n hasDriver: !!config.driver,\n why: `migration log needs a database connection and driver to read the ledger (set db.connection in ${configPath}, or pass --db <url>)`,\n commandName: 'migration log',\n });\n if (missingDb) {\n return notOk(missingDb);\n }\n if (!targetSupportsMigrations(config.target)) {\n return notOk(errorUnexpected('Target does not support migrations'));\n }\n\n if (!flags.json && !flags.quiet) {\n const header = formatStyledHeader({\n command: 'migration log',\n description: 'Show executed migration history from the database ledger',\n details: [\n { label: 'config', value: configPath },\n ...(typeof dbConnection === 'string'\n ? [{ label: 'database', value: maskConnectionUrl(dbConnection) }]\n : []),\n ],\n flags,\n });\n ui.stderr(header);\n }\n\n const client = createControlClient({\n family: config.family,\n target: config.target,\n adapter: config.adapter,\n ...ifDefined('driver', config.driver),\n extensionPacks: config.extensionPacks ?? [],\n });\n\n try {\n await client.connect(dbConnection);\n const ledger = await client.readLedger();\n return ok(ledger);\n } catch (error) {\n if (CliStructuredError.is(error)) return notOk(error);\n if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to read migration log: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n } finally {\n await client.close();\n }\n}\n\nexport function createMigrationLogCommand(): Command {\n const command = new Command('log');\n setCommandDescriptions(\n command,\n 'Show executed migration history',\n 'Reads the database ledger and displays every applied migration edge\\n' +\n 'in chronological order, including rollbacks and re-applies, merged\\n' +\n 'across all contract spaces. Requires a database connection.',\n );\n setCommandExamples(command, [\n 'prisma-next migration log --db $DATABASE_URL',\n 'prisma-next migration log --utc --db $DATABASE_URL',\n 'prisma-next migration log --json --db $DATABASE_URL',\n ]);\n setCommandSeeAlso(command, [\n { verb: 'migration status', oneLiner: 'Show migration path and pending status' },\n { verb: 'migration list', oneLiner: 'List on-disk migrations' },\n { verb: 'migration graph', oneLiner: 'Show the migration graph topology' },\n { verb: 'migration show', oneLiner: 'Display migration package contents' },\n ]);\n addGlobalOptions(command)\n .option('--db <url>', 'Database connection string')\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .option('--utc', 'Render human timestamps in UTC instead of local time')\n .option('--ascii', 'Use ASCII glyphs (pipe-friendly)')\n .action(async (options: MigrationLogOptions) => {\n const flags = parseGlobalFlagsOrExit(options);\n const ui = createTerminalUI(flags);\n const result = await executeMigrationLogCommand(options, flags, ui);\n const exitCode = handleResult(result, flags, ui, (entries) => {\n if (flags.json) {\n const records = serializeLedgerEntriesForJson(entries);\n const result: MigrationLogResult = {\n ok: true,\n records,\n summary: `${records.length} migration(s) applied`,\n };\n ui.output(JSON.stringify(result, null, 2));\n } else if (!flags.quiet) {\n if (entries.length === 0) {\n ui.output(MIGRATION_LOG_EMPTY_MESSAGE);\n } else {\n const styler = createAnsiMigrationListStyler({ useColor: ui.useColor });\n ui.output(\n renderMigrationLogTable(entries, {\n utc: options.utc === true,\n styler,\n glyphMode: ui.resolveGlyphMode(options.ascii === true),\n }),\n );\n }\n }\n });\n process.exit(exitCode);\n });\n return command;\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { rt as errorRuntime } from "./command-helpers-DGMvGBeX.mjs";
1
+ import { rt as errorRuntime } from "./command-helpers-6cNJZ863.mjs";
2
2
  import { notOk, ok } from "@prisma-next/utils/result";
3
3
  import { isAbsolute, relative, resolve } from "pathe";
4
4
  //#region src/utils/migration-path-target.ts
@@ -35,4 +35,4 @@ function findPackageByDirPath(packages, resolvedDirPath) {
35
35
  //#endregion
36
36
  export { resolveTargetPathAcrossSpaces as i, looksLikePath as n, resolveAppTargetPath as r, findPackageByDirPath as t };
37
37
 
38
- //# sourceMappingURL=migration-path-target-Ce6OZImp.mjs.map
38
+ //# sourceMappingURL=migration-path-target-zK6RG6JY.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"migration-path-target-Ce6OZImp.mjs","names":[],"sources":["../src/utils/migration-path-target.ts"],"sourcesContent":["import type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { isAbsolute, relative, resolve } from 'pathe';\nimport { type CliStructuredError, errorRuntime } from './cli-errors';\n\nexport function looksLikePath(target: string): boolean {\n return target.includes('/') || target.includes('\\\\');\n}\n\nexport function resolveAppTargetPath(\n target: string,\n appMigrationsDir: string,\n appMigrationsRelative: string,\n): Result<string, CliStructuredError> {\n const targetPath = resolve(target);\n const relativeToApp = relative(appMigrationsDir, targetPath);\n const isOutsideAppDir =\n relativeToApp === '' ||\n relativeToApp === '.' ||\n relativeToApp.startsWith('..') ||\n isAbsolute(relativeToApp);\n if (isOutsideAppDir) {\n return notOk(\n errorRuntime('Target must point to an app-space migration', {\n why: `Expected a path under ${appMigrationsRelative}, got ${target}`,\n fix: 'Pass an app-space migration directory or use a hash prefix.',\n }),\n );\n }\n return ok(targetPath);\n}\n\n/**\n * Resolve a filesystem-path target to the migration dir that contains it,\n * searching each in-scope space's `migrationsDir`. A path is explicit, so\n * it can belong to at most one space — returns the first match, or `null`\n * when the path falls outside every space dir.\n */\nexport function resolveTargetPathAcrossSpaces(\n target: string,\n spaces: ReadonlyArray<{ readonly migrationsDir: string }>,\n): string | null {\n const targetPath = resolve(target);\n for (const space of spaces) {\n const rel = relative(space.migrationsDir, targetPath);\n const isOutside = rel === '' || rel === '.' || rel.startsWith('..') || isAbsolute(rel);\n if (!isOutside) {\n return targetPath;\n }\n }\n return null;\n}\n\nexport function findPackageByDirPath(\n packages: readonly OnDiskMigrationPackage[],\n resolvedDirPath: string,\n): OnDiskMigrationPackage | undefined {\n const normalized = resolve(resolvedDirPath);\n return packages.find((p) => resolve(p.dirPath) === normalized);\n}\n"],"mappings":";;;;AAKA,SAAgB,cAAc,QAAyB;CACrD,OAAO,OAAO,SAAS,GAAG,KAAK,OAAO,SAAS,IAAI;AACrD;AAEA,SAAgB,qBACd,QACA,kBACA,uBACoC;CACpC,MAAM,aAAa,QAAQ,MAAM;CACjC,MAAM,gBAAgB,SAAS,kBAAkB,UAAU;CAM3D,IAJE,kBAAkB,MAClB,kBAAkB,OAClB,cAAc,WAAW,IAAI,KAC7B,WAAW,aAAa,GAExB,OAAO,MACL,aAAa,+CAA+C;EAC1D,KAAK,yBAAyB,sBAAsB,QAAQ;EAC5D,KAAK;CACP,CAAC,CACH;CAEF,OAAO,GAAG,UAAU;AACtB;;;;;;;AAQA,SAAgB,8BACd,QACA,QACe;CACf,MAAM,aAAa,QAAQ,MAAM;CACjC,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,MAAM,SAAS,MAAM,eAAe,UAAU;EAEpD,IAAI,EADc,QAAQ,MAAM,QAAQ,OAAO,IAAI,WAAW,IAAI,KAAK,WAAW,GAAG,IAEnF,OAAO;CAEX;CACA,OAAO;AACT;AAEA,SAAgB,qBACd,UACA,iBACoC;CACpC,MAAM,aAAa,QAAQ,eAAe;CAC1C,OAAO,SAAS,MAAM,MAAM,QAAQ,EAAE,OAAO,MAAM,UAAU;AAC/D"}
1
+ {"version":3,"file":"migration-path-target-zK6RG6JY.mjs","names":[],"sources":["../src/utils/migration-path-target.ts"],"sourcesContent":["import type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { isAbsolute, relative, resolve } from 'pathe';\nimport { type CliStructuredError, errorRuntime } from './cli-errors';\n\nexport function looksLikePath(target: string): boolean {\n return target.includes('/') || target.includes('\\\\');\n}\n\nexport function resolveAppTargetPath(\n target: string,\n appMigrationsDir: string,\n appMigrationsRelative: string,\n): Result<string, CliStructuredError> {\n const targetPath = resolve(target);\n const relativeToApp = relative(appMigrationsDir, targetPath);\n const isOutsideAppDir =\n relativeToApp === '' ||\n relativeToApp === '.' ||\n relativeToApp.startsWith('..') ||\n isAbsolute(relativeToApp);\n if (isOutsideAppDir) {\n return notOk(\n errorRuntime('Target must point to an app-space migration', {\n why: `Expected a path under ${appMigrationsRelative}, got ${target}`,\n fix: 'Pass an app-space migration directory or use a hash prefix.',\n }),\n );\n }\n return ok(targetPath);\n}\n\n/**\n * Resolve a filesystem-path target to the migration dir that contains it,\n * searching each in-scope space's `migrationsDir`. A path is explicit, so\n * it can belong to at most one space — returns the first match, or `null`\n * when the path falls outside every space dir.\n */\nexport function resolveTargetPathAcrossSpaces(\n target: string,\n spaces: ReadonlyArray<{ readonly migrationsDir: string }>,\n): string | null {\n const targetPath = resolve(target);\n for (const space of spaces) {\n const rel = relative(space.migrationsDir, targetPath);\n const isOutside = rel === '' || rel === '.' || rel.startsWith('..') || isAbsolute(rel);\n if (!isOutside) {\n return targetPath;\n }\n }\n return null;\n}\n\nexport function findPackageByDirPath(\n packages: readonly OnDiskMigrationPackage[],\n resolvedDirPath: string,\n): OnDiskMigrationPackage | undefined {\n const normalized = resolve(resolvedDirPath);\n return packages.find((p) => resolve(p.dirPath) === normalized);\n}\n"],"mappings":";;;;AAKA,SAAgB,cAAc,QAAyB;CACrD,OAAO,OAAO,SAAS,GAAG,KAAK,OAAO,SAAS,IAAI;AACrD;AAEA,SAAgB,qBACd,QACA,kBACA,uBACoC;CACpC,MAAM,aAAa,QAAQ,MAAM;CACjC,MAAM,gBAAgB,SAAS,kBAAkB,UAAU;CAM3D,IAJE,kBAAkB,MAClB,kBAAkB,OAClB,cAAc,WAAW,IAAI,KAC7B,WAAW,aAAa,GAExB,OAAO,MACL,aAAa,+CAA+C;EAC1D,KAAK,yBAAyB,sBAAsB,QAAQ;EAC5D,KAAK;CACP,CAAC,CACH;CAEF,OAAO,GAAG,UAAU;AACtB;;;;;;;AAQA,SAAgB,8BACd,QACA,QACe;CACf,MAAM,aAAa,QAAQ,MAAM;CACjC,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,MAAM,SAAS,MAAM,eAAe,UAAU;EAEpD,IAAI,EADc,QAAQ,MAAM,QAAQ,OAAO,IAAI,WAAW,IAAI,KAAK,WAAW,GAAG,IAEnF,OAAO;CAEX;CACA,OAAO;AACT;AAEA,SAAgB,qBACd,UACA,iBACoC;CACpC,MAAM,aAAa,QAAQ,eAAe;CAC1C,OAAO,SAAS,MAAM,MAAM,QAAQ,EAAE,OAAO,MAAM,UAAU;AAC/D"}
@@ -1,10 +1,10 @@
1
- import { t as loadConfig } from "./config-loader-p9JMrekQ.mjs";
2
- import { A as formatStyledHeader, B as errorContractValidationFailed, F as CliStructuredError, Q as errorPlanForgotTheFlag, W as errorFileNotFound, X as errorMigrationPlanningFailed, _ as createTerminalUI, ct as errorUnexpected, g as parseGlobalFlagsOrExit, it as errorSnapshotMissing, l as setCommandDescriptions, lt as mapMigrationToolsError, o as resolveContractPath, ot as errorTargetMigrationNotSupported, r as getTargetMigrations, s as resolveMigrationPaths, t as addGlobalOptions, u as setCommandExamples, ut as mapRefResolutionError, y as handleResult } from "./command-helpers-DGMvGBeX.mjs";
1
+ import { A as formatStyledHeader, B as errorContractValidationFailed, F as CliStructuredError, Q as errorPlanForgotTheFlag, W as errorFileNotFound, X as errorMigrationPlanningFailed, _ as createTerminalUI, ct as errorUnexpected, g as parseGlobalFlagsOrExit, it as errorSnapshotMissing, l as setCommandDescriptions, lt as mapMigrationToolsError, o as resolveContractPath, ot as errorTargetMigrationNotSupported, r as getTargetMigrations, s as resolveMigrationPaths, t as addGlobalOptions, u as setCommandExamples, ut as mapRefResolutionError, y as handleResult } from "./command-helpers-6cNJZ863.mjs";
3
2
  import { n as toExtensionInputs } from "./extension-pack-inputs-1ySHqxKG.mjs";
4
- import { t as assertFrameworkComponentsCompatible } from "./framework-components-YVQHhPH7.mjs";
5
- import { i as loadContractSpaceAggregateForCli, t as buildContractSpaceAggregate } from "./contract-space-aggregate-loader-ClI1KN6d.mjs";
6
- import { t as mapContractAtError } from "./contract-at-errors-CFXsstzm.mjs";
3
+ import { t as assertFrameworkComponentsCompatible } from "./framework-components-5RljWT7Q.mjs";
4
+ import { i as loadContractSpaceAggregateForCli, t as buildContractSpaceAggregate } from "./contract-space-aggregate-loader-Bu2fjSWV.mjs";
5
+ import { t as mapContractAtError } from "./contract-at-errors-DdxWRjMc.mjs";
7
6
  import { Command } from "commander";
7
+ import { loadConfig } from "@prisma-next/config-loader";
8
8
  import { getEmittedArtifactPaths } from "@prisma-next/emitter";
9
9
  import { notOk, ok } from "@prisma-next/utils/result";
10
10
  import { join, relative } from "pathe";
@@ -766,4 +766,4 @@ function resolveBundleByPrefix(bundles, needle) {
766
766
  //#endregion
767
767
  export { formatMigrationPlanOutput as n, resolveBundleByPrefix as r, createMigrationPlanCommand as t };
768
768
 
769
- //# sourceMappingURL=migration-plan-W_E8FQOk.mjs.map
769
+ //# sourceMappingURL=migration-plan-o2aqjnfd.mjs.map