@geekmidas/constructs 10.0.0-alpha.12 → 10.0.0-alpha.120

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 (454) hide show
  1. package/README.md +222 -158
  2. package/dist/Construct-B7AXn0xH.d.mts +230 -0
  3. package/dist/Construct-B7AXn0xH.d.mts.map +1 -0
  4. package/dist/Construct-CFW0zjdD.d.cts +230 -0
  5. package/dist/Construct-CFW0zjdD.d.cts.map +1 -0
  6. package/dist/{Construct-BD5neF7w.mjs → Construct-CKUA-CTA.mjs} +35 -35
  7. package/dist/Construct-CKUA-CTA.mjs.map +1 -0
  8. package/dist/{Construct-CyS3RpTL.cjs → Construct-DEOVauyA.cjs} +55 -49
  9. package/dist/Construct-DEOVauyA.cjs.map +1 -0
  10. package/dist/{Cron-DmQ8vQ4b.d.cts → Cron-bdGRSROY.d.cts} +15 -9
  11. package/dist/Cron-bdGRSROY.d.cts.map +1 -0
  12. package/dist/{Cron-dh4rUgib.d.mts → Cron-i4Uetezh.d.mts} +15 -9
  13. package/dist/Cron-i4Uetezh.d.mts.map +1 -0
  14. package/dist/CronBuilder-9H4BnD6W.d.mts +47 -0
  15. package/dist/CronBuilder-9H4BnD6W.d.mts.map +1 -0
  16. package/dist/{CronBuilder-Da0C0edR.cjs → CronBuilder-BXTG10u5.cjs} +47 -31
  17. package/dist/CronBuilder-BXTG10u5.cjs.map +1 -0
  18. package/dist/CronBuilder-B_24LZpn.d.cts +47 -0
  19. package/dist/CronBuilder-B_24LZpn.d.cts.map +1 -0
  20. package/dist/{CronBuilder-CYKT5RiO.mjs → CronBuilder-D3sF9tS_.mjs} +37 -21
  21. package/dist/CronBuilder-D3sF9tS_.mjs.map +1 -0
  22. package/dist/{Endpoint-BjWKinFA.d.mts → Endpoint-CeFkSwh5.d.mts} +129 -99
  23. package/dist/Endpoint-CeFkSwh5.d.mts.map +1 -0
  24. package/dist/{Endpoint-dav89W6-.d.cts → Endpoint-DgrpG15P.d.cts} +127 -97
  25. package/dist/Endpoint-DgrpG15P.d.cts.map +1 -0
  26. package/dist/{Endpoint-DCQPMdUX.mjs → Endpoint-gPXhHKhJ.mjs} +208 -100
  27. package/dist/Endpoint-gPXhHKhJ.mjs.map +1 -0
  28. package/dist/{Endpoint-DcWXE-SZ.cjs → Endpoint-yhR2T2Wt.cjs} +248 -122
  29. package/dist/Endpoint-yhR2T2Wt.cjs.map +1 -0
  30. package/dist/{EndpointFactory-CdWB7wPw.cjs → EndpointFactory-CSrAyazj.cjs} +361 -112
  31. package/dist/EndpointFactory-CSrAyazj.cjs.map +1 -0
  32. package/dist/{index-lAO9egTb.d.mts → EndpointFactory-Cc863XJS.d.cts} +190 -136
  33. package/dist/EndpointFactory-Cc863XJS.d.cts.map +1 -0
  34. package/dist/{index-Bd5FqCoe.d.cts → EndpointFactory-Dbw9ugkX.d.mts} +189 -135
  35. package/dist/EndpointFactory-Dbw9ugkX.d.mts.map +1 -0
  36. package/dist/{EndpointFactory-CjV8F_lJ.mjs → EndpointFactory-c2ZugGig.mjs} +283 -76
  37. package/dist/EndpointFactory-c2ZugGig.mjs.map +1 -0
  38. package/dist/Function-B0uZhZnU.d.cts +76 -0
  39. package/dist/Function-B0uZhZnU.d.cts.map +1 -0
  40. package/dist/Function-B_9R7Zy1.d.mts +76 -0
  41. package/dist/Function-B_9R7Zy1.d.mts.map +1 -0
  42. package/dist/{HonoEndpointAdaptor-DtL9j19R.cjs → HonoEndpointAdaptor-CN1jjUBJ.cjs} +115 -71
  43. package/dist/HonoEndpointAdaptor-CN1jjUBJ.cjs.map +1 -0
  44. package/dist/{HonoEndpointAdaptor-P1ahhZHu.mjs → HonoEndpointAdaptor-euH1oSdY.mjs} +105 -64
  45. package/dist/HonoEndpointAdaptor-euH1oSdY.mjs.map +1 -0
  46. package/dist/{Queue-D7ZuwBhy.d.cts → Queue-B-dDXyjF.d.mts} +48 -48
  47. package/dist/Queue-B-dDXyjF.d.mts.map +1 -0
  48. package/dist/Queue-BhuofM3k.cjs +109 -0
  49. package/dist/Queue-BhuofM3k.cjs.map +1 -0
  50. package/dist/{Queue-gVGfi2Yk.d.mts → Queue-JbIjVtTf.d.cts} +49 -49
  51. package/dist/Queue-JbIjVtTf.d.cts.map +1 -0
  52. package/dist/Queue-cmhqEBOB.mjs +104 -0
  53. package/dist/Queue-cmhqEBOB.mjs.map +1 -0
  54. package/dist/QueueBuilder-48W-q8In.d.mts +75 -0
  55. package/dist/QueueBuilder-48W-q8In.d.mts.map +1 -0
  56. package/dist/QueueBuilder-DXu1qxT1.mjs +118 -0
  57. package/dist/QueueBuilder-DXu1qxT1.mjs.map +1 -0
  58. package/dist/QueueBuilder-DdU2klPu.cjs +135 -0
  59. package/dist/QueueBuilder-DdU2klPu.cjs.map +1 -0
  60. package/dist/QueueBuilder-MfpkJ3R9.d.cts +75 -0
  61. package/dist/QueueBuilder-MfpkJ3R9.d.cts.map +1 -0
  62. package/dist/{Subscriber-2mEi0ImH.d.cts → Subscriber-CEt7kHkc.d.cts} +29 -20
  63. package/dist/Subscriber-CEt7kHkc.d.cts.map +1 -0
  64. package/dist/{Subscriber-xTJ9ayPN.d.mts → Subscriber-dsxc5LKa.d.mts} +29 -20
  65. package/dist/Subscriber-dsxc5LKa.d.mts.map +1 -0
  66. package/dist/SubscriberBuilder-BRa_nrmH.d.cts +67 -0
  67. package/dist/SubscriberBuilder-BRa_nrmH.d.cts.map +1 -0
  68. package/dist/SubscriberBuilder-D9bkrYzH.d.mts +67 -0
  69. package/dist/SubscriberBuilder-D9bkrYzH.d.mts.map +1 -0
  70. package/dist/{SubscriberBuilder-BjkbeT9l.mjs → SubscriberBuilder-DRXfxWn_.mjs} +50 -24
  71. package/dist/SubscriberBuilder-DRXfxWn_.mjs.map +1 -0
  72. package/dist/{SubscriberBuilder-4OPehf2R.cjs → SubscriberBuilder-M2EAgQhC.cjs} +61 -36
  73. package/dist/SubscriberBuilder-M2EAgQhC.cjs.map +1 -0
  74. package/dist/Topic-D2R0r24C.mjs +99 -0
  75. package/dist/Topic-D2R0r24C.mjs.map +1 -0
  76. package/dist/Topic-DwS3QO0m.cjs +104 -0
  77. package/dist/Topic-DwS3QO0m.cjs.map +1 -0
  78. package/dist/adaptors/aws.cjs +200 -246
  79. package/dist/adaptors/aws.cjs.map +1 -1
  80. package/dist/adaptors/aws.d.cts +58 -62
  81. package/dist/adaptors/aws.d.cts.map +1 -1
  82. package/dist/adaptors/aws.d.mts +59 -63
  83. package/dist/adaptors/aws.d.mts.map +1 -1
  84. package/dist/adaptors/aws.mjs +171 -219
  85. package/dist/adaptors/aws.mjs.map +1 -1
  86. package/dist/adaptors/hono.cjs +3 -10
  87. package/dist/adaptors/hono.d.cts +7 -16
  88. package/dist/adaptors/hono.d.cts.map +1 -1
  89. package/dist/adaptors/hono.d.mts +9 -18
  90. package/dist/adaptors/hono.d.mts.map +1 -1
  91. package/dist/adaptors/hono.mjs +2 -10
  92. package/dist/adaptors/testing.cjs +765 -99
  93. package/dist/adaptors/testing.cjs.map +1 -1
  94. package/dist/adaptors/testing.d.cts +351 -54
  95. package/dist/adaptors/testing.d.cts.map +1 -1
  96. package/dist/adaptors/testing.d.mts +351 -54
  97. package/dist/adaptors/testing.d.mts.map +1 -1
  98. package/dist/adaptors/testing.mjs +726 -78
  99. package/dist/adaptors/testing.mjs.map +1 -1
  100. package/dist/auth-BMCJN30Y.d.cts +268 -0
  101. package/dist/auth-BMCJN30Y.d.cts.map +1 -0
  102. package/dist/auth-D_0788uW.d.mts +268 -0
  103. package/dist/auth-D_0788uW.d.mts.map +1 -0
  104. package/dist/auth.cjs +301 -33
  105. package/dist/auth.cjs.map +1 -1
  106. package/dist/auth.d.cts +2 -108
  107. package/dist/auth.d.mts +2 -108
  108. package/dist/auth.mjs +289 -27
  109. package/dist/auth.mjs.map +1 -1
  110. package/dist/{cache-BrRz3fEB.d.mts → cache-5QZzh5Er.d.mts} +3 -6
  111. package/dist/cache-5QZzh5Er.d.mts.map +1 -0
  112. package/dist/{cache-BtZY_tHe.d.cts → cache-DnGDGly3.d.cts} +3 -6
  113. package/dist/cache-DnGDGly3.d.cts.map +1 -0
  114. package/dist/cache.cjs +68 -2
  115. package/dist/cache.cjs.map +1 -0
  116. package/dist/cache.d.cts +1 -2
  117. package/dist/cache.d.mts +1 -2
  118. package/dist/cache.mjs +67 -2
  119. package/dist/cache.mjs.map +1 -0
  120. package/dist/cipher-C1lEFX63.mjs +225 -0
  121. package/dist/cipher-C1lEFX63.mjs.map +1 -0
  122. package/dist/cipher-CVxfwLjk.cjs +332 -0
  123. package/dist/cipher-CVxfwLjk.cjs.map +1 -0
  124. package/dist/cipher-DSDxPRIp.d.cts +106 -0
  125. package/dist/cipher-DSDxPRIp.d.cts.map +1 -0
  126. package/dist/cipher-DSDxPRIp.d.mts +106 -0
  127. package/dist/cipher-DSDxPRIp.d.mts.map +1 -0
  128. package/dist/{clone-DblgEZZa.mjs → clone-EKNovYHu.mjs} +3 -3
  129. package/dist/clone-EKNovYHu.mjs.map +1 -0
  130. package/dist/{clone-Dy0cR2SZ.cjs → clone-UGJpvQnL.cjs} +7 -8
  131. package/dist/clone-UGJpvQnL.cjs.map +1 -0
  132. package/dist/{construct-interface-D9d4Gm4p.d.cts → construct-interface-B1AfqRl4.d.cts} +42 -11
  133. package/dist/construct-interface-B1AfqRl4.d.cts.map +1 -0
  134. package/dist/{construct-interface-g5GM5pwR.d.mts → construct-interface-ZvYQbivO.d.mts} +42 -11
  135. package/dist/construct-interface-ZvYQbivO.d.mts.map +1 -0
  136. package/dist/construct-interface.cjs +113 -7
  137. package/dist/construct-interface.cjs.map +1 -0
  138. package/dist/construct-interface.d.cts +2 -2
  139. package/dist/construct-interface.d.mts +2 -2
  140. package/dist/construct-interface.mjs +106 -2
  141. package/dist/construct-interface.mjs.map +1 -0
  142. package/dist/credential-DYymbp_7.cjs +257 -0
  143. package/dist/credential-DYymbp_7.cjs.map +1 -0
  144. package/dist/credential-DfSAOOdY.mjs +222 -0
  145. package/dist/credential-DfSAOOdY.mjs.map +1 -0
  146. package/dist/credential.cjs +8 -96
  147. package/dist/credential.d.cts +2 -61
  148. package/dist/credential.d.mts +2 -61
  149. package/dist/credential.mjs +2 -94
  150. package/dist/crons/index.cjs +129 -7
  151. package/dist/crons/index.cjs.map +1 -1
  152. package/dist/crons/index.d.cts +45 -11
  153. package/dist/crons/index.d.cts.map +1 -1
  154. package/dist/crons/index.d.mts +45 -11
  155. package/dist/crons/index.d.mts.map +1 -1
  156. package/dist/crons/index.mjs +127 -8
  157. package/dist/crons/index.mjs.map +1 -1
  158. package/dist/database/kysely.cjs +4 -181
  159. package/dist/database/kysely.d.cts +2 -152
  160. package/dist/database/kysely.d.mts +2 -152
  161. package/dist/database/kysely.mjs +2 -180
  162. package/dist/email.cjs +25 -12
  163. package/dist/email.cjs.map +1 -1
  164. package/dist/email.d.cts +3 -7
  165. package/dist/email.d.cts.map +1 -1
  166. package/dist/email.d.mts +4 -8
  167. package/dist/email.d.mts.map +1 -1
  168. package/dist/email.mjs +16 -3
  169. package/dist/email.mjs.map +1 -1
  170. package/dist/encryption/kms.cjs +95 -0
  171. package/dist/encryption/kms.cjs.map +1 -0
  172. package/dist/encryption/kms.d.cts +7 -0
  173. package/dist/encryption/kms.d.cts.map +1 -0
  174. package/dist/encryption/kms.d.mts +7 -0
  175. package/dist/encryption/kms.d.mts.map +1 -0
  176. package/dist/encryption/kms.mjs +93 -0
  177. package/dist/encryption/kms.mjs.map +1 -0
  178. package/dist/encryption.cjs +116 -0
  179. package/dist/encryption.cjs.map +1 -0
  180. package/dist/encryption.d.cts +37 -0
  181. package/dist/encryption.d.cts.map +1 -0
  182. package/dist/encryption.d.mts +37 -0
  183. package/dist/encryption.d.mts.map +1 -0
  184. package/dist/encryption.mjs +101 -0
  185. package/dist/encryption.mjs.map +1 -0
  186. package/dist/endpoints/index.cjs +27 -16
  187. package/dist/endpoints/index.d.cts +105 -7
  188. package/dist/endpoints/index.d.cts.map +1 -0
  189. package/dist/endpoints/index.d.mts +105 -7
  190. package/dist/endpoints/index.d.mts.map +1 -0
  191. package/dist/endpoints/index.mjs +6 -10
  192. package/dist/expire-C586zznE.d.cts +38 -0
  193. package/dist/expire-C586zznE.d.cts.map +1 -0
  194. package/dist/expire-C586zznE.d.mts +38 -0
  195. package/dist/expire-C586zznE.d.mts.map +1 -0
  196. package/dist/expire-CieftS01.cjs +67 -0
  197. package/dist/expire-CieftS01.cjs.map +1 -0
  198. package/dist/expire-CpzUAfr9.mjs +50 -0
  199. package/dist/expire-CpzUAfr9.mjs.map +1 -0
  200. package/dist/external-api.cjs +106 -0
  201. package/dist/external-api.cjs.map +1 -0
  202. package/dist/external-api.d.cts +2 -0
  203. package/dist/external-api.d.mts +2 -0
  204. package/dist/external-api.mjs +103 -0
  205. package/dist/external-api.mjs.map +1 -0
  206. package/dist/fake-B7rvi4Qp.d.cts +201 -0
  207. package/dist/fake-B7rvi4Qp.d.cts.map +1 -0
  208. package/dist/fake-JXbkdao8.d.mts +201 -0
  209. package/dist/fake-JXbkdao8.d.mts.map +1 -0
  210. package/dist/file-server.cjs +66 -11
  211. package/dist/file-server.cjs.map +1 -1
  212. package/dist/file-server.d.cts +25 -10
  213. package/dist/file-server.d.cts.map +1 -1
  214. package/dist/file-server.d.mts +25 -10
  215. package/dist/file-server.d.mts.map +1 -1
  216. package/dist/file-server.mjs +58 -4
  217. package/dist/file-server.mjs.map +1 -1
  218. package/dist/functions/index.cjs +3 -6
  219. package/dist/functions/index.d.cts +3 -4
  220. package/dist/functions/index.d.mts +3 -4
  221. package/dist/functions/index.mjs +2 -6
  222. package/dist/{functions-14lpEFL4.mjs → functions-CTI-hDem.mjs} +376 -238
  223. package/dist/{functions-14lpEFL4.mjs.map → functions-CTI-hDem.mjs.map} +1 -1
  224. package/dist/{functions-i_la-N5r.cjs → functions-D7O5SGVc.cjs} +414 -264
  225. package/dist/functions-D7O5SGVc.cjs.map +1 -0
  226. package/dist/index-Cd2w-DRL.d.cts +115 -0
  227. package/dist/index-Cd2w-DRL.d.cts.map +1 -0
  228. package/dist/index-DdRjYhiJ.d.cts +119 -0
  229. package/dist/index-DdRjYhiJ.d.cts.map +1 -0
  230. package/dist/index-DdRjYhiJ.d.mts +119 -0
  231. package/dist/index-DdRjYhiJ.d.mts.map +1 -0
  232. package/dist/index-aBIcAiH5.d.mts +115 -0
  233. package/dist/index-aBIcAiH5.d.mts.map +1 -0
  234. package/dist/index.cjs +6 -3
  235. package/dist/index.d.cts +27 -4
  236. package/dist/index.d.cts.map +1 -0
  237. package/dist/index.d.mts +27 -4
  238. package/dist/index.d.mts.map +1 -0
  239. package/dist/index.mjs +3 -3
  240. package/dist/kysely-BHYV-WC-.d.cts +216 -0
  241. package/dist/kysely-BHYV-WC-.d.cts.map +1 -0
  242. package/dist/kysely-CAW3QSqp.d.mts +216 -0
  243. package/dist/kysely-CAW3QSqp.d.mts.map +1 -0
  244. package/dist/kysely-Cs4uPO8k.mjs +371 -0
  245. package/dist/kysely-Cs4uPO8k.mjs.map +1 -0
  246. package/dist/kysely-CukArHJ0.cjs +384 -0
  247. package/dist/kysely-CukArHJ0.cjs.map +1 -0
  248. package/dist/{telemetry-C3L0iS28.d.cts → lambdaTelemetry-8vnmVZQ5.d.cts} +3 -6
  249. package/dist/lambdaTelemetry-8vnmVZQ5.d.cts.map +1 -0
  250. package/dist/{telemetry-D7bVFLGi.d.mts → lambdaTelemetry-8vnmVZQ5.d.mts} +3 -6
  251. package/dist/lambdaTelemetry-8vnmVZQ5.d.mts.map +1 -0
  252. package/dist/mobile-app.cjs +74 -0
  253. package/dist/mobile-app.cjs.map +1 -0
  254. package/dist/mobile-app.d.cts +73 -0
  255. package/dist/mobile-app.d.cts.map +1 -0
  256. package/dist/mobile-app.d.mts +73 -0
  257. package/dist/mobile-app.d.mts.map +1 -0
  258. package/dist/mobile-app.mjs +73 -0
  259. package/dist/mobile-app.mjs.map +1 -0
  260. package/dist/object-storage.cjs +26 -11
  261. package/dist/object-storage.cjs.map +1 -1
  262. package/dist/object-storage.d.cts +12 -7
  263. package/dist/object-storage.d.cts.map +1 -1
  264. package/dist/object-storage.d.mts +12 -7
  265. package/dist/object-storage.d.mts.map +1 -1
  266. package/dist/object-storage.mjs +18 -5
  267. package/dist/object-storage.mjs.map +1 -1
  268. package/dist/queue/index.cjs +6 -189
  269. package/dist/queue/index.d.cts +3 -60
  270. package/dist/queue/index.d.mts +3 -60
  271. package/dist/queue/index.mjs +3 -186
  272. package/dist/random-password.cjs +86 -0
  273. package/dist/random-password.cjs.map +1 -0
  274. package/dist/random-password.d.cts +30 -0
  275. package/dist/random-password.d.cts.map +1 -0
  276. package/dist/random-password.d.mts +30 -0
  277. package/dist/random-password.d.mts.map +1 -0
  278. package/dist/random-password.mjs +84 -0
  279. package/dist/random-password.mjs.map +1 -0
  280. package/dist/{lazyAccessors-BoJN3SMd.mjs → readRequestBody-BTIl3k-D.mjs} +117 -60
  281. package/dist/readRequestBody-BTIl3k-D.mjs.map +1 -0
  282. package/dist/readRequestBody-Dej_L3c4.cjs +342 -0
  283. package/dist/readRequestBody-Dej_L3c4.cjs.map +1 -0
  284. package/dist/requestLogger-BZcXwdBs.cjs +207 -0
  285. package/dist/requestLogger-BZcXwdBs.cjs.map +1 -0
  286. package/dist/requestLogger-pA4gi3E1.mjs +178 -0
  287. package/dist/requestLogger-pA4gi3E1.mjs.map +1 -0
  288. package/dist/rest-api.cjs +90 -59
  289. package/dist/rest-api.cjs.map +1 -1
  290. package/dist/rest-api.d.cts +70 -42
  291. package/dist/rest-api.d.cts.map +1 -1
  292. package/dist/rest-api.d.mts +70 -42
  293. package/dist/rest-api.d.mts.map +1 -1
  294. package/dist/rest-api.mjs +82 -51
  295. package/dist/rest-api.mjs.map +1 -1
  296. package/dist/{chunk-DWy1uDak.cjs → rolldown-runtime-y5ARvAq5.cjs} +14 -18
  297. package/dist/runSubscriber-BHvJ8POr.mjs +109 -0
  298. package/dist/runSubscriber-BHvJ8POr.mjs.map +1 -0
  299. package/dist/runSubscriber-D8nLXIv1.cjs +126 -0
  300. package/dist/runSubscriber-D8nLXIv1.cjs.map +1 -0
  301. package/dist/shutdown-BMJmAhSt.cjs +38 -0
  302. package/dist/shutdown-BMJmAhSt.cjs.map +1 -0
  303. package/dist/shutdown-C7xa8x9S.mjs +27 -0
  304. package/dist/shutdown-C7xa8x9S.mjs.map +1 -0
  305. package/dist/site.cjs +33 -7
  306. package/dist/site.cjs.map +1 -1
  307. package/dist/site.d.cts +17 -7
  308. package/dist/site.d.cts.map +1 -1
  309. package/dist/site.d.mts +17 -7
  310. package/dist/site.d.mts.map +1 -1
  311. package/dist/site.mjs +29 -3
  312. package/dist/site.mjs.map +1 -1
  313. package/dist/stages-BSCF19o2.d.cts +56 -0
  314. package/dist/stages-BSCF19o2.d.cts.map +1 -0
  315. package/dist/stages-BSCF19o2.d.mts +56 -0
  316. package/dist/stages-BSCF19o2.d.mts.map +1 -0
  317. package/dist/subscribers/index.cjs +118 -6
  318. package/dist/subscribers/index.cjs.map +1 -0
  319. package/dist/subscribers/index.d.cts +51 -6
  320. package/dist/subscribers/index.d.cts.map +1 -0
  321. package/dist/subscribers/index.d.mts +51 -6
  322. package/dist/subscribers/index.d.mts.map +1 -0
  323. package/dist/subscribers/index.mjs +115 -5
  324. package/dist/subscribers/index.mjs.map +1 -0
  325. package/dist/surfaceEnv-CNimK76p.cjs +56 -0
  326. package/dist/surfaceEnv-CNimK76p.cjs.map +1 -0
  327. package/dist/surfaceEnv-wMT5QzgZ.mjs +45 -0
  328. package/dist/surfaceEnv-wMT5QzgZ.mjs.map +1 -0
  329. package/dist/telemetry-B48RLjyk.d.cts +40 -0
  330. package/dist/telemetry-B48RLjyk.d.cts.map +1 -0
  331. package/dist/telemetry-BEoBn6GO.d.mts +40 -0
  332. package/dist/telemetry-BEoBn6GO.d.mts.map +1 -0
  333. package/dist/telemetry.cjs +56 -0
  334. package/dist/telemetry.cjs.map +1 -0
  335. package/dist/telemetry.d.cts +2 -0
  336. package/dist/telemetry.d.mts +2 -0
  337. package/dist/telemetry.mjs +55 -0
  338. package/dist/telemetry.mjs.map +1 -0
  339. package/dist/topic/index.cjs +3 -137
  340. package/dist/topic/index.d.cts +2 -4
  341. package/dist/topic/index.d.mts +2 -4
  342. package/dist/topic/index.mjs +2 -134
  343. package/dist/tracing-Bbj1uoFJ.mjs +127 -0
  344. package/dist/tracing-Bbj1uoFJ.mjs.map +1 -0
  345. package/dist/tracing-C0ooU0V2.cjs +150 -0
  346. package/dist/tracing-C0ooU0V2.cjs.map +1 -0
  347. package/dist/{types-BlAkSuGI.d.cts → types-DMuSQeLL.d.cts} +3 -5
  348. package/dist/types-DMuSQeLL.d.cts.map +1 -0
  349. package/dist/{types-aKksMEWL.d.mts → types-DMuSQeLL.d.mts} +3 -5
  350. package/dist/types-DMuSQeLL.d.mts.map +1 -0
  351. package/dist/types.d.cts +1 -1
  352. package/dist/types.d.mts +1 -1
  353. package/dist/types.mjs +1 -0
  354. package/dist/worker.cjs +59 -34
  355. package/dist/worker.cjs.map +1 -1
  356. package/dist/worker.d.cts +70 -45
  357. package/dist/worker.d.cts.map +1 -1
  358. package/dist/worker.d.mts +71 -45
  359. package/dist/worker.d.mts.map +1 -1
  360. package/dist/worker.mjs +54 -29
  361. package/dist/worker.mjs.map +1 -1
  362. package/package.json +115 -41
  363. package/dist/Construct-BD5neF7w.mjs.map +0 -1
  364. package/dist/Construct-C0ijr2qp.d.cts +0 -117
  365. package/dist/Construct-C0ijr2qp.d.cts.map +0 -1
  366. package/dist/Construct-Ct8nalEi.d.mts +0 -117
  367. package/dist/Construct-Ct8nalEi.d.mts.map +0 -1
  368. package/dist/Construct-CyS3RpTL.cjs.map +0 -1
  369. package/dist/Cron-DmQ8vQ4b.d.cts.map +0 -1
  370. package/dist/Cron-dh4rUgib.d.mts.map +0 -1
  371. package/dist/CronBuilder-BquXDbDI.d.cts +0 -44
  372. package/dist/CronBuilder-BquXDbDI.d.cts.map +0 -1
  373. package/dist/CronBuilder-CGgY2080.d.mts +0 -44
  374. package/dist/CronBuilder-CGgY2080.d.mts.map +0 -1
  375. package/dist/CronBuilder-CYKT5RiO.mjs.map +0 -1
  376. package/dist/CronBuilder-Da0C0edR.cjs.map +0 -1
  377. package/dist/Endpoint-BjWKinFA.d.mts.map +0 -1
  378. package/dist/Endpoint-DCQPMdUX.mjs.map +0 -1
  379. package/dist/Endpoint-DcWXE-SZ.cjs.map +0 -1
  380. package/dist/Endpoint-dav89W6-.d.cts.map +0 -1
  381. package/dist/EndpointFactory-CdWB7wPw.cjs.map +0 -1
  382. package/dist/EndpointFactory-CjV8F_lJ.mjs.map +0 -1
  383. package/dist/HonoEndpointAdaptor-DtL9j19R.cjs.map +0 -1
  384. package/dist/HonoEndpointAdaptor-P1ahhZHu.mjs.map +0 -1
  385. package/dist/Queue-D7ZuwBhy.d.cts.map +0 -1
  386. package/dist/Queue-gVGfi2Yk.d.mts.map +0 -1
  387. package/dist/Subscriber-2mEi0ImH.d.cts.map +0 -1
  388. package/dist/Subscriber-xTJ9ayPN.d.mts.map +0 -1
  389. package/dist/SubscriberBuilder-4OPehf2R.cjs.map +0 -1
  390. package/dist/SubscriberBuilder-BjkbeT9l.mjs.map +0 -1
  391. package/dist/SubscriberBuilder-Dptmo-1A.d.mts +0 -62
  392. package/dist/SubscriberBuilder-Dptmo-1A.d.mts.map +0 -1
  393. package/dist/SubscriberBuilder-waNhzjSM.d.cts +0 -62
  394. package/dist/SubscriberBuilder-waNhzjSM.d.cts.map +0 -1
  395. package/dist/Topic-19QeHBIR.d.cts +0 -91
  396. package/dist/Topic-19QeHBIR.d.cts.map +0 -1
  397. package/dist/Topic-DuFeqkBh.d.mts +0 -91
  398. package/dist/Topic-DuFeqkBh.d.mts.map +0 -1
  399. package/dist/auth.d.cts.map +0 -1
  400. package/dist/auth.d.mts.map +0 -1
  401. package/dist/cache-0ANvRQnD.mjs +0 -51
  402. package/dist/cache-0ANvRQnD.mjs.map +0 -1
  403. package/dist/cache-3WSvAulx.cjs +0 -57
  404. package/dist/cache-3WSvAulx.cjs.map +0 -1
  405. package/dist/cache-BrRz3fEB.d.mts.map +0 -1
  406. package/dist/cache-BtZY_tHe.d.cts.map +0 -1
  407. package/dist/clone-DblgEZZa.mjs.map +0 -1
  408. package/dist/clone-Dy0cR2SZ.cjs.map +0 -1
  409. package/dist/construct-interface-BfjcOD_E.cjs +0 -117
  410. package/dist/construct-interface-BfjcOD_E.cjs.map +0 -1
  411. package/dist/construct-interface-D9d4Gm4p.d.cts.map +0 -1
  412. package/dist/construct-interface-Z4CpS8tK.mjs +0 -81
  413. package/dist/construct-interface-Z4CpS8tK.mjs.map +0 -1
  414. package/dist/construct-interface-g5GM5pwR.d.mts.map +0 -1
  415. package/dist/credential.cjs.map +0 -1
  416. package/dist/credential.d.cts.map +0 -1
  417. package/dist/credential.d.mts.map +0 -1
  418. package/dist/credential.mjs.map +0 -1
  419. package/dist/database/kysely.cjs.map +0 -1
  420. package/dist/database/kysely.d.cts.map +0 -1
  421. package/dist/database/kysely.d.mts.map +0 -1
  422. package/dist/database/kysely.mjs.map +0 -1
  423. package/dist/functions-i_la-N5r.cjs.map +0 -1
  424. package/dist/index-Bd5FqCoe.d.cts.map +0 -1
  425. package/dist/index-BknhM6Ux.d.mts +0 -31
  426. package/dist/index-BknhM6Ux.d.mts.map +0 -1
  427. package/dist/index-CBSfrowv.d.cts +0 -31
  428. package/dist/index-CBSfrowv.d.cts.map +0 -1
  429. package/dist/index-D24Xm79M.d.cts +0 -171
  430. package/dist/index-D24Xm79M.d.cts.map +0 -1
  431. package/dist/index-_ajboYxK.d.mts +0 -171
  432. package/dist/index-_ajboYxK.d.mts.map +0 -1
  433. package/dist/index-lAO9egTb.d.mts.map +0 -1
  434. package/dist/lazyAccessors-BoJN3SMd.mjs.map +0 -1
  435. package/dist/lazyAccessors-e3j2oE2m.cjs +0 -274
  436. package/dist/lazyAccessors-e3j2oE2m.cjs.map +0 -1
  437. package/dist/processAudits-CGqttuXm.cjs +0 -92
  438. package/dist/processAudits-CGqttuXm.cjs.map +0 -1
  439. package/dist/processAudits-Dmts9H_b.mjs +0 -80
  440. package/dist/processAudits-Dmts9H_b.mjs.map +0 -1
  441. package/dist/queue/index.cjs.map +0 -1
  442. package/dist/queue/index.d.cts.map +0 -1
  443. package/dist/queue/index.d.mts.map +0 -1
  444. package/dist/queue/index.mjs.map +0 -1
  445. package/dist/surfaceEnv-BFXEms2f.cjs +0 -39
  446. package/dist/surfaceEnv-BFXEms2f.cjs.map +0 -1
  447. package/dist/surfaceEnv-C3Ktahea.mjs +0 -27
  448. package/dist/surfaceEnv-C3Ktahea.mjs.map +0 -1
  449. package/dist/telemetry-C3L0iS28.d.cts.map +0 -1
  450. package/dist/telemetry-D7bVFLGi.d.mts.map +0 -1
  451. package/dist/topic/index.cjs.map +0 -1
  452. package/dist/topic/index.mjs.map +0 -1
  453. package/dist/types-BlAkSuGI.d.cts.map +0 -1
  454. package/dist/types-aKksMEWL.d.mts.map +0 -1
