@optimystic/db-core 0.13.5 → 0.14.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 (447) hide show
  1. package/dist/src/cluster/structs.d.ts +13 -0
  2. package/dist/src/cluster/structs.d.ts.map +1 -1
  3. package/dist/src/cohort-topic/addressing.d.ts +53 -0
  4. package/dist/src/cohort-topic/addressing.d.ts.map +1 -0
  5. package/dist/src/cohort-topic/addressing.js +95 -0
  6. package/dist/src/cohort-topic/addressing.js.map +1 -0
  7. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.d.ts +105 -0
  8. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.d.ts.map +1 -0
  9. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.js +197 -0
  10. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.js.map +1 -0
  11. package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts +50 -0
  12. package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts.map +1 -0
  13. package/dist/src/cohort-topic/antidos/bootstrap-evidence.js +62 -0
  14. package/dist/src/cohort-topic/antidos/bootstrap-evidence.js.map +1 -0
  15. package/dist/src/cohort-topic/antidos/index.d.ts +6 -0
  16. package/dist/src/cohort-topic/antidos/index.d.ts.map +1 -0
  17. package/dist/src/cohort-topic/antidos/index.js +6 -0
  18. package/dist/src/cohort-topic/antidos/index.js.map +1 -0
  19. package/dist/src/cohort-topic/antidos/rate-limiter.d.ts +95 -0
  20. package/dist/src/cohort-topic/antidos/rate-limiter.d.ts.map +1 -0
  21. package/dist/src/cohort-topic/antidos/rate-limiter.js +146 -0
  22. package/dist/src/cohort-topic/antidos/rate-limiter.js.map +1 -0
  23. package/dist/src/cohort-topic/antidos/replay-guard.d.ts +37 -0
  24. package/dist/src/cohort-topic/antidos/replay-guard.d.ts.map +1 -0
  25. package/dist/src/cohort-topic/antidos/replay-guard.js +78 -0
  26. package/dist/src/cohort-topic/antidos/replay-guard.js.map +1 -0
  27. package/dist/src/cohort-topic/antidos/topic-budget.d.ts +50 -0
  28. package/dist/src/cohort-topic/antidos/topic-budget.d.ts.map +1 -0
  29. package/dist/src/cohort-topic/antidos/topic-budget.js +95 -0
  30. package/dist/src/cohort-topic/antidos/topic-budget.js.map +1 -0
  31. package/dist/src/cohort-topic/antiflood/index.d.ts +3 -0
  32. package/dist/src/cohort-topic/antiflood/index.d.ts.map +1 -0
  33. package/dist/src/cohort-topic/antiflood/index.js +3 -0
  34. package/dist/src/cohort-topic/antiflood/index.js.map +1 -0
  35. package/dist/src/cohort-topic/antiflood/invariants.d.ts +70 -0
  36. package/dist/src/cohort-topic/antiflood/invariants.d.ts.map +1 -0
  37. package/dist/src/cohort-topic/antiflood/invariants.js +86 -0
  38. package/dist/src/cohort-topic/antiflood/invariants.js.map +1 -0
  39. package/dist/src/cohort-topic/antiflood/jitter.d.ts +65 -0
  40. package/dist/src/cohort-topic/antiflood/jitter.d.ts.map +1 -0
  41. package/dist/src/cohort-topic/antiflood/jitter.js +75 -0
  42. package/dist/src/cohort-topic/antiflood/jitter.js.map +1 -0
  43. package/dist/src/cohort-topic/coldstart.d.ts +117 -0
  44. package/dist/src/cohort-topic/coldstart.d.ts.map +1 -0
  45. package/dist/src/cohort-topic/coldstart.js +127 -0
  46. package/dist/src/cohort-topic/coldstart.js.map +1 -0
  47. package/dist/src/cohort-topic/dmax.d.ts +44 -0
  48. package/dist/src/cohort-topic/dmax.d.ts.map +1 -0
  49. package/dist/src/cohort-topic/dmax.js +70 -0
  50. package/dist/src/cohort-topic/dmax.js.map +1 -0
  51. package/dist/src/cohort-topic/gossip/bus.d.ts +85 -0
  52. package/dist/src/cohort-topic/gossip/bus.d.ts.map +1 -0
  53. package/dist/src/cohort-topic/gossip/bus.js +146 -0
  54. package/dist/src/cohort-topic/gossip/bus.js.map +1 -0
  55. package/dist/src/cohort-topic/gossip/index.d.ts +4 -0
  56. package/dist/src/cohort-topic/gossip/index.d.ts.map +1 -0
  57. package/dist/src/cohort-topic/gossip/index.js +4 -0
  58. package/dist/src/cohort-topic/gossip/index.js.map +1 -0
  59. package/dist/src/cohort-topic/gossip/records.d.ts +12 -0
  60. package/dist/src/cohort-topic/gossip/records.d.ts.map +1 -0
  61. package/dist/src/cohort-topic/gossip/records.js +41 -0
  62. package/dist/src/cohort-topic/gossip/records.js.map +1 -0
  63. package/dist/src/cohort-topic/gossip/view.d.ts +39 -0
  64. package/dist/src/cohort-topic/gossip/view.d.ts.map +1 -0
  65. package/dist/src/cohort-topic/gossip/view.js +30 -0
  66. package/dist/src/cohort-topic/gossip/view.js.map +1 -0
  67. package/dist/src/cohort-topic/index.d.ts +21 -0
  68. package/dist/src/cohort-topic/index.d.ts.map +1 -0
  69. package/dist/src/cohort-topic/index.js +21 -0
  70. package/dist/src/cohort-topic/index.js.map +1 -0
  71. package/dist/src/cohort-topic/load/barometer.d.ts +78 -0
  72. package/dist/src/cohort-topic/load/barometer.d.ts.map +1 -0
  73. package/dist/src/cohort-topic/load/barometer.js +93 -0
  74. package/dist/src/cohort-topic/load/barometer.js.map +1 -0
  75. package/dist/src/cohort-topic/load/index.d.ts +2 -0
  76. package/dist/src/cohort-topic/load/index.d.ts.map +1 -0
  77. package/dist/src/cohort-topic/load/index.js +2 -0
  78. package/dist/src/cohort-topic/load/index.js.map +1 -0
  79. package/dist/src/cohort-topic/member-engine.d.ts +134 -0
  80. package/dist/src/cohort-topic/member-engine.d.ts.map +1 -0
  81. package/dist/src/cohort-topic/member-engine.js +264 -0
  82. package/dist/src/cohort-topic/member-engine.js.map +1 -0
  83. package/dist/src/cohort-topic/membership/index.d.ts +4 -0
  84. package/dist/src/cohort-topic/membership/index.d.ts.map +1 -0
  85. package/dist/src/cohort-topic/membership/index.js +4 -0
  86. package/dist/src/cohort-topic/membership/index.js.map +1 -0
  87. package/dist/src/cohort-topic/membership/publisher.d.ts +82 -0
  88. package/dist/src/cohort-topic/membership/publisher.d.ts.map +1 -0
  89. package/dist/src/cohort-topic/membership/publisher.js +107 -0
  90. package/dist/src/cohort-topic/membership/publisher.js.map +1 -0
  91. package/dist/src/cohort-topic/membership/source.d.ts +31 -0
  92. package/dist/src/cohort-topic/membership/source.d.ts.map +1 -0
  93. package/dist/src/cohort-topic/membership/source.js +32 -0
  94. package/dist/src/cohort-topic/membership/source.js.map +1 -0
  95. package/dist/src/cohort-topic/membership/verifier.d.ts +96 -0
  96. package/dist/src/cohort-topic/membership/verifier.d.ts.map +1 -0
  97. package/dist/src/cohort-topic/membership/verifier.js +250 -0
  98. package/dist/src/cohort-topic/membership/verifier.js.map +1 -0
  99. package/dist/src/cohort-topic/ports.d.ts +149 -0
  100. package/dist/src/cohort-topic/ports.d.ts.map +1 -0
  101. package/dist/src/cohort-topic/ports.js +25 -0
  102. package/dist/src/cohort-topic/ports.js.map +1 -0
  103. package/dist/src/cohort-topic/promotion.d.ts +135 -0
  104. package/dist/src/cohort-topic/promotion.d.ts.map +1 -0
  105. package/dist/src/cohort-topic/promotion.js +269 -0
  106. package/dist/src/cohort-topic/promotion.js.map +1 -0
  107. package/dist/src/cohort-topic/registration/bytes.d.ts +16 -0
  108. package/dist/src/cohort-topic/registration/bytes.d.ts.map +1 -0
  109. package/dist/src/cohort-topic/registration/bytes.js +32 -0
  110. package/dist/src/cohort-topic/registration/bytes.js.map +1 -0
  111. package/dist/src/cohort-topic/registration/handoff.d.ts +69 -0
  112. package/dist/src/cohort-topic/registration/handoff.d.ts.map +1 -0
  113. package/dist/src/cohort-topic/registration/handoff.js +98 -0
  114. package/dist/src/cohort-topic/registration/handoff.js.map +1 -0
  115. package/dist/src/cohort-topic/registration/index.d.ts +7 -0
  116. package/dist/src/cohort-topic/registration/index.d.ts.map +1 -0
  117. package/dist/src/cohort-topic/registration/index.js +7 -0
  118. package/dist/src/cohort-topic/registration/index.js.map +1 -0
  119. package/dist/src/cohort-topic/registration/renewal.d.ts +115 -0
  120. package/dist/src/cohort-topic/registration/renewal.d.ts.map +1 -0
  121. package/dist/src/cohort-topic/registration/renewal.js +306 -0
  122. package/dist/src/cohort-topic/registration/renewal.js.map +1 -0
  123. package/dist/src/cohort-topic/registration/sharding.d.ts +31 -0
  124. package/dist/src/cohort-topic/registration/sharding.d.ts.map +1 -0
  125. package/dist/src/cohort-topic/registration/sharding.js +48 -0
  126. package/dist/src/cohort-topic/registration/sharding.js.map +1 -0
  127. package/dist/src/cohort-topic/registration/store.d.ts +13 -0
  128. package/dist/src/cohort-topic/registration/store.d.ts.map +1 -0
  129. package/dist/src/cohort-topic/registration/store.js +72 -0
  130. package/dist/src/cohort-topic/registration/store.js.map +1 -0
  131. package/dist/src/cohort-topic/registration/types.d.ts +71 -0
  132. package/dist/src/cohort-topic/registration/types.d.ts.map +1 -0
  133. package/dist/src/cohort-topic/registration/types.js +27 -0
  134. package/dist/src/cohort-topic/registration/types.js.map +1 -0
  135. package/dist/src/cohort-topic/ring-hash.d.ts +27 -0
  136. package/dist/src/cohort-topic/ring-hash.d.ts.map +1 -0
  137. package/dist/src/cohort-topic/ring-hash.js +45 -0
  138. package/dist/src/cohort-topic/ring-hash.js.map +1 -0
  139. package/dist/src/cohort-topic/service.d.ts +157 -0
  140. package/dist/src/cohort-topic/service.d.ts.map +1 -0
  141. package/dist/src/cohort-topic/service.js +237 -0
  142. package/dist/src/cohort-topic/service.js.map +1 -0
  143. package/dist/src/cohort-topic/sig/index.d.ts +3 -0
  144. package/dist/src/cohort-topic/sig/index.d.ts.map +1 -0
  145. package/dist/src/cohort-topic/sig/index.js +3 -0
  146. package/dist/src/cohort-topic/sig/index.js.map +1 -0
  147. package/dist/src/cohort-topic/sig/payloads.d.ts +25 -0
  148. package/dist/src/cohort-topic/sig/payloads.d.ts.map +1 -0
  149. package/dist/src/cohort-topic/sig/payloads.js +25 -0
  150. package/dist/src/cohort-topic/sig/payloads.js.map +1 -0
  151. package/dist/src/cohort-topic/sig/threshold.d.ts +33 -0
  152. package/dist/src/cohort-topic/sig/threshold.d.ts.map +1 -0
  153. package/dist/src/cohort-topic/sig/threshold.js +51 -0
  154. package/dist/src/cohort-topic/sig/threshold.js.map +1 -0
  155. package/dist/src/cohort-topic/tiers.d.ts +58 -0
  156. package/dist/src/cohort-topic/tiers.d.ts.map +1 -0
  157. package/dist/src/cohort-topic/tiers.js +49 -0
  158. package/dist/src/cohort-topic/tiers.js.map +1 -0
  159. package/dist/src/cohort-topic/traffic.d.ts +90 -0
  160. package/dist/src/cohort-topic/traffic.d.ts.map +1 -0
  161. package/dist/src/cohort-topic/traffic.js +146 -0
  162. package/dist/src/cohort-topic/traffic.js.map +1 -0
  163. package/dist/src/cohort-topic/walk.d.ts +123 -0
  164. package/dist/src/cohort-topic/walk.d.ts.map +1 -0
  165. package/dist/src/cohort-topic/walk.js +150 -0
  166. package/dist/src/cohort-topic/walk.js.map +1 -0
  167. package/dist/src/cohort-topic/willingness.d.ts +123 -0
  168. package/dist/src/cohort-topic/willingness.d.ts.map +1 -0
  169. package/dist/src/cohort-topic/willingness.js +143 -0
  170. package/dist/src/cohort-topic/willingness.js.map +1 -0
  171. package/dist/src/cohort-topic/wire/codec.d.ts +42 -0
  172. package/dist/src/cohort-topic/wire/codec.d.ts.map +1 -0
  173. package/dist/src/cohort-topic/wire/codec.js +163 -0
  174. package/dist/src/cohort-topic/wire/codec.js.map +1 -0
  175. package/dist/src/cohort-topic/wire/index.d.ts +6 -0
  176. package/dist/src/cohort-topic/wire/index.d.ts.map +1 -0
  177. package/dist/src/cohort-topic/wire/index.js +6 -0
  178. package/dist/src/cohort-topic/wire/index.js.map +1 -0
  179. package/dist/src/cohort-topic/wire/payloads.d.ts +38 -0
  180. package/dist/src/cohort-topic/wire/payloads.d.ts.map +1 -0
  181. package/dist/src/cohort-topic/wire/payloads.js +102 -0
  182. package/dist/src/cohort-topic/wire/payloads.js.map +1 -0
  183. package/dist/src/cohort-topic/wire/types.d.ts +304 -0
  184. package/dist/src/cohort-topic/wire/types.d.ts.map +1 -0
  185. package/dist/src/cohort-topic/wire/types.js +16 -0
  186. package/dist/src/cohort-topic/wire/types.js.map +1 -0
  187. package/dist/src/cohort-topic/wire/validate.d.ts +25 -0
  188. package/dist/src/cohort-topic/wire/validate.d.ts.map +1 -0
  189. package/dist/src/cohort-topic/wire/validate.js +427 -0
  190. package/dist/src/cohort-topic/wire/validate.js.map +1 -0
  191. package/dist/src/collection/collection.d.ts +58 -1
  192. package/dist/src/collection/collection.d.ts.map +1 -1
  193. package/dist/src/collection/collection.js +76 -0
  194. package/dist/src/collection/collection.js.map +1 -1
  195. package/dist/src/collections/tree/tree.d.ts +58 -2
  196. package/dist/src/collections/tree/tree.d.ts.map +1 -1
  197. package/dist/src/collections/tree/tree.js +59 -2
  198. package/dist/src/collections/tree/tree.js.map +1 -1
  199. package/dist/src/index.d.ts +3 -0
  200. package/dist/src/index.d.ts.map +1 -1
  201. package/dist/src/index.js +3 -0
  202. package/dist/src/index.js.map +1 -1
  203. package/dist/src/log/log.d.ts +42 -4
  204. package/dist/src/log/log.d.ts.map +1 -1
  205. package/dist/src/log/log.js +71 -3
  206. package/dist/src/log/log.js.map +1 -1
  207. package/dist/src/log/struct.d.ts +122 -2
  208. package/dist/src/log/struct.d.ts.map +1 -1
  209. package/dist/src/log/struct.js.map +1 -1
  210. package/dist/src/matchmaking/capability-filter.d.ts +24 -0
  211. package/dist/src/matchmaking/capability-filter.d.ts.map +1 -0
  212. package/dist/src/matchmaking/capability-filter.js +37 -0
  213. package/dist/src/matchmaking/capability-filter.js.map +1 -0
  214. package/dist/src/matchmaking/config.d.ts +77 -0
  215. package/dist/src/matchmaking/config.d.ts.map +1 -0
  216. package/dist/src/matchmaking/config.js +69 -0
  217. package/dist/src/matchmaking/config.js.map +1 -0
  218. package/dist/src/matchmaking/index.d.ts +21 -0
  219. package/dist/src/matchmaking/index.d.ts.map +1 -0
  220. package/dist/src/matchmaking/index.js +21 -0
  221. package/dist/src/matchmaking/index.js.map +1 -0
  222. package/dist/src/matchmaking/multi-cohort-seeker.d.ts +133 -0
  223. package/dist/src/matchmaking/multi-cohort-seeker.d.ts.map +1 -0
  224. package/dist/src/matchmaking/multi-cohort-seeker.js +142 -0
  225. package/dist/src/matchmaking/multi-cohort-seeker.js.map +1 -0
  226. package/dist/src/matchmaking/provider.d.ts +70 -0
  227. package/dist/src/matchmaking/provider.d.ts.map +1 -0
  228. package/dist/src/matchmaking/provider.js +93 -0
  229. package/dist/src/matchmaking/provider.js.map +1 -0
  230. package/dist/src/matchmaking/query-eval.d.ts +53 -0
  231. package/dist/src/matchmaking/query-eval.d.ts.map +1 -0
  232. package/dist/src/matchmaking/query-eval.js +69 -0
  233. package/dist/src/matchmaking/query-eval.js.map +1 -0
  234. package/dist/src/matchmaking/seeker-walk.d.ts +95 -0
  235. package/dist/src/matchmaking/seeker-walk.d.ts.map +1 -0
  236. package/dist/src/matchmaking/seeker-walk.js +75 -0
  237. package/dist/src/matchmaking/seeker-walk.js.map +1 -0
  238. package/dist/src/matchmaking/seeker.d.ts +51 -0
  239. package/dist/src/matchmaking/seeker.d.ts.map +1 -0
  240. package/dist/src/matchmaking/seeker.js +62 -0
  241. package/dist/src/matchmaking/seeker.js.map +1 -0
  242. package/dist/src/matchmaking/topic-anchor.d.ts +54 -0
  243. package/dist/src/matchmaking/topic-anchor.d.ts.map +1 -0
  244. package/dist/src/matchmaking/topic-anchor.js +67 -0
  245. package/dist/src/matchmaking/topic-anchor.js.map +1 -0
  246. package/dist/src/matchmaking/voting-quorum.d.ts +233 -0
  247. package/dist/src/matchmaking/voting-quorum.d.ts.map +1 -0
  248. package/dist/src/matchmaking/voting-quorum.js +238 -0
  249. package/dist/src/matchmaking/voting-quorum.js.map +1 -0
  250. package/dist/src/matchmaking/wire.d.ts +259 -0
  251. package/dist/src/matchmaking/wire.d.ts.map +1 -0
  252. package/dist/src/matchmaking/wire.js +484 -0
  253. package/dist/src/matchmaking/wire.js.map +1 -0
  254. package/dist/src/network/i-repo.d.ts +9 -0
  255. package/dist/src/network/i-repo.d.ts.map +1 -1
  256. package/dist/src/network/repo-protocol.d.ts +3 -1
  257. package/dist/src/network/repo-protocol.d.ts.map +1 -1
  258. package/dist/src/network/struct.d.ts +23 -1
  259. package/dist/src/network/struct.d.ts.map +1 -1
  260. package/dist/src/reactivity/backfill.d.ts +118 -0
  261. package/dist/src/reactivity/backfill.d.ts.map +1 -0
  262. package/dist/src/reactivity/backfill.js +137 -0
  263. package/dist/src/reactivity/backfill.js.map +1 -0
  264. package/dist/src/reactivity/backpressure.d.ts +99 -0
  265. package/dist/src/reactivity/backpressure.d.ts.map +1 -0
  266. package/dist/src/reactivity/backpressure.js +153 -0
  267. package/dist/src/reactivity/backpressure.js.map +1 -0
  268. package/dist/src/reactivity/checkpoint.d.ts +165 -0
  269. package/dist/src/reactivity/checkpoint.d.ts.map +1 -0
  270. package/dist/src/reactivity/checkpoint.js +232 -0
  271. package/dist/src/reactivity/checkpoint.js.map +1 -0
  272. package/dist/src/reactivity/config.d.ts +131 -0
  273. package/dist/src/reactivity/config.d.ts.map +1 -0
  274. package/dist/src/reactivity/config.js +135 -0
  275. package/dist/src/reactivity/config.js.map +1 -0
  276. package/dist/src/reactivity/dedupe.d.ts +51 -0
  277. package/dist/src/reactivity/dedupe.d.ts.map +1 -0
  278. package/dist/src/reactivity/dedupe.js +90 -0
  279. package/dist/src/reactivity/dedupe.js.map +1 -0
  280. package/dist/src/reactivity/forwarder.d.ts +50 -0
  281. package/dist/src/reactivity/forwarder.d.ts.map +1 -0
  282. package/dist/src/reactivity/forwarder.js +52 -0
  283. package/dist/src/reactivity/forwarder.js.map +1 -0
  284. package/dist/src/reactivity/index.d.ts +34 -0
  285. package/dist/src/reactivity/index.d.ts.map +1 -0
  286. package/dist/src/reactivity/index.js +34 -0
  287. package/dist/src/reactivity/index.js.map +1 -0
  288. package/dist/src/reactivity/notification.d.ts +72 -0
  289. package/dist/src/reactivity/notification.d.ts.map +1 -0
  290. package/dist/src/reactivity/notification.js +92 -0
  291. package/dist/src/reactivity/notification.js.map +1 -0
  292. package/dist/src/reactivity/policy.d.ts +59 -0
  293. package/dist/src/reactivity/policy.d.ts.map +1 -0
  294. package/dist/src/reactivity/policy.js +64 -0
  295. package/dist/src/reactivity/policy.js.map +1 -0
  296. package/dist/src/reactivity/push-state.d.ts +140 -0
  297. package/dist/src/reactivity/push-state.d.ts.map +1 -0
  298. package/dist/src/reactivity/push-state.js +270 -0
  299. package/dist/src/reactivity/push-state.js.map +1 -0
  300. package/dist/src/reactivity/recover.d.ts +75 -0
  301. package/dist/src/reactivity/recover.d.ts.map +1 -0
  302. package/dist/src/reactivity/recover.js +105 -0
  303. package/dist/src/reactivity/recover.js.map +1 -0
  304. package/dist/src/reactivity/replay-buffer.d.ts +57 -0
  305. package/dist/src/reactivity/replay-buffer.d.ts.map +1 -0
  306. package/dist/src/reactivity/replay-buffer.js +88 -0
  307. package/dist/src/reactivity/replay-buffer.js.map +1 -0
  308. package/dist/src/reactivity/resume.d.ts +214 -0
  309. package/dist/src/reactivity/resume.d.ts.map +1 -0
  310. package/dist/src/reactivity/resume.js +358 -0
  311. package/dist/src/reactivity/resume.js.map +1 -0
  312. package/dist/src/reactivity/rotation.d.ts +283 -0
  313. package/dist/src/reactivity/rotation.d.ts.map +1 -0
  314. package/dist/src/reactivity/rotation.js +265 -0
  315. package/dist/src/reactivity/rotation.js.map +1 -0
  316. package/dist/src/reactivity/subscriber.d.ts +66 -0
  317. package/dist/src/reactivity/subscriber.d.ts.map +1 -0
  318. package/dist/src/reactivity/subscriber.js +75 -0
  319. package/dist/src/reactivity/subscriber.js.map +1 -0
  320. package/dist/src/reactivity/subscription.d.ts +52 -0
  321. package/dist/src/reactivity/subscription.d.ts.map +1 -0
  322. package/dist/src/reactivity/subscription.js +28 -0
  323. package/dist/src/reactivity/subscription.js.map +1 -0
  324. package/dist/src/reactivity/topic-anchor.d.ts +46 -0
  325. package/dist/src/reactivity/topic-anchor.d.ts.map +1 -0
  326. package/dist/src/reactivity/topic-anchor.js +61 -0
  327. package/dist/src/reactivity/topic-anchor.js.map +1 -0
  328. package/dist/src/reactivity/verify.d.ts +41 -0
  329. package/dist/src/reactivity/verify.d.ts.map +1 -0
  330. package/dist/src/reactivity/verify.js +48 -0
  331. package/dist/src/reactivity/verify.js.map +1 -0
  332. package/dist/src/reactivity/wire-validate.d.ts +28 -0
  333. package/dist/src/reactivity/wire-validate.d.ts.map +1 -0
  334. package/dist/src/reactivity/wire-validate.js +79 -0
  335. package/dist/src/reactivity/wire-validate.js.map +1 -0
  336. package/dist/src/reactivity/wire.d.ts +102 -0
  337. package/dist/src/reactivity/wire.d.ts.map +1 -0
  338. package/dist/src/reactivity/wire.js +129 -0
  339. package/dist/src/reactivity/wire.js.map +1 -0
  340. package/dist/src/transaction/coordinator.d.ts +9 -5
  341. package/dist/src/transaction/coordinator.d.ts.map +1 -1
  342. package/dist/src/transaction/coordinator.js +48 -25
  343. package/dist/src/transaction/coordinator.js.map +1 -1
  344. package/dist/src/transactor/change-notifier.d.ts +74 -0
  345. package/dist/src/transactor/change-notifier.d.ts.map +1 -0
  346. package/dist/src/transactor/change-notifier.js +4 -0
  347. package/dist/src/transactor/change-notifier.js.map +1 -0
  348. package/dist/src/transactor/index.d.ts +1 -0
  349. package/dist/src/transactor/index.d.ts.map +1 -1
  350. package/dist/src/transactor/index.js +1 -0
  351. package/dist/src/transactor/index.js.map +1 -1
  352. package/dist/src/transactor/network-transactor.d.ts +34 -2
  353. package/dist/src/transactor/network-transactor.d.ts.map +1 -1
  354. package/dist/src/transactor/network-transactor.js +113 -13
  355. package/dist/src/transactor/network-transactor.js.map +1 -1
  356. package/package.json +2 -2
  357. package/src/cluster/structs.ts +13 -0
  358. package/src/cohort-topic/addressing.ts +112 -0
  359. package/src/cohort-topic/antidos/bootstrap-evidence-envelope.ts +253 -0
  360. package/src/cohort-topic/antidos/bootstrap-evidence.ts +94 -0
  361. package/src/cohort-topic/antidos/index.ts +5 -0
  362. package/src/cohort-topic/antidos/rate-limiter.ts +210 -0
  363. package/src/cohort-topic/antidos/replay-guard.ts +109 -0
  364. package/src/cohort-topic/antidos/topic-budget.ts +145 -0
  365. package/src/cohort-topic/antiflood/index.ts +2 -0
  366. package/src/cohort-topic/antiflood/invariants.ts +108 -0
  367. package/src/cohort-topic/antiflood/jitter.ts +117 -0
  368. package/src/cohort-topic/coldstart.ts +204 -0
  369. package/src/cohort-topic/dmax.ts +88 -0
  370. package/src/cohort-topic/gossip/bus.ts +217 -0
  371. package/src/cohort-topic/gossip/index.ts +3 -0
  372. package/src/cohort-topic/gossip/records.ts +45 -0
  373. package/src/cohort-topic/gossip/view.ts +66 -0
  374. package/src/cohort-topic/index.ts +20 -0
  375. package/src/cohort-topic/load/barometer.ts +134 -0
  376. package/src/cohort-topic/load/index.ts +1 -0
  377. package/src/cohort-topic/member-engine.ts +409 -0
  378. package/src/cohort-topic/membership/index.ts +3 -0
  379. package/src/cohort-topic/membership/publisher.ts +171 -0
  380. package/src/cohort-topic/membership/source.ts +41 -0
  381. package/src/cohort-topic/membership/verifier.ts +345 -0
  382. package/src/cohort-topic/ports.ts +157 -0
  383. package/src/cohort-topic/promotion.ts +384 -0
  384. package/src/cohort-topic/registration/bytes.ts +37 -0
  385. package/src/cohort-topic/registration/handoff.ts +154 -0
  386. package/src/cohort-topic/registration/index.ts +6 -0
  387. package/src/cohort-topic/registration/renewal.ts +423 -0
  388. package/src/cohort-topic/registration/sharding.ts +61 -0
  389. package/src/cohort-topic/registration/store.ts +81 -0
  390. package/src/cohort-topic/registration/types.ts +76 -0
  391. package/src/cohort-topic/ring-hash.ts +50 -0
  392. package/src/cohort-topic/service.ts +390 -0
  393. package/src/cohort-topic/sig/index.ts +2 -0
  394. package/src/cohort-topic/sig/payloads.ts +39 -0
  395. package/src/cohort-topic/sig/threshold.ts +64 -0
  396. package/src/cohort-topic/tiers.ts +74 -0
  397. package/src/cohort-topic/traffic.ts +211 -0
  398. package/src/cohort-topic/walk.ts +255 -0
  399. package/src/cohort-topic/willingness.ts +237 -0
  400. package/src/cohort-topic/wire/codec.ts +204 -0
  401. package/src/cohort-topic/wire/index.ts +16 -0
  402. package/src/cohort-topic/wire/payloads.ts +117 -0
  403. package/src/cohort-topic/wire/types.ts +343 -0
  404. package/src/cohort-topic/wire/validate.ts +478 -0
  405. package/src/collection/collection.ts +93 -1
  406. package/src/collections/tree/tree.ts +84 -3
  407. package/src/index.ts +3 -0
  408. package/src/log/log.ts +86 -7
  409. package/src/log/struct.ts +126 -2
  410. package/src/matchmaking/capability-filter.ts +45 -0
  411. package/src/matchmaking/config.ts +98 -0
  412. package/src/matchmaking/index.ts +21 -0
  413. package/src/matchmaking/multi-cohort-seeker.ts +234 -0
  414. package/src/matchmaking/provider.ts +123 -0
  415. package/src/matchmaking/query-eval.ts +105 -0
  416. package/src/matchmaking/seeker-walk.ts +127 -0
  417. package/src/matchmaking/seeker.ts +86 -0
  418. package/src/matchmaking/topic-anchor.ts +90 -0
  419. package/src/matchmaking/voting-quorum.ts +394 -0
  420. package/src/matchmaking/wire.ts +680 -0
  421. package/src/network/i-repo.ts +9 -0
  422. package/src/network/repo-protocol.ts +3 -2
  423. package/src/network/struct.ts +24 -1
  424. package/src/reactivity/backfill.ts +220 -0
  425. package/src/reactivity/backpressure.ts +191 -0
  426. package/src/reactivity/checkpoint.ts +308 -0
  427. package/src/reactivity/config.ts +172 -0
  428. package/src/reactivity/dedupe.ts +132 -0
  429. package/src/reactivity/forwarder.ts +87 -0
  430. package/src/reactivity/index.ts +34 -0
  431. package/src/reactivity/notification.ts +123 -0
  432. package/src/reactivity/policy.ts +79 -0
  433. package/src/reactivity/push-state.ts +343 -0
  434. package/src/reactivity/recover.ts +153 -0
  435. package/src/reactivity/replay-buffer.ts +141 -0
  436. package/src/reactivity/resume.ts +549 -0
  437. package/src/reactivity/rotation.ts +415 -0
  438. package/src/reactivity/subscriber.ts +132 -0
  439. package/src/reactivity/subscription.ts +66 -0
  440. package/src/reactivity/topic-anchor.ts +71 -0
  441. package/src/reactivity/verify.ts +73 -0
  442. package/src/reactivity/wire-validate.ts +87 -0
  443. package/src/reactivity/wire.ts +224 -0
  444. package/src/transaction/coordinator.ts +53 -27
  445. package/src/transactor/change-notifier.ts +80 -0
  446. package/src/transactor/index.ts +1 -0
  447. package/src/transactor/network-transactor.ts +133 -15
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/cohort-topic/antidos/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC"}
@@ -0,0 +1,6 @@
1
+ export * from "./rate-limiter.js";
2
+ export * from "./topic-budget.js";
3
+ export * from "./replay-guard.js";
4
+ export * from "./bootstrap-evidence.js";
5
+ export * from "./bootstrap-evidence-envelope.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/cohort-topic/antidos/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Cohort-topic substrate — per-peer registration rate limiter (anti-DoS).
3
+ *
4
+ * Transcribed from `docs/cohort-topic.md` §Anti-DoS bullet 1. A cohort member tracks inbound
5
+ * `RegisterV1` rate per `(sourcePeerId, topicId)` and refuses a source that exceeds
6
+ * `register_rate_per_peer` (default 4 / min). An over-rate registration draws `UnwillingCohort` with
7
+ * an **exponential** `retryAfter`, so a peer hammering one topic at one cohort backs off geometrically
8
+ * — the same capped-doubling curve the willingness back-off uses ({@link backoffRetryMs}).
9
+ *
10
+ * The limiter is a sliding-window counter: it keeps the accept timestamps inside the trailing
11
+ * `windowMs` for each key, admits while the window holds `< ratePerWindow`, and on rejection advances
12
+ * a per-key strike counter that indexes the back-off curve. A run of strikes decays once the source
13
+ * has been quiet for a full window (its window empties and the strike counter resets), so a
14
+ * well-behaved peer is never permanently penalized. Each key's accept history is trimmed to the live
15
+ * window whenever that key is checked, so a key's footprint stays `O(ratePerWindow)`.
16
+ *
17
+ * The `states` map is bounded two complementary ways so a long-running host cannot leak memory and a
18
+ * flood of attacker-chosen keys cannot exhaust it:
19
+ *
20
+ * - **Hard LRU cap (`maxKeys`)** — enforced inline in {@link SlidingWindowRateLimiter.check}. Every
21
+ * check moves its key to the most-recently-checked end (the {@link import("../../utility/lru-map.js").LruMap}
22
+ * delete-then-set trick over `Map` insertion order); when a *new* key would exceed `maxKeys` the
23
+ * least-recently-checked keys are evicted until within cap. Recency is refreshed on **every** check,
24
+ * rejects included, so a source mid-attack (accumulated `strikes` driving back-off) stays at the hot
25
+ * end and is never the eviction victim — only genuinely idle keys age to the cold end.
26
+ * - **Idle-TTL sweep (`idleTtlMs`)** — {@link SlidingWindowRateLimiter.sweep} drops keys not checked
27
+ * within `idleTtlMs`. Driver-called on the host's gossip cadence, it reclaims steady-state footprint
28
+ * proportional to *active* keys.
29
+ *
30
+ * Eviction is penalty-free when a key is dropped only after a full window of quiet: the window logic
31
+ * already forgives a source quiet for a full window (its accepts age out and `strikes` resets), so
32
+ * dropping such a key and re-allocating a fresh `{ accepts:[now], strikes:0 }` on its return is
33
+ * observationally identical to keeping it — just an earlier reclaim. This holds for the LRU cap (a
34
+ * mid-attack key is refreshed to the hot end on every check, so only genuinely idle keys are evicted)
35
+ * and for the default `idleTtlMs` (`== windowMs`). A configured `idleTtlMs < windowMs` reclaims more
36
+ * aggressively: `sweep` may then drop a key whose accepts have *not* fully aged out, forgiving its
37
+ * accumulated `strikes` sooner than the window alone would — an accepted footprint/strike-accounting
38
+ * tradeoff, not an identity. The substrate does not defend against unbounded Sybil key creation (that is FRET's /
39
+ * the reputation subsystem's concern; see §Anti-DoS closing note).
40
+ */
41
+ import { type BackoffConfig } from "../willingness.js";
42
+ /** Default per-peer-per-topic acceptance ceiling per window — `register_rate_per_peer` (4 / min). */
43
+ export declare const DEFAULT_REGISTER_RATE_PER_PEER = 4;
44
+ /** Default sliding window the ceiling applies over (ms). */
45
+ export declare const DEFAULT_RATE_WINDOW_MS = 60000;
46
+ /** Default hard cap on tracked `(peer, topic)` keys; the least-recently-checked are evicted beyond this. */
47
+ export declare const DEFAULT_RATE_LIMITER_MAX_KEYS = 100000;
48
+ /** Default idle threshold for {@link RegisterRateLimiter.sweep} — one quiet window makes a key evictable. */
49
+ export declare const DEFAULT_RATE_LIMITER_IDLE_TTL_MS = 60000;
50
+ export interface RegisterRateLimiterConfig {
51
+ /** Accepts permitted per window per `(peer, topic)`. Default {@link DEFAULT_REGISTER_RATE_PER_PEER}. */
52
+ ratePerWindow?: number;
53
+ /** Sliding-window length (ms). Default {@link DEFAULT_RATE_WINDOW_MS}. */
54
+ windowMs?: number;
55
+ /** Exponential `retryAfter` curve for over-rate sources. Default {@link DEFAULT_BACKOFF_CONFIG}. */
56
+ backoff?: BackoffConfig;
57
+ /** Hard cap on tracked `(peer, topic)` keys; least-recently-checked evicted beyond this. Default {@link DEFAULT_RATE_LIMITER_MAX_KEYS}. */
58
+ maxKeys?: number;
59
+ /**
60
+ * A key not checked within this many ms is evictable by {@link RegisterRateLimiter.sweep}. Default
61
+ * {@link DEFAULT_RATE_LIMITER_IDLE_TTL_MS} (`== windowMs`). Keep `>= windowMs` to preserve the
62
+ * penalty-free invariant; a smaller value reclaims sooner but may forgive an idle source's strikes
63
+ * before its window would (see the class doc comment).
64
+ */
65
+ idleTtlMs?: number;
66
+ }
67
+ /** Outcome of a rate check: admitted, or refused with the back-off the caller puts in `UnwillingCohort`. */
68
+ export type RateCheckResult = {
69
+ ok: true;
70
+ } | {
71
+ ok: false;
72
+ retryAfterMs: number;
73
+ };
74
+ /** Per-peer-per-topic inbound `RegisterV1` rate limiter. */
75
+ export interface RegisterRateLimiter {
76
+ /**
77
+ * Classify a register from `peerId` for `topicId` at `now`. Records the accept on `{ ok: true }`;
78
+ * an over-rate source gets `{ ok: false, retryAfterMs }` with exponential back-off and the strike
79
+ * is *not* recorded as an accept (so back-off cannot itself fill the window). Every call — accept
80
+ * **or** reject — refreshes the key's recency, so an actively-hammering source is never evicted by
81
+ * the LRU cap (which would reset its back-off escalation).
82
+ */
83
+ check(peerId: Uint8Array, topicId: Uint8Array, now: number): RateCheckResult;
84
+ /**
85
+ * Evict keys idle (not checked) for `>= idleTtlMs`. Returns the number evicted. Driver-called on
86
+ * the host's gossip cadence to reclaim steady-state footprint; the hard `maxKeys` LRU cap bounds
87
+ * worst-case footprint even without it.
88
+ */
89
+ sweep(now: number): number;
90
+ /** Tracked `(peer, topic)` key count (test/diagnostic introspection). */
91
+ readonly size: number;
92
+ }
93
+ /** Build a {@link RegisterRateLimiter} over the configured ceiling, window, and back-off curve. */
94
+ export declare function createRegisterRateLimiter(config?: RegisterRateLimiterConfig): RegisterRateLimiter;
95
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../../../src/cohort-topic/antidos/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAGH,OAAO,EAAkB,KAAK,aAAa,EAA0B,MAAM,mBAAmB,CAAC;AAK/F,qGAAqG;AACrG,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,4DAA4D;AAC5D,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAC7C,4GAA4G;AAC5G,eAAO,MAAM,6BAA6B,SAAU,CAAC;AACrD,6GAA6G;AAC7G,eAAO,MAAM,gCAAgC,QAAyB,CAAC;AAEvE,MAAM,WAAW,yBAAyB;IACzC,wGAAwG;IACxG,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oGAAoG;IACpG,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,2IAA2I;IAC3I,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,4GAA4G;AAC5G,MAAM,MAAM,eAAe,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjF,4DAA4D;AAC5D,MAAM,WAAW,mBAAmB;IACnC;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC;IAC7E;;;;OAIG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AA+GD,mGAAmG;AACnG,wBAAgB,yBAAyB,CAAC,MAAM,GAAE,yBAA8B,GAAG,mBAAmB,CAErG"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Cohort-topic substrate — per-peer registration rate limiter (anti-DoS).
3
+ *
4
+ * Transcribed from `docs/cohort-topic.md` §Anti-DoS bullet 1. A cohort member tracks inbound
5
+ * `RegisterV1` rate per `(sourcePeerId, topicId)` and refuses a source that exceeds
6
+ * `register_rate_per_peer` (default 4 / min). An over-rate registration draws `UnwillingCohort` with
7
+ * an **exponential** `retryAfter`, so a peer hammering one topic at one cohort backs off geometrically
8
+ * — the same capped-doubling curve the willingness back-off uses ({@link backoffRetryMs}).
9
+ *
10
+ * The limiter is a sliding-window counter: it keeps the accept timestamps inside the trailing
11
+ * `windowMs` for each key, admits while the window holds `< ratePerWindow`, and on rejection advances
12
+ * a per-key strike counter that indexes the back-off curve. A run of strikes decays once the source
13
+ * has been quiet for a full window (its window empties and the strike counter resets), so a
14
+ * well-behaved peer is never permanently penalized. Each key's accept history is trimmed to the live
15
+ * window whenever that key is checked, so a key's footprint stays `O(ratePerWindow)`.
16
+ *
17
+ * The `states` map is bounded two complementary ways so a long-running host cannot leak memory and a
18
+ * flood of attacker-chosen keys cannot exhaust it:
19
+ *
20
+ * - **Hard LRU cap (`maxKeys`)** — enforced inline in {@link SlidingWindowRateLimiter.check}. Every
21
+ * check moves its key to the most-recently-checked end (the {@link import("../../utility/lru-map.js").LruMap}
22
+ * delete-then-set trick over `Map` insertion order); when a *new* key would exceed `maxKeys` the
23
+ * least-recently-checked keys are evicted until within cap. Recency is refreshed on **every** check,
24
+ * rejects included, so a source mid-attack (accumulated `strikes` driving back-off) stays at the hot
25
+ * end and is never the eviction victim — only genuinely idle keys age to the cold end.
26
+ * - **Idle-TTL sweep (`idleTtlMs`)** — {@link SlidingWindowRateLimiter.sweep} drops keys not checked
27
+ * within `idleTtlMs`. Driver-called on the host's gossip cadence, it reclaims steady-state footprint
28
+ * proportional to *active* keys.
29
+ *
30
+ * Eviction is penalty-free when a key is dropped only after a full window of quiet: the window logic
31
+ * already forgives a source quiet for a full window (its accepts age out and `strikes` resets), so
32
+ * dropping such a key and re-allocating a fresh `{ accepts:[now], strikes:0 }` on its return is
33
+ * observationally identical to keeping it — just an earlier reclaim. This holds for the LRU cap (a
34
+ * mid-attack key is refreshed to the hot end on every check, so only genuinely idle keys are evicted)
35
+ * and for the default `idleTtlMs` (`== windowMs`). A configured `idleTtlMs < windowMs` reclaims more
36
+ * aggressively: `sweep` may then drop a key whose accepts have *not* fully aged out, forgiving its
37
+ * accumulated `strikes` sooner than the window alone would — an accepted footprint/strike-accounting
38
+ * tradeoff, not an identity. The substrate does not defend against unbounded Sybil key creation (that is FRET's /
39
+ * the reputation subsystem's concern; see §Anti-DoS closing note).
40
+ */
41
+ import { recordKey } from "../registration/bytes.js";
42
+ import { backoffRetryMs, DEFAULT_BACKOFF_CONFIG } from "../willingness.js";
43
+ import { createLogger } from "../../logger.js";
44
+ const log = createLogger("cohort-topic:antidos");
45
+ /** Default per-peer-per-topic acceptance ceiling per window — `register_rate_per_peer` (4 / min). */
46
+ export const DEFAULT_REGISTER_RATE_PER_PEER = 4;
47
+ /** Default sliding window the ceiling applies over (ms). */
48
+ export const DEFAULT_RATE_WINDOW_MS = 60_000;
49
+ /** Default hard cap on tracked `(peer, topic)` keys; the least-recently-checked are evicted beyond this. */
50
+ export const DEFAULT_RATE_LIMITER_MAX_KEYS = 100_000;
51
+ /** Default idle threshold for {@link RegisterRateLimiter.sweep} — one quiet window makes a key evictable. */
52
+ export const DEFAULT_RATE_LIMITER_IDLE_TTL_MS = DEFAULT_RATE_WINDOW_MS;
53
+ class SlidingWindowRateLimiter {
54
+ states = new Map();
55
+ ratePerWindow;
56
+ windowMs;
57
+ backoff;
58
+ maxKeys;
59
+ idleTtlMs;
60
+ constructor(config = {}) {
61
+ this.ratePerWindow = config.ratePerWindow ?? DEFAULT_REGISTER_RATE_PER_PEER;
62
+ if (!Number.isInteger(this.ratePerWindow) || this.ratePerWindow <= 0) {
63
+ throw new RangeError(`ratePerWindow must be a positive integer, got ${this.ratePerWindow}`);
64
+ }
65
+ this.windowMs = config.windowMs ?? DEFAULT_RATE_WINDOW_MS;
66
+ if (!(this.windowMs > 0)) {
67
+ throw new RangeError(`windowMs must be > 0, got ${this.windowMs}`);
68
+ }
69
+ this.backoff = config.backoff ?? DEFAULT_BACKOFF_CONFIG;
70
+ this.maxKeys = config.maxKeys ?? DEFAULT_RATE_LIMITER_MAX_KEYS;
71
+ if (!Number.isInteger(this.maxKeys) || this.maxKeys <= 0) {
72
+ throw new RangeError(`maxKeys must be a positive integer, got ${this.maxKeys}`);
73
+ }
74
+ this.idleTtlMs = config.idleTtlMs ?? DEFAULT_RATE_LIMITER_IDLE_TTL_MS;
75
+ if (!(this.idleTtlMs > 0)) {
76
+ throw new RangeError(`idleTtlMs must be > 0, got ${this.idleTtlMs}`);
77
+ }
78
+ }
79
+ check(peerId, topicId, now) {
80
+ const key = recordKey(topicId, peerId);
81
+ const state = this.states.get(key);
82
+ const cutoff = now - this.windowMs;
83
+ if (state === undefined) {
84
+ // New key: enforce the hard cap by evicting the least-recently-checked keys (oldest by
85
+ // `Map` insertion order) until this insertion stays within `maxKeys`. The evicted keys
86
+ // are by construction the coldest — an actively-checked key is refreshed to the hot end
87
+ // below and so is never the victim.
88
+ while (this.states.size >= this.maxKeys) {
89
+ const oldest = this.states.keys().next().value;
90
+ if (oldest === undefined)
91
+ break;
92
+ this.states.delete(oldest);
93
+ }
94
+ this.states.set(key, { accepts: [now], strikes: 0, lastSeen: now });
95
+ return { ok: true };
96
+ }
97
+ // Refresh LRU recency on every check (accept OR reject): delete-then-set moves this key to the
98
+ // most-recently-checked end of the `Map`, and `lastSeen = now` is the idle-TTL key sweep() reads.
99
+ // A source mid-attack stays at the hot end here even on a reject, so the cap never evicts (and
100
+ // thus never resets the back-off of) an active attacker.
101
+ this.states.delete(key);
102
+ this.states.set(key, state);
103
+ state.lastSeen = now;
104
+ // Drop accepts that have aged out of the trailing window.
105
+ let live = 0;
106
+ for (const t of state.accepts) {
107
+ if (t > cutoff) {
108
+ state.accepts[live++] = t;
109
+ }
110
+ }
111
+ state.accepts.length = live;
112
+ if (live === 0) {
113
+ // The source has been quiet for a full window — forgive accumulated strikes.
114
+ state.strikes = 0;
115
+ }
116
+ if (live < this.ratePerWindow) {
117
+ state.accepts.push(now);
118
+ state.strikes = 0;
119
+ return { ok: true };
120
+ }
121
+ // Over rate: exponential back-off indexed by the strike count, then advance the strike.
122
+ const retryAfterMs = backoffRetryMs(state.strikes, this.backoff);
123
+ state.strikes++;
124
+ log("rate-limit reject window=%d/%d strikes=%d retryAfter=%d", live, this.ratePerWindow, state.strikes, retryAfterMs);
125
+ return { ok: false, retryAfterMs };
126
+ }
127
+ sweep(now) {
128
+ let evicted = 0;
129
+ // Deleting the current key during `Map` iteration is well-defined and does not skip entries.
130
+ for (const [key, state] of this.states) {
131
+ if (now - state.lastSeen >= this.idleTtlMs) {
132
+ this.states.delete(key);
133
+ evicted++;
134
+ }
135
+ }
136
+ return evicted;
137
+ }
138
+ get size() {
139
+ return this.states.size;
140
+ }
141
+ }
142
+ /** Build a {@link RegisterRateLimiter} over the configured ceiling, window, and back-off curve. */
143
+ export function createRegisterRateLimiter(config = {}) {
144
+ return new SlidingWindowRateLimiter(config);
145
+ }
146
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../../../src/cohort-topic/antidos/rate-limiter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Cohort-topic substrate — correlation-id replay guard (anti-DoS).
3
+ *
4
+ * Transcribed from `docs/cohort-topic.md` §Anti-DoS bullet 3. Every `RegisterV1` carries a
5
+ * `correlationId` (16 random bytes) and a signature over `(topicId, tier, correlationId, timestamp)`.
6
+ * The signature (verified elsewhere — the participant-key check) proves authorship; this guard adds
7
+ * **freshness**: it drops a registration whose `timestamp` is outside the accepted skew window
8
+ * (stale, or implausibly far in the future) and drops a `correlationId` it has already seen inside the
9
+ * window (a captured-and-replayed registration).
10
+ *
11
+ * Because a stale registration is rejected outright, the guard only needs to remember correlation ids
12
+ * for one `maxAgeMs` window: an id older than that would be rejected on timestamp alone, so its record
13
+ * can be pruned. Pruning runs on access, bounding memory to the live window's worth of registrations.
14
+ */
15
+ /** Default acceptance window: a registration timestamped older than this (relative to `now`) is stale. */
16
+ export declare const DEFAULT_REPLAY_MAX_AGE_MS = 60000;
17
+ /** Default tolerated forward clock skew: a timestamp this far past `now` is rejected as implausible. */
18
+ export declare const DEFAULT_REPLAY_MAX_FUTURE_SKEW_MS = 5000;
19
+ export interface CorrelationReplayGuardConfig {
20
+ /** A timestamp older than `now − maxAgeMs` is stale. Default {@link DEFAULT_REPLAY_MAX_AGE_MS}. */
21
+ maxAgeMs?: number;
22
+ /** A timestamp newer than `now + maxFutureSkewMs` is rejected. Default {@link DEFAULT_REPLAY_MAX_FUTURE_SKEW_MS}. */
23
+ maxFutureSkewMs?: number;
24
+ }
25
+ /** Freshness + anti-replay gate over registration `correlationId`s and timestamps. */
26
+ export interface CorrelationReplayGuard {
27
+ /**
28
+ * Accept `correlationId` (from `peerId`, stamped `timestamp`) evaluated at `now`. Returns `false`
29
+ * — and records nothing — when the timestamp is stale or implausibly future, or when this
30
+ * `correlationId` was already accepted inside the window (replay). Returns `true` and remembers the
31
+ * id on first sight of a fresh registration.
32
+ */
33
+ accept(correlationId: Uint8Array, peerId: Uint8Array, timestamp: number, now: number): boolean;
34
+ }
35
+ /** Build a {@link CorrelationReplayGuard} over the configured staleness window and forward skew. */
36
+ export declare function createCorrelationReplayGuard(config?: CorrelationReplayGuardConfig): CorrelationReplayGuard;
37
+ //# sourceMappingURL=replay-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay-guard.d.ts","sourceRoot":"","sources":["../../../../src/cohort-topic/antidos/replay-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAOH,0GAA0G;AAC1G,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAChD,wGAAwG;AACxG,eAAO,MAAM,iCAAiC,OAAQ,CAAC;AAEvD,MAAM,WAAW,4BAA4B;IAC5C,mGAAmG;IACnG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qHAAqH;IACrH,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,sFAAsF;AACtF,MAAM,WAAW,sBAAsB;IACtC;;;;;OAKG;IACH,MAAM,CAAC,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC/F;AAgED,oGAAoG;AACpG,wBAAgB,4BAA4B,CAAC,MAAM,GAAE,4BAAiC,GAAG,sBAAsB,CAE9G"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Cohort-topic substrate — correlation-id replay guard (anti-DoS).
3
+ *
4
+ * Transcribed from `docs/cohort-topic.md` §Anti-DoS bullet 3. Every `RegisterV1` carries a
5
+ * `correlationId` (16 random bytes) and a signature over `(topicId, tier, correlationId, timestamp)`.
6
+ * The signature (verified elsewhere — the participant-key check) proves authorship; this guard adds
7
+ * **freshness**: it drops a registration whose `timestamp` is outside the accepted skew window
8
+ * (stale, or implausibly far in the future) and drops a `correlationId` it has already seen inside the
9
+ * window (a captured-and-replayed registration).
10
+ *
11
+ * Because a stale registration is rejected outright, the guard only needs to remember correlation ids
12
+ * for one `maxAgeMs` window: an id older than that would be rejected on timestamp alone, so its record
13
+ * can be pruned. Pruning runs on access, bounding memory to the live window's worth of registrations.
14
+ */
15
+ import { bytesKey } from "../registration/bytes.js";
16
+ import { createLogger } from "../../logger.js";
17
+ const log = createLogger("cohort-topic:antidos");
18
+ /** Default acceptance window: a registration timestamped older than this (relative to `now`) is stale. */
19
+ export const DEFAULT_REPLAY_MAX_AGE_MS = 60_000;
20
+ /** Default tolerated forward clock skew: a timestamp this far past `now` is rejected as implausible. */
21
+ export const DEFAULT_REPLAY_MAX_FUTURE_SKEW_MS = 5_000;
22
+ class WindowedReplayGuard {
23
+ seen = new Map();
24
+ maxAgeMs;
25
+ maxFutureSkewMs;
26
+ /** `now` of the last prune, so pruning amortizes rather than scanning on every call. */
27
+ lastPruneAt = -Infinity;
28
+ constructor(config = {}) {
29
+ this.maxAgeMs = config.maxAgeMs ?? DEFAULT_REPLAY_MAX_AGE_MS;
30
+ if (!(this.maxAgeMs > 0)) {
31
+ throw new RangeError(`maxAgeMs must be > 0, got ${this.maxAgeMs}`);
32
+ }
33
+ this.maxFutureSkewMs = config.maxFutureSkewMs ?? DEFAULT_REPLAY_MAX_FUTURE_SKEW_MS;
34
+ if (!(this.maxFutureSkewMs >= 0)) {
35
+ throw new RangeError(`maxFutureSkewMs must be >= 0, got ${this.maxFutureSkewMs}`);
36
+ }
37
+ }
38
+ accept(correlationId, peerId, timestamp, now) {
39
+ if (timestamp < now - this.maxAgeMs) {
40
+ log("replay-guard reject: stale timestamp age=%d > maxAge=%d", now - timestamp, this.maxAgeMs);
41
+ return false; // stale
42
+ }
43
+ if (timestamp > now + this.maxFutureSkewMs) {
44
+ log("replay-guard reject: future timestamp skew=%d > maxSkew=%d", timestamp - now, this.maxFutureSkewMs);
45
+ return false; // implausibly future
46
+ }
47
+ this.maybePrune(now);
48
+ const key = bytesKey(correlationId);
49
+ const prior = this.seen.get(key);
50
+ if (prior !== undefined) {
51
+ // Attribute the replay to the *original* accepter: the correlationId (not the peer id) is the
52
+ // anti-replay key, so a replayer that spoofs a different source id is still caught here.
53
+ log("replay-guard reject: replayed correlationId (first seen from peer=%s)", prior.peer);
54
+ return false; // replay
55
+ }
56
+ this.seen.set(key, { timestamp, peer: bytesKey(peerId) });
57
+ return true;
58
+ }
59
+ /** Forget ids whose timestamp has aged past the window — they would be rejected as stale anyway. */
60
+ maybePrune(now) {
61
+ // Amortize: prune at most once per window rather than scanning on every accept.
62
+ if (now - this.lastPruneAt < this.maxAgeMs) {
63
+ return;
64
+ }
65
+ this.lastPruneAt = now;
66
+ const cutoff = now - this.maxAgeMs;
67
+ for (const [key, entry] of this.seen) {
68
+ if (entry.timestamp < cutoff) {
69
+ this.seen.delete(key);
70
+ }
71
+ }
72
+ }
73
+ }
74
+ /** Build a {@link CorrelationReplayGuard} over the configured staleness window and forward skew. */
75
+ export function createCorrelationReplayGuard(config = {}) {
76
+ return new WindowedReplayGuard(config);
77
+ }
78
+ //# sourceMappingURL=replay-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay-guard.js","sourceRoot":"","sources":["../../../../src/cohort-topic/antidos/replay-guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Cohort-topic substrate — per-cohort topic budget (anti-DoS).
3
+ *
4
+ * Transcribed from `docs/cohort-topic.md` §Anti-DoS bullet 2. A cohort holds forwarder state for at
5
+ * most `topics_max` (default 2048) topics. Once the budget is full a request to instantiate a **new**
6
+ * topic is refused (the caller answers `UnwillingCohort`); topics already in the budget continue to
7
+ * serve. To keep room for genuinely active topics, the budget evicts cold ones: eviction order is
8
+ * **LRU by participant count**, with **zero-recent-registration topics dropped first** — a topic that
9
+ * carries no direct participants is the cheapest to shed and is shed before any populated topic.
10
+ *
11
+ * `admit` only ever evicts a *zero-participant* topic. If every resident topic still carries
12
+ * participants the budget is genuinely saturated and the new instantiation is refused — an attacker
13
+ * cannot, by flooding new topic ids, evict a topic that real participants are using. LRU ordering
14
+ * uses a monotonic access counter bumped on every `admit`/`touch`, so it is deterministic and needs
15
+ * no wall clock.
16
+ */
17
+ /** Default per-cohort forwarder-topic ceiling — `topics_max`. */
18
+ export declare const DEFAULT_TOPICS_MAX = 2048;
19
+ export interface TopicBudgetConfig {
20
+ /** Max topics with forwarder state. Default {@link DEFAULT_TOPICS_MAX}. */
21
+ topicsMax?: number;
22
+ }
23
+ /** Per-cohort cap on the number of topics with forwarder state, with LRU eviction of cold topics. */
24
+ export interface TopicBudget {
25
+ /**
26
+ * Admit `topicId` to the forwarder-state budget. Returns `true` if it is already resident or there
27
+ * is (or can be freed) room; `false` when the budget is full of populated topics, in which case the
28
+ * caller answers `UnwillingCohort` for the new instantiation. Admitting a *new* topic when full
29
+ * evicts the coldest zero-participant resident first.
30
+ */
31
+ admit(topicId: Uint8Array): boolean;
32
+ /**
33
+ * Record the current direct-participant count for a resident topic (LRU bookkeeping). Updates the
34
+ * eviction key and bumps the topic's recency; a no-op for a topic that is not resident.
35
+ */
36
+ touch(topicId: Uint8Array, participantCount: number): void;
37
+ /** Whether `topicId` currently holds forwarder state. */
38
+ has(topicId: Uint8Array): boolean;
39
+ /**
40
+ * The direct-participant count last reported for a resident `topicId` (the eviction key), or
41
+ * `undefined` when the topic is not resident. A resident reporting `0` is cold/evictable. Read-only
42
+ * introspection for tests/diagnostics — eviction policy reads the same field internally.
43
+ */
44
+ participantCount(topicId: Uint8Array): number | undefined;
45
+ /** Number of resident topics. */
46
+ readonly size: number;
47
+ }
48
+ /** Build a {@link TopicBudget} over the configured `topics_max`. */
49
+ export declare function createTopicBudget(config?: TopicBudgetConfig): TopicBudget;
50
+ //# sourceMappingURL=topic-budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topic-budget.d.ts","sourceRoot":"","sources":["../../../../src/cohort-topic/antidos/topic-budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,MAAM,WAAW,iBAAiB;IACjC,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qGAAqG;AACrG,MAAM,WAAW,WAAW;IAC3B;;;;;OAKG;IACH,KAAK,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC;IACpC;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3D,yDAAyD;IACzD,GAAG,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC;IAClC;;;;OAIG;IACH,gBAAgB,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1D,iCAAiC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAuFD,oEAAoE;AACpE,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,iBAAsB,GAAG,WAAW,CAE7E"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Cohort-topic substrate — per-cohort topic budget (anti-DoS).
3
+ *
4
+ * Transcribed from `docs/cohort-topic.md` §Anti-DoS bullet 2. A cohort holds forwarder state for at
5
+ * most `topics_max` (default 2048) topics. Once the budget is full a request to instantiate a **new**
6
+ * topic is refused (the caller answers `UnwillingCohort`); topics already in the budget continue to
7
+ * serve. To keep room for genuinely active topics, the budget evicts cold ones: eviction order is
8
+ * **LRU by participant count**, with **zero-recent-registration topics dropped first** — a topic that
9
+ * carries no direct participants is the cheapest to shed and is shed before any populated topic.
10
+ *
11
+ * `admit` only ever evicts a *zero-participant* topic. If every resident topic still carries
12
+ * participants the budget is genuinely saturated and the new instantiation is refused — an attacker
13
+ * cannot, by flooding new topic ids, evict a topic that real participants are using. LRU ordering
14
+ * uses a monotonic access counter bumped on every `admit`/`touch`, so it is deterministic and needs
15
+ * no wall clock.
16
+ */
17
+ import { bytesKey } from "../registration/bytes.js";
18
+ import { createLogger } from "../../logger.js";
19
+ const log = createLogger("cohort-topic:antidos");
20
+ /** Default per-cohort forwarder-topic ceiling — `topics_max`. */
21
+ export const DEFAULT_TOPICS_MAX = 2048;
22
+ class LruTopicBudget {
23
+ residents = new Map();
24
+ topicsMax;
25
+ seqCounter = 0;
26
+ constructor(config = {}) {
27
+ this.topicsMax = config.topicsMax ?? DEFAULT_TOPICS_MAX;
28
+ if (!Number.isInteger(this.topicsMax) || this.topicsMax <= 0) {
29
+ throw new RangeError(`topicsMax must be a positive integer, got ${this.topicsMax}`);
30
+ }
31
+ }
32
+ get size() {
33
+ return this.residents.size;
34
+ }
35
+ has(topicId) {
36
+ return this.residents.has(bytesKey(topicId));
37
+ }
38
+ participantCount(topicId) {
39
+ return this.residents.get(bytesKey(topicId))?.participantCount;
40
+ }
41
+ admit(topicId) {
42
+ const key = bytesKey(topicId);
43
+ const existing = this.residents.get(key);
44
+ if (existing !== undefined) {
45
+ existing.seq = ++this.seqCounter; // already resident — refresh recency, always allowed
46
+ return true;
47
+ }
48
+ if (this.residents.size < this.topicsMax) {
49
+ this.residents.set(key, { participantCount: 0, seq: ++this.seqCounter });
50
+ return true;
51
+ }
52
+ // Full: only a zero-participant resident may be evicted to make room for a new topic.
53
+ const victim = this.coldestEvictable();
54
+ if (victim === undefined) {
55
+ log("topic-budget full size=%d max=%d — refuse new topic", this.residents.size, this.topicsMax);
56
+ return false;
57
+ }
58
+ this.residents.delete(victim);
59
+ this.residents.set(key, { participantCount: 0, seq: ++this.seqCounter });
60
+ log("topic-budget evicted cold topic to admit new (size=%d max=%d)", this.residents.size, this.topicsMax);
61
+ return true;
62
+ }
63
+ touch(topicId, participantCount) {
64
+ const key = bytesKey(topicId);
65
+ const state = this.residents.get(key);
66
+ if (state === undefined) {
67
+ return;
68
+ }
69
+ state.participantCount = participantCount;
70
+ state.seq = ++this.seqCounter;
71
+ }
72
+ /**
73
+ * The coldest evictable resident key: among zero-participant topics, the least-recently used.
74
+ * `undefined` when every resident still carries participants (nothing may be evicted for a new topic).
75
+ */
76
+ coldestEvictable() {
77
+ let victim;
78
+ let victimSeq = Infinity;
79
+ for (const [key, state] of this.residents) {
80
+ if (state.participantCount > 0) {
81
+ continue; // populated topics continue — never evicted for a new instantiation
82
+ }
83
+ if (state.seq < victimSeq) {
84
+ victim = key;
85
+ victimSeq = state.seq;
86
+ }
87
+ }
88
+ return victim;
89
+ }
90
+ }
91
+ /** Build a {@link TopicBudget} over the configured `topics_max`. */
92
+ export function createTopicBudget(config = {}) {
93
+ return new LruTopicBudget(config);
94
+ }
95
+ //# sourceMappingURL=topic-budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topic-budget.js","sourceRoot":"","sources":["../../../../src/cohort-topic/antidos/topic-budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,GAAG,GAAG,YAAY,CAAC,sBAAsB,CAAC,CAAC;AAEjD,iEAAiE;AACjE,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAyCvC,MAAM,cAAc;IACF,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC7C,SAAS,CAAS;IAC3B,UAAU,GAAG,CAAC,CAAC;IAEvB,YAAY,SAA4B,EAAE;QACzC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,kBAAkB,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,EAAE,CAAC;YAC9D,MAAM,IAAI,UAAU,CAAC,6CAA6C,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;QACrF,CAAC;IACF,CAAC;IAED,IAAI,IAAI;QACP,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;IAC5B,CAAC;IAED,GAAG,CAAC,OAAmB;QACtB,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,gBAAgB,CAAC,OAAmB;QACnC,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,gBAAgB,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,OAAmB;QACxB,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACzC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC5B,QAAQ,CAAC,GAAG,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,qDAAqD;YACvF,OAAO,IAAI,CAAC;QACb,CAAC;QACD,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;YAC1C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,gBAAgB,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;YACzE,OAAO,IAAI,CAAC;QACb,CAAC;QACD,sFAAsF;QACtF,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACvC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAC1B,GAAG,CAAC,qDAAqD,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YAChG,OAAO,KAAK,CAAC;QACd,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,gBAAgB,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;QACzE,GAAG,CAAC,+DAA+D,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC1G,OAAO,IAAI,CAAC;IACb,CAAC;IAED,KAAK,CAAC,OAAmB,EAAE,gBAAwB;QAClD,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO;QACR,CAAC;QACD,KAAK,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;QAC1C,KAAK,CAAC,GAAG,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC;IAC/B,CAAC;IAED;;;OAGG;IACK,gBAAgB;QACvB,IAAI,MAA0B,CAAC;QAC/B,IAAI,SAAS,GAAG,QAAQ,CAAC;QACzB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC3C,IAAI,KAAK,CAAC,gBAAgB,GAAG,CAAC,EAAE,CAAC;gBAChC,SAAS,CAAC,oEAAoE;YAC/E,CAAC;YACD,IAAI,KAAK,CAAC,GAAG,GAAG,SAAS,EAAE,CAAC;gBAC3B,MAAM,GAAG,GAAG,CAAC;gBACb,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC;YACvB,CAAC;QACF,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC;CACD;AAED,oEAAoE;AACpE,MAAM,UAAU,iBAAiB,CAAC,SAA4B,EAAE;IAC/D,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from "./jitter.js";
2
+ export * from "./invariants.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/cohort-topic/antiflood/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from "./jitter.js";
2
+ export * from "./invariants.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/cohort-topic/antiflood/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC"}