@mebius-io/web 0.6.2 → 0.7.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/dist/index.d.cts CHANGED
@@ -34,6 +34,21 @@ interface MebiusConnectOptions {
34
34
  * The app secret must never be embedded in client code.
35
35
  */
36
36
  token: string;
37
+ /**
38
+ * Called when the current token is about to expire, to mint the next one.
39
+ * Return a fresh token from the same backend endpoint that produced `token`.
40
+ *
41
+ * WITHOUT this, a connection lives exactly as long as its token: Mebius emits
42
+ * `TOKEN_EXPIRED` at that moment and playback stops. That is fine for a short
43
+ * watch and wrong for anything unattended — a stream left running overnight,
44
+ * a lobby screen, a 24/7 broadcast — where nobody is there to reconnect.
45
+ *
46
+ * WITH it, Mebius refreshes ahead of expiry and keeps the session going
47
+ * indefinitely; a `"token-refreshed"` event is emitted each time. If the call
48
+ * fails it is retried with backoff until the old token genuinely expires, so a
49
+ * brief backend blip costs nothing.
50
+ */
51
+ getToken?: () => string | Promise<string>;
37
52
  /**
38
53
  * The `deliveries` list your backend received together with the token. Pass it
39
54
  * through as-is and Mebius will pick the best route for each viewer's device,
@@ -84,6 +99,28 @@ type PlaybackMode = "auto" | "low-latency" | "balanced" | "scale";
84
99
  interface PlayerOptions {
85
100
  /** Defaults to `"auto"` — let Mebius choose per viewer. */
86
101
  mode?: PlaybackMode;
102
+ /**
103
+ * How much delay to trade for smoothness, in milliseconds. Higher is steadier.
104
+ *
105
+ * Mebius holds roughly this much video ahead of the picture. That buffer is
106
+ * what absorbs an unsteady network: when a piece of video arrives late or has
107
+ * to be re-sent, it still lands before its turn to be shown, and the viewer
108
+ * sees nothing. With too small a buffer the same event freezes the picture —
109
+ * and a freeze is not brief, because video can only resume at the next
110
+ * complete frame, typically a second or two later. Small buffers therefore do
111
+ * not produce small glitches; they produce multi-second stalls.
112
+ *
113
+ * Pick from the viewing experience, not the number:
114
+ * - `~300` (default on the real-time route) — conversational: co-hosts, PK
115
+ * battles, anything where people talk back and delay is felt.
116
+ * - `1500`-`3000` — watching: screen shares, presentations, long unattended
117
+ * broadcasts. Costs a couple of seconds nobody notices and removes the
118
+ * stalls everybody notices.
119
+ *
120
+ * Applies to whichever route serves the viewer, so the trade you choose holds
121
+ * even when Mebius falls back to another one.
122
+ */
123
+ targetLatencyMs?: number;
87
124
  }
88
125
  /**
89
126
  * Where a player renders video: a `<video>` element, or a CSS selector that
@@ -120,8 +157,25 @@ interface PlaybackStats {
120
157
  bitrateKbps?: number;
121
158
  /** Frames per second currently being rendered, when known. */
122
159
  framesPerSecond?: number;
123
- /** Estimated end-to-end latency in milliseconds, if known. */
160
+ /**
161
+ * How far behind the source the picture is running, in milliseconds, when the
162
+ * route can measure it. This is the delay the viewer actually experiences.
163
+ */
124
164
  latencyMs?: number;
165
+ /** Round-trip time to the serving edge in milliseconds, if known. */
166
+ rttMs?: number;
167
+ /** Percentage of video that had to be re-sent or was lost, if known. */
168
+ packetLossPct?: number;
169
+ /**
170
+ * Milliseconds the picture was frozen since the previous reading, when the
171
+ * route measures this itself.
172
+ *
173
+ * Some routes must: a real-time connection can sit frozen for seconds while
174
+ * the connection reports perfect health and the video element raises no
175
+ * event, so a freeze there is invisible from the outside. A route that leaves
176
+ * this absent is one whose stalls are already visible to the player.
177
+ */
178
+ freezeMs?: number;
125
179
  }
126
180
  /** Options for {@link MebiusClient.createCaptions}. */
