theokit 0.4.0-beta.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{actions-virtual-module-HWNTIEPI.js → actions-virtual-module-IY46EEEX.js} +2 -2
- package/dist/{actions-virtual-module-SEIPACG5.js → actions-virtual-module-XNHDNCZ6.js} +2 -2
- package/dist/add-2ZFFGGE4.js +0 -0
- package/dist/{app-typed-client-ITIYTBXO.js → app-typed-client-OUJO3V5J.js} +10 -5
- package/dist/{app-typed-client-62FYBLVJ.js.map → app-typed-client-OUJO3V5J.js.map} +1 -1
- package/dist/{app-typed-client-62FYBLVJ.js → app-typed-client-YOMWSA3F.js} +11 -6
- package/dist/{app-typed-client-ITIYTBXO.js.map → app-typed-client-YOMWSA3F.js.map} +1 -1
- package/dist/audit-log-BQWM5YLG.d.ts +60 -0
- package/dist/{aws-lambda-XYCGZH3I.js → aws-lambda-GKSTX6HD.js} +3 -3
- package/dist/body-parser-web-MUWBKZ3F.js +70 -0
- package/dist/body-parser-web-MUWBKZ3F.js.map +1 -0
- package/dist/broadcast-QOQGUNNK.js +0 -0
- package/dist/{build-HIGHJQQV.js → build-D4J7XECU.js} +31 -22
- package/dist/build-D4J7XECU.js.map +1 -0
- package/dist/build-request-body-preview-II2235E6.js +0 -0
- package/dist/{bun-K73TQEWC.js → bun-ISHSNFIO.js} +3 -3
- package/dist/{check-PZR67OY5.js → check-NXYPLM6M.js} +2 -2
- package/dist/{chunk-WZ7CPVQB.js → chunk-27LWMYNK.js} +28 -24
- package/dist/chunk-27LWMYNK.js.map +1 -0
- package/dist/{chunk-BIGBFZSU.js → chunk-2F4FROEQ.js} +1689 -1521
- package/dist/chunk-2F4FROEQ.js.map +1 -0
- package/dist/chunk-4HBI7DHP.js +20 -0
- package/dist/chunk-4HBI7DHP.js.map +1 -0
- package/dist/{chunk-X4JAX2U3.js → chunk-4S2UCILN.js} +180 -125
- package/dist/chunk-4S2UCILN.js.map +1 -0
- package/dist/{chunk-C52GSJPF.js → chunk-4S6YHNG2.js} +11 -8
- package/dist/chunk-4S6YHNG2.js.map +1 -0
- package/dist/chunk-5ODOE6EF.js +0 -0
- package/dist/{chunk-KJVEJILQ.js → chunk-5PU65T5W.js} +9 -3
- package/dist/chunk-5PU65T5W.js.map +1 -0
- package/dist/chunk-5QW7IQQU.js +36 -0
- package/dist/chunk-5QW7IQQU.js.map +1 -0
- package/dist/chunk-5Z2727GV.js +1324 -0
- package/dist/chunk-5Z2727GV.js.map +1 -0
- package/dist/{chunk-YLBSSEHX.js → chunk-6NEXVBPY.js} +5 -15
- package/dist/chunk-6NEXVBPY.js.map +1 -0
- package/dist/chunk-7MQOHNHE.js +36 -0
- package/dist/chunk-7MQOHNHE.js.map +1 -0
- package/dist/{chunk-TPCT3MXV.js → chunk-ABVITXFH.js} +405 -272
- package/dist/chunk-ABVITXFH.js.map +1 -0
- package/dist/chunk-ASDXVRJD.js +185 -0
- package/dist/chunk-ASDXVRJD.js.map +1 -0
- package/dist/chunk-B3L3TJVF.js +406 -0
- package/dist/chunk-B3L3TJVF.js.map +1 -0
- package/dist/{chunk-PB4GR7EP.js → chunk-C3ZZ56YZ.js} +1 -18
- package/dist/chunk-C3ZZ56YZ.js.map +1 -0
- package/dist/{chunk-O7335ZGH.js → chunk-CG563V5M.js} +5 -3
- package/dist/chunk-CG563V5M.js.map +1 -0
- package/dist/{chunk-5OFPQK6N.js → chunk-COXLEZCN.js} +2 -1
- package/dist/chunk-COXLEZCN.js.map +1 -0
- package/dist/{chunk-O4BLVPML.js → chunk-DNMSV3R3.js} +22 -28
- package/dist/chunk-DNMSV3R3.js.map +1 -0
- package/dist/chunk-E3JH6YUM.js +1 -0
- package/dist/chunk-ESC54TAK.js +220 -0
- package/dist/chunk-ESC54TAK.js.map +1 -0
- package/dist/chunk-FI7MPTJW.js +77 -0
- package/dist/chunk-FI7MPTJW.js.map +1 -0
- package/dist/chunk-H4J2LECY.js +201 -0
- package/dist/chunk-H4J2LECY.js.map +1 -0
- package/dist/chunk-IAJ2JVEH.js +256 -0
- package/dist/chunk-IAJ2JVEH.js.map +1 -0
- package/dist/{chunk-F3TG5FJV.js → chunk-IEZCAT2I.js} +7 -1
- package/dist/{chunk-F3TG5FJV.js.map → chunk-IEZCAT2I.js.map} +1 -1
- package/dist/chunk-JHEAWR3L.js +1 -0
- package/dist/chunk-JQSKBMXP.js +17 -0
- package/dist/chunk-JQSKBMXP.js.map +1 -0
- package/dist/{chunk-4QTKSLTO.js → chunk-K4M64WD6.js} +9 -5
- package/dist/{chunk-4QTKSLTO.js.map → chunk-K4M64WD6.js.map} +1 -1
- package/dist/chunk-KI7OWQUX.js +293 -0
- package/dist/chunk-KI7OWQUX.js.map +1 -0
- package/dist/chunk-KT3MWNZG.js +0 -0
- package/dist/{chunk-ZJW5FFYJ.js → chunk-LC5PYNJP.js} +2 -2
- package/dist/{chunk-JAAHOGKI.js → chunk-M2EY7KDR.js} +1223 -1124
- package/dist/chunk-M2EY7KDR.js.map +1 -0
- package/dist/{chunk-2VQKDRVF.js → chunk-MR7OLIC6.js} +3 -3
- package/dist/chunk-NM4WF3XD.js +63 -0
- package/dist/chunk-NM4WF3XD.js.map +1 -0
- package/dist/chunk-NPMCKOX4.js +1 -0
- package/dist/{chunk-VRHXOKRP.js → chunk-NZXH2LQQ.js} +86 -83
- package/dist/chunk-NZXH2LQQ.js.map +1 -0
- package/dist/{chunk-B6RP57M3.js → chunk-OLPB36O2.js} +4 -4
- package/dist/chunk-PIVX3DYW.js +142 -0
- package/dist/chunk-PIVX3DYW.js.map +1 -0
- package/dist/{chunk-7TOMTMHT.js → chunk-R7OC6UDK.js} +2 -1
- package/dist/chunk-R7OC6UDK.js.map +1 -0
- package/dist/chunk-RESN62GB.js +269 -0
- package/dist/chunk-RESN62GB.js.map +1 -0
- package/dist/{chunk-64JI5LTO.js → chunk-RMU7EBRO.js} +2 -2
- package/dist/chunk-RMU7EBRO.js.map +1 -0
- package/dist/chunk-TQYSPYKU.js +131 -0
- package/dist/chunk-TQYSPYKU.js.map +1 -0
- package/dist/chunk-TSLSIRBR.js +107 -0
- package/dist/chunk-TSLSIRBR.js.map +1 -0
- package/dist/{chunk-XP7YXRHO.js → chunk-U6TPSW4L.js} +37 -5
- package/dist/chunk-U6TPSW4L.js.map +1 -0
- package/dist/chunk-UD3LFGDL.js +45 -0
- package/dist/chunk-UD3LFGDL.js.map +1 -0
- package/dist/chunk-UUFYP2UM.js +161 -0
- package/dist/chunk-UUFYP2UM.js.map +1 -0
- package/dist/{chunk-OXIQI2XZ.js → chunk-VBZCVFSA.js} +2 -2
- package/dist/chunk-VMEWD57H.js +137 -0
- package/dist/chunk-VMEWD57H.js.map +1 -0
- package/dist/{chunk-TRH2L3FI.js → chunk-WEGALZEU.js} +3 -3
- package/dist/{chunk-ZOXNJV2O.js → chunk-WGHJD6I7.js} +35 -138
- package/dist/chunk-WGHJD6I7.js.map +1 -0
- package/dist/chunk-XMS5VRIK.js +0 -0
- package/dist/chunk-Y4H3JNVK.js +18 -0
- package/dist/chunk-Y4H3JNVK.js.map +1 -0
- package/dist/chunk-Z5IGLBWE.js +329 -0
- package/dist/chunk-Z5IGLBWE.js.map +1 -0
- package/dist/chunk-ZEGYW52B.js +26 -0
- package/dist/chunk-ZEGYW52B.js.map +1 -0
- package/dist/chunk-ZJQ4O76G.js +87 -0
- package/dist/chunk-ZJQ4O76G.js.map +1 -0
- package/dist/cli/index.js +32 -21
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.d.ts +107 -1
- package/dist/client/index.js +152 -6
- package/dist/client/index.js.map +1 -1
- package/dist/{cloudflare-LCAOTRYZ.js → cloudflare-OJGJ7R2D.js} +3 -3
- package/dist/configure-agent-registry-6YGTJYBC.js +0 -0
- package/dist/cookies-MJKMGJ7N.js +22 -0
- package/dist/csrf-BBrEZSBW.d.ts +107 -0
- package/dist/csrf-readiness-store-CjIoub3U.d.ts +43 -0
- package/dist/define-websocket-CdK94O-D.d.ts +64 -0
- package/dist/{deno-deploy-ZN4RE5HF.js → deno-deploy-BHWKFTOW.js} +3 -3
- package/dist/{dev-266MVCVA.js → dev-MJVSRZGF.js} +11 -11
- package/dist/{dev-emit-RBSFZZNO.js → dev-emit-5VPRCHOD.js} +39 -142
- package/dist/dev-emit-5VPRCHOD.js.map +1 -0
- package/dist/{dev-emit-NO6OZJOQ.js → dev-emit-HHP2XJXE.js} +5 -5
- package/dist/devtools/entry.js +185 -131
- package/dist/devtools/entry.js.map +1 -1
- package/dist/{dispatcher-AQJGI3HU.js → dispatcher-63LBXYLE.js} +2 -2
- package/dist/{dispatcher-E6QV32BO.js → dispatcher-EJME6C6M.js} +5 -2
- package/dist/dispatcher-EJME6C6M.js.map +1 -0
- package/dist/{dist-5V77Z223.js → dist-KRW6OVD4.js} +7 -7
- package/dist/dist-KRW6OVD4.js.map +1 -0
- package/dist/docker-EZDA5FT7.js +0 -0
- package/dist/error-envelope-BsNzzAV5.d.ts +62 -0
- package/dist/{generate-DT4IIAHF.js → generate-AZ2K6Z2Z.js} +75 -2
- package/dist/generate-AZ2K6Z2Z.js.map +1 -0
- package/dist/index-DWWEdFh5.d.ts +399 -0
- package/dist/index.d.ts +161 -1391
- package/dist/index.js +20 -8
- package/dist/index.js.map +1 -1
- package/dist/{info-FJ4NXKTB.js → info-47VB62MV.js} +5 -5
- package/dist/job-backend-CgC8Xf33.d.ts +68 -0
- package/dist/{lib-NL5JXGEB.js → lib-NST3PNZX.js} +31 -31
- package/dist/lib-NST3PNZX.js.map +1 -0
- package/dist/module-loader-Cfz7dgCP.d.ts +26 -0
- package/dist/{netlify-5VQ22Z2P.js → netlify-GSNSJGMN.js} +3 -3
- package/dist/{node-3APTLGWB.js → node-6I5B6RL3.js} +3 -3
- package/dist/node-6I5B6RL3.js.map +1 -0
- package/dist/{openapi-FNVSWZOL.js → openapi-NFLSNNVQ.js} +10 -10
- package/dist/plugin-runner-BGBkzgi0.d.ts +95 -0
- package/dist/plugin-types-DNJGxr4Z.d.ts +79 -0
- package/dist/{proper-lockfile-4Y3DP3RP.js → proper-lockfile-MVKMQGFK.js} +27 -27
- package/dist/proper-lockfile-MVKMQGFK.js.map +1 -0
- package/dist/rate-limit-BdNDZ3vt.d.ts +58 -0
- package/dist/registry-QHEZ3BWB.js +25 -0
- package/dist/router-RGZFX75G.js +0 -0
- package/dist/{routes-H4SPLFHJ.js → routes-3A4XGIEJ.js} +6 -6
- package/dist/{scan-C2BOKLSY.js → scan-NQFI5S7P.js} +2 -2
- package/dist/scan-NQFI5S7P.js.map +1 -0
- package/dist/schema-D3v8VB7o.d.ts +76 -0
- package/dist/{schema-VR6YNVLQ.js → schema-KZVOV333.js} +5 -3
- package/dist/schema-KZVOV333.js.map +1 -0
- package/dist/server/agent/index.d.ts +229 -0
- package/dist/server/agent/index.js +16 -0
- package/dist/server/agent/index.js.map +1 -0
- package/dist/server/auth/index.d.ts +46 -1
- package/dist/server/auth/index.js +10 -3
- package/dist/server/cost/index.d.ts +1 -3
- package/dist/server/cost/index.js +1 -1
- package/dist/server/cron/index.js +4 -2
- package/dist/server/define/index.d.ts +281 -0
- package/dist/server/define/index.js +26 -0
- package/dist/server/define/index.js.map +1 -0
- package/dist/server/http/index.d.ts +10 -0
- package/dist/server/http/index.js +74 -0
- package/dist/server/http/index.js.map +1 -0
- package/dist/server/index.d.ts +151 -1677
- package/dist/server/index.js +558 -1102
- package/dist/server/index.js.map +1 -1
- package/dist/server/jobs/index.d.ts +348 -2
- package/dist/server/jobs/index.js +5 -3
- package/dist/server/observability/index.d.ts +324 -0
- package/dist/server/observability/index.js +48 -0
- package/dist/server/observability/index.js.map +1 -0
- package/dist/server/plugins/index.d.ts +17 -0
- package/dist/server/plugins/index.js +17 -0
- package/dist/server/plugins/index.js.map +1 -0
- package/dist/server/rate-limit/index.d.ts +105 -0
- package/dist/server/rate-limit/index.js +25 -0
- package/dist/server/rate-limit/index.js.map +1 -0
- package/dist/server/realtime/index.d.ts +15 -0
- package/dist/server/realtime/index.js +8 -0
- package/dist/server/realtime/index.js.map +1 -0
- package/dist/server/scan/index.d.ts +106 -0
- package/dist/server/scan/index.js +43 -0
- package/dist/server/scan/index.js.map +1 -0
- package/dist/server/security/index.d.ts +193 -0
- package/dist/server/security/index.js +50 -0
- package/dist/server/security/index.js.map +1 -0
- package/dist/server/storage/index.d.ts +22 -0
- package/dist/server/storage/index.js +14 -0
- package/dist/server/storage/index.js.map +1 -0
- package/dist/server/webhook/index.d.ts +148 -0
- package/dist/server/webhook/index.js +19 -0
- package/dist/server/webhook/index.js.map +1 -0
- package/dist/server-error-to-envelope-NO4CCAFQ.js +8 -0
- package/dist/server-error-to-envelope-NO4CCAFQ.js.map +1 -0
- package/dist/server-error-to-envelope-UJK6OWDJ.js +134 -0
- package/dist/server-error-to-envelope-UJK6OWDJ.js.map +1 -0
- package/dist/server-routes-hmr-GZJGPWMS.js +0 -0
- package/dist/services-json-Z2QNGVPW.js +142 -0
- package/dist/services-json-Z2QNGVPW.js.map +1 -0
- package/dist/{services-typed-client-XWYYBWGW.js → services-typed-client-KK6RHRDG.js} +2 -2
- package/dist/{services-typed-client-ZX5JUHH3.js → services-typed-client-T7M5VPBP.js} +2 -2
- package/dist/{sqlite-vec-ARPNUBWT.js → sqlite-vec-54KB4RD3.js} +2 -2
- package/dist/sqlite-vec-54KB4RD3.js.map +1 -0
- package/dist/{start-HX3QUSOS.js → start-CGSZPBAG.js} +23 -23
- package/dist/start-CGSZPBAG.js.map +1 -0
- package/dist/{static-TZBVBDTB.js → static-QI5NQT2X.js} +6 -6
- package/dist/storage-manager-C4jsO0Tp.d.ts +89 -0
- package/dist/storage-manager-D5KBXXKB.js +0 -0
- package/dist/{storage-types-DtIVejC1.d.ts → storage-types-DsDTCPbp.d.ts} +1 -1
- package/dist/{theo-cloud-3K4DGB3S.js → theo-cloud-RSQCBNXL.js} +4 -4
- package/dist/theo-cloud-RSQCBNXL.js.map +1 -0
- package/dist/upgrade-readiness-GUJBCJIS.js +0 -0
- package/dist/{vercel-MWW24ABC.js → vercel-TKPZK62A.js} +3 -3
- package/dist/vite-plugin/index.d.ts +3 -1
- package/dist/vite-plugin/index.js +20 -8
- package/dist/{vite-plugin-IK3NOWXS.js → vite-plugin-CR4JDFUQ.js} +9 -9
- package/dist/vite-plugin-CR4JDFUQ.js.map +1 -0
- package/package.json +59 -10
- package/LICENSE +0 -201
- package/dist/build-HIGHJQQV.js.map +0 -1
- package/dist/chunk-5OFPQK6N.js.map +0 -1
- package/dist/chunk-64JI5LTO.js.map +0 -1
- package/dist/chunk-7TOMTMHT.js.map +0 -1
- package/dist/chunk-BIGBFZSU.js.map +0 -1
- package/dist/chunk-C52GSJPF.js.map +0 -1
- package/dist/chunk-CZM6WWWS.js +0 -2450
- package/dist/chunk-CZM6WWWS.js.map +0 -1
- package/dist/chunk-JAAHOGKI.js.map +0 -1
- package/dist/chunk-KJVEJILQ.js.map +0 -1
- package/dist/chunk-O4BLVPML.js.map +0 -1
- package/dist/chunk-O7335ZGH.js.map +0 -1
- package/dist/chunk-PB4GR7EP.js.map +0 -1
- package/dist/chunk-TPCT3MXV.js.map +0 -1
- package/dist/chunk-VRHXOKRP.js.map +0 -1
- package/dist/chunk-WZ7CPVQB.js.map +0 -1
- package/dist/chunk-X4JAX2U3.js.map +0 -1
- package/dist/chunk-XP7YXRHO.js.map +0 -1
- package/dist/chunk-YLBSSEHX.js.map +0 -1
- package/dist/chunk-ZOXNJV2O.js.map +0 -1
- package/dist/dev-emit-RBSFZZNO.js.map +0 -1
- package/dist/dispatcher-E6QV32BO.js.map +0 -1
- package/dist/dist-5V77Z223.js.map +0 -1
- package/dist/generate-DT4IIAHF.js.map +0 -1
- package/dist/index-li83Swxa.d.ts +0 -506
- package/dist/lib-NL5JXGEB.js.map +0 -1
- package/dist/proper-lockfile-4Y3DP3RP.js.map +0 -1
- package/dist/registry-4MZ26TZD.js +0 -25
- package/dist/schema-CjVly9c_.d.ts +0 -274
- package/dist/sqlite-vec-ARPNUBWT.js.map +0 -1
- package/dist/start-HX3QUSOS.js.map +0 -1
- package/dist/theo-cloud-3K4DGB3S.js.map +0 -1
- /package/dist/{actions-virtual-module-HWNTIEPI.js.map → actions-virtual-module-IY46EEEX.js.map} +0 -0
- /package/dist/{actions-virtual-module-SEIPACG5.js.map → actions-virtual-module-XNHDNCZ6.js.map} +0 -0
- /package/dist/{aws-lambda-XYCGZH3I.js.map → aws-lambda-GKSTX6HD.js.map} +0 -0
- /package/dist/{bun-K73TQEWC.js.map → bun-ISHSNFIO.js.map} +0 -0
- /package/dist/{check-PZR67OY5.js.map → check-NXYPLM6M.js.map} +0 -0
- /package/dist/{dispatcher-AQJGI3HU.js.map → chunk-E3JH6YUM.js.map} +0 -0
- /package/dist/{node-3APTLGWB.js.map → chunk-JHEAWR3L.js.map} +0 -0
- /package/dist/{chunk-ZJW5FFYJ.js.map → chunk-LC5PYNJP.js.map} +0 -0
- /package/dist/{chunk-2VQKDRVF.js.map → chunk-MR7OLIC6.js.map} +0 -0
- /package/dist/{scan-C2BOKLSY.js.map → chunk-NPMCKOX4.js.map} +0 -0
- /package/dist/{chunk-B6RP57M3.js.map → chunk-OLPB36O2.js.map} +0 -0
- /package/dist/{chunk-OXIQI2XZ.js.map → chunk-VBZCVFSA.js.map} +0 -0
- /package/dist/{chunk-TRH2L3FI.js.map → chunk-WEGALZEU.js.map} +0 -0
- /package/dist/{cloudflare-LCAOTRYZ.js.map → cloudflare-OJGJ7R2D.js.map} +0 -0
- /package/dist/{schema-VR6YNVLQ.js.map → cookies-MJKMGJ7N.js.map} +0 -0
- /package/dist/{deno-deploy-ZN4RE5HF.js.map → deno-deploy-BHWKFTOW.js.map} +0 -0
- /package/dist/{dev-266MVCVA.js.map → dev-MJVSRZGF.js.map} +0 -0
- /package/dist/{dev-emit-NO6OZJOQ.js.map → dev-emit-HHP2XJXE.js.map} +0 -0
- /package/dist/{vite-plugin-IK3NOWXS.js.map → dispatcher-63LBXYLE.js.map} +0 -0
- /package/dist/{info-FJ4NXKTB.js.map → info-47VB62MV.js.map} +0 -0
- /package/dist/{netlify-5VQ22Z2P.js.map → netlify-GSNSJGMN.js.map} +0 -0
- /package/dist/{openapi-FNVSWZOL.js.map → openapi-NFLSNNVQ.js.map} +0 -0
- /package/dist/{registry-4MZ26TZD.js.map → registry-QHEZ3BWB.js.map} +0 -0
- /package/dist/{routes-H4SPLFHJ.js.map → routes-3A4XGIEJ.js.map} +0 -0
- /package/dist/{services-typed-client-XWYYBWGW.js.map → services-typed-client-KK6RHRDG.js.map} +0 -0
- /package/dist/{services-typed-client-ZX5JUHH3.js.map → services-typed-client-T7M5VPBP.js.map} +0 -0
- /package/dist/{static-TZBVBDTB.js.map → static-QI5NQT2X.js.map} +0 -0
- /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-
|
|
16
|
+
} from "./chunk-ABVITXFH.js";
|
|
17
17
|
import {
|
|
18
18
|
loadConfig
|
|
19
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
34
|
+
} from "./chunk-VBZCVFSA.js";
|
|
35
35
|
import {
|
|
36
36
|
scanServerActions
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-COXLEZCN.js";
|
|
38
38
|
|
|
39
39
|
// src/vite-plugin/index.ts
|
|
40
|
-
import { existsSync as
|
|
41
|
-
import { resolve as
|
|
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/
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
773
|
-
|
|
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
|
-
|
|
779
|
-
|
|
780
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
822
|
+
await new Promise((resolve8, reject) => {
|
|
1005
823
|
req.on("data", (c) => chunks.push(c));
|
|
1006
824
|
req.on("end", () => {
|
|
1007
|
-
|
|
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/
|
|
1154
|
-
import {
|
|
1155
|
-
import {
|
|
1156
|
-
function
|
|
1157
|
-
return
|
|
1158
|
-
|
|
1159
|
-
|
|
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
|
|
1163
|
-
|
|
1164
|
-
|
|
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
|
|
1167
|
-
const
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
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
|
-
|
|
1174
|
-
if (
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
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 =
|
|
1316
|
+
const parent = dirname3(dir);
|
|
1201
1317
|
if (parent === dir) break;
|
|
1202
1318
|
dir = parent;
|
|
1203
1319
|
}
|
|
1204
|
-
return
|
|
1205
|
-
}
|
|
1206
|
-
function
|
|
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
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
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
|
-
|
|
1214
|
-
return false;
|
|
1372
|
+
return [];
|
|
1215
1373
|
}
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
const
|
|
1219
|
-
if (
|
|
1220
|
-
|
|
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
|
-
|
|
1223
|
-
|
|
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
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
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
|
-
|
|
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/
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
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/
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
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/
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
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
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
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
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
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
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
const
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
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
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
const
|
|
1449
|
-
if (
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
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
|
-
|
|
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/
|
|
1487
|
-
function
|
|
1488
|
-
|
|
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
|
|
1517
|
-
|
|
1518
|
-
|
|
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
|
|
1523
|
-
|
|
1524
|
-
|
|
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
|
-
|
|
1535
|
-
if (
|
|
1536
|
-
|
|
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
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
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
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
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
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
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
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
return [];
|
|
1884
|
+
lines.push("");
|
|
1885
|
+
for (const imp of staticImports) {
|
|
1886
|
+
lines.push(`import ${imp.varName} from '${imp.importPath}'`);
|
|
1617
1887
|
}
|
|
1618
|
-
const
|
|
1619
|
-
|
|
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
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
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
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
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/
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
if (
|
|
1710
|
-
|
|
1711
|
-
if (
|
|
1712
|
-
void
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
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
|
-
}
|
|
1745
|
-
|
|
1746
|
-
|
|
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 =
|
|
1862
|
-
if (
|
|
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-
|
|
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-
|
|
2128
|
+
const { appTypedClientPlugin } = await import("./app-typed-client-OUJO3V5J.js");
|
|
1890
2129
|
const appClientPlugin = appTypedClientPlugin({
|
|
1891
2130
|
cwd: projectRoot,
|
|
1892
|
-
serverDir:
|
|
1893
|
-
distDir:
|
|
2131
|
+
serverDir: resolve7(projectRoot, "server"),
|
|
2132
|
+
distDir: resolve7(projectRoot, ".theo")
|
|
1894
2133
|
});
|
|
1895
|
-
const { actionsVirtualModule } = await import("./actions-virtual-module-
|
|
2134
|
+
const { actionsVirtualModule } = await import("./actions-virtual-module-IY46EEEX.js");
|
|
1896
2135
|
const actionsPlugin = actionsVirtualModule({
|
|
1897
|
-
serverDir:
|
|
1898
|
-
distDir:
|
|
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:
|
|
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 (
|
|
2154
|
+
if (existsSync7(resolve7(projectRoot, "node_modules", "@theokit", "ui"))) {
|
|
1916
2155
|
include.push("@theokit/ui");
|
|
1917
2156
|
}
|
|
1918
|
-
if (
|
|
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 =
|
|
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
|
-
|
|
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
|
-
//
|
|
1972
|
-
//
|
|
1973
|
-
// @tailwindcss/vite auto-chain lives in
|
|
1974
|
-
// Vite drops plugins returned from a
|
|
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
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
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
|
-
//
|
|
2041
|
-
//
|
|
2042
|
-
//
|
|
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
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
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
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
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-
|
|
2300
|
+
//# sourceMappingURL=chunk-M2EY7KDR.js.map
|