@prisma/orm-toolchain 8.0.0-rc.1-dev.44 → 8.0.0-rc.1-dev.46

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 (303) hide show
  1. package/README.md +3 -3
  2. package/dist/{aggregate-BBsCbXgJ.mjs → aggregate-CPZB020V.mjs} +9 -9
  3. package/dist/{aggregate-BBsCbXgJ.mjs.map → aggregate-CPZB020V.mjs.map} +1 -1
  4. package/dist/{aggregate-CA6bJ8UO.d.mts → aggregate-DLUJLq5Y.d.mts} +3 -3
  5. package/dist/{aggregate-CA6bJ8UO.d.mts.map → aggregate-DLUJLq5Y.d.mts.map} +1 -1
  6. package/dist/cli-telemetry.d.mts +19 -5
  7. package/dist/cli-telemetry.d.mts.map +1 -1
  8. package/dist/cli-telemetry.mjs +287 -8
  9. package/dist/cli-telemetry.mjs.map +1 -1
  10. package/dist/cli.d.mts +11 -13
  11. package/dist/cli.d.mts.map +1 -1
  12. package/dist/cli.mjs +5382 -1095
  13. package/dist/cli.mjs.map +1 -1
  14. package/dist/cli__control-api.d.mts +298 -27
  15. package/dist/cli__control-api.d.mts.map +1 -1
  16. package/dist/cli__control-api.mjs +2 -19
  17. package/dist/cli__control-api__testing.d.mts +1 -1
  18. package/dist/cli__control-api__testing.mjs.map +1 -1
  19. package/dist/cli__init-output.d.mts +0 -1
  20. package/dist/cli__init-output.d.mts.map +1 -1
  21. package/dist/cli__init-output.mjs +1 -1
  22. package/dist/cli__migration-cli.mjs +9 -9
  23. package/dist/cli__migration-cli.mjs.map +1 -1
  24. package/dist/config-loader.d.mts +1 -1
  25. package/dist/config-loader.mjs +1 -1
  26. package/dist/{contract-snapshot-store-B0G1Fw_F-C4JjmWEA.mjs → contract-snapshot-store-B0G1Fw_F-CHc4QfKb.mjs} +4 -4
  27. package/dist/{contract-snapshot-store-B0G1Fw_F-C4JjmWEA.mjs.map → contract-snapshot-store-B0G1Fw_F-CHc4QfKb.mjs.map} +1 -1
  28. package/dist/{cli-errors-iquUoY6O-BFboxglv.d.mts → db-verify-BwgNK50T-DGvKgFCK.d.mts} +81 -81
  29. package/dist/db-verify-BwgNK50T-DGvKgFCK.d.mts.map +1 -0
  30. package/dist/emitter.d.mts +1 -1
  31. package/dist/{errors-CXbGUWGu-BQi6YS-H.mjs → errors-CXbGUWGu-CstQHPh6.mjs} +2 -2
  32. package/dist/{errors-CXbGUWGu-BQi6YS-H.mjs.map → errors-CXbGUWGu-CstQHPh6.mjs.map} +1 -1
  33. package/dist/{errors-KWM8yBAL.d.mts → errors-D59OCpoI.d.mts} +2 -2
  34. package/dist/{errors-KWM8yBAL.d.mts.map → errors-D59OCpoI.d.mts.map} +1 -1
  35. package/dist/{exports-B3xY_FHp.mjs → exports-8ZUN43ve.mjs} +6 -6
  36. package/dist/exports-8ZUN43ve.mjs.map +1 -0
  37. package/dist/{exports-BZLNbtkY.mjs → exports-BZaG8_w_.mjs} +64 -36
  38. package/dist/exports-BZaG8_w_.mjs.map +1 -0
  39. package/dist/{graph-membership-BViWUMpP-Cgu4Z2ox.mjs → graph-membership-BViWUMpP-C4lYYJdU.mjs} +2 -2
  40. package/dist/{graph-membership-BViWUMpP-Cgu4Z2ox.mjs.map → graph-membership-BViWUMpP-C4lYYJdU.mjs.map} +1 -1
  41. package/dist/{import-roots-NDOIEDVj.mjs → import-roots-CNFBPAT1.mjs} +10 -3
  42. package/dist/{import-roots-NDOIEDVj.mjs.map → import-roots-CNFBPAT1.mjs.map} +1 -1
  43. package/dist/{import-roots-B6CtsWXb.d.mts → import-roots-CfoYgejL.d.mts} +2 -2
  44. package/dist/{import-roots-B6CtsWXb.d.mts.map → import-roots-CfoYgejL.d.mts.map} +1 -1
  45. package/dist/{index-mCsi_ND5.d.mts → index-DRs7L6N0.d.mts} +11 -3
  46. package/dist/index-DRs7L6N0.d.mts.map +1 -0
  47. package/dist/{index-CnOyYxmv.d.mts → index-Dgwl8Tzs.d.mts} +2 -2
  48. package/dist/{index-CnOyYxmv.d.mts.map → index-Dgwl8Tzs.d.mts.map} +1 -1
  49. package/dist/{invariants-CTHy88OP-DJ-i5yTz.mjs → invariants-CTHy88OP-CYI2a8IO.mjs} +2 -2
  50. package/dist/{invariants-CTHy88OP-DJ-i5yTz.mjs.map → invariants-CTHy88OP-CYI2a8IO.mjs.map} +1 -1
  51. package/dist/{io-635GlNx1-D94PxRc-.mjs → io-635GlNx1-D3zrw2qu.mjs} +4 -4
  52. package/dist/{io-635GlNx1-D94PxRc-.mjs.map → io-635GlNx1-D3zrw2qu.mjs.map} +1 -1
  53. package/dist/{migration-graph-_SJImD5V-D5RCc651.mjs → migration-graph-_SJImD5V-BURyY1KM.mjs} +2 -2
  54. package/dist/{migration-graph-_SJImD5V-D5RCc651.mjs.map → migration-graph-_SJImD5V-BURyY1KM.mjs.map} +1 -1
  55. package/dist/{migration-gubSSeMk.mjs → migration-hspPIEeV.mjs} +4 -4
  56. package/dist/{migration-gubSSeMk.mjs.map → migration-hspPIEeV.mjs.map} +1 -1
  57. package/dist/migration-tools.d.mts +4 -4
  58. package/dist/migration-tools.mjs +13 -13
  59. package/dist/migration-tools__aggregate.d.mts +1 -1
  60. package/dist/migration-tools__aggregate.mjs +1 -1
  61. package/dist/migration-tools__contract-snapshot-store.mjs +1 -1
  62. package/dist/migration-tools__errors.d.mts +1 -1
  63. package/dist/migration-tools__errors.mjs +1 -1
  64. package/dist/migration-tools__invariants.mjs +1 -1
  65. package/dist/migration-tools__io.mjs +1 -1
  66. package/dist/migration-tools__migration-graph.mjs +2 -2
  67. package/dist/migration-tools__migration-ts.mjs +1 -1
  68. package/dist/migration-tools__migration.mjs +1 -1
  69. package/dist/migration-tools__ref-resolution.mjs +1 -1
  70. package/dist/migration-tools__refs.mjs +1 -1
  71. package/dist/migration-tools__spaces.d.mts +2 -2
  72. package/dist/migration-tools__spaces.mjs +2 -2
  73. package/dist/{migration-ts-BoHxxGvi.mjs → migration-ts-DdVm0rws.mjs} +2 -2
  74. package/dist/{migration-ts-BoHxxGvi.mjs.map → migration-ts-DdVm0rws.mjs.map} +1 -1
  75. package/dist/{output-DP3woAjV-BxCr9nHA.mjs → output-CovTDY14-CUoAqLML.mjs} +3 -45
  76. package/dist/output-CovTDY14-CUoAqLML.mjs.map +1 -0
  77. package/dist/publish-surface.d.mts +2 -2
  78. package/dist/publish-surface.mjs +2 -2
  79. package/dist/publish-surface__import-roots.d.mts +1 -1
  80. package/dist/publish-surface__import-roots.mjs +1 -1
  81. package/dist/publish-surface__shells.d.mts +1 -1
  82. package/dist/publish-surface__shells.mjs +1 -1
  83. package/dist/quick-reference-mongo.md +12 -9
  84. package/dist/quick-reference-postgres.md +12 -9
  85. package/dist/readme-mongo.md +2 -2
  86. package/dist/readme-postgres.md +2 -2
  87. package/dist/ref-h6InR6dO-rGfl18Jv.mjs +4686 -0
  88. package/dist/ref-h6InR6dO-rGfl18Jv.mjs.map +1 -0
  89. package/dist/{ref-resolution-sGcINe7o.mjs → ref-resolution-HeNBXW53.mjs} +2 -2
  90. package/dist/{ref-resolution-sGcINe7o.mjs.map → ref-resolution-HeNBXW53.mjs.map} +1 -1
  91. package/dist/{refs-WK86uYYp-B6EZkmmT.mjs → refs-WK86uYYp-gM8tvWQP.mjs} +2 -2
  92. package/dist/{refs-WK86uYYp-B6EZkmmT.mjs.map → refs-WK86uYYp-gM8tvWQP.mjs.map} +1 -1
  93. package/dist/{shells-BenrJHp2.mjs → shells-C4HbRAr5.mjs} +13 -14
  94. package/dist/shells-C4HbRAr5.mjs.map +1 -0
  95. package/dist/{shells-COHV_Ozf.d.mts → shells-CIUmT4u4.d.mts} +14 -20
  96. package/dist/shells-CIUmT4u4.d.mts.map +1 -0
  97. package/dist/{spaces-BG2w0P8d.mjs → spaces-4qMOAYE9.mjs} +7 -7
  98. package/dist/{spaces-BG2w0P8d.mjs.map → spaces-4qMOAYE9.mjs.map} +1 -1
  99. package/dist/{spaces-tWQi7CUv.d.mts → spaces-Dj65JrwZ.d.mts} +2 -2
  100. package/dist/{spaces-tWQi7CUv.d.mts.map → spaces-Dj65JrwZ.d.mts.map} +1 -1
  101. package/dist/{verify-contract-spaces-Cc1u_wJo-BilPJS2S.d.mts → verify-contract-spaces-C3PiFBk3-DfomYyOE.d.mts} +3 -3
  102. package/dist/{verify-contract-spaces-Cc1u_wJo-BilPJS2S.d.mts.map → verify-contract-spaces-C3PiFBk3-DfomYyOE.d.mts.map} +1 -1
  103. package/dist/{verify-contract-spaces-DhJTb0zM-_oh6W6lp.mjs → verify-contract-spaces-DhJTb0zM-B62maCfE.mjs} +4 -4
  104. package/dist/{verify-contract-spaces-DhJTb0zM-_oh6W6lp.mjs.map → verify-contract-spaces-DhJTb0zM-B62maCfE.mjs.map} +1 -1
  105. package/dist/vite-plugin-contract-emit.d.mts +2 -2
  106. package/dist/vite-plugin-contract-emit.mjs +5 -5
  107. package/dist/vite-plugin-contract-emit.mjs.map +1 -1
  108. package/package.json +14 -38
  109. package/dist/bin__prisma-next.mjs +0 -512
  110. package/dist/bin__prisma-next.mjs.map +0 -1
  111. package/dist/caught-errors-BZo7zcRc-C-pIEasG.mjs +0 -14
  112. package/dist/caught-errors-BZo7zcRc-C-pIEasG.mjs.map +0 -1
  113. package/dist/cli-errors-B4zTLaqQ-CXgjO9t4.mjs +0 -347
  114. package/dist/cli-errors-B4zTLaqQ-CXgjO9t4.mjs.map +0 -1
  115. package/dist/cli-errors-iquUoY6O-BFboxglv.d.mts.map +0 -1
  116. package/dist/cli__commands__contract-emit.d.mts +0 -2
  117. package/dist/cli__commands__contract-emit.mjs +0 -2
  118. package/dist/cli__commands__contract-infer.d.mts +0 -7
  119. package/dist/cli__commands__contract-infer.d.mts.map +0 -1
  120. package/dist/cli__commands__contract-infer.mjs +0 -2
  121. package/dist/cli__commands__db-init.d.mts +0 -7
  122. package/dist/cli__commands__db-init.d.mts.map +0 -1
  123. package/dist/cli__commands__db-init.mjs +0 -2
  124. package/dist/cli__commands__db-schema.d.mts +0 -7
  125. package/dist/cli__commands__db-schema.d.mts.map +0 -1
  126. package/dist/cli__commands__db-schema.mjs +0 -2
  127. package/dist/cli__commands__db-sign.d.mts +0 -7
  128. package/dist/cli__commands__db-sign.d.mts.map +0 -1
  129. package/dist/cli__commands__db-sign.mjs +0 -2
  130. package/dist/cli__commands__db-update.d.mts +0 -7
  131. package/dist/cli__commands__db-update.d.mts.map +0 -1
  132. package/dist/cli__commands__db-update.mjs +0 -2
  133. package/dist/cli__commands__db-verify.d.mts +0 -7
  134. package/dist/cli__commands__db-verify.d.mts.map +0 -1
  135. package/dist/cli__commands__db-verify.mjs +0 -2
  136. package/dist/cli__commands__migrate.d.mts +0 -57
  137. package/dist/cli__commands__migrate.d.mts.map +0 -1
  138. package/dist/cli__commands__migrate.mjs +0 -2
  139. package/dist/cli__commands__migration-check.d.mts +0 -8
  140. package/dist/cli__commands__migration-check.d.mts.map +0 -1
  141. package/dist/cli__commands__migration-check.mjs +0 -3
  142. package/dist/cli__commands__migration-graph.d.mts +0 -38
  143. package/dist/cli__commands__migration-graph.d.mts.map +0 -1
  144. package/dist/cli__commands__migration-graph.mjs +0 -2
  145. package/dist/cli__commands__migration-list.d.mts +0 -39
  146. package/dist/cli__commands__migration-list.d.mts.map +0 -1
  147. package/dist/cli__commands__migration-list.mjs +0 -2
  148. package/dist/cli__commands__migration-log.d.mts +0 -20
  149. package/dist/cli__commands__migration-log.d.mts.map +0 -1
  150. package/dist/cli__commands__migration-log.mjs +0 -2
  151. package/dist/cli__commands__migration-new.d.mts +0 -7
  152. package/dist/cli__commands__migration-new.d.mts.map +0 -1
  153. package/dist/cli__commands__migration-new.mjs +0 -2
  154. package/dist/cli__commands__migration-plan.d.mts +0 -34
  155. package/dist/cli__commands__migration-plan.d.mts.map +0 -1
  156. package/dist/cli__commands__migration-plan.mjs +0 -2
  157. package/dist/cli__commands__migration-show.d.mts +0 -27
  158. package/dist/cli__commands__migration-show.d.mts.map +0 -1
  159. package/dist/cli__commands__migration-show.mjs +0 -2
  160. package/dist/cli__commands__migration-status.d.mts +0 -55
  161. package/dist/cli__commands__migration-status.d.mts.map +0 -1
  162. package/dist/cli__commands__migration-status.mjs +0 -3
  163. package/dist/cli__commands__ref.d.mts +0 -7
  164. package/dist/cli__commands__ref.d.mts.map +0 -1
  165. package/dist/cli__commands__ref.mjs +0 -2
  166. package/dist/cli__commands__telemetry.d.mts +0 -7
  167. package/dist/cli__commands__telemetry.d.mts.map +0 -1
  168. package/dist/cli__commands__telemetry.mjs +0 -2
  169. package/dist/client-C6Qp5ujC-Dxb7r79B.mjs +0 -1653
  170. package/dist/client-C6Qp5ujC-Dxb7r79B.mjs.map +0 -1
  171. package/dist/code-templates-D_sGnEF6-DRLPzlq8.mjs +0 -295
  172. package/dist/code-templates-D_sGnEF6-DRLPzlq8.mjs.map +0 -1
  173. package/dist/combine-verify-results-B87B_pMc-D4l22OqY.mjs +0 -65
  174. package/dist/combine-verify-results-B87B_pMc-D4l22OqY.mjs.map +0 -1
  175. package/dist/command-helpers-BCgwcqTe-lNCtppzM.mjs +0 -776
  176. package/dist/command-helpers-BCgwcqTe-lNCtppzM.mjs.map +0 -1
  177. package/dist/contract-at-errors-BSoDI0Hw-BXeUNE3s.mjs +0 -44
  178. package/dist/contract-at-errors-BSoDI0Hw-BXeUNE3s.mjs.map +0 -1
  179. package/dist/contract-emit-BG7KL4zk-_a0J310Z.mjs +0 -331
  180. package/dist/contract-emit-BG7KL4zk-_a0J310Z.mjs.map +0 -1
  181. package/dist/contract-emit-BkEj3siG-C8M-IyvS.mjs +0 -2
  182. package/dist/contract-emit-CvwNE4R1-DEn1V1Zo.d.mts +0 -7
  183. package/dist/contract-emit-CvwNE4R1-DEn1V1Zo.d.mts.map +0 -1
  184. package/dist/contract-emit-GknCmgn_-BSWdFprC.mjs +0 -154
  185. package/dist/contract-emit-GknCmgn_-BSWdFprC.mjs.map +0 -1
  186. package/dist/contract-enrichment-DrMvvY2A-DxNGeF4d.mjs +0 -56
  187. package/dist/contract-enrichment-DrMvvY2A-DxNGeF4d.mjs.map +0 -1
  188. package/dist/contract-infer-BwIDGl7v-CZrdWXAr.mjs +0 -74
  189. package/dist/contract-infer-BwIDGl7v-CZrdWXAr.mjs.map +0 -1
  190. package/dist/contract-snapshot-resolution-De2VvOJn-DqIAV9cr.mjs +0 -82
  191. package/dist/contract-snapshot-resolution-De2VvOJn-DqIAV9cr.mjs.map +0 -1
  192. package/dist/contract-space-aggregate-loader-fa04OIaL-B7LXHle3.mjs +0 -303
  193. package/dist/contract-space-aggregate-loader-fa04OIaL-B7LXHle3.mjs.map +0 -1
  194. package/dist/db-init-BolXrKAI-CwG-zEDW.mjs +0 -109
  195. package/dist/db-init-BolXrKAI-CwG-zEDW.mjs.map +0 -1
  196. package/dist/db-init-failure-B5cQML4A-5zr-99Ek.mjs +0 -44
  197. package/dist/db-init-failure-B5cQML4A-5zr-99Ek.mjs.map +0 -1
  198. package/dist/db-schema-DvdLBdEY-Dl6e1d_Y.mjs +0 -48
  199. package/dist/db-schema-DvdLBdEY-Dl6e1d_Y.mjs.map +0 -1
  200. package/dist/db-sign-DCp4c8CA-B3tun-kh.mjs +0 -172
  201. package/dist/db-sign-DCp4c8CA-B3tun-kh.mjs.map +0 -1
  202. package/dist/db-update-CfayE8WE-CPhQyDaZ.mjs +0 -171
  203. package/dist/db-update-CfayE8WE-CPhQyDaZ.mjs.map +0 -1
  204. package/dist/db-verify-BN7IrOAL-kEzRNJUl.mjs +0 -358
  205. package/dist/db-verify-BN7IrOAL-kEzRNJUl.mjs.map +0 -1
  206. package/dist/exports-B3xY_FHp.mjs.map +0 -1
  207. package/dist/exports-BZLNbtkY.mjs.map +0 -1
  208. package/dist/exports-CqNUihkI.mjs +0 -276
  209. package/dist/exports-CqNUihkI.mjs.map +0 -1
  210. package/dist/format-Ca7dUE6B-BMFqrc6D.mjs +0 -110
  211. package/dist/format-Ca7dUE6B-BMFqrc6D.mjs.map +0 -1
  212. package/dist/framework-components-Bau7o1-J-DTp6AUoq.mjs +0 -60
  213. package/dist/framework-components-Bau7o1-J-DTp6AUoq.mjs.map +0 -1
  214. package/dist/global-flags-BpoOYtNZ-CHZD3fud.d.mts +0 -35
  215. package/dist/global-flags-BpoOYtNZ-CHZD3fud.d.mts.map +0 -1
  216. package/dist/index-mCsi_ND5.d.mts.map +0 -1
  217. package/dist/init-B86mXFNC-C2HAUCRn.mjs +0 -800
  218. package/dist/init-B86mXFNC-C2HAUCRn.mjs.map +0 -1
  219. package/dist/inspect-live-schema-BPHXdvqW-DrzL2ZkQ.mjs +0 -95
  220. package/dist/inspect-live-schema-BPHXdvqW-DrzL2ZkQ.mjs.map +0 -1
  221. package/dist/integrity-violation-to-check-failure-BmWMLAae-DqiC4_3p.mjs +0 -115
  222. package/dist/integrity-violation-to-check-failure-BmWMLAae-DqiC4_3p.mjs.map +0 -1
  223. package/dist/invariants-Bltd8DyA-Cg_1Lu0q.mjs +0 -53
  224. package/dist/invariants-Bltd8DyA-Cg_1Lu0q.mjs.map +0 -1
  225. package/dist/legend-DGv9e2_0-BtetgRz4.mjs +0 -22
  226. package/dist/legend-DGv9e2_0-BtetgRz4.mjs.map +0 -1
  227. package/dist/migrate-BXATUjYE-Do79Imf-.mjs +0 -343
  228. package/dist/migrate-BXATUjYE-Do79Imf-.mjs.map +0 -1
  229. package/dist/migrate-failure-D4NvkoCk-0bttmQs2.mjs +0 -96
  230. package/dist/migrate-failure-D4NvkoCk-0bttmQs2.mjs.map +0 -1
  231. package/dist/migrate-show-B8UJ9dmb-C4RTPbDk.d.mts +0 -63
  232. package/dist/migrate-show-B8UJ9dmb-C4RTPbDk.d.mts.map +0 -1
  233. package/dist/migrate-show-C_9xR4Qt-B-Y0PGKw.mjs +0 -199
  234. package/dist/migrate-show-C_9xR4Qt-B-Y0PGKw.mjs.map +0 -1
  235. package/dist/migration-check-Bbdw7t-r-C2VTMK61.mjs +0 -358
  236. package/dist/migration-check-Bbdw7t-r-C2VTMK61.mjs.map +0 -1
  237. package/dist/migration-check-DbHgYK7F-7mtYNVFu.mjs +0 -163
  238. package/dist/migration-check-DbHgYK7F-7mtYNVFu.mjs.map +0 -1
  239. package/dist/migration-command-scaffold-SzjR2naH-Yi3-rNfX.mjs +0 -121
  240. package/dist/migration-command-scaffold-SzjR2naH-Yi3-rNfX.mjs.map +0 -1
  241. package/dist/migration-graph-B8WxW_tm-qGNf7jQh.mjs +0 -35
  242. package/dist/migration-graph-B8WxW_tm-qGNf7jQh.mjs.map +0 -1
  243. package/dist/migration-graph-BOwu76mF-kOTWFvfV.mjs +0 -144
  244. package/dist/migration-graph-BOwu76mF-kOTWFvfV.mjs.map +0 -1
  245. package/dist/migration-graph-command-render-h0VdTkSM-C3rPZEw1.mjs +0 -2127
  246. package/dist/migration-graph-command-render-h0VdTkSM-C3rPZEw1.mjs.map +0 -1
  247. package/dist/migration-graph-sections-BBHPOFN--COKyUKQ4.mjs +0 -69
  248. package/dist/migration-graph-sections-BBHPOFN--COKyUKQ4.mjs.map +0 -1
  249. package/dist/migration-list-CKuYcMQq-CubZzE9i.mjs +0 -130
  250. package/dist/migration-list-CKuYcMQq-CubZzE9i.mjs.map +0 -1
  251. package/dist/migration-list-CY08bD8I-PoXuv94c.mjs +0 -102
  252. package/dist/migration-list-CY08bD8I-PoXuv94c.mjs.map +0 -1
  253. package/dist/migration-log-CUbGla-9-BLIrWjwN.mjs +0 -123
  254. package/dist/migration-log-CUbGla-9-BLIrWjwN.mjs.map +0 -1
  255. package/dist/migration-log-table-eES9lPjv-BCFm8qJr.mjs +0 -116
  256. package/dist/migration-log-table-eES9lPjv-BCFm8qJr.mjs.map +0 -1
  257. package/dist/migration-new-ChvZjf5X-BFhEM0BF.mjs +0 -70
  258. package/dist/migration-new-ChvZjf5X-BFhEM0BF.mjs.map +0 -1
  259. package/dist/migration-new-DXMvJdbB-BkrpDfcr.mjs +0 -130
  260. package/dist/migration-new-DXMvJdbB-BkrpDfcr.mjs.map +0 -1
  261. package/dist/migration-path-target-JDWqF9kj-B7zoMq6X.mjs +0 -38
  262. package/dist/migration-path-target-JDWqF9kj-B7zoMq6X.mjs.map +0 -1
  263. package/dist/migration-plan-BV9f5Xjq-DiyscOMc.d.mts +0 -164
  264. package/dist/migration-plan-BV9f5Xjq-DiyscOMc.d.mts.map +0 -1
  265. package/dist/migration-plan-Bz1hOTHF-CFCtPuOv.mjs +0 -192
  266. package/dist/migration-plan-Bz1hOTHF-CFCtPuOv.mjs.map +0 -1
  267. package/dist/migration-plan-Cfz0efEL-DMNFq5b2.mjs +0 -621
  268. package/dist/migration-plan-Cfz0efEL-DMNFq5b2.mjs.map +0 -1
  269. package/dist/migration-show-BGdR1xnT-Cg3M0FPh.mjs +0 -178
  270. package/dist/migration-show-BGdR1xnT-Cg3M0FPh.mjs.map +0 -1
  271. package/dist/migration-status-BIrAJ5af-D-_mMBUA.mjs +0 -427
  272. package/dist/migration-status-BIrAJ5af-D-_mMBUA.mjs.map +0 -1
  273. package/dist/output-DP3woAjV-BxCr9nHA.mjs.map +0 -1
  274. package/dist/progress-adapter-CjAeTxY_-BXYSd1eY.mjs +0 -43
  275. package/dist/progress-adapter-CjAeTxY_-BXYSd1eY.mjs.map +0 -1
  276. package/dist/project-import-root-B4nM-VWX-D__g0hTb.mjs +0 -101
  277. package/dist/project-import-root-B4nM-VWX-D__g0hTb.mjs.map +0 -1
  278. package/dist/redact-secrets-ojVi2cpy-CLyiSDk5.mjs +0 -1476
  279. package/dist/redact-secrets-ojVi2cpy-CLyiSDk5.mjs.map +0 -1
  280. package/dist/ref-4x6BGSsd-ClaoZIQh.mjs +0 -95
  281. package/dist/ref-4x6BGSsd-ClaoZIQh.mjs.map +0 -1
  282. package/dist/ref-WXLIR9Xm-12IUtoBU.mjs +0 -104
  283. package/dist/ref-WXLIR9Xm-12IUtoBU.mjs.map +0 -1
  284. package/dist/ref-advancement-DaZBzm7m-WoqnFAyr.mjs +0 -96
  285. package/dist/ref-advancement-DaZBzm7m-WoqnFAyr.mjs.map +0 -1
  286. package/dist/ref-resolution-BSN1amKm-C6-TTM2N.mjs +0 -16
  287. package/dist/ref-resolution-BSN1amKm-C6-TTM2N.mjs.map +0 -1
  288. package/dist/refs-fQLFcMoq-aTBeUU9H.mjs +0 -47
  289. package/dist/refs-fQLFcMoq-aTBeUU9H.mjs.map +0 -1
  290. package/dist/schemas-BGgjiKV0-Rh8RlMCl.d.mts +0 -206
  291. package/dist/schemas-BGgjiKV0-Rh8RlMCl.d.mts.map +0 -1
  292. package/dist/schemas-CYSD83Cu-Bff4PPoi.mjs +0 -119
  293. package/dist/schemas-CYSD83Cu-Bff4PPoi.mjs.map +0 -1
  294. package/dist/shells-BenrJHp2.mjs.map +0 -1
  295. package/dist/shells-COHV_Ozf.d.mts.map +0 -1
  296. package/dist/telemetry-BD52LETE-Cqeb7ebh.mjs +0 -121
  297. package/dist/telemetry-BD52LETE-Cqeb7ebh.mjs.map +0 -1
  298. package/dist/terminal-ui-BN4QOsm5-Bf3pim46.d.mts +0 -133
  299. package/dist/terminal-ui-BN4QOsm5-Bf3pim46.d.mts.map +0 -1
  300. package/dist/terminal-ui-Cqz4LVL7-B0mcVdLy.mjs +0 -676
  301. package/dist/terminal-ui-Cqz4LVL7-B0mcVdLy.mjs.map +0 -1
  302. package/dist/verify-Cogr_Fj9-D3lCWA6t.mjs +0 -283
  303. package/dist/verify-Cogr_Fj9-D3lCWA6t.mjs.map +0 -1
