@geekmidas/constructs 10.0.0-alpha.2 → 10.0.0-alpha.21

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 (333) hide show
  1. package/README.md +46 -3
  2. package/dist/{Construct-Ct8nalEi.d.mts → Construct-DTqYvkPG.d.cts} +11 -13
  3. package/dist/Construct-DTqYvkPG.d.cts.map +1 -0
  4. package/dist/{Construct-BD5neF7w.mjs → Construct-Detz65RA.mjs} +31 -29
  5. package/dist/Construct-Detz65RA.mjs.map +1 -0
  6. package/dist/{Construct-C0ijr2qp.d.cts → Construct-pVadmURD.d.mts} +10 -12
  7. package/dist/Construct-pVadmURD.d.mts.map +1 -0
  8. package/dist/{Construct-CyS3RpTL.cjs → Construct-yG9Sw-5p.cjs} +51 -43
  9. package/dist/Construct-yG9Sw-5p.cjs.map +1 -0
  10. package/dist/{Cron-ByxdOfpE.d.mts → Cron-AYpQq725.d.mts} +13 -6
  11. package/dist/Cron-AYpQq725.d.mts.map +1 -0
  12. package/dist/{Cron-Wqh464aO.d.cts → Cron-Bdcg9OAL.d.cts} +12 -5
  13. package/dist/Cron-Bdcg9OAL.d.cts.map +1 -0
  14. package/dist/{CronBuilder-BL0AP6fs.d.mts → CronBuilder-DR21m9h2.d.cts} +7 -10
  15. package/dist/CronBuilder-DR21m9h2.d.cts.map +1 -0
  16. package/dist/{CronBuilder-BqpEzMPQ.d.cts → CronBuilder-DeBnPVIT.d.mts} +7 -10
  17. package/dist/CronBuilder-DeBnPVIT.d.mts.map +1 -0
  18. package/dist/{CronBuilder-BmABazzH.mjs → CronBuilder-vkjuo1UP.mjs} +23 -15
  19. package/dist/CronBuilder-vkjuo1UP.mjs.map +1 -0
  20. package/dist/{CronBuilder-CkXPJiGO.cjs → CronBuilder-zdTdaT-e.cjs} +33 -25
  21. package/dist/CronBuilder-zdTdaT-e.cjs.map +1 -0
  22. package/dist/{Endpoint-2Wg7T7Cq.d.mts → Endpoint-BxkRWQ8G.d.mts} +63 -84
  23. package/dist/Endpoint-BxkRWQ8G.d.mts.map +1 -0
  24. package/dist/{Endpoint-BiaaMEn_.cjs → Endpoint-CtiPUpRX.cjs} +145 -131
  25. package/dist/Endpoint-CtiPUpRX.cjs.map +1 -0
  26. package/dist/{Endpoint-VIOpSo0S.mjs → Endpoint-DCr5tlbJ.mjs} +117 -109
  27. package/dist/Endpoint-DCr5tlbJ.mjs.map +1 -0
  28. package/dist/{Endpoint-CH9Fn1AD.d.cts → Endpoint-DbYQJgb-.d.cts} +61 -82
  29. package/dist/Endpoint-DbYQJgb-.d.cts.map +1 -0
  30. package/dist/{EndpointFactory-DTTCkGEL.mjs → EndpointFactory-Bs8pu-yY.mjs} +20 -29
  31. package/dist/EndpointFactory-Bs8pu-yY.mjs.map +1 -0
  32. package/dist/{index-BxHDdy6a.d.mts → EndpointFactory-DE6nKtFZ.d.cts} +12 -92
  33. package/dist/EndpointFactory-DE6nKtFZ.d.cts.map +1 -0
  34. package/dist/{index-V2nksZIJ.d.cts → EndpointFactory-T06kTFHg.d.mts} +12 -92
  35. package/dist/EndpointFactory-T06kTFHg.d.mts.map +1 -0
  36. package/dist/{EndpointFactory-ChxLZllA.cjs → EndpointFactory-vE2C1NG1.cjs} +57 -66
  37. package/dist/EndpointFactory-vE2C1NG1.cjs.map +1 -0
  38. package/dist/{HonoEndpointAdaptor-NKQQQbBY.mjs → HonoEndpointAdaptor-Be6UdK0r.mjs} +40 -47
  39. package/dist/{HonoEndpointAdaptor-D233Jvpk.cjs.map → HonoEndpointAdaptor-Be6UdK0r.mjs.map} +1 -1
  40. package/dist/{HonoEndpointAdaptor-D233Jvpk.cjs → HonoEndpointAdaptor-C9aivwKl.cjs} +48 -52
  41. package/dist/HonoEndpointAdaptor-C9aivwKl.cjs.map +1 -0
  42. package/dist/{Queue-D7ZuwBhy.d.cts → Queue-Bhzi2WQC.d.cts} +8 -9
  43. package/dist/Queue-Bhzi2WQC.d.cts.map +1 -0
  44. package/dist/{Queue-gVGfi2Yk.d.mts → Queue-BoUA-e83.d.mts} +9 -10
  45. package/dist/Queue-BoUA-e83.d.mts.map +1 -0
  46. package/dist/{Subscriber-2mEi0ImH.d.cts → Subscriber-CIO3pwji.d.mts} +7 -8
  47. package/dist/Subscriber-CIO3pwji.d.mts.map +1 -0
  48. package/dist/{Subscriber-xTJ9ayPN.d.mts → Subscriber-DQCaiOXF.d.cts} +7 -8
  49. package/dist/Subscriber-DQCaiOXF.d.cts.map +1 -0
  50. package/dist/{SubscriberBuilder-BjkbeT9l.mjs → SubscriberBuilder-B4YRTBng.mjs} +16 -10
  51. package/dist/SubscriberBuilder-B4YRTBng.mjs.map +1 -0
  52. package/dist/{SubscriberBuilder-4OPehf2R.cjs → SubscriberBuilder-C2bCOuPT.cjs} +28 -23
  53. package/dist/SubscriberBuilder-C2bCOuPT.cjs.map +1 -0
  54. package/dist/{SubscriberBuilder-Dptmo-1A.d.mts → SubscriberBuilder-ChX5v948.d.cts} +8 -11
  55. package/dist/SubscriberBuilder-ChX5v948.d.cts.map +1 -0
  56. package/dist/{SubscriberBuilder-waNhzjSM.d.cts → SubscriberBuilder-Dbiaskfi.d.mts} +8 -11
  57. package/dist/SubscriberBuilder-Dbiaskfi.d.mts.map +1 -0
  58. package/dist/{Topic-19QeHBIR.d.cts → Topic-D6blllEn.d.cts} +5 -7
  59. package/dist/Topic-D6blllEn.d.cts.map +1 -0
  60. package/dist/{Topic-DuFeqkBh.d.mts → Topic-DVvheJ_-.d.mts} +6 -8
  61. package/dist/Topic-DVvheJ_-.d.mts.map +1 -0
  62. package/dist/adaptors/aws.cjs +87 -106
  63. package/dist/adaptors/aws.cjs.map +1 -1
  64. package/dist/adaptors/aws.d.cts +14 -27
  65. package/dist/adaptors/aws.d.cts.map +1 -1
  66. package/dist/adaptors/aws.d.mts +16 -29
  67. package/dist/adaptors/aws.d.mts.map +1 -1
  68. package/dist/adaptors/aws.mjs +60 -81
  69. package/dist/adaptors/aws.mjs.map +1 -1
  70. package/dist/adaptors/hono.cjs +3 -10
  71. package/dist/adaptors/hono.d.cts +5 -13
  72. package/dist/adaptors/hono.d.cts.map +1 -1
  73. package/dist/adaptors/hono.d.mts +7 -15
  74. package/dist/adaptors/hono.d.mts.map +1 -1
  75. package/dist/adaptors/hono.mjs +2 -10
  76. package/dist/adaptors/testing.cjs +271 -82
  77. package/dist/adaptors/testing.cjs.map +1 -1
  78. package/dist/adaptors/testing.d.cts +62 -33
  79. package/dist/adaptors/testing.d.cts.map +1 -1
  80. package/dist/adaptors/testing.d.mts +63 -34
  81. package/dist/adaptors/testing.d.mts.map +1 -1
  82. package/dist/adaptors/testing.mjs +249 -63
  83. package/dist/adaptors/testing.mjs.map +1 -1
  84. package/dist/auth-BlO8MUkR.d.cts +123 -0
  85. package/dist/auth-BlO8MUkR.d.cts.map +1 -0
  86. package/dist/auth-CHWIdcQO.d.mts +123 -0
  87. package/dist/auth-CHWIdcQO.d.mts.map +1 -0
  88. package/dist/auth.cjs +41 -16
  89. package/dist/auth.cjs.map +1 -1
  90. package/dist/auth.d.cts +2 -108
  91. package/dist/auth.d.mts +2 -108
  92. package/dist/auth.mjs +32 -8
  93. package/dist/auth.mjs.map +1 -1
  94. package/dist/{cache-BrRz3fEB.d.mts → cache-AgQUMma6.d.mts} +3 -6
  95. package/dist/cache-AgQUMma6.d.mts.map +1 -0
  96. package/dist/{cache-BtZY_tHe.d.cts → cache-obq0fOIM.d.cts} +3 -6
  97. package/dist/cache-obq0fOIM.d.cts.map +1 -0
  98. package/dist/cache.cjs +68 -2
  99. package/dist/cache.cjs.map +1 -0
  100. package/dist/cache.d.cts +1 -2
  101. package/dist/cache.d.mts +1 -2
  102. package/dist/cache.mjs +67 -2
  103. package/dist/cache.mjs.map +1 -0
  104. package/dist/{clone-DblgEZZa.mjs → clone-EKNovYHu.mjs} +3 -3
  105. package/dist/clone-EKNovYHu.mjs.map +1 -0
  106. package/dist/{clone-Dy0cR2SZ.cjs → clone-UGJpvQnL.cjs} +7 -8
  107. package/dist/clone-UGJpvQnL.cjs.map +1 -0
  108. package/dist/{construct-interface-g5GM5pwR.d.mts → construct-interface-Clz7_BwB.d.mts} +15 -6
  109. package/dist/construct-interface-Clz7_BwB.d.mts.map +1 -0
  110. package/dist/{construct-interface-D9d4Gm4p.d.cts → construct-interface-DIaTNKax.d.cts} +15 -6
  111. package/dist/construct-interface-DIaTNKax.d.cts.map +1 -0
  112. package/dist/construct-interface.cjs +87 -7
  113. package/dist/construct-interface.cjs.map +1 -0
  114. package/dist/construct-interface.d.cts +2 -2
  115. package/dist/construct-interface.d.mts +2 -2
  116. package/dist/construct-interface.mjs +81 -2
  117. package/dist/construct-interface.mjs.map +1 -0
  118. package/dist/credential.cjs +43 -9
  119. package/dist/credential.cjs.map +1 -1
  120. package/dist/credential.d.cts +5 -8
  121. package/dist/credential.d.cts.map +1 -1
  122. package/dist/credential.d.mts +5 -8
  123. package/dist/credential.d.mts.map +1 -1
  124. package/dist/credential.mjs +36 -2
  125. package/dist/credential.mjs.map +1 -1
  126. package/dist/crons/index.cjs +68 -10
  127. package/dist/crons/index.cjs.map +1 -1
  128. package/dist/crons/index.d.cts +32 -14
  129. package/dist/crons/index.d.cts.map +1 -1
  130. package/dist/crons/index.d.mts +32 -14
  131. package/dist/crons/index.d.mts.map +1 -1
  132. package/dist/crons/index.mjs +66 -10
  133. package/dist/crons/index.mjs.map +1 -1
  134. package/dist/database/kysely.cjs +28 -14
  135. package/dist/database/kysely.cjs.map +1 -1
  136. package/dist/database/kysely.d.cts +2 -152
  137. package/dist/database/kysely.d.mts +2 -152
  138. package/dist/database/kysely.mjs +16 -4
  139. package/dist/database/kysely.mjs.map +1 -1
  140. package/dist/email.cjs +25 -12
  141. package/dist/email.cjs.map +1 -1
  142. package/dist/email.d.cts +3 -7
  143. package/dist/email.d.cts.map +1 -1
  144. package/dist/email.d.mts +3 -7
  145. package/dist/email.d.mts.map +1 -1
  146. package/dist/email.mjs +16 -3
  147. package/dist/email.mjs.map +1 -1
  148. package/dist/endpoints/index.cjs +7 -10
  149. package/dist/endpoints/index.d.cts +60 -7
  150. package/dist/endpoints/index.d.cts.map +1 -0
  151. package/dist/endpoints/index.d.mts +60 -7
  152. package/dist/endpoints/index.d.mts.map +1 -0
  153. package/dist/endpoints/index.mjs +5 -10
  154. package/dist/file-server.cjs +42 -10
  155. package/dist/file-server.cjs.map +1 -1
  156. package/dist/file-server.d.cts +6 -10
  157. package/dist/file-server.d.cts.map +1 -1
  158. package/dist/file-server.d.mts +6 -10
  159. package/dist/file-server.d.mts.map +1 -1
  160. package/dist/file-server.mjs +34 -2
  161. package/dist/file-server.mjs.map +1 -1
  162. package/dist/functions/index.cjs +2 -6
  163. package/dist/functions/index.d.cts +2 -4
  164. package/dist/functions/index.d.mts +2 -4
  165. package/dist/functions/index.mjs +2 -6
  166. package/dist/{functions-Bd3TPxid.cjs → functions-VnOTEp74.cjs} +309 -264
  167. package/dist/functions-VnOTEp74.cjs.map +1 -0
  168. package/dist/{functions-Cxbo-pEd.mjs → functions-faoJOUmr.mjs} +282 -234
  169. package/dist/functions-faoJOUmr.mjs.map +1 -0
  170. package/dist/index-CeEtxDmK.d.cts +79 -0
  171. package/dist/index-CeEtxDmK.d.cts.map +1 -0
  172. package/dist/index-CeEtxDmK.d.mts +79 -0
  173. package/dist/index-CeEtxDmK.d.mts.map +1 -0
  174. package/dist/{index-JSf9xNIJ.d.cts → index-DU3Twmwh.d.mts} +19 -24
  175. package/dist/index-DU3Twmwh.d.mts.map +1 -0
  176. package/dist/index-fnpjXiA-.d.mts +26 -0
  177. package/dist/index-fnpjXiA-.d.mts.map +1 -0
  178. package/dist/{index-CubPzHd9.d.mts → index-nBQT1tAh.d.cts} +19 -24
  179. package/dist/index-nBQT1tAh.d.cts.map +1 -0
  180. package/dist/index-tE_119SX.d.cts +26 -0
  181. package/dist/index-tE_119SX.d.cts.map +1 -0
  182. package/dist/index.cjs +3 -3
  183. package/dist/index.d.cts +4 -4
  184. package/dist/index.d.mts +4 -4
  185. package/dist/index.mjs +2 -3
  186. package/dist/kysely-CXi49zc2.d.cts +150 -0
  187. package/dist/kysely-CXi49zc2.d.cts.map +1 -0
  188. package/dist/kysely-D3-G5b0j.d.mts +150 -0
  189. package/dist/kysely-D3-G5b0j.d.mts.map +1 -0
  190. package/dist/{lazyAccessors-BoJN3SMd.mjs → lazyAccessors-BHtcqgz2.mjs} +22 -25
  191. package/dist/lazyAccessors-BHtcqgz2.mjs.map +1 -0
  192. package/dist/{lazyAccessors-e3j2oE2m.cjs → lazyAccessors-Dx9NLwJZ.cjs} +72 -76
  193. package/dist/lazyAccessors-Dx9NLwJZ.cjs.map +1 -0
  194. package/dist/object-storage.cjs +18 -10
  195. package/dist/object-storage.cjs.map +1 -1
  196. package/dist/object-storage.d.cts +3 -7
  197. package/dist/object-storage.d.cts.map +1 -1
  198. package/dist/object-storage.d.mts +3 -7
  199. package/dist/object-storage.d.mts.map +1 -1
  200. package/dist/object-storage.mjs +11 -3
  201. package/dist/object-storage.mjs.map +1 -1
  202. package/dist/{processAudits-Dmts9H_b.mjs → processAudits-BpRlPQMW.mjs} +16 -19
  203. package/dist/processAudits-BpRlPQMW.mjs.map +1 -0
  204. package/dist/{processAudits-CGqttuXm.cjs → processAudits-D_2W5x8k.cjs} +26 -30
  205. package/dist/processAudits-D_2W5x8k.cjs.map +1 -0
  206. package/dist/queue/index.cjs +25 -21
  207. package/dist/queue/index.cjs.map +1 -1
  208. package/dist/queue/index.d.cts +8 -15
  209. package/dist/queue/index.d.cts.map +1 -1
  210. package/dist/queue/index.d.mts +8 -15
  211. package/dist/queue/index.d.mts.map +1 -1
  212. package/dist/queue/index.mjs +15 -11
  213. package/dist/queue/index.mjs.map +1 -1
  214. package/dist/rest-api.cjs +88 -62
  215. package/dist/rest-api.cjs.map +1 -1
  216. package/dist/rest-api.d.cts +62 -60
  217. package/dist/rest-api.d.cts.map +1 -1
  218. package/dist/rest-api.d.mts +62 -60
  219. package/dist/rest-api.d.mts.map +1 -1
  220. package/dist/rest-api.mjs +80 -54
  221. package/dist/rest-api.mjs.map +1 -1
  222. package/dist/{chunk-DWy1uDak.cjs → rolldown-runtime-y5ARvAq5.cjs} +14 -18
  223. package/dist/site.cjs +33 -9
  224. package/dist/site.cjs.map +1 -1
  225. package/dist/site.d.cts +11 -22
  226. package/dist/site.d.cts.map +1 -1
  227. package/dist/site.d.mts +11 -22
  228. package/dist/site.d.mts.map +1 -1
  229. package/dist/site.mjs +29 -5
  230. package/dist/site.mjs.map +1 -1
  231. package/dist/subscribers/index.cjs +2 -11
  232. package/dist/subscribers/index.d.cts +3 -14
  233. package/dist/subscribers/index.d.mts +3 -14
  234. package/dist/subscribers/index.mjs +2 -11
  235. package/dist/surfaceEnv-CNimK76p.cjs +56 -0
  236. package/dist/surfaceEnv-CNimK76p.cjs.map +1 -0
  237. package/dist/surfaceEnv-wMT5QzgZ.mjs +45 -0
  238. package/dist/surfaceEnv-wMT5QzgZ.mjs.map +1 -0
  239. package/dist/{telemetry-C3L0iS28.d.cts → telemetry-DGcFlVpB.d.cts} +2 -5
  240. package/dist/telemetry-DGcFlVpB.d.cts.map +1 -0
  241. package/dist/{telemetry-D7bVFLGi.d.mts → telemetry-DGcFlVpB.d.mts} +2 -5
  242. package/dist/telemetry-DGcFlVpB.d.mts.map +1 -0
  243. package/dist/topic/index.cjs +18 -20
  244. package/dist/topic/index.cjs.map +1 -1
  245. package/dist/topic/index.d.cts +3 -32
  246. package/dist/topic/index.d.mts +3 -32
  247. package/dist/topic/index.mjs +8 -10
  248. package/dist/topic/index.mjs.map +1 -1
  249. package/dist/{types-BlAkSuGI.d.cts → types-DMuSQeLL.d.cts} +3 -5
  250. package/dist/types-DMuSQeLL.d.cts.map +1 -0
  251. package/dist/{types-aKksMEWL.d.mts → types-DMuSQeLL.d.mts} +3 -5
  252. package/dist/types-DMuSQeLL.d.mts.map +1 -0
  253. package/dist/types.d.cts +1 -1
  254. package/dist/types.d.mts +1 -1
  255. package/dist/types.mjs +1 -0
  256. package/dist/worker.cjs +109 -35
  257. package/dist/worker.cjs.map +1 -1
  258. package/dist/worker.d.cts +87 -42
  259. package/dist/worker.d.cts.map +1 -1
  260. package/dist/worker.d.mts +87 -42
  261. package/dist/worker.d.mts.map +1 -1
  262. package/dist/worker.mjs +104 -30
  263. package/dist/worker.mjs.map +1 -1
  264. package/package.json +59 -55
  265. package/dist/Construct-BD5neF7w.mjs.map +0 -1
  266. package/dist/Construct-C0ijr2qp.d.cts.map +0 -1
  267. package/dist/Construct-Ct8nalEi.d.mts.map +0 -1
  268. package/dist/Construct-CyS3RpTL.cjs.map +0 -1
  269. package/dist/Cron-ByxdOfpE.d.mts.map +0 -1
  270. package/dist/Cron-Wqh464aO.d.cts.map +0 -1
  271. package/dist/CronBuilder-BL0AP6fs.d.mts.map +0 -1
  272. package/dist/CronBuilder-BmABazzH.mjs.map +0 -1
  273. package/dist/CronBuilder-BqpEzMPQ.d.cts.map +0 -1
  274. package/dist/CronBuilder-CkXPJiGO.cjs.map +0 -1
  275. package/dist/Endpoint-2Wg7T7Cq.d.mts.map +0 -1
  276. package/dist/Endpoint-BiaaMEn_.cjs.map +0 -1
  277. package/dist/Endpoint-CH9Fn1AD.d.cts.map +0 -1
  278. package/dist/Endpoint-VIOpSo0S.mjs.map +0 -1
  279. package/dist/EndpointFactory-ChxLZllA.cjs.map +0 -1
  280. package/dist/EndpointFactory-DTTCkGEL.mjs.map +0 -1
  281. package/dist/HonoEndpointAdaptor-NKQQQbBY.mjs.map +0 -1
  282. package/dist/Queue-D7ZuwBhy.d.cts.map +0 -1
  283. package/dist/Queue-gVGfi2Yk.d.mts.map +0 -1
  284. package/dist/Subscriber-2mEi0ImH.d.cts.map +0 -1
  285. package/dist/Subscriber-xTJ9ayPN.d.mts.map +0 -1
  286. package/dist/SubscriberBuilder-4OPehf2R.cjs.map +0 -1
  287. package/dist/SubscriberBuilder-BjkbeT9l.mjs.map +0 -1
  288. package/dist/SubscriberBuilder-Dptmo-1A.d.mts.map +0 -1
  289. package/dist/SubscriberBuilder-waNhzjSM.d.cts.map +0 -1
  290. package/dist/Topic-19QeHBIR.d.cts.map +0 -1
  291. package/dist/Topic-DuFeqkBh.d.mts.map +0 -1
  292. package/dist/auth.d.cts.map +0 -1
  293. package/dist/auth.d.mts.map +0 -1
  294. package/dist/cache-0ANvRQnD.mjs +0 -51
  295. package/dist/cache-0ANvRQnD.mjs.map +0 -1
  296. package/dist/cache-3WSvAulx.cjs +0 -57
  297. package/dist/cache-3WSvAulx.cjs.map +0 -1
  298. package/dist/cache-BrRz3fEB.d.mts.map +0 -1
  299. package/dist/cache-BtZY_tHe.d.cts.map +0 -1
  300. package/dist/clone-DblgEZZa.mjs.map +0 -1
  301. package/dist/clone-Dy0cR2SZ.cjs.map +0 -1
  302. package/dist/construct-interface-BfjcOD_E.cjs +0 -117
  303. package/dist/construct-interface-BfjcOD_E.cjs.map +0 -1
  304. package/dist/construct-interface-D9d4Gm4p.d.cts.map +0 -1
  305. package/dist/construct-interface-Z4CpS8tK.mjs +0 -81
  306. package/dist/construct-interface-Z4CpS8tK.mjs.map +0 -1
  307. package/dist/construct-interface-g5GM5pwR.d.mts.map +0 -1
  308. package/dist/database/kysely.d.cts.map +0 -1
  309. package/dist/database/kysely.d.mts.map +0 -1
  310. package/dist/functions-Bd3TPxid.cjs.map +0 -1
  311. package/dist/functions-Cxbo-pEd.mjs.map +0 -1
  312. package/dist/index-BxHDdy6a.d.mts.map +0 -1
  313. package/dist/index-CubPzHd9.d.mts.map +0 -1
  314. package/dist/index-JSf9xNIJ.d.cts.map +0 -1
  315. package/dist/index-V2nksZIJ.d.cts.map +0 -1
  316. package/dist/lazyAccessors-BoJN3SMd.mjs.map +0 -1
  317. package/dist/lazyAccessors-e3j2oE2m.cjs.map +0 -1
  318. package/dist/processAudits-CGqttuXm.cjs.map +0 -1
  319. package/dist/processAudits-Dmts9H_b.mjs.map +0 -1
  320. package/dist/subscribers/index.cjs.map +0 -1
  321. package/dist/subscribers/index.d.cts.map +0 -1
  322. package/dist/subscribers/index.d.mts.map +0 -1
  323. package/dist/subscribers/index.mjs.map +0 -1
  324. package/dist/surfaceEnv-BFXEms2f.cjs +0 -39
  325. package/dist/surfaceEnv-BFXEms2f.cjs.map +0 -1
  326. package/dist/surfaceEnv-C3Ktahea.mjs +0 -27
  327. package/dist/surfaceEnv-C3Ktahea.mjs.map +0 -1
  328. package/dist/telemetry-C3L0iS28.d.cts.map +0 -1
  329. package/dist/telemetry-D7bVFLGi.d.mts.map +0 -1
  330. package/dist/topic/index.d.cts.map +0 -1
  331. package/dist/topic/index.d.mts.map +0 -1
  332. package/dist/types-BlAkSuGI.d.cts.map +0 -1
  333. package/dist/types-aKksMEWL.d.mts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"testing.mjs","names":["endpoints: Endpoint<any, any, any, any, any, any>[]","options: CreateMswHandlersOptions","id: string","ctxOptions: RegisterContextOptions","serviceDefs: Service[]","handlers: HttpHandler[]","name: string","value: string","options?: CookieOptions","_endpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTAuditAction,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>","endpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTAuditAction,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>","serviceDiscovery: ServiceDiscovery<any>","ctx: TestRequestAdaptor<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>","auditContext: AuditExecutionContext<TAuditAction> | undefined","actor: AuditActor","metadata","output","result","headers: Record<string, string | string[]>","setCookieValues: string[]","cookie","_fn: Function<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTOutSchema,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName,\n\t\t\tTAuditAction,\n\t\t\tany\n\t\t>","fn: Function<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTOutSchema,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName,\n\t\t\tTAuditAction,\n\t\t\tany\n\t\t>","serviceDiscovery: ServiceDiscovery<any>","ctx: TestFunctionRequest<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTDatabase,\n\t\t\tTAuditAction\n\t\t>","services: ServiceRecord<TServices>","db: TDatabase | undefined","auditor: Auditor<TAuditAction> | undefined","queue: Queue<TName, TMessage, TServices, TLogger>","serviceDiscovery: ServiceDiscovery<any>","request: TestQueueRequest<TMessage, TServices>","subscriber: Subscriber<\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tOutSchema,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTSubscribedEvents\n\t\t>","serviceDiscovery: ServiceDiscovery<any>","request: TestSubscriberRequest<\n\t\t\tTEventPublisher,\n\t\t\tTSubscribedEvents,\n\t\t\tTServices\n\t\t>","services: ServiceRecord<TServices>","output: any","events: ExtractEventPayloads<TEventPublisher, TSubscribedEvents>[]","event: any"],"sources":["../../src/endpoints/MswEndpointAdaptor.ts","../../src/endpoints/TestEndpointAdaptor.ts","../../src/functions/TestFunctionAdaptor.ts","../../src/queue/TestQueueAdaptor.ts","../../src/subscribers/TestSubscriberAdaptor.ts"],"sourcesContent":["import { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Service } from '@geekmidas/services';\nimport { ServiceDiscovery } from '@geekmidas/services';\nimport { Hono } from 'hono';\nimport { type HttpHandler, http } from 'msw';\nimport type { Endpoint } from './Endpoint';\nimport { HonoEndpoint } from './HonoEndpointAdaptor';\n\n/**\n * Header used to identify which test context a request belongs to.\n * Each concurrent test registers its own context (services, db transaction, etc.)\n * and the MSW handler looks it up by this header value.\n */\nexport const TEST_CONTEXT_HEADER = 'x-test-context-id';\n\n/**\n * Per-test context containing an isolated ServiceDiscovery instance.\n * Registered in the context map and looked up per-request via the context ID header.\n */\nexport interface MswTestContext {\n\tserviceDiscovery: ServiceDiscovery<any>;\n}\n\n/**\n * Options for creating MSW handlers from endpoint constructs.\n *\n * @example\n * ```typescript\n * import { createMswHandlers, registerTestContext } from '@geekmidas/constructs/testing';\n * import { setupServer } from 'msw/node';\n *\n * // Create handlers once (global)\n * const { handlers, registerContext, removeContext } = createMswHandlers(\n * [getUsers, createUser],\n * { baseURL: 'http://localhost:3000' },\n * );\n * const server = setupServer(...handlers);\n *\n * // Per test: register an isolated context with its own services/transaction\n * it('should list users', async ({ db }) => {\n * const contextId = crypto.randomUUID();\n * registerContext(contextId, {\n * services: { database: db, auth: mockAuth },\n * });\n *\n * const api = createApi({\n * baseURL: 'http://localhost:3000',\n * headers: { [TEST_CONTEXT_HEADER]: contextId },\n * });\n *\n * const result = await api('GET /users');\n * expect(result).toBeDefined();\n *\n * removeContext(contextId);\n * });\n * ```\n */\nexport interface CreateMswHandlersOptions {\n\t/** Base URL the client fetches from (e.g., 'http://localhost:3000') */\n\tbaseURL: string;\n}\n\n/**\n * Options for registering a per-test context.\n * Follows the same pattern as TestRequestAdaptor — services, database,\n * publisher, and auditorStorage are provided explicitly.\n */\nexport interface RegisterContextOptions {\n\t/** Service instances keyed by serviceName */\n\tservices?: Record<string, unknown>;\n\t/** Database instance — required when endpoints use .database() */\n\tdatabase?: unknown;\n\t/** Event publisher service definition */\n\tpublisher?: Service;\n\t/** Audit storage instance — required when endpoints use .auditor() */\n\tauditorStorage?: unknown;\n}\n\nconst MSW_HTTP_METHODS = [\n\t'get',\n\t'post',\n\t'put',\n\t'patch',\n\t'delete',\n\t'options',\n] as const;\n\ntype MswHttpMethod = (typeof MSW_HTTP_METHODS)[number];\n\n/**\n * Result of creating MSW handlers from endpoint constructs.\n */\nexport interface CreateMswHandlersResult {\n\t/** MSW handlers to pass to setupServer() */\n\thandlers: HttpHandler[];\n\t/** Register an isolated context for a test */\n\tregisterContext: (id: string, options: RegisterContextOptions) => void;\n}\n\n/**\n * Creates MSW HTTP handlers from endpoint constructs.\n *\n * Mounts endpoints on a Hono app and creates MSW handlers that intercept\n * fetch requests and route them through `app.request()` — giving frontend\n * tests full endpoint behavior (validation, auth, sessions) without HTTP.\n *\n * Each test registers its own isolated context via `registerContext()`,\n * which is resolved per-request using the `x-test-context-id` header.\n * This allows concurrent tests with their own transactions and services.\n *\n * @param endpoints - Array of endpoint constructs to create handlers for\n * @param options - Configuration including baseURL\n * @returns MSW handlers, the Hono app, and context management functions\n */\nexport function createMswHandlers(\n\tendpoints: Endpoint<any, any, any, any, any, any>[],\n\toptions: CreateMswHandlersOptions,\n): CreateMswHandlersResult {\n\tconst { baseURL } = options;\n\tconst contexts = new Map<string, MswTestContext>();\n\n\t/**\n\t * Register an isolated context for a test.\n\t * Creates a fresh ServiceDiscovery and pre-registers all provided services.\n\t */\n\tfunction registerContext(id: string, ctxOptions: RegisterContextOptions) {\n\t\tconst envParser = new EnvironmentParser({});\n\t\tconst serviceDiscovery = new ServiceDiscovery(envParser);\n\n\t\tconst serviceDefs: Service[] = [];\n\n\t\t// Register explicit services\n\t\tif (ctxOptions.services) {\n\t\t\tfor (const [name, instance] of Object.entries(ctxOptions.services)) {\n\t\t\t\tserviceDefs.push({\n\t\t\t\t\tserviceName: name,\n\t\t\t\t\tregister: () => instance,\n\t\t\t\t} as Service);\n\t\t\t}\n\t\t}\n\n\t\t// Register database service from endpoint metadata\n\t\tif (ctxOptions.database !== undefined) {\n\t\t\tfor (const endpoint of endpoints) {\n\t\t\t\tif (endpoint.databaseService) {\n\t\t\t\t\tserviceDefs.push({\n\t\t\t\t\t\tserviceName: endpoint.databaseService.serviceName,\n\t\t\t\t\t\tregister: () => ctxOptions.database,\n\t\t\t\t\t} as Service);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Register publisher service\n\t\tif (ctxOptions.publisher) {\n\t\t\tserviceDefs.push(ctxOptions.publisher);\n\t\t}\n\n\t\t// Register auditor storage service from endpoint metadata\n\t\tif (ctxOptions.auditorStorage !== undefined) {\n\t\t\tfor (const endpoint of endpoints) {\n\t\t\t\tif (endpoint.auditorStorageService) {\n\t\t\t\t\tserviceDefs.push({\n\t\t\t\t\t\tserviceName: endpoint.auditorStorageService.serviceName,\n\t\t\t\t\t\tregister: () => ctxOptions.auditorStorage,\n\t\t\t\t\t} as Service);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (serviceDefs.length > 0) {\n\t\t\tserviceDiscovery.register(serviceDefs);\n\t\t}\n\n\t\tcontexts.set(id, { serviceDiscovery });\n\t}\n\n\t// Create a Hono app per-request that uses the correct ServiceDiscovery.\n\t// We use a wrapper app that resolves the context from the header,\n\t// then delegates to a context-specific Hono app.\n\tconst handlers: HttpHandler[] = [];\n\n\tfor (const endpoint of endpoints) {\n\t\tconst method = endpoint.method.toLowerCase() as MswHttpMethod;\n\t\tif (!MSW_HTTP_METHODS.includes(method)) continue;\n\n\t\tconst mswUrl = `${baseURL}${endpoint.route}`;\n\t\tconst mswMethod = http[method];\n\n\t\thandlers.push(\n\t\t\tmswMethod(mswUrl, async ({ request }) => {\n\t\t\t\tconst contextId = request.headers.get(TEST_CONTEXT_HEADER);\n\t\t\t\tconst ctx = contextId ? contexts.get(contextId) : undefined;\n\n\t\t\t\tif (!ctx) {\n\t\t\t\t\treturn new Response(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\terror: 'Missing or unknown test context ID',\n\t\t\t\t\t\t\thint: `Set the '${TEST_CONTEXT_HEADER}' header to a registered context ID`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t\t{ status: 500, headers: { 'content-type': 'application/json' } },\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Build a fresh Hono app with this context's ServiceDiscovery\n\t\t\t\tconst app = new Hono();\n\t\t\t\tHonoEndpoint.addRoute(endpoint, ctx.serviceDiscovery as any, app);\n\n\t\t\t\tconst response = await app.request(request);\n\n\t\t\t\treturn new Response(response.body, {\n\t\t\t\t\tstatus: response.status,\n\t\t\t\t\tstatusText: response.statusText,\n\t\t\t\t\theaders: response.headers,\n\t\t\t\t});\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn {\n\t\thandlers,\n\t\tregisterContext,\n\t};\n}\n","import type {\n\tAuditActor,\n\tAuditableAction,\n\tAuditStorage,\n} from '@geekmidas/audit';\nimport { DefaultAuditor } from '@geekmidas/audit';\nimport { EnvironmentParser } from '@geekmidas/envkit';\nimport { ForbiddenError } from '@geekmidas/errors';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type {\n\tInferComposableStandardSchema,\n\tInferStandardSchema,\n} from '@geekmidas/schema';\nimport {\n\trunWithRequestContext,\n\ttype Service,\n\tServiceDiscovery,\n\ttype ServiceRecord,\n} from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { publishConstructEvents } from '../publisher';\nimport type { HttpMethod } from '../types';\nimport type { MappedAudit } from './audit';\nimport {\n\ttype CookieOptions,\n\tEndpoint,\n\ttype EndpointSchemas,\n\tResponseBuilder,\n} from './Endpoint';\nimport {\n\tcreateCookieHeaderAccessor,\n\tcreateObjectHeaders,\n} from './lazyAccessors';\nimport {\n\ttype AuditExecutionContext,\n\texecuteWithAuditTransaction,\n} from './processAudits';\n\nexport type TestHttpResponse<TBody = any> = {\n\tbody: TBody;\n\tstatus: number;\n\theaders: Record<string, string | string[]>;\n};\n\n/**\n * Serializes a cookie into a Set-Cookie header string\n */\nfunction serializeCookie(\n\tname: string,\n\tvalue: string,\n\toptions?: CookieOptions,\n): string {\n\tlet cookieString = `${name}=${value}`;\n\n\tif (options) {\n\t\tif (options.maxAge !== undefined) {\n\t\t\tcookieString += `; Max-Age=${options.maxAge}`;\n\t\t}\n\t\tif (options.expires) {\n\t\t\tcookieString += `; Expires=${options.expires.toUTCString()}`;\n\t\t}\n\t\tif (options.domain) {\n\t\t\tcookieString += `; Domain=${options.domain}`;\n\t\t}\n\t\tif (options.path) {\n\t\t\tcookieString += `; Path=${options.path}`;\n\t\t}\n\t\tif (options.httpOnly) {\n\t\t\tcookieString += '; HttpOnly';\n\t\t}\n\t\tif (options.secure) {\n\t\t\tcookieString += '; Secure';\n\t\t}\n\t\tif (options.sameSite) {\n\t\t\tcookieString += `; SameSite=${options.sameSite}`;\n\t\t}\n\t}\n\n\treturn cookieString;\n}\n\nexport class TestEndpointAdaptor<\n\tTRoute extends string,\n\tTMethod extends HttpMethod,\n\tTInput extends EndpointSchemas = {},\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTSession = unknown,\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTEventPublisherServiceName extends string = string,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\tTAuditStorageServiceName extends string = string,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> {\n\tstatic getDefaultServiceDiscover<\n\t\tTRoute extends string,\n\t\tTMethod extends HttpMethod,\n\t\tTInput extends EndpointSchemas = {},\n\t\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\t\tTServices extends Service[] = [],\n\t\tTLogger extends Logger = Logger,\n\t\tTSession = unknown,\n\t\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\t\tTEventPublisherServiceName extends string = string,\n\t\tTAuditStorage extends AuditStorage | undefined = undefined,\n\t\tTAuditStorageServiceName extends string = string,\n\t\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>,\n\t\tTDatabase = undefined,\n\t\tTDatabaseServiceName extends string = string,\n\t>(\n\t\t_endpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTAuditAction,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t) {\n\t\treturn ServiceDiscovery.getInstance(new EnvironmentParser({}));\n\t}\n\tconstructor(\n\t\tprivate readonly endpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTAuditAction,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t\tprivate serviceDiscovery: ServiceDiscovery<any> = TestEndpointAdaptor.getDefaultServiceDiscover(\n\t\t\tendpoint,\n\t\t),\n\t) {}\n\n\tasync fullRequest(\n\t\tctx: TestRequestAdaptor<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t): Promise<TestHttpResponse<InferStandardSchema<TOutSchema>>> {\n\t\tconst body = await this.endpoint.parseInput((ctx as any).body, 'body');\n\t\tconst query = await this.endpoint.parseInput((ctx as any).query, 'query');\n\t\tconst params = await this.endpoint.parseInput(\n\t\t\t(ctx as any).params,\n\t\t\t'params',\n\t\t);\n\n\t\t// Lazy accessors - defer parsing until needed\n\t\tconst header = createObjectHeaders(ctx.headers);\n\t\tconst cookie = createCookieHeaderAccessor(ctx.headers.cookie);\n\n\t\t// Request context setup\n\t\tconst startTime = Date.now();\n\t\tconst requestId =\n\t\t\t(ctx as any).requestId ??\n\t\t\tctx.headers['x-request-id'] ??\n\t\t\tcrypto.randomUUID();\n\n\t\tconst logger = this.endpoint.logger.child({\n\t\t\trequestId,\n\t\t\troute: this.endpoint.route,\n\t\t\thost: ctx.headers.host,\n\t\t\tmethod: this.endpoint.method,\n\t\t}) as TLogger;\n\n\t\t// Get database from context for session extraction\n\t\tconst rawDb = (ctx as any).database as TDatabase;\n\n\t\t// Wrap handler execution in request context\n\t\treturn runWithRequestContext({ logger, requestId, startTime }, async () => {\n\t\t\tconst session = await this.endpoint.getSession({\n\t\t\t\tlogger,\n\t\t\t\tservices: ctx.services,\n\t\t\t\theader,\n\t\t\t\tcookie,\n\t\t\t\t...(rawDb !== undefined && { db: rawDb }),\n\t\t\t} as any);\n\n\t\t\t// Check authorization\n\t\t\tconst isAuthorized = await this.endpoint.authorize({\n\t\t\t\theader,\n\t\t\t\tcookie,\n\t\t\t\tservices: ctx.services,\n\t\t\t\tlogger,\n\t\t\t\tsession,\n\t\t\t\t...(rawDb !== undefined && { db: rawDb }),\n\t\t\t\tbody,\n\t\t\t\tquery,\n\t\t\t\tparams,\n\t\t\t} as any);\n\n\t\t\tif (!isAuthorized) {\n\t\t\t\tlogger.warn('Forbidden access attempt');\n\t\t\t\tthrow new ForbiddenError(\n\t\t\t\t\t'Forbidden access to the endpoint',\n\t\t\t\t\t'You do not have permission to access this resource.',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Create audit context if audit storage is provided\n\t\t\t// The auditorStorage instance is required when endpoint uses .auditor()\n\t\t\tconst auditorStorage = (ctx as any).auditorStorage as TAuditStorage;\n\t\t\tlet auditContext: AuditExecutionContext<TAuditAction> | undefined;\n\n\t\t\tif (auditorStorage) {\n\t\t\t\t// Extract actor if configured\n\t\t\t\tlet actor: AuditActor = { id: 'system', type: 'system' };\n\t\t\t\tif (this.endpoint.actorExtractor) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tactor = await this.endpoint.actorExtractor({\n\t\t\t\t\t\t\tservices: ctx.services as any,\n\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tlogger.error(error as Error, 'Failed to extract actor for audits');\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst auditor = new DefaultAuditor<TAuditAction>({\n\t\t\t\t\tactor,\n\t\t\t\t\tstorage: auditorStorage as AuditStorage,\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\tendpoint: this.endpoint.route,\n\t\t\t\t\t\tmethod: this.endpoint.method,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tauditContext = { auditor, storage: auditorStorage as AuditStorage };\n\t\t\t}\n\n\t\t\t// Warn if declarative audits are configured but no audit storage\n\t\t\tconst audits = this.endpoint.audits as MappedAudit<\n\t\t\t\tTAuditAction,\n\t\t\t\tTOutSchema\n\t\t\t>[];\n\t\t\tif (!auditContext && audits?.length) {\n\t\t\t\tlogger.warn('No auditor storage service available');\n\t\t\t}\n\n\t\t\t// Execute handler with automatic audit transaction support\n\t\t\tconst result = await executeWithAuditTransaction(\n\t\t\t\tauditContext,\n\t\t\t\tasync (auditor) => {\n\t\t\t\t\t// Use audit transaction as db if available (when storage has same database)\n\t\t\t\t\t// For testing, the tester controls whether to use transactional auditing\n\t\t\t\t\tconst trx = auditor?.getTransaction?.();\n\t\t\t\t\tconst db = trx ?? rawDb;\n\n\t\t\t\t\tconst responseBuilder = new ResponseBuilder();\n\t\t\t\t\tconst response = await this.endpoint.handler(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbody,\n\t\t\t\t\t\t\tquery,\n\t\t\t\t\t\t\tparams,\n\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\tservices: ctx.services,\n\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\tauditor,\n\t\t\t\t\t\t\tdb,\n\t\t\t\t\t\t} as any,\n\t\t\t\t\t\tresponseBuilder,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Check if response has metadata\n\t\t\t\t\tlet data = response;\n\t\t\t\t\tlet metadata = responseBuilder.getMetadata();\n\n\t\t\t\t\tif (Endpoint.hasMetadata(response)) {\n\t\t\t\t\t\tdata = response.data;\n\t\t\t\t\t\tmetadata = response.metadata;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst output = await this.endpoint.parseOutput(data);\n\n\t\t\t\t\treturn { output, metadata, responseBuilder };\n\t\t\t\t},\n\t\t\t\t// Process declarative audits after handler (inside transaction)\n\t\t\t\tasync (result, auditor) => {\n\t\t\t\t\tif (!audits?.length) return;\n\n\t\t\t\t\tfor (const audit of audits) {\n\t\t\t\t\t\tif (audit.when && !audit.when(result.output as any)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst payload = audit.payload(result.output as any);\n\t\t\t\t\t\tconst entityId = audit.entityId?.(result.output as any);\n\t\t\t\t\t\tauditor.audit(audit.type as any, payload as any, {\n\t\t\t\t\t\t\ttable: audit.table,\n\t\t\t\t\t\t\tentityId,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t// Pass rawDb so storage can reuse existing transactions\n\t\t\t\t{ db: rawDb },\n\t\t\t);\n\n\t\t\tconst { output, metadata } = result;\n\n\t\t\tctx.publisher && (await this.serviceDiscovery.register([ctx.publisher]));\n\t\t\tawait publishConstructEvents(\n\t\t\t\tthis.endpoint,\n\t\t\t\toutput,\n\t\t\t\tthis.serviceDiscovery,\n\t\t\t);\n\n\t\t\t// Convert cookies to Set-Cookie headers\n\t\t\tconst headers: Record<string, string | string[]> = {\n\t\t\t\t...(metadata.headers || {}),\n\t\t\t};\n\n\t\t\tif (metadata.cookies && metadata.cookies.size > 0) {\n\t\t\t\tconst setCookieValues: string[] = [];\n\t\t\t\tfor (const [name, cookie] of metadata.cookies.entries()) {\n\t\t\t\t\tsetCookieValues.push(\n\t\t\t\t\t\tserializeCookie(name, cookie.value, cookie.options),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\theaders['set-cookie'] = setCookieValues;\n\t\t\t}\n\n\t\t\t// Return HTTP response format\n\t\t\treturn {\n\t\t\t\tbody: output,\n\t\t\t\tstatus: metadata.status || 200,\n\t\t\t\theaders,\n\t\t\t};\n\t\t});\n\t}\n\n\tasync request(\n\t\tctx: TestRequestAdaptor<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t): Promise<InferStandardSchema<TOutSchema>> {\n\t\tconst response = await this.fullRequest(ctx);\n\t\treturn response.body;\n\t}\n}\n\n/**\n * Conditional audit storage requirement - required when TAuditStorage is configured\n */\ntype AuditStorageRequirement<\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n> = TAuditStorage extends undefined\n\t? {}\n\t: {\n\t\t\t/** Audit storage instance - required when endpoint uses .auditor() */\n\t\t\tauditorStorage: TAuditStorage;\n\t\t};\n\n/**\n * Conditional database requirement - required when TDatabase is configured\n */\ntype DatabaseRequirement<TDatabase = undefined> = TDatabase extends undefined\n\t? {}\n\t: {\n\t\t\t/** Database instance - required when endpoint uses .database() */\n\t\t\tdatabase: TDatabase;\n\t\t};\n\nexport type TestRequestAdaptor<\n\tTInput extends EndpointSchemas = {},\n\tTServices extends Service[] = [],\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTEventPublisherServiceName extends string = string,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\t_TAuditStorageServiceName extends string = string,\n\tTDatabase = undefined,\n\t_TDatabaseServiceName extends string = string,\n> = {\n\tservices: ServiceRecord<TServices>;\n\theaders: Record<string, string>;\n\tpublisher?: Service<TEventPublisherServiceName, TEventPublisher>;\n} & InferComposableStandardSchema<TInput> &\n\tAuditStorageRequirement<TAuditStorage> &\n\tDatabaseRequirement<TDatabase>;\n","import type { AuditableAction, Auditor, AuditStorage } from '@geekmidas/audit';\nimport { DefaultAuditor } from '@geekmidas/audit';\nimport { EnvironmentParser } from '@geekmidas/envkit';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type {\n\tComposableStandardSchema,\n\tInferComposableStandardSchema,\n\tInferStandardSchema,\n} from '@geekmidas/schema';\nimport {\n\trunWithRequestContext,\n\ttype Service,\n\tServiceDiscovery,\n\ttype ServiceRecord,\n} from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { publishEvents } from '../publisher';\nimport type { Function } from './Function';\nimport { FunctionBuilder } from './FunctionBuilder';\n\nexport class TestFunctionAdaptor<\n\tTInput extends ComposableStandardSchema | undefined = undefined,\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTEventPublisherServiceName extends string = string,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\tTAuditStorageServiceName extends string = string,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n> {\n\tstatic getDefaultServiceDiscovery<\n\t\tTInput extends ComposableStandardSchema | undefined = undefined,\n\t\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\t\tTServices extends Service[] = [],\n\t\tTLogger extends Logger = Logger,\n\t\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\t\tTEventPublisherServiceName extends string = string,\n\t\tTAuditStorage extends AuditStorage | undefined = undefined,\n\t\tTAuditStorageServiceName extends string = string,\n\t\tTDatabase = undefined,\n\t\tTDatabaseServiceName extends string = string,\n\t\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>,\n\t>(\n\t\t_fn: Function<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTOutSchema,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName,\n\t\t\tTAuditAction,\n\t\t\tany\n\t\t>,\n\t) {\n\t\treturn ServiceDiscovery.getInstance(new EnvironmentParser({}));\n\t}\n\n\tconstructor(\n\t\tprivate readonly fn: Function<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTOutSchema,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName,\n\t\t\tTAuditAction,\n\t\t\tany\n\t\t>,\n\t\tprivate serviceDiscovery: ServiceDiscovery<any> = TestFunctionAdaptor.getDefaultServiceDiscovery(\n\t\t\tfn,\n\t\t),\n\t) {}\n\n\tasync invoke(\n\t\tctx: TestFunctionRequest<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTDatabase,\n\t\t\tTAuditAction\n\t\t>,\n\t): Promise<InferStandardSchema<TOutSchema>> {\n\t\t// Parse input if schema is provided\n\n\t\tconst parsedInput = await FunctionBuilder.parseComposableStandardSchema(\n\t\t\tctx.input,\n\t\t\tthis.fn.input,\n\t\t);\n\n\t\t// Create logger with context\n\t\tconst logger = this.fn.logger.child({\n\t\t\ttest: true,\n\t\t}) as TLogger;\n\n\t\t// Register services (use provided services or register from function)\n\t\tlet services: ServiceRecord<TServices>;\n\t\tif (ctx.services) {\n\t\t\tservices = ctx.services;\n\t\t} else {\n\t\t\tservices = await this.serviceDiscovery.register(this.fn.services);\n\t\t}\n\n\t\t// Resolve database (use provided db or register from function)\n\t\tlet db: TDatabase | undefined;\n\t\tif ('db' in ctx && ctx.db !== undefined) {\n\t\t\tdb = ctx.db;\n\t\t} else if (this.fn.databaseService) {\n\t\t\tconst dbServices = await this.serviceDiscovery.register([\n\t\t\t\tthis.fn.databaseService,\n\t\t\t]);\n\t\t\tdb = dbServices[\n\t\t\t\tthis.fn.databaseService.serviceName as keyof typeof dbServices\n\t\t\t] as TDatabase;\n\t\t}\n\n\t\t// Resolve auditor (use provided auditor or create from function)\n\t\tlet auditor: Auditor<TAuditAction> | undefined;\n\t\tif ('auditor' in ctx && ctx.auditor !== undefined) {\n\t\t\tauditor = ctx.auditor;\n\t\t} else if (this.fn.auditorStorageService) {\n\t\t\tconst auditServices = await this.serviceDiscovery.register([\n\t\t\t\tthis.fn.auditorStorageService,\n\t\t\t]);\n\t\t\tconst storage = auditServices[\n\t\t\t\tthis.fn.auditorStorageService.serviceName as keyof typeof auditServices\n\t\t\t] as AuditStorage;\n\n\t\t\tauditor = new DefaultAuditor<TAuditAction>({\n\t\t\t\tactor: { id: 'system', type: 'system' },\n\t\t\t\tstorage,\n\t\t\t\tmetadata: {\n\t\t\t\t\tfunction: this.fn.type,\n\t\t\t\t\ttest: true,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\t// Wrap execution in request context for service access via context.getLogger()\n\t\tconst requestId = `test-${Date.now()}`;\n\t\tconst startTime = Date.now();\n\n\t\treturn runWithRequestContext({ logger, requestId, startTime }, async () => {\n\t\t\t// Execute the function\n\t\t\tconst response = await this.fn.fn({\n\t\t\t\tinput: parsedInput,\n\t\t\t\tservices,\n\t\t\t\tlogger,\n\t\t\t\tdb,\n\t\t\t\tauditor,\n\t\t\t} as any);\n\n\t\t\t// Parse output if schema is provided\n\t\t\tconst output = await this.fn.parseOutput(response);\n\n\t\t\t// Flush audits if any were recorded\n\t\t\tif (auditor) {\n\t\t\t\tconst records = auditor.getRecords();\n\t\t\t\tif (records.length > 0) {\n\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t{ auditCount: records.length },\n\t\t\t\t\t\t'Flushing function audits',\n\t\t\t\t\t);\n\t\t\t\t\tawait auditor.flush();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Register publisher service if provided in context\n\n\t\t\tawait publishEvents(\n\t\t\t\tlogger,\n\t\t\t\tthis.serviceDiscovery,\n\t\t\t\tthis.fn.events,\n\t\t\t\toutput,\n\t\t\t\tthis.fn.publisherService,\n\t\t\t);\n\n\t\t\treturn output;\n\t\t}) as Promise<InferStandardSchema<TOutSchema>>;\n\t}\n}\n\nexport type TestFunctionRequest<\n\tTInput extends ComposableStandardSchema | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTEventPublisherServiceName extends string = string,\n\tTDatabase = undefined,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n> = {\n\tinput: InferComposableStandardSchema<TInput>;\n\tservices: ServiceRecord<TServices>;\n\tpublisher?: Service<TEventPublisherServiceName, TEventPublisher>;\n\tdb?: TDatabase;\n\tauditor?: Auditor<TAuditAction>;\n} & InferComposableStandardSchema<{ input: TInput }>;\n","import { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport { runWithRequestContext, ServiceDiscovery } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type { Queue } from './Queue';\n\n/**\n * In-memory test driver for a {@link Queue} worker. Hand it a batch of messages\n * and it registers the queue's services, runs the handler in a request context,\n * and returns the handler result — no SQS/pg-boss required.\n *\n * @example\n * const adapter = new TestQueueAdaptor(ordersQueue);\n * await adapter.invoke({ messages: [{ orderId: '123' }] });\n */\nexport class TestQueueAdaptor<\n\tTName extends string = string,\n\tTMessage extends StandardSchemaV1 = StandardSchemaV1,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n> {\n\tstatic getDefaultServiceDiscovery() {\n\t\treturn ServiceDiscovery.getInstance(new EnvironmentParser({}));\n\t}\n\n\tconstructor(\n\t\tprivate readonly queue: Queue<TName, TMessage, TServices, TLogger>,\n\t\tprivate serviceDiscovery: ServiceDiscovery<any> = TestQueueAdaptor.getDefaultServiceDiscovery(),\n\t) {}\n\n\tasync invoke(\n\t\trequest: TestQueueRequest<TMessage, TServices>,\n\t): Promise<unknown> {\n\t\tconst logger = this.queue.logger.child({ test: true }) as TLogger;\n\n\t\tconst services =\n\t\t\trequest.services ??\n\t\t\t((await this.serviceDiscovery.register(\n\t\t\t\tthis.queue.services,\n\t\t\t)) as ServiceRecord<TServices>);\n\n\t\tconst requestId = `test-${Date.now()}`;\n\t\tconst startTime = Date.now();\n\n\t\treturn runWithRequestContext({ logger, requestId, startTime }, () =>\n\t\t\tthis.queue.handler({\n\t\t\t\tmessages: request.messages,\n\t\t\t\tservices,\n\t\t\t\tlogger,\n\t\t\t}),\n\t\t);\n\t}\n}\n\nexport type TestQueueRequest<\n\tTMessage extends StandardSchemaV1 = StandardSchemaV1,\n\tTServices extends Service[] = [],\n> = {\n\tmessages: InferStandardSchema<TMessage>[];\n\tservices?: ServiceRecord<TServices>;\n};\n","import { EnvironmentParser } from '@geekmidas/envkit';\nimport type {\n\tEventPublisher,\n\tExtractPublisherMessage,\n} from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport { runWithRequestContext, ServiceDiscovery } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { publishEvents } from '../publisher';\nimport type { Subscriber } from './Subscriber';\n\n// Helper type to extract payload types for subscribed events\ntype ExtractEventPayloads<\n\tTPublisher extends EventPublisher<any> | undefined,\n\tTEventTypes extends any[],\n> = TPublisher extends EventPublisher<any>\n\t? Extract<ExtractPublisherMessage<TPublisher>, { type: TEventTypes[number] }>\n\t: never;\n\nexport class TestSubscriberAdaptor<\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTEventPublisherServiceName extends string = string,\n\tTSubscribedEvents extends\n\t\tExtractPublisherMessage<TEventPublisher>['type'][] = ExtractPublisherMessage<TEventPublisher>['type'][],\n> {\n\tstatic getDefaultServiceDiscovery() {\n\t\treturn ServiceDiscovery.getInstance(new EnvironmentParser({}));\n\t}\n\n\tconstructor(\n\t\tprivate readonly subscriber: Subscriber<\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tOutSchema,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTSubscribedEvents\n\t\t>,\n\t\tprivate serviceDiscovery: ServiceDiscovery<any> = TestSubscriberAdaptor.getDefaultServiceDiscovery(),\n\t) {}\n\n\tasync invoke(\n\t\trequest: TestSubscriberRequest<\n\t\t\tTEventPublisher,\n\t\t\tTSubscribedEvents,\n\t\t\tTServices\n\t\t>,\n\t): Promise<InferStandardSchema<OutSchema>> {\n\t\t// Create logger with test context\n\t\tconst logger = this.subscriber.logger.child({\n\t\t\ttest: true,\n\t\t}) as TLogger;\n\n\t\t// Resolve services (use provided or auto-register)\n\t\tlet services: ServiceRecord<TServices>;\n\t\tif (request.services) {\n\t\t\tservices = request.services;\n\t\t} else {\n\t\t\tservices = await this.serviceDiscovery.register(this.subscriber.services);\n\t\t}\n\n\t\t// Filter events to only subscribed types\n\t\tconst filteredEvents = this.filterEvents(request.events);\n\n\t\t// Return early if no events after filtering (mirrors AWSLambdaSubscriber)\n\t\tif (filteredEvents.length === 0) {\n\t\t\treturn { batchItemFailures: [] } as any;\n\t\t}\n\n\t\t// Wrap execution in request context for service access via context.getLogger()\n\t\tconst requestId = `test-${Date.now()}`;\n\t\tconst startTime = Date.now();\n\n\t\treturn runWithRequestContext({ logger, requestId, startTime }, async () => {\n\t\t\t// Execute the subscriber handler\n\t\t\tconst result = await this.subscriber.handler({\n\t\t\t\tevents: filteredEvents,\n\t\t\t\tservices,\n\t\t\t\tlogger,\n\t\t\t});\n\n\t\t\t// Validate output if schema is provided\n\t\t\tlet output: any = result;\n\t\t\tif (this.subscriber.outputSchema && result) {\n\t\t\t\tconst validationResult =\n\t\t\t\t\tawait this.subscriber.outputSchema['~standard'].validate(result);\n\n\t\t\t\tif (validationResult.issues) {\n\t\t\t\t\tthrow new Error('Subscriber output validation failed');\n\t\t\t\t}\n\n\t\t\t\toutput = validationResult.value;\n\t\t\t}\n\n\t\t\t// Publish events if configured\n\t\t\tawait publishEvents(\n\t\t\t\tlogger,\n\t\t\t\tthis.serviceDiscovery,\n\t\t\t\tthis.subscriber.events,\n\t\t\t\toutput,\n\t\t\t\tthis.subscriber.publisherService,\n\t\t\t);\n\n\t\t\treturn output;\n\t\t}) as Promise<InferStandardSchema<OutSchema>>;\n\t}\n\n\tprivate filterEvents(\n\t\tevents: ExtractEventPayloads<TEventPublisher, TSubscribedEvents>[],\n\t): ExtractEventPayloads<TEventPublisher, TSubscribedEvents>[] {\n\t\tif (!this.subscriber.subscribedEvents) {\n\t\t\treturn events;\n\t\t}\n\n\t\treturn events.filter((event: any) =>\n\t\t\tthis.subscriber.subscribedEvents!.includes(event.type),\n\t\t);\n\t}\n}\n\nexport type TestSubscriberRequest<\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTSubscribedEvents extends any[] = [],\n\tTServices extends Service[] = [],\n> = {\n\tevents: ExtractEventPayloads<TEventPublisher, TSubscribedEvents>[];\n\tservices?: ServiceRecord<TServices>;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAaA,MAAa,sBAAsB;AAiEnC,MAAM,mBAAmB;CACxB;CACA;CACA;CACA;CACA;CACA;AACA;;;;;;;;;;;;;;;;AA6BD,SAAgB,kBACfA,WACAC,SAC0B;CAC1B,MAAM,EAAE,SAAS,GAAG;CACpB,MAAM,2BAAW,IAAI;;;;;CAMrB,SAAS,gBAAgBC,IAAYC,YAAoC;EACxE,MAAM,YAAY,IAAI,kBAAkB,CAAE;EAC1C,MAAM,mBAAmB,IAAI,iBAAiB;EAE9C,MAAMC,cAAyB,CAAE;AAGjC,MAAI,WAAW,SACd,MAAK,MAAM,CAAC,MAAM,SAAS,IAAI,OAAO,QAAQ,WAAW,SAAS,CACjE,aAAY,KAAK;GAChB,aAAa;GACb,UAAU,MAAM;EAChB,EAAY;AAKf,MAAI,WAAW,qBACd;QAAK,MAAM,YAAY,UACtB,KAAI,SAAS,iBAAiB;AAC7B,gBAAY,KAAK;KAChB,aAAa,SAAS,gBAAgB;KACtC,UAAU,MAAM,WAAW;IAC3B,EAAY;AACb;GACA;EACD;AAIF,MAAI,WAAW,UACd,aAAY,KAAK,WAAW,UAAU;AAIvC,MAAI,WAAW,2BACd;QAAK,MAAM,YAAY,UACtB,KAAI,SAAS,uBAAuB;AACnC,gBAAY,KAAK;KAChB,aAAa,SAAS,sBAAsB;KAC5C,UAAU,MAAM,WAAW;IAC3B,EAAY;AACb;GACA;EACD;AAGF,MAAI,YAAY,SAAS,EACxB,kBAAiB,SAAS,YAAY;AAGvC,WAAS,IAAI,IAAI,EAAE,iBAAkB,EAAC;CACtC;CAKD,MAAMC,WAA0B,CAAE;AAElC,MAAK,MAAM,YAAY,WAAW;EACjC,MAAM,SAAS,SAAS,OAAO,aAAa;AAC5C,OAAK,iBAAiB,SAAS,OAAO,CAAE;EAExC,MAAM,UAAU,EAAE,QAAQ,EAAE,SAAS,MAAM;EAC3C,MAAM,YAAY,KAAK;AAEvB,WAAS,KACR,UAAU,QAAQ,OAAO,EAAE,SAAS,KAAK;GACxC,MAAM,YAAY,QAAQ,QAAQ,IAAI,oBAAoB;GAC1D,MAAM,MAAM,YAAY,SAAS,IAAI,UAAU;AAE/C,QAAK,IACJ,QAAO,IAAI,SACV,KAAK,UAAU;IACd,OAAO;IACP,OAAO,WAAW,oBAAoB;GACtC,EAAC,EACF;IAAE,QAAQ;IAAK,SAAS,EAAE,gBAAgB,mBAAoB;GAAE;GAKlE,MAAM,MAAM,IAAI;AAChB,gBAAa,SAAS,UAAU,IAAI,kBAAyB,IAAI;GAEjE,MAAM,WAAW,MAAM,IAAI,QAAQ,QAAQ;AAE3C,UAAO,IAAI,SAAS,SAAS,MAAM;IAClC,QAAQ,SAAS;IACjB,YAAY,SAAS;IACrB,SAAS,SAAS;GAClB;EACD,EAAC,CACF;CACD;AAED,QAAO;EACN;EACA;CACA;AACD;;;;;;;ACjLD,SAAS,gBACRC,MACAC,OACAC,SACS;CACT,IAAI,gBAAgB,EAAE,KAAK,GAAG,MAAM;AAEpC,KAAI,SAAS;AACZ,MAAI,QAAQ,kBACX,kBAAiB,YAAY,QAAQ,OAAO;AAE7C,MAAI,QAAQ,QACX,kBAAiB,YAAY,QAAQ,QAAQ,aAAa,CAAC;AAE5D,MAAI,QAAQ,OACX,kBAAiB,WAAW,QAAQ,OAAO;AAE5C,MAAI,QAAQ,KACX,kBAAiB,SAAS,QAAQ,KAAK;AAExC,MAAI,QAAQ,SACX,iBAAgB;AAEjB,MAAI,QAAQ,OACX,iBAAgB;AAEjB,MAAI,QAAQ,SACX,kBAAiB,aAAa,QAAQ,SAAS;CAEhD;AAED,QAAO;AACP;AAED,IAAa,sBAAb,MAAa,oBAkBX;CACD,OAAO,0BAmBNC,WAgBC;AACD,SAAO,iBAAiB,YAAY,IAAI,kBAAkB,CAAE,GAAE;CAC9D;CACD,YACkBC,UAgBTC,mBAA0C,oBAAoB,0BACrE,SACA,EACA;EAnBgB;EAgBT;CAGL;CAEJ,MAAM,YACLC,KAU6D;EAC7D,MAAM,OAAO,MAAM,KAAK,SAAS,WAAY,IAAY,MAAM,OAAO;EACtE,MAAM,QAAQ,MAAM,KAAK,SAAS,WAAY,IAAY,OAAO,QAAQ;EACzE,MAAM,SAAS,MAAM,KAAK,SAAS,WACjC,IAAY,QACb,SACA;EAGD,MAAM,SAAS,oBAAoB,IAAI,QAAQ;EAC/C,MAAM,SAAS,2BAA2B,IAAI,QAAQ,OAAO;EAG7D,MAAM,YAAY,KAAK,KAAK;EAC5B,MAAM,YACJ,IAAY,aACb,IAAI,QAAQ,mBACZ,OAAO,YAAY;EAEpB,MAAM,SAAS,KAAK,SAAS,OAAO,MAAM;GACzC;GACA,OAAO,KAAK,SAAS;GACrB,MAAM,IAAI,QAAQ;GAClB,QAAQ,KAAK,SAAS;EACtB,EAAC;EAGF,MAAM,QAAS,IAAY;AAG3B,SAAO,sBAAsB;GAAE;GAAQ;GAAW;EAAW,GAAE,YAAY;GAC1E,MAAM,UAAU,MAAM,KAAK,SAAS,WAAW;IAC9C;IACA,UAAU,IAAI;IACd;IACA;IACA,GAAI,oBAAuB,EAAE,IAAI,MAAO;GACxC,EAAQ;GAGT,MAAM,eAAe,MAAM,KAAK,SAAS,UAAU;IAClD;IACA;IACA,UAAU,IAAI;IACd;IACA;IACA,GAAI,oBAAuB,EAAE,IAAI,MAAO;IACxC;IACA;IACA;GACA,EAAQ;AAET,QAAK,cAAc;AAClB,WAAO,KAAK,2BAA2B;AACvC,UAAM,IAAI,eACT,oCACA;GAED;GAID,MAAM,iBAAkB,IAAY;GACpC,IAAIC;AAEJ,OAAI,gBAAgB;IAEnB,IAAIC,QAAoB;KAAE,IAAI;KAAU,MAAM;IAAU;AACxD,QAAI,KAAK,SAAS,eACjB,KAAI;AACH,aAAQ,MAAM,KAAK,SAAS,eAAe;MAC1C,UAAU,IAAI;MACd;MACA;MACA;MACA;KACA,EAAC;IACF,SAAQ,OAAO;AACf,YAAO,MAAM,OAAgB,qCAAqC;IAClE;IAGF,MAAM,UAAU,IAAI,eAA6B;KAChD;KACA,SAAS;KACT,UAAU;MACT,UAAU,KAAK,SAAS;MACxB,QAAQ,KAAK,SAAS;KACtB;IACD;AAED,mBAAe;KAAE;KAAS,SAAS;IAAgC;GACnE;GAGD,MAAM,SAAS,KAAK,SAAS;AAI7B,QAAK,gBAAgB,QAAQ,OAC5B,QAAO,KAAK,uCAAuC;GAIpD,MAAM,SAAS,MAAM,4BACpB,cACA,OAAO,YAAY;IAGlB,MAAM,MAAM,SAAS,kBAAkB;IACvC,MAAM,KAAK,OAAO;IAElB,MAAM,kBAAkB,IAAI;IAC5B,MAAM,WAAW,MAAM,KAAK,SAAS,QACpC;KACC;KACA;KACA;KACA;KACA,UAAU,IAAI;KACd;KACA;KACA;KACA;KACA;IACA,GACD,gBACA;IAGD,IAAI,OAAO;IACX,IAAIC,aAAW,gBAAgB,aAAa;AAE5C,QAAI,SAAS,YAAY,SAAS,EAAE;AACnC,YAAO,SAAS;AAChB,kBAAW,SAAS;IACpB;IAED,MAAMC,WAAS,MAAM,KAAK,SAAS,YAAY,KAAK;AAEpD,WAAO;KAAE;KAAQ;KAAU;IAAiB;GAC5C,GAED,OAAOC,UAAQ,YAAY;AAC1B,SAAK,QAAQ,OAAQ;AAErB,SAAK,MAAM,SAAS,QAAQ;AAC3B,SAAI,MAAM,SAAS,MAAM,KAAKA,SAAO,OAAc,CAClD;KAED,MAAM,UAAU,MAAM,QAAQA,SAAO,OAAc;KACnD,MAAM,WAAW,MAAM,WAAWA,SAAO,OAAc;AACvD,aAAQ,MAAM,MAAM,MAAa,SAAgB;MAChD,OAAO,MAAM;MACb;KACA,EAAC;IACF;GACD,GAED,EAAE,IAAI,MAAO,EACb;GAED,MAAM,EAAE,QAAQ,UAAU,GAAG;AAE7B,OAAI,aAAc,MAAM,KAAK,iBAAiB,SAAS,CAAC,IAAI,SAAU,EAAC;AACvE,SAAM,uBACL,KAAK,UACL,QACA,KAAK,iBACL;GAGD,MAAMC,UAA6C,EAClD,GAAI,SAAS,WAAW,CAAE,EAC1B;AAED,OAAI,SAAS,WAAW,SAAS,QAAQ,OAAO,GAAG;IAClD,MAAMC,kBAA4B,CAAE;AACpC,SAAK,MAAM,CAAC,MAAMC,SAAO,IAAI,SAAS,QAAQ,SAAS,CACtD,iBAAgB,KACf,gBAAgB,MAAMA,SAAO,OAAOA,SAAO,QAAQ,CACnD;AAEF,YAAQ,gBAAgB;GACxB;AAGD,UAAO;IACN,MAAM;IACN,QAAQ,SAAS,UAAU;IAC3B;GACA;EACD,EAAC;CACF;CAED,MAAM,QACLR,KAU2C;EAC3C,MAAM,WAAW,MAAM,KAAK,YAAY,IAAI;AAC5C,SAAO,SAAS;CAChB;AACD;;;;ACzWD,IAAa,sBAAb,MAAa,oBAeX;CACD,OAAO,2BAgBNS,KAcC;AACD,SAAO,iBAAiB,YAAY,IAAI,kBAAkB,CAAE,GAAE;CAC9D;CAED,YACkBC,IAcTC,mBAA0C,oBAAoB,2BACrE,GACA,EACA;EAjBgB;EAcT;CAGL;CAEJ,MAAM,OACLC,KAQ2C;EAG3C,MAAM,cAAc,MAAM,gBAAgB,8BACzC,IAAI,OACJ,KAAK,GAAG,MACR;EAGD,MAAM,SAAS,KAAK,GAAG,OAAO,MAAM,EACnC,MAAM,KACN,EAAC;EAGF,IAAIC;AACJ,MAAI,IAAI,SACP,YAAW,IAAI;MAEf,YAAW,MAAM,KAAK,iBAAiB,SAAS,KAAK,GAAG,SAAS;EAIlE,IAAIC;AACJ,MAAI,QAAQ,OAAO,IAAI,cACtB,MAAK,IAAI;WACC,KAAK,GAAG,iBAAiB;GACnC,MAAM,aAAa,MAAM,KAAK,iBAAiB,SAAS,CACvD,KAAK,GAAG,eACR,EAAC;AACF,QAAK,WACJ,KAAK,GAAG,gBAAgB;EAEzB;EAGD,IAAIC;AACJ,MAAI,aAAa,OAAO,IAAI,mBAC3B,WAAU,IAAI;WACJ,KAAK,GAAG,uBAAuB;GACzC,MAAM,gBAAgB,MAAM,KAAK,iBAAiB,SAAS,CAC1D,KAAK,GAAG,qBACR,EAAC;GACF,MAAM,UAAU,cACf,KAAK,GAAG,sBAAsB;AAG/B,aAAU,IAAI,eAA6B;IAC1C,OAAO;KAAE,IAAI;KAAU,MAAM;IAAU;IACvC;IACA,UAAU;KACT,UAAU,KAAK,GAAG;KAClB,MAAM;IACN;GACD;EACD;EAGD,MAAM,aAAa,OAAO,KAAK,KAAK,CAAC;EACrC,MAAM,YAAY,KAAK,KAAK;AAE5B,SAAO,sBAAsB;GAAE;GAAQ;GAAW;EAAW,GAAE,YAAY;GAE1E,MAAM,WAAW,MAAM,KAAK,GAAG,GAAG;IACjC,OAAO;IACP;IACA;IACA;IACA;GACA,EAAQ;GAGT,MAAM,SAAS,MAAM,KAAK,GAAG,YAAY,SAAS;AAGlD,OAAI,SAAS;IACZ,MAAM,UAAU,QAAQ,YAAY;AACpC,QAAI,QAAQ,SAAS,GAAG;AACvB,YAAO,MACN,EAAE,YAAY,QAAQ,OAAQ,GAC9B,2BACA;AACD,WAAM,QAAQ,OAAO;IACrB;GACD;AAID,SAAM,cACL,QACA,KAAK,kBACL,KAAK,GAAG,QACR,QACA,KAAK,GAAG,iBACR;AAED,UAAO;EACP,EAAC;CACF;AACD;;;;;;;;;;;;;ACrLD,IAAa,mBAAb,MAAa,iBAKX;CACD,OAAO,6BAA6B;AACnC,SAAO,iBAAiB,YAAY,IAAI,kBAAkB,CAAE,GAAE;CAC9D;CAED,YACkBC,OACTC,mBAA0C,iBAAiB,4BAA4B,EAC9F;EAFgB;EACT;CACL;CAEJ,MAAM,OACLC,SACmB;EACnB,MAAM,SAAS,KAAK,MAAM,OAAO,MAAM,EAAE,MAAM,KAAM,EAAC;EAEtD,MAAM,WACL,QAAQ,YACN,MAAM,KAAK,iBAAiB,SAC7B,KAAK,MAAM,SACX;EAEF,MAAM,aAAa,OAAO,KAAK,KAAK,CAAC;EACrC,MAAM,YAAY,KAAK,KAAK;AAE5B,SAAO,sBAAsB;GAAE;GAAQ;GAAW;EAAW,GAAE,MAC9D,KAAK,MAAM,QAAQ;GAClB,UAAU,QAAQ;GAClB;GACA;EACA,EAAC,CACF;CACD;AACD;;;;ACjCD,IAAa,wBAAb,MAAa,sBAQX;CACD,OAAO,6BAA6B;AACnC,SAAO,iBAAiB,YAAY,IAAI,kBAAkB,CAAE,GAAE;CAC9D;CAED,YACkBC,YAQTC,mBAA0C,sBAAsB,4BAA4B,EACnG;EATgB;EAQT;CACL;CAEJ,MAAM,OACLC,SAK0C;EAE1C,MAAM,SAAS,KAAK,WAAW,OAAO,MAAM,EAC3C,MAAM,KACN,EAAC;EAGF,IAAIC;AACJ,MAAI,QAAQ,SACX,YAAW,QAAQ;MAEnB,YAAW,MAAM,KAAK,iBAAiB,SAAS,KAAK,WAAW,SAAS;EAI1E,MAAM,iBAAiB,KAAK,aAAa,QAAQ,OAAO;AAGxD,MAAI,eAAe,WAAW,EAC7B,QAAO,EAAE,mBAAmB,CAAE,EAAE;EAIjC,MAAM,aAAa,OAAO,KAAK,KAAK,CAAC;EACrC,MAAM,YAAY,KAAK,KAAK;AAE5B,SAAO,sBAAsB;GAAE;GAAQ;GAAW;EAAW,GAAE,YAAY;GAE1E,MAAM,SAAS,MAAM,KAAK,WAAW,QAAQ;IAC5C,QAAQ;IACR;IACA;GACA,EAAC;GAGF,IAAIC,SAAc;AAClB,OAAI,KAAK,WAAW,gBAAgB,QAAQ;IAC3C,MAAM,mBACL,MAAM,KAAK,WAAW,aAAa,aAAa,SAAS,OAAO;AAEjE,QAAI,iBAAiB,OACpB,OAAM,IAAI,MAAM;AAGjB,aAAS,iBAAiB;GAC1B;AAGD,SAAM,cACL,QACA,KAAK,kBACL,KAAK,WAAW,QAChB,QACA,KAAK,WAAW,iBAChB;AAED,UAAO;EACP,EAAC;CACF;CAED,AAAQ,aACPC,QAC6D;AAC7D,OAAK,KAAK,WAAW,iBACpB,QAAO;AAGR,SAAO,OAAO,OAAO,CAACC,UACrB,KAAK,WAAW,iBAAkB,SAAS,MAAM,KAAK,CACtD;CACD;AACD"}
