@lovrozagar/honey 0.6.5 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (674) hide show
  1. package/README.md +514 -192
  2. package/bin/honey.js +25 -0
  3. package/dist/accepts.d.ts +7 -1
  4. package/dist/accepts.d.ts.map +1 -1
  5. package/dist/accepts.js +126 -0
  6. package/dist/accepts.js.map +1 -0
  7. package/dist/body-kind.d.ts +26 -0
  8. package/dist/body-kind.d.ts.map +1 -0
  9. package/dist/body-kind.js +70 -0
  10. package/dist/body-kind.js.map +1 -0
  11. package/dist/body-limit.d.ts +5 -1
  12. package/dist/body-limit.d.ts.map +1 -1
  13. package/dist/body-limit.js +126 -0
  14. package/dist/body-limit.js.map +1 -0
  15. package/dist/build/index.d.ts +19 -3
  16. package/dist/build/index.d.ts.map +1 -1
  17. package/dist/build/index.js +173 -0
  18. package/dist/build/index.js.map +1 -0
  19. package/dist/cli-args.d.ts +33 -0
  20. package/dist/cli-args.d.ts.map +1 -0
  21. package/dist/cli-args.js +102 -0
  22. package/dist/cli-args.js.map +1 -0
  23. package/dist/cli-go/auth.go +26 -0
  24. package/dist/cli-go/config.go +192 -0
  25. package/dist/cli-go/errors.go +75 -0
  26. package/dist/cli-go/multipart.go +79 -0
  27. package/dist/cli-go/output.go +163 -0
  28. package/dist/cli-go/root.go +30 -0
  29. package/dist/cli-go/stream.go +88 -0
  30. package/dist/cli-go/version.go +5 -0
  31. package/dist/cli.d.ts +1 -1
  32. package/dist/cli.js +209 -0
  33. package/dist/cli.js.map +1 -0
  34. package/dist/client/defaults.js +48 -0
  35. package/dist/client/defaults.js.map +1 -0
  36. package/dist/client/error.d.ts +35 -0
  37. package/dist/client/error.d.ts.map +1 -1
  38. package/dist/client/error.js +71 -0
  39. package/dist/client/error.js.map +1 -0
  40. package/dist/client/http.d.ts +67 -8
  41. package/dist/client/http.d.ts.map +1 -1
  42. package/dist/client/http.js +571 -0
  43. package/dist/client/http.js.map +1 -0
  44. package/dist/client/index.d.ts +3 -2
  45. package/dist/client/index.d.ts.map +1 -1
  46. package/dist/client/index.js +95 -0
  47. package/dist/client/index.js.map +1 -0
  48. package/dist/client/path.d.ts +26 -0
  49. package/dist/client/path.d.ts.map +1 -0
  50. package/dist/client/path.js +163 -0
  51. package/dist/client/path.js.map +1 -0
  52. package/dist/client/redirect-policy.d.ts +12 -0
  53. package/dist/client/redirect-policy.d.ts.map +1 -0
  54. package/dist/client/redirect-policy.js +20 -0
  55. package/dist/client/redirect-policy.js.map +1 -0
  56. package/dist/client/sdk.d.ts +36 -0
  57. package/dist/client/sdk.d.ts.map +1 -1
  58. package/dist/client/sdk.js +292 -0
  59. package/dist/client/sdk.js.map +1 -0
  60. package/dist/client/sse.d.ts +13 -0
  61. package/dist/client/sse.d.ts.map +1 -1
  62. package/dist/client/sse.js +129 -0
  63. package/dist/client/sse.js.map +1 -0
  64. package/dist/client/types.js +2 -0
  65. package/dist/client/types.js.map +1 -0
  66. package/dist/client/ws.d.ts +9 -2
  67. package/dist/client/ws.d.ts.map +1 -1
  68. package/dist/client/ws.js +112 -0
  69. package/dist/client/ws.js.map +1 -0
  70. package/dist/client-go/errors.go +236 -0
  71. package/dist/client-go/export.go +44 -0
  72. package/dist/client-go/invalidation.go +371 -0
  73. package/dist/client-go/nullable.go +62 -0
  74. package/dist/client-go/realtime.go +354 -0
  75. package/dist/client-go/result.go +20 -0
  76. package/dist/client-go/runtime.go +975 -0
  77. package/dist/client-go/sse.go +159 -0
  78. package/dist/client-go/transport.go +495 -0
  79. package/dist/client-go/ws.go +128 -0
  80. package/dist/client-info.d.ts +36 -0
  81. package/dist/client-info.d.ts.map +1 -0
  82. package/dist/client-info.js +53 -0
  83. package/dist/client-info.js.map +1 -0
  84. package/dist/client-mcp/README.md +10 -0
  85. package/dist/client-mcp/server.ts +221 -0
  86. package/dist/client-mcp/types.ts +19 -0
  87. package/dist/client-python/_errors.py +115 -0
  88. package/dist/client-python/_invalidation.py +494 -0
  89. package/dist/client-python/_realtime.py +179 -0
  90. package/dist/client-python/_runtime.py +860 -0
  91. package/dist/client-python/_sse.py +155 -0
  92. package/dist/client-python/_transport.py +366 -0
  93. package/dist/client-python/_ws.py +159 -0
  94. package/dist/client-python/py.typed +0 -0
  95. package/dist/client-rust/errors.rs +220 -0
  96. package/dist/client-rust/invalidation.rs +412 -0
  97. package/dist/client-rust/invalidation_sync.rs +93 -0
  98. package/dist/client-rust/nullable.rs +17 -0
  99. package/dist/client-rust/realtime.rs +702 -0
  100. package/dist/client-rust/result.rs +22 -0
  101. package/dist/client-rust/runtime.rs +980 -0
  102. package/dist/client-rust/runtime_sync.rs +484 -0
  103. package/dist/client-rust/sse.rs +188 -0
  104. package/dist/client-rust/ws.rs +167 -0
  105. package/dist/codegen-go-cli.d.ts.map +1 -1
  106. package/dist/codegen-go-cli.js +964 -0
  107. package/dist/codegen-go-cli.js.map +1 -0
  108. package/dist/codegen-go.d.ts +15 -3
  109. package/dist/codegen-go.d.ts.map +1 -1
  110. package/dist/codegen-go.js +872 -0
  111. package/dist/codegen-go.js.map +1 -0
  112. package/dist/codegen-hash.js +9 -0
  113. package/dist/codegen-hash.js.map +1 -0
  114. package/dist/codegen-ir.d.ts +69 -2
  115. package/dist/codegen-ir.d.ts.map +1 -1
  116. package/dist/codegen-ir.js +661 -0
  117. package/dist/codegen-ir.js.map +1 -0
  118. package/dist/codegen-lang.d.ts +65 -0
  119. package/dist/codegen-lang.d.ts.map +1 -0
  120. package/dist/codegen-lang.js +526 -0
  121. package/dist/codegen-lang.js.map +1 -0
  122. package/dist/codegen-load.d.ts +31 -0
  123. package/dist/codegen-load.d.ts.map +1 -0
  124. package/dist/codegen-load.js +138 -0
  125. package/dist/codegen-load.js.map +1 -0
  126. package/dist/codegen-loaders.js +55 -0
  127. package/dist/codegen-loaders.js.map +1 -0
  128. package/dist/codegen-mcp.d.ts.map +1 -1
  129. package/dist/codegen-mcp.js +368 -0
  130. package/dist/codegen-mcp.js.map +1 -0
  131. package/dist/codegen-python.d.ts +13 -1
  132. package/dist/codegen-python.d.ts.map +1 -1
  133. package/dist/codegen-python.js +828 -0
  134. package/dist/codegen-python.js.map +1 -0
  135. package/dist/codegen-route-tree-intern.d.ts +15 -5
  136. package/dist/codegen-route-tree-intern.d.ts.map +1 -1
  137. package/dist/codegen-route-tree-intern.js +221 -0
  138. package/dist/codegen-route-tree-intern.js.map +1 -0
  139. package/dist/codegen-rust.d.ts.map +1 -1
  140. package/dist/codegen-rust.js +1293 -0
  141. package/dist/codegen-rust.js.map +1 -0
  142. package/dist/codegen-sanitize.d.ts.map +1 -1
  143. package/dist/codegen-sanitize.js +56 -0
  144. package/dist/codegen-sanitize.js.map +1 -0
  145. package/dist/codegen-schema-naming.d.ts +5 -0
  146. package/dist/codegen-schema-naming.d.ts.map +1 -1
  147. package/dist/codegen-schema-naming.js +150 -0
  148. package/dist/codegen-schema-naming.js.map +1 -0
  149. package/dist/codegen-sdk-model.d.ts +71 -0
  150. package/dist/codegen-sdk-model.d.ts.map +1 -0
  151. package/dist/codegen-sdk-model.js +179 -0
  152. package/dist/codegen-sdk-model.js.map +1 -0
  153. package/dist/codegen.d.ts +41 -23
  154. package/dist/codegen.d.ts.map +1 -1
  155. package/dist/codegen.js +4610 -0
  156. package/dist/codegen.js.map +1 -0
  157. package/dist/context.d.ts +36 -6
  158. package/dist/context.d.ts.map +1 -1
  159. package/dist/context.js +242 -0
  160. package/dist/context.js.map +1 -0
  161. package/dist/cookie-sign.d.ts +16 -2
  162. package/dist/cookie-sign.d.ts.map +1 -1
  163. package/dist/cookie-sign.js +104 -0
  164. package/dist/cookie-sign.js.map +1 -0
  165. package/dist/cookie.d.ts +12 -0
  166. package/dist/cookie.d.ts.map +1 -1
  167. package/dist/cookie.js +138 -0
  168. package/dist/cookie.js.map +1 -0
  169. package/dist/cors.d.ts +1 -0
  170. package/dist/cors.d.ts.map +1 -1
  171. package/dist/cors.js +87 -0
  172. package/dist/cors.js.map +1 -0
  173. package/dist/crypto.d.ts +6 -1
  174. package/dist/crypto.d.ts.map +1 -1
  175. package/dist/crypto.js +38 -0
  176. package/dist/crypto.js.map +1 -0
  177. package/dist/csrf.d.ts +19 -0
  178. package/dist/csrf.d.ts.map +1 -1
  179. package/dist/csrf.js +79 -0
  180. package/dist/csrf.js.map +1 -0
  181. package/dist/curl-logger.d.ts +14 -3
  182. package/dist/curl-logger.d.ts.map +1 -1
  183. package/dist/curl-logger.js +199 -0
  184. package/dist/curl-logger.js.map +1 -0
  185. package/dist/detect-runtime.js +13 -0
  186. package/dist/detect-runtime.js.map +1 -0
  187. package/dist/dict.d.ts +11 -0
  188. package/dist/dict.d.ts.map +1 -0
  189. package/dist/dict.js +13 -0
  190. package/dist/dict.js.map +1 -0
  191. package/dist/error.d.ts.map +1 -1
  192. package/dist/error.js +44 -0
  193. package/dist/error.js.map +1 -0
  194. package/dist/errors.js +39 -0
  195. package/dist/errors.js.map +1 -0
  196. package/dist/etag.d.ts +2 -0
  197. package/dist/etag.d.ts.map +1 -1
  198. package/dist/etag.js +82 -0
  199. package/dist/etag.js.map +1 -0
  200. package/dist/feature-detect.d.ts +25 -0
  201. package/dist/feature-detect.d.ts.map +1 -0
  202. package/dist/feature-detect.js +144 -0
  203. package/dist/feature-detect.js.map +1 -0
  204. package/dist/feature-load.js +36 -0
  205. package/dist/feature-load.js.map +1 -0
  206. package/dist/feature-slots.d.ts +44 -0
  207. package/dist/feature-slots.d.ts.map +1 -0
  208. package/dist/feature-slots.js +48 -0
  209. package/dist/feature-slots.js.map +1 -0
  210. package/dist/fetch-request.d.ts +10 -0
  211. package/dist/fetch-request.d.ts.map +1 -0
  212. package/dist/fetch-request.js +13 -0
  213. package/dist/fetch-request.js.map +1 -0
  214. package/dist/gen-process.d.ts +15 -0
  215. package/dist/gen-process.d.ts.map +1 -0
  216. package/dist/gen-process.js +43 -0
  217. package/dist/gen-process.js.map +1 -0
  218. package/dist/gen-write.d.ts +19 -0
  219. package/dist/gen-write.d.ts.map +1 -0
  220. package/dist/gen-write.js +120 -0
  221. package/dist/gen-write.js.map +1 -0
  222. package/dist/glob.d.ts +21 -0
  223. package/dist/glob.d.ts.map +1 -0
  224. package/dist/glob.js +115 -0
  225. package/dist/glob.js.map +1 -0
  226. package/dist/go-type-emitter.d.ts +18 -4
  227. package/dist/go-type-emitter.d.ts.map +1 -1
  228. package/dist/go-type-emitter.js +594 -0
  229. package/dist/go-type-emitter.js.map +1 -0
  230. package/dist/honey-response.d.ts.map +1 -1
  231. package/dist/honey-response.js +338 -0
  232. package/dist/honey-response.js.map +1 -0
  233. package/dist/i18n-slot.d.ts +8 -0
  234. package/dist/i18n-slot.d.ts.map +1 -1
  235. package/dist/i18n-slot.js +14 -0
  236. package/dist/i18n-slot.js.map +1 -0
  237. package/dist/i18n.d.ts +3 -0
  238. package/dist/i18n.d.ts.map +1 -1
  239. package/dist/i18n.js +250 -0
  240. package/dist/i18n.js.map +1 -0
  241. package/dist/index.d.ts +410 -149
  242. package/dist/index.d.ts.map +1 -1
  243. package/dist/index.js +2324 -0
  244. package/dist/index.js.map +1 -0
  245. package/dist/init.d.ts +3 -5
  246. package/dist/init.d.ts.map +1 -1
  247. package/dist/init.js +140 -0
  248. package/dist/init.js.map +1 -0
  249. package/dist/input.js +4 -0
  250. package/dist/input.js.map +1 -0
  251. package/dist/invalidate-check.d.ts +11 -0
  252. package/dist/invalidate-check.d.ts.map +1 -1
  253. package/dist/invalidate-check.js +163 -0
  254. package/dist/invalidate-check.js.map +1 -0
  255. package/dist/invoke-user.d.ts +35 -0
  256. package/dist/invoke-user.d.ts.map +1 -0
  257. package/dist/invoke-user.js +73 -0
  258. package/dist/invoke-user.js.map +1 -0
  259. package/dist/ip-restrict.d.ts +13 -1
  260. package/dist/ip-restrict.d.ts.map +1 -1
  261. package/dist/ip-restrict.js +60 -0
  262. package/dist/ip-restrict.js.map +1 -0
  263. package/dist/ip.d.ts +39 -0
  264. package/dist/ip.d.ts.map +1 -0
  265. package/dist/ip.js +209 -0
  266. package/dist/ip.js.map +1 -0
  267. package/dist/logger.d.ts.map +1 -1
  268. package/dist/logger.js +113 -0
  269. package/dist/logger.js.map +1 -0
  270. package/dist/media-type.d.ts +30 -0
  271. package/dist/media-type.d.ts.map +1 -0
  272. package/dist/media-type.js +97 -0
  273. package/dist/media-type.js.map +1 -0
  274. package/dist/meta-spec-merge.d.ts +32 -0
  275. package/dist/meta-spec-merge.d.ts.map +1 -0
  276. package/dist/meta-spec-merge.js +68 -0
  277. package/dist/meta-spec-merge.js.map +1 -0
  278. package/dist/meta-spec.d.ts +16 -3
  279. package/dist/meta-spec.d.ts.map +1 -1
  280. package/dist/meta-spec.js +535 -0
  281. package/dist/meta-spec.js.map +1 -0
  282. package/dist/middleware.d.ts +23 -6
  283. package/dist/middleware.d.ts.map +1 -1
  284. package/dist/middleware.js +253 -0
  285. package/dist/middleware.js.map +1 -0
  286. package/dist/node-request.d.ts +57 -12
  287. package/dist/node-request.d.ts.map +1 -1
  288. package/dist/node-request.js +407 -0
  289. package/dist/node-request.js.map +1 -0
  290. package/dist/node.d.ts +20 -3
  291. package/dist/node.d.ts.map +1 -1
  292. package/dist/node.js +396 -0
  293. package/dist/node.js.map +1 -0
  294. package/dist/openapi/collect.d.ts +6 -3
  295. package/dist/openapi/collect.d.ts.map +1 -1
  296. package/dist/openapi/collect.js +30 -0
  297. package/dist/openapi/collect.js.map +1 -0
  298. package/dist/openapi/docs-page.d.ts +42 -0
  299. package/dist/openapi/docs-page.d.ts.map +1 -0
  300. package/dist/openapi/docs-page.js +80 -0
  301. package/dist/openapi/docs-page.js.map +1 -0
  302. package/dist/openapi/document.d.ts +4 -0
  303. package/dist/openapi/document.d.ts.map +1 -1
  304. package/dist/openapi/document.js +785 -0
  305. package/dist/openapi/document.js.map +1 -0
  306. package/dist/openapi/json-schema-slot.d.ts +7 -0
  307. package/dist/openapi/json-schema-slot.d.ts.map +1 -1
  308. package/dist/openapi/json-schema-slot.js +38 -0
  309. package/dist/openapi/json-schema-slot.js.map +1 -0
  310. package/dist/openapi/register.d.ts.map +1 -1
  311. package/dist/openapi/register.js +20 -0
  312. package/dist/openapi/register.js.map +1 -0
  313. package/dist/openapi/scalar.d.ts +3 -1
  314. package/dist/openapi/scalar.d.ts.map +1 -1
  315. package/dist/openapi/scalar.js +21 -0
  316. package/dist/openapi/scalar.js.map +1 -0
  317. package/dist/openapi/serve.d.ts +9 -0
  318. package/dist/openapi/serve.d.ts.map +1 -0
  319. package/dist/openapi/serve.js +36 -0
  320. package/dist/openapi/serve.js.map +1 -0
  321. package/dist/openapi/spec-factory.d.ts +30 -1
  322. package/dist/openapi/spec-factory.d.ts.map +1 -1
  323. package/dist/openapi/spec-factory.js +52 -0
  324. package/dist/openapi/spec-factory.js.map +1 -0
  325. package/dist/openapi/spec.d.ts +14 -5
  326. package/dist/openapi/spec.d.ts.map +1 -1
  327. package/dist/openapi/spec.js +50 -0
  328. package/dist/openapi/spec.js.map +1 -0
  329. package/dist/openapi/swagger.d.ts +3 -1
  330. package/dist/openapi/swagger.d.ts.map +1 -1
  331. package/dist/openapi/swagger.js +22 -0
  332. package/dist/openapi/swagger.js.map +1 -0
  333. package/dist/pattern.d.ts +71 -0
  334. package/dist/pattern.d.ts.map +1 -0
  335. package/dist/pattern.js +239 -0
  336. package/dist/pattern.js.map +1 -0
  337. package/dist/peer.d.ts +17 -0
  338. package/dist/peer.d.ts.map +1 -0
  339. package/dist/peer.js +50 -0
  340. package/dist/peer.js.map +1 -0
  341. package/dist/plugin.d.ts +67 -16
  342. package/dist/plugin.d.ts.map +1 -1
  343. package/dist/plugin.js +649 -0
  344. package/dist/plugin.js.map +1 -0
  345. package/dist/powered-by.d.ts.map +1 -1
  346. package/dist/powered-by.js +11 -0
  347. package/dist/powered-by.js.map +1 -0
  348. package/dist/pretty-json.d.ts.map +1 -1
  349. package/dist/pretty-json.js +47 -0
  350. package/dist/pretty-json.js.map +1 -0
  351. package/dist/producer-stream.d.ts +47 -0
  352. package/dist/producer-stream.d.ts.map +1 -0
  353. package/dist/producer-stream.js +267 -0
  354. package/dist/producer-stream.js.map +1 -0
  355. package/dist/proxy.d.ts +47 -8
  356. package/dist/proxy.d.ts.map +1 -1
  357. package/dist/proxy.js +274 -0
  358. package/dist/proxy.js.map +1 -0
  359. package/dist/python-type-emitter.d.ts +14 -1
  360. package/dist/python-type-emitter.d.ts.map +1 -1
  361. package/dist/python-type-emitter.js +157 -0
  362. package/dist/python-type-emitter.js.map +1 -0
  363. package/dist/realtime/bus.d.ts +17 -1
  364. package/dist/realtime/bus.d.ts.map +1 -1
  365. package/dist/realtime/bus.js +112 -0
  366. package/dist/realtime/bus.js.map +1 -0
  367. package/dist/realtime/register.d.ts +3 -0
  368. package/dist/realtime/register.d.ts.map +1 -0
  369. package/dist/realtime/register.js +10 -0
  370. package/dist/realtime/register.js.map +1 -0
  371. package/dist/realtime/route.d.ts +65 -26
  372. package/dist/realtime/route.d.ts.map +1 -1
  373. package/dist/realtime/route.js +72 -0
  374. package/dist/realtime/route.js.map +1 -0
  375. package/dist/realtime/server.d.ts +43 -0
  376. package/dist/realtime/server.d.ts.map +1 -0
  377. package/dist/realtime/server.js +255 -0
  378. package/dist/realtime/server.js.map +1 -0
  379. package/dist/request-id.d.ts +7 -0
  380. package/dist/request-id.d.ts.map +1 -1
  381. package/dist/request-id.js +16 -0
  382. package/dist/request-id.js.map +1 -0
  383. package/dist/request-limits.d.ts +3 -0
  384. package/dist/request-limits.d.ts.map +1 -0
  385. package/dist/request-limits.js +3 -0
  386. package/dist/request-limits.js.map +1 -0
  387. package/dist/request-path.d.ts +33 -0
  388. package/dist/request-path.d.ts.map +1 -0
  389. package/dist/request-path.js +178 -0
  390. package/dist/request-path.js.map +1 -0
  391. package/dist/request-to-curl.d.ts +7 -1
  392. package/dist/request-to-curl.d.ts.map +1 -1
  393. package/dist/request-to-curl.js +48 -0
  394. package/dist/request-to-curl.js.map +1 -0
  395. package/dist/response.d.ts +25 -2
  396. package/dist/response.d.ts.map +1 -1
  397. package/dist/response.js +313 -0
  398. package/dist/response.js.map +1 -0
  399. package/dist/rust-type-emitter.d.ts +14 -2
  400. package/dist/rust-type-emitter.d.ts.map +1 -1
  401. package/dist/rust-type-emitter.js +463 -0
  402. package/dist/rust-type-emitter.js.map +1 -0
  403. package/dist/secure-headers.d.ts.map +1 -1
  404. package/dist/secure-headers.js +53 -0
  405. package/dist/secure-headers.js.map +1 -0
  406. package/dist/serve-register.d.ts +4 -0
  407. package/dist/serve-register.d.ts.map +1 -1
  408. package/dist/serve-register.js +9 -0
  409. package/dist/serve-register.js.map +1 -0
  410. package/dist/serve-slot.d.ts +3 -0
  411. package/dist/serve-slot.d.ts.map +1 -1
  412. package/dist/serve-slot.js +52 -0
  413. package/dist/serve-slot.js.map +1 -0
  414. package/dist/serve.d.ts +18 -1
  415. package/dist/serve.d.ts.map +1 -1
  416. package/dist/serve.js +157 -0
  417. package/dist/serve.js.map +1 -0
  418. package/dist/server-timing.d.ts.map +1 -1
  419. package/dist/server-timing.js +51 -0
  420. package/dist/server-timing.js.map +1 -0
  421. package/dist/static.d.ts +9 -0
  422. package/dist/static.d.ts.map +1 -1
  423. package/dist/static.js +102 -0
  424. package/dist/static.js.map +1 -0
  425. package/dist/telemetry/otel.d.ts +2 -0
  426. package/dist/telemetry/otel.d.ts.map +1 -1
  427. package/dist/telemetry/otel.js +135 -0
  428. package/dist/telemetry/otel.js.map +1 -0
  429. package/dist/testing.d.ts +12 -0
  430. package/dist/testing.d.ts.map +1 -1
  431. package/dist/testing.js +124 -0
  432. package/dist/testing.js.map +1 -0
  433. package/dist/timeout.d.ts +6 -0
  434. package/dist/timeout.d.ts.map +1 -1
  435. package/dist/timeout.js +36 -0
  436. package/dist/timeout.js.map +1 -0
  437. package/dist/tree.d.ts +133 -36
  438. package/dist/tree.d.ts.map +1 -1
  439. package/dist/tree.js +437 -0
  440. package/dist/tree.js.map +1 -0
  441. package/dist/trust.d.ts +21 -0
  442. package/dist/trust.d.ts.map +1 -0
  443. package/dist/trust.js +100 -0
  444. package/dist/trust.js.map +1 -0
  445. package/dist/ts-type-emitter.d.ts.map +1 -1
  446. package/dist/ts-type-emitter.js +150 -0
  447. package/dist/ts-type-emitter.js.map +1 -0
  448. package/dist/type-emitter.d.ts +16 -2
  449. package/dist/type-emitter.d.ts.map +1 -1
  450. package/dist/type-emitter.js +729 -0
  451. package/dist/type-emitter.js.map +1 -0
  452. package/dist/type-extractor.d.ts +8 -4
  453. package/dist/type-extractor.d.ts.map +1 -1
  454. package/dist/type-extractor.js +776 -0
  455. package/dist/type-extractor.js.map +1 -0
  456. package/dist/types.d.ts +7 -0
  457. package/dist/types.d.ts.map +1 -1
  458. package/dist/types.js +96 -0
  459. package/dist/types.js.map +1 -0
  460. package/dist/validation.d.ts +14 -2
  461. package/dist/validation.d.ts.map +1 -1
  462. package/dist/validation.js +335 -0
  463. package/dist/validation.js.map +1 -0
  464. package/dist/with-headers.d.ts +15 -0
  465. package/dist/with-headers.d.ts.map +1 -0
  466. package/dist/with-headers.js +45 -0
  467. package/dist/with-headers.js.map +1 -0
  468. package/dist/ws/bun.d.ts +11 -5
  469. package/dist/ws/bun.d.ts.map +1 -1
  470. package/dist/ws/bun.js +82 -0
  471. package/dist/ws/bun.js.map +1 -0
  472. package/dist/ws/cloudflare.d.ts +25 -3
  473. package/dist/ws/cloudflare.d.ts.map +1 -1
  474. package/dist/ws/cloudflare.js +117 -0
  475. package/dist/ws/cloudflare.js.map +1 -0
  476. package/dist/ws/deno.d.ts +4 -4
  477. package/dist/ws/deno.d.ts.map +1 -1
  478. package/dist/ws/deno.js +133 -0
  479. package/dist/ws/deno.js.map +1 -0
  480. package/dist/ws/node.d.ts +20 -3
  481. package/dist/ws/node.d.ts.map +1 -1
  482. package/dist/ws/node.js +173 -0
  483. package/dist/ws/node.js.map +1 -0
  484. package/dist/ws/session.d.ts +11 -0
  485. package/dist/ws/session.d.ts.map +1 -0
  486. package/dist/ws/session.js +60 -0
  487. package/dist/ws/session.js.map +1 -0
  488. package/dist/ws/shared.d.ts +65 -0
  489. package/dist/ws/shared.d.ts.map +1 -0
  490. package/dist/ws/shared.js +103 -0
  491. package/dist/ws/shared.js.map +1 -0
  492. package/dist/ws-origin.d.ts +20 -0
  493. package/dist/ws-origin.d.ts.map +1 -0
  494. package/dist/ws-origin.js +41 -0
  495. package/dist/ws-origin.js.map +1 -0
  496. package/dist/yaml.d.ts +15 -1
  497. package/dist/yaml.d.ts.map +1 -1
  498. package/dist/yaml.js +101 -0
  499. package/dist/yaml.js.map +1 -0
  500. package/docs/invalidate-spec.md +13 -1
  501. package/docs/meta-spec.md +36 -12
  502. package/docs/packaging.md +43 -24
  503. package/docs/regression-matrix/README.md +32 -0
  504. package/docs/regression-matrix/review.md +44 -0
  505. package/docs/regression-matrix/ws1-3.md +97 -0
  506. package/docs/regression-matrix/ws4-8.md +107 -0
  507. package/docs/regression-matrix/ws9-12.md +266 -0
  508. package/docs/sdk.md +102 -52
  509. package/package.json +183 -52
  510. package/src/accepts.ts +91 -33
  511. package/src/body-kind.ts +67 -0
  512. package/src/body-limit.ts +102 -87
  513. package/src/build/index.ts +104 -54
  514. package/src/cli-args.ts +123 -0
  515. package/src/cli-go/auth.go +12 -1
  516. package/src/cli-go/config.go +29 -2
  517. package/src/cli-go/errors.go +28 -10
  518. package/src/cli-go/multipart.go +51 -27
  519. package/src/cli-go/output.go +58 -4
  520. package/src/cli-go/root.go +1 -1
  521. package/src/cli-go/version.go +2 -2
  522. package/src/cli.ts +154 -148
  523. package/src/client/error.ts +56 -1
  524. package/src/client/http.ts +479 -256
  525. package/src/client/index.ts +55 -17
  526. package/src/client/path.ts +169 -0
  527. package/src/client/redirect-policy.ts +19 -0
  528. package/src/client/sdk.ts +180 -101
  529. package/src/client/sse.ts +92 -66
  530. package/src/client/ws.ts +67 -31
  531. package/src/client-go/errors.go +41 -12
  532. package/src/client-go/export.go +44 -0
  533. package/src/client-go/invalidation.go +52 -54
  534. package/src/client-go/nullable.go +62 -0
  535. package/src/client-go/realtime.go +147 -110
  536. package/src/client-go/runtime.go +646 -88
  537. package/src/client-go/sse.go +104 -94
  538. package/src/client-go/transport.go +67 -27
  539. package/src/client-go/ws.go +49 -25
  540. package/src/client-info.ts +79 -0
  541. package/src/client-mcp/server.ts +133 -3
  542. package/src/client-python/_invalidation.py +207 -291
  543. package/src/client-python/_realtime.py +63 -40
  544. package/src/client-python/_runtime.py +501 -119
  545. package/src/client-python/_sse.py +130 -74
  546. package/src/client-python/_transport.py +37 -9
  547. package/src/client-python/_ws.py +50 -22
  548. package/src/client-rust/errors.rs +21 -1
  549. package/src/client-rust/invalidation.rs +202 -231
  550. package/src/client-rust/invalidation_sync.rs +16 -229
  551. package/src/client-rust/nullable.rs +17 -0
  552. package/src/client-rust/realtime.rs +217 -217
  553. package/src/client-rust/runtime.rs +602 -177
  554. package/src/client-rust/runtime_sync.rs +169 -145
  555. package/src/client-rust/sse.rs +142 -121
  556. package/src/client-rust/ws.rs +61 -37
  557. package/src/codegen-go-cli.ts +607 -728
  558. package/src/codegen-go.ts +710 -770
  559. package/src/codegen-ir.ts +381 -75
  560. package/src/codegen-lang.ts +517 -0
  561. package/src/codegen-load.ts +150 -0
  562. package/src/codegen-mcp.ts +207 -108
  563. package/src/codegen-python.ts +684 -816
  564. package/src/codegen-route-tree-intern.ts +51 -25
  565. package/src/codegen-rust.ts +946 -1240
  566. package/src/codegen-sanitize.ts +0 -4
  567. package/src/codegen-schema-naming.ts +19 -6
  568. package/src/codegen-sdk-model.ts +228 -0
  569. package/src/codegen.ts +1488 -1140
  570. package/src/context.ts +130 -11
  571. package/src/cookie-sign.ts +85 -16
  572. package/src/cookie.ts +66 -11
  573. package/src/cors.ts +55 -54
  574. package/src/crypto.ts +27 -5
  575. package/src/csrf.ts +57 -25
  576. package/src/curl-logger.ts +155 -114
  577. package/src/dict.ts +12 -0
  578. package/src/error.ts +4 -2
  579. package/src/etag.ts +58 -34
  580. package/src/feature-detect.ts +147 -0
  581. package/src/feature-slots.ts +70 -0
  582. package/src/fetch-request.ts +13 -0
  583. package/src/gen-process.ts +42 -0
  584. package/src/gen-write.ts +127 -0
  585. package/src/glob.ts +109 -0
  586. package/src/go-type-emitter.ts +406 -155
  587. package/src/honey-response.ts +36 -9
  588. package/src/i18n-slot.ts +10 -0
  589. package/src/i18n.ts +98 -23
  590. package/src/index.ts +1893 -1761
  591. package/src/init.ts +40 -28
  592. package/src/invalidate-check.ts +42 -4
  593. package/src/invoke-user.ts +95 -0
  594. package/src/ip-restrict.ts +47 -161
  595. package/src/ip.ts +194 -0
  596. package/src/logger.ts +65 -36
  597. package/src/media-type.ts +104 -0
  598. package/src/meta-spec-merge.ts +80 -0
  599. package/src/meta-spec.ts +0 -0
  600. package/src/middleware.ts +112 -12
  601. package/src/node-request.ts +242 -52
  602. package/src/node.ts +318 -75
  603. package/src/openapi/collect.ts +19 -44
  604. package/src/openapi/docs-page.ts +89 -0
  605. package/src/openapi/document.ts +503 -224
  606. package/src/openapi/json-schema-slot.ts +40 -0
  607. package/src/openapi/register.ts +7 -2
  608. package/src/openapi/scalar.ts +11 -10
  609. package/src/openapi/serve.ts +39 -0
  610. package/src/openapi/spec-factory.ts +50 -1
  611. package/src/openapi/spec.ts +53 -30
  612. package/src/openapi/swagger.ts +12 -11
  613. package/src/pattern.ts +245 -0
  614. package/src/peer.ts +53 -0
  615. package/src/plugin.ts +270 -185
  616. package/src/powered-by.ts +2 -2
  617. package/src/pretty-json.ts +25 -9
  618. package/src/producer-stream.ts +296 -0
  619. package/src/proxy.ts +271 -55
  620. package/src/python-type-emitter.ts +68 -56
  621. package/src/realtime/bus.ts +56 -9
  622. package/src/realtime/register.ts +13 -0
  623. package/src/realtime/route.ts +129 -80
  624. package/src/realtime/server.ts +298 -0
  625. package/src/request-id.ts +13 -3
  626. package/src/request-limits.ts +2 -0
  627. package/src/request-path.ts +165 -0
  628. package/src/request-to-curl.ts +28 -7
  629. package/src/response.ts +143 -103
  630. package/src/rust-type-emitter.ts +265 -170
  631. package/src/secure-headers.ts +8 -4
  632. package/src/serve-register.ts +4 -0
  633. package/src/serve-slot.ts +38 -0
  634. package/src/serve.ts +113 -25
  635. package/src/server-timing.ts +11 -7
  636. package/src/static.ts +85 -32
  637. package/src/telemetry/otel.ts +29 -7
  638. package/src/testing.ts +76 -14
  639. package/src/timeout.ts +15 -7
  640. package/src/tree.ts +405 -333
  641. package/src/trust.ts +117 -0
  642. package/src/ts-type-emitter.ts +18 -12
  643. package/src/type-emitter.ts +227 -89
  644. package/src/type-extractor.ts +562 -424
  645. package/src/types.ts +8 -0
  646. package/src/validation.ts +221 -96
  647. package/src/with-headers.ts +45 -0
  648. package/src/ws/bun.ts +53 -15
  649. package/src/ws/cloudflare.ts +81 -19
  650. package/src/ws/deno.ts +124 -36
  651. package/src/ws/node.ts +173 -79
  652. package/src/ws/session.ts +73 -0
  653. package/src/ws/shared.ts +140 -0
  654. package/src/ws-origin.ts +53 -0
  655. package/src/yaml.ts +45 -22
  656. package/dist/client-mcp/server.d.ts +0 -10
  657. package/dist/client-mcp/server.d.ts.map +0 -1
  658. package/dist/client-mcp/types.d.ts +0 -16
  659. package/dist/client-mcp/types.d.ts.map +0 -1
  660. package/dist/cloudflare/realtime.d.ts +0 -30
  661. package/dist/cloudflare/realtime.d.ts.map +0 -1
  662. package/dist/realtime/buffer.d.ts +0 -18
  663. package/dist/realtime/buffer.d.ts.map +0 -1
  664. package/dist/realtime/longpoll.d.ts +0 -23
  665. package/dist/realtime/longpoll.d.ts.map +0 -1
  666. package/dist/realtime/protocol.d.ts +0 -50
  667. package/dist/realtime/protocol.d.ts.map +0 -1
  668. package/dist/realtime/runtime.d.ts +0 -74
  669. package/dist/realtime/runtime.d.ts.map +0 -1
  670. package/src/cloudflare/realtime.ts +0 -234
  671. package/src/realtime/buffer.ts +0 -101
  672. package/src/realtime/longpoll.ts +0 -148
  673. package/src/realtime/protocol.ts +0 -128
  674. package/src/realtime/runtime.ts +0 -427
