@fairfox/polly 0.52.0 → 0.54.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/mesh.js CHANGED
@@ -1849,8 +1849,8 @@ import {
1849
1849
  } from "@automerge/automerge-repo/slim";
1850
1850
 
1851
1851
  // src/shared/lib/sync-fragment.ts
1852
- var SYNC_FRAGMENT_THRESHOLD = 64 * 1024;
1853
- var SYNC_FRAGMENT_CHUNK_SIZE = 64 * 1024;
1852
+ var SYNC_FRAGMENT_THRESHOLD = 60 * 1024;
1853
+ var SYNC_FRAGMENT_CHUNK_SIZE = 60 * 1024;
1854
1854
  function serialiseSyncFragment(header, data) {
1855
1855
  const headerBytes = new TextEncoder().encode(JSON.stringify(header));
1856
1856
  const size = 4 + headerBytes.length + data.length;
@@ -1943,6 +1943,11 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
1943
1943
  iceServers;
1944
1944
  dataChannelLabel;
1945
1945
  knownPeers;
1946
+ keyringSource;
1947
+ knownPeersRefreshTimer;
1948
+ knownPeersRefreshIntervalMs;
1949
+ syncYieldEnabled;
1950
+ syncFragmentChunkSize;
1946
1951
  presentPeers = new Set;
1947
1952
  localPeerId;
1948
1953
  RTCPeerConnectionCtor;
@@ -1950,8 +1955,17 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
1950
1955
  ready = false;
1951
1956
  readyResolver;
1952
1957
  get knownPeerIds() {
1958
+ if (this.keyringSource !== undefined) {
1959
+ return [...this.keyringSource().knownPeers.keys()].filter((id) => id !== this.localPeerId);
1960
+ }
1953
1961
  return [...this.knownPeers];
1954
1962
  }
1963
+ hasKnownPeer(remotePeerId) {
1964
+ if (this.keyringSource !== undefined) {
1965
+ return this.keyringSource().knownPeers.has(remotePeerId);
1966
+ }
1967
+ return this.knownPeers.has(remotePeerId);
1968
+ }
1955
1969
  onBlobMessage;
1956
1970
  constructor(options) {
1957
1971
  super();
@@ -1959,6 +1973,10 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
1959
1973
  this.iceServers = options.iceServers ?? DEFAULT_ICE_SERVERS;
1960
1974
  this.dataChannelLabel = options.dataChannelLabel ?? "polly-mesh";
1961
1975
  this.knownPeers = new Set(options.knownPeerIds ?? []);
1976
+ this.keyringSource = options.keyringSource;
1977
+ this.knownPeersRefreshIntervalMs = options.knownPeersRefreshIntervalMs ?? 2000;
1978
+ this.syncYieldEnabled = options.syncYieldEnabled ?? true;
1979
+ this.syncFragmentChunkSize = options.syncFragmentChunkSizeOverride ?? SYNC_FRAGMENT_CHUNK_SIZE;
1962
1980
  this.localPeerId = options.peerId;
1963
1981
  const PC = options.RTCPeerConnection ?? globalThis.RTCPeerConnection;
1964
1982
  if (typeof PC !== "function") {
@@ -1972,6 +1990,42 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
1972
1990
  peerSlotCount() {
1973
1991
  return this.slots.size;
1974
1992
  }
1993
+ getPeerStateSnapshot() {
1994
+ const knownPeerIds = this.knownPeerIds;
1995
+ const presentPeerIds = [...this.presentPeers];
1996
+ const knownPeerSet = new Set(knownPeerIds);
1997
+ const allPeers = new Set;
1998
+ for (const id of knownPeerIds)
1999
+ allPeers.add(id);
2000
+ for (const id of presentPeerIds)
2001
+ allPeers.add(id);
2002
+ for (const id of this.slots.keys())
2003
+ allPeers.add(id);
2004
+ const peers = [];
2005
+ for (const peerId of allPeers) {
2006
+ const slot = this.slots.get(peerId);
2007
+ peers.push({
2008
+ peerId,
2009
+ knownInKeyring: knownPeerSet.has(peerId),
2010
+ presentInSignalling: this.presentPeers.has(peerId),
2011
+ slot: slot ? {
2012
+ signalingState: slot.connection.signalingState,
2013
+ iceConnectionState: slot.connection.iceConnectionState,
2014
+ connectionState: slot.connection.connectionState,
2015
+ dataChannelState: slot.channel?.readyState ?? "no-channel",
2016
+ pendingSendCount: slot.pendingSends.length,
2017
+ pendingRemoteIceCount: slot.pendingRemoteIce.length,
2018
+ inFlightSync: slot.inFlightSync ? { ...slot.inFlightSync } : undefined
2019
+ } : undefined
2020
+ });
2021
+ }
2022
+ return {
2023
+ localPeerId: this.localPeerId,
2024
+ knownPeerIds,
2025
+ presentPeerIds,
2026
+ peers
2027
+ };
2028
+ }
1975
2029
  handlePeerJoined(remotePeerId) {
1976
2030
  this.presentPeers.add(remotePeerId);
1977
2031
  if (!this.shouldInitiateTo(remotePeerId))
@@ -1998,19 +2052,28 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
1998
2052
  addKnownPeer(remotePeerId) {
1999
2053
  if (remotePeerId === this.localPeerId)
2000
2054
  return;
2001
- if (this.knownPeers.has(remotePeerId))
2002
- return;
2003
- this.knownPeers.add(remotePeerId);
2055
+ if (this.keyringSource === undefined) {
2056
+ if (this.knownPeers.has(remotePeerId))
2057
+ return;
2058
+ this.knownPeers.add(remotePeerId);
2059
+ }
2004
2060
  if (!this.presentPeers.has(remotePeerId))
2005
2061
  return;
2006
2062
  if (!this.shouldInitiateTo(remotePeerId))
2007
2063
  return;
2008
2064
  this.createInitiatingSlot(remotePeerId);
2009
2065
  }
2066
+ refreshKnownPeers() {
2067
+ for (const remotePeerId of this.presentPeers) {
2068
+ if (!this.shouldInitiateTo(remotePeerId))
2069
+ continue;
2070
+ this.createInitiatingSlot(remotePeerId);
2071
+ }
2072
+ }
2010
2073
  shouldInitiateTo(remotePeerId) {
2011
2074
  if (remotePeerId === this.localPeerId)
2012
2075
  return false;
2013
- if (!this.knownPeers.has(remotePeerId))
2076
+ if (!this.hasKnownPeer(remotePeerId))
2014
2077
  return false;
2015
2078
  if (this.slots.has(remotePeerId))
2016
2079
  return false;
@@ -2032,8 +2095,10 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
2032
2095
  }
2033
2096
  this.ready = true;
2034
2097
  this.readyResolver?.();
2098
+ this.startKnownPeersSweep();
2035
2099
  }
2036
2100
  disconnect() {
2101
+ this.stopKnownPeersSweep();
2037
2102
  for (const slot of this.slots.values()) {
2038
2103
  slot.channel?.close();
2039
2104
  slot.connection.close();
@@ -2043,6 +2108,23 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
2043
2108
  this.ready = false;
2044
2109
  this.emit("close");
2045
2110
  }
2111
+ startKnownPeersSweep() {
2112
+ if (this.keyringSource === undefined)
2113
+ return;
2114
+ if (this.knownPeersRefreshIntervalMs <= 0)
2115
+ return;
2116
+ if (this.knownPeersRefreshTimer !== undefined)
2117
+ return;
2118
+ this.knownPeersRefreshTimer = setInterval(() => {
2119
+ this.refreshKnownPeers();
2120
+ }, this.knownPeersRefreshIntervalMs);
2121
+ }
2122
+ stopKnownPeersSweep() {
2123
+ if (this.knownPeersRefreshTimer === undefined)
2124
+ return;
2125
+ clearInterval(this.knownPeersRefreshTimer);
2126
+ this.knownPeersRefreshTimer = undefined;
2127
+ }
2046
2128
  send(message) {
2047
2129
  const targetId = message.targetId;
2048
2130
  const bytes = this.serialiseMessage(message);
@@ -2056,15 +2138,29 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
2056
2138
  slot.pendingSends.push(bytes);
2057
2139
  }
2058
2140
  }
2059
- sendBytesMaybeFragmented(channel, bytes) {
2141
+ static SEND_YIELD_EVERY_N_FRAGMENTS = 8;
2142
+ async sendBytesMaybeFragmented(channel, bytes) {
2060
2143
  if (bytes.length <= SYNC_FRAGMENT_THRESHOLD) {
2061
2144
  channel.send(bytes);
2062
2145
  return;
2063
2146
  }
2064
2147
  const id = crypto.randomUUID();
2065
- const fragments = chunkSyncMessage(bytes, id);
2148
+ const fragments = chunkSyncMessage(bytes, id, this.syncFragmentChunkSize);
2149
+ if (!this.syncYieldEnabled) {
2150
+ for (const fragment of fragments) {
2151
+ channel.send(fragment);
2152
+ }
2153
+ return;
2154
+ }
2155
+ let i = 0;
2066
2156
  for (const fragment of fragments) {
2067
2157
  channel.send(fragment);
2158
+ i += 1;
2159
+ if (i % MeshWebRTCAdapter.SEND_YIELD_EVERY_N_FRAGMENTS === 0 && i < fragments.length) {
2160
+ await new Promise((resolve) => {
2161
+ setTimeout(resolve, 0);
2162
+ });
2163
+ }
2068
2164
  }
2069
2165
  }
2070
2166
  handleSignal(fromPeerId, rawPayload) {
@@ -2092,7 +2188,8 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
2092
2188
  channel,
2093
2189
  pendingSends: [],
2094
2190
  pendingFragments: new Map,
2095
- pendingRemoteIce: []
2191
+ pendingRemoteIce: [],
2192
+ inFlightSync: undefined
2096
2193
  };
2097
2194
  this.slots.set(targetId, slot);
2098
2195
  this.wireConnection(targetId, connection);
@@ -2122,7 +2219,8 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
2122
2219
  channel: undefined,
2123
2220
  pendingSends: [],
2124
2221
  pendingFragments: new Map,
2125
- pendingRemoteIce: []
2222
+ pendingRemoteIce: [],
2223
+ inFlightSync: undefined
2126
2224
  };
2127
2225
  this.slots.set(fromPeerId, slot);
2128
2226
  this.wireConnection(fromPeerId, connection);
@@ -2233,9 +2331,56 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
2233
2331
  return;
2234
2332
  }
2235
2333
  const message = this.deserialiseMessage(bytes);
2236
- this.emit("message", message);
2334
+ this.scheduleEmitMessage(fromPeerId, message, false);
2237
2335
  } catch {}
2238
2336
  }