1
+ {"version":3,"file":"testing.mjs","names":["TestBrowser"],"sources":["../../src/endpoints/MswEndpointAdaptor.ts","../../src/endpoints/TestEndpointAdaptor.ts","../../src/functions/TestFunctionAdaptor.ts","../../src/queue/TestQueueAdaptor.ts","../../src/subscribers/TestSubscriberAdaptor.ts","../../src/testing/featureTest.ts"],"sourcesContent":["import { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Service } from '@geekmidas/services';\nimport { ServiceDiscovery } from '@geekmidas/services';\nimport { TEST_CONTEXT_HEADER } from '@geekmidas/testkit/context';\nimport { Hono } from 'hono';\nimport { type HttpHandler, http } from 'msw';\nimport type { Endpoint } from './Endpoint';\nimport { HonoEndpoint } from './HonoEndpointAdaptor';\n\n/**\n * Header used to identify which test context a request belongs to — defined\n * once, in testkit, where the stamped `fetch` that writes it lives.\n */\nexport { TEST_CONTEXT_HEADER };\n\n/**\n * Per-test context containing an isolated ServiceDiscovery instance.\n * Registered in the context map and looked up per-request via the context ID header.\n */\nexport interface MswTestContext {\n\tserviceDiscovery: ServiceDiscovery<any>;\n}\n\n/**\n * Options for creating MSW handlers from endpoint constructs.\n *\n * @example\n * ```typescript\n * import { createMswHandlers, registerTestContext } from '@geekmidas/constructs/testing';\n * import { setupServer } from 'msw/node';\n *\n * // Create handlers once (global)\n * const { handlers, registerContext, removeContext } = createMswHandlers(\n * [getUsers, createUser],\n * { baseURL: 'http://localhost:3000' },\n * );\n * const server = setupServer(...handlers);\n *\n * // Per test: register an isolated context with its own services/transaction\n * it('should list users', async ({ db }) => {\n * const contextId = crypto.randomUUID();\n * registerContext(contextId, {\n * services: { database: db, auth: mockAuth },\n * });\n *\n * const api = createApi({\n * baseURL: 'http://localhost:3000',\n * headers: { [TEST_CONTEXT_HEADER]: contextId },\n * });\n *\n * const result = await api('GET /users');\n * expect(result).toBeDefined();\n *\n * removeContext(contextId);\n * });\n * ```\n */\nexport interface CreateMswHandlersOptions {\n\t/** Base URL the client fetches from (e.g., 'http://localhost:3000') */\n\tbaseURL: string;\n}\n\n/**\n * Options for registering a per-test context.\n * Follows the same pattern as TestRequestAdaptor — services, database,\n * publisher, and auditorStorage are provided explicitly.\n */\nexport interface RegisterContextOptions {\n\t/** Service instances keyed by serviceName */\n\tservices?: Record<string, unknown>;\n\t/** Database instance — required when endpoints use .database() */\n\tdatabase?: unknown;\n\t/** Event publisher service definition */\n\tpublisher?: Service;\n\t/** Audit storage instance — required when endpoints use .auditor() */\n\tauditorStorage?: unknown;\n}\n\nconst MSW_HTTP_METHODS = [\n\t'get',\n\t'post',\n\t'put',\n\t'patch',\n\t'delete',\n\t'options',\n] as const;\n\ntype MswHttpMethod = (typeof MSW_HTTP_METHODS)[number];\n\n/**\n * Result of creating MSW handlers from endpoint constructs.\n */\nexport interface CreateMswHandlersResult {\n\t/** MSW handlers to pass to setupServer() */\n\thandlers: HttpHandler[];\n\t/** Register an isolated context for a test */\n\tregisterContext: (id: string, options: RegisterContextOptions) => void;\n}\n\n/**\n * Creates MSW HTTP handlers from endpoint constructs.\n *\n * Mounts endpoints on a Hono app and creates MSW handlers that intercept\n * fetch requests and route them through `app.request()` — giving frontend\n * tests full endpoint behavior (validation, auth, sessions) without HTTP.\n *\n * Each test registers its own isolated context via `registerContext()`,\n * which is resolved per-request using the `x-test-context-id` header.\n * This allows concurrent tests with their own transactions and services.\n *\n * @param endpoints - Array of endpoint constructs to create handlers for\n * @param options - Configuration including baseURL\n * @returns MSW handlers, the Hono app, and context management functions\n */\nexport function createMswHandlers(\n\tendpoints: Endpoint<any, any, any, any, any, any>[],\n\toptions: CreateMswHandlersOptions,\n): CreateMswHandlersResult {\n\tconst { baseURL } = options;\n\tconst contexts = new Map<string, MswTestContext>();\n\n\t/**\n\t * Register an isolated context for a test.\n\t * Creates a fresh ServiceDiscovery and pre-registers all provided services.\n\t */\n\tfunction registerContext(id: string, ctxOptions: RegisterContextOptions) {\n\t\tconst envParser = new EnvironmentParser({});\n\t\tconst serviceDiscovery = new ServiceDiscovery(envParser);\n\n\t\tconst serviceDefs: Service[] = [];\n\n\t\t// Register explicit services\n\t\tif (ctxOptions.services) {\n\t\t\tfor (const [name, instance] of Object.entries(ctxOptions.services)) {\n\t\t\t\tserviceDefs.push({\n\t\t\t\t\tserviceName: name,\n\t\t\t\t\tregister: () => instance,\n\t\t\t\t} as Service);\n\t\t\t}\n\t\t}\n\n\t\t// Register database service from endpoint metadata\n\t\tif (ctxOptions.database !== undefined) {\n\t\t\tfor (const endpoint of endpoints) {\n\t\t\t\tif (endpoint.databaseService) {\n\t\t\t\t\tserviceDefs.push({\n\t\t\t\t\t\tserviceName: endpoint.databaseService.serviceName,\n\t\t\t\t\t\tregister: () => ctxOptions.database,\n\t\t\t\t\t} as Service);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Register publisher service\n\t\tif (ctxOptions.publisher) {\n\t\t\tserviceDefs.push(ctxOptions.publisher);\n\t\t}\n\n\t\t// Register auditor storage service from endpoint metadata\n\t\tif (ctxOptions.auditorStorage !== undefined) {\n\t\t\tfor (const endpoint of endpoints) {\n\t\t\t\tif (endpoint.auditorStorageService) {\n\t\t\t\t\tserviceDefs.push({\n\t\t\t\t\t\tserviceName: endpoint.auditorStorageService.serviceName,\n\t\t\t\t\t\tregister: () => ctxOptions.auditorStorage,\n\t\t\t\t\t} as Service);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (serviceDefs.length > 0) {\n\t\t\tserviceDiscovery.register(serviceDefs);\n\t\t}\n\n\t\tcontexts.set(id, { serviceDiscovery });\n\t}\n\n\t// Create a Hono app per-request that uses the correct ServiceDiscovery.\n\t// We use a wrapper app that resolves the context from the header,\n\t// then delegates to a context-specific Hono app.\n\tconst handlers: HttpHandler[] = [];\n\n\tfor (const endpoint of endpoints) {\n\t\tconst method = endpoint.method.toLowerCase() as MswHttpMethod;\n\t\tif (!MSW_HTTP_METHODS.includes(method)) continue;\n\n\t\tconst mswUrl = `${baseURL}${endpoint.route}`;\n\t\tconst mswMethod = http[method];\n\n\t\thandlers.push(\n\t\t\tmswMethod(mswUrl, async ({ request }) => {\n\t\t\t\tconst contextId = request.headers.get(TEST_CONTEXT_HEADER);\n\t\t\t\tconst ctx = contextId ? contexts.get(contextId) : undefined;\n\n\t\t\t\tif (!ctx) {\n\t\t\t\t\treturn new Response(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\terror: 'Missing or unknown test context ID',\n\t\t\t\t\t\t\thint: `Set the '${TEST_CONTEXT_HEADER}' header to a registered context ID`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t\t{ status: 500, headers: { 'content-type': 'application/json' } },\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Build a fresh Hono app with this context's ServiceDiscovery\n\t\t\t\tconst app = new Hono();\n\t\t\t\tHonoEndpoint.addRoute(endpoint, ctx.serviceDiscovery as any, app);\n\n\t\t\t\tconst response = await app.request(request);\n\n\t\t\t\treturn new Response(response.body, {\n\t\t\t\t\tstatus: response.status,\n\t\t\t\t\tstatusText: response.statusText,\n\t\t\t\t\theaders: response.headers,\n\t\t\t\t});\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn {\n\t\thandlers,\n\t\tregisterContext,\n\t};\n}\n","import type {\n\tAuditActor,\n\tAuditableAction,\n\tAuditStorage,\n} from '@geekmidas/audit';\nimport { DefaultAuditor } from '@geekmidas/audit';\nimport { EnvironmentParser } from '@geekmidas/envkit';\nimport { ForbiddenError } from '@geekmidas/errors';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type {\n\tInferComposableStandardSchema,\n\tInferStandardSchema,\n} from '@geekmidas/schema';\nimport {\n\trunWithRequestContext,\n\ttype Service,\n\tServiceDiscovery,\n\ttype ServiceRecord,\n} from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { publishConstructEvents } from '../publisher';\nimport type { HttpMethod } from '../types';\nimport type { MappedAudit } from './audit';\nimport {\n\ttype CookieOptions,\n\tEndpoint,\n\ttype EndpointSchemas,\n\tResponseBuilder,\n} from './Endpoint';\nimport {\n\tcreateCookieHeaderAccessor,\n\tcreateObjectHeaders,\n} from './lazyAccessors';\nimport {\n\ttype AuditExecutionContext,\n\texecuteWithAuditTransaction,\n} from './processAudits';\n\nexport type TestHttpResponse<TBody = any> = {\n\tbody: TBody;\n\tstatus: number;\n\theaders: Record<string, string | string[]>;\n};\n\n/**\n * Serializes a cookie into a Set-Cookie header string\n */\nfunction serializeCookie(\n\tname: string,\n\tvalue: string,\n\toptions?: CookieOptions,\n): string {\n\tlet cookieString = `${name}=${value}`;\n\n\tif (options) {\n\t\tif (options.maxAge !== undefined) {\n\t\t\tcookieString += `; Max-Age=${options.maxAge}`;\n\t\t}\n\t\tif (options.expires) {\n\t\t\tcookieString += `; Expires=${options.expires.toUTCString()}`;\n\t\t}\n\t\tif (options.domain) {\n\t\t\tcookieString += `; Domain=${options.domain}`;\n\t\t}\n\t\tif (options.path) {\n\t\t\tcookieString += `; Path=${options.path}`;\n\t\t}\n\t\tif (options.httpOnly) {\n\t\t\tcookieString += '; HttpOnly';\n\t\t}\n\t\tif (options.secure) {\n\t\t\tcookieString += '; Secure';\n\t\t}\n\t\tif (options.sameSite) {\n\t\t\tcookieString += `; SameSite=${options.sameSite}`;\n\t\t}\n\t}\n\n\treturn cookieString;\n}\n\nexport class TestEndpointAdaptor<\n\tTRoute extends string,\n\tTMethod extends HttpMethod,\n\tTInput extends EndpointSchemas = {},\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTSession = unknown,\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTEventPublisherServiceName extends string = string,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\tTAuditStorageServiceName extends string = string,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> {\n\tstatic getDefaultServiceDiscover<\n\t\tTRoute extends string,\n\t\tTMethod extends HttpMethod,\n\t\tTInput extends EndpointSchemas = {},\n\t\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\t\tTServices extends Service[] = [],\n\t\tTLogger extends Logger = Logger,\n\t\tTSession = unknown,\n\t\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\t\tTEventPublisherServiceName extends string = string,\n\t\tTAuditStorage extends AuditStorage | undefined = undefined,\n\t\tTAuditStorageServiceName extends string = string,\n\t\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>,\n\t\tTDatabase = undefined,\n\t\tTDatabaseServiceName extends string = string,\n\t>(\n\t\t_endpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTAuditAction,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t) {\n\t\treturn ServiceDiscovery.getInstance(new EnvironmentParser({}));\n\t}\n\tconstructor(\n\t\tprivate readonly endpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTAuditAction,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t\tprivate serviceDiscovery: ServiceDiscovery<any> = TestEndpointAdaptor.getDefaultServiceDiscover(\n\t\t\tendpoint,\n\t\t),\n\t) {}\n\n\tasync fullRequest(\n\t\tctx: TestRequestAdaptor<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t): Promise<TestHttpResponse<InferStandardSchema<TOutSchema>>> {\n\t\tconst body = await this.endpoint.parseInput((ctx as any).body, 'body');\n\t\tconst query = await this.endpoint.parseInput((ctx as any).query, 'query');\n\t\tconst params = await this.endpoint.parseInput(\n\t\t\t(ctx as any).params,\n\t\t\t'params',\n\t\t);\n\n\t\t// Lazy accessors - defer parsing until needed\n\t\tconst header = createObjectHeaders(ctx.headers);\n\t\tconst cookie = createCookieHeaderAccessor(ctx.headers.cookie);\n\n\t\t// Request context setup\n\t\tconst startTime = Date.now();\n\t\tconst requestId =\n\t\t\t(ctx as any).requestId ??\n\t\t\tctx.headers['x-request-id'] ??\n\t\t\tcrypto.randomUUID();\n\n\t\tconst logger = this.endpoint.logger.child({\n\t\t\trequestId,\n\t\t\troute: this.endpoint.route,\n\t\t\thost: ctx.headers.host,\n\t\t\tmethod: this.endpoint.method,\n\t\t}) as TLogger;\n\n\t\t// Get database from context for session extraction\n\t\tconst rawDb = (ctx as any).database as TDatabase;\n\n\t\t// Wrap handler execution in request context\n\t\treturn runWithRequestContext({ logger, requestId, startTime }, async () => {\n\t\t\tconst session = await this.endpoint.getSession({\n\t\t\t\tlogger,\n\t\t\t\tservices: ctx.services,\n\t\t\t\theader,\n\t\t\t\tcookie,\n\t\t\t\t...(rawDb !== undefined && { db: rawDb }),\n\t\t\t} as any);\n\n\t\t\t// Check authorization\n\t\t\tconst isAuthorized = await this.endpoint.authorize({\n\t\t\t\theader,\n\t\t\t\tcookie,\n\t\t\t\tservices: ctx.services,\n\t\t\t\tlogger,\n\t\t\t\tsession,\n\t\t\t\t...(rawDb !== undefined && { db: rawDb }),\n\t\t\t\tbody,\n\t\t\t\tquery,\n\t\t\t\tparams,\n\t\t\t} as any);\n\n\t\t\tif (!isAuthorized) {\n\t\t\t\tlogger.warn('Forbidden access attempt');\n\t\t\t\tthrow new ForbiddenError(\n\t\t\t\t\t'Forbidden access to the endpoint',\n\t\t\t\t\t'You do not have permission to access this resource.',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Create audit context if audit storage is provided\n\t\t\t// The auditorStorage instance is required when endpoint uses .auditor()\n\t\t\tconst auditorStorage = (ctx as any).auditorStorage as TAuditStorage;\n\t\t\tlet auditContext: AuditExecutionContext<TAuditAction> | undefined;\n\n\t\t\tif (auditorStorage) {\n\t\t\t\t// Extract actor if configured\n\t\t\t\tlet actor: AuditActor = { id: 'system', type: 'system' };\n\t\t\t\tif (this.endpoint.actorExtractor) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tactor = await this.endpoint.actorExtractor({\n\t\t\t\t\t\t\tservices: ctx.services as any,\n\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tlogger.error(error as Error, 'Failed to extract actor for audits');\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst auditor = new DefaultAuditor<TAuditAction>({\n\t\t\t\t\tactor,\n\t\t\t\t\tstorage: auditorStorage as AuditStorage,\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\tendpoint: this.endpoint.route,\n\t\t\t\t\t\tmethod: this.endpoint.method,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tauditContext = { auditor, storage: auditorStorage as AuditStorage };\n\t\t\t}\n\n\t\t\t// Warn if declarative audits are configured but no audit storage\n\t\t\tconst audits = this.endpoint.audits as MappedAudit<\n\t\t\t\tTAuditAction,\n\t\t\t\tTOutSchema\n\t\t\t>[];\n\t\t\tif (!auditContext && audits?.length) {\n\t\t\t\tlogger.warn('No auditor storage service available');\n\t\t\t}\n\n\t\t\t// Execute handler with automatic audit transaction support\n\t\t\tconst result = await executeWithAuditTransaction(\n\t\t\t\tauditContext,\n\t\t\t\tasync (auditor) => {\n\t\t\t\t\t// Use audit transaction as db if available (when storage has same database)\n\t\t\t\t\t// For testing, the tester controls whether to use transactional auditing\n\t\t\t\t\tconst trx = auditor?.getTransaction?.();\n\t\t\t\t\tconst db = trx ?? rawDb;\n\n\t\t\t\t\tconst responseBuilder = new ResponseBuilder();\n\t\t\t\t\tconst response = await this.endpoint.handler(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbody,\n\t\t\t\t\t\t\tquery,\n\t\t\t\t\t\t\tparams,\n\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\tservices: ctx.services,\n\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\tauditor,\n\t\t\t\t\t\t\tdb,\n\t\t\t\t\t\t} as any,\n\t\t\t\t\t\tresponseBuilder,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Check if response has metadata\n\t\t\t\t\tlet data = response;\n\t\t\t\t\tlet metadata = responseBuilder.getMetadata();\n\n\t\t\t\t\tif (Endpoint.hasMetadata(response)) {\n\t\t\t\t\t\tdata = response.data;\n\t\t\t\t\t\tmetadata = response.metadata;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst output = await this.endpoint.parseOutput(data);\n\n\t\t\t\t\treturn { output, metadata, responseBuilder };\n\t\t\t\t},\n\t\t\t\t// Process declarative audits after handler (inside transaction)\n\t\t\t\tasync (result, auditor) => {\n\t\t\t\t\tif (!audits?.length) return;\n\n\t\t\t\t\tfor (const audit of audits) {\n\t\t\t\t\t\tif (audit.when && !audit.when(result.output as any)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst payload = audit.payload(result.output as any);\n\t\t\t\t\t\tconst entityId = audit.entityId?.(result.output as any);\n\t\t\t\t\t\tauditor.audit(audit.type as any, payload as any, {\n\t\t\t\t\t\t\ttable: audit.table,\n\t\t\t\t\t\t\tentityId,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t// Pass rawDb so storage can reuse existing transactions\n\t\t\t\t{ db: rawDb },\n\t\t\t);\n\n\t\t\tconst { output, metadata } = result;\n\n\t\t\tctx.publisher && (await this.serviceDiscovery.register([ctx.publisher]));\n\t\t\tawait publishConstructEvents(\n\t\t\t\tthis.endpoint,\n\t\t\t\toutput,\n\t\t\t\tthis.serviceDiscovery,\n\t\t\t);\n\n\t\t\t// Convert cookies to Set-Cookie headers\n\t\t\tconst headers: Record<string, string | string[]> = {\n\t\t\t\t...(metadata.headers || {}),\n\t\t\t};\n\n\t\t\tif (metadata.cookies && metadata.cookies.size > 0) {\n\t\t\t\tconst setCookieValues: string[] = [];\n\t\t\t\tfor (const [name, cookie] of metadata.cookies.entries()) {\n\t\t\t\t\tsetCookieValues.push(\n\t\t\t\t\t\tserializeCookie(name, cookie.value, cookie.options),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\theaders['set-cookie'] = setCookieValues;\n\t\t\t}\n\n\t\t\t// Return HTTP response format\n\t\t\treturn {\n\t\t\t\tbody: output,\n\t\t\t\tstatus: metadata.status || 200,\n\t\t\t\theaders,\n\t\t\t};\n\t\t});\n\t}\n\n\tasync request(\n\t\tctx: TestRequestAdaptor<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t): Promise<InferStandardSchema<TOutSchema>> {\n\t\tconst response = await this.fullRequest(ctx);\n\t\treturn response.body;\n\t}\n}\n\n/**\n * Conditional audit storage requirement - required when TAuditStorage is configured\n */\ntype AuditStorageRequirement<\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n> = TAuditStorage extends undefined\n\t? {}\n\t: {\n\t\t\t/** Audit storage instance - required when endpoint uses .auditor() */\n\t\t\tauditorStorage: TAuditStorage;\n\t\t};\n\n/**\n * Conditional database requirement - required when TDatabase is configured\n */\ntype DatabaseRequirement<TDatabase = undefined> = TDatabase extends undefined\n\t? {}\n\t: {\n\t\t\t/** Database instance - required when endpoint uses .database() */\n\t\t\tdatabase: TDatabase;\n\t\t};\n\nexport type TestRequestAdaptor<\n\tTInput extends EndpointSchemas = {},\n\tTServices extends Service[] = [],\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTEventPublisherServiceName extends string = string,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\t_TAuditStorageServiceName extends string = string,\n\tTDatabase = undefined,\n\t_TDatabaseServiceName extends string = string,\n> = {\n\tservices: ServiceRecord<TServices>;\n\theaders: Record<string, string>;\n\tpublisher?: Service<TEventPublisherServiceName, TEventPublisher>;\n} & InferComposableStandardSchema<TInput> &\n\tAuditStorageRequirement<TAuditStorage> &\n\tDatabaseRequirement<TDatabase>;\n","import type { AuditableAction, Auditor, AuditStorage } from '@geekmidas/audit';\nimport { DefaultAuditor } from '@geekmidas/audit';\nimport { EnvironmentParser } from '@geekmidas/envkit';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type {\n\tComposableStandardSchema,\n\tInferComposableStandardSchema,\n\tInferStandardSchema,\n} from '@geekmidas/schema';\nimport {\n\trunWithRequestContext,\n\ttype Service,\n\tServiceDiscovery,\n\ttype ServiceRecord,\n} from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { publishEvents } from '../publisher';\nimport type { Function } from './Function';\nimport { FunctionBuilder } from './FunctionBuilder';\n\nexport class TestFunctionAdaptor<\n\tTInput extends ComposableStandardSchema | undefined = undefined,\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTEventPublisherServiceName extends string = string,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\tTAuditStorageServiceName extends string = string,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n> {\n\tstatic getDefaultServiceDiscovery<\n\t\tTInput extends ComposableStandardSchema | undefined = undefined,\n\t\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\t\tTServices extends Service[] = [],\n\t\tTLogger extends Logger = Logger,\n\t\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\t\tTEventPublisherServiceName extends string = string,\n\t\tTAuditStorage extends AuditStorage | undefined = undefined,\n\t\tTAuditStorageServiceName extends string = string,\n\t\tTDatabase = undefined,\n\t\tTDatabaseServiceName extends string = string,\n\t\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>,\n\t>(\n\t\t_fn: Function<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTOutSchema,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName,\n\t\t\tTAuditAction,\n\t\t\tany\n\t\t>,\n\t) {\n\t\treturn ServiceDiscovery.getInstance(new EnvironmentParser({}));\n\t}\n\n\tconstructor(\n\t\tprivate readonly fn: Function<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTOutSchema,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName,\n\t\t\tTAuditAction,\n\t\t\tany\n\t\t>,\n\t\tprivate serviceDiscovery: ServiceDiscovery<any> = TestFunctionAdaptor.getDefaultServiceDiscovery(\n\t\t\tfn,\n\t\t),\n\t) {}\n\n\tasync invoke(\n\t\tctx: TestFunctionRequest<\n\t\t\tTInput,\n\t\t\tTServices,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTDatabase,\n\t\t\tTAuditAction\n\t\t>,\n\t): Promise<InferStandardSchema<TOutSchema>> {\n\t\t// Parse input if schema is provided\n\n\t\tconst parsedInput = await FunctionBuilder.parseComposableStandardSchema(\n\t\t\tctx.input,\n\t\t\tthis.fn.input,\n\t\t);\n\n\t\t// Create logger with context\n\t\tconst logger = this.fn.logger.child({\n\t\t\ttest: true,\n\t\t}) as TLogger;\n\n\t\t// Register services (use provided services or register from function)\n\t\tlet services: ServiceRecord<TServices>;\n\t\tif (ctx.services) {\n\t\t\tservices = ctx.services;\n\t\t} else {\n\t\t\tservices = await this.serviceDiscovery.register(this.fn.services);\n\t\t}\n\n\t\t// Resolve database (use provided db or register from function)\n\t\tlet db: TDatabase | undefined;\n\t\tif ('db' in ctx && ctx.db !== undefined) {\n\t\t\tdb = ctx.db;\n\t\t} else if (this.fn.databaseService) {\n\t\t\tconst dbServices = await this.serviceDiscovery.register([\n\t\t\t\tthis.fn.databaseService,\n\t\t\t]);\n\t\t\tdb = dbServices[\n\t\t\t\tthis.fn.databaseService.serviceName as keyof typeof dbServices\n\t\t\t] as TDatabase;\n\t\t}\n\n\t\t// Resolve auditor (use provided auditor or create from function)\n\t\tlet auditor: Auditor<TAuditAction> | undefined;\n\t\tif ('auditor' in ctx && ctx.auditor !== undefined) {\n\t\t\tauditor = ctx.auditor;\n\t\t} else if (this.fn.auditorStorageService) {\n\t\t\tconst auditServices = await this.serviceDiscovery.register([\n\t\t\t\tthis.fn.auditorStorageService,\n\t\t\t]);\n\t\t\tconst storage = auditServices[\n\t\t\t\tthis.fn.auditorStorageService.serviceName as keyof typeof auditServices\n\t\t\t] as AuditStorage;\n\n\t\t\tauditor = new DefaultAuditor<TAuditAction>({\n\t\t\t\tactor: { id: 'system', type: 'system' },\n\t\t\t\tstorage,\n\t\t\t\tmetadata: {\n\t\t\t\t\tfunction: this.fn.type,\n\t\t\t\t\ttest: true,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\t// Wrap execution in request context for service access via context.getLogger()\n\t\tconst requestId = `test-${Date.now()}`;\n\t\tconst startTime = Date.now();\n\n\t\treturn runWithRequestContext({ logger, requestId, startTime }, async () => {\n\t\t\t// Execute the function\n\t\t\tconst response = await this.fn.fn({\n\t\t\t\tinput: parsedInput,\n\t\t\t\tservices,\n\t\t\t\tlogger,\n\t\t\t\tdb,\n\t\t\t\tauditor,\n\t\t\t} as any);\n\n\t\t\t// Parse output if schema is provided\n\t\t\tconst output = await this.fn.parseOutput(response);\n\n\t\t\t// Flush audits if any were recorded\n\t\t\tif (auditor) {\n\t\t\t\tconst records = auditor.getRecords();\n\t\t\t\tif (records.length > 0) {\n\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t{ auditCount: records.length },\n\t\t\t\t\t\t'Flushing function audits',\n\t\t\t\t\t);\n\t\t\t\t\tawait auditor.flush();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Register publisher service if provided in context\n\n\t\t\tawait publishEvents(\n\t\t\t\tlogger,\n\t\t\t\tthis.serviceDiscovery,\n\t\t\t\tthis.fn.events,\n\t\t\t\toutput,\n\t\t\t\tthis.fn.publisherService,\n\t\t\t);\n\n\t\t\treturn output;\n\t\t}) as Promise<InferStandardSchema<TOutSchema>>;\n\t}\n}\n\nexport type TestFunctionRequest<\n\tTInput extends ComposableStandardSchema | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTEventPublisherServiceName extends string = string,\n\tTDatabase = undefined,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n> = {\n\tinput: InferComposableStandardSchema<TInput>;\n\tservices: ServiceRecord<TServices>;\n\tpublisher?: Service<TEventPublisherServiceName, TEventPublisher>;\n\tdb?: TDatabase;\n\tauditor?: Auditor<TAuditAction>;\n} & InferComposableStandardSchema<{ input: TInput }>;\n","import { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport { runWithRequestContext, ServiceDiscovery } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type { Queue } from './Queue';\n\n/**\n * In-memory test driver for a {@link Queue} worker. Hand it a batch of messages\n * and it registers the queue's services, runs the handler in a request context,\n * and returns the handler result — no SQS/pg-boss required.\n *\n * @example\n * const adapter = new TestQueueAdaptor(ordersQueue);\n * await adapter.invoke({ messages: [{ orderId: '123' }] });\n */\nexport class TestQueueAdaptor<\n\tTName extends string = string,\n\tTMessage extends StandardSchemaV1 = StandardSchemaV1,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n> {\n\tstatic getDefaultServiceDiscovery() {\n\t\treturn ServiceDiscovery.getInstance(new EnvironmentParser({}));\n\t}\n\n\tconstructor(\n\t\tprivate readonly queue: Queue<TName, TMessage, TServices, TLogger>,\n\t\tprivate serviceDiscovery: ServiceDiscovery<any> = TestQueueAdaptor.getDefaultServiceDiscovery(),\n\t) {}\n\n\tasync invoke(\n\t\trequest: TestQueueRequest<TMessage, TServices>,\n\t): Promise<unknown> {\n\t\tconst logger = this.queue.logger.child({ test: true }) as TLogger;\n\n\t\tconst services =\n\t\t\trequest.services ??\n\t\t\t((await this.serviceDiscovery.register(\n\t\t\t\tthis.queue.services,\n\t\t\t)) as ServiceRecord<TServices>);\n\n\t\tconst requestId = `test-${Date.now()}`;\n\t\tconst startTime = Date.now();\n\n\t\treturn runWithRequestContext({ logger, requestId, startTime }, () =>\n\t\t\tthis.queue.handler({\n\t\t\t\tmessages: request.messages,\n\t\t\t\tservices,\n\t\t\t\tlogger,\n\t\t\t}),\n\t\t);\n\t}\n}\n\nexport type TestQueueRequest<\n\tTMessage extends StandardSchemaV1 = StandardSchemaV1,\n\tTServices extends Service[] = [],\n> = {\n\tmessages: InferStandardSchema<TMessage>[];\n\tservices?: ServiceRecord<TServices>;\n};\n","import { EnvironmentParser } from '@geekmidas/envkit';\nimport type {\n\tEventPublisher,\n\tExtractPublisherMessage,\n} from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport type { InferStandardSchema } from '@geekmidas/schema';\nimport type { Service, ServiceRecord } from '@geekmidas/services';\nimport { runWithRequestContext, ServiceDiscovery } from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { publishEvents } from '../publisher';\nimport type { Subscriber } from './Subscriber';\n\n// Helper type to extract payload types for subscribed events\ntype ExtractEventPayloads<\n\tTPublisher extends EventPublisher<any> | undefined,\n\tTEventTypes extends any[],\n> = TPublisher extends EventPublisher<any>\n\t? Extract<ExtractPublisherMessage<TPublisher>, { type: TEventTypes[number] }>\n\t: never;\n\nexport class TestSubscriberAdaptor<\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTEventPublisherServiceName extends string = string,\n\tTSubscribedEvents extends\n\t\tExtractPublisherMessage<TEventPublisher>['type'][] = ExtractPublisherMessage<TEventPublisher>['type'][],\n> {\n\tstatic getDefaultServiceDiscovery() {\n\t\treturn ServiceDiscovery.getInstance(new EnvironmentParser({}));\n\t}\n\n\tconstructor(\n\t\tprivate readonly subscriber: Subscriber<\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tOutSchema,\n\t\t\tTEventPublisher,\n\t\t\tTEventPublisherServiceName,\n\t\t\tTSubscribedEvents\n\t\t>,\n\t\tprivate serviceDiscovery: ServiceDiscovery<any> = TestSubscriberAdaptor.getDefaultServiceDiscovery(),\n\t) {}\n\n\tasync invoke(\n\t\trequest: TestSubscriberRequest<\n\t\t\tTEventPublisher,\n\t\t\tTSubscribedEvents,\n\t\t\tTServices\n\t\t>,\n\t): Promise<InferStandardSchema<OutSchema>> {\n\t\t// Create logger with test context\n\t\tconst logger = this.subscriber.logger.child({\n\t\t\ttest: true,\n\t\t}) as TLogger;\n\n\t\t// Resolve services (use provided or auto-register)\n\t\tlet services: ServiceRecord<TServices>;\n\t\tif (request.services) {\n\t\t\tservices = request.services;\n\t\t} else {\n\t\t\tservices = await this.serviceDiscovery.register(this.subscriber.services);\n\t\t}\n\n\t\t// Filter events to only subscribed types\n\t\tconst filteredEvents = this.filterEvents(request.events);\n\n\t\t// Return early if no events after filtering (mirrors AWSLambdaSubscriber)\n\t\tif (filteredEvents.length === 0) {\n\t\t\treturn { batchItemFailures: [] } as any;\n\t\t}\n\n\t\t// Wrap execution in request context for service access via context.getLogger()\n\t\tconst requestId = `test-${Date.now()}`;\n\t\tconst startTime = Date.now();\n\n\t\treturn runWithRequestContext({ logger, requestId, startTime }, async () => {\n\t\t\t// Execute the subscriber handler\n\t\t\tconst result = await this.subscriber.handler({\n\t\t\t\tevents: filteredEvents,\n\t\t\t\tservices,\n\t\t\t\tlogger,\n\t\t\t});\n\n\t\t\t// Validate output if schema is provided\n\t\t\tlet output: any = result;\n\t\t\tif (this.subscriber.outputSchema && result) {\n\t\t\t\tconst validationResult =\n\t\t\t\t\tawait this.subscriber.outputSchema['~standard'].validate(result);\n\n\t\t\t\tif (validationResult.issues) {\n\t\t\t\t\tthrow new Error('Subscriber output validation failed');\n\t\t\t\t}\n\n\t\t\t\toutput = validationResult.value;\n\t\t\t}\n\n\t\t\t// Publish events if configured\n\t\t\tawait publishEvents(\n\t\t\t\tlogger,\n\t\t\t\tthis.serviceDiscovery,\n\t\t\t\tthis.subscriber.events,\n\t\t\t\toutput,\n\t\t\t\tthis.subscriber.publisherService,\n\t\t\t);\n\n\t\t\treturn output;\n\t\t}) as Promise<InferStandardSchema<OutSchema>>;\n\t}\n\n\tprivate filterEvents(\n\t\tevents: ExtractEventPayloads<TEventPublisher, TSubscribedEvents>[],\n\t): ExtractEventPayloads<TEventPublisher, TSubscribedEvents>[] {\n\t\tif (!this.subscriber.subscribedEvents) {\n\t\t\treturn events;\n\t\t}\n\n\t\treturn events.filter((event: any) =>\n\t\t\tthis.subscriber.subscribedEvents!.includes(event.type),\n\t\t);\n\t}\n}\n\nexport type TestSubscriberRequest<\n\tTEventPublisher extends EventPublisher<any> | undefined = undefined,\n\tTSubscribedEvents extends any[] = [],\n\tTServices extends Service[] = [],\n> = {\n\tevents: ExtractEventPayloads<TEventPublisher, TSubscribedEvents>[];\n\tservices?: ServiceRecord<TServices>;\n};\n","/**\n * `featureTest`: an app, driven the way it runs deployed.\n *\n * A browser signs in and calls the API; the API calls the auth server; each\n * over its URL, each database in its own transaction. Nothing runs as a server:\n * every URL an app's constructs answer on is served in-process through MSW,\n * from the real handler, for the test that made the request. Everything else\n * with a real local equivalent — Postgres, Mailpit, MinIO — is used directly.\n *\n * ```ts\n * // test/config.ts\n * export const it = featureTest({\n * modules: import.meta.glob(['../../constructs/*.ts', '../src/endpoints/**\\/*.ts'], { eager: true }),\n * database,\n * browser: Browser,\n * });\n *\n * // a test\n * it('shows the signed-in user their profile', async ({ browser, mailbox }) => {\n * await browser.auth.signIn.magicLink({ email: 'ada@shop.test' });\n * await browser.visit((await mailbox('ada@shop.test').last()).link!);\n * expect(await browser.api.get('/profile')).toMatchObject({ email: 'ada@shop.test' });\n * });\n * ```\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { EnvironmentParser } from '@geekmidas/envkit';\nimport { provideKey } from '@geekmidas/manifest';\nimport { ServiceDiscovery, serviceContext } from '@geekmidas/services';\nimport { Browser as TestBrowser } from '@geekmidas/testkit/browser';\nimport {\n\tcurrentTestContext,\n\tinstallContextFetch,\n\trunAsServer,\n\trunInTestContext,\n\ttestContextOf,\n} from '@geekmidas/testkit/context';\nimport { createMailbox, type Mailbox } from '@geekmidas/testkit/mailbox';\nimport { TransactionRegistry } from '@geekmidas/testkit/transactions';\nimport { Hono } from 'hono';\nimport type { Kysely } from 'kysely';\nimport { http } from 'msw';\nimport { setupServer } from 'msw/node';\nimport { afterAll, beforeAll, test } from 'vitest';\nimport { BetterAuth } from '../auth';\nimport { KyselyDatabase } from '../database/kysely';\nimport { Email } from '../email';\nimport { Endpoint } from '../endpoints/Endpoint';\nimport { HonoEndpoint } from '../endpoints/HonoEndpointAdaptor';\n\nexport interface FeatureTestOptions<TBrowser extends TestBrowser, DB> {\n\t/**\n\t * The modules an app's constructs and endpoints are exported from — what\n\t * `import.meta.glob(…, { eager: true })` returns, or a list of modules.\n\t * Discovery reads exports, as the build does.\n\t */\n\tmodules: Record<string, unknown> | readonly unknown[];\n\t/** The app's own database: what a test receives as `db`. */\n\tdatabase?: KyselyDatabase<DB>;\n\t/** The browser each test gets — the app's subclass, with its clients. */\n\tbrowser?: new () => TBrowser;\n\t/** The environment `gkm test` injected. Defaults to `process.env`. */\n\tenv?: Record<string, string | undefined>;\n}\n\nexport interface FeatureContext<TBrowser extends TestBrowser, DB> {\n\t/** This test's browser, already the global `fetch`. */\n\tbrowser: TBrowser;\n\t/** The app database's transaction for this test, rolled back after it. */\n\tdb: Kysely<DB>;\n\t/** The mail sent to an address during this test, read from Mailpit. */\n\tmailbox: (address: string) => Mailbox;\n}\n\ntype FeatureFn<TBrowser extends TestBrowser, DB> = (\n\tcontext: FeatureContext<TBrowser, DB>,\n) => unknown;\n\nexport interface FeatureIt<TBrowser extends TestBrowser, DB> {\n\t(name: string, fn: FeatureFn<TBrowser, DB>, timeout?: number): void;\n\tonly(name: string, fn: FeatureFn<TBrowser, DB>, timeout?: number): void;\n\tskip(name: string, fn: FeatureFn<TBrowser, DB>, timeout?: number): void;\n}\n\n/** Everything one test owns, found again from its id. */\ninterface ContextState {\n\ttransactions: TransactionRegistry;\n\tdiscovery: ServiceDiscovery<any>;\n\t/** One app per surface, with this test's services. */\n\tsurfaces: Map<string, Promise<Hono>>;\n\t/** One auth server per construct, on this test's transaction. */\n\tauth: Map<BetterAuth, Promise<Hono>>;\n\t/** The addresses this test read mail for, cleared after it. */\n\taddresses: Set<string>;\n}\n\n/** Every test in flight in this process, by id. */\nconst contexts = new Map<string, ContextState>();\n\n/** Databases already resolving to test transactions — patched once each. */\nconst bound = new WeakSet<KyselyDatabase>();\n\nexport function featureTest<\n\tTBrowser extends TestBrowser = TestBrowser,\n\tDB = unknown,\n>(options: FeatureTestOptions<TBrowser, DB>): FeatureIt<TBrowser, DB> {\n\tconst env = options.env ?? process.env;\n\tconst envParser = new EnvironmentParser({ ...env });\n\tconst found = discover(options.modules);\n\tconst BrowserClass = (options.browser ?? TestBrowser) as new () => TBrowser;\n\n\tfor (const database of found.databases) bindToTests(database);\n\n\tconst network = setupServer(\n\t\t...surfaceHandlers(found.endpoints, env),\n\t\t...found.auths.map((auth) => authHandler(auth, env, envParser)),\n\t);\n\n\tconst inbox = found.emails\n\t\t.map((email) => env[provideKey(email.id, 'inboxUrl')])\n\t\t.find(Boolean);\n\tconst readMail = inbox ? createMailbox({ inbox }) : undefined;\n\n\tlet restoreFetch: () => void = () => {};\n\tbeforeAll(() => {\n\t\tnetwork.listen({\n\t\t\tonUnhandledRequest(request, print) {\n\t\t\t\t// A real local service — Mailpit, MinIO — is used directly; only a\n\t\t\t\t// URL with nothing real behind it has to be answered here.\n\t\t\t\tif (isLocal(new URL(request.url))) return;\n\t\t\t\tprint.error();\n\t\t\t},\n\t\t});\n\t\t// After MSW's interceptor, so the stamp is on before a request is caught.\n\t\trestoreFetch = installContextFetch();\n\t});\n\tafterAll(() => {\n\t\trestoreFetch();\n\t\tnetwork.close();\n\t});\n\n\tconst run = (fn: FeatureFn<TBrowser, DB>) => async (): Promise<void> => {\n\t\tconst id = randomUUID();\n\t\tconst state: ContextState = {\n\t\t\ttransactions: new TransactionRegistry(),\n\t\t\tdiscovery: new ServiceDiscovery(envParser),\n\t\t\tsurfaces: new Map(),\n\t\t\tauth: new Map(),\n\t\t\taddresses: new Set(),\n\t\t};\n\t\tcontexts.set(id, state);\n\n\t\ttry {\n\t\t\tawait runInTestContext(id, async () => {\n\t\t\t\tconst browser = new BrowserClass();\n\t\t\t\tconst restore = browser.install();\n\t\t\t\ttry {\n\t\t\t\t\tawait fn({\n\t\t\t\t\t\tbrowser,\n\t\t\t\t\t\tdb: options.database\n\t\t\t\t\t\t\t? await state.transactions.get(\n\t\t\t\t\t\t\t\t\toptions.database.id,\n\t\t\t\t\t\t\t\t\turlOf(options.database, envParser),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: (undefined as never),\n\t\t\t\t\t\tmailbox: (address) => {\n\t\t\t\t\t\t\tif (!readMail) throw new NoInbox();\n\t\t\t\t\t\t\tstate.addresses.add(address);\n\t\t\t\t\t\t\treturn readMail(address);\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t} finally {\n\t\t\t\t\trestore();\n\t\t\t\t}\n\t\t\t});\n\t\t} finally {\n\t\t\tcontexts.delete(id);\n\t\t\tawait Promise.all([\n\t\t\t\tstate.transactions.rollbackAll(),\n\t\t\t\t...[...state.addresses].map((address) => readMail?.(address).clear()),\n\t\t\t]);\n\t\t}\n\t};\n\n\tconst it = ((name, fn, timeout) => test(name, run(fn), timeout)) as FeatureIt<\n\t\tTBrowser,\n\t\tDB\n\t>;\n\tit.only = (name, fn, timeout) => test.only(name, run(fn), timeout);\n\tit.skip = (name, fn, timeout) => test.skip(name, run(fn), timeout);\n\treturn it;\n}\n\n/** What an app's modules export, sorted by what a test does with it. */\nfunction discover(modules: FeatureTestOptions<any, any>['modules']) {\n\tconst values = (\n\t\tArray.isArray(modules) ? modules : Object.values(modules)\n\t).flatMap((module) =>\n\t\tmodule && typeof module === 'object' ? Object.values(module) : [],\n\t);\n\n\treturn {\n\t\tendpoints: values.filter((value): value is Endpoint<any, any, any, any> =>\n\t\t\tEndpoint.isEndpoint(value),\n\t\t),\n\t\tdatabases: values.filter(\n\t\t\t(value): value is KyselyDatabase => value instanceof KyselyDatabase,\n\t\t),\n\t\tauths: values.filter(\n\t\t\t(value): value is BetterAuth => value instanceof BetterAuth,\n\t\t),\n\t\temails: values.filter((value): value is Email => value instanceof Email),\n\t};\n}\n\n/**\n * Make a database resolve, inside a test, to that test's transaction.\n *\n * Everything reaches a database through its service — an endpoint through\n * service discovery, the auth server through its tenant directly — so this is\n * the one place a test's connection can be handed over. Outside a test it is\n * the database it always was.\n */\nfunction bindToTests(database: KyselyDatabase): void {\n\tif (bound.has(database)) return;\n\tbound.add(database);\n\n\tconst service = database.service as { register: (options: any) => unknown };\n\tconst register = service.register;\n\n\tservice.register = (options) => {\n\t\tconst context = currentTestContext();\n\t\tconst state = context && contexts.get(context.id);\n\t\tif (!state) return register(options);\n\n\t\treturn state.transactions.get(\n\t\t\tdatabase.id,\n\t\t\turlOf(database, options.envParser),\n\t\t);\n\t};\n}\n\nfunction urlOf(database: { id: string }, envParser: EnvironmentParser<{}>) {\n\treturn envParser\n\t\t.create((get) => ({ url: get(provideKey(database.id, 'url')).string() }))\n\t\t.parse().url;\n}\n\n/** Each surface's endpoints, served at its URL for the test that asked. */\nfunction surfaceHandlers(\n\tendpoints: Endpoint<any, any, any, any>[],\n\tenv: Record<string, string | undefined>,\n) {\n\tconst bySurface = new Map<string, Endpoint<any, any, any, any>[]>();\n\tfor (const endpoint of endpoints) {\n\t\tconst surface = endpoint.surface?.id;\n\t\tif (!surface) continue;\n\t\tbySurface.set(surface, [...(bySurface.get(surface) ?? []), endpoint]);\n\t}\n\n\treturn [...bySurface].flatMap(([surface, served]) => {\n\t\tconst base = env[provideKey(surface, 'url')];\n\t\tif (!base) return [];\n\n\t\treturn http.all(`${trim(base)}/*`, ({ request }) =>\n\t\t\tserve(request, async (state) => {\n\t\t\t\tlet app = state.surfaces.get(surface);\n\t\t\t\tif (!app) {\n\t\t\t\t\tapp = Promise.resolve().then(() => {\n\t\t\t\t\t\tconst hono = new Hono();\n\t\t\t\t\t\tHonoEndpoint.addRoutes(served, state.discovery, hono);\n\t\t\t\t\t\treturn hono;\n\t\t\t\t\t});\n\t\t\t\t\tstate.surfaces.set(surface, app);\n\t\t\t\t}\n\t\t\t\treturn (await app).fetch(request);\n\t\t\t}),\n\t\t);\n\t});\n}\n\n/** An auth server, served at its URL, on the transaction of the test that asked. */\nfunction authHandler(\n\tauth: BetterAuth,\n\tenv: Record<string, string | undefined>,\n\tenvParser: EnvironmentParser<{}>,\n) {\n\tconst base = trim(env[provideKey(auth.id, 'url')] ?? '');\n\n\treturn http.all(`${base}${auth.basePath}/*`, ({ request }) =>\n\t\tserve(request, async (state) => {\n\t\t\tlet app = state.auth.get(auth);\n\t\t\tif (!app) {\n\t\t\t\tapp = auth\n\t\t\t\t\t.server({ envParser, context: serviceContext })\n\t\t\t\t\t.then(({ app }) => app);\n\t\t\t\tstate.auth.set(auth, app);\n\t\t\t}\n\t\t\treturn (await app).fetch(request);\n\t\t}),\n\t);\n}\n\n/** Handle a request as the server side of the test it was made for. */\nasync function serve(\n\trequest: Request,\n\thandle: (state: ContextState) => Promise<Response>,\n): Promise<Response> {\n\tconst id = testContextOf(request);\n\tconst state = id ? contexts.get(id) : undefined;\n\tif (!id || !state) throw new UnknownTestContext(request.url, id);\n\n\treturn runAsServer(id, () => handle(state));\n}\n\nfunction trim(url: string): string {\n\treturn url.replace(/\\/$/, '');\n}\n\nfunction isLocal(url: URL): boolean {\n\treturn (\n\t\turl.hostname === 'localhost' ||\n\t\turl.hostname === '127.0.0.1' ||\n\t\turl.hostname.endsWith('.localhost')\n\t);\n}\n\n/** A request reached an in-process server without a test to belong to. */\nexport class UnknownTestContext extends Error {\n\tconstructor(\n\t\treadonly url: string,\n\t\treadonly id: string | undefined,\n\t) {\n\t\tsuper(\n\t\t\tid\n\t\t\t\t? `${url} was requested for test ${id}, which is not running. A ` +\n\t\t\t\t\t\t`request that outlives its test — an un-awaited call — lands here.`\n\t\t\t\t: `${url} was requested outside any test. Make the request inside a ` +\n\t\t\t\t\t\t`featureTest, so it reaches that test's transactions.`,\n\t\t);\n\t\tthis.name = 'UnknownTestContext';\n\t}\n}\n\n/** `mailbox` was used in an app that declares no Email construct. */\nexport class NoInbox extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t`mailbox() reads the inbox of the app's Email construct, and none was ` +\n\t\t\t\t`found in featureTest's modules — or \\`gkm test\\` did not inject its ` +\n\t\t\t\t`<ID>_INBOX_URL.`,\n\t\t);\n\t\tthis.name = 'NoInbox';\n\t}\n}\n"],"mappings":"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"}
@@ -0,0 +1,123 @@
1
+ import { n as Construct, r as Consumable, t as Authenticator } from "./construct-interface-DIaTNKax.cjs";
2
+ import { EnvironmentParser } from "@geekmidas/envkit";
3
+ import { Service, ServiceRegisterOptions } from "@geekmidas/services";
4
+ import { ConstructName, Declaration } from "@geekmidas/manifest";
5
+ import { betterAuth } from "better-auth";
6
+ import { Hono } from "hono";
7
+ //#region src/auth.d.ts
8
+ /** The server better-auth hands back. */
9
+ type AuthServer = ReturnType<typeof betterAuth>;
10
+ /** A signed-in session as better-auth hands it back: `{ user, session }`. */
11
+ type AuthSession = AuthServer['$Infer']['Session'];
12
+ /** Everything better-auth takes, minus what the construct owns. */
13
+ type BetterAuthOptions = Omit<Parameters<typeof betterAuth>[0], 'database' | 'secret' | 'basePath' | 'baseURL'>;
14
+ interface BetterAuthConfig<TDatabase extends Consumable> {
15
+ /**
16
+ * The schema tenant its tables live in.
17
+ *
18
+ * A construct rather than a URL: the tenant already declares its schema and
19
+ * its role, and taking the declaration is what puts the edge in the graph
20
+ * instead of a connection string in two places.
21
+ */
22
+ database: TDatabase;
23
+ /**
24
+ * The app that serves this auth server, relative to the workspace root —
25
+ * `'apps/auth'`.
26
+ *
27
+ * Required, for the reason `RestApi.path` is. Not to be confused with
28
+ * `basePath`, which is where its routes are mounted in a URL.
29
+ */
30
+ path: string;
31
+ /**
32
+ * Where the auth routes are mounted, e.g. `/api/auth`.
33
+ *
34
+ * Structural — it is part of the URL every client calls, so it cannot differ
35
+ * between stages the way the host can.
36
+ */
37
+ basePath?: string;
38
+ /**
39
+ * The rest of better-auth's options: providers, plugins, email settings.
40
+ *
41
+ * A function when they need another construct — a magic-link plugin has to
42
+ * *send* the link, and the thing that sends mail is a construct whose client
43
+ * has to be registered. It is handed the same `ServiceRegisterOptions` this
44
+ * construct was, so the env parser flows through the graph rather than being
45
+ * imported out of application config.
46
+ */
47
+ options?: BetterAuthOptions | ((options: ServiceRegisterOptions) => BetterAuthOptions | Promise<BetterAuthOptions>);
48
+ }
49
+ declare class BetterAuth<TName extends string = string, TDatabase extends Consumable = Consumable> implements Construct<TName, AuthServer>, Authenticator<AuthSession> {
50
+ private readonly config;
51
+ readonly id: TName;
52
+ readonly service: Service<Uncapitalize<TName>, AuthServer>;
53
+ readonly basePath: string;
54
+ /**
55
+ * Declared once and read by both `declare()` and `connect()`, so the keys the
56
+ * target publishes and the keys the server reads cannot drift.
57
+ */
58
+ private readonly keys;
59
+ constructor(id: ConstructName<TName>, config: BetterAuthConfig<TDatabase>);
60
+ /**
61
+ * A signing secret and a surface.
62
+ *
63
+ * The database is *not* declared here — the tenant that was passed in
64
+ * declares it, and declaring it twice is the duplication the whole model
65
+ * removes. What is declared is what this construct owns: the key it signs
66
+ * with, and the routes it answers on.
67
+ *
68
+ * One endpoint, wildcarded. Better Auth routes internally and the set of
69
+ * paths depends on which capabilities are enabled, so enumerating them here
70
+ * would be a copy of its router that goes stale on its next release. The
71
+ * surface's job is to send everything under `basePath` to one handler, which
72
+ * is exactly what the declaration says.
73
+ */
74
+ declare(): Declaration[];
75
+ /**
76
+ * Whose request this is: the session for these headers, or `null`.
77
+ *
78
+ * Asked of the auth server over HTTP, at the URL the `.auth()` edge injects —
79
+ * so it is the same call whether the server runs in this process, beside
80
+ * it, or behind MSW in a test. Only the session headers are forwarded; the
81
+ * surface decides which.
82
+ */
83
+ verify(headers: Headers, envParser: EnvironmentParser<{}>): Promise<AuthSession | null>;
84
+ /**
85
+ * Better Auth's own migrations, for the app's migrate step.
86
+ *
87
+ * It brings its own schema — users, sessions, accounts, verifications — so
88
+ * the app never writes those tables and never gets to drift from them.
89
+ */
90
+ migrations(options: ServiceRegisterOptions): Promise<() => Promise<void>>;
91
+ /**
92
+ * The server this surface runs as, built from its own declaration.
93
+ *
94
+ * A surface is a deploy unit, so something has to serve it, and the thing
95
+ * that knows how is the construct — not a generator writing a mount into
96
+ * somebody else's entry point. That is how the auth server came to be
97
+ * mounted by a hook in the application it was supposed to be separate from,
98
+ * and then to be served by nothing at all when the hook was deleted.
99
+ *
100
+ * The routes come from {@link declare}: one wildcard under `basePath`, read
101
+ * off the same declaration the manifest carries, so the surface the graph
102
+ * describes and the surface that answers requests cannot disagree.
103
+ *
104
+ * ```ts
105
+ * // .gkm/server/app.ts — the whole generated entry
106
+ * import { auth } from '@acme/constructs/auth.js';
107
+ * export const { app } = await auth.server({ envParser });
108
+ * ```
109
+ */
110
+ server(options: ServiceRegisterOptions): Promise<{
111
+ app: Hono;
112
+ }>;
113
+ private connect;
114
+ }
115
+ /** The auth server answered a session check with something other than 200. */
116
+ declare class SessionCheckFailed extends Error {
117
+ readonly authenticator: string;
118
+ readonly status: number;
119
+ constructor(authenticator: string, status: number);
120
+ }
121
+ //#endregion
122
+ export { BetterAuthOptions as a, BetterAuthConfig as i, AuthSession as n, SessionCheckFailed as o, BetterAuth as r, AuthServer as t };
123
+ //# sourceMappingURL=auth-BlO8MUkR.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-BlO8MUkR.d.cts","names":[],"sources":["../src/auth.ts"],"mappings":";;;;;;;;KAgDY,aAAa,kBAAkB;;KAG/B,cAAc;;KAGd,oBAAoB,KAC/B,kBAAkB;UAIF,iBAAiB,kBAAkB;;;;;;;;EAQnD,UAAU;;;;;;;;EAQV;;;;;;;EAOA;;;;;;;;;;EAUA,UACG,sBAEA,SAAS,2BACJ,oBAAoB,QAAQ;;cAKxB,WACZ,+BACA,kBAAkB,aAAa,uBACnB,UAAU,OAAO,aAAa,cAAc;mBAoBtC;WAlBT,IAAI;WACJ,SAAS,QAAQ,aAAa,QAAQ;WACtC;;;;;mBAMQ;EAQjB,YACC,IAAI,cAAc,QACD,QAAQ,iBAAiB;;;;;;;;;;;;;;;EAuC3C,WAAW;;;;;;;;;EA0CL,OACL,SAAS,SACT,WAAW,wBACT,QAAQ;;;;;;;EAyBL,WACL,SAAS,yBACP,cAAc;;;;;;;;;;;;;;;;;;;;EAkCX,OAAO,SAAS,yBAAyB;IAAU,KAAK;;UA+ChD;;;cAgGF,2BAA2B;WAE7B;WACA;EAFV,YACU,uBACA"}
@@ -0,0 +1,123 @@
1
+ import { n as Construct, r as Consumable, t as Authenticator } from "./construct-interface-Clz7_BwB.mjs";
2
+ import { ConstructName, Declaration } from "@geekmidas/manifest";
3
+ import { betterAuth } from "better-auth";
4
+ import { EnvironmentParser } from "@geekmidas/envkit";
5
+ import { Service, ServiceRegisterOptions } from "@geekmidas/services";
6
+ import { Hono } from "hono";
7
+ //#region src/auth.d.ts
8
+ /** The server better-auth hands back. */
9
+ type AuthServer = ReturnType<typeof betterAuth>;
10
+ /** A signed-in session as better-auth hands it back: `{ user, session }`. */
11
+ type AuthSession = AuthServer['$Infer']['Session'];
12
+ /** Everything better-auth takes, minus what the construct owns. */
13
+ type BetterAuthOptions = Omit<Parameters<typeof betterAuth>[0], 'database' | 'secret' | 'basePath' | 'baseURL'>;
14
+ interface BetterAuthConfig<TDatabase extends Consumable> {
15
+ /**
16
+ * The schema tenant its tables live in.
17
+ *
18
+ * A construct rather than a URL: the tenant already declares its schema and
19
+ * its role, and taking the declaration is what puts the edge in the graph
20
+ * instead of a connection string in two places.
21
+ */
22
+ database: TDatabase;
23
+ /**
24
+ * The app that serves this auth server, relative to the workspace root —
25
+ * `'apps/auth'`.
26
+ *
27
+ * Required, for the reason `RestApi.path` is. Not to be confused with
28
+ * `basePath`, which is where its routes are mounted in a URL.
29
+ */
30
+ path: string;
31
+ /**
32
+ * Where the auth routes are mounted, e.g. `/api/auth`.
33
+ *
34
+ * Structural — it is part of the URL every client calls, so it cannot differ
35
+ * between stages the way the host can.
36
+ */
37
+ basePath?: string;
38
+ /**
39
+ * The rest of better-auth's options: providers, plugins, email settings.
40
+ *
41
+ * A function when they need another construct — a magic-link plugin has to
42
+ * *send* the link, and the thing that sends mail is a construct whose client
43
+ * has to be registered. It is handed the same `ServiceRegisterOptions` this
44
+ * construct was, so the env parser flows through the graph rather than being
45
+ * imported out of application config.
46
+ */
47
+ options?: BetterAuthOptions | ((options: ServiceRegisterOptions) => BetterAuthOptions | Promise<BetterAuthOptions>);
48
+ }
49
+ declare class BetterAuth<TName extends string = string, TDatabase extends Consumable = Consumable> implements Construct<TName, AuthServer>, Authenticator<AuthSession> {
50
+ private readonly config;
51
+ readonly id: TName;
52
+ readonly service: Service<Uncapitalize<TName>, AuthServer>;
53
+ readonly basePath: string;
54
+ /**
55
+ * Declared once and read by both `declare()` and `connect()`, so the keys the
56
+ * target publishes and the keys the server reads cannot drift.
57
+ */
58
+ private readonly keys;
59
+ constructor(id: ConstructName<TName>, config: BetterAuthConfig<TDatabase>);
60
+ /**
61
+ * A signing secret and a surface.
62
+ *
63
+ * The database is *not* declared here — the tenant that was passed in
64
+ * declares it, and declaring it twice is the duplication the whole model
65
+ * removes. What is declared is what this construct owns: the key it signs
66
+ * with, and the routes it answers on.
67
+ *
68
+ * One endpoint, wildcarded. Better Auth routes internally and the set of
69
+ * paths depends on which capabilities are enabled, so enumerating them here
70
+ * would be a copy of its router that goes stale on its next release. The
71
+ * surface's job is to send everything under `basePath` to one handler, which
72
+ * is exactly what the declaration says.
73
+ */
74
+ declare(): Declaration[];
75
+ /**
76
+ * Whose request this is: the session for these headers, or `null`.
77
+ *
78
+ * Asked of the auth server over HTTP, at the URL the `.auth()` edge injects —
79
+ * so it is the same call whether the server runs in this process, beside
80
+ * it, or behind MSW in a test. Only the session headers are forwarded; the
81
+ * surface decides which.
82
+ */
83
+ verify(headers: Headers, envParser: EnvironmentParser<{}>): Promise<AuthSession | null>;
84
+ /**
85
+ * Better Auth's own migrations, for the app's migrate step.
86
+ *
87
+ * It brings its own schema — users, sessions, accounts, verifications — so
88
+ * the app never writes those tables and never gets to drift from them.
89
+ */
90
+ migrations(options: ServiceRegisterOptions): Promise<() => Promise<void>>;
91
+ /**
92
+ * The server this surface runs as, built from its own declaration.
93
+ *
94
+ * A surface is a deploy unit, so something has to serve it, and the thing
95
+ * that knows how is the construct — not a generator writing a mount into
96
+ * somebody else's entry point. That is how the auth server came to be
97
+ * mounted by a hook in the application it was supposed to be separate from,
98
+ * and then to be served by nothing at all when the hook was deleted.
99
+ *
100
+ * The routes come from {@link declare}: one wildcard under `basePath`, read
101
+ * off the same declaration the manifest carries, so the surface the graph
102
+ * describes and the surface that answers requests cannot disagree.
103
+ *
104
+ * ```ts
105
+ * // .gkm/server/app.ts — the whole generated entry
106
+ * import { auth } from '@acme/constructs/auth.js';
107
+ * export const { app } = await auth.server({ envParser });
108
+ * ```
109
+ */
110
+ server(options: ServiceRegisterOptions): Promise<{
111
+ app: Hono;
112
+ }>;
113
+ private connect;
114
+ }
115
+ /** The auth server answered a session check with something other than 200. */
116
+ declare class SessionCheckFailed extends Error {
117
+ readonly authenticator: string;
118
+ readonly status: number;
119
+ constructor(authenticator: string, status: number);
120
+ }
121
+ //#endregion
122
+ export { BetterAuthOptions as a, BetterAuthConfig as i, AuthSession as n, SessionCheckFailed as o, BetterAuth as r, AuthServer as t };
123
+ //# sourceMappingURL=auth-CHWIdcQO.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-CHWIdcQO.d.mts","names":[],"sources":["../src/auth.ts"],"mappings":";;;;;;;;KAgDY,aAAa,kBAAkB;;KAG/B,cAAc;;KAGd,oBAAoB,KAC/B,kBAAkB;UAIF,iBAAiB,kBAAkB;;;;;;;;EAQnD,UAAU;;;;;;;;EAQV;;;;;;;EAOA;;;;;;;;;;EAUA,UACG,sBAEA,SAAS,2BACJ,oBAAoB,QAAQ;;cAKxB,WACZ,+BACA,kBAAkB,aAAa,uBACnB,UAAU,OAAO,aAAa,cAAc;mBAoBtC;WAlBT,IAAI;WACJ,SAAS,QAAQ,aAAa,QAAQ;WACtC;;;;;mBAMQ;EAQjB,YACC,IAAI,cAAc,QACD,QAAQ,iBAAiB;;;;;;;;;;;;;;;EAuC3C,WAAW;;;;;;;;;EA0CL,OACL,SAAS,SACT,WAAW,wBACT,QAAQ;;;;;;;EAyBL,WACL,SAAS,yBACP,cAAc;;;;;;;;;;;;;;;;;;;;EAkCX,OAAO,SAAS,yBAAyB;IAAU,KAAK;;UA+ChD;;;cAgGF,2BAA2B;WAE7B;WACA;EAFV,YACU,uBACA"}
package/dist/auth.cjs CHANGED
@@ -1,11 +1,11 @@
1
- const require_chunk = require('./chunk-DWy1uDak.cjs');
2
- const require_construct_interface = require('./construct-interface-BfjcOD_E.cjs');
3
- const __geekmidas_manifest = require_chunk.__toESM(require("@geekmidas/manifest"));
4
- const better_auth = require_chunk.__toESM(require("better-auth"));
5
-
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_construct_interface = require("./construct-interface.cjs");
3
+ let _geekmidas_manifest = require("@geekmidas/manifest");
4
+ let better_auth = require("better-auth");
6
5
  //#region src/auth.ts