@@ -0,0 +1,4686 @@
1
+ import { r as getEmittedArtifactPaths, t as emit } from "./exports-BCLg4CiV-05kuwcb0.mjs";
2
+ import { O as errorNoInvariantPath, P as errorUnknownInvariant, b as errorInvalidRefName, i as errorContractDeserializationFailed, t as MigrationToolsError } from "./errors-CXbGUWGu-CstQHPh6.mjs";
3
+ import { a as snapshotsImportPathFrom, o as writeContractSnapshot, r as readContractSnapshotJson, t as contractSnapshotDir } from "./contract-snapshot-store-B0G1Fw_F-CHc4QfKb.mjs";
4
+ import { n as verifyMigrationHash, t as computeMigrationHash } from "./hash-DH8hl5Rq-BpM2t3Nu.mjs";
5
+ import { t as deriveProvidedInvariants } from "./invariants-CTHy88OP-CYI2a8IO.mjs";
6
+ import { i as materialiseExtensionMigrationPackageIfMissing, r as formatMigrationDirName, u as writeMigrationPackage } from "./io-635GlNx1-D3zrw2qu.mjs";
7
+ import { t as EMPTY_CONTRACT_HASH } from "./constants-CM-w38DQ-Cz_EXbOc.mjs";
8
+ import { n as isGraphNode, t as assertHashIsGraphNode } from "./graph-membership-BViWUMpP-C4lYYJdU.mjs";
9
+ import { a as findPath, o as findPathWithDecision, r as findLatestMigration } from "./migration-graph-_SJImD5V-BURyY1KM.mjs";
10
+ import { d as writeRef, i as readRefs, l as validateRefName, n as deleteRef, o as refsByContractHash, t as HEAD_REF_NAME, u as validateRefValue } from "./refs-WK86uYYp-gM8tvWQP.mjs";
11
+ import { a as isValidSpaceId, c as spaceMigrationDirectory, l as spaceRefsDirectory, o as listContractSpaceDirectories, s as readContractSpaceHeadRef, t as APP_SPACE_ID$1 } from "./verify-contract-spaces-DhJTb0zM-B62maCfE.mjs";
12
+ import { d as resolveRecordedPath, f as verifyMigration, l as planMigration, n as buildFabricatedMigrationEdge, r as collectAggregateNamespaces, s as loadContractSpaceAggregate, t as allStorageElementsExternal, u as requireHeadRef } from "./aggregate-CPZB020V.mjs";
13
+ import { i as emitContractSpaceArtifacts, o as planAllSpaces } from "./spaces-4qMOAYE9.mjs";
14
+ import { c as internalImportRoot, o as importRootForDependencies, r as createImportSpecifierResolver } from "./import-roots-CNFBPAT1.mjs";
15
+ import { n as parseContractRef, r as parseMigrationRef } from "./ref-resolution-HeNBXW53.mjs";
16
+ import { i as writeMigrationTs } from "./migration-ts-DdVm0rws.mjs";
17
+ import { createRequire } from "node:module";
18
+ import { ifDefined } from "@prisma/orm-framework/utils/defined";
19
+ import { structuredError } from "@prisma/orm-framework/utils/structured-error";
20
+ import { blindCast, castAs } from "@prisma/orm-framework/utils/casts";
21
+ import { InternalError } from "@prisma/orm-framework/utils/internal-error";
22
+ import { CliStructuredError, CliStructuredError as CliStructuredError$1, errorConfigValidation as errorConfigValidation$1, errorContractConfigMissing, errorContractValidationFailed, errorDatabaseConnectionRequired, errorDriverRequired, errorFileNotFound, errorMigrationPlanningFailed, errorTargetMigrationNotSupported, errorUnexpected } from "@prisma/orm-framework/errors/control";
23
+ import { APP_SPACE_ID, createControlStack, hasMigrations, hasOperationPreview, hasPslContractInfer, hasSchemaSubjectClassifier, hasSchemaView } from "@prisma/orm-framework/components/control";
24
+ import { notOk, ok } from "@prisma/orm-framework/utils/result";
25
+ import { mergeCapabilityMatrices } from "@prisma/orm-framework/components/components";
26
+ import { errorConsentPlanMismatch, errorDestructiveChanges, errorHashMismatch, errorMarkerMissing, errorRunnerFailed, errorRuntime, errorTargetMismatch } from "@prisma/orm-framework/errors/execution";
27
+ import "@prisma/orm-framework/errors/migration";
28
+ import { basename, dirname, isAbsolute, join, relative, resolve } from "pathe";
29
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
30
+ import { createHash } from "node:crypto";
31
+ import { canonicalizeJson } from "@prisma/orm-framework/components/utils";
32
+ import { abortable } from "@prisma/orm-framework/utils/abortable";
33
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
34
+ //#region ../../../1-framework/3-tooling/cli/dist/ref-h6InR6dO.mjs
35
+ /**
36
+ * The typed remediation the CLI attaches to its own errors and findings.
37
+ * Constructors live here rather than in any library the CLI calls: naming a
38
+ * runnable invocation is something only the CLI can do.
39
+ */
40
+ function runCommandAction(label, command) {
41
+ return {
42
+ kind: "run-command",
43
+ label,
44
+ command
45
+ };
46
+ }
47
+ /** Advice the user acts on themselves — there is no command to run. */
48
+ function chooseAction(label) {
49
+ return {
50
+ kind: "user-choice",
51
+ label
52
+ };
53
+ }
54
+ /**
55
+ * A CLI-raised error that carries the typed remediation as well as the prose.
56
+ * Only the CLI knows which invocation fixes a failure, so only errors raised
57
+ * here spell one; the foundation classes every library also raises keep
58
+ * carrying `code`, `why` and `fix` alone.
59
+ *
60
+ * Both fields are required: the `fix` prose is what the commander shell still
61
+ * renders, and the handler boundary drops it in favour of `nextActions` when it
62
+ * settles the engine's envelope.
63
+ */
64
+ var ActionableCliError = class extends CliStructuredError$1 {
65
+ nextActions;
66
+ constructor(code, summary, options) {
67
+ super(code, summary, options);
68
+ this.nextActions = options.nextActions;
69
+ }
70
+ };
71
+ /**
72
+ * A command that accepts a contract reference both as a positional and as a
73
+ * flag was given both. Structured rather than a bare stderr line, so a script
74
+ * sees the same envelope every other invocation failure produces.
75
+ */
76
+ function errorContractArgConflict(options) {
77
+ const fix = "Pass the contract reference once — either as the positional argument or as --contract.";
78
+ return new ActionableCliError("CLI.CONTRACT_ARG_CONFLICT", "Cannot specify both a positional contract argument and --contract", {
79
+ why: `The positional argument names "${options.positional}" and --contract names "${options.flag}"; there is no rule for which wins.`,
80
+ fix,
81
+ nextActions: [chooseAction(fix)],
82
+ meta: {
83
+ positional: options.positional,
84
+ flag: options.flag
85
+ }
86
+ });
87
+ }
88
+ function errorRefSetHashNotInGraph(resolvedHash, reachableHashes, graphTipHash) {
89
+ const reachableList = reachableHashes.length > 0 ? reachableHashes.join(", ") : "(none — migration graph is empty)";
90
+ const fix = reachableHashes.length > 0 ? graphTipHash !== null ? `Set the ref to a graph-node hash such as ${graphTipHash}, or run \`prisma-cli migration plan\` to extend the graph.` : "Set the ref to a hash that appears in the migration graph." : "Run `prisma-cli migration plan` first.";
91
+ const nextActions = reachableHashes.length > 0 ? graphTipHash !== null ? [chooseAction(`Set the ref to a graph-node hash such as ${graphTipHash}`), runCommandAction("Extend the migration graph", "prisma-cli migration plan")] : [chooseAction("Set the ref to a hash that appears in the migration graph")] : [runCommandAction("Plan the first migration", "prisma-cli migration plan")];
92
+ return new ActionableCliError("MIGRATION.HASH_NOT_IN_GRAPH", `Resolved contract hash is not in the migration graph: ${resolvedHash}`, {
93
+ why: reachableHashes.length > 0 ? `The migration graph reaches ${reachableList}; resolved ${resolvedHash} isn't a graph node.` : "The migration graph is empty — no hashes reachable.",
94
+ fix,
95
+ nextActions,
96
+ meta: {
97
+ resolvedHash,
98
+ reachableHashes: [...reachableHashes],
99
+ ...graphTipHash !== null ? { graphTipHash } : {}
100
+ }
101
+ });
102
+ }
103
+ function errorRefSetEmptySentinel(hash) {
104
+ return new ActionableCliError("MIGRATION.REF_SET_EMPTY_SENTINEL", `Cannot set ref to the empty-database sentinel: ${hash}`, {
105
+ why: "The empty-database sentinel is a planner internal; it is not a valid ref target.",
106
+ fix: "Set the ref to a contract hash from the migration graph, or use another ref name.",
107
+ nextActions: [chooseAction("Set the ref to a contract hash from the migration graph"), chooseAction("Or use another ref name")],
108
+ meta: { hash }
109
+ });
110
+ }
111
+ /**
112
+ * `--legend` was combined with a machine-readable or silent output flag.
113
+ * The legend is human-only decoration on stderr.
114
+ */
115
+ function errorLegendHumanOnly(conflictingFlag) {
116
+ return new ActionableCliError("MIGRATION.LEGEND_HUMAN_ONLY", "`--legend` is only available for human-readable output", {
117
+ why: `\`--legend\` prints a glyph key to stderr and cannot be combined with ${conflictingFlag}.`,
118
+ fix: `Omit ${conflictingFlag} to print the legend alongside the tree, or omit --legend when using ${conflictingFlag}.`,
119
+ nextActions: [chooseAction(`Omit ${conflictingFlag} to print the legend alongside the tree`), chooseAction(`Or omit --legend when using ${conflictingFlag}`)],
120
+ meta: { conflictingFlag }
121
+ });
122
+ }
123
+ /**
124
+ * `--space <id>` was given a value that doesn't satisfy the contract-space
125
+ * naming rule (`[a-z][a-z0-9_-]{0,63}` per `isValidSpaceId`). Fires before
126
+ * any fs work — the input is syntactically rejected the same way an on-disk
127
+ * directory with that name would be skipped by the enumerator.
128
+ */
129
+ function errorInvalidSpaceId(spaceId) {
130
+ return new ActionableCliError("MIGRATION.INVALID_SPACE_ID", `Invalid contract space id: ${spaceId}`, {
131
+ why: "Contract space ids must match [a-z][a-z0-9_-]{0,63} (lowercase, starts with a letter, max 64 characters — the rule applied to every on-disk space directory).",
132
+ fix: "Pass a space id that matches the directory naming rule, or omit --space to list every space.",
133
+ nextActions: [chooseAction("Pass a space id matching [a-z][a-z0-9_-]{0,63}"), runCommandAction("Or list every space", "prisma-cli migration list")],
134
+ meta: { spaceId }
135
+ });
136
+ }
137
+ /**
138
+ * `migration list --space <id>` was given a contract-space id that has no
139
+ * directory under `migrations/`. Distinct from "the space exists but is
140
+ * empty" — that path renders the empty-state line and exits 0 per the
141
+ * slice spec § Empty-state. This error fires only when `<projectMigrationsDir>/<spaceId>`
142
+ * does not exist on disk.
143
+ *
144
+ * `availableSpaces` lists the contract-space directory names actually
145
+ * present, sorted lex-asc, so the diagnostic can suggest a near match
146
+ * without making the user reach for `ls`.
147
+ */
148
+ function errorSpaceNotFound(spaceId, availableSpaces) {
149
+ const availableList = availableSpaces.length > 0 ? availableSpaces.join(", ") : "(none — no contract spaces on disk yet)";
150
+ const fix = availableSpaces.length > 0 ? `Pick one of: ${availableList}. Run \`prisma-cli migration list\` (no --space) to see every space's migrations.` : "Author a migration with `prisma-cli migration new` to create the first contract-space directory.";
151
+ const nextActions = availableSpaces.length > 0 ? [chooseAction(`Pick one of: ${availableList}`), runCommandAction("See every space's migrations", "prisma-cli migration list")] : [runCommandAction("Author the first migration, which creates the contract-space directory", "prisma-cli migration new")];
152
+ return new ActionableCliError("MIGRATION.SPACE_NOT_FOUND", `Unknown contract space: ${spaceId}`, {
153
+ why: `No directory named "${spaceId}" exists under the migrations root.`,
154
+ fix,
155
+ nextActions,
156
+ meta: {
157
+ spaceId,
158
+ availableSpaces: [...availableSpaces]
159
+ }
160
+ });
161
+ }
162
+ /**
163
+ * A `migration show` target resolved to a directory or a graph node, but no
164
+ * on-disk package was loaded for it.
165
+ */
166
+ function errorMigrationPackageNotFound(why) {
167
+ return new ActionableCliError("MIGRATION.PACKAGE_NOT_FOUND", "Migration package not found", {
168
+ why,
169
+ fix: "Pass a directory name, hash prefix, or path to an on-disk app-space migration package.",
170
+ nextActions: [chooseAction("Pass a directory name, hash prefix, or path to an on-disk app-space migration package"), runCommandAction("List what is on disk", "prisma-cli migration list")]
171
+ });
172
+ }
173
+ /** The app space has no migration packages at all, so no target can resolve. */
174
+ function errorNoMigrations(appMigrationsRelative) {
175
+ return new ActionableCliError("MIGRATION.NO_MIGRATIONS", "No migrations found", {
176
+ why: `No migration packages found in ${appMigrationsRelative}`,
177
+ fix: "Run `prisma-cli migration plan` to create a migration first.",
178
+ nextActions: [runCommandAction("Create the first migration", "prisma-cli migration plan")]
179
+ });
180
+ }
181
+ function errorRefSetBundleNotFound(hash) {
182
+ return new ActionableCliError("MIGRATION.REF_SET_BUNDLE_NOT_FOUND", `No migration bundle matches graph-node hash ${hash}`, {
183
+ why: `The hash is a graph node but no on-disk bundle has metadata.to = ${hash}.`,
184
+ fix: "Run `pnpm fixtures:check`, or re-emit the migration that produces this hash so its bundle is restored.",
185
+ nextActions: [runCommandAction("Restore the checked-in fixtures", "pnpm fixtures:check"), chooseAction("Or re-emit the migration that produces this hash")],
186
+ meta: { hash }
187
+ });
188
+ }
189
+ function errorPlanForgotTheFlag(resolvedHash, reachableRefs, graphTipHash, options) {
190
+ const reachableList = reachableRefs.length > 0 ? reachableRefs.map((r) => `${r.name} (${r.hash})`).join(", ") : "(none)";
191
+ const refFix = reachableRefs.length > 0 ? `Run migration plan with ${reachableRefs.map((r) => `--from ${r.name}`).join(" or ")}.` : graphTipHash !== null ? `Run migration plan --from ${graphTipHash}.` : "Commit pending migrations first, then run migration plan.";
192
+ const nextActions = reachableRefs.length > 0 ? reachableRefs.map((ref) => runCommandAction(`Plan from ${ref.name}`, `prisma-cli migration plan --from ${ref.name}`)) : graphTipHash !== null ? [runCommandAction("Plan from the graph tip", `prisma-cli migration plan --from ${graphTipHash}`)] : [chooseAction("Commit pending migrations first, then run migration plan")];
193
+ return new ActionableCliError("MIGRATION.HASH_NOT_IN_GRAPH", `Resolved from-hash is not in the migration graph: ${resolvedHash}`, {
194
+ why: `The migration graph reaches ${reachableList}; resolved ${resolvedHash} isn't a graph node.`,
195
+ fix: refFix,
196
+ nextActions,
197
+ meta: {
198
+ resolvedHash,
199
+ reachableRefs: reachableRefs.map((r) => r.name),
200
+ ...graphTipHash !== null ? { graphTipHash } : {}
201
+ },
202
+ ...ifDefined("cause", options?.cause)
203
+ });
204
+ }
205
+ /**
206
+ * `viaRef: true` (the default) mirrors migration-tools' `errorRefNotResolvable`:
207
+ * a ref name with no pointer file, where the fallback hash isn't a graph
208
+ * node either — there's nothing to materialize a contract from.
209
+ * `viaRef: false` is a distinct, ref-independent case: an explicit `--from
210
+ * <hash>` that doesn't name a ref, on an empty migration graph, so there is
211
+ * no graph node and no ref to resolve a contract through.
212
+ */
213
+ function errorSnapshotMissing(identifier, options) {
214
+ const viaRef = options?.viaRef !== false;
215
+ const fix = viaRef ? `Create the ref with "prisma-cli ref set ${identifier} <hash>" (or advance it via "prisma-cli db update --advance-ref ${identifier}"), or pass a hash that is a node in the migration graph.` : `No contract source exists for hash "${identifier}" on an empty migration graph. Use --from with a ref name (its contract resolves through the snapshot store), or run db update first.`;
216
+ const nextActions = viaRef ? [
217
+ runCommandAction(`Create the ref "${identifier}"`, `prisma-cli ref set ${identifier} <hash>`),
218
+ runCommandAction("Or advance it from the database", `prisma-cli db update --advance-ref ${identifier}`),
219
+ chooseAction("Or pass a hash that is a node in the migration graph")
220
+ ] : [chooseAction("Pass --from a ref name, whose contract resolves through the snapshot store"), runCommandAction("Or populate the graph first", "prisma-cli db update")];
221
+ return new ActionableCliError("MIGRATION.SNAPSHOT_MISSING", viaRef ? `Ref "${identifier}" is not resolvable` : `No contract source for from-hash "${identifier}"`, {
222
+ why: viaRef ? `Ref "${identifier}" has no pointer file, and the hash being resolved is not a node in the migration graph either.` : `Hash "${identifier}" is not a node in the migration graph (the graph is empty), and it does not name a ref either.`,
223
+ fix,
224
+ nextActions,
225
+ meta: {
226
+ identifier,
227
+ viaRef
228
+ },
229
+ ...ifDefined("cause", options?.cause)
230
+ });
231
+ }
232
+ function errorMarkerMismatch(markerHash, reachableHashes, graphTip) {
233
+ const reachableList = reachableHashes.length > 0 ? reachableHashes.join(", ") : "(none — migration graph is empty)";
234
+ const planFromFix = graphTip !== null ? `Run \`prisma-cli migration plan --from ${graphTip}\` if the live marker is canonical and the on-disk graph needs catching up.` : "Run `prisma-cli migration plan` if the live marker is canonical and the on-disk graph needs catching up.";
235
+ const planCommand = graphTip !== null ? `prisma-cli migration plan --from ${graphTip}` : "prisma-cli migration plan";
236
+ return new ActionableCliError("MIGRATION.MARKER_MISMATCH", "Database marker is not reachable in the on-disk migration graph", {
237
+ why: `DB marker is ${markerHash}, but the on-disk migration graph reaches: ${reachableList}.`,
238
+ fix: [
239
+ planFromFix,
240
+ `Run \`prisma-cli ref set db ${markerHash}\` if the on-disk graph is canonical and the local \`db\` ref drifted.`,
241
+ "Investigate whether the database was migrated by an out-of-band process."
242
+ ].join("\n"),
243
+ nextActions: [
244
+ runCommandAction("Catch the on-disk graph up to the live marker", planCommand),
245
+ runCommandAction("Point the local db ref at the live marker", `prisma-cli ref set db ${markerHash}`),
246
+ chooseAction("Investigate whether the database was migrated by an out-of-band process")
247
+ ],
248
+ meta: {
249
+ markerHash,
250
+ reachableHashes: [...reachableHashes],
251
+ ...graphTip !== null ? { graphTip } : {}
252
+ }
253
+ });
254
+ }
255
+ const ROLLBACK_IS_DESTRUCTIVE = "A rollback (reverse) plan is expected to contain destructive (DROP) operations — review them before applying";
256
+ const NARROWER_CASES_NEED_A_HINT = "Narrower cases (rename inference, re-adding a required field without a safe default, or a type change that needs data) may additionally need a hint in the planned migration";
257
+ function errorPathUnreachable(failure) {
258
+ const meta = failure.meta ?? {};
259
+ const fromHashMeta = typeof meta["fromHash"] === "string" ? meta["fromHash"] : null;
260
+ const planFromHash = fromHashMeta === "<empty>" ? null : fromHashMeta;
261
+ const targetHash = typeof meta["targetHash"] === "string" ? meta["targetHash"] : typeof meta["target"] === "string" ? meta["target"] : null;
262
+ const deadEnds = meta["deadEnds"];
263
+ const deadEndsSuffix = Array.isArray(deadEnds) && deadEnds.length > 0 ? ` Dead-ends: ${deadEnds.map(String).join(", ")}.` : "";
264
+ const neverPlanned = meta["kind"] === "neverPlanned";
265
+ const planCommand = (() => {
266
+ if (neverPlanned) return "prisma-cli migration plan --name <slug>";
267
+ if (planFromHash !== null && targetHash !== null) return `prisma-cli migration plan --from ${planFromHash} --to ${targetHash} --name <slug>`;
268
+ if (targetHash !== null) return `prisma-cli migration plan --to ${targetHash} --name <slug>`;
269
+ if (planFromHash !== null) return `prisma-cli migration plan --from ${planFromHash} --name <slug>`;
270
+ return "prisma-cli migration plan";
271
+ })();
272
+ const applyCommand = targetHash !== null && !neverPlanned ? `prisma-cli migrate --to ${targetHash}` : "prisma-cli migrate";
273
+ return new ActionableCliError("MIGRATION.PATH_UNREACHABLE", failure.summary, {
274
+ why: failure.why ?? `Cannot reach target "${targetHash ?? "<unknown>"}" from current marker "${fromHashMeta ?? "<unknown>"}".${deadEndsSuffix}`,
275
+ fix: [
276
+ "Plan the missing edge, then apply it:",
277
+ ` 1. ${planCommand}`,
278
+ ` 2. ${applyCommand}`,
279
+ `${ROLLBACK_IS_DESTRUCTIVE}.`,
280
+ `${NARROWER_CASES_NEED_A_HINT}.`,
281
+ "Inspect the on-disk graph with `prisma-cli migration list`, or `prisma-cli migration show <bundle>` for any bundle in the path you expected."
282
+ ].join("\n"),
283
+ nextActions: [
284
+ runCommandAction("Plan the missing edge", planCommand),
285
+ runCommandAction("Apply it", applyCommand),
286
+ chooseAction(ROLLBACK_IS_DESTRUCTIVE),
287
+ chooseAction(NARROWER_CASES_NEED_A_HINT),
288
+ runCommandAction("Inspect the on-disk graph", "prisma-cli migration list"),
289
+ runCommandAction("Inspect a bundle in the path you expected", "prisma-cli migration show <bundle>")
290
+ ],
291
+ meta: { ...meta }
292
+ });
293
+ }
294
+ /**
295
+ * Shared "needs a live database" precondition for read verbs that consult the
296
+ * marker/ledger (`migration log`, `migration status`). A command needs both a
297
+ * connection string and a control-plane driver; either missing yields the same
298
+ * `CONFIG.DB_CONNECTION_REQUIRED` envelope with `meta.missingFlags` (canonical long-form flags
299
+ * per CLI Style Guide §Errors) so callers can react programmatically. Returns
300
+ * `null` when both are present.
301
+ */
302
+ function requireLiveDatabase(args) {
303
+ if (args.dbConnection && args.hasDriver) return null;
304
+ const missingFlags = args.dbConnection ? [] : ["--db"];
305
+ return errorDatabaseConnectionRequired({
306
+ why: args.why,
307
+ missingFlags,
308
+ ...ifDefined("commandName", args.commandName),
309
+ ...ifDefined("retryCommand", args.retryCommand)
310
+ });
311
+ }
312
+ /**
313
+ * Maps a `RefResolutionError` from the contract/migration reference
314
+ * resolver into a CLI structured error envelope.
315
+ */
316
+ /**
317
+ * A migration ref (dirName or hash-prefix) resolves in more than one contract
318
+ * space. The user must qualify with `--space <id>` to disambiguate.
319
+ */
320
+ function errorAmbiguousMigrationRef(ref, spaceIds) {
321
+ const spaceList = spaceIds.join(", ");
322
+ return new ActionableCliError("MIGRATION.AMBIGUOUS_MIGRATION_REF", `Ambiguous migration reference: "${ref}" resolves in multiple spaces — qualify with --space <id>`, {
323
+ why: `"${ref}" matches migrations in spaces: ${spaceList}.`,
324
+ fix: `Qualify with --space <id> to select one space. Available matching spaces: ${spaceList}.`,
325
+ nextActions: [chooseAction(`Qualify with --space <id>, one of: ${spaceList}`)],
326
+ meta: {
327
+ ref,
328
+ spaceIds: [...spaceIds]
329
+ }
330
+ });
331
+ }
332
+ function mapRefResolutionError(error) {
333
+ switch (error.kind) {
334
+ case "not-found": {
335
+ const fix = error.grammar === "contract" ? "Provide a valid contract hash, ref name, or migration directory name." : "Provide a valid migration directory name or migration hash.";
336
+ return new ActionableCliError("MIGRATION.REF_NOT_FOUND", `Not a known ${error.grammar} reference: "${error.input}"`, {
337
+ why: `No ${error.grammar} matching "${error.input}" exists in the migration graph or refs index.`,
338
+ fix,
339
+ nextActions: [chooseAction(fix)],
340
+ meta: {
341
+ input: error.input,
342
+ grammar: error.grammar
343
+ }
344
+ });
345
+ }
346
+ case "ambiguous": return new ActionableCliError("MIGRATION.REF_AMBIGUOUS", `Ambiguous ${error.grammar} reference: "${error.input}"`, {
347
+ why: `"${error.input}" matches multiple ${error.grammar}s: ${error.candidates.join(", ")}`,
348
+ fix: "Provide a longer prefix or use the full hash to disambiguate.",
349
+ nextActions: [chooseAction("Provide a longer prefix or use the full hash to disambiguate")],
350
+ meta: {
351
+ input: error.input,
352
+ candidates: error.candidates,
353
+ grammar: error.grammar
354
+ }
355
+ });
356
+ case "wrong-grammar": return new ActionableCliError("MIGRATION.REF_WRONG_GRAMMAR", error.message, {
357
+ why: error.message,
358
+ fix: error.fix,
359
+ nextActions: [chooseAction(error.fix)],
360
+ meta: {
361
+ input: error.input,
362
+ expectedGrammar: error.expectedGrammar
363
+ }
364
+ });
365
+ case "invalid-format": return new ActionableCliError("MIGRATION.REF_INVALID_FORMAT", `Invalid reference format: "${error.input}"`, {
366
+ why: error.reason,
367
+ fix: "Provide a valid contract hash, ref name, or migration directory name.",
368
+ nextActions: [chooseAction("Provide a valid contract hash, ref name, or migration directory name")],
369
+ meta: { input: error.input }
370
+ });
371
+ }
372
+ }
373
+ /**
374
+ * Asserts that all framework components are compatible with the expected family and target.
375
+ *
376
+ * This function validates that each component in the framework components array:
377
+ * - Has kind 'target', 'adapter', 'extension', or 'driver'
378
+ * - Has familyId matching expectedFamilyId
379
+ * - Has targetId matching expectedTargetId
380
+ *
381
+ * This validation happens at the CLI composition boundary, before passing components
382
+ * to typed planner/runner instances. It fills the gap between runtime validation
383
+ * (via the config loader's `collectConfigIssues` diagnostics) and compile-time
384
+ * type enforcement.
385
+ *
386
+ * @param expectedFamilyId - The expected family ID (e.g., 'sql')
387
+ * @param expectedTargetId - The expected target ID (e.g., 'postgres')
388
+ * @param frameworkComponents - Array of framework components to validate
389
+ * @returns The same array typed as TargetBoundComponentDescriptor
390
+ * @throws CliStructuredError if any component is incompatible
391
+ *
392
+ * @example
393
+ * ```ts
394
+ * const configResult = await loadConfigForSections(undefined, ['family', 'target', 'adapter', 'extensions']);
395
+ * const config = configResult.assertOk();
396
+ * const frameworkComponents = [config.target, config.adapter, ...(config.extensions ?? [])];
397
+ *
398
+ * // Validate and type-narrow components before passing to planner
399
+ * const typedComponents = assertFrameworkComponentsCompatible(
400
+ * config.family.familyId,
401
+ * config.target.targetId,
402
+ * frameworkComponents
403
+ * );
404
+ *
405
+ * const planner = target.migrations.createPlanner(familyInstance);
406
+ * planner.plan({ contract, schema, policy, frameworkComponents: typedComponents });
407
+ * ```
408
+ */
409
+ function assertFrameworkComponentsCompatible(expectedFamilyId, expectedTargetId, frameworkComponents) {
410
+ for (let i = 0; i < frameworkComponents.length; i++) {
411
+ const component = frameworkComponents[i];
412
+ if (typeof component !== "object" || component === null) throw errorConfigValidation$1("frameworkComponents[]", { why: `Framework component at index ${i} must be an object` });
413
+ const record = component;
414
+ if (!Object.hasOwn(record, "kind")) throw errorConfigValidation$1("frameworkComponents[].kind", { why: `Framework component at index ${i} must have 'kind' property` });
415
+ const kind = record["kind"];
416
+ if (kind !== "target" && kind !== "adapter" && kind !== "extension" && kind !== "driver") throw errorConfigValidation$1("frameworkComponents[].kind", { why: `Framework component at index ${i} has invalid kind '${String(kind)}' (must be 'target', 'adapter', 'extension', or 'driver')` });
417
+ if (!Object.hasOwn(record, "familyId")) throw errorConfigValidation$1("frameworkComponents[].familyId", { why: `Framework component at index ${i} (kind: ${String(kind)}) must have 'familyId' property` });
418
+ const familyId = record["familyId"];
419
+ if (familyId !== expectedFamilyId) throw errorConfigValidation$1("frameworkComponents[].familyId", { why: `Framework component at index ${i} (kind: ${String(kind)}) has familyId '${String(familyId)}' but expected '${expectedFamilyId}'` });
420
+ if (!Object.hasOwn(record, "targetId")) throw errorConfigValidation$1("frameworkComponents[].targetId", { why: `Framework component at index ${i} (kind: ${String(kind)}) must have 'targetId' property` });
421
+ const targetId = record["targetId"];
422
+ if (targetId !== expectedTargetId) throw errorConfigValidation$1("frameworkComponents[].targetId", { why: `Framework component at index ${i} (kind: ${String(kind)}) has targetId '${String(targetId)}' but expected '${expectedTargetId}'` });
423
+ }
424
+ return frameworkComponents;
425
+ }
426
+ function isPlainObject(value) {
427
+ return typeof value === "object" && value !== null && !Array.isArray(value);
428
+ }
429
+ function sortDeep(value) {
430
+ if (Array.isArray(value)) return value.map(sortDeep);
431
+ if (!isPlainObject(value)) return value;
432
+ const entries = Object.entries(value).sort(([a], [b]) => a.localeCompare(b));
433
+ const next = {};
434
+ for (const [key, child] of entries) next[key] = sortDeep(child);
435
+ return next;
436
+ }
437
+ function sortDeepTyped(value) {
438
+ return sortDeep(value);
439
+ }
440
+ function extractExtensionPackMeta(component) {
441
+ const { kind, id, version, capabilities, types } = component;
442
+ const base = {
443
+ kind,
444
+ id,
445
+ familyId: component.familyId,
446
+ targetId: component.targetId,
447
+ version
448
+ };
449
+ if (capabilities) base["capabilities"] = capabilities;
450
+ if (types) if (types.codecTypes) {
451
+ const { controlPlaneHooks: _, codecDescriptors: _cd, ...cleanedCodecTypes } = types.codecTypes;
452
+ base["types"] = {
453
+ ...types,
454
+ codecTypes: cleanedCodecTypes
455
+ };
456
+ } else base["types"] = types;
457
+ return base;
458
+ }
459
+ /**
460
+ * Enriches a raw contract with framework-derived metadata: capabilities from all component descriptors and extension pack metadata from extension descriptors. Produces deterministically sorted output.
461
+ */
462
+ function enrichContract(ir, components) {
463
+ const mergedCapabilities = mergeCapabilityMatrices(ir.capabilities, components);
464
+ const extensionsMeta = {};
465
+ for (const component of components) if (component.kind === "extension") extensionsMeta[component.id] = extractExtensionPackMeta(component);
466
+ const extensions = Object.keys(extensionsMeta).length > 0 ? {
467
+ ...ir.extensions,
468
+ ...extensionsMeta
469
+ } : ir.extensions;
470
+ return {
471
+ ...ir,
472
+ capabilities: sortDeepTyped(mergedCapabilities),
473
+ extensions: sortDeepTyped(extensions)
474
+ };
475
+ }
476
+ /**
477
+ * Resolves the absolute path to contract.json from the config.
478
+ */
479
+ function resolveContractPath(config) {
480
+ if (config.contract?.output === void 0) throw errorRuntime("CONFIG.VALIDATION_FAILED", "config.contract.output is required to resolve the contract path", {
481
+ why: "CLI commands read the emitted contract from config.contract.output; the config has no value to read.",
482
+ fix: "Ensure your prisma.config.ts goes through `defineConfig()`, which normalises a default output when the provider supplies an input path, or set `contract.output` explicitly."
483
+ });
484
+ return resolve(config.contract.output);
485
+ }
486
+ /**
487
+ * Resolves the migrations directory and config path from CLI options.
488
+ * Shared by migrate, migration-plan, and migration-status.
489
+ *
490
+ * - `migrationsDir` is the project's top-level `migrations/` directory
491
+ * (the root that the aggregate loader walks for every contract space).
492
+ * - `appMigrationsDir` is the app subspace directory under it
493
+ * (`<migrationsDir>/<APP_SPACE_ID>/`). Every per-app reader / writer
494
+ * (`migration new`, `migration plan`, `migrate`,
495
+ * `migration status`, `migration show`, `migration ref`) operates on
496
+ * this directory. Extensions own their own `migrations/<spaceId>/`.
497
+ * - `refsDir` is the app's refs directory (`<appMigrationsDir>/refs/`).
498
+ * The framework does not maintain refs at the migrations root.
499
+ *
500
+ * `cwd` is the directory the command was invoked from; every relative path in
501
+ * the result is computed against it.
502
+ */
503
+ function resolveMigrationPaths(configOption, config, cwd) {
504
+ const resolvedConfigPath = configOption ? resolve(cwd, configOption) : void 0;
505
+ const configPath = resolvedConfigPath ? relative(cwd, resolvedConfigPath) : "prisma.config.ts";
506
+ const migrationsDir = resolve(resolvedConfigPath ? resolve(resolvedConfigPath, "..") : cwd, config.migrations?.dir ?? "migrations");
507
+ const migrationsRelative = relative(cwd, migrationsDir);
508
+ const appMigrationsDir = spaceMigrationDirectory(migrationsDir, APP_SPACE_ID$1);
509
+ return {
510
+ configPath,
511
+ migrationsDir,
512
+ migrationsRelative,
513
+ appMigrationsDir,
514
+ appMigrationsRelative: relative(cwd, appMigrationsDir),
515
+ refsDir: resolve(appMigrationsDir, "refs")
516
+ };
517
+ }
518
+ function collectDeclaredInvariants(graph) {
519
+ const declared = /* @__PURE__ */ new Set();
520
+ for (const edges of graph.forwardChain.values()) for (const edge of edges) for (const inv of edge.invariants) declared.add(inv);
521
+ return declared;
522
+ }
523
+ /**
524
+ * Maps a `MigrationEdge` to the structural-edge shape used in the
525
+ * `MIGRATION.NO_INVARIANT_PATH` error envelope. Shared between
526
+ * `migrate` and `migration status` so both commands surface
527
+ * the same JSON wire shape when an invariant-aware route is unsatisfiable.
528
+ */
529
+ function toStructuralEdge(edge) {
530
+ return {
531
+ dirName: edge.dirName,
532
+ migrationHash: edge.migrationHash,
533
+ from: edge.from,
534
+ to: edge.to,
535
+ invariants: edge.invariants
536
+ };
537
+ }
538
+ function targetSupportsMigrations(target) {
539
+ return hasMigrations(target);
540
+ }
541
+ /**
542
+ * Hangs up without letting the hang-up decide the command's result. A rejection out of a
543
+ * `finally` replaces the value the `try`/`catch` already returned, so an unguarded close turns a
544
+ * mapped connection error into an unmapped one — and that is the case it hits most, because a
545
+ * `connect()` that failed leaves nothing to close.
546
+ */
547
+ async function closeQuietly(client) {
548
+ try {
549
+ await client.close();
550
+ } catch {}
551
+ }
552
+ function getTargetMigrations(target) {
553
+ return hasMigrations(target) ? target.migrations : void 0;
554
+ }
555
+ /**
556
+ * Reads and parses contract.json, validating the framework-level envelope
557
+ * fields (storageHash, schemaVersion, target, targetFamily).
558
+ *
559
+ * Family-specific validation (storage structure, codec mappings, etc.)
560
+ * happens downstream in the control client via the family instance.
561
+ */
562
+ async function readContractEnvelope(config) {
563
+ const contractPath = resolveContractPath(config);
564
+ const content = await readFile(contractPath, "utf-8");
565
+ const json = JSON.parse(content);
566
+ const { schemaVersion, target, targetFamily, profileHash } = json;
567
+ const storageHash = json["storage"]?.["storageHash"];
568
+ if (typeof storageHash !== "string") throw new CliStructuredError$1("CONTRACT.VALIDATION_FAILED", `Contract at ${relative(process.cwd(), contractPath)} is missing a valid storage.storageHash. Run \`prisma-cli contract emit\` to regenerate.`, { where: { path: contractPath } });
569
+ if (typeof schemaVersion !== "string") throw new CliStructuredError$1("CONTRACT.VALIDATION_FAILED", `Contract at ${relative(process.cwd(), contractPath)} is missing schemaVersion.`, { where: { path: contractPath } });
570
+ if (typeof target !== "string") throw new CliStructuredError$1("CONTRACT.VALIDATION_FAILED", `Contract at ${relative(process.cwd(), contractPath)} is missing target.`, { where: { path: contractPath } });
571
+ if (typeof targetFamily !== "string") throw new CliStructuredError$1("CONTRACT.VALIDATION_FAILED", `Contract at ${relative(process.cwd(), contractPath)} is missing targetFamily.`, { where: { path: contractPath } });
572
+ return {
573
+ ...json,
574
+ storageHash,
575
+ schemaVersion,
576
+ target,
577
+ targetFamily,
578
+ ...typeof profileHash === "string" ? { profileHash } : {}
579
+ };
580
+ }
581
+ /**
582
+ * Masks credentials in a database connection URL.
583
+ * Handles standard URLs (username + password + query params) and libpq-style key=value strings.
584
+ */
585
+ function maskConnectionUrl(url) {
586
+ try {
587
+ const parsed = new URL(url);
588
+ if (parsed.username) parsed.username = "****";
589
+ if (parsed.password) parsed.password = "****";
590
+ for (const key of [...parsed.searchParams.keys()]) if (/password/i.test(key)) parsed.searchParams.set(key, "****");
591
+ return parsed.toString();
592
+ } catch {
593
+ return url.replace(/password\s*=\s*\S+/gi, "password=****").replace(/user\s*=\s*\S+/gi, "user=****");
594
+ }
595
+ }
596
+ /**
597
+ * Strips raw connection URL fragments from an error message to prevent credential leakage.
598
+ * Call this before surfacing driver errors to the user.
599
+ */
600
+ function sanitizeErrorMessage(message, connectionUrl) {
601
+ if (!connectionUrl) return message;
602
+ try {
603
+ const parsed = new URL(connectionUrl);
604
+ let sanitized = message;
605
+ sanitized = sanitized.replaceAll(connectionUrl, maskConnectionUrl(connectionUrl));
606
+ if (parsed.password) sanitized = sanitized.replaceAll(parsed.password, "****");
607
+ if (parsed.username) sanitized = sanitized.replaceAll(parsed.username, "****");
608
+ return sanitized;
609
+ } catch {
610
+ return message.replace(/password\s*=\s*\S+/gi, "password=****").replace(/user\s*=\s*\S+/gi, "user=****");
611
+ }
612
+ }
613
+ /**
614
+ * Project the CLI's `Config.extensions` array into the canonical
615
+ * {@link ExtensionPackInput} shape. The single `as ExtensionPackLike`
616
+ * structural cast in the CLI lives inside this function.
617
+ */
618
+ function toExtensionInputs(extensions) {
619
+ return extensions.map((raw) => {
620
+ const pack = raw;
621
+ if (pack.contractSpace === void 0) return {
622
+ id: pack.id,
623
+ targetId: pack.targetId
624
+ };
625
+ return {
626
+ id: pack.id,
627
+ targetId: pack.targetId,
628
+ contractSpace: {
629
+ contractJson: pack.contractSpace.contractJson,
630
+ headRef: pack.contractSpace.headRef,
631
+ migrations: pack.contractSpace.migrations ?? []
632
+ }
633
+ };
634
+ });
635
+ }
636
+ /**
637
+ * Minimal aggregate-loader projection that extracts `id` + `targetId`
638
+ * from raw extension pack descriptors **without invoking any
639
+ * `contractSpace` accessor**. Inspects the own-property descriptor so
640
+ * that getter-backed `contractSpace` declarations are detected but
641
+ * never called.
642
+ *
643
+ * Inclusion semantics match {@link toDeclaredExtensions}: a data
644
+ * property whose value is explicitly `undefined` is treated as "no
645
+ * contract-space declaration" and skipped, mirroring the
646
+ * `pack.contractSpace === undefined` check used on canonicalised
647
+ * inputs. Prototype-chain `contractSpace` properties (no own
648
+ * descriptor) are also skipped.
649
+ *
650
+ * This variant must be used by `buildContractSpaceAggregate` so that
651
+ * the aggregate path (including `db verify`) never reads
652
+ * `contractSpace.contractJson` from extension descriptors — the loader
653
+ * always reads the contract from on-disk artefacts instead.
654
+ */
655
+ function toDeclaredExtensionsFromRaw(extensions) {
656
+ const entries = [];
657
+ for (const raw of extensions) {
658
+ if (typeof raw !== "object" || raw === null) continue;
659
+ const descriptor = Object.getOwnPropertyDescriptor(raw, "contractSpace");
660
+ if (descriptor === void 0) continue;
661
+ if ("value" in descriptor && descriptor.value === void 0) continue;
662
+ const pack = raw;
663
+ entries.push({
664
+ id: pack.id,
665
+ targetId: pack.targetId
666
+ });
667
+ }
668
+ return entries;
669
+ }
670
+ const CONTRACT_SPACES_DOCS_URL = "https://pris.ly/contract-spaces";
671
+ function contractSpaceViolationError(summary, options) {
672
+ return new CliStructuredError$1("MIGRATION.CONTRACT_SPACE_VIOLATION", summary, {
673
+ why: options.why,
674
+ fix: options.fix,
675
+ docsUrl: CONTRACT_SPACES_DOCS_URL,
676
+ meta: { violations: options.violations }
677
+ });
678
+ }
679
+ /**
680
+ * Build the `MIGRATION.CONTRACT_SPACE_VIOLATION` structured-error envelope for a contract-space
681
+ * target mismatch. Shared between the declared-extension precheck (the
682
+ * descriptor's configured target disagrees with the project target) and
683
+ * the on-disk-contract check surfaced by `checkIntegrity`.
684
+ */
685
+ function targetMismatchError(spaceId, expected, actual) {
686
+ return contractSpaceViolationError(`Contract-space target mismatch for "${spaceId}"`, {
687
+ why: `Space "${spaceId}" targets "${actual}" but the project's adapter targets "${expected}".`,
688
+ fix: "Update the extension descriptor to target the configured database, or change the project adapter.",
689
+ violations: [{
690
+ kind: "targetMismatch",
691
+ spaceId,
692
+ expected,
693
+ actual
694
+ }]
695
+ });
696
+ }
697
+ /**
698
+ * Human-readable detail for an integrity violation, used as the `why`
699
+ * of the `integrityFailure` envelope. Mirrors the messages the prior
700
+ * throw-on-load loader produced so downstream consumers see the same
701
+ * text for the same on-disk state.
702
+ */
703
+ function describeIntegrityViolation(violation) {
704
+ switch (violation.kind) {
705
+ case "hashMismatch": return `Migration "${violation.dirName}" stored hash "${violation.stored}" does not match computed hash "${violation.computed}".`;
706
+ case "providedInvariantsMismatch": return `Migration "${violation.dirName}" providedInvariants in migration.json disagrees with ops.json.`;
707
+ case "packageUnloadable": return `Migration "${violation.dirName}" could not be loaded: ${violation.detail}`;
708
+ case "sameSourceAndTarget": return `Migration "${violation.dirName}" has source equal to target (${violation.hash}) with no data invariant — a true no-op self-edge.`;
709
+ case "headRefMissing": return `Head ref \`refs/head.json\` is missing for contract space "${violation.spaceId}".`;
710
+ case "headRefNotInGraph": return `Head ref ${violation.hash} for contract space "${violation.spaceId}" is not present in the migration graph.`;
711
+ case "refUnreadable": return `Ref "${violation.refName}" for contract space "${violation.spaceId}" is unreadable: ${violation.detail}`;
712
+ case "duplicateMigrationHash": return `Multiple migrations in space "${violation.spaceId}" share migrationHash "${violation.migrationHash}" (${violation.dirNames.join(", ")}).`;
713
+ default: {
714
+ const spaceId = "spaceId" in violation ? violation.spaceId : "*";
715
+ return `Integrity violation "${violation.kind}" for contract space "${spaceId}".`;
716
+ }
717
+ }
718
+ }
719
+ /**
720
+ * Map the integrity violations `checkIntegrity` reports into a single
721
+ * CLI structured-error envelope, preserving the error codes the prior
722
+ * throw-on-load loader emitted: `MIGRATION.CONTRACT_SPACE_LAYOUT_VIOLATION`
723
+ * (layout drift, bundled) and `MIGRATION.CONTRACT_SPACE_VIOLATION` (target /
724
+ * disjointness / contract-validation / structural integrity). Returns
725
+ * `null` when there is nothing to refuse on.
726
+ *
727
+ * Precedence reproduces the prior loader's first-failure ordering:
728
+ * layout drift first (every offence bundled into one envelope), then
729
+ * target mismatch, then disjointness, then a contract-validation
730
+ * failure, then any remaining structural integrity violation.
731
+ */
732
+ function mapIntegrityViolations(violations) {
733
+ if (violations.length === 0) return null;
734
+ const layout = violations.filter((v) => v.kind === "orphanSpaceDir" || v.kind === "declaredButUnmigrated");
735
+ if (layout.length > 0) {
736
+ const lines = layout.map((v) => `- [${v.kind}] ${v.spaceId}`);
737
+ return new CliStructuredError$1("MIGRATION.CONTRACT_SPACE_LAYOUT_VIOLATION", layout.length === 1 ? "Contract-space layout violation detected" : `Contract-space layout violations detected (${layout.length})`, {
738
+ why: `The on-disk \`migrations/\` directory and your \`extensions\` declaration are not in agreement.\n${lines.join("\n")}`,
739
+ fix: "Declare the extension in `extensions` and re-emit its contract-space artefacts, or remove the orphan `migrations/<space>` directory.",
740
+ docsUrl: CONTRACT_SPACES_DOCS_URL,
741
+ meta: { violations: layout }
742
+ });
743
+ }
744
+ const targetMismatch = violations.find((v) => v.kind === "targetMismatch");
745
+ if (targetMismatch && targetMismatch.kind === "targetMismatch") return targetMismatchError(targetMismatch.spaceId, targetMismatch.expected, targetMismatch.actual);
746
+ const disjointness = violations.find((v) => v.kind === "disjointness");
747
+ if (disjointness && disjointness.kind === "disjointness") return contractSpaceViolationError(`Contract-space disjointness violation: storage element "${disjointness.element}" claimed by multiple spaces`, {
748
+ why: `Spaces ${disjointness.claimedBy.map((s) => `"${s}"`).join(", ")} all claim the storage element "${disjointness.element}". Each storage element must be owned by exactly one contract space.`,
749
+ fix: "Update the conflicting contracts so each storage element is claimed by exactly one space.",
750
+ violations: [disjointness]
751
+ });
752
+ const contractUnreadable = violations.find((v) => v.kind === "contractUnreadable");
753
+ if (contractUnreadable && contractUnreadable.kind === "contractUnreadable") return contractSpaceViolationError(`Contract-space contract validation failed for "${contractUnreadable.spaceId}"`, {
754
+ why: contractUnreadable.detail,
755
+ fix: "Re-emit the extension contract with `prisma-cli contract emit`, or fix the extension pack descriptor producing the invalid contract.",
756
+ violations: [contractUnreadable]
757
+ });
758
+ const structural = violations[0];
759
+ return contractSpaceViolationError(`Contract-space integrity failure for "${"spaceId" in structural ? structural.spaceId : "*"}"`, {
760
+ why: describeIntegrityViolation(structural),
761
+ fix: "Re-emit the affected migration package(s) or restore the on-disk `migrations/` directory from version control.",
762
+ violations: [structural]
763
+ });
764
+ }
765
+ function declaredExtensionsFromInputs(extensions) {
766
+ return toDeclaredExtensionsFromRaw(extensions);
767
+ }
768
+ /**
769
+ * Reject extension descriptors whose configured target disagrees with
770
+ * the project target before any on-disk read.
771
+ */
772
+ function refuseDeclaredExtensionTargetMismatch(inputs) {
773
+ for (const declared of declaredExtensionsFromInputs(inputs.extensions)) if (declared.targetId !== inputs.targetId) return targetMismatchError(declared.id, inputs.targetId, declared.targetId);
774
+ return null;
775
+ }
776
+ /**
777
+ * Load the tolerant {@link ContractSpaceAggregate} once at the CLI
778
+ * surface. Construction never throws on disk content; callers query
779
+ * {@link ContractSpaceAggregate.app} / extension facets instead of
780
+ * re-reading `migrations/`.
781
+ */
782
+ async function loadContractSpaceAggregateForCli(inputs) {
783
+ const targetFailure = refuseDeclaredExtensionTargetMismatch(inputs);
784
+ if (targetFailure) return notOk(targetFailure);
785
+ return ok(await loadContractSpaceAggregate({
786
+ migrationsDir: inputs.migrationsDir,
787
+ deserializeContract: inputs.deserializeContract,
788
+ appContract: inputs.appContract
789
+ }));
790
+ }
791
+ /**
792
+ * Run `checkIntegrity` on a loaded aggregate and map violations into
793
+ * the contract-space refusal envelope, or return `null` when the model
794
+ * is acceptable for the requested check scope.
795
+ */
796
+ function refuseContractSpaceIntegrity(aggregate, options) {
797
+ return mapIntegrityViolations(aggregate.checkIntegrity(options));
798
+ }
799
+ const PACKAGE_CORRUPTION_KINDS = /* @__PURE__ */ new Set([
800
+ "hashMismatch",
801
+ "providedInvariantsMismatch",
802
+ "packageUnloadable"
803
+ ]);
804
+ /**
805
+ * Reader-subset integrity refusal for `migration status`: package
806
+ * corruptions only (`hashMismatch`, `providedInvariantsMismatch`,
807
+ * `packageUnloadable`).
808
+ */
809
+ function refusePackageCorruptionOnAggregate(aggregate) {
810
+ return mapIntegrityViolations(aggregate.checkIntegrity().filter((v) => PACKAGE_CORRUPTION_KINDS.has(v.kind)));
811
+ }
812
+ /**
813
+ * Construct the tolerant {@link ContractSpaceAggregate} at the CLI
814
+ * surface and apply the explicit integrity refusal.
815
+ *
816
+ * App-space migration packages are read from `migrations/<app>/` by the
817
+ * loader itself; callers no longer thread them through.
818
+ */
819
+ async function buildContractSpaceAggregate(inputs) {
820
+ const declaredExtensions = declaredExtensionsFromInputs(inputs.extensions);
821
+ const loaded = await loadContractSpaceAggregateForCli(inputs);
822
+ if (!loaded.ok) return loaded;
823
+ const failure = refuseContractSpaceIntegrity(loaded.value, {
824
+ declaredExtensions,
825
+ checkContracts: true
826
+ });
827
+ if (failure) return notOk(failure);
828
+ return ok(loaded.value);
829
+ }
830
+ /**
831
+ * Build a minimal app {@link Contract} carrying only the project's
832
+ * contract-identity (`storage.storageHash` + `target` / `targetFamily`)
833
+ * when the real `contract.json` is absent or undeserializable.
834
+ *
835
+ * `loadContractSpaceAggregate` requires an `appContract` to synthesise the
836
+ * app space's head ref from its storage hash. Read commands query only that
837
+ * hash and the target — never `models` — so an empty-`models` stand-in is
838
+ * sufficient for them. It is *not* a valid contract for any consumer that
839
+ * reads schema, which is why this is confined to the read-aggregate path.
840
+ */
841
+ function appContractStandInFromIdentity(args) {
842
+ return blindCast({
843
+ storage: { storageHash: args.contractHash },
844
+ schemaVersion: "0.0.0",
845
+ target: args.targetId,
846
+ targetFamily: args.targetFamily,
847
+ models: {},
848
+ profileHash: EMPTY_CONTRACT_HASH
849
+ });
850
+ }
851
+ async function loadContractRawSafely(config) {
852
+ try {
853
+ const raw = await readFile(resolveContractPath(config), "utf-8");
854
+ return JSON.parse(raw);
855
+ } catch {
856
+ return null;
857
+ }
858
+ }
859
+ /**
860
+ * Tolerant {@link ContractSpaceAggregate} assembly for read-only CLI
861
+ * commands. No integrity gate — callers query `aggregate.app` (or other
862
+ * facets) without re-reading `migrations/`. When `contract.json` is absent
863
+ * or undeserializable, the app contract falls back to an identity-only
864
+ * stand-in ({@link appContractStandInFromIdentity}), so these commands
865
+ * load without requiring a readable contract.
866
+ */
867
+ async function buildReadAggregate(config, options) {
868
+ let contractHash = EMPTY_CONTRACT_HASH;
869
+ try {
870
+ contractHash = (await readContractEnvelope(config)).storageHash;
871
+ } catch {}
872
+ try {
873
+ const contractRawForAggregate = await loadContractRawSafely(config);
874
+ const stack = createControlStack(config);
875
+ const familyInstance = config.family.create(stack);
876
+ const deserializeContract = (json) => familyInstance.deserializeContract(json);
877
+ let appContractForLoad = appContractStandInFromIdentity({
878
+ contractHash,
879
+ targetId: config.target.id,
880
+ targetFamily: config.target.familyId
881
+ });
882
+ if (contractRawForAggregate !== null) try {
883
+ appContractForLoad = deserializeContract(contractRawForAggregate);
884
+ } catch {}
885
+ const loaded = await loadContractSpaceAggregateForCli({
886
+ targetId: config.target.id,
887
+ migrationsDir: options.migrationsDir,
888
+ appContract: appContractForLoad,
889
+ extensions: config.extensions ?? [],
890
+ deserializeContract
891
+ });
892
+ if (!loaded.ok) return loaded;
893
+ return ok({
894
+ aggregate: loaded.value,
895
+ contractHash
896
+ });
897
+ } catch (error) {
898
+ if (MigrationToolsError.is(error)) return notOk(error);
899
+ return notOk(errorUnexpected(error instanceof Error ? error.message : String(error), { why: `Failed to read migrations directory: ${error instanceof Error ? error.message : String(error)}` }));
900
+ }
901
+ }
902
+ /**
903
+ * Strips operation objects to their public shape (id, label, operationClass).
904
+ * Used at the API boundary to avoid leaking internal fields (precheck, execute, postcheck, etc.).
905
+ */
906
+ function stripOperations(operations) {
907
+ return operations.map((op) => ({
908
+ id: op.id,
909
+ label: op.label,
910
+ operationClass: op.operationClass
911
+ }));
912
+ }
913
+ /**
914
+ * Content identity of a `db update` plan: what it does (the ordered display
915
+ * operations) and where it lands (the destination storage hash). A
916
+ * `DESTRUCTIVE_CHANGES` refusal names the refused plan by this hash, and the
917
+ * apply that carries the consent back recomputes it to prove it is still
918
+ * applying the plan that was consented to.
919
+ */
920
+ function computePlanHash(plan) {
921
+ const canonical = canonicalizeJson({
922
+ operations: plan.operations,
923
+ destination: { storageHash: plan.destination.storageHash }
924
+ });
925
+ return createHash("sha256").update(canonical).digest("hex");
926
+ }
927
+ /**
928
+ * Span id emitted via `onProgress` for the run phase. Stable
929
+ * identifier consumed by the structured-output renderer and by tests.
930
+ */
931
+ const RUN_SPAN_ID = "apply";
932
+ /**
933
+ * Runner-driving tail shared by every run caller — `db init`,
934
+ * `db update`, and `migrate`. Consumes already-resolved per-space
935
+ * plans (the planner-vs-replay distinction is owned by the caller) and
936
+ * dispatches them to the runner in canonical order.
937
+ *
938
+ * Marker advancement is part of the runner's per-space transaction
939
+ * (the SQL family runner writes the marker as the last step of each
940
+ * space's transaction), so this primitive does not advance markers
941
+ * separately — by the time `execute` returns ok, every
942
+ * space's marker has been advanced to its plan's destination.
943
+ *
944
+ * Span emission (`spanStart 'apply'` / `spanEnd 'apply'`) is owned here
945
+ * so callers don't have to duplicate it; the `action` field on each
946
+ * progress event is taken from the caller's `action` argument.
947
+ */
948
+ async function runMigration(inputs) {
949
+ const { aggregate, perSpacePlans, applyOrder, driver, familyInstance, migrations, frameworkComponents, policy, action, onProgress } = inputs;
950
+ const orderedResolutions = collectOrdered(applyOrder, perSpacePlans);
951
+ const runner = migrations.createRunner(familyInstance);
952
+ onProgress?.({
953
+ action,
954
+ kind: "spanStart",
955
+ spanId: RUN_SPAN_ID,
956
+ label: progressLabelForAction(action)
957
+ });
958
+ const perSpaceOptions = orderedResolutions.map((r) => ({
959
+ space: r.spaceId,
960
+ plan: r.entry.plan,
961
+ driver,
962
+ destinationContract: r.entry.destinationContract,
963
+ policy,
964
+ frameworkComponents,
965
+ migrationEdges: r.entry.migrationEdges,
966
+ strictVerification: false
967
+ }));
968
+ const runnerResult = await runner.execute({
969
+ driver,
970
+ perSpaceOptions
971
+ });
972
+ if (!runnerResult.ok) {
973
+ onProgress?.({
974
+ action,
975
+ kind: "spanEnd",
976
+ spanId: RUN_SPAN_ID,
977
+ outcome: "error"
978
+ });
979
+ return notOk({
980
+ summary: runnerResult.failure.summary,
981
+ ...ifDefined("why", runnerResult.failure.why),
982
+ meta: {
983
+ ...runnerResult.failure.meta ?? {},
984
+ failingSpace: runnerResult.failure.failingSpace,
985
+ runnerErrorCode: runnerResult.failure.code
986
+ },
987
+ cause: runnerResult.failure
988
+ });
989
+ }
990
+ onProgress?.({
991
+ action,
992
+ kind: "spanEnd",
993
+ spanId: RUN_SPAN_ID,
994
+ outcome: "ok"
995
+ });
996
+ return ok({
997
+ orderedResolutions,
998
+ totalOpsPlanned: runnerResult.value.perSpaceResults.reduce((sum, r) => sum + r.value.operationsPlanned, 0),
999
+ totalOpsExecuted: runnerResult.value.perSpaceResults.reduce((sum, r) => sum + r.value.operationsExecuted, 0),
1000
+ perSpace: buildPerSpaceBreakdown(orderedResolutions, aggregate.app.spaceId, { includeMarkers: true })
1001
+ });
1002
+ }
1003
+ /**
1004
+ * Project the planner's per-space resolutions into the
1005
+ * `PerSpaceExecutionEntry[]` shape the CLI surfaces.
1006
+ *
1007
+ * `includeMarkers` is `true` for apply-mode (each space's marker is
1008
+ * the `destination.storageHash` of its plan, which the runner
1009
+ * advances as the last step of each space's transaction) and `false`
1010
+ * for plan-mode (no marker has been written yet).
1011
+ *
1012
+ * Exported alongside {@link runMigration} so plan-mode callers can
1013
+ * assemble the same per-space block without going through the runner.
1014
+ */
1015
+ function buildPerSpaceBreakdown(orderedResolutions, appSpaceId, options) {
1016
+ return orderedResolutions.map((r) => {
1017
+ const operations = r.entry.displayOps.map((op) => ({
1018
+ id: op.id,
1019
+ label: op.label,
1020
+ operationClass: op.operationClass
1021
+ }));
1022
+ const base = {
1023
+ spaceId: r.spaceId,
1024
+ kind: r.spaceId === appSpaceId ? "app" : "extension",
1025
+ operations
1026
+ };
1027
+ if (!options.includeMarkers) return base;
1028
+ return {
1029
+ ...base,
1030
+ marker: { storageHash: r.entry.plan.destination.storageHash }
1031
+ };
1032
+ });
1033
+ }
1034
+ /**
1035
+ * Materialise the `applyOrder` ordering into resolved per-space
1036
+ * entries. Throws if the planner output is missing a contract space listed
1037
+ * in `applyOrder` — a wiring bug that should never reach runtime.
1038
+ *
1039
+ * Exported so callers building their own success envelopes after a
1040
+ * plan-mode dispatch can replay the same ordering.
1041
+ */
1042
+ function collectOrdered(applyOrder, perSpace) {
1043
+ return applyOrder.map((spaceId) => {
1044
+ const entry = perSpace.get(spaceId);
1045
+ if (!entry) throw new InternalError(`planner output missing per-space plan for "${spaceId}"`);
1046
+ return {
1047
+ spaceId,
1048
+ entry
1049
+ };
1050
+ });
1051
+ }
1052
+ /**
1053
+ * Action-appropriate label for the `spanStart` event the run
1054
+ * primitive emits. `runMigration` is shared by `db init`, `db update`,
1055
+ * and `migrate`; the span label tracks the user-visible action
1056
+ * so structured-progress output reads naturally for each surface.
1057
+ */
1058
+ function progressLabelForAction(action) {
1059
+ switch (action) {
1060
+ case "dbInit": return "Initialising database across spaces";
1061
+ case "dbUpdate": return "Updating database across spaces";
1062
+ case "migrate": return "Running migration plan across spaces";
1063
+ }
1064
+ }
1065
+ /**
1066
+ * Span IDs emitted via `onProgress` during the run flow.
1067
+ * Stable identifiers consumed by the structured-output renderer and by
1068
+ * tests asserting on span ids. The `apply` span itself is owned by
1069
+ * the {@link runMigration} primitive — only the introspect / plan
1070
+ * spans are emitted directly here.
1071
+ */
1072
+ const SPAN_IDS$1 = {
1073
+ introspect: "introspect",
1074
+ plan: "plan"
1075
+ };
1076
+ /**
1077
+ * Loader → planner → runner pipeline shared by `db init` and `db update`.
1078
+ *
1079
+ * The pipeline:
1080
+ *
1081
+ * 1. **Load**: build a {@link ContractSpaceAggregate} from the descriptor
1082
+ * set + on-disk on-disk artefacts. Any layout / drift / disjointness /
1083
+ * integrity violation short-circuits with a structured error.
1084
+ * 2. **Read DB state**: marker rows (`familyInstance.readAllMarkers`)
1085
+ * + introspected schema (`familyInstance.introspect`).
1086
+ * 3. **Plan**: {@link planMigration} chooses `resolveRecordedPath` vs
1087
+ * `planFromDiff` per space according to `callerPolicy.ignoreGraphFor`.
1088
+ * The app space is forced through `planFromDiff` (today's daily-driver
1089
+ * behaviour); every extension space walks its on-disk graph via
1090
+ * `resolveRecordedPath`.
1091
+ * 4. **Apply** (when `mode === 'apply'`): every per-space `MigrationPlan`
1092
+ * feeds into the runner's `execute` — one outer
1093
+ * transaction across every space; failure on any space rolls back
1094
+ * every space's writes.
1095
+ */
1096
+ async function executeRun(options) {
1097
+ const { driver, adapter, familyInstance, contract, mode, migrations, frameworkComponents, migrationsDir, extensions, targetId, policy, action, onProgress } = options;
1098
+ const loaded = await buildContractSpaceAggregate({
1099
+ targetId,
1100
+ migrationsDir,
1101
+ appContract: contract,
1102
+ extensions,
1103
+ deserializeContract: (json) => familyInstance.deserializeContract(json)
1104
+ });
1105
+ if (!loaded.ok) throw loaded.failure;
1106
+ const aggregate = loaded.value;
1107
+ const markerRows = await familyInstance.readAllMarkers({ driver });
1108
+ if (mode === "apply") {
1109
+ const orphanMarkerError = detectOrphanMarkers(aggregate, markerRows);
1110
+ if (orphanMarkerError !== null) throw orphanMarkerError;
1111
+ }
1112
+ onProgress?.({
1113
+ action,
1114
+ kind: "spanStart",
1115
+ spanId: SPAN_IDS$1.introspect,
1116
+ label: "Introspecting database schema"
1117
+ });
1118
+ const schemaIR = await familyInstance.introspect({
1119
+ driver,
1120
+ contract: collectAggregateNamespaces(aggregate)
1121
+ });
1122
+ onProgress?.({
1123
+ action,
1124
+ kind: "spanEnd",
1125
+ spanId: SPAN_IDS$1.introspect,
1126
+ outcome: "ok"
1127
+ });
1128
+ onProgress?.({
1129
+ action,
1130
+ kind: "spanStart",
1131
+ spanId: SPAN_IDS$1.plan,
1132
+ label: "Planning migration"
1133
+ });
1134
+ const planResult = await planMigration({
1135
+ aggregate,
1136
+ currentDBState: {
1137
+ markersBySpaceId: markerRows,
1138
+ schemaIntrospection: schemaIR
1139
+ },
1140
+ adapter,
1141
+ migrations,
1142
+ frameworkComponents,
1143
+ callerPolicy: { ignoreGraphFor: /* @__PURE__ */ new Set([aggregate.app.spaceId]) },
1144
+ operationPolicy: policy
1145
+ });
1146
+ if (!planResult.ok) {
1147
+ onProgress?.({
1148
+ action,
1149
+ kind: "spanEnd",
1150
+ spanId: SPAN_IDS$1.plan,
1151
+ outcome: "error"
1152
+ });
1153
+ return mapPlannerError(planResult.failure);
1154
+ }
1155
+ onProgress?.({
1156
+ action,
1157
+ kind: "spanEnd",
1158
+ spanId: SPAN_IDS$1.plan,
1159
+ outcome: "ok"
1160
+ });
1161
+ const orderedResolutions = collectOrdered(planResult.value.applyOrder, planResult.value.perSpace);
1162
+ const plannerWarnings = aggregatePlannerWarnings(orderedResolutions);
1163
+ const appResolution = orderedResolutions.find((r) => r.spaceId === aggregate.app.spaceId);
1164
+ if (!appResolution) throw new InternalError("Aggregate planner returned no plan for the app space — the planner is supposed to always emit one.");
1165
+ const appPlan = appResolution.entry.plan;
1166
+ if (mode === "plan") {
1167
+ const aggregateOps = orderedResolutions.flatMap((r) => r.entry.displayOps);
1168
+ const preview = hasOperationPreview(familyInstance) ? familyInstance.toOperationPreview(aggregateOps) : void 0;
1169
+ const perSpace = buildPerSpaceBreakdown(orderedResolutions, aggregate.app.spaceId, { includeMarkers: false });
1170
+ const summary = `Planned ${aggregateOps.length} operation(s) across ${orderedResolutions.length} space(s)`;
1171
+ return wrapPlanResult({
1172
+ operations: aggregateOps,
1173
+ destination: appPlan.destination,
1174
+ preview,
1175
+ perSpace,
1176
+ summary,
1177
+ ...ifDefined("warnings", plannerWarnings)
1178
+ });
1179
+ }
1180
+ if (options.consentedPlanHash !== void 0) {
1181
+ const planHash = computePlanHash({
1182
+ operations: stripOperations(orderedResolutions.flatMap((r) => r.entry.displayOps)),
1183
+ destination: appPlan.destination
1184
+ });
1185
+ if (planHash !== options.consentedPlanHash) return notOk({
1186
+ code: "CONSENT_PLAN_MISMATCH",
1187
+ summary: "The plan changed between consent and apply",
1188
+ why: "The plan recomputed for the consented apply is not the plan that was consented to, so applying it could destroy something nobody agreed to.",
1189
+ conflicts: void 0,
1190
+ meta: void 0,
1191
+ consentPlanMismatch: {
1192
+ consentedPlanHash: options.consentedPlanHash,
1193
+ planHash
1194
+ },
1195
+ ...ifDefined("warnings", plannerWarnings)
1196
+ });
1197
+ }
1198
+ const applied = await runMigration({
1199
+ aggregate,
1200
+ perSpacePlans: planResult.value.perSpace,
1201
+ applyOrder: planResult.value.applyOrder,
1202
+ driver,
1203
+ familyInstance,
1204
+ migrations,
1205
+ frameworkComponents,
1206
+ policy,
1207
+ action,
1208
+ ...ifDefined("onProgress", onProgress)
1209
+ });
1210
+ if (!applied.ok) return buildRunnerFailure({
1211
+ summary: applied.failure.summary,
1212
+ ...ifDefined("why", applied.failure.why),
1213
+ meta: applied.failure.meta,
1214
+ ...ifDefined("warnings", plannerWarnings),
1215
+ ...ifDefined("cause", applied.failure.cause)
1216
+ });
1217
+ const aggregateOps = applied.value.orderedResolutions.flatMap((r) => r.entry.displayOps);
1218
+ const summary = action === "dbInit" ? `Applied ${applied.value.totalOpsExecuted} operation(s) across ${applied.value.orderedResolutions.length} space(s), database signed` : applied.value.totalOpsExecuted === 0 ? `Database already matches contract across ${applied.value.orderedResolutions.length} space(s), signature updated` : `Applied ${applied.value.totalOpsExecuted} operation(s) across ${applied.value.orderedResolutions.length} space(s), signature updated`;
1219
+ return wrapApplyResult({
1220
+ operations: aggregateOps,
1221
+ destination: appPlan.destination,
1222
+ operationsPlanned: applied.value.totalOpsPlanned,
1223
+ operationsExecuted: applied.value.totalOpsExecuted,
1224
+ perSpace: applied.value.perSpace,
1225
+ summary,
1226
+ ...ifDefined("warnings", plannerWarnings)
1227
+ });
1228
+ }
1229
+ function aggregatePlannerWarnings(orderedResolutions) {
1230
+ const warnings = orderedResolutions.flatMap((r) => r.entry.warnings ?? []);
1231
+ return warnings.length > 0 ? warnings : void 0;
1232
+ }
1233
+ /**
1234
+ * Compare the live `_prisma_marker` rows against the aggregate's
1235
+ * declared contract spaces. Any marker row whose `space` is not a space of
1236
+ * the aggregate is an "orphan" — typically a marker left behind by
1237
+ * an extension that was removed from `extensions` without first
1238
+ * cleaning up its on-disk migrations / database tables.
1239
+ *
1240
+ * Returns a {@link CliStructuredError} envelope (code
1241
+ * `MIGRATION.CONTRACT_SPACE_VIOLATION`, `kind: 'orphanMarker'`) for the
1242
+ * first orphan it finds, or `null`
1243
+ * when every marker row maps to a declared contract space. Mirrors the M2
1244
+ * `runContractSpaceVerifierMarkerCheck` envelope so downstream
1245
+ * tooling (integration tests, JSON consumers) keeps asserting on the
1246
+ * same shape.
1247
+ */
1248
+ function detectOrphanMarkers(aggregate, markerRows) {
1249
+ const aggregateSpaceIds = /* @__PURE__ */ new Set([aggregate.app.spaceId, ...aggregate.extensions.map((m) => m.spaceId)]);
1250
+ const orphans = [];
1251
+ for (const [spaceId, row] of markerRows) if (row !== null && row !== void 0 && !aggregateSpaceIds.has(spaceId)) orphans.push(spaceId);
1252
+ if (orphans.length === 0) return null;
1253
+ orphans.sort((a, b) => a.localeCompare(b));
1254
+ return new CliStructuredError$1("MIGRATION.CONTRACT_SPACE_VIOLATION", orphans.length === 1 ? `Orphan contract-space marker detected for "${orphans[0]}"` : `Orphan contract-space markers detected for ${orphans.length} spaces`, {
1255
+ why: `The database has \`_prisma_marker\` rows for spaces (${orphans.map((s) => `"${s}"`).join(", ")}) that are not declared in the project's \`extensions\`. The aggregate pipeline refuses to advance markers it cannot account for.`,
1256
+ fix: "Either re-declare the missing extension(s) in `extensions` (so the aggregate owns them again), or remove the orphan marker row(s) from `_prisma_marker` once you have confirmed the corresponding tables can be safely retired.",
1257
+ docsUrl: "https://pris.ly/contract-spaces",
1258
+ meta: { violations: orphans.map((spaceId) => ({
1259
+ kind: "orphanMarker",
1260
+ spaceId
1261
+ })) }
1262
+ });
1263
+ }
1264
+ function mapPlannerError(error) {
1265
+ if (error.kind === "planFromDiffFailed") return blindCast(notOk({
1266
+ code: "PLANNING_FAILED",
1267
+ summary: "Migration planning failed due to conflicts",
1268
+ conflicts: error.conflicts,
1269
+ why: void 0,
1270
+ meta: void 0
1271
+ }));
1272
+ if (error.kind === "extensionPathUnreachable") return buildRunnerFailure({
1273
+ summary: `Cannot resolve apply path for extension space "${error.spaceId}"`,
1274
+ why: `No path in the on-disk migration graph for extension space "${error.spaceId}" reaches the on-disk head ref hash "${error.target}".`,
1275
+ meta: {
1276
+ spaceId: error.spaceId,
1277
+ target: error.target
1278
+ }
1279
+ });
1280
+ if (error.kind === "extensionPathUnsatisfiable") return buildRunnerFailure({
1281
+ summary: `Cannot resolve apply path for extension space "${error.spaceId}"`,
1282
+ why: `On-disk migration graph for extension space "${error.spaceId}" reaches the on-disk head ref but does not cover required invariants: ${error.missingInvariants.join(", ")}.`,
1283
+ meta: {
1284
+ spaceId: error.spaceId,
1285
+ missingInvariants: error.missingInvariants
1286
+ }
1287
+ });
1288
+ return buildRunnerFailure({
1289
+ summary: `Aggregate planner policy conflict for space "${error.spaceId}"`,
1290
+ why: error.detail,
1291
+ meta: { spaceId: error.spaceId }
1292
+ });
1293
+ }
1294
+ function wrapPlanResult(args) {
1295
+ return ok({
1296
+ mode: "plan",
1297
+ plan: {
1298
+ operations: stripOperations(args.operations),
1299
+ ...ifDefined("preview", args.preview)
1300
+ },
1301
+ destination: {
1302
+ storageHash: args.destination.storageHash,
1303
+ ...ifDefined("profileHash", args.destination.profileHash)
1304
+ },
1305
+ perSpace: args.perSpace,
1306
+ summary: args.summary,
1307
+ ...ifDefined("warnings", args.warnings)
1308
+ });
1309
+ }
1310
+ function wrapApplyResult(args) {
1311
+ return ok({
1312
+ mode: "apply",
1313
+ plan: { operations: stripOperations(args.operations) },
1314
+ destination: {
1315
+ storageHash: args.destination.storageHash,
1316
+ ...ifDefined("profileHash", args.destination.profileHash)
1317
+ },
1318
+ execution: {
1319
+ operationsPlanned: args.operationsPlanned,
1320
+ operationsExecuted: args.operationsExecuted
1321
+ },
1322
+ marker: args.destination.profileHash ? {
1323
+ storageHash: args.destination.storageHash,
1324
+ profileHash: args.destination.profileHash
1325
+ } : { storageHash: args.destination.storageHash },
1326
+ perSpace: args.perSpace,
1327
+ summary: args.summary,
1328
+ ...ifDefined("warnings", args.warnings)
1329
+ });
1330
+ }
1331
+ function buildRunnerFailure(args) {
1332
+ return blindCast(notOk({
1333
+ code: "RUNNER_FAILED",
1334
+ summary: args.summary,
1335
+ why: args.why,
1336
+ meta: args.meta,
1337
+ conflicts: void 0,
1338
+ ...ifDefined("warnings", args.warnings),
1339
+ ...ifDefined("cause", args.cause)
1340
+ }));
1341
+ }
1342
+ /**
1343
+ * Execute `db init` against the configured contract.
1344
+ *
1345
+ * Routes through the loader → planner → runner pipeline (sub-spec
1346
+ * "Commit-by-commit § Commit 4"). Always additive-only; destructive
1347
+ * changes belong to `db update`.
1348
+ */
1349
+ async function executeDbInit(options) {
1350
+ return await executeRun({
1351
+ driver: options.driver,
1352
+ adapter: options.adapter,
1353
+ familyInstance: options.familyInstance,
1354
+ contract: options.contract,
1355
+ mode: options.mode,
1356
+ migrations: options.migrations,
1357
+ frameworkComponents: options.frameworkComponents,
1358
+ migrationsDir: options.migrationsDir,
1359
+ targetId: options.targetId,
1360
+ extensions: options.extensions ?? [],
1361
+ policy: { allowedOperationClasses: ["additive"] },
1362
+ action: "dbInit",
1363
+ ...ifDefined("onProgress", options.onProgress)
1364
+ });
1365
+ }
1366
+ const DB_UPDATE_POLICY = { allowedOperationClasses: [
1367
+ "additive",
1368
+ "widening",
1369
+ "destructive"
1370
+ ] };
1371
+ /**
1372
+ * Execute `db update` against the configured contract.
1373
+ *
1374
+ * Routes through the loader → planner → runner pipeline. Destructive
1375
+ * operations require either `acceptDataLoss: true` or a prior
1376
+ * `mode: 'plan'` invocation that surfaces the destructive ops; the
1377
+ * confirmation gate is implemented here so the lower-level applier
1378
+ * remains policy-agnostic.
1379
+ */
1380
+ async function executeDbUpdate(options) {
1381
+ const sharedInputs = {
1382
+ driver: options.driver,
1383
+ adapter: options.adapter,
1384
+ familyInstance: options.familyInstance,
1385
+ contract: options.contract,
1386
+ migrations: options.migrations,
1387
+ frameworkComponents: options.frameworkComponents,
1388
+ migrationsDir: options.migrationsDir,
1389
+ targetId: options.targetId,
1390
+ extensions: options.extensions ?? [],
1391
+ policy: DB_UPDATE_POLICY,
1392
+ action: "dbUpdate",
1393
+ ...ifDefined("onProgress", options.onProgress)
1394
+ };
1395
+ if (options.mode === "apply" && !options.acceptDataLoss && options.consent === void 0) {
1396
+ const refusal = await guardDestructiveChanges(sharedInputs);
1397
+ if (refusal !== null) return refusal;
1398
+ }
1399
+ return await executeRun({
1400
+ ...sharedInputs,
1401
+ mode: options.mode,
1402
+ ...ifDefined("consentedPlanHash", options.consent?.planHash)
1403
+ });
1404
+ }
1405
+ /**
1406
+ * Pre-plan once when running `db update apply` without `acceptDataLoss`.
1407
+ * Surfaces destructive operations across every space; if any are
1408
+ * planned, returns a `DESTRUCTIVE_CHANGES` failure that the CLI shows
1409
+ * as a confirmation prompt. Returns `null` when the apply is safe to
1410
+ * run.
1411
+ */
1412
+ async function guardDestructiveChanges(sharedInputs) {
1413
+ const planResult = await executeRun({
1414
+ ...sharedInputs,
1415
+ mode: "plan"
1416
+ });
1417
+ if (!planResult.ok) return planResult;
1418
+ const destructiveOps = planResult.value.plan.operations.filter((op) => op.operationClass === "destructive").map((op) => ({
1419
+ id: op.id,
1420
+ label: op.label
1421
+ }));
1422
+ if (destructiveOps.length === 0) return null;
1423
+ const databaseName = await sharedInputs.driver.databaseName?.();
1424
+ return notOk({
1425
+ code: "DESTRUCTIVE_CHANGES",
1426
+ summary: `Planned ${destructiveOps.length} destructive operation(s) that require confirmation`,
1427
+ why: "Destructive operations require explicit consent, which the caller has not given",
1428
+ conflicts: void 0,
1429
+ meta: void 0,
1430
+ destructiveChanges: {
1431
+ destructiveOperations: destructiveOps,
1432
+ databaseName,
1433
+ planHash: computePlanHash({
1434
+ operations: planResult.value.plan.operations,
1435
+ destination: planResult.value.destination
1436
+ })
1437
+ },
1438
+ ...ifDefined("warnings", planResult.value.warnings)
1439
+ });
1440
+ }
1441
+ /**
1442
+ * Span IDs emitted via `onProgress` during the aggregate verify flow.
1443
+ * Mirrors the span identifiers used by the legacy precheck / marker-check
1444
+ * helpers so structured-output renderers and progress tests keep working.
1445
+ */
1446
+ const SPAN_IDS = {
1447
+ introspect: "introspect",
1448
+ verify: "verify"
1449
+ };
1450
+ /**
1451
+ * Loader → verifier pipeline shared by `db verify` modes (`full`,
1452
+ * `marker-only`, `schema-only`).
1453
+ *
1454
+ * 1. **Load**: build a {@link import('@internal/migration-tools/aggregate').ContractSpaceAggregate}
1455
+ * from descriptors + on-disk on-disk artefacts. Layout / drift /
1456
+ * integrity / disjointness violations short-circuit with a
1457
+ * structured CLI error.
1458
+ * 2. **Read DB state**: marker rows + (when `skipSchema` is `false`)
1459
+ * schema introspection.
1460
+ * 3. **Verify**: {@link verifyMigration} returns per-space `markerCheck` +
1461
+ * per-space `schemaCheck` (each contract space verified against the full schema,
1462
+ * then scoped to its own contract space). Marker drift is returned as the
1463
+ * success value's `markerDrift` envelope for callers to settle as a
1464
+ * finding. Verify results are returned to the caller verbatim.
1465
+ */
1466
+ async function executeDbVerify(options) {
1467
+ const { driver, familyInstance, onProgress, skipSchema, skipMarker } = options;
1468
+ const loaded = await buildContractSpaceAggregate(buildLoadInputs(options));
1469
+ if (!loaded.ok) return notOk(loaded.failure);
1470
+ const aggregate = loaded.value;
1471
+ const markersBySpaceId = await familyInstance.readAllMarkers({ driver });
1472
+ const schemaIntrospection = skipSchema ? null : await runIntrospection({
1473
+ driver,
1474
+ familyInstance,
1475
+ onProgress,
1476
+ contract: collectAggregateNamespaces(aggregate)
1477
+ });
1478
+ const classifySubjectGranularity = hasSchemaSubjectClassifier(familyInstance) ? (issue) => familyInstance.classifySubjectGranularity(issue) : void 0;
1479
+ const classifyEntityKind = hasSchemaSubjectClassifier(familyInstance) ? (issue) => familyInstance.classifyEntityKind(issue) : void 0;
1480
+ emitVerifySpan(onProgress, "spanStart");
1481
+ return finaliseVerifyResult({
1482
+ verifyResult: verifyMigration({
1483
+ aggregate,
1484
+ markersBySpaceId,
1485
+ schemaIntrospection,
1486
+ mode: options.mode,
1487
+ verifySchemaForSpace: createPerSpaceVerifier(options),
1488
+ ...ifDefined("classifySubjectGranularity", classifySubjectGranularity),
1489
+ ...ifDefined("classifyEntityKind", classifyEntityKind)
1490
+ }),
1491
+ aggregate,
1492
+ skipMarker,
1493
+ onProgress
1494
+ });
1495
+ }
1496
+ function buildLoadInputs(options) {
1497
+ return {
1498
+ targetId: options.targetId,
1499
+ migrationsDir: options.migrationsDir,
1500
+ appContract: options.contract,
1501
+ extensions: options.extensions,
1502
+ deserializeContract: (json) => options.familyInstance.deserializeContract(json)
1503
+ };
1504
+ }
1505
+ async function runIntrospection(args) {
1506
+ const { driver, familyInstance, onProgress, contract } = args;
1507
+ onProgress?.({
1508
+ action: "dbVerify",
1509
+ kind: "spanStart",
1510
+ spanId: SPAN_IDS.introspect,
1511
+ label: "Introspecting database schema"
1512
+ });
1513
+ try {
1514
+ const result = await familyInstance.introspect({
1515
+ driver,
1516
+ contract
1517
+ });
1518
+ onProgress?.({
1519
+ action: "dbVerify",
1520
+ kind: "spanEnd",
1521
+ spanId: SPAN_IDS.introspect,
1522
+ outcome: "ok"
1523
+ });
1524
+ return result;
1525
+ } catch (error) {
1526
+ onProgress?.({
1527
+ action: "dbVerify",
1528
+ kind: "spanEnd",
1529
+ spanId: SPAN_IDS.introspect,
1530
+ outcome: "error"
1531
+ });
1532
+ throw error;
1533
+ }
1534
+ }
1535
+ /**
1536
+ * Build the per-space schema callback handed to the aggregate verifier.
1537
+ * When `skipSchema` is true the callback short-circuits with a synthetic
1538
+ * `ok` result so the verifier still runs the (cheap) schemaCheck loop
1539
+ * without invoking the family's verification path.
1540
+ */
1541
+ function createPerSpaceVerifier(options) {
1542
+ const { skipSchema, familyInstance, frameworkComponents } = options;
1543
+ return (schema, space, verifyMode) => {
1544
+ if (skipSchema) return buildSkippedSchemaResult(space);
1545
+ return familyInstance.verifySchema({
1546
+ contract: space.contract(),
1547
+ schema,
1548
+ strict: verifyMode === "strict",
1549
+ frameworkComponents
1550
+ });
1551
+ };
1552
+ }
1553
+ function emitVerifySpan(onProgress, kind) {
1554
+ if (kind === "spanStart") {
1555
+ onProgress?.({
1556
+ action: "dbVerify",
1557
+ kind: "spanStart",
1558
+ spanId: SPAN_IDS.verify,
1559
+ label: "Verifying contract spaces"
1560
+ });
1561
+ return;
1562
+ }
1563
+ onProgress?.({
1564
+ action: "dbVerify",
1565
+ kind: "spanEnd",
1566
+ spanId: SPAN_IDS.verify,
1567
+ outcome: kind === "spanEndOk" ? "ok" : "error"
1568
+ });
1569
+ }
1570
+ /**
1571
+ * Map an {@link VerifierOutput} to the operation's
1572
+ * {@link ExecuteDbVerifyResult}, applying the `skipMarker` policy used
1573
+ * by the CLI's `--schema-only` mode.
1574
+ */
1575
+ function finaliseVerifyResult(args) {
1576
+ const { verifyResult, aggregate, skipMarker, onProgress } = args;
1577
+ if (!verifyResult.ok) {
1578
+ emitVerifySpan(onProgress, "spanEndError");
1579
+ return notOk(new CliStructuredError$1("MIGRATION.CONTRACT_SPACE_VIOLATION", "Aggregate verifier introspection failed", {
1580
+ why: verifyResult.failure.detail,
1581
+ fix: "Check database connectivity and the introspection tooling.",
1582
+ docsUrl: "https://pris.ly/contract-spaces"
1583
+ }));
1584
+ }
1585
+ const markerDrift = skipMarker ? null : mapMarkerCheckFailures(aggregate.app.spaceId, verifyResult.value.markerCheck);
1586
+ emitVerifySpan(onProgress, markerDrift === null ? "spanEndOk" : "spanEndError");
1587
+ return ok({
1588
+ schemaResults: verifyResult.value.schemaCheck.perSpace,
1589
+ unclaimed: verifyResult.value.schemaCheck.unclaimed,
1590
+ spaceOrder: [aggregate.app.spaceId, ...aggregate.extensions.map((e) => e.spaceId)],
1591
+ appSpaceId: aggregate.app.spaceId,
1592
+ markerDrift
1593
+ });
1594
+ }
1595
+ function buildSkippedSchemaResult(space) {
1596
+ const contract = space.contract();
1597
+ const headRef = requireHeadRef(space);
1598
+ const profileHash = castAs(contract).profileHash;
1599
+ return {
1600
+ ok: true,
1601
+ summary: "Schema verification skipped",
1602
+ contract: {
1603
+ storageHash: headRef.hash,
1604
+ ...profileHash ? { profileHash } : {}
1605
+ },
1606
+ target: { expected: contract.target },
1607
+ schema: { issues: [] },
1608
+ timings: { total: 0 }
1609
+ };
1610
+ }
1611
+ /**
1612
+ * Translate per-space marker check failures and orphan markers into a
1613
+ * single CLI structured error envelope. Preserves the legacy code
1614
+ * `MIGRATION.CONTRACT_SPACE_VIOLATION` (was emitted by
1615
+ * `runContractSpaceVerifierMarkerCheck`).
1616
+ */
1617
+ function mapMarkerCheckFailures(appSpaceId, section) {
1618
+ const violations = [];
1619
+ for (const [spaceId, result] of section.perSpace) {
1620
+ if (result.kind === "ok" || result.kind === "absent") continue;
1621
+ if (result.kind === "hashMismatch") {
1622
+ violations.push({
1623
+ kind: "hashMismatch",
1624
+ spaceId,
1625
+ remediation: spaceId === appSpaceId ? "Run `prisma-cli db update` to advance the marker, or roll the database back to the recorded hash." : `Apply on-disk migrations under \`migrations/${spaceId}/\` to advance the marker, or remove the conflicting marker row.`
1626
+ });
1627
+ continue;
1628
+ }
1629
+ if (result.kind === "missingInvariants") violations.push({
1630
+ kind: "invariantsMismatch",
1631
+ spaceId,
1632
+ remediation: `Re-apply the migrations under \`migrations/${spaceId}/\` so the marker carries invariants: ${result.missing.join(", ")}.`
1633
+ });
1634
+ }
1635
+ for (const orphan of section.orphanMarkers) violations.push({
1636
+ kind: "orphanMarker",
1637
+ spaceId: orphan.spaceId,
1638
+ remediation: `Add the corresponding extension to \`extensions\` in \`prisma.config.ts\`, or delete the orphan marker row for "${orphan.spaceId}".`
1639
+ });
1640
+ if (violations.length === 0) return null;
1641
+ const lines = violations.map((v) => `- [${v.kind}] ${v.spaceId}: ${v.remediation}`);
1642
+ return new CliStructuredError$1("MIGRATION.CONTRACT_SPACE_VIOLATION", violations.length === 1 ? "Contract-space verifier found a violation" : `Contract-space verifier found violations (${violations.length})`, {
1643
+ why: `The on-disk \`migrations/\` directory, the \`extensions\` declaration, and the live database marker rows are not in agreement.\n${lines.join("\n")}`,
1644
+ fix: violations[0]?.remediation ?? "Review and reconcile the violations listed above.",
1645
+ docsUrl: "https://pris.ly/contract-spaces",
1646
+ meta: { violations }
1647
+ });
1648
+ }
1649
+ /**
1650
+ * Apply pending migrations across every contract space (app +
1651
+ * extensions). Replay-only: graph-walk against the on-disk graph for
1652
+ * every contract space; no synth, no introspection.
1653
+ *
1654
+ * Pipeline:
1655
+ *
1656
+ * 1. Load aggregate from disk (loader hydrates extension graphs;
1657
+ * caller provides app-space packages).
1658
+ * 2. Read live marker rows per space (`familyInstance.readAllMarkers`).
1659
+ * 3. Per space: `resolveRecordedPath` plots the path from the live
1660
+ * marker to `space.headRef.hash` (or `refHash` for the app
1661
+ * space when provided). An empty-graph space whose elements are ALL
1662
+ * externally managed resolves declaratively (marker to head, zero
1663
+ * ops), mirroring the db-init aggregate planner: such a space ships
1664
+ * no DDL and has nothing to author. Every other empty-graph space
1665
+ * fails loudly — "never planned" is a user-error condition for
1666
+ * replay.
1667
+ * 4. Hand off to {@link runMigration} (the runner-driving tail
1668
+ * shared with `db init` / `db update`). Marker advancement is
1669
+ * inside the per-space transaction.
1670
+ *
1671
+ * Encodes the replay-only contract: the app contract space must have an
1672
+ * authored migration graph on disk before this operation can advance it.
1673
+ */
1674
+ async function executeMigrate(options) {
1675
+ const { driver, familyInstance, contract, migrations, frameworkComponents, migrationsDir, extensions, targetId, refHash, refInvariants, refName, onProgress } = options;
1676
+ const loaded = await buildContractSpaceAggregate({
1677
+ targetId,
1678
+ migrationsDir,
1679
+ appContract: contract,
1680
+ extensions,
1681
+ deserializeContract: (json) => familyInstance.deserializeContract(json)
1682
+ });
1683
+ if (!loaded.ok) throw loaded.failure;
1684
+ const aggregate = loaded.value;
1685
+ const markerRows = await familyInstance.readAllMarkers({ driver });
1686
+ const allSpaces = [aggregate.app, ...aggregate.extensions];
1687
+ const perSpacePlans = /* @__PURE__ */ new Map();
1688
+ const atHeadResolutions = /* @__PURE__ */ new Map();
1689
+ for (const space of allSpaces) {
1690
+ const isAppSpace = space.spaceId === aggregate.app.spaceId;
1691
+ const headRef = requireHeadRef(space);
1692
+ const spaceTargetHash = isAppSpace && refHash !== void 0 ? refHash : headRef.hash;
1693
+ const outcome = planSpacePath({
1694
+ space,
1695
+ aggregate,
1696
+ targetHash: spaceTargetHash,
1697
+ refInvariants: isAppSpace && refHash !== void 0 ? refInvariants : void 0,
1698
+ liveMarker: markerRows.get(space.spaceId) ?? null,
1699
+ ...isAppSpace ? { refName } : {}
1700
+ });
1701
+ if (outcome.kind === "at-head") {
1702
+ atHeadResolutions.set(space.spaceId, outcome.plan);
1703
+ continue;
1704
+ }
1705
+ if (outcome.kind === "never-planned") return notOk(buildNeverPlannedFailure(outcome.spaceId, outcome.targetHash));
1706
+ if (outcome.kind === "unreachable") return notOk(buildPathNotFoundFailure(outcome.spaceId, outcome.liveMarker, outcome.targetHash));
1707
+ if (outcome.kind === "unsatisfiable") {
1708
+ const structural = findPathWithDecision(outcome.targetSpace.graph(), outcome.liveHash, spaceTargetHash, { required: /* @__PURE__ */ new Set() });
1709
+ const structuralPath = structural.kind === "ok" ? structural.decision.selectedPath.map((edge) => ({
1710
+ dirName: edge.dirName,
1711
+ migrationHash: edge.migrationHash,
1712
+ from: edge.from,
1713
+ to: edge.to,
1714
+ invariants: edge.invariants
1715
+ })) : [];
1716
+ throw errorNoInvariantPath({
1717
+ ...outcome.refName !== void 0 ? { refName: outcome.refName } : {},
1718
+ required: outcome.targetInvariants,
1719
+ missing: outcome.missing,
1720
+ structuralPath
1721
+ });
1722
+ }
1723
+ perSpacePlans.set(space.spaceId, outcome.plan);
1724
+ }
1725
+ const canonicalOrder = [...aggregate.extensions.map((m) => m.spaceId), aggregate.app.spaceId];
1726
+ const applyOrder = canonicalOrder.filter((spaceId) => perSpacePlans.has(spaceId));
1727
+ if (!applyOrder.some((spaceId) => {
1728
+ const entry = perSpacePlans.get(spaceId);
1729
+ return entry !== void 0 && planRequiresExecution(entry);
1730
+ })) {
1731
+ const ordered = canonicalOrder.filter((spaceId) => perSpacePlans.has(spaceId) || atHeadResolutions.has(spaceId)).map((spaceId) => {
1732
+ const entry = perSpacePlans.get(spaceId) ?? atHeadResolutions.get(spaceId);
1733
+ if (entry === void 0) throw new InternalError(`Unreachable: missing per-space plan for "${spaceId}"`);
1734
+ return {
1735
+ spaceId,
1736
+ entry
1737
+ };
1738
+ });
1739
+ const perSpace = buildPerSpaceBreakdown(ordered, aggregate.app.spaceId, { includeMarkers: true });
1740
+ const totalSpaces = ordered.length;
1741
+ return ok(buildSuccess({
1742
+ aggregate,
1743
+ orderedResolutions: ordered,
1744
+ perSpace,
1745
+ totalOpsExecuted: 0,
1746
+ summary: totalSpaces === 0 ? "Already up to date — no contract spaces are loaded" : totalSpaces === 1 ? "Already up to date" : `Already up to date across ${totalSpaces} space(s)`
1747
+ }));
1748
+ }
1749
+ const applied = await runMigration({
1750
+ aggregate,
1751
+ perSpacePlans,
1752
+ applyOrder,
1753
+ driver,
1754
+ familyInstance,
1755
+ migrations,
1756
+ frameworkComponents,
1757
+ policy: { allowedOperationClasses: [
1758
+ "additive",
1759
+ "widening",
1760
+ "destructive",
1761
+ "data"
1762
+ ] },
1763
+ action: "migrate",
1764
+ ...ifDefined("onProgress", onProgress)
1765
+ });
1766
+ if (!applied.ok) return notOk({
1767
+ code: "RUNNER_FAILED",
1768
+ summary: applied.failure.summary,
1769
+ why: applied.failure.why,
1770
+ meta: applied.failure.meta,
1771
+ ...ifDefined("cause", applied.failure.cause)
1772
+ });
1773
+ const orderedAll = canonicalOrder.filter((spaceId) => perSpacePlans.has(spaceId) || atHeadResolutions.has(spaceId)).map((spaceId) => {
1774
+ if (perSpacePlans.has(spaceId)) {
1775
+ const fromRunner = applied.value.orderedResolutions.find((r) => r.spaceId === spaceId);
1776
+ if (fromRunner !== void 0) return fromRunner;
1777
+ }
1778
+ const entry = atHeadResolutions.get(spaceId);
1779
+ if (entry === void 0) throw new InternalError(`Unreachable: missing per-space plan for "${spaceId}"`);
1780
+ return {
1781
+ spaceId,
1782
+ entry
1783
+ };
1784
+ });
1785
+ const perSpaceAll = buildPerSpaceBreakdown(orderedAll, aggregate.app.spaceId, { includeMarkers: true });
1786
+ const summary = `Applied ${applied.value.orderedResolutions.reduce((sum, r) => sum + r.entry.migrationEdges.length, 0)} migration(s) (${applied.value.totalOpsExecuted} operation(s)) across ${orderedAll.length} contract space(s)`;
1787
+ return ok(buildSuccess({
1788
+ aggregate,
1789
+ orderedResolutions: orderedAll,
1790
+ perSpace: perSpaceAll,
1791
+ totalOpsExecuted: applied.value.totalOpsExecuted,
1792
+ summary
1793
+ }));
1794
+ }
1795
+ /**
1796
+ * Compute the graph-walk path for one contract space.
1797
+ *
1798
+ * Encapsulates the invariant-correct input assembly that both
1799
+ * `executeMigrate` and `executeMigrateShowCommand` must use:
1800
+ * - `currentMarker` carries the full live marker including `invariants`
1801
+ * (not a stripped `{ storageHash, invariants: [] }` shell).
1802
+ * - `targetInvariants` uses the caller-supplied `refInvariants` when a
1803
+ * `--to` ref was resolved (not always the file head ref's invariants).
1804
+ *
1805
+ * Both callers map the returned `SpacePathOutcome` to their own error
1806
+ * representation; the path-compute logic is shared and identical.
1807
+ *
1808
+ * @internal Exported for `executeMigrateShowCommand`.
1809
+ */
1810
+ function planSpacePath({ space, aggregate, targetHash, refInvariants, liveMarker, refName }) {
1811
+ const isAppSpace = space.spaceId === aggregate.app.spaceId;
1812
+ const headRef = requireHeadRef(space);
1813
+ if (space.graph().nodes.size === 0) {
1814
+ const liveHash = liveMarker?.storageHash;
1815
+ if (targetHash === liveHash || liveHash === void 0 && targetHash === "empty") return {
1816
+ kind: "at-head",
1817
+ plan: buildAtHeadResolution({
1818
+ aggregateTargetId: aggregate.targetId,
1819
+ space,
1820
+ targetHash,
1821
+ liveMarker
1822
+ })
1823
+ };
1824
+ if (!isAppSpace && allStorageElementsExternal(space.contract())) {
1825
+ if (headRef.invariants.length > 0) return {
1826
+ kind: "unsatisfiable",
1827
+ spaceId: space.spaceId,
1828
+ isAppSpace,
1829
+ missing: [...headRef.invariants].sort(),
1830
+ targetInvariants: headRef.invariants,
1831
+ targetSpace: space,
1832
+ liveHash: liveHash ?? "empty",
1833
+ refName: void 0
1834
+ };
1835
+ return {
1836
+ kind: "ok",
1837
+ plan: buildAtHeadResolution({
1838
+ aggregateTargetId: aggregate.targetId,
1839
+ space,
1840
+ targetHash,
1841
+ liveMarker
1842
+ })
1843
+ };
1844
+ }
1845
+ return {
1846
+ kind: "never-planned",
1847
+ spaceId: space.spaceId,
1848
+ targetHash
1849
+ };
1850
+ }
1851
+ const targetInvariants = isAppSpace && refInvariants !== void 0 ? refInvariants : headRef.invariants;
1852
+ const targetSpace = targetHash === headRef.hash && targetInvariants === headRef.invariants ? space : {
1853
+ ...space,
1854
+ headRef: {
1855
+ hash: targetHash,
1856
+ invariants: targetInvariants
1857
+ }
1858
+ };
1859
+ const walked = resolveRecordedPath({
1860
+ aggregateTargetId: aggregate.targetId,
1861
+ space: targetSpace,
1862
+ currentMarker: liveMarker,
1863
+ ...isAppSpace && refName !== void 0 ? { refName } : {}
1864
+ });
1865
+ if (walked.kind === "unreachable") return {
1866
+ kind: "unreachable",
1867
+ spaceId: space.spaceId,
1868
+ liveMarker,
1869
+ targetHash
1870
+ };
1871
+ if (walked.kind === "unsatisfiable") {
1872
+ const liveHash = liveMarker?.storageHash ?? "empty";
1873
+ return {
1874
+ kind: "unsatisfiable",
1875
+ spaceId: space.spaceId,
1876
+ isAppSpace,
1877
+ missing: walked.missing,
1878
+ targetInvariants,
1879
+ targetSpace,
1880
+ liveHash,
1881
+ refName
1882
+ };
1883
+ }
1884
+ return {
1885
+ kind: "ok",
1886
+ plan: walked.result
1887
+ };
1888
+ }
1889
+ /**
1890
+ * Build a zero-op {@link PerSpacePlan} for an empty-graph space —
1891
+ * either one whose live marker already matches the target (at-head), or
1892
+ * an all-external extension space whose marker must advance to the head
1893
+ * ref with no DDL (declared-state). Lets the apply pipeline thread the
1894
+ * space through `perSpacePlans` -> `applyOrder` -> the success
1895
+ * envelope's `perSpace[]` block so the result reflects every loaded
1896
+ * space, even when there is nothing to execute.
1897
+ */
1898
+ function buildAtHeadResolution(args) {
1899
+ const { aggregateTargetId, space, targetHash, liveMarker } = args;
1900
+ return {
1901
+ plan: {
1902
+ targetId: aggregateTargetId,
1903
+ spaceId: space.spaceId,
1904
+ origin: liveMarker === null ? null : { storageHash: liveMarker.storageHash },
1905
+ destination: { storageHash: targetHash },
1906
+ operations: [],
1907
+ providedInvariants: []
1908
+ },
1909
+ displayOps: [],
1910
+ destinationContract: space.contract(),
1911
+ strategy: "declared-state",
1912
+ migrationEdges: [buildFabricatedMigrationEdge({
1913
+ currentMarkerStorageHash: liveMarker?.storageHash,
1914
+ destinationStorageHash: targetHash,
1915
+ operationCount: 0
1916
+ })]
1917
+ };
1918
+ }
1919
+ /**
1920
+ * A plan needs the runner when it executes operations or advances the
1921
+ * space's marker (a declared-state resolution has zero operations but a
1922
+ * destination hash the live marker doesn't carry yet).
1923
+ */
1924
+ function planRequiresExecution(entry) {
1925
+ if (entry.plan.operations.length > 0) return true;
1926
+ return entry.plan.origin?.storageHash !== entry.plan.destination.storageHash;
1927
+ }
1928
+ function buildSuccess(args) {
1929
+ const appResolution = args.orderedResolutions.find((r) => r.spaceId === args.aggregate.app.spaceId);
1930
+ const appMarkerHash = appResolution?.entry.plan.destination.storageHash ?? requireHeadRef(args.aggregate.app).hash;
1931
+ const applied = args.orderedResolutions.flatMap((r) => {
1932
+ return r.entry.migrationEdges.map((edge) => ({
1933
+ spaceId: r.spaceId,
1934
+ dirName: edge.dirName,
1935
+ migrationHash: edge.migrationHash,
1936
+ from: edge.from,
1937
+ to: edge.to,
1938
+ operationsExecuted: edge.operationCount
1939
+ }));
1940
+ });
1941
+ const appPlan = appResolution?.entry;
1942
+ const pathDecision = appPlan?.pathDecision ? {
1943
+ fromHash: appPlan.pathDecision.fromHash,
1944
+ toHash: appPlan.pathDecision.toHash,
1945
+ alternativeCount: appPlan.pathDecision.alternativeCount,
1946
+ tieBreakReasons: appPlan.pathDecision.tieBreakReasons,
1947
+ ...appPlan.pathDecision.refName !== void 0 ? { refName: appPlan.pathDecision.refName } : {},
1948
+ requiredInvariants: appPlan.pathDecision.requiredInvariants ?? [],
1949
+ satisfiedInvariants: appPlan.pathDecision.satisfiedInvariants ?? [],
1950
+ selectedPath: appPlan.pathDecision.selectedPath.map((entry) => ({
1951
+ dirName: entry.dirName,
1952
+ migrationHash: entry.migrationHash,
1953
+ from: entry.from,
1954
+ to: entry.to,
1955
+ invariants: entry.invariants
1956
+ }))
1957
+ } : void 0;
1958
+ return {
1959
+ migrationsApplied: applied.length,
1960
+ markerHash: appMarkerHash,
1961
+ applied,
1962
+ summary: args.summary,
1963
+ perSpace: args.perSpace,
1964
+ ...pathDecision !== void 0 ? { pathDecision } : {}
1965
+ };
1966
+ }
1967
+ /**
1968
+ * Build the `neverPlanned` failure raised when a contract space that
1969
+ * declares managed storage elements has no on-disk migration graph but
1970
+ * migrate was asked to reach a target hash. All-external spaces never reach
1971
+ * this: they resolve declaratively (marker advances to the head ref with
1972
+ * zero operations). The `why` states only the condition; the recovery
1973
+ * sequence is composed by `errorPathUnreachable`'s `fix`.
1974
+ *
1975
+ * @internal Exported for testing only.
1976
+ */
1977
+ function buildNeverPlannedFailure(spaceId, targetHash) {
1978
+ return {
1979
+ code: "MIGRATION_PATH_NOT_FOUND",
1980
+ summary: `No on-disk migrations for contract space "${spaceId}"`,
1981
+ why: `migrate is replay-only: a contract space that declares managed storage elements must have an authored migration graph on disk. Space "${spaceId}" has no migrations under \`migrations/${spaceId}/\` but its head ref targets "${targetHash}".`,
1982
+ meta: {
1983
+ spaceId,
1984
+ target: targetHash,
1985
+ kind: "neverPlanned"
1986
+ }
1987
+ };
1988
+ }
1989
+ /**
1990
+ * Build the `pathUnreachable` failure raised when an emitted contract has no
1991
+ * on-disk migration edge from the current marker to the target. The `why`
1992
+ * states only the condition (no edge between the two named states, and migrate
1993
+ * replays edges rather than inventing them); the recovery sequence — plan the
1994
+ * edge, then re-apply — is composed by `errorPathUnreachable`'s `fix`, so the
1995
+ * two read as one non-redundant plan-then-apply story.
1996
+ *
1997
+ * @internal Exported for testing only.
1998
+ */
1999
+ function buildPathNotFoundFailure(spaceId, marker, targetHash) {
2000
+ const fromHash = marker?.storageHash ?? "<empty>";
2001
+ return {
2002
+ code: "MIGRATION_PATH_NOT_FOUND",
2003
+ summary: spaceId === "app" ? "Current contract has no planned migration path" : `Current contract has no planned migration path for contract space "${spaceId}"`,
2004
+ why: `No migration edge connects the current state "${fromHash}" to the target "${targetHash}" in contract space "${spaceId}". The on-disk migration graph does not join the two, and migrate replays existing edges — it never invents one.`,
2005
+ meta: {
2006
+ spaceId,
2007
+ fromHash,
2008
+ targetHash,
2009
+ kind: "pathUnreachable"
2010
+ }
2011
+ };
2012
+ }
2013
+ /**
2014
+ * Creates a programmatic control client for Prisma Next operations.
2015
+ *
2016
+ * The client accepts framework component descriptors at creation time,
2017
+ * manages driver lifecycle via connect()/close(), and exposes domain
2018
+ * operations that delegate to the existing family instance methods.
2019
+ *
2020
+ * @see {@link ControlClient} for the client interface
2021
+ * @see README.md "Programmatic Control API" section for usage examples
2022
+ */
2023
+ function createControlClient(options) {
2024
+ return new ControlClientImpl(options);
2025
+ }
2026
+ /**
2027
+ * Implementation of ControlClient.
2028
+ * Manages initialization and connection state, delegates operations to family instance.
2029
+ */
2030
+ var ControlClientImpl = class {
2031
+ options;
2032
+ stack = null;
2033
+ driver = null;
2034
+ familyInstance = null;
2035
+ frameworkComponents = null;
2036
+ initialized = false;
2037
+ defaultConnection;
2038
+ constructor(options) {
2039
+ this.options = options;
2040
+ this.defaultConnection = options.connection;
2041
+ }
2042
+ init() {
2043
+ if (this.initialized) return;
2044
+ this.stack = createControlStack({
2045
+ family: this.options.family,
2046
+ target: this.options.target,
2047
+ adapter: this.options.adapter,
2048
+ driver: this.options.driver,
2049
+ extensions: this.options.extensions
2050
+ });
2051
+ this.familyInstance = this.options.family.create(this.stack);
2052
+ const rawComponents = [
2053
+ this.options.target,
2054
+ this.options.adapter,
2055
+ ...this.options.extensions ?? []
2056
+ ];
2057
+ this.frameworkComponents = assertFrameworkComponentsCompatible(this.options.family.familyId, this.options.target.targetId, rawComponents);
2058
+ this.initialized = true;
2059
+ }
2060
+ async connect(connection) {
2061
+ this.init();
2062
+ if (this.driver) throw new CliStructuredError("DRIVER.ALREADY_CONNECTED", "Already connected. Call close() before reconnecting.");
2063
+ const resolvedConnection = connection ?? this.defaultConnection;
2064
+ if (resolvedConnection === void 0) throw new CliStructuredError("CONFIG.DB_CONNECTION_REQUIRED", "No connection provided. Pass a connection to connect() or provide a default connection when creating the client.");
2065
+ if (!this.stack?.driver) throw new CliStructuredError("CONFIG.DRIVER_REQUIRED", "Driver is not configured. Pass a driver descriptor when creating the control client to enable database operations.");
2066
+ this.driver = await this.stack.driver.create(resolvedConnection);
2067
+ }
2068
+ async close() {
2069
+ if (this.driver) {
2070
+ await this.driver.close();
2071
+ this.driver = null;
2072
+ }
2073
+ }
2074
+ /**
2075
+ * Construct the control adapter once for a migration operation and return
2076
+ * it, mirroring how the runtime plane builds the execution adapter once in
2077
+ * `createExecutionStack`. Only `dbInit` / `dbUpdate` need it (it lowers the
2078
+ * planner's DDL); read-only operations never build it. The descriptor is
2079
+ * optional on the stack — targets without migrations omit it.
2080
+ */
2081
+ buildControlAdapter() {
2082
+ this.init();
2083
+ if (!this.stack?.adapter) throw new CliStructuredError("MIGRATION.TARGET_UNSUPPORTED", `Target "${this.options.target.targetId}" requires an adapter for migrations`);
2084
+ return this.stack.adapter.create(this.stack);
2085
+ }
2086
+ async ensureConnected() {
2087
+ this.init();
2088
+ if (!this.driver && this.defaultConnection !== void 0) await this.connect(this.defaultConnection);
2089
+ if (!this.driver || !this.familyInstance || !this.frameworkComponents) throw new CliStructuredError("DRIVER.NOT_CONNECTED", "Not connected. Call connect(connection) first.");
2090
+ return {
2091
+ driver: this.driver,
2092
+ familyInstance: this.familyInstance,
2093
+ frameworkComponents: this.frameworkComponents
2094
+ };
2095
+ }
2096
+ async connectWithProgress(connection, action, onProgress) {
2097
+ if (connection === void 0) return;
2098
+ onProgress?.({
2099
+ action,
2100
+ kind: "spanStart",
2101
+ spanId: "connect",
2102
+ label: "Connecting to database..."
2103
+ });
2104
+ try {
2105
+ await this.connect(connection);
2106
+ onProgress?.({
2107
+ action,
2108
+ kind: "spanEnd",
2109
+ spanId: "connect",
2110
+ outcome: "ok"
2111
+ });
2112
+ } catch (error) {
2113
+ onProgress?.({
2114
+ action,
2115
+ kind: "spanEnd",
2116
+ spanId: "connect",
2117
+ outcome: "error"
2118
+ });
2119
+ throw error;
2120
+ }
2121
+ }
2122
+ async verify(options) {
2123
+ const { onProgress } = options;
2124
+ await this.connectWithProgress(options.connection, "verify", onProgress);
2125
+ const { driver, familyInstance } = await this.ensureConnected();
2126
+ let contract;
2127
+ try {
2128
+ contract = familyInstance.deserializeContract(options.contract);
2129
+ } catch (error) {
2130
+ throw structuredError("CONTRACT.VALIDATION_FAILED", error instanceof Error ? error.message : String(error), { cause: error });
2131
+ }
2132
+ onProgress?.({
2133
+ action: "verify",
2134
+ kind: "spanStart",
2135
+ spanId: "verify",
2136
+ label: "Verifying database marker..."
2137
+ });
2138
+ try {
2139
+ const result = await familyInstance.verify({
2140
+ driver,
2141
+ contract,
2142
+ expectedTargetId: this.options.target.targetId,
2143
+ contractPath: ""
2144
+ });
2145
+ onProgress?.({
2146
+ action: "verify",
2147
+ kind: "spanEnd",
2148
+ spanId: "verify",
2149
+ outcome: result.ok ? "ok" : "error"
2150
+ });
2151
+ return result;
2152
+ } catch (error) {
2153
+ onProgress?.({
2154
+ action: "verify",
2155
+ kind: "spanEnd",
2156
+ spanId: "verify",
2157
+ outcome: "error"
2158
+ });
2159
+ throw error;
2160
+ }
2161
+ }
2162
+ async schemaVerify(options) {
2163
+ const { onProgress } = options;
2164
+ await this.connectWithProgress(options.connection, "schemaVerify", onProgress);
2165
+ const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
2166
+ let contract;
2167
+ try {
2168
+ contract = familyInstance.deserializeContract(options.contract);
2169
+ } catch (error) {
2170
+ throw structuredError("CONTRACT.VALIDATION_FAILED", error instanceof Error ? error.message : String(error), { cause: error });
2171
+ }
2172
+ onProgress?.({
2173
+ action: "schemaVerify",
2174
+ kind: "spanStart",
2175
+ spanId: "schemaVerify",
2176
+ label: "Verifying database schema..."
2177
+ });
2178
+ try {
2179
+ const schema = await familyInstance.introspect({
2180
+ driver,
2181
+ contract
2182
+ });
2183
+ const result = familyInstance.verifySchema({
2184
+ contract,
2185
+ schema,
2186
+ strict: options.strict ?? false,
2187
+ frameworkComponents
2188
+ });
2189
+ onProgress?.({
2190
+ action: "schemaVerify",
2191
+ kind: "spanEnd",
2192
+ spanId: "schemaVerify",
2193
+ outcome: result.ok ? "ok" : "error"
2194
+ });
2195
+ return result;
2196
+ } catch (error) {
2197
+ onProgress?.({
2198
+ action: "schemaVerify",
2199
+ kind: "spanEnd",
2200
+ spanId: "schemaVerify",
2201
+ outcome: "error"
2202
+ });
2203
+ throw error;
2204
+ }
2205
+ }
2206
+ async sign(options) {
2207
+ const { onProgress } = options;
2208
+ await this.connectWithProgress(options.connection, "sign", onProgress);
2209
+ const { driver, familyInstance } = await this.ensureConnected();
2210
+ let contract;
2211
+ try {
2212
+ contract = familyInstance.deserializeContract(options.contract);
2213
+ } catch (error) {
2214
+ throw structuredError("CONTRACT.VALIDATION_FAILED", error instanceof Error ? error.message : String(error), { cause: error });
2215
+ }
2216
+ onProgress?.({
2217
+ action: "sign",
2218
+ kind: "spanStart",
2219
+ spanId: "sign",
2220
+ label: "Signing database..."
2221
+ });
2222
+ try {
2223
+ const result = await familyInstance.sign({
2224
+ driver,
2225
+ contract,
2226
+ contractPath: options.contractPath ?? "",
2227
+ ...ifDefined("configPath", options.configPath)
2228
+ });
2229
+ onProgress?.({
2230
+ action: "sign",
2231
+ kind: "spanEnd",
2232
+ spanId: "sign",
2233
+ outcome: "ok"
2234
+ });
2235
+ return result;
2236
+ } catch (error) {
2237
+ onProgress?.({
2238
+ action: "sign",
2239
+ kind: "spanEnd",
2240
+ spanId: "sign",
2241
+ outcome: "error"
2242
+ });
2243
+ throw error;
2244
+ }
2245
+ }
2246
+ async dbInit(options) {
2247
+ const { onProgress } = options;
2248
+ await this.connectWithProgress(options.connection, "dbInit", onProgress);
2249
+ const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
2250
+ if (!hasMigrations(this.options.target)) throw new CliStructuredError("MIGRATION.TARGET_UNSUPPORTED", `Target "${this.options.target.targetId}" does not support migrations`);
2251
+ const adapter = this.buildControlAdapter();
2252
+ let contract;
2253
+ try {
2254
+ contract = familyInstance.deserializeContract(options.contract);
2255
+ } catch (error) {
2256
+ throw structuredError("CONTRACT.VALIDATION_FAILED", error instanceof Error ? error.message : String(error), { cause: error });
2257
+ }
2258
+ return executeDbInit({
2259
+ driver,
2260
+ adapter,
2261
+ familyInstance,
2262
+ contract,
2263
+ mode: options.mode,
2264
+ migrations: this.options.target.migrations,
2265
+ frameworkComponents,
2266
+ migrationsDir: options.migrationsDir,
2267
+ targetId: this.options.target.targetId,
2268
+ extensions: this.options.extensions ?? [],
2269
+ ...ifDefined("onProgress", onProgress)
2270
+ });
2271
+ }
2272
+ async dbUpdate(options) {
2273
+ const { onProgress } = options;
2274
+ await this.connectWithProgress(options.connection, "dbUpdate", onProgress);
2275
+ const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
2276
+ if (!hasMigrations(this.options.target)) throw new CliStructuredError("MIGRATION.TARGET_UNSUPPORTED", `Target "${this.options.target.targetId}" does not support migrations`);
2277
+ const adapter = this.buildControlAdapter();
2278
+ let contract;
2279
+ try {
2280
+ contract = familyInstance.deserializeContract(options.contract);
2281
+ } catch (error) {
2282
+ throw structuredError("CONTRACT.VALIDATION_FAILED", error instanceof Error ? error.message : String(error), { cause: error });
2283
+ }
2284
+ return executeDbUpdate({
2285
+ driver,
2286
+ adapter,
2287
+ familyInstance,
2288
+ contract,
2289
+ mode: options.mode,
2290
+ migrations: this.options.target.migrations,
2291
+ frameworkComponents,
2292
+ migrationsDir: options.migrationsDir,
2293
+ targetId: this.options.target.targetId,
2294
+ extensions: this.options.extensions ?? [],
2295
+ ...ifDefined("acceptDataLoss", options.acceptDataLoss),
2296
+ ...ifDefined("consent", options.consent),
2297
+ ...ifDefined("onProgress", onProgress)
2298
+ });
2299
+ }
2300
+ async dbVerify(options) {
2301
+ const { onProgress } = options;
2302
+ await this.connectWithProgress(options.connection, "dbVerify", onProgress);
2303
+ const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
2304
+ return executeDbVerify({
2305
+ driver,
2306
+ familyInstance,
2307
+ contract: options.contract,
2308
+ migrationsDir: options.migrationsDir,
2309
+ targetId: this.options.target.targetId,
2310
+ extensions: this.options.extensions ?? [],
2311
+ frameworkComponents,
2312
+ mode: options.strict ? "strict" : "lenient",
2313
+ skipSchema: options.skipSchema,
2314
+ skipMarker: options.skipMarker,
2315
+ ...ifDefined("onProgress", onProgress)
2316
+ });
2317
+ }
2318
+ async readMarker() {
2319
+ const { driver, familyInstance } = await this.ensureConnected();
2320
+ return familyInstance.readMarker({
2321
+ driver,
2322
+ space: APP_SPACE_ID
2323
+ });
2324
+ }
2325
+ async readAllMarkers() {
2326
+ const { driver, familyInstance } = await this.ensureConnected();
2327
+ return familyInstance.readAllMarkers({ driver });
2328
+ }
2329
+ /** Reads the per-migration journal; omit `space` to return every space. */
2330
+ async readLedger(space) {
2331
+ const { driver, familyInstance } = await this.ensureConnected();
2332
+ return familyInstance.readLedger({
2333
+ driver,
2334
+ ...ifDefined("space", space)
2335
+ });
2336
+ }
2337
+ async migrate(options) {
2338
+ const { onProgress } = options;
2339
+ await this.connectWithProgress(options.connection, "migrate", onProgress);
2340
+ const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
2341
+ if (!hasMigrations(this.options.target)) throw new CliStructuredError("MIGRATION.TARGET_UNSUPPORTED", `Target "${this.options.target.targetId}" does not support migrations`);
2342
+ let contract;
2343
+ try {
2344
+ contract = familyInstance.deserializeContract(options.contract);
2345
+ } catch (error) {
2346
+ throw structuredError("CONTRACT.VALIDATION_FAILED", error instanceof Error ? error.message : String(error), { cause: error });
2347
+ }
2348
+ return executeMigrate({
2349
+ driver,
2350
+ familyInstance,
2351
+ contract,
2352
+ migrations: this.options.target.migrations,
2353
+ frameworkComponents,
2354
+ migrationsDir: options.migrationsDir,
2355
+ extensions: this.options.extensions ?? [],
2356
+ targetId: this.options.target.targetId,
2357
+ ...ifDefined("refHash", options.refHash),
2358
+ ...ifDefined("refInvariants", options.refInvariants),
2359
+ ...ifDefined("refName", options.refName),
2360
+ ...ifDefined("onProgress", onProgress)
2361
+ });
2362
+ }
2363
+ async introspect(options) {
2364
+ const onProgress = options?.onProgress;
2365
+ await this.connectWithProgress(options?.connection, "introspect", onProgress);
2366
+ const { driver, familyInstance } = await this.ensureConnected();
2367
+ options?.schema;
2368
+ onProgress?.({
2369
+ action: "introspect",
2370
+ kind: "spanStart",
2371
+ spanId: "introspect",
2372
+ label: "Introspecting database schema..."
2373
+ });
2374
+ try {
2375
+ const result = await familyInstance.introspect({ driver });
2376
+ onProgress?.({
2377
+ action: "introspect",
2378
+ kind: "spanEnd",
2379
+ spanId: "introspect",
2380
+ outcome: "ok"
2381
+ });
2382
+ return result;
2383
+ } catch (error) {
2384
+ onProgress?.({
2385
+ action: "introspect",
2386
+ kind: "spanEnd",
2387
+ spanId: "introspect",
2388
+ outcome: "error"
2389
+ });
2390
+ throw error;
2391
+ }
2392
+ }
2393
+ toSchemaView(schemaIR) {
2394
+ this.init();
2395
+ if (this.familyInstance && hasSchemaView(this.familyInstance)) return this.familyInstance.toSchemaView(schemaIR);
2396
+ }
2397
+ inferPslContract(schemaIR) {
2398
+ this.init();
2399
+ if (this.familyInstance && hasPslContractInfer(this.familyInstance)) return this.familyInstance.inferPslContract(schemaIR);
2400
+ }
2401
+ getPslBlockDescriptors() {
2402
+ this.init();
2403
+ return this.stack.authoringContributions.pslBlockDescriptors;
2404
+ }
2405
+ toOperationPreview(operations) {
2406
+ this.init();
2407
+ if (this.familyInstance && hasOperationPreview(this.familyInstance)) return this.familyInstance.toOperationPreview(operations);
2408
+ }
2409
+ async emit(options) {
2410
+ const { onProgress, contractConfig } = options;
2411
+ this.init();
2412
+ if (!this.familyInstance) throw new InternalError("Family instance was not initialized. This is a bug.");
2413
+ let contractRaw;
2414
+ onProgress?.({
2415
+ action: "emit",
2416
+ kind: "spanStart",
2417
+ spanId: "resolveSource",
2418
+ label: "Resolving contract source..."
2419
+ });
2420
+ try {
2421
+ const stack = this.stack;
2422
+ const sourceContext = {
2423
+ composedExtensions: stack.extensions.map((p) => p.id),
2424
+ composedExtensionContracts: stack.extensionContracts,
2425
+ authoringContributions: stack.authoringContributions,
2426
+ codecLookup: stack.codecLookup,
2427
+ controlMutationDefaults: stack.controlMutationDefaults,
2428
+ resolvedInputs: contractConfig.source.inputs ?? [],
2429
+ capabilities: stack.capabilities
2430
+ };
2431
+ const providerResult = await contractConfig.source.load(sourceContext);
2432
+ if (!providerResult.ok) {
2433
+ onProgress?.({
2434
+ action: "emit",
2435
+ kind: "spanEnd",
2436
+ spanId: "resolveSource",
2437
+ outcome: "error"
2438
+ });
2439
+ return notOk({
2440
+ code: "CONTRACT_SOURCE_INVALID",
2441
+ summary: providerResult.failure.summary,
2442
+ why: providerResult.failure.summary,
2443
+ meta: providerResult.failure.meta,
2444
+ diagnostics: providerResult.failure
2445
+ });
2446
+ }
2447
+ contractRaw = providerResult.value;
2448
+ onProgress?.({
2449
+ action: "emit",
2450
+ kind: "spanEnd",
2451
+ spanId: "resolveSource",
2452
+ outcome: "ok"
2453
+ });
2454
+ } catch (error) {
2455
+ onProgress?.({
2456
+ action: "emit",
2457
+ kind: "spanEnd",
2458
+ spanId: "resolveSource",
2459
+ outcome: "error"
2460
+ });
2461
+ const message = error instanceof Error ? error.message : String(error);
2462
+ return notOk({
2463
+ code: "CONTRACT_SOURCE_INVALID",
2464
+ summary: "Failed to resolve contract source",
2465
+ why: message,
2466
+ diagnostics: {
2467
+ summary: "Contract source provider threw an exception",
2468
+ diagnostics: [{
2469
+ code: "PROVIDER_THROW",
2470
+ message
2471
+ }]
2472
+ },
2473
+ meta: void 0
2474
+ });
2475
+ }
2476
+ onProgress?.({
2477
+ action: "emit",
2478
+ kind: "spanStart",
2479
+ spanId: "emit",
2480
+ label: "Emitting contract..."
2481
+ });
2482
+ try {
2483
+ const enrichedIR = enrichContract(contractRaw, this.frameworkComponents ?? []);
2484
+ const rawContractJson = this.options.target.contractSerializer.serializeContract(enrichedIR);
2485
+ let deserializedContract;
2486
+ try {
2487
+ deserializedContract = this.familyInstance.deserializeContract(rawContractJson);
2488
+ } catch (error) {
2489
+ onProgress?.({
2490
+ action: "emit",
2491
+ kind: "spanEnd",
2492
+ spanId: "emit",
2493
+ outcome: "error"
2494
+ });
2495
+ return notOk({
2496
+ code: "CONTRACT_VALIDATION_FAILED",
2497
+ summary: "Contract validation failed",
2498
+ why: error instanceof Error ? error.message : String(error),
2499
+ meta: void 0
2500
+ });
2501
+ }
2502
+ const result = await emit(deserializedContract, this.stack, this.options.family.emission, { serializeContract: (contract) => this.options.target.contractSerializer.serializeContract(contract) });
2503
+ onProgress?.({
2504
+ action: "emit",
2505
+ kind: "spanEnd",
2506
+ spanId: "emit",
2507
+ outcome: "ok"
2508
+ });
2509
+ return ok({
2510
+ storageHash: result.storageHash,
2511
+ ...ifDefined("executionHash", result.executionHash),
2512
+ profileHash: result.profileHash,
2513
+ contractJson: result.contractJson,
2514
+ contractDts: result.contractDts
2515
+ });
2516
+ } catch (error) {
2517
+ onProgress?.({
2518
+ action: "emit",
2519
+ kind: "spanEnd",
2520
+ spanId: "emit",
2521
+ outcome: "error"
2522
+ });
2523
+ return notOk({
2524
+ code: "EMIT_FAILED",
2525
+ summary: "Failed to emit contract",
2526
+ why: error instanceof Error ? error.message : String(error),
2527
+ meta: void 0
2528
+ });
2529
+ }
2530
+ }
2531
+ };
2532
+ /**
2533
+ * Per-output FIFO queue for `executeContractEmit`.
2534
+ *
2535
+ * Ensures that at most one emit (load → resolve source → emit bytes → publish)
2536
+ * runs per output JSON path at a time. Concurrent calls for the same path
2537
+ * line up behind the in-flight one and run in submission order; the user-visible
2538
+ * outcome is "last submission wins on disk" without any supersession bookkeeping.
2539
+ *
2540
+ * Long-lived hosts (Vite dev server, watch CLIs) must call `disposeEmitQueue`
2541
+ * when they stop publishing to a path, otherwise the module-global `Map`
2542
+ * accumulates one entry per unique output path for the lifetime of the process.
2543
+ */
2544
+ const emitQueues = /* @__PURE__ */ new Map();
2545
+ function queueEmitByOutput(outputJsonPath, action) {
2546
+ const next = (emitQueues.get(outputJsonPath) ?? Promise.resolve()).then(action, action);
2547
+ emitQueues.set(outputJsonPath, next);
2548
+ return next;
2549
+ }
2550
+ function disposeEmitQueue(outputJsonPath) {
2551
+ emitQueues.delete(outputJsonPath);
2552
+ }
2553
+ /**
2554
+ * The import root of the project being emitted for, read from its own
2555
+ * manifest.
2556
+ *
2557
+ * Generated files are kept by the application, so every package name in them
2558
+ * has to be one the application can resolve — which means one of its direct
2559
+ * dependencies (ADR 242). The application already states those in
2560
+ * `package.json`, so emission reads them instead of taking a mode from
2561
+ * configuration that could disagree with what is installed.
2562
+ */
2563
+ const DEPENDENCY_FIELDS = ["dependencies", "devDependencies"];
2564
+ function isRecord$2(value) {
2565
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2566
+ }
2567
+ /** The error codes that mean "no manifest here", as opposed to "cannot read it". */
2568
+ const MANIFEST_ABSENT_CODES = /* @__PURE__ */ new Set(["ENOENT", "ENOTDIR"]);
2569
+ function errorCode(error) {
2570
+ if (!isRecord$2(error)) return void 0;
2571
+ const code = error["code"];
2572
+ return typeof code === "string" ? code : void 0;
2573
+ }
2574
+ /**
2575
+ * The nearest `package.json` at or above `from`, or `undefined` if there is
2576
+ * none. The nearest one wins: a package inside a workspace states its own
2577
+ * dependencies, and the workspace root's are somebody else's.
2578
+ *
2579
+ * @throws {CliStructuredError} when a manifest is there but cannot be used —
2580
+ * unreadable, not valid JSON, or not a JSON object. Only "there is no manifest
2581
+ * at this level" continues the walk up; treating a permissions failure that
2582
+ * way would silently emit against the wrong project's dependencies.
2583
+ */
2584
+ function nearestManifest(from) {
2585
+ let dir = from;
2586
+ while (true) {
2587
+ const path = join(dir, "package.json");
2588
+ let raw;
2589
+ try {
2590
+ raw = readFileSync(path, "utf8");
2591
+ } catch (cause) {
2592
+ if (!MANIFEST_ABSENT_CODES.has(errorCode(cause) ?? "")) throw errorRuntime("CLI.PROJECT_MANIFEST_UNREADABLE", `Failed to read ${path}`, {
2593
+ why: `\`${path}\` exists but could not be read: ${cause instanceof Error ? cause.message : String(cause)}`,
2594
+ fix: `Make \`${path}\` readable, then re-run the command. Emission reads it to decide which package names generated files should import.`,
2595
+ meta: { path },
2596
+ cause
2597
+ });
2598
+ const parent = dirname(dir);
2599
+ if (parent === dir) return void 0;
2600
+ dir = parent;
2601
+ continue;
2602
+ }
2603
+ let parsed;
2604
+ try {
2605
+ parsed = JSON.parse(raw);
2606
+ } catch (cause) {
2607
+ throw errorRuntime("CLI.PROJECT_MANIFEST_INVALID", `Failed to parse ${path}`, {
2608
+ why: `\`${path}\` is not valid JSON: ${cause instanceof Error ? cause.message : String(cause)}`,
2609
+ fix: `Fix the JSON syntax in \`${path}\` (a missing comma or unbalanced brace is the most common cause), then re-run the command. Emission reads it to decide which package names generated files should import.`,
2610
+ meta: { path },
2611
+ cause
2612
+ });
2613
+ }
2614
+ if (!isRecord$2(parsed)) throw errorRuntime("CLI.PROJECT_MANIFEST_INVALID", `Failed to read ${path}`, {
2615
+ why: `\`${path}\` is valid JSON but not a JSON object, so it states no dependencies.`,
2616
+ fix: `Make \`${path}\` a JSON object with the usual manifest fields, then re-run the command. Emission reads it to decide which package names generated files should import.`,
2617
+ meta: { path }
2618
+ });
2619
+ return parsed;
2620
+ }
2621
+ }
2622
+ function declaredDependencies(manifest) {
2623
+ return DEPENDENCY_FIELDS.flatMap((field) => {
2624
+ const value = manifest[field];
2625
+ return isRecord$2(value) ? Object.keys(value) : [];
2626
+ });
2627
+ }
2628
+ /**
2629
+ * The import root for the project that owns `configPath`, or for the working
2630
+ * directory when the config was discovered rather than named.
2631
+ *
2632
+ * A project with no manifest, or one that names no published package, is on
2633
+ * the internal root — which emits every specifier exactly as authored, so a
2634
+ * project that has not moved to published names is unaffected.
2635
+ */
2636
+ function projectImportRoot(configPath) {
2637
+ const manifest = nearestManifest(configPath === void 0 ? resolve(".") : dirname(resolve(configPath)));
2638
+ if (manifest === void 0) return internalImportRoot;
2639
+ return importRootForDependencies(declaredDependencies(manifest));
2640
+ }
2641
+ /** The specifier resolver emission should use for the project owning `configPath`. */
2642
+ function createProjectSpecifierResolver(configPath) {
2643
+ return createImportSpecifierResolver(projectImportRoot(configPath));
2644
+ }
2645
+ function isRecord$1(value) {
2646
+ return typeof value === "object" && value !== null;
2647
+ }
2648
+ function createTempArtifactPath(path, publicationToken, phase) {
2649
+ return join(dirname(path), `.${basename(path)}.${process.pid}.${publicationToken}.${phase}.tmp`);
2650
+ }
2651
+ async function readExistingArtifact(path) {
2652
+ try {
2653
+ return { content: await readFile(path, "utf-8") };
2654
+ } catch (error) {
2655
+ if (isRecord$1(error) && error["code"] === "ENOENT") return "remove";
2656
+ throw error;
2657
+ }
2658
+ }
2659
+ async function restoreArtifact(path, previous, publicationToken) {
2660
+ if (previous === "remove") {
2661
+ await rm(path, { force: true });
2662
+ return;
2663
+ }
2664
+ const restorePath = createTempArtifactPath(path, publicationToken, "rollback");
2665
+ await writeFile(restorePath, previous.content, "utf-8");
2666
+ try {
2667
+ await rename(restorePath, path);
2668
+ } finally {
2669
+ await rm(restorePath, { force: true });
2670
+ }
2671
+ }
2672
+ function withRollbackFailureCause(error, rollbackFailures) {
2673
+ const rollbackCause = new AggregateError(rollbackFailures, "Failed to restore published artifacts");
2674
+ if (error instanceof Error) {
2675
+ Object.defineProperty(error, "cause", {
2676
+ value: rollbackCause,
2677
+ configurable: true,
2678
+ writable: true
2679
+ });
2680
+ return error;
2681
+ }
2682
+ return new Error(String(error), { cause: rollbackCause });
2683
+ }
2684
+ async function publishPairWithRollback(entries, publicationToken) {
2685
+ const replaced = [];
2686
+ try {
2687
+ for (const entry of entries) {
2688
+ await rename(entry.tempPath, entry.outputPath);
2689
+ replaced.push(entry);
2690
+ }
2691
+ } catch (error) {
2692
+ const rollbackFailures = (await Promise.allSettled(replaced.map((entry) => restoreArtifact(entry.outputPath, entry.previous, publicationToken)))).flatMap((result) => result.status === "rejected" ? [result.reason] : []);
2693
+ if (rollbackFailures.length > 0) throw withRollbackFailureCause(error, rollbackFailures);
2694
+ throw error;
2695
+ }
2696
+ }
2697
+ async function publishContractArtifactPair({ outputJsonPath, outputDtsPath, contractJson, contractDts, publicationToken, beforePublish }) {
2698
+ const tempJsonPath = createTempArtifactPath(outputJsonPath, publicationToken, "next");
2699
+ const tempDtsPath = createTempArtifactPath(outputDtsPath, publicationToken, "next");
2700
+ try {
2701
+ await writeFile(tempJsonPath, contractJson, "utf-8");
2702
+ await writeFile(tempDtsPath, contractDts, "utf-8");
2703
+ if (await beforePublish?.() === false) return false;
2704
+ const previousJson = await readExistingArtifact(outputJsonPath);
2705
+ await publishPairWithRollback([{
2706
+ tempPath: tempDtsPath,
2707
+ outputPath: outputDtsPath,
2708
+ previous: await readExistingArtifact(outputDtsPath)
2709
+ }, {
2710
+ tempPath: tempJsonPath,
2711
+ outputPath: outputJsonPath,
2712
+ previous: previousJson
2713
+ }], publicationToken);
2714
+ return true;
2715
+ } finally {
2716
+ await Promise.allSettled([rm(tempJsonPath, { force: true }), rm(tempDtsPath, { force: true })]);
2717
+ }
2718
+ }
2719
+ const IMPORT_PATTERN = /import\s+type\s+.*?\s+from\s+['"](@[^/]+\/[^/'"]+)/g;
2720
+ function extractPackageSpecifiers(dtsContent) {
2721
+ const packages = /* @__PURE__ */ new Set();
2722
+ for (const match of dtsContent.matchAll(IMPORT_PATTERN)) {
2723
+ const pkg = match[1];
2724
+ if (pkg) packages.add(pkg);
2725
+ }
2726
+ return [...packages];
2727
+ }
2728
+ function validateContractDeps(dtsContent, projectRoot) {
2729
+ const packages = extractPackageSpecifiers(dtsContent);
2730
+ const resolve = createRequire(`${projectRoot}/package.json`);
2731
+ const missing = [];
2732
+ for (const pkg of packages) try {
2733
+ resolve.resolve(`${pkg}/package.json`);
2734
+ } catch {
2735
+ missing.push(pkg);
2736
+ }
2737
+ if (missing.length === 0) return { missing };
2738
+ return {
2739
+ missing,
2740
+ warning: [
2741
+ "contract.d.ts imports types from packages that are not installed:",
2742
+ missing.map((p) => ` - ${p}`).join("\n"),
2743
+ "",
2744
+ "Install them with your package manager:",
2745
+ ...missing.map((p) => ` ${p}`)
2746
+ ].join("\n")
2747
+ };
2748
+ }
2749
+ const EMIT_ACTION = "emit";
2750
+ const defaultContractEmitDependencies = { emit };
2751
+ function isRecord(value) {
2752
+ return typeof value === "object" && value !== null;
2753
+ }
2754
+ function startSpan(onProgress, spanId, label) {
2755
+ onProgress?.({
2756
+ action: EMIT_ACTION,
2757
+ kind: "spanStart",
2758
+ spanId,
2759
+ label
2760
+ });
2761
+ }
2762
+ function endSpan(onProgress, spanId, outcome) {
2763
+ onProgress?.({
2764
+ action: EMIT_ACTION,
2765
+ kind: "spanEnd",
2766
+ spanId,
2767
+ outcome
2768
+ });
2769
+ }
2770
+ function failedToResolveContractSource(why, fix, meta, cause) {
2771
+ return errorRuntime("CONTRACT.SOURCE_LOAD_FAILED", "Failed to resolve contract source", {
2772
+ why,
2773
+ fix,
2774
+ ...ifDefined("meta", meta),
2775
+ ...ifDefined("cause", cause)
2776
+ });
2777
+ }
2778
+ function diagnosticLocationSuffix(diagnostic) {
2779
+ const sourceId = typeof diagnostic["sourceId"] === "string" ? diagnostic["sourceId"] : void 0;
2780
+ const span = isRecord(diagnostic["span"]) ? diagnostic["span"] : void 0;
2781
+ const start = span && isRecord(span["start"]) ? span["start"] : void 0;
2782
+ const line = start && typeof start["line"] === "number" ? start["line"] : void 0;
2783
+ const column = start && typeof start["column"] === "number" ? start["column"] : void 0;
2784
+ if (sourceId && line !== void 0 && column !== void 0) return ` (${sourceId}:${line}:${column})`;
2785
+ if (sourceId) return ` (${sourceId})`;
2786
+ return "";
2787
+ }
2788
+ function mapDiagnosticsToIssues(diagnostics) {
2789
+ const issues = [];
2790
+ for (const raw of diagnostics) {
2791
+ if (!isRecord(raw)) continue;
2792
+ const code = typeof raw["code"] === "string" ? raw["code"] : "diagnostic";
2793
+ const message = typeof raw["message"] === "string" ? raw["message"] : "";
2794
+ issues.push({
2795
+ kind: code,
2796
+ message: `${message}${diagnosticLocationSuffix(raw)}`
2797
+ });
2798
+ }
2799
+ return issues;
2800
+ }
2801
+ function validateProviderResult(providerResult) {
2802
+ if (!isRecord(providerResult) || typeof providerResult["ok"] !== "boolean") return {
2803
+ ok: false,
2804
+ error: failedToResolveContractSource("Contract source provider returned malformed result shape.", "Ensure contract.source.load resolves to ok(Contract) or notOk({ summary, diagnostics }).")
2805
+ };
2806
+ if (providerResult["ok"]) {
2807
+ if (!("value" in providerResult)) return {
2808
+ ok: false,
2809
+ error: failedToResolveContractSource("Contract source provider returned malformed success result: missing value.", "Ensure contract.source.load success payload is ok(Contract).")
2810
+ };
2811
+ return {
2812
+ ok: true,
2813
+ value: providerResult["value"]
2814
+ };
2815
+ }
2816
+ const failure = providerResult["failure"];
2817
+ if (!isRecord(failure) || typeof failure["summary"] !== "string" || !Array.isArray(failure["diagnostics"])) return {
2818
+ ok: false,
2819
+ error: failedToResolveContractSource("Contract source provider returned malformed failure result: expected summary and diagnostics.", "Ensure contract.source.load failure payload is notOk({ summary, diagnostics, meta? }).")
2820
+ };
2821
+ return {
2822
+ ok: false,
2823
+ error: failedToResolveContractSource(String(failure["summary"]), "Fix contract source diagnostics and return ok(Contract).", {
2824
+ diagnostics: failure["diagnostics"],
2825
+ issues: mapDiagnosticsToIssues(failure["diagnostics"]),
2826
+ ...ifDefined("providerMeta", failure["meta"])
2827
+ })
2828
+ };
2829
+ }
2830
+ /**
2831
+ * Canonical contract emit operation.
2832
+ *
2833
+ * This is the SINGLE publication path used by both the CLI command
2834
+ * (`prisma-next contract emit`) and the Vite plugin
2835
+ * (`@internal/vite-plugin-contract-emit`). New callers must go through this
2836
+ * function rather than re-implementing load → emit → publish.
2837
+ *
2838
+ * The whole flow (load config → resolve source → emit bytes → atomic publish)
2839
+ * is serialized per output JSON path via `queueEmitByOutput`. Concurrent calls
2840
+ * for the same output line up FIFO; the user-visible outcome is "last
2841
+ * submission wins on disk" without any supersession bookkeeping. Within a
2842
+ * single emit, `publishContractArtifactPair` stages temp files, renames
2843
+ * `contract.d.ts` before `contract.json`, and attempts to restore the previous
2844
+ * pair if either rename fails — so type-only consumers never observe a
2845
+ * mismatched pair.
2846
+ *
2847
+ * @throws {CliStructuredError} on config/source/validation problems
2848
+ * @throws {DOMException} `AbortError` if cancelled via `signal`
2849
+ */
2850
+ async function executeContractEmit(options, dependencies = defaultContractEmitDependencies) {
2851
+ const { config, configPath, outputPath, signal = new AbortController().signal, onProgress } = options;
2852
+ const unlessAborted = abortable(signal);
2853
+ if (!config.contract) throw errorContractConfigMissing({ why: "Config.contract is required for emit. Define it in your config: contract: { source: ..., output: ... }" });
2854
+ const contractConfig = config.contract;
2855
+ const effectiveOutput = outputPath !== void 0 ? join(outputPath, "contract.json") : contractConfig.output;
2856
+ if (!effectiveOutput) throw errorContractConfigMissing({ why: "Contract config must have output path. This should not happen if defineConfig() was used." });
2857
+ if (typeof contractConfig.source?.load !== "function") throw errorContractConfigMissing({ why: "Contract config must include a valid source provider object" });
2858
+ let outputPaths;
2859
+ try {
2860
+ outputPaths = getEmittedArtifactPaths(effectiveOutput);
2861
+ } catch (error) {
2862
+ throw errorContractConfigMissing({ why: error instanceof Error ? error.message : String(error) });
2863
+ }
2864
+ const { jsonPath: outputJsonPath, dtsPath: outputDtsPath } = outputPaths;
2865
+ return queueEmitByOutput(outputJsonPath, async () => {
2866
+ const stack = createControlStack(config);
2867
+ const sourceContext = {
2868
+ composedExtensions: stack.extensions.map((p) => p.id),
2869
+ composedExtensionContracts: stack.extensionContracts,
2870
+ authoringContributions: stack.authoringContributions,
2871
+ codecLookup: stack.codecLookup,
2872
+ controlMutationDefaults: stack.controlMutationDefaults,
2873
+ resolvedInputs: contractConfig.source.inputs ?? [],
2874
+ capabilities: stack.capabilities
2875
+ };
2876
+ startSpan(onProgress, "resolveSource", "Resolving contract source...");
2877
+ let providerResult;
2878
+ try {
2879
+ providerResult = await unlessAborted(contractConfig.source.load(sourceContext));
2880
+ } catch (error) {
2881
+ endSpan(onProgress, "resolveSource", "error");
2882
+ if (signal.aborted || isRecord(error) && error["name"] === "AbortError") throw error;
2883
+ throw failedToResolveContractSource(error instanceof Error ? error.message : String(error), "Ensure contract.source.load resolves to ok(Contract) or returns structured diagnostics.", void 0, error);
2884
+ }
2885
+ const validatedContract = validateProviderResult(providerResult);
2886
+ if (!validatedContract.ok) {
2887
+ endSpan(onProgress, "resolveSource", "error");
2888
+ throw validatedContract.error;
2889
+ }
2890
+ endSpan(onProgress, "resolveSource", "ok");
2891
+ startSpan(onProgress, "emit", "Emitting contract...");
2892
+ let emitResult;
2893
+ try {
2894
+ const familyInstance = config.family.create(stack);
2895
+ const rawComponents = [
2896
+ config.target,
2897
+ config.adapter,
2898
+ ...config.extensions ?? []
2899
+ ];
2900
+ const frameworkComponents = assertFrameworkComponentsCompatible(config.family.familyId, config.target.targetId, rawComponents);
2901
+ const enrichedIR = enrichContract(validatedContract.value, frameworkComponents);
2902
+ const rawContractJson = config.target.contractSerializer.serializeContract(enrichedIR);
2903
+ const deserializedContract = familyInstance.deserializeContract(rawContractJson);
2904
+ const { contractSerializer } = config.target;
2905
+ const serializeContract = (c) => contractSerializer.serializeContract(c);
2906
+ emitResult = await unlessAborted(dependencies.emit(deserializedContract, stack, config.family.emission, {
2907
+ outputJsonPath,
2908
+ serializeContract,
2909
+ resolveImportSpecifier: createProjectSpecifierResolver(configPath ?? outputJsonPath),
2910
+ ...ifDefined("shouldPreserveEmpty", contractSerializer.shouldPreserveEmpty),
2911
+ ...ifDefined("sortStorage", contractSerializer.sortStorage)
2912
+ }));
2913
+ } catch (error) {
2914
+ endSpan(onProgress, "emit", "error");
2915
+ throw error;
2916
+ }
2917
+ endSpan(onProgress, "emit", "ok");
2918
+ await unlessAborted(mkdir(dirname(outputJsonPath), { recursive: true }));
2919
+ await publishContractArtifactPair({
2920
+ outputJsonPath,
2921
+ outputDtsPath,
2922
+ contractJson: emitResult.contractJson,
2923
+ contractDts: emitResult.contractDts,
2924
+ publicationToken: String(process.hrtime.bigint())
2925
+ });
2926
+ const validationWarning = validateContractDeps(emitResult.contractDts, dirname(outputDtsPath)).warning;
2927
+ return {
2928
+ storageHash: emitResult.storageHash,
2929
+ ...ifDefined("executionHash", emitResult.executionHash),
2930
+ profileHash: emitResult.profileHash,
2931
+ files: {
2932
+ json: outputJsonPath,
2933
+ dts: outputDtsPath
2934
+ },
2935
+ ...ifDefined("validationWarning", validationWarning)
2936
+ };
2937
+ });
2938
+ }
2939
+ function computeRefAdvancementName(options) {
2940
+ if (options.advanceRef !== void 0) return options.advanceRef;
2941
+ if (options.db === void 0) return "db";
2942
+ return null;
2943
+ }
2944
+ async function readContractIR(contractJson, contractJsonPath) {
2945
+ return {
2946
+ contract: contractJson,
2947
+ contractDts: await readFile(contractJsonPath.replace(/\.json$/i, ".d.ts"), "utf-8")
2948
+ };
2949
+ }
2950
+ async function executeRefAdvancement(refsDir, migrationsDir, name, hash, contractIR) {
2951
+ if (!validateRefName(name)) throw errorInvalidRefName(name);
2952
+ await writeContractSnapshot(migrationsDir, hash, {
2953
+ contractJson: contractIR.contract,
2954
+ contractDts: contractIR.contractDts
2955
+ });
2956
+ await writeRef(refsDir, name, {
2957
+ hash,
2958
+ invariants: []
2959
+ });
2960
+ return {
2961
+ name,
2962
+ hash
2963
+ };
2964
+ }
2965
+ async function buildRefAdvancementFields(options) {
2966
+ const name = computeRefAdvancementName({
2967
+ ...ifDefined("advanceRef", options.advanceRef),
2968
+ ...ifDefined("db", options.db)
2969
+ });
2970
+ if (name === null) return {
2971
+ advancedRef: null,
2972
+ plannedAdvanceRef: null
2973
+ };
2974
+ if (options.mode === "plan") return {
2975
+ advancedRef: null,
2976
+ plannedAdvanceRef: {
2977
+ name,
2978
+ hash: options.hash
2979
+ }
2980
+ };
2981
+ return {
2982
+ advancedRef: await executeRefAdvancement(options.refsDir, options.migrationsDir, name, options.hash, options.contractIR),
2983
+ plannedAdvanceRef: null
2984
+ };
2985
+ }
2986
+ /**
2987
+ * Full ref-advancement phase for db init/update: ok({advancedRef:null, plannedAdvanceRef:null})
2988
+ * when computeRefAdvancementName is null; else readContractIR + buildRefAdvancementFields with
2989
+ * MigrationToolsError mapped, other errors rethrown.
2990
+ */
2991
+ async function resolveRefAdvancementFields(options) {
2992
+ if (computeRefAdvancementName({
2993
+ ...ifDefined("advanceRef", options.advanceRef),
2994
+ ...ifDefined("db", options.db)
2995
+ }) === null) return ok({
2996
+ advancedRef: null,
2997
+ plannedAdvanceRef: null
2998
+ });
2999
+ try {
3000
+ const contractIR = await readContractIR(options.contractJson, options.contractJsonPath);
3001
+ return ok(await buildRefAdvancementFields({
3002
+ ...ifDefined("advanceRef", options.advanceRef),
3003
+ ...ifDefined("db", options.db),
3004
+ refsDir: options.refsDir,
3005
+ migrationsDir: options.migrationsDir,
3006
+ contractIR,
3007
+ mode: options.mode,
3008
+ hash: options.hash
3009
+ }));
3010
+ } catch (error) {
3011
+ if (MigrationToolsError.is(error)) return notOk(error);
3012
+ throw error;
3013
+ }
3014
+ }
3015
+ /** migrate's --advance-ref tail: executeRefAdvancement with MigrationToolsError passed through, others rethrown. */
3016
+ async function advanceRefSafely(args) {
3017
+ try {
3018
+ return ok(await executeRefAdvancement(args.refsDir, args.migrationsDir, args.name, args.hash, args.contractIR));
3019
+ } catch (error) {
3020
+ if (MigrationToolsError.is(error)) return notOk(error);
3021
+ throw error;
3022
+ }
3023
+ }
3024
+ /**
3025
+ * Resolves a user-supplied contract reference to an on-disk contract JSON (snapshot store or emitted contract), shared by db sign and db update --to.
3026
+ */
3027
+ function isEnoent$1(error) {
3028
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
3029
+ }
3030
+ async function resolveContractRefToSnapshot(options) {
3031
+ try {
3032
+ const loaded = await buildReadAggregate(options.config, { migrationsDir: options.migrationsDir });
3033
+ if (!loaded.ok) return notOk(loaded.failure);
3034
+ const graph = loaded.value.aggregate.app.graph();
3035
+ const bundles = loaded.value.aggregate.app.packages;
3036
+ const refs = loaded.value.aggregate.app.refs;
3037
+ const refResult = parseContractRef(options.refInput, {
3038
+ graph,
3039
+ refs
3040
+ });
3041
+ if (!refResult.ok) return notOk(mapRefResolutionError(refResult.failure));
3042
+ const targetHash = refResult.value.hash;
3043
+ if (bundles.find((p) => p.metadata.to === targetHash)) return ok({
3044
+ hash: targetHash,
3045
+ contractJson: blindCast(await readContractSnapshotJson(options.migrationsDir, targetHash)),
3046
+ contractJsonPath: join(contractSnapshotDir(options.migrationsDir, targetHash), "contract.json"),
3047
+ source: "snapshot"
3048
+ });
3049
+ if (!options.fallbackToEmitted) return notOk(errorUnexpected(`No migration bundle found for ${options.missingBundleFlag} "${options.refInput}" (resolved hash: ${targetHash})`, {
3050
+ why: `The ref resolved successfully but no on-disk migration package has a destination (\`to\`) hash matching ${targetHash}.`,
3051
+ fix: "Provide a ref or hash that corresponds to an existing migration package, or run `migration list` to see available migrations."
3052
+ }));
3053
+ let defaultRaw;
3054
+ try {
3055
+ defaultRaw = await readFile(options.contractPathAbsolute, "utf-8");
3056
+ } catch (error) {
3057
+ if (isEnoent$1(error)) return notOk(errorFileNotFound(options.contractPathAbsolute, {
3058
+ why: `Contract file not found at ${options.contractPathAbsolute}`,
3059
+ fix: "Run `prisma-cli contract emit` first to generate the contract",
3060
+ cause: error
3061
+ }));
3062
+ return notOk(errorUnexpected(error instanceof Error ? error.message : String(error), {
3063
+ why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`,
3064
+ cause: error
3065
+ }));
3066
+ }
3067
+ let parsed;
3068
+ try {
3069
+ parsed = castAs(JSON.parse(defaultRaw));
3070
+ } catch (error) {
3071
+ return notOk(errorContractDeserializationFailed(options.contractPathAbsolute, error instanceof Error ? error.message : String(error), { cause: error }));
3072
+ }
3073
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return notOk(errorContractDeserializationFailed(options.contractPathAbsolute, `expected a JSON object, got ${parsed === null ? "null" : Array.isArray(parsed) ? "an array" : `a ${typeof parsed}`}`));
3074
+ const defaultContract = blindCast(parsed);
3075
+ const storage = defaultContract["storage"];
3076
+ if ((storage !== null && typeof storage === "object" ? blindCast(storage)["storageHash"] : void 0) === targetHash) return ok({
3077
+ hash: targetHash,
3078
+ contractJson: defaultContract,
3079
+ contractJsonPath: options.contractPathAbsolute,
3080
+ source: "emitted"
3081
+ });
3082
+ return notOk(errorRuntime("MIGRATION.SNAPSHOT_MISSING", `No contract file found for hash "${targetHash}"`, {
3083
+ why: `Resolved contract reference "${options.refInput}" to hash "${targetHash}" but no migration produces that hash and the emitted contract does not match.`,
3084
+ fix: "Ensure the target contract exists on disk — either as a migration endpoint or as the emitted contract.json."
3085
+ }));
3086
+ } catch (error) {
3087
+ if (MigrationToolsError.is(error)) return notOk(error);
3088
+ if (CliStructuredError$1.is(error)) return notOk(error);
3089
+ throw error;
3090
+ }
3091
+ }
3092
+ /**
3093
+ * Classifies errors caught around migration-tools calls so commands never import the MigrationToolsError class directly.
3094
+ */
3095
+ /** CliStructuredError (including MigrationToolsError) → identity; anything else → null (caller rethrows/wraps). */
3096
+ function mapCaughtMigrationError(error) {
3097
+ if (CliStructuredError$1.is(error)) return error;
3098
+ return null;
3099
+ }
3100
+ function mapContractAtError(error, options) {
3101
+ if (MigrationToolsError.is(error)) switch (error.code) {
3102
+ case "MIGRATION.REF_NOT_RESOLVABLE": return notOk(errorSnapshotMissing(typeof error.meta?.["refName"] === "string" ? error.meta["refName"] : typeof error.meta?.["identifier"] === "string" ? error.meta["identifier"] : "unknown", { cause: error }));
3103
+ case "MIGRATION.CONTRACT_DESERIALIZATION_FAILED": {
3104
+ const filePath = typeof error.meta?.["filePath"] === "string" ? error.meta["filePath"] : "unknown";
3105
+ return notOk(errorContractValidationFailed(`Predecessor contract at ${filePath} failed to deserialize: ${typeof error.meta?.["message"] === "string" ? error.meta["message"] : error.message}`, {
3106
+ where: { path: filePath },
3107
+ cause: error
3108
+ }));
3109
+ }
3110
+ case "MIGRATION.INVALID_JSON": {
3111
+ const filePath = typeof error.meta?.["filePath"] === "string" ? error.meta["filePath"] : "unknown";
3112
+ const message = typeof error.meta?.["parseError"] === "string" ? error.meta["parseError"] : error.message;
3113
+ return notOk(errorContractValidationFailed((options?.artifactRole ?? "from") === "to" ? `Target contract at ${filePath} failed to deserialize: ${message}` : `Predecessor contract at ${filePath} failed to deserialize: ${message}`, {
3114
+ where: { path: filePath },
3115
+ cause: error
3116
+ }));
3117
+ }
3118
+ case "MIGRATION.BUNDLE_NOT_FOUND_FOR_GRAPH_NODE": return notOk(errorUnexpected(error.message, {
3119
+ why: error.why,
3120
+ fix: error.fix,
3121
+ cause: error
3122
+ }));
3123
+ case "MIGRATION.CONTRACT_SNAPSHOT_MISSING": {
3124
+ const expectedPath = typeof error.meta?.["expectedPath"] === "string" ? error.meta["expectedPath"] : "migrations/snapshots/";
3125
+ return notOk(errorFileNotFound(expectedPath, {
3126
+ why: (options?.artifactRole ?? "from") === "to" ? `Target migration is missing its contract snapshot at ${expectedPath}` : `Predecessor migration is missing its contract snapshot at ${expectedPath}`,
3127
+ fix: "Restore migrations/snapshots/ from version control, or re-run the command that produced this migration to regenerate its snapshot.",
3128
+ cause: error
3129
+ }));
3130
+ }
3131
+ default: return notOk(error);
3132
+ }
3133
+ if (CliStructuredError$1.is(error)) return notOk(error);
3134
+ throw error;
3135
+ }
3136
+ /** True when the on-disk graph contains a path fromHash → toHash. */
3137
+ function hasMigrationPath(graph, fromHash, toHash) {
3138
+ return findPath(graph, fromHash, toHash) !== null;
3139
+ }
3140
+ /**
3141
+ * Refusal for a live marker hash that is not a node of the on-disk app graph.
3142
+ * Same errorMarkerMismatch envelope migrate raises today, or null when the marker is a node.
3143
+ */
3144
+ function refuseMarkerOutsideGraph(args) {
3145
+ if (isGraphNode(args.markerHash, args.graph)) return null;
3146
+ return errorMarkerMismatch(args.markerHash, [...args.graph.nodes].sort(), findLatestMigration(args.graph)?.to ?? null);
3147
+ }
3148
+ /**
3149
+ * Invariant refusals shared by migrate and migration status: unknown ref invariants and unsatisfiable invariant paths.
3150
+ */
3151
+ /** Refuses ref invariants neither declared on any graph edge nor present on the live marker. */
3152
+ function refuseUnknownInvariants(args) {
3153
+ const declared = collectDeclaredInvariants(args.graph);
3154
+ const known = new Set(declared);
3155
+ for (const id of args.markerInvariants) known.add(id);
3156
+ const unknown = args.refInvariants.filter((id) => !known.has(id));
3157
+ if (unknown.length === 0) return null;
3158
+ return errorUnknownInvariant({
3159
+ ...ifDefined("refName", args.refName),
3160
+ unknown,
3161
+ declared: [...declared].sort()
3162
+ });
3163
+ }
3164
+ /** Refuses a --to target whose missing invariants no path from originHash satisfies. */
3165
+ function refuseMissingInvariantPath(args) {
3166
+ const outcome = findPathWithDecision(args.graph, args.originHash, args.targetHash, {
3167
+ ...ifDefined("refName", args.refName),
3168
+ required: new Set(args.missing)
3169
+ });
3170
+ if (outcome.kind !== "unsatisfiable") return null;
3171
+ return errorNoInvariantPath({
3172
+ ...ifDefined("refName", args.refName),
3173
+ required: [...args.missing].sort(),
3174
+ missing: outcome.missing,
3175
+ structuralPath: outcome.structuralPath.map(toStructuralEdge)
3176
+ });
3177
+ }
3178
+ /**
3179
+ * Computes the path through the SAME seam as `executeMigrate`:
3180
+ * - `readAllMarkers()` for the from-state (when no `--from` is given), preserving
3181
+ * the full marker including `invariants` (not just `storageHash`).
3182
+ * - `planSpacePath()` (shared with `executeMigrate`) for per-space path selection,
3183
+ * which feeds `resolveRecordedPath()` with the same target hash, target invariants,
3184
+ * and current marker as the real apply path uses.
3185
+ *
3186
+ * Returns BEFORE any write boundary (`runMigration` / marker / DDL). No
3187
+ * DB state is mutated.
3188
+ */
3189
+ async function executeMigrateShowPlan(options) {
3190
+ const config = options.config;
3191
+ const { configPath, migrationsDir, migrationsRelative, refsDir } = resolveMigrationPaths(options.configPath, config, options.cwd);
3192
+ const dbConnection = options.db ?? config.db?.connection;
3193
+ const hasDriver = !!config.driver;
3194
+ const hasExplicitFrom = options.from !== void 0;
3195
+ if (!hasExplicitFrom) {
3196
+ const missingDb = requireLiveDatabase({
3197
+ dbConnection,
3198
+ hasDriver,
3199
+ why: "migrate --show needs a database connection to read the live marker (or pass --from <contract> for an offline preview)",
3200
+ retryCommand: "prisma-cli migrate --show --from <contract>"
3201
+ });
3202
+ if (missingDb) return notOk(missingDb);
3203
+ }
3204
+ let allRefs = {};
3205
+ try {
3206
+ allRefs = await readRefs(refsDir);
3207
+ } catch (error) {
3208
+ if (MigrationToolsError.is(error)) return notOk(error);
3209
+ throw error;
3210
+ }
3211
+ const loaded = await buildReadAggregate(config, { migrationsDir });
3212
+ if (!loaded.ok) return notOk(loaded.failure);
3213
+ const { aggregate, contractHash } = loaded.value;
3214
+ const appGraph = aggregate.app.graph();
3215
+ let targetHash = contractHash;
3216
+ let refInvariants;
3217
+ if (options.to) {
3218
+ const toResult = parseContractRef(options.to, {
3219
+ graph: appGraph,
3220
+ refs: allRefs,
3221
+ contractHash
3222
+ });
3223
+ if (!toResult.ok) return notOk(mapRefResolutionError(toResult.failure));
3224
+ if (toResult.value.provenance.kind === "reserved-db") return notOk(errorDatabaseConnectionRequired({
3225
+ why: "@db is not valid as a --to target; it names the live database state, not a target contract.",
3226
+ commandName: "migrate --show"
3227
+ }));
3228
+ targetHash = toResult.value.hash;
3229
+ if (toResult.value.provenance.kind === "ref") {
3230
+ const refEntry = allRefs[toResult.value.provenance.refName];
3231
+ if (refEntry) refInvariants = refEntry.invariants;
3232
+ }
3233
+ }
3234
+ options.onPreflightComplete?.({
3235
+ configPath,
3236
+ migrationsRelative,
3237
+ dbConnection,
3238
+ hasExplicitFrom
3239
+ });
3240
+ const markerBySpace = /* @__PURE__ */ new Map();
3241
+ const allSpaces = [aggregate.app, ...aggregate.extensions];
3242
+ if (hasExplicitFrom) if (options.from === "@db") {
3243
+ const missingDb = requireLiveDatabase({
3244
+ dbConnection,
3245
+ hasDriver,
3246
+ why: "@db resolves to the live database marker and requires a --db connection",
3247
+ retryCommand: "prisma-cli migrate --show --from @db --db $DATABASE_URL"
3248
+ });
3249
+ if (missingDb) return notOk(missingDb);
3250
+ } else {
3251
+ const fromResult = parseContractRef(options.from, {
3252
+ graph: appGraph,
3253
+ refs: allRefs,
3254
+ contractHash
3255
+ });
3256
+ if (!fromResult.ok) return notOk(mapRefResolutionError(fromResult.failure));
3257
+ if (fromResult.value.provenance.kind === "reserved-db") {
3258
+ const missingDb = requireLiveDatabase({
3259
+ dbConnection,
3260
+ hasDriver,
3261
+ why: "@db resolves to the live database marker and requires a --db connection"
3262
+ });
3263
+ if (missingDb) return notOk(missingDb);
3264
+ } else {
3265
+ const fromHash = fromResult.value.hash;
3266
+ const offlineMarker = fromHash === "empty" ? null : {
3267
+ storageHash: fromHash,
3268
+ invariants: []
3269
+ };
3270
+ markerBySpace.set(aggregate.app.spaceId, offlineMarker);
3271
+ }
3272
+ }
3273
+ const needsLiveMarker = !hasExplicitFrom || options.from === "@db";
3274
+ if (needsLiveMarker) {
3275
+ if (!dbConnection || !hasDriver) return notOk(errorDatabaseConnectionRequired({
3276
+ why: "A database connection is required to read the live marker for migrate --show",
3277
+ commandName: "migrate --show"
3278
+ }));
3279
+ const client = (options.createClient ?? createControlClient)({
3280
+ family: config.family,
3281
+ target: config.target,
3282
+ adapter: config.adapter,
3283
+ driver: config.driver,
3284
+ extensions: config.extensions ?? []
3285
+ });
3286
+ try {
3287
+ await client.connect(dbConnection);
3288
+ const allMarkers = await client.readAllMarkers();
3289
+ for (const space of allSpaces) {
3290
+ const marker = allMarkers.get(space.spaceId);
3291
+ markerBySpace.set(space.spaceId, marker ?? null);
3292
+ }
3293
+ } catch (error) {
3294
+ if (CliStructuredError$1.is(error)) return notOk(error);
3295
+ return notOk(errorUnexpected(error instanceof Error ? error.message : String(error), { why: `Failed to read live DB marker: ${error instanceof Error ? error.message : String(error)}` }));
3296
+ } finally {
3297
+ await closeQuietly(client);
3298
+ }
3299
+ }
3300
+ const canonicalOrderSpaces = [...aggregate.extensions, aggregate.app];
3301
+ const orderedMigrations = [];
3302
+ for (const space of canonicalOrderSpaces) {
3303
+ const isAppSpace = space.spaceId === aggregate.app.spaceId;
3304
+ const headRef = requireHeadRef(space);
3305
+ const outcome = planSpacePath({
3306
+ space,
3307
+ aggregate,
3308
+ targetHash: isAppSpace ? targetHash : headRef.hash,
3309
+ refInvariants: isAppSpace ? refInvariants : void 0,
3310
+ liveMarker: markerBySpace.get(space.spaceId) ?? null
3311
+ });
3312
+ if (outcome.kind === "at-head") continue;
3313
+ if (outcome.kind === "never-planned") return notOk(errorPathUnreachable({
3314
+ code: "MIGRATION_PATH_NOT_FOUND",
3315
+ summary: `No on-disk migrations for contract space "${outcome.spaceId}"`,
3316
+ why: `migrate is replay-only: space "${outcome.spaceId}" has no on-disk migrations but its head ref targets "${outcome.targetHash}".`,
3317
+ meta: {
3318
+ spaceId: outcome.spaceId,
3319
+ target: outcome.targetHash,
3320
+ kind: "neverPlanned"
3321
+ }
3322
+ }));
3323
+ if (outcome.kind === "unreachable") {
3324
+ const fromHash = outcome.liveMarker?.storageHash ?? "empty";
3325
+ return notOk(errorPathUnreachable({
3326
+ code: "MIGRATION_PATH_NOT_FOUND",
3327
+ summary: `No migration path from ${fromHash.slice(0, 14)} to ${outcome.targetHash.slice(0, 14)} in space "${outcome.spaceId}".`,
3328
+ why: `The migration graph has no path from the from-state to the target in space "${outcome.spaceId}".`,
3329
+ meta: {
3330
+ spaceId: outcome.spaceId,
3331
+ from: fromHash,
3332
+ to: outcome.targetHash
3333
+ }
3334
+ }));
3335
+ }
3336
+ if (outcome.kind === "unsatisfiable") return notOk(errorRuntime("MIGRATION.NO_INVARIANT_PATH", `Missing required invariants for space "${outcome.spaceId}"`, {
3337
+ why: `The path requires invariants not available on disk: ${outcome.missing.join(", ")}`,
3338
+ fix: "Add a migration on the path that runs `dataTransform({ invariantId: \"<id>\", … })` for each missing invariant, or retarget the ref to a hash whose path already provides them.",
3339
+ meta: {
3340
+ spaceId: outcome.spaceId,
3341
+ missing: [...outcome.missing]
3342
+ }
3343
+ }));
3344
+ for (const edge of outcome.plan.migrationEdges) orderedMigrations.push({
3345
+ spaceId: space.spaceId,
3346
+ dirName: edge.dirName,
3347
+ migrationHash: edge.migrationHash,
3348
+ from: edge.from,
3349
+ to: edge.to
3350
+ });
3351
+ }
3352
+ const count = orderedMigrations.length;
3353
+ return ok({
3354
+ aggregate,
3355
+ contractHash,
3356
+ migrations: orderedMigrations,
3357
+ summary: count === 0 ? "Already up to date — nothing to run" : `${count} migration${count === 1 ? "" : "s"} will run`,
3358
+ renderMarkerHashBySpace: new Map(allSpaces.map((s) => [s.spaceId, markerBySpace.get(s.spaceId)?.storageHash ?? "empty"])),
3359
+ usedLiveMarker: needsLiveMarker
3360
+ });
3361
+ }
3362
+ function resolveContractRef$1(input, context) {
3363
+ const result = parseContractRef(input, context);
3364
+ return result.ok ? ok(result.value) : notOk(mapRefResolutionError(result.failure));
3365
+ }
3366
+ function resolveMigrationRef(input, context) {
3367
+ const result = parseMigrationRef(input, context);
3368
+ return result.ok ? ok(result.value) : notOk(mapRefResolutionError(result.failure));
3369
+ }
3370
+ function compareSpaceIds(a, b) {
3371
+ if (a === APP_SPACE_ID$1) return b === APP_SPACE_ID$1 ? 0 : -1;
3372
+ if (b === APP_SPACE_ID$1) return 1;
3373
+ if (a < b) return -1;
3374
+ if (a > b) return 1;
3375
+ return 0;
3376
+ }
3377
+ function compareDirNamesDescending(a, b) {
3378
+ if (a.name < b.name) return 1;
3379
+ if (a.name > b.name) return -1;
3380
+ return 0;
3381
+ }
3382
+ /**
3383
+ * Ref names decorating a space's destination contract hashes. The
3384
+ * tolerant `space.refs` deliberately omits the structural `head.json`;
3385
+ * for extension spaces the old enumerator surfaced it as a `head`
3386
+ * decoration on the tip migration, so fold `space.headRef` back in to
3387
+ * keep that output. The app space synthesises its head, so it carries
3388
+ * no on-disk `head` ref to restore.
3389
+ */
3390
+ function listRefsByContractHash(space) {
3391
+ const byHash = new Map(refsByContractHash(space.refs));
3392
+ if (space.spaceId !== APP_SPACE_ID$1 && space.headRef !== null) {
3393
+ const hash = space.headRef.hash;
3394
+ const bucket = byHash.get(hash) ?? [];
3395
+ if (!bucket.includes("head")) byHash.set(hash, [...bucket, HEAD_REF_NAME].sort());
3396
+ }
3397
+ return byHash;
3398
+ }
3399
+ async function orderedOnDiskSpaceIds(projectMigrationsDir) {
3400
+ return (await listContractSpaceDirectories(projectMigrationsDir)).filter(isValidSpaceId).sort(compareSpaceIds);
3401
+ }
3402
+ /**
3403
+ * Project the loaded {@link ContractSpaceAggregate} into the render-ready
3404
+ * {@link MigrationSpaceListEntry} rows `migration list` displays.
3405
+ *
3406
+ * Space membership matches the on-disk contract-space directories (not the
3407
+ * aggregate's always-present synthesized app space when `migrations/app/`
3408
+ * is absent); package and ref data come from `aggregate.space(id)`.
3409
+ */
3410
+ async function migrationSpaceListEntriesFromAggregate(aggregate, projectMigrationsDir) {
3411
+ const spaceIds = await orderedOnDiskSpaceIds(projectMigrationsDir);
3412
+ const spaces = [];
3413
+ for (const spaceId of spaceIds) {
3414
+ const space = aggregate.space(spaceId);
3415
+ if (space === void 0) continue;
3416
+ const refsByHash = listRefsByContractHash(space);
3417
+ const migrations = space.packages.map((pkg) => ({
3418
+ name: pkg.dirName,
3419
+ hash: pkg.metadata.migrationHash,
3420
+ fromContract: pkg.metadata.from,
3421
+ toContract: pkg.metadata.to,
3422
+ operationCount: pkg.ops.length,
3423
+ createdAt: pkg.metadata.createdAt,
3424
+ refs: [...refsByHash.get(pkg.metadata.to) ?? []],
3425
+ providedInvariants: [...pkg.metadata.providedInvariants]
3426
+ })).sort(compareDirNamesDescending);
3427
+ spaces.push({
3428
+ space: spaceId,
3429
+ migrations
3430
+ });
3431
+ }
3432
+ return spaces;
3433
+ }
3434
+ function computeSummary(spaces) {
3435
+ const totalMigrations = spaces.reduce((count, space) => count + space.migrations.length, 0);
3436
+ if (spaces.length <= 1) return `${totalMigrations} migration(s) on disk`;
3437
+ return `${totalMigrations} migration(s) across ${spaces.length} contract space(s)`;
3438
+ }
3439
+ /**
3440
+ * Policy core of `migration list`: validates `--space`, narrows the
3441
+ * pre-enumerated spaces, and assembles a {@link MigrationListResult}.
3442
+ *
3443
+ * - `migrations/` missing or contains no valid space directories →
3444
+ * caller passes `spaces: []`; this synthesizes `[{ spaceId: APP_SPACE_ID, migrations: [] }]`.
3445
+ * - `--space <id>` on an existing-but-empty space → `{ spaceId, migrations: [] }` in the input.
3446
+ * - `--space <id>` on a non-existent (or reserved) space → `SPACE_NOT_FOUND`.
3447
+ */
3448
+ function runMigrationList(inputs) {
3449
+ const { spaces, spaceFilter } = inputs;
3450
+ if (spaceFilter !== void 0 && !isValidSpaceId(spaceFilter)) return notOk(errorInvalidSpaceId(spaceFilter));
3451
+ if (spaceFilter !== void 0 && !spaces.some((s) => s.space === spaceFilter)) return notOk(errorSpaceNotFound(spaceFilter, spaces.map((s) => s.space).sort()));
3452
+ const scopedSpaces = spaceFilter !== void 0 ? spaces.filter((s) => s.space === spaceFilter) : spaces;
3453
+ const resultSpaces = scopedSpaces.length === 0 ? [{
3454
+ space: APP_SPACE_ID$1,
3455
+ migrations: []
3456
+ }] : scopedSpaces;
3457
+ return ok({
3458
+ ok: true,
3459
+ spaces: [...resultSpaces],
3460
+ summary: computeSummary(resultSpaces)
3461
+ });
3462
+ }
3463
+ function looksLikePath(target) {
3464
+ return target.includes("/") || target.includes("\\");
3465
+ }
3466
+ function resolveAppTargetPath(cwd, target, appMigrationsDir, appMigrationsRelative) {
3467
+ const targetPath = resolve(cwd, target);
3468
+ const relativeToApp = relative(appMigrationsDir, targetPath);
3469
+ if (relativeToApp === "" || relativeToApp === "." || relativeToApp.startsWith("..") || isAbsolute(relativeToApp)) return notOk(errorRuntime("MIGRATION.TARGET_NOT_APP_SPACE", "Target must point to an app-space migration", {
3470
+ why: `Expected a path under ${appMigrationsRelative}, got ${target}`,
3471
+ fix: "Pass an app-space migration directory or use a hash prefix."
3472
+ }));
3473
+ return ok(targetPath);
3474
+ }
3475
+ /**
3476
+ * Resolve a filesystem-path target to the migration dir that contains it,
3477
+ * searching each in-scope space's `migrationsDir`. A path is explicit, so
3478
+ * it can belong to at most one space — returns the first match, or `null`
3479
+ * when the path falls outside every space dir.
3480
+ */
3481
+ function resolveTargetPathAcrossSpaces(cwd, target, spaces) {
3482
+ const targetPath = resolve(cwd, target);
3483
+ for (const space of spaces) {
3484
+ const rel = relative(space.migrationsDir, targetPath);
3485
+ if (!(rel === "" || rel === "." || rel.startsWith("..") || isAbsolute(rel))) return targetPath;
3486
+ }
3487
+ return null;
3488
+ }
3489
+ function findPackageByDirPath(packages, resolvedDirPath) {
3490
+ const normalized = resolve(resolvedDirPath);
3491
+ return packages.find((p) => resolve(p.dirPath) === normalized);
3492
+ }
3493
+ /**
3494
+ * Policy core of `migration check`: per-space explicit graph checks, aggregate-integrity loading, and single-target resolution.
3495
+ */
3496
+ function migrationPathRelative(cwd, dirPath) {
3497
+ return relative(cwd, dirPath);
3498
+ }
3499
+ function migrationFileRelative(cwd, dirPath, fileName) {
3500
+ return join(migrationPathRelative(cwd, dirPath), fileName);
3501
+ }
3502
+ function checkFileExists(space, dirPath, dirName, fileName) {
3503
+ if (!existsSync(join(dirPath, fileName))) return {
3504
+ space: space.spaceId,
3505
+ code: "MIGRATION.CHECK_FILE_MISSING",
3506
+ where: migrationFileRelative(space.cwd, dirPath, fileName),
3507
+ why: `${fileName} is missing from ${dirName}`,
3508
+ nextActions: [chooseAction("Re-emit the migration package, or restore it from version control")]
3509
+ };
3510
+ return null;
3511
+ }
3512
+ /**
3513
+ * Snapshot-store consistency check for one migration package (D6.5):
3514
+ * absent store entry is not an issue (runner independence, ADR 199 — a
3515
+ * package dir with only `migration.json` + `ops.json` is legitimate); a
3516
+ * present entry whose inner `storage.storageHash` disagrees with
3517
+ * `pkg.metadata.to` is `MIGRATION.CHECK_SNAPSHOT_HASH_MISMATCH`; an unparseable store
3518
+ * entry (or a malformed `to`) is `MIGRATION.CHECK_SNAPSHOT_UNPARSEABLE`.
3519
+ */
3520
+ async function checkSnapshotConsistency(space, pkg, migrationsDir) {
3521
+ const spaceId = space.spaceId;
3522
+ let snapshotDir;
3523
+ try {
3524
+ snapshotDir = contractSnapshotDir(migrationsDir, pkg.metadata.to);
3525
+ } catch {
3526
+ return {
3527
+ space: spaceId,
3528
+ code: "MIGRATION.CHECK_SNAPSHOT_UNPARSEABLE",
3529
+ where: migrationPathRelative(space.cwd, pkg.dirPath),
3530
+ why: `Migration "${pkg.dirName}" declares to="${pkg.metadata.to}", which is not a well-formed contract snapshot hash.`,
3531
+ nextActions: [chooseAction("Re-emit the migration package so migration.json declares a valid 64-hex to-hash")]
3532
+ };
3533
+ }
3534
+ let raw;
3535
+ try {
3536
+ raw = await readContractSnapshotJson(migrationsDir, pkg.metadata.to);
3537
+ } catch (error) {
3538
+ if (MigrationToolsError.is(error) && error.code === "MIGRATION.CONTRACT_SNAPSHOT_MISSING") return null;
3539
+ return {
3540
+ space: spaceId,
3541
+ code: "MIGRATION.CHECK_SNAPSHOT_UNPARSEABLE",
3542
+ where: migrationPathRelative(space.cwd, pkg.dirPath),
3543
+ why: `Migration "${pkg.dirName}" has an unparseable contract snapshot at ${snapshotDir}/contract.json.`,
3544
+ nextActions: [chooseAction("Restore migrations/snapshots/ from version control"), chooseAction("Or re-run the command that produced this migration to regenerate its snapshot")]
3545
+ };
3546
+ }
3547
+ const snapshotHash = (raw !== null && typeof raw === "object" ? raw : {})["storage"]?.["storageHash"];
3548
+ if (typeof snapshotHash === "string" && snapshotHash !== pkg.metadata.to) return {
3549
+ space: spaceId,
3550
+ code: "MIGRATION.CHECK_SNAPSHOT_HASH_MISMATCH",
3551
+ where: migrationPathRelative(space.cwd, pkg.dirPath),
3552
+ why: `Migration "${pkg.dirName}" declares to=${pkg.metadata.to} but its contract snapshot has storageHash=${snapshotHash}`,
3553
+ nextActions: [chooseAction("Re-emit the migration package so migration.json and its contract snapshot agree")]
3554
+ };
3555
+ return null;
3556
+ }
3557
+ /**
3558
+ * Project the loaded {@link ContractSpaceAggregate} into the
3559
+ * {@link CheckSpace} rows the multi-space check iterates — one per on-disk
3560
+ * contract-space directory, in the aggregate's `app`-first ordering. Mirrors
3561
+ * `migration list`'s `migrationSpaceListEntriesFromAggregate`: space
3562
+ * membership matches the on-disk directories, package / ref / graph data come
3563
+ * from `aggregate.space(id)`.
3564
+ */
3565
+ async function enumerateCheckSpaces(aggregate, projectMigrationsDir, cwd) {
3566
+ const candidateDirs = await listContractSpaceDirectories(projectMigrationsDir);
3567
+ const onDiskSpaceIds = new Set(candidateDirs.filter(isValidSpaceId));
3568
+ const spaces = [];
3569
+ for (const space of aggregate.spaces()) {
3570
+ const spaceId = space.spaceId;
3571
+ if (!isValidSpaceId(spaceId)) continue;
3572
+ if (!onDiskSpaceIds.has(spaceId)) continue;
3573
+ const migrationsDir = spaceMigrationDirectory(projectMigrationsDir, spaceId);
3574
+ spaces.push({
3575
+ spaceId,
3576
+ packages: space.packages,
3577
+ refs: space.refs,
3578
+ graph: space.graph(),
3579
+ migrationsDir,
3580
+ refsDir: spaceRefsDirectory(migrationsDir),
3581
+ projectMigrationsDir,
3582
+ cwd
3583
+ });
3584
+ }
3585
+ return spaces;
3586
+ }
3587
+ function checkManifestFilesPresent(space) {
3588
+ if (!existsSync(space.migrationsDir)) return [];
3589
+ const loadedDirNames = new Set(space.packages.map((p) => p.dirName));
3590
+ const failures = [];
3591
+ let entries;
3592
+ try {
3593
+ entries = readdirSync(space.migrationsDir);
3594
+ } catch {
3595
+ return failures;
3596
+ }
3597
+ for (const entry of entries) {
3598
+ if (entry.startsWith(".") || entry.startsWith("_") || entry === "refs") continue;
3599
+ const entryPath = join(space.migrationsDir, entry);
3600
+ try {
3601
+ if (!statSync(entryPath).isDirectory()) continue;
3602
+ } catch {
3603
+ continue;
3604
+ }
3605
+ if (!loadedDirNames.has(entry)) for (const f of ["migration.json", "ops.json"]) {
3606
+ const fail = checkFileExists(space, entryPath, entry, f);
3607
+ if (fail) failures.push(fail);
3608
+ }
3609
+ }
3610
+ return failures;
3611
+ }
3612
+ function checkReachability(space) {
3613
+ const allToHashes = new Set(space.packages.map((p) => p.metadata.to));
3614
+ const failures = [];
3615
+ for (const pkg of space.packages) if (!(pkg.metadata.from === null || allToHashes.has(pkg.metadata.from) || pkg.metadata.from === "empty")) failures.push({
3616
+ space: space.spaceId,
3617
+ code: "MIGRATION.CHECK_UNREACHABLE_MIGRATION",
3618
+ where: migrationPathRelative(space.cwd, pkg.dirPath),
3619
+ why: `Migration "${pkg.dirName}" starts from ${pkg.metadata.from} which no other migration produces`,
3620
+ nextActions: [chooseAction("Delete the unreachable migration"), chooseAction("Or re-emit a migration that connects it to the graph")]
3621
+ });
3622
+ return failures;
3623
+ }
3624
+ function checkDanglingRefs(space) {
3625
+ const failures = [];
3626
+ for (const [name, entry] of Object.entries(space.refs)) if (!space.graph.nodes.has(entry.hash)) failures.push({
3627
+ space: space.spaceId,
3628
+ code: "MIGRATION.CHECK_DANGLING_REF",
3629
+ where: relative(space.cwd, join(space.refsDir, `${name}.json`)),
3630
+ why: `Ref "${name}" points at ${entry.hash} which does not exist in the migration graph`,
3631
+ nextActions: [runCommandAction("Point the ref at a graph node", `prisma-cli ref set ${name} <valid-hash>`), chooseAction("Or delete the ref")]
3632
+ });
3633
+ return failures;
3634
+ }
3635
+ async function checkSpace(space) {
3636
+ const snapshotFailures = await Promise.all(space.packages.map((pkg) => checkSnapshotConsistency(space, pkg, space.projectMigrationsDir)));
3637
+ return [
3638
+ ...checkManifestFilesPresent(space),
3639
+ ...snapshotFailures.filter((f) => f !== null),
3640
+ ...checkReachability(space),
3641
+ ...checkDanglingRefs(space)
3642
+ ];
3643
+ }
3644
+ /**
3645
+ * Policy core of the holistic `migration check`: validates `--space`,
3646
+ * narrows the pre-enumerated spaces, and runs the per-space explicit graph
3647
+ * checks (file-existence, snapshot consistency, reachability, dangling
3648
+ * refs), aggregating every failure into one {@link MigrationCheckResult}.
3649
+ *
3650
+ * `--space` validation mirrors `migration list`: an invalid id →
3651
+ * {@link errorInvalidSpaceId}; an id with no on-disk space →
3652
+ * {@link errorSpaceNotFound}. Both map to exit `PRECONDITION` at the shell.
3653
+ * Aggregate-integrity violations (which already span every space) are folded
3654
+ * in by the caller, not here.
3655
+ */
3656
+ async function runMigrationCheck(inputs) {
3657
+ const { spaces, spaceFilter } = inputs;
3658
+ if (spaceFilter !== void 0 && !isValidSpaceId(spaceFilter)) return notOk(errorInvalidSpaceId(spaceFilter));
3659
+ if (spaceFilter !== void 0 && !spaces.some((s) => s.spaceId === spaceFilter)) return notOk(errorSpaceNotFound(spaceFilter, spaces.map((s) => s.spaceId).sort()));
3660
+ const scopedSpaces = spaceFilter !== void 0 ? spaces.filter((s) => s.spaceId === spaceFilter) : spaces;
3661
+ const failures = (await Promise.all(scopedSpaces.map(checkSpace))).flat();
3662
+ if (failures.length === 0) return ok({
3663
+ ok: true,
3664
+ failures: [],
3665
+ summary: "All checks passed"
3666
+ });
3667
+ return ok({
3668
+ ok: false,
3669
+ failures,
3670
+ summary: `${failures.length} integrity failure(s)`
3671
+ });
3672
+ }
3673
+ async function loadAggregateIntegrityViolations(config, migrationsDir) {
3674
+ try {
3675
+ const contractJsonContent = await readFile(resolveContractPath(config), "utf-8");
3676
+ const familyInstance = config.family.create(createControlStack(config));
3677
+ const declaredExtensions = toDeclaredExtensionsFromRaw(config.extensions ?? []);
3678
+ const parsedAppContract = JSON.parse(contractJsonContent);
3679
+ return (await loadContractSpaceAggregate({
3680
+ migrationsDir,
3681
+ deserializeContract: (json) => familyInstance.deserializeContract(json),
3682
+ appContract: familyInstance.deserializeContract(parsedAppContract)
3683
+ })).checkIntegrity({
3684
+ declaredExtensions,
3685
+ checkContracts: true
3686
+ });
3687
+ } catch {
3688
+ return [];
3689
+ }
3690
+ }
3691
+ /**
3692
+ * Ranks ref-resolution failure kinds by how informative they are, so a
3693
+ * single-target check surfaces the most useful failure across spaces instead of
3694
+ * whichever space failed first. `not-found` (the input matched nothing here)
3695
+ * says the least; a malformed input, a wrong grammar, or an in-space ambiguity
3696
+ * all say more.
3697
+ */
3698
+ function refFailureSpecificity(error) {
3699
+ switch (error.kind) {
3700
+ case "wrong-grammar": return 3;
3701
+ case "ambiguous": return 2;
3702
+ case "invalid-format": return 1;
3703
+ case "not-found": return 0;
3704
+ }
3705
+ }
3706
+ /**
3707
+ * Single-target (`check <ref/path>`) mode — resolves a migration reference
3708
+ * across all contract spaces (or the one space narrowed by `--space <id>`).
3709
+ *
3710
+ * Resolution:
3711
+ * - filesystem path → find the owning space by checking which space's
3712
+ * `migrationsDir` contains the resolved path; falls back to app-relative
3713
+ * validation when the path is outside every space dir.
3714
+ * - ref → `parseMigrationRef` against each in-scope space; collect every
3715
+ * (space, package) hit; 0 hits = not-found, 1 = check it, >1 = ambiguity
3716
+ * error (qualify with `--space`).
3717
+ *
3718
+ * `--space <id>` is validated the same way the holistic path does it:
3719
+ * invalid id → `errorInvalidSpaceId`; no on-disk space → `errorSpaceNotFound`.
3720
+ */
3721
+ async function checkSingleTarget(target, inputs) {
3722
+ const { spaces, spaceFilter, appMigrationsDir, appMigrationsRelative, cwd } = inputs;
3723
+ if (spaceFilter !== void 0 && !isValidSpaceId(spaceFilter)) return {
3724
+ error: errorInvalidSpaceId(spaceFilter),
3725
+ exitCode: 2
3726
+ };
3727
+ if (spaceFilter !== void 0 && !spaces.some((s) => s.spaceId === spaceFilter)) return {
3728
+ error: errorSpaceNotFound(spaceFilter, spaces.map((s) => s.spaceId).sort()),
3729
+ exitCode: 2
3730
+ };
3731
+ const scopedSpaces = spaceFilter !== void 0 ? spaces.filter((s) => s.spaceId === spaceFilter) : spaces;
3732
+ let matchedSpace;
3733
+ let matchedPkg;
3734
+ if (looksLikePath(target)) {
3735
+ const resolvedPath = resolveTargetPathAcrossSpaces(cwd, target, scopedSpaces);
3736
+ if (resolvedPath !== null) for (const space of scopedSpaces) {
3737
+ const found = findPackageByDirPath(space.packages, resolvedPath);
3738
+ if (found) {
3739
+ matchedSpace = space;
3740
+ matchedPkg = found;
3741
+ break;
3742
+ }
3743
+ }
3744
+ else {
3745
+ const resolved = resolveAppTargetPath(cwd, target, appMigrationsDir, appMigrationsRelative);
3746
+ if (!resolved.ok) return {
3747
+ error: resolved.failure,
3748
+ exitCode: 2
3749
+ };
3750
+ const appSpace = scopedSpaces.find((s) => s.spaceId === "app");
3751
+ if (appSpace) {
3752
+ matchedSpace = appSpace;
3753
+ matchedPkg = findPackageByDirPath(appSpace.packages, resolved.value);
3754
+ }
3755
+ }
3756
+ } else {
3757
+ const hits = [];
3758
+ let bestParseFailure;
3759
+ for (const space of scopedSpaces) {
3760
+ const migResult = parseMigrationRef(target, {
3761
+ graph: space.graph,
3762
+ refs: space.refs
3763
+ });
3764
+ if (!migResult.ok) {
3765
+ if (bestParseFailure === void 0 || refFailureSpecificity(migResult.failure) > refFailureSpecificity(bestParseFailure)) bestParseFailure = migResult.failure;
3766
+ continue;
3767
+ }
3768
+ const pkg = space.packages.find((p) => p.metadata.migrationHash === migResult.value.migrationHash);
3769
+ if (pkg) hits.push({
3770
+ space,
3771
+ pkg
3772
+ });
3773
+ }
3774
+ if (hits.length > 1) return {
3775
+ error: errorAmbiguousMigrationRef(target, hits.map((h) => h.space.spaceId)),
3776
+ exitCode: 2
3777
+ };
3778
+ if (hits.length === 1) {
3779
+ matchedSpace = hits[0].space;
3780
+ matchedPkg = hits[0].pkg;
3781
+ } else if (bestParseFailure !== void 0) return {
3782
+ error: mapRefResolutionError(bestParseFailure),
3783
+ exitCode: 2
3784
+ };
3785
+ }
3786
+ if (!matchedPkg || !matchedSpace) return {
3787
+ result: {
3788
+ ok: false,
3789
+ failures: [],
3790
+ summary: `Migration package for "${target}" not found on disk`
3791
+ },
3792
+ exitCode: 2
3793
+ };
3794
+ const failures = [...checkManifestFilesPresent(matchedSpace)];
3795
+ for (const f of ["migration.json", "ops.json"]) {
3796
+ const fail = checkFileExists(matchedSpace, matchedPkg.dirPath, matchedPkg.dirName, f);
3797
+ if (fail) failures.push(fail);
3798
+ }
3799
+ const verification = verifyMigrationHash(matchedPkg);
3800
+ if (!verification.ok) failures.push({
3801
+ space: matchedSpace.spaceId,
3802
+ code: "MIGRATION.CHECK_HASH_MISMATCH",
3803
+ where: migrationFileRelative(matchedSpace.cwd, matchedPkg.dirPath, "migration.json"),
3804
+ why: `Stored hash ${verification.storedHash} does not match recomputed hash ${verification.computedHash}`,
3805
+ nextActions: [chooseAction("Re-emit the migration package, or restore it from version control")]
3806
+ });
3807
+ const snapshotFailure = await checkSnapshotConsistency(matchedSpace, matchedPkg, matchedSpace.projectMigrationsDir);
3808
+ if (snapshotFailure) failures.push(snapshotFailure);
3809
+ const resolvedSpaceId = matchedSpace.spaceId !== "app" ? matchedSpace.spaceId : void 0;
3810
+ if (failures.length === 0) return {
3811
+ result: {
3812
+ ok: true,
3813
+ failures: [],
3814
+ summary: "All checks passed"
3815
+ },
3816
+ exitCode: 0,
3817
+ ...ifDefined("resolvedSpaceId", resolvedSpaceId)
3818
+ };
3819
+ return {
3820
+ result: {
3821
+ ok: false,
3822
+ failures,
3823
+ summary: `${failures.length} integrity failure(s)`
3824
+ },
3825
+ exitCode: 4,
3826
+ ...ifDefined("resolvedSpaceId", resolvedSpaceId)
3827
+ };
3828
+ }
3829
+ /**
3830
+ * Project scoped list entries into the per-space contracts+migrations rows the migration
3831
+ * graph --json output serializes (EMPTY_CONTRACT_HASH from-hash → null). Skips space ids
3832
+ * the aggregate no longer resolves, mirroring the command loop.
3833
+ */
3834
+ function buildMigrationSpaceGraphEntries(args) {
3835
+ const spaces = [];
3836
+ for (const spaceEntry of args.scopedSpaces) {
3837
+ const space = args.aggregate.space(spaceEntry.space);
3838
+ if (space === void 0) continue;
3839
+ const graph = space.graph();
3840
+ const refsByHash = listRefsByContractHash(space);
3841
+ spaces.push({
3842
+ space: spaceEntry.space,
3843
+ contracts: [...graph.nodes].map((hash) => ({
3844
+ hash,
3845
+ refs: [...refsByHash.get(hash) ?? []]
3846
+ })),
3847
+ migrations: [...graph.migrationByHash.values()].map((edge) => ({
3848
+ name: edge.dirName,
3849
+ hash: edge.migrationHash,
3850
+ fromContract: edge.from === "empty" ? null : edge.from,
3851
+ toContract: edge.to
3852
+ }))
3853
+ });
3854
+ }
3855
+ return spaces;
3856
+ }
3857
+ /**
3858
+ * Policy core of `migration new`: scaffolds a migration package with a `migration.ts` stub for manual authoring.
3859
+ */
3860
+ async function executeMigrationNewCommand(options) {
3861
+ const config = options.config;
3862
+ const cwd = options.cwd;
3863
+ const { migrationsDir, appMigrationsDir, appMigrationsRelative } = resolveMigrationPaths(options.configPath, config, cwd);
3864
+ const stack = createControlStack(config);
3865
+ const familyInstance = config.family.create(stack);
3866
+ const controlAdapter = config.adapter.create(stack);
3867
+ const contractPathAbsolute = resolveContractPath(config);
3868
+ let contractJsonContent;
3869
+ try {
3870
+ contractJsonContent = await readFile(contractPathAbsolute, "utf-8");
3871
+ } catch (error) {
3872
+ if (error instanceof Error && error.code === "ENOENT") return notOk(errorRuntime("CLI.FILE_NOT_FOUND", `Contract file not found at ${contractPathAbsolute}`, {
3873
+ why: `Contract file not found at ${contractPathAbsolute}`,
3874
+ fix: "Run `prisma-cli contract emit` first to generate the contract"
3875
+ }));
3876
+ throw error;
3877
+ }
3878
+ let toContract;
3879
+ try {
3880
+ const parsedContract = JSON.parse(contractJsonContent);
3881
+ toContract = familyInstance.deserializeContract(parsedContract);
3882
+ } catch (error) {
3883
+ return notOk(errorRuntime("MIGRATION.CONTRACT_DESERIALIZATION_FAILED", "Contract JSON is invalid", {
3884
+ why: `Failed to deserialize ${contractPathAbsolute}: ${error instanceof Error ? error.message : String(error)}`,
3885
+ fix: "Run `prisma-cli contract emit` to regenerate the contract",
3886
+ cause: error
3887
+ }));
3888
+ }
3889
+ const toStorageHash = toContract.storage?.storageHash;
3890
+ if (typeof toStorageHash !== "string") return notOk(errorRuntime("CONTRACT.VALIDATION_FAILED", "Contract is missing storageHash", {
3891
+ why: `Contract at ${contractPathAbsolute} has no storageHash`,
3892
+ fix: "Run `prisma-cli contract emit` to regenerate the contract"
3893
+ }));
3894
+ const aggregate = await loadContractSpaceAggregate({
3895
+ migrationsDir,
3896
+ deserializeContract: (json) => familyInstance.deserializeContract(json),
3897
+ appContract: toContract
3898
+ });
3899
+ const packageCorruptionFailure = refusePackageCorruptionOnAggregate(aggregate);
3900
+ if (packageCorruptionFailure) return notOk(packageCorruptionFailure);
3901
+ const packages = aggregate.app.packages;
3902
+ const graph = aggregate.app.graph();
3903
+ let fromHash = null;
3904
+ if (packages.length > 0) if (options.from) {
3905
+ const match = packages.find((p) => p.metadata.to.startsWith(options.from));
3906
+ if (!match) return notOk(errorRuntime("MIGRATION.HASH_NOT_IN_GRAPH", "Starting contract not found", {
3907
+ why: `No migration with to hash matching "${options.from}" exists in ${appMigrationsRelative}`,
3908
+ fix: "Check that the --from hash matches a known migration target hash."
3909
+ }));
3910
+ fromHash = match.metadata.to;
3911
+ } else {
3912
+ const latestMigration = findLatestMigration(graph);
3913
+ if (latestMigration) fromHash = latestMigration.to;
3914
+ }
3915
+ if (fromHash === toStorageHash && !options.from) return notOk(errorRuntime("MIGRATION.NO_CHANGES", "No changes detected", {
3916
+ why: "The from and to contract hashes are identical — there is nothing to migrate.",
3917
+ fix: "Change the contract and run `prisma-cli contract emit` before creating a new migration. To author a data-only migration on the current contract hash, pass `--from <hash>` explicitly."
3918
+ }));
3919
+ const timestamp = /* @__PURE__ */ new Date();
3920
+ const packageDir = join(appMigrationsDir, formatMigrationDirName(timestamp, options.name ?? "migration"));
3921
+ const baseMetadata = {
3922
+ from: fromHash,
3923
+ to: toStorageHash,
3924
+ providedInvariants: [],
3925
+ createdAt: timestamp.toISOString()
3926
+ };
3927
+ const metadata = {
3928
+ ...baseMetadata,
3929
+ migrationHash: computeMigrationHash(baseMetadata, [])
3930
+ };
3931
+ const migrations = getTargetMigrations(config.target);
3932
+ if (!migrations) return notOk(errorTargetMigrationNotSupported({ why: `Target "${config.target.targetId}" does not support migrations` }));
3933
+ try {
3934
+ assertFrameworkComponentsCompatible(config.family.familyId, config.target.targetId, [
3935
+ config.target,
3936
+ config.adapter,
3937
+ ...config.extensions ?? []
3938
+ ]);
3939
+ const resolveSpecifier = createProjectSpecifierResolver(options.configPath);
3940
+ await writeMigrationPackage(packageDir, metadata, []);
3941
+ const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);
3942
+ const [contractJsonRaw, contractDts] = await Promise.all([readFile(destinationArtifacts.jsonPath, "utf-8"), readFile(destinationArtifacts.dtsPath, "utf-8")]);
3943
+ await writeContractSnapshot(migrationsDir, toStorageHash, {
3944
+ contractJson: JSON.parse(contractJsonRaw),
3945
+ contractDts
3946
+ });
3947
+ await writeMigrationTs(packageDir, migrations.createPlanner(controlAdapter).emptyMigration({
3948
+ packageDir,
3949
+ contractJsonPath: join(contractSnapshotDir(migrationsDir, toStorageHash), "contract.json"),
3950
+ fromHash,
3951
+ toHash: toStorageHash,
3952
+ snapshotsImportPath: snapshotsImportPathFrom(packageDir, migrationsDir)
3953
+ }, APP_SPACE_ID).renderTypeScript(resolveSpecifier));
3954
+ return ok({
3955
+ ok: true,
3956
+ dir: relative(cwd, packageDir),
3957
+ from: fromHash,
3958
+ to: toStorageHash,
3959
+ summary: `Scaffolded migration at ${relative(cwd, packageDir)}`
3960
+ });
3961
+ } catch (error) {
3962
+ if (CliStructuredError$1.is(error)) return notOk(error);
3963
+ return notOk(errorUnexpected(error instanceof Error ? error.message : String(error), { why: `Failed to scaffold migration: ${error instanceof Error ? error.message : String(error)}` }));
3964
+ }
3965
+ }
3966
+ /**
3967
+ * Phase-1 of the two-phase `migration plan` pipeline (sub-spec § 4).
3968
+ *
3969
+ * For every extension that exposes a `contractSpace`:
3970
+ *
3971
+ * 1. Read the on-disk head ref (returns `null` on first emit).
3972
+ * 2. Write the head contract into the migrations-root snapshot store
3973
+ * (write-if-absent, keyed by hash) and unconditionally re-emit
3974
+ * `refs/head.json`, via {@link emitContractSpaceArtifacts}. The
3975
+ * framework owns `refs/head.json`; re-emit is the contract for that
3976
+ * file. The snapshot itself is only written once per distinct hash.
3977
+ * 3. Materialise any descriptor-shipped migration packages not yet on
3978
+ * disk via {@link materialiseExtensionMigrationPackageIfMissing}.
3979
+ * Existing packages are left untouched (by-existence skip).
3980
+ *
3981
+ * The return value lets the caller render a per-space status line and
3982
+ * lets the phase-2 aggregate loader run on a now-consistent disk state
3983
+ * (every loaded extension is guaranteed to have its head ref pinned
3984
+ * to the descriptor's hash and to ship every package the descriptor
3985
+ * declares).
3986
+ *
3987
+ * Output ordering is deterministic and alphabetical by spaceId (via
3988
+ * {@link planAllSpaces}, which also detects duplicate spaceIds). This
3989
+ * matches the canonical sort order used by every other aggregate
3990
+ * surface (`migrate`, `migration status`, the runner).
3991
+ */
3992
+ async function runContractSpaceSeedPhase(inputs) {
3993
+ const planned = planAllSpaces(inputs.extensions.filter((pack) => pack.contractSpace !== void 0).map((pack) => ({
3994
+ spaceId: pack.id,
3995
+ priorContract: null,
3996
+ newContract: pack.contractSpace.contractJson,
3997
+ __pack: pack.contractSpace
3998
+ })), (input) => input.__pack.migrations);
3999
+ const descriptorBySpace = /* @__PURE__ */ new Map();
4000
+ for (const pack of inputs.extensions) if (pack.contractSpace !== void 0) descriptorBySpace.set(pack.id, pack.contractSpace);
4001
+ const seeded = [];
4002
+ for (const space of planned) {
4003
+ const descriptor = descriptorBySpace.get(space.spaceId);
4004
+ if (descriptor === void 0) continue;
4005
+ const priorHash = (await readContractSpaceHeadRef(inputs.migrationsDir, space.spaceId))?.hash ?? null;
4006
+ await emitContractSpaceArtifacts(inputs.migrationsDir, space.spaceId, {
4007
+ contract: descriptor.contractJson,
4008
+ contractDts: buildPlaceholderContractDts(space.spaceId),
4009
+ headRef: {
4010
+ hash: descriptor.headRef.hash,
4011
+ invariants: descriptor.headRef.invariants
4012
+ }
4013
+ });
4014
+ const spaceDir = spaceMigrationDirectory(inputs.migrationsDir, space.spaceId);
4015
+ const newMigrationDirs = [];
4016
+ for (const pkg of space.migrationPackages) {
4017
+ const { written } = await materialiseExtensionMigrationPackageIfMissing(spaceDir, pkg);
4018
+ if (written) newMigrationDirs.push(pkg.dirName);
4019
+ }
4020
+ const action = priorHash !== descriptor.headRef.hash || newMigrationDirs.length > 0 ? "updated" : "unchanged";
4021
+ seeded.push({
4022
+ spaceId: space.spaceId,
4023
+ action,
4024
+ priorHash,
4025
+ newHash: descriptor.headRef.hash,
4026
+ newMigrationDirs
4027
+ });
4028
+ }
4029
+ return { seeded };
4030
+ }
4031
+ /**
4032
+ * Placeholder `.d.ts` content for an extension space's on-disk mirror.
4033
+ *
4034
+ * Rendering a fully-typed `.d.ts` for an extension contract requires
4035
+ * the SQL-family renderer with the codec / typemap registry threaded
4036
+ * through; until that integration ships, the on-disk `.d.ts` is a
4037
+ * stub `export {};` module that documents how consumers should
4038
+ * validate the sibling `contract.json`. The stub typechecks on its
4039
+ * own and does not need any TypeScript suppressions.
4040
+ */
4041
+ function buildPlaceholderContractDts(spaceId) {
4042
+ return [
4043
+ "/**",
4044
+ ` * Placeholder \`.d.ts\` for extension space "${spaceId}".`,
4045
+ " *",
4046
+ " * The framework re-emits this file on every `migration plan` run",
4047
+ " * alongside `contract.json` and `refs/head.json`. A typed `.d.ts`",
4048
+ " * rendering pass for extension contracts is tracked separately;",
4049
+ " * until that ships, consumers should import `contract.json`",
4050
+ " * and pass it through the target descriptor’s `contractSerializer`.",
4051
+ " */",
4052
+ "export {};",
4053
+ ""
4054
+ ].join("\n");
4055
+ }
4056
+ const FULL_HASH_PATTERN = /^([0-9a-f]{64}|empty)$/;
4057
+ function looksLikeFullHash(input) {
4058
+ return FULL_HASH_PATTERN.test(input);
4059
+ }
4060
+ function graphIsEmpty(space) {
4061
+ return space.packages.length === 0;
4062
+ }
4063
+ function getReachableRefs(refs, graph) {
4064
+ return Object.entries(refs).flatMap(([name, entry]) => entry && isGraphNode(entry.hash, graph) ? [{
4065
+ name,
4066
+ hash: entry.hash
4067
+ }] : []).sort((a, b) => a.name.localeCompare(b.name));
4068
+ }
4069
+ function assertFromIsGraphNode(fromHash, graph, refs, graphTipHash) {
4070
+ try {
4071
+ assertHashIsGraphNode(fromHash, graph);
4072
+ } catch (error) {
4073
+ if (MigrationToolsError.is(error) && error.code === "MIGRATION.HASH_NOT_IN_GRAPH") throw errorPlanForgotTheFlag(fromHash, getReachableRefs(refs, graph), graphTipHash, { cause: error });
4074
+ throw error;
4075
+ }
4076
+ }
4077
+ async function resolveContractRef(parsed, space, options) {
4078
+ const { hash, provenance } = parsed;
4079
+ const refName = provenance.kind === "ref" ? provenance.refName : void 0;
4080
+ try {
4081
+ const at = await space.contractAt(hash, refName !== void 0 ? { refName } : void 0);
4082
+ if (at.provenance === "ref") return ok({
4083
+ kind: "ref",
4084
+ hash: at.hash,
4085
+ contract: at.contract,
4086
+ contractJson: at.contractJson,
4087
+ contractDts: at.contractDts
4088
+ });
4089
+ return ok({
4090
+ kind: "graph-node",
4091
+ hash: at.hash,
4092
+ contract: at.contract,
4093
+ contractJson: at.contractJson,
4094
+ contractDts: at.contractDts
4095
+ });
4096
+ } catch (error) {
4097
+ return mapContractAtError(error, options?.artifactRole !== void 0 ? { artifactRole: options.artifactRole } : void 0);
4098
+ }
4099
+ }
4100
+ async function resolveFromPolicy(parsed, input, refs, explicitFromLabel) {
4101
+ const resolution = await resolveContractRef(parsed, input.space, {
4102
+ ...explicitFromLabel !== void 0 ? { explicitLabel: explicitFromLabel } : {},
4103
+ artifactRole: "from"
4104
+ });
4105
+ if (!resolution.ok) return resolution;
4106
+ if (resolution.value.kind === "graph-node") return ok({
4107
+ kind: "graph-node",
4108
+ fromHash: resolution.value.hash,
4109
+ fromContract: resolution.value.contract
4110
+ });
4111
+ const { hash, contract, contractJson, contractDts } = resolution.value;
4112
+ if (graphIsEmpty(input.space)) return ok({
4113
+ kind: "auto-baseline",
4114
+ fromHash: hash,
4115
+ fromContract: contract,
4116
+ contractDts,
4117
+ contractJson
4118
+ });
4119
+ const graph = input.space.graph();
4120
+ const graphTip = findLatestMigration(graph)?.to ?? null;
4121
+ try {
4122
+ assertFromIsGraphNode(hash, graph, refs, graphTip);
4123
+ } catch (error) {
4124
+ if (CliStructuredError$1.is(error)) return notOk(error);
4125
+ throw error;
4126
+ }
4127
+ return ok({
4128
+ kind: "ref",
4129
+ fromHash: hash,
4130
+ fromContract: contract,
4131
+ contractDts,
4132
+ contractJson
4133
+ });
4134
+ }
4135
+ async function resolveFromForPlan(input) {
4136
+ const { optionsFrom, space } = input;
4137
+ const graph = space.graph();
4138
+ const refs = space.refs;
4139
+ if (optionsFrom === void 0) {
4140
+ const dbRef = refs["db"];
4141
+ if (!dbRef) return ok({
4142
+ kind: "greenfield",
4143
+ fromHash: null,
4144
+ fromContract: null
4145
+ });
4146
+ return resolveFromPolicy({
4147
+ hash: dbRef.hash,
4148
+ provenance: {
4149
+ kind: "ref",
4150
+ refName: "db"
4151
+ }
4152
+ }, input, refs);
4153
+ }
4154
+ const refResult = parseContractRef(optionsFrom, {
4155
+ graph,
4156
+ refs
4157
+ });
4158
+ if (!refResult.ok) {
4159
+ if (looksLikeFullHash(optionsFrom)) {
4160
+ const empty = graphIsEmpty(space);
4161
+ const graphTip = findLatestMigration(graph)?.to ?? null;
4162
+ if (empty) return notOk(errorSnapshotMissing(optionsFrom, { viaRef: false }));
4163
+ return notOk(errorPlanForgotTheFlag(optionsFrom, getReachableRefs(refs, graph), graphTip));
4164
+ }
4165
+ return notOk(mapRefResolutionError(refResult.failure));
4166
+ }
4167
+ return resolveFromPolicy(refResult.value, input, refs, optionsFrom);
4168
+ }
4169
+ async function resolveToForPlan(optionsTo, input) {
4170
+ const { space } = input;
4171
+ const graph = space.graph();
4172
+ const refs = space.refs;
4173
+ const refResult = parseContractRef(optionsTo, {
4174
+ graph,
4175
+ refs
4176
+ });
4177
+ if (!refResult.ok) return notOk(mapRefResolutionError(refResult.failure));
4178
+ const resolution = await resolveContractRef(refResult.value, space, {
4179
+ explicitLabel: optionsTo,
4180
+ artifactRole: "to"
4181
+ });
4182
+ if (!resolution.ok) return resolution;
4183
+ const { hash, contract, contractJson, contractDts } = resolution.value;
4184
+ return ok({
4185
+ hash,
4186
+ contract,
4187
+ contractJson,
4188
+ contractDts
4189
+ });
4190
+ }
4191
+ /**
4192
+ * Policy core of `migration plan`: resolves from/to contracts, runs the planner legs, and writes the planned migration packages.
4193
+ */
4194
+ function isEnoent(error) {
4195
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
4196
+ }
4197
+ async function runPlannerLeg(planner, migrations, frameworkComponents, contract, fromContract, spaceId, ownership, snapshotsImportPath, resolveImportSpecifier) {
4198
+ const fromSchema = migrations.contractToSchema(fromContract, frameworkComponents);
4199
+ const plannerResult = planner.plan({
4200
+ contract,
4201
+ schema: fromSchema,
4202
+ policy: { allowedOperationClasses: [
4203
+ "additive",
4204
+ "widening",
4205
+ "destructive",
4206
+ "data"
4207
+ ] },
4208
+ fromContract,
4209
+ frameworkComponents,
4210
+ spaceId,
4211
+ ownership,
4212
+ snapshotsImportPath
4213
+ });
4214
+ if (plannerResult.kind === "failure") return notOk(errorMigrationPlanningFailed({ conflicts: castAs(plannerResult.conflicts) }));
4215
+ let plannedOps = [];
4216
+ let hasPlaceholders = false;
4217
+ try {
4218
+ plannedOps = await Promise.all(plannerResult.plan.operations);
4219
+ if (plannedOps.length === 0) return notOk(errorMigrationPlanningFailed({ conflicts: [{
4220
+ kind: "unsupportedChange",
4221
+ summary: "Contract changed but planner produced no operations. This indicates unsupported or ignored changes."
4222
+ }] }));
4223
+ } catch (e) {
4224
+ if (CliStructuredError$1.is(e) && e.code === "MIGRATION.UNFILLED_PLACEHOLDER") hasPlaceholders = true;
4225
+ else throw e;
4226
+ }
4227
+ return ok({
4228
+ plannedOps,
4229
+ migrationTsContent: plannerResult.plan.renderTypeScript(resolveImportSpecifier),
4230
+ hasPlaceholders
4231
+ });
4232
+ }
4233
+ async function writePlannedMigrationPackage(packageDir, fromHash, toHash, createdAt, leg) {
4234
+ const opsForWrite = leg.hasPlaceholders ? [] : leg.plannedOps;
4235
+ const metadataWithInvariants = {
4236
+ from: fromHash,
4237
+ to: toHash,
4238
+ providedInvariants: deriveProvidedInvariants(opsForWrite),
4239
+ createdAt: createdAt.toISOString()
4240
+ };
4241
+ await writeMigrationPackage(packageDir, {
4242
+ ...metadataWithInvariants,
4243
+ migrationHash: computeMigrationHash(metadataWithInvariants, opsForWrite)
4244
+ }, opsForWrite);
4245
+ await writeMigrationTs(packageDir, leg.migrationTsContent);
4246
+ }
4247
+ async function executeMigrationPlanCommand(options, startTime, callbacks) {
4248
+ try {
4249
+ return await executeMigrationPlanCommandInner(options, startTime, callbacks);
4250
+ } catch (error) {
4251
+ if (CliStructuredError$1.is(error)) return notOk(error);
4252
+ const message = error instanceof Error ? error.message : String(error);
4253
+ return notOk(errorUnexpected(message, { why: `Unexpected error during migration plan: ${message}` }));
4254
+ }
4255
+ }
4256
+ async function executeMigrationPlanCommandInner(options, startTime, callbacks) {
4257
+ const config = options.config;
4258
+ const cwd = options.cwd;
4259
+ const { configPath, migrationsDir, appMigrationsDir, appMigrationsRelative } = resolveMigrationPaths(options.configPath, config, cwd);
4260
+ const contractPathAbsolute = resolveContractPath(config);
4261
+ const contractPath = relative(cwd, contractPathAbsolute);
4262
+ callbacks?.onContextResolved?.({
4263
+ configPath,
4264
+ contractPath,
4265
+ appMigrationsRelative
4266
+ });
4267
+ let contractJsonContent;
4268
+ try {
4269
+ contractJsonContent = await readFile(contractPathAbsolute, "utf-8");
4270
+ } catch (error) {
4271
+ if (isEnoent(error)) return notOk(errorFileNotFound(contractPathAbsolute, {
4272
+ why: `Contract file not found at ${contractPathAbsolute}`,
4273
+ fix: `Run \`prisma-cli contract emit\` to generate ${contractPath}, or update \`config.contract.output\` in ${configPath}`
4274
+ }));
4275
+ return notOk(errorUnexpected(error instanceof Error ? error.message : String(error), { why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}` }));
4276
+ }
4277
+ const stack = createControlStack(config);
4278
+ const familyInstance = config.family.create(stack);
4279
+ const controlAdapter = config.adapter.create(stack);
4280
+ let toContract;
4281
+ try {
4282
+ toContract = familyInstance.deserializeContract(castAs(JSON.parse(contractJsonContent)));
4283
+ } catch (error) {
4284
+ return notOk(errorContractValidationFailed(`Contract at ${contractPathAbsolute} failed to deserialize: ${error instanceof Error ? error.message : String(error)}`, { where: { path: contractPathAbsolute } }));
4285
+ }
4286
+ const rawStorageHash = toContract.storage?.storageHash;
4287
+ if (typeof rawStorageHash !== "string") return notOk(errorContractValidationFailed("Contract is missing storageHash", { where: { path: contractPathAbsolute } }));
4288
+ let toStorageHash = rawStorageHash;
4289
+ let toArtifacts = null;
4290
+ let fromContract = null;
4291
+ let fromHash = null;
4292
+ let snapshotStartContract = null;
4293
+ let isAutoBaseline = false;
4294
+ const tolerantAggregateResult = await loadContractSpaceAggregateForCli({
4295
+ targetId: config.target.targetId,
4296
+ migrationsDir,
4297
+ appContract: toContract,
4298
+ extensions: config.extensions ?? [],
4299
+ deserializeContract: (json) => familyInstance.deserializeContract(json)
4300
+ });
4301
+ if (!tolerantAggregateResult.ok) return notOk(tolerantAggregateResult.failure);
4302
+ const resolutionSpace = tolerantAggregateResult.value.app;
4303
+ const resolutionResult = await resolveFromForPlan({
4304
+ optionsFrom: options.from,
4305
+ space: resolutionSpace
4306
+ });
4307
+ if (!resolutionResult.ok) return notOk(resolutionResult.failure);
4308
+ switch (resolutionResult.value.kind) {
4309
+ case "greenfield": break;
4310
+ case "graph-node":
4311
+ fromHash = resolutionResult.value.fromHash;
4312
+ fromContract = resolutionResult.value.fromContract;
4313
+ break;
4314
+ case "ref":
4315
+ fromHash = resolutionResult.value.fromHash;
4316
+ fromContract = resolutionResult.value.fromContract;
4317
+ snapshotStartContract = {
4318
+ fromHash: resolutionResult.value.fromHash,
4319
+ contractJson: resolutionResult.value.contractJson,
4320
+ contractDts: resolutionResult.value.contractDts
4321
+ };
4322
+ break;
4323
+ case "auto-baseline":
4324
+ fromHash = resolutionResult.value.fromHash;
4325
+ fromContract = resolutionResult.value.fromContract;
4326
+ snapshotStartContract = {
4327
+ fromHash: resolutionResult.value.fromHash,
4328
+ contractJson: resolutionResult.value.contractJson,
4329
+ contractDts: resolutionResult.value.contractDts
4330
+ };
4331
+ isAutoBaseline = true;
4332
+ break;
4333
+ }
4334
+ if (options.to !== void 0) {
4335
+ const toResolution = await resolveToForPlan(options.to, { space: resolutionSpace });
4336
+ if (!toResolution.ok) return notOk(toResolution.failure);
4337
+ toContract = toResolution.value.contract;
4338
+ toStorageHash = toResolution.value.hash;
4339
+ toArtifacts = {
4340
+ contractJson: toResolution.value.contractJson,
4341
+ contractDts: toResolution.value.contractDts
4342
+ };
4343
+ }
4344
+ const resolveImportSpecifier = createProjectSpecifierResolver(options.configPath);
4345
+ const seedResult = await runContractSpaceSeedPhase({
4346
+ migrationsDir,
4347
+ extensions: toExtensionInputs(config.extensions ?? [])
4348
+ });
4349
+ for (const record of seedResult.seeded) callbacks?.onSeeded?.(record);
4350
+ const emittedExtensionDirs = seedResult.seeded.flatMap((r) => r.newMigrationDirs.map((dirName) => ({
4351
+ spaceId: r.spaceId,
4352
+ dirName
4353
+ })));
4354
+ if (fromHash === toStorageHash && !isAutoBaseline) return ok({
4355
+ ok: true,
4356
+ noOp: true,
4357
+ from: fromHash,
4358
+ to: toStorageHash,
4359
+ operations: [],
4360
+ emittedExtensionDirs,
4361
+ summary: "No changes detected between contracts",
4362
+ timings: { total: Date.now() - startTime }
4363
+ });
4364
+ const migrations = getTargetMigrations(config.target);
4365
+ if (!migrations) return notOk(errorTargetMigrationNotSupported({ why: `Target "${config.target.id}" does not support migrations` }));
4366
+ const aggregateResult = await buildContractSpaceAggregate({
4367
+ targetId: config.target.targetId,
4368
+ migrationsDir,
4369
+ appContract: toContract,
4370
+ extensions: config.extensions ?? [],
4371
+ deserializeContract: (json) => familyInstance.deserializeContract(json)
4372
+ });
4373
+ if (!aggregateResult.ok) return notOk(aggregateResult.failure);
4374
+ const aggregate = aggregateResult.value;
4375
+ const frameworkComponents = assertFrameworkComponentsCompatible(config.family.familyId, config.target.targetId, [
4376
+ config.target,
4377
+ config.adapter,
4378
+ ...config.extensions ?? []
4379
+ ]);
4380
+ async function writeDestinationSnapshot(destHash) {
4381
+ if (toArtifacts !== null) {
4382
+ await writeContractSnapshot(migrationsDir, destHash, {
4383
+ contractJson: toArtifacts.contractJson,
4384
+ contractDts: toArtifacts.contractDts
4385
+ });
4386
+ return;
4387
+ }
4388
+ const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);
4389
+ const [contractJsonRaw, contractDts] = await Promise.all([readFile(destinationArtifacts.jsonPath, "utf-8"), readFile(destinationArtifacts.dtsPath, "utf-8")]);
4390
+ await writeContractSnapshot(migrationsDir, destHash, {
4391
+ contractJson: castAs(JSON.parse(contractJsonRaw)),
4392
+ contractDts
4393
+ });
4394
+ }
4395
+ try {
4396
+ const planner = migrations.createPlanner(controlAdapter);
4397
+ if (isAutoBaseline && fromHash !== null && fromContract !== null && snapshotStartContract !== null) {
4398
+ const baselineTimestamp = /* @__PURE__ */ new Date();
4399
+ const deltaTimestamp = new Date(baselineTimestamp.getTime() + 6e4);
4400
+ const baselineDirName = formatMigrationDirName(baselineTimestamp, "baseline");
4401
+ const deltaDirName = formatMigrationDirName(deltaTimestamp, options.name ?? "migration");
4402
+ const baselinePackageDir = join(appMigrationsDir, baselineDirName);
4403
+ const deltaPackageDir = join(appMigrationsDir, deltaDirName);
4404
+ const baselineLeg = await runPlannerLeg(planner, migrations, frameworkComponents, fromContract, null, aggregate.app.spaceId, aggregate, snapshotsImportPathFrom(baselinePackageDir, migrationsDir), resolveImportSpecifier);
4405
+ if (!baselineLeg.ok) return notOk(baselineLeg.failure);
4406
+ await writePlannedMigrationPackage(baselinePackageDir, null, fromHash, baselineTimestamp, baselineLeg.value);
4407
+ await writeContractSnapshot(migrationsDir, fromHash, {
4408
+ contractJson: snapshotStartContract.contractJson,
4409
+ contractDts: snapshotStartContract.contractDts
4410
+ });
4411
+ if (fromHash === toStorageHash) {
4412
+ const baselineOps = baselineLeg.value.hasPlaceholders ? [] : baselineLeg.value.plannedOps;
4413
+ if (baselineLeg.value.hasPlaceholders) {
4414
+ const baselineDir = relative(cwd, baselinePackageDir);
4415
+ return ok({
4416
+ ok: true,
4417
+ noOp: false,
4418
+ from: fromHash,
4419
+ to: toStorageHash,
4420
+ dir: baselineDir,
4421
+ baselineDir,
4422
+ operations: [],
4423
+ emittedExtensionDirs,
4424
+ pendingPlaceholders: true,
4425
+ summary: "Planned baseline with placeholder(s) — edit migration.ts then run `node migration.ts` to self-emit",
4426
+ timings: { total: Date.now() - startTime }
4427
+ });
4428
+ }
4429
+ const preview = hasOperationPreview(familyInstance) ? familyInstance.toOperationPreview(baselineOps) : void 0;
4430
+ return ok({
4431
+ ok: true,
4432
+ noOp: false,
4433
+ from: fromHash,
4434
+ to: toStorageHash,
4435
+ baselineDir: relative(cwd, baselinePackageDir),
4436
+ operations: baselineOps.map((op) => ({
4437
+ id: op.id,
4438
+ label: op.label,
4439
+ operationClass: op.operationClass
4440
+ })),
4441
+ emittedExtensionDirs,
4442
+ ...preview !== void 0 ? { preview } : {},
4443
+ summary: buildAutoBaselinePlanSummary(0, emittedExtensionDirs.length),
4444
+ timings: { total: Date.now() - startTime }
4445
+ });
4446
+ }
4447
+ const deltaLeg = await runPlannerLeg(planner, migrations, frameworkComponents, aggregate.app.contract(), fromContract, aggregate.app.spaceId, aggregate, snapshotsImportPathFrom(deltaPackageDir, migrationsDir), resolveImportSpecifier);
4448
+ if (!deltaLeg.ok) return notOk(deltaLeg.failure);
4449
+ await writePlannedMigrationPackage(deltaPackageDir, fromHash, toStorageHash, deltaTimestamp, deltaLeg.value);
4450
+ await writeDestinationSnapshot(toStorageHash);
4451
+ await writeContractSnapshot(migrationsDir, fromHash, {
4452
+ contractJson: snapshotStartContract.contractJson,
4453
+ contractDts: snapshotStartContract.contractDts
4454
+ });
4455
+ const deltaOps = deltaLeg.value.hasPlaceholders ? [] : deltaLeg.value.plannedOps;
4456
+ if (deltaLeg.value.hasPlaceholders) return ok({
4457
+ ok: true,
4458
+ noOp: false,
4459
+ from: fromHash,
4460
+ to: toStorageHash,
4461
+ dir: relative(cwd, deltaPackageDir),
4462
+ baselineDir: relative(cwd, baselinePackageDir),
4463
+ operations: [],
4464
+ emittedExtensionDirs,
4465
+ pendingPlaceholders: true,
4466
+ summary: "Planned baseline + migration with placeholder(s) — edit migration.ts then run `node migration.ts` to self-emit",
4467
+ timings: { total: Date.now() - startTime }
4468
+ });
4469
+ const preview = hasOperationPreview(familyInstance) ? familyInstance.toOperationPreview(deltaOps) : void 0;
4470
+ return ok({
4471
+ ok: true,
4472
+ noOp: false,
4473
+ from: fromHash,
4474
+ to: toStorageHash,
4475
+ dir: relative(cwd, deltaPackageDir),
4476
+ baselineDir: relative(cwd, baselinePackageDir),
4477
+ operations: deltaOps.map((op) => ({
4478
+ id: op.id,
4479
+ label: op.label,
4480
+ operationClass: op.operationClass
4481
+ })),
4482
+ emittedExtensionDirs,
4483
+ ...preview !== void 0 ? { preview } : {},
4484
+ summary: buildAutoBaselinePlanSummary(deltaOps.length, emittedExtensionDirs.length),
4485
+ timings: { total: Date.now() - startTime }
4486
+ });
4487
+ }
4488
+ const timestamp = /* @__PURE__ */ new Date();
4489
+ const packageDir = join(appMigrationsDir, formatMigrationDirName(timestamp, options.name ?? "migration"));
4490
+ const deltaLeg = await runPlannerLeg(planner, migrations, frameworkComponents, aggregate.app.contract(), fromContract, aggregate.app.spaceId, aggregate, snapshotsImportPathFrom(packageDir, migrationsDir), resolveImportSpecifier);
4491
+ if (!deltaLeg.ok) return notOk(deltaLeg.failure);
4492
+ await writePlannedMigrationPackage(packageDir, fromHash, toStorageHash, timestamp, deltaLeg.value);
4493
+ await writeDestinationSnapshot(toStorageHash);
4494
+ if (snapshotStartContract !== null) await writeContractSnapshot(migrationsDir, snapshotStartContract.fromHash, {
4495
+ contractJson: snapshotStartContract.contractJson,
4496
+ contractDts: snapshotStartContract.contractDts
4497
+ });
4498
+ if (deltaLeg.value.hasPlaceholders) return ok({
4499
+ ok: true,
4500
+ noOp: false,
4501
+ from: fromHash,
4502
+ to: toStorageHash,
4503
+ dir: relative(cwd, packageDir),
4504
+ operations: [],
4505
+ emittedExtensionDirs,
4506
+ pendingPlaceholders: true,
4507
+ summary: "Planned migration with placeholder(s) — edit migration.ts then run `node migration.ts` to self-emit",
4508
+ timings: { total: Date.now() - startTime }
4509
+ });
4510
+ const plannedOps = deltaLeg.value.plannedOps;
4511
+ const preview = hasOperationPreview(familyInstance) ? familyInstance.toOperationPreview(plannedOps) : void 0;
4512
+ return ok({
4513
+ ok: true,
4514
+ noOp: false,
4515
+ from: fromHash,
4516
+ to: toStorageHash,
4517
+ dir: relative(cwd, packageDir),
4518
+ operations: plannedOps.map((op) => ({
4519
+ id: op.id,
4520
+ label: op.label,
4521
+ operationClass: op.operationClass
4522
+ })),
4523
+ emittedExtensionDirs,
4524
+ ...preview !== void 0 ? { preview } : {},
4525
+ summary: buildPlanSummary(plannedOps.length, emittedExtensionDirs.length),
4526
+ timings: { total: Date.now() - startTime }
4527
+ });
4528
+ } catch (error) {
4529
+ if (CliStructuredError$1.is(error)) return notOk(error);
4530
+ if (MigrationToolsError.is(error)) return notOk(error);
4531
+ const message = error instanceof Error ? error.message : String(error);
4532
+ return notOk(errorUnexpected(message, { why: `Unexpected error during migration plan: ${message}` }));
4533
+ }
4534
+ }
4535
+ /**
4536
+ * Compose the success-line summary so the cross-space side effect
4537
+ * (extension-space migration packages materialised on disk during
4538
+ * this `plan` run) is visible in the top line — not just in the
4539
+ * step log above it.
4540
+ *
4541
+ * Example outputs:
4542
+ * - `Planned 3 operation(s)` (app-space-only project)
4543
+ * - `Planned 3 operation(s); materialised 1 extension-space migration` (one extension)
4544
+ * - `Planned 3 operation(s); materialised 2 extension-space migrations` (two extensions)
4545
+ *
4546
+ * Locks AC3 at the summary-line level: a reader of the success line
4547
+ * can tell that something happened beyond the app space.
4548
+ */
4549
+ function buildPlanSummary(plannedOpsCount, emittedExtensionDirsCount) {
4550
+ const base = `Planned ${plannedOpsCount} operation(s)`;
4551
+ if (emittedExtensionDirsCount === 0) return base;
4552
+ return `${base}; materialised ${emittedExtensionDirsCount} ${emittedExtensionDirsCount === 1 ? "extension-space migration" : "extension-space migrations"}`;
4553
+ }
4554
+ function buildAutoBaselinePlanSummary(deltaOpsCount, emittedExtensionDirsCount) {
4555
+ const base = `Planned baseline + ${deltaOpsCount} operation(s)`;
4556
+ if (emittedExtensionDirsCount === 0) return base;
4557
+ return `${base}; materialised ${emittedExtensionDirsCount} ${emittedExtensionDirsCount === 1 ? "extension-space migration" : "extension-space migrations"}`;
4558
+ }
4559
+ /** Origin hash for status path computation: the live/override marker, or the empty-contract sentinel. */
4560
+ function originHashForStatus(markerHash) {
4561
+ return markerHash ?? "empty";
4562
+ }
4563
+ function deriveStatusEdgeAnnotations(input) {
4564
+ const annotations = /* @__PURE__ */ new Map();
4565
+ if (input.showAppliedOverlay) {
4566
+ for (const edge of input.graph.migrationByHash.values()) if (input.appliedMigrationHashes.has(edge.migrationHash)) annotations.set(edge.migrationHash, { status: "applied" });
4567
+ }
4568
+ if (!input.graph.nodes.has(input.originHash)) return annotations;
4569
+ const pendingPath = findPath(input.graph, input.originHash, input.targetHash);
4570
+ if (!pendingPath) return annotations;
4571
+ for (const edge of pendingPath) {
4572
+ if (input.appliedMigrationHashes.has(edge.migrationHash)) continue;
4573
+ if (annotations.get(edge.migrationHash)?.status === "applied") continue;
4574
+ annotations.set(edge.migrationHash, { status: "pending" });
4575
+ }
4576
+ return annotations;
4577
+ }
4578
+ function appliedHashesFromLedger(ledgerEntries) {
4579
+ return new Set(ledgerEntries.map((entry) => entry.migrationHash));
4580
+ }
4581
+ function statusForMigrationHash(migrationHash, annotations) {
4582
+ return annotations.get(migrationHash)?.status ?? null;
4583
+ }
4584
+ /**
4585
+ * Reads the migrations refs index for commands, mapping MigrationToolsError into the CLI envelope.
4586
+ */
4587
+ /** Reads migrations/<app>/refs, passing MigrationToolsError through; other errors rethrow. */
4588
+ async function readMigrationRefs(refsDir) {
4589
+ try {
4590
+ return ok(await readRefs(refsDir));
4591
+ } catch (error) {
4592
+ if (MigrationToolsError.is(error)) return notOk(error);
4593
+ throw error;
4594
+ }
4595
+ }
4596
+ function mapError(error) {
4597
+ if (MigrationToolsError.is(error)) return error;
4598
+ return errorUnexpected(error instanceof Error ? error.message : String(error));
4599
+ }
4600
+ function cliErrorInvalidRefName(name) {
4601
+ return errorRuntime("MIGRATION.INVALID_REF_NAME", `Invalid ref name "${name}"`, {
4602
+ why: `Ref name "${name}" does not match the required format`,
4603
+ fix: "Ref names must be lowercase alphanumeric with hyphens or forward slashes, no `.` or `..` segments"
4604
+ });
4605
+ }
4606
+ async function executeRefSetCommand(name, contractInput, options) {
4607
+ if (!validateRefName(name)) return notOk(cliErrorInvalidRefName(name));
4608
+ const config = options.config;
4609
+ try {
4610
+ const { migrationsDir, refsDir } = resolveMigrationPaths(options.configPath, config, options.cwd);
4611
+ const loaded = await buildReadAggregate(config, { migrationsDir });
4612
+ if (!loaded.ok) return notOk(loaded.failure);
4613
+ const graph = loaded.value.aggregate.app.graph();
4614
+ const bundles = loaded.value.aggregate.app.packages;
4615
+ const refs = loaded.value.aggregate.app.refs;
4616
+ let resolvedHash;
4617
+ if (validateRefValue(contractInput)) resolvedHash = contractInput;
4618
+ else {
4619
+ const refResult = parseContractRef(contractInput, {
4620
+ graph,
4621
+ refs
4622
+ });
4623
+ if (!refResult.ok) return notOk(mapRefResolutionError(refResult.failure));
4624
+ resolvedHash = refResult.value.hash;
4625
+ }
4626
+ if (resolvedHash === "empty") return notOk(errorRefSetEmptySentinel(resolvedHash));
4627
+ if (!isGraphNode(resolvedHash, graph)) {
4628
+ const graphTip = findLatestMigration(graph)?.to ?? null;
4629
+ return notOk(errorRefSetHashNotInGraph(resolvedHash, [...graph.nodes].sort(), graphTip));
4630
+ }
4631
+ if (!bundles.find((bundle) => bundle.metadata.to === resolvedHash)) return notOk(errorRefSetBundleNotFound(resolvedHash));
4632
+ const contractJsonPath = join(contractSnapshotDir(migrationsDir, resolvedHash), "contract.json");
4633
+ try {
4634
+ await readContractSnapshotJson(migrationsDir, resolvedHash);
4635
+ } catch (readError) {
4636
+ if (MigrationToolsError.is(readError) && readError.code === "MIGRATION.CONTRACT_SNAPSHOT_MISSING") return notOk(errorFileNotFound(contractJsonPath, {
4637
+ why: `Migration bundle for hash ${resolvedHash} is missing its contract snapshot at ${contractJsonPath}`,
4638
+ fix: "Restore migrations/snapshots/ from version control, or re-run the command that produced this migration to regenerate its snapshot."
4639
+ }));
4640
+ throw readError;
4641
+ }
4642
+ await writeRef(refsDir, name, {
4643
+ hash: resolvedHash,
4644
+ invariants: []
4645
+ });
4646
+ return ok({
4647
+ ok: true,
4648
+ ref: name,
4649
+ hash: resolvedHash,
4650
+ invariants: []
4651
+ });
4652
+ } catch (error) {
4653
+ if (error instanceof CliStructuredError$1) return notOk(error);
4654
+ return notOk(mapError(error));
4655
+ }
4656
+ }
4657
+ async function executeRefDeleteCommand(name, options) {
4658
+ try {
4659
+ const { refsDir } = resolveMigrationPaths(options.configPath, options.config, options.cwd);
4660
+ await deleteRef(refsDir, name);
4661
+ return ok({
4662
+ ok: true,
4663
+ ref: name,
4664
+ deleted: true
4665
+ });
4666
+ } catch (error) {
4667
+ if (error instanceof CliStructuredError$1) return notOk(error);
4668
+ return notOk(mapError(error));
4669
+ }
4670
+ }
4671
+ async function executeRefListCommand(options) {
4672
+ try {
4673
+ const { refsDir } = resolveMigrationPaths(options.configPath, options.config, options.cwd);
4674
+ return ok({
4675
+ ok: true,
4676
+ refs: await readRefs(refsDir)
4677
+ });
4678
+ } catch (error) {
4679
+ if (error instanceof CliStructuredError$1) return notOk(error);
4680
+ return notOk(mapError(error));
4681
+ }
4682
+ }
4683
+ //#endregion
4684
+ export { loadContractRawSafely as $, errorMigrationPlanningFailed as A, statusForMigrationHash as At, executeDbUpdate as B, errorDestructiveChanges as C, resolveRefAdvancementFields as Ct, errorLegendHumanOnly as D, runMigrationCheck as Dt, errorHashMismatch as E, runContractSpaceSeedPhase as Et, errorTargetMigrationNotSupported as F, executeRefAdvancement as G, executeMigrateShowPlan as H, errorTargetMismatch as I, executeRefSetCommand as J, executeRefDeleteCommand as K, errorUnexpected as L, errorPathUnreachable as M, toDeclaredExtensionsFromRaw as Mt, errorRunnerFailed as N, errorMarkerMissing as O, runMigrationList as Ot, errorRuntime as P, loadAggregateIntegrityViolations as Q, executeContractEmit as R, errorDatabaseConnectionRequired as S, resolveMigrationRef as St, errorFileNotFound as T, runCommandAction as Tt, executeMigrationNewCommand as U, executeDbVerify as V, executeMigrationPlanCommand as W, hasMigrationPath as X, findPackageByDirPath as Y, listRefsByContractHash as Z, enumerateCheckSpaces as _, requireLiveDatabase as _t, buildContractSpaceAggregate as a, maskConnectionUrl as at, errorContractArgConflict as b, resolveContractRefToSnapshot as bt, buildRefAdvancementFields as c, readContractEnvelope as ct, closeQuietly as d, refuseContractSpaceIntegrity as dt, loadContractSpaceAggregateForCli as et, computeRefAdvancementName as f, refuseDeclaredExtensionTargetMismatch as ft, enrichContract as g, refuseUnknownInvariants as gt, disposeEmitQueue as h, refusePackageCorruptionOnAggregate as ht, appliedHashesFromLedger as i, mapIntegrityViolations as it, errorNoMigrations as j, targetSupportsMigrations as jt, errorMigrationPackageNotFound as k, sanitizeErrorMessage as kt, checkSingleTarget as l, readContractIR as lt, deriveStatusEdgeAnnotations as m, refuseMissingInvariantPath as mt, advanceRefSafely as n, mapCaughtMigrationError as nt, buildMigrationSpaceGraphEntries as o, migrationSpaceListEntriesFromAggregate as ot, createControlClient as p, refuseMarkerOutsideGraph as pt, executeRefListCommand as q, appContractStandInFromIdentity as r, mapContractAtError as rt, buildReadAggregate as s, originHashForStatus as st, CliStructuredError$1 as t, looksLikePath as tt, chooseAction as u, readMigrationRefs as ut, errorConfigValidation$1 as v, resolveAppTargetPath as vt, errorDriverRequired as w, resolveToForPlan as wt, errorContractValidationFailed as x, resolveFromForPlan as xt, errorConsentPlanMismatch as y, resolveContractRef$1 as yt, executeDbInit as z };
4685
+
4686
+ //# sourceMappingURL=ref-h6InR6dO-rGfl18Jv.mjs.map