2337
+ scheduleEmitMessage(fromPeerId, message, viaFragmentPath) {
2338
+ if (!this.syncYieldEnabled) {
2339
+ this.emit("message", message);
2340
+ if (viaFragmentPath) {
2341
+ this.finishInFlightSyncApply(fromPeerId);
2342
+ }
2343
+ return;
2344
+ }
2345
+ if (viaFragmentPath) {
2346
+ const slot = this.slots.get(fromPeerId);
2347
+ if (slot?.inFlightSync) {
2348
+ slot.inFlightSync.applyBacklog += 1;
2349
+ }
2350
+ }
2351
+ setTimeout(() => {
2352
+ try {
2353
+ this.emit("message", message);
2354
+ } finally {
2355
+ if (viaFragmentPath) {
2356
+ this.finishInFlightSyncApply(fromPeerId);
2357
+ }
2358
+ }
2359
+ }, 0);
2360
+ }
2361
+ finishInFlightSyncApply(fromPeerId) {
2362
+ const slot = this.slots.get(fromPeerId);
2363
+ if (!slot?.inFlightSync)
2364
+ return;
2365
+ slot.inFlightSync.applyBacklog = Math.max(0, slot.inFlightSync.applyBacklog - 1);
2366
+ this.emitSyncProgress(fromPeerId, "dispatch-applied", 0);
2367
+ if (slot.inFlightSync.applyBacklog === 0 && slot.pendingFragments.size === 0) {
2368
+ slot.inFlightSync = undefined;
2369
+ }
2370
+ }
2371
+ emitSyncProgress(fromPeerId, kind, bytesDelta) {
2372
+ const slot = this.slots.get(fromPeerId);
2373
+ const inFlightSync = slot?.inFlightSync;
2374
+ this.emit("sync-progress", {
2375
+ peerId: fromPeerId,
2376
+ kind,
2377
+ bytesDelta,
2378
+ chunksReceived: inFlightSync?.chunksReceived ?? 0,
2379
+ bytesReceived: inFlightSync?.bytesReceived ?? 0,
2380
+ applyBacklog: inFlightSync?.applyBacklog ?? 0,
2381
+ at: performance.now()
2382
+ });
2383
+ }
2239
2384
  handleSyncFragment(fromPeerId, bytes) {
2240
2385
  const parsed = parseSyncFragment(bytes);
2241
2386
  if (!parsed)
@@ -2250,11 +2395,46 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
2250
2395
  slot.pendingFragments.set(header.id, entry);
2251
2396
  }
