@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
@@ -10,8 +10,16 @@ import (
10
10
  "errors"
11
11
  "fmt"
12
12
  "io"
13
+ "math"
14
+ "mime/multipart"
13
15
  "net/http"
16
+ "net/textproto"
14
17
  "net/url"
18
+ "reflect"
19
+ "sort"
20
+ "strconv"
21
+ "strings"
22
+ "sync"
15
23
  "time"
16
24
  )
17
25
 
@@ -48,21 +56,102 @@ type ResponseContext struct {
48
56
  }
49
57
 
50
58
  // Config holds all configuration for the SDK client.
59
+ //
60
+ // Timeout bounds a whole regular request, including reading the response body.
61
+ // Streams (SSE, WebSocket, realtime) are not bounded by it. Zero means 30s; a
62
+ // negative value disables the timeout.
63
+ //
64
+ // HTTPClient defaults to a client that does not follow redirects to another
65
+ // host (the 3xx response is returned as an error instead), so custom auth
66
+ // headers never reach a different origin.
67
+ //
68
+ // MaxResponseBytes caps a buffered response body. Zero means 100 MiB; a
69
+ // negative value disables the cap.
51
70
  type Config struct {
52
- BaseURL string
53
- BearerToken string
54
- Headers map[string]string
55
- Timeout time.Duration
56
- ThrowOnError bool
57
- StaleMaxEntries int
58
- Invalidation *InvalidationConfig
59
- OnAuthExpired func(context.Context) (string, error)
60
- HTTPClient *http.Client
61
- AuthHeaderName string
71
+ BaseURL string
72
+ BearerToken string
73
+ Headers map[string]string
74
+ Timeout time.Duration
75
+ ThrowOnError bool
76
+ StaleMaxEntries int
77
+ Invalidation *InvalidationConfig
78
+ OnAuthExpired func(context.Context) (string, error)
79
+ HTTPClient *http.Client
80
+ AuthHeaderName string
62
81
  AuthHeaderPrefix string
63
- OnRequest []func(*RequestContext) error
64
- OnResponse []func(*ResponseContext) error
65
- OnLog func(LogEntry)
82
+ OnRequest []func(*RequestContext) error
83
+ OnResponse []func(*ResponseContext) error
84
+ OnLog func(LogEntry)
85
+ MaxResponseBytes int64
86
+
87
+ auth *authState
88
+ }
89
+
90
+ const (
91
+ defaultTimeout = 30 * time.Second
92
+ defaultMaxResponseBytes = 100 << 20
93
+ maxErrorMessageRunes = 512
94
+ )
95
+
96
+ // authState holds the current bearer token so a refreshed token is reused by
97
+ // later calls, and serializes refreshes so N concurrent 401s refresh once.
98
+ type authState struct {
99
+ mu sync.Mutex
100
+ token string
101
+ }
102
+
103
+ func (a *authState) current(cfg Config) string {
104
+ if a == nil {
105
+ return cfg.BearerToken
106
+ }
107
+ a.mu.Lock()
108
+ defer a.mu.Unlock()
109
+ return a.token
110
+ }
111
+
112
+ // refresh calls OnAuthExpired unless another call already replaced rejected.
113
+ func (a *authState) refresh(ctx context.Context, cfg Config, rejected string) (string, error) {
114
+ if a == nil {
115
+ return cfg.OnAuthExpired(ctx)
116
+ }
117
+ a.mu.Lock()
118
+ defer a.mu.Unlock()
119
+ if a.token != rejected {
120
+ return a.token, nil
121
+ }
122
+ token, err := cfg.OnAuthExpired(ctx)
123
+ if err != nil {
124
+ return "", err
125
+ }
126
+ a.token = token
127
+ return token, nil
128
+ }
129
+
130
+ // prepareConfig fills defaults. NewClient calls it once.
131
+ func prepareConfig(cfg Config) Config {
132
+ if cfg.HTTPClient == nil {
133
+ cfg.HTTPClient = &http.Client{CheckRedirect: sameHostRedirects}
134
+ }
135
+ if cfg.Timeout == 0 {
136
+ cfg.Timeout = defaultTimeout
137
+ }
138
+ if cfg.MaxResponseBytes == 0 {
139
+ cfg.MaxResponseBytes = defaultMaxResponseBytes
140
+ }
141
+ cfg.auth = &authState{token: cfg.BearerToken}
142
+ return cfg
143
+ }
144
+
145
+ // sameHostRedirects follows redirects on the same scheme and host only.
146
+ func sameHostRedirects(req *http.Request, via []*http.Request) error {
147
+ if len(via) >= 10 {
148
+ return errors.New("honey: stopped after 10 redirects")
149
+ }
150
+ first := via[0].URL
151
+ if req.URL.Scheme != first.Scheme || req.URL.Host != first.Host {
152
+ return http.ErrUseLastResponse
153
+ }
154
+ return nil
66
155
  }
