@lovrozagar/honey 0.6.8 → 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 +408 -148
  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 +1887 -1756
  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 +253 -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
@@ -0,0 +1,2 @@
1
+ /** Bun's default request body cap; honey's Node adapter applies the same one. */
2
+ export const DEFAULT_MAX_REQUEST_BODY = 128 * 1024 * 1024
@@ -0,0 +1,165 @@
1
+ const PLAIN = 0
2
+ /** a character the slow path rewrites: a backslash, or one `encodeSegment` percent-encodes */
3
+ const SLOW = 1
4
+ const PERCENT = 2
5
+ const SLASH = 3
6
+
7
+ const CHAR_CLASS = new Uint8Array(128)
8
+ for (let c = 0; c < 128; c++) {
9
+ if (c <= 0x20 || c === 0x7f || c === 92 || needsEncoding(c)) CHAR_CLASS[c] = SLOW
10
+ }
11
+ CHAR_CLASS[37] = PERCENT
12
+ CHAR_CLASS[47] = SLASH
13
+
14
+ /**
15
+ * The request path the router, scopes, `ctx.path`, `proxy()` and `staticFiles()` all see.
16
+ *
17
+ * Policy, applied the same way on every runtime:
18
+ * - empty segments collapse: `//admin///users` is `/admin/users`
19
+ * - dot segments resolve: `.` is dropped and `..` removes the segment before it, never past
20
+ * the root; `%2e` counts as a dot, as it does for WHATWG URL parsing
21
+ * - a trailing slash is kept (the router and `trailingSlash()` decide what it means)
22
+ * - a backslash is a `/`, and characters a URL path cannot hold raw (space, controls, `"`, `<`,
23
+ * `>`, `` ` ``, `{`, `}`, non-ASCII) are percent-encoded as UTF-8 — what WHATWG URL parsing
24
+ * does, so a runtime that hands the app the raw target (Deno) and one that parses it first
25
+ * (Bun, Node) route the same bytes the same way
26
+ * - an encoded `/` or `\` (`%2F`, `%5C`) is rejected unless `encodedSlashes` is `"allow"`: a
27
+ * segment that decodes to a path separator means one thing to this router and another to
28
+ * whatever the path is forwarded to (a proxy upstream, a file system). Allowed, it stays
29
+ * encoded in the path and decodes into a param value.
30
+ * - everything else stays percent-encoded; params are decoded when they are extracted
31
+ *
32
+ * Returns `null` for a rejected path; the app answers 400.
33
+ */
34
+ export function normalizePath(raw: string, encodedSlashes: "allow" | "reject" = "reject"): string | null {
35
+ const rejectEncoded = encodedSlashes === "reject"
36
+ let canonical = raw.charCodeAt(0) === 47
37
+ const len = raw.length
38
+ for (let i = 0; i < len; i++) {
39
+ const c = raw.charCodeAt(i)
40
+ const k = c < 128 ? CHAR_CLASS[c] : SLOW
41
+ if (k === PLAIN) continue
42
+ if (k === SLOW) {
43
+ canonical = false
44
+ } else if (k === PERCENT) {
45
+ const hi = raw.charCodeAt(i + 1)
46
+ const lo = raw.charCodeAt(i + 2) | 0x20
47
+ if (hi === 50 /* 2 */) {
48
+ if (lo === 102 /* f */ && rejectEncoded) return null
49
+ if (lo === 101 /* e */) canonical = false
50
+ } else if (hi === 53 /* 5 */ && lo === 99 /* c */ && rejectEncoded) {
51
+ return null
52
+ }
53
+ } else {
54
+ const n = raw.charCodeAt(i + 1)
55
+ if (n === 47 || n === 46 /* . */) canonical = false
56
+ }
57
+ }
58
+ if (canonical) return raw
59
+
60
+ const out: string[] = []
61
+ const segments = raw.replaceAll("\\", "/").split("/")
62
+ let trailing = false
63
+ for (let i = 0; i < segments.length; i++) {
64
+ const seg = segments[i]
65
+ const last = i === segments.length - 1
66
+ if (seg === "") {
67
+ if (last && i > 0) trailing = true
68
+ continue
69
+ }
70
+ const dots = dotSegment(seg)
71
+ if (dots === 0) {
72
+ out.push(encodeSegment(seg))
73
+ continue
74
+ }
75
+ if (dots === 2) out.pop()
76
+ /* `/a/b/..` is `/a/`, like URL resolution */
77
+ if (last) trailing = true
78
+ }
79
+ if (out.length === 0) return "/"
80
+ const path = `/${out.join("/")}`
81
+ return trailing ? `${path}/` : path
82
+ }
83
+
84
+ /**
85
+ * The WHATWG path percent-encode set, plus every non-ASCII code unit. `^` joined the set in
86
+ * 2023; Node and Bun encode it, Deno's parser does not yet, so it is encoded here to keep
87
+ * `ctx.path` the same on all three.
88
+ */
89
+ function needsEncoding(c: number): boolean {
90
+ return (
91
+ c <= 0x20 ||
92
+ c >= 0x7f ||
93
+ c === 34 /* " */ ||
94
+ c === 60 /* < */ ||
95
+ c === 62 /* > */ ||
96
+ c === 94 /* ^ */ ||
97
+ c === 96 /* ` */ ||
98
+ c === 123 /* { */ ||
99
+ c === 125 /* } */
100
+ )
101
+ }
102
+
103
+ /**
104
+ * One path segment as a normalized request path holds it: characters a URL path cannot hold
105
+ * raw become UTF-8 percent escapes, everything else (existing escapes included) is kept.
106
+ * Route patterns encode their static segments with this too, so `app.get("/é")` matches the
107
+ * `/%C3%A9` every runtime hands the app.
108
+ */
109
+ export function encodeSegment(seg: string): string {
110
+ let needs = false
111
+ for (let i = 0; i < seg.length; i++) {
112
+ if (needsEncoding(seg.charCodeAt(i))) {
113
+ needs = true
114
+ break
115
+ }
116
+ }
117
+ if (!needs) return seg
118
+ let out = ""
119
+ for (const ch of seg) {
120
+ const c = ch.charCodeAt(0)
121
+ if (ch.length === 1 && !needsEncoding(c)) {
122
+ out += ch
123
+ continue
124
+ }
125
+ try {
126
+ out += encodeURIComponent(ch)
127
+ } catch {
128
+ /* a lone surrogate: U+FFFD, as URL parsing encodes it */
129
+ out += "%EF%BF%BD"
130
+ }
131
+ }
132
+ return out
133
+ }
134
+
135
+ /** 1 for `.`, 2 for `..` (either may be written `%2e`), 0 for anything else. */
136
+ function dotSegment(seg: string): 0 | 1 | 2 {
137
+ if (seg.length > 6) return 0
138
+ const s = seg.toLowerCase().replaceAll("%2e", ".")
139
+ if (s === ".") return 1
140
+ if (s === "..") return 2
141
+ return 0
142
+ }
143
+
144
+ /** The path of a request URL without `new URL()`: everything from the first `/` after the authority, up to `?` or `#`. */
145
+ export function pathOfUrl(rawUrl: string): string {
146
+ const protoEnd = rawUrl.indexOf("//")
147
+ const pathStart = protoEnd === -1 ? 0 : rawUrl.indexOf("/", protoEnd + 2)
148
+ if (pathStart === -1) return "/"
149
+ for (let i = pathStart; i < rawUrl.length; i++) {
150
+ const c = rawUrl.charCodeAt(i)
151
+ if (c === 63 /* ? */ || c === 35 /* # */) return rawUrl.substring(pathStart, i)
152
+ }
153
+ return rawUrl.substring(pathStart)
154
+ }
155
+
156
+ /** `?query` of a request URL (with the `?`), or `""`. Never includes a fragment. */
157
+ export function searchOfUrl(rawUrl: string): string {
158
+ const protoEnd = rawUrl.indexOf("//")
159
+ const pathStart = protoEnd === -1 ? 0 : rawUrl.indexOf("/", protoEnd + 2)
160
+ if (pathStart === -1) return ""
161
+ const q = rawUrl.indexOf("?", pathStart)
162
+ if (q === -1) return ""
163
+ const hash = rawUrl.indexOf("#", q)
164
+ return hash === -1 ? rawUrl.substring(q) : rawUrl.substring(q, hash)
165
+ }
@@ -2,30 +2,51 @@ export type RequestToCurlOptions = {
2
2
  excludeHeader?: (name: string, value: string) => boolean
3
3
  }
