@fairfox/polly 0.53.0 → 0.55.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.
|
@@ -75,6 +75,21 @@ export interface CreateMeshClientOptions {
|
|
|
75
75
|
* the credential window closes.
|
|
76
76
|
*/
|
|
77
77
|
iceCredentialResolver?: () => Promise<RTCIceServer[]>;
|
|
78
|
+
/** Forward of {@link MeshWebRTCAdapterOptions.iceTransportPolicy}.
|
|
79
|
+
* Set to `"relay"` to force every candidate pair through TURN; the
|
|
80
|
+
* default leaves the underlying {@link RTCPeerConnection}
|
|
81
|
+
* implementation's default in place. The
|
|
82
|
+
* `examples/mesh-large-initial-sync-turn` harness uses `"relay"` to
|
|
83
|
+
* exercise the polly#105 real-transport contract. */
|
|
84
|
+
iceTransportPolicy?: MeshWebRTCAdapterOptions["iceTransportPolicy"];
|
|
85
|
+
/** Forward of {@link MeshWebRTCAdapterOptions.iceRelayEnforcement}.
|
|
86
|
+
* Defaults to `true`. Set to `false` to bypass the polly#105
|
|
87
|
+
* relay-only enforcement layer; the
|
|
88
|
+
* `examples/mesh-large-initial-sync-turn` falsification path
|
|
89
|
+
* (`POLLY_105_DISABLE_TURN_FIX=1`) does this to reproduce the
|
|
90
|
+
* pre-#105 candidate-leak shape. Production callers should leave
|
|
91
|
+
* this at the default. */
|
|
92
|
+
iceRelayEnforcement?: MeshWebRTCAdapterOptions["iceRelayEnforcement"];
|
|
78
93
|
dataChannelLabel?: string;
|
|
79
94
|
/** How often the mesh client re-evaluates whether to dial peers
|
|
80
95
|
* already present in the signalling roster against the live
|
|
@@ -89,6 +104,20 @@ export interface CreateMeshClientOptions {
|
|
|
89
104
|
* `examples/mesh-recovery-pair`). Forwarded straight to
|
|
90
105
|
* {@link MeshWebRTCAdapterOptions.knownPeersRefreshIntervalMs}. */
|
|
91
106
|
knownPeersRefreshIntervalMs?: MeshWebRTCAdapterOptions["knownPeersRefreshIntervalMs"];
|
|
107
|
+
/** Forward of {@link MeshWebRTCAdapterOptions.syncYieldEnabled}.
|
|
108
|
+
* Defaults to `true`. The `examples/mesh-large-initial-sync`
|
|
109
|
+
* example flips this to `false` when `POLLY_104_DISABLE_FIX=1` is
|
|
110
|
+
* set, to demonstrate the pre-#104 tight-loop behaviour against
|
|
111
|
+
* post-fix polly. Production callers should leave this at the
|
|
112
|
+
* default. */
|
|
113
|
+
syncYieldEnabled?: MeshWebRTCAdapterOptions["syncYieldEnabled"];
|
|
114
|
+
/** Forward of
|
|
115
|
+
* {@link MeshWebRTCAdapterOptions.syncFragmentChunkSizeOverride}.
|
|
116
|
+
* Production callers should leave this undefined. The
|
|
117
|
+
* `examples/mesh-large-initial-sync` example passes 64 KiB when
|
|
118
|
+
* `POLLY_104_DISABLE_FIX=1` to recreate the pre-#104
|
|
119
|
+
* fragmentation bug. */
|
|
120
|
+
syncFragmentChunkSizeOverride?: MeshWebRTCAdapterOptions["syncFragmentChunkSizeOverride"];
|
|
92
121
|
};
|
|
93
122
|
/** The local peer's keyring — one of three shapes:
|
|
94
123
|
*
|
|
@@ -172,6 +201,14 @@ export interface MeshClient {
|
|
|
172
201
|
* harness can answer "is the mesh layer in a known good state"
|
|
173
202
|
* without instrumenting polly internals. Polly issue #103 item 7. */
|
|
174
203
|
getPeerStateSnapshot(): ReturnType<MeshWebRTCAdapter["getPeerStateSnapshot"]>;
|
|
204
|
+
/** Refresh every active peer slot's transport-level summary —
|
|
205
|
+
* selected ICE candidate pair, SCTP retransmission counters, last
|
|
206
|
+
* data-channel error — and populate it into the next
|
|
207
|
+
* {@link getPeerStateSnapshot}. Walks {@link RTCPeerConnection.getStats}
|
|
208
|
+
* once per peer, so it isn't free; consumers that want continuous
|
|
209
|
+
* visibility should call this on a polling cadence the cost can
|
|
210
|
+
* absorb. Polly issue #105 item 7. */
|
|
211
|
+
refreshTransportStats(): Promise<void>;
|
|
175
212
|
/** Close the signalling WebSocket, tear down every RTCPeerConnection,
|
|
176
213
|
* and shut the Repo cleanly. Idempotent. */
|
|
177
214
|
close(): Promise<void>;
|
|
@@ -102,6 +102,27 @@ export interface MeshWebRTCAdapterOptions {
|
|
|
102
102
|
knownPeersRefreshIntervalMs?: number;
|
|
103
103
|
/** Optional ICE server list override. Defaults to {@link DEFAULT_ICE_SERVERS}. */
|
|
104
104
|
iceServers?: RTCIceServer[];
|
|
105
|
+
/** Optional ICE transport policy. Defaults to the
|
|
106
|
+
* {@link RTCPeerConnection} implementation's own default (`"all"` in
|
|
107
|
+
* Chrome, Firefox, and werift). Set to `"relay"` to force every
|
|
108
|
+
* candidate pair through a TURN relay — the shape the polly#105
|
|
109
|
+
* falsification harness needs to exercise the real-transport contract
|
|
110
|
+
* the polly#104 in-process throttle could not reach. */
|
|
111
|
+
iceTransportPolicy?: RTCIceTransportPolicy;
|
|
112
|
+
/** When `true` (the default), polly enforces
|
|
113
|
+
* {@link iceTransportPolicy} `"relay"` on its side of the signalling
|
|
114
|
+
* channel by filtering non-relay ICE candidates out of both the SDP
|
|
115
|
+
* description it forwards and the trickle ICE stream it emits. This
|
|
116
|
+
* closes the gap exposed by polly#105: Chrome's implementation
|
|
117
|
+
* already filters at the source, but werift only filters its own
|
|
118
|
+
* outgoing connectivity checks and still advertises host and srflx
|
|
119
|
+
* candidates upstream — so a relay-only peer on werift will, against
|
|
120
|
+
* a peer with the default policy, still pair through a non-relay
|
|
121
|
+
* candidate (or against a host-derived peer-reflexive remote).
|
|
122
|
+
* Setting this option to `false` reverts the enforcement and is the
|
|
123
|
+
* shape used by the `POLLY_105_DISABLE_TURN_FIX=1` falsification
|
|
124
|
+
* path. Production callers should leave this at the default. */
|
|
125
|
+
iceRelayEnforcement?: boolean;
|
|
105
126
|
/** Optional data channel label. Defaults to "polly-mesh". Applications
|
|
106
127
|
* that share a signalling server between multiple meshes may want
|
|
107
128
|
* distinct labels per mesh. */
|
|
@@ -111,6 +132,115 @@ export interface MeshWebRTCAdapterOptions {
|
|
|
111
132
|
* (e.g. `werift` or `@roamhq/wrtc`) when running outside a browser, or
|
|
112
133
|
* to use a custom subclass for tests or instrumentation. */
|
|
113
134
|
RTCPeerConnection?: typeof RTCPeerConnection;
|
|
135
|
+
/** When `true` (the default), the adapter yields to the event loop
|
|
136
|
+
* between the points where a large initial sync would otherwise hold
|
|
137
|
+
* the main thread for tens of seconds: between each batch of
|
|
138
|
+
* fragmented `RTCDataChannel.send` calls on the sender side, and at
|
|
139
|
+
* the boundary between reassembling a sync message and dispatching
|
|
140
|
+
* it (deserialise → MeshNetworkAdapter unwrap → Automerge
|
|
141
|
+
* `applyChanges`) on the receiver side. Set to `false` to recover
|
|
142
|
+
* the pre-#104 tight-loop behaviour; this is the configuration the
|
|
143
|
+
* `POLLY_104_DISABLE_FIX=1` falsification path in
|
|
144
|
+
* `examples/mesh-large-initial-sync` uses to demonstrate the bug
|
|
145
|
+
* against post-fix polly. Production callers should leave this at
|
|
146
|
+
* the default. */
|
|
147
|
+
syncYieldEnabled?: boolean;
|
|
148
|
+
/** Override the sync fragment chunk size. Defaults to
|
|
149
|
+
* {@link SYNC_FRAGMENT_CHUNK_SIZE} (60 KiB), which leaves header
|
|
150
|
+
* overhead inside werift's hard 64 KiB max-message-size cap.
|
|
151
|
+
* Setting this to 64 KiB recreates the pre-#104 fragmentation bug,
|
|
152
|
+
* where peer A's outbound fragments overshoot the cap and werift
|
|
153
|
+
* rejects them silently — sync stalls forever. Used by the
|
|
154
|
+
* `POLLY_104_DISABLE_FIX=1` falsification path. Production callers
|
|
155
|
+
* should leave this at the default. */
|
|
156
|
+
syncFragmentChunkSizeOverride?: number;
|
|
157
|
+
}
|
|
158
|
+
/** Payload of the polly-specific `"sync-progress"` event emitted by
|
|
159
|
+
* {@link MeshWebRTCAdapter}. Consumers can subscribe via the adapter's
|
|
160
|
+
* standard `.on()` surface (the same one that carries `peer-candidate`
|
|
161
|
+
* and `peer-disconnected`) to observe fragment receive and dispatch
|
|
162
|
+
* activity in real time, without polling
|
|
163
|
+
* {@link MeshWebRTCAdapter.getPeerStateSnapshot}. Polly issue #104
|
|
164
|
+
* item 7. */
|
|
165
|
+
export interface SyncProgressEvent {
|
|
166
|
+
/** Remote peer the fragment or dispatch is for. */
|
|
167
|
+
peerId: string;
|
|
168
|
+
/** Lifecycle stage. `fragment-received` fires for each chunk that
|
|
169
|
+
* arrives during reassembly; `dispatch-applied` fires once the
|
|
170
|
+
* reassembled message has been emitted upward to Automerge. */
|
|
171
|
+
kind: "fragment-received" | "dispatch-applied";
|
|
172
|
+
/** Bytes carried by the chunk that triggered the event. Zero for
|
|
173
|
+
* `dispatch-applied`. */
|
|
174
|
+
bytesDelta: number;
|
|
175
|
+
/** Running total of fragments received for the current reassembly. */
|
|
176
|
+
chunksReceived: number;
|
|
177
|
+
/** Running total of bytes received for the current reassembly. */
|
|
178
|
+
bytesReceived: number;
|
|
179
|
+
/** Number of reassembled messages whose dispatch has been scheduled
|
|
180
|
+
* but not yet emitted upward to Automerge. */
|
|
181
|
+
applyBacklog: number;
|
|
182
|
+
/** `performance.now()` at event emission. */
|
|
183
|
+
at: number;
|
|
184
|
+
}
|
|
185
|
+
/** Last-seen transport-level summary for a peer slot. Populated
|
|
186
|
+
* lazily by {@link MeshWebRTCAdapter.refreshTransportStats}, which
|
|
187
|
+
* the consumer calls (typically from a polling loop in a debugging
|
|
188
|
+
* harness) so the cost of {@link RTCPeerConnection.getStats} is
|
|
189
|
+
* incurred only on demand. Polly issue #105 item 7 — exposes the
|
|
190
|
+
* dimension of the transport that {@link InFlightSyncSnapshot}
|
|
191
|
+
* doesn't reach: the negotiated ICE candidate pair, the SCTP
|
|
192
|
+
* retransmission counters, and the last data-channel-level error if
|
|
193
|
+
* one has been observed.
|
|
194
|
+
*
|
|
195
|
+
* Field names mirror the W3C stats spec (`selectedCandidatePairId`,
|
|
196
|
+
* `localCandidateType`, etc.) so consumers can correlate with the
|
|
197
|
+
* raw `RTCStatsReport`. werift exposes a stats shape close to the
|
|
198
|
+
* spec; Chrome exposes the spec itself; this view is implementation-
|
|
199
|
+
* agnostic. */
|
|
200
|
+
export interface TransportSnapshot {
|
|
201
|
+
/** ICE-level summary of the pair currently carrying data. */
|
|
202
|
+
selectedCandidatePair: {
|
|
203
|
+
localCandidateType: string;
|
|
204
|
+
remoteCandidateType: string;
|
|
205
|
+
state: string;
|
|
206
|
+
nominated: boolean;
|
|
207
|
+
bytesSent: number;
|
|
208
|
+
bytesReceived: number;
|
|
209
|
+
} | undefined;
|
|
210
|
+
/** SCTP / data-channel retransmission counters. Some implementations
|
|
211
|
+
* surface these on the transport stat, others on the data-channel
|
|
212
|
+
* stat — we expose the values we found, leaving the field undefined
|
|
213
|
+
* when the underlying impl doesn't surface them. */
|
|
214
|
+
retransmittedPacketsSent: number | undefined;
|
|
215
|
+
retransmittedBytesSent: number | undefined;
|
|
216
|
+
/** Most-recent data-channel error message, if `error` ever fired on
|
|
217
|
+
* the channel for this peer. Cleared only when the slot is replaced. */
|
|
218
|
+
lastDataChannelError: string | undefined;
|
|
219
|
+
/** `performance.now()` at the time the stats were last refreshed. */
|
|
220
|
+
at: number;
|
|
221
|
+
}
|
|
222
|
+
/** Per-peer view of an in-flight initial sync. Populated by
|
|
223
|
+
* {@link MeshWebRTCAdapter.handleSyncFragment} as fragments of a
|
|
224
|
+
* single reassembly arrive, and reset to `undefined` once the
|
|
225
|
+
* reassembled message has been dispatched. Exposed verbatim through
|
|
226
|
+
* {@link MeshWebRTCAdapter.getPeerStateSnapshot} so a consumer
|
|
227
|
+
* harness can observe progress mid-stream. Polly issue #104 item 7. */
|
|
228
|
+
export interface InFlightSyncSnapshot {
|
|
229
|
+
/** Fragments received for the current reassembly. Cleared once
|
|
230
|
+
* reassembly completes. */
|
|
231
|
+
chunksReceived: number;
|
|
232
|
+
/** Bytes received across the fragments of the current reassembly.
|
|
233
|
+
* The reassembled message will be slightly smaller than this sum
|
|
234
|
+
* because each fragment carries a small header. */
|
|
235
|
+
bytesReceived: number;
|
|
236
|
+
/** `performance.now()` value at the last fragment arrival. */
|
|
237
|
+
lastChunkAt: number;
|
|
238
|
+
/** Count of reassembled messages whose dispatch has been
|
|
239
|
+
* scheduled but not yet run. With the receiver-side `setTimeout(0)`
|
|
240
|
+
* yield enabled, this is normally 0 or 1; with the yield
|
|
241
|
+
* disabled (the falsification path) dispatch runs synchronously
|
|
242
|
+
* and this stays 0. */
|
|
243
|
+
applyBacklog: number;
|
|
114
244
|
}
|
|
115
245
|
/**
|
|
116
246
|
* Automerge-Repo NetworkAdapter backed by real WebRTC data channels.
|
|
@@ -120,6 +250,8 @@ export interface MeshWebRTCAdapterOptions {
|
|
|
120
250
|
export declare class MeshWebRTCAdapter extends NetworkAdapter {
|
|
121
251
|
readonly signaling: MeshSignalingClient;
|
|
122
252
|
readonly iceServers: RTCIceServer[];
|
|
253
|
+
readonly iceTransportPolicy: RTCIceTransportPolicy | undefined;
|
|
254
|
+
private readonly iceRelayEnforcement;
|
|
123
255
|
readonly dataChannelLabel: string;
|
|
124
256
|
/** Peers this adapter is willing to dial. Mutable so callers that pair
|
|
125
257
|
* a new device after construction (e.g. a CLI `add-device` process whose
|
|
@@ -142,6 +274,17 @@ export declare class MeshWebRTCAdapter extends NetworkAdapter {
|
|
|
142
274
|
* {@link MeshWebRTCAdapterOptions.knownPeersRefreshIntervalMs} at
|
|
143
275
|
* construction. Defaults to 2000ms; tests override to 100–250ms. */
|
|
144
276
|
private readonly knownPeersRefreshIntervalMs;
|
|
277
|
+
/** When `true`, the sender side awaits between batches of fragment
|
|
278
|
+
* sends and the receiver side schedules dispatch via `setTimeout(0)`
|
|
279
|
+
* so the JS event loop can drain timers (including the consumer's
|
|
280
|
+
* own setInterval-based liveness probes) between large-message
|
|
281
|
+
* apply calls. Defaults to `true`; set to `false` only by the
|
|
282
|
+
* `POLLY_104_DISABLE_FIX` falsification path. */
|
|
283
|
+
private readonly syncYieldEnabled;
|
|
284
|
+
/** Resolved chunk size for fragmenting oversized messages.
|
|
285
|
+
* Defaults to {@link SYNC_FRAGMENT_CHUNK_SIZE}; can be overridden
|
|
286
|
+
* via {@link MeshWebRTCAdapterOptions.syncFragmentChunkSizeOverride}. */
|
|
287
|
+
private readonly syncFragmentChunkSize;
|
|
145
288
|
/** Peers currently visible in the signalling roster — populated by
|
|
146
289
|
* {@link handlePeersPresent} / {@link handlePeerJoined} and pruned by
|
|
147
290
|
* {@link handlePeerLeft}. Read by {@link addKnownPeer} to decide
|
|
@@ -216,6 +359,8 @@ export declare class MeshWebRTCAdapter extends NetworkAdapter {
|
|
|
216
359
|
dataChannelState: string;
|
|
217
360
|
pendingSendCount: number;
|
|
218
361
|
pendingRemoteIceCount: number;
|
|
362
|
+
inFlightSync: InFlightSyncSnapshot | undefined;
|
|
363
|
+
transport: TransportSnapshot | undefined;
|
|
219
364
|
};
|
|
220
365
|
}>;
|
|
221
366
|
};
|
|
@@ -293,13 +438,30 @@ export declare class MeshWebRTCAdapter extends NetworkAdapter {
|
|
|
293
438
|
* queued until the data channel is open.
|
|
294
439
|
*/
|
|
295
440
|
send(message: Message): void;
|
|
441
|
+
/** Number of consecutive fragment sends after which the sender
|
|
442
|
+
* yields to the macrotask queue when {@link syncYieldEnabled} is on.
|
|
443
|
+
* 8 × 64 KiB = 512 KiB of bytes between yields — small enough that
|
|
444
|
+
* a 5 MB sync produces many yield points (and the JS event loop
|
|
445
|
+
* drains the consumer's `setInterval` liveness probes between
|
|
446
|
+
* them), large enough that the per-yield overhead does not dominate
|
|
447
|
+
* the wire cost. */
|
|
448
|
+
private static readonly SEND_YIELD_EVERY_N_FRAGMENTS;
|
|
296
449
|
/** Send raw wire bytes, fragmenting if they exceed the SCTP maxMessageSize
|
|
297
450
|
* cap. The default RTCDataChannel limit is 256 KiB in current Chrome and
|
|
298
451
|
* werift; oversized sends either throw, drop silently, or stall the
|
|
299
452
|
* channel, none of which surface as an error to the caller. Fragments
|
|
300
453
|
* use the same length-prefixed JSON header wire format as ordinary
|
|
301
454
|
* messages but carry a `sync-fragment` type that the receive path
|
|
302
|
-
* detects and reassembles before deserialising.
|
|
455
|
+
* detects and reassembles before deserialising.
|
|
456
|
+
*
|
|
457
|
+
* When {@link MeshWebRTCAdapterOptions.syncYieldEnabled} is true (the
|
|
458
|
+
* default), the loop awaits the macrotask queue every
|
|
459
|
+
* {@link SEND_YIELD_EVERY_N_FRAGMENTS} fragments so the JS event
|
|
460
|
+
* loop drains between batches — without this, a 5–8 MB initial
|
|
461
|
+
* sync produces 78–125 back-to-back `RTCDataChannel.send` calls in
|
|
462
|
+
* a tight loop, starving anything else on the main thread (polly
|
|
463
|
+
* issue #104, sender side). When the option is false the legacy
|
|
464
|
+
* tight-loop shape is preserved for the falsification path. */
|
|
303
465
|
private sendBytesMaybeFragmented;
|
|
304
466
|
/**
|
|
305
467
|
* Entry point the signalling client calls when it receives a signal
|
|
@@ -310,6 +472,44 @@ export declare class MeshWebRTCAdapter extends NetworkAdapter {
|
|
|
310
472
|
* method.
|
|
311
473
|
*/
|
|
312
474
|
handleSignal(fromPeerId: string, rawPayload: unknown): void;
|
|
475
|
+
/** Assemble the {@link RTCConfiguration} every new
|
|
476
|
+
* {@link RTCPeerConnection} is built with. Centralised so every slot
|
|
477
|
+
* (initiator and answerer) honours the same iceTransportPolicy. */
|
|
478
|
+
private buildRtcConfiguration;
|
|
479
|
+
/** Decide whether a local ICE candidate should be relayed through
|
|
480
|
+
* the signalling channel to the remote peer. Chrome's iceTransport
|
|
481
|
+
* Policy = "relay" implementation filters non-relay candidates at
|
|
482
|
+
* the source so the remote peer never sees them; werift's only
|
|
483
|
+
* filters its own outgoing connectivity checks and still emits host
|
|
484
|
+
* and srflx candidates upstream, so a remote peer with policy "all"
|
|
485
|
+
* can pair against them — making relay-only enforcement leaky in
|
|
486
|
+
* the mixed-implementation case the polly#105 falsification harness
|
|
487
|
+
* exposes. Mirroring Chrome's filter at this layer gives a uniform
|
|
488
|
+
* contract on every RTCPeerConnection implementation polly supports.
|
|
489
|
+
*
|
|
490
|
+
* The candidate `type` field is the SDP-spec form; we additionally
|
|
491
|
+
* parse it out of the legacy `candidate` string for implementations
|
|
492
|
+
* that don't surface the typed field directly (werift exposes the
|
|
493
|
+
* SDP string only). */
|
|
494
|
+
private isRelayCandidateInit;
|
|
495
|
+
private shouldSendCandidate;
|
|
496
|
+
/** Strip non-relay `a=candidate:` lines from an SDP when
|
|
497
|
+
* iceTransportPolicy is `"relay"`. Some RTCPeerConnection
|
|
498
|
+
* implementations (werift, notably) embed every gathered candidate
|
|
499
|
+
* in the SDP regardless of policy, and a remote peer parsing those
|
|
500
|
+
* via `setRemoteDescription` will pair against them — bypassing the
|
|
501
|
+
* relay-only contract. Chrome already filters in-SDP candidates by
|
|
502
|
+
* policy, so this is only load-bearing for werift consumers, but
|
|
503
|
+
* the SDP rewrite is implementation-agnostic and idempotent. */
|
|
504
|
+
private filterSdpCandidatesByPolicy;
|
|
505
|
+
/** Apply {@link filterSdpCandidatesByPolicy} to an SDP
|
|
506
|
+
* description ahead of `setLocalDescription` (filter outgoing) or
|
|
507
|
+
* `setRemoteDescription` (filter incoming). The defensive
|
|
508
|
+
* receive-side filter exists because we cannot trust the remote
|
|
509
|
+
* peer's adapter to have stripped its non-relay candidates first —
|
|
510
|
+
* polly might be talking to a pre-#105 polly, or to a non-polly
|
|
511
|
+
* peer whose policy enforcement is different. */
|
|
512
|
+
private applySdpPolicyFilter;
|
|
313
513
|
private createInitiatingSlot;
|
|
314
514
|
private initiateOffer;
|
|
315
515
|
private handleOffer;
|
|
@@ -322,8 +522,47 @@ export declare class MeshWebRTCAdapter extends NetworkAdapter {
|
|
|
322
522
|
private flushPendingRemoteIce;
|
|
323
523
|
private wireConnection;
|
|
324
524
|
private wireDataChannel;
|
|
525
|
+
/** Refresh the per-peer {@link TransportSnapshot} for one peer by
|
|
526
|
+
* pulling {@link RTCPeerConnection.getStats} and distilling the
|
|
527
|
+
* result. Cheap-ish but not free — getStats walks the underlying
|
|
528
|
+
* stats graph — so this is opt-in: callers (typically a debugging
|
|
529
|
+
* harness or a periodic observability loop) invoke it explicitly
|
|
530
|
+
* and the result lives on the slot for the next
|
|
531
|
+
* {@link getPeerStateSnapshot}. Returns the refreshed snapshot, or
|
|
532
|
+
* `undefined` if there is no slot for the peer or stats are
|
|
533
|
+
* unavailable. Polly issue #105 item 7. */
|
|
534
|
+
refreshTransportStats(peerId: string): Promise<TransportSnapshot | undefined>;
|
|
535
|
+
/** Refresh transport stats for every active peer slot in one shot.
|
|
536
|
+
* Returns a map keyed by peerId so a caller can render the result
|
|
537
|
+
* without re-walking {@link getPeerStateSnapshot}. The underlying
|
|
538
|
+
* `getStats` calls run concurrently. */
|
|
539
|
+
refreshAllTransportStats(): Promise<Map<string, TransportSnapshot>>;
|
|
325
540
|
private dispatchMessage;
|
|
541
|
+
/** Hand a deserialised Automerge message off to whoever is listening
|
|
542
|
+
* on this adapter's `"message"` event. When
|
|
543
|
+
* {@link MeshWebRTCAdapterOptions.syncYieldEnabled} is true (the
|
|
544
|
+
* default), the emit runs on a fresh macrotask so the crypto-unwrap
|
|
545
|
+
* and Automerge `applyChanges` chain downstream of this method does
|
|
546
|
+
* not sit on the same JS stack frame as the wire `onmessage` callback
|
|
547
|
+
* — that's the receiver-side starvation site polly issue #104
|
|
548
|
+
* documents. When the option is false the emit is synchronous,
|
|
549
|
+
* recovering the pre-fix shape used by the falsification path.
|
|
550
|
+
*
|
|
551
|
+
* The `viaFragmentPath` argument tags whether this dispatch came out
|
|
552
|
+
* of a reassembled fragment chain; only those carry an
|
|
553
|
+
* `inFlightSync` reassembly state worth bookkeeping. Small
|
|
554
|
+
* single-message dispatches yield but don't touch inFlightSync. */
|
|
555
|
+
private scheduleEmitMessage;
|
|
556
|
+
private finishInFlightSyncApply;
|
|
557
|
+
private emitSyncProgress;
|
|
326
558
|
private handleSyncFragment;
|
|
559
|
+
/** Dispatch a reassembled fragment payload back through
|
|
560
|
+
* {@link dispatchMessage}, but tagged so the
|
|
561
|
+
* {@link scheduleEmitMessage} path knows it owes a
|
|
562
|
+
* `finishInFlightSyncApply` afterwards. Synchronous re-entry into
|
|
563
|
+
* `dispatchMessage` would lose that signal, so the post-fragment
|
|
564
|
+
* deserialise+emit is inlined here. */
|
|
565
|
+
private dispatchReassembled;
|
|
327
566
|
/** Peer IDs with an open data channel, suitable for blob requests. */
|
|
328
567
|
get connectedPeerIds(): string[];
|
|
329
568
|
/** Send a pre-serialised blob message to a specific peer. Returns false
|
|
@@ -17,12 +17,23 @@
|
|
|
17
17
|
* does not mistake a sync fragment for a blob chunk.
|
|
18
18
|
*/
|
|
19
19
|
/** Maximum bytes a single channel.send may carry without fragmentation.
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* Werift (the node-side WebRTC implementation polly recommends for
|
|
21
|
+
* CLI/daemon use) enforces a hard 64 KiB (65536 bytes) maxMessageSize
|
|
22
|
+
* on its RTCDataChannel — anything larger is rejected with a
|
|
23
|
+
* `max-message-size exceeded` error and silently drops the channel
|
|
24
|
+
* for that send. Chrome's SCTP cap is 256 KiB and would tolerate
|
|
25
|
+
* larger frames, but the threshold is chosen to fit inside werift's
|
|
26
|
+
* cap WITH per-fragment header overhead included so a single mesh
|
|
27
|
+
* deployment works on both transports. Matches the blob-transfer
|
|
28
|
+
* chunk size so the two transports have a consistent per-message
|
|
29
|
+
* footprint on the data channel. */
|
|
24
30
|
export declare const SYNC_FRAGMENT_THRESHOLD: number;
|
|
25
|
-
/** Chunk size used when a message exceeds the threshold.
|
|
31
|
+
/** Chunk size used when a message exceeds the threshold. Left at the
|
|
32
|
+
* same value as {@link SYNC_FRAGMENT_THRESHOLD} so the framing
|
|
33
|
+
* header (a JSON-encoded `SyncFragmentHeader` of ~90 bytes plus a
|
|
34
|
+
* 4-byte length prefix) does not push any fragment over werift's
|
|
35
|
+
* 64 KiB wire limit — see polly issue #104 for the failure mode
|
|
36
|
+
* this guards against. */
|
|
26
37
|
export declare const SYNC_FRAGMENT_CHUNK_SIZE: number;
|
|
27
38
|
export interface SyncFragmentHeader {
|
|
28
39
|
type: "sync-fragment";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fairfox/polly",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.55.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Multi-execution-context framework with reactive state and cross-context messaging for Chrome extensions, PWAs, and worker-based applications",
|