package/README.md CHANGED
@@ -26,21 +26,20 @@ pnpm add @geekmidas/constructs
26
26
 
27
27
  ```typescript
28
28
  // The HTTP surface. Endpoints are built from it — `api.get(…)`, or
29
- // `api.endpoints` for a branch that a group of endpoints shares.
29
+ // `api.database(db)` for a branch that a group of endpoints shares.
30
30
  import { RestApi } from '@geekmidas/constructs/rest-api';
31
31
 
32
32
  // Endpoint internals: the factory a surface hands you, and the builder it
33
33
  // returns. There is no free-standing `e` — an endpoint belongs to a surface.
34
34
  import { EndpointBuilder, EndpointFactory } from '@geekmidas/constructs/endpoints';
35
35
 
36
- // Functions
37
- import { f, FunctionBuilder } from '@geekmidas/constructs/functions';
36
+ // The process with no port. Crons, topic subscribers, queues and functions
37
+ // are built from it — `worker.cron(…)`, `worker.topic(…)`, `worker.queue(…)`,
38
+ // `worker.input(…)` — and carry its logger.
39
+ import { Worker } from '@geekmidas/constructs/worker';
38
40
 
39
- // Crons
40
- import { c, CronBuilder } from '@geekmidas/constructs/crons';
41
-
42
- // Subscribers
43
- import { s, SubscriberBuilder } from '@geekmidas/constructs/subscribers';
41
+ // A declared event contract. Its publisher is its `service`.
42
+ import { Topic } from '@geekmidas/constructs/topic';
44
43
  ```
