@afterrealism/dendri-client 2.3.7 → 2.5.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 +65 -72
- package/dist/{chunk-MJW5M75V.js → chunk-3CE674DE.js} +267 -24
- package/dist/chunk-3CE674DE.js.map +1 -0
- package/dist/dendri.browser.global.js +233 -18
- package/dist/dendri.browser.global.js.map +1 -1
- package/dist/dendri.cjs +266 -22
- package/dist/dendri.cjs.map +1 -1
- package/dist/dendri.d.cts +13 -4
- package/dist/dendri.d.ts +13 -4
- 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 +31 -4
- package/dist/serializer.msgpack.d.ts +31 -4
- package/dist/serializer.msgpack.js +2 -2
- package/dist/serializer.msgpack.js.map +1 -1
- package/dist/{store-RTivRmUW.d.cts → store-C3Nwl62R.d.cts} +38 -5
- package/dist/{store-RTivRmUW.d.ts → store-C3Nwl62R.d.ts} +38 -5
- package/dist/store.cjs +256 -21
- 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 +8 -2
- package/dist/chunk-MJW5M75V.js.map +0 -1
package/dist/dendri.cjs
CHANGED
|
@@ -3533,7 +3533,7 @@ var Logger = class {
|
|
|
3533
3533
|
var logger_default = new Logger();
|
|
3534
3534
|
|
|
3535
3535
|
// src/api.ts
|
|
3536
|
-
var version = "2.
|
|
3536
|
+
var version = "2.5.0";
|
|
3537
3537
|
var API = class _API {
|
|
3538
3538
|
constructor(_options) {
|
|
3539
3539
|
this._options = _options;
|
|
@@ -3546,6 +3546,7 @@ var API = class _API {
|
|
|
3546
3546
|
const url = new URL(`${protocol}://${host}:${port}${path}${key}/${method}`);
|
|
3547
3547
|
url.searchParams.set("ts", `${Date.now()}${Math.random()}`);
|
|
3548
3548
|
url.searchParams.set("version", version);
|
|
3549
|
+
if (this._options.apiKey) url.searchParams.set("api_key", this._options.apiKey);
|
|
3549
3550
|
const controller = new AbortController();
|
|
3550
3551
|
const timeoutId = setTimeout(() => controller.abort(), _API.FETCH_TIMEOUT);
|
|
3551
3552
|
return fetch(url.href, {
|
|
@@ -3570,15 +3571,16 @@ var API = class _API {
|
|
|
3570
3571
|
throw new Error(`Could not get an ID from the server.${pathError}`);
|
|
3571
3572
|
}
|
|
3572
3573
|
}
|
|
3573
|
-
/** Fetch TURN credentials from the signaling server's
|
|
3574
|
+
/** Fetch TURN credentials from the signaling server's turn-credentials endpoint. */
|
|
3574
3575
|
async getTurnCredentials() {
|
|
3575
3576
|
const protocol = this._options.secure ? "https" : "http";
|
|
3576
|
-
const { host, port } = this._options;
|
|
3577
|
-
const url = `${protocol}://${host}:${port}/turn
|
|
3577
|
+
const { host, port, path, key } = this._options;
|
|
3578
|
+
const url = new URL(`${protocol}://${host}:${port}${path}${key}/turn-credentials`);
|
|
3579
|
+
if (this._options.apiKey) url.searchParams.set("api_key", this._options.apiKey);
|
|
3578
3580
|
try {
|
|
3579
3581
|
const controller = new AbortController();
|
|
3580
3582
|
const timeoutId = setTimeout(() => controller.abort(), _API.FETCH_TIMEOUT);
|
|
3581
|
-
const response = await fetch(url, {
|
|
3583
|
+
const response = await fetch(url.href, {
|
|
3582
3584
|
referrerPolicy: this._options.referrerPolicy,
|
|
3583
3585
|
signal: controller.signal
|
|
3584
3586
|
}).finally(() => clearTimeout(timeoutId));
|
|
@@ -3683,6 +3685,9 @@ var ServerMessageType = /* @__PURE__ */ ((ServerMessageType2) => {
|
|
|
3683
3685
|
ServerMessageType2["HostMigrate"] = "HOST-MIGRATE";
|
|
3684
3686
|
ServerMessageType2["PresenceUpdate"] = "PRESENCE-UPDATE";
|
|
3685
3687
|
ServerMessageType2["KeyExchange"] = "KEY-EXCHANGE";
|
|
3688
|
+
ServerMessageType2["ConnectRequest"] = "CONNECT-REQUEST";
|
|
3689
|
+
ServerMessageType2["DcutrConnect"] = "DCUTR-CONNECT";
|
|
3690
|
+
ServerMessageType2["DcutrSync"] = "DCUTR-SYNC";
|
|
3686
3691
|
return ServerMessageType2;
|
|
3687
3692
|
})(ServerMessageType || {});
|
|
3688
3693
|
var TransportMode = /* @__PURE__ */ ((TransportMode2) => {
|
|
@@ -3708,6 +3713,11 @@ var ConnectionQuality = /* @__PURE__ */ ((ConnectionQuality2) => {
|
|
|
3708
3713
|
ConnectionQuality2["Unknown"] = "unknown";
|
|
3709
3714
|
return ConnectionQuality2;
|
|
3710
3715
|
})(ConnectionQuality || {});
|
|
3716
|
+
var TopicClass = /* @__PURE__ */ ((TopicClass2) => {
|
|
3717
|
+
TopicClass2["Persistent"] = "persistent";
|
|
3718
|
+
TopicClass2["Ephemeral"] = "ephemeral";
|
|
3719
|
+
return TopicClass2;
|
|
3720
|
+
})(TopicClass || {});
|
|
3711
3721
|
|
|
3712
3722
|
// src/dendriError.ts
|
|
3713
3723
|
var import_eventemitter3 = __toESM(require_eventemitter3(), 1);
|
|
@@ -3796,6 +3806,7 @@ var Negotiator = class {
|
|
|
3796
3806
|
}
|
|
3797
3807
|
connection;
|
|
3798
3808
|
_pendingCandidates = [];
|
|
3809
|
+
_iceCandidateFilter = null;
|
|
3799
3810
|
/** Returns a PeerConnection object set up correctly (for data, media). */
|
|
3800
3811
|
startConnection(options) {
|
|
3801
3812
|
const peerConnection = this._startPeerConnection();
|
|
@@ -3818,6 +3829,13 @@ var Negotiator = class {
|
|
|
3818
3829
|
_startPeerConnection() {
|
|
3819
3830
|
logger_default.log("Creating RTCPeerConnection.");
|
|
3820
3831
|
const peerConnection = new RTCPeerConnection(this.connection.provider?.options.config);
|
|
3832
|
+
if (this.connection.provider?.options.ipPolicy === "public") {
|
|
3833
|
+
const isPublicCandidate = (c) => {
|
|
3834
|
+
const sdp2 = c.candidate ?? "";
|
|
3835
|
+
return !sdp2.includes("typ host");
|
|
3836
|
+
};
|
|
3837
|
+
this._iceCandidateFilter = isPublicCandidate;
|
|
3838
|
+
}
|
|
3821
3839
|
this._setupListeners(peerConnection);
|
|
3822
3840
|
return peerConnection;
|
|
3823
3841
|
}
|
|
@@ -3830,6 +3848,9 @@ var Negotiator = class {
|
|
|
3830
3848
|
logger_default.log("Listening for ICE candidates.");
|
|
3831
3849
|
peerConnection.onicecandidate = (evt) => {
|
|
3832
3850
|
if (!evt.candidate?.candidate) return;
|
|
3851
|
+
if (this._iceCandidateFilter && !this._iceCandidateFilter(evt.candidate)) {
|
|
3852
|
+
return;
|
|
3853
|
+
}
|
|
3833
3854
|
logger_default.log(`Received ICE candidates for ${peerId}:`, evt.candidate);
|
|
3834
3855
|
provider.socket.send({
|
|
3835
3856
|
type: "CANDIDATE" /* Candidate */,
|
|
@@ -4168,6 +4189,7 @@ var DataConnection = class _DataConnection extends BaseConnection {
|
|
|
4168
4189
|
this.dataChannel.onopen = () => {
|
|
4169
4190
|
logger_default.log(`DC#${this.connectionId} dc connection success`);
|
|
4170
4191
|
this._open = true;
|
|
4192
|
+
this._applyAdaptiveBuffer(dc);
|
|
4171
4193
|
this.emit("open");
|
|
4172
4194
|
};
|
|
4173
4195
|
this.dataChannel.onclose = () => {
|
|
@@ -4175,6 +4197,24 @@ var DataConnection = class _DataConnection extends BaseConnection {
|
|
|
4175
4197
|
this.close();
|
|
4176
4198
|
};
|
|
4177
4199
|
}
|
|
4200
|
+
_applyAdaptiveBuffer(dc) {
|
|
4201
|
+
const pc = this.peerConnection;
|
|
4202
|
+
if (!pc || typeof pc.getStats !== "function") return;
|
|
4203
|
+
pc.getStats().then((stats) => {
|
|
4204
|
+
let rtt = null;
|
|
4205
|
+
stats.forEach((report) => {
|
|
4206
|
+
if (report.type === "candidate-pair" && report.state === "succeeded" && report.currentRoundTripTime) {
|
|
4207
|
+
rtt = report.currentRoundTripTime * 1e3;
|
|
4208
|
+
}
|
|
4209
|
+
});
|
|
4210
|
+
if (rtt !== null) {
|
|
4211
|
+
const bdp = 12.5 * 1024 * 1024 * (rtt / 1e3);
|
|
4212
|
+
const optimal = Math.max(1 * 1024 * 1024, Math.min(32 * 1024 * 1024, Math.ceil(bdp)));
|
|
4213
|
+
dc.bufferedAmountLowThreshold = optimal;
|
|
4214
|
+
}
|
|
4215
|
+
}).catch(() => {
|
|
4216
|
+
});
|
|
4217
|
+
}
|
|
4178
4218
|
/**
|
|
4179
4219
|
* Exposed functionality for users.
|
|
4180
4220
|
*/
|
|
@@ -4709,7 +4749,17 @@ var HybridConnection = class extends import_eventemitter32.EventEmitter {
|
|
|
4709
4749
|
if (this._closed) {
|
|
4710
4750
|
return;
|
|
4711
4751
|
}
|
|
4712
|
-
|
|
4752
|
+
logger_default.log(
|
|
4753
|
+
`HybridConnection: start peer=${this.peer} iceTimeout=${this._options.iceTimeout ?? 1e4}ms encryptRelay=${this._encryptRelay}`
|
|
4754
|
+
);
|
|
4755
|
+
if (typeof this._provider?.on !== "function") {
|
|
4756
|
+
this._attemptWebRTC();
|
|
4757
|
+
return;
|
|
4758
|
+
}
|
|
4759
|
+
this._tryConnectionReversal().then((direct) => {
|
|
4760
|
+
if (direct) return;
|
|
4761
|
+
this._attemptWebRTC();
|
|
4762
|
+
});
|
|
4713
4763
|
}
|
|
4714
4764
|
/** Send data through the best available transport, optionally tagged with a topic. */
|
|
4715
4765
|
send(data, options) {
|
|
@@ -4999,10 +5049,16 @@ var HybridConnection = class extends import_eventemitter32.EventEmitter {
|
|
|
4999
5049
|
this._dataConnection = dc;
|
|
5000
5050
|
this._iceTimer = setTimeout(() => {
|
|
5001
5051
|
if (this._mode !== "webrtc" /* WebRTC */) {
|
|
5052
|
+
logger_default.warn(
|
|
5053
|
+
`HybridConnection: ICE timeout after ${iceTimeout}ms for ${this.peer}, falling back to relay`
|
|
5054
|
+
);
|
|
5002
5055
|
this._fallbackToRelay();
|
|
5003
5056
|
}
|
|
5004
5057
|
}, iceTimeout);
|
|
5005
5058
|
this._dataConnection.on("open", () => {
|
|
5059
|
+
logger_default.log(
|
|
5060
|
+
`HybridConnection: WebRTC opened to ${this.peer} (attempt ${this._upgradeAttempts + 1})`
|
|
5061
|
+
);
|
|
5006
5062
|
this._clearIceTimer();
|
|
5007
5063
|
this._clearUpgradeTimer();
|
|
5008
5064
|
this._upgradeAttempts = 0;
|
|
@@ -5043,6 +5099,7 @@ var HybridConnection = class extends import_eventemitter32.EventEmitter {
|
|
|
5043
5099
|
/** Update the transport mode and emit if changed. */
|
|
5044
5100
|
_setMode(mode) {
|
|
5045
5101
|
if (this._mode !== mode) {
|
|
5102
|
+
logger_default.log(`HybridConnection: transport ${this._mode} -> ${mode} for ${this.peer}`);
|
|
5046
5103
|
this._mode = mode;
|
|
5047
5104
|
this.emit("transportChanged", mode);
|
|
5048
5105
|
}
|
|
@@ -5083,6 +5140,117 @@ var HybridConnection = class extends import_eventemitter32.EventEmitter {
|
|
|
5083
5140
|
this._attemptWebRTC();
|
|
5084
5141
|
}, interval);
|
|
5085
5142
|
}
|
|
5143
|
+
async _tryConnectionReversal() {
|
|
5144
|
+
if (typeof this._provider?.on !== "function") return false;
|
|
5145
|
+
try {
|
|
5146
|
+
const resp = await new Promise((resolve, reject) => {
|
|
5147
|
+
const timer = setTimeout(() => reject(new Error("timeout")), 3e3);
|
|
5148
|
+
const handler = (data) => {
|
|
5149
|
+
clearTimeout(timer);
|
|
5150
|
+
this._provider.off("CONNECT-REQUEST" /* ConnectRequest */, handler);
|
|
5151
|
+
resolve(data);
|
|
5152
|
+
};
|
|
5153
|
+
this._provider.on("CONNECT-REQUEST" /* ConnectRequest */, handler);
|
|
5154
|
+
this._provider.socket.send({
|
|
5155
|
+
type: "CONNECT-REQUEST" /* ConnectRequest */,
|
|
5156
|
+
payload: { peer: this.peer }
|
|
5157
|
+
});
|
|
5158
|
+
});
|
|
5159
|
+
const addr = resp?.address;
|
|
5160
|
+
if (!addr) return false;
|
|
5161
|
+
const pc = new RTCPeerConnection(this._provider.options.config);
|
|
5162
|
+
const dc = pc.createDataChannel("probe", { id: 0 });
|
|
5163
|
+
await new Promise((resolve, reject) => {
|
|
5164
|
+
const timer = setTimeout(() => {
|
|
5165
|
+
pc.close();
|
|
5166
|
+
reject(new Error("direct-dial-timeout"));
|
|
5167
|
+
}, 2500);
|
|
5168
|
+
dc.onopen = () => {
|
|
5169
|
+
clearTimeout(timer);
|
|
5170
|
+
resolve();
|
|
5171
|
+
};
|
|
5172
|
+
dc.onerror = () => {
|
|
5173
|
+
clearTimeout(timer);
|
|
5174
|
+
pc.close();
|
|
5175
|
+
reject(new Error("dc-error"));
|
|
5176
|
+
};
|
|
5177
|
+
});
|
|
5178
|
+
this._dataConnection = void 0;
|
|
5179
|
+
this._setMode("webrtc" /* WebRTC */);
|
|
5180
|
+
pc.close();
|
|
5181
|
+
return true;
|
|
5182
|
+
} catch {
|
|
5183
|
+
return false;
|
|
5184
|
+
}
|
|
5185
|
+
}
|
|
5186
|
+
async _gatherSrflxCandidates() {
|
|
5187
|
+
const pc = new RTCPeerConnection({ iceServers: this._provider.options.config?.iceServers });
|
|
5188
|
+
pc.createDataChannel("probe");
|
|
5189
|
+
const offer = await pc.createOffer();
|
|
5190
|
+
await pc.setLocalDescription(offer);
|
|
5191
|
+
const candidates = [];
|
|
5192
|
+
await new Promise((resolve) => {
|
|
5193
|
+
const timer = setTimeout(resolve, 2e3);
|
|
5194
|
+
pc.onicecandidate = (evt) => {
|
|
5195
|
+
if (!evt.candidate) {
|
|
5196
|
+
clearTimeout(timer);
|
|
5197
|
+
resolve();
|
|
5198
|
+
return;
|
|
5199
|
+
}
|
|
5200
|
+
if (!evt.candidate.candidate.includes("typ host")) {
|
|
5201
|
+
candidates.push(evt.candidate.candidate);
|
|
5202
|
+
}
|
|
5203
|
+
};
|
|
5204
|
+
});
|
|
5205
|
+
pc.close();
|
|
5206
|
+
return candidates;
|
|
5207
|
+
}
|
|
5208
|
+
async _dcutrHolePunch() {
|
|
5209
|
+
try {
|
|
5210
|
+
const localAddrs = await this._gatherSrflxCandidates();
|
|
5211
|
+
const t0 = performance.now();
|
|
5212
|
+
const peerAddrs = await new Promise((resolve, reject) => {
|
|
5213
|
+
const timer = setTimeout(() => reject(new Error("dcutr-timeout")), 8e3);
|
|
5214
|
+
const handler = (data) => {
|
|
5215
|
+
clearTimeout(timer);
|
|
5216
|
+
this._provider.off("DCUTR-CONNECT" /* DcutrConnect */, handler);
|
|
5217
|
+
resolve(data?.addresses ?? []);
|
|
5218
|
+
};
|
|
5219
|
+
this._provider.on("DCUTR-CONNECT" /* DcutrConnect */, handler);
|
|
5220
|
+
this._provider.socket.send({
|
|
5221
|
+
type: "DCUTR-CONNECT" /* DcutrConnect */,
|
|
5222
|
+
payload: { addresses: localAddrs }
|
|
5223
|
+
});
|
|
5224
|
+
});
|
|
5225
|
+
const relayRtt = performance.now() - t0;
|
|
5226
|
+
this._provider.socket.send({ type: "DCUTR-SYNC" /* DcutrSync */, payload: {} });
|
|
5227
|
+
await new Promise((r) => setTimeout(r, relayRtt / 2));
|
|
5228
|
+
for (let i = 0; i < Math.min(peerAddrs.length, 4); i++) {
|
|
5229
|
+
try {
|
|
5230
|
+
const pc = new RTCPeerConnection(this._provider.options.config);
|
|
5231
|
+
await new Promise((resolve, reject) => {
|
|
5232
|
+
const timer = setTimeout(() => {
|
|
5233
|
+
pc.close();
|
|
5234
|
+
reject(new Error("dc-dial-timeout"));
|
|
5235
|
+
}, 5e3);
|
|
5236
|
+
const dc = pc.createDataChannel("dcutr");
|
|
5237
|
+
dc.onopen = () => {
|
|
5238
|
+
clearTimeout(timer);
|
|
5239
|
+
resolve();
|
|
5240
|
+
};
|
|
5241
|
+
});
|
|
5242
|
+
this._dataConnection = void 0;
|
|
5243
|
+
this._setMode("webrtc" /* WebRTC */);
|
|
5244
|
+
pc.close();
|
|
5245
|
+
return true;
|
|
5246
|
+
} catch {
|
|
5247
|
+
}
|
|
5248
|
+
}
|
|
5249
|
+
return false;
|
|
5250
|
+
} catch {
|
|
5251
|
+
return false;
|
|
5252
|
+
}
|
|
5253
|
+
}
|
|
5086
5254
|
};
|
|
5087
5255
|
|
|
5088
5256
|
// src/mediaconnection.ts
|
|
@@ -5239,17 +5407,29 @@ var PollingTransport = class _PollingTransport extends SignalingTransport {
|
|
|
5239
5407
|
_heartbeatTimer;
|
|
5240
5408
|
_lastSeq = 0;
|
|
5241
5409
|
_baseUrl;
|
|
5410
|
+
_key;
|
|
5411
|
+
_jwt;
|
|
5412
|
+
_apiKey;
|
|
5242
5413
|
_pingInterval;
|
|
5243
5414
|
_abortController;
|
|
5244
5415
|
/** Backoff schedule base delays in milliseconds. */
|
|
5245
5416
|
static BACKOFF_SCHEDULE = [0, 1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
|
|
5246
5417
|
/** Random jitter range in milliseconds (applied as +/-). */
|
|
5247
5418
|
static BACKOFF_JITTER = 500;
|
|
5248
|
-
constructor(secure, host, port, path,
|
|
5419
|
+
constructor(secure, host, port, path, key, pingInterval = 5e3, jwt, apiKey) {
|
|
5249
5420
|
super();
|
|
5250
5421
|
const protocol = secure ? "https://" : "http://";
|
|
5251
5422
|
this._baseUrl = `${protocol + host}:${port}${path}`;
|
|
5252
5423
|
this._pingInterval = pingInterval;
|
|
5424
|
+
this._key = key;
|
|
5425
|
+
this._jwt = jwt;
|
|
5426
|
+
this._apiKey = apiKey;
|
|
5427
|
+
}
|
|
5428
|
+
/** Append the shared auth params (key, jwt, api_key) so every HTTP call authenticates like the WS transport. */
|
|
5429
|
+
_applyAuthParams(params) {
|
|
5430
|
+
params.set("key", this._key);
|
|
5431
|
+
if (this._jwt) params.set("jwt", this._jwt);
|
|
5432
|
+
if (this._apiKey) params.set("api_key", this._apiKey);
|
|
5253
5433
|
}
|
|
5254
5434
|
get reconnectAttempt() {
|
|
5255
5435
|
return this._reconnectAttempt;
|
|
@@ -5277,6 +5457,7 @@ var PollingTransport = class _PollingTransport extends SignalingTransport {
|
|
|
5277
5457
|
id: this._id,
|
|
5278
5458
|
token: this._token
|
|
5279
5459
|
});
|
|
5460
|
+
this._applyAuthParams(params);
|
|
5280
5461
|
if (this._lastSeq > 0) params.set("last_seq", String(this._lastSeq));
|
|
5281
5462
|
const response = await fetch(`${this._baseUrl}http/poll?${params}`, {
|
|
5282
5463
|
signal: this._abortController.signal
|
|
@@ -5315,6 +5496,7 @@ var PollingTransport = class _PollingTransport extends SignalingTransport {
|
|
|
5315
5496
|
id: this._id,
|
|
5316
5497
|
token: this._token
|
|
5317
5498
|
});
|
|
5499
|
+
this._applyAuthParams(params);
|
|
5318
5500
|
try {
|
|
5319
5501
|
await fetch(`${this._baseUrl}http/send?${params}`, {
|
|
5320
5502
|
method: "POST",
|
|
@@ -5379,13 +5561,16 @@ var PollingTransport = class _PollingTransport extends SignalingTransport {
|
|
|
5379
5561
|
};
|
|
5380
5562
|
|
|
5381
5563
|
// src/socket.ts
|
|
5382
|
-
var version2 = "2.
|
|
5564
|
+
var version2 = "2.5.0";
|
|
5383
5565
|
var Socket = class _Socket extends SignalingTransport {
|
|
5384
|
-
constructor(secure, host, port, path, key, pingInterval = 5e3, jwt) {
|
|
5566
|
+
constructor(secure, host, port, path, key, pingInterval = 5e3, jwt, apiKey) {
|
|
5385
5567
|
super();
|
|
5386
5568
|
this.pingInterval = pingInterval;
|
|
5387
5569
|
const wsProtocol = secure ? "wss://" : "ws://";
|
|
5388
5570
|
this._baseUrl = `${wsProtocol + host}:${port}${path}dendri?key=${key}`;
|
|
5571
|
+
if (apiKey) {
|
|
5572
|
+
this._baseUrl += `&api_key=${encodeURIComponent(apiKey)}`;
|
|
5573
|
+
}
|
|
5389
5574
|
this._jwt = jwt;
|
|
5390
5575
|
}
|
|
5391
5576
|
pingInterval;
|
|
@@ -5421,7 +5606,7 @@ var Socket = class _Socket extends SignalingTransport {
|
|
|
5421
5606
|
if (this._jwt) {
|
|
5422
5607
|
wsUrl += `&jwt=${encodeURIComponent(this._jwt)}`;
|
|
5423
5608
|
}
|
|
5424
|
-
if (
|
|
5609
|
+
if (this._socket || !this._disconnected) {
|
|
5425
5610
|
return;
|
|
5426
5611
|
}
|
|
5427
5612
|
this._socket = new WebSocket(`${wsUrl}&version=${version2}`);
|
|
@@ -5691,18 +5876,28 @@ var SSETransport = class _SSETransport extends SignalingTransport {
|
|
|
5691
5876
|
_heartbeatTimer;
|
|
5692
5877
|
_lastSeq = 0;
|
|
5693
5878
|
_baseUrl;
|
|
5879
|
+
_key;
|
|
5694
5880
|
_jwt;
|
|
5881
|
+
_apiKey;
|
|
5695
5882
|
_pingInterval;
|
|
5696
5883
|
/** Backoff schedule base delays in milliseconds. */
|
|
5697
5884
|
static BACKOFF_SCHEDULE = [0, 1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
|
|
5698
5885
|
/** Random jitter range in milliseconds (applied as +/-). */
|
|
5699
5886
|
static BACKOFF_JITTER = 500;
|
|
5700
|
-
constructor(secure, host, port, path,
|
|
5887
|
+
constructor(secure, host, port, path, key, pingInterval = 5e3, jwt, apiKey) {
|
|
5701
5888
|
super();
|
|
5702
5889
|
const protocol = secure ? "https://" : "http://";
|
|
5703
5890
|
this._baseUrl = `${protocol + host}:${port}${path}`;
|
|
5704
5891
|
this._pingInterval = pingInterval;
|
|
5892
|
+
this._key = key;
|
|
5705
5893
|
this._jwt = jwt;
|
|
5894
|
+
this._apiKey = apiKey;
|
|
5895
|
+
}
|
|
5896
|
+
/** Append the shared auth params (key, jwt, api_key) so every HTTP call authenticates like the WS transport. */
|
|
5897
|
+
_applyAuthParams(params) {
|
|
5898
|
+
params.set("key", this._key);
|
|
5899
|
+
if (this._jwt) params.set("jwt", this._jwt);
|
|
5900
|
+
if (this._apiKey) params.set("api_key", this._apiKey);
|
|
5706
5901
|
}
|
|
5707
5902
|
get reconnectAttempt() {
|
|
5708
5903
|
return this._reconnectAttempt;
|
|
@@ -5720,10 +5915,9 @@ var SSETransport = class _SSETransport extends SignalingTransport {
|
|
|
5720
5915
|
if (this._disconnected) return;
|
|
5721
5916
|
const params = new URLSearchParams({
|
|
5722
5917
|
id: this._id,
|
|
5723
|
-
token: this._token
|
|
5724
|
-
key: "dendri"
|
|
5918
|
+
token: this._token
|
|
5725
5919
|
});
|
|
5726
|
-
|
|
5920
|
+
this._applyAuthParams(params);
|
|
5727
5921
|
if (this._lastSeq > 0) params.set("last_seq", String(this._lastSeq));
|
|
5728
5922
|
const url = `${this._baseUrl}http/sse?${params}`;
|
|
5729
5923
|
try {
|
|
@@ -5797,6 +5991,7 @@ var SSETransport = class _SSETransport extends SignalingTransport {
|
|
|
5797
5991
|
id: this._id,
|
|
5798
5992
|
token: this._token
|
|
5799
5993
|
});
|
|
5994
|
+
this._applyAuthParams(params);
|
|
5800
5995
|
try {
|
|
5801
5996
|
await fetch(`${this._baseUrl}http/send?${params}`, {
|
|
5802
5997
|
method: "POST",
|
|
@@ -5869,6 +6064,28 @@ var SSETransport = class _SSETransport extends SignalingTransport {
|
|
|
5869
6064
|
};
|
|
5870
6065
|
|
|
5871
6066
|
// src/dendri.ts
|
|
6067
|
+
function parseServerUrl(url) {
|
|
6068
|
+
let parsed;
|
|
6069
|
+
try {
|
|
6070
|
+
parsed = new URL(url);
|
|
6071
|
+
} catch {
|
|
6072
|
+
throw new Error(
|
|
6073
|
+
`Invalid Dendri "url" option: "${url}". Expected a full URL like "wss://signal.example.com".`
|
|
6074
|
+
);
|
|
6075
|
+
}
|
|
6076
|
+
const secure = parsed.protocol === "wss:" || parsed.protocol === "https:";
|
|
6077
|
+
if (!secure && parsed.protocol !== "ws:" && parsed.protocol !== "http:") {
|
|
6078
|
+
throw new Error(
|
|
6079
|
+
`Invalid Dendri "url" protocol: "${parsed.protocol}". Use wss://, ws://, https://, or http://.`
|
|
6080
|
+
);
|
|
6081
|
+
}
|
|
6082
|
+
return {
|
|
6083
|
+
host: parsed.hostname,
|
|
6084
|
+
port: parsed.port ? Number(parsed.port) : secure ? 443 : 80,
|
|
6085
|
+
secure,
|
|
6086
|
+
path: parsed.pathname || "/"
|
|
6087
|
+
};
|
|
6088
|
+
}
|
|
5872
6089
|
var Dendri = class _Dendri extends EventEmitterWithError {
|
|
5873
6090
|
static DEFAULT_KEY = "dendri";
|
|
5874
6091
|
_serializers = {
|
|
@@ -5959,6 +6176,9 @@ var Dendri = class _Dendri extends EventEmitterWithError {
|
|
|
5959
6176
|
} else if (id) {
|
|
5960
6177
|
userId = id.toString();
|
|
5961
6178
|
}
|
|
6179
|
+
if (providedOptions?.url) {
|
|
6180
|
+
providedOptions = { ...parseServerUrl(providedOptions.url), ...providedOptions };
|
|
6181
|
+
}
|
|
5962
6182
|
const normalizedOptions = {
|
|
5963
6183
|
debug: 0,
|
|
5964
6184
|
// 1: Errors, 2: Warnings, 3: All logs
|
|
@@ -6012,7 +6232,7 @@ var Dendri = class _Dendri extends EventEmitterWithError {
|
|
|
6012
6232
|
return;
|
|
6013
6233
|
}
|
|
6014
6234
|
}
|
|
6015
|
-
if (
|
|
6235
|
+
if (userId && !util.validateId(userId)) {
|
|
6016
6236
|
this._delayedAbort("invalid-id" /* InvalidID */, `ID "${userId}" is invalid`);
|
|
6017
6237
|
return;
|
|
6018
6238
|
}
|
|
@@ -6077,7 +6297,8 @@ var Dendri = class _Dendri extends EventEmitterWithError {
|
|
|
6077
6297
|
this._options.path,
|
|
6078
6298
|
this._options.key,
|
|
6079
6299
|
this._options.pingInterval,
|
|
6080
|
-
this._options.jwt
|
|
6300
|
+
this._options.jwt,
|
|
6301
|
+
this._options.apiKey
|
|
6081
6302
|
) : transport === "polling" ? new PollingTransport(
|
|
6082
6303
|
this._options.secure ?? false,
|
|
6083
6304
|
this._options.host,
|
|
@@ -6085,7 +6306,8 @@ var Dendri = class _Dendri extends EventEmitterWithError {
|
|
|
6085
6306
|
this._options.path,
|
|
6086
6307
|
this._options.key,
|
|
6087
6308
|
this._options.pingInterval,
|
|
6088
|
-
this._options.jwt
|
|
6309
|
+
this._options.jwt,
|
|
6310
|
+
this._options.apiKey
|
|
6089
6311
|
) : new Socket(
|
|
6090
6312
|
this._options.secure ?? false,
|
|
6091
6313
|
this._options.host,
|
|
@@ -6093,7 +6315,8 @@ var Dendri = class _Dendri extends EventEmitterWithError {
|
|
|
6093
6315
|
this._options.path,
|
|
6094
6316
|
this._options.key,
|
|
6095
6317
|
this._options.pingInterval,
|
|
6096
|
-
this._options.jwt
|
|
6318
|
+
this._options.jwt,
|
|
6319
|
+
this._options.apiKey
|
|
6097
6320
|
);
|
|
6098
6321
|
socket.on("message" /* Message */, (data) => {
|
|
6099
6322
|
this._handleMessage(data);
|
|
@@ -8791,7 +9014,13 @@ var Room = class extends import_eventemitter35.EventEmitter {
|
|
|
8791
9014
|
sentViaWebRTC = true;
|
|
8792
9015
|
}
|
|
8793
9016
|
}
|
|
8794
|
-
if (
|
|
9017
|
+
if (this._dendriOptions?.enableRelay && this._peer?.socket) {
|
|
9018
|
+
this._peer.socket.send({
|
|
9019
|
+
type: "DATA" /* Data */,
|
|
9020
|
+
room: this._roomId,
|
|
9021
|
+
payload: wire
|
|
9022
|
+
});
|
|
9023
|
+
} else if (!sentViaWebRTC && this._peer?.socket) {
|
|
8795
9024
|
this._peer.socket.send({
|
|
8796
9025
|
type: "DATA" /* Data */,
|
|
8797
9026
|
room: this._roomId,
|
|
@@ -8825,7 +9054,13 @@ var Room = class extends import_eventemitter35.EventEmitter {
|
|
|
8825
9054
|
sentViaWebRTC = true;
|
|
8826
9055
|
}
|
|
8827
9056
|
}
|
|
8828
|
-
if (
|
|
9057
|
+
if (this._dendriOptions?.enableRelay && this._peer?.socket) {
|
|
9058
|
+
this._peer.socket.send({
|
|
9059
|
+
type: "DATA" /* Data */,
|
|
9060
|
+
room: this._roomId,
|
|
9061
|
+
payload: wire
|
|
9062
|
+
});
|
|
9063
|
+
} else if (!sentViaWebRTC && this._peer?.socket) {
|
|
8829
9064
|
this._peer.socket.send({
|
|
8830
9065
|
type: "DATA" /* Data */,
|
|
8831
9066
|
room: this._roomId,
|
|
@@ -9016,8 +9251,8 @@ var Room = class extends import_eventemitter35.EventEmitter {
|
|
|
9016
9251
|
const remotePeerId = conn.peer;
|
|
9017
9252
|
this._connections.set(remotePeerId, conn);
|
|
9018
9253
|
this._knownPeers.add(remotePeerId);
|
|
9254
|
+
this.emit("peerJoined", remotePeerId);
|
|
9019
9255
|
conn.on("open", () => {
|
|
9020
|
-
this.emit("peerJoined", remotePeerId);
|
|
9021
9256
|
for (const [peerId, c] of this._connections) {
|
|
9022
9257
|
if (peerId !== remotePeerId && c.open) {
|
|
9023
9258
|
c.send({ __room: { type: "peer-joined", peerId: remotePeerId } });
|
|
@@ -9339,6 +9574,14 @@ var Room = class extends import_eventemitter35.EventEmitter {
|
|
|
9339
9574
|
const conn = this._connections.get(peerId);
|
|
9340
9575
|
if (conn?.open) {
|
|
9341
9576
|
conn.send(data);
|
|
9577
|
+
if (this._dendriOptions?.enableRelay && this._peer?.socket) {
|
|
9578
|
+
this._peer.socket.send({
|
|
9579
|
+
type: "DATA" /* Data */,
|
|
9580
|
+
dst: peerId,
|
|
9581
|
+
room: this._roomId,
|
|
9582
|
+
payload: data
|
|
9583
|
+
});
|
|
9584
|
+
}
|
|
9342
9585
|
return;
|
|
9343
9586
|
}
|
|
9344
9587
|
if (!this._isHost && this._hostId) {
|
|
@@ -9403,7 +9646,7 @@ var DendriServerAPI = class {
|
|
|
9403
9646
|
}
|
|
9404
9647
|
/** Get TURN credentials */
|
|
9405
9648
|
async getTurnCredentials() {
|
|
9406
|
-
const res = await fetch(`${this._baseUrl}/turn`);
|
|
9649
|
+
const res = await fetch(`${this._baseUrl}/${this._key}/turn-credentials`);
|
|
9407
9650
|
if (!res.ok) throw new Error(`TURN credentials failed: ${res.status}`);
|
|
9408
9651
|
return res.json();
|
|
9409
9652
|
}
|
|
@@ -9712,6 +9955,7 @@ exports.ServerMessageType = ServerMessageType;
|
|
|
9712
9955
|
exports.SignalingTransport = SignalingTransport;
|
|
9713
9956
|
exports.SocketEventType = SocketEventType;
|
|
9714
9957
|
exports.StreamConnection = StreamConnection;
|
|
9958
|
+
exports.TopicClass = TopicClass;
|
|
9715
9959
|
exports.TopicManager = TopicManager;
|
|
9716
9960
|
exports.TransportMode = TransportMode;
|
|
9717
9961
|
exports.createDendriStore = createDendriStore;
|