@optimystic/db-core 0.13.4 → 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 +29 -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 +19 -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 +44 -2
  353. package/dist/src/transactor/network-transactor.d.ts.map +1 -1
  354. package/dist/src/transactor/network-transactor.js +132 -17
  355. package/dist/src/transactor/network-transactor.js.map +1 -1
  356. package/package.json +3 -2
  357. package/src/cluster/structs.ts +86 -57
  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 +19 -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 +162 -19
@@ -0,0 +1,478 @@
1
+ /**
2
+ * Cohort-topic substrate — per-message structural validation.
3
+ *
4
+ * Each validator narrows an already-parsed `unknown` (the output of `JSON.parse` on a decoded
5
+ * frame) to a concrete V1 interface, throwing {@link CohortWireError} on any structural defect:
6
+ * missing required field, wrong `v`, out-of-enum discriminant, a byte field that does not decode
7
+ * as base64url, or an out-of-range numeric. Validators never return partial data — they either
8
+ * narrow cleanly or throw.
9
+ */
10
+
11
+ import { b64urlToBytes } from "./codec.js";
12
+ import type {
13
+ CohortGossipV1,
14
+ CohortTopicSummary,
15
+ DemotionNoticeV1,
16
+ GossipRecordRefV1,
17
+ GossipRecordV1,
18
+ MembershipCertV1,
19
+ PromotionNoticeV1,
20
+ RegisterReplyV1,
21
+ RegisterResult,
22
+ RegisterV1,
23
+ RenewReplyV1,
24
+ RenewV1,
25
+ SignKind,
26
+ SignRequestV1,
27
+ SignReplyV1,
28
+ TopicTrafficV1,
29
+ } from "./types.js";
30
+
31
+ /** Thrown for any malformed, oversized, or structurally invalid cohort-topic frame. */
32
+ export class CohortWireError extends Error {
33
+ constructor(message: string) {
34
+ super(message);
35
+ this.name = "CohortWireError";
36
+ }
37
+ }
38
+
39
+ function fail(message: string): never {
40
+ throw new CohortWireError(message);
41
+ }
42
+
43
+ /** Set `obj[key]` only when `value` is defined — keeps absent optionals off the decoded object. */
44
+ function assignDefined<T extends object, K extends keyof T>(obj: T, key: K, value: T[K] | undefined): void {
45
+ if (value !== undefined) {
46
+ obj[key] = value;
47
+ }
48
+ }
49
+
50
+ function asObject(value: unknown, what: string): Record<string, unknown> {
51
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
52
+ fail(`${what}: expected an object`);
53
+ }
54
+ return value as Record<string, unknown>;
55
+ }
56
+
57
+ function requireV1(obj: Record<string, unknown>, what: string): void {
58
+ if (obj["v"] !== 1) {
59
+ fail(`${what}: expected v === 1, got ${JSON.stringify(obj["v"])}`);
60
+ }
61
+ }
62
+
63
+ function reqString(obj: Record<string, unknown>, key: string, what: string): string {
64
+ const value = obj[key];
65
+ if (typeof value !== "string") {
66
+ fail(`${what}: field "${key}" must be a string`);
67
+ }
68
+ return value;
69
+ }
70
+
71
+ function optString(obj: Record<string, unknown>, key: string, what: string): string | undefined {
72
+ const value = obj[key];
73
+ if (value === undefined) {
74
+ return undefined;
75
+ }
76
+ if (typeof value !== "string") {
77
+ fail(`${what}: field "${key}" must be a string when present`);
78
+ }
79
+ return value;
80
+ }
81
+
82
+ function reqFiniteNumber(obj: Record<string, unknown>, key: string, what: string): number {
83
+ const value = obj[key];
84
+ if (typeof value !== "number" || !Number.isFinite(value)) {
85
+ fail(`${what}: field "${key}" must be a finite number`);
86
+ }
87
+ return value;
88
+ }
89
+
90
+ function optFiniteNumber(obj: Record<string, unknown>, key: string, what: string): number | undefined {
91
+ const value = obj[key];
92
+ if (value === undefined) {
93
+ return undefined;
94
+ }
95
+ if (typeof value !== "number" || !Number.isFinite(value)) {
96
+ fail(`${what}: field "${key}" must be a finite number when present`);
97
+ }
98
+ return value;
99
+ }
100
+
101
+ function reqBool(obj: Record<string, unknown>, key: string, what: string): boolean {
102
+ const value = obj[key];
103
+ if (typeof value !== "boolean") {
104
+ fail(`${what}: field "${key}" must be a boolean`);
105
+ }
106
+ return value;
107
+ }
108
+
109
+ function optBool(obj: Record<string, unknown>, key: string, what: string): boolean | undefined {
110
+ const value = obj[key];
111
+ if (value === undefined) {
112
+ return undefined;
113
+ }
114
+ if (typeof value !== "boolean") {
115
+ fail(`${what}: field "${key}" must be a boolean when present`);
116
+ }
117
+ return value;
118
+ }
119
+
120
+ function reqStringArray(obj: Record<string, unknown>, key: string, what: string): string[] {
121
+ const value = obj[key];
122
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
123
+ fail(`${what}: field "${key}" must be an array of strings`);
124
+ }
125
+ return value as string[];
126
+ }
127
+
128
+ function optStringArray(obj: Record<string, unknown>, key: string, what: string): string[] | undefined {
129
+ if (obj[key] === undefined) {
130
+ return undefined;
131
+ }
132
+ return reqStringArray(obj, key, what);
133
+ }
134
+
135
+ function tier(value: number, what: string): number {
136
+ if (!Number.isInteger(value) || value < 0 || value > 3) {
137
+ fail(`${what}: tier must be an integer in 0..3, got ${value}`);
138
+ }
139
+ return value;
140
+ }
141
+
142
+ /** Assert a base64url string decodes cleanly; returns it unchanged. */
143
+ function b64urlField(value: string, key: string, what: string): string {
144
+ try {
145
+ b64urlToBytes(value);
146
+ } catch {
147
+ fail(`${what}: field "${key}" is not valid base64url`);
148
+ }
149
+ return value;
150
+ }
151
+
152
+ function reqEnum<T extends string>(obj: Record<string, unknown>, key: string, allowed: readonly T[], what: string): T {
153
+ const value = obj[key];
154
+ if (typeof value !== "string" || !(allowed as readonly string[]).includes(value)) {
155
+ fail(`${what}: field "${key}" must be one of ${allowed.join(" | ")}`);
156
+ }
157
+ return value as T;
158
+ }
159
+
160
+ const REGISTER_RESULTS: readonly RegisterResult[] = [
161
+ "accepted",
162
+ "no_state",
163
+ "promoted",
164
+ "unwilling_member",
165
+ "unwilling_cohort",
166
+ ];
167
+
168
+ export function validateRegisterV1(value: unknown): RegisterV1 {
169
+ const what = "RegisterV1";
170
+ const obj = asObject(value, what);
171
+ requireV1(obj, what);
172
+ const out: RegisterV1 = {
173
+ v: 1,
174
+ topicId: b64urlField(reqString(obj, "topicId", what), "topicId", what),
175
+ tier: tier(reqFiniteNumber(obj, "tier", what), what),
176
+ treeTier: reqFiniteNumber(obj, "treeTier", what),
177
+ participantCoord: b64urlField(reqString(obj, "participantCoord", what), "participantCoord", what),
178
+ ttl: reqFiniteNumber(obj, "ttl", what),
179
+ timestamp: reqFiniteNumber(obj, "timestamp", what),
180
+ correlationId: b64urlField(reqString(obj, "correlationId", what), "correlationId", what),
181
+ signature: b64urlField(reqString(obj, "signature", what), "signature", what),
182
+ };
183
+ const bootstrap = optBool(obj, "bootstrap", what);
184
+ if (bootstrap !== undefined) {
185
+ out.bootstrap = bootstrap;
186
+ }
187
+ const probe = optBool(obj, "probe", what);
188
+ if (probe !== undefined) {
189
+ out.probe = probe;
190
+ }
191
+ const appPayload = optString(obj, "appPayload", what);
192
+ if (appPayload !== undefined) {
193
+ out.appPayload = b64urlField(appPayload, "appPayload", what);
194
+ }
195
+ // Bootstrap-evidence envelope (base64url; present only on a `bootstrap: true` register). A non-string
196
+ // is rejected as malformed; an empty string is treated as absent so it normalizes to the same signed
197
+ // image placeholder as a missing field (see `registerSigningPayload`'s `normalizeEvidence`).
198
+ const bootstrapEvidence = optString(obj, "bootstrapEvidence", what);
199
+ if (bootstrapEvidence !== undefined && bootstrapEvidence !== "") {
200
+ out.bootstrapEvidence = b64urlField(bootstrapEvidence, "bootstrapEvidence", what);
201
+ }
202
+ return out;
203
+ }
204
+
205
+ function validateTopicTrafficV1(value: unknown): TopicTrafficV1 {
206
+ const what = "TopicTrafficV1";
207
+ const obj = asObject(value, what);
208
+ return {
209
+ windowSeconds: reqFiniteNumber(obj, "windowSeconds", what),
210
+ arrivalsPerMin: reqFiniteNumber(obj, "arrivalsPerMin", what),
211
+ queriesPerMin: reqFiniteNumber(obj, "queriesPerMin", what),
212
+ directParticipants: reqFiniteNumber(obj, "directParticipants", what),
213
+ childCohortCount: reqFiniteNumber(obj, "childCohortCount", what),
214
+ };
215
+ }
216
+
217
+ export function validateRegisterReplyV1(value: unknown): RegisterReplyV1 {
218
+ const what = "RegisterReplyV1";
219
+ const obj = asObject(value, what);
220
+ requireV1(obj, what);
221
+ const out: RegisterReplyV1 = {
222
+ v: 1,
223
+ result: reqEnum(obj, "result", REGISTER_RESULTS, what),
224
+ };
225
+ assignDefined(out, "primary", optString(obj, "primary", what));
226
+ assignDefined(out, "backups", optStringArray(obj, "backups", what));
227
+ const cohortEpoch = optString(obj, "cohortEpoch", what);
228
+ if (cohortEpoch !== undefined) {
229
+ out.cohortEpoch = b64urlField(cohortEpoch, "cohortEpoch", what);
230
+ }
231
+ assignDefined(out, "cohortMembers", optStringArray(obj, "cohortMembers", what));
232
+ if (obj["topicTraffic"] !== undefined) {
233
+ out.topicTraffic = validateTopicTrafficV1(obj["topicTraffic"]);
234
+ }
235
+ assignDefined(out, "targetTier", optFiniteNumber(obj, "targetTier", what));
236
+ assignDefined(out, "candidateMembers", optStringArray(obj, "candidateMembers", what));
237
+ assignDefined(out, "retryAfterMs", optFiniteNumber(obj, "retryAfterMs", what));
238
+ assignDefined(out, "reason", optString(obj, "reason", what));
239
+ return out;
240
+ }
241
+
242
+ export function validateRenewV1(value: unknown): RenewV1 {
243
+ const what = "RenewV1";
244
+ const obj = asObject(value, what);
245
+ requireV1(obj, what);
246
+ const out: RenewV1 = {
247
+ v: 1,
248
+ topicId: b64urlField(reqString(obj, "topicId", what), "topicId", what),
249
+ participantId: reqString(obj, "participantId", what),
250
+ correlationId: b64urlField(reqString(obj, "correlationId", what), "correlationId", what),
251
+ timestamp: reqFiniteNumber(obj, "timestamp", what),
252
+ signature: b64urlField(reqString(obj, "signature", what), "signature", what),
253
+ };
254
+ assignDefined(out, "reattach", optBool(obj, "reattach", what));
255
+ assignDefined(out, "withdraw", optBool(obj, "withdraw", what));
256
+ return out;
257
+ }
258
+
259
+ export function validateRenewReplyV1(value: unknown): RenewReplyV1 {
260
+ const what = "RenewReplyV1";
261
+ const obj = asObject(value, what);
262
+ requireV1(obj, what);
263
+ const out: RenewReplyV1 = {
264
+ v: 1,
265
+ result: reqEnum(obj, "result", ["ok", "unknown_registration", "primary_moved", "withdrawn"] as const, what),
266
+ };
267
+ assignDefined(out, "newPrimary", optString(obj, "newPrimary", what));
268
+ assignDefined(out, "newBackups", optStringArray(obj, "newBackups", what));
269
+ const cohortEpoch = optString(obj, "cohortEpoch", what);
270
+ if (cohortEpoch !== undefined) {
271
+ out.cohortEpoch = b64urlField(cohortEpoch, "cohortEpoch", what);
272
+ }
273
+ return out;
274
+ }
275
+
276
+ export function validatePromotionNoticeV1(value: unknown): PromotionNoticeV1 {
277
+ const what = "PromotionNoticeV1";
278
+ const obj = asObject(value, what);
279
+ requireV1(obj, what);
280
+ return {
281
+ v: 1,
282
+ topicId: b64urlField(reqString(obj, "topicId", what), "topicId", what),
283
+ fromTier: tier(reqFiniteNumber(obj, "fromTier", what), what),
284
+ toTier: tier(reqFiniteNumber(obj, "toTier", what), what),
285
+ effectiveAt: reqFiniteNumber(obj, "effectiveAt", what),
286
+ thresholdSig: b64urlField(reqString(obj, "thresholdSig", what), "thresholdSig", what),
287
+ signers: reqStringArray(obj, "signers", what),
288
+ cohortEpoch: b64urlField(reqString(obj, "cohortEpoch", what), "cohortEpoch", what),
289
+ };
290
+ }
291
+
292
+ export function validateDemotionNoticeV1(value: unknown): DemotionNoticeV1 {
293
+ const what = "DemotionNoticeV1";
294
+ const obj = asObject(value, what);
295
+ requireV1(obj, what);
296
+ return {
297
+ v: 1,
298
+ topicId: b64urlField(reqString(obj, "topicId", what), "topicId", what),
299
+ tier: tier(reqFiniteNumber(obj, "tier", what), what),
300
+ parentCohortCoord: b64urlField(reqString(obj, "parentCohortCoord", what), "parentCohortCoord", what),
301
+ effectiveAt: reqFiniteNumber(obj, "effectiveAt", what),
302
+ thresholdSig: b64urlField(reqString(obj, "thresholdSig", what), "thresholdSig", what),
303
+ signers: reqStringArray(obj, "signers", what),
304
+ cohortEpoch: b64urlField(reqString(obj, "cohortEpoch", what), "cohortEpoch", what),
305
+ };
306
+ }
307
+
308
+ function validateCohortTopicSummary(value: unknown): CohortTopicSummary {
309
+ const what = "CohortTopicSummary";
310
+ const obj = asObject(value, what);
311
+ return {
312
+ topicId: b64urlField(reqString(obj, "topicId", what), "topicId", what),
313
+ tier: tier(reqFiniteNumber(obj, "tier", what), what),
314
+ directParticipants: reqFiniteNumber(obj, "directParticipants", what),
315
+ arrivalsPerMin: reqFiniteNumber(obj, "arrivalsPerMin", what),
316
+ queriesPerMin: reqFiniteNumber(obj, "queriesPerMin", what),
317
+ promoted: reqBool(obj, "promoted", what),
318
+ childCohortCount: reqFiniteNumber(obj, "childCohortCount", what),
319
+ };
320
+ }
321
+
322
+ function validateGossipRecordV1(value: unknown): GossipRecordV1 {
323
+ const what = "GossipRecordV1";
324
+ const obj = asObject(value, what);
325
+ const out: GossipRecordV1 = {
326
+ topicId: b64urlField(reqString(obj, "topicId", what), "topicId", what),
327
+ participantId: b64urlField(reqString(obj, "participantId", what), "participantId", what),
328
+ tier: tier(reqFiniteNumber(obj, "tier", what), what),
329
+ primary: b64urlField(reqString(obj, "primary", what), "primary", what),
330
+ backups: reqStringArray(obj, "backups", what).map((b) => b64urlField(b, "backups", what)),
331
+ attachedAt: reqFiniteNumber(obj, "attachedAt", what),
332
+ lastPing: reqFiniteNumber(obj, "lastPing", what),
333
+ ttl: reqFiniteNumber(obj, "ttl", what),
334
+ };
335
+ const appState = optString(obj, "appState", what);
336
+ if (appState !== undefined) {
337
+ out.appState = b64urlField(appState, "appState", what);
338
+ }
339
+ return out;
340
+ }
341
+
342
+ function validateGossipRecordRefV1(value: unknown): GossipRecordRefV1 {
343
+ const what = "GossipRecordRefV1";
344
+ const obj = asObject(value, what);
345
+ return {
346
+ topicId: b64urlField(reqString(obj, "topicId", what), "topicId", what),
347
+ participantId: b64urlField(reqString(obj, "participantId", what), "participantId", what),
348
+ };
349
+ }
350
+
351
+ /** `willingnessBits` carries exactly 4 bits (T0..T3) as a single hex nibble. */
352
+ const WILLINGNESS_RE = /^[0-9a-fA-F]$/;
353
+
354
+ export function validateCohortGossipV1(value: unknown): CohortGossipV1 {
355
+ const what = "CohortGossipV1";
356
+ const obj = asObject(value, what);
357
+ requireV1(obj, what);
358
+ const willingnessBits = reqString(obj, "willingnessBits", what);
359
+ if (!WILLINGNESS_RE.test(willingnessBits)) {
360
+ fail(`${what}: field "willingnessBits" must be a single hex nibble (4 bits)`);
361
+ }
362
+ const loadBuckets = obj["loadBuckets"];
363
+ if (!Array.isArray(loadBuckets) || loadBuckets.length !== 4) {
364
+ fail(`${what}: field "loadBuckets" must be an array of length 4`);
365
+ }
366
+ for (const bucket of loadBuckets) {
367
+ if (typeof bucket !== "number" || !Number.isInteger(bucket) || bucket < 0 || bucket > 7) {
368
+ fail(`${what}: each loadBuckets entry must be an integer in 0..7`);
369
+ }
370
+ }
371
+ const summaries = obj["topicSummaries"];
372
+ if (!Array.isArray(summaries)) {
373
+ fail(`${what}: field "topicSummaries" must be an array`);
374
+ }
375
+ const out: CohortGossipV1 = {
376
+ v: 1,
377
+ fromMember: reqString(obj, "fromMember", what),
378
+ coord: b64urlField(reqString(obj, "coord", what), "coord", what),
379
+ cohortEpoch: b64urlField(reqString(obj, "cohortEpoch", what), "cohortEpoch", what),
380
+ willingnessBits,
381
+ loadBuckets: loadBuckets as number[],
382
+ windowSeconds: reqFiniteNumber(obj, "windowSeconds", what),
383
+ topicSummaries: summaries.map(validateCohortTopicSummary),
384
+ timestamp: reqFiniteNumber(obj, "timestamp", what),
385
+ signature: b64urlField(reqString(obj, "signature", what), "signature", what),
386
+ };
387
+ const records = obj["records"];
388
+ if (records !== undefined) {
389
+ if (!Array.isArray(records)) {
390
+ fail(`${what}: field "records" must be an array when present`);
391
+ }
392
+ out.records = records.map(validateGossipRecordV1);
393
+ }
394
+ const evicted = obj["evicted"];
395
+ if (evicted !== undefined) {
396
+ if (!Array.isArray(evicted)) {
397
+ fail(`${what}: field "evicted" must be an array when present`);
398
+ }
399
+ out.evicted = evicted.map(validateGossipRecordRefV1);
400
+ }
401
+ return out;
402
+ }
403
+
404
+ const SIGN_KINDS: readonly SignKind[] = ["membership", "promotion", "demotion", "rotation"];
405
+
406
+ export function validateSignRequestV1(value: unknown): SignRequestV1 {
407
+ const what = "SignRequestV1";
408
+ const obj = asObject(value, what);
409
+ requireV1(obj, what);
410
+ return {
411
+ v: 1,
412
+ kind: reqEnum(obj, "kind", SIGN_KINDS, what),
413
+ coord: b64urlField(reqString(obj, "coord", what), "coord", what),
414
+ cohortEpoch: b64urlField(reqString(obj, "cohortEpoch", what), "cohortEpoch", what),
415
+ payload: b64urlField(reqString(obj, "payload", what), "payload", what),
416
+ };
417
+ }
418
+
419
+ export function validateSignReplyV1(value: unknown): SignReplyV1 {
420
+ const what = "SignReplyV1";
421
+ const obj = asObject(value, what);
422
+ requireV1(obj, what);
423
+ // Discriminated by `refused`: a refusal carries a reason; an endorsement carries signer + signature.
424
+ if (obj["refused"] !== undefined) {
425
+ if (obj["refused"] !== true) {
426
+ fail(`${what}: field "refused" must be true when present`);
427
+ }
428
+ return { v: 1, refused: true, reason: reqString(obj, "reason", what) };
429
+ }
430
+ return {
431
+ v: 1,
432
+ signer: b64urlField(reqString(obj, "signer", what), "signer", what),
433
+ signature: b64urlField(reqString(obj, "signature", what), "signature", what),
434
+ };
435
+ }
436
+
437
+ export function validateMembershipCertV1(value: unknown): MembershipCertV1 {
438
+ const what = "MembershipCertV1";
439
+ const obj = asObject(value, what);
440
+ requireV1(obj, what);
441
+ const out: MembershipCertV1 = {
442
+ v: 1,
443
+ cohortCoord: b64urlField(reqString(obj, "cohortCoord", what), "cohortCoord", what),
444
+ cohortEpoch: b64urlField(reqString(obj, "cohortEpoch", what), "cohortEpoch", what),
445
+ members: reqStringArray(obj, "members", what),
446
+ stabilizedAt: reqFiniteNumber(obj, "stabilizedAt", what),
447
+ thresholdSig: b64urlField(reqString(obj, "thresholdSig", what), "thresholdSig", what),
448
+ signers: reqStringArray(obj, "signers", what),
449
+ };
450
+ const attestation = optString(obj, "fretAttestation", what);
451
+ if (attestation !== undefined) {
452
+ out.fretAttestation = b64urlField(attestation, "fretAttestation", what);
453
+ }
454
+ validateRotationAttestation(obj, out, what);
455
+ return out;
456
+ }
457
+
458
+ /**
459
+ * Validate the optional rotation attestation as an all-or-nothing group: either all three of
460
+ * `prevEpoch`/`rotationSig`/`rotationSigners` are present (and well-formed) or all are absent. A
461
+ * partial set is a {@link CohortWireError}. `rotationSigners` is validated only as a string array (its
462
+ * elements are decoded per-element later, mirroring `signers`).
463
+ */
464
+ function validateRotationAttestation(obj: Record<string, unknown>, out: MembershipCertV1, what: string): void {
465
+ const prevEpoch = optString(obj, "prevEpoch", what);
466
+ const rotationSig = optString(obj, "rotationSig", what);
467
+ const rotationSigners = optStringArray(obj, "rotationSigners", what);
468
+ const presentCount = [prevEpoch, rotationSig, rotationSigners].filter((v) => v !== undefined).length;
469
+ if (presentCount === 0) {
470
+ return;
471
+ }
472
+ if (presentCount !== 3) {
473
+ fail(`${what}: rotation attestation requires all of prevEpoch, rotationSig, rotationSigners — or none`);
474
+ }
475
+ out.prevEpoch = b64urlField(prevEpoch!, "prevEpoch", what);
476
+ out.rotationSig = b64urlField(rotationSig!, "rotationSig", what);
477
+ out.rotationSigners = rotationSigners!;
478
+ }
@@ -1,4 +1,4 @@
1
- import type { IBlock, Action, ActionType, ActionHandler, BlockId, ITransactor, BlockStore } from "../index.js";
1
+ import type { IBlock, Action, ActionType, ActionHandler, BlockId, ITransactor, BlockStore, Transforms } from "../index.js";
2
2
  import { Log, Atomic, Tracker, copyTransforms, CacheSource, isTransformsEmpty, TransactorSource } from "../index.js";