45
44
 
46
45
  ## Quick Start
@@ -126,15 +125,31 @@ export const searchUsers = api
126
125
  });
127
126
  ```
128
127
 
128
+ ### The Worker
129
+
130
+ Everything that is not an HTTP route is built from a `Worker`: the process
131
+ that runs a schedule, drains a queue or consumes a topic. It takes no
132
+ authorizer, because nothing reaches it from outside.
133
+
134
+ ```typescript
135
+ // constructs/worker.ts
136
+ import { Worker } from '@geekmidas/constructs/worker';
137
+ import { database } from './database';
138
+ import { logger } from './logger';
139
+
140
+ // `.database(…)` is where a server target keeps cron schedules.
141
+ export const worker = new Worker('Jobs', { logger }).database(database);
142
+ ```
143
+
129
144
  ### Cloud Functions
130
145
 
131
- Create serverless functions with input/output validation:
146
+ Create functions with input/output validation:
132
147
 
133
148
  ```typescript
134
- import { f } from '@geekmidas/constructs/functions';
149
+ import { worker } from '../constructs/worker';
135
150
  import { z } from 'zod';
136
151
 
137
- export const processOrder = f
152
+ export const processOrder = worker
138
153
  .input(z.object({
139
154
  orderId: z.string(),
140
155
  items: z.array(z.object({
@@ -145,13 +160,12 @@ export const processOrder = f
145
160
  .output(z.object({
146
161
  orderId: z.string(),
147
162
  status: z.enum(['processing', 'completed', 'failed']),
148
- processedAt: z.string().datetime()
163
+ processedAt: z.iso.datetime()
149
164
  }))
150
165
  .timeout(300000) // 5 minutes
151
166
  .handle(async ({ input, logger }) => {
152
167
  logger.info(`Processing order ${input.orderId}`);
153
168
 
154
- // Process order logic
155
169
  for (const item of input.items) {
156
170
  logger.info(`Processing item ${item.id}, quantity: ${item.quantity}`);
157
171
  }
@@ -166,87 +180,99 @@ export const processOrder = f
166
180
 
167
181
  ### Scheduled Tasks (Crons)
168
182
 
169
- Define cron jobs with schedules:
170
-
171
183
  ```typescript
172
- import { cron } from '@geekmidas/constructs/crons';
184
+ import { worker } from '../constructs/worker';
173
185
 
174
186
  // Daily report at 9 AM UTC
175
- export const dailyReport = cron
176
- .schedule('cron(0 9 * * ? *)')
187
+ export const dailyReport = worker
188
+ .cron('cron(0 9 * * ? *)')
177
189
  .timeout(600000) // 10 minutes
178
190
  .handle(async ({ logger }) => {
179
- logger.info('Generating daily report');
180
-
181
- const reportDate = new Date().toISOString().split('T')[0];
182
191
  const reportData = {
183
- date: reportDate,
192
+ date: new Date().toISOString().split('T')[0],
184
193
  totalOrders: 150,
185
- totalRevenue: 12500.00
186
194
  };
187
195
 
188
- logger.info('Daily report generated', reportData);
196
+ logger.info(reportData, 'Daily report generated');
189
197
  return reportData;
190
198
  });
191
199
 
192
200
  // Hourly cleanup
193
- export const hourlyCleanup = cron
194
- .schedule('rate(1 hour)')
195
- .timeout(300000) // 5 minutes
196
- .handle(async ({ logger }) => {
197
- logger.info('Running hourly cleanup');
198
-
199
- const itemsCleaned = 42;
200
- logger.info(`Cleaned ${itemsCleaned} items`);
201
+ export const hourlyCleanup = worker
202
+ .cron('rate(1 hour)')
203
+ .dependsOn([database])
204
+ .handle(async ({ services, logger }) => {
205
+ const result = await services.database
206
+ .deleteFrom('sessions')
207
+ .where('expires_at', '<', new Date())
208
+ .executeTakeFirst();
201
209
 
202
- return { itemsCleaned };
210
+ logger.info({ deleted: Number(result.numDeletedRows) }, 'Cleaned up');
203
211
  });
204
212
  ```
205
213
 
206
- ### Event Subscribers
214
+ On AWS a cron is an EventBridge rule; on a server the worker schedules it in
215
+ the database it was given.
216
+
217
+ ### Topics and Subscribers
207
218
 
208
- Handle events with type-safe message processing:
219
+ A topic declares the event contract once. A subscriber binds to it from the
220
+ worker and is typed by it:
209
221
 
210
222
  ```typescript
211
- import { SubscriberBuilder } from '@geekmidas/constructs/subscribers';
223
+ // constructs/topics.ts
224
+ import { Topic } from '@geekmidas/constructs/topic';
212
225
  import { z } from 'zod';
213
226
 
214
- export const userEventsSubscriber = new SubscriberBuilder()
215
- .subscribe(['user.created', 'user.updated', 'user.deleted'])
216
- .timeout(30000)
217
- .output(z.object({
218
- processed: z.number(),
219
- success: z.boolean()
220
- }))
221
- .handle(async ({ events, logger }) => {
222
- logger.info(
223
- { eventCount: events.length },
224
- 'Processing user events'
225
- );
227
+ export const users = new Topic('Users', {
228
+ events: {
229
+ 'user.created': z.object({ userId: z.string(), email: z.email() }),
230
+ 'user.updated': z.object({ userId: z.string(), changes: z.array(z.string()) }),
231
+ },
232
+ });
226
233
 
234
+ // subscribers/userEvents.ts
235
+ export const userEventsSubscriber = worker
236
+ .topic(users)
237
+ .subscribe(['user.created', 'user.updated'])
238
+ .handle(async ({ events, logger }) => {
227
239
  for (const event of events) {
228
- try {
229
- switch (event.type) {
230
- case 'user.created':
231
- logger.info({ userId: event.data.userId }, 'User created');
232
- break;
233
- case 'user.updated':
234
- logger.info({ userId: event.data.userId }, 'User updated');
235
- break;
236
- case 'user.deleted':
237
- logger.info({ userId: event.data.userId }, 'User deleted');
238
- break;
239
- }
240
- } catch (error) {
241
- logger.error({ error, event }, 'Failed to process event');
242
- throw error;
243
- }
240
+ // event.type narrows event.payload to that event's schema
241
+ logger.info({ type: event.type, userId: event.payload.userId }, 'User event');
244
242
  }
243
+ });
244
+ ```
245
245
 
246
- return {
247
- processed: events.length,
248
- success: true
249
- };
246
+ A subscriber is bound, not handed a publisher. One that emits follow-ups
247
+ depends on the topic it emits to: `.dependsOn([notifications])`.
248
+
249
+ ### Queues
250
+
251
+ A queue and its one consumer are one construct. Producers send by depending on
252
+ it:
253
+
254
+ ```typescript
255
+ // queues/emails.ts
256
+ export const emails = worker
257
+ .queue('Emails')
258
+ .message(z.object({ to: z.email(), template: z.enum(['welcome']) }))
259
+ .dependsOn([mail])
260
+ .handle(async ({ messages, services }) => {
261
+ for (const { to, template } of messages) {
262
+ await services.mail.sendTemplate(template, { to, subject: 'Welcome', props: {} });
263
+ }
264
+ });
265
+
266
+ // endpoints/invites.ts
267
+ export const invite = api
268
+ .post('/invites')
269
+ .body(z.object({ email: z.email() }))
270
+ .dependsOn([emails])
271
+ .handle(async ({ body, services }) => {
272
+ // `type` is the queue's name exactly as written
273
+ await services.emails.publish([
274
+ { type: 'Emails', payload: { to: body.email, template: 'welcome' } },
275
+ ]);
250
276
  });
251
277
  ```
252
278
 
@@ -289,7 +315,7 @@ For custom authentication methods, use `.securitySchemes()` on the surface's fac
289
315
  ```typescript
290
316
  import { api } from '../constructs/api';
291
317
 
292
- const router = api.endpoints
318
+ const router = api
293
319
  .securitySchemes({
294
320
  // Custom OAuth2 with specific flows
295
321
  oauth2: {
@@ -328,7 +354,7 @@ Set a default authorizer for every endpoint built from one branch:
328
354
  // A branch of the surface's factory, with a default every endpoint on it
329
355
  // inherits. (The surface's own `default` does the same thing for every
330
356
  // endpoint it serves — this narrows it for one group.)
331
- const router = api.endpoints.authorizer('jwt');
357
+ const router = api.authorizer('jwt');
332
358
 
333
359
  // Protected by default
334
360
  export const getProfile = router
@@ -344,99 +370,69 @@ export const getHealth = router
344
370
  .handle(async () => ({ status: 'ok' }));
345
371
  ```
346
372
 
347
- ### Service Discovery
373
+ ### Dependencies
348
374
 
349
- Inject services into your constructs:
375
+ `.dependsOn([...])` takes constructs. Each one's client lands in the handler's
376
+ `services` under the construct's own id, and the edge is what the manifest
377
+ records — so the environment, the runtime client and the cloud access all come
378
+ from the one line:
350
379
 
351
380
  ```typescript
352
381
  import { api } from '../constructs/api';
353
- import type { Service } from '@geekmidas/services';
354
- import type { EnvironmentParser } from '@geekmidas/envkit';
355
- import { Kysely } from 'kysely';
382
+ import { database } from '../constructs/database';
356
383
  import { z } from 'zod';
357
384
 
358
- // Define a database service
359
- const databaseService = {
360
- serviceName: 'database' as const,
361
- async register(envParser: EnvironmentParser<{}>) {
362
- const config = envParser.create((get) => ({
363
- url: get('DATABASE_URL').string()
364
- })).parse();
365
-
366
- const db = new Kysely({ /* config */ });
367
- return db;
368
- }
369
- } satisfies Service<'database', Kysely<Database>>;
370
-
371
- // Use service in endpoint
372
385
  export const getUserFromDb = api
373
386
  .get('/users/:id')
374
387
  .params(z.object({ id: z.string() }))
375
- .services([databaseService])
388
+ .dependsOn([database])
376
389
  .handle(async ({ params, services }) => {
377
- // services.database is fully typed
378
- const user = await services.database
390
+ // services.database is a Kysely client typed by the construct's schema
391
+ return services.database
379
392
  .selectFrom('users')
380
393
  .where('id', '=', params.id)
381
394
  .selectAll()
382
395
  .executeTakeFirst();
383
-
384
- return user;
385
396
  });
386
397
  ```
387
398
 
399
+ A hand-written `Service` still works through `.services()` on every builder,
400
+ for code that has not moved to a construct yet. It records no edge, so a deploy
401
+ target cannot grant it anything.
402
+
388
403
  ### Event Publishing
389
404
 
390
- Publish events from any construct:
405
+ An endpoint, function or cron publishes to a topic with `.event(topic, …)`,
406
+ once its handler has succeeded:
391
407
 
392
408
  ```typescript
393
- import { api } from '../constructs/api';
394
- import type { Service } from '@geekmidas/services';
395
- import type { EventPublisher, PublishableMessage } from '@geekmidas/events';
396
- import { z } from 'zod';
397
-
398
- // Define event types
399
- type UserEvents =
400
- | PublishableMessage<'user.created', { userId: string; email: string }>
401
- | PublishableMessage<'user.updated', { userId: string }>;
402
-
403
- // Create event publisher service
404
- const userEventPublisher = {
405
- serviceName: 'userEventPublisher' as const,
406
- async register(envParser: EnvironmentParser<{}>) {
407
- const config = envParser.create((get) => ({
408
- publisherUrl: get('EVENT_PUBLISHER_URL').string()
409
- })).parse();
409
+ import { users } from '../constructs/topics';
410
410
 
411
- const { Publisher } = await import('@geekmidas/events');
412
- return Publisher.fromConnectionString<UserEvents>(config.publisherUrl);
413
- }
414
- } satisfies Service<'userEventPublisher', EventPublisher<UserEvents>>;
415
-
416
- // Use in endpoint with event publishing
417
- export const createUser = api
411
+ export const createUser = router
418
412
  .post('/users')
419
- .body(z.object({
420
- name: z.string(),
421
- email: z.email()
422
- }))
423
- .publisher(userEventPublisher)
424
- .event('user.created', (body, result) => ({
425
- userId: result.id,
426
- email: body.email
427
- }))
428
- .handle(async ({ body, publish }) => {
429
- const user = {
430
- id: crypto.randomUUID(),
431
- name: body.name,
432
- email: body.email
433
- };
434
-
435
- // Events are automatically published after successful execution
436
- return user;
437
- });
413
+ .body(z.object({ name: z.string(), email: z.email() }))
414
+ .output(z.object({ id: z.string(), name: z.string(), email: z.email() }))
415
+ .event(users, {
416
+ type: 'user.created',
417
+ payload: (user) => ({ userId: user.id, email: user.email }),
418
+ when: (user) => !user.email.endsWith('@example.com'), // optional
419
+ })
420
+ .handle(async ({ body }) => ({ id: crypto.randomUUID(), ...body }));
438
421
  ```
439
422
 
423
+ - `type` is checked against the topic's events, and `payload` against that
424
+ event's schema.
425
+ - `.event()` is repeatable across topics. Two calls to two topics publish to
426
+ each, every event through its own topic's publisher.
427
+ - It also puts the topic's publisher in the handler as `services.users`, as
428
+ `.dependsOn([users])` would, for events the handler decides on:
429
+ `await services.users.publish([{ type: 'user.updated', payload }])`.
430
+ - A failed publish is logged, never thrown — the handler already succeeded.
431
+
432
+ The publisher reads `USERS_PUBLISHER_CONNECTION_STRING`, which only constructs
433
+ that publish to the topic are given, and picks its transport from the protocol:
434
+ `pgboss://` locally, `sns://` deployed.
435
+
440
436
  ### Database Context
441
437
 
442
438
  Inject a database instance directly into the handler context using `.database()`:
@@ -633,10 +629,11 @@ For complex scenarios, use `ctx.auditor` to record audits manually within your h
633
629
 
634
630
  ```typescript
635
631
  export const processOrder = api
636
- .post('/orders')
637
- .database(databaseService)
638
- .services([paymentService])
632
+ .database(database)
639
633
  .auditor(auditStorageService)
634
+ .post('/orders')
635
+ // `payments` is an ExternalApi whose client has `charge()`
636
+ .dependsOn([payments])
640
637
  .actor(({ session }) => ({ id: session.userId, type: 'user' }))
641
638
  .handle(async ({ body, db, services, auditor }) => {
642
639
  // db is automatically the transaction when auditor uses KyselyAuditStorage
@@ -647,7 +644,7 @@ export const processOrder = api
647
644
  .executeTakeFirstOrThrow();
648
645
 
649
646
  // Manual audit for payment (external service call)
650
- const payment = await services.payment.charge(order.total);
647
+ const payment = await services.payments.charge(order.total);
651
648
  auditor.audit('payment.processed', {
652
649
  orderId: order.id,
653
650
  amount: order.total,
@@ -908,6 +905,69 @@ export const sendMessage = api
908
905
  });
909
906
  ```
910
907
 
908
+ ## Feature Tests
909
+
910
+ A feature test drives an app the way it runs deployed: a browser signs in and
911
+ calls the API, the API asks the auth server who is calling, each over its URL,
912
+ and every database is in its own transaction, rolled back after the test.
913
+ Nothing runs as a server — each URL an app's constructs answer on is served
914
+ in-process through MSW, from the real handler, for the test that made the
915
+ request. Postgres, Mailpit and MinIO are the real local ones `gkm test` starts.
916
+
917
+ Nothing is declared in test code. `gkm test` writes `.gkm/test/` into each app:
918
+
919
+ - `manifest.json` — which construct and endpoint is exported where, and the
920
+ test stage's environment, keyed as the constructs derive their keys;
921
+ - `clients/<surface>.ts` — each surface's typed client, from the same generator
922
+ `gkm build` uses;
923
+ - `index.ts` — a `Browser` with a client per surface (`Api` → `browser.api`)
924
+ and per auth server (`browser.auth`, its better-auth plugins paired with
925
+ their client plugins), and the configured `it`.
926
+
927
+ Map it once in the app's `package.json` — `"imports": { "#test":
928
+ "./.gkm/test/index.ts" }` — and a test is:
929
+
930
+ ```typescript
931
+ import { it } from '#test';
932
+
933
+ it('shows the signed-in user their orders', async ({ browser, db, factories }) => {
934
+ const factory = await factories.get('database');
935
+ await factory.insert('orders', { email: 'ada@shop.test' });
936
+
937
+ await browser.signIn('ada@shop.test');
938
+ const { orders } = await browser.api.get('/orders');
939
+ expect(orders).toHaveLength(1);
940
+
941
+ const app = await db.get('database');
942
+ expect(await app.selectFrom('orders').selectAll().execute()).toHaveLength(1);
943
+ });
944
+ ```
945
+
946
+ - **`browser`** — already the global `fetch`; its cookie jar follows the
947
+ browser's rules, so a session cookie set for the wrong domain never reaches
948
+ the API.
949
+ - **`browser.signIn(email)`** — when the auth server has the magic-link plugin
950
+ and the app sends mail: the link requested, read from the inbox and
951
+ followed, as a person signs in. Returns the session the auth server reports.
952
+ - **`db.get(name)`** — the app's own databases by service name
953
+ (`db.get('database')`), each this test's transaction, typed by its schema.
954
+ Opened on first use by whatever reaches it first — the test, a factory, an
955
+ endpoint — and the same one for all of them. A tenant an auth server owns is
956
+ not among them: it is reached through the auth server, as the app reaches
957
+ it. Nor is a reader.
958
+ - **`factories.get(name)`** — each of those databases' factory, from
959
+ `test/factories/<construct>.ts` at the project root, on the same
960
+ transaction: `(await factories.get('database')).insert('users', …)`.
961
+ - **`mailbox(address)`** — the mail actually sent, read from Mailpit.
962
+ - **`published(topic | queue)`** — what the test published or enqueued.
963
+ Nothing is delivered: a subscriber or a worker is run on its own with
964
+ **`subscriber(s).invoke({ events })`** / **`queue(q).invoke({ messages })`**,
965
+ on the test's services and transactions.
966
+ - Each database construct — the app's and each schema tenant — gets its own
967
+ transaction on its own connection, as deployed.
968
+ - A request that belongs to no running test is refused with
969
+ `UnknownTestContext` rather than reaching another test's data.
970
+
911
971
  ## AWS Lambda Adapters
912
972
 
913
973
  Deploy your constructs to AWS Lambda:
@@ -1034,19 +1094,19 @@ Every construct has an async `getEnvironment()` method that returns the environm
1034
1094
 
1035
1095
  ```typescript
1036
1096
  import { api } from '../constructs/api';
1037
- import { databaseService } from './services/database';
1038
- import { cacheService } from './services/cache';
1097
+ import { cache } from '../constructs/cache';
1098
+ import { database } from '../constructs/database';
1039
1099
 
1040
1100
  const endpoint = api
1041
1101
  .get('/users')
1042
- .services([databaseService, cacheService])
1102
+ .dependsOn([database, cache])
1043
1103
  .handle(async ({ services }) => {
1044
1104
  // Implementation
1045
1105
  });
1046
1106
 
1047
1107
  // Detect required environment variables
1048
1108
  const envVars = await endpoint.getEnvironment();
1049
- // Returns: ['CACHE_URL', 'DATABASE_URL'] (sorted alphabetically)
1109
+ // Returns: ['DATABASE_URL', 'SESSIONS_URL'] (sorted alphabetically)
1050
1110
  ```
1051
1111
 
1052
1112
  ### How It Works
@@ -1134,33 +1194,37 @@ This manifest can then be used by infrastructure-as-code tools (Terraform, CDK,
1134
1194
  - **Deduplication**: Each variable listed once even if used by multiple services
1135
1195
  - **Sorted Output**: Variables always returned in alphabetical order
1136
1196
  - **Error Resilient**: Parse failures don't affect detection
1137
- - **Publisher Support**: Detects variables from `.publisher()` services
1197
+ - **Topic Support**: A topic named by `.event(topic, …)` contributes its `<ID>_PUBLISHER_CONNECTION_STRING`
1138
1198
 
1139
1199
  ### Example with Multiple Services
1140
1200
 
1141
1201
  ```typescript
1142
1202
  import { api } from '../constructs/api';
1143
- import { databaseService } from './services/database';
1144
- import { cacheService } from './services/cache';
1145
- import { emailService } from './services/email';
1203
+ import { cache } from '../constructs/cache';
1204
+ import { database } from '../constructs/database';
1205
+ import { mail } from '../constructs/email';
1206
+ import { users } from '../constructs/topics';
1146
1207
 
1147
1208
  const endpoint = api
1148
1209
  .post('/users')
1149
- .services([databaseService, cacheService, emailService])
1210
+ .dependsOn([database, cache, mail])
1211
+ .output(userSchema)
1212
+ .event(users, {
1213
+ type: 'user.created',
1214
+ payload: (user) => ({ userId: user.id, email: user.email }),
1215
+ })
1150
1216
  .handle(async ({ services }) => {
1151
1217
  // Create user
1152
1218
  });
1153
1219
 
1154
- // Automatically detects all variables from all services
1220
+ // Detects the keys of every construct it names
1155
1221
  const envVars = await endpoint.getEnvironment();
1156
1222
  // Returns: [
1157
- // 'CACHE_URL',
1158
1223
  // 'DATABASE_URL',
1159
- // 'DATABASE_PORT',
1160
- // 'SMTP_HOST',
1161
- // 'SMTP_PORT',
1162
- // 'SMTP_USER',
1163
- // 'SMTP_PASS'
1224
+ // 'MAIL_FROM',
1225
+ // 'MAIL_URL',
1226
+ // 'SESSIONS_URL',
1227
+ // 'USERS_PUBLISHER_CONNECTION_STRING'
1164
1228
  // ]
1165
1229
  ```
1166
1230