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