theokit 0.4.0-beta.0 → 0.4.0

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 (297) hide show
  1. package/dist/{actions-virtual-module-HWNTIEPI.js → actions-virtual-module-IY46EEEX.js} +2 -2
  2. package/dist/{actions-virtual-module-SEIPACG5.js → actions-virtual-module-XNHDNCZ6.js} +2 -2
  3. package/dist/add-2ZFFGGE4.js +0 -0
  4. package/dist/{app-typed-client-ITIYTBXO.js → app-typed-client-OUJO3V5J.js} +10 -5
  5. package/dist/{app-typed-client-62FYBLVJ.js.map → app-typed-client-OUJO3V5J.js.map} +1 -1
  6. package/dist/{app-typed-client-62FYBLVJ.js → app-typed-client-YOMWSA3F.js} +11 -6
  7. package/dist/{app-typed-client-ITIYTBXO.js.map → app-typed-client-YOMWSA3F.js.map} +1 -1
  8. package/dist/audit-log-BQWM5YLG.d.ts +60 -0
  9. package/dist/{aws-lambda-XYCGZH3I.js → aws-lambda-GKSTX6HD.js} +3 -3
  10. package/dist/body-parser-web-MUWBKZ3F.js +70 -0
  11. package/dist/body-parser-web-MUWBKZ3F.js.map +1 -0
  12. package/dist/broadcast-QOQGUNNK.js +0 -0
  13. package/dist/{build-HIGHJQQV.js → build-D4J7XECU.js} +31 -22
  14. package/dist/build-D4J7XECU.js.map +1 -0
  15. package/dist/build-request-body-preview-II2235E6.js +0 -0
  16. package/dist/{bun-K73TQEWC.js → bun-ISHSNFIO.js} +3 -3
  17. package/dist/{check-PZR67OY5.js → check-NXYPLM6M.js} +2 -2
  18. package/dist/{chunk-WZ7CPVQB.js → chunk-27LWMYNK.js} +28 -24
  19. package/dist/chunk-27LWMYNK.js.map +1 -0
  20. package/dist/{chunk-BIGBFZSU.js → chunk-2F4FROEQ.js} +1689 -1521
  21. package/dist/chunk-2F4FROEQ.js.map +1 -0
  22. package/dist/chunk-4HBI7DHP.js +20 -0
  23. package/dist/chunk-4HBI7DHP.js.map +1 -0
  24. package/dist/{chunk-X4JAX2U3.js → chunk-4S2UCILN.js} +180 -125
  25. package/dist/chunk-4S2UCILN.js.map +1 -0
  26. package/dist/{chunk-C52GSJPF.js → chunk-4S6YHNG2.js} +11 -8
  27. package/dist/chunk-4S6YHNG2.js.map +1 -0
  28. package/dist/chunk-5ODOE6EF.js +0 -0
  29. package/dist/{chunk-KJVEJILQ.js → chunk-5PU65T5W.js} +9 -3
  30. package/dist/chunk-5PU65T5W.js.map +1 -0
  31. package/dist/chunk-5QW7IQQU.js +36 -0
  32. package/dist/chunk-5QW7IQQU.js.map +1 -0
  33. package/dist/chunk-5Z2727GV.js +1324 -0
  34. package/dist/chunk-5Z2727GV.js.map +1 -0
  35. package/dist/{chunk-YLBSSEHX.js → chunk-6NEXVBPY.js} +5 -15
  36. package/dist/chunk-6NEXVBPY.js.map +1 -0
  37. package/dist/chunk-7MQOHNHE.js +36 -0
  38. package/dist/chunk-7MQOHNHE.js.map +1 -0
  39. package/dist/{chunk-TPCT3MXV.js → chunk-ABVITXFH.js} +405 -272
  40. package/dist/chunk-ABVITXFH.js.map +1 -0
  41. package/dist/chunk-ASDXVRJD.js +185 -0
  42. package/dist/chunk-ASDXVRJD.js.map +1 -0
  43. package/dist/chunk-B3L3TJVF.js +406 -0
  44. package/dist/chunk-B3L3TJVF.js.map +1 -0
  45. package/dist/{chunk-PB4GR7EP.js → chunk-C3ZZ56YZ.js} +1 -18
  46. package/dist/chunk-C3ZZ56YZ.js.map +1 -0
  47. package/dist/{chunk-O7335ZGH.js → chunk-CG563V5M.js} +5 -3
  48. package/dist/chunk-CG563V5M.js.map +1 -0
  49. package/dist/{chunk-5OFPQK6N.js → chunk-COXLEZCN.js} +2 -1
  50. package/dist/chunk-COXLEZCN.js.map +1 -0
  51. package/dist/{chunk-O4BLVPML.js → chunk-DNMSV3R3.js} +22 -28
  52. package/dist/chunk-DNMSV3R3.js.map +1 -0
  53. package/dist/chunk-E3JH6YUM.js +1 -0
  54. package/dist/chunk-ESC54TAK.js +220 -0
  55. package/dist/chunk-ESC54TAK.js.map +1 -0
  56. package/dist/chunk-FI7MPTJW.js +77 -0
  57. package/dist/chunk-FI7MPTJW.js.map +1 -0
  58. package/dist/chunk-H4J2LECY.js +201 -0
  59. package/dist/chunk-H4J2LECY.js.map +1 -0
  60. package/dist/chunk-IAJ2JVEH.js +256 -0
  61. package/dist/chunk-IAJ2JVEH.js.map +1 -0
  62. package/dist/{chunk-F3TG5FJV.js → chunk-IEZCAT2I.js} +7 -1
  63. package/dist/{chunk-F3TG5FJV.js.map → chunk-IEZCAT2I.js.map} +1 -1
  64. package/dist/chunk-JHEAWR3L.js +1 -0
  65. package/dist/chunk-JQSKBMXP.js +17 -0
  66. package/dist/chunk-JQSKBMXP.js.map +1 -0
  67. package/dist/{chunk-4QTKSLTO.js → chunk-K4M64WD6.js} +9 -5
  68. package/dist/{chunk-4QTKSLTO.js.map → chunk-K4M64WD6.js.map} +1 -1
  69. package/dist/chunk-KI7OWQUX.js +293 -0
  70. package/dist/chunk-KI7OWQUX.js.map +1 -0
  71. package/dist/chunk-KT3MWNZG.js +0 -0
  72. package/dist/{chunk-ZJW5FFYJ.js → chunk-LC5PYNJP.js} +2 -2
  73. package/dist/{chunk-JAAHOGKI.js → chunk-M2EY7KDR.js} +1223 -1124
  74. package/dist/chunk-M2EY7KDR.js.map +1 -0
  75. package/dist/{chunk-2VQKDRVF.js → chunk-MR7OLIC6.js} +3 -3
  76. package/dist/chunk-NM4WF3XD.js +63 -0
  77. package/dist/chunk-NM4WF3XD.js.map +1 -0
  78. package/dist/chunk-NPMCKOX4.js +1 -0
  79. package/dist/{chunk-VRHXOKRP.js → chunk-NZXH2LQQ.js} +86 -83
  80. package/dist/chunk-NZXH2LQQ.js.map +1 -0
  81. package/dist/{chunk-B6RP57M3.js → chunk-OLPB36O2.js} +4 -4
  82. package/dist/chunk-PIVX3DYW.js +142 -0
  83. package/dist/chunk-PIVX3DYW.js.map +1 -0
  84. package/dist/{chunk-7TOMTMHT.js → chunk-R7OC6UDK.js} +2 -1
  85. package/dist/chunk-R7OC6UDK.js.map +1 -0
  86. package/dist/chunk-RESN62GB.js +269 -0
  87. package/dist/chunk-RESN62GB.js.map +1 -0
  88. package/dist/{chunk-64JI5LTO.js → chunk-RMU7EBRO.js} +2 -2
  89. package/dist/chunk-RMU7EBRO.js.map +1 -0
  90. package/dist/chunk-TQYSPYKU.js +131 -0
  91. package/dist/chunk-TQYSPYKU.js.map +1 -0
  92. package/dist/chunk-TSLSIRBR.js +107 -0
  93. package/dist/chunk-TSLSIRBR.js.map +1 -0
  94. package/dist/{chunk-XP7YXRHO.js → chunk-U6TPSW4L.js} +37 -5
  95. package/dist/chunk-U6TPSW4L.js.map +1 -0
  96. package/dist/chunk-UD3LFGDL.js +45 -0
  97. package/dist/chunk-UD3LFGDL.js.map +1 -0
  98. package/dist/chunk-UUFYP2UM.js +161 -0
  99. package/dist/chunk-UUFYP2UM.js.map +1 -0
  100. package/dist/{chunk-OXIQI2XZ.js → chunk-VBZCVFSA.js} +2 -2
  101. package/dist/chunk-VMEWD57H.js +137 -0
  102. package/dist/chunk-VMEWD57H.js.map +1 -0
  103. package/dist/{chunk-TRH2L3FI.js → chunk-WEGALZEU.js} +3 -3
  104. package/dist/{chunk-ZOXNJV2O.js → chunk-WGHJD6I7.js} +35 -138
  105. package/dist/chunk-WGHJD6I7.js.map +1 -0
  106. package/dist/chunk-XMS5VRIK.js +0 -0
  107. package/dist/chunk-Y4H3JNVK.js +18 -0
  108. package/dist/chunk-Y4H3JNVK.js.map +1 -0
  109. package/dist/chunk-Z5IGLBWE.js +329 -0
  110. package/dist/chunk-Z5IGLBWE.js.map +1 -0
  111. package/dist/chunk-ZEGYW52B.js +26 -0
  112. package/dist/chunk-ZEGYW52B.js.map +1 -0
  113. package/dist/chunk-ZJQ4O76G.js +87 -0
  114. package/dist/chunk-ZJQ4O76G.js.map +1 -0
  115. package/dist/cli/index.js +32 -21
  116. package/dist/cli/index.js.map +1 -1
  117. package/dist/client/index.d.ts +107 -1
  118. package/dist/client/index.js +152 -6
  119. package/dist/client/index.js.map +1 -1
  120. package/dist/{cloudflare-LCAOTRYZ.js → cloudflare-OJGJ7R2D.js} +3 -3
  121. package/dist/configure-agent-registry-6YGTJYBC.js +0 -0
  122. package/dist/cookies-MJKMGJ7N.js +22 -0
  123. package/dist/csrf-BBrEZSBW.d.ts +107 -0
  124. package/dist/csrf-readiness-store-CjIoub3U.d.ts +43 -0
  125. package/dist/define-websocket-CdK94O-D.d.ts +64 -0
  126. package/dist/{deno-deploy-ZN4RE5HF.js → deno-deploy-BHWKFTOW.js} +3 -3
  127. package/dist/{dev-266MVCVA.js → dev-MJVSRZGF.js} +11 -11
  128. package/dist/{dev-emit-RBSFZZNO.js → dev-emit-5VPRCHOD.js} +39 -142
  129. package/dist/dev-emit-5VPRCHOD.js.map +1 -0
  130. package/dist/{dev-emit-NO6OZJOQ.js → dev-emit-HHP2XJXE.js} +5 -5
  131. package/dist/devtools/entry.js +185 -131
  132. package/dist/devtools/entry.js.map +1 -1
  133. package/dist/{dispatcher-AQJGI3HU.js → dispatcher-63LBXYLE.js} +2 -2
  134. package/dist/{dispatcher-E6QV32BO.js → dispatcher-EJME6C6M.js} +5 -2
  135. package/dist/dispatcher-EJME6C6M.js.map +1 -0
  136. package/dist/{dist-5V77Z223.js → dist-KRW6OVD4.js} +7 -7
  137. package/dist/dist-KRW6OVD4.js.map +1 -0
  138. package/dist/docker-EZDA5FT7.js +0 -0
  139. package/dist/error-envelope-BsNzzAV5.d.ts +62 -0
  140. package/dist/{generate-DT4IIAHF.js → generate-AZ2K6Z2Z.js} +75 -2
  141. package/dist/generate-AZ2K6Z2Z.js.map +1 -0
  142. package/dist/index-DWWEdFh5.d.ts +399 -0
  143. package/dist/index.d.ts +161 -1391
  144. package/dist/index.js +20 -8
  145. package/dist/index.js.map +1 -1
  146. package/dist/{info-FJ4NXKTB.js → info-47VB62MV.js} +5 -5
  147. package/dist/job-backend-CgC8Xf33.d.ts +68 -0
  148. package/dist/{lib-NL5JXGEB.js → lib-NST3PNZX.js} +31 -31
  149. package/dist/lib-NST3PNZX.js.map +1 -0
  150. package/dist/module-loader-Cfz7dgCP.d.ts +26 -0
  151. package/dist/{netlify-5VQ22Z2P.js → netlify-GSNSJGMN.js} +3 -3
  152. package/dist/{node-3APTLGWB.js → node-6I5B6RL3.js} +3 -3
  153. package/dist/node-6I5B6RL3.js.map +1 -0
  154. package/dist/{openapi-FNVSWZOL.js → openapi-NFLSNNVQ.js} +10 -10
  155. package/dist/plugin-runner-BGBkzgi0.d.ts +95 -0
  156. package/dist/plugin-types-DNJGxr4Z.d.ts +79 -0
  157. package/dist/{proper-lockfile-4Y3DP3RP.js → proper-lockfile-MVKMQGFK.js} +27 -27
  158. package/dist/proper-lockfile-MVKMQGFK.js.map +1 -0
  159. package/dist/rate-limit-BdNDZ3vt.d.ts +58 -0
  160. package/dist/registry-QHEZ3BWB.js +25 -0
  161. package/dist/router-RGZFX75G.js +0 -0
  162. package/dist/{routes-H4SPLFHJ.js → routes-3A4XGIEJ.js} +6 -6
  163. package/dist/{scan-C2BOKLSY.js → scan-NQFI5S7P.js} +2 -2
  164. package/dist/scan-NQFI5S7P.js.map +1 -0
  165. package/dist/schema-D3v8VB7o.d.ts +76 -0
  166. package/dist/{schema-VR6YNVLQ.js → schema-KZVOV333.js} +5 -3
  167. package/dist/schema-KZVOV333.js.map +1 -0
  168. package/dist/server/agent/index.d.ts +229 -0
  169. package/dist/server/agent/index.js +16 -0
  170. package/dist/server/agent/index.js.map +1 -0
  171. package/dist/server/auth/index.d.ts +46 -1
  172. package/dist/server/auth/index.js +10 -3
  173. package/dist/server/cost/index.d.ts +1 -3
  174. package/dist/server/cost/index.js +1 -1
  175. package/dist/server/cron/index.js +4 -2
  176. package/dist/server/define/index.d.ts +281 -0
  177. package/dist/server/define/index.js +26 -0
  178. package/dist/server/define/index.js.map +1 -0
  179. package/dist/server/http/index.d.ts +10 -0
  180. package/dist/server/http/index.js +74 -0
  181. package/dist/server/http/index.js.map +1 -0
  182. package/dist/server/index.d.ts +151 -1677
  183. package/dist/server/index.js +558 -1102
  184. package/dist/server/index.js.map +1 -1
  185. package/dist/server/jobs/index.d.ts +348 -2
  186. package/dist/server/jobs/index.js +5 -3
  187. package/dist/server/observability/index.d.ts +324 -0
  188. package/dist/server/observability/index.js +48 -0
  189. package/dist/server/observability/index.js.map +1 -0
  190. package/dist/server/plugins/index.d.ts +17 -0
  191. package/dist/server/plugins/index.js +17 -0
  192. package/dist/server/plugins/index.js.map +1 -0
  193. package/dist/server/rate-limit/index.d.ts +105 -0
  194. package/dist/server/rate-limit/index.js +25 -0
  195. package/dist/server/rate-limit/index.js.map +1 -0
  196. package/dist/server/realtime/index.d.ts +15 -0
  197. package/dist/server/realtime/index.js +8 -0
  198. package/dist/server/realtime/index.js.map +1 -0
  199. package/dist/server/scan/index.d.ts +106 -0
  200. package/dist/server/scan/index.js +43 -0
  201. package/dist/server/scan/index.js.map +1 -0
  202. package/dist/server/security/index.d.ts +193 -0
  203. package/dist/server/security/index.js +50 -0
  204. package/dist/server/security/index.js.map +1 -0
  205. package/dist/server/storage/index.d.ts +22 -0
  206. package/dist/server/storage/index.js +14 -0
  207. package/dist/server/storage/index.js.map +1 -0
  208. package/dist/server/webhook/index.d.ts +148 -0
  209. package/dist/server/webhook/index.js +19 -0
  210. package/dist/server/webhook/index.js.map +1 -0
  211. package/dist/server-error-to-envelope-NO4CCAFQ.js +8 -0
  212. package/dist/server-error-to-envelope-NO4CCAFQ.js.map +1 -0
  213. package/dist/server-error-to-envelope-UJK6OWDJ.js +134 -0
  214. package/dist/server-error-to-envelope-UJK6OWDJ.js.map +1 -0
  215. package/dist/server-routes-hmr-GZJGPWMS.js +0 -0
  216. package/dist/services-json-Z2QNGVPW.js +142 -0
  217. package/dist/services-json-Z2QNGVPW.js.map +1 -0
  218. package/dist/{services-typed-client-XWYYBWGW.js → services-typed-client-KK6RHRDG.js} +2 -2
  219. package/dist/{services-typed-client-ZX5JUHH3.js → services-typed-client-T7M5VPBP.js} +2 -2
  220. package/dist/{sqlite-vec-ARPNUBWT.js → sqlite-vec-54KB4RD3.js} +2 -2
  221. package/dist/sqlite-vec-54KB4RD3.js.map +1 -0
  222. package/dist/{start-HX3QUSOS.js → start-CGSZPBAG.js} +23 -23
  223. package/dist/start-CGSZPBAG.js.map +1 -0
  224. package/dist/{static-TZBVBDTB.js → static-QI5NQT2X.js} +6 -6
  225. package/dist/storage-manager-C4jsO0Tp.d.ts +89 -0
  226. package/dist/storage-manager-D5KBXXKB.js +0 -0
  227. package/dist/{storage-types-DtIVejC1.d.ts → storage-types-DsDTCPbp.d.ts} +1 -1
  228. package/dist/{theo-cloud-3K4DGB3S.js → theo-cloud-RSQCBNXL.js} +4 -4
  229. package/dist/theo-cloud-RSQCBNXL.js.map +1 -0
  230. package/dist/upgrade-readiness-GUJBCJIS.js +0 -0
  231. package/dist/{vercel-MWW24ABC.js → vercel-TKPZK62A.js} +3 -3
  232. package/dist/vite-plugin/index.d.ts +3 -1
  233. package/dist/vite-plugin/index.js +20 -8
  234. package/dist/{vite-plugin-IK3NOWXS.js → vite-plugin-CR4JDFUQ.js} +9 -9
  235. package/dist/vite-plugin-CR4JDFUQ.js.map +1 -0
  236. package/package.json +59 -10
  237. package/LICENSE +0 -201
  238. package/dist/build-HIGHJQQV.js.map +0 -1
  239. package/dist/chunk-5OFPQK6N.js.map +0 -1
  240. package/dist/chunk-64JI5LTO.js.map +0 -1
  241. package/dist/chunk-7TOMTMHT.js.map +0 -1
  242. package/dist/chunk-BIGBFZSU.js.map +0 -1
  243. package/dist/chunk-C52GSJPF.js.map +0 -1
  244. package/dist/chunk-CZM6WWWS.js +0 -2450
  245. package/dist/chunk-CZM6WWWS.js.map +0 -1
  246. package/dist/chunk-JAAHOGKI.js.map +0 -1
  247. package/dist/chunk-KJVEJILQ.js.map +0 -1
  248. package/dist/chunk-O4BLVPML.js.map +0 -1
  249. package/dist/chunk-O7335ZGH.js.map +0 -1
  250. package/dist/chunk-PB4GR7EP.js.map +0 -1
  251. package/dist/chunk-TPCT3MXV.js.map +0 -1
  252. package/dist/chunk-VRHXOKRP.js.map +0 -1
  253. package/dist/chunk-WZ7CPVQB.js.map +0 -1
  254. package/dist/chunk-X4JAX2U3.js.map +0 -1
  255. package/dist/chunk-XP7YXRHO.js.map +0 -1
  256. package/dist/chunk-YLBSSEHX.js.map +0 -1
  257. package/dist/chunk-ZOXNJV2O.js.map +0 -1
  258. package/dist/dev-emit-RBSFZZNO.js.map +0 -1
  259. package/dist/dispatcher-E6QV32BO.js.map +0 -1
  260. package/dist/dist-5V77Z223.js.map +0 -1
  261. package/dist/generate-DT4IIAHF.js.map +0 -1
  262. package/dist/index-li83Swxa.d.ts +0 -506
  263. package/dist/lib-NL5JXGEB.js.map +0 -1
  264. package/dist/proper-lockfile-4Y3DP3RP.js.map +0 -1
  265. package/dist/registry-4MZ26TZD.js +0 -25
  266. package/dist/schema-CjVly9c_.d.ts +0 -274
  267. package/dist/sqlite-vec-ARPNUBWT.js.map +0 -1
  268. package/dist/start-HX3QUSOS.js.map +0 -1
  269. package/dist/theo-cloud-3K4DGB3S.js.map +0 -1
  270. /package/dist/{actions-virtual-module-HWNTIEPI.js.map → actions-virtual-module-IY46EEEX.js.map} +0 -0
  271. /package/dist/{actions-virtual-module-SEIPACG5.js.map → actions-virtual-module-XNHDNCZ6.js.map} +0 -0
  272. /package/dist/{aws-lambda-XYCGZH3I.js.map → aws-lambda-GKSTX6HD.js.map} +0 -0
  273. /package/dist/{bun-K73TQEWC.js.map → bun-ISHSNFIO.js.map} +0 -0
  274. /package/dist/{check-PZR67OY5.js.map → check-NXYPLM6M.js.map} +0 -0
  275. /package/dist/{dispatcher-AQJGI3HU.js.map → chunk-E3JH6YUM.js.map} +0 -0
  276. /package/dist/{node-3APTLGWB.js.map → chunk-JHEAWR3L.js.map} +0 -0
  277. /package/dist/{chunk-ZJW5FFYJ.js.map → chunk-LC5PYNJP.js.map} +0 -0
  278. /package/dist/{chunk-2VQKDRVF.js.map → chunk-MR7OLIC6.js.map} +0 -0
  279. /package/dist/{scan-C2BOKLSY.js.map → chunk-NPMCKOX4.js.map} +0 -0
  280. /package/dist/{chunk-B6RP57M3.js.map → chunk-OLPB36O2.js.map} +0 -0
  281. /package/dist/{chunk-OXIQI2XZ.js.map → chunk-VBZCVFSA.js.map} +0 -0
  282. /package/dist/{chunk-TRH2L3FI.js.map → chunk-WEGALZEU.js.map} +0 -0
  283. /package/dist/{cloudflare-LCAOTRYZ.js.map → cloudflare-OJGJ7R2D.js.map} +0 -0
  284. /package/dist/{schema-VR6YNVLQ.js.map → cookies-MJKMGJ7N.js.map} +0 -0
  285. /package/dist/{deno-deploy-ZN4RE5HF.js.map → deno-deploy-BHWKFTOW.js.map} +0 -0
  286. /package/dist/{dev-266MVCVA.js.map → dev-MJVSRZGF.js.map} +0 -0
  287. /package/dist/{dev-emit-NO6OZJOQ.js.map → dev-emit-HHP2XJXE.js.map} +0 -0
  288. /package/dist/{vite-plugin-IK3NOWXS.js.map → dispatcher-63LBXYLE.js.map} +0 -0
  289. /package/dist/{info-FJ4NXKTB.js.map → info-47VB62MV.js.map} +0 -0
  290. /package/dist/{netlify-5VQ22Z2P.js.map → netlify-GSNSJGMN.js.map} +0 -0
  291. /package/dist/{openapi-FNVSWZOL.js.map → openapi-NFLSNNVQ.js.map} +0 -0
  292. /package/dist/{registry-4MZ26TZD.js.map → registry-QHEZ3BWB.js.map} +0 -0
  293. /package/dist/{routes-H4SPLFHJ.js.map → routes-3A4XGIEJ.js.map} +0 -0
  294. /package/dist/{services-typed-client-XWYYBWGW.js.map → services-typed-client-KK6RHRDG.js.map} +0 -0
  295. /package/dist/{services-typed-client-ZX5JUHH3.js.map → services-typed-client-T7M5VPBP.js.map} +0 -0
  296. /package/dist/{static-TZBVBDTB.js.map → static-QI5NQT2X.js.map} +0 -0
  297. /package/dist/{vercel-MWW24ABC.js.map → vercel-TKPZK62A.js.map} +0 -0
