@optimystic/db-p2p 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 (342) 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 +7 -0
  134. package/dist/src/libp2p-key-network.d.ts.map +1 -1
  135. package/dist/src/libp2p-key-network.js +49 -5
  136. package/dist/src/libp2p-key-network.js.map +1 -1
  137. package/dist/src/libp2p-node-base.d.ts +45 -3
  138. package/dist/src/libp2p-node-base.d.ts.map +1 -1
  139. package/dist/src/libp2p-node-base.js +522 -5
  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 +13 -0
  186. package/dist/src/protocol-client.d.ts.map +1 -1
  187. package/dist/src/protocol-client.js +74 -2
  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 -23
  235. package/dist/src/repo/client.js.map +1 -1
  236. package/dist/src/repo/service.d.ts +46 -1
  237. package/dist/src/repo/service.d.ts.map +1 -1
  238. package/dist/src/repo/service.js +79 -45
  239. package/dist/src/repo/service.js.map +1 -1
  240. package/dist/src/rpc-deadline.d.ts +37 -0
  241. package/dist/src/rpc-deadline.d.ts.map +1 -0
  242. package/dist/src/rpc-deadline.js +31 -0
  243. package/dist/src/rpc-deadline.js.map +1 -0
  244. package/dist/src/storage/block-storage.d.ts +2 -0
  245. package/dist/src/storage/block-storage.d.ts.map +1 -1
  246. package/dist/src/storage/block-storage.js +95 -2
  247. package/dist/src/storage/block-storage.js.map +1 -1
  248. package/dist/src/storage/i-block-storage.d.ts +25 -0
  249. package/dist/src/storage/i-block-storage.d.ts.map +1 -1
  250. package/dist/src/storage/storage-repo.d.ts +53 -2
  251. package/dist/src/storage/storage-repo.d.ts.map +1 -1
  252. package/dist/src/storage/storage-repo.js +193 -9
  253. package/dist/src/storage/storage-repo.js.map +1 -1
  254. package/dist/src/sync/client.d.ts +4 -1
  255. package/dist/src/sync/client.d.ts.map +1 -1
  256. package/dist/src/sync/client.js +5 -2
  257. package/dist/src/sync/client.js.map +1 -1
  258. package/dist/src/testing/cohort-topic-mesh-harness.d.ts +219 -0
  259. package/dist/src/testing/cohort-topic-mesh-harness.d.ts.map +1 -0
  260. package/dist/src/testing/cohort-topic-mesh-harness.js +467 -0
  261. package/dist/src/testing/cohort-topic-mesh-harness.js.map +1 -0
  262. package/dist/src/testing/matchmaking-mesh-harness.d.ts +187 -0
  263. package/dist/src/testing/matchmaking-mesh-harness.d.ts.map +1 -0
  264. package/dist/src/testing/matchmaking-mesh-harness.js +358 -0
  265. package/dist/src/testing/matchmaking-mesh-harness.js.map +1 -0
  266. package/dist/src/testing/mesh-harness.d.ts.map +1 -1
  267. package/dist/src/testing/mesh-harness.js +23 -1
  268. package/dist/src/testing/mesh-harness.js.map +1 -1
  269. package/dist/src/testing/reactivity-mesh-harness.d.ts +298 -0
  270. package/dist/src/testing/reactivity-mesh-harness.d.ts.map +1 -0
  271. package/dist/src/testing/reactivity-mesh-harness.js +663 -0
  272. package/dist/src/testing/reactivity-mesh-harness.js.map +1 -0
  273. package/package.json +7 -4
  274. package/src/cluster/block-transfer-service.ts +133 -68
  275. package/src/cluster/client.ts +64 -45
  276. package/src/cluster/cluster-error.ts +64 -0
  277. package/src/cluster/cluster-repo.ts +405 -55
  278. package/src/cluster/commit-cert.ts +139 -0
  279. package/src/cluster/service.ts +116 -44
  280. package/src/cluster/spread-on-churn.ts +328 -285
  281. package/src/cohort-topic/bootstrap-evidence-builder.ts +122 -0
  282. package/src/cohort-topic/bootstrap-evidence-verifiers.ts +132 -0
  283. package/src/cohort-topic/bootstrap-parent-reference.ts +159 -0
  284. package/src/cohort-topic/change-bridge.ts +109 -0
  285. package/src/cohort-topic/cohort-gossip-driver.ts +144 -0
  286. package/src/cohort-topic/cohort-gossip-transport.ts +74 -0
  287. package/src/cohort-topic/fret-trust-anchor.ts +153 -0
  288. package/src/cohort-topic/host.ts +2175 -0
  289. package/src/cohort-topic/index.ts +13 -0
  290. package/src/cohort-topic/membership-publish-sink.ts +20 -0
  291. package/src/cohort-topic/membership-source.ts +68 -0
  292. package/src/cohort-topic/peer-codec.ts +31 -0
  293. package/src/cohort-topic/peer-sig.ts +86 -0
  294. package/src/cohort-topic/protocols.ts +71 -0
  295. package/src/cohort-topic/reactivity-membership-gate.ts +77 -0
  296. package/src/cohort-topic/size-estimator.ts +16 -0
  297. package/src/cohort-topic/stream-util.ts +87 -0
  298. package/src/cohort-topic/threshold-crypto.ts +239 -0
  299. package/src/cohort-topic/topic-router.ts +77 -0
  300. package/src/dispute/cascade.ts +517 -0
  301. package/src/dispute/client.ts +12 -2
  302. package/src/dispute/dispute-service.ts +171 -24
  303. package/src/dispute/index.ts +42 -0
  304. package/src/dispute/invalidation.ts +587 -0
  305. package/src/dispute/types.ts +14 -0
  306. package/src/index.ts +4 -0
  307. package/src/libp2p-key-network.ts +49 -6
  308. package/src/libp2p-node-base.ts +620 -12
  309. package/src/matchmaking/aggregate-counts.ts +104 -0
  310. package/src/matchmaking/index.ts +20 -0
  311. package/src/matchmaking/module.ts +363 -0
  312. package/src/matchmaking/protocols.ts +51 -0
  313. package/src/matchmaking/provider-manager.ts +95 -0
  314. package/src/matchmaking/query-handler.ts +88 -0
  315. package/src/matchmaking/query-transport.ts +483 -0
  316. package/src/matchmaking/seeker-manager.ts +64 -0
  317. package/src/matchmaking/seeker-walk-client.ts +293 -0
  318. package/src/matchmaking/traffic-validation.ts +195 -0
  319. package/src/network/network-manager-service.ts +9 -2
  320. package/src/protocol-client.ts +64 -3
  321. package/src/reactivity/forwarder-host.ts +438 -0
  322. package/src/reactivity/index.ts +19 -0
  323. package/src/reactivity/notify-transport.ts +144 -0
  324. package/src/reactivity/origination-manager.ts +192 -0
  325. package/src/reactivity/protocols.ts +61 -0
  326. package/src/reactivity/push-state-gossip.ts +291 -0
  327. package/src/reactivity/recover-transport.ts +408 -0
  328. package/src/reactivity/rotation-rereg-scheduler.ts +256 -0
  329. package/src/reactivity/subscriber-registry.ts +96 -0
  330. package/src/reactivity/subscription-manager.ts +450 -0
  331. package/src/reactivity/topic-bytes.ts +37 -0
  332. package/src/repo/client.ts +54 -23
  333. package/src/repo/service.ts +90 -42
  334. package/src/rpc-deadline.ts +45 -0
  335. package/src/storage/block-storage.ts +106 -3
  336. package/src/storage/i-block-storage.ts +27 -0
  337. package/src/storage/storage-repo.ts +209 -13
  338. package/src/sync/client.ts +5 -2
  339. package/src/testing/cohort-topic-mesh-harness.ts +613 -0
  340. package/src/testing/matchmaking-mesh-harness.ts +475 -0
  341. package/src/testing/mesh-harness.ts +22 -2
  342. package/src/testing/reactivity-mesh-harness.ts +922 -0
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Reactivity — `PushState` intra-cohort gossip driver (`docs/reactivity.md` §Replay window,
3
+ * §Forwarder-cohort state).
4
+ *
5
+ * The forwarder-cohort replay ring + dedupe window are the substrate that makes the doc's "**any** cohort
6
+ * member — not just the primary — can serve a replay/backfill" property real: a member that missed an
7
+ * origin dial still ends up holding the entry, so a backfill (12.4) survives a primary failover. The
8
+ * db-core {@link PushState} already owns the codec ({@link encodePushStateGossipV1} /
9
+ * {@link decodePushStateGossipV1}, {@link PushState.serializeGossip} / {@link PushState.mergeGossip}); this
10
+ * driver is the **replication** layer that actually broadcasts and merges that state over libp2p on a
11
+ * periodic cadence — the backstop to the live fan-out ({@link import("./forwarder-host.js")}).
12
+ *
13
+ * **Transport reuse.** Rather than stand up a new transport, this rides the cohort gossip transport's
14
+ * {@link import("../cohort-topic/cohort-gossip-transport.js").FretCohortGossipTransport.broadcastOver}
15
+ * seam — the same one the promote-notice broadcast reuses — so the cohort peer resolution, self-exclusion,
16
+ * and per-peer failure swallowing are shared. Frames ride the dedicated
17
+ * {@link PROTOCOL_REACTIVITY_PUSH_STATE_GOSSIP} (one-way), fanned to the FRET-assembled cohort around each
18
+ * served collection's cohort coord. Inbound frames arrive on this node's `push-state-gossip` protocol
19
+ * handler (registered by the node-wiring ticket) and route to {@link ReactivityPushStateGossipDriver.deliver}.
20
+ *
21
+ * **Cadence.** Its own unref'd timer (parallel to the cohort host's single `setInterval`), reusing
22
+ * {@link DEFAULT_GOSSIP_INTERVAL_MS} and the host's re-entrancy / `stopped` / `unref` discipline verbatim.
23
+ * Reactivity collections are not 1:1 with the host's per-`CoordEngine` tick and the forwarder host owns the
24
+ * live {@link PushState} set, so a separate timer is the clean seam now; a future consolidation could fold
25
+ * this into the host tick via a hook, but a separate unref'd timer cannot pin an idle process.
26
+ *
27
+ * The **driver** touches neither the libp2p node assembly nor FRET cohort assembly — it depends only on the
28
+ * `broadcastOver` seam + db-core codec, so it is unit-testable with a fake transport and real `PushState`s.
29
+ * The co-located {@link registerPushStateGossipHandler} (the inbound protocol-handler registration) does
30
+ * bind libp2p, mirroring {@link import("./notify-transport.js").registerNotifyHandler} — the driver class
31
+ * stays pure; only that one helper reaches the node.
32
+ */
33
+ import { encodePushStateGossipV1, decodePushStateGossipV1, } from "@optimystic/db-core";
34
+ import { readAllBounded } from "p2p-fret";
35
+ import { DEFAULT_GOSSIP_INTERVAL_MS } from "../cohort-topic/cohort-gossip-driver.js";
36
+ import { DEFAULT_STREAM_MAX_BYTES } from "../cohort-topic/stream-util.js";
37
+ import { PROTOCOL_REACTIVITY_PUSH_STATE_GOSSIP } from "./protocols.js";
38
+ import { createLogger } from "../logger.js";
39
+ const log = createLogger("reactivity-push-state-gossip");
40
+ /** Encode a probe frame with no size ceiling so the driver can measure its true length before bounding. */
41
+ const NO_FRAME_LIMIT = Number.MAX_SAFE_INTEGER;
42
+ /**
43
+ * Drives {@link PushState} convergence across a forwarder cohort: each round it broadcasts every live
44
+ * collection's serialized push-state to the cohort (clipped to the frame bound), and each inbound frame is
45
+ * gated, resolved to the owning collection, and merged. Build one per node and bind its inbound
46
+ * {@link ReactivityPushStateGossipDriver.deliver} to the `push-state-gossip` protocol handler.
47
+ */
48
+ export class ReactivityPushStateGossipDriver {
49
+ gossipTransport;
50
+ liveCollections;
51
+ pushStateForGossip;
52
+ isCohortMember;
53
+ intervalMs;
54
+ maxBytes;
55
+ onTruncate;
56
+ timer;
57
+ stopped = false;
58
+ /** Re-entrancy guard mirroring the host tick: skip a round that overlaps a slow prior one. */
59
+ rounding = false;
60
+ constructor(deps) {
61
+ this.gossipTransport = deps.gossipTransport;
62
+ this.liveCollections = deps.liveCollections;
63
+ this.pushStateForGossip = deps.pushStateForGossip;
64
+ this.isCohortMember = deps.isCohortMember;
65
+ this.intervalMs = deps.intervalMs ?? DEFAULT_GOSSIP_INTERVAL_MS;
66
+ this.maxBytes = deps.maxBytes ?? DEFAULT_STREAM_MAX_BYTES;
67
+ this.onTruncate = deps.onTruncate;
68
+ }
69
+ /** Begin the unref'd cadence timer. Idempotent; a no-op once {@link stop} has run. */
70
+ start() {
71
+ if (this.stopped || this.timer !== undefined) {
72
+ return;
73
+ }
74
+ const timer = setInterval(() => {
75
+ this.round();
76
+ }, this.intervalMs);
77
+ // Node timers keep the event loop alive; push-state gossip must not pin a process that is otherwise idle.
78
+ timer.unref?.();
79
+ this.timer = timer;
80
+ }
81
+ /**
82
+ * One round: broadcast each live collection's serialized {@link PushState} to its cohort, clipped to the
83
+ * frame bound. Per-collection isolated (one collection's fault never skips the rest) and `stopped`-gated
84
+ * mid-loop, mirroring the host tick.
85
+ */
86
+ round() {
87
+ if (this.stopped || this.rounding) {
88
+ return;
89
+ }
90
+ this.rounding = true;
91
+ try {
92
+ for (const { pushState, cohortCoord } of this.liveCollections()) {
93
+ if (this.stopped) {
94
+ break;
95
+ }
96
+ try {
97
+ this.broadcastOne(pushState, cohortCoord);
98
+ }
99
+ catch (err) {
100
+ log("push-state gossip round failed for a collection (isolated): %o", err);
101
+ }
102
+ }
103
+ }
104
+ finally {
105
+ this.rounding = false;
106
+ }
107
+ }
108
+ /**
109
+ * Inbound handler body: decode → membership gate → resolve owning collection → merge. Never throws on a
110
+ * bad frame (a malformed/forged/foreign frame is logged and dropped), so a stream handler can call it
111
+ * directly. `mergeGossip` independently guards a collection/topic mismatch, so the resolve step is a fast
112
+ * pre-filter, not the only line of defense.
113
+ */
114
+ deliver(fromPeerId, frame) {
115
+ let g;
116
+ try {
117
+ g = decodePushStateGossipV1(frame, this.maxBytes);
118
+ }
119
+ catch (err) {
120
+ log("dropped an undecodable push-state gossip frame from %s: %o", fromPeerId, err);
121
+ return;
122
+ }
123
+ if (this.isCohortMember !== undefined && !this.isCohortMember(fromPeerId, g)) {
124
+ log("dropped push-state gossip from non-member %s for collection=%s topic=%s", fromPeerId, g.collectionId, g.topicId);
125
+ return;
126
+ }
127
+ const pushState = this.pushStateForGossip(g);
128
+ if (pushState === undefined) {
129
+ return; // gossip for a collection this node does not serve — nothing to merge into.
130
+ }
131
+ try {
132
+ pushState.mergeGossip(g);
133
+ }
134
+ catch (err) {
135
+ log("push-state mergeGossip threw (isolated) for collection=%s: %o", g.collectionId, err);
136
+ }
137
+ }
138
+ /** Stop the cadence: short-circuit any in-flight/future round and clear the timer. */
139
+ stop() {
140
+ this.stopped = true;
141
+ if (this.timer !== undefined) {
142
+ clearInterval(this.timer);
143
+ this.timer = undefined;
144
+ }
145
+ }
146
+ /** Serialize one collection, clip to the frame bound, and broadcast to its cohort. */
147
+ broadcastOne(pushState, cohortCoord) {
148
+ const g = pushState.serializeGossip();
149
+ const { frame, kept, total } = this.boundedFrame(g);
150
+ if (kept < total) {
151
+ // "No silent caps" (AGENTS.md): a clipped ring is always surfaced. Convergence still completes —
152
+ // each entry replicates while it is within the most-recent window across successive rounds, and
153
+ // `mergeGossip` unions ring entries so a partial frame loses nothing already held by a peer.
154
+ log("push-state gossip frame clipped for collection=%s: kept %d/%d most-recent replay entries to fit maxBytes=%d", g.collectionId, kept, total, this.maxBytes);
155
+ this.onTruncate?.({ collectionId: g.collectionId, topicId: g.topicId, kept, total, frameBytes: frame.length, maxBytes: this.maxBytes });
156
+ }
157
+ this.gossipTransport.broadcastOver(PROTOCOL_REACTIVITY_PUSH_STATE_GOSSIP, cohortCoord, frame);
158
+ }
159
+ /**
160
+ * Build the broadcast frame for one serialized push-state, kept within {@link maxBytes}.
161
+ *
162
+ * The whole replay ring (`W` up to 256 full `NotificationV1`s) can exceed the frame bound. When it does,
163
+ * keep the full (small) dedupe window and the **most-recent-N** replay entries that fit — sliced from the
164
+ * high-revision end, since a lagging subscriber backfills the newest gap first and older revisions roll
165
+ * to the parent checkpoint anyway. Frame size is monotone in the entry count, so binary-search the
166
+ * high-water N rather than re-encoding per entry.
167
+ */
168
+ boundedFrame(g) {
169
+ const total = g.replayBuffer.entries.length;
170
+ const full = encodePushStateGossipV1(g, NO_FRAME_LIMIT);
171
+ if (full.length <= this.maxBytes) {
172
+ return { frame: full, kept: total, total };
173
+ }
174
+ // Largest most-recent-N replay slice whose frame still fits. `encodeSlice(g, 0)` (dedupe window only)
175
+ // is the floor we ship if even that overruns the bound (pathological — the dedupe window is small).
176
+ let lo = 0;
177
+ let hi = total;
178
+ let bestN = 0;
179
+ let bestFrame = this.encodeSlice(g, 0);
180
+ while (lo <= hi) {
181
+ const mid = (lo + hi) >>> 1;
182
+ const frame = this.encodeSlice(g, mid);
183
+ if (frame.length <= this.maxBytes) {
184
+ bestN = mid;
185
+ bestFrame = frame;
186
+ lo = mid + 1;
187
+ }
188
+ else {
189
+ hi = mid - 1;
190
+ }
191
+ }
192
+ return { frame: bestFrame, kept: bestN, total };
193
+ }
194
+ /** Encode `g` with only its most-recent `keep` replay entries retained (the full dedupe window is kept). */
195
+ encodeSlice(g, keep) {
196
+ const entries = g.replayBuffer.entries;
197
+ const kept = keep >= entries.length ? entries : entries.slice(entries.length - keep);
198
+ const sliced = {
199
+ ...g,
200
+ replayBuffer: { capacity: g.replayBuffer.capacity, entries: kept },
201
+ };
202
+ return encodePushStateGossipV1(sliced, NO_FRAME_LIMIT);
203
+ }
204
+ }
205
+ /**
206
+ * Register the inbound push-state-gossip protocol handler: read one bounded frame and hand it to
207
+ * {@link ReactivityPushStateGossipDriver.deliver} (which decodes, membership-gates, and merges), then close.
208
+ * One-way — no reply frame, matching the cohort-topic gossip handlers. A read error aborts the stream and
209
+ * {@link ReactivityPushStateGossipDriver.deliver} swallows a decode/forge/foreign failure, so the handler
210
+ * never throws on the stream. Mirrors {@link import("./notify-transport.js").registerNotifyHandler}.
211
+ */
212
+ export function registerPushStateGossipHandler(node, protocol, driver, maxBytes = DEFAULT_STREAM_MAX_BYTES) {
213
+ void node.handle(protocol, (stream, connection) => {
214
+ void (async () => {
215
+ try {
216
+ const frame = await readAllBounded(stream, maxBytes);
217
+ driver.deliver(connection.remotePeer.toString(), frame);
218
+ await stream.close();
219
+ }
220
+ catch {
221
+ try {
222
+ stream.abort(new Error("reactivity push-state gossip stream handler error"));
223
+ }
224
+ catch {
225
+ /* already aborted */
226
+ }
227
+ }
228
+ })();
229
+ });
230
+ }
231
+ //# sourceMappingURL=push-state-gossip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push-state-gossip.js","sourceRoot":"","sources":["../../../src/reactivity/push-state-gossip.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,167 @@
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 type { Libp2p } from "libp2p";
35
+ import type { PeerId, PrivateKey } from "@libp2p/interface";
36
+ import { type BackfillSignable, type ResumeSignable, type BackfillTransport, type PushState, type StickyCohortHintCache, type CorrelationReplayGuard, type RotationRedirectV1 } from "@optimystic/db-core";
37
+ import type { ResumeTransport } from "./subscription-manager.js";
38
+ /**
39
+ * Decode a sticky-cohort-hint `primary` (base64url of member-id bytes = `base64url(utf8(peerIdString))`)
40
+ * back to the dialable peer-id string. The inverse trap of feeding the raw value to `peerIdFromString`
41
+ * (which expects a peer-id string, not base64url-of-its-utf8) — pinned by the dial-target encoding test.
42
+ */
43
+ export declare function decodeCohortHintTarget(primary: string): string;
44
+ /**
45
+ * The synchronous request-signing seam the production subscribe factory feeds a
46
+ * {@link import("./subscription-manager.js").ReactivitySubscriptionManager}: it builds a
47
+ * {@link BackfillV1} / {@link ResumeV1} signature (base64url) over the unsigned image.
48
+ */
49
+ export interface RecoverRequestSigners {
50
+ /** Sign a {@link BackfillV1} over {@link backfillSigningPayload} of its unsigned image. */
51
+ readonly signBackfill: (unsigned: BackfillSignable) => string;
52
+ /** Sign a {@link ResumeV1} over {@link resumeSigningPayload} of its unsigned image. */
53
+ readonly signResume: (unsigned: ResumeSignable) => string;
54
+ }
55
+ /**
56
+ * Build the subscriber's recover request signers from the node's libp2p Ed25519 private key.
57
+ *
58
+ * This resolves the recover wiring's one design point: the subscription-manager `signBackfill` /
59
+ * `signResume` ports are **synchronous** (`(unsigned) => string`) — the db-core driver builds the
60
+ * unsigned image internally, so a pre-signed value is impossible — but libp2p `PrivateKey.sign` is
61
+ * **async**. Rather than make the seam (and `createBackfillRequester`'s `sign`, a db-core change) async,
62
+ * sign with the synchronous {@link signPeerSig} (noble, over the raw Ed25519 seed) over db-core's
63
+ * canonical signing payloads. The produced signature verifies on the serving side under
64
+ * {@link verifyPeerSig} over the same bytes (both noble, RFC8032), so the round trip is symmetric.
65
+ */
66
+ export declare function createRecoverRequestSigners(privateKey: PrivateKey): RecoverRequestSigners;
67
+ /**
68
+ * Thrown out of {@link Libp2pReactivityRecoverTransport.backfillTransport} / `resumeTransport` when the
69
+ * dialed cohort answered with a `kind: "rotated"` recover reply: the outgoing tail this request reached has
70
+ * rotated and is draining, so it bounced the request to the new tree (`docs/reactivity.md` §Tail rotation).
71
+ * It carries the {@link RotationRedirectV1} so the subscription manager can move itself to the successor.
72
+ *
73
+ * A `kind: "rotated"` reply is **terminal** for the cohort-walk — the dialed member spoke authoritatively for
74
+ * the cohort, so the transport stops rather than falling through to the next target (contrast a *dial
75
+ * failure*, which still falls through). The subscriber honors it like a notification-driven rotation.
76
+ */
77
+ export declare class RotationRedirectError extends Error {
78
+ /** The drain-window redirect the serving cohort returned (`newTailId` / `newTopicId` / `effectiveAtRevision`). */
79
+ readonly redirect: RotationRedirectV1;
80
+ constructor(redirect: RotationRedirectV1);
81
+ }
82
+ /** The wire exchange seam: open the recover protocol to `target`, send `frame`, return the bounded reply. */
83
+ export interface RecoverDialer {
84
+ /** Exchange one request frame for one reply frame with `target` (peer-id string). Rejects on a dial failure. */
85
+ exchange(target: string, frame: Uint8Array): Promise<Uint8Array>;
86
+ }
87
+ /** Build the production libp2p-backed {@link RecoverDialer} over {@link requestResponse}. */
88
+ export declare function createLibp2pRecoverDialer(node: Libp2p, recoverProtocol?: string, maxBytes?: number): RecoverDialer;
89
+ /** Construction inputs for a {@link Libp2pReactivityRecoverTransport}. */
90
+ export interface Libp2pReactivityRecoverTransportOptions {
91
+ /** Wire exchange (e.g. {@link createLibp2pRecoverDialer} over the live node). */
92
+ readonly dialer: RecoverDialer;
93
+ /** This node's peer-id string (`node.peerId.toString()`); never dialed — a co-located serve is the node wiring's. */
94
+ readonly selfPeerId: string;
95
+ /** Sticky cohort-hint cache, shared with the subscription manager (the one-RT primary for the happy path). */
96
+ readonly cohortHintCache: StickyCohortHintCache;
97
+ /** FRET cohort-walk fallback: a topic id → dialable cohort member peer-id strings. */
98
+ readonly resolveCohort: (topicId: Uint8Array) => string[];
99
+ /** Per-frame ceiling for encode/decode; default {@link DEFAULT_STREAM_MAX_BYTES}. */
100
+ readonly maxBytes?: number;
101
+ }
102
+ /**
103
+ * The outbound recover transport: one instance per node, exposing the two db-core function seams against it.
104
+ * Each returned transport dials the **sticky primary first** (one round trip after a brief flap), falling
105
+ * back to a **cohort-walk** member on a dial failure (any member that holds the topic's gossiped `PushState`
106
+ * can answer). A kind mismatch or all-targets-failed surfaces as a rejection so the caller's retry/escalation
107
+ * policy (the subscription manager's backfill escalation, or `manager.resume()`'s caller) takes over.
108
+ */
109
+ export declare class Libp2pReactivityRecoverTransport {
110
+ private readonly dialer;
111
+ private readonly selfPeerId;
112
+ private readonly cohortHintCache;
113
+ private readonly resolveCohort;
114
+ private readonly maxBytes;
115
+ constructor(options: Libp2pReactivityRecoverTransportOptions);
116
+ /** The db-core {@link BackfillTransport} for `(topicId, collectionId)` — frames + dials a signed {@link BackfillV1}. */
117
+ backfillTransport(topicId: Uint8Array, collectionId: string): BackfillTransport;
118
+ /** The {@link ResumeTransport} for `(topicId, collectionId)` — frames + dials a signed {@link ResumeV1}. */
119
+ resumeTransport(topicId: Uint8Array, collectionId: string): ResumeTransport;
120
+ /**
121
+ * Exchange one recover frame with the first reachable target: sticky primary, then each cohort-walk
122
+ * member. A **dial failure** falls through to the next candidate; a successful dial is **terminal** (the
123
+ * member answered for the cohort) — a `kind: "rotated"` redirect throws {@link RotationRedirectError}, and
124
+ * a reply decoding to the wrong `kind` is a protocol error. Throws when no candidate succeeds.
125
+ */
126
+ private exchange;
127
+ /**
128
+ * Ordered dial candidates for a recover: the sticky primary (decoded from its base64url-of-bytes form)
129
+ * first, then the cohort-walk members. Self is never dialed (a co-located serve is the node wiring's
130
+ * concern), and duplicates collapse.
131
+ */
132
+ private selectTargets;
133
+ }
134
+ /** Live `PushState` resolvers + replay guard the recover serve handler dispatches against. */
135
+ export interface RecoverServeDeps {
136
+ /** Resolve the served `PushState` for an exact reactivity topic id (resume's stale-tail lookup). */
137
+ readonly pushStateFor: (topicId: Uint8Array) => PushState | undefined;
138
+ /** Resolve the served `PushState` for a collection id — the current tail (backfill, and rotated resume). */
139
+ readonly pushStateForCollection: (collectionId: string) => PushState | undefined;
140
+ /** Node-level freshness + anti-replay gate keyed on the request signature bytes. */
141
+ readonly replayGuard: CorrelationReplayGuard;
142
+ /**
143
+ * The drain-window redirect for a request that reached an **outgoing (rotated)** tail still in its drain
144
+ * window, or `undefined` if the resolved topic never rotated / has drained. The node wiring binds this to
145
+ * `ReactivityForwarderHost.rotationRedirectFor`, resolving the old topic from the request: for **resume**
146
+ * the request carries `topicId = reactivityTopicId(latestKnownTailId)`; for **backfill** (no `topicId`)
147
+ * the binding resolves the collection's current served topic. When it returns a redirect the serve replies
148
+ * `kind: "rotated"` instead of serving data, moving the subscriber to the new tree. Absent ⇒ never redirect.
149
+ */
150
+ readonly rotationFor?: (req: {
151
+ topicId?: Uint8Array;
152
+ collectionId: string;
153
+ }, now: number) => RotationRedirectV1 | undefined;
154
+ /** Unix-ms clock for the replay-guard window. Default `Date.now`. */
155
+ readonly clock?: () => number;
156
+ /** Per-frame decode ceiling; default {@link DEFAULT_STREAM_MAX_BYTES}. */
157
+ readonly maxBytes?: number;
158
+ }
159
+ /**
160
+ * Build the recover serve callback for {@link handleRequestResponse}: it returns the reply frame, or
161
+ * `undefined` for **no reply** (a decode/verify/replay/resolve failure aborts the stream). It never throws
162
+ * out of the handler.
163
+ */
164
+ export declare function createRecoverRequestHandler(deps: RecoverServeDeps): (frame: Uint8Array, fromPeer: PeerId) => Promise<Uint8Array | undefined>;
165
+ /** Register the inbound recover protocol handler on `node` (request-reply over the recover protocol). */
166
+ export declare function registerRecoverHandler(node: Libp2p, protocol: string, deps: RecoverServeDeps): void;
167
+ //# sourceMappingURL=recover-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recover-transport.d.ts","sourceRoot":"","sources":["../../../src/reactivity/recover-transport.ts"],"names":[],"mappings":"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"}