@lovrozagar/honey 0.6.5 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (674) hide show
  1. package/README.md +514 -192
  2. package/bin/honey.js +25 -0
  3. package/dist/accepts.d.ts +7 -1
  4. package/dist/accepts.d.ts.map +1 -1
  5. package/dist/accepts.js +126 -0
  6. package/dist/accepts.js.map +1 -0
  7. package/dist/body-kind.d.ts +26 -0
  8. package/dist/body-kind.d.ts.map +1 -0
  9. package/dist/body-kind.js +70 -0
  10. package/dist/body-kind.js.map +1 -0
  11. package/dist/body-limit.d.ts +5 -1
  12. package/dist/body-limit.d.ts.map +1 -1
  13. package/dist/body-limit.js +126 -0
  14. package/dist/body-limit.js.map +1 -0
  15. package/dist/build/index.d.ts +19 -3
  16. package/dist/build/index.d.ts.map +1 -1
  17. package/dist/build/index.js +173 -0
  18. package/dist/build/index.js.map +1 -0
  19. package/dist/cli-args.d.ts +33 -0
  20. package/dist/cli-args.d.ts.map +1 -0
  21. package/dist/cli-args.js +102 -0
  22. package/dist/cli-args.js.map +1 -0
  23. package/dist/cli-go/auth.go +26 -0
  24. package/dist/cli-go/config.go +192 -0
  25. package/dist/cli-go/errors.go +75 -0
  26. package/dist/cli-go/multipart.go +79 -0
  27. package/dist/cli-go/output.go +163 -0
  28. package/dist/cli-go/root.go +30 -0
  29. package/dist/cli-go/stream.go +88 -0
  30. package/dist/cli-go/version.go +5 -0
  31. package/dist/cli.d.ts +1 -1
  32. package/dist/cli.js +209 -0
  33. package/dist/cli.js.map +1 -0
  34. package/dist/client/defaults.js +48 -0
  35. package/dist/client/defaults.js.map +1 -0
  36. package/dist/client/error.d.ts +35 -0
  37. package/dist/client/error.d.ts.map +1 -1
  38. package/dist/client/error.js +71 -0
  39. package/dist/client/error.js.map +1 -0
  40. package/dist/client/http.d.ts +67 -8
  41. package/dist/client/http.d.ts.map +1 -1
  42. package/dist/client/http.js +571 -0
  43. package/dist/client/http.js.map +1 -0
  44. package/dist/client/index.d.ts +3 -2
  45. package/dist/client/index.d.ts.map +1 -1
  46. package/dist/client/index.js +95 -0
  47. package/dist/client/index.js.map +1 -0
  48. package/dist/client/path.d.ts +26 -0
  49. package/dist/client/path.d.ts.map +1 -0
  50. package/dist/client/path.js +163 -0
  51. package/dist/client/path.js.map +1 -0
  52. package/dist/client/redirect-policy.d.ts +12 -0
  53. package/dist/client/redirect-policy.d.ts.map +1 -0
  54. package/dist/client/redirect-policy.js +20 -0
  55. package/dist/client/redirect-policy.js.map +1 -0
  56. package/dist/client/sdk.d.ts +36 -0
  57. package/dist/client/sdk.d.ts.map +1 -1
  58. package/dist/client/sdk.js +292 -0
  59. package/dist/client/sdk.js.map +1 -0
  60. package/dist/client/sse.d.ts +13 -0
  61. package/dist/client/sse.d.ts.map +1 -1
  62. package/dist/client/sse.js +129 -0
  63. package/dist/client/sse.js.map +1 -0
  64. package/dist/client/types.js +2 -0
  65. package/dist/client/types.js.map +1 -0
  66. package/dist/client/ws.d.ts +9 -2
  67. package/dist/client/ws.d.ts.map +1 -1
  68. package/dist/client/ws.js +112 -0
  69. package/dist/client/ws.js.map +1 -0
  70. package/dist/client-go/errors.go +236 -0
  71. package/dist/client-go/export.go +44 -0
  72. package/dist/client-go/invalidation.go +371 -0
  73. package/dist/client-go/nullable.go +62 -0
  74. package/dist/client-go/realtime.go +354 -0
  75. package/dist/client-go/result.go +20 -0
  76. package/dist/client-go/runtime.go +975 -0
  77. package/dist/client-go/sse.go +159 -0
  78. package/dist/client-go/transport.go +495 -0
  79. package/dist/client-go/ws.go +128 -0
  80. package/dist/client-info.d.ts +36 -0
  81. package/dist/client-info.d.ts.map +1 -0
  82. package/dist/client-info.js +53 -0
  83. package/dist/client-info.js.map +1 -0
  84. package/dist/client-mcp/README.md +10 -0
  85. package/dist/client-mcp/server.ts +221 -0
  86. package/dist/client-mcp/types.ts +19 -0
  87. package/dist/client-python/_errors.py +115 -0
  88. package/dist/client-python/_invalidation.py +494 -0
  89. package/dist/client-python/_realtime.py +179 -0
  90. package/dist/client-python/_runtime.py +860 -0
  91. package/dist/client-python/_sse.py +155 -0
  92. package/dist/client-python/_transport.py +366 -0
  93. package/dist/client-python/_ws.py +159 -0
  94. package/dist/client-python/py.typed +0 -0
  95. package/dist/client-rust/errors.rs +220 -0
  96. package/dist/client-rust/invalidation.rs +412 -0
  97. package/dist/client-rust/invalidation_sync.rs +93 -0
  98. package/dist/client-rust/nullable.rs +17 -0
  99. package/dist/client-rust/realtime.rs +702 -0
  100. package/dist/client-rust/result.rs +22 -0
  101. package/dist/client-rust/runtime.rs +980 -0
  102. package/dist/client-rust/runtime_sync.rs +484 -0
  103. package/dist/client-rust/sse.rs +188 -0
  104. package/dist/client-rust/ws.rs +167 -0
  105. package/dist/codegen-go-cli.d.ts.map +1 -1
  106. package/dist/codegen-go-cli.js +964 -0
  107. package/dist/codegen-go-cli.js.map +1 -0
  108. package/dist/codegen-go.d.ts +15 -3
  109. package/dist/codegen-go.d.ts.map +1 -1
  110. package/dist/codegen-go.js +872 -0
  111. package/dist/codegen-go.js.map +1 -0
  112. package/dist/codegen-hash.js +9 -0
  113. package/dist/codegen-hash.js.map +1 -0
  114. package/dist/codegen-ir.d.ts +69 -2
  115. package/dist/codegen-ir.d.ts.map +1 -1
  116. package/dist/codegen-ir.js +661 -0
  117. package/dist/codegen-ir.js.map +1 -0
  118. package/dist/codegen-lang.d.ts +65 -0
  119. package/dist/codegen-lang.d.ts.map +1 -0
  120. package/dist/codegen-lang.js +526 -0
  121. package/dist/codegen-lang.js.map +1 -0
  122. package/dist/codegen-load.d.ts +31 -0
  123. package/dist/codegen-load.d.ts.map +1 -0
  124. package/dist/codegen-load.js +138 -0
  125. package/dist/codegen-load.js.map +1 -0
  126. package/dist/codegen-loaders.js +55 -0
  127. package/dist/codegen-loaders.js.map +1 -0
  128. package/dist/codegen-mcp.d.ts.map +1 -1
  129. package/dist/codegen-mcp.js +368 -0
  130. package/dist/codegen-mcp.js.map +1 -0
  131. package/dist/codegen-python.d.ts +13 -1
  132. package/dist/codegen-python.d.ts.map +1 -1
  133. package/dist/codegen-python.js +828 -0
  134. package/dist/codegen-python.js.map +1 -0
  135. package/dist/codegen-route-tree-intern.d.ts +15 -5
  136. package/dist/codegen-route-tree-intern.d.ts.map +1 -1
  137. package/dist/codegen-route-tree-intern.js +221 -0
  138. package/dist/codegen-route-tree-intern.js.map +1 -0
  139. package/dist/codegen-rust.d.ts.map +1 -1
  140. package/dist/codegen-rust.js +1293 -0
  141. package/dist/codegen-rust.js.map +1 -0
  142. package/dist/codegen-sanitize.d.ts.map +1 -1
  143. package/dist/codegen-sanitize.js +56 -0
  144. package/dist/codegen-sanitize.js.map +1 -0
  145. package/dist/codegen-schema-naming.d.ts +5 -0
  146. package/dist/codegen-schema-naming.d.ts.map +1 -1
  147. package/dist/codegen-schema-naming.js +150 -0
  148. package/dist/codegen-schema-naming.js.map +1 -0
  149. package/dist/codegen-sdk-model.d.ts +71 -0
  150. package/dist/codegen-sdk-model.d.ts.map +1 -0
  151. package/dist/codegen-sdk-model.js +179 -0
  152. package/dist/codegen-sdk-model.js.map +1 -0
  153. package/dist/codegen.d.ts +41 -23
  154. package/dist/codegen.d.ts.map +1 -1
  155. package/dist/codegen.js +4610 -0
  156. package/dist/codegen.js.map +1 -0
  157. package/dist/context.d.ts +36 -6
  158. package/dist/context.d.ts.map +1 -1
  159. package/dist/context.js +242 -0
  160. package/dist/context.js.map +1 -0
  161. package/dist/cookie-sign.d.ts +16 -2
  162. package/dist/cookie-sign.d.ts.map +1 -1
  163. package/dist/cookie-sign.js +104 -0
  164. package/dist/cookie-sign.js.map +1 -0
  165. package/dist/cookie.d.ts +12 -0
  166. package/dist/cookie.d.ts.map +1 -1
  167. package/dist/cookie.js +138 -0
  168. package/dist/cookie.js.map +1 -0
  169. package/dist/cors.d.ts +1 -0
  170. package/dist/cors.d.ts.map +1 -1
  171. package/dist/cors.js +87 -0
  172. package/dist/cors.js.map +1 -0
  173. package/dist/crypto.d.ts +6 -1
  174. package/dist/crypto.d.ts.map +1 -1
  175. package/dist/crypto.js +38 -0
  176. package/dist/crypto.js.map +1 -0
  177. package/dist/csrf.d.ts +19 -0
  178. package/dist/csrf.d.ts.map +1 -1
  179. package/dist/csrf.js +79 -0
  180. package/dist/csrf.js.map +1 -0
  181. package/dist/curl-logger.d.ts +14 -3
  182. package/dist/curl-logger.d.ts.map +1 -1
  183. package/dist/curl-logger.js +199 -0
  184. package/dist/curl-logger.js.map +1 -0
  185. package/dist/detect-runtime.js +13 -0
  186. package/dist/detect-runtime.js.map +1 -0
  187. package/dist/dict.d.ts +11 -0
  188. package/dist/dict.d.ts.map +1 -0
  189. package/dist/dict.js +13 -0
  190. package/dist/dict.js.map +1 -0
  191. package/dist/error.d.ts.map +1 -1
  192. package/dist/error.js +44 -0
  193. package/dist/error.js.map +1 -0
  194. package/dist/errors.js +39 -0
  195. package/dist/errors.js.map +1 -0
  196. package/dist/etag.d.ts +2 -0
  197. package/dist/etag.d.ts.map +1 -1
  198. package/dist/etag.js +82 -0
  199. package/dist/etag.js.map +1 -0
  200. package/dist/feature-detect.d.ts +25 -0
  201. package/dist/feature-detect.d.ts.map +1 -0
  202. package/dist/feature-detect.js +144 -0
  203. package/dist/feature-detect.js.map +1 -0
  204. package/dist/feature-load.js +36 -0
  205. package/dist/feature-load.js.map +1 -0
  206. package/dist/feature-slots.d.ts +44 -0
  207. package/dist/feature-slots.d.ts.map +1 -0
  208. package/dist/feature-slots.js +48 -0
  209. package/dist/feature-slots.js.map +1 -0
  210. package/dist/fetch-request.d.ts +10 -0
  211. package/dist/fetch-request.d.ts.map +1 -0
  212. package/dist/fetch-request.js +13 -0
  213. package/dist/fetch-request.js.map +1 -0
  214. package/dist/gen-process.d.ts +15 -0
  215. package/dist/gen-process.d.ts.map +1 -0
  216. package/dist/gen-process.js +43 -0
  217. package/dist/gen-process.js.map +1 -0
  218. package/dist/gen-write.d.ts +19 -0
  219. package/dist/gen-write.d.ts.map +1 -0
  220. package/dist/gen-write.js +120 -0
  221. package/dist/gen-write.js.map +1 -0
  222. package/dist/glob.d.ts +21 -0
  223. package/dist/glob.d.ts.map +1 -0
  224. package/dist/glob.js +115 -0
  225. package/dist/glob.js.map +1 -0
  226. package/dist/go-type-emitter.d.ts +18 -4
  227. package/dist/go-type-emitter.d.ts.map +1 -1
  228. package/dist/go-type-emitter.js +594 -0
  229. package/dist/go-type-emitter.js.map +1 -0
  230. package/dist/honey-response.d.ts.map +1 -1
  231. package/dist/honey-response.js +338 -0
  232. package/dist/honey-response.js.map +1 -0
  233. package/dist/i18n-slot.d.ts +8 -0
  234. package/dist/i18n-slot.d.ts.map +1 -1
  235. package/dist/i18n-slot.js +14 -0
  236. package/dist/i18n-slot.js.map +1 -0
  237. package/dist/i18n.d.ts +3 -0
  238. package/dist/i18n.d.ts.map +1 -1
  239. package/dist/i18n.js +250 -0
  240. package/dist/i18n.js.map +1 -0
  241. package/dist/index.d.ts +410 -149
  242. package/dist/index.d.ts.map +1 -1
  243. package/dist/index.js +2324 -0
  244. package/dist/index.js.map +1 -0
  245. package/dist/init.d.ts +3 -5
  246. package/dist/init.d.ts.map +1 -1
  247. package/dist/init.js +140 -0
  248. package/dist/init.js.map +1 -0
  249. package/dist/input.js +4 -0
  250. package/dist/input.js.map +1 -0
  251. package/dist/invalidate-check.d.ts +11 -0
  252. package/dist/invalidate-check.d.ts.map +1 -1
  253. package/dist/invalidate-check.js +163 -0
  254. package/dist/invalidate-check.js.map +1 -0
  255. package/dist/invoke-user.d.ts +35 -0
  256. package/dist/invoke-user.d.ts.map +1 -0
  257. package/dist/invoke-user.js +73 -0
  258. package/dist/invoke-user.js.map +1 -0
  259. package/dist/ip-restrict.d.ts +13 -1
  260. package/dist/ip-restrict.d.ts.map +1 -1
  261. package/dist/ip-restrict.js +60 -0
  262. package/dist/ip-restrict.js.map +1 -0
  263. package/dist/ip.d.ts +39 -0
  264. package/dist/ip.d.ts.map +1 -0
  265. package/dist/ip.js +209 -0
  266. package/dist/ip.js.map +1 -0
  267. package/dist/logger.d.ts.map +1 -1
  268. package/dist/logger.js +113 -0
  269. package/dist/logger.js.map +1 -0
  270. package/dist/media-type.d.ts +30 -0
  271. package/dist/media-type.d.ts.map +1 -0
  272. package/dist/media-type.js +97 -0
  273. package/dist/media-type.js.map +1 -0
  274. package/dist/meta-spec-merge.d.ts +32 -0
  275. package/dist/meta-spec-merge.d.ts.map +1 -0
  276. package/dist/meta-spec-merge.js +68 -0
  277. package/dist/meta-spec-merge.js.map +1 -0
  278. package/dist/meta-spec.d.ts +16 -3
  279. package/dist/meta-spec.d.ts.map +1 -1
  280. package/dist/meta-spec.js +535 -0
  281. package/dist/meta-spec.js.map +1 -0
  282. package/dist/middleware.d.ts +23 -6
  283. package/dist/middleware.d.ts.map +1 -1
  284. package/dist/middleware.js +253 -0
  285. package/dist/middleware.js.map +1 -0
  286. package/dist/node-request.d.ts +57 -12
  287. package/dist/node-request.d.ts.map +1 -1
  288. package/dist/node-request.js +407 -0
  289. package/dist/node-request.js.map +1 -0
  290. package/dist/node.d.ts +20 -3
  291. package/dist/node.d.ts.map +1 -1
  292. package/dist/node.js +396 -0
  293. package/dist/node.js.map +1 -0
  294. package/dist/openapi/collect.d.ts +6 -3
  295. package/dist/openapi/collect.d.ts.map +1 -1
  296. package/dist/openapi/collect.js +30 -0
  297. package/dist/openapi/collect.js.map +1 -0
  298. package/dist/openapi/docs-page.d.ts +42 -0
  299. package/dist/openapi/docs-page.d.ts.map +1 -0
  300. package/dist/openapi/docs-page.js +80 -0
  301. package/dist/openapi/docs-page.js.map +1 -0
  302. package/dist/openapi/document.d.ts +4 -0
  303. package/dist/openapi/document.d.ts.map +1 -1
  304. package/dist/openapi/document.js +785 -0
  305. package/dist/openapi/document.js.map +1 -0
  306. package/dist/openapi/json-schema-slot.d.ts +7 -0
  307. package/dist/openapi/json-schema-slot.d.ts.map +1 -1
  308. package/dist/openapi/json-schema-slot.js +38 -0
  309. package/dist/openapi/json-schema-slot.js.map +1 -0
  310. package/dist/openapi/register.d.ts.map +1 -1
  311. package/dist/openapi/register.js +20 -0
  312. package/dist/openapi/register.js.map +1 -0
  313. package/dist/openapi/scalar.d.ts +3 -1
  314. package/dist/openapi/scalar.d.ts.map +1 -1
  315. package/dist/openapi/scalar.js +21 -0
  316. package/dist/openapi/scalar.js.map +1 -0
  317. package/dist/openapi/serve.d.ts +9 -0
  318. package/dist/openapi/serve.d.ts.map +1 -0
  319. package/dist/openapi/serve.js +36 -0
  320. package/dist/openapi/serve.js.map +1 -0
  321. package/dist/openapi/spec-factory.d.ts +30 -1
  322. package/dist/openapi/spec-factory.d.ts.map +1 -1
  323. package/dist/openapi/spec-factory.js +52 -0
  324. package/dist/openapi/spec-factory.js.map +1 -0
  325. package/dist/openapi/spec.d.ts +14 -5
  326. package/dist/openapi/spec.d.ts.map +1 -1
  327. package/dist/openapi/spec.js +50 -0
  328. package/dist/openapi/spec.js.map +1 -0
  329. package/dist/openapi/swagger.d.ts +3 -1
  330. package/dist/openapi/swagger.d.ts.map +1 -1
  331. package/dist/openapi/swagger.js +22 -0
  332. package/dist/openapi/swagger.js.map +1 -0
  333. package/dist/pattern.d.ts +71 -0
  334. package/dist/pattern.d.ts.map +1 -0
  335. package/dist/pattern.js +239 -0
  336. package/dist/pattern.js.map +1 -0
  337. package/dist/peer.d.ts +17 -0
  338. package/dist/peer.d.ts.map +1 -0
  339. package/dist/peer.js +50 -0
  340. package/dist/peer.js.map +1 -0
  341. package/dist/plugin.d.ts +67 -16
  342. package/dist/plugin.d.ts.map +1 -1
  343. package/dist/plugin.js +649 -0
  344. package/dist/plugin.js.map +1 -0
  345. package/dist/powered-by.d.ts.map +1 -1
  346. package/dist/powered-by.js +11 -0
  347. package/dist/powered-by.js.map +1 -0
  348. package/dist/pretty-json.d.ts.map +1 -1
  349. package/dist/pretty-json.js +47 -0
  350. package/dist/pretty-json.js.map +1 -0
  351. package/dist/producer-stream.d.ts +47 -0
  352. package/dist/producer-stream.d.ts.map +1 -0
  353. package/dist/producer-stream.js +267 -0
  354. package/dist/producer-stream.js.map +1 -0
  355. package/dist/proxy.d.ts +47 -8
  356. package/dist/proxy.d.ts.map +1 -1
  357. package/dist/proxy.js +274 -0
  358. package/dist/proxy.js.map +1 -0
  359. package/dist/python-type-emitter.d.ts +14 -1
  360. package/dist/python-type-emitter.d.ts.map +1 -1
  361. package/dist/python-type-emitter.js +157 -0
  362. package/dist/python-type-emitter.js.map +1 -0
  363. package/dist/realtime/bus.d.ts +17 -1
  364. package/dist/realtime/bus.d.ts.map +1 -1
  365. package/dist/realtime/bus.js +112 -0
  366. package/dist/realtime/bus.js.map +1 -0
  367. package/dist/realtime/register.d.ts +3 -0
  368. package/dist/realtime/register.d.ts.map +1 -0
  369. package/dist/realtime/register.js +10 -0
  370. package/dist/realtime/register.js.map +1 -0
  371. package/dist/realtime/route.d.ts +65 -26
  372. package/dist/realtime/route.d.ts.map +1 -1
  373. package/dist/realtime/route.js +72 -0
  374. package/dist/realtime/route.js.map +1 -0
  375. package/dist/realtime/server.d.ts +43 -0
  376. package/dist/realtime/server.d.ts.map +1 -0
  377. package/dist/realtime/server.js +255 -0
  378. package/dist/realtime/server.js.map +1 -0
  379. package/dist/request-id.d.ts +7 -0
  380. package/dist/request-id.d.ts.map +1 -1
  381. package/dist/request-id.js +16 -0
  382. package/dist/request-id.js.map +1 -0
  383. package/dist/request-limits.d.ts +3 -0
  384. package/dist/request-limits.d.ts.map +1 -0
  385. package/dist/request-limits.js +3 -0
  386. package/dist/request-limits.js.map +1 -0
  387. package/dist/request-path.d.ts +33 -0
  388. package/dist/request-path.d.ts.map +1 -0
  389. package/dist/request-path.js +178 -0
  390. package/dist/request-path.js.map +1 -0
  391. package/dist/request-to-curl.d.ts +7 -1
  392. package/dist/request-to-curl.d.ts.map +1 -1
  393. package/dist/request-to-curl.js +48 -0
  394. package/dist/request-to-curl.js.map +1 -0
  395. package/dist/response.d.ts +25 -2
  396. package/dist/response.d.ts.map +1 -1
  397. package/dist/response.js +313 -0
  398. package/dist/response.js.map +1 -0
  399. package/dist/rust-type-emitter.d.ts +14 -2
  400. package/dist/rust-type-emitter.d.ts.map +1 -1
  401. package/dist/rust-type-emitter.js +463 -0
  402. package/dist/rust-type-emitter.js.map +1 -0
  403. package/dist/secure-headers.d.ts.map +1 -1
  404. package/dist/secure-headers.js +53 -0
  405. package/dist/secure-headers.js.map +1 -0
  406. package/dist/serve-register.d.ts +4 -0
  407. package/dist/serve-register.d.ts.map +1 -1
  408. package/dist/serve-register.js +9 -0
  409. package/dist/serve-register.js.map +1 -0
  410. package/dist/serve-slot.d.ts +3 -0
  411. package/dist/serve-slot.d.ts.map +1 -1
  412. package/dist/serve-slot.js +52 -0
  413. package/dist/serve-slot.js.map +1 -0
  414. package/dist/serve.d.ts +18 -1
  415. package/dist/serve.d.ts.map +1 -1
  416. package/dist/serve.js +157 -0
  417. package/dist/serve.js.map +1 -0
  418. package/dist/server-timing.d.ts.map +1 -1
  419. package/dist/server-timing.js +51 -0
  420. package/dist/server-timing.js.map +1 -0
  421. package/dist/static.d.ts +9 -0
  422. package/dist/static.d.ts.map +1 -1
  423. package/dist/static.js +102 -0
  424. package/dist/static.js.map +1 -0
  425. package/dist/telemetry/otel.d.ts +2 -0
  426. package/dist/telemetry/otel.d.ts.map +1 -1
  427. package/dist/telemetry/otel.js +135 -0
  428. package/dist/telemetry/otel.js.map +1 -0
  429. package/dist/testing.d.ts +12 -0
  430. package/dist/testing.d.ts.map +1 -1
  431. package/dist/testing.js +124 -0
  432. package/dist/testing.js.map +1 -0
  433. package/dist/timeout.d.ts +6 -0
  434. package/dist/timeout.d.ts.map +1 -1
  435. package/dist/timeout.js +36 -0
  436. package/dist/timeout.js.map +1 -0
  437. package/dist/tree.d.ts +133 -36
  438. package/dist/tree.d.ts.map +1 -1
  439. package/dist/tree.js +437 -0
  440. package/dist/tree.js.map +1 -0
  441. package/dist/trust.d.ts +21 -0
  442. package/dist/trust.d.ts.map +1 -0
  443. package/dist/trust.js +100 -0
  444. package/dist/trust.js.map +1 -0
  445. package/dist/ts-type-emitter.d.ts.map +1 -1
  446. package/dist/ts-type-emitter.js +150 -0
  447. package/dist/ts-type-emitter.js.map +1 -0
  448. package/dist/type-emitter.d.ts +16 -2
  449. package/dist/type-emitter.d.ts.map +1 -1
  450. package/dist/type-emitter.js +729 -0
  451. package/dist/type-emitter.js.map +1 -0
  452. package/dist/type-extractor.d.ts +8 -4
  453. package/dist/type-extractor.d.ts.map +1 -1
  454. package/dist/type-extractor.js +776 -0
  455. package/dist/type-extractor.js.map +1 -0
  456. package/dist/types.d.ts +7 -0
  457. package/dist/types.d.ts.map +1 -1
  458. package/dist/types.js +96 -0
  459. package/dist/types.js.map +1 -0
  460. package/dist/validation.d.ts +14 -2
  461. package/dist/validation.d.ts.map +1 -1
  462. package/dist/validation.js +335 -0
  463. package/dist/validation.js.map +1 -0
  464. package/dist/with-headers.d.ts +15 -0
  465. package/dist/with-headers.d.ts.map +1 -0
  466. package/dist/with-headers.js +45 -0
  467. package/dist/with-headers.js.map +1 -0
  468. package/dist/ws/bun.d.ts +11 -5
  469. package/dist/ws/bun.d.ts.map +1 -1
  470. package/dist/ws/bun.js +82 -0
  471. package/dist/ws/bun.js.map +1 -0
  472. package/dist/ws/cloudflare.d.ts +25 -3
  473. package/dist/ws/cloudflare.d.ts.map +1 -1
  474. package/dist/ws/cloudflare.js +117 -0
  475. package/dist/ws/cloudflare.js.map +1 -0
  476. package/dist/ws/deno.d.ts +4 -4
  477. package/dist/ws/deno.d.ts.map +1 -1
  478. package/dist/ws/deno.js +133 -0
  479. package/dist/ws/deno.js.map +1 -0
  480. package/dist/ws/node.d.ts +20 -3
  481. package/dist/ws/node.d.ts.map +1 -1
  482. package/dist/ws/node.js +173 -0
  483. package/dist/ws/node.js.map +1 -0
  484. package/dist/ws/session.d.ts +11 -0
  485. package/dist/ws/session.d.ts.map +1 -0
  486. package/dist/ws/session.js +60 -0
  487. package/dist/ws/session.js.map +1 -0
  488. package/dist/ws/shared.d.ts +65 -0
  489. package/dist/ws/shared.d.ts.map +1 -0
  490. package/dist/ws/shared.js +103 -0
  491. package/dist/ws/shared.js.map +1 -0
  492. package/dist/ws-origin.d.ts +20 -0
  493. package/dist/ws-origin.d.ts.map +1 -0
  494. package/dist/ws-origin.js +41 -0
  495. package/dist/ws-origin.js.map +1 -0
  496. package/dist/yaml.d.ts +15 -1
  497. package/dist/yaml.d.ts.map +1 -1
  498. package/dist/yaml.js +101 -0
  499. package/dist/yaml.js.map +1 -0
  500. package/docs/invalidate-spec.md +13 -1
  501. package/docs/meta-spec.md +36 -12
  502. package/docs/packaging.md +43 -24
  503. package/docs/regression-matrix/README.md +32 -0
  504. package/docs/regression-matrix/review.md +44 -0
  505. package/docs/regression-matrix/ws1-3.md +97 -0
  506. package/docs/regression-matrix/ws4-8.md +107 -0
  507. package/docs/regression-matrix/ws9-12.md +266 -0
  508. package/docs/sdk.md +102 -52
  509. package/package.json +183 -52
  510. package/src/accepts.ts +91 -33
  511. package/src/body-kind.ts +67 -0
  512. package/src/body-limit.ts +102 -87
  513. package/src/build/index.ts +104 -54
  514. package/src/cli-args.ts +123 -0
  515. package/src/cli-go/auth.go +12 -1
  516. package/src/cli-go/config.go +29 -2
  517. package/src/cli-go/errors.go +28 -10
  518. package/src/cli-go/multipart.go +51 -27
  519. package/src/cli-go/output.go +58 -4
  520. package/src/cli-go/root.go +1 -1
  521. package/src/cli-go/version.go +2 -2
  522. package/src/cli.ts +154 -148
  523. package/src/client/error.ts +56 -1
  524. package/src/client/http.ts +479 -256
  525. package/src/client/index.ts +55 -17
  526. package/src/client/path.ts +169 -0
  527. package/src/client/redirect-policy.ts +19 -0
  528. package/src/client/sdk.ts +180 -101
  529. package/src/client/sse.ts +92 -66
  530. package/src/client/ws.ts +67 -31
  531. package/src/client-go/errors.go +41 -12
  532. package/src/client-go/export.go +44 -0
  533. package/src/client-go/invalidation.go +52 -54
  534. package/src/client-go/nullable.go +62 -0
  535. package/src/client-go/realtime.go +147 -110
  536. package/src/client-go/runtime.go +646 -88
  537. package/src/client-go/sse.go +104 -94
  538. package/src/client-go/transport.go +67 -27
  539. package/src/client-go/ws.go +49 -25
  540. package/src/client-info.ts +79 -0
  541. package/src/client-mcp/server.ts +133 -3
  542. package/src/client-python/_invalidation.py +207 -291
  543. package/src/client-python/_realtime.py +63 -40
  544. package/src/client-python/_runtime.py +501 -119
  545. package/src/client-python/_sse.py +130 -74
  546. package/src/client-python/_transport.py +37 -9
  547. package/src/client-python/_ws.py +50 -22
  548. package/src/client-rust/errors.rs +21 -1
  549. package/src/client-rust/invalidation.rs +202 -231
  550. package/src/client-rust/invalidation_sync.rs +16 -229
  551. package/src/client-rust/nullable.rs +17 -0
  552. package/src/client-rust/realtime.rs +217 -217
  553. package/src/client-rust/runtime.rs +602 -177
  554. package/src/client-rust/runtime_sync.rs +169 -145
  555. package/src/client-rust/sse.rs +142 -121
  556. package/src/client-rust/ws.rs +61 -37
  557. package/src/codegen-go-cli.ts +607 -728
  558. package/src/codegen-go.ts +710 -770
  559. package/src/codegen-ir.ts +381 -75
  560. package/src/codegen-lang.ts +517 -0
  561. package/src/codegen-load.ts +150 -0
  562. package/src/codegen-mcp.ts +207 -108
  563. package/src/codegen-python.ts +684 -816
  564. package/src/codegen-route-tree-intern.ts +51 -25
  565. package/src/codegen-rust.ts +946 -1240
  566. package/src/codegen-sanitize.ts +0 -4
  567. package/src/codegen-schema-naming.ts +19 -6
  568. package/src/codegen-sdk-model.ts +228 -0
  569. package/src/codegen.ts +1488 -1140
  570. package/src/context.ts +130 -11
  571. package/src/cookie-sign.ts +85 -16
  572. package/src/cookie.ts +66 -11
  573. package/src/cors.ts +55 -54
  574. package/src/crypto.ts +27 -5
  575. package/src/csrf.ts +57 -25
  576. package/src/curl-logger.ts +155 -114
  577. package/src/dict.ts +12 -0
  578. package/src/error.ts +4 -2
  579. package/src/etag.ts +58 -34
  580. package/src/feature-detect.ts +147 -0
  581. package/src/feature-slots.ts +70 -0
  582. package/src/fetch-request.ts +13 -0
  583. package/src/gen-process.ts +42 -0
  584. package/src/gen-write.ts +127 -0
  585. package/src/glob.ts +109 -0
  586. package/src/go-type-emitter.ts +406 -155
  587. package/src/honey-response.ts +36 -9
  588. package/src/i18n-slot.ts +10 -0
  589. package/src/i18n.ts +98 -23
  590. package/src/index.ts +1893 -1761
  591. package/src/init.ts +40 -28
  592. package/src/invalidate-check.ts +42 -4
  593. package/src/invoke-user.ts +95 -0
  594. package/src/ip-restrict.ts +47 -161
  595. package/src/ip.ts +194 -0
  596. package/src/logger.ts +65 -36
  597. package/src/media-type.ts +104 -0
  598. package/src/meta-spec-merge.ts +80 -0
  599. package/src/meta-spec.ts +0 -0
  600. package/src/middleware.ts +112 -12
  601. package/src/node-request.ts +242 -52
  602. package/src/node.ts +318 -75
  603. package/src/openapi/collect.ts +19 -44
  604. package/src/openapi/docs-page.ts +89 -0
  605. package/src/openapi/document.ts +503 -224
  606. package/src/openapi/json-schema-slot.ts +40 -0
  607. package/src/openapi/register.ts +7 -2
  608. package/src/openapi/scalar.ts +11 -10
  609. package/src/openapi/serve.ts +39 -0
  610. package/src/openapi/spec-factory.ts +50 -1
  611. package/src/openapi/spec.ts +53 -30
  612. package/src/openapi/swagger.ts +12 -11
  613. package/src/pattern.ts +245 -0
  614. package/src/peer.ts +53 -0
  615. package/src/plugin.ts +270 -185
  616. package/src/powered-by.ts +2 -2
  617. package/src/pretty-json.ts +25 -9
  618. package/src/producer-stream.ts +296 -0
  619. package/src/proxy.ts +271 -55
  620. package/src/python-type-emitter.ts +68 -56
  621. package/src/realtime/bus.ts +56 -9
  622. package/src/realtime/register.ts +13 -0
  623. package/src/realtime/route.ts +129 -80
  624. package/src/realtime/server.ts +298 -0
  625. package/src/request-id.ts +13 -3
  626. package/src/request-limits.ts +2 -0
  627. package/src/request-path.ts +165 -0
  628. package/src/request-to-curl.ts +28 -7
  629. package/src/response.ts +143 -103
  630. package/src/rust-type-emitter.ts +265 -170
  631. package/src/secure-headers.ts +8 -4
  632. package/src/serve-register.ts +4 -0
  633. package/src/serve-slot.ts +38 -0
  634. package/src/serve.ts +113 -25
  635. package/src/server-timing.ts +11 -7
  636. package/src/static.ts +85 -32
  637. package/src/telemetry/otel.ts +29 -7
  638. package/src/testing.ts +76 -14
  639. package/src/timeout.ts +15 -7
  640. package/src/tree.ts +405 -333
  641. package/src/trust.ts +117 -0
  642. package/src/ts-type-emitter.ts +18 -12
  643. package/src/type-emitter.ts +227 -89
  644. package/src/type-extractor.ts +562 -424
  645. package/src/types.ts +8 -0
  646. package/src/validation.ts +221 -96
  647. package/src/with-headers.ts +45 -0
  648. package/src/ws/bun.ts +53 -15
  649. package/src/ws/cloudflare.ts +81 -19
  650. package/src/ws/deno.ts +124 -36
  651. package/src/ws/node.ts +173 -79
  652. package/src/ws/session.ts +73 -0
  653. package/src/ws/shared.ts +140 -0
  654. package/src/ws-origin.ts +53 -0
  655. package/src/yaml.ts +45 -22
  656. package/dist/client-mcp/server.d.ts +0 -10
  657. package/dist/client-mcp/server.d.ts.map +0 -1
  658. package/dist/client-mcp/types.d.ts +0 -16
  659. package/dist/client-mcp/types.d.ts.map +0 -1
  660. package/dist/cloudflare/realtime.d.ts +0 -30
  661. package/dist/cloudflare/realtime.d.ts.map +0 -1
  662. package/dist/realtime/buffer.d.ts +0 -18
  663. package/dist/realtime/buffer.d.ts.map +0 -1
  664. package/dist/realtime/longpoll.d.ts +0 -23
  665. package/dist/realtime/longpoll.d.ts.map +0 -1
  666. package/dist/realtime/protocol.d.ts +0 -50
  667. package/dist/realtime/protocol.d.ts.map +0 -1
  668. package/dist/realtime/runtime.d.ts +0 -74
  669. package/dist/realtime/runtime.d.ts.map +0 -1
  670. package/src/cloudflare/realtime.ts +0 -234
  671. package/src/realtime/buffer.ts +0 -101
  672. package/src/realtime/longpoll.ts +0 -148
  673. package/src/realtime/protocol.ts +0 -128
  674. package/src/realtime/runtime.ts +0 -427