7
6
  const DEFAULT_BASE_PATH = "/api/auth";
8
7
  var BetterAuth = class {
8
+ config;
9
9
  id;
10
10
  service;
11
11
  basePath;
@@ -16,19 +16,19 @@ var BetterAuth = class {
16
16
  keys;
17
17
  constructor(id, config) {
18
18
  this.config = config;
19
- const canonical = (0, __geekmidas_manifest.canonicalId)(id);
19
+ const canonical = (0, _geekmidas_manifest.canonicalId)(id);
20
20
  this.id = canonical;
21
21
  this.basePath = config.basePath ?? DEFAULT_BASE_PATH;
22
22
  const secretId = `${canonical}Secret`;
23
23
  this.keys = {
24
24
  secretId,
25
- secret: (0, __geekmidas_manifest.environmentCase)(secretId),
26
- url: (0, __geekmidas_manifest.provideKey)(canonical, "url"),
27
- trustedOrigins: (0, __geekmidas_manifest.provideKey)(canonical, "trustedOrigins"),
28
- cookieDomain: (0, __geekmidas_manifest.provideKey)(canonical, "cookieDomain")
25
+ secret: (0, _geekmidas_manifest.environmentCase)(secretId),
26
+ url: (0, _geekmidas_manifest.provideKey)(canonical, "url"),
27
+ trustedOrigins: (0, _geekmidas_manifest.provideKey)(canonical, "trustedOrigins"),
28
+ cookieDomain: (0, _geekmidas_manifest.provideKey)(canonical, "cookieDomain")
29
29
  };
30
30
  this.service = {
31
- serviceName: (0, __geekmidas_manifest.serviceKey)(canonical),
31
+ serviceName: (0, _geekmidas_manifest.serviceKey)(canonical),
32
32
  register: (options) => this.connect(options)
33
33
  };
34
34
  }
@@ -54,7 +54,7 @@ var BetterAuth = class {
54
54
  }, {
55
55
  kind: "rest-api",
56
56
  id: this.id,
57
- ...this.config.app ? { app: this.config.app } : {},
57
+ path: this.config.path,
58
58
  provides: [
59
59
  this.keys.url,
60
60
  this.keys.trustedOrigins,
@@ -71,6 +71,20 @@ var BetterAuth = class {
71
71
  }];
72
72
  }
73
73
  /**
74
+ * Whose request this is: the session for these headers, or `null`.
75
+ *
76
+ * Asked of the auth server over HTTP, at the URL the `.auth()` edge injects —
77
+ * so it is the same call whether the server runs in this process, beside
78
+ * it, or behind MSW in a test. Only the session headers are forwarded; the
79
+ * surface decides which.
80
+ */
81
+ async verify(headers, envParser) {
82
+ const { url } = envParser.create((get) => ({ url: get(this.keys.url).string() })).parse();
83
+ const response = await fetch(`${url}${this.basePath}/get-session`, { headers });
84
+ if (!response.ok) throw new SessionCheckFailed(this.id, response.status);
85
+ return await response.json();
86
+ }
87
+ /**
74
88
  * Better Auth's own migrations, for the app's migrate step.
75
89
  *
76
90
  * It brings its own schema — users, sessions, accounts, verifications — so
@@ -78,7 +92,7 @@ var BetterAuth = class {
78
92
  */
79
93
  async migrations(options) {
80
94
  const auth = await this.connect(options, { owner: true });
81
- const { getMigrations } = await import("better-auth/db");
95
+ const { getMigrations } = await import("better-auth/db/migration");
82
96
  const { runMigrations } = await getMigrations(auth.options);
83
97
  return runMigrations;
84
98
  }
@@ -134,8 +148,7 @@ var BetterAuth = class {
134
148
  cookieDomain: get(this.keys.cookieDomain).string().optional()
135
149
  })).parse();
