@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/serve-slot.ts CHANGED
@@ -1,9 +1,16 @@
1
+ import { detectRuntime } from "./detect-runtime.ts"
1
2
  import type { HoneyServeOptions, ServeHandle } from "./serve.ts"
2
3
 
3
4
  export type ServeStart = (app: unknown, options?: HoneyServeOptions) => Promise<ServeHandle>
4
5
 
5
6
  const MISSING = 'Honey.serve() requires `import "@lovrozagar/honey/serve"` in the app entry.'
6
7
 
8
+ /**
9
+ * Set while codegen evaluates an app module. A global, not a module variable: the app may load a
10
+ * different copy of this module than the generator (jiti instance, source vs compiled build).
11
+ */
12
+ const CODEGEN = Symbol.for("@lovrozagar/honey.codegen")
13
+
7
14
  let runtime: ServeStart | undefined
8
15
 
9
16
  export function registerServeRuntime(next: ServeStart): void {
@@ -15,6 +22,37 @@ export function resetServeRuntime(): void {
15
22
  }
16
23
 
17
24
  export function getServeRuntime(): ServeStart {
25
+ if (isCodegenProcess()) return codegenServe
18
26
  if (!runtime) throw new Error(MISSING)
19
27
  return runtime
20
28
  }
29
+
30
+ /** Marks this process as running codegen: `app.serve()` resolves without listening. */
31
+ export function setCodegenProcess(active: boolean): void {
32
+ const slot = globalThis as unknown as Record<symbol, unknown>
33
+ if (active) slot[CODEGEN] = true
34
+ else delete slot[CODEGEN]
35
+ }
36
+
37
+ export function isCodegenProcess(): boolean {
38
+ return (globalThis as unknown as Record<symbol, unknown>)[CODEGEN] === true
39
+ }
40
+
41
+ /* codegen loads the app to read its routes; a top-level `await app.serve()` must not bind a port */
42
+ const codegenServe: ServeStart = async (_app, options = {}) => {
43
+ let detected = options.runtime
44
+ try {
45
+ detected ??= detectRuntime()
46
+ } catch {
47
+ detected = "node"
48
+ }
49
+ const hostname = options.hostname ?? "127.0.0.1"
50
+ const port = options.port ?? 3000
51
+ return {
52
+ async close() {},
53
+ hostname,
54
+ port,
55
+ runtime: detected === "cloudflare" ? "node" : detected,
56
+ url: `http://${hostname}:${port}`,
57
+ }
58
+ }
package/src/serve.ts CHANGED
@@ -1,12 +1,20 @@
1
1
  import type { Honey } from "./index.ts"
2
2
  import { cors, type CORSOptions } from "./cors.ts"
3
3
  import { detectRuntime, type ServeRuntime } from "./detect-runtime.ts"
4
+ import { DEFAULT_MAX_REQUEST_BODY } from "./request-limits.ts"
5
+ import { setPeerAddress } from "./peer.ts"
6
+ import { hasValidHost } from "./client-info.ts"
7
+ import type { WSAdapter } from "./ws/cloudflare.ts"
4
8
 
5
9
  export type { ServeRuntime }
6
10
  export { detectRuntime }
7
11
 
8
12
  export type ServeHandle = {
9
- close(): Promise<void>
13
+ /**
14
+ * Stop accepting connections, close WebSockets with 1001, and let in-flight requests finish
15
+ * for up to `timeout` ms (default 1000); then every connection is cut. Same on Node, Bun and Deno.
16
+ */
17
+ close(timeout?: number): Promise<void>
10
18
  hostname: string
11
19
  port: number
12
20
  runtime: Exclude<ServeRuntime, "cloudflare">
@@ -19,6 +27,41 @@ export type HoneyServeOptions = {
19
27
  hostname?: string
20
28
  port?: number
21
29
  runtime?: ServeRuntime
30
+ /**
31
+ * Largest request body, in bytes; a bigger one is answered 413. Default 128 MiB on Node and
32
+ * Bun. Deno has no cap of its own.
33
+ */
34
+ maxRequestBodySize?: number
35
+ /** Node only: `server.headersTimeout`, ms. */
36
+ headersTimeout?: number
37
+ /** Node only: `server.requestTimeout`, ms. */
38
+ requestTimeout?: number
39
+ /** Node only: `server.keepAliveTimeout`, ms. */
40
+ keepAliveTimeout?: number
41
+ /** Node only: how long an upgrade request may take to become a WebSocket, ms (default 30 000). */
42
+ upgradeTimeout?: number
43
+ }
44
+
45
+ const DEFAULT_CLOSE_TIMEOUT = 1_000
46
+
47
+ type AppInternals = { _graph: { settings: { wsAdapter: WSAdapter | null } } }
48
+
49
+ /** The adapter the app already has (`app.wsAdapter(...)`), if any: serve() keeps it. */
50
+ function ownAdapter(app: Honey<Record<string, unknown>>): WSAdapter | null {
51
+ return (app as unknown as AppInternals)._graph.settings.wsAdapter
52
+ }
53
+
54
+ /** Settle with `work`, or with `"timeout"` after `ms`; the timer never outlives the race. */
55
+ async function within(work: Promise<unknown>, ms: number): Promise<"done" | "timeout"> {
56
+ let timer: ReturnType<typeof setTimeout> | undefined
57
+ const timeout = new Promise<"timeout">((r) => {
58
+ timer = setTimeout(() => r("timeout"), ms)
59
+ })
60
+ try {
61
+ return await Promise.race([work.then(() => "done" as const), timeout])
62
+ } finally {
63
+ clearTimeout(timer)
64
+ }
22
65
  }
23
66
 
24
67
  const CF_SERVE_ERROR =
@@ -40,31 +83,51 @@ export async function startHoneyServer(
40
83
  throw new Error(CF_SERVE_ERROR)
41
84
  }
42
85
 
43
- let listening: Honey<Record<string, unknown>> = app
44
- if (options.cors) {
45
- const corsOpts = options.cors === true ? undefined : options.cors
46
- listening = app.use(cors(corsOpts)) as Honey<Record<string, unknown>>
47
- }
86
+ const listening: Honey<Record<string, unknown>> = app
87
+ /* app-wide: every route, 404, 405 and preflight run it first — replaced, not stacked, on a re-serve */
88
+ const corsMw = options.cors ? cors(options.cors === true ? undefined : options.cors) : null
89
+ ;(app as unknown as { _setGlobal(key: string, mw: unknown): void })._setGlobal("serve:cors", corsMw)
48
90
 
49
91
  const hostname = options.hostname ?? (runtime === "deno" ? "127.0.0.1" : "0.0.0.0")
50
92
  const port = options.port ?? 3000
51
93
  const env = (options.env ?? {}) as Record<string, unknown>
52
94
 
53
95
  if (runtime === "bun") {
54
- const { bunWebSocket } = await import("./ws/bun.ts")
55
- const bunWs = bunWebSocket()
56
- listening.wsAdapter(bunWs)
96
+ const existing = ownAdapter(listening)
97
+ let bunWs: BunWSAdapter
98
+ if (existing === null) {
99
+ const { bunWebSocket } = await import("./ws/bun.ts")
100
+ bunWs = bunWebSocket()
101
+ listening.wsAdapter(bunWs)
102
+ } else if ("websocket" in existing) {
103
+ bunWs = existing as BunWSAdapter
104
+ } else {
105
+ throw new Error("serve() on Bun needs a bunWebSocket() adapter, but app.wsAdapter() was given another one")
106
+ }
57
107
  const BunNs = (globalThis as unknown as { Bun: { serve: (opts: Record<string, unknown>) => BunServer } }).Bun
108
+ /* `server` is the same object for every request: build the env once */
109
+ let envWithServer: Record<string, unknown> | null = null
58
110
  const server = BunNs.serve({
59
- fetch: (req: Request, server: unknown) => listening.fetch(req, { ...env, server }),
111
+ fetch: (req: Request, srv: unknown) => {
112
+ if (envWithServer === null) envWithServer = { ...env, server: srv }
113
+ return listening.fetch(req, envWithServer)
114
+ },
60
115
  hostname,
116
+ maxRequestBodySize: options.maxRequestBodySize ?? DEFAULT_MAX_REQUEST_BODY,
61
117
  port,
62
118
  websocket: bunWs.websocket,
63
119
  })
64
120
  const bound = server.port
65
121
  return {
66
- async close() {
67
- await Promise.resolve(server.stop(true))
122
+ async close(timeout = DEFAULT_CLOSE_TIMEOUT) {
123
+ bunWs.closeAll?.(1001, "server shutting down")
124
+ /* graceful first: in-flight requests finish; then cut what is left. Bun before 1.4 keeps
125
+ * idle keep-alive connections serving after stop(false), so close those explicitly,
126
+ * now and again once in-flight requests have finished and left theirs idle. */
127
+ const graceful = Promise.resolve(server.stop(false))
128
+ server.closeIdleConnections?.()
129
+ if ((await within(graceful, timeout)) === "timeout") await Promise.resolve(server.stop(true))
130
+ server.closeIdleConnections?.()
68
131
  },
69
132
  hostname,
70
133
  port: bound,
@@ -74,29 +137,40 @@ export async function startHoneyServer(
74
137
  }
75
138
 
76
139
  if (runtime === "deno") {
77
- const { denoWebSocket } = await import("./ws/deno.ts")
78
- listening.wsAdapter(denoWebSocket())
140
+ if (ownAdapter(listening) === null) {
141
+ const { denoWebSocket } = await import("./ws/deno.ts")
142
+ listening.wsAdapter(denoWebSocket())
143
+ }
79
144
  const DenoNs = (
80
145
  globalThis as unknown as {
81
146
  Deno: {
82
147
  serve: (
83
148
  opts: { hostname: string; port: number; signal?: AbortSignal },
84
- handler: (req: Request) => Response | Promise<Response>,
149
+ handler: (req: Request, info: { remoteAddr?: { hostname?: string } }) => Response | Promise<Response>,
85
150
  ) => { addr?: { port?: number }; finished?: Promise<void>; shutdown?: () => Promise<void> }
86
151
  }
87
152
  }
88
153
  ).Deno
89
154
  const ac = new AbortController()
90
- const server = DenoNs.serve({ hostname, port, signal: ac.signal }, (req) => listening.fetch(req, env))
155
+ const server = DenoNs.serve({ hostname, port, signal: ac.signal }, (req, info) => {
156
+ /* Deno builds req.url from Host and the target: a `Host: x/admin?` would pick the path */
157
+ if (!hasValidHost(req)) return (listening as unknown as { _badRequestTarget(): Response })._badRequestTarget()
158
+ const peer = info?.remoteAddr?.hostname
159
+ if (typeof peer === "string") setPeerAddress(req, peer)
160
+ return listening.fetch(req, env)
161
+ })
91
162
  const bound = server.addr?.port ?? port
92
163
  return {
93
- async close() {
94
- ac.abort()
164
+ async close(timeout = DEFAULT_CLOSE_TIMEOUT) {
165
+ ownAdapter(listening)?.closeAll?.(1001, "server shutting down")
95
166
  if (server.shutdown) {
96
- await Promise.race([server.shutdown(), new Promise<void>((r) => setTimeout(r, 1_000))])
167
+ /* graceful: in-flight requests finish. Deno has no forced close, so a request still
168
+ * running after `timeout` ends on its own (aborting the signal now would throw) */
169
+ await within(server.shutdown(), timeout)
97
170
  return
98
171
  }
99
- await Promise.race([server.finished ?? Promise.resolve(), new Promise<void>((r) => setTimeout(r, 1_000))])
172
+ ac.abort()
173
+ await within(server.finished ?? Promise.resolve(), timeout)
100
174
  },
101
175
  hostname,
102
176
  port: bound,
@@ -105,10 +179,21 @@ export async function startHoneyServer(
105
179
  }
106
180
  }
107
181
 
108
- const { nodeWebSocket } = await import("./ws/node.ts")
109
182
  const { serve } = await import("./node.ts")
110
- listening.wsAdapter(nodeWebSocket())
111
- const server = serve(listening as never, { env, hostname, port })
183
+ if (ownAdapter(listening) === null) {
184
+ const { nodeWebSocket } = await import("./ws/node.ts")
185
+ listening.wsAdapter(nodeWebSocket())
186
+ }
187
+ const server = serve(listening as never, {
188
+ env,
189
+ headersTimeout: options.headersTimeout,
190
+ hostname,
191
+ keepAliveTimeout: options.keepAliveTimeout,
192
+ maxRequestBodySize: options.maxRequestBodySize,
193
+ port,
194
+ requestTimeout: options.requestTimeout,
195
+ upgradeTimeout: options.upgradeTimeout,
196
+ })
112
197
  await new Promise<void>((resolve, reject) => {
113
198
  server.once("listening", () => resolve())
114
199
  server.once("error", reject)
@@ -116,8 +201,8 @@ export async function startHoneyServer(
116
201
  const addr = server.address()
117
202
  const bound = typeof addr === "object" && addr !== null ? addr.port : port
118
203
  return {
119
- async close() {
120
- await server.shutdown(1_000)
204
+ async close(timeout = DEFAULT_CLOSE_TIMEOUT) {
205
+ await server.shutdown(timeout)
121
206
  },
122
207
  hostname,
123
208
  port: bound,
@@ -126,7 +211,10 @@ export async function startHoneyServer(
126
211
  }
127
212
  }
128
213
 
214
+ type BunWSAdapter = WSAdapter & { websocket: Record<string, unknown> }
215
+
129
216
  type BunServer = {
217
+ closeIdleConnections?(): void
130
218
  port: number
131
219
  stop(closeActiveConnections?: boolean): void | Promise<void>
132
220
  }
@@ -1,5 +1,6 @@
1
1
  import { namedMiddleware } from "./middleware.ts"
2
2
  import type { MiddlewareFn } from "./middleware.ts"
3
+ import { withHeaders } from "./with-headers.ts"
3
4
 
4
5
  export type Timing = {
5
6
  end(name: string): void
@@ -12,14 +13,17 @@ type TimingEntry = {
12
13
  start: number
13
14
  }
14
15
 
15
- /** Strip characters that would break Server-Timing header syntax */
16
- const unsafeNameRe = /[;,="]/g
16
+ /* metric names are RFC 9110 tokens; anything else becomes `_` */
17
+ const nonTokenRe = /[^!#$%&'*+.^`|~\w-]/g
17
18
  function sanitizeName(name: string): string {
18
- return name.replace(unsafeNameRe, "")
19
+ const token = name.replace(nonTokenRe, "_")
20
+ return token.length > 0 ? token : "_"
19
21
  }
20
22
 
23
+ /* descriptions are quoted-strings: latin1 only, no controls, `\` and `"` escaped */
24
+ const nonQuotableRe = /[^\t\x20-\x7e\x80-\xff]/g
21
25
  function escapeDescription(desc: string): string {
22
- return desc.replace(/\\/g, "\\\\").replace(/"/g, '\\"')
26
+ return desc.replace(nonQuotableRe, "?").replace(/[\\"]/g, "\\$&")
23
27
  }
24
28
 
25
29
  export function serverTiming(): MiddlewareFn<{}, { timing: Timing }> {
@@ -58,9 +62,9 @@ export function serverTiming(): MiddlewareFn<{}, { timing: Timing }> {
58
62
  const totalDur = (performance.now() - requestStart).toFixed(2)
59
63
  parts.push(`total;dur=${totalDur}`)
60
64
 
61
- response.headers.set("server-timing", parts.join(", "))
62
-
63
- return response
65
+ /* append: an upstream or handler Server-Timing is kept */
66
+ const value = parts.join(", ")
67
+ return withHeaders(response, (headers) => headers.append("server-timing", value))
64
68
  }
65
69
 
66
70
  return namedMiddleware("serverTiming", mw)
package/src/static.ts CHANGED
@@ -1,58 +1,111 @@
1
1
  import { namedMiddleware } from "./middleware.ts"
2
2
  import type { MiddlewareFn } from "./middleware.ts"
3
+ import { normalizePath, pathOfUrl } from "./request-path.ts"
4
+ import { withHeaders } from "./with-headers.ts"
3
5
 
4
6
  type StaticConfig<TCtx> = {
7
+ /**
8
+ * Returns the file for `filePath`, or `null` to fall through to the next handler.
9
+ * `filePath` starts with `/`, is decoded once, and has no `.`, `..`, empty, NUL, `:` or
10
+ * separator-bearing segments — joining it onto a root directory cannot leave that root,
11
+ * on POSIX or Windows.
12
+ */
5
13
  resolve: (ctx: TCtx, filePath: string) => Response | null | Promise<Response | null>
14
+ /** Mount point, matched on whole segments: `/assets` serves `/assets/x`, never `/assets-private/x`. */
6
15
  prefix?: string
16
+ /** Extra response headers; a function receives the same `filePath` that `resolve` got. */
7
17
  headers?: Record<string, string> | ((filePath: string) => Record<string, string>)
18
+ /** Rewrites the decoded file path before `resolve`. The result is checked again. */
8
19
  rewritePath?: (filePath: string, ctx: TCtx) => string
9
20
  }
10
21
 
11
- const TRAVERSAL = /(?:^|\/)\.\.\//
22
+ function normalizePrefix(prefix: string): string {
23
+ const normalized = normalizePath(prefix.startsWith("/") ? prefix : `/${prefix}`)
24
+ if (normalized === null) throw new Error(`staticFiles: invalid prefix ${JSON.stringify(prefix)}`)
25
+ return normalized.length > 1 && normalized.endsWith("/") ? normalized.slice(0, -1) : normalized
26
+ }
27
+
28
+ /**
29
+ * `/`-joined decoded segments, or `null` when any segment is malformed percent-encoding or
30
+ * decodes to something a file system treats specially (`.`, `..`, empty, a separator, NUL, `:`).
31
+ */
32
+ function safeFilePath(encoded: string): string | null {
33
+ const parts = encoded.split("/")
34
+ const out: string[] = []
35
+ for (let i = 0; i < parts.length; i++) {
36
+ const part = parts[i]
37
+ if (part === "") {
38
+ /* the leading slash, and a trailing one (a directory request) */
39
+ if (i === 0 || i === parts.length - 1) continue
40
+ return null
41
+ }
42
+ let seg: string
43
+ try {
44
+ seg = decodeURIComponent(part)
45
+ } catch {
46
+ return null
47
+ }
48
+ if (!isSafeSegment(seg)) return null
49
+ out.push(seg)
50
+ }
51
+ const joined = `/${out.join("/")}`
52
+ return out.length > 0 && encoded.endsWith("/") ? `${joined}/` : joined
53
+ }
54
+
55
+ function isSafeSegment(seg: string): boolean {
56
+ if (seg === "" || seg === "." || seg === "..") return false
57
+ /* `:` makes a Windows drive (`C:`) or alternate data stream (`a.txt::$DATA`) */
58
+ return !seg.includes("/") && !seg.includes("\\") && !seg.includes("\0") && !seg.includes(":")
59
+ }
60
+
61
+ /** Re-checks a `rewritePath` result: it must be the same safe shape `resolve` would get without one. */
62
+ function safeRewrite(path: string): string | null {
63
+ if (!path.startsWith("/")) return null
64
+ const parts = path.split("/")
65
+ for (let i = 1; i < parts.length; i++) {
66
+ if (parts[i] === "" && i === parts.length - 1) continue
67
+ if (!isSafeSegment(parts[i])) return null
68
+ }
69
+ return path
70
+ }
12
71
 
13
72
  export function staticFiles<TCtx extends { req: Request }>(config: StaticConfig<TCtx>): MiddlewareFn<TCtx, {}> {
14
- const prefix = config.prefix ?? "/"
73
+ const prefix = normalizePrefix(config.prefix ?? "/")
15
74
 
16
75
  const mw: MiddlewareFn<TCtx, {}> = async (ctx, next) => {
17
76
  const method = ctx.req.method
18
77
  if (method !== "GET" && method !== "HEAD") return next()
19
78
 
20
- /* fast path extraction — no new URL() */
21
- const raw = ctx.req.url
22
- const protoEnd = raw.indexOf("//")
23
- const pathStart = protoEnd === -1 ? 0 : raw.indexOf("/", protoEnd + 2)
24
- const qIdx = pathStart === -1 ? -1 : raw.indexOf("?", pathStart)
25
- let path: string
26
- if (pathStart === -1) {
27
- path = "/"
28
- } else if (qIdx === -1) {
29
- path = raw.substring(pathStart)
79
+ /* the normalized path the router matched — never the raw request target */
80
+ const fromCtx = (ctx as { path?: unknown }).path
81
+ const path = typeof fromCtx === "string" && fromCtx !== "" ? fromCtx : normalizePath(pathOfUrl(ctx.req.url))
82
+ if (path === null) return next()
83
+
84
+ let rest: string
85
+ if (prefix === "/") {
86
+ rest = path
87
+ } else if (path === prefix) {
88
+ rest = "/"
89
+ } else if (path.startsWith(prefix) && path.charCodeAt(prefix.length) === 47) {
90
+ rest = path.substring(prefix.length)
30
91
  } else {
31
- path = raw.substring(pathStart, qIdx)
92
+ return next()
32
93
  }
33
94
 
34
- if (!path.startsWith(prefix)) return next()
95
+ const decoded = safeFilePath(rest)
96
+ if (decoded === null) return next()
97
+ const filePath = config.rewritePath ? safeRewrite(config.rewritePath(decoded, ctx)) : decoded
98
+ if (filePath === null) return next()
35
99
 
36
- const stripped = prefix === "/" ? path : path.substring(prefix.length)
37
- const filePath = stripped === "" ? "/" : stripped
38
-
39
- /* path traversal guard — reject encoded and literal .. segments */
40
- const decoded = decodeURIComponent(filePath)
41
- if (TRAVERSAL.test(decoded)) return next()
42
-
43
- const resolved = config.rewritePath ? config.rewritePath(filePath, ctx) : filePath
44
- const response = await config.resolve(ctx, resolved)
100
+ const response = await config.resolve(ctx, filePath)
45
101
  if (response === null) return next()
46
102
 
47
- /* apply extra headers */
48
- if (config.headers) {
49
- const extra = typeof config.headers === "function" ? config.headers(filePath) : config.headers
50
- for (const key in extra) {
51
- response.headers.set(key, extra[key])
52
- }
53
- }
54
-
55
- return response
103
+ if (!config.headers) return response
104
+ /* a resolver's Response can have immutable headers (`fetch()`, a Workers ASSETS binding) */
105
+ const extra = typeof config.headers === "function" ? config.headers(filePath) : config.headers
106
+ return withHeaders(response, (headers) => {
107
+ for (const key of Object.keys(extra)) headers.set(key, extra[key])
108
+ })
56
109
  }
57
110
 
58
111
  return namedMiddleware("staticFiles", mw)
@@ -12,13 +12,32 @@ type Tracer = {
12
12
 
13
13
  type TelemetryAdapter = {
14
14
  onError?(ctx: { duration: number; error: HoneyError; method: string; path: string }): void
15
- onHandler?(ctx: { duration: number; method: string; path: string; status: number }): void
15
+ onHandler?(ctx: { duration: number; method: string; path: string; route?: string; status: number }): void
16
16
  onMethodNotAllowed?(ctx: { allowed: string[]; method: string; path: string; req: Request }): void
17
17
  onMiddleware?(ctx: { duration: number; error?: unknown; name: string }): void
18
18
  onNotFound?(ctx: { method: string; path: string; req: Request }): void
19
19
  onRequest?(ctx: { env: unknown; req: Request }): void
20
20
  onResponse?(ctx: { duration: number; req: Request; status: number }): void
21
- onRoute?(ctx: { method: string; params: Record<string, string>; path: string; req: Request }): void
21
+ onRoute?(ctx: { method: string; params: Record<string, string>; path: string; req: Request; route?: string }): void
22
+ }
23
+
24
+ /**
25
+ * The request URL without its query string or fragment: queries carry tokens and session ids,
26
+ * and attribute values are exported to whatever backend the tracer feeds.
27
+ */
28
+ function urlWithoutQuery(url: string): string {
29
+ const q = url.indexOf("?")
30
+ const h = url.indexOf("#")
31
+ const end = q === -1 ? h : h === -1 ? q : Math.min(q, h)
32
+ return end === -1 ? url : url.slice(0, end)
33
+ }
34
+
35
+ /** Path part of an absolute URL, without the query. */
36
+ function pathOf(url: string): string {
37
+ const bare = urlWithoutQuery(url)
38
+ const scheme = bare.indexOf("//")
39
+ const start = scheme === -1 ? 0 : bare.indexOf("/", scheme + 2)
40
+ return start === -1 ? "/" : bare.slice(start)
22
41
  }
23
42
 
24
43
  export function otelAdapter(options: { tracer: Tracer }): TelemetryAdapter {
@@ -45,7 +64,8 @@ export function otelAdapter(options: { tracer: Tracer }): TelemetryAdapter {
45
64
  try {
46
65
  const span = options.tracer.startSpan("http.handler")
47
66
  span.setAttribute("http.method", ctx.method)
48
- span.setAttribute("http.route", ctx.path)
67
+ /* the pattern, never the request path: one series per route, not per id */
68
+ if (ctx.route !== undefined && ctx.route !== "") span.setAttribute("http.route", ctx.route)
49
69
  span.setAttribute("http.status_code", ctx.status)
50
70
  span.setAttribute("http.duration_ms", ctx.duration)
51
71
  span.end()
@@ -61,7 +81,7 @@ export function otelAdapter(options: { tracer: Tracer }): TelemetryAdapter {
61
81
  span.addEvent("method_not_allowed", {
62
82
  "http.allowed_methods": ctx.allowed.join(", "),
63
83
  "http.method": ctx.method,
64
- "http.route": ctx.path,
84
+ "url.path": ctx.path,
65
85
  })
66
86
  }
67
87
  } catch {
@@ -87,7 +107,7 @@ export function otelAdapter(options: { tracer: Tracer }): TelemetryAdapter {
87
107
  if (span) {
88
108
  span.addEvent("not_found", {
89
109
  "http.method": ctx.method,
90
- "http.path": ctx.path,
110
+ "url.path": ctx.path,
91
111
  })
92
112
  }
93
113
  } catch {
@@ -98,7 +118,9 @@ export function otelAdapter(options: { tracer: Tracer }): TelemetryAdapter {
98
118
  onRequest(ctx) {
99
119
  try {
100
120
  const span = options.tracer.startSpan("http.request")
101
- span.setAttribute("http.url", ctx.req.url)
121
+ span.setAttribute("http.method", ctx.req.method)
122
+ span.setAttribute("http.url", urlWithoutQuery(ctx.req.url))
123
+ span.setAttribute("url.path", pathOf(ctx.req.url))
102
124
  spans.set(ctx.req, span)
103
125
  } catch {
104
126
  /* swallow */
@@ -124,7 +146,7 @@ export function otelAdapter(options: { tracer: Tracer }): TelemetryAdapter {
124
146
  const span = spans.get(ctx.req)
125
147
  if (span) {
126
148
  span.setAttribute("http.method", ctx.method)
127
- span.setAttribute("http.route", ctx.path)
149
+ if (ctx.route !== undefined && ctx.route !== "") span.setAttribute("http.route", ctx.route)
128
150
  }
129
151
  } catch {
130
152
  /* swallow */