@mebius-io/web 0.1.1 → 0.1.3
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 +4 -3
- package/dist/index.cjs +20 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -5
- package/dist/index.d.ts +12 -5
- package/dist/index.global.js +20 -14
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +20 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ npm i @mebius-io/web
|
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Package berisi `dist/` (ESM + CJS + UMD + types) dan menarik dependency runtime
|
|
24
|
-
|
|
24
|
+
playback-nya otomatis. Tidak perlu build di sisi consumer.
|
|
25
25
|
|
|
26
26
|
```ts
|
|
27
27
|
const { Mebius } = require("@mebius-io/web"); // CJS — works
|
|
@@ -191,8 +191,9 @@ export function useWatch(streamId: string) {
|
|
|
191
191
|
|
|
192
192
|
### Vite
|
|
193
193
|
|
|
194
|
-
Tidak ada config khusus. ESM langsung jalan;
|
|
195
|
-
|
|
194
|
+
Tidak ada config khusus. ESM langsung jalan; engine playback untuk mode
|
|
195
|
+
`"scale"` di-load lazy hanya saat mode itu dipakai, jadi tidak menambah bundle
|
|
196
|
+
low-latency.
|
|
196
197
|
|
|
197
198
|
## API Reference
|
|
198
199
|
|
package/dist/index.cjs
CHANGED
|
@@ -146,13 +146,13 @@ var WhipPublishTransport = class {
|
|
|
146
146
|
await waitForIceGathering(pc);
|
|
147
147
|
const localSdp = pc.localDescription?.sdp;
|
|
148
148
|
if (!localSdp) throw mebiusError("CONNECTION_FAILED", "Failed to create a local session.");
|
|
149
|
-
const {
|
|
150
|
-
"
|
|
149
|
+
const { answer, resourceUrl } = await this.signaling.exchangeSession(
|
|
150
|
+
"publish",
|
|
151
151
|
streamId,
|
|
152
152
|
localSdp
|
|
153
153
|
);
|
|
154
154
|
this.resourceUrl = resourceUrl;
|
|
155
|
-
await pc.setRemoteDescription({ type: "answer", sdp:
|
|
155
|
+
await pc.setRemoteDescription({ type: "answer", sdp: answer });
|
|
156
156
|
}
|
|
157
157
|
async replaceVideoTrack(track) {
|
|
158
158
|
const sender = this.pc?.getSenders().find((s) => s.track?.kind === "video");
|
|
@@ -226,13 +226,13 @@ var WhepViewTransport = class {
|
|
|
226
226
|
await waitForIceGathering(pc);
|
|
227
227
|
const localSdp = pc.localDescription?.sdp;
|
|
228
228
|
if (!localSdp) throw mebiusError("CONNECTION_FAILED", "Failed to create a local session.");
|
|
229
|
-
const {
|
|
230
|
-
"
|
|
229
|
+
const { answer, resourceUrl } = await this.signaling.exchangeSession(
|
|
230
|
+
"view",
|
|
231
231
|
streamId,
|
|
232
232
|
localSdp
|
|
233
233
|
);
|
|
234
234
|
this.resourceUrl = resourceUrl;
|
|
235
|
-
await pc.setRemoteDescription({ type: "answer", sdp:
|
|
235
|
+
await pc.setRemoteDescription({ type: "answer", sdp: answer });
|
|
236
236
|
}
|
|
237
237
|
async stop() {
|
|
238
238
|
await this.signaling.deleteResource(this.resourceUrl);
|
|
@@ -579,21 +579,27 @@ var SignalingClient = class {
|
|
|
579
579
|
balancedStreamUrl(streamId) {
|
|
580
580
|
return this.withToken(`${this.base()}/flv/${encodeURIComponent(streamId)}.flv`);
|
|
581
581
|
}
|
|
582
|
-
//
|
|
583
|
-
// view
|
|
584
|
-
//
|
|
582
|
+
// Maps a neutral session kind to the concrete signaling path segment. This
|
|
583
|
+
// mapping (publish -> WHIP, view -> WHEP) lives ONLY in this method body, so
|
|
584
|
+
// the protocol names never appear in any exported type signature.
|
|
585
|
+
pathFor(kind) {
|
|
586
|
+
return kind === "publish" ? "whip" : "whep";
|
|
587
|
+
}
|
|
588
|
+
// Performs the offer/answer exchange for a publish or a low-latency view
|
|
589
|
+
// session. Protocol detail kept inside the method body so it never leaks into
|
|
590
|
+
// the bundled public .d.ts.
|
|
585
591
|
/**
|
|
586
592
|
* Run the session offer/answer exchange. Throws a {@link MebiusError} with a
|
|
587
593
|
* Mebius-flavored code on failure — never the raw protocol name.
|
|
588
594
|
*/
|
|
589
|
-
async
|
|
590
|
-
const url = this.withToken(`${this.base()}/${kind}/${encodeURIComponent(streamId)}`);
|
|
595
|
+
async exchangeSession(kind, streamId, offer) {
|
|
596
|
+
const url = this.withToken(`${this.base()}/${this.pathFor(kind)}/${encodeURIComponent(streamId)}`);
|
|
591
597
|
let res;
|
|
592
598
|
try {
|
|
593
599
|
res = await fetch(url, {
|
|
594
600
|
method: "POST",
|
|
595
601
|
headers: this.headers("application/sdp"),
|
|
596
|
-
body:
|
|
602
|
+
body: offer
|
|
597
603
|
});
|
|
598
604
|
} catch (cause) {
|
|
599
605
|
throw mebiusError("CONNECTION_FAILED", void 0, cause);
|
|
@@ -607,10 +613,10 @@ var SignalingClient = class {
|
|
|
607
613
|
if (!res.ok) {
|
|
608
614
|
throw mebiusError("CONNECTION_FAILED", `Mebius gateway returned ${res.status}.`);
|
|
609
615
|
}
|
|
610
|
-
const
|
|
616
|
+
const answer = await res.text();
|
|
611
617
|
const location = res.headers.get("Location");
|
|
612
618
|
const resourceUrl = location ? new URL(location, url).toString() : null;
|
|
613
|
-
return {
|
|
619
|
+
return { answer, resourceUrl };
|
|
614
620
|
}
|
|
615
621
|
/** Tear down a previously-created session resource. Best-effort. */
|
|
616
622
|
async deleteResource(resourceUrl) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/events.ts","../src/internal/view-target.ts","../src/internal/webrtc-util.ts","../src/internal/publish-transport.ts","../src/internal/ll-view-transport.ts","../src/internal/balanced-view-transport.ts","../src/internal/scale-view-transport.ts","../src/internal/transport.ts","../src/broadcaster.ts","../src/player.ts","../src/internal/signaling.ts","../src/internal/token.ts","../src/client.ts","../src/mebius.ts"],"sourcesContent":["/**\n * @mebius-io/web — Mebius Client SDK for the web.\n *\n * Public surface only. Everything under `internal/` is private and is never\n * re-exported here.\n */\nexport { Mebius } from \"./mebius.js\";\nexport { MebiusClient } from \"./client.js\";\nexport { MebiusBroadcaster } from \"./broadcaster.js\";\nexport { MebiusPlayer } from \"./player.js\";\nexport { MebiusError, mebiusError } from \"./errors.js\";\n\nexport type {\n ClientEventMap,\n BroadcasterEventMap,\n PlayerEventMap,\n} from \"./events.js\";\n\nexport type {\n MebiusInitOptions,\n MebiusConnectOptions,\n BroadcasterOptions,\n PlayerOptions,\n PlaybackMode,\n ViewTarget,\n MediaConstraint,\n BroadcastStats,\n PlaybackStats,\n MebiusErrorCode,\n} from \"./types.js\";\n","import type { MebiusErrorCode } from \"./types.js\";\n\n/**\n * The single error type the SDK surfaces. Always uses Mebius terminology —\n * never raw transport/protocol wording.\n *\n * Inspect {@link MebiusError.code} to decide how to recover (for example,\n * refresh the token on `\"TOKEN_EXPIRED\"`).\n */\nexport class MebiusError extends Error {\n readonly code: MebiusErrorCode;\n /** The underlying cause, if any. Useful for logging, opaque to the contract. */\n readonly cause?: unknown;\n\n constructor(code: MebiusErrorCode, message: string, cause?: unknown) {\n super(message);\n this.name = \"MebiusError\";\n this.code = code;\n this.cause = cause;\n Object.setPrototypeOf(this, MebiusError.prototype);\n }\n}\n\n/** Human-readable default messages, kept in Mebius terms. */\nconst DEFAULT_MESSAGES: Record<MebiusErrorCode, string> = {\n TOKEN_EXPIRED: \"Your Mebius token has expired. Mint a fresh token and reconnect.\",\n PERMISSION_DENIED: \"Camera/microphone permission was denied by the user or browser.\",\n CONNECTION_FAILED: \"Could not establish a connection to the Mebius gateway.\",\n NOT_CONNECTED: \"Not connected to Mebius. Call connect() before using this client.\",\n STREAM_NOT_FOUND: \"The requested stream could not be found on the Mebius gateway.\",\n UNKNOWN: \"An unexpected Mebius error occurred.\",\n};\n\n/** Create a {@link MebiusError}, falling back to a sensible default message. */\nexport function mebiusError(\n code: MebiusErrorCode,\n message?: string,\n cause?: unknown,\n): MebiusError {\n return new MebiusError(code, message ?? DEFAULT_MESSAGES[code], cause);\n}\n","import type { MebiusError } from \"./errors.js\";\nimport type { BroadcastStats, PlaybackStats } from \"./types.js\";\n\n// NOTE: these are `type` aliases (not interfaces) so they satisfy the\n// `Record<string, unknown>` constraint on TypedEmitter — TS only treats object\n// type aliases (not augmentable interfaces) as having an implicit index\n// signature.\n\n/** Event payloads emitted by {@link MebiusClient}. */\nexport type ClientEventMap = {\n connected: void;\n disconnected: { reason?: string };\n error: MebiusError;\n};\n\n/** Event payloads emitted by a broadcaster. */\nexport type BroadcasterEventMap = {\n started: { streamId: string };\n stopped: void;\n stats: BroadcastStats;\n};\n\n/** Event payloads emitted by a player. */\nexport type PlayerEventMap = {\n playing: { streamId: string };\n buffering: void;\n ended: void;\n stats: PlaybackStats;\n};\n\ntype Listener<T> = (payload: T) => void;\n\n/**\n * A tiny strongly-typed event emitter. `EventMap` maps each event name to its\n * payload type, so `on(\"started\", cb)` infers `cb`'s argument automatically.\n */\nexport class TypedEmitter<EventMap extends Record<string, unknown>> {\n private readonly listeners = new Map<keyof EventMap, Set<Listener<unknown>>>();\n\n /** Subscribe to an event. Returns an unsubscribe function. */\n on<K extends keyof EventMap>(event: K, cb: Listener<EventMap[K]>): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(cb as Listener<unknown>);\n return () => this.off(event, cb);\n }\n\n /** Unsubscribe a previously-registered listener. */\n off<K extends keyof EventMap>(event: K, cb: Listener<EventMap[K]>): void {\n this.listeners.get(event)?.delete(cb as Listener<unknown>);\n }\n\n /** Emit an event to all listeners. Internal use. */\n protected emit<K extends keyof EventMap>(event: K, payload: EventMap[K]): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const cb of [...set]) (cb as Listener<EventMap[K]>)(payload);\n }\n\n /** Remove every listener. Internal use during teardown. */\n protected removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","/** INTERNAL — resolve a public {@link ViewTarget} to a real `<video>` element. */\nimport { mebiusError } from \"../errors.js\";\nimport type { ViewTarget } from \"../types.js\";\n\nexport function resolveVideoElement(target: ViewTarget): HTMLVideoElement {\n if (typeof target !== \"string\") {\n if (target instanceof HTMLVideoElement) return target;\n throw mebiusError(\"UNKNOWN\", \"View target must be a <video> element or a CSS selector.\");\n }\n const el = document.querySelector(target);\n if (!el) {\n throw mebiusError(\"UNKNOWN\", `No element matches the selector \"${target}\".`);\n }\n if (!(el instanceof HTMLVideoElement)) {\n throw mebiusError(\"UNKNOWN\", `Selector \"${target}\" did not resolve to a <video> element.`);\n }\n return el;\n}\n","/** INTERNAL — small WebRTC helpers shared by publish/view transports. */\n\n/**\n * Wait until ICE gathering completes (or a short timeout elapses) so the SDP\n * we send already contains candidates. Keeps the gateway exchange to a single\n * round-trip.\n */\nexport function waitForIceGathering(pc: RTCPeerConnection, timeoutMs = 2000): Promise<void> {\n if (pc.iceGatheringState === \"complete\") return Promise.resolve();\n return new Promise((resolve) => {\n const done = () => {\n pc.removeEventListener(\"icegatheringstatechange\", check);\n clearTimeout(timer);\n resolve();\n };\n const check = () => {\n if (pc.iceGatheringState === \"complete\") done();\n };\n const timer = setTimeout(done, timeoutMs);\n pc.addEventListener(\"icegatheringstatechange\", check);\n });\n}\n\n/** Default ICE configuration. The gateway may also relay; STUN aids direct paths. */\nexport const DEFAULT_RTC_CONFIG: RTCConfiguration = {\n iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }],\n};\n","/**\n * INTERNAL — publish transport (WHIP over the gateway).\n *\n * Sends a locally-captured MediaStream to the Mebius gateway via a standard\n * WHIP offer/answer exchange. Hidden from the public API.\n */\nimport type { BroadcastStats } from \"../types.js\";\nimport { mebiusError } from \"../errors.js\";\nimport type { SignalingClient } from \"./signaling.js\";\nimport type { PublishTransport } from \"./transport.js\";\nimport { DEFAULT_RTC_CONFIG, waitForIceGathering } from \"./webrtc-util.js\";\n\nexport class WhipPublishTransport implements PublishTransport {\n private pc: RTCPeerConnection | null = null;\n private resourceUrl: string | null = null;\n\n constructor(private readonly signaling: SignalingClient) {}\n\n async start(streamId: string, stream: MediaStream): Promise<void> {\n const pc = new RTCPeerConnection(DEFAULT_RTC_CONFIG);\n this.pc = pc;\n\n for (const track of stream.getTracks()) {\n pc.addTrack(track, stream);\n }\n\n const offer = await pc.createOffer();\n await pc.setLocalDescription(offer);\n await waitForIceGathering(pc);\n\n const localSdp = pc.localDescription?.sdp;\n if (!localSdp) throw mebiusError(\"CONNECTION_FAILED\", \"Failed to create a local session.\");\n\n const { answerSdp, resourceUrl } = await this.signaling.exchangeSdp(\n \"whip\",\n streamId,\n localSdp,\n );\n this.resourceUrl = resourceUrl;\n await pc.setRemoteDescription({ type: \"answer\", sdp: answerSdp });\n }\n\n async replaceVideoTrack(track: MediaStreamTrack | null): Promise<void> {\n const sender = this.pc?.getSenders().find((s) => s.track?.kind === \"video\");\n if (sender) await sender.replaceTrack(track);\n }\n\n async stop(): Promise<void> {\n await this.signaling.deleteResource(this.resourceUrl);\n this.resourceUrl = null;\n this.pc?.getSenders().forEach((s) => s.track?.stop());\n this.pc?.close();\n this.pc = null;\n }\n\n async getStats(): Promise<BroadcastStats | null> {\n if (!this.pc) return null;\n const report = await this.pc.getStats();\n let bitrateKbps = 0;\n let framesPerSecond = 0;\n let rttMs: number | undefined;\n report.forEach((stat) => {\n if (stat.type === \"outbound-rtp\" && !stat.isRemote) {\n if (typeof stat.framesPerSecond === \"number\") framesPerSecond = stat.framesPerSecond;\n }\n if (stat.type === \"candidate-pair\" && stat.state === \"succeeded\") {\n if (typeof stat.availableOutgoingBitrate === \"number\") {\n bitrateKbps = Math.round(stat.availableOutgoingBitrate / 1000);\n }\n if (typeof stat.currentRoundTripTime === \"number\") {\n rttMs = Math.round(stat.currentRoundTripTime * 1000);\n }\n }\n });\n return { bitrateKbps, framesPerSecond, rttMs };\n }\n}\n","/**\n * INTERNAL — low-latency view transport (WHEP over the gateway).\n *\n * Pulls a remote stream from the Mebius gateway via a standard WHEP exchange\n * and renders it into a video element. Hidden from the public API.\n */\nimport type { PlaybackStats } from \"../types.js\";\nimport { mebiusError } from \"../errors.js\";\nimport type { SignalingClient } from \"./signaling.js\";\nimport type { ViewTransport } from \"./transport.js\";\nimport { DEFAULT_RTC_CONFIG, waitForIceGathering } from \"./webrtc-util.js\";\n\nexport class WhepViewTransport implements ViewTransport {\n private pc: RTCPeerConnection | null = null;\n private resourceUrl: string | null = null;\n private endedCb: (() => void) | null = null;\n private bufferingCb: (() => void) | null = null;\n\n constructor(private readonly signaling: SignalingClient) {}\n\n onEnded(cb: () => void): void {\n this.endedCb = cb;\n }\n\n onBuffering(cb: () => void): void {\n this.bufferingCb = cb;\n }\n\n async start(streamId: string, video: HTMLVideoElement): Promise<void> {\n const pc = new RTCPeerConnection(DEFAULT_RTC_CONFIG);\n this.pc = pc;\n const remote = new MediaStream();\n\n pc.addTransceiver(\"video\", { direction: \"recvonly\" });\n pc.addTransceiver(\"audio\", { direction: \"recvonly\" });\n\n pc.ontrack = (ev) => {\n remote.addTrack(ev.track);\n video.srcObject = remote;\n void video.play().catch(() => {\n /* autoplay may require a user gesture; left to the app */\n });\n };\n pc.onconnectionstatechange = () => {\n if (pc.connectionState === \"disconnected\" || pc.connectionState === \"failed\") {\n this.bufferingCb?.();\n }\n if (pc.connectionState === \"closed\") this.endedCb?.();\n };\n\n const offer = await pc.createOffer();\n await pc.setLocalDescription(offer);\n await waitForIceGathering(pc);\n\n const localSdp = pc.localDescription?.sdp;\n if (!localSdp) throw mebiusError(\"CONNECTION_FAILED\", \"Failed to create a local session.\");\n\n const { answerSdp, resourceUrl } = await this.signaling.exchangeSdp(\n \"whep\",\n streamId,\n localSdp,\n );\n this.resourceUrl = resourceUrl;\n await pc.setRemoteDescription({ type: \"answer\", sdp: answerSdp });\n }\n\n async stop(): Promise<void> {\n await this.signaling.deleteResource(this.resourceUrl);\n this.resourceUrl = null;\n this.pc?.close();\n this.pc = null;\n }\n\n async getStats(): Promise<PlaybackStats | null> {\n if (!this.pc) return null;\n const report = await this.pc.getStats();\n let bitrateKbps = 0;\n let framesPerSecond = 0;\n let latencyMs: number | undefined;\n report.forEach((stat) => {\n if (stat.type === \"inbound-rtp\") {\n if (typeof stat.framesPerSecond === \"number\") framesPerSecond = stat.framesPerSecond;\n if (typeof stat.jitter === \"number\") latencyMs = Math.round(stat.jitter * 1000);\n }\n if (stat.type === \"candidate-pair\" && stat.state === \"succeeded\") {\n if (typeof stat.availableIncomingBitrate === \"number\") {\n bitrateKbps = Math.round(stat.availableIncomingBitrate / 1000);\n }\n }\n });\n return { bitrateKbps, framesPerSecond, latencyMs };\n }\n}\n","/**\n * INTERNAL — balanced view transport (HTTP-FLV via flv.js).\n *\n * For typical one-to-many web viewers Mebius pulls an HTTP-FLV stream and feeds\n * it through flv.js (Media Source Extensions). Latency is ~1-3s — lower than\n * HLS, higher than the WebRTC pull — and it scales over a CDN edge. flv.js is\n * loaded lazily so it never weighs down apps that don't use this mode.\n *\n * Browser-only: flv.js needs MSE, which iOS Safari lacks. Hidden from the\n * public API; the public surface speaks only of the `\"balanced\"` playback mode.\n */\nimport type { PlaybackStats } from \"../types.js\";\nimport { mebiusError } from \"../errors.js\";\nimport type { SignalingClient } from \"./signaling.js\";\nimport type { ViewTransport } from \"./transport.js\";\n\n// Minimal structural typing for the parts of flv.js we use, declared locally so\n// this package type-checks WITHOUT flv.js installed (it is an optional\n// dependency the host app provides).\ninterface FlvPlayer {\n attachMediaElement(el: HTMLVideoElement): void;\n load(): void;\n play(): Promise<void> | void;\n unload(): void;\n detachMediaElement(): void;\n destroy(): void;\n on(event: string, cb: (...args: unknown[]) => void): void;\n}\ninterface FlvModule {\n isSupported(): boolean;\n createPlayer(config: { type: string; url: string; isLive?: boolean }): FlvPlayer;\n Events: Record<string, string>;\n}\n\nexport class FlvViewTransport implements ViewTransport {\n private player: FlvPlayer | null = null;\n private video: HTMLVideoElement | null = null;\n private endedCb: (() => void) | null = null;\n private bufferingCb: (() => void) | null = null;\n\n constructor(private readonly signaling: SignalingClient) {}\n\n onEnded(cb: () => void): void {\n this.endedCb = cb;\n }\n\n onBuffering(cb: () => void): void {\n this.bufferingCb = cb;\n }\n\n async start(streamId: string, video: HTMLVideoElement): Promise<void> {\n this.video = video;\n const url = this.signaling.balancedStreamUrl(streamId);\n\n video.addEventListener(\"ended\", () => this.endedCb?.());\n video.addEventListener(\"waiting\", () => this.bufferingCb?.());\n\n let mod: { default: FlvModule };\n try {\n // Non-literal specifier so the optional dependency is resolved at runtime\n // and does not break type-checking when it is not installed.\n const spec = \"flv.js\";\n mod = (await import(/* @vite-ignore */ spec)) as { default: FlvModule };\n } catch (cause) {\n throw mebiusError(\"CONNECTION_FAILED\", \"Balanced playback support failed to load.\", cause);\n }\n\n const flvjs = mod.default;\n if (!flvjs.isSupported()) {\n throw mebiusError(\"CONNECTION_FAILED\", \"Balanced playback is not supported in this browser.\");\n }\n\n const player = flvjs.createPlayer({ type: \"flv\", url, isLive: true });\n this.player = player;\n player.on(flvjs.Events.ERROR ?? \"error\", () => this.bufferingCb?.());\n player.attachMediaElement(video);\n player.load();\n await Promise.resolve(player.play()).catch(() => undefined);\n }\n\n async stop(): Promise<void> {\n if (this.player) {\n this.player.unload();\n this.player.detachMediaElement();\n this.player.destroy();\n this.player = null;\n }\n if (this.video) {\n this.video.removeAttribute(\"src\");\n this.video.load();\n }\n this.video = null;\n }\n\n async getStats(): Promise<PlaybackStats | null> {\n if (!this.video) return null;\n return {\n bitrateKbps: 0,\n framesPerSecond: 0,\n latencyMs: undefined,\n };\n }\n}\n","/**\n * INTERNAL — scale view transport (HLS via hls.js / native).\n *\n * For large-audience playback Mebius delivers an HLS playlist from the\n * gateway. hls.js is loaded lazily; Safari plays the playlist natively.\n * Hidden from the public API.\n */\nimport type { PlaybackStats } from \"../types.js\";\nimport { mebiusError } from \"../errors.js\";\nimport type { SignalingClient } from \"./signaling.js\";\nimport type { ViewTransport } from \"./transport.js\";\n\n// Loaded on demand so it never weighs down low-latency-only apps.\ntype HlsModule = typeof import(\"hls.js\");\ntype HlsInstance = import(\"hls.js\").default;\n\nexport class HlsViewTransport implements ViewTransport {\n private hls: HlsInstance | null = null;\n private video: HTMLVideoElement | null = null;\n private endedCb: (() => void) | null = null;\n private bufferingCb: (() => void) | null = null;\n\n constructor(private readonly signaling: SignalingClient) {}\n\n onEnded(cb: () => void): void {\n this.endedCb = cb;\n }\n\n onBuffering(cb: () => void): void {\n this.bufferingCb = cb;\n }\n\n async start(streamId: string, video: HTMLVideoElement): Promise<void> {\n this.video = video;\n const url = this.signaling.scalePlaylistUrl(streamId);\n\n video.addEventListener(\"ended\", () => this.endedCb?.());\n video.addEventListener(\"waiting\", () => this.bufferingCb?.());\n\n // Safari and iOS play HLS natively — no library needed.\n if (video.canPlayType(\"application/vnd.apple.mpegurl\")) {\n video.src = url;\n await video.play().catch(() => undefined);\n return;\n }\n\n let mod: HlsModule;\n try {\n mod = await import(\"hls.js\");\n } catch (cause) {\n throw mebiusError(\"CONNECTION_FAILED\", \"Scale playback support failed to load.\", cause);\n }\n const Hls = mod.default;\n if (!Hls.isSupported()) {\n throw mebiusError(\"CONNECTION_FAILED\", \"Scale playback is not supported in this browser.\");\n }\n\n const hls = new Hls({ lowLatencyMode: true });\n this.hls = hls;\n hls.on(Hls.Events.ERROR, (_evt, data) => {\n if (data.fatal) this.bufferingCb?.();\n });\n hls.loadSource(url);\n hls.attachMedia(video);\n await video.play().catch(() => undefined);\n }\n\n async stop(): Promise<void> {\n this.hls?.destroy();\n this.hls = null;\n if (this.video) {\n this.video.removeAttribute(\"src\");\n this.video.load();\n }\n this.video = null;\n }\n\n async getStats(): Promise<PlaybackStats | null> {\n if (!this.video) return null;\n const level = this.hls?.levels?.[this.hls.currentLevel];\n return {\n bitrateKbps: level ? Math.round(level.bitrate / 1000) : 0,\n framesPerSecond: 0,\n };\n }\n}\n","/**\n * INTERNAL — transport interfaces + auto-selection.\n *\n * The public API never names a transport; it only asks for a playback *mode*.\n * This factory maps a mode to the right hidden delivery mechanism.\n */\nimport type { BroadcastStats, PlaybackMode, PlaybackStats } from \"../types.js\";\nimport type { SignalingClient } from \"./signaling.js\";\nimport { WhipPublishTransport } from \"./publish-transport.js\";\nimport { WhepViewTransport } from \"./ll-view-transport.js\";\nimport { FlvViewTransport } from \"./balanced-view-transport.js\";\nimport { HlsViewTransport } from \"./scale-view-transport.js\";\n\n/** Hidden transport that sends a captured stream to the gateway. */\nexport interface PublishTransport {\n start(streamId: string, stream: MediaStream): Promise<void>;\n stop(): Promise<void>;\n getStats(): Promise<BroadcastStats | null>;\n /** Swap the outgoing video track in place (e.g. on camera switch). */\n replaceVideoTrack(track: MediaStreamTrack | null): Promise<void>;\n}\n\n/** Hidden transport that renders a remote stream into a video element. */\nexport interface ViewTransport {\n start(streamId: string, video: HTMLVideoElement): Promise<void>;\n stop(): Promise<void>;\n getStats(): Promise<PlaybackStats | null>;\n /** Fired by the transport when playback reaches its natural end. */\n onEnded(cb: () => void): void;\n /** Fired when the transport (re)enters a buffering state. */\n onBuffering(cb: () => void): void;\n}\n\nexport function createPublishTransport(signaling: SignalingClient): PublishTransport {\n return new WhipPublishTransport(signaling);\n}\n\n/** Auto-select the view transport for a playback mode. */\nexport function createViewTransport(\n mode: PlaybackMode,\n signaling: SignalingClient,\n): ViewTransport {\n switch (mode) {\n case \"low-latency\":\n return new WhepViewTransport(signaling);\n case \"balanced\":\n return new FlvViewTransport(signaling);\n case \"scale\":\n return new HlsViewTransport(signaling);\n }\n}\n","import { mebiusError } from \"./errors.js\";\nimport { TypedEmitter, type BroadcasterEventMap } from \"./events.js\";\nimport { resolveVideoElement } from \"./internal/view-target.js\";\nimport type { SignalingClient } from \"./internal/signaling.js\";\nimport { createPublishTransport, type PublishTransport } from \"./internal/transport.js\";\nimport type { BroadcasterOptions, MediaConstraint, ViewTarget } from \"./types.js\";\n\nconst STATS_INTERVAL_MS = 2000;\n\n/**\n * Publishes the local camera/microphone to a Mebius stream.\n *\n * Create one with {@link MebiusClient.createBroadcaster}, then\n * {@link MebiusBroadcaster.start | start} it with a stream id.\n */\nexport class MebiusBroadcaster extends TypedEmitter<BroadcasterEventMap> {\n private readonly transport: PublishTransport;\n private stream: MediaStream | null = null;\n private facingMode: \"user\" | \"environment\" = \"user\";\n private statsTimer: ReturnType<typeof setInterval> | null = null;\n private started = false;\n\n /** @internal */\n constructor(\n signaling: SignalingClient,\n private readonly options: BroadcasterOptions,\n ) {\n super();\n this.transport = createPublishTransport(signaling);\n }\n\n /** Begin broadcasting under the given stream id. */\n async start(streamId: string): Promise<void> {\n if (this.started) return;\n this.stream = await this.capture();\n await this.transport.start(streamId, this.stream);\n this.started = true;\n this.startStats();\n this.emit(\"started\", { streamId });\n }\n\n /** Stop broadcasting and release the camera/microphone. */\n async stop(): Promise<void> {\n this.stopStats();\n await this.transport.stop();\n this.stream?.getTracks().forEach((t) => t.stop());\n this.stream = null;\n this.started = false;\n this.emit(\"stopped\", undefined);\n }\n\n /** Flip between front and back camera (where available). */\n async switchCamera(): Promise<void> {\n if (!this.stream) return;\n this.facingMode = this.facingMode === \"user\" ? \"environment\" : \"user\";\n const next = await navigator.mediaDevices.getUserMedia({\n video: { facingMode: this.facingMode },\n audio: false,\n });\n const newTrack = next.getVideoTracks()[0] ?? null;\n const oldTrack = this.stream.getVideoTracks()[0];\n if (oldTrack) {\n this.stream.removeTrack(oldTrack);\n oldTrack.stop();\n }\n if (newTrack) this.stream.addTrack(newTrack);\n await this.transport.replaceVideoTrack(newTrack);\n }\n\n /** Mute or unmute the outgoing microphone. */\n setMicEnabled(enabled: boolean): void {\n this.stream?.getAudioTracks().forEach((t) => (t.enabled = enabled));\n }\n\n /** Enable or disable the outgoing camera. */\n setCameraEnabled(enabled: boolean): void {\n this.stream?.getVideoTracks().forEach((t) => (t.enabled = enabled));\n }\n\n /**\n * Web convenience: render the local camera preview into a `<video>` element.\n * This is the web analog of the mobile preview view; it does not affect what\n * is broadcast.\n */\n attachPreview(target: ViewTarget): void {\n if (!this.stream) return;\n const video = resolveVideoElement(target);\n video.srcObject = this.stream;\n video.muted = true;\n void video.play().catch(() => undefined);\n }\n\n private async capture(): Promise<MediaStream> {\n const video = normalize(this.options.video, true);\n const audio = normalize(this.options.audio, true);\n try {\n return await navigator.mediaDevices.getUserMedia({ video, audio });\n } catch (cause) {\n throw mebiusError(\"PERMISSION_DENIED\", undefined, cause);\n }\n }\n\n private startStats(): void {\n this.statsTimer = setInterval(async () => {\n const stats = await this.transport.getStats();\n if (stats) this.emit(\"stats\", stats);\n }, STATS_INTERVAL_MS);\n }\n\n private stopStats(): void {\n if (this.statsTimer) clearInterval(this.statsTimer);\n this.statsTimer = null;\n }\n}\n\nfunction normalize(c: MediaConstraint | undefined, fallback: boolean): boolean | MediaTrackConstraints {\n if (c === undefined) return fallback;\n return c;\n}\n","import { TypedEmitter, type PlayerEventMap } from \"./events.js\";\nimport { resolveVideoElement } from \"./internal/view-target.js\";\nimport type { SignalingClient } from \"./internal/signaling.js\";\nimport { createViewTransport, type ViewTransport } from \"./internal/transport.js\";\nimport type { PlayerOptions, ViewTarget } from \"./types.js\";\n\nconst STATS_INTERVAL_MS = 2000;\n\n/**\n * Plays a Mebius stream into a `<video>` element.\n *\n * Create one with {@link MebiusClient.createPlayer}, choosing a playback\n * {@link PlaybackMode | mode}; Mebius selects the right delivery automatically.\n */\nexport class MebiusPlayer extends TypedEmitter<PlayerEventMap> {\n private readonly transport: ViewTransport;\n private video: HTMLVideoElement | null = null;\n private statsTimer: ReturnType<typeof setInterval> | null = null;\n private playing = false;\n\n /** @internal */\n constructor(signaling: SignalingClient, options: PlayerOptions) {\n super();\n this.transport = createViewTransport(options.mode, signaling);\n this.transport.onEnded(() => {\n this.playing = false;\n this.stopStats();\n this.emit(\"ended\", undefined);\n });\n this.transport.onBuffering(() => this.emit(\"buffering\", undefined));\n }\n\n /** Start playing `streamId` into the given video element or selector. */\n async play(streamId: string, viewTarget: ViewTarget): Promise<void> {\n if (this.playing) return;\n this.video = resolveVideoElement(viewTarget);\n await this.transport.start(streamId, this.video);\n this.playing = true;\n this.startStats();\n this.emit(\"playing\", { streamId });\n }\n\n /** Stop playback and detach from the video element. */\n async stop(): Promise<void> {\n this.stopStats();\n await this.transport.stop();\n this.video = null;\n this.playing = false;\n }\n\n /** Set output volume in the range 0..1. */\n setVolume(volume: number): void {\n const v = Math.min(1, Math.max(0, volume));\n if (this.video) this.video.volume = v;\n }\n\n private startStats(): void {\n this.statsTimer = setInterval(async () => {\n const stats = await this.transport.getStats();\n if (stats) this.emit(\"stats\", stats);\n }, STATS_INTERVAL_MS);\n }\n\n private stopStats(): void {\n if (this.statsTimer) clearInterval(this.statsTimer);\n this.statsTimer = null;\n }\n}\n","/**\n * INTERNAL — gateway signaling client.\n *\n * This module is the ONE place that knows the wire protocols Mebius uses\n * behind the scenes (WHIP for publishing, WHEP for low-latency viewing, HLS\n * for scale viewing). None of these terms ever escape `internal/` — the public\n * API speaks only in Mebius vocabulary.\n *\n * The gateway HTTP contract (mebius-stream-engine public edge):\n * - Publish: POST {gateway}/whip/{streamId}?token=<jwt> (application/sdp)\n * - View low-latency: POST {gateway}/whep/{streamId}?token=<jwt> (application/sdp)\n * - View scale: GET {gateway}/live/{streamId}/index.m3u8?token=<jwt>\n * - Teardown: DELETE {resourceUrl}\n *\n * The engine validates the token from the `?token=` QUERY parameter (its\n * MediaMTX auth hook + HLS playback gate both read the query, not a header).\n * We still send `Authorization: Bearer <token>` for gateways that prefer it,\n * but the query token is what the engine actually enforces. HLS segment URLs\n * inside the playlist inherit `?token=` automatically (the engine rewrites the\n * m3u8), so no extra header is needed for segment fetches.\n */\nimport { mebiusError } from \"../errors.js\";\n\nexport type SignalingKind = \"whip\" | \"whep\";\n\nexport interface SdpExchangeResult {\n /** The remote SDP answer. */\n answerSdp: string;\n /** Resource URL to DELETE on teardown, if the gateway returned one. */\n resourceUrl: string | null;\n}\n\nexport class SignalingClient {\n constructor(\n private readonly gateway: string,\n private readonly token: string,\n ) {}\n\n private base(): string {\n return this.gateway.replace(/\\/+$/, \"\");\n }\n\n private headers(contentType?: string): HeadersInit {\n const h: Record<string, string> = { Authorization: `Bearer ${this.token}` };\n if (contentType) h[\"Content-Type\"] = contentType;\n return h;\n }\n\n /** Append the access token as a query param (the form the engine enforces). */\n private withToken(url: string): string {\n const sep = url.includes(\"?\") ? \"&\" : \"?\";\n return `${url}${sep}token=${encodeURIComponent(this.token)}`;\n }\n\n // Build the playlist URL used by scale-mode playback (HLS path, hidden). The\n // engine serves the playlist under /live/{id}/index.m3u8 and requires the\n // token in the query; segment URIs in the playlist inherit it automatically.\n /** Playlist URL for scale-mode playback. */\n scalePlaylistUrl(streamId: string): string {\n return this.withToken(`${this.base()}/live/${encodeURIComponent(streamId)}/index.m3u8`);\n }\n\n // Build the HTTP-FLV pull URL used by balanced-mode playback. Served by the\n // CDN (CDN_PULL_FORMAT `.flv`) or an SRS/nginx-rtmp edge in front of the\n // engine — MediaMTX itself does not vend HTTP-FLV. Hidden from the public API.\n /** Pull URL for balanced-mode playback. */\n balancedStreamUrl(streamId: string): string {\n return this.withToken(`${this.base()}/flv/${encodeURIComponent(streamId)}.flv`);\n }\n\n // Performs the SDP offer/answer exchange for a publish (WHIP) or low-latency\n // view (WHEP) session. Protocol detail kept in line comments so it never\n // leaks into the bundled public .d.ts.\n /**\n * Run the session offer/answer exchange. Throws a {@link MebiusError} with a\n * Mebius-flavored code on failure — never the raw protocol name.\n */\n async exchangeSdp(\n kind: SignalingKind,\n streamId: string,\n offerSdp: string,\n ): Promise<SdpExchangeResult> {\n const url = this.withToken(`${this.base()}/${kind}/${encodeURIComponent(streamId)}`);\n let res: Response;\n try {\n res = await fetch(url, {\n method: \"POST\",\n headers: this.headers(\"application/sdp\"),\n body: offerSdp,\n });\n } catch (cause) {\n throw mebiusError(\"CONNECTION_FAILED\", undefined, cause);\n }\n\n if (res.status === 401 || res.status === 403) {\n throw mebiusError(\"TOKEN_EXPIRED\");\n }\n if (res.status === 404) {\n throw mebiusError(\"STREAM_NOT_FOUND\");\n }\n if (!res.ok) {\n throw mebiusError(\"CONNECTION_FAILED\", `Mebius gateway returned ${res.status}.`);\n }\n\n const answerSdp = await res.text();\n const location = res.headers.get(\"Location\");\n const resourceUrl = location ? new URL(location, url).toString() : null;\n return { answerSdp, resourceUrl };\n }\n\n /** Tear down a previously-created session resource. Best-effort. */\n async deleteResource(resourceUrl: string | null): Promise<void> {\n if (!resourceUrl) return;\n try {\n await fetch(resourceUrl, { method: \"DELETE\", headers: this.headers() });\n } catch {\n // Teardown is best-effort; the gateway reaps idle sessions anyway.\n }\n }\n}\n","/**\n * INTERNAL — read (NOT verify) a Mebius token's expiry.\n *\n * The token is a short-lived JWT minted by the developer's backend. The client\n * never verifies it (only the gateway can) — it just peeks at `exp` so it can\n * proactively surface a TOKEN_EXPIRED error and let the app refresh.\n */\nexport interface TokenInfo {\n /** Expiry as a UNIX epoch in milliseconds, if present. */\n expiresAtMs: number | null;\n}\n\nfunction base64UrlDecode(input: string): string {\n const padded = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const pad = padded.length % 4 === 0 ? \"\" : \"=\".repeat(4 - (padded.length % 4));\n const b64 = padded + pad;\n if (typeof atob === \"function\") return atob(b64);\n // Node fallback (tests / SSR).\n return Buffer.from(b64, \"base64\").toString(\"binary\");\n}\n\nexport function readToken(token: string): TokenInfo {\n const parts = token.split(\".\");\n if (parts.length < 2) return { expiresAtMs: null };\n try {\n const payload = JSON.parse(base64UrlDecode(parts[1] ?? \"\")) as { exp?: number };\n return { expiresAtMs: typeof payload.exp === \"number\" ? payload.exp * 1000 : null };\n } catch {\n return { expiresAtMs: null };\n }\n}\n","import { MebiusBroadcaster } from \"./broadcaster.js\";\nimport { mebiusError } from \"./errors.js\";\nimport { TypedEmitter, type ClientEventMap } from \"./events.js\";\nimport { MebiusPlayer } from \"./player.js\";\nimport { SignalingClient } from \"./internal/signaling.js\";\nimport { readToken } from \"./internal/token.js\";\nimport type { BroadcasterOptions, MebiusInitOptions, PlayerOptions } from \"./types.js\";\n\n/**\n * A live connection to Mebius. Obtain one from {@link Mebius.connect}, then\n * create broadcasters and players from it.\n */\nexport class MebiusClient extends TypedEmitter<ClientEventMap> {\n private readonly signaling: SignalingClient;\n private expiryTimer: ReturnType<typeof setTimeout> | null = null;\n private connected = false;\n\n /** @internal */\n constructor(config: MebiusInitOptions, private readonly token: string) {\n super();\n this.signaling = new SignalingClient(config.gateway, token);\n }\n\n /** @internal Called by {@link Mebius.connect}. */\n open(): void {\n const { expiresAtMs } = readToken(this.token);\n const now = Date.now();\n if (expiresAtMs !== null && expiresAtMs <= now) {\n // Surface asynchronously so listeners attached after connect() still fire.\n queueMicrotask(() => this.emit(\"error\", mebiusError(\"TOKEN_EXPIRED\")));\n return;\n }\n this.connected = true;\n if (expiresAtMs !== null) {\n this.expiryTimer = setTimeout(\n () => this.emit(\"error\", mebiusError(\"TOKEN_EXPIRED\")),\n Math.max(0, expiresAtMs - now),\n );\n }\n queueMicrotask(() => this.emit(\"connected\", undefined));\n }\n\n /** Create a broadcaster bound to this connection. */\n createBroadcaster(options: BroadcasterOptions = {}): MebiusBroadcaster {\n this.assertConnected();\n return new MebiusBroadcaster(this.signaling, options);\n }\n\n /** Create a player bound to this connection. */\n createPlayer(options: PlayerOptions): MebiusPlayer {\n this.assertConnected();\n return new MebiusPlayer(this.signaling, options);\n }\n\n /** Close the connection and release resources. */\n disconnect(reason?: string): void {\n if (this.expiryTimer) clearTimeout(this.expiryTimer);\n this.expiryTimer = null;\n this.connected = false;\n this.emit(\"disconnected\", { reason });\n this.removeAllListeners();\n }\n\n private assertConnected(): void {\n if (!this.connected) throw mebiusError(\"NOT_CONNECTED\");\n }\n}\n","import { MebiusClient } from \"./client.js\";\nimport { mebiusError } from \"./errors.js\";\nimport type { MebiusConnectOptions, MebiusInitOptions } from \"./types.js\";\n\nlet config: MebiusInitOptions | null = null;\n\n/**\n * Entry point to the Mebius Web SDK.\n *\n * ```ts\n * Mebius.init({ appId: \"app_123\", gateway: \"https://gateway.mebius.io\" });\n * const client = Mebius.connect({ token });\n * ```\n */\nexport const Mebius = {\n /** Configure the SDK once, before connecting. */\n init(options: MebiusInitOptions): void {\n if (!options.appId) throw mebiusError(\"UNKNOWN\", \"Mebius.init requires an appId.\");\n if (!options.gateway) throw mebiusError(\"UNKNOWN\", \"Mebius.init requires a gateway URL.\");\n config = { ...options };\n },\n\n /**\n * Connect using a short-lived token minted by your backend. Returns a\n * {@link MebiusClient}. Listen for `\"connected\"` / `\"error\"` on it.\n */\n connect(options: MebiusConnectOptions): MebiusClient {\n if (!config) {\n throw mebiusError(\"UNKNOWN\", \"Call Mebius.init() before Mebius.connect().\");\n }\n if (!options.token) throw mebiusError(\"UNKNOWN\", \"Mebius.connect requires a token.\");\n const client = new MebiusClient(config, options.token);\n client.open();\n return client;\n },\n\n /** @internal Reset configuration (used in tests). */\n _reset(): void {\n config = null;\n },\n};\n"],"mappings":"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","names":["STATS_INTERVAL_MS","config"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/events.ts","../src/internal/view-target.ts","../src/internal/webrtc-util.ts","../src/internal/publish-transport.ts","../src/internal/ll-view-transport.ts","../src/internal/balanced-view-transport.ts","../src/internal/scale-view-transport.ts","../src/internal/transport.ts","../src/broadcaster.ts","../src/player.ts","../src/internal/signaling.ts","../src/internal/token.ts","../src/client.ts","../src/mebius.ts"],"sourcesContent":["/**\n * @mebius-io/web — Mebius Client SDK for the web.\n *\n * Public surface only. Everything under `internal/` is private and is never\n * re-exported here.\n */\nexport { Mebius } from \"./mebius.js\";\nexport { MebiusClient } from \"./client.js\";\nexport { MebiusBroadcaster } from \"./broadcaster.js\";\nexport { MebiusPlayer } from \"./player.js\";\nexport { MebiusError, mebiusError } from \"./errors.js\";\n\nexport type {\n ClientEventMap,\n BroadcasterEventMap,\n PlayerEventMap,\n} from \"./events.js\";\n\nexport type {\n MebiusInitOptions,\n MebiusConnectOptions,\n BroadcasterOptions,\n PlayerOptions,\n PlaybackMode,\n ViewTarget,\n MediaConstraint,\n BroadcastStats,\n PlaybackStats,\n MebiusErrorCode,\n} from \"./types.js\";\n","import type { MebiusErrorCode } from \"./types.js\";\n\n/**\n * The single error type the SDK surfaces. Always uses Mebius terminology —\n * never raw transport/protocol wording.\n *\n * Inspect {@link MebiusError.code} to decide how to recover (for example,\n * refresh the token on `\"TOKEN_EXPIRED\"`).\n */\nexport class MebiusError extends Error {\n readonly code: MebiusErrorCode;\n /** The underlying cause, if any. Useful for logging, opaque to the contract. */\n readonly cause?: unknown;\n\n constructor(code: MebiusErrorCode, message: string, cause?: unknown) {\n super(message);\n this.name = \"MebiusError\";\n this.code = code;\n this.cause = cause;\n Object.setPrototypeOf(this, MebiusError.prototype);\n }\n}\n\n/** Human-readable default messages, kept in Mebius terms. */\nconst DEFAULT_MESSAGES: Record<MebiusErrorCode, string> = {\n TOKEN_EXPIRED: \"Your Mebius token has expired. Mint a fresh token and reconnect.\",\n PERMISSION_DENIED: \"Camera/microphone permission was denied by the user or browser.\",\n CONNECTION_FAILED: \"Could not establish a connection to the Mebius gateway.\",\n NOT_CONNECTED: \"Not connected to Mebius. Call connect() before using this client.\",\n STREAM_NOT_FOUND: \"The requested stream could not be found on the Mebius gateway.\",\n UNKNOWN: \"An unexpected Mebius error occurred.\",\n};\n\n/** Create a {@link MebiusError}, falling back to a sensible default message. */\nexport function mebiusError(\n code: MebiusErrorCode,\n message?: string,\n cause?: unknown,\n): MebiusError {\n return new MebiusError(code, message ?? DEFAULT_MESSAGES[code], cause);\n}\n","import type { MebiusError } from \"./errors.js\";\nimport type { BroadcastStats, PlaybackStats } from \"./types.js\";\n\n// NOTE: these are `type` aliases (not interfaces) so they satisfy the\n// `Record<string, unknown>` constraint on TypedEmitter — TS only treats object\n// type aliases (not augmentable interfaces) as having an implicit index\n// signature.\n\n/** Event payloads emitted by {@link MebiusClient}. */\nexport type ClientEventMap = {\n connected: void;\n disconnected: { reason?: string };\n error: MebiusError;\n};\n\n/** Event payloads emitted by a broadcaster. */\nexport type BroadcasterEventMap = {\n started: { streamId: string };\n stopped: void;\n stats: BroadcastStats;\n};\n\n/** Event payloads emitted by a player. */\nexport type PlayerEventMap = {\n playing: { streamId: string };\n buffering: void;\n ended: void;\n stats: PlaybackStats;\n};\n\ntype Listener<T> = (payload: T) => void;\n\n/**\n * A tiny strongly-typed event emitter. `EventMap` maps each event name to its\n * payload type, so `on(\"started\", cb)` infers `cb`'s argument automatically.\n */\nexport class TypedEmitter<EventMap extends Record<string, unknown>> {\n private readonly listeners = new Map<keyof EventMap, Set<Listener<unknown>>>();\n\n /** Subscribe to an event. Returns an unsubscribe function. */\n on<K extends keyof EventMap>(event: K, cb: Listener<EventMap[K]>): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(cb as Listener<unknown>);\n return () => this.off(event, cb);\n }\n\n /** Unsubscribe a previously-registered listener. */\n off<K extends keyof EventMap>(event: K, cb: Listener<EventMap[K]>): void {\n this.listeners.get(event)?.delete(cb as Listener<unknown>);\n }\n\n /** Emit an event to all listeners. Internal use. */\n protected emit<K extends keyof EventMap>(event: K, payload: EventMap[K]): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const cb of [...set]) (cb as Listener<EventMap[K]>)(payload);\n }\n\n /** Remove every listener. Internal use during teardown. */\n protected removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","/** INTERNAL — resolve a public {@link ViewTarget} to a real `<video>` element. */\nimport { mebiusError } from \"../errors.js\";\nimport type { ViewTarget } from \"../types.js\";\n\nexport function resolveVideoElement(target: ViewTarget): HTMLVideoElement {\n if (typeof target !== \"string\") {\n if (target instanceof HTMLVideoElement) return target;\n throw mebiusError(\"UNKNOWN\", \"View target must be a <video> element or a CSS selector.\");\n }\n const el = document.querySelector(target);\n if (!el) {\n throw mebiusError(\"UNKNOWN\", `No element matches the selector \"${target}\".`);\n }\n if (!(el instanceof HTMLVideoElement)) {\n throw mebiusError(\"UNKNOWN\", `Selector \"${target}\" did not resolve to a <video> element.`);\n }\n return el;\n}\n","/** INTERNAL — small WebRTC helpers shared by publish/view transports. */\n\n/**\n * Wait until ICE gathering completes (or a short timeout elapses) so the SDP\n * we send already contains candidates. Keeps the gateway exchange to a single\n * round-trip.\n */\nexport function waitForIceGathering(pc: RTCPeerConnection, timeoutMs = 2000): Promise<void> {\n if (pc.iceGatheringState === \"complete\") return Promise.resolve();\n return new Promise((resolve) => {\n const done = () => {\n pc.removeEventListener(\"icegatheringstatechange\", check);\n clearTimeout(timer);\n resolve();\n };\n const check = () => {\n if (pc.iceGatheringState === \"complete\") done();\n };\n const timer = setTimeout(done, timeoutMs);\n pc.addEventListener(\"icegatheringstatechange\", check);\n });\n}\n\n/** Default ICE configuration. The gateway may also relay; STUN aids direct paths. */\nexport const DEFAULT_RTC_CONFIG: RTCConfiguration = {\n iceServers: [{ urls: \"stun:stun.l.google.com:19302\" }],\n};\n","/**\n * INTERNAL — publish transport (WHIP over the gateway).\n *\n * Sends a locally-captured MediaStream to the Mebius gateway via a standard\n * WHIP offer/answer exchange. Hidden from the public API.\n */\nimport type { BroadcastStats } from \"../types.js\";\nimport { mebiusError } from \"../errors.js\";\nimport type { SignalingClient } from \"./signaling.js\";\nimport type { PublishTransport } from \"./transport.js\";\nimport { DEFAULT_RTC_CONFIG, waitForIceGathering } from \"./webrtc-util.js\";\n\nexport class WhipPublishTransport implements PublishTransport {\n private pc: RTCPeerConnection | null = null;\n private resourceUrl: string | null = null;\n\n constructor(private readonly signaling: SignalingClient) {}\n\n async start(streamId: string, stream: MediaStream): Promise<void> {\n const pc = new RTCPeerConnection(DEFAULT_RTC_CONFIG);\n this.pc = pc;\n\n for (const track of stream.getTracks()) {\n pc.addTrack(track, stream);\n }\n\n const offer = await pc.createOffer();\n await pc.setLocalDescription(offer);\n await waitForIceGathering(pc);\n\n const localSdp = pc.localDescription?.sdp;\n if (!localSdp) throw mebiusError(\"CONNECTION_FAILED\", \"Failed to create a local session.\");\n\n const { answer, resourceUrl } = await this.signaling.exchangeSession(\n \"publish\",\n streamId,\n localSdp,\n );\n this.resourceUrl = resourceUrl;\n await pc.setRemoteDescription({ type: \"answer\", sdp: answer });\n }\n\n async replaceVideoTrack(track: MediaStreamTrack | null): Promise<void> {\n const sender = this.pc?.getSenders().find((s) => s.track?.kind === \"video\");\n if (sender) await sender.replaceTrack(track);\n }\n\n async stop(): Promise<void> {\n await this.signaling.deleteResource(this.resourceUrl);\n this.resourceUrl = null;\n this.pc?.getSenders().forEach((s) => s.track?.stop());\n this.pc?.close();\n this.pc = null;\n }\n\n async getStats(): Promise<BroadcastStats | null> {\n if (!this.pc) return null;\n const report = await this.pc.getStats();\n let bitrateKbps = 0;\n let framesPerSecond = 0;\n let rttMs: number | undefined;\n report.forEach((stat) => {\n if (stat.type === \"outbound-rtp\" && !stat.isRemote) {\n if (typeof stat.framesPerSecond === \"number\") framesPerSecond = stat.framesPerSecond;\n }\n if (stat.type === \"candidate-pair\" && stat.state === \"succeeded\") {\n if (typeof stat.availableOutgoingBitrate === \"number\") {\n bitrateKbps = Math.round(stat.availableOutgoingBitrate / 1000);\n }\n if (typeof stat.currentRoundTripTime === \"number\") {\n rttMs = Math.round(stat.currentRoundTripTime * 1000);\n }\n }\n });\n return { bitrateKbps, framesPerSecond, rttMs };\n }\n}\n","/**\n * INTERNAL — low-latency view transport (WHEP over the gateway).\n *\n * Pulls a remote stream from the Mebius gateway via a standard WHEP exchange\n * and renders it into a video element. Hidden from the public API.\n */\nimport type { PlaybackStats } from \"../types.js\";\nimport { mebiusError } from \"../errors.js\";\nimport type { SignalingClient } from \"./signaling.js\";\nimport type { ViewTransport } from \"./transport.js\";\nimport { DEFAULT_RTC_CONFIG, waitForIceGathering } from \"./webrtc-util.js\";\n\nexport class WhepViewTransport implements ViewTransport {\n private pc: RTCPeerConnection | null = null;\n private resourceUrl: string | null = null;\n private endedCb: (() => void) | null = null;\n private bufferingCb: (() => void) | null = null;\n\n constructor(private readonly signaling: SignalingClient) {}\n\n onEnded(cb: () => void): void {\n this.endedCb = cb;\n }\n\n onBuffering(cb: () => void): void {\n this.bufferingCb = cb;\n }\n\n async start(streamId: string, video: HTMLVideoElement): Promise<void> {\n const pc = new RTCPeerConnection(DEFAULT_RTC_CONFIG);\n this.pc = pc;\n const remote = new MediaStream();\n\n pc.addTransceiver(\"video\", { direction: \"recvonly\" });\n pc.addTransceiver(\"audio\", { direction: \"recvonly\" });\n\n pc.ontrack = (ev) => {\n remote.addTrack(ev.track);\n video.srcObject = remote;\n void video.play().catch(() => {\n /* autoplay may require a user gesture; left to the app */\n });\n };\n pc.onconnectionstatechange = () => {\n if (pc.connectionState === \"disconnected\" || pc.connectionState === \"failed\") {\n this.bufferingCb?.();\n }\n if (pc.connectionState === \"closed\") this.endedCb?.();\n };\n\n const offer = await pc.createOffer();\n await pc.setLocalDescription(offer);\n await waitForIceGathering(pc);\n\n const localSdp = pc.localDescription?.sdp;\n if (!localSdp) throw mebiusError(\"CONNECTION_FAILED\", \"Failed to create a local session.\");\n\n const { answer, resourceUrl } = await this.signaling.exchangeSession(\n \"view\",\n streamId,\n localSdp,\n );\n this.resourceUrl = resourceUrl;\n await pc.setRemoteDescription({ type: \"answer\", sdp: answer });\n }\n\n async stop(): Promise<void> {\n await this.signaling.deleteResource(this.resourceUrl);\n this.resourceUrl = null;\n this.pc?.close();\n this.pc = null;\n }\n\n async getStats(): Promise<PlaybackStats | null> {\n if (!this.pc) return null;\n const report = await this.pc.getStats();\n let bitrateKbps = 0;\n let framesPerSecond = 0;\n let latencyMs: number | undefined;\n report.forEach((stat) => {\n if (stat.type === \"inbound-rtp\") {\n if (typeof stat.framesPerSecond === \"number\") framesPerSecond = stat.framesPerSecond;\n if (typeof stat.jitter === \"number\") latencyMs = Math.round(stat.jitter * 1000);\n }\n if (stat.type === \"candidate-pair\" && stat.state === \"succeeded\") {\n if (typeof stat.availableIncomingBitrate === \"number\") {\n bitrateKbps = Math.round(stat.availableIncomingBitrate / 1000);\n }\n }\n });\n return { bitrateKbps, framesPerSecond, latencyMs };\n }\n}\n","/**\n * INTERNAL — balanced view transport (HTTP-FLV via flv.js).\n *\n * For typical one-to-many web viewers Mebius pulls an HTTP-FLV stream and feeds\n * it through flv.js (Media Source Extensions). Latency is ~1-3s — lower than\n * HLS, higher than the WebRTC pull — and it scales over a CDN edge. flv.js is\n * loaded lazily so it never weighs down apps that don't use this mode.\n *\n * Browser-only: flv.js needs MSE, which iOS Safari lacks. Hidden from the\n * public API; the public surface speaks only of the `\"balanced\"` playback mode.\n */\nimport type { PlaybackStats } from \"../types.js\";\nimport { mebiusError } from \"../errors.js\";\nimport type { SignalingClient } from \"./signaling.js\";\nimport type { ViewTransport } from \"./transport.js\";\n\n// Minimal structural typing for the parts of flv.js we use, declared locally so\n// this package type-checks WITHOUT flv.js installed (it is an optional\n// dependency the host app provides).\ninterface FlvPlayer {\n attachMediaElement(el: HTMLVideoElement): void;\n load(): void;\n play(): Promise<void> | void;\n unload(): void;\n detachMediaElement(): void;\n destroy(): void;\n on(event: string, cb: (...args: unknown[]) => void): void;\n}\ninterface FlvModule {\n isSupported(): boolean;\n createPlayer(config: { type: string; url: string; isLive?: boolean }): FlvPlayer;\n Events: Record<string, string>;\n}\n\nexport class FlvViewTransport implements ViewTransport {\n private player: FlvPlayer | null = null;\n private video: HTMLVideoElement | null = null;\n private endedCb: (() => void) | null = null;\n private bufferingCb: (() => void) | null = null;\n\n constructor(private readonly signaling: SignalingClient) {}\n\n onEnded(cb: () => void): void {\n this.endedCb = cb;\n }\n\n onBuffering(cb: () => void): void {\n this.bufferingCb = cb;\n }\n\n async start(streamId: string, video: HTMLVideoElement): Promise<void> {\n this.video = video;\n const url = this.signaling.balancedStreamUrl(streamId);\n\n video.addEventListener(\"ended\", () => this.endedCb?.());\n video.addEventListener(\"waiting\", () => this.bufferingCb?.());\n\n let mod: { default: FlvModule };\n try {\n // Non-literal specifier so the optional dependency is resolved at runtime\n // and does not break type-checking when it is not installed.\n const spec = \"flv.js\";\n mod = (await import(/* @vite-ignore */ spec)) as { default: FlvModule };\n } catch (cause) {\n throw mebiusError(\"CONNECTION_FAILED\", \"Balanced playback support failed to load.\", cause);\n }\n\n const flvjs = mod.default;\n if (!flvjs.isSupported()) {\n throw mebiusError(\"CONNECTION_FAILED\", \"Balanced playback is not supported in this browser.\");\n }\n\n const player = flvjs.createPlayer({ type: \"flv\", url, isLive: true });\n this.player = player;\n player.on(flvjs.Events.ERROR ?? \"error\", () => this.bufferingCb?.());\n player.attachMediaElement(video);\n player.load();\n await Promise.resolve(player.play()).catch(() => undefined);\n }\n\n async stop(): Promise<void> {\n if (this.player) {\n this.player.unload();\n this.player.detachMediaElement();\n this.player.destroy();\n this.player = null;\n }\n if (this.video) {\n this.video.removeAttribute(\"src\");\n this.video.load();\n }\n this.video = null;\n }\n\n async getStats(): Promise<PlaybackStats | null> {\n if (!this.video) return null;\n return {\n bitrateKbps: 0,\n framesPerSecond: 0,\n latencyMs: undefined,\n };\n }\n}\n","/**\n * INTERNAL — scale view transport (HLS via hls.js / native).\n *\n * For large-audience playback Mebius delivers an HLS playlist from the\n * gateway. hls.js is loaded lazily; Safari plays the playlist natively.\n * Hidden from the public API.\n */\nimport type { PlaybackStats } from \"../types.js\";\nimport { mebiusError } from \"../errors.js\";\nimport type { SignalingClient } from \"./signaling.js\";\nimport type { ViewTransport } from \"./transport.js\";\n\n// Loaded on demand so it never weighs down low-latency-only apps.\ntype HlsModule = typeof import(\"hls.js\");\ntype HlsInstance = import(\"hls.js\").default;\n\nexport class HlsViewTransport implements ViewTransport {\n private hls: HlsInstance | null = null;\n private video: HTMLVideoElement | null = null;\n private endedCb: (() => void) | null = null;\n private bufferingCb: (() => void) | null = null;\n\n constructor(private readonly signaling: SignalingClient) {}\n\n onEnded(cb: () => void): void {\n this.endedCb = cb;\n }\n\n onBuffering(cb: () => void): void {\n this.bufferingCb = cb;\n }\n\n async start(streamId: string, video: HTMLVideoElement): Promise<void> {\n this.video = video;\n const url = this.signaling.scalePlaylistUrl(streamId);\n\n video.addEventListener(\"ended\", () => this.endedCb?.());\n video.addEventListener(\"waiting\", () => this.bufferingCb?.());\n\n // Safari and iOS play HLS natively — no library needed.\n if (video.canPlayType(\"application/vnd.apple.mpegurl\")) {\n video.src = url;\n await video.play().catch(() => undefined);\n return;\n }\n\n let mod: HlsModule;\n try {\n mod = await import(\"hls.js\");\n } catch (cause) {\n throw mebiusError(\"CONNECTION_FAILED\", \"Scale playback support failed to load.\", cause);\n }\n const Hls = mod.default;\n if (!Hls.isSupported()) {\n throw mebiusError(\"CONNECTION_FAILED\", \"Scale playback is not supported in this browser.\");\n }\n\n const hls = new Hls({ lowLatencyMode: true });\n this.hls = hls;\n hls.on(Hls.Events.ERROR, (_evt, data) => {\n if (data.fatal) this.bufferingCb?.();\n });\n hls.loadSource(url);\n hls.attachMedia(video);\n await video.play().catch(() => undefined);\n }\n\n async stop(): Promise<void> {\n this.hls?.destroy();\n this.hls = null;\n if (this.video) {\n this.video.removeAttribute(\"src\");\n this.video.load();\n }\n this.video = null;\n }\n\n async getStats(): Promise<PlaybackStats | null> {\n if (!this.video) return null;\n const level = this.hls?.levels?.[this.hls.currentLevel];\n return {\n bitrateKbps: level ? Math.round(level.bitrate / 1000) : 0,\n framesPerSecond: 0,\n };\n }\n}\n","/**\n * INTERNAL — transport interfaces + auto-selection.\n *\n * The public API never names a transport; it only asks for a playback *mode*.\n * This factory maps a mode to the right hidden delivery mechanism.\n */\nimport type { BroadcastStats, PlaybackMode, PlaybackStats } from \"../types.js\";\nimport type { SignalingClient } from \"./signaling.js\";\nimport { WhipPublishTransport } from \"./publish-transport.js\";\nimport { WhepViewTransport } from \"./ll-view-transport.js\";\nimport { FlvViewTransport } from \"./balanced-view-transport.js\";\nimport { HlsViewTransport } from \"./scale-view-transport.js\";\n\n/** Hidden transport that sends a captured stream to the gateway. */\nexport interface PublishTransport {\n start(streamId: string, stream: MediaStream): Promise<void>;\n stop(): Promise<void>;\n getStats(): Promise<BroadcastStats | null>;\n /** Swap the outgoing video track in place (e.g. on camera switch). */\n replaceVideoTrack(track: MediaStreamTrack | null): Promise<void>;\n}\n\n/** Hidden transport that renders a remote stream into a video element. */\nexport interface ViewTransport {\n start(streamId: string, video: HTMLVideoElement): Promise<void>;\n stop(): Promise<void>;\n getStats(): Promise<PlaybackStats | null>;\n /** Fired by the transport when playback reaches its natural end. */\n onEnded(cb: () => void): void;\n /** Fired when the transport (re)enters a buffering state. */\n onBuffering(cb: () => void): void;\n}\n\nexport function createPublishTransport(signaling: SignalingClient): PublishTransport {\n return new WhipPublishTransport(signaling);\n}\n\n/** Auto-select the view transport for a playback mode. */\nexport function createViewTransport(\n mode: PlaybackMode,\n signaling: SignalingClient,\n): ViewTransport {\n switch (mode) {\n case \"low-latency\":\n return new WhepViewTransport(signaling);\n case \"balanced\":\n return new FlvViewTransport(signaling);\n case \"scale\":\n return new HlsViewTransport(signaling);\n }\n}\n","import { mebiusError } from \"./errors.js\";\nimport { TypedEmitter, type BroadcasterEventMap } from \"./events.js\";\nimport { resolveVideoElement } from \"./internal/view-target.js\";\nimport type { SignalingClient } from \"./internal/signaling.js\";\nimport { createPublishTransport, type PublishTransport } from \"./internal/transport.js\";\nimport type { BroadcasterOptions, MediaConstraint, ViewTarget } from \"./types.js\";\n\nconst STATS_INTERVAL_MS = 2000;\n\n/**\n * Publishes the local camera/microphone to a Mebius stream.\n *\n * Create one with {@link MebiusClient.createBroadcaster}, then\n * {@link MebiusBroadcaster.start | start} it with a stream id.\n */\nexport class MebiusBroadcaster extends TypedEmitter<BroadcasterEventMap> {\n private readonly transport: PublishTransport;\n private stream: MediaStream | null = null;\n private facingMode: \"user\" | \"environment\" = \"user\";\n private statsTimer: ReturnType<typeof setInterval> | null = null;\n private started = false;\n\n /** @internal */\n constructor(\n signaling: SignalingClient,\n private readonly options: BroadcasterOptions,\n ) {\n super();\n this.transport = createPublishTransport(signaling);\n }\n\n /** Begin broadcasting under the given stream id. */\n async start(streamId: string): Promise<void> {\n if (this.started) return;\n this.stream = await this.capture();\n await this.transport.start(streamId, this.stream);\n this.started = true;\n this.startStats();\n this.emit(\"started\", { streamId });\n }\n\n /** Stop broadcasting and release the camera/microphone. */\n async stop(): Promise<void> {\n this.stopStats();\n await this.transport.stop();\n this.stream?.getTracks().forEach((t) => t.stop());\n this.stream = null;\n this.started = false;\n this.emit(\"stopped\", undefined);\n }\n\n /** Flip between front and back camera (where available). */\n async switchCamera(): Promise<void> {\n if (!this.stream) return;\n this.facingMode = this.facingMode === \"user\" ? \"environment\" : \"user\";\n const next = await navigator.mediaDevices.getUserMedia({\n video: { facingMode: this.facingMode },\n audio: false,\n });\n const newTrack = next.getVideoTracks()[0] ?? null;\n const oldTrack = this.stream.getVideoTracks()[0];\n if (oldTrack) {\n this.stream.removeTrack(oldTrack);\n oldTrack.stop();\n }\n if (newTrack) this.stream.addTrack(newTrack);\n await this.transport.replaceVideoTrack(newTrack);\n }\n\n /** Mute or unmute the outgoing microphone. */\n setMicEnabled(enabled: boolean): void {\n this.stream?.getAudioTracks().forEach((t) => (t.enabled = enabled));\n }\n\n /** Enable or disable the outgoing camera. */\n setCameraEnabled(enabled: boolean): void {\n this.stream?.getVideoTracks().forEach((t) => (t.enabled = enabled));\n }\n\n /**\n * Web convenience: render the local camera preview into a `<video>` element.\n * This is the web analog of the mobile preview view; it does not affect what\n * is broadcast.\n */\n attachPreview(target: ViewTarget): void {\n if (!this.stream) return;\n const video = resolveVideoElement(target);\n video.srcObject = this.stream;\n video.muted = true;\n void video.play().catch(() => undefined);\n }\n\n private async capture(): Promise<MediaStream> {\n const video = normalize(this.options.video, true);\n const audio = normalize(this.options.audio, true);\n try {\n return await navigator.mediaDevices.getUserMedia({ video, audio });\n } catch (cause) {\n throw mebiusError(\"PERMISSION_DENIED\", undefined, cause);\n }\n }\n\n private startStats(): void {\n this.statsTimer = setInterval(async () => {\n const stats = await this.transport.getStats();\n if (stats) this.emit(\"stats\", stats);\n }, STATS_INTERVAL_MS);\n }\n\n private stopStats(): void {\n if (this.statsTimer) clearInterval(this.statsTimer);\n this.statsTimer = null;\n }\n}\n\nfunction normalize(c: MediaConstraint | undefined, fallback: boolean): boolean | MediaTrackConstraints {\n if (c === undefined) return fallback;\n return c;\n}\n","import { TypedEmitter, type PlayerEventMap } from \"./events.js\";\nimport { resolveVideoElement } from \"./internal/view-target.js\";\nimport type { SignalingClient } from \"./internal/signaling.js\";\nimport { createViewTransport, type ViewTransport } from \"./internal/transport.js\";\nimport type { PlayerOptions, ViewTarget } from \"./types.js\";\n\nconst STATS_INTERVAL_MS = 2000;\n\n/**\n * Plays a Mebius stream into a `<video>` element.\n *\n * Create one with {@link MebiusClient.createPlayer}, choosing a playback\n * {@link PlaybackMode | mode}; Mebius selects the right delivery automatically.\n */\nexport class MebiusPlayer extends TypedEmitter<PlayerEventMap> {\n private readonly transport: ViewTransport;\n private video: HTMLVideoElement | null = null;\n private statsTimer: ReturnType<typeof setInterval> | null = null;\n private playing = false;\n\n /** @internal */\n constructor(signaling: SignalingClient, options: PlayerOptions) {\n super();\n this.transport = createViewTransport(options.mode, signaling);\n this.transport.onEnded(() => {\n this.playing = false;\n this.stopStats();\n this.emit(\"ended\", undefined);\n });\n this.transport.onBuffering(() => this.emit(\"buffering\", undefined));\n }\n\n /** Start playing `streamId` into the given video element or selector. */\n async play(streamId: string, viewTarget: ViewTarget): Promise<void> {\n if (this.playing) return;\n this.video = resolveVideoElement(viewTarget);\n await this.transport.start(streamId, this.video);\n this.playing = true;\n this.startStats();\n this.emit(\"playing\", { streamId });\n }\n\n /** Stop playback and detach from the video element. */\n async stop(): Promise<void> {\n this.stopStats();\n await this.transport.stop();\n this.video = null;\n this.playing = false;\n }\n\n /** Set output volume in the range 0..1. */\n setVolume(volume: number): void {\n const v = Math.min(1, Math.max(0, volume));\n if (this.video) this.video.volume = v;\n }\n\n private startStats(): void {\n this.statsTimer = setInterval(async () => {\n const stats = await this.transport.getStats();\n if (stats) this.emit(\"stats\", stats);\n }, STATS_INTERVAL_MS);\n }\n\n private stopStats(): void {\n if (this.statsTimer) clearInterval(this.statsTimer);\n this.statsTimer = null;\n }\n}\n","/**\n * INTERNAL — gateway signaling client.\n *\n * This module is the ONE place that knows the wire protocols Mebius uses\n * behind the scenes (WHIP for publishing, WHEP for low-latency viewing, HLS\n * for scale viewing). None of these terms ever escape `internal/` — the public\n * API speaks only in Mebius vocabulary.\n *\n * The gateway HTTP contract (mebius-stream-engine public edge):\n * - Publish: POST {gateway}/whip/{streamId}?token=<jwt> (application/sdp)\n * - View low-latency: POST {gateway}/whep/{streamId}?token=<jwt> (application/sdp)\n * - View scale: GET {gateway}/live/{streamId}/index.m3u8?token=<jwt>\n * - Teardown: DELETE {resourceUrl}\n *\n * The engine validates the token from the `?token=` QUERY parameter (its\n * MediaMTX auth hook + HLS playback gate both read the query, not a header).\n * We still send `Authorization: Bearer <token>` for gateways that prefer it,\n * but the query token is what the engine actually enforces. HLS segment URLs\n * inside the playlist inherit `?token=` automatically (the engine rewrites the\n * m3u8), so no extra header is needed for segment fetches.\n */\nimport { mebiusError } from \"../errors.js\";\n\n/**\n * A media session direction. Deliberately neutral vocabulary (\"publish\" /\n * \"view\") so that if a type bundler ever inlines this into the public `.d.ts`\n * (e.g. via a private field reference), no wire-protocol term leaks to clients.\n * The concrete path segment is derived inside {@link SignalingClient} only.\n */\nexport type SessionKind = \"publish\" | \"view\";\n\nexport interface SessionResult {\n /** The remote session answer returned by the gateway. */\n answer: string;\n /** Resource URL to DELETE on teardown, if the gateway returned one. */\n resourceUrl: string | null;\n}\n\nexport class SignalingClient {\n constructor(\n private readonly gateway: string,\n private readonly token: string,\n ) {}\n\n private base(): string {\n return this.gateway.replace(/\\/+$/, \"\");\n }\n\n private headers(contentType?: string): HeadersInit {\n const h: Record<string, string> = { Authorization: `Bearer ${this.token}` };\n if (contentType) h[\"Content-Type\"] = contentType;\n return h;\n }\n\n /** Append the access token as a query param (the form the engine enforces). */\n private withToken(url: string): string {\n const sep = url.includes(\"?\") ? \"&\" : \"?\";\n return `${url}${sep}token=${encodeURIComponent(this.token)}`;\n }\n\n // Build the playlist URL used by scale-mode playback (HLS path, hidden). The\n // engine serves the playlist under /live/{id}/index.m3u8 and requires the\n // token in the query; segment URIs in the playlist inherit it automatically.\n /** Playlist URL for scale-mode playback. */\n scalePlaylistUrl(streamId: string): string {\n return this.withToken(`${this.base()}/live/${encodeURIComponent(streamId)}/index.m3u8`);\n }\n\n // Build the HTTP-FLV pull URL used by balanced-mode playback. Served by the\n // CDN (CDN_PULL_FORMAT `.flv`) or an SRS/nginx-rtmp edge in front of the\n // engine — MediaMTX itself does not vend HTTP-FLV. Hidden from the public API.\n /** Pull URL for balanced-mode playback. */\n balancedStreamUrl(streamId: string): string {\n return this.withToken(`${this.base()}/flv/${encodeURIComponent(streamId)}.flv`);\n }\n\n // Maps a neutral session kind to the concrete signaling path segment. This\n // mapping (publish -> WHIP, view -> WHEP) lives ONLY in this method body, so\n // the protocol names never appear in any exported type signature.\n private pathFor(kind: SessionKind): string {\n return kind === \"publish\" ? \"whip\" : \"whep\";\n }\n\n // Performs the offer/answer exchange for a publish or a low-latency view\n // session. Protocol detail kept inside the method body so it never leaks into\n // the bundled public .d.ts.\n /**\n * Run the session offer/answer exchange. Throws a {@link MebiusError} with a\n * Mebius-flavored code on failure — never the raw protocol name.\n */\n async exchangeSession(\n kind: SessionKind,\n streamId: string,\n offer: string,\n ): Promise<SessionResult> {\n const url = this.withToken(`${this.base()}/${this.pathFor(kind)}/${encodeURIComponent(streamId)}`);\n let res: Response;\n try {\n res = await fetch(url, {\n method: \"POST\",\n headers: this.headers(\"application/sdp\"),\n body: offer,\n });\n } catch (cause) {\n throw mebiusError(\"CONNECTION_FAILED\", undefined, cause);\n }\n\n if (res.status === 401 || res.status === 403) {\n throw mebiusError(\"TOKEN_EXPIRED\");\n }\n if (res.status === 404) {\n throw mebiusError(\"STREAM_NOT_FOUND\");\n }\n if (!res.ok) {\n throw mebiusError(\"CONNECTION_FAILED\", `Mebius gateway returned ${res.status}.`);\n }\n\n const answer = await res.text();\n const location = res.headers.get(\"Location\");\n const resourceUrl = location ? new URL(location, url).toString() : null;\n return { answer, resourceUrl };\n }\n\n /** Tear down a previously-created session resource. Best-effort. */\n async deleteResource(resourceUrl: string | null): Promise<void> {\n if (!resourceUrl) return;\n try {\n await fetch(resourceUrl, { method: \"DELETE\", headers: this.headers() });\n } catch {\n // Teardown is best-effort; the gateway reaps idle sessions anyway.\n }\n }\n}\n","/**\n * INTERNAL — read (NOT verify) a Mebius token's expiry.\n *\n * The token is a short-lived JWT minted by the developer's backend. The client\n * never verifies it (only the gateway can) — it just peeks at `exp` so it can\n * proactively surface a TOKEN_EXPIRED error and let the app refresh.\n */\nexport interface TokenInfo {\n /** Expiry as a UNIX epoch in milliseconds, if present. */\n expiresAtMs: number | null;\n}\n\nfunction base64UrlDecode(input: string): string {\n const padded = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const pad = padded.length % 4 === 0 ? \"\" : \"=\".repeat(4 - (padded.length % 4));\n const b64 = padded + pad;\n if (typeof atob === \"function\") return atob(b64);\n // Node fallback (tests / SSR).\n return Buffer.from(b64, \"base64\").toString(\"binary\");\n}\n\nexport function readToken(token: string): TokenInfo {\n const parts = token.split(\".\");\n if (parts.length < 2) return { expiresAtMs: null };\n try {\n const payload = JSON.parse(base64UrlDecode(parts[1] ?? \"\")) as { exp?: number };\n return { expiresAtMs: typeof payload.exp === \"number\" ? payload.exp * 1000 : null };\n } catch {\n return { expiresAtMs: null };\n }\n}\n","import { MebiusBroadcaster } from \"./broadcaster.js\";\nimport { mebiusError } from \"./errors.js\";\nimport { TypedEmitter, type ClientEventMap } from \"./events.js\";\nimport { MebiusPlayer } from \"./player.js\";\nimport { SignalingClient } from \"./internal/signaling.js\";\nimport { readToken } from \"./internal/token.js\";\nimport type { BroadcasterOptions, MebiusInitOptions, PlayerOptions } from \"./types.js\";\n\n/**\n * A live connection to Mebius. Obtain one from {@link Mebius.connect}, then\n * create broadcasters and players from it.\n */\nexport class MebiusClient extends TypedEmitter<ClientEventMap> {\n private readonly signaling: SignalingClient;\n private expiryTimer: ReturnType<typeof setTimeout> | null = null;\n private connected = false;\n\n /** @internal */\n constructor(config: MebiusInitOptions, private readonly token: string) {\n super();\n this.signaling = new SignalingClient(config.gateway, token);\n }\n\n /** @internal Called by {@link Mebius.connect}. */\n open(): void {\n const { expiresAtMs } = readToken(this.token);\n const now = Date.now();\n if (expiresAtMs !== null && expiresAtMs <= now) {\n // Surface asynchronously so listeners attached after connect() still fire.\n queueMicrotask(() => this.emit(\"error\", mebiusError(\"TOKEN_EXPIRED\")));\n return;\n }\n this.connected = true;\n if (expiresAtMs !== null) {\n this.expiryTimer = setTimeout(\n () => this.emit(\"error\", mebiusError(\"TOKEN_EXPIRED\")),\n Math.max(0, expiresAtMs - now),\n );\n }\n queueMicrotask(() => this.emit(\"connected\", undefined));\n }\n\n /** Create a broadcaster bound to this connection. */\n createBroadcaster(options: BroadcasterOptions = {}): MebiusBroadcaster {\n this.assertConnected();\n return new MebiusBroadcaster(this.signaling, options);\n }\n\n /** Create a player bound to this connection. */\n createPlayer(options: PlayerOptions): MebiusPlayer {\n this.assertConnected();\n return new MebiusPlayer(this.signaling, options);\n }\n\n /** Close the connection and release resources. */\n disconnect(reason?: string): void {\n if (this.expiryTimer) clearTimeout(this.expiryTimer);\n this.expiryTimer = null;\n this.connected = false;\n this.emit(\"disconnected\", { reason });\n this.removeAllListeners();\n }\n\n private assertConnected(): void {\n if (!this.connected) throw mebiusError(\"NOT_CONNECTED\");\n }\n}\n","import { MebiusClient } from \"./client.js\";\nimport { mebiusError } from \"./errors.js\";\nimport type { MebiusConnectOptions, MebiusInitOptions } from \"./types.js\";\n\nlet config: MebiusInitOptions | null = null;\n\n/**\n * Entry point to the Mebius Web SDK.\n *\n * ```ts\n * Mebius.init({ appId: \"app_123\", gateway: \"https://gateway.mebius.io\" });\n * const client = Mebius.connect({ token });\n * ```\n */\nexport const Mebius = {\n /** Configure the SDK once, before connecting. */\n init(options: MebiusInitOptions): void {\n if (!options.appId) throw mebiusError(\"UNKNOWN\", \"Mebius.init requires an appId.\");\n if (!options.gateway) throw mebiusError(\"UNKNOWN\", \"Mebius.init requires a gateway URL.\");\n config = { ...options };\n },\n\n /**\n * Connect using a short-lived token minted by your backend. Returns a\n * {@link MebiusClient}. Listen for `\"connected\"` / `\"error\"` on it.\n */\n connect(options: MebiusConnectOptions): MebiusClient {\n if (!config) {\n throw mebiusError(\"UNKNOWN\", \"Call Mebius.init() before Mebius.connect().\");\n }\n if (!options.token) throw mebiusError(\"UNKNOWN\", \"Mebius.connect requires a token.\");\n const client = new MebiusClient(config, options.token);\n client.open();\n return client;\n },\n\n /** @internal Reset configuration (used in tests). */\n _reset(): void {\n config = null;\n },\n};\n"],"mappings":"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","names":["STATS_INTERVAL_MS","config"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -132,10 +132,16 @@ declare class TypedEmitter<EventMap extends Record<string, unknown>> {
|
|
|
132
132
|
protected removeAllListeners(): void;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
/**
|
|
136
|
+
* A media session direction. Deliberately neutral vocabulary ("publish" /
|
|
137
|
+
* "view") so that if a type bundler ever inlines this into the public `.d.ts`
|
|
138
|
+
* (e.g. via a private field reference), no wire-protocol term leaks to clients.
|
|
139
|
+
* The concrete path segment is derived inside {@link SignalingClient} only.
|
|
140
|
+
*/
|
|
141
|
+
type SessionKind = "publish" | "view";
|
|
142
|
+
interface SessionResult {
|
|
143
|
+
/** The remote session answer returned by the gateway. */
|
|
144
|
+
answer: string;
|
|
139
145
|
/** Resource URL to DELETE on teardown, if the gateway returned one. */
|
|
140
146
|
resourceUrl: string | null;
|
|
141
147
|
}
|
|
@@ -151,11 +157,12 @@ declare class SignalingClient {
|
|
|
151
157
|
scalePlaylistUrl(streamId: string): string;
|
|
152
158
|
/** Pull URL for balanced-mode playback. */
|
|
153
159
|
balancedStreamUrl(streamId: string): string;
|
|
160
|
+
private pathFor;
|
|
154
161
|
/**
|
|
155
162
|
* Run the session offer/answer exchange. Throws a {@link MebiusError} with a
|
|
156
163
|
* Mebius-flavored code on failure — never the raw protocol name.
|
|
157
164
|
*/
|
|
158
|
-
|
|
165
|
+
exchangeSession(kind: SessionKind, streamId: string, offer: string): Promise<SessionResult>;
|
|
159
166
|
/** Tear down a previously-created session resource. Best-effort. */
|
|
160
167
|
deleteResource(resourceUrl: string | null): Promise<void>;
|
|
161
168
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -132,10 +132,16 @@ declare class TypedEmitter<EventMap extends Record<string, unknown>> {
|
|
|
132
132
|
protected removeAllListeners(): void;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
/**
|
|
136
|
+
* A media session direction. Deliberately neutral vocabulary ("publish" /
|
|
137
|
+
* "view") so that if a type bundler ever inlines this into the public `.d.ts`
|
|
138
|
+
* (e.g. via a private field reference), no wire-protocol term leaks to clients.
|
|
139
|
+
* The concrete path segment is derived inside {@link SignalingClient} only.
|
|
140
|
+
*/
|
|
141
|
+
type SessionKind = "publish" | "view";
|
|
142
|
+
interface SessionResult {
|
|
143
|
+
/** The remote session answer returned by the gateway. */
|
|
144
|
+
answer: string;
|
|
139
145
|
/** Resource URL to DELETE on teardown, if the gateway returned one. */
|
|
140
146
|
resourceUrl: string | null;
|
|
141
147
|
}
|
|
@@ -151,11 +157,12 @@ declare class SignalingClient {
|
|
|
151
157
|
scalePlaylistUrl(streamId: string): string;
|
|
152
158
|
/** Pull URL for balanced-mode playback. */
|
|
153
159
|
balancedStreamUrl(streamId: string): string;
|
|
160
|
+
private pathFor;
|
|
154
161
|
/**
|
|
155
162
|
* Run the session offer/answer exchange. Throws a {@link MebiusError} with a
|
|
156
163
|
* Mebius-flavored code on failure — never the raw protocol name.
|
|
157
164
|
*/
|
|
158
|
-
|
|
165
|
+
exchangeSession(kind: SessionKind, streamId: string, offer: string): Promise<SessionResult>;
|
|
159
166
|
/** Tear down a previously-created session resource. Best-effort. */
|
|
160
167
|
deleteResource(resourceUrl: string | null): Promise<void>;
|
|
161
168
|
}
|
package/dist/index.global.js
CHANGED
|
@@ -33214,13 +33214,13 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
33214
33214
|
await waitForIceGathering(pc);
|
|
33215
33215
|
const localSdp = pc.localDescription?.sdp;
|
|
33216
33216
|
if (!localSdp) throw mebiusError("CONNECTION_FAILED", "Failed to create a local session.");
|
|
33217
|
-
const {
|
|
33218
|
-
"
|
|
33217
|
+
const { answer, resourceUrl } = await this.signaling.exchangeSession(
|
|
33218
|
+
"publish",
|
|
33219
33219
|
streamId,
|
|
33220
33220
|
localSdp
|
|
33221
33221
|
);
|
|
33222
33222
|
this.resourceUrl = resourceUrl;
|
|
33223
|
-
await pc.setRemoteDescription({ type: "answer", sdp:
|
|
33223
|
+
await pc.setRemoteDescription({ type: "answer", sdp: answer });
|
|
33224
33224
|
}
|
|
33225
33225
|
async replaceVideoTrack(track) {
|
|
33226
33226
|
const sender = this.pc?.getSenders().find((s) => s.track?.kind === "video");
|
|
@@ -33294,13 +33294,13 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
33294
33294
|
await waitForIceGathering(pc);
|
|
33295
33295
|
const localSdp = pc.localDescription?.sdp;
|
|
33296
33296
|
if (!localSdp) throw mebiusError("CONNECTION_FAILED", "Failed to create a local session.");
|
|
33297
|
-
const {
|
|
33298
|
-
"
|
|
33297
|
+
const { answer, resourceUrl } = await this.signaling.exchangeSession(
|
|
33298
|
+
"view",
|
|
33299
33299
|
streamId,
|
|
33300
33300
|
localSdp
|
|
33301
33301
|
);
|
|
33302
33302
|
this.resourceUrl = resourceUrl;
|
|
33303
|
-
await pc.setRemoteDescription({ type: "answer", sdp:
|
|
33303
|
+
await pc.setRemoteDescription({ type: "answer", sdp: answer });
|
|
33304
33304
|
}
|
|
33305
33305
|
async stop() {
|
|
33306
33306
|
await this.signaling.deleteResource(this.resourceUrl);
|
|
@@ -33647,21 +33647,27 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
33647
33647
|
balancedStreamUrl(streamId) {
|
|
33648
33648
|
return this.withToken(`${this.base()}/flv/${encodeURIComponent(streamId)}.flv`);
|
|
33649
33649
|
}
|
|
33650
|
-
//
|
|
33651
|
-
// view
|
|
33652
|
-
//
|
|
33650
|
+
// Maps a neutral session kind to the concrete signaling path segment. This
|
|
33651
|
+
// mapping (publish -> WHIP, view -> WHEP) lives ONLY in this method body, so
|
|
33652
|
+
// the protocol names never appear in any exported type signature.
|
|
33653
|
+
pathFor(kind) {
|
|
33654
|
+
return kind === "publish" ? "whip" : "whep";
|
|
33655
|
+
}
|
|
33656
|
+
// Performs the offer/answer exchange for a publish or a low-latency view
|
|
33657
|
+
// session. Protocol detail kept inside the method body so it never leaks into
|
|
33658
|
+
// the bundled public .d.ts.
|
|
33653
33659
|
/**
|
|
33654
33660
|
* Run the session offer/answer exchange. Throws a {@link MebiusError} with a
|
|
33655
33661
|
* Mebius-flavored code on failure — never the raw protocol name.
|
|
33656
33662
|
*/
|
|
33657
|
-
async
|
|
33658
|
-
const url = this.withToken(`${this.base()}/${kind}/${encodeURIComponent(streamId)}`);
|
|
33663
|
+
async exchangeSession(kind, streamId, offer) {
|
|
33664
|
+
const url = this.withToken(`${this.base()}/${this.pathFor(kind)}/${encodeURIComponent(streamId)}`);
|
|
33659
33665
|
let res;
|
|
33660
33666
|
try {
|
|
33661
33667
|
res = await fetch(url, {
|
|
33662
33668
|
method: "POST",
|
|
33663
33669
|
headers: this.headers("application/sdp"),
|
|
33664
|
-
body:
|
|
33670
|
+
body: offer
|
|
33665
33671
|
});
|
|
33666
33672
|
} catch (cause) {
|
|
33667
33673
|
throw mebiusError("CONNECTION_FAILED", void 0, cause);
|
|
@@ -33675,10 +33681,10 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
|
|
|
33675
33681
|
if (!res.ok) {
|
|
33676
33682
|
throw mebiusError("CONNECTION_FAILED", `Mebius gateway returned ${res.status}.`);
|
|
33677
33683
|
}
|
|
33678
|
-
const
|
|
33684
|
+
const answer = await res.text();
|
|
33679
33685
|
const location2 = res.headers.get("Location");
|
|
33680
33686
|
const resourceUrl = location2 ? new URL(location2, url).toString() : null;
|
|
33681
|
-
return {
|
|
33687
|
+
return { answer, resourceUrl };
|
|
33682
33688
|
}
|
|
33683
33689
|
/** Tear down a previously-created session resource. Best-effort. */
|
|
33684
33690
|
async deleteResource(resourceUrl) {
|