@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
@@ -14,7 +14,9 @@ def _now() -> float:
14
14
  return time.monotonic()
15
15
 
16
16
 
17
- _PATH_PARAM_RE = re.compile(r"\{([a-zA-Z_][a-zA-Z0-9_]*)\}|:([a-zA-Z_][a-zA-Z0-9_]*)")
17
+ # `{name}` (any name without braces or slashes) or a `:name` segment; a colon
18
+ # inside a segment (`/ops/{id}:cancel`) is literal.
19
+ _PATH_PARAM_RE = re.compile(r"\{([^{}/]+)\}|(?:(?<=/)|^):([a-zA-Z_][a-zA-Z0-9_]*)")
18
20
 
19
21
 
20
22
  @dataclass
@@ -79,8 +81,8 @@ def resolve_invalidation_targets_for_mutation(
79
81
  ) -> list[str]:
80
82
  """Expand ``METHOD /path/{id}`` template invalidation targets to concrete selectors.
81
83
 
82
- Targets without placeholders are passed through. Targets whose placeholders cannot
83
- be resolved against *params* are dropped (no point storing a half-templated key).
84
+ Targets without placeholders are passed through. A target whose placeholders cannot
85
+ all be resolved stays a pattern, so it marks every instance stale (TS parity).
84
86
  """
85
87
  resolved: list[str] = []
86
88
  for entry in targets:
@@ -96,6 +98,7 @@ def resolve_invalidation_targets_for_mutation(
96
98
  try:
97
99
  concrete = interpolate_path(template, params)
98
100
  except ValueError:
101
+ resolved.append(entry)
99
102
  continue
100
103
  resolved.append(f"{method} {concrete}")
101
104
  return resolved
@@ -136,6 +139,158 @@ def lookup_stale(key: str, stale_map: "OrderedDict[str, Any]") -> bool:
136
139
  return _now() < until
137
140
 
138
141
 
142
+ class _StaleStore:
143
+ """Stale state shared by the async and sync trackers; callers hold their own lock.
144
+
145
+ Concrete keys are looked up directly; keys that still hold ``{name}``
146
+ placeholders are also indexed per method, so a lookup scans only the patterns
147
+ of its method, not every entry. Expired concrete entries go on access, expired
148
+ patterns when scanned, and the rest when the map is over capacity.
149
+ """
150
+
151
+ def __init__(
152
+ self, stale_time: float, stale_max_entries: int, max_sources_per_target: int
153
+ ) -> None:
154
+ self.enabled = stale_time > 0
155
+ self.stale_time = max(stale_time, 0.0)
156
+ self.max_entries = max(stale_max_entries, 1)
157
+ self.max_sources = max(max_sources_per_target, 1)
158
+ self.map: OrderedDict[str, _StaleEntry] = OrderedDict()
159
+ self.patterns: dict[str, dict[str, None]] = {}
160
+ self.seq = 0
161
+
162
+ def delete(self, key: str) -> None:
163
+ if self.map.pop(key, None) is None:
164
+ return
165
+ method, path = _split_key(key)
166
+ bucket = self.patterns.get(method)
167
+ if bucket is not None and _has_unresolved(path):
168
+ bucket.pop(key, None)
169
+ if not bucket:
170
+ del self.patterns[method]
171
+
172
+ def upsert(
173
+ self, key: str, until: float, seq: int, mutation_selector: str | None
174
+ ) -> None:
175
+ existing = self.map.get(key)
176
+ if existing is not None:
177
+ existing.until = until
178
+ existing.seq = seq
179
+ if mutation_selector and mutation_selector not in existing.by:
180
+ if len(existing.by) < self.max_sources:
181
+ existing.by.append(mutation_selector)
182
+ self.map.move_to_end(key)
183
+ return
184
+ by = [mutation_selector] if mutation_selector else []
185
+ self.map[key] = _StaleEntry(by=by, seq=seq, until=until)
186
+ method, path = _split_key(key)
187
+ if _has_unresolved(path):
188
+ self.patterns.setdefault(method, {})[key] = None
189
+
190
+ def invalidate(self, patterns: list[str]) -> None:
191
+ self.evict_expired()
192
+ targets = resolve_invalidation_targets(patterns, self.map)
193
+ self.seq += 1
194
+ until = _now() + self.stale_time
195
+ for t in targets:
196
+ self.upsert(t, until, self.seq, None)
197
+ self.enforce_capacity()
198
+
199
+ def mark_stale(
200
+ self,
201
+ invalidate: list[str],
202
+ params: dict[str, str] | None,
203
+ mutation_selector: str,
204
+ ) -> None:
205
+ self.seq += 1
206
+ until = _now() + self.stale_time
207
+ for target in resolve_invalidation_targets_for_mutation(invalidate, params):
208
+ self.upsert(target, until, self.seq, mutation_selector)
209
+ self.enforce_capacity()
210
+
211
+ def mark(self, key: str) -> None:
212
+ self.seq += 1
213
+ self.upsert(key, _now() + self.stale_time, self.seq, None)
214
+ self.enforce_capacity()
215
+
216
+ def lookup(
217
+ self, concrete_selector: str, concrete_path: str, method: str, now: float
218
+ ) -> tuple[list[str], bool]:
219
+ all_by: list[str] = []
220
+ exact = self.map.get(concrete_selector)
221
+ if exact is not None:
222
+ if exact.until > now:
223
+ all_by.extend(exact.by)
224
+ else:
225
+ self.delete(concrete_selector)
226
+ expired: list[str] = []
227
+ for key in self.patterns.get(method, {}):
228
+ entry = self.map[key]
229
+ if entry.until <= now:
230
+ expired.append(key)
231
+ continue
232
+ if key != concrete_selector and path_matches_pattern(
233
+ concrete_path, _split_key(key)[1]
234
+ ):
235
+ all_by.extend(entry.by)
236
+ for key in expired:
237
+ self.delete(key)
238
+ deduped = list(dict.fromkeys(all_by))
239
+ return deduped, len(deduped) > 0
240
+
241
+ def clear(
242
+ self, concrete_selector: str, concrete_path: str, method: str, seq_snapshot: int
243
+ ) -> None:
244
+ exact = self.map.get(concrete_selector)
245
+ if exact and exact.seq <= seq_snapshot:
246
+ self.delete(concrete_selector)
247
+ to_delete = [
248
+ key
249
+ for key in self.patterns.get(method, {})
250
+ if self.map[key].seq <= seq_snapshot
251
+ and path_matches_pattern(concrete_path, _split_key(key)[1])
252
+ ]
253
+ for key in to_delete:
254
+ self.delete(key)
255
+
256
+ def request_meta(
257
+ self, concrete_selector: str, concrete_path: str, method: str
258
+ ) -> _RequestMeta:
259
+ by, is_stale = self.lookup(concrete_selector, concrete_path, method, _now())
260
+ return _RequestMeta(
261
+ selector=concrete_selector,
262
+ is_stale=is_stale,
263
+ invalidated_by=by,
264
+ seq_snapshot=self.seq,
265
+ )
266
+
267
+ def evict_expired(self) -> None:
268
+ now = _now()
269
+ for key in [k for k, v in self.map.items() if now >= v.until]:
270
+ self.delete(key)
271
+
272
+ def enforce_capacity(self) -> None:
273
+ if len(self.map) <= self.max_entries:
274
+ return
275
+ self.evict_expired()
276
+ target = max(self.max_entries // 2, 1)
277
+ while len(self.map) > target:
278
+ self.delete(next(iter(self.map)))
279
+
280
+
281
+ def _split_key(key: str) -> tuple[str, str]:
282
+ space_idx = key.find(" ")
283
+ if space_idx == -1:
284
+ return key, ""
285
+ return key[:space_idx], key[space_idx + 1 :]
286
+
287
+
288
+ def _split_method_path(method_or_key: str, path: str | None) -> tuple[str, str]:
289
+ if path is None:
290
+ return _split_key(method_or_key)
291
+ return method_or_key, path
292
+
293
+
139
294
  class _StaleTracker:
140
295
  """Async-aware stale tracker backed by ``asyncio.Lock``."""
141
296
 
@@ -145,31 +300,24 @@ class _StaleTracker:
145
300
  stale_max_entries: int = 1000,
146
301
  max_sources_per_target: int = 16,
147
302
  ) -> None:
148
- """ stale_time == 0 disables the tracker (no stale window applied). """
149
- self._enabled = stale_time > 0
150
- self._stale_time = max(stale_time, 0.0)
151
- self._max_entries = max(stale_max_entries, 1)
152
- self._max_sources = max(max_sources_per_target, 1)
153
- self._map: OrderedDict[str, _StaleEntry] = OrderedDict()
303
+ """stale_time == 0 disables the tracker (no stale window applied)."""
304
+ self._store = _StaleStore(stale_time, stale_max_entries, max_sources_per_target)
154
305
  self._lock = asyncio.Lock()
155
- self._seq = 0
156
306
 
157
307
  @property
158
308
  def enabled(self) -> bool:
159
- return self._enabled
309
+ return self._store.enabled
310
+
311
+ @property
312
+ def _map(self) -> "OrderedDict[str, _StaleEntry]":
313
+ return self._store.map
160
314
 
161
315
  async def invalidate(self, patterns: list[str]) -> None:
162
316
  """Mark every existing key matching *patterns* as freshly stale."""
163
- if not self._enabled:
317
+ if not self.enabled:
164
318
  return
165
319
  async with self._lock:
166
- self._evict_expired()
167
- targets = resolve_invalidation_targets(patterns, self._map)
168
- self._seq += 1
169
- until = _now() + self._stale_time
170
- for t in targets:
171
- self._upsert(t, until, self._seq, mutation_selector=None)
172
- self._enforce_capacity()
320
+ self._store.invalidate(patterns)
173
321
 
174
322
  async def mark_stale(
175
323
  self,
@@ -178,27 +326,17 @@ class _StaleTracker:
178
326
  mutation_selector: str,
179
327
  ) -> None:
180
328
  """Bump seq, expand templated invalidation entries, store stale keys."""
181
- if not self._enabled or not invalidate:
329
+ if not self.enabled or not invalidate:
182
330
  return
183
331
  async with self._lock:
184
- self._seq += 1
185
- until = _now() + self._stale_time
186
- resolved = resolve_invalidation_targets_for_mutation(invalidate, params)
187
- for target in resolved:
188
- self._upsert(
189
- target, until, self._seq, mutation_selector=mutation_selector,
190
- )
191
- self._enforce_capacity()
332
+ self._store.mark_stale(invalidate, params, mutation_selector)
192
333
 
193
334
  async def mark(self, key: str) -> None:
194
335
  """Force *key* into the stale map. Used by SSE/WS open hooks and tests."""
195
- if not self._enabled:
336
+ if not self.enabled:
196
337
  return
197
338
  async with self._lock:
198
- self._seq += 1
199
- until = _now() + self._stale_time
200
- self._upsert(key, until, self._seq, mutation_selector=None)
201
- self._enforce_capacity()
339
+ self._store.mark(key)
202
340
 
203
341
  async def is_stale(self, method_or_key: str, path: str | None = None) -> bool:
204
342
  """Return True if (method, path) is currently stale.
@@ -206,21 +344,13 @@ class _StaleTracker:
206
344
  Accepts either ``is_stale("GET /users")`` or ``is_stale("GET", "/users")``.
207
345
  Walks pattern entries too.
208
346
  """
209
- if not self._enabled:
347
+ if not self.enabled:
210
348
  return False
211
- if path is None:
212
- method, concrete_path = self._split_key(method_or_key)
213
- else:
214
- method, concrete_path = method_or_key, path
349
+ method, concrete_path = _split_method_path(method_or_key, path)
215
350
  async with self._lock:
216
- now = _now()
217
- _by, is_stale = self._lookup_locked(
218
- concrete_selector=f"{method} {concrete_path}",
219
- concrete_path=concrete_path,
220
- method=method,
221
- now=now,
222
- )
223
- return is_stale
351
+ return self._store.lookup(
352
+ f"{method} {concrete_path}", concrete_path, method, _now()
353
+ )[1]
224
354
 
225
355
  async def lookup_stale(
226
356
  self,
@@ -229,10 +359,10 @@ class _StaleTracker:
229
359
  method: str,
230
360
  ) -> tuple[list[str], bool]:
231
361
  """Return (invalidated_by_list, is_stale): exact + pattern lookup with sweep."""
232
- if not self._enabled:
362
+ if not self.enabled:
233
363
  return [], False
234
364
  async with self._lock:
235
- return self._lookup_locked(concrete_selector, concrete_path, method, _now())
365
+ return self._store.lookup(concrete_selector, concrete_path, method, _now())
236
366
 
237
367
  async def clear_stale(
238
368
  self,
@@ -242,25 +372,10 @@ class _StaleTracker:
242
372
  seq_snapshot: int,
243
373
  ) -> None:
244
374
  """Drop seq-older entries matching *concrete_path* (exact + pattern keys)."""
245
- if not self._enabled:
375
+ if not self.enabled:
246
376
  return
247
377
  async with self._lock:
248
- exact = self._map.get(concrete_selector)
249
- if exact and exact.seq <= seq_snapshot:
250
- del self._map[concrete_selector]
251
- to_delete: list[str] = []
252
- for key, entry in self._map.items():
253
- if entry.seq > seq_snapshot:
254
- continue
255
- key_method, key_pattern = self._split_key(key)
256
- if "{" not in key_pattern and ":" not in key_pattern:
257
- continue
258
- if key_method != method:
259
- continue
260
- if path_matches_pattern(concrete_path, key_pattern):
261
- to_delete.append(key)
262
- for key in to_delete:
263
- del self._map[key]
378
+ self._store.clear(concrete_selector, concrete_path, method, seq_snapshot)
264
379
 
265
380
  async def build_request_meta(
266
381
  self,
@@ -268,98 +383,14 @@ class _StaleTracker:
268
383
  concrete_path: str,
269
384
  method: str,
270
385
  ) -> _RequestMeta | None:
271
- if not self._enabled:
386
+ if not self.enabled:
272
387
  return None
273
388
  async with self._lock:
274
- by, is_stale = self._lookup_locked(
275
- concrete_selector, concrete_path, method, _now(),
276
- )
277
- return _RequestMeta(
278
- selector=concrete_selector,
279
- is_stale=is_stale,
280
- invalidated_by=by,
281
- seq_snapshot=self._seq,
282
- )
283
-
284
- def _upsert(
285
- self,
286
- key: str,
287
- until: float,
288
- seq: int,
289
- mutation_selector: str | None,
290
- ) -> None:
291
- existing = self._map.get(key)
292
- if existing is not None:
293
- existing.until = until
294
- existing.seq = seq
295
- if mutation_selector and mutation_selector not in existing.by:
296
- if len(existing.by) < self._max_sources:
297
- existing.by.append(mutation_selector)
298
- self._map.move_to_end(key)
299
- return
300
- by = [mutation_selector] if mutation_selector else []
301
- self._map[key] = _StaleEntry(by=by, seq=seq, until=until)
302
- self._map.move_to_end(key)
303
-
304
- def _lookup_locked(
305
- self,
306
- concrete_selector: str,
307
- concrete_path: str,
308
- method: str,
309
- now: float,
310
- ) -> tuple[list[str], bool]:
311
- all_by: list[str] = []
312
- exact = self._map.get(concrete_selector)
313
- if exact is not None:
314
- if exact.until > now:
315
- all_by.extend(exact.by)
316
- else:
317
- del self._map[concrete_selector]
318
- expired: list[str] = []
319
- for key, entry in self._map.items():
320
- if entry.until <= now:
321
- expired.append(key)
322
- continue
323
- if "{" not in key and ":" not in key:
324
- continue
325
- if key == concrete_selector:
326
- continue
327
- key_method, key_pattern = self._split_key(key)
328
- if key_method != method:
329
- continue
330
- if path_matches_pattern(concrete_path, key_pattern):
331
- all_by.extend(entry.by)
332
- for key in expired:
333
- del self._map[key]
334
- deduped: list[str] = []
335
- seen: set[str] = set()
336
- for src in all_by:
337
- if src in seen:
338
- continue
339
- seen.add(src)
340
- deduped.append(src)
341
- return deduped, len(deduped) > 0
342
-
343
- def _evict_expired(self) -> None:
344
- now = _now()
345
- expired = [k for k, v in self._map.items() if now >= v.until]
346
- for k in expired:
347
- del self._map[k]
348
-
349
- def _enforce_capacity(self) -> None:
350
- if len(self._map) <= self._max_entries:
351
- return
352
- self._evict_expired()
353
- target = max(self._max_entries // 2, 1)
354
- while len(self._map) > target:
355
- self._map.popitem(last=False)
389
+ return self._store.request_meta(concrete_selector, concrete_path, method)
356
390
 
357
391
  @staticmethod
358
392
  def _split_key(key: str) -> tuple[str, str]:
359
- space_idx = key.find(" ")
360
- if space_idx == -1:
361
- return key, ""
362
- return key[:space_idx], key[space_idx + 1 :]
393
+ return _split_key(key)
363
394
 
364
395
 
365
396
  class _StaleTrackerSync:
@@ -371,30 +402,23 @@ class _StaleTrackerSync:
371
402
  stale_max_entries: int = 1000,
372
403
  max_sources_per_target: int = 16,
373
404
  ) -> None:
374
- """ stale_time == 0 disables the tracker (no stale window applied). """
375
- self._enabled = stale_time > 0
376
- self._stale_time = max(stale_time, 0.0)
377
- self._max_entries = max(stale_max_entries, 1)
378
- self._max_sources = max(max_sources_per_target, 1)
379
- self._map: OrderedDict[str, _StaleEntry] = OrderedDict()
405
+ """stale_time == 0 disables the tracker (no stale window applied)."""
406
+ self._store = _StaleStore(stale_time, stale_max_entries, max_sources_per_target)
380
407
  self._lock = threading.Lock()
381
- self._seq = 0
382
408
 
383
409
  @property
384
410
  def enabled(self) -> bool:
385
- return self._enabled
411
+ return self._store.enabled
412
+
413
+ @property
414
+ def _map(self) -> "OrderedDict[str, _StaleEntry]":
415
+ return self._store.map
386
416
 
387
417
  def invalidate(self, patterns: list[str]) -> None:
388
- if not self._enabled:
418
+ if not self.enabled:
389
419
  return
390
420
  with self._lock:
391
- self._evict_expired()
392
- targets = resolve_invalidation_targets(patterns, self._map)
393
- self._seq += 1
394
- until = _now() + self._stale_time
395
- for t in targets:
396
- self._upsert(t, until, self._seq, None)
397
- self._enforce_capacity()
421
+ self._store.invalidate(patterns)
398
422
 
399
423
  def mark_stale(
400
424
  self,
@@ -402,37 +426,25 @@ class _StaleTrackerSync:
402
426
  params: dict[str, str] | None,
403
427
  mutation_selector: str,
404
428
  ) -> None:
405
- if not self._enabled or not invalidate:
429
+ if not self.enabled or not invalidate:
406
430
  return
407
431
  with self._lock:
408
- self._seq += 1
409
- until = _now() + self._stale_time
410
- resolved = resolve_invalidation_targets_for_mutation(invalidate, params)
411
- for target in resolved:
412
- self._upsert(target, until, self._seq, mutation_selector)
413
- self._enforce_capacity()
432
+ self._store.mark_stale(invalidate, params, mutation_selector)
414
433
 
415
434
  def mark(self, key: str) -> None:
416
- if not self._enabled:
435
+ if not self.enabled:
417
436
  return
418
437
  with self._lock:
419
- self._seq += 1
420
- until = _now() + self._stale_time
421
- self._upsert(key, until, self._seq, None)
422
- self._enforce_capacity()
438
+ self._store.mark(key)
423
439
 
424
440
  def is_stale(self, method_or_key: str, path: str | None = None) -> bool:
425
- if not self._enabled:
441
+ if not self.enabled:
426
442
  return False
427
- if path is None:
428
- method, concrete_path = _StaleTracker._split_key(method_or_key)
429
- else:
430
- method, concrete_path = method_or_key, path
443
+ method, concrete_path = _split_method_path(method_or_key, path)
431
444
  with self._lock:
432
- _by, is_stale = self._lookup_locked(
433
- f"{method} {concrete_path}", concrete_path, method, _now(),
434
- )
435
- return is_stale
445
+ return self._store.lookup(
446
+ f"{method} {concrete_path}", concrete_path, method, _now()
447
+ )[1]
436
448
 
437
449
  def lookup_stale(
438
450
  self,
@@ -440,10 +452,10 @@ class _StaleTrackerSync:
440
452
  concrete_path: str,
441
453
  method: str,
442
454
  ) -> tuple[list[str], bool]:
443
- if not self._enabled:
455
+ if not self.enabled:
444
456
  return [], False
445
457
  with self._lock:
446
- return self._lookup_locked(concrete_selector, concrete_path, method, _now())
458
+ return self._store.lookup(concrete_selector, concrete_path, method, _now())
447
459
 
448
460
  def clear_stale(
449
461
  self,
@@ -452,25 +464,10 @@ class _StaleTrackerSync:
452
464
  method: str,
453
465
  seq_snapshot: int,
454
466
  ) -> None:
455
- if not self._enabled:
467
+ if not self.enabled:
456
468
  return
457
469
  with self._lock:
458
- exact = self._map.get(concrete_selector)
459
- if exact and exact.seq <= seq_snapshot:
460
- del self._map[concrete_selector]
461
- to_delete: list[str] = []
462
- for key, entry in self._map.items():
463
- if entry.seq > seq_snapshot:
464
- continue
465
- key_method, key_pattern = _StaleTracker._split_key(key)
466
- if "{" not in key_pattern and ":" not in key_pattern:
467
- continue
468
- if key_method != method:
469
- continue
470
- if path_matches_pattern(concrete_path, key_pattern):
471
- to_delete.append(key)
472
- for key in to_delete:
473
- del self._map[key]
470
+ self._store.clear(concrete_selector, concrete_path, method, seq_snapshot)
474
471
 
475
472
  def build_request_meta(
476
473
  self,
@@ -478,91 +475,10 @@ class _StaleTrackerSync:
478
475
  concrete_path: str,
479
476
  method: str,
480
477
  ) -> _RequestMeta | None:
481
- if not self._enabled:
478
+ if not self.enabled:
482
479
  return None
483
480
  with self._lock:
484
- by, is_stale = self._lookup_locked(
485
- concrete_selector, concrete_path, method, _now(),
486
- )
487
- return _RequestMeta(
488
- selector=concrete_selector,
489
- is_stale=is_stale,
490
- invalidated_by=by,
491
- seq_snapshot=self._seq,
492
- )
493
-
494
- def _upsert(
495
- self,
496
- key: str,
497
- until: float,
498
- seq: int,
499
- mutation_selector: str | None,
500
- ) -> None:
501
- existing = self._map.get(key)
502
- if existing is not None:
503
- existing.until = until
504
- existing.seq = seq
505
- if mutation_selector and mutation_selector not in existing.by:
506
- if len(existing.by) < self._max_sources:
507
- existing.by.append(mutation_selector)
508
- self._map.move_to_end(key)
509
- return
510
- by = [mutation_selector] if mutation_selector else []
511
- self._map[key] = _StaleEntry(by=by, seq=seq, until=until)
512
- self._map.move_to_end(key)
513
-
514
- def _lookup_locked(
515
- self,
516
- concrete_selector: str,
517
- concrete_path: str,
518
- method: str,
519
- now: float,
520
- ) -> tuple[list[str], bool]:
521
- all_by: list[str] = []
522
- exact = self._map.get(concrete_selector)
523
- if exact is not None:
524
- if exact.until > now:
525
- all_by.extend(exact.by)
526
- else:
527
- del self._map[concrete_selector]
528
- expired: list[str] = []
529
- for key, entry in self._map.items():
530
- if entry.until <= now:
531
- expired.append(key)
532
- continue
533
- if "{" not in key and ":" not in key:
534
- continue
535
- if key == concrete_selector:
536
- continue
537
- key_method, key_pattern = _StaleTracker._split_key(key)
538
- if key_method != method:
539
- continue
540
- if path_matches_pattern(concrete_path, key_pattern):
541
- all_by.extend(entry.by)
542
- for key in expired:
543
- del self._map[key]
544
- deduped: list[str] = []
545
- seen: set[str] = set()
546
- for src in all_by:
547
- if src in seen:
548
- continue
549
- seen.add(src)
550
- deduped.append(src)
551
- return deduped, len(deduped) > 0
552
-
553
- def _evict_expired(self) -> None:
554
- now = _now()
555
- expired = [k for k, v in self._map.items() if now >= v.until]
556
- for k in expired:
557
- del self._map[k]
558
-
559
- def _enforce_capacity(self) -> None:
560
- if len(self._map) <= self._max_entries:
561
- return
562
- self._evict_expired()
563
- target = max(self._max_entries // 2, 1)
564
- while len(self._map) > target:
565
- self._map.popitem(last=False)
481
+ return self._store.request_meta(concrete_selector, concrete_path, method)
566
482
 
567
483
 
568
484
  __all__ = [