package/src/proxy.ts CHANGED
@@ -1,35 +1,159 @@
1
+ import { clientInfo, type HoneyContext } from "./context.ts"
1
2
  import { HoneyError } from "./error.ts"
3
+ import { registerFeature } from "./feature-slots.ts"
4
+ import { searchOfUrl } from "./request-path.ts"
2
5
  import { EK, SK } from "./types.ts"
3
6
 
4
- const BODY_METHODS = new Set(["POST", "PUT", "PATCH"])
7
+ /* Bodies up to this size with a declared length are forwarded as bytes; larger or unsized ones
8
+ stream (uploads). */
9
+ const MAX_BUFFERED_BODY_BYTES = 1024 * 1024
10
+
11
+ /** Default time allowed until the upstream's response headers arrive. */
12
+ const DEFAULT_TIMEOUT_MS = 60_000
13
+
14
+ /**
15
+ * Hop-by-hop headers (RFC 9110 §7.6.1) describe one connection and never cross a proxy, in
16
+ * either direction. `proxy-connection` is the pre-standard spelling clients still send.
17
+ * Headers a `Connection` header names are hop-by-hop too, see `stripHopByHop`.
18
+ */
19
+ const HOP_BY_HOP = [
20
+ "connection",
21
+ "keep-alive",
22
+ "proxy-authenticate",
23
+ "proxy-authorization",
24
+ "proxy-connection",
25
+ "te",
26
+ "trailer",
27
+ "transfer-encoding",
28
+ "upgrade",
29
+ ]
30
+
31
+ /**
32
+ * Client-sent forwarding headers. The app's `trustProxy()` setting has already decided what is
33
+ * true about the client (`clientInfo`); what the client wrote is replaced, never passed on.
34
+ */
35
+ const FORWARDING = ["forwarded", "x-forwarded-for", "x-forwarded-host", "x-forwarded-proto", "x-real-ip"]
36
+
37
+ /** Statuses whose response can never carry a body: rebuilding one with a body throws. */
38
+ const NULL_BODY = new Set([101, 103, 204, 205, 304])
39
+
40
+ function stripHopByHop(headers: Headers, keepUpgrade: boolean): void {
41
+ const named = headers.get("connection")
42
+ if (named !== null) {
43
+ for (const token of named.split(",")) {
44
+ const name = token.trim().toLowerCase()
45
+ if (name === "" || (keepUpgrade && name === "upgrade")) continue
46
+ headers.delete(name)
47
+ }
48
+ }
49
+ for (const name of HOP_BY_HOP) {
50
+ if (keepUpgrade && name === "upgrade") continue
51
+ headers.delete(name)
52
+ }
53
+ }
54
+
55
+ /**
56
+ * A streamed body that the destination never reads (a route without input that was sent `{}`)
57
+ * can keep the call open until it times out: a service binding does not settle while the
58
+ * request stream is unconsumed, and a stream teed by `request.clone()` (a body-logging
59
+ * middleware) has no known length. Bytes have neither problem.
60
+ */
61
+ async function forwardBody(request: Request): Promise<ArrayBuffer | ReadableStream<Uint8Array> | null> {
62
+ if (request.body === null) return null
63
+ const declared = request.headers.get("content-length")
64
+ const length = declared === null ? Number.NaN : Number.parseInt(declared, 10)
65
+ if (Number.isFinite(length) && length <= MAX_BUFFERED_BODY_BYTES) return request.arrayBuffer()
66
+ return request.body
67
+ }
68
+
69
+ /** Network-level codes runtimes put on errors when the upstream cannot be reached or drops. */
70
+ const NETWORK_CODES = /^(?:E[A-Z0-9_]+|Connection\w*|UND_ERR\w*|UNABLE_\w+|CERT_\w+|DEPTH_ZERO\w*)$/
71
+
72
+ /**
73
+ * Whether `error` means the upstream failed (502), as opposed to a bug in user code (500).
74
+ * Fetch reports network failures as a `TypeError` — so does a typo in `destination` — and the
75
+ * runtimes tell them apart differently: undici and Deno say "fetch failed" (with a `cause`),
76
+ * Bun puts a `code` on the error (`ConnectionRefused`, `ENOTFOUND`), Workers say the network
77
+ * connection was lost. An invalid URL is the destination's bug.
78
+ */
79
+ function isUpstreamFailure(error: unknown): boolean {
80
+ if (!(error instanceof Error)) return false
81
+ const code = (error as { code?: unknown }).code
82
+ if (typeof code === "string") return !code.startsWith("ERR_INVALID") && NETWORK_CODES.test(code)
83
+ if (!(error instanceof TypeError)) return false
84
+ const message = error.message
85
+ return /^fetch failed/i.test(message) || /network connection (?:was )?lost/i.test(message)
86
+ }
5
87
 
