@geekmidas/cli 10.0.0-alpha.123 → 10.0.0-alpha.124

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 (247) hide show
  1. package/dist/{EndpointGenerator-BDa6pPgE.cjs → EndpointGenerator-B2MUa4M2.cjs} +2 -2
  2. package/dist/{EndpointGenerator-BDa6pPgE.cjs.map → EndpointGenerator-B2MUa4M2.cjs.map} +1 -1
  3. package/dist/{EndpointGenerator-CP_z_E3e.mjs → EndpointGenerator-Z_BUTocT.mjs} +2 -2
  4. package/dist/{EndpointGenerator-CP_z_E3e.mjs.map → EndpointGenerator-Z_BUTocT.mjs.map} +1 -1
  5. package/dist/{GoDaddyProvider-Dubq-pCc.cjs → GoDaddyProvider-0z5RAN1k.cjs} +3 -3
  6. package/dist/{GoDaddyProvider-Dubq-pCc.cjs.map → GoDaddyProvider-0z5RAN1k.cjs.map} +1 -1
  7. package/dist/{GoDaddyProvider-BlB5nJky.mjs → GoDaddyProvider-55x4o-4z.mjs} +3 -3
  8. package/dist/{GoDaddyProvider-BlB5nJky.mjs.map → GoDaddyProvider-55x4o-4z.mjs.map} +1 -1
  9. package/dist/{HostingerProvider-BNpS9aRJ.mjs → HostingerProvider-Cjh_2Ml6.mjs} +2 -2
  10. package/dist/{HostingerProvider-BNpS9aRJ.mjs.map → HostingerProvider-Cjh_2Ml6.mjs.map} +1 -1
  11. package/dist/{HostingerProvider-DR1iws1P.cjs → HostingerProvider-OdQmKXE3.cjs} +2 -2
  12. package/dist/{HostingerProvider-DR1iws1P.cjs.map → HostingerProvider-OdQmKXE3.cjs.map} +1 -1
  13. package/dist/{LegacyStateStore-DGALQGZu.mjs → LegacyStateStore-C-vumKSN.mjs} +3 -3
  14. package/dist/{LegacyStateStore-DGALQGZu.mjs.map → LegacyStateStore-C-vumKSN.mjs.map} +1 -1
  15. package/dist/{LegacyStateStore-ClvUC8qy.cjs → LegacyStateStore-DRpB-yC5.cjs} +3 -3
  16. package/dist/{LegacyStateStore-ClvUC8qy.cjs.map → LegacyStateStore-DRpB-yC5.cjs.map} +1 -1
  17. package/dist/{LocalStateStore-43kSYNpw.cjs → LocalStateStore-C3ummuDT.cjs} +2 -2
  18. package/dist/{LocalStateStore-43kSYNpw.cjs.map → LocalStateStore-C3ummuDT.cjs.map} +1 -1
  19. package/dist/{LocalStateStore-CBYUzVR5.mjs → LocalStateStore-CtOjpV1w.mjs} +2 -2
  20. package/dist/{LocalStateStore-CBYUzVR5.mjs.map → LocalStateStore-CtOjpV1w.mjs.map} +1 -1
  21. package/dist/{OpenApiTsGenerator-CsrukCE-.mjs → OpenApiTsGenerator-MtnMQL_g.mjs} +2 -2
  22. package/dist/{OpenApiTsGenerator-CsrukCE-.mjs.map → OpenApiTsGenerator-MtnMQL_g.mjs.map} +1 -1
  23. package/dist/{OpenApiTsGenerator-FNhY7Ssz.cjs → OpenApiTsGenerator-t3_E98Fi.cjs} +2 -2
  24. package/dist/{OpenApiTsGenerator-FNhY7Ssz.cjs.map → OpenApiTsGenerator-t3_E98Fi.cjs.map} +1 -1
  25. package/dist/{S3StateStore-DLHjDYYP.mjs → S3StateStore-BC4Jm3Tw.mjs} +2 -2
  26. package/dist/{S3StateStore-DLHjDYYP.mjs.map → S3StateStore-BC4Jm3Tw.mjs.map} +1 -1
  27. package/dist/{S3StateStore-ahE7hSNK.cjs → S3StateStore-BeArp0aF.cjs} +2 -2
  28. package/dist/{S3StateStore-ahE7hSNK.cjs.map → S3StateStore-BeArp0aF.cjs.map} +1 -1
  29. package/dist/{SSMStateStore-j5RuMkch.mjs → SSMStateStore-Crl76aYZ.mjs} +2 -2
  30. package/dist/{SSMStateStore-j5RuMkch.mjs.map → SSMStateStore-Crl76aYZ.mjs.map} +1 -1
  31. package/dist/{SSMStateStore-qIzIuazA.cjs → SSMStateStore-DcstR6cW.cjs} +2 -2
  32. package/dist/{SSMStateStore-qIzIuazA.cjs.map → SSMStateStore-DcstR6cW.cjs.map} +1 -1
  33. package/dist/{SubscriberGenerator-C8CPxrCK.cjs → SubscriberGenerator-C3QTae-R.cjs} +237 -54
  34. package/dist/SubscriberGenerator-C3QTae-R.cjs.map +1 -0
  35. package/dist/{SubscriberGenerator-y3pncae_.mjs → SubscriberGenerator-COAVPgM-.mjs} +227 -56
  36. package/dist/SubscriberGenerator-COAVPgM-.mjs.map +1 -0
  37. package/dist/{add-C2CvXrYw.cjs → add-BFjb6mMq.cjs} +6 -6
  38. package/dist/{add-C2CvXrYw.cjs.map → add-BFjb6mMq.cjs.map} +1 -1
  39. package/dist/{add-Bm-C56lH.mjs → add-CIjmgzkb.mjs} +5 -5
  40. package/dist/{add-Bm-C56lH.mjs.map → add-CIjmgzkb.mjs.map} +1 -1
  41. package/dist/{bundler-Dk7tAvAX.cjs → bundler-CiUoIsku.cjs} +5 -5
  42. package/dist/{bundler-Dk7tAvAX.cjs.map → bundler-CiUoIsku.cjs.map} +1 -1
  43. package/dist/{bundler-DnkFt4mn.mjs → bundler-D6KFNpQk.mjs} +5 -5
  44. package/dist/{bundler-DnkFt4mn.mjs.map → bundler-D6KFNpQk.mjs.map} +1 -1
  45. package/dist/{cli-CVeHFXNE.mjs → cli-DNtSTtMc.mjs} +4 -4
  46. package/dist/{cli-CVeHFXNE.mjs.map → cli-DNtSTtMc.mjs.map} +1 -1
  47. package/dist/{cli-Di464j1O.cjs → cli-r4eUr98C.cjs} +4 -4
  48. package/dist/{cli-Di464j1O.cjs.map → cli-r4eUr98C.cjs.map} +1 -1
  49. package/dist/{compose-ChVvb3xM.cjs → compose-BfXXWJSp.cjs} +328 -54
  50. package/dist/compose-BfXXWJSp.cjs.map +1 -0
  51. package/dist/{compose-cHkF---f.mjs → compose-DJvvmF3X.mjs} +299 -25
  52. package/dist/compose-DJvvmF3X.mjs.map +1 -0
  53. package/dist/{config-DrOX29sT.d.mts → config-IgK6YaGp.d.cts} +3 -4
  54. package/dist/config-IgK6YaGp.d.cts.map +1 -0
  55. package/dist/{config-B3iz9BtK.d.cts → config-kE-p5hxq.d.mts} +3 -4
  56. package/dist/config-kE-p5hxq.d.mts.map +1 -0
  57. package/dist/config.cjs +1 -1
  58. package/dist/config.d.cts +3 -3
  59. package/dist/config.d.mts +3 -3
  60. package/dist/config.mjs +1 -1
  61. package/dist/{credentialSchemas-B67WJOOm.cjs → credentialSchemas-AhIBuQxQ.cjs} +2 -2
  62. package/dist/{credentialSchemas-B67WJOOm.cjs.map → credentialSchemas-AhIBuQxQ.cjs.map} +1 -1
  63. package/dist/{credentialSchemas-DDOLRCLF.mjs → credentialSchemas-DLKbxYBy.mjs} +2 -2
  64. package/dist/{credentialSchemas-DDOLRCLF.mjs.map → credentialSchemas-DLKbxYBy.mjs.map} +1 -1
  65. package/dist/{credentials-Zqr3QQYB.cjs → credentials-D7XJypNd.cjs} +173 -3
  66. package/dist/credentials-D7XJypNd.cjs.map +1 -0
  67. package/dist/{credentials-B5N5yKk6.mjs → credentials-DpfnUvIR.mjs} +132 -4
  68. package/dist/credentials-DpfnUvIR.mjs.map +1 -0
  69. package/dist/deploy/public.cjs +15 -16
  70. package/dist/deploy/public.cjs.map +1 -1
  71. package/dist/deploy/public.d.cts +3 -5
  72. package/dist/deploy/public.d.cts.map +1 -1
  73. package/dist/deploy/public.d.mts +3 -5
  74. package/dist/deploy/public.d.mts.map +1 -1
  75. package/dist/deploy/public.mjs +10 -11
  76. package/dist/deploy/public.mjs.map +1 -1
  77. package/dist/{deploy-BffR_uEN.cjs → deploy-BbI1UMh3.cjs} +476 -156
  78. package/dist/deploy-BbI1UMh3.cjs.map +1 -0
  79. package/dist/{deploy-DO4djCHb.mjs → deploy-CiRYwFmr.mjs} +454 -116
  80. package/dist/deploy-CiRYwFmr.mjs.map +1 -0
  81. package/dist/{discover-C5roJuc9.mjs → discover-BvJOVsYG.mjs} +66 -14
  82. package/dist/discover-BvJOVsYG.mjs.map +1 -0
  83. package/dist/{discover-DozUKs8f.cjs → discover-ojkcbxHw.cjs} +88 -12
  84. package/dist/discover-ojkcbxHw.cjs.map +1 -0
  85. package/dist/{dokploy-CsdvfH3t.cjs → dokploy-C0aQrCgs.cjs} +2 -2
  86. package/dist/{dokploy-CsdvfH3t.cjs.map → dokploy-C0aQrCgs.cjs.map} +1 -1
  87. package/dist/{dokploy-BcZm93Cc.mjs → dokploy-MM7c3IE0.mjs} +2 -2
  88. package/dist/{dokploy-BcZm93Cc.mjs.map → dokploy-MM7c3IE0.mjs.map} +1 -1
  89. package/dist/{drivers-DbjiYpW6.cjs → drivers-CQ32PB5w.cjs} +2 -2
  90. package/dist/{drivers-DbjiYpW6.cjs.map → drivers-CQ32PB5w.cjs.map} +1 -1
  91. package/dist/{drivers-BjGDYfnC.mjs → drivers-Chb5Akov.mjs} +2 -2
  92. package/dist/{drivers-BjGDYfnC.mjs.map → drivers-Chb5Akov.mjs.map} +1 -1
  93. package/dist/{eas-CBShP20v.mjs → eas-1Ee6kql0.mjs} +2 -2
  94. package/dist/{eas-CBShP20v.mjs.map → eas-1Ee6kql0.mjs.map} +1 -1
  95. package/dist/{eas-CN_cUXth.cjs → eas-CJoys9yh.cjs} +2 -2
  96. package/dist/{eas-CN_cUXth.cjs.map → eas-CJoys9yh.cjs.map} +1 -1
  97. package/dist/{emit-C1e8qD08.mjs → emit-CNpuV_6Q.mjs} +17 -1
  98. package/dist/emit-CNpuV_6Q.mjs.map +1 -0
  99. package/dist/{emit-Dmc1nvdS.cjs → emit-qvHxMV9b.cjs} +17 -1
  100. package/dist/emit-qvHxMV9b.cjs.map +1 -0
  101. package/dist/{encryption-CITa6dXL.mjs → encryption-B8Bk1Mvu.mjs} +2 -2
  102. package/dist/{encryption-CITa6dXL.mjs.map → encryption-B8Bk1Mvu.mjs.map} +1 -1
  103. package/dist/{encryption-CPDM49Hi.cjs → encryption-Bo9VlRnl.cjs} +2 -2
  104. package/dist/{encryption-CPDM49Hi.cjs.map → encryption-Bo9VlRnl.cjs.map} +1 -1
  105. package/dist/{engine-CTnHmHjz.mjs → engine-BuinpJfS.mjs} +6 -7
  106. package/dist/{engine-CTnHmHjz.mjs.map → engine-BuinpJfS.mjs.map} +1 -1
  107. package/dist/{engine-D-jfMVj-.cjs → engine-esxTxcIx.cjs} +9 -10
  108. package/dist/{engine-D-jfMVj-.cjs.map → engine-esxTxcIx.cjs.map} +1 -1
  109. package/dist/{github-D56PSK39.cjs → github-BEA4K3_X.cjs} +79 -46
  110. package/dist/github-BEA4K3_X.cjs.map +1 -0
  111. package/dist/{github-9RlA7EjN.mjs → github-BUlKJfOV.mjs} +77 -44
  112. package/dist/github-BUlKJfOV.mjs.map +1 -0
  113. package/dist/{googlePlay-COi5LXON.mjs → googlePlay-CK05QiTq.mjs} +4 -4
  114. package/dist/{googlePlay-COi5LXON.mjs.map → googlePlay-CK05QiTq.mjs.map} +1 -1
  115. package/dist/{googlePlay-SX_jQeJf.cjs → googlePlay-CiqAS2ma.cjs} +5 -5
  116. package/dist/{googlePlay-SX_jQeJf.cjs.map → googlePlay-CiqAS2ma.cjs.map} +1 -1
  117. package/dist/{index-DcyRA5Rf.d.mts → index-4CO0HD27.d.cts} +2 -2
  118. package/dist/{index-DcyRA5Rf.d.mts.map → index-4CO0HD27.d.cts.map} +1 -1
  119. package/dist/{index-DjLSMgAL.d.cts → index-DhUf0B0m.d.mts} +2 -2
  120. package/dist/{index-DjLSMgAL.d.cts.map → index-DhUf0B0m.d.mts.map} +1 -1
  121. package/dist/index.cjs +79 -80
  122. package/dist/index.cjs.map +1 -1
  123. package/dist/index.mjs +62 -63
  124. package/dist/index.mjs.map +1 -1
  125. package/dist/{local-BwC2v2Y8.mjs → local-B7Zv1NoA.mjs} +2 -2
  126. package/dist/{local-BwC2v2Y8.mjs.map → local-B7Zv1NoA.mjs.map} +1 -1
  127. package/dist/{local-DNhnkugT.cjs → local-OPC9BCZt.cjs} +3 -3
  128. package/dist/{local-DNhnkugT.cjs.map → local-OPC9BCZt.cjs.map} +1 -1
  129. package/dist/{migrate-DJa_Dlsl.mjs → migrate-BzCHT9qs.mjs} +2 -2
  130. package/dist/{migrate-DJa_Dlsl.mjs.map → migrate-BzCHT9qs.mjs.map} +1 -1
  131. package/dist/{migrate-BCz1lkh_.cjs → migrate-C-hmbjpG.cjs} +2 -2
  132. package/dist/{migrate-BCz1lkh_.cjs.map → migrate-C-hmbjpG.cjs.map} +1 -1
  133. package/dist/{migrate-0J6gsfrQ.mjs → migrate-CnbvrVag.mjs} +3 -3
  134. package/dist/{migrate-0J6gsfrQ.mjs.map → migrate-CnbvrVag.mjs.map} +1 -1
  135. package/dist/{migrate-CrtxXeGY.cjs → migrate-DeQJBexh.cjs} +3 -3
  136. package/dist/{migrate-CrtxXeGY.cjs.map → migrate-DeQJBexh.cjs.map} +1 -1
  137. package/dist/openapi.cjs +2 -2
  138. package/dist/openapi.d.cts +1 -1
  139. package/dist/openapi.d.mts +1 -1
  140. package/dist/openapi.mjs +2 -2
  141. package/dist/{publish-CuQlOW5p.mjs → publish-BISBrrqR.mjs} +2 -2
  142. package/dist/{publish-CuQlOW5p.mjs.map → publish-BISBrrqR.mjs.map} +1 -1
  143. package/dist/{publish-qSpnvmdX.cjs → publish-DtSIB-hZ.cjs} +2 -2
  144. package/dist/{publish-qSpnvmdX.cjs.map → publish-DtSIB-hZ.cjs.map} +1 -1
  145. package/dist/{ready-BOfogqzr.mjs → ready-DqsyadOs.mjs} +6 -6
  146. package/dist/{ready-BOfogqzr.mjs.map → ready-DqsyadOs.mjs.map} +1 -1
  147. package/dist/{ready-BFFP838V.cjs → ready-IVGu-q18.cjs} +6 -6
  148. package/dist/{ready-BFFP838V.cjs.map → ready-IVGu-q18.cjs.map} +1 -1
  149. package/dist/reconcile/public.cjs +2 -2
  150. package/dist/reconcile/public.d.cts +1 -2
  151. package/dist/reconcile/public.d.cts.map +1 -1
  152. package/dist/reconcile/public.d.mts +1 -2
  153. package/dist/reconcile/public.d.mts.map +1 -1
  154. package/dist/reconcile/public.mjs +2 -2
  155. package/dist/{resolve-j8hUcbGz.mjs → resolve-BT7fkPN5.mjs} +4 -4
  156. package/dist/{resolve-j8hUcbGz.mjs.map → resolve-BT7fkPN5.mjs.map} +1 -1
  157. package/dist/{resolve-AsfohXd1.cjs → resolve-C7GKQcE4.cjs} +4 -4
  158. package/dist/{resolve-AsfohXd1.cjs.map → resolve-C7GKQcE4.cjs.map} +1 -1
  159. package/dist/{rollback-Bxq_URgL.cjs → rollback-BEiB-Pje.cjs} +6 -6
  160. package/dist/{rollback-Bxq_URgL.cjs.map → rollback-BEiB-Pje.cjs.map} +1 -1
  161. package/dist/{rollback-JgbAY43C.mjs → rollback-DbP0KuDw.mjs} +6 -6
  162. package/dist/{rollback-JgbAY43C.mjs.map → rollback-DbP0KuDw.mjs.map} +1 -1
  163. package/dist/{runtime-DIZTeGJV.d.cts → runtime-BFJQNqwd.d.mts} +2 -2
  164. package/dist/{runtime-DIZTeGJV.d.cts.map → runtime-BFJQNqwd.d.mts.map} +1 -1
  165. package/dist/{runtime-WMLJY-db.d.mts → runtime-BJO-FFef.d.cts} +2 -2
  166. package/dist/{runtime-WMLJY-db.d.mts.map → runtime-BJO-FFef.d.cts.map} +1 -1
  167. package/dist/sandbox/credentials-worker.cjs +2 -2
  168. package/dist/sandbox/credentials-worker.mjs +2 -2
  169. package/dist/sandbox/discover-worker.cjs +1 -1
  170. package/dist/sandbox/discover-worker.mjs +1 -1
  171. package/dist/sandbox/migrate-worker.cjs +1 -1
  172. package/dist/sandbox/migrate-worker.mjs +1 -1
  173. package/dist/{seeds-BWC-qjSI.cjs → seeds-DOfxmDRO.cjs} +360 -32
  174. package/dist/seeds-DOfxmDRO.cjs.map +1 -0
  175. package/dist/{seeds-Dv78P9LS.mjs → seeds-rG-Evfz9.mjs} +339 -29
  176. package/dist/seeds-rG-Evfz9.mjs.map +1 -0
  177. package/dist/{serverSetup-CtV80a5M.cjs → serverSetup-CWWVcU6Y.cjs} +5 -5
  178. package/dist/{serverSetup-CtV80a5M.cjs.map → serverSetup-CWWVcU6Y.cjs.map} +1 -1
  179. package/dist/{serverSetup-DB6sKwAy.mjs → serverSetup-xGw1fxuZ.mjs} +5 -5
  180. package/dist/{serverSetup-DB6sKwAy.mjs.map → serverSetup-xGw1fxuZ.mjs.map} +1 -1
  181. package/dist/{sniffer-ctpwBBkT.cjs → sniffer-C7O9Pna_.cjs} +8 -8
  182. package/dist/{sniffer-ctpwBBkT.cjs.map → sniffer-C7O9Pna_.cjs.map} +1 -1
  183. package/dist/{sniffer-qDFUQmWP.mjs → sniffer-DVPgfyHr.mjs} +8 -8
  184. package/dist/{sniffer-qDFUQmWP.mjs.map → sniffer-DVPgfyHr.mjs.map} +1 -1
  185. package/dist/{dns-Bmnuttja.cjs → snsSubscriptions-CSkteK2J.cjs} +338 -2
  186. package/dist/snsSubscriptions-CSkteK2J.cjs.map +1 -0
  187. package/dist/{dns-DFvO5a53.mjs → snsSubscriptions-CvBIGGp0.mjs} +279 -3
  188. package/dist/snsSubscriptions-CvBIGGp0.mjs.map +1 -0
  189. package/dist/{sst-C1pxbFEd.mjs → sst-CTqZZl1_.mjs} +3 -4
  190. package/dist/{sst-C1pxbFEd.mjs.map → sst-CTqZZl1_.mjs.map} +1 -1
  191. package/dist/{sst-B-Uz4cvm.cjs → sst-lEkBwzGc.cjs} +4 -5
  192. package/dist/{sst-B-Uz4cvm.cjs.map → sst-lEkBwzGc.cjs.map} +1 -1
  193. package/dist/{stage-C7lf36az.mjs → stage-2EKpzPQ1.mjs} +5 -5
  194. package/dist/{stage-C7lf36az.mjs.map → stage-2EKpzPQ1.mjs.map} +1 -1
  195. package/dist/{stage-DzALYUwY.cjs → stage-fRfFtLwj.cjs} +6 -6
  196. package/dist/{stage-DzALYUwY.cjs.map → stage-fRfFtLwj.cjs.map} +1 -1
  197. package/dist/target/public.cjs +1 -1
  198. package/dist/target/public.d.cts +2 -2
  199. package/dist/target/public.d.mts +2 -2
  200. package/dist/target/public.mjs +1 -1
  201. package/dist/{terminal-CW_dWeTe.mjs → terminal-BAVrDhhN.mjs} +3 -3
  202. package/dist/{terminal-CW_dWeTe.mjs.map → terminal-BAVrDhhN.mjs.map} +1 -1
  203. package/dist/{terminal-DJlqcW_U.cjs → terminal-D1aeTkgv.cjs} +3 -3
  204. package/dist/{terminal-DJlqcW_U.cjs.map → terminal-D1aeTkgv.cjs.map} +1 -1
  205. package/dist/{types-C8gXOLWL.d.mts → types-CFU2uvrz.d.cts} +242 -3
  206. package/dist/types-CFU2uvrz.d.cts.map +1 -0
  207. package/dist/{types-Q2SC_ly2.d.cts → types-D3BeGucy.d.mts} +241 -4
  208. package/dist/types-D3BeGucy.d.mts.map +1 -0
  209. package/dist/vitest.cjs +1 -1
  210. package/dist/vitest.mjs +1 -1
  211. package/dist/workspace/index.d.cts +2 -2
  212. package/dist/workspace/index.d.mts +2 -2
  213. package/package.json +15 -15
  214. package/dist/SubscriberGenerator-C8CPxrCK.cjs.map +0 -1
  215. package/dist/SubscriberGenerator-y3pncae_.mjs.map +0 -1
  216. package/dist/compose-ChVvb3xM.cjs.map +0 -1
  217. package/dist/compose-cHkF---f.mjs.map +0 -1
  218. package/dist/config-B3iz9BtK.d.cts.map +0 -1
  219. package/dist/config-DrOX29sT.d.mts.map +0 -1
  220. package/dist/credentials-B5N5yKk6.mjs.map +0 -1
  221. package/dist/credentials-Zqr3QQYB.cjs.map +0 -1
  222. package/dist/deploy-BffR_uEN.cjs.map +0 -1
  223. package/dist/deploy-DO4djCHb.mjs.map +0 -1
  224. package/dist/discover-C08meHhu.d.mts +0 -125
  225. package/dist/discover-C08meHhu.d.mts.map +0 -1
  226. package/dist/discover-C5roJuc9.mjs.map +0 -1
  227. package/dist/discover-DipvfWpK.d.cts +0 -125
  228. package/dist/discover-DipvfWpK.d.cts.map +0 -1
  229. package/dist/discover-DozUKs8f.cjs.map +0 -1
  230. package/dist/dns-Bmnuttja.cjs.map +0 -1
  231. package/dist/dns-DFvO5a53.mjs.map +0 -1
  232. package/dist/emit-C1e8qD08.mjs.map +0 -1
  233. package/dist/emit-Dmc1nvdS.cjs.map +0 -1
  234. package/dist/github-9RlA7EjN.mjs.map +0 -1
  235. package/dist/github-D56PSK39.cjs.map +0 -1
  236. package/dist/run-BJLIerMM.mjs +0 -133
  237. package/dist/run-BJLIerMM.mjs.map +0 -1
  238. package/dist/run-fVZ-rLnc.cjs +0 -174
  239. package/dist/run-fVZ-rLnc.cjs.map +0 -1
  240. package/dist/sandbox-Co1y2CmF.d.mts +0 -104
  241. package/dist/sandbox-Co1y2CmF.d.mts.map +0 -1
  242. package/dist/sandbox-DvNqr6od.d.cts +0 -104
  243. package/dist/sandbox-DvNqr6od.d.cts.map +0 -1
  244. package/dist/seeds-BWC-qjSI.cjs.map +0 -1
  245. package/dist/seeds-Dv78P9LS.mjs.map +0 -1
  246. package/dist/types-C8gXOLWL.d.mts.map +0 -1
  247. package/dist/types-Q2SC_ly2.d.cts.map +0 -1
package/dist/openapi.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  import { o as appConstructGlobs } from "./workspace-DUnnVjqC.mjs";
3
3
  import { t as appKey } from "./derive-B4vDpzgP.mjs";
4
- import { c as parseTelemetryFlag, t as EndpointGenerator, u as telemetryArgs } from "./EndpointGenerator-CP_z_E3e.mjs";
4
+ import { c as parseTelemetryFlag, t as EndpointGenerator, u as telemetryArgs } from "./EndpointGenerator-Z_BUTocT.mjs";
5
5
  import { loadWorkspaceConfig } from "./config.mjs";
6
- import { t as OpenApiTsGenerator } from "./OpenApiTsGenerator-CsrukCE-.mjs";
6
+ import { t as OpenApiTsGenerator } from "./OpenApiTsGenerator-MtnMQL_g.mjs";
7
7
  import { dirname, join, relative, resolve } from "node:path";
8
8
  import { mkdir, writeFile } from "node:fs/promises";
9
9
  import { spawn } from "node:child_process";
@@ -1,5 +1,5 @@
1
1
  import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
2
- import { easCli, easSync } from "./eas-CBShP20v.mjs";
2
+ import { easCli, easSync } from "./eas-1Ee6kql0.mjs";
3
3
  import { join } from "node:path";
4
4
  import { existsSync, readFileSync } from "node:fs";
5
5
  import { spawnSync } from "node:child_process";
