prisma-next 0.5.0-dev.8 → 0.5.0-dev.80

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 (142) hide show
  1. package/dist/cli-errors-B9OBbled.d.mts +3 -0
  2. package/dist/cli-errors-D3_sMh2K.mjs +33 -0
  3. package/dist/cli-errors-D3_sMh2K.mjs.map +1 -0
  4. package/dist/cli.mjs +16 -78
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/client-qVH-rEgd.mjs +1595 -0
  7. package/dist/client-qVH-rEgd.mjs.map +1 -0
  8. package/dist/{result-handler-Ba3zWQsI.mjs → command-helpers-BeZHkxV8.mjs} +70 -47
  9. package/dist/command-helpers-BeZHkxV8.mjs.map +1 -0
  10. package/dist/commands/contract-emit.d.mts.map +1 -1
  11. package/dist/commands/contract-emit.mjs +2 -4
  12. package/dist/commands/contract-infer.d.mts.map +1 -1
  13. package/dist/commands/contract-infer.mjs +2 -4
  14. package/dist/commands/db-init.d.mts.map +1 -1
  15. package/dist/commands/db-init.mjs +16 -13
  16. package/dist/commands/db-init.mjs.map +1 -1
  17. package/dist/commands/db-schema.d.mts.map +1 -1
  18. package/dist/commands/db-schema.mjs +6 -7
  19. package/dist/commands/db-schema.mjs.map +1 -1
  20. package/dist/commands/db-sign.d.mts.map +1 -1
  21. package/dist/commands/db-sign.mjs +9 -9
  22. package/dist/commands/db-sign.mjs.map +1 -1
  23. package/dist/commands/db-update.d.mts.map +1 -1
  24. package/dist/commands/db-update.mjs +15 -13
  25. package/dist/commands/db-update.mjs.map +1 -1
  26. package/dist/commands/db-verify.d.mts.map +1 -1
  27. package/dist/commands/db-verify.mjs +1 -321
  28. package/dist/commands/migration-apply.d.mts +28 -13
  29. package/dist/commands/migration-apply.d.mts.map +1 -1
  30. package/dist/commands/migration-apply.mjs +55 -151
  31. package/dist/commands/migration-apply.mjs.map +1 -1
  32. package/dist/commands/migration-new.d.mts +0 -1
  33. package/dist/commands/migration-new.d.mts.map +1 -1
  34. package/dist/commands/migration-new.mjs +34 -40
  35. package/dist/commands/migration-new.mjs.map +1 -1
  36. package/dist/commands/migration-plan.d.mts +33 -6
  37. package/dist/commands/migration-plan.d.mts.map +1 -1
  38. package/dist/commands/migration-plan.mjs +2 -348
  39. package/dist/commands/migration-ref.d.mts +1 -1
  40. package/dist/commands/migration-ref.d.mts.map +1 -1
  41. package/dist/commands/migration-ref.mjs +8 -12
  42. package/dist/commands/migration-ref.mjs.map +1 -1
  43. package/dist/commands/migration-show.d.mts +13 -7
  44. package/dist/commands/migration-show.d.mts.map +1 -1
  45. package/dist/commands/migration-show.mjs +35 -36
  46. package/dist/commands/migration-show.mjs.map +1 -1
  47. package/dist/commands/migration-status.d.mts +126 -5
  48. package/dist/commands/migration-status.d.mts.map +1 -1
  49. package/dist/commands/migration-status.mjs +2 -4
  50. package/dist/{config-loader-C25b63rJ.mjs → config-loader-B6sJjXTv.mjs} +3 -5
  51. package/dist/config-loader-B6sJjXTv.mjs.map +1 -0
  52. package/dist/config-loader.d.mts +0 -1
  53. package/dist/config-loader.d.mts.map +1 -1
  54. package/dist/config-loader.mjs +2 -3
  55. package/dist/contract-emit-9DBda5Ou.mjs +150 -0
  56. package/dist/contract-emit-9DBda5Ou.mjs.map +1 -0
  57. package/dist/contract-emit-B77TsJqf.mjs +327 -0
  58. package/dist/contract-emit-B77TsJqf.mjs.map +1 -0
  59. package/dist/{contract-enrichment-CAOELa-H.mjs → contract-enrichment-Dani0mMW.mjs} +4 -6
  60. package/dist/contract-enrichment-Dani0mMW.mjs.map +1 -0
  61. package/dist/{contract-infer-D9cC3rJm.mjs → contract-infer-BK9YFGEG.mjs} +13 -22
  62. package/dist/contract-infer-BK9YFGEG.mjs.map +1 -0
  63. package/dist/db-verify-C0y1PCO2.mjs +404 -0
  64. package/dist/db-verify-C0y1PCO2.mjs.map +1 -0
  65. package/dist/exports/config-types.mjs +1 -2
  66. package/dist/exports/control-api.d.mts +101 -586
  67. package/dist/exports/control-api.d.mts.map +1 -1
  68. package/dist/exports/control-api.mjs +4 -6
  69. package/dist/exports/index.d.mts.map +1 -1
  70. package/dist/exports/index.mjs +28 -30
  71. package/dist/exports/index.mjs.map +1 -1
  72. package/dist/exports/init-output.d.mts +2 -4
  73. package/dist/exports/init-output.d.mts.map +1 -1
  74. package/dist/exports/init-output.mjs +2 -3
  75. package/dist/extension-pack-inputs-C7xgE-vv.mjs +74 -0
  76. package/dist/extension-pack-inputs-C7xgE-vv.mjs.map +1 -0
  77. package/dist/{framework-components-Cr--XBKy.mjs → framework-components-ChqVUxR-.mjs} +3 -4
  78. package/dist/{framework-components-Cr--XBKy.mjs.map → framework-components-ChqVUxR-.mjs.map} +1 -1
  79. package/dist/global-flags-Icqpxk23.d.mts +12 -0
  80. package/dist/global-flags-Icqpxk23.d.mts.map +1 -0
  81. package/dist/helpers-eqdN8tH6.mjs +25 -0
  82. package/dist/helpers-eqdN8tH6.mjs.map +1 -0
  83. package/dist/{init-C5220SY9.mjs → init-CoDVPvQ4.mjs} +26 -35
  84. package/dist/init-CoDVPvQ4.mjs.map +1 -0
  85. package/dist/{inspect-live-schema-yrHAvG71.mjs → inspect-live-schema-CWYxGKlb.mjs} +10 -11
  86. package/dist/inspect-live-schema-CWYxGKlb.mjs.map +1 -0
  87. package/dist/migration-cli.d.mts +41 -12
  88. package/dist/migration-cli.d.mts.map +1 -1
  89. package/dist/migration-cli.mjs +309 -86
  90. package/dist/migration-cli.mjs.map +1 -1
  91. package/dist/{migration-command-scaffold-B3B09et6.mjs → migration-command-scaffold-B5dORFEv.mjs} +8 -9
  92. package/dist/migration-command-scaffold-B5dORFEv.mjs.map +1 -0
  93. package/dist/migration-plan-C6lVaHsO.mjs +554 -0
  94. package/dist/migration-plan-C6lVaHsO.mjs.map +1 -0
  95. package/dist/{migration-status-DUMiH8_G.mjs → migration-status-CZ-D5k7k.mjs} +272 -65
  96. package/dist/migration-status-CZ-D5k7k.mjs.map +1 -0
  97. package/dist/migrations-D_UJnpuW.mjs +216 -0
  98. package/dist/migrations-D_UJnpuW.mjs.map +1 -0
  99. package/dist/{output-BpcQrnnq.mjs → output-B16Kefzx.mjs} +9 -3
  100. package/dist/output-B16Kefzx.mjs.map +1 -0
  101. package/dist/{progress-adapter-DvQWB1nK.mjs → progress-adapter-DFfvZcYL.mjs} +2 -2
  102. package/dist/{progress-adapter-DvQWB1nK.mjs.map → progress-adapter-DFfvZcYL.mjs.map} +1 -1
  103. package/dist/result-handler-rmPVKIP2.mjs +25 -0
  104. package/dist/result-handler-rmPVKIP2.mjs.map +1 -0
  105. package/dist/rolldown-runtime-twds-ZHy.mjs +14 -0
  106. package/dist/{terminal-ui-C3ZLwQxK.mjs → terminal-ui-C_hFNbAn.mjs} +4 -28
  107. package/dist/terminal-ui-C_hFNbAn.mjs.map +1 -0
  108. package/dist/types-D7x-IFLO.d.mts +858 -0
  109. package/dist/types-D7x-IFLO.d.mts.map +1 -0
  110. package/dist/{verify-Bkycc-Tf.mjs → verify-CiwNWM9N.mjs} +3 -4
  111. package/dist/verify-CiwNWM9N.mjs.map +1 -0
  112. package/package.json +19 -17
  113. package/dist/cli-errors-BFYgBH3L.d.mts +0 -4
  114. package/dist/cli-errors-Cd79vmTH.mjs +0 -5
  115. package/dist/client-CrsnY58k.mjs +0 -997
  116. package/dist/client-CrsnY58k.mjs.map +0 -1
  117. package/dist/commands/db-verify.mjs.map +0 -1
  118. package/dist/commands/migration-plan.mjs.map +0 -1
  119. package/dist/config-loader-C25b63rJ.mjs.map +0 -1
  120. package/dist/contract-emit--feXyNd7.mjs +0 -4
  121. package/dist/contract-emit-NJ01hiiv.mjs +0 -195
  122. package/dist/contract-emit-NJ01hiiv.mjs.map +0 -1
  123. package/dist/contract-emit-V5SSitUT.mjs +0 -122
  124. package/dist/contract-emit-V5SSitUT.mjs.map +0 -1
  125. package/dist/contract-enrichment-CAOELa-H.mjs.map +0 -1
  126. package/dist/contract-infer-D9cC3rJm.mjs.map +0 -1
  127. package/dist/extract-operation-statements-DsFfxXVZ.mjs +0 -13
  128. package/dist/extract-operation-statements-DsFfxXVZ.mjs.map +0 -1
  129. package/dist/extract-sql-ddl-D9UbZDyz.mjs +0 -26
  130. package/dist/extract-sql-ddl-D9UbZDyz.mjs.map +0 -1
  131. package/dist/init-C5220SY9.mjs.map +0 -1
  132. package/dist/inspect-live-schema-yrHAvG71.mjs.map +0 -1
  133. package/dist/migration-command-scaffold-B3B09et6.mjs.map +0 -1
  134. package/dist/migration-status-DUMiH8_G.mjs.map +0 -1
  135. package/dist/migrations-Bo5WtTla.mjs +0 -153
  136. package/dist/migrations-Bo5WtTla.mjs.map +0 -1
  137. package/dist/output-BpcQrnnq.mjs.map +0 -1
  138. package/dist/result-handler-Ba3zWQsI.mjs.map +0 -1
  139. package/dist/terminal-ui-C3ZLwQxK.mjs.map +0 -1
  140. package/dist/validate-contract-deps-B_Cs29TL.mjs +0 -37
  141. package/dist/validate-contract-deps-B_Cs29TL.mjs.map +0 -1
  142. package/dist/verify-Bkycc-Tf.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspect-live-schema-CWYxGKlb.mjs","names":[],"sources":["../src/commands/inspect-live-schema.ts"],"sourcesContent":["import 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 { loadConfig } from '../config-loader';\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 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\n const dbUrl = typeof dbConnection === 'string' ? maskConnectionUrl(dbConnection) : undefined;\n\n return ok({\n config,\n schema,\n schemaView,\n pslContractAst,\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":";;;;;;;;AAsDA,eAAsB,kBACpB,SACA,OACA,IACA,WACA,SAC8D;CAC9D,IAAI;CACJ,IAAI;EACF,SAAS,MAAM,WAAW,QAAQ,OAAO;UAClC,OAAO;EACd,IAAI,mBAAmB,GAAG,MAAM,EAC9B,OAAO,MAAM,MAAM;EAGrB,OAAO,MACL,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAAE,EACtE,KAAK,yBACN,CAAC,CACH;;CAGH,MAAM,aAAa,QAAQ,SACvB,SAAS,QAAQ,KAAK,EAAE,QAAQ,QAAQ,OAAO,CAAC,GAChD;CAEJ,IAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,OAAO;EAC/B,MAAM,UAAmD,CACvD;GAAE,OAAO;GAAU,OAAO;GAAY,CACvC;EAED,IAAI,QAAQ,IACV,QAAQ,KAAK;GAAE,OAAO;GAAY,OAAO,kBAAkB,QAAQ,GAAG;GAAE,CAAC;OACpE,IAAI,OAAO,IAAI,cAAc,OAAO,OAAO,GAAG,eAAe,UAClE,QAAQ,KAAK;GAAE,OAAO;GAAY,OAAO,kBAAkB,OAAO,GAAG,WAAW;GAAE,CAAC;EAGrF,GAAG,OACD,mBAAmB;GACjB,SAAS,QAAQ;GACjB,aAAa,QAAQ;GACrB,KAAK,QAAQ;GACb;GACA;GACD,CAAC,CACH;;CAGH,MAAM,eAAe,QAAQ,MAAM,OAAO,IAAI;CAC9C,IAAI,CAAC,cACH,OAAO,MACL,gCAAgC;EAC9B,KAAK,uCAAuC,QAAQ,YAAY,yBAAyB,WAAW;EACpG,aAAa,QAAQ;EACtB,CAAC,CACH;CAGH,IAAI,CAAC,OAAO,QACV,OAAO,MACL,oBAAoB,EAClB,KAAK,iCAAiC,QAAQ,eAC/C,CAAC,CACH;CAGH,MAAM,SAAS,oBAAoB;EACjC,QAAQ,OAAO;EACf,QAAQ,OAAO;EACf,SAAS,OAAO;EAChB,QAAQ,OAAO;EACf,gBAAgB,OAAO,kBAAkB,EAAE;EAC5C,CAAC;CACF,MAAM,aAAa,sBAAsB;EAAE;EAAI;EAAO,CAAC;CAEvD,IAAI;EACF,MAAM,SAAS,MAAM,OAAO,WAAW;GACrC,YAAY;GACZ;GACD,CAAC;EACF,MAAM,aAAa,OAAO,aAAa,OAAO;EAC9C,MAAM,iBAAiB,OAAO,iBAAiB,OAAO;EAEtD,MAAM,QAAQ,OAAO,iBAAiB,WAAW,kBAAkB,aAAa,GAAG,KAAA;EAEnF,OAAO,GAAG;GACR;GACA;GACA;GACA;GACA,QAAQ;IACN,UAAU,OAAO,OAAO;IACxB,IAAI,OAAO,OAAO;IACnB;GACD,MAAM;IACJ;IACA,GAAI,QAAQ,EAAE,OAAO,GAAG,EAAE;IAC3B;GACD,SAAS,EACP,OAAO,KAAK,KAAK,GAAG,WACrB;GACF,CAAC;UACK,OAAO;EACd,IAAI,mBAAmB,GAAG,MAAM,EAC9B,OAAO,MAAM,MAAM;EAIrB,MAAM,cAAc,qBADD,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAGvE,OAAO,iBAAiB,WAAW,eAAe,KAAA,EACnD;EACD,OAAO,MACL,gBAAgB,aAAa,EAC3B,KAAK,2BAA2B,QAAQ,YAAY,IAAI,eACzD,CAAC,CACH;WACO;EACR,MAAM,OAAO,OAAO"}
@@ -1,7 +1,7 @@
1
1
  import { Migration } from "@prisma-next/migration-tools/migration";
2
+ import { Writable } from "node:stream";
2
3
 
3
4
  //#region src/migration-cli.d.ts
4
-
5
5
  /**
6
6
  * Constructor shape accepted by `MigrationCLI.run`. `Migration` subclasses
7
7
  * accept an optional `ControlStack` in their constructor (each subclass
@@ -18,6 +18,20 @@ import { Migration } from "@prisma-next/migration-tools/migration";
18
18
  * point for additional construction arguments.
19
19
  */
20
20
  type MigrationConstructor = new (...args: any[]) => Migration;
21
+ /**
22
+ * Stream surface accepted by `MigrationCLI.run`'s `options.stdout` /
23
+ * `options.stderr`. Aliases node's `Writable` because clipanion's
24
+ * `BaseContext.stdout`/`stderr` are typed as `Writable`, and the CLI
25
+ * forwards the injected streams into clipanion's context.
26
+ *
27
+ * `process.stdout` and `process.stderr` are `Writable`-shaped, so the
28
+ * default-fallback path remains a no-op for existing two-argument
29
+ * callers like `MigrationCLI.run(import.meta.url, MyMigration)`.
30
+ *
31
+ * Tests inject a `Writable` subclass that captures chunks for
32
+ * assertions.
33
+ */
34
+ type MigrationCliWritable = Writable;
21
35
  /**
22
36
  * The CLI surface invoked by an authored `migration.ts` file. Exposed as
23
37
  * a class with a static `run` method (rather than a free function) to
@@ -32,19 +46,34 @@ type MigrationConstructor = new (...args: any[]) => Migration;
32
46
  */
33
47
  declare class MigrationCLI {
34
48
  /**
35
- * Orchestrates a class-flow `migration.ts` script run. Awaitable:
36
- * callers may `await MigrationCLI.run(...)` to surface async failures
37
- * from config loading, but the typical usage pattern (top-level call
38
- * after the class definition) does not require awaiting because
39
- * node's module evaluation keeps the promise alive until completion.
49
+ * Orchestrates a class-flow `migration.ts` script run.
50
+ *
51
+ * The third argument is the in-process testability surface: callers
52
+ * (and tests) may inject `argv`, `stdout`, and `stderr` instead of
53
+ * relying on `process.argv` / `process.stdout` / `process.stderr`.
54
+ * Each option defaults to its `process` global when omitted, so
55
+ * existing two-argument call sites
56
+ * (`MigrationCLI.run(import.meta.url, MyMigration)`) continue to
57
+ * compile and behave identically.
58
+ *
59
+ * Returns the exit code so the caller can branch on it. Also writes
60
+ * the same code to `process.exitCode` so script-style callers that
61
+ * don't await the return value still surface a non-zero exit when
62
+ * something fails.
40
63
  *
41
- * Any throwable inside this function must surface through the internal
42
- * try/catchscript callers do not await, so an unhandled rejection
43
- * would silently exit 0. Treat the try/catch as load-bearing for the
44
- * no-await usage pattern.
64
+ * Exit codes:
65
+ * - 0 success, or `--help`, or imported-not-entrypoint no-op.
66
+ * - 1 runtime/orchestration error (config not found, target
67
+ * mismatch, etc.).
68
+ * - 2 — usage error (unknown flag, malformed `--config`). Aligns
69
+ * with `docs/CLI Style Guide.md` § Exit Codes.
45
70
  */
46
- static run(importMetaUrl: string, MigrationClass: MigrationConstructor): Promise<void>;
71
+ static run(importMetaUrl: string, MigrationClass: MigrationConstructor, options?: {
72
+ readonly argv?: readonly string[];
73
+ readonly stdout?: MigrationCliWritable;
74
+ readonly stderr?: MigrationCliWritable;
75
+ }): Promise<number>;
47
76
  }
48
77
  //#endregion
49
- export { MigrationCLI, MigrationConstructor };
78
+ export { MigrationCLI, MigrationCliWritable, MigrationConstructor };
50
79
  //# sourceMappingURL=migration-cli.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"migration-cli.d.mts","names":[],"sources":["../src/migration-cli.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;KAqEY,oBAAA,2BAA+C;;;;;;;;;;;;;cAiE9C,YAAA;;;;;;;;;;;;;oDAa6C,uBAAuB"}
1
+ {"version":3,"file":"migration-cli.d.mts","names":[],"sources":["../src/migration-cli.ts"],"mappings":";;;;;;;;;;;;;;;;;;;KA6EY,oBAAA,WAA+B,IAAA,YAAgB,SAAA;;;;;;;;;;;;;;KAe/C,oBAAA,GAAuB,QAAA;;;;;;;;;;;;;cAuEtB,YAAA;;;;;;;;;;;;;;;;;;;;;;;;SAwBE,GAAA,CACX,aAAA,UACA,cAAA,EAAgB,oBAAA,EAChB,OAAA;IAAA,SACW,IAAA;IAAA,SACA,MAAA,GAAS,oBAAA;IAAA,SACT,MAAA,GAAS,oBAAA;EAAA,IAEnB,OAAA;AAAA"}
@@ -1,12 +1,13 @@
1
- import { t as loadConfig } from "./config-loader-C25b63rJ.mjs";
2
- import { readFileSync, writeFileSync } from "node:fs";
3
- import { CliStructuredError, errorMigrationCliInvalidConfigArg } from "@prisma-next/errors/control";
1
+ import { t as loadConfig } from "./config-loader-B6sJjXTv.mjs";
2
+ import { CliStructuredError, errorMigrationCliInvalidConfigArg, errorMigrationCliUnknownFlag } from "@prisma-next/errors/control";
4
3
  import { dirname, join } from "pathe";
5
4
  import { createControlStack } from "@prisma-next/framework-components/control";
6
5
  import { errorMigrationTargetMismatch } from "@prisma-next/errors/migration";
6
+ import { readFileSync, realpathSync, writeFileSync } from "node:fs";
7
+ import { MigrationToolsError, errorInvalidJson } from "@prisma-next/migration-tools/errors";
7
8
  import { fileURLToPath } from "node:url";
8
- import { buildMigrationArtifacts, isDirectEntrypoint, printMigrationHelp } from "@prisma-next/migration-tools/migration";
9
-
9
+ import { buildMigrationArtifacts } from "@prisma-next/migration-tools/migration";
10
+ import { Cli, Command, Option, UsageError } from "clipanion";
10
11
  //#region src/migration-cli.ts
11
12
  /**
12
13
  * The migration-file CLI interface: the actor invoked when the author runs
@@ -25,7 +26,8 @@ import { buildMigrationArtifacts, isDirectEntrypoint, printMigrationHelp } from
25
26
  * entrypoint (`node migration.ts`), the CLI:
26
27
  *
27
28
  * 1. Detects whether the file is the direct entrypoint (no-op when imported).
28
- * 2. Parses CLI args (`--help`, `--dry-run`, `--config <path>`).
29
+ * 2. Parses CLI args (`--help`, `--dry-run`, `--config <path>`) via
30
+ * [clipanion](https://github.com/arcanis/clipanion).
29
31
  * 3. Loads the project's `prisma-next.config.ts` via the same `loadConfig`
30
32
  * the CLI commands use, walking up from the migration file's directory.
31
33
  * 4. Probe-instantiates the migration class without a stack so it can read
@@ -44,44 +46,62 @@ import { buildMigrationArtifacts, isDirectEntrypoint, printMigrationHelp } from
44
46
  * on-disk persistence. `@prisma-next/migration-tools` owns the pure
45
47
  * conversion from a `Migration` instance to artifact strings; `Migration`
46
48
  * stays a pure abstract class.
49
+ *
50
+ * Parser library: clipanion (chosen over Commander/citty/`node:util.parseArgs`
51
+ * for its in-process testability and runtime-agnostic execution surface; see
52
+ * `docs/architecture docs/research/commander-friction-points.md` for the
53
+ * evaluation rubric and the durable rationale that drove the choice).
47
54
  */
48
55
  /**
49
- * Parse the subset of `process.argv` that `MigrationCLI.run` cares about.
50
- * Recognised flags: `--help`, `--dry-run`, `--config <path>` /
51
- * `--config=<path>`. Unknown flags are ignored to keep the surface
52
- * forgiving for ad-hoc tooling that wraps a migration file.
56
+ * Flags exposed by the migration-file CLI.
53
57
  *
54
- * Throws `errorMigrationCliInvalidConfigArg` (`PN-CLI-4012`) when
55
- * `--config` is missing its path argument or is followed by another flag
56
- * (e.g. `--config --dry-run`); silently consuming the next flag would
57
- * either drop dry-run handling or serialize against the wrong project.
58
- *
59
- * NOTE: this hand-rolled parser is a known wart, tracked separately by
60
- * TML-2318 ("Migration CLI: replace handrolled arg parser with shared
61
- * CLI library"). Until that lands the surface is intentionally tiny.
58
+ * Must stay in sync with the `Option` declarations on
59
+ * `MigrationFileCommand` below. This list is rendered in the
60
+ * `errorMigrationCliUnknownFlag` envelope's `fix` text and `meta`,
61
+ * so order matters for user-visible output (declaration order is the
62
+ * order users see when they run `--help`).
63
+ */
64
+ const KNOWN_FLAGS = [
65
+ "--help",
66
+ "--dry-run",
67
+ "--config"
68
+ ];
69
+ /**
70
+ * The clipanion command that owns the migration-file CLI's option
71
+ * declarations. The class is internal — `MigrationCLI.run` is the
72
+ * stable public surface. Adding a flag here automatically updates
73
+ * `--help` rendering and the `KNOWN_FLAGS` list (the latter must be
74
+ * updated in tandem).
62
75
  */
63
- function parseArgs(argv) {
64
- let help = false;
65
- let dryRun = false;
66
- let configPath;
67
- for (let i = 0; i < argv.length; i++) {
68
- const arg = argv[i];
69
- if (arg === "--help" || arg === "-h") help = true;
70
- else if (arg === "--dry-run") dryRun = true;
71
- else if (arg === "--config") {
72
- const next = argv[i + 1];
73
- if (next === void 0) throw errorMigrationCliInvalidConfigArg();
74
- if (next.startsWith("-")) throw errorMigrationCliInvalidConfigArg({ nextToken: next });
75
- configPath = next;
76
- i++;
77
- } else if (arg.startsWith("--config=")) configPath = arg.slice(9);
76
+ var MigrationFileCommand = class extends Command {
77
+ static paths = [Command.Default];
78
+ static usage = Command.Usage({
79
+ description: "Self-emit ops.json and migration.json from a class-flow migration",
80
+ details: `
81
+ Loads the project's prisma-next.config.ts, assembles a ControlStack
82
+ from the configured target/adapter/extensions, and serializes the
83
+ migration's operations + metadata next to this file.
84
+ `,
85
+ examples: [
86
+ ["Self-emit ops.json + migration.json next to migration.ts", "$0"],
87
+ ["Preview without writing files", "$0 --dry-run"],
88
+ ["Use a non-default config path", "$0 --config ./custom.config.ts"]
89
+ ]
90
+ });
91
+ dryRun = Option.Boolean("--dry-run", false, { description: "Print operations to stdout without writing files" });
92
+ config = Option.String("--config", { description: "Path to prisma-next.config.ts" });
93
+ /**
94
+ * Unused: orchestration runs inside `MigrationCLI.run` so error
95
+ * routing stays under our control (clipanion's `cli.run` writes
96
+ * error output to `context.stdout`, but our contract requires
97
+ * structured errors on stderr). `cli.process` is used to parse
98
+ * argv into a populated `MigrationFileCommand` instance whose
99
+ * fields drive the orchestration directly.
100
+ */
101
+ async execute() {
102
+ return 0;
78
103
  }
79
- return {
80
- help,
81
- dryRun,
82
- configPath
83
- };
84
- }
104
+ };
85
105
  /**
86
106
  * The CLI surface invoked by an authored `migration.ts` file. Exposed as
87
107
  * a class with a static `run` method (rather than a free function) to
@@ -96,59 +116,243 @@ function parseArgs(argv) {
96
116
  */
97
117
  var MigrationCLI = class {
98
118
  /**
99
- * Orchestrates a class-flow `migration.ts` script run. Awaitable:
100
- * callers may `await MigrationCLI.run(...)` to surface async failures
101
- * from config loading, but the typical usage pattern (top-level call
102
- * after the class definition) does not require awaiting because
103
- * node's module evaluation keeps the promise alive until completion.
119
+ * Orchestrates a class-flow `migration.ts` script run.
120
+ *
121
+ * The third argument is the in-process testability surface: callers
122
+ * (and tests) may inject `argv`, `stdout`, and `stderr` instead of
123
+ * relying on `process.argv` / `process.stdout` / `process.stderr`.
124
+ * Each option defaults to its `process` global when omitted, so
125
+ * existing two-argument call sites
126
+ * (`MigrationCLI.run(import.meta.url, MyMigration)`) continue to
127
+ * compile and behave identically.
104
128
  *
105
- * Any throwable inside this function must surface through the internal
106
- * try/catch script callers do not await, so an unhandled rejection
107
- * would silently exit 0. Treat the try/catch as load-bearing for the
108
- * no-await usage pattern.
129
+ * Returns the exit code so the caller can branch on it. Also writes
130
+ * the same code to `process.exitCode` so script-style callers that
131
+ * don't await the return value still surface a non-zero exit when
132
+ * something fails.
133
+ *
134
+ * Exit codes:
135
+ * - 0 — success, or `--help`, or imported-not-entrypoint no-op.
136
+ * - 1 — runtime/orchestration error (config not found, target
137
+ * mismatch, etc.).
138
+ * - 2 — usage error (unknown flag, malformed `--config`). Aligns
139
+ * with `docs/CLI Style Guide.md` § Exit Codes.
109
140
  */
110
- static async run(importMetaUrl, MigrationClass) {
111
- if (!importMetaUrl) return;
112
- if (!isDirectEntrypoint(importMetaUrl)) return;
113
- try {
114
- const args = parseArgs(process.argv.slice(2));
115
- if (args.help) {
116
- printMigrationHelp();
117
- return;
141
+ static async run(importMetaUrl, MigrationClass, options = {}) {
142
+ if (!importMetaUrl) return 0;
143
+ const argv = options.argv ?? process.argv;
144
+ const stdout = options.stdout ?? process.stdout;
145
+ const stderr = options.stderr ?? process.stderr;
146
+ if (!isDirectEntrypoint(importMetaUrl, argv)) return 0;
147
+ const exitCode = await orchestrate(importMetaUrl, MigrationClass, {
148
+ argv,
149
+ stdout,
150
+ stderr
151
+ });
152
+ if (exitCode !== 0 || !process.exitCode) process.exitCode = exitCode;
153
+ return exitCode;
154
+ }
155
+ };
156
+ /**
157
+ * Argv-aware variant of the entrypoint guard. The shared
158
+ * `@prisma-next/migration-tools` helper of the same name reads
159
+ * `process.argv[1]` directly, which doesn't compose with the new
160
+ * in-process testability surface (tests inject `argv` without mutating
161
+ * the process global). Inlined here so the migration-file CLI's check
162
+ * follows the injected `argv[1]` consistently.
163
+ */
164
+ function isDirectEntrypoint(importMetaUrl, argv) {
165
+ const argv1 = argv[1];
166
+ if (!argv1) return false;
167
+ try {
168
+ return realpathSync(fileURLToPath(importMetaUrl)) === realpathSync(argv1);
169
+ } catch {
170
+ return false;
171
+ }
172
+ }
173
+ /**
174
+ * Argv-and-stream-driven orchestration body. Pulled out of the static
175
+ * method so the entrypoint guard / process-default plumbing stays
176
+ * separate from the parse + load + serialize steps.
177
+ */
178
+ async function orchestrate(importMetaUrl, MigrationClass, ctx) {
179
+ const cli = Cli.from([MigrationFileCommand], {
180
+ binaryName: "migration.ts",
181
+ binaryLabel: "Migration file CLI"
182
+ });
183
+ const input = ctx.argv.slice(2);
184
+ const configError = detectInvalidConfig(input);
185
+ if (configError) {
186
+ writeStructuredError(ctx.stderr, configError);
187
+ return 2;
188
+ }
189
+ let parsed;
190
+ try {
191
+ const command = cli.process({
192
+ input: [...input],
193
+ context: {
194
+ stdout: ctx.stdout,
195
+ stderr: ctx.stderr
118
196
  }
119
- const migrationDir = dirname(fileURLToPath(importMetaUrl));
120
- const config = await loadConfig(args.configPath);
121
- const probe = new MigrationClass();
122
- if (probe.targetId !== config.target.targetId) throw errorMigrationTargetMismatch({
123
- migrationTargetId: probe.targetId,
124
- configTargetId: config.target.targetId
125
- });
126
- serializeMigrationToDisk(new MigrationClass(createControlStack(config)), migrationDir, args.dryRun);
127
- } catch (err) {
128
- if (CliStructuredError.is(err)) process.stderr.write(`${err.message}: ${err.why}\n`);
129
- else process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
130
- process.exitCode = 1;
197
+ });
198
+ if (!(command instanceof MigrationFileCommand)) {
199
+ ctx.stdout.write(cli.usage(MigrationFileCommand, { detailed: true }));
200
+ return 0;
131
201
  }
202
+ parsed = command;
203
+ } catch (err) {
204
+ return renderParseError(err, input, ctx.stderr);
132
205
  }
133
- };
206
+ if (parsed.help) {
207
+ ctx.stdout.write(cli.usage(MigrationFileCommand, { detailed: true }));
208
+ return 0;
209
+ }
210
+ try {
211
+ await runMigration(importMetaUrl, MigrationClass, parsed, ctx);
212
+ return 0;
213
+ } catch (err) {
214
+ if (CliStructuredError.is(err)) writeStructuredError(ctx.stderr, err);
215
+ else if (MigrationToolsError.is(err)) {
216
+ const fix = err.fix ? `\n${err.fix}` : "";
217
+ ctx.stderr.write(`${err.code}: ${err.message}\n${err.why}${fix}\n`);
218
+ } else ctx.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
219
+ return 1;
220
+ }
221
+ }
222
+ /**
223
+ * Returns an `errorMigrationCliInvalidConfigArg` envelope when `input`
224
+ * contains a malformed `--config`:
225
+ *
226
+ * - `--config` as the last token (no value follows).
227
+ * - `--config <flag>` where `<flag>` starts with `-` (silently
228
+ * consuming the next flag would either drop the flag or serialize
229
+ * against the wrong project).
230
+ * - `--config <empty>` where the value is the empty string. Shells
231
+ * expand `--config ""` (or `--config "$UNSET_VAR"`) into a real
232
+ * empty argv token; treating that as a usage error here surfaces
233
+ * `PN-CLI-4012` instead of a less actionable loader error on an
234
+ * empty path.
235
+ * - `--config=` (the equals form with an empty value). Same shape as
236
+ * the empty-string case above; the user expressed intent to override
237
+ * the config path but the override is empty.
238
+ *
239
+ * `--config=<value>` and `--config <value>` with a non-empty value are
240
+ * both valid (and the equals form's value is allowed to start with
241
+ * `-` — the `=` makes the binding explicit).
242
+ */
243
+ function detectInvalidConfig(input) {
244
+ for (let i = 0; i < input.length; i++) {
245
+ const token = input[i];
246
+ if (token === "--config") {
247
+ const next = input[i + 1];
248
+ if (next === void 0 || next === "") return errorMigrationCliInvalidConfigArg();
249
+ if (next.startsWith("-")) return errorMigrationCliInvalidConfigArg({ nextToken: next });
250
+ continue;
251
+ }
252
+ if (token === "--config=") return errorMigrationCliInvalidConfigArg();
253
+ }
254
+ return null;
255
+ }
256
+ /**
257
+ * Translate clipanion's parse-time errors into the project's structured
258
+ * error envelopes.
259
+ *
260
+ * - `UnknownSyntaxError` covers both unknown flags (`--frobnicate`) and
261
+ * the bare-trailing `--config` case (where arity-1 needs a value but
262
+ * none was supplied). Distinguished by inspecting the input array.
263
+ * - `UsageError` covers schema/validator failures from typanion. None
264
+ * of the migration-file CLI's options have validators today, but we
265
+ * still translate it as a usage error (exit 2) for forward-compat.
266
+ * - Anything else re-throws — caller's outer catch will surface it as
267
+ * exit 1 (runtime error).
268
+ */
269
+ function renderParseError(err, input, stderr) {
270
+ if (isUnknownSyntaxError(err)) {
271
+ writeStructuredError(stderr, errorMigrationCliUnknownFlag({
272
+ flag: findOffendingFlag(input),
273
+ knownFlags: KNOWN_FLAGS
274
+ }));
275
+ return 2;
276
+ }
277
+ if (err instanceof UsageError) {
278
+ writeStructuredError(stderr, errorMigrationCliInvalidConfigArg({ nextToken: err.message }));
279
+ return 2;
280
+ }
281
+ throw err;
282
+ }
283
+ /**
284
+ * Duck-type check for clipanion's `UnknownSyntaxError`: the class is
285
+ * thrown by the parser but is not re-exported from the package's main
286
+ * entry (only `UsageError` is — see clipanion's `advanced/index.d.ts`).
287
+ * Identified by `name === 'UnknownSyntaxError'` and the
288
+ * `clipanion.type === 'none'` discriminator that clipanion's
289
+ * `ErrorWithMeta` interface guarantees.
290
+ */
291
+ function isUnknownSyntaxError(err) {
292
+ if (!(err instanceof Error) || err.name !== "UnknownSyntaxError") return false;
293
+ const meta = err.clipanion;
294
+ return typeof meta === "object" && meta !== null && meta.type === "none";
295
+ }
296
+ /**
297
+ * Best-effort: pull the first input token that doesn't match a known
298
+ * flag. Falls back to the first token when we can't pinpoint it. The
299
+ * returned name is rendered into the user-visible PN-CLI-4013 envelope
300
+ * (`Unknown flag \`<name>\``) and round-tripped via `meta.flag` so
301
+ * agent consumers can render their own "did you mean" suggestions.
302
+ */
303
+ function findOffendingFlag(input) {
304
+ for (const token of input) {
305
+ if (!token.startsWith("-")) continue;
306
+ const head = token.split("=", 1)[0] ?? token;
307
+ if (!KNOWN_FLAGS.includes(head) && head !== "-h") return head;
308
+ }
309
+ return input[0] ?? "";
310
+ }
311
+ /**
312
+ * Write a `CliStructuredError` envelope to the given stream. Format
313
+ * matches the legacy hand-rolled writer (`message: why`) so the rest of
314
+ * the project's error rendering stays consistent across surfaces. The
315
+ * full PN code (`PN-<domain>-<code>`) is included so consumers can
316
+ * grep for stable identifiers.
317
+ */
318
+ function writeStructuredError(stream, err) {
319
+ const envelope = err.toEnvelope();
320
+ const why = envelope.why ?? envelope.summary;
321
+ const fix = envelope.fix ? `\n${envelope.fix}` : "";
322
+ stream.write(`${envelope.code}: ${envelope.summary}\n${why}${fix}\n`);
323
+ }
134
324
  /**
135
325
  * Read a previously-scaffolded `migration.json` from disk, returning
136
- * `null` when the file is missing or unparseable. The CLI feeds this into
137
- * `buildMigrationArtifacts` so the pure builder can preserve fields owned
138
- * by `migration plan` (contract bookends, hints, labels, `createdAt`)
139
- * across re-emits.
326
+ * `null` when the file is missing and throwing `MIGRATION.INVALID_JSON`
327
+ * when the file is present but cannot be parsed as JSON. The CLI feeds
328
+ * this into `buildMigrationArtifacts` so the pure builder can preserve
329
+ * fields owned by `migration plan` (contract bookends, hints, labels,
330
+ * `createdAt`) across re-emits.
331
+ *
332
+ * Author-time path: this loader still does not verify the manifest hash
333
+ * or schema — that is the apply-time loader's job. Hash mismatch is the
334
+ * *expected* outcome of a re-author (the developer's source changes
335
+ * invalidate the prior hash by construction), and verification here
336
+ * would block legitimate regenerations. Syntactic JSON-parse failure,
337
+ * however, is now surfaced rather than swallowed: a malformed
338
+ * `migration.json` indicates either a hand-edit gone wrong or partial
339
+ * write, and silently rebuilding from `describe()` would discard the
340
+ * user's on-disk content (preserved bookends, hints, labels,
341
+ * `createdAt`) without any indication something was wrong on disk.
342
+ * Apply-time consumers always route through the verifying
343
+ * `readMigrationPackage` in `@prisma-next/migration-tools/io` instead.
140
344
  */
141
- function readExistingManifest(manifestPath) {
345
+ function readExistingMetadata(metadataPath) {
142
346
  let raw;
143
347
  try {
144
- raw = readFileSync(manifestPath, "utf-8");
348
+ raw = readFileSync(metadataPath, "utf-8");
145
349
  } catch {
146
350
  return null;
147
351
  }
148
352
  try {
149
353
  return JSON.parse(raw);
150
- } catch {
151
- return null;
354
+ } catch (e) {
355
+ throw errorInvalidJson(metadataPath, e instanceof Error ? e.message : String(e));
152
356
  }
153
357
  }
154
358
  /**
@@ -165,20 +369,39 @@ function readExistingManifest(manifestPath) {
165
369
  * legitimate site for combining config loading, stack assembly, and
166
370
  * filesystem persistence.
167
371
  */
168
- function serializeMigrationToDisk(instance, migrationDir, dryRun) {
169
- const manifestPath = join(migrationDir, "migration.json");
170
- const { opsJson, manifestJson } = buildMigrationArtifacts(instance, readExistingManifest(manifestPath));
372
+ function serializeMigrationToDisk(instance, migrationDir, dryRun, stdout) {
373
+ const metadataPath = join(migrationDir, "migration.json");
374
+ const { opsJson, metadataJson } = buildMigrationArtifacts(instance, readExistingMetadata(metadataPath));
171
375
  if (dryRun) {
172
- process.stdout.write(`--- migration.json ---\n${manifestJson}\n`);
173
- process.stdout.write("--- ops.json ---\n");
174
- process.stdout.write(`${opsJson}\n`);
376
+ stdout.write(`--- migration.json ---\n${metadataJson}\n`);
377
+ stdout.write("--- ops.json ---\n");
378
+ stdout.write(`${opsJson}\n`);
175
379
  return;
176
380
  }
177
381
  writeFileSync(join(migrationDir, "ops.json"), opsJson);
178
- writeFileSync(manifestPath, manifestJson);
179
- process.stdout.write(`Wrote ops.json + migration.json to ${migrationDir}\n`);
382
+ writeFileSync(metadataPath, metadataJson);
383
+ stdout.write(`Wrote ops.json + migration.json to ${migrationDir}\n`);
384
+ }
385
+ /**
386
+ * Inner orchestration: load config, probe-construct the migration,
387
+ * verify target, assemble the stack, construct with the stack, persist.
388
+ *
389
+ * Throws `CliStructuredError` for known failure modes (config not
390
+ * found, target mismatch); the outer `orchestrate` translates those to
391
+ * exit 1.
392
+ */
393
+ async function runMigration(importMetaUrl, MigrationClass, parsed, ctx) {
394
+ const migrationDir = dirname(fileURLToPath(importMetaUrl));
395
+ const config = await loadConfig(parsed.config);
396
+ const probe = new MigrationClass();
397
+ if (probe.targetId !== config.target.targetId) throw errorMigrationTargetMismatch({
398
+ migrationTargetId: probe.targetId,
399
+ configTargetId: config.target.targetId
400
+ });
401
+ serializeMigrationToDisk(new MigrationClass(createControlStack(config)), migrationDir, parsed.dryRun, ctx.stdout);
402
+ ctx.stderr;
180
403
  }
181
-
182
404
  //#endregion
183
405
  export { MigrationCLI };
406
+
184
407
  //# sourceMappingURL=migration-cli.mjs.map