67
156
 
68
157
  // LogEntry is the payload passed to Config.OnLog. Consumers plug in their own
@@ -116,6 +205,395 @@ type requestResult struct {
116
205
  resp *http.Response
117
206
  }
118
207
 
208
+ // requestBody is one request payload. JSON values and byte payloads can be
209
+ // sent again on an auth retry; a Reader cannot.
210
+ type requestBody struct {
211
+ json any
212
+ hasJSON bool
213
+ raw []byte
214
+ hasRaw bool
215
+ reader io.Reader
216
+ contentType string
217
+ }
218
+
219
+ func noBody() requestBody { return requestBody{} }
220
+
221
+ func jsonBody(v any) requestBody { return requestBody{json: v, hasJSON: true} }
222
+
223
+ func readerBody(r io.Reader, contentType string) requestBody {
224
+ return requestBody{reader: r, contentType: contentType}
225
+ }
226
+
227
+ func formBody(values url.Values) requestBody {
228
+ return requestBody{raw: []byte(values.Encode()), hasRaw: true, contentType: "application/x-www-form-urlencoded"}
229
+ }
230
+
231
+ func rawBody(raw []byte, contentType string) requestBody {
232
+ return requestBody{raw: raw, hasRaw: true, contentType: contentType}
233
+ }
234
+
235
+ // setHeaderValue sets a header parameter: nil and nil pointers are skipped,
236
+ // slices are joined with commas.
237
+ func setHeaderValue(h map[string]string, name string, v any) {
238
+ var q queryList
239
+ q.set(name, v)
240
+ if len(q) == 0 {
241
+ return
242
+ }
243
+ values := make([]string, 0, len(q))
244
+ for _, p := range q {
245
+ values = append(values, p.value)
246
+ }
247
+ h[name] = strings.Join(values, ",")
248
+ }
249
+
250
+ // hasHeader reports whether h has name in any casing.
251
+ func hasHeader(h map[string]string, name string) bool {
252
+ for k := range h {
253
+ if strings.EqualFold(k, name) {
254
+ return true
255
+ }
256
+ }
257
+ return false
258
+ }
259
+
260
+ func (b requestBody) present() bool { return b.hasJSON || b.hasRaw || b.reader != nil }
261
+
262
+ func (b requestBody) replayable() bool { return b.reader == nil }
263
+
264
+ // bytes returns the encoded payload for JSON and raw bodies.
265
+ func (b requestBody) bytes() ([]byte, error) {
266
+ if b.hasRaw {
267
+ return b.raw, nil
268
+ }
269
+ if b.hasJSON {
270
+ encoded, err := json.Marshal(b.json)
271
+ if err != nil {
272
+ return nil, fmt.Errorf("honey: marshal body: %w", err)
273
+ }
274
+ return encoded, nil
275
+ }
276
+ return nil, nil
277
+ }
278
+
279
+ // FilePart is one file in a multipart/form-data upload. The SDK reads Data
280
+ // fully before sending, so the request can be retried after an auth refresh.
281
+ type FilePart struct {
282
+ Filename string
283
+ ContentType string
284
+ Data io.Reader
285
+ }
286
+
287
+ type multipartField struct {
288
+ name string
289
+ value string
290
+ file *FilePart
291
+ }
292
+
293
+ func textField(name string, value any) multipartField {
294
+ return multipartField{name: name, value: formatQueryValue(value)}
295
+ }
296
+
297
+ func fileField(name string, file FilePart) multipartField {
298
+ return multipartField{name: name, file: &file}
299
+ }
300
+
301
+ func multipartBody(fields []multipartField) (requestBody, error) {
302
+ var buf bytes.Buffer
303
+ w := multipart.NewWriter(&buf)
304
+ for _, f := range fields {
305
+ if f.file == nil {
306
+ if err := w.WriteField(f.name, f.value); err != nil {
307
+ return requestBody{}, err
308
+ }
309
+ continue
310
+ }
311
+ h := make(textproto.MIMEHeader)
312
+ filename := f.file.Filename
313
+ if filename == "" {
314
+ filename = f.name
315
+ }
316
+ h.Set("Content-Disposition", fmt.Sprintf(`form-data; name=%s; filename=%s`, strconv.Quote(f.name), strconv.Quote(filename)))
317
+ ct := f.file.ContentType
318
+ if ct == "" {
319
+ ct = "application/octet-stream"
320
+ }
321
+ h.Set("Content-Type", ct)
322
+ part, err := w.CreatePart(h)
323
+ if err != nil {
324
+ return requestBody{}, err
325
+ }
326
+ if f.file.Data != nil {
327
+ if _, err := io.Copy(part, f.file.Data); err != nil {
328
+ return requestBody{}, err
329
+ }
330
+ }
331
+ }
332
+ if err := w.Close(); err != nil {
333
+ return requestBody{}, err
334
+ }
335
+ return requestBody{raw: buf.Bytes(), hasRaw: true, contentType: w.FormDataContentType()}, nil
336
+ }
337
+
338
+ // PathParamError reports a path parameter that is missing, or that would
339
+ // change the request path once a URL parser normalizes it ("", "." and "..").
340
+ type PathParamError struct {
341
+ Name string
342
+ Value string
343
+ Missing bool
344
+ }
345
+
346
+ func (e *PathParamError) Error() string {
347
+ if e.Missing {
348
+ return "Missing path param: " + e.Name
349
+ }
350
+ return fmt.Sprintf("Invalid path param %q: %q is not a path segment", e.Name, e.Value)
351
+ }
352
+
353
+ func percentEncode(s string, keep func(c byte) bool, spaceAsPlus bool) string {
354
+ const hex = "0123456789ABCDEF"
355
+ var b strings.Builder
356
+ for i := 0; i < len(s); i++ {
357
+ c := s[i]
358
+ switch {
359
+ case (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || keep(c):
360
+ b.WriteByte(c)
361
+ case c == ' ' && spaceAsPlus:
362
+ b.WriteByte('+')
363
+ default:
364
+ b.WriteByte('%')
365
+ b.WriteByte(hex[c>>4])
366
+ b.WriteByte(hex[c&0x0f])
367
+ }
368
+ }
369
+ return b.String()
370
+ }
371
+
372
+ // escapePathSegment encodes like JavaScript's encodeURIComponent, so every
373
+ // SDK language (and the TypeScript client) sends the same path bytes.
374
+ func escapePathSegment(s string) string {
375
+ return percentEncode(s, func(c byte) bool { return strings.IndexByte("-_.!~*'()", c) >= 0 }, false)
376
+ }
377
+
378
+ // escapeQueryComponent encodes like URLSearchParams (space becomes '+').
379
+ func escapeQueryComponent(s string) string {
380
+ return percentEncode(s, func(c byte) bool { return strings.IndexByte("*-._", c) >= 0 }, true)
381
+ }
382
+
383
+ func encodeSegmentValue(name, value string) (string, error) {
384
+ if value == "" || value == "." || value == ".." {
385
+ return "", &PathParamError{Name: name, Value: value}
386
+ }
387
+ return escapePathSegment(value), nil
388
+ }
389
+
390
+ // expandPath fills a path template, segment by segment, with the same grammar
391
+ // as the TypeScript client: `:name` (or `:name?`) takes a whole segment,
392
+ // `*name` (or `*`) as the last segment takes the rest of the path, `{name}`
393
+ // may appear anywhere inside a segment. Values are encoded; values that a URL
394
+ // parser would collapse ("", ".", "..") are rejected.
395
+ func expandPath(template string, params map[string]string) (string, error) {
396
+ segments := strings.Split(template, "/")
397
+ out := make([]string, 0, len(segments))
398
+ for i, seg := range segments {
399
+ switch {
400
+ case len(seg) > 1 && seg[0] == ':':
401
+ optional := strings.HasSuffix(seg, "?")
402
+ name := strings.TrimSuffix(seg[1:], "?")
403
+ if !optional {
404
+ name = seg[1:]
405
+ }
406
+ value, ok := params[name]
407
+ if !ok {
408
+ if optional {
409
+ continue
410
+ }
411
+ return "", &PathParamError{Name: name, Missing: true}
412
+ }
413
+ enc, err := encodeSegmentValue(name, value)
414
+ if err != nil {
415
+ return "", err
416
+ }
417
+ out = append(out, enc)
418
+ case len(seg) > 0 && seg[0] == '*' && i == len(segments)-1:
419
+ name := "*"
420
+ if len(seg) > 1 {
421
+ name = seg[1:]
422
+ }
423
+ value, ok := params[name]
424
+ if !ok {
425
+ if len(seg) == 1 {
426
+ out = append(out, "")
427
+ continue
428
+ }
429
+ return "", &PathParamError{Name: name, Missing: true}
430
+ }
431
+ if value == "" {
432
+ out = append(out, "")
433
+ continue
434
+ }
435
+ parts := strings.Split(value, "/")
436
+ for j, part := range parts {
437
+ enc, err := encodeSegmentValue(name, part)
438
+ if err != nil {
439
+ return "", err
440
+ }
441
+ parts[j] = enc
442
+ }
443
+ out = append(out, strings.Join(parts, "/"))
444
+ default:
445
+ var b strings.Builder
446
+ rest := seg
447
+ for {
448
+ open := strings.IndexByte(rest, '{')
449
+ end := -1
450
+ if open >= 0 {
451
+ end = strings.IndexByte(rest[open:], '}')
452
+ }
453
+ if open < 0 || end < 0 || strings.ContainsRune(rest[open+1:open+end], '{') {
454
+ b.WriteString(rest)
455
+ break
456
+ }
457
+ name := rest[open+1 : open+end]
458
+ value, ok := params[name]
459
+ if !ok {
460
+ return "", &PathParamError{Name: name, Missing: true}
461
+ }
462
+ enc, err := encodeSegmentValue(name, value)
463
+ if err != nil {
464
+ return "", err
465
+ }
466
+ b.WriteString(rest[:open])
467
+ b.WriteString(enc)
468
+ rest = rest[open+end+1:]
469
+ }
470
+ out = append(out, b.String())
471
+ }
472
+ }
473
+ return strings.Join(out, "/"), nil
474
+ }
475
+
476
+ // queryPair is one key=value in send order.
477
+ type queryPair struct {
478
+ key string
479
+ value string
480
+ }
481
+
482
+ type queryList []queryPair
483
+
484
+ // set appends v under key: nil and nil pointers are skipped, slices repeat the key.
485
+ func (q *queryList) set(key string, v any) {
486
+ rv := reflect.ValueOf(v)
487
+ for rv.IsValid() && (rv.Kind() == reflect.Pointer || rv.Kind() == reflect.Interface) {
488
+ if rv.IsNil() {
489
+ return
490
+ }
491
+ rv = rv.Elem()
492
+ }
493
+ if !rv.IsValid() {
494
+ return
495
+ }
496
+ if (rv.Kind() == reflect.Slice || rv.Kind() == reflect.Array) && rv.Type().Elem().Kind() != reflect.Uint8 {
497
+ for i := 0; i < rv.Len(); i++ {
498
+ q.set(key, rv.Index(i).Interface())
499
+ }
500
+ return
501
+ }
502
+ *q = append(*q, queryPair{key: key, value: formatQueryValue(rv.Interface())})
503
+ }
504
+
505
+ func (q queryList) encode() string {
506
+ parts := make([]string, 0, len(q))
507
+ for _, p := range q {
508
+ parts = append(parts, escapeQueryComponent(p.key)+"="+escapeQueryComponent(p.value))
509
+ }
510
+ return strings.Join(parts, "&")
511
+ }
512
+
513
+ // formatQueryValue renders a scalar the way every SDK language does: strings
514
+ // as-is, booleans as true/false, numbers in shortest decimal form, anything
515
+ // else as JSON.
516
+ func formatQueryValue(v any) string {
517
+ rv := reflect.ValueOf(v)
518
+ switch rv.Kind() {
519
+ case reflect.String:
520
+ return rv.String()
521
+ case reflect.Bool:
522
+ return strconv.FormatBool(rv.Bool())
523
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
524
+ return strconv.FormatInt(rv.Int(), 10)
525
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
526
+ return strconv.FormatUint(rv.Uint(), 10)
527
+ case reflect.Float32, reflect.Float64:
528
+ return formatFloatJS(rv.Float())
529
+ }
530
+ encoded, err := json.Marshal(v)
531
+ if err != nil {
532
+ return fmt.Sprint(v)
533
+ }
534
+ return string(encoded)
535
+ }
536
+
537
+ // formatFloatJS renders a float like JavaScript's Number#toString.
538
+ func formatFloatJS(f float64) string {
539
+ switch {
540
+ case math.IsNaN(f):
541
+ return "NaN"
542
+ case math.IsInf(f, 1):
543
+ return "Infinity"
544
+ case math.IsInf(f, -1):
545
+ return "-Infinity"
546
+ case f == 0:
547
+ return "0"
548
+ }
549
+ abs := math.Abs(f)
550
+ if abs >= 1e-6 && abs < 1e21 {
551
+ return strconv.FormatFloat(f, 'f', -1, 64)
552
+ }
553
+ s := strconv.FormatFloat(f, 'e', -1, 64)
554
+ mantissa, exp, _ := strings.Cut(s, "e")
555
+ n, _ := strconv.Atoi(exp)
556
+ sign := "+"
557
+ if n < 0 {
558
+ sign = "-"
559
+ n = -n
560
+ }
561
+ return mantissa + "e" + sign + strconv.Itoa(n)
562
+ }
563
+
564
+ // buildURL joins base and an already-escaped path, keeping the base path and
565
+ // base query, then appends query pairs.
566
+ func buildURL(baseURL, path string, query queryList) (string, error) {
567
+ base, err := url.Parse(baseURL)
568
+ if err != nil {
569
+ return "", err
570
+ }
571
+ if base.Scheme == "" || base.Host == "" {
572
+ return "", fmt.Errorf("honey: base URL %q needs a scheme and host", baseURL)
573
+ }
574
+ if !strings.HasPrefix(path, "/") {
575
+ path = "/" + path
576
+ }
577
+ out := *base
578
+ out.Fragment = ""
579
+ out.RawFragment = ""
580
+ joined := strings.TrimRight(base.EscapedPath(), "/") + path
581
+ unescaped, err := url.PathUnescape(joined)
582
+ if err != nil {
583
+ return "", err
584
+ }
585
+ out.Path = unescaped
586
+ out.RawPath = joined
587
+ extra := query.encode()
588
+ switch {
589
+ case out.RawQuery == "":
590
+ out.RawQuery = extra
591
+ case extra != "":
592
+ out.RawQuery = out.RawQuery + "&" + extra
593
+ }
594
+ return out.String(), nil
595
+ }
596
+
119
597
  // doRequest executes an HTTP request, injects auth, merges headers, fires
120
598
  // OnRequest/OnResponse hooks in declaration order, handles OnAuthExpired retry
121
599
  // on 401, and wraps context cancellation errors. Emits OnLog lifecycle entries
@@ -126,11 +604,9 @@ func doRequest(
126
604
  ctx context.Context,
127
605
  cfg Config,
128
606
  method string,
129
- rawURL string,
130
- queryParams url.Values,
131
- bodyData interface{},
132
- bodyReader io.Reader,
133
- contentType string,
607
+ path string,
608
+ query queryList,
609
+ body requestBody,
134
610
  callHeaders map[string]string,
135
611
  operation string,
136
612
  requestMeta *RequestMeta,
@@ -152,6 +628,11 @@ func doRequest(
152
628
  if ctx.Err() != nil {
153
629
  return nil, fmt.Errorf("honey: request canceled: %w", ctx.Err())
154
630
  }
631
+ if cfg.Timeout > 0 {
632
+ var cancel context.CancelFunc
633
+ ctx, cancel = context.WithTimeout(ctx, cfg.Timeout)
634
+ defer cancel()
635
+ }
155
636
 
156
637
  emitLog(cfg, LogEntry{
157
638
  Level: "debug",
@@ -159,23 +640,23 @@ func doRequest(
159
640
  Operation: operation,
160
641
  })
161
642
 
162
- result, err = executeRequest(ctx, cfg, method, rawURL, queryParams, bodyData, bodyReader, contentType, callHeaders, cfg.BearerToken, false, requestMeta)
643
+ token := cfg.auth.current(cfg)
644
+ result, err = executeRequest(ctx, cfg, method, path, query, body, callHeaders, token, false, requestMeta)
163
645
  if err != nil {
164
646
  return nil, err
165
647
  }
166
648
 
167
- if result.status == 401 && cfg.OnAuthExpired != nil {
168
- newToken, authErr := cfg.OnAuthExpired(ctx)
649
+ if result.status == 401 && cfg.OnAuthExpired != nil && body.replayable() {
650
+ newToken, authErr := cfg.auth.refresh(ctx, cfg, token)
169
651
  if authErr != nil {
170
652
  err = fmt.Errorf("honey: OnAuthExpired failed: %w", authErr)
171
653
  return nil, err
172
654
  }
173
- /* streaming bodies are single-shot: once consumed by the first request,
174
- * retry has no way to replay. Documented as v1 non-goal — consumers
175
- * needing auto-retry on streamed uploads wrap in an outer retry loop. */
176
- result, err = executeRequest(ctx, cfg, method, rawURL, queryParams, bodyData, bodyReader, contentType, callHeaders, newToken, true, requestMeta)
177
- if err != nil {
178
- return nil, err
655
+ if newToken != "" {
656
+ result, err = executeRequest(ctx, cfg, method, path, query, body, callHeaders, newToken, true, requestMeta)
657
+ if err != nil {
658
+ return nil, err
659
+ }
179
660
  }
180
661
  }
181
662
 
@@ -200,44 +681,50 @@ func doRequest(
200
681
  return result, nil
201
682
  }
202
683
 
203
- func executeRequest(
684
+ func wrapTransportError(ctx context.Context, err error) error {
685
+ if ctx.Err() != nil {
686
+ if errors.Is(ctx.Err(), context.DeadlineExceeded) {
687
+ return fmt.Errorf("honey: request timed out: %w", ctx.Err())
688
+ }
689
+ if errors.Is(ctx.Err(), context.Canceled) {
690
+ return fmt.Errorf("honey: request canceled: %w", ctx.Err())
691
+ }
692
+ return fmt.Errorf("honey: context error: %w", ctx.Err())
693
+ }
694
+ return fmt.Errorf("honey: http do: %w", err)
695
+ }
696
+
697
+ // newRequest builds the outgoing request after running the OnRequest hooks.
698
+ func newRequest(
204
699
  ctx context.Context,
205
700
  cfg Config,
206
701
  method string,
207
- rawURL string,
208
- queryParams url.Values,
209
- bodyData interface{},
210
- bodyReader io.Reader,
211
- contentType string,
702
+ path string,
703
+ query queryList,
704
+ body requestBody,
212
705
  callHeaders map[string]string,
213
706
  bearerToken string,
214
707
  isRetry bool,
215
708
  requestMeta *RequestMeta,
216
- ) (*requestResult, error) {
217
- fullURL, err := buildURL(cfg.BaseURL, rawURL, queryParams)
709
+ ) (*http.Request, error) {
710
+ fullURL, err := buildURL(cfg.BaseURL, path, query)
218
711
  if err != nil {
219
712
  return nil, fmt.Errorf("honey: build URL: %w", err)
220
713
  }
221
714
 
222
- /* bodyReader wins over bodyData — streaming uploads bypass JSON marshal.
223
- * RequestContext.Body stays nil for streams so hook consumers don't try to
224
- * read or re-hash a single-shot reader. */
225
- var bodyBytes []byte
226
- if bodyReader == nil && bodyData != nil {
227
- encoded, encErr := json.Marshal(bodyData)
228
- if encErr != nil {
229
- return nil, fmt.Errorf("honey: marshal body: %w", encErr)
230
- }
231
- bodyBytes = encoded
715
+ /* Reader bodies stream as-is; RequestContext.Body stays nil for them so hook
716
+ * consumers don't try to read or re-hash a single-shot reader. */
717
+ bodyBytes, err := body.bytes()
718
+ if err != nil {
719
+ return nil, err
232
720
  }
233
- hasBody := bodyReader != nil || bodyData != nil
234
721
 
235
- headers := mergeHeaders(cfg, callHeaders, bearerToken, hasBody, contentType)
722
+ headers := mergeHeaders(cfg, callHeaders, bearerToken, body.present(), body.contentType)
236
723
 
237
724
  reqCtx := &RequestContext{
238
725
  Method: method,
239
726
  URL: fullURL,
240
- Path: rawURL,
727
+ Path: path,
241
728
  Headers: headers,
242
729
  Body: bodyBytes,
243
730
  IsRetry: isRetry,
@@ -255,8 +742,8 @@ func executeRequest(
255
742
  }
256
743
 
257
744
  var reader io.Reader
258
- if bodyReader != nil {
259
- reader = bodyReader
745
+ if body.reader != nil {
746
+ reader = body.reader
260
747
  } else if reqCtx.Body != nil {
261
748
  reader = bytes.NewReader(reqCtx.Body)
262
749
  }
@@ -266,31 +753,41 @@ func executeRequest(
266
753
  return nil, fmt.Errorf("honey: build request: %w", err)
267
754
  }
268
755
  req.Header = reqCtx.Headers
756
+ return req, nil
757
+ }
758
+
759
+ func executeRequest(
760
+ ctx context.Context,
761
+ cfg Config,
762
+ method string,
763
+ path string,
764
+ query queryList,
765
+ body requestBody,
766
+ callHeaders map[string]string,
767
+ bearerToken string,
768
+ isRetry bool,
769
+ requestMeta *RequestMeta,
770
+ ) (*requestResult, error) {
771
+ req, err := newRequest(ctx, cfg, method, path, query, body, callHeaders, bearerToken, isRetry, requestMeta)
772
+ if err != nil {
773
+ return nil, err
774
+ }
269
775
 
270
776
  resp, err := cfg.HTTPClient.Do(req)
271
777
  if err != nil {
272
- if ctx.Err() != nil {
273
- if errors.Is(ctx.Err(), context.DeadlineExceeded) {
274
- return nil, fmt.Errorf("honey: request timed out: %w", ctx.Err())
275
- }
276
- if errors.Is(ctx.Err(), context.Canceled) {
277
- return nil, fmt.Errorf("honey: request canceled: %w", ctx.Err())
278
- }
279
- return nil, fmt.Errorf("honey: context error: %w", ctx.Err())
280
- }
281
- return nil, fmt.Errorf("honey: http do: %w", err)
778
+ return nil, wrapTransportError(ctx, err)
282
779
  }
283
780
  defer resp.Body.Close()
284
781
 
285
- body, readErr := io.ReadAll(resp.Body)
782
+ respBody, readErr := readBody(resp.Body, cfg.MaxResponseBytes)
286
783
  if readErr != nil {
287
- return nil, fmt.Errorf("honey: read body: %w", readErr)
784
+ return nil, wrapTransportError(ctx, readErr)
288
785
  }
289
786
 
290
787
  respCtx := &ResponseContext{
291
788
  Status: resp.StatusCode,
292
789
  Headers: resp.Header,
293
- Body: body,
790
+ Body: respBody,
294
791
  IsRetry: isRetry,
295
792
  }
296
793
  if requestMeta != nil {
@@ -308,36 +805,88 @@ func executeRequest(
308
805
  return &requestResult{body: respCtx.Body, status: resp.StatusCode, resp: resp}, nil
309
806
  }
310
807
 
311
- func buildURL(baseURL, path string, queryParams url.Values) (string, error) {
312
- base, err := url.Parse(baseURL)
313
- if err != nil {
314
- return "", err
808
+ // ErrResponseTooLarge is returned when a buffered body exceeds Config.MaxResponseBytes.
809
+ var ErrResponseTooLarge = errors.New("honey: response body exceeds MaxResponseBytes")
810
+
811
+ func readBody(r io.Reader, limit int64) ([]byte, error) {
812
+ if limit < 0 {
813
+ return io.ReadAll(r)
315
814
  }
316
- ref, err := url.Parse(path)
815
+ body, err := io.ReadAll(io.LimitReader(r, limit+1))
317
816
  if err != nil {
318
- return "", err
817
+ return nil, err
319
818
  }
320
- resolved := base.ResolveReference(ref)
321
- if len(queryParams) > 0 {
322
- existing := resolved.Query()
323
- for k, vals := range queryParams {
324
- for _, v := range vals {
325
- existing.Add(k, v)
819
+ if int64(len(body)) > limit {
820
+ return nil, ErrResponseTooLarge
821
+ }
822
+ return body, nil
823
+ }
824
+
825
+ // streamClient is the configured client without its overall timeout: an
826
+ // http.Client Timeout also bounds body reads, which would cut every stream.
827
+ func streamClient(cfg Config) *http.Client {
828
+ c := *cfg.HTTPClient
829
+ c.Timeout = 0
830
+ return &c
831
+ }
832
+
833
+ // openStream sends a streaming request (SSE) through the same URL, header,
834
+ // auth and hook pipeline as a regular request. Non-2xx statuses become typed
835
+ // errors; the caller owns the returned body.
836
+ func openStream(
837
+ ctx context.Context,
838
+ cfg Config,
839
+ method string,
840
+ path string,
841
+ query queryList,
842
+ body requestBody,
843
+ callHeaders map[string]string,
844
+ ) (*http.Response, error) {
845
+ token := cfg.auth.current(cfg)
846
+ for attempt := 0; ; attempt++ {
847
+ req, err := newRequest(ctx, cfg, method, path, query, body, callHeaders, token, attempt > 0, nil)
848
+ if err != nil {
849
+ return nil, err
850
+ }
851
+ resp, err := streamClient(cfg).Do(req)
852
+ if err != nil {
853
+ return nil, wrapTransportError(ctx, err)
854
+ }
855
+ if resp.StatusCode == 401 && attempt == 0 && cfg.OnAuthExpired != nil && body.replayable() {
856
+ errBody, _ := readBody(resp.Body, 64<<10)
857
+ resp.Body.Close()
858
+ newToken, authErr := cfg.auth.refresh(ctx, cfg, token)
859
+ if authErr != nil {
860
+ return nil, fmt.Errorf("honey: OnAuthExpired failed: %w", authErr)
861
+ }
862
+ if newToken == "" {
863
+ return nil, raiseForStatus(resp, errBody)
326
864
  }
865
+ token = newToken
866
+ continue
327
867
  }
328
- resolved.RawQuery = existing.Encode()
868
+ if resp.StatusCode < 200 || resp.StatusCode >= 300 {
869
+ defer resp.Body.Close()
870
+ errBody, _ := readBody(resp.Body, 64<<10)
871
+ return nil, raiseForStatus(resp, errBody)
872
+ }
873
+ return resp, nil
329
874
  }
330
- return resolved.String(), nil
875
+ }
876
+
877
+ // authHeaders returns cfg.Headers plus the auth header, for transports that
878
+ // cannot run the full request pipeline (WebSocket dials).
879
+ func authHeaders(cfg Config, extra map[string]string) http.Header {
880
+ return mergeHeaders(cfg, extra, cfg.auth.current(cfg), false, "")
331
881
  }
332
882
 
333
883
  // mergeHeaders builds the final http.Header for a request by merging in order:
334
- // config Headers → call Headers (per-call wins per key) → Content-Type when
335
- // hasBody → Authorization when bearerToken non-empty → X-Request-Id when
336
- // absent (OnRequest hooks may still overwrite). Returned header is owned
337
- // by the caller and may be mutated freely (e.g. by OnRequest hooks).
338
- // contentType is the explicit Content-Type for the request body; when empty
339
- // and hasBody is true, defaults to application/json. Callers pass
340
- // application/octet-stream for streaming-upload ops.
884
+ // config Headers → call Headers (per-call wins per key, case-insensitively) →
885
+ // Content-Type when hasBody → Authorization when bearerToken non-empty →
886
+ // X-Request-Id when absent (OnRequest hooks may still overwrite). Returned
887
+ // header is owned by the caller and may be mutated freely (e.g. by OnRequest
888
+ // hooks). contentType is the explicit Content-Type for the request body; when
889
+ // empty and hasBody is true, defaults to application/json.
341
890
  func mergeHeaders(
342
891
  cfg Config,
343
892
  callHeaders map[string]string,
@@ -353,11 +902,11 @@ func mergeHeaders(
353
902
  }
354
903
  h.Set("Content-Type", ct)
355
904
  }
356
- for k, v := range cfg.Headers {
357
- h.Set(k, v)
905
+ for _, k := range sortedKeys(cfg.Headers) {
906
+ h.Set(k, cfg.Headers[k])
358
907
  }
359
- for k, v := range callHeaders {
360
- h.Set(k, v)
908
+ for _, k := range sortedKeys(callHeaders) {
909
+ h.Set(k, callHeaders[k])
361
910
  }
362
911
  if bearerToken != "" {
363
912
  name := cfg.AuthHeaderName
@@ -376,6 +925,15 @@ func mergeHeaders(
376
925
  return h
377
926
  }
378
927
 
928
+ func sortedKeys(m map[string]string) []string {
929
+ keys := make([]string, 0, len(m))
930
+ for k := range m {
931
+ keys = append(keys, k)
932
+ }
933
+ sort.Strings(keys)
934
+ return keys
935
+ }
936
+
379
937
  func unmarshalBody[T any](body []byte) (*T, error) {
380
938
  if len(body) == 0 {
381
939
  return nil, nil