@meshwhisper/sdk 0.1.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/README.md +138 -0
- package/dist/browser/index.d.ts +4 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +19 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/chaff/index.d.ts +91 -0
- package/dist/chaff/index.d.ts.map +1 -0
- package/dist/chaff/index.js +268 -0
- package/dist/chaff/index.js.map +1 -0
- package/dist/cluster/index.d.ts +159 -0
- package/dist/cluster/index.d.ts.map +1 -0
- package/dist/cluster/index.js +393 -0
- package/dist/cluster/index.js.map +1 -0
- package/dist/compliance/index.d.ts +129 -0
- package/dist/compliance/index.d.ts.map +1 -0
- package/dist/compliance/index.js +315 -0
- package/dist/compliance/index.js.map +1 -0
- package/dist/crypto/index.d.ts +65 -0
- package/dist/crypto/index.d.ts.map +1 -0
- package/dist/crypto/index.js +146 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/group/index.d.ts +155 -0
- package/dist/group/index.d.ts.map +1 -0
- package/dist/group/index.js +560 -0
- package/dist/group/index.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/namespace/index.d.ts +155 -0
- package/dist/namespace/index.d.ts.map +1 -0
- package/dist/namespace/index.js +278 -0
- package/dist/namespace/index.js.map +1 -0
- package/dist/node/index.d.ts +4 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +19 -0
- package/dist/node/index.js.map +1 -0
- package/dist/packet/index.d.ts +63 -0
- package/dist/packet/index.d.ts.map +1 -0
- package/dist/packet/index.js +244 -0
- package/dist/packet/index.js.map +1 -0
- package/dist/permissions/index.d.ts +107 -0
- package/dist/permissions/index.d.ts.map +1 -0
- package/dist/permissions/index.js +282 -0
- package/dist/permissions/index.js.map +1 -0
- package/dist/persistence/idb-storage.d.ts +27 -0
- package/dist/persistence/idb-storage.d.ts.map +1 -0
- package/dist/persistence/idb-storage.js +75 -0
- package/dist/persistence/idb-storage.js.map +1 -0
- package/dist/persistence/index.d.ts +4 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +3 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/node-storage.d.ts +33 -0
- package/dist/persistence/node-storage.d.ts.map +1 -0
- package/dist/persistence/node-storage.js +90 -0
- package/dist/persistence/node-storage.js.map +1 -0
- package/dist/persistence/serialization.d.ts +4 -0
- package/dist/persistence/serialization.d.ts.map +1 -0
- package/dist/persistence/serialization.js +49 -0
- package/dist/persistence/serialization.js.map +1 -0
- package/dist/persistence/types.d.ts +29 -0
- package/dist/persistence/types.d.ts.map +1 -0
- package/dist/persistence/types.js +5 -0
- package/dist/persistence/types.js.map +1 -0
- package/dist/ratchet/index.d.ts +80 -0
- package/dist/ratchet/index.d.ts.map +1 -0
- package/dist/ratchet/index.js +259 -0
- package/dist/ratchet/index.js.map +1 -0
- package/dist/reciprocity/index.d.ts +109 -0
- package/dist/reciprocity/index.d.ts.map +1 -0
- package/dist/reciprocity/index.js +311 -0
- package/dist/reciprocity/index.js.map +1 -0
- package/dist/relay/index.d.ts +87 -0
- package/dist/relay/index.d.ts.map +1 -0
- package/dist/relay/index.js +286 -0
- package/dist/relay/index.js.map +1 -0
- package/dist/routing/index.d.ts +136 -0
- package/dist/routing/index.d.ts.map +1 -0
- package/dist/routing/index.js +478 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/sdk/index.d.ts +322 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +1530 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/sybil/index.d.ts +123 -0
- package/dist/sybil/index.d.ts.map +1 -0
- package/dist/sybil/index.js +491 -0
- package/dist/sybil/index.js.map +1 -0
- package/dist/transport/browser/index.d.ts +34 -0
- package/dist/transport/browser/index.d.ts.map +1 -0
- package/dist/transport/browser/index.js +176 -0
- package/dist/transport/browser/index.js.map +1 -0
- package/dist/transport/local/index.d.ts +57 -0
- package/dist/transport/local/index.d.ts.map +1 -0
- package/dist/transport/local/index.js +442 -0
- package/dist/transport/local/index.js.map +1 -0
- package/dist/transport/negotiator/index.d.ts +79 -0
- package/dist/transport/negotiator/index.d.ts.map +1 -0
- package/dist/transport/negotiator/index.js +289 -0
- package/dist/transport/negotiator/index.js.map +1 -0
- package/dist/transport/node/index.d.ts +56 -0
- package/dist/transport/node/index.d.ts.map +1 -0
- package/dist/transport/node/index.js +209 -0
- package/dist/transport/node/index.js.map +1 -0
- package/dist/transport/noop/index.d.ts +11 -0
- package/dist/transport/noop/index.d.ts.map +1 -0
- package/dist/transport/noop/index.js +20 -0
- package/dist/transport/noop/index.js.map +1 -0
- package/dist/transport/p2p/index.d.ts +109 -0
- package/dist/transport/p2p/index.d.ts.map +1 -0
- package/dist/transport/p2p/index.js +237 -0
- package/dist/transport/p2p/index.js.map +1 -0
- package/dist/transport/websocket/index.d.ts +89 -0
- package/dist/transport/websocket/index.d.ts.map +1 -0
- package/dist/transport/websocket/index.js +498 -0
- package/dist/transport/websocket/index.js.map +1 -0
- package/dist/transport/websocket/serialize.d.ts +5 -0
- package/dist/transport/websocket/serialize.d.ts.map +1 -0
- package/dist/transport/websocket/serialize.js +55 -0
- package/dist/transport/websocket/serialize.js.map +1 -0
- package/dist/types.d.ts +215 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -0
- package/dist/x3dh/index.d.ts +120 -0
- package/dist/x3dh/index.d.ts.map +1 -0
- package/dist/x3dh/index.js +290 -0
- package/dist/x3dh/index.js.map +1 -0
- package/package.json +59 -0
- package/src/browser/index.ts +19 -0
- package/src/chaff/index.ts +340 -0
- package/src/cluster/index.ts +482 -0
- package/src/compliance/index.ts +407 -0
- package/src/crypto/index.ts +193 -0
- package/src/group/index.ts +719 -0
- package/src/index.ts +87 -0
- package/src/lz4js.d.ts +58 -0
- package/src/namespace/index.ts +336 -0
- package/src/node/index.ts +19 -0
- package/src/packet/index.ts +326 -0
- package/src/permissions/index.ts +405 -0
- package/src/persistence/idb-storage.ts +83 -0
- package/src/persistence/index.ts +3 -0
- package/src/persistence/node-storage.ts +96 -0
- package/src/persistence/serialization.ts +75 -0
- package/src/persistence/types.ts +33 -0
- package/src/ratchet/index.ts +363 -0
- package/src/reciprocity/index.ts +371 -0
- package/src/relay/index.ts +382 -0
- package/src/routing/index.ts +577 -0
- package/src/sdk/index.ts +1994 -0
- package/src/sybil/index.ts +661 -0
- package/src/transport/browser/index.ts +201 -0
- package/src/transport/local/index.ts +540 -0
- package/src/transport/negotiator/index.ts +397 -0
- package/src/transport/node/index.ts +234 -0
- package/src/transport/noop/index.ts +22 -0
- package/src/transport/p2p/index.ts +345 -0
- package/src/transport/websocket/index.ts +660 -0
- package/src/transport/websocket/serialize.ts +68 -0
- package/src/types.ts +275 -0
- package/src/x3dh/index.ts +388 -0
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// MeshWhisper SDK — Social Graph Routing
|
|
3
|
+
// Uses social proximity (not geographic or DHT) as routing
|
|
4
|
+
// topology. Implements Bubble-Rap-inspired discovery with
|
|
5
|
+
// small-world properties (~6 hop average path length).
|
|
6
|
+
// See PRD section 7.1.
|
|
7
|
+
// ============================================================
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Constants
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
/** Default TTL for proximity table entries (1 hour). */
|
|
12
|
+
const DEFAULT_PEER_TTL_MS = 60 * 60 * 1000;
|
|
13
|
+
/** Default TTL for cached routes (5 minutes). */
|
|
14
|
+
const DEFAULT_ROUTE_TTL_MS = 5 * 60 * 1000;
|
|
15
|
+
/** Default timeout for pending route requests (5 seconds). */
|
|
16
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 5 * 1000;
|
|
17
|
+
/** Maximum number of recent packet hashes kept for deduplication. */
|
|
18
|
+
const SEEN_PACKET_CAPACITY = 10_000;
|
|
19
|
+
/** Number of socially-closest peers to gossip route requests to. */
|
|
20
|
+
const GOSSIP_FANOUT = 5;
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Helpers
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
/** Convert a Uint8Array to a hex string for use as a Map key. */
|
|
25
|
+
function hashToHex(hash) {
|
|
26
|
+
let out = '';
|
|
27
|
+
for (let i = 0; i < hash.length; i++) {
|
|
28
|
+
out += hash[i].toString(16).padStart(2, '0');
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
/** Constant-time-ish byte array equality. */
|
|
33
|
+
function bytesEqual(a, b) {
|
|
34
|
+
if (a.length !== b.length)
|
|
35
|
+
return false;
|
|
36
|
+
let diff = 0;
|
|
37
|
+
for (let i = 0; i < a.length; i++) {
|
|
38
|
+
diff |= a[i] ^ b[i];
|
|
39
|
+
}
|
|
40
|
+
return diff === 0;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* XOR distance between two destination hashes.
|
|
44
|
+
* Returns a number usable for comparison (lower = closer).
|
|
45
|
+
* Works on up to 8-byte hashes.
|
|
46
|
+
*/
|
|
47
|
+
function xorDistance(a, b) {
|
|
48
|
+
const len = Math.min(a.length, b.length);
|
|
49
|
+
let distance = 0;
|
|
50
|
+
for (let i = 0; i < len; i++) {
|
|
51
|
+
distance = distance * 256 + (a[i] ^ b[i]);
|
|
52
|
+
}
|
|
53
|
+
return distance;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Compute a simple hash of a packet for deduplication.
|
|
57
|
+
* Uses destHash + senderEphemeralId + first 8 bytes of payload.
|
|
58
|
+
*/
|
|
59
|
+
function packetFingerprint(packet) {
|
|
60
|
+
const parts = [];
|
|
61
|
+
for (let i = 0; i < packet.destHash.length; i++) {
|
|
62
|
+
parts.push(packet.destHash[i]);
|
|
63
|
+
}
|
|
64
|
+
for (let i = 0; i < packet.senderEphemeralId.length; i++) {
|
|
65
|
+
parts.push(packet.senderEphemeralId[i]);
|
|
66
|
+
}
|
|
67
|
+
const payloadSample = Math.min(8, packet.encryptedPayload.length);
|
|
68
|
+
for (let i = 0; i < payloadSample; i++) {
|
|
69
|
+
parts.push(packet.encryptedPayload[i]);
|
|
70
|
+
}
|
|
71
|
+
return parts.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
72
|
+
}
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// PeerProximityTable
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
/**
|
|
77
|
+
* Maintains a table of known peers indexed by social proximity.
|
|
78
|
+
* Entries are keyed by peerId and can also be looked up by destHash.
|
|
79
|
+
*/
|
|
80
|
+
export class PeerProximityTable {
|
|
81
|
+
entries = new Map();
|
|
82
|
+
destHashIndex = new Map(); // hex(destHash) -> peerId
|
|
83
|
+
ttlMs;
|
|
84
|
+
constructor(ttlMs = DEFAULT_PEER_TTL_MS) {
|
|
85
|
+
this.ttlMs = ttlMs;
|
|
86
|
+
}
|
|
87
|
+
/** Add or replace a peer entry. */
|
|
88
|
+
addPeer(entry) {
|
|
89
|
+
this.entries.set(entry.peerId, entry);
|
|
90
|
+
this.destHashIndex.set(hashToHex(entry.destHash), entry.peerId);
|
|
91
|
+
}
|
|
92
|
+
/** Remove a peer by ID. */
|
|
93
|
+
removePeer(peerId) {
|
|
94
|
+
const existing = this.entries.get(peerId);
|
|
95
|
+
if (existing) {
|
|
96
|
+
this.destHashIndex.delete(hashToHex(existing.destHash));
|
|
97
|
+
this.entries.delete(peerId);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** Partially update an existing peer entry. */
|
|
101
|
+
updatePeer(peerId, updates) {
|
|
102
|
+
const existing = this.entries.get(peerId);
|
|
103
|
+
if (!existing)
|
|
104
|
+
return;
|
|
105
|
+
// If destHash changes, update the index.
|
|
106
|
+
if (updates.destHash && !bytesEqual(updates.destHash, existing.destHash)) {
|
|
107
|
+
this.destHashIndex.delete(hashToHex(existing.destHash));
|
|
108
|
+
this.destHashIndex.set(hashToHex(updates.destHash), peerId);
|
|
109
|
+
}
|
|
110
|
+
const updated = { ...existing, ...updates };
|
|
111
|
+
this.entries.set(peerId, updated);
|
|
112
|
+
}
|
|
113
|
+
/** Find a peer that owns the given destination hash. */
|
|
114
|
+
findPeer(destHash) {
|
|
115
|
+
const hex = hashToHex(destHash);
|
|
116
|
+
const peerId = this.destHashIndex.get(hex);
|
|
117
|
+
if (peerId === undefined)
|
|
118
|
+
return null;
|
|
119
|
+
const entry = this.entries.get(peerId);
|
|
120
|
+
if (!entry) {
|
|
121
|
+
// Stale index entry — clean up.
|
|
122
|
+
this.destHashIndex.delete(hex);
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
return entry;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Return the N peers whose known destHash is closest (by XOR distance) to
|
|
129
|
+
* the target destination hash. Results are sorted closest-first.
|
|
130
|
+
*/
|
|
131
|
+
getClosestPeers(destHash, count) {
|
|
132
|
+
const scored = [];
|
|
133
|
+
for (const entry of this.entries.values()) {
|
|
134
|
+
scored.push({
|
|
135
|
+
entry,
|
|
136
|
+
distance: xorDistance(entry.destHash, destHash),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
scored.sort((a, b) => a.distance - b.distance);
|
|
140
|
+
return scored.slice(0, count).map(s => s.entry);
|
|
141
|
+
}
|
|
142
|
+
/** Remove entries whose lastSeen is older than maxAgeMs. */
|
|
143
|
+
pruneStale(maxAgeMs = this.ttlMs) {
|
|
144
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
145
|
+
for (const [peerId, entry] of this.entries) {
|
|
146
|
+
if (entry.lastSeen < cutoff) {
|
|
147
|
+
this.removePeer(peerId);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/** Number of entries currently in the table. */
|
|
152
|
+
get size() {
|
|
153
|
+
return this.entries.size;
|
|
154
|
+
}
|
|
155
|
+
/** Iterate all entries (snapshot). */
|
|
156
|
+
allEntries() {
|
|
157
|
+
return Array.from(this.entries.values());
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
// Seen-packet LRU set (bounded ring buffer)
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
class SeenPacketSet {
|
|
164
|
+
capacity;
|
|
165
|
+
set = new Set();
|
|
166
|
+
ring;
|
|
167
|
+
cursor = 0;
|
|
168
|
+
constructor(capacity = SEEN_PACKET_CAPACITY) {
|
|
169
|
+
this.capacity = capacity;
|
|
170
|
+
this.ring = new Array(capacity).fill('');
|
|
171
|
+
}
|
|
172
|
+
/** Returns true if the fingerprint was already in the set. */
|
|
173
|
+
testAndAdd(fingerprint) {
|
|
174
|
+
if (this.set.has(fingerprint))
|
|
175
|
+
return true;
|
|
176
|
+
// Evict oldest if at capacity.
|
|
177
|
+
if (this.set.size >= this.capacity) {
|
|
178
|
+
const evict = this.ring[this.cursor];
|
|
179
|
+
if (evict)
|
|
180
|
+
this.set.delete(evict);
|
|
181
|
+
}
|
|
182
|
+
this.ring[this.cursor] = fingerprint;
|
|
183
|
+
this.set.add(fingerprint);
|
|
184
|
+
this.cursor = (this.cursor + 1) % this.capacity;
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
has(fingerprint) {
|
|
188
|
+
return this.set.has(fingerprint);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
// Route cache
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
class RouteCache {
|
|
195
|
+
routes = new Map();
|
|
196
|
+
getCachedRoute(destHash) {
|
|
197
|
+
const key = hashToHex(destHash);
|
|
198
|
+
const route = this.routes.get(key);
|
|
199
|
+
if (!route)
|
|
200
|
+
return null;
|
|
201
|
+
if (Date.now() > route.expiresAt) {
|
|
202
|
+
this.routes.delete(key);
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
return route;
|
|
206
|
+
}
|
|
207
|
+
cacheRoute(route) {
|
|
208
|
+
this.routes.set(hashToHex(route.destHash), route);
|
|
209
|
+
}
|
|
210
|
+
invalidateRoute(destHash) {
|
|
211
|
+
this.routes.delete(hashToHex(destHash));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
// SocialGraphRouter
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
/**
|
|
218
|
+
* Core social graph router.
|
|
219
|
+
*
|
|
220
|
+
* Uses the peer proximity table as the routing topology.
|
|
221
|
+
* Route discovery gossips route requests to socially proximate peers
|
|
222
|
+
* and selects the shortest/fastest offered route.
|
|
223
|
+
*/
|
|
224
|
+
export class SocialGraphRouter {
|
|
225
|
+
localPeerId;
|
|
226
|
+
proximityTable;
|
|
227
|
+
routeCache = new RouteCache();
|
|
228
|
+
seenPackets = new SeenPacketSet();
|
|
229
|
+
pendingRequests = new Map();
|
|
230
|
+
routeTtlMs;
|
|
231
|
+
requestTimeoutMs;
|
|
232
|
+
constructor(localPeerId, proximityTable, options) {
|
|
233
|
+
this.localPeerId = localPeerId;
|
|
234
|
+
this.proximityTable = proximityTable;
|
|
235
|
+
this.routeTtlMs = options?.routeTtlMs ?? DEFAULT_ROUTE_TTL_MS;
|
|
236
|
+
this.requestTimeoutMs = options?.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
237
|
+
}
|
|
238
|
+
// -----------------------------------------------------------------------
|
|
239
|
+
// Route cache public API
|
|
240
|
+
// -----------------------------------------------------------------------
|
|
241
|
+
/** Retrieve a cached route if it exists and has not expired. */
|
|
242
|
+
getCachedRoute(destHash) {
|
|
243
|
+
return this.routeCache.getCachedRoute(destHash);
|
|
244
|
+
}
|
|
245
|
+
/** Store a route in the cache. */
|
|
246
|
+
cacheRoute(route) {
|
|
247
|
+
this.routeCache.cacheRoute(route);
|
|
248
|
+
}
|
|
249
|
+
/** Remove a cached route. */
|
|
250
|
+
invalidateRoute(destHash) {
|
|
251
|
+
this.routeCache.invalidateRoute(destHash);
|
|
252
|
+
}
|
|
253
|
+
// -----------------------------------------------------------------------
|
|
254
|
+
// Route discovery (PRD section 7.1)
|
|
255
|
+
// -----------------------------------------------------------------------
|
|
256
|
+
/**
|
|
257
|
+
* Discover a route to the given destination hash.
|
|
258
|
+
*
|
|
259
|
+
* Algorithm:
|
|
260
|
+
* 1. Check the route cache for a non-expired entry.
|
|
261
|
+
* 2. Check if any connected peer in the proximity table has a recent
|
|
262
|
+
* relay path to the destination.
|
|
263
|
+
* 3. If not, create a RouteRequest (containing only dest_hash, not
|
|
264
|
+
* sender identity) and gossip it to socially proximate peers.
|
|
265
|
+
* 4. Collect RouteOffer responses within the request timeout.
|
|
266
|
+
* 5. Select the shortest/fastest offered route.
|
|
267
|
+
*
|
|
268
|
+
* Returns the route, or null if no route could be discovered within the
|
|
269
|
+
* timeout window.
|
|
270
|
+
*/
|
|
271
|
+
findRoute(destHash) {
|
|
272
|
+
// 1. Check cache.
|
|
273
|
+
const cached = this.routeCache.getCachedRoute(destHash);
|
|
274
|
+
if (cached)
|
|
275
|
+
return Promise.resolve(cached);
|
|
276
|
+
// 2. Check proximity table for a direct or relay path.
|
|
277
|
+
const directPeer = this.proximityTable.findPeer(destHash);
|
|
278
|
+
if (directPeer && this.isRecent(directPeer.lastSeen)) {
|
|
279
|
+
const route = this.peerToRoute(directPeer, destHash);
|
|
280
|
+
this.routeCache.cacheRoute(route);
|
|
281
|
+
return Promise.resolve(route);
|
|
282
|
+
}
|
|
283
|
+
// 3. Create a RouteRequest and gossip to close peers.
|
|
284
|
+
const requestId = this.generateRequestId();
|
|
285
|
+
const request = {
|
|
286
|
+
destHash,
|
|
287
|
+
requestId,
|
|
288
|
+
ttl: 6, // small-world average path length
|
|
289
|
+
timestamp: Date.now(),
|
|
290
|
+
};
|
|
291
|
+
return new Promise((resolve) => {
|
|
292
|
+
const pending = {
|
|
293
|
+
request,
|
|
294
|
+
createdAt: Date.now(),
|
|
295
|
+
timeoutMs: this.requestTimeoutMs,
|
|
296
|
+
offers: [],
|
|
297
|
+
resolve,
|
|
298
|
+
};
|
|
299
|
+
const key = hashToHex(requestId);
|
|
300
|
+
this.pendingRequests.set(key, pending);
|
|
301
|
+
// 4-5. Timeout: after the window closes, pick the best offer.
|
|
302
|
+
setTimeout(() => {
|
|
303
|
+
this.resolvePendingRequest(key);
|
|
304
|
+
}, this.requestTimeoutMs);
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Handle an incoming route request from a neighbouring peer.
|
|
309
|
+
*
|
|
310
|
+
* If this node knows the destination (via its proximity table), it
|
|
311
|
+
* returns a RouteOffer. Otherwise returns null (the caller should
|
|
312
|
+
* decide whether to forward the request further).
|
|
313
|
+
*/
|
|
314
|
+
handleRouteRequest(request, fromPeer) {
|
|
315
|
+
const peer = this.proximityTable.findPeer(request.destHash);
|
|
316
|
+
if (!peer)
|
|
317
|
+
return null;
|
|
318
|
+
if (!this.isRecent(peer.lastSeen))
|
|
319
|
+
return null;
|
|
320
|
+
return {
|
|
321
|
+
requestId: request.requestId,
|
|
322
|
+
hopCount: peer.hopCount + 1, // +1 for this node in the path
|
|
323
|
+
estimatedLatency: peer.latency,
|
|
324
|
+
offeredBy: this.localPeerId,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Register a received route offer against the matching pending request.
|
|
329
|
+
* If no pending request exists for the offer's requestId, the offer is
|
|
330
|
+
* silently dropped.
|
|
331
|
+
*/
|
|
332
|
+
handleRouteOffer(offer) {
|
|
333
|
+
const key = hashToHex(offer.requestId);
|
|
334
|
+
const pending = this.pendingRequests.get(key);
|
|
335
|
+
if (!pending)
|
|
336
|
+
return;
|
|
337
|
+
pending.offers.push(offer);
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Return a list of the RouteRequests that are still awaiting responses.
|
|
341
|
+
* Expired requests are pruned as a side-effect.
|
|
342
|
+
*/
|
|
343
|
+
getPendingRequests() {
|
|
344
|
+
this.pruneExpiredRequests();
|
|
345
|
+
const result = [];
|
|
346
|
+
for (const pending of this.pendingRequests.values()) {
|
|
347
|
+
result.push(pending.request);
|
|
348
|
+
}
|
|
349
|
+
return result;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Return the list of socially proximate peers that a route request
|
|
353
|
+
* should be gossiped to. Useful for the transport layer to know where
|
|
354
|
+
* to send route requests produced by findRoute.
|
|
355
|
+
*/
|
|
356
|
+
getGossipTargets(destHash) {
|
|
357
|
+
return this.proximityTable.getClosestPeers(destHash, GOSSIP_FANOUT);
|
|
358
|
+
}
|
|
359
|
+
// -----------------------------------------------------------------------
|
|
360
|
+
// Message forwarding
|
|
361
|
+
// -----------------------------------------------------------------------
|
|
362
|
+
/**
|
|
363
|
+
* Determine the next-hop peer to forward a message to for the given
|
|
364
|
+
* destination hash.
|
|
365
|
+
*
|
|
366
|
+
* Returns null if no route is known.
|
|
367
|
+
*/
|
|
368
|
+
getNextHop(destHash) {
|
|
369
|
+
// Try cache first.
|
|
370
|
+
const cached = this.routeCache.getCachedRoute(destHash);
|
|
371
|
+
if (cached)
|
|
372
|
+
return cached.nextHop;
|
|
373
|
+
// Try direct peer from proximity table.
|
|
374
|
+
const peer = this.proximityTable.findPeer(destHash);
|
|
375
|
+
if (peer && this.isRecent(peer.lastSeen)) {
|
|
376
|
+
return peer.relayPath.length > 0 ? peer.relayPath[0] : peer.peerId;
|
|
377
|
+
}
|
|
378
|
+
// Try closest peer as a heuristic relay.
|
|
379
|
+
const closest = this.proximityTable.getClosestPeers(destHash, 1);
|
|
380
|
+
if (closest.length > 0 && this.isRecent(closest[0].lastSeen)) {
|
|
381
|
+
return closest[0].peerId;
|
|
382
|
+
}
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Determine whether this device should relay a given packet.
|
|
387
|
+
*
|
|
388
|
+
* A packet is relayed when:
|
|
389
|
+
* - Its TTL is greater than 0
|
|
390
|
+
* - It has not been seen before (deduplication)
|
|
391
|
+
*/
|
|
392
|
+
shouldRelay(packet) {
|
|
393
|
+
if (packet.ttl <= 0)
|
|
394
|
+
return false;
|
|
395
|
+
const fp = packetFingerprint(packet);
|
|
396
|
+
const alreadySeen = this.seenPackets.testAndAdd(fp);
|
|
397
|
+
return !alreadySeen;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Return a copy of the packet with TTL decremented by 1.
|
|
401
|
+
* Callers must ensure TTL > 0 before calling (see shouldRelay).
|
|
402
|
+
*/
|
|
403
|
+
decrementTTL(packet) {
|
|
404
|
+
if (packet.ttl <= 0) {
|
|
405
|
+
throw new RangeError('Cannot decrement TTL below 0');
|
|
406
|
+
}
|
|
407
|
+
return {
|
|
408
|
+
...packet,
|
|
409
|
+
ttl: packet.ttl - 1,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
// -----------------------------------------------------------------------
|
|
413
|
+
// Internal helpers
|
|
414
|
+
// -----------------------------------------------------------------------
|
|
415
|
+
/** Check if a timestamp is recent enough to trust. */
|
|
416
|
+
isRecent(timestamp) {
|
|
417
|
+
return (Date.now() - timestamp) < DEFAULT_PEER_TTL_MS;
|
|
418
|
+
}
|
|
419
|
+
/** Convert a proximity entry to a Route. */
|
|
420
|
+
peerToRoute(peer, destHash) {
|
|
421
|
+
const now = Date.now();
|
|
422
|
+
return {
|
|
423
|
+
destHash,
|
|
424
|
+
nextHop: peer.relayPath.length > 0 ? peer.relayPath[0] : peer.peerId,
|
|
425
|
+
hopCount: peer.hopCount,
|
|
426
|
+
estimatedLatency: peer.latency,
|
|
427
|
+
discoveredAt: now,
|
|
428
|
+
expiresAt: now + this.routeTtlMs,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
/** Resolve a pending request by selecting the best offer. */
|
|
432
|
+
resolvePendingRequest(key) {
|
|
433
|
+
const pending = this.pendingRequests.get(key);
|
|
434
|
+
if (!pending)
|
|
435
|
+
return;
|
|
436
|
+
this.pendingRequests.delete(key);
|
|
437
|
+
if (pending.offers.length === 0) {
|
|
438
|
+
pending.resolve(null);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
// Select best offer: prefer fewest hops, then lowest latency.
|
|
442
|
+
pending.offers.sort((a, b) => {
|
|
443
|
+
if (a.hopCount !== b.hopCount)
|
|
444
|
+
return a.hopCount - b.hopCount;
|
|
445
|
+
return a.estimatedLatency - b.estimatedLatency;
|
|
446
|
+
});
|
|
447
|
+
const best = pending.offers[0];
|
|
448
|
+
const now = Date.now();
|
|
449
|
+
const route = {
|
|
450
|
+
destHash: pending.request.destHash,
|
|
451
|
+
nextHop: best.offeredBy,
|
|
452
|
+
hopCount: best.hopCount,
|
|
453
|
+
estimatedLatency: best.estimatedLatency,
|
|
454
|
+
discoveredAt: now,
|
|
455
|
+
expiresAt: now + this.routeTtlMs,
|
|
456
|
+
};
|
|
457
|
+
this.routeCache.cacheRoute(route);
|
|
458
|
+
pending.resolve(route);
|
|
459
|
+
}
|
|
460
|
+
/** Remove pending requests that have exceeded their timeout. */
|
|
461
|
+
pruneExpiredRequests() {
|
|
462
|
+
const now = Date.now();
|
|
463
|
+
for (const [key, pending] of this.pendingRequests) {
|
|
464
|
+
if (now - pending.createdAt > pending.timeoutMs) {
|
|
465
|
+
this.pendingRequests.delete(key);
|
|
466
|
+
pending.resolve(null);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
/** Generate a random 16-byte request ID. */
|
|
471
|
+
generateRequestId() {
|
|
472
|
+
const id = new Uint8Array(16);
|
|
473
|
+
// globalThis.crypto is available in Node.js 19+ and all modern browsers.
|
|
474
|
+
globalThis.crypto.getRandomValues(id);
|
|
475
|
+
return id;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routing/index.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,yCAAyC;AACzC,2DAA2D;AAC3D,2DAA2D;AAC3D,uDAAuD;AACvD,uBAAuB;AACvB,+DAA+D;AAI/D,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,wDAAwD;AACxD,MAAM,mBAAmB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE3C,iDAAiD;AACjD,MAAM,oBAAoB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAE3C,8DAA8D;AAC9D,MAAM,0BAA0B,GAAG,CAAC,GAAG,IAAI,CAAC;AAE5C,qEAAqE;AACrE,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAEpC,oEAAoE;AACpE,MAAM,aAAa,GAAG,CAAC,CAAC;AAexB,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,iEAAiE;AACjE,SAAS,SAAS,CAAC,IAAgB;IACjC,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,6CAA6C;AAC7C,SAAS,UAAU,CAAC,CAAa,EAAE,CAAa;IAC9C,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACtB,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,CAAC;AACpB,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,CAAa,EAAE,CAAa;IAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,QAAQ,GAAG,QAAQ,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CAAC,MAAc;IACvC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChD,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzD,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IACD,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAClE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,8EAA8E;AAC9E,qBAAqB;AACrB,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,OAAO,kBAAkB;IACZ,OAAO,GAAoC,IAAI,GAAG,EAAE,CAAC;IACrD,aAAa,GAAwB,IAAI,GAAG,EAAE,CAAC,CAAC,0BAA0B;IAC1E,KAAK,CAAS;IAE/B,YAAY,QAAgB,mBAAmB;QAC7C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,mCAAmC;IACnC,OAAO,CAAC,KAAyB;QAC/B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACtC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAClE,CAAC;IAED,2BAA2B;IAC3B,UAAU,CAAC,MAAc;QACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;YACxD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,+CAA+C;IAC/C,UAAU,CAAC,MAAc,EAAE,OAAoC;QAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ;YAAE,OAAO;QAEtB,yCAAyC;QACzC,IAAI,OAAO,CAAC,QAAQ,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzE,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;YACxD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;QAC9D,CAAC;QAED,MAAM,OAAO,GAAuB,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,CAAC;QAChE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,CAAC;IAED,wDAAwD;IACxD,QAAQ,CAAC,QAAoB;QAC3B,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QAEtC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,gCAAgC;YAChC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC/B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;OAGG;IACH,eAAe,CAAC,QAAoB,EAAE,KAAa;QACjD,MAAM,MAAM,GAA2D,EAAE,CAAC;QAE1E,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1C,MAAM,CAAC,IAAI,CAAC;gBACV,KAAK;gBACL,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC;aAChD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC/C,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC;IAED,4DAA4D;IAC5D,UAAU,CAAC,WAAmB,IAAI,CAAC,KAAK;QACtC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC;QACrC,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC3C,IAAI,KAAK,CAAC,QAAQ,GAAG,MAAM,EAAE,CAAC;gBAC5B,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IAED,gDAAgD;IAChD,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IAC3B,CAAC;IAED,sCAAsC;IACtC,UAAU;QACR,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3C,CAAC;CACF;AAED,8EAA8E;AAC9E,4CAA4C;AAC5C,8EAA8E;AAE9E,MAAM,aAAa;IACA,QAAQ,CAAS;IACjB,GAAG,GAAgB,IAAI,GAAG,EAAE,CAAC;IAC7B,IAAI,CAAW;IACxB,MAAM,GAAG,CAAC,CAAC;IAEnB,YAAY,WAAmB,oBAAoB;QACjD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,KAAK,CAAS,QAAQ,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,8DAA8D;IAC9D,UAAU,CAAC,WAAmB;QAC5B,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC;YAAE,OAAO,IAAI,CAAC;QAE3C,+BAA+B;QAC/B,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrC,IAAI,KAAK;gBAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC;QACrC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;QAChD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,GAAG,CAAC,WAAmB;QACrB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACnC,CAAC;CACF;AAcD,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E,MAAM,UAAU;IACG,MAAM,GAAuB,IAAI,GAAG,EAAE,CAAC;IAExD,cAAc,CAAC,QAAoB;QACjC,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,UAAU,CAAC,KAAY;QACrB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAC;IACpD,CAAC;IAED,eAAe,CAAC,QAAoB;QAClC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC1C,CAAC;CACF;AAED,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,OAAO,iBAAiB;IACnB,WAAW,CAAS;IACpB,cAAc,CAAqB;IAE3B,UAAU,GAAe,IAAI,UAAU,EAAE,CAAC;IAC1C,WAAW,GAAkB,IAAI,aAAa,EAAE,CAAC;IACjD,eAAe,GAAgC,IAAI,GAAG,EAAE,CAAC;IACzD,UAAU,CAAS;IACnB,gBAAgB,CAAS;IAE1C,YACE,WAAmB,EACnB,cAAkC,EAClC,OAGC;QAED,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,oBAAoB,CAAC;QAC9D,IAAI,CAAC,gBAAgB,GAAG,OAAO,EAAE,gBAAgB,IAAI,0BAA0B,CAAC;IAClF,CAAC;IAED,0EAA0E;IAC1E,yBAAyB;IACzB,0EAA0E;IAE1E,gEAAgE;IAChE,cAAc,CAAC,QAAoB;QACjC,OAAO,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;IAClD,CAAC;IAED,kCAAkC;IAClC,UAAU,CAAC,KAAY;QACrB,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,6BAA6B;IAC7B,eAAe,CAAC,QAAoB;QAClC,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC5C,CAAC;IAED,0EAA0E;IAC1E,oCAAoC;IACpC,0EAA0E;IAE1E;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,QAAoB;QAC5B,kBAAkB;QAClB,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QACxD,IAAI,MAAM;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAE3C,uDAAuD;QACvD,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC1D,IAAI,UAAU,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YACrD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAClC,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;QAED,sDAAsD;QACtD,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAiB;YAC5B,QAAQ;YACR,SAAS;YACT,GAAG,EAAE,CAAC,EAAE,kCAAkC;YAC1C,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,OAAO,IAAI,OAAO,CAAe,CAAC,OAAO,EAAE,EAAE;YAC3C,MAAM,OAAO,GAAmB;gBAC9B,OAAO;gBACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;gBACrB,SAAS,EAAE,IAAI,CAAC,gBAAgB;gBAChC,MAAM,EAAE,EAAE;gBACV,OAAO;aACR,CAAC;YAEF,MAAM,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;YACjC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAEvC,8DAA8D;YAC9D,UAAU,CAAC,GAAG,EAAE;gBACd,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC;YAClC,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACH,kBAAkB,CAAC,OAAqB,EAAE,QAAgB;QACxD,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAE/C,OAAO;YACL,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,QAAQ,EAAE,IAAI,CAAC,QAAQ,GAAG,CAAC,EAAE,+BAA+B;YAC5D,gBAAgB,EAAE,IAAI,CAAC,OAAO;YAC9B,SAAS,EAAE,IAAI,CAAC,WAAW;SAC5B,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,gBAAgB,CAAC,KAAiB;QAChC,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,CAAC,OAAO;YAAE,OAAO;QAErB,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED;;;OAGG;IACH,kBAAkB;QAChB,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAmB,EAAE,CAAC;QAClC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,EAAE,CAAC;YACpD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;OAIG;IACH,gBAAgB,CAAC,QAAoB;QACnC,OAAO,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IACtE,CAAC;IAED,0EAA0E;IAC1E,qBAAqB;IACrB,0EAA0E;IAE1E;;;;;OAKG;IACH,UAAU,CAAC,QAAoB;QAC7B,mBAAmB;QACnB,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QACxD,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC,OAAO,CAAC;QAElC,wCAAwC;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACpD,IAAI,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;QACrE,CAAC;QAED,yCAAyC;QACzC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QACjE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7D,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAC3B,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;OAMG;IACH,WAAW,CAAC,MAAc;QACxB,IAAI,MAAM,CAAC,GAAG,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;QAElC,MAAM,EAAE,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACpD,OAAO,CAAC,WAAW,CAAC;IACtB,CAAC;IAED;;;OAGG;IACH,YAAY,CAAC,MAAc;QACzB,IAAI,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;YACpB,MAAM,IAAI,UAAU,CAAC,8BAA8B,CAAC,CAAC;QACvD,CAAC;QACD,OAAO;YACL,GAAG,MAAM;YACT,GAAG,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC;SACpB,CAAC;IACJ,CAAC;IAED,0EAA0E;IAC1E,mBAAmB;IACnB,0EAA0E;IAE1E,sDAAsD;IAC9C,QAAQ,CAAC,SAAiB;QAChC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,mBAAmB,CAAC;IACxD,CAAC;IAED,4CAA4C;IACpC,WAAW,CAAC,IAAwB,EAAE,QAAoB;QAChE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,OAAO;YACL,QAAQ;YACR,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM;YACpE,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,gBAAgB,EAAE,IAAI,CAAC,OAAO;YAC9B,YAAY,EAAE,GAAG;YACjB,SAAS,EAAE,GAAG,GAAG,IAAI,CAAC,UAAU;SACjC,CAAC;IACJ,CAAC;IAED,6DAA6D;IACrD,qBAAqB,CAAC,GAAW;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,CAAC,OAAO;YAAE,OAAO;QAErB,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAEjC,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACtB,OAAO;QACT,CAAC;QAED,8DAA8D;QAC9D,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAC3B,IAAI,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ;gBAAE,OAAO,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC;YAC9D,OAAO,CAAC,CAAC,gBAAgB,GAAG,CAAC,CAAC,gBAAgB,CAAC;QACjD,CAAC,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,KAAK,GAAU;YACnB,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,QAAQ;YAClC,OAAO,EAAE,IAAI,CAAC,SAAS;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;YACvC,YAAY,EAAE,GAAG;YACjB,SAAS,EAAE,GAAG,GAAG,IAAI,CAAC,UAAU;SACjC,CAAC;QAEF,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAClC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED,gEAAgE;IACxD,oBAAoB;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YAClD,IAAI,GAAG,GAAG,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;gBAChD,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACjC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACxB,CAAC;QACH,CAAC;IACH,CAAC;IAED,4CAA4C;IACpC,iBAAiB;QACvB,MAAM,EAAE,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;QAC9B,yEAAyE;QACzE,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QACtC,OAAO,EAAE,CAAC;IACZ,CAAC;CACF"}
|