@optimystic/db-core 0.13.5 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (447) hide show
  1. package/dist/src/cluster/structs.d.ts +13 -0
  2. package/dist/src/cluster/structs.d.ts.map +1 -1
  3. package/dist/src/cohort-topic/addressing.d.ts +53 -0
  4. package/dist/src/cohort-topic/addressing.d.ts.map +1 -0
  5. package/dist/src/cohort-topic/addressing.js +95 -0
  6. package/dist/src/cohort-topic/addressing.js.map +1 -0
  7. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.d.ts +105 -0
  8. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.d.ts.map +1 -0
  9. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.js +197 -0
  10. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.js.map +1 -0
  11. package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts +50 -0
  12. package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts.map +1 -0
  13. package/dist/src/cohort-topic/antidos/bootstrap-evidence.js +62 -0
  14. package/dist/src/cohort-topic/antidos/bootstrap-evidence.js.map +1 -0
  15. package/dist/src/cohort-topic/antidos/index.d.ts +6 -0
  16. package/dist/src/cohort-topic/antidos/index.d.ts.map +1 -0
  17. package/dist/src/cohort-topic/antidos/index.js +6 -0
  18. package/dist/src/cohort-topic/antidos/index.js.map +1 -0
  19. package/dist/src/cohort-topic/antidos/rate-limiter.d.ts +95 -0
  20. package/dist/src/cohort-topic/antidos/rate-limiter.d.ts.map +1 -0
  21. package/dist/src/cohort-topic/antidos/rate-limiter.js +146 -0
  22. package/dist/src/cohort-topic/antidos/rate-limiter.js.map +1 -0
  23. package/dist/src/cohort-topic/antidos/replay-guard.d.ts +37 -0
  24. package/dist/src/cohort-topic/antidos/replay-guard.d.ts.map +1 -0
  25. package/dist/src/cohort-topic/antidos/replay-guard.js +78 -0
  26. package/dist/src/cohort-topic/antidos/replay-guard.js.map +1 -0
  27. package/dist/src/cohort-topic/antidos/topic-budget.d.ts +50 -0
  28. package/dist/src/cohort-topic/antidos/topic-budget.d.ts.map +1 -0
  29. package/dist/src/cohort-topic/antidos/topic-budget.js +95 -0
  30. package/dist/src/cohort-topic/antidos/topic-budget.js.map +1 -0
  31. package/dist/src/cohort-topic/antiflood/index.d.ts +3 -0
  32. package/dist/src/cohort-topic/antiflood/index.d.ts.map +1 -0
  33. package/dist/src/cohort-topic/antiflood/index.js +3 -0
  34. package/dist/src/cohort-topic/antiflood/index.js.map +1 -0
  35. package/dist/src/cohort-topic/antiflood/invariants.d.ts +70 -0
  36. package/dist/src/cohort-topic/antiflood/invariants.d.ts.map +1 -0
  37. package/dist/src/cohort-topic/antiflood/invariants.js +86 -0
  38. package/dist/src/cohort-topic/antiflood/invariants.js.map +1 -0
  39. package/dist/src/cohort-topic/antiflood/jitter.d.ts +65 -0
  40. package/dist/src/cohort-topic/antiflood/jitter.d.ts.map +1 -0
  41. package/dist/src/cohort-topic/antiflood/jitter.js +75 -0
  42. package/dist/src/cohort-topic/antiflood/jitter.js.map +1 -0
  43. package/dist/src/cohort-topic/coldstart.d.ts +117 -0
  44. package/dist/src/cohort-topic/coldstart.d.ts.map +1 -0
  45. package/dist/src/cohort-topic/coldstart.js +127 -0
  46. package/dist/src/cohort-topic/coldstart.js.map +1 -0
  47. package/dist/src/cohort-topic/dmax.d.ts +44 -0
  48. package/dist/src/cohort-topic/dmax.d.ts.map +1 -0
  49. package/dist/src/cohort-topic/dmax.js +70 -0
  50. package/dist/src/cohort-topic/dmax.js.map +1 -0
  51. package/dist/src/cohort-topic/gossip/bus.d.ts +85 -0
  52. package/dist/src/cohort-topic/gossip/bus.d.ts.map +1 -0
  53. package/dist/src/cohort-topic/gossip/bus.js +146 -0
  54. package/dist/src/cohort-topic/gossip/bus.js.map +1 -0
  55. package/dist/src/cohort-topic/gossip/index.d.ts +4 -0
  56. package/dist/src/cohort-topic/gossip/index.d.ts.map +1 -0
  57. package/dist/src/cohort-topic/gossip/index.js +4 -0
  58. package/dist/src/cohort-topic/gossip/index.js.map +1 -0
  59. package/dist/src/cohort-topic/gossip/records.d.ts +12 -0
  60. package/dist/src/cohort-topic/gossip/records.d.ts.map +1 -0
  61. package/dist/src/cohort-topic/gossip/records.js +41 -0
  62. package/dist/src/cohort-topic/gossip/records.js.map +1 -0
  63. package/dist/src/cohort-topic/gossip/view.d.ts +39 -0
  64. package/dist/src/cohort-topic/gossip/view.d.ts.map +1 -0
  65. package/dist/src/cohort-topic/gossip/view.js +30 -0
  66. package/dist/src/cohort-topic/gossip/view.js.map +1 -0
  67. package/dist/src/cohort-topic/index.d.ts +21 -0
  68. package/dist/src/cohort-topic/index.d.ts.map +1 -0
  69. package/dist/src/cohort-topic/index.js +21 -0
  70. package/dist/src/cohort-topic/index.js.map +1 -0
  71. package/dist/src/cohort-topic/load/barometer.d.ts +78 -0
  72. package/dist/src/cohort-topic/load/barometer.d.ts.map +1 -0
  73. package/dist/src/cohort-topic/load/barometer.js +93 -0
  74. package/dist/src/cohort-topic/load/barometer.js.map +1 -0
  75. package/dist/src/cohort-topic/load/index.d.ts +2 -0
  76. package/dist/src/cohort-topic/load/index.d.ts.map +1 -0
  77. package/dist/src/cohort-topic/load/index.js +2 -0
  78. package/dist/src/cohort-topic/load/index.js.map +1 -0
  79. package/dist/src/cohort-topic/member-engine.d.ts +134 -0
  80. package/dist/src/cohort-topic/member-engine.d.ts.map +1 -0
  81. package/dist/src/cohort-topic/member-engine.js +264 -0
  82. package/dist/src/cohort-topic/member-engine.js.map +1 -0
  83. package/dist/src/cohort-topic/membership/index.d.ts +4 -0
  84. package/dist/src/cohort-topic/membership/index.d.ts.map +1 -0
  85. package/dist/src/cohort-topic/membership/index.js +4 -0
  86. package/dist/src/cohort-topic/membership/index.js.map +1 -0
  87. package/dist/src/cohort-topic/membership/publisher.d.ts +82 -0
  88. package/dist/src/cohort-topic/membership/publisher.d.ts.map +1 -0
  89. package/dist/src/cohort-topic/membership/publisher.js +107 -0
  90. package/dist/src/cohort-topic/membership/publisher.js.map +1 -0
  91. package/dist/src/cohort-topic/membership/source.d.ts +31 -0
  92. package/dist/src/cohort-topic/membership/source.d.ts.map +1 -0
  93. package/dist/src/cohort-topic/membership/source.js +32 -0
  94. package/dist/src/cohort-topic/membership/source.js.map +1 -0
  95. package/dist/src/cohort-topic/membership/verifier.d.ts +96 -0
  96. package/dist/src/cohort-topic/membership/verifier.d.ts.map +1 -0
  97. package/dist/src/cohort-topic/membership/verifier.js +250 -0
  98. package/dist/src/cohort-topic/membership/verifier.js.map +1 -0
  99. package/dist/src/cohort-topic/ports.d.ts +149 -0
  100. package/dist/src/cohort-topic/ports.d.ts.map +1 -0
  101. package/dist/src/cohort-topic/ports.js +25 -0
  102. package/dist/src/cohort-topic/ports.js.map +1 -0
  103. package/dist/src/cohort-topic/promotion.d.ts +135 -0
  104. package/dist/src/cohort-topic/promotion.d.ts.map +1 -0
  105. package/dist/src/cohort-topic/promotion.js +269 -0
  106. package/dist/src/cohort-topic/promotion.js.map +1 -0
  107. package/dist/src/cohort-topic/registration/bytes.d.ts +16 -0
  108. package/dist/src/cohort-topic/registration/bytes.d.ts.map +1 -0
  109. package/dist/src/cohort-topic/registration/bytes.js +32 -0
  110. package/dist/src/cohort-topic/registration/bytes.js.map +1 -0
  111. package/dist/src/cohort-topic/registration/handoff.d.ts +69 -0
  112. package/dist/src/cohort-topic/registration/handoff.d.ts.map +1 -0
  113. package/dist/src/cohort-topic/registration/handoff.js +98 -0
  114. package/dist/src/cohort-topic/registration/handoff.js.map +1 -0
  115. package/dist/src/cohort-topic/registration/index.d.ts +7 -0
  116. package/dist/src/cohort-topic/registration/index.d.ts.map +1 -0
  117. package/dist/src/cohort-topic/registration/index.js +7 -0
  118. package/dist/src/cohort-topic/registration/index.js.map +1 -0
  119. package/dist/src/cohort-topic/registration/renewal.d.ts +115 -0
  120. package/dist/src/cohort-topic/registration/renewal.d.ts.map +1 -0
  121. package/dist/src/cohort-topic/registration/renewal.js +306 -0
  122. package/dist/src/cohort-topic/registration/renewal.js.map +1 -0
  123. package/dist/src/cohort-topic/registration/sharding.d.ts +31 -0
  124. package/dist/src/cohort-topic/registration/sharding.d.ts.map +1 -0
  125. package/dist/src/cohort-topic/registration/sharding.js +48 -0
  126. package/dist/src/cohort-topic/registration/sharding.js.map +1 -0
  127. package/dist/src/cohort-topic/registration/store.d.ts +13 -0
  128. package/dist/src/cohort-topic/registration/store.d.ts.map +1 -0
  129. package/dist/src/cohort-topic/registration/store.js +72 -0
  130. package/dist/src/cohort-topic/registration/store.js.map +1 -0
  131. package/dist/src/cohort-topic/registration/types.d.ts +71 -0
  132. package/dist/src/cohort-topic/registration/types.d.ts.map +1 -0
  133. package/dist/src/cohort-topic/registration/types.js +27 -0
  134. package/dist/src/cohort-topic/registration/types.js.map +1 -0
  135. package/dist/src/cohort-topic/ring-hash.d.ts +27 -0
  136. package/dist/src/cohort-topic/ring-hash.d.ts.map +1 -0
  137. package/dist/src/cohort-topic/ring-hash.js +45 -0
  138. package/dist/src/cohort-topic/ring-hash.js.map +1 -0
  139. package/dist/src/cohort-topic/service.d.ts +157 -0
  140. package/dist/src/cohort-topic/service.d.ts.map +1 -0
  141. package/dist/src/cohort-topic/service.js +237 -0
  142. package/dist/src/cohort-topic/service.js.map +1 -0
  143. package/dist/src/cohort-topic/sig/index.d.ts +3 -0
  144. package/dist/src/cohort-topic/sig/index.d.ts.map +1 -0
  145. package/dist/src/cohort-topic/sig/index.js +3 -0
  146. package/dist/src/cohort-topic/sig/index.js.map +1 -0
  147. package/dist/src/cohort-topic/sig/payloads.d.ts +25 -0
  148. package/dist/src/cohort-topic/sig/payloads.d.ts.map +1 -0
  149. package/dist/src/cohort-topic/sig/payloads.js +25 -0
  150. package/dist/src/cohort-topic/sig/payloads.js.map +1 -0
  151. package/dist/src/cohort-topic/sig/threshold.d.ts +33 -0
  152. package/dist/src/cohort-topic/sig/threshold.d.ts.map +1 -0
  153. package/dist/src/cohort-topic/sig/threshold.js +51 -0
  154. package/dist/src/cohort-topic/sig/threshold.js.map +1 -0
  155. package/dist/src/cohort-topic/tiers.d.ts +58 -0
  156. package/dist/src/cohort-topic/tiers.d.ts.map +1 -0
  157. package/dist/src/cohort-topic/tiers.js +49 -0
  158. package/dist/src/cohort-topic/tiers.js.map +1 -0
  159. package/dist/src/cohort-topic/traffic.d.ts +90 -0
  160. package/dist/src/cohort-topic/traffic.d.ts.map +1 -0
  161. package/dist/src/cohort-topic/traffic.js +146 -0
  162. package/dist/src/cohort-topic/traffic.js.map +1 -0
  163. package/dist/src/cohort-topic/walk.d.ts +123 -0
  164. package/dist/src/cohort-topic/walk.d.ts.map +1 -0
  165. package/dist/src/cohort-topic/walk.js +150 -0
  166. package/dist/src/cohort-topic/walk.js.map +1 -0
  167. package/dist/src/cohort-topic/willingness.d.ts +123 -0
  168. package/dist/src/cohort-topic/willingness.d.ts.map +1 -0
  169. package/dist/src/cohort-topic/willingness.js +143 -0
  170. package/dist/src/cohort-topic/willingness.js.map +1 -0
  171. package/dist/src/cohort-topic/wire/codec.d.ts +42 -0
  172. package/dist/src/cohort-topic/wire/codec.d.ts.map +1 -0
  173. package/dist/src/cohort-topic/wire/codec.js +163 -0
  174. package/dist/src/cohort-topic/wire/codec.js.map +1 -0
  175. package/dist/src/cohort-topic/wire/index.d.ts +6 -0
  176. package/dist/src/cohort-topic/wire/index.d.ts.map +1 -0
  177. package/dist/src/cohort-topic/wire/index.js +6 -0
  178. package/dist/src/cohort-topic/wire/index.js.map +1 -0
  179. package/dist/src/cohort-topic/wire/payloads.d.ts +38 -0
  180. package/dist/src/cohort-topic/wire/payloads.d.ts.map +1 -0
  181. package/dist/src/cohort-topic/wire/payloads.js +102 -0
  182. package/dist/src/cohort-topic/wire/payloads.js.map +1 -0
  183. package/dist/src/cohort-topic/wire/types.d.ts +304 -0
  184. package/dist/src/cohort-topic/wire/types.d.ts.map +1 -0
  185. package/dist/src/cohort-topic/wire/types.js +16 -0
  186. package/dist/src/cohort-topic/wire/types.js.map +1 -0
  187. package/dist/src/cohort-topic/wire/validate.d.ts +25 -0
  188. package/dist/src/cohort-topic/wire/validate.d.ts.map +1 -0
  189. package/dist/src/cohort-topic/wire/validate.js +427 -0
  190. package/dist/src/cohort-topic/wire/validate.js.map +1 -0
  191. package/dist/src/collection/collection.d.ts +58 -1
  192. package/dist/src/collection/collection.d.ts.map +1 -1
  193. package/dist/src/collection/collection.js +76 -0
  194. package/dist/src/collection/collection.js.map +1 -1
  195. package/dist/src/collections/tree/tree.d.ts +58 -2
  196. package/dist/src/collections/tree/tree.d.ts.map +1 -1
  197. package/dist/src/collections/tree/tree.js +59 -2
  198. package/dist/src/collections/tree/tree.js.map +1 -1
  199. package/dist/src/index.d.ts +3 -0
  200. package/dist/src/index.d.ts.map +1 -1
  201. package/dist/src/index.js +3 -0
  202. package/dist/src/index.js.map +1 -1
  203. package/dist/src/log/log.d.ts +42 -4
  204. package/dist/src/log/log.d.ts.map +1 -1
  205. package/dist/src/log/log.js +71 -3
  206. package/dist/src/log/log.js.map +1 -1
  207. package/dist/src/log/struct.d.ts +122 -2
  208. package/dist/src/log/struct.d.ts.map +1 -1
  209. package/dist/src/log/struct.js.map +1 -1
  210. package/dist/src/matchmaking/capability-filter.d.ts +24 -0
  211. package/dist/src/matchmaking/capability-filter.d.ts.map +1 -0
  212. package/dist/src/matchmaking/capability-filter.js +37 -0
  213. package/dist/src/matchmaking/capability-filter.js.map +1 -0
  214. package/dist/src/matchmaking/config.d.ts +77 -0
  215. package/dist/src/matchmaking/config.d.ts.map +1 -0
  216. package/dist/src/matchmaking/config.js +69 -0
  217. package/dist/src/matchmaking/config.js.map +1 -0
  218. package/dist/src/matchmaking/index.d.ts +21 -0
  219. package/dist/src/matchmaking/index.d.ts.map +1 -0
  220. package/dist/src/matchmaking/index.js +21 -0
  221. package/dist/src/matchmaking/index.js.map +1 -0
  222. package/dist/src/matchmaking/multi-cohort-seeker.d.ts +133 -0
  223. package/dist/src/matchmaking/multi-cohort-seeker.d.ts.map +1 -0
  224. package/dist/src/matchmaking/multi-cohort-seeker.js +142 -0
  225. package/dist/src/matchmaking/multi-cohort-seeker.js.map +1 -0
  226. package/dist/src/matchmaking/provider.d.ts +70 -0
  227. package/dist/src/matchmaking/provider.d.ts.map +1 -0
  228. package/dist/src/matchmaking/provider.js +93 -0
  229. package/dist/src/matchmaking/provider.js.map +1 -0
  230. package/dist/src/matchmaking/query-eval.d.ts +53 -0
  231. package/dist/src/matchmaking/query-eval.d.ts.map +1 -0
  232. package/dist/src/matchmaking/query-eval.js +69 -0
  233. package/dist/src/matchmaking/query-eval.js.map +1 -0
  234. package/dist/src/matchmaking/seeker-walk.d.ts +95 -0
  235. package/dist/src/matchmaking/seeker-walk.d.ts.map +1 -0
  236. package/dist/src/matchmaking/seeker-walk.js +75 -0
  237. package/dist/src/matchmaking/seeker-walk.js.map +1 -0
  238. package/dist/src/matchmaking/seeker.d.ts +51 -0
  239. package/dist/src/matchmaking/seeker.d.ts.map +1 -0
  240. package/dist/src/matchmaking/seeker.js +62 -0
  241. package/dist/src/matchmaking/seeker.js.map +1 -0
  242. package/dist/src/matchmaking/topic-anchor.d.ts +54 -0
  243. package/dist/src/matchmaking/topic-anchor.d.ts.map +1 -0
  244. package/dist/src/matchmaking/topic-anchor.js +67 -0
  245. package/dist/src/matchmaking/topic-anchor.js.map +1 -0
  246. package/dist/src/matchmaking/voting-quorum.d.ts +233 -0
  247. package/dist/src/matchmaking/voting-quorum.d.ts.map +1 -0
  248. package/dist/src/matchmaking/voting-quorum.js +238 -0
  249. package/dist/src/matchmaking/voting-quorum.js.map +1 -0
  250. package/dist/src/matchmaking/wire.d.ts +259 -0
  251. package/dist/src/matchmaking/wire.d.ts.map +1 -0
  252. package/dist/src/matchmaking/wire.js +484 -0
  253. package/dist/src/matchmaking/wire.js.map +1 -0
  254. package/dist/src/network/i-repo.d.ts +9 -0
  255. package/dist/src/network/i-repo.d.ts.map +1 -1
  256. package/dist/src/network/repo-protocol.d.ts +3 -1
  257. package/dist/src/network/repo-protocol.d.ts.map +1 -1
  258. package/dist/src/network/struct.d.ts +23 -1
  259. package/dist/src/network/struct.d.ts.map +1 -1
  260. package/dist/src/reactivity/backfill.d.ts +118 -0
  261. package/dist/src/reactivity/backfill.d.ts.map +1 -0
  262. package/dist/src/reactivity/backfill.js +137 -0
  263. package/dist/src/reactivity/backfill.js.map +1 -0
  264. package/dist/src/reactivity/backpressure.d.ts +99 -0
  265. package/dist/src/reactivity/backpressure.d.ts.map +1 -0
  266. package/dist/src/reactivity/backpressure.js +153 -0
  267. package/dist/src/reactivity/backpressure.js.map +1 -0
  268. package/dist/src/reactivity/checkpoint.d.ts +165 -0
  269. package/dist/src/reactivity/checkpoint.d.ts.map +1 -0
  270. package/dist/src/reactivity/checkpoint.js +232 -0
  271. package/dist/src/reactivity/checkpoint.js.map +1 -0
  272. package/dist/src/reactivity/config.d.ts +131 -0
  273. package/dist/src/reactivity/config.d.ts.map +1 -0
  274. package/dist/src/reactivity/config.js +135 -0
  275. package/dist/src/reactivity/config.js.map +1 -0
  276. package/dist/src/reactivity/dedupe.d.ts +51 -0
  277. package/dist/src/reactivity/dedupe.d.ts.map +1 -0
  278. package/dist/src/reactivity/dedupe.js +90 -0
  279. package/dist/src/reactivity/dedupe.js.map +1 -0
  280. package/dist/src/reactivity/forwarder.d.ts +50 -0
  281. package/dist/src/reactivity/forwarder.d.ts.map +1 -0
  282. package/dist/src/reactivity/forwarder.js +52 -0
  283. package/dist/src/reactivity/forwarder.js.map +1 -0
  284. package/dist/src/reactivity/index.d.ts +34 -0
  285. package/dist/src/reactivity/index.d.ts.map +1 -0
  286. package/dist/src/reactivity/index.js +34 -0
  287. package/dist/src/reactivity/index.js.map +1 -0
  288. package/dist/src/reactivity/notification.d.ts +72 -0
  289. package/dist/src/reactivity/notification.d.ts.map +1 -0
  290. package/dist/src/reactivity/notification.js +92 -0
  291. package/dist/src/reactivity/notification.js.map +1 -0
  292. package/dist/src/reactivity/policy.d.ts +59 -0
  293. package/dist/src/reactivity/policy.d.ts.map +1 -0
  294. package/dist/src/reactivity/policy.js +64 -0
  295. package/dist/src/reactivity/policy.js.map +1 -0
  296. package/dist/src/reactivity/push-state.d.ts +140 -0
  297. package/dist/src/reactivity/push-state.d.ts.map +1 -0
  298. package/dist/src/reactivity/push-state.js +270 -0
  299. package/dist/src/reactivity/push-state.js.map +1 -0
  300. package/dist/src/reactivity/recover.d.ts +75 -0
  301. package/dist/src/reactivity/recover.d.ts.map +1 -0
  302. package/dist/src/reactivity/recover.js +105 -0
  303. package/dist/src/reactivity/recover.js.map +1 -0
  304. package/dist/src/reactivity/replay-buffer.d.ts +57 -0
  305. package/dist/src/reactivity/replay-buffer.d.ts.map +1 -0
  306. package/dist/src/reactivity/replay-buffer.js +88 -0
  307. package/dist/src/reactivity/replay-buffer.js.map +1 -0
  308. package/dist/src/reactivity/resume.d.ts +214 -0
  309. package/dist/src/reactivity/resume.d.ts.map +1 -0
  310. package/dist/src/reactivity/resume.js +358 -0
  311. package/dist/src/reactivity/resume.js.map +1 -0
  312. package/dist/src/reactivity/rotation.d.ts +283 -0
  313. package/dist/src/reactivity/rotation.d.ts.map +1 -0
  314. package/dist/src/reactivity/rotation.js +265 -0
  315. package/dist/src/reactivity/rotation.js.map +1 -0
  316. package/dist/src/reactivity/subscriber.d.ts +66 -0
  317. package/dist/src/reactivity/subscriber.d.ts.map +1 -0
  318. package/dist/src/reactivity/subscriber.js +75 -0
  319. package/dist/src/reactivity/subscriber.js.map +1 -0
  320. package/dist/src/reactivity/subscription.d.ts +52 -0
  321. package/dist/src/reactivity/subscription.d.ts.map +1 -0
  322. package/dist/src/reactivity/subscription.js +28 -0
  323. package/dist/src/reactivity/subscription.js.map +1 -0
  324. package/dist/src/reactivity/topic-anchor.d.ts +46 -0
  325. package/dist/src/reactivity/topic-anchor.d.ts.map +1 -0
  326. package/dist/src/reactivity/topic-anchor.js +61 -0
  327. package/dist/src/reactivity/topic-anchor.js.map +1 -0
  328. package/dist/src/reactivity/verify.d.ts +41 -0
  329. package/dist/src/reactivity/verify.d.ts.map +1 -0
  330. package/dist/src/reactivity/verify.js +48 -0
  331. package/dist/src/reactivity/verify.js.map +1 -0
  332. package/dist/src/reactivity/wire-validate.d.ts +28 -0
  333. package/dist/src/reactivity/wire-validate.d.ts.map +1 -0
  334. package/dist/src/reactivity/wire-validate.js +79 -0
  335. package/dist/src/reactivity/wire-validate.js.map +1 -0
  336. package/dist/src/reactivity/wire.d.ts +102 -0
  337. package/dist/src/reactivity/wire.d.ts.map +1 -0
  338. package/dist/src/reactivity/wire.js +129 -0
  339. package/dist/src/reactivity/wire.js.map +1 -0
  340. package/dist/src/transaction/coordinator.d.ts +9 -5
  341. package/dist/src/transaction/coordinator.d.ts.map +1 -1
  342. package/dist/src/transaction/coordinator.js +48 -25
  343. package/dist/src/transaction/coordinator.js.map +1 -1
  344. package/dist/src/transactor/change-notifier.d.ts +74 -0
  345. package/dist/src/transactor/change-notifier.d.ts.map +1 -0
  346. package/dist/src/transactor/change-notifier.js +4 -0
  347. package/dist/src/transactor/change-notifier.js.map +1 -0
  348. package/dist/src/transactor/index.d.ts +1 -0
  349. package/dist/src/transactor/index.d.ts.map +1 -1
  350. package/dist/src/transactor/index.js +1 -0
  351. package/dist/src/transactor/index.js.map +1 -1
  352. package/dist/src/transactor/network-transactor.d.ts +34 -2
  353. package/dist/src/transactor/network-transactor.d.ts.map +1 -1
  354. package/dist/src/transactor/network-transactor.js +113 -13
  355. package/dist/src/transactor/network-transactor.js.map +1 -1
  356. package/package.json +2 -2
  357. package/src/cluster/structs.ts +13 -0
  358. package/src/cohort-topic/addressing.ts +112 -0
  359. package/src/cohort-topic/antidos/bootstrap-evidence-envelope.ts +253 -0
  360. package/src/cohort-topic/antidos/bootstrap-evidence.ts +94 -0
  361. package/src/cohort-topic/antidos/index.ts +5 -0
  362. package/src/cohort-topic/antidos/rate-limiter.ts +210 -0
  363. package/src/cohort-topic/antidos/replay-guard.ts +109 -0
  364. package/src/cohort-topic/antidos/topic-budget.ts +145 -0
  365. package/src/cohort-topic/antiflood/index.ts +2 -0
  366. package/src/cohort-topic/antiflood/invariants.ts +108 -0
  367. package/src/cohort-topic/antiflood/jitter.ts +117 -0
  368. package/src/cohort-topic/coldstart.ts +204 -0
  369. package/src/cohort-topic/dmax.ts +88 -0
  370. package/src/cohort-topic/gossip/bus.ts +217 -0
  371. package/src/cohort-topic/gossip/index.ts +3 -0
  372. package/src/cohort-topic/gossip/records.ts +45 -0
  373. package/src/cohort-topic/gossip/view.ts +66 -0
  374. package/src/cohort-topic/index.ts +20 -0
  375. package/src/cohort-topic/load/barometer.ts +134 -0
  376. package/src/cohort-topic/load/index.ts +1 -0
  377. package/src/cohort-topic/member-engine.ts +409 -0
  378. package/src/cohort-topic/membership/index.ts +3 -0
  379. package/src/cohort-topic/membership/publisher.ts +171 -0
  380. package/src/cohort-topic/membership/source.ts +41 -0
  381. package/src/cohort-topic/membership/verifier.ts +345 -0
  382. package/src/cohort-topic/ports.ts +157 -0
  383. package/src/cohort-topic/promotion.ts +384 -0
  384. package/src/cohort-topic/registration/bytes.ts +37 -0
  385. package/src/cohort-topic/registration/handoff.ts +154 -0
  386. package/src/cohort-topic/registration/index.ts +6 -0
  387. package/src/cohort-topic/registration/renewal.ts +423 -0
  388. package/src/cohort-topic/registration/sharding.ts +61 -0
  389. package/src/cohort-topic/registration/store.ts +81 -0
  390. package/src/cohort-topic/registration/types.ts +76 -0
  391. package/src/cohort-topic/ring-hash.ts +50 -0
  392. package/src/cohort-topic/service.ts +390 -0
  393. package/src/cohort-topic/sig/index.ts +2 -0
  394. package/src/cohort-topic/sig/payloads.ts +39 -0
  395. package/src/cohort-topic/sig/threshold.ts +64 -0
  396. package/src/cohort-topic/tiers.ts +74 -0
  397. package/src/cohort-topic/traffic.ts +211 -0
  398. package/src/cohort-topic/walk.ts +255 -0
  399. package/src/cohort-topic/willingness.ts +237 -0
  400. package/src/cohort-topic/wire/codec.ts +204 -0
  401. package/src/cohort-topic/wire/index.ts +16 -0
  402. package/src/cohort-topic/wire/payloads.ts +117 -0
  403. package/src/cohort-topic/wire/types.ts +343 -0
  404. package/src/cohort-topic/wire/validate.ts +478 -0
  405. package/src/collection/collection.ts +93 -1
  406. package/src/collections/tree/tree.ts +84 -3
  407. package/src/index.ts +3 -0
  408. package/src/log/log.ts +86 -7
  409. package/src/log/struct.ts +126 -2
  410. package/src/matchmaking/capability-filter.ts +45 -0
  411. package/src/matchmaking/config.ts +98 -0
  412. package/src/matchmaking/index.ts +21 -0
  413. package/src/matchmaking/multi-cohort-seeker.ts +234 -0
  414. package/src/matchmaking/provider.ts +123 -0
  415. package/src/matchmaking/query-eval.ts +105 -0
  416. package/src/matchmaking/seeker-walk.ts +127 -0
  417. package/src/matchmaking/seeker.ts +86 -0
  418. package/src/matchmaking/topic-anchor.ts +90 -0
  419. package/src/matchmaking/voting-quorum.ts +394 -0
  420. package/src/matchmaking/wire.ts +680 -0
  421. package/src/network/i-repo.ts +9 -0
  422. package/src/network/repo-protocol.ts +3 -2
  423. package/src/network/struct.ts +24 -1
  424. package/src/reactivity/backfill.ts +220 -0
  425. package/src/reactivity/backpressure.ts +191 -0
  426. package/src/reactivity/checkpoint.ts +308 -0
  427. package/src/reactivity/config.ts +172 -0
  428. package/src/reactivity/dedupe.ts +132 -0
  429. package/src/reactivity/forwarder.ts +87 -0
  430. package/src/reactivity/index.ts +34 -0
  431. package/src/reactivity/notification.ts +123 -0
  432. package/src/reactivity/policy.ts +79 -0
  433. package/src/reactivity/push-state.ts +343 -0
  434. package/src/reactivity/recover.ts +153 -0
  435. package/src/reactivity/replay-buffer.ts +141 -0
  436. package/src/reactivity/resume.ts +549 -0
  437. package/src/reactivity/rotation.ts +415 -0
  438. package/src/reactivity/subscriber.ts +132 -0
  439. package/src/reactivity/subscription.ts +66 -0
  440. package/src/reactivity/topic-anchor.ts +71 -0
  441. package/src/reactivity/verify.ts +73 -0
  442. package/src/reactivity/wire-validate.ts +87 -0
  443. package/src/reactivity/wire.ts +224 -0
  444. package/src/transaction/coordinator.ts +53 -27
  445. package/src/transactor/change-notifier.ts +80 -0
  446. package/src/transactor/index.ts +1 -0
  447. package/src/transactor/network-transactor.ts +133 -15
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Reactivity — tail rotation lifecycle (`docs/reactivity.md` §Rotating tail anchor, §Tail rotation).
3
+ *
4
+ * `topicId = H(tailId ‖ "reactivity")` is derived per emission ({@link reactivityTopicId}). When the tail
5
+ * block fills (`block_fill_size` transactions, default 64) a new tail block is born and `tailId` — hence
6
+ * `topicId` — changes; the cohort-topic layer treats the new `topicId` as an **entirely new topic** (fresh
7
+ * tree, new ring coord). Reactivity manages the subscriber/replay-state migration explicitly. This module
8
+ * owns that lifecycle:
9
+ *
10
+ * - **Pre-announce** — the block-filling commit's notification carries {@link RotationHintV1}; built here
11
+ * by {@link buildRotationHint} and detected subscriber-side by {@link detectRotation}.
12
+ * - **Block-fill tracking** — {@link BlockFillTracker} counts transactions in the current tail block and
13
+ * fires anticipatory **warm-up** at `block_fill_size − warm_threshold` and the **filling** signal at
14
+ * `block_fill_size` (the commit that carries the hint).
15
+ * - **Drain** — {@link TailDrainGate} keeps the outgoing tail serving renewals/replays for `T_drain` while
16
+ * bouncing *new* subscriptions with a `Promoted`-shaped {@link RotationRedirectV1} to the new tree.
17
+ * - **Jittered re-registration** — {@link planReRegistration} / {@link planReRegistrationWave} schedule a
18
+ * subscriber's move to the new `topicId` over `T_rejoin_jitter` (the cohort-topic {@link RejoinJitter}),
19
+ * carrying its existing `lastRevision` (revisions are continuous across rotations).
20
+ * - **Buffer-to-checkpoint handoff** — {@link buildRotationHandoffCheckpoint} folds the outgoing tail's
21
+ * replay buffer into a final {@link CheckpointSummary} covering `[lastCheckpoint.toRevision + 1,
22
+ * rotationRevision]`, the **only** state migrated across a rotation; {@link applyRotationHandoff} lands it
23
+ * at the new tail so a `ResumeV1` spanning the rotation is recoverable.
24
+ *
25
+ * Forwarder draining is *emergent*: forwarders under the old tail watch their direct-subscriber count drop
26
+ * as subscribers re-register elsewhere and demote naturally per the cohort-topic demotion protocol (no
27
+ * state migrates; the new tree rebuilds via re-registration). It needs no code here.
28
+ *
29
+ * Coordination with [reactivity-backfill-resume-checkpoints]: that ticket owns the `ResumeReplyV1.TailRotated`
30
+ * variant and the `latestKnownTailId`-staleness classification; this ticket produces the handoff checkpoint
31
+ * and the rotation *condition*. The new tail's {@link PushState.inheritedCheckpoint} (set by
32
+ * {@link applyRotationHandoff}) is the seam the resume classifier consults to answer a checkpoint-window
33
+ * resume whose span crosses the rotation: `classifyResume`/`serveResume` now read it (after the rolling
34
+ * `checkpoint` misses) and serve the inherited summary, so a cross-rotation resume no longer falls to
35
+ * `out_of_window` (`docs/reactivity.md` §Resume, §Tail rotation step 5). The drain-window redirect a new
36
+ * subscription receives, {@link RotationRedirectV1}, is serialized by {@link validateRotationRedirectV1} and
37
+ * rides the recover reply envelope as `kind: "rotated"` ({@link import("./recover.js").RecoverReplyV1}).
38
+ */
39
+ import type { IRingHash } from "../cohort-topic/ports.js";
40
+ import type { RejoinJitter } from "../cohort-topic/antiflood/jitter.js";
41
+ import { type CheckpointSummary } from "./checkpoint.js";
42
+ import type { PushState } from "./push-state.js";
43
+ import type { NotificationV1, RotationHintV1 } from "./wire.js";
44
+ /**
45
+ * Build the rotation pre-announce embedded in the block-filling commit's notification. The new tail block
46
+ * id is known at the filling commit; the rotation becomes effective at the next revision, so
47
+ * `effectiveAtRevision = fillingRevision + 1` (`docs/reactivity.md` §Tail rotation, worked scenario:
48
+ * revision 5400 carries `{ newTailId: T_6, effectiveAtRevision: 5401 }`).
49
+ */
50
+ export declare function buildRotationHint(newTailId: string, fillingRevision: number): RotationHintV1;
51
+ /** The subscriber-side rotation verdict for one delivered notification. */
52
+ export interface RotationDetection {
53
+ /** True iff the subscriber's tree has rotated (delivered tail differs) or a rotation is pre-announced. */
54
+ readonly rotated: boolean;
55
+ /** The tail id to re-register under, base64url — present iff `rotated`. */
56
+ readonly newTailId?: string;
57
+ /**
58
+ * True iff this is a **pre-announce**: the delivered `tailId` still matches `tailIdAtAttach`, but the
59
+ * notification's `rotationHint.newTailId` names a different successor. (False when the delivered tail
60
+ * already differs — the tree has *already* migrated.)
61
+ */
62
+ readonly preAnnounced: boolean;
63
+ }
64
+ /**
65
+ * Detect tail rotation for a subscriber attached at `tailIdAtAttach` (base64url) from a delivered
66
+ * notification. Rotation is signaled when the delivered `tailId` **or** the `rotationHint.newTailId`
67
+ * differs from `tailIdAtAttach` (`docs/reactivity.md` §Rotating tail anchor). An already-rotated delivery
68
+ * (different `tailId`) takes precedence over a pre-announce.
69
+ */
70
+ export declare function detectRotation(tailIdAtAttach: string, n: Pick<NotificationV1, "tailId" | "rotationHint">): RotationDetection;
71
+ /** The fill signal for one committed transaction in the current tail block. */
72
+ export type BlockFillSignal =
73
+ /** Below the warm-up threshold — nothing to do. */
74
+ {
75
+ readonly kind: "none";
76
+ readonly count: number;
77
+ }
78
+ /**
79
+ * Anticipatory warm-up: the block has reached `block_fill_size − warm_threshold` transactions with
80
+ * `remaining` to go. The outgoing tail opportunistically biases FRET pre-dialing toward likely-successor
81
+ * coords (best-effort; the next `tailId` is not yet knowable). No state migrates.
82
+ */
83
+ | {
84
+ readonly kind: "warmup";
85
+ readonly count: number;
86
+ readonly remaining: number;
87
+ }
88
+ /**
89
+ * The **block-filling** transaction: this commit's notification carries the {@link RotationHintV1} and
90
+ * triggers the rotation. The tracker resets for the next block after emitting this.
91
+ */
92
+ | {
93
+ readonly kind: "filling";
94
+ readonly count: number;
95
+ };
96
+ /** Construction inputs for a {@link BlockFillTracker}. */
97
+ export interface BlockFillTrackerInit {
98
+ /** Transactions per block before the tail rotates (default {@link BLOCK_FILL_SIZE_DEFAULT}). */
99
+ readonly blockFillSize?: number;
100
+ /** Transactions remaining in the tail when anticipatory warm-up fires (default {@link WARM_THRESHOLD_DEFAULT}). */
101
+ readonly warmThreshold?: number;
102
+ }
103
+ /**
104
+ * Counts transactions committed into the current tail block and emits the {@link BlockFillSignal} that
105
+ * drives anticipatory warm-up and the block-filling rotation trigger (`docs/reactivity.md` §Tail rotation,
106
+ * §Anticipatory warm-up). One tracker per collection on the tail-cohort primary.
107
+ */
108
+ export declare class BlockFillTracker {
109
+ readonly blockFillSize: number;
110
+ readonly warmThreshold: number;
111
+ private count;
112
+ constructor(init?: BlockFillTrackerInit);
113
+ /** Transactions committed into the current (not-yet-rotated) block. */
114
+ get transactionsInBlock(): number;
115
+ /** The fill count at which anticipatory warm-up fires (`block_fill_size − warm_threshold`). */
116
+ get warmAt(): number;
117
+ /**
118
+ * Record one committed transaction and return its fill signal. Emits `warmup` exactly when the block
119
+ * reaches `block_fill_size − warm_threshold`, and `filling` when it reaches `block_fill_size` — after
120
+ * which the counter resets for the next block.
121
+ */
122
+ onCommit(): BlockFillSignal;
123
+ /** Reset the block counter (e.g. an externally-observed rotation the tracker did not drive). */
124
+ reset(): void;
125
+ }
126
+ /** The kind of request arriving at the outgoing tail during its drain window. */
127
+ export type DrainOp =
128
+ /** A *fresh* subscription (`lastKnownRev == 0` or a never-seen subscriber) — bounced to the new tree. */
129
+ "new_subscribe"
130
+ /** A renewal of an existing registration — served through the drain. */
131
+ | "renew"
132
+ /** A replay/backfill/resume request — served through the drain. */
133
+ | "replay";
134
+ /**
135
+ * A `Promoted`-shaped redirect to the rotated tree (`docs/reactivity.md` §Tail rotation step 2). Unlike the
136
+ * cohort-topic tier-based `Promoted`, this redirects to an entirely **new topic** — the new tail's tree at
137
+ * `coord_0(_, newTopicId)` — so it carries the successor's `newTailId` + derived `newTopicId`.
138
+ */
139
+ export interface RotationRedirectV1 {
140
+ readonly v: 1;
141
+ readonly result: "rotated";
142
+ /** New tail block id the topic anchor rotated to, base64url. */
143
+ readonly newTailId: string;
144
+ /** `H(newTailId ‖ "reactivity")` — the new tree's topic id, base64url (the redirect target). */
145
+ readonly newTopicId: string;
146
+ /** Revision at which the rotation took effect. */
147
+ readonly effectiveAtRevision: number;
148
+ }
149
+ /**
150
+ * Narrow an already-parsed value to a {@link RotationRedirectV1}, throwing {@link CohortWireError} on any
151
+ * defect. The redirect rides the recover reply envelope as `kind: "rotated"`
152
+ * ({@link import("./recover.js").RecoverReplyV1}); it carries no signature — like the other recover reply
153
+ * variants it is an unauthenticated routing hint (the subscriber re-walks and verifies the new tree on
154
+ * arrival), so its trust comes from the new tree, not the redirect.
155
+ */
156
+ export declare function validateRotationRedirectV1(value: unknown): RotationRedirectV1;
157
+ /** The drain gate's decision for one inbound request. */
158
+ export type DrainDecision =
159
+ /** Serve normally (renewals / replays during the drain window). */
160
+ {
161
+ readonly kind: "serve";
162
+ }
163
+ /** Bounce to the new tree (a new subscription during the drain window). */
164
+ | {
165
+ readonly kind: "redirect";
166
+ readonly redirect: RotationRedirectV1;
167
+ }
168
+ /** The drain window has elapsed — the outgoing tail holds nothing; the caller re-registers from `d_max`. */
169
+ | {
170
+ readonly kind: "drained";
171
+ };
172
+ /** Construction inputs for a {@link TailDrainGate}. */
173
+ export interface TailDrainGateInit {
174
+ /** Unix ms the rotation took effect (drain starts here). */
175
+ readonly rotatedAt: number;
176
+ /** New tail block id the topic rotated to, base64url. */
177
+ readonly newTailId: string;
178
+ /** Revision at which the rotation took effect. */
179
+ readonly effectiveAtRevision: number;
180
+ /** Drain duration `T_drain` (ms, default {@link T_DRAIN_MS}). */
181
+ readonly tDrainMs?: number;
182
+ /** Ring hash for the `newTopicId` derivation (must match cohort-topic routing). Default db-core SHA-256. */
183
+ readonly hash?: IRingHash;
184
+ }
185
+ /**
186
+ * The outgoing tail's drain state machine. For `T_drain` after a rotation it accepts **renewals** and
187
+ * serves **replays**, but bounces **new subscriptions** with a {@link RotationRedirectV1} to the new tree;
188
+ * after `T_drain` it reports `drained` for everything (the subscriber re-registers from `d_max`).
189
+ */
190
+ export declare class TailDrainGate {
191
+ readonly rotatedAt: number;
192
+ readonly tDrainMs: number;
193
+ private readonly redirect;
194
+ constructor(init: TailDrainGateInit);
195
+ /** Unix ms the drain window closes. */
196
+ get drainEndsAt(): number;
197
+ /** True while the outgoing tail is still draining (within `T_drain` of the rotation). */
198
+ isDraining(now: number): boolean;
199
+ /** The redirect this gate hands a new subscription (exposed for the redirect transport / tests). */
200
+ get rotationRedirect(): RotationRedirectV1;
201
+ /**
202
+ * Classify an inbound request. Within the drain window: `new_subscribe` → redirect, `renew`/`replay` →
203
+ * serve. After the window: `drained` (the old tail has released its forwarder state).
204
+ */
205
+ classify(op: DrainOp, now: number): DrainDecision;
206
+ }
207
+ /** A subscriber's planned re-registration at the rotated tree. */
208
+ export interface ReRegistrationPlan {
209
+ /** New tail block id (raw bytes) to attach under. */
210
+ readonly newTailId: Uint8Array;
211
+ /** `H(newTailId ‖ "reactivity")` (raw bytes) — the new tree's topic id to register at. */
212
+ readonly newTopicId: Uint8Array;
213
+ /** The subscriber's existing `lastRevision`, carried so revisions stay continuous across the rotation. */
214
+ readonly lastRevision: number;
215
+ /** Unix ms at which to fire the re-registration (jittered over `T_rejoin_jitter`). */
216
+ readonly fireAt: number;
217
+ }
218
+ /** Inputs shared by the single and wave re-registration planners. */
219
+ interface ReRegistrationTarget {
220
+ /** The rotation hint (or any object carrying the successor `newTailId`), base64url. */
221
+ readonly hint: Pick<RotationHintV1, "newTailId">;
222
+ /** Ring hash for the `newTopicId` derivation. Default db-core SHA-256. */
223
+ readonly hash?: IRingHash;
224
+ }
225
+ /**
226
+ * Plan **one** subscriber's re-registration at the rotated tree: derive the new `topicId` from the hint's
227
+ * `newTailId`, carry the subscriber's `lastRevision`, and draw a jittered `fireAt` over `T_rejoin_jitter`
228
+ * via the cohort-topic {@link RejoinJitter} (`scheduleRejoin` — a uniform offset, decorrelating this
229
+ * subscriber's re-join from its peers).
230
+ */
231
+ export declare function planReRegistration(opts: ReRegistrationTarget & {
232
+ lastRevision: number;
233
+ now: number;
234
+ jitter: RejoinJitter;
235
+ }): ReRegistrationPlan;
236
+ /**
237
+ * Plan a **whole wave** of subscribers' re-registrations at the rotated tree. Uses the
238
+ * {@link RejoinJitter.scheduleWave} hard-bound staggering so any `T_rejoin_jitter`-long window holds at
239
+ * most the injected jitter's `capPromote` arrivals — the new tail never sees more than
240
+ * `capPromote / T_rejoin_jitter` re-registrations per second.
241
+ *
242
+ * **Caller contract:** rotation is governed by the *fast-promote* bound, so the host MUST build this `jitter`
243
+ * with `capPromote = cap_promote_fast` ({@link import("../cohort-topic/promotion.js").DEFAULT_CAP_PROMOTE_FAST}
244
+ * = 32), **not** the default `createRejoinJitter()` cap (the cohort-failure `cap_promote = 64`). With the
245
+ * default cap the wave bounds to 64/window and silently overruns the documented rotation burst ceiling
246
+ * (`docs/reactivity.md` §Tail rotation rotation-cost). Returns one plan per subscriber, ascending by `fireAt`.
247
+ */
248
+ export declare function planReRegistrationWave(opts: ReRegistrationTarget & {
249
+ subscribers: readonly {
250
+ readonly lastRevision: number;
251
+ }[];
252
+ now: number;
253
+ jitter: RejoinJitter;
254
+ }): ReRegistrationPlan[];
255
+ /** The state migrated from the outgoing tail to the new tail on rotation. */
256
+ export interface RotationHandoff {
257
+ /** The final checkpoint covering `[lastCheckpoint.toRevision + 1, rotationRevision]`. */
258
+ readonly checkpoint: CheckpointSummary;
259
+ /** The revision the rotation took effect at (the high edge the handoff checkpoint covers). */
260
+ readonly rotationRevision: number;
261
+ }
262
+ /**
263
+ * Fold the outgoing tail's replay buffer into the final {@link CheckpointSummary} handed to the new tail
264
+ * (`docs/reactivity.md` §Tail rotation step 5 — the **only** state migration across a rotation). The
265
+ * handoff covers `[lastCheckpoint.toRevision + 1, rotationRevision]`: the revisions still live in the
266
+ * replay ring (above the rolling checkpoint's high edge) up to the rotation revision. Returns `undefined`
267
+ * when the outgoing tail's ring is empty (nothing to migrate). Reuses the rolling checkpoint's own fold
268
+ * options so the merged digest is byte-identical to a steady-state checkpoint.
269
+ */
270
+ export declare function buildRotationHandoffCheckpoint(state: PushState, opts?: {
271
+ rotationRevision?: number;
272
+ }): RotationHandoff | undefined;
273
+ /**
274
+ * Land a {@link RotationHandoff} at the new tail's {@link PushState}: record the inherited checkpoint so a
275
+ * `ResumeV1` whose span crosses the rotation is recoverable from the new tail (the new tail "holds the old
276
+ * checkpoint", `docs/reactivity.md` §Tail rotation step 5). The handoff is a one-time migration — it does
277
+ * not feed the new tail's rolling checkpoint (that rolls from the new tree's own replay-ring eviction).
278
+ */
279
+ export declare function applyRotationHandoff(newTailState: PushState, handoff: RotationHandoff): void;
280
+ /** True iff `summary` covers `revision` (its inclusive `[fromRevision, toRevision]` span). */
281
+ export declare function checkpointCovers(summary: CheckpointSummary, revision: number): boolean;
282
+ export {};
283
+ //# sourceMappingURL=rotation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rotation.d.ts","sourceRoot":"","sources":["../../../src/reactivity/rotation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Reactivity — tail rotation lifecycle (`docs/reactivity.md` §Rotating tail anchor, §Tail rotation).
3
+ *
4
+ * `topicId = H(tailId ‖ "reactivity")` is derived per emission ({@link reactivityTopicId}). When the tail
5
+ * block fills (`block_fill_size` transactions, default 64) a new tail block is born and `tailId` — hence
6
+ * `topicId` — changes; the cohort-topic layer treats the new `topicId` as an **entirely new topic** (fresh
7
+ * tree, new ring coord). Reactivity manages the subscriber/replay-state migration explicitly. This module
8
+ * owns that lifecycle:
9
+ *
10
+ * - **Pre-announce** — the block-filling commit's notification carries {@link RotationHintV1}; built here
11
+ * by {@link buildRotationHint} and detected subscriber-side by {@link detectRotation}.
12
+ * - **Block-fill tracking** — {@link BlockFillTracker} counts transactions in the current tail block and
13
+ * fires anticipatory **warm-up** at `block_fill_size − warm_threshold` and the **filling** signal at
14
+ * `block_fill_size` (the commit that carries the hint).
15
+ * - **Drain** — {@link TailDrainGate} keeps the outgoing tail serving renewals/replays for `T_drain` while
16
+ * bouncing *new* subscriptions with a `Promoted`-shaped {@link RotationRedirectV1} to the new tree.
17
+ * - **Jittered re-registration** — {@link planReRegistration} / {@link planReRegistrationWave} schedule a
18
+ * subscriber's move to the new `topicId` over `T_rejoin_jitter` (the cohort-topic {@link RejoinJitter}),
19
+ * carrying its existing `lastRevision` (revisions are continuous across rotations).
20
+ * - **Buffer-to-checkpoint handoff** — {@link buildRotationHandoffCheckpoint} folds the outgoing tail's
21
+ * replay buffer into a final {@link CheckpointSummary} covering `[lastCheckpoint.toRevision + 1,
22
+ * rotationRevision]`, the **only** state migrated across a rotation; {@link applyRotationHandoff} lands it
23
+ * at the new tail so a `ResumeV1` spanning the rotation is recoverable.
24
+ *
25
+ * Forwarder draining is *emergent*: forwarders under the old tail watch their direct-subscriber count drop
26
+ * as subscribers re-register elsewhere and demote naturally per the cohort-topic demotion protocol (no
27
+ * state migrates; the new tree rebuilds via re-registration). It needs no code here.
28
+ *
29
+ * Coordination with [reactivity-backfill-resume-checkpoints]: that ticket owns the `ResumeReplyV1.TailRotated`
30
+ * variant and the `latestKnownTailId`-staleness classification; this ticket produces the handoff checkpoint
31
+ * and the rotation *condition*. The new tail's {@link PushState.inheritedCheckpoint} (set by
32
+ * {@link applyRotationHandoff}) is the seam the resume classifier consults to answer a checkpoint-window
33
+ * resume whose span crosses the rotation: `classifyResume`/`serveResume` now read it (after the rolling
34
+ * `checkpoint` misses) and serve the inherited summary, so a cross-rotation resume no longer falls to
35
+ * `out_of_window` (`docs/reactivity.md` §Resume, §Tail rotation step 5). The drain-window redirect a new
36
+ * subscription receives, {@link RotationRedirectV1}, is serialized by {@link validateRotationRedirectV1} and
37
+ * rides the recover reply envelope as `kind: "rotated"` ({@link import("./recover.js").RecoverReplyV1}).
38
+ */
39
+ import { bytesToB64url, b64urlToBytes } from "../cohort-topic/wire/codec.js";
40
+ import { createRingHash } from "../cohort-topic/ring-hash.js";
41
+ import { BLOCK_FILL_SIZE_DEFAULT, T_DRAIN_MS, WARM_THRESHOLD_DEFAULT } from "./config.js";
42
+ import { buildCheckpointSummary } from "./checkpoint.js";
43
+ import { reactivityTopicId } from "./topic-anchor.js";
44
+ import { asObject, b64urlField, failWire, reqIntInRange, reqString, requireV1 } from "./wire-validate.js";
45
+ // --- pre-announce + subscriber-side detection --------------------------------
46
+ /**
47
+ * Build the rotation pre-announce embedded in the block-filling commit's notification. The new tail block
48
+ * id is known at the filling commit; the rotation becomes effective at the next revision, so
49
+ * `effectiveAtRevision = fillingRevision + 1` (`docs/reactivity.md` §Tail rotation, worked scenario:
50
+ * revision 5400 carries `{ newTailId: T_6, effectiveAtRevision: 5401 }`).
51
+ */
52
+ export function buildRotationHint(newTailId, fillingRevision) {
53
+ return { newTailId, effectiveAtRevision: fillingRevision + 1 };
54
+ }
55
+ /**
56
+ * Detect tail rotation for a subscriber attached at `tailIdAtAttach` (base64url) from a delivered
57
+ * notification. Rotation is signaled when the delivered `tailId` **or** the `rotationHint.newTailId`
58
+ * differs from `tailIdAtAttach` (`docs/reactivity.md` §Rotating tail anchor). An already-rotated delivery
59
+ * (different `tailId`) takes precedence over a pre-announce.
60
+ */
61
+ export function detectRotation(tailIdAtAttach, n) {
62
+ if (n.tailId !== tailIdAtAttach) {
63
+ // The delivered notification rides the *new* tree already — a hard rotation, not a pre-announce.
64
+ return { rotated: true, newTailId: n.tailId, preAnnounced: false };
65
+ }
66
+ if (n.rotationHint !== undefined && n.rotationHint.newTailId !== tailIdAtAttach) {
67
+ return { rotated: true, newTailId: n.rotationHint.newTailId, preAnnounced: true };
68
+ }
69
+ return { rotated: false, preAnnounced: false };
70
+ }
71
+ /**
72
+ * Counts transactions committed into the current tail block and emits the {@link BlockFillSignal} that
73
+ * drives anticipatory warm-up and the block-filling rotation trigger (`docs/reactivity.md` §Tail rotation,
74
+ * §Anticipatory warm-up). One tracker per collection on the tail-cohort primary.
75
+ */
76
+ export class BlockFillTracker {
77
+ blockFillSize;
78
+ warmThreshold;
79
+ count = 0;
80
+ constructor(init = {}) {
81
+ const blockFillSize = init.blockFillSize ?? BLOCK_FILL_SIZE_DEFAULT;
82
+ const warmThreshold = init.warmThreshold ?? WARM_THRESHOLD_DEFAULT;
83
+ if (!Number.isInteger(blockFillSize) || blockFillSize < 1) {
84
+ throw new RangeError(`reactivity rotation: blockFillSize must be an integer >= 1, got ${blockFillSize}`);
85
+ }
86
+ if (!Number.isInteger(warmThreshold) || warmThreshold < 0 || warmThreshold >= blockFillSize) {
87
+ throw new RangeError(`reactivity rotation: warmThreshold must be an integer in 0..${blockFillSize - 1}, got ${warmThreshold}`);
88
+ }
89
+ this.blockFillSize = blockFillSize;
90
+ this.warmThreshold = warmThreshold;
91
+ }
92
+ /** Transactions committed into the current (not-yet-rotated) block. */
93
+ get transactionsInBlock() {
94
+ return this.count;
95
+ }
96
+ /** The fill count at which anticipatory warm-up fires (`block_fill_size − warm_threshold`). */
97
+ get warmAt() {
98
+ return this.blockFillSize - this.warmThreshold;
99
+ }
100
+ /**
101
+ * Record one committed transaction and return its fill signal. Emits `warmup` exactly when the block
102
+ * reaches `block_fill_size − warm_threshold`, and `filling` when it reaches `block_fill_size` — after
103
+ * which the counter resets for the next block.
104
+ */
105
+ onCommit() {
106
+ this.count += 1;
107
+ if (this.count >= this.blockFillSize) {
108
+ const count = this.count;
109
+ this.count = 0; // a new tail block is born; start counting it
110
+ return { kind: "filling", count };
111
+ }
112
+ if (this.count === this.warmAt && this.warmThreshold > 0) {
113
+ return { kind: "warmup", count: this.count, remaining: this.blockFillSize - this.count };
114
+ }
115
+ return { kind: "none", count: this.count };
116
+ }
117
+ /** Reset the block counter (e.g. an externally-observed rotation the tracker did not drive). */
118
+ reset() {
119
+ this.count = 0;
120
+ }
121
+ }
122
+ /**
123
+ * Narrow an already-parsed value to a {@link RotationRedirectV1}, throwing {@link CohortWireError} on any
124
+ * defect. The redirect rides the recover reply envelope as `kind: "rotated"`
125
+ * ({@link import("./recover.js").RecoverReplyV1}); it carries no signature — like the other recover reply
126
+ * variants it is an unauthenticated routing hint (the subscriber re-walks and verifies the new tree on
127
+ * arrival), so its trust comes from the new tree, not the redirect.
128
+ */
129
+ export function validateRotationRedirectV1(value) {
130
+ const what = "RotationRedirectV1";
131
+ const obj = asObject(value, what);
132
+ requireV1(obj, what);
133
+ if (obj["result"] !== "rotated") {
134
+ failWire(`${what}: field "result" must be "rotated"`);
135
+ }
136
+ return {
137
+ v: 1,
138
+ result: "rotated",
139
+ newTailId: b64urlField(reqString(obj, "newTailId", what), "newTailId", what),
140
+ newTopicId: b64urlField(reqString(obj, "newTopicId", what), "newTopicId", what),
141
+ effectiveAtRevision: reqIntInRange(obj, "effectiveAtRevision", what, 0),
142
+ };
143
+ }
144
+ /**
145
+ * The outgoing tail's drain state machine. For `T_drain` after a rotation it accepts **renewals** and
146
+ * serves **replays**, but bounces **new subscriptions** with a {@link RotationRedirectV1} to the new tree;
147
+ * after `T_drain` it reports `drained` for everything (the subscriber re-registers from `d_max`).
148
+ */
149
+ export class TailDrainGate {
150
+ rotatedAt;
151
+ tDrainMs;
152
+ redirect;
153
+ constructor(init) {
154
+ const tDrainMs = init.tDrainMs ?? T_DRAIN_MS;
155
+ if (!(tDrainMs > 0)) {
156
+ throw new RangeError(`reactivity rotation: tDrainMs must be > 0, got ${tDrainMs}`);
157
+ }
158
+ this.rotatedAt = init.rotatedAt;
159
+ this.tDrainMs = tDrainMs;
160
+ const hash = init.hash ?? createRingHash();
161
+ const newTopicId = bytesToB64url(reactivityTopicId(b64urlToBytes(init.newTailId), hash));
162
+ this.redirect = {
163
+ v: 1,
164
+ result: "rotated",
165
+ newTailId: init.newTailId,
166
+ newTopicId,
167
+ effectiveAtRevision: init.effectiveAtRevision,
168
+ };
169
+ }
170
+ /** Unix ms the drain window closes. */
171
+ get drainEndsAt() {
172
+ return this.rotatedAt + this.tDrainMs;
173
+ }
174
+ /** True while the outgoing tail is still draining (within `T_drain` of the rotation). */
175
+ isDraining(now) {
176
+ return now < this.drainEndsAt;
177
+ }
178
+ /** The redirect this gate hands a new subscription (exposed for the redirect transport / tests). */
179
+ get rotationRedirect() {
180
+ return this.redirect;
181
+ }
182
+ /**
183
+ * Classify an inbound request. Within the drain window: `new_subscribe` → redirect, `renew`/`replay` →
184
+ * serve. After the window: `drained` (the old tail has released its forwarder state).
185
+ */
186
+ classify(op, now) {
187
+ if (!this.isDraining(now)) {
188
+ return { kind: "drained" };
189
+ }
190
+ return op === "new_subscribe" ? { kind: "redirect", redirect: this.redirect } : { kind: "serve" };
191
+ }
192
+ }
193
+ /** Resolve `(newTailId, newTopicId)` raw bytes for a rotation target. */
194
+ function resolveRotationTarget(target) {
195
+ const newTailId = b64urlToBytes(target.hint.newTailId);
196
+ const newTopicId = reactivityTopicId(newTailId, target.hash ?? createRingHash());
197
+ return { newTailId, newTopicId };
198
+ }
199
+ /**
200
+ * Plan **one** subscriber's re-registration at the rotated tree: derive the new `topicId` from the hint's
201
+ * `newTailId`, carry the subscriber's `lastRevision`, and draw a jittered `fireAt` over `T_rejoin_jitter`
202
+ * via the cohort-topic {@link RejoinJitter} (`scheduleRejoin` — a uniform offset, decorrelating this
203
+ * subscriber's re-join from its peers).
204
+ */
205
+ export function planReRegistration(opts) {
206
+ const { newTailId, newTopicId } = resolveRotationTarget(opts);
207
+ return { newTailId, newTopicId, lastRevision: opts.lastRevision, fireAt: opts.jitter.scheduleRejoin(opts.now) };
208
+ }
209
+ /**
210
+ * Plan a **whole wave** of subscribers' re-registrations at the rotated tree. Uses the
211
+ * {@link RejoinJitter.scheduleWave} hard-bound staggering so any `T_rejoin_jitter`-long window holds at
212
+ * most the injected jitter's `capPromote` arrivals — the new tail never sees more than
213
+ * `capPromote / T_rejoin_jitter` re-registrations per second.
214
+ *
215
+ * **Caller contract:** rotation is governed by the *fast-promote* bound, so the host MUST build this `jitter`
216
+ * with `capPromote = cap_promote_fast` ({@link import("../cohort-topic/promotion.js").DEFAULT_CAP_PROMOTE_FAST}
217
+ * = 32), **not** the default `createRejoinJitter()` cap (the cohort-failure `cap_promote = 64`). With the
218
+ * default cap the wave bounds to 64/window and silently overruns the documented rotation burst ceiling
219
+ * (`docs/reactivity.md` §Tail rotation rotation-cost). Returns one plan per subscriber, ascending by `fireAt`.
220
+ */
221
+ export function planReRegistrationWave(opts) {
222
+ const { newTailId, newTopicId } = resolveRotationTarget(opts);
223
+ const fireAts = opts.jitter.scheduleWave(opts.subscribers.length, opts.now);
224
+ return opts.subscribers.map((s, i) => ({ newTailId, newTopicId, lastRevision: s.lastRevision, fireAt: fireAts[i] }));
225
+ }
226
+ /**
227
+ * Fold the outgoing tail's replay buffer into the final {@link CheckpointSummary} handed to the new tail
228
+ * (`docs/reactivity.md` §Tail rotation step 5 — the **only** state migration across a rotation). The
229
+ * handoff covers `[lastCheckpoint.toRevision + 1, rotationRevision]`: the revisions still live in the
230
+ * replay ring (above the rolling checkpoint's high edge) up to the rotation revision. Returns `undefined`
231
+ * when the outgoing tail's ring is empty (nothing to migrate). Reuses the rolling checkpoint's own fold
232
+ * options so the merged digest is byte-identical to a steady-state checkpoint.
233
+ */
234
+ export function buildRotationHandoffCheckpoint(state, opts = {}) {
235
+ const ringLow = state.replayBuffer.lowRevision;
236
+ const ringHigh = state.replayBuffer.highRevision;
237
+ if (ringLow === undefined || ringHigh === undefined) {
238
+ return undefined; // nothing live in the ring to migrate
239
+ }
240
+ const rotationRevision = opts.rotationRevision ?? ringHigh;
241
+ // The handoff continues immediately above the rolling checkpoint's high edge; when no checkpoint has
242
+ // formed yet, that is the ring's low edge. The two windows stack, so `from` abuts `lastCheckpoint`.
243
+ const priorCheckpointTo = state.checkpoint.toRevision;
244
+ const from = priorCheckpointTo !== undefined ? priorCheckpointTo + 1 : ringLow;
245
+ const entries = state.replayBuffer.range(from, rotationRevision);
246
+ const checkpoint = buildCheckpointSummary(entries, state.checkpoint.foldOptions);
247
+ if (checkpoint === undefined) {
248
+ return undefined;
249
+ }
250
+ return { checkpoint, rotationRevision };
251
+ }
252
+ /**
253
+ * Land a {@link RotationHandoff} at the new tail's {@link PushState}: record the inherited checkpoint so a
254
+ * `ResumeV1` whose span crosses the rotation is recoverable from the new tail (the new tail "holds the old
255
+ * checkpoint", `docs/reactivity.md` §Tail rotation step 5). The handoff is a one-time migration — it does
256
+ * not feed the new tail's rolling checkpoint (that rolls from the new tree's own replay-ring eviction).
257
+ */
258
+ export function applyRotationHandoff(newTailState, handoff) {
259
+ newTailState.adoptRotationCheckpoint(handoff.checkpoint);
260
+ }
261
+ /** True iff `summary` covers `revision` (its inclusive `[fromRevision, toRevision]` span). */
262
+ export function checkpointCovers(summary, revision) {
263
+ return revision >= summary.fromRevision && revision <= summary.toRevision;
264
+ }
265
+ //# sourceMappingURL=rotation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rotation.js","sourceRoot":"","sources":["../../../src/reactivity/rotation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Reactivity — subscriber delivery path (`docs/reactivity.md` §Delivery).
3
+ *
4
+ * A subscriber receiving a notification:
5
+ * 1. **verifies** `sig` against the cached tail-cohort `MembershipCertV1` (the {@link NotificationVerifier}
6
+ * owns the **one fetch-and-retry** on a stale cache);
7
+ * 2. checks `revision == lastRevision + 1`. If not, requests a backfill for the gap
8
+ * `[lastRevision + 1, revision]` via the {@link requestBackfill} seam (the `BackfillV1` request shape
9
+ * lands in [reactivity-backfill-resume-checkpoints]; this ticket only *detects* the gap and calls the
10
+ * hook);
11
+ * 3. updates `lastRevision` once revisions are contiguous;
12
+ * 4. surfaces the notification to the application layer.
13
+ *
14
+ * Subscribers dedupe by `(collectionId, revision)`; duplicates from forwarder retries are discarded.
15
+ * A fresh subscription (`lastKnownRev == 0`, uninitialized) adopts the first verified notification as its
16
+ * baseline rather than demanding a backfill from revision 1.
17
+ */
18
+ import type { NotificationV1 } from "./wire.js";
19
+ import type { NotificationVerifier } from "./verify.js";
20
+ /** The delivery outcome for one inbound notification. */
21
+ export type DeliveryOutcome =
22
+ /** Verified and contiguous: surfaced to the application, `lastRevision` advanced. */
23
+ "delivered"
24
+ /** `(collectionId, revision)` already delivered: discarded. */
25
+ | "duplicate"
26
+ /** Verified but ahead of `lastRevision + 1`: a backfill was requested; not yet surfaced. */
27
+ | "gap"
28
+ /** Signature did not verify (even after the one refetch): dropped. */
29
+ | "untrusted"
30
+ /** For a different collection than this subscription: ignored. */
31
+ | "foreign";
32
+ /** Application sink + backfill seam for a {@link ReactivitySubscriber}. */
33
+ export interface ReactivitySubscriberDeps {
34
+ /** Collection this subscription tracks, base64url (matches {@link NotificationV1.collectionId}). */
35
+ readonly collectionId: string;
36
+ /** Verifies inbound notifications (owns the single stale-cache refetch). */
37
+ readonly verifier: NotificationVerifier;
38
+ /** Surface a verified, contiguous notification to the application. */
39
+ readonly deliver: (n: NotificationV1) => void;
40
+ /**
41
+ * Request a backfill for the inclusive revision gap `[from, to]`. The `BackfillV1` request/response
42
+ * lands in [reactivity-backfill-resume-checkpoints]; the returned entries are fed back through
43
+ * {@link ReactivitySubscriber.onNotification} to close the gap. Absent ⇒ the gap is recorded only.
44
+ */
45
+ readonly requestBackfill?: (from: number, to: number) => void;
46
+ /** Last revision already held; `0` (default) ⇒ fresh subscribe, adopt the first notification as baseline. */
47
+ readonly lastKnownRev?: number;
48
+ }
49
+ /** Drives the subscriber-side verify → contiguity → deliver path for one collection. */
50
+ export interface ReactivitySubscriber {
51
+ /** Last contiguously-delivered revision (`lastKnownRev` until the first delivery). */
52
+ readonly lastRevision: number;
53
+ /** Process one inbound notification; returns the {@link DeliveryOutcome}. */
54
+ onNotification(n: NotificationV1): Promise<DeliveryOutcome>;
55
+ /**
56
+ * Advance the contiguity head to `revision` because a **verified parent checkpoint** summarized every
57
+ * revision up to it (`docs/reactivity.md` §Parent checkpoint summaries). The subscriber then treats
58
+ * `revision + 1` as the next contiguous revision, so the checkpoint's `recentEntries` replay without a
59
+ * spurious gap/backfill for the digest-covered range. Only advances — never rewinds past delivered
60
+ * state. The merged digest itself is applied by the application; this just moves the contiguity head.
61
+ */
62
+ rebaseline(revision: number): void;
63
+ }
64
+ /** Build a {@link ReactivitySubscriber} for one collection. */
65
+ export declare function createReactivitySubscriber(deps: ReactivitySubscriberDeps): ReactivitySubscriber;
66
+ //# sourceMappingURL=subscriber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscriber.d.ts","sourceRoot":"","sources":["../../../src/reactivity/subscriber.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,yDAAyD;AACzD,MAAM,MAAM,eAAe;AAC1B,qFAAqF;AACnF,WAAW;AACb,+DAA+D;GAC7D,WAAW;AACb,4FAA4F;GAC1F,KAAK;AACP,sEAAsE;GACpE,WAAW;AACb,kEAAkE;GAChE,SAAS,CAAC;AAEb,2EAA2E;AAC3E,MAAM,WAAW,wBAAwB;IACxC,oGAAoG;IACpG,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,sEAAsE;IACtE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9D,6GAA6G;IAC7G,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,wFAAwF;AACxF,MAAM,WAAW,oBAAoB;IACpC,sFAAsF;IACtF,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,6EAA6E;IAC7E,cAAc,CAAC,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5D;;;;;;OAMG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AA8DD,+DAA+D;AAC/D,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,wBAAwB,GAAG,oBAAoB,CAE/F"}