@prisma/orm-toolchain 8.0.0-rc.1-dev.45 → 8.0.0-rc.10

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