@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,234 @@
1
+ /**
2
+ * Matchmaking — multi-cohort sweep (db-core, pure orchestration of the hot-topic representative sample).
3
+ *
4
+ * Per `docs/matchmaking.md` §Multi-cohort sweep. When a topic is hot enough that its providers live
5
+ * across many tier-`d >= 1` cohorts, a seeker that wants a *representative* cross-ring sample (rather
6
+ * than the prefix-biased single-cohort slice) does:
7
+ *
8
+ * 1. Registers at its natural tier as usual (the single-cohort {@link import("./seeker-walk.js").decide}
9
+ * walk — not this module).
10
+ * 2. Queries the **root** cohort, which returns an {@link AggregateCountV1}: log-bucketed provider counts
11
+ * per tier-1 prefix shard, **threshold-signed**. A cold root that fell through to `NoState` produces
12
+ * no aggregate (the producer gates on tree depth — see `db-p2p/matchmaking/aggregate-counts.ts`).
13
+ * 3. Selects the high-population tier-1 shards ({@link selectShards}) and queries them directly, unioning
14
+ * the returned providers into a deduped, re-validated set.
15
+ *
16
+ * This module is **pure**: the root-aggregate fetch, the per-shard query, and the optional threshold-sig
17
+ * verification are injected as a {@link MultiCohortSweepPorts} port (db-p2p binds them to the matchmaking
18
+ * query RPCs), exactly like the single-cohort walk splits pure `decide` (here) from the db-p2p walk
19
+ * client. The advisory trust model is preserved end-to-end: every shard entry is re-validated with
20
+ * {@link verifyProviderEntry} before it counts, so a lying shard primary buys nothing.
21
+ *
22
+ * The sweep costs more RPCs than the single-cohort sample and is reserved for representativeness-over-
23
+ * latency use cases (voting quorums, capability fairness audits); db-p2p binds it to the voting
24
+ * `QuorumDiscovery.sweep` port.
25
+ */
26
+
27
+ import { matchesFilter } from "./capability-filter.js";
28
+ import { verifyProviderEntry, type AggregateCountV1, type CapabilityFilter, type EntrySigVerifier, type ProviderEntryV1 } from "./wire.js";
29
+
30
+ /** The tier whose prefix shards the sweep ranges over (`docs/matchmaking.md` §Wire formats — typically 1). */
31
+ export const DEFAULT_SWEEP_TARGET_TIER = 1;
32
+ /** Fan-out ceiling: never query more than this many shards in one sweep (bounds RPC cost). */
33
+ export const DEFAULT_SWEEP_MAX_SHARDS = 16;
34
+ /**
35
+ * Multiplier on `wantCount` when accumulating bucketed shard populations. `1` because {@link logBucketCount}
36
+ * already rounds counts *down* — the true population is `>=` the reported sum, so the selection already
37
+ * over-provisions without an extra factor.
38
+ */
39
+ export const DEFAULT_SWEEP_OVERPROVISION = 1;
40
+
41
+ /** One tier-1 prefix shard the sweep elected to query, with its (bucketed) reported population. */
42
+ export interface ShardSelection {
43
+ /** Prefix slot `0..F-1` identifying the tier-1 cohort. */
44
+ readonly prefixSlot: number;
45
+ /** The tier whose shard this is (typically 1). */
46
+ readonly targetTier: number;
47
+ /** The shard's log-bucketed reported provider count (rounds down — see {@link logBucketCount}). */
48
+ readonly bucketedCount: number;
49
+ }
50
+
51
+ /** Inputs to {@link selectShards}. */
52
+ export interface SelectShardsOptions {
53
+ /** Providers the seeker needs (drives how many shards are unioned). */
54
+ readonly wantCount: number;
55
+ /** Which `targetTier` buckets to consider. Default {@link DEFAULT_SWEEP_TARGET_TIER}. */
56
+ readonly targetTier?: number;
57
+ /** Fan-out ceiling. Default {@link DEFAULT_SWEEP_MAX_SHARDS}. */
58
+ readonly maxShards?: number;
59
+ /** Multiplier on `wantCount`. Default {@link DEFAULT_SWEEP_OVERPROVISION}. */
60
+ readonly overprovision?: number;
61
+ }
62
+
63
+ /**
64
+ * Choose which tier-1 shards to query from an {@link AggregateCountV1}: the highest-population shards
65
+ * first (ties broken by ascending `prefixSlot` for determinism), accumulating bucketed counts until they
66
+ * cover `wantCount * overprovision`, capped at `maxShards`. Empty shards (`count === 0`) are skipped.
67
+ * Pure and deterministic.
68
+ */
69
+ export function selectShards(aggregate: AggregateCountV1, opts: SelectShardsOptions): ShardSelection[] {
70
+ const targetTier = opts.targetTier ?? DEFAULT_SWEEP_TARGET_TIER;
71
+ const maxShards = opts.maxShards ?? DEFAULT_SWEEP_MAX_SHARDS;
72
+ const overprovision = opts.overprovision ?? DEFAULT_SWEEP_OVERPROVISION;
73
+ const need = Math.max(1, Math.ceil(opts.wantCount * overprovision));
74
+
75
+ const ranked = aggregate.bucketCounts
76
+ .filter((b) => b.targetTier === targetTier && b.count > 0)
77
+ .sort((a, b) => b.count - a.count || a.prefixSlot - b.prefixSlot);
78
+
79
+ const selected: ShardSelection[] = [];
80
+ let cumulative = 0;
81
+ for (const bucket of ranked) {
82
+ if (selected.length >= maxShards) {
83
+ break;
84
+ }
85
+ selected.push({ prefixSlot: bucket.prefixSlot, targetTier: bucket.targetTier, bucketedCount: bucket.count });
86
+ cumulative += bucket.count;
87
+ if (cumulative >= need) {
88
+ break;
89
+ }
90
+ }
91
+ return selected;
92
+ }
93
+
94
+ /** Identifies one tier shard to query directly (db-p2p resolves it to `coord_d` and dials the cohort). */
95
+ export interface SweepShardQuery {
96
+ readonly prefixSlot: number;
97
+ readonly targetTier: number;
98
+ }
99
+
100
+ /**
101
+ * The transport seam the sweep drives, injected by db-p2p. `fetchAggregate` queries the root cohort
102
+ * (resolving `undefined` when the root is cold / unpromoted and returns no {@link AggregateCountV1});
103
+ * `queryShard` queries one elected tier-1 cohort; `verifyAggregate` (optional) threshold-verifies the
104
+ * aggregate before its counts are trusted.
105
+ */
106
+ export interface MultiCohortSweepPorts {
107
+ /** Query the root cohort for the aggregate; `undefined` when the root produced none (cold / unpromoted). */
108
+ fetchAggregate(): Promise<AggregateCountV1 | undefined>;
109
+ /** Threshold-verify the aggregate (db-p2p binds the cohort crypto). Omitted → trusted unconditionally. */
110
+ verifyAggregate?(aggregate: AggregateCountV1): boolean;
111
+ /** Query one elected shard; returns its advisory provider entries (the sweep re-validates each). */
112
+ queryShard(shard: SweepShardQuery): Promise<readonly ProviderEntryV1[]>;
113
+ }
114
+
115
+ /** Inputs to {@link runMultiCohortSweep}. */
116
+ export interface MultiCohortSweepOptions {
117
+ /** The matchmaking topic id (used to re-validate each forwarded entry's `registrationSig`). */
118
+ readonly topicId: Uint8Array;
119
+ /** Providers the seeker needs (drives shard selection). */
120
+ readonly wantCount: number;
121
+ /** Per-entry signature verifier (db-p2p binds `verifyPeerSig`). */
122
+ readonly verifyEntry: EntrySigVerifier;
123
+ /** Optional capability filter, re-applied over every shard's returned set. */
124
+ readonly filter?: CapabilityFilter;
125
+ /** Which `targetTier` shards to range over. Default {@link DEFAULT_SWEEP_TARGET_TIER}. */
126
+ readonly targetTier?: number;
127
+ /** Fan-out ceiling. Default {@link DEFAULT_SWEEP_MAX_SHARDS}. */
128
+ readonly maxShards?: number;
129
+ /** Multiplier on `wantCount`. Default {@link DEFAULT_SWEEP_OVERPROVISION}. */
130
+ readonly overprovision?: number;
131
+ /**
132
+ * Patience budget (ms) for the whole sweep; drains across the shard fan-out. Omitted ⇒ unbounded
133
+ * (query every selected shard, today's behaviour). Mirrors the walk leg's budget.
134
+ */
135
+ readonly patienceMs?: number;
136
+ /** Wall clock (unix ms); injectable for tests. Default `Date.now`. Only consulted when `patienceMs` set. */
137
+ readonly clock?: () => number;
138
+ }
139
+
140
+ /** The assembled result of a multi-cohort sweep. */
141
+ export interface MultiCohortSweepResult {
142
+ /** The unioned, filtered, `registrationSig`-re-validated providers, deduped by `participantId`. */
143
+ readonly providers: ProviderEntryV1[];
144
+ /** The shards selected from the aggregate (empty when no aggregate / untrusted). */
145
+ readonly selectedShards: ShardSelection[];
146
+ /** How many shards were actually queried (== `selectedShards.length` on success). */
147
+ readonly shardsQueried: number;
148
+ /** Whether the root produced an aggregate at all (`false` for a cold / unpromoted root). */
149
+ readonly aggregateAvailable: boolean;
150
+ /** Whether the aggregate's threshold signature verified (`false` when absent or invalid). */
151
+ readonly aggregateTrusted: boolean;
152
+ }
153
+
154
+ /** An empty result (no aggregate, or an aggregate that failed verification). */
155
+ function emptyResult(aggregateAvailable: boolean, aggregateTrusted: boolean): MultiCohortSweepResult {
156
+ return { providers: [], selectedShards: [], shardsQueried: 0, aggregateAvailable, aggregateTrusted };
157
+ }
158
+
159
+ /**
160
+ * Run the multi-cohort sweep (`docs/matchmaking.md` §Multi-cohort sweep): fetch the root aggregate,
161
+ * threshold-verify it, select high-population shards, query each, and union the deduped + re-validated
162
+ * providers. A cold root (no aggregate) or an aggregate that fails verification yields an empty set so
163
+ * the caller falls back to the single-cohort sample. Each shard entry is filtered and
164
+ * `registrationSig`-re-validated before it counts — the cohort vouches only for "the set I held".
165
+ *
166
+ * When `opts.patienceMs` is supplied, a wall-clock deadline is fixed at entry and the shard fan-out
167
+ * stops as soon as the budget drains — mirroring the walk leg's patience model. This is
168
+ * "stop starting new shard queries": an in-flight `queryShard` call is not cancelled mid-flight.
169
+ * When `opts.patienceMs` is absent, every elected shard is queried (today's behaviour).
170
+ */
171
+ export async function runMultiCohortSweep(ports: MultiCohortSweepPorts, opts: MultiCohortSweepOptions): Promise<MultiCohortSweepResult> {
172
+ // Budget: optional wall-clock deadline mirroring the walk leg. Only consulted when patienceMs is set.
173
+ const clock = opts.clock ?? ((): number => Date.now());
174
+ const deadline = opts.patienceMs !== undefined ? clock() + opts.patienceMs : undefined;
175
+ const remaining = (): number => (deadline !== undefined ? Math.max(0, deadline - clock()) : Infinity);
176
+ // If the budget is already drained on entry, skip even the aggregate RPC — the walk already consumed
177
+ // all patience and there is no point spending an RPC on an aggregate we have no time to act on.
178
+ if (deadline !== undefined && remaining() <= 0) {
179
+ return emptyResult(false, false);
180
+ }
181
+
182
+ const aggregate = await ports.fetchAggregate();
183
+ if (aggregate === undefined) {
184
+ return emptyResult(false, false);
185
+ }
186
+ // `aggregateTrusted` is asserted only when a verifier actually ran and passed. With no verifier injected
187
+ // the sweep still proceeds — every shard entry is `registrationSig`-re-validated below, so a forged
188
+ // aggregate can at worst mis-steer shard selection (wasted RPCs / a thinner sample), never inject
189
+ // providers — but the flag stays honest rather than claiming a trust that was never established.
190
+ const aggregateTrusted = ports.verifyAggregate !== undefined;
191
+ if (ports.verifyAggregate !== undefined && !ports.verifyAggregate(aggregate)) {
192
+ return emptyResult(true, false);
193
+ }
194
+
195
+ const selectShardOpts: SelectShardsOptions = { wantCount: opts.wantCount };
196
+ if (opts.targetTier !== undefined) {
197
+ (selectShardOpts as { targetTier: number }).targetTier = opts.targetTier;
198
+ }
199
+ if (opts.maxShards !== undefined) {
200
+ (selectShardOpts as { maxShards: number }).maxShards = opts.maxShards;
201
+ }
202
+ if (opts.overprovision !== undefined) {
203
+ (selectShardOpts as { overprovision: number }).overprovision = opts.overprovision;
204
+ }
205
+ const selected = selectShards(aggregate, selectShardOpts);
206
+
207
+ const matched = new Map<string, ProviderEntryV1>();
208
+ let shardsQueried = 0;
209
+ for (const shard of selected) {
210
+ // Stop starting new shard queries once the budget has drained.
211
+ if (remaining() <= 0) {
212
+ break;
213
+ }
214
+ const entries = await ports.queryShard({ prefixSlot: shard.prefixSlot, targetTier: shard.targetTier });
215
+ shardsQueried++;
216
+ for (const entry of entries) {
217
+ if (!matchesFilter(entry, opts.filter)) {
218
+ continue;
219
+ }
220
+ if (!verifyProviderEntry(opts.topicId, entry, opts.verifyEntry)) {
221
+ continue;
222
+ }
223
+ matched.set(entry.participantId, entry);
224
+ }
225
+ }
226
+
227
+ return {
228
+ providers: [...matched.values()],
229
+ selectedShards: selected,
230
+ shardsQueried,
231
+ aggregateAvailable: true,
232
+ aggregateTrusted,
233
+ };
234
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Matchmaking — provider decision/state (db-core, transport-agnostic).
3
+ *
4
+ * A {@link MatchmakingProvider} owns the live provider state for one topic: its capability tags, its
5
+ * current `capacityBudget`, and a correlation id (registration identity; not bound into the
6
+ * matchmaking signature — see {@link providerSigningPayload} option (b)). It builds the
7
+ * signed {@link ProviderAppPayloadV1} (and the opaque bytes for the cohort-topic
8
+ * `RegisterV1.appPayload` slot) that the db-p2p `provider-manager` registers at cohort-topic tier
9
+ * **T2** (`docs/matchmaking.md` §Provider registration).
10
+ *
11
+ * It is crypto-free: signing is an injected callback (db-p2p supplies the libp2p peer key), matching
12
+ * the cohort-topic {@link import("../cohort-topic/service.js").ParticipantSigner} pattern.
13
+ *
14
+ * Self-throttling (`docs/matchmaking.md` §Provider self-throttling) is expressed here as state:
15
+ * - **Signal full** — {@link MatchmakingProvider.signalFull} sets `capacityBudget = 0`; the provider
16
+ * stays listed as "available but at capacity". The manager re-registers to push the new payload
17
+ * (the cohort-topic `RenewV1` carries no `appPayload`, so a capacity change is a re-register, not a
18
+ * ping — see the implement handoff).
19
+ * - **Withdraw** — {@link MatchmakingProvider.markWithdrawn} records intent; the manager stops
20
+ * renewing so the record ages out by TTL. Withdrawal is an **optimization, not a correctness
21
+ * requirement** (§Provider self-throttling, GROUNDING resolution): a non-withdrawn registration is
22
+ * bounded by TTL eviction.
23
+ */
24
+
25
+ import { randomBytes } from "@noble/hashes/utils.js";
26
+ import { providerSigningPayload, type ProviderAppPayloadV1, encodeProviderAppPayload } from "./wire.js";
27
+
28
+ /** Construction inputs for a {@link MatchmakingProvider}. */
29
+ export interface MatchmakingProviderOptions {
30
+ /** The matchmaking topic this provider serves (from {@link import("./topic-anchor.js").MatchTopicAnchor}). */
31
+ readonly topicId: Uint8Array;
32
+ /** Application-defined capability tags. */
33
+ readonly capabilities: readonly string[];
34
+ /** Initial concurrent-task budget (integer `>= 0`). */
35
+ readonly capacityBudget: number;
36
+ /** Multiaddr or PeerId-based callback. */
37
+ readonly contactHint: string;
38
+ /** Optional soft expiry hint (unix ms). */
39
+ readonly serviceUntil?: number;
40
+ /** Sign the canonical registration image; resolves the base64url signature. */
41
+ readonly sign: (payload: Uint8Array) => Promise<string>;
42
+ /** 16-byte registration correlation id (not signature-bound); default fresh CSPRNG bytes. */
43
+ readonly correlationId?: Uint8Array;
44
+ /** CSPRNG source (injectable for deterministic tests). Default `@noble/hashes` `randomBytes`. */
45
+ readonly randomBytes?: (n: number) => Uint8Array;
46
+ }
47
+
48
+ /** Live provider state + signed-payload builder for one matchmaking topic. */
49
+ export class MatchmakingProvider {
50
+ readonly topicId: Uint8Array;
51
+ readonly correlationId: Uint8Array;
52
+ private readonly capabilities: readonly string[];
53
+ private readonly contactHint: string;
54
+ private readonly serviceUntil?: number;
55
+ private readonly sign: (payload: Uint8Array) => Promise<string>;
56
+ private capacity: number;
57
+ private withdrawnFlag = false;
58
+
59
+ constructor(options: MatchmakingProviderOptions) {
60
+ this.topicId = options.topicId;
61
+ this.capabilities = [...options.capabilities];
62
+ this.contactHint = options.contactHint;
63
+ this.serviceUntil = options.serviceUntil;
64
+ this.sign = options.sign;
65
+ this.capacity = requireBudget(options.capacityBudget);
66
+ const rand = options.randomBytes ?? randomBytes;
67
+ this.correlationId = options.correlationId ?? rand(16);
68
+ }
69
+
70
+ /** Current concurrent-task budget; `0` means "listed but full". */
71
+ get capacityBudget(): number {
72
+ return this.capacity;
73
+ }
74
+
75
+ /** True once {@link markWithdrawn} has been called (the manager should stop renewing). */
76
+ get withdrawn(): boolean {
77
+ return this.withdrawnFlag;
78
+ }
79
+
80
+ /** Set the live budget (integer `>= 0`). The next built payload reflects it. */
81
+ setCapacity(budget: number): void {
82
+ this.capacity = requireBudget(budget);
83
+ }
84
+
85
+ /** Signal "available but at capacity" by setting `capacityBudget = 0` (§Provider self-throttling). */
86
+ signalFull(): void {
87
+ this.capacity = 0;
88
+ }
89
+
90
+ /** Record withdrawal intent; the manager stops renewing so the record TTL-expires (optimization). */
91
+ markWithdrawn(): void {
92
+ this.withdrawnFlag = true;
93
+ }
94
+
95
+ /** Build the signed {@link ProviderAppPayloadV1} reflecting the current capacity. */
96
+ async buildAppPayload(): Promise<ProviderAppPayloadV1> {
97
+ const signature = await this.sign(providerSigningPayload(this.topicId, this.capabilities, this.capacity));
98
+ const payload: ProviderAppPayloadV1 = {
99
+ kind: "match-provider",
100
+ capabilities: [...this.capabilities],
101
+ capacityBudget: this.capacity,
102
+ contactHint: this.contactHint,
103
+ signature,
104
+ };
105
+ if (this.serviceUntil !== undefined) {
106
+ payload.serviceUntil = this.serviceUntil;
107
+ }
108
+ return payload;
109
+ }
110
+
111
+ /** Build the opaque bytes for the cohort-topic `RegisterV1.appPayload` slot. */
112
+ async appPayloadBytes(): Promise<Uint8Array> {
113
+ return encodeProviderAppPayload(await this.buildAppPayload());
114
+ }
115
+ }
116
+
117
+ /** Validate a capacity budget is an integer `>= 0`. */
118
+ function requireBudget(budget: number): number {
119
+ if (!Number.isInteger(budget) || budget < 0) {
120
+ throw new RangeError(`matchmaking provider: capacityBudget must be an integer >= 0, got ${budget}`);
121
+ }
122
+ return budget;
123
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Matchmaking — pure `QueryV1` evaluation (db-core, transport-agnostic).
3
+ *
4
+ * The cohort-side query handler (`db-p2p/src/matchmaking/query-handler.ts`) decodes its local
5
+ * registration records into the {@link LocalProviderRegistration} / {@link LocalSeekerRegistration}
6
+ * shapes below and hands them here; this module performs the *advisory* selection that
7
+ * `docs/matchmaking.md` §Seeker query / §Capability filter specify:
8
+ *
9
+ * - Providers are filtered through {@link matchesFilter} (when `includeProviders`).
10
+ * - Each included set is truncated to `query.limit` (`<= query_limit_max`, enforced on decode in
11
+ * `wire.ts`); {@link QueryEvalResult.truncated} is set when *any* included set had more matches than
12
+ * `limit` allowed, so the seeker knows to re-query a sibling cohort.
13
+ * - The forwarded entries carry the provider/seeker's own `registrationSig` verbatim, so the seeker
14
+ * re-validates each one (`verifyProviderEntry`) — the cohort vouches only for the *set it held*.
15
+ *
16
+ * Pure: no I/O, no clock, no crypto. The db-p2p handler attaches `topicTraffic` / `cohortEpoch` and
17
+ * the primary's reply signature.
18
+ */
19
+
20
+ import { matchesFilter } from "./capability-filter.js";
21
+ import type { ProviderAppPayloadV1, SeekerAppPayloadV1, ProviderEntryV1, SeekerEntryV1, QueryV1 } from "./wire.js";
22
+
23
+ /** A decoded local provider registration held by the cohort, ready for {@link evaluateQuery}. */
24
+ export interface LocalProviderRegistration {
25
+ /** The provider's peer-id string — the entry's `participantId` AND the `registrationSig` signer. */
26
+ readonly participantId: string;
27
+ /** Unix ms the registration first attached (forwarded verbatim for seeker FCFS ordering). */
28
+ readonly attachedAt: number;
29
+ /** The decoded, validated provider app payload (capabilities, budget, contact, signature). */
30
+ readonly payload: ProviderAppPayloadV1;
31
+ }
32
+
33
+ /** A decoded local seeker registration held by the cohort (collective-assembly discovery). */
34
+ export interface LocalSeekerRegistration {
35
+ readonly participantId: string;
36
+ readonly attachedAt: number;
37
+ readonly payload: SeekerAppPayloadV1;
38
+ }
39
+
40
+ /** The selected entries for a {@link QueryReplyV1} body (the db-p2p handler signs + frames it). */
41
+ export interface QueryEvalResult {
42
+ readonly providers?: ProviderEntryV1[];
43
+ readonly seekers?: SeekerEntryV1[];
44
+ /** `true` when an included set had more matches than `query.limit` allowed (re-query hint). */
45
+ readonly truncated: boolean;
46
+ }
47
+
48
+ /** Build a forwarded {@link ProviderEntryV1} from a local registration (signature forwarded verbatim). */
49
+ export function providerEntryOf(reg: LocalProviderRegistration): ProviderEntryV1 {
50
+ return {
51
+ participantId: reg.participantId,
52
+ capabilities: [...reg.payload.capabilities],
53
+ capacityBudget: reg.payload.capacityBudget,
54
+ contactHint: reg.payload.contactHint,
55
+ attachedAt: reg.attachedAt,
56
+ registrationSig: reg.payload.signature,
57
+ };
58
+ }
59
+
60
+ /** Build a forwarded {@link SeekerEntryV1} from a local registration (signature forwarded verbatim). */
61
+ export function seekerEntryOf(reg: LocalSeekerRegistration): SeekerEntryV1 {
62
+ return {
63
+ participantId: reg.participantId,
64
+ wantCount: reg.payload.wantCount,
65
+ contactHint: reg.payload.contactHint,
66
+ attachedAt: reg.attachedAt,
67
+ registrationSig: reg.payload.signature,
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Evaluate `query` against the cohort's locally-held registrations: filter providers, optionally include
73
+ * seekers, truncate each included set to `query.limit`. Inputs are returned in `attachedAt` order
74
+ * (oldest first) so truncation keeps the longest-waiting registrations — the FCFS bias the arrival-push
75
+ * fairness rule also uses (`docs/matchmaking.md` §Fairness).
76
+ */
77
+ export function evaluateQuery(
78
+ query: QueryV1,
79
+ providers: readonly LocalProviderRegistration[],
80
+ seekers: readonly LocalSeekerRegistration[],
81
+ ): QueryEvalResult {
82
+ let truncated = false;
83
+ const result: { providers?: ProviderEntryV1[]; seekers?: SeekerEntryV1[]; truncated: boolean } = { truncated: false };
84
+
85
+ if (query.includeProviders) {
86
+ const matched = providers
87
+ .filter((reg) => matchesFilter(reg.payload, query.filter))
88
+ .sort((a, b) => a.attachedAt - b.attachedAt);
89
+ if (matched.length > query.limit) {
90
+ truncated = true;
91
+ }
92
+ result.providers = matched.slice(0, query.limit).map(providerEntryOf);
93
+ }
94
+
95
+ if (query.includeSeekers) {
96
+ const ordered = [...seekers].sort((a, b) => a.attachedAt - b.attachedAt);
97
+ if (ordered.length > query.limit) {
98
+ truncated = true;
99
+ }
100
+ result.seekers = ordered.slice(0, query.limit).map(seekerEntryOf);
101
+ }
102
+
103
+ result.truncated = truncated;
104
+ return result;
105
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Matchmaking — seeker hang-out-vs-continue decision engine (db-core, pure).
3
+ *
4
+ * Per `docs/matchmaking.md` §Hang-out vs. continue. After a seeker registers at a cohort and receives
5
+ * `Accepted` with `topicTraffic` at tree tier `d`, it must decide: is *this* tier the right place to
6
+ * wait, or should it walk one tier toward the root? This module is the pure arithmetic of that choice
7
+ * — no I/O, no clock — so it is unit-testable directly against the doc's worked examples. The db-p2p
8
+ * `seeker-walk-client` drives the walk (register / query / renew / withdraw) and calls {@link decide}.
9
+ *
10
+ * Decision rule (`docs/matchmaking.md` §Decision rule):
11
+ *
12
+ * 1. **Immediate-match.** If `currentMatches >= wantCount` → `done`. (The caller has already issued the
13
+ * `QueryV1`; `currentMatches` is its filter-matched, re-validated yield. This also covers edge case 2
14
+ * — a stale `arrivalsPerMin = 0` after an epoch rotation still gets a real query first, so a quiet
15
+ * cohort that actually holds enough providers resolves to `done` rather than a spurious escalate.)
16
+ * 2. **Hang-out feasibility.**
17
+ * ```
18
+ * expectedNewMatches ≈ arrivalsPerMin × filterAcceptRatio × (patienceMsRemaining / 60000)
19
+ * contentionFactor ≈ min(1 + (queriesPerMin × meanWantCount) / max(arrivalsPerMin, 1), cap)
20
+ * hang out iff currentMatches + expectedNewMatches ≥ wantCount × contentionFactor
21
+ * ```
22
+ * 3. **Otherwise escalate** (walk one tier toward the root).
23
+ *
24
+ * Edge cases the engine encodes (the rest live in the walk client, which owns the walk topology):
25
+ * - **Filter matches almost nothing (case 4):** a `filterAcceptRatio` decayed toward 0 collapses
26
+ * `expectedNewMatches`, so the feasibility test fails at every tier and the seeker walks to the root.
27
+ * - **Many seekers competing (case 5):** a high `queriesPerMin` inflates `contentionFactor` up to
28
+ * `cap`, raising the threshold so more seekers escalate toward aggregation — self-balancing, bounded.
29
+ */
30
+
31
+ import type { HangOutConfig } from "./config.js";
32
+ import { FILTER_ACCEPT_RATIO_INITIAL, MEAN_WANT_COUNT_DEFAULT } from "./config.js";
33
+
34
+ /** Inputs to {@link decide}: the cohort's `topicTraffic` plus the seeker's running state. */
35
+ export interface SeekerDecisionInputs {
36
+ /** Filter-matched, re-validated providers from the immediate `QueryV1` at this tier. */
37
+ readonly currentMatches: number;
38
+ /** `topicTraffic.directParticipants` — informational (providers known here right now). */
39
+ readonly directParticipants: number;
40
+ /** `topicTraffic.arrivalsPerMin` — provider registration + renewal rate. */
41
+ readonly arrivalsPerMin: number;
42
+ /** `topicTraffic.queriesPerMin` — competing seeker activity over the same pool. */
43
+ readonly queriesPerMin: number;
44
+ /** `topicTraffic.childCohortCount` — `> 0` means this tier has promoted (descend territory). */
45
+ readonly childCohortCount: number;
46
+ /** Providers the seeker needs. */
47
+ readonly wantCount: number;
48
+ /** Patience budget left at this tier (drains across walked tiers — the client tracks it). */
49
+ readonly patienceMsRemaining: number;
50
+ /** Running estimate of the fraction of returned providers that pass the seeker's filter (decays from 1.0). */
51
+ readonly filterAcceptRatio: number;
52
+ /** Assumed mean `wantCount` of competing seekers (small constant or learned). */
53
+ readonly meanWantCount: number;
54
+ }
55
+
56
+ /** The outcome of {@link decide}. `hangOut` carries the poll cadence for the requery loop. */
57
+ export type SeekerDecision =
58
+ | { readonly action: "done" }
59
+ | { readonly action: "hangOut"; readonly requeryIntervalMs: number }
60
+ | { readonly action: "escalate" };
61
+
62
+ /** `expectedNewMatches ≈ arrivalsPerMin × filterAcceptRatio × (patienceMsRemaining / 60000)`. */
63
+ export function expectedNewMatches(inputs: SeekerDecisionInputs): number {
64
+ return inputs.arrivalsPerMin * inputs.filterAcceptRatio * (inputs.patienceMsRemaining / 60_000);
65
+ }
66
+
67
+ /** `contentionFactor ≈ min(1 + (queriesPerMin × meanWantCount) / max(arrivalsPerMin, 1), cap)`. */
68
+ export function contentionFactor(inputs: SeekerDecisionInputs, cfg: HangOutConfig): number {
69
+ const raw = 1 + (inputs.queriesPerMin * inputs.meanWantCount) / Math.max(inputs.arrivalsPerMin, 1);
70
+ return Math.min(raw, cfg.contentionFactorCap);
71
+ }
72
+
73
+ /** The hang-out feasibility threshold `wantCount × contentionFactor`. */
74
+ export function hangOutThreshold(inputs: SeekerDecisionInputs, cfg: HangOutConfig): number {
75
+ return inputs.wantCount * contentionFactor(inputs, cfg);
76
+ }
77
+
78
+ /**
79
+ * Decide whether the seeker is `done` (immediate match), should `hangOut` (feasible to wait here), or
80
+ * should `escalate` (walk one tier toward the root). Pure — see the module header for the rule.
81
+ */
82
+ export function decide(inputs: SeekerDecisionInputs, cfg: HangOutConfig): SeekerDecision {
83
+ if (inputs.currentMatches >= inputs.wantCount) {
84
+ return { action: "done" };
85
+ }
86
+ const projected = inputs.currentMatches + expectedNewMatches(inputs);
87
+ if (projected >= hangOutThreshold(inputs, cfg)) {
88
+ return { action: "hangOut", requeryIntervalMs: cfg.requeryIntervalMs };
89
+ }
90
+ return { action: "escalate" };
91
+ }
92
+
93
+ // --- filterAcceptRatio running refinement (`docs/matchmaking.md` §Decision rule / §Edge cases 4) ---
94
+
95
+ /**
96
+ * Running yield accumulator for `filterAcceptRatio`. The ratio starts at `filter_accept_ratio_initial`
97
+ * (1.0, used before any observation) and is refined to the cumulative `matched / returned` over the
98
+ * walk: after two cohorts that each return ~10% matchable providers it settles near 0.1, collapsing
99
+ * `expectedNewMatches` for a pathological filter (edge case 4).
100
+ */
101
+ export interface FilterAcceptRatioState {
102
+ /** Total filter-matched providers observed across queries so far. */
103
+ readonly matched: number;
104
+ /** Total providers returned across queries so far. */
105
+ readonly returned: number;
106
+ }
107
+
108
+ /** A fresh (zero-observation) {@link FilterAcceptRatioState}. */
109
+ export function newFilterAcceptRatioState(): FilterAcceptRatioState {
110
+ return { matched: 0, returned: 0 };
111
+ }
112
+
113
+ /** Fold one query's yield (`matched` of `returned`) into the running state. */
114
+ export function observeYield(state: FilterAcceptRatioState, matched: number, returned: number): FilterAcceptRatioState {
115
+ return { matched: state.matched + matched, returned: state.returned + returned };
116
+ }
117
+
118
+ /**
119
+ * The current `filterAcceptRatio`: cumulative `matched / returned`, or `initial` (1.0) before any
120
+ * providers have been returned (so the first hang-out estimate is optimistic, then refines).
121
+ */
122
+ export function filterAcceptRatio(state: FilterAcceptRatioState, initial: number = FILTER_ACCEPT_RATIO_INITIAL): number {
123
+ return state.returned > 0 ? state.matched / state.returned : initial;
124
+ }
125
+
126
+ /** The default assumed competing-seeker `meanWantCount` (re-exported for the walk client). */
127
+ export const DEFAULT_MEAN_WANT_COUNT = MEAN_WANT_COUNT_DEFAULT;
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Matchmaking — seeker registration state (db-core, transport-agnostic).
3
+ *
4
+ * A {@link MatchmakingSeeker} owns the short-lived seeker state for one topic and builds the signed
5
+ * {@link SeekerAppPayloadV1} that the db-p2p `seeker-manager` registers at cohort-topic tier **T2**
6
+ * with a short TTL (`seeker_ttl`, default 10 s — `docs/matchmaking.md` §Seeker query). The seeker
7
+ * registers briefly so other seekers can find it (collective assembly) and the cohort sees active
8
+ * demand.
9
+ *
10
+ * This module holds *registration* state only. The `QueryV1` issuance and the hang-out-vs-continue
11
+ * decision live in the pure {@link import("./seeker-walk.js").decide} engine and the db-p2p
12
+ * `seeker-walk-client` that drives the walk; the capability filter is {@link import("./capability-filter.js").matchesFilter}.
13
+ *
14
+ * Crypto-free: signing is an injected callback, matching {@link MatchmakingProvider}.
15
+ */
16
+
17
+ import { randomBytes } from "@noble/hashes/utils.js";
18
+ import { seekerSigningPayload, type CapabilityFilter, type SeekerAppPayloadV1, encodeSeekerAppPayload } from "./wire.js";
19
+
20
+ /** Construction inputs for a {@link MatchmakingSeeker}. */
21
+ export interface MatchmakingSeekerOptions {
22
+ /** The matchmaking topic this seeker is querying. */
23
+ readonly topicId: Uint8Array;
24
+ /** Number of providers desired (integer `>= 1`). */
25
+ readonly wantCount: number;
26
+ /** Multiaddr or PeerId-based callback (collective-assembly use). */
27
+ readonly contactHint: string;
28
+ /** Optional capability filter (evaluated cohort-side in the next ticket; carried here). */
29
+ readonly filter?: CapabilityFilter;
30
+ /** Opt into arrival pushes; default false (poll path). Consumed by the next ticket. */
31
+ readonly pushOnArrival?: boolean;
32
+ /** Sign the canonical registration image; resolves the base64url signature. */
33
+ readonly sign: (payload: Uint8Array) => Promise<string>;
34
+ /** 16-byte registration correlation id (not signature-bound); default fresh CSPRNG bytes. */
35
+ readonly correlationId?: Uint8Array;
36
+ /** CSPRNG source (injectable for deterministic tests). Default `@noble/hashes` `randomBytes`. */
37
+ readonly randomBytes?: (n: number) => Uint8Array;
38
+ }
39
+
40
+ /** Live seeker state + signed-payload builder for one matchmaking topic (registration only). */
41
+ export class MatchmakingSeeker {
42
+ readonly topicId: Uint8Array;
43
+ readonly correlationId: Uint8Array;
44
+ private readonly wantCount: number;
45
+ private readonly contactHint: string;
46
+ private readonly filter?: CapabilityFilter;
47
+ private readonly pushOnArrival?: boolean;
48
+ private readonly sign: (payload: Uint8Array) => Promise<string>;
49
+
50
+ constructor(options: MatchmakingSeekerOptions) {
51
+ if (!Number.isInteger(options.wantCount) || options.wantCount < 1) {
52
+ throw new RangeError(`matchmaking seeker: wantCount must be an integer >= 1, got ${options.wantCount}`);
53
+ }
54
+ this.topicId = options.topicId;
55
+ this.wantCount = options.wantCount;
56
+ this.contactHint = options.contactHint;
57
+ this.filter = options.filter;
58
+ this.pushOnArrival = options.pushOnArrival;
59
+ this.sign = options.sign;
60
+ const rand = options.randomBytes ?? randomBytes;
61
+ this.correlationId = options.correlationId ?? rand(16);
62
+ }
63
+
64
+ /** Build the signed {@link SeekerAppPayloadV1} for this seeker's registration. */
65
+ async buildAppPayload(): Promise<SeekerAppPayloadV1> {
66
+ const signature = await this.sign(seekerSigningPayload(this.topicId, this.wantCount));
67
+ const payload: SeekerAppPayloadV1 = {
68
+ kind: "match-seeker",
69
+ wantCount: this.wantCount,
70
+ contactHint: this.contactHint,
71
+ signature,
72
+ };
73
+ if (this.filter !== undefined) {
74
+ payload.filter = this.filter;
75
+ }
76
+ if (this.pushOnArrival !== undefined) {
77
+ payload.pushOnArrival = this.pushOnArrival;
78
+ }
79
+ return payload;
80
+ }
81
+
82
+ /** Build the opaque bytes for the cohort-topic `RegisterV1.appPayload` slot. */
83
+ async appPayloadBytes(): Promise<Uint8Array> {
84
+ return encodeSeekerAppPayload(await this.buildAppPayload());
85
+ }
86
+ }