@@ -120,4 +120,4 @@ function commitSubject(root, stage) {
120
120
  //#endregion
121
121
  export { PublishModeConflict, publish };
122
122
 
123
- //# sourceMappingURL=publish-CuQlOW5p.mjs.map
123
+ //# sourceMappingURL=publish-BISBrrqR.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"publish-CuQlOW5p.mjs","names":[],"sources":["../src/mobile/publish.ts"],"sourcesContent":["/**\n * `gkm publish --stage <stage>`: a mobile app shipped for a deployed stage.\n *\n * First the stage's EAS project is synced (`gkm eas:sync`), so what is built\n * reads the stage's values. Then, per platform, the cheapest release that\n * gets the app's current code to the stage's users:\n *\n * - **An update**, when a store build with the same native code is already\n * out: the fingerprint `runtimeVersion: { policy: 'fingerprint' }` gives a\n * build is computed with the stage's profile, and a finished store build\n * of that profile with that fingerprint means only JavaScript changed. It\n * goes out on the stage's channel (`eas update`), which only builds of\n * that runtime receive.\n * - **A store build**, otherwise, submitted to the store as the stage's\n * submit profile says (`eas build --auto-submit`): Android to its Play\n * track; iOS to App Store Connect once `submit.<stage>.ios.ascAppId` is\n * set — without it, iOS is built and not submitted, and gkm says so,\n * rather than leave a prompt nobody in CI can answer.\n *\n * `--build` or `--update` decides instead. An update with no store build to\n * receive it reaches nobody, so `--update` without one is refused.\n */\n\nimport { spawnSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { GkmError } from '../errors';\nimport {\n\ttype EasCli,\n\ttype EasPlatform,\n\ttype EasSyncOptions,\n\teasCli,\n\teasSync,\n} from './eas.js';\n\nconst logger = console;\n\n/** `--update`, and no store build has this native code. */\nexport class NoStoreBuildForUpdate extends GkmError {\n\tconstructor(\n\t\treadonly platform: EasPlatform,\n\t\treadonly stage: string,\n\t\treadonly fingerprint: string,\n\t) {\n\t\tsuper(\n\t\t\t`No ${platform} store build of '${stage}' has this native code ` +\n\t\t\t\t`(fingerprint ${fingerprint}), so an update would reach nobody: a phone ` +\n\t\t\t\t'only takes updates for the runtime it was built with. Build one: ' +\n\t\t\t\t`gkm publish --stage ${stage} --platform ${platform} --build.`,\n\t\t);\n\t\tthis.name = 'NoStoreBuildForUpdate';\n\t}\n}\n\n/** `--build` and `--update` together. */\nexport class PublishModeConflict extends GkmError {\n\tconstructor() {\n\t\tsuper('Pass --build or --update, not both: one release per platform.');\n\t\tthis.name = 'PublishModeConflict';\n\t}\n}\n\nexport interface PublishOptions extends EasSyncOptions {\n\t/** `ios`, `android`, or both (the default). */\n\tplatform?: EasPlatform | 'all';\n\t/** Build, or update, whatever the fingerprint says. */\n\tmode?: 'build' | 'update';\n\t/** Wait for a build to finish (and submit), rather than leave it on EAS. */\n\twait?: boolean;\n\t/** The update's message: the commit's subject by default. */\n\tmessage?: string;\n}\n\n/** What one platform's release was — or, dry, would be. */\nexport interface PlatformRelease {\n\tplatform: EasPlatform;\n\tfingerprint: string;\n\t/** The store build that already has this native code, if one does. */\n\tstoreBuild?: string;\n\trelease: 'build' | 'update';\n\t/** Whether a build is submitted to its store. */\n\tsubmit: boolean;\n}\n\n/**\n * Ship a mobile app for a deployed stage.\n *\n * @throws {NoStoreBuildForUpdate} — and what `easSync` throws\n */\nexport async function publish(\n\toptions: PublishOptions,\n\teas?: EasCli,\n): Promise<PlatformRelease[]> {\n\tconst synced = await easSync(options, eas);\n\tconst cli = eas ?? easCli(synced.root, synced.values);\n\tconst { dir, stage } = synced;\n\tconst profile = stage.stage;\n\n\tconst easJson = existsSync(join(dir, 'eas.json'))\n\t\t? (JSON.parse(readFileSync(join(dir, 'eas.json'), 'utf8')) as {\n\t\t\t\tsubmit?: Record<string, { ios?: { ascAppId?: string } }>;\n\t\t\t})\n\t\t: {};\n\tconst iosSubmits = Boolean(easJson.submit?.[profile]?.ios?.ascAppId);\n\n\tconst platforms: EasPlatform[] =\n\t\t!options.platform || options.platform === 'all'\n\t\t\t? ['ios', 'android']\n\t\t\t: [options.platform];\n\n\tconst releases: PlatformRelease[] = [];\n\tfor (const platform of platforms) {\n\t\tconst fingerprint = await cli.fingerprint(dir, { platform, profile });\n\t\tconst storeBuild =\n\t\t\toptions.mode === 'build'\n\t\t\t\t? undefined\n\t\t\t\t: await cli.storeBuild(dir, { platform, profile, fingerprint });\n\t\tif (options.mode === 'update' && !storeBuild) {\n\t\t\tthrow new NoStoreBuildForUpdate(platform, profile, fingerprint);\n\t\t}\n\t\tconst release = options.mode ?? (storeBuild ? 'update' : 'build');\n\t\tconst submit = platform === 'android' || iosSubmits;\n\t\treleases.push({\n\t\t\tplatform,\n\t\t\tfingerprint,\n\t\t\t...(storeBuild ? { storeBuild } : {}),\n\t\t\trelease,\n\t\t\tsubmit: release === 'build' && submit,\n\t\t});\n\n\t\tconst why = storeBuild\n\t\t\t? `store build ${storeBuild} has this native code`\n\t\t\t: 'no store build has this native code';\n\t\tif (release === 'update') {\n\t\t\tlogger.log(\n\t\t\t\t`🔁 ${platform}: an update on '${stage.channel}' (${why})${options.dryRun ? ' (dry run)' : ''}`,\n\t\t\t);\n\t\t\tif (options.dryRun) continue;\n\t\t\tawait cli.update(dir, {\n\t\t\t\tplatform,\n\t\t\t\tchannel: stage.channel,\n\t\t\t\tenvironment: stage.environment,\n\t\t\t\tmessage: options.message ?? commitSubject(synced.root, profile),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst where =\n\t\t\tplatform === 'android'\n\t\t\t\t? `submitted to Play's ${stage.android.track} track (${stage.android.releaseStatus})`\n\t\t\t\t: iosSubmits\n\t\t\t\t\t? 'submitted to App Store Connect'\n\t\t\t\t\t: `not submitted: submit.${profile}.ios.ascAppId is not set in eas.json`;\n\t\tlogger.log(\n\t\t\t`📦 ${platform}: a store build of '${profile}', ${where} (${why})${options.dryRun ? ' (dry run)' : ''}`,\n\t\t);\n\t\tif (options.dryRun) continue;\n\t\tawait cli.build(dir, {\n\t\t\tplatform,\n\t\t\tprofile,\n\t\t\tsubmit,\n\t\t\twait: options.wait ?? false,\n\t\t});\n\t}\n\treturn releases;\n}\n\n/** The current commit's subject, for an update's message. */\nfunction commitSubject(root: string, stage: string): string {\n\tconst result = spawnSync('git', ['log', '-1', '--format=%s'], {\n\t\tcwd: root,\n\t\tencoding: 'utf8',\n\t});\n\tconst subject = result.status === 0 ? result.stdout.trim() : '';\n\treturn subject || `gkm publish --stage ${stage}`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAM,SAAS;;AAGf,IAAa,wBAAb,cAA2C,SAAS;CAEzC;CACA;CACA;CAHV,YACC,UACA,OACA,aACC;EACD,MACC,MAAM,SAAS,mBAAmB,MAAM,sCACvB,YAAY,mIAEL,MAAM,cAAc,SAAS,UACtD;EATS,KAAA,WAAA;EACA,KAAA,QAAA;EACA,KAAA,cAAA;EAQT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,sBAAb,cAAyC,SAAS;CACjD,cAAc;EACb,MAAM,+DAA+D;EACrE,KAAK,OAAO;CACb;AACD;;;;;;AA6BA,eAAsB,QACrB,SACA,KAC6B;CAC7B,MAAM,SAAS,MAAM,QAAQ,SAAS,GAAG;CACzC,MAAM,MAAM,OAAO,OAAO,OAAO,MAAM,OAAO,MAAM;CACpD,MAAM,EAAE,KAAK,UAAU;CACvB,MAAM,UAAU,MAAM;CAEtB,MAAM,UAAU,WAAW,KAAK,KAAK,UAAU,CAAC,IAC5C,KAAK,MAAM,aAAa,KAAK,KAAK,UAAU,GAAG,MAAM,CAAC,IAGvD,CAAC;CACJ,MAAM,aAAa,QAAQ,QAAQ,SAAS,QAAQ,EAAE,KAAK,QAAQ;CAEnE,MAAM,YACL,CAAC,QAAQ,YAAY,QAAQ,aAAa,QACvC,CAAC,OAAO,SAAS,IACjB,CAAC,QAAQ,QAAQ;CAErB,MAAM,WAA8B,CAAC;CACrC,KAAK,MAAM,YAAY,WAAW;EACjC,MAAM,cAAc,MAAM,IAAI,YAAY,KAAK;GAAE;GAAU;EAAQ,CAAC;EACpE,MAAM,aACL,QAAQ,SAAS,UACd,KAAA,IACA,MAAM,IAAI,WAAW,KAAK;GAAE;GAAU;GAAS;EAAY,CAAC;EAChE,IAAI,QAAQ,SAAS,YAAY,CAAC,YACjC,MAAM,IAAI,sBAAsB,UAAU,SAAS,WAAW;EAE/D,MAAM,UAAU,QAAQ,SAAS,aAAa,WAAW;EACzD,MAAM,SAAS,aAAa,aAAa;EACzC,SAAS,KAAK;GACb;GACA;GACA,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;GACnC;GACA,QAAQ,YAAY,WAAW;EAChC,CAAC;EAED,MAAM,MAAM,aACT,eAAe,WAAW,yBAC1B;EACH,IAAI,YAAY,UAAU;GACzB,OAAO,IACN,MAAM,SAAS,kBAAkB,MAAM,QAAQ,KAAK,IAAI,GAAG,QAAQ,SAAS,eAAe,IAC5F;GACA,IAAI,QAAQ,QAAQ;GACpB,MAAM,IAAI,OAAO,KAAK;IACrB;IACA,SAAS,MAAM;IACf,aAAa,MAAM;IACnB,SAAS,QAAQ,WAAW,cAAc,OAAO,MAAM,OAAO;GAC/D,CAAC;GACD;EACD;EAEA,MAAM,QACL,aAAa,YACV,uBAAuB,MAAM,QAAQ,MAAM,UAAU,MAAM,QAAQ,cAAc,KACjF,aACC,mCACA,yBAAyB,QAAQ;EACtC,OAAO,IACN,MAAM,SAAS,sBAAsB,QAAQ,KAAK,MAAM,IAAI,IAAI,GAAG,QAAQ,SAAS,eAAe,IACpG;EACA,IAAI,QAAQ,QAAQ;EACpB,MAAM,IAAI,MAAM,KAAK;GACpB;GACA;GACA;GACA,MAAM,QAAQ,QAAQ;EACvB,CAAC;CACF;CACA,OAAO;AACR;;AAGA,SAAS,cAAc,MAAc,OAAuB;CAC3D,MAAM,SAAS,UAAU,OAAO;EAAC;EAAO;EAAM;CAAa,GAAG;EAC7D,KAAK;EACL,UAAU;CACX,CAAC;CAED,QADgB,OAAO,WAAW,IAAI,OAAO,OAAO,KAAK,IAAI,OAC3C,uBAAuB;AAC1C"}
1
+ {"version":3,"file":"publish-BISBrrqR.mjs","names":[],"sources":["../src/mobile/publish.ts"],"sourcesContent":["/**\n * `gkm publish --stage <stage>`: a mobile app shipped for a deployed stage.\n *\n * First the stage's EAS project is synced (`gkm eas:sync`), so what is built\n * reads the stage's values. Then, per platform, the cheapest release that\n * gets the app's current code to the stage's users:\n *\n * - **An update**, when a store build with the same native code is already\n * out: the fingerprint `runtimeVersion: { policy: 'fingerprint' }` gives a\n * build is computed with the stage's profile, and a finished store build\n * of that profile with that fingerprint means only JavaScript changed. It\n * goes out on the stage's channel (`eas update`), which only builds of\n * that runtime receive.\n * - **A store build**, otherwise, submitted to the store as the stage's\n * submit profile says (`eas build --auto-submit`): Android to its Play\n * track; iOS to App Store Connect once `submit.<stage>.ios.ascAppId` is\n * set — without it, iOS is built and not submitted, and gkm says so,\n * rather than leave a prompt nobody in CI can answer.\n *\n * `--build` or `--update` decides instead. An update with no store build to\n * receive it reaches nobody, so `--update` without one is refused.\n */\n\nimport { spawnSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { GkmError } from '../errors';\nimport {\n\ttype EasCli,\n\ttype EasPlatform,\n\ttype EasSyncOptions,\n\teasCli,\n\teasSync,\n} from './eas.js';\n\nconst logger = console;\n\n/** `--update`, and no store build has this native code. */\nexport class NoStoreBuildForUpdate extends GkmError {\n\tconstructor(\n\t\treadonly platform: EasPlatform,\n\t\treadonly stage: string,\n\t\treadonly fingerprint: string,\n\t) {\n\t\tsuper(\n\t\t\t`No ${platform} store build of '${stage}' has this native code ` +\n\t\t\t\t`(fingerprint ${fingerprint}), so an update would reach nobody: a phone ` +\n\t\t\t\t'only takes updates for the runtime it was built with. Build one: ' +\n\t\t\t\t`gkm publish --stage ${stage} --platform ${platform} --build.`,\n\t\t);\n\t\tthis.name = 'NoStoreBuildForUpdate';\n\t}\n}\n\n/** `--build` and `--update` together. */\nexport class PublishModeConflict extends GkmError {\n\tconstructor() {\n\t\tsuper('Pass --build or --update, not both: one release per platform.');\n\t\tthis.name = 'PublishModeConflict';\n\t}\n}\n\nexport interface PublishOptions extends EasSyncOptions {\n\t/** `ios`, `android`, or both (the default). */\n\tplatform?: EasPlatform | 'all';\n\t/** Build, or update, whatever the fingerprint says. */\n\tmode?: 'build' | 'update';\n\t/** Wait for a build to finish (and submit), rather than leave it on EAS. */\n\twait?: boolean;\n\t/** The update's message: the commit's subject by default. */\n\tmessage?: string;\n}\n\n/** What one platform's release was — or, dry, would be. */\nexport interface PlatformRelease {\n\tplatform: EasPlatform;\n\tfingerprint: string;\n\t/** The store build that already has this native code, if one does. */\n\tstoreBuild?: string;\n\trelease: 'build' | 'update';\n\t/** Whether a build is submitted to its store. */\n\tsubmit: boolean;\n}\n\n/**\n * Ship a mobile app for a deployed stage.\n *\n * @throws {NoStoreBuildForUpdate} — and what `easSync` throws\n */\nexport async function publish(\n\toptions: PublishOptions,\n\teas?: EasCli,\n): Promise<PlatformRelease[]> {\n\tconst synced = await easSync(options, eas);\n\tconst cli = eas ?? easCli(synced.root, synced.values);\n\tconst { dir, stage } = synced;\n\tconst profile = stage.stage;\n\n\tconst easJson = existsSync(join(dir, 'eas.json'))\n\t\t? (JSON.parse(readFileSync(join(dir, 'eas.json'), 'utf8')) as {\n\t\t\t\tsubmit?: Record<string, { ios?: { ascAppId?: string } }>;\n\t\t\t})\n\t\t: {};\n\tconst iosSubmits = Boolean(easJson.submit?.[profile]?.ios?.ascAppId);\n\n\tconst platforms: EasPlatform[] =\n\t\t!options.platform || options.platform === 'all'\n\t\t\t? ['ios', 'android']\n\t\t\t: [options.platform];\n\n\tconst releases: PlatformRelease[] = [];\n\tfor (const platform of platforms) {\n\t\tconst fingerprint = await cli.fingerprint(dir, { platform, profile });\n\t\tconst storeBuild =\n\t\t\toptions.mode === 'build'\n\t\t\t\t? undefined\n\t\t\t\t: await cli.storeBuild(dir, { platform, profile, fingerprint });\n\t\tif (options.mode === 'update' && !storeBuild) {\n\t\t\tthrow new NoStoreBuildForUpdate(platform, profile, fingerprint);\n\t\t}\n\t\tconst release = options.mode ?? (storeBuild ? 'update' : 'build');\n\t\tconst submit = platform === 'android' || iosSubmits;\n\t\treleases.push({\n\t\t\tplatform,\n\t\t\tfingerprint,\n\t\t\t...(storeBuild ? { storeBuild } : {}),\n\t\t\trelease,\n\t\t\tsubmit: release === 'build' && submit,\n\t\t});\n\n\t\tconst why = storeBuild\n\t\t\t? `store build ${storeBuild} has this native code`\n\t\t\t: 'no store build has this native code';\n\t\tif (release === 'update') {\n\t\t\tlogger.log(\n\t\t\t\t`🔁 ${platform}: an update on '${stage.channel}' (${why})${options.dryRun ? ' (dry run)' : ''}`,\n\t\t\t);\n\t\t\tif (options.dryRun) continue;\n\t\t\tawait cli.update(dir, {\n\t\t\t\tplatform,\n\t\t\t\tchannel: stage.channel,\n\t\t\t\tenvironment: stage.environment,\n\t\t\t\tmessage: options.message ?? commitSubject(synced.root, profile),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst where =\n\t\t\tplatform === 'android'\n\t\t\t\t? `submitted to Play's ${stage.android.track} track (${stage.android.releaseStatus})`\n\t\t\t\t: iosSubmits\n\t\t\t\t\t? 'submitted to App Store Connect'\n\t\t\t\t\t: `not submitted: submit.${profile}.ios.ascAppId is not set in eas.json`;\n\t\tlogger.log(\n\t\t\t`📦 ${platform}: a store build of '${profile}', ${where} (${why})${options.dryRun ? ' (dry run)' : ''}`,\n\t\t);\n\t\tif (options.dryRun) continue;\n\t\tawait cli.build(dir, {\n\t\t\tplatform,\n\t\t\tprofile,\n\t\t\tsubmit,\n\t\t\twait: options.wait ?? false,\n\t\t});\n\t}\n\treturn releases;\n}\n\n/** The current commit's subject, for an update's message. */\nfunction commitSubject(root: string, stage: string): string {\n\tconst result = spawnSync('git', ['log', '-1', '--format=%s'], {\n\t\tcwd: root,\n\t\tencoding: 'utf8',\n\t});\n\tconst subject = result.status === 0 ? result.stdout.trim() : '';\n\treturn subject || `gkm publish --stage ${stage}`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAM,SAAS;;AAGf,IAAa,wBAAb,cAA2C,SAAS;CAEzC;CACA;CACA;CAHV,YACC,UACA,OACA,aACC;EACD,MACC,MAAM,SAAS,mBAAmB,MAAM,sCACvB,YAAY,mIAEL,MAAM,cAAc,SAAS,UACtD;EATS,KAAA,WAAA;EACA,KAAA,QAAA;EACA,KAAA,cAAA;EAQT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,sBAAb,cAAyC,SAAS;CACjD,cAAc;EACb,MAAM,+DAA+D;EACrE,KAAK,OAAO;CACb;AACD;;;;;;AA6BA,eAAsB,QACrB,SACA,KAC6B;CAC7B,MAAM,SAAS,MAAM,QAAQ,SAAS,GAAG;CACzC,MAAM,MAAM,OAAO,OAAO,OAAO,MAAM,OAAO,MAAM;CACpD,MAAM,EAAE,KAAK,UAAU;CACvB,MAAM,UAAU,MAAM;CAEtB,MAAM,UAAU,WAAW,KAAK,KAAK,UAAU,CAAC,IAC5C,KAAK,MAAM,aAAa,KAAK,KAAK,UAAU,GAAG,MAAM,CAAC,IAGvD,CAAC;CACJ,MAAM,aAAa,QAAQ,QAAQ,SAAS,QAAQ,EAAE,KAAK,QAAQ;CAEnE,MAAM,YACL,CAAC,QAAQ,YAAY,QAAQ,aAAa,QACvC,CAAC,OAAO,SAAS,IACjB,CAAC,QAAQ,QAAQ;CAErB,MAAM,WAA8B,CAAC;CACrC,KAAK,MAAM,YAAY,WAAW;EACjC,MAAM,cAAc,MAAM,IAAI,YAAY,KAAK;GAAE;GAAU;EAAQ,CAAC;EACpE,MAAM,aACL,QAAQ,SAAS,UACd,KAAA,IACA,MAAM,IAAI,WAAW,KAAK;GAAE;GAAU;GAAS;EAAY,CAAC;EAChE,IAAI,QAAQ,SAAS,YAAY,CAAC,YACjC,MAAM,IAAI,sBAAsB,UAAU,SAAS,WAAW;EAE/D,MAAM,UAAU,QAAQ,SAAS,aAAa,WAAW;EACzD,MAAM,SAAS,aAAa,aAAa;EACzC,SAAS,KAAK;GACb;GACA;GACA,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;GACnC;GACA,QAAQ,YAAY,WAAW;EAChC,CAAC;EAED,MAAM,MAAM,aACT,eAAe,WAAW,yBAC1B;EACH,IAAI,YAAY,UAAU;GACzB,OAAO,IACN,MAAM,SAAS,kBAAkB,MAAM,QAAQ,KAAK,IAAI,GAAG,QAAQ,SAAS,eAAe,IAC5F;GACA,IAAI,QAAQ,QAAQ;GACpB,MAAM,IAAI,OAAO,KAAK;IACrB;IACA,SAAS,MAAM;IACf,aAAa,MAAM;IACnB,SAAS,QAAQ,WAAW,cAAc,OAAO,MAAM,OAAO;GAC/D,CAAC;GACD;EACD;EAEA,MAAM,QACL,aAAa,YACV,uBAAuB,MAAM,QAAQ,MAAM,UAAU,MAAM,QAAQ,cAAc,KACjF,aACC,mCACA,yBAAyB,QAAQ;EACtC,OAAO,IACN,MAAM,SAAS,sBAAsB,QAAQ,KAAK,MAAM,IAAI,IAAI,GAAG,QAAQ,SAAS,eAAe,IACpG;EACA,IAAI,QAAQ,QAAQ;EACpB,MAAM,IAAI,MAAM,KAAK;GACpB;GACA;GACA;GACA,MAAM,QAAQ,QAAQ;EACvB,CAAC;CACF;CACA,OAAO;AACR;;AAGA,SAAS,cAAc,MAAc,OAAuB;CAC3D,MAAM,SAAS,UAAU,OAAO;EAAC;EAAO;EAAM;CAAa,GAAG;EAC7D,KAAK;EACL,UAAU;CACX,CAAC;CAED,QADgB,OAAO,WAAW,IAAI,OAAO,OAAO,KAAK,IAAI,OAC3C,uBAAuB;AAC1C"}
@@ -1,5 +1,5 @@
1
1
  const require_errors = require("./errors-DMC4Umsb.cjs");
2
- const require_eas = require("./eas-CN_cUXth.cjs");
2
+ const require_eas = require("./eas-CJoys9yh.cjs");
3
3
  let node_path = require("node:path");
4
4
  let node_fs = require("node:fs");
5
5
  let node_child_process = require("node:child_process");
@@ -121,4 +121,4 @@ function commitSubject(root, stage) {
121
121
  exports.PublishModeConflict = PublishModeConflict;
122
122
  exports.publish = publish;
123
123
 
124
- //# sourceMappingURL=publish-qSpnvmdX.cjs.map
124
+ //# sourceMappingURL=publish-DtSIB-hZ.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"publish-qSpnvmdX.cjs","names":["GkmError","easSync","easCli","existsSync","join","readFileSync","spawnSync"],"sources":["../src/mobile/publish.ts"],"sourcesContent":["/**\n * `gkm publish --stage <stage>`: a mobile app shipped for a deployed stage.\n *\n * First the stage's EAS project is synced (`gkm eas:sync`), so what is built\n * reads the stage's values. Then, per platform, the cheapest release that\n * gets the app's current code to the stage's users:\n *\n * - **An update**, when a store build with the same native code is already\n * out: the fingerprint `runtimeVersion: { policy: 'fingerprint' }` gives a\n * build is computed with the stage's profile, and a finished store build\n * of that profile with that fingerprint means only JavaScript changed. It\n * goes out on the stage's channel (`eas update`), which only builds of\n * that runtime receive.\n * - **A store build**, otherwise, submitted to the store as the stage's\n * submit profile says (`eas build --auto-submit`): Android to its Play\n * track; iOS to App Store Connect once `submit.<stage>.ios.ascAppId` is\n * set — without it, iOS is built and not submitted, and gkm says so,\n * rather than leave a prompt nobody in CI can answer.\n *\n * `--build` or `--update` decides instead. An update with no store build to\n * receive it reaches nobody, so `--update` without one is refused.\n */\n\nimport { spawnSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { GkmError } from '../errors';\nimport {\n\ttype EasCli,\n\ttype EasPlatform,\n\ttype EasSyncOptions,\n\teasCli,\n\teasSync,\n} from './eas.js';\n\nconst logger = console;\n\n/** `--update`, and no store build has this native code. */\nexport class NoStoreBuildForUpdate extends GkmError {\n\tconstructor(\n\t\treadonly platform: EasPlatform,\n\t\treadonly stage: string,\n\t\treadonly fingerprint: string,\n\t) {\n\t\tsuper(\n\t\t\t`No ${platform} store build of '${stage}' has this native code ` +\n\t\t\t\t`(fingerprint ${fingerprint}), so an update would reach nobody: a phone ` +\n\t\t\t\t'only takes updates for the runtime it was built with. Build one: ' +\n\t\t\t\t`gkm publish --stage ${stage} --platform ${platform} --build.`,\n\t\t);\n\t\tthis.name = 'NoStoreBuildForUpdate';\n\t}\n}\n\n/** `--build` and `--update` together. */\nexport class PublishModeConflict extends GkmError {\n\tconstructor() {\n\t\tsuper('Pass --build or --update, not both: one release per platform.');\n\t\tthis.name = 'PublishModeConflict';\n\t}\n}\n\nexport interface PublishOptions extends EasSyncOptions {\n\t/** `ios`, `android`, or both (the default). */\n\tplatform?: EasPlatform | 'all';\n\t/** Build, or update, whatever the fingerprint says. */\n\tmode?: 'build' | 'update';\n\t/** Wait for a build to finish (and submit), rather than leave it on EAS. */\n\twait?: boolean;\n\t/** The update's message: the commit's subject by default. */\n\tmessage?: string;\n}\n\n/** What one platform's release was — or, dry, would be. */\nexport interface PlatformRelease {\n\tplatform: EasPlatform;\n\tfingerprint: string;\n\t/** The store build that already has this native code, if one does. */\n\tstoreBuild?: string;\n\trelease: 'build' | 'update';\n\t/** Whether a build is submitted to its store. */\n\tsubmit: boolean;\n}\n\n/**\n * Ship a mobile app for a deployed stage.\n *\n * @throws {NoStoreBuildForUpdate} — and what `easSync` throws\n */\nexport async function publish(\n\toptions: PublishOptions,\n\teas?: EasCli,\n): Promise<PlatformRelease[]> {\n\tconst synced = await easSync(options, eas);\n\tconst cli = eas ?? easCli(synced.root, synced.values);\n\tconst { dir, stage } = synced;\n\tconst profile = stage.stage;\n\n\tconst easJson = existsSync(join(dir, 'eas.json'))\n\t\t? (JSON.parse(readFileSync(join(dir, 'eas.json'), 'utf8')) as {\n\t\t\t\tsubmit?: Record<string, { ios?: { ascAppId?: string } }>;\n\t\t\t})\n\t\t: {};\n\tconst iosSubmits = Boolean(easJson.submit?.[profile]?.ios?.ascAppId);\n\n\tconst platforms: EasPlatform[] =\n\t\t!options.platform || options.platform === 'all'\n\t\t\t? ['ios', 'android']\n\t\t\t: [options.platform];\n\n\tconst releases: PlatformRelease[] = [];\n\tfor (const platform of platforms) {\n\t\tconst fingerprint = await cli.fingerprint(dir, { platform, profile });\n\t\tconst storeBuild =\n\t\t\toptions.mode === 'build'\n\t\t\t\t? undefined\n\t\t\t\t: await cli.storeBuild(dir, { platform, profile, fingerprint });\n\t\tif (options.mode === 'update' && !storeBuild) {\n\t\t\tthrow new NoStoreBuildForUpdate(platform, profile, fingerprint);\n\t\t}\n\t\tconst release = options.mode ?? (storeBuild ? 'update' : 'build');\n\t\tconst submit = platform === 'android' || iosSubmits;\n\t\treleases.push({\n\t\t\tplatform,\n\t\t\tfingerprint,\n\t\t\t...(storeBuild ? { storeBuild } : {}),\n\t\t\trelease,\n\t\t\tsubmit: release === 'build' && submit,\n\t\t});\n\n\t\tconst why = storeBuild\n\t\t\t? `store build ${storeBuild} has this native code`\n\t\t\t: 'no store build has this native code';\n\t\tif (release === 'update') {\n\t\t\tlogger.log(\n\t\t\t\t`🔁 ${platform}: an update on '${stage.channel}' (${why})${options.dryRun ? ' (dry run)' : ''}`,\n\t\t\t);\n\t\t\tif (options.dryRun) continue;\n\t\t\tawait cli.update(dir, {\n\t\t\t\tplatform,\n\t\t\t\tchannel: stage.channel,\n\t\t\t\tenvironment: stage.environment,\n\t\t\t\tmessage: options.message ?? commitSubject(synced.root, profile),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst where =\n\t\t\tplatform === 'android'\n\t\t\t\t? `submitted to Play's ${stage.android.track} track (${stage.android.releaseStatus})`\n\t\t\t\t: iosSubmits\n\t\t\t\t\t? 'submitted to App Store Connect'\n\t\t\t\t\t: `not submitted: submit.${profile}.ios.ascAppId is not set in eas.json`;\n\t\tlogger.log(\n\t\t\t`📦 ${platform}: a store build of '${profile}', ${where} (${why})${options.dryRun ? ' (dry run)' : ''}`,\n\t\t);\n\t\tif (options.dryRun) continue;\n\t\tawait cli.build(dir, {\n\t\t\tplatform,\n\t\t\tprofile,\n\t\t\tsubmit,\n\t\t\twait: options.wait ?? false,\n\t\t});\n\t}\n\treturn releases;\n}\n\n/** The current commit's subject, for an update's message. */\nfunction commitSubject(root: string, stage: string): string {\n\tconst result = spawnSync('git', ['log', '-1', '--format=%s'], {\n\t\tcwd: root,\n\t\tencoding: 'utf8',\n\t});\n\tconst subject = result.status === 0 ? result.stdout.trim() : '';\n\treturn subject || `gkm publish --stage ${stage}`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAM,SAAS;;AAGf,IAAa,wBAAb,cAA2CA,eAAAA,SAAS;CAEzC;CACA;CACA;CAHV,YACC,UACA,OACA,aACC;EACD,MACC,MAAM,SAAS,mBAAmB,MAAM,sCACvB,YAAY,mIAEL,MAAM,cAAc,SAAS,UACtD;EATS,KAAA,WAAA;EACA,KAAA,QAAA;EACA,KAAA,cAAA;EAQT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,sBAAb,cAAyCA,eAAAA,SAAS;CACjD,cAAc;EACb,MAAM,+DAA+D;EACrE,KAAK,OAAO;CACb;AACD;;;;;;AA6BA,eAAsB,QACrB,SACA,KAC6B;CAC7B,MAAM,SAAS,MAAMC,YAAAA,QAAQ,SAAS,GAAG;CACzC,MAAM,MAAM,OAAOC,YAAAA,OAAO,OAAO,MAAM,OAAO,MAAM;CACpD,MAAM,EAAE,KAAK,UAAU;CACvB,MAAM,UAAU,MAAM;CAEtB,MAAM,WAAA,GAAUC,QAAAA,WAAAA,EAAAA,GAAWC,UAAAA,KAAAA,CAAK,KAAK,UAAU,CAAC,IAC5C,KAAK,OAAA,GAAMC,QAAAA,aAAAA,EAAAA,GAAaD,UAAAA,KAAAA,CAAK,KAAK,UAAU,GAAG,MAAM,CAAC,IAGvD,CAAC;CACJ,MAAM,aAAa,QAAQ,QAAQ,SAAS,QAAQ,EAAE,KAAK,QAAQ;CAEnE,MAAM,YACL,CAAC,QAAQ,YAAY,QAAQ,aAAa,QACvC,CAAC,OAAO,SAAS,IACjB,CAAC,QAAQ,QAAQ;CAErB,MAAM,WAA8B,CAAC;CACrC,KAAK,MAAM,YAAY,WAAW;EACjC,MAAM,cAAc,MAAM,IAAI,YAAY,KAAK;GAAE;GAAU;EAAQ,CAAC;EACpE,MAAM,aACL,QAAQ,SAAS,UACd,KAAA,IACA,MAAM,IAAI,WAAW,KAAK;GAAE;GAAU;GAAS;EAAY,CAAC;EAChE,IAAI,QAAQ,SAAS,YAAY,CAAC,YACjC,MAAM,IAAI,sBAAsB,UAAU,SAAS,WAAW;EAE/D,MAAM,UAAU,QAAQ,SAAS,aAAa,WAAW;EACzD,MAAM,SAAS,aAAa,aAAa;EACzC,SAAS,KAAK;GACb;GACA;GACA,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;GACnC;GACA,QAAQ,YAAY,WAAW;EAChC,CAAC;EAED,MAAM,MAAM,aACT,eAAe,WAAW,yBAC1B;EACH,IAAI,YAAY,UAAU;GACzB,OAAO,IACN,MAAM,SAAS,kBAAkB,MAAM,QAAQ,KAAK,IAAI,GAAG,QAAQ,SAAS,eAAe,IAC5F;GACA,IAAI,QAAQ,QAAQ;GACpB,MAAM,IAAI,OAAO,KAAK;IACrB;IACA,SAAS,MAAM;IACf,aAAa,MAAM;IACnB,SAAS,QAAQ,WAAW,cAAc,OAAO,MAAM,OAAO;GAC/D,CAAC;GACD;EACD;EAEA,MAAM,QACL,aAAa,YACV,uBAAuB,MAAM,QAAQ,MAAM,UAAU,MAAM,QAAQ,cAAc,KACjF,aACC,mCACA,yBAAyB,QAAQ;EACtC,OAAO,IACN,MAAM,SAAS,sBAAsB,QAAQ,KAAK,MAAM,IAAI,IAAI,GAAG,QAAQ,SAAS,eAAe,IACpG;EACA,IAAI,QAAQ,QAAQ;EACpB,MAAM,IAAI,MAAM,KAAK;GACpB;GACA;GACA;GACA,MAAM,QAAQ,QAAQ;EACvB,CAAC;CACF;CACA,OAAO;AACR;;AAGA,SAAS,cAAc,MAAc,OAAuB;CAC3D,MAAM,UAAA,GAASE,mBAAAA,UAAAA,CAAU,OAAO;EAAC;EAAO;EAAM;CAAa,GAAG;EAC7D,KAAK;EACL,UAAU;CACX,CAAC;CAED,QADgB,OAAO,WAAW,IAAI,OAAO,OAAO,KAAK,IAAI,OAC3C,uBAAuB;AAC1C"}
1
+ {"version":3,"file":"publish-DtSIB-hZ.cjs","names":["GkmError","easSync","easCli","existsSync","join","readFileSync","spawnSync"],"sources":["../src/mobile/publish.ts"],"sourcesContent":["/**\n * `gkm publish --stage <stage>`: a mobile app shipped for a deployed stage.\n *\n * First the stage's EAS project is synced (`gkm eas:sync`), so what is built\n * reads the stage's values. Then, per platform, the cheapest release that\n * gets the app's current code to the stage's users:\n *\n * - **An update**, when a store build with the same native code is already\n * out: the fingerprint `runtimeVersion: { policy: 'fingerprint' }` gives a\n * build is computed with the stage's profile, and a finished store build\n * of that profile with that fingerprint means only JavaScript changed. It\n * goes out on the stage's channel (`eas update`), which only builds of\n * that runtime receive.\n * - **A store build**, otherwise, submitted to the store as the stage's\n * submit profile says (`eas build --auto-submit`): Android to its Play\n * track; iOS to App Store Connect once `submit.<stage>.ios.ascAppId` is\n * set — without it, iOS is built and not submitted, and gkm says so,\n * rather than leave a prompt nobody in CI can answer.\n *\n * `--build` or `--update` decides instead. An update with no store build to\n * receive it reaches nobody, so `--update` without one is refused.\n */\n\nimport { spawnSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { GkmError } from '../errors';\nimport {\n\ttype EasCli,\n\ttype EasPlatform,\n\ttype EasSyncOptions,\n\teasCli,\n\teasSync,\n} from './eas.js';\n\nconst logger = console;\n\n/** `--update`, and no store build has this native code. */\nexport class NoStoreBuildForUpdate extends GkmError {\n\tconstructor(\n\t\treadonly platform: EasPlatform,\n\t\treadonly stage: string,\n\t\treadonly fingerprint: string,\n\t) {\n\t\tsuper(\n\t\t\t`No ${platform} store build of '${stage}' has this native code ` +\n\t\t\t\t`(fingerprint ${fingerprint}), so an update would reach nobody: a phone ` +\n\t\t\t\t'only takes updates for the runtime it was built with. Build one: ' +\n\t\t\t\t`gkm publish --stage ${stage} --platform ${platform} --build.`,\n\t\t);\n\t\tthis.name = 'NoStoreBuildForUpdate';\n\t}\n}\n\n/** `--build` and `--update` together. */\nexport class PublishModeConflict extends GkmError {\n\tconstructor() {\n\t\tsuper('Pass --build or --update, not both: one release per platform.');\n\t\tthis.name = 'PublishModeConflict';\n\t}\n}\n\nexport interface PublishOptions extends EasSyncOptions {\n\t/** `ios`, `android`, or both (the default). */\n\tplatform?: EasPlatform | 'all';\n\t/** Build, or update, whatever the fingerprint says. */\n\tmode?: 'build' | 'update';\n\t/** Wait for a build to finish (and submit), rather than leave it on EAS. */\n\twait?: boolean;\n\t/** The update's message: the commit's subject by default. */\n\tmessage?: string;\n}\n\n/** What one platform's release was — or, dry, would be. */\nexport interface PlatformRelease {\n\tplatform: EasPlatform;\n\tfingerprint: string;\n\t/** The store build that already has this native code, if one does. */\n\tstoreBuild?: string;\n\trelease: 'build' | 'update';\n\t/** Whether a build is submitted to its store. */\n\tsubmit: boolean;\n}\n\n/**\n * Ship a mobile app for a deployed stage.\n *\n * @throws {NoStoreBuildForUpdate} — and what `easSync` throws\n */\nexport async function publish(\n\toptions: PublishOptions,\n\teas?: EasCli,\n): Promise<PlatformRelease[]> {\n\tconst synced = await easSync(options, eas);\n\tconst cli = eas ?? easCli(synced.root, synced.values);\n\tconst { dir, stage } = synced;\n\tconst profile = stage.stage;\n\n\tconst easJson = existsSync(join(dir, 'eas.json'))\n\t\t? (JSON.parse(readFileSync(join(dir, 'eas.json'), 'utf8')) as {\n\t\t\t\tsubmit?: Record<string, { ios?: { ascAppId?: string } }>;\n\t\t\t})\n\t\t: {};\n\tconst iosSubmits = Boolean(easJson.submit?.[profile]?.ios?.ascAppId);\n\n\tconst platforms: EasPlatform[] =\n\t\t!options.platform || options.platform === 'all'\n\t\t\t? ['ios', 'android']\n\t\t\t: [options.platform];\n\n\tconst releases: PlatformRelease[] = [];\n\tfor (const platform of platforms) {\n\t\tconst fingerprint = await cli.fingerprint(dir, { platform, profile });\n\t\tconst storeBuild =\n\t\t\toptions.mode === 'build'\n\t\t\t\t? undefined\n\t\t\t\t: await cli.storeBuild(dir, { platform, profile, fingerprint });\n\t\tif (options.mode === 'update' && !storeBuild) {\n\t\t\tthrow new NoStoreBuildForUpdate(platform, profile, fingerprint);\n\t\t}\n\t\tconst release = options.mode ?? (storeBuild ? 'update' : 'build');\n\t\tconst submit = platform === 'android' || iosSubmits;\n\t\treleases.push({\n\t\t\tplatform,\n\t\t\tfingerprint,\n\t\t\t...(storeBuild ? { storeBuild } : {}),\n\t\t\trelease,\n\t\t\tsubmit: release === 'build' && submit,\n\t\t});\n\n\t\tconst why = storeBuild\n\t\t\t? `store build ${storeBuild} has this native code`\n\t\t\t: 'no store build has this native code';\n\t\tif (release === 'update') {\n\t\t\tlogger.log(\n\t\t\t\t`🔁 ${platform}: an update on '${stage.channel}' (${why})${options.dryRun ? ' (dry run)' : ''}`,\n\t\t\t);\n\t\t\tif (options.dryRun) continue;\n\t\t\tawait cli.update(dir, {\n\t\t\t\tplatform,\n\t\t\t\tchannel: stage.channel,\n\t\t\t\tenvironment: stage.environment,\n\t\t\t\tmessage: options.message ?? commitSubject(synced.root, profile),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst where =\n\t\t\tplatform === 'android'\n\t\t\t\t? `submitted to Play's ${stage.android.track} track (${stage.android.releaseStatus})`\n\t\t\t\t: iosSubmits\n\t\t\t\t\t? 'submitted to App Store Connect'\n\t\t\t\t\t: `not submitted: submit.${profile}.ios.ascAppId is not set in eas.json`;\n\t\tlogger.log(\n\t\t\t`📦 ${platform}: a store build of '${profile}', ${where} (${why})${options.dryRun ? ' (dry run)' : ''}`,\n\t\t);\n\t\tif (options.dryRun) continue;\n\t\tawait cli.build(dir, {\n\t\t\tplatform,\n\t\t\tprofile,\n\t\t\tsubmit,\n\t\t\twait: options.wait ?? false,\n\t\t});\n\t}\n\treturn releases;\n}\n\n/** The current commit's subject, for an update's message. */\nfunction commitSubject(root: string, stage: string): string {\n\tconst result = spawnSync('git', ['log', '-1', '--format=%s'], {\n\t\tcwd: root,\n\t\tencoding: 'utf8',\n\t});\n\tconst subject = result.status === 0 ? result.stdout.trim() : '';\n\treturn subject || `gkm publish --stage ${stage}`;\n}\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
2
- import { a as readFakes, t as SubscriberGenerator } from "./SubscriberGenerator-y3pncae_.mjs";
3
- import { t as EndpointGenerator } from "./EndpointGenerator-CP_z_E3e.mjs";
4
- import { i as discover } from "./discover-C5roJuc9.mjs";
5
- import { t as OpenApiTsGenerator } from "./OpenApiTsGenerator-CsrukCE-.mjs";
6
- import { n as driversFor, r as eventsBackendsIn, t as cacheBackendsIn } from "./drivers-BjGDYfnC.mjs";
2
+ import { i as readFakes, t as SubscriberGenerator } from "./SubscriberGenerator-COAVPgM-.mjs";
3
+ import { t as EndpointGenerator } from "./EndpointGenerator-Z_BUTocT.mjs";
4
+ import { i as discover } from "./discover-BvJOVsYG.mjs";
5
+ import { t as OpenApiTsGenerator } from "./OpenApiTsGenerator-MtnMQL_g.mjs";
6
+ import { n as driversFor, r as eventsBackendsIn, t as cacheBackendsIn } from "./drivers-Chb5Akov.mjs";
7
7
  import { extname, join, relative } from "node:path";
8
8
  import { mkdir, readdir, rm, writeFile } from "node:fs/promises";
9
9
  import { canonicalId, kebabCase, provideKey, serviceKey } from "@geekmidas/manifest";
@@ -372,4 +372,4 @@ const TEST_READY_FILE = ".gkm/test-ready.json";
372
372
  //#endregion
373
373
  export { writeTestHarness as a, TEST_MANIFEST_ENV as i, TEST_READY_FILE as n, DEFAULT_FACTORIES_DIR as r, TEST_READY_ENV as t };
374
374
 
375
- //# sourceMappingURL=ready-BOfogqzr.mjs.map
375
+ //# sourceMappingURL=ready-DqsyadOs.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ready-BOfogqzr.mjs","names":[],"sources":["../src/test/harness.ts","../src/test/ready.ts"],"sourcesContent":["/**\n * What `gkm test` writes for a feature test to be built from.\n *\n * `gkm test` already discovers an app's constructs and resolves the test\n * stage's environment; this keeps both, under `.gkm/test/`:\n *\n * - `manifest.json` — which construct and endpoint is exported where, and the\n * environment the test stage resolved, keyed as the constructs derive keys.\n * - `clients/<surface>.ts` — each surface's typed client, from the same\n * generator `gkm build` uses for the app's own.\n * - `index.ts` — the configured `it`, and a `Browser` holding one client per\n * surface and one per auth server, each on the browser's `fetch`.\n *\n * A test imports `it` from there and declares nothing — not a construct, not an\n * environment key, not a client.\n */\n\nimport { mkdir, readdir, rm, writeFile } from 'node:fs/promises';\nimport { extname, join, relative } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { TestStage } from '@geekmidas/constructs';\nimport type { BetterAuth } from '@geekmidas/constructs/auth';\nimport type { Endpoint } from '@geekmidas/constructs/endpoints';\nimport type { TestManifest } from '@geekmidas/constructs/testing';\nimport { EnvironmentParser } from '@geekmidas/envkit';\nimport {\n\tcanonicalId,\n\tkebabCase,\n\tprovideKey,\n\tserviceKey,\n} from '@geekmidas/manifest';\nimport { serviceContext } from '@geekmidas/services';\nimport { GkmError } from '../errors';\nimport {\n\tcacheBackendsIn,\n\tdriversFor,\n\teventsBackendsIn,\n\ttype RuntimeDrivers,\n} from '../generators/drivers.js';\nimport { EndpointGenerator } from '../generators/EndpointGenerator.js';\nimport { OpenApiTsGenerator } from '../generators/OpenApiTsGenerator.js';\nimport { SubscriberGenerator } from '../generators/SubscriberGenerator.js';\nimport { type ConstructSource, discover } from '../reconcile/discover.js';\nimport { readFakes } from '../reconcile/fakes.js';\nimport type { CacheBackend, EventsBackend } from '../types.js';\n\n/**\n * The variable the manifest's path travels in — what\n * `@geekmidas/constructs/testing` reads. Its own constant rather than an import:\n * that entry carries the whole harness (MSW, Hono, Vitest), and importing it\n * here put all of it on the start-up path of every `gkm` command.\n */\nexport const TEST_MANIFEST_ENV = 'GKM_TEST_MANIFEST';\n\n/** Where the harness lives, relative to the workspace root. */\nexport const TEST_HARNESS_DIR = '.gkm/test';\n\n/**\n * Better-auth's client plugin for each server plugin id.\n *\n * Written down rather than derived: the ids and the client names do not follow\n * one rule (`admin` pairs with `adminClient`, `email-otp` with\n * `emailOTPClient`), and a guess that missed would hand a test a client\n * silently lacking every method of that plugin. A server plugin not listed has\n * no client counterpart — `bearer`, `open-api` — or needs one from another\n * package, and is left out.\n */\nconst CLIENT_PLUGINS: Readonly<Record<string, string>> = {\n\tadmin: 'adminClient',\n\tanonymous: 'anonymousClient',\n\t'device-authorization': 'deviceAuthorizationClient',\n\t'email-otp': 'emailOTPClient',\n\tjwt: 'jwtClient',\n\t'last-login-method': 'lastLoginMethodClient',\n\t'magic-link': 'magicLinkClient',\n\t'multi-session': 'multiSessionClient',\n\t'oauth-popup': 'oauthPopupClient',\n\t'one-tap': 'oneTapClient',\n\t'one-time-token': 'oneTimeTokenClient',\n\torganization: 'organizationClient',\n\t'phone-number': 'phoneNumberClient',\n\tsiwe: 'siweClient',\n\t'two-factor': 'twoFactorClient',\n\tusername: 'usernameClient',\n};\n\nexport interface WriteTestHarnessOptions {\n\t/** The workspace root: where discovery runs from. */\n\troot: string;\n\t/**\n\t * The directories to write `.gkm/test/` into — each app's own. A package\n\t * maps its `#test` import only to a path inside itself, so each app gets its\n\t * own copy of the harness rather than one at the root it cannot reach.\n\t */\n\ttargets: string[];\n\t/** The constructs glob — the same patterns reconcile and the build use. */\n\tpatterns: string[];\n\t/** The stage the environment was resolved for: always `test`. */\n\tstage: TestStage;\n\t/** The environment the suite runs with. */\n\tenv: Record<string, string>;\n\t/**\n\t * Where a cache lives when it names nowhere — the target's default, which\n\t * the build's server entry registers a driver for.\n\t */\n\tcacheBackend: CacheBackend;\n\t/**\n\t * The broker the target puts topics and queues on, which the server entry\n\t * registers a driver for when the app declares one.\n\t */\n\teventsBackend: EventsBackend;\n\t/**\n\t * The folder of test factories, absolute: `test/factories` at the root, or\n\t * where `test.factories` in the config points. Absent or empty, a test is\n\t * handed no factories.\n\t */\n\tfactories?: string;\n}\n\n/** Where a project keeps its test factories, relative to its root. */\nexport const DEFAULT_FACTORIES_DIR = 'test/factories';\n\n/** A file in the factories folder that names no database construct. */\nexport class UnknownFactoryFile extends GkmError {\n\tconstructor(\n\t\treadonly file: string,\n\t\treadonly known: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${file} names no database construct, so no test could be handed it. ` +\n\t\t\t\t(known.length > 0\n\t\t\t\t\t? `The databases: ${known.join(', ')}. `\n\t\t\t\t\t: 'This project declares no database. ') +\n\t\t\t\t`Name a factory after its database: test/factories/<construct>.ts.`,\n\t\t);\n\t\tthis.name = 'UnknownFactoryFile';\n\t}\n}\n\n/** A factory file that exports no `createFactory`. */\nexport class FactoryHasNoCreate extends GkmError {\n\tconstructor(readonly file: string) {\n\t\tsuper(\n\t\t\t`${file} is a test factory but exports no \\`createFactory\\`. ` +\n\t\t\t\t`Export \\`function createFactory(db: Kysely<Database>)\\` returning ` +\n\t\t\t\t`the factory.`,\n\t\t);\n\t\tthis.name = 'FactoryHasNoCreate';\n\t}\n}\n\n/** One database's factory: the file, and the service name a test keys it by. */\nexport interface FactorySource {\n\tfile: string;\n\tservice: string;\n}\n\n/**\n * The factories in a folder, each matched to the database it is named after.\n *\n * A file that names no database, or exports no `createFactory`, is refused\n * rather than skipped: either is a factory no test would ever be handed.\n */\nexport async function factorySources(\n\tfolder: string | undefined,\n\tdatabases: readonly string[],\n): Promise<FactorySource[]> {\n\tif (!folder) return [];\n\n\tconst entries = await readdir(folder, { withFileTypes: true }).catch(\n\t\t(error: NodeJS.ErrnoException) => {\n\t\t\tif (error.code === 'ENOENT') return [];\n\t\t\tthrow error;\n\t\t},\n\t);\n\n\tconst found: FactorySource[] = [];\n\tfor (const entry of entries) {\n\t\tif (!entry.isFile() || !isFactoryFile(entry.name)) continue;\n\n\t\tconst file = join(folder, entry.name);\n\t\tconst id = canonicalId(entry.name.slice(0, -extname(entry.name).length));\n\t\tif (!databases.includes(id)) {\n\t\t\tthrow new UnknownFactoryFile(file, databases);\n\t\t}\n\n\t\tconst module = (await import(pathToFileURL(file).href)) as {\n\t\t\tcreateFactory?: unknown;\n\t\t};\n\t\tif (typeof module.createFactory !== 'function') {\n\t\t\tthrow new FactoryHasNoCreate(file);\n\t\t}\n\n\t\tfound.push({ file, service: serviceKey(id) });\n\t}\n\n\treturn found.sort((a, b) => (a.service < b.service ? -1 : 1));\n}\n\nfunction isFactoryFile(file: string): boolean {\n\tif (file.endsWith('.d.ts')) return false;\n\tif (/\\.(spec|test)\\.[cm]?[jt]s$/.test(file)) return false;\n\treturn ['.ts', '.mts', '.js', '.mjs'].includes(extname(file));\n}\n\n/** Write the manifest, the clients and the harness; return the manifest path. */\nexport async function writeTestHarness(\n\toptions: WriteTestHarnessOptions,\n): Promise<string> {\n\tconst sources: Record<string, ConstructSource> = {};\n\tconst declared = await discover({\n\t\tpatterns: options.patterns,\n\t\tcwd: options.root,\n\t\tsources,\n\t});\n\n\tconst loaded = await new EndpointGenerator().load(\n\t\toptions.patterns,\n\t\toptions.root,\n\t);\n\tconst endpoints = loaded\n\t\t.map(({ key, construct, path }) => ({\n\t\t\tsurface: construct.owner ?? construct.surface?.id,\n\t\t\tconstruct,\n\t\t\tsource: { file: path.absolute, export: key },\n\t\t}))\n\t\t.filter(\n\t\t\t(endpoint): endpoint is typeof endpoint & { surface: string } =>\n\t\t\t\tendpoint.surface !== undefined,\n\t\t);\n\n\t// Topic subscribers, which a test delivers to. Not constructs, so the build\n\t// finds them the way it finds endpoints.\n\tconst subscribers = (\n\t\tawait new SubscriberGenerator().load(options.patterns, options.root)\n\t).map(({ key, path }) => ({ source: { file: path.absolute, export: key } }));\n\n\tconst manifest: TestManifest = {\n\t\tstage: options.stage,\n\t\tconstructs: Object.fromEntries(\n\t\t\tObject.entries(declared).flatMap(([id, declaration]) => {\n\t\t\t\tconst source = sources[id];\n\t\t\t\treturn source\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tkind: declaration.kind,\n\t\t\t\t\t\t\t\t\tsource: { file: source.file, export: source.exportName },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t]\n\t\t\t\t\t: [];\n\t\t\t}),\n\t\t),\n\t\tendpoints: endpoints.map(({ surface, source }) => ({ surface, source })),\n\t\tsubscribers,\n\t\tenv: options.env,\n\t};\n\t// One typed client per surface, from the generator the build uses.\n\tconst surfaces = [...new Set(endpoints.map(({ surface }) => surface))];\n\tconst generator = new OpenApiTsGenerator();\n\tconst clients = await Promise.all(\n\t\tsurfaces.map(async (surface) => ({\n\t\t\tfile: `${kebabCase(surface)}.ts`,\n\t\t\tcontent: await generator.generate(\n\t\t\t\tendpoints\n\t\t\t\t\t.filter((endpoint) => endpoint.surface === surface)\n\t\t\t\t\t.map(({ construct }) => construct as Endpoint<any, any, any, any>),\n\t\t\t\t{ title: surface, version: '1.0.0' },\n\t\t\t),\n\t\t})),\n\t);\n\n\tconst auths = await authClients(declared, sources, options.env);\n\t// The drivers the server entry registers — which cache and storage clients\n\t// exist is the entry's decision, and the harness stands where the entry does.\n\tconst drivers = driversFor({\n\t\tappRoot: options.root,\n\t\tcache: cacheBackendsIn(declared, options.cacheBackend),\n\t\tevents: eventsBackendsIn(declared, options.eventsBackend),\n\t});\n\n\t// The app's own databases: not a tenant an auth server owns — reached\n\t// through that server — and not a reader of one. What a test is handed.\n\tconst owners = new Set(auths.map((auth) => auth.databaseId));\n\tconst databases = databaseSources(declared, sources).filter(\n\t\t({ id, kind }) => !owners.has(id) && kind !== 'database-reader',\n\t);\n\tconst factories = await factorySources(\n\t\toptions.factories,\n\t\tdatabases.map(({ id }) => id),\n\t);\n\t// Each external API's fake, from the convention rather than the construct —\n\t// which is what keeps a fake out of every deployed bundle.\n\tconst fakes = Object.entries(await readFakes(options.root, declared)).map(\n\t\t([id, { file }]) => ({ id, file }),\n\t);\n\t// Every construct a handler can depend on — what a test reaches through\n\t// `services.get(…)`, typed by the client its service registers. A tenant an\n\t// auth server owns is reached through that server, as databases are.\n\tconst services = serviceSources(declared, sources).filter(\n\t\t({ id }) => !owners.has(id),\n\t);\n\tconst files = [\n\t\t...new Set([\n\t\t\t...Object.values(manifest.constructs).map(({ source }) => source.file),\n\t\t\t...manifest.endpoints.map(({ source }) => source.file),\n\t\t\t// Delivery loads each subscriber too. Left to a dynamic import, it is\n\t\t\t// resolved by Node — which knows nothing of the app's tsconfig paths, so\n\t\t\t// a subscriber importing `~/…` failed every test file that delivered.\n\t\t\t...(manifest.subscribers ?? []).map(({ source }) => source.file),\n\t\t]),\n\t];\n\tconst harnessFor = (dir: string) =>\n\t\tharnessModule({\n\t\t\tdir,\n\t\t\tfiles,\n\t\t\tdatabases,\n\t\t\tsurfaces: surfaces.map((id, index) => ({\n\t\t\t\tid,\n\t\t\t\t// The client the generator writes for a surface with authorizers\n\t\t\t\t// takes a strategy per scheme; one without takes none.\n\t\t\t\tsecured: clients[index]!.content.includes('authStrategies:'),\n\t\t\t})),\n\t\t\tauths,\n\t\t\tdrivers,\n\t\t\tfactories,\n\t\t\tfakes,\n\t\t\tservices,\n\t\t\tmail: Object.values(declared).some(({ kind }) => kind === 'email'),\n\t\t});\n\tconst json = `${JSON.stringify(manifest, null, 2)}\\n`;\n\n\tconst written = await Promise.all(\n\t\t[...new Set(options.targets)].map(async (target) => {\n\t\t\tconst dir = join(target, TEST_HARNESS_DIR);\n\t\t\t// Rewritten whole on every run: a client for a surface that no longer\n\t\t\t// exists is not something a test should be able to import.\n\t\t\tawait rm(dir, { recursive: true, force: true });\n\t\t\tawait mkdir(join(dir, 'clients'), { recursive: true });\n\t\t\tawait writeFile(join(dir, 'manifest.json'), json);\n\t\t\tawait writeFile(join(dir, 'index.ts'), harnessFor(dir));\n\t\t\tfor (const client of clients) {\n\t\t\t\tawait writeFile(join(dir, 'clients', client.file), client.content);\n\t\t\t}\n\t\t\treturn join(dir, 'manifest.json');\n\t\t}),\n\t);\n\n\treturn written[0]!;\n}\n\ninterface AuthClient {\n\tid: string;\n\tbasePath: string;\n\tplugins: string[];\n\t/** The schema tenant it owns — reached through it, never by a test. */\n\tdatabaseId: string;\n}\n\n/** Each auth server, with the client plugins its server plugins pair with. */\nasync function authClients(\n\tdeclared: Awaited<ReturnType<typeof discover>>,\n\tsources: Record<string, ConstructSource>,\n\tenv: Record<string, string>,\n): Promise<AuthClient[]> {\n\tconst clients: AuthClient[] = [];\n\tconst envParser = new EnvironmentParser({ ...env });\n\n\tfor (const [id, declaration] of Object.entries(declared)) {\n\t\tconst source = sources[id];\n\t\tif (declaration.kind !== 'rest-api' || !source) continue;\n\n\t\tconst exported = (await import(source.file))[source.exportName] as unknown;\n\t\tif (!isBetterAuth(exported) || exported.id !== id) continue;\n\n\t\tconst ids = await exported.pluginIds({\n\t\t\tenvParser,\n\t\t\tcontext: serviceContext,\n\t\t});\n\t\tclients.push({\n\t\t\tid,\n\t\t\tbasePath: exported.basePath,\n\t\t\tplugins: ids.flatMap((plugin) => CLIENT_PLUGINS[plugin] ?? []),\n\t\t\tdatabaseId: exported.databaseId,\n\t\t});\n\t}\n\n\treturn clients;\n}\n\nfunction isBetterAuth(value: unknown): value is BetterAuth {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\t'pluginIds' in value &&\n\t\t'basePath' in value\n\t);\n}\n\n/** `Api` → `api`, `AdminApi` → `adminApi`: a construct id as a property name. */\nfunction propertyOf(id: string): string {\n\treturn id.charAt(0).toLowerCase() + id.slice(1);\n}\n\n/**\n * The database a test's `db` is — the one the endpoints were given with\n * `.database()`, found by the service they hold, which is the construct's own.\n * The same rule `featureTest` applies at runtime, applied here so `db` can be\n * typed by the construct's schema.\n */\n/** A database construct: its id, its service name, and where it is exported. */\ninterface DatabaseSource {\n\tid: string;\n\tkind: string;\n\tservice: string;\n\tsource: ConstructSource;\n}\n\n/**\n * Every database construct the app declares — a database, a schema tenant, a\n * reader — keyed as a test is handed its transaction.\n */\nfunction databaseSources(\n\tdeclared: Awaited<ReturnType<typeof discover>>,\n\tsources: Record<string, ConstructSource>,\n): DatabaseSource[] {\n\treturn Object.entries(declared)\n\t\t.filter(([id, declaration]) =>\n\t\t\tBoolean(sources[id] && declaration.kind.startsWith('database')),\n\t\t)\n\t\t.map(([id, declaration]) => ({\n\t\t\tid,\n\t\t\tkind: declaration.kind,\n\t\t\tservice: serviceKey(id),\n\t\t\tsource: sources[id]!,\n\t\t}));\n}\n\n/** The kinds whose construct hands a handler a client — what `services` reaches. */\nconst SERVICE_KINDS = new Set([\n\t'cache',\n\t'credential',\n\t'database',\n\t'database-schema',\n\t'database-reader',\n\t'email',\n\t'encryption',\n\t'external-api',\n\t'file-server',\n\t'objects',\n\t'queue',\n\t'secret',\n\t'topic',\n]);\n\nfunction serviceSources(\n\tdeclared: Awaited<ReturnType<typeof discover>>,\n\tsources: Record<string, ConstructSource>,\n): DatabaseSource[] {\n\treturn Object.entries(declared)\n\t\t.filter(\n\t\t\t([id, declaration]) =>\n\t\t\t\tSERVICE_KINDS.has(declaration.kind) &&\n\t\t\t\t// The construct exported *is* this node, rather than declaring it\n\t\t\t\t// on the way to something else: an auth server's signing secret is\n\t\t\t\t// its own, and nothing depends on it.\n\t\t\t\t(sources[id]?.construct as { id?: string } | undefined)?.id === id,\n\t\t)\n\t\t.map(([id, declaration]) => ({\n\t\t\tid,\n\t\t\tkind: declaration.kind,\n\t\t\tservice: serviceKey(id),\n\t\t\tsource: sources[id]!,\n\t\t}));\n}\n\n/** An import specifier for `file`, from the harness written into `dir`. */\nfunction specifierFrom(dir: string, file: string): string {\n\tconst path = relative(dir, file).replace(/\\.tsx?$/, '.js');\n\treturn path.startsWith('.') ? path : `./${path}`;\n}\n\nfunction harnessModule(options: {\n\tdir: string;\n\t/** Every module the manifest points at, by absolute path. */\n\tfiles: string[];\n\tdatabases: DatabaseSource[];\n\t/** Every construct a test reaches through `services`, by service name. */\n\tservices: DatabaseSource[];\n\tsurfaces: { id: string; secured: boolean }[];\n\tauths: AuthClient[];\n\tdrivers: RuntimeDrivers;\n\tfactories: FactorySource[];\n\t/** Each external API's fake, by construct id and the file it lives in. */\n\tfakes: { id: string; file: string }[];\n\t/** Whether the app sends mail — what a magic-link sign-in is read from. */\n\tmail: boolean;\n}): string {\n\tconst {\n\t\tsurfaces,\n\t\tauths,\n\t\tdrivers,\n\t\tdatabases,\n\t\tservices,\n\t\tdir,\n\t\tfiles,\n\t\tfactories,\n\t\tfakes,\n\t\tmail,\n\t} = options;\n\t// One auth server a test can sign in to without a person: a magic link,\n\t// read from the app's inbox. With two, which one \\`signIn\\` means is a\n\t// guess, so neither gets it.\n\tconst magicLinks = mail\n\t\t? auths.filter(({ plugins }) => plugins.includes('magicLinkClient'))\n\t\t: [];\n\tconst signIn = magicLinks.length === 1 ? magicLinks[0] : undefined;\n\tconst plugins = [...new Set(auths.flatMap(({ plugins }) => plugins))].sort();\n\n\tconst imports = [\n\t\t`import {${services.length ? ' type ClientOf,' : ''}${databases.length ? ' type DatabaseOf,' : ''} featureTest, loadTestManifest${signIn ? ', signInWithMagicLink' : ''} } from '@geekmidas/constructs/testing';`,\n\t\t// A type import per construct the types name: each database, and each\n\t\t// construct a test reaches through `services` — once, though a database\n\t\t// is both.\n\t\t...[\n\t\t\t...new Map(\n\t\t\t\t[...databases, ...services].map((construct) => [\n\t\t\t\t\tconstruct.id,\n\t\t\t\t\tconstruct,\n\t\t\t\t]),\n\t\t\t).values(),\n\t\t].map(\n\t\t\t({ id, source }) =>\n\t\t\t\t`import type { ${source.exportName} as __${id} } from '${specifierFrom(dir, source.file)}';`,\n\t\t),\n\t\t`import { Browser as TestBrowser } from '@geekmidas/testkit/browser';`,\n\t\t...surfaces.map(\n\t\t\t({ id }) =>\n\t\t\t\t`import { createApi as create${id} } from './clients/${kebabCase(id)}.js';`,\n\t\t),\n\t\t...(auths.length\n\t\t\t? [`import { createAuthClient } from 'better-auth/client';`]\n\t\t\t: []),\n\t\t...(plugins.length\n\t\t\t? [`import { ${plugins.join(', ')} } from 'better-auth/client/plugins';`]\n\t\t\t: []),\n\t\t...(drivers.imports ? [drivers.imports] : []),\n\t\t...factories.map(\n\t\t\t({ file, service }) =>\n\t\t\t\t`import { createFactory as __${service}Factory } from '${specifierFrom(dir, file)}';`,\n\t\t),\n\t\t...fakes.map(\n\t\t\t({ id, file }) =>\n\t\t\t\t`import __${id}Fake from '${specifierFrom(dir, file)}';`,\n\t\t),\n\t\t// Every construct and endpoint module, imported here — inside the app,\n\t\t// where its tsconfig paths resolve — rather than by path from the kit.\n\t\t...files.map(\n\t\t\t(file, index) =>\n\t\t\t\t`import * as __module${index} from '${specifierFrom(dir, file)}';`,\n\t\t),\n\t];\n\n\tconst url = (id: string) =>\n\t\t`manifest.env[${JSON.stringify(provideKey(id, 'url'))}]!`;\n\n\tconst members = [\n\t\t...surfaces.map(\n\t\t\t({ id, secured }) => `\t/** \\`${id}\\`, at ${provideKey(id, 'url')}. */\n\treadonly ${propertyOf(id)} = create${id}({\n\t\tbaseURL: ${url(id)},\n\t\tfetch: this.fetch,${\n\t\t\tsecured\n\t\t\t\t? `\n\t\t// Served in-process: no authorizer runs, so nothing is signed. A scheme\n\t\t// with no strategy sends its request without credentials.\n\t\tauthStrategies: {} as never,`\n\t\t\t\t: ''\n\t\t}\n\t});`,\n\t\t),\n\t\t...auths.map(\n\t\t\t(auth) => `\t/** \\`${auth.id}\\`, at ${provideKey(auth.id, 'url')}. */\n\treadonly ${propertyOf(auth.id)} = createAuthClient({\n\t\tbaseURL: \\`\\${${url(auth.id)}}${auth.basePath}\\`,\n\t\tplugins: [${auth.plugins.map((plugin) => `${plugin}()`).join(', ')}],\n\t\tfetchOptions: { customFetchImpl: this.fetch },\n\t});`,\n\t\t),\n\t\t...(signIn\n\t\t\t? [\n\t\t\t\t\t`\t/**\n\t * Sign in as \\`email\\` through \\`${signIn.id}\\`'s magic link, the way a person\n\t * does — the email opened, the link followed. The session it then reports.\n\t * Without an address, as somebody new: \\`user.email\\` says who.\n\t */\n\tsignIn(email?: string) {\n\t\treturn signInWithMagicLink(this, this.${propertyOf(signIn.id)}, email);\n\t}`,\n\t\t\t\t]\n\t\t\t: []),\n\t];\n\n\t// Each database's factory, keyed as the test is handed it.\n\tconst factoriesType = factories.length\n\t\t? `{ ${factories.map(({ service }) => `${service}: typeof __${service}Factory`).join('; ')} }`\n\t\t: '';\n\tconst factoriesOption = factories.length\n\t\t? `, factories: { ${factories.map(({ service }) => `${service}: __${service}Factory`).join(', ')} }`\n\t\t: '';\n\tconst fakesOption = fakes.length\n\t\t? `, fakes: { ${fakes.map(({ id }) => `${id}: __${id}Fake`).join(', ')} }`\n\t\t: '';\n\t// Each database's schema, keyed as the test is handed its transaction.\n\tconst databasesType = `{ ${databases.map(({ id, service }) => `${service}: DatabaseOf<typeof __${id}>`).join('; ')} }`;\n\t// Each construct's client, keyed as `services.get(…)` is called.\n\tconst servicesType = services.length\n\t\t? `{ ${services.map(({ id, service }) => `${service}: ClientOf<typeof __${id}>`).join('; ')} }`\n\t\t: '';\n\t// Positional, so a later one needs those before it, even when empty.\n\tconst generics =\n\t\tdatabases.length || factoriesType || servicesType\n\t\t\t? `<Browser, ${databases.length ? databasesType : '{}'}${\n\t\t\t\t\tfactoriesType || servicesType ? `, ${factoriesType || '{}'}` : ''\n\t\t\t\t}${servicesType ? `, ${servicesType}` : ''}>`\n\t\t\t: '';\n\n\treturn `// Generated by \\`gkm test\\` from the app's constructs — do not edit.\n${imports.join('\\n')}\n\n${drivers.setup ? `// The drivers the app's server entry registers, registered the same way.\\n${drivers.setup}\\n\\n` : ''}const manifest = loadTestManifest(new URL('./manifest.json', import.meta.url));\n\n/** A browser with a client for every surface the app declares. */\nexport class Browser extends TestBrowser {\n${members.join('\\n\\n')}\n}\n\n/** The app's modules, by the path the manifest records for each. */\nconst modules = {\n${files.map((file, index) => `\\t${JSON.stringify(file)}: __module${index},`).join('\\n')}\n};\n\n/** \\`it\\`, for a test that drives the app the way it runs deployed. */\nexport const it = featureTest${generics}({ manifest, modules, browser: Browser${factoriesOption}${fakesOption} });\n`;\n}\n","/**\n * The hand-off between `gkm test` and the Vitest global setup.\n *\n * `gkm test` reconciles and migrates the test stage before Vitest starts; the\n * setup, run inside Vitest, only has to load what that left. Started by plain\n * `vitest` instead, the setup runs `gkm test --setup` itself first — so there is\n * one way the stage gets ready, whichever started the suite.\n */\n\n/** Set on the Vitest process by `gkm test`: the stage is ready, here. */\nexport const TEST_READY_ENV = 'GKM_TEST_READY';\n\n/** Where `gkm test` records what it prepared, from where it ran. */\nexport const TEST_READY_FILE = '.gkm/test-ready.json';\n\nexport interface TestReady {\n\t/** The resolved environment, as a JSON object of strings. */\n\tenv: string;\n\t/** The test manifest the `#test` harness reads. */\n\tmanifest?: string;\n\t/**\n\t * The test stage's databases this run created, and the port of the\n\t * Postgres that holds them: what `gkm test --teardown` drops when the\n\t * suite ends — or, if a run was killed before its teardown, what the next\n\t * one drops before it starts.\n\t */\n\tdatabases?: { port: number; names: string[] };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,MAAa,oBAAoB;;AAGjC,MAAa,mBAAmB;;;;;;;;;;;AAYhC,MAAM,iBAAmD;CACxD,OAAO;CACP,WAAW;CACX,wBAAwB;CACxB,aAAa;CACb,KAAK;CACL,qBAAqB;CACrB,cAAc;CACd,iBAAiB;CACjB,eAAe;CACf,WAAW;CACX,kBAAkB;CAClB,cAAc;CACd,gBAAgB;CAChB,MAAM;CACN,cAAc;CACd,UAAU;AACX;;AAoCA,MAAa,wBAAwB;;AAGrC,IAAa,qBAAb,cAAwC,SAAS;CAEtC;CACA;CAFV,YACC,MACA,OACC;EACD,MACC,GAAG,KAAK,kEACN,MAAM,SAAS,IACb,kBAAkB,MAAM,KAAK,IAAI,EAAE,MACnC,yCACH,mEACF;EATS,KAAA,OAAA;EACA,KAAA,QAAA;EAST,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,qBAAb,cAAwC,SAAS;CAC3B;CAArB,YAAY,MAAuB;EAClC,MACC,GAAG,KAAK,oIAGT;EALoB,KAAA,OAAA;EAMpB,KAAK,OAAO;CACb;AACD;;;;;;;AAcA,eAAsB,eACrB,QACA,WAC2B;CAC3B,IAAI,CAAC,QAAQ,OAAO,CAAC;CAErB,MAAM,UAAU,MAAM,QAAQ,QAAQ,EAAE,eAAe,KAAK,CAAC,CAAC,CAAC,OAC7D,UAAiC;EACjC,IAAI,MAAM,SAAS,UAAU,OAAO,CAAC;EACrC,MAAM;CACP,CACD;CAEA,MAAM,QAAyB,CAAC;CAChC,KAAK,MAAM,SAAS,SAAS;EAC5B,IAAI,CAAC,MAAM,OAAO,KAAK,CAAC,cAAc,MAAM,IAAI,GAAG;EAEnD,MAAM,OAAO,KAAK,QAAQ,MAAM,IAAI;EACpC,MAAM,KAAK,YAAY,MAAM,KAAK,MAAM,GAAG,CAAC,QAAQ,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC;EACvE,IAAI,CAAC,UAAU,SAAS,EAAE,GACzB,MAAM,IAAI,mBAAmB,MAAM,SAAS;EAM7C,IAAI,QAAO,MAHW,OAAO,cAAc,IAAI,CAAC,CAAC,MAAA,CAG/B,kBAAkB,YACnC,MAAM,IAAI,mBAAmB,IAAI;EAGlC,MAAM,KAAK;GAAE;GAAM,SAAS,WAAW,EAAE;EAAE,CAAC;CAC7C;CAEA,OAAO,MAAM,MAAM,GAAG,MAAO,EAAE,UAAU,EAAE,UAAU,KAAK,CAAE;AAC7D;AAEA,SAAS,cAAc,MAAuB;CAC7C,IAAI,KAAK,SAAS,OAAO,GAAG,OAAO;CACnC,IAAI,6BAA6B,KAAK,IAAI,GAAG,OAAO;CACpD,OAAO;EAAC;EAAO;EAAQ;EAAO;CAAM,CAAC,CAAC,SAAS,QAAQ,IAAI,CAAC;AAC7D;;AAGA,eAAsB,iBACrB,SACkB;CAClB,MAAM,UAA2C,CAAC;CAClD,MAAM,WAAW,MAAM,SAAS;EAC/B,UAAU,QAAQ;EAClB,KAAK,QAAQ;EACb;CACD,CAAC;CAMD,MAAM,aAAY,MAJG,IAAI,kBAAkB,CAAC,CAAC,KAC5C,QAAQ,UACR,QAAQ,IACT,EAAA,CAEE,KAAK,EAAE,KAAK,WAAW,YAAY;EACnC,SAAS,UAAU,SAAS,UAAU,SAAS;EAC/C;EACA,QAAQ;GAAE,MAAM,KAAK;GAAU,QAAQ;EAAI;CAC5C,EAAE,CAAC,CACF,QACC,aACA,SAAS,YAAY,KAAA,CACvB;CAID,MAAM,eACL,MAAM,IAAI,oBAAoB,CAAC,CAAC,KAAK,QAAQ,UAAU,QAAQ,IAAI,EAAA,CAClE,KAAK,EAAE,KAAK,YAAY,EAAE,QAAQ;EAAE,MAAM,KAAK;EAAU,QAAQ;CAAI,EAAE,EAAE;CAE3E,MAAM,WAAyB;EAC9B,OAAO,QAAQ;EACf,YAAY,OAAO,YAClB,OAAO,QAAQ,QAAQ,CAAC,CAAC,SAAS,CAAC,IAAI,iBAAiB;GACvD,MAAM,SAAS,QAAQ;GACvB,OAAO,SACJ,CACA,CACC,IACA;IACC,MAAM,YAAY;IAClB,QAAQ;KAAE,MAAM,OAAO;KAAM,QAAQ,OAAO;IAAW;GACxD,CACD,CACD,IACC,CAAC;EACL,CAAC,CACF;EACA,WAAW,UAAU,KAAK,EAAE,SAAS,cAAc;GAAE;GAAS;EAAO,EAAE;EACvE;EACA,KAAK,QAAQ;CACd;CAEA,MAAM,WAAW,CAAC,GAAG,IAAI,IAAI,UAAU,KAAK,EAAE,cAAc,OAAO,CAAC,CAAC;CACrE,MAAM,YAAY,IAAI,mBAAmB;CACzC,MAAM,UAAU,MAAM,QAAQ,IAC7B,SAAS,IAAI,OAAO,aAAa;EAChC,MAAM,GAAG,UAAU,OAAO,EAAE;EAC5B,SAAS,MAAM,UAAU,SACxB,UACE,QAAQ,aAAa,SAAS,YAAY,OAAO,CAAC,CAClD,KAAK,EAAE,gBAAgB,SAAyC,GAClE;GAAE,OAAO;GAAS,SAAS;EAAQ,CACpC;CACD,EAAE,CACH;CAEA,MAAM,QAAQ,MAAM,YAAY,UAAU,SAAS,QAAQ,GAAG;CAG9D,MAAM,UAAU,WAAW;EAC1B,SAAS,QAAQ;EACjB,OAAO,gBAAgB,UAAU,QAAQ,YAAY;EACrD,QAAQ,iBAAiB,UAAU,QAAQ,aAAa;CACzD,CAAC;CAID,MAAM,SAAS,IAAI,IAAI,MAAM,KAAK,SAAS,KAAK,UAAU,CAAC;CAC3D,MAAM,YAAY,gBAAgB,UAAU,OAAO,CAAC,CAAC,QACnD,EAAE,IAAI,WAAW,CAAC,OAAO,IAAI,EAAE,KAAK,SAAS,iBAC/C;CACA,MAAM,YAAY,MAAM,eACvB,QAAQ,WACR,UAAU,KAAK,EAAE,SAAS,EAAE,CAC7B;CAGA,MAAM,QAAQ,OAAO,QAAQ,MAAM,UAAU,QAAQ,MAAM,QAAQ,CAAC,CAAC,CAAC,KACpE,CAAC,IAAI,EAAE,aAAa;EAAE;EAAI;CAAK,EACjC;CAIA,MAAM,WAAW,eAAe,UAAU,OAAO,CAAC,CAAC,QACjD,EAAE,SAAS,CAAC,OAAO,IAAI,EAAE,CAC3B;CACA,MAAM,QAAQ,CACb,mBAAG,IAAI,IAAI;EACV,GAAG,OAAO,OAAO,SAAS,UAAU,CAAC,CAAC,KAAK,EAAE,aAAa,OAAO,IAAI;EACrE,GAAG,SAAS,UAAU,KAAK,EAAE,aAAa,OAAO,IAAI;EAIrD,IAAI,SAAS,eAAe,CAAC,EAAA,CAAG,KAAK,EAAE,aAAa,OAAO,IAAI;CAChE,CAAC,CACF;CACA,MAAM,cAAc,QACnB,cAAc;EACb;EACA;EACA;EACA,UAAU,SAAS,KAAK,IAAI,WAAW;GACtC;GAGA,SAAS,QAAQ,MAAM,CAAE,QAAQ,SAAS,iBAAiB;EAC5D,EAAE;EACF;EACA;EACA;EACA;EACA;EACA,MAAM,OAAO,OAAO,QAAQ,CAAC,CAAC,MAAM,EAAE,WAAW,SAAS,OAAO;CAClE,CAAC;CACF,MAAM,OAAO,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,EAAE;CAkBlD,QAAO,MAhBe,QAAQ,IAC7B,CAAC,GAAG,IAAI,IAAI,QAAQ,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,WAAW;EACnD,MAAM,MAAM,KAAK,QAAQ,gBAAgB;EAGzC,MAAM,GAAG,KAAK;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;EAC9C,MAAM,MAAM,KAAK,KAAK,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;EACrD,MAAM,UAAU,KAAK,KAAK,eAAe,GAAG,IAAI;EAChD,MAAM,UAAU,KAAK,KAAK,UAAU,GAAG,WAAW,GAAG,CAAC;EACtD,KAAK,MAAM,UAAU,SACpB,MAAM,UAAU,KAAK,KAAK,WAAW,OAAO,IAAI,GAAG,OAAO,OAAO;EAElE,OAAO,KAAK,KAAK,eAAe;CACjC,CAAC,CACF,EAAA,CAEe;AAChB;;AAWA,eAAe,YACd,UACA,SACA,KACwB;CACxB,MAAM,UAAwB,CAAC;CAC/B,MAAM,YAAY,IAAI,kBAAkB,EAAE,GAAG,IAAI,CAAC;CAElD,KAAK,MAAM,CAAC,IAAI,gBAAgB,OAAO,QAAQ,QAAQ,GAAG;EACzD,MAAM,SAAS,QAAQ;EACvB,IAAI,YAAY,SAAS,cAAc,CAAC,QAAQ;EAEhD,MAAM,YAAY,MAAM,OAAO,OAAO,MAAA,CAAO,OAAO;EACpD,IAAI,CAAC,aAAa,QAAQ,KAAK,SAAS,OAAO,IAAI;EAEnD,MAAM,MAAM,MAAM,SAAS,UAAU;GACpC;GACA,SAAS;EACV,CAAC;EACD,QAAQ,KAAK;GACZ;GACA,UAAU,SAAS;GACnB,SAAS,IAAI,SAAS,WAAW,eAAe,WAAW,CAAC,CAAC;GAC7D,YAAY,SAAS;EACtB,CAAC;CACF;CAEA,OAAO;AACR;AAEA,SAAS,aAAa,OAAqC;CAC1D,OACC,OAAO,UAAU,YACjB,UAAU,QACV,eAAe,SACf,cAAc;AAEhB;;AAGA,SAAS,WAAW,IAAoB;CACvC,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,GAAG,MAAM,CAAC;AAC/C;;;;;AAoBA,SAAS,gBACR,UACA,SACmB;CACnB,OAAO,OAAO,QAAQ,QAAQ,CAAC,CAC7B,QAAQ,CAAC,IAAI,iBACb,QAAQ,QAAQ,OAAO,YAAY,KAAK,WAAW,UAAU,CAAC,CAC/D,CAAC,CACA,KAAK,CAAC,IAAI,kBAAkB;EAC5B;EACA,MAAM,YAAY;EAClB,SAAS,WAAW,EAAE;EACtB,QAAQ,QAAQ;CACjB,EAAE;AACJ;;AAGA,MAAM,gCAAgB,IAAI,IAAI;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;AAED,SAAS,eACR,UACA,SACmB;CACnB,OAAO,OAAO,QAAQ,QAAQ,CAAC,CAC7B,QACC,CAAC,IAAI,iBACL,cAAc,IAAI,YAAY,IAAI,MAIjC,QAAQ,GAAG,EAAE,UAAA,EAA2C,OAAO,EAClE,CAAC,CACA,KAAK,CAAC,IAAI,kBAAkB;EAC5B;EACA,MAAM,YAAY;EAClB,SAAS,WAAW,EAAE;EACtB,QAAQ,QAAQ;CACjB,EAAE;AACJ;;AAGA,SAAS,cAAc,KAAa,MAAsB;CACzD,MAAM,OAAO,SAAS,KAAK,IAAI,CAAC,CAAC,QAAQ,WAAW,KAAK;CACzD,OAAO,KAAK,WAAW,GAAG,IAAI,OAAO,KAAK;AAC3C;AAEA,SAAS,cAAc,SAeZ;CACV,MAAM,EACL,UACA,OACA,SACA,WACA,UACA,KACA,OACA,WACA,OACA,SACG;CAIJ,MAAM,aAAa,OAChB,MAAM,QAAQ,EAAE,cAAc,QAAQ,SAAS,iBAAiB,CAAC,IACjE,CAAC;CACJ,MAAM,SAAS,WAAW,WAAW,IAAI,WAAW,KAAK,KAAA;CACzD,MAAM,UAAU,CAAC,GAAG,IAAI,IAAI,MAAM,SAAS,EAAE,cAAc,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK;CAE3E,MAAM,UAAU;EACf,WAAW,SAAS,SAAS,oBAAoB,KAAK,UAAU,SAAS,sBAAsB,GAAG,gCAAgC,SAAS,0BAA0B,GAAG;EAIxK,GAAG,CACF,GAAG,IAAI,IACN,CAAC,GAAG,WAAW,GAAG,QAAQ,CAAC,CAAC,KAAK,cAAc,CAC9C,UAAU,IACV,SACD,CAAC,CACF,CAAC,CAAC,OAAO,CACV,CAAC,CAAC,KACA,EAAE,IAAI,aACN,iBAAiB,OAAO,WAAW,QAAQ,GAAG,WAAW,cAAc,KAAK,OAAO,IAAI,EAAE,GAC3F;EACA;EACA,GAAG,SAAS,KACV,EAAE,SACF,+BAA+B,GAAG,qBAAqB,UAAU,EAAE,EAAE,MACvE;EACA,GAAI,MAAM,SACP,CAAC,wDAAwD,IACzD,CAAC;EACJ,GAAI,QAAQ,SACT,CAAC,YAAY,QAAQ,KAAK,IAAI,EAAE,sCAAsC,IACtE,CAAC;EACJ,GAAI,QAAQ,UAAU,CAAC,QAAQ,OAAO,IAAI,CAAC;EAC3C,GAAG,UAAU,KACX,EAAE,MAAM,cACR,+BAA+B,QAAQ,kBAAkB,cAAc,KAAK,IAAI,EAAE,GACpF;EACA,GAAG,MAAM,KACP,EAAE,IAAI,WACN,YAAY,GAAG,aAAa,cAAc,KAAK,IAAI,EAAE,GACvD;EAGA,GAAG,MAAM,KACP,MAAM,UACN,uBAAuB,MAAM,SAAS,cAAc,KAAK,IAAI,EAAE,GACjE;CACD;CAEA,MAAM,OAAO,OACZ,gBAAgB,KAAK,UAAU,WAAW,IAAI,KAAK,CAAC,EAAE;CAEvD,MAAM,UAAU;EACf,GAAG,SAAS,KACV,EAAE,IAAI,cAAc,UAAU,GAAG,SAAS,WAAW,IAAI,KAAK,EAAE;YACxD,WAAW,EAAE,EAAE,WAAW,GAAG;aAC5B,IAAI,EAAE,EAAE;sBAElB,UACG;;;kCAIA,GACH;KAED;EACA,GAAG,MAAM,KACP,SAAS,UAAU,KAAK,GAAG,SAAS,WAAW,KAAK,IAAI,KAAK,EAAE;YACvD,WAAW,KAAK,EAAE,EAAE;kBACd,IAAI,KAAK,EAAE,EAAE,GAAG,KAAK,SAAS;cAClC,KAAK,QAAQ,KAAK,WAAW,GAAG,OAAO,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE;;KAGnE;EACA,GAAI,SACD,CACA;qCACgC,OAAO,GAAG;;;;;0CAKL,WAAW,OAAO,EAAE,EAAE;GAE5D,IACC,CAAC;CACL;CAGA,MAAM,gBAAgB,UAAU,SAC7B,KAAK,UAAU,KAAK,EAAE,cAAc,GAAG,QAAQ,aAAa,QAAQ,QAAQ,CAAC,CAAC,KAAK,IAAI,EAAE,MACzF;CACH,MAAM,kBAAkB,UAAU,SAC/B,kBAAkB,UAAU,KAAK,EAAE,cAAc,GAAG,QAAQ,MAAM,QAAQ,QAAQ,CAAC,CAAC,KAAK,IAAI,EAAE,MAC/F;CACH,MAAM,cAAc,MAAM,SACvB,cAAc,MAAM,KAAK,EAAE,SAAS,GAAG,GAAG,MAAM,GAAG,KAAK,CAAC,CAAC,KAAK,IAAI,EAAE,MACrE;CAEH,MAAM,gBAAgB,KAAK,UAAU,KAAK,EAAE,IAAI,cAAc,GAAG,QAAQ,wBAAwB,GAAG,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE;CAEnH,MAAM,eAAe,SAAS,SAC3B,KAAK,SAAS,KAAK,EAAE,IAAI,cAAc,GAAG,QAAQ,sBAAsB,GAAG,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE,MAC1F;CAEH,MAAM,WACL,UAAU,UAAU,iBAAiB,eAClC,aAAa,UAAU,SAAS,gBAAgB,OAChD,iBAAiB,eAAe,KAAK,iBAAiB,SAAS,KAC7D,eAAe,KAAK,iBAAiB,GAAG,KAC1C;CAEJ,OAAO;EACN,QAAQ,KAAK,IAAI,EAAE;;EAEnB,QAAQ,QAAQ,8EAA8E,QAAQ,MAAM,QAAQ,GAAG;;;;EAIvH,QAAQ,KAAK,MAAM,EAAE;;;;;EAKrB,MAAM,KAAK,MAAM,UAAU,KAAK,KAAK,UAAU,IAAI,EAAE,YAAY,MAAM,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE;;;;+BAIzD,SAAS,wCAAwC,kBAAkB,YAAY;;AAE9G;;;;;;;;;;;;AC7nBA,MAAa,iBAAiB;;AAG9B,MAAa,kBAAkB"}
1
+ {"version":3,"file":"ready-DqsyadOs.mjs","names":[],"sources":["../src/test/harness.ts","../src/test/ready.ts"],"sourcesContent":["/**\n * What `gkm test` writes for a feature test to be built from.\n *\n * `gkm test` already discovers an app's constructs and resolves the test\n * stage's environment; this keeps both, under `.gkm/test/`:\n *\n * - `manifest.json` — which construct and endpoint is exported where, and the\n * environment the test stage resolved, keyed as the constructs derive keys.\n * - `clients/<surface>.ts` — each surface's typed client, from the same\n * generator `gkm build` uses for the app's own.\n * - `index.ts` — the configured `it`, and a `Browser` holding one client per\n * surface and one per auth server, each on the browser's `fetch`.\n *\n * A test imports `it` from there and declares nothing — not a construct, not an\n * environment key, not a client.\n */\n\nimport { mkdir, readdir, rm, writeFile } from 'node:fs/promises';\nimport { extname, join, relative } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { TestStage } from '@geekmidas/constructs';\nimport type { BetterAuth } from '@geekmidas/constructs/auth';\nimport type { Endpoint } from '@geekmidas/constructs/endpoints';\nimport type { TestManifest } from '@geekmidas/constructs/testing';\nimport { EnvironmentParser } from '@geekmidas/envkit';\nimport {\n\tcanonicalId,\n\tkebabCase,\n\tprovideKey,\n\tserviceKey,\n} from '@geekmidas/manifest';\nimport { serviceContext } from '@geekmidas/services';\nimport { GkmError } from '../errors';\nimport {\n\tcacheBackendsIn,\n\tdriversFor,\n\teventsBackendsIn,\n\ttype RuntimeDrivers,\n} from '../generators/drivers.js';\nimport { EndpointGenerator } from '../generators/EndpointGenerator.js';\nimport { OpenApiTsGenerator } from '../generators/OpenApiTsGenerator.js';\nimport { SubscriberGenerator } from '../generators/SubscriberGenerator.js';\nimport { type ConstructSource, discover } from '../reconcile/discover.js';\nimport { readFakes } from '../reconcile/fakes.js';\nimport type { CacheBackend, EventsBackend } from '../types.js';\n\n/**\n * The variable the manifest's path travels in — what\n * `@geekmidas/constructs/testing` reads. Its own constant rather than an import:\n * that entry carries the whole harness (MSW, Hono, Vitest), and importing it\n * here put all of it on the start-up path of every `gkm` command.\n */\nexport const TEST_MANIFEST_ENV = 'GKM_TEST_MANIFEST';\n\n/** Where the harness lives, relative to the workspace root. */\nexport const TEST_HARNESS_DIR = '.gkm/test';\n\n/**\n * Better-auth's client plugin for each server plugin id.\n *\n * Written down rather than derived: the ids and the client names do not follow\n * one rule (`admin` pairs with `adminClient`, `email-otp` with\n * `emailOTPClient`), and a guess that missed would hand a test a client\n * silently lacking every method of that plugin. A server plugin not listed has\n * no client counterpart — `bearer`, `open-api` — or needs one from another\n * package, and is left out.\n */\nconst CLIENT_PLUGINS: Readonly<Record<string, string>> = {\n\tadmin: 'adminClient',\n\tanonymous: 'anonymousClient',\n\t'device-authorization': 'deviceAuthorizationClient',\n\t'email-otp': 'emailOTPClient',\n\tjwt: 'jwtClient',\n\t'last-login-method': 'lastLoginMethodClient',\n\t'magic-link': 'magicLinkClient',\n\t'multi-session': 'multiSessionClient',\n\t'oauth-popup': 'oauthPopupClient',\n\t'one-tap': 'oneTapClient',\n\t'one-time-token': 'oneTimeTokenClient',\n\torganization: 'organizationClient',\n\t'phone-number': 'phoneNumberClient',\n\tsiwe: 'siweClient',\n\t'two-factor': 'twoFactorClient',\n\tusername: 'usernameClient',\n};\n\nexport interface WriteTestHarnessOptions {\n\t/** The workspace root: where discovery runs from. */\n\troot: string;\n\t/**\n\t * The directories to write `.gkm/test/` into — each app's own. A package\n\t * maps its `#test` import only to a path inside itself, so each app gets its\n\t * own copy of the harness rather than one at the root it cannot reach.\n\t */\n\ttargets: string[];\n\t/** The constructs glob — the same patterns reconcile and the build use. */\n\tpatterns: string[];\n\t/** The stage the environment was resolved for: always `test`. */\n\tstage: TestStage;\n\t/** The environment the suite runs with. */\n\tenv: Record<string, string>;\n\t/**\n\t * Where a cache lives when it names nowhere — the target's default, which\n\t * the build's server entry registers a driver for.\n\t */\n\tcacheBackend: CacheBackend;\n\t/**\n\t * The broker the target puts topics and queues on, which the server entry\n\t * registers a driver for when the app declares one.\n\t */\n\teventsBackend: EventsBackend;\n\t/**\n\t * The folder of test factories, absolute: `test/factories` at the root, or\n\t * where `test.factories` in the config points. Absent or empty, a test is\n\t * handed no factories.\n\t */\n\tfactories?: string;\n}\n\n/** Where a project keeps its test factories, relative to its root. */\nexport const DEFAULT_FACTORIES_DIR = 'test/factories';\n\n/** A file in the factories folder that names no database construct. */\nexport class UnknownFactoryFile extends GkmError {\n\tconstructor(\n\t\treadonly file: string,\n\t\treadonly known: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${file} names no database construct, so no test could be handed it. ` +\n\t\t\t\t(known.length > 0\n\t\t\t\t\t? `The databases: ${known.join(', ')}. `\n\t\t\t\t\t: 'This project declares no database. ') +\n\t\t\t\t`Name a factory after its database: test/factories/<construct>.ts.`,\n\t\t);\n\t\tthis.name = 'UnknownFactoryFile';\n\t}\n}\n\n/** A factory file that exports no `createFactory`. */\nexport class FactoryHasNoCreate extends GkmError {\n\tconstructor(readonly file: string) {\n\t\tsuper(\n\t\t\t`${file} is a test factory but exports no \\`createFactory\\`. ` +\n\t\t\t\t`Export \\`function createFactory(db: Kysely<Database>)\\` returning ` +\n\t\t\t\t`the factory.`,\n\t\t);\n\t\tthis.name = 'FactoryHasNoCreate';\n\t}\n}\n\n/** One database's factory: the file, and the service name a test keys it by. */\nexport interface FactorySource {\n\tfile: string;\n\tservice: string;\n}\n\n/**\n * The factories in a folder, each matched to the database it is named after.\n *\n * A file that names no database, or exports no `createFactory`, is refused\n * rather than skipped: either is a factory no test would ever be handed.\n */\nexport async function factorySources(\n\tfolder: string | undefined,\n\tdatabases: readonly string[],\n): Promise<FactorySource[]> {\n\tif (!folder) return [];\n\n\tconst entries = await readdir(folder, { withFileTypes: true }).catch(\n\t\t(error: NodeJS.ErrnoException) => {\n\t\t\tif (error.code === 'ENOENT') return [];\n\t\t\tthrow error;\n\t\t},\n\t);\n\n\tconst found: FactorySource[] = [];\n\tfor (const entry of entries) {\n\t\tif (!entry.isFile() || !isFactoryFile(entry.name)) continue;\n\n\t\tconst file = join(folder, entry.name);\n\t\tconst id = canonicalId(entry.name.slice(0, -extname(entry.name).length));\n\t\tif (!databases.includes(id)) {\n\t\t\tthrow new UnknownFactoryFile(file, databases);\n\t\t}\n\n\t\tconst module = (await import(pathToFileURL(file).href)) as {\n\t\t\tcreateFactory?: unknown;\n\t\t};\n\t\tif (typeof module.createFactory !== 'function') {\n\t\t\tthrow new FactoryHasNoCreate(file);\n\t\t}\n\n\t\tfound.push({ file, service: serviceKey(id) });\n\t}\n\n\treturn found.sort((a, b) => (a.service < b.service ? -1 : 1));\n}\n\nfunction isFactoryFile(file: string): boolean {\n\tif (file.endsWith('.d.ts')) return false;\n\tif (/\\.(spec|test)\\.[cm]?[jt]s$/.test(file)) return false;\n\treturn ['.ts', '.mts', '.js', '.mjs'].includes(extname(file));\n}\n\n/** Write the manifest, the clients and the harness; return the manifest path. */\nexport async function writeTestHarness(\n\toptions: WriteTestHarnessOptions,\n): Promise<string> {\n\tconst sources: Record<string, ConstructSource> = {};\n\tconst declared = await discover({\n\t\tpatterns: options.patterns,\n\t\tcwd: options.root,\n\t\tsources,\n\t});\n\n\tconst loaded = await new EndpointGenerator().load(\n\t\toptions.patterns,\n\t\toptions.root,\n\t);\n\tconst endpoints = loaded\n\t\t.map(({ key, construct, path }) => ({\n\t\t\tsurface: construct.owner ?? construct.surface?.id,\n\t\t\tconstruct,\n\t\t\tsource: { file: path.absolute, export: key },\n\t\t}))\n\t\t.filter(\n\t\t\t(endpoint): endpoint is typeof endpoint & { surface: string } =>\n\t\t\t\tendpoint.surface !== undefined,\n\t\t);\n\n\t// Topic subscribers, which a test delivers to. Not constructs, so the build\n\t// finds them the way it finds endpoints.\n\tconst subscribers = (\n\t\tawait new SubscriberGenerator().load(options.patterns, options.root)\n\t).map(({ key, path }) => ({ source: { file: path.absolute, export: key } }));\n\n\tconst manifest: TestManifest = {\n\t\tstage: options.stage,\n\t\tconstructs: Object.fromEntries(\n\t\t\tObject.entries(declared).flatMap(([id, declaration]) => {\n\t\t\t\tconst source = sources[id];\n\t\t\t\treturn source\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tkind: declaration.kind,\n\t\t\t\t\t\t\t\t\tsource: { file: source.file, export: source.exportName },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t]\n\t\t\t\t\t: [];\n\t\t\t}),\n\t\t),\n\t\tendpoints: endpoints.map(({ surface, source }) => ({ surface, source })),\n\t\tsubscribers,\n\t\tenv: options.env,\n\t};\n\t// One typed client per surface, from the generator the build uses.\n\tconst surfaces = [...new Set(endpoints.map(({ surface }) => surface))];\n\tconst generator = new OpenApiTsGenerator();\n\tconst clients = await Promise.all(\n\t\tsurfaces.map(async (surface) => ({\n\t\t\tfile: `${kebabCase(surface)}.ts`,\n\t\t\tcontent: await generator.generate(\n\t\t\t\tendpoints\n\t\t\t\t\t.filter((endpoint) => endpoint.surface === surface)\n\t\t\t\t\t.map(({ construct }) => construct as Endpoint<any, any, any, any>),\n\t\t\t\t{ title: surface, version: '1.0.0' },\n\t\t\t),\n\t\t})),\n\t);\n\n\tconst auths = await authClients(declared, sources, options.env);\n\t// The drivers the server entry registers — which cache and storage clients\n\t// exist is the entry's decision, and the harness stands where the entry does.\n\tconst drivers = driversFor({\n\t\tappRoot: options.root,\n\t\tcache: cacheBackendsIn(declared, options.cacheBackend),\n\t\tevents: eventsBackendsIn(declared, options.eventsBackend),\n\t});\n\n\t// The app's own databases: not a tenant an auth server owns — reached\n\t// through that server — and not a reader of one. What a test is handed.\n\tconst owners = new Set(auths.map((auth) => auth.databaseId));\n\tconst databases = databaseSources(declared, sources).filter(\n\t\t({ id, kind }) => !owners.has(id) && kind !== 'database-reader',\n\t);\n\tconst factories = await factorySources(\n\t\toptions.factories,\n\t\tdatabases.map(({ id }) => id),\n\t);\n\t// Each external API's fake, from the convention rather than the construct —\n\t// which is what keeps a fake out of every deployed bundle.\n\tconst fakes = Object.entries(await readFakes(options.root, declared)).map(\n\t\t([id, { file }]) => ({ id, file }),\n\t);\n\t// Every construct a handler can depend on — what a test reaches through\n\t// `services.get(…)`, typed by the client its service registers. A tenant an\n\t// auth server owns is reached through that server, as databases are.\n\tconst services = serviceSources(declared, sources).filter(\n\t\t({ id }) => !owners.has(id),\n\t);\n\tconst files = [\n\t\t...new Set([\n\t\t\t...Object.values(manifest.constructs).map(({ source }) => source.file),\n\t\t\t...manifest.endpoints.map(({ source }) => source.file),\n\t\t\t// Delivery loads each subscriber too. Left to a dynamic import, it is\n\t\t\t// resolved by Node — which knows nothing of the app's tsconfig paths, so\n\t\t\t// a subscriber importing `~/…` failed every test file that delivered.\n\t\t\t...(manifest.subscribers ?? []).map(({ source }) => source.file),\n\t\t]),\n\t];\n\tconst harnessFor = (dir: string) =>\n\t\tharnessModule({\n\t\t\tdir,\n\t\t\tfiles,\n\t\t\tdatabases,\n\t\t\tsurfaces: surfaces.map((id, index) => ({\n\t\t\t\tid,\n\t\t\t\t// The client the generator writes for a surface with authorizers\n\t\t\t\t// takes a strategy per scheme; one without takes none.\n\t\t\t\tsecured: clients[index]!.content.includes('authStrategies:'),\n\t\t\t})),\n\t\t\tauths,\n\t\t\tdrivers,\n\t\t\tfactories,\n\t\t\tfakes,\n\t\t\tservices,\n\t\t\tmail: Object.values(declared).some(({ kind }) => kind === 'email'),\n\t\t});\n\tconst json = `${JSON.stringify(manifest, null, 2)}\\n`;\n\n\tconst written = await Promise.all(\n\t\t[...new Set(options.targets)].map(async (target) => {\n\t\t\tconst dir = join(target, TEST_HARNESS_DIR);\n\t\t\t// Rewritten whole on every run: a client for a surface that no longer\n\t\t\t// exists is not something a test should be able to import.\n\t\t\tawait rm(dir, { recursive: true, force: true });\n\t\t\tawait mkdir(join(dir, 'clients'), { recursive: true });\n\t\t\tawait writeFile(join(dir, 'manifest.json'), json);\n\t\t\tawait writeFile(join(dir, 'index.ts'), harnessFor(dir));\n\t\t\tfor (const client of clients) {\n\t\t\t\tawait writeFile(join(dir, 'clients', client.file), client.content);\n\t\t\t}\n\t\t\treturn join(dir, 'manifest.json');\n\t\t}),\n\t);\n\n\treturn written[0]!;\n}\n\ninterface AuthClient {\n\tid: string;\n\tbasePath: string;\n\tplugins: string[];\n\t/** The schema tenant it owns — reached through it, never by a test. */\n\tdatabaseId: string;\n}\n\n/** Each auth server, with the client plugins its server plugins pair with. */\nasync function authClients(\n\tdeclared: Awaited<ReturnType<typeof discover>>,\n\tsources: Record<string, ConstructSource>,\n\tenv: Record<string, string>,\n): Promise<AuthClient[]> {\n\tconst clients: AuthClient[] = [];\n\tconst envParser = new EnvironmentParser({ ...env });\n\n\tfor (const [id, declaration] of Object.entries(declared)) {\n\t\tconst source = sources[id];\n\t\tif (declaration.kind !== 'rest-api' || !source) continue;\n\n\t\tconst exported = (await import(source.file))[source.exportName] as unknown;\n\t\tif (!isBetterAuth(exported) || exported.id !== id) continue;\n\n\t\tconst ids = await exported.pluginIds({\n\t\t\tenvParser,\n\t\t\tcontext: serviceContext,\n\t\t});\n\t\tclients.push({\n\t\t\tid,\n\t\t\tbasePath: exported.basePath,\n\t\t\tplugins: ids.flatMap((plugin) => CLIENT_PLUGINS[plugin] ?? []),\n\t\t\tdatabaseId: exported.databaseId,\n\t\t});\n\t}\n\n\treturn clients;\n}\n\nfunction isBetterAuth(value: unknown): value is BetterAuth {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\t'pluginIds' in value &&\n\t\t'basePath' in value\n\t);\n}\n\n/** `Api` → `api`, `AdminApi` → `adminApi`: a construct id as a property name. */\nfunction propertyOf(id: string): string {\n\treturn id.charAt(0).toLowerCase() + id.slice(1);\n}\n\n/**\n * The database a test's `db` is — the one the endpoints were given with\n * `.database()`, found by the service they hold, which is the construct's own.\n * The same rule `featureTest` applies at runtime, applied here so `db` can be\n * typed by the construct's schema.\n */\n/** A database construct: its id, its service name, and where it is exported. */\ninterface DatabaseSource {\n\tid: string;\n\tkind: string;\n\tservice: string;\n\tsource: ConstructSource;\n}\n\n/**\n * Every database construct the app declares — a database, a schema tenant, a\n * reader — keyed as a test is handed its transaction.\n */\nfunction databaseSources(\n\tdeclared: Awaited<ReturnType<typeof discover>>,\n\tsources: Record<string, ConstructSource>,\n): DatabaseSource[] {\n\treturn Object.entries(declared)\n\t\t.filter(([id, declaration]) =>\n\t\t\tBoolean(sources[id] && declaration.kind.startsWith('database')),\n\t\t)\n\t\t.map(([id, declaration]) => ({\n\t\t\tid,\n\t\t\tkind: declaration.kind,\n\t\t\tservice: serviceKey(id),\n\t\t\tsource: sources[id]!,\n\t\t}));\n}\n\n/** The kinds whose construct hands a handler a client — what `services` reaches. */\nconst SERVICE_KINDS = new Set([\n\t'cache',\n\t'credential',\n\t'database',\n\t'database-schema',\n\t'database-reader',\n\t'email',\n\t'encryption',\n\t'external-api',\n\t'file-server',\n\t'objects',\n\t'queue',\n\t'secret',\n\t'topic',\n]);\n\nfunction serviceSources(\n\tdeclared: Awaited<ReturnType<typeof discover>>,\n\tsources: Record<string, ConstructSource>,\n): DatabaseSource[] {\n\treturn Object.entries(declared)\n\t\t.filter(\n\t\t\t([id, declaration]) =>\n\t\t\t\tSERVICE_KINDS.has(declaration.kind) &&\n\t\t\t\t// The construct exported *is* this node, rather than declaring it\n\t\t\t\t// on the way to something else: an auth server's signing secret is\n\t\t\t\t// its own, and nothing depends on it.\n\t\t\t\t(sources[id]?.construct as { id?: string } | undefined)?.id === id,\n\t\t)\n\t\t.map(([id, declaration]) => ({\n\t\t\tid,\n\t\t\tkind: declaration.kind,\n\t\t\tservice: serviceKey(id),\n\t\t\tsource: sources[id]!,\n\t\t}));\n}\n\n/** An import specifier for `file`, from the harness written into `dir`. */\nfunction specifierFrom(dir: string, file: string): string {\n\tconst path = relative(dir, file).replace(/\\.tsx?$/, '.js');\n\treturn path.startsWith('.') ? path : `./${path}`;\n}\n\nfunction harnessModule(options: {\n\tdir: string;\n\t/** Every module the manifest points at, by absolute path. */\n\tfiles: string[];\n\tdatabases: DatabaseSource[];\n\t/** Every construct a test reaches through `services`, by service name. */\n\tservices: DatabaseSource[];\n\tsurfaces: { id: string; secured: boolean }[];\n\tauths: AuthClient[];\n\tdrivers: RuntimeDrivers;\n\tfactories: FactorySource[];\n\t/** Each external API's fake, by construct id and the file it lives in. */\n\tfakes: { id: string; file: string }[];\n\t/** Whether the app sends mail — what a magic-link sign-in is read from. */\n\tmail: boolean;\n}): string {\n\tconst {\n\t\tsurfaces,\n\t\tauths,\n\t\tdrivers,\n\t\tdatabases,\n\t\tservices,\n\t\tdir,\n\t\tfiles,\n\t\tfactories,\n\t\tfakes,\n\t\tmail,\n\t} = options;\n\t// One auth server a test can sign in to without a person: a magic link,\n\t// read from the app's inbox. With two, which one \\`signIn\\` means is a\n\t// guess, so neither gets it.\n\tconst magicLinks = mail\n\t\t? auths.filter(({ plugins }) => plugins.includes('magicLinkClient'))\n\t\t: [];\n\tconst signIn = magicLinks.length === 1 ? magicLinks[0] : undefined;\n\tconst plugins = [...new Set(auths.flatMap(({ plugins }) => plugins))].sort();\n\n\tconst imports = [\n\t\t`import {${services.length ? ' type ClientOf,' : ''}${databases.length ? ' type DatabaseOf,' : ''} featureTest, loadTestManifest${signIn ? ', signInWithMagicLink' : ''} } from '@geekmidas/constructs/testing';`,\n\t\t// A type import per construct the types name: each database, and each\n\t\t// construct a test reaches through `services` — once, though a database\n\t\t// is both.\n\t\t...[\n\t\t\t...new Map(\n\t\t\t\t[...databases, ...services].map((construct) => [\n\t\t\t\t\tconstruct.id,\n\t\t\t\t\tconstruct,\n\t\t\t\t]),\n\t\t\t).values(),\n\t\t].map(\n\t\t\t({ id, source }) =>\n\t\t\t\t`import type { ${source.exportName} as __${id} } from '${specifierFrom(dir, source.file)}';`,\n\t\t),\n\t\t`import { Browser as TestBrowser } from '@geekmidas/testkit/browser';`,\n\t\t...surfaces.map(\n\t\t\t({ id }) =>\n\t\t\t\t`import { createApi as create${id} } from './clients/${kebabCase(id)}.js';`,\n\t\t),\n\t\t...(auths.length\n\t\t\t? [`import { createAuthClient } from 'better-auth/client';`]\n\t\t\t: []),\n\t\t...(plugins.length\n\t\t\t? [`import { ${plugins.join(', ')} } from 'better-auth/client/plugins';`]\n\t\t\t: []),\n\t\t...(drivers.imports ? [drivers.imports] : []),\n\t\t...factories.map(\n\t\t\t({ file, service }) =>\n\t\t\t\t`import { createFactory as __${service}Factory } from '${specifierFrom(dir, file)}';`,\n\t\t),\n\t\t...fakes.map(\n\t\t\t({ id, file }) =>\n\t\t\t\t`import __${id}Fake from '${specifierFrom(dir, file)}';`,\n\t\t),\n\t\t// Every construct and endpoint module, imported here — inside the app,\n\t\t// where its tsconfig paths resolve — rather than by path from the kit.\n\t\t...files.map(\n\t\t\t(file, index) =>\n\t\t\t\t`import * as __module${index} from '${specifierFrom(dir, file)}';`,\n\t\t),\n\t];\n\n\tconst url = (id: string) =>\n\t\t`manifest.env[${JSON.stringify(provideKey(id, 'url'))}]!`;\n\n\tconst members = [\n\t\t...surfaces.map(\n\t\t\t({ id, secured }) => `\t/** \\`${id}\\`, at ${provideKey(id, 'url')}. */\n\treadonly ${propertyOf(id)} = create${id}({\n\t\tbaseURL: ${url(id)},\n\t\tfetch: this.fetch,${\n\t\t\tsecured\n\t\t\t\t? `\n\t\t// Served in-process: no authorizer runs, so nothing is signed. A scheme\n\t\t// with no strategy sends its request without credentials.\n\t\tauthStrategies: {} as never,`\n\t\t\t\t: ''\n\t\t}\n\t});`,\n\t\t),\n\t\t...auths.map(\n\t\t\t(auth) => `\t/** \\`${auth.id}\\`, at ${provideKey(auth.id, 'url')}. */\n\treadonly ${propertyOf(auth.id)} = createAuthClient({\n\t\tbaseURL: \\`\\${${url(auth.id)}}${auth.basePath}\\`,\n\t\tplugins: [${auth.plugins.map((plugin) => `${plugin}()`).join(', ')}],\n\t\tfetchOptions: { customFetchImpl: this.fetch },\n\t});`,\n\t\t),\n\t\t...(signIn\n\t\t\t? [\n\t\t\t\t\t`\t/**\n\t * Sign in as \\`email\\` through \\`${signIn.id}\\`'s magic link, the way a person\n\t * does — the email opened, the link followed. The session it then reports.\n\t * Without an address, as somebody new: \\`user.email\\` says who.\n\t */\n\tsignIn(email?: string) {\n\t\treturn signInWithMagicLink(this, this.${propertyOf(signIn.id)}, email);\n\t}`,\n\t\t\t\t]\n\t\t\t: []),\n\t];\n\n\t// Each database's factory, keyed as the test is handed it.\n\tconst factoriesType = factories.length\n\t\t? `{ ${factories.map(({ service }) => `${service}: typeof __${service}Factory`).join('; ')} }`\n\t\t: '';\n\tconst factoriesOption = factories.length\n\t\t? `, factories: { ${factories.map(({ service }) => `${service}: __${service}Factory`).join(', ')} }`\n\t\t: '';\n\tconst fakesOption = fakes.length\n\t\t? `, fakes: { ${fakes.map(({ id }) => `${id}: __${id}Fake`).join(', ')} }`\n\t\t: '';\n\t// Each database's schema, keyed as the test is handed its transaction.\n\tconst databasesType = `{ ${databases.map(({ id, service }) => `${service}: DatabaseOf<typeof __${id}>`).join('; ')} }`;\n\t// Each construct's client, keyed as `services.get(…)` is called.\n\tconst servicesType = services.length\n\t\t? `{ ${services.map(({ id, service }) => `${service}: ClientOf<typeof __${id}>`).join('; ')} }`\n\t\t: '';\n\t// Positional, so a later one needs those before it, even when empty.\n\tconst generics =\n\t\tdatabases.length || factoriesType || servicesType\n\t\t\t? `<Browser, ${databases.length ? databasesType : '{}'}${\n\t\t\t\t\tfactoriesType || servicesType ? `, ${factoriesType || '{}'}` : ''\n\t\t\t\t}${servicesType ? `, ${servicesType}` : ''}>`\n\t\t\t: '';\n\n\treturn `// Generated by \\`gkm test\\` from the app's constructs — do not edit.\n${imports.join('\\n')}\n\n${drivers.setup ? `// The drivers the app's server entry registers, registered the same way.\\n${drivers.setup}\\n\\n` : ''}const manifest = loadTestManifest(new URL('./manifest.json', import.meta.url));\n\n/** A browser with a client for every surface the app declares. */\nexport class Browser extends TestBrowser {\n${members.join('\\n\\n')}\n}\n\n/** The app's modules, by the path the manifest records for each. */\nconst modules = {\n${files.map((file, index) => `\\t${JSON.stringify(file)}: __module${index},`).join('\\n')}\n};\n\n/** \\`it\\`, for a test that drives the app the way it runs deployed. */\nexport const it = featureTest${generics}({ manifest, modules, browser: Browser${factoriesOption}${fakesOption} });\n`;\n}\n","/**\n * The hand-off between `gkm test` and the Vitest global setup.\n *\n * `gkm test` reconciles and migrates the test stage before Vitest starts; the\n * setup, run inside Vitest, only has to load what that left. Started by plain\n * `vitest` instead, the setup runs `gkm test --setup` itself first — so there is\n * one way the stage gets ready, whichever started the suite.\n */\n\n/** Set on the Vitest process by `gkm test`: the stage is ready, here. */\nexport const TEST_READY_ENV = 'GKM_TEST_READY';\n\n/** Where `gkm test` records what it prepared, from where it ran. */\nexport const TEST_READY_FILE = '.gkm/test-ready.json';\n\nexport interface TestReady {\n\t/** The resolved environment, as a JSON object of strings. */\n\tenv: string;\n\t/** The test manifest the `#test` harness reads. */\n\tmanifest?: string;\n\t/**\n\t * The test stage's databases this run created, and the port of the\n\t * Postgres that holds them: what `gkm test --teardown` drops when the\n\t * suite ends — or, if a run was killed before its teardown, what the next\n\t * one drops before it starts.\n\t */\n\tdatabases?: { port: number; names: string[] };\n}\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  const require_errors = require("./errors-DMC4Umsb.cjs");
2
- const require_SubscriberGenerator = require("./SubscriberGenerator-C8CPxrCK.cjs");
3
- const require_EndpointGenerator = require("./EndpointGenerator-BDa6pPgE.cjs");
4
- const require_discover = require("./discover-DozUKs8f.cjs");
5
- const require_OpenApiTsGenerator = require("./OpenApiTsGenerator-FNhY7Ssz.cjs");
6
- const require_drivers = require("./drivers-DbjiYpW6.cjs");
2
+ const require_SubscriberGenerator = require("./SubscriberGenerator-C3QTae-R.cjs");
3
+ const require_EndpointGenerator = require("./EndpointGenerator-B2MUa4M2.cjs");
4
+ const require_discover = require("./discover-ojkcbxHw.cjs");
5
+ const require_OpenApiTsGenerator = require("./OpenApiTsGenerator-t3_E98Fi.cjs");
6
+ const require_drivers = require("./drivers-CQ32PB5w.cjs");
7
7
  let node_path = require("node:path");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  let _geekmidas_manifest = require("@geekmidas/manifest");
@@ -401,4 +401,4 @@ Object.defineProperty(exports, "writeTestHarness", {
401
401
  }
402
402
  });
403
403
 
404
- //# sourceMappingURL=ready-BFFP838V.cjs.map
404
+ //# sourceMappingURL=ready-IVGu-q18.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ready-BFFP838V.cjs","names":["GkmError","readdir","join","canonicalId","extname","pathToFileURL","serviceKey","discover","EndpointGenerator","SubscriberGenerator","OpenApiTsGenerator","kebabCase","driversFor","cacheBackendsIn","eventsBackendsIn","readFakes","rm","mkdir","writeFile","EnvironmentParser","serviceContext","relative","provideKey"],"sources":["../src/test/harness.ts","../src/test/ready.ts"],"sourcesContent":["/**\n * What `gkm test` writes for a feature test to be built from.\n *\n * `gkm test` already discovers an app's constructs and resolves the test\n * stage's environment; this keeps both, under `.gkm/test/`:\n *\n * - `manifest.json` — which construct and endpoint is exported where, and the\n * environment the test stage resolved, keyed as the constructs derive keys.\n * - `clients/<surface>.ts` — each surface's typed client, from the same\n * generator `gkm build` uses for the app's own.\n * - `index.ts` — the configured `it`, and a `Browser` holding one client per\n * surface and one per auth server, each on the browser's `fetch`.\n *\n * A test imports `it` from there and declares nothing — not a construct, not an\n * environment key, not a client.\n */\n\nimport { mkdir, readdir, rm, writeFile } from 'node:fs/promises';\nimport { extname, join, relative } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { TestStage } from '@geekmidas/constructs';\nimport type { BetterAuth } from '@geekmidas/constructs/auth';\nimport type { Endpoint } from '@geekmidas/constructs/endpoints';\nimport type { TestManifest } from '@geekmidas/constructs/testing';\nimport { EnvironmentParser } from '@geekmidas/envkit';\nimport {\n\tcanonicalId,\n\tkebabCase,\n\tprovideKey,\n\tserviceKey,\n} from '@geekmidas/manifest';\nimport { serviceContext } from '@geekmidas/services';\nimport { GkmError } from '../errors';\nimport {\n\tcacheBackendsIn,\n\tdriversFor,\n\teventsBackendsIn,\n\ttype RuntimeDrivers,\n} from '../generators/drivers.js';\nimport { EndpointGenerator } from '../generators/EndpointGenerator.js';\nimport { OpenApiTsGenerator } from '../generators/OpenApiTsGenerator.js';\nimport { SubscriberGenerator } from '../generators/SubscriberGenerator.js';\nimport { type ConstructSource, discover } from '../reconcile/discover.js';\nimport { readFakes } from '../reconcile/fakes.js';\nimport type { CacheBackend, EventsBackend } from '../types.js';\n\n/**\n * The variable the manifest's path travels in — what\n * `@geekmidas/constructs/testing` reads. Its own constant rather than an import:\n * that entry carries the whole harness (MSW, Hono, Vitest), and importing it\n * here put all of it on the start-up path of every `gkm` command.\n */\nexport const TEST_MANIFEST_ENV = 'GKM_TEST_MANIFEST';\n\n/** Where the harness lives, relative to the workspace root. */\nexport const TEST_HARNESS_DIR = '.gkm/test';\n\n/**\n * Better-auth's client plugin for each server plugin id.\n *\n * Written down rather than derived: the ids and the client names do not follow\n * one rule (`admin` pairs with `adminClient`, `email-otp` with\n * `emailOTPClient`), and a guess that missed would hand a test a client\n * silently lacking every method of that plugin. A server plugin not listed has\n * no client counterpart — `bearer`, `open-api` — or needs one from another\n * package, and is left out.\n */\nconst CLIENT_PLUGINS: Readonly<Record<string, string>> = {\n\tadmin: 'adminClient',\n\tanonymous: 'anonymousClient',\n\t'device-authorization': 'deviceAuthorizationClient',\n\t'email-otp': 'emailOTPClient',\n\tjwt: 'jwtClient',\n\t'last-login-method': 'lastLoginMethodClient',\n\t'magic-link': 'magicLinkClient',\n\t'multi-session': 'multiSessionClient',\n\t'oauth-popup': 'oauthPopupClient',\n\t'one-tap': 'oneTapClient',\n\t'one-time-token': 'oneTimeTokenClient',\n\torganization: 'organizationClient',\n\t'phone-number': 'phoneNumberClient',\n\tsiwe: 'siweClient',\n\t'two-factor': 'twoFactorClient',\n\tusername: 'usernameClient',\n};\n\nexport interface WriteTestHarnessOptions {\n\t/** The workspace root: where discovery runs from. */\n\troot: string;\n\t/**\n\t * The directories to write `.gkm/test/` into — each app's own. A package\n\t * maps its `#test` import only to a path inside itself, so each app gets its\n\t * own copy of the harness rather than one at the root it cannot reach.\n\t */\n\ttargets: string[];\n\t/** The constructs glob — the same patterns reconcile and the build use. */\n\tpatterns: string[];\n\t/** The stage the environment was resolved for: always `test`. */\n\tstage: TestStage;\n\t/** The environment the suite runs with. */\n\tenv: Record<string, string>;\n\t/**\n\t * Where a cache lives when it names nowhere — the target's default, which\n\t * the build's server entry registers a driver for.\n\t */\n\tcacheBackend: CacheBackend;\n\t/**\n\t * The broker the target puts topics and queues on, which the server entry\n\t * registers a driver for when the app declares one.\n\t */\n\teventsBackend: EventsBackend;\n\t/**\n\t * The folder of test factories, absolute: `test/factories` at the root, or\n\t * where `test.factories` in the config points. Absent or empty, a test is\n\t * handed no factories.\n\t */\n\tfactories?: string;\n}\n\n/** Where a project keeps its test factories, relative to its root. */\nexport const DEFAULT_FACTORIES_DIR = 'test/factories';\n\n/** A file in the factories folder that names no database construct. */\nexport class UnknownFactoryFile extends GkmError {\n\tconstructor(\n\t\treadonly file: string,\n\t\treadonly known: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${file} names no database construct, so no test could be handed it. ` +\n\t\t\t\t(known.length > 0\n\t\t\t\t\t? `The databases: ${known.join(', ')}. `\n\t\t\t\t\t: 'This project declares no database. ') +\n\t\t\t\t`Name a factory after its database: test/factories/<construct>.ts.`,\n\t\t);\n\t\tthis.name = 'UnknownFactoryFile';\n\t}\n}\n\n/** A factory file that exports no `createFactory`. */\nexport class FactoryHasNoCreate extends GkmError {\n\tconstructor(readonly file: string) {\n\t\tsuper(\n\t\t\t`${file} is a test factory but exports no \\`createFactory\\`. ` +\n\t\t\t\t`Export \\`function createFactory(db: Kysely<Database>)\\` returning ` +\n\t\t\t\t`the factory.`,\n\t\t);\n\t\tthis.name = 'FactoryHasNoCreate';\n\t}\n}\n\n/** One database's factory: the file, and the service name a test keys it by. */\nexport interface FactorySource {\n\tfile: string;\n\tservice: string;\n}\n\n/**\n * The factories in a folder, each matched to the database it is named after.\n *\n * A file that names no database, or exports no `createFactory`, is refused\n * rather than skipped: either is a factory no test would ever be handed.\n */\nexport async function factorySources(\n\tfolder: string | undefined,\n\tdatabases: readonly string[],\n): Promise<FactorySource[]> {\n\tif (!folder) return [];\n\n\tconst entries = await readdir(folder, { withFileTypes: true }).catch(\n\t\t(error: NodeJS.ErrnoException) => {\n\t\t\tif (error.code === 'ENOENT') return [];\n\t\t\tthrow error;\n\t\t},\n\t);\n\n\tconst found: FactorySource[] = [];\n\tfor (const entry of entries) {\n\t\tif (!entry.isFile() || !isFactoryFile(entry.name)) continue;\n\n\t\tconst file = join(folder, entry.name);\n\t\tconst id = canonicalId(entry.name.slice(0, -extname(entry.name).length));\n\t\tif (!databases.includes(id)) {\n\t\t\tthrow new UnknownFactoryFile(file, databases);\n\t\t}\n\n\t\tconst module = (await import(pathToFileURL(file).href)) as {\n\t\t\tcreateFactory?: unknown;\n\t\t};\n\t\tif (typeof module.createFactory !== 'function') {\n\t\t\tthrow new FactoryHasNoCreate(file);\n\t\t}\n\n\t\tfound.push({ file, service: serviceKey(id) });\n\t}\n\n\treturn found.sort((a, b) => (a.service < b.service ? -1 : 1));\n}\n\nfunction isFactoryFile(file: string): boolean {\n\tif (file.endsWith('.d.ts')) return false;\n\tif (/\\.(spec|test)\\.[cm]?[jt]s$/.test(file)) return false;\n\treturn ['.ts', '.mts', '.js', '.mjs'].includes(extname(file));\n}\n\n/** Write the manifest, the clients and the harness; return the manifest path. */\nexport async function writeTestHarness(\n\toptions: WriteTestHarnessOptions,\n): Promise<string> {\n\tconst sources: Record<string, ConstructSource> = {};\n\tconst declared = await discover({\n\t\tpatterns: options.patterns,\n\t\tcwd: options.root,\n\t\tsources,\n\t});\n\n\tconst loaded = await new EndpointGenerator().load(\n\t\toptions.patterns,\n\t\toptions.root,\n\t);\n\tconst endpoints = loaded\n\t\t.map(({ key, construct, path }) => ({\n\t\t\tsurface: construct.owner ?? construct.surface?.id,\n\t\t\tconstruct,\n\t\t\tsource: { file: path.absolute, export: key },\n\t\t}))\n\t\t.filter(\n\t\t\t(endpoint): endpoint is typeof endpoint & { surface: string } =>\n\t\t\t\tendpoint.surface !== undefined,\n\t\t);\n\n\t// Topic subscribers, which a test delivers to. Not constructs, so the build\n\t// finds them the way it finds endpoints.\n\tconst subscribers = (\n\t\tawait new SubscriberGenerator().load(options.patterns, options.root)\n\t).map(({ key, path }) => ({ source: { file: path.absolute, export: key } }));\n\n\tconst manifest: TestManifest = {\n\t\tstage: options.stage,\n\t\tconstructs: Object.fromEntries(\n\t\t\tObject.entries(declared).flatMap(([id, declaration]) => {\n\t\t\t\tconst source = sources[id];\n\t\t\t\treturn source\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tkind: declaration.kind,\n\t\t\t\t\t\t\t\t\tsource: { file: source.file, export: source.exportName },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t]\n\t\t\t\t\t: [];\n\t\t\t}),\n\t\t),\n\t\tendpoints: endpoints.map(({ surface, source }) => ({ surface, source })),\n\t\tsubscribers,\n\t\tenv: options.env,\n\t};\n\t// One typed client per surface, from the generator the build uses.\n\tconst surfaces = [...new Set(endpoints.map(({ surface }) => surface))];\n\tconst generator = new OpenApiTsGenerator();\n\tconst clients = await Promise.all(\n\t\tsurfaces.map(async (surface) => ({\n\t\t\tfile: `${kebabCase(surface)}.ts`,\n\t\t\tcontent: await generator.generate(\n\t\t\t\tendpoints\n\t\t\t\t\t.filter((endpoint) => endpoint.surface === surface)\n\t\t\t\t\t.map(({ construct }) => construct as Endpoint<any, any, any, any>),\n\t\t\t\t{ title: surface, version: '1.0.0' },\n\t\t\t),\n\t\t})),\n\t);\n\n\tconst auths = await authClients(declared, sources, options.env);\n\t// The drivers the server entry registers — which cache and storage clients\n\t// exist is the entry's decision, and the harness stands where the entry does.\n\tconst drivers = driversFor({\n\t\tappRoot: options.root,\n\t\tcache: cacheBackendsIn(declared, options.cacheBackend),\n\t\tevents: eventsBackendsIn(declared, options.eventsBackend),\n\t});\n\n\t// The app's own databases: not a tenant an auth server owns — reached\n\t// through that server — and not a reader of one. What a test is handed.\n\tconst owners = new Set(auths.map((auth) => auth.databaseId));\n\tconst databases = databaseSources(declared, sources).filter(\n\t\t({ id, kind }) => !owners.has(id) && kind !== 'database-reader',\n\t);\n\tconst factories = await factorySources(\n\t\toptions.factories,\n\t\tdatabases.map(({ id }) => id),\n\t);\n\t// Each external API's fake, from the convention rather than the construct —\n\t// which is what keeps a fake out of every deployed bundle.\n\tconst fakes = Object.entries(await readFakes(options.root, declared)).map(\n\t\t([id, { file }]) => ({ id, file }),\n\t);\n\t// Every construct a handler can depend on — what a test reaches through\n\t// `services.get(…)`, typed by the client its service registers. A tenant an\n\t// auth server owns is reached through that server, as databases are.\n\tconst services = serviceSources(declared, sources).filter(\n\t\t({ id }) => !owners.has(id),\n\t);\n\tconst files = [\n\t\t...new Set([\n\t\t\t...Object.values(manifest.constructs).map(({ source }) => source.file),\n\t\t\t...manifest.endpoints.map(({ source }) => source.file),\n\t\t\t// Delivery loads each subscriber too. Left to a dynamic import, it is\n\t\t\t// resolved by Node — which knows nothing of the app's tsconfig paths, so\n\t\t\t// a subscriber importing `~/…` failed every test file that delivered.\n\t\t\t...(manifest.subscribers ?? []).map(({ source }) => source.file),\n\t\t]),\n\t];\n\tconst harnessFor = (dir: string) =>\n\t\tharnessModule({\n\t\t\tdir,\n\t\t\tfiles,\n\t\t\tdatabases,\n\t\t\tsurfaces: surfaces.map((id, index) => ({\n\t\t\t\tid,\n\t\t\t\t// The client the generator writes for a surface with authorizers\n\t\t\t\t// takes a strategy per scheme; one without takes none.\n\t\t\t\tsecured: clients[index]!.content.includes('authStrategies:'),\n\t\t\t})),\n\t\t\tauths,\n\t\t\tdrivers,\n\t\t\tfactories,\n\t\t\tfakes,\n\t\t\tservices,\n\t\t\tmail: Object.values(declared).some(({ kind }) => kind === 'email'),\n\t\t});\n\tconst json = `${JSON.stringify(manifest, null, 2)}\\n`;\n\n\tconst written = await Promise.all(\n\t\t[...new Set(options.targets)].map(async (target) => {\n\t\t\tconst dir = join(target, TEST_HARNESS_DIR);\n\t\t\t// Rewritten whole on every run: a client for a surface that no longer\n\t\t\t// exists is not something a test should be able to import.\n\t\t\tawait rm(dir, { recursive: true, force: true });\n\t\t\tawait mkdir(join(dir, 'clients'), { recursive: true });\n\t\t\tawait writeFile(join(dir, 'manifest.json'), json);\n\t\t\tawait writeFile(join(dir, 'index.ts'), harnessFor(dir));\n\t\t\tfor (const client of clients) {\n\t\t\t\tawait writeFile(join(dir, 'clients', client.file), client.content);\n\t\t\t}\n\t\t\treturn join(dir, 'manifest.json');\n\t\t}),\n\t);\n\n\treturn written[0]!;\n}\n\ninterface AuthClient {\n\tid: string;\n\tbasePath: string;\n\tplugins: string[];\n\t/** The schema tenant it owns — reached through it, never by a test. */\n\tdatabaseId: string;\n}\n\n/** Each auth server, with the client plugins its server plugins pair with. */\nasync function authClients(\n\tdeclared: Awaited<ReturnType<typeof discover>>,\n\tsources: Record<string, ConstructSource>,\n\tenv: Record<string, string>,\n): Promise<AuthClient[]> {\n\tconst clients: AuthClient[] = [];\n\tconst envParser = new EnvironmentParser({ ...env });\n\n\tfor (const [id, declaration] of Object.entries(declared)) {\n\t\tconst source = sources[id];\n\t\tif (declaration.kind !== 'rest-api' || !source) continue;\n\n\t\tconst exported = (await import(source.file))[source.exportName] as unknown;\n\t\tif (!isBetterAuth(exported) || exported.id !== id) continue;\n\n\t\tconst ids = await exported.pluginIds({\n\t\t\tenvParser,\n\t\t\tcontext: serviceContext,\n\t\t});\n\t\tclients.push({\n\t\t\tid,\n\t\t\tbasePath: exported.basePath,\n\t\t\tplugins: ids.flatMap((plugin) => CLIENT_PLUGINS[plugin] ?? []),\n\t\t\tdatabaseId: exported.databaseId,\n\t\t});\n\t}\n\n\treturn clients;\n}\n\nfunction isBetterAuth(value: unknown): value is BetterAuth {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\t'pluginIds' in value &&\n\t\t'basePath' in value\n\t);\n}\n\n/** `Api` → `api`, `AdminApi` → `adminApi`: a construct id as a property name. */\nfunction propertyOf(id: string): string {\n\treturn id.charAt(0).toLowerCase() + id.slice(1);\n}\n\n/**\n * The database a test's `db` is — the one the endpoints were given with\n * `.database()`, found by the service they hold, which is the construct's own.\n * The same rule `featureTest` applies at runtime, applied here so `db` can be\n * typed by the construct's schema.\n */\n/** A database construct: its id, its service name, and where it is exported. */\ninterface DatabaseSource {\n\tid: string;\n\tkind: string;\n\tservice: string;\n\tsource: ConstructSource;\n}\n\n/**\n * Every database construct the app declares — a database, a schema tenant, a\n * reader — keyed as a test is handed its transaction.\n */\nfunction databaseSources(\n\tdeclared: Awaited<ReturnType<typeof discover>>,\n\tsources: Record<string, ConstructSource>,\n): DatabaseSource[] {\n\treturn Object.entries(declared)\n\t\t.filter(([id, declaration]) =>\n\t\t\tBoolean(sources[id] && declaration.kind.startsWith('database')),\n\t\t)\n\t\t.map(([id, declaration]) => ({\n\t\t\tid,\n\t\t\tkind: declaration.kind,\n\t\t\tservice: serviceKey(id),\n\t\t\tsource: sources[id]!,\n\t\t}));\n}\n\n/** The kinds whose construct hands a handler a client — what `services` reaches. */\nconst SERVICE_KINDS = new Set([\n\t'cache',\n\t'credential',\n\t'database',\n\t'database-schema',\n\t'database-reader',\n\t'email',\n\t'encryption',\n\t'external-api',\n\t'file-server',\n\t'objects',\n\t'queue',\n\t'secret',\n\t'topic',\n]);\n\nfunction serviceSources(\n\tdeclared: Awaited<ReturnType<typeof discover>>,\n\tsources: Record<string, ConstructSource>,\n): DatabaseSource[] {\n\treturn Object.entries(declared)\n\t\t.filter(\n\t\t\t([id, declaration]) =>\n\t\t\t\tSERVICE_KINDS.has(declaration.kind) &&\n\t\t\t\t// The construct exported *is* this node, rather than declaring it\n\t\t\t\t// on the way to something else: an auth server's signing secret is\n\t\t\t\t// its own, and nothing depends on it.\n\t\t\t\t(sources[id]?.construct as { id?: string } | undefined)?.id === id,\n\t\t)\n\t\t.map(([id, declaration]) => ({\n\t\t\tid,\n\t\t\tkind: declaration.kind,\n\t\t\tservice: serviceKey(id),\n\t\t\tsource: sources[id]!,\n\t\t}));\n}\n\n/** An import specifier for `file`, from the harness written into `dir`. */\nfunction specifierFrom(dir: string, file: string): string {\n\tconst path = relative(dir, file).replace(/\\.tsx?$/, '.js');\n\treturn path.startsWith('.') ? path : `./${path}`;\n}\n\nfunction harnessModule(options: {\n\tdir: string;\n\t/** Every module the manifest points at, by absolute path. */\n\tfiles: string[];\n\tdatabases: DatabaseSource[];\n\t/** Every construct a test reaches through `services`, by service name. */\n\tservices: DatabaseSource[];\n\tsurfaces: { id: string; secured: boolean }[];\n\tauths: AuthClient[];\n\tdrivers: RuntimeDrivers;\n\tfactories: FactorySource[];\n\t/** Each external API's fake, by construct id and the file it lives in. */\n\tfakes: { id: string; file: string }[];\n\t/** Whether the app sends mail — what a magic-link sign-in is read from. */\n\tmail: boolean;\n}): string {\n\tconst {\n\t\tsurfaces,\n\t\tauths,\n\t\tdrivers,\n\t\tdatabases,\n\t\tservices,\n\t\tdir,\n\t\tfiles,\n\t\tfactories,\n\t\tfakes,\n\t\tmail,\n\t} = options;\n\t// One auth server a test can sign in to without a person: a magic link,\n\t// read from the app's inbox. With two, which one \\`signIn\\` means is a\n\t// guess, so neither gets it.\n\tconst magicLinks = mail\n\t\t? auths.filter(({ plugins }) => plugins.includes('magicLinkClient'))\n\t\t: [];\n\tconst signIn = magicLinks.length === 1 ? magicLinks[0] : undefined;\n\tconst plugins = [...new Set(auths.flatMap(({ plugins }) => plugins))].sort();\n\n\tconst imports = [\n\t\t`import {${services.length ? ' type ClientOf,' : ''}${databases.length ? ' type DatabaseOf,' : ''} featureTest, loadTestManifest${signIn ? ', signInWithMagicLink' : ''} } from '@geekmidas/constructs/testing';`,\n\t\t// A type import per construct the types name: each database, and each\n\t\t// construct a test reaches through `services` — once, though a database\n\t\t// is both.\n\t\t...[\n\t\t\t...new Map(\n\t\t\t\t[...databases, ...services].map((construct) => [\n\t\t\t\t\tconstruct.id,\n\t\t\t\t\tconstruct,\n\t\t\t\t]),\n\t\t\t).values(),\n\t\t].map(\n\t\t\t({ id, source }) =>\n\t\t\t\t`import type { ${source.exportName} as __${id} } from '${specifierFrom(dir, source.file)}';`,\n\t\t),\n\t\t`import { Browser as TestBrowser } from '@geekmidas/testkit/browser';`,\n\t\t...surfaces.map(\n\t\t\t({ id }) =>\n\t\t\t\t`import { createApi as create${id} } from './clients/${kebabCase(id)}.js';`,\n\t\t),\n\t\t...(auths.length\n\t\t\t? [`import { createAuthClient } from 'better-auth/client';`]\n\t\t\t: []),\n\t\t...(plugins.length\n\t\t\t? [`import { ${plugins.join(', ')} } from 'better-auth/client/plugins';`]\n\t\t\t: []),\n\t\t...(drivers.imports ? [drivers.imports] : []),\n\t\t...factories.map(\n\t\t\t({ file, service }) =>\n\t\t\t\t`import { createFactory as __${service}Factory } from '${specifierFrom(dir, file)}';`,\n\t\t),\n\t\t...fakes.map(\n\t\t\t({ id, file }) =>\n\t\t\t\t`import __${id}Fake from '${specifierFrom(dir, file)}';`,\n\t\t),\n\t\t// Every construct and endpoint module, imported here — inside the app,\n\t\t// where its tsconfig paths resolve — rather than by path from the kit.\n\t\t...files.map(\n\t\t\t(file, index) =>\n\t\t\t\t`import * as __module${index} from '${specifierFrom(dir, file)}';`,\n\t\t),\n\t];\n\n\tconst url = (id: string) =>\n\t\t`manifest.env[${JSON.stringify(provideKey(id, 'url'))}]!`;\n\n\tconst members = [\n\t\t...surfaces.map(\n\t\t\t({ id, secured }) => `\t/** \\`${id}\\`, at ${provideKey(id, 'url')}. */\n\treadonly ${propertyOf(id)} = create${id}({\n\t\tbaseURL: ${url(id)},\n\t\tfetch: this.fetch,${\n\t\t\tsecured\n\t\t\t\t? `\n\t\t// Served in-process: no authorizer runs, so nothing is signed. A scheme\n\t\t// with no strategy sends its request without credentials.\n\t\tauthStrategies: {} as never,`\n\t\t\t\t: ''\n\t\t}\n\t});`,\n\t\t),\n\t\t...auths.map(\n\t\t\t(auth) => `\t/** \\`${auth.id}\\`, at ${provideKey(auth.id, 'url')}. */\n\treadonly ${propertyOf(auth.id)} = createAuthClient({\n\t\tbaseURL: \\`\\${${url(auth.id)}}${auth.basePath}\\`,\n\t\tplugins: [${auth.plugins.map((plugin) => `${plugin}()`).join(', ')}],\n\t\tfetchOptions: { customFetchImpl: this.fetch },\n\t});`,\n\t\t),\n\t\t...(signIn\n\t\t\t? [\n\t\t\t\t\t`\t/**\n\t * Sign in as \\`email\\` through \\`${signIn.id}\\`'s magic link, the way a person\n\t * does — the email opened, the link followed. The session it then reports.\n\t * Without an address, as somebody new: \\`user.email\\` says who.\n\t */\n\tsignIn(email?: string) {\n\t\treturn signInWithMagicLink(this, this.${propertyOf(signIn.id)}, email);\n\t}`,\n\t\t\t\t]\n\t\t\t: []),\n\t];\n\n\t// Each database's factory, keyed as the test is handed it.\n\tconst factoriesType = factories.length\n\t\t? `{ ${factories.map(({ service }) => `${service}: typeof __${service}Factory`).join('; ')} }`\n\t\t: '';\n\tconst factoriesOption = factories.length\n\t\t? `, factories: { ${factories.map(({ service }) => `${service}: __${service}Factory`).join(', ')} }`\n\t\t: '';\n\tconst fakesOption = fakes.length\n\t\t? `, fakes: { ${fakes.map(({ id }) => `${id}: __${id}Fake`).join(', ')} }`\n\t\t: '';\n\t// Each database's schema, keyed as the test is handed its transaction.\n\tconst databasesType = `{ ${databases.map(({ id, service }) => `${service}: DatabaseOf<typeof __${id}>`).join('; ')} }`;\n\t// Each construct's client, keyed as `services.get(…)` is called.\n\tconst servicesType = services.length\n\t\t? `{ ${services.map(({ id, service }) => `${service}: ClientOf<typeof __${id}>`).join('; ')} }`\n\t\t: '';\n\t// Positional, so a later one needs those before it, even when empty.\n\tconst generics =\n\t\tdatabases.length || factoriesType || servicesType\n\t\t\t? `<Browser, ${databases.length ? databasesType : '{}'}${\n\t\t\t\t\tfactoriesType || servicesType ? `, ${factoriesType || '{}'}` : ''\n\t\t\t\t}${servicesType ? `, ${servicesType}` : ''}>`\n\t\t\t: '';\n\n\treturn `// Generated by \\`gkm test\\` from the app's constructs — do not edit.\n${imports.join('\\n')}\n\n${drivers.setup ? `// The drivers the app's server entry registers, registered the same way.\\n${drivers.setup}\\n\\n` : ''}const manifest = loadTestManifest(new URL('./manifest.json', import.meta.url));\n\n/** A browser with a client for every surface the app declares. */\nexport class Browser extends TestBrowser {\n${members.join('\\n\\n')}\n}\n\n/** The app's modules, by the path the manifest records for each. */\nconst modules = {\n${files.map((file, index) => `\\t${JSON.stringify(file)}: __module${index},`).join('\\n')}\n};\n\n/** \\`it\\`, for a test that drives the app the way it runs deployed. */\nexport const it = featureTest${generics}({ manifest, modules, browser: Browser${factoriesOption}${fakesOption} });\n`;\n}\n","/**\n * The hand-off between `gkm test` and the Vitest global setup.\n *\n * `gkm test` reconciles and migrates the test stage before Vitest starts; the\n * setup, run inside Vitest, only has to load what that left. Started by plain\n * `vitest` instead, the setup runs `gkm test --setup` itself first — so there is\n * one way the stage gets ready, whichever started the suite.\n */\n\n/** Set on the Vitest process by `gkm test`: the stage is ready, here. */\nexport const TEST_READY_ENV = 'GKM_TEST_READY';\n\n/** Where `gkm test` records what it prepared, from where it ran. */\nexport const TEST_READY_FILE = '.gkm/test-ready.json';\n\nexport interface TestReady {\n\t/** The resolved environment, as a JSON object of strings. */\n\tenv: string;\n\t/** The test manifest the `#test` harness reads. */\n\tmanifest?: string;\n\t/**\n\t * The test stage's databases this run created, and the port of the\n\t * Postgres that holds them: what `gkm test --teardown` drops when the\n\t * suite ends — or, if a run was killed before its teardown, what the next\n\t * one drops before it starts.\n\t */\n\tdatabases?: { port: number; names: string[] };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ready-IVGu-q18.cjs","names":["GkmError","readdir","join","canonicalId","extname","pathToFileURL","serviceKey","discover","EndpointGenerator","SubscriberGenerator","OpenApiTsGenerator","kebabCase","driversFor","cacheBackendsIn","eventsBackendsIn","readFakes","rm","mkdir","writeFile","EnvironmentParser","serviceContext","relative","provideKey"],"sources":["../src/test/harness.ts","../src/test/ready.ts"],"sourcesContent":["/**\n * What `gkm test` writes for a feature test to be built from.\n *\n * `gkm test` already discovers an app's constructs and resolves the test\n * stage's environment; this keeps both, under `.gkm/test/`:\n *\n * - `manifest.json` — which construct and endpoint is exported where, and the\n * environment the test stage resolved, keyed as the constructs derive keys.\n * - `clients/<surface>.ts` — each surface's typed client, from the same\n * generator `gkm build` uses for the app's own.\n * - `index.ts` — the configured `it`, and a `Browser` holding one client per\n * surface and one per auth server, each on the browser's `fetch`.\n *\n * A test imports `it` from there and declares nothing — not a construct, not an\n * environment key, not a client.\n */\n\nimport { mkdir, readdir, rm, writeFile } from 'node:fs/promises';\nimport { extname, join, relative } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { TestStage } from '@geekmidas/constructs';\nimport type { BetterAuth } from '@geekmidas/constructs/auth';\nimport type { Endpoint } from '@geekmidas/constructs/endpoints';\nimport type { TestManifest } from '@geekmidas/constructs/testing';\nimport { EnvironmentParser } from '@geekmidas/envkit';\nimport {\n\tcanonicalId,\n\tkebabCase,\n\tprovideKey,\n\tserviceKey,\n} from '@geekmidas/manifest';\nimport { serviceContext } from '@geekmidas/services';\nimport { GkmError } from '../errors';\nimport {\n\tcacheBackendsIn,\n\tdriversFor,\n\teventsBackendsIn,\n\ttype RuntimeDrivers,\n} from '../generators/drivers.js';\nimport { EndpointGenerator } from '../generators/EndpointGenerator.js';\nimport { OpenApiTsGenerator } from '../generators/OpenApiTsGenerator.js';\nimport { SubscriberGenerator } from '../generators/SubscriberGenerator.js';\nimport { type ConstructSource, discover } from '../reconcile/discover.js';\nimport { readFakes } from '../reconcile/fakes.js';\nimport type { CacheBackend, EventsBackend } from '../types.js';\n\n/**\n * The variable the manifest's path travels in — what\n * `@geekmidas/constructs/testing` reads. Its own constant rather than an import:\n * that entry carries the whole harness (MSW, Hono, Vitest), and importing it\n * here put all of it on the start-up path of every `gkm` command.\n */\nexport const TEST_MANIFEST_ENV = 'GKM_TEST_MANIFEST';\n\n/** Where the harness lives, relative to the workspace root. */\nexport const TEST_HARNESS_DIR = '.gkm/test';\n\n/**\n * Better-auth's client plugin for each server plugin id.\n *\n * Written down rather than derived: the ids and the client names do not follow\n * one rule (`admin` pairs with `adminClient`, `email-otp` with\n * `emailOTPClient`), and a guess that missed would hand a test a client\n * silently lacking every method of that plugin. A server plugin not listed has\n * no client counterpart — `bearer`, `open-api` — or needs one from another\n * package, and is left out.\n */\nconst CLIENT_PLUGINS: Readonly<Record<string, string>> = {\n\tadmin: 'adminClient',\n\tanonymous: 'anonymousClient',\n\t'device-authorization': 'deviceAuthorizationClient',\n\t'email-otp': 'emailOTPClient',\n\tjwt: 'jwtClient',\n\t'last-login-method': 'lastLoginMethodClient',\n\t'magic-link': 'magicLinkClient',\n\t'multi-session': 'multiSessionClient',\n\t'oauth-popup': 'oauthPopupClient',\n\t'one-tap': 'oneTapClient',\n\t'one-time-token': 'oneTimeTokenClient',\n\torganization: 'organizationClient',\n\t'phone-number': 'phoneNumberClient',\n\tsiwe: 'siweClient',\n\t'two-factor': 'twoFactorClient',\n\tusername: 'usernameClient',\n};\n\nexport interface WriteTestHarnessOptions {\n\t/** The workspace root: where discovery runs from. */\n\troot: string;\n\t/**\n\t * The directories to write `.gkm/test/` into — each app's own. A package\n\t * maps its `#test` import only to a path inside itself, so each app gets its\n\t * own copy of the harness rather than one at the root it cannot reach.\n\t */\n\ttargets: string[];\n\t/** The constructs glob — the same patterns reconcile and the build use. */\n\tpatterns: string[];\n\t/** The stage the environment was resolved for: always `test`. */\n\tstage: TestStage;\n\t/** The environment the suite runs with. */\n\tenv: Record<string, string>;\n\t/**\n\t * Where a cache lives when it names nowhere — the target's default, which\n\t * the build's server entry registers a driver for.\n\t */\n\tcacheBackend: CacheBackend;\n\t/**\n\t * The broker the target puts topics and queues on, which the server entry\n\t * registers a driver for when the app declares one.\n\t */\n\teventsBackend: EventsBackend;\n\t/**\n\t * The folder of test factories, absolute: `test/factories` at the root, or\n\t * where `test.factories` in the config points. Absent or empty, a test is\n\t * handed no factories.\n\t */\n\tfactories?: string;\n}\n\n/** Where a project keeps its test factories, relative to its root. */\nexport const DEFAULT_FACTORIES_DIR = 'test/factories';\n\n/** A file in the factories folder that names no database construct. */\nexport class UnknownFactoryFile extends GkmError {\n\tconstructor(\n\t\treadonly file: string,\n\t\treadonly known: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${file} names no database construct, so no test could be handed it. ` +\n\t\t\t\t(known.length > 0\n\t\t\t\t\t? `The databases: ${known.join(', ')}. `\n\t\t\t\t\t: 'This project declares no database. ') +\n\t\t\t\t`Name a factory after its database: test/factories/<construct>.ts.`,\n\t\t);\n\t\tthis.name = 'UnknownFactoryFile';\n\t}\n}\n\n/** A factory file that exports no `createFactory`. */\nexport class FactoryHasNoCreate extends GkmError {\n\tconstructor(readonly file: string) {\n\t\tsuper(\n\t\t\t`${file} is a test factory but exports no \\`createFactory\\`. ` +\n\t\t\t\t`Export \\`function createFactory(db: Kysely<Database>)\\` returning ` +\n\t\t\t\t`the factory.`,\n\t\t);\n\t\tthis.name = 'FactoryHasNoCreate';\n\t}\n}\n\n/** One database's factory: the file, and the service name a test keys it by. */\nexport interface FactorySource {\n\tfile: string;\n\tservice: string;\n}\n\n/**\n * The factories in a folder, each matched to the database it is named after.\n *\n * A file that names no database, or exports no `createFactory`, is refused\n * rather than skipped: either is a factory no test would ever be handed.\n */\nexport async function factorySources(\n\tfolder: string | undefined,\n\tdatabases: readonly string[],\n): Promise<FactorySource[]> {\n\tif (!folder) return [];\n\n\tconst entries = await readdir(folder, { withFileTypes: true }).catch(\n\t\t(error: NodeJS.ErrnoException) => {\n\t\t\tif (error.code === 'ENOENT') return [];\n\t\t\tthrow error;\n\t\t},\n\t);\n\n\tconst found: FactorySource[] = [];\n\tfor (const entry of entries) {\n\t\tif (!entry.isFile() || !isFactoryFile(entry.name)) continue;\n\n\t\tconst file = join(folder, entry.name);\n\t\tconst id = canonicalId(entry.name.slice(0, -extname(entry.name).length));\n\t\tif (!databases.includes(id)) {\n\t\t\tthrow new UnknownFactoryFile(file, databases);\n\t\t}\n\n\t\tconst module = (await import(pathToFileURL(file).href)) as {\n\t\t\tcreateFactory?: unknown;\n\t\t};\n\t\tif (typeof module.createFactory !== 'function') {\n\t\t\tthrow new FactoryHasNoCreate(file);\n\t\t}\n\n\t\tfound.push({ file, service: serviceKey(id) });\n\t}\n\n\treturn found.sort((a, b) => (a.service < b.service ? -1 : 1));\n}\n\nfunction isFactoryFile(file: string): boolean {\n\tif (file.endsWith('.d.ts')) return false;\n\tif (/\\.(spec|test)\\.[cm]?[jt]s$/.test(file)) return false;\n\treturn ['.ts', '.mts', '.js', '.mjs'].includes(extname(file));\n}\n\n/** Write the manifest, the clients and the harness; return the manifest path. */\nexport async function writeTestHarness(\n\toptions: WriteTestHarnessOptions,\n): Promise<string> {\n\tconst sources: Record<string, ConstructSource> = {};\n\tconst declared = await discover({\n\t\tpatterns: options.patterns,\n\t\tcwd: options.root,\n\t\tsources,\n\t});\n\n\tconst loaded = await new EndpointGenerator().load(\n\t\toptions.patterns,\n\t\toptions.root,\n\t);\n\tconst endpoints = loaded\n\t\t.map(({ key, construct, path }) => ({\n\t\t\tsurface: construct.owner ?? construct.surface?.id,\n\t\t\tconstruct,\n\t\t\tsource: { file: path.absolute, export: key },\n\t\t}))\n\t\t.filter(\n\t\t\t(endpoint): endpoint is typeof endpoint & { surface: string } =>\n\t\t\t\tendpoint.surface !== undefined,\n\t\t);\n\n\t// Topic subscribers, which a test delivers to. Not constructs, so the build\n\t// finds them the way it finds endpoints.\n\tconst subscribers = (\n\t\tawait new SubscriberGenerator().load(options.patterns, options.root)\n\t).map(({ key, path }) => ({ source: { file: path.absolute, export: key } }));\n\n\tconst manifest: TestManifest = {\n\t\tstage: options.stage,\n\t\tconstructs: Object.fromEntries(\n\t\t\tObject.entries(declared).flatMap(([id, declaration]) => {\n\t\t\t\tconst source = sources[id];\n\t\t\t\treturn source\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tkind: declaration.kind,\n\t\t\t\t\t\t\t\t\tsource: { file: source.file, export: source.exportName },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t]\n\t\t\t\t\t: [];\n\t\t\t}),\n\t\t),\n\t\tendpoints: endpoints.map(({ surface, source }) => ({ surface, source })),\n\t\tsubscribers,\n\t\tenv: options.env,\n\t};\n\t// One typed client per surface, from the generator the build uses.\n\tconst surfaces = [...new Set(endpoints.map(({ surface }) => surface))];\n\tconst generator = new OpenApiTsGenerator();\n\tconst clients = await Promise.all(\n\t\tsurfaces.map(async (surface) => ({\n\t\t\tfile: `${kebabCase(surface)}.ts`,\n\t\t\tcontent: await generator.generate(\n\t\t\t\tendpoints\n\t\t\t\t\t.filter((endpoint) => endpoint.surface === surface)\n\t\t\t\t\t.map(({ construct }) => construct as Endpoint<any, any, any, any>),\n\t\t\t\t{ title: surface, version: '1.0.0' },\n\t\t\t),\n\t\t})),\n\t);\n\n\tconst auths = await authClients(declared, sources, options.env);\n\t// The drivers the server entry registers — which cache and storage clients\n\t// exist is the entry's decision, and the harness stands where the entry does.\n\tconst drivers = driversFor({\n\t\tappRoot: options.root,\n\t\tcache: cacheBackendsIn(declared, options.cacheBackend),\n\t\tevents: eventsBackendsIn(declared, options.eventsBackend),\n\t});\n\n\t// The app's own databases: not a tenant an auth server owns — reached\n\t// through that server — and not a reader of one. What a test is handed.\n\tconst owners = new Set(auths.map((auth) => auth.databaseId));\n\tconst databases = databaseSources(declared, sources).filter(\n\t\t({ id, kind }) => !owners.has(id) && kind !== 'database-reader',\n\t);\n\tconst factories = await factorySources(\n\t\toptions.factories,\n\t\tdatabases.map(({ id }) => id),\n\t);\n\t// Each external API's fake, from the convention rather than the construct —\n\t// which is what keeps a fake out of every deployed bundle.\n\tconst fakes = Object.entries(await readFakes(options.root, declared)).map(\n\t\t([id, { file }]) => ({ id, file }),\n\t);\n\t// Every construct a handler can depend on — what a test reaches through\n\t// `services.get(…)`, typed by the client its service registers. A tenant an\n\t// auth server owns is reached through that server, as databases are.\n\tconst services = serviceSources(declared, sources).filter(\n\t\t({ id }) => !owners.has(id),\n\t);\n\tconst files = [\n\t\t...new Set([\n\t\t\t...Object.values(manifest.constructs).map(({ source }) => source.file),\n\t\t\t...manifest.endpoints.map(({ source }) => source.file),\n\t\t\t// Delivery loads each subscriber too. Left to a dynamic import, it is\n\t\t\t// resolved by Node — which knows nothing of the app's tsconfig paths, so\n\t\t\t// a subscriber importing `~/…` failed every test file that delivered.\n\t\t\t...(manifest.subscribers ?? []).map(({ source }) => source.file),\n\t\t]),\n\t];\n\tconst harnessFor = (dir: string) =>\n\t\tharnessModule({\n\t\t\tdir,\n\t\t\tfiles,\n\t\t\tdatabases,\n\t\t\tsurfaces: surfaces.map((id, index) => ({\n\t\t\t\tid,\n\t\t\t\t// The client the generator writes for a surface with authorizers\n\t\t\t\t// takes a strategy per scheme; one without takes none.\n\t\t\t\tsecured: clients[index]!.content.includes('authStrategies:'),\n\t\t\t})),\n\t\t\tauths,\n\t\t\tdrivers,\n\t\t\tfactories,\n\t\t\tfakes,\n\t\t\tservices,\n\t\t\tmail: Object.values(declared).some(({ kind }) => kind === 'email'),\n\t\t});\n\tconst json = `${JSON.stringify(manifest, null, 2)}\\n`;\n\n\tconst written = await Promise.all(\n\t\t[...new Set(options.targets)].map(async (target) => {\n\t\t\tconst dir = join(target, TEST_HARNESS_DIR);\n\t\t\t// Rewritten whole on every run: a client for a surface that no longer\n\t\t\t// exists is not something a test should be able to import.\n\t\t\tawait rm(dir, { recursive: true, force: true });\n\t\t\tawait mkdir(join(dir, 'clients'), { recursive: true });\n\t\t\tawait writeFile(join(dir, 'manifest.json'), json);\n\t\t\tawait writeFile(join(dir, 'index.ts'), harnessFor(dir));\n\t\t\tfor (const client of clients) {\n\t\t\t\tawait writeFile(join(dir, 'clients', client.file), client.content);\n\t\t\t}\n\t\t\treturn join(dir, 'manifest.json');\n\t\t}),\n\t);\n\n\treturn written[0]!;\n}\n\ninterface AuthClient {\n\tid: string;\n\tbasePath: string;\n\tplugins: string[];\n\t/** The schema tenant it owns — reached through it, never by a test. */\n\tdatabaseId: string;\n}\n\n/** Each auth server, with the client plugins its server plugins pair with. */\nasync function authClients(\n\tdeclared: Awaited<ReturnType<typeof discover>>,\n\tsources: Record<string, ConstructSource>,\n\tenv: Record<string, string>,\n): Promise<AuthClient[]> {\n\tconst clients: AuthClient[] = [];\n\tconst envParser = new EnvironmentParser({ ...env });\n\n\tfor (const [id, declaration] of Object.entries(declared)) {\n\t\tconst source = sources[id];\n\t\tif (declaration.kind !== 'rest-api' || !source) continue;\n\n\t\tconst exported = (await import(source.file))[source.exportName] as unknown;\n\t\tif (!isBetterAuth(exported) || exported.id !== id) continue;\n\n\t\tconst ids = await exported.pluginIds({\n\t\t\tenvParser,\n\t\t\tcontext: serviceContext,\n\t\t});\n\t\tclients.push({\n\t\t\tid,\n\t\t\tbasePath: exported.basePath,\n\t\t\tplugins: ids.flatMap((plugin) => CLIENT_PLUGINS[plugin] ?? []),\n\t\t\tdatabaseId: exported.databaseId,\n\t\t});\n\t}\n\n\treturn clients;\n}\n\nfunction isBetterAuth(value: unknown): value is BetterAuth {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\t'pluginIds' in value &&\n\t\t'basePath' in value\n\t);\n}\n\n/** `Api` → `api`, `AdminApi` → `adminApi`: a construct id as a property name. */\nfunction propertyOf(id: string): string {\n\treturn id.charAt(0).toLowerCase() + id.slice(1);\n}\n\n/**\n * The database a test's `db` is — the one the endpoints were given with\n * `.database()`, found by the service they hold, which is the construct's own.\n * The same rule `featureTest` applies at runtime, applied here so `db` can be\n * typed by the construct's schema.\n */\n/** A database construct: its id, its service name, and where it is exported. */\ninterface DatabaseSource {\n\tid: string;\n\tkind: string;\n\tservice: string;\n\tsource: ConstructSource;\n}\n\n/**\n * Every database construct the app declares — a database, a schema tenant, a\n * reader — keyed as a test is handed its transaction.\n */\nfunction databaseSources(\n\tdeclared: Awaited<ReturnType<typeof discover>>,\n\tsources: Record<string, ConstructSource>,\n): DatabaseSource[] {\n\treturn Object.entries(declared)\n\t\t.filter(([id, declaration]) =>\n\t\t\tBoolean(sources[id] && declaration.kind.startsWith('database')),\n\t\t)\n\t\t.map(([id, declaration]) => ({\n\t\t\tid,\n\t\t\tkind: declaration.kind,\n\t\t\tservice: serviceKey(id),\n\t\t\tsource: sources[id]!,\n\t\t}));\n}\n\n/** The kinds whose construct hands a handler a client — what `services` reaches. */\nconst SERVICE_KINDS = new Set([\n\t'cache',\n\t'credential',\n\t'database',\n\t'database-schema',\n\t'database-reader',\n\t'email',\n\t'encryption',\n\t'external-api',\n\t'file-server',\n\t'objects',\n\t'queue',\n\t'secret',\n\t'topic',\n]);\n\nfunction serviceSources(\n\tdeclared: Awaited<ReturnType<typeof discover>>,\n\tsources: Record<string, ConstructSource>,\n): DatabaseSource[] {\n\treturn Object.entries(declared)\n\t\t.filter(\n\t\t\t([id, declaration]) =>\n\t\t\t\tSERVICE_KINDS.has(declaration.kind) &&\n\t\t\t\t// The construct exported *is* this node, rather than declaring it\n\t\t\t\t// on the way to something else: an auth server's signing secret is\n\t\t\t\t// its own, and nothing depends on it.\n\t\t\t\t(sources[id]?.construct as { id?: string } | undefined)?.id === id,\n\t\t)\n\t\t.map(([id, declaration]) => ({\n\t\t\tid,\n\t\t\tkind: declaration.kind,\n\t\t\tservice: serviceKey(id),\n\t\t\tsource: sources[id]!,\n\t\t}));\n}\n\n/** An import specifier for `file`, from the harness written into `dir`. */\nfunction specifierFrom(dir: string, file: string): string {\n\tconst path = relative(dir, file).replace(/\\.tsx?$/, '.js');\n\treturn path.startsWith('.') ? path : `./${path}`;\n}\n\nfunction harnessModule(options: {\n\tdir: string;\n\t/** Every module the manifest points at, by absolute path. */\n\tfiles: string[];\n\tdatabases: DatabaseSource[];\n\t/** Every construct a test reaches through `services`, by service name. */\n\tservices: DatabaseSource[];\n\tsurfaces: { id: string; secured: boolean }[];\n\tauths: AuthClient[];\n\tdrivers: RuntimeDrivers;\n\tfactories: FactorySource[];\n\t/** Each external API's fake, by construct id and the file it lives in. */\n\tfakes: { id: string; file: string }[];\n\t/** Whether the app sends mail — what a magic-link sign-in is read from. */\n\tmail: boolean;\n}): string {\n\tconst {\n\t\tsurfaces,\n\t\tauths,\n\t\tdrivers,\n\t\tdatabases,\n\t\tservices,\n\t\tdir,\n\t\tfiles,\n\t\tfactories,\n\t\tfakes,\n\t\tmail,\n\t} = options;\n\t// One auth server a test can sign in to without a person: a magic link,\n\t// read from the app's inbox. With two, which one \\`signIn\\` means is a\n\t// guess, so neither gets it.\n\tconst magicLinks = mail\n\t\t? auths.filter(({ plugins }) => plugins.includes('magicLinkClient'))\n\t\t: [];\n\tconst signIn = magicLinks.length === 1 ? magicLinks[0] : undefined;\n\tconst plugins = [...new Set(auths.flatMap(({ plugins }) => plugins))].sort();\n\n\tconst imports = [\n\t\t`import {${services.length ? ' type ClientOf,' : ''}${databases.length ? ' type DatabaseOf,' : ''} featureTest, loadTestManifest${signIn ? ', signInWithMagicLink' : ''} } from '@geekmidas/constructs/testing';`,\n\t\t// A type import per construct the types name: each database, and each\n\t\t// construct a test reaches through `services` — once, though a database\n\t\t// is both.\n\t\t...[\n\t\t\t...new Map(\n\t\t\t\t[...databases, ...services].map((construct) => [\n\t\t\t\t\tconstruct.id,\n\t\t\t\t\tconstruct,\n\t\t\t\t]),\n\t\t\t).values(),\n\t\t].map(\n\t\t\t({ id, source }) =>\n\t\t\t\t`import type { ${source.exportName} as __${id} } from '${specifierFrom(dir, source.file)}';`,\n\t\t),\n\t\t`import { Browser as TestBrowser } from '@geekmidas/testkit/browser';`,\n\t\t...surfaces.map(\n\t\t\t({ id }) =>\n\t\t\t\t`import { createApi as create${id} } from './clients/${kebabCase(id)}.js';`,\n\t\t),\n\t\t...(auths.length\n\t\t\t? [`import { createAuthClient } from 'better-auth/client';`]\n\t\t\t: []),\n\t\t...(plugins.length\n\t\t\t? [`import { ${plugins.join(', ')} } from 'better-auth/client/plugins';`]\n\t\t\t: []),\n\t\t...(drivers.imports ? [drivers.imports] : []),\n\t\t...factories.map(\n\t\t\t({ file, service }) =>\n\t\t\t\t`import { createFactory as __${service}Factory } from '${specifierFrom(dir, file)}';`,\n\t\t),\n\t\t...fakes.map(\n\t\t\t({ id, file }) =>\n\t\t\t\t`import __${id}Fake from '${specifierFrom(dir, file)}';`,\n\t\t),\n\t\t// Every construct and endpoint module, imported here — inside the app,\n\t\t// where its tsconfig paths resolve — rather than by path from the kit.\n\t\t...files.map(\n\t\t\t(file, index) =>\n\t\t\t\t`import * as __module${index} from '${specifierFrom(dir, file)}';`,\n\t\t),\n\t];\n\n\tconst url = (id: string) =>\n\t\t`manifest.env[${JSON.stringify(provideKey(id, 'url'))}]!`;\n\n\tconst members = [\n\t\t...surfaces.map(\n\t\t\t({ id, secured }) => `\t/** \\`${id}\\`, at ${provideKey(id, 'url')}. */\n\treadonly ${propertyOf(id)} = create${id}({\n\t\tbaseURL: ${url(id)},\n\t\tfetch: this.fetch,${\n\t\t\tsecured\n\t\t\t\t? `\n\t\t// Served in-process: no authorizer runs, so nothing is signed. A scheme\n\t\t// with no strategy sends its request without credentials.\n\t\tauthStrategies: {} as never,`\n\t\t\t\t: ''\n\t\t}\n\t});`,\n\t\t),\n\t\t...auths.map(\n\t\t\t(auth) => `\t/** \\`${auth.id}\\`, at ${provideKey(auth.id, 'url')}. */\n\treadonly ${propertyOf(auth.id)} = createAuthClient({\n\t\tbaseURL: \\`\\${${url(auth.id)}}${auth.basePath}\\`,\n\t\tplugins: [${auth.plugins.map((plugin) => `${plugin}()`).join(', ')}],\n\t\tfetchOptions: { customFetchImpl: this.fetch },\n\t});`,\n\t\t),\n\t\t...(signIn\n\t\t\t? [\n\t\t\t\t\t`\t/**\n\t * Sign in as \\`email\\` through \\`${signIn.id}\\`'s magic link, the way a person\n\t * does — the email opened, the link followed. The session it then reports.\n\t * Without an address, as somebody new: \\`user.email\\` says who.\n\t */\n\tsignIn(email?: string) {\n\t\treturn signInWithMagicLink(this, this.${propertyOf(signIn.id)}, email);\n\t}`,\n\t\t\t\t]\n\t\t\t: []),\n\t];\n\n\t// Each database's factory, keyed as the test is handed it.\n\tconst factoriesType = factories.length\n\t\t? `{ ${factories.map(({ service }) => `${service}: typeof __${service}Factory`).join('; ')} }`\n\t\t: '';\n\tconst factoriesOption = factories.length\n\t\t? `, factories: { ${factories.map(({ service }) => `${service}: __${service}Factory`).join(', ')} }`\n\t\t: '';\n\tconst fakesOption = fakes.length\n\t\t? `, fakes: { ${fakes.map(({ id }) => `${id}: __${id}Fake`).join(', ')} }`\n\t\t: '';\n\t// Each database's schema, keyed as the test is handed its transaction.\n\tconst databasesType = `{ ${databases.map(({ id, service }) => `${service}: DatabaseOf<typeof __${id}>`).join('; ')} }`;\n\t// Each construct's client, keyed as `services.get(…)` is called.\n\tconst servicesType = services.length\n\t\t? `{ ${services.map(({ id, service }) => `${service}: ClientOf<typeof __${id}>`).join('; ')} }`\n\t\t: '';\n\t// Positional, so a later one needs those before it, even when empty.\n\tconst generics =\n\t\tdatabases.length || factoriesType || servicesType\n\t\t\t? `<Browser, ${databases.length ? databasesType : '{}'}${\n\t\t\t\t\tfactoriesType || servicesType ? `, ${factoriesType || '{}'}` : ''\n\t\t\t\t}${servicesType ? `, ${servicesType}` : ''}>`\n\t\t\t: '';\n\n\treturn `// Generated by \\`gkm test\\` from the app's constructs — do not edit.\n${imports.join('\\n')}\n\n${drivers.setup ? `// The drivers the app's server entry registers, registered the same way.\\n${drivers.setup}\\n\\n` : ''}const manifest = loadTestManifest(new URL('./manifest.json', import.meta.url));\n\n/** A browser with a client for every surface the app declares. */\nexport class Browser extends TestBrowser {\n${members.join('\\n\\n')}\n}\n\n/** The app's modules, by the path the manifest records for each. */\nconst modules = {\n${files.map((file, index) => `\\t${JSON.stringify(file)}: __module${index},`).join('\\n')}\n};\n\n/** \\`it\\`, for a test that drives the app the way it runs deployed. */\nexport const it = featureTest${generics}({ manifest, modules, browser: Browser${factoriesOption}${fakesOption} });\n`;\n}\n","/**\n * The hand-off between `gkm test` and the Vitest global setup.\n *\n * `gkm test` reconciles and migrates the test stage before Vitest starts; the\n * setup, run inside Vitest, only has to load what that left. Started by plain\n * `vitest` instead, the setup runs `gkm test --setup` itself first — so there is\n * one way the stage gets ready, whichever started the suite.\n */\n\n/** Set on the Vitest process by `gkm test`: the stage is ready, here. */\nexport const TEST_READY_ENV = 'GKM_TEST_READY';\n\n/** Where `gkm test` records what it prepared, from where it ran. */\nexport const TEST_READY_FILE = '.gkm/test-ready.json';\n\nexport interface TestReady {\n\t/** The resolved environment, as a JSON object of strings. */\n\tenv: string;\n\t/** The test manifest the `#test` harness reads. */\n\tmanifest?: string;\n\t/**\n\t * The test stage's databases this run created, and the port of the\n\t * Postgres that holds them: what `gkm test --teardown` drops when the\n\t * suite ends — or, if a run was killed before its teardown, what the next\n\t * one drops before it starts.\n\t */\n\tdatabases?: { port: number; names: string[] };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,MAAa,oBAAoB;;AAGjC,MAAa,mBAAmB;;;;;;;;;;;AAYhC,MAAM,iBAAmD;CACxD,OAAO;CACP,WAAW;CACX,wBAAwB;CACxB,aAAa;CACb,KAAK;CACL,qBAAqB;CACrB,cAAc;CACd,iBAAiB;CACjB,eAAe;CACf,WAAW;CACX,kBAAkB;CAClB,cAAc;CACd,gBAAgB;CAChB,MAAM;CACN,cAAc;CACd,UAAU;AACX;;AAoCA,MAAa,wBAAwB;;AAGrC,IAAa,qBAAb,cAAwCA,eAAAA,SAAS;CAEtC;CACA;CAFV,YACC,MACA,OACC;EACD,MACC,GAAG,KAAK,kEACN,MAAM,SAAS,IACb,kBAAkB,MAAM,KAAK,IAAI,EAAE,MACnC,yCACH,mEACF;EATS,KAAA,OAAA;EACA,KAAA,QAAA;EAST,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,qBAAb,cAAwCA,eAAAA,SAAS;CAC3B;CAArB,YAAY,MAAuB;EAClC,MACC,GAAG,KAAK,oIAGT;EALoB,KAAA,OAAA;EAMpB,KAAK,OAAO;CACb;AACD;;;;;;;AAcA,eAAsB,eACrB,QACA,WAC2B;CAC3B,IAAI,CAAC,QAAQ,OAAO,CAAC;CAErB,MAAM,UAAU,OAAA,GAAMC,iBAAAA,QAAAA,CAAQ,QAAQ,EAAE,eAAe,KAAK,CAAC,CAAC,CAAC,OAC7D,UAAiC;EACjC,IAAI,MAAM,SAAS,UAAU,OAAO,CAAC;EACrC,MAAM;CACP,CACD;CAEA,MAAM,QAAyB,CAAC;CAChC,KAAK,MAAM,SAAS,SAAS;EAC5B,IAAI,CAAC,MAAM,OAAO,KAAK,CAAC,cAAc,MAAM,IAAI,GAAG;EAEnD,MAAM,QAAA,GAAOC,UAAAA,KAAAA,CAAK,QAAQ,MAAM,IAAI;EACpC,MAAM,MAAA,GAAKC,oBAAAA,YAAAA,CAAY,MAAM,KAAK,MAAM,GAAG,EAAA,GAACC,UAAAA,QAAAA,CAAQ,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC;EACvE,IAAI,CAAC,UAAU,SAAS,EAAE,GACzB,MAAM,IAAI,mBAAmB,MAAM,SAAS;EAM7C,IAAI,QAAO,MAHW,QAAA,GAAOC,SAAAA,cAAAA,CAAc,IAAI,CAAC,CAAC,MAAA,CAG/B,kBAAkB,YACnC,MAAM,IAAI,mBAAmB,IAAI;EAGlC,MAAM,KAAK;GAAE;GAAM,UAAA,GAASC,oBAAAA,WAAAA,CAAW,EAAE;EAAE,CAAC;CAC7C;CAEA,OAAO,MAAM,MAAM,GAAG,MAAO,EAAE,UAAU,EAAE,UAAU,KAAK,CAAE;AAC7D;AAEA,SAAS,cAAc,MAAuB;CAC7C,IAAI,KAAK,SAAS,OAAO,GAAG,OAAO;CACnC,IAAI,6BAA6B,KAAK,IAAI,GAAG,OAAO;CACpD,OAAO;EAAC;EAAO;EAAQ;EAAO;CAAM,CAAC,CAAC,UAAA,GAASF,UAAAA,QAAAA,CAAQ,IAAI,CAAC;AAC7D;;AAGA,eAAsB,iBACrB,SACkB;CAClB,MAAM,UAA2C,CAAC;CAClD,MAAM,WAAW,MAAMG,iBAAAA,SAAS;EAC/B,UAAU,QAAQ;EAClB,KAAK,QAAQ;EACb;CACD,CAAC;CAMD,MAAM,aAAY,MAJG,IAAIC,0BAAAA,kBAAkB,CAAC,CAAC,KAC5C,QAAQ,UACR,QAAQ,IACT,EAAA,CAEE,KAAK,EAAE,KAAK,WAAW,YAAY;EACnC,SAAS,UAAU,SAAS,UAAU,SAAS;EAC/C;EACA,QAAQ;GAAE,MAAM,KAAK;GAAU,QAAQ;EAAI;CAC5C,EAAE,CAAC,CACF,QACC,aACA,SAAS,YAAY,KAAA,CACvB;CAID,MAAM,eACL,MAAM,IAAIC,4BAAAA,oBAAoB,CAAC,CAAC,KAAK,QAAQ,UAAU,QAAQ,IAAI,EAAA,CAClE,KAAK,EAAE,KAAK,YAAY,EAAE,QAAQ;EAAE,MAAM,KAAK;EAAU,QAAQ;CAAI,EAAE,EAAE;CAE3E,MAAM,WAAyB;EAC9B,OAAO,QAAQ;EACf,YAAY,OAAO,YAClB,OAAO,QAAQ,QAAQ,CAAC,CAAC,SAAS,CAAC,IAAI,iBAAiB;GACvD,MAAM,SAAS,QAAQ;GACvB,OAAO,SACJ,CACA,CACC,IACA;IACC,MAAM,YAAY;IAClB,QAAQ;KAAE,MAAM,OAAO;KAAM,QAAQ,OAAO;IAAW;GACxD,CACD,CACD,IACC,CAAC;EACL,CAAC,CACF;EACA,WAAW,UAAU,KAAK,EAAE,SAAS,cAAc;GAAE;GAAS;EAAO,EAAE;EACvE;EACA,KAAK,QAAQ;CACd;CAEA,MAAM,WAAW,CAAC,GAAG,IAAI,IAAI,UAAU,KAAK,EAAE,cAAc,OAAO,CAAC,CAAC;CACrE,MAAM,YAAY,IAAIC,2BAAAA,mBAAmB;CACzC,MAAM,UAAU,MAAM,QAAQ,IAC7B,SAAS,IAAI,OAAO,aAAa;EAChC,MAAM,IAAA,GAAGC,oBAAAA,UAAAA,CAAU,OAAO,EAAE;EAC5B,SAAS,MAAM,UAAU,SACxB,UACE,QAAQ,aAAa,SAAS,YAAY,OAAO,CAAC,CAClD,KAAK,EAAE,gBAAgB,SAAyC,GAClE;GAAE,OAAO;GAAS,SAAS;EAAQ,CACpC;CACD,EAAE,CACH;CAEA,MAAM,QAAQ,MAAM,YAAY,UAAU,SAAS,QAAQ,GAAG;CAG9D,MAAM,UAAUC,gBAAAA,WAAW;EAC1B,SAAS,QAAQ;EACjB,OAAOC,gBAAAA,gBAAgB,UAAU,QAAQ,YAAY;EACrD,QAAQC,gBAAAA,iBAAiB,UAAU,QAAQ,aAAa;CACzD,CAAC;CAID,MAAM,SAAS,IAAI,IAAI,MAAM,KAAK,SAAS,KAAK,UAAU,CAAC;CAC3D,MAAM,YAAY,gBAAgB,UAAU,OAAO,CAAC,CAAC,QACnD,EAAE,IAAI,WAAW,CAAC,OAAO,IAAI,EAAE,KAAK,SAAS,iBAC/C;CACA,MAAM,YAAY,MAAM,eACvB,QAAQ,WACR,UAAU,KAAK,EAAE,SAAS,EAAE,CAC7B;CAGA,MAAM,QAAQ,OAAO,QAAQ,MAAMC,4BAAAA,UAAU,QAAQ,MAAM,QAAQ,CAAC,CAAC,CAAC,KACpE,CAAC,IAAI,EAAE,aAAa;EAAE;EAAI;CAAK,EACjC;CAIA,MAAM,WAAW,eAAe,UAAU,OAAO,CAAC,CAAC,QACjD,EAAE,SAAS,CAAC,OAAO,IAAI,EAAE,CAC3B;CACA,MAAM,QAAQ,CACb,mBAAG,IAAI,IAAI;EACV,GAAG,OAAO,OAAO,SAAS,UAAU,CAAC,CAAC,KAAK,EAAE,aAAa,OAAO,IAAI;EACrE,GAAG,SAAS,UAAU,KAAK,EAAE,aAAa,OAAO,IAAI;EAIrD,IAAI,SAAS,eAAe,CAAC,EAAA,CAAG,KAAK,EAAE,aAAa,OAAO,IAAI;CAChE,CAAC,CACF;CACA,MAAM,cAAc,QACnB,cAAc;EACb;EACA;EACA;EACA,UAAU,SAAS,KAAK,IAAI,WAAW;GACtC;GAGA,SAAS,QAAQ,MAAM,CAAE,QAAQ,SAAS,iBAAiB;EAC5D,EAAE;EACF;EACA;EACA;EACA;EACA;EACA,MAAM,OAAO,OAAO,QAAQ,CAAC,CAAC,MAAM,EAAE,WAAW,SAAS,OAAO;CAClE,CAAC;CACF,MAAM,OAAO,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,EAAE;CAkBlD,QAAO,MAhBe,QAAQ,IAC7B,CAAC,GAAG,IAAI,IAAI,QAAQ,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,WAAW;EACnD,MAAM,OAAA,GAAMb,UAAAA,KAAAA,CAAK,QAAQ,gBAAgB;EAGzC,OAAA,GAAMc,iBAAAA,GAAAA,CAAG,KAAK;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;EAC9C,OAAA,GAAMC,iBAAAA,MAAAA,EAAAA,GAAMf,UAAAA,KAAAA,CAAK,KAAK,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;EACrD,OAAA,GAAMgB,iBAAAA,UAAAA,EAAAA,GAAUhB,UAAAA,KAAAA,CAAK,KAAK,eAAe,GAAG,IAAI;EAChD,OAAA,GAAMgB,iBAAAA,UAAAA,EAAAA,GAAUhB,UAAAA,KAAAA,CAAK,KAAK,UAAU,GAAG,WAAW,GAAG,CAAC;EACtD,KAAK,MAAM,UAAU,SACpB,OAAA,GAAMgB,iBAAAA,UAAAA,EAAAA,GAAUhB,UAAAA,KAAAA,CAAK,KAAK,WAAW,OAAO,IAAI,GAAG,OAAO,OAAO;EAElE,QAAA,GAAOA,UAAAA,KAAAA,CAAK,KAAK,eAAe;CACjC,CAAC,CACF,EAAA,CAEe;AAChB;;AAWA,eAAe,YACd,UACA,SACA,KACwB;CACxB,MAAM,UAAwB,CAAC;CAC/B,MAAM,YAAY,IAAIiB,kBAAAA,kBAAkB,EAAE,GAAG,IAAI,CAAC;CAElD,KAAK,MAAM,CAAC,IAAI,gBAAgB,OAAO,QAAQ,QAAQ,GAAG;EACzD,MAAM,SAAS,QAAQ;EACvB,IAAI,YAAY,SAAS,cAAc,CAAC,QAAQ;EAEhD,MAAM,YAAY,MAAM,OAAO,OAAO,MAAA,CAAO,OAAO;EACpD,IAAI,CAAC,aAAa,QAAQ,KAAK,SAAS,OAAO,IAAI;EAEnD,MAAM,MAAM,MAAM,SAAS,UAAU;GACpC;GACA,SAASC,oBAAAA;EACV,CAAC;EACD,QAAQ,KAAK;GACZ;GACA,UAAU,SAAS;GACnB,SAAS,IAAI,SAAS,WAAW,eAAe,WAAW,CAAC,CAAC;GAC7D,YAAY,SAAS;EACtB,CAAC;CACF;CAEA,OAAO;AACR;AAEA,SAAS,aAAa,OAAqC;CAC1D,OACC,OAAO,UAAU,YACjB,UAAU,QACV,eAAe,SACf,cAAc;AAEhB;;AAGA,SAAS,WAAW,IAAoB;CACvC,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,GAAG,MAAM,CAAC;AAC/C;;;;;AAoBA,SAAS,gBACR,UACA,SACmB;CACnB,OAAO,OAAO,QAAQ,QAAQ,CAAC,CAC7B,QAAQ,CAAC,IAAI,iBACb,QAAQ,QAAQ,OAAO,YAAY,KAAK,WAAW,UAAU,CAAC,CAC/D,CAAC,CACA,KAAK,CAAC,IAAI,kBAAkB;EAC5B;EACA,MAAM,YAAY;EAClB,UAAA,GAASd,oBAAAA,WAAAA,CAAW,EAAE;EACtB,QAAQ,QAAQ;CACjB,EAAE;AACJ;;AAGA,MAAM,gCAAgB,IAAI,IAAI;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;AAED,SAAS,eACR,UACA,SACmB;CACnB,OAAO,OAAO,QAAQ,QAAQ,CAAC,CAC7B,QACC,CAAC,IAAI,iBACL,cAAc,IAAI,YAAY,IAAI,MAIjC,QAAQ,GAAG,EAAE,UAAA,EAA2C,OAAO,EAClE,CAAC,CACA,KAAK,CAAC,IAAI,kBAAkB;EAC5B;EACA,MAAM,YAAY;EAClB,UAAA,GAASA,oBAAAA,WAAAA,CAAW,EAAE;EACtB,QAAQ,QAAQ;CACjB,EAAE;AACJ;;AAGA,SAAS,cAAc,KAAa,MAAsB;CACzD,MAAM,QAAA,GAAOe,UAAAA,SAAAA,CAAS,KAAK,IAAI,CAAC,CAAC,QAAQ,WAAW,KAAK;CACzD,OAAO,KAAK,WAAW,GAAG,IAAI,OAAO,KAAK;AAC3C;AAEA,SAAS,cAAc,SAeZ;CACV,MAAM,EACL,UACA,OACA,SACA,WACA,UACA,KACA,OACA,WACA,OACA,SACG;CAIJ,MAAM,aAAa,OAChB,MAAM,QAAQ,EAAE,cAAc,QAAQ,SAAS,iBAAiB,CAAC,IACjE,CAAC;CACJ,MAAM,SAAS,WAAW,WAAW,IAAI,WAAW,KAAK,KAAA;CACzD,MAAM,UAAU,CAAC,GAAG,IAAI,IAAI,MAAM,SAAS,EAAE,cAAc,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK;CAE3E,MAAM,UAAU;EACf,WAAW,SAAS,SAAS,oBAAoB,KAAK,UAAU,SAAS,sBAAsB,GAAG,gCAAgC,SAAS,0BAA0B,GAAG;EAIxK,GAAG,CACF,GAAG,IAAI,IACN,CAAC,GAAG,WAAW,GAAG,QAAQ,CAAC,CAAC,KAAK,cAAc,CAC9C,UAAU,IACV,SACD,CAAC,CACF,CAAC,CAAC,OAAO,CACV,CAAC,CAAC,KACA,EAAE,IAAI,aACN,iBAAiB,OAAO,WAAW,QAAQ,GAAG,WAAW,cAAc,KAAK,OAAO,IAAI,EAAE,GAC3F;EACA;EACA,GAAG,SAAS,KACV,EAAE,SACF,+BAA+B,GAAG,sBAAA,GAAqBV,oBAAAA,UAAAA,CAAU,EAAE,EAAE,MACvE;EACA,GAAI,MAAM,SACP,CAAC,wDAAwD,IACzD,CAAC;EACJ,GAAI,QAAQ,SACT,CAAC,YAAY,QAAQ,KAAK,IAAI,EAAE,sCAAsC,IACtE,CAAC;EACJ,GAAI,QAAQ,UAAU,CAAC,QAAQ,OAAO,IAAI,CAAC;EAC3C,GAAG,UAAU,KACX,EAAE,MAAM,cACR,+BAA+B,QAAQ,kBAAkB,cAAc,KAAK,IAAI,EAAE,GACpF;EACA,GAAG,MAAM,KACP,EAAE,IAAI,WACN,YAAY,GAAG,aAAa,cAAc,KAAK,IAAI,EAAE,GACvD;EAGA,GAAG,MAAM,KACP,MAAM,UACN,uBAAuB,MAAM,SAAS,cAAc,KAAK,IAAI,EAAE,GACjE;CACD;CAEA,MAAM,OAAO,OACZ,gBAAgB,KAAK,WAAA,GAAUW,oBAAAA,WAAAA,CAAW,IAAI,KAAK,CAAC,EAAE;CAEvD,MAAM,UAAU;EACf,GAAG,SAAS,KACV,EAAE,IAAI,cAAc,UAAU,GAAG,UAAA,GAASA,oBAAAA,WAAAA,CAAW,IAAI,KAAK,EAAE;YACxD,WAAW,EAAE,EAAE,WAAW,GAAG;aAC5B,IAAI,EAAE,EAAE;sBAElB,UACG;;;kCAIA,GACH;KAED;EACA,GAAG,MAAM,KACP,SAAS,UAAU,KAAK,GAAG,UAAA,GAASA,oBAAAA,WAAAA,CAAW,KAAK,IAAI,KAAK,EAAE;YACvD,WAAW,KAAK,EAAE,EAAE;kBACd,IAAI,KAAK,EAAE,EAAE,GAAG,KAAK,SAAS;cAClC,KAAK,QAAQ,KAAK,WAAW,GAAG,OAAO,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE;;KAGnE;EACA,GAAI,SACD,CACA;qCACgC,OAAO,GAAG;;;;;0CAKL,WAAW,OAAO,EAAE,EAAE;GAE5D,IACC,CAAC;CACL;CAGA,MAAM,gBAAgB,UAAU,SAC7B,KAAK,UAAU,KAAK,EAAE,cAAc,GAAG,QAAQ,aAAa,QAAQ,QAAQ,CAAC,CAAC,KAAK,IAAI,EAAE,MACzF;CACH,MAAM,kBAAkB,UAAU,SAC/B,kBAAkB,UAAU,KAAK,EAAE,cAAc,GAAG,QAAQ,MAAM,QAAQ,QAAQ,CAAC,CAAC,KAAK,IAAI,EAAE,MAC/F;CACH,MAAM,cAAc,MAAM,SACvB,cAAc,MAAM,KAAK,EAAE,SAAS,GAAG,GAAG,MAAM,GAAG,KAAK,CAAC,CAAC,KAAK,IAAI,EAAE,MACrE;CAEH,MAAM,gBAAgB,KAAK,UAAU,KAAK,EAAE,IAAI,cAAc,GAAG,QAAQ,wBAAwB,GAAG,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE;CAEnH,MAAM,eAAe,SAAS,SAC3B,KAAK,SAAS,KAAK,EAAE,IAAI,cAAc,GAAG,QAAQ,sBAAsB,GAAG,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE,MAC1F;CAEH,MAAM,WACL,UAAU,UAAU,iBAAiB,eAClC,aAAa,UAAU,SAAS,gBAAgB,OAChD,iBAAiB,eAAe,KAAK,iBAAiB,SAAS,KAC7D,eAAe,KAAK,iBAAiB,GAAG,KAC1C;CAEJ,OAAO;EACN,QAAQ,KAAK,IAAI,EAAE;;EAEnB,QAAQ,QAAQ,8EAA8E,QAAQ,MAAM,QAAQ,GAAG;;;;EAIvH,QAAQ,KAAK,MAAM,EAAE;;;;;EAKrB,MAAM,KAAK,MAAM,UAAU,KAAK,KAAK,UAAU,IAAI,EAAE,YAAY,MAAM,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE;;;;+BAIzD,SAAS,wCAAwC,kBAAkB,YAAY;;AAE9G;;;;;;;;;;;;AC7nBA,MAAa,iBAAiB;;AAG9B,MAAa,kBAAkB"}
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_discover = require("../discover-DozUKs8f.cjs");
3
- const require_emit = require("../emit-Dmc1nvdS.cjs");
2
+ const require_discover = require("../discover-ojkcbxHw.cjs");
3
+ const require_emit = require("../emit-qvHxMV9b.cjs");
4
4
  exports.discover = require_discover.discover;
5
5
  exports.isDeclarable = require_discover.isDeclarable;
6
6
  exports.manifestModule = require_emit.manifestModule;
@@ -1,5 +1,4 @@
1
- import { n as EmailBackend, t as CacheBackend } from "../types-Q2SC_ly2.cjs";
2
- import { a as isDeclarable, i as discover, r as DiscoverOptions } from "../discover-DipvfWpK.cjs";
1
+ import { at as discover, it as DiscoverOptions, n as EmailBackend, ot as isDeclarable, t as CacheBackend } from "../types-CFU2uvrz.cjs";
3
2
  import { ConstructManifest, RouteInfo } from "@geekmidas/manifest";
4
3
  //#region src/reconcile/emit.d.ts
5
4
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.cts","names":[],"sources":["../../src/reconcile/emit.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA6DgB,WACf,UAAU,mBACV,iBAAiB,aACjB;EAAW;EAAmB;IAC5B;;;;;;wBA4Ja,eACf,UAAU,mBACV,WAAU;;UA8BM;EAChB,QAAQ;EACR,QAAQ"}
1
+ {"version":3,"file":"public.d.cts","names":[],"sources":["../../src/reconcile/emit.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA6DgB,WACf,UAAU,mBACV,iBAAiB,aACjB;EAAW;EAAmB;IAC5B;;;;;;wBAmLa,eACf,UAAU,mBACV,WAAU;;UA8BM;EAChB,QAAQ;EACR,QAAQ"}
@@ -1,5 +1,4 @@
1
- import { n as EmailBackend, t as CacheBackend } from "../types-C8gXOLWL.mjs";
2
- import { a as isDeclarable, i as discover, r as DiscoverOptions } from "../discover-C08meHhu.mjs";
1
+ import { at as discover, it as DiscoverOptions, n as EmailBackend, ot as isDeclarable, t as CacheBackend } from "../types-D3BeGucy.mjs";
3
2
  import { ConstructManifest, RouteInfo } from "@geekmidas/manifest";
4
3
  //#region src/reconcile/emit.d.ts
5
4
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.mts","names":[],"sources":["../../src/reconcile/emit.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA6DgB,WACf,UAAU,mBACV,iBAAiB,aACjB;EAAW;EAAmB;IAC5B;;;;;;wBA4Ja,eACf,UAAU,mBACV,WAAU;;UA8BM;EAChB,QAAQ;EACR,QAAQ"}
1
+ {"version":3,"file":"public.d.mts","names":[],"sources":["../../src/reconcile/emit.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA6DgB,WACf,UAAU,mBACV,iBAAiB,aACjB;EAAW;EAAmB;IAC5B;;;;;;wBAmLa,eACf,UAAU,mBACV,WAAU;;UA8BM;EAChB,QAAQ;EACR,QAAQ"}
@@ -1,3 +1,3 @@
1
- import { i as discover, o as isDeclarable } from "../discover-C5roJuc9.mjs";
2
- import { r as withRoutes, t as manifestModule } from "../emit-C1e8qD08.mjs";
1
+ import { i as discover, o as isDeclarable } from "../discover-BvJOVsYG.mjs";
2
+ import { r as withRoutes, t as manifestModule } from "../emit-CNpuV_6Q.mjs";
3
3
  export { discover, isDeclarable, manifestModule, withRoutes };
@@ -200,9 +200,9 @@ var InvalidTargetOptions = class extends GkmError {
200
200
  };
201
201
  /** The built-ins `gkm deploy` deploys through, loaded only when used. */
202
202
  const BUILTIN_LOADERS = {
203
- dokploy: async () => (await import("./dokploy-BcZm93Cc.mjs")).dokployTarget,
204
- compose: async () => (await import("./compose-cHkF---f.mjs").then((n) => n.n)).composeTarget(),
205
- sst: async () => (await import("./sst-C1pxbFEd.mjs").then((n) => n.n)).sstTarget
203
+ dokploy: async () => (await import("./dokploy-MM7c3IE0.mjs")).dokployTarget,
204
+ compose: async () => (await import("./compose-DJvvmF3X.mjs").then((n) => n.n)).composeTarget(),
205
+ sst: async () => (await import("./sst-CTqZZl1_.mjs").then((n) => n.n)).sstTarget
206
206
  };
207
207
  /** The target `name` means, in this workspace, for this host. */
208
208
  async function resolveTarget(name, { workspace, host, stage }) {
@@ -260,4 +260,4 @@ async function parseTargetOptions(resolved) {
260
260
  //#endregion
261
261
  export { parseTargetOptions as a, runtimeOf as c, TargetRuntimeMismatch as i, TargetPackageNotFound as l, TargetEntryInvalid as n, resolveTarget as o, TargetPackageInvalid as r, TargetRuntimeUndeclared as s, InvalidTargetOptions as t };
262
262
 
263
- //# sourceMappingURL=resolve-j8hUcbGz.mjs.map
263
+ //# sourceMappingURL=resolve-BT7fkPN5.mjs.map