@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/README.md
CHANGED
|
@@ -55,12 +55,12 @@ external deps. `Mebius` becomes a global.
|
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
File + full PHP example: [`standalone/`](./standalone/). Raw download:
|
|
58
|
-
`https://raw.githubusercontent.com/russimobiledroidx/mebius-web-sdk/v0.
|
|
58
|
+
`https://raw.githubusercontent.com/russimobiledroidx/mebius-web-sdk/v0.4.1/packages/web/standalone/mebius.min.js`
|
|
59
59
|
|
|
60
60
|
The drop-in file is not part of the npm package — `files` ships only `dist` — so it
|
|
61
61
|
is fetched from the tag, and the tag must match the version you installed. For a
|
|
62
62
|
page with a build step, or one that can use an import map, prefer the ESM path:
|
|
63
|
-
`https://esm.sh/@mebius-io/web@0.
|
|
63
|
+
`https://esm.sh/@mebius-io/web@0.4.1`.
|
|
64
64
|
|
|
65
65
|
## Quick Start
|
|
66
66
|
|
|
@@ -162,6 +162,29 @@ Teruskan apa adanya — isinya opaque dan Mebius yang mengurutkan serta memilih.
|
|
|
162
162
|
Opsional: tanpa itu playback tetap jalan, tapi setiap penonton dilayani dari
|
|
163
163
|
origin Mebius, bukan edge terdekat.
|
|
164
164
|
|
|
165
|
+
### `beaconToken` / `beaconUrl` — quality di dashboard
|
|
166
|
+
|
|
167
|
+
Response token juga membawa dua field opsional. Teruskan keduanya dan SDK
|
|
168
|
+
melaporkan kualitas stream ini (bitrate, fps, rtt, jeda first-frame) tiap ~15
|
|
169
|
+
detik:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
const { token, deliveries, beaconToken, beaconUrl } = await (await fetch("/api/mebius-token")).json();
|
|
173
|
+
const client = Mebius.connect({ token, deliveries, beaconToken, beaconUrl });
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Itu yang mengisi **Quality → Publish / Play** di dashboard Mebius, dan yang jadi
|
|
177
|
+
dasar hitung **viewer minutes** — laporan sisi penonton adalah satu-satunya sumber
|
|
178
|
+
data itu, karena cuma client yang bisa melihat pengalaman penonton sebenarnya.
|
|
179
|
+
|
|
180
|
+
Aman di browser: kredensialnya terikat klaim bertanda tangan ke **satu** stream dan
|
|
181
|
+
**satu** project, jadi tak bisa dipakai menulis telemetri milik stream lain. SDK
|
|
182
|
+
tidak pernah tahu tenant-mu — informasi itu ada di dalam klaim, bukan di kode client.
|
|
183
|
+
|
|
184
|
+
Tanpa dua field itu stream tetap jalan normal; kamu hanya tidak melihat data
|
|
185
|
+
kualitasnya. Tambahkan `userId` di `connect()` kalau ingin laporan itu ikut membawa
|
|
186
|
+
id pengguna versimu.
|
|
187
|
+
|
|
165
188
|
## Integrasi per framework
|
|
166
189
|
|
|
167
190
|
### Vanilla JS
|
package/dist/index.cjs
CHANGED
|
@@ -188,6 +188,28 @@ var WhipPublishTransport = class {
|
|
|
188
188
|
}
|
|
189
189
|
};
|
|
190
190
|
|
|
191
|
+
// src/internal/autoplay.ts
|
|
192
|
+
function isAutoplayBlocked(e) {
|
|
193
|
+
return typeof e === "object" && e !== null && e.name === "NotAllowedError";
|
|
194
|
+
}
|
|
195
|
+
async function playWithAutoplayFallback(video) {
|
|
196
|
+
video.playsInline = true;
|
|
197
|
+
try {
|
|
198
|
+
await video.play();
|
|
199
|
+
return { mutedByPolicy: false };
|
|
200
|
+
} catch (e) {
|
|
201
|
+
if (!isAutoplayBlocked(e) || video.muted) throw e;
|
|
202
|
+
video.muted = true;
|
|
203
|
+
await video.play();
|
|
204
|
+
return { mutedByPolicy: true };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function resetVideoElement(video) {
|
|
208
|
+
video.srcObject = null;
|
|
209
|
+
video.removeAttribute("src");
|
|
210
|
+
video.load();
|
|
211
|
+
}
|
|
212
|
+
|
|
191
213
|
// src/internal/ll-view-transport.ts
|
|
192
214
|
var WhepViewTransport = class {
|
|
193
215
|
constructor(signaling) {
|
|
@@ -196,6 +218,10 @@ var WhepViewTransport = class {
|
|
|
196
218
|
this.resourceUrl = null;
|
|
197
219
|
this.endedCb = null;
|
|
198
220
|
this.bufferingCb = null;
|
|
221
|
+
/** Element this route attached a MediaStream to, so stop() can release it. */
|
|
222
|
+
this.video = null;
|
|
223
|
+
/** True when playback only started because the element had to be muted. */
|
|
224
|
+
this.mutedByPolicy = false;
|
|
199
225
|
}
|
|
200
226
|
onEnded(cb) {
|
|
201
227
|
this.endedCb = cb;
|
|
@@ -204,6 +230,7 @@ var WhepViewTransport = class {
|
|
|
204
230
|
this.bufferingCb = cb;
|
|
205
231
|
}
|
|
206
232
|
async start(streamId, video) {
|
|
233
|
+
this.video = video;
|
|
207
234
|
const pc = new RTCPeerConnection(DEFAULT_RTC_CONFIG);
|
|
208
235
|
this.pc = pc;
|
|
209
236
|
const remote = new MediaStream();
|
|
@@ -212,7 +239,9 @@ var WhepViewTransport = class {
|
|
|
212
239
|
pc.ontrack = (ev) => {
|
|
213
240
|
remote.addTrack(ev.track);
|
|
214
241
|
video.srcObject = remote;
|
|
215
|
-
void video
|
|
242
|
+
void playWithAutoplayFallback(video).then((o) => {
|
|
243
|
+
this.mutedByPolicy = o.mutedByPolicy;
|
|
244
|
+
}).catch(() => {
|
|
216
245
|
});
|
|
217
246
|
};
|
|
218
247
|
pc.onconnectionstatechange = () => {
|
|
@@ -239,6 +268,8 @@ var WhepViewTransport = class {
|
|
|
239
268
|
this.resourceUrl = null;
|
|
240
269
|
this.pc?.close();
|
|
241
270
|
this.pc = null;
|
|
271
|
+
if (this.video) resetVideoElement(this.video);
|
|
272
|
+
this.video = null;
|
|
242
273
|
}
|
|
243
274
|
async getStats() {
|
|
244
275
|
if (!this.pc) return null;
|
|
@@ -276,6 +307,8 @@ var HlsViewTransport = class {
|
|
|
276
307
|
this.video = null;
|
|
277
308
|
this.endedCb = null;
|
|
278
309
|
this.bufferingCb = null;
|
|
310
|
+
/** True when playback only started because the element had to be muted. */
|
|
311
|
+
this.mutedByPolicy = false;
|
|
279
312
|
}
|
|
280
313
|
onEnded(cb) {
|
|
281
314
|
this.endedCb = cb;
|
|
@@ -290,7 +323,7 @@ var HlsViewTransport = class {
|
|
|
290
323
|
video.addEventListener("waiting", () => this.bufferingCb?.());
|
|
291
324
|
if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
292
325
|
video.src = url;
|
|
293
|
-
await video
|
|
326
|
+
this.mutedByPolicy = (await playWithAutoplayFallback(video)).mutedByPolicy;
|
|
294
327
|
return;
|
|
295
328
|
}
|
|
296
329
|
let mod;
|
|
@@ -303,14 +336,14 @@ var HlsViewTransport = class {
|
|
|
303
336
|
if (!Hls.isSupported()) {
|
|
304
337
|
throw mebiusError("CONNECTION_FAILED", "Scale playback is not supported in this browser.");
|
|
305
338
|
}
|
|
306
|
-
const hls = new Hls(
|
|
339
|
+
const hls = new Hls();
|
|
307
340
|
this.hls = hls;
|
|
308
341
|
hls.on(Hls.Events.ERROR, (_evt, data) => {
|
|
309
342
|
if (data.fatal) this.bufferingCb?.();
|
|
310
343
|
});
|
|
311
344
|
hls.loadSource(url);
|
|
312
345
|
hls.attachMedia(video);
|
|
313
|
-
await video
|
|
346
|
+
this.mutedByPolicy = (await playWithAutoplayFallback(video)).mutedByPolicy;
|
|
314
347
|
}
|
|
315
348
|
async stop() {
|
|
316
349
|
this.hls?.destroy();
|
|
@@ -340,6 +373,8 @@ var FlvViewTransport = class {
|
|
|
340
373
|
this.video = null;
|
|
341
374
|
this.endedCb = null;
|
|
342
375
|
this.bufferingCb = null;
|
|
376
|
+
/** True when playback only started because the element had to be muted. */
|
|
377
|
+
this.mutedByPolicy = false;
|
|
343
378
|
}
|
|
344
379
|
onEnded(cb) {
|
|
345
380
|
this.endedCb = cb;
|
|
@@ -367,7 +402,7 @@ var FlvViewTransport = class {
|
|
|
367
402
|
player.on(flvjs.Events.ERROR ?? "error", () => this.bufferingCb?.());
|
|
368
403
|
player.attachMediaElement(video);
|
|
369
404
|
player.load();
|
|
370
|
-
await
|
|
405
|
+
this.mutedByPolicy = (await playWithAutoplayFallback(video)).mutedByPolicy;
|
|
371
406
|
}
|
|
372
407
|
async stop() {
|
|
373
408
|
if (this.player) {
|
|
@@ -423,17 +458,103 @@ function createViewCandidates(mode, signaling, deliveries = []) {
|
|
|
423
458
|
}
|
|
424
459
|
}
|
|
425
460
|
|
|
461
|
+
// src/internal/telemetry.ts
|
|
462
|
+
var SDK_VERSION = "web/0.4.1";
|
|
463
|
+
var FLUSH_INTERVAL_MS = 15e3;
|
|
464
|
+
var MAX_BATCH = 64;
|
|
465
|
+
function describeDevice() {
|
|
466
|
+
const nav = typeof navigator === "undefined" ? void 0 : navigator;
|
|
467
|
+
const uaData = nav?.userAgentData;
|
|
468
|
+
return { os: uaData?.platform || nav?.platform || void 0, sdk: SDK_VERSION };
|
|
469
|
+
}
|
|
470
|
+
function describeNetwork() {
|
|
471
|
+
const conn = typeof navigator === "undefined" ? void 0 : navigator.connection;
|
|
472
|
+
return conn?.effectiveType ? { type: conn.effectiveType } : void 0;
|
|
473
|
+
}
|
|
474
|
+
var QoeReporter = class {
|
|
475
|
+
constructor(target, role, streamId, userId) {
|
|
476
|
+
this.target = target;
|
|
477
|
+
this.role = role;
|
|
478
|
+
this.streamId = streamId;
|
|
479
|
+
this.userId = userId;
|
|
480
|
+
this.sessionId = randomId();
|
|
481
|
+
this.buffer = [];
|
|
482
|
+
this.timer = null;
|
|
483
|
+
this.unloadHandler = null;
|
|
484
|
+
}
|
|
485
|
+
start() {
|
|
486
|
+
if (this.timer) return;
|
|
487
|
+
this.timer = setInterval(() => void this.flush(), FLUSH_INTERVAL_MS);
|
|
488
|
+
if (typeof window !== "undefined") {
|
|
489
|
+
this.unloadHandler = () => void this.flush(true);
|
|
490
|
+
window.addEventListener("pagehide", this.unloadHandler);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
add(sample) {
|
|
494
|
+
this.buffer.push(sample);
|
|
495
|
+
if (this.buffer.length >= MAX_BATCH) void this.flush();
|
|
496
|
+
}
|
|
497
|
+
async stop() {
|
|
498
|
+
if (this.timer) clearInterval(this.timer);
|
|
499
|
+
this.timer = null;
|
|
500
|
+
if (this.unloadHandler && typeof window !== "undefined") {
|
|
501
|
+
window.removeEventListener("pagehide", this.unloadHandler);
|
|
502
|
+
}
|
|
503
|
+
this.unloadHandler = null;
|
|
504
|
+
await this.flush();
|
|
505
|
+
}
|
|
506
|
+
/** Send and clear the buffer. `beacon` uses sendBeacon, for page-unload flushes. */
|
|
507
|
+
async flush(beacon = false) {
|
|
508
|
+
if (!this.buffer.length) return;
|
|
509
|
+
const samples = this.buffer.splice(0, MAX_BATCH);
|
|
510
|
+
const body = JSON.stringify({
|
|
511
|
+
sessionId: this.sessionId,
|
|
512
|
+
streamId: this.streamId,
|
|
513
|
+
role: this.role,
|
|
514
|
+
userId: this.userId,
|
|
515
|
+
samples,
|
|
516
|
+
device: describeDevice(),
|
|
517
|
+
network: describeNetwork()
|
|
518
|
+
});
|
|
519
|
+
if (beacon && typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
520
|
+
const url = `${this.target.url}${this.target.url.includes("?") ? "&" : "?"}token=${encodeURIComponent(this.target.token)}`;
|
|
521
|
+
try {
|
|
522
|
+
navigator.sendBeacon(url, new Blob([body], { type: "application/json" }));
|
|
523
|
+
} catch {
|
|
524
|
+
}
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
try {
|
|
528
|
+
await fetch(this.target.url, {
|
|
529
|
+
method: "POST",
|
|
530
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${this.target.token}` },
|
|
531
|
+
body,
|
|
532
|
+
keepalive: true
|
|
533
|
+
});
|
|
534
|
+
} catch {
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
function randomId() {
|
|
539
|
+
const c = typeof crypto === "undefined" ? void 0 : crypto;
|
|
540
|
+
if (c?.randomUUID) return c.randomUUID();
|
|
541
|
+
return `s-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
542
|
+
}
|
|
543
|
+
|
|
426
544
|
// src/broadcaster.ts
|
|
427
545
|
var STATS_INTERVAL_MS = 2e3;
|
|
428
546
|
var MebiusBroadcaster = class extends TypedEmitter {
|
|
429
547
|
/** @internal */
|
|
430
|
-
constructor(signaling, options) {
|
|
548
|
+
constructor(signaling, options, telemetry = null, userId) {
|
|
431
549
|
super();
|
|
432
550
|
this.options = options;
|
|
551
|
+
this.telemetry = telemetry;
|
|
552
|
+
this.userId = userId;
|
|
433
553
|
this.stream = null;
|
|
434
554
|
this.facingMode = "user";
|
|
435
555
|
this.statsTimer = null;
|
|
436
556
|
this.started = false;
|
|
557
|
+
this.reporter = null;
|
|
437
558
|
this.transport = createPublishTransport(signaling);
|
|
438
559
|
}
|
|
439
560
|
/** Begin broadcasting under the given stream id. */
|
|
@@ -442,12 +563,18 @@ var MebiusBroadcaster = class extends TypedEmitter {
|
|
|
442
563
|
this.stream = await this.capture();
|
|
443
564
|
await this.transport.start(streamId, this.stream);
|
|
444
565
|
this.started = true;
|
|
566
|
+
if (this.telemetry) {
|
|
567
|
+
this.reporter = new QoeReporter(this.telemetry, "pub", streamId, this.userId);
|
|
568
|
+
this.reporter.start();
|
|
569
|
+
}
|
|
445
570
|
this.startStats();
|
|
446
571
|
this.emit("started", { streamId });
|
|
447
572
|
}
|
|
448
573
|
/** Stop broadcasting and release the camera/microphone. */
|
|
449
574
|
async stop() {
|
|
450
575
|
this.stopStats();
|
|
576
|
+
await this.reporter?.stop();
|
|
577
|
+
this.reporter = null;
|
|
451
578
|
await this.transport.stop();
|
|
452
579
|
this.stream?.getTracks().forEach((t) => t.stop());
|
|
453
580
|
this.stream = null;
|
|
@@ -503,7 +630,14 @@ var MebiusBroadcaster = class extends TypedEmitter {
|
|
|
503
630
|
startStats() {
|
|
504
631
|
this.statsTimer = setInterval(async () => {
|
|
505
632
|
const stats = await this.transport.getStats();
|
|
506
|
-
if (stats)
|
|
633
|
+
if (!stats) return;
|
|
634
|
+
this.emit("stats", stats);
|
|
635
|
+
this.reporter?.add({
|
|
636
|
+
ts: Math.floor(Date.now() / 1e3),
|
|
637
|
+
bitrateKbps: stats.bitrateKbps,
|
|
638
|
+
fps: stats.framesPerSecond,
|
|
639
|
+
rttMs: stats.rttMs
|
|
640
|
+
});
|
|
507
641
|
}, STATS_INTERVAL_MS);
|
|
508
642
|
}
|
|
509
643
|
stopStats() {
|
|
@@ -521,12 +655,15 @@ var STATS_INTERVAL_MS2 = 2e3;
|
|
|
521
655
|
var FIRST_FRAME_TIMEOUT_MS = 8e3;
|
|
522
656
|
var MebiusPlayer = class extends TypedEmitter {
|
|
523
657
|
/** @internal */
|
|
524
|
-
constructor(signaling, options = {}, deliveries = []) {
|
|
658
|
+
constructor(signaling, options = {}, deliveries = [], telemetry = null, userId) {
|
|
525
659
|
super();
|
|
660
|
+
this.telemetry = telemetry;
|
|
661
|
+
this.userId = userId;
|
|
526
662
|
this.transport = null;
|
|
527
663
|
this.video = null;
|
|
528
664
|
this.statsTimer = null;
|
|
529
665
|
this.playing = false;
|
|
666
|
+
this.reporter = null;
|
|
530
667
|
this.candidates = createViewCandidates(options.mode ?? "auto", signaling, deliveries);
|
|
531
668
|
}
|
|
532
669
|
/** Start playing `streamId` into the given video element or selector. */
|
|
@@ -534,14 +671,21 @@ var MebiusPlayer = class extends TypedEmitter {
|
|
|
534
671
|
if (this.playing) return;
|
|
535
672
|
const video = resolveVideoElement(viewTarget);
|
|
536
673
|
this.video = video;
|
|
674
|
+
const startedAtMs = Date.now();
|
|
537
675
|
let lastError = null;
|
|
538
676
|
for (const candidate of this.candidates) {
|
|
539
677
|
try {
|
|
678
|
+
resetVideoElement(video);
|
|
540
679
|
this.attach(candidate);
|
|
541
680
|
await candidate.start(streamId, video);
|
|
542
681
|
if (await hasFirstFrame(video)) {
|
|
543
682
|
this.transport = candidate;
|
|
544
683
|
this.playing = true;
|
|
684
|
+
if (this.telemetry) {
|
|
685
|
+
this.reporter = new QoeReporter(this.telemetry, "play", streamId, this.userId);
|
|
686
|
+
this.reporter.start();
|
|
687
|
+
this.reporter.add({ ts: Math.floor(Date.now() / 1e3), firstFrameMs: Date.now() - startedAtMs });
|
|
688
|
+
}
|
|
545
689
|
this.startStats();
|
|
546
690
|
this.emit("playing", { streamId });
|
|
547
691
|
return;
|
|
@@ -558,6 +702,8 @@ var MebiusPlayer = class extends TypedEmitter {
|
|
|
558
702
|
/** Stop playback and detach from the video element. */
|
|
559
703
|
async stop() {
|
|
560
704
|
this.stopStats();
|
|
705
|
+
await this.reporter?.stop();
|
|
706
|
+
this.reporter = null;
|
|
561
707
|
await this.transport?.stop();
|
|
562
708
|
this.transport = null;
|
|
563
709
|
this.video = null;
|
|
@@ -573,6 +719,8 @@ var MebiusPlayer = class extends TypedEmitter {
|
|
|
573
719
|
if (this.transport !== transport) return;
|
|
574
720
|
this.playing = false;
|
|
575
721
|
this.stopStats();
|
|
722
|
+
void this.reporter?.stop();
|
|
723
|
+
this.reporter = null;
|
|
576
724
|
this.emit("ended", void 0);
|
|
577
725
|
});
|
|
578
726
|
transport.onBuffering(() => {
|
|
@@ -583,7 +731,13 @@ var MebiusPlayer = class extends TypedEmitter {
|
|
|
583
731
|
startStats() {
|
|
584
732
|
this.statsTimer = setInterval(async () => {
|
|
585
733
|
const stats = await this.transport?.getStats();
|
|
586
|
-
if (stats)
|
|
734
|
+
if (!stats) return;
|
|
735
|
+
this.emit("stats", stats);
|
|
736
|
+
this.reporter?.add({
|
|
737
|
+
ts: Math.floor(Date.now() / 1e3),
|
|
738
|
+
bitrateKbps: stats.bitrateKbps,
|
|
739
|
+
fps: stats.framesPerSecond
|
|
740
|
+
});
|
|
587
741
|
}, STATS_INTERVAL_MS2);
|
|
588
742
|
}
|
|
589
743
|
stopStats() {
|
|
@@ -718,10 +872,12 @@ function readToken(token) {
|
|
|
718
872
|
// src/client.ts
|
|
719
873
|
var MebiusClient = class extends TypedEmitter {
|
|
720
874
|
/** @internal */
|
|
721
|
-
constructor(config2, token, deliveries = []) {
|
|
875
|
+
constructor(config2, token, deliveries = [], telemetry = null, userId) {
|
|
722
876
|
super();
|
|
723
877
|
this.token = token;
|
|
724
878
|
this.deliveries = deliveries;
|
|
879
|
+
this.telemetry = telemetry;
|
|
880
|
+
this.userId = userId;
|
|
725
881
|
this.expiryTimer = null;
|
|
726
882
|
this.connected = false;
|
|
727
883
|
this.signaling = new SignalingClient(config2.gateway, token);
|
|
@@ -746,12 +902,12 @@ var MebiusClient = class extends TypedEmitter {
|
|
|
746
902
|
/** Create a broadcaster bound to this connection. */
|
|
747
903
|
createBroadcaster(options = {}) {
|
|
748
904
|
this.assertConnected();
|
|
749
|
-
return new MebiusBroadcaster(this.signaling, options);
|
|
905
|
+
return new MebiusBroadcaster(this.signaling, options, this.telemetry, this.userId);
|
|
750
906
|
}
|
|
751
907
|
/** Create a player bound to this connection. */
|
|
752
908
|
createPlayer(options = {}) {
|
|
753
909
|
this.assertConnected();
|
|
754
|
-
return new MebiusPlayer(this.signaling, options, this.deliveries);
|
|
910
|
+
return new MebiusPlayer(this.signaling, options, this.deliveries, this.telemetry, this.userId);
|
|
755
911
|
}
|
|
756
912
|
/**
|
|
757
913
|
* Create a monitor: a player tuned for watching a stream you are interacting
|
|
@@ -766,7 +922,7 @@ var MebiusClient = class extends TypedEmitter {
|
|
|
766
922
|
*/
|
|
767
923
|
createMonitor() {
|
|
768
924
|
this.assertConnected();
|
|
769
|
-
return new MebiusPlayer(this.signaling, { mode: "low-latency" }, this.deliveries);
|
|
925
|
+
return new MebiusPlayer(this.signaling, { mode: "low-latency" }, this.deliveries, this.telemetry, this.userId);
|
|
770
926
|
}
|
|
771
927
|
/** Close the connection and release resources. */
|
|
772
928
|
disconnect(reason) {
|
|
@@ -799,7 +955,8 @@ var Mebius = {
|
|
|
799
955
|
throw mebiusError("UNKNOWN", "Call Mebius.init() before Mebius.connect().");
|
|
800
956
|
}
|
|
801
957
|
if (!options.token) throw mebiusError("UNKNOWN", "Mebius.connect requires a token.");
|
|
802
|
-
const
|
|
958
|
+
const telemetry = options.beaconToken && options.beaconUrl ? { token: options.beaconToken, url: options.beaconUrl } : null;
|
|
959
|
+
const client = new MebiusClient(config, options.token, options.deliveries ?? [], telemetry, options.userId);
|
|
803
960
|
client.open();
|
|
804
961
|
return client;
|
|
805
962
|
},
|