2252
2397
  entry.chunks.set(header.index, data.slice());
2398
+ if (!slot.inFlightSync) {
2399
+ slot.inFlightSync = {
2400
+ chunksReceived: 0,
2401
+ bytesReceived: 0,
2402
+ lastChunkAt: performance.now(),
2403
+ applyBacklog: 0
2404
+ };
2405
+ }
2406
+ slot.inFlightSync.chunksReceived += 1;
2407
+ slot.inFlightSync.bytesReceived += data.byteLength;
2408
+ slot.inFlightSync.lastChunkAt = performance.now();
2409
+ this.emitSyncProgress(fromPeerId, "fragment-received", data.byteLength);
2253
2410
  if (entry.chunks.size < entry.total)
2254
2411
  return;
2255
2412
  slot.pendingFragments.delete(header.id);
2256
2413
  const reassembled = reassembleSyncFragments(entry.chunks, entry.total);
2257
- this.dispatchMessage(fromPeerId, reassembled);
2414
+ if (!this.syncYieldEnabled) {
2415
+ this.dispatchReassembled(fromPeerId, reassembled);
2416
+ return;
2417
+ }
2418
+ setTimeout(() => {
2419
+ this.dispatchReassembled(fromPeerId, reassembled);
2420
+ }, 0);
2421
+ }
2422
+ dispatchReassembled(fromPeerId, bytes) {
2423
+ try {
2424
+ if (this.onBlobMessage && isBlobMessageType(bytes)) {
2425
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
2426
+ const headerLen = view.getUint32(0, false);
2427
+ const header = JSON.parse(new TextDecoder().decode(bytes.subarray(4, 4 + headerLen)));
2428
+ const data = bytes.subarray(4 + headerLen);
2429
+ this.onBlobMessage(fromPeerId, header, data);
2430
+ this.finishInFlightSyncApply(fromPeerId);
2431
+ return;
2432
+ }
2433
+ const message = this.deserialiseMessage(bytes);
2434
+ this.scheduleEmitMessage(fromPeerId, message, true);
2435
+ } catch {
2436
+ this.finishInFlightSyncApply(fromPeerId);
2437
+ }
2258
2438
  }
