@lovrozagar/honey 0.6.8 → 0.7.1

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 +819 -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 +603 -423
  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/context.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  import { isNodeOutbound } from "./honey-response.ts"
2
+ import type { HoneyError } from "./error.ts"
3
+ import type { ProducerOptions } from "./producer-stream.ts"
2
4
  import type { SSEOptions, SSEStream, TypedResponse } from "./response.ts"
3
5
  import { HoneyRes } from "./response.ts"
6
+ import { dict } from "./dict.ts"
7
+ import { peerAddressOf } from "./peer.ts"
8
+ import { resolveClient, TRUST_OFF, type ClientInfo, type TrustSetting } from "./client-info.ts"
4
9
  import type { PendingTap } from "./types.ts"
5
10
  import { EMPTY_OBJ } from "./types.ts"
6
11
  import { parseCookies } from "./validation.ts"
@@ -11,11 +16,22 @@ import { parseCookies } from "./validation.ts"
11
16
  */
12
17
  class ContextRes extends HoneyRes {
13
18
  private _req: Request
19
+ private _ctx: HoneyContext<unknown>
14
20
  private _lastEventId: string | undefined | false = false
15
21
 
16
- constructor(req: Request) {
22
+ constructor(req: Request, ctx: HoneyContext<unknown>) {
17
23
  super(isNodeOutbound(req))
18
24
  this._req = req
25
+ this._ctx = ctx
26
+ }
27
+
28
+ /** Streams end with the request: `ctx.signal`. Producer failures go to the app logger. */
29
+ protected override _producer(): ProducerOptions {
30
+ const ctx = this._ctx
31
+ return {
32
+ report: (err) => reportStreamError(ctx, err),
33
+ signal: () => ctx.signal,
34
+ }
19
35
  }
20
36
 
21
37
  override sse(
@@ -33,23 +49,41 @@ class ContextRes extends HoneyRes {
33
49
  }
34
50
  function parseSearch(ctx: HoneyContext): void {
35
51
  const url = ctx._lzUrlFn ? ctx._lzUrlFn() : new URL(ctx.req.url)
36
- const first: Record<string, string> = {}
37
- const all: Record<string, string[]> = {}
52
+ /* keys are request data: no prototype, so `?__proto__=` or `?toString=` is just a key */
53
+ const first = dict<string>()
54
+ const all = dict<string[]>()
38
55
  for (const [key, value] of url.searchParams) {
39
- if (first[key] === undefined) {
56
+ const values = all[key]
57
+ if (values === undefined) {
40
58
  first[key] = value
41
59
  all[key] = [value]
42
60
  } else {
43
- const values = all[key]
44
- if (values) {
45
- values.push(value)
46
- }
61
+ values.push(value)
47
62
  }
48
63
  }
49
64
  ctx._lzSearch = first
50
65
  ctx._lzSearchAll = all
51
66
  }
52
67
 
68
+ /** What `ctx._rq` carries for client-info resolution; the dispatcher's FetchCtx has these fields. */
69
+ type ClientSource = { env?: unknown; request?: Request; trust?: TrustSetting }
70
+
71
+ /**
72
+ * The client address, scheme and host of the request `ctx` serves, decided by the app's
73
+ * `trustProxy()` setting. `ctx.ip` is its `ip`.
74
+ */
75
+ export function clientInfo(ctx: HoneyContext<never> | HoneyContext): ClientInfo {
76
+ const c = ctx as HoneyContext
77
+ if (c._lzClient === null) {
78
+ const src = (c._rq ?? {}) as ClientSource
79
+ /* the peer is registered on the request the adapter handed over; headers are read from
80
+ * ctx.req, which survives Deno's upgrade (a header snapshot) */
81
+ const peer = peerAddressOf(src.request ?? c.req, src.env ?? c.env)
82
+ c._lzClient = resolveClient(src.trust ?? TRUST_OFF, c.req, peer)
83
+ }
84
+ return c._lzClient
85
+ }
86
+
53
87
  function bgSwallow(p: Promise<unknown>) {
54
88
  p.catch(noop)
55
89
  }
@@ -63,6 +97,44 @@ function noop() {}
63
97
  * `declare` fields have no private brand, so Omit<HoneyContext, "res">
64
98
  * works structurally for ApplyOutput type narrowing.
65
99
  */
100
+ type Logger = { error?: (obj: unknown, msg?: string) => void }
101
+
102
+ function reportStreamError(ctx: HoneyContext<unknown>, err: unknown): void {
103
+ const log = (ctx._rq as { log?: Logger } | null)?.log
104
+ if (typeof log?.error === "function") {
105
+ log.error({ err, path: ctx.path }, "stream producer failed")
106
+ return
107
+ }
108
+ console.error("honey: stream producer failed", err)
109
+ }
110
+
111
+ /**
112
+ * Abort `ctx.signal` (with `reason`) for the request `ctx` serves: `timeout()` uses it. Streams
113
+ * still running for the request end, and handlers that pass the signal on stop their work.
114
+ */
115
+ export function abortRequest(ctx: HoneyContext<never> | HoneyContext, reason?: unknown): void {
116
+ const c = ctx as HoneyContext
117
+ if (c._ac === null) linkSignal(c)
118
+ c._ac?.abort(reason)
119
+ }
120
+
121
+ /** Create `ctx.signal`, following the request's own signal (client disconnect, shutdown). */
122
+ function linkSignal(ctx: HoneyContext): AbortSignal {
123
+ const ac = new AbortController()
124
+ ctx._ac = ac
125
+ let request: AbortSignal | undefined
126
+ try {
127
+ request = ctx.req.signal
128
+ } catch {
129
+ /* a request shim without a signal: only timeout() can abort */
130
+ }
131
+ if (request !== undefined && request !== null) {
132
+ if (request.aborted) ac.abort(request.reason)
133
+ else request.addEventListener("abort", () => ac.abort(request.reason), { once: true })
134
+ }
135
+ return ac.signal
136
+ }
137
+
66
138
  export class HoneyContext<TEnv = Record<string, unknown>> {
67
139
  readonly background: (p: Promise<unknown>) => void
68
140
  readonly env: TEnv
@@ -75,14 +147,39 @@ export class HoneyContext<TEnv = Record<string, unknown>> {
75
147
  declare readonly errors: Record<string, (...args: never[]) => unknown>
76
148
  declare readonly executionCtx: { waitUntil?: (p: Promise<unknown>) => void } | undefined
77
149
  declare readonly headers: Record<string, string>
150
+ /**
151
+ * The client's IP address in canonical form (`::ffff:1.2.3.4` is `1.2.3.4`), or `null` when
152
+ * the runtime does not report one. Behind reverse proxies, set `app.trustProxy()`; until then
153
+ * this is the proxy's address and forwarding headers are ignored.
154
+ */
155
+ declare readonly ip: string | null
78
156
  declare readonly meta: Record<string, unknown>
79
157
  declare readonly path: string
80
- declare readonly realtime: { publish(topic: string, data: unknown): void }
158
+ /** Publish into a realtime namespace (see `RealtimePublisher`); spelled out so generated types inline it. */
159
+ declare readonly realtime: {
160
+ publish(topic: string, data: unknown): void
161
+ namespace(name: string): { publish(topic: string, data: unknown): void }
162
+ }
81
163
  declare readonly routePattern: string
82
164
  declare readonly search: Record<string, string>
83
165
  declare readonly searchAll: Record<string, string[]>
166
+ /**
167
+ * Aborts when the client disconnects, when `timeout()` fires, or when the server shuts down.
168
+ * Pass it to `fetch()`, database drivers and timers so abandoned work stops; streams from
169
+ * `ctx.res.sse()`, `stream()` and `generate()` already end with it.
170
+ */
171
+ declare readonly signal: AbortSignal
172
+ /**
173
+ * The error behind the response, once a handler or a deeper middleware threw and the
174
+ * `next()` boundary turned it into an error response; `undefined` while nothing failed.
175
+ * Read it after `await next()` for metrics, logs or reporting: `errorKey`, `status`, and
176
+ * `cause` (the original throw when it was not a `HoneyError`). Later errors replace it.
177
+ */
178
+ declare readonly error: HoneyError | undefined
84
179
 
85
180
  /* backing state for lazy getters — stored directly on instance to avoid extra object allocation */
181
+ /** @internal */ _ac: AbortController | null
182
+ /** @internal */ _lzClient: ClientInfo | null
86
183
  /** @internal */ _lzCookies: Record<string, string> | null
87
184
  /** @internal */ _lzHeaders: Record<string, string> | null
88
185
  /** @internal */ _lzSearch: Record<string, string> | null
@@ -96,6 +193,9 @@ export class HoneyContext<TEnv = Record<string, unknown>> {
96
193
  /* tap side-effects — queued by c.tap(), drained after handler */
97
194
  /** @internal */ _pendingTaps: PendingTap[] | null
98
195
 
196
+ /* the request this context serves — read by the error boundary and terminal handlers */
197
+ /** @internal */ _rq: unknown
198
+
99
199
  constructor(opts: {
100
200
  env: TEnv
101
201
  executionCtx?: { waitUntil?: (p: Promise<unknown>) => void }
@@ -109,7 +209,7 @@ export class HoneyContext<TEnv = Record<string, unknown>> {
109
209
  this.req = opts.req
110
210
  this.env = opts.env
111
211
  this.params = opts.params
112
- this.res = new ContextRes(opts.req)
212
+ this.res = new ContextRes(opts.req, this as HoneyContext<unknown>)
113
213
 
114
214
  /* direct assignment — no defineProperty */
115
215
  ;(this as Record<string, unknown>)["executionCtx"] = opts.executionCtx
@@ -127,6 +227,8 @@ export class HoneyContext<TEnv = Record<string, unknown>> {
127
227
  : bgSwallow
128
228
 
129
229
  /* lazy getter backing — flat on instance, no extra object */
230
+ this._ac = null
231
+ this._lzClient = null
130
232
  this._lzCookies = null
131
233
  this._lzHeaders = null
132
234
  this._lzSearch = null
@@ -134,11 +236,14 @@ export class HoneyContext<TEnv = Record<string, unknown>> {
134
236
  this._lzUrlFn = opts.urlFn ?? null
135
237
 
136
238
  /* error propagation */
239
+ ;(this as Record<string, unknown>)["error"] = undefined
137
240
  this._errorToResponse = null
138
241
  this._isErrorResponse = false
139
242
 
140
243
  /* taps */
141
244
  this._pendingTaps = null
245
+
246
+ this._rq = null
142
247
  }
143
248
 
144
249
  /** @internal */
@@ -173,7 +278,7 @@ export class HoneyContext<TEnv = Record<string, unknown>> {
173
278
  enumerable: true,
174
279
  get(this: HoneyContext): Record<string, string> {
175
280
  if (this._lzHeaders === null) {
176
- const h: Record<string, string> = {}
281
+ const h = dict<string>()
177
282
  this.req.headers.forEach((value, key) => {
178
283
  h[key] = value
179
284
  })
@@ -182,6 +287,20 @@ export class HoneyContext<TEnv = Record<string, unknown>> {
182
287
  return this._lzHeaders
183
288
  },
184
289
  })
290
+ Object.defineProperty(HoneyContext.prototype, "ip", {
291
+ configurable: true,
292
+ enumerable: true,
293
+ get(this: HoneyContext): string | null {
294
+ return clientInfo(this).ip
295
+ },
296
+ })
297
+ Object.defineProperty(HoneyContext.prototype, "signal", {
298
+ configurable: true,
299
+ enumerable: true,
300
+ get(this: HoneyContext): AbortSignal {
301
+ return this._ac === null ? linkSignal(this) : this._ac.signal
302
+ },
303
+ })
185
304
  Object.defineProperty(HoneyContext.prototype, "search", {
186
305
  configurable: true,
187
306
  enumerable: true,
@@ -1,10 +1,56 @@
1
1
  const encoder = new TextEncoder()
2
2
 
3
+ /**
4
+ * Signed value formats:
5
+ *
6
+ * - v2 (current): `<value>.~<sig>`, where `sig` is the HMAC-SHA256 of
7
+ * `"honey-cookie-v2\0" + name + "\0" + value`. The cookie name is bound into
8
+ * the MAC, so a signature cannot be replayed under another cookie that shares
9
+ * the secret. `~` is outside the base64url alphabet, so the two formats never
10
+ * overlap.
11
+ * - v1 (legacy): `<value>.<sig>`, the HMAC of the value alone. `verify` accepts
12
+ * it while `legacy` is true (the default) so cookies issued before the change
13
+ * keep working during a migration window.
14
+ */
15
+ const V2_MARKER = "~"
16
+ const V2_DOMAIN = "honey-cookie-v2\0"
17
+ /* An HMAC-SHA256 tag is 32 bytes: 43 base64url characters, no padding. */
18
+ const SIG_RE = /^[A-Za-z0-9_-]{43}$/
19
+
20
+ export type SignOptions = {
21
+ /** The cookie name to bind the signature to. Pass the same name to `verify`. */
22
+ name?: string
23
+ }
24
+
25
+ export type VerifyOptions = {
26
+ /** The cookie name the signature must be bound to. */
27
+ name?: string
28
+ /**
29
+ * Also accept legacy (v1) signatures, which are not bound to a name.
30
+ * Default `true`; set `false` once every v1 cookie has expired.
31
+ */
32
+ legacy?: boolean
33
+ }
34
+
35
+ const MAX_CACHED_KEYS = 32
36
+ const keyCache = new Map<string, Promise<CryptoKey>>()
37
+
3
38
  function importKey(secret: string): Promise<CryptoKey> {
4
- return crypto.subtle.importKey("raw", encoder.encode(secret), { hash: "SHA-256", name: "HMAC" }, false, [
5
- "sign",
6
- "verify",
7
- ])
39
+ let key = keyCache.get(secret)
40
+ if (key === undefined) {
41
+ key = crypto.subtle.importKey("raw", encoder.encode(secret), { hash: "SHA-256", name: "HMAC" }, false, [
42
+ "sign",
43
+ "verify",
44
+ ])
45
+ if (keyCache.size >= MAX_CACHED_KEYS) {
46
+ const oldest = keyCache.keys().next().value
47
+ if (oldest !== undefined) keyCache.delete(oldest)
48
+ }
49
+ keyCache.set(secret, key)
50
+ /* A rejected import must not stay cached. */
51
+ key.catch(() => keyCache.delete(secret))
52
+ }
53
+ return key
8
54
  }
9
55
 
10
56
  function toBase64Url(buffer: ArrayBuffer): string {
@@ -16,34 +62,57 @@ function toBase64Url(buffer: ArrayBuffer): string {
16
62
  return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "")
17
63
  }
18
64
 
19
- function fromBase64Url(str: string): Uint8Array {
20
- const padded = str.replace(/-/g, "+").replace(/_/g, "/")
21
- const binary = atob(padded)
65
+ /** Strict decode: the input must be the canonical encoding of a 32-byte tag. */
66
+ function fromBase64Url(str: string): Uint8Array<ArrayBuffer> | null {
67
+ if (!SIG_RE.test(str)) return null
68
+ let binary: string
69
+ try {
70
+ binary = atob(str.replace(/-/g, "+").replace(/_/g, "/"))
71
+ } catch {
72
+ return null
73
+ }
22
74
  const bytes = new Uint8Array(binary.length)
23
75
  for (let i = 0; i < binary.length; i++) {
24
76
  bytes[i] = binary.charCodeAt(i)
25
77
  }
78
+ /* Reject non-zero trailing bits, which would make signatures malleable. */
79
+ if (toBase64Url(bytes.buffer) !== str) return null
26
80
  return bytes
27
81
  }
28
82
 
29
- export async function sign(value: string, secret: string): Promise<string> {
83
+ function v2Payload(name: string, value: string): Uint8Array<ArrayBuffer> {
84
+ return encoder.encode(`${V2_DOMAIN}${name}\0${value}`)
85
+ }
86
+
87
+ export async function sign(value: string, secret: string, opts: SignOptions = {}): Promise<string> {
30
88
  const key = await importKey(secret)
31
- const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(value))
32
- return `${value}.${toBase64Url(signature)}`
89
+ const signature = await crypto.subtle.sign("HMAC", key, v2Payload(opts.name ?? "", value))
90
+ return `${value}.${V2_MARKER}${toBase64Url(signature)}`
33
91
  }
34
92
 
35
- export async function verify(signed: string, secrets: string[]): Promise<string | null> {
36
- if (signed.length === 0) return null
93
+ /** Returns the original value, or `null` when no secret verifies it or the input is malformed. */
94
+ export async function verify(signed: string, secrets: string[], opts: VerifyOptions = {}): Promise<string | null> {
95
+ if (typeof signed !== "string" || signed.length === 0) return null
37
96
  const lastDot = signed.lastIndexOf(".")
38
97
  if (lastDot === -1) return null
39
98
 
40
99
  const value = signed.slice(0, lastDot)
41
- const sig = fromBase64Url(signed.slice(lastDot + 1))
100
+ let sigText = signed.slice(lastDot + 1)
101
+ const isV2 = sigText.startsWith(V2_MARKER)
102
+ if (isV2) sigText = sigText.slice(V2_MARKER.length)
103
+ else if (opts.legacy === false) return null
104
+
105
+ const sig = fromBase64Url(sigText)
106
+ if (sig === null) return null
107
+ const payload = isV2 ? v2Payload(opts.name ?? "", value) : encoder.encode(value)
42
108
 
43
109
  for (const secret of secrets) {
44
- const key = await importKey(secret)
45
- const valid = await crypto.subtle.verify("HMAC", key, sig.buffer as ArrayBuffer, encoder.encode(value))
46
- if (valid) return value
110
+ try {
111
+ const key = await importKey(secret)
112
+ if (await crypto.subtle.verify("HMAC", key, sig, payload)) return value
113
+ } catch {
114
+ /* An unusable secret never verifies. */
115
+ }
47
116
  }
48
117
 
49
118
  return null
package/src/cookie.ts CHANGED
@@ -9,8 +9,11 @@ export type CookieOptions = {
9
9
  value: string
10
10
  }
11
11
 
12
+ const UNSAFE_KEYS = new Set(["__proto__", "constructor", "prototype"])
13
+
12
14
  /**
13
- * Percent-encode characters outside the RFC 6265 cookie-octet range.
15
+ * Percent-encode characters outside the RFC 6265 cookie-octet range, plus `%`
16
+ * itself so the value always decodes back to what was set.
14
17
  * Cookie-octets: 0x21, 0x23-0x2B, 0x2D-0x3A, 0x3C-0x5B, 0x5D-0x7E
15
18
  */
16
19
  function encodeCookieValue(value: string): string {
@@ -18,13 +21,16 @@ function encodeCookieValue(value: string): string {
18
21
  for (const ch of value) {
19
22
  const c = ch.codePointAt(0) ?? 0
20
23
  if (
21
- c === 0x21 ||
22
- (c >= 0x23 && c <= 0x2b) ||
23
- (c >= 0x2d && c <= 0x3a) ||
24
- (c >= 0x3c && c <= 0x5b) ||
25
- (c >= 0x5d && c <= 0x7e)
24
+ c !== 0x25 &&
25
+ (c === 0x21 ||
26
+ (c >= 0x23 && c <= 0x2b) ||
27
+ (c >= 0x2d && c <= 0x3a) ||
28
+ (c >= 0x3c && c <= 0x5b) ||
29
+ (c >= 0x5d && c <= 0x7e))
26
30
  ) {
27
31
  encoded += ch
32
+ } else if (c >= 0xd800 && c <= 0xdfff) {
33
+ throw new TypeError("Cookie value contains a lone surrogate and cannot be encoded")
28
34
  } else {
29
35
  encoded += encodeURIComponent(ch)
30
36
  }
@@ -32,7 +38,52 @@ function encodeCookieValue(value: string): string {
32
38
  return encoded
33
39
  }
34
40
 
41
+ /**
42
+ * Decode `%XX` sequences that form valid UTF-8 and keep every other byte as
43
+ * sent. A stray `%` in a foreign cookie no longer disables decoding for the
44
+ * whole value.
45
+ */
46
+ export function decodeCookieValue(value: string): string {
47
+ if (value.indexOf("%") === -1) return value
48
+ try {
49
+ return decodeURIComponent(value)
50
+ } catch {
51
+ return value.replace(/(?:%[0-9A-Fa-f]{2})+/g, (run) => {
52
+ try {
53
+ return decodeURIComponent(run)
54
+ } catch {
55
+ return run
56
+ }
57
+ })
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Parse a `Cookie` request header. When a name repeats, the first value wins:
63
+ * browsers send the cookie with the most specific path first (RFC 6265 §5.4).
64
+ * Names are trimmed on both sides; empty names and prototype keys are skipped.
65
+ */
66
+ export function parseCookieHeader(header: string): Record<string, string> {
67
+ const result: Record<string, string> = {}
68
+ if (header.length === 0) return result
69
+ for (const pair of header.split(";")) {
70
+ const eqIdx = pair.indexOf("=")
71
+ if (eqIdx === -1) continue
72
+ const name = pair.slice(0, eqIdx).trim()
73
+ if (name.length === 0 || UNSAFE_KEYS.has(name) || Object.hasOwn(result, name)) continue
74
+ let value = pair.slice(eqIdx + 1).trim()
75
+ /* RFC 6265: strip surrounding double quotes before decoding, so an encoded quote survives. */
76
+ if (value.length >= 2 && value.charCodeAt(0) === 34 && value.charCodeAt(value.length - 1) === 34) {
77
+ value = value.slice(1, -1)
78
+ }
79
+ result[name] = decodeCookieValue(value)
80
+ }
81
+ return result
82
+ }
83
+
35
84
  const validCookieNameRe = /^[\w!#$%&'*.^`|~+-]+$/
85
+ /* A host name or IP literal, optionally with a leading dot. */
86
+ const validDomainRe = /^\.?(?:[A-Za-z0-9-]+\.)*[A-Za-z0-9-]+$|^\[[0-9A-Fa-f:.]+\]$/
36
87
 
37
88
  export function serializeCookie(name: string, opts: CookieOptions): string {
38
89
  if (!validCookieNameRe.test(name)) {
@@ -42,7 +93,9 @@ export function serializeCookie(name: string, opts: CookieOptions): string {
42
93
  let secure = opts.secure
43
94
  let path = opts.path
44
95
 
45
- if (name.startsWith("__Host-")) {
96
+ /* Browsers match the prefixes case-insensitively (RFC 6265bis §4.1.3). */
97
+ const lower = name.toLowerCase()
98
+ if (lower.startsWith("__host-")) {
46
99
  if (!opts.secure) throw new Error("__Host- cookies require secure: true")
47
100
  if (opts.domain) throw new Error("__Host- cookies must not set domain")
48
101
  if (opts.path !== "/" && opts.path !== undefined) {
@@ -50,7 +103,7 @@ export function serializeCookie(name: string, opts: CookieOptions): string {
50
103
  }
51
104
  secure = true
52
105
  path = path ?? "/"
53
- } else if (name.startsWith("__Secure-")) {
106
+ } else if (lower.startsWith("__secure-")) {
54
107
  if (!opts.secure) throw new Error("__Secure- cookies require secure: true")
55
108
  secure = true
56
109
  }
@@ -59,10 +112,11 @@ export function serializeCookie(name: string, opts: CookieOptions): string {
59
112
  throw new Error("SameSite=None cookies require secure: true")
60
113
  }
61
114
 
62
- if (opts.domain && /[;\r\n]/.test(opts.domain)) {
115
+ if (opts.domain && !validDomainRe.test(opts.domain)) {
63
116
  throw new Error("Cookie domain contains invalid characters")
64
117
  }
65
- if (path && /[;\r\n]/.test(path)) {
118
+ /* av-value: any CHAR except CTLs or ";" (RFC 6265 §4.1.1). */
119
+ if (path && /[^\x20-\x3a\x3c-\x7e]/.test(path)) {
66
120
  throw new Error("Cookie path contains invalid characters")
67
121
  }
68
122
 
@@ -73,7 +127,8 @@ export function serializeCookie(name: string, opts: CookieOptions): string {
73
127
  if (!Number.isFinite(opts.maxAge) || opts.maxAge < 0) {
74
128
  throw new Error(`Invalid cookie Max-Age: ${opts.maxAge}`)
75
129
  }
76
- cookie += `; Max-Age=${Math.floor(opts.maxAge)}`
130
+ /* Clamp so the value prints as digits; browsers cap Max-Age at 400 days anyway. */
131
+ cookie += `; Max-Age=${Math.min(Math.floor(opts.maxAge), Number.MAX_SAFE_INTEGER)}`
77
132
  }
78
133
  if (opts.expires) {
79
134
  if (Number.isNaN(opts.expires.getTime())) {
package/src/cors.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import { namedMiddleware } from "./middleware.ts"
2
- import { replaceResponse } from "./honey-response.ts"
3
2
  import type { MiddlewareFn } from "./middleware.ts"
3
+ import { withHeaders } from "./with-headers.ts"
4
4
 
5
5
  export type CORSOptions = {
6
+ /** Requires an explicit `origin` list, string or predicate; `"*"` or no origin throws. */
6
7
  credentials?: boolean
7
8
  exposeHeaders?: string[]
8
9
  headers?: string[]
@@ -14,79 +15,79 @@ export type CORSOptions = {
14
15
  const DEFAULT_METHODS = ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"]
15
16
  const DEFAULT_MAX_AGE = 86400
16
17
 
18
+ /**
19
+ * Returns the value for `Access-Control-Allow-Origin`, or null when the origin
20
+ * is not allowed. The opaque origin `null` (sandboxed iframes, `file:`, some
21
+ * redirects) is never reflected: any page can produce it.
22
+ */
17
23
  function matchOrigin(origin: string, config: CORSOptions["origin"]): string | null {
18
24
  if (config === undefined || config === "*") return "*"
25
+ if (origin === "null") return null
19
26
  if (typeof config === "string") return config === origin ? origin : null
20
27
  if (Array.isArray(config)) return config.includes(origin) ? origin : null
21
- if (typeof config === "function") return config(origin) ? origin : null
22
- return null
28
+ return config(origin) ? origin : null
29
+ }
30
+
31
+ /** Append `value` to `Vary` unless it is already listed (or `*`). */
32
+ function addVary(headers: Headers, value: string): void {
33
+ const current = headers.get("vary")
34
+ if (current === null || current.trim() === "") {
35
+ headers.set("vary", value)
36
+ return
37
+ }
38
+ const tokens = current.split(",").map((t) => t.trim().toLowerCase())
39
+ if (tokens.includes("*") || tokens.includes(value.toLowerCase())) return
40
+ headers.set("vary", `${current}, ${value}`)
23
41
  }
24
42
 
25
43
  export function cors(options?: CORSOptions): MiddlewareFn<{ req: Request }, {}> {
26
44
  const opts = options ?? {}
27
45
 
28
- /* credentials + wildcard is a CORS spec violation — auto-narrow to echo origin */
29
- const credentialWildcard = opts.credentials === true && (opts.origin === undefined || opts.origin === "*")
46
+ if (opts.credentials === true && (opts.origin === undefined || opts.origin === "*")) {
47
+ throw new TypeError(
48
+ "cors: `credentials: true` needs an explicit `origin` (string, list or predicate). A wildcard would let every site make credentialed requests and read the response.",
49
+ )
50
+ }
51
+
52
+ /* `Vary: Origin` whenever the answer depends on the request's Origin — that is,
53
+ * on every response unless the policy is a plain wildcard. Without it a shared
54
+ * cache can serve one origin's ACAO (or its absence) to another. */
55
+ const varies = !(opts.origin === undefined || opts.origin === "*")
56
+ const methods = (opts.methods ?? DEFAULT_METHODS).join(", ")
57
+ const allowHeaders = opts.headers && opts.headers.length > 0 ? opts.headers.join(", ") : null
58
+ const exposeHeaders = opts.exposeHeaders && opts.exposeHeaders.length > 0 ? opts.exposeHeaders.join(", ") : null
59
+ const maxAge = String(opts.maxAge ?? DEFAULT_MAX_AGE)
30
60
 
31
61
  const mw: MiddlewareFn<{ req: Request }, {}> = async (ctx, next) => {
32
62
  const req = ctx.req
33
63
  const requestOrigin = req.headers.get("origin")
64
+ const allowedOrigin = requestOrigin === null ? null : matchOrigin(requestOrigin, opts.origin)
34
65
 
35
- /* no origin → not a CORS request */
36
- if (requestOrigin === null) {
37
- return next()
38
- }
39
-
40
- const allowedOrigin = credentialWildcard ? requestOrigin : matchOrigin(requestOrigin, opts.origin)
41
-
42
- /* origin not allowed */
43
- if (allowedOrigin === null) {
44
- return next()
45
- }
46
-
47
- const isPreflight = req.method === "OPTIONS" && req.headers.has("access-control-request-method")
48
-
49
- /* Vary: Origin whenever response depends on the request origin (non-wildcard) */
50
- const needsVary = allowedOrigin !== "*"
51
-
52
- if (isPreflight) {
53
- const corsHeaders = new Headers()
54
- corsHeaders.set("access-control-allow-origin", allowedOrigin)
55
- corsHeaders.set("access-control-allow-methods", (opts.methods ?? DEFAULT_METHODS).join(", "))
56
- if (opts.headers && opts.headers.length > 0) {
57
- corsHeaders.set("access-control-allow-headers", opts.headers.join(", "))
66
+ if (allowedOrigin !== null && req.method === "OPTIONS" && req.headers.has("access-control-request-method")) {
67
+ const headers = new Headers()
68
+ headers.set("access-control-allow-origin", allowedOrigin)
69
+ headers.set("access-control-allow-methods", methods)
70
+ if (allowHeaders !== null) {
71
+ headers.set("access-control-allow-headers", allowHeaders)
58
72
  } else {
59
73
  const requested = req.headers.get("access-control-request-headers")
60
- if (requested) {
61
- corsHeaders.set("access-control-allow-headers", requested)
62
- }
63
- }
64
- corsHeaders.set("access-control-max-age", String(opts.maxAge ?? DEFAULT_MAX_AGE))
65
- if (opts.credentials) {
66
- corsHeaders.set("access-control-allow-credentials", "true")
74
+ if (requested) headers.set("access-control-allow-headers", requested)
75
+ addVary(headers, "Access-Control-Request-Headers")
67
76
  }
68
- if (needsVary) {
69
- corsHeaders.set("vary", "Origin")
70
- }
71
- return new Response(null, { headers: corsHeaders, status: 204 })
77
+ headers.set("access-control-max-age", maxAge)
78
+ if (opts.credentials) headers.set("access-control-allow-credentials", "true")
79
+ if (varies) addVary(headers, "Origin")
80
+ return new Response(null, { headers, status: 204 })
72
81
  }
73
82
 
74
- /* simple/actual request */
75
83
  const response = await next()
76
- const headers = new Headers(response.headers)
77
- headers.set("access-control-allow-origin", allowedOrigin)
78
- if (opts.credentials) {
79
- headers.set("access-control-allow-credentials", "true")
80
- }
81
- if (opts.exposeHeaders && opts.exposeHeaders.length > 0) {
82
- headers.set("access-control-expose-headers", opts.exposeHeaders.join(", "))
83
- }
84
- if (needsVary) {
85
- headers.append("vary", "Origin")
86
- }
87
- return replaceResponse(response, {
88
- headers,
89
- status: response.status,
84
+ if (allowedOrigin === null && !varies) return response
85
+ return withHeaders(response, (headers) => {
86
+ if (varies) addVary(headers, "Origin")
87
+ if (allowedOrigin === null) return
88
+ headers.set("access-control-allow-origin", allowedOrigin)
89
+ if (opts.credentials) headers.set("access-control-allow-credentials", "true")
90
+ if (exposeHeaders !== null) headers.set("access-control-expose-headers", exposeHeaders)
90
91
  })
91
92
  }
92
93