4
4
 
5
- function shellEscape(value: string): string {
6
- return value.replace(/'/g, "'\\''")
5
+ function hasControl(value: string): boolean {
6
+ for (let i = 0; i < value.length; i++) {
7
+ const code = value.charCodeAt(i)
8
+ if (code < 0x20 || code === 0x7f) return true
9
+ }
10
+ return false
11
+ }
12
+
13
+ /**
14
+ * Quote one shell word. Plain values use single quotes; values with control
15
+ * characters (CR, LF, ESC, …) use ANSI-C `$'…'` quoting with `\xHH` escapes, so
16
+ * a pasted command cannot carry a raw newline and a log line cannot be split.
17
+ */
18
+ export function shellQuote(value: string): string {
19
+ if (!hasControl(value)) return `'${value.replace(/'/g, "'\\''")}'`
20
+ let out = "$'"
21
+ for (const ch of value) {
22
+ const code = ch.codePointAt(0) as number
23
+ if (ch === "\\" || ch === "'") out += `\\${ch}`
24
+ else if (code < 0x20 || code === 0x7f) out += `\\x${code.toString(16).padStart(2, "0")}`
25
+ else out += ch
26
+ }
27
+ return `${out}'`
7
28
  }
8
29
 
9
30
  /**
10
- * Convert a native Request into a curl command string.
31
+ * Convert a native Request into a curl command string. Every word is quoted.
11
32
  * The request is cloned so reading the body does not consume the original stream.
12
33
  */
13
34
  export async function requestToCurl(request: Request, options?: RequestToCurlOptions): Promise<string> {
14
35
  const clonedRequest = request.clone()
15
- const parts: string[] = [`curl -X ${clonedRequest.method}`]
36
+ const parts: string[] = ["curl", "-X", shellQuote(clonedRequest.method)]
16
37
 
17
38
  for (const [name, value] of clonedRequest.headers.entries()) {
18
39
  if (options?.excludeHeader?.(name, value) === true) continue
19
- parts.push(`-H '${shellEscape(name)}: ${shellEscape(value)}'`)
40
+ parts.push("-H", shellQuote(`${name}: ${value}`))
20
41
  }
21
42
 
22
43
  const body = await clonedRequest.text()
23
44
 
24
45
  if (body.length > 0) {
25
- parts.push(`--data-raw '${shellEscape(body)}'`)
46
+ parts.push("--data-raw", shellQuote(body))
26
47
  }
27
48
 
28
- parts.push(`'${shellEscape(clonedRequest.url)}'`)
49
+ parts.push(shellQuote(clonedRequest.url))
29
50
 
30
51
  return parts.join(" ")
31
52
  }
package/src/response.ts CHANGED
@@ -2,6 +2,7 @@ import type { CookieOptions } from "./cookie.ts"
2
2
  import { serializeCookie } from "./cookie.ts"
3
3
  import type { HoneyError } from "./error.ts"
4
4
  import { createHoneyResponse, isHoneyResponse } from "./honey-response.ts"
5
+ import { generatorStream, producerStream, tagStream, type ProducerOptions } from "./producer-stream.ts"
5
6
  import type { StatusKey } from "./types.ts"
6
7
  import { statusKeyToCode } from "./types.ts"
7
8
 
@@ -28,9 +29,19 @@ export type SSEOptions = ResponseOptions & {
28
29
  }
29
30
 
30
31
  export type SSEStream = {
32
+ /** End the stream. Idempotent and synchronous: safe in a `finally`. */
31
33
  close(): void
34
+ /** True once the stream ended: closed, or the client went away. */
35
+ readonly closed: boolean
32
36
  lastEventId: string | undefined
37
+ /**
38
+ * Queue an event. Resolves when the client is ready for more. After the stream ended it
39
+ * rejects with an `AbortError`, which ends an awaiting loop; Honey does not report that
40
+ * rejection, and an unawaited one never surfaces as an unhandled rejection.
41
+ */
33
42
  send(event: SSEEvent): Promise<void>
43
+ /** Aborts when the client disconnects, the request times out, or the server shuts down. */
44
+ readonly signal: AbortSignal
34
45
  }
35
46
 
36
47
  export type ErrorFormatter = (error: HoneyError, defaultShape: Record<string, unknown>) => Record<string, unknown>
@@ -55,7 +66,9 @@ function applyPlainOptions(headers: Record<string, string | string[]>, opts?: Re
55
66
  }
56
67
  }
57
68
  if (opts?.cookies) {
58
- const cookies: string[] = []
69
+ /* append, as `Headers.append` does on the native path: a `set-cookie` in `opts.headers` stays */
70
+ const existing = headers["set-cookie"]
71
+ const cookies: string[] = existing === undefined ? [] : Array.isArray(existing) ? [...existing] : [existing]
59
72
  for (const [name, cookieOpts] of Object.entries(opts.cookies)) {
60
73
  cookies.push(serializeCookie(name, cookieOpts))
61
74
  }
@@ -63,6 +76,11 @@ function applyPlainOptions(headers: Record<string, string | string[]>, opts?: Re
63
76
  }
64
77
  }
65
78
 
79
+ /** Statuses whose responses never carry a body (Fetch "null body status"). */
80
+ function isNullBodyStatus(status: number): boolean {
81
+ return status === 101 || status === 103 || status === 204 || status === 205 || status === 304
82
+ }
83
+
66
84
  /* phantom brands — exist only at type level, never assigned at runtime */
67
85
  declare const CONTENT_TYPE: unique symbol
68
86
  declare const STATUS_KEY: unique symbol
@@ -93,6 +111,11 @@ const HTML_HEADERS = { "content-type": "text/html; charset=utf-8" }
93
111
  const CSV_HEADERS = { "content-type": "text/csv; charset=utf-8" }
94
112
  const BINARY_HEADERS = { "content-type": "application/octet-stream" }
95
113
 
114
+ /** Report a stream producer failure where no logger is wired (a bare `HoneyRes`). */
115
+ function reportToConsole(err: unknown): void {
116
+ console.error("honey: stream producer failed", err)
117
+ }
118
+
96
119
  export class HoneyRes {
97
120
  protected readonly _nodeOut: boolean
98
121
 
@@ -100,12 +123,22 @@ export class HoneyRes {
100
123
  this._nodeOut = nodeOut
101
124
  }
102
125
 
126
+ /** Signal and error reporting for streams; the per-request res wires `ctx.signal` and the logger. */
127
+ protected _producer(): ProducerOptions {
128
+ return { report: reportToConsole }
129
+ }
130
+
103
131
  private _known<CT extends string, SK extends string>(
104
- status: number,
132
+ statusCode: number,
105
133
  headers: Record<string, string>,
106
134
  raw: string | Uint8Array,
107
135
  opts?: ResponseOptions,
108
136
  ): TypedResponse<CT, SK> {
137
+ const status = opts?.status ?? statusCode
138
+ if (isNullBodyStatus(status)) {
139
+ /* `new Response(body, { status: 204 })` throws on the native path; fail the same way everywhere */
140
+ throw new TypeError(`A ${status} response cannot have a body; use ctx.res.noContent() or another status`)
141
+ }
109
142
  if (this._nodeOut) {
110
143
  if (!opts?.headers && !opts?.cookies) {
111
144
  return typed(createHoneyResponse({ headers, raw, status }))
@@ -181,129 +214,128 @@ export class HoneyRes {
181
214
  }
182
215
 
183
216
  sse(callback: (stream: SSEStream) => Promise<void>, opts?: SSEOptions): TypedResponse<"text/event-stream", "ok"> {
184
- const { readable, writable } = new TransformStream()
185
- const writer = writable.getWriter()
186
217
  const encoder = new TextEncoder()
187
- let keepaliveTimer: ReturnType<typeof setInterval> | undefined
188
-
189
- let closed = false
190
- const sseStream: SSEStream = {
191
- close() {
192
- if (closed) return
193
- closed = true
194
- if (keepaliveTimer !== undefined) clearInterval(keepaliveTimer)
195
- writer.close()
196
- },
197
- lastEventId: opts?.lastEventId,
198
- send(event) {
199
- if (/[\r\n]/.test(event.event)) {
200
- throw new Error("SSE event name must not contain newlines")
201
- }
202
- if (event.id && /[\r\n]/.test(event.id)) {
203
- throw new Error("SSE id must not contain newlines")
204
- }
205
- const dataStr = typeof event.data === "string" ? event.data : JSON.stringify(event.data)
206
- const dataLines = dataStr
207
- .split(/\r\n|\r|\n/)
208
- .map((line) => `data: ${line}`)
209
- .join("\n")
210
- let msg = `event: ${event.event}\n${dataLines}\n`
211
- if (event.id) msg += `id: ${event.id}\n`
212
- /** SSE retry must be a non-negative integer per spec — skip if invalid */
213
- if (event.retry !== undefined && Number.isFinite(event.retry) && event.retry >= 0) {
214
- msg += `retry: ${Math.floor(event.retry)}\n`
215
- }
216
- msg += "\n"
217
- return writer.write(encoder.encode(msg))
218
- },
219
- }
220
-
221
- if (opts?.defaultRetry !== undefined) {
222
- writer.write(encoder.encode(`retry: ${opts.defaultRetry}\n\n`))
223
- }
224
-
225
- if (opts?.keepalive !== undefined && opts.keepalive > 0) {
226
- keepaliveTimer = setInterval(() => {
227
- writer.write(encoder.encode(": heartbeat\n\n")).catch(() => {
228
- if (keepaliveTimer !== undefined) clearInterval(keepaliveTimer)
229
- })
230
- }, opts.keepalive)
231
- }
232
-
233
- void callback(sseStream).then(
234
- async () => {
235
- if (closed) return
236
- closed = true
237
- if (keepaliveTimer !== undefined) clearInterval(keepaliveTimer)
238
- try {
239
- await writer.close()
240
- } catch {}
241
- },
242
- async () => {
243
- if (closed) return
244
- closed = true
245
- if (keepaliveTimer !== undefined) clearInterval(keepaliveTimer)
246
- try {
247
- await writer.close()
248
- } catch {}
249
- },
250
- )
218
+ const body = producerStream((sink) => {
219
+ const stream: SSEStream = {
220
+ close: () => sink.close(),
221
+ get closed() {
222
+ return sink.closed
223
+ },
224
+ lastEventId: opts?.lastEventId,
225
+ send(event) {
226
+ if (/[\r\n]/.test(event.event)) {
227
+ throw new Error("SSE event name must not contain newlines")
228
+ }
229
+ if (event.id && /[\r\n]/.test(event.id)) {
230
+ throw new Error("SSE id must not contain newlines")
231
+ }
232
+ const dataStr = typeof event.data === "string" ? event.data : JSON.stringify(event.data)
233
+ const dataLines = dataStr
234
+ .split(/\r\n|\r|\n/)
235
+ .map((line) => `data: ${line}`)
236
+ .join("\n")
237
+ let msg = `event: ${event.event}\n${dataLines}\n`
238
+ if (event.id) msg += `id: ${event.id}\n`
239
+ /** SSE retry must be a non-negative integer per spec — skip if invalid */
240
+ if (event.retry !== undefined && Number.isFinite(event.retry) && event.retry >= 0) {
241
+ msg += `retry: ${Math.floor(event.retry)}\n`
242
+ }
243
+ msg += "\n"
244
+ return sink.write(encoder.encode(msg))
245
+ },
246
+ signal: sink.signal,
247
+ }
248
+ if (opts?.defaultRetry !== undefined) {
249
+ void sink.write(encoder.encode(`retry: ${opts.defaultRetry}\n\n`))
250
+ }
251
+ if (opts?.keepalive !== undefined && opts.keepalive > 0) {
252
+ const heartbeat = encoder.encode(": heartbeat\n\n")
253
+ const timer = setInterval(() => {
254
+ void sink.write(heartbeat)
255
+ }, opts.keepalive)
256
+ sink.onEnd(() => clearInterval(timer))
257
+ }
258
+ return callback(stream)
259
+ }, this._producer())
251
260
 
261
+ /* no `connection: keep-alive`: HTTP/1.1 keeps the connection by default, the header would
262
+ * override a client's `Connection: close`, and HTTP/2 forbids it */
252
263
  const headers = new Headers({
253
264
  "cache-control": "no-cache",
254
- connection: "keep-alive",
255
265
  "content-type": "text/event-stream",
256
266
  })
257
267
  applyResponseOptions(headers, opts)
258
- return typed(new Response(readable, { headers, status: 200 }))
268
+ return typed(tagStream(new Response(body, { headers, status: opts?.status ?? 200 })))
259
269
  }
260
270
 
271
+ /**
272
+ * Stream a generator's values. A disconnect, `timeout()` or shutdown calls `generator.return()`,
273
+ * so its `finally` runs; a throw mid-stream breaks the body instead of ending it cleanly.
274
+ */
261
275
  generate(
262
- generator: AsyncGenerator<string, void, unknown> | Generator<string, void, unknown>,
276
+ generator: AsyncGenerator<string | Uint8Array, void, unknown> | Generator<string | Uint8Array, void, unknown>,
263
277
  opts?: { contentType?: string; status?: number },
264
278
  ): TypedResponse<string, "ok"> {
265
- const encoder = new TextEncoder()
266
- const gen = generator
267
- const readable = new ReadableStream({
268
- async pull(controller) {
269
- try {
270
- const { done, value } = await gen.next()
271
- if (done) {
272
- controller.close()
273
- return
274
- }
275
- controller.enqueue(encoder.encode(value))
276
- } catch {
277
- controller.close()
278
- }
279
- },
280
- })
281
279
  const headers = new Headers({
282
280
  "content-type": opts?.contentType ?? "application/octet-stream",
283
281
  })
284
- return typed(new Response(readable, { headers, status: opts?.status ?? 200 }))
282
+ const body = generatorStream(generator, this._producer())
283
+ return typed(tagStream(new Response(body, { headers, status: opts?.status ?? 200 })))
285
284
  }
286
285
 
286
+ /**
287
+ * Write the body through a `WritableStream`. The callback starts when the body is first read
288
+ * (never for HEAD); `signal` aborts when the client disconnects, the request times out, or the
289
+ * server shuts down. Returning ends the body after queued writes flush, whether or not the
290
+ * writable was closed; throwing breaks it, even while the callback holds a writer.
291
+ */
287
292
  stream(
288
- callback: (writable: WritableStream) => Promise<void>,
293
+ callback: (writable: WritableStream, signal: AbortSignal) => Promise<void>,
289
294
  opts?: ResponseOptions,
290
295
  ): TypedResponse<"application/octet-stream", "ok"> {
291
- const { readable, writable } = new TransformStream()
292
- void callback(writable).then(
293
- async () => {
294
- try {
295
- await writable.close()
296
- } catch {}
297
- },
298
- async () => {
299
- try {
300
- await writable.close()
301
- } catch {}
302
- },
303
- )
296
+ const encoder = new TextEncoder()
297
+ const body = producerStream(async (sink) => {
298
+ /* `size()` runs as each chunk enters the writable's queue: it counts writes not yet flushed */
299
+ let queued = 0
300
+ let flushed = 0
301
+ let onFlushed: (() => void) | null = null
302
+ const writable = new WritableStream<unknown>(
303
+ {
304
+ abort(reason) {
305
+ sink.error(reason)
306
+ },
307
+ close() {
308
+ sink.close()
309
+ },
310
+ async write(chunk) {
311
+ try {
312
+ await sink.write(toBytes(chunk, encoder))
313
+ } finally {
314
+ flushed++
315
+ if (flushed === queued) onFlushed?.()
316
+ }
317
+ },
318
+ },
319
+ {
320
+ highWaterMark: 1,
321
+ size() {
322
+ queued++
323
+ return 1
324
+ },
325
+ },
326
+ )
327
+ await callback(writable, sink.signal)
328
+ if (sink.closed) return
329
+ if (flushed < queued) {
330
+ await new Promise<void>((resolve) => {
331
+ onFlushed = resolve
332
+ sink.onEnd(resolve)
333
+ })
334
+ }
335
+ }, this._producer())
304
336
  const headers = new Headers()
305
337
  applyResponseOptions(headers, opts)
306
- return typed(new Response(readable, { headers, status: opts?.status ?? 200 }))
338
+ return typed(tagStream(new Response(body, { headers, status: opts?.status ?? 200 })))
307
339
  }
308
340
 
309
341
  text<SK extends StatusKey>(statusKey: SK, body: string, opts?: ResponseOptions): TypedResponse<"text/plain", SK> {
@@ -315,6 +347,14 @@ export class HoneyRes {
315
347
  }
316
348
  }
317
349
 
350
+ function toBytes(chunk: unknown, encoder: TextEncoder): Uint8Array {
351
+ if (chunk instanceof Uint8Array) return chunk
352
+ if (typeof chunk === "string") return encoder.encode(chunk)
353
+ if (chunk instanceof ArrayBuffer) return new Uint8Array(chunk)
354
+ if (ArrayBuffer.isView(chunk)) return new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength)
355
+ throw new TypeError("ctx.res.stream() chunks must be strings, ArrayBuffers or typed arrays")
356
+ }
357
+
318
358
  export type CustomErrorFormatter = (error: HoneyError, data: Record<string, unknown>) => Record<string, unknown>
319
359
 
320
360
  export function createErrorResponse(