@fairfox/polly 0.56.0 → 0.58.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.
@@ -21,15 +21,15 @@ export type { EncryptedEnvelope, SealedBytes, } from "./shared/lib/encryption";
21
21
  export { decrypt, decryptOrThrow, EncryptionError, encrypt, generateDocumentKey, KEY_BYTES as ENCRYPTION_KEY_BYTES, NONCE_BYTES as ENCRYPTION_NONCE_BYTES, TAG_BYTES as ENCRYPTION_TAG_BYTES, } from "./shared/lib/encryption";
22
22
  export type { KeyringStorage } from "./shared/lib/keyring-storage";
23
23
  export { deserialiseKeyring, memoryKeyringStorage, serialiseKeyring, } from "./shared/lib/keyring-storage";
24
- export type { CreateMeshClientOptions, MeshClient } from "./shared/lib/mesh-client";
24
+ export type { CreateMeshClientOptions, MeshClient, MeshClientHandleSnapshot, MeshClientPeerStateSnapshot, MeshStateModuleDiagnostics, } from "./shared/lib/mesh-client";
25
25
  export { createMeshClient } from "./shared/lib/mesh-client";
26
26
  export type { MeshKeyring, MeshNetworkAdapterOptions, } from "./shared/lib/mesh-network-adapter";
27
27
  export { DEFAULT_MESH_KEY_ID, MeshNetworkAdapter, } from "./shared/lib/mesh-network-adapter";
28
28
  export type { MeshSignalingClientOptions, SignalingMessage as MeshSignalingMessage, } from "./shared/lib/mesh-signaling-client";
29
29
  export { MeshSignalingClient } from "./shared/lib/mesh-signaling-client";
30
30
  export type { MeshStateOptions } from "./shared/lib/mesh-state";
31
- export { $meshCounter, $meshList, $meshState, $meshText, configureMeshState, resetMeshState, } from "./shared/lib/mesh-state";
32
- export type { InFlightSyncSnapshot, MeshWebRTCAdapterOptions, SlotInitiationDecision, SlotInitiationRejectionReason, SweepSnapshot, SyncHandshakeAttemptSnapshot, SyncProgressEvent, TransportSnapshot, } from "./shared/lib/mesh-webrtc-adapter";
31
+ export { $meshCounter, $meshList, $meshState, $meshText, configureMeshState, getLastConfiguredRepoPeerId, getMeshStateModuleId, isMeshStateConfigured, MESH_STATE_MODULE_ID, resetMeshState, wasMeshStateResolved, } from "./shared/lib/mesh-state";
32
+ export type { HandleSyncSnapshot, InFlightSyncSnapshot, MeshWebRTCAdapterOptions, SlotInitiationDecision, SlotInitiationRejectionReason, SweepSnapshot, SyncHandshakeAttemptSnapshot, SyncProgressEvent, TransportSnapshot, } from "./shared/lib/mesh-webrtc-adapter";
33
33
  export { DEFAULT_ICE_SERVERS, MeshWebRTCAdapter } from "./shared/lib/mesh-webrtc-adapter";
34
34
  export type { CreatePairingTokenOptions, PairingToken, } from "./shared/lib/pairing";
35
35
  export { applyPairingToken, createPairingToken, createPairingTokenWithFreshIdentity, DEFAULT_PAIRING_TTL_MS, decodePairingToken, encodePairingToken, isPairingTokenExpired, PAIRING_NONCE_BYTES, PAIRING_TOKEN_VERSION, PairingError, parsePairingToken, serialisePairingToken, } from "./shared/lib/pairing";
package/dist/src/mesh.js CHANGED
@@ -1075,12 +1075,16 @@ class MeshNetworkAdapter extends NetworkAdapter {
1075
1075
  }
1076
1076
  const signed = signEnvelope(payloadToSign, message.senderId, keyring.identity.secretKey);
1077
1077
  const signedBytes = encodeSignedEnvelope(signed);
1078
- return {
1078
+ const outer = {
1079
1079
  type: message.type,
1080
1080
  senderId: message.senderId,
1081
1081
  targetId: message.targetId,
1082
1082
  data: signedBytes
1083
1083
  };
1084
+ if ("documentId" in message && message.documentId !== undefined) {
1085
+ outer["documentId"] = message.documentId;
1086
+ }
1087
+ return outer;
1084
1088
  }