@@ -5,13 +5,15 @@
5
5
  use crate::runtime::InvalidationConfig;
6
6
  use once_cell::sync::Lazy;
7
7
  use regex::Regex;
8
- use std::collections::HashMap;
8
+ use std::collections::{BTreeMap, HashMap, HashSet};
9
9
  use std::time::{Duration, Instant};
10
10
  use tokio::sync::Mutex;
11
11
 
12
- /* PLACEHOLDER_RE matches `{name}` and `:name` placeholders. */
12
+ /* PLACEHOLDER_RE matches `{name}` (any name without braces or slashes) and a
13
+ * `:name` segment; a colon inside a segment (`/ops/1:cancel`) is literal.
14
+ * Group 1: braced name; group 2: the `/` (or start) before a colon param; group 3: its name. */
13
15
  static PLACEHOLDER_RE: Lazy<Regex> =
14
- Lazy::new(|| Regex::new(r"\{([a-zA-Z_][a-zA-Z0-9_]*)\}|:([a-zA-Z_][a-zA-Z0-9_]*)").unwrap());
16
+ Lazy::new(|| Regex::new(r"\{([^{}/]+)\}|(^|/):([a-zA-Z_][a-zA-Z0-9_]*)").unwrap());
15
17
 
16
18
  /* PATTERN_REGEX_CACHE caches compiled placeholder→`[^/]+` regexps per pattern key.
17
19
  * std::sync::Mutex is fine — pure CPU work, never held across an .await point. */