127
181
  interface CaptionsOptions {
@@ -187,6 +241,13 @@ type ClientEventMap = {
187
241
  reason?: string;
188
242
  };
189
243
  error: MebiusError;
244
+ /**
245
+ * A fresh access token was fetched and is now in use. Purely informational —
246
+ * playback and publishing continue uninterrupted; nothing needs to be done in
247
+ * response. Useful for logging that an unattended long-running session is
248
+ * still renewing itself.
249
+ */
250
+ "token-refreshed": void;
190
251
  };
191
252
  /** Event payloads emitted by a broadcaster. */
192
253
  type BroadcasterEventMap = {
@@ -248,8 +309,19 @@ interface SessionResult {
248
309
  }
249
310
  declare class SignalingClient {
250
311
  private readonly gateway;
251
- private readonly token;
312
+ private token;
252
313
  constructor(gateway: string, token: string);
314
+ /**
315
+ * Swap in a freshly-minted access token.
316
+ *
317
+ * Every URL this class builds is built at call time, so a session that starts
318
+ * a new request after this point uses the new token with no further wiring.
319
+ * What it does NOT reach is a request already in flight or a media URL another
320
+ * library has memorised — see the scale route's loader for that half.
321
+ */
322
+ setToken(token: string): void;
323
+ /** The current access token, for transports that must re-stamp their own URLs. */
324
+ accessToken(): string;
253
325
  private base;
254
326
  private headers;
255
327
  /** Append the access token as a query param (the form the engine enforces). */
@@ -379,13 +451,15 @@ declare class MebiusPlayer extends TypedEmitter<PlayerEventMap> {
379
451
  setVolume(volume: number): void;
380
452
  /**
381
453
  * Wall-clock time (Unix ms) currently on screen, or `null` when the active
382
- * route cannot produce one (HTTP-FLV, WHEP see {@link ViewTransport}).
454
+ * route cannot produce one. A real-time route carries no wall clock at all,
455
+ * and a segmented route has none until its first timestamped segment arrives
456
+ * (see {@link ViewTransport}).
383
457
  *
384
458
  * This is what {@link MebiusClient.createCaptions} compares against a
385
459
  * segment's `epochMs` to know when it is due. Delegating to the transport
386
460
  * rather than reading the element directly is what keeps this correct across
387
- * a route failover: the player may switch from HLS to FLV mid-session, and
388
- * the clock source has to follow.
461
+ * a route failover: the player may change route mid-session, and the clock
462
+ * source has to follow.
389
463
  */
390
464
  currentEpochMs(): number | null;
391
465
  private attach;
@@ -464,17 +538,36 @@ declare class MebiusCaptions extends TypedEmitter<CaptionsEventMap> {
464
538
  * create broadcasters and players from it.
465
539
  */
466
540
  declare class MebiusClient extends TypedEmitter<ClientEventMap> {
467
- private readonly token;
541
+ private token;
468
542
  private readonly deliveries;
469
543
  private readonly telemetry;
470
544
  private readonly userId?;
545
+ private readonly getToken?;
471
546
  private readonly signaling;
472
547
  private expiryTimer;
473
548
  private connected;
549
+ private refreshFailures;
474
550
  /** @internal */
475
- constructor(config: MebiusInitOptions, token: string, deliveries?: readonly MebiusDelivery[], telemetry?: TelemetryTarget | null, userId?: string | undefined);
551
+ constructor(config: MebiusInitOptions, token: string, deliveries?: readonly MebiusDelivery[], telemetry?: TelemetryTarget | null, userId?: string | undefined, getToken?: (() => string | Promise<string>) | undefined);
476
552
  /** @internal Called by {@link Mebius.connect}. */
477
553
  open(): void;
554
+ /**
555
+ * Arm whatever has to happen as this token approaches its expiry: renew it if
556
+ * the app gave us a way to, otherwise report that the session is over.
557
+ *
558
+ * Renewing is what makes an unattended session possible at all. The gateway
559
+ * checks the token on every media request, so without a fresh one playback
560
+ * stops the moment it expires — no matter how healthy the stream is.
561
+ */
562
+ private scheduleTokenWork;
563
+ private refreshToken;
564
+ /**
565
+ * A failed refresh is not a dead session: the current token is still valid
566
+ * until `expiryMs`, and the viewer is still watching. Retry inside that window
567
+ * and only report expiry once it has actually run out.
568
+ */
569
+ private onRefreshFailed;
570
+ private clearTimer;
478
571
  /** Create a broadcaster bound to this connection. */
479
572
  createBroadcaster(options?: BroadcasterOptions): MebiusBroadcaster;
480
573
  /** Create a player bound to this connection. */
package/dist/index.d.ts CHANGED
@@ -34,6 +34,21 @@ interface MebiusConnectOptions {
34
34
  * The app secret must never be embedded in client code.
35
35
  */
36
36
  token: string;
37
+ /**
38
+ * Called when the current token is about to expire, to mint the next one.
39
+ * Return a fresh token from the same backend endpoint that produced `token`.
40
+ *
41
+ * WITHOUT this, a connection lives exactly as long as its token: Mebius emits
42
+ * `TOKEN_EXPIRED` at that moment and playback stops. That is fine for a short
43
+ * watch and wrong for anything unattended — a stream left running overnight,
44
+ * a lobby screen, a 24/7 broadcast — where nobody is there to reconnect.
45
+ *
46
+ * WITH it, Mebius refreshes ahead of expiry and keeps the session going
47
+ * indefinitely; a `"token-refreshed"` event is emitted each time. If the call
48
+ * fails it is retried with backoff until the old token genuinely expires, so a
49
+ * brief backend blip costs nothing.
50
+ */
51
+ getToken?: () => string | Promise<string>;
37
52
  /**
38
53
  * The `deliveries` list your backend received together with the token. Pass it
39
54
  * through as-is and Mebius will pick the best route for each viewer's device,
@@ -84,6 +99,28 @@ type PlaybackMode = "auto" | "low-latency" | "balanced" | "scale";
84
99
  interface PlayerOptions {
85
100
  /** Defaults to `"auto"` — let Mebius choose per viewer. */
86
101
  mode?: PlaybackMode;
102
+ /**
103
+ * How much delay to trade for smoothness, in milliseconds. Higher is steadier.
104
+ *
105
+ * Mebius holds roughly this much video ahead of the picture. That buffer is
106
+ * what absorbs an unsteady network: when a piece of video arrives late or has
107
+ * to be re-sent, it still lands before its turn to be shown, and the viewer
108
+ * sees nothing. With too small a buffer the same event freezes the picture —
109
+ * and a freeze is not brief, because video can only resume at the next
110
+ * complete frame, typically a second or two later. Small buffers therefore do
111
+ * not produce small glitches; they produce multi-second stalls.
112
+ *
113
+ * Pick from the viewing experience, not the number:
114
+ * - `~300` (default on the real-time route) — conversational: co-hosts, PK
115
+ * battles, anything where people talk back and delay is felt.
116
+ * - `1500`-`3000` — watching: screen shares, presentations, long unattended
117
+ * broadcasts. Costs a couple of seconds nobody notices and removes the
118
+ * stalls everybody notices.
119
+ *
120
+ * Applies to whichever route serves the viewer, so the trade you choose holds
121
+ * even when Mebius falls back to another one.
122
+ */
123
+ targetLatencyMs?: number;
87
124
  }
88
125
  /**
89
126
  * Where a player renders video: a `<video>` element, or a CSS selector that
@@ -120,8 +157,25 @@ interface PlaybackStats {
120
157
  bitrateKbps?: number;
121
158
  /** Frames per second currently being rendered, when known. */
122
159
  framesPerSecond?: number;
123
- /** Estimated end-to-end latency in milliseconds, if known. */
160
+ /**
161
+ * How far behind the source the picture is running, in milliseconds, when the
162
+ * route can measure it. This is the delay the viewer actually experiences.
163
+ */
124
164
  latencyMs?: number;
165
+ /** Round-trip time to the serving edge in milliseconds, if known. */
166
+ rttMs?: number;
167
+ /** Percentage of video that had to be re-sent or was lost, if known. */
168
+ packetLossPct?: number;
169
+ /**
170
+ * Milliseconds the picture was frozen since the previous reading, when the
171
+ * route measures this itself.
172
+ *
173
+ * Some routes must: a real-time connection can sit frozen for seconds while
174
+ * the connection reports perfect health and the video element raises no
175
+ * event, so a freeze there is invisible from the outside. A route that leaves
176
+ * this absent is one whose stalls are already visible to the player.
177
+ */
178
+ freezeMs?: number;
125
179
  }
126
180
  /** Options for {@link MebiusClient.createCaptions}. */
127
181
  interface CaptionsOptions {
@@ -187,6 +241,13 @@ type ClientEventMap = {
187
241
  reason?: string;
188
242
  };
189
243
  error: MebiusError;
244
+ /**
245
+ * A fresh access token was fetched and is now in use. Purely informational —
246
+ * playback and publishing continue uninterrupted; nothing needs to be done in
247
+ * response. Useful for logging that an unattended long-running session is
248
+ * still renewing itself.
249
+ */
250
+ "token-refreshed": void;
190
251
  };
191
252
  /** Event payloads emitted by a broadcaster. */
192
253
  type BroadcasterEventMap = {
@@ -248,8 +309,19 @@ interface SessionResult {
248
309
  }
249
310
  declare class SignalingClient {
250
311
  private readonly gateway;
251
- private readonly token;
312
+ private token;
252
313
  constructor(gateway: string, token: string);
314
+ /**
315
+ * Swap in a freshly-minted access token.
316
+ *
317
+ * Every URL this class builds is built at call time, so a session that starts
318
+ * a new request after this point uses the new token with no further wiring.
319
+ * What it does NOT reach is a request already in flight or a media URL another
320
+ * library has memorised — see the scale route's loader for that half.
321
+ */
322
+ setToken(token: string): void;
323
+ /** The current access token, for transports that must re-stamp their own URLs. */
324
+ accessToken(): string;
253
325
  private base;
254
326
  private headers;
255
327
  /** Append the access token as a query param (the form the engine enforces). */
@@ -379,13 +451,15 @@ declare class MebiusPlayer extends TypedEmitter<PlayerEventMap> {
379
451
  setVolume(volume: number): void;
380
452
  /**
381
453
  * Wall-clock time (Unix ms) currently on screen, or `null` when the active
382
- * route cannot produce one (HTTP-FLV, WHEP see {@link ViewTransport}).
454
+ * route cannot produce one. A real-time route carries no wall clock at all,
455
+ * and a segmented route has none until its first timestamped segment arrives
456
+ * (see {@link ViewTransport}).
383
457
  *
384
458
  * This is what {@link MebiusClient.createCaptions} compares against a
385
459
  * segment's `epochMs` to know when it is due. Delegating to the transport
386
460
  * rather than reading the element directly is what keeps this correct across
387
- * a route failover: the player may switch from HLS to FLV mid-session, and
388
- * the clock source has to follow.
461
+ * a route failover: the player may change route mid-session, and the clock
462
+ * source has to follow.
389
463
  */
390
464
  currentEpochMs(): number | null;
391
465
  private attach;
@@ -464,17 +538,36 @@ declare class MebiusCaptions extends TypedEmitter<CaptionsEventMap> {
464
538
  * create broadcasters and players from it.
465
539
  */
466
540
  declare class MebiusClient extends TypedEmitter<ClientEventMap> {
467
- private readonly token;
541
+ private token;
468
542
  private readonly deliveries;
469
543
  private readonly telemetry;
470
544
  private readonly userId?;
545
+ private readonly getToken?;
471
546
  private readonly signaling;
472
547
  private expiryTimer;
473
548
  private connected;
549
+ private refreshFailures;
474
550
  /** @internal */
475
- constructor(config: MebiusInitOptions, token: string, deliveries?: readonly MebiusDelivery[], telemetry?: TelemetryTarget | null, userId?: string | undefined);
551
+ constructor(config: MebiusInitOptions, token: string, deliveries?: readonly MebiusDelivery[], telemetry?: TelemetryTarget | null, userId?: string | undefined, getToken?: (() => string | Promise<string>) | undefined);
476
552
  /** @internal Called by {@link Mebius.connect}. */
477
553
  open(): void;
554
+ /**
555
+ * Arm whatever has to happen as this token approaches its expiry: renew it if
556
+ * the app gave us a way to, otherwise report that the session is over.
557
+ *
558
+ * Renewing is what makes an unattended session possible at all. The gateway
559
+ * checks the token on every media request, so without a fresh one playback
560
+ * stops the moment it expires — no matter how healthy the stream is.
561
+ */
562
+ private scheduleTokenWork;
563
+ private refreshToken;
564
+ /**
565
+ * A failed refresh is not a dead session: the current token is still valid
566
+ * until `expiryMs`, and the viewer is still watching. Retry inside that window
567
+ * and only report expiry once it has actually run out.
568
+ */
569
+ private onRefreshFailed;
570
+ private clearTimer;
478
571
  /** Create a broadcaster bound to this connection. */
479
572
  createBroadcaster(options?: BroadcasterOptions): MebiusBroadcaster;
480
573
  /** Create a player bound to this connection. */