@optimystic/db-p2p 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 (352) hide show
  1. package/README.md +6 -6
  2. package/dist/src/cluster/block-transfer-service.d.ts +67 -8
  3. package/dist/src/cluster/block-transfer-service.d.ts.map +1 -1
  4. package/dist/src/cluster/block-transfer-service.js +101 -62
  5. package/dist/src/cluster/block-transfer-service.js.map +1 -1
  6. package/dist/src/cluster/client.d.ts +18 -1
  7. package/dist/src/cluster/client.d.ts.map +1 -1
  8. package/dist/src/cluster/client.js +55 -39
  9. package/dist/src/cluster/client.js.map +1 -1
  10. package/dist/src/cluster/cluster-error.d.ts +38 -0
  11. package/dist/src/cluster/cluster-error.d.ts.map +1 -0
  12. package/dist/src/cluster/cluster-error.js +51 -0
  13. package/dist/src/cluster/cluster-error.js.map +1 -0
  14. package/dist/src/cluster/cluster-repo.d.ts +135 -3
  15. package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
  16. package/dist/src/cluster/cluster-repo.js +333 -58
  17. package/dist/src/cluster/cluster-repo.js.map +1 -1
  18. package/dist/src/cluster/commit-cert.d.ts +63 -0
  19. package/dist/src/cluster/commit-cert.d.ts.map +1 -0
  20. package/dist/src/cluster/commit-cert.js +108 -0
  21. package/dist/src/cluster/commit-cert.js.map +1 -0
  22. package/dist/src/cluster/service.d.ts +46 -11
  23. package/dist/src/cluster/service.d.ts.map +1 -1
  24. package/dist/src/cluster/service.js +97 -33
  25. package/dist/src/cluster/service.js.map +1 -1
  26. package/dist/src/cluster/spread-on-churn.d.ts +13 -0
  27. package/dist/src/cluster/spread-on-churn.d.ts.map +1 -1
  28. package/dist/src/cluster/spread-on-churn.js +34 -4
  29. package/dist/src/cluster/spread-on-churn.js.map +1 -1
  30. package/dist/src/cohort-topic/bootstrap-evidence-builder.d.ts +59 -0
  31. package/dist/src/cohort-topic/bootstrap-evidence-builder.d.ts.map +1 -0
  32. package/dist/src/cohort-topic/bootstrap-evidence-builder.js +79 -0
  33. package/dist/src/cohort-topic/bootstrap-evidence-builder.js.map +1 -0
  34. package/dist/src/cohort-topic/bootstrap-evidence-verifiers.d.ts +81 -0
  35. package/dist/src/cohort-topic/bootstrap-evidence-verifiers.d.ts.map +1 -0
  36. package/dist/src/cohort-topic/bootstrap-evidence-verifiers.js +93 -0
  37. package/dist/src/cohort-topic/bootstrap-evidence-verifiers.js.map +1 -0
  38. package/dist/src/cohort-topic/bootstrap-parent-reference.d.ts +108 -0
  39. package/dist/src/cohort-topic/bootstrap-parent-reference.d.ts.map +1 -0
  40. package/dist/src/cohort-topic/bootstrap-parent-reference.js +108 -0
  41. package/dist/src/cohort-topic/bootstrap-parent-reference.js.map +1 -0
  42. package/dist/src/cohort-topic/change-bridge.d.ts +65 -0
  43. package/dist/src/cohort-topic/change-bridge.d.ts.map +1 -0
  44. package/dist/src/cohort-topic/change-bridge.js +76 -0
  45. package/dist/src/cohort-topic/change-bridge.js.map +1 -0
  46. package/dist/src/cohort-topic/cohort-gossip-driver.d.ts +74 -0
  47. package/dist/src/cohort-topic/cohort-gossip-driver.d.ts.map +1 -0
  48. package/dist/src/cohort-topic/cohort-gossip-driver.js +87 -0
  49. package/dist/src/cohort-topic/cohort-gossip-driver.js.map +1 -0
  50. package/dist/src/cohort-topic/cohort-gossip-transport.d.ts +42 -0
  51. package/dist/src/cohort-topic/cohort-gossip-transport.d.ts.map +1 -0
  52. package/dist/src/cohort-topic/cohort-gossip-transport.js +57 -0
  53. package/dist/src/cohort-topic/cohort-gossip-transport.js.map +1 -0
  54. package/dist/src/cohort-topic/fret-trust-anchor.d.ts +100 -0
  55. package/dist/src/cohort-topic/fret-trust-anchor.d.ts.map +1 -0
  56. package/dist/src/cohort-topic/fret-trust-anchor.js +115 -0
  57. package/dist/src/cohort-topic/fret-trust-anchor.js.map +1 -0
  58. package/dist/src/cohort-topic/host.d.ts +573 -0
  59. package/dist/src/cohort-topic/host.d.ts.map +1 -0
  60. package/dist/src/cohort-topic/host.js +1460 -0
  61. package/dist/src/cohort-topic/host.js.map +1 -0
  62. package/dist/src/cohort-topic/index.d.ts +14 -0
  63. package/dist/src/cohort-topic/index.d.ts.map +1 -0
  64. package/dist/src/cohort-topic/index.js +14 -0
  65. package/dist/src/cohort-topic/index.js.map +1 -0
  66. package/dist/src/cohort-topic/membership-publish-sink.d.ts +14 -0
  67. package/dist/src/cohort-topic/membership-publish-sink.d.ts.map +1 -0
  68. package/dist/src/cohort-topic/membership-publish-sink.js +17 -0
  69. package/dist/src/cohort-topic/membership-publish-sink.js.map +1 -0
  70. package/dist/src/cohort-topic/membership-source.d.ts +36 -0
  71. package/dist/src/cohort-topic/membership-source.d.ts.map +1 -0
  72. package/dist/src/cohort-topic/membership-source.js +58 -0
  73. package/dist/src/cohort-topic/membership-source.js.map +1 -0
  74. package/dist/src/cohort-topic/peer-codec.d.ts +18 -0
  75. package/dist/src/cohort-topic/peer-codec.d.ts.map +1 -0
  76. package/dist/src/cohort-topic/peer-codec.js +26 -0
  77. package/dist/src/cohort-topic/peer-codec.js.map +1 -0
  78. package/dist/src/cohort-topic/peer-sig.d.ts +52 -0
  79. package/dist/src/cohort-topic/peer-sig.d.ts.map +1 -0
  80. package/dist/src/cohort-topic/peer-sig.js +83 -0
  81. package/dist/src/cohort-topic/peer-sig.js.map +1 -0
  82. package/dist/src/cohort-topic/protocols.d.ts +49 -0
  83. package/dist/src/cohort-topic/protocols.d.ts.map +1 -0
  84. package/dist/src/cohort-topic/protocols.js +58 -0
  85. package/dist/src/cohort-topic/protocols.js.map +1 -0
  86. package/dist/src/cohort-topic/reactivity-membership-gate.d.ts +49 -0
  87. package/dist/src/cohort-topic/reactivity-membership-gate.d.ts.map +1 -0
  88. package/dist/src/cohort-topic/reactivity-membership-gate.js +42 -0
  89. package/dist/src/cohort-topic/reactivity-membership-gate.js.map +1 -0
  90. package/dist/src/cohort-topic/size-estimator.d.ts +16 -0
  91. package/dist/src/cohort-topic/size-estimator.d.ts.map +1 -0
  92. package/dist/src/cohort-topic/size-estimator.js +16 -0
  93. package/dist/src/cohort-topic/size-estimator.js.map +1 -0
  94. package/dist/src/cohort-topic/stream-util.d.ts +19 -0
  95. package/dist/src/cohort-topic/stream-util.d.ts.map +1 -0
  96. package/dist/src/cohort-topic/stream-util.js +76 -0
  97. package/dist/src/cohort-topic/stream-util.js.map +1 -0
  98. package/dist/src/cohort-topic/threshold-crypto.d.ts +94 -0
  99. package/dist/src/cohort-topic/threshold-crypto.d.ts.map +1 -0
  100. package/dist/src/cohort-topic/threshold-crypto.js +178 -0
  101. package/dist/src/cohort-topic/threshold-crypto.js.map +1 -0
  102. package/dist/src/cohort-topic/topic-router.d.ts +41 -0
  103. package/dist/src/cohort-topic/topic-router.d.ts.map +1 -0
  104. package/dist/src/cohort-topic/topic-router.js +63 -0
  105. package/dist/src/cohort-topic/topic-router.js.map +1 -0
  106. package/dist/src/dispute/cascade.d.ts +179 -0
  107. package/dist/src/dispute/cascade.d.ts.map +1 -0
  108. package/dist/src/dispute/cascade.js +315 -0
  109. package/dist/src/dispute/cascade.js.map +1 -0
  110. package/dist/src/dispute/client.d.ts +2 -1
  111. package/dist/src/dispute/client.d.ts.map +1 -1
  112. package/dist/src/dispute/client.js +12 -3
  113. package/dist/src/dispute/client.js.map +1 -1
  114. package/dist/src/dispute/dispute-service.d.ts +41 -1
  115. package/dist/src/dispute/dispute-service.d.ts.map +1 -1
  116. package/dist/src/dispute/dispute-service.js +147 -21
  117. package/dist/src/dispute/dispute-service.js.map +1 -1
  118. package/dist/src/dispute/index.d.ts +2 -0
  119. package/dist/src/dispute/index.d.ts.map +1 -1
  120. package/dist/src/dispute/index.js +2 -0
  121. package/dist/src/dispute/index.js.map +1 -1
  122. package/dist/src/dispute/invalidation.d.ts +266 -0
  123. package/dist/src/dispute/invalidation.d.ts.map +1 -0
  124. package/dist/src/dispute/invalidation.js +405 -0
  125. package/dist/src/dispute/invalidation.js.map +1 -0
  126. package/dist/src/dispute/types.d.ts +14 -0
  127. package/dist/src/dispute/types.d.ts.map +1 -1
  128. package/dist/src/dispute/types.js.map +1 -1
  129. package/dist/src/index.d.ts +4 -0
  130. package/dist/src/index.d.ts.map +1 -1
  131. package/dist/src/index.js +4 -0
  132. package/dist/src/index.js.map +1 -1
  133. package/dist/src/libp2p-key-network.d.ts +8 -1
  134. package/dist/src/libp2p-key-network.d.ts.map +1 -1
  135. package/dist/src/libp2p-key-network.js +67 -8
  136. package/dist/src/libp2p-key-network.js.map +1 -1
  137. package/dist/src/libp2p-node-base.d.ts +57 -3
  138. package/dist/src/libp2p-node-base.d.ts.map +1 -1
  139. package/dist/src/libp2p-node-base.js +523 -6
  140. package/dist/src/libp2p-node-base.js.map +1 -1
  141. package/dist/src/matchmaking/aggregate-counts.d.ts +67 -0
  142. package/dist/src/matchmaking/aggregate-counts.d.ts.map +1 -0
  143. package/dist/src/matchmaking/aggregate-counts.js +60 -0
  144. package/dist/src/matchmaking/aggregate-counts.js.map +1 -0
  145. package/dist/src/matchmaking/index.d.ts +20 -0
  146. package/dist/src/matchmaking/index.d.ts.map +1 -0
  147. package/dist/src/matchmaking/index.js +20 -0
  148. package/dist/src/matchmaking/index.js.map +1 -0
  149. package/dist/src/matchmaking/module.d.ts +181 -0
  150. package/dist/src/matchmaking/module.d.ts.map +1 -0
  151. package/dist/src/matchmaking/module.js +221 -0
  152. package/dist/src/matchmaking/module.js.map +1 -0
  153. package/dist/src/matchmaking/protocols.d.ts +37 -0
  154. package/dist/src/matchmaking/protocols.d.ts.map +1 -0
  155. package/dist/src/matchmaking/protocols.js +42 -0
  156. package/dist/src/matchmaking/protocols.js.map +1 -0
  157. package/dist/src/matchmaking/provider-manager.d.ts +52 -0
  158. package/dist/src/matchmaking/provider-manager.d.ts.map +1 -0
  159. package/dist/src/matchmaking/provider-manager.js +74 -0
  160. package/dist/src/matchmaking/provider-manager.js.map +1 -0
  161. package/dist/src/matchmaking/query-handler.d.ts +34 -0
  162. package/dist/src/matchmaking/query-handler.d.ts.map +1 -0
  163. package/dist/src/matchmaking/query-handler.js +62 -0
  164. package/dist/src/matchmaking/query-handler.js.map +1 -0
  165. package/dist/src/matchmaking/query-transport.d.ts +180 -0
  166. package/dist/src/matchmaking/query-transport.d.ts.map +1 -0
  167. package/dist/src/matchmaking/query-transport.js +315 -0
  168. package/dist/src/matchmaking/query-transport.js.map +1 -0
  169. package/dist/src/matchmaking/seeker-manager.d.ts +39 -0
  170. package/dist/src/matchmaking/seeker-manager.d.ts.map +1 -0
  171. package/dist/src/matchmaking/seeker-manager.js +49 -0
  172. package/dist/src/matchmaking/seeker-manager.js.map +1 -0
  173. package/dist/src/matchmaking/seeker-walk-client.d.ts +127 -0
  174. package/dist/src/matchmaking/seeker-walk-client.d.ts.map +1 -0
  175. package/dist/src/matchmaking/seeker-walk-client.js +189 -0
  176. package/dist/src/matchmaking/seeker-walk-client.js.map +1 -0
  177. package/dist/src/matchmaking/traffic-validation.d.ts +128 -0
  178. package/dist/src/matchmaking/traffic-validation.d.ts.map +1 -0
  179. package/dist/src/matchmaking/traffic-validation.js +105 -0
  180. package/dist/src/matchmaking/traffic-validation.js.map +1 -0
  181. package/dist/src/network/network-manager-service.d.ts +4 -2
  182. package/dist/src/network/network-manager-service.d.ts.map +1 -1
  183. package/dist/src/network/network-manager-service.js +9 -3
  184. package/dist/src/network/network-manager-service.js.map +1 -1
  185. package/dist/src/protocol-client.d.ts +25 -0
  186. package/dist/src/protocol-client.d.ts.map +1 -1
  187. package/dist/src/protocol-client.js +126 -4
  188. package/dist/src/protocol-client.js.map +1 -1
  189. package/dist/src/reactivity/forwarder-host.d.ts +198 -0
  190. package/dist/src/reactivity/forwarder-host.d.ts.map +1 -0
  191. package/dist/src/reactivity/forwarder-host.js +355 -0
  192. package/dist/src/reactivity/forwarder-host.js.map +1 -0
  193. package/dist/src/reactivity/index.d.ts +19 -0
  194. package/dist/src/reactivity/index.d.ts.map +1 -0
  195. package/dist/src/reactivity/index.js +19 -0
  196. package/dist/src/reactivity/index.js.map +1 -0
  197. package/dist/src/reactivity/notify-transport.d.ts +70 -0
  198. package/dist/src/reactivity/notify-transport.d.ts.map +1 -0
  199. package/dist/src/reactivity/notify-transport.js +111 -0
  200. package/dist/src/reactivity/notify-transport.js.map +1 -0
  201. package/dist/src/reactivity/origination-manager.d.ts +110 -0
  202. package/dist/src/reactivity/origination-manager.d.ts.map +1 -0
  203. package/dist/src/reactivity/origination-manager.js +129 -0
  204. package/dist/src/reactivity/origination-manager.js.map +1 -0
  205. package/dist/src/reactivity/protocols.d.ts +43 -0
  206. package/dist/src/reactivity/protocols.d.ts.map +1 -0
  207. package/dist/src/reactivity/protocols.js +50 -0
  208. package/dist/src/reactivity/protocols.js.map +1 -0
  209. package/dist/src/reactivity/push-state-gossip.d.ts +132 -0
  210. package/dist/src/reactivity/push-state-gossip.d.ts.map +1 -0
  211. package/dist/src/reactivity/push-state-gossip.js +231 -0
  212. package/dist/src/reactivity/push-state-gossip.js.map +1 -0
  213. package/dist/src/reactivity/recover-transport.d.ts +167 -0
  214. package/dist/src/reactivity/recover-transport.d.ts.map +1 -0
  215. package/dist/src/reactivity/recover-transport.js +308 -0
  216. package/dist/src/reactivity/recover-transport.js.map +1 -0
  217. package/dist/src/reactivity/rotation-rereg-scheduler.d.ts +142 -0
  218. package/dist/src/reactivity/rotation-rereg-scheduler.d.ts.map +1 -0
  219. package/dist/src/reactivity/rotation-rereg-scheduler.js +223 -0
  220. package/dist/src/reactivity/rotation-rereg-scheduler.js.map +1 -0
  221. package/dist/src/reactivity/subscriber-registry.d.ts +47 -0
  222. package/dist/src/reactivity/subscriber-registry.d.ts.map +1 -0
  223. package/dist/src/reactivity/subscriber-registry.js +87 -0
  224. package/dist/src/reactivity/subscriber-registry.js.map +1 -0
  225. package/dist/src/reactivity/subscription-manager.d.ts +221 -0
  226. package/dist/src/reactivity/subscription-manager.d.ts.map +1 -0
  227. package/dist/src/reactivity/subscription-manager.js +302 -0
  228. package/dist/src/reactivity/subscription-manager.js.map +1 -0
  229. package/dist/src/reactivity/topic-bytes.d.ts +32 -0
  230. package/dist/src/reactivity/topic-bytes.d.ts.map +1 -0
  231. package/dist/src/reactivity/topic-bytes.js +34 -0
  232. package/dist/src/reactivity/topic-bytes.js.map +1 -0
  233. package/dist/src/repo/client.d.ts.map +1 -1
  234. package/dist/src/repo/client.js +51 -19
  235. package/dist/src/repo/client.js.map +1 -1
  236. package/dist/src/repo/cluster-coordinator.d.ts +9 -0
  237. package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
  238. package/dist/src/repo/cluster-coordinator.js +65 -22
  239. package/dist/src/repo/cluster-coordinator.js.map +1 -1
  240. package/dist/src/repo/coordinator-repo.d.ts +20 -0
  241. package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
  242. package/dist/src/repo/coordinator-repo.js +101 -16
  243. package/dist/src/repo/coordinator-repo.js.map +1 -1
  244. package/dist/src/repo/service.d.ts +46 -1
  245. package/dist/src/repo/service.d.ts.map +1 -1
  246. package/dist/src/repo/service.js +79 -45
  247. package/dist/src/repo/service.js.map +1 -1
  248. package/dist/src/rpc-deadline.d.ts +37 -0
  249. package/dist/src/rpc-deadline.d.ts.map +1 -0
  250. package/dist/src/rpc-deadline.js +31 -0
  251. package/dist/src/rpc-deadline.js.map +1 -0
  252. package/dist/src/storage/block-storage.d.ts +2 -0
  253. package/dist/src/storage/block-storage.d.ts.map +1 -1
  254. package/dist/src/storage/block-storage.js +95 -2
  255. package/dist/src/storage/block-storage.js.map +1 -1
  256. package/dist/src/storage/i-block-storage.d.ts +25 -0
  257. package/dist/src/storage/i-block-storage.d.ts.map +1 -1
  258. package/dist/src/storage/storage-repo.d.ts +53 -2
  259. package/dist/src/storage/storage-repo.d.ts.map +1 -1
  260. package/dist/src/storage/storage-repo.js +193 -9
  261. package/dist/src/storage/storage-repo.js.map +1 -1
  262. package/dist/src/sync/client.d.ts +4 -1
  263. package/dist/src/sync/client.d.ts.map +1 -1
  264. package/dist/src/sync/client.js +5 -2
  265. package/dist/src/sync/client.js.map +1 -1
  266. package/dist/src/testing/cohort-topic-mesh-harness.d.ts +219 -0
  267. package/dist/src/testing/cohort-topic-mesh-harness.d.ts.map +1 -0
  268. package/dist/src/testing/cohort-topic-mesh-harness.js +467 -0
  269. package/dist/src/testing/cohort-topic-mesh-harness.js.map +1 -0
  270. package/dist/src/testing/matchmaking-mesh-harness.d.ts +187 -0
  271. package/dist/src/testing/matchmaking-mesh-harness.d.ts.map +1 -0
  272. package/dist/src/testing/matchmaking-mesh-harness.js +358 -0
  273. package/dist/src/testing/matchmaking-mesh-harness.js.map +1 -0
  274. package/dist/src/testing/mesh-harness.d.ts.map +1 -1
  275. package/dist/src/testing/mesh-harness.js +23 -1
  276. package/dist/src/testing/mesh-harness.js.map +1 -1
  277. package/dist/src/testing/reactivity-mesh-harness.d.ts +298 -0
  278. package/dist/src/testing/reactivity-mesh-harness.d.ts.map +1 -0
  279. package/dist/src/testing/reactivity-mesh-harness.js +663 -0
  280. package/dist/src/testing/reactivity-mesh-harness.js.map +1 -0
  281. package/package.json +7 -4
  282. package/src/cluster/block-transfer-service.ts +133 -68
  283. package/src/cluster/client.ts +64 -45
  284. package/src/cluster/cluster-error.ts +64 -0
  285. package/src/cluster/cluster-repo.ts +405 -55
  286. package/src/cluster/commit-cert.ts +139 -0
  287. package/src/cluster/service.ts +116 -44
  288. package/src/cluster/spread-on-churn.ts +328 -285
  289. package/src/cohort-topic/bootstrap-evidence-builder.ts +122 -0
  290. package/src/cohort-topic/bootstrap-evidence-verifiers.ts +132 -0
  291. package/src/cohort-topic/bootstrap-parent-reference.ts +159 -0
  292. package/src/cohort-topic/change-bridge.ts +109 -0
  293. package/src/cohort-topic/cohort-gossip-driver.ts +144 -0
  294. package/src/cohort-topic/cohort-gossip-transport.ts +74 -0
  295. package/src/cohort-topic/fret-trust-anchor.ts +153 -0
  296. package/src/cohort-topic/host.ts +2175 -0
  297. package/src/cohort-topic/index.ts +13 -0
  298. package/src/cohort-topic/membership-publish-sink.ts +20 -0
  299. package/src/cohort-topic/membership-source.ts +68 -0
  300. package/src/cohort-topic/peer-codec.ts +31 -0
  301. package/src/cohort-topic/peer-sig.ts +86 -0
  302. package/src/cohort-topic/protocols.ts +71 -0
  303. package/src/cohort-topic/reactivity-membership-gate.ts +77 -0
  304. package/src/cohort-topic/size-estimator.ts +16 -0
  305. package/src/cohort-topic/stream-util.ts +87 -0
  306. package/src/cohort-topic/threshold-crypto.ts +239 -0
  307. package/src/cohort-topic/topic-router.ts +77 -0
  308. package/src/dispute/cascade.ts +517 -0
  309. package/src/dispute/client.ts +12 -2
  310. package/src/dispute/dispute-service.ts +171 -24
  311. package/src/dispute/index.ts +42 -0
  312. package/src/dispute/invalidation.ts +587 -0
  313. package/src/dispute/types.ts +14 -0
  314. package/src/index.ts +4 -0
  315. package/src/libp2p-key-network.ts +67 -9
  316. package/src/libp2p-node-base.ts +633 -14
  317. package/src/matchmaking/aggregate-counts.ts +104 -0
  318. package/src/matchmaking/index.ts +20 -0
  319. package/src/matchmaking/module.ts +363 -0
  320. package/src/matchmaking/protocols.ts +51 -0
  321. package/src/matchmaking/provider-manager.ts +95 -0
  322. package/src/matchmaking/query-handler.ts +88 -0
  323. package/src/matchmaking/query-transport.ts +483 -0
  324. package/src/matchmaking/seeker-manager.ts +64 -0
  325. package/src/matchmaking/seeker-walk-client.ts +293 -0
  326. package/src/matchmaking/traffic-validation.ts +195 -0
  327. package/src/network/network-manager-service.ts +9 -2
  328. package/src/protocol-client.ts +119 -5
  329. package/src/reactivity/forwarder-host.ts +438 -0
  330. package/src/reactivity/index.ts +19 -0
  331. package/src/reactivity/notify-transport.ts +144 -0
  332. package/src/reactivity/origination-manager.ts +192 -0
  333. package/src/reactivity/protocols.ts +61 -0
  334. package/src/reactivity/push-state-gossip.ts +291 -0
  335. package/src/reactivity/recover-transport.ts +408 -0
  336. package/src/reactivity/rotation-rereg-scheduler.ts +256 -0
  337. package/src/reactivity/subscriber-registry.ts +96 -0
  338. package/src/reactivity/subscription-manager.ts +450 -0
  339. package/src/reactivity/topic-bytes.ts +37 -0
  340. package/src/repo/client.ts +54 -19
  341. package/src/repo/cluster-coordinator.ts +66 -26
  342. package/src/repo/coordinator-repo.ts +99 -15
  343. package/src/repo/service.ts +90 -42
  344. package/src/rpc-deadline.ts +45 -0
  345. package/src/storage/block-storage.ts +106 -3
  346. package/src/storage/i-block-storage.ts +27 -0
  347. package/src/storage/storage-repo.ts +209 -13
  348. package/src/sync/client.ts +5 -2
  349. package/src/testing/cohort-topic-mesh-harness.ts +613 -0
  350. package/src/testing/matchmaking-mesh-harness.ts +475 -0
  351. package/src/testing/mesh-harness.ts +22 -2
  352. package/src/testing/reactivity-mesh-harness.ts +922 -0
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Reactivity recover RPC transport — the live libp2p backfill/resume recovery protocol
3
+ * (`docs/reactivity.md` §Backfill RPC, §Resume).
4
+ *
5
+ * This is the **pull** companion to the one-way notify transport: a subscriber that detected a gap (or
6
+ * woke from sleep) asks a serving cohort member "what did I miss?" and is brought current. The protocol is
7
+ * **request-reply** — one {@link RecoverRequestV1} frame out, one {@link RecoverReplyV1} frame back over a
8
+ * single stream — so it rides the cohort-topic {@link requestResponse} / {@link handleRequestResponse}
9
+ * helpers (NOT the one-way notify helpers, which would desync the dialer).
10
+ *
11
+ * Two pieces live here, both decoupled from the live node assembly so they unit-test in isolation:
12
+ *
13
+ * - {@link Libp2pReactivityRecoverTransport} — the **outbound** side. Supplies the two db-core seams
14
+ * (`BackfillTransport` / `ResumeTransport`) against one node: pick a target (sticky cohort-hint primary
15
+ * first for the one-RT happy path, else a cohort-walk member), frame the request, exchange it over the
16
+ * recover protocol, and return the inner reply. The wire exchange is injected as a {@link RecoverDialer}
17
+ * so the target-selection + framing logic tests without a real socket; {@link createLibp2pRecoverDialer}
18
+ * is the production libp2p-backed implementation.
19
+ * - {@link createRecoverRequestHandler} / {@link registerRecoverHandler} — the **inbound** serve handler.
20
+ * Decode (bounded) → verify the request's peer-key signature against the dialing peer → reject a
21
+ * replay/stale request → resolve the live `PushState` → `serveBackfill` / `serveResume` → reply. Any
22
+ * failure produces **no reply** (the stream aborts) rather than throwing out of the handler.
23
+ *
24
+ * ## Dial-target encoding bridge (load-bearing)
25
+ *
26
+ * The sticky {@link import("@optimystic/db-core").ReactivityCohortHint}`.primary` is **base64url of
27
+ * cohort member-id bytes** (`base64url(utf8(peerIdString))`), per its db-core JSDoc, whereas
28
+ * {@link requestResponse} needs a `peerIdFromString`-parseable peer-id **string**. Feeding the raw
29
+ * base64url straight to `peerIdFromString` throws → the dial is swallowed → recovery silently never
30
+ * reaches the cohort. {@link decodeCohortHintTarget} pins the conversion
31
+ * (`bytesToPeerIdString(b64urlToBytes(primary))`); the `resolveCohort` walk, by contrast, already returns
32
+ * peer-id strings (the cohort-topic `CohortPeerResolver` space) and is used as-is.
33
+ */
34
+ import { peerIdFromString } from "@libp2p/peer-id";
35
+ import { b64urlToBytes, bytesToB64url, reactivityTopicId, serveBackfill, serveResume, backfillSigningPayload, resumeSigningPayload, encodeRecoverRequestV1, decodeRecoverRequestV1, encodeRecoverReplyV1, decodeRecoverReplyV1, } from "@optimystic/db-core";
36
+ import { requestResponse, handleRequestResponse, DEFAULT_STREAM_MAX_BYTES } from "../cohort-topic/stream-util.js";
37
+ import { verifyPeerSig, signPeerSig } from "../cohort-topic/peer-sig.js";
38
+ import { peerIdToBytes, bytesToPeerIdString } from "../cohort-topic/peer-codec.js";
39
+ import { PROTOCOL_REACTIVITY_RECOVER } from "./protocols.js";
40
+ import { createLogger } from "../logger.js";
41
+ const log = createLogger("reactivity-recover");
42
+ /**
43
+ * Decode a sticky-cohort-hint `primary` (base64url of member-id bytes = `base64url(utf8(peerIdString))`)
44
+ * back to the dialable peer-id string. The inverse trap of feeding the raw value to `peerIdFromString`
45
+ * (which expects a peer-id string, not base64url-of-its-utf8) — pinned by the dial-target encoding test.
46
+ */
47
+ export function decodeCohortHintTarget(primary) {
48
+ return bytesToPeerIdString(b64urlToBytes(primary));
49
+ }
50
+ /**
51
+ * Build the subscriber's recover request signers from the node's libp2p Ed25519 private key.
52
+ *
53
+ * This resolves the recover wiring's one design point: the subscription-manager `signBackfill` /
54
+ * `signResume` ports are **synchronous** (`(unsigned) => string`) — the db-core driver builds the
55
+ * unsigned image internally, so a pre-signed value is impossible — but libp2p `PrivateKey.sign` is
56
+ * **async**. Rather than make the seam (and `createBackfillRequester`'s `sign`, a db-core change) async,
57
+ * sign with the synchronous {@link signPeerSig} (noble, over the raw Ed25519 seed) over db-core's
58
+ * canonical signing payloads. The produced signature verifies on the serving side under
59
+ * {@link verifyPeerSig} over the same bytes (both noble, RFC8032), so the round trip is symmetric.
60
+ */
61
+ export function createRecoverRequestSigners(privateKey) {
62
+ return {
63
+ signBackfill: (unsigned) => bytesToB64url(signPeerSig(privateKey, backfillSigningPayload(unsigned))),
64
+ signResume: (unsigned) => bytesToB64url(signPeerSig(privateKey, resumeSigningPayload(unsigned))),
65
+ };
66
+ }
67
+ // --- outbound transport ---
68
+ /**
69
+ * Thrown out of {@link Libp2pReactivityRecoverTransport.backfillTransport} / `resumeTransport` when the
70
+ * dialed cohort answered with a `kind: "rotated"` recover reply: the outgoing tail this request reached has
71
+ * rotated and is draining, so it bounced the request to the new tree (`docs/reactivity.md` §Tail rotation).
72
+ * It carries the {@link RotationRedirectV1} so the subscription manager can move itself to the successor.
73
+ *
74
+ * A `kind: "rotated"` reply is **terminal** for the cohort-walk — the dialed member spoke authoritatively for
75
+ * the cohort, so the transport stops rather than falling through to the next target (contrast a *dial
76
+ * failure*, which still falls through). The subscriber honors it like a notification-driven rotation.
77
+ */
78
+ export class RotationRedirectError extends Error {
79
+ /** The drain-window redirect the serving cohort returned (`newTailId` / `newTopicId` / `effectiveAtRevision`). */
80
+ redirect;
81
+ constructor(redirect) {
82
+ super(`reactivity recover: cohort rotated to tail ${redirect.newTailId} (effectiveAtRevision ${redirect.effectiveAtRevision})`);
83
+ this.name = "RotationRedirectError";
84
+ this.redirect = redirect;
85
+ }
86
+ }
87
+ /** Build the production libp2p-backed {@link RecoverDialer} over {@link requestResponse}. */
88
+ export function createLibp2pRecoverDialer(node, recoverProtocol = PROTOCOL_REACTIVITY_RECOVER, maxBytes = DEFAULT_STREAM_MAX_BYTES) {
89
+ return {
90
+ exchange: (target, frame) => requestResponse(node, peerIdFromString(target), recoverProtocol, frame, maxBytes),
91
+ };
92
+ }
93
+ /**
94
+ * The outbound recover transport: one instance per node, exposing the two db-core function seams against it.
95
+ * Each returned transport dials the **sticky primary first** (one round trip after a brief flap), falling
96
+ * back to a **cohort-walk** member on a dial failure (any member that holds the topic's gossiped `PushState`
97
+ * can answer). A kind mismatch or all-targets-failed surfaces as a rejection so the caller's retry/escalation
98
+ * policy (the subscription manager's backfill escalation, or `manager.resume()`'s caller) takes over.
99
+ */
100
+ export class Libp2pReactivityRecoverTransport {
101
+ dialer;
102
+ selfPeerId;
103
+ cohortHintCache;
104
+ resolveCohort;
105
+ maxBytes;
106
+ constructor(options) {
107
+ this.dialer = options.dialer;
108
+ this.selfPeerId = options.selfPeerId;
109
+ this.cohortHintCache = options.cohortHintCache;
110
+ this.resolveCohort = options.resolveCohort;
111
+ this.maxBytes = options.maxBytes ?? DEFAULT_STREAM_MAX_BYTES;
112
+ }
113
+ /** The db-core {@link BackfillTransport} for `(topicId, collectionId)` — frames + dials a signed {@link BackfillV1}. */
114
+ backfillTransport(topicId, collectionId) {
115
+ return async (req) => {
116
+ const frame = encodeRecoverRequestV1({ v: 1, kind: "backfill", backfill: req }, this.maxBytes);
117
+ const reply = await this.exchange("backfill", frame, topicId, collectionId);
118
+ if (reply.backfillReply === undefined) {
119
+ throw new Error("reactivity recover: backfill reply missing its body");
120
+ }
121
+ return reply.backfillReply;
122
+ };
123
+ }
124
+ /** The {@link ResumeTransport} for `(topicId, collectionId)` — frames + dials a signed {@link ResumeV1}. */
125
+ resumeTransport(topicId, collectionId) {
126
+ return async (req) => {
127
+ const frame = encodeRecoverRequestV1({ v: 1, kind: "resume", resume: req }, this.maxBytes);
128
+ const reply = await this.exchange("resume", frame, topicId, collectionId);
129
+ if (reply.resumeReply === undefined) {
130
+ throw new Error("reactivity recover: resume reply missing its body");
131
+ }
132
+ return reply.resumeReply;
133
+ };
134
+ }
135
+ /**
136
+ * Exchange one recover frame with the first reachable target: sticky primary, then each cohort-walk
137
+ * member. A **dial failure** falls through to the next candidate; a successful dial is **terminal** (the
138
+ * member answered for the cohort) — a `kind: "rotated"` redirect throws {@link RotationRedirectError}, and
139
+ * a reply decoding to the wrong `kind` is a protocol error. Throws when no candidate succeeds.
140
+ */
141
+ async exchange(kind, frame, topicId, collectionId) {
142
+ const targets = this.selectTargets(topicId, collectionId);
143
+ if (targets.length === 0) {
144
+ throw new Error("reactivity recover: no serving cohort target resolved");
145
+ }
146
+ let lastErr;
147
+ for (const target of targets) {
148
+ let replyFrame;
149
+ try {
150
+ replyFrame = await this.dialer.exchange(target, frame);
151
+ }
152
+ catch (err) {
153
+ lastErr = err; // dial failure → fall back to the next candidate (sticky → walk)
154
+ log("recover dial to %s failed, trying next target: %o", target, err);
155
+ continue;
156
+ }
157
+ const reply = decodeRecoverReplyV1(replyFrame, this.maxBytes); // a decode failure here is terminal
158
+ // A rotated cohort answered authoritatively: surface the redirect and stop the walk (terminal, never
159
+ // a fallthrough — the dialed member spoke for the cohort).
160
+ if (reply.kind === "rotated") {
161
+ throw new RotationRedirectError(reply.rotated);
162
+ }
163
+ if (reply.kind !== kind) {
164
+ throw new Error(`reactivity recover: reply kind "${reply.kind}" does not match request kind "${kind}"`);
165
+ }
166
+ return reply;
167
+ }
168
+ throw lastErr ?? new Error("reactivity recover: all targets failed");
169
+ }
170
+ /**
171
+ * Ordered dial candidates for a recover: the sticky primary (decoded from its base64url-of-bytes form)
172
+ * first, then the cohort-walk members. Self is never dialed (a co-located serve is the node wiring's
173
+ * concern), and duplicates collapse.
174
+ */
175
+ selectTargets(topicId, collectionId) {
176
+ const seen = new Set();
177
+ const targets = [];
178
+ const add = (target) => {
179
+ if (target === undefined || target === this.selfPeerId || seen.has(target)) {
180
+ return;
181
+ }
182
+ seen.add(target);
183
+ targets.push(target);
184
+ };
185
+ const hint = this.cohortHintCache.get(collectionId);
186
+ if (hint?.primary !== undefined) {
187
+ try {
188
+ add(decodeCohortHintTarget(hint.primary));
189
+ }
190
+ catch (err) {
191
+ log("recover: malformed sticky primary for %s, skipping to cohort-walk: %o", collectionId, err);
192
+ }
193
+ }
194
+ for (const member of this.resolveCohort(topicId)) {
195
+ add(member);
196
+ }
197
+ return targets;
198
+ }
199
+ }
200
+ /**
201
+ * Verify the request's peer-key signature against the dialing peer, then admit it through the freshness +
202
+ * anti-replay guard. The dialing peer's id **is** the signer (no signer-id field on the wire); the signature
203
+ * bytes are the anti-replay key (globally unique + authenticated). Returns `false` (reject, no reply) on a
204
+ * bad signature, a stale/future timestamp, or a replay.
205
+ */
206
+ function verifyAndAdmit(deps, signerId, payload, signatureB64, timestamp, now) {
207
+ const signature = b64urlToBytes(signatureB64);
208
+ if (!verifyPeerSig(signerId, payload, signature)) {
209
+ log("recover serve: signature verification failed for %s (no reply)", signerId);
210
+ return false;
211
+ }
212
+ if (!deps.replayGuard.accept(signature, peerIdToBytes(signerId), timestamp, now)) {
213
+ log("recover serve: replay/stale request from %s (no reply)", signerId);
214
+ return false;
215
+ }
216
+ return true;
217
+ }
218
+ /**
219
+ * Serve a backfill from the collection's current served tail, or `undefined` if this node serves none. When
220
+ * the node serves **only** the outgoing (draining) tail, `rotationFor` returns the drain redirect and the
221
+ * reply is `kind: "rotated"` instead — a best-effort secondary path (the primary mechanism for an active
222
+ * subscriber is notify-driven detection). Once both tails coexist `pushStateForCollection` resolves the new
223
+ * tail, so the redirect is emitted only while the old tail is the sole served state (see §Tail rotation).
224
+ */
225
+ function serveBackfillReply(deps, req, now) {
226
+ const maxBytes = deps.maxBytes ?? DEFAULT_STREAM_MAX_BYTES;
227
+ const redirect = deps.rotationFor?.({ collectionId: req.collectionId }, now);
228
+ if (redirect !== undefined) {
229
+ return encodeRecoverReplyV1({ v: 1, kind: "rotated", rotated: redirect }, maxBytes);
230
+ }
231
+ const ps = deps.pushStateForCollection(req.collectionId);
232
+ if (ps === undefined) {
233
+ return undefined; // not a serving member for this collection → no reply (subscriber walks/chain-reads)
234
+ }
235
+ const backfillReply = serveBackfill(ps.replayBuffer, req, ps.collectionId);
236
+ return encodeRecoverReplyV1({ v: 1, kind: "backfill", backfillReply }, maxBytes);
237
+ }
238
+ /**
239
+ * Serve a resume against the live `PushState`. A resume reaching the **outgoing (draining)** tail — its
240
+ * `latestKnownTailId` anchors a topic this node has marked rotated — is answered with the drain redirect
241
+ * (`kind: "rotated"`), moving the subscriber to the new tree. Otherwise prefer the exact topic the request's
242
+ * `latestKnownTailId` anchors (so a non-rotated subscriber classifies into backfill/checkpoint/out_of_window);
243
+ * if this node no longer serves that tail's topic, fall back to the collection's current tail so the cohort
244
+ * can still answer `tail_rotated` (its `currentTailId` differs from the request's stale tail) or, for a span
245
+ * that crosses a rotation, serve from the new tail's `inheritedCheckpoint`. `undefined` ⇒ no served state.
246
+ */
247
+ function serveResumeReply(deps, req, now) {
248
+ const maxBytes = deps.maxBytes ?? DEFAULT_STREAM_MAX_BYTES;
249
+ const staleTopic = reactivityTopicId(b64urlToBytes(req.latestKnownTailId));
250
+ const redirect = deps.rotationFor?.({ topicId: staleTopic, collectionId: req.collectionId }, now);
251
+ if (redirect !== undefined) {
252
+ return encodeRecoverReplyV1({ v: 1, kind: "rotated", rotated: redirect }, maxBytes);
253
+ }
254
+ const ps = deps.pushStateFor(staleTopic) ?? deps.pushStateForCollection(req.collectionId);
255
+ if (ps === undefined) {
256
+ return undefined;
257
+ }
258
+ const resumeReply = serveResume(req, {
259
+ buffer: ps.replayBuffer,
260
+ checkpoint: ps.checkpoint,
261
+ inheritedCheckpoint: ps.inheritedCheckpoint,
262
+ currentTailId: ps.tailIdAtJoin,
263
+ currentRevision: ps.lastRevision,
264
+ rotationRevision: ps.lastRevision,
265
+ expectedCollectionId: ps.collectionId,
266
+ });
267
+ return encodeRecoverReplyV1({ v: 1, kind: "resume", resumeReply }, maxBytes);
268
+ }
269
+ /**
270
+ * Build the recover serve callback for {@link handleRequestResponse}: it returns the reply frame, or
271
+ * `undefined` for **no reply** (a decode/verify/replay/resolve failure aborts the stream). It never throws
272
+ * out of the handler.
273
+ */
274
+ export function createRecoverRequestHandler(deps) {
275
+ const maxBytes = deps.maxBytes ?? DEFAULT_STREAM_MAX_BYTES;
276
+ const clock = deps.clock ?? (() => Date.now());
277
+ return (frame, fromPeer) => {
278
+ try {
279
+ const req = decodeRecoverRequestV1(frame, maxBytes);
280
+ const signerId = fromPeer.toString();
281
+ const now = clock();
282
+ if (req.kind === "backfill") {
283
+ const b = req.backfill;
284
+ if (b === undefined || !verifyAndAdmit(deps, signerId, backfillSigningPayload(b), b.signature, b.timestamp, now)) {
285
+ return Promise.resolve(undefined);
286
+ }
287
+ return Promise.resolve(serveBackfillReply(deps, b, now));
288
+ }
289
+ const r = req.resume;
290
+ if (r === undefined || !verifyAndAdmit(deps, signerId, resumeSigningPayload(r), r.signature, r.timestamp, now)) {
291
+ return Promise.resolve(undefined);
292
+ }
293
+ return Promise.resolve(serveResumeReply(deps, r, now));
294
+ }
295
+ catch (err) {
296
+ // A malformed/foreign request (decode failure, foreign collectionId from serve*) must never throw
297
+ // out of the stream handler: log + no reply (the stream aborts, the subscriber falls back).
298
+ log("recover serve: dropping request (no reply): %o", err);
299
+ return Promise.resolve(undefined);
300
+ }
301
+ };
302
+ }
303
+ /** Register the inbound recover protocol handler on `node` (request-reply over the recover protocol). */
304
+ export function registerRecoverHandler(node, protocol, deps) {
305
+ const maxBytes = deps.maxBytes ?? DEFAULT_STREAM_MAX_BYTES;
306
+ handleRequestResponse(node, protocol, createRecoverRequestHandler(deps), maxBytes);
307
+ }
308
+ //# sourceMappingURL=recover-transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recover-transport.js","sourceRoot":"","sources":["../../../src/reactivity/recover-transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Reactivity — tail-rotation re-registration timer host seam (`docs/reactivity.md` §Tail rotation step 3).
3
+ *
4
+ * The {@link import("./subscription-manager.js").ReactivitySubscriptionManager} surfaces a
5
+ * {@link import("./subscription-manager.js").RotationNotice} **once per successor tail** (both the
6
+ * notify-driven pre-announce and the recover-driven `RotationRedirectError` converge on the same
7
+ * `surfaceRotation` seam), but it neither schedules the timer nor performs the move. This host component
8
+ * does: it consumes a notice, schedules a one-shot timer for `max(0, plan.fireAt - now())`, and on fire
9
+ * invokes an injected `reRegister(plan)` seam that re-subscribes the watcher at the rotated tree. The node
10
+ * composition that constructs the manager + binds this scheduler to its `onRotation` observer lives in
11
+ * `reactivity-rotation-host-wiring-e2e`; this module is the standalone, unit-testable piece.
12
+ *
13
+ * **Where the stagger lives.** The spread that keeps the new tail from being flooded is entirely in
14
+ * `plan.fireAt`, drawn by the **manager's** `rejoinJitter`. This scheduler only *consumes* `plan.fireAt`; it
15
+ * never sees the jitter. The manager uses the *single*-subscriber planner `planReRegistration` →
16
+ * `RejoinJitter.scheduleRejoin`, which draws a **uniform** offset `now + ⌊U[0, T_rejoin_jitter)⌋` — so the
17
+ * load-bearing knob for this path is the **window** `T_rejoin_jitter` (default 30 s), not a `capPromote`. Each
18
+ * subscriber jitters independently, giving the new tail an inbound rate of ≈ `subscribers / T_rejoin_jitter`
19
+ * in expectation; the burst is absorbed on the *receiving* side by the new tail cohort's `cap_promote_fast`
20
+ * fast-promotion (a cohort-topic promotion mechanism — see `docs/reactivity.md` §Tail rotation rotation-cost
21
+ * and the Worked scenario), which is independent of the jitter's `capPromote`.
22
+ *
23
+ * Note that `RejoinJitter`'s `capPromote` is consulted **only** by the *wave* planner
24
+ * `scheduleWave` / `planReRegistrationWave` (a single host staggering a whole wave with a hard per-window
25
+ * ceiling — used by the mesh harness, not by the production manager). If the composing site
26
+ * (`reactivity-rotation-host-wiring-e2e`) ever switches the manager onto the wave planner, *then* it must
27
+ * build the jitter as `createRejoinJitter({ capPromote: DEFAULT_CAP_PROMOTE_FAST })` (= 32), since the default
28
+ * `createRejoinJitter()` cap is the cohort-failure `cap_promote = 64`. For the current single-planner path,
29
+ * setting `capPromote` has **no effect** on `fireAt`.
30
+ *
31
+ * **Determinism.** An injected `setTimer(fn, delayMs) => cancel` and `now()` clock make the scheduler
32
+ * testable with a fake clock (no wall clock). They default to a production binding: an **unref'd**
33
+ * `setTimeout`/`clearTimeout` and `Date.now`. The unref is load-bearing — an idle re-registration timer must
34
+ * not pin an otherwise-idle process, mirroring the push-state-gossip driver's unref'd timer
35
+ * ({@link import("./push-state-gossip.js").ReactivityPushStateGossipDriver}).
36
+ *
37
+ * **Idempotence.** De-duped by successor `newTopicId` (base64url): a second notice for a successor already
38
+ * scheduled or fired is ignored. The manager already fires once per successor (its `rotationHandledFor`
39
+ * guard), but a redirect and a pre-announce can surface the **same** successor near-simultaneously, so the
40
+ * scheduler is independently idempotent. The de-dupe ledger (`seen`) survives the timer fire (so a late
41
+ * re-surface of an already-fired successor stays a no-op), which on the normal schedule→fire path would let it
42
+ * grow without bound across a long-lived, fast-rotating subscription. It is therefore **bounded**: capped at
43
+ * `SEEN_LEDGER_CAP` entries with oldest-first eviction (insertion order), and eviction **never** drops a key
44
+ * still in `pending` — that would break the "every pending key ∈ `seen`" invariant and could let a duplicate
45
+ * notice for a pending successor arm a second timer over the live one. A re-surface older than the cap (≥ cap
46
+ * distinct successors later) degrades to at most one harmless, idempotent re-register.
47
+ *
48
+ * **Chained rotation OLD→A→B** before A's timer fires → A and B are distinct successors → two independent
49
+ * timers; **both may fire**. Re-registering at A and then immediately rotating A→B is self-correcting via the
50
+ * manager's `rotationHandledFor` guard, so a superseded timer is intentionally **not** cancelled (a
51
+ * superseded re-register is harmless and rare).
52
+ */
53
+ import { type ReRegistrationPlan } from "@optimystic/db-core";
54
+ import type { RotationNotice } from "./subscription-manager.js";
55
+ /**
56
+ * Upper bound on the idempotence ledger (`seen`). Far above any realistic count of *concurrently-pending*
57
+ * successors (single digits: 1 in the normal case, a handful across a chained rotation), so it never evicts a
58
+ * still-live entry in practice while keeping the ledger's memory to tens of KB of short base64url strings at the
59
+ * ceiling. Tunable — raising it widens the late-re-surface de-dupe window at a proportional memory cost.
60
+ * Exported for the regression test that asserts the bound; production callers don't read it.
61
+ */
62
+ export declare const SEEN_LEDGER_CAP = 1024;
63
+ /** Cancel handle returned by an injected timer; cancels a not-yet-fired timer (safe no-op after fire/cancel). */
64
+ export type RotationTimerCancel = () => void;
65
+ /** Construction inputs for a {@link RotationReRegistrationScheduler}. */
66
+ export interface RotationReRegistrationSchedulerOptions {
67
+ /**
68
+ * Perform the actual move: re-subscribe the watcher at the rotated tree (the new `topicId`, carrying the
69
+ * plan's `lastRevision`). A rejection is isolated + logged — a failed move must never throw out of the
70
+ * timer callback. Not retried this pass (see the class doc).
71
+ */
72
+ readonly reRegister: (plan: ReRegistrationPlan) => Promise<void>;
73
+ /**
74
+ * Schedule a one-shot timer; returns a cancel handle. Defaults to an **unref'd** `setTimeout`. Tests inject
75
+ * a fake timer queue for determinism.
76
+ */
77
+ readonly setTimer?: (fn: () => void, delayMs: number) => RotationTimerCancel;
78
+ /** Clock (Unix ms). Defaults to `Date.now`; tests inject a fake clock. */
79
+ readonly now?: () => number;
80
+ }
81
+ /**
82
+ * Hosts the per-successor one-shot timers that move a subscriber to the rotated tree. Construct one per
83
+ * subscription manager and bind {@link schedule} to the manager's
84
+ * {@link import("./subscription-manager.js").ReactivitySubscriptionManagerOptions.onRotation} observer.
85
+ *
86
+ * See the module doc for where the re-registration stagger actually lives (the manager's `rejoinJitter`; this
87
+ * scheduler only consumes `plan.fireAt`, it does not draw it).
88
+ */
89
+ export declare class RotationReRegistrationScheduler {
90
+ private readonly reRegister;
91
+ private readonly setTimer;
92
+ private readonly now;
93
+ /** Successors with a still-pending timer, keyed by base64url `newTopicId` → its cancel handle. */
94
+ private readonly pending;
95
+ /**
96
+ * Successors recently scheduled (pending **or** already fired) — the idempotence ledger that survives fire.
97
+ * Insertion-ordered and **bounded** at {@link SEEN_LEDGER_CAP}: {@link evictSeenOverCap} drops the oldest
98
+ * non-pending entries once over cap (never a still-pending key — see that method's doc).
99
+ */
100
+ private readonly seen;
101
+ private stopped;
102
+ constructor(options: RotationReRegistrationSchedulerOptions);
103
+ /** Successors with a timer still pending (not yet fired/cancelled). Diagnostic / test seam. */
104
+ get pendingCount(): number;
105
+ /**
106
+ * Size of the idempotence ledger (pending + recently-fired-and-retained). Diagnostic / test seam; the
107
+ * regression test asserts this stays ≤ {@link SEEN_LEDGER_CAP} under unbounded sequential rotations.
108
+ */
109
+ get seenCount(): number;
110
+ /**
111
+ * Schedule the re-registration timer for a rotation notice. De-duped by successor `newTopicId`: a second
112
+ * notice for an already-scheduled-or-fired successor is a no-op (a redirect and a pre-announce can surface
113
+ * the same successor). The delay is `max(0, plan.fireAt - now())`, so a `fireAt` already in the past fires
114
+ * on the next tick (clamped to 0, never negative). A no-op once {@link stop} has run.
115
+ */
116
+ schedule(notice: RotationNotice): void;
117
+ /**
118
+ * Cancel a single pending timer (by successor `newTopicId`) or, when called with no argument, **all**
119
+ * pending timers (teardown). Cancelling an already-fired or unknown successor is a safe no-op. A cancelled
120
+ * successor is forgotten (dropped from the idempotence ledger), so a later notice for it would reschedule;
121
+ * use {@link stop} for permanent teardown.
122
+ */
123
+ cancel(newTopicId?: Uint8Array): void;
124
+ /**
125
+ * Stop the scheduler permanently (teardown): cancel every pending timer and refuse all further scheduling
126
+ * and firing. A timer that races `stop` (its callback already queued) finds the `stopped` gate and never
127
+ * invokes `reRegister`. Idempotent.
128
+ */
129
+ stop(): void;
130
+ /**
131
+ * Bound the idempotence ledger: evict the oldest entries (insertion order) that are **not** still pending,
132
+ * until back within {@link SEEN_LEDGER_CAP}. Never evicts a pending key — that would break the "every pending
133
+ * key ∈ `seen`" invariant and could double-fire a successor whose later duplicate notice then re-armed a timer
134
+ * over the live one. If every over-cap entry is somehow still pending (`pending.size > cap` — not reachable in
135
+ * practice, since pending successors are the most recently scheduled), the loop bails and the ledger is left to
136
+ * grow with `pending` this round; it returns within cap as soon as those timers fire and a later schedule runs.
137
+ */
138
+ private evictSeenOverCap;
139
+ /** Timer callback: drop the pending entry, then invoke the isolated re-registration (unless stopped). */
140
+ private fire;
141
+ }
142
+ //# sourceMappingURL=rotation-rereg-scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rotation-rereg-scheduler.d.ts","sourceRoot":"","sources":["../../../src/reactivity/rotation-rereg-scheduler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAKhE;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC,iHAAiH;AACjH,MAAM,MAAM,mBAAmB,GAAG,MAAM,IAAI,CAAC;AAc7C,yEAAyE;AACzE,MAAM,WAAW,sCAAsC;IACtD;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,mBAAmB,CAAC;IAC7E,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,qBAAa,+BAA+B;IAC3C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA8C;IACzE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA2D;IACpF,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IAEnC,kGAAkG;IAClG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0C;IAClE;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;IAC1C,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,sCAAsC;IAM3D,+FAA+F;IAC/F,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;;OAGG;IACH,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAwBtC;;;;;OAKG;IACH,MAAM,CAAC,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI;IAkBrC;;;;OAIG;IACH,IAAI,IAAI,IAAI;IASZ;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IAiBxB,yGAAyG;IACzG,OAAO,CAAC,IAAI;CAmBZ"}