6
88
  /**
7
89
  * Proxy configuration — controls how requests are forwarded to downstream services.
8
90
  *
9
- * `destination` is the only required field. Framework builds the RequestInit
91
+ * `destination` is the only required field. The framework builds the RequestInit
10
92
  * (headers, body, signal, redirect) and passes it along with the resolved URL.
93
+ *
94
+ * What the framework does, both ways:
95
+ * - Hop-by-hop headers (`Connection`, `Keep-Alive`, `Transfer-Encoding`, `TE`, `Trailer`,
96
+ * `Upgrade`, `Proxy-*`, and every header `Connection` names) are dropped. A WebSocket upgrade
97
+ * keeps `Upgrade: websocket` and `Connection: upgrade`.
98
+ * - The client's `Host` is not sent: the upstream sees the destination's host.
99
+ * `X-Forwarded-For`, `-Proto` and `-Host` describe the client as the app's `trustProxy()`
100
+ * setting decided it (`clientInfo(ctx)`), with one `X-Forwarded-For` entry; whatever
101
+ * forwarding headers the client sent (`Forwarded`, `X-Forwarded-*`, `X-Real-IP`) are dropped.
102
+ * Configure the upstream to trust exactly one hop: this proxy.
103
+ * - `Expect` is dropped (the proxy reads the body itself).
104
+ * - Every method except GET and HEAD forwards its body (DELETE, OPTIONS, PROPFIND…).
105
+ * - The request follows `ctx.signal`: a client disconnect, `timeout()` or shutdown cancels the
106
+ * upstream call and its body.
107
+ * - Redirects are returned to the client, never followed.
11
108
  */
