theokit 0.2.4 → 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 (303) 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-6GC6VWXS.js → app-typed-client-OUJO3V5J.js} +10 -5
  5. package/dist/{app-typed-client-6GC6VWXS.js.map → app-typed-client-OUJO3V5J.js.map} +1 -1
  6. package/dist/{app-typed-client-US2ZXBEC.js → app-typed-client-YOMWSA3F.js} +11 -6
  7. package/dist/{app-typed-client-US2ZXBEC.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-MREW6MVS.js → build-D4J7XECU.js} +33 -24
  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-5OKZY2VL.js → chunk-2F4FROEQ.js} +1695 -1522
  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-X2OLD264.js → chunk-M2EY7KDR.js} +1228 -1124
  74. package/dist/chunk-M2EY7KDR.js.map +1 -0
  75. package/dist/{chunk-YWWHK7R6.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-2BQYEBCK.js → chunk-OLPB36O2.js} +87 -5
  82. package/dist/chunk-OLPB36O2.js.map +1 -0
  83. package/dist/chunk-PIVX3DYW.js +142 -0
  84. package/dist/chunk-PIVX3DYW.js.map +1 -0
  85. package/dist/{chunk-7TOMTMHT.js → chunk-R7OC6UDK.js} +2 -1
  86. package/dist/chunk-R7OC6UDK.js.map +1 -0
  87. package/dist/chunk-RESN62GB.js +269 -0
  88. package/dist/chunk-RESN62GB.js.map +1 -0
  89. package/dist/{chunk-64JI5LTO.js → chunk-RMU7EBRO.js} +2 -2
  90. package/dist/chunk-RMU7EBRO.js.map +1 -0
  91. package/dist/chunk-TQYSPYKU.js +131 -0
  92. package/dist/chunk-TQYSPYKU.js.map +1 -0
  93. package/dist/chunk-TSLSIRBR.js +107 -0
  94. package/dist/chunk-TSLSIRBR.js.map +1 -0
  95. package/dist/{chunk-XP7YXRHO.js → chunk-U6TPSW4L.js} +37 -5
  96. package/dist/chunk-U6TPSW4L.js.map +1 -0
  97. package/dist/chunk-UD3LFGDL.js +45 -0
  98. package/dist/chunk-UD3LFGDL.js.map +1 -0
  99. package/dist/chunk-UUFYP2UM.js +161 -0
  100. package/dist/chunk-UUFYP2UM.js.map +1 -0
  101. package/dist/{chunk-SNDZQ64K.js → chunk-VBZCVFSA.js} +85 -3
  102. package/dist/chunk-VBZCVFSA.js.map +1 -0
  103. package/dist/chunk-VMEWD57H.js +137 -0
  104. package/dist/chunk-VMEWD57H.js.map +1 -0
  105. package/dist/{chunk-BE2LKDI7.js → chunk-WEGALZEU.js} +3 -3
  106. package/dist/{chunk-ZOXNJV2O.js → chunk-WGHJD6I7.js} +35 -138
  107. package/dist/chunk-WGHJD6I7.js.map +1 -0
  108. package/dist/chunk-XMS5VRIK.js +0 -0
  109. package/dist/chunk-Y4H3JNVK.js +18 -0
  110. package/dist/chunk-Y4H3JNVK.js.map +1 -0
  111. package/dist/chunk-Z5IGLBWE.js +329 -0
  112. package/dist/chunk-Z5IGLBWE.js.map +1 -0
  113. package/dist/chunk-ZEGYW52B.js +26 -0
  114. package/dist/chunk-ZEGYW52B.js.map +1 -0
  115. package/dist/chunk-ZJQ4O76G.js +87 -0
  116. package/dist/chunk-ZJQ4O76G.js.map +1 -0
  117. package/dist/cli/index.js +44 -9
  118. package/dist/cli/index.js.map +1 -1
  119. package/dist/client/index.d.ts +107 -1
  120. package/dist/client/index.js +152 -6
  121. package/dist/client/index.js.map +1 -1
  122. package/dist/{cloudflare-LCAOTRYZ.js → cloudflare-OJGJ7R2D.js} +3 -3
  123. package/dist/configure-agent-registry-6YGTJYBC.js +0 -0
  124. package/dist/cookies-MJKMGJ7N.js +22 -0
  125. package/dist/csrf-BBrEZSBW.d.ts +107 -0
  126. package/dist/csrf-readiness-store-CjIoub3U.d.ts +43 -0
  127. package/dist/define-websocket-CdK94O-D.d.ts +64 -0
  128. package/dist/{deno-deploy-ZN4RE5HF.js → deno-deploy-BHWKFTOW.js} +3 -3
  129. package/dist/{dev-6P2DM33Q.js → dev-MJVSRZGF.js} +13 -13
  130. package/dist/{dev-emit-MLDFOLUU.js → dev-emit-5VPRCHOD.js} +39 -142
  131. package/dist/dev-emit-5VPRCHOD.js.map +1 -0
  132. package/dist/{dev-emit-PRSRPDHA.js → dev-emit-HHP2XJXE.js} +5 -5
  133. package/dist/devtools/entry.js +185 -131
  134. package/dist/devtools/entry.js.map +1 -1
  135. package/dist/{dispatcher-AQJGI3HU.js → dispatcher-63LBXYLE.js} +2 -2
  136. package/dist/{dispatcher-E6QV32BO.js → dispatcher-EJME6C6M.js} +5 -2
  137. package/dist/dispatcher-EJME6C6M.js.map +1 -0
  138. package/dist/{dist-E6BL4ZVY.js → dist-KRW6OVD4.js} +7 -7
  139. package/dist/dist-KRW6OVD4.js.map +1 -0
  140. package/dist/docker-EZDA5FT7.js +0 -0
  141. package/dist/error-envelope-BsNzzAV5.d.ts +62 -0
  142. package/dist/{generate-DT4IIAHF.js → generate-AZ2K6Z2Z.js} +75 -2
  143. package/dist/generate-AZ2K6Z2Z.js.map +1 -0
  144. package/dist/index-DWWEdFh5.d.ts +399 -0
  145. package/dist/index.d.ts +161 -1391
  146. package/dist/index.js +20 -8
  147. package/dist/index.js.map +1 -1
  148. package/dist/{info-FJ4NXKTB.js → info-47VB62MV.js} +5 -5
  149. package/dist/job-backend-CgC8Xf33.d.ts +68 -0
  150. package/dist/{lib-NL5JXGEB.js → lib-NST3PNZX.js} +31 -31
  151. package/dist/lib-NST3PNZX.js.map +1 -0
  152. package/dist/module-loader-Cfz7dgCP.d.ts +26 -0
  153. package/dist/{netlify-5VQ22Z2P.js → netlify-GSNSJGMN.js} +3 -3
  154. package/dist/{node-3APTLGWB.js → node-6I5B6RL3.js} +3 -3
  155. package/dist/node-6I5B6RL3.js.map +1 -0
  156. package/dist/{openapi-SOKZTFE3.js → openapi-NFLSNNVQ.js} +10 -10
  157. package/dist/plugin-runner-BGBkzgi0.d.ts +95 -0
  158. package/dist/plugin-types-DNJGxr4Z.d.ts +79 -0
  159. package/dist/{proper-lockfile-4Y3DP3RP.js → proper-lockfile-MVKMQGFK.js} +27 -27
  160. package/dist/proper-lockfile-MVKMQGFK.js.map +1 -0
  161. package/dist/rate-limit-BdNDZ3vt.d.ts +58 -0
  162. package/dist/registry-QHEZ3BWB.js +25 -0
  163. package/dist/router-RGZFX75G.js +274 -0
  164. package/dist/router-RGZFX75G.js.map +1 -0
  165. package/dist/{routes-XVRAUH6H.js → routes-3A4XGIEJ.js} +6 -6
  166. package/dist/{scan-C2BOKLSY.js → scan-NQFI5S7P.js} +2 -2
  167. package/dist/scan-NQFI5S7P.js.map +1 -0
  168. package/dist/schema-D3v8VB7o.d.ts +76 -0
  169. package/dist/{schema-VR6YNVLQ.js → schema-KZVOV333.js} +5 -3
  170. package/dist/schema-KZVOV333.js.map +1 -0
  171. package/dist/server/agent/index.d.ts +229 -0
  172. package/dist/server/agent/index.js +16 -0
  173. package/dist/server/agent/index.js.map +1 -0
  174. package/dist/server/auth/index.d.ts +46 -1
  175. package/dist/server/auth/index.js +10 -3
  176. package/dist/server/cost/index.d.ts +1 -3
  177. package/dist/server/cost/index.js +1 -1
  178. package/dist/server/cron/index.js +4 -2
  179. package/dist/server/define/index.d.ts +281 -0
  180. package/dist/server/define/index.js +26 -0
  181. package/dist/server/define/index.js.map +1 -0
  182. package/dist/server/http/index.d.ts +10 -0
  183. package/dist/server/http/index.js +74 -0
  184. package/dist/server/http/index.js.map +1 -0
  185. package/dist/server/index.d.ts +151 -1677
  186. package/dist/server/index.js +558 -1102
  187. package/dist/server/index.js.map +1 -1
  188. package/dist/server/jobs/index.d.ts +348 -2
  189. package/dist/server/jobs/index.js +5 -3
  190. package/dist/server/observability/index.d.ts +324 -0
  191. package/dist/server/observability/index.js +48 -0
  192. package/dist/server/observability/index.js.map +1 -0
  193. package/dist/server/plugins/index.d.ts +17 -0
  194. package/dist/server/plugins/index.js +17 -0
  195. package/dist/server/plugins/index.js.map +1 -0
  196. package/dist/server/rate-limit/index.d.ts +105 -0
  197. package/dist/server/rate-limit/index.js +25 -0
  198. package/dist/server/rate-limit/index.js.map +1 -0
  199. package/dist/server/realtime/index.d.ts +15 -0
  200. package/dist/server/realtime/index.js +8 -0
  201. package/dist/server/realtime/index.js.map +1 -0
  202. package/dist/server/scan/index.d.ts +106 -0
  203. package/dist/server/scan/index.js +43 -0
  204. package/dist/server/scan/index.js.map +1 -0
  205. package/dist/server/security/index.d.ts +193 -0
  206. package/dist/server/security/index.js +50 -0
  207. package/dist/server/security/index.js.map +1 -0
  208. package/dist/server/storage/index.d.ts +22 -0
  209. package/dist/server/storage/index.js +14 -0
  210. package/dist/server/storage/index.js.map +1 -0
  211. package/dist/server/webhook/index.d.ts +148 -0
  212. package/dist/server/webhook/index.js +19 -0
  213. package/dist/server/webhook/index.js.map +1 -0
  214. package/dist/server-error-to-envelope-NO4CCAFQ.js +8 -0
  215. package/dist/server-error-to-envelope-NO4CCAFQ.js.map +1 -0
  216. package/dist/server-error-to-envelope-UJK6OWDJ.js +134 -0
  217. package/dist/server-error-to-envelope-UJK6OWDJ.js.map +1 -0
  218. package/dist/server-routes-hmr-GZJGPWMS.js +44 -0
  219. package/dist/server-routes-hmr-GZJGPWMS.js.map +1 -0
  220. package/dist/server-routes-hmr-PBHSKCQJ.js +42 -0
  221. package/dist/server-routes-hmr-PBHSKCQJ.js.map +1 -0
  222. package/dist/services-json-Z2QNGVPW.js +142 -0
  223. package/dist/services-json-Z2QNGVPW.js.map +1 -0
  224. package/dist/{services-typed-client-XWYYBWGW.js → services-typed-client-KK6RHRDG.js} +2 -2
  225. package/dist/{services-typed-client-ZX5JUHH3.js → services-typed-client-T7M5VPBP.js} +2 -2
  226. package/dist/{sqlite-vec-ARPNUBWT.js → sqlite-vec-54KB4RD3.js} +2 -2
  227. package/dist/sqlite-vec-54KB4RD3.js.map +1 -0
  228. package/dist/{start-R7XSQL5L.js → start-CGSZPBAG.js} +23 -23
  229. package/dist/start-CGSZPBAG.js.map +1 -0
  230. package/dist/{static-TZBVBDTB.js → static-QI5NQT2X.js} +6 -6
  231. package/dist/storage-manager-C4jsO0Tp.d.ts +89 -0
  232. package/dist/storage-manager-D5KBXXKB.js +0 -0
  233. package/dist/{storage-types-DtIVejC1.d.ts → storage-types-DsDTCPbp.d.ts} +1 -1
  234. package/dist/{theo-cloud-3K4DGB3S.js → theo-cloud-RSQCBNXL.js} +4 -4
  235. package/dist/theo-cloud-RSQCBNXL.js.map +1 -0
  236. package/dist/upgrade-readiness-GUJBCJIS.js +0 -0
  237. package/dist/{vercel-MWW24ABC.js → vercel-TKPZK62A.js} +3 -3
  238. package/dist/vite-plugin/index.d.ts +3 -1
  239. package/dist/vite-plugin/index.js +20 -8
  240. package/dist/{vite-plugin-6MPHTKIW.js → vite-plugin-CR4JDFUQ.js} +9 -9
  241. package/dist/vite-plugin-CR4JDFUQ.js.map +1 -0
  242. package/package.json +59 -10
  243. package/LICENSE +0 -201
  244. package/dist/build-MREW6MVS.js.map +0 -1
  245. package/dist/chunk-2BQYEBCK.js.map +0 -1
  246. package/dist/chunk-5OFPQK6N.js.map +0 -1
  247. package/dist/chunk-5OKZY2VL.js.map +0 -1
  248. package/dist/chunk-64JI5LTO.js.map +0 -1
  249. package/dist/chunk-7TOMTMHT.js.map +0 -1
  250. package/dist/chunk-C52GSJPF.js.map +0 -1
  251. package/dist/chunk-CZM6WWWS.js +0 -2450
  252. package/dist/chunk-CZM6WWWS.js.map +0 -1
  253. package/dist/chunk-KJVEJILQ.js.map +0 -1
  254. package/dist/chunk-O4BLVPML.js.map +0 -1
  255. package/dist/chunk-O7335ZGH.js.map +0 -1
  256. package/dist/chunk-PB4GR7EP.js.map +0 -1
  257. package/dist/chunk-SNDZQ64K.js.map +0 -1
  258. package/dist/chunk-TPCT3MXV.js.map +0 -1
  259. package/dist/chunk-VRHXOKRP.js.map +0 -1
  260. package/dist/chunk-WZ7CPVQB.js.map +0 -1
  261. package/dist/chunk-X2OLD264.js.map +0 -1
  262. package/dist/chunk-X4JAX2U3.js.map +0 -1
  263. package/dist/chunk-XP7YXRHO.js.map +0 -1
  264. package/dist/chunk-YLBSSEHX.js.map +0 -1
  265. package/dist/chunk-ZOXNJV2O.js.map +0 -1
  266. package/dist/dev-emit-MLDFOLUU.js.map +0 -1
  267. package/dist/dispatcher-E6QV32BO.js.map +0 -1
  268. package/dist/dist-E6BL4ZVY.js.map +0 -1
  269. package/dist/generate-DT4IIAHF.js.map +0 -1
  270. package/dist/index-li83Swxa.d.ts +0 -506
  271. package/dist/lib-NL5JXGEB.js.map +0 -1
  272. package/dist/proper-lockfile-4Y3DP3RP.js.map +0 -1
  273. package/dist/registry-5QFTLOL2.js +0 -25
  274. package/dist/schema-CjVly9c_.d.ts +0 -274
  275. package/dist/sqlite-vec-ARPNUBWT.js.map +0 -1
  276. package/dist/start-R7XSQL5L.js.map +0 -1
  277. package/dist/theo-cloud-3K4DGB3S.js.map +0 -1
  278. /package/dist/{actions-virtual-module-HWNTIEPI.js.map → actions-virtual-module-IY46EEEX.js.map} +0 -0
  279. /package/dist/{actions-virtual-module-SEIPACG5.js.map → actions-virtual-module-XNHDNCZ6.js.map} +0 -0
  280. /package/dist/{aws-lambda-XYCGZH3I.js.map → aws-lambda-GKSTX6HD.js.map} +0 -0
  281. /package/dist/{bun-K73TQEWC.js.map → bun-ISHSNFIO.js.map} +0 -0
  282. /package/dist/{check-PZR67OY5.js.map → check-NXYPLM6M.js.map} +0 -0
  283. /package/dist/{dispatcher-AQJGI3HU.js.map → chunk-E3JH6YUM.js.map} +0 -0
  284. /package/dist/{node-3APTLGWB.js.map → chunk-JHEAWR3L.js.map} +0 -0
  285. /package/dist/{chunk-ZJW5FFYJ.js.map → chunk-LC5PYNJP.js.map} +0 -0
  286. /package/dist/{chunk-YWWHK7R6.js.map → chunk-MR7OLIC6.js.map} +0 -0
  287. /package/dist/{scan-C2BOKLSY.js.map → chunk-NPMCKOX4.js.map} +0 -0
  288. /package/dist/{chunk-BE2LKDI7.js.map → chunk-WEGALZEU.js.map} +0 -0
  289. /package/dist/{cloudflare-LCAOTRYZ.js.map → cloudflare-OJGJ7R2D.js.map} +0 -0
  290. /package/dist/{schema-VR6YNVLQ.js.map → cookies-MJKMGJ7N.js.map} +0 -0
  291. /package/dist/{deno-deploy-ZN4RE5HF.js.map → deno-deploy-BHWKFTOW.js.map} +0 -0
  292. /package/dist/{dev-6P2DM33Q.js.map → dev-MJVSRZGF.js.map} +0 -0
  293. /package/dist/{dev-emit-PRSRPDHA.js.map → dev-emit-HHP2XJXE.js.map} +0 -0
  294. /package/dist/{vite-plugin-6MPHTKIW.js.map → dispatcher-63LBXYLE.js.map} +0 -0
  295. /package/dist/{info-FJ4NXKTB.js.map → info-47VB62MV.js.map} +0 -0
  296. /package/dist/{netlify-5VQ22Z2P.js.map → netlify-GSNSJGMN.js.map} +0 -0
  297. /package/dist/{openapi-SOKZTFE3.js.map → openapi-NFLSNNVQ.js.map} +0 -0
  298. /package/dist/{registry-5QFTLOL2.js.map → registry-QHEZ3BWB.js.map} +0 -0
  299. /package/dist/{routes-XVRAUH6H.js.map → routes-3A4XGIEJ.js.map} +0 -0
  300. /package/dist/{services-typed-client-XWYYBWGW.js.map → services-typed-client-KK6RHRDG.js.map} +0 -0
  301. /package/dist/{services-typed-client-ZX5JUHH3.js.map → services-typed-client-T7M5VPBP.js.map} +0 -0
  302. /package/dist/{static-TZBVBDTB.js.map → static-QI5NQT2X.js.map} +0 -0
  303. /package/dist/{vercel-MWW24ABC.js.map → vercel-TKPZK62A.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/vite-plugin/openapi-emit/emit.ts","../src/vite-plugin/openapi-emit/zod-to-openapi.ts","../src/vite-plugin/openapi-emit/load-routes.ts"],"sourcesContent":["/**\n * In-house `emitOpenApi()` orchestrator (build-time only).\n *\n * Pure transformation: takes a route manifest (already hydrated with Zod\n * schemas extracted from `defineRoute()` config) + the openapi config\n * block, builds an OpenAPI 3.x document, writes `<outDir>/openapi.json`,\n * returns the document for downstream callers (CLI logs, tests).\n *\n * Per G2 plan v1.1 T2.1. Modelled on trpc-openapi's\n * `generateOpenApiDocument` + encore's `Generator.Generate()`.\n *\n * NEVER ship to runtime — devDep-shaped artifact.\n *\n * Path templating: TheoKit's `:param` syntax is converted to OpenAPI's\n * `{param}` per the spec. Env-var override: `THEOKIT_OPENAPI_SERVER_URL`\n * overrides `servers[0].url` without rebuilding the config.\n */\n/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time emit. outDir is a config-controlled relative path. The\n * caller already refused absolute paths via theoConfigSchema.distDir\n * pattern; the openapi.outDir field follows the same convention.\n */\nimport { mkdirSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport type { z } from 'zod'\n\nimport { zodToOpenApiSchema, type ConvertCtx, type OpenApiSchema } from './zod-to-openapi.js'\n\nexport interface OpenApiManifestRoute {\n /** TheoKit-style path with `:param` placeholders (`/users/:id`). */\n routePath: string\n /** Uppercase HTTP methods the route exports (`['GET','POST']`). */\n methods: string[]\n /** Optional `defineRoute({body})` schema. */\n body?: z.ZodType\n /** Optional `defineRoute({query})` schema. */\n query?: z.ZodType\n /** Optional `defineRoute({params})` schema. */\n params?: z.ZodType\n /** Optional `defineRoute({response})` schema. */\n response?: z.ZodType\n}\n\nexport interface OpenApiEmitConfig {\n servers: { url: string; description?: string }[]\n specVersion: '3.1.0' | '3.0.3'\n title: string\n version: string\n outDir: string\n}\n\nexport interface OpenApiOperation {\n operationId?: string\n parameters?: OpenApiParameter[]\n requestBody?: {\n required?: boolean\n content: Record<string, { schema: OpenApiSchema }>\n }\n responses: Record<\n string,\n { description: string; content?: Record<string, { schema: OpenApiSchema }> }\n >\n}\n\nexport interface OpenApiParameter {\n name: string\n in: 'path' | 'query' | 'header' | 'cookie'\n required: boolean\n schema: OpenApiSchema\n description?: string\n}\n\nexport type OpenApiPathItem = Partial<\n Record<'get' | 'post' | 'put' | 'patch' | 'delete' | 'head' | 'options', OpenApiOperation>\n>\n\nexport interface OpenApiDocument {\n openapi: '3.1.0' | '3.0.3'\n info: { title: string; version: string }\n servers: { url: string; description?: string }[]\n paths: Record<string, OpenApiPathItem>\n components?: { schemas?: Record<string, OpenApiSchema> }\n}\n\nexport interface EmitOpenApiInput {\n manifest: OpenApiManifestRoute[]\n config: OpenApiEmitConfig\n}\n\nexport interface EmitOpenApiResult {\n document: OpenApiDocument\n path: string\n}\n\nconst METHOD_TO_KEY: Record<string, keyof OpenApiPathItem> = {\n GET: 'get',\n POST: 'post',\n PUT: 'put',\n PATCH: 'patch',\n DELETE: 'delete',\n HEAD: 'head',\n OPTIONS: 'options',\n}\n\n/** Convert `/users/:id/posts/:postId?` → `/users/{id}/posts/{postId}`. */\nexport function templatePath(routePath: string): string {\n // Bounded quantifier (\\w{0,64}) is OSS-safe — TheoKit route param names\n // never exceed 64 chars in practice; cap prevents super-linear backtracking.\n return routePath.replace(/:([A-Za-z_]\\w{0,64})\\??/g, '{$1}')\n}\n\nfunction buildParameters(\n paramsSchema: z.ZodType | undefined,\n querySchema: z.ZodType | undefined,\n ctx: ConvertCtx,\n): OpenApiParameter[] {\n const out: OpenApiParameter[] = []\n if (paramsSchema !== undefined) {\n appendObjectAsParameters(paramsSchema, 'path', out, ctx)\n }\n if (querySchema !== undefined) {\n appendObjectAsParameters(querySchema, 'query', out, ctx)\n }\n return out\n}\n\nfunction appendObjectAsParameters(\n schema: z.ZodType,\n location: 'path' | 'query',\n out: OpenApiParameter[],\n ctx: ConvertCtx,\n): void {\n // We expect an object schema; if it's not, skip silently (caller already\n // validated upstream when scanning defineRoute()).\n const shape = (schema as unknown as { shape?: Record<string, z.ZodType> }).shape\n if (shape === undefined) return\n for (const [name, child] of Object.entries(shape)) {\n const childDef = (child as unknown as { _def: { typeName?: string } })._def\n const optional = childDef.typeName === 'ZodOptional' || childDef.typeName === 'ZodDefault'\n out.push({\n name,\n in: location,\n required: location === 'path' ? true : !optional,\n schema: zodToOpenApiSchema(child, { ctx }),\n })\n }\n}\n\nfunction buildOperation(\n route: OpenApiManifestRoute,\n ctx: ConvertCtx,\n method: string,\n): OpenApiOperation {\n // Bounded sanitizer: two single-pass replacements (no nested quantifiers).\n const sanitized = route.routePath.replace(/[/:\\-{}]/g, '_').replace(/_+/g, '_')\n const operationId = `${method.toLowerCase()}_${sanitized.replace(/^_|_$/g, '')}`\n const op: OpenApiOperation = {\n operationId,\n responses: {\n '200': { description: 'OK' },\n },\n }\n\n const parameters = buildParameters(route.params, route.query, ctx)\n if (parameters.length > 0) op.parameters = parameters\n\n // Body only meaningful for write methods; we still attach if defined so\n // the document is honest about the contract.\n if (route.body !== undefined) {\n op.requestBody = {\n required: true,\n content: {\n 'application/json': { schema: zodToOpenApiSchema(route.body, { ctx }) },\n },\n }\n }\n\n if (route.response !== undefined) {\n op.responses['200'] = {\n description: 'OK',\n content: {\n 'application/json': { schema: zodToOpenApiSchema(route.response, { ctx }) },\n },\n }\n }\n\n return op\n}\n\nexport function emitOpenApi(input: EmitOpenApiInput): EmitOpenApiResult {\n const { manifest, config } = input\n\n const ctx: ConvertCtx = { seen: new Map(), components: {} }\n\n const paths: Record<string, OpenApiPathItem> = {}\n for (const route of manifest) {\n const templatedPath = templatePath(route.routePath)\n const item: OpenApiPathItem = paths[templatedPath] ?? {}\n for (const method of route.methods) {\n // Defensive: manifest emitter normalizes to uppercase, but if a\n // future scanner produces a non-HTTP verb the key lookup yields\n // undefined and we skip silently. TS narrows the indexed type to\n // keyof OpenApiPathItem; the runtime undefined check is honest.\n const key = METHOD_TO_KEY[method.toUpperCase()] as keyof OpenApiPathItem | undefined\n if (key === undefined) continue\n item[key] = buildOperation(route, ctx, method)\n }\n paths[templatedPath] = item\n }\n\n const servers = applyServerOverride(config.servers)\n\n const document: OpenApiDocument = {\n openapi: config.specVersion,\n info: { title: config.title, version: config.version },\n servers,\n paths,\n }\n\n if (Object.keys(ctx.components).length > 0) {\n document.components = { schemas: ctx.components }\n }\n\n mkdirSync(config.outDir, { recursive: true })\n const outPath = join(config.outDir, 'openapi.json')\n writeFileSync(outPath, `${JSON.stringify(document, null, 2)}\\n`, 'utf8')\n\n return { document, path: outPath }\n}\n\nfunction applyServerOverride(\n servers: { url: string; description?: string }[],\n): { url: string; description?: string }[] {\n const override = process.env.THEOKIT_OPENAPI_SERVER_URL\n if (override === undefined || override === '') return servers\n if (servers.length === 0) return [{ url: override }]\n const [first, ...rest] = servers\n return [\n {\n url: override,\n ...(first.description !== undefined ? { description: first.description } : {}),\n },\n ...rest,\n ]\n}\n","/**\n * Zod → OpenAPI 3.0 Schema converter (build-time only).\n *\n * Uses Zod v4's native `z.toJSONSchema()` and post-processes\n * the JSON Schema output into OpenAPI 3.0 format:\n * - Removes `$schema` (invalid in OpenAPI 3.0)\n * - Converts `type: [\"string\", \"null\"]` → `{ type: \"string\", nullable: true }`\n * - Recursively normalizes nested schemas\n *\n * NEVER ship to runtime — devDep-shaped artifact.\n */\nimport { z } from 'zod'\n\nexport interface OpenApiSchema {\n type?: 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'object' | 'null'\n format?: string\n items?: OpenApiSchema\n properties?: Record<string, OpenApiSchema>\n required?: string[]\n additionalProperties?: boolean | OpenApiSchema\n oneOf?: OpenApiSchema[]\n anyOf?: OpenApiSchema[]\n allOf?: OpenApiSchema[]\n discriminator?: { propertyName: string; mapping?: Record<string, string> }\n enum?: unknown[]\n nullable?: boolean\n description?: string\n default?: unknown\n $ref?: string\n}\n\nexport interface ConvertCtx {\n seen: Map<z.ZodType, string>\n components: Record<string, OpenApiSchema>\n}\n\nexport interface ConvertOptions {\n ctx?: ConvertCtx\n name?: string\n}\n\nexport class ZodToOpenApiError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'ZodToOpenApiError'\n }\n}\n\nexport function zodToOpenApiSchema(schema: z.ZodType, options: ConvertOptions = {}): OpenApiSchema {\n const ctx: ConvertCtx = options.ctx ?? { seen: new Map(), components: {} }\n\n const name = options.name\n if (name !== undefined) {\n if (Object.prototype.hasOwnProperty.call(ctx.components, name)) {\n return { $ref: `#/components/schemas/${name}` }\n }\n ctx.seen.set(schema, name)\n ctx.components[name] = {}\n const body = convertSchema(schema, ctx)\n ctx.components[name] = body\n return { $ref: `#/components/schemas/${name}` }\n }\n\n return convertSchema(schema, ctx)\n}\n\nfunction convertSchema(schema: z.ZodType, ctx: ConvertCtx): OpenApiSchema {\n const existing = ctx.seen.get(schema)\n if (existing !== undefined) {\n return { $ref: `#/components/schemas/${existing}` }\n }\n\n try {\n const jsonSchema = z.toJSONSchema(schema) as Record<string, unknown>\n return toOpenApi3(jsonSchema)\n } catch {\n return {}\n }\n}\n\n/**\n * Post-process JSON Schema (draft-2020-12) → OpenAPI 3.0.\n * Handles:\n * - $schema removal\n * - nullable conversion\n * - Recursive normalization of properties/items/anyOf/oneOf/allOf\n */\nfunction toOpenApi3(js: Record<string, unknown>): OpenApiSchema {\n const { $schema: _, $defs: _defs, ...rest } = js\n const out: Record<string, unknown> = {}\n\n for (const [key, value] of Object.entries(rest)) {\n if (key === 'type' && Array.isArray(value)) {\n const types = value as string[]\n const hasNull = types.includes('null')\n const nonNull = types.filter((t: string) => t !== 'null')\n out.type = nonNull.length === 1 ? nonNull[0] : nonNull\n if (hasNull) out.nullable = true\n } else if (key === 'properties' && value && typeof value === 'object') {\n const props: Record<string, OpenApiSchema> = {}\n for (const [pk, pv] of Object.entries(value as Record<string, unknown>)) {\n props[pk] = pv && typeof pv === 'object' ? toOpenApi3(pv as Record<string, unknown>) : (pv as OpenApiSchema)\n }\n out.properties = props\n } else if (key === 'items' && value && typeof value === 'object') {\n out.items = toOpenApi3(value as Record<string, unknown>)\n } else if ((key === 'anyOf' || key === 'oneOf' || key === 'allOf') && Array.isArray(value)) {\n out[key] = (value as Record<string, unknown>[]).map((v) => toOpenApi3(v))\n } else if (key === 'additionalProperties' && value && typeof value === 'object') {\n out.additionalProperties = toOpenApi3(value as Record<string, unknown>)\n } else {\n out[key] = value\n }\n }\n\n return out as OpenApiSchema\n}\n","/**\n * Build-time route loader for OpenAPI emit.\n *\n * Takes the G1 manifest (filePath + routePath + methods) and hydrates each\n * route's Zod schemas by dynamically importing the module. `defineRoute`\n * is an identity function, so the imported config exposes `body/query/\n * params/response` directly without any AST gymnastics.\n *\n * Supports both export shapes:\n * 1. `export const POST = defineRoute({...})` (per-method named exports)\n * 2. `export default defineRoute({...})` (single-export legacy shape)\n *\n * Per G2 plan v1.1 T2.2. Uses Vite's `ssrLoadModule` because route files\n * are TS at build time — Node's native loader doesn't transpile.\n */\nimport { resolve } from 'node:path'\n\nimport type { z } from 'zod'\n\nimport type { ManifestRoute } from '../../server/scan/manifest.js'\n\nimport type { OpenApiManifestRoute } from './emit.js'\n\ninterface RouteConfigShape {\n body?: z.ZodType\n query?: z.ZodType\n params?: z.ZodType\n response?: z.ZodType\n}\n\ninterface ViteLikeServer {\n ssrLoadModule: (id: string) => Promise<Record<string, unknown>>\n close: () => Promise<void>\n}\n\ninterface DefaultLoader {\n load: (absPath: string) => Promise<Record<string, unknown>>\n dispose: () => Promise<void>\n}\n\n/** Method names recognized as per-route named exports (UPPERCASE). */\nconst HTTP_METHOD_KEYS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'] as const\n\nexport interface LoadRoutesOptions {\n serverDir: string\n routes: ManifestRoute[]\n /** Injectable for tests; defaults to a fresh Vite SSR dev server. */\n loadModule?: (absPath: string) => Promise<Record<string, unknown>>\n}\n\n/**\n * Hydrate manifest routes with Zod schemas extracted from their modules.\n *\n * Returns one `OpenApiManifestRoute` per (file × method) tuple. If a route\n * file declares multiple methods (`GET` + `POST`), each method gets its\n * own entry with the schemas from that method's `defineRoute()` call.\n *\n * Routes that fail to load are skipped with a console warning — the emit\n * should be best-effort, not a hard build failure.\n *\n * The default Vite loader is created once per call and disposed in the\n * `finally` block so the underlying server is released even when route\n * loading throws.\n */\nexport async function loadRoutesForOpenApi(\n options: LoadRoutesOptions,\n): Promise<OpenApiManifestRoute[]> {\n const { serverDir, routes } = options\n let loadModule = options.loadModule\n let dispose: (() => Promise<void>) | undefined\n if (loadModule === undefined) {\n const created = await createDefaultLoader(serverDir)\n loadModule = created.load\n dispose = created.dispose\n }\n\n const out: OpenApiManifestRoute[] = []\n try {\n for (const r of routes) {\n const abs = resolve(serverDir, r.filePath)\n let mod: Record<string, unknown>\n try {\n mod = await loadModule(abs)\n } catch (err) {\n console.warn(\n `[openapi-emit] Skipping ${r.filePath}: load failed (${(err as Error).message})`,\n )\n continue\n }\n\n const methods = r.methods ?? HTTP_METHOD_KEYS.filter((m) => mod[m] !== undefined)\n if (methods.length === 0) {\n // Fall back to default export when no method-named exports detected.\n const def = mod.default\n if (def !== undefined && def !== null) {\n out.push({ routePath: r.routePath, methods: ['GET'], ...extractSchemas(def) })\n }\n continue\n }\n\n for (const method of methods) {\n const handler = mod[method] ?? mod.default\n if (handler === undefined || handler === null) continue\n out.push({ routePath: r.routePath, methods: [method], ...extractSchemas(handler) })\n }\n }\n } finally {\n if (dispose !== undefined) {\n await dispose()\n }\n }\n return out\n}\n\nfunction extractSchemas(handlerExport: unknown): RouteConfigShape {\n if (typeof handlerExport !== 'object' || handlerExport === null) return {}\n const cfg = handlerExport as RouteConfigShape\n const out: RouteConfigShape = {}\n if (cfg.body !== undefined) out.body = cfg.body\n if (cfg.query !== undefined) out.query = cfg.query\n if (cfg.params !== undefined) out.params = cfg.params\n if (cfg.response !== undefined) out.response = cfg.response\n return out\n}\n\n/**\n * Default loader: spin up a minimal Vite SSR dev server (no HMR, no\n * middlewares) just to use its `ssrLoadModule`. Vite handles TS, ESM, and\n * import resolution for free.\n *\n * Returns `{load, dispose}` — the caller owns the lifecycle and MUST\n * call `dispose()` (typically in a `finally`) so the server is closed.\n */\nasync function createDefaultLoader(serverDir: string): Promise<DefaultLoader> {\n // Vite is a peerDep in build flows; import dynamically.\n const { createServer } = await import('vite')\n const server = (await createServer({\n root: serverDir,\n configFile: false,\n server: { middlewareMode: true },\n appType: 'custom',\n logLevel: 'silent',\n })) as unknown as ViteLikeServer\n\n const swallow = (): void => {\n /* eat close errors — best-effort cleanup */\n }\n return {\n load: async (absPath) => server.ssrLoadModule(absPath),\n dispose: async () => {\n await server.close().catch(swallow)\n },\n }\n}\n"],"mappings":"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","names":[]}
File without changes
@@ -0,0 +1,18 @@
1
+ // src/server/_internal/atomic-write.ts
2
+ import { mkdirSync, renameSync, writeFileSync } from "fs";
3
+ import { dirname } from "path";
4
+ function writeAtomic(path, content) {
5
+ mkdirSync(dirname(path), { recursive: true });
6
+ const randBuf = new Uint8Array(4);
7
+ globalThis.crypto.getRandomValues(randBuf);
8
+ let suffix = "";
9
+ for (const b of randBuf) suffix += b.toString(16).padStart(2, "0");
10
+ const tmp = `${path}.tmp-${process.pid}-${suffix}`;
11
+ writeFileSync(tmp, content, "utf8");
12
+ renameSync(tmp, path);
13
+ }
14
+
15
+ export {
16
+ writeAtomic
17
+ };
18
+ //# sourceMappingURL=chunk-Y4H3JNVK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/_internal/atomic-write.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time atomic write: caller-controlled paths only. No HTTP input\n * reaches these fs calls.\n */\n// T5a.1b — Web Crypto migration. Build-time only; node:fs + node:path stay\n// because this is a manifest-write leaf (e.g. .theo/jobs.json) and per\n// ADR-0028 the runtime-portable boundary is the request handler, not the\n// scanner. Only node:crypto is swapped out — Web Crypto's\n// getRandomValues works in every supported runtime.\nimport { mkdirSync, renameSync, writeFileSync } from 'node:fs'\nimport { dirname } from 'node:path'\n\n/**\n * Write `content` to `path` atomically via the tmp + rename pattern.\n *\n * Two concurrent calls to `writeAtomic(path, ...)` are guaranteed to\n * leave `path` containing valid content from ONE of the calls (never\n * truncated, never interleaved). POSIX rename is atomic on the same\n * filesystem.\n *\n * EC-106 (jobs-crons-webhooks-cost-tracking-plan) — shared helper for\n * `.theo/crons.json` and `.theo/jobs.json` manifest writes so a\n * concurrent dev-server scan + build manifest emit never produces\n * partial JSON.\n *\n * @param path destination path\n * @param content bytes (UTF-8 string) to write\n */\nexport function writeAtomic(path: string, content: string): void {\n mkdirSync(dirname(path), { recursive: true })\n // Include a random suffix so two concurrent writes don't trample each\n // other's tmp file. Web Crypto getRandomValues is non-blocking +\n // collision-safe for the tiny entropy we need (8 hex chars = 32 bits).\n // Hex-encoded manually to avoid Buffer (runtime-agnostic per ADR-0028).\n const randBuf = new Uint8Array(4)\n globalThis.crypto.getRandomValues(randBuf)\n let suffix = ''\n for (const b of randBuf) suffix += b.toString(16).padStart(2, '0')\n const tmp = `${path}.tmp-${process.pid}-${suffix}`\n writeFileSync(tmp, content, 'utf8')\n renameSync(tmp, path)\n}\n"],"mappings":";AASA,SAAS,WAAW,YAAY,qBAAqB;AACrD,SAAS,eAAe;AAkBjB,SAAS,YAAY,MAAc,SAAuB;AAC/D,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAK5C,QAAM,UAAU,IAAI,WAAW,CAAC;AAChC,aAAW,OAAO,gBAAgB,OAAO;AACzC,MAAI,SAAS;AACb,aAAW,KAAK,QAAS,WAAU,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AACjE,QAAM,MAAM,GAAG,IAAI,QAAQ,QAAQ,GAAG,IAAI,MAAM;AAChD,gBAAc,KAAK,SAAS,MAAM;AAClC,aAAW,KAAK,IAAI;AACtB;","names":[]}
@@ -0,0 +1,329 @@
1
+ // src/server/body-parser.ts
2
+ import { basename } from "path";
3
+ import Busboy from "busboy";
4
+ var METHODS_WITH_BODY = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH"]);
5
+ var DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
6
+ var DEFAULT_MAX_FILES = 10;
7
+ var DEFAULT_MAX_FIELD_SIZE = 1 * 1024 * 1024;
8
+ var FileTooLargeError = class extends Error {
9
+ constructor(message, truncatedFilenames, maxFileSize) {
10
+ super(message);
11
+ this.truncatedFilenames = truncatedFilenames;
12
+ this.maxFileSize = maxFileSize;
13
+ this.name = "FileTooLargeError";
14
+ }
15
+ truncatedFilenames;
16
+ maxFileSize;
17
+ code = "FILE_TOO_LARGE";
18
+ status = 413;
19
+ };
20
+ function stashBodyPreview(req, body) {
21
+ try {
22
+ ;
23
+ req[DEVTOOLS_BODY_PREVIEW] = body;
24
+ } catch {
25
+ }
26
+ }
27
+ function parseJsonBody(req) {
28
+ return new Promise((resolve, reject) => {
29
+ const chunks = [];
30
+ req.on("data", (chunk) => chunks.push(chunk));
31
+ req.on("end", () => {
32
+ const raw = Buffer.concat(chunks).toString();
33
+ if (!raw) {
34
+ resolve(void 0);
35
+ return;
36
+ }
37
+ try {
38
+ resolve(JSON.parse(raw));
39
+ } catch {
40
+ reject(new Error("Invalid JSON body"));
41
+ }
42
+ });
43
+ req.on("error", reject);
44
+ });
45
+ }
46
+ function parseMultipartBody(req, contentType, options) {
47
+ if (!contentType.includes("boundary=")) {
48
+ return Promise.reject(new Error("Missing multipart boundary"));
49
+ }
50
+ return new Promise((resolve, reject) => {
51
+ const fields = {};
52
+ const files = [];
53
+ const truncatedFilenames = [];
54
+ let fileCount = 0;
55
+ const bb = Busboy({
56
+ headers: req.headers,
57
+ limits: {
58
+ fileSize: options.maxFileSize,
59
+ files: options.maxFiles,
60
+ fieldSize: options.maxFieldSize
61
+ }
62
+ });
63
+ bb.on("field", (name, value) => {
64
+ fields[name] = value;
65
+ });
66
+ bb.on(
67
+ "file",
68
+ (fieldname, stream, info) => {
69
+ fileCount++;
70
+ if (fileCount > options.maxFiles) {
71
+ stream.resume();
72
+ return;
73
+ }
74
+ const safeName = basename(info.filename || "unnamed");
75
+ const chunks = [];
76
+ let size = 0;
77
+ let truncated = false;
78
+ stream.on("data", (chunk) => {
79
+ size += chunk.length;
80
+ if (size > options.maxFileSize) {
81
+ truncated = true;
82
+ stream.resume();
83
+ return;
84
+ }
85
+ chunks.push(chunk);
86
+ });
87
+ stream.on("end", () => {
88
+ if (truncated) {
89
+ truncatedFilenames.push(safeName);
90
+ return;
91
+ }
92
+ const buffer = Buffer.concat(chunks);
93
+ files.push({
94
+ fieldname,
95
+ filename: safeName,
96
+ encoding: info.encoding,
97
+ mimeType: info.mimeType,
98
+ buffer,
99
+ size: buffer.length
100
+ });
101
+ });
102
+ }
103
+ );
104
+ bb.on("filesLimit", () => {
105
+ reject(new Error(`Too many files. Maximum: ${options.maxFiles}`));
106
+ });
107
+ bb.on("error", (err) => {
108
+ reject(err);
109
+ });
110
+ bb.on("close", () => {
111
+ if (truncatedFilenames.length > 0) {
112
+ reject(
113
+ new FileTooLargeError(
114
+ `File too large (max ${options.maxFileSize} bytes): ${truncatedFilenames.join(", ")}`,
115
+ truncatedFilenames,
116
+ options.maxFileSize
117
+ )
118
+ );
119
+ return;
120
+ }
121
+ resolve({ fields, files });
122
+ });
123
+ req.pipe(bb);
124
+ });
125
+ }
126
+ var DEVTOOLS_BODY_PREVIEW = /* @__PURE__ */ Symbol("theo:devtools:bodyPreview");
127
+ async function parseRequestBody(req, options) {
128
+ const method = req.method?.toUpperCase() ?? "GET";
129
+ if (!METHODS_WITH_BODY.has(method)) {
130
+ return { fields: {}, files: [], json: void 0 };
131
+ }
132
+ const contentType = req.headers["content-type"] ?? "";
133
+ if (contentType.includes("application/json")) {
134
+ const json = await parseJsonBody(req);
135
+ stashBodyPreview(req, json);
136
+ return { fields: {}, files: [], json };
137
+ }
138
+ if (contentType.includes("multipart/form-data")) {
139
+ const limits = {
140
+ maxFileSize: options?.maxFileSize ?? DEFAULT_MAX_FILE_SIZE,
141
+ maxFiles: options?.maxFiles ?? DEFAULT_MAX_FILES,
142
+ maxFieldSize: options?.maxFieldSize ?? DEFAULT_MAX_FIELD_SIZE
143
+ };
144
+ const { fields, files } = await parseMultipartBody(req, contentType, limits);
145
+ stashBodyPreview(req, {
146
+ fields,
147
+ files: files.map((f) => ({
148
+ fieldname: f.fieldname,
149
+ filename: f.filename,
150
+ mimeType: f.mimeType,
151
+ size: f.size
152
+ }))
153
+ });
154
+ return { fields, files };
155
+ }
156
+ if (!contentType) {
157
+ return { fields: {}, files: [] };
158
+ }
159
+ throw new Error(`Unsupported Content-Type: ${contentType}`);
160
+ }
161
+
162
+ // src/server/observability/request-log.ts
163
+ var defaultLoggerFn = (log) => {
164
+ console.log(JSON.stringify(log));
165
+ };
166
+ function logRequest(info, customLogger, req) {
167
+ const log = {
168
+ level: "info",
169
+ ...info,
170
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
171
+ };
172
+ const logger = customLogger ?? defaultLoggerFn;
173
+ logger(log);
174
+ broadcastRequestToDevtools(info, req);
175
+ }
176
+ function randomRequestId() {
177
+ return `req-${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
178
+ }
179
+ function broadcastToDevtoolsCore(info, headers, stashedBody) {
180
+ void Promise.all([
181
+ import("./broadcast-4SX7R5XE.js"),
182
+ import("./build-request-body-preview-EOP2Y4F3.js")
183
+ ]).then(([{ broadcastRequest }, { buildRequestBodyPreview }]) => {
184
+ const bodyInfo = buildRequestBodyPreview(stashedBody);
185
+ broadcastRequest({
186
+ id: randomRequestId(),
187
+ traceId: info.requestId,
188
+ method: info.method,
189
+ path: info.url,
190
+ status: info.status,
191
+ durationMs: info.duration,
192
+ startedAt: Date.now() - info.duration,
193
+ ...headers ? { headers } : {},
194
+ ...bodyInfo ? {
195
+ bodyPreview: bodyInfo.preview,
196
+ bodyLength: bodyInfo.length,
197
+ bodyTruncated: bodyInfo.truncated
198
+ } : {}
199
+ });
200
+ }).catch(() => {
201
+ });
202
+ }
203
+ function broadcastRequestToDevtools(info, req) {
204
+ const headers = req?.headers ? Object.fromEntries(
205
+ Object.entries(req.headers).map(([k, v]) => [
206
+ k,
207
+ Array.isArray(v) ? v.join(", ") : v ?? ""
208
+ ])
209
+ ) : void 0;
210
+ const stashedBody = req ? req[DEVTOOLS_BODY_PREVIEW] : void 0;
211
+ broadcastToDevtoolsCore(info, headers, stashedBody);
212
+ }
213
+
214
+ // src/server/observability/logger.ts
215
+ var LEVEL_ORDER = {
216
+ debug: 0,
217
+ info: 1,
218
+ warn: 2,
219
+ error: 3,
220
+ silent: 4
221
+ };
222
+ function shouldLog(msgLevel, configLevel) {
223
+ return LEVEL_ORDER[msgLevel] >= LEVEL_ORDER[configLevel];
224
+ }
225
+ function defaultOutput(log) {
226
+ console.log(JSON.stringify(log));
227
+ }
228
+ function createLogger(options) {
229
+ const level = options?.level ?? "info";
230
+ const output = options?.output ?? defaultOutput;
231
+ const baseContext = options?.context ?? {};
232
+ function log(msgLevel, msg, context) {
233
+ if (!shouldLog(msgLevel, level)) return;
234
+ const entry = {
235
+ level: msgLevel,
236
+ msg,
237
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
238
+ ...baseContext,
239
+ ...context
240
+ };
241
+ if (entry.error instanceof Error) {
242
+ entry.error = entry.error.stack ?? entry.error.message;
243
+ }
244
+ output(entry);
245
+ }
246
+ return {
247
+ debug: (msg, ctx) => {
248
+ log("debug", msg, ctx);
249
+ },
250
+ info: (msg, ctx) => {
251
+ log("info", msg, ctx);
252
+ },
253
+ warn: (msg, ctx) => {
254
+ log("warn", msg, ctx);
255
+ },
256
+ error: (msg, ctx) => {
257
+ log("error", msg, ctx);
258
+ },
259
+ child(ctx) {
260
+ return createLogger({
261
+ level,
262
+ output,
263
+ context: { ...baseContext, ...ctx }
264
+ });
265
+ }
266
+ };
267
+ }
268
+ var _warnOnceSeen = /* @__PURE__ */ new Set();
269
+ var MAX_SEEN = 1024;
270
+ function _resetWarnOnceForTests() {
271
+ _warnOnceSeen.clear();
272
+ }
273
+ function warnOnce(key, payload) {
274
+ if (_warnOnceSeen.has(key)) return;
275
+ if (_warnOnceSeen.size >= MAX_SEEN) {
276
+ const oldest = _warnOnceSeen.values().next().value;
277
+ if (oldest !== void 0) _warnOnceSeen.delete(oldest);
278
+ }
279
+ _warnOnceSeen.add(key);
280
+ let line;
281
+ try {
282
+ line = JSON.stringify({ ...payload, warnOnce: true });
283
+ } catch {
284
+ line = `[warnOnce] ${key} \u2014 (payload could not be serialized)`;
285
+ }
286
+ console.warn(line);
287
+ broadcastWarnOnceToDevtools(key, payload);
288
+ }
289
+ function payloadField(payload, key, fallback = "") {
290
+ const value = payload[key];
291
+ if (typeof value === "string") return value;
292
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
293
+ return fallback;
294
+ }
295
+ function randomDevtoolsId() {
296
+ return `warn-${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
297
+ }
298
+ function broadcastWarnOnceToDevtools(key, payload) {
299
+ void import("./broadcast-4SX7R5XE.js").then(({ broadcastCsrfWarn, broadcastError }) => {
300
+ if (payload.event === "csrf.warn" && typeof payload.code === "string" && typeof payload.docsUrl === "string") {
301
+ broadcastCsrfWarn({
302
+ event: "csrf.warn",
303
+ code: payload.code,
304
+ docsUrl: payload.docsUrl,
305
+ method: payloadField(payload, "method"),
306
+ path: payloadField(payload, "path"),
307
+ reason: payloadField(payload, "reason")
308
+ });
309
+ } else {
310
+ broadcastError({
311
+ id: randomDevtoolsId(),
312
+ type: "console",
313
+ message: payloadField(payload, "event", key),
314
+ timestamp: Date.now()
315
+ });
316
+ }
317
+ }).catch(() => {
318
+ });
319
+ }
320
+
321
+ export {
322
+ FileTooLargeError,
323
+ parseRequestBody,
324
+ logRequest,
325
+ createLogger,
326
+ _resetWarnOnceForTests,
327
+ warnOnce
328
+ };
329
+ //# sourceMappingURL=chunk-Z5IGLBWE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/body-parser.ts","../src/server/observability/request-log.ts","../src/server/observability/logger.ts"],"sourcesContent":["import type { IncomingMessage } from 'node:http'\nimport { basename } from 'node:path'\n\nimport Busboy from 'busboy'\n\n// --- Types ---\n\nexport interface UploadedFile {\n fieldname: string\n filename: string\n encoding: string\n mimeType: string\n buffer: Buffer\n size: number\n}\n\nexport interface ParsedBody {\n fields: Record<string, string>\n files: UploadedFile[]\n json?: unknown\n}\n\nexport interface BodyParserOptions {\n maxFileSize?: number // bytes, default 10MB\n maxFiles?: number // default 10\n maxFieldSize?: number // bytes, default 1MB\n}\n\nconst METHODS_WITH_BODY = new Set(['POST', 'PUT', 'PATCH'])\n\nconst DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024 // 10MB\nconst DEFAULT_MAX_FILES = 10\nconst DEFAULT_MAX_FIELD_SIZE = 1 * 1024 * 1024 // 1MB\n\nexport class FileTooLargeError extends Error {\n readonly code = 'FILE_TOO_LARGE'\n readonly status = 413\n constructor(\n message: string,\n readonly truncatedFilenames: string[],\n readonly maxFileSize: number,\n ) {\n super(message)\n this.name = 'FileTooLargeError'\n }\n}\n\n// --- JSON parsing ---\n\n/**\n * Stash the parsed body on the IncomingMessage for the devtools forwarder\n * to read at log time. Symbol-keyed so it never leaks into user-visible\n * surfaces. Best-effort — failures (e.g. frozen req) are swallowed.\n */\nfunction stashBodyPreview(req: IncomingMessage, body: unknown): void {\n try {\n ;(req as unknown as Record<symbol, unknown>)[DEVTOOLS_BODY_PREVIEW] = body\n } catch {\n // best-effort; never break the request\n }\n}\n\nfunction parseJsonBody(req: IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = []\n req.on('data', (chunk: Buffer) => chunks.push(chunk))\n req.on('end', () => {\n const raw = Buffer.concat(chunks).toString()\n if (!raw) {\n resolve(undefined)\n return\n }\n try {\n resolve(JSON.parse(raw))\n } catch {\n reject(new Error('Invalid JSON body'))\n }\n })\n req.on('error', reject)\n })\n}\n\n// --- Multipart parsing ---\n\nfunction parseMultipartBody(\n req: IncomingMessage,\n contentType: string,\n options: Required<Pick<BodyParserOptions, 'maxFileSize' | 'maxFiles' | 'maxFieldSize'>>,\n): Promise<{ fields: Record<string, string>; files: UploadedFile[] }> {\n // EC-3: Validate boundary exists.\n if (!contentType.includes('boundary=')) {\n return Promise.reject(new Error('Missing multipart boundary'))\n }\n\n return new Promise((resolve, reject) => {\n const fields: Record<string, string> = {}\n const files: UploadedFile[] = []\n // CR-010: track filenames that were truncated mid-stream. The previous\n // implementation skipped truncated files from `files` and relied on\n // `files.some(f => f.size > maxFileSize)` to detect — but since the\n // file was never added, the guard never fired. Silent data loss.\n const truncatedFilenames: string[] = []\n let fileCount = 0\n\n const bb = Busboy({\n headers: req.headers,\n limits: {\n fileSize: options.maxFileSize,\n files: options.maxFiles,\n fieldSize: options.maxFieldSize,\n },\n })\n\n bb.on('field', (name: string, value: string) => {\n fields[name] = value\n })\n\n bb.on(\n 'file',\n (\n fieldname: string,\n stream: NodeJS.ReadableStream,\n info: { filename: string; encoding: string; mimeType: string },\n ) => {\n fileCount++\n if (fileCount > options.maxFiles) {\n stream.resume() // drain\n return\n }\n\n // EC-6: Sanitize filename — basename only, no path traversal.\n const safeName = basename(info.filename || 'unnamed')\n const chunks: Buffer[] = []\n let size = 0\n let truncated = false\n\n stream.on('data', (chunk: Buffer) => {\n size += chunk.length\n if (size > options.maxFileSize) {\n truncated = true\n stream.resume() // drain the rest\n return\n }\n chunks.push(chunk)\n })\n\n stream.on('end', () => {\n if (truncated) {\n truncatedFilenames.push(safeName)\n return\n }\n\n const buffer = Buffer.concat(chunks)\n files.push({\n fieldname,\n filename: safeName,\n encoding: info.encoding,\n mimeType: info.mimeType,\n buffer,\n size: buffer.length,\n })\n })\n },\n )\n\n bb.on('filesLimit', () => {\n reject(new Error(`Too many files. Maximum: ${options.maxFiles}`))\n })\n\n bb.on('error', (err: Error) => {\n reject(err)\n })\n\n bb.on('close', () => {\n if (truncatedFilenames.length > 0) {\n reject(\n new FileTooLargeError(\n `File too large (max ${options.maxFileSize} bytes): ${truncatedFilenames.join(', ')}`,\n truncatedFilenames,\n options.maxFileSize,\n ),\n )\n return\n }\n resolve({ fields, files })\n })\n\n req.pipe(bb)\n })\n}\n\n// --- Main parser ---\n\n/**\n * Symbol-keyed slot on IncomingMessage that the devtools request log\n * forwarder reads to enrich the broadcast payload with a body preview.\n * Symbol-keyed so it can't collide with user-land props and is invisible\n * to JSON.stringify / iteration. Always optional — readers must guard.\n */\nexport const DEVTOOLS_BODY_PREVIEW = Symbol('theo:devtools:bodyPreview')\n\nexport async function parseRequestBody(\n req: IncomingMessage,\n options?: BodyParserOptions,\n): Promise<ParsedBody> {\n const method = req.method?.toUpperCase() ?? 'GET'\n if (!METHODS_WITH_BODY.has(method)) {\n return { fields: {}, files: [], json: undefined }\n }\n\n const contentType = req.headers['content-type'] ?? ''\n\n // JSON\n if (contentType.includes('application/json')) {\n const json = await parseJsonBody(req)\n stashBodyPreview(req, json)\n return { fields: {}, files: [], json }\n }\n\n // Multipart\n if (contentType.includes('multipart/form-data')) {\n const limits = {\n maxFileSize: options?.maxFileSize ?? DEFAULT_MAX_FILE_SIZE,\n maxFiles: options?.maxFiles ?? DEFAULT_MAX_FILES,\n maxFieldSize: options?.maxFieldSize ?? DEFAULT_MAX_FIELD_SIZE,\n }\n const { fields, files } = await parseMultipartBody(req, contentType, limits)\n stashBodyPreview(req, {\n fields,\n files: files.map((f) => ({\n fieldname: f.fieldname,\n filename: f.filename,\n mimeType: f.mimeType,\n size: f.size,\n })),\n })\n return { fields, files }\n }\n\n // No body or empty\n if (!contentType) {\n return { fields: {}, files: [] }\n }\n\n // Unsupported content type\n throw new Error(`Unsupported Content-Type: ${contentType}`)\n}\n","/**\n * Request log primitive (T6.2 of architecture-review-remediation-plan).\n *\n * Split out from `logger.ts` per PV-12 (SRP) — the file previously mixed\n * 3 concerns (factory, warnOnce, request log). This module owns ONLY the\n * `logRequest` flow + the devtools broadcast best-effort forwarder.\n *\n * Public API surface is unchanged — `logger.ts` re-exports `logRequest`\n * + `RequestLog` + `LoggerFn` for backward compat.\n */\nimport type { IncomingMessage } from 'node:http'\n\nimport { DEVTOOLS_BODY_PREVIEW } from '../body-parser.js'\n\nexport interface RequestLog {\n level: string\n method: string\n url: string\n status: number\n duration: number\n requestId: string\n timestamp: string\n}\n\nexport type LoggerFn = (log: RequestLog) => void\n\nconst defaultLoggerFn: LoggerFn = (log) => {\n // eslint-disable-next-line no-console -- request-log default output IS the contract\n console.log(JSON.stringify(log))\n}\n\nexport function logRequest(\n info: Omit<RequestLog, 'level' | 'timestamp'>,\n customLogger?: LoggerFn,\n req?: IncomingMessage,\n): void {\n const log: RequestLog = {\n level: 'info',\n ...info,\n timestamp: new Date().toISOString(),\n }\n const logger = customLogger ?? defaultLoggerFn\n logger(log)\n // T2.1 — also broadcast to devtools (no-op in prod / when no dev server).\n // Pass `req` so the devtools forwarder can extract headers + body preview\n // stashed by body-parser (Symbol-keyed). Without `req`, only metadata\n // (method/path/status/duration) reaches the devtools UI — the expanded\n // row view then shows nothing useful.\n broadcastRequestToDevtools(info, req)\n}\n\n/**\n * T2.1 — Forward a request record to the devtools UI. Lazy import keeps\n * this module dep-free for prod; broadcast.ts internally guards on\n * `globalThis.__theoViteHotServer`.\n */\nfunction randomRequestId(): string {\n // eslint-disable-next-line sonarjs/pseudo-random -- non-secret correlation id for devtools UI\n return `req-${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`\n}\n\n/**\n * T5a.2 Phase C slice 2/2 — pure devtools broadcast that takes a\n * pre-extracted headers object + optional stashed body, so both\n * IncomingMessage and Web Request paths share the same dispatch logic.\n *\n * Errors here must not propagate — devtools forwarding is best-effort\n * and silenced.\n */\nfunction broadcastToDevtoolsCore(\n info: Omit<RequestLog, 'level' | 'timestamp'>,\n headers: Record<string, string> | undefined,\n stashedBody: unknown,\n): void {\n void Promise.all([\n import('../../devtools/server-side/broadcast.js'),\n import('../../devtools/server-side/build-request-body-preview.js'),\n ])\n .then(([{ broadcastRequest }, { buildRequestBodyPreview }]) => {\n const bodyInfo = buildRequestBodyPreview(stashedBody)\n broadcastRequest({\n id: randomRequestId(),\n traceId: info.requestId,\n method: info.method,\n path: info.url,\n status: info.status,\n durationMs: info.duration,\n startedAt: Date.now() - info.duration,\n ...(headers ? { headers } : {}),\n ...(bodyInfo\n ? {\n bodyPreview: bodyInfo.preview,\n bodyLength: bodyInfo.length,\n bodyTruncated: bodyInfo.truncated,\n }\n : {}),\n })\n })\n .catch(() => {\n // No-op: devtools forwarding is best-effort.\n })\n}\n\nfunction broadcastRequestToDevtools(\n info: Omit<RequestLog, 'level' | 'timestamp'>,\n req?: IncomingMessage,\n): void {\n // T2.1 forwarder. Errors here must not propagate.\n const headers = req?.headers\n ? Object.fromEntries(\n Object.entries(req.headers).map(([k, v]) => [\n k,\n Array.isArray(v) ? v.join(', ') : (v ?? ''),\n ]),\n )\n : undefined\n const stashedBody = req\n ? (req as unknown as Record<symbol, unknown>)[DEVTOOLS_BODY_PREVIEW]\n : undefined\n broadcastToDevtoolsCore(info, headers, stashedBody)\n}\n\n/**\n * T5a.2 Phase C slice 2/2 — Web-Standards request log + devtools\n * forward. Mirror of `logRequest(info, logger?, req?: IncomingMessage)`\n * accepting `request?: Request`.\n *\n * Extracts headers via `request.headers.entries()` (Web `Headers`\n * iterator). Body preview stash is NOT yet supported on the Web path —\n * `body-parser-web.ts` doesn't yet stash like `body-parser.ts` does for\n * the IncomingMessage path. That hookup is Phase E scope (body parsing\n * migration). Until then, the devtools UI shows headers but no body\n * preview for Web-handled requests — same surface as the IncomingMessage\n * path before the stash convention shipped.\n */\nexport function logRequestFromRequest(\n info: Omit<RequestLog, 'level' | 'timestamp'>,\n customLogger?: LoggerFn,\n request?: Request,\n): void {\n const log: RequestLog = {\n level: 'info',\n ...info,\n timestamp: new Date().toISOString(),\n }\n const logger = customLogger ?? defaultLoggerFn\n logger(log)\n\n let headers: Record<string, string> | undefined\n if (request) {\n headers = {}\n for (const [k, v] of request.headers.entries()) {\n headers[k] = v\n }\n }\n // Phase E will add body-preview stash on Web Request; for now no stash.\n broadcastToDevtoolsCore(info, headers, undefined)\n}\n","// --- Types ---\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'\n\nexport interface StructuredLog {\n level: string\n msg: string\n timestamp: string\n [key: string]: unknown\n}\n\nexport interface TheoLogger {\n debug(msg: string, context?: Record<string, unknown>): void\n info(msg: string, context?: Record<string, unknown>): void\n warn(msg: string, context?: Record<string, unknown>): void\n error(msg: string, context?: Record<string, unknown>): void\n child(context: Record<string, unknown>): TheoLogger\n}\n\n// --- Level filtering ---\n\nconst LEVEL_ORDER: Record<string, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n silent: 4,\n}\n\nfunction shouldLog(msgLevel: string, configLevel: string): boolean {\n return LEVEL_ORDER[msgLevel] >= LEVEL_ORDER[configLevel]\n}\n\n// --- Default output ---\n\nfunction defaultOutput(log: StructuredLog): void {\n // eslint-disable-next-line no-console -- structured logger output IS the contract here\n console.log(JSON.stringify(log))\n}\n\n// --- Factory ---\n\nexport function createLogger(options?: {\n level?: LogLevel\n output?: (log: StructuredLog) => void\n context?: Record<string, unknown>\n}): TheoLogger {\n const level = options?.level ?? 'info'\n const output = options?.output ?? defaultOutput\n const baseContext = options?.context ?? {}\n\n function log(msgLevel: string, msg: string, context?: Record<string, unknown>): void {\n if (!shouldLog(msgLevel, level)) return\n\n const entry: StructuredLog = {\n level: msgLevel,\n msg,\n timestamp: new Date().toISOString(),\n ...baseContext,\n ...context,\n }\n\n // Serialize Error objects to stack string\n if (entry.error instanceof Error) {\n entry.error = entry.error.stack ?? entry.error.message\n }\n\n output(entry)\n }\n\n return {\n debug: (msg, ctx) => {\n log('debug', msg, ctx)\n },\n info: (msg, ctx) => {\n log('info', msg, ctx)\n },\n warn: (msg, ctx) => {\n log('warn', msg, ctx)\n },\n error: (msg, ctx) => {\n log('error', msg, ctx)\n },\n child(ctx) {\n return createLogger({\n level,\n output,\n context: { ...baseContext, ...ctx },\n })\n },\n }\n}\n\n// --- warnOnce (T2.1) ---\n\n/**\n * Per-key dedup state. Module-scoped — reset per-process at boot.\n *\n * CR-011 fix: previous implementation used an unbounded `Set` which grew\n * forever in long-running prod processes with high-cardinality keys\n * (e.g. dynamic path segments). The bound below uses LRU-by-insertion:\n * when the Set hits `MAX_SEEN`, the OLDEST key is evicted. Subsequent\n * occurrences of that key will warn ONCE more — acceptable in exchange\n * for bounded memory.\n */\nconst _warnOnceSeen = new Set<string>()\nconst MAX_SEEN = 1024\n\n/**\n * Reset internal state. Test-only export — do not call from production.\n */\nexport function _resetWarnOnceForTests(): void {\n _warnOnceSeen.clear()\n}\n\n/**\n * Emit a structured warning ONCE per key. Subsequent calls with the same\n * key are suppressed. Used for cutover-style warnings (e.g. CSRF warn)\n * that would otherwise flood logs under load.\n *\n * Convention: key is `<event>:<method>:<path>` — see callers in csrf.ts.\n *\n * EC-2: payload may contain circular references. Wrap `JSON.stringify`\n * in try/catch so a malformed payload doesn't crash the request handler.\n */\nexport function warnOnce(key: string, payload: Record<string, unknown>): void {\n if (_warnOnceSeen.has(key)) return\n // CR-011: bounded LRU. Evict oldest insertion when at capacity.\n if (_warnOnceSeen.size >= MAX_SEEN) {\n const oldest = _warnOnceSeen.values().next().value\n if (oldest !== undefined) _warnOnceSeen.delete(oldest)\n }\n _warnOnceSeen.add(key)\n\n let line: string\n try {\n line = JSON.stringify({ ...payload, warnOnce: true })\n } catch {\n // EC-2 fallback — preserve the key for grep-ability even when payload\n // can't serialize (e.g., circular references, BigInt, etc.).\n line = `[warnOnce] ${key} — (payload could not be serialized)`\n }\n console.warn(line)\n\n // T2.1 — also broadcast to devtools (no-op in prod / when no dev server).\n // Imported lazily to keep logger.ts dependency-free for non-dev contexts;\n // broadcastToDevtools internally guards on globalThis.__theoViteHotServer.\n broadcastWarnOnceToDevtools(key, payload)\n}\n\n/**\n * T2.1 — Forward a warnOnce payload to the devtools UI. Recognizes the\n * canonical CSRF warn shape and dispatches as 'csrf.warn'; otherwise\n * falls back to a generic 'error' broadcast.\n *\n * No-op when:\n * - globalThis.__theoViteHotServer is undefined (prod / no dev server)\n * - any error occurs (default-deny via broadcastToDevtools' try/catch)\n */\nfunction payloadField(payload: Record<string, unknown>, key: string, fallback = ''): string {\n const value = payload[key]\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean') return String(value)\n return fallback\n}\n\nfunction randomDevtoolsId(): string {\n // eslint-disable-next-line sonarjs/pseudo-random -- non-secret correlation id for devtools UI\n return `warn-${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`\n}\n\nfunction broadcastWarnOnceToDevtools(key: string, payload: Record<string, unknown>): void {\n // CR-021: removed dead `void key` statement.\n // T2.1 forwarder. Errors here must not propagate (default-deny).\n void import('../../devtools/server-side/broadcast.js')\n .then(({ broadcastCsrfWarn, broadcastError }) => {\n if (\n payload.event === 'csrf.warn' &&\n typeof payload.code === 'string' &&\n typeof payload.docsUrl === 'string'\n ) {\n broadcastCsrfWarn({\n event: 'csrf.warn',\n code: payload.code,\n docsUrl: payload.docsUrl,\n method: payloadField(payload, 'method'),\n path: payloadField(payload, 'path'),\n reason: payloadField(payload, 'reason'),\n })\n } else {\n broadcastError({\n id: randomDevtoolsId(),\n type: 'console',\n message: payloadField(payload, 'event', key),\n timestamp: Date.now(),\n })\n }\n })\n .catch(() => {\n // No-op: devtools forwarding is best-effort.\n })\n}\n\n// --- Backward compat re-exports ---\n//\n// T6.2 (PV-12 SRP): `logRequest` + `RequestLog` + `LoggerFn` moved to a\n// dedicated `request-log.ts`. Re-exported here so consumers importing\n// from `theokit/server` see no change.\n\nexport { logRequest } from './request-log.js'\nexport type { RequestLog, LoggerFn } from './request-log.js'\n"],"mappings":"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","names":[]}
@@ -0,0 +1,26 @@
1
+ // src/server/scan/middleware-scan.ts
2
+ import { readdirSync, existsSync, statSync } from "fs";
3
+ import { join, extname } from "path";
4
+ var MW_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
5
+ function scanMiddlewares(serverDir) {
6
+ const mwDir = join(serverDir, "middleware");
7
+ if (!existsSync(mwDir) || !statSync(mwDir).isDirectory()) {
8
+ return [];
9
+ }
10
+ const entries = readdirSync(mwDir, { withFileTypes: true });
11
+ const files = [];
12
+ for (const entry of entries) {
13
+ if (!entry.isFile()) continue;
14
+ if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
15
+ const ext = extname(entry.name);
16
+ if (!MW_EXTENSIONS.has(ext)) continue;
17
+ files.push(join(mwDir, entry.name));
18
+ }
19
+ files.sort((a, b) => a.localeCompare(b));
20
+ return files;
21
+ }
22
+
23
+ export {
24
+ scanMiddlewares
25
+ };
26
+ //# sourceMappingURL=chunk-ZEGYW52B.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/scan/middleware-scan.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time scanner: walks `serverDir/middleware/` derived from cwd.\n * No HTTP input ever reaches these fs calls.\n */\nimport { readdirSync, existsSync, statSync } from 'node:fs'\nimport { join, extname } from 'node:path'\n\nconst MW_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx'])\n\n/**\n * Scan the server/middleware/ directory for middleware files.\n * Files are returned sorted alphabetically — use numeric prefixes\n * (e.g. 01-cors.ts, 02-auth.ts) to control execution order.\n *\n * Files starting with '_' or '.' are ignored (helpers, hidden files).\n */\nexport function scanMiddlewares(serverDir: string): string[] {\n const mwDir = join(serverDir, 'middleware')\n if (!existsSync(mwDir) || !statSync(mwDir).isDirectory()) {\n return []\n }\n\n const entries = readdirSync(mwDir, { withFileTypes: true })\n const files: string[] = []\n\n for (const entry of entries) {\n if (!entry.isFile()) continue\n if (entry.name.startsWith('_') || entry.name.startsWith('.')) continue\n const ext = extname(entry.name)\n if (!MW_EXTENSIONS.has(ext)) continue\n files.push(join(mwDir, entry.name))\n }\n\n // Sort alphabetically — numeric prefix (01-, 02-) guarantees order.\n // Use `localeCompare` for stable, locale-aware ordering.\n files.sort((a, b) => a.localeCompare(b))\n return files\n}\n"],"mappings":";AAIA,SAAS,aAAa,YAAY,gBAAgB;AAClD,SAAS,MAAM,eAAe;AAE9B,IAAM,gBAAgB,oBAAI,IAAI,CAAC,OAAO,QAAQ,OAAO,MAAM,CAAC;AASrD,SAAS,gBAAgB,WAA6B;AAC3D,QAAM,QAAQ,KAAK,WAAW,YAAY;AAC1C,MAAI,CAAC,WAAW,KAAK,KAAK,CAAC,SAAS,KAAK,EAAE,YAAY,GAAG;AACxD,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,UAAU,YAAY,OAAO,EAAE,eAAe,KAAK,CAAC;AAC1D,QAAM,QAAkB,CAAC;AAEzB,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAM,OAAO,EAAG;AACrB,QAAI,MAAM,KAAK,WAAW,GAAG,KAAK,MAAM,KAAK,WAAW,GAAG,EAAG;AAC9D,UAAM,MAAM,QAAQ,MAAM,IAAI;AAC9B,QAAI,CAAC,cAAc,IAAI,GAAG,EAAG;AAC7B,UAAM,KAAK,KAAK,OAAO,MAAM,IAAI,CAAC;AAAA,EACpC;AAIA,QAAM,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;AACvC,SAAO;AACT;","names":[]}
@@ -0,0 +1,87 @@
1
+ // src/server/http/cookies.ts
2
+ function parseCookieHeader(header) {
3
+ const map = /* @__PURE__ */ new Map();
4
+ if (!header) return map;
5
+ for (const pair of header.split(";")) {
6
+ const trimmed = pair.trim();
7
+ const eqIdx = trimmed.indexOf("=");
8
+ if (eqIdx === -1) continue;
9
+ const key = trimmed.slice(0, eqIdx);
10
+ const rawValue = trimmed.slice(eqIdx + 1);
11
+ let value;
12
+ try {
13
+ value = decodeURIComponent(rawValue);
14
+ } catch {
15
+ value = rawValue;
16
+ }
17
+ map.set(key, value);
18
+ }
19
+ return map;
20
+ }
21
+ function getCookie(req, name) {
22
+ const cookies = parseCookieHeader(req.headers.cookie);
23
+ const value = cookies.get(name);
24
+ if (value === void 0) return void 0;
25
+ if (/(?:%[^0-9A-Fa-f])|(?:%[0-9A-Fa-f]$)/.test(value)) return void 0;
26
+ return value;
27
+ }
28
+ function serializeCookie(name, value, options) {
29
+ const opts = {
30
+ httpOnly: true,
31
+ secure: process.env.NODE_ENV === "production",
32
+ sameSite: "lax",
33
+ path: "/",
34
+ ...options
35
+ };
36
+ const parts = [`${name}=${encodeURIComponent(value)}`];
37
+ if (opts.httpOnly) parts.push("HttpOnly");
38
+ if (opts.secure) parts.push("Secure");
39
+ const sameSiteCanonical = opts.sameSite.charAt(0).toUpperCase() + opts.sameSite.slice(1);
40
+ parts.push(`SameSite=${sameSiteCanonical}`);
41
+ if (opts.maxAge !== void 0) parts.push(`Max-Age=${opts.maxAge}`);
42
+ if (opts.path) parts.push(`Path=${opts.path}`);
43
+ if (opts.domain) parts.push(`Domain=${opts.domain}`);
44
+ return parts.join("; ");
45
+ }
46
+ function setCookie(res, name, value, options) {
47
+ const serialized = serializeCookie(name, value, options);
48
+ const existing = res.getHeader("Set-Cookie");
49
+ let cookies = [];
50
+ if (Array.isArray(existing)) {
51
+ cookies = existing.map(String);
52
+ } else if (existing !== void 0) {
53
+ cookies = [String(existing)];
54
+ }
55
+ cookies.push(serialized);
56
+ res.setHeader("Set-Cookie", cookies);
57
+ }
58
+ function deleteCookie(res, name, options) {
59
+ setCookie(res, name, "", { maxAge: 0, path: options?.path ?? "/" });
60
+ }
61
+ function getCookieFromRequest(request, name) {
62
+ const cookieHeader = request.headers.get("cookie");
63
+ if (cookieHeader === null) return void 0;
64
+ const cookies = parseCookieHeader(cookieHeader);
65
+ const value = cookies.get(name);
66
+ if (value === void 0) return void 0;
67
+ if (/(?:%[^0-9A-Fa-f])|(?:%[0-9A-Fa-f]$)/.test(value)) return void 0;
68
+ return value;
69
+ }
70
+ function appendCookieToHeaders(target, name, value, options) {
71
+ target.append("Set-Cookie", serializeCookie(name, value, options));
72
+ }
73
+ function appendDeleteCookieToHeaders(target, name, options) {
74
+ appendCookieToHeaders(target, name, "", { maxAge: 0, path: options?.path ?? "/" });
75
+ }
76
+
77
+ export {
78
+ parseCookieHeader,
79
+ getCookie,
80
+ serializeCookie,
81
+ setCookie,
82
+ deleteCookie,
83
+ getCookieFromRequest,
84
+ appendCookieToHeaders,
85
+ appendDeleteCookieToHeaders
86
+ };
87
+ //# sourceMappingURL=chunk-ZJQ4O76G.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/http/cookies.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\n\nexport interface CookieOptions {\n httpOnly?: boolean\n secure?: boolean\n sameSite?: 'strict' | 'lax' | 'none'\n maxAge?: number\n path?: string\n domain?: string\n}\n\n/**\n * Parse a `Cookie` header into a Map (RFC 6265 §5.4).\n * - Empty/missing input → empty Map.\n * - Malformed entries (no `=`) skipped silently (defensive).\n * - Duplicate names → last-wins.\n * - Values URL-decoded if possible; raw on decode failure (CR-009 protection).\n *\n * Canonical helper (T3.2 of architecture-review-remediation-plan; PV-4 DRY).\n * Consumed by `getCookie` here AND by `rate-limit/rate-limit-per-route.ts`.\n */\nexport function parseCookieHeader(header: string | undefined): Map<string, string> {\n const map = new Map<string, string>()\n if (!header) return map\n for (const pair of header.split(';')) {\n const trimmed = pair.trim()\n const eqIdx = trimmed.indexOf('=')\n if (eqIdx === -1) continue\n const key = trimmed.slice(0, eqIdx)\n const rawValue = trimmed.slice(eqIdx + 1)\n let value: string\n try {\n value = decodeURIComponent(rawValue)\n } catch {\n // Malformed percent-encoding — preserve raw to not lose data,\n // but the consumer is responsible for treating it as untrusted.\n value = rawValue\n }\n map.set(key, value)\n }\n return map\n}\n\nexport function getCookie(req: IncomingMessage, name: string): string | undefined {\n const cookies = parseCookieHeader(req.headers.cookie)\n const value = cookies.get(name)\n // CR-009 fix: malformed percent-encoding returns raw via parseCookieHeader.\n // If decoding failed in parse, we get the raw value (with %XX intact).\n // Treat that as \"unreadable\" for the typed getCookie API to preserve the\n // original \"treat as unauthenticated\" semantics.\n if (value === undefined) return undefined\n // Sanity: if rawValue contains lone `%` that didn't decode, parseCookieHeader\n // returned the raw — return undefined to preserve old getCookie contract.\n if (/(?:%[^0-9A-Fa-f])|(?:%[0-9A-Fa-f]$)/.test(value)) return undefined\n return value\n}\n\n/**\n * T5a.2 Phase B slice 6/6 — pure Set-Cookie serializer. Extracted from\n * `setCookie(res, ...)` so the IncomingMessage path AND the Web Headers\n * path can share the cookie attribute composition.\n *\n * Returns the canonical `Set-Cookie` header value string (no surrounding\n * `Set-Cookie:` prefix — caller wraps via `res.setHeader('Set-Cookie', ...)`\n * or `headers.append('Set-Cookie', ...)`).\n *\n * Defaults: `httpOnly: true`, `secure: NODE_ENV === 'production'`,\n * `sameSite: 'lax'`, `path: '/'`.\n */\nexport function serializeCookie(name: string, value: string, options?: CookieOptions): string {\n const opts: Required<Omit<CookieOptions, 'maxAge' | 'domain'>> &\n Pick<CookieOptions, 'maxAge' | 'domain'> = {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n ...options,\n }\n\n const parts = [`${name}=${encodeURIComponent(value)}`]\n if (opts.httpOnly) parts.push('HttpOnly')\n if (opts.secure) parts.push('Secure')\n const sameSiteCanonical = opts.sameSite.charAt(0).toUpperCase() + opts.sameSite.slice(1)\n parts.push(`SameSite=${sameSiteCanonical}`)\n if (opts.maxAge !== undefined) parts.push(`Max-Age=${opts.maxAge}`)\n if (opts.path) parts.push(`Path=${opts.path}`)\n if (opts.domain) parts.push(`Domain=${opts.domain}`)\n return parts.join('; ')\n}\n\nexport function setCookie(\n res: ServerResponse,\n name: string,\n value: string,\n options?: CookieOptions,\n): void {\n const serialized = serializeCookie(name, value, options)\n // Append to existing Set-Cookie headers (EC-1: don't overwrite)\n const existing = res.getHeader('Set-Cookie')\n let cookies: string[] = []\n if (Array.isArray(existing)) {\n cookies = existing.map(String)\n } else if (existing !== undefined) {\n cookies = [String(existing)]\n }\n cookies.push(serialized)\n res.setHeader('Set-Cookie', cookies)\n}\n\nexport function deleteCookie(res: ServerResponse, name: string, options?: { path?: string }): void {\n setCookie(res, name, '', { maxAge: 0, path: options?.path ?? '/' })\n}\n\n/**\n * T5a.2 Phase B slice 6/6 — Web-Standards cookie getter.\n *\n * Mirror of `getCookie(req: IncomingMessage, name)` for the Web `Request`\n * shape. Consumes `request.headers.get('cookie')` (native `Headers` API)\n * instead of `req.headers.cookie`. Same parse logic + same malformed\n * percent-encoding sanity (returns undefined when raw value contains a\n * lone `%` that didn't decode, preserving the IncomingMessage path's\n * \"treat as unreadable\" CR-009 semantics).\n */\nexport function getCookieFromRequest(request: Request, name: string): string | undefined {\n const cookieHeader = request.headers.get('cookie')\n if (cookieHeader === null) return undefined\n const cookies = parseCookieHeader(cookieHeader)\n const value = cookies.get(name)\n if (value === undefined) return undefined\n if (/(?:%[^0-9A-Fa-f])|(?:%[0-9A-Fa-f]$)/.test(value)) return undefined\n return value\n}\n\n/**\n * T5a.2 Phase B slice 6/6 — Web-Standards cookie setter.\n *\n * Mirror of `setCookie(res, name, value, options)` for the Web `Headers`\n * shape. Appends a `Set-Cookie` entry to the caller's `Headers` instance\n * via `headers.append('Set-Cookie', ...)`. Multiple appends produce\n * multiple `Set-Cookie` headers per the Web spec; `headers.getSetCookie()`\n * retrieves them as an array (the one multi-value header the Web API\n * exposes natively).\n *\n * Uses the shared `serializeCookie` pure helper — same defaults and\n * attribute composition as the IncomingMessage path.\n */\nexport function appendCookieToHeaders(\n target: Headers,\n name: string,\n value: string,\n options?: CookieOptions,\n): void {\n target.append('Set-Cookie', serializeCookie(name, value, options))\n}\n\n/**\n * T5a.2 Phase B slice 6/6 — Web-Standards cookie deleter.\n *\n * Mirror of `deleteCookie(res, name, options)` — emits a Set-Cookie with\n * `Max-Age=0` so the browser drops the cookie immediately.\n */\nexport function appendDeleteCookieToHeaders(\n target: Headers,\n name: string,\n options?: { path?: string },\n): void {\n appendCookieToHeaders(target, name, '', { maxAge: 0, path: options?.path ?? '/' })\n}\n"],"mappings":";AAqBO,SAAS,kBAAkB,QAAiD;AACjF,QAAM,MAAM,oBAAI,IAAoB;AACpC,MAAI,CAAC,OAAQ,QAAO;AACpB,aAAW,QAAQ,OAAO,MAAM,GAAG,GAAG;AACpC,UAAM,UAAU,KAAK,KAAK;AAC1B,UAAM,QAAQ,QAAQ,QAAQ,GAAG;AACjC,QAAI,UAAU,GAAI;AAClB,UAAM,MAAM,QAAQ,MAAM,GAAG,KAAK;AAClC,UAAM,WAAW,QAAQ,MAAM,QAAQ,CAAC;AACxC,QAAI;AACJ,QAAI;AACF,cAAQ,mBAAmB,QAAQ;AAAA,IACrC,QAAQ;AAGN,cAAQ;AAAA,IACV;AACA,QAAI,IAAI,KAAK,KAAK;AAAA,EACpB;AACA,SAAO;AACT;AAEO,SAAS,UAAU,KAAsB,MAAkC;AAChF,QAAM,UAAU,kBAAkB,IAAI,QAAQ,MAAM;AACpD,QAAM,QAAQ,QAAQ,IAAI,IAAI;AAK9B,MAAI,UAAU,OAAW,QAAO;AAGhC,MAAI,sCAAsC,KAAK,KAAK,EAAG,QAAO;AAC9D,SAAO;AACT;AAcO,SAAS,gBAAgB,MAAc,OAAe,SAAiC;AAC5F,QAAM,OACuC;AAAA,IAC3C,UAAU;AAAA,IACV,QAAQ,QAAQ,IAAI,aAAa;AAAA,IACjC,UAAU;AAAA,IACV,MAAM;AAAA,IACN,GAAG;AAAA,EACL;AAEA,QAAM,QAAQ,CAAC,GAAG,IAAI,IAAI,mBAAmB,KAAK,CAAC,EAAE;AACrD,MAAI,KAAK,SAAU,OAAM,KAAK,UAAU;AACxC,MAAI,KAAK,OAAQ,OAAM,KAAK,QAAQ;AACpC,QAAM,oBAAoB,KAAK,SAAS,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,SAAS,MAAM,CAAC;AACvF,QAAM,KAAK,YAAY,iBAAiB,EAAE;AAC1C,MAAI,KAAK,WAAW,OAAW,OAAM,KAAK,WAAW,KAAK,MAAM,EAAE;AAClE,MAAI,KAAK,KAAM,OAAM,KAAK,QAAQ,KAAK,IAAI,EAAE;AAC7C,MAAI,KAAK,OAAQ,OAAM,KAAK,UAAU,KAAK,MAAM,EAAE;AACnD,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,SAAS,UACd,KACA,MACA,OACA,SACM;AACN,QAAM,aAAa,gBAAgB,MAAM,OAAO,OAAO;AAEvD,QAAM,WAAW,IAAI,UAAU,YAAY;AAC3C,MAAI,UAAoB,CAAC;AACzB,MAAI,MAAM,QAAQ,QAAQ,GAAG;AAC3B,cAAU,SAAS,IAAI,MAAM;AAAA,EAC/B,WAAW,aAAa,QAAW;AACjC,cAAU,CAAC,OAAO,QAAQ,CAAC;AAAA,EAC7B;AACA,UAAQ,KAAK,UAAU;AACvB,MAAI,UAAU,cAAc,OAAO;AACrC;AAEO,SAAS,aAAa,KAAqB,MAAc,SAAmC;AACjG,YAAU,KAAK,MAAM,IAAI,EAAE,QAAQ,GAAG,MAAM,SAAS,QAAQ,IAAI,CAAC;AACpE;AAYO,SAAS,qBAAqB,SAAkB,MAAkC;AACvF,QAAM,eAAe,QAAQ,QAAQ,IAAI,QAAQ;AACjD,MAAI,iBAAiB,KAAM,QAAO;AAClC,QAAM,UAAU,kBAAkB,YAAY;AAC9C,QAAM,QAAQ,QAAQ,IAAI,IAAI;AAC9B,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,sCAAsC,KAAK,KAAK,EAAG,QAAO;AAC9D,SAAO;AACT;AAeO,SAAS,sBACd,QACA,MACA,OACA,SACM;AACN,SAAO,OAAO,cAAc,gBAAgB,MAAM,OAAO,OAAO,CAAC;AACnE;AAQO,SAAS,4BACd,QACA,MACA,SACM;AACN,wBAAsB,QAAQ,MAAM,IAAI,EAAE,QAAQ,GAAG,MAAM,SAAS,QAAQ,IAAI,CAAC;AACnF;","names":[]}