@prisma/orm-toolchain 0.16.0-dev.36

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 (321) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +28 -0
  3. package/dist/aggregate-B4mhR4Sk.mjs +1012 -0
  4. package/dist/aggregate-B4mhR4Sk.mjs.map +1 -0
  5. package/dist/aggregate-BflrjWPO.d.mts +803 -0
  6. package/dist/aggregate-BflrjWPO.d.mts.map +1 -0
  7. package/dist/bin__prisma-next.mjs +800 -0
  8. package/dist/bin__prisma-next.mjs.map +1 -0
  9. package/dist/cli-telemetry.d.mts +367 -0
  10. package/dist/cli-telemetry.d.mts.map +1 -0
  11. package/dist/cli-telemetry.mjs +48 -0
  12. package/dist/cli-telemetry.mjs.map +1 -0
  13. package/dist/cli-telemetry__sender.d.mts +1 -0
  14. package/dist/cli-telemetry__sender.mjs +1 -0
  15. package/dist/cli.d.mts +31 -0
  16. package/dist/cli.d.mts.map +1 -0
  17. package/dist/cli.mjs +180 -0
  18. package/dist/cli.mjs.map +1 -0
  19. package/dist/cli__commands__contract-emit.d.mts +2 -0
  20. package/dist/cli__commands__contract-emit.mjs +2 -0
  21. package/dist/cli__commands__contract-infer.d.mts +7 -0
  22. package/dist/cli__commands__contract-infer.d.mts.map +1 -0
  23. package/dist/cli__commands__contract-infer.mjs +2 -0
  24. package/dist/cli__commands__db-init.d.mts +7 -0
  25. package/dist/cli__commands__db-init.d.mts.map +1 -0
  26. package/dist/cli__commands__db-init.mjs +2 -0
  27. package/dist/cli__commands__db-schema.d.mts +7 -0
  28. package/dist/cli__commands__db-schema.d.mts.map +1 -0
  29. package/dist/cli__commands__db-schema.mjs +2 -0
  30. package/dist/cli__commands__db-sign.d.mts +7 -0
  31. package/dist/cli__commands__db-sign.d.mts.map +1 -0
  32. package/dist/cli__commands__db-sign.mjs +2 -0
  33. package/dist/cli__commands__db-update.d.mts +7 -0
  34. package/dist/cli__commands__db-update.d.mts.map +1 -0
  35. package/dist/cli__commands__db-update.mjs +2 -0
  36. package/dist/cli__commands__db-verify.d.mts +7 -0
  37. package/dist/cli__commands__db-verify.d.mts.map +1 -0
  38. package/dist/cli__commands__db-verify.mjs +2 -0
  39. package/dist/cli__commands__migrate.d.mts +66 -0
  40. package/dist/cli__commands__migrate.d.mts.map +1 -0
  41. package/dist/cli__commands__migrate.mjs +2 -0
  42. package/dist/cli__commands__migration-check.d.mts +64 -0
  43. package/dist/cli__commands__migration-check.d.mts.map +1 -0
  44. package/dist/cli__commands__migration-check.mjs +3 -0
  45. package/dist/cli__commands__migration-graph.d.mts +36 -0
  46. package/dist/cli__commands__migration-graph.d.mts.map +1 -0
  47. package/dist/cli__commands__migration-graph.mjs +2 -0
  48. package/dist/cli__commands__migration-list.d.mts +79 -0
  49. package/dist/cli__commands__migration-list.d.mts.map +1 -0
  50. package/dist/cli__commands__migration-list.mjs +2 -0
  51. package/dist/cli__commands__migration-log.d.mts +20 -0
  52. package/dist/cli__commands__migration-log.d.mts.map +1 -0
  53. package/dist/cli__commands__migration-log.mjs +2 -0
  54. package/dist/cli__commands__migration-new.d.mts +7 -0
  55. package/dist/cli__commands__migration-new.d.mts.map +1 -0
  56. package/dist/cli__commands__migration-new.mjs +2 -0
  57. package/dist/cli__commands__migration-plan.d.mts +79 -0
  58. package/dist/cli__commands__migration-plan.d.mts.map +1 -0
  59. package/dist/cli__commands__migration-plan.mjs +2 -0
  60. package/dist/cli__commands__migration-show.d.mts +27 -0
  61. package/dist/cli__commands__migration-show.d.mts.map +1 -0
  62. package/dist/cli__commands__migration-show.mjs +2 -0
  63. package/dist/cli__commands__migration-status.d.mts +55 -0
  64. package/dist/cli__commands__migration-status.d.mts.map +1 -0
  65. package/dist/cli__commands__migration-status.mjs +3 -0
  66. package/dist/cli__commands__ref.d.mts +34 -0
  67. package/dist/cli__commands__ref.d.mts.map +1 -0
  68. package/dist/cli__commands__ref.mjs +2 -0
  69. package/dist/cli__commands__telemetry.d.mts +7 -0
  70. package/dist/cli__commands__telemetry.d.mts.map +1 -0
  71. package/dist/cli__commands__telemetry.mjs +2 -0
  72. package/dist/cli__config-types.d.mts +2 -0
  73. package/dist/cli__config-types.mjs +2 -0
  74. package/dist/cli__control-api.d.mts +141 -0
  75. package/dist/cli__control-api.d.mts.map +1 -0
  76. package/dist/cli__control-api.mjs +4 -0
  77. package/dist/cli__init-output.d.mts +37 -0
  78. package/dist/cli__init-output.d.mts.map +1 -0
  79. package/dist/cli__init-output.mjs +2 -0
  80. package/dist/cli__migration-cli.d.mts +79 -0
  81. package/dist/cli__migration-cli.d.mts.map +1 -0
  82. package/dist/cli__migration-cli.mjs +407 -0
  83. package/dist/cli__migration-cli.mjs.map +1 -0
  84. package/dist/client-75qrAPqf-DZAVUxFa.mjs +1606 -0
  85. package/dist/client-75qrAPqf-DZAVUxFa.mjs.map +1 -0
  86. package/dist/command-helpers-CAoXqAY9-DWUBcTXM.mjs +1671 -0
  87. package/dist/command-helpers-CAoXqAY9-DWUBcTXM.mjs.map +1 -0
  88. package/dist/config-loader.d.mts +18 -0
  89. package/dist/config-loader.d.mts.map +1 -0
  90. package/dist/config-loader.mjs +2 -0
  91. package/dist/constants-CM-w38DQ-Cz_EXbOc.mjs +10 -0
  92. package/dist/constants-CM-w38DQ-Cz_EXbOc.mjs.map +1 -0
  93. package/dist/constants-Dkcn8Qu4.d.mts +10 -0
  94. package/dist/constants-Dkcn8Qu4.d.mts.map +1 -0
  95. package/dist/contract-at-errors-BX7w-p19-SxA8eaV2.mjs +36 -0
  96. package/dist/contract-at-errors-BX7w-p19-SxA8eaV2.mjs.map +1 -0
  97. package/dist/contract-emit-0hSjbuxp-BBOKldTZ.mjs +148 -0
  98. package/dist/contract-emit-0hSjbuxp-BBOKldTZ.mjs.map +1 -0
  99. package/dist/contract-emit-B65_UiFn.d.mts +7 -0
  100. package/dist/contract-emit-B65_UiFn.d.mts.map +1 -0
  101. package/dist/contract-emit-B6odMoem-CK3diBPk.mjs +359 -0
  102. package/dist/contract-emit-B6odMoem-CK3diBPk.mjs.map +1 -0
  103. package/dist/contract-enrichment-DrMvvY2A-DxNGeF4d.mjs +56 -0
  104. package/dist/contract-enrichment-DrMvvY2A-DxNGeF4d.mjs.map +1 -0
  105. package/dist/contract-infer-BnkqpftN-DVM2eKJ3.mjs +73 -0
  106. package/dist/contract-infer-BnkqpftN-DVM2eKJ3.mjs.map +1 -0
  107. package/dist/contract-snapshot-store-CbqynbtE.d.mts +28 -0
  108. package/dist/contract-snapshot-store-CbqynbtE.d.mts.map +1 -0
  109. package/dist/contract-snapshot-store-sf5nP4mb-MLpG-ihh.mjs +118 -0
  110. package/dist/contract-snapshot-store-sf5nP4mb-MLpG-ihh.mjs.map +1 -0
  111. package/dist/contract-space-aggregate-loader-CFH-336b-BhAyRQEL.mjs +302 -0
  112. package/dist/contract-space-aggregate-loader-CFH-336b-BhAyRQEL.mjs.map +1 -0
  113. package/dist/db-init-Vbkb_TKZ.mjs +153 -0
  114. package/dist/db-init-Vbkb_TKZ.mjs.map +1 -0
  115. package/dist/db-schema-ClQCu3xY.mjs +47 -0
  116. package/dist/db-schema-ClQCu3xY.mjs.map +1 -0
  117. package/dist/db-sign-jjXDgQ1u.mjs +175 -0
  118. package/dist/db-sign-jjXDgQ1u.mjs.map +1 -0
  119. package/dist/db-update-BQlrqtDD.mjs +183 -0
  120. package/dist/db-update-BQlrqtDD.mjs.map +1 -0
  121. package/dist/db-verify-CKPsRFrj-3udprZQD.mjs +396 -0
  122. package/dist/db-verify-CKPsRFrj-3udprZQD.mjs.map +1 -0
  123. package/dist/domain-type-generation-DAn9y5d2-BgoT5uKI.mjs +334 -0
  124. package/dist/domain-type-generation-DAn9y5d2-BgoT5uKI.mjs.map +1 -0
  125. package/dist/domain-type-generation-PkAb39ob-Ct8AGqow.d.mts +63 -0
  126. package/dist/domain-type-generation-PkAb39ob-Ct8AGqow.d.mts.map +1 -0
  127. package/dist/emitter.d.mts +75 -0
  128. package/dist/emitter.d.mts.map +1 -0
  129. package/dist/emitter.mjs +3 -0
  130. package/dist/emitter__domain-type-generation.d.mts +2 -0
  131. package/dist/emitter__domain-type-generation.mjs +2 -0
  132. package/dist/emitter__type-expression-safety.d.mts +6 -0
  133. package/dist/emitter__type-expression-safety.d.mts.map +1 -0
  134. package/dist/emitter__type-expression-safety.mjs +2 -0
  135. package/dist/errors--qsXZUtL.d.mts +72 -0
  136. package/dist/errors--qsXZUtL.d.mts.map +1 -0
  137. package/dist/errors-IpZm1Skp-BH495dRd.mjs +378 -0
  138. package/dist/errors-IpZm1Skp-BH495dRd.mjs.map +1 -0
  139. package/dist/exports-BN0vFTxG.mjs +276 -0
  140. package/dist/exports-BN0vFTxG.mjs.map +1 -0
  141. package/dist/exports-B_xhKZmC.mjs +1436 -0
  142. package/dist/exports-B_xhKZmC.mjs.map +1 -0
  143. package/dist/exports-D3-NwrMA-CadaBGQ3.mjs +154 -0
  144. package/dist/exports-D3-NwrMA-CadaBGQ3.mjs.map +1 -0
  145. package/dist/exports-Dl2aGAk7.mjs +119 -0
  146. package/dist/exports-Dl2aGAk7.mjs.map +1 -0
  147. package/dist/format-ByDm2goZ-DKLyhNHo.mjs +101 -0
  148. package/dist/format-ByDm2goZ-DKLyhNHo.mjs.map +1 -0
  149. package/dist/framework-components-CjshQKHA-Dlp6Leo5.mjs +58 -0
  150. package/dist/framework-components-CjshQKHA-Dlp6Leo5.mjs.map +1 -0
  151. package/dist/global-flags-BpoOYtNZ-CHZD3fud.d.mts +35 -0
  152. package/dist/global-flags-BpoOYtNZ-CHZD3fud.d.mts.map +1 -0
  153. package/dist/graph-bDjJ4GL9-BhFevsJS.d.mts +28 -0
  154. package/dist/graph-bDjJ4GL9-BhFevsJS.d.mts.map +1 -0
  155. package/dist/graph-membership-DW6nnbyQ-Bp2KcVwR.mjs +14 -0
  156. package/dist/graph-membership-DW6nnbyQ-Bp2KcVwR.mjs.map +1 -0
  157. package/dist/hash-DH8hl5Rq-BpM2t3Nu.mjs +64 -0
  158. package/dist/hash-DH8hl5Rq-BpM2t3Nu.mjs.map +1 -0
  159. package/dist/hash-XZYYKJ-i.d.mts +53 -0
  160. package/dist/hash-XZYYKJ-i.d.mts.map +1 -0
  161. package/dist/import-roots-B6CtsWXb.d.mts +119 -0
  162. package/dist/import-roots-B6CtsWXb.d.mts.map +1 -0
  163. package/dist/import-roots-NDOIEDVj.mjs +217 -0
  164. package/dist/import-roots-NDOIEDVj.mjs.map +1 -0
  165. package/dist/init-DHctXAI9-DSlaaYXN.mjs +2186 -0
  166. package/dist/init-DHctXAI9-DSlaaYXN.mjs.map +1 -0
  167. package/dist/inspect-live-schema-CnwYTRZ0-BErPHMni.mjs +91 -0
  168. package/dist/inspect-live-schema-CnwYTRZ0-BErPHMni.mjs.map +1 -0
  169. package/dist/invariants-B5wLrgQS.d.mts +39 -0
  170. package/dist/invariants-B5wLrgQS.d.mts.map +1 -0
  171. package/dist/invariants-lXzy6txi-DAoP4nTE.mjs +57 -0
  172. package/dist/invariants-lXzy6txi-DAoP4nTE.mjs.map +1 -0
  173. package/dist/io-C8TdyBKt-DloEBi70.mjs +313 -0
  174. package/dist/io-C8TdyBKt-DloEBi70.mjs.map +1 -0
  175. package/dist/io-DYfeLJxR.d.mts +1 -0
  176. package/dist/io-sr4CO9_t-C3eV_kn-.d.mts +125 -0
  177. package/dist/io-sr4CO9_t-C3eV_kn-.d.mts.map +1 -0
  178. package/dist/ledger-origin-BGRN77Oy.d.mts +6 -0
  179. package/dist/ledger-origin-BGRN77Oy.d.mts.map +1 -0
  180. package/dist/ledger-origin-D7PN4Fon.mjs +9 -0
  181. package/dist/ledger-origin-D7PN4Fon.mjs.map +1 -0
  182. package/dist/metadata-2nbOAGsV-uYNeq2Cp.d.mts +2 -0
  183. package/dist/metadata-BtscDay2.d.mts +1 -0
  184. package/dist/migrate-Cb_iv264.mjs +500 -0
  185. package/dist/migrate-Cb_iv264.mjs.map +1 -0
  186. package/dist/migration-B2ljMJim.d.mts +155 -0
  187. package/dist/migration-B2ljMJim.d.mts.map +1 -0
  188. package/dist/migration-BuUmO7It.mjs +209 -0
  189. package/dist/migration-BuUmO7It.mjs.map +1 -0
  190. package/dist/migration-check-CcG-fB8r-CDs5C9vt.mjs +597 -0
  191. package/dist/migration-check-CcG-fB8r-CDs5C9vt.mjs.map +1 -0
  192. package/dist/migration-command-scaffold-BCuoeAki-B1pVGX8d.mjs +103 -0
  193. package/dist/migration-command-scaffold-BCuoeAki-B1pVGX8d.mjs.map +1 -0
  194. package/dist/migration-graph-CBf9ORe7-B4xAF8GN.mjs +500 -0
  195. package/dist/migration-graph-CBf9ORe7-B4xAF8GN.mjs.map +1 -0
  196. package/dist/migration-graph-DkoCC0Qy.mjs +185 -0
  197. package/dist/migration-graph-DkoCC0Qy.mjs.map +1 -0
  198. package/dist/migration-graph-Dqndu0i5.d.mts +9 -0
  199. package/dist/migration-graph-Dqndu0i5.d.mts.map +1 -0
  200. package/dist/migration-graph-command-render-BKrg6BPv-BP8Pfj4J.mjs +1945 -0
  201. package/dist/migration-graph-command-render-BKrg6BPv-BP8Pfj4J.mjs.map +1 -0
  202. package/dist/migration-graph-tarTxJ99-BrGBANeE.d.mts +124 -0
  203. package/dist/migration-graph-tarTxJ99-BrGBANeE.d.mts.map +1 -0
  204. package/dist/migration-list-BFBeo1_X-c0kqYokL.mjs +228 -0
  205. package/dist/migration-list-BFBeo1_X-c0kqYokL.mjs.map +1 -0
  206. package/dist/migration-log-CS5IWcNq-BQ-32Low.mjs +220 -0
  207. package/dist/migration-log-CS5IWcNq-BQ-32Low.mjs.map +1 -0
  208. package/dist/migration-new-BurCjyaQ.mjs +168 -0
  209. package/dist/migration-new-BurCjyaQ.mjs.map +1 -0
  210. package/dist/migration-path-target-D3Sj55PI-BICblCuk.mjs +38 -0
  211. package/dist/migration-path-target-D3Sj55PI-BICblCuk.mjs.map +1 -0
  212. package/dist/migration-plan-q_L84PN1-BWE0LTH6.mjs +759 -0
  213. package/dist/migration-plan-q_L84PN1-BWE0LTH6.mjs.map +1 -0
  214. package/dist/migration-show-Bx5I5aW9.mjs +171 -0
  215. package/dist/migration-show-Bx5I5aW9.mjs.map +1 -0
  216. package/dist/migration-status-CFlzlRmu-DewBN8Ys.mjs +444 -0
  217. package/dist/migration-status-CFlzlRmu-DewBN8Ys.mjs.map +1 -0
  218. package/dist/migration-tools.d.mts +23 -0
  219. package/dist/migration-tools.mjs +17 -0
  220. package/dist/migration-tools__aggregate.d.mts +2 -0
  221. package/dist/migration-tools__aggregate.mjs +2 -0
  222. package/dist/migration-tools__constants.d.mts +2 -0
  223. package/dist/migration-tools__constants.mjs +2 -0
  224. package/dist/migration-tools__contract-snapshot-store.d.mts +2 -0
  225. package/dist/migration-tools__contract-snapshot-store.mjs +2 -0
  226. package/dist/migration-tools__errors.d.mts +2 -0
  227. package/dist/migration-tools__errors.mjs +2 -0
  228. package/dist/migration-tools__graph.d.mts +2 -0
  229. package/dist/migration-tools__graph.mjs +1 -0
  230. package/dist/migration-tools__hash.d.mts +2 -0
  231. package/dist/migration-tools__hash.mjs +2 -0
  232. package/dist/migration-tools__invariants.d.mts +2 -0
  233. package/dist/migration-tools__invariants.mjs +2 -0
  234. package/dist/migration-tools__io.d.mts +3 -0
  235. package/dist/migration-tools__io.mjs +2 -0
  236. package/dist/migration-tools__ledger-origin.d.mts +2 -0
  237. package/dist/migration-tools__ledger-origin.mjs +2 -0
  238. package/dist/migration-tools__metadata.d.mts +3 -0
  239. package/dist/migration-tools__metadata.mjs +1 -0
  240. package/dist/migration-tools__migration-graph.d.mts +3 -0
  241. package/dist/migration-tools__migration-graph.mjs +3 -0
  242. package/dist/migration-tools__migration-ts.d.mts +2 -0
  243. package/dist/migration-tools__migration-ts.mjs +2 -0
  244. package/dist/migration-tools__migration.d.mts +2 -0
  245. package/dist/migration-tools__migration.mjs +2 -0
  246. package/dist/migration-tools__package.d.mts +3 -0
  247. package/dist/migration-tools__package.mjs +1 -0
  248. package/dist/migration-tools__ref-resolution.d.mts +2 -0
  249. package/dist/migration-tools__ref-resolution.mjs +2 -0
  250. package/dist/migration-tools__refs.d.mts +2 -0
  251. package/dist/migration-tools__refs.mjs +2 -0
  252. package/dist/migration-tools__spaces.d.mts +3 -0
  253. package/dist/migration-tools__spaces.mjs +3 -0
  254. package/dist/migration-ts-CNaAtK8C.d.mts +38 -0
  255. package/dist/migration-ts-CNaAtK8C.d.mts.map +1 -0
  256. package/dist/migration-ts-FyF55c4M.mjs +67 -0
  257. package/dist/migration-ts-FyF55c4M.mjs.map +1 -0
  258. package/dist/op-schema-BSVHzEQN-DIWt28D8.mjs +13 -0
  259. package/dist/op-schema-BSVHzEQN-DIWt28D8.mjs.map +1 -0
  260. package/dist/output-DWNYqZGc-D2sRgLAm.mjs +109 -0
  261. package/dist/output-DWNYqZGc-D2sRgLAm.mjs.map +1 -0
  262. package/dist/package-CfWiDNrr-ZR4HM_Iz.d.mts +21 -0
  263. package/dist/package-CfWiDNrr-ZR4HM_Iz.d.mts.map +1 -0
  264. package/dist/package-N9PPdFV_.d.mts +3 -0
  265. package/dist/progress-adapter-CjAeTxY_-BXYSd1eY.mjs +43 -0
  266. package/dist/progress-adapter-CjAeTxY_-BXYSd1eY.mjs.map +1 -0
  267. package/dist/project-import-root-CpwLCDLY-DkVwAnWj.mjs +99 -0
  268. package/dist/project-import-root-CpwLCDLY-DkVwAnWj.mjs.map +1 -0
  269. package/dist/publish-surface.d.mts +3 -0
  270. package/dist/publish-surface.mjs +3 -0
  271. package/dist/publish-surface__import-roots.d.mts +2 -0
  272. package/dist/publish-surface__import-roots.mjs +2 -0
  273. package/dist/publish-surface__shells.d.mts +2 -0
  274. package/dist/publish-surface__shells.mjs +2 -0
  275. package/dist/quick-reference-mongo.md +114 -0
  276. package/dist/quick-reference-postgres.md +93 -0
  277. package/dist/readme-mongo.md +35 -0
  278. package/dist/readme-postgres.md +34 -0
  279. package/dist/ref-B6cTthTi.mjs +171 -0
  280. package/dist/ref-B6cTthTi.mjs.map +1 -0
  281. package/dist/ref-advancement-CSPMoQs3-fEipZrdu.mjs +57 -0
  282. package/dist/ref-advancement-CSPMoQs3-fEipZrdu.mjs.map +1 -0
  283. package/dist/ref-resolution-BjJBWPjX.mjs +250 -0
  284. package/dist/ref-resolution-BjJBWPjX.mjs.map +1 -0
  285. package/dist/ref-resolution-COGOSL57.d.mts +127 -0
  286. package/dist/ref-resolution-COGOSL57.d.mts.map +1 -0
  287. package/dist/refs-BDbB9BRB-D6Za3Q49.mjs +241 -0
  288. package/dist/refs-BDbB9BRB-D6Za3Q49.mjs.map +1 -0
  289. package/dist/refs-BNCUu58Y-BIso5Jfg.d.mts +43 -0
  290. package/dist/refs-BNCUu58Y-BIso5Jfg.d.mts.map +1 -0
  291. package/dist/schemas-CeGMYFYX-h3OcU__O.d.mts +192 -0
  292. package/dist/schemas-CeGMYFYX-h3OcU__O.d.mts.map +1 -0
  293. package/dist/schemas-KhXMzNA_-CzZ7GFX_.mjs +112 -0
  294. package/dist/schemas-KhXMzNA_-CzZ7GFX_.mjs.map +1 -0
  295. package/dist/shells-BenrJHp2.mjs +602 -0
  296. package/dist/shells-BenrJHp2.mjs.map +1 -0
  297. package/dist/shells-COHV_Ozf.d.mts +135 -0
  298. package/dist/shells-COHV_Ozf.d.mts.map +1 -0
  299. package/dist/spaces-C27bA-xc.mjs +267 -0
  300. package/dist/spaces-C27bA-xc.mjs.map +1 -0
  301. package/dist/spaces-DxFj5y8l.d.mts +415 -0
  302. package/dist/spaces-DxFj5y8l.d.mts.map +1 -0
  303. package/dist/telemetry-CmbCa3Ld-CmE5zcIG.mjs +120 -0
  304. package/dist/telemetry-CmbCa3Ld-CmE5zcIG.mjs.map +1 -0
  305. package/dist/terminal-ui-BN4QOsm5-Bf3pim46.d.mts +133 -0
  306. package/dist/terminal-ui-BN4QOsm5-Bf3pim46.d.mts.map +1 -0
  307. package/dist/type-expression-safety-CgHTWypx.mjs +8 -0
  308. package/dist/type-expression-safety-CgHTWypx.mjs.map +1 -0
  309. package/dist/types-DZWEs4si-D8UlyJI0.d.mts +893 -0
  310. package/dist/types-DZWEs4si-D8UlyJI0.d.mts.map +1 -0
  311. package/dist/verify-DnVNjc5C-BGdsblBK.mjs +283 -0
  312. package/dist/verify-DnVNjc5C-BGdsblBK.mjs.map +1 -0
  313. package/dist/verify-contract-spaces-BZDcJsuA-De8v4Z4T.mjs +252 -0
  314. package/dist/verify-contract-spaces-BZDcJsuA-De8v4Z4T.mjs.map +1 -0
  315. package/dist/verify-contract-spaces-Cc1u_wJo-BilPJS2S.d.mts +136 -0
  316. package/dist/verify-contract-spaces-Cc1u_wJo-BilPJS2S.d.mts.map +1 -0
  317. package/dist/vite-plugin-contract-emit.d.mts +54 -0
  318. package/dist/vite-plugin-contract-emit.d.mts.map +1 -0
  319. package/dist/vite-plugin-contract-emit.mjs +303 -0
  320. package/dist/vite-plugin-contract-emit.mjs.map +1 -0
  321. package/package.json +127 -0