1085
1089
  tryUnwrap(message) {
1086
1090
  if (!message.data)
@@ -1760,16 +1764,37 @@ function fieldEquals(a, b) {
1760
1764
 
1761
1765
  // src/shared/lib/mesh-state.ts
1762
1766
  var defaultRepo;
1767
+ var MESH_STATE_MODULE_ID = `mesh-state-${typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : Math.random().toString(36).slice(2) + Date.now().toString(36)}`;
1768
+ function getMeshStateModuleId() {
1769
+ return MESH_STATE_MODULE_ID;
1770
+ }
1771
+ var lastConfiguredRepoPeerId;
1772
+ function getLastConfiguredRepoPeerId() {
1773
+ return lastConfiguredRepoPeerId;
1774
+ }
1763
1775
  function configureMeshState(repo) {
1764
1776
  defaultRepo = repo;
1777
+ lastConfiguredRepoPeerId = repo.peerId;
1765
1778
  }
1766
1779
  function resetMeshState() {
1767
1780
  defaultRepo = undefined;
1781
+ lastConfiguredRepoPeerId = undefined;
1782
+ }
1783
+ function isMeshStateConfigured() {
1784
+ return defaultRepo !== undefined;
1785
+ }
1786
+ var meshStateEverResolved = false;
1787
+ function wasMeshStateResolved() {
1788
+ return meshStateEverResolved;
1768
1789
  }
1769
1790
  function resolveRepo(option) {
1791
+ meshStateEverResolved = true;
1770
1792
  const repo = option ?? defaultRepo;
1771
1793
  if (!repo) {
1772
- throw new Error("Polly $meshState: no Repo configured. Call configureMeshState(repo) at startup or pass { repo } in the primitive options.");
1794
+ if (typeof console !== "undefined" && typeof console.warn === "function") {
1795
+ console.warn(`[polly#107 H5] $meshState resolved against unconfigured module instance ${MESH_STATE_MODULE_ID}. If createMeshClient was called elsewhere, the consumer's wrappers and the mesh client are reaching different module instances — see polly#107.`);
1796
+ }
1797
+ throw new Error(`Polly $meshState: no Repo configured (module instance ${MESH_STATE_MODULE_ID}). Call configureMeshState(repo) at startup or pass { repo } in the primitive options. If you have called configureMeshState elsewhere, the most likely cause is that the call resolved to a different module instance than this one — see polly#107.`);
1773
1798
  }
1774
1799
  return repo;
1775
1800
  }
@@ -2033,6 +2058,10 @@ function selectActivePair(parsed) {
2033
2058
  return;
2034
2059
  }
2035
2060
  function serialiseSlotView(slot) {
2061
+ const handles = {};
2062
+ for (const [docId, snapshot] of slot.handles) {
2063
+ handles[docId] = { ...snapshot };
2064
+ }
2036
2065
  return {
2037
2066
  signalingState: slot.connection.signalingState,
2038
2067
  iceConnectionState: slot.connection.iceConnectionState,
@@ -2045,7 +2074,8 @@ function serialiseSlotView(slot) {
2045
2074
  ...slot.transport,
2046
2075
  selectedCandidatePair: slot.transport.selectedCandidatePair ? { ...slot.transport.selectedCandidatePair } : undefined
2047
2076
  } : undefined,
2048
- lastSyncHandshakeAttempt: { ...slot.lastSyncHandshakeAttempt }
2077
+ lastSyncHandshakeAttempt: { ...slot.lastSyncHandshakeAttempt },
2078
+ handles
2049
2079
  };
2050
2080
  }
2051
2081
  function emptySyncHandshakeAttempt() {
@@ -2072,6 +2102,14 @@ var DEFAULT_ICE_SERVERS = [
2072
2102
  { urls: "stun:stun.l.google.com:19302" },
2073
2103
  { urls: "stun:stun1.l.google.com:19302" }
2074
2104
  ];
2105
+ function emptyHandleSyncSnapshot() {
2106
+ return {
2107
+ lastSyncMessageOutAt: undefined,
2108
+ lastSyncMessageInAt: undefined,
2109
+ lastSyncMessageOutSize: undefined,
2110
+ lastSyncMessageOutType: undefined
2111
+ };
2112
+ }
2075
2113
 
2076
2114
  class MeshWebRTCAdapter extends NetworkAdapter2 {
2077
2115
  signaling;
@@ -2319,6 +2357,17 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
2319
2357
  if (slot.lastSyncHandshakeAttempt.firstOutboundSendAt === undefined) {
2320
2358
  slot.lastSyncHandshakeAttempt.firstOutboundSendAt = performance.now();
2321
2359
  }
2360
+ const documentId = message.documentId;
2361
+ if (typeof documentId === "string") {
2362
+ let handleEntry = slot.handles.get(documentId);
2363
+ if (!handleEntry) {
2364
+ handleEntry = emptyHandleSyncSnapshot();
2365
+ slot.handles.set(documentId, handleEntry);
2366
+ }
2367
+ handleEntry.lastSyncMessageOutAt = performance.now();
2368
+ handleEntry.lastSyncMessageOutSize = bytes.length;
2369
+ handleEntry.lastSyncMessageOutType = typeof message.type === "string" ? message.type : undefined;
2370
+ }
2322
2371
  if (slot.channel && slot.channel.readyState === "open") {
2323
2372
  this.sendBytesMaybeFragmented(slot.channel, bytes);
2324
2373
  } else {
@@ -2432,7 +2481,8 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
2432
2481
  inFlightSync: undefined,
2433
2482
  transport: undefined,
2434
2483
  lastDataChannelError: undefined,
2435
- lastSyncHandshakeAttempt: emptySyncHandshakeAttempt()
2484
+ lastSyncHandshakeAttempt: emptySyncHandshakeAttempt(),
2485
+ handles: new Map
2436
2486
  };
2437
2487
  this.slots.set(targetId, slot);
2438
2488
  this.wireConnection(targetId, connection);
@@ -2474,7 +2524,8 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
2474
2524
  inFlightSync: undefined,
2475
2525
  transport: undefined,
2476
2526
  lastDataChannelError: undefined,
2477
- lastSyncHandshakeAttempt: emptySyncHandshakeAttempt()
2527
+ lastSyncHandshakeAttempt: emptySyncHandshakeAttempt(),
2528
+ handles: new Map
2478
2529
  };
2479
2530
  this.slots.set(fromPeerId, slot);
2480
2531
  this.wireConnection(fromPeerId, connection);
@@ -2638,6 +2689,7 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
2638
2689
  }
2639
2690
  scheduleEmitMessage(fromPeerId, message, viaFragmentPath) {
2640
2691
  this.stampFirstInboundMessage(fromPeerId);
2692
+ this.stampHandleInbound(fromPeerId, message);
2641
2693
  if (!this.syncYieldEnabled) {
2642
2694
  this.emit("message", message);
2643
2695
  if (viaFragmentPath) {
@@ -2669,6 +2721,20 @@ class MeshWebRTCAdapter extends NetworkAdapter2 {
2669
2721
  return;
2670
2722
  slot.lastSyncHandshakeAttempt.firstInboundMessageAt = performance.now();
2671
2723
  }
2724
+ stampHandleInbound(fromPeerId, message) {
2725
+ const documentId = message.documentId;
2726
+ if (typeof documentId !== "string")
2727
+ return;
2728
+ const slot = this.slots.get(fromPeerId);
2729
+ if (!slot)
2730
+ return;
2731
+ let entry = slot.handles.get(documentId);
2732
+ if (!entry) {
2733
+ entry = emptyHandleSyncSnapshot();
2734
+ slot.handles.set(documentId, entry);
2735
+ }
2736
+ entry.lastSyncMessageInAt = performance.now();
2737
+ }
2672
2738
  finishInFlightSyncApply(fromPeerId) {
2673
2739
  const slot = this.slots.get(fromPeerId);
2674
2740
  if (!slot?.inFlightSync)
@@ -2810,6 +2876,54 @@ async function resolveIceServers(rtc) {
2810
2876
  }
2811
2877
  return rtc?.iceServers;
2812
2878
  }
2879
+ function buildHandleEntry(state, wire) {
2880
+ return {
2881
+ state,
2882
+ announcedToPeer: wire?.lastSyncMessageOutAt !== undefined,
2883
+ lastSyncMessageOutAt: wire?.lastSyncMessageOutAt,
2884
+ lastSyncMessageInAt: wire?.lastSyncMessageInAt,
2885
+ lastSyncMessageOutSize: wire?.lastSyncMessageOutSize,
2886
+ lastSyncMessageOutType: wire?.lastSyncMessageOutType
2887
+ };
2888
+ }
2889
+ function stringifyHandleState(handle) {
2890
+ if (handle === undefined)
2891
+ return "unknown";
2892
+ return typeof handle.state === "string" ? handle.state : String(handle.state ?? "unknown");
2893
+ }
2894
+ function enrichPeerSlot(peer, knownHandleIds, repoHandles) {
2895
+ if (!peer.slot) {
2896
+ return { ...peer, slot: undefined };
2897
+ }
2898
+ const enriched = {};
2899
+ for (const docId of knownHandleIds) {
2900
+ enriched[docId] = buildHandleEntry(stringifyHandleState(repoHandles[docId]), peer.slot.handles[docId]);
2901
+ }
2902
+ for (const docId of Object.keys(peer.slot.handles)) {
2903
+ if (enriched[docId])
2904
+ continue;
2905
+ enriched[docId] = buildHandleEntry("unknown", peer.slot.handles[docId]);
2906
+ }
2907
+ return { ...peer, slot: { ...peer.slot, handles: enriched } };
2908
+ }
2909
+ function getReevaluateDocumentShare(repo) {
2910
+ const sync = repo.synchronizer;
2911
+ const fn = sync?.reevaluateDocumentShare;
2912
+ if (typeof fn !== "function" || sync === undefined)
2913
+ return;
2914
+ return () => fn.call(sync);
2915
+ }
2916
+ function installPolly107SyncReevaluation(networkAdapter, repo) {
2917
+ const disable = typeof process !== "undefined" && process.env?.["POLLY_107_DISABLE_FIX"] === "1";
2918
+ if (disable)
2919
+ return;
2920
+ const reevaluate = getReevaluateDocumentShare(repo);
2921
+ if (!reevaluate)
2922
+ return;
2923
+ networkAdapter.on("peer-candidate", () => {
2924
+ reevaluate().catch(() => {});
2925
+ });
2926
+ }
2813
2927
  async function createMeshClient(options) {
2814
2928
  const keyringSource = await resolveKeyringSource(options.keyring);
2815
2929
  const keyring = keyringSource();
@@ -2882,6 +2996,7 @@ async function createMeshClient(options) {
2882
2996
  ...options.repoStorage !== undefined && { storage: options.repoStorage }
2883
2997
  });
2884
2998
  configureMeshState(repo);
2999
+ installPolly107SyncReevaluation(networkAdapter, repo);
2885
3000
  await signaling.connect();
2886
3001
  return {
2887
3002
  repo,
@@ -2906,10 +3021,43 @@ async function createMeshClient(options) {
2906
3021
  runCount: 0,
2907
3022
  lastRunAt: undefined
2908
3023
  },
2909
- peers: []
3024
+ peers: [],
3025
+ meshStateModule: {
3026
+ moduleId: getMeshStateModuleId(),
3027
+ configured: isMeshStateConfigured(),
3028
+ lastConfiguredRepoPeerId: getLastConfiguredRepoPeerId(),
3029
+ wasResolved: wasMeshStateResolved()
3030
+ },
3031
+ repoHandleCount: Object.keys(repo.handles).length,
3032
+ repoHandleIds: Object.keys(repo.handles)
2910
3033
  };
2911
3034
  }
2912
- return webrtcAdapter.getPeerStateSnapshot();
3035
+ const base = webrtcAdapter.getPeerStateSnapshot();
3036
+ const repoHandles = repo.handles;
3037
+ const knownHandleIds = Object.keys(repoHandles);
3038
+ const enrichedPeers = base.peers.map((peer) => enrichPeerSlot(peer, knownHandleIds, repoHandles));
3039
+ const out = {
3040
+ localPeerId: base.localPeerId,
3041
+ knownPeerIds: base.knownPeerIds,
3042
+ presentPeerIds: base.presentPeerIds,
3043
+ sweep: base.sweep,
3044
+ peers: enrichedPeers,
3045
+ meshStateModule: {
3046
+ moduleId: getMeshStateModuleId(),
3047
+ configured: isMeshStateConfigured(),
3048
+ lastConfiguredRepoPeerId: getLastConfiguredRepoPeerId(),
3049
+ wasResolved: wasMeshStateResolved()
3050
+ },
3051
+ repoHandleCount: knownHandleIds.length,
3052
+ repoHandleIds: knownHandleIds
3053
+ };
3054
+ return out;
3055
+ },
3056
+ reevaluateAllSync: async () => {
3057
+ const reevaluate = getReevaluateDocumentShare(repo);
3058
+ if (!reevaluate)
3059
+ return;
3060
+ await reevaluate();
2913
3061
  },
2914
3062
  refreshTransportStats: async () => {
2915
3063
  if (!webrtcAdapter)
@@ -3271,6 +3419,7 @@ function decodeRevocation(bytes, keyring) {
3271
3419
  return record;
3272
3420
  }
3273
3421
  export {
3422
+ wasMeshStateResolved,
3274
3423
  verify,
3275
3424
  signingKeyPairFromSecret,
3276
3425
  sign,
@@ -3281,7 +3430,10 @@ export {
3281
3430
  parsePairingToken,
3282
3431
  memoryKeyringStorage,
3283
3432
  isPairingTokenExpired,
3433
+ isMeshStateConfigured,
3284
3434
  isBlobRef,
3435
+ getMeshStateModuleId,
3436
+ getLastConfiguredRepoPeerId,
3285
3437
  generateSigningKeyPair,
3286
3438
  generateDocumentKey,
3287
3439
  encrypt,
@@ -3316,6 +3468,7 @@ export {
3316
3468
  MeshSignalingClient,
3317
3469
  MeshNetworkAdapter,
3318
3470
  MemoryBlobCache,
3471
+ MESH_STATE_MODULE_ID,
3319
3472
  IndexedDBBlobCache,
3320
3473
  EncryptionError,
3321
3474
  TAG_BYTES as ENCRYPTION_TAG_BYTES,
@@ -3330,4 +3483,4 @@ export {
3330
3483
  $meshCounter
3331
3484
  };
3332
3485
 
3333
- //# debugId=27D9577FC215E14564756E2164756E21
3486
+ //# debugId=23078FA0DB4C154064756E2164756E21