@afterrealism/dendri-client 2.3.7 → 2.4.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/LICENSE +202 -21
- package/README.md +61 -59
- package/dist/{chunk-MJW5M75V.js → chunk-MBMSG4EC.js} +200 -14
- package/dist/chunk-MBMSG4EC.js.map +1 -0
- package/dist/dendri.browser.global.js +166 -8
- package/dist/dendri.browser.global.js.map +1 -1
- package/dist/dendri.cjs +199 -12
- package/dist/dendri.cjs.map +1 -1
- package/dist/dendri.d.cts +2 -2
- package/dist/dendri.d.ts +2 -2
- package/dist/dendri.js +3 -3
- package/dist/dendri.js.map +1 -1
- package/dist/dendri.min.global.js +13 -13
- package/dist/dendri.min.global.js.map +1 -1
- package/dist/serializer.msgpack.cjs +3 -3
- package/dist/serializer.msgpack.cjs.map +1 -1
- package/dist/serializer.msgpack.d.cts +17 -4
- package/dist/serializer.msgpack.d.ts +17 -4
- package/dist/serializer.msgpack.js +2 -2
- package/dist/serializer.msgpack.js.map +1 -1
- package/dist/{store-RTivRmUW.d.cts → store-DVE0ih44.d.cts} +24 -5
- package/dist/{store-RTivRmUW.d.ts → store-DVE0ih44.d.ts} +24 -5
- package/dist/store.cjs +189 -11
- package/dist/store.cjs.map +1 -1
- package/dist/store.d.cts +1 -1
- package/dist/store.d.ts +1 -1
- package/dist/store.js +1 -1
- package/package.json +7 -2
- package/dist/chunk-MJW5M75V.js.map +0 -1
|
@@ -133,7 +133,10 @@ declare enum ServerMessageType {
|
|
|
133
133
|
RoomPeers = "ROOM-PEERS",// List of peers in a room.
|
|
134
134
|
HostMigrate = "HOST-MIGRATE",// Host migration notification.
|
|
135
135
|
PresenceUpdate = "PRESENCE-UPDATE",// Presence data broadcast.
|
|
136
|
-
KeyExchange = "KEY-EXCHANGE"
|
|
136
|
+
KeyExchange = "KEY-EXCHANGE",// E2E encryption key exchange for relay.
|
|
137
|
+
ConnectRequest = "CONNECT-REQUEST",// Address exchange for Connection Reversal (H1)
|
|
138
|
+
DcutrConnect = "DCUTR-CONNECT",// DCUtR hole punch address exchange (H2)
|
|
139
|
+
DcutrSync = "DCUTR-SYNC"
|
|
137
140
|
}
|
|
138
141
|
declare enum TransportMode {
|
|
139
142
|
WebRTC = "webrtc",
|
|
@@ -162,6 +165,12 @@ declare enum ConnectionQuality {
|
|
|
162
165
|
Poor = "poor",
|
|
163
166
|
Unknown = "unknown"
|
|
164
167
|
}
|
|
168
|
+
declare enum TopicClass {
|
|
169
|
+
/** Reliable, persisted, replayed — data integrity critical (Yjs updates, RPC) */
|
|
170
|
+
Persistent = "persistent",
|
|
171
|
+
/** Best-effort, not replayed — ephemeral UX state (cursor, scroll, typing indicators) */
|
|
172
|
+
Ephemeral = "ephemeral"
|
|
173
|
+
}
|
|
165
174
|
|
|
166
175
|
interface ServerMessage {
|
|
167
176
|
type: ServerMessageType;
|
|
@@ -262,6 +271,7 @@ declare abstract class DataConnection extends BaseConnection<DataConnectionEvent
|
|
|
262
271
|
constructor(peerId: string, provider: Dendri, options: any);
|
|
263
272
|
/** Called by the Negotiator when the DataChannel is ready. */
|
|
264
273
|
_initializeDataChannel(dc: RTCDataChannel): void;
|
|
274
|
+
private _applyAdaptiveBuffer;
|
|
265
275
|
/**
|
|
266
276
|
* Exposed functionality for users.
|
|
267
277
|
*/
|
|
@@ -325,7 +335,7 @@ interface DendriOption {
|
|
|
325
335
|
config?: RTCConfiguration;
|
|
326
336
|
debug?: number;
|
|
327
337
|
referrerPolicy?: ReferrerPolicy;
|
|
328
|
-
/** Auto-fetch TURN credentials from the signaling server's GET /turn endpoint. */
|
|
338
|
+
/** Auto-fetch TURN credentials from the signaling server's GET /{key}/turn-credentials endpoint. */
|
|
329
339
|
fetchTurnCredentials?: boolean;
|
|
330
340
|
/** Optional JWT for authenticated connections. */
|
|
331
341
|
jwt?: string;
|
|
@@ -515,6 +525,9 @@ declare class HybridConnection extends EventEmitter<HybridConnectionEvents> {
|
|
|
515
525
|
* Respects `autoUpgrade`, `upgradeInterval`, and `maxUpgradeAttempts` options.
|
|
516
526
|
*/
|
|
517
527
|
private _scheduleUpgrade;
|
|
528
|
+
private _tryConnectionReversal;
|
|
529
|
+
private _gatherSrflxCandidates;
|
|
530
|
+
_dcutrHolePunch(): Promise<boolean>;
|
|
518
531
|
}
|
|
519
532
|
|
|
520
533
|
declare enum LogLevel {
|
|
@@ -648,7 +661,7 @@ declare class DendriOptions implements DendriOption {
|
|
|
648
661
|
*
|
|
649
662
|
* Defaults to {@apilink util.defaultConfig}
|
|
650
663
|
*/
|
|
651
|
-
config?:
|
|
664
|
+
config?: RTCConfiguration;
|
|
652
665
|
/**
|
|
653
666
|
* Set to true `true` if you're using TLS.
|
|
654
667
|
* :::danger
|
|
@@ -658,7 +671,7 @@ declare class DendriOptions implements DendriOption {
|
|
|
658
671
|
secure?: boolean;
|
|
659
672
|
pingInterval?: number;
|
|
660
673
|
referrerPolicy?: ReferrerPolicy;
|
|
661
|
-
logFunction?: (logLevel: LogLevel, ...rest:
|
|
674
|
+
logFunction?: (logLevel: LogLevel, ...rest: unknown[]) => void;
|
|
662
675
|
serializers?: SerializerMapping;
|
|
663
676
|
/** Auto-fetch TURN credentials from the signaling server's GET /turn endpoint. */
|
|
664
677
|
fetchTurnCredentials?: boolean;
|
|
@@ -670,6 +683,12 @@ declare class DendriOptions implements DendriOption {
|
|
|
670
683
|
validateMetadata?: (metadata: unknown) => boolean;
|
|
671
684
|
/** Signaling transport: 'websocket' (default), 'sse', 'polling', or 'auto' (tries WS then SSE then polling) */
|
|
672
685
|
signalingTransport?: "websocket" | "sse" | "polling" | "auto";
|
|
686
|
+
/**
|
|
687
|
+
* ICE candidate privacy policy.
|
|
688
|
+
* - 'all': RFC 8828 mode 1 — all candidates including host IPs (default)
|
|
689
|
+
* - 'public': RFC 8828 mode 3 — only STUN/TURN (srflx + relay) candidates
|
|
690
|
+
*/
|
|
691
|
+
ipPolicy?: "all" | "public";
|
|
673
692
|
}
|
|
674
693
|
|
|
675
694
|
interface SerializerMapping {
|
|
@@ -1375,4 +1394,4 @@ interface DendriStore {
|
|
|
1375
1394
|
*/
|
|
1376
1395
|
declare function createDendriStore(input?: CreateDendriStoreInput): DendriStore;
|
|
1377
1396
|
|
|
1378
|
-
export { AckManager as A, BaseConnectionErrorType as B, type CallOption as C, Dendri as D, type RpcResponse as E, SerializationType as F, ServerMessageType as G, HybridConnection as H, SocketEventType as I, type StoreListener as J,
|
|
1397
|
+
export { AckManager as A, BaseConnectionErrorType as B, type CallOption as C, Dendri as D, type RpcResponse as E, SerializationType as F, ServerMessageType as G, HybridConnection as H, SocketEventType as I, type StoreListener as J, type TransportEvents as K, LogLevel as L, MediaConnection as M, TransportMode as N, createDendriStore as O, type PresenceEvents as P, isRpcRequest as Q, Room as R, type SerializerMapping as S, TopicClass as T, isRpcResponse as U, type CreateDendriStoreInput as V, DataConnection as a, SignalingTransport as b, type AnswerOption as c, ConnectionQuality as d, ConnectionState as e, ConnectionType as f, DataConnectionErrorType as g, type DendriConnectOption as h, DendriError as i, DendriErrorType as j, type DendriEvents as k, type DendriOption as l, DendriOptions as m, type DendriStore as n, type DendriStoreOptions as o, type DendriStoreSnapshot as p, type HybridConnectionOption as q, type HybridSendOptions as r, PresenceManager as s, type RoomEvents as t, type RoomOptions as u, RpcError as v, RpcErrorCode as w, type RpcHandler as x, RpcManager as y, type RpcRequest as z };
|
|
@@ -133,7 +133,10 @@ declare enum ServerMessageType {
|
|
|
133
133
|
RoomPeers = "ROOM-PEERS",// List of peers in a room.
|
|
134
134
|
HostMigrate = "HOST-MIGRATE",// Host migration notification.
|
|
135
135
|
PresenceUpdate = "PRESENCE-UPDATE",// Presence data broadcast.
|
|
136
|
-
KeyExchange = "KEY-EXCHANGE"
|
|
136
|
+
KeyExchange = "KEY-EXCHANGE",// E2E encryption key exchange for relay.
|
|
137
|
+
ConnectRequest = "CONNECT-REQUEST",// Address exchange for Connection Reversal (H1)
|
|
138
|
+
DcutrConnect = "DCUTR-CONNECT",// DCUtR hole punch address exchange (H2)
|
|
139
|
+
DcutrSync = "DCUTR-SYNC"
|
|
137
140
|
}
|
|
138
141
|
declare enum TransportMode {
|
|
139
142
|
WebRTC = "webrtc",
|
|
@@ -162,6 +165,12 @@ declare enum ConnectionQuality {
|
|
|
162
165
|
Poor = "poor",
|
|
163
166
|
Unknown = "unknown"
|
|
164
167
|
}
|
|
168
|
+
declare enum TopicClass {
|
|
169
|
+
/** Reliable, persisted, replayed — data integrity critical (Yjs updates, RPC) */
|
|
170
|
+
Persistent = "persistent",
|
|
171
|
+
/** Best-effort, not replayed — ephemeral UX state (cursor, scroll, typing indicators) */
|
|
172
|
+
Ephemeral = "ephemeral"
|
|
173
|
+
}
|
|
165
174
|
|
|
166
175
|
interface ServerMessage {
|
|
167
176
|
type: ServerMessageType;
|
|
@@ -262,6 +271,7 @@ declare abstract class DataConnection extends BaseConnection<DataConnectionEvent
|
|
|
262
271
|
constructor(peerId: string, provider: Dendri, options: any);
|
|
263
272
|
/** Called by the Negotiator when the DataChannel is ready. */
|
|
264
273
|
_initializeDataChannel(dc: RTCDataChannel): void;
|
|
274
|
+
private _applyAdaptiveBuffer;
|
|
265
275
|
/**
|
|
266
276
|
* Exposed functionality for users.
|
|
267
277
|
*/
|
|
@@ -325,7 +335,7 @@ interface DendriOption {
|
|
|
325
335
|
config?: RTCConfiguration;
|
|
326
336
|
debug?: number;
|
|
327
337
|
referrerPolicy?: ReferrerPolicy;
|
|
328
|
-
/** Auto-fetch TURN credentials from the signaling server's GET /turn endpoint. */
|
|
338
|
+
/** Auto-fetch TURN credentials from the signaling server's GET /{key}/turn-credentials endpoint. */
|
|
329
339
|
fetchTurnCredentials?: boolean;
|
|
330
340
|
/** Optional JWT for authenticated connections. */
|
|
331
341
|
jwt?: string;
|
|
@@ -515,6 +525,9 @@ declare class HybridConnection extends EventEmitter<HybridConnectionEvents> {
|
|
|
515
525
|
* Respects `autoUpgrade`, `upgradeInterval`, and `maxUpgradeAttempts` options.
|
|
516
526
|
*/
|
|
517
527
|
private _scheduleUpgrade;
|
|
528
|
+
private _tryConnectionReversal;
|
|
529
|
+
private _gatherSrflxCandidates;
|
|
530
|
+
_dcutrHolePunch(): Promise<boolean>;
|
|
518
531
|
}
|
|
519
532
|
|
|
520
533
|
declare enum LogLevel {
|
|
@@ -648,7 +661,7 @@ declare class DendriOptions implements DendriOption {
|
|
|
648
661
|
*
|
|
649
662
|
* Defaults to {@apilink util.defaultConfig}
|
|
650
663
|
*/
|
|
651
|
-
config?:
|
|
664
|
+
config?: RTCConfiguration;
|
|
652
665
|
/**
|
|
653
666
|
* Set to true `true` if you're using TLS.
|
|
654
667
|
* :::danger
|
|
@@ -658,7 +671,7 @@ declare class DendriOptions implements DendriOption {
|
|
|
658
671
|
secure?: boolean;
|
|
659
672
|
pingInterval?: number;
|
|
660
673
|
referrerPolicy?: ReferrerPolicy;
|
|
661
|
-
logFunction?: (logLevel: LogLevel, ...rest:
|
|
674
|
+
logFunction?: (logLevel: LogLevel, ...rest: unknown[]) => void;
|
|
662
675
|
serializers?: SerializerMapping;
|
|
663
676
|
/** Auto-fetch TURN credentials from the signaling server's GET /turn endpoint. */
|
|
664
677
|
fetchTurnCredentials?: boolean;
|
|
@@ -670,6 +683,12 @@ declare class DendriOptions implements DendriOption {
|
|
|
670
683
|
validateMetadata?: (metadata: unknown) => boolean;
|
|
671
684
|
/** Signaling transport: 'websocket' (default), 'sse', 'polling', or 'auto' (tries WS then SSE then polling) */
|
|
672
685
|
signalingTransport?: "websocket" | "sse" | "polling" | "auto";
|
|
686
|
+
/**
|
|
687
|
+
* ICE candidate privacy policy.
|
|
688
|
+
* - 'all': RFC 8828 mode 1 — all candidates including host IPs (default)
|
|
689
|
+
* - 'public': RFC 8828 mode 3 — only STUN/TURN (srflx + relay) candidates
|
|
690
|
+
*/
|
|
691
|
+
ipPolicy?: "all" | "public";
|
|
673
692
|
}
|
|
674
693
|
|
|
675
694
|
interface SerializerMapping {
|
|
@@ -1375,4 +1394,4 @@ interface DendriStore {
|
|
|
1375
1394
|
*/
|
|
1376
1395
|
declare function createDendriStore(input?: CreateDendriStoreInput): DendriStore;
|
|
1377
1396
|
|
|
1378
|
-
export { AckManager as A, BaseConnectionErrorType as B, type CallOption as C, Dendri as D, type RpcResponse as E, SerializationType as F, ServerMessageType as G, HybridConnection as H, SocketEventType as I, type StoreListener as J,
|
|
1397
|
+
export { AckManager as A, BaseConnectionErrorType as B, type CallOption as C, Dendri as D, type RpcResponse as E, SerializationType as F, ServerMessageType as G, HybridConnection as H, SocketEventType as I, type StoreListener as J, type TransportEvents as K, LogLevel as L, MediaConnection as M, TransportMode as N, createDendriStore as O, type PresenceEvents as P, isRpcRequest as Q, Room as R, type SerializerMapping as S, TopicClass as T, isRpcResponse as U, type CreateDendriStoreInput as V, DataConnection as a, SignalingTransport as b, type AnswerOption as c, ConnectionQuality as d, ConnectionState as e, ConnectionType as f, DataConnectionErrorType as g, type DendriConnectOption as h, DendriError as i, DendriErrorType as j, type DendriEvents as k, type DendriOption as l, DendriOptions as m, type DendriStore as n, type DendriStoreOptions as o, type DendriStoreSnapshot as p, type HybridConnectionOption as q, type HybridSendOptions as r, PresenceManager as s, type RoomEvents as t, type RoomOptions as u, RpcError as v, RpcErrorCode as w, type RpcHandler as x, RpcManager as y, type RpcRequest as z };
|
package/dist/store.cjs
CHANGED
|
@@ -3531,7 +3531,7 @@ var Util = class extends BinaryPackChunker {
|
|
|
3531
3531
|
var util = new Util();
|
|
3532
3532
|
|
|
3533
3533
|
// src/api.ts
|
|
3534
|
-
var version = "2.
|
|
3534
|
+
var version = "2.4.0";
|
|
3535
3535
|
var API = class _API {
|
|
3536
3536
|
constructor(_options) {
|
|
3537
3537
|
this._options = _options;
|
|
@@ -3568,11 +3568,11 @@ var API = class _API {
|
|
|
3568
3568
|
throw new Error(`Could not get an ID from the server.${pathError}`);
|
|
3569
3569
|
}
|
|
3570
3570
|
}
|
|
3571
|
-
/** Fetch TURN credentials from the signaling server's
|
|
3571
|
+
/** Fetch TURN credentials from the signaling server's turn-credentials endpoint. */
|
|
3572
3572
|
async getTurnCredentials() {
|
|
3573
3573
|
const protocol = this._options.secure ? "https" : "http";
|
|
3574
|
-
const { host, port } = this._options;
|
|
3575
|
-
const url = `${protocol}://${host}:${port}/turn`;
|
|
3574
|
+
const { host, port, path, key } = this._options;
|
|
3575
|
+
const url = `${protocol}://${host}:${port}${path}${key}/turn-credentials`;
|
|
3576
3576
|
try {
|
|
3577
3577
|
const controller = new AbortController();
|
|
3578
3578
|
const timeoutId = setTimeout(() => controller.abort(), _API.FETCH_TIMEOUT);
|
|
@@ -3701,6 +3701,7 @@ var Negotiator = class {
|
|
|
3701
3701
|
}
|
|
3702
3702
|
connection;
|
|
3703
3703
|
_pendingCandidates = [];
|
|
3704
|
+
_iceCandidateFilter = null;
|
|
3704
3705
|
/** Returns a PeerConnection object set up correctly (for data, media). */
|
|
3705
3706
|
startConnection(options) {
|
|
3706
3707
|
const peerConnection = this._startPeerConnection();
|
|
@@ -3723,6 +3724,13 @@ var Negotiator = class {
|
|
|
3723
3724
|
_startPeerConnection() {
|
|
3724
3725
|
logger_default.log("Creating RTCPeerConnection.");
|
|
3725
3726
|
const peerConnection = new RTCPeerConnection(this.connection.provider?.options.config);
|
|
3727
|
+
if (this.connection.provider?.options.ipPolicy === "public") {
|
|
3728
|
+
const isPublicCandidate = (c) => {
|
|
3729
|
+
const sdp2 = c.candidate ?? "";
|
|
3730
|
+
return !sdp2.includes("typ host");
|
|
3731
|
+
};
|
|
3732
|
+
this._iceCandidateFilter = isPublicCandidate;
|
|
3733
|
+
}
|
|
3726
3734
|
this._setupListeners(peerConnection);
|
|
3727
3735
|
return peerConnection;
|
|
3728
3736
|
}
|
|
@@ -3735,6 +3743,9 @@ var Negotiator = class {
|
|
|
3735
3743
|
logger_default.log("Listening for ICE candidates.");
|
|
3736
3744
|
peerConnection.onicecandidate = (evt) => {
|
|
3737
3745
|
if (!evt.candidate?.candidate) return;
|
|
3746
|
+
if (this._iceCandidateFilter && !this._iceCandidateFilter(evt.candidate)) {
|
|
3747
|
+
return;
|
|
3748
|
+
}
|
|
3738
3749
|
logger_default.log(`Received ICE candidates for ${peerId}:`, evt.candidate);
|
|
3739
3750
|
provider.socket.send({
|
|
3740
3751
|
type: "CANDIDATE" /* Candidate */,
|
|
@@ -4073,6 +4084,7 @@ var DataConnection = class _DataConnection extends BaseConnection {
|
|
|
4073
4084
|
this.dataChannel.onopen = () => {
|
|
4074
4085
|
logger_default.log(`DC#${this.connectionId} dc connection success`);
|
|
4075
4086
|
this._open = true;
|
|
4087
|
+
this._applyAdaptiveBuffer(dc);
|
|
4076
4088
|
this.emit("open");
|
|
4077
4089
|
};
|
|
4078
4090
|
this.dataChannel.onclose = () => {
|
|
@@ -4080,6 +4092,24 @@ var DataConnection = class _DataConnection extends BaseConnection {
|
|
|
4080
4092
|
this.close();
|
|
4081
4093
|
};
|
|
4082
4094
|
}
|
|
4095
|
+
_applyAdaptiveBuffer(dc) {
|
|
4096
|
+
const pc = this.peerConnection;
|
|
4097
|
+
if (!pc || typeof pc.getStats !== "function") return;
|
|
4098
|
+
pc.getStats().then((stats) => {
|
|
4099
|
+
let rtt = null;
|
|
4100
|
+
stats.forEach((report) => {
|
|
4101
|
+
if (report.type === "candidate-pair" && report.state === "succeeded" && report.currentRoundTripTime) {
|
|
4102
|
+
rtt = report.currentRoundTripTime * 1e3;
|
|
4103
|
+
}
|
|
4104
|
+
});
|
|
4105
|
+
if (rtt !== null) {
|
|
4106
|
+
const bdp = 12.5 * 1024 * 1024 * (rtt / 1e3);
|
|
4107
|
+
const optimal = Math.max(1 * 1024 * 1024, Math.min(32 * 1024 * 1024, Math.ceil(bdp)));
|
|
4108
|
+
dc.bufferedAmountLowThreshold = optimal;
|
|
4109
|
+
}
|
|
4110
|
+
}).catch(() => {
|
|
4111
|
+
});
|
|
4112
|
+
}
|
|
4083
4113
|
/**
|
|
4084
4114
|
* Exposed functionality for users.
|
|
4085
4115
|
*/
|
|
@@ -4614,7 +4644,17 @@ var HybridConnection = class extends import_eventemitter32.EventEmitter {
|
|
|
4614
4644
|
if (this._closed) {
|
|
4615
4645
|
return;
|
|
4616
4646
|
}
|
|
4617
|
-
|
|
4647
|
+
logger_default.log(
|
|
4648
|
+
`HybridConnection: start peer=${this.peer} iceTimeout=${this._options.iceTimeout ?? 1e4}ms encryptRelay=${this._encryptRelay}`
|
|
4649
|
+
);
|
|
4650
|
+
if (typeof this._provider?.on !== "function") {
|
|
4651
|
+
this._attemptWebRTC();
|
|
4652
|
+
return;
|
|
4653
|
+
}
|
|
4654
|
+
this._tryConnectionReversal().then((direct) => {
|
|
4655
|
+
if (direct) return;
|
|
4656
|
+
this._attemptWebRTC();
|
|
4657
|
+
});
|
|
4618
4658
|
}
|
|
4619
4659
|
/** Send data through the best available transport, optionally tagged with a topic. */
|
|
4620
4660
|
send(data, options) {
|
|
@@ -4904,10 +4944,16 @@ var HybridConnection = class extends import_eventemitter32.EventEmitter {
|
|
|
4904
4944
|
this._dataConnection = dc;
|
|
4905
4945
|
this._iceTimer = setTimeout(() => {
|
|
4906
4946
|
if (this._mode !== "webrtc" /* WebRTC */) {
|
|
4947
|
+
logger_default.warn(
|
|
4948
|
+
`HybridConnection: ICE timeout after ${iceTimeout}ms for ${this.peer}, falling back to relay`
|
|
4949
|
+
);
|
|
4907
4950
|
this._fallbackToRelay();
|
|
4908
4951
|
}
|
|
4909
4952
|
}, iceTimeout);
|
|
4910
4953
|
this._dataConnection.on("open", () => {
|
|
4954
|
+
logger_default.log(
|
|
4955
|
+
`HybridConnection: WebRTC opened to ${this.peer} (attempt ${this._upgradeAttempts + 1})`
|
|
4956
|
+
);
|
|
4911
4957
|
this._clearIceTimer();
|
|
4912
4958
|
this._clearUpgradeTimer();
|
|
4913
4959
|
this._upgradeAttempts = 0;
|
|
@@ -4948,6 +4994,7 @@ var HybridConnection = class extends import_eventemitter32.EventEmitter {
|
|
|
4948
4994
|
/** Update the transport mode and emit if changed. */
|
|
4949
4995
|
_setMode(mode) {
|
|
4950
4996
|
if (this._mode !== mode) {
|
|
4997
|
+
logger_default.log(`HybridConnection: transport ${this._mode} -> ${mode} for ${this.peer}`);
|
|
4951
4998
|
this._mode = mode;
|
|
4952
4999
|
this.emit("transportChanged", mode);
|
|
4953
5000
|
}
|
|
@@ -4988,6 +5035,117 @@ var HybridConnection = class extends import_eventemitter32.EventEmitter {
|
|
|
4988
5035
|
this._attemptWebRTC();
|
|
4989
5036
|
}, interval);
|
|
4990
5037
|
}
|
|
5038
|
+
async _tryConnectionReversal() {
|
|
5039
|
+
if (typeof this._provider?.on !== "function") return false;
|
|
5040
|
+
try {
|
|
5041
|
+
const resp = await new Promise((resolve, reject) => {
|
|
5042
|
+
const timer = setTimeout(() => reject(new Error("timeout")), 3e3);
|
|
5043
|
+
const handler = (data) => {
|
|
5044
|
+
clearTimeout(timer);
|
|
5045
|
+
this._provider.off("CONNECT-REQUEST" /* ConnectRequest */, handler);
|
|
5046
|
+
resolve(data);
|
|
5047
|
+
};
|
|
5048
|
+
this._provider.on("CONNECT-REQUEST" /* ConnectRequest */, handler);
|
|
5049
|
+
this._provider.socket.send({
|
|
5050
|
+
type: "CONNECT-REQUEST" /* ConnectRequest */,
|
|
5051
|
+
payload: { peer: this.peer }
|
|
5052
|
+
});
|
|
5053
|
+
});
|
|
5054
|
+
const addr = resp?.address;
|
|
5055
|
+
if (!addr) return false;
|
|
5056
|
+
const pc = new RTCPeerConnection(this._provider.options.config);
|
|
5057
|
+
const dc = pc.createDataChannel("probe", { id: 0 });
|
|
5058
|
+
await new Promise((resolve, reject) => {
|
|
5059
|
+
const timer = setTimeout(() => {
|
|
5060
|
+
pc.close();
|
|
5061
|
+
reject(new Error("direct-dial-timeout"));
|
|
5062
|
+
}, 2500);
|
|
5063
|
+
dc.onopen = () => {
|
|
5064
|
+
clearTimeout(timer);
|
|
5065
|
+
resolve();
|
|
5066
|
+
};
|
|
5067
|
+
dc.onerror = () => {
|
|
5068
|
+
clearTimeout(timer);
|
|
5069
|
+
pc.close();
|
|
5070
|
+
reject(new Error("dc-error"));
|
|
5071
|
+
};
|
|
5072
|
+
});
|
|
5073
|
+
this._dataConnection = void 0;
|
|
5074
|
+
this._setMode("webrtc" /* WebRTC */);
|
|
5075
|
+
pc.close();
|
|
5076
|
+
return true;
|
|
5077
|
+
} catch {
|
|
5078
|
+
return false;
|
|
5079
|
+
}
|
|
5080
|
+
}
|
|
5081
|
+
async _gatherSrflxCandidates() {
|
|
5082
|
+
const pc = new RTCPeerConnection({ iceServers: this._provider.options.config?.iceServers });
|
|
5083
|
+
pc.createDataChannel("probe");
|
|
5084
|
+
const offer = await pc.createOffer();
|
|
5085
|
+
await pc.setLocalDescription(offer);
|
|
5086
|
+
const candidates = [];
|
|
5087
|
+
await new Promise((resolve) => {
|
|
5088
|
+
const timer = setTimeout(resolve, 2e3);
|
|
5089
|
+
pc.onicecandidate = (evt) => {
|
|
5090
|
+
if (!evt.candidate) {
|
|
5091
|
+
clearTimeout(timer);
|
|
5092
|
+
resolve();
|
|
5093
|
+
return;
|
|
5094
|
+
}
|
|
5095
|
+
if (!evt.candidate.candidate.includes("typ host")) {
|
|
5096
|
+
candidates.push(evt.candidate.candidate);
|
|
5097
|
+
}
|
|
5098
|
+
};
|
|
5099
|
+
});
|
|
5100
|
+
pc.close();
|
|
5101
|
+
return candidates;
|
|
5102
|
+
}
|
|
5103
|
+
async _dcutrHolePunch() {
|
|
5104
|
+
try {
|
|
5105
|
+
const localAddrs = await this._gatherSrflxCandidates();
|
|
5106
|
+
const t0 = performance.now();
|
|
5107
|
+
const peerAddrs = await new Promise((resolve, reject) => {
|
|
5108
|
+
const timer = setTimeout(() => reject(new Error("dcutr-timeout")), 8e3);
|
|
5109
|
+
const handler = (data) => {
|
|
5110
|
+
clearTimeout(timer);
|
|
5111
|
+
this._provider.off("DCUTR-CONNECT" /* DcutrConnect */, handler);
|
|
5112
|
+
resolve(data?.addresses ?? []);
|
|
5113
|
+
};
|
|
5114
|
+
this._provider.on("DCUTR-CONNECT" /* DcutrConnect */, handler);
|
|
5115
|
+
this._provider.socket.send({
|
|
5116
|
+
type: "DCUTR-CONNECT" /* DcutrConnect */,
|
|
5117
|
+
payload: { addresses: localAddrs }
|
|
5118
|
+
});
|
|
5119
|
+
});
|
|
5120
|
+
const relayRtt = performance.now() - t0;
|
|
5121
|
+
this._provider.socket.send({ type: "DCUTR-SYNC" /* DcutrSync */, payload: {} });
|
|
5122
|
+
await new Promise((r) => setTimeout(r, relayRtt / 2));
|
|
5123
|
+
for (let i = 0; i < Math.min(peerAddrs.length, 4); i++) {
|
|
5124
|
+
try {
|
|
5125
|
+
const pc = new RTCPeerConnection(this._provider.options.config);
|
|
5126
|
+
await new Promise((resolve, reject) => {
|
|
5127
|
+
const timer = setTimeout(() => {
|
|
5128
|
+
pc.close();
|
|
5129
|
+
reject(new Error("dc-dial-timeout"));
|
|
5130
|
+
}, 5e3);
|
|
5131
|
+
const dc = pc.createDataChannel("dcutr");
|
|
5132
|
+
dc.onopen = () => {
|
|
5133
|
+
clearTimeout(timer);
|
|
5134
|
+
resolve();
|
|
5135
|
+
};
|
|
5136
|
+
});
|
|
5137
|
+
this._dataConnection = void 0;
|
|
5138
|
+
this._setMode("webrtc" /* WebRTC */);
|
|
5139
|
+
pc.close();
|
|
5140
|
+
return true;
|
|
5141
|
+
} catch {
|
|
5142
|
+
}
|
|
5143
|
+
}
|
|
5144
|
+
return false;
|
|
5145
|
+
} catch {
|
|
5146
|
+
return false;
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
4991
5149
|
};
|
|
4992
5150
|
|
|
4993
5151
|
// src/mediaconnection.ts
|
|
@@ -5284,7 +5442,7 @@ var PollingTransport = class _PollingTransport extends SignalingTransport {
|
|
|
5284
5442
|
};
|
|
5285
5443
|
|
|
5286
5444
|
// src/socket.ts
|
|
5287
|
-
var version2 = "2.
|
|
5445
|
+
var version2 = "2.4.0";
|
|
5288
5446
|
var Socket = class _Socket extends SignalingTransport {
|
|
5289
5447
|
constructor(secure, host, port, path, key, pingInterval = 5e3, jwt) {
|
|
5290
5448
|
super();
|
|
@@ -5326,7 +5484,7 @@ var Socket = class _Socket extends SignalingTransport {
|
|
|
5326
5484
|
if (this._jwt) {
|
|
5327
5485
|
wsUrl += `&jwt=${encodeURIComponent(this._jwt)}`;
|
|
5328
5486
|
}
|
|
5329
|
-
if (
|
|
5487
|
+
if (this._socket || !this._disconnected) {
|
|
5330
5488
|
return;
|
|
5331
5489
|
}
|
|
5332
5490
|
this._socket = new WebSocket(`${wsUrl}&version=${version2}`);
|
|
@@ -5917,7 +6075,7 @@ var Dendri = class _Dendri extends EventEmitterWithError {
|
|
|
5917
6075
|
return;
|
|
5918
6076
|
}
|
|
5919
6077
|
}
|
|
5920
|
-
if (
|
|
6078
|
+
if (userId && !util.validateId(userId)) {
|
|
5921
6079
|
this._delayedAbort("invalid-id" /* InvalidID */, `ID "${userId}" is invalid`);
|
|
5922
6080
|
return;
|
|
5923
6081
|
}
|
|
@@ -6830,7 +6988,13 @@ var Room = class extends import_eventemitter35.EventEmitter {
|
|
|
6830
6988
|
sentViaWebRTC = true;
|
|
6831
6989
|
}
|
|
6832
6990
|
}
|
|
6833
|
-
if (
|
|
6991
|
+
if (this._dendriOptions?.enableRelay && this._peer?.socket) {
|
|
6992
|
+
this._peer.socket.send({
|
|
6993
|
+
type: "DATA" /* Data */,
|
|
6994
|
+
room: this._roomId,
|
|
6995
|
+
payload: wire
|
|
6996
|
+
});
|
|
6997
|
+
} else if (!sentViaWebRTC && this._peer?.socket) {
|
|
6834
6998
|
this._peer.socket.send({
|
|
6835
6999
|
type: "DATA" /* Data */,
|
|
6836
7000
|
room: this._roomId,
|
|
@@ -6864,7 +7028,13 @@ var Room = class extends import_eventemitter35.EventEmitter {
|
|
|
6864
7028
|
sentViaWebRTC = true;
|
|
6865
7029
|
}
|
|
6866
7030
|
}
|
|
6867
|
-
if (
|
|
7031
|
+
if (this._dendriOptions?.enableRelay && this._peer?.socket) {
|
|
7032
|
+
this._peer.socket.send({
|
|
7033
|
+
type: "DATA" /* Data */,
|
|
7034
|
+
room: this._roomId,
|
|
7035
|
+
payload: wire
|
|
7036
|
+
});
|
|
7037
|
+
} else if (!sentViaWebRTC && this._peer?.socket) {
|
|
6868
7038
|
this._peer.socket.send({
|
|
6869
7039
|
type: "DATA" /* Data */,
|
|
6870
7040
|
room: this._roomId,
|
|
@@ -7055,8 +7225,8 @@ var Room = class extends import_eventemitter35.EventEmitter {
|
|
|
7055
7225
|
const remotePeerId = conn.peer;
|
|
7056
7226
|
this._connections.set(remotePeerId, conn);
|
|
7057
7227
|
this._knownPeers.add(remotePeerId);
|
|
7228
|
+
this.emit("peerJoined", remotePeerId);
|
|
7058
7229
|
conn.on("open", () => {
|
|
7059
|
-
this.emit("peerJoined", remotePeerId);
|
|
7060
7230
|
for (const [peerId, c] of this._connections) {
|
|
7061
7231
|
if (peerId !== remotePeerId && c.open) {
|
|
7062
7232
|
c.send({ __room: { type: "peer-joined", peerId: remotePeerId } });
|
|
@@ -7378,6 +7548,14 @@ var Room = class extends import_eventemitter35.EventEmitter {
|
|
|
7378
7548
|
const conn = this._connections.get(peerId);
|
|
7379
7549
|
if (conn?.open) {
|
|
7380
7550
|
conn.send(data);
|
|
7551
|
+
if (this._dendriOptions?.enableRelay && this._peer?.socket) {
|
|
7552
|
+
this._peer.socket.send({
|
|
7553
|
+
type: "DATA" /* Data */,
|
|
7554
|
+
dst: peerId,
|
|
7555
|
+
room: this._roomId,
|
|
7556
|
+
payload: data
|
|
7557
|
+
});
|
|
7558
|
+
}
|
|
7381
7559
|
return;
|
|
7382
7560
|
}
|
|
7383
7561
|
if (!this._isHost && this._hostId) {
|