@@ -0,0 +1,2186 @@
1
+ import { I as formatErrorJson, L as formatErrorOutput, o as createTerminalUI, t as CliStructuredError$1 } from "./command-helpers-CAoXqAY9-DWUBcTXM.mjs";
2
+ import { i as renderInitOutro, n as buildNextSteps, r as formatInitJson, t as InitOutputSchema } from "./output-DWNYqZGc-D2sRgLAm.mjs";
3
+ import { a as schemaSample, c as targetLabel, i as scaffoldSpecifierResolverFor, l as targetPackageName, n as dbFile, o as starterSchema, r as defaultSchemaPath, s as targetEntrypoint, t as configFile, u as version } from "./bin__prisma-next.mjs";
4
+ import { createRequire } from "node:module";
5
+ import { CliStructuredError } from "@prisma/orm-framework/errors/control";
6
+ import { basename, dirname, extname, isAbsolute, join, normalize } from "pathe";
7
+ import { blindCast } from "@prisma/orm-framework/utils/casts";
8
+ import { InternalError } from "@prisma/orm-framework/utils/internal-error";
9
+ import * as clack from "@clack/prompts";
10
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
11
+ import { docsUrlFor } from "@prisma/orm-framework/utils/structured-error";
12
+ import { keepInternalSpecifiers } from "@prisma/orm-framework/components/emission";
13
+ import { execFile } from "node:child_process";
14
+ import { promisify } from "node:util";
15
+ import { detect, getUserAgent } from "package-manager-detector/detect";
16
+ import { applyEdits, modify, parse, printParseErrorCode } from "jsonc-parser";
17
+ //#region ../../../1-framework/3-tooling/cli/dist/init-DHctXAI9.mjs
18
+ const KNOWN = /* @__PURE__ */ new Set([
19
+ "pnpm",
20
+ "npm",
21
+ "yarn",
22
+ "bun",
23
+ "deno"
24
+ ]);
25
+ function isPackageManager(name) {
26
+ return KNOWN.has(name);
27
+ }
28
+ /**
29
+ * Resolves the package manager `init` should drive for `add` / `install`
30
+ * commands. Tries, in order:
31
+ *
32
+ * 1. **`detect()`** — walks up from `cwd` looking for a lockfile, the
33
+ * `packageManager` field, the `devEngines.packageManager` field, or
34
+ * install metadata. This is the right answer whenever the user is
35
+ * anywhere inside an existing project, including a deep workspace
36
+ * subdirectory.
37
+ *
38
+ * 2. **`getUserAgent()`** — parses `npm_config_user_agent`, the env var
39
+ * every PM sets when it spawns a script. This catches the
40
+ * bare-directory case where there's no project to walk up to but the
41
+ * user invoked us via `pnpm dlx prisma-next init` / `bunx
42
+ * prisma-next init` / `yarn dlx …`. Same signal used by every
43
+ * `create-*` tool in the ecosystem (`create-vite`, `create-next-app`,
44
+ * `create-astro`, `@antfu/ni`, …).
45
+ *
46
+ * 3. **`npm`** — final fallback. Always present alongside Node.
47
+ */
48
+ async function detectPackageManager(cwd) {
49
+ const detected = await detect({ cwd });
50
+ if (detected && isPackageManager(detected.name)) return detected.name;
51
+ const userAgent = getUserAgent();
52
+ if (userAgent !== null && isPackageManager(userAgent)) return userAgent;
53
+ return "npm";
54
+ }
55
+ function hasProjectManifest(cwd) {
56
+ return existsSync(join(cwd, "package.json")) || existsSync(join(cwd, "deno.json")) || existsSync(join(cwd, "deno.jsonc"));
57
+ }
58
+ function formatRunCommand(pm, bin, args) {
59
+ if (pm === "npm") return `npx ${bin} ${args}`;
60
+ if (pm === "deno") return `deno run npm:${bin} ${args}`;
61
+ return `${pm} ${bin} ${args}`;
62
+ }
63
+ function formatRunScriptCommand(pm, scriptName) {
64
+ switch (pm) {
65
+ case "deno": return `deno task ${scriptName}`;
66
+ case "bun": return `bun run ${scriptName}`;
67
+ case "pnpm": return `pnpm run ${scriptName}`;
68
+ case "yarn": return `yarn run ${scriptName}`;
69
+ default: return `npm run ${scriptName}`;
70
+ }
71
+ }
72
+ function formatAddArgs(pm, packages) {
73
+ if (pm === "deno") return ["add", ...packages.map((p) => `npm:${p}`)];
74
+ return ["add", ...packages];
75
+ }
76
+ function formatAddDevArgs(pm, packages) {
77
+ if (pm === "deno") return [
78
+ "add",
79
+ "--dev",
80
+ ...packages.map((p) => `npm:${p}`)
81
+ ];
82
+ return [
83
+ "add",
84
+ "-D",
85
+ ...packages
86
+ ];
87
+ }
88
+ /**
89
+ * Walks up from `baseDir` looking for `pnpm-workspace.yaml`, then scans
90
+ * its top-level `catalog:` block for entries that match any of `packages`.
91
+ *
92
+ * Implements FR7.3 / Spec Decision 8 (honour-and-warn): when `init` runs
93
+ * inside a pnpm workspace whose catalog overrides one of the packages it
94
+ * installs, surface a structured warning so the user knows the catalog
95
+ * version (not the published `latest`) is what ended up in their
96
+ * `node_modules`. pnpm itself does this silently; the warning closes the
97
+ * "looks fine, must be wrong version six months later" gap.
98
+ *
99
+ * Notes / scope:
100
+ *
101
+ * - We only inspect the unnamed top-level `catalog:` block. pnpm also
102
+ * supports `catalogs:` (plural — *named* catalogs referenced via
103
+ * `catalog:foo` specifiers); those don't apply to a vanilla
104
+ * `pnpm add prisma-next` invocation, so we skip them.
105
+ * - We don't validate YAML syntax exhaustively. The file format pnpm
106
+ * ships is line-oriented and well-known; a minimal regex is more
107
+ * robust than depending on a YAML parser for one warning.
108
+ * - We don't compare against the registry's `latest` — pnpm uses the
109
+ * catalog version regardless, so the warning fires whenever a match
110
+ * exists. The user-facing copy explains how to opt out.
111
+ */
112
+ function detectPnpmCatalogOverrides(baseDir, packages) {
113
+ const workspaceFile = findNearestPnpmWorkspaceFile(baseDir);
114
+ if (workspaceFile === null) return null;
115
+ const catalog = extractCatalogBlock(readFileSync(workspaceFile, "utf-8"));
116
+ if (catalog === null) return {
117
+ workspaceFile,
118
+ entries: []
119
+ };
120
+ const wanted = new Set(packages);
121
+ const entries = [];
122
+ for (const [name, version] of catalog) if (wanted.has(name)) entries.push({
123
+ name,
124
+ version
125
+ });
126
+ return {
127
+ workspaceFile,
128
+ entries
129
+ };
130
+ }
131
+ function findNearestPnpmWorkspaceFile(baseDir) {
132
+ let dir = baseDir;
133
+ let prev = "";
134
+ while (dir !== prev) {
135
+ const candidate = join(dir, "pnpm-workspace.yaml");
136
+ if (existsSync(candidate)) return candidate;
137
+ prev = dir;
138
+ dir = dirname(dir);
139
+ }
140
+ return null;
141
+ }
142
+ /**
143
+ * Returns the entries inside the top-level `catalog:` block as `[name, version]`
144
+ * pairs in document order, or `null` when no `catalog:` block exists.
145
+ *
146
+ * The parser is intentionally minimal: it reads line-by-line, locates the
147
+ * top-level `catalog:` line (no leading whitespace), then collects every
148
+ * subsequent indented line of the form `<key>: <value>` until the next
149
+ * top-level key (or end of file). Quotes around `<key>` and `<value>`
150
+ * are stripped; comments (`#…`) are ignored.
151
+ */
152
+ function extractCatalogBlock(contents) {
153
+ const lines = contents.split(/\r?\n/);
154
+ const startIdx = lines.findIndex((line) => /^catalog\s*:\s*$/.test(line));
155
+ if (startIdx === -1) return null;
156
+ const entries = [];
157
+ for (let i = startIdx + 1; i < lines.length; i++) {
158
+ const raw = lines[i] ?? "";
159
+ if (raw.trim() === "" || /^\s*#/.test(raw)) continue;
160
+ if (!/^\s/.test(raw)) break;
161
+ const match = raw.match(/^\s+(?:'([^']+)'|"([^"]+)"|([^:\s'"]+))\s*:\s*(.*?)\s*(?:#.*)?$/);
162
+ if (!match) continue;
163
+ const name = match[1] ?? match[2] ?? match[3];
164
+ if (name === void 0) continue;
165
+ const version = stripQuotes((match[4] ?? "").trim());
166
+ if (version === "") continue;
167
+ entries.push([name, version]);
168
+ }
169
+ return entries;
170
+ }
171
+ function stripQuotes(value) {
172
+ if (value.length >= 2) {
173
+ const first = value[0];
174
+ const last = value[value.length - 1];
175
+ if (first === "\"" && last === "\"" || first === "'" && last === "'") return value.slice(1, -1);
176
+ }
177
+ return value;
178
+ }
179
+ /**
180
+ * Re-init in non-interactive mode without `--force`. Distinct from the
181
+ * decline-the-prompt path (which is `errorInitUserAborted`) because here
182
+ * the user was never given the choice — `--force` is the contract.
183
+ */
184
+ function errorInitReinitNeedsForce() {
185
+ return new CliStructuredError$1("CLI.INIT_REINIT_NEEDS_FORCE", "Project is already initialized", {
186
+ why: "A `prisma-next.config.ts` already exists in this directory. Re-running `init` would overwrite the scaffolded files; in non-interactive mode `init` will not do that without `--force`.",
187
+ fix: "Pass `--force` to overwrite the existing scaffold, or run `init` interactively to confirm.",
188
+ docsUrl: docsUrlFor("CLI.INIT_REINIT_NEEDS_FORCE")
189
+ });
190
+ }
191
+ /**
192
+ * Non-interactive mode is missing one or more required inputs. Lists every
193
+ * missing flag in the error so an agent / CI script can react without
194
+ * needing to parse English.
195
+ *
196
+ * @param missing — kebab-case flag names without leading dashes
197
+ * @param why — additional context (e.g. "stdin is not a TTY") that helps
198
+ * the user understand why interactive fallback was skipped.
199
+ */
200
+ function errorInitMissingFlags(options) {
201
+ const flagList = options.missing.map((flag) => `--${flag}`).join(", ");
202
+ const fixList = options.missing.map((flag) => {
203
+ switch (flag) {
204
+ case "target": return "--target postgres|mongodb";
205
+ case "authoring": return "--authoring psl|typescript";
206
+ case "schema-path": return "--schema-path <path>";
207
+ default: return `--${flag} <value>`;
208
+ }
209
+ }).join(" ");
210
+ return new CliStructuredError$1("CLI.INIT_MISSING_FLAGS", "Missing required flags", {
211
+ why: `${options.why} Missing required flag(s): ${flagList}.`,
212
+ fix: `Re-run with the missing flag(s) supplied, e.g. \`prisma-next init --yes ${fixList}\`. Use \`prisma-next init --help\` to see every flag.`,
213
+ docsUrl: docsUrlFor("CLI.INIT_MISSING_FLAGS"),
214
+ meta: { missingFlags: options.missing }
215
+ });
216
+ }
217
+ /**
218
+ * A flag value was supplied but is not in the allowed set. Lists the
219
+ * allowed values in `meta` for machine-readable consumption.
220
+ */
221
+ function errorInitInvalidFlagValue(options) {
222
+ return new CliStructuredError$1("CLI.INIT_INVALID_FLAG_VALUE", `Invalid value for --${options.flag}`, {
223
+ why: `\`--${options.flag} ${options.value}\` is not one of: ${options.allowed.join(", ")}.`,
224
+ fix: `Use one of: ${options.allowed.map((v) => `--${options.flag} ${v}`).join(", ")}.`,
225
+ docsUrl: docsUrlFor("CLI.INIT_INVALID_FLAG_VALUE"),
226
+ meta: {
227
+ flag: options.flag,
228
+ value: options.value,
229
+ allowed: options.allowed
230
+ }
231
+ });
232
+ }
233
+ /**
234
+ * `--authoring` and `--schema-path` disagree on file extension (e.g. PSL
235
+ * authoring with a `.ts` path). Surfaces before any scaffold files are
236
+ * written so the project tree stays untouched.
237
+ */
238
+ function errorInitAuthoringSchemaPathMismatch(options) {
239
+ const expectedAuthoring = options.expectedExtension === ".ts" ? "typescript" : "psl";
240
+ return new CliStructuredError$1("CLI.INIT_AUTHORING_SCHEMA_PATH_MISMATCH", "Authoring and schema path do not match", {
241
+ why: `\`--authoring ${options.authoring}\` requires a schema file ending in ${options.expectedExtension}, but \`--schema-path ${options.schemaPath}\` ends in ${options.actualExtension}.`,
242
+ fix: `Use a matching pair, for example \`--authoring ${expectedAuthoring} --schema-path <path>${options.expectedExtension}\`, or change \`--authoring\` to match the path you supplied. You can also omit \`--schema-path\` to use the default for the chosen authoring.`,
243
+ docsUrl: docsUrlFor("CLI.INIT_AUTHORING_SCHEMA_PATH_MISMATCH"),
244
+ meta: {
245
+ authoring: options.authoring,
246
+ schemaPath: options.schemaPath,
247
+ actualExtension: options.actualExtension,
248
+ expectedExtension: options.expectedExtension
249
+ }
250
+ });
251
+ }
252
+ /**
253
+ * The user cancelled an interactive prompt (Ctrl-C, escape, declined a
254
+ * selection). Distinct from `errorInitReinitNeedsForce` because that path
255
+ * applies to non-interactive mode where the user was never given the
256
+ * choice; this one is the generic "user said no" path. Maps to exit code
257
+ * 3 (USER_ABORTED).
258
+ */
259
+ function errorInitUserAborted() {
260
+ return new CliStructuredError$1("CLI.INIT_USER_ABORTED", "Init cancelled", {
261
+ why: "The interactive prompt was cancelled before all required inputs were supplied. No files were modified.",
262
+ fix: "Re-run `prisma-next init` and complete the prompts, or pass the required inputs as flags (see `--help`) for a non-interactive run.",
263
+ severity: "info"
264
+ });
265
+ }
266
+ /**
267
+ * `--strict-probe` was supplied without `--probe-db`. Per FR8.3 / NFR9
268
+ * (offline-by-default), `--strict-probe` is a no-op without `--probe-db` —
269
+ * but rather than silently ignoring it we tell the user what they probably
270
+ * meant. Without this guard, the flag combination silently does nothing,
271
+ * which is exactly the kind of "looks like it worked" trap that a strict
272
+ * mode is supposed to prevent.
273
+ */
274
+ function errorInitStrictProbeWithoutProbe() {
275
+ return new CliStructuredError$1("CLI.INIT_STRICT_PROBE_WITHOUT_PROBE", "`--strict-probe` requires `--probe-db`", {
276
+ why: "`--strict-probe` only changes how a *failed* probe is reported; without `--probe-db` no probe is attempted in the first place. (`init` is offline-by-default — it never opens a connection to your database without explicit consent.)",
277
+ fix: "Add `--probe-db` to opt in to the probe, or drop `--strict-probe` if you do not need the version check.",
278
+ docsUrl: docsUrlFor("CLI.INIT_STRICT_PROBE_WITHOUT_PROBE")
279
+ });
280
+ }
281
+ /**
282
+ * Dependency installation failed and the pnpm → npm fallback (FR7.2)
283
+ * either did not apply (pm ≠ pnpm or stderr did not match a recognised
284
+ * leak) or also failed. Files scaffolded before the install step are
285
+ * already on disk; `meta.filesWritten` carries the list so a follow-up
286
+ * agent can resume manually. Maps to exit code `4 = INSTALL_FAILED`.
287
+ */
288
+ function errorInitInstallFailed(options) {
289
+ const trimmed = options.stderrLines.map((s) => s.trim()).filter(Boolean);
290
+ return new CliStructuredError$1("CLI.INIT_INSTALL_FAILED", "Failed to install dependencies", {
291
+ why: trimmed.length === 0 ? "The package manager exited with an error and no recoverable fallback applied." : `The package manager exited with: ${trimmed[0]}`,
292
+ fix: `Install manually:\n ${options.addCommand}\n ${options.addDevCommand}\nThen run \`${options.emitCommand}\` to emit the contract.`,
293
+ docsUrl: docsUrlFor("CLI.INIT_INSTALL_FAILED"),
294
+ meta: {
295
+ filesWritten: options.filesWritten,
296
+ stderr: trimmed
297
+ }
298
+ });
299
+ }
300
+ /**
301
+ * The user's project manifest (typically `package.json`) failed to parse
302
+ * as JSON. Init reads the manifest to merge `scripts` (FR3.5) and to
303
+ * skip `@types/node` when it is already declared (FR2.1); a malformed
304
+ * file would otherwise surface as an `INTERNAL_ERROR` with a raw
305
+ * `SyntaxError` stack, which violates the FR1.6 contract that every
306
+ * documented failure mode maps to a stable exit code.
307
+ *
308
+ * Maps to exit code `2 = PRECONDITION` — the user can fix the manifest
309
+ * and re-run.
310
+ */
311
+ function errorInitInvalidManifest(options) {
312
+ return new CliStructuredError$1("CLI.INIT_INVALID_MANIFEST", `Failed to parse ${options.path}`, {
313
+ why: `\`${options.path}\` is not valid JSON: ${options.cause}`,
314
+ fix: `Fix the JSON syntax in \`${options.path}\` (a missing comma or unbalanced brace is the most common cause), then re-run \`prisma-next init\`.`,
315
+ docsUrl: docsUrlFor("CLI.INIT_INVALID_MANIFEST"),
316
+ meta: {
317
+ path: options.path,
318
+ cause: options.cause
319
+ }
320
+ });
321
+ }
322
+ /**
323
+ * The user's existing `tsconfig.json` could not be parsed even with JSONC
324
+ * tolerance (comments + trailing commas) enabled. Init merges the
325
+ * minimum compiler options the scaffolded files need (FR2.2), so an
326
+ * unparseable tsconfig is a hard precondition failure: we cannot
327
+ * faithfully edit a file we cannot read.
328
+ *
329
+ * Init must surface this **before** writing any scaffold file so the
330
+ * user's working tree stays byte-identical (FR6.2 / NFR3) — see
331
+ * `runInit` for the precondition gate.
332
+ *
333
+ * Maps to exit code `2 = PRECONDITION` — the user can fix the file and
334
+ * re-run.
335
+ */
336
+ function errorInitInvalidTsconfig(options) {
337
+ return new CliStructuredError$1("CLI.INIT_INVALID_TSCONFIG", `Failed to parse ${options.path}`, {
338
+ why: `\`${options.path}\` is not valid JSON or JSONC: ${options.cause}`,
339
+ fix: `Fix the syntax in \`${options.path}\` and re-run \`prisma-next init\`. \`init\` accepts JSONC (comments and trailing commas) but cannot recover from unbalanced braces or missing commas.`,
340
+ docsUrl: docsUrlFor("CLI.INIT_INVALID_TSCONFIG"),
341
+ meta: {
342
+ path: options.path,
343
+ cause: options.cause
344
+ }
345
+ });
346
+ }
347
+ /**
348
+ * `--probe-db` was supplied along with `--strict-probe` and the probe
349
+ * could not complete (no `DATABASE_URL`, network/auth error, the target
350
+ * driver was not installed, …). Without `--strict-probe` the probe
351
+ * surfaces these as warnings; `--strict-probe` escalates them to
352
+ * fatal so a CI gate can rely on "init exit code 2 means something
353
+ * about the runtime environment is wrong" (FR8.3).
354
+ *
355
+ * Maps to exit code `2 = PRECONDITION`. The caller's project files
356
+ * are already on disk by this point — the probe runs after the write
357
+ * phase — but the install/emit steps may or may not have completed
358
+ * depending on `--no-install` and the exact failure mode; `meta`
359
+ * carries `filesWritten` so a follow-up agent can resume manually.
360
+ */
361
+ function errorInitProbeFailed(options) {
362
+ return new CliStructuredError$1("CLI.INIT_PROBE_FAILED", "Database probe failed", {
363
+ why: `\`--probe-db\` could not complete and \`--strict-probe\` was set: ${options.cause}`,
364
+ fix: "Confirm `DATABASE_URL` points at a reachable server, or drop `--strict-probe` to treat probe failures as warnings.",
365
+ docsUrl: docsUrlFor("CLI.INIT_PROBE_FAILED"),
366
+ meta: {
367
+ filesWritten: options.filesWritten,
368
+ cause: options.cause
369
+ }
370
+ });
371
+ }
372
+ /**
373
+ * `prisma-next contract emit` failed after a successful install. Surface
374
+ * the underlying error so the user can fix it and re-run; files and
375
+ * dependencies remain on disk untouched. Maps to exit code
376
+ * `5 = EMIT_FAILED`.
377
+ */
378
+ function errorInitEmitFailed(options) {
379
+ return new CliStructuredError$1("CLI.INIT_EMIT_FAILED", "Failed to emit contract", {
380
+ why: `\`prisma-next contract emit\` failed: ${options.cause}`,
381
+ fix: `Inspect your contract file, fix the underlying issue, then re-run \`${options.emitCommand}\`. Pass \`-v\` for the full error envelope.`,
382
+ docsUrl: docsUrlFor("CLI.INIT_EMIT_FAILED"),
383
+ meta: {
384
+ filesWritten: options.filesWritten,
385
+ cause: options.cause
386
+ }
387
+ });
388
+ }
389
+ /**
390
+ * The project-level skills install (`npx skills add
391
+ * prisma/prisma#v<version>`) failed after a successful dependency
392
+ * install + emit. The project's scaffold remains on disk; the user
393
+ * can either fix the underlying issue (network, registry, PATH) and
394
+ * run the install command manually, or re-run `init --no-skill` to
395
+ * proceed without the skill.
396
+ *
397
+ * Non-rolling-back, matching the existing install/emit failure
398
+ * semantics. Maps to exit code `6 = SKILL_INSTALL_FAILED`.
399
+ */
400
+ function errorInitSkillInstallFailed(options) {
401
+ return new CliStructuredError$1("CLI.INIT_SKILL_INSTALL_FAILED", "Failed to install Prisma Next skills", {
402
+ why: `\`${options.skillInstallCommand}\` exited with an error: ${options.cause}`,
403
+ fix: `Either:
404
+ - Re-run \`prisma-next init --no-skill${options.filesWritten.length > 0 ? " --force" : ""}\` to skip the skill install for this run, or\n - Fix the underlying issue (network, npm registry, \`npx skills\` on PATH) and install manually:\n ${options.skillInstallCommand}`,
405
+ docsUrl: docsUrlFor("CLI.INIT_SKILL_INSTALL_FAILED"),
406
+ meta: {
407
+ filesWritten: options.filesWritten,
408
+ skillInstallCommand: options.skillInstallCommand,
409
+ cause: options.cause
410
+ }
411
+ });
412
+ }
413
+ /**
414
+ * The `.gitattributes` entries written for a freshly initialised project
415
+ * (FR3.4). Mirrors the relevant subset of the repo-root
416
+ * [`.gitattributes`](../../../../../../../../.gitattributes):
417
+ *
418
+ * - **Today**: `contract.json`, `contract.d.ts` are emitted on every
419
+ * `prisma-next contract emit`. Marking them `linguist-generated`
420
+ * keeps GitHub's diff stats honest and collapses the file in code
421
+ * review by default.
422
+ * - **Forward-looking**: `ops.json`, `migration.json` are not yet emitted
423
+ * by `init` flows but will be produced by adjacent commands (lower /
424
+ * migration tooling). Adding them now matches Decision 5
425
+ * (forward-looking subset) so the file does not need to be amended
426
+ * every time a new artifact lands.
427
+ *
428
+ * `ARTIFACT_FILENAMES` entries are written relative to the schema
429
+ * directory so a user who runs `init --schema-path db/contract.prisma`
430
+ * gets `db/contract.json linguist-generated` — not the workspace-glob
431
+ * form `<glob>/contract.json` (which would over-match any unrelated
432
+ * `contract.json` the user has elsewhere) and not the absolute
433
+ * `DEFAULT_CONTRACT_SOURCE_DIR/contract.json` (which would silently
434
+ * break for a non-default schema path).
435
+ *
436
+ * The migration contract snapshot store (`migrations/snapshots/<hex>/`)
437
+ * is anchored to the migrations root instead, not the schema directory:
438
+ * migration package depth under `migrations/` varies (`app/<pkg>`,
439
+ * `<space>/<pkg>`, or a bare `<pkg>` in extension source repos), so no
440
+ * single schema-dir-relative pattern can reach every snapshot. See
441
+ * `STORE_GITATTRIBUTES_LINES` below.
442
+ */
443
+ const ARTIFACT_FILENAMES$1 = [
444
+ "contract.json",
445
+ "contract.d.ts",
446
+ "ops.json",
447
+ "migration.json"
448
+ ];
449
+ const ATTRIBUTE = "linguist-generated";
450
+ /**
451
+ * Full `.gitattributes` lines for the migration contract snapshot store,
452
+ * already anchored to the migrations root — unlike `ARTIFACT_FILENAMES`,
453
+ * these are not combined with the schema-relative prefix.
454
+ */
455
+ const STORE_GITATTRIBUTES_LINES = [`migrations/snapshots/**/contract.json ${ATTRIBUTE}`, `migrations/snapshots/**/contract.d.ts ${ATTRIBUTE}`];
456
+ /**
457
+ * Computes the `.gitattributes` lines this scaffold expects to own. Each
458
+ * line has the shape `<path> linguist-generated`. The `target` parameter
459
+ * is currently unused but accepted for symmetry with the other hygiene
460
+ * helpers and to leave room for target-specific entries (e.g. a future
461
+ * family-specific artifact) without a signature break.
462
+ */
463
+ function requiredGitattributesLines(schemaDir, _target) {
464
+ const dir = schemaDir === "." ? "" : schemaDir.replace(/\/+$/, "");
465
+ const prefix = dir === "" ? "" : `${dir}/`;
466
+ return [...ARTIFACT_FILENAMES$1.map((file) => `${prefix}${file} ${ATTRIBUTE}`), ...STORE_GITATTRIBUTES_LINES];
467
+ }
468
+ /**
469
+ * Idempotent `.gitattributes` merge (FR3.4 / FR9.3). Returns the new file
470
+ * content given the existing content (or `undefined` if the file does
471
+ * not yet exist).
472
+ *
473
+ * Equivalence is exact-line: a user-customised line like
474
+ * `prisma/*.json linguist-generated` is *not* recognised as covering
475
+ * `DEFAULT_CONTRACT_SOURCE_DIR/contract.json linguist-generated`. We accept that
476
+ * over-specification — preserving the user's broad pattern *and*
477
+ * appending the narrow one — because the narrow lines are what the
478
+ * acceptance criteria pin (FR3.4 AC).
479
+ *
480
+ * Returns `null` when no changes are required (file already contains
481
+ * every required entry).
482
+ */
483
+ function mergeGitattributes(existing, required) {
484
+ if (existing === void 0) return `${required.join("\n")}\n`;
485
+ const presentLines = new Set(existing.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")));
486
+ const missing = required.filter((line) => !presentLines.has(line));
487
+ if (missing.length === 0) return null;
488
+ return `${existing}${existing.length === 0 || existing.endsWith("\n") ? "" : "\n"}${missing.join("\n")}\n`;
489
+ }
490
+ /**
491
+ * The minimal `.gitignore` lines a Prisma Next scaffold needs (FR3.3).
492
+ * Order matches what Node tooling typically writes today.
493
+ *
494
+ * `node_modules/` first because it's the byte-largest miss; `dist/`
495
+ * because the scaffolded `tsconfig.json` writes there; `.env` last so
496
+ * the secret-bearing file is the one most-recently visible in any diff
497
+ * (a paranoid-correct ordering — humans skim from the top).
498
+ */
499
+ const REQUIRED_GITIGNORE_ENTRIES = [
500
+ "node_modules/",
501
+ "dist/",
502
+ ".env"
503
+ ];
504
+ /**
505
+ * Idempotent `.gitignore` merge (FR3.3 / FR9.3). Returns the new file
506
+ * content given the existing content (or `undefined` if the file does
507
+ * not yet exist). Adds only entries that are not already present and
508
+ * never duplicates a line. Existing comments and blank lines are
509
+ * preserved verbatim — `.gitignore` is parsed by `git` without a tree,
510
+ * so any line modification risks changing semantics.
511
+ *
512
+ * Pattern equivalence is line-literal: `node_modules/` and `node_modules`
513
+ * are treated as different entries. This is intentional — `git` treats
514
+ * them differently (the trailing slash restricts the match to
515
+ * directories), and the AC pins the trailing-slash form.
516
+ *
517
+ * Returns `null` when no changes are required (file already contains
518
+ * every required entry). The caller can use this to decide whether to
519
+ * include `.gitignore` in `filesWritten`.
520
+ */
521
+ function mergeGitignore(existing) {
522
+ if (existing === void 0) return `${REQUIRED_GITIGNORE_ENTRIES.join("\n")}\n`;
523
+ const present = new Set(existing.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")));
524
+ const missing = REQUIRED_GITIGNORE_ENTRIES.filter((entry) => !present.has(entry));
525
+ if (missing.length === 0) return null;
526
+ return `${existing}${existing.length === 0 || existing.endsWith("\n") ? "" : "\n"}${missing.join("\n")}\n`;
527
+ }
528
+ const REQUIRED_SCRIPTS = [{
529
+ name: "contract:emit",
530
+ command: "prisma-next contract emit"
531
+ }];
532
+ /**
533
+ * Idempotent `package.json#scripts` merge with collision detection
534
+ * (FR3.5 / FR9.3):
535
+ *
536
+ * - If a required script is **missing**, append it.
537
+ * - If a required script is **already present and identical**, leave
538
+ * the file alone (idempotency).
539
+ * - If a required script is **present but maps to a different command**,
540
+ * skip the write for that script and surface a structured warning.
541
+ * The user's override is sacred — `init` should never silently
542
+ * overwrite a custom build pipeline.
543
+ *
544
+ * Preserves the existing key order (so a user who has alphabetised
545
+ * their scripts does not see them reshuffled) and appends new entries
546
+ * at the end.
547
+ *
548
+ * The `package.json` is parsed and re-stringified through `JSON` —
549
+ * comments are not preserved (package.json does not support them per
550
+ * spec). Trailing newline matches the original input's trailing
551
+ * newline behaviour.
552
+ */
553
+ function mergePackageScripts(existing, required = REQUIRED_SCRIPTS) {
554
+ const parsed = blindCast(JSON.parse(existing));
555
+ const scripts = typeof parsed["scripts"] === "object" && parsed["scripts"] !== null ? { ...blindCast(parsed["scripts"]) } : {};
556
+ const warnings = [];
557
+ let mutated = false;
558
+ for (const { name, command } of required) {
559
+ const existingValue = scripts[name];
560
+ if (existingValue === void 0) {
561
+ scripts[name] = command;
562
+ mutated = true;
563
+ continue;
564
+ }
565
+ if (existingValue !== command) warnings.push(`package.json already has a "${name}" script with a different command — keeping yours.\n existing: ${existingValue}\n expected: ${command}\nIf you want the default, remove your "${name}" script and re-run \`init\`.`);
566
+ }
567
+ if (!mutated) return {
568
+ content: null,
569
+ warnings
570
+ };
571
+ parsed["scripts"] = scripts;
572
+ const trailingNewline = existing.endsWith("\n") ? "\n" : "";
573
+ return {
574
+ content: `${JSON.stringify(parsed, null, 2)}${trailingNewline}`,
575
+ warnings
576
+ };
577
+ }
578
+ /**
579
+ * Idempotently sets `"type": "module"` on a `package.json` so the
580
+ * scaffolded `prisma/db.ts` — which uses the ESM-only `with { type: 'json' }`
581
+ * import attribute — loads as ES module under Node's loader (TML-2494).
582
+ *
583
+ * Without this field Node either:
584
+ *
585
+ * - emits `MODULE_TYPELESS_PACKAGE_JSON` and reparses the file as ESM
586
+ * with a perf penalty (Node 22+ with `--experimental-strip-types`), or
587
+ * - hard-fails with `ERR_*` because the CJS loader cannot parse the
588
+ * import-attribute syntax (older Node, or any tool that doesn't
589
+ * reparse).
590
+ *
591
+ * Behaviour:
592
+ *
593
+ * - **Field missing** → set to `"module"`. New entry is inserted right
594
+ * after `"name"` (when present) so the diff lands in a conventional
595
+ * spot for human review; falls through to the natural append position
596
+ * otherwise.
597
+ * - **Field already `"module"`** → no-op (idempotent).
598
+ * - **Field set to anything else** (e.g. `"commonjs"`) → leave it alone
599
+ * and surface a structured warning. The user explicitly opted out of
600
+ * ESM and we don't silently overwrite that.
601
+ */
602
+ function ensureEsmModuleType(existing) {
603
+ const parsed = blindCast(JSON.parse(existing));
604
+ const currentType = parsed["type"];
605
+ if (currentType === "module") return {
606
+ content: null,
607
+ warning: null
608
+ };
609
+ if (typeof currentType === "string" && currentType !== "module") return {
610
+ content: null,
611
+ warning: `package.json declares "type": "${currentType}" — keeping yours, but the scaffolded prisma/db.ts uses an ESM-only import attribute (\`with { type: 'json' }\`) and will not load under that module type.\nIf you want the default, set "type": "module" in package.json.`
612
+ };
613
+ const next = {};
614
+ let inserted = false;
615
+ for (const [key, value] of Object.entries(parsed)) {
616
+ if (key === "type") continue;
617
+ next[key] = value;
618
+ if (!inserted && key === "name") {
619
+ next["type"] = "module";
620
+ inserted = true;
621
+ }
622
+ }
623
+ if (!inserted) next["type"] = "module";
624
+ const trailingNewline = existing.endsWith("\n") ? "\n" : "";
625
+ return {
626
+ content: `${JSON.stringify(next, null, 2)}${trailingNewline}`,
627
+ warning: null
628
+ };
629
+ }
630
+ const TARGET_ALIASES = /* @__PURE__ */ new Map([
631
+ ["postgres", "postgres"],
632
+ ["postgresql", "postgres"],
633
+ ["mongo", "mongo"],
634
+ ["mongodb", "mongo"]
635
+ ]);
636
+ const AUTHORING_VALUES = /* @__PURE__ */ new Map([
637
+ ["psl", "psl"],
638
+ ["typescript", "typescript"],
639
+ ["ts", "typescript"]
640
+ ]);
641
+ /**
642
+ * Resolves every required input for `runInit`. In interactive mode, missing
643
+ * inputs are prompted via clack; in non-interactive mode, missing required
644
+ * inputs throw a structured error listing exactly which flags are missing
645
+ * (FR1.4). Throws `CliStructuredError` on any unrecoverable input issue.
646
+ *
647
+ * `canPrompt` is decoupled from `flags.interactive` so the action handler
648
+ * (`./index.ts`) owns the merge of stdout-TTY (decoration) and stdin-TTY
649
+ * (prompts). `flags.interactive` continues to gate `TerminalUI` decoration
650
+ * — see [Style Guide § Interactivity](../../../../../../../docs/CLI%20Style%20Guide.md#interactivity).
651
+ */
652
+ async function resolveInitInputs(ctx) {
653
+ const { baseDir, options, flags, canPrompt } = ctx;
654
+ const force = Boolean(options.force);
655
+ const autoAcceptPrompts = Boolean(flags.yes);
656
+ if (options.strictProbe && !options.probeDb) throw errorInitStrictProbeWithoutProbe();
657
+ const reinit = await resolveReinit({
658
+ baseDir,
659
+ force,
660
+ canPrompt,
661
+ autoAcceptPrompts
662
+ });
663
+ const target = resolveTarget(options.target);
664
+ const authoring = resolveAuthoring(options.authoring);
665
+ const missing = [];
666
+ if (target === void 0) missing.push("target");
667
+ if (authoring === void 0) missing.push("authoring");
668
+ if (!canPrompt && missing.length > 0) throw errorInitMissingFlags({
669
+ missing,
670
+ why: process.stdin.isTTY ? "Non-interactive mode is active (`--no-interactive` or stdout is piped)." : "stdin is not a TTY, so `init` cannot prompt interactively."
671
+ });
672
+ const finalTarget = target ?? await promptTarget();
673
+ const finalAuthoring = authoring ?? await promptAuthoring();
674
+ const finalSchemaPath = options.schemaPath !== void 0 ? validateSchemaPath(options.schemaPath, finalAuthoring) : canPrompt ? await promptSchemaPath(finalAuthoring) : defaultSchemaPath(finalAuthoring);
675
+ const writeEnv = await resolveWriteEnv({
676
+ flag: options.writeEnv,
677
+ canPrompt,
678
+ autoAcceptPrompts
679
+ });
680
+ const removePreviousFacade = await resolveRemovePreviousFacade({
681
+ baseDir,
682
+ target: finalTarget,
683
+ reinit,
684
+ force,
685
+ canPrompt,
686
+ autoAcceptPrompts
687
+ });
688
+ const installProjectSkill = options.skill !== false;
689
+ return {
690
+ target: finalTarget,
691
+ authoring: finalAuthoring,
692
+ schemaPath: finalSchemaPath,
693
+ install: options.install !== false,
694
+ writeEnv,
695
+ probeDb: Boolean(options.probeDb),
696
+ strictProbe: Boolean(options.strictProbe),
697
+ reinit,
698
+ removePreviousFacade,
699
+ installProjectSkill
700
+ };
701
+ }
702
+ async function resolveWriteEnv(opts) {
703
+ if (opts.flag !== void 0) return Boolean(opts.flag);
704
+ if (!opts.canPrompt || opts.autoAcceptPrompts) return false;
705
+ const result = await clack.confirm({
706
+ message: "Also write a .env file from .env.example? (gitignored)",
707
+ initialValue: false,
708
+ output: process.stderr
709
+ });
710
+ if (clack.isCancel(result)) throw errorInitUserAborted();
711
+ return Boolean(result);
712
+ }
713
+ /**
714
+ * FR9.2 — detects whether re-init is switching targets (the previous
715
+ * facade differs from the chosen target's facade) and resolves the
716
+ * remove-or-keep question.
717
+ *
718
+ * The non-interactive contract is the same as the `--force` re-init
719
+ * gate above: a non-interactive run that reaches this helper always
720
+ * has `--force` (otherwise `resolveReinit` would have thrown 5002), so
721
+ * the removal proceeds without further prompting. Interactive runs see
722
+ * a `clack.confirm` with `initialValue: true` — the destructive default
723
+ * is correct because keeping both facades produces a project that
724
+ * imports from one but pays for both in the lockfile, which is a
725
+ * silent foot-gun the user almost never wants.
726
+ *
727
+ * Returns the previous facade package name when the user consented (or
728
+ * was force-ed) to remove it, otherwise `null`. Parse failures on
729
+ * `package.json` resolve to `null` here — `runInit`'s precondition
730
+ * gate surfaces a structured 5010 error for the same file shortly
731
+ * after, so we avoid double-reporting and keep this helper side-effect
732
+ * free under hostile inputs.
733
+ */
734
+ async function resolveRemovePreviousFacade(opts) {
735
+ if (!opts.reinit) return null;
736
+ const packageJsonPath = join(opts.baseDir, "package.json");
737
+ if (!existsSync(packageJsonPath)) return null;
738
+ const otherTarget = opts.target === "postgres" ? "mongo" : "postgres";
739
+ const otherFacadeNames = [targetPackageName(otherTarget, scaffoldSpecifierResolverFor(otherTarget)), targetPackageName(otherTarget)];
740
+ let parsed;
741
+ try {
742
+ parsed = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
743
+ } catch {
744
+ return null;
745
+ }
746
+ const deps = parsed["dependencies"];
747
+ if (deps === null || typeof deps !== "object" || Array.isArray(deps)) return null;
748
+ const otherFacade = otherFacadeNames.find((name) => Object.hasOwn(deps, name));
749
+ if (otherFacade === void 0) return null;
750
+ if (opts.force || opts.canPrompt && opts.autoAcceptPrompts) return otherFacade;
751
+ if (!opts.canPrompt) return otherFacade;
752
+ const result = await clack.confirm({
753
+ message: `Switching from ${targetLabel(otherTarget)} to ${targetLabel(opts.target)} — remove ${otherFacade} from package.json dependencies?`,
754
+ initialValue: true,
755
+ output: process.stderr
756
+ });
757
+ if (clack.isCancel(result)) throw errorInitUserAborted();
758
+ return result === true ? otherFacade : null;
759
+ }
760
+ async function resolveReinit(opts) {
761
+ if (!existsSync(join(opts.baseDir, "prisma-next.config.ts"))) return false;
762
+ if (opts.force) return true;
763
+ if (!opts.canPrompt) throw errorInitReinitNeedsForce();
764
+ if (opts.autoAcceptPrompts) return true;
765
+ const result = await clack.confirm({
766
+ message: "This project is already initialized. Re-initialize? This will overwrite all generated files.",
767
+ initialValue: false,
768
+ output: process.stderr
769
+ });
770
+ if (clack.isCancel(result) || result !== true) throw errorInitUserAborted();
771
+ return true;
772
+ }
773
+ function resolveTarget(value) {
774
+ if (value === void 0) return void 0;
775
+ const mapped = TARGET_ALIASES.get(value.toLowerCase());
776
+ if (mapped === void 0) throw errorInitInvalidFlagValue({
777
+ flag: "target",
778
+ value,
779
+ allowed: ["postgres", "mongodb"]
780
+ });
781
+ return mapped;
782
+ }
783
+ function resolveAuthoring(value) {
784
+ if (value === void 0) return void 0;
785
+ const mapped = AUTHORING_VALUES.get(value.toLowerCase());
786
+ if (mapped === void 0) throw errorInitInvalidFlagValue({
787
+ flag: "authoring",
788
+ value,
789
+ allowed: ["psl", "typescript"]
790
+ });
791
+ return mapped;
792
+ }
793
+ /**
794
+ * Validates `--schema-path` against the chosen `--authoring` style: PSL
795
+ * authoring requires a `.prisma` file and TypeScript authoring requires a
796
+ * `.ts` file. Mismatched combinations would silently scaffold PSL content
797
+ * into a `.ts` file (or vice versa); this validator surfaces the mistake
798
+ * as a precondition error naming both flags.
799
+ */
800
+ function validateSchemaPath(value, authoring) {
801
+ const trimmed = value.trim();
802
+ if (trimmed.length === 0) throw errorInitInvalidFlagValue({
803
+ flag: "schema-path",
804
+ value,
805
+ allowed: ["<non-empty file path with .prisma or .ts extension>"]
806
+ });
807
+ if (trimmed.endsWith("/") || trimmed.endsWith("\\")) throw errorInitInvalidFlagValue({
808
+ flag: "schema-path",
809
+ value,
810
+ allowed: ["<file path, not a directory>"]
811
+ });
812
+ const ext = extname(trimmed).toLowerCase();
813
+ const expected = authoring === "typescript" ? ".ts" : ".prisma";
814
+ if (ext !== expected) throw errorInitAuthoringSchemaPathMismatch({
815
+ authoring,
816
+ schemaPath: trimmed,
817
+ actualExtension: ext.length > 0 ? ext : "(none)",
818
+ expectedExtension: expected
819
+ });
820
+ return normalize(trimmed);
821
+ }
822
+ async function promptTarget() {
823
+ const result = await clack.select({
824
+ message: "What database are you using?",
825
+ options: [{
826
+ value: "postgres",
827
+ label: "PostgreSQL"
828
+ }, {
829
+ value: "mongo",
830
+ label: "MongoDB"
831
+ }],
832
+ output: process.stderr
833
+ });
834
+ if (clack.isCancel(result)) throw errorInitUserAborted();
835
+ return result;
836
+ }
837
+ async function promptAuthoring() {
838
+ const result = await clack.select({
839
+ message: "How do you want to write your schema?",
840
+ options: [{
841
+ value: "psl",
842
+ label: "Prisma Schema Language (.prisma)"
843
+ }, {
844
+ value: "typescript",
845
+ label: "TypeScript (.ts)"
846
+ }],
847
+ output: process.stderr
848
+ });
849
+ if (clack.isCancel(result)) throw errorInitUserAborted();
850
+ return result;
851
+ }
852
+ async function promptSchemaPath(authoring) {
853
+ const expectedExt = authoring === "typescript" ? ".ts" : ".prisma";
854
+ const result = await clack.text({
855
+ message: "Where should the schema file go?",
856
+ initialValue: defaultSchemaPath(authoring),
857
+ validate(value = "") {
858
+ const trimmed = value.trim();
859
+ if (trimmed.length === 0) return "Path cannot be empty";
860
+ if (trimmed.endsWith("/") || trimmed.endsWith("\\")) return "Path must be a file, not a directory";
861
+ const ext = extname(trimmed).toLowerCase();
862
+ if (ext === "") return "Path must include a file extension (e.g. .prisma or .ts)";
863
+ if (ext !== expectedExt) return `Schema path must end in ${expectedExt} for --authoring ${authoring} (got ${ext}).`;
864
+ },
865
+ output: process.stderr
866
+ });
867
+ if (clack.isCancel(result)) throw errorInitUserAborted();
868
+ return validateSchemaPath(result, authoring);
869
+ }
870
+ /**
871
+ * Connects (when configured) to the user's database and returns a
872
+ * structured outcome describing whether the server meets the declared
873
+ * minimum (FR8.1). Pure with respect to its inputs: no I/O happens
874
+ * unless `databaseUrl` is set.
875
+ *
876
+ * The outcome is shaped so that `--strict-probe` can branch on the
877
+ * `kind`/`meetsMinimum` pair without re-stringifying the message:
878
+ *
879
+ * - `ok` — informational; `init` continues.
880
+ * - `below-minimum` — warning; `init` continues regardless of
881
+ * `--strict-probe` (the spec scopes strict-probe to "probe
882
+ * *failures*", and a successful probe that finds an old server is
883
+ * not a failure).
884
+ * - `no-database-url` / `connection-failed` / `driver-missing` —
885
+ * warning by default, fatal under `--strict-probe`.
886
+ */
887
+ async function probeServerVersion(ctx, overrides = {}) {
888
+ const { databaseUrl, minVersion, target } = ctx;
889
+ if (databaseUrl === void 0 || databaseUrl.trim().length === 0) return {
890
+ kind: "no-database-url",
891
+ minVersion,
892
+ meetsMinimum: null,
893
+ message: "Skipped --probe-db: DATABASE_URL is not set in the current shell environment. (init does not read .env for the probe; export the variable or drop --probe-db.)"
894
+ };
895
+ let driverResult;
896
+ try {
897
+ if (target === "postgres") driverResult = overrides.probePostgres !== void 0 ? await overrides.probePostgres(databaseUrl) : await defaultProbePostgres(databaseUrl, ctx.baseDir, overrides);
898
+ else driverResult = overrides.probeMongo !== void 0 ? await overrides.probeMongo(databaseUrl) : await defaultProbeMongo(databaseUrl, ctx.baseDir, overrides);
899
+ } catch (err) {
900
+ if (err instanceof DriverMissingError) return {
901
+ kind: "driver-missing",
902
+ minVersion,
903
+ meetsMinimum: null,
904
+ cause: err.message,
905
+ message: `Skipped --probe-db: ${err.message}. (Run with install enabled, or install the driver yourself, then re-run \`prisma-next init --probe-db\`.)`
906
+ };
907
+ const cause = redactDatabaseUrlSecrets(causeMessage$1(err));
908
+ return {
909
+ kind: "connection-failed",
910
+ minVersion,
911
+ meetsMinimum: null,
912
+ cause,
913
+ message: `--probe-db could not connect: ${cause}.`
914
+ };
915
+ }
916
+ if (compareVersionPrefix(driverResult.serverVersion, minVersion) < 0) return {
917
+ kind: "below-minimum",
918
+ serverVersion: driverResult.serverVersion,
919
+ minVersion,
920
+ meetsMinimum: false,
921
+ message: `--probe-db: server reports version ${driverResult.serverVersion}, below the declared minimum (${minVersion}). Some queries may fail until the server is upgraded.`
922
+ };
923
+ return {
924
+ kind: "ok",
925
+ serverVersion: driverResult.serverVersion,
926
+ minVersion,
927
+ meetsMinimum: true,
928
+ message: `--probe-db: server reports version ${driverResult.serverVersion} (>= ${minVersion}).`
929
+ };
930
+ }
931
+ /**
932
+ * Compares two semver-prefix strings ("14", "14.2", "6.0", …) by
933
+ * numeric components left-to-right. Returns a negative number when `a`
934
+ * is older than `b`, zero when both versions agree on every numeric
935
+ * component (treating missing trailing components as `0`), and a
936
+ * positive number when `a` is newer.
937
+ *
938
+ * The loop runs over the **longer** of the two prefixes so that
939
+ * `'14'` compares less than `'14.1'` — without that, the shorter
940
+ * prefix would be silently accepted whenever the configured minimum
941
+ * has a non-zero minor or patch.
942
+ *
943
+ * Exported for unit tests.
944
+ */
945
+ function compareVersionPrefix(a, b) {
946
+ const aParts = parseNumericParts(a);
947
+ const bParts = parseNumericParts(b);
948
+ const len = Math.max(aParts.length, bParts.length);
949
+ for (let i = 0; i < len; i += 1) {
950
+ const aPart = aParts[i] ?? 0;
951
+ const bPart = bParts[i] ?? 0;
952
+ if (aPart !== bPart) return aPart - bPart;
953
+ }
954
+ return 0;
955
+ }
956
+ function parseNumericParts(version) {
957
+ const match = version.match(/^[^\d]*(\d+(?:\.\d+){0,3})/);
958
+ if (match === null) return [];
959
+ return (match[1] ?? "").split(".").map((part) => Number.parseInt(part, 10));
960
+ }
961
+ var DriverMissingError = class extends Error {};
962
+ function causeMessage$1(err) {
963
+ if (err instanceof Error) return err.message;
964
+ return String(err);
965
+ }
966
+ /**
967
+ * Strips `user:password@` userinfo from any URL-shaped substring before
968
+ * we surface the cause to the user. Mirrors `redactSecrets` in
969
+ * `init.ts` — the probe path has its own redactor because the inputs
970
+ * here include the raw connection string by construction (driver
971
+ * errors echo the URL back).
972
+ *
973
+ * Exported for unit tests.
974
+ */
975
+ function redactDatabaseUrlSecrets(text) {
976
+ if (!text) return text;
977
+ return text.replace(/([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)([^/@\s]+)@/g, "$1***@");
978
+ }
979
+ async function defaultProbePostgres(databaseUrl, baseDir, overrides) {
980
+ const client = new (requirePeer("pg", baseDir, overrides)).Client({ connectionString: databaseUrl });
981
+ await client.connect();
982
+ try {
983
+ const result = await client.query("SELECT version() as version");
984
+ return { serverVersion: parsePostgresVersion(String(result?.rows?.[0]?.version ?? "")) };
985
+ } finally {
986
+ await client.end().catch(() => void 0);
987
+ }
988
+ }
989
+ /**
990
+ * Extracts the numeric prefix from a Postgres `version()` row, e.g.
991
+ *
992
+ * `PostgreSQL 14.10 on x86_64-pc-linux-gnu, ...` → `"14.10"`
993
+ * `PostgreSQL 16beta1 on …` → `"16"` (we
994
+ * conservatively drop the suffix; minimum-version comparisons
995
+ * treat 16beta1 as 16, which is what every reasonable user
996
+ * expects).
997
+ *
998
+ * Exported for unit tests.
999
+ */
1000
+ function parsePostgresVersion(versionString) {
1001
+ const match = versionString.match(/PostgreSQL\s+(\d+(?:\.\d+)?)/i);
1002
+ if (match === null || match[1] === void 0) throw new CliStructuredError("CLI.INIT_PROBE_FAILED", `Could not parse PostgreSQL version from \`${versionString}\``);
1003
+ return match[1];
1004
+ }
1005
+ async function defaultProbeMongo(databaseUrl, baseDir, overrides) {
1006
+ const client = new (requirePeer("mongodb", baseDir, overrides)).MongoClient(databaseUrl);
1007
+ await client.connect();
1008
+ try {
1009
+ const buildInfo = await client.db().admin().command({ buildInfo: 1 });
1010
+ const versionString = String(buildInfo.version ?? "");
1011
+ if (versionString.length === 0) throw new CliStructuredError("CLI.INIT_PROBE_FAILED", "buildInfo did not include a `version` field");
1012
+ return { serverVersion: versionString };
1013
+ } finally {
1014
+ await client.close().catch(() => void 0);
1015
+ }
1016
+ }
1017
+ /**
1018
+ * Loads a peer driver (`pg` / `mongodb`) from the user's project
1019
+ * `node_modules`. We deliberately resolve from `baseDir` rather than
1020
+ * from the CLI bundle — the CLI does not depend on `pg` or `mongodb`
1021
+ * directly, but the user's `init`-generated `package.json` does (via
1022
+ * the target facade). Failure to resolve is folded into a typed
1023
+ * `DriverMissingError` so `probeServerVersion` can map it to a
1024
+ * `driver-missing` outcome rather than letting a `MODULE_NOT_FOUND`
1025
+ * leak as a generic connection failure.
1026
+ */
1027
+ function requirePeer(moduleId, baseDir, overrides) {
1028
+ try {
1029
+ if (overrides.requireFromBaseDir !== void 0) return overrides.requireFromBaseDir(baseDir, moduleId);
1030
+ return createRequire(join(baseDir, "package.json"))(moduleId);
1031
+ } catch (err) {
1032
+ throw new DriverMissingError(`\`${moduleId}\` is not installed in this project (resolved from ${baseDir}; cause: ${causeMessage$1(err)})`);
1033
+ }
1034
+ }
1035
+ /**
1036
+ * Filenames the contract pipeline emits next to the user's schema source
1037
+ * (`<schemaDir>/contract.json`, `<schemaDir>/contract.d.ts`, …). Mirrors
1038
+ * the schema-dir-relative `ARTIFACT_FILENAMES` in `hygiene-gitattributes.ts`
1039
+ * (not that file's migrations-root-anchored store lines, which are outside
1040
+ * this command's scope — reinit only ever touches `schemaDir`); kept as a
1041
+ * separate constant here because the cleanup contract is target-agnostic
1042
+ * and we deliberately do not want a stale artifact from a previous target
1043
+ * lingering after a re-init.
1044
+ *
1045
+ * If a future emit pipeline produces an additional schema-dir artifact,
1046
+ * add it here **and** to `ARTIFACT_FILENAMES` in `hygiene-gitattributes.ts`
1047
+ * — the two stay in lockstep so the file `init` advertises as
1048
+ * `linguist-generated` is exactly the file `init` is willing to delete on
1049
+ * re-init.
1050
+ */
1051
+ const ARTIFACT_FILENAMES = [
1052
+ "contract.json",
1053
+ "contract.d.ts",
1054
+ "ops.json",
1055
+ "migration.json"
1056
+ ];
1057
+ /**
1058
+ * Returns the schema-relative paths of stale contract artifacts the
1059
+ * previous `init` run (or a `contract emit`) left behind in `schemaDir`.
1060
+ * Paths are returned relative to `baseDir` so the caller can plumb them
1061
+ * into `filesWritten`-style logging without re-deriving the path.
1062
+ *
1063
+ * Pure function: no filesystem mutation. Used by `runInit`'s precondition
1064
+ * phase (FR6.2 / NFR3 atomicity) so a downstream parse failure leaves
1065
+ * the artifacts on disk and the project byte-identical to its pre-init
1066
+ * state.
1067
+ */
1068
+ function findStaleArtifacts(baseDir, schemaDir) {
1069
+ const result = [];
1070
+ for (const filename of ARTIFACT_FILENAMES) {
1071
+ const rel = join(schemaDir, filename);
1072
+ if (existsSync(join(baseDir, rel))) result.push(rel);
1073
+ }
1074
+ return result;
1075
+ }
1076
+ /**
1077
+ * Drops a single key from `package.json#dependencies`, returning the new
1078
+ * file content. Returns `null` when the dependency was already absent —
1079
+ * the caller can skip the write to keep re-init idempotent (FR9.3).
1080
+ *
1081
+ * Used by `runInit` for the FR9.2 target-switch path: when the user
1082
+ * re-inits a project from `--target postgres` to `--target mongodb` (or
1083
+ * vice versa), the previous facade is removed from `dependencies` so the
1084
+ * resulting project depends only on the chosen target's facade.
1085
+ *
1086
+ * Devs/peers/optional dep groups are intentionally *not* touched — the
1087
+ * facades are only ever in `dependencies` (FR4 / FR7), and broadening
1088
+ * the search would risk clobbering an unrelated dep with the same name
1089
+ * in `peerDependencies`.
1090
+ *
1091
+ * Throws `SyntaxError` if `existing` is not parseable as JSON; the
1092
+ * caller (`runInit`) already guards on that with a structured 5010
1093
+ * error before this helper is reached.
1094
+ */
1095
+ function removeDependency(existing, depName) {
1096
+ const parsed = JSON.parse(existing);
1097
+ const deps = parsed["dependencies"];
1098
+ if (deps === null || typeof deps !== "object" || Array.isArray(deps)) return null;
1099
+ if (!Object.hasOwn(deps, depName)) return null;
1100
+ const next = { ...deps };
1101
+ delete next[depName];
1102
+ parsed["dependencies"] = next;
1103
+ const trailingNewline = existing.endsWith("\n") ? "\n" : "";
1104
+ return `${JSON.stringify(parsed, null, 2)}${trailingNewline}`;
1105
+ }
1106
+ const exec = promisify(execFile);
1107
+ /**
1108
+ * Default base for the GitHub-URL form `<owner>/<repo>` consumed by
1109
+ * upstream `skills add`. Each `SkillSource` joins this base with its
1110
+ * own subpath (and optional `#ref` for version-pinned clusters).
1111
+ */
1112
+ const DEFAULT_SKILL_BASE = "prisma/prisma";
1113
+ const DEFAULT_SKILL_SOURCES = [
1114
+ {
1115
+ subpath: "skills",
1116
+ ref: "cli",
1117
+ description: "usage skills (version-locked to installed Prisma Next)"
1118
+ },
1119
+ {
1120
+ subpath: "skills/upgrade",
1121
+ ref: null,
1122
+ description: "upgrade skill (always tracks `main`)"
1123
+ },
1124
+ {
1125
+ subpath: "skills/extension-author",
1126
+ ref: null,
1127
+ description: "extension-author skill (always tracks `main`)"
1128
+ }
1129
+ ];
1130
+ /**
1131
+ * Test-only escape hatch for pinning the install base to a local
1132
+ * checkout. Production runs leave this unset, so installs always use
1133
+ * `DEFAULT_SKILL_BASE`.
1134
+ *
1135
+ * When set to an absolute filesystem path (typical for tests), the
1136
+ * `#ref` fragment is dropped — local-path mode in upstream's CLI does
1137
+ * not accept refs, and the local clone has whatever content the test
1138
+ * checked into it anyway. When set to anything else (e.g. a fork name
1139
+ * `myuser/prisma-next`), the ref policy is preserved.
1140
+ */
1141
+ function resolveAgentSkillBase() {
1142
+ const override = process.env["PRISMA_NEXT_SKILLS_BASE"]?.trim();
1143
+ return override && override.length > 0 ? override : DEFAULT_SKILL_BASE;
1144
+ }
1145
+ function isLocalPath(base) {
1146
+ return base.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(base);
1147
+ }
1148
+ /**
1149
+ * Agents passed to every project-level init install. Upstream `skills add`
1150
+ * is the source of truth for per-agent install behaviour; the CLI lists
1151
+ * every supported runtime on one invocation and delegates the rest.
1152
+ */
1153
+ const DEFAULT_SKILL_AGENTS = [
1154
+ "cursor",
1155
+ "claude-code",
1156
+ "codex",
1157
+ "windsurf"
1158
+ ];
1159
+ /**
1160
+ * Build the `<base>/<subpath>[#ref]` URL the `skills` CLI will
1161
+ * resolve. Exported for unit tests so the per-source format can be
1162
+ * asserted without going through the full install loop.
1163
+ */
1164
+ function formatSkillSourceUrl(source) {
1165
+ const base = resolveAgentSkillBase();
1166
+ const url = `${base}/${source.subpath}`;
1167
+ if (source.ref === null) return url;
1168
+ if (isLocalPath(base)) return url;
1169
+ if (source.ref === "cli") return `${url}#v${version}`;
1170
+ return url;
1171
+ }
1172
+ /**
1173
+ * The skill-install command for one source, formatted for the
1174
+ * project's detected package manager. `npx`/`pnpm dlx`/`bunx` are
1175
+ * interchangeable to the user; we pick the variant that matches the
1176
+ * rest of the install step so a single project consistently uses one
1177
+ * runner.
1178
+ *
1179
+ * `--agent` takes space-separated slugs on one flag; `--skill '*'` and `-y`
1180
+ * skip the multi-select prompts a non-interactive scaffold step cannot show.
1181
+ *
1182
+ * Exported for unit tests so the per-PM dispatch can be asserted
1183
+ * without a live subprocess.
1184
+ */
1185
+ function formatSkillInstallCommand(args) {
1186
+ const agents = args.agents ?? DEFAULT_SKILL_AGENTS;
1187
+ const cliArgs = [
1188
+ "skills@latest",
1189
+ "add",
1190
+ formatSkillSourceUrl(args.source),
1191
+ "--agent",
1192
+ ...agents,
1193
+ "--skill",
1194
+ "'*'",
1195
+ "-y"
1196
+ ];
1197
+ return formatPackageManagerCommand(args.pm, cliArgs);
1198
+ }
1199
+ /**
1200
+ * Ordered skill-install commands for one init run. Exported for unit tests.
1201
+ */
1202
+ function resolveProjectSkillInstallCommands(pm) {
1203
+ return DEFAULT_SKILL_SOURCES.map((source) => formatSkillInstallCommand({
1204
+ pm,
1205
+ source
1206
+ }));
1207
+ }
1208
+ function formatPackageManagerCommand(pm, args) {
1209
+ switch (pm) {
1210
+ case "pnpm": return `pnpm dlx ${args.join(" ")}`;
1211
+ case "yarn": return `yarn dlx ${args.join(" ")}`;
1212
+ case "bun": return `bunx ${args.join(" ")}`;
1213
+ case "deno": return `deno run -A npm:${args.join(" ")}`;
1214
+ case "npm": return `npx ${args.join(" ")}`;
1215
+ }
1216
+ }
1217
+ /**
1218
+ * Parse the project-pm-formatted command into an exec call. The
1219
+ * format-then-parse split keeps the user-facing command string the same
1220
+ * as the surface the structured error advertises, so a user who copies
1221
+ * the error's `fix` line gets the same invocation that init just
1222
+ * attempted. Single quotes are preserved in the display form so `*` is
1223
+ * safe to copy into a shell, then stripped before `execFile`.
1224
+ */
1225
+ function commandToExec(command) {
1226
+ const tokens = (command.match(/'[^']*'|\S+/g) ?? []).map((token) => token.startsWith("'") && token.endsWith("'") ? token.slice(1, -1) : token);
1227
+ return {
1228
+ file: tokens[0] ?? "npx",
1229
+ args: tokens.slice(1)
1230
+ };
1231
+ }
1232
+ /**
1233
+ * Runs the project-level skill install for every source in
1234
+ * `DEFAULT_SKILL_SOURCES`, in order. Returns
1235
+ * `{ ok: true, commands }` on success; throws a structured
1236
+ * `errorInitSkillInstallFailed` on the first failure (subsequent
1237
+ * sources are not attempted — the user opted into Prisma Next by
1238
+ * running `init` and a partial install would leave the project in an
1239
+ * ambiguous state). The throw is intentionally fatal — project-level
1240
+ * skill install is unconditional (modulo `--no-skill`).
1241
+ */
1242
+ async function runProjectLevelSkillInstall(ctx) {
1243
+ const commands = [];
1244
+ const installCommands = resolveProjectSkillInstallCommands(ctx.pm);
1245
+ for (const command of installCommands) {
1246
+ const { file, args } = commandToExec(command);
1247
+ try {
1248
+ await exec(file, args, { cwd: ctx.baseDir });
1249
+ commands.push(command);
1250
+ } catch (err) {
1251
+ throw errorInitSkillInstallFailed({
1252
+ skillInstallCommand: command,
1253
+ filesWritten: ctx.filesWritten,
1254
+ cause: redactSecrets$1(readChildStderr$1(err)) || (err instanceof Error ? err.message : String(err))
1255
+ });
1256
+ }
1257
+ }
1258
+ return {
1259
+ ok: true,
1260
+ commands
1261
+ };
1262
+ }
1263
+ function readChildStderr$1(err) {
1264
+ if (err instanceof Error && "stderr" in err) return String(err.stderr ?? "");
1265
+ return "";
1266
+ }
1267
+ /**
1268
+ * Strips credentials from a `scheme://user:pass@host/...` URL anywhere
1269
+ * in `stderr`. Package-manager stderr regularly contains credentialed
1270
+ * registry URLs (private npm registries, GitHub Packages tokens), and
1271
+ * those bubble into the structured `errorInitSkillInstallFailed`
1272
+ * envelope, which ends up in logs and CI output. Redact at the
1273
+ * boundary so we never re-emit a secret.
1274
+ *
1275
+ * Exported for unit tests.
1276
+ */
1277
+ function redactSecrets$1(stderr) {
1278
+ if (!stderr) return stderr;
1279
+ return stderr.replace(/([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)([^/@\s]+)@/g, "$1***@");
1280
+ }
1281
+ /**
1282
+ * Hand-rolled skill stub path that init must not leave behind. Removed
1283
+ * on every init run so a project's `.agents/skills/prisma-next/` does
1284
+ * not shadow the installed Prisma Next skill cluster.
1285
+ */
1286
+ const LEGACY_SKILL_FILE = ".agents/skills/prisma-next/SKILL.md";
1287
+ /**
1288
+ * The minimum supported server version for each target. The
1289
+ * authoritative source of truth is each target package's
1290
+ * `package.json#prismaNext.minServerVersion` field — this module
1291
+ * mirrors those values and a workspace-level test asserts the two
1292
+ * never drift (`templates/tsconfig-env.test.ts`).
1293
+ *
1294
+ * Bumping a value here in isolation is **not** safe: edit the
1295
+ * corresponding target package's `package.json` first, then mirror
1296
+ * here. The scaffold's `.env.example` and the "Requirements" section
1297
+ * of `prisma-next.md` both read from this constant, so a stale value
1298
+ * lies to every freshly initialised user.
1299
+ */
1300
+ const MIN_SERVER_VERSION = {
1301
+ postgres: "17",
1302
+ mongo: "8.0"
1303
+ };
1304
+ const TARGET_LABEL = {
1305
+ postgres: "PostgreSQL",
1306
+ mongo: "MongoDB"
1307
+ };
1308
+ /**
1309
+ * Renders the placeholder body shared by `.env` and `.env.example`:
1310
+ * the target-specific connection-string requirement comments and the
1311
+ * commented-shape `DATABASE_URL` line. The output is identical for both
1312
+ * authoring styles — the env file is orthogonal to PSL vs TS schema
1313
+ * authoring.
1314
+ */
1315
+ function envPlaceholderBody(target) {
1316
+ const label = TARGET_LABEL[target];
1317
+ const minVersion = MIN_SERVER_VERSION[target];
1318
+ const lines = [];
1319
+ lines.push(`# Connection string for ${label}.`);
1320
+ lines.push(`# Requires ${label} >= ${minVersion}.`);
1321
+ lines.push("");
1322
+ if (target === "postgres") lines.push("DATABASE_URL=\"postgresql://user:password@localhost:5432/mydb\"");
1323
+ else {
1324
+ lines.push("# Standalone local mongod / `docker run mongo:8` — no replica set required for first-run queries.");
1325
+ lines.push("# Transactions and change streams need a replica set; add ?replicaSet=... only after initiating one.");
1326
+ lines.push("");
1327
+ lines.push("DATABASE_URL=\"mongodb://user:password@localhost:27017/mydb\"");
1328
+ }
1329
+ lines.push("");
1330
+ return lines.join("\n");
1331
+ }
1332
+ /**
1333
+ * Renders the `.env.example` content for a given target:
1334
+ *
1335
+ * - Carries a "Copy this file to `.env`…" intro that only makes sense
1336
+ * for the example file (the real `.env` is the destination of that
1337
+ * copy and so does not get the same intro).
1338
+ * - Documents the `DATABASE_URL` placeholder in the target's native URL
1339
+ * shape (Postgres: standard `postgresql://`, Mongo: `mongodb://` plus
1340
+ * a `mydb` database segment so the lazy facade has a `dbName`).
1341
+ * - Carries a `# Requires <db> >= <version>` comment so a fresh user
1342
+ * knows the minimum supported server before they first try to connect.
1343
+ */
1344
+ function envExampleContent(target) {
1345
+ const lines = [];
1346
+ lines.push("# Copy this file to `.env` and replace the placeholder with your real connection string.");
1347
+ lines.push(envPlaceholderBody(target));
1348
+ return lines.join("\n");
1349
+ }
1350
+ /**
1351
+ * Renders the initial `.env` content for `--write-env` / interactive
1352
+ * opt-in. Same placeholder body as `.env.example`, **without** the
1353
+ * example file's "Copy this file to `.env`…" intro: the real `.env` is
1354
+ * the destination of that copy, so the line would lie. Writing this
1355
+ * file is gitignored (`.env` lands in `.gitignore` during init).
1356
+ */
1357
+ function envFileContent(target) {
1358
+ return envPlaceholderBody(target);
1359
+ }
1360
+ function renderTemplate(templateFile, variableNames, vars) {
1361
+ let result = readFileSync(join(import.meta.dirname, templateFile), "utf-8");
1362
+ for (const key of variableNames) {
1363
+ const value = vars[key];
1364
+ if (value === void 0) throw new InternalError(`Template variable '${key}' is not defined`);
1365
+ result = result.replaceAll(`{{${key}}}`, value);
1366
+ }
1367
+ return result;
1368
+ }
1369
+ const variables = [
1370
+ "schemaPath",
1371
+ "schemaDir",
1372
+ "dbImportPath",
1373
+ "pkgRun",
1374
+ "pkg",
1375
+ "configEntrypoint",
1376
+ "schemaSample",
1377
+ "requirements"
1378
+ ];
1379
+ function quickReferenceMd(target, authoring, schemaPath, pkgRun, resolveImportSpecifier = keepInternalSpecifiers) {
1380
+ const schemaDir = dirname(schemaPath);
1381
+ const pkg = targetPackageName(target, resolveImportSpecifier);
1382
+ const vars = {
1383
+ schemaPath,
1384
+ schemaDir,
1385
+ dbImportPath: `./${schemaDir}/db`,
1386
+ pkgRun,
1387
+ pkg,
1388
+ configEntrypoint: targetEntrypoint(target, "config", resolveImportSpecifier),
1389
+ schemaSample: schemaSample(target, authoring, resolveImportSpecifier),
1390
+ requirements: requirementsBlock(target)
1391
+ };
1392
+ return renderTemplate(`quick-reference-${target}.md`, variables, vars);
1393
+ }
1394
+ /**
1395
+ * Renders the FR8.2 "Requirements" block injected into `prisma-next.md`
1396
+ * (the user-facing quick reference). Sources the minimum server
1397
+ * version from `MIN_SERVER_VERSION` — itself mirrored from each
1398
+ * target package's `package.json#prismaNext.minServerVersion`
1399
+ * (FR8.1).
1400
+ *
1401
+ * The verification command is target-specific — Postgres scaffolds
1402
+ * shouldn't ship Mongo's `db.runCommand` (and vice versa) just because
1403
+ * we couldn't be bothered to branch.
1404
+ */
1405
+ function requirementsBlock(target) {
1406
+ return [
1407
+ "## Requirements",
1408
+ "",
1409
+ `- **${TARGET_LABEL[target]} ${MIN_SERVER_VERSION[target]} or newer.** Older servers are not supported. Run ${target === "postgres" ? "`SELECT version()`" : "`db.runCommand({ buildInfo: 1 })`"} against your server to verify.`,
1410
+ "- The CLI never connects to your database without explicit consent. Pass `--probe-db` to `prisma-next init` if you want `init` to verify the server version itself."
1411
+ ].join("\n");
1412
+ }
1413
+ const sharedVariables = [
1414
+ "projectName",
1415
+ "contractPath",
1416
+ "runDev",
1417
+ "runContractEmit"
1418
+ ];
1419
+ const postgresVariables = [
1420
+ ...sharedVariables,
1421
+ "runDbInit",
1422
+ "runDbUpdate",
1423
+ "runMigrationPlan",
1424
+ "runMigrate",
1425
+ "runDbSeed"
1426
+ ];
1427
+ const mongoVariables = [
1428
+ ...sharedVariables,
1429
+ "runDbUp",
1430
+ "runDbDown",
1431
+ "runDbReset",
1432
+ "runMigrationPlan",
1433
+ "runMigrate",
1434
+ "runDbSeed"
1435
+ ];
1436
+ function minimalProjectReadmeMd(target, schemaPath, projectName, pm) {
1437
+ const run = (script) => formatRunScriptCommand(pm, script);
1438
+ const shared = {
1439
+ projectName,
1440
+ contractPath: schemaPath,
1441
+ runDev: run("dev"),
1442
+ runContractEmit: run("contract:emit"),
1443
+ runMigrationPlan: run("migration:plan"),
1444
+ runMigrate: run("migrate"),
1445
+ runDbSeed: run("db:seed")
1446
+ };
1447
+ if (target === "mongo") {
1448
+ const vars = {
1449
+ ...shared,
1450
+ runDbUp: run("db:up"),
1451
+ runDbDown: run("db:down"),
1452
+ runDbReset: run("db:reset")
1453
+ };
1454
+ return renderTemplate("readme-mongo.md", mongoVariables, vars);
1455
+ }
1456
+ const vars = {
1457
+ ...shared,
1458
+ runDbInit: run("db:init"),
1459
+ runDbUpdate: run("db:update")
1460
+ };
1461
+ return renderTemplate("readme-postgres.md", postgresVariables, vars);
1462
+ }
1463
+ /**
1464
+ * Compiler options the scaffolded `prisma-next.config.ts` and `db.ts` need
1465
+ * to typecheck:
1466
+ *
1467
+ * - `module: 'preserve'` + `moduleResolution: 'bundler'` align with how
1468
+ * modern bundlers (and `tsdown`) consume our facade packages.
1469
+ * - `resolveJsonModule` lets `db.ts` import `contract.json with { type:
1470
+ * 'json' }` — the runtime path the facades document (FR4).
1471
+ *
1472
+ * `types: ['node']` is FR2.2 territory and lives in
1473
+ * `REQUIRED_COMPILER_OPTIONS_TYPES` because TS only honours an _array_
1474
+ * here, and a string-keyed merge would clobber any user-specified entries.
1475
+ * Merge handling preserves any extra `types` the user added.
1476
+ */
1477
+ const REQUIRED_COMPILER_OPTIONS = {
1478
+ module: "preserve",
1479
+ moduleResolution: "bundler",
1480
+ resolveJsonModule: true
1481
+ };
1482
+ /**
1483
+ * Types that must be present in `compilerOptions.types` for the scaffold
1484
+ * to typecheck. With `moduleResolution: 'bundler'`, TypeScript does not
1485
+ * implicitly include all `@types/*` packages — `process.env` only resolves
1486
+ * when `node` is in this array (or `types` is omitted, but then any other
1487
+ * type listed here would force the same behaviour). Listing `node`
1488
+ * explicitly is the documented escape hatch (FR2.2).
1489
+ */
1490
+ const REQUIRED_COMPILER_OPTIONS_TYPES = ["node"];
1491
+ function defaultTsConfig() {
1492
+ return JSON.stringify({
1493
+ compilerOptions: {
1494
+ target: "ES2022",
1495
+ ...REQUIRED_COMPILER_OPTIONS,
1496
+ types: [...REQUIRED_COMPILER_OPTIONS_TYPES],
1497
+ strict: true,
1498
+ skipLibCheck: true,
1499
+ esModuleInterop: true,
1500
+ outDir: "dist"
1501
+ },
1502
+ include: ["**/*.ts"]
1503
+ }, null, 2);
1504
+ }
1505
+ /**
1506
+ * Thrown by `mergeTsConfig` when the user's existing `tsconfig.json` is
1507
+ * not parseable as JSONC (TypeScript's actual configured dialect — see
1508
+ * FR6.1). Carries the raw parse errors so the caller can render an
1509
+ * actionable, location-aware message.
1510
+ *
1511
+ * `runInit` catches this exception during the precondition phase and
1512
+ * maps it to a `CliStructuredError(5011)` so the user's working tree
1513
+ * stays byte-identical when init bails (FR6.2 / NFR3).
1514
+ */
1515
+ var TsConfigParseError = class extends Error {
1516
+ errors;
1517
+ constructor(errors) {
1518
+ super(formatTsConfigParseErrors(errors));
1519
+ this.errors = errors;
1520
+ this.name = "TsConfigParseError";
1521
+ }
1522
+ };
1523
+ function formatTsConfigParseErrors(errors) {
1524
+ if (errors.length === 0) return "tsconfig.json is empty or not an object";
1525
+ return errors.map((e) => `${printParseErrorCode(e.error)} at offset ${e.offset}`).join("; ");
1526
+ }
1527
+ /**
1528
+ * Merges the required compiler options into an existing `tsconfig.json`.
1529
+ *
1530
+ * Parsing is delegated to `jsonc-parser` so JSONC inputs (comments,
1531
+ * trailing commas) — TypeScript's real configuration dialect — survive
1532
+ * unchanged: edits are applied as text patches via `modify` /
1533
+ * `applyEdits`, preserving the user's formatting, key ordering, and
1534
+ * comments wherever the touched paths permit (FR6.1, AC "Hostile
1535
+ * inputs").
1536
+ *
1537
+ * Throws `TsConfigParseError` when the input is not parseable as JSONC.
1538
+ * The caller must catch this and surface a structured error before
1539
+ * writing any scaffold files (FR6.2 atomicity).
1540
+ */
1541
+ function mergeTsConfig(existing) {
1542
+ const { config } = parseTsConfigText(existing);
1543
+ const formattingOptions = {
1544
+ tabSize: detectIndent(existing),
1545
+ insertSpaces: true,
1546
+ eol: existing.includes("\r\n") ? "\r\n" : "\n"
1547
+ };
1548
+ let result = existing;
1549
+ for (const [key, value] of Object.entries(REQUIRED_COMPILER_OPTIONS)) {
1550
+ const edits = modify(result, ["compilerOptions", key], value, { formattingOptions });
1551
+ result = applyEdits(result, edits);
1552
+ }
1553
+ const existingTypes = config["compilerOptions"]?.["types"];
1554
+ const mergedTypes = mergeTypesArray(existingTypes);
1555
+ const typesEdits = modify(result, ["compilerOptions", "types"], mergedTypes, { formattingOptions });
1556
+ result = applyEdits(result, typesEdits);
1557
+ return result;
1558
+ }
1559
+ /**
1560
+ * Parses an existing `tsconfig.json` (JSONC) and returns the structured
1561
+ * config alongside any non-fatal parse warnings. Throws
1562
+ * `TsConfigParseError` if the input cannot be parsed at all or does
1563
+ * not resolve to a JSON object — both cases mean we cannot safely
1564
+ * apply edits.
1565
+ *
1566
+ * Exposed independently so callers (notably `runInit`'s precondition
1567
+ * gate) can validate the file *before* any scaffold file is written.
1568
+ */
1569
+ function parseTsConfigText(text) {
1570
+ const errors = [];
1571
+ const value = parse(text, errors, {
1572
+ allowTrailingComma: true,
1573
+ disallowComments: false,
1574
+ allowEmptyContent: false
1575
+ });
1576
+ if (value === void 0 || value === null || typeof value !== "object" || Array.isArray(value)) throw new TsConfigParseError(errors);
1577
+ if (errors.length > 0) throw new TsConfigParseError(errors);
1578
+ return { config: value };
1579
+ }
1580
+ function detectIndent(text) {
1581
+ const match = text.match(/^([ \t]+)\S/m);
1582
+ if (match === null) return 2;
1583
+ const indent = match[1] ?? "";
1584
+ if (indent.startsWith(" ")) return 1;
1585
+ return indent.length || 2;
1586
+ }
1587
+ /**
1588
+ * Merges `REQUIRED_COMPILER_OPTIONS_TYPES` into the user's existing
1589
+ * `compilerOptions.types` array. Preserves order and dedupes. If the
1590
+ * user has no `types` array (or has set it to a non-array), we replace
1591
+ * with the required minimum — overwriting a non-array `types` is the
1592
+ * correct fix because anything other than a string array is invalid TS
1593
+ * config.
1594
+ */
1595
+ function mergeTypesArray(existing) {
1596
+ const result = [];
1597
+ if (Array.isArray(existing)) {
1598
+ for (const item of existing) if (typeof item === "string" && !result.includes(item)) result.push(item);
1599
+ }
1600
+ for (const required of REQUIRED_COMPILER_OPTIONS_TYPES) if (!result.includes(required)) result.push(required);
1601
+ return result;
1602
+ }
1603
+ /**
1604
+ * Runs the `init` command end-to-end and returns the exit code. Catches
1605
+ * structured CLI errors raised at every phase (input resolution, install,
1606
+ * emit) and renders them via the same UI surface as success output
1607
+ * (`--json` to stdout, human to stderr). Exit codes follow the documented
1608
+ * stable set in `./exit-codes.ts` and the
1609
+ * [Style Guide § Exit Codes](../../../../../../../docs/CLI%20Style%20Guide.md#exit-codes).
1610
+ *
1611
+ * Layered for testability: the action handler in `./index.ts` is
1612
+ * responsible for parsing flags and constructing `runOptions`; this
1613
+ * function does no flag parsing of its own.
1614
+ */
1615
+ async function runInit(baseDir, runOptions) {
1616
+ const { options, flags, canPrompt, probeOverrides } = runOptions;
1617
+ const ui = createTerminalUI(flags);
1618
+ const warnings = [];
1619
+ const filesWritten = [];
1620
+ const filesDeleted = [];
1621
+ if (!flags.json && !flags.quiet) clack.intro("prisma-next init", { output: process.stderr });
1622
+ let inputs;
1623
+ try {
1624
+ inputs = await resolveInitInputs({
1625
+ baseDir,
1626
+ options,
1627
+ flags,
1628
+ canPrompt
1629
+ });
1630
+ } catch (error) {
1631
+ if (CliStructuredError$1.is(error)) return emitError(ui, flags, error);
1632
+ throw error;
1633
+ }
1634
+ const pm = await detectPackageManager(baseDir);
1635
+ const pkgRun = formatRunCommand(pm, "prisma-next", "").trimEnd();
1636
+ const schemaDir = dirname(inputs.schemaPath);
1637
+ const configContractPath = isAbsolute(inputs.schemaPath) ? inputs.schemaPath : `./${inputs.schemaPath}`;
1638
+ const resolveImportSpecifier = scaffoldSpecifierResolverFor(inputs.target);
1639
+ const filesToWrite = [
1640
+ {
1641
+ path: inputs.schemaPath,
1642
+ content: starterSchema(inputs.target, inputs.authoring, resolveImportSpecifier)
1643
+ },
1644
+ {
1645
+ path: "prisma-next.config.ts",
1646
+ content: configFile(inputs.target, configContractPath, resolveImportSpecifier)
1647
+ },
1648
+ {
1649
+ path: join(schemaDir, "db.ts"),
1650
+ content: dbFile(inputs.target, resolveImportSpecifier)
1651
+ },
1652
+ {
1653
+ path: "prisma-next.md",
1654
+ content: quickReferenceMd(inputs.target, inputs.authoring, inputs.schemaPath, pkgRun, resolveImportSpecifier)
1655
+ },
1656
+ {
1657
+ path: ".env.example",
1658
+ content: envExampleContent(inputs.target)
1659
+ }
1660
+ ];
1661
+ const filesToDelete = inputs.reinit ? [...findStaleArtifacts(baseDir, schemaDir)] : [];
1662
+ if (existsSync(join(baseDir, ".agents/skills/prisma-next/SKILL.md"))) filesToDelete.push(LEGACY_SKILL_FILE);
1663
+ if (inputs.writeEnv) if (!existsSync(join(baseDir, ".env"))) filesToWrite.push({
1664
+ path: ".env",
1665
+ content: envFileContent(inputs.target)
1666
+ });
1667
+ else warnings.push(".env already exists; leaving it untouched. Compare with .env.example for any new keys.");
1668
+ const tsconfigPath = join(baseDir, "tsconfig.json");
1669
+ if (existsSync(tsconfigPath)) {
1670
+ const existing = readFileSync(tsconfigPath, "utf-8");
1671
+ let merged;
1672
+ try {
1673
+ merged = mergeTsConfig(existing);
1674
+ } catch (err) {
1675
+ if (err instanceof TsConfigParseError) return emitError(ui, flags, errorInitInvalidTsconfig({
1676
+ path: "tsconfig.json",
1677
+ cause: err.message
1678
+ }));
1679
+ throw err;
1680
+ }
1681
+ filesToWrite.push({
1682
+ path: "tsconfig.json",
1683
+ content: merged,
1684
+ logMessage: "Updated tsconfig.json with required compiler options."
1685
+ });
1686
+ } else filesToWrite.push({
1687
+ path: "tsconfig.json",
1688
+ content: defaultTsConfig()
1689
+ });
1690
+ const gitignorePath = join(baseDir, ".gitignore");
1691
+ const newGitignore = mergeGitignore(existsSync(gitignorePath) ? readFileSync(gitignorePath, "utf-8") : void 0);
1692
+ if (newGitignore !== null) filesToWrite.push({
1693
+ path: ".gitignore",
1694
+ content: newGitignore
1695
+ });
1696
+ const gitattributesPath = join(baseDir, ".gitattributes");
1697
+ const newGitattributes = mergeGitattributes(existsSync(gitattributesPath) ? readFileSync(gitattributesPath, "utf-8") : void 0, requiredGitattributesLines(schemaDir, inputs.target));
1698
+ if (newGitattributes !== null) filesToWrite.push({
1699
+ path: ".gitattributes",
1700
+ content: newGitattributes
1701
+ });
1702
+ const packageJsonPath = join(baseDir, "package.json");
1703
+ const packageJsonExisted = existsSync(packageJsonPath);
1704
+ const synthesisePackageJson = !packageJsonExisted && !hasProjectManifest(baseDir);
1705
+ let parsedPackageJson = null;
1706
+ if (packageJsonExisted || synthesisePackageJson) {
1707
+ const pkgRaw = packageJsonExisted ? readFileSync(packageJsonPath, "utf-8") : defaultPackageJsonContent(basename(baseDir));
1708
+ try {
1709
+ parsedPackageJson = JSON.parse(pkgRaw);
1710
+ } catch (err) {
1711
+ if (err instanceof SyntaxError) return emitError(ui, flags, errorInitInvalidManifest({
1712
+ path: "package.json",
1713
+ cause: err.message
1714
+ }));
1715
+ throw err;
1716
+ }
1717
+ let workingPkg = pkgRaw;
1718
+ let pkgChanged = synthesisePackageJson;
1719
+ if (inputs.removePreviousFacade !== null) {
1720
+ const next = removeDependency(workingPkg, inputs.removePreviousFacade);
1721
+ if (next !== null) {
1722
+ workingPkg = next;
1723
+ pkgChanged = true;
1724
+ }
1725
+ }
1726
+ const { content: nextPkg, warnings: scriptWarnings } = mergePackageScripts(workingPkg, REQUIRED_SCRIPTS);
1727
+ if (nextPkg !== null) {
1728
+ workingPkg = nextPkg;
1729
+ pkgChanged = true;
1730
+ }
1731
+ const { content: typedPkg, warning: typeWarning } = ensureEsmModuleType(workingPkg);
1732
+ if (typedPkg !== null) {
1733
+ workingPkg = typedPkg;
1734
+ pkgChanged = true;
1735
+ }
1736
+ if (pkgChanged) filesToWrite.push({
1737
+ path: "package.json",
1738
+ content: workingPkg
1739
+ });
1740
+ warnings.push(...scriptWarnings);
1741
+ if (typeWarning !== null) warnings.push(typeWarning);
1742
+ if (synthesisePackageJson) warnings.push("No package.json found in the target directory; created a minimal one. Edit `name` / `version` to taste.");
1743
+ }
1744
+ if (existsSync(join(baseDir, "src/index.ts"))) if (!existsSync(join(baseDir, "README.md"))) {
1745
+ const rawName = parsedPackageJson !== null && typeof parsedPackageJson["name"] === "string" ? parsedPackageJson["name"] : basename(baseDir);
1746
+ filesToWrite.push({
1747
+ path: "README.md",
1748
+ content: minimalProjectReadmeMd(inputs.target, inputs.schemaPath, sanitisePackageName(rawName), pm)
1749
+ });
1750
+ } else warnings.push("README.md already exists; leaving it untouched.");
1751
+ for (const file of filesToWrite) {
1752
+ const fullPath = join(baseDir, file.path);
1753
+ mkdirSync(dirname(fullPath), { recursive: true });
1754
+ writeFileSync(fullPath, file.content, "utf-8");
1755
+ filesWritten.push(file.path);
1756
+ if (file.logMessage !== void 0 && !flags.json && !flags.quiet) ui.log(file.logMessage);
1757
+ }
1758
+ for (const rel of filesToDelete) {
1759
+ const fullPath = join(baseDir, rel);
1760
+ if (!existsSync(fullPath)) continue;
1761
+ try {
1762
+ unlinkSync(fullPath);
1763
+ filesDeleted.push(rel);
1764
+ } catch (err) {
1765
+ if (!(err instanceof Error && "code" in err && err.code === "ENOENT")) throw err;
1766
+ }
1767
+ }
1768
+ const emitCommand = formatRunCommand(pm, "prisma-next", "contract emit");
1769
+ let install;
1770
+ try {
1771
+ install = await runInstall({
1772
+ baseDir,
1773
+ pm,
1774
+ target: inputs.target,
1775
+ install: inputs.install,
1776
+ flags,
1777
+ ui,
1778
+ filesWritten,
1779
+ resolveImportSpecifier,
1780
+ hasTypesNode: parsedPackageJson !== null ? hasDirectDep(parsedPackageJson, "@types/node") : false
1781
+ });
1782
+ } catch (error) {
1783
+ if (CliStructuredError$1.is(error)) return emitError(ui, flags, error);
1784
+ throw error;
1785
+ }
1786
+ warnings.push(...install.warnings);
1787
+ let contractEmitted = false;
1788
+ if (!install.skipped) try {
1789
+ await runEmit({
1790
+ baseDir,
1791
+ ui,
1792
+ filesWritten,
1793
+ emitCommand
1794
+ });
1795
+ contractEmitted = true;
1796
+ } catch (error) {
1797
+ if (CliStructuredError$1.is(error)) return emitError(ui, flags, error);
1798
+ throw error;
1799
+ }
1800
+ if (inputs.probeDb) {
1801
+ const escalated = applyProbeOutcome(await probeServerVersion({
1802
+ baseDir,
1803
+ target: inputs.target,
1804
+ databaseUrl: process.env["DATABASE_URL"],
1805
+ minVersion: MIN_SERVER_VERSION[inputs.target]
1806
+ }, probeOverrides ?? {}), {
1807
+ strictProbe: inputs.strictProbe,
1808
+ warnings
1809
+ });
1810
+ if (escalated !== null) return emitError(ui, flags, errorInitProbeFailed({
1811
+ cause: escalated,
1812
+ filesWritten
1813
+ }));
1814
+ }
1815
+ const manualProjectSkillSummary = DEFAULT_SKILL_SOURCES.map((source) => formatSkillInstallCommand({
1816
+ pm: install.effectivePm,
1817
+ source
1818
+ })).map((c) => `\`${c}\``).join(" && ");
1819
+ let skillRegistered = false;
1820
+ if (!inputs.installProjectSkill) warnings.push(`Skipped Prisma Next skills install (--no-skill). To install the skills later, run: ${manualProjectSkillSummary}`);
1821
+ else {
1822
+ const spinner = ui.spinner();
1823
+ spinner.start("Registering Prisma Next skills with the agent runtime...");
1824
+ try {
1825
+ const project = await runProjectLevelSkillInstall({
1826
+ baseDir,
1827
+ pm: install.effectivePm,
1828
+ filesWritten
1829
+ });
1830
+ spinner.stop(`Registered Prisma Next skills (project level) — ran ${project.commands.map((c) => `\`${c}\``).join(", ")}`);
1831
+ skillRegistered = true;
1832
+ } catch (error) {
1833
+ spinner.stop("Agent-skill install failed");
1834
+ if (CliStructuredError$1.is(error)) return emitError(ui, flags, error);
1835
+ throw error;
1836
+ }
1837
+ }
1838
+ const output = {
1839
+ ok: true,
1840
+ target: inputs.target === "mongo" ? "mongodb" : "postgres",
1841
+ authoring: inputs.authoring,
1842
+ schemaPath: inputs.schemaPath,
1843
+ filesWritten,
1844
+ filesDeleted,
1845
+ packagesInstalled: {
1846
+ skipped: install.skipped,
1847
+ deps: [...install.deps],
1848
+ devDeps: [...install.devDeps]
1849
+ },
1850
+ contractEmitted,
1851
+ nextSteps: buildNextSteps({
1852
+ target: inputs.target === "mongo" ? "mongodb" : "postgres",
1853
+ contractEmitted,
1854
+ emitCommand,
1855
+ schemaPath: inputs.schemaPath,
1856
+ skillRegistered
1857
+ }),
1858
+ warnings
1859
+ };
1860
+ const validated = InitOutputSchema(output);
1861
+ if (validated instanceof Error || validated.problems !== void 0) return emitError(ui, flags, new CliStructuredError$1("CLI.INIT_INVALID_OUTPUT_DOCUMENT", "Init produced an invalid output document", {
1862
+ why: `The success document failed schema validation: ${String(validated)}`,
1863
+ fix: "This is a bug in prisma-next. Please report it with the full `-v` output.",
1864
+ docsUrl: docsUrlFor("CLI.INIT_INVALID_OUTPUT_DOCUMENT")
1865
+ }));
1866
+ if (flags.json) ui.output(formatInitJson(output));
1867
+ else {
1868
+ renderInitOutro(ui, output, flags);
1869
+ if (!flags.quiet) clack.outro("Done. Open prisma-next.md to get started.", { output: process.stderr });
1870
+ }
1871
+ return 0;
1872
+ }
1873
+ /**
1874
+ * Renders a structured CLI error to the right channel and returns the exit
1875
+ * code derived from the error's PN code. JSON-mode errors go to stdout
1876
+ * (so consumers always parse from one place); human-mode errors go to
1877
+ * stderr. Mirrors `handleResult` but returns init-specific exit codes
1878
+ * rather than the CLI/RUN binary.
1879
+ */
1880
+ function emitError(ui, flags, error) {
1881
+ const envelope = error.toEnvelope();
1882
+ if (flags.json) ui.output(formatErrorJson(envelope));
1883
+ else ui.error(formatErrorOutput(envelope, flags));
1884
+ return exitCodeForError(error);
1885
+ }
1886
+ /**
1887
+ * Maps a structured init error to its documented exit code. Centralised so
1888
+ * the error → exit-code contract lives next to the codes themselves.
1889
+ *
1890
+ * `CLI.INIT_INVALID_OUTPUT_DOCUMENT` (and the unknown-code default branch)
1891
+ * routes to `INIT_EXIT_INTERNAL_ERROR` because those represent prisma-next
1892
+ * bugs the user did not cause — surfacing them as `PRECONDITION` would
1893
+ * mislead automation into thinking the caller mis-invoked the CLI.
1894
+ *
1895
+ * See [exit-codes.ts](./exit-codes.ts) for the canonical list and
1896
+ * [Style Guide § Exit Codes](../../../../../../../docs/CLI%20Style%20Guide.md#exit-codes)
1897
+ * for the reservation policy.
1898
+ *
1899
+ * Exported for unit tests so the mapping can be asserted without
1900
+ * round-tripping a full `runInit` invocation.
1901
+ */
1902
+ function exitCodeForError(error) {
1903
+ switch (error.code) {
1904
+ case "CLI.INIT_REINIT_NEEDS_FORCE":
1905
+ case "CLI.INIT_MISSING_FLAGS":
1906
+ case "CLI.INIT_INVALID_FLAG_VALUE":
1907
+ case "CLI.INIT_STRICT_PROBE_WITHOUT_PROBE":
1908
+ case "CLI.INIT_INVALID_MANIFEST":
1909
+ case "CLI.INIT_INVALID_TSCONFIG":
1910
+ case "CLI.INIT_PROBE_FAILED":
1911
+ case "CLI.INIT_AUTHORING_SCHEMA_PATH_MISMATCH": return 2;
1912
+ case "CLI.INIT_USER_ABORTED": return 3;
1913
+ case "CLI.INIT_INSTALL_FAILED": return 4;
1914
+ case "CLI.INIT_EMIT_FAILED": return 5;
1915
+ case "CLI.INIT_INVALID_OUTPUT_DOCUMENT": return 1;
1916
+ case "CLI.INIT_SKILL_INSTALL_FAILED": return 6;
1917
+ default: return 1;
1918
+ }
1919
+ }
1920
+ /**
1921
+ * Folds a `ProbeOutcome` into init's warning channel and returns the
1922
+ * fatal cause string when `--strict-probe` should escalate. Mirrors
1923
+ * the FR8.3 contract:
1924
+ *
1925
+ * - `ok` — informational; nothing surfaced unless verbose. (We could
1926
+ * plumb a `note` here, but the spec only requires the warning side
1927
+ * of the contract; an "all good" line would just be noise on the
1928
+ * common path.)
1929
+ * - `below-minimum` — warning regardless of `--strict-probe`. The
1930
+ * probe ran successfully and found an old server; that is not a
1931
+ * probe *failure* (which is what `--strict-probe` escalates), it
1932
+ * is the probe doing its job.
1933
+ * - `no-database-url` / `connection-failed` / `driver-missing` —
1934
+ * warning by default, fatal under `--strict-probe`.
1935
+ *
1936
+ * Exported for unit tests so the branching contract can be asserted
1937
+ * without spinning up a full `runInit` round trip.
1938
+ */
1939
+ function applyProbeOutcome(outcome, ctx) {
1940
+ switch (outcome.kind) {
1941
+ case "ok": return null;
1942
+ case "below-minimum":
1943
+ ctx.warnings.push(outcome.message);
1944
+ return null;
1945
+ case "no-database-url":
1946
+ case "connection-failed":
1947
+ case "driver-missing":
1948
+ if (ctx.strictProbe) return outcome.message;
1949
+ ctx.warnings.push(outcome.message);
1950
+ return null;
1951
+ }
1952
+ }
1953
+ /**
1954
+ * Drives the `pnpm add` / `npm install` step. Failures are escalated to
1955
+ * a structured `errorInitInstallFailed` (exit code 4) — the spec treats
1956
+ * an unrecoverable install as a hard outcome rather than a warning so
1957
+ * CI/agents can branch on the exit code (FR1.6).
1958
+ *
1959
+ * For pnpm specifically, we additionally implement the FR7.2 fallback:
1960
+ * if pnpm fails with a recognised workspace/catalog resolution error
1961
+ * class (typically caused by a registry version that leaked
1962
+ * `workspace:*` or `catalog:` specifiers), we retry the install using
1963
+ * `npm` and surface a non-fatal warning explaining the swap.
1964
+ */
1965
+ async function runInstall(ctx) {
1966
+ const { baseDir, pm, target, install, flags, ui, filesWritten, hasTypesNode } = ctx;
1967
+ const deps = [targetPackageName(target, ctx.resolveImportSpecifier), "dotenv"];
1968
+ const devDeps = hasTypesNode ? ["prisma-next"] : ["prisma-next", "@types/node"];
1969
+ const addCommand = `${pm} ${formatAddArgs(pm, deps).join(" ")}`;
1970
+ const addDevCommand = `${pm} ${formatAddDevArgs(pm, devDeps).join(" ")}`;
1971
+ const emitCommand = formatRunCommand(pm, "prisma-next", "contract emit");
1972
+ const catalogWarnings = pm === "pnpm" ? buildCatalogWarnings(baseDir, [...deps, ...devDeps]) : [];
1973
+ if (!install) {
1974
+ if (!flags.json && !flags.quiet) ui.note([
1975
+ "Run the following commands to complete setup:",
1976
+ "",
1977
+ " 1. Install dependencies:",
1978
+ ` ${addCommand}`,
1979
+ ` ${addDevCommand}`,
1980
+ "",
1981
+ " 2. Emit the contract:",
1982
+ ` ${emitCommand}`
1983
+ ].join("\n"), "Manual steps");
1984
+ return {
1985
+ skipped: true,
1986
+ deps: [],
1987
+ devDeps: [],
1988
+ warnings: catalogWarnings,
1989
+ effectivePm: pm
1990
+ };
1991
+ }
1992
+ const exec = promisify(execFile);
1993
+ const runPair = async (manager) => {
1994
+ await exec(manager, formatAddArgs(manager, deps), { cwd: baseDir });
1995
+ await exec(manager, formatAddDevArgs(manager, devDeps), { cwd: baseDir });
1996
+ };
1997
+ const allPackages = [...deps, ...devDeps].join(", ");
1998
+ const spinner = ui.spinner();
1999
+ spinner.start(`Installing ${allPackages}...`);
2000
+ try {
2001
+ await runPair(pm);
2002
+ spinner.stop(`Installed ${allPackages}`);
2003
+ return {
2004
+ skipped: false,
2005
+ deps,
2006
+ devDeps,
2007
+ warnings: catalogWarnings,
2008
+ effectivePm: pm
2009
+ };
2010
+ } catch (err) {
2011
+ const stderrText = redactSecrets(readChildStderr(err));
2012
+ if (pm === "pnpm" && isRecognisedPnpmResolutionError(stderrText)) {
2013
+ spinner.message("pnpm could not resolve a workspace/catalog dependency, retrying with npm...");
2014
+ try {
2015
+ await runPair("npm");
2016
+ spinner.stop(`Installed ${allPackages} via npm (pnpm fallback)`);
2017
+ return {
2018
+ skipped: false,
2019
+ deps,
2020
+ devDeps,
2021
+ warnings: [[
2022
+ "pnpm could not install: a published Prisma Next dependency leaked a `workspace:*` or `catalog:` specifier.",
2023
+ "Falling back to `npm install` so init can complete.",
2024
+ stderrText ? ` pnpm error: ${stderrText.trim().split("\n")[0]}` : "",
2025
+ "Once the offending package republishes a clean version, re-run `pnpm install` to switch back."
2026
+ ].filter(Boolean).join("\n")],
2027
+ effectivePm: "npm"
2028
+ };
2029
+ } catch (npmErr) {
2030
+ spinner.stop("Installation failed");
2031
+ throw errorInitInstallFailed({
2032
+ addCommand,
2033
+ addDevCommand,
2034
+ emitCommand,
2035
+ filesWritten,
2036
+ stderrLines: [stderrText, redactSecrets(readChildStderr(npmErr))]
2037
+ });
2038
+ }
2039
+ }
2040
+ spinner.stop("Installation failed");
2041
+ throw errorInitInstallFailed({
2042
+ addCommand,
2043
+ addDevCommand,
2044
+ emitCommand,
2045
+ filesWritten,
2046
+ stderrLines: [stderrText]
2047
+ });
2048
+ }
2049
+ }
2050
+ /**
2051
+ * Builds the FR7.3 catalog-honoured warning(s) for the surrounding pnpm
2052
+ * workspace, if any. Returns an empty array when no `pnpm-workspace.yaml`
2053
+ * exists in any ancestor or when the workspace's catalog has no entry
2054
+ * for any of the packages `init` is about to install.
2055
+ *
2056
+ * Exported for unit tests.
2057
+ */
2058
+ function buildCatalogWarnings(baseDir, packages) {
2059
+ const result = detectPnpmCatalogOverrides(baseDir, packages);
2060
+ if (result === null || result.entries.length === 0) return [];
2061
+ return [formatCatalogWarning(result.workspaceFile, result.entries)];
2062
+ }
2063
+ function formatCatalogWarning(workspaceFile, entries) {
2064
+ return [
2065
+ "pnpm workspace catalog overrides detected — pnpm will install these versions instead of `latest`:",
2066
+ entries.map((entry) => ` • ${entry.name}: ${entry.version}`).join("\n"),
2067
+ `Catalog source: ${workspaceFile}`,
2068
+ "To use the published `latest` instead, remove or update the catalog entry, then re-run `pnpm install`."
2069
+ ].join("\n");
2070
+ }
2071
+ /**
2072
+ * Recognised pnpm error signatures that justify a fallback to npm.
2073
+ *
2074
+ * These patterns indicate the published artifact itself is at fault
2075
+ * (a leaked `workspace:*` or `catalog:` specifier), not the user's
2076
+ * environment — pnpm is faithfully reporting "I cannot resolve this
2077
+ * registry version", and npm is willing to install it because npm
2078
+ * doesn't care about the protocol prefix when there's a fallback range.
2079
+ *
2080
+ * Exported for unit tests; do not depend on this from outside the init
2081
+ * command.
2082
+ */
2083
+ function isRecognisedPnpmResolutionError(stderr) {
2084
+ if (!stderr) return false;
2085
+ return stderr.includes("ERR_PNPM_WORKSPACE_PKG_NOT_FOUND") || stderr.includes("ERR_PNPM_NO_MATCHING_VERSION") || /No matching version found for .* in the catalog/i.test(stderr) || /workspace:[^\s]+ is not a valid (version|spec)/i.test(stderr) || /catalog:[^\s]* is not a valid (version|spec)/i.test(stderr);
2086
+ }
2087
+ /**
2088
+ * FR2.1 — true when the parsed `package.json` declares `name` directly
2089
+ * in either `dependencies` or `devDependencies`. We deliberately don't
2090
+ * inspect `peerDependencies` (irrelevant for a leaf project) or the
2091
+ * lockfile (transitive presence is brittle to detect and not the
2092
+ * realistic clobber-risk path).
2093
+ *
2094
+ * Exported for unit tests.
2095
+ */
2096
+ function hasDirectDep(parsed, name) {
2097
+ for (const field of ["dependencies", "devDependencies"]) {
2098
+ const value = parsed[field];
2099
+ if (value !== null && typeof value === "object" && name in value) return true;
2100
+ }
2101
+ return false;
2102
+ }
2103
+ function readChildStderr(err) {
2104
+ if (err instanceof Error && "stderr" in err) return String(err.stderr ?? "");
2105
+ return "";
2106
+ }
2107
+ /**
2108
+ * Redacts userinfo (`user:password@`) from any URL-shaped substring inside
2109
+ * package-manager stderr before we surface it in a warning or error
2110
+ * meta. pnpm and npm both include the offending registry URL in resolve
2111
+ * errors, and that URL can carry an auth token (e.g. corporate registry
2112
+ * mirrors that bake `_authToken` into the URL). The Style Guide
2113
+ * (Testing & Accessibility — "Security: never print secrets") requires
2114
+ * we never surface those.
2115
+ *
2116
+ * Exported for unit tests.
2117
+ */
2118
+ function redactSecrets(stderr) {
2119
+ if (!stderr) return stderr;
2120
+ return stderr.replace(/([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)([^/@\s]+)@/g, "$1***@");
2121
+ }
2122
+ /**
2123
+ * Drives `prisma-next contract emit` against the freshly scaffolded
2124
+ * project. On failure, throws `errorInitEmitFailed` with the underlying
2125
+ * cause embedded in `meta.cause` so the user can re-run with `-v` to see
2126
+ * the full envelope and follow the fix steps. Maps to exit code
2127
+ * `5 = EMIT_FAILED` (FR1.6).
2128
+ */
2129
+ async function runEmit(ctx) {
2130
+ const spinner = ctx.ui.spinner();
2131
+ spinner.start("Emitting contract...");
2132
+ try {
2133
+ const { executeContractEmit } = await import("./contract-emit-B6odMoem-CK3diBPk.mjs").then((n) => n.t).then((n) => n.t);
2134
+ await executeContractEmit({ configPath: join(ctx.baseDir, "prisma-next.config.ts") });
2135
+ spinner.stop("Contract emitted");
2136
+ } catch (err) {
2137
+ spinner.stop("Contract emission failed");
2138
+ throw errorInitEmitFailed({
2139
+ emitCommand: ctx.emitCommand,
2140
+ filesWritten: ctx.filesWritten,
2141
+ cause: causeMessage(err)
2142
+ });
2143
+ }
2144
+ }
2145
+ function causeMessage(err) {
2146
+ if (err instanceof Error) return err.message;
2147
+ return String(err);
2148
+ }
2149
+ /**
2150
+ * Minimal `package.json` content used when init runs in a directory
2151
+ * that has no project manifest (TML-2496). Mirrors the npm 11 `init -y`
2152
+ * defaults, with two deliberate deviations:
2153
+ *
2154
+ * - `"private": true` so a stray `npm publish` cannot leak the
2155
+ * placeholder. Users who want to publish have to opt in by removing
2156
+ * the field.
2157
+ * - `"type": "module"` so the scaffolded ESM imports in
2158
+ * `prisma-next.config.ts` and `db.ts` typecheck and run without
2159
+ * additional tsconfig coercion.
2160
+ *
2161
+ * Exported for unit tests so the canonical shape is asserted in one
2162
+ * place rather than re-derived at every call site.
2163
+ */
2164
+ function defaultPackageJsonContent(rawName) {
2165
+ return `${JSON.stringify({
2166
+ name: sanitisePackageName(rawName),
2167
+ version: "0.0.0",
2168
+ private: true,
2169
+ type: "module"
2170
+ }, null, 2)}\n`;
2171
+ }
2172
+ /**
2173
+ * npm package names are restricted to lowercase, no leading dot/underscore,
2174
+ * and a small URL-safe character set. `basename(cwd)` happily returns
2175
+ * "My Project" or ".hidden" — both rejected by `npm install` validation.
2176
+ * Coerce to a safe fallback rather than emit a manifest npm refuses to
2177
+ * read.
2178
+ */
2179
+ function sanitisePackageName(raw) {
2180
+ const trimmed = raw.toLowerCase().replace(/[^a-z0-9._~-]/g, "-").replace(/^[._-]+/, "").replace(/-+/g, "-");
2181
+ return trimmed.length > 0 ? trimmed : "my-app";
2182
+ }
2183
+ //#endregion
2184
+ export { runInit };
2185
+
2186
+ //# sourceMappingURL=init-DHctXAI9-DSlaaYXN.mjs.map