@optimystic/db-core 0.13.5 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (447) hide show
  1. package/dist/src/cluster/structs.d.ts +13 -0
  2. package/dist/src/cluster/structs.d.ts.map +1 -1
  3. package/dist/src/cohort-topic/addressing.d.ts +53 -0
  4. package/dist/src/cohort-topic/addressing.d.ts.map +1 -0
  5. package/dist/src/cohort-topic/addressing.js +95 -0
  6. package/dist/src/cohort-topic/addressing.js.map +1 -0
  7. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.d.ts +105 -0
  8. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.d.ts.map +1 -0
  9. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.js +197 -0
  10. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.js.map +1 -0
  11. package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts +50 -0
  12. package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts.map +1 -0
  13. package/dist/src/cohort-topic/antidos/bootstrap-evidence.js +62 -0
  14. package/dist/src/cohort-topic/antidos/bootstrap-evidence.js.map +1 -0
  15. package/dist/src/cohort-topic/antidos/index.d.ts +6 -0
  16. package/dist/src/cohort-topic/antidos/index.d.ts.map +1 -0
  17. package/dist/src/cohort-topic/antidos/index.js +6 -0
  18. package/dist/src/cohort-topic/antidos/index.js.map +1 -0
  19. package/dist/src/cohort-topic/antidos/rate-limiter.d.ts +95 -0
  20. package/dist/src/cohort-topic/antidos/rate-limiter.d.ts.map +1 -0
  21. package/dist/src/cohort-topic/antidos/rate-limiter.js +146 -0
  22. package/dist/src/cohort-topic/antidos/rate-limiter.js.map +1 -0
  23. package/dist/src/cohort-topic/antidos/replay-guard.d.ts +37 -0
  24. package/dist/src/cohort-topic/antidos/replay-guard.d.ts.map +1 -0
  25. package/dist/src/cohort-topic/antidos/replay-guard.js +78 -0
  26. package/dist/src/cohort-topic/antidos/replay-guard.js.map +1 -0
  27. package/dist/src/cohort-topic/antidos/topic-budget.d.ts +50 -0
  28. package/dist/src/cohort-topic/antidos/topic-budget.d.ts.map +1 -0
  29. package/dist/src/cohort-topic/antidos/topic-budget.js +95 -0
  30. package/dist/src/cohort-topic/antidos/topic-budget.js.map +1 -0
  31. package/dist/src/cohort-topic/antiflood/index.d.ts +3 -0
  32. package/dist/src/cohort-topic/antiflood/index.d.ts.map +1 -0
  33. package/dist/src/cohort-topic/antiflood/index.js +3 -0
  34. package/dist/src/cohort-topic/antiflood/index.js.map +1 -0
  35. package/dist/src/cohort-topic/antiflood/invariants.d.ts +70 -0
  36. package/dist/src/cohort-topic/antiflood/invariants.d.ts.map +1 -0
  37. package/dist/src/cohort-topic/antiflood/invariants.js +86 -0
  38. package/dist/src/cohort-topic/antiflood/invariants.js.map +1 -0
  39. package/dist/src/cohort-topic/antiflood/jitter.d.ts +65 -0
  40. package/dist/src/cohort-topic/antiflood/jitter.d.ts.map +1 -0
  41. package/dist/src/cohort-topic/antiflood/jitter.js +75 -0
  42. package/dist/src/cohort-topic/antiflood/jitter.js.map +1 -0
  43. package/dist/src/cohort-topic/coldstart.d.ts +117 -0
  44. package/dist/src/cohort-topic/coldstart.d.ts.map +1 -0
  45. package/dist/src/cohort-topic/coldstart.js +127 -0
  46. package/dist/src/cohort-topic/coldstart.js.map +1 -0
  47. package/dist/src/cohort-topic/dmax.d.ts +44 -0
  48. package/dist/src/cohort-topic/dmax.d.ts.map +1 -0
  49. package/dist/src/cohort-topic/dmax.js +70 -0
  50. package/dist/src/cohort-topic/dmax.js.map +1 -0
  51. package/dist/src/cohort-topic/gossip/bus.d.ts +85 -0
  52. package/dist/src/cohort-topic/gossip/bus.d.ts.map +1 -0
  53. package/dist/src/cohort-topic/gossip/bus.js +146 -0
  54. package/dist/src/cohort-topic/gossip/bus.js.map +1 -0
  55. package/dist/src/cohort-topic/gossip/index.d.ts +4 -0
  56. package/dist/src/cohort-topic/gossip/index.d.ts.map +1 -0
  57. package/dist/src/cohort-topic/gossip/index.js +4 -0
  58. package/dist/src/cohort-topic/gossip/index.js.map +1 -0
  59. package/dist/src/cohort-topic/gossip/records.d.ts +12 -0
  60. package/dist/src/cohort-topic/gossip/records.d.ts.map +1 -0
  61. package/dist/src/cohort-topic/gossip/records.js +41 -0
  62. package/dist/src/cohort-topic/gossip/records.js.map +1 -0
  63. package/dist/src/cohort-topic/gossip/view.d.ts +39 -0
  64. package/dist/src/cohort-topic/gossip/view.d.ts.map +1 -0
  65. package/dist/src/cohort-topic/gossip/view.js +30 -0
  66. package/dist/src/cohort-topic/gossip/view.js.map +1 -0
  67. package/dist/src/cohort-topic/index.d.ts +21 -0
  68. package/dist/src/cohort-topic/index.d.ts.map +1 -0
  69. package/dist/src/cohort-topic/index.js +21 -0
  70. package/dist/src/cohort-topic/index.js.map +1 -0
  71. package/dist/src/cohort-topic/load/barometer.d.ts +78 -0
  72. package/dist/src/cohort-topic/load/barometer.d.ts.map +1 -0
  73. package/dist/src/cohort-topic/load/barometer.js +93 -0
  74. package/dist/src/cohort-topic/load/barometer.js.map +1 -0
  75. package/dist/src/cohort-topic/load/index.d.ts +2 -0
  76. package/dist/src/cohort-topic/load/index.d.ts.map +1 -0
  77. package/dist/src/cohort-topic/load/index.js +2 -0
  78. package/dist/src/cohort-topic/load/index.js.map +1 -0
  79. package/dist/src/cohort-topic/member-engine.d.ts +134 -0
  80. package/dist/src/cohort-topic/member-engine.d.ts.map +1 -0
  81. package/dist/src/cohort-topic/member-engine.js +264 -0
  82. package/dist/src/cohort-topic/member-engine.js.map +1 -0
  83. package/dist/src/cohort-topic/membership/index.d.ts +4 -0
  84. package/dist/src/cohort-topic/membership/index.d.ts.map +1 -0
  85. package/dist/src/cohort-topic/membership/index.js +4 -0
  86. package/dist/src/cohort-topic/membership/index.js.map +1 -0
  87. package/dist/src/cohort-topic/membership/publisher.d.ts +82 -0
  88. package/dist/src/cohort-topic/membership/publisher.d.ts.map +1 -0
  89. package/dist/src/cohort-topic/membership/publisher.js +107 -0
  90. package/dist/src/cohort-topic/membership/publisher.js.map +1 -0
  91. package/dist/src/cohort-topic/membership/source.d.ts +31 -0
  92. package/dist/src/cohort-topic/membership/source.d.ts.map +1 -0
  93. package/dist/src/cohort-topic/membership/source.js +32 -0
  94. package/dist/src/cohort-topic/membership/source.js.map +1 -0
  95. package/dist/src/cohort-topic/membership/verifier.d.ts +96 -0
  96. package/dist/src/cohort-topic/membership/verifier.d.ts.map +1 -0
  97. package/dist/src/cohort-topic/membership/verifier.js +250 -0
  98. package/dist/src/cohort-topic/membership/verifier.js.map +1 -0
  99. package/dist/src/cohort-topic/ports.d.ts +149 -0
  100. package/dist/src/cohort-topic/ports.d.ts.map +1 -0
  101. package/dist/src/cohort-topic/ports.js +25 -0
  102. package/dist/src/cohort-topic/ports.js.map +1 -0
  103. package/dist/src/cohort-topic/promotion.d.ts +135 -0
  104. package/dist/src/cohort-topic/promotion.d.ts.map +1 -0
  105. package/dist/src/cohort-topic/promotion.js +269 -0
  106. package/dist/src/cohort-topic/promotion.js.map +1 -0
  107. package/dist/src/cohort-topic/registration/bytes.d.ts +16 -0
  108. package/dist/src/cohort-topic/registration/bytes.d.ts.map +1 -0
  109. package/dist/src/cohort-topic/registration/bytes.js +32 -0
  110. package/dist/src/cohort-topic/registration/bytes.js.map +1 -0
  111. package/dist/src/cohort-topic/registration/handoff.d.ts +69 -0
  112. package/dist/src/cohort-topic/registration/handoff.d.ts.map +1 -0
  113. package/dist/src/cohort-topic/registration/handoff.js +98 -0
  114. package/dist/src/cohort-topic/registration/handoff.js.map +1 -0
  115. package/dist/src/cohort-topic/registration/index.d.ts +7 -0
  116. package/dist/src/cohort-topic/registration/index.d.ts.map +1 -0
  117. package/dist/src/cohort-topic/registration/index.js +7 -0
  118. package/dist/src/cohort-topic/registration/index.js.map +1 -0
  119. package/dist/src/cohort-topic/registration/renewal.d.ts +115 -0
  120. package/dist/src/cohort-topic/registration/renewal.d.ts.map +1 -0
  121. package/dist/src/cohort-topic/registration/renewal.js +306 -0
  122. package/dist/src/cohort-topic/registration/renewal.js.map +1 -0
  123. package/dist/src/cohort-topic/registration/sharding.d.ts +31 -0
  124. package/dist/src/cohort-topic/registration/sharding.d.ts.map +1 -0
  125. package/dist/src/cohort-topic/registration/sharding.js +48 -0
  126. package/dist/src/cohort-topic/registration/sharding.js.map +1 -0
  127. package/dist/src/cohort-topic/registration/store.d.ts +13 -0
  128. package/dist/src/cohort-topic/registration/store.d.ts.map +1 -0
  129. package/dist/src/cohort-topic/registration/store.js +72 -0
  130. package/dist/src/cohort-topic/registration/store.js.map +1 -0
  131. package/dist/src/cohort-topic/registration/types.d.ts +71 -0
  132. package/dist/src/cohort-topic/registration/types.d.ts.map +1 -0
  133. package/dist/src/cohort-topic/registration/types.js +27 -0
  134. package/dist/src/cohort-topic/registration/types.js.map +1 -0
  135. package/dist/src/cohort-topic/ring-hash.d.ts +27 -0
  136. package/dist/src/cohort-topic/ring-hash.d.ts.map +1 -0
  137. package/dist/src/cohort-topic/ring-hash.js +45 -0
  138. package/dist/src/cohort-topic/ring-hash.js.map +1 -0
  139. package/dist/src/cohort-topic/service.d.ts +157 -0
  140. package/dist/src/cohort-topic/service.d.ts.map +1 -0
  141. package/dist/src/cohort-topic/service.js +237 -0
  142. package/dist/src/cohort-topic/service.js.map +1 -0
  143. package/dist/src/cohort-topic/sig/index.d.ts +3 -0
  144. package/dist/src/cohort-topic/sig/index.d.ts.map +1 -0
  145. package/dist/src/cohort-topic/sig/index.js +3 -0
  146. package/dist/src/cohort-topic/sig/index.js.map +1 -0
  147. package/dist/src/cohort-topic/sig/payloads.d.ts +25 -0
  148. package/dist/src/cohort-topic/sig/payloads.d.ts.map +1 -0
  149. package/dist/src/cohort-topic/sig/payloads.js +25 -0
  150. package/dist/src/cohort-topic/sig/payloads.js.map +1 -0
  151. package/dist/src/cohort-topic/sig/threshold.d.ts +33 -0
  152. package/dist/src/cohort-topic/sig/threshold.d.ts.map +1 -0
  153. package/dist/src/cohort-topic/sig/threshold.js +51 -0
  154. package/dist/src/cohort-topic/sig/threshold.js.map +1 -0
  155. package/dist/src/cohort-topic/tiers.d.ts +58 -0
  156. package/dist/src/cohort-topic/tiers.d.ts.map +1 -0
  157. package/dist/src/cohort-topic/tiers.js +49 -0
  158. package/dist/src/cohort-topic/tiers.js.map +1 -0
  159. package/dist/src/cohort-topic/traffic.d.ts +90 -0
  160. package/dist/src/cohort-topic/traffic.d.ts.map +1 -0
  161. package/dist/src/cohort-topic/traffic.js +146 -0
  162. package/dist/src/cohort-topic/traffic.js.map +1 -0
  163. package/dist/src/cohort-topic/walk.d.ts +123 -0
  164. package/dist/src/cohort-topic/walk.d.ts.map +1 -0
  165. package/dist/src/cohort-topic/walk.js +150 -0
  166. package/dist/src/cohort-topic/walk.js.map +1 -0
  167. package/dist/src/cohort-topic/willingness.d.ts +123 -0
  168. package/dist/src/cohort-topic/willingness.d.ts.map +1 -0
  169. package/dist/src/cohort-topic/willingness.js +143 -0
  170. package/dist/src/cohort-topic/willingness.js.map +1 -0
  171. package/dist/src/cohort-topic/wire/codec.d.ts +42 -0
  172. package/dist/src/cohort-topic/wire/codec.d.ts.map +1 -0
  173. package/dist/src/cohort-topic/wire/codec.js +163 -0
  174. package/dist/src/cohort-topic/wire/codec.js.map +1 -0
  175. package/dist/src/cohort-topic/wire/index.d.ts +6 -0
  176. package/dist/src/cohort-topic/wire/index.d.ts.map +1 -0
  177. package/dist/src/cohort-topic/wire/index.js +6 -0
  178. package/dist/src/cohort-topic/wire/index.js.map +1 -0
  179. package/dist/src/cohort-topic/wire/payloads.d.ts +38 -0
  180. package/dist/src/cohort-topic/wire/payloads.d.ts.map +1 -0
  181. package/dist/src/cohort-topic/wire/payloads.js +102 -0
  182. package/dist/src/cohort-topic/wire/payloads.js.map +1 -0
  183. package/dist/src/cohort-topic/wire/types.d.ts +304 -0
  184. package/dist/src/cohort-topic/wire/types.d.ts.map +1 -0
  185. package/dist/src/cohort-topic/wire/types.js +16 -0
  186. package/dist/src/cohort-topic/wire/types.js.map +1 -0
  187. package/dist/src/cohort-topic/wire/validate.d.ts +25 -0
  188. package/dist/src/cohort-topic/wire/validate.d.ts.map +1 -0
  189. package/dist/src/cohort-topic/wire/validate.js +427 -0
  190. package/dist/src/cohort-topic/wire/validate.js.map +1 -0
  191. package/dist/src/collection/collection.d.ts +58 -1
  192. package/dist/src/collection/collection.d.ts.map +1 -1
  193. package/dist/src/collection/collection.js +76 -0
  194. package/dist/src/collection/collection.js.map +1 -1
  195. package/dist/src/collections/tree/tree.d.ts +58 -2
  196. package/dist/src/collections/tree/tree.d.ts.map +1 -1
  197. package/dist/src/collections/tree/tree.js +59 -2
  198. package/dist/src/collections/tree/tree.js.map +1 -1
  199. package/dist/src/index.d.ts +3 -0
  200. package/dist/src/index.d.ts.map +1 -1
  201. package/dist/src/index.js +3 -0
  202. package/dist/src/index.js.map +1 -1
  203. package/dist/src/log/log.d.ts +42 -4
  204. package/dist/src/log/log.d.ts.map +1 -1
  205. package/dist/src/log/log.js +71 -3
  206. package/dist/src/log/log.js.map +1 -1
  207. package/dist/src/log/struct.d.ts +122 -2
  208. package/dist/src/log/struct.d.ts.map +1 -1
  209. package/dist/src/log/struct.js.map +1 -1
  210. package/dist/src/matchmaking/capability-filter.d.ts +24 -0
  211. package/dist/src/matchmaking/capability-filter.d.ts.map +1 -0
  212. package/dist/src/matchmaking/capability-filter.js +37 -0
  213. package/dist/src/matchmaking/capability-filter.js.map +1 -0
  214. package/dist/src/matchmaking/config.d.ts +77 -0
  215. package/dist/src/matchmaking/config.d.ts.map +1 -0
  216. package/dist/src/matchmaking/config.js +69 -0
  217. package/dist/src/matchmaking/config.js.map +1 -0
  218. package/dist/src/matchmaking/index.d.ts +21 -0
  219. package/dist/src/matchmaking/index.d.ts.map +1 -0
  220. package/dist/src/matchmaking/index.js +21 -0
  221. package/dist/src/matchmaking/index.js.map +1 -0
  222. package/dist/src/matchmaking/multi-cohort-seeker.d.ts +133 -0
  223. package/dist/src/matchmaking/multi-cohort-seeker.d.ts.map +1 -0
  224. package/dist/src/matchmaking/multi-cohort-seeker.js +142 -0
  225. package/dist/src/matchmaking/multi-cohort-seeker.js.map +1 -0
  226. package/dist/src/matchmaking/provider.d.ts +70 -0
  227. package/dist/src/matchmaking/provider.d.ts.map +1 -0
  228. package/dist/src/matchmaking/provider.js +93 -0
  229. package/dist/src/matchmaking/provider.js.map +1 -0
  230. package/dist/src/matchmaking/query-eval.d.ts +53 -0
  231. package/dist/src/matchmaking/query-eval.d.ts.map +1 -0
  232. package/dist/src/matchmaking/query-eval.js +69 -0
  233. package/dist/src/matchmaking/query-eval.js.map +1 -0
  234. package/dist/src/matchmaking/seeker-walk.d.ts +95 -0
  235. package/dist/src/matchmaking/seeker-walk.d.ts.map +1 -0
  236. package/dist/src/matchmaking/seeker-walk.js +75 -0
  237. package/dist/src/matchmaking/seeker-walk.js.map +1 -0
  238. package/dist/src/matchmaking/seeker.d.ts +51 -0
  239. package/dist/src/matchmaking/seeker.d.ts.map +1 -0
  240. package/dist/src/matchmaking/seeker.js +62 -0
  241. package/dist/src/matchmaking/seeker.js.map +1 -0
  242. package/dist/src/matchmaking/topic-anchor.d.ts +54 -0
  243. package/dist/src/matchmaking/topic-anchor.d.ts.map +1 -0
  244. package/dist/src/matchmaking/topic-anchor.js +67 -0
  245. package/dist/src/matchmaking/topic-anchor.js.map +1 -0
  246. package/dist/src/matchmaking/voting-quorum.d.ts +233 -0
  247. package/dist/src/matchmaking/voting-quorum.d.ts.map +1 -0
  248. package/dist/src/matchmaking/voting-quorum.js +238 -0
  249. package/dist/src/matchmaking/voting-quorum.js.map +1 -0
  250. package/dist/src/matchmaking/wire.d.ts +259 -0
  251. package/dist/src/matchmaking/wire.d.ts.map +1 -0
  252. package/dist/src/matchmaking/wire.js +484 -0
  253. package/dist/src/matchmaking/wire.js.map +1 -0
  254. package/dist/src/network/i-repo.d.ts +9 -0
  255. package/dist/src/network/i-repo.d.ts.map +1 -1
  256. package/dist/src/network/repo-protocol.d.ts +3 -1
  257. package/dist/src/network/repo-protocol.d.ts.map +1 -1
  258. package/dist/src/network/struct.d.ts +23 -1
  259. package/dist/src/network/struct.d.ts.map +1 -1
  260. package/dist/src/reactivity/backfill.d.ts +118 -0
  261. package/dist/src/reactivity/backfill.d.ts.map +1 -0
  262. package/dist/src/reactivity/backfill.js +137 -0
  263. package/dist/src/reactivity/backfill.js.map +1 -0
  264. package/dist/src/reactivity/backpressure.d.ts +99 -0
  265. package/dist/src/reactivity/backpressure.d.ts.map +1 -0
  266. package/dist/src/reactivity/backpressure.js +153 -0
  267. package/dist/src/reactivity/backpressure.js.map +1 -0
  268. package/dist/src/reactivity/checkpoint.d.ts +165 -0
  269. package/dist/src/reactivity/checkpoint.d.ts.map +1 -0
  270. package/dist/src/reactivity/checkpoint.js +232 -0
  271. package/dist/src/reactivity/checkpoint.js.map +1 -0
  272. package/dist/src/reactivity/config.d.ts +131 -0
  273. package/dist/src/reactivity/config.d.ts.map +1 -0
  274. package/dist/src/reactivity/config.js +135 -0
  275. package/dist/src/reactivity/config.js.map +1 -0
  276. package/dist/src/reactivity/dedupe.d.ts +51 -0
  277. package/dist/src/reactivity/dedupe.d.ts.map +1 -0
  278. package/dist/src/reactivity/dedupe.js +90 -0
  279. package/dist/src/reactivity/dedupe.js.map +1 -0
  280. package/dist/src/reactivity/forwarder.d.ts +50 -0
  281. package/dist/src/reactivity/forwarder.d.ts.map +1 -0
  282. package/dist/src/reactivity/forwarder.js +52 -0
  283. package/dist/src/reactivity/forwarder.js.map +1 -0
  284. package/dist/src/reactivity/index.d.ts +34 -0
  285. package/dist/src/reactivity/index.d.ts.map +1 -0
  286. package/dist/src/reactivity/index.js +34 -0
  287. package/dist/src/reactivity/index.js.map +1 -0
  288. package/dist/src/reactivity/notification.d.ts +72 -0
  289. package/dist/src/reactivity/notification.d.ts.map +1 -0
  290. package/dist/src/reactivity/notification.js +92 -0
  291. package/dist/src/reactivity/notification.js.map +1 -0
  292. package/dist/src/reactivity/policy.d.ts +59 -0
  293. package/dist/src/reactivity/policy.d.ts.map +1 -0
  294. package/dist/src/reactivity/policy.js +64 -0
  295. package/dist/src/reactivity/policy.js.map +1 -0
  296. package/dist/src/reactivity/push-state.d.ts +140 -0
  297. package/dist/src/reactivity/push-state.d.ts.map +1 -0
  298. package/dist/src/reactivity/push-state.js +270 -0
  299. package/dist/src/reactivity/push-state.js.map +1 -0
  300. package/dist/src/reactivity/recover.d.ts +75 -0
  301. package/dist/src/reactivity/recover.d.ts.map +1 -0
  302. package/dist/src/reactivity/recover.js +105 -0
  303. package/dist/src/reactivity/recover.js.map +1 -0
  304. package/dist/src/reactivity/replay-buffer.d.ts +57 -0
  305. package/dist/src/reactivity/replay-buffer.d.ts.map +1 -0
  306. package/dist/src/reactivity/replay-buffer.js +88 -0
  307. package/dist/src/reactivity/replay-buffer.js.map +1 -0
  308. package/dist/src/reactivity/resume.d.ts +214 -0
  309. package/dist/src/reactivity/resume.d.ts.map +1 -0
  310. package/dist/src/reactivity/resume.js +358 -0
  311. package/dist/src/reactivity/resume.js.map +1 -0
  312. package/dist/src/reactivity/rotation.d.ts +283 -0
  313. package/dist/src/reactivity/rotation.d.ts.map +1 -0
  314. package/dist/src/reactivity/rotation.js +265 -0
  315. package/dist/src/reactivity/rotation.js.map +1 -0
  316. package/dist/src/reactivity/subscriber.d.ts +66 -0
  317. package/dist/src/reactivity/subscriber.d.ts.map +1 -0
  318. package/dist/src/reactivity/subscriber.js +75 -0
  319. package/dist/src/reactivity/subscriber.js.map +1 -0
  320. package/dist/src/reactivity/subscription.d.ts +52 -0
  321. package/dist/src/reactivity/subscription.d.ts.map +1 -0
  322. package/dist/src/reactivity/subscription.js +28 -0
  323. package/dist/src/reactivity/subscription.js.map +1 -0
  324. package/dist/src/reactivity/topic-anchor.d.ts +46 -0
  325. package/dist/src/reactivity/topic-anchor.d.ts.map +1 -0
  326. package/dist/src/reactivity/topic-anchor.js +61 -0
  327. package/dist/src/reactivity/topic-anchor.js.map +1 -0
  328. package/dist/src/reactivity/verify.d.ts +41 -0
  329. package/dist/src/reactivity/verify.d.ts.map +1 -0
  330. package/dist/src/reactivity/verify.js +48 -0
  331. package/dist/src/reactivity/verify.js.map +1 -0
  332. package/dist/src/reactivity/wire-validate.d.ts +28 -0
  333. package/dist/src/reactivity/wire-validate.d.ts.map +1 -0
  334. package/dist/src/reactivity/wire-validate.js +79 -0
  335. package/dist/src/reactivity/wire-validate.js.map +1 -0
  336. package/dist/src/reactivity/wire.d.ts +102 -0
  337. package/dist/src/reactivity/wire.d.ts.map +1 -0
  338. package/dist/src/reactivity/wire.js +129 -0
  339. package/dist/src/reactivity/wire.js.map +1 -0
  340. package/dist/src/transaction/coordinator.d.ts +9 -5
  341. package/dist/src/transaction/coordinator.d.ts.map +1 -1
  342. package/dist/src/transaction/coordinator.js +48 -25
  343. package/dist/src/transaction/coordinator.js.map +1 -1
  344. package/dist/src/transactor/change-notifier.d.ts +74 -0
  345. package/dist/src/transactor/change-notifier.d.ts.map +1 -0
  346. package/dist/src/transactor/change-notifier.js +4 -0
  347. package/dist/src/transactor/change-notifier.js.map +1 -0
  348. package/dist/src/transactor/index.d.ts +1 -0
  349. package/dist/src/transactor/index.d.ts.map +1 -1
  350. package/dist/src/transactor/index.js +1 -0
  351. package/dist/src/transactor/index.js.map +1 -1
  352. package/dist/src/transactor/network-transactor.d.ts +34 -2
  353. package/dist/src/transactor/network-transactor.d.ts.map +1 -1
  354. package/dist/src/transactor/network-transactor.js +113 -13
  355. package/dist/src/transactor/network-transactor.js.map +1 -1
  356. package/package.json +2 -2
  357. package/src/cluster/structs.ts +13 -0
  358. package/src/cohort-topic/addressing.ts +112 -0
  359. package/src/cohort-topic/antidos/bootstrap-evidence-envelope.ts +253 -0
  360. package/src/cohort-topic/antidos/bootstrap-evidence.ts +94 -0
  361. package/src/cohort-topic/antidos/index.ts +5 -0
  362. package/src/cohort-topic/antidos/rate-limiter.ts +210 -0
  363. package/src/cohort-topic/antidos/replay-guard.ts +109 -0
  364. package/src/cohort-topic/antidos/topic-budget.ts +145 -0
  365. package/src/cohort-topic/antiflood/index.ts +2 -0
  366. package/src/cohort-topic/antiflood/invariants.ts +108 -0
  367. package/src/cohort-topic/antiflood/jitter.ts +117 -0
  368. package/src/cohort-topic/coldstart.ts +204 -0
  369. package/src/cohort-topic/dmax.ts +88 -0
  370. package/src/cohort-topic/gossip/bus.ts +217 -0
  371. package/src/cohort-topic/gossip/index.ts +3 -0
  372. package/src/cohort-topic/gossip/records.ts +45 -0
  373. package/src/cohort-topic/gossip/view.ts +66 -0
  374. package/src/cohort-topic/index.ts +20 -0
  375. package/src/cohort-topic/load/barometer.ts +134 -0
  376. package/src/cohort-topic/load/index.ts +1 -0
  377. package/src/cohort-topic/member-engine.ts +409 -0
  378. package/src/cohort-topic/membership/index.ts +3 -0
  379. package/src/cohort-topic/membership/publisher.ts +171 -0
  380. package/src/cohort-topic/membership/source.ts +41 -0
  381. package/src/cohort-topic/membership/verifier.ts +345 -0
  382. package/src/cohort-topic/ports.ts +157 -0
  383. package/src/cohort-topic/promotion.ts +384 -0
  384. package/src/cohort-topic/registration/bytes.ts +37 -0
  385. package/src/cohort-topic/registration/handoff.ts +154 -0
  386. package/src/cohort-topic/registration/index.ts +6 -0
  387. package/src/cohort-topic/registration/renewal.ts +423 -0
  388. package/src/cohort-topic/registration/sharding.ts +61 -0
  389. package/src/cohort-topic/registration/store.ts +81 -0
  390. package/src/cohort-topic/registration/types.ts +76 -0
  391. package/src/cohort-topic/ring-hash.ts +50 -0
  392. package/src/cohort-topic/service.ts +390 -0
  393. package/src/cohort-topic/sig/index.ts +2 -0
  394. package/src/cohort-topic/sig/payloads.ts +39 -0
  395. package/src/cohort-topic/sig/threshold.ts +64 -0
  396. package/src/cohort-topic/tiers.ts +74 -0
  397. package/src/cohort-topic/traffic.ts +211 -0
  398. package/src/cohort-topic/walk.ts +255 -0
  399. package/src/cohort-topic/willingness.ts +237 -0
  400. package/src/cohort-topic/wire/codec.ts +204 -0
  401. package/src/cohort-topic/wire/index.ts +16 -0
  402. package/src/cohort-topic/wire/payloads.ts +117 -0
  403. package/src/cohort-topic/wire/types.ts +343 -0
  404. package/src/cohort-topic/wire/validate.ts +478 -0
  405. package/src/collection/collection.ts +93 -1
  406. package/src/collections/tree/tree.ts +84 -3
  407. package/src/index.ts +3 -0
  408. package/src/log/log.ts +86 -7
  409. package/src/log/struct.ts +126 -2
  410. package/src/matchmaking/capability-filter.ts +45 -0
  411. package/src/matchmaking/config.ts +98 -0
  412. package/src/matchmaking/index.ts +21 -0
  413. package/src/matchmaking/multi-cohort-seeker.ts +234 -0
  414. package/src/matchmaking/provider.ts +123 -0
  415. package/src/matchmaking/query-eval.ts +105 -0
  416. package/src/matchmaking/seeker-walk.ts +127 -0
  417. package/src/matchmaking/seeker.ts +86 -0
  418. package/src/matchmaking/topic-anchor.ts +90 -0
  419. package/src/matchmaking/voting-quorum.ts +394 -0
  420. package/src/matchmaking/wire.ts +680 -0
  421. package/src/network/i-repo.ts +9 -0
  422. package/src/network/repo-protocol.ts +3 -2
  423. package/src/network/struct.ts +24 -1
  424. package/src/reactivity/backfill.ts +220 -0
  425. package/src/reactivity/backpressure.ts +191 -0
  426. package/src/reactivity/checkpoint.ts +308 -0
  427. package/src/reactivity/config.ts +172 -0
  428. package/src/reactivity/dedupe.ts +132 -0
  429. package/src/reactivity/forwarder.ts +87 -0
  430. package/src/reactivity/index.ts +34 -0
  431. package/src/reactivity/notification.ts +123 -0
  432. package/src/reactivity/policy.ts +79 -0
  433. package/src/reactivity/push-state.ts +343 -0
  434. package/src/reactivity/recover.ts +153 -0
  435. package/src/reactivity/replay-buffer.ts +141 -0
  436. package/src/reactivity/resume.ts +549 -0
  437. package/src/reactivity/rotation.ts +415 -0
  438. package/src/reactivity/subscriber.ts +132 -0
  439. package/src/reactivity/subscription.ts +66 -0
  440. package/src/reactivity/topic-anchor.ts +71 -0
  441. package/src/reactivity/verify.ts +73 -0
  442. package/src/reactivity/wire-validate.ts +87 -0
  443. package/src/reactivity/wire.ts +224 -0
  444. package/src/transaction/coordinator.ts +53 -27
  445. package/src/transactor/change-notifier.ts +80 -0
  446. package/src/transactor/index.ts +1 -0
  447. package/src/transactor/network-transactor.ts +133 -15
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Reactivity — rotating, tail-anchored topic.
3
+ *
4
+ * Transcribed from `docs/reactivity.md` §Anchor:
5
+ *
6
+ * ```
7
+ * topicId(collection C, tail T) = H(T.blockId ‖ "reactivity")
8
+ * ```
9
+ *
10
+ * Unlike matchmaking (whose anchor is a stable `(kind, label)`), the reactivity anchor **rotates** with
11
+ * the collection's tail block: when the tail fills and a new tail block is born, `topicId` changes and
12
+ * the cohort-topic layer treats it as an entirely new topic. The whole-tree migration that follows is
13
+ * the rotation ticket's concern; this module owns only the pure derivation.
14
+ *
15
+ * The resulting `topicId` is fed verbatim into cohort-topic tier addressing (`coord_d(self, topicId)`),
16
+ * so it is derived with the **same** {@link IRingHash} primitive cohort-topic uses for `coord_d` input —
17
+ * db-core's own SHA-256 truncated to the ring width, **not** a FRET import. The trailing `"reactivity"`
18
+ * literal domain-separates the reactivity tree from any other application anchoring on the same tail id.
19
+ *
20
+ * Concatenation is delimiter-free, exactly as the spec writes it; the constant suffix is unambiguous
21
+ * because no tail-id byte string can alias a `(tailId, "reactivity")` pair of a different length.
22
+ *
23
+ * `reactivityTopicId` is the small pure helper the ticket asks for once and the rotation ticket
24
+ * ([reactivity-rotation-backpressure-policy]) reuses per-emission — define it here, not at the call site.
25
+ */
26
+
27
+ import { createRingHash } from "../cohort-topic/ring-hash.js";
28
+ import type { IRingHash } from "../cohort-topic/ports.js";
29
+
30
+ /** Domain-separation suffix mixed into every reactivity anchor. */
31
+ const REACTIVITY_SUFFIX = "reactivity";
32
+
33
+ const utf8 = new TextEncoder();
34
+
35
+ /**
36
+ * `H(tailId ‖ "reactivity")` over the injected ring hash — the cohort-topic `topicId` for the
37
+ * reactivity tree anchored on `tailId` (the collection's current tail block id, as raw bytes).
38
+ *
39
+ * The shared helper consumed by subscriber attachment, origination, and (per-emission) the rotation
40
+ * ticket. `hash` defaults to db-core's own 256-bit SHA-256, byte-identical to the cohort-topic host's.
41
+ */
42
+ export function reactivityTopicId(tailId: Uint8Array, hash: IRingHash = createRingHash()): Uint8Array {
43
+ const suffixBytes = utf8.encode(REACTIVITY_SUFFIX);
44
+ const input = new Uint8Array(tailId.length + suffixBytes.length);
45
+ input.set(tailId, 0);
46
+ input.set(suffixBytes, tailId.length);
47
+ return hash.H(input);
48
+ }
49
+
50
+ /** Derives the rotating `topicId` for a collection's current tail. */
51
+ export interface ReactivityTopicAnchor {
52
+ /** `H(tailId ‖ "reactivity")` for the given tail block id (bytes). */
53
+ topicId(tailId: Uint8Array): Uint8Array;
54
+ }
55
+
56
+ class HashReactivityTopicAnchor implements ReactivityTopicAnchor {
57
+ constructor(private readonly hash: IRingHash) {}
58
+
59
+ topicId(tailId: Uint8Array): Uint8Array {
60
+ return reactivityTopicId(tailId, this.hash);
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Build a {@link ReactivityTopicAnchor} over the injected hash. db-p2p passes the same {@link IRingHash}
66
+ * it binds to FRET's `RING_BITS` so the anchor and cohort-topic routing keys line up; the default
67
+ * constructs db-core's own {@link createRingHash} (256-bit SHA-256).
68
+ */
69
+ export function createReactivityTopicAnchor(hash: IRingHash = createRingHash()): ReactivityTopicAnchor {
70
+ return new HashReactivityTopicAnchor(hash);
71
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Reactivity — subscriber/forwarder notification verification seam
3
+ * (`docs/reactivity.md` §Propagation, §Delivery, §Authentication).
4
+ *
5
+ * A forwarder and a subscriber both verify a notification's threshold signature against the **tail
6
+ * cohort's** `MembershipCertV1` before trusting it — forwarders never re-sign, so the same end-to-end
7
+ * signature is verified regardless of hop count. The standard cohort-topic membership-snapshot path
8
+ * ({@link MembershipVerifier}) already provides the **one fetch-and-retry** on a stale/missing cached
9
+ * cert, so this module is a thin adapter: derive the tail cohort's `coord_0(_, topicId)` from the
10
+ * notification's `tailId`, then hand `(signers, coord, tier, digest, sig)` to the verifier.
11
+ *
12
+ * The signed payload is the commit `digest` (the commit cert's threshold signature is over the commit
13
+ * hash — see {@link import("./notification.js").buildNotificationV1}). `signers` arrive base64url-encoded
14
+ * as the cohort member-id bytes the verifier compares against `cert.members`; a custom `signersToBytes`
15
+ * seam is exposed for bindings that carry signers in a different encoding.
16
+ */
17
+
18
+ import { createTierAddressing, DEFAULT_FANOUT } from "../cohort-topic/addressing.js";
19
+ import { createRingHash } from "../cohort-topic/ring-hash.js";
20
+ import { b64urlToBytes } from "../cohort-topic/wire/codec.js";
21
+ import { Tier } from "../cohort-topic/tiers.js";
22
+ import type { IRingHash } from "../cohort-topic/ports.js";
23
+ import type { MembershipVerifier, VerifyResult } from "../cohort-topic/membership/verifier.js";
24
+ import { reactivityTopicId } from "./topic-anchor.js";
25
+ import type { NotificationV1 } from "./wire.js";
26
+
27
+ /** Verifies a {@link NotificationV1}'s threshold signature against the tail cohort's membership. */
28
+ export interface NotificationVerifier {
29
+ /** `"verified"` iff `sig` is a valid `≥ minSigs` cohort signature over the commit digest. */
30
+ verify(n: NotificationV1): Promise<VerifyResult>;
31
+ }
32
+
33
+ /** Construction inputs for the default {@link NotificationVerifier}. */
34
+ export interface NotificationVerifierDeps {
35
+ /** The cohort-topic participant-side membership verifier (owns the one fetch-and-retry). */
36
+ readonly verifier: MembershipVerifier;
37
+ /** Ring hash for `topicId` / `coord_0` derivation. Default db-core 256-bit SHA-256. */
38
+ readonly hash?: IRingHash;
39
+ /** Fan-out `F` for tier addressing. Default {@link DEFAULT_FANOUT}. */
40
+ readonly fanout?: number;
41
+ /** Reactivity runs at T3; overridable for tests. */
42
+ readonly tier?: Tier;
43
+ /** Map a wire signer string to the verifier's member-id bytes. Default base64url decode. */
44
+ readonly signersToBytes?: (signer: string) => Uint8Array;
45
+ }
46
+
47
+ class MembershipNotificationVerifier implements NotificationVerifier {
48
+ private readonly addressing: ReturnType<typeof createTierAddressing>;
49
+ private readonly hash: IRingHash;
50
+ private readonly tier: Tier;
51
+ private readonly signersToBytes: (signer: string) => Uint8Array;
52
+
53
+ constructor(private readonly deps: NotificationVerifierDeps) {
54
+ this.hash = deps.hash ?? createRingHash();
55
+ this.addressing = createTierAddressing(this.hash, deps.fanout ?? DEFAULT_FANOUT);
56
+ this.tier = deps.tier ?? Tier.T3;
57
+ this.signersToBytes = deps.signersToBytes ?? b64urlToBytes;
58
+ }
59
+
60
+ async verify(n: NotificationV1): Promise<VerifyResult> {
61
+ const topicId = reactivityTopicId(b64urlToBytes(n.tailId), this.hash);
62
+ const expectedCoord = this.addressing.coord0(topicId);
63
+ const signers = n.signers.map(this.signersToBytes);
64
+ const payload = b64urlToBytes(n.digest);
65
+ const sig = b64urlToBytes(n.sig);
66
+ return this.deps.verifier.verifyMessage(signers, expectedCoord, this.tier, payload, sig);
67
+ }
68
+ }
69
+
70
+ /** Build the default {@link NotificationVerifier} over the cohort-topic {@link MembershipVerifier}. */
71
+ export function createNotificationVerifier(deps: NotificationVerifierDeps): NotificationVerifier {
72
+ return new MembershipNotificationVerifier(deps);
73
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Reactivity — shared wire-validation primitives.
3
+ *
4
+ * The reactivity message codecs ({@link import("./wire.js")}, {@link import("./push-state.js")},
5
+ * {@link import("./backfill.js")}, {@link import("./resume.js")}) all decode untrusted JSON into
6
+ * validated V1 shapes with the same structural checks. Those primitives live here so each codec module
7
+ * narrows fields the same way (DRY) — every helper throws {@link CohortWireError} on a defect, matching
8
+ * the cohort-topic wire conventions (base64url byte fields, finite/integer numbers, `v: 1`).
9
+ */
10
+
11
+ import { b64urlToBytes } from "../cohort-topic/wire/codec.js";
12
+ import { CohortWireError } from "../cohort-topic/wire/validate.js";
13
+
14
+ /** Throw a {@link CohortWireError} with `message`. */
15
+ export function failWire(message: string): never {
16
+ throw new CohortWireError(message);
17
+ }
18
+
19
+ /** Narrow `value` to a plain object (not null, not an array), or throw. */
20
+ export function asObject(value: unknown, what: string): Record<string, unknown> {
21
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
22
+ failWire(`${what}: expected an object`);
23
+ }
24
+ return value as Record<string, unknown>;
25
+ }
26
+
27
+ /** Require `obj.v === 1`. */
28
+ export function requireV1(obj: Record<string, unknown>, what: string): void {
29
+ if (obj["v"] !== 1) {
30
+ failWire(`${what}: expected v === 1, got ${JSON.stringify(obj["v"])}`);
31
+ }
32
+ }
33
+
34
+ /** Require `obj[key]` to be a string; returns it. */
35
+ export function reqString(obj: Record<string, unknown>, key: string, what: string): string {
36
+ const value = obj[key];
37
+ if (typeof value !== "string") {
38
+ failWire(`${what}: field "${key}" must be a string`);
39
+ }
40
+ return value;
41
+ }
42
+
43
+ /** Require `obj[key]` to be a boolean; returns it. */
44
+ export function reqBoolean(obj: Record<string, unknown>, key: string, what: string): boolean {
45
+ const value = obj[key];
46
+ if (typeof value !== "boolean") {
47
+ failWire(`${what}: field "${key}" must be a boolean`);
48
+ }
49
+ return value;
50
+ }
51
+
52
+ /** Require `obj[key]` to be a finite number; returns it. */
53
+ export function reqFiniteNumber(obj: Record<string, unknown>, key: string, what: string): number {
54
+ const value = obj[key];
55
+ if (typeof value !== "number" || !Number.isFinite(value)) {
56
+ failWire(`${what}: field "${key}" must be a finite number`);
57
+ }
58
+ return value;
59
+ }
60
+
61
+ /** Require `obj[key]` to be an integer `>= min` (and `<= max` when supplied); returns it. */
62
+ export function reqIntInRange(obj: Record<string, unknown>, key: string, what: string, min: number, max?: number): number {
63
+ const value = reqFiniteNumber(obj, key, what);
64
+ if (!Number.isInteger(value) || value < min || (max !== undefined && value > max)) {
65
+ const bound = max === undefined ? `>= ${min}` : `in ${min}..${max}`;
66
+ failWire(`${what}: field "${key}" must be an integer ${bound}, got ${value}`);
67
+ }
68
+ return value;
69
+ }
70
+
71
+ /** Assert a base64url string decodes cleanly; returns it unchanged. */
72
+ export function b64urlField(value: string, key: string, what: string): string {
73
+ try {
74
+ b64urlToBytes(value);
75
+ } catch {
76
+ failWire(`${what}: field "${key}" is not valid base64url`);
77
+ }
78
+ return value;
79
+ }
80
+
81
+ /** Require `value` to be an array of strings; returns it (callers map each element through {@link b64urlField}). */
82
+ export function reqStringArray(value: unknown, key: string, what: string): string[] {
83
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
84
+ failWire(`${what}: field "${key}" must be an array of strings`);
85
+ }
86
+ return value as string[];
87
+ }
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Reactivity — V1 wire types, codecs, and per-message validation.
3
+ *
4
+ * Transcribed from `docs/reactivity.md` §Wire formats. Reactivity reuses the cohort-topic `RegisterV1`
5
+ * / `RenewV1` envelopes **unchanged**; the reactivity-specific shapes live in two places:
6
+ *
7
+ * 1. **Subscribe app payload** ({@link SubscribeAppPayloadV1}) carried opaquely inside
8
+ * `RegisterV1.appPayload`. Serialized to UTF-8 JSON bytes and handed to the cohort-topic
9
+ * {@link import("../cohort-topic/service.js").RegisterRequest}`.appPayload` slot, which base64url-encodes
10
+ * it on the wire. It is therefore **not** length-framed here — the cohort-topic `RegisterV1` frame
11
+ * wraps it. The subscribe RPC's authentication (peer-key signature over `correlationId` + `timestamp`)
12
+ * is the cohort-topic `RegisterV1` envelope's job, so the payload itself carries no signature.
13
+ *
14
+ * 2. **Notification** ({@link NotificationV1}) sent as a standalone RPC over the reactivity application
15
+ * protocol. It rides the same length-prefixed UTF-8 JSON framing as cohort-topic messages
16
+ * ({@link encodeCohortMessage}). Its `sig` is **bit-for-bit** the commit certificate's threshold
17
+ * signature — reactivity never re-signs (`docs/reactivity.md` §Notification origination).
18
+ *
19
+ * Conventions (matching the cohort-topic wire conventions): all JSON, byte fields base64url (no
20
+ * padding), unix-millisecond timestamps, per-message structural validation on decode, byte-fidelity
21
+ * round-trips (encode→decode→encode is stable). `collectionId` / `tailId` travel as the collection's
22
+ * base64url block ids verbatim. The `BackfillV1` / `ResumeV1` codecs belong to the sibling tickets.
23
+ */
24
+
25
+ import {
26
+ decodeCohortMessage,
27
+ encodeCohortMessage,
28
+ DEFAULT_MAX_MESSAGE_BYTES,
29
+ } from "../cohort-topic/wire/codec.js";
30
+ import {
31
+ asObject,
32
+ b64urlField,
33
+ failWire as fail,
34
+ reqBoolean,
35
+ reqFiniteNumber,
36
+ reqIntInRange,
37
+ reqString,
38
+ reqStringArray,
39
+ requireV1,
40
+ } from "./wire-validate.js";
41
+
42
+ const utf8Encoder = new TextEncoder();
43
+ const utf8Decoder = new TextDecoder("utf-8", { fatal: true });
44
+
45
+ /** Ceiling for an opaque subscribe app payload, guarding decode allocation. */
46
+ export const DEFAULT_MAX_SUBSCRIBE_PAYLOAD_BYTES = 64 * 1024;
47
+
48
+ // --- Subscribe app payload (carried inside cohort-topic RegisterV1.appPayload) ---
49
+
50
+ /** A subscriber's registration payload (`RegisterV1.appPayload`, `kind == "reactivity"`). */
51
+ export interface SubscribeAppPayloadV1 {
52
+ kind: "reactivity";
53
+ /** Stable collection identity, base64url (the collection's id block id). */
54
+ collectionId: string;
55
+ /** Tail block id at registration time, base64url (the subscriber-side rotation detector). */
56
+ tailIdAtAttach: string;
57
+ /** Last revision the subscriber already holds; `0` for a fresh subscribe. */
58
+ lastKnownRev: number;
59
+ /** Max delta bytes the subscriber accepts; `0` declines delta payloads (Edge). */
60
+ deltaMaxBytes: number;
61
+ }
62
+
63
+ // --- Notification (length-framed RPC) ---
64
+
65
+ /** The tail cohort's rotation pre-announce, carried inside (and signed by) a {@link NotificationV1}. */
66
+ export interface RotationHintV1 {
67
+ /** New tail block id the topic anchor is rotating to, base64url. */
68
+ newTailId: string;
69
+ /** Revision at which the new tail becomes effective. */
70
+ effectiveAtRevision: number;
71
+ }
72
+
73
+ /** A signed change notification fanned out through the reactivity tree (`docs/reactivity.md` §Notification origination). */
74
+ export interface NotificationV1 {
75
+ v: 1;
76
+ /** Collection id, base64url. */
77
+ collectionId: string;
78
+ /** Tail block id the reactivity topic is anchored on, base64url. */
79
+ tailId: string;
80
+ /** Per-collection monotonic revision. */
81
+ revision: number;
82
+ /**
83
+ * The commit-vote **signed payload** `utf8(commitHash + ":approve")`, base64url — the exact bytes the
84
+ * threshold signature {@link sig} was computed over. A subscriber threshold-verifies {@link sig} over
85
+ * `b64urlToBytes(digest)`, so this reproduces the signed image against real Ed25519.
86
+ */
87
+ digest: string;
88
+ /** Optional bounded delta, base64url; omitted when `delta_max == 0` or the collection declines it. */
89
+ delta?: string;
90
+ /** Unix ms. */
91
+ timestamp: number;
92
+ /** Threshold signature, base64url — **= the commit cert**, never re-signed. */
93
+ sig: string;
94
+ /** PeerIds contributing to {@link sig}, base64url of the cohort member-id bytes. */
95
+ signers: string[];
96
+ /** Tail-rotation pre-announce, when this notification carries one (rotation ticket fills it). */
97
+ rotationHint?: RotationHintV1;
98
+ /**
99
+ * `true` iff this notification announces a durable **invalidation** (a reversal of a previously
100
+ * committed action proven invalid by dispute) rather than an ordinary commit
101
+ * (`docs/reactivity.md` §Notification origination, `docs/right-is-right.md` §Durable Invalidation).
102
+ * An invalidation is a committed collection change like any other and rides the same notification
103
+ * path, reusing the **invalidation's** commit cert as {@link sig} (never re-signed) — so it verifies
104
+ * against the tail cohort exactly like a commit notification. The flag is a hint that lets a
105
+ * subscriber react (drop derived results + resubmit) rather than merely refresh; a subscriber that
106
+ * ignores it still re-reads the authoritative reverted state. Omitted (falsy) on commit notifications.
107
+ */
108
+ invalidation?: boolean;
109
+ /**
110
+ * When {@link invalidation} is set, the original action id that was reversed
111
+ * (`InvalidationEntry.invalidatedActionId`). Lets a client dedup/coalesce the several cascade
112
+ * notifications a single dispute can produce by the action they reverse. Omitted on commits.
113
+ */
114
+ invalidatedActionId?: string;
115
+ }
116
+
117
+ // --- validation helpers ---
118
+ // Generic primitives live in ./wire-validate.js (shared across the reactivity codecs); the
119
+ // notification-specific narrowing stays here next to the type it produces.
120
+
121
+ function validateRotationHint(value: unknown, what: string): RotationHintV1 {
122
+ const obj = asObject(value, what);
123
+ return {
124
+ newTailId: b64urlField(reqString(obj, "newTailId", what), "newTailId", what),
125
+ effectiveAtRevision: reqIntInRange(obj, "effectiveAtRevision", what, 0),
126
+ };
127
+ }
128
+
129
+ // --- Subscribe app payload (opaque RegisterV1.appPayload bytes) ---
130
+
131
+ /** Narrow an already-parsed value to {@link SubscribeAppPayloadV1}, throwing on any defect. */
132
+ export function validateSubscribeAppPayloadV1(value: unknown): SubscribeAppPayloadV1 {
133
+ const what = "SubscribeAppPayloadV1";
134
+ const obj = asObject(value, what);
135
+ if (obj["kind"] !== "reactivity") {
136
+ fail(`${what}: field "kind" must be exactly "reactivity"`);
137
+ }
138
+ return {
139
+ kind: "reactivity",
140
+ collectionId: b64urlField(reqString(obj, "collectionId", what), "collectionId", what),
141
+ tailIdAtAttach: b64urlField(reqString(obj, "tailIdAtAttach", what), "tailIdAtAttach", what),
142
+ lastKnownRev: reqIntInRange(obj, "lastKnownRev", what, 0),
143
+ deltaMaxBytes: reqIntInRange(obj, "deltaMaxBytes", what, 0),
144
+ };
145
+ }
146
+
147
+ /** Serialize a {@link SubscribeAppPayloadV1} to the opaque UTF-8 JSON bytes for `RegisterV1.appPayload`. */
148
+ export function encodeSubscribeAppPayload(payload: SubscribeAppPayloadV1): Uint8Array {
149
+ return utf8Encoder.encode(JSON.stringify(validateSubscribeAppPayloadV1(payload)));
150
+ }
151
+
152
+ /** Decode opaque `RegisterV1.appPayload` bytes back to a validated {@link SubscribeAppPayloadV1}. */
153
+ export function decodeSubscribeAppPayload(bytes: Uint8Array, maxBytes: number = DEFAULT_MAX_SUBSCRIBE_PAYLOAD_BYTES): SubscribeAppPayloadV1 {
154
+ return validateSubscribeAppPayloadV1(parseJsonBytes(bytes, maxBytes, "SubscribeAppPayloadV1"));
155
+ }
156
+
157
+ /** Parse opaque (non-framed) UTF-8 JSON payload bytes, rejecting oversized/invalid input. */
158
+ function parseJsonBytes(bytes: Uint8Array, maxBytes: number, what: string): unknown {
159
+ if (bytes.length > maxBytes) {
160
+ fail(`${what}: payload ${bytes.length} exceeds max ${maxBytes} bytes`);
161
+ }
162
+ let text: string;
163
+ try {
164
+ text = utf8Decoder.decode(bytes);
165
+ } catch {
166
+ fail(`${what}: payload is not valid UTF-8`);
167
+ }
168
+ try {
169
+ return JSON.parse(text) as unknown;
170
+ } catch {
171
+ fail(`${what}: payload is not valid JSON`);
172
+ }
173
+ }
174
+
175
+ // --- Notification (length-framed) ---
176
+
177
+ /** Narrow an already-parsed value to {@link NotificationV1}, throwing on any defect. */
178
+ export function validateNotificationV1(value: unknown): NotificationV1 {
179
+ const what = "NotificationV1";
180
+ const obj = asObject(value, what);
181
+ requireV1(obj, what);
182
+ const signers = reqStringArray(obj["signers"], "signers", what);
183
+ const out: NotificationV1 = {
184
+ v: 1,
185
+ collectionId: b64urlField(reqString(obj, "collectionId", what), "collectionId", what),
186
+ tailId: b64urlField(reqString(obj, "tailId", what), "tailId", what),
187
+ revision: reqIntInRange(obj, "revision", what, 0),
188
+ digest: b64urlField(reqString(obj, "digest", what), "digest", what),
189
+ timestamp: reqFiniteNumber(obj, "timestamp", what),
190
+ sig: b64urlField(reqString(obj, "sig", what), "sig", what),
191
+ signers: signers.map((s, i) => b64urlField(s, `signers[${i}]`, what)),
192
+ };
193
+ if (obj["delta"] !== undefined) {
194
+ out.delta = b64urlField(reqString(obj, "delta", what), "delta", what);
195
+ }
196
+ if (obj["rotationHint"] !== undefined) {
197
+ out.rotationHint = validateRotationHint(obj["rotationHint"], `${what}.rotationHint`);
198
+ }
199
+ if (obj["invalidation"] !== undefined) {
200
+ out.invalidation = reqBoolean(obj, "invalidation", what);
201
+ }
202
+ if (obj["invalidatedActionId"] !== undefined) {
203
+ out.invalidatedActionId = reqString(obj, "invalidatedActionId", what);
204
+ }
205
+ return out;
206
+ }
207
+
208
+ /** Validate an array of {@link NotificationV1} (the `entries`/`recentEntries` carried in backfill/resume replies). */
209
+ export function validateNotificationArray(value: unknown, what: string): NotificationV1[] {
210
+ if (!Array.isArray(value)) {
211
+ fail(`${what}: field "entries" must be an array`);
212
+ }
213
+ return value.map((entry) => validateNotificationV1(entry));
214
+ }
215
+
216
+ /** Encode a {@link NotificationV1} as a length-prefixed UTF-8 JSON frame. */
217
+ export function encodeNotificationV1(msg: NotificationV1, maxMessageBytes: number = DEFAULT_MAX_MESSAGE_BYTES): Uint8Array {
218
+ return encodeCohortMessage(validateNotificationV1(msg), maxMessageBytes);
219
+ }
220
+
221
+ /** Decode a length-prefixed {@link NotificationV1} frame. */
222
+ export function decodeNotificationV1(bytes: Uint8Array, maxMessageBytes?: number): NotificationV1 {
223
+ return validateNotificationV1(decodeCohortMessage(bytes, maxMessageBytes));
224
+ }
@@ -94,11 +94,15 @@ export class TransactionCoordinator {
94
94
  }
95
95
 
96
96
  /**
97
- * Commit a transaction (actions already applied, orchestrate PEND/COMMIT).
97
+ * Commit a transaction: materialise a log entry from each collection's staged
98
+ * pending actions, then orchestrate the distributed consensus (GATHER/PEND/COMMIT).
98
99
  *
99
- * This is called by TransactionSession.commit() after all statements have been executed.
100
- * Actions have already been applied to collections via applyActions(), so this method
101
- * just orchestrates the distributed consensus.
100
+ * Called by TransactionSession.commit() after all statements have executed. The
101
+ * staged mutations already live in each collection's tracker applied either via
102
+ * applyActions() (engine-driven path) or directly via Collection.act()/Tree.stage
103
+ * (the vtab's deferred-DML path) — but in BOTH cases without a log entry yet, so
104
+ * this method appends that entry here (see the inline note below) before pending,
105
+ * and folds the committed transforms back into each collection's read cache.
102
106
  *
103
107
  * @param transaction - The transaction to commit
104
108
  */
@@ -107,7 +111,7 @@ export class TransactionCoordinator {
107
111
  throw new Error(`Transaction expired at ${transaction.stamp.expiration}`);
108
112
  }
109
113
 
110
- // Collect transforms and determine critical blocks for each affected collection
114
+ // Collect collections with staged (un-synced) changes.
111
115
  const collectionData = Array.from(this.collections.entries())
112
116
  .map(([collectionId, collection]) => ({
113
117
  collectionId,
@@ -124,30 +128,38 @@ export class TransactionCoordinator {
124
128
  return; // Nothing to commit
125
129
  }
126
130
 
127
- // Get critical block IDs (log tail) for each affected collection
128
- // The critical block is the current log tail that must participate in consensus
131
+ // Append each collection's staged actions to its log, then collect the
132
+ // resulting transforms + critical (log-tail) block for consensus.
133
+ //
134
+ // The actions were staged directly into the trackers (Collection.act, e.g.
135
+ // via Tree.stage) WITHOUT first appending a log entry, so — exactly as
136
+ // execute()/applyActionsToCollection does — we materialise the log entry
137
+ // here from each collection's pending actions. Reading raw tracker
138
+ // transforms without a fresh log entry only ever "worked" for a
139
+ // collection's pristine first commit (where the initial empty log block is
140
+ // itself an uncommitted tracker insert); it broke for any collection with
141
+ // prior committed state — a pre-synced index tree, or a second commit on
142
+ // the same collection — whose log tail lives in storage, not the tracker.
143
+ const allCollectionIds = collectionData.map(({ collectionId }) => collectionId);
129
144
  const collectionTransforms = new Map<CollectionId, Transforms>();
130
145
  const criticalBlocks = new Map<CollectionId, BlockId>();
131
146
 
132
- for (const { collectionId, collection, transforms } of collectionData) {
133
- collectionTransforms.set(collectionId, transforms);
134
-
135
- // Get the current log tail block ID (critical block)
136
- const log = await Log.open(collection.tracker, collectionId);
137
- if (!log) {
138
- throw new Error(`Log not found for collection ${collectionId}`);
139
- }
140
-
141
- const tailPath = await (log as unknown as { chain: { getTail: () => Promise<{ block: { header: { id: BlockId } } } | undefined> } }).chain.getTail();
142
- if (tailPath) {
143
- criticalBlocks.set(collectionId, tailPath.block.header.id);
147
+ for (const { collectionId, collection } of collectionData) {
148
+ const applyResult = await this.applyActionsToCollection(
149
+ { collectionId, actions: collection.getPendingActions() },
150
+ transaction,
151
+ allCollectionIds
152
+ );
153
+ if (!applyResult.success) {
154
+ throw new Error(`Transaction commit failed: ${applyResult.error}`);
144
155
  }
156
+ collectionTransforms.set(collectionId, applyResult.transforms!);
157
+ criticalBlocks.set(collectionId, applyResult.logTailBlockId!);
145
158
  }
146
159
 
147
- // Compute hash of ALL operations across ALL collections
148
- // This hash is used for validation - validators re-execute the transaction
149
- // and compare their computed operations hash with this one
150
- const allOperations = collectionData.flatMap(({ collectionId, transforms }) => [
160
+ // Compute hash of ALL operations across ALL collections (post-log-append).
161
+ // Validators re-execute the transaction and compare their computed hash.
162
+ const allOperations = Array.from(collectionTransforms.entries()).flatMap(([collectionId, transforms]) => [
151
163
  ...Object.entries(transforms.inserts ?? {}).map(([blockId, block]) =>
152
164
  ({ type: 'insert' as const, collectionId, blockId, block })
153
165
  ),
@@ -173,8 +185,14 @@ export class TransactionCoordinator {
173
185
  throw new Error(`Transaction commit failed: ${coordResult.error}`);
174
186
  }
175
187
 
176
- // Reset trackers and update actionContext after successful commit
177
- for (const { collection } of collectionData) {
188
+ // Advance actionContext, fold the committed transforms into each
189
+ // collection's read cache, reset the tracker, and drop the now-committed
190
+ // pending actions. Order matters: cache the committed blocks BEFORE
191
+ // resetting the tracker (the transforms are read live), so a collection
192
+ // with prior committed state (a pre-synced index, or any second commit)
193
+ // serves the new revision instead of the stale cached one. Clearing
194
+ // pending keeps a subsequent commit from re-logging these actions.
195
+ for (const { collectionId, collection } of collectionData) {
178
196
  const newRev = (collection['source'].actionContext?.rev ?? 0) + 1;
179
197
  collection['source'].actionContext = {
180
198
  committed: [
@@ -183,7 +201,9 @@ export class TransactionCoordinator {
183
201
  ],
184
202
  rev: newRev,
185
203
  };
204
+ collection.applyCommittedToCache(collectionTransforms.get(collectionId)!);
186
205
  collection.tracker.reset();
206
+ collection.clearPendingActions();
187
207
  }
188
208
 
189
209
  // Clean up stamp tracking data
@@ -512,13 +532,19 @@ export class TransactionCoordinator {
512
532
  const actionId = transaction.id;
513
533
  const newRev = (collection['source'].actionContext?.rev ?? 0) + 1;
514
534
 
515
- // Add actions to log (this updates the tracker with log block changes)
535
+ // Add actions to log (this updates the tracker with log block changes).
536
+ // Persist the transaction's read set on the entry so a later invalidation cascade can
537
+ // discover this action's read-dependents (see ActionEntry.reads). The whole transaction's
538
+ // reads are recorded on every collection's entry: a read may target a block in another
539
+ // collection, and the cascade matches read-dependents by (blockId, revision) regardless of
540
+ // which collection's log the dependent landed in.
516
541
  const addResult = await log.addActions(
517
542
  collectionActions.actions,
518
543
  actionId,
519
544
  newRev,
520
545
  () => blockIdsForTransforms(transforms),
521
- allCollectionIds
546
+ allCollectionIds,
547
+ transaction.reads
522
548
  );
523
549
 
524
550
  // Return the transforms and log tail block ID