@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
|
@@ -3536,7 +3536,7 @@ var dendri = (() => {
|
|
|
3536
3536
|
var util = new Util();
|
|
3537
3537
|
|
|
3538
3538
|
// src/api.ts
|
|
3539
|
-
var version = "2.
|
|
3539
|
+
var version = "2.4.0";
|
|
3540
3540
|
var API = class _API {
|
|
3541
3541
|
constructor(_options) {
|
|
3542
3542
|
this._options = _options;
|
|
@@ -3573,11 +3573,11 @@ var dendri = (() => {
|
|
|
3573
3573
|
throw new Error(`Could not get an ID from the server.${pathError}`);
|
|
3574
3574
|
}
|
|
3575
3575
|
}
|
|
3576
|
-
/** Fetch TURN credentials from the signaling server's
|
|
3576
|
+
/** Fetch TURN credentials from the signaling server's turn-credentials endpoint. */
|
|
3577
3577
|
async getTurnCredentials() {
|
|
3578
3578
|
const protocol = this._options.secure ? "https" : "http";
|
|
3579
|
-
const { host, port } = this._options;
|
|
3580
|
-
const url = `${protocol}://${host}:${port}/turn`;
|
|
3579
|
+
const { host, port, path, key } = this._options;
|
|
3580
|
+
const url = `${protocol}://${host}:${port}${path}${key}/turn-credentials`;
|
|
3581
3581
|
try {
|
|
3582
3582
|
const controller = new AbortController();
|
|
3583
3583
|
const timeoutId = setTimeout(() => controller.abort(), _API.FETCH_TIMEOUT);
|
|
@@ -3706,6 +3706,7 @@ var dendri = (() => {
|
|
|
3706
3706
|
}
|
|
3707
3707
|
connection;
|
|
3708
3708
|
_pendingCandidates = [];
|
|
3709
|
+
_iceCandidateFilter = null;
|
|
3709
3710
|
/** Returns a PeerConnection object set up correctly (for data, media). */
|
|
3710
3711
|
startConnection(options) {
|
|
3711
3712
|
const peerConnection = this._startPeerConnection();
|
|
@@ -3728,6 +3729,13 @@ var dendri = (() => {
|
|
|
3728
3729
|
_startPeerConnection() {
|
|
3729
3730
|
logger_default.log("Creating RTCPeerConnection.");
|
|
3730
3731
|
const peerConnection = new RTCPeerConnection(this.connection.provider?.options.config);
|
|
3732
|
+
if (this.connection.provider?.options.ipPolicy === "public") {
|
|
3733
|
+
const isPublicCandidate = (c) => {
|
|
3734
|
+
const sdp2 = c.candidate ?? "";
|
|
3735
|
+
return !sdp2.includes("typ host");
|
|
3736
|
+
};
|
|
3737
|
+
this._iceCandidateFilter = isPublicCandidate;
|
|
3738
|
+
}
|
|
3731
3739
|
this._setupListeners(peerConnection);
|
|
3732
3740
|
return peerConnection;
|
|
3733
3741
|
}
|
|
@@ -3740,6 +3748,9 @@ var dendri = (() => {
|
|
|
3740
3748
|
logger_default.log("Listening for ICE candidates.");
|
|
3741
3749
|
peerConnection.onicecandidate = (evt) => {
|
|
3742
3750
|
if (!evt.candidate?.candidate) return;
|
|
3751
|
+
if (this._iceCandidateFilter && !this._iceCandidateFilter(evt.candidate)) {
|
|
3752
|
+
return;
|
|
3753
|
+
}
|
|
3743
3754
|
logger_default.log(`Received ICE candidates for ${peerId}:`, evt.candidate);
|
|
3744
3755
|
provider.socket.send({
|
|
3745
3756
|
type: "CANDIDATE" /* Candidate */,
|
|
@@ -4078,6 +4089,7 @@ var dendri = (() => {
|
|
|
4078
4089
|
this.dataChannel.onopen = () => {
|
|
4079
4090
|
logger_default.log(`DC#${this.connectionId} dc connection success`);
|
|
4080
4091
|
this._open = true;
|
|
4092
|
+
this._applyAdaptiveBuffer(dc);
|
|
4081
4093
|
this.emit("open");
|
|
4082
4094
|
};
|
|
4083
4095
|
this.dataChannel.onclose = () => {
|
|
@@ -4085,6 +4097,24 @@ var dendri = (() => {
|
|
|
4085
4097
|
this.close();
|
|
4086
4098
|
};
|
|
4087
4099
|
}
|
|
4100
|
+
_applyAdaptiveBuffer(dc) {
|
|
4101
|
+
const pc = this.peerConnection;
|
|
4102
|
+
if (!pc || typeof pc.getStats !== "function") return;
|
|
4103
|
+
pc.getStats().then((stats) => {
|
|
4104
|
+
let rtt = null;
|
|
4105
|
+
stats.forEach((report) => {
|
|
4106
|
+
if (report.type === "candidate-pair" && report.state === "succeeded" && report.currentRoundTripTime) {
|
|
4107
|
+
rtt = report.currentRoundTripTime * 1e3;
|
|
4108
|
+
}
|
|
4109
|
+
});
|
|
4110
|
+
if (rtt !== null) {
|
|
4111
|
+
const bdp = 12.5 * 1024 * 1024 * (rtt / 1e3);
|
|
4112
|
+
const optimal = Math.max(1 * 1024 * 1024, Math.min(32 * 1024 * 1024, Math.ceil(bdp)));
|
|
4113
|
+
dc.bufferedAmountLowThreshold = optimal;
|
|
4114
|
+
}
|
|
4115
|
+
}).catch(() => {
|
|
4116
|
+
});
|
|
4117
|
+
}
|
|
4088
4118
|
/**
|
|
4089
4119
|
* Exposed functionality for users.
|
|
4090
4120
|
*/
|
|
@@ -4619,7 +4649,17 @@ var dendri = (() => {
|
|
|
4619
4649
|
if (this._closed) {
|
|
4620
4650
|
return;
|
|
4621
4651
|
}
|
|
4622
|
-
|
|
4652
|
+
logger_default.log(
|
|
4653
|
+
`HybridConnection: start peer=${this.peer} iceTimeout=${this._options.iceTimeout ?? 1e4}ms encryptRelay=${this._encryptRelay}`
|
|
4654
|
+
);
|
|
4655
|
+
if (typeof this._provider?.on !== "function") {
|
|
4656
|
+
this._attemptWebRTC();
|
|
4657
|
+
return;
|
|
4658
|
+
}
|
|
4659
|
+
this._tryConnectionReversal().then((direct) => {
|
|
4660
|
+
if (direct) return;
|
|
4661
|
+
this._attemptWebRTC();
|
|
4662
|
+
});
|
|
4623
4663
|
}
|
|
4624
4664
|
/** Send data through the best available transport, optionally tagged with a topic. */
|
|
4625
4665
|
send(data, options) {
|
|
@@ -4909,10 +4949,16 @@ var dendri = (() => {
|
|
|
4909
4949
|
this._dataConnection = dc;
|
|
4910
4950
|
this._iceTimer = setTimeout(() => {
|
|
4911
4951
|
if (this._mode !== "webrtc" /* WebRTC */) {
|
|
4952
|
+
logger_default.warn(
|
|
4953
|
+
`HybridConnection: ICE timeout after ${iceTimeout}ms for ${this.peer}, falling back to relay`
|
|
4954
|
+
);
|
|
4912
4955
|
this._fallbackToRelay();
|
|
4913
4956
|
}
|
|
4914
4957
|
}, iceTimeout);
|
|
4915
4958
|
this._dataConnection.on("open", () => {
|
|
4959
|
+
logger_default.log(
|
|
4960
|
+
`HybridConnection: WebRTC opened to ${this.peer} (attempt ${this._upgradeAttempts + 1})`
|
|
4961
|
+
);
|
|
4916
4962
|
this._clearIceTimer();
|
|
4917
4963
|
this._clearUpgradeTimer();
|
|
4918
4964
|
this._upgradeAttempts = 0;
|
|
@@ -4953,6 +4999,7 @@ var dendri = (() => {
|
|
|
4953
4999
|
/** Update the transport mode and emit if changed. */
|
|
4954
5000
|
_setMode(mode) {
|
|
4955
5001
|
if (this._mode !== mode) {
|
|
5002
|
+
logger_default.log(`HybridConnection: transport ${this._mode} -> ${mode} for ${this.peer}`);
|
|
4956
5003
|
this._mode = mode;
|
|
4957
5004
|
this.emit("transportChanged", mode);
|
|
4958
5005
|
}
|
|
@@ -4993,6 +5040,117 @@ var dendri = (() => {
|
|
|
4993
5040
|
this._attemptWebRTC();
|
|
4994
5041
|
}, interval);
|
|
4995
5042
|
}
|
|
5043
|
+
async _tryConnectionReversal() {
|
|
5044
|
+
if (typeof this._provider?.on !== "function") return false;
|
|
5045
|
+
try {
|
|
5046
|
+
const resp = await new Promise((resolve, reject) => {
|
|
5047
|
+
const timer = setTimeout(() => reject(new Error("timeout")), 3e3);
|
|
5048
|
+
const handler = (data) => {
|
|
5049
|
+
clearTimeout(timer);
|
|
5050
|
+
this._provider.off("CONNECT-REQUEST" /* ConnectRequest */, handler);
|
|
5051
|
+
resolve(data);
|
|
5052
|
+
};
|
|
5053
|
+
this._provider.on("CONNECT-REQUEST" /* ConnectRequest */, handler);
|
|
5054
|
+
this._provider.socket.send({
|
|
5055
|
+
type: "CONNECT-REQUEST" /* ConnectRequest */,
|
|
5056
|
+
payload: { peer: this.peer }
|
|
5057
|
+
});
|
|
5058
|
+
});
|
|
5059
|
+
const addr = resp?.address;
|
|
5060
|
+
if (!addr) return false;
|
|
5061
|
+
const pc = new RTCPeerConnection(this._provider.options.config);
|
|
5062
|
+
const dc = pc.createDataChannel("probe", { id: 0 });
|
|
5063
|
+
await new Promise((resolve, reject) => {
|
|
5064
|
+
const timer = setTimeout(() => {
|
|
5065
|
+
pc.close();
|
|
5066
|
+
reject(new Error("direct-dial-timeout"));
|
|
5067
|
+
}, 2500);
|
|
5068
|
+
dc.onopen = () => {
|
|
5069
|
+
clearTimeout(timer);
|
|
5070
|
+
resolve();
|
|
5071
|
+
};
|
|
5072
|
+
dc.onerror = () => {
|
|
5073
|
+
clearTimeout(timer);
|
|
5074
|
+
pc.close();
|
|
5075
|
+
reject(new Error("dc-error"));
|
|
5076
|
+
};
|
|
5077
|
+
});
|
|
5078
|
+
this._dataConnection = void 0;
|
|
5079
|
+
this._setMode("webrtc" /* WebRTC */);
|
|
5080
|
+
pc.close();
|
|
5081
|
+
return true;
|
|
5082
|
+
} catch {
|
|
5083
|
+
return false;
|
|
5084
|
+
}
|
|
5085
|
+
}
|
|
5086
|
+
async _gatherSrflxCandidates() {
|
|
5087
|
+
const pc = new RTCPeerConnection({ iceServers: this._provider.options.config?.iceServers });
|
|
5088
|
+
pc.createDataChannel("probe");
|
|
5089
|
+
const offer = await pc.createOffer();
|
|
5090
|
+
await pc.setLocalDescription(offer);
|
|
5091
|
+
const candidates = [];
|
|
5092
|
+
await new Promise((resolve) => {
|
|
5093
|
+
const timer = setTimeout(resolve, 2e3);
|
|
5094
|
+
pc.onicecandidate = (evt) => {
|
|
5095
|
+
if (!evt.candidate) {
|
|
5096
|
+
clearTimeout(timer);
|
|
5097
|
+
resolve();
|
|
5098
|
+
return;
|
|
5099
|
+
}
|
|
5100
|
+
if (!evt.candidate.candidate.includes("typ host")) {
|
|
5101
|
+
candidates.push(evt.candidate.candidate);
|
|
5102
|
+
}
|
|
5103
|
+
};
|
|
5104
|
+
});
|
|
5105
|
+
pc.close();
|
|
5106
|
+
return candidates;
|
|
5107
|
+
}
|
|
5108
|
+
async _dcutrHolePunch() {
|
|
5109
|
+
try {
|
|
5110
|
+
const localAddrs = await this._gatherSrflxCandidates();
|
|
5111
|
+
const t0 = performance.now();
|
|
5112
|
+
const peerAddrs = await new Promise((resolve, reject) => {
|
|
5113
|
+
const timer = setTimeout(() => reject(new Error("dcutr-timeout")), 8e3);
|
|
5114
|
+
const handler = (data) => {
|
|
5115
|
+
clearTimeout(timer);
|
|
5116
|
+
this._provider.off("DCUTR-CONNECT" /* DcutrConnect */, handler);
|
|
5117
|
+
resolve(data?.addresses ?? []);
|
|
5118
|
+
};
|
|
5119
|
+
this._provider.on("DCUTR-CONNECT" /* DcutrConnect */, handler);
|
|
5120
|
+
this._provider.socket.send({
|
|
5121
|
+
type: "DCUTR-CONNECT" /* DcutrConnect */,
|
|
5122
|
+
payload: { addresses: localAddrs }
|
|
5123
|
+
});
|
|
5124
|
+
});
|
|
5125
|
+
const relayRtt = performance.now() - t0;
|
|
5126
|
+
this._provider.socket.send({ type: "DCUTR-SYNC" /* DcutrSync */, payload: {} });
|
|
5127
|
+
await new Promise((r) => setTimeout(r, relayRtt / 2));
|
|
5128
|
+
for (let i = 0; i < Math.min(peerAddrs.length, 4); i++) {
|
|
5129
|
+
try {
|
|
5130
|
+
const pc = new RTCPeerConnection(this._provider.options.config);
|
|
5131
|
+
await new Promise((resolve, reject) => {
|
|
5132
|
+
const timer = setTimeout(() => {
|
|
5133
|
+
pc.close();
|
|
5134
|
+
reject(new Error("dc-dial-timeout"));
|
|
5135
|
+
}, 5e3);
|
|
5136
|
+
const dc = pc.createDataChannel("dcutr");
|
|
5137
|
+
dc.onopen = () => {
|
|
5138
|
+
clearTimeout(timer);
|
|
5139
|
+
resolve();
|
|
5140
|
+
};
|
|
5141
|
+
});
|
|
5142
|
+
this._dataConnection = void 0;
|
|
5143
|
+
this._setMode("webrtc" /* WebRTC */);
|
|
5144
|
+
pc.close();
|
|
5145
|
+
return true;
|
|
5146
|
+
} catch {
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
5149
|
+
return false;
|
|
5150
|
+
} catch {
|
|
5151
|
+
return false;
|
|
5152
|
+
}
|
|
5153
|
+
}
|
|
4996
5154
|
};
|
|
4997
5155
|
|
|
4998
5156
|
// src/mediaconnection.ts
|
|
@@ -5289,7 +5447,7 @@ var dendri = (() => {
|
|
|
5289
5447
|
};
|
|
5290
5448
|
|
|
5291
5449
|
// src/socket.ts
|
|
5292
|
-
var version2 = "2.
|
|
5450
|
+
var version2 = "2.4.0";
|
|
5293
5451
|
var Socket = class _Socket extends SignalingTransport {
|
|
5294
5452
|
constructor(secure, host, port, path, key, pingInterval = 5e3, jwt) {
|
|
5295
5453
|
super();
|
|
@@ -5331,7 +5489,7 @@ var dendri = (() => {
|
|
|
5331
5489
|
if (this._jwt) {
|
|
5332
5490
|
wsUrl += `&jwt=${encodeURIComponent(this._jwt)}`;
|
|
5333
5491
|
}
|
|
5334
|
-
if (
|
|
5492
|
+
if (this._socket || !this._disconnected) {
|
|
5335
5493
|
return;
|
|
5336
5494
|
}
|
|
5337
5495
|
this._socket = new WebSocket(`${wsUrl}&version=${version2}`);
|
|
@@ -5922,7 +6080,7 @@ var dendri = (() => {
|
|
|
5922
6080
|
return;
|
|
5923
6081
|
}
|
|
5924
6082
|
}
|
|
5925
|
-
if (
|
|
6083
|
+
if (userId && !util.validateId(userId)) {
|
|
5926
6084
|
this._delayedAbort("invalid-id" /* InvalidID */, `ID "${userId}" is invalid`);
|
|
5927
6085
|
return;
|
|
5928
6086
|
}
|