@@ -76,6 +76,11 @@ function readNvmrcFloor(cwd) {
76
76
  return void 0;
77
77
  }
78
78
  }
79
+ function envFlagIsTruthy(value) {
80
+ if (value === void 0 || value === "") return false;
81
+ const v = value.toLowerCase();
82
+ return v !== "0" && v !== "false" && v !== "no";
83
+ }
79
84
  function preflightNodeAndBindings(cwd) {
80
85
  const nodeCheck = checkNodeFloor();
81
86
  if (!nodeCheck.ok) {
@@ -85,6 +90,7 @@ function preflightNodeAndBindings(cwd) {
85
90
  Your project pins Node ${pinned} in .nvmrc.` : "";
86
91
  throw new Error(`[theokit preflight] ${nodeCheck.reason}${suffix}`);
87
92
  }
93
+ if (envFlagIsTruthy(process.env.THEOKIT_SKIP_NATIVE_PREFLIGHT)) return;
88
94
  const abiCheck = checkBindingAbi(cwd);
89
95
  if (!abiCheck.ok) {
90
96
  throw new Error(`[theokit preflight] ${abiCheck.reason}`);
@@ -94,4 +100,4 @@ Your project pins Node ${pinned} in .nvmrc.` : "";
94
100
  export {
95
101
  preflightNodeAndBindings
96
102
  };
97
- //# sourceMappingURL=chunk-F3TG5FJV.js.map
103
+ //# sourceMappingURL=chunk-IEZCAT2I.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/preflight-node-version.ts"],"sourcesContent":["/**\n * Node version + native-binding ABI preflight for `theokit dev` / `theokit build` / `theokit start`.\n *\n * Why this exists:\n * In multi-Node-version setups (developer workflow: Node 22 for one\n * project, Node 20 for another, nvm switching frequently), the\n * `nohup env ...` invocation pattern strips the nvm PATH prefix and\n * the dev server launches under whichever `node` binary is FIRST in\n * PATH at spawn time — NOT the version the calling shell reported.\n *\n * When `theokit dev` boots under the wrong Node, every native binding\n * in `node_modules` (`better-sqlite3`, optionally `@lancedb/lancedb`,\n * etc.) fails with NODE_MODULE_VERSION mismatch on first use. The\n * error message references a low-level binary path; the dev wastes\n * time investigating a process-management issue.\n *\n * This preflight runs UPFRONT (before any module that touches native\n * bindings loads) and emits an actionable error referencing nvm + the\n * pinned `.nvmrc` floor.\n *\n * What it does:\n * 1. Reads `process.versions.node` + `process.versions.modules` (ABI).\n * 2. If Node major < 22, refuse to boot with a nvm install/use hint.\n * 3. If at least one native binding's compiled ABI != current ABI,\n * refuse to boot with a `pnpm rebuild <pkg>` hint.\n *\n * What it does NOT do:\n * * Auto-rebuild — that's the SDK's preflight (vitest setup, sentinel\n * cached). Auto-rebuild in a dev CLI is risky (slow, can run during\n * `pnpm dev` watch loops).\n * * Probe every native dep — only checks the ones the framework KNOWS\n * are required (currently `better-sqlite3` because of the cost\n * ledger + cron persistence + memory index DB).\n *\n * @internal\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { join } from 'node:path'\n\nconst NODE_MIN_MAJOR = 22\nconst NODE_MIN_FULL = '22.12.0'\n\ninterface BindingProbe {\n pkg: string\n /** path under <cwd>/node_modules — used only for the diagnostic message */\n hintPath?: string\n required: boolean\n}\n\nconst BINDINGS_TO_CHECK: readonly BindingProbe[] = [\n // better-sqlite3 is the only HARD-required native dep at framework boot\n // (cost ledger + cron persistence + memory index). Other native deps\n // (@lancedb/lancedb) are opt-in peers — preflight skips them when absent.\n { pkg: 'better-sqlite3', required: true },\n { pkg: '@lancedb/lancedb', required: false },\n]\n\nfunction checkNodeFloor(): { ok: true } | { ok: false; reason: string } {\n const major = Number.parseInt(process.versions.node.split('.')[0] ?? '0', 10)\n if (major >= NODE_MIN_MAJOR) return { ok: true }\n return {\n ok: false,\n reason:\n `theokit requires Node >= ${NODE_MIN_FULL} (you are running v${process.versions.node}).\\n\\n` +\n 'Quick fix:\\n' +\n ' nvm use 22 # switch to a Node 22.x already installed\\n' +\n \" nvm install 22.12 # install + switch if you don't have it\\n\\n\" +\n 'Why this matters:\\n' +\n ' theokit ships AsyncLocalStorage + native fetch + structured WHATWG\\n' +\n ' Streams optimizations that depend on Node 22 internals. Older Node\\n' +\n ' versions silently miscompile SSR streaming and break native\\n' +\n ' bindings (NODE_MODULE_VERSION mismatch).\\n\\n' +\n 'Production tip: pin your CI runner to `actions/setup-node` v4 with\\n' +\n \" `node-version-file: .nvmrc` — that's what `create-theokit` writes.\",\n }\n}\n\nfunction checkBindingAbi(cwd: string): { ok: true } | { ok: false; reason: string } {\n const offenders: string[] = []\n // Build createRequire from the cwd so we resolve the consumer's deps,\n // NOT theokit's own node_modules.\n const cwdRequire = createRequire(join(cwd, 'package.json'))\n for (const probe of BINDINGS_TO_CHECK) {\n try {\n cwdRequire.resolve(probe.pkg)\n } catch {\n // not installed — required vs optional gates the behaviour\n if (probe.required) {\n offenders.push(`${probe.pkg} (required dep is NOT installed — run \\`pnpm install\\`)`)\n }\n continue\n }\n // Try a dry require — better-sqlite3 dlopens at load-time, so the ABI\n // mismatch surfaces here as a sync throw.\n try {\n cwdRequire(probe.pkg)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n if (/NODE_MODULE_VERSION|was compiled against|did not self-register/.test(msg)) {\n offenders.push(`${probe.pkg} (compiled against different Node ABI)`)\n } else if (probe.required) {\n // surface non-ABI errors verbatim for required deps\n offenders.push(`${probe.pkg} (require failed: ${msg.split('\\n')[0]})`)\n }\n }\n }\n if (offenders.length === 0) return { ok: true }\n return {\n ok: false,\n reason:\n `Native binding ABI mismatch detected (Node v${process.versions.node}, ABI ${process.versions.modules}).\\n\\n` +\n 'Offending packages:\\n' +\n offenders.map((o) => ` - ${o}`).join('\\n') +\n '\\n\\nQuick fix:\\n' +\n ' pnpm rebuild # rebuild ALL native bindings for current Node\\n' +\n ' pnpm rebuild better-sqlite3 # rebuild just one\\n\\n' +\n 'Why this happened:\\n' +\n ' Your `theokit dev` (or build/start) launched under a different\\n' +\n ' Node version than the one used to install/build node_modules.\\n' +\n ' Common cause: `nohup env ...` strips the nvm PATH prefix, so the\\n' +\n ' dev server picked the system `node` instead of `nvm use 22`.\\n\\n' +\n 'Permanent fix:\\n' +\n ' Prefix your dev/start command with the absolute Node 22 path:\\n' +\n ' PATH=\"$(nvm which 22 | xargs dirname):$PATH\" theokit dev\\n' +\n ' Or use direnv with `.envrc` containing `use node 22.12.0`.',\n }\n}\n\n/**\n * Read `.nvmrc` from cwd if present — used only to enrich error messages\n * with the project's pinned floor. Falls back to the framework default.\n *\n * @internal\n */\nfunction readNvmrcFloor(cwd: string): string | undefined {\n const path = join(cwd, '.nvmrc')\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI reads consumer's own .nvmrc; consumer owns cwd\n if (!existsSync(path)) return undefined\n try {\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- same\n return readFileSync(path, 'utf8').trim()\n } catch {\n return undefined\n }\n}\n\n/**\n * Run the Node + ABI preflight. Throws a descriptive Error on mismatch\n * BEFORE any module that imports better-sqlite3 loads. The error message\n * is the only user-facing artefact — caller can rely on `error.message`\n * being a full actionable diagnostic.\n *\n * @throws Error with multi-line actionable message when Node version or\n * native binding ABI is wrong.\n * @internal\n */\nexport function preflightNodeAndBindings(cwd: string): void {\n const nodeCheck = checkNodeFloor()\n if (!nodeCheck.ok) {\n const pinned = readNvmrcFloor(cwd)\n const suffix = pinned !== undefined ? `\\n\\nYour project pins Node ${pinned} in .nvmrc.` : ''\n throw new Error(`[theokit preflight] ${nodeCheck.reason}${suffix}`)\n }\n const abiCheck = checkBindingAbi(cwd)\n if (!abiCheck.ok) {\n throw new Error(`[theokit preflight] ${abiCheck.reason}`)\n }\n}\n"],"mappings":";;;;AAqCA,SAAS,YAAY,oBAAoB;AACzC,SAAS,qBAAqB;AAC9B,SAAS,YAAY;AAErB,IAAM,iBAAiB;AACvB,IAAM,gBAAgB;AAStB,IAAM,oBAA6C;AAAA;AAAA;AAAA;AAAA,EAIjD,EAAE,KAAK,kBAAkB,UAAU,KAAK;AAAA,EACxC,EAAE,KAAK,oBAAoB,UAAU,MAAM;AAC7C;AAEA,SAAS,iBAA+D;AACtE,QAAM,QAAQ,OAAO,SAAS,QAAQ,SAAS,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,KAAK,EAAE;AAC5E,MAAI,SAAS,eAAgB,QAAO,EAAE,IAAI,KAAK;AAC/C,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,QACE,4BAA4B,aAAa,sBAAsB,QAAQ,SAAS,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWxF;AACF;AAEA,SAAS,gBAAgB,KAA2D;AAClF,QAAM,YAAsB,CAAC;AAG7B,QAAM,aAAa,cAAc,KAAK,KAAK,cAAc,CAAC;AAC1D,aAAW,SAAS,mBAAmB;AACrC,QAAI;AACF,iBAAW,QAAQ,MAAM,GAAG;AAAA,IAC9B,QAAQ;AAEN,UAAI,MAAM,UAAU;AAClB,kBAAU,KAAK,GAAG,MAAM,GAAG,8DAAyD;AAAA,MACtF;AACA;AAAA,IACF;AAGA,QAAI;AACF,iBAAW,MAAM,GAAG;AAAA,IACtB,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAI,iEAAiE,KAAK,GAAG,GAAG;AAC9E,kBAAU,KAAK,GAAG,MAAM,GAAG,wCAAwC;AAAA,MACrE,WAAW,MAAM,UAAU;AAEzB,kBAAU,KAAK,GAAG,MAAM,GAAG,qBAAqB,IAAI,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG;AAAA,MACvE;AAAA,IACF;AAAA,EACF;AACA,MAAI,UAAU,WAAW,EAAG,QAAO,EAAE,IAAI,KAAK;AAC9C,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,QACE,+CAA+C,QAAQ,SAAS,IAAI,SAAS,QAAQ,SAAS,OAAO;AAAA;AAAA;AAAA,IAErG,UAAU,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI,IAC1C;AAAA,EAYJ;AACF;AAQA,SAAS,eAAe,KAAiC;AACvD,QAAM,OAAO,KAAK,KAAK,QAAQ;AAE/B,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AAEF,WAAO,aAAa,MAAM,MAAM,EAAE,KAAK;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYO,SAAS,yBAAyB,KAAmB;AAC1D,QAAM,YAAY,eAAe;AACjC,MAAI,CAAC,UAAU,IAAI;AACjB,UAAM,SAAS,eAAe,GAAG;AACjC,UAAM,SAAS,WAAW,SAAY;AAAA;AAAA,yBAA8B,MAAM,gBAAgB;AAC1F,UAAM,IAAI,MAAM,uBAAuB,UAAU,MAAM,GAAG,MAAM,EAAE;AAAA,EACpE;AACA,QAAM,WAAW,gBAAgB,GAAG;AACpC,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,uBAAuB,SAAS,MAAM,EAAE;AAAA,EAC1D;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/cli/preflight-node-version.ts"],"sourcesContent":["/**\n * Node version + native-binding ABI preflight for `theokit dev` / `theokit build` / `theokit start`.\n *\n * Why this exists:\n * In multi-Node-version setups (developer workflow: Node 22 for one\n * project, Node 20 for another, nvm switching frequently), the\n * `nohup env ...` invocation pattern strips the nvm PATH prefix and\n * the dev server launches under whichever `node` binary is FIRST in\n * PATH at spawn time — NOT the version the calling shell reported.\n *\n * When `theokit dev` boots under the wrong Node, every native binding\n * in `node_modules` (`better-sqlite3`, optionally `@lancedb/lancedb`,\n * etc.) fails with NODE_MODULE_VERSION mismatch on first use. The\n * error message references a low-level binary path; the dev wastes\n * time investigating a process-management issue.\n *\n * This preflight runs UPFRONT (before any module that touches native\n * bindings loads) and emits an actionable error referencing nvm + the\n * pinned `.nvmrc` floor.\n *\n * What it does:\n * 1. Reads `process.versions.node` + `process.versions.modules` (ABI).\n * 2. If Node major < 22, refuse to boot with a nvm install/use hint.\n * 3. If at least one native binding's compiled ABI != current ABI,\n * refuse to boot with a `pnpm rebuild <pkg>` hint.\n *\n * What it does NOT do:\n * * Auto-rebuild — that's the SDK's preflight (vitest setup, sentinel\n * cached). Auto-rebuild in a dev CLI is risky (slow, can run during\n * `pnpm dev` watch loops).\n * * Probe every native dep — only checks the ones the framework KNOWS\n * are required (currently `better-sqlite3` because of the cost\n * ledger + cron persistence + memory index DB).\n *\n * @internal\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { join } from 'node:path'\n\nconst NODE_MIN_MAJOR = 22\nconst NODE_MIN_FULL = '22.12.0'\n\ninterface BindingProbe {\n pkg: string\n /** path under <cwd>/node_modules — used only for the diagnostic message */\n hintPath?: string\n required: boolean\n}\n\nconst BINDINGS_TO_CHECK: readonly BindingProbe[] = [\n // better-sqlite3 is the only HARD-required native dep at framework boot\n // (cost ledger + cron persistence + memory index). Other native deps\n // (@lancedb/lancedb) are opt-in peers — preflight skips them when absent.\n { pkg: 'better-sqlite3', required: true },\n { pkg: '@lancedb/lancedb', required: false },\n]\n\nfunction checkNodeFloor(): { ok: true } | { ok: false; reason: string } {\n const major = Number.parseInt(process.versions.node.split('.')[0] ?? '0', 10)\n if (major >= NODE_MIN_MAJOR) return { ok: true }\n return {\n ok: false,\n reason:\n `theokit requires Node >= ${NODE_MIN_FULL} (you are running v${process.versions.node}).\\n\\n` +\n 'Quick fix:\\n' +\n ' nvm use 22 # switch to a Node 22.x already installed\\n' +\n \" nvm install 22.12 # install + switch if you don't have it\\n\\n\" +\n 'Why this matters:\\n' +\n ' theokit ships AsyncLocalStorage + native fetch + structured WHATWG\\n' +\n ' Streams optimizations that depend on Node 22 internals. Older Node\\n' +\n ' versions silently miscompile SSR streaming and break native\\n' +\n ' bindings (NODE_MODULE_VERSION mismatch).\\n\\n' +\n 'Production tip: pin your CI runner to `actions/setup-node` v4 with\\n' +\n \" `node-version-file: .nvmrc` — that's what `create-theokit` writes.\",\n }\n}\n\nfunction checkBindingAbi(cwd: string): { ok: true } | { ok: false; reason: string } {\n const offenders: string[] = []\n // Build createRequire from the cwd so we resolve the consumer's deps,\n // NOT theokit's own node_modules.\n const cwdRequire = createRequire(join(cwd, 'package.json'))\n for (const probe of BINDINGS_TO_CHECK) {\n try {\n cwdRequire.resolve(probe.pkg)\n } catch {\n // not installed — required vs optional gates the behaviour\n if (probe.required) {\n offenders.push(`${probe.pkg} (required dep is NOT installed — run \\`pnpm install\\`)`)\n }\n continue\n }\n // Try a dry require — better-sqlite3 dlopens at load-time, so the ABI\n // mismatch surfaces here as a sync throw.\n try {\n cwdRequire(probe.pkg)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n if (/NODE_MODULE_VERSION|was compiled against|did not self-register/.test(msg)) {\n offenders.push(`${probe.pkg} (compiled against different Node ABI)`)\n } else if (probe.required) {\n // surface non-ABI errors verbatim for required deps\n offenders.push(`${probe.pkg} (require failed: ${msg.split('\\n')[0]})`)\n }\n }\n }\n if (offenders.length === 0) return { ok: true }\n return {\n ok: false,\n reason:\n `Native binding ABI mismatch detected (Node v${process.versions.node}, ABI ${process.versions.modules}).\\n\\n` +\n 'Offending packages:\\n' +\n offenders.map((o) => ` - ${o}`).join('\\n') +\n '\\n\\nQuick fix:\\n' +\n ' pnpm rebuild # rebuild ALL native bindings for current Node\\n' +\n ' pnpm rebuild better-sqlite3 # rebuild just one\\n\\n' +\n 'Why this happened:\\n' +\n ' Your `theokit dev` (or build/start) launched under a different\\n' +\n ' Node version than the one used to install/build node_modules.\\n' +\n ' Common cause: `nohup env ...` strips the nvm PATH prefix, so the\\n' +\n ' dev server picked the system `node` instead of `nvm use 22`.\\n\\n' +\n 'Permanent fix:\\n' +\n ' Prefix your dev/start command with the absolute Node 22 path:\\n' +\n ' PATH=\"$(nvm which 22 | xargs dirname):$PATH\" theokit dev\\n' +\n ' Or use direnv with `.envrc` containing `use node 22.12.0`.',\n }\n}\n\n/**\n * Read `.nvmrc` from cwd if present — used only to enrich error messages\n * with the project's pinned floor. Falls back to the framework default.\n *\n * @internal\n */\nfunction readNvmrcFloor(cwd: string): string | undefined {\n const path = join(cwd, '.nvmrc')\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI reads consumer's own .nvmrc; consumer owns cwd\n if (!existsSync(path)) return undefined\n try {\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- same\n return readFileSync(path, 'utf8').trim()\n } catch {\n return undefined\n }\n}\n\n/**\n * Coerce common truthy env-var values to boolean. Every explicit truthy\n * string activates (\"1\", \"true\", \"yes\"); empty/undefined/\"0\"/\"false\" → off.\n *\n * @internal\n */\nfunction envFlagIsTruthy(value: string | undefined): boolean {\n if (value === undefined || value === '') return false\n const v = value.toLowerCase()\n return v !== '0' && v !== 'false' && v !== 'no'\n}\n\n/**\n * Run the Node + ABI preflight. Throws a descriptive Error on mismatch\n * BEFORE any module that imports better-sqlite3 loads. The error message\n * is the only user-facing artefact — caller can rely on `error.message`\n * being a full actionable diagnostic.\n *\n * Escape hatches (env-var; honor production-grade observability):\n * - `THEOKIT_SKIP_NATIVE_PREFLIGHT=1` — skip the ABI/native-binding check\n * entirely (still enforces Node-floor version). Test fixtures + cleanroom\n * consumer envs that don't actually use better-sqlite3 (no audit-log, no\n * LanceDB embedder, etc.) can opt out. Documented as a Phase 6 readiness\n * fix in docs/plans/t5a2-incoming-message-to-request-shape-refactor-plan.md\n * § Test infrastructure prerequisites (Option B).\n *\n * @throws Error with multi-line actionable message when Node version or\n * native binding ABI is wrong.\n * @internal\n */\nexport function preflightNodeAndBindings(cwd: string): void {\n const nodeCheck = checkNodeFloor()\n if (!nodeCheck.ok) {\n const pinned = readNvmrcFloor(cwd)\n const suffix = pinned !== undefined ? `\\n\\nYour project pins Node ${pinned} in .nvmrc.` : ''\n throw new Error(`[theokit preflight] ${nodeCheck.reason}${suffix}`)\n }\n // T5a.2 prerequisite: env-var escape hatch for native-binding ABI check.\n // Node-floor check above stays enforced unconditionally.\n if (envFlagIsTruthy(process.env.THEOKIT_SKIP_NATIVE_PREFLIGHT)) return\n const abiCheck = checkBindingAbi(cwd)\n if (!abiCheck.ok) {\n throw new Error(`[theokit preflight] ${abiCheck.reason}`)\n }\n}\n"],"mappings":";;;;AAqCA,SAAS,YAAY,oBAAoB;AACzC,SAAS,qBAAqB;AAC9B,SAAS,YAAY;AAErB,IAAM,iBAAiB;AACvB,IAAM,gBAAgB;AAStB,IAAM,oBAA6C;AAAA;AAAA;AAAA;AAAA,EAIjD,EAAE,KAAK,kBAAkB,UAAU,KAAK;AAAA,EACxC,EAAE,KAAK,oBAAoB,UAAU,MAAM;AAC7C;AAEA,SAAS,iBAA+D;AACtE,QAAM,QAAQ,OAAO,SAAS,QAAQ,SAAS,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,KAAK,EAAE;AAC5E,MAAI,SAAS,eAAgB,QAAO,EAAE,IAAI,KAAK;AAC/C,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,QACE,4BAA4B,aAAa,sBAAsB,QAAQ,SAAS,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWxF;AACF;AAEA,SAAS,gBAAgB,KAA2D;AAClF,QAAM,YAAsB,CAAC;AAG7B,QAAM,aAAa,cAAc,KAAK,KAAK,cAAc,CAAC;AAC1D,aAAW,SAAS,mBAAmB;AACrC,QAAI;AACF,iBAAW,QAAQ,MAAM,GAAG;AAAA,IAC9B,QAAQ;AAEN,UAAI,MAAM,UAAU;AAClB,kBAAU,KAAK,GAAG,MAAM,GAAG,8DAAyD;AAAA,MACtF;AACA;AAAA,IACF;AAGA,QAAI;AACF,iBAAW,MAAM,GAAG;AAAA,IACtB,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAI,iEAAiE,KAAK,GAAG,GAAG;AAC9E,kBAAU,KAAK,GAAG,MAAM,GAAG,wCAAwC;AAAA,MACrE,WAAW,MAAM,UAAU;AAEzB,kBAAU,KAAK,GAAG,MAAM,GAAG,qBAAqB,IAAI,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG;AAAA,MACvE;AAAA,IACF;AAAA,EACF;AACA,MAAI,UAAU,WAAW,EAAG,QAAO,EAAE,IAAI,KAAK;AAC9C,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,QACE,+CAA+C,QAAQ,SAAS,IAAI,SAAS,QAAQ,SAAS,OAAO;AAAA;AAAA;AAAA,IAErG,UAAU,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI,IAC1C;AAAA,EAYJ;AACF;AAQA,SAAS,eAAe,KAAiC;AACvD,QAAM,OAAO,KAAK,KAAK,QAAQ;AAE/B,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AAEF,WAAO,aAAa,MAAM,MAAM,EAAE,KAAK;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQA,SAAS,gBAAgB,OAAoC;AAC3D,MAAI,UAAU,UAAa,UAAU,GAAI,QAAO;AAChD,QAAM,IAAI,MAAM,YAAY;AAC5B,SAAO,MAAM,OAAO,MAAM,WAAW,MAAM;AAC7C;AAoBO,SAAS,yBAAyB,KAAmB;AAC1D,QAAM,YAAY,eAAe;AACjC,MAAI,CAAC,UAAU,IAAI;AACjB,UAAM,SAAS,eAAe,GAAG;AACjC,UAAM,SAAS,WAAW,SAAY;AAAA;AAAA,yBAA8B,MAAM,gBAAgB;AAC1F,UAAM,IAAI,MAAM,uBAAuB,UAAU,MAAM,GAAG,MAAM,EAAE;AAAA,EACpE;AAGA,MAAI,gBAAgB,QAAQ,IAAI,6BAA6B,EAAG;AAChE,QAAM,WAAW,gBAAgB,GAAG;AACpC,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,uBAAuB,SAAS,MAAM,EAAE;AAAA,EAC1D;AACF;","names":[]}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=chunk-JHEAWR3L.js.map
@@ -0,0 +1,17 @@
1
+ // src/server/scan/module-loader.ts
2
+ import { pathToFileURL } from "url";
3
+ function createViteLoader(vite) {
4
+ return (path) => vite.ssrLoadModule(path);
5
+ }
6
+ function createProductionLoader() {
7
+ return async (path) => {
8
+ const url = pathToFileURL(path).href;
9
+ return import(url);
10
+ };
11
+ }
12
+
13
+ export {
14
+ createViteLoader,
15
+ createProductionLoader
16
+ };
17
+ //# sourceMappingURL=chunk-JQSKBMXP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/scan/module-loader.ts"],"sourcesContent":["import { pathToFileURL } from 'node:url'\n\nimport type { ViteDevServer } from 'vite'\n\nexport type LoadModule = (path: string) => Promise<Record<string, unknown>>\n\nexport function createViteLoader(vite: ViteDevServer): LoadModule {\n return (path) => vite.ssrLoadModule(path) as Promise<Record<string, unknown>>\n}\n\nexport function createProductionLoader(): LoadModule {\n return async (path) => {\n const url = pathToFileURL(path).href\n return import(url) as Promise<Record<string, unknown>>\n }\n}\n"],"mappings":";AAAA,SAAS,qBAAqB;AAMvB,SAAS,iBAAiB,MAAiC;AAChE,SAAO,CAAC,SAAS,KAAK,cAAc,IAAI;AAC1C;AAEO,SAAS,yBAAqC;AACnD,SAAO,OAAO,SAAS;AACrB,UAAM,MAAM,cAAc,IAAI,EAAE;AAChC,WAAO,OAAO;AAAA,EAChB;AACF;","names":[]}
@@ -1,7 +1,12 @@
1
1
  import {
2
- generateNewTraceContext,
3
2
  writeAtomic
4
- } from "./chunk-YLBSSEHX.js";
3
+ } from "./chunk-Y4H3JNVK.js";
4
+ import {
5
+ generateNewTraceContext
6
+ } from "./chunk-6NEXVBPY.js";
7
+ import {
8
+ importUserModule
9
+ } from "./chunk-7MQOHNHE.js";
5
10
  import {
6
11
  walkSourceFiles
7
12
  } from "./chunk-X2VVCJ4V.js";
@@ -302,7 +307,6 @@ function createCronScheduler(definitions) {
302
307
  // src/server/cron/cron-scan.ts
303
308
  import { existsSync as existsSync2 } from "fs";
304
309
  import { basename } from "path";
305
- import { pathToFileURL } from "url";
306
310
  var DuplicateCronNameError = class extends Error {
307
311
  constructor(cronName, filePaths) {
308
312
  super(
@@ -334,7 +338,7 @@ async function scanCrons(cronsDir) {
334
338
  for (const filePath of filePaths) {
335
339
  let mod;
336
340
  try {
337
- mod = await import(pathToFileURL(filePath).href);
341
+ mod = await importUserModule(filePath);
338
342
  } catch (err) {
339
343
  const reason = err instanceof Error ? err.message : String(err);
340
344
  throw new Error(`Failed to import cron file "${filePath}": ${reason}`);
@@ -382,4 +386,4 @@ export {
382
386
  DuplicateCronNameError,
383
387
  scanCrons
384
388
  };
385
- //# sourceMappingURL=chunk-4QTKSLTO.js.map
389
+ //# sourceMappingURL=chunk-K4M64WD6.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/cron/cron-validate.ts","../src/server/cron/define-cron.ts","../src/server/cron/cron-manifest.ts","../src/server/cron/adapter-translators.ts","../src/server/cron/cron-runtime-node.ts","../src/server/cron/cron-scan.ts"],"sourcesContent":["import { CronExpressionParser } from 'cron-parser'\n\n/**\n * Validate a cron schedule string per ADR-0004 — 5-field UTC strict.\n *\n * Accepts:\n * - Standard 5-field expressions: `minute hour dayOfMonth month dayOfWeek`\n * - Step (`*\\/15`), range (`1-5`), list (`MON,TUE,FRI`), wildcards\n *\n * Rejects:\n * - 6-field with seconds (`* * * * * *`)\n * - 7-field with year\n * - Shorthand (`@daily`, `@hourly`, `@yearly`, `@reboot`)\n * - Timezone suffix\n * - Empty / whitespace-only\n * - Malformed grammar\n *\n * Throws on every invalid input. Every error message includes the\n * original input and the fix.\n *\n * @see docs/adr/0004-cron-schedule-5-field-utc-strict.md\n */\nexport function validateCronSchedule(schedule: string): void {\n if (typeof schedule !== 'string') {\n throw new TypeError(`Invalid cron schedule: expected string, got ${typeof schedule}.`)\n }\n const trimmed = schedule.trim()\n if (trimmed.length === 0) {\n throw new Error(\n 'Invalid cron schedule: empty string. TheoKit cron uses 5-field UTC strict format, e.g. \"0 9 * * *\".',\n )\n }\n\n // Shorthand check BEFORE field counting — `@daily` is one \"field\" but\n // semantically not in the 5-field grammar.\n if (trimmed.startsWith('@')) {\n throw new Error(\n `Invalid cron schedule \"${schedule}\": shorthand not supported. ` +\n 'Use the equivalent 5-field form, e.g. \"@daily\" → \"0 0 * * *\", \"@hourly\" → \"0 * * * *\".',\n )\n }\n\n const parts = trimmed.split(/\\s+/)\n if (parts.length !== 5) {\n throw new Error(\n `Invalid cron schedule \"${schedule}\": expected 5 fields ` +\n '(\"minute hour dayOfMonth month dayOfWeek\"), ' +\n `got ${parts.length}. TheoKit treats all schedules as UTC; ` +\n 'for second-precision or timezone, see docs/concepts/crons.md.',\n )\n }\n\n // Final grammar validation via cron-parser. We pass `tz: 'UTC'`\n // explicitly so any embedded timezone hint surfaces as an error.\n try {\n CronExpressionParser.parse(trimmed, { tz: 'UTC' })\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err)\n throw new Error(`Invalid cron schedule \"${schedule}\": ${reason}`)\n }\n}\n","import type { CronDefinition, CronOptions } from './cron-types.js'\nimport { validateCronSchedule } from './cron-validate.js'\n\nconst NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/\n\nfunction validateName(name: string): void {\n if (typeof name !== 'string' || !NAME_RE.test(name)) {\n throw new Error(\n `defineCron: invalid name \"${name}\". ` +\n 'Must be 1-64 chars, lowercase alphanumeric + hyphen, starting with [a-z0-9].',\n )\n }\n}\n\n/**\n * Declare a time-triggered handler. Pure identity helper — no\n * registration side effect; the build-time scanner (T1.3) discovers\n * definitions by walking `server/crons/` and emits a manifest the\n * adapters translate at deploy.\n *\n * @example\n * ```ts\n * // server/crons/morning-summary.ts\n * export default defineCron('morning-summary', {\n * schedule: '0 9 * * *', // 09:00 UTC\n * async handler({ traceId, scheduledAt, signal }) {\n * // ...\n * },\n * })\n * ```\n */\nexport function defineCron(name: string, options: CronOptions): CronDefinition {\n validateName(name)\n validateCronSchedule(options.schedule)\n return {\n name,\n schedule: options.schedule,\n handler: options.handler,\n concurrency: options.concurrency ?? 'forbid',\n }\n}\n","import { writeAtomic } from '../_internal/atomic-write.js'\n\nimport type { CronNode } from './cron-scan.js'\n\nexport const CRON_MANIFEST_SCHEMA_VERSION = 1 as const\n\nexport interface CronManifestEntry {\n readonly name: string\n readonly filePath: string\n readonly schedule: string\n readonly concurrency: 'forbid' | 'allow'\n}\n\nexport interface CronManifest {\n readonly schemaVersion: typeof CRON_MANIFEST_SCHEMA_VERSION\n readonly generatedAt: string\n readonly crons: readonly CronManifestEntry[]\n}\n\n/**\n * Build a `CronManifest` from scanned `CronNode[]`. Filepaths are kept\n * relative to the caller's project root for portability.\n */\nexport function buildCronManifest(nodes: readonly CronNode[], projectRoot?: string): CronManifest {\n const crons: CronManifestEntry[] = nodes.map((n) => ({\n name: n.name,\n filePath: projectRoot ? relativize(n.filePath, projectRoot) : n.filePath,\n schedule: n.schedule,\n concurrency: n.concurrency,\n }))\n return {\n schemaVersion: CRON_MANIFEST_SCHEMA_VERSION,\n generatedAt: new Date().toISOString(),\n crons,\n }\n}\n\n/**\n * Write the cron manifest to `path` atomically (EC-106).\n *\n * Accepts either a pre-built `CronManifest` OR an array of `CronNode[]`\n * (which gets converted via `buildCronManifest`). The atomic-write\n * helper guarantees `path` always contains valid JSON, even under\n * concurrent writes (e.g., dev-server rescan + build).\n */\nexport function writeCronManifest(\n path: string,\n input: readonly CronNode[] | CronManifest,\n projectRoot?: string,\n): void {\n const manifest: CronManifest = isManifest(input) ? input : buildCronManifest(input, projectRoot)\n writeAtomic(path, JSON.stringify(manifest, null, 2))\n}\n\nfunction isManifest(value: unknown): value is CronManifest {\n return typeof value === 'object' && value !== null && 'schemaVersion' in value && 'crons' in value\n}\n\nfunction relativize(absPath: string, root: string): string {\n if (absPath.startsWith(root)) {\n const trimmed = absPath.slice(root.length)\n return trimmed.startsWith('/') ? trimmed.slice(1) : trimmed\n }\n return absPath\n}\n","/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time adapter translators: caller-controlled paths only.\n */\nimport { existsSync, readFileSync } from 'node:fs'\n\nimport { writeAtomic } from '../_internal/atomic-write.js'\n\nimport type { CronManifestEntry } from './cron-manifest.js'\n\n/**\n * Thrown when an existing platform-config file (vercel.json, wrangler.toml,\n * serverless.yml) cannot be parsed. Caller must fix the file before\n * re-running `theokit build`. We NEVER silently overwrite a user's config.\n */\nexport class ExistingConfigUnparseableError extends Error {\n readonly code = 'EXISTING_CONFIG_UNPARSEABLE'\n constructor(\n public readonly filePath: string,\n public readonly parseError: string,\n ) {\n super(\n `Existing config \"${filePath}\" could not be parsed: ${parseError}. ` +\n 'Fix or remove the file before re-running build — TheoKit never silently overwrites user configuration.',\n )\n this.name = 'ExistingConfigUnparseableError'\n }\n}\n\n// ──────────────────────────────────────────────────────────\n// Vercel — vercel.json crons[]\n// ──────────────────────────────────────────────────────────\n\ninterface VercelJson {\n crons?: { path: string; schedule: string }[]\n [key: string]: unknown\n}\n\n/**\n * Translate a TheoKit cron manifest into `vercel.json crons[]`.\n *\n * EC-105: existing fields (functions, headers, redirects, rewrites, env,\n * etc.) are preserved verbatim — ONLY the `crons[]` slice is replaced.\n */\nexport function translateCronToVercel(\n vercelJsonPath: string,\n crons: readonly CronManifestEntry[],\n): void {\n let existing: VercelJson = {}\n if (existsSync(vercelJsonPath)) {\n const raw = readFileSync(vercelJsonPath, 'utf8')\n try {\n existing = JSON.parse(raw) as VercelJson\n } catch (err) {\n throw new ExistingConfigUnparseableError(\n vercelJsonPath,\n err instanceof Error ? err.message : String(err),\n )\n }\n }\n const merged: VercelJson = {\n ...existing,\n crons: crons.map((c) => ({\n path: `/api/__crons/${c.name}`,\n schedule: c.schedule,\n })),\n }\n writeAtomic(vercelJsonPath, JSON.stringify(merged, null, 2))\n}\n\n// ──────────────────────────────────────────────────────────\n// Cloudflare — wrangler.toml [triggers] crons\n// ──────────────────────────────────────────────────────────\n\n/**\n * Translate a TheoKit cron manifest into `wrangler.toml [triggers] crons`.\n *\n * EC-105: regex-based mutation that preserves comments + other sections\n * verbatim. Replaces only the `[triggers]` block (or appends if absent).\n */\nexport function translateCronToCloudflare(\n wranglerTomlPath: string,\n crons: readonly CronManifestEntry[],\n): void {\n const schedules = crons.map((c) => `\"${c.schedule}\"`).join(', ')\n const triggersBlock = `[triggers]\\ncrons = [${schedules}]\\n`\n\n if (!existsSync(wranglerTomlPath)) {\n writeAtomic(wranglerTomlPath, triggersBlock)\n return\n }\n\n const existing = readFileSync(wranglerTomlPath, 'utf8')\n\n // Replace existing [triggers] section if present. Strategy: split into\n // line array, find the [triggers] header line, drop subsequent lines\n // until the next section header (line starting with `[`) or EOF.\n const lines = existing.split('\\n')\n const triggersIdx = lines.findIndex((l) => l.trim() === '[triggers]')\n\n let next: string\n if (triggersIdx !== -1) {\n let endIdx = lines.length\n for (let i = triggersIdx + 1; i < lines.length; i++) {\n if (lines[i].trim().startsWith('[')) {\n endIdx = i\n break\n }\n }\n const before = lines.slice(0, triggersIdx).join('\\n')\n const after = lines.slice(endIdx).join('\\n')\n next = `${before}\\n${triggersBlock.trimEnd()}\\n${after}`.replace(/\\n{3,}/g, '\\n\\n')\n } else {\n next = existing.endsWith('\\n')\n ? `${existing}\\n${triggersBlock}`\n : `${existing}\\n\\n${triggersBlock}`\n }\n writeAtomic(wranglerTomlPath, next)\n}\n\n// ──────────────────────────────────────────────────────────\n// AWS Lambda — serverless.yml functions.<fn>.events: schedule\n// ──────────────────────────────────────────────────────────\n\n/**\n * Convert TheoKit's 5-field UTC cron to AWS EventBridge's 6-field format.\n * EventBridge requires `?` in EITHER day-of-month OR day-of-week (not both `*`).\n *\n * Algorithm:\n * - If DOM is \"*\" and DOW is \"*\" → insert ? in DOW (default)\n * - If DOM is \"*\" and DOW is specific → insert ? in DOM\n * - If DOM is specific and DOW is \"*\" → insert ? in DOW\n * - Append \"*\" year field at end.\n */\nexport function convertToAwsCron(schedule: string): string {\n const parts = schedule.trim().split(/\\s+/)\n if (parts.length !== 5) {\n throw new Error(`Invalid 5-field schedule for AWS conversion: \"${schedule}\"`)\n }\n const [minute, hour, dom, month, dow] = parts\n let awsDom = dom\n let awsDow = dow\n if (dom === '*' && dow === '*') {\n awsDow = '?'\n } else if (dom === '*') {\n awsDom = '?'\n } else if (dow === '*') {\n awsDow = '?'\n }\n return `cron(${minute} ${hour} ${awsDom} ${month} ${awsDow} *)`\n}\n\n/**\n * Translate a TheoKit cron manifest into a `serverless.yml` functions\n * map with `events: - schedule: cron(...)` entries.\n *\n * EC-105: appends to `functions:` block, preserving all existing\n * functions/sections. Cron functions are named `cron_<name>` to avoid\n * collision with user-declared functions.\n */\nexport function translateCronToAws(\n serverlessYmlPath: string,\n crons: readonly CronManifestEntry[],\n): void {\n const cronFunctionsYaml = crons\n .map(\n (c) => ` cron_${c.name}:\n handler: .theo/server/crons/${c.name}.handler\n events:\n - schedule: ${convertToAwsCron(c.schedule)}`,\n )\n .join('\\n')\n\n const block = `\\nfunctions:\\n${cronFunctionsYaml}\\n`\n\n if (!existsSync(serverlessYmlPath)) {\n // Minimal serverless.yml scaffold\n writeAtomic(\n serverlessYmlPath,\n `service: theokit-app\\nprovider:\\n name: aws\\n runtime: nodejs22.x\\n${block}`,\n )\n return\n }\n\n const existing = readFileSync(serverlessYmlPath, 'utf8')\n\n // Look for an existing top-level `functions:` block; if present, append our\n // cron entries under it (don't replace user-declared functions).\n const functionsRe = /^functions:\\s*\\n/m\n let next: string\n if (functionsRe.test(existing)) {\n next = existing.replace(functionsRe, (match) => `${match}${cronFunctionsYaml}\\n`)\n } else {\n next = existing.endsWith('\\n') ? `${existing}${block}` : `${existing}\\n${block}`\n }\n writeAtomic(serverlessYmlPath, next)\n}\n\n// ──────────────────────────────────────────────────────────\n// Deno Deploy — Deno.cron registrations\n// ──────────────────────────────────────────────────────────\n\n/**\n * Emit a Deno entry file that registers each cron via `Deno.cron`.\n *\n * Unlike Vercel/CF/AWS, Deno.cron is an in-process runtime API — the\n * entry file is a managed artifact (overwritten each build), not a user\n * config (so EC-105 doesn't apply here).\n */\nexport function translateCronToDeno(entryPath: string, crons: readonly CronManifestEntry[]): void {\n const lines = crons.map(\n (c) =>\n `Deno.cron(\"${c.name}\", \"${c.schedule}\", async () => {\n const mod = await import(\"../${c.filePath}\");\n const def = mod.default;\n await def.handler({\n traceId: crypto.randomUUID().replace(/-/g, \"\"),\n scheduledAt: new Date(),\n signal: AbortSignal.timeout(60_000),\n });\n});`,\n )\n const content = `// AUTOGENERATED by TheoKit cron build — DO NOT EDIT.\n// Source: server/crons/*.ts → .theo/crons.json → this file.\n${lines.join('\\n\\n')}\n`\n writeAtomic(entryPath, content)\n}\n","import { CronExpressionParser } from 'cron-parser'\n\nimport { generateNewTraceContext } from '../observability/trace-context-propagation.js'\n\nimport type { CronContext, CronDefinition } from './cron-types.js'\n\n/**\n * In-memory cron scheduler for `theokit dev` (T1.4).\n *\n * Algorithm:\n * - For each cron, compute `nextFireAt = cron-parser.next()`.\n * - Schedule a `setTimeout(handler, nextFireAt - now)`.\n * - After handler invocation (sync return or Promise scheduled), recompute\n * next fire from CURRENT time (drift-free vs scheduled time).\n *\n * Per-cron isolation (EC-109):\n * - Each cron's handler invocation is fire-and-forget (`void` scheduled).\n * A hanging handler does NOT block the scheduler loop nor other crons.\n * - `concurrency: 'forbid'` (default) tracks an in-flight flag per-cron;\n * subsequent ticks skip + warn while the in-flight flag is set.\n * - `concurrency: 'allow'` runs handlers concurrently — caller's responsibility.\n *\n * Production deploys use platform-native triggers (T1.5 adapter translators);\n * this scheduler exists only for local dev iteration.\n */\n\nexport interface CronScheduler {\n start(): void\n stop(): void\n}\n\ninterface CronJobState {\n readonly def: CronDefinition\n inFlight: boolean\n timer: NodeJS.Timeout | null\n abortController: AbortController | null\n}\n\nexport function createCronScheduler(definitions: readonly CronDefinition[]): CronScheduler {\n const states: CronJobState[] = definitions.map((def) => ({\n def,\n inFlight: false,\n timer: null,\n abortController: null,\n }))\n\n let started = false\n\n const fireAndReschedule = (state: CronJobState, scheduledAt: Date): void => {\n state.timer = null\n\n if (state.inFlight && state.def.concurrency === 'forbid') {\n console.warn(\n `[theokit:cron] \"${state.def.name}\" skipped tick at ${scheduledAt.toISOString()} ` +\n '— previous handler still running (concurrency: forbid).',\n )\n scheduleNext(state)\n return\n }\n\n state.inFlight = true\n state.abortController = new AbortController()\n const traceCtx = generateNewTraceContext()\n const ctx: CronContext = {\n traceId: traceCtx.trace_id,\n scheduledAt,\n signal: state.abortController.signal,\n }\n\n // Fire-and-forget: EC-109 — never await here so a hanging handler\n // can't block the scheduler loop or other crons.\n void Promise.resolve()\n .then(() => state.def.handler(ctx))\n .catch((err: unknown) => {\n console.error(\n `[theokit:cron] \"${state.def.name}\" handler error:`,\n err instanceof Error ? err.message : err,\n )\n })\n .finally(() => {\n state.inFlight = false\n })\n\n scheduleNext(state)\n }\n\n const scheduleNext = (state: CronJobState): void => {\n if (!started) return\n const interval = CronExpressionParser.parse(state.def.schedule, {\n tz: 'UTC',\n currentDate: new Date(),\n })\n const next = interval.next().toDate()\n const delayMs = Math.max(0, next.getTime() - Date.now())\n state.timer = setTimeout(() => {\n fireAndReschedule(state, next)\n }, delayMs)\n // setTimeout returns a Timeout object; in Node, .unref() is available but\n // we DO want this to keep the event loop alive in dev — explicit no-unref.\n }\n\n return {\n start(): void {\n if (started) return\n started = true\n for (const state of states) {\n scheduleNext(state)\n }\n },\n stop(): void {\n started = false\n for (const state of states) {\n if (state.timer) {\n clearTimeout(state.timer)\n state.timer = null\n }\n state.abortController?.abort()\n state.abortController = null\n }\n },\n }\n}\n","/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time scanner: caller-controlled directory paths only. No HTTP\n * input ever reaches these fs calls.\n */\nimport { existsSync } from 'node:fs'\nimport { basename } from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\nimport { walkSourceFiles } from '../_internal/scan-walker.js'\n\nimport type { CronConcurrencyPolicy, CronDefinition } from './cron-types.js'\n\n/**\n * One discovered cron from build-time scan. The handler is intentionally\n * NOT included — manifest is platform-neutral and consumed by adapters\n * that emit static config. Runtime dispatch loads the handler at fire time.\n */\nexport interface CronNode {\n readonly name: string\n readonly filePath: string\n readonly schedule: string\n readonly concurrency: CronConcurrencyPolicy\n}\n\nexport class DuplicateCronNameError extends Error {\n readonly code = 'DUPLICATE_CRON_NAME'\n constructor(\n public readonly cronName: string,\n public readonly filePaths: readonly string[],\n ) {\n super(\n `Duplicate cron name \"${cronName}\" defined in: ${filePaths.join(', ')}. ` +\n 'Cron names must be unique across server/crons/.',\n )\n this.name = 'DuplicateCronNameError'\n }\n}\n\nconst CRON_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.mjs'])\n\ninterface CronModule {\n default?: unknown\n}\n\nfunction isCronDefinition(value: unknown): value is CronDefinition {\n if (typeof value !== 'object' || value === null) return false\n const def = value as Record<string, unknown>\n return (\n typeof def.name === 'string' &&\n typeof def.schedule === 'string' &&\n typeof def.handler === 'function' &&\n (def.concurrency === 'forbid' || def.concurrency === 'allow')\n )\n}\n\n/**\n * Scan a directory for cron definition files and return the discovered\n * `CronNode[]` sorted by name. Throws `DuplicateCronNameError` on name\n * collision and `Error` on missing default export.\n *\n * Sequential by design — module imports are awaited one-by-one to keep\n * error messages anchored to the file that failed.\n */\nexport async function scanCrons(cronsDir: string): Promise<CronNode[]> {\n if (!existsSync(cronsDir)) return []\n\n const filePaths: string[] = []\n walkSourceFiles(cronsDir, { extensions: CRON_EXTENSIONS }, (p) => {\n const base = basename(p)\n // Skip private helpers (`_helper.ts`) and OS junk (`.DS_Store`).\n if (base.startsWith('_') || base.startsWith('.')) return\n filePaths.push(p)\n })\n\n const nodes: CronNode[] = []\n for (const filePath of filePaths) {\n let mod: CronModule\n try {\n mod = (await import(pathToFileURL(filePath).href)) as CronModule\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err)\n throw new Error(`Failed to import cron file \"${filePath}\": ${reason}`)\n }\n const exported = mod.default\n if (!isCronDefinition(exported)) {\n throw new Error(\n `Cron file \"${filePath}\" is missing a valid default export. ` +\n 'Expected `export default defineCron(name, { schedule, handler })`.',\n )\n }\n nodes.push({\n name: exported.name,\n filePath,\n schedule: exported.schedule,\n concurrency: exported.concurrency,\n })\n }\n\n // Dup-name detection\n const byName = new Map<string, string[]>()\n for (const node of nodes) {\n const bucket = byName.get(node.name) ?? []\n bucket.push(node.filePath)\n byName.set(node.name, bucket)\n }\n for (const [name, paths] of byName) {\n if (paths.length > 1) {\n throw new DuplicateCronNameError(name, paths)\n }\n }\n\n return nodes.sort((a, b) => a.name.localeCompare(b.name))\n}\n"],"mappings":"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","names":["CronExpressionParser","CronExpressionParser","existsSync","existsSync"]}
1
+ {"version":3,"sources":["../src/server/cron/cron-validate.ts","../src/server/cron/define-cron.ts","../src/server/cron/cron-manifest.ts","../src/server/cron/adapter-translators.ts","../src/server/cron/cron-runtime-node.ts","../src/server/cron/cron-scan.ts"],"sourcesContent":["import { CronExpressionParser } from 'cron-parser'\n\n/**\n * Validate a cron schedule string per ADR-0004 — 5-field UTC strict.\n *\n * Accepts:\n * - Standard 5-field expressions: `minute hour dayOfMonth month dayOfWeek`\n * - Step (`*\\/15`), range (`1-5`), list (`MON,TUE,FRI`), wildcards\n *\n * Rejects:\n * - 6-field with seconds (`* * * * * *`)\n * - 7-field with year\n * - Shorthand (`@daily`, `@hourly`, `@yearly`, `@reboot`)\n * - Timezone suffix\n * - Empty / whitespace-only\n * - Malformed grammar\n *\n * Throws on every invalid input. Every error message includes the\n * original input and the fix.\n *\n * @see docs/adr/0004-cron-schedule-5-field-utc-strict.md\n */\nexport function validateCronSchedule(schedule: string): void {\n if (typeof schedule !== 'string') {\n throw new TypeError(`Invalid cron schedule: expected string, got ${typeof schedule}.`)\n }\n const trimmed = schedule.trim()\n if (trimmed.length === 0) {\n throw new Error(\n 'Invalid cron schedule: empty string. TheoKit cron uses 5-field UTC strict format, e.g. \"0 9 * * *\".',\n )\n }\n\n // Shorthand check BEFORE field counting — `@daily` is one \"field\" but\n // semantically not in the 5-field grammar.\n if (trimmed.startsWith('@')) {\n throw new Error(\n `Invalid cron schedule \"${schedule}\": shorthand not supported. ` +\n 'Use the equivalent 5-field form, e.g. \"@daily\" → \"0 0 * * *\", \"@hourly\" → \"0 * * * *\".',\n )\n }\n\n const parts = trimmed.split(/\\s+/)\n if (parts.length !== 5) {\n throw new Error(\n `Invalid cron schedule \"${schedule}\": expected 5 fields ` +\n '(\"minute hour dayOfMonth month dayOfWeek\"), ' +\n `got ${parts.length}. TheoKit treats all schedules as UTC; ` +\n 'for second-precision or timezone, see docs/concepts/crons.md.',\n )\n }\n\n // Final grammar validation via cron-parser. We pass `tz: 'UTC'`\n // explicitly so any embedded timezone hint surfaces as an error.\n try {\n CronExpressionParser.parse(trimmed, { tz: 'UTC' })\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err)\n throw new Error(`Invalid cron schedule \"${schedule}\": ${reason}`)\n }\n}\n","import type { CronDefinition, CronOptions } from './cron-types.js'\nimport { validateCronSchedule } from './cron-validate.js'\n\nconst NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/\n\nfunction validateName(name: string): void {\n if (typeof name !== 'string' || !NAME_RE.test(name)) {\n throw new Error(\n `defineCron: invalid name \"${name}\". ` +\n 'Must be 1-64 chars, lowercase alphanumeric + hyphen, starting with [a-z0-9].',\n )\n }\n}\n\n/**\n * Declare a time-triggered handler. Pure identity helper — no\n * registration side effect; the build-time scanner (T1.3) discovers\n * definitions by walking `server/crons/` and emits a manifest the\n * adapters translate at deploy.\n *\n * @example\n * ```ts\n * // server/crons/morning-summary.ts\n * export default defineCron('morning-summary', {\n * schedule: '0 9 * * *', // 09:00 UTC\n * async handler({ traceId, scheduledAt, signal }) {\n * // ...\n * },\n * })\n * ```\n */\nexport function defineCron(name: string, options: CronOptions): CronDefinition {\n validateName(name)\n validateCronSchedule(options.schedule)\n return {\n name,\n schedule: options.schedule,\n handler: options.handler,\n concurrency: options.concurrency ?? 'forbid',\n }\n}\n","import { writeAtomic } from '../_internal/atomic-write.js'\n\nimport type { CronNode } from './cron-scan.js'\n\nexport const CRON_MANIFEST_SCHEMA_VERSION = 1 as const\n\nexport interface CronManifestEntry {\n readonly name: string\n readonly filePath: string\n readonly schedule: string\n readonly concurrency: 'forbid' | 'allow'\n}\n\nexport interface CronManifest {\n readonly schemaVersion: typeof CRON_MANIFEST_SCHEMA_VERSION\n readonly generatedAt: string\n readonly crons: readonly CronManifestEntry[]\n}\n\n/**\n * Build a `CronManifest` from scanned `CronNode[]`. Filepaths are kept\n * relative to the caller's project root for portability.\n */\nexport function buildCronManifest(nodes: readonly CronNode[], projectRoot?: string): CronManifest {\n const crons: CronManifestEntry[] = nodes.map((n) => ({\n name: n.name,\n filePath: projectRoot ? relativize(n.filePath, projectRoot) : n.filePath,\n schedule: n.schedule,\n concurrency: n.concurrency,\n }))\n return {\n schemaVersion: CRON_MANIFEST_SCHEMA_VERSION,\n generatedAt: new Date().toISOString(),\n crons,\n }\n}\n\n/**\n * Write the cron manifest to `path` atomically (EC-106).\n *\n * Accepts either a pre-built `CronManifest` OR an array of `CronNode[]`\n * (which gets converted via `buildCronManifest`). The atomic-write\n * helper guarantees `path` always contains valid JSON, even under\n * concurrent writes (e.g., dev-server rescan + build).\n */\nexport function writeCronManifest(\n path: string,\n input: readonly CronNode[] | CronManifest,\n projectRoot?: string,\n): void {\n const manifest: CronManifest = isManifest(input) ? input : buildCronManifest(input, projectRoot)\n writeAtomic(path, JSON.stringify(manifest, null, 2))\n}\n\nfunction isManifest(value: unknown): value is CronManifest {\n return typeof value === 'object' && value !== null && 'schemaVersion' in value && 'crons' in value\n}\n\nfunction relativize(absPath: string, root: string): string {\n if (absPath.startsWith(root)) {\n const trimmed = absPath.slice(root.length)\n return trimmed.startsWith('/') ? trimmed.slice(1) : trimmed\n }\n return absPath\n}\n","/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time adapter translators: caller-controlled paths only.\n */\nimport { existsSync, readFileSync } from 'node:fs'\n\nimport { writeAtomic } from '../_internal/atomic-write.js'\n\nimport type { CronManifestEntry } from './cron-manifest.js'\n\n/**\n * Thrown when an existing platform-config file (vercel.json, wrangler.toml,\n * serverless.yml) cannot be parsed. Caller must fix the file before\n * re-running `theokit build`. We NEVER silently overwrite a user's config.\n */\nexport class ExistingConfigUnparseableError extends Error {\n readonly code = 'EXISTING_CONFIG_UNPARSEABLE'\n constructor(\n public readonly filePath: string,\n public readonly parseError: string,\n ) {\n super(\n `Existing config \"${filePath}\" could not be parsed: ${parseError}. ` +\n 'Fix or remove the file before re-running build — TheoKit never silently overwrites user configuration.',\n )\n this.name = 'ExistingConfigUnparseableError'\n }\n}\n\n// ──────────────────────────────────────────────────────────\n// Vercel — vercel.json crons[]\n// ──────────────────────────────────────────────────────────\n\ninterface VercelJson {\n crons?: { path: string; schedule: string }[]\n [key: string]: unknown\n}\n\n/**\n * Translate a TheoKit cron manifest into `vercel.json crons[]`.\n *\n * EC-105: existing fields (functions, headers, redirects, rewrites, env,\n * etc.) are preserved verbatim — ONLY the `crons[]` slice is replaced.\n */\nexport function translateCronToVercel(\n vercelJsonPath: string,\n crons: readonly CronManifestEntry[],\n): void {\n let existing: VercelJson = {}\n if (existsSync(vercelJsonPath)) {\n const raw = readFileSync(vercelJsonPath, 'utf8')\n try {\n existing = JSON.parse(raw) as VercelJson\n } catch (err) {\n throw new ExistingConfigUnparseableError(\n vercelJsonPath,\n err instanceof Error ? err.message : String(err),\n )\n }\n }\n const merged: VercelJson = {\n ...existing,\n crons: crons.map((c) => ({\n path: `/api/__crons/${c.name}`,\n schedule: c.schedule,\n })),\n }\n writeAtomic(vercelJsonPath, JSON.stringify(merged, null, 2))\n}\n\n// ──────────────────────────────────────────────────────────\n// Cloudflare — wrangler.toml [triggers] crons\n// ──────────────────────────────────────────────────────────\n\n/**\n * Translate a TheoKit cron manifest into `wrangler.toml [triggers] crons`.\n *\n * EC-105: regex-based mutation that preserves comments + other sections\n * verbatim. Replaces only the `[triggers]` block (or appends if absent).\n */\nexport function translateCronToCloudflare(\n wranglerTomlPath: string,\n crons: readonly CronManifestEntry[],\n): void {\n const schedules = crons.map((c) => `\"${c.schedule}\"`).join(', ')\n const triggersBlock = `[triggers]\\ncrons = [${schedules}]\\n`\n\n if (!existsSync(wranglerTomlPath)) {\n writeAtomic(wranglerTomlPath, triggersBlock)\n return\n }\n\n const existing = readFileSync(wranglerTomlPath, 'utf8')\n\n // Replace existing [triggers] section if present. Strategy: split into\n // line array, find the [triggers] header line, drop subsequent lines\n // until the next section header (line starting with `[`) or EOF.\n const lines = existing.split('\\n')\n const triggersIdx = lines.findIndex((l) => l.trim() === '[triggers]')\n\n let next: string\n if (triggersIdx !== -1) {\n let endIdx = lines.length\n for (let i = triggersIdx + 1; i < lines.length; i++) {\n if (lines[i].trim().startsWith('[')) {\n endIdx = i\n break\n }\n }\n const before = lines.slice(0, triggersIdx).join('\\n')\n const after = lines.slice(endIdx).join('\\n')\n next = `${before}\\n${triggersBlock.trimEnd()}\\n${after}`.replace(/\\n{3,}/g, '\\n\\n')\n } else {\n next = existing.endsWith('\\n')\n ? `${existing}\\n${triggersBlock}`\n : `${existing}\\n\\n${triggersBlock}`\n }\n writeAtomic(wranglerTomlPath, next)\n}\n\n// ──────────────────────────────────────────────────────────\n// AWS Lambda — serverless.yml functions.<fn>.events: schedule\n// ──────────────────────────────────────────────────────────\n\n/**\n * Convert TheoKit's 5-field UTC cron to AWS EventBridge's 6-field format.\n * EventBridge requires `?` in EITHER day-of-month OR day-of-week (not both `*`).\n *\n * Algorithm:\n * - If DOM is \"*\" and DOW is \"*\" → insert ? in DOW (default)\n * - If DOM is \"*\" and DOW is specific → insert ? in DOM\n * - If DOM is specific and DOW is \"*\" → insert ? in DOW\n * - Append \"*\" year field at end.\n */\nexport function convertToAwsCron(schedule: string): string {\n const parts = schedule.trim().split(/\\s+/)\n if (parts.length !== 5) {\n throw new Error(`Invalid 5-field schedule for AWS conversion: \"${schedule}\"`)\n }\n const [minute, hour, dom, month, dow] = parts\n let awsDom = dom\n let awsDow = dow\n if (dom === '*' && dow === '*') {\n awsDow = '?'\n } else if (dom === '*') {\n awsDom = '?'\n } else if (dow === '*') {\n awsDow = '?'\n }\n return `cron(${minute} ${hour} ${awsDom} ${month} ${awsDow} *)`\n}\n\n/**\n * Translate a TheoKit cron manifest into a `serverless.yml` functions\n * map with `events: - schedule: cron(...)` entries.\n *\n * EC-105: appends to `functions:` block, preserving all existing\n * functions/sections. Cron functions are named `cron_<name>` to avoid\n * collision with user-declared functions.\n */\nexport function translateCronToAws(\n serverlessYmlPath: string,\n crons: readonly CronManifestEntry[],\n): void {\n const cronFunctionsYaml = crons\n .map(\n (c) => ` cron_${c.name}:\n handler: .theo/server/crons/${c.name}.handler\n events:\n - schedule: ${convertToAwsCron(c.schedule)}`,\n )\n .join('\\n')\n\n const block = `\\nfunctions:\\n${cronFunctionsYaml}\\n`\n\n if (!existsSync(serverlessYmlPath)) {\n // Minimal serverless.yml scaffold\n writeAtomic(\n serverlessYmlPath,\n `service: theokit-app\\nprovider:\\n name: aws\\n runtime: nodejs22.x\\n${block}`,\n )\n return\n }\n\n const existing = readFileSync(serverlessYmlPath, 'utf8')\n\n // Look for an existing top-level `functions:` block; if present, append our\n // cron entries under it (don't replace user-declared functions).\n const functionsRe = /^functions:\\s*\\n/m\n let next: string\n if (functionsRe.test(existing)) {\n next = existing.replace(functionsRe, (match) => `${match}${cronFunctionsYaml}\\n`)\n } else {\n next = existing.endsWith('\\n') ? `${existing}${block}` : `${existing}\\n${block}`\n }\n writeAtomic(serverlessYmlPath, next)\n}\n\n// ──────────────────────────────────────────────────────────\n// Deno Deploy — Deno.cron registrations\n// ──────────────────────────────────────────────────────────\n\n/**\n * Emit a Deno entry file that registers each cron via `Deno.cron`.\n *\n * Unlike Vercel/CF/AWS, Deno.cron is an in-process runtime API — the\n * entry file is a managed artifact (overwritten each build), not a user\n * config (so EC-105 doesn't apply here).\n */\nexport function translateCronToDeno(entryPath: string, crons: readonly CronManifestEntry[]): void {\n const lines = crons.map(\n (c) =>\n `Deno.cron(\"${c.name}\", \"${c.schedule}\", async () => {\n const mod = await import(\"../${c.filePath}\");\n const def = mod.default;\n await def.handler({\n traceId: crypto.randomUUID().replace(/-/g, \"\"),\n scheduledAt: new Date(),\n signal: AbortSignal.timeout(60_000),\n });\n});`,\n )\n const content = `// AUTOGENERATED by TheoKit cron build — DO NOT EDIT.\n// Source: server/crons/*.ts → .theo/crons.json → this file.\n${lines.join('\\n\\n')}\n`\n writeAtomic(entryPath, content)\n}\n","import { CronExpressionParser } from 'cron-parser'\n\nimport { generateNewTraceContext } from '../observability/trace-context-propagation.js'\n\nimport type { CronContext, CronDefinition } from './cron-types.js'\n\n/**\n * In-memory cron scheduler for `theokit dev` (T1.4).\n *\n * Algorithm:\n * - For each cron, compute `nextFireAt = cron-parser.next()`.\n * - Schedule a `setTimeout(handler, nextFireAt - now)`.\n * - After handler invocation (sync return or Promise scheduled), recompute\n * next fire from CURRENT time (drift-free vs scheduled time).\n *\n * Per-cron isolation (EC-109):\n * - Each cron's handler invocation is fire-and-forget (`void` scheduled).\n * A hanging handler does NOT block the scheduler loop nor other crons.\n * - `concurrency: 'forbid'` (default) tracks an in-flight flag per-cron;\n * subsequent ticks skip + warn while the in-flight flag is set.\n * - `concurrency: 'allow'` runs handlers concurrently — caller's responsibility.\n *\n * Production deploys use platform-native triggers (T1.5 adapter translators);\n * this scheduler exists only for local dev iteration.\n */\n\nexport interface CronScheduler {\n start(): void\n stop(): void\n}\n\ninterface CronJobState {\n readonly def: CronDefinition\n inFlight: boolean\n timer: NodeJS.Timeout | null\n abortController: AbortController | null\n}\n\nexport function createCronScheduler(definitions: readonly CronDefinition[]): CronScheduler {\n const states: CronJobState[] = definitions.map((def) => ({\n def,\n inFlight: false,\n timer: null,\n abortController: null,\n }))\n\n let started = false\n\n const fireAndReschedule = (state: CronJobState, scheduledAt: Date): void => {\n state.timer = null\n\n if (state.inFlight && state.def.concurrency === 'forbid') {\n console.warn(\n `[theokit:cron] \"${state.def.name}\" skipped tick at ${scheduledAt.toISOString()} ` +\n '— previous handler still running (concurrency: forbid).',\n )\n scheduleNext(state)\n return\n }\n\n state.inFlight = true\n state.abortController = new AbortController()\n const traceCtx = generateNewTraceContext()\n const ctx: CronContext = {\n traceId: traceCtx.trace_id,\n scheduledAt,\n signal: state.abortController.signal,\n }\n\n // Fire-and-forget: EC-109 — never await here so a hanging handler\n // can't block the scheduler loop or other crons.\n void Promise.resolve()\n .then(() => state.def.handler(ctx))\n .catch((err: unknown) => {\n console.error(\n `[theokit:cron] \"${state.def.name}\" handler error:`,\n err instanceof Error ? err.message : err,\n )\n })\n .finally(() => {\n state.inFlight = false\n })\n\n scheduleNext(state)\n }\n\n const scheduleNext = (state: CronJobState): void => {\n if (!started) return\n const interval = CronExpressionParser.parse(state.def.schedule, {\n tz: 'UTC',\n currentDate: new Date(),\n })\n const next = interval.next().toDate()\n const delayMs = Math.max(0, next.getTime() - Date.now())\n state.timer = setTimeout(() => {\n fireAndReschedule(state, next)\n }, delayMs)\n // setTimeout returns a Timeout object; in Node, .unref() is available but\n // we DO want this to keep the event loop alive in dev — explicit no-unref.\n }\n\n return {\n start(): void {\n if (started) return\n started = true\n for (const state of states) {\n scheduleNext(state)\n }\n },\n stop(): void {\n started = false\n for (const state of states) {\n if (state.timer) {\n clearTimeout(state.timer)\n state.timer = null\n }\n state.abortController?.abort()\n state.abortController = null\n }\n },\n }\n}\n","/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time scanner: caller-controlled directory paths only. No HTTP\n * input ever reaches these fs calls.\n */\nimport { existsSync } from 'node:fs'\nimport { basename } from 'node:path'\n\nimport { importUserModule } from '../../config/import-user-module.js'\nimport { walkSourceFiles } from '../_internal/scan-walker.js'\n\nimport type { CronConcurrencyPolicy, CronDefinition } from './cron-types.js'\n\n/**\n * One discovered cron from build-time scan. The handler is intentionally\n * NOT included — manifest is platform-neutral and consumed by adapters\n * that emit static config. Runtime dispatch loads the handler at fire time.\n */\nexport interface CronNode {\n readonly name: string\n readonly filePath: string\n readonly schedule: string\n readonly concurrency: CronConcurrencyPolicy\n}\n\nexport class DuplicateCronNameError extends Error {\n readonly code = 'DUPLICATE_CRON_NAME'\n constructor(\n public readonly cronName: string,\n public readonly filePaths: readonly string[],\n ) {\n super(\n `Duplicate cron name \"${cronName}\" defined in: ${filePaths.join(', ')}. ` +\n 'Cron names must be unique across server/crons/.',\n )\n this.name = 'DuplicateCronNameError'\n }\n}\n\nconst CRON_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.mjs'])\n\ninterface CronModule {\n default?: unknown\n}\n\nfunction isCronDefinition(value: unknown): value is CronDefinition {\n if (typeof value !== 'object' || value === null) return false\n const def = value as Record<string, unknown>\n return (\n typeof def.name === 'string' &&\n typeof def.schedule === 'string' &&\n typeof def.handler === 'function' &&\n (def.concurrency === 'forbid' || def.concurrency === 'allow')\n )\n}\n\n/**\n * Scan a directory for cron definition files and return the discovered\n * `CronNode[]` sorted by name. Throws `DuplicateCronNameError` on name\n * collision and `Error` on missing default export.\n *\n * Sequential by design — module imports are awaited one-by-one to keep\n * error messages anchored to the file that failed.\n */\nexport async function scanCrons(cronsDir: string): Promise<CronNode[]> {\n if (!existsSync(cronsDir)) return []\n\n const filePaths: string[] = []\n walkSourceFiles(cronsDir, { extensions: CRON_EXTENSIONS }, (p) => {\n const base = basename(p)\n // Skip private helpers (`_helper.ts`) and OS junk (`.DS_Store`).\n if (base.startsWith('_') || base.startsWith('.')) return\n filePaths.push(p)\n })\n\n const nodes: CronNode[] = []\n for (const filePath of filePaths) {\n let mod: CronModule\n try {\n mod = await importUserModule(filePath)\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err)\n throw new Error(`Failed to import cron file \"${filePath}\": ${reason}`)\n }\n const exported = mod.default\n if (!isCronDefinition(exported)) {\n throw new Error(\n `Cron file \"${filePath}\" is missing a valid default export. ` +\n 'Expected `export default defineCron(name, { schedule, handler })`.',\n )\n }\n nodes.push({\n name: exported.name,\n filePath,\n schedule: exported.schedule,\n concurrency: exported.concurrency,\n })\n }\n\n // Dup-name detection\n const byName = new Map<string, string[]>()\n for (const node of nodes) {\n const bucket = byName.get(node.name) ?? []\n bucket.push(node.filePath)\n byName.set(node.name, bucket)\n }\n for (const [name, paths] of byName) {\n if (paths.length > 1) {\n throw new DuplicateCronNameError(name, paths)\n }\n }\n\n return nodes.sort((a, b) => a.name.localeCompare(b.name))\n}\n"],"mappings":"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","names":["CronExpressionParser","CronExpressionParser","existsSync","existsSync"]}
@@ -0,0 +1,293 @@
1
+ // src/server/observability/span.ts
2
+ var SpanImpl = class {
3
+ data;
4
+ ended = false;
5
+ constructor(name, attributes) {
6
+ this.data = {
7
+ name,
8
+ attributes: {},
9
+ status: "ok",
10
+ startTimeMs: Date.now()
11
+ };
12
+ if (attributes) {
13
+ for (const [k, v] of Object.entries(attributes)) {
14
+ if (v !== void 0) this.data.attributes[k] = v;
15
+ }
16
+ }
17
+ }
18
+ setAttribute(key, value) {
19
+ if (!this.ended) this.data.attributes[key] = value;
20
+ }
21
+ setStatus(status, message) {
22
+ if (!this.ended) {
23
+ this.data.status = status;
24
+ this.data.statusMessage = message;
25
+ }
26
+ }
27
+ end() {
28
+ if (this.ended) return;
29
+ this.ended = true;
30
+ this.data.endTimeMs = Date.now();
31
+ this.data.durationMs = this.data.endTimeMs - this.data.startTimeMs;
32
+ }
33
+ /** Read-only access to span data (for adapters to export). */
34
+ getData() {
35
+ return { ...this.data, attributes: { ...this.data.attributes } };
36
+ }
37
+ isEnded() {
38
+ return this.ended;
39
+ }
40
+ };
41
+ var NoopSpan = class {
42
+ setAttribute() {
43
+ }
44
+ setStatus() {
45
+ }
46
+ end() {
47
+ }
48
+ };
49
+
50
+ // src/server/observability/adapters/noop.ts
51
+ var NoopObservabilityAdapter = class {
52
+ name = "noop";
53
+ isShutdown = false;
54
+ startSpan(_name, _attributes) {
55
+ return new NoopSpan();
56
+ }
57
+ counter(_name, _value, _attributes) {
58
+ }
59
+ histogram(_name, _value, _attributes) {
60
+ }
61
+ log(_level, _message, _attributes) {
62
+ }
63
+ async flush() {
64
+ }
65
+ async shutdown() {
66
+ this.isShutdown = true;
67
+ }
68
+ };
69
+
70
+ // src/server/observability/adapters/console.ts
71
+ var ConsoleObservabilityAdapter = class {
72
+ name = "console";
73
+ write;
74
+ isShutdown = false;
75
+ spans = [];
76
+ constructor(options = {}) {
77
+ this.write = options.write ?? ((line) => process.stderr.write(line + "\n"));
78
+ }
79
+ startSpan(name, attributes) {
80
+ if (this.isShutdown) return new NoopSpan();
81
+ const span = new SpanImpl(name, attributes);
82
+ this.spans.push(span);
83
+ return {
84
+ setAttribute: (k, v) => span.setAttribute(k, v),
85
+ setStatus: (s, m) => span.setStatus(s, m),
86
+ end: () => {
87
+ span.end();
88
+ this.emitSpan(span.getData());
89
+ }
90
+ };
91
+ }
92
+ counter(name, value, attributes) {
93
+ if (this.isShutdown) return;
94
+ this.emit({ type: "counter", metric: name, value, attributes: attributes ?? {}, timestamp: Date.now() });
95
+ }
96
+ histogram(name, value, attributes) {
97
+ if (this.isShutdown) return;
98
+ this.emit({ type: "histogram", metric: name, value, attributes: attributes ?? {}, timestamp: Date.now() });
99
+ }
100
+ log(level, message, attributes) {
101
+ if (this.isShutdown) return;
102
+ this.emit({ type: "log", level, message, attributes: attributes ?? {}, timestamp: Date.now() });
103
+ }
104
+ async flush() {
105
+ }
106
+ async shutdown() {
107
+ this.isShutdown = true;
108
+ }
109
+ emitSpan(data) {
110
+ this.emit({
111
+ type: "span",
112
+ name: data.name,
113
+ status: data.status,
114
+ duration_ms: data.durationMs ?? 0,
115
+ attributes: data.attributes,
116
+ timestamp: data.startTimeMs
117
+ });
118
+ }
119
+ emit(record) {
120
+ this.write(JSON.stringify(record));
121
+ }
122
+ };
123
+
124
+ // src/server/observability/otlp-serializer.ts
125
+ function serializeSpansToOtlp(spans, serviceName = "theokit") {
126
+ const otlpSpans = spans.map((s) => ({
127
+ traceId: randomHex(32),
128
+ spanId: randomHex(16),
129
+ name: s.name,
130
+ kind: 2,
131
+ // SPAN_KIND_SERVER
132
+ startTimeUnixNano: String(s.startTimeMs * 1e6),
133
+ endTimeUnixNano: String((s.endTimeMs ?? s.startTimeMs) * 1e6),
134
+ attributes: Object.entries(s.attributes).map(([key, value]) => ({
135
+ key,
136
+ value: typeof value === "string" ? { stringValue: value } : typeof value === "number" ? { intValue: String(value) } : { boolValue: value }
137
+ })),
138
+ status: { code: s.status === "ok" ? 1 : 2, message: s.statusMessage }
139
+ }));
140
+ const request = {
141
+ resourceSpans: [{
142
+ scopeSpans: [{
143
+ scope: { name: serviceName, version: "1.0.0" },
144
+ spans: otlpSpans
145
+ }]
146
+ }]
147
+ };
148
+ return new TextEncoder().encode(JSON.stringify(request));
149
+ }
150
+ function randomHex(length) {
151
+ const bytes = new Uint8Array(length / 2);
152
+ globalThis.crypto.getRandomValues(bytes);
153
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
154
+ }
155
+
156
+ // src/server/observability/adapters/theo-cloud.ts
157
+ var TheoCloudObservabilityAdapter = class {
158
+ name = "theo-cloud";
159
+ pendingSpans = [];
160
+ isShutdown = false;
161
+ opts;
162
+ constructor(options) {
163
+ this.opts = { flushIntervalMs: 5e3, ...options };
164
+ }
165
+ startSpan(name, attributes) {
166
+ if (this.isShutdown) return new NoopSpan();
167
+ const span = new SpanImpl(name, attributes);
168
+ return {
169
+ setAttribute: (k, v) => span.setAttribute(k, v),
170
+ setStatus: (s, m) => span.setStatus(s, m),
171
+ end: () => {
172
+ span.end();
173
+ this.pendingSpans.push(span.getData());
174
+ }
175
+ };
176
+ }
177
+ counter(_name, _value, _attributes) {
178
+ }
179
+ histogram(_name, _value, _attributes) {
180
+ }
181
+ log(_level, _message, _attributes) {
182
+ }
183
+ async flush() {
184
+ if (this.isShutdown || this.pendingSpans.length === 0) return;
185
+ const spans = this.pendingSpans.splice(0);
186
+ const body = serializeSpansToOtlp(spans);
187
+ const fetchFn = this.opts._mockFetch ?? globalThis.fetch;
188
+ try {
189
+ await fetchFn(this.opts.ingestUrl, {
190
+ method: "POST",
191
+ headers: {
192
+ "content-type": "application/json",
193
+ "authorization": `Bearer ${this.opts.token}`
194
+ },
195
+ body
196
+ });
197
+ } catch (err) {
198
+ console.error(
199
+ `[theokit:observability] flush failed: ${err instanceof Error ? err.message : "unknown error"}`
200
+ );
201
+ }
202
+ }
203
+ async shutdown() {
204
+ if (this.isShutdown) return;
205
+ await this.flush();
206
+ this.isShutdown = true;
207
+ }
208
+ };
209
+
210
+ // src/server/observability/adapter-registry.ts
211
+ function resolveAdapter(options) {
212
+ if (options.config?.provider) {
213
+ return options.config.provider;
214
+ }
215
+ const ingestUrl = options.env.THEO_CLOUD_INGEST_URL;
216
+ const apiKey = options.env.THEO_CLOUD_API_KEY;
217
+ if (ingestUrl && apiKey) {
218
+ return new TheoCloudObservabilityAdapter({ ingestUrl, token: apiKey });
219
+ }
220
+ if (options.env.NODE_ENV === "development") {
221
+ return new ConsoleObservabilityAdapter();
222
+ }
223
+ return new NoopObservabilityAdapter();
224
+ }
225
+
226
+ // src/server/observability/middleware.ts
227
+ var activeSpans = /* @__PURE__ */ new Map();
228
+ function createObservabilityPlugin(adapter) {
229
+ return {
230
+ name: "theokit:observability",
231
+ async onRequest(ctx) {
232
+ const url = new URL(ctx.request.url, "http://localhost");
233
+ const span = adapter.startSpan("http.request", {
234
+ method: ctx.request.method,
235
+ path: url.pathname,
236
+ requestId: ctx.requestId
237
+ });
238
+ activeSpans.set(ctx.requestId, span);
239
+ },
240
+ async onResponse(ctx) {
241
+ const span = activeSpans.get(ctx.requestId);
242
+ if (!span) return;
243
+ activeSpans.delete(ctx.requestId);
244
+ if (ctx.response) {
245
+ span.setAttribute("status", ctx.response.statusCode);
246
+ }
247
+ span.setStatus("ok");
248
+ span.end();
249
+ adapter.counter("http.requests", 1, {
250
+ method: ctx.request.method,
251
+ status: ctx.response?.statusCode ?? 0
252
+ });
253
+ },
254
+ async onError(ctx) {
255
+ const span = activeSpans.get(ctx.requestId);
256
+ if (!span) return;
257
+ activeSpans.delete(ctx.requestId);
258
+ const message = ctx.error instanceof Error ? ctx.error.message : "unknown error";
259
+ span.setAttribute("error", true);
260
+ span.setStatus("error", message);
261
+ span.end();
262
+ adapter.counter("http.errors", 1, {
263
+ method: ctx.request.method
264
+ });
265
+ }
266
+ };
267
+ }
268
+
269
+ // src/server/observability/define-adapter.ts
270
+ function defineObservabilityAdapter(config) {
271
+ return {
272
+ name: config.name,
273
+ startSpan: (name, attrs) => config.startSpan(name, attrs),
274
+ counter: (name, value, attrs) => config.counter(name, value, attrs),
275
+ histogram: (name, value, attrs) => config.histogram(name, value, attrs),
276
+ log: (level, message, attrs) => config.log(level, message, attrs),
277
+ flush: () => config.flush(),
278
+ shutdown: () => config.shutdown()
279
+ };
280
+ }
281
+
282
+ export {
283
+ SpanImpl,
284
+ NoopSpan,
285
+ NoopObservabilityAdapter,
286
+ ConsoleObservabilityAdapter,
287
+ serializeSpansToOtlp,
288
+ TheoCloudObservabilityAdapter,
289
+ resolveAdapter,
290
+ createObservabilityPlugin,
291
+ defineObservabilityAdapter
292
+ };
293
+ //# sourceMappingURL=chunk-KI7OWQUX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/observability/span.ts","../src/server/observability/adapters/noop.ts","../src/server/observability/adapters/console.ts","../src/server/observability/otlp-serializer.ts","../src/server/observability/adapters/theo-cloud.ts","../src/server/observability/adapter-registry.ts","../src/server/observability/middleware.ts","../src/server/observability/define-adapter.ts"],"sourcesContent":["/**\n * SpanHandle implementation — records timing + attributes.\n *\n * Used by console and theo-cloud adapters. Noop adapter uses NoopSpan.\n */\nimport type { SpanHandle, SpanAttributes } from './adapters/types.js'\n\nexport interface SpanData {\n name: string\n attributes: Record<string, string | number | boolean>\n status: 'ok' | 'error'\n statusMessage?: string\n startTimeMs: number\n endTimeMs?: number\n durationMs?: number\n}\n\nexport class SpanImpl implements SpanHandle {\n private readonly data: SpanData\n private ended = false\n\n constructor(name: string, attributes?: SpanAttributes) {\n this.data = {\n name,\n attributes: {},\n status: 'ok',\n startTimeMs: Date.now(),\n }\n if (attributes) {\n for (const [k, v] of Object.entries(attributes)) {\n if (v !== undefined) this.data.attributes[k] = v\n }\n }\n }\n\n setAttribute(key: string, value: string | number | boolean): void {\n if (!this.ended) this.data.attributes[key] = value\n }\n\n setStatus(status: 'ok' | 'error', message?: string): void {\n if (!this.ended) {\n this.data.status = status\n this.data.statusMessage = message\n }\n }\n\n end(): void {\n if (this.ended) return // idempotent\n this.ended = true\n this.data.endTimeMs = Date.now()\n this.data.durationMs = this.data.endTimeMs - this.data.startTimeMs\n }\n\n /** Read-only access to span data (for adapters to export). */\n getData(): SpanData {\n return { ...this.data, attributes: { ...this.data.attributes } }\n }\n\n isEnded(): boolean {\n return this.ended\n }\n}\n\n/** Noop span — used by NoopAdapter and post-shutdown fallback (EC-2). */\nexport class NoopSpan implements SpanHandle {\n setAttribute(): void {}\n setStatus(): void {}\n end(): void {}\n}\n","/**\n * NoopObservabilityAdapter — silent fallback.\n *\n * All methods are no-ops. Never throws, never blocks.\n * Used as the default when no other adapter is configured.\n * EC-2: startSpan after shutdown returns a noop span (no crash).\n */\nimport type { ObservabilityAdapter, SpanHandle, SpanAttributes } from './types.js'\nimport { NoopSpan } from '../span.js'\n\nexport class NoopObservabilityAdapter implements ObservabilityAdapter {\n readonly name = 'noop'\n private isShutdown = false\n\n startSpan(_name: string, _attributes?: SpanAttributes): SpanHandle {\n return new NoopSpan()\n }\n\n counter(_name: string, _value: number, _attributes?: SpanAttributes): void {}\n histogram(_name: string, _value: number, _attributes?: SpanAttributes): void {}\n log(_level: 'debug' | 'info' | 'warn' | 'error', _message: string, _attributes?: SpanAttributes): void {}\n\n async flush(): Promise<void> {}\n\n async shutdown(): Promise<void> {\n this.isShutdown = true\n }\n}\n","/**\n * ConsoleObservabilityAdapter — dev-mode console output.\n *\n * Emits JSON-structured lines to a configurable writer (default: process.stderr).\n * Inspired by Hono middleware timing pattern.\n */\nimport type { ObservabilityAdapter, SpanHandle, SpanAttributes } from './types.js'\nimport { SpanImpl, NoopSpan, type SpanData } from '../span.js'\n\nexport interface ConsoleAdapterOptions {\n /** Writer function — defaults to process.stderr.write. */\n write?: (line: string) => void\n}\n\nexport class ConsoleObservabilityAdapter implements ObservabilityAdapter {\n readonly name = 'console'\n private write: (line: string) => void\n private isShutdown = false\n private spans: SpanImpl[] = []\n\n constructor(options: ConsoleAdapterOptions = {}) {\n this.write = options.write ?? ((line: string) => process.stderr.write(line + '\\n'))\n }\n\n startSpan(name: string, attributes?: SpanAttributes): SpanHandle {\n if (this.isShutdown) return new NoopSpan()\n const span = new SpanImpl(name, attributes)\n this.spans.push(span)\n return {\n setAttribute: (k, v) => span.setAttribute(k, v),\n setStatus: (s, m) => span.setStatus(s, m),\n end: () => {\n span.end()\n this.emitSpan(span.getData())\n },\n }\n }\n\n counter(name: string, value: number, attributes?: SpanAttributes): void {\n if (this.isShutdown) return\n this.emit({ type: 'counter', metric: name, value, attributes: attributes ?? {}, timestamp: Date.now() })\n }\n\n histogram(name: string, value: number, attributes?: SpanAttributes): void {\n if (this.isShutdown) return\n this.emit({ type: 'histogram', metric: name, value, attributes: attributes ?? {}, timestamp: Date.now() })\n }\n\n log(level: 'debug' | 'info' | 'warn' | 'error', message: string, attributes?: SpanAttributes): void {\n if (this.isShutdown) return\n this.emit({ type: 'log', level, message, attributes: attributes ?? {}, timestamp: Date.now() })\n }\n\n async flush(): Promise<void> {}\n\n async shutdown(): Promise<void> {\n this.isShutdown = true\n }\n\n private emitSpan(data: SpanData): void {\n this.emit({\n type: 'span',\n name: data.name,\n status: data.status,\n duration_ms: data.durationMs ?? 0,\n attributes: data.attributes,\n timestamp: data.startTimeMs,\n })\n }\n\n private emit(record: Record<string, unknown>): void {\n this.write(JSON.stringify(record))\n }\n}\n","/**\n * Lightweight OTLP JSON serializer — ~50 LoC, no @opentelemetry/* dependency.\n *\n * Per ADR D456: in-house serializer for the theo-cloud adapter.\n * Produces valid ExportTraceServiceRequest JSON (OTLP v1.0).\n * Pinned to OTLP JSON v1.0 (stable since 2023).\n */\nimport type { SpanData } from './span.js'\n\ninterface OtlpSpan {\n traceId: string\n spanId: string\n name: string\n kind: number\n startTimeUnixNano: string\n endTimeUnixNano: string\n attributes: { key: string; value: { stringValue?: string; intValue?: string; boolValue?: boolean } }[]\n status: { code: number; message?: string }\n}\n\ninterface ExportTraceServiceRequest {\n resourceSpans: [{\n scopeSpans: [{\n scope: { name: string; version: string }\n spans: OtlpSpan[]\n }]\n }]\n}\n\n/** Convert SpanData[] to OTLP JSON bytes (Uint8Array). */\nexport function serializeSpansToOtlp(spans: SpanData[], serviceName = 'theokit'): Uint8Array {\n const otlpSpans: OtlpSpan[] = spans.map((s) => ({\n traceId: randomHex(32),\n spanId: randomHex(16),\n name: s.name,\n kind: 2, // SPAN_KIND_SERVER\n startTimeUnixNano: String(s.startTimeMs * 1_000_000),\n endTimeUnixNano: String((s.endTimeMs ?? s.startTimeMs) * 1_000_000),\n attributes: Object.entries(s.attributes).map(([key, value]) => ({\n key,\n value: typeof value === 'string'\n ? { stringValue: value }\n : typeof value === 'number'\n ? { intValue: String(value) }\n : { boolValue: value },\n })),\n status: { code: s.status === 'ok' ? 1 : 2, message: s.statusMessage },\n }))\n\n const request: ExportTraceServiceRequest = {\n resourceSpans: [{\n scopeSpans: [{\n scope: { name: serviceName, version: '1.0.0' },\n spans: otlpSpans,\n }],\n }],\n }\n\n return new TextEncoder().encode(JSON.stringify(request))\n}\n\nfunction randomHex(length: number): string {\n const bytes = new Uint8Array(length / 2)\n globalThis.crypto.getRandomValues(bytes)\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n}\n","/**\n * TheoCloudObservabilityAdapter — OTLP/HTTP batched export for TheoCloud.\n *\n * Zero-config via env vars (THEO_CLOUD_INGEST_URL, THEO_CLOUD_API_KEY).\n * Batches spans and flushes via native fetch() POST.\n *\n * EC-1: flush failure logs warning, does NOT throw, does NOT retry (KISS).\n * EC-2: startSpan after shutdown returns noop span.\n */\nimport type { ObservabilityAdapter, SpanHandle, SpanAttributes } from './types.js'\nimport { SpanImpl, NoopSpan, type SpanData } from '../span.js'\nimport { serializeSpansToOtlp } from '../otlp-serializer.js'\n\nexport interface TheoCloudAdapterOptions {\n /** TheoCloud ingest endpoint URL. */\n ingestUrl: string\n /** TheoCloud API key for authentication. */\n token: string\n /** Flush interval in ms (default: 5000). */\n flushIntervalMs?: number\n /** Mock fetch for testing (never in production). */\n _mockFetch?: typeof globalThis.fetch\n}\n\nexport class TheoCloudObservabilityAdapter implements ObservabilityAdapter {\n readonly name = 'theo-cloud'\n private pendingSpans: SpanData[] = []\n private isShutdown = false\n private readonly opts: Required<Pick<TheoCloudAdapterOptions, 'ingestUrl' | 'token'>> & TheoCloudAdapterOptions\n\n constructor(options: TheoCloudAdapterOptions) {\n this.opts = { flushIntervalMs: 5000, ...options }\n }\n\n startSpan(name: string, attributes?: SpanAttributes): SpanHandle {\n if (this.isShutdown) return new NoopSpan()\n const span = new SpanImpl(name, attributes)\n return {\n setAttribute: (k, v) => span.setAttribute(k, v),\n setStatus: (s, m) => span.setStatus(s, m),\n end: () => {\n span.end()\n this.pendingSpans.push(span.getData())\n },\n }\n }\n\n counter(_name: string, _value: number, _attributes?: SpanAttributes): void {}\n histogram(_name: string, _value: number, _attributes?: SpanAttributes): void {}\n log(_level: 'debug' | 'info' | 'warn' | 'error', _message: string, _attributes?: SpanAttributes): void {}\n\n async flush(): Promise<void> {\n if (this.isShutdown || this.pendingSpans.length === 0) return\n\n const spans = this.pendingSpans.splice(0)\n const body = serializeSpansToOtlp(spans) as unknown as BodyInit\n const fetchFn = this.opts._mockFetch ?? globalThis.fetch\n\n try {\n await fetchFn(this.opts.ingestUrl, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'authorization': `Bearer ${this.opts.token}`,\n },\n body,\n })\n } catch (err) {\n // EC-1: log warning, don't throw, don't retry\n console.error(\n `[theokit:observability] flush failed: ${err instanceof Error ? err.message : 'unknown error'}`,\n )\n }\n }\n\n async shutdown(): Promise<void> {\n if (this.isShutdown) return\n await this.flush()\n this.isShutdown = true\n }\n}\n","/**\n * Adapter registry — resolves the active observability adapter.\n *\n * Per ADR D457 (v1.1) priority chain:\n * 1. Explicit config (theo.config.ts observability.provider) — ALWAYS wins\n * 2. THEO_CLOUD_INGEST_URL env → theo-cloud adapter\n * 3. NODE_ENV=development → console adapter\n * 4. Fallback → noop adapter\n */\nimport type { ObservabilityAdapter } from './adapters/types.js'\nimport { NoopObservabilityAdapter } from './adapters/noop.js'\nimport { ConsoleObservabilityAdapter } from './adapters/console.js'\nimport { TheoCloudObservabilityAdapter } from './adapters/theo-cloud.js'\n\nexport interface ResolveAdapterOptions {\n env: Record<string, string | undefined>\n config?: {\n provider?: ObservabilityAdapter\n }\n}\n\n/**\n * Resolve the observability adapter from config + env.\n * Called once at boot — returns the active adapter for the process lifetime.\n */\nexport function resolveAdapter(options: ResolveAdapterOptions): ObservabilityAdapter {\n // 1. Explicit config ALWAYS wins (EC-4)\n if (options.config?.provider) {\n return options.config.provider\n }\n\n // 2. TheoCloud env vars → theo-cloud adapter\n const ingestUrl = options.env.THEO_CLOUD_INGEST_URL\n const apiKey = options.env.THEO_CLOUD_API_KEY\n if (ingestUrl && apiKey) {\n return new TheoCloudObservabilityAdapter({ ingestUrl, token: apiKey })\n }\n\n // 3. Development → console adapter\n if (options.env.NODE_ENV === 'development') {\n return new ConsoleObservabilityAdapter()\n }\n\n // 4. Fallback → noop\n return new NoopObservabilityAdapter()\n}\n","/**\n * Auto-instrumentation middleware — creates spans for every HTTP request.\n *\n * Registers as a standard TheoKit plugin via existing hook system.\n * No changes to plugin-runner needed.\n *\n * Per blueprint Pattern 2 (Hono middleware timing):\n * - onRequest: start span with method + path\n * - onResponse: end span with status + duration\n * - onError: set span status to error\n */\nimport type { ObservabilityAdapter, SpanHandle } from './adapters/types.js'\n\nexport interface PluginContext {\n requestId: string\n request: { method: string; url: string }\n response?: { statusCode: number }\n}\n\nexport interface ObservabilityPlugin {\n name: string\n onRequest(ctx: PluginContext): Promise<void>\n onResponse(ctx: PluginContext): Promise<void>\n onError(ctx: PluginContext & { error: unknown }): Promise<void>\n}\n\n// Active spans keyed by requestId — cleared on response/error\nconst activeSpans = new Map<string, SpanHandle>()\n\n/**\n * Create an observability plugin that auto-instruments HTTP requests.\n *\n * EC-5: For SSE responses, span ends on response close, not headers sent.\n * The caller should invoke onResponse when the stream actually closes.\n */\nexport function createObservabilityPlugin(adapter: ObservabilityAdapter): ObservabilityPlugin {\n return {\n name: 'theokit:observability',\n\n async onRequest(ctx: PluginContext): Promise<void> {\n const url = new URL(ctx.request.url, 'http://localhost')\n const span = adapter.startSpan('http.request', {\n method: ctx.request.method,\n path: url.pathname,\n requestId: ctx.requestId,\n })\n activeSpans.set(ctx.requestId, span)\n },\n\n async onResponse(ctx: PluginContext): Promise<void> {\n const span = activeSpans.get(ctx.requestId)\n if (!span) return\n activeSpans.delete(ctx.requestId)\n\n if (ctx.response) {\n span.setAttribute('status', ctx.response.statusCode)\n }\n span.setStatus('ok')\n span.end()\n\n adapter.counter('http.requests', 1, {\n method: ctx.request.method,\n status: ctx.response?.statusCode ?? 0,\n })\n },\n\n async onError(ctx: PluginContext & { error: unknown }): Promise<void> {\n const span = activeSpans.get(ctx.requestId)\n if (!span) return\n activeSpans.delete(ctx.requestId)\n\n const message = ctx.error instanceof Error ? ctx.error.message : 'unknown error'\n span.setAttribute('error', true)\n span.setStatus('error', message)\n span.end()\n\n adapter.counter('http.errors', 1, {\n method: ctx.request.method,\n })\n },\n }\n}\n","/**\n * defineObservabilityAdapter() — public API for custom adapters.\n *\n * The escape hatch for self-host users who want Datadog, Grafana, Honeycomb, etc.\n * Validates the adapter shape and returns a typed ObservabilityAdapter.\n *\n * @example\n * ```ts\n * const datadogAdapter = defineObservabilityAdapter({\n * name: 'datadog',\n * startSpan: (name, attrs) => { ... },\n * counter: (name, value) => { ... },\n * histogram: (name, value) => { ... },\n * log: (level, message) => { ... },\n * flush: async () => { ... },\n * shutdown: async () => { ... },\n * })\n *\n * // theo.config.ts\n * export default defineConfig({\n * observability: { provider: datadogAdapter },\n * })\n * ```\n */\nimport type { ObservabilityAdapter, SpanHandle, SpanAttributes } from './adapters/types.js'\n\nexport interface DefineAdapterConfig {\n name: string\n startSpan(name: string, attributes?: SpanAttributes): SpanHandle\n counter(name: string, value: number, attributes?: SpanAttributes): void\n histogram(name: string, value: number, attributes?: SpanAttributes): void\n log(level: 'debug' | 'info' | 'warn' | 'error', message: string, attributes?: SpanAttributes): void\n flush(): Promise<void>\n shutdown(): Promise<void>\n}\n\nexport function defineObservabilityAdapter(config: DefineAdapterConfig): ObservabilityAdapter {\n return {\n name: config.name,\n startSpan: (name, attrs) => config.startSpan(name, attrs),\n counter: (name, value, attrs) => config.counter(name, value, attrs),\n histogram: (name, value, attrs) => config.histogram(name, value, attrs),\n log: (level, message, attrs) => config.log(level, message, attrs),\n flush: () => config.flush(),\n shutdown: () => config.shutdown(),\n }\n}\n"],"mappings":"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","names":[]}
File without changes