@mebius-io/web 0.3.0 → 0.4.1
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/README.md +25 -2
- package/dist/index.cjs +171 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -3
- package/dist/index.d.ts +32 -3
- package/dist/index.global.js +171 -14
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +171 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.global.js
CHANGED
|
@@ -42764,6 +42764,28 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
42764
42764
|
}
|
|
42765
42765
|
};
|
|
42766
42766
|
|
|
42767
|
+
// src/internal/autoplay.ts
|
|
42768
|
+
function isAutoplayBlocked(e) {
|
|
42769
|
+
return typeof e === "object" && e !== null && e.name === "NotAllowedError";
|
|
42770
|
+
}
|
|
42771
|
+
async function playWithAutoplayFallback(video) {
|
|
42772
|
+
video.playsInline = true;
|
|
42773
|
+
try {
|
|
42774
|
+
await video.play();
|
|
42775
|
+
return { mutedByPolicy: false };
|
|
42776
|
+
} catch (e) {
|
|
42777
|
+
if (!isAutoplayBlocked(e) || video.muted) throw e;
|
|
42778
|
+
video.muted = true;
|
|
42779
|
+
await video.play();
|
|
42780
|
+
return { mutedByPolicy: true };
|
|
42781
|
+
}
|
|
42782
|
+
}
|
|
42783
|
+
function resetVideoElement(video) {
|
|
42784
|
+
video.srcObject = null;
|
|
42785
|
+
video.removeAttribute("src");
|
|
42786
|
+
video.load();
|
|
42787
|
+
}
|
|
42788
|
+
|
|
42767
42789
|
// src/internal/ll-view-transport.ts
|
|
42768
42790
|
var WhepViewTransport = class {
|
|
42769
42791
|
constructor(signaling) {
|
|
@@ -42772,6 +42794,10 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
42772
42794
|
this.resourceUrl = null;
|
|
42773
42795
|
this.endedCb = null;
|
|
42774
42796
|
this.bufferingCb = null;
|
|
42797
|
+
/** Element this route attached a MediaStream to, so stop() can release it. */
|
|
42798
|
+
this.video = null;
|
|
42799
|
+
/** True when playback only started because the element had to be muted. */
|
|
42800
|
+
this.mutedByPolicy = false;
|
|
42775
42801
|
}
|
|
42776
42802
|
onEnded(cb) {
|
|
42777
42803
|
this.endedCb = cb;
|
|
@@ -42780,6 +42806,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
42780
42806
|
this.bufferingCb = cb;
|
|
42781
42807
|
}
|
|
42782
42808
|
async start(streamId, video) {
|
|
42809
|
+
this.video = video;
|
|
42783
42810
|
const pc = new RTCPeerConnection(DEFAULT_RTC_CONFIG);
|
|
42784
42811
|
this.pc = pc;
|
|
42785
42812
|
const remote = new MediaStream();
|
|
@@ -42788,7 +42815,9 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
42788
42815
|
pc.ontrack = (ev) => {
|
|
42789
42816
|
remote.addTrack(ev.track);
|
|
42790
42817
|
video.srcObject = remote;
|
|
42791
|
-
void video
|
|
42818
|
+
void playWithAutoplayFallback(video).then((o) => {
|
|
42819
|
+
this.mutedByPolicy = o.mutedByPolicy;
|
|
42820
|
+
}).catch(() => {
|
|
42792
42821
|
});
|
|
42793
42822
|
};
|
|
42794
42823
|
pc.onconnectionstatechange = () => {
|
|
@@ -42815,6 +42844,8 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
42815
42844
|
this.resourceUrl = null;
|
|
42816
42845
|
this.pc?.close();
|
|
42817
42846
|
this.pc = null;
|
|
42847
|
+
if (this.video) resetVideoElement(this.video);
|
|
42848
|
+
this.video = null;
|
|
42818
42849
|
}
|
|
42819
42850
|
async getStats() {
|
|
42820
42851
|
if (!this.pc) return null;
|
|
@@ -42852,6 +42883,8 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
42852
42883
|
this.video = null;
|
|
42853
42884
|
this.endedCb = null;
|
|
42854
42885
|
this.bufferingCb = null;
|
|
42886
|
+
/** True when playback only started because the element had to be muted. */
|
|
42887
|
+
this.mutedByPolicy = false;
|
|
42855
42888
|
}
|
|
42856
42889
|
onEnded(cb) {
|
|
42857
42890
|
this.endedCb = cb;
|
|
@@ -42866,7 +42899,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
42866
42899
|
video.addEventListener("waiting", () => this.bufferingCb?.());
|
|
42867
42900
|
if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
42868
42901
|
video.src = url;
|
|
42869
|
-
await video
|
|
42902
|
+
this.mutedByPolicy = (await playWithAutoplayFallback(video)).mutedByPolicy;
|
|
42870
42903
|
return;
|
|
42871
42904
|
}
|
|
42872
42905
|
let mod;
|
|
@@ -42879,14 +42912,14 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
42879
42912
|
if (!Hls2.isSupported()) {
|
|
42880
42913
|
throw mebiusError("CONNECTION_FAILED", "Scale playback is not supported in this browser.");
|
|
42881
42914
|
}
|
|
42882
|
-
const hls = new Hls2(
|
|
42915
|
+
const hls = new Hls2();
|
|
42883
42916
|
this.hls = hls;
|
|
42884
42917
|
hls.on(Hls2.Events.ERROR, (_evt, data) => {
|
|
42885
42918
|
if (data.fatal) this.bufferingCb?.();
|
|
42886
42919
|
});
|
|
42887
42920
|
hls.loadSource(url);
|
|
42888
42921
|
hls.attachMedia(video);
|
|
42889
|
-
await video
|
|
42922
|
+
this.mutedByPolicy = (await playWithAutoplayFallback(video)).mutedByPolicy;
|
|
42890
42923
|
}
|
|
42891
42924
|
async stop() {
|
|
42892
42925
|
this.hls?.destroy();
|
|
@@ -42916,6 +42949,8 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
42916
42949
|
this.video = null;
|
|
42917
42950
|
this.endedCb = null;
|
|
42918
42951
|
this.bufferingCb = null;
|
|
42952
|
+
/** True when playback only started because the element had to be muted. */
|
|
42953
|
+
this.mutedByPolicy = false;
|
|
42919
42954
|
}
|
|
42920
42955
|
onEnded(cb) {
|
|
42921
42956
|
this.endedCb = cb;
|
|
@@ -42943,7 +42978,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
42943
42978
|
player.on(flvjs.Events.ERROR ?? "error", () => this.bufferingCb?.());
|
|
42944
42979
|
player.attachMediaElement(video);
|
|
42945
42980
|
player.load();
|
|
42946
|
-
await
|
|
42981
|
+
this.mutedByPolicy = (await playWithAutoplayFallback(video)).mutedByPolicy;
|
|
42947
42982
|
}
|
|
42948
42983
|
async stop() {
|
|
42949
42984
|
if (this.player) {
|
|
@@ -42999,17 +43034,103 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
42999
43034
|
}
|
|
43000
43035
|
}
|
|
43001
43036
|
|
|
43037
|
+
// src/internal/telemetry.ts
|
|
43038
|
+
var SDK_VERSION = "web/0.4.1";
|
|
43039
|
+
var FLUSH_INTERVAL_MS = 15e3;
|
|
43040
|
+
var MAX_BATCH = 64;
|
|
43041
|
+
function describeDevice() {
|
|
43042
|
+
const nav = typeof navigator === "undefined" ? void 0 : navigator;
|
|
43043
|
+
const uaData = nav?.userAgentData;
|
|
43044
|
+
return { os: uaData?.platform || nav?.platform || void 0, sdk: SDK_VERSION };
|
|
43045
|
+
}
|
|
43046
|
+
function describeNetwork() {
|
|
43047
|
+
const conn = typeof navigator === "undefined" ? void 0 : navigator.connection;
|
|
43048
|
+
return conn?.effectiveType ? { type: conn.effectiveType } : void 0;
|
|
43049
|
+
}
|
|
43050
|
+
var QoeReporter = class {
|
|
43051
|
+
constructor(target, role, streamId, userId) {
|
|
43052
|
+
this.target = target;
|
|
43053
|
+
this.role = role;
|
|
43054
|
+
this.streamId = streamId;
|
|
43055
|
+
this.userId = userId;
|
|
43056
|
+
this.sessionId = randomId();
|
|
43057
|
+
this.buffer = [];
|
|
43058
|
+
this.timer = null;
|
|
43059
|
+
this.unloadHandler = null;
|
|
43060
|
+
}
|
|
43061
|
+
start() {
|
|
43062
|
+
if (this.timer) return;
|
|
43063
|
+
this.timer = setInterval(() => void this.flush(), FLUSH_INTERVAL_MS);
|
|
43064
|
+
if (typeof window !== "undefined") {
|
|
43065
|
+
this.unloadHandler = () => void this.flush(true);
|
|
43066
|
+
window.addEventListener("pagehide", this.unloadHandler);
|
|
43067
|
+
}
|
|
43068
|
+
}
|
|
43069
|
+
add(sample) {
|
|
43070
|
+
this.buffer.push(sample);
|
|
43071
|
+
if (this.buffer.length >= MAX_BATCH) void this.flush();
|
|
43072
|
+
}
|
|
43073
|
+
async stop() {
|
|
43074
|
+
if (this.timer) clearInterval(this.timer);
|
|
43075
|
+
this.timer = null;
|
|
43076
|
+
if (this.unloadHandler && typeof window !== "undefined") {
|
|
43077
|
+
window.removeEventListener("pagehide", this.unloadHandler);
|
|
43078
|
+
}
|
|
43079
|
+
this.unloadHandler = null;
|
|
43080
|
+
await this.flush();
|
|
43081
|
+
}
|
|
43082
|
+
/** Send and clear the buffer. `beacon` uses sendBeacon, for page-unload flushes. */
|
|
43083
|
+
async flush(beacon = false) {
|
|
43084
|
+
if (!this.buffer.length) return;
|
|
43085
|
+
const samples = this.buffer.splice(0, MAX_BATCH);
|
|
43086
|
+
const body = JSON.stringify({
|
|
43087
|
+
sessionId: this.sessionId,
|
|
43088
|
+
streamId: this.streamId,
|
|
43089
|
+
role: this.role,
|
|
43090
|
+
userId: this.userId,
|
|
43091
|
+
samples,
|
|
43092
|
+
device: describeDevice(),
|
|
43093
|
+
network: describeNetwork()
|
|
43094
|
+
});
|
|
43095
|
+
if (beacon && typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
43096
|
+
const url = `${this.target.url}${this.target.url.includes("?") ? "&" : "?"}token=${encodeURIComponent(this.target.token)}`;
|
|
43097
|
+
try {
|
|
43098
|
+
navigator.sendBeacon(url, new Blob([body], { type: "application/json" }));
|
|
43099
|
+
} catch {
|
|
43100
|
+
}
|
|
43101
|
+
return;
|
|
43102
|
+
}
|
|
43103
|
+
try {
|
|
43104
|
+
await fetch(this.target.url, {
|
|
43105
|
+
method: "POST",
|
|
43106
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${this.target.token}` },
|
|
43107
|
+
body,
|
|
43108
|
+
keepalive: true
|
|
43109
|
+
});
|
|
43110
|
+
} catch {
|
|
43111
|
+
}
|
|
43112
|
+
}
|
|
43113
|
+
};
|
|
43114
|
+
function randomId() {
|
|
43115
|
+
const c = typeof crypto === "undefined" ? void 0 : crypto;
|
|
43116
|
+
if (c?.randomUUID) return c.randomUUID();
|
|
43117
|
+
return `s-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
43118
|
+
}
|
|
43119
|
+
|
|
43002
43120
|
// src/broadcaster.ts
|
|
43003
43121
|
var STATS_INTERVAL_MS = 2e3;
|
|
43004
43122
|
var MebiusBroadcaster = class extends TypedEmitter {
|
|
43005
43123
|
/** @internal */
|
|
43006
|
-
constructor(signaling, options) {
|
|
43124
|
+
constructor(signaling, options, telemetry = null, userId) {
|
|
43007
43125
|
super();
|
|
43008
43126
|
this.options = options;
|
|
43127
|
+
this.telemetry = telemetry;
|
|
43128
|
+
this.userId = userId;
|
|
43009
43129
|
this.stream = null;
|
|
43010
43130
|
this.facingMode = "user";
|
|
43011
43131
|
this.statsTimer = null;
|
|
43012
43132
|
this.started = false;
|
|
43133
|
+
this.reporter = null;
|
|
43013
43134
|
this.transport = createPublishTransport(signaling);
|
|
43014
43135
|
}
|
|
43015
43136
|
/** Begin broadcasting under the given stream id. */
|
|
@@ -43018,12 +43139,18 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
43018
43139
|
this.stream = await this.capture();
|
|
43019
43140
|
await this.transport.start(streamId, this.stream);
|
|
43020
43141
|
this.started = true;
|
|
43142
|
+
if (this.telemetry) {
|
|
43143
|
+
this.reporter = new QoeReporter(this.telemetry, "pub", streamId, this.userId);
|
|
43144
|
+
this.reporter.start();
|
|
43145
|
+
}
|
|
43021
43146
|
this.startStats();
|
|
43022
43147
|
this.emit("started", { streamId });
|
|
43023
43148
|
}
|
|
43024
43149
|
/** Stop broadcasting and release the camera/microphone. */
|
|
43025
43150
|
async stop() {
|
|
43026
43151
|
this.stopStats();
|
|
43152
|
+
await this.reporter?.stop();
|
|
43153
|
+
this.reporter = null;
|
|
43027
43154
|
await this.transport.stop();
|
|
43028
43155
|
this.stream?.getTracks().forEach((t) => t.stop());
|
|
43029
43156
|
this.stream = null;
|
|
@@ -43079,7 +43206,14 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
43079
43206
|
startStats() {
|
|
43080
43207
|
this.statsTimer = setInterval(async () => {
|
|
43081
43208
|
const stats = await this.transport.getStats();
|
|
43082
|
-
if (stats)
|
|
43209
|
+
if (!stats) return;
|
|
43210
|
+
this.emit("stats", stats);
|
|
43211
|
+
this.reporter?.add({
|
|
43212
|
+
ts: Math.floor(Date.now() / 1e3),
|
|
43213
|
+
bitrateKbps: stats.bitrateKbps,
|
|
43214
|
+
fps: stats.framesPerSecond,
|
|
43215
|
+
rttMs: stats.rttMs
|
|
43216
|
+
});
|
|
43083
43217
|
}, STATS_INTERVAL_MS);
|
|
43084
43218
|
}
|
|
43085
43219
|
stopStats() {
|
|
@@ -43097,12 +43231,15 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
43097
43231
|
var FIRST_FRAME_TIMEOUT_MS = 8e3;
|
|
43098
43232
|
var MebiusPlayer = class extends TypedEmitter {
|
|
43099
43233
|
/** @internal */
|
|
43100
|
-
constructor(signaling, options = {}, deliveries = []) {
|
|
43234
|
+
constructor(signaling, options = {}, deliveries = [], telemetry = null, userId) {
|
|
43101
43235
|
super();
|
|
43236
|
+
this.telemetry = telemetry;
|
|
43237
|
+
this.userId = userId;
|
|
43102
43238
|
this.transport = null;
|
|
43103
43239
|
this.video = null;
|
|
43104
43240
|
this.statsTimer = null;
|
|
43105
43241
|
this.playing = false;
|
|
43242
|
+
this.reporter = null;
|
|
43106
43243
|
this.candidates = createViewCandidates(options.mode ?? "auto", signaling, deliveries);
|
|
43107
43244
|
}
|
|
43108
43245
|
/** Start playing `streamId` into the given video element or selector. */
|
|
@@ -43110,14 +43247,21 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
43110
43247
|
if (this.playing) return;
|
|
43111
43248
|
const video = resolveVideoElement(viewTarget);
|
|
43112
43249
|
this.video = video;
|
|
43250
|
+
const startedAtMs = Date.now();
|
|
43113
43251
|
let lastError = null;
|
|
43114
43252
|
for (const candidate of this.candidates) {
|
|
43115
43253
|
try {
|
|
43254
|
+
resetVideoElement(video);
|
|
43116
43255
|
this.attach(candidate);
|
|
43117
43256
|
await candidate.start(streamId, video);
|
|
43118
43257
|
if (await hasFirstFrame(video)) {
|
|
43119
43258
|
this.transport = candidate;
|
|
43120
43259
|
this.playing = true;
|
|
43260
|
+
if (this.telemetry) {
|
|
43261
|
+
this.reporter = new QoeReporter(this.telemetry, "play", streamId, this.userId);
|
|
43262
|
+
this.reporter.start();
|
|
43263
|
+
this.reporter.add({ ts: Math.floor(Date.now() / 1e3), firstFrameMs: Date.now() - startedAtMs });
|
|
43264
|
+
}
|
|
43121
43265
|
this.startStats();
|
|
43122
43266
|
this.emit("playing", { streamId });
|
|
43123
43267
|
return;
|
|
@@ -43134,6 +43278,8 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
43134
43278
|
/** Stop playback and detach from the video element. */
|
|
43135
43279
|
async stop() {
|
|
43136
43280
|
this.stopStats();
|
|
43281
|
+
await this.reporter?.stop();
|
|
43282
|
+
this.reporter = null;
|
|
43137
43283
|
await this.transport?.stop();
|
|
43138
43284
|
this.transport = null;
|
|
43139
43285
|
this.video = null;
|
|
@@ -43149,6 +43295,8 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
43149
43295
|
if (this.transport !== transport) return;
|
|
43150
43296
|
this.playing = false;
|
|
43151
43297
|
this.stopStats();
|
|
43298
|
+
void this.reporter?.stop();
|
|
43299
|
+
this.reporter = null;
|
|
43152
43300
|
this.emit("ended", void 0);
|
|
43153
43301
|
});
|
|
43154
43302
|
transport.onBuffering(() => {
|
|
@@ -43159,7 +43307,13 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
43159
43307
|
startStats() {
|
|
43160
43308
|
this.statsTimer = setInterval(async () => {
|
|
43161
43309
|
const stats = await this.transport?.getStats();
|
|
43162
|
-
if (stats)
|
|
43310
|
+
if (!stats) return;
|
|
43311
|
+
this.emit("stats", stats);
|
|
43312
|
+
this.reporter?.add({
|
|
43313
|
+
ts: Math.floor(Date.now() / 1e3),
|
|
43314
|
+
bitrateKbps: stats.bitrateKbps,
|
|
43315
|
+
fps: stats.framesPerSecond
|
|
43316
|
+
});
|
|
43163
43317
|
}, STATS_INTERVAL_MS2);
|
|
43164
43318
|
}
|
|
43165
43319
|
stopStats() {
|
|
@@ -43294,10 +43448,12 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
43294
43448
|
// src/client.ts
|
|
43295
43449
|
var MebiusClient = class extends TypedEmitter {
|
|
43296
43450
|
/** @internal */
|
|
43297
|
-
constructor(config2, token, deliveries = []) {
|
|
43451
|
+
constructor(config2, token, deliveries = [], telemetry = null, userId) {
|
|
43298
43452
|
super();
|
|
43299
43453
|
this.token = token;
|
|
43300
43454
|
this.deliveries = deliveries;
|
|
43455
|
+
this.telemetry = telemetry;
|
|
43456
|
+
this.userId = userId;
|
|
43301
43457
|
this.expiryTimer = null;
|
|
43302
43458
|
this.connected = false;
|
|
43303
43459
|
this.signaling = new SignalingClient(config2.gateway, token);
|
|
@@ -43322,12 +43478,12 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
43322
43478
|
/** Create a broadcaster bound to this connection. */
|
|
43323
43479
|
createBroadcaster(options = {}) {
|
|
43324
43480
|
this.assertConnected();
|
|
43325
|
-
return new MebiusBroadcaster(this.signaling, options);
|
|
43481
|
+
return new MebiusBroadcaster(this.signaling, options, this.telemetry, this.userId);
|
|
43326
43482
|
}
|
|
43327
43483
|
/** Create a player bound to this connection. */
|
|
43328
43484
|
createPlayer(options = {}) {
|
|
43329
43485
|
this.assertConnected();
|
|
43330
|
-
return new MebiusPlayer(this.signaling, options, this.deliveries);
|
|
43486
|
+
return new MebiusPlayer(this.signaling, options, this.deliveries, this.telemetry, this.userId);
|
|
43331
43487
|
}
|
|
43332
43488
|
/**
|
|
43333
43489
|
* Create a monitor: a player tuned for watching a stream you are interacting
|
|
@@ -43342,7 +43498,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
43342
43498
|
*/
|
|
43343
43499
|
createMonitor() {
|
|
43344
43500
|
this.assertConnected();
|
|
43345
|
-
return new MebiusPlayer(this.signaling, { mode: "low-latency" }, this.deliveries);
|
|
43501
|
+
return new MebiusPlayer(this.signaling, { mode: "low-latency" }, this.deliveries, this.telemetry, this.userId);
|
|
43346
43502
|
}
|
|
43347
43503
|
/** Close the connection and release resources. */
|
|
43348
43504
|
disconnect(reason) {
|
|
@@ -43375,7 +43531,8 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
43375
43531
|
throw mebiusError("UNKNOWN", "Call Mebius.init() before Mebius.connect().");
|
|
43376
43532
|
}
|
|
43377
43533
|
if (!options.token) throw mebiusError("UNKNOWN", "Mebius.connect requires a token.");
|
|
43378
|
-
const
|
|
43534
|
+
const telemetry = options.beaconToken && options.beaconUrl ? { token: options.beaconToken, url: options.beaconUrl } : null;
|
|
43535
|
+
const client = new MebiusClient(config, options.token, options.deliveries ?? [], telemetry, options.userId);
|
|
43379
43536
|
client.open();
|
|
43380
43537
|
return client;
|
|
43381
43538
|
},
|