12
109
  export type ProxyConfig<TCtx> = {
13
110
  /**
14
111
  * Where to send the request. Framework doesn't care how — CF service binding,
15
112
  * URL fetch, Durable Object stub, anything that returns a Response.
16
113
  *
114
+ * A network failure the runtime reports (connection refused, DNS, reset) answers 502; any
115
+ * other throw — a bug in this function included — is a 500.
116
+ *
17
117
  * @param ctx - honey context (middleware additions available)
18
- * @param url - path + query string, after rewriteUrl if provided
118
+ * @param url - normalized path + query string, after rewriteUrl if provided
19
119
  * @param init - framework-prepared RequestInit (method, headers, body, signal, redirect)
20
120
  */
21
121
  destination: (ctx: TCtx, url: string, init: RequestInit) => Response | Promise<Response>
22
122
 
123
+ /**
124
+ * Whether the destination's Response body is already decoded, as `fetch()` always returns
125
+ * it (the runtime decompresses gzip, br, zstd and keeps the `Content-Encoding` header).
126
+ * When true (the default), `Content-Encoding` and `Content-Length` are dropped from encoded
127
+ * responses so the headers describe the bytes the client gets. Set false when the
128
+ * destination returns bytes still encoded (a store of compressed objects), so the headers
129
+ * pass through.
130
+ */
131
+ decoded?: boolean
132
+
133
+ /**
134
+ * Add `X-Forwarded-For`, `-Proto` and `-Host`. Default true. Client-sent forwarding headers
135
+ * are dropped either way.
136
+ */
137
+ forwardedHeaders?: boolean
138
+
139
+ /**
140
+ * Most milliseconds the upstream may stay silent while the body streams: the time between
141
+ * two chunks. Omitted or non-positive: no limit (streams such as SSE may idle). A stall
142
+ * aborts the upstream and errors the body. Disabled for WebSocket upgrades.
143
+ */
144
+ idleTimeout?: number | ((ctx: TCtx) => number)
145
+
23
146
  /**
24
147
  * Hook after downstream responds, before returning to client.
25
148
  * Return void to passthrough, or return a new Response to replace.
26
- * Can mutate response.headers in place (zero allocation).
149
+ * The response is the proxy's own copy, so its headers can be set in place.
27
150
  * NOT called for 101 WebSocket upgrades (opaque response).
28
151
  */
29
152
  onResponse?: (ctx: TCtx, response: Response) => void | Response | Promise<void | Response>
30
153
 
31
154
  /**
32
- * Set request headers before forwarding. Called after hop-by-hop headers are stripped.
155
+ * Set request headers before forwarding. Called after hop-by-hop and forwarding headers
156
+ * are handled, so it can override any of them (including `Host` on runtimes that send it).
33
157
  * Static record: entries are set on the headers object.
34
158
  * Function: mutate headers in place.
35
159
  */
@@ -37,18 +161,87 @@ export type ProxyConfig<TCtx> = {
37
161
 
38
162
  /**
39
163
  * Rewrite URL before passing to destination.
40
- * Receives path + query string, returns transformed URL.
164
+ * Receives the normalized path + query string, returns transformed URL.
41
165
  */
42
166
  rewriteUrl?: (url: string, ctx: TCtx) => string
43
167
 
44
168
  /**
45
- * Timeout in milliseconds. Omitted: no abort. `0` or a non-positive
46
- * value: no abort. Accepts a number or a function that receives ctx
47
- * and returns a number. Disabled for WebSocket upgrades.
169
+ * Most milliseconds to wait for the upstream's response headers; a later answer is a 504.
170
+ * Default 60 000. `0` or a non-positive value: no limit. The body is not covered — see
171
+ * `idleTimeout`. Accepts a number or a function of ctx. Disabled for WebSocket upgrades.
48
172
  */
49
173
  timeout?: number | ((ctx: TCtx) => number)
50
174
  }
51
175
 
176
+ function resolveMs<TCtx>(opt: number | ((ctx: TCtx) => number) | undefined, ctx: TCtx, fallback?: number) {
177
+ const ms = typeof opt === "function" ? opt(ctx) : (opt ?? fallback)
178
+ return ms !== undefined && ms > 0 ? ms : undefined
179
+ }
180
+
181
+ function timeoutError(): DOMException {
182
+ return new DOMException("upstream timed out", "TimeoutError")
183
+ }
184
+
185
+ /**
186
+ * The body, erroring (and aborting the upstream through `onIdle`) when the upstream sends
187
+ * nothing for `ms`. The clock runs only while a read is outstanding: a client that reads slowly
188
+ * is backpressure, not an idle upstream.
189
+ */
190
+ function withIdleTimeout(body: ReadableStream<Uint8Array>, ms: number, onIdle: () => void): ReadableStream<Uint8Array> {
191
+ const reader = body.getReader()
192
+ return new ReadableStream<Uint8Array>({
193
+ async cancel(reason) {
194
+ await reader.cancel(reason)
195
+ },
196
+ async pull(controller) {
197
+ let timer: ReturnType<typeof setTimeout> | undefined
198
+ const idle = new Promise<"idle">((resolve) => {
199
+ timer = setTimeout(() => resolve("idle"), ms)
200
+ })
201
+ try {
202
+ const result = await Promise.race([reader.read(), idle])
203
+ if (result === "idle") {
204
+ onIdle()
205
+ const error = timeoutError()
206
+ /* the upstream may not react to the abort (a binding, a stub): drop it either way */
207
+ reader.cancel(error).catch(() => {})
208
+ controller.error(error)
209
+ } else if (result.done) {
210
+ controller.close()
211
+ } else {
212
+ controller.enqueue(result.value)
213
+ }
214
+ } catch (error) {
215
+ controller.error(error)
216
+ } finally {
217
+ clearTimeout(timer)
218
+ }
219
+ },
220
+ })
221
+ }
222
+
223
+ /**
224
+ * The response the client gets: hop-by-hop headers dropped, encoding headers made true for
225
+ * the bytes, in a copy whose headers are mutable (fetch() responses are immutable).
226
+ */
227
+ function clientResponse(response: Response, decoded: boolean, body: ReadableStream<Uint8Array> | null): Response {
228
+ const headers = new Headers(response.headers)
229
+ stripHopByHop(headers, false)
230
+ if (decoded) {
231
+ const encoding = headers.get("content-encoding")
232
+ if (encoding !== null && encoding.trim().toLowerCase() !== "identity") {
233
+ headers.delete("content-encoding")
234
+ /* the length of the encoded bytes, not of the decoded body the client receives */
235
+ headers.delete("content-length")
236
+ }
237
+ }
238
+ return new Response(NULL_BODY.has(response.status) ? null : body, {
239
+ headers,
240
+ status: response.status,
241
+ statusText: response.statusText,
242
+ })
243
+ }
244
+
52
245
  /**
53
246
  * Creates a proxy handler function from config.
54
247
  * Used internally by RouteBuilder.proxy() — not meant for direct consumption.
@@ -58,39 +251,45 @@ export type ProxyConfig<TCtx> = {
58
251
  * which has both fields — we access them via property access on the object.
59
252
  */
60
253
  export function createProxyHandler<TCtx>(config: ProxyConfig<TCtx>): (ctx: TCtx) => Promise<Response> {
61
- const timeoutOpt = config.timeout
254
+ const decoded = config.decoded !== false
255
+ const forwarded = config.forwardedHeaders !== false
62
256
 
63
257
  return async (ctx: TCtx) => {
64
- const timeoutMs = typeof timeoutOpt === "function" ? timeoutOpt(ctx) : timeoutOpt
65
- /* HoneyContext always has path + req — safe at runtime */
66
- const c = ctx as Record<string, unknown>
67
- const request = c["req"] as Request
68
- const method = request.method
69
- const isWs = request.headers.get("upgrade") === "websocket"
70
- const abortMs = !isWs && timeoutMs != null && timeoutMs > 0 ? timeoutMs : undefined
71
-
72
- /* URL resolution — path + query, no new URL() */
73
- const rawUrl = request.url
74
- const protoEnd = rawUrl.indexOf("//")
75
- const pathStart = protoEnd === -1 ? 0 : rawUrl.indexOf("/", protoEnd + 2)
76
- const qIdx = pathStart === -1 ? -1 : rawUrl.indexOf("?", pathStart)
77
- const pathOnly = c["path"] as string
78
- const rawPathQuery = qIdx === -1 ? pathOnly : pathOnly + rawUrl.substring(qIdx)
79
- const url = config.rewriteUrl ? config.rewriteUrl(rawPathQuery, ctx) : rawPathQuery
80
-
81
- /* headers — one copy (original is immutable), strip hop-by-hop */
82
- const headers = new Headers(request.headers)
83
- headers.delete("connection")
84
- headers.delete("keep-alive")
85
- headers.delete("transfer-encoding")
86
- headers.delete("te")
87
- headers.delete("trailer")
88
- headers.delete("proxy-authenticate")
89
- headers.delete("proxy-authorization")
258
+ /* HoneyContext always has path + req + signal — safe at runtime */
259
+ const c = ctx as unknown as HoneyContext
260
+ const request = c.req
261
+ const method = request.method.toUpperCase()
262
+ const isWs = request.headers.get("upgrade")?.trim().toLowerCase() === "websocket"
263
+ const headerMs = isWs ? undefined : resolveMs(config.timeout, ctx, DEFAULT_TIMEOUT_MS)
264
+ const idleMs = isWs ? undefined : resolveMs(config.idleTimeout, ctx)
265
+
266
+ /* URL: the normalized path the router matched, plus the raw query — no new URL() */
267
+ const pathQuery = c.path + searchOfUrl(request.url)
268
+ const url = config.rewriteUrl ? config.rewriteUrl(pathQuery, ctx) : pathQuery
90
269
 
270
+ /* headers — one copy (the original is immutable) */
271
+ const headers = new Headers(request.headers)
272
+ stripHopByHop(headers, isWs)
91
273
  if (isWs) {
274
+ headers.set("upgrade", "websocket")
92
275
  headers.set("connection", "upgrade")
93
276
  }
277
+ headers.delete("host")
278
+ headers.delete("expect")
279
+ for (const name of FORWARDING) headers.delete(name)
280
+ if (forwarded) {
281
+ const client = clientInfo(c)
282
+ if (client.ip !== null) headers.set("x-forwarded-for", client.ip)
283
+ headers.set("x-forwarded-proto", client.protocol)
284
+ if (client.host !== null) headers.set("x-forwarded-host", client.host)
285
+ }
286
+
287
+ /* body: every method that can carry one (fetch refuses a GET or HEAD body) */
288
+ const body = method === "GET" || method === "HEAD" ? null : await forwardBody(request)
289
+ if (body === null || body instanceof ArrayBuffer) {
290
+ /* no body, or bytes whose length fetch() computes itself */
291
+ headers.delete("content-length")
292
+ }
94
293
 
95
294
  if (config.requestHeaders) {
96
295
  if (typeof config.requestHeaders === "function") {
@@ -102,39 +301,48 @@ export function createProxyHandler<TCtx>(config: ProxyConfig<TCtx>): (ctx: TCtx)
102
301
  }
103
302
  }
104
303
 
105
- /* build init — plain object, no new Request() */
106
- const hasBody = BODY_METHODS.has(method)
304
+ /* one controller for the proxy's own timeouts, following the request's signal */
305
+ const upstream = new AbortController()
306
+ const clientSignal = c.signal as AbortSignal | undefined
307
+ const signal = clientSignal ? AbortSignal.any([clientSignal, upstream.signal]) : upstream.signal
308
+
107
309
  const init: RequestInit = {
108
- body: hasBody ? request.body : undefined,
310
+ body: body ?? undefined,
109
311
  headers,
110
312
  method,
111
313
  redirect: "manual",
112
- signal: abortMs == null ? undefined : AbortSignal.timeout(abortMs),
314
+ signal,
113
315
  }
114
316
 
115
317
  /* duplex required for streaming body (Node needs it, CF handles implicitly) */
116
- if (hasBody && request.body) {
318
+ if (body instanceof ReadableStream) {
117
319
  ;(init as Record<string, unknown>)["duplex"] = "half"
118
320
  }
119
321
 
120
322
  /* forward */
323
+ let headerTimedOut = false
324
+ const headerTimer =
325
+ headerMs === undefined
326
+ ? undefined
327
+ : setTimeout(() => {
328
+ headerTimedOut = true
329
+ upstream.abort(timeoutError())
330
+ }, headerMs)
121
331
  let response: Response
122
332
  try {
123
333
  response = await config.destination(ctx, url, init)
124
334
  } catch (error) {
125
- if (error instanceof DOMException && error.name === "TimeoutError") {
126
- throw new HoneyError({
127
- errorKey: EK.gateway_timeout,
128
- status: SK.gateway_timeout,
129
- })
335
+ /* ours, or the destination's own AbortSignal.timeout() */
336
+ if (headerTimedOut || (error instanceof DOMException && error.name === "TimeoutError")) {
337
+ throw new HoneyError({ errorKey: EK.gateway_timeout, status: SK.gateway_timeout })
130
338
  }
131
- if (error instanceof TypeError) {
132
- throw new HoneyError({
133
- errorKey: EK.bad_gateway,
134
- status: SK.bad_gateway,
135
- })
339
+ if (clientSignal?.aborted) throw error
340
+ if (isUpstreamFailure(error)) {
341
+ throw new HoneyError({ errorKey: EK.bad_gateway, status: SK.bad_gateway })
136
342
  }
137
343
  throw error
344
+ } finally {
345
+ clearTimeout(headerTimer)
138
346
  }
139
347
 
140
348
  /* WS 101 — return immediately, opaque */
@@ -142,13 +350,21 @@ export function createProxyHandler<TCtx>(config: ProxyConfig<TCtx>): (ctx: TCtx)
142
350
  return response
143
351
  }
144
352
 
353
+ let responseBody: ReadableStream<Uint8Array> | null = response.body
354
+ if (responseBody !== null && idleMs !== undefined) {
355
+ responseBody = withIdleTimeout(responseBody, idleMs, () => upstream.abort(timeoutError()))
356
+ }
357
+ let result = clientResponse(response, decoded, responseBody)
358
+
145
359
  /* response hook */
146
360
  if (config.onResponse) {
147
- const replaced = await config.onResponse(ctx, response)
148
- if (replaced) return replaced
361
+ const replaced = await config.onResponse(ctx, result)
362
+ if (replaced) result = replaced
149
363
  }
150
364
 
151
- /* stream through — zero wrapping */
152
- return response
365
+ return result
153
366
  }
154
367
  }
368
+
369
+ /* `import "@lovrozagar/honey/proxy"` is what makes `.proxy()` available; the core never imports this module */
370
+ registerFeature("proxy", { createProxyHandler })
@@ -1,50 +1,23 @@
1
1
  import { schemaToIR } from "./codegen-ir.ts"
2
2
  import type { IRField, IRSchema } from "./codegen-ir.ts"
3
+ import { cmpCodeUnit, pyClassName, pyString } from "./codegen-lang.ts"
3
4
 
4
- const PY_RESERVED = new Set([
5
- "False",
6
- "None",
7
- "True",
8
- "and",
9
- "as",
10
- "assert",
11
- "async",
12
- "await",
13
- "break",
14
- "class",
15
- "continue",
16
- "def",
17
- "del",
18
- "elif",
19
- "else",
20
- "except",
21
- "finally",
22
- "for",
23
- "from",
24
- "global",
25
- "if",
26
- "import",
27
- "in",
28
- "is",
29
- "lambda",
30
- "nonlocal",
31
- "not",
32
- "or",
33
- "pass",
34
- "raise",
35
- "return",
36
- "try",
37
- "while",
38
- "with",
39
- "yield",
40
- ])
41
-
42
- /** Suffix reserved words with `_` per PEP 8 convention. */
5
+ /** Attribute-safe name that keeps the original casing (`apiKeys` stays `apiKeys`).
6
+ * Non-identifier characters become `_`; keywords get a `_` suffix per PEP 8. */
43
7
  export function pyIdent(name: string): string {
44
- if (PY_RESERVED.has(name)) return `${name}_`
45
- return name.replace(/[^a-zA-Z0-9_]/g, "_")
8
+ let out = name.replace(/[^A-Za-z0-9_]/g, "_")
9
+ if (out === "") out = "value"
10
+ if (/^[0-9]/.test(out)) out = `_${out}`
11
+ if (PY_HARD_KEYWORDS.has(out) || out === "self") return `${out}_`
12
+ return out
46
13
  }
47
14
 
15
+ const PY_HARD_KEYWORDS = new Set(
16
+ "False None True and as assert async await break class continue def del elif else except finally for from global if import in is lambda nonlocal not or pass raise return try while with yield".split(
17
+ " ",
18
+ ),
19
+ )
20
+
48
21
  function primitiveToPy(t: string): string {
49
22
  if (t === "string") return "str"
50
23
  if (t === "integer") return "int"
@@ -55,34 +28,52 @@ function primitiveToPy(t: string): string {
55
28
  }
56
29
 
57
30
  function formatConstPy(v: string | number | boolean): string {
58
- if (typeof v === "string") return JSON.stringify(v)
31
+ if (typeof v === "string") return pyString(v)
59
32
  if (typeof v === "boolean") return v ? "True" : "False"
60
33
  return String(v)
61
34
  }
62
35
 
63
36
  function emitScalarPy(ir: Extract<IRSchema, { kind: "scalar" }>): string {
64
37
  if (ir.enum) {
65
- const members = ir.enum.map((v) => (typeof v === "string" ? JSON.stringify(v) : String(v))).join(", ")
38
+ const members = ir.enum
39
+ .filter((v) => v !== null)
40
+ .map((v) => (typeof v === "string" ? pyString(v) : typeof v === "boolean" ? (v ? "True" : "False") : String(v)))
41
+ .join(", ")
42
+ if (members === "") return primitiveToPy(ir.type)
66
43
  return `Literal[${members}]`
67
44
  }
68
45
  return primitiveToPy(ir.type)
69
46
  }
70
47
 
71
- function emitObjectPy(ir: Extract<IRSchema, { kind: "object" }>, depth: number): string {
48
+ /** Module-level naming for generated types. Without it, nested objects render inline. */
49
+ export type PyTypeNames = {
50
+ ref: (schemaName: string) => string
51
+ /** Hoists an object schema to a named TypedDict and returns its name. */
52
+ hoist: (key: string, base: string, schema: Extract<IRSchema, { kind: "object" }>) => string
53
+ }
54
+
55
+ type Ctx = { names?: PyTypeNames; parent: string; field: string }
56
+
57
+ function emitObjectPy(ir: Extract<IRSchema, { kind: "object" }>, depth: number, ctx: Ctx): string {
72
58
  const { fields, additional } = ir
73
59
 
74
60
  if (fields.length === 0) {
75
61
  if (additional) {
76
- return `dict[str, ${irToPython(additional, depth + 1)}]`
62
+ return `dict[str, ${irToPythonCtx(additional, depth + 1, { ...ctx, field: `${ctx.field}Value` })}]`
77
63
  }
78
64
  return "dict[str, Any]"
79
65
  }
80
66
 
81
- const sortedFields = fields.slice().sort((a, b) => a.name.localeCompare(b.name))
67
+ if (ctx.names) {
68
+ const base = ctx.field === "" ? ctx.parent : `${ctx.parent}${pyClassName(ctx.field)}`
69
+ return ctx.names.hoist(`${ctx.parent}.${ctx.field}`, base, ir)
70
+ }
71
+
72
+ const sortedFields = fields.slice().sort((a, b) => cmpCodeUnit(a.name, b.name))
82
73
  const entries = sortedFields.map((f) => {
83
- const pyType = irToPython(f.schema, depth + 1)
74
+ const pyType = irToPythonCtx(f.schema, depth + 1, ctx)
84
75
  const annotation = f.required ? pyType : `NotRequired[${pyType}]`
85
- return `${JSON.stringify(f.name)}: ${annotation}`
76
+ return `${pyString(f.name)}: ${annotation}`
86
77
  })
87
78
 
88
79
  return `TypedDict("X", {${entries.join(", ")}})`
@@ -93,6 +84,15 @@ function emitObjectPy(ir: Extract<IRSchema, { kind: "object" }>, depth: number):
93
84
  * depth > 8 returns `Any` (mirrors TS `unknown` fallback).
94
85
  */
95
86
  export function irToPython(ir: IRSchema, depth = 0): string {
87
+ return irToPythonCtx(ir, depth, { field: "Value", parent: "Model" })
88
+ }
89
+
90
+ /** `irToPython` with module naming: refs map to emitted class names, nested objects are hoisted. */
91
+ export function irToPythonNamed(ir: IRSchema, names: PyTypeNames, parent: string, field: string): string {
92
+ return irToPythonCtx(ir, 0, { field, names, parent })
93
+ }
94
+
95
+ function irToPythonCtx(ir: IRSchema, depth: number, ctx: Ctx): string {
96
96
  if (depth > 8) return "Any"
97
97
 
98
98
  switch (ir.kind) {
@@ -103,13 +103,15 @@ export function irToPython(ir: IRSchema, depth = 0): string {
103
103
  return `Literal[${formatConstPy(ir.value)}]`
104
104
 
105
105
  case "object":
106
- return emitObjectPy(ir, depth)
106
+ return emitObjectPy(ir, depth, ctx)
107
107
 
108
108
  case "array":
109
- return `list[${irToPython(ir.items, depth + 1)}]`
109
+ return `list[${irToPythonCtx(ir.items, depth + 1, { ...ctx, field: `${ctx.field}Item` })}]`
110
110
 
111
111
  case "tuple":
112
- return `tuple[${ir.items.map((i) => irToPython(i, depth + 1)).join(", ")}]`
112
+ return ir.items.length === 0
113
+ ? "tuple[()]"
114
+ : `tuple[${ir.items.map((i, n) => irToPythonCtx(i, depth + 1, { ...ctx, field: `${ctx.field}${n}` })).join(", ")}]`
113
115
 
114
116
  case "union": {
115
117
  /* union-of-const → single Literal[...] to match mixed enum behavior */
@@ -119,12 +121,17 @@ export function irToPython(ir: IRSchema, depth = 0): string {
119
121
  .join(", ")
120
122
  return `Literal[${members}]`
121
123
  }
122
- return ir.variants.map((v) => irToPython(v, depth + 1)).join(" | ")
124
+ return ir.variants
125
+ .map((v, n) => irToPythonCtx(v, depth + 1, { ...ctx, field: `${ctx.field}Variant${n}` }))
126
+ .join(" | ")
123
127
  }
124
128
 
125
129
  case "allOf": {
126
130
  const allObjects = ir.parts.every((p) => p.kind === "object")
127
- if (!allObjects) return "Any"
131
+ if (!allObjects) {
132
+ if (ctx.names && ir.parts.length === 1) return irToPythonCtx(ir.parts[0], depth + 1, ctx)
133
+ return "Any"
134
+ }
128
135
  /* later parts overwrite earlier (mirrors Object.assign semantics in old jsonSchemaToPy) */
129
136
  const mergedMap = new Map<string, IRField>()
130
137
  const requiredSet = new Set<string>()
@@ -140,14 +147,14 @@ export function irToPython(ir: IRSchema, depth = 0): string {
140
147
  mf.required = requiredSet.has(f.name)
141
148
  return mf
142
149
  })
143
- return emitObjectPy({ fields: mergedFields, kind: "object" }, depth)
150
+ return emitObjectPy({ fields: mergedFields, kind: "object" }, depth, ctx)
144
151
  }
145
152
 
146
153
  case "ref":
147
- return ir.name
154
+ return ctx.names ? ctx.names.ref(ir.name) : ir.name
148
155
 
149
156
  case "nullable":
150
- return `${irToPython(ir.inner, depth + 1)} | None`
157
+ return `${irToPythonCtx(ir.inner, depth + 1, ctx)} | None`
151
158
 
152
159
  /*
153
160
  * binary kind arises from {type:"string", format:"binary"}.
@@ -167,3 +174,8 @@ export function jsonSchemaToPy(schema: Record<string, unknown> | undefined, dept
167
174
  if (!schema || depth > 8) return "Any"
168
175
  return irToPython(schemaToIR(schema), depth)
169
176
  }
177
+
178
+ /** A key usable as a TypedDict class-syntax attribute (else the functional syntax is used). */
179
+ export function isPyAttributeKey(key: string): boolean {
180
+ return /^[A-Za-z_][A-Za-z0-9_]*$/.test(key) && !PY_HARD_KEYWORDS.has(key) && !key.startsWith("__")
181
+ }