@fairfox/polly 0.54.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
|
|
@@ -186,6 +201,14 @@ export interface MeshClient {
|
|
|
186
201
|
* harness can answer "is the mesh layer in a known good state"
|
|
187
202
|
* without instrumenting polly internals. Polly issue #103 item 7. */
|
|
188
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>;
|
|
189
212
|
/** Close the signalling WebSocket, tear down every RTCPeerConnection,
|
|
190
213
|
* and shut the Repo cleanly. Idempotent. */
|
|
191
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. */
|
|
@@ -161,6 +182,43 @@ export interface SyncProgressEvent {
|
|
|
161
182
|
/** `performance.now()` at event emission. */
|
|
162
183
|
at: number;
|
|
163
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
|
+
}
|
|
164
222
|
/** Per-peer view of an in-flight initial sync. Populated by
|
|
165
223
|
* {@link MeshWebRTCAdapter.handleSyncFragment} as fragments of a
|
|
166
224
|
* single reassembly arrive, and reset to `undefined` once the
|
|
@@ -192,6 +250,8 @@ export interface InFlightSyncSnapshot {
|
|
|
192
250
|
export declare class MeshWebRTCAdapter extends NetworkAdapter {
|
|
193
251
|
readonly signaling: MeshSignalingClient;
|
|
194
252
|
readonly iceServers: RTCIceServer[];
|
|
253
|
+
readonly iceTransportPolicy: RTCIceTransportPolicy | undefined;
|
|
254
|
+
private readonly iceRelayEnforcement;
|
|
195
255
|
readonly dataChannelLabel: string;
|
|
196
256
|
/** Peers this adapter is willing to dial. Mutable so callers that pair
|
|
197
257
|
* a new device after construction (e.g. a CLI `add-device` process whose
|
|
@@ -300,6 +360,7 @@ export declare class MeshWebRTCAdapter extends NetworkAdapter {
|
|
|
300
360
|
pendingSendCount: number;
|
|
301
361
|
pendingRemoteIceCount: number;
|
|
302
362
|
inFlightSync: InFlightSyncSnapshot | undefined;
|
|
363
|
+
transport: TransportSnapshot | undefined;
|
|
303
364
|
};
|
|
304
365
|
}>;
|
|
305
366
|
};
|
|
@@ -411,6 +472,44 @@ export declare class MeshWebRTCAdapter extends NetworkAdapter {
|
|
|
411
472
|
* method.
|
|
412
473
|
*/
|
|
413
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;
|
|
414
513
|
private createInitiatingSlot;
|
|
415
514
|
private initiateOffer;
|
|
416
515
|
private handleOffer;
|
|
@@ -423,6 +522,21 @@ export declare class MeshWebRTCAdapter extends NetworkAdapter {
|
|
|
423
522
|
private flushPendingRemoteIce;
|
|
424
523
|
private wireConnection;
|
|
425
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>>;
|
|
426
540
|
private dispatchMessage;
|
|
427
541
|
/** Hand a deserialised Automerge message off to whoever is listening
|
|
428
542
|
* on this adapter's `"message"` event. When
|
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",
|