@@ -50,8 +52,11 @@ pub fn compile_pattern(pattern: &str) -> Regex {
50
52
  let mut out = String::with_capacity(pattern.len() + 4);
51
53
  out.push('^');
52
54
  let mut last = 0usize;
53
- for m in PLACEHOLDER_RE.find_iter(pattern) {
54
- out.push_str(&regex::escape(&pattern[last..m.start()]));
55
+ for caps in PLACEHOLDER_RE.captures_iter(pattern) {
56
+ let m = caps.get(0).unwrap();
57
+ /* keep the segment slash of a `:name` match literal */
58
+ let start = m.start() + caps.get(2).map(|p| p.len()).unwrap_or(0);
59
+ out.push_str(&regex::escape(&pattern[last..start]));
55
60
  out.push_str("[^/]+");
56
61
  last = m.end();
57
62
  }
@@ -91,13 +96,17 @@ pub fn interpolate_path(
91
96
  for caps in PLACEHOLDER_RE.captures_iter(template) {
92
97
  let m = caps.get(0).unwrap();
93
98
  out.push_str(&template[last..m.start()]);
99
+ let prefix = caps.get(2).map(|c| c.as_str()).unwrap_or_default();
94
100
  let key = caps
95
101
  .get(1)
96
- .or_else(|| caps.get(2))
102
+ .or_else(|| caps.get(3))
97
103
  .map(|c| c.as_str())
98
104
  .unwrap_or_default();
99
105
  match params.get(key) {
100
- Some(v) => out.push_str(&urlencoding::encode(v)),
106
+ Some(v) => {
107
+ out.push_str(prefix);
108
+ out.push_str(&crate::runtime::escape_segment(v));
109
+ }
101
110
  None => {
102
111
  if missing.is_none() {
103
112
  missing = Some(key.to_string());
@@ -116,8 +125,8 @@ pub fn interpolate_path(
116
125
 
117
126
  /// resolve_invalidation_targets_for_mutation expands templated `METHOD /path/{x}`
118
127
  /// invalidation entries to concrete `METHOD /path/abc` selectors using params.
119
- /// Pattern-only entries (no placeholders) pass through; entries whose
120
- /// placeholders cannot be resolved are dropped.
128
+ /// Entries without placeholders pass through; an entry whose placeholders cannot
129
+ /// all be resolved stays a pattern, so it marks every instance stale (TS parity).
121
130
  pub fn resolve_invalidation_targets_for_mutation(
122
131
  targets: &[String],
123
132
  params: &HashMap<String, String>,
@@ -139,17 +148,158 @@ pub fn resolve_invalidation_targets_for_mutation(
139
148
  }
140
149
  match interpolate_path(template, params) {
141
150
  Ok(concrete) => out.push(format!("{} {}", method, concrete)),
142
- Err(_) => continue,
151
+ Err(_) => out.push(entry.clone()),
143
152
  }
144
153
  }
145
154
  out
146
155
  }
147
156
 
148
- struct TrackerState {
149
- entries: HashMap<String, StaleEntry>,
150
- /* insertion order; oldest at index 0 */
151
- order: Vec<String>,
152
- seq: u64,
157
+ struct Slot {
158
+ entry: StaleEntry,
159
+ /* position in `order`; a touch moves the entry to a fresh tick */
160
+ tick: u64,
161
+ }
162
+
163
+ /// Stale state shared by the async and blocking trackers; each wraps it in its own mutex.
164
+ /// Concrete keys are found by one map read; keys that still hold placeholders are also
165
+ /// indexed per method, so a lookup scans only the patterns of its method. `order` keeps
166
+ /// LRU order by tick, so touch and eviction are O(log n).
167
+ #[derive(Default)]
168
+ pub(crate) struct TrackerState {
169
+ entries: HashMap<String, Slot>,
170
+ order: BTreeMap<u64, String>,
171
+ patterns: HashMap<String, HashSet<String>>,
172
+ next_tick: u64,
173
+ pub(crate) seq: u64,
174
+ }
175
+
176
+ impl TrackerState {
177
+ fn tick(&mut self) -> u64 {
178
+ self.next_tick += 1;
179
+ self.next_tick
180
+ }
181
+
182
+ pub(crate) fn upsert(&mut self, key: &str, until: Instant, seq: u64, mutation_selector: &str, max_sources: usize) {
183
+ let tick = self.tick();
184
+ if let Some(slot) = self.entries.get_mut(key) {
185
+ slot.entry.until = until;
186
+ slot.entry.seq = seq;
187
+ if !mutation_selector.is_empty()
188
+ && slot.entry.by.len() < max_sources
189
+ && !slot.entry.by.iter().any(|b| b == mutation_selector)
190
+ {
191
+ slot.entry.by.push(mutation_selector.to_string());
192
+ }
193
+ let old = std::mem::replace(&mut slot.tick, tick);
194
+ self.order.remove(&old);
195
+ self.order.insert(tick, key.to_string());
196
+ return;
197
+ }
198
+ let by = if mutation_selector.is_empty() { Vec::new() } else { vec![mutation_selector.to_string()] };
199
+ self.entries.insert(key.to_string(), Slot { entry: StaleEntry { by, seq, until }, tick });
200
+ self.order.insert(tick, key.to_string());
201
+ if let Some((method, _)) = key.split_once(' ') {
202
+ if key_is_pattern(key) {
203
+ self.patterns.entry(method.to_string()).or_default().insert(key.to_string());
204
+ }
205
+ }
206
+ }
207
+
208
+ pub(crate) fn delete(&mut self, key: &str) {
209
+ let Some(slot) = self.entries.remove(key) else { return };
210
+ self.order.remove(&slot.tick);
211
+ if let Some((method, _)) = key.split_once(' ') {
212
+ if let Some(bucket) = self.patterns.get_mut(method) {
213
+ bucket.remove(key);
214
+ if bucket.is_empty() {
215
+ self.patterns.remove(method);
216
+ }
217
+ }
218
+ }
219
+ }
220
+
221
+ pub(crate) fn seq_of(&self, key: &str) -> Option<u64> {
222
+ self.entries.get(key).map(|s| s.entry.seq)
223
+ }
224
+
225
+ pub(crate) fn lookup(&mut self, concrete_selector: &str, concrete_path: &str, method: &str, now: Instant) -> (Vec<String>, bool) {
226
+ let mut all_by: Vec<String> = Vec::new();
227
+ let mut exact_expired = false;
228
+ if let Some(exact) = self.entries.get(concrete_selector) {
229
+ if exact.entry.until > now {
230
+ all_by.extend(exact.entry.by.iter().cloned());
231
+ } else {
232
+ exact_expired = true;
233
+ }
234
+ }
235
+ if exact_expired {
236
+ self.delete(concrete_selector);
237
+ }
238
+ let mut expired: Vec<String> = Vec::new();
239
+ if let Some(bucket) = self.patterns.get(method) {
240
+ for key in bucket {
241
+ let Some(slot) = self.entries.get(key) else { continue };
242
+ if slot.entry.until <= now {
243
+ expired.push(key.clone());
244
+ continue;
245
+ }
246
+ if key == concrete_selector {
247
+ continue;
248
+ }
249
+ let pattern = key.split_once(' ').map(|(_, p)| p).unwrap_or("");
250
+ if path_matches_pattern(concrete_path, pattern) {
251
+ all_by.extend(slot.entry.by.iter().cloned());
252
+ }
253
+ }
254
+ }
255
+ for key in expired {
256
+ self.delete(&key);
257
+ }
258
+ let mut seen = HashSet::new();
259
+ all_by.retain(|src| seen.insert(src.clone()));
260
+ let any = !all_by.is_empty();
261
+ (all_by, any)
262
+ }
263
+
264
+ pub(crate) fn clear(&mut self, concrete_selector: &str, concrete_path: &str, method: &str, seq_snapshot: u64) {
265
+ if self.seq_of(concrete_selector).is_some_and(|seq| seq <= seq_snapshot) {
266
+ self.delete(concrete_selector);
267
+ }
268
+ let to_delete: Vec<String> = self
269
+ .patterns
270
+ .get(method)
271
+ .map(|bucket| {
272
+ bucket
273
+ .iter()
274
+ .filter(|key| {
275
+ self.seq_of(key).is_some_and(|seq| seq <= seq_snapshot)
276
+ && path_matches_pattern(concrete_path, key.split_once(' ').map(|(_, p)| p).unwrap_or(""))
277
+ })
278
+ .cloned()
279
+ .collect()
280
+ })
281
+ .unwrap_or_default();
282
+ for key in to_delete {
283
+ self.delete(&key);
284
+ }
285
+ }
286
+
287
+ pub(crate) fn enforce_capacity(&mut self, max_entries: usize) {
288
+ if self.entries.len() <= max_entries {
289
+ return;
290
+ }
291
+ let now = Instant::now();
292
+ let expired: Vec<String> = self.entries.iter().filter(|(_, s)| s.entry.until <= now).map(|(k, _)| k.clone()).collect();
293
+ for key in expired {
294
+ self.delete(&key);
295
+ }
296
+ let target = (max_entries / 2).max(1);
297
+ while self.entries.len() > target {
298
+ /* the oldest tick; delete() drops the rest of its state */
299
+ let Some((_, oldest)) = self.order.pop_first() else { break };
300
+ self.delete(&oldest);
301
+ }
302
+ }
153
303
  }
154
304
 
155
305
  /// StaleTracker tracks invalidation state per `METHOD /path` selector with TS-
@@ -164,37 +314,30 @@ pub struct StaleTracker {
164
314
  max_sources: usize,
165
315
  }
166
316
 
317
+ /// (enabled, stale_time, max_entries, max_sources) from a config; disabled when absent or stale_time ≤ 0.
318
+ pub(crate) fn tracker_settings(cfg: Option<&InvalidationConfig>) -> (bool, Duration, usize, usize) {
319
+ let mut out = (false, Duration::from_millis(0), 1000usize, 16usize);
320
+ if let Some(c) = cfg {
321
+ if c.stale_time > 0 {
322
+ out.0 = true;
323
+ out.1 = Duration::from_millis(c.stale_time as u64);
324
+ if c.stale_max_entries > 0 {
325
+ out.2 = c.stale_max_entries;
326
+ }
327
+ if c.max_sources_per_target > 0 {
328
+ out.3 = c.max_sources_per_target;
329
+ }
330
+ }
331
+ }
332
+ out
333
+ }
334
+
167
335
  impl StaleTracker {
168
336
  /// new constructs a tracker from an InvalidationConfig.
169
337
  /// Tracker is disabled (cheap no-op) when cfg is None or stale_time ≤ 0.
170
338
  pub fn new(cfg: Option<&InvalidationConfig>) -> Self {
171
- let mut enabled = false;
172
- let mut stale_time = Duration::from_millis(0);
173
- let mut max_entries = 1000usize;
174
- let mut max_sources = 16usize;
175
- if let Some(c) = cfg {
176
- if c.stale_time > 0 {
177
- enabled = true;
178
- stale_time = Duration::from_millis(c.stale_time as u64);
179
- if c.stale_max_entries > 0 {
180
- max_entries = c.stale_max_entries;
181
- }
182
- if c.max_sources_per_target > 0 {
183
- max_sources = c.max_sources_per_target;
184
- }
185
- }
186
- }
187
- StaleTracker {
188
- state: Mutex::new(TrackerState {
189
- entries: HashMap::new(),
190
- order: Vec::new(),
191
- seq: 0,
192
- }),
193
- enabled,
194
- stale_time,
195
- max_entries,
196
- max_sources,
197
- }
339
+ let (enabled, stale_time, max_entries, max_sources) = tracker_settings(cfg);
340
+ StaleTracker { state: Mutex::new(TrackerState::default()), enabled, stale_time, max_entries, max_sources }
198
341
  }
199
342
 
200
343
  /// enabled reports whether the tracker will record / report stale entries.
@@ -206,12 +349,7 @@ impl StaleTracker {
206
349
  /// `params`, and records each resolved target as freshly stale.
207
350
  /// `mutation_selector` is appended to each entry's `by` list (capped at
208
351
  /// max_sources, deduped).
209
- pub async fn mark_stale(
210
- &self,
211
- invalidate: &[String],
212
- params: &HashMap<String, String>,
213
- mutation_selector: &str,
214
- ) {
352
+ pub async fn mark_stale(&self, invalidate: &[String], params: &HashMap<String, String>, mutation_selector: &str) {
215
353
  if !self.enabled || invalidate.is_empty() {
216
354
  return;
217
355
  }
@@ -219,69 +357,28 @@ impl StaleTracker {
219
357
  s.seq += 1;
220
358
  let seq = s.seq;
221
359
  let until = Instant::now() + self.stale_time;
222
- let resolved = resolve_invalidation_targets_for_mutation(invalidate, params);
223
- for target in resolved {
224
- upsert_locked(&mut s, &target, until, seq, mutation_selector, self.max_sources);
360
+ for target in resolve_invalidation_targets_for_mutation(invalidate, params) {
361
+ s.upsert(&target, until, seq, mutation_selector, self.max_sources);
225
362
  }
226
- enforce_capacity_locked(&mut s, self.max_entries);
363
+ s.enforce_capacity(self.max_entries);
227
364
  }
228
365
 
229
366
  /// lookup_stale returns (invalidated_by, is_stale): exact + pattern lookup
230
367
  /// with expired-key sweep. Disabled tracker → (vec![], false).
231
- pub async fn lookup_stale(
232
- &self,
233
- concrete_selector: &str,
234
- concrete_path: &str,
235
- method: &str,
236
- now: Instant,
237
- ) -> (Vec<String>, bool) {
368
+ pub async fn lookup_stale(&self, concrete_selector: &str, concrete_path: &str, method: &str, now: Instant) -> (Vec<String>, bool) {
238
369
  if !self.enabled {
239
370
  return (Vec::new(), false);
240
371
  }
241
- let mut s = self.state.lock().await;
242
- lookup_locked(&mut s, concrete_selector, concrete_path, method, now)
372
+ self.state.lock().await.lookup(concrete_selector, concrete_path, method, now)
243
373
  }
244
374
 
245
375
  /// clear_stale drops stale entries whose seq ≤ seq_snapshot and which match
246
376
  /// the given concrete_path under method (exact key + pattern keys).
247
- pub async fn clear_stale(
248
- &self,
249
- concrete_selector: &str,
250
- concrete_path: &str,
251
- method: &str,
252
- seq_snapshot: u64,
253
- ) {
377
+ pub async fn clear_stale(&self, concrete_selector: &str, concrete_path: &str, method: &str, seq_snapshot: u64) {
254
378
  if !self.enabled {
255
379
  return;
256
380
  }
257
- let mut s = self.state.lock().await;
258
- if let Some(entry) = s.entries.get(concrete_selector) {
259
- if entry.seq <= seq_snapshot {
260
- delete_locked(&mut s, concrete_selector);
261
- }
262
- }
263
- let mut to_delete: Vec<String> = Vec::new();
264
- for (key, entry) in s.entries.iter() {
265
- if entry.seq > seq_snapshot {
266
- continue;
267
- }
268
- if !key.contains('{') && !key.contains(':') {
269
- continue;
270
- }
271
- let space_idx = match key.find(' ') {
272
- Some(i) => i,
273
- None => continue,
274
- };
275
- if &key[..space_idx] != method {
276
- continue;
277
- }
278
- if path_matches_pattern(concrete_path, &key[space_idx + 1..]) {
279
- to_delete.push(key.clone());
280
- }
281
- }
282
- for key in to_delete {
283
- delete_locked(&mut s, &key);
284
- }
381
+ self.state.lock().await.clear(concrete_selector, concrete_path, method, seq_snapshot);
285
382
  }
286
383
 
287
384
  /// is_stale reports whether (method, path) currently sits inside an active
@@ -290,152 +387,26 @@ impl StaleTracker {
290
387
  if !self.enabled {
291
388
  return false;
292
389
  }
293
- let mut s = self.state.lock().await;
294
390
  let selector = format!("{} {}", method, path);
295
- let (_, is_stale) = lookup_locked(&mut s, &selector, path, method, Instant::now());
296
- is_stale
391
+ self.state.lock().await.lookup(&selector, path, method, Instant::now()).1
297
392
  }
298
393
 
299
394
  /// build_request_meta returns a RequestMeta snapshot for the request about
300
395
  /// to fire, or None when tracker is disabled.
301
- pub async fn build_request_meta(
302
- &self,
303
- concrete_selector: &str,
304
- concrete_path: &str,
305
- method: &str,
306
- ) -> Option<RequestMeta> {
396
+ pub async fn build_request_meta(&self, concrete_selector: &str, concrete_path: &str, method: &str) -> Option<RequestMeta> {
307
397
  if !self.enabled {
308
398
  return None;
309
399
  }
310
400
  let mut s = self.state.lock().await;
311
- let (by, is_stale) =
312
- lookup_locked(&mut s, concrete_selector, concrete_path, method, Instant::now());
313
- Some(RequestMeta {
314
- selector: concrete_selector.to_string(),
315
- is_stale,
316
- invalidated_by: by,
317
- seq_snapshot: s.seq,
318
- })
319
- }
320
- }
321
-
322
- fn upsert_locked(
323
- s: &mut TrackerState,
324
- key: &str,
325
- until: Instant,
326
- seq: u64,
327
- mutation_selector: &str,
328
- max_sources: usize,
329
- ) {
330
- if let Some(existing) = s.entries.get_mut(key) {
331
- existing.until = until;
332
- existing.seq = seq;
333
- if !mutation_selector.is_empty() && existing.by.len() < max_sources {
334
- let already = existing.by.iter().any(|b| b == mutation_selector);
335
- if !already {
336
- existing.by.push(mutation_selector.to_string());
337
- }
338
- }
339
- touch_order_locked(s, key);
340
- return;
341
- }
342
- let by = if mutation_selector.is_empty() {
343
- Vec::new()
344
- } else {
345
- vec![mutation_selector.to_string()]
346
- };
347
- s.entries.insert(key.to_string(), StaleEntry { by, seq, until });
348
- s.order.push(key.to_string());
349
- }
350
-
351
- fn touch_order_locked(s: &mut TrackerState, key: &str) {
352
- if let Some(idx) = s.order.iter().position(|k| k == key) {
353
- s.order.remove(idx);
401
+ let (by, is_stale) = s.lookup(concrete_selector, concrete_path, method, Instant::now());
402
+ Some(RequestMeta { selector: concrete_selector.to_string(), is_stale, invalidated_by: by, seq_snapshot: s.seq })
354
403
  }
355
- s.order.push(key.to_string());
356
404
  }
357
405
 
358
- fn delete_locked(s: &mut TrackerState, key: &str) {
359
- if s.entries.remove(key).is_none() {
360
- return;
361
- }
362
- if let Some(idx) = s.order.iter().position(|k| k == key) {
363
- s.order.remove(idx);
364
- }
365
- }
366
-
367
- fn lookup_locked(
368
- s: &mut TrackerState,
369
- concrete_selector: &str,
370
- concrete_path: &str,
371
- method: &str,
372
- now: Instant,
373
- ) -> (Vec<String>, bool) {
374
- let mut all_by: Vec<String> = Vec::new();
375
- let mut exact_expired = false;
376
- if let Some(exact) = s.entries.get(concrete_selector) {
377
- if exact.until > now {
378
- all_by.extend(exact.by.iter().cloned());
379
- } else {
380
- exact_expired = true;
381
- }
382
- }
383
- if exact_expired {
384
- delete_locked(s, concrete_selector);
385
- }
386
- let mut expired: Vec<String> = Vec::new();
387
- for (key, entry) in s.entries.iter() {
388
- if entry.until <= now {
389
- expired.push(key.clone());
390
- continue;
391
- }
392
- if key == concrete_selector {
393
- continue;
394
- }
395
- if !key.contains('{') && !key.contains(':') {
396
- continue;
397
- }
398
- let space_idx = match key.find(' ') {
399
- Some(i) => i,
400
- None => continue,
401
- };
402
- if &key[..space_idx] != method {
403
- continue;
404
- }
405
- if path_matches_pattern(concrete_path, &key[space_idx + 1..]) {
406
- all_by.extend(entry.by.iter().cloned());
407
- }
408
- }
409
- for key in expired {
410
- delete_locked(s, &key);
411
- }
412
- let mut deduped: Vec<String> = Vec::with_capacity(all_by.len());
413
- for src in all_by {
414
- if !deduped.iter().any(|d| d == &src) {
415
- deduped.push(src);
416
- }
417
- }
418
- let any = !deduped.is_empty();
419
- (deduped, any)
420
- }
421
-
422
- fn enforce_capacity_locked(s: &mut TrackerState, max_entries: usize) {
423
- if s.entries.len() <= max_entries {
424
- return;
425
- }
426
- let now = Instant::now();
427
- let expired: Vec<String> = s
428
- .entries
429
- .iter()
430
- .filter(|(_, e)| e.until <= now)
431
- .map(|(k, _)| k.clone())
432
- .collect();
433
- for key in expired {
434
- delete_locked(s, &key);
435
- }
436
- let target = if max_entries / 2 < 1 { 1 } else { max_entries / 2 };
437
- while s.entries.len() > target && !s.order.is_empty() {
438
- let oldest = s.order[0].clone();
439
- delete_locked(s, &oldest);
406
+ /// key_is_pattern reports whether a stored `METHOD /path` key holds placeholders.
407
+ pub(crate) fn key_is_pattern(key: &str) -> bool {
408
+ match key.find(' ') {
409
+ Some(i) => path_has_placeholders(&key[i + 1..]),
410
+ None => false,
440
411
  }
441
412
  }