@optimystic/db-core 0.13.5 → 0.14.1

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,92 @@
1
+ /**
2
+ * Reactivity — notification origination (`docs/reactivity.md` §Notification origination).
3
+ *
4
+ * The tail cohort's primary for a collection is, by construction, the transaction-layer tail-cluster:
5
+ * the cohort-topic primary at `coord_0(_, topicId)` where `topicId = H(tailId ‖ "reactivity")`. As soon
6
+ * as the commit's threshold signature is assembled, it emits a {@link NotificationV1} whose `sig` is
7
+ * **bit-for-bit** the commit certificate's threshold signature — reactivity never re-signs.
8
+ *
9
+ * This module is the pure assembler. The local-change-notifier bridge ([local-change-notifier-bridge])
10
+ * supplies the {@link CollectionChangeEvent} and the pass-through {@link CommitCert}; db-p2p's
11
+ * origination manager calls {@link buildNotificationV1} with those plus the per-emission context
12
+ * (`tailId`, `timestamp`, the per-collection `deltaMaxBytes`) and fans the result out. Crypto-free: it
13
+ * copies `commitCert.thresholdSig` and `commitCert.signers` through unchanged.
14
+ */
15
+ import { createRingHash } from "../cohort-topic/ring-hash.js";
16
+ import { bytesToB64url } from "../cohort-topic/wire/codec.js";
17
+ const utf8 = new TextEncoder();
18
+ /**
19
+ * Assemble the {@link NotificationV1} for one committed change, reusing the commit cert's threshold
20
+ * signature unchanged.
21
+ *
22
+ * `digest` carries the commit-vote **signed payload** the threshold signature was computed over —
23
+ * `commitCert.signedPayload`, the cluster's per-member `utf8(commitHash + ":approve")` byte image,
24
+ * identical across all approving signers. Because the verifier recomputes the cohort threshold check over
25
+ * exactly these bytes (`payload = b64urlToBytes(n.digest)`), a subscriber's **real** Ed25519
26
+ * threshold-verify over `digest` reproduces the signed image and succeeds — the cluster↔reactivity
27
+ * integration seam is **closed** (no pass-crypto stub required). The hint-only contract still holds for
28
+ * the optional `delta`: deltas are advisory; the digest + threshold signature are the authoritative proof.
29
+ */
30
+ export function buildNotificationV1(event, commitCert, ctx) {
31
+ const encodeSigner = ctx.encodeSigner ?? ((s) => s);
32
+ const notification = {
33
+ v: 1,
34
+ collectionId: event.collectionId,
35
+ tailId: ctx.tailId,
36
+ revision: event.rev,
37
+ digest: bytesToB64url(commitCert.signedPayload),
38
+ timestamp: ctx.timestamp,
39
+ sig: bytesToB64url(commitCert.thresholdSig),
40
+ signers: commitCert.signers.map(encodeSigner),
41
+ };
42
+ if (ctx.deltaMaxBytes > 0 && ctx.delta !== undefined && ctx.delta.length <= ctx.deltaMaxBytes) {
43
+ notification.delta = bytesToB64url(ctx.delta);
44
+ }
45
+ if (ctx.rotationHint !== undefined) {
46
+ notification.rotationHint = ctx.rotationHint;
47
+ }
48
+ // An invalidation change event carries a typed marker the subscriber uses to distinguish a reversal
49
+ // from an ordinary commit (drop derived results + resubmit, vs. refresh). It rides the same path and
50
+ // reuses the invalidation's commit cert as `sig` — see {@link CollectionChangeEvent.invalidation}.
51
+ if (event.invalidation) {
52
+ notification.invalidation = true;
53
+ if (event.invalidatedActionId !== undefined) {
54
+ notification.invalidatedActionId = event.invalidatedActionId;
55
+ }
56
+ }
57
+ return notification;
58
+ }
59
+ /**
60
+ * Compact, stable digest of a notification's signature for the dedupe key `(revision, sigDigest)`
61
+ * (`docs/reactivity.md` §Per-revision dedupe). Hashing the signature bytes keeps the key bounded and
62
+ * distinguishes two distinct threshold sigs that legitimately share a revision during partition merge.
63
+ */
64
+ export function sigDigest(sig, hash = createRingHash()) {
65
+ return bytesToB64url(hash.H(utf8.encode(sig)));
66
+ }
67
+ /** The dedupe-set key for a notification: `${revision}:${sigDigest(sig)}`. */
68
+ export function dedupeKey(revision, sig, hash) {
69
+ return `${revision}:${sigDigest(sig, hash)}`;
70
+ }
71
+ /**
72
+ * Collapse a batch of inbound notifications to the distinct **invalidated action ids** a client must act
73
+ * on, in first-seen order. A single dispute can fan out several invalidation notifications — one per
74
+ * cascade child (`docs/right-is-right.md` §Read-Dependency Cascade) — and a client holding dependents of
75
+ * several may receive several; coalescing by `invalidatedActionId` lets it drop + resubmit each affected
76
+ * unit exactly once instead of once per notification. Notifications that are not invalidations (ordinary
77
+ * commits) and invalidations missing an `invalidatedActionId` are ignored here — the former drive a plain
78
+ * refresh, the latter still surface the reverted state on the next authoritative read. A hint-layer helper:
79
+ * the client still verifies against committed state.
80
+ */
81
+ export function coalesceInvalidatedActionIds(notifications) {
82
+ const seen = new Set();
83
+ const ordered = [];
84
+ for (const n of notifications) {
85
+ if (n.invalidation && n.invalidatedActionId !== undefined && !seen.has(n.invalidatedActionId)) {
86
+ seen.add(n.invalidatedActionId);
87
+ ordered.push(n.invalidatedActionId);
88
+ }
89
+ }
90
+ return ordered;
91
+ }
92
+ //# sourceMappingURL=notification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification.js","sourceRoot":"","sources":["../../../src/reactivity/notification.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAK9D,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC;AAyB/B;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAA4B,EAAE,UAAsB,EAAE,GAAuB;IAChH,MAAM,YAAY,GAAG,GAAG,CAAC,YAAY,IAAI,CAAC,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,YAAY,GAAmB;QACpC,CAAC,EAAE,CAAC;QACJ,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,QAAQ,EAAE,KAAK,CAAC,GAAG;QACnB,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,aAAa,CAAC;QAC/C,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,GAAG,EAAE,aAAa,CAAC,UAAU,CAAC,YAAY,CAAC;QAC3C,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;KAC7C,CAAC;IACF,IAAI,GAAG,CAAC,aAAa,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,GAAG,CAAC,aAAa,EAAE,CAAC;QAC/F,YAAY,CAAC,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,GAAG,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACpC,YAAY,CAAC,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC;IAC9C,CAAC;IACD,oGAAoG;IACpG,qGAAqG;IACrG,mGAAmG;IACnG,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACxB,YAAY,CAAC,YAAY,GAAG,IAAI,CAAC;QACjC,IAAI,KAAK,CAAC,mBAAmB,KAAK,SAAS,EAAE,CAAC;YAC7C,YAAY,CAAC,mBAAmB,GAAG,KAAK,CAAC,mBAAmB,CAAC;QAC9D,CAAC;IACF,CAAC;IACD,OAAO,YAAY,CAAC;AACrB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,OAAkB,cAAc,EAAE;IACxE,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,GAAW,EAAE,IAAgB;IACxE,OAAO,GAAG,QAAQ,IAAI,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC;AAC9C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,4BAA4B,CAAC,aAAuC;IACnF,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;QAC/B,IAAI,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,mBAAmB,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,mBAAmB,CAAC,EAAE,CAAC;YAC/F,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC;YAChC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC;QACrC,CAAC;IACF,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Reactivity — per-cohort Edge/Core policy (`docs/reactivity.md` §Per-cohort policy).
3
+ *
4
+ * Reactivity is **T3 (luxury)** at the cohort-topic layer. Two consequences are enforced here, the
5
+ * authoritative home for the reactivity producer-side policy:
6
+ *
7
+ * 1. **Edge nodes never serve as reactivity forwarders** — only as subscribers. A T3 *consumer* is fine
8
+ * on Edge (a phone wants notifications); a T3 *producer* is off in the Edge profile. The willingness
9
+ * check ({@link import("../cohort-topic/willingness.js")}) already declines T3 admission on an Edge
10
+ * node because `edgeProfile().willingTiers` excludes {@link Tier.T3}; this module adds the *reactivity*
11
+ * gate at the point a node decides whether to instantiate a {@link PushState} / become a forwarder, so
12
+ * the decision is explicit and testable rather than implicit in cohort admission.
13
+ * 2. **`delta_max` is Edge 0 / Core 4096** — the authoritative plumbing the origination ticket only
14
+ * *consumed*. Re-exported here from {@link import("./config.js")} so the policy surface is singular.
15
+ *
16
+ * The per-cohort topic budget (`topics_max`) bounds the collections a cohort serves; reactivity needs no
17
+ * admission policy beyond the cohort-topic default, so it is not re-implemented here.
18
+ */
19
+ import { type NodeProfile } from "../cohort-topic/tiers.js";
20
+ import { PushState, type PushStateInit } from "./push-state.js";
21
+ /**
22
+ * Whether a node with `profile` may serve as a reactivity **forwarder** (T3 producer). `true` only when
23
+ * the profile is willing to forward T3 — Core by default, an operator-narrowable Core, never Edge.
24
+ * Equivalent to `profile.willingTiers.has(Tier.T3)`: Edge's willing set is `{T0, T1}`, so this is `false`
25
+ * on every Edge node and on a Core node an operator has restricted away from T3.
26
+ */
27
+ export declare function mayServeAsReactivityForwarder(profile: NodeProfile): boolean;
28
+ /** A subscriber-only node (Edge, or a Core narrowed off T3) attempted to instantiate a reactivity forwarder. */
29
+ export declare class ReactivityForwarderForbiddenError extends Error {
30
+ readonly profileKind: NodeProfile["kind"];
31
+ constructor(profileKind: NodeProfile["kind"]);
32
+ }
33
+ /**
34
+ * Instantiate a forwarder {@link PushState} **only if** `profile` may forward T3. Returns `undefined` for a
35
+ * subscriber-only node (Edge, or a Core narrowed off T3) — the caller does not become a forwarder, leaving
36
+ * it a pure subscriber. This is the explicit gate the §Per-cohort policy "Edge nodes never serve as
37
+ * reactivity forwarders" rule names; a node consults it before allocating any per-collection push state.
38
+ */
39
+ export declare function instantiateForwarderPushState(profile: NodeProfile, init: PushStateInit): PushState | undefined;
40
+ /**
41
+ * Like {@link instantiateForwarderPushState} but **throws** {@link ReactivityForwarderForbiddenError} on a
42
+ * subscriber-only node, for call sites that treat an Edge forwarder attempt as a programming error rather
43
+ * than a silent no-op (e.g. a host wiring that should never route forwarder duty to an Edge node).
44
+ */
45
+ export declare function requireForwarderPushState(profile: NodeProfile, init: PushStateInit): PushState;
46
+ /**
47
+ * The effective reactivity producer policy for a node profile: whether it may forward, and its `delta_max`
48
+ * budget (Edge 0 — reject inbound `delta`, re-read the chain; Core 4096). The single struct a host reads to
49
+ * configure reactivity on a node (`docs/reactivity.md` §Per-cohort policy, §Delta payloads).
50
+ */
51
+ export interface ReactivityNodePolicy {
52
+ /** Whether this node may instantiate a forwarder / become a T3 producer. */
53
+ readonly mayForward: boolean;
54
+ /** `delta_max` (bytes): Core 4096, Edge 0 (declines deltas). */
55
+ readonly deltaMaxBytes: number;
56
+ }
57
+ /** Resolve the {@link ReactivityNodePolicy} for a profile (forwarder eligibility + `delta_max`). */
58
+ export declare function reactivityNodePolicy(profile: NodeProfile): ReactivityNodePolicy;
59
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../../src/reactivity/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAQ,KAAK,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhE;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAE3E;AAED,gHAAgH;AAChH,qBAAa,iCAAkC,SAAQ,KAAK;IAC/C,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC;gBAAhC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC;CAIrD;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,GAAG,SAAS,GAAG,SAAS,CAE9G;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,GAAG,SAAS,CAK9F;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACpC,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,gEAAgE;IAChE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAC/B;AAED,oGAAoG;AACpG,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,WAAW,GAAG,oBAAoB,CAE/E"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Reactivity — per-cohort Edge/Core policy (`docs/reactivity.md` §Per-cohort policy).
3
+ *
4
+ * Reactivity is **T3 (luxury)** at the cohort-topic layer. Two consequences are enforced here, the
5
+ * authoritative home for the reactivity producer-side policy:
6
+ *
7
+ * 1. **Edge nodes never serve as reactivity forwarders** — only as subscribers. A T3 *consumer* is fine
8
+ * on Edge (a phone wants notifications); a T3 *producer* is off in the Edge profile. The willingness
9
+ * check ({@link import("../cohort-topic/willingness.js")}) already declines T3 admission on an Edge
10
+ * node because `edgeProfile().willingTiers` excludes {@link Tier.T3}; this module adds the *reactivity*
11
+ * gate at the point a node decides whether to instantiate a {@link PushState} / become a forwarder, so
12
+ * the decision is explicit and testable rather than implicit in cohort admission.
13
+ * 2. **`delta_max` is Edge 0 / Core 4096** — the authoritative plumbing the origination ticket only
14
+ * *consumed*. Re-exported here from {@link import("./config.js")} so the policy surface is singular.
15
+ *
16
+ * The per-cohort topic budget (`topics_max`) bounds the collections a cohort serves; reactivity needs no
17
+ * admission policy beyond the cohort-topic default, so it is not re-implemented here.
18
+ */
19
+ import { Tier } from "../cohort-topic/tiers.js";
20
+ import { deltaMaxForProfile } from "./config.js";
21
+ import { PushState } from "./push-state.js";
22
+ /**
23
+ * Whether a node with `profile` may serve as a reactivity **forwarder** (T3 producer). `true` only when
24
+ * the profile is willing to forward T3 — Core by default, an operator-narrowable Core, never Edge.
25
+ * Equivalent to `profile.willingTiers.has(Tier.T3)`: Edge's willing set is `{T0, T1}`, so this is `false`
26
+ * on every Edge node and on a Core node an operator has restricted away from T3.
27
+ */
28
+ export function mayServeAsReactivityForwarder(profile) {
29
+ return profile.willingTiers.has(Tier.T3);
30
+ }
31
+ /** A subscriber-only node (Edge, or a Core narrowed off T3) attempted to instantiate a reactivity forwarder. */
32
+ export class ReactivityForwarderForbiddenError extends Error {
33
+ profileKind;
34
+ constructor(profileKind) {
35
+ super(`reactivity: a ${profileKind} node that does not forward T3 cannot instantiate a forwarder (subscriber-only)`);
36
+ this.profileKind = profileKind;
37
+ this.name = "ReactivityForwarderForbiddenError";
38
+ }
39
+ }
40
+ /**
41
+ * Instantiate a forwarder {@link PushState} **only if** `profile` may forward T3. Returns `undefined` for a
42
+ * subscriber-only node (Edge, or a Core narrowed off T3) — the caller does not become a forwarder, leaving
43
+ * it a pure subscriber. This is the explicit gate the §Per-cohort policy "Edge nodes never serve as
44
+ * reactivity forwarders" rule names; a node consults it before allocating any per-collection push state.
45
+ */
46
+ export function instantiateForwarderPushState(profile, init) {
47
+ return mayServeAsReactivityForwarder(profile) ? new PushState(init) : undefined;
48
+ }
49
+ /**
50
+ * Like {@link instantiateForwarderPushState} but **throws** {@link ReactivityForwarderForbiddenError} on a
51
+ * subscriber-only node, for call sites that treat an Edge forwarder attempt as a programming error rather
52
+ * than a silent no-op (e.g. a host wiring that should never route forwarder duty to an Edge node).
53
+ */
54
+ export function requireForwarderPushState(profile, init) {
55
+ if (!mayServeAsReactivityForwarder(profile)) {
56
+ throw new ReactivityForwarderForbiddenError(profile.kind);
57
+ }
58
+ return new PushState(init);
59
+ }
60
+ /** Resolve the {@link ReactivityNodePolicy} for a profile (forwarder eligibility + `delta_max`). */
61
+ export function reactivityNodePolicy(profile) {
62
+ return { mayForward: mayServeAsReactivityForwarder(profile), deltaMaxBytes: deltaMaxForProfile(profile) };
63
+ }
64
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../src/reactivity/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,IAAI,EAAoB,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,SAAS,EAAsB,MAAM,iBAAiB,CAAC;AAEhE;;;;;GAKG;AACH,MAAM,UAAU,6BAA6B,CAAC,OAAoB;IACjE,OAAO,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED,gHAAgH;AAChH,MAAM,OAAO,iCAAkC,SAAQ,KAAK;IACtC;IAArB,YAAqB,WAAgC;QACpD,KAAK,CAAC,iBAAiB,WAAW,iFAAiF,CAAC,CAAC;QADjG,gBAAW,GAAX,WAAW,CAAqB;QAEpD,IAAI,CAAC,IAAI,GAAG,mCAAmC,CAAC;IACjD,CAAC;CACD;AAED;;;;;GAKG;AACH,MAAM,UAAU,6BAA6B,CAAC,OAAoB,EAAE,IAAmB;IACtF,OAAO,6BAA6B,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACjF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAoB,EAAE,IAAmB;IAClF,IAAI,CAAC,6BAA6B,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,iCAAiC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAcD,oGAAoG;AACpG,MAAM,UAAU,oBAAoB,CAAC,OAAoB;IACxD,OAAO,EAAE,UAAU,EAAE,6BAA6B,CAAC,OAAO,CAAC,EAAE,aAAa,EAAE,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;AAC3G,CAAC"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Reactivity — forwarder-cohort `PushState` and its intra-cohort gossip codec
3
+ * (`docs/reactivity.md` §Forwarder-cohort state, §Replay window).
4
+ *
5
+ * A reactivity forwarder cohort holds the cohort-topic registration records (the direct-subscriber
6
+ * list **is** the `RegistrationRecord` set with `appPayload.kind == "reactivity"` — reactivity reads it,
7
+ * it does not duplicate it) plus this per-collection {@link PushState}: topic identity, parent/child
8
+ * cohort refs, the {@link ReplayBuffer} ring, the {@link DedupeWindow}, and `lastRevision`.
9
+ *
10
+ * `PushState` is gossiped within the cohort (over the cohort-topic gossip channel) so any member can
11
+ * serve a backfill/replay if the primary is unavailable. This module defines the **full** struct and its
12
+ * gossip codec so all three reactivity tickets share one definition. The rolling parent {@link checkpoint}
13
+ * (and its derived {@link parentCheckpoint} summary) is fed by replay-ring eviction
14
+ * ([reactivity-backfill-resume-checkpoints]); the `perSubscriberQueue` field carries the primary-local
15
+ * per-subscriber {@link SubscriberBackpressure} ([reactivity-rotation-backpressure-policy]). The
16
+ * backpressure map is **primary-local fan-out state, never gossiped** — it is absent from
17
+ * {@link PushStateGossipV1}; a handoff rebuilds it empty and the replay buffer covers the gap.
18
+ */
19
+ import { type SubscriberBackpressure, type SubscriberEnqueueResult } from "./backpressure.js";
20
+ import { RollingCheckpoint, type CheckpointSummary, type DeltaCoalesce, type DigestFold } from "./checkpoint.js";
21
+ import { type DedupeStateV1, type DedupeWindow } from "./dedupe.js";
22
+ import { type ReplayBuffer, type ReplayBufferStateV1 } from "./replay-buffer.js";
23
+ import { type NotificationV1 } from "./wire.js";
24
+ /** A reference to a parent/child cohort in the reactivity tree (`tier-(d∓1)`). */
25
+ export interface CohortRef {
26
+ /** The cohort's served ring coordinate, base64url. */
27
+ readonly coord: string;
28
+ /** The cohort's primary member id, base64url (when known). */
29
+ readonly primary?: string;
30
+ }
31
+ /** Construction inputs for a {@link PushState}. */
32
+ export interface PushStateInit {
33
+ /** Collection id, base64url. */
34
+ readonly collectionId: string;
35
+ /** Reactivity topic id, base64url. */
36
+ readonly topicId: string;
37
+ /** Tail block id when this cohort joined the topic, base64url. */
38
+ readonly tailIdAtJoin: string;
39
+ /** Tier-`(d−1)` parent cohort (absent at the tail/root). */
40
+ readonly parentCohort?: CohortRef;
41
+ /** Tier-`(d+1)` child cohorts. */
42
+ readonly childCohorts?: CohortRef[];
43
+ /** Replay buffer depth `W` (default {@link W_DEFAULT}). */
44
+ readonly w?: number;
45
+ /** Dedupe-window span (default {@link DEDUPE_WINDOW_DEFAULT}). */
46
+ readonly dedupeWindow?: number;
47
+ /** Parent-checkpoint span `W_checkpoint` (default {@link import("./config.js").W_CHECKPOINT_DEFAULT}). */
48
+ readonly wCheckpoint?: number;
49
+ /** Per-subscriber bounded-queue depth `queue_max` (default {@link QUEUE_MAX_DEFAULT}). */
50
+ readonly queueMax?: number;
51
+ /** `delta_max` for the checkpoint's coalesced `mergedDelta`; `0` (default) ⇒ never emit a delta. */
52
+ readonly deltaMaxBytes?: number;
53
+ /** `mergedDigest` fold override (default: deterministic running hash over per-revision commit digests). */
54
+ readonly checkpointFold?: DigestFold;
55
+ /** `mergedDelta` coalescer override (default: ordered concatenation). */
56
+ readonly coalesceDeltas?: DeltaCoalesce;
57
+ }
58
+ /** The cohort-gossiped wire image of a {@link PushState}. */
59
+ export interface PushStateGossipV1 {
60
+ v: 1;
61
+ collectionId: string;
62
+ topicId: string;
63
+ tailIdAtJoin: string;
64
+ parentCohort?: CohortRef;
65
+ childCohorts: CohortRef[];
66
+ lastRevision: number;
67
+ replayBuffer: ReplayBufferStateV1;
68
+ dedupe: DedupeStateV1;
69
+ }
70
+ /**
71
+ * Per-collection forwarder-cohort state. Owns the live {@link ReplayBuffer} and {@link DedupeWindow}
72
+ * components and the scalar identity/revision state; the reserved sibling-ticket fields hang off it.
73
+ */
74
+ export declare class PushState {
75
+ readonly collectionId: string;
76
+ readonly topicId: string;
77
+ tailIdAtJoin: string;
78
+ parentCohort?: CohortRef;
79
+ childCohorts: CohortRef[];
80
+ readonly replayBuffer: ReplayBuffer;
81
+ readonly dedupe: DedupeWindow;
82
+ lastRevision: number;
83
+ /**
84
+ * The rolling parent checkpoint ([reactivity-backfill-resume-checkpoints], `docs/reactivity.md`
85
+ * §Parent checkpoint summaries). Fed by replay-ring eviction — as revisions retire from the `W`-deep
86
+ * ring they roll into this `W_checkpoint`-span summary sitting immediately below the ring, so a resume
87
+ * recovers `W + W_checkpoint` revisions in one round trip (the stacked-window semantics).
88
+ */
89
+ readonly checkpoint: RollingCheckpoint;
90
+ /**
91
+ * The forwarder primary's per-subscriber drop-oldest backpressure map
92
+ * ([reactivity-rotation-backpressure-policy], `docs/reactivity.md` §Slow-subscriber backpressure).
93
+ * Primary-local fan-out state — **not** gossiped, so it is absent from {@link PushStateGossipV1}.
94
+ */
95
+ readonly perSubscriberQueue: SubscriberBackpressure;
96
+ /**
97
+ * The checkpoint migrated from the **outgoing** tail when this cohort became the new tail across a
98
+ * rotation ([reactivity-rotation-backpressure-policy], `docs/reactivity.md` §Tail rotation step 5). The
99
+ * new tail "holds the old checkpoint" so a `ResumeV1` whose span crosses the rotation is recoverable;
100
+ * `undefined` until a handoff lands via {@link adoptRotationCheckpoint}. Distinct from the live rolling
101
+ * {@link checkpoint}, which rolls from this cohort's *own* replay-ring eviction.
102
+ */
103
+ private inherited?;
104
+ constructor(init: PushStateInit);
105
+ /**
106
+ * Fan one notification onto each subscriber's bounded queue (the primary's per-subscriber backpressure,
107
+ * `docs/reactivity.md` §Slow-subscriber backpressure). Returns only the subscribers whose queue dropped
108
+ * its oldest under pressure — a slow subscriber's drop never stalls fan-out to the rest of the cohort.
109
+ */
110
+ enqueueForSubscribers(subscriberIds: Iterable<string>, n: NotificationV1): SubscriberEnqueueResult[];
111
+ /** The checkpoint inherited from the outgoing tail on rotation, or `undefined` until a handoff lands. */
112
+ get inheritedCheckpoint(): CheckpointSummary | undefined;
113
+ /**
114
+ * Land an outgoing tail's rotation handoff checkpoint on this (new tail) cohort
115
+ * (`docs/reactivity.md` §Tail rotation step 5). Idempotent: a later handoff replaces only if it covers a
116
+ * higher revision, so a re-delivered or older handoff never rewinds the inherited span.
117
+ */
118
+ adoptRotationCheckpoint(summary: CheckpointSummary): void;
119
+ /**
120
+ * The current parent-checkpoint summary, or `undefined` until revisions have retired from the replay
121
+ * ring (`docs/reactivity.md` §Parent checkpoint summaries). Re-derived from the live rolling checkpoint
122
+ * on each read so it always reflects the latest retired range.
123
+ */
124
+ get parentCheckpoint(): CheckpointSummary | undefined;
125
+ /** Snapshot the gossipable slice for cohort replication. */
126
+ serializeGossip(): PushStateGossipV1;
127
+ /** Merge a peer member's gossiped state so the whole cohort converges (any member can then serve). */
128
+ mergeGossip(g: PushStateGossipV1): void;
129
+ }
130
+ /** Narrow an already-parsed value to {@link PushStateGossipV1}, throwing on any defect. */
131
+ export declare function validatePushStateGossipV1(value: unknown): PushStateGossipV1;
132
+ /** Encode a {@link PushStateGossipV1} as a length-prefixed UTF-8 JSON frame. */
133
+ export declare function encodePushStateGossipV1(msg: PushStateGossipV1, maxMessageBytes?: number): Uint8Array;
134
+ /** Decode a length-prefixed {@link PushStateGossipV1} frame. */
135
+ export declare function decodePushStateGossipV1(bytes: Uint8Array, maxMessageBytes?: number): PushStateGossipV1;
136
+ /** Round-trip helper for `bytesToB64url` consumers wanting the raw coord bytes of a {@link CohortRef}. */
137
+ export declare function cohortRefCoordBytes(ref: CohortRef): Uint8Array;
138
+ /** Build a {@link CohortRef} from raw coord (and optional primary) bytes. */
139
+ export declare function makeCohortRef(coord: Uint8Array, primary?: Uint8Array): CohortRef;
140
+ //# sourceMappingURL=push-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push-state.d.ts","sourceRoot":"","sources":["../../../src/reactivity/push-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,EAAgC,KAAK,sBAAsB,EAAE,KAAK,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5H,OAAO,EAAE,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACjH,OAAO,EAAsB,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,EAAsB,KAAK,YAAY,EAAE,KAAK,mBAAmB,EAAsB,MAAM,oBAAoB,CAAC;AACzH,OAAO,EAA0B,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAExE,kFAAkF;AAClF,MAAM,WAAW,SAAS;IACzB,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,mDAAmD;AACnD,MAAM,WAAW,aAAa;IAC7B,gCAAgC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,sCAAsC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,4DAA4D;IAC5D,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,CAAC;IAClC,kCAAkC;IAClC,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC;IACpC,2DAA2D;IAC3D,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,0GAA0G;IAC1G,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,0FAA0F;IAC1F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,oGAAoG;IACpG,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,2GAA2G;IAC3G,QAAQ,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC;IACrC,yEAAyE;IACzE,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC;CACxC;AAED,6DAA6D;AAC7D,MAAM,WAAW,iBAAiB;IACjC,CAAC,EAAE,CAAC,CAAC;IACL,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,YAAY,EAAE,SAAS,EAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,mBAAmB,CAAC;IAClC,MAAM,EAAE,aAAa,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,SAAS;IACrB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,YAAY,EAAE,SAAS,EAAE,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IAErB;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,EAAE,sBAAsB,CAAC;IACpD;;;;;;OAMG;IACH,OAAO,CAAC,SAAS,CAAC,CAAoB;gBAE1B,IAAI,EAAE,aAAa;IAqB/B;;;;OAIG;IACH,qBAAqB,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,cAAc,GAAG,uBAAuB,EAAE;IAIpG,yGAAyG;IACzG,IAAI,mBAAmB,IAAI,iBAAiB,GAAG,SAAS,CAEvD;IAED;;;;OAIG;IACH,uBAAuB,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAMzD;;;;OAIG;IACH,IAAI,gBAAgB,IAAI,iBAAiB,GAAG,SAAS,CAEpD;IAED,4DAA4D;IAC5D,eAAe,IAAI,iBAAiB;IAiBpC,sGAAsG;IACtG,WAAW,CAAC,CAAC,EAAE,iBAAiB,GAAG,IAAI;CAgBvC;AA4FD,2FAA2F;AAC3F,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,CAwB3E;AAED,gFAAgF;AAChF,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,iBAAiB,EAAE,eAAe,GAAE,MAAkC,GAAG,UAAU,CAE/H;AAED,gEAAgE;AAChE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,UAAU,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAEtG;AAED,0GAA0G;AAC1G,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,SAAS,GAAG,UAAU,CAE9D;AAED,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,SAAS,CAIhF"}
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Reactivity — forwarder-cohort `PushState` and its intra-cohort gossip codec
3
+ * (`docs/reactivity.md` §Forwarder-cohort state, §Replay window).
4
+ *
5
+ * A reactivity forwarder cohort holds the cohort-topic registration records (the direct-subscriber
6
+ * list **is** the `RegistrationRecord` set with `appPayload.kind == "reactivity"` — reactivity reads it,
7
+ * it does not duplicate it) plus this per-collection {@link PushState}: topic identity, parent/child
8
+ * cohort refs, the {@link ReplayBuffer} ring, the {@link DedupeWindow}, and `lastRevision`.
9
+ *
10
+ * `PushState` is gossiped within the cohort (over the cohort-topic gossip channel) so any member can
11
+ * serve a backfill/replay if the primary is unavailable. This module defines the **full** struct and its
12
+ * gossip codec so all three reactivity tickets share one definition. The rolling parent {@link checkpoint}
13
+ * (and its derived {@link parentCheckpoint} summary) is fed by replay-ring eviction
14
+ * ([reactivity-backfill-resume-checkpoints]); the `perSubscriberQueue` field carries the primary-local
15
+ * per-subscriber {@link SubscriberBackpressure} ([reactivity-rotation-backpressure-policy]). The
16
+ * backpressure map is **primary-local fan-out state, never gossiped** — it is absent from
17
+ * {@link PushStateGossipV1}; a handoff rebuilds it empty and the replay buffer covers the gap.
18
+ */
19
+ import { bytesToB64url, b64urlToBytes, decodeCohortMessage, encodeCohortMessage, DEFAULT_MAX_MESSAGE_BYTES } from "../cohort-topic/wire/codec.js";
20
+ import { CohortWireError } from "../cohort-topic/wire/validate.js";
21
+ import { DEDUPE_WINDOW_DEFAULT, QUEUE_MAX_DEFAULT, W_DEFAULT } from "./config.js";
22
+ import { createSubscriberBackpressure } from "./backpressure.js";
23
+ import { RollingCheckpoint } from "./checkpoint.js";
24
+ import { createDedupeWindow } from "./dedupe.js";
25
+ import { createReplayBuffer } from "./replay-buffer.js";
26
+ import { validateNotificationV1 } from "./wire.js";
27
+ /**
28
+ * Per-collection forwarder-cohort state. Owns the live {@link ReplayBuffer} and {@link DedupeWindow}
29
+ * components and the scalar identity/revision state; the reserved sibling-ticket fields hang off it.
30
+ */
31
+ export class PushState {
32
+ collectionId;
33
+ topicId;
34
+ tailIdAtJoin;
35
+ parentCohort;
36
+ childCohorts;
37
+ replayBuffer;
38
+ dedupe;
39
+ lastRevision;
40
+ /**
41
+ * The rolling parent checkpoint ([reactivity-backfill-resume-checkpoints], `docs/reactivity.md`
42
+ * §Parent checkpoint summaries). Fed by replay-ring eviction — as revisions retire from the `W`-deep
43
+ * ring they roll into this `W_checkpoint`-span summary sitting immediately below the ring, so a resume
44
+ * recovers `W + W_checkpoint` revisions in one round trip (the stacked-window semantics).
45
+ */
46
+ checkpoint;
47
+ /**
48
+ * The forwarder primary's per-subscriber drop-oldest backpressure map
49
+ * ([reactivity-rotation-backpressure-policy], `docs/reactivity.md` §Slow-subscriber backpressure).
50
+ * Primary-local fan-out state — **not** gossiped, so it is absent from {@link PushStateGossipV1}.
51
+ */
52
+ perSubscriberQueue;
53
+ /**
54
+ * The checkpoint migrated from the **outgoing** tail when this cohort became the new tail across a
55
+ * rotation ([reactivity-rotation-backpressure-policy], `docs/reactivity.md` §Tail rotation step 5). The
56
+ * new tail "holds the old checkpoint" so a `ResumeV1` whose span crosses the rotation is recoverable;
57
+ * `undefined` until a handoff lands via {@link adoptRotationCheckpoint}. Distinct from the live rolling
58
+ * {@link checkpoint}, which rolls from this cohort's *own* replay-ring eviction.
59
+ */
60
+ inherited;
61
+ constructor(init) {
62
+ this.collectionId = init.collectionId;
63
+ this.topicId = init.topicId;
64
+ this.tailIdAtJoin = init.tailIdAtJoin;
65
+ this.parentCohort = init.parentCohort;
66
+ this.childCohorts = init.childCohorts ?? [];
67
+ this.checkpoint = new RollingCheckpoint({
68
+ collectionId: init.collectionId,
69
+ span: init.wCheckpoint,
70
+ deltaMaxBytes: init.deltaMaxBytes,
71
+ fold: init.checkpointFold,
72
+ coalesceDeltas: init.coalesceDeltas,
73
+ });
74
+ // Wire the ring's low-edge eviction into the rolling checkpoint: a revision leaving the replay
75
+ // window is exactly a revision that should roll into the parent-checkpoint summary.
76
+ this.replayBuffer = createReplayBuffer(init.w ?? W_DEFAULT, (evicted) => this.checkpoint.retire(evicted));
77
+ this.dedupe = createDedupeWindow(init.dedupeWindow ?? DEDUPE_WINDOW_DEFAULT);
78
+ this.perSubscriberQueue = createSubscriberBackpressure(init.queueMax ?? QUEUE_MAX_DEFAULT);
79
+ this.lastRevision = -1;
80
+ }
81
+ /**
82
+ * Fan one notification onto each subscriber's bounded queue (the primary's per-subscriber backpressure,
83
+ * `docs/reactivity.md` §Slow-subscriber backpressure). Returns only the subscribers whose queue dropped
84
+ * its oldest under pressure — a slow subscriber's drop never stalls fan-out to the rest of the cohort.
85
+ */
86
+ enqueueForSubscribers(subscriberIds, n) {
87
+ return this.perSubscriberQueue.fanOut(subscriberIds, n);
88
+ }
89
+ /** The checkpoint inherited from the outgoing tail on rotation, or `undefined` until a handoff lands. */
90
+ get inheritedCheckpoint() {
91
+ return this.inherited;
92
+ }
93
+ /**
94
+ * Land an outgoing tail's rotation handoff checkpoint on this (new tail) cohort
95
+ * (`docs/reactivity.md` §Tail rotation step 5). Idempotent: a later handoff replaces only if it covers a
96
+ * higher revision, so a re-delivered or older handoff never rewinds the inherited span.
97
+ */
98
+ adoptRotationCheckpoint(summary) {
99
+ if (this.inherited === undefined || summary.toRevision > this.inherited.toRevision) {
100
+ this.inherited = summary;
101
+ }
102
+ }
103
+ /**
104
+ * The current parent-checkpoint summary, or `undefined` until revisions have retired from the replay
105
+ * ring (`docs/reactivity.md` §Parent checkpoint summaries). Re-derived from the live rolling checkpoint
106
+ * on each read so it always reflects the latest retired range.
107
+ */
108
+ get parentCheckpoint() {
109
+ return this.checkpoint.summary();
110
+ }
111
+ /** Snapshot the gossipable slice for cohort replication. */
112
+ serializeGossip() {
113
+ const out = {
114
+ v: 1,
115
+ collectionId: this.collectionId,
116
+ topicId: this.topicId,
117
+ tailIdAtJoin: this.tailIdAtJoin,
118
+ childCohorts: this.childCohorts,
119
+ lastRevision: this.lastRevision,
120
+ replayBuffer: this.replayBuffer.serialize(),
121
+ dedupe: this.dedupe.serialize(),
122
+ };
123
+ if (this.parentCohort !== undefined) {
124
+ out.parentCohort = this.parentCohort;
125
+ }
126
+ return out;
127
+ }
128
+ /** Merge a peer member's gossiped state so the whole cohort converges (any member can then serve). */
129
+ mergeGossip(g) {
130
+ if (g.collectionId !== this.collectionId || g.topicId !== this.topicId) {
131
+ return; // gossip for a different collection/topic is not ours to merge
132
+ }
133
+ this.replayBuffer.merge(g.replayBuffer);
134
+ this.dedupe.merge(g.dedupe);
135
+ if (g.lastRevision > this.lastRevision) {
136
+ this.lastRevision = g.lastRevision;
137
+ }
138
+ if (g.childCohorts.length > this.childCohorts.length) {
139
+ this.childCohorts = g.childCohorts;
140
+ }
141
+ if (this.parentCohort === undefined && g.parentCohort !== undefined) {
142
+ this.parentCohort = g.parentCohort;
143
+ }
144
+ }
145
+ }
146
+ // --- gossip codec (length-framed JSON, mirroring cohort-topic conventions) ---
147
+ function fail(message) {
148
+ throw new CohortWireError(message);
149
+ }
150
+ function asObject(value, what) {
151
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
152
+ fail(`${what}: expected an object`);
153
+ }
154
+ return value;
155
+ }
156
+ function reqString(obj, key, what) {
157
+ const value = obj[key];
158
+ if (typeof value !== "string") {
159
+ fail(`${what}: field "${key}" must be a string`);
160
+ }
161
+ return value;
162
+ }
163
+ function reqFiniteNumber(obj, key, what) {
164
+ const value = obj[key];
165
+ if (typeof value !== "number" || !Number.isFinite(value)) {
166
+ fail(`${what}: field "${key}" must be a finite number`);
167
+ }
168
+ return value;
169
+ }
170
+ function reqInt(value, key, what) {
171
+ if (!Number.isInteger(value)) {
172
+ fail(`${what}: field "${key}" must be an integer, got ${value}`);
173
+ }
174
+ return value;
175
+ }
176
+ function b64urlField(value, key, what) {
177
+ try {
178
+ b64urlToBytes(value);
179
+ }
180
+ catch {
181
+ fail(`${what}: field "${key}" is not valid base64url`);
182
+ }
183
+ return value;
184
+ }
185
+ function validateCohortRef(value, what) {
186
+ const obj = asObject(value, what);
187
+ const coord = b64urlField(reqString(obj, "coord", what), "coord", what);
188
+ if (obj["primary"] !== undefined) {
189
+ return { coord, primary: b64urlField(reqString(obj, "primary", what), "primary", what) };
190
+ }
191
+ return { coord };
192
+ }
193
+ function validateRevisionEntry(value, what) {
194
+ const obj = asObject(value, what);
195
+ return {
196
+ revision: reqInt(reqFiniteNumber(obj, "revision", what), "revision", what),
197
+ payload: validateNotificationV1(obj["payload"]),
198
+ receivedAt: reqFiniteNumber(obj, "receivedAt", what),
199
+ };
200
+ }
201
+ function validateReplayBufferState(value, what) {
202
+ const obj = asObject(value, what);
203
+ const entries = obj["entries"];
204
+ if (!Array.isArray(entries)) {
205
+ fail(`${what}: field "entries" must be an array`);
206
+ }
207
+ return {
208
+ capacity: reqInt(reqFiniteNumber(obj, "capacity", what), "capacity", what),
209
+ entries: entries.map((e) => validateRevisionEntry(e, `${what}.entry`)),
210
+ };
211
+ }
212
+ function validateDedupeState(value, what) {
213
+ const obj = asObject(value, what);
214
+ const entries = obj["entries"];
215
+ if (!Array.isArray(entries)) {
216
+ fail(`${what}: field "entries" must be an array`);
217
+ }
218
+ return {
219
+ highestRevision: reqInt(reqFiniteNumber(obj, "highestRevision", what), "highestRevision", what),
220
+ entries: entries.map((e) => {
221
+ const eo = asObject(e, `${what}.entry`);
222
+ return { key: reqString(eo, "key", `${what}.entry`), revision: reqInt(reqFiniteNumber(eo, "revision", `${what}.entry`), "revision", `${what}.entry`) };
223
+ }),
224
+ };
225
+ }
226
+ /** Narrow an already-parsed value to {@link PushStateGossipV1}, throwing on any defect. */
227
+ export function validatePushStateGossipV1(value) {
228
+ const what = "PushStateGossipV1";
229
+ const obj = asObject(value, what);
230
+ if (obj["v"] !== 1) {
231
+ fail(`${what}: expected v === 1, got ${JSON.stringify(obj["v"])}`);
232
+ }
233
+ const childCohorts = obj["childCohorts"];
234
+ if (!Array.isArray(childCohorts)) {
235
+ fail(`${what}: field "childCohorts" must be an array`);
236
+ }
237
+ const out = {
238
+ v: 1,
239
+ collectionId: b64urlField(reqString(obj, "collectionId", what), "collectionId", what),
240
+ topicId: b64urlField(reqString(obj, "topicId", what), "topicId", what),
241
+ tailIdAtJoin: b64urlField(reqString(obj, "tailIdAtJoin", what), "tailIdAtJoin", what),
242
+ childCohorts: childCohorts.map((c) => validateCohortRef(c, `${what}.childCohort`)),
243
+ lastRevision: reqInt(reqFiniteNumber(obj, "lastRevision", what), "lastRevision", what),
244
+ replayBuffer: validateReplayBufferState(obj["replayBuffer"], `${what}.replayBuffer`),
245
+ dedupe: validateDedupeState(obj["dedupe"], `${what}.dedupe`),
246
+ };
247
+ if (obj["parentCohort"] !== undefined) {
248
+ out.parentCohort = validateCohortRef(obj["parentCohort"], `${what}.parentCohort`);
249
+ }
250
+ return out;
251
+ }
252
+ /** Encode a {@link PushStateGossipV1} as a length-prefixed UTF-8 JSON frame. */
253
+ export function encodePushStateGossipV1(msg, maxMessageBytes = DEFAULT_MAX_MESSAGE_BYTES) {
254
+ return encodeCohortMessage(validatePushStateGossipV1(msg), maxMessageBytes);
255
+ }
256
+ /** Decode a length-prefixed {@link PushStateGossipV1} frame. */
257
+ export function decodePushStateGossipV1(bytes, maxMessageBytes) {
258
+ return validatePushStateGossipV1(decodeCohortMessage(bytes, maxMessageBytes));
259
+ }
260
+ /** Round-trip helper for `bytesToB64url` consumers wanting the raw coord bytes of a {@link CohortRef}. */
261
+ export function cohortRefCoordBytes(ref) {
262
+ return b64urlToBytes(ref.coord);
263
+ }
264
+ /** Build a {@link CohortRef} from raw coord (and optional primary) bytes. */
265
+ export function makeCohortRef(coord, primary) {
266
+ return primary !== undefined
267
+ ? { coord: bytesToB64url(coord), primary: bytesToB64url(primary) }
268
+ : { coord: bytesToB64url(coord) };
269
+ }
270
+ //# sourceMappingURL=push-state.js.map