136
150
  const tenant = this.config.database;
137
- const source = as.owner && tenant.owner ? tenant.owner : tenant.service;
138
- const db = await source.register(options);
151
+ const db = await (as.owner && tenant.owner ? tenant.owner : tenant.service).register(options);
139
152
  const configure = this.config.options;
140
153
  const configured = typeof configure === "function" ? await configure(options) : configure ?? {};
141
154
  return (0, better_auth.betterAuth)({
@@ -159,7 +172,19 @@ var BetterAuth = class {
159
172
  });
160
173
  }
161
174
  };
162
-
175
+ /** The auth server answered a session check with something other than 200. */
176
+ var SessionCheckFailed = class extends Error {
177
+ authenticator;
178
+ status;
179
+ constructor(authenticator, status) {
180
+ super(`'${authenticator}' answered a session check with ${status}. A request with no session gets 200 and null; this is the server failing — check that it is running and reachable at its URL.`);
181
+ this.authenticator = authenticator;
182
+ this.status = status;
183
+ this.name = "SessionCheckFailed";
184
+ }
185
+ };
163
186
  //#endregion
164
187
  exports.BetterAuth = BetterAuth;
188
+ exports.SessionCheckFailed = SessionCheckFailed;
189
+
165
190
  //# sourceMappingURL=auth.cjs.map