theokit 0.4.0-beta.0 → 0.5.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 +61 -9
  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
@@ -13,34 +13,244 @@ import {
13
13
  resolveTransformer,
14
14
  safeAudit,
15
15
  sendError
16
- } from "./chunk-TPCT3MXV.js";
16
+ } from "./chunk-ABVITXFH.js";
17
17
  import {
18
18
  loadConfig
19
- } from "./chunk-XP7YXRHO.js";
19
+ } from "./chunk-U6TPSW4L.js";
20
+ import {
21
+ buildServicesProxyConfig
22
+ } from "./chunk-27LWMYNK.js";
20
23
  import {
21
24
  broadcastToDevtools
22
25
  } from "./chunk-5ODOE6EF.js";
23
26
  import {
24
27
  isRouteFile,
25
28
  scanRoutes
26
- } from "./chunk-O7335ZGH.js";
27
- import {
28
- buildServicesProxyConfig
29
- } from "./chunk-WZ7CPVQB.js";
29
+ } from "./chunk-CG563V5M.js";
30
30
  import {
31
31
  matchRoute,
32
32
  scanServerRoutes,
33
33
  scanWebSocketRoutes
34
- } from "./chunk-OXIQI2XZ.js";
34
+ } from "./chunk-VBZCVFSA.js";
35
35
  import {
36
36
  scanServerActions
37
- } from "./chunk-5OFPQK6N.js";
37
+ } from "./chunk-COXLEZCN.js";
38
38
 
39
39
  // src/vite-plugin/index.ts
40
- import { existsSync as existsSync5 } from "fs";
41
- import { resolve as resolve4, basename, dirname as dirname4 } from "path";
40
+ import { existsSync as existsSync7 } from "fs";
41
+ import { resolve as resolve7, dirname as dirname4 } from "path";
42
42
  import { fileURLToPath } from "url";
43
43
 
