@optimystic/db-core 0.13.4 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cluster/structs.d.ts +29 -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 +19 -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 +44 -2
- package/dist/src/transactor/network-transactor.d.ts.map +1 -1
- package/dist/src/transactor/network-transactor.js +132 -17
- package/dist/src/transactor/network-transactor.js.map +1 -1
- package/package.json +3 -2
- package/src/cluster/structs.ts +86 -57
- 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 +19 -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 +162 -19
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matchmaking — multi-cohort sweep (db-core, pure orchestration of the hot-topic representative sample).
|
|
3
|
+
*
|
|
4
|
+
* Per `docs/matchmaking.md` §Multi-cohort sweep. When a topic is hot enough that its providers live
|
|
5
|
+
* across many tier-`d >= 1` cohorts, a seeker that wants a *representative* cross-ring sample (rather
|
|
6
|
+
* than the prefix-biased single-cohort slice) does:
|
|
7
|
+
*
|
|
8
|
+
* 1. Registers at its natural tier as usual (the single-cohort {@link import("./seeker-walk.js").decide}
|
|
9
|
+
* walk — not this module).
|
|
10
|
+
* 2. Queries the **root** cohort, which returns an {@link AggregateCountV1}: log-bucketed provider counts
|
|
11
|
+
* per tier-1 prefix shard, **threshold-signed**. A cold root that fell through to `NoState` produces
|
|
12
|
+
* no aggregate (the producer gates on tree depth — see `db-p2p/matchmaking/aggregate-counts.ts`).
|
|
13
|
+
* 3. Selects the high-population tier-1 shards ({@link selectShards}) and queries them directly, unioning
|
|
14
|
+
* the returned providers into a deduped, re-validated set.
|
|
15
|
+
*
|
|
16
|
+
* This module is **pure**: the root-aggregate fetch, the per-shard query, and the optional threshold-sig
|
|
17
|
+
* verification are injected as a {@link MultiCohortSweepPorts} port (db-p2p binds them to the matchmaking
|
|
18
|
+
* query RPCs), exactly like the single-cohort walk splits pure `decide` (here) from the db-p2p walk
|
|
19
|
+
* client. The advisory trust model is preserved end-to-end: every shard entry is re-validated with
|
|
20
|
+
* {@link verifyProviderEntry} before it counts, so a lying shard primary buys nothing.
|
|
21
|
+
*
|
|
22
|
+
* The sweep costs more RPCs than the single-cohort sample and is reserved for representativeness-over-
|
|
23
|
+
* latency use cases (voting quorums, capability fairness audits); db-p2p binds it to the voting
|
|
24
|
+
* `QuorumDiscovery.sweep` port.
|
|
25
|
+
*/
|
|
26
|
+
import { matchesFilter } from "./capability-filter.js";
|
|
27
|
+
import { verifyProviderEntry } from "./wire.js";
|
|
28
|
+
/** The tier whose prefix shards the sweep ranges over (`docs/matchmaking.md` §Wire formats — typically 1). */
|
|
29
|
+
export const DEFAULT_SWEEP_TARGET_TIER = 1;
|
|
30
|
+
/** Fan-out ceiling: never query more than this many shards in one sweep (bounds RPC cost). */
|
|
31
|
+
export const DEFAULT_SWEEP_MAX_SHARDS = 16;
|
|
32
|
+
/**
|
|
33
|
+
* Multiplier on `wantCount` when accumulating bucketed shard populations. `1` because {@link logBucketCount}
|
|
34
|
+
* already rounds counts *down* — the true population is `>=` the reported sum, so the selection already
|
|
35
|
+
* over-provisions without an extra factor.
|
|
36
|
+
*/
|
|
37
|
+
export const DEFAULT_SWEEP_OVERPROVISION = 1;
|
|
38
|
+
/**
|
|
39
|
+
* Choose which tier-1 shards to query from an {@link AggregateCountV1}: the highest-population shards
|
|
40
|
+
* first (ties broken by ascending `prefixSlot` for determinism), accumulating bucketed counts until they
|
|
41
|
+
* cover `wantCount * overprovision`, capped at `maxShards`. Empty shards (`count === 0`) are skipped.
|
|
42
|
+
* Pure and deterministic.
|
|
43
|
+
*/
|
|
44
|
+
export function selectShards(aggregate, opts) {
|
|
45
|
+
const targetTier = opts.targetTier ?? DEFAULT_SWEEP_TARGET_TIER;
|
|
46
|
+
const maxShards = opts.maxShards ?? DEFAULT_SWEEP_MAX_SHARDS;
|
|
47
|
+
const overprovision = opts.overprovision ?? DEFAULT_SWEEP_OVERPROVISION;
|
|
48
|
+
const need = Math.max(1, Math.ceil(opts.wantCount * overprovision));
|
|
49
|
+
const ranked = aggregate.bucketCounts
|
|
50
|
+
.filter((b) => b.targetTier === targetTier && b.count > 0)
|
|
51
|
+
.sort((a, b) => b.count - a.count || a.prefixSlot - b.prefixSlot);
|
|
52
|
+
const selected = [];
|
|
53
|
+
let cumulative = 0;
|
|
54
|
+
for (const bucket of ranked) {
|
|
55
|
+
if (selected.length >= maxShards) {
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
selected.push({ prefixSlot: bucket.prefixSlot, targetTier: bucket.targetTier, bucketedCount: bucket.count });
|
|
59
|
+
cumulative += bucket.count;
|
|
60
|
+
if (cumulative >= need) {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return selected;
|
|
65
|
+
}
|
|
66
|
+
/** An empty result (no aggregate, or an aggregate that failed verification). */
|
|
67
|
+
function emptyResult(aggregateAvailable, aggregateTrusted) {
|
|
68
|
+
return { providers: [], selectedShards: [], shardsQueried: 0, aggregateAvailable, aggregateTrusted };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Run the multi-cohort sweep (`docs/matchmaking.md` §Multi-cohort sweep): fetch the root aggregate,
|
|
72
|
+
* threshold-verify it, select high-population shards, query each, and union the deduped + re-validated
|
|
73
|
+
* providers. A cold root (no aggregate) or an aggregate that fails verification yields an empty set so
|
|
74
|
+
* the caller falls back to the single-cohort sample. Each shard entry is filtered and
|
|
75
|
+
* `registrationSig`-re-validated before it counts — the cohort vouches only for "the set I held".
|
|
76
|
+
*
|
|
77
|
+
* When `opts.patienceMs` is supplied, a wall-clock deadline is fixed at entry and the shard fan-out
|
|
78
|
+
* stops as soon as the budget drains — mirroring the walk leg's patience model. This is
|
|
79
|
+
* "stop starting new shard queries": an in-flight `queryShard` call is not cancelled mid-flight.
|
|
80
|
+
* When `opts.patienceMs` is absent, every elected shard is queried (today's behaviour).
|
|
81
|
+
*/
|
|
82
|
+
export async function runMultiCohortSweep(ports, opts) {
|
|
83
|
+
// Budget: optional wall-clock deadline mirroring the walk leg. Only consulted when patienceMs is set.
|
|
84
|
+
const clock = opts.clock ?? (() => Date.now());
|
|
85
|
+
const deadline = opts.patienceMs !== undefined ? clock() + opts.patienceMs : undefined;
|
|
86
|
+
const remaining = () => (deadline !== undefined ? Math.max(0, deadline - clock()) : Infinity);
|
|
87
|
+
// If the budget is already drained on entry, skip even the aggregate RPC — the walk already consumed
|
|
88
|
+
// all patience and there is no point spending an RPC on an aggregate we have no time to act on.
|
|
89
|
+
if (deadline !== undefined && remaining() <= 0) {
|
|
90
|
+
return emptyResult(false, false);
|
|
91
|
+
}
|
|
92
|
+
const aggregate = await ports.fetchAggregate();
|
|
93
|
+
if (aggregate === undefined) {
|
|
94
|
+
return emptyResult(false, false);
|
|
95
|
+
}
|
|
96
|
+
// `aggregateTrusted` is asserted only when a verifier actually ran and passed. With no verifier injected
|
|
97
|
+
// the sweep still proceeds — every shard entry is `registrationSig`-re-validated below, so a forged
|
|
98
|
+
// aggregate can at worst mis-steer shard selection (wasted RPCs / a thinner sample), never inject
|
|
99
|
+
// providers — but the flag stays honest rather than claiming a trust that was never established.
|
|
100
|
+
const aggregateTrusted = ports.verifyAggregate !== undefined;
|
|
101
|
+
if (ports.verifyAggregate !== undefined && !ports.verifyAggregate(aggregate)) {
|
|
102
|
+
return emptyResult(true, false);
|
|
103
|
+
}
|
|
104
|
+
const selectShardOpts = { wantCount: opts.wantCount };
|
|
105
|
+
if (opts.targetTier !== undefined) {
|
|
106
|
+
selectShardOpts.targetTier = opts.targetTier;
|
|
107
|
+
}
|
|
108
|
+
if (opts.maxShards !== undefined) {
|
|
109
|
+
selectShardOpts.maxShards = opts.maxShards;
|
|
110
|
+
}
|
|
111
|
+
if (opts.overprovision !== undefined) {
|
|
112
|
+
selectShardOpts.overprovision = opts.overprovision;
|
|
113
|
+
}
|
|
114
|
+
const selected = selectShards(aggregate, selectShardOpts);
|
|
115
|
+
const matched = new Map();
|
|
116
|
+
let shardsQueried = 0;
|
|
117
|
+
for (const shard of selected) {
|
|
118
|
+
// Stop starting new shard queries once the budget has drained.
|
|
119
|
+
if (remaining() <= 0) {
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
const entries = await ports.queryShard({ prefixSlot: shard.prefixSlot, targetTier: shard.targetTier });
|
|
123
|
+
shardsQueried++;
|
|
124
|
+
for (const entry of entries) {
|
|
125
|
+
if (!matchesFilter(entry, opts.filter)) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (!verifyProviderEntry(opts.topicId, entry, opts.verifyEntry)) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
matched.set(entry.participantId, entry);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
providers: [...matched.values()],
|
|
136
|
+
selectedShards: selected,
|
|
137
|
+
shardsQueried,
|
|
138
|
+
aggregateAvailable: true,
|
|
139
|
+
aggregateTrusted,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=multi-cohort-seeker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-cohort-seeker.js","sourceRoot":"","sources":["../../../src/matchmaking/multi-cohort-seeker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matchmaking — provider decision/state (db-core, transport-agnostic).
|
|
3
|
+
*
|
|
4
|
+
* A {@link MatchmakingProvider} owns the live provider state for one topic: its capability tags, its
|
|
5
|
+
* current `capacityBudget`, and a correlation id (registration identity; not bound into the
|
|
6
|
+
* matchmaking signature — see {@link providerSigningPayload} option (b)). It builds the
|
|
7
|
+
* signed {@link ProviderAppPayloadV1} (and the opaque bytes for the cohort-topic
|
|
8
|
+
* `RegisterV1.appPayload` slot) that the db-p2p `provider-manager` registers at cohort-topic tier
|
|
9
|
+
* **T2** (`docs/matchmaking.md` §Provider registration).
|
|
10
|
+
*
|
|
11
|
+
* It is crypto-free: signing is an injected callback (db-p2p supplies the libp2p peer key), matching
|
|
12
|
+
* the cohort-topic {@link import("../cohort-topic/service.js").ParticipantSigner} pattern.
|
|
13
|
+
*
|
|
14
|
+
* Self-throttling (`docs/matchmaking.md` §Provider self-throttling) is expressed here as state:
|
|
15
|
+
* - **Signal full** — {@link MatchmakingProvider.signalFull} sets `capacityBudget = 0`; the provider
|
|
16
|
+
* stays listed as "available but at capacity". The manager re-registers to push the new payload
|
|
17
|
+
* (the cohort-topic `RenewV1` carries no `appPayload`, so a capacity change is a re-register, not a
|
|
18
|
+
* ping — see the implement handoff).
|
|
19
|
+
* - **Withdraw** — {@link MatchmakingProvider.markWithdrawn} records intent; the manager stops
|
|
20
|
+
* renewing so the record ages out by TTL. Withdrawal is an **optimization, not a correctness
|
|
21
|
+
* requirement** (§Provider self-throttling, GROUNDING resolution): a non-withdrawn registration is
|
|
22
|
+
* bounded by TTL eviction.
|
|
23
|
+
*/
|
|
24
|
+
import { type ProviderAppPayloadV1 } from "./wire.js";
|
|
25
|
+
/** Construction inputs for a {@link MatchmakingProvider}. */
|
|
26
|
+
export interface MatchmakingProviderOptions {
|
|
27
|
+
/** The matchmaking topic this provider serves (from {@link import("./topic-anchor.js").MatchTopicAnchor}). */
|
|
28
|
+
readonly topicId: Uint8Array;
|
|
29
|
+
/** Application-defined capability tags. */
|
|
30
|
+
readonly capabilities: readonly string[];
|
|
31
|
+
/** Initial concurrent-task budget (integer `>= 0`). */
|
|
32
|
+
readonly capacityBudget: number;
|
|
33
|
+
/** Multiaddr or PeerId-based callback. */
|
|
34
|
+
readonly contactHint: string;
|
|
35
|
+
/** Optional soft expiry hint (unix ms). */
|
|
36
|
+
readonly serviceUntil?: number;
|
|
37
|
+
/** Sign the canonical registration image; resolves the base64url signature. */
|
|
38
|
+
readonly sign: (payload: Uint8Array) => Promise<string>;
|
|
39
|
+
/** 16-byte registration correlation id (not signature-bound); default fresh CSPRNG bytes. */
|
|
40
|
+
readonly correlationId?: Uint8Array;
|
|
41
|
+
/** CSPRNG source (injectable for deterministic tests). Default `@noble/hashes` `randomBytes`. */
|
|
42
|
+
readonly randomBytes?: (n: number) => Uint8Array;
|
|
43
|
+
}
|
|
44
|
+
/** Live provider state + signed-payload builder for one matchmaking topic. */
|
|
45
|
+
export declare class MatchmakingProvider {
|
|
46
|
+
readonly topicId: Uint8Array;
|
|
47
|
+
readonly correlationId: Uint8Array;
|
|
48
|
+
private readonly capabilities;
|
|
49
|
+
private readonly contactHint;
|
|
50
|
+
private readonly serviceUntil?;
|
|
51
|
+
private readonly sign;
|
|
52
|
+
private capacity;
|
|
53
|
+
private withdrawnFlag;
|
|
54
|
+
constructor(options: MatchmakingProviderOptions);
|
|
55
|
+
/** Current concurrent-task budget; `0` means "listed but full". */
|
|
56
|
+
get capacityBudget(): number;
|
|
57
|
+
/** True once {@link markWithdrawn} has been called (the manager should stop renewing). */
|
|
58
|
+
get withdrawn(): boolean;
|
|
59
|
+
/** Set the live budget (integer `>= 0`). The next built payload reflects it. */
|
|
60
|
+
setCapacity(budget: number): void;
|
|
61
|
+
/** Signal "available but at capacity" by setting `capacityBudget = 0` (§Provider self-throttling). */
|
|
62
|
+
signalFull(): void;
|
|
63
|
+
/** Record withdrawal intent; the manager stops renewing so the record TTL-expires (optimization). */
|
|
64
|
+
markWithdrawn(): void;
|
|
65
|
+
/** Build the signed {@link ProviderAppPayloadV1} reflecting the current capacity. */
|
|
66
|
+
buildAppPayload(): Promise<ProviderAppPayloadV1>;
|
|
67
|
+
/** Build the opaque bytes for the cohort-topic `RegisterV1.appPayload` slot. */
|
|
68
|
+
appPayloadBytes(): Promise<Uint8Array>;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/matchmaking/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAA0B,KAAK,oBAAoB,EAA4B,MAAM,WAAW,CAAC;AAExG,6DAA6D;AAC7D,MAAM,WAAW,0BAA0B;IAC1C,8GAA8G;IAC9G,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,2CAA2C;IAC3C,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,uDAAuD;IACvD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,0CAA0C;IAC1C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,2CAA2C;IAC3C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD,6FAA6F;IAC7F,QAAQ,CAAC,aAAa,CAAC,EAAE,UAAU,CAAC;IACpC,iGAAiG;IACjG,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,UAAU,CAAC;CACjD;AAED,8EAA8E;AAC9E,qBAAa,mBAAmB;IAC/B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,UAAU,CAAC;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA2C;IAChE,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,aAAa,CAAS;gBAElB,OAAO,EAAE,0BAA0B;IAW/C,mEAAmE;IACnE,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED,0FAA0F;IAC1F,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,gFAAgF;IAChF,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIjC,sGAAsG;IACtG,UAAU,IAAI,IAAI;IAIlB,qGAAqG;IACrG,aAAa,IAAI,IAAI;IAIrB,qFAAqF;IAC/E,eAAe,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAetD,gFAAgF;IAC1E,eAAe,IAAI,OAAO,CAAC,UAAU,CAAC;CAG5C"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matchmaking — provider decision/state (db-core, transport-agnostic).
|
|
3
|
+
*
|
|
4
|
+
* A {@link MatchmakingProvider} owns the live provider state for one topic: its capability tags, its
|
|
5
|
+
* current `capacityBudget`, and a correlation id (registration identity; not bound into the
|
|
6
|
+
* matchmaking signature — see {@link providerSigningPayload} option (b)). It builds the
|
|
7
|
+
* signed {@link ProviderAppPayloadV1} (and the opaque bytes for the cohort-topic
|
|
8
|
+
* `RegisterV1.appPayload` slot) that the db-p2p `provider-manager` registers at cohort-topic tier
|
|
9
|
+
* **T2** (`docs/matchmaking.md` §Provider registration).
|
|
10
|
+
*
|
|
11
|
+
* It is crypto-free: signing is an injected callback (db-p2p supplies the libp2p peer key), matching
|
|
12
|
+
* the cohort-topic {@link import("../cohort-topic/service.js").ParticipantSigner} pattern.
|
|
13
|
+
*
|
|
14
|
+
* Self-throttling (`docs/matchmaking.md` §Provider self-throttling) is expressed here as state:
|
|
15
|
+
* - **Signal full** — {@link MatchmakingProvider.signalFull} sets `capacityBudget = 0`; the provider
|
|
16
|
+
* stays listed as "available but at capacity". The manager re-registers to push the new payload
|
|
17
|
+
* (the cohort-topic `RenewV1` carries no `appPayload`, so a capacity change is a re-register, not a
|
|
18
|
+
* ping — see the implement handoff).
|
|
19
|
+
* - **Withdraw** — {@link MatchmakingProvider.markWithdrawn} records intent; the manager stops
|
|
20
|
+
* renewing so the record ages out by TTL. Withdrawal is an **optimization, not a correctness
|
|
21
|
+
* requirement** (§Provider self-throttling, GROUNDING resolution): a non-withdrawn registration is
|
|
22
|
+
* bounded by TTL eviction.
|
|
23
|
+
*/
|
|
24
|
+
import { randomBytes } from "@noble/hashes/utils.js";
|
|
25
|
+
import { providerSigningPayload, encodeProviderAppPayload } from "./wire.js";
|
|
26
|
+
/** Live provider state + signed-payload builder for one matchmaking topic. */
|
|
27
|
+
export class MatchmakingProvider {
|
|
28
|
+
topicId;
|
|
29
|
+
correlationId;
|
|
30
|
+
capabilities;
|
|
31
|
+
contactHint;
|
|
32
|
+
serviceUntil;
|
|
33
|
+
sign;
|
|
34
|
+
capacity;
|
|
35
|
+
withdrawnFlag = false;
|
|
36
|
+
constructor(options) {
|
|
37
|
+
this.topicId = options.topicId;
|
|
38
|
+
this.capabilities = [...options.capabilities];
|
|
39
|
+
this.contactHint = options.contactHint;
|
|
40
|
+
this.serviceUntil = options.serviceUntil;
|
|
41
|
+
this.sign = options.sign;
|
|
42
|
+
this.capacity = requireBudget(options.capacityBudget);
|
|
43
|
+
const rand = options.randomBytes ?? randomBytes;
|
|
44
|
+
this.correlationId = options.correlationId ?? rand(16);
|
|
45
|
+
}
|
|
46
|
+
/** Current concurrent-task budget; `0` means "listed but full". */
|
|
47
|
+
get capacityBudget() {
|
|
48
|
+
return this.capacity;
|
|
49
|
+
}
|
|
50
|
+
/** True once {@link markWithdrawn} has been called (the manager should stop renewing). */
|
|
51
|
+
get withdrawn() {
|
|
52
|
+
return this.withdrawnFlag;
|
|
53
|
+
}
|
|
54
|
+
/** Set the live budget (integer `>= 0`). The next built payload reflects it. */
|
|
55
|
+
setCapacity(budget) {
|
|
56
|
+
this.capacity = requireBudget(budget);
|
|
57
|
+
}
|
|
58
|
+
/** Signal "available but at capacity" by setting `capacityBudget = 0` (§Provider self-throttling). */
|
|
59
|
+
signalFull() {
|
|
60
|
+
this.capacity = 0;
|
|
61
|
+
}
|
|
62
|
+
/** Record withdrawal intent; the manager stops renewing so the record TTL-expires (optimization). */
|
|
63
|
+
markWithdrawn() {
|
|
64
|
+
this.withdrawnFlag = true;
|
|
65
|
+
}
|
|
66
|
+
/** Build the signed {@link ProviderAppPayloadV1} reflecting the current capacity. */
|
|
67
|
+
async buildAppPayload() {
|
|
68
|
+
const signature = await this.sign(providerSigningPayload(this.topicId, this.capabilities, this.capacity));
|
|
69
|
+
const payload = {
|
|
70
|
+
kind: "match-provider",
|
|
71
|
+
capabilities: [...this.capabilities],
|
|
72
|
+
capacityBudget: this.capacity,
|
|
73
|
+
contactHint: this.contactHint,
|
|
74
|
+
signature,
|
|
75
|
+
};
|
|
76
|
+
if (this.serviceUntil !== undefined) {
|
|
77
|
+
payload.serviceUntil = this.serviceUntil;
|
|
78
|
+
}
|
|
79
|
+
return payload;
|
|
80
|
+
}
|
|
81
|
+
/** Build the opaque bytes for the cohort-topic `RegisterV1.appPayload` slot. */
|
|
82
|
+
async appPayloadBytes() {
|
|
83
|
+
return encodeProviderAppPayload(await this.buildAppPayload());
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** Validate a capacity budget is an integer `>= 0`. */
|
|
87
|
+
function requireBudget(budget) {
|
|
88
|
+
if (!Number.isInteger(budget) || budget < 0) {
|
|
89
|
+
throw new RangeError(`matchmaking provider: capacityBudget must be an integer >= 0, got ${budget}`);
|
|
90
|
+
}
|
|
91
|
+
return budget;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/matchmaking/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAA6B,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAsBxG,8EAA8E;AAC9E,MAAM,OAAO,mBAAmB;IACtB,OAAO,CAAa;IACpB,aAAa,CAAa;IAClB,YAAY,CAAoB;IAChC,WAAW,CAAS;IACpB,YAAY,CAAU;IACtB,IAAI,CAA2C;IACxD,QAAQ,CAAS;IACjB,aAAa,GAAG,KAAK,CAAC;IAE9B,YAAY,OAAmC;QAC9C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,YAAY,GAAG,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;QAC9C,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACtD,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,IAAI,WAAW,CAAC;QAChD,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,mEAAmE;IACnE,IAAI,cAAc;QACjB,OAAO,IAAI,CAAC,QAAQ,CAAC;IACtB,CAAC;IAED,0FAA0F;IAC1F,IAAI,SAAS;QACZ,OAAO,IAAI,CAAC,aAAa,CAAC;IAC3B,CAAC;IAED,gFAAgF;IAChF,WAAW,CAAC,MAAc;QACzB,IAAI,CAAC,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,sGAAsG;IACtG,UAAU;QACT,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;IACnB,CAAC;IAED,qGAAqG;IACrG,aAAa;QACZ,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;IAC3B,CAAC;IAED,qFAAqF;IACrF,KAAK,CAAC,eAAe;QACpB,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC1G,MAAM,OAAO,GAAyB;YACrC,IAAI,EAAE,gBAAgB;YACtB,YAAY,EAAE,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC;YACpC,cAAc,EAAE,IAAI,CAAC,QAAQ;YAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,SAAS;SACT,CAAC;QACF,IAAI,IAAI,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YACrC,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;QAC1C,CAAC;QACD,OAAO,OAAO,CAAC;IAChB,CAAC;IAED,gFAAgF;IAChF,KAAK,CAAC,eAAe;QACpB,OAAO,wBAAwB,CAAC,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC;IAC/D,CAAC;CACD;AAED,uDAAuD;AACvD,SAAS,aAAa,CAAC,MAAc;IACpC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,UAAU,CAAC,qEAAqE,MAAM,EAAE,CAAC,CAAC;IACrG,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matchmaking — pure `QueryV1` evaluation (db-core, transport-agnostic).
|
|
3
|
+
*
|
|
4
|
+
* The cohort-side query handler (`db-p2p/src/matchmaking/query-handler.ts`) decodes its local
|
|
5
|
+
* registration records into the {@link LocalProviderRegistration} / {@link LocalSeekerRegistration}
|
|
6
|
+
* shapes below and hands them here; this module performs the *advisory* selection that
|
|
7
|
+
* `docs/matchmaking.md` §Seeker query / §Capability filter specify:
|
|
8
|
+
*
|
|
9
|
+
* - Providers are filtered through {@link matchesFilter} (when `includeProviders`).
|
|
10
|
+
* - Each included set is truncated to `query.limit` (`<= query_limit_max`, enforced on decode in
|
|
11
|
+
* `wire.ts`); {@link QueryEvalResult.truncated} is set when *any* included set had more matches than
|
|
12
|
+
* `limit` allowed, so the seeker knows to re-query a sibling cohort.
|
|
13
|
+
* - The forwarded entries carry the provider/seeker's own `registrationSig` verbatim, so the seeker
|
|
14
|
+
* re-validates each one (`verifyProviderEntry`) — the cohort vouches only for the *set it held*.
|
|
15
|
+
*
|
|
16
|
+
* Pure: no I/O, no clock, no crypto. The db-p2p handler attaches `topicTraffic` / `cohortEpoch` and
|
|
17
|
+
* the primary's reply signature.
|
|
18
|
+
*/
|
|
19
|
+
import type { ProviderAppPayloadV1, SeekerAppPayloadV1, ProviderEntryV1, SeekerEntryV1, QueryV1 } from "./wire.js";
|
|
20
|
+
/** A decoded local provider registration held by the cohort, ready for {@link evaluateQuery}. */
|
|
21
|
+
export interface LocalProviderRegistration {
|
|
22
|
+
/** The provider's peer-id string — the entry's `participantId` AND the `registrationSig` signer. */
|
|
23
|
+
readonly participantId: string;
|
|
24
|
+
/** Unix ms the registration first attached (forwarded verbatim for seeker FCFS ordering). */
|
|
25
|
+
readonly attachedAt: number;
|
|
26
|
+
/** The decoded, validated provider app payload (capabilities, budget, contact, signature). */
|
|
27
|
+
readonly payload: ProviderAppPayloadV1;
|
|
28
|
+
}
|
|
29
|
+
/** A decoded local seeker registration held by the cohort (collective-assembly discovery). */
|
|
30
|
+
export interface LocalSeekerRegistration {
|
|
31
|
+
readonly participantId: string;
|
|
32
|
+
readonly attachedAt: number;
|
|
33
|
+
readonly payload: SeekerAppPayloadV1;
|
|
34
|
+
}
|
|
35
|
+
/** The selected entries for a {@link QueryReplyV1} body (the db-p2p handler signs + frames it). */
|
|
36
|
+
export interface QueryEvalResult {
|
|
37
|
+
readonly providers?: ProviderEntryV1[];
|
|
38
|
+
readonly seekers?: SeekerEntryV1[];
|
|
39
|
+
/** `true` when an included set had more matches than `query.limit` allowed (re-query hint). */
|
|
40
|
+
readonly truncated: boolean;
|
|
41
|
+
}
|
|
42
|
+
/** Build a forwarded {@link ProviderEntryV1} from a local registration (signature forwarded verbatim). */
|
|
43
|
+
export declare function providerEntryOf(reg: LocalProviderRegistration): ProviderEntryV1;
|
|
44
|
+
/** Build a forwarded {@link SeekerEntryV1} from a local registration (signature forwarded verbatim). */
|
|
45
|
+
export declare function seekerEntryOf(reg: LocalSeekerRegistration): SeekerEntryV1;
|
|
46
|
+
/**
|
|
47
|
+
* Evaluate `query` against the cohort's locally-held registrations: filter providers, optionally include
|
|
48
|
+
* seekers, truncate each included set to `query.limit`. Inputs are returned in `attachedAt` order
|
|
49
|
+
* (oldest first) so truncation keeps the longest-waiting registrations — the FCFS bias the arrival-push
|
|
50
|
+
* fairness rule also uses (`docs/matchmaking.md` §Fairness).
|
|
51
|
+
*/
|
|
52
|
+
export declare function evaluateQuery(query: QueryV1, providers: readonly LocalProviderRegistration[], seekers: readonly LocalSeekerRegistration[]): QueryEvalResult;
|
|
53
|
+
//# sourceMappingURL=query-eval.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-eval.d.ts","sourceRoot":"","sources":["../../../src/matchmaking/query-eval.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,eAAe,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnH,iGAAiG;AACjG,MAAM,WAAW,yBAAyB;IACzC,oGAAoG;IACpG,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,6FAA6F;IAC7F,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,8FAA8F;IAC9F,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;CACvC;AAED,8FAA8F;AAC9F,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;CACrC;AAED,mGAAmG;AACnG,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,eAAe,EAAE,CAAC;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IACnC,+FAA+F;IAC/F,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC5B;AAED,0GAA0G;AAC1G,wBAAgB,eAAe,CAAC,GAAG,EAAE,yBAAyB,GAAG,eAAe,CAS/E;AAED,wGAAwG;AACxG,wBAAgB,aAAa,CAAC,GAAG,EAAE,uBAAuB,GAAG,aAAa,CAQzE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC5B,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,SAAS,yBAAyB,EAAE,EAC/C,OAAO,EAAE,SAAS,uBAAuB,EAAE,GACzC,eAAe,CAwBjB"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matchmaking — pure `QueryV1` evaluation (db-core, transport-agnostic).
|
|
3
|
+
*
|
|
4
|
+
* The cohort-side query handler (`db-p2p/src/matchmaking/query-handler.ts`) decodes its local
|
|
5
|
+
* registration records into the {@link LocalProviderRegistration} / {@link LocalSeekerRegistration}
|
|
6
|
+
* shapes below and hands them here; this module performs the *advisory* selection that
|
|
7
|
+
* `docs/matchmaking.md` §Seeker query / §Capability filter specify:
|
|
8
|
+
*
|
|
9
|
+
* - Providers are filtered through {@link matchesFilter} (when `includeProviders`).
|
|
10
|
+
* - Each included set is truncated to `query.limit` (`<= query_limit_max`, enforced on decode in
|
|
11
|
+
* `wire.ts`); {@link QueryEvalResult.truncated} is set when *any* included set had more matches than
|
|
12
|
+
* `limit` allowed, so the seeker knows to re-query a sibling cohort.
|
|
13
|
+
* - The forwarded entries carry the provider/seeker's own `registrationSig` verbatim, so the seeker
|
|
14
|
+
* re-validates each one (`verifyProviderEntry`) — the cohort vouches only for the *set it held*.
|
|
15
|
+
*
|
|
16
|
+
* Pure: no I/O, no clock, no crypto. The db-p2p handler attaches `topicTraffic` / `cohortEpoch` and
|
|
17
|
+
* the primary's reply signature.
|
|
18
|
+
*/
|
|
19
|
+
import { matchesFilter } from "./capability-filter.js";
|
|
20
|
+
/** Build a forwarded {@link ProviderEntryV1} from a local registration (signature forwarded verbatim). */
|
|
21
|
+
export function providerEntryOf(reg) {
|
|
22
|
+
return {
|
|
23
|
+
participantId: reg.participantId,
|
|
24
|
+
capabilities: [...reg.payload.capabilities],
|
|
25
|
+
capacityBudget: reg.payload.capacityBudget,
|
|
26
|
+
contactHint: reg.payload.contactHint,
|
|
27
|
+
attachedAt: reg.attachedAt,
|
|
28
|
+
registrationSig: reg.payload.signature,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** Build a forwarded {@link SeekerEntryV1} from a local registration (signature forwarded verbatim). */
|
|
32
|
+
export function seekerEntryOf(reg) {
|
|
33
|
+
return {
|
|
34
|
+
participantId: reg.participantId,
|
|
35
|
+
wantCount: reg.payload.wantCount,
|
|
36
|
+
contactHint: reg.payload.contactHint,
|
|
37
|
+
attachedAt: reg.attachedAt,
|
|
38
|
+
registrationSig: reg.payload.signature,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Evaluate `query` against the cohort's locally-held registrations: filter providers, optionally include
|
|
43
|
+
* seekers, truncate each included set to `query.limit`. Inputs are returned in `attachedAt` order
|
|
44
|
+
* (oldest first) so truncation keeps the longest-waiting registrations — the FCFS bias the arrival-push
|
|
45
|
+
* fairness rule also uses (`docs/matchmaking.md` §Fairness).
|
|
46
|
+
*/
|
|
47
|
+
export function evaluateQuery(query, providers, seekers) {
|
|
48
|
+
let truncated = false;
|
|
49
|
+
const result = { truncated: false };
|
|
50
|
+
if (query.includeProviders) {
|
|
51
|
+
const matched = providers
|
|
52
|
+
.filter((reg) => matchesFilter(reg.payload, query.filter))
|
|
53
|
+
.sort((a, b) => a.attachedAt - b.attachedAt);
|
|
54
|
+
if (matched.length > query.limit) {
|
|
55
|
+
truncated = true;
|
|
56
|
+
}
|
|
57
|
+
result.providers = matched.slice(0, query.limit).map(providerEntryOf);
|
|
58
|
+
}
|
|
59
|
+
if (query.includeSeekers) {
|
|
60
|
+
const ordered = [...seekers].sort((a, b) => a.attachedAt - b.attachedAt);
|
|
61
|
+
if (ordered.length > query.limit) {
|
|
62
|
+
truncated = true;
|
|
63
|
+
}
|
|
64
|
+
result.seekers = ordered.slice(0, query.limit).map(seekerEntryOf);
|
|
65
|
+
}
|
|
66
|
+
result.truncated = truncated;
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=query-eval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-eval.js","sourceRoot":"","sources":["../../../src/matchmaking/query-eval.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AA4BvD,0GAA0G;AAC1G,MAAM,UAAU,eAAe,CAAC,GAA8B;IAC7D,OAAO;QACN,aAAa,EAAE,GAAG,CAAC,aAAa;QAChC,YAAY,EAAE,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC;QAC3C,cAAc,EAAE,GAAG,CAAC,OAAO,CAAC,cAAc;QAC1C,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC,WAAW;QACpC,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,eAAe,EAAE,GAAG,CAAC,OAAO,CAAC,SAAS;KACtC,CAAC;AACH,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,aAAa,CAAC,GAA4B;IACzD,OAAO;QACN,aAAa,EAAE,GAAG,CAAC,aAAa;QAChC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,SAAS;QAChC,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC,WAAW;QACpC,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,eAAe,EAAE,GAAG,CAAC,OAAO,CAAC,SAAS;KACtC,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC5B,KAAc,EACd,SAA+C,EAC/C,OAA2C;IAE3C,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,MAAM,MAAM,GAAqF,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAEtH,IAAI,KAAK,CAAC,gBAAgB,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,SAAS;aACvB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;aACzD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;QAC9C,IAAI,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;YAClC,SAAS,GAAG,IAAI,CAAC;QAClB,CAAC;QACD,MAAM,CAAC,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACvE,CAAC;IAED,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;QACzE,IAAI,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;YAClC,SAAS,GAAG,IAAI,CAAC;QAClB,CAAC;QACD,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,OAAO,MAAM,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matchmaking — seeker hang-out-vs-continue decision engine (db-core, pure).
|
|
3
|
+
*
|
|
4
|
+
* Per `docs/matchmaking.md` §Hang-out vs. continue. After a seeker registers at a cohort and receives
|
|
5
|
+
* `Accepted` with `topicTraffic` at tree tier `d`, it must decide: is *this* tier the right place to
|
|
6
|
+
* wait, or should it walk one tier toward the root? This module is the pure arithmetic of that choice
|
|
7
|
+
* — no I/O, no clock — so it is unit-testable directly against the doc's worked examples. The db-p2p
|
|
8
|
+
* `seeker-walk-client` drives the walk (register / query / renew / withdraw) and calls {@link decide}.
|
|
9
|
+
*
|
|
10
|
+
* Decision rule (`docs/matchmaking.md` §Decision rule):
|
|
11
|
+
*
|
|
12
|
+
* 1. **Immediate-match.** If `currentMatches >= wantCount` → `done`. (The caller has already issued the
|
|
13
|
+
* `QueryV1`; `currentMatches` is its filter-matched, re-validated yield. This also covers edge case 2
|
|
14
|
+
* — a stale `arrivalsPerMin = 0` after an epoch rotation still gets a real query first, so a quiet
|
|
15
|
+
* cohort that actually holds enough providers resolves to `done` rather than a spurious escalate.)
|
|
16
|
+
* 2. **Hang-out feasibility.**
|
|
17
|
+
* ```
|
|
18
|
+
* expectedNewMatches ≈ arrivalsPerMin × filterAcceptRatio × (patienceMsRemaining / 60000)
|
|
19
|
+
* contentionFactor ≈ min(1 + (queriesPerMin × meanWantCount) / max(arrivalsPerMin, 1), cap)
|
|
20
|
+
* hang out iff currentMatches + expectedNewMatches ≥ wantCount × contentionFactor
|
|
21
|
+
* ```
|
|
22
|
+
* 3. **Otherwise escalate** (walk one tier toward the root).
|
|
23
|
+
*
|
|
24
|
+
* Edge cases the engine encodes (the rest live in the walk client, which owns the walk topology):
|
|
25
|
+
* - **Filter matches almost nothing (case 4):** a `filterAcceptRatio` decayed toward 0 collapses
|
|
26
|
+
* `expectedNewMatches`, so the feasibility test fails at every tier and the seeker walks to the root.
|
|
27
|
+
* - **Many seekers competing (case 5):** a high `queriesPerMin` inflates `contentionFactor` up to
|
|
28
|
+
* `cap`, raising the threshold so more seekers escalate toward aggregation — self-balancing, bounded.
|
|
29
|
+
*/
|
|
30
|
+
import type { HangOutConfig } from "./config.js";
|
|
31
|
+
/** Inputs to {@link decide}: the cohort's `topicTraffic` plus the seeker's running state. */
|
|
32
|
+
export interface SeekerDecisionInputs {
|
|
33
|
+
/** Filter-matched, re-validated providers from the immediate `QueryV1` at this tier. */
|
|
34
|
+
readonly currentMatches: number;
|
|
35
|
+
/** `topicTraffic.directParticipants` — informational (providers known here right now). */
|
|
36
|
+
readonly directParticipants: number;
|
|
37
|
+
/** `topicTraffic.arrivalsPerMin` — provider registration + renewal rate. */
|
|
38
|
+
readonly arrivalsPerMin: number;
|
|
39
|
+
/** `topicTraffic.queriesPerMin` — competing seeker activity over the same pool. */
|
|
40
|
+
readonly queriesPerMin: number;
|
|
41
|
+
/** `topicTraffic.childCohortCount` — `> 0` means this tier has promoted (descend territory). */
|
|
42
|
+
readonly childCohortCount: number;
|
|
43
|
+
/** Providers the seeker needs. */
|
|
44
|
+
readonly wantCount: number;
|
|
45
|
+
/** Patience budget left at this tier (drains across walked tiers — the client tracks it). */
|
|
46
|
+
readonly patienceMsRemaining: number;
|
|
47
|
+
/** Running estimate of the fraction of returned providers that pass the seeker's filter (decays from 1.0). */
|
|
48
|
+
readonly filterAcceptRatio: number;
|
|
49
|
+
/** Assumed mean `wantCount` of competing seekers (small constant or learned). */
|
|
50
|
+
readonly meanWantCount: number;
|
|
51
|
+
}
|
|
52
|
+
/** The outcome of {@link decide}. `hangOut` carries the poll cadence for the requery loop. */
|
|
53
|
+
export type SeekerDecision = {
|
|
54
|
+
readonly action: "done";
|
|
55
|
+
} | {
|
|
56
|
+
readonly action: "hangOut";
|
|
57
|
+
readonly requeryIntervalMs: number;
|
|
58
|
+
} | {
|
|
59
|
+
readonly action: "escalate";
|
|
60
|
+
};
|
|
61
|
+
/** `expectedNewMatches ≈ arrivalsPerMin × filterAcceptRatio × (patienceMsRemaining / 60000)`. */
|
|
62
|
+
export declare function expectedNewMatches(inputs: SeekerDecisionInputs): number;
|
|
63
|
+
/** `contentionFactor ≈ min(1 + (queriesPerMin × meanWantCount) / max(arrivalsPerMin, 1), cap)`. */
|
|
64
|
+
export declare function contentionFactor(inputs: SeekerDecisionInputs, cfg: HangOutConfig): number;
|
|
65
|
+
/** The hang-out feasibility threshold `wantCount × contentionFactor`. */
|
|
66
|
+
export declare function hangOutThreshold(inputs: SeekerDecisionInputs, cfg: HangOutConfig): number;
|
|
67
|
+
/**
|
|
68
|
+
* Decide whether the seeker is `done` (immediate match), should `hangOut` (feasible to wait here), or
|
|
69
|
+
* should `escalate` (walk one tier toward the root). Pure — see the module header for the rule.
|
|
70
|
+
*/
|
|
71
|
+
export declare function decide(inputs: SeekerDecisionInputs, cfg: HangOutConfig): SeekerDecision;
|
|
72
|
+
/**
|
|
73
|
+
* Running yield accumulator for `filterAcceptRatio`. The ratio starts at `filter_accept_ratio_initial`
|
|
74
|
+
* (1.0, used before any observation) and is refined to the cumulative `matched / returned` over the
|
|
75
|
+
* walk: after two cohorts that each return ~10% matchable providers it settles near 0.1, collapsing
|
|
76
|
+
* `expectedNewMatches` for a pathological filter (edge case 4).
|
|
77
|
+
*/
|
|
78
|
+
export interface FilterAcceptRatioState {
|
|
79
|
+
/** Total filter-matched providers observed across queries so far. */
|
|
80
|
+
readonly matched: number;
|
|
81
|
+
/** Total providers returned across queries so far. */
|
|
82
|
+
readonly returned: number;
|
|
83
|
+
}
|
|
84
|
+
/** A fresh (zero-observation) {@link FilterAcceptRatioState}. */
|
|
85
|
+
export declare function newFilterAcceptRatioState(): FilterAcceptRatioState;
|
|
86
|
+
/** Fold one query's yield (`matched` of `returned`) into the running state. */
|
|
87
|
+
export declare function observeYield(state: FilterAcceptRatioState, matched: number, returned: number): FilterAcceptRatioState;
|
|
88
|
+
/**
|
|
89
|
+
* The current `filterAcceptRatio`: cumulative `matched / returned`, or `initial` (1.0) before any
|
|
90
|
+
* providers have been returned (so the first hang-out estimate is optimistic, then refines).
|
|
91
|
+
*/
|
|
92
|
+
export declare function filterAcceptRatio(state: FilterAcceptRatioState, initial?: number): number;
|
|
93
|
+
/** The default assumed competing-seeker `meanWantCount` (re-exported for the walk client). */
|
|
94
|
+
export declare const DEFAULT_MEAN_WANT_COUNT = 3;
|
|
95
|
+
//# sourceMappingURL=seeker-walk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seeker-walk.d.ts","sourceRoot":"","sources":["../../../src/matchmaking/seeker-walk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,6FAA6F;AAC7F,MAAM,WAAW,oBAAoB;IACpC,wFAAwF;IACxF,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,0FAA0F;IAC1F,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,4EAA4E;IAC5E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,mFAAmF;IACnF,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,gGAAgG;IAChG,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,kCAAkC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6FAA6F;IAC7F,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,8GAA8G;IAC9G,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,iFAAiF;IACjF,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAC/B;AAED,8FAA8F;AAC9F,MAAM,MAAM,cAAc,GACvB;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3B;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC;AAEnC,iGAAiG;AACjG,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAEvE;AAED,mGAAmG;AACnG,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,oBAAoB,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,CAGzF;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,oBAAoB,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,CAEzF;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,oBAAoB,EAAE,GAAG,EAAE,aAAa,GAAG,cAAc,CASvF;AAID;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACtC,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC1B;AAED,iEAAiE;AACjE,wBAAgB,yBAAyB,IAAI,sBAAsB,CAElE;AAED,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,KAAK,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,sBAAsB,CAErH;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,EAAE,OAAO,GAAE,MAAoC,GAAG,MAAM,CAEtH;AAED,8FAA8F;AAC9F,eAAO,MAAM,uBAAuB,IAA0B,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matchmaking — seeker hang-out-vs-continue decision engine (db-core, pure).
|
|
3
|
+
*
|
|
4
|
+
* Per `docs/matchmaking.md` §Hang-out vs. continue. After a seeker registers at a cohort and receives
|
|
5
|
+
* `Accepted` with `topicTraffic` at tree tier `d`, it must decide: is *this* tier the right place to
|
|
6
|
+
* wait, or should it walk one tier toward the root? This module is the pure arithmetic of that choice
|
|
7
|
+
* — no I/O, no clock — so it is unit-testable directly against the doc's worked examples. The db-p2p
|
|
8
|
+
* `seeker-walk-client` drives the walk (register / query / renew / withdraw) and calls {@link decide}.
|
|
9
|
+
*
|
|
10
|
+
* Decision rule (`docs/matchmaking.md` §Decision rule):
|
|
11
|
+
*
|
|
12
|
+
* 1. **Immediate-match.** If `currentMatches >= wantCount` → `done`. (The caller has already issued the
|
|
13
|
+
* `QueryV1`; `currentMatches` is its filter-matched, re-validated yield. This also covers edge case 2
|
|
14
|
+
* — a stale `arrivalsPerMin = 0` after an epoch rotation still gets a real query first, so a quiet
|
|
15
|
+
* cohort that actually holds enough providers resolves to `done` rather than a spurious escalate.)
|
|
16
|
+
* 2. **Hang-out feasibility.**
|
|
17
|
+
* ```
|
|
18
|
+
* expectedNewMatches ≈ arrivalsPerMin × filterAcceptRatio × (patienceMsRemaining / 60000)
|
|
19
|
+
* contentionFactor ≈ min(1 + (queriesPerMin × meanWantCount) / max(arrivalsPerMin, 1), cap)
|
|
20
|
+
* hang out iff currentMatches + expectedNewMatches ≥ wantCount × contentionFactor
|
|
21
|
+
* ```
|
|
22
|
+
* 3. **Otherwise escalate** (walk one tier toward the root).
|
|
23
|
+
*
|
|
24
|
+
* Edge cases the engine encodes (the rest live in the walk client, which owns the walk topology):
|
|
25
|
+
* - **Filter matches almost nothing (case 4):** a `filterAcceptRatio` decayed toward 0 collapses
|
|
26
|
+
* `expectedNewMatches`, so the feasibility test fails at every tier and the seeker walks to the root.
|
|
27
|
+
* - **Many seekers competing (case 5):** a high `queriesPerMin` inflates `contentionFactor` up to
|
|
28
|
+
* `cap`, raising the threshold so more seekers escalate toward aggregation — self-balancing, bounded.
|
|
29
|
+
*/
|
|
30
|
+
import { FILTER_ACCEPT_RATIO_INITIAL, MEAN_WANT_COUNT_DEFAULT } from "./config.js";
|
|
31
|
+
/** `expectedNewMatches ≈ arrivalsPerMin × filterAcceptRatio × (patienceMsRemaining / 60000)`. */
|
|
32
|
+
export function expectedNewMatches(inputs) {
|
|
33
|
+
return inputs.arrivalsPerMin * inputs.filterAcceptRatio * (inputs.patienceMsRemaining / 60_000);
|
|
34
|
+
}
|
|
35
|
+
/** `contentionFactor ≈ min(1 + (queriesPerMin × meanWantCount) / max(arrivalsPerMin, 1), cap)`. */
|
|
36
|
+
export function contentionFactor(inputs, cfg) {
|
|
37
|
+
const raw = 1 + (inputs.queriesPerMin * inputs.meanWantCount) / Math.max(inputs.arrivalsPerMin, 1);
|
|
38
|
+
return Math.min(raw, cfg.contentionFactorCap);
|
|
39
|
+
}
|
|
40
|
+
/** The hang-out feasibility threshold `wantCount × contentionFactor`. */
|
|
41
|
+
export function hangOutThreshold(inputs, cfg) {
|
|
42
|
+
return inputs.wantCount * contentionFactor(inputs, cfg);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Decide whether the seeker is `done` (immediate match), should `hangOut` (feasible to wait here), or
|
|
46
|
+
* should `escalate` (walk one tier toward the root). Pure — see the module header for the rule.
|
|
47
|
+
*/
|
|
48
|
+
export function decide(inputs, cfg) {
|
|
49
|
+
if (inputs.currentMatches >= inputs.wantCount) {
|
|
50
|
+
return { action: "done" };
|
|
51
|
+
}
|
|
52
|
+
const projected = inputs.currentMatches + expectedNewMatches(inputs);
|
|
53
|
+
if (projected >= hangOutThreshold(inputs, cfg)) {
|
|
54
|
+
return { action: "hangOut", requeryIntervalMs: cfg.requeryIntervalMs };
|
|
55
|
+
}
|
|
56
|
+
return { action: "escalate" };
|
|
57
|
+
}
|
|
58
|
+
/** A fresh (zero-observation) {@link FilterAcceptRatioState}. */
|
|
59
|
+
export function newFilterAcceptRatioState() {
|
|
60
|
+
return { matched: 0, returned: 0 };
|
|
61
|
+
}
|
|
62
|
+
/** Fold one query's yield (`matched` of `returned`) into the running state. */
|
|
63
|
+
export function observeYield(state, matched, returned) {
|
|
64
|
+
return { matched: state.matched + matched, returned: state.returned + returned };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The current `filterAcceptRatio`: cumulative `matched / returned`, or `initial` (1.0) before any
|
|
68
|
+
* providers have been returned (so the first hang-out estimate is optimistic, then refines).
|
|
69
|
+
*/
|
|
70
|
+
export function filterAcceptRatio(state, initial = FILTER_ACCEPT_RATIO_INITIAL) {
|
|
71
|
+
return state.returned > 0 ? state.matched / state.returned : initial;
|
|
72
|
+
}
|
|
73
|
+
/** The default assumed competing-seeker `meanWantCount` (re-exported for the walk client). */
|
|
74
|
+
export const DEFAULT_MEAN_WANT_COUNT = MEAN_WANT_COUNT_DEFAULT;
|
|
75
|
+
//# sourceMappingURL=seeker-walk.js.map
|