2259
2439
  get connectedPeerIds() {
2260
2440
  const ids = [];
@@ -2332,12 +2512,22 @@ async function createMeshClient(options) {
2332
2512
  signaling: undefined,
2333
2513
  peerId: options.signaling.peerId,
2334
2514
  knownPeerIds,
2515
+ keyringSource,
2335
2516
  ...resolvedIceServers !== undefined && { iceServers: resolvedIceServers },
2336
2517
  ...options.rtc?.dataChannelLabel !== undefined && {
2337
2518
  dataChannelLabel: options.rtc.dataChannelLabel
2338
2519
  },
2339
2520
  ...options.rtc?.RTCPeerConnection !== undefined && {
2340
2521
  RTCPeerConnection: options.rtc.RTCPeerConnection
2522
+ },
2523
+ ...options.rtc?.knownPeersRefreshIntervalMs !== undefined && {
2524
+ knownPeersRefreshIntervalMs: options.rtc.knownPeersRefreshIntervalMs
2525
+ },
2526
+ ...options.rtc?.syncYieldEnabled !== undefined && {
2527
+ syncYieldEnabled: options.rtc.syncYieldEnabled
2528
+ },
2529
+ ...options.rtc?.syncFragmentChunkSizeOverride !== undefined && {
2530
+ syncFragmentChunkSizeOverride: options.rtc.syncFragmentChunkSizeOverride
2341
2531
  }
2342
2532
  };
2343
2533
  let webrtcAdapter;
@@ -2384,6 +2574,20 @@ async function createMeshClient(options) {
2384
2574
  signaling,
2385
2575
  networkAdapter,
2386
2576
  webrtcAdapter,
2577
+ refreshKnownPeers: () => {
2578
+ webrtcAdapter?.refreshKnownPeers();
2579
+ },
2580
+ getPeerStateSnapshot: () => {
2581
+ if (!webrtcAdapter) {
2582
+ return {
2583
+ localPeerId: options.signaling.peerId,
2584
+ knownPeerIds: [],
2585
+ presentPeerIds: [],
2586
+ peers: []
2587
+ };
2588
+ }
2589
+ return webrtcAdapter.getPeerStateSnapshot();
2590
+ },
2387
2591
  close: async () => {
2388
2592
  signaling.close();
2389
2593
  webrtcAdapter?.disconnect();
@@ -2798,4 +3002,4 @@ export {
2798
3002
  $meshCounter
2799
3003
  };
2800
3004
 
2801
- //# debugId=0A403673455F136164756E2164756E21
3005
+ //# debugId=18409B674B254AA864756E2164756E21