44
+ // src/vite-plugin/config-hook.ts
45
+ import { existsSync } from "fs";
46
+ import { resolve } from "path";
47
+ function runConfigHook(ctx) {
48
+ const ext = existsSync(resolve(ctx.theoSrcDir, "index.ts")) ? ".ts" : ".js";
49
+ const warmupClientFiles = [];
50
+ for (const candidate of ["app/layout.tsx", "app/page.tsx", "app/page.jsx"]) {
51
+ const full = resolve(ctx.projectRoot, candidate);
52
+ if (existsSync(full)) warmupClientFiles.push(`./${candidate}`);
53
+ }
54
+ const servicesProxy = ctx.services && Object.keys(ctx.services).length > 0 ? buildServicesProxyConfig(ctx.services) : void 0;
55
+ return {
56
+ envPrefix: "THEO_PUBLIC_",
57
+ optimizeDeps: {
58
+ ...ctx.optimizeDepsInclude.length > 0 ? { include: ctx.optimizeDepsInclude } : {},
59
+ force: true
60
+ },
61
+ server: {
62
+ ...warmupClientFiles.length > 0 ? { warmup: { clientFiles: warmupClientFiles } } : {},
63
+ ...servicesProxy !== void 0 ? { proxy: servicesProxy } : {},
64
+ // Skip watching gitignored heavy dirs to avoid hitting ENOSPC
65
+ // (inotify watcher exhaustion).
66
+ watch: {
67
+ ignored: [
68
+ "**/referencias/**",
69
+ "**/.theokit/**",
70
+ "**/.theo/**",
71
+ "**/dist/**",
72
+ "**/node_modules/**"
73
+ ]
74
+ }
75
+ },
76
+ resolve: {
77
+ alias: [
78
+ // Order matters: most-specific first so `theokit/X` doesn't
79
+ // get matched by the bare `theokit` alias.
80
+ { find: "theokit/server", replacement: resolve(ctx.theoSrcDir, `server/index${ext}`) },
81
+ { find: "theokit/client", replacement: resolve(ctx.theoSrcDir, `client/index${ext}`) },
82
+ {
83
+ // T2.1 (M5 lonely folders) moved the source from
84
+ // `react-query/index.ts` into `client/react-query.ts` (sibling of
85
+ // `client/index.ts`). The package.json export `./react-query`
86
+ // still points at the build artifact `./dist/react-query/index.{js,d.ts}`,
87
+ // but the dev-time source alias must follow the new location.
88
+ find: "theokit/react-query",
89
+ replacement: resolve(ctx.theoSrcDir, `client/react-query${ext}`)
90
+ },
91
+ {
92
+ find: "theokit/vite-plugin",
93
+ replacement: resolve(ctx.theoSrcDir, `vite-plugin/index${ext}`)
94
+ },
95
+ {
96
+ find: "theokit/adapters/web-shim",
97
+ replacement: resolve(ctx.theoSrcDir, `adapters/web-shim${ext}`)
98
+ },
99
+ {
100
+ find: "theokit/adapters/ws-shim",
101
+ replacement: resolve(ctx.theoSrcDir, `adapters/ws-shim${ext}`)
102
+ },
103
+ // T1.2 — devtools entry (DEV only; tree-shaken in build).
104
+ // Source layout: src/devtools/dom/entry.tsx (has /dom/ segment)
105
+ // Dist layout: dist/devtools/entry.js (tsup flattens /dom/)
106
+ {
107
+ find: "theokit/devtools/entry",
108
+ replacement: resolve(
109
+ ctx.theoSrcDir,
110
+ ext === ".ts" ? "devtools/dom/entry.tsx" : "devtools/entry.js"
111
+ )
112
+ },
113
+ { find: "theokit", replacement: resolve(ctx.theoSrcDir, `index${ext}`) }
114
+ ]
115
+ }
116
+ };
117
+ }
118
+
119
+ // src/vite-plugin/theoui-detect.ts
120
+ import { existsSync as existsSync2, readFileSync } from "fs";
121
+ import { dirname, join } from "path";
122
+ function resolveTheoUiConfig(raw) {
123
+ return {
124
+ theme: raw?.theme ?? "violet-forge",
125
+ fonts: raw?.fonts ?? "bundled"
126
+ };
127
+ }
128
+ function resolveExportSubpath(pkgRoot, subpath) {
129
+ const pkgJsonPath = join(pkgRoot, "package.json");
130
+ if (!existsSync2(pkgJsonPath)) return null;
131
+ try {
132
+ const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
133
+ const exportKey = `./${subpath}`;
134
+ const exp = pkg.exports?.[exportKey];
135
+ if (!exp) {
136
+ const fallback = join(pkgRoot, "dist", subpath);
137
+ return existsSync2(fallback) ? fallback : null;
138
+ }
139
+ let target;
140
+ if (typeof exp === "string") target = exp;
141
+ else if (typeof exp === "object") {
142
+ const e = exp;
143
+ target = e.import ?? e.default;
144
+ }
145
+ if (!target) return null;
146
+ const cleaned = target.replace(/^\.\//, "");
147
+ const candidate = join(pkgRoot, cleaned);
148
+ return existsSync2(candidate) ? candidate : null;
149
+ } catch {
150
+ return null;
151
+ }
152
+ }
153
+ var defaultResolver = (specifier, projectRoot) => {
154
+ const parts = specifier.split("/");
155
+ const pkgName = parts[0]?.startsWith("@") && parts.length >= 2 ? `${parts[0]}/${parts[1]}` : parts[0];
156
+ if (!pkgName) return false;
157
+ const subpath = specifier.slice(pkgName.length + 1);
158
+ if (!subpath) return false;
159
+ let dir = projectRoot;
160
+ for (let depth = 0; depth < 10; depth++) {
161
+ const pkgRoot = join(dir, "node_modules", ...pkgName.split("/"));
162
+ if (existsSync2(pkgRoot)) {
163
+ const resolved = resolveExportSubpath(pkgRoot, subpath);
164
+ if (resolved) return true;
165
+ }
166
+ const parent = dirname(dir);
167
+ if (parent === dir) break;
168
+ dir = parent;
169
+ }
170
+ return false;
171
+ };
172
+ function isDeclaredInPackageJson(projectRoot) {
173
+ try {
174
+ const raw = readFileSync(join(projectRoot, "package.json"), "utf-8");
175
+ const pkg = JSON.parse(raw);
176
+ return Boolean(
177
+ pkg.dependencies?.["@theokit/ui"] ?? pkg.devDependencies?.["@theokit/ui"] ?? pkg.peerDependencies?.["@theokit/ui"]
178
+ );
179
+ } catch {
180
+ return false;
181
+ }
182
+ }
183
+ function detectTheoUi(projectRoot, rawConfig, resolver = defaultResolver) {
184
+ const config = resolveTheoUiConfig(typeof rawConfig === "object" ? rawConfig : void 0);
185
+ if (rawConfig === false) {
186
+ return { enabled: false, config };
187
+ }
188
+ if (!isDeclaredInPackageJson(projectRoot)) {
189
+ return { enabled: false, config };
190
+ }
191
+ const probes = ["@theokit/ui/styles.css", "@theokit/ui/fonts.css"];
192
+ for (const probe of probes) {
193
+ if (resolver(probe, projectRoot)) {
194
+ return { enabled: true, config };
195
+ }
196
+ }
197
+ return { enabled: false, config };
198
+ }
199
+
200
+ // src/vite-plugin/config-resolve.ts
201
+ var DEFAULT_RESOLVED = {
202
+ pluginRunner: void 0,
203
+ transformer: void 0,
204
+ resolvedBatching: void 0,
205
+ theoUi: void 0,
206
+ csrfMode: "strict",
207
+ securityHeaders: void 0,
208
+ disallowed: void 0,
209
+ cors: void 0,
210
+ auditLogger: void 0,
211
+ devtoolsEnabled: true,
212
+ openapi: void 0,
213
+ distDir: ".theo"
214
+ };
215
+ async function resolvePluginConfig(projectRoot) {
216
+ try {
217
+ const userConfig = await loadConfig(projectRoot);
218
+ const pluginRunner = await createPluginRunnerFromConfig(userConfig.plugins);
219
+ const transformer = resolveTransformer(userConfig.serialization);
220
+ let resolvedBatching;
221
+ if (userConfig.batching === true) {
222
+ resolvedBatching = {};
223
+ } else if (typeof userConfig.batching === "object") {
224
+ resolvedBatching = userConfig.batching;
225
+ }
226
+ const theoUi = detectTheoUi(projectRoot, userConfig.ui);
227
+ let auditLogger;
228
+ const maybeLogger = userConfig.audit?.logger;
229
+ if (maybeLogger && typeof maybeLogger.log === "function") {
230
+ auditLogger = maybeLogger;
231
+ }
232
+ return {
233
+ pluginRunner,
234
+ transformer,
235
+ resolvedBatching,
236
+ theoUi,
237
+ csrfMode: userConfig.security?.csrf ?? "strict",
238
+ securityHeaders: userConfig.security?.headers,
239
+ disallowed: userConfig.security?.disallowed,
240
+ cors: userConfig.security?.cors,
241
+ auditLogger,
242
+ devtoolsEnabled: userConfig.devtools !== false,
243
+ openapi: userConfig.openapi,
244
+ distDir: userConfig.distDir
245
+ };
246
+ } catch {
247
+ return DEFAULT_RESOLVED;
248
+ }
249
+ }
250
+
251
+ // src/vite-plugin/configure-server-hook.ts
252
+ import { resolve as resolve4, basename } from "path";
253
+
44
254
  // src/devtools/server-side/route-manifest.ts
45
255
  function buildPath(parents, segment) {
46
256
  const joined = [...parents, segment].filter(Boolean).join("/");
@@ -75,422 +285,27 @@ function broadcastRouteManifest(tree) {
75
285
  broadcastToDevtools("theo:devtools:manifest", manifest);
76
286
  }
77
287
 
78
- // src/router/entry-server.ts
79
- function generateEntryServer(options = {}) {
80
- if (options.streaming) {
81
- return generateStreamingEntry(options);
288
+ // src/server/security/csrf-readiness-store.ts
289
+ var CsrfReadinessStore = class _CsrfReadinessStore {
290
+ static MAX_ENTRIES = 1e3;
291
+ entries = /* @__PURE__ */ new Map();
292
+ keyFor(event) {
293
+ return `${event.method}\0${event.path}\0${event.reason}`;
82
294
  }
83
- return generateSingleShotEntry(options);
84
- }
85
- function buildAppTreeJs(options) {
86
- const theme = options.theoUi?.theme ?? "violet-forge";
87
- if (options.theoUi) {
88
- return [
89
- `React.createElement(TheoUIProvider, { theme: { defaultTheme: '${theme}' } },`,
90
- ` React.createElement(Suspense, { fallback: null },`,
91
- ` React.createElement(StaticRouterProvider, { router, context, hydrate: false })`,
92
- ` )`,
93
- ` )`
94
- ].join("\n");
95
- }
96
- return `React.createElement(Suspense, { fallback: null },
97
- React.createElement(StaticRouterProvider, { router, context, hydrate: false })
98
- )`;
99
- }
100
- function hydrationDataExtractSnippet() {
101
- return [
102
- ` const hydrationData = {`,
103
- ` loaderData: context.loaderData,`,
104
- ` actionData: context.actionData,`,
105
- ` errors: context.errors,`,
106
- ` }`
107
- ].join("\n");
108
- }
109
- function generateSingleShotEntry(options) {
110
- const theoUiImport = options.theoUi ? `import { TheoUIProvider } from '@theokit/ui'
111
- ` : "";
112
- const appTree = buildAppTreeJs(options);
113
- return [
114
- `import React, { Suspense } from 'react'`,
115
- `import { renderToPipeableStream } from 'react-dom/server'`,
116
- `import { createStaticHandler, createStaticRouter, StaticRouterProvider } from 'react-router'`,
117
- `import { PassThrough } from 'node:stream'`,
118
- `import { routes } from '/@theo/route-manifest'`,
119
- theoUiImport,
120
- `export async function render(url, options = {}) {`,
121
- ` const handler = createStaticHandler(routes)`,
122
- ` const request = new Request('http://localhost' + url)`,
123
- ` const context = await handler.query(request)`,
124
- ``,
125
- ` if (context instanceof Response) {`,
126
- ` return { redirect: context }`,
127
- ` }`,
128
- ``,
129
- ` const router = createStaticRouter(handler.dataRoutes, context)`,
130
- ` const app = ${appTree}`,
131
- ``,
132
- hydrationDataExtractSnippet(),
133
- ``,
134
- ` return new Promise((resolve, reject) => {`,
135
- ` let html = ''`,
136
- ` let piped = false`,
137
- ` const passthrough = new PassThrough()`,
138
- ` passthrough.on('data', (chunk) => { html += chunk.toString() })`,
139
- ` passthrough.on('end', () => { resolve({ html, hydrationData }) })`,
140
- ` passthrough.on('error', reject)`,
141
- ``,
142
- ` // Pipe on onShellReady (Next.js pattern). Calling pipe() twice`,
143
- ` // throws "React currently only supports piping to one writable`,
144
- ` // stream". The \`piped\` flag is a belt-and-suspenders guard if`,
145
- ` // onShellReady fires unexpectedly more than once.`,
146
- ` // Forward options.nonce to React so every <script> tag React`,
147
- ` // emits (Suspense boundary scripts) carries the nonce attribute.`,
148
- ` const { pipe } = renderToPipeableStream(app, {`,
149
- ` nonce: options.nonce,`,
150
- ` onShellReady() { if (!piped) { piped = true; pipe(passthrough) } },`,
151
- ` onShellError(err) { reject(err) },`,
152
- ` onError(err) { console.error('[SSR Error]', err) },`,
153
- ` })`,
154
- ` })`,
155
- `}`
156
- ].join("\n");
157
- }
158
- function streamingWebRenderer(appTree) {
159
- return [
160
- `// T2.3 \u2014 Web Standards streaming entry for edge runtimes (Cloudflare,`,
161
- `// Bun, Deno, Vercel Edge). Uses renderToReadableStream and returns a`,
162
- `// Response with the stream as body. Honors request.signal for client`,
163
- `// disconnect cleanup.`,
164
- `export async function renderStreamingWeb(request, options = {}) {`,
165
- ` const handler = createStaticHandler(routes)`,
166
- ` const url = new URL(request.url)`,
167
- ` const context = await handler.query(request)`,
168
- ``,
169
- ` if (context instanceof Response) {`,
170
- ` return context`,
171
- ` }`,
172
- ``,
173
- ` const router = createStaticRouter(handler.dataRoutes, context)`,
174
- ` const app = ${appTree}`,
175
- ``,
176
- ` const stream = await renderToReadableStream(app, {`,
177
- ` signal: request.signal,`,
178
- ` nonce: options.nonce,`,
179
- ` onError(err) { console.error('[SSR Web Stream Error]', err) },`,
180
- ` })`,
181
- ` return new Response(stream, {`,
182
- ` status: 200,`,
183
- ` headers: {`,
184
- ` 'Content-Type': 'text/html; charset=utf-8',`,
185
- ` 'Transfer-Encoding': 'chunked',`,
186
- ` },`,
187
- ` })`,
188
- `}`
189
- ];
190
- }
191
- function streamingNodeRenderer(appTree) {
192
- return [
193
- `// T6.1 \u2014 Node streaming SSR entry (opt-in via theo.config.ts > ssrStreaming: true)`,
194
- `// Flushes the shell as soon as it's ready, then streams Suspense boundaries.`,
195
- `// EC-11: respects request.signal for client-disconnect cleanup.`,
196
- `export async function renderStreaming(url, response, options = {}) {`,
197
- ` const handler = createStaticHandler(routes)`,
198
- ` const request = new Request('http://localhost' + url, { signal: options.signal })`,
199
- ` const context = await handler.query(request)`,
200
- ``,
201
- ` if (context instanceof Response) {`,
202
- ` return { redirect: context }`,
203
- ` }`,
204
- ``,
205
- ` const router = createStaticRouter(handler.dataRoutes, context)`,
206
- ` const app = ${appTree}`,
207
- ``,
208
- ` return new Promise((resolve, reject) => {`,
209
- ` let didError = false`,
210
- ` const stream = renderToPipeableStream(app, {`,
211
- ` nonce: options.nonce,`,
212
- ` onShellReady() {`,
213
- ` response.statusCode = didError ? 500 : 200`,
214
- ` response.setHeader('Content-Type', 'text/html; charset=utf-8')`,
215
- ` response.setHeader('Transfer-Encoding', 'chunked')`,
216
- ` stream.pipe(response)`,
217
- ` resolve({ streaming: true })`,
218
- ` },`,
219
- ` onShellError(err) { reject(err) },`,
220
- ` onError(err) {`,
221
- ` didError = true`,
222
- ` console.error('[SSR Stream Error]', err)`,
223
- ` },`,
224
- ` })`,
225
- ``,
226
- ` // EC-11: client disconnect cleanup`,
227
- ` if (options.signal) {`,
228
- ` options.signal.addEventListener('abort', () => { stream.abort() })`,
229
- ` }`,
230
- ` })`,
231
- `}`
232
- ];
233
- }
234
- function backCompatRenderer(appTree) {
235
- return [
236
- `// Backward compatibility: keep the single-shot render export available so`,
237
- `// callers that always used 'render()' don't break when streaming is on.`,
238
- `export async function render(url, options = {}) {`,
239
- ` const handler = createStaticHandler(routes)`,
240
- ` const request = new Request('http://localhost' + url)`,
241
- ` const context = await handler.query(request)`,
242
- ``,
243
- ` if (context instanceof Response) {`,
244
- ` return { redirect: context }`,
245
- ` }`,
246
- ``,
247
- ` const router = createStaticRouter(handler.dataRoutes, context)`,
248
- ` const app = ${appTree}`,
249
- ``,
250
- hydrationDataExtractSnippet(),
251
- ``,
252
- ` const { PassThrough } = await import('node:stream')`,
253
- ` return new Promise((resolve, reject) => {`,
254
- ` let html = ''`,
255
- ` let piped = false`,
256
- ` const passthrough = new PassThrough()`,
257
- ` passthrough.on('data', (chunk) => { html += chunk.toString() })`,
258
- ` passthrough.on('end', () => { resolve({ html, hydrationData }) })`,
259
- ` passthrough.on('error', reject)`,
260
- ``,
261
- ` // Pipe on onShellReady (Next.js pattern). nonce forwarded.`,
262
- ` const { pipe } = renderToPipeableStream(app, {`,
263
- ` nonce: options.nonce,`,
264
- ` onShellReady() { if (!piped) { piped = true; pipe(passthrough) } },`,
265
- ` onShellError(err) { reject(err) },`,
266
- ` onError(err) { console.error('[SSR Error]', err) },`,
267
- ` })`,
268
- ` })`,
269
- `}`
270
- ];
271
- }
272
- function generateStreamingEntry(options) {
273
- const theoUiImport = options.theoUi ? `import { TheoUIProvider } from '@theokit/ui'
274
- ` : "";
275
- const appTree = buildAppTreeJs(options);
276
- return [
277
- `import React, { Suspense } from 'react'`,
278
- `import { renderToPipeableStream, renderToReadableStream } from 'react-dom/server'`,
279
- `import { createStaticHandler, createStaticRouter, StaticRouterProvider } from 'react-router'`,
280
- `import { routes } from '/@theo/route-manifest'`,
281
- theoUiImport,
282
- ``,
283
- ...streamingWebRenderer(appTree),
284
- ``,
285
- ...streamingNodeRenderer(appTree),
286
- ``,
287
- ...backCompatRenderer(appTree)
288
- ].join("\n");
289
- }
290
-
291
- // src/router/entry.ts
292
- function generateEntryClient(ssr, opts = {}) {
293
- const rootMethod = ssr ? "hydrateRoot" : "createRoot";
294
- const theoUiImports = [];
295
- if (opts.theoUi) {
296
- theoUiImports.push(
297
- `// T2.2 \u2014 TheoUI CSS auto-injected (config.ui)`,
298
- `import '@theokit/ui/styles.css'`
299
- );
300
- const fontsModule = opts.theoUi.fonts === "cdn" ? "fonts-cdn.css" : "fonts.css";
301
- theoUiImports.push(
302
- `import '@theokit/ui/${fontsModule}'`,
303
- `// T2.3 \u2014 TheoUIProvider auto-wraps RouterProvider`,
304
- `import { TheoUIProvider } from '@theokit/ui'`
305
- );
306
- }
307
- const theme = opts.theoUi?.theme ?? "violet-forge";
308
- const routerTree = opts.theoUi ? `React.createElement(TheoUIProvider, { theme: { defaultTheme: '${theme}' } },
309
- React.createElement(Suspense, { fallback: null },
310
- React.createElement(RouterProvider, { router })
311
- )
312
- )` : `React.createElement(Suspense, { fallback: null },
313
- React.createElement(RouterProvider, { router })
314
- )`;
315
- const renderCall = ssr ? ` ${rootMethod}(el,
316
- ${routerTree}
317
- )` : ` ${rootMethod}(el).render(
318
- ${routerTree}
319
- )`;
320
- const hydrationLine = ssr ? `const router = createBrowserRouter(routes, { hydrationData: window.__staticRouterHydrationData })` : `const router = createBrowserRouter(routes)`;
321
- const preloadBlock = ssr ? [
322
- `// Phase 4 \u2014 preload matched-route modules before hydrate (EC-3 safeguard)`,
323
- `const __theoMatches = matchRoutes(routes, window.location.pathname) ?? []`,
324
- `const __theoPreloadPaths = __theoMatches`,
325
- ` .map((m) => m.route && (m.route).path)`,
326
- ` .filter((p) => typeof p === 'string' && p in __theoPreloadMap)`,
327
- `const __theoPreloadPromise = Promise.all(`,
328
- ` __theoPreloadPaths.map((p) => __theoPreloadMap[p]().catch((err) => { console.error('[theo] preload failed', p, err); return null }))`,
329
- `)`,
330
- `const __theoTimeout = new Promise((resolve) => setTimeout(() => resolve('timeout'), 1500))`,
331
- `await Promise.race([__theoPreloadPromise, __theoTimeout])`,
332
- ""
333
- ] : [];
334
- const renderBlock = ssr ? [
335
- `if (el) {`,
336
- ` ;(async () => {`,
337
- ...preloadBlock.map((l) => " " + l),
338
- renderCall,
339
- ` })()`,
340
- `}`
341
- ] : [`if (el) {`, renderCall, `}`];
342
- const manifestImports = ssr ? `import { routes, __theoPreloadMap } from '/@theo/route-manifest'` : `import { routes } from '/@theo/route-manifest'`;
343
- const reactRouterImports = ssr ? `import { createBrowserRouter, RouterProvider, matchRoutes } from 'react-router'` : `import { createBrowserRouter, RouterProvider } from 'react-router'`;
344
- return [
345
- `import React, { Suspense } from 'react'`,
346
- `import { ${rootMethod} } from 'react-dom/client'`,
347
- reactRouterImports,
348
- manifestImports,
349
- `// T1.3 \u2014 side-effect import sets globalThis.__THEO_TRANSFORMER__ for theoFetch`,
350
- `import '/@theo/runtime-config'`,
351
- ...theoUiImports,
352
- ``,
353
- hydrationLine,
354
- `const el = document.getElementById('root')`,
355
- ...renderBlock
356
- ].join("\n");
357
- }
358
-
359
- // src/router/generate.ts
360
- function normalizePath(p) {
361
- return p.replace(/\\/g, "/");
362
- }
363
- function safeVarName(segment, prefix) {
364
- const safe = segment.replace(/[^a-zA-Z0-9]/g, "_") || "root";
365
- return `${prefix}_${safe}`;
366
- }
367
- function buildRoutePath(parents, segment) {
368
- const joined = [...parents, segment].filter(Boolean).join("/");
369
- return "/" + joined;
370
- }
371
- function pushIf(staticImports, filePath, varName) {
372
- if (filePath !== void 0) {
373
- staticImports.push({ varName, importPath: normalizePath(filePath) });
374
- }
375
- }
376
- function walkRouteTree(node, parents, acc) {
377
- const seg = node.segment || "root";
378
- const routePath = buildRoutePath(parents, node.segment);
379
- if (node.page !== void 0) {
380
- acc.lazyPages.push({
381
- varName: safeVarName(seg, "Page"),
382
- importPath: normalizePath(node.page),
383
- routePath
384
- });
385
- }
386
- if (node.layout !== void 0) acc.layoutState.found = true;
387
- pushIf(acc.staticImports, node.layout, safeVarName(seg, "Layout"));
388
- pushIf(acc.staticImports, node.error, safeVarName(seg, "Error"));
389
- pushIf(acc.staticImports, node.loading, safeVarName(seg, "Loading"));
390
- pushIf(acc.staticImports, node.notFound, safeVarName(seg, "NotFound"));
391
- for (const child of node.children) {
392
- const nextParents = node.segment ? [...parents, node.segment] : parents;
393
- walkRouteTree(child, nextParents, acc);
394
- }
395
- }
396
- function generateRouteManifest(tree) {
397
- const acc = {
398
- staticImports: [],
399
- lazyPages: [],
400
- layoutState: { found: false }
401
- };
402
- walkRouteTree(tree, [], acc);
403
- const staticImports = acc.staticImports;
404
- const lazyPages = acc.lazyPages;
405
- const layoutState = acc.layoutState;
406
- const lines = [`import React, { Suspense } from 'react'`];
407
- if (layoutState.found) {
408
- lines.push(`import { Outlet } from 'react-router'`);
409
- }
410
- lines.push("");
411
- for (const imp of staticImports) {
412
- lines.push(`import ${imp.varName} from '${imp.importPath}'`);
413
- }
414
- for (const lp of lazyPages) {
415
- lines.push(`const ${lp.varName} = React.lazy(() => import('${lp.importPath}'))`);
416
- }
417
- lines.push("");
418
- const preloadEntries = lazyPages.map(
419
- (lp) => ` '${lp.routePath}': () => import('${lp.importPath}'),`
420
- );
421
- lines.push("export const __theoPreloadMap = {");
422
- for (const e of preloadEntries) lines.push(e);
423
- lines.push("}", "");
424
- function buildChildrenArray(node, seg) {
425
- const childConfigs = [];
426
- if (node.page) {
427
- const pageVar = safeVarName(seg, "Page");
428
- const fallbackEl = node.loading ? `React.createElement(${safeVarName(seg, "Loading")})` : "null";
429
- const pageElement = `React.createElement(Suspense, { fallback: ${fallbackEl} }, React.createElement(${pageVar}))`;
430
- childConfigs.push(`{ index: true, element: ${pageElement} }`);
431
- }
432
- for (const child of node.children) {
433
- childConfigs.push(genRouteConfig(child, false));
434
- }
435
- if (node.notFound) {
436
- const nfVar = safeVarName(seg, "NotFound");
437
- childConfigs.push(`{ path: '*', element: React.createElement(${nfVar}) }`);
438
- }
439
- let arr = `[${childConfigs.join(", ")}]`;
440
- if (node.error) {
441
- const errVar = safeVarName(seg, "Error");
442
- arr = `[{ errorElement: React.createElement(${errVar}), children: ${arr} }]`;
443
- }
444
- return arr;
445
- }
446
- function genRouteConfig(node, isRoot) {
447
- const seg = node.segment || "root";
448
- const childrenArray = buildChildrenArray(node, seg);
449
- if (node.layout) {
450
- const layoutVar = safeVarName(seg, "Layout");
451
- const pathPart = isRoot ? `path: '/'` : `path: '${node.segment}'`;
452
- return `{ ${pathPart}, element: React.createElement(${layoutVar}, { children: React.createElement(Outlet) }), children: ${childrenArray} }`;
453
- }
454
- if (isRoot) {
455
- if (node.children.length === 0 && !node.page && !node.notFound && !node.error) {
456
- return `{ path: '/', children: [] }`;
457
- }
458
- return `{ path: '/', children: ${childrenArray} }`;
459
- }
460
- if (node.page && node.children.length === 0 && !node.error && !node.notFound) {
461
- const pageVar = safeVarName(seg, "Page");
462
- const fallbackEl = node.loading ? `React.createElement(${safeVarName(seg, "Loading")})` : "null";
463
- const pageElement = `React.createElement(Suspense, { fallback: ${fallbackEl} }, React.createElement(${pageVar}))`;
464
- return `{ path: '${node.segment}', element: ${pageElement} }`;
465
- }
466
- return `{ path: '${node.segment}', children: ${childrenArray} }`;
467
- }
468
- const routeConfig = genRouteConfig(tree, true);
469
- lines.push(`export const routes = [${routeConfig}]`);
470
- return lines.join("\n");
471
- }
472
-
473
- // src/server/security/csrf-readiness-store.ts
474
- var CsrfReadinessStore = class _CsrfReadinessStore {
475
- static MAX_ENTRIES = 1e3;
476
- entries = /* @__PURE__ */ new Map();
477
- keyFor(event) {
478
- return `${event.method}\0${event.path}\0${event.reason}`;
479
- }
480
- record(event) {
481
- const key = this.keyFor(event);
482
- const now = (/* @__PURE__ */ new Date()).toISOString();
483
- const existing = this.entries.get(key);
484
- if (existing) {
485
- existing.count++;
486
- existing.lastSeen = now;
487
- return;
488
- }
489
- if (this.entries.size >= _CsrfReadinessStore.MAX_ENTRIES) {
490
- const firstKey = this.entries.keys().next().value;
491
- if (firstKey !== void 0) this.entries.delete(firstKey);
492
- }
493
- this.entries.set(key, { count: 1, firstSeen: now, lastSeen: now });
295
+ record(event) {
296
+ const key = this.keyFor(event);
297
+ const now = (/* @__PURE__ */ new Date()).toISOString();
298
+ const existing = this.entries.get(key);
299
+ if (existing) {
300
+ existing.count++;
301
+ existing.lastSeen = now;
302
+ return;
303
+ }
304
+ if (this.entries.size >= _CsrfReadinessStore.MAX_ENTRIES) {
305
+ const firstKey = this.entries.keys().next().value;
306
+ if (firstKey !== void 0) this.entries.delete(firstKey);
307
+ }
308
+ this.entries.set(key, { count: 1, firstSeen: now, lastSeen: now });
494
309
  }
495
310
  summary() {
496
311
  let total = 0;
@@ -617,9 +432,9 @@ var DEFAULT_MAX_BATCH = 32;
617
432
  var batchRequestSchema = z.object({
618
433
  path: z.string().min(1),
619
434
  method: z.string().min(1),
620
- query: z.record(z.unknown()).optional(),
435
+ query: z.record(z.string(), z.unknown()).optional(),
621
436
  body: z.unknown().optional(),
622
- headers: z.record(z.string()).optional()
437
+ headers: z.record(z.string(), z.string()).optional()
623
438
  });
624
439
  var batchPayloadSchema = z.object({
625
440
  requests: z.array(batchRequestSchema).min(1)
@@ -746,7 +561,6 @@ function createCorsHandler(config) {
746
561
  }
747
562
 
748
563
  // src/server/http/trace-context.ts
749
- import { randomUUID as randomUUID2 } from "crypto";
750
564
  var TRACE_HEADER = "x-trace-id";
751
565
  var TRACE_PARENT_HEADER = "traceparent";
752
566
  var REQUEST_ID_HEADER = "x-request-id";
@@ -769,15 +583,19 @@ function pickHeader(value) {
769
583
  if (typeof value === "string" && value.length > 0) return value;
770
584
  return null;
771
585
  }
772
- function extractTraceId(req) {
773
- const traceparent = pickHeader(req.headers[TRACE_PARENT_HEADER]);
774
- if (traceparent) {
586
+ function resolveTraceIdFromHeaders(traceparent, requestId) {
587
+ if (traceparent !== null) {
775
588
  const parsed = parseTraceparent(traceparent);
776
- if (parsed) return parsed;
589
+ if (parsed !== null) return parsed;
777
590
  }
778
- const requestId = pickHeader(req.headers[REQUEST_ID_HEADER]);
779
- if (requestId) return requestId;
780
- return randomUUID2();
591
+ if (requestId !== null) return requestId;
592
+ return globalThis.crypto.randomUUID();
593
+ }
594
+ function extractTraceId(req) {
595
+ return resolveTraceIdFromHeaders(
596
+ pickHeader(req.headers[TRACE_PARENT_HEADER]),
597
+ pickHeader(req.headers[REQUEST_ID_HEADER])
598
+ );
781
599
  }
782
600
 
783
601
  // src/server/security/csp-report.ts
@@ -826,7 +644,7 @@ function normalizeNew(entry) {
826
644
  };
827
645
  }
828
646
  async function readBody(req, maxBytes) {
829
- return await new Promise((resolve5, reject) => {
647
+ return await new Promise((resolve8, reject) => {
830
648
  const chunks = [];
831
649
  let total = 0;
832
650
  req.on("data", (chunk) => {
@@ -839,7 +657,7 @@ async function readBody(req, maxBytes) {
839
657
  chunks.push(chunk);
840
658
  });
841
659
  req.on("end", () => {
842
- resolve5(Buffer.concat(chunks).toString("utf8"));
660
+ resolve8(Buffer.concat(chunks).toString("utf8"));
843
661
  });
844
662
  req.on("error", reject);
845
663
  });
@@ -1001,10 +819,10 @@ async function handleBatchIfMatch(req, res, ctx) {
1001
819
  async function handleBatchInline(req, res, batching, requestId) {
1002
820
  try {
1003
821
  const chunks = [];
1004
- await new Promise((resolve5, reject) => {
822
+ await new Promise((resolve8, reject) => {
1005
823
  req.on("data", (c) => chunks.push(c));
1006
824
  req.on("end", () => {
1007
- resolve5();
825
+ resolve8();
1008
826
  });
1009
827
  req.on("error", reject);
1010
828
  });
@@ -1150,138 +968,483 @@ function createApiMiddleware(vite, serverDir, rateLimitConfigOrOptions) {
1150
968
  };
1151
969
  }
1152
970
 
1153
- // src/vite-plugin/theoui-detect.ts
1154
- import { existsSync, readFileSync } from "fs";
1155
- import { dirname, join } from "path";
1156
- function resolveTheoUiConfig(raw) {
1157
- return {
1158
- theme: raw?.theme ?? "violet-forge",
1159
- fonts: raw?.fonts ?? "bundled"
1160
- };
971
+ // src/vite-plugin/ssr-dev-middleware.ts
972
+ import { readFileSync as readFileSync2 } from "fs";
973
+ import { resolve as resolve2 } from "path";
974
+ function isSsrRenderResult(value) {
975
+ if (typeof value !== "object" || value === null) return false;
976
+ if (!("html" in value)) return false;
977
+ return typeof value.html === "string";
1161
978
  }
1162
- function resolveExportSubpath(pkgRoot, subpath) {
1163
- const pkgJsonPath = join(pkgRoot, "package.json");
1164
- if (!existsSync(pkgJsonPath)) return null;
979
+ function setupSsrDevMiddleware(server, opts) {
980
+ server.middlewares.use((req, res, next) => {
981
+ void (async () => {
982
+ const url = req.url ?? "/";
983
+ if (url.startsWith("/api/") || url.startsWith("/@") || url.startsWith("/node_modules/") || url.includes(".")) {
984
+ next();
985
+ return;
986
+ }
987
+ try {
988
+ const indexPath = resolve2(opts.projectRoot, "index.html");
989
+ let template = readFileSync2(indexPath, "utf-8");
990
+ template = await server.transformIndexHtml(url, template);
991
+ const nonce = generateNonce();
992
+ applySecurityHeaders(
993
+ res,
994
+ opts.securityHeaders ?? {},
995
+ { production: process.env.NODE_ENV === "production" },
996
+ { nonce }
997
+ );
998
+ const mod = await server.ssrLoadModule(opts.virtualEntryServerId);
999
+ const result = await mod.render(url, { nonce });
1000
+ if (result && typeof result === "object" && "redirect" in result) {
1001
+ res.writeHead(302, {
1002
+ Location: result.redirect.headers.get("location") ?? "/"
1003
+ });
1004
+ res.end();
1005
+ return;
1006
+ }
1007
+ let ssrHtml;
1008
+ let hydrationScript = "";
1009
+ if (typeof result === "string") {
1010
+ ssrHtml = result;
1011
+ } else if (isSsrRenderResult(result)) {
1012
+ ssrHtml = result.html;
1013
+ const dataJson = JSON.stringify(result.hydrationData).replace(/</g, "\\u003c");
1014
+ hydrationScript = `<script nonce="${nonce}">window.__staticRouterHydrationData=${dataJson}</script>`;
1015
+ } else {
1016
+ ssrHtml = "";
1017
+ }
1018
+ const rootDivMatch = /<div id=["']root["'][^>]*>/.exec(template);
1019
+ if (!rootDivMatch) {
1020
+ res.writeHead(200, { "Content-Type": "text/html" });
1021
+ res.end(template);
1022
+ return;
1023
+ }
1024
+ const splitIdx = template.indexOf(rootDivMatch[0]) + rootDivMatch[0].length;
1025
+ const html = template.slice(0, splitIdx) + ssrHtml + hydrationScript + template.slice(splitIdx);
1026
+ res.writeHead(200, { "Content-Type": "text/html" });
1027
+ res.end(html);
1028
+ } catch (err) {
1029
+ server.ssrFixStacktrace(err);
1030
+ console.error("[SSR Dev Error]", err);
1031
+ next();
1032
+ return;
1033
+ }
1034
+ })();
1035
+ });
1036
+ }
1037
+
1038
+ // src/vite-plugin/ws-upgrade.ts
1039
+ import { resolve as resolve3 } from "path";
1040
+ function setupWsUpgrade(server, projectRoot) {
1041
+ const wsRoutes = scanWebSocketRoutes(resolve3(projectRoot, "server"));
1042
+ if (wsRoutes.length === 0) return;
1043
+ const wsHttpServer = server.httpServer;
1044
+ if (!wsHttpServer) return;
1045
+ void import("ws").then(({ WebSocketServer }) => {
1046
+ const wss = new WebSocketServer({ noServer: true });
1047
+ wsHttpServer.on("upgrade", (request, socket, head) => {
1048
+ void (async () => {
1049
+ const url = request.url ?? "/";
1050
+ if (!url.startsWith("/ws/")) return;
1051
+ const wsPath = url.split("?")[0];
1052
+ const match = wsRoutes.find((r) => r.wsPath === wsPath);
1053
+ if (!match) {
1054
+ socket.destroy();
1055
+ return;
1056
+ }
1057
+ try {
1058
+ const mod = await server.ssrLoadModule(match.filePath);
1059
+ const handler = mod.default ?? mod;
1060
+ wss.handleUpgrade(request, socket, head, (ws) => {
1061
+ void handler.onOpen?.(ws, request);
1062
+ ws.on("message", (data) => {
1063
+ void handler.onMessage?.(ws, data.toString());
1064
+ });
1065
+ ws.on("close", (code, reason) => {
1066
+ void handler.onClose?.(ws, code, reason);
1067
+ });
1068
+ ws.on("error", (err) => {
1069
+ void handler.onError?.(ws, err);
1070
+ });
1071
+ });
1072
+ } catch {
1073
+ socket.destroy();
1074
+ }
1075
+ })();
1076
+ });
1077
+ }).catch(() => {
1078
+ console.warn(
1079
+ '[Theo] WebSocket routes found but "ws" package not installed. Run: npm install ws'
1080
+ );
1081
+ });
1082
+ }
1083
+
1084
+ // src/vite-plugin/configure-server-hook.ts
1085
+ async function runConfigureServer(server, ctx) {
1086
+ ctx.isDevMode.value = true;
1087
+ globalThis.__theoViteHotServer = server;
1088
+ server.ws.on("theo:devtools:request-manifest", () => {
1089
+ try {
1090
+ const tree = scanRoutes(ctx.appDir);
1091
+ broadcastRouteManifest(tree);
1092
+ } catch {
1093
+ }
1094
+ });
1095
+ const serverDir = resolve4(ctx.projectRoot, "server");
1096
+ server.middlewares.use(
1097
+ createActionMiddleware(server, serverDir, {
1098
+ pluginRunner: ctx.pluginRunner,
1099
+ csrfMode: ctx.csrfMode,
1100
+ disallowed: ctx.disallowed
1101
+ })
1102
+ );
1103
+ const servicesProxyPrefixes = ctx.services && Object.keys(ctx.services).length > 0 ? Object.values(ctx.services).map((s) => s.proxy) : [];
1104
+ const csrfReadinessStore = ctx.csrfReadinessStoreOverride ?? new CsrfReadinessStore();
1105
+ server.middlewares.use(
1106
+ createApiMiddleware(server, serverDir, {
1107
+ rateLimitConfig: ctx.rateLimit,
1108
+ pluginRunner: ctx.pluginRunner,
1109
+ batching: ctx.resolvedBatching,
1110
+ transformer: ctx.transformer,
1111
+ csrfMode: ctx.csrfMode,
1112
+ securityHeaders: ctx.securityHeaders,
1113
+ disallowed: ctx.disallowed,
1114
+ cors: ctx.cors,
1115
+ auditLogger: ctx.auditLogger,
1116
+ servicesProxyPrefixes,
1117
+ csrfReadinessStore
1118
+ })
1119
+ );
1120
+ const configPath = resolve4(ctx.projectRoot, "theo.config.ts");
1121
+ server.watcher.on("change", (file) => {
1122
+ if (file === configPath) {
1123
+ console.warn(
1124
+ "\n[theokit] theo.config.ts changed; restart dev server for plugin changes to take effect\n"
1125
+ );
1126
+ }
1127
+ });
1128
+ if (ctx.ssrEnabled) {
1129
+ setupSsrDevMiddleware(server, {
1130
+ projectRoot: ctx.projectRoot,
1131
+ virtualEntryServerId: ctx.virtualEntryServerId,
1132
+ securityHeaders: ctx.securityHeaders
1133
+ });
1134
+ }
1135
+ function handleRouteChange(filePath) {
1136
+ if (!isRouteFile(basename(filePath))) return;
1137
+ if (!filePath.startsWith(ctx.appDir)) return;
1138
+ const mod = server.moduleGraph.getModuleById(ctx.resolvedManifestId);
1139
+ if (mod) {
1140
+ server.moduleGraph.invalidateModule(mod);
1141
+ server.ws.send({ type: "full-reload" });
1142
+ }
1143
+ }
1144
+ server.watcher.on("add", handleRouteChange);
1145
+ server.watcher.on("unlink", handleRouteChange);
1146
+ if (ctx.resolvedOpenApi !== void 0) {
1147
+ const openApiCfg = ctx.resolvedOpenApi;
1148
+ const { reEmitOpenApi } = await import("./dev-emit-HHP2XJXE.js");
1149
+ const openApiServerDir = resolve4(ctx.projectRoot, "server");
1150
+ const openApiDistDir = resolve4(ctx.projectRoot, ctx.resolvedDistDir);
1151
+ const isRouteFileForOpenApi = (file) => file.startsWith(openApiServerDir) && /\.(ts|tsx|js|mjs)$/.test(file);
1152
+ void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
1153
+ server.watcher.on("change", (file) => {
1154
+ if (isRouteFileForOpenApi(file)) {
1155
+ void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
1156
+ }
1157
+ });
1158
+ server.watcher.on("add", (file) => {
1159
+ if (isRouteFileForOpenApi(file)) {
1160
+ void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
1161
+ }
1162
+ });
1163
+ server.watcher.on("unlink", (file) => {
1164
+ if (isRouteFileForOpenApi(file)) {
1165
+ void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
1166
+ }
1167
+ });
1168
+ }
1169
+ setupWsUpgrade(server, ctx.projectRoot);
1170
+ server.httpServer?.once("close", () => {
1171
+ ;
1172
+ globalThis.__theoViteHotServer = void 0;
1173
+ });
1174
+ }
1175
+
1176
+ // src/vite-plugin/integrate-ui.ts
1177
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
1178
+ import { dirname as dirname3, join as join3 } from "path";
1179
+ import { pathToFileURL } from "url";
1180
+
1181
+ // src/vite-plugin/auto-detect.ts
1182
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
1183
+ import { dirname as dirname2, join as join2 } from "path";
1184
+ function isDeclared(name, projectRoot) {
1165
1185
  try {
1166
- const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
1167
- const exportKey = `./${subpath}`;
1168
- const exp = pkg.exports?.[exportKey];
1169
- if (!exp) {
1170
- const fallback = join(pkgRoot, "dist", subpath);
1171
- return existsSync(fallback) ? fallback : null;
1186
+ const raw = readFileSync3(join2(projectRoot, "package.json"), "utf-8");
1187
+ const pkg = JSON.parse(raw);
1188
+ return Boolean(
1189
+ pkg.dependencies?.[name] ?? pkg.devDependencies?.[name] ?? pkg.peerDependencies?.[name]
1190
+ );
1191
+ } catch {
1192
+ return false;
1193
+ }
1194
+ }
1195
+ function resolvePackageJson(name, cwd) {
1196
+ let dir = cwd;
1197
+ for (let depth = 0; depth < 10; depth++) {
1198
+ const pkgJsonPath = join2(dir, "node_modules", ...name.split("/"), "package.json");
1199
+ if (existsSync3(pkgJsonPath)) {
1200
+ try {
1201
+ const raw = readFileSync3(pkgJsonPath, "utf-8");
1202
+ const pkg = JSON.parse(raw);
1203
+ if (pkg.name === name) {
1204
+ return { path: pkgJsonPath, version: pkg.version };
1205
+ }
1206
+ } catch {
1207
+ }
1208
+ }
1209
+ const parent = dirname2(dir);
1210
+ if (parent === dir) break;
1211
+ dir = parent;
1212
+ }
1213
+ return null;
1214
+ }
1215
+ function fallbackProbe(name, cwd) {
1216
+ const candidates = ["index.mjs", "dist/index.mjs", "dist/index.js", "index.js"];
1217
+ let dir = cwd;
1218
+ for (let depth = 0; depth < 10; depth++) {
1219
+ const pkgDir = join2(dir, "node_modules", ...name.split("/"));
1220
+ for (const candidate of candidates) {
1221
+ const entry = join2(pkgDir, candidate);
1222
+ if (existsSync3(entry)) return { resolvedEntry: entry };
1223
+ }
1224
+ const parent = dirname2(dir);
1225
+ if (parent === dir) break;
1226
+ dir = parent;
1227
+ }
1228
+ return null;
1229
+ }
1230
+ function findOwningPackageJson(entry, expectedName) {
1231
+ let dir = dirname2(entry);
1232
+ for (let i = 0; i < 10; i++) {
1233
+ const candidate = join2(dir, "package.json");
1234
+ if (existsSync3(candidate)) {
1235
+ try {
1236
+ const raw = readFileSync3(candidate, "utf-8");
1237
+ const pkg = JSON.parse(raw);
1238
+ if (pkg.name === expectedName) {
1239
+ return { path: candidate, version: pkg.version };
1240
+ }
1241
+ } catch {
1242
+ }
1243
+ }
1244
+ const parent = dirname2(dir);
1245
+ if (parent === dir) break;
1246
+ dir = parent;
1247
+ }
1248
+ return null;
1249
+ }
1250
+ function detectPackage(name, cwd) {
1251
+ if (!isDeclared(name, cwd)) {
1252
+ return { installed: false };
1253
+ }
1254
+ const pkgJson = resolvePackageJson(name, cwd);
1255
+ if (pkgJson) {
1256
+ return {
1257
+ installed: true,
1258
+ version: pkgJson.version,
1259
+ resolvedPath: pkgJson.path
1260
+ };
1261
+ }
1262
+ let dir = cwd;
1263
+ for (let i = 0; i < 10; i++) {
1264
+ const nmPath = join2(dir, "node_modules", ...name.split("/"));
1265
+ const nmPkgJson = join2(nmPath, "package.json");
1266
+ if (existsSync3(nmPkgJson)) {
1267
+ try {
1268
+ const raw = readFileSync3(nmPkgJson, "utf-8");
1269
+ const pkg = JSON.parse(raw);
1270
+ if (pkg.name === name) {
1271
+ return { installed: true, version: pkg.version, resolvedPath: nmPkgJson };
1272
+ }
1273
+ } catch {
1274
+ }
1275
+ return { installed: true };
1172
1276
  }
1173
- let target;
1174
- if (typeof exp === "string") target = exp;
1175
- else if (typeof exp === "object") {
1176
- const e = exp;
1177
- target = e.import ?? e.default;
1277
+ const parent = dirname2(dir);
1278
+ if (parent === dir) break;
1279
+ dir = parent;
1280
+ }
1281
+ const probe = fallbackProbe(name, cwd);
1282
+ if (probe) {
1283
+ const owning = findOwningPackageJson(probe.resolvedEntry, name);
1284
+ if (owning) {
1285
+ return { installed: true, version: owning.version, resolvedPath: owning.path };
1178
1286
  }
1179
- if (!target) return null;
1180
- const cleaned = target.replace(/^\.\//, "");
1181
- const candidate = join(pkgRoot, cleaned);
1182
- return existsSync(candidate) ? candidate : null;
1183
- } catch {
1184
- return null;
1287
+ return { installed: true };
1185
1288
  }
1289
+ return { installed: false };
1186
1290
  }
1187
- var defaultResolver = (specifier, projectRoot) => {
1188
- const parts = specifier.split("/");
1189
- const pkgName = parts[0]?.startsWith("@") && parts.length >= 2 ? `${parts[0]}/${parts[1]}` : parts[0];
1190
- if (!pkgName) return false;
1191
- const subpath = specifier.slice(pkgName.length + 1);
1192
- if (!subpath) return false;
1193
- let dir = projectRoot;
1194
- for (let depth = 0; depth < 10; depth++) {
1195
- const pkgRoot = join(dir, "node_modules", ...pkgName.split("/"));
1196
- if (existsSync(pkgRoot)) {
1197
- const resolved = resolveExportSubpath(pkgRoot, subpath);
1198
- if (resolved) return true;
1291
+
1292
+ // src/vite-plugin/integrate-ui.ts
1293
+ function resolveConsumerImport(name, cwd) {
1294
+ let dir = cwd;
1295
+ for (let i = 0; i < 10; i++) {
1296
+ const pkgDir = join3(dir, "node_modules", ...name.split("/"));
1297
+ const pkgJsonPath = join3(pkgDir, "package.json");
1298
+ if (existsSync4(pkgJsonPath)) {
1299
+ try {
1300
+ const raw = readFileSync4(pkgJsonPath, "utf-8");
1301
+ const pkg = JSON.parse(raw);
1302
+ let entry;
1303
+ const dotExport = pkg.exports?.["."];
1304
+ if (typeof dotExport === "string") entry = dotExport;
1305
+ else if (dotExport && typeof dotExport === "object") {
1306
+ entry = dotExport.import ?? dotExport.default;
1307
+ }
1308
+ entry ??= pkg.module ?? pkg.main;
1309
+ if (!entry) return null;
1310
+ const cleaned = entry.replace(/^\.\//, "");
1311
+ return pathToFileURL(join3(pkgDir, cleaned)).href;
1312
+ } catch {
1313
+ return null;
1314
+ }
1199
1315
  }
1200
- const parent = dirname(dir);
1316
+ const parent = dirname3(dir);
1201
1317
  if (parent === dir) break;
1202
1318
  dir = parent;
1203
1319
  }
1204
- return false;
1205
- };
1206
- function isDeclaredInPackageJson(projectRoot) {
1320
+ return null;
1321
+ }
1322
+ function isValidPlugin(value) {
1323
+ if (value === null || typeof value !== "object") return false;
1324
+ if (Array.isArray(value)) return false;
1325
+ if (!("name" in value)) return false;
1326
+ return typeof value.name === "string";
1327
+ }
1328
+ function normalizePluginReturn(value) {
1329
+ if (Array.isArray(value)) {
1330
+ const result = [];
1331
+ for (const entry of value) {
1332
+ if (!isValidPlugin(entry)) return null;
1333
+ result.push(entry);
1334
+ }
1335
+ return result.length > 0 ? result : null;
1336
+ }
1337
+ if (isValidPlugin(value)) return [value];
1338
+ return null;
1339
+ }
1340
+ async function integrateUseTheoUI(cwd, opts) {
1341
+ if (opts?.enabled === false) return [];
1342
+ const uiDetect = detectPackage("@theokit/ui", cwd);
1343
+ if (!uiDetect.installed) return [];
1344
+ if (opts?.consumerTailwindConfig || opts?.consumerPostcssConfig) {
1345
+ console.info(
1346
+ `[theokit] Detected your tailwind.config / postcss.config \u2014 skipping auto-config. Extend with \`import preset from '@theokit/ui/preset'\` to apply UI theme.`
1347
+ );
1348
+ return [];
1349
+ }
1350
+ const tailwindDetect = detectPackage("@tailwindcss/vite", cwd);
1351
+ if (!tailwindDetect.installed) {
1352
+ console.warn(
1353
+ `[theokit] @theokit/ui detected but @tailwindcss/vite is not installed. Run \`pnpm add -D @tailwindcss/vite\` to enable styling.`
1354
+ );
1355
+ return [];
1356
+ }
1357
+ const tailwindUrl = resolveConsumerImport("@tailwindcss/vite", cwd);
1358
+ if (!tailwindUrl) {
1359
+ console.warn(
1360
+ `[theokit] @tailwindcss/vite installed but its package entry was not resolvable from ${cwd}. Skipping auto-config.`
1361
+ );
1362
+ return [];
1363
+ }
1364
+ let tailwindMod;
1365
+ let uiMod;
1207
1366
  try {
1208
- const raw = readFileSync(join(projectRoot, "package.json"), "utf-8");
1209
- const pkg = JSON.parse(raw);
1210
- return Boolean(
1211
- pkg.dependencies?.["@theokit/ui"] ?? pkg.devDependencies?.["@theokit/ui"] ?? pkg.peerDependencies?.["@theokit/ui"]
1367
+ tailwindMod = await import(tailwindUrl);
1368
+ } catch (err) {
1369
+ console.warn(
1370
+ `[theokit] @tailwindcss/vite dynamic import failed (${err.message}). Skipping auto-config.`
1212
1371
  );
1213
- } catch {
1214
- return false;
1372
+ return [];
1215
1373
  }
1216
- }
1217
- function detectTheoUi(projectRoot, rawConfig, resolver = defaultResolver) {
1218
- const config = resolveTheoUiConfig(typeof rawConfig === "object" ? rawConfig : void 0);
1219
- if (rawConfig === false) {
1220
- return { enabled: false, config };
1374
+ const uiPkgInstalled = detectPackage("@theokit/ui", cwd);
1375
+ const uiPkgDir = uiPkgInstalled.resolvedPath ? uiPkgInstalled.resolvedPath.replace(/[\\/]package\.json$/, "") : null;
1376
+ const uiPluginPath = uiPkgDir ? join3(uiPkgDir, "dist", "vite-plugin.js") : null;
1377
+ if (!uiPluginPath || !existsSync4(uiPluginPath)) {
1378
+ console.warn(
1379
+ `[theokit] @theokit/ui/vite-plugin entry not found at ${String(uiPluginPath)}. Skipping auto-config.`
1380
+ );
1381
+ return [];
1221
1382
  }
1222
- if (!isDeclaredInPackageJson(projectRoot)) {
1223
- return { enabled: false, config };
1383
+ try {
1384
+ uiMod = await import(pathToFileURL(uiPluginPath).href);
1385
+ } catch (err) {
1386
+ console.warn(
1387
+ `[theokit] @theokit/ui/vite-plugin dynamic import failed (${err.message}). Skipping auto-config.`
1388
+ );
1389
+ return [];
1224
1390
  }
1225
- const probes = ["@theokit/ui/styles.css", "@theokit/ui/fonts.css"];
1226
- for (const probe of probes) {
1227
- if (resolver(probe, projectRoot)) {
1228
- return { enabled: true, config };
1229
- }
1391
+ if (typeof tailwindMod.default !== "function") {
1392
+ console.warn(
1393
+ `[theokit] @tailwindcss/vite does not expose a default-export function. Skipping auto-config.`
1394
+ );
1395
+ return [];
1230
1396
  }
1231
- return { enabled: false, config };
1397
+ if (typeof uiMod.default !== "function") {
1398
+ console.warn(
1399
+ `[theokit] @theokit/ui/vite-plugin does not expose a default-export function. Skipping auto-config.`
1400
+ );
1401
+ return [];
1402
+ }
1403
+ let tailwindPlugin;
1404
+ let uiPlugin;
1405
+ try {
1406
+ tailwindPlugin = tailwindMod.default();
1407
+ } catch (err) {
1408
+ console.warn(`[theokit] @tailwindcss/vite() threw: ${err.message}`);
1409
+ return [];
1410
+ }
1411
+ try {
1412
+ uiPlugin = uiMod.default({
1413
+ tailwind: false
1414
+ });
1415
+ } catch (err) {
1416
+ console.warn(`[theokit] @theokit/ui/vite-plugin() threw: ${err.message}`);
1417
+ return [];
1418
+ }
1419
+ const tailwindPlugins = normalizePluginReturn(tailwindPlugin);
1420
+ if (tailwindPlugins === null) {
1421
+ console.warn(`[theokit] @tailwindcss/vite returned unexpected shape. Skipping auto-config.`);
1422
+ return [];
1423
+ }
1424
+ const uiPlugins = normalizePluginReturn(uiPlugin);
1425
+ if (uiPlugins === null) {
1426
+ console.warn(
1427
+ `[theokit] @theokit/ui/vite-plugin returned unexpected shape. Skipping auto-config.`
1428
+ );
1429
+ return [];
1430
+ }
1431
+ return [...tailwindPlugins, ...uiPlugins];
1232
1432
  }
1233
1433
 
1234
- // src/vite-plugin/config-resolve.ts
1235
- var DEFAULT_RESOLVED = {
1236
- pluginRunner: void 0,
1237
- transformer: void 0,
1238
- resolvedBatching: void 0,
1239
- theoUi: void 0,
1240
- csrfMode: "strict",
1241
- securityHeaders: void 0,
1242
- disallowed: void 0,
1243
- cors: void 0,
1244
- auditLogger: void 0,
1245
- devtoolsEnabled: true,
1246
- openapi: void 0,
1247
- distDir: ".theo"
1248
- };
1249
- async function resolvePluginConfig(projectRoot) {
1250
- try {
1251
- const userConfig = await loadConfig(projectRoot);
1252
- const pluginRunner = await createPluginRunnerFromConfig(userConfig.plugins);
1253
- const transformer = resolveTransformer(userConfig.serialization);
1254
- let resolvedBatching;
1255
- if (userConfig.batching === true) {
1256
- resolvedBatching = {};
1257
- } else if (typeof userConfig.batching === "object") {
1258
- resolvedBatching = userConfig.batching;
1259
- }
1260
- const theoUi = detectTheoUi(projectRoot, userConfig.ui);
1261
- let auditLogger;
1262
- const maybeLogger = userConfig.audit?.logger;
1263
- if (maybeLogger && typeof maybeLogger.log === "function") {
1264
- auditLogger = maybeLogger;
1265
- }
1266
- return {
1267
- pluginRunner,
1268
- transformer,
1269
- resolvedBatching,
1270
- theoUi,
1271
- csrfMode: userConfig.security?.csrf ?? "strict",
1272
- securityHeaders: userConfig.security?.headers,
1273
- disallowed: userConfig.security?.disallowed,
1274
- cors: userConfig.security?.cors,
1275
- auditLogger,
1276
- devtoolsEnabled: userConfig.devtools !== false,
1277
- openapi: userConfig.openapi,
1278
- distDir: userConfig.distDir
1279
- };
1280
- } catch {
1281
- return DEFAULT_RESOLVED;
1434
+ // src/vite-plugin/resolve-theo-root.ts
1435
+ import { existsSync as existsSync5 } from "fs";
1436
+ import { resolve as resolve5 } from "path";
1437
+ function resolveTheoRootDir(currentDir) {
1438
+ if (existsSync5(resolve5(currentDir, "client"))) {
1439
+ return currentDir;
1282
1440
  }
1441
+ return resolve5(currentDir, "..");
1283
1442
  }
1284
1443
 
1444
+ // src/vite-plugin/transform-html-hook.ts
1445
+ import { existsSync as existsSync6 } from "fs";
1446
+ import { resolve as resolve6 } from "path";
1447
+
1285
1448
  // src/vite-plugin/inject-devtools.ts
1286
1449
  var DEVTOOLS_VIRTUAL_ID = "/@theo/devtools/entry.js";
1287
1450
  var DEVTOOLS_RESOLVED_ID = "\0@theo/devtools/entry.js";
@@ -1367,385 +1530,461 @@ function injectStylesheets(html, opts) {
1367
1530
  ${html}`, injected: tagsToInject };
1368
1531
  }
1369
1532
 
1370
- // src/vite-plugin/integrate-ui.ts
1371
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
1372
- import { dirname as dirname3, join as join3 } from "path";
1373
- import { pathToFileURL } from "url";
1533
+ // src/vite-plugin/transform-html-hook.ts
1534
+ function runTransformIndexHtml(html, ctx) {
1535
+ const entry = injectEntryClient(html);
1536
+ if (entry.warning) console.warn(entry.warning);
1537
+ let next = entry.html;
1538
+ const devtools = injectDevtoolsScript(next, {
1539
+ isDev: ctx.isDevMode.value,
1540
+ enabled: ctx.devtoolsEnabled
1541
+ });
1542
+ if (devtools.warning) console.warn(devtools.warning);
1543
+ next = devtools.html;
1544
+ const styles = injectStylesheets(next, {
1545
+ isDev: ctx.isDevMode.value,
1546
+ hasPackage: (name) => existsSync6(resolve6(ctx.projectRoot, "node_modules", ...name.split("/")))
1547
+ });
1548
+ next = styles.html;
1549
+ return next;
1550
+ }
1374
1551
 
1375
- // src/vite-plugin/auto-detect.ts
1376
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
1377
- import { dirname as dirname2, join as join2 } from "path";
1378
- function isDeclared(name, projectRoot) {
1379
- try {
1380
- const raw = readFileSync2(join2(projectRoot, "package.json"), "utf-8");
1381
- const pkg = JSON.parse(raw);
1382
- return Boolean(
1383
- pkg.dependencies?.[name] ?? pkg.devDependencies?.[name] ?? pkg.peerDependencies?.[name]
1384
- );
1385
- } catch {
1386
- return false;
1552
+ // src/router/entry-server.ts
1553
+ function generateEntryServer(options = {}) {
1554
+ if (options.streaming) {
1555
+ return generateStreamingEntry(options);
1387
1556
  }
1557
+ return generateSingleShotEntry(options);
1388
1558
  }
1389
- function resolvePackageJson(name, cwd) {
1390
- let dir = cwd;
1391
- for (let depth = 0; depth < 10; depth++) {
1392
- const pkgJsonPath = join2(dir, "node_modules", ...name.split("/"), "package.json");
1393
- if (existsSync2(pkgJsonPath)) {
1394
- try {
1395
- const raw = readFileSync2(pkgJsonPath, "utf-8");
1396
- const pkg = JSON.parse(raw);
1397
- if (pkg.name === name) {
1398
- return { path: pkgJsonPath, version: pkg.version };
1399
- }
1400
- } catch {
1401
- }
1402
- }
1403
- const parent = dirname2(dir);
1404
- if (parent === dir) break;
1405
- dir = parent;
1559
+ function buildAppTreeJs(options) {
1560
+ const theme = options.theoUi?.theme ?? "violet-forge";
1561
+ if (options.theoUi) {
1562
+ return [
1563
+ `React.createElement(TheoUIProvider, { theme: { defaultTheme: '${theme}' } },`,
1564
+ ` React.createElement(Suspense, { fallback: null },`,
1565
+ ` React.createElement(StaticRouterProvider, { router, context, hydrate: false })`,
1566
+ ` )`,
1567
+ ` )`
1568
+ ].join("\n");
1406
1569
  }
1407
- return null;
1570
+ return `React.createElement(Suspense, { fallback: null },
1571
+ React.createElement(StaticRouterProvider, { router, context, hydrate: false })
1572
+ )`;
1408
1573
  }
1409
- function fallbackProbe(name, cwd) {
1410
- const candidates = ["index.mjs", "dist/index.mjs", "dist/index.js", "index.js"];
1411
- let dir = cwd;
1412
- for (let depth = 0; depth < 10; depth++) {
1413
- const pkgDir = join2(dir, "node_modules", ...name.split("/"));
1414
- for (const candidate of candidates) {
1415
- const entry = join2(pkgDir, candidate);
1416
- if (existsSync2(entry)) return { resolvedEntry: entry };
1417
- }
1418
- const parent = dirname2(dir);
1419
- if (parent === dir) break;
1420
- dir = parent;
1421
- }
1422
- return null;
1574
+ function hydrationDataExtractSnippet() {
1575
+ return [
1576
+ ` const hydrationData = {`,
1577
+ ` loaderData: context.loaderData,`,
1578
+ ` actionData: context.actionData,`,
1579
+ ` errors: context.errors,`,
1580
+ ` }`
1581
+ ].join("\n");
1423
1582
  }
1424
- function findOwningPackageJson(entry, expectedName) {
1425
- let dir = dirname2(entry);
1426
- for (let i = 0; i < 10; i++) {
1427
- const candidate = join2(dir, "package.json");
1428
- if (existsSync2(candidate)) {
1429
- try {
1430
- const raw = readFileSync2(candidate, "utf-8");
1431
- const pkg = JSON.parse(raw);
1432
- if (pkg.name === expectedName) {
1433
- return { path: candidate, version: pkg.version };
1434
- }
1435
- } catch {
1436
- }
1437
- }
1438
- const parent = dirname2(dir);
1439
- if (parent === dir) break;
1440
- dir = parent;
1441
- }
1442
- return null;
1583
+ function generateSingleShotEntry(options) {
1584
+ const theoUiImport = options.theoUi ? `import { TheoUIProvider } from '@theokit/ui'
1585
+ ` : "";
1586
+ const appTree = buildAppTreeJs(options);
1587
+ return [
1588
+ `import React, { Suspense } from 'react'`,
1589
+ `import { renderToPipeableStream } from 'react-dom/server'`,
1590
+ `import { createStaticHandler, createStaticRouter, StaticRouterProvider } from 'react-router'`,
1591
+ `import { PassThrough } from 'node:stream'`,
1592
+ `import { routes } from '/@theo/route-manifest'`,
1593
+ theoUiImport,
1594
+ `export async function render(url, options = {}) {`,
1595
+ ` const handler = createStaticHandler(routes)`,
1596
+ ` const request = new Request('http://localhost' + url)`,
1597
+ ` const context = await handler.query(request)`,
1598
+ ``,
1599
+ ` if (context instanceof Response) {`,
1600
+ ` return { redirect: context }`,
1601
+ ` }`,
1602
+ ``,
1603
+ ` const router = createStaticRouter(handler.dataRoutes, context)`,
1604
+ ` const app = ${appTree}`,
1605
+ ``,
1606
+ hydrationDataExtractSnippet(),
1607
+ ``,
1608
+ ` return new Promise((resolve, reject) => {`,
1609
+ ` let html = ''`,
1610
+ ` let piped = false`,
1611
+ ` const passthrough = new PassThrough()`,
1612
+ ` passthrough.on('data', (chunk) => { html += chunk.toString() })`,
1613
+ ` passthrough.on('end', () => { resolve({ html, hydrationData }) })`,
1614
+ ` passthrough.on('error', reject)`,
1615
+ ``,
1616
+ ` // Pipe on onShellReady (Next.js pattern). Calling pipe() twice`,
1617
+ ` // throws "React currently only supports piping to one writable`,
1618
+ ` // stream". The \`piped\` flag is a belt-and-suspenders guard if`,
1619
+ ` // onShellReady fires unexpectedly more than once.`,
1620
+ ` // Forward options.nonce to React so every <script> tag React`,
1621
+ ` // emits (Suspense boundary scripts) carries the nonce attribute.`,
1622
+ ` const { pipe } = renderToPipeableStream(app, {`,
1623
+ ` nonce: options.nonce,`,
1624
+ ` onShellReady() { if (!piped) { piped = true; pipe(passthrough) } },`,
1625
+ ` onShellError(err) { reject(err) },`,
1626
+ ` onError(err) { console.error('[SSR Error]', err) },`,
1627
+ ` })`,
1628
+ ` })`,
1629
+ `}`
1630
+ ].join("\n");
1631
+ }
1632
+ function streamingWebRenderer(appTree) {
1633
+ return [
1634
+ `// T2.3 \u2014 Web Standards streaming entry for edge runtimes (Cloudflare,`,
1635
+ `// Bun, Deno, Vercel Edge). Uses renderToReadableStream and returns a`,
1636
+ `// Response with the stream as body. Honors request.signal for client`,
1637
+ `// disconnect cleanup.`,
1638
+ `export async function renderStreamingWeb(request, options = {}) {`,
1639
+ ` const handler = createStaticHandler(routes)`,
1640
+ ` const url = new URL(request.url)`,
1641
+ ` const context = await handler.query(request)`,
1642
+ ``,
1643
+ ` if (context instanceof Response) {`,
1644
+ ` return context`,
1645
+ ` }`,
1646
+ ``,
1647
+ ` const router = createStaticRouter(handler.dataRoutes, context)`,
1648
+ ` const app = ${appTree}`,
1649
+ ``,
1650
+ ` const stream = await renderToReadableStream(app, {`,
1651
+ ` signal: request.signal,`,
1652
+ ` nonce: options.nonce,`,
1653
+ ` onError(err) { console.error('[SSR Web Stream Error]', err) },`,
1654
+ ` })`,
1655
+ ` return new Response(stream, {`,
1656
+ ` status: 200,`,
1657
+ ` headers: {`,
1658
+ ` 'Content-Type': 'text/html; charset=utf-8',`,
1659
+ ` 'Transfer-Encoding': 'chunked',`,
1660
+ ` },`,
1661
+ ` })`,
1662
+ `}`
1663
+ ];
1664
+ }
1665
+ function streamingNodeRenderer(appTree) {
1666
+ return [
1667
+ `// T6.1 \u2014 Node streaming SSR entry (opt-in via theo.config.ts > ssrStreaming: true)`,
1668
+ `// Flushes the shell as soon as it's ready, then streams Suspense boundaries.`,
1669
+ `// EC-11: respects request.signal for client-disconnect cleanup.`,
1670
+ `export async function renderStreaming(url, response, options = {}) {`,
1671
+ ` const handler = createStaticHandler(routes)`,
1672
+ ` const request = new Request('http://localhost' + url, { signal: options.signal })`,
1673
+ ` const context = await handler.query(request)`,
1674
+ ``,
1675
+ ` if (context instanceof Response) {`,
1676
+ ` return { redirect: context }`,
1677
+ ` }`,
1678
+ ``,
1679
+ ` const router = createStaticRouter(handler.dataRoutes, context)`,
1680
+ ` const app = ${appTree}`,
1681
+ ``,
1682
+ ` return new Promise((resolve, reject) => {`,
1683
+ ` let didError = false`,
1684
+ ` const stream = renderToPipeableStream(app, {`,
1685
+ ` nonce: options.nonce,`,
1686
+ ` onShellReady() {`,
1687
+ ` response.statusCode = didError ? 500 : 200`,
1688
+ ` response.setHeader('Content-Type', 'text/html; charset=utf-8')`,
1689
+ ` response.setHeader('Transfer-Encoding', 'chunked')`,
1690
+ ` stream.pipe(response)`,
1691
+ ` resolve({ streaming: true })`,
1692
+ ` },`,
1693
+ ` onShellError(err) { reject(err) },`,
1694
+ ` onError(err) {`,
1695
+ ` didError = true`,
1696
+ ` console.error('[SSR Stream Error]', err)`,
1697
+ ` },`,
1698
+ ` })`,
1699
+ ``,
1700
+ ` // EC-11: client disconnect cleanup`,
1701
+ ` if (options.signal) {`,
1702
+ ` options.signal.addEventListener('abort', () => { stream.abort() })`,
1703
+ ` }`,
1704
+ ` })`,
1705
+ `}`
1706
+ ];
1707
+ }
1708
+ function backCompatRenderer(appTree) {
1709
+ return [
1710
+ `// Backward compatibility: keep the single-shot render export available so`,
1711
+ `// callers that always used 'render()' don't break when streaming is on.`,
1712
+ `export async function render(url, options = {}) {`,
1713
+ ` const handler = createStaticHandler(routes)`,
1714
+ ` const request = new Request('http://localhost' + url)`,
1715
+ ` const context = await handler.query(request)`,
1716
+ ``,
1717
+ ` if (context instanceof Response) {`,
1718
+ ` return { redirect: context }`,
1719
+ ` }`,
1720
+ ``,
1721
+ ` const router = createStaticRouter(handler.dataRoutes, context)`,
1722
+ ` const app = ${appTree}`,
1723
+ ``,
1724
+ hydrationDataExtractSnippet(),
1725
+ ``,
1726
+ ` const { PassThrough } = await import('node:stream')`,
1727
+ ` return new Promise((resolve, reject) => {`,
1728
+ ` let html = ''`,
1729
+ ` let piped = false`,
1730
+ ` const passthrough = new PassThrough()`,
1731
+ ` passthrough.on('data', (chunk) => { html += chunk.toString() })`,
1732
+ ` passthrough.on('end', () => { resolve({ html, hydrationData }) })`,
1733
+ ` passthrough.on('error', reject)`,
1734
+ ``,
1735
+ ` // Pipe on onShellReady (Next.js pattern). nonce forwarded.`,
1736
+ ` const { pipe } = renderToPipeableStream(app, {`,
1737
+ ` nonce: options.nonce,`,
1738
+ ` onShellReady() { if (!piped) { piped = true; pipe(passthrough) } },`,
1739
+ ` onShellError(err) { reject(err) },`,
1740
+ ` onError(err) { console.error('[SSR Error]', err) },`,
1741
+ ` })`,
1742
+ ` })`,
1743
+ `}`
1744
+ ];
1745
+ }
1746
+ function generateStreamingEntry(options) {
1747
+ const theoUiImport = options.theoUi ? `import { TheoUIProvider } from '@theokit/ui'
1748
+ ` : "";
1749
+ const appTree = buildAppTreeJs(options);
1750
+ return [
1751
+ `import React, { Suspense } from 'react'`,
1752
+ `import { renderToPipeableStream, renderToReadableStream } from 'react-dom/server'`,
1753
+ `import { createStaticHandler, createStaticRouter, StaticRouterProvider } from 'react-router'`,
1754
+ `import { routes } from '/@theo/route-manifest'`,
1755
+ theoUiImport,
1756
+ ``,
1757
+ ...streamingWebRenderer(appTree),
1758
+ ``,
1759
+ ...streamingNodeRenderer(appTree),
1760
+ ``,
1761
+ ...backCompatRenderer(appTree)
1762
+ ].join("\n");
1443
1763
  }
1444
- function detectPackage(name, cwd) {
1445
- if (!isDeclared(name, cwd)) {
1446
- return { installed: false };
1447
- }
1448
- const pkgJson = resolvePackageJson(name, cwd);
1449
- if (pkgJson) {
1450
- return {
1451
- installed: true,
1452
- version: pkgJson.version,
1453
- resolvedPath: pkgJson.path
1454
- };
1455
- }
1456
- let dir = cwd;
1457
- for (let i = 0; i < 10; i++) {
1458
- const nmPath = join2(dir, "node_modules", ...name.split("/"));
1459
- const nmPkgJson = join2(nmPath, "package.json");
1460
- if (existsSync2(nmPkgJson)) {
1461
- try {
1462
- const raw = readFileSync2(nmPkgJson, "utf-8");
1463
- const pkg = JSON.parse(raw);
1464
- if (pkg.name === name) {
1465
- return { installed: true, version: pkg.version, resolvedPath: nmPkgJson };
1466
- }
1467
- } catch {
1468
- }
1469
- return { installed: true };
1470
- }
1471
- const parent = dirname2(dir);
1472
- if (parent === dir) break;
1473
- dir = parent;
1474
- }
1475
- const probe = fallbackProbe(name, cwd);
1476
- if (probe) {
1477
- const owning = findOwningPackageJson(probe.resolvedEntry, name);
1478
- if (owning) {
1479
- return { installed: true, version: owning.version, resolvedPath: owning.path };
1480
- }
1481
- return { installed: true };
1764
+
1765
+ // src/router/entry.ts
1766
+ function generateEntryClient(ssr, opts = {}) {
1767
+ const rootMethod = ssr ? "hydrateRoot" : "createRoot";
1768
+ const theoUiImports = [];
1769
+ if (opts.theoUi) {
1770
+ theoUiImports.push(
1771
+ `// T2.2 \u2014 TheoUI CSS auto-injected (config.ui)`,
1772
+ `import '@theokit/ui/styles.css'`
1773
+ );
1774
+ const fontsModule = opts.theoUi.fonts === "cdn" ? "fonts-cdn.css" : "fonts.css";
1775
+ theoUiImports.push(
1776
+ `import '@theokit/ui/${fontsModule}'`,
1777
+ `// T2.3 \u2014 TheoUIProvider auto-wraps RouterProvider`,
1778
+ `import { TheoUIProvider } from '@theokit/ui'`
1779
+ );
1482
1780
  }
1483
- return { installed: false };
1781
+ const theme = opts.theoUi?.theme ?? "violet-forge";
1782
+ const routerTree = opts.theoUi ? `React.createElement(TheoUIProvider, { theme: { defaultTheme: '${theme}' } },
1783
+ React.createElement(Suspense, { fallback: null },
1784
+ React.createElement(RouterProvider, { router })
1785
+ )
1786
+ )` : `React.createElement(Suspense, { fallback: null },
1787
+ React.createElement(RouterProvider, { router })
1788
+ )`;
1789
+ const renderCall = ssr ? ` ${rootMethod}(el,
1790
+ ${routerTree}
1791
+ )` : ` ${rootMethod}(el).render(
1792
+ ${routerTree}
1793
+ )`;
1794
+ const hydrationLine = ssr ? `const router = createBrowserRouter(routes, { hydrationData: window.__staticRouterHydrationData })` : `const router = createBrowserRouter(routes)`;
1795
+ const preloadBlock = ssr ? [
1796
+ `// Phase 4 \u2014 preload matched-route modules before hydrate (EC-3 safeguard)`,
1797
+ `const __theoMatches = matchRoutes(routes, window.location.pathname) ?? []`,
1798
+ `const __theoPreloadPaths = __theoMatches`,
1799
+ ` .map((m) => m.route && (m.route).path)`,
1800
+ ` .filter((p) => typeof p === 'string' && p in __theoPreloadMap)`,
1801
+ `const __theoPreloadPromise = Promise.all(`,
1802
+ ` __theoPreloadPaths.map((p) => __theoPreloadMap[p]().catch((err) => { console.error('[theo] preload failed', p, err); return null }))`,
1803
+ `)`,
1804
+ `const __theoTimeout = new Promise((resolve) => setTimeout(() => resolve('timeout'), 1500))`,
1805
+ `await Promise.race([__theoPreloadPromise, __theoTimeout])`,
1806
+ ""
1807
+ ] : [];
1808
+ const renderBlock = ssr ? [
1809
+ `if (el) {`,
1810
+ ` ;(async () => {`,
1811
+ ...preloadBlock.map((l) => " " + l),
1812
+ renderCall,
1813
+ ` })()`,
1814
+ `}`
1815
+ ] : [`if (el) {`, renderCall, `}`];
1816
+ const manifestImports = ssr ? `import { routes, __theoPreloadMap } from '/@theo/route-manifest'` : `import { routes } from '/@theo/route-manifest'`;
1817
+ const reactRouterImports = ssr ? `import { createBrowserRouter, RouterProvider, matchRoutes } from 'react-router'` : `import { createBrowserRouter, RouterProvider } from 'react-router'`;
1818
+ return [
1819
+ `import React, { Suspense } from 'react'`,
1820
+ `import { ${rootMethod} } from 'react-dom/client'`,
1821
+ reactRouterImports,
1822
+ manifestImports,
1823
+ `// T1.3 \u2014 side-effect import sets globalThis.__THEO_TRANSFORMER__ for theoFetch`,
1824
+ `import '/@theo/runtime-config'`,
1825
+ ...theoUiImports,
1826
+ ``,
1827
+ hydrationLine,
1828
+ `const el = document.getElementById('root')`,
1829
+ ...renderBlock
1830
+ ].join("\n");
1484
1831
  }
1485
1832
 
1486
- // src/vite-plugin/integrate-ui.ts
1487
- function resolveConsumerImport(name, cwd) {
1488
- let dir = cwd;
1489
- for (let i = 0; i < 10; i++) {
1490
- const pkgDir = join3(dir, "node_modules", ...name.split("/"));
1491
- const pkgJsonPath = join3(pkgDir, "package.json");
1492
- if (existsSync3(pkgJsonPath)) {
1493
- try {
1494
- const raw = readFileSync3(pkgJsonPath, "utf-8");
1495
- const pkg = JSON.parse(raw);
1496
- let entry;
1497
- const dotExport = pkg.exports?.["."];
1498
- if (typeof dotExport === "string") entry = dotExport;
1499
- else if (dotExport && typeof dotExport === "object") {
1500
- entry = dotExport.import ?? dotExport.default;
1501
- }
1502
- entry ??= pkg.module ?? pkg.main;
1503
- if (!entry) return null;
1504
- const cleaned = entry.replace(/^\.\//, "");
1505
- return pathToFileURL(join3(pkgDir, cleaned)).href;
1506
- } catch {
1507
- return null;
1508
- }
1509
- }
1510
- const parent = dirname3(dir);
1511
- if (parent === dir) break;
1512
- dir = parent;
1513
- }
1514
- return null;
1833
+ // src/router/generate.ts
1834
+ function normalizePath(p) {
1835
+ return p.replace(/\\/g, "/");
1515
1836
  }
1516
- function isValidPlugin(value) {
1517
- if (value === null || typeof value !== "object") return false;
1518
- if (Array.isArray(value)) return false;
1519
- if (!("name" in value)) return false;
1520
- return typeof value.name === "string";
1837
+ function safeVarName(segment, prefix) {
1838
+ const safe = segment.replace(/[^a-zA-Z0-9]/g, "_") || "root";
1839
+ return `${prefix}_${safe}`;
1521
1840
  }
1522
- function normalizePluginReturn(value) {
1523
- if (Array.isArray(value)) {
1524
- const result = [];
1525
- for (const entry of value) {
1526
- if (!isValidPlugin(entry)) return null;
1527
- result.push(entry);
1528
- }
1529
- return result.length > 0 ? result : null;
1530
- }
1531
- if (isValidPlugin(value)) return [value];
1532
- return null;
1841
+ function buildRoutePath(parents, segment) {
1842
+ const joined = [...parents, segment].filter(Boolean).join("/");
1843
+ return "/" + joined;
1533
1844
  }
1534
- async function integrateUseTheoUI(cwd, opts) {
1535
- if (opts?.enabled === false) return [];
1536
- const uiDetect = detectPackage("@theokit/ui", cwd);
1537
- if (!uiDetect.installed) return [];
1538
- if (opts?.consumerTailwindConfig || opts?.consumerPostcssConfig) {
1539
- console.info(
1540
- `[theokit] Detected your tailwind.config / postcss.config \u2014 skipping auto-config. Extend with \`import preset from '@theokit/ui/preset'\` to apply UI theme.`
1541
- );
1542
- return [];
1543
- }
1544
- const tailwindDetect = detectPackage("@tailwindcss/vite", cwd);
1545
- if (!tailwindDetect.installed) {
1546
- console.warn(
1547
- `[theokit] @theokit/ui detected but @tailwindcss/vite is not installed. Run \`pnpm add -D @tailwindcss/vite\` to enable styling.`
1548
- );
1549
- return [];
1550
- }
1551
- const tailwindUrl = resolveConsumerImport("@tailwindcss/vite", cwd);
1552
- if (!tailwindUrl) {
1553
- console.warn(
1554
- `[theokit] @tailwindcss/vite installed but its package entry was not resolvable from ${cwd}. Skipping auto-config.`
1555
- );
1556
- return [];
1557
- }
1558
- let tailwindMod;
1559
- let uiMod;
1560
- try {
1561
- tailwindMod = await import(tailwindUrl);
1562
- } catch (err) {
1563
- console.warn(
1564
- `[theokit] @tailwindcss/vite dynamic import failed (${err.message}). Skipping auto-config.`
1565
- );
1566
- return [];
1567
- }
1568
- const uiPkgInstalled = detectPackage("@theokit/ui", cwd);
1569
- const uiPkgDir = uiPkgInstalled.resolvedPath ? uiPkgInstalled.resolvedPath.replace(/[\\/]package\.json$/, "") : null;
1570
- const uiPluginPath = uiPkgDir ? join3(uiPkgDir, "dist", "vite-plugin.js") : null;
1571
- if (!uiPluginPath || !existsSync3(uiPluginPath)) {
1572
- console.warn(
1573
- `[theokit] @theokit/ui/vite-plugin entry not found at ${String(uiPluginPath)}. Skipping auto-config.`
1574
- );
1575
- return [];
1576
- }
1577
- try {
1578
- uiMod = await import(pathToFileURL(uiPluginPath).href);
1579
- } catch (err) {
1580
- console.warn(
1581
- `[theokit] @theokit/ui/vite-plugin dynamic import failed (${err.message}). Skipping auto-config.`
1582
- );
1583
- return [];
1584
- }
1585
- if (typeof tailwindMod.default !== "function") {
1586
- console.warn(
1587
- `[theokit] @tailwindcss/vite does not expose a default-export function. Skipping auto-config.`
1588
- );
1589
- return [];
1845
+ function pushIf(staticImports, filePath, varName) {
1846
+ if (filePath !== void 0) {
1847
+ staticImports.push({ varName, importPath: normalizePath(filePath) });
1590
1848
  }
1591
- if (typeof uiMod.default !== "function") {
1592
- console.warn(
1593
- `[theokit] @theokit/ui/vite-plugin does not expose a default-export function. Skipping auto-config.`
1594
- );
1595
- return [];
1849
+ }
1850
+ function walkRouteTree(node, parents, acc) {
1851
+ const seg = node.segment || "root";
1852
+ const routePath = buildRoutePath(parents, node.segment);
1853
+ if (node.page !== void 0) {
1854
+ acc.lazyPages.push({
1855
+ varName: safeVarName(seg, "Page"),
1856
+ importPath: normalizePath(node.page),
1857
+ routePath
1858
+ });
1596
1859
  }
1597
- let tailwindPlugin;
1598
- let uiPlugin;
1599
- try {
1600
- tailwindPlugin = tailwindMod.default();
1601
- } catch (err) {
1602
- console.warn(`[theokit] @tailwindcss/vite() threw: ${err.message}`);
1603
- return [];
1860
+ if (node.layout !== void 0) acc.layoutState.found = true;
1861
+ pushIf(acc.staticImports, node.layout, safeVarName(seg, "Layout"));
1862
+ pushIf(acc.staticImports, node.error, safeVarName(seg, "Error"));
1863
+ pushIf(acc.staticImports, node.loading, safeVarName(seg, "Loading"));
1864
+ pushIf(acc.staticImports, node.notFound, safeVarName(seg, "NotFound"));
1865
+ for (const child of node.children) {
1866
+ const nextParents = node.segment ? [...parents, node.segment] : parents;
1867
+ walkRouteTree(child, nextParents, acc);
1604
1868
  }
1605
- try {
1606
- uiPlugin = uiMod.default({
1607
- tailwind: false
1608
- });
1609
- } catch (err) {
1610
- console.warn(`[theokit] @theokit/ui/vite-plugin() threw: ${err.message}`);
1611
- return [];
1869
+ }
1870
+ function generateRouteManifest(tree) {
1871
+ const acc = {
1872
+ staticImports: [],
1873
+ lazyPages: [],
1874
+ layoutState: { found: false }
1875
+ };
1876
+ walkRouteTree(tree, [], acc);
1877
+ const staticImports = acc.staticImports;
1878
+ const lazyPages = acc.lazyPages;
1879
+ const layoutState = acc.layoutState;
1880
+ const lines = [`import React, { Suspense } from 'react'`];
1881
+ if (layoutState.found) {
1882
+ lines.push(`import { Outlet } from 'react-router'`);
1612
1883
  }
1613
- const tailwindPlugins = normalizePluginReturn(tailwindPlugin);
1614
- if (tailwindPlugins === null) {
1615
- console.warn(`[theokit] @tailwindcss/vite returned unexpected shape. Skipping auto-config.`);
1616
- return [];
1884
+ lines.push("");
1885
+ for (const imp of staticImports) {
1886
+ lines.push(`import ${imp.varName} from '${imp.importPath}'`);
1617
1887
  }
1618
- const uiPlugins = normalizePluginReturn(uiPlugin);
1619
- if (uiPlugins === null) {
1620
- console.warn(
1621
- `[theokit] @theokit/ui/vite-plugin returned unexpected shape. Skipping auto-config.`
1622
- );
1623
- return [];
1888
+ for (const lp of lazyPages) {
1889
+ lines.push(`const ${lp.varName} = React.lazy(() => import('${lp.importPath}'))`);
1624
1890
  }
1625
- return [...tailwindPlugins, ...uiPlugins];
1626
- }
1627
-
1628
- // src/vite-plugin/resolve-theo-root.ts
1629
- import { existsSync as existsSync4 } from "fs";
1630
- import { resolve } from "path";
1631
- function resolveTheoRootDir(currentDir) {
1632
- if (existsSync4(resolve(currentDir, "client"))) {
1633
- return currentDir;
1891
+ lines.push("");
1892
+ const preloadEntries = lazyPages.map(
1893
+ (lp) => ` '${lp.routePath}': () => import('${lp.importPath}'),`
1894
+ );
1895
+ lines.push("export const __theoPreloadMap = {");
1896
+ for (const e of preloadEntries) lines.push(e);
1897
+ lines.push("}", "");
1898
+ function buildChildrenArray(node, seg) {
1899
+ const childConfigs = [];
1900
+ if (node.page) {
1901
+ const pageVar = safeVarName(seg, "Page");
1902
+ const fallbackEl = node.loading ? `React.createElement(${safeVarName(seg, "Loading")})` : "null";
1903
+ const pageElement = `React.createElement(Suspense, { fallback: ${fallbackEl} }, React.createElement(${pageVar}))`;
1904
+ childConfigs.push(`{ index: true, element: ${pageElement} }`);
1905
+ }
1906
+ for (const child of node.children) {
1907
+ childConfigs.push(genRouteConfig(child, false));
1908
+ }
1909
+ if (node.notFound) {
1910
+ const nfVar = safeVarName(seg, "NotFound");
1911
+ childConfigs.push(`{ path: '*', element: React.createElement(${nfVar}) }`);
1912
+ }
1913
+ let arr = `[${childConfigs.join(", ")}]`;
1914
+ if (node.error) {
1915
+ const errVar = safeVarName(seg, "Error");
1916
+ arr = `[{ errorElement: React.createElement(${errVar}), children: ${arr} }]`;
1917
+ }
1918
+ return arr;
1634
1919
  }
1635
- return resolve(currentDir, "..");
1636
- }
1637
-
1638
- // src/vite-plugin/ssr-dev-middleware.ts
1639
- import { readFileSync as readFileSync4 } from "fs";
1640
- import { resolve as resolve2 } from "path";
1641
- function isSsrRenderResult(value) {
1642
- if (typeof value !== "object" || value === null) return false;
1643
- if (!("html" in value)) return false;
1644
- return typeof value.html === "string";
1645
- }
1646
- function setupSsrDevMiddleware(server, opts) {
1647
- server.middlewares.use((req, res, next) => {
1648
- void (async () => {
1649
- const url = req.url ?? "/";
1650
- if (url.startsWith("/api/") || url.startsWith("/@") || url.startsWith("/node_modules/") || url.includes(".")) {
1651
- next();
1652
- return;
1653
- }
1654
- try {
1655
- const indexPath = resolve2(opts.projectRoot, "index.html");
1656
- let template = readFileSync4(indexPath, "utf-8");
1657
- template = await server.transformIndexHtml(url, template);
1658
- const nonce = generateNonce();
1659
- applySecurityHeaders(
1660
- res,
1661
- opts.securityHeaders ?? {},
1662
- { production: process.env.NODE_ENV === "production" },
1663
- { nonce }
1664
- );
1665
- const mod = await server.ssrLoadModule(opts.virtualEntryServerId);
1666
- const result = await mod.render(url, { nonce });
1667
- if (result && typeof result === "object" && "redirect" in result) {
1668
- res.writeHead(302, {
1669
- Location: result.redirect.headers.get("location") ?? "/"
1670
- });
1671
- res.end();
1672
- return;
1673
- }
1674
- let ssrHtml;
1675
- let hydrationScript = "";
1676
- if (typeof result === "string") {
1677
- ssrHtml = result;
1678
- } else if (isSsrRenderResult(result)) {
1679
- ssrHtml = result.html;
1680
- const dataJson = JSON.stringify(result.hydrationData).replace(/</g, "\\u003c");
1681
- hydrationScript = `<script nonce="${nonce}">window.__staticRouterHydrationData=${dataJson}</script>`;
1682
- } else {
1683
- ssrHtml = "";
1684
- }
1685
- const rootDivMatch = /<div id=["']root["'][^>]*>/.exec(template);
1686
- if (!rootDivMatch) {
1687
- res.writeHead(200, { "Content-Type": "text/html" });
1688
- res.end(template);
1689
- return;
1690
- }
1691
- const splitIdx = template.indexOf(rootDivMatch[0]) + rootDivMatch[0].length;
1692
- const html = template.slice(0, splitIdx) + ssrHtml + hydrationScript + template.slice(splitIdx);
1693
- res.writeHead(200, { "Content-Type": "text/html" });
1694
- res.end(html);
1695
- } catch (err) {
1696
- server.ssrFixStacktrace(err);
1697
- console.error("[SSR Dev Error]", err);
1698
- next();
1699
- return;
1920
+ function genRouteConfig(node, isRoot) {
1921
+ const seg = node.segment || "root";
1922
+ const childrenArray = buildChildrenArray(node, seg);
1923
+ if (node.layout) {
1924
+ const layoutVar = safeVarName(seg, "Layout");
1925
+ const pathPart = isRoot ? `path: '/'` : `path: '${node.segment}'`;
1926
+ return `{ ${pathPart}, element: React.createElement(${layoutVar}, { children: React.createElement(Outlet) }), children: ${childrenArray} }`;
1927
+ }
1928
+ if (isRoot) {
1929
+ if (node.children.length === 0 && !node.page && !node.notFound && !node.error) {
1930
+ return `{ path: '/', children: [] }`;
1700
1931
  }
1701
- })();
1702
- });
1932
+ return `{ path: '/', children: ${childrenArray} }`;
1933
+ }
1934
+ if (node.page && node.children.length === 0 && !node.error && !node.notFound) {
1935
+ const pageVar = safeVarName(seg, "Page");
1936
+ const fallbackEl = node.loading ? `React.createElement(${safeVarName(seg, "Loading")})` : "null";
1937
+ const pageElement = `React.createElement(Suspense, { fallback: ${fallbackEl} }, React.createElement(${pageVar}))`;
1938
+ return `{ path: '${node.segment}', element: ${pageElement} }`;
1939
+ }
1940
+ return `{ path: '${node.segment}', children: ${childrenArray} }`;
1941
+ }
1942
+ const routeConfig = genRouteConfig(tree, true);
1943
+ lines.push(`export const routes = [${routeConfig}]`);
1944
+ return lines.join("\n");
1703
1945
  }
1704
1946
 
1705
- // src/vite-plugin/ws-upgrade.ts
1706
- import { resolve as resolve3 } from "path";
1707
- function setupWsUpgrade(server, projectRoot) {
1708
- const wsRoutes = scanWebSocketRoutes(resolve3(projectRoot, "server"));
1709
- if (wsRoutes.length === 0) return;
1710
- const wsHttpServer = server.httpServer;
1711
- if (!wsHttpServer) return;
1712
- void import("ws").then(({ WebSocketServer }) => {
1713
- const wss = new WebSocketServer({ noServer: true });
1714
- wsHttpServer.on("upgrade", (request, socket, head) => {
1715
- void (async () => {
1716
- const url = request.url ?? "/";
1717
- if (!url.startsWith("/ws/")) return;
1718
- const wsPath = url.split("?")[0];
1719
- const match = wsRoutes.find((r) => r.wsPath === wsPath);
1720
- if (!match) {
1721
- socket.destroy();
1722
- return;
1723
- }
1724
- try {
1725
- const mod = await server.ssrLoadModule(match.filePath);
1726
- const handler = mod.default ?? mod;
1727
- wss.handleUpgrade(request, socket, head, (ws) => {
1728
- void handler.onOpen?.(ws, request);
1729
- ws.on("message", (data) => {
1730
- void handler.onMessage?.(ws, data.toString());
1731
- });
1732
- ws.on("close", (code, reason) => {
1733
- void handler.onClose?.(ws, code, reason);
1734
- });
1735
- ws.on("error", (err) => {
1736
- void handler.onError?.(ws, err);
1737
- });
1738
- });
1739
- } catch {
1740
- socket.destroy();
1741
- }
1742
- })();
1947
+ // src/vite-plugin/virtual-modules-hook.ts
1948
+ function resolveVirtualModuleId(id, ids) {
1949
+ if (id === ids.VIRTUAL_ENTRY_ID) return ids.RESOLVED_ENTRY_ID;
1950
+ if (id === ids.VIRTUAL_MANIFEST_ID) return ids.RESOLVED_MANIFEST_ID;
1951
+ if (id === ids.VIRTUAL_ENTRY_SERVER_ID) return ids.RESOLVED_ENTRY_SERVER_ID;
1952
+ if (id === ids.VIRTUAL_RUNTIME_CONFIG_ID) return ids.RESOLVED_RUNTIME_CONFIG_ID;
1953
+ if (id === DEVTOOLS_VIRTUAL_ID) return DEVTOOLS_RESOLVED_ID;
1954
+ return void 0;
1955
+ }
1956
+ function loadVirtualModule(id, ctx) {
1957
+ if (id === ctx.ids.RESOLVED_ENTRY_ID) {
1958
+ return generateEntryClient(ctx.ssrEnabled, {
1959
+ theoUi: ctx.theoUi?.enabled ? { fonts: ctx.theoUi.config.fonts, theme: ctx.theoUi.config.theme } : void 0
1743
1960
  });
1744
- }).catch(() => {
1745
- console.warn(
1746
- '[Theo] WebSocket routes found but "ws" package not installed. Run: npm install ws'
1747
- );
1748
- });
1961
+ }
1962
+ if (id === ctx.ids.RESOLVED_MANIFEST_ID) {
1963
+ const tree = scanRoutes(ctx.appDir);
1964
+ broadcastRouteManifest(tree);
1965
+ return generateRouteManifest(tree);
1966
+ }
1967
+ if (id === ctx.ids.RESOLVED_ENTRY_SERVER_ID) {
1968
+ return generateEntryServer({
1969
+ streaming: ctx.streamingEnabled,
1970
+ theoUi: ctx.theoUi?.enabled ? { theme: ctx.theoUi.config.theme } : void 0
1971
+ });
1972
+ }
1973
+ if (id === ctx.ids.RESOLVED_RUNTIME_CONFIG_ID) {
1974
+ const tName = ctx.transformer?.name ?? "json";
1975
+ return [
1976
+ `// Generated by Theo Vite plugin`,
1977
+ `;(globalThis).__THEO_TRANSFORMER__ = ${JSON.stringify(tName)}`,
1978
+ `export const TRANSFORMER_NAME = ${JSON.stringify(tName)}`
1979
+ ].join("\n");
1980
+ }
1981
+ if (id === DEVTOOLS_RESOLVED_ID) {
1982
+ return [
1983
+ `// Theo devtools \u2014 virtual entry (DEV only)`,
1984
+ `import('theokit/devtools/entry').catch((e) => console.error('[theo devtools] mount failed', e))`
1985
+ ].join("\n");
1986
+ }
1987
+ return void 0;
1749
1988
  }
1750
1989
 
1751
1990
  // src/vite-plugin/integrations.ts
@@ -1858,8 +2097,8 @@ function findConsumerConfig(projectRoot, basename2) {
1858
2097
  let dir = projectRoot;
1859
2098
  for (let level = 0; level < 3; level++) {
1860
2099
  for (const ext of extensions) {
1861
- const candidate = resolve4(dir, `${basename2}${ext}`);
1862
- if (existsSync5(candidate)) return candidate;
2100
+ const candidate = resolve7(dir, `${basename2}${ext}`);
2101
+ if (existsSync7(candidate)) return candidate;
1863
2102
  }
1864
2103
  const parent = dirname4(dir);
1865
2104
  if (parent === dir) break;
@@ -1878,7 +2117,7 @@ async function theoPluginAsync(rootOrOptions) {
1878
2117
  });
1879
2118
  const servicesPlugins = [];
1880
2119
  if (options.services && Object.keys(options.services).length > 0) {
1881
- const { servicesTypedClientPlugin } = await import("./services-typed-client-ZX5JUHH3.js");
2120
+ const { servicesTypedClientPlugin } = await import("./services-typed-client-T7M5VPBP.js");
1882
2121
  servicesPlugins.push(
1883
2122
  servicesTypedClientPlugin({
1884
2123
  cwd: projectRoot,
@@ -1886,20 +2125,20 @@ async function theoPluginAsync(rootOrOptions) {
1886
2125
  })
1887
2126
  );
1888
2127
  }
1889
- const { appTypedClientPlugin } = await import("./app-typed-client-ITIYTBXO.js");
2128
+ const { appTypedClientPlugin } = await import("./app-typed-client-OUJO3V5J.js");
1890
2129
  const appClientPlugin = appTypedClientPlugin({
1891
2130
  cwd: projectRoot,
1892
- serverDir: resolve4(projectRoot, "server"),
1893
- distDir: resolve4(projectRoot, ".theo")
2131
+ serverDir: resolve7(projectRoot, "server"),
2132
+ distDir: resolve7(projectRoot, ".theo")
1894
2133
  });
1895
- const { actionsVirtualModule } = await import("./actions-virtual-module-HWNTIEPI.js");
2134
+ const { actionsVirtualModule } = await import("./actions-virtual-module-IY46EEEX.js");
1896
2135
  const actionsPlugin = actionsVirtualModule({
1897
- serverDir: resolve4(projectRoot, "server"),
1898
- distDir: resolve4(projectRoot, ".theo")
2136
+ serverDir: resolve7(projectRoot, "server"),
2137
+ distDir: resolve7(projectRoot, ".theo")
1899
2138
  });
1900
2139
  const { serverRoutesHmrPlugin } = await import("./server-routes-hmr-GZJGPWMS.js");
1901
2140
  const routesHmrPlugin = serverRoutesHmrPlugin({
1902
- serverDir: resolve4(projectRoot, "server")
2141
+ serverDir: resolve7(projectRoot, "server")
1903
2142
  });
1904
2143
  return [
1905
2144
  theoPlugin(rootOrOptions),
@@ -1912,10 +2151,10 @@ async function theoPluginAsync(rootOrOptions) {
1912
2151
  }
1913
2152
  function buildOptimizeDepsInclude(projectRoot, viteOptimizeDeps) {
1914
2153
  const include = [];
1915
- if (existsSync5(resolve4(projectRoot, "node_modules", "@theokit", "ui"))) {
2154
+ if (existsSync7(resolve7(projectRoot, "node_modules", "@theokit", "ui"))) {
1916
2155
  include.push("@theokit/ui");
1917
2156
  }
1918
- if (existsSync5(resolve4(projectRoot, "node_modules", "lucide-react"))) {
2157
+ if (existsSync7(resolve7(projectRoot, "node_modules", "lucide-react"))) {
1919
2158
  include.push("lucide-react");
1920
2159
  }
1921
2160
  include.push("devalue");
@@ -1928,10 +2167,20 @@ function buildOptimizeDepsInclude(projectRoot, viteOptimizeDeps) {
1928
2167
  }
1929
2168
  return include;
1930
2169
  }
2170
+ var VIRTUAL_MODULE_IDS = {
2171
+ VIRTUAL_ENTRY_ID,
2172
+ RESOLVED_ENTRY_ID,
2173
+ VIRTUAL_MANIFEST_ID,
2174
+ RESOLVED_MANIFEST_ID,
2175
+ VIRTUAL_ENTRY_SERVER_ID,
2176
+ RESOLVED_ENTRY_SERVER_ID,
2177
+ VIRTUAL_RUNTIME_CONFIG_ID,
2178
+ RESOLVED_RUNTIME_CONFIG_ID
2179
+ };
1931
2180
  function theoPlugin(rootOrOptions) {
1932
2181
  const options = typeof rootOrOptions === "string" ? { root: rootOrOptions } : rootOrOptions ?? {};
1933
2182
  const projectRoot = options.root ?? process.cwd();
1934
- const appDir = resolve4(projectRoot, "app");
2183
+ const appDir = resolve7(projectRoot, "app");
1935
2184
  const ssrEnabled = options.ssr ?? false;
1936
2185
  const currentDir = dirname4(fileURLToPath(import.meta.url));
1937
2186
  const theoSrcDir = resolveTheoRootDir(currentDir);
@@ -1947,7 +2196,7 @@ function theoPlugin(rootOrOptions) {
1947
2196
  let devtoolsEnabled = true;
1948
2197
  let resolvedOpenApi;
1949
2198
  let resolvedDistDir = ".theo";
1950
- let isDevMode = false;
2199
+ const isDevModeRef = { value: false };
1951
2200
  let configLoadedOnce = false;
1952
2201
  return {
1953
2202
  name: "theo",
@@ -1968,223 +2217,73 @@ function theoPlugin(rootOrOptions) {
1968
2217
  resolvedOpenApi = resolved.openapi;
1969
2218
  resolvedDistDir = resolved.distDir;
1970
2219
  },
1971
- // Vite calls this hook (sync return is fine no awaits left after
1972
- // the auto-chain moved to theoPluginAsync). The @theokit/ui +
1973
- // @tailwindcss/vite auto-chain lives in `theoPluginAsync` because
1974
- // Vite drops plugins returned from a child plugin's config() hook.
2220
+ // T2.6 (M6) config() body extracted to config-hook.ts. The hook
2221
+ // owns optimizeDeps + warmup + services proxy + alias cascade.
2222
+ // The @theokit/ui + @tailwindcss/vite auto-chain lives in
2223
+ // `theoPluginAsync` because Vite drops plugins returned from a
2224
+ // child plugin's config() hook.
1975
2225
  config() {
1976
- const ext = existsSync5(resolve4(theoSrcDir, "index.ts")) ? ".ts" : ".js";
1977
- const optimizeDepsInclude = buildOptimizeDepsInclude(projectRoot, options.viteOptimizeDeps);
1978
- const warmupClientFiles = [];
1979
- for (const candidate of ["app/layout.tsx", "app/page.tsx", "app/page.jsx"]) {
1980
- const full = resolve4(projectRoot, candidate);
1981
- if (existsSync5(full)) warmupClientFiles.push(`./${candidate}`);
1982
- }
1983
- const servicesProxy = options.services && Object.keys(options.services).length > 0 ? buildServicesProxyConfig(options.services) : void 0;
1984
- return {
1985
- envPrefix: "THEO_PUBLIC_",
1986
- optimizeDeps: {
1987
- ...optimizeDepsInclude.length > 0 ? { include: optimizeDepsInclude } : {},
1988
- force: true
1989
- },
1990
- server: {
1991
- ...warmupClientFiles.length > 0 ? { warmup: { clientFiles: warmupClientFiles } } : {},
1992
- ...servicesProxy !== void 0 ? { proxy: servicesProxy } : {},
1993
- // Skip watching gitignored heavy dirs to avoid hitting ENOSPC
1994
- // (inotify watcher exhaustion). `referencias/` are deep-research
1995
- // clones; `.theokit/` is per-conversation cache; `.theo/` is
1996
- // build output. None should trigger HMR.
1997
- watch: {
1998
- ignored: [
1999
- "**/referencias/**",
2000
- "**/.theokit/**",
2001
- "**/.theo/**",
2002
- "**/dist/**",
2003
- "**/node_modules/**"
2004
- ]
2005
- }
2006
- },
2007
- resolve: {
2008
- alias: [
2009
- // Order matters: most-specific first so `theokit/X` doesn't
2010
- // get matched by the bare `theokit` alias.
2011
- { find: "theokit/server", replacement: resolve4(theoSrcDir, `server/index${ext}`) },
2012
- { find: "theokit/client", replacement: resolve4(theoSrcDir, `client/index${ext}`) },
2013
- {
2014
- find: "theokit/react-query",
2015
- replacement: resolve4(theoSrcDir, `react-query/index${ext}`)
2016
- },
2017
- {
2018
- find: "theokit/vite-plugin",
2019
- replacement: resolve4(theoSrcDir, `vite-plugin/index${ext}`)
2020
- },
2021
- {
2022
- find: "theokit/adapters/web-shim",
2023
- replacement: resolve4(theoSrcDir, `adapters/web-shim${ext}`)
2024
- },
2025
- {
2026
- find: "theokit/adapters/ws-shim",
2027
- replacement: resolve4(theoSrcDir, `adapters/ws-shim${ext}`)
2028
- },
2029
- // T1.2 — devtools entry (DEV only; tree-shaken in build because
2030
- // the script tag is never injected by inject-devtools.ts in build mode)
2031
- {
2032
- find: "theokit/devtools/entry",
2033
- replacement: resolve4(theoSrcDir, `devtools/entry${ext === ".ts" ? ".tsx" : ".js"}`)
2034
- },
2035
- { find: "theokit", replacement: resolve4(theoSrcDir, `index${ext}`) }
2036
- ]
2037
- }
2038
- };
2226
+ return runConfigHook({
2227
+ projectRoot,
2228
+ theoSrcDir,
2229
+ services: options.services,
2230
+ optimizeDepsInclude: buildOptimizeDepsInclude(projectRoot, options.viteOptimizeDeps)
2231
+ });
2039
2232
  },
2040
- // Auto-inject the entry-client `<script>` into every served HTML
2041
- // (Phase 2 of nextjs-maturity plan). Runs BEFORE Vite's own
2042
- // transform so the injection lands inside <body>, not after
2043
- // Vite-injected content. Idempotent: if the user already wrote
2044
- // the script tag, the HTML is returned unchanged.
2233
+ // T2.6 (M6) transformIndexHtml hook extracted to transform-html-hook.ts.
2234
+ // Preserves the canonical 3-step order (entry-client devtools → styles)
2235
+ // so EC-10 (Vite hook ordering side effects) is honored.
2045
2236
  transformIndexHtml: {
2046
2237
  order: "pre",
2047
2238
  handler(html) {
2048
- const entry = injectEntryClient(html);
2049
- if (entry.warning) console.warn(entry.warning);
2050
- let next = entry.html;
2051
- const devtools = injectDevtoolsScript(next, {
2052
- isDev: isDevMode,
2053
- enabled: devtoolsEnabled
2239
+ return runTransformIndexHtml(html, {
2240
+ isDevMode: isDevModeRef,
2241
+ devtoolsEnabled,
2242
+ projectRoot
2054
2243
  });
2055
- if (devtools.warning) console.warn(devtools.warning);
2056
- next = devtools.html;
2057
- const styles = injectStylesheets(next, {
2058
- isDev: isDevMode,
2059
- hasPackage: (name) => existsSync5(resolve4(projectRoot, "node_modules", ...name.split("/")))
2060
- });
2061
- next = styles.html;
2062
- return next;
2063
2244
  }
2064
2245
  },
2246
+ // T2.6 (M6) — virtual-module resolveId + load dispatch extracted to
2247
+ // virtual-modules-hook.ts. Pure structural split; identity branches
2248
+ // preserved.
2065
2249
  resolveId(id) {
2066
- if (id === VIRTUAL_ENTRY_ID) return RESOLVED_ENTRY_ID;
2067
- if (id === VIRTUAL_MANIFEST_ID) return RESOLVED_MANIFEST_ID;
2068
- if (id === VIRTUAL_ENTRY_SERVER_ID) return RESOLVED_ENTRY_SERVER_ID;
2069
- if (id === VIRTUAL_RUNTIME_CONFIG_ID) return RESOLVED_RUNTIME_CONFIG_ID;
2070
- if (id === DEVTOOLS_VIRTUAL_ID) return DEVTOOLS_RESOLVED_ID;
2250
+ return resolveVirtualModuleId(id, VIRTUAL_MODULE_IDS);
2071
2251
  },
2072
2252
  load(id) {
2073
- if (id === RESOLVED_ENTRY_ID) {
2074
- return generateEntryClient(ssrEnabled, {
2075
- theoUi: theoUi?.enabled ? { fonts: theoUi.config.fonts, theme: theoUi.config.theme } : void 0
2076
- });
2077
- }
2078
- if (id === RESOLVED_MANIFEST_ID) {
2079
- const tree = scanRoutes(appDir);
2080
- broadcastRouteManifest(tree);
2081
- return generateRouteManifest(tree);
2082
- }
2083
- if (id === RESOLVED_ENTRY_SERVER_ID) {
2084
- return generateEntryServer({
2085
- streaming: options.ssrStreaming === true,
2086
- theoUi: theoUi?.enabled ? { theme: theoUi.config.theme } : void 0
2087
- });
2088
- }
2089
- if (id === RESOLVED_RUNTIME_CONFIG_ID) {
2090
- const tName = transformer?.name ?? "json";
2091
- return [
2092
- `// Generated by Theo Vite plugin`,
2093
- `;(globalThis).__THEO_TRANSFORMER__ = ${JSON.stringify(tName)}`,
2094
- `export const TRANSFORMER_NAME = ${JSON.stringify(tName)}`
2095
- ].join("\n");
2096
- }
2097
- if (id === DEVTOOLS_RESOLVED_ID) {
2098
- return [
2099
- `// Theo devtools \u2014 virtual entry (DEV only)`,
2100
- `import('theokit/devtools/entry').catch((e) => console.error('[theo devtools] mount failed', e))`
2101
- ].join("\n");
2102
- }
2253
+ return loadVirtualModule(id, {
2254
+ ids: VIRTUAL_MODULE_IDS,
2255
+ appDir,
2256
+ ssrEnabled,
2257
+ streamingEnabled: options.ssrStreaming === true,
2258
+ theoUi,
2259
+ transformer
2260
+ });
2103
2261
  },
2262
+ // T2.6 (M6) — configureServer body extracted to configure-server-hook.ts.
2263
+ // The hook owns middleware order, ws subscriptions, watcher handlers,
2264
+ // OpenAPI re-emit, and WS upgrade. EC-10 honored (every effect preserved
2265
+ // in its original ordering).
2104
2266
  async configureServer(server) {
2105
- isDevMode = true;
2106
- globalThis.__theoViteHotServer = server;
2107
- server.ws.on("theo:devtools:request-manifest", () => {
2108
- try {
2109
- const tree = scanRoutes(appDir);
2110
- broadcastRouteManifest(tree);
2111
- } catch {
2112
- }
2113
- });
2114
- const serverDir = resolve4(projectRoot, "server");
2115
- server.middlewares.use(
2116
- createActionMiddleware(server, serverDir, { pluginRunner, csrfMode, disallowed })
2117
- );
2118
- const servicesProxyPrefixes = options.services && Object.keys(options.services).length > 0 ? Object.values(options.services).map((s) => s.proxy) : [];
2119
- const csrfReadinessStore = options.csrfReadinessStore ?? new CsrfReadinessStore();
2120
- server.middlewares.use(
2121
- createApiMiddleware(server, serverDir, {
2122
- rateLimitConfig: options.rateLimit,
2123
- pluginRunner,
2124
- batching: resolvedBatching,
2125
- transformer,
2126
- csrfMode,
2127
- securityHeaders,
2128
- disallowed,
2129
- cors,
2130
- auditLogger,
2131
- servicesProxyPrefixes,
2132
- csrfReadinessStore
2133
- })
2134
- );
2135
- const configPath = resolve4(projectRoot, "theo.config.ts");
2136
- server.watcher.on("change", (file) => {
2137
- if (file === configPath) {
2138
- console.warn(
2139
- "\n[theokit] theo.config.ts changed; restart dev server for plugin changes to take effect\n"
2140
- );
2141
- }
2142
- });
2143
- if (ssrEnabled) {
2144
- setupSsrDevMiddleware(server, {
2145
- projectRoot,
2146
- virtualEntryServerId: VIRTUAL_ENTRY_SERVER_ID,
2147
- securityHeaders
2148
- });
2149
- }
2150
- function handleRouteChange(filePath) {
2151
- if (!isRouteFile(basename(filePath))) return;
2152
- if (!filePath.startsWith(appDir)) return;
2153
- const mod = server.moduleGraph.getModuleById(RESOLVED_MANIFEST_ID);
2154
- if (mod) {
2155
- server.moduleGraph.invalidateModule(mod);
2156
- server.ws.send({ type: "full-reload" });
2157
- }
2158
- }
2159
- server.watcher.on("add", handleRouteChange);
2160
- server.watcher.on("unlink", handleRouteChange);
2161
- if (resolvedOpenApi !== void 0) {
2162
- const openApiCfg = resolvedOpenApi;
2163
- const { reEmitOpenApi } = await import("./dev-emit-NO6OZJOQ.js");
2164
- const openApiServerDir = resolve4(projectRoot, "server");
2165
- const openApiDistDir = resolve4(projectRoot, resolvedDistDir);
2166
- const isRouteFileForOpenApi = (file) => file.startsWith(openApiServerDir) && /\.(ts|tsx|js|mjs)$/.test(file);
2167
- void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
2168
- server.watcher.on("change", (file) => {
2169
- if (isRouteFileForOpenApi(file)) {
2170
- void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
2171
- }
2172
- });
2173
- server.watcher.on("add", (file) => {
2174
- if (isRouteFileForOpenApi(file)) {
2175
- void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
2176
- }
2177
- });
2178
- server.watcher.on("unlink", (file) => {
2179
- if (isRouteFileForOpenApi(file)) {
2180
- void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
2181
- }
2182
- });
2183
- }
2184
- setupWsUpgrade(server, projectRoot);
2185
- server.httpServer?.once("close", () => {
2186
- ;
2187
- globalThis.__theoViteHotServer = void 0;
2267
+ await runConfigureServer(server, {
2268
+ projectRoot,
2269
+ appDir,
2270
+ resolvedDistDir,
2271
+ ssrEnabled,
2272
+ isDevMode: isDevModeRef,
2273
+ pluginRunner,
2274
+ transformer,
2275
+ resolvedBatching,
2276
+ csrfMode,
2277
+ securityHeaders,
2278
+ disallowed,
2279
+ cors,
2280
+ auditLogger,
2281
+ resolvedOpenApi,
2282
+ services: options.services,
2283
+ rateLimit: options.rateLimit,
2284
+ csrfReadinessStoreOverride: options.csrfReadinessStore,
2285
+ virtualEntryServerId: VIRTUAL_ENTRY_SERVER_ID,
2286
+ resolvedManifestId: RESOLVED_MANIFEST_ID
2188
2287
  });
2189
2288
  }
2190
2289
  };
@@ -2198,4 +2297,4 @@ export {
2198
2297
  theoPluginAsync,
2199
2298
  theoPlugin
2200
2299
  };
2201
- //# sourceMappingURL=chunk-JAAHOGKI.js.map
2300
+ //# sourceMappingURL=chunk-M2EY7KDR.js.map