13 KiB
@suaveplan/network-discovery-hybrid - Browser-tier multi-strategy DiscoveryStrategy orchestrator: composes any subset of the five Wave-2 sibling strategies (peerjs / crdt / trystero / webtorrent / gundb) into a single HybridDiscoveryManager with priority-based peer selection, per-strategy health tracking, automatic fallback, de-duplicated peer set with PeerSource provenance, and unified lifecycle. Implements the network-bootstrap DiscoveryStrategy contract so it can itself be chained.
Summary
Browser-tier multi-strategy
DiscoveryStrategyorchestrator. Composes any subset of the five Wave-2 sibling discovery strategies behind a singleHybridDiscoveryManagerwith priority-based peer selection, per-strategy health tracking, automatic fallback, and a de-duplicated peer set withPeerSourceprovenance.
Documentation tree
- hybrid-discovery-manager — (no submodules)
- hybrid-discovery-manager — (no submodules)
- lifecycle — (no submodules)
- lifecycle — (no submodules)
- peer-tracker — (no submodules)
- peer-tracker — (no submodules)
- strategy-loader — (no submodules)
- strategy-loader — (no submodules)
- strategy-manager — (no submodules)
- strategy-manager — (no submodules)
- errors — (no submodules)
- errors — (no submodules)
- otel — (no submodules)
- otel — (no submodules)
- types — (no submodules)
- types — (no submodules)
Overview
HybridDiscoveryManager registers any subset of the following sibling strategies (and arbitrary "custom" strategies) and presents them as a single DiscoveryStrategy from @suaveplan/network-bootstrap:
@suaveplan/network-discovery-peerjs@suaveplan/network-discovery-crdt@suaveplan/network-discovery-trystero@suaveplan/network-discovery-webtorrent@suaveplan/network-discovery-gundb
Each sibling is declared as an optional peer-dependency so consumers only pay for the strategies they actually register. Missing peer-deps surface as STRATEGY_LOAD_FAILED health records, not crashes.
Because the orchestrator itself implements DiscoveryStrategy, it can be chained into a higher-level DiscoveryChain from @suaveplan/network-bootstrap — the hybrid is a recursive composer.
Basic Example
import { HybridDiscoveryManager } from "@suaveplan/network-discovery-hybrid";
const manager = new HybridDiscoveryManager({
strategy: "hybrid",
strategies: [
{ id: "peerjs", priority: "primary", strategy: peerJsStrategy },
{ id: "trystero", priority: "fallback", strategy: trysteroStrategy },
],
});
await manager.start();
const peers = await manager.discover(5_000);
await manager.stop();
Advanced Example
import { createEventBus } from "@suaveplan/events";
import {
HybridDiscoveryManager,
type HybridDiscoveryEventMap,
} from "@suaveplan/network-discovery-hybrid";
const bus = createEventBus<HybridDiscoveryEventMap>();
bus.on("hybrid:fallback", ({ from, to }) =>
console.log(`Fallback ${from} → ${to}`)
);
const manager = new HybridDiscoveryManager(
{
strategy: "hybrid",
loadBalancing: "fastest",
autoFallback: true,
deduplication: true,
discoveryTimeoutMs: 8_000,
strategies: [
{ id: "peerjs", priority: "primary", weight: 70 },
{
id: "trystero",
priority: "fallback",
weight: 30,
factory: async () => {
const mod = await import(
"@suaveplan/network-discovery-trystero"
);
return new mod.TrysteroDiscovery({ /* ... */ });
},
},
],
},
{ bus }
);
const controller = new AbortController();
await manager.start(controller.signal);
try {
const peers = await manager.discover(5_000, controller.signal);
console.log(manager.getStatus());
} finally {
await manager.dispose();
}
Features
- Three-bucket strategy priority —
primary,fallback,backup. - Three load-balancing modes —
priority,weighted,fastest. - Per-strategy
StrategyHealthwith rolling success-rate EMA, consecutive-failure counter, and last-error. - Automatic fallback — degraded primaries trigger dormant fallbacks to come online.
- De-duplicated peer set with append-only
PeerSource.discoveredByprovenance. - Unified lifecycle (
start/stop/dispose) — idempotent throughout. @suaveplan/timingSchedulerdriving periodic health-checks + stale-peer sweep.- Optional
@suaveplan/eventsEventBusLike<HybridDiscoveryEventMap>integration. - Full OTel surface (counters, histograms, gauge, spans).
- AbortSignal threaded through every async path.
- Zod boundary validation on the public config.
- 100% test coverage including fast-check property tests.
Error Codes
| Code | Description |
|---|---|
NET_DISC_HYBRID.NO_STRATEGIES |
Configuration contained an empty strategies array. |
NET_DISC_HYBRID.ALL_STRATEGIES_DEGRADED |
Every registered strategy reported unavailable. |
NET_DISC_HYBRID.STRATEGY_LOAD_FAILED |
Dynamic load of an optional peer-dep failed; recorded into health, not bubbled. |
NET_DISC_HYBRID.DUPLICATE_STRATEGY |
Two StrategyConfig entries shared the same id. |
NET_DISC_HYBRID.INVALID_OPTIONS |
Caller-supplied configuration failed Zod validation. |
NET_DISC_HYBRID.DISPOSED |
Operation attempted after dispose(). |
Testing
The package contains a real in-package TestStrategy fixture under tests/setup/test-strategy.ts that fully implements the DiscoveryStrategy contract deterministically. There are NO mocks anywhere — no vi.mock, no __mocks__/, no module-level stubs. The integration suite in tests/integration.test.ts exercises the orchestrator against multiple real TestStrategy instances tagged with the five Wave-2 sibling ids and additionally verifies the real sibling packages are importable as optional peer-deps.