@lovrozagar/honey 0.6.8 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (674) hide show
  1. package/README.md +514 -192
  2. package/bin/honey.js +25 -0
  3. package/dist/accepts.d.ts +7 -1
  4. package/dist/accepts.d.ts.map +1 -1
  5. package/dist/accepts.js +126 -0
  6. package/dist/accepts.js.map +1 -0
  7. package/dist/body-kind.d.ts +26 -0
  8. package/dist/body-kind.d.ts.map +1 -0
  9. package/dist/body-kind.js +70 -0
  10. package/dist/body-kind.js.map +1 -0
  11. package/dist/body-limit.d.ts +5 -1
  12. package/dist/body-limit.d.ts.map +1 -1
  13. package/dist/body-limit.js +126 -0
  14. package/dist/body-limit.js.map +1 -0
  15. package/dist/build/index.d.ts +19 -3
  16. package/dist/build/index.d.ts.map +1 -1
  17. package/dist/build/index.js +173 -0
  18. package/dist/build/index.js.map +1 -0
  19. package/dist/cli-args.d.ts +33 -0
  20. package/dist/cli-args.d.ts.map +1 -0
  21. package/dist/cli-args.js +102 -0
  22. package/dist/cli-args.js.map +1 -0
  23. package/dist/cli-go/auth.go +26 -0
  24. package/dist/cli-go/config.go +192 -0
  25. package/dist/cli-go/errors.go +75 -0
  26. package/dist/cli-go/multipart.go +79 -0
  27. package/dist/cli-go/output.go +163 -0
  28. package/dist/cli-go/root.go +30 -0
  29. package/dist/cli-go/stream.go +88 -0
  30. package/dist/cli-go/version.go +5 -0
  31. package/dist/cli.d.ts +1 -1
  32. package/dist/cli.js +209 -0
  33. package/dist/cli.js.map +1 -0
  34. package/dist/client/defaults.js +48 -0
  35. package/dist/client/defaults.js.map +1 -0
  36. package/dist/client/error.d.ts +35 -0
  37. package/dist/client/error.d.ts.map +1 -1
  38. package/dist/client/error.js +71 -0
  39. package/dist/client/error.js.map +1 -0
  40. package/dist/client/http.d.ts +67 -8
  41. package/dist/client/http.d.ts.map +1 -1
  42. package/dist/client/http.js +571 -0
  43. package/dist/client/http.js.map +1 -0
  44. package/dist/client/index.d.ts +3 -2
  45. package/dist/client/index.d.ts.map +1 -1
  46. package/dist/client/index.js +95 -0
  47. package/dist/client/index.js.map +1 -0
  48. package/dist/client/path.d.ts +26 -0
  49. package/dist/client/path.d.ts.map +1 -0
  50. package/dist/client/path.js +163 -0
  51. package/dist/client/path.js.map +1 -0
  52. package/dist/client/redirect-policy.d.ts +12 -0
  53. package/dist/client/redirect-policy.d.ts.map +1 -0
  54. package/dist/client/redirect-policy.js +20 -0
  55. package/dist/client/redirect-policy.js.map +1 -0
  56. package/dist/client/sdk.d.ts +36 -0
  57. package/dist/client/sdk.d.ts.map +1 -1
  58. package/dist/client/sdk.js +292 -0
  59. package/dist/client/sdk.js.map +1 -0
  60. package/dist/client/sse.d.ts +13 -0
  61. package/dist/client/sse.d.ts.map +1 -1
  62. package/dist/client/sse.js +129 -0
  63. package/dist/client/sse.js.map +1 -0
  64. package/dist/client/types.js +2 -0
  65. package/dist/client/types.js.map +1 -0
  66. package/dist/client/ws.d.ts +9 -2
  67. package/dist/client/ws.d.ts.map +1 -1
  68. package/dist/client/ws.js +112 -0
  69. package/dist/client/ws.js.map +1 -0
  70. package/dist/client-go/errors.go +236 -0
  71. package/dist/client-go/export.go +44 -0
  72. package/dist/client-go/invalidation.go +371 -0
  73. package/dist/client-go/nullable.go +62 -0
  74. package/dist/client-go/realtime.go +354 -0
  75. package/dist/client-go/result.go +20 -0
  76. package/dist/client-go/runtime.go +975 -0
  77. package/dist/client-go/sse.go +159 -0
  78. package/dist/client-go/transport.go +495 -0
  79. package/dist/client-go/ws.go +128 -0
  80. package/dist/client-info.d.ts +36 -0
  81. package/dist/client-info.d.ts.map +1 -0
  82. package/dist/client-info.js +53 -0
  83. package/dist/client-info.js.map +1 -0
  84. package/dist/client-mcp/README.md +10 -0
  85. package/dist/client-mcp/server.ts +221 -0
  86. package/dist/client-mcp/types.ts +19 -0
  87. package/dist/client-python/_errors.py +115 -0
  88. package/dist/client-python/_invalidation.py +494 -0
  89. package/dist/client-python/_realtime.py +179 -0
  90. package/dist/client-python/_runtime.py +860 -0
  91. package/dist/client-python/_sse.py +155 -0
  92. package/dist/client-python/_transport.py +366 -0
  93. package/dist/client-python/_ws.py +159 -0
  94. package/dist/client-python/py.typed +0 -0
  95. package/dist/client-rust/errors.rs +220 -0
  96. package/dist/client-rust/invalidation.rs +412 -0
  97. package/dist/client-rust/invalidation_sync.rs +93 -0
  98. package/dist/client-rust/nullable.rs +17 -0
  99. package/dist/client-rust/realtime.rs +702 -0
  100. package/dist/client-rust/result.rs +22 -0
  101. package/dist/client-rust/runtime.rs +980 -0
  102. package/dist/client-rust/runtime_sync.rs +484 -0
  103. package/dist/client-rust/sse.rs +188 -0
  104. package/dist/client-rust/ws.rs +167 -0
  105. package/dist/codegen-go-cli.d.ts.map +1 -1
  106. package/dist/codegen-go-cli.js +964 -0
  107. package/dist/codegen-go-cli.js.map +1 -0
  108. package/dist/codegen-go.d.ts +15 -3
  109. package/dist/codegen-go.d.ts.map +1 -1
  110. package/dist/codegen-go.js +872 -0
  111. package/dist/codegen-go.js.map +1 -0
  112. package/dist/codegen-hash.js +9 -0
  113. package/dist/codegen-hash.js.map +1 -0
  114. package/dist/codegen-ir.d.ts +69 -2
  115. package/dist/codegen-ir.d.ts.map +1 -1
  116. package/dist/codegen-ir.js +661 -0
  117. package/dist/codegen-ir.js.map +1 -0
  118. package/dist/codegen-lang.d.ts +65 -0
  119. package/dist/codegen-lang.d.ts.map +1 -0
  120. package/dist/codegen-lang.js +526 -0
  121. package/dist/codegen-lang.js.map +1 -0
  122. package/dist/codegen-load.d.ts +31 -0
  123. package/dist/codegen-load.d.ts.map +1 -0
  124. package/dist/codegen-load.js +138 -0
  125. package/dist/codegen-load.js.map +1 -0
  126. package/dist/codegen-loaders.js +55 -0
  127. package/dist/codegen-loaders.js.map +1 -0
  128. package/dist/codegen-mcp.d.ts.map +1 -1
  129. package/dist/codegen-mcp.js +368 -0
  130. package/dist/codegen-mcp.js.map +1 -0
  131. package/dist/codegen-python.d.ts +13 -1
  132. package/dist/codegen-python.d.ts.map +1 -1
  133. package/dist/codegen-python.js +828 -0
  134. package/dist/codegen-python.js.map +1 -0
  135. package/dist/codegen-route-tree-intern.d.ts +15 -5
  136. package/dist/codegen-route-tree-intern.d.ts.map +1 -1
  137. package/dist/codegen-route-tree-intern.js +221 -0
  138. package/dist/codegen-route-tree-intern.js.map +1 -0
  139. package/dist/codegen-rust.d.ts.map +1 -1
  140. package/dist/codegen-rust.js +1293 -0
  141. package/dist/codegen-rust.js.map +1 -0
  142. package/dist/codegen-sanitize.d.ts.map +1 -1
  143. package/dist/codegen-sanitize.js +56 -0
  144. package/dist/codegen-sanitize.js.map +1 -0
  145. package/dist/codegen-schema-naming.d.ts +5 -0
  146. package/dist/codegen-schema-naming.d.ts.map +1 -1
  147. package/dist/codegen-schema-naming.js +150 -0
  148. package/dist/codegen-schema-naming.js.map +1 -0
  149. package/dist/codegen-sdk-model.d.ts +71 -0
  150. package/dist/codegen-sdk-model.d.ts.map +1 -0
  151. package/dist/codegen-sdk-model.js +179 -0
  152. package/dist/codegen-sdk-model.js.map +1 -0
  153. package/dist/codegen.d.ts +41 -23
  154. package/dist/codegen.d.ts.map +1 -1
  155. package/dist/codegen.js +4610 -0
  156. package/dist/codegen.js.map +1 -0
  157. package/dist/context.d.ts +36 -6
  158. package/dist/context.d.ts.map +1 -1
  159. package/dist/context.js +242 -0
  160. package/dist/context.js.map +1 -0
  161. package/dist/cookie-sign.d.ts +16 -2
  162. package/dist/cookie-sign.d.ts.map +1 -1
  163. package/dist/cookie-sign.js +104 -0
  164. package/dist/cookie-sign.js.map +1 -0
  165. package/dist/cookie.d.ts +12 -0
  166. package/dist/cookie.d.ts.map +1 -1
  167. package/dist/cookie.js +138 -0
  168. package/dist/cookie.js.map +1 -0
  169. package/dist/cors.d.ts +1 -0
  170. package/dist/cors.d.ts.map +1 -1
  171. package/dist/cors.js +87 -0
  172. package/dist/cors.js.map +1 -0
  173. package/dist/crypto.d.ts +6 -1
  174. package/dist/crypto.d.ts.map +1 -1
  175. package/dist/crypto.js +38 -0
  176. package/dist/crypto.js.map +1 -0
  177. package/dist/csrf.d.ts +19 -0
  178. package/dist/csrf.d.ts.map +1 -1
  179. package/dist/csrf.js +79 -0
  180. package/dist/csrf.js.map +1 -0
  181. package/dist/curl-logger.d.ts +14 -3
  182. package/dist/curl-logger.d.ts.map +1 -1
  183. package/dist/curl-logger.js +199 -0
  184. package/dist/curl-logger.js.map +1 -0
  185. package/dist/detect-runtime.js +13 -0
  186. package/dist/detect-runtime.js.map +1 -0
  187. package/dist/dict.d.ts +11 -0
  188. package/dist/dict.d.ts.map +1 -0
  189. package/dist/dict.js +13 -0
  190. package/dist/dict.js.map +1 -0
  191. package/dist/error.d.ts.map +1 -1
  192. package/dist/error.js +44 -0
  193. package/dist/error.js.map +1 -0
  194. package/dist/errors.js +39 -0
  195. package/dist/errors.js.map +1 -0
  196. package/dist/etag.d.ts +2 -0
  197. package/dist/etag.d.ts.map +1 -1
  198. package/dist/etag.js +82 -0
  199. package/dist/etag.js.map +1 -0
  200. package/dist/feature-detect.d.ts +25 -0
  201. package/dist/feature-detect.d.ts.map +1 -0
  202. package/dist/feature-detect.js +144 -0
  203. package/dist/feature-detect.js.map +1 -0
  204. package/dist/feature-load.js +36 -0
  205. package/dist/feature-load.js.map +1 -0
  206. package/dist/feature-slots.d.ts +44 -0
  207. package/dist/feature-slots.d.ts.map +1 -0
  208. package/dist/feature-slots.js +48 -0
  209. package/dist/feature-slots.js.map +1 -0
  210. package/dist/fetch-request.d.ts +10 -0
  211. package/dist/fetch-request.d.ts.map +1 -0
  212. package/dist/fetch-request.js +13 -0
  213. package/dist/fetch-request.js.map +1 -0
  214. package/dist/gen-process.d.ts +15 -0
  215. package/dist/gen-process.d.ts.map +1 -0
  216. package/dist/gen-process.js +43 -0
  217. package/dist/gen-process.js.map +1 -0
  218. package/dist/gen-write.d.ts +19 -0
  219. package/dist/gen-write.d.ts.map +1 -0
  220. package/dist/gen-write.js +120 -0
  221. package/dist/gen-write.js.map +1 -0
  222. package/dist/glob.d.ts +21 -0
  223. package/dist/glob.d.ts.map +1 -0
  224. package/dist/glob.js +115 -0
  225. package/dist/glob.js.map +1 -0
  226. package/dist/go-type-emitter.d.ts +18 -4
  227. package/dist/go-type-emitter.d.ts.map +1 -1
  228. package/dist/go-type-emitter.js +594 -0
  229. package/dist/go-type-emitter.js.map +1 -0
  230. package/dist/honey-response.d.ts.map +1 -1
  231. package/dist/honey-response.js +338 -0
  232. package/dist/honey-response.js.map +1 -0
  233. package/dist/i18n-slot.d.ts +8 -0
  234. package/dist/i18n-slot.d.ts.map +1 -1
  235. package/dist/i18n-slot.js +14 -0
  236. package/dist/i18n-slot.js.map +1 -0
  237. package/dist/i18n.d.ts +3 -0
  238. package/dist/i18n.d.ts.map +1 -1
  239. package/dist/i18n.js +250 -0
  240. package/dist/i18n.js.map +1 -0
  241. package/dist/index.d.ts +408 -148
  242. package/dist/index.d.ts.map +1 -1
  243. package/dist/index.js +2324 -0
  244. package/dist/index.js.map +1 -0
  245. package/dist/init.d.ts +3 -5
  246. package/dist/init.d.ts.map +1 -1
  247. package/dist/init.js +140 -0
  248. package/dist/init.js.map +1 -0
  249. package/dist/input.js +4 -0
  250. package/dist/input.js.map +1 -0
  251. package/dist/invalidate-check.d.ts +11 -0
  252. package/dist/invalidate-check.d.ts.map +1 -1
  253. package/dist/invalidate-check.js +163 -0
  254. package/dist/invalidate-check.js.map +1 -0
  255. package/dist/invoke-user.d.ts +35 -0
  256. package/dist/invoke-user.d.ts.map +1 -0
  257. package/dist/invoke-user.js +73 -0
  258. package/dist/invoke-user.js.map +1 -0
  259. package/dist/ip-restrict.d.ts +13 -1
  260. package/dist/ip-restrict.d.ts.map +1 -1
  261. package/dist/ip-restrict.js +60 -0
  262. package/dist/ip-restrict.js.map +1 -0
  263. package/dist/ip.d.ts +39 -0
  264. package/dist/ip.d.ts.map +1 -0
  265. package/dist/ip.js +209 -0
  266. package/dist/ip.js.map +1 -0
  267. package/dist/logger.d.ts.map +1 -1
  268. package/dist/logger.js +113 -0
  269. package/dist/logger.js.map +1 -0
  270. package/dist/media-type.d.ts +30 -0
  271. package/dist/media-type.d.ts.map +1 -0
  272. package/dist/media-type.js +97 -0
  273. package/dist/media-type.js.map +1 -0
  274. package/dist/meta-spec-merge.d.ts +32 -0
  275. package/dist/meta-spec-merge.d.ts.map +1 -0
  276. package/dist/meta-spec-merge.js +68 -0
  277. package/dist/meta-spec-merge.js.map +1 -0
  278. package/dist/meta-spec.d.ts +16 -3
  279. package/dist/meta-spec.d.ts.map +1 -1
  280. package/dist/meta-spec.js +535 -0
  281. package/dist/meta-spec.js.map +1 -0
  282. package/dist/middleware.d.ts +23 -6
  283. package/dist/middleware.d.ts.map +1 -1
  284. package/dist/middleware.js +253 -0
  285. package/dist/middleware.js.map +1 -0
  286. package/dist/node-request.d.ts +57 -12
  287. package/dist/node-request.d.ts.map +1 -1
  288. package/dist/node-request.js +407 -0
  289. package/dist/node-request.js.map +1 -0
  290. package/dist/node.d.ts +20 -3
  291. package/dist/node.d.ts.map +1 -1
  292. package/dist/node.js +396 -0
  293. package/dist/node.js.map +1 -0
  294. package/dist/openapi/collect.d.ts +6 -3
  295. package/dist/openapi/collect.d.ts.map +1 -1
  296. package/dist/openapi/collect.js +30 -0
  297. package/dist/openapi/collect.js.map +1 -0
  298. package/dist/openapi/docs-page.d.ts +42 -0
  299. package/dist/openapi/docs-page.d.ts.map +1 -0
  300. package/dist/openapi/docs-page.js +80 -0
  301. package/dist/openapi/docs-page.js.map +1 -0
  302. package/dist/openapi/document.d.ts +4 -0
  303. package/dist/openapi/document.d.ts.map +1 -1
  304. package/dist/openapi/document.js +785 -0
  305. package/dist/openapi/document.js.map +1 -0
  306. package/dist/openapi/json-schema-slot.d.ts +7 -0
  307. package/dist/openapi/json-schema-slot.d.ts.map +1 -1
  308. package/dist/openapi/json-schema-slot.js +38 -0
  309. package/dist/openapi/json-schema-slot.js.map +1 -0
  310. package/dist/openapi/register.d.ts.map +1 -1
  311. package/dist/openapi/register.js +20 -0
  312. package/dist/openapi/register.js.map +1 -0
  313. package/dist/openapi/scalar.d.ts +3 -1
  314. package/dist/openapi/scalar.d.ts.map +1 -1
  315. package/dist/openapi/scalar.js +21 -0
  316. package/dist/openapi/scalar.js.map +1 -0
  317. package/dist/openapi/serve.d.ts +9 -0
  318. package/dist/openapi/serve.d.ts.map +1 -0
  319. package/dist/openapi/serve.js +36 -0
  320. package/dist/openapi/serve.js.map +1 -0
  321. package/dist/openapi/spec-factory.d.ts +30 -1
  322. package/dist/openapi/spec-factory.d.ts.map +1 -1
  323. package/dist/openapi/spec-factory.js +52 -0
  324. package/dist/openapi/spec-factory.js.map +1 -0
  325. package/dist/openapi/spec.d.ts +14 -5
  326. package/dist/openapi/spec.d.ts.map +1 -1
  327. package/dist/openapi/spec.js +50 -0
  328. package/dist/openapi/spec.js.map +1 -0
  329. package/dist/openapi/swagger.d.ts +3 -1
  330. package/dist/openapi/swagger.d.ts.map +1 -1
  331. package/dist/openapi/swagger.js +22 -0
  332. package/dist/openapi/swagger.js.map +1 -0
  333. package/dist/pattern.d.ts +71 -0
  334. package/dist/pattern.d.ts.map +1 -0
  335. package/dist/pattern.js +239 -0
  336. package/dist/pattern.js.map +1 -0
  337. package/dist/peer.d.ts +17 -0
  338. package/dist/peer.d.ts.map +1 -0
  339. package/dist/peer.js +50 -0
  340. package/dist/peer.js.map +1 -0
  341. package/dist/plugin.d.ts +67 -16
  342. package/dist/plugin.d.ts.map +1 -1
  343. package/dist/plugin.js +649 -0
  344. package/dist/plugin.js.map +1 -0
  345. package/dist/powered-by.d.ts.map +1 -1
  346. package/dist/powered-by.js +11 -0
  347. package/dist/powered-by.js.map +1 -0
  348. package/dist/pretty-json.d.ts.map +1 -1
  349. package/dist/pretty-json.js +47 -0
  350. package/dist/pretty-json.js.map +1 -0
  351. package/dist/producer-stream.d.ts +47 -0
  352. package/dist/producer-stream.d.ts.map +1 -0
  353. package/dist/producer-stream.js +267 -0
  354. package/dist/producer-stream.js.map +1 -0
  355. package/dist/proxy.d.ts +47 -8
  356. package/dist/proxy.d.ts.map +1 -1
  357. package/dist/proxy.js +274 -0
  358. package/dist/proxy.js.map +1 -0
  359. package/dist/python-type-emitter.d.ts +14 -1
  360. package/dist/python-type-emitter.d.ts.map +1 -1
  361. package/dist/python-type-emitter.js +157 -0
  362. package/dist/python-type-emitter.js.map +1 -0
  363. package/dist/realtime/bus.d.ts +17 -1
  364. package/dist/realtime/bus.d.ts.map +1 -1
  365. package/dist/realtime/bus.js +112 -0
  366. package/dist/realtime/bus.js.map +1 -0
  367. package/dist/realtime/register.d.ts +3 -0
  368. package/dist/realtime/register.d.ts.map +1 -0
  369. package/dist/realtime/register.js +10 -0
  370. package/dist/realtime/register.js.map +1 -0
  371. package/dist/realtime/route.d.ts +65 -26
  372. package/dist/realtime/route.d.ts.map +1 -1
  373. package/dist/realtime/route.js +72 -0
  374. package/dist/realtime/route.js.map +1 -0
  375. package/dist/realtime/server.d.ts +43 -0
  376. package/dist/realtime/server.d.ts.map +1 -0
  377. package/dist/realtime/server.js +255 -0
  378. package/dist/realtime/server.js.map +1 -0
  379. package/dist/request-id.d.ts +7 -0
  380. package/dist/request-id.d.ts.map +1 -1
  381. package/dist/request-id.js +16 -0
  382. package/dist/request-id.js.map +1 -0
  383. package/dist/request-limits.d.ts +3 -0
  384. package/dist/request-limits.d.ts.map +1 -0
  385. package/dist/request-limits.js +3 -0
  386. package/dist/request-limits.js.map +1 -0
  387. package/dist/request-path.d.ts +33 -0
  388. package/dist/request-path.d.ts.map +1 -0
  389. package/dist/request-path.js +178 -0
  390. package/dist/request-path.js.map +1 -0
  391. package/dist/request-to-curl.d.ts +7 -1
  392. package/dist/request-to-curl.d.ts.map +1 -1
  393. package/dist/request-to-curl.js +48 -0
  394. package/dist/request-to-curl.js.map +1 -0
  395. package/dist/response.d.ts +25 -2
  396. package/dist/response.d.ts.map +1 -1
  397. package/dist/response.js +313 -0
  398. package/dist/response.js.map +1 -0
  399. package/dist/rust-type-emitter.d.ts +14 -2
  400. package/dist/rust-type-emitter.d.ts.map +1 -1
  401. package/dist/rust-type-emitter.js +463 -0
  402. package/dist/rust-type-emitter.js.map +1 -0
  403. package/dist/secure-headers.d.ts.map +1 -1
  404. package/dist/secure-headers.js +53 -0
  405. package/dist/secure-headers.js.map +1 -0
  406. package/dist/serve-register.d.ts +4 -0
  407. package/dist/serve-register.d.ts.map +1 -1
  408. package/dist/serve-register.js +9 -0
  409. package/dist/serve-register.js.map +1 -0
  410. package/dist/serve-slot.d.ts +3 -0
  411. package/dist/serve-slot.d.ts.map +1 -1
  412. package/dist/serve-slot.js +52 -0
  413. package/dist/serve-slot.js.map +1 -0
  414. package/dist/serve.d.ts +18 -1
  415. package/dist/serve.d.ts.map +1 -1
  416. package/dist/serve.js +157 -0
  417. package/dist/serve.js.map +1 -0
  418. package/dist/server-timing.d.ts.map +1 -1
  419. package/dist/server-timing.js +51 -0
  420. package/dist/server-timing.js.map +1 -0
  421. package/dist/static.d.ts +9 -0
  422. package/dist/static.d.ts.map +1 -1
  423. package/dist/static.js +102 -0
  424. package/dist/static.js.map +1 -0
  425. package/dist/telemetry/otel.d.ts +2 -0
  426. package/dist/telemetry/otel.d.ts.map +1 -1
  427. package/dist/telemetry/otel.js +135 -0
  428. package/dist/telemetry/otel.js.map +1 -0
  429. package/dist/testing.d.ts +12 -0
  430. package/dist/testing.d.ts.map +1 -1
  431. package/dist/testing.js +124 -0
  432. package/dist/testing.js.map +1 -0
  433. package/dist/timeout.d.ts +6 -0
  434. package/dist/timeout.d.ts.map +1 -1
  435. package/dist/timeout.js +36 -0
  436. package/dist/timeout.js.map +1 -0
  437. package/dist/tree.d.ts +133 -36
  438. package/dist/tree.d.ts.map +1 -1
  439. package/dist/tree.js +437 -0
  440. package/dist/tree.js.map +1 -0
  441. package/dist/trust.d.ts +21 -0
  442. package/dist/trust.d.ts.map +1 -0
  443. package/dist/trust.js +100 -0
  444. package/dist/trust.js.map +1 -0
  445. package/dist/ts-type-emitter.d.ts.map +1 -1
  446. package/dist/ts-type-emitter.js +150 -0
  447. package/dist/ts-type-emitter.js.map +1 -0
  448. package/dist/type-emitter.d.ts +16 -2
  449. package/dist/type-emitter.d.ts.map +1 -1
  450. package/dist/type-emitter.js +729 -0
  451. package/dist/type-emitter.js.map +1 -0
  452. package/dist/type-extractor.d.ts +8 -4
  453. package/dist/type-extractor.d.ts.map +1 -1
  454. package/dist/type-extractor.js +776 -0
  455. package/dist/type-extractor.js.map +1 -0
  456. package/dist/types.d.ts +7 -0
  457. package/dist/types.d.ts.map +1 -1
  458. package/dist/types.js +96 -0
  459. package/dist/types.js.map +1 -0
  460. package/dist/validation.d.ts +14 -2
  461. package/dist/validation.d.ts.map +1 -1
  462. package/dist/validation.js +335 -0
  463. package/dist/validation.js.map +1 -0
  464. package/dist/with-headers.d.ts +15 -0
  465. package/dist/with-headers.d.ts.map +1 -0
  466. package/dist/with-headers.js +45 -0
  467. package/dist/with-headers.js.map +1 -0
  468. package/dist/ws/bun.d.ts +11 -5
  469. package/dist/ws/bun.d.ts.map +1 -1
  470. package/dist/ws/bun.js +82 -0
  471. package/dist/ws/bun.js.map +1 -0
  472. package/dist/ws/cloudflare.d.ts +25 -3
  473. package/dist/ws/cloudflare.d.ts.map +1 -1
  474. package/dist/ws/cloudflare.js +117 -0
  475. package/dist/ws/cloudflare.js.map +1 -0
  476. package/dist/ws/deno.d.ts +4 -4
  477. package/dist/ws/deno.d.ts.map +1 -1
  478. package/dist/ws/deno.js +133 -0
  479. package/dist/ws/deno.js.map +1 -0
  480. package/dist/ws/node.d.ts +20 -3
  481. package/dist/ws/node.d.ts.map +1 -1
  482. package/dist/ws/node.js +173 -0
  483. package/dist/ws/node.js.map +1 -0
  484. package/dist/ws/session.d.ts +11 -0
  485. package/dist/ws/session.d.ts.map +1 -0
  486. package/dist/ws/session.js +60 -0
  487. package/dist/ws/session.js.map +1 -0
  488. package/dist/ws/shared.d.ts +65 -0
  489. package/dist/ws/shared.d.ts.map +1 -0
  490. package/dist/ws/shared.js +103 -0
  491. package/dist/ws/shared.js.map +1 -0
  492. package/dist/ws-origin.d.ts +20 -0
  493. package/dist/ws-origin.d.ts.map +1 -0
  494. package/dist/ws-origin.js +41 -0
  495. package/dist/ws-origin.js.map +1 -0
  496. package/dist/yaml.d.ts +15 -1
  497. package/dist/yaml.d.ts.map +1 -1
  498. package/dist/yaml.js +101 -0
  499. package/dist/yaml.js.map +1 -0
  500. package/docs/invalidate-spec.md +13 -1
  501. package/docs/meta-spec.md +36 -12
  502. package/docs/packaging.md +43 -24
  503. package/docs/regression-matrix/README.md +32 -0
  504. package/docs/regression-matrix/review.md +44 -0
  505. package/docs/regression-matrix/ws1-3.md +97 -0
  506. package/docs/regression-matrix/ws4-8.md +107 -0
  507. package/docs/regression-matrix/ws9-12.md +266 -0
  508. package/docs/sdk.md +102 -52
  509. package/package.json +183 -52
  510. package/src/accepts.ts +91 -33
  511. package/src/body-kind.ts +67 -0
  512. package/src/body-limit.ts +102 -87
  513. package/src/build/index.ts +104 -54
  514. package/src/cli-args.ts +123 -0
  515. package/src/cli-go/auth.go +12 -1
  516. package/src/cli-go/config.go +29 -2
  517. package/src/cli-go/errors.go +28 -10
  518. package/src/cli-go/multipart.go +51 -27
  519. package/src/cli-go/output.go +58 -4
  520. package/src/cli-go/root.go +1 -1
  521. package/src/cli-go/version.go +2 -2
  522. package/src/cli.ts +154 -148
  523. package/src/client/error.ts +56 -1
  524. package/src/client/http.ts +479 -256
  525. package/src/client/index.ts +55 -17
  526. package/src/client/path.ts +169 -0
  527. package/src/client/redirect-policy.ts +19 -0
  528. package/src/client/sdk.ts +180 -101
  529. package/src/client/sse.ts +92 -66
  530. package/src/client/ws.ts +67 -31
  531. package/src/client-go/errors.go +41 -12
  532. package/src/client-go/export.go +44 -0
  533. package/src/client-go/invalidation.go +52 -54
  534. package/src/client-go/nullable.go +62 -0
  535. package/src/client-go/realtime.go +147 -110
  536. package/src/client-go/runtime.go +646 -88
  537. package/src/client-go/sse.go +104 -94
  538. package/src/client-go/transport.go +67 -27
  539. package/src/client-go/ws.go +49 -25
  540. package/src/client-info.ts +79 -0
  541. package/src/client-mcp/server.ts +133 -3
  542. package/src/client-python/_invalidation.py +207 -291
  543. package/src/client-python/_realtime.py +63 -40
  544. package/src/client-python/_runtime.py +501 -119
  545. package/src/client-python/_sse.py +130 -74
  546. package/src/client-python/_transport.py +37 -9
  547. package/src/client-python/_ws.py +50 -22
  548. package/src/client-rust/errors.rs +21 -1
  549. package/src/client-rust/invalidation.rs +202 -231
  550. package/src/client-rust/invalidation_sync.rs +16 -229
  551. package/src/client-rust/nullable.rs +17 -0
  552. package/src/client-rust/realtime.rs +217 -217
  553. package/src/client-rust/runtime.rs +602 -177
  554. package/src/client-rust/runtime_sync.rs +169 -145
  555. package/src/client-rust/sse.rs +142 -121
  556. package/src/client-rust/ws.rs +61 -37
  557. package/src/codegen-go-cli.ts +607 -728
  558. package/src/codegen-go.ts +710 -770
  559. package/src/codegen-ir.ts +381 -75
  560. package/src/codegen-lang.ts +517 -0
  561. package/src/codegen-load.ts +150 -0
  562. package/src/codegen-mcp.ts +207 -108
  563. package/src/codegen-python.ts +684 -816
  564. package/src/codegen-route-tree-intern.ts +51 -25
  565. package/src/codegen-rust.ts +946 -1240
  566. package/src/codegen-sanitize.ts +0 -4
  567. package/src/codegen-schema-naming.ts +19 -6
  568. package/src/codegen-sdk-model.ts +228 -0
  569. package/src/codegen.ts +1488 -1140
  570. package/src/context.ts +130 -11
  571. package/src/cookie-sign.ts +85 -16
  572. package/src/cookie.ts +66 -11
  573. package/src/cors.ts +55 -54
  574. package/src/crypto.ts +27 -5
  575. package/src/csrf.ts +57 -25
  576. package/src/curl-logger.ts +155 -114
  577. package/src/dict.ts +12 -0
  578. package/src/error.ts +4 -2
  579. package/src/etag.ts +58 -34
  580. package/src/feature-detect.ts +147 -0
  581. package/src/feature-slots.ts +70 -0
  582. package/src/fetch-request.ts +13 -0
  583. package/src/gen-process.ts +42 -0
  584. package/src/gen-write.ts +127 -0
  585. package/src/glob.ts +109 -0
  586. package/src/go-type-emitter.ts +406 -155
  587. package/src/honey-response.ts +36 -9
  588. package/src/i18n-slot.ts +10 -0
  589. package/src/i18n.ts +98 -23
  590. package/src/index.ts +1887 -1756
  591. package/src/init.ts +40 -28
  592. package/src/invalidate-check.ts +42 -4
  593. package/src/invoke-user.ts +95 -0
  594. package/src/ip-restrict.ts +47 -161
  595. package/src/ip.ts +194 -0
  596. package/src/logger.ts +65 -36
  597. package/src/media-type.ts +104 -0
  598. package/src/meta-spec-merge.ts +80 -0
  599. package/src/meta-spec.ts +0 -0
  600. package/src/middleware.ts +112 -12
  601. package/src/node-request.ts +242 -52
  602. package/src/node.ts +318 -75
  603. package/src/openapi/collect.ts +19 -44
  604. package/src/openapi/docs-page.ts +89 -0
  605. package/src/openapi/document.ts +503 -224
  606. package/src/openapi/json-schema-slot.ts +40 -0
  607. package/src/openapi/register.ts +7 -2
  608. package/src/openapi/scalar.ts +11 -10
  609. package/src/openapi/serve.ts +39 -0
  610. package/src/openapi/spec-factory.ts +50 -1
  611. package/src/openapi/spec.ts +53 -30
  612. package/src/openapi/swagger.ts +12 -11
  613. package/src/pattern.ts +245 -0
  614. package/src/peer.ts +53 -0
  615. package/src/plugin.ts +270 -185
  616. package/src/powered-by.ts +2 -2
  617. package/src/pretty-json.ts +25 -9
  618. package/src/producer-stream.ts +296 -0
  619. package/src/proxy.ts +253 -55
  620. package/src/python-type-emitter.ts +68 -56
  621. package/src/realtime/bus.ts +56 -9
  622. package/src/realtime/register.ts +13 -0
  623. package/src/realtime/route.ts +129 -80
  624. package/src/realtime/server.ts +298 -0
  625. package/src/request-id.ts +13 -3
  626. package/src/request-limits.ts +2 -0
  627. package/src/request-path.ts +165 -0
  628. package/src/request-to-curl.ts +28 -7
  629. package/src/response.ts +143 -103
  630. package/src/rust-type-emitter.ts +265 -170
  631. package/src/secure-headers.ts +8 -4
  632. package/src/serve-register.ts +4 -0
  633. package/src/serve-slot.ts +38 -0
  634. package/src/serve.ts +113 -25
  635. package/src/server-timing.ts +11 -7
  636. package/src/static.ts +85 -32
  637. package/src/telemetry/otel.ts +29 -7
  638. package/src/testing.ts +76 -14
  639. package/src/timeout.ts +15 -7
  640. package/src/tree.ts +405 -333
  641. package/src/trust.ts +117 -0
  642. package/src/ts-type-emitter.ts +18 -12
  643. package/src/type-emitter.ts +227 -89
  644. package/src/type-extractor.ts +562 -424
  645. package/src/types.ts +8 -0
  646. package/src/validation.ts +221 -96
  647. package/src/with-headers.ts +45 -0
  648. package/src/ws/bun.ts +53 -15
  649. package/src/ws/cloudflare.ts +81 -19
  650. package/src/ws/deno.ts +124 -36
  651. package/src/ws/node.ts +173 -79
  652. package/src/ws/session.ts +73 -0
  653. package/src/ws/shared.ts +140 -0
  654. package/src/ws-origin.ts +53 -0
  655. package/src/yaml.ts +45 -22
  656. package/dist/client-mcp/server.d.ts +0 -10
  657. package/dist/client-mcp/server.d.ts.map +0 -1
  658. package/dist/client-mcp/types.d.ts +0 -16
  659. package/dist/client-mcp/types.d.ts.map +0 -1
  660. package/dist/cloudflare/realtime.d.ts +0 -30
  661. package/dist/cloudflare/realtime.d.ts.map +0 -1
  662. package/dist/realtime/buffer.d.ts +0 -18
  663. package/dist/realtime/buffer.d.ts.map +0 -1
  664. package/dist/realtime/longpoll.d.ts +0 -23
  665. package/dist/realtime/longpoll.d.ts.map +0 -1
  666. package/dist/realtime/protocol.d.ts +0 -50
  667. package/dist/realtime/protocol.d.ts.map +0 -1
  668. package/dist/realtime/runtime.d.ts +0 -74
  669. package/dist/realtime/runtime.d.ts.map +0 -1
  670. package/src/cloudflare/realtime.ts +0 -234
  671. package/src/realtime/buffer.ts +0 -101
  672. package/src/realtime/longpoll.ts +0 -148
  673. package/src/realtime/protocol.ts +0 -128
  674. package/src/realtime/runtime.ts +0 -427
package/src/init.ts CHANGED
@@ -1,11 +1,10 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
2
2
  import { basename, dirname, join } from "node:path"
3
3
  import { fileURLToPath } from "node:url"
4
+ import type { InitFlags } from "./cli-args.ts"
4
5
 
5
- export type InitFlags = {
6
- cf: boolean
7
- force: boolean
8
- }
6
+ export type { InitFlags } from "./cli-args.ts"
7
+ export { parseInitFlags } from "./cli-args.ts"
9
8
 
10
9
  type PackageJson = {
11
10
  dependencies?: Record<string, string>
@@ -50,13 +49,6 @@ export default {
50
49
  }
51
50
  `
52
51
 
53
- export function parseInitFlags(args: string[]): InitFlags {
54
- return {
55
- cf: args.includes("--cf"),
56
- force: args.includes("--force"),
57
- }
58
- }
59
-
60
52
  export function runInit(cwd: string, flags: InitFlags): void {
61
53
  const files: Record<string, string> = {
62
54
  "src/app.ts": APP_TS,
@@ -76,7 +68,7 @@ export function runInit(cwd: string, flags: InitFlags): void {
76
68
  for (const [rel, contents] of Object.entries(files)) {
77
69
  writeText(join(cwd, rel), contents)
78
70
  }
79
- writePackageJson(cwd)
71
+ writePackageJson(cwd, flags.force)
80
72
  console.log("honey: initialized")
81
73
  }
82
74
 
@@ -91,25 +83,45 @@ function wranglerJsonc(name: string): string {
91
83
  `
92
84
  }
93
85
 
94
- function writePackageJson(cwd: string): void {
86
+ const PACKAGE_NAME = "@lovrozagar/honey"
87
+
88
+ const SCRIPTS: Record<string, string> = {
89
+ dev: "bun --watch src/server.ts",
90
+ generate: "honey generate",
91
+ }
92
+
93
+ /**
94
+ * Creates package.json, or adds what is missing to an existing one. Existing scripts, `type` and
95
+ * dependency ranges are kept unless `force` is set; each kept value is reported.
96
+ */
97
+ function writePackageJson(cwd: string, force: boolean): void {
95
98
  const path = join(cwd, "package.json")
96
- const existing = existsSync(path) ? readPackageJson(path) : {}
97
- const next: PackageJson = {
98
- ...existing,
99
- name: existing.name ?? packageNameFromDir(cwd),
100
- private: existing.private ?? true,
101
- type: existing.type ?? "module",
102
- scripts: {
103
- ...existing.scripts,
104
- dev: "bun --watch src/server.ts",
105
- generate: "honey generate",
106
- },
107
- dependencies: {
108
- ...existing.dependencies,
109
- honey: existing.dependencies?.honey ?? `^${honeyVersion()}`,
110
- },
99
+ const exists = existsSync(path)
100
+ const existing = exists ? readPackageJson(path) : {}
101
+ const notes: string[] = []
102
+
103
+ const scripts = { ...existing.scripts }
104
+ for (const [name, command] of Object.entries(SCRIPTS)) {
105
+ const current = scripts[name]
106
+ if (current === undefined || current === command || force) scripts[name] = command
107
+ else notes.push(`kept scripts.${name} ("${current}"); honey expects "${command}"`)
108
+ }
109
+
110
+ const dependencies = { ...existing.dependencies }
111
+ if (dependencies[PACKAGE_NAME] === undefined || force) dependencies[PACKAGE_NAME] = `^${honeyVersion()}`
112
+
113
+ const next: PackageJson = { ...existing, dependencies, scripts }
114
+ if (!exists) {
115
+ next.name = packageNameFromDir(cwd)
116
+ next.private = true
117
+ }
118
+ if (existing.type === undefined) {
119
+ if (!exists || force) next.type = "module"
120
+ else notes.push('package.json has no "type"; the scaffold is ESM, so set "type": "module" or rerun with --force')
111
121
  }
122
+
112
123
  writeText(path, `${JSON.stringify(next, null, "\t")}\n`)
124
+ for (const note of notes) console.log(`honey: ${note}`)
113
125
  }
114
126
 
115
127
  function readPackageJson(path: string): PackageJson {
@@ -62,8 +62,15 @@ function declaredNothing(meta: Record<string, unknown> | null): boolean {
62
62
  return value === null || (Array.isArray(value) && value.length === 0)
63
63
  }
64
64
 
65
- function alreadyInvalidates(operation: Record<string, unknown>): boolean {
66
- const emitted = operation["x-invalidate"]
65
+ /**
66
+ * Did the author declare targets? Read from the meta, not the emitted operation: a profile
67
+ * that leaves `x-invalidate` out of its document must not make every declared mutation look
68
+ * undeclared.
69
+ */
70
+ function alreadyInvalidates(entry: InvalidateCheckOperation): boolean {
71
+ const declared = entry.meta?.invalidate
72
+ if (Array.isArray(declared) && declared.length > 0) return true
73
+ const emitted = entry.operation["x-invalidate"]
67
74
  return Array.isArray(emitted) && emitted.length > 0
68
75
  }
69
76
 
@@ -112,7 +119,7 @@ export function findMissingInvalidate(
112
119
  const findings: InvalidateFinding[] = []
113
120
  for (const entry of operations) {
114
121
  if (!MUTATION_METHODS.has(entry.method)) continue
115
- if (alreadyInvalidates(entry.operation)) continue
122
+ if (alreadyInvalidates(entry)) continue
116
123
  if (declaredNothing(entry.meta)) continue
117
124
 
118
125
  let affects: string[] = []
@@ -120,7 +127,9 @@ export function findMissingInvalidate(
120
127
  if (entity !== undefined && entity !== null) {
121
128
  /* sharper than path shape: the same entity is read somewhere else in the document */
122
129
  affects = readsByEntity.get(JSON.stringify(entity)) ?? []
123
- } else {
130
+ }
131
+ if (affects.length === 0) {
132
+ /* no reader is tagged with the entity — path shape is still a signal, not silence */
124
133
  affects = pathFamily(entry.path)
125
134
  .filter((candidate) => readPaths.has(candidate))
126
135
  .map((candidate) => `GET ${candidate}`)
@@ -165,3 +174,32 @@ export function reportMissingInvalidate(
165
174
  if (level === "error") throw new Error(report)
166
175
  console.warn(report)
167
176
  }
177
+
178
+ /**
179
+ * `invalidate` selectors that name no route. A selector is `"<METHOD> <pattern>"`, exactly as
180
+ * the generated `RouteSelector` union spells it; anything else invalidates nothing at runtime.
181
+ */
182
+ export function findInvalidSelectors(
183
+ operations: readonly InvalidateCheckOperation[],
184
+ inventory: ReadonlySet<string>,
185
+ ): Array<{ method: string; path: string; selector: unknown }> {
186
+ const out: Array<{ method: string; path: string; selector: unknown }> = []
187
+ const seen = new Set<string>()
188
+ for (const entry of operations) {
189
+ const declared = entry.meta?.invalidate
190
+ if (!Array.isArray(declared)) continue
191
+ for (const selector of declared) {
192
+ if (typeof selector === "string" && inventory.has(selector)) continue
193
+ const key = `${entry.method} ${entry.path} ${String(selector)}`
194
+ if (seen.has(key)) continue
195
+ seen.add(key)
196
+ out.push({ method: entry.method.toUpperCase(), path: entry.path, selector })
197
+ }
198
+ }
199
+ return out
200
+ }
201
+
202
+ /** The level an `invalidate` config reports at. */
203
+ export function invalidateLevel(config: InvalidateCheckConfig | undefined): InvalidateCheckLevel {
204
+ return resolveConfig(config).level
205
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Containment for user callbacks the framework calls outside a request's middleware chain
3
+ * (WebSocket and realtime events, adapter hooks). A sync throw or a rejection never escapes
4
+ * into the runtime, where it would be an uncaught exception or an unhandled rejection and,
5
+ * on Node, end the process.
6
+ */
7
+
8
+ /** The error side of a structured logger (`error(obj, msg)`), e.g. honey/logger. */
9
+ export type InvokeLogger = {
10
+ error?: (obj: Record<string, unknown>, msg?: string) => void
11
+ }
12
+
13
+ export type InvokeOptions = {
14
+ /** Where the callback's error goes. When it throws or rejects itself, that is logged and it is not called again for its own error. */
15
+ onError?: ((err: unknown) => unknown) | null
16
+ /** The app logger; `console.error` when absent. */
17
+ log?: InvokeLogger | null
18
+ /** A label for the log line (`"open"`, `"message"`, …). */
19
+ phase: string
20
+ /** Extra fields for the log line. */
21
+ fields?: Record<string, unknown>
22
+ }
23
+
24
+ function logFailure(err: unknown, opts: InvokeOptions, phase: string): void {
25
+ const fields = { ...opts.fields, err, phase }
26
+ try {
27
+ if (opts.log?.error) {
28
+ opts.log.error(fields, "callback failed")
29
+ return
30
+ }
31
+ } catch {
32
+ /* a broken logger must not take the callback's place as the thing that escapes */
33
+ }
34
+ console.error("honey: callback failed", fields)
35
+ }
36
+
37
+ function isThenable(value: unknown): value is PromiseLike<unknown> {
38
+ return value !== null && typeof value === "object" && typeof (value as { then?: unknown }).then === "function"
39
+ }
40
+
41
+ /** Report `err` through `onError`, or log it. Never throws, never rejects. */
42
+ export function reportUserError(err: unknown, opts: InvokeOptions): void {
43
+ const onError = opts.onError
44
+ if (!onError) {
45
+ logFailure(err, opts, opts.phase)
46
+ return
47
+ }
48
+ try {
49
+ const r = onError(err)
50
+ if (isThenable(r)) r.then(undefined, (e: unknown) => logFailure(e, opts, "onError"))
51
+ } catch (e) {
52
+ logFailure(e, opts, "onError")
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Call `fn` and resolve once it settles. Resolves `true` when it succeeded and `false` when it
58
+ * threw or rejected (the error went to `onError` or the logger). Never rejects.
59
+ */
60
+ export function invokeUser(fn: () => unknown, opts: InvokeOptions): Promise<boolean> {
61
+ try {
62
+ const r = fn()
63
+ if (isThenable(r)) {
64
+ return Promise.resolve(r).then(
65
+ () => true,
66
+ (e: unknown) => {
67
+ reportUserError(e, opts)
68
+ return false
69
+ },
70
+ )
71
+ }
72
+ return Promise.resolve(true)
73
+ } catch (e) {
74
+ reportUserError(e, opts)
75
+ return Promise.resolve(false)
76
+ }
77
+ }
78
+
79
+ /**
80
+ * One ordered event queue: each task starts after the previous one settled, and a failing task
81
+ * never stops the ones after it.
82
+ */
83
+ export function createEventQueue(): { push(task: () => Promise<unknown>): Promise<void> } {
84
+ let tail: Promise<void> = Promise.resolve()
85
+ return {
86
+ push(task) {
87
+ const next = tail.then(task).then(
88
+ () => undefined,
89
+ () => undefined,
90
+ )
91
+ tail = next
92
+ return next
93
+ },
94
+ }
95
+ }
@@ -1,153 +1,29 @@
1
1
  import { namedMiddleware } from "./middleware.ts"
2
2
  import { HoneyError } from "./error.ts"
3
+ import { ipInRange, parseIp, parseIpRange, type IpRange } from "./ip.ts"
3
4
  import type { MiddlewareFn } from "./middleware.ts"
4
5
  import { EK, SK } from "./types.ts"
5
6
 
6
7
  type IpRestrictOptions = {
8
+ /** Addresses and CIDR ranges allowed through. When set, everything else is rejected. */
7
9
  allowList?: string[]
10
+ /** Addresses and CIDR ranges rejected. Checked before `allowList`. */
8
11
  denyList?: string[]
12
+ /**
13
+ * Returns the client address, replacing `ctx.ip`. Leave it out to use `ctx.ip`, which the
14
+ * app's `trustProxy()` setting decides — the same address every other feature sees.
15
+ */
9
16
  getIp?: (req: Request) => string | null
10
- trustProxy?: boolean
11
17
  }
12
18
 
13
- function stripBrackets(ip: string): string {
14
- if (ip.startsWith("[") && ip.endsWith("]")) {
15
- return ip.slice(1, -1)
16
- }
17
- return ip
18
- }
19
-
20
- function defaultGetIp(req: Request): string | null {
21
- const cfIp = req.headers.get("cf-connecting-ip")
22
- if (cfIp) return stripBrackets(cfIp.trim())
23
-
24
- return null
25
- }
26
-
27
- function proxyAwareGetIp(req: Request): string | null {
28
- const cfIp = req.headers.get("cf-connecting-ip")
29
- if (cfIp) return stripBrackets(cfIp.trim())
30
-
31
- const xff = req.headers.get("x-forwarded-for")
32
- if (xff) return stripBrackets(xff.split(",")[0].trim())
33
-
34
- const realIp = req.headers.get("x-real-ip")
35
- if (realIp) return stripBrackets(realIp.trim())
36
-
37
- return null
38
- }
39
-
40
- function parseIpv4(ip: string): number | null {
41
- const parts = ip.split(".")
42
- if (parts.length !== 4) return null
43
- let result = 0
44
- for (const part of parts) {
45
- const n = Number.parseInt(part, 10)
46
- if (Number.isNaN(n) || n < 0 || n > 255) return null
47
- result = (result << 8) | n
48
- }
49
- return result >>> 0
50
- }
51
-
52
- type Ipv4CidrRule = {
53
- base: number
54
- mask: number
55
- v6: false
56
- }
57
-
58
- type Ipv6CidrRule = {
59
- base: bigint
60
- mask: bigint
61
- v6: true
62
- }
63
-
64
- type CidrRule = Ipv4CidrRule | Ipv6CidrRule
65
-
66
- type ParsedRule = {
67
- cidr?: CidrRule
68
- exact: string
69
- }
70
-
71
- function expandIpv6(ip: string): bigint | null {
72
- let addr = ip
73
-
74
- /* handle ::ffff:1.2.3.4 mapped IPv4 */
75
- const v4Suffix = addr.match(/:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/)
76
- if (v4Suffix) {
77
- const v4 = parseIpv4(v4Suffix[1])
78
- if (v4 === null) return null
79
- addr = `${addr.slice(0, addr.lastIndexOf(":"))}:${((v4 >>> 16) & 0xffff).toString(16)}:${(v4 & 0xffff).toString(16)}`
80
- }
81
-
82
- /* expand :: */
83
- const halves = addr.split("::")
84
- if (halves.length > 2) return null
85
-
86
- let groups: string[]
87
- if (halves.length === 2) {
88
- const left = halves[0] === "" ? [] : halves[0].split(":")
89
- const right = halves[1] === "" ? [] : halves[1].split(":")
90
- const fill = 8 - left.length - right.length
91
- if (fill < 0) return null
92
- groups = [...left, ...Array.from<string>({ length: fill }).fill("0"), ...right]
93
- } else {
94
- groups = addr.split(":")
95
- }
96
-
97
- if (groups.length !== 8) return null
98
-
99
- let result = 0n
100
- for (const g of groups) {
101
- const n = Number.parseInt(g, 16)
102
- if (Number.isNaN(n) || n < 0 || n > 0xffff) return null
103
- result = (result << 16n) | BigInt(n)
104
- }
105
- return result
106
- }
107
-
108
- function isIpv6(ip: string): boolean {
109
- return ip.includes(":")
110
- }
111
-
112
- function parseRule(rule: string): ParsedRule {
113
- const slashIdx = rule.indexOf("/")
114
- if (slashIdx === -1) {
115
- return { exact: rule }
116
- }
117
-
118
- const ip = rule.slice(0, slashIdx)
119
- const bits = Number.parseInt(rule.slice(slashIdx + 1), 10)
120
-
121
- if (isIpv6(ip)) {
122
- const base = expandIpv6(ip)
123
- if (base === null || Number.isNaN(bits) || bits < 0 || bits > 128) {
124
- return { exact: rule }
125
- }
126
- const mask = bits === 0 ? 0n : ((1n << BigInt(bits)) - 1n) << BigInt(128 - bits)
127
- return { cidr: { base: base & mask, mask, v6: true }, exact: rule }
128
- }
129
-
130
- const base = parseIpv4(ip)
131
- if (base === null || Number.isNaN(bits) || bits < 0 || bits > 32) {
132
- return { exact: rule }
133
- }
134
-
135
- const mask = bits === 0 ? 0 : (~0 << (32 - bits)) >>> 0
136
- return { cidr: { base: (base & mask) >>> 0, mask, v6: false }, exact: rule }
137
- }
138
-
139
- function matchesRule(ip: string, rule: ParsedRule): boolean {
140
- if (rule.cidr) {
141
- if (rule.cidr.v6) {
142
- const parsed = expandIpv6(ip)
143
- if (parsed === null) return false
144
- return (parsed & rule.cidr.mask) === rule.cidr.base
145
- }
146
- const parsed = parseIpv4(ip)
147
- if (parsed === null) return false
148
- return (parsed & rule.cidr.mask) >>> 0 === rule.cidr.base
149
- }
150
- return ip === rule.exact
19
+ function parseRules(list: readonly string[] | undefined, name: string): IpRange[] {
20
+ if (list === undefined) return []
21
+ return list.map((rule) => {
22
+ const range = typeof rule === "string" ? parseIpRange(rule) : null
23
+ if (range === null)
24
+ throw new Error(`ipRestrict: ${name} entry ${JSON.stringify(rule)} is not an IP address or CIDR range`)
25
+ return range
26
+ })
151
27
  }
152
28
 
153
29
  function throwForbidden(): never {
@@ -157,35 +33,45 @@ function throwForbidden(): never {
157
33
  })
158
34
  }
159
35
 
36
+ /**
37
+ * Allows or rejects requests by client address. Rules and addresses are compared in canonical
38
+ * numeric form, so `::ffff:203.0.113.7`, `203.0.113.7` and `203.0.113.7:51234` are one address.
39
+ *
40
+ * Fails closed: a request whose address is unknown or unparseable is rejected with 403, under
41
+ * a deny list as much as under an allow list.
42
+ */
160
43
  export function ipRestrict(opts: IpRestrictOptions): MiddlewareFn<{ req: Request }, {}> {
161
- const getIp = opts.getIp ?? (opts.trustProxy === true ? proxyAwareGetIp : defaultGetIp)
162
- const denyRules = opts.denyList?.map(parseRule) ?? []
163
- const allowRules = opts.allowList?.map(parseRule) ?? []
164
- const hasAllowList = allowRules.length > 0
44
+ const legacy = opts as { trustCloudflare?: unknown; trustProxy?: unknown }
45
+ if (legacy.trustProxy !== undefined || legacy.trustCloudflare !== undefined) {
46
+ throw new Error(
47
+ "ipRestrict: `trustProxy` and `trustCloudflare` moved to the app. Call `app.trustProxy(1)` " +
48
+ "(one reverse proxy) or `app.trustProxy([...proxy ranges])`; on Cloudflare Workers `ctx.ip` " +
49
+ "already reads CF-Connecting-IP.",
50
+ )
51
+ }
52
+ if (opts.allowList !== undefined && opts.allowList.length === 0) {
53
+ throw new Error("ipRestrict: `allowList: []` would reject every request. Remove the middleware or list addresses.")
54
+ }
55
+ const denyRules = parseRules(opts.denyList, "denyList")
56
+ const allowRules = parseRules(opts.allowList, "allowList")
57
+ if (denyRules.length === 0 && allowRules.length === 0) {
58
+ throw new Error("ipRestrict: pass a non-empty `allowList` or `denyList`")
59
+ }
60
+ const getIp = opts.getIp
165
61
 
166
62
  const mw: MiddlewareFn<{ req: Request }, {}> = (ctx, next) => {
167
- const ip = getIp(ctx.req)
63
+ const raw = getIp ? getIp(ctx.req) : ((ctx as { ip?: string | null }).ip ?? null)
64
+ const addr = raw === null || raw === "" ? null : parseIp(raw)
168
65
 
169
- /* deny list checked first */
170
- if (ip !== null) {
171
- for (const rule of denyRules) {
172
- if (matchesRule(ip, rule)) throwForbidden()
173
- }
174
- }
66
+ /* fail closed: an unknown address can match neither list */
67
+ if (addr === null) throwForbidden()
175
68
 
176
- /* allow list: if set, IP must match at least one rule */
177
- if (hasAllowList) {
178
- if (ip === null) throwForbidden()
179
- let allowed = false
180
- for (const rule of allowRules) {
181
- if (matchesRule(ip, rule)) {
182
- allowed = true
183
- break
184
- }
185
- }
186
- if (!allowed) throwForbidden()
69
+ for (const rule of denyRules) {
70
+ if (ipInRange(addr, rule)) throwForbidden()
187
71
  }
188
72
 
73
+ if (allowRules.length > 0 && !allowRules.some((rule) => ipInRange(addr, rule))) throwForbidden()
74
+
189
75
  return next()
190
76
  }
191
77
 
package/src/ip.ts ADDED
@@ -0,0 +1,194 @@
1
+ /**
2
+ * IP addresses in canonical numeric form. Every comparison the framework makes (`ipRestrict`
3
+ * rules, trusted proxy ranges) parses both sides here first, so `::ffff:203.0.113.7`,
4
+ * `203.0.113.7` and `203.0.113.7:51234` are one address and `2001:DB8::1` equals `2001:db8::1`.
5
+ */
6
+
7
+ export type IpAddress = { readonly v: 4; readonly n: number } | { readonly v: 6; readonly n: bigint }
8
+
9
+ export type IpRange =
10
+ | { readonly v: 4; readonly base: number; readonly mask: number }
11
+ | { readonly v: 6; readonly base: bigint; readonly mask: bigint }
12
+
13
+ const V4_MAPPED_PREFIX = 0xffffn << 32n
14
+ const V4_MAPPED_MASK = ~0xffffffffn & ((1n << 128n) - 1n)
15
+
16
+ function parseIpv4(s: string): number | null {
17
+ const parts = s.split(".")
18
+ if (parts.length !== 4) return null
19
+ let n = 0
20
+ for (const part of parts) {
21
+ /* digits only, no leading zeros (`010` is octal to some parsers, decimal to others) */
22
+ if (part.length === 0 || part.length > 3 || !/^\d+$/.test(part)) return null
23
+ if (part.length > 1 && part.charCodeAt(0) === 48) return null
24
+ const octet = Number(part)
25
+ if (octet > 255) return null
26
+ n = n * 256 + octet
27
+ }
28
+ return n
29
+ }
30
+
31
+ /** The `:`-separated hex groups of one side of `::`; `null` when a group is not 1–4 hex digits. */
32
+ function groupsOf(part: string): string[] | null {
33
+ if (part === "") return []
34
+ const groups = part.split(":")
35
+ for (const g of groups) {
36
+ if (!/^[0-9A-Fa-f]{1,4}$/.test(g)) return null
37
+ }
38
+ return groups
39
+ }
40
+
41
+ function parseIpv6(s: string): bigint | null {
42
+ let addr = s
43
+ let tail: number[] = []
44
+ const lastColon = addr.lastIndexOf(":")
45
+ if (lastColon !== -1 && addr.indexOf(".", lastColon) !== -1) {
46
+ const v4 = parseIpv4(addr.slice(lastColon + 1))
47
+ if (v4 === null) return null
48
+ tail = [Math.floor(v4 / 65536), v4 % 65536]
49
+ addr = addr.slice(0, lastColon + 1)
50
+ /* "::1.2.3.4" leaves "::", "a::1.2.3.4" leaves "a::"; "a:1.2.3.4" leaves "a:" */
51
+ if (addr.endsWith(":") && !addr.endsWith("::")) addr = addr.slice(0, -1)
52
+ }
53
+ const halves = addr.split("::")
54
+ if (halves.length > 2) return null
55
+ const left = groupsOf(halves[0])
56
+ if (left === null) return null
57
+ let groups: number[]
58
+ if (halves.length === 2) {
59
+ const right = groupsOf(halves[1])
60
+ if (right === null) return null
61
+ const fill = 8 - left.length - right.length - tail.length
62
+ if (fill < 1) return null
63
+ groups = [...left.map(hex), ...Array.from({ length: fill }, () => 0), ...right.map(hex), ...tail]
64
+ } else {
65
+ groups = [...left.map(hex), ...tail]
66
+ }
67
+ if (groups.length !== 8) return null
68
+ let n = 0n
69
+ for (const g of groups) n = (n << 16n) | BigInt(g)
70
+ return n
71
+ }
72
+
73
+ function hex(g: string): number {
74
+ return Number.parseInt(g, 16)
75
+ }
76
+
77
+ /** An IPv4-mapped IPv6 address (`::ffff:a.b.c.d`) is the IPv4 address it maps. */
78
+ function fromV6(n: bigint): IpAddress {
79
+ if ((n & V4_MAPPED_MASK) === V4_MAPPED_PREFIX) return { n: Number(n & 0xffffffffn), v: 4 }
80
+ return { n, v: 6 }
81
+ }
82
+
83
+ /**
84
+ * Parses an address as a peer or a forwarding header writes it: brackets, an IPv6 zone
85
+ * (`%eth0`) and a port (`1.2.3.4:5678`, `[::1]:5678`) are accepted and dropped. Returns
86
+ * `null` for anything that is not exactly one address.
87
+ */
88
+ export function parseIp(input: string): IpAddress | null {
89
+ let s = input.trim()
90
+ if (s.startsWith("[")) {
91
+ const close = s.indexOf("]")
92
+ if (close === -1) return null
93
+ const after = s.slice(close + 1)
94
+ if (after !== "" && !/^:\d{1,5}$/.test(after)) return null
95
+ s = s.slice(1, close)
96
+ } else if (s.indexOf(":") !== -1 && s.indexOf(":") === s.lastIndexOf(":") && s.includes(".")) {
97
+ /* "1.2.3.4:5678" — one colon and a dot can only be IPv4 with a port */
98
+ const [host, port] = s.split(":")
99
+ if (!/^\d{1,5}$/.test(port)) return null
100
+ s = host
101
+ }
102
+ const zone = s.indexOf("%")
103
+ if (zone !== -1) {
104
+ if (!s.includes(":")) return null
105
+ s = s.slice(0, zone)
106
+ }
107
+ if (s.includes(":")) {
108
+ const n = parseIpv6(s)
109
+ return n === null ? null : fromV6(n)
110
+ }
111
+ const n = parseIpv4(s)
112
+ return n === null ? null : { n, v: 4 }
113
+ }
114
+
115
+ /** Canonical text: dotted IPv4, or RFC 5952 IPv6 (lowercase, longest zero run compressed). */
116
+ export function formatIp(addr: IpAddress): string {
117
+ if (addr.v === 4) {
118
+ const n = addr.n
119
+ return `${Math.floor(n / 16777216)}.${Math.floor(n / 65536) % 256}.${Math.floor(n / 256) % 256}.${n % 256}`
120
+ }
121
+ const groups: number[] = []
122
+ for (let i = 7; i >= 0; i--) groups.push(Number((addr.n >> BigInt(i * 16)) & 0xffffn))
123
+ let bestStart = -1
124
+ let bestLen = 1
125
+ for (let i = 0; i < 8;) {
126
+ if (groups[i] !== 0) {
127
+ i++
128
+ continue
129
+ }
130
+ let j = i
131
+ while (j < 8 && groups[j] === 0) j++
132
+ if (j - i > bestLen) {
133
+ bestStart = i
134
+ bestLen = j - i
135
+ }
136
+ i = j
137
+ }
138
+ const text = groups.map((g) => g.toString(16))
139
+ if (bestStart === -1) return text.join(":")
140
+ const head = text.slice(0, bestStart).join(":")
141
+ const tail = text.slice(bestStart + bestLen).join(":")
142
+ return `${head}::${tail}`
143
+ }
144
+
145
+ /** `canonicalIp("::ffff:127.0.0.1")` is `"127.0.0.1"`; `null` when the input is not an address. */
146
+ export function canonicalIp(input: string): string | null {
147
+ const addr = parseIp(input)
148
+ return addr === null ? null : formatIp(addr)
149
+ }
150
+
151
+ /**
152
+ * Parses an address or CIDR rule (`10.0.0.0/8`, `2001:db8::/32`, `203.0.113.7`). Strict: no
153
+ * port, no zone, a prefix length that fits the family. An IPv4-mapped IPv6 range of `/96` or
154
+ * longer is the IPv4 range it maps. Returns `null` when the rule is invalid.
155
+ */
156
+ export function parseIpRange(rule: string): IpRange | null {
157
+ const s = rule.trim()
158
+ const slash = s.indexOf("/")
159
+ const ipText = slash === -1 ? s : s.slice(0, slash)
160
+ if (ipText.startsWith("[") || ipText.includes("%")) return null
161
+ let bits: number | null = null
162
+ if (slash !== -1) {
163
+ const bitsText = s.slice(slash + 1)
164
+ if (!/^\d{1,3}$/.test(bitsText)) return null
165
+ bits = Number(bitsText)
166
+ }
167
+ if (ipText.includes(":")) {
168
+ const n = parseIpv6(ipText)
169
+ if (n === null) return null
170
+ const prefix = bits ?? 128
171
+ if (prefix > 128) return null
172
+ if ((n & V4_MAPPED_MASK) === V4_MAPPED_PREFIX && prefix >= 96) {
173
+ return v4Range(Number(n & 0xffffffffn), prefix - 96)
174
+ }
175
+ const mask = prefix === 0 ? 0n : (((1n << BigInt(prefix)) - 1n) << BigInt(128 - prefix)) & ((1n << 128n) - 1n)
176
+ return { base: n & mask, mask, v: 6 }
177
+ }
178
+ const n = parseIpv4(ipText)
179
+ if (n === null) return null
180
+ const prefix = bits ?? 32
181
+ if (prefix > 32) return null
182
+ return v4Range(n, prefix)
183
+ }
184
+
185
+ function v4Range(n: number, prefix: number): IpRange {
186
+ const mask = prefix === 0 ? 0 : (0xffffffff << (32 - prefix)) >>> 0
187
+ return { base: (n & mask) >>> 0, mask, v: 4 }
188
+ }
189
+
190
+ export function ipInRange(addr: IpAddress, range: IpRange): boolean {
191
+ if (addr.v === 4 && range.v === 4) return (addr.n & range.mask) >>> 0 === range.base
192
+ if (addr.v === 6 && range.v === 6) return (addr.n & range.mask) === range.base
193
+ return false
194
+ }