3
3
  import type { CollectionHeaderBlock, CollectionId, ICollection } from "./index.js";
4
4
  import type { ReadDependency } from "../transaction/transaction.js";
@@ -20,6 +20,15 @@ export type CollectionInitOptions<TAction> = {
20
20
  filterConflict?: (action: Action<TAction>, potential: Action<TAction>[]) => Action<TAction> | undefined
21
21
  }
22
22
 
23
+ /** A point-in-time copy of a collection's staged (un-synced) state, produced by
24
+ * {@link Collection.snapshotPending} and consumed by {@link Collection.restorePending}. */
25
+ export interface CollectionSnapshot<TAction> {
26
+ /** Deep-cloned tracker transforms at snapshot time. */
27
+ transforms: Transforms;
28
+ /** Pending actions queued at snapshot time. */
29
+ pending: Action<TAction>[];
30
+ }
31
+
23
32
  export class Collection<TAction> implements ICollection<TAction> {
24
33
  private pending: Action<TAction>[] = [];
25
34
  private readonly latchId: string;
@@ -127,6 +136,21 @@ export class Collection<TAction> implements ICollection<TAction> {
127
136
  anyConflicts = anyConflicts || this.tracker.conflicts(new Set(entry.blockIds)).length > 0;
128
137
  }
129
138
 
139
+ // React to durable invalidations that landed since we last synced. getFrom intentionally skips
140
+ // invalidation entries (they are not pending/committed actions), so surface them separately: an
141
+ // invalidation reverted committed content this client may have read, so treat it like a stale
142
+ // read — drop the reverted blocks from the read cache and replay pending work against the reverted
143
+ // base (docs/right-is-right.md §Client notification). De-duped across cascade children by reverted
144
+ // block; over-inclusive by design (over-invalidation just resubmits — it never wrongly retains).
145
+ const invalidations = log ? await log.getInvalidationsFrom(actionContext?.rev ?? 0) : [];
146
+ if (invalidations.length > 0) {
147
+ const revertedBlockIds = [...new Set(invalidations.flatMap(inv => inv.reverted.map(r => r.blockId)))];
148
+ this.sourceCache.clear(revertedBlockIds);
149
+ if (this.pending.length > 0) {
150
+ anyConflicts = true;
151
+ }
152
+ }
153
+
130
154
  // On conflicts, clear related caching and block-tracking and replay logical operations
131
155
  if (anyConflicts) {
132
156
  await this.replayActions();
@@ -136,6 +160,74 @@ export class Collection<TAction> implements ICollection<TAction> {
136
160
  this.source.actionContext = latest?.context;
137
161
  }
138
162
 
163
+ /** Capture the current staged state — tracker transforms plus the pending
164
+ * action queue — so it can be restored later via {@link restorePending}.
165
+ *
166
+ * Use to bracket a unit of staged DML that may need to be rolled back. Unlike
167
+ * a blanket "reset to empty", restoring this snapshot preserves any structural
168
+ * baseline that predates the staged DML — most importantly a brand-new
169
+ * collection's header/root blocks, which live in the tracker (uncommitted)
170
+ * until the first sync. Resetting such a collection to empty would leave it
171
+ * unreadable; restoring the snapshot returns it to its prior (readable) state.
172
+ *
173
+ * The returned snapshot is deep-cloned and independent of subsequent mutations.
174
+ * Synchronous and latch-free: intended to bracket transaction-scoped staging,
175
+ * when no concurrent act/sync is in flight. */
176
+ snapshotPending(): CollectionSnapshot<TAction> {
177
+ return { transforms: copyTransforms(this.tracker.transforms), pending: [...this.pending] };
178
+ }
179
+
180
+ /** Restore the staged state captured by {@link snapshotPending}, discarding any
181
+ * mutations staged since. Reads through the collection then observe exactly the
182
+ * snapshot state again; storage is untouched because nothing was ever synced. */
183
+ restorePending(snapshot: CollectionSnapshot<TAction>): void {
184
+ this.tracker.reset(copyTransforms(snapshot.transforms));
185
+ this.pending = [...snapshot.pending];
186
+ }
187
+
188
+ /** A read-only {@link Tracker} over this collection's committed source cache,
189
+ * seeded with a (deep-copied) set of pre-transaction transforms. Reads through it
190
+ * observe committed state plus exactly those transforms — and crucially NOT the
191
+ * mutations staged into this collection's live tracker afterward. Used to build a
192
+ * committed read view (see {@link Tree.readView}) that excludes a transaction's
193
+ * own in-flight rows. The returned tracker shares the source cache (committed
194
+ * blocks) but has its own independent transform set; it is never sync()'d. */
195
+ createReadTracker(transforms: Transforms): Tracker<IBlock> {
196
+ return new Tracker(this.sourceCache, copyTransforms(transforms));
197
+ }
198
+
199
+ /** The staged (not-yet-synced) actions queued by {@link act}.
200
+ *
201
+ * Exposed so a {@link TransactionCoordinator} can append them to the log at
202
+ * commit time — mirroring what {@link sync} does internally — when the actions
203
+ * were staged directly into this collection (e.g. through a Tree's stage())
204
+ * rather than applied via the coordinator's own action path. */
205
+ getPendingActions(): Action<TAction>[] {
206
+ return this.pending;
207
+ }
208
+
209
+ /** Drop the staged actions after they have been committed through a
210
+ * coordinator. Counterpart to {@link getPendingActions}; {@link sync} clears
211
+ * its own pending inline, so this is only needed when commit was orchestrated
212
+ * externally. */
213
+ clearPendingActions(): void {
214
+ this.pending = [];
215
+ }
216
+
217
+ /** Fold a just-committed set of transforms into this collection's read cache
218
+ * so subsequent reads (and stages) through THIS instance observe the committed
219
+ * state, mirroring what {@link sync} does inline after a successful transact.
220
+ *
221
+ * Needed when commit was orchestrated externally (a coordinator): the tracker
222
+ * is reset to empty, but the cache still holds the pre-commit blocks. Without
223
+ * this, a collection that already had committed state (e.g. a pre-synced index
224
+ * tree, or any collection on its second commit) keeps serving the stale prior
225
+ * revision because {@link update} sees its rev is already current and refetches
226
+ * nothing. Call BEFORE resetting the tracker (the transforms are read live). */
227
+ applyCommittedToCache(transforms: Transforms): void {
228
+ this.sourceCache.transformCache(transforms);
229
+ }
230
+
139
231
  /** Push our pending actions to the transactor */
140
232
  async sync() {
141
233
  const release = await Latches.acquire(this.latchId);