theokit 0.48.13 → 0.50.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/README.md +1 -0
- package/dist/{actions-virtual-module-YQXRSSMW.js → actions-virtual-module-5WBVSDOM.js} +22 -12
- package/dist/actions-virtual-module-5WBVSDOM.js.map +1 -0
- package/dist/{actions-virtual-module-LB4NGONI.js → actions-virtual-module-ZYLMNW6K.js} +13 -8
- package/dist/{actions-virtual-module-LB4NGONI.js.map → actions-virtual-module-ZYLMNW6K.js.map} +1 -1
- package/dist/adapters/agent-mount.d.ts +127 -0
- package/dist/adapters/agent-mount.js +24 -0
- package/dist/adapters/security-headers.d.ts +132 -0
- package/dist/adapters/security-headers.js +64 -0
- package/dist/adapters/security-headers.js.map +1 -0
- package/dist/adapters/web-shim.d.ts +100 -13
- package/dist/adapters/web-shim.js +158 -54
- package/dist/adapters/web-shim.js.map +1 -1
- package/dist/agent-GKUEL6OH.js +15 -0
- package/dist/agent-scan-BZa-pagG.d.ts +16 -0
- package/dist/{app-typed-client-GHO3EWZK.js → app-typed-client-4LAIEGFX.js} +23 -13
- package/dist/app-typed-client-4LAIEGFX.js.map +1 -0
- package/dist/{app-typed-client-G3J5G7E5.js → app-typed-client-DQM3Y6YY.js} +14 -9
- package/dist/app-typed-client-DQM3Y6YY.js.map +1 -0
- package/dist/aws-lambda-VKMYUMP2.js +261 -0
- package/dist/aws-lambda-VKMYUMP2.js.map +1 -0
- package/dist/{build-WU6AKKIB.js → build-FLX6VF7Q.js} +71 -26
- package/dist/build-FLX6VF7Q.js.map +1 -0
- package/dist/{bun-33RD6K7O.js → bun-L7PP6ID5.js} +105 -13
- package/dist/bun-L7PP6ID5.js.map +1 -0
- package/dist/{check-45NKUVSQ.js → check-GIPZBFTX.js} +2 -2
- package/dist/{chunk-KD5G3YXI.js → chunk-2AMLGJJA.js} +35 -9
- package/dist/chunk-2AMLGJJA.js.map +1 -0
- package/dist/chunk-3PBAN2YE.js +146 -0
- package/dist/chunk-3PBAN2YE.js.map +1 -0
- package/dist/{chunk-HBU2GIMZ.js → chunk-3QPVEJ7D.js} +15 -9
- package/dist/chunk-3QPVEJ7D.js.map +1 -0
- package/dist/{chunk-47CINLWF.js → chunk-44J2ANQC.js} +2 -2
- package/dist/{chunk-IEES3CHD.js → chunk-47MM2JUK.js} +8 -2
- package/dist/chunk-47MM2JUK.js.map +1 -0
- package/dist/chunk-4X6H2BXP.js +108 -0
- package/dist/chunk-4X6H2BXP.js.map +1 -0
- package/dist/{chunk-A7JBF5FO.js → chunk-5K4TDLRG.js} +8 -5
- package/dist/chunk-5K4TDLRG.js.map +1 -0
- package/dist/chunk-5YPXXSYY.js +181 -0
- package/dist/chunk-5YPXXSYY.js.map +1 -0
- package/dist/{chunk-YGAXV5HR.js → chunk-6J4JWHDJ.js} +191 -112
- package/dist/chunk-6J4JWHDJ.js.map +1 -0
- package/dist/chunk-72DMJBXI.js +424 -0
- package/dist/chunk-72DMJBXI.js.map +1 -0
- package/dist/chunk-7PM6K5EV.js +513 -0
- package/dist/chunk-7PM6K5EV.js.map +1 -0
- package/dist/{chunk-MTEWLGVY.js → chunk-BPYMC7SU.js} +4 -48
- package/dist/chunk-BPYMC7SU.js.map +1 -0
- package/dist/chunk-BU6RJPPJ.js +421 -0
- package/dist/chunk-BU6RJPPJ.js.map +1 -0
- package/dist/{chunk-FB2HBNIC.js → chunk-BX6EUYHU.js} +6 -2
- package/dist/chunk-BX6EUYHU.js.map +1 -0
- package/dist/{chunk-BIVCP6X5.js → chunk-C62IZEJI.js} +465 -202
- package/dist/chunk-C62IZEJI.js.map +1 -0
- package/dist/chunk-D4VP2QPR.js +16 -0
- package/dist/chunk-D4VP2QPR.js.map +1 -0
- package/dist/{chunk-J5MXCGKA.js → chunk-DHAD2O3S.js} +6 -6
- package/dist/chunk-DHAD2O3S.js.map +1 -0
- package/dist/chunk-E43VJ3E5.js +188 -0
- package/dist/chunk-E43VJ3E5.js.map +1 -0
- package/dist/chunk-EVZKNA2W.js +110 -0
- package/dist/chunk-EVZKNA2W.js.map +1 -0
- package/dist/chunk-FD4ZDTQ6.js +181 -0
- package/dist/chunk-FD4ZDTQ6.js.map +1 -0
- package/dist/chunk-FDOOBTXJ.js +17 -0
- package/dist/chunk-FDOOBTXJ.js.map +1 -0
- package/dist/chunk-FH3SQR6P.js +275 -0
- package/dist/chunk-FH3SQR6P.js.map +1 -0
- package/dist/chunk-HJHGTFEJ.js +23 -0
- package/dist/chunk-HJHGTFEJ.js.map +1 -0
- package/dist/{chunk-LXAESF4Y.js → chunk-HK4AZBKX.js} +7 -4
- package/dist/chunk-HK4AZBKX.js.map +1 -0
- package/dist/{chunk-T5C46IIU.js → chunk-HWBRS4DZ.js} +28 -269
- package/dist/chunk-HWBRS4DZ.js.map +1 -0
- package/dist/chunk-IRQGAC4L.js +52 -0
- package/dist/chunk-IRQGAC4L.js.map +1 -0
- package/dist/{chunk-FNTLKHZ7.js → chunk-JDN5SAAD.js} +7 -4
- package/dist/chunk-JDN5SAAD.js.map +1 -0
- package/dist/{chunk-GXEX4CBH.js → chunk-JIKNR5LX.js} +673 -484
- package/dist/chunk-JIKNR5LX.js.map +1 -0
- package/dist/{chunk-GRI4XHBJ.js → chunk-JWRCZIA4.js} +266 -108
- package/dist/chunk-JWRCZIA4.js.map +1 -0
- package/dist/{chunk-E576KYYT.js → chunk-KI4DOTN4.js} +25 -2
- package/dist/chunk-KI4DOTN4.js.map +1 -0
- package/dist/{chunk-ZB7D62OA.js → chunk-KUZVRJ7M.js} +3 -3
- package/dist/chunk-L4VFNFDL.js +248 -0
- package/dist/chunk-L4VFNFDL.js.map +1 -0
- package/dist/chunk-M2WC5MF7.js +127 -0
- package/dist/chunk-M2WC5MF7.js.map +1 -0
- package/dist/{chunk-O3JIAU3V.js → chunk-MCTC6HSE.js} +157 -103
- package/dist/chunk-MCTC6HSE.js.map +1 -0
- package/dist/{chunk-PXFXSFJL.js → chunk-NZ5JU6D3.js} +9 -9
- package/dist/{chunk-YRBR424R.js → chunk-OAUI273L.js} +338 -165
- package/dist/chunk-OAUI273L.js.map +1 -0
- package/dist/chunk-OGLS76RN.js +43 -0
- package/dist/chunk-OGLS76RN.js.map +1 -0
- package/dist/{chunk-7LVM22VG.js → chunk-OQCRCXCP.js} +6 -6
- package/dist/chunk-OQCRCXCP.js.map +1 -0
- package/dist/{chunk-OLGPYOLO.js → chunk-P6OS7652.js} +5 -2
- package/dist/chunk-P6OS7652.js.map +1 -0
- package/dist/chunk-Q5L5EGWZ.js +26 -0
- package/dist/chunk-Q5L5EGWZ.js.map +1 -0
- package/dist/{chunk-S433N3CT.js → chunk-QTHVHNJJ.js} +6 -36
- package/dist/chunk-QTHVHNJJ.js.map +1 -0
- package/dist/chunk-RPET332G.js +84 -0
- package/dist/chunk-RPET332G.js.map +1 -0
- package/dist/{chunk-646CA6RV.js → chunk-RQW5YMH7.js} +12 -4
- package/dist/chunk-RQW5YMH7.js.map +1 -0
- package/dist/{chunk-XALU5LCU.js → chunk-RWNYIU6A.js} +550 -146
- package/dist/chunk-RWNYIU6A.js.map +1 -0
- package/dist/chunk-SL7YUHPV.js +111 -0
- package/dist/chunk-SL7YUHPV.js.map +1 -0
- package/dist/chunk-U5PWXS3J.js +39 -0
- package/dist/chunk-U5PWXS3J.js.map +1 -0
- package/dist/chunk-ULDCFL2O.js +273 -0
- package/dist/chunk-ULDCFL2O.js.map +1 -0
- package/dist/{chunk-RWNSW4FM.js → chunk-UMNXCU3K.js} +12 -6
- package/dist/{chunk-RWNSW4FM.js.map → chunk-UMNXCU3K.js.map} +1 -1
- package/dist/chunk-VI7ZANB3.js +193 -0
- package/dist/chunk-VI7ZANB3.js.map +1 -0
- package/dist/chunk-VQPHPQ5L.js +14 -0
- package/dist/chunk-VQPHPQ5L.js.map +1 -0
- package/dist/chunk-WNJTJRM6.js +147 -0
- package/dist/chunk-WNJTJRM6.js.map +1 -0
- package/dist/chunk-WOBSEO75.js +790 -0
- package/dist/chunk-WOBSEO75.js.map +1 -0
- package/dist/{chunk-BMHS4WAN.js → chunk-XS7EX55U.js} +156 -42
- package/dist/chunk-XS7EX55U.js.map +1 -0
- package/dist/chunk-YN4W7LCX.js +31 -0
- package/dist/chunk-YN4W7LCX.js.map +1 -0
- package/dist/chunk-YSJOZEI6.js +1 -0
- package/dist/{chunk-JA2ZAT5Q.js → chunk-Z4PJMZLM.js} +2 -2
- package/dist/chunk-Z4PJMZLM.js.map +1 -0
- package/dist/{chunk-7VPQHEAJ.js → chunk-ZNZISJDZ.js} +6 -2
- package/dist/chunk-ZNZISJDZ.js.map +1 -0
- package/dist/chunk-ZWN3AODA.js +11 -0
- package/dist/chunk-ZWN3AODA.js.map +1 -0
- package/dist/cli/index.js +25 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/client/core.d.ts +1 -1
- package/dist/client/core.js +2 -0
- package/dist/client/core.js.map +1 -1
- package/dist/client/index.d.ts +44 -10
- package/dist/client/index.js +35 -0
- package/dist/client/index.js.map +1 -1
- package/dist/cloudflare-SIG42OIW.js +335 -0
- package/dist/cloudflare-SIG42OIW.js.map +1 -0
- package/dist/config-support-WQHQIJTA.js +104 -0
- package/dist/config-support-WQHQIJTA.js.map +1 -0
- package/dist/{csrf-BBrEZSBW.d.ts → csrf-Dgot9qSM.d.ts} +1 -1
- package/dist/deno-deploy-LQAYOOPW.js +196 -0
- package/dist/deno-deploy-LQAYOOPW.js.map +1 -0
- package/dist/{dev-MUWJAZRD.js → dev-ANDSEVLB.js} +28 -18
- package/dist/dev-ANDSEVLB.js.map +1 -0
- package/dist/{dev-emit-GWD6IXV7.js → dev-emit-BFNMDCPB.js} +5 -5
- package/dist/{dev-emit-NBT6JD7R.js → dev-emit-MVAJAFGX.js} +5 -3
- package/dist/dev-emit-MVAJAFGX.js.map +1 -0
- package/dist/engine-singleton-X4R7AZBJ.js +315 -0
- package/dist/engine-singleton-X4R7AZBJ.js.map +1 -0
- package/dist/{generate-Y3GWM57K.js → generate-EN6EDITF.js} +43 -4
- package/dist/generate-EN6EDITF.js.map +1 -0
- package/dist/index-CzxtQcnp.d.ts +1058 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +24 -13
- package/dist/index.js.map +1 -1
- package/dist/{info-XW7HGUPH.js → info-Y55RWS42.js} +8 -6
- package/dist/{info-XW7HGUPH.js.map → info-Y55RWS42.js.map} +1 -1
- package/dist/{internal-api-V5L65YSJ.js → internal-api-IP35YU3G.js} +22 -12
- package/dist/{internal-api-WEOSTMSN.js → internal-api-OPU456AT.js} +30 -16
- package/dist/internal-api-OPU456AT.js.map +1 -0
- package/dist/load-config-4ATXQUN3.js +16 -0
- package/dist/match-DmGgxjIU.d.ts +44 -0
- package/dist/{mcp-ULUFJNSZ.js → mcp-5LLNAGKA.js} +7 -7
- package/dist/netlify-C2IRCZKI.js +245 -0
- package/dist/netlify-C2IRCZKI.js.map +1 -0
- package/dist/node-OSVPYQ3D.js +10 -0
- package/dist/nonce-BXt7Qu_C.d.ts +31 -0
- package/dist/observability-bootstrap-MZMSOE66.js +15 -0
- package/dist/{openapi-HZXR335W.js → openapi-VFPEZSRI.js} +13 -11
- package/dist/{openapi-HZXR335W.js.map → openapi-VFPEZSRI.js.map} +1 -1
- package/dist/{plugin-runner-BFEcgtmJ.d.ts → plugin-runner-BVMOSxvb.d.ts} +1 -1
- package/dist/{plugin-types-D69-lJZ9.d.ts → plugin-types-BUVfUQ0V.d.ts} +13 -1
- package/dist/preview-GHPT2EQ5.js +23 -0
- package/dist/preview-GHPT2EQ5.js.map +1 -0
- package/dist/provider-resolver-Dx6QuWrw.d.ts +128 -0
- package/dist/registry-KBZDCFVQ.js +23 -0
- package/dist/route-config-Cm_ZMA1C.d.ts +72 -0
- package/dist/route-policy-BxOycCDq.d.ts +65 -0
- package/dist/router/element-scroll-restoration.d.ts +28 -0
- package/dist/router/element-scroll-restoration.js +96 -0
- package/dist/router/element-scroll-restoration.js.map +1 -0
- package/dist/{routes-OLNQX7CT.js → routes-BPLU3ISV.js} +11 -9
- package/dist/{routes-OLNQX7CT.js.map → routes-BPLU3ISV.js.map} +1 -1
- package/dist/{scan-7MJC6PYU.js → scan-YJEE4KQK.js} +2 -2
- package/dist/{schema-ZO3OSWVG.js → schema-SOAD43RQ.js} +7 -5
- package/dist/schemas-4AXHQMV3.js +25 -0
- package/dist/schemas-4AXHQMV3.js.map +1 -0
- package/dist/security-headers-Ccj7ncNx.d.ts +129 -0
- package/dist/server/agent/index.d.ts +2 -90
- package/dist/server/agent/index.js +1 -1
- package/dist/server/auth/index.d.ts +2 -21
- package/dist/server/auth/index.js +3 -2
- package/dist/server/cron/index.js +4 -3
- package/dist/server/define/index.d.ts +51 -57
- package/dist/server/define/index.js +10 -1
- package/dist/server/http/index.d.ts +7 -5
- package/dist/server/http/index.js +56 -12
- package/dist/server/index.d.ts +47 -439
- package/dist/server/index.js +119 -201
- package/dist/server/index.js.map +1 -1
- package/dist/server/jobs/index.js +4 -3
- package/dist/server/observability/index.d.ts +109 -35
- package/dist/server/observability/index.js +10 -7
- package/dist/server/plugins/index.d.ts +3 -3
- package/dist/server/scan/index.d.ts +47 -12
- package/dist/server/scan/index.js +13 -4
- package/dist/server/security/index.d.ts +61 -103
- package/dist/server/security/index.js +15 -6
- package/dist/server/webhook/index.d.ts +90 -3
- package/dist/server/webhook/index.js +12 -2
- package/dist/server-boundary-6B4ZQUY7.js +148 -0
- package/dist/server-boundary-6B4ZQUY7.js.map +1 -0
- package/dist/server-boundary-7VZQRPPX.js +133 -0
- package/dist/server-boundary-7VZQRPPX.js.map +1 -0
- package/dist/{services-typed-client-PSAGRPFF.js → services-typed-client-XGT5SUUN.js} +2 -2
- package/dist/{start-KR2JJR7K.js → start-UZDMOT7I.js} +342 -111
- package/dist/start-UZDMOT7I.js.map +1 -0
- package/dist/{static-M2W4HUWS.js → static-SVDWIOXJ.js} +24 -13
- package/dist/static-SVDWIOXJ.js.map +1 -0
- package/dist/{theo-cloud-XAHWOUFK.js → theo-cloud-3ET4G5T6.js} +9 -2
- package/dist/theo-cloud-3ET4G5T6.js.map +1 -0
- package/dist/vercel-BMCQKCWE.js +277 -0
- package/dist/vercel-BMCQKCWE.js.map +1 -0
- package/dist/vite-plugin/index.js +24 -13
- package/dist/vite-plugin-VKCT2LF2.js +42 -0
- package/dist/vite-plugin-VKCT2LF2.js.map +1 -0
- package/package.json +19 -6
- package/dist/actions-virtual-module-YQXRSSMW.js.map +0 -1
- package/dist/agent-7JJW3TPB.js +0 -15
- package/dist/app-typed-client-G3J5G7E5.js.map +0 -1
- package/dist/app-typed-client-GHO3EWZK.js.map +0 -1
- package/dist/aws-lambda-CYNDGU5O.js +0 -172
- package/dist/aws-lambda-CYNDGU5O.js.map +0 -1
- package/dist/build-WU6AKKIB.js.map +0 -1
- package/dist/bun-33RD6K7O.js.map +0 -1
- package/dist/chunk-646CA6RV.js.map +0 -1
- package/dist/chunk-6E5WKMDS.js +0 -67
- package/dist/chunk-6E5WKMDS.js.map +0 -1
- package/dist/chunk-6GGWS7J3.js +0 -91
- package/dist/chunk-6GGWS7J3.js.map +0 -1
- package/dist/chunk-7LVM22VG.js.map +0 -1
- package/dist/chunk-7VPQHEAJ.js.map +0 -1
- package/dist/chunk-A7JBF5FO.js.map +0 -1
- package/dist/chunk-AM3QABW5.js +0 -161
- package/dist/chunk-AM3QABW5.js.map +0 -1
- package/dist/chunk-BIVCP6X5.js.map +0 -1
- package/dist/chunk-BMHS4WAN.js.map +0 -1
- package/dist/chunk-E576KYYT.js.map +0 -1
- package/dist/chunk-FB2HBNIC.js.map +0 -1
- package/dist/chunk-FNTLKHZ7.js.map +0 -1
- package/dist/chunk-GRI4XHBJ.js.map +0 -1
- package/dist/chunk-GVW5EKEU.js +0 -422
- package/dist/chunk-GVW5EKEU.js.map +0 -1
- package/dist/chunk-GXEX4CBH.js.map +0 -1
- package/dist/chunk-HBU2GIMZ.js.map +0 -1
- package/dist/chunk-IEES3CHD.js.map +0 -1
- package/dist/chunk-J5MXCGKA.js.map +0 -1
- package/dist/chunk-JA2ZAT5Q.js.map +0 -1
- package/dist/chunk-KD5G3YXI.js.map +0 -1
- package/dist/chunk-LNVMHQ2Z.js +0 -54
- package/dist/chunk-LNVMHQ2Z.js.map +0 -1
- package/dist/chunk-LXAESF4Y.js.map +0 -1
- package/dist/chunk-MTEWLGVY.js.map +0 -1
- package/dist/chunk-O3JIAU3V.js.map +0 -1
- package/dist/chunk-OJLL4YD6.js +0 -403
- package/dist/chunk-OJLL4YD6.js.map +0 -1
- package/dist/chunk-OLGPYOLO.js.map +0 -1
- package/dist/chunk-S433N3CT.js.map +0 -1
- package/dist/chunk-T5C46IIU.js.map +0 -1
- package/dist/chunk-U267AOWS.js +0 -28
- package/dist/chunk-U267AOWS.js.map +0 -1
- package/dist/chunk-UW2TEAGR.js +0 -77
- package/dist/chunk-UW2TEAGR.js.map +0 -1
- package/dist/chunk-WPJOTQA5.js +0 -1
- package/dist/chunk-XALU5LCU.js.map +0 -1
- package/dist/chunk-XR74JD4M.js +0 -457
- package/dist/chunk-XR74JD4M.js.map +0 -1
- package/dist/chunk-YGAXV5HR.js.map +0 -1
- package/dist/chunk-YRBR424R.js.map +0 -1
- package/dist/cloudflare-KYUWCEPL.js +0 -122
- package/dist/cloudflare-KYUWCEPL.js.map +0 -1
- package/dist/deno-deploy-CCTB7HGS.js +0 -107
- package/dist/deno-deploy-CCTB7HGS.js.map +0 -1
- package/dist/dev-MUWJAZRD.js.map +0 -1
- package/dist/dev-emit-NBT6JD7R.js.map +0 -1
- package/dist/generate-Y3GWM57K.js.map +0 -1
- package/dist/index-BEtEv6ZH.d.ts +0 -575
- package/dist/load-config-VKD6LKWU.js +0 -14
- package/dist/match-CfbEFRG4.d.ts +0 -26
- package/dist/netlify-AFMDNKT4.js +0 -150
- package/dist/netlify-AFMDNKT4.js.map +0 -1
- package/dist/node-ZJQEJSP3.js +0 -10
- package/dist/registry-GKR3EWRW.js +0 -23
- package/dist/start-KR2JJR7K.js.map +0 -1
- package/dist/static-M2W4HUWS.js.map +0 -1
- package/dist/theo-cloud-XAHWOUFK.js.map +0 -1
- package/dist/vercel-SRS2DH6O.js +0 -133
- package/dist/vercel-SRS2DH6O.js.map +0 -1
- package/dist/vite-plugin-VPJFD4HK.js +0 -36
- /package/dist/{chunk-WPJOTQA5.js.map → adapters/agent-mount.js.map} +0 -0
- /package/dist/{agent-7JJW3TPB.js.map → agent-GKUEL6OH.js.map} +0 -0
- /package/dist/{check-45NKUVSQ.js.map → check-GIPZBFTX.js.map} +0 -0
- /package/dist/{chunk-47CINLWF.js.map → chunk-44J2ANQC.js.map} +0 -0
- /package/dist/{chunk-ZB7D62OA.js.map → chunk-KUZVRJ7M.js.map} +0 -0
- /package/dist/{chunk-PXFXSFJL.js.map → chunk-NZ5JU6D3.js.map} +0 -0
- /package/dist/{internal-api-WEOSTMSN.js.map → chunk-YSJOZEI6.js.map} +0 -0
- /package/dist/{dev-emit-GWD6IXV7.js.map → dev-emit-BFNMDCPB.js.map} +0 -0
- /package/dist/{internal-api-V5L65YSJ.js.map → internal-api-IP35YU3G.js.map} +0 -0
- /package/dist/{load-config-VKD6LKWU.js.map → load-config-4ATXQUN3.js.map} +0 -0
- /package/dist/{mcp-ULUFJNSZ.js.map → mcp-5LLNAGKA.js.map} +0 -0
- /package/dist/{node-ZJQEJSP3.js.map → node-OSVPYQ3D.js.map} +0 -0
- /package/dist/{scan-7MJC6PYU.js.map → observability-bootstrap-MZMSOE66.js.map} +0 -0
- /package/dist/{registry-GKR3EWRW.js.map → registry-KBZDCFVQ.js.map} +0 -0
- /package/dist/{schema-ZO3OSWVG.js.map → scan-YJEE4KQK.js.map} +0 -0
- /package/dist/{vite-plugin-VPJFD4HK.js.map → schema-SOAD43RQ.js.map} +0 -0
- /package/dist/{services-typed-client-PSAGRPFF.js.map → services-typed-client-XGT5SUUN.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/netlify.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Netlify deploy adapter. All paths derived from `cwd` and a fixed\n * `.theokit/netlify/` output layout. Build-time tool — no HTTP input.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport type { TheoConfig } from '../config/schema.js'\nimport type { SecurityHeadersConfig } from '../core/contracts/security-headers.js'\nimport { assertServicesUnsupported, readManifest } from '../services/index.js'\n\nimport { deployedCorsFragment, type DeployedCorsOptions } from './deployed-cors.js'\nimport { deployedCsrfFragment, type DeployedCsrfOptions } from './deployed-csrf.js'\nimport {\n deployedRuntimeConfigFragment,\n type DeployedRuntimeConfigOptions,\n} from './deployed-runtime-config.js'\nimport { deployedTraceFragment } from './deployed-trace.js'\nimport { nodeAdapter } from './node.js'\nimport {\n buildSecurityHeaders,\n describeDeployedSecurityHeaders,\n renderSecurityHeadersConfigLiteral,\n} from './security-headers.js'\nimport type { AdapterBuildContext, DeployAdapter } from './types.js'\n\nexport class NetlifyConflictError extends Error {\n constructor(path: string, currentTo: string) {\n super(\n `netlify.toml has a conflicting redirect: \"${path}\" → \"${currentTo}\". ` +\n `Remove it manually or edit it to point to \"/.netlify/functions/theo\" ` +\n `before running the Netlify adapter again.`,\n )\n this.name = 'NetlifyConflictError'\n }\n}\n\nexport interface NetlifyBuildDeps {\n runNodeBuild?: (config: TheoConfig, cwd: string, ctx?: AdapterBuildContext) => Promise<void>\n writeFile?: (path: string, content: string) => void\n ensureDir?: (path: string) => void\n readTomlIfExists?: () => string | null\n}\n\nexport function renderNetlifyFunction(\n opts: { securityHeaders?: SecurityHeadersConfig } & DeployedCsrfOptions &\n DeployedRuntimeConfigOptions &\n DeployedCorsOptions = {},\n): string {\n const runtimeConfig = deployedRuntimeConfigFragment(opts)\n return [\n `// Generated by Theo — Netlify Functions adapter`,\n `import { resolve } from 'node:path'`,\n `import { scanServerRoutes, matchRoute, executeRoute, createProductionLoader, extractTraceIdFromRequest, TRACE_HEADER, createCorsWebHandler } from 'theokit/server'`,\n `import { createWebShim } from 'theokit/adapters/web-shim'`,\n `import { buildSecurityHeaders, withSecurityHeaders } from 'theokit/adapters/security-headers'`,\n ``,\n `const cwd = process.cwd()`,\n `const serverDir = resolve(cwd, 'server')`,\n `let routesCache = null`,\n `let loaderCache = null`,\n ``,\n `// #410 — the security baseline \\`theokit start\\` puts on every response,`,\n `// carried here as a literal because the deployed function has no`,\n `// theo.config.ts to read. The netlify.toml redirect routes only /api/* here,`,\n `// so this covers the API and not the document Netlify's static host serves`,\n `// (usetheokit/theokit#412).`,\n `const SECURITY_HEADERS_CONFIG = ${renderSecurityHeadersConfigLiteral(opts.securityHeaders)}`,\n `const SECURITY_HEADERS = buildSecurityHeaders(SECURITY_HEADERS_CONFIG, { production: true })`,\n ``,\n ...runtimeConfig.imports,\n ...runtimeConfig.declarations,\n ...deployedCsrfFragment(opts),\n ``,\n ...deployedCorsFragment(opts.cors, 'netlify'),\n ``,\n `export default async (request, context) => {`,\n ` // #409 — the preflight is answered BEFORE anything routes: an OPTIONS the router`,\n ` // handles is an OPTIONS the browser never gets a CORS answer to.`,\n ` const preflight = corsPreflight(request)`,\n ` if (preflight !== null) return withSecurityHeaders(preflight, SECURITY_HEADERS)`,\n ` return withCors(request, withSecurityHeaders(await handleRequest(request), SECURITY_HEADERS))`,\n `}`,\n ``,\n `async function handleRequest(request) {`,\n ` const url = new URL(request.url)`,\n ` if (!url.pathname.startsWith('/api/')) {`,\n ` return new Response('Not Found', { status: 404 })`,\n ` }`,\n ``,\n ` if (!routesCache) routesCache = scanServerRoutes(serverDir)`,\n ` if (!loaderCache) loaderCache = createProductionLoader()`,\n ``,\n ` const match = matchRoute(url.pathname, routesCache)`,\n ` if (!match) {`,\n ` return new Response(JSON.stringify({ error: { code: 'NOT_FOUND' } }), {`,\n ` status: 404,`,\n ` headers: { 'content-type': 'application/json' },`,\n ` })`,\n ` }`,\n ``,\n ` const { req, res, toResponse } = createWebShim(request)`,\n ...deployedTraceFragment('request', ' '),\n ` const method = request.method.toUpperCase()`,\n ` // #382 — the run is not awaited: toResponse() settles at the headers and`,\n ` // carries a live body, so Netlify streams while the handler writes.`,\n ` return toResponse(executeRoute({ route: match.route, method, params: match.params, req, res, loadModule: loaderCache, serverDir, requestId, ...CSRF_CONFIG, ${runtimeConfig.executeRouteSpread} }))`,\n `}`,\n ].join('\\n')\n}\n\nconst THEO_REDIRECT_FROM = '/api/*'\nconst THEO_REDIRECT_TO = '/.netlify/functions/theo'\n\n/**\n * TOML merge (EC-2) — non-destructive.\n *\n * We avoid adding a TOML parser dependency by working at the line level. The\n * supported subset is sufficient for the Netlify use case:\n * - detect existing `[[redirects]]` blocks\n * - find a block whose `from = \"...\"` matches our target\n * - error if such a block points elsewhere\n * - otherwise append our block\n *\n * Unknown sections (e.g. `[build]`, `[[headers]]`, `[context.production.environment]`)\n * are preserved as-is.\n */\ninterface NetlifyRedirectBlock {\n startLine: number\n endLine: number\n from?: string\n to?: string\n}\n\nfunction parseRedirectBlocks(lines: readonly string[]): NetlifyRedirectBlock[] {\n const blocks: NetlifyRedirectBlock[] = []\n let current: NetlifyRedirectBlock | null = null\n\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i].trim()\n if (trimmed === '[[redirects]]') {\n if (current) {\n current.endLine = i - 1\n blocks.push(current)\n }\n current = { startLine: i, endLine: i }\n continue\n }\n if (current && /^\\[(\\[|[^[])/.test(trimmed)) {\n // Entering another section — close current.\n current.endLine = i - 1\n blocks.push(current)\n current = null\n }\n if (current) {\n const fromMatch = /^from\\s*=\\s*\"([^\"]+)\"/.exec(trimmed)\n if (fromMatch) current.from = fromMatch[1]\n const toMatch = /^to\\s*=\\s*\"([^\"]+)\"/.exec(trimmed)\n if (toMatch) current.to = toMatch[1]\n current.endLine = i\n }\n }\n if (current) blocks.push(current)\n return blocks\n}\n\nconst THEO_REDIRECT_TARGET = [\n '[[redirects]]',\n ' from = \"/api/*\"',\n ' to = \"/.netlify/functions/theo\"',\n ' status = 200',\n ' force = true',\n].join('\\n')\n\n/**\n * Marks the block this build owns, so it can be REGENERATED rather than merely not duplicated.\n *\n * Idempotence alone would be the wrong contract here: the block carries configuration, so a\n * `security.headers` change has to reach the file. Leaving an existing block in place — which is\n * what the redirect above correctly does, because its content is fixed — would silently pin the\n * baseline to whatever the first build emitted.\n */\nconst THEO_HEADERS_MARKER = '# Generated by Theo — security baseline (usetheokit/theokit#412)'\n\n/**\n * The `[[headers]]` block telling Netlify's static host the baseline.\n *\n * The emitted function applies these to every response IT returns, and it never returns the HTML\n * document: the `[[redirects]]` block routes only `/api/*`, so the page comes from Netlify's static\n * host and carried none of them. The values come from `buildSecurityHeaders` — the same function\n * the handler calls — rather than being written out here, because two lists of headers that must\n * agree are two lists that eventually do not.\n */\nfunction renderHeadersBlock(securityHeaders: SecurityHeadersConfig | undefined): string {\n const values = buildSecurityHeaders(securityHeaders ?? {}, { production: true })\n return [\n THEO_HEADERS_MARKER,\n '[[headers]]',\n ' for = \"/*\"',\n ' [headers.values]',\n ...Object.entries(values).map(([k, v]) => ` ${k} = ${JSON.stringify(v)}`),\n ].join('\\n')\n}\n\n/**\n * Drop a previously generated block, so the next one replaces it.\n *\n * Bounded by the marker and the next top-level section at column 0 (or EOF) — the shape TOML gives\n * a block. A user's own `[[headers]]` is untouched because it does not carry the marker: their\n * file, their rules, and Netlify applies both.\n */\nfunction withoutGeneratedHeaders(source: string): string {\n const lines = source.split(/\\r?\\n/)\n const start = lines.indexOf(THEO_HEADERS_MARKER)\n if (start < 0) return source\n\n const opensSection = (line: string | undefined): boolean => (line ?? '').startsWith('[')\n\n let end = start + 1\n while (end < lines.length && !opensSection(lines[end])) end += 1\n // The marker line itself opens the block, so `end` now sits on the NEXT section — but the block's\n // own `[[headers]]` is the first `[` after the marker, so step past it before scanning again.\n end += 1\n while (end < lines.length && !opensSection(lines[end])) end += 1\n\n return [...lines.slice(0, start), ...lines.slice(end)].join('\\n').replace(/\\n{3,}/gu, '\\n\\n')\n}\n\nexport function mergeNetlifyToml(\n existing: string | null,\n securityHeaders?: SecurityHeadersConfig,\n): string {\n const headersBlock = renderHeadersBlock(securityHeaders)\n\n if (existing === null || existing.trim().length === 0) {\n return `${THEO_REDIRECT_TARGET}\\n\\n${headersBlock}\\n`\n }\n\n existing = withoutGeneratedHeaders(existing)\n\n const blocks = parseRedirectBlocks(existing.split(/\\r?\\n/))\n\n // Detect conflict: every `/api/*` block must point at our function.\n for (const b of blocks) {\n if (b.from !== THEO_REDIRECT_FROM) continue\n if (b.to === THEO_REDIRECT_TO) {\n // The redirect is already there. The headers block is regenerated regardless, because it\n // carries configuration and the redirect does not.\n const sep = existing.endsWith('\\n') ? '' : '\\n'\n return `${existing}${sep}\\n${headersBlock}\\n`\n }\n throw new NetlifyConflictError(b.from, b.to ?? '(unknown)')\n }\n\n // No conflict — append target block.\n const sep = existing.endsWith('\\n') ? '' : '\\n'\n return `${existing}${sep}\\n${THEO_REDIRECT_TARGET}\\n\\n${headersBlock}\\n`\n}\n\nexport async function buildNetlify(\n config: TheoConfig,\n cwd: string,\n deps: NetlifyBuildDeps = {},\n ctx?: AdapterBuildContext,\n): Promise<void> {\n // Wave 2 (T2.2) — reject polyglot services on this adapter.\n assertServicesUnsupported('netlify', readManifest(cwd))\n\n const runNodeBuild = deps.runNodeBuild ?? nodeAdapter.build.bind(nodeAdapter)\n await runNodeBuild(config, cwd, ctx)\n\n const ensureDir = deps.ensureDir ?? ((p: string) => mkdirSync(p, { recursive: true }))\n const writeFile =\n deps.writeFile ??\n ((p, c) => {\n writeFileSync(p, c)\n })\n const readTomlIfExists =\n deps.readTomlIfExists ??\n (() => {\n const p = resolve(cwd, 'netlify.toml')\n return existsSync(p) ? readFileSync(p, 'utf-8') : null\n })\n\n const fnDir = resolve(cwd, '.netlify/functions')\n ensureDir(fnDir)\n writeFile(\n resolve(fnDir, 'theo.mjs'),\n renderNetlifyFunction({\n securityHeaders: config.security?.headers,\n csrf: config.security?.csrf,\n disallowed: config.security?.disallowed,\n cors: config.security?.cors,\n // #425 — a selector, not a transformer, so it rides as a literal like the values above.\n serialization: config.serialization,\n }),\n )\n\n const existingToml = readTomlIfExists()\n const merged = mergeNetlifyToml(existingToml, config.security?.headers)\n writeFile(resolve(cwd, 'netlify.toml'), merged)\n\n // eslint-disable-next-line no-console -- CLI build progress\n console.log('\\n ✓ Netlify output → .netlify/functions/theo.mjs + netlify.toml')\n // eslint-disable-next-line no-console -- CLI build progress\n console.log(\n `${describeDeployedSecurityHeaders({\n target: 'netlify',\n securityHeaders: config.security?.headers,\n mintsNonce: false,\n documentHeaders: 'platform-configured',\n })}\\n`,\n )\n}\n\nexport const netlifyAdapter: DeployAdapter = {\n name: 'netlify',\n streamsResponses: true,\n // #409 / #410 — the generated entry calls `executeRoute` with routes, loader\n // and serverDir only. CSRF, route policy, file middleware and Zod validation\n // still run because they live inside `executeRoute`; none of the remaining\n // configurable concerns reach it. Declared explicitly rather than omitted so\n // the gap is a statement in the source and not an absence.\n //\n // `securityHeaders` IS applied -- to every response this function returns.\n // The document is served by Netlify's static host and does not pass through\n // it (usetheokit/theokit#412).\n appliesConfig: ['securityHeaders', 'csrf', 'disallowed', 'cors', 'serialization'],\n build(config, cwd, ctx) {\n return buildNetlify(config, cwd, {}, ctx)\n },\n}\n"],"mappings":"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","names":["sep"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-request CSP nonce, minted the same way on every target.
|
|
3
|
+
*
|
|
4
|
+
* It lives in `core/contracts/` for the same reason
|
|
5
|
+
* `core/contracts/security-headers.ts` does: `theokit start` needs it
|
|
6
|
+
* (`cli/commands/start/request-handler.ts`) and so does the streamed Cloudflare
|
|
7
|
+
* worker, which is the one deploy path that renders HTML at request time and can
|
|
8
|
+
* put the same value on the script tags it emits. `adapters → server` is not an
|
|
9
|
+
* edge in the module DAG (ADR-0001 v3), and a Web-Crypto call that returns a
|
|
10
|
+
* base64 string was never server code.
|
|
11
|
+
*
|
|
12
|
+
* T4.1 — Per-request CSP nonce generation.
|
|
13
|
+
*
|
|
14
|
+
* Returns a 22–24 character base64-encoded string carrying 16 bytes of
|
|
15
|
+
* cryptographic entropy. Used by the SSR pipeline to nonce the inline
|
|
16
|
+
* hydration data script so the default CSP can drop `'unsafe-inline'`
|
|
17
|
+
* from `script-src` in 0.3.0.
|
|
18
|
+
*
|
|
19
|
+
* Runtime portability: prefers Web Crypto (`globalThis.crypto`) because
|
|
20
|
+
* it is available on every target runtime (Node 19+, Bun, Deno, Vercel
|
|
21
|
+
* Edge, Cloudflare Workers). Falls back to `node:crypto` for older Node
|
|
22
|
+
* builds where `globalThis.crypto` is absent.
|
|
23
|
+
*
|
|
24
|
+
* NOT a security primitive in the cryptographic sense — the nonce is a
|
|
25
|
+
* one-shot, single-request value that defeats trivial XSS injection by
|
|
26
|
+
* making the attacker guess a 128-bit string per request. It is NOT
|
|
27
|
+
* suitable for signing or session tokens.
|
|
28
|
+
*/
|
|
29
|
+
declare function generateNonce(): string;
|
|
30
|
+
|
|
31
|
+
export { generateNonce as g };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
3
|
+
import {
|
|
4
|
+
_resetObservabilityAdapter,
|
|
5
|
+
createObservabilityPluginFromConfig,
|
|
6
|
+
getObservabilityAdapter
|
|
7
|
+
} from "./chunk-7PM6K5EV.js";
|
|
8
|
+
import "./chunk-2AMLGJJA.js";
|
|
9
|
+
import "./chunk-VI7ZANB3.js";
|
|
10
|
+
export {
|
|
11
|
+
_resetObservabilityAdapter,
|
|
12
|
+
createObservabilityPluginFromConfig,
|
|
13
|
+
getObservabilityAdapter
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=observability-bootstrap-MZMSOE66.js.map
|
|
@@ -4,18 +4,20 @@ import {
|
|
|
4
4
|
emitOpenApi,
|
|
5
5
|
loadRoutesForOpenApi
|
|
6
6
|
} from "./chunk-47VNWTL4.js";
|
|
7
|
-
import {
|
|
8
|
-
loadConfig
|
|
9
|
-
} from "./chunk-S433N3CT.js";
|
|
10
|
-
import "./chunk-HBU2GIMZ.js";
|
|
11
|
-
import "./chunk-GVW5EKEU.js";
|
|
12
|
-
import "./chunk-7VPQHEAJ.js";
|
|
13
7
|
import {
|
|
14
8
|
generateManifest
|
|
15
|
-
} from "./chunk-
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
18
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-KUZVRJ7M.js";
|
|
10
|
+
import "./chunk-EVZKNA2W.js";
|
|
11
|
+
import "./chunk-XS7EX55U.js";
|
|
12
|
+
import "./chunk-FD4ZDTQ6.js";
|
|
13
|
+
import {
|
|
14
|
+
loadConfig
|
|
15
|
+
} from "./chunk-QTHVHNJJ.js";
|
|
16
|
+
import "./chunk-3QPVEJ7D.js";
|
|
17
|
+
import "./chunk-U5PWXS3J.js";
|
|
18
|
+
import "./chunk-FH3SQR6P.js";
|
|
19
|
+
import "./chunk-ZNZISJDZ.js";
|
|
20
|
+
import "./chunk-VI7ZANB3.js";
|
|
19
21
|
|
|
20
22
|
// src/cli/commands/openapi.ts
|
|
21
23
|
import { mkdtempSync, rmSync } from "fs";
|
|
@@ -82,4 +84,4 @@ function emitOpenApiInMemory(manifest, config) {
|
|
|
82
84
|
export {
|
|
83
85
|
openapiCommand
|
|
84
86
|
};
|
|
85
|
-
//# sourceMappingURL=openapi-
|
|
87
|
+
//# sourceMappingURL=openapi-VFPEZSRI.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/commands/openapi.ts"],"sourcesContent":["/**\n * `theokit openapi` — regenerate `<distDir>/openapi.json` standalone.\n *\n * Mirrors trpc-openapi's `pnpm codegen` script. Useful in dev workflows\n * when only schemas changed and a full `theokit build` would be\n * overkill.\n *\n * Per G2 plan v1.1 T2.3. Single output (dev location only) — for the\n * dist artifact, run `theokit build`.\n *\n * Flags:\n * --dry-run Validate + print the document to stdout; skip the\n * filesystem write (EC-3 absorbed).\n */\nimport { mkdtempSync, rmSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { join, resolve } from 'node:path'\n\nimport { loadConfig } from '../../config/load-config.js'\nimport { generateManifest } from '../../server/scan/manifest.js'\nimport { emitOpenApi } from '../../vite-plugin/openapi-emit/emit.js'\nimport { loadRoutesForOpenApi } from '../../vite-plugin/openapi-emit/load-routes.js'\n\nconst OPENAPI_DOCS_URL = 'https://theokit.dev/concepts/openapi'\n\ninterface OpenApiCommandOptions {\n /** Project root (defaults to `process.cwd()`). */\n cwd?: string\n /** Skip the filesystem write; print the document to stdout (EC-3). */\n dryRun?: boolean\n}\n\nexport async function openapiCommand(options: OpenApiCommandOptions = {}): Promise<void> {\n const cwd = options.cwd ?? process.cwd()\n const config = await loadConfig(cwd)\n\n if (config.openapi === undefined) {\n console.error(\n `\\n ✗ openapi: not configured in theo.config.ts\\n\\n` +\n ` Add the \\`openapi\\` block to opt in:\\n\\n` +\n ` export default defineConfig({\\n` +\n ` openapi: {\\n` +\n ` title: 'My App',\\n` +\n ` version: '1.0.0',\\n` +\n ` },\\n` +\n ` })\\n\\n` +\n ` See ${OPENAPI_DOCS_URL} for the full reference.\\n`,\n )\n process.exit(1)\n return\n }\n\n const serverDir = resolve(cwd, config.serverDir)\n const manifest = generateManifest(serverDir)\n const hydrated = await loadRoutesForOpenApi({ serverDir, routes: manifest.routes })\n\n if (options.dryRun) {\n // Build the document without writing — for the dry-run we still want a\n // valid emit pass, but with an in-memory output dir we never touch disk.\n const result = emitOpenApiInMemory(hydrated, config.openapi)\n console.log(`\\n ✓ openapi.json (dry-run): ${String(hydrated.length)} ops`)\n console.log(JSON.stringify(result.document, null, 2))\n console.log(`\\n Docs: ${OPENAPI_DOCS_URL}\\n`)\n return\n }\n\n const distDir = resolve(cwd, config.distDir)\n const result = emitOpenApi({\n manifest: hydrated,\n config: { ...config.openapi, outDir: distDir },\n })\n console.log(\n `\\n ✓ openapi.json: ${String(hydrated.length)} ops → ${result.path}\\n` +\n ` Docs: ${OPENAPI_DOCS_URL}\\n`,\n )\n}\n\n/**\n * Dry-run helper: build the OpenAPI doc without writing. Routes through\n * the same `emitOpenApi` but discards the side-effecting write by\n * targeting a temp directory we immediately ignore.\n *\n * Honest tradeoff: this still calls `mkdirSync` (no-op on existing tmpdir)\n * + a synchronous write to a discardable tempfile. Cost is < 1ms and the\n * code stays the single transformation. A pure no-write helper would\n * duplicate the orchestration in `emit.ts` — KISS prefers reuse.\n */\nfunction emitOpenApiInMemory(\n manifest: Parameters<typeof emitOpenApi>[0]['manifest'],\n config: Omit<Parameters<typeof emitOpenApi>[0]['config'], 'outDir'>,\n): { document: unknown } {\n const tmp = mkdtempSync(join(tmpdir(), 'theokit-openapi-dryrun-'))\n try {\n const result = emitOpenApi({ manifest, config: { ...config, outDir: tmp } })\n return { document: result.document }\n } finally {\n rmSync(tmp, { recursive: true, force: true })\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/openapi.ts"],"sourcesContent":["/**\n * `theokit openapi` — regenerate `<distDir>/openapi.json` standalone.\n *\n * Mirrors trpc-openapi's `pnpm codegen` script. Useful in dev workflows\n * when only schemas changed and a full `theokit build` would be\n * overkill.\n *\n * Per G2 plan v1.1 T2.3. Single output (dev location only) — for the\n * dist artifact, run `theokit build`.\n *\n * Flags:\n * --dry-run Validate + print the document to stdout; skip the\n * filesystem write (EC-3 absorbed).\n */\nimport { mkdtempSync, rmSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { join, resolve } from 'node:path'\n\nimport { loadConfig } from '../../config/load-config.js'\nimport { generateManifest } from '../../server/scan/manifest.js'\nimport { emitOpenApi } from '../../vite-plugin/openapi-emit/emit.js'\nimport { loadRoutesForOpenApi } from '../../vite-plugin/openapi-emit/load-routes.js'\n\nconst OPENAPI_DOCS_URL = 'https://theokit.dev/concepts/openapi'\n\ninterface OpenApiCommandOptions {\n /** Project root (defaults to `process.cwd()`). */\n cwd?: string\n /** Skip the filesystem write; print the document to stdout (EC-3). */\n dryRun?: boolean\n}\n\nexport async function openapiCommand(options: OpenApiCommandOptions = {}): Promise<void> {\n const cwd = options.cwd ?? process.cwd()\n const config = await loadConfig(cwd)\n\n if (config.openapi === undefined) {\n console.error(\n `\\n ✗ openapi: not configured in theo.config.ts\\n\\n` +\n ` Add the \\`openapi\\` block to opt in:\\n\\n` +\n ` export default defineConfig({\\n` +\n ` openapi: {\\n` +\n ` title: 'My App',\\n` +\n ` version: '1.0.0',\\n` +\n ` },\\n` +\n ` })\\n\\n` +\n ` See ${OPENAPI_DOCS_URL} for the full reference.\\n`,\n )\n process.exit(1)\n return\n }\n\n const serverDir = resolve(cwd, config.serverDir)\n const manifest = generateManifest(serverDir)\n const hydrated = await loadRoutesForOpenApi({ serverDir, routes: manifest.routes })\n\n if (options.dryRun) {\n // Build the document without writing — for the dry-run we still want a\n // valid emit pass, but with an in-memory output dir we never touch disk.\n const result = emitOpenApiInMemory(hydrated, config.openapi)\n console.log(`\\n ✓ openapi.json (dry-run): ${String(hydrated.length)} ops`)\n console.log(JSON.stringify(result.document, null, 2))\n console.log(`\\n Docs: ${OPENAPI_DOCS_URL}\\n`)\n return\n }\n\n const distDir = resolve(cwd, config.distDir)\n const result = emitOpenApi({\n manifest: hydrated,\n config: { ...config.openapi, outDir: distDir },\n })\n console.log(\n `\\n ✓ openapi.json: ${String(hydrated.length)} ops → ${result.path}\\n` +\n ` Docs: ${OPENAPI_DOCS_URL}\\n`,\n )\n}\n\n/**\n * Dry-run helper: build the OpenAPI doc without writing. Routes through\n * the same `emitOpenApi` but discards the side-effecting write by\n * targeting a temp directory we immediately ignore.\n *\n * Honest tradeoff: this still calls `mkdirSync` (no-op on existing tmpdir)\n * + a synchronous write to a discardable tempfile. Cost is < 1ms and the\n * code stays the single transformation. A pure no-write helper would\n * duplicate the orchestration in `emit.ts` — KISS prefers reuse.\n */\nfunction emitOpenApiInMemory(\n manifest: Parameters<typeof emitOpenApi>[0]['manifest'],\n config: Omit<Parameters<typeof emitOpenApi>[0]['config'], 'outDir'>,\n): { document: unknown } {\n const tmp = mkdtempSync(join(tmpdir(), 'theokit-openapi-dryrun-'))\n try {\n const result = emitOpenApi({ manifest, config: { ...config, outDir: tmp } })\n return { document: result.document }\n } finally {\n rmSync(tmp, { recursive: true, force: true })\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAcA,SAAS,aAAa,cAAc;AACpC,SAAS,cAAc;AACvB,SAAS,MAAM,eAAe;AAO9B,IAAM,mBAAmB;AASzB,eAAsB,eAAe,UAAiC,CAAC,GAAkB;AACvF,QAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;AACvC,QAAM,SAAS,MAAM,WAAW,GAAG;AAEnC,MAAI,OAAO,YAAY,QAAW;AAChC,YAAQ;AAAA,MACN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAQW,gBAAgB;AAAA;AAAA,IAC7B;AACA,YAAQ,KAAK,CAAC;AACd;AAAA,EACF;AAEA,QAAM,YAAY,QAAQ,KAAK,OAAO,SAAS;AAC/C,QAAM,WAAW,iBAAiB,SAAS;AAC3C,QAAM,WAAW,MAAM,qBAAqB,EAAE,WAAW,QAAQ,SAAS,OAAO,CAAC;AAElF,MAAI,QAAQ,QAAQ;AAGlB,UAAMA,UAAS,oBAAoB,UAAU,OAAO,OAAO;AAC3D,YAAQ,IAAI;AAAA,mCAAiC,OAAO,SAAS,MAAM,CAAC,MAAM;AAC1E,YAAQ,IAAI,KAAK,UAAUA,QAAO,UAAU,MAAM,CAAC,CAAC;AACpD,YAAQ,IAAI;AAAA,UAAa,gBAAgB;AAAA,CAAI;AAC7C;AAAA,EACF;AAEA,QAAM,UAAU,QAAQ,KAAK,OAAO,OAAO;AAC3C,QAAM,SAAS,YAAY;AAAA,IACzB,UAAU;AAAA,IACV,QAAQ,EAAE,GAAG,OAAO,SAAS,QAAQ,QAAQ;AAAA,EAC/C,CAAC;AACD,UAAQ;AAAA,IACN;AAAA,yBAAuB,OAAO,SAAS,MAAM,CAAC,eAAU,OAAO,IAAI;AAAA,UACtD,gBAAgB;AAAA;AAAA,EAC/B;AACF;AAYA,SAAS,oBACP,UACA,QACuB;AACvB,QAAM,MAAM,YAAY,KAAK,OAAO,GAAG,yBAAyB,CAAC;AACjE,MAAI;AACF,UAAM,SAAS,YAAY,EAAE,UAAU,QAAQ,EAAE,GAAG,QAAQ,QAAQ,IAAI,EAAE,CAAC;AAC3E,WAAO,EAAE,UAAU,OAAO,SAAS;AAAA,EACrC,UAAE;AACA,WAAO,KAAK,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EAC9C;AACF;","names":["result"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { T as TheoPlugin, a as TheoApp, P as PluginContext, H as HookResult, R as RunHookOptions } from './plugin-types-
|
|
1
|
+
import { T as TheoPlugin, a as TheoApp, P as PluginContext, H as HookResult, R as RunHookOptions } from './plugin-types-BUVfUQ0V.js';
|
|
2
2
|
|
|
3
3
|
declare class DuplicatePluginError extends Error {
|
|
4
4
|
constructor(name: string);
|
|
@@ -7,6 +7,15 @@ interface PluginContext {
|
|
|
7
7
|
* `request.method` is portable across `theokit dev` / `theokit start` (Node) and Web runtimes (#119,
|
|
8
8
|
* ADR-0028 R3a). Method + absolute URL + headers; the request body is not exposed here (onRequest /
|
|
9
9
|
* preHandler fire before the body is parsed — the handler reads it via `ctx.body`).
|
|
10
|
+
*
|
|
11
|
+
* `request.url` is ABSOLUTE (`http://host/api/thing`), because that is what a Web `Request`
|
|
12
|
+
* carries. A guard written as `request.url.startsWith('/api/')` is therefore false for every real
|
|
13
|
+
* request and fails silently — the hook simply never matches. To match on a path:
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* const { pathname } = new URL(ctx.request.url)
|
|
17
|
+
* if (pathname.startsWith('/api/')) { … }
|
|
18
|
+
* ```
|
|
10
19
|
*/
|
|
11
20
|
request: Request;
|
|
12
21
|
response: ServerResponse;
|
|
@@ -49,6 +58,9 @@ interface TheoPlugin {
|
|
|
49
58
|
* Available during `onResponse` / `onError`. `undefined` during
|
|
50
59
|
* `onRequest` / `preHandler` (which fire before the handler runs).
|
|
51
60
|
* - `ctx` / `requestId` — same semantics as the IncomingMessage path.
|
|
61
|
+
*
|
|
62
|
+
* `request.url` is ABSOLUTE here too — see the note on `PluginContext.request`; match paths with
|
|
63
|
+
* `new URL(ctx.request.url).pathname`, never `request.url.startsWith('/…')`.
|
|
52
64
|
*/
|
|
53
65
|
interface WebPluginContext {
|
|
54
66
|
request: Request;
|
|
@@ -65,4 +77,4 @@ type WebPreHandlerHook = (ctx: WebPluginContext) => void | Promise<void>;
|
|
|
65
77
|
type WebOnResponseHook = (ctx: WebPluginContext) => void | Promise<void>;
|
|
66
78
|
type WebOnErrorHook = (ctx: WebPluginErrorContext) => void | Promise<void>;
|
|
67
79
|
|
|
68
|
-
export type { HookResult as H, OnErrorHook as O, PluginContext as P, RunHookOptions as R, TheoPlugin as T, WebOnRequestHook as W, TheoApp as a,
|
|
80
|
+
export type { HookResult as H, OnErrorHook as O, PluginContext as P, RunHookOptions as R, TheoPlugin as T, WebOnRequestHook as W, TheoApp as a, HookName as b, OnRequestHook as c, OnResponseHook as d, PluginErrorContext as e, PreHandlerHook as f, WebPreHandlerHook as g, WebOnResponseHook as h, WebOnErrorHook as i };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
3
|
+
|
|
4
|
+
// src/cli/commands/preview.ts
|
|
5
|
+
async function defaultSteps() {
|
|
6
|
+
const [{ buildCommand }, { startCommand }] = await Promise.all([
|
|
7
|
+
import("./build-FLX6VF7Q.js"),
|
|
8
|
+
import("./start-UZDMOT7I.js")
|
|
9
|
+
]);
|
|
10
|
+
return {
|
|
11
|
+
build: (opts) => buildCommand(opts),
|
|
12
|
+
start: (opts) => startCommand(opts)
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
async function previewCommand(options = {}, steps) {
|
|
16
|
+
const { build, start } = steps ?? await defaultSteps();
|
|
17
|
+
await build({ target: options.target });
|
|
18
|
+
await start({ port: options.port });
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
previewCommand
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=preview-GHPT2EQ5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/preview.ts"],"sourcesContent":["/**\n * `theokit preview` — build for production, then serve the result.\n *\n * Reproducing production locally was two commands, `theokit build` then\n * `theokit start`, and the two-step version fails silently: `start` serves\n * whatever `.theokit/` already holds, so a skipped build serves the previous one\n * and nothing says so. The gap is widest exactly when it matters — after a\n * change, checking whether the change works (B-030).\n *\n * This is not a third implementation of either step. It calls both, in order,\n * and never reaches the second when the first throws. `build` and `start` stay\n * separately invocable because CI builds and serves in different jobs.\n */\n\nexport interface PreviewOptions {\n /** Port for the production server. Forwarded to `start`. */\n port?: number\n /** Deploy target to build for. Forwarded to `build`. */\n target?: string\n}\n\n/**\n * The two steps, injected so the composition can be tested without running a\n * real build — the composition IS the behaviour here, and a test that shells out\n * to a full build would be testing the build.\n */\nexport interface PreviewSteps {\n build: (opts: { target?: string }) => Promise<void>\n start: (opts: { port?: number }) => Promise<void>\n}\n\nasync function defaultSteps(): Promise<PreviewSteps> {\n const [{ buildCommand }, { startCommand }] = await Promise.all([\n import('./build.js'),\n import('./start/index.js'),\n ])\n return {\n build: (opts) => buildCommand(opts),\n start: (opts) => startCommand(opts),\n }\n}\n\nexport async function previewCommand(\n options: PreviewOptions = {},\n steps?: PreviewSteps,\n): Promise<void> {\n const { build, start } = steps ?? (await defaultSteps())\n\n // No catch: a build failure must reach the caller unchanged. Swallowing it\n // here and starting anyway would serve the previous build — the exact defect\n // this command exists to remove, reproduced inside its own fix.\n await build({ target: options.target })\n await start({ port: options.port })\n}\n"],"mappings":";;;;AA+BA,eAAe,eAAsC;AACnD,QAAM,CAAC,EAAE,aAAa,GAAG,EAAE,aAAa,CAAC,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC7D,OAAO,qBAAY;AAAA,IACnB,OAAO,qBAAkB;AAAA,EAC3B,CAAC;AACD,SAAO;AAAA,IACL,OAAO,CAAC,SAAS,aAAa,IAAI;AAAA,IAClC,OAAO,CAAC,SAAS,aAAa,IAAI;AAAA,EACpC;AACF;AAEA,eAAsB,eACpB,UAA0B,CAAC,GAC3B,OACe;AACf,QAAM,EAAE,OAAO,MAAM,IAAI,SAAU,MAAM,aAAa;AAKtD,QAAM,MAAM,EAAE,QAAQ,QAAQ,OAAO,CAAC;AACtC,QAAM,MAAM,EAAE,MAAM,QAAQ,KAAK,CAAC;AACpC;","names":[]}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Resolver — Strategy + Registry pattern (FAANG-grade).
|
|
3
|
+
*
|
|
4
|
+
* Inspiration: Dapr Conversation Registry (`dapr/pkg/components/conversation/registry.go`)
|
|
5
|
+
* + Encore Manager provider array (`encore/runtimes/go/pubsub/manager_internal.go`).
|
|
6
|
+
*
|
|
7
|
+
* Principle: provider routing is the FRAMEWORK's responsibility, not the consumer's — but the
|
|
8
|
+
* consumer still gets to SAY which provider it wants, and it says so in the model id.
|
|
9
|
+
*
|
|
10
|
+
* `provider/model` is the convention every agent in this ecosystem already writes
|
|
11
|
+
* (`anthropic/claude-sonnet-4-6`, `openrouter/anthropic/claude-haiku-4.5`), and it is the one the
|
|
12
|
+
* SDK routes on. Until theokit#326 this resolver ignored it entirely and picked by env-var
|
|
13
|
+
* priority, so an agent that declared `anthropic/...` was handed an OpenRouter key whenever one
|
|
14
|
+
* happened to be present — and every turn failed with `auth_failed (HTTP 401)` against a provider
|
|
15
|
+
* nobody asked for. The declared provider now wins; priority is the fallback for a bare model id.
|
|
16
|
+
*
|
|
17
|
+
* Wire protocol: OpenAI Chat Completions (universal — implemented by every
|
|
18
|
+
* os providers: OpenRouter, Groq, Mistral, Together, Anthropic via proxy, etc).
|
|
19
|
+
*
|
|
20
|
+
* Resolution by priority (FIRST match wins):
|
|
21
|
+
* 1. OPENROUTER_API_KEY → baseUrl=openrouter.ai (multi-model gateway)
|
|
22
|
+
* 2. OPENAI_API_KEY → baseUrl=api.openai.com
|
|
23
|
+
* 3. ANTHROPIC_API_KEY → direct Anthropic (Messages API, not OpenAI-compat)
|
|
24
|
+
*
|
|
25
|
+
* Escape hatch: an explicit `options.apiKey` OVERRIDES auto-resolution
|
|
26
|
+
* (the consumer can force a specific provider if it wants to).
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Provider configuration descriptor — Registry entry shape.
|
|
30
|
+
*
|
|
31
|
+
* @public
|
|
32
|
+
*/
|
|
33
|
+
interface ProviderDescriptor {
|
|
34
|
+
/** Stable name used internally — not exposed on the wire. */
|
|
35
|
+
name: string;
|
|
36
|
+
/**
|
|
37
|
+
* Environment variable that holds the API key for this provider.
|
|
38
|
+
*
|
|
39
|
+
* **Absent means the provider needs no credential** — a model running on the developer's own
|
|
40
|
+
* machine has nothing to authenticate against. Such a provider is reachable ONLY when a model id
|
|
41
|
+
* names it (`ollama/llama3.2`); it never participates in the priority walk, because the walk
|
|
42
|
+
* reads "this variable is set" as "a human configured this provider", and a keyless entry offers
|
|
43
|
+
* no equivalent signal. Including it would route every bare model id to localhost the moment no
|
|
44
|
+
* cloud key was set (usetheokit/theokit#407).
|
|
45
|
+
*/
|
|
46
|
+
envKey?: string;
|
|
47
|
+
/** Base URL for the provider's OpenAI-compatible (or native) API. */
|
|
48
|
+
baseUrl: string;
|
|
49
|
+
/**
|
|
50
|
+
* Environment variable that OVERRIDES {@link baseUrl} when set.
|
|
51
|
+
*
|
|
52
|
+
* Scoped honestly: every caller inside this framework takes `.apiKey` from the resolved provider
|
|
53
|
+
* and discards `.baseUrl` (`cli/commands/start/handlers.ts`, `vite-plugin/api-middleware.ts`,
|
|
54
|
+
* `cli/commands/agent.ts`), so the endpoint a request actually reaches is chosen by the SDK, from
|
|
55
|
+
* its own profile — which reads `OLLAMA_HOST` itself. This field therefore does not redirect
|
|
56
|
+
* traffic today.
|
|
57
|
+
*
|
|
58
|
+
* It exists so that the `baseUrl` this function REPORTS agrees with where the request goes, for
|
|
59
|
+
* the consumers that read it — `resolveProvider` is public API, and a reported endpoint that
|
|
60
|
+
* contradicts the real one is a debugging trap rather than a harmless inaccuracy.
|
|
61
|
+
*/
|
|
62
|
+
baseUrlEnv?: string;
|
|
63
|
+
/** Resolution priority (lower = higher priority). FIRST match wins. */
|
|
64
|
+
priority: number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Resolved provider configuration — output of `resolveProvider()`.
|
|
68
|
+
*
|
|
69
|
+
* @public
|
|
70
|
+
*/
|
|
71
|
+
interface ResolvedProvider {
|
|
72
|
+
name: string;
|
|
73
|
+
apiKey: string;
|
|
74
|
+
baseUrl: string;
|
|
75
|
+
}
|
|
76
|
+
/** Where the announcement goes, and what it may contain. @public */
|
|
77
|
+
interface ResolveOptions {
|
|
78
|
+
/** Receives one line per provider, at most once. NEVER receives the key. */
|
|
79
|
+
announce?: (line: string) => void;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Register a new provider (Registry pattern — runtime extension point).
|
|
83
|
+
* Useful for self-hosted endpoints or custom providers without touching theokit src.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* registerProvider({
|
|
87
|
+
* name: 'self-hosted',
|
|
88
|
+
* envKey: 'SELF_HOSTED_API_KEY',
|
|
89
|
+
* baseUrl: 'https://llm.internal.acme.com/v1',
|
|
90
|
+
* priority: 0, // highest priority
|
|
91
|
+
* })
|
|
92
|
+
*
|
|
93
|
+
* @public
|
|
94
|
+
*/
|
|
95
|
+
declare function registerProvider(descriptor: ProviderDescriptor): void;
|
|
96
|
+
/**
|
|
97
|
+
* Reset registry to DEFAULT_REGISTRY (test-only / dev escape hatch).
|
|
98
|
+
*
|
|
99
|
+
* @public
|
|
100
|
+
*/
|
|
101
|
+
declare function resetProviderRegistry(): void;
|
|
102
|
+
/**
|
|
103
|
+
* Get current registry snapshot (read-only — inspection).
|
|
104
|
+
*
|
|
105
|
+
* @public
|
|
106
|
+
*/
|
|
107
|
+
declare function listProviders(): readonly ProviderDescriptor[];
|
|
108
|
+
/**
|
|
109
|
+
* Resolve the provider for a model.
|
|
110
|
+
*
|
|
111
|
+
* When `modelId` declares a registered provider (`anthropic/…`), THAT provider's key is
|
|
112
|
+
* required — no substitution. Otherwise the registry is walked by priority, first match wins.
|
|
113
|
+
*
|
|
114
|
+
* @returns ResolvedProvider with apiKey + baseUrl + name
|
|
115
|
+
* @throws Error if NO provider env var is set (actionable message)
|
|
116
|
+
*
|
|
117
|
+
* @public
|
|
118
|
+
*/
|
|
119
|
+
declare function resolveProvider(modelId?: string, options?: ResolveOptions): ResolvedProvider;
|
|
120
|
+
/**
|
|
121
|
+
* Try to resolve — does NOT throw. Returns null if no provider available.
|
|
122
|
+
* Useful for graceful degradation (e.g., mock mode).
|
|
123
|
+
*
|
|
124
|
+
* @public
|
|
125
|
+
*/
|
|
126
|
+
declare function tryResolveProvider(modelId?: string): ResolvedProvider | null;
|
|
127
|
+
|
|
128
|
+
export { type ProviderDescriptor as P, type ResolvedProvider as R, registerProvider as a, resetProviderRegistry as b, listProviders as l, resolveProvider as r, tryResolveProvider as t };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
3
|
+
|
|
4
|
+
// src/adapters/registry.ts
|
|
5
|
+
var adapterRegistry = {
|
|
6
|
+
node: async () => (await import("./node-OSVPYQ3D.js")).nodeAdapter,
|
|
7
|
+
vercel: async () => (await import("./vercel-BMCQKCWE.js")).vercelAdapter,
|
|
8
|
+
cloudflare: async () => (await import("./cloudflare-SIG42OIW.js")).cloudflareAdapter,
|
|
9
|
+
static: async () => (await import("./static-SVDWIOXJ.js")).staticAdapter,
|
|
10
|
+
bun: async () => (await import("./bun-L7PP6ID5.js")).bunAdapter,
|
|
11
|
+
"deno-deploy": async () => (await import("./deno-deploy-LQAYOOPW.js")).denoDeployAdapter,
|
|
12
|
+
netlify: async () => (await import("./netlify-C2IRCZKI.js")).netlifyAdapter,
|
|
13
|
+
"aws-lambda": async () => (await import("./aws-lambda-VKMYUMP2.js")).awsLambdaAdapter,
|
|
14
|
+
"theo-cloud": async () => (await import("./theo-cloud-3ET4G5T6.js")).theoCloudAdapter
|
|
15
|
+
};
|
|
16
|
+
async function resolveAdapter(target) {
|
|
17
|
+
const factory = adapterRegistry[target];
|
|
18
|
+
return factory();
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
resolveAdapter
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=registry-KBZDCFVQ.js.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { R as RoutePolicy } from './route-policy-BxOycCDq.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* core/contracts/route-config.ts
|
|
6
|
+
*
|
|
7
|
+
* Canonical home for `RouteConfig<TQuery, TBody, TParams, TCtx, TResponse>` —
|
|
8
|
+
* the contract shape consumed by `defineRoute()` (server) and
|
|
9
|
+
* `defineCachedRoute()` (cache).
|
|
10
|
+
*
|
|
11
|
+
* Moved here in T2.2 of architecture-cleanup so `cache → core/contracts`
|
|
12
|
+
* is the legal edge (replacing the prior `cache → server` violation).
|
|
13
|
+
*
|
|
14
|
+
* GAP-4 (plan v1.1): the 5-arity generic shape `<TQuery, TBody, TParams, TCtx, TResponse>`
|
|
15
|
+
* MUST be preserved byte-by-byte. Type tests assert this in
|
|
16
|
+
* `tests/unit/route-config-generic-arity.test.ts`.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
interface RouteConfig<TQuery extends z.ZodType = z.ZodUndefined, TBody extends z.ZodType = z.ZodUndefined, TParams extends z.ZodType = z.ZodUndefined, TCtx = unknown, TResponse = unknown> {
|
|
20
|
+
query?: TQuery;
|
|
21
|
+
body?: TBody;
|
|
22
|
+
params?: TParams;
|
|
23
|
+
status?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Optional Zod schema for the handler's plain-object return value. When
|
|
26
|
+
* present, BOTH runtimes (Node `executeRoute` + Web `executeWebRequest`)
|
|
27
|
+
* validate the handler's plain-object return against it BEFORE serializing.
|
|
28
|
+
*
|
|
29
|
+
* A mismatch is a SERVER fault (the handler violated its own declared
|
|
30
|
+
* contract) → 500 `INTERNAL_SERVER_ERROR`, distinct from the 400 used for
|
|
31
|
+
* input (`query`/`body`/`params`) validation failures.
|
|
32
|
+
*
|
|
33
|
+
* `Response`-instance returns (and `undefined`/`null` → 204) are NOT
|
|
34
|
+
* validated. This is a plain optional field — runtime validation only; the
|
|
35
|
+
* handler return type is NOT statically inferred from `response` (YAGNI).
|
|
36
|
+
*/
|
|
37
|
+
response?: z.ZodType;
|
|
38
|
+
/**
|
|
39
|
+
* Opt out of CSRF enforcement for this route. Use for endpoints that
|
|
40
|
+
* legitimately receive third-party POSTs (Stripe webhooks, GitHub
|
|
41
|
+
* webhooks, OAuth callbacks). Defaults to enforced per `config.security.csrf`.
|
|
42
|
+
*
|
|
43
|
+
* Setting `csrf: false` only disables the per-route check — it does NOT
|
|
44
|
+
* disable the global mode setting for other routes.
|
|
45
|
+
*/
|
|
46
|
+
csrf?: false;
|
|
47
|
+
/**
|
|
48
|
+
* Who may perform this operation (ADR 0001).
|
|
49
|
+
*
|
|
50
|
+
* Evaluated by the Node executor, the Web executor AND `callProcedure`, from
|
|
51
|
+
* one implementation — which is what makes "the same route gives the same
|
|
52
|
+
* access decision on every transport" a property of the code. Before this
|
|
53
|
+
* existed, a route's access rules applied over HTTP and applied nowhere
|
|
54
|
+
* in-process, and in-process is the path the desktop and terminal targets are
|
|
55
|
+
* built on.
|
|
56
|
+
*
|
|
57
|
+
* `'public'` is a declaration and not a default. Absence currently means "not
|
|
58
|
+
* declared" and is NOT reinterpreted as denial at runtime: flipping that would
|
|
59
|
+
* break every existing route in every consumer at once. The ADR routes it
|
|
60
|
+
* through a build-time gate and a migration instead.
|
|
61
|
+
*/
|
|
62
|
+
policy?: RoutePolicy<z.infer<TQuery>, z.infer<TBody>, z.infer<TParams>>;
|
|
63
|
+
handler: (ctx: {
|
|
64
|
+
query: z.infer<TQuery>;
|
|
65
|
+
body: z.infer<TBody>;
|
|
66
|
+
params: z.infer<TParams>;
|
|
67
|
+
request: Request;
|
|
68
|
+
ctx: TCtx;
|
|
69
|
+
}) => TResponse | Promise<TResponse>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type { RouteConfig as R };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who is asking. Supplied by the caller through the run-context, never read from
|
|
3
|
+
* a transport.
|
|
4
|
+
*
|
|
5
|
+
* A cookie is one way to establish this and a terminal has no cookies, so the
|
|
6
|
+
* contract takes the resolved subject rather than the mechanism that resolved it
|
|
7
|
+
* (ADR 0001 section Decision, point 4).
|
|
8
|
+
*/
|
|
9
|
+
interface RouteSubject {
|
|
10
|
+
id: string;
|
|
11
|
+
[claim: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
/** The answer a policy gives. A denial carries its reason so the caller can say it. */
|
|
14
|
+
type AccessDecision = {
|
|
15
|
+
allowed: true;
|
|
16
|
+
} | {
|
|
17
|
+
allowed: false;
|
|
18
|
+
reason: string;
|
|
19
|
+
};
|
|
20
|
+
/** What a policy is handed. Deliberately transport-free: no headers, no cookies, no response. */
|
|
21
|
+
interface RoutePolicyInput<TQuery = unknown, TBody = unknown, TParams = unknown> {
|
|
22
|
+
subject: RouteSubject | null;
|
|
23
|
+
query: TQuery;
|
|
24
|
+
body: TBody;
|
|
25
|
+
params: TParams;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A route's access policy.
|
|
29
|
+
*
|
|
30
|
+
* `'public'` is a declaration, not a default. The point of writing it is that
|
|
31
|
+
* openness becomes greppable — today a route that enforces nothing looks exactly
|
|
32
|
+
* like a route nobody thought about (ADR 0001 section Consequences, Risks).
|
|
33
|
+
*/
|
|
34
|
+
type RoutePolicy<TQuery = unknown, TBody = unknown, TParams = unknown> = 'public' | ((input: RoutePolicyInput<TQuery, TBody, TParams>) => boolean | AccessDecision | Promise<boolean | AccessDecision>);
|
|
35
|
+
/** Narrow a run-context to the subject it carries, if it carries one. */
|
|
36
|
+
declare function subjectFromContext(context: unknown): RouteSubject | null;
|
|
37
|
+
/**
|
|
38
|
+
* Evaluate a route policy. THE single implementation - the HTTP executors and
|
|
39
|
+
* the in-process caller all reach this function, which is what makes "the same
|
|
40
|
+
* route gives the same access decision on every transport" a property of the code
|
|
41
|
+
* rather than a claim in a document.
|
|
42
|
+
*
|
|
43
|
+
* The shape mirrors `validateRouteInput`, which is already described in-source as
|
|
44
|
+
* "the SINGLE shared pipeline ... One pipeline, no drift". Access control had no
|
|
45
|
+
* such pipeline; that absence is what ADR 0001 exists to close.
|
|
46
|
+
*
|
|
47
|
+
* An undeclared policy returns `allowed` and is NOT the same thing as `'public'`.
|
|
48
|
+
* Absence is refused where an application DECLARES its routes — the file-system
|
|
49
|
+
* scanner fails the build naming the file (`MissingRoutePolicyError`) — and not
|
|
50
|
+
* here, where the argument may be a `RouteConfig` a caller built in memory and
|
|
51
|
+
* never scanned. Reinterpreting absence as denial at this point would break
|
|
52
|
+
* every direct caller of the executors at once, which the ADR routes through the
|
|
53
|
+
* build gate and a migration instead.
|
|
54
|
+
*/
|
|
55
|
+
declare function evaluateRoutePolicy(policy: RoutePolicy | undefined, input: RoutePolicyInput): Promise<AccessDecision>;
|
|
56
|
+
/**
|
|
57
|
+
* The authorization primitive the framework did not have.
|
|
58
|
+
*
|
|
59
|
+
* `requireAuth` answers "is anyone there". Every action then answered "may THIS
|
|
60
|
+
* subject touch THIS record" on its own, which is one implementation per action
|
|
61
|
+
* and one place per action to get it wrong. This is that answer, once.
|
|
62
|
+
*/
|
|
63
|
+
declare function requireOwner(subject: RouteSubject | null, ownerId: string | null | undefined): AccessDecision;
|
|
64
|
+
|
|
65
|
+
export { type AccessDecision as A, type RoutePolicy as R, type RouteSubject as a, type RoutePolicyInput as b, evaluateRoutePolicy as e, requireOwner as r, subjectFromContext as s };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Restore the scroll offset of elements the application marked as scrollers (#421).
|
|
3
|
+
*
|
|
4
|
+
* Mounted beside react-router's `<ScrollRestoration>`, not instead of it: that one owns the
|
|
5
|
+
* DOCUMENT, correctly and with its own timing, and this one owns the elements it cannot see. An app
|
|
6
|
+
* whose document scrolls keeps exactly the behaviour it had.
|
|
7
|
+
*
|
|
8
|
+
* ## What an application does
|
|
9
|
+
*
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <main data-theo-scroll="main" className="overflow-y-auto">…</main>
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* The attribute's VALUE is the id, so two scrollers on one page stay distinguishable.
|
|
15
|
+
*
|
|
16
|
+
* ## Why every decision is elsewhere
|
|
17
|
+
*
|
|
18
|
+
* This component queries, sequences, and nothing else — `scroll-restoration.ts` decides which key
|
|
19
|
+
* an offset lands under, when it is read back, and what happens to an element that is gone on
|
|
20
|
+
* return. That split is because this repository sets up no DOM test environment, so the part that
|
|
21
|
+
* cannot be covered by a test is deliberately the part with no branches in it.
|
|
22
|
+
*
|
|
23
|
+
* `useLayoutEffect` and not `useEffect`: the offset must be applied before the browser paints, or
|
|
24
|
+
* the user sees the top of the list and then a jump.
|
|
25
|
+
*/
|
|
26
|
+
declare function ElementScrollRestoration(): null;
|
|
27
|
+
|
|
28
|
+
export { ElementScrollRestoration };
|