UNPKG

callskit

Version:

A toolkit for building call experience using Cloudflare Realtime

1,378 lines (1,368 loc) 50.6 kB
import { setLogLevel, PartyTracks, getMic, getCamera, devices$, getScreenshare } from 'partytracks/client'; import { Observable, BehaviorSubject, Subject, interval, of, filter as filter$1, switchMap } from 'rxjs'; import { takeUntil, debounceTime, distinctUntilChanged, filter } from 'rxjs/operators'; import invariant from 'tiny-invariant'; import PartySocket from 'partysocket'; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __typeError = (msg) => { throw TypeError(msg); }; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value); var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/utils/events-handler.ts var _callbacksMap, _globalCallbacks; var EventsHandler = class { constructor() { __privateAdd(this, _callbacksMap); __privateAdd(this, _globalCallbacks); __privateSet(this, _callbacksMap, /* @__PURE__ */ new Map()); __privateSet(this, _globalCallbacks, /* @__PURE__ */ new Set()); } emit(event, ...args) { var _a; (_a = __privateGet(this, _callbacksMap).get(event)) == null ? void 0 : _a.forEach((callback) => callback(...args)); __privateGet(this, _globalCallbacks).forEach((callback) => callback(event, ...args)); } addEventListener(event, callback) { if (!__privateGet(this, _callbacksMap).has(event)) { __privateGet(this, _callbacksMap).set(event, /* @__PURE__ */ new Set()); } __privateGet(this, _callbacksMap).get(event).add(callback); } removeEventListener(event, callback) { var _a; (_a = __privateGet(this, _callbacksMap).get(event)) == null ? void 0 : _a.delete(callback); } subscribe(event, callback) { this.addEventListener(event, callback); return () => this.removeEventListener(event, callback); } subscribeAll(callback) { __privateGet(this, _globalCallbacks).add(callback); return () => { __privateGet(this, _globalCallbacks).delete(callback); }; } unsubscribeAll(callback) { __privateGet(this, _globalCallbacks).delete(callback); } once(event, callback) { const wrapper = (...args) => { this.removeEventListener(event, wrapper); callback(...args); }; this.addEventListener(event, wrapper); return () => { this.removeEventListener(event, wrapper); }; } clearAll() { __privateGet(this, _callbacksMap).clear(); __privateGet(this, _globalCallbacks).clear(); } }; _callbacksMap = new WeakMap(); _globalCallbacks = new WeakMap(); // src/utils/logger.ts var _Logger = class _Logger { constructor(options = {}) { var _a, _b, _c; this.level = (_a = options.level) != null ? _a : "info"; this.prefix = (_b = options.prefix) != null ? _b : ""; this.enabled = (_c = options.enabled) != null ? _c : true; } shouldLog(level) { return this.enabled && _Logger.levels[level] >= _Logger.levels[this.level]; } formatMessage(level, args) { const prefix = this.prefix ? `[${this.prefix}]` : ""; return [prefix, ...args]; } debug(...args) { if (this.shouldLog("debug")) { console.debug(...this.formatMessage("debug", args)); } } info(...args) { if (this.shouldLog("info")) { console.info(...this.formatMessage("info", args)); } } warn(...args) { if (this.shouldLog("warn")) { console.warn(...this.formatMessage("warn", args)); } } error(...args) { if (this.shouldLog("error")) { console.error(...this.formatMessage("error", args)); } } setLevel(level) { this.level = level; } enable() { this.enabled = true; } disable() { this.enabled = false; } }; _Logger.levels = { debug: 0, info: 1, warn: 2, error: 3 }; var Logger = _Logger; var contextStack = []; function runWithContext(context, fn) { contextStack.push(context); try { return fn(); } finally { contextStack.pop(); } } function getCurrentCallContext() { const ctx = contextStack[contextStack.length - 1]; invariant(ctx, "No active CallContext"); return ctx; } // src/lib/call-chat/call-chat.ts var _ctx, _CallChat_instances, sendMessage_fn; var CallChat = class extends EventsHandler { constructor() { super(); __privateAdd(this, _CallChat_instances); this.messages = []; __privateAdd(this, _ctx); __privateSet(this, _ctx, getCurrentCallContext()); } sendTextMessage(message) { __privateMethod(this, _CallChat_instances, sendMessage_fn).call(this, { type: "text", message: message.trim() }); } addMessage(message) { const parsedMessage = __spreadProps(__spreadValues({}, message), { created_at: new Date(message.created_at) }); this.messages = [...this.messages, parsedMessage]; this.emit("message", parsedMessage); } addMessagesInBulk(messages) { const parsed = messages.map((message) => __spreadProps(__spreadValues({}, message), { created_at: new Date(message.created_at) })); this.messages = [...this.messages, ...parsed]; this.emit("loaded", parsed); } }; _ctx = new WeakMap(); _CallChat_instances = new WeakSet(); sendMessage_fn = function(message) { __privateGet(this, _ctx).socket.sendAction({ action: "chat/message", message }); }; new Observable( (subscriber) => { const canvas = document.createElement("canvas"); canvas.height = 720; canvas.width = 1280; const ctx = canvas.getContext("2d"); invariant(ctx); ctx.fillStyle = "black"; ctx.fillRect(0, 0, canvas.width, canvas.height); const interval2 = setInterval(() => { ctx.fillStyle = "black"; ctx.fillRect(0, 0, canvas.width, canvas.height); }, 1e3); subscriber.next(canvas.captureStream().getVideoTracks()[0]); return () => { clearInterval(interval2); }; } ); function getInaudibleTrack() { const audioContext = new AudioContext(); audioContext.resume(); const oscillator = audioContext.createOscillator(); oscillator.type = "triangle"; oscillator.frequency.setValueAtTime(20, audioContext.currentTime); const gainNode = audioContext.createGain(); gainNode.gain.setValueAtTime(0, audioContext.currentTime); oscillator.connect(gainNode); const destination = audioContext.createMediaStreamDestination(); gainNode.connect(destination); oscillator.start(); const track = destination.stream.getAudioTracks()[0]; return { track, audioContext }; } new Observable( (subscriber) => { const { audioContext, track } = getInaudibleTrack(); subscriber.next(track); return () => { track.stop(); audioContext.close(); }; } ); function createTrackId(metadata) { return `${metadata.sessionId}/${metadata.trackName}`; } function parseTrackId(trackId) { const [sessionId, trackName] = trackId.split("/"); return { sessionId, trackName }; } // src/lib/call-participant/call-participant.ts var _ctx2, _micEnabled$, _micTrackId$, _micEnabled, _micTrack, _cameraEnabled$, _cameraTrackId$, _cameraEnabled, _cameraTrack, _screenshareEnabled$, _screenshareVideoTrackId$, _screenshareAudioTrackId$, _screenshareEnabled, _screenshareVideoTrack, _screenshareAudioTrack; var _CallParticipant = class _CallParticipant extends EventsHandler { constructor(options) { super(); __privateAdd(this, _ctx2); __privateAdd(this, _micEnabled$); __privateAdd(this, _micTrackId$); __privateAdd(this, _micEnabled, false); __privateAdd(this, _micTrack); __privateAdd(this, _cameraEnabled$); __privateAdd(this, _cameraTrackId$); __privateAdd(this, _cameraEnabled, false); __privateAdd(this, _cameraTrack); __privateAdd(this, _screenshareEnabled$, new BehaviorSubject(false)); __privateAdd(this, _screenshareVideoTrackId$); __privateAdd(this, _screenshareAudioTrackId$); __privateAdd(this, _screenshareEnabled, false); __privateAdd(this, _screenshareVideoTrack); __privateAdd(this, _screenshareAudioTrack); this.id = options.id; this.name = options.name; __privateSet(this, _ctx2, getCurrentCallContext()); __privateSet(this, _micEnabled$, new BehaviorSubject( options.micEnabled || false )); __privateSet(this, _micTrackId$, new BehaviorSubject( options.micEnabled ? options.micTrackId : void 0 )); __privateSet(this, _cameraEnabled$, new BehaviorSubject( options.cameraEnabled || false )); __privateSet(this, _cameraTrackId$, new BehaviorSubject( options.cameraEnabled ? options.cameraTrackId : void 0 )); __privateSet(this, _screenshareEnabled, options.screenshareEnabled || false); __privateSet(this, _screenshareVideoTrackId$, new BehaviorSubject( options.screenshareEnabled ? options.screenshareVideoTrackId : void 0 )); __privateSet(this, _screenshareAudioTrackId$, new BehaviorSubject( options.screenshareEnabled ? options.screenshareAudioTrackId : void 0 )); __privateGet(this, _micEnabled$).subscribe((enabled) => { if (!enabled) { __privateSet(this, _micEnabled, false); this.emit("micUpdate", { micEnabled: false }); __privateGet(this, _ctx2).call.participants.joined.emit("micUpdate", this); } else if (enabled && __privateGet(this, _micTrack)) { __privateSet(this, _micEnabled, true); this.emit("micUpdate", { micEnabled: true, micTrack: __privateGet(this, _micTrack) }); __privateGet(this, _ctx2).call.participants.joined.emit("micUpdate", this); __privateGet(this, _ctx2).call.participants.emit("micUpdate", this); } }); const micMetadata$ = __privateGet(this, _micTrackId$).pipe( filter$1((id) => typeof id === "string"), switchMap((id) => { const { sessionId, trackName } = parseTrackId(id); return of({ sessionId, trackName, location: "remote" }); }) ); const micTrack$ = __privateGet(this, _ctx2).partyTracks.pull(micMetadata$); micTrack$.subscribe((track) => { __privateSet(this, _micEnabled, true); __privateSet(this, _micTrack, track); this.emit("micUpdate", { micEnabled: true, micTrack: track }); __privateGet(this, _ctx2).call.participants.joined.emit("micUpdate", this); __privateGet(this, _ctx2).call.participants.emit("micUpdate", this); }); __privateGet(this, _cameraEnabled$).subscribe((enabled) => { if (!enabled) { __privateSet(this, _cameraEnabled, false); this.emit("cameraUpdate", { cameraEnabled: false }); __privateGet(this, _ctx2).call.participants.joined.emit("cameraUpdate", this); __privateGet(this, _ctx2).call.participants.emit("cameraUpdate", this); } else if (enabled && __privateGet(this, _cameraTrack)) { __privateSet(this, _cameraEnabled, true); this.emit("cameraUpdate", { cameraEnabled: true, cameraTrack: __privateGet(this, _cameraTrack) }); __privateGet(this, _ctx2).call.participants.joined.emit("cameraUpdate", this); __privateGet(this, _ctx2).call.participants.emit("cameraUpdate", this); } }); const cameraMetadata$ = __privateGet(this, _cameraTrackId$).pipe( filter$1((id) => typeof id === "string"), switchMap((id) => { const { sessionId, trackName } = parseTrackId(id); return of({ sessionId, trackName, location: "remote" }); }) ); const cameraTrack$ = __privateGet(this, _ctx2).partyTracks.pull(cameraMetadata$, { simulcast: { preferredRid$: __privateGet(this, _ctx2).cameraRid$ } }); cameraTrack$.subscribe((track) => { __privateSet(this, _cameraEnabled, true); __privateSet(this, _cameraTrack, track); this.emit("cameraUpdate", { cameraEnabled: true, cameraTrack: track }); __privateGet(this, _ctx2).call.participants.joined.emit("cameraUpdate", this); __privateGet(this, _ctx2).call.participants.emit("cameraUpdate", this); }); const screenshareVideoMetadata$ = __privateGet(this, _screenshareVideoTrackId$).pipe( filter$1((id) => typeof id === "string"), switchMap((id) => { const { sessionId, trackName } = parseTrackId(id); return of({ sessionId, trackName, location: "remote" }); }) ); const screenshareVideoTrack$ = __privateGet(this, _ctx2).partyTracks.pull( screenshareVideoMetadata$ ); screenshareVideoTrack$.subscribe((track) => { __privateSet(this, _screenshareVideoTrack, track); this.emit("screenshareUpdate", { screenshareEnabled: true, screenshareVideoTrack: track, screenshareAudioTrack: __privateGet(this, _screenshareAudioTrack) }); __privateGet(this, _ctx2).call.participants.joined.emit("screenshareUpdate", this); __privateGet(this, _ctx2).call.participants.emit("screenshareUpdate", this); }); const screenshareAudioMetadata$ = __privateGet(this, _screenshareAudioTrackId$).pipe( filter$1((id) => typeof id === "string"), switchMap((id) => { const { sessionId, trackName } = parseTrackId(id); return of({ sessionId, trackName, location: "remote" }); }) ); const screenshareAudioTrack$ = __privateGet(this, _ctx2).partyTracks.pull( screenshareAudioMetadata$ ); screenshareAudioTrack$.subscribe((track) => { __privateSet(this, _screenshareAudioTrack, track); this.emit("screenshareUpdate", { screenshareEnabled: true, screenshareVideoTrack: __privateGet(this, _screenshareVideoTrack), screenshareAudioTrack: track }); __privateGet(this, _ctx2).call.participants.joined.emit("screenshareUpdate", this); __privateGet(this, _ctx2).call.participants.emit("screenshareUpdate", this); }); } updateMicState(updates) { __privateGet(this, _ctx2).logger.debug("\u{1F399}\uFE0F participant mic state updated", updates); if (updates.micEnabled && updates.micTrackId) { __privateGet(this, _micTrackId$).next(updates.micTrackId); __privateGet(this, _micEnabled$).next(true); } else { __privateGet(this, _micEnabled$).next(false); } } updateCameraState(updates) { __privateGet(this, _ctx2).logger.debug("\u{1F3A5} participant camera state updated", updates); if (updates.cameraEnabled && updates.cameraTrackId) { __privateGet(this, _cameraTrackId$).next(updates.cameraTrackId); __privateGet(this, _cameraEnabled$).next(true); } else { __privateGet(this, _cameraEnabled$).next(false); } } updateScreenshareState(updates) { __privateGet(this, _ctx2).logger.debug("\u{1F5A5}\uFE0F participant screenshare state updated", updates); if (updates.screenshareEnabled) { if (updates.screenshareVideoTrackId) { __privateGet(this, _screenshareVideoTrackId$).next(updates.screenshareVideoTrackId); } if (updates.screenshareAudioTrackId) { __privateGet(this, _screenshareAudioTrackId$).next(updates.screenshareAudioTrackId); } __privateGet(this, _screenshareEnabled$).next(true); __privateSet(this, _screenshareEnabled, true); } else { __privateGet(this, _screenshareEnabled$).next(false); __privateGet(this, _screenshareVideoTrackId$).next(void 0); __privateGet(this, _screenshareAudioTrackId$).next(void 0); __privateSet(this, _screenshareEnabled, false); this.emit("screenshareUpdate", { screenshareEnabled: false }); __privateGet(this, _ctx2).call.participants.joined.emit("screenshareUpdate", this); __privateGet(this, _ctx2).call.participants.emit("screenshareUpdate", this); } } get micEnabled() { return __privateGet(this, _micEnabled); } get micTrack() { return __privateGet(this, _micEnabled) ? __privateGet(this, _micTrack) : void 0; } get cameraEnabled() { return __privateGet(this, _cameraEnabled); } get cameraTrack() { return __privateGet(this, _cameraEnabled) ? __privateGet(this, _cameraTrack) : void 0; } get screenshareEnabled() { return __privateGet(this, _screenshareEnabled); } get screenshareTracks() { if (!this.screenshareEnabled) { return void 0; } return { video: __privateGet(this, _screenshareVideoTrack), audio: __privateGet(this, _screenshareAudioTrack) }; } toString() { return `CallParticipant::${this.id}::${this.name}`; } static fromJSON(obj) { return new _CallParticipant(obj); } }; _ctx2 = new WeakMap(); _micEnabled$ = new WeakMap(); _micTrackId$ = new WeakMap(); _micEnabled = new WeakMap(); _micTrack = new WeakMap(); _cameraEnabled$ = new WeakMap(); _cameraTrackId$ = new WeakMap(); _cameraEnabled = new WeakMap(); _cameraTrack = new WeakMap(); _screenshareEnabled$ = new WeakMap(); _screenshareVideoTrackId$ = new WeakMap(); _screenshareAudioTrackId$ = new WeakMap(); _screenshareEnabled = new WeakMap(); _screenshareVideoTrack = new WeakMap(); _screenshareAudioTrack = new WeakMap(); var CallParticipant = _CallParticipant; // src/lib/call-participant-map/call-participant-map.ts var _participants; var CallParticipantMap = class extends EventsHandler { constructor() { super(); __privateAdd(this, _participants, /* @__PURE__ */ new Map()); } get(id) { return __privateGet(this, _participants).get(id); } set(id, participant) { const isUpdate = __privateGet(this, _participants).has(id); __privateGet(this, _participants).set(id, participant); this.emit(isUpdate ? "updated" : "added", participant); return this; } delete(id) { const removed = __privateGet(this, _participants).get(id); const didDelete = __privateGet(this, _participants).delete(id); if (didDelete && removed) { this.emit("removed", removed); } return didDelete; } has(id) { return __privateGet(this, _participants).has(id); } values() { return __privateGet(this, _participants).values(); } keys() { return __privateGet(this, _participants).keys(); } entries() { return __privateGet(this, _participants).entries(); } forEach(callbackfn, thisArg) { return __privateGet(this, _participants).forEach(callbackfn, thisArg); } get size() { return __privateGet(this, _participants).size; } clear() { __privateGet(this, _participants).clear(); this.emit("cleared"); } toArray() { return Array.from(__privateGet(this, _participants).values()); } [Symbol.iterator]() { return __privateGet(this, _participants)[Symbol.iterator](); } get [Symbol.toStringTag]() { return "CallParticipantMap"; } }; _participants = new WeakMap(); // src/lib/participants-controller/participants-controller.ts var ParticipantsController = class extends EventsHandler { constructor() { super(); this.joined = new CallParticipantMap(); } addParticipant(participant) { this.joined.set(participant.id, participant); } removeParticipantById(participantId) { this.joined.delete(participantId); } }; var _ctx3, _mic, _camera, _screenshare, _micEnabled2, _micTrackId, _micTrack2, _cameraEnabled2, _cameraTrackId, _cameraTrack2, _screenshareEnabled2, _screenshareVideoTrackId, _screenshareVideoTrack2, _screenshareAudioTrackId, _screenshareAudioTrack2, _micDevice, _cameraDevice, _devices; var CallSelf = class extends EventsHandler { constructor(options) { var _a, _b; super(); this.id = crypto.randomUUID(); __privateAdd(this, _ctx3); __privateAdd(this, _mic); __privateAdd(this, _camera); __privateAdd(this, _screenshare); __privateAdd(this, _micEnabled2, false); __privateAdd(this, _micTrackId); __privateAdd(this, _micTrack2); __privateAdd(this, _cameraEnabled2, false); __privateAdd(this, _cameraTrackId); __privateAdd(this, _cameraTrack2); __privateAdd(this, _screenshareEnabled2, false); __privateAdd(this, _screenshareVideoTrackId); __privateAdd(this, _screenshareVideoTrack2); __privateAdd(this, _screenshareAudioTrackId); __privateAdd(this, _screenshareAudioTrack2); __privateAdd(this, _micDevice); __privateAdd(this, _cameraDevice); __privateAdd(this, _devices); this.name = options.name; __privateSet(this, _ctx3, getCurrentCallContext()); __privateSet(this, _mic, getMic()); __privateSet(this, _camera, getCamera()); __privateGet(this, _mic).isBroadcasting$.subscribe((enabled) => { __privateSet(this, _micEnabled2, enabled); }); __privateGet(this, _mic).broadcastTrack$.subscribe((track) => { __privateSet(this, _micTrack2, track); }); __privateGet(this, _camera).isBroadcasting$.subscribe((enabled) => { __privateSet(this, _cameraEnabled2, enabled); }); __privateGet(this, _camera).broadcastTrack$.subscribe((track) => { __privateSet(this, _cameraTrack2, track); }); __privateGet(this, _mic).activeDevice$.subscribe((device) => { __privateSet(this, _micDevice, device); this.emit("activeDeviceUpdate", "mic", device); }); __privateGet(this, _camera).activeDevice$.subscribe((device) => { __privateSet(this, _cameraDevice, device); this.emit("activeDeviceUpdate", "camera", device); }); devices$.subscribe((devices) => { __privateSet(this, _devices, devices); this.emit("devicesUpdate", devices); }); const micMetadata$ = __privateGet(this, _ctx3).partyTracks.push(__privateGet(this, _mic).broadcastTrack$, { sendEncodings$: of([ { networkPriority: "high" } ]) }); micMetadata$.subscribe((metadata) => { if (__privateGet(this, _micEnabled2) && __privateGet(this, _micTrack2)) { const micTrackId = createTrackId(metadata); __privateSet(this, _micTrackId, micTrackId); __privateGet(this, _ctx3).socket.sendAction({ action: "self/mic-update", updates: { micEnabled: true, micTrackId } }); this.emit("micUpdate", { micEnabled: true, micTrack: __privateGet(this, _micTrack2) }); } else if (!__privateGet(this, _micEnabled2)) { __privateGet(this, _ctx3).socket.sendAction({ action: "self/mic-update", updates: { micEnabled: false } }); this.emit("micUpdate", { micEnabled: false }); } }); const cameraMetadata$ = __privateGet(this, _ctx3).partyTracks.push( __privateGet(this, _camera).broadcastTrack$, { sendEncodings$: __privateGet(this, _ctx3).cameraEncodings$ } ); cameraMetadata$.subscribe((metadata) => { if (__privateGet(this, _cameraEnabled2) && __privateGet(this, _cameraTrack2)) { const cameraTrackId = createTrackId(metadata); __privateSet(this, _cameraTrackId, cameraTrackId); __privateGet(this, _ctx3).socket.sendAction({ action: "self/camera-update", updates: { cameraEnabled: true, cameraTrackId } }); this.emit("cameraUpdate", { cameraEnabled: true, cameraTrack: __privateGet(this, _cameraTrack2) }); } else if (!__privateGet(this, _cameraEnabled2)) { __privateSet(this, _cameraTrackId, void 0); __privateGet(this, _ctx3).socket.sendAction({ action: "self/camera-update", updates: { cameraEnabled: false } }); this.emit("cameraUpdate", { cameraEnabled: false }); } }); if ((_a = options.defaults) == null ? void 0 : _a.audio) { this.startMic(); } if ((_b = options.defaults) == null ? void 0 : _b.video) { this.startCamera(); } __privateSet(this, _screenshare, getScreenshare({ audio: true })); __privateGet(this, _screenshare).video.isBroadcasting$.subscribe((enabled) => { __privateSet(this, _screenshareEnabled2, enabled); }); __privateGet(this, _screenshare).video.broadcastTrack$.subscribe((track) => { __privateSet(this, _screenshareVideoTrack2, track); }); __privateGet(this, _screenshare).audio.broadcastTrack$.subscribe((track) => { __privateSet(this, _screenshareAudioTrack2, track); }); const screenshareVideoMetadata$ = __privateGet(this, _ctx3).partyTracks.push( __privateGet(this, _screenshare).video.broadcastTrack$ ); const screenshareAudioMetadata$ = __privateGet(this, _ctx3).partyTracks.push( __privateGet(this, _screenshare).audio.broadcastTrack$ ); screenshareVideoMetadata$.subscribe((metadata) => { if (!__privateGet(this, _screenshareEnabled2)) { __privateGet(this, _ctx3).socket.sendAction({ action: "self/screenshare-update", updates: { screenshareEnabled: false } }); this.emit("screenshareUpdate", { screenshareEnabled: false }); } else { __privateSet(this, _screenshareVideoTrackId, createTrackId(metadata)); __privateGet(this, _ctx3).socket.sendAction({ action: "self/screenshare-update", updates: { screenshareEnabled: true, screenshareVideoTrackId: __privateGet(this, _screenshareVideoTrackId) } }); this.emit("screenshareUpdate", { screenshareEnabled: true, screenshareVideoTrack: __privateGet(this, _screenshareVideoTrack2) }); } }); screenshareAudioMetadata$.subscribe((metadata) => { if (!__privateGet(this, _screenshareEnabled2)) { __privateGet(this, _ctx3).socket.sendAction({ action: "self/screenshare-update", updates: { screenshareEnabled: false } }); this.emit("screenshareUpdate", { screenshareEnabled: false }); } else { __privateSet(this, _screenshareAudioTrackId, createTrackId(metadata)); __privateGet(this, _ctx3).socket.sendAction({ action: "self/screenshare-update", updates: { screenshareEnabled: true, screenshareAudioTrackId: __privateGet(this, _screenshareAudioTrackId) } }); this.emit("screenshareUpdate", { screenshareEnabled: true, screenshareAudioTrack: __privateGet(this, _screenshareAudioTrack2) }); } }); } startMic() { __privateGet(this, _mic).startBroadcasting(); } stopMic() { __privateGet(this, _mic).stopBroadcasting(); } startCamera() { __privateGet(this, _camera).startBroadcasting(); } stopCamera() { __privateGet(this, _camera).stopBroadcasting(); } startScreenshare() { __privateGet(this, _screenshare).startBroadcasting(); } stopScreenshare() { __privateGet(this, _screenshare).stopBroadcasting(); } setCameraDevice(deviceId) { return __async(this, null, function* () { const devices = this.devices; const device = devices == null ? void 0 : devices.find((d) => d.deviceId === deviceId); if (!device) { throw new Error(`Camera device not found: ${deviceId}`); } __privateGet(this, _camera).setPreferredDevice(device); }); } setMicDevice(deviceId) { return __async(this, null, function* () { const devices = this.devices; const device = devices == null ? void 0 : devices.find((d) => d.deviceId === deviceId); if (!device) { throw new Error(`Microphone device not found: ${deviceId}`); } __privateGet(this, _mic).setPreferredDevice(device); }); } get micEnabled() { return __privateGet(this, _micEnabled2); } get micTrack() { return __privateGet(this, _micEnabled2) ? __privateGet(this, _micTrack2) : void 0; } get cameraEnabled() { return __privateGet(this, _cameraEnabled2); } get cameraTrack() { return __privateGet(this, _cameraEnabled2) ? __privateGet(this, _cameraTrack2) : void 0; } get screenshareEnabled() { return __privateGet(this, _screenshareEnabled2); } get screenshareTracks() { if (!this.screenshareEnabled) { return void 0; } return { video: __privateGet(this, _screenshareVideoTrack2), audio: __privateGet(this, _screenshareAudioTrack2) }; } get devices() { return __privateGet(this, _devices); } get activeDevices() { var _a, _b, _c, _d; return { mic: (_b = __privateGet(this, _micDevice)) != null ? _b : (_a = this.devices) == null ? void 0 : _a.find( (d) => d.deviceId === "default" && d.kind === "audioinput" ), camera: (_d = __privateGet(this, _cameraDevice)) != null ? _d : (_c = this.devices) == null ? void 0 : _c.find( (d) => d.deviceId === "default" && d.kind === "videoinput" ) }; } setName(name) { const oldName = this.name; this.name = name; this.emit("nameChange", this.name, oldName); } toJSON() { return { id: this.id, name: this.name, micEnabled: this.micEnabled, micTrackId: __privateGet(this, _micTrackId), cameraEnabled: this.cameraEnabled, cameraTrackId: __privateGet(this, _cameraTrackId), screenshareEnabled: this.screenshareEnabled, screenshareVideoTrackId: __privateGet(this, _screenshareVideoTrackId), screenshareAudioTrackId: __privateGet(this, _screenshareAudioTrackId) }; } toString() { return `CallSelf::${this.id}::${this.name}`; } }; _ctx3 = new WeakMap(); _mic = new WeakMap(); _camera = new WeakMap(); _screenshare = new WeakMap(); _micEnabled2 = new WeakMap(); _micTrackId = new WeakMap(); _micTrack2 = new WeakMap(); _cameraEnabled2 = new WeakMap(); _cameraTrackId = new WeakMap(); _cameraTrack2 = new WeakMap(); _screenshareEnabled2 = new WeakMap(); _screenshareVideoTrackId = new WeakMap(); _screenshareVideoTrack2 = new WeakMap(); _screenshareAudioTrackId = new WeakMap(); _screenshareAudioTrack2 = new WeakMap(); _micDevice = new WeakMap(); _cameraDevice = new WeakMap(); _devices = new WeakMap(); var _logger; var CallSocket = class extends PartySocket { constructor(options) { super(options); __privateAdd(this, _logger); __privateSet(this, _logger, options.logger); } sendAction(action) { __privateGet(this, _logger).debug("CallSocket:sendAction", action); return this.send(JSON.stringify(action)); } }; _logger = new WeakMap(); // src/lib/call-self/call-self-data.ts var cameraEncodings = [ { rid: "a", maxBitrate: 13e5, maxFramerate: 30, active: true }, { rid: "b", scaleResolutionDownBy: 2, maxBitrate: 5e5, maxFramerate: 24, active: true } ]; var DEFAULT_CONFIG = { monitoringInterval: 2e3, // 2 seconds adaptiveQualityEnabled: true, qualityThresholds: { excellent: { minBandwidth: 2e3, maxRtt: 100, maxPacketLoss: 1 }, good: { minBandwidth: 1e3, maxRtt: 200, maxPacketLoss: 3 }, fair: { minBandwidth: 500, maxRtt: 400, maxPacketLoss: 8 } }, qualityChangeDebounce: 3e3 // 3 seconds }; var _logger2, _config, _destroy$, _peerConnection, _statsHistory, _maxHistorySize, _lastQualityChange, _NetworkQualityMonitor_instances, startMonitoring_fn, collectNetworkStats_fn, parseWebRTCStats_fn, estimateBandwidthFromRTT_fn, calculateNetworkQuality_fn, addToHistory_fn, setupAdaptiveQuality_fn, getRecommendedCameraQuality_fn; var NetworkQualityMonitor = class { constructor(logger, config) { __privateAdd(this, _NetworkQualityMonitor_instances); __privateAdd(this, _logger2); __privateAdd(this, _config); __privateAdd(this, _destroy$, new Subject()); __privateAdd(this, _peerConnection, null); // Observables this.networkStats$ = new BehaviorSubject(null); this.networkQuality$ = new BehaviorSubject("good"); this.recommendedCameraQuality$ = new BehaviorSubject("a"); // Internal state __privateAdd(this, _statsHistory, []); __privateAdd(this, _maxHistorySize, 10); __privateAdd(this, _lastQualityChange, 0); __privateSet(this, _logger2, logger); __privateSet(this, _config, __spreadValues(__spreadValues({}, DEFAULT_CONFIG), config)); __privateMethod(this, _NetworkQualityMonitor_instances, startMonitoring_fn).call(this); __privateMethod(this, _NetworkQualityMonitor_instances, setupAdaptiveQuality_fn).call(this); } setPeerConnection(peerConnection) { __privateSet(this, _peerConnection, peerConnection); __privateGet(this, _logger2).debug("NetworkQualityMonitor: PeerConnection set"); } getAverageStats(windowSize = 5) { if (__privateGet(this, _statsHistory).length === 0) { return null; } const recentStats = __privateGet(this, _statsHistory).slice(-windowSize); const count = recentStats.length; return { rtt: recentStats.reduce((sum, stat) => sum + stat.rtt, 0) / count, packetLoss: recentStats.reduce((sum, stat) => sum + stat.packetLoss, 0) / count, jitter: recentStats.reduce((sum, stat) => sum + stat.jitter, 0) / count, bandwidth: recentStats.reduce((sum, stat) => sum + stat.bandwidth, 0) / count }; } getCurrentQuality() { return this.networkQuality$.value; } getRecommendedCameraQuality() { return this.recommendedCameraQuality$.value; } destroy() { __privateGet(this, _destroy$).next(); __privateGet(this, _destroy$).complete(); this.networkStats$.complete(); this.networkQuality$.complete(); this.recommendedCameraQuality$.complete(); } }; _logger2 = new WeakMap(); _config = new WeakMap(); _destroy$ = new WeakMap(); _peerConnection = new WeakMap(); _statsHistory = new WeakMap(); _maxHistorySize = new WeakMap(); _lastQualityChange = new WeakMap(); _NetworkQualityMonitor_instances = new WeakSet(); startMonitoring_fn = function() { interval(__privateGet(this, _config).monitoringInterval).pipe(takeUntil(__privateGet(this, _destroy$))).subscribe(() => { __privateMethod(this, _NetworkQualityMonitor_instances, collectNetworkStats_fn).call(this); }); }; collectNetworkStats_fn = function() { return __async(this, null, function* () { if (!__privateGet(this, _peerConnection)) { return; } try { const stats = yield __privateGet(this, _peerConnection).getStats(); const networkStats = __privateMethod(this, _NetworkQualityMonitor_instances, parseWebRTCStats_fn).call(this, stats); if (networkStats) { __privateMethod(this, _NetworkQualityMonitor_instances, addToHistory_fn).call(this, networkStats); this.networkStats$.next(networkStats); this.networkQuality$.next(networkStats.quality); __privateGet(this, _logger2).debug("NetworkQualityMonitor: Stats collected", { quality: networkStats.quality, rtt: networkStats.rtt, bandwidth: networkStats.bandwidth, packetLoss: networkStats.packetLoss }); } } catch (error) { __privateGet(this, _logger2).warn("NetworkQualityMonitor: Failed to collect stats", error); } }); }; parseWebRTCStats_fn = function(stats) { let rtt = 0; let packetLoss = 0; let jitter = 0; let bandwidth = 0; let hasValidStats = false; for (const [, stat] of stats) { if (stat.type === "candidate-pair" && stat.state === "succeeded") { if (typeof stat.currentRoundTripTime === "number") { rtt = stat.currentRoundTripTime * 1e3; hasValidStats = true; } } if (stat.type === "inbound-rtp" && stat.mediaType === "video") { if (typeof stat.packetsLost === "number" && typeof stat.packetsReceived === "number") { const totalPackets = stat.packetsLost + stat.packetsReceived; if (totalPackets > 0) { packetLoss = stat.packetsLost / totalPackets * 100; } } if (typeof stat.jitter === "number") { jitter = stat.jitter * 1e3; } hasValidStats = true; } if (stat.type === "transport") { if (typeof stat.selectedCandidatePairChanges === "number") { const bytesReceived = stat.bytesReceived || 0; const bytesSent = stat.bytesSent || 0; const totalBytes = bytesReceived + bytesSent; if (totalBytes > 0) { bandwidth = Math.min(totalBytes * 8 / (__privateGet(this, _config).monitoringInterval / 1e3) / 1e3, 1e4); } } } } if (!hasValidStats) { return null; } if (bandwidth === 0) { bandwidth = __privateMethod(this, _NetworkQualityMonitor_instances, estimateBandwidthFromRTT_fn).call(this, rtt, packetLoss); } const quality = __privateMethod(this, _NetworkQualityMonitor_instances, calculateNetworkQuality_fn).call(this, rtt, packetLoss, bandwidth); return { rtt, packetLoss, jitter, bandwidth, quality, timestamp: Date.now() }; }; estimateBandwidthFromRTT_fn = function(rtt, packetLoss) { if (rtt < 50 && packetLoss < 1) { return 3e3; } else if (rtt < 100 && packetLoss < 2) { return 2e3; } else if (rtt < 200 && packetLoss < 5) { return 1e3; } else { return 500; } }; calculateNetworkQuality_fn = function(rtt, packetLoss, bandwidth) { const { excellent, good, fair } = __privateGet(this, _config).qualityThresholds; if (bandwidth >= excellent.minBandwidth && rtt <= excellent.maxRtt && packetLoss <= excellent.maxPacketLoss) { return "excellent"; } if (bandwidth >= good.minBandwidth && rtt <= good.maxRtt && packetLoss <= good.maxPacketLoss) { return "good"; } if (bandwidth >= fair.minBandwidth && rtt <= fair.maxRtt && packetLoss <= fair.maxPacketLoss) { return "fair"; } return "poor"; }; addToHistory_fn = function(stats) { __privateGet(this, _statsHistory).push(stats); if (__privateGet(this, _statsHistory).length > __privateGet(this, _maxHistorySize)) { __privateGet(this, _statsHistory).shift(); } }; setupAdaptiveQuality_fn = function() { if (!__privateGet(this, _config).adaptiveQualityEnabled) { return; } this.networkQuality$.pipe( takeUntil(__privateGet(this, _destroy$)), debounceTime(__privateGet(this, _config).qualityChangeDebounce), distinctUntilChanged(), filter(() => { const now = Date.now(); const timeSinceLastChange = now - __privateGet(this, _lastQualityChange); return timeSinceLastChange >= __privateGet(this, _config).qualityChangeDebounce; }) ).subscribe((quality) => { const recommendedQuality = __privateMethod(this, _NetworkQualityMonitor_instances, getRecommendedCameraQuality_fn).call(this, quality); if (recommendedQuality !== this.recommendedCameraQuality$.value) { __privateSet(this, _lastQualityChange, Date.now()); this.recommendedCameraQuality$.next(recommendedQuality); __privateGet(this, _logger2).info("NetworkQualityMonitor: Quality change recommended", { networkQuality: quality, recommendedCameraQuality: recommendedQuality }); } }); }; getRecommendedCameraQuality_fn = function(networkQuality) { switch (networkQuality) { case "excellent": case "good": return "a"; // High quality case "fair": case "poor": return "b"; // Lower quality default: return "a"; } }; // src/lib/call-client/call-client.ts setLogLevel("debug"); var _ctx4, _logger3, _autoJoin, _networkQualityMonitor, _adaptiveQualityEnabled, _CallClient_instances, setupAdaptiveQuality_fn2, onMessage_fn; var CallClient = class extends EventsHandler { constructor(options) { var _a, _b, _c, _d; super(); __privateAdd(this, _CallClient_instances); this.state = void 0; __privateAdd(this, _ctx4); __privateAdd(this, _logger3); __privateAdd(this, _autoJoin); __privateAdd(this, _networkQualityMonitor); __privateAdd(this, _adaptiveQualityEnabled, true); this.room = options.room; __privateSet(this, _autoJoin, options.autoJoin || false); __privateSet(this, _logger3, new Logger({ level: (_a = options.logLevel) != null ? _a : "warn" // prefix: 'callskit', })); const socket = new CallSocket({ room: this.room, host: options.socketBaseUrl, logger: __privateGet(this, _logger3) }); socket.addEventListener("message", __privateMethod(this, _CallClient_instances, onMessage_fn).bind(this)); if (options.onError) { const onError = options.onError; socket.addEventListener("error", (e) => { onError(new Error("Error while connecting to server")); }); } const partyTracks = new PartyTracks({ prefix: options.apiBaseUrl + "/partytracks", headers: options.apiHeaders }); let unsubSession = partyTracks.session$.subscribe( () => { this.emit("mediaConnected"); unsubSession.unsubscribe(); unsubSession = void 0; } ); __privateSet(this, _networkQualityMonitor, new NetworkQualityMonitor( __privateGet(this, _logger3), (_b = options.config) == null ? void 0 : _b.networkQuality )); __privateSet(this, _ctx4, { socket, partyTracks, call: this, logger: __privateGet(this, _logger3), cameraRid$: new BehaviorSubject( (_d = (_c = options.config) == null ? void 0 : _c.preferredCameraQuality) != null ? _d : "a" ), cameraEncodings$: new BehaviorSubject( cameraEncodings ), networkQualityMonitor: __privateGet(this, _networkQualityMonitor), onError: options.onError }); this.self = runWithContext( __privateGet(this, _ctx4), () => new CallSelf({ name: options.displayName, defaults: options.defaults }) ); this.participants = runWithContext( __privateGet(this, _ctx4), () => new ParticipantsController() ); this.chat = runWithContext(__privateGet(this, _ctx4), () => new CallChat()); __privateMethod(this, _CallClient_instances, setupAdaptiveQuality_fn2).call(this); } join() { if (this.state !== "connected") return; __privateGet(this, _ctx4).socket.sendAction({ action: "join", self: this.self.toJSON() }); } leave() { __privateGet(this, _networkQualityMonitor).destroy(); __privateGet(this, _ctx4).socket.close(); this.state = "left"; this.emit("left"); } get cameraTrackQuality() { return __privateGet(this, _ctx4).cameraRid$.value; } /** * Set the remote camera track quality. * - `a` - high quality * - `b` - lower quality */ setRemoteCameraTrackQuality(quality) { const qualities = ["a", "b"]; if (qualities.includes(quality)) { const oldQuality = __privateGet(this, _ctx4).cameraRid$.value; __privateGet(this, _ctx4).cameraRid$.next(quality); this.emit("cameraQualityChanged", quality, oldQuality); __privateGet(this, _logger3).info("Camera quality changed manually", { from: oldQuality, to: quality }); } } /** * Enable or disable adaptive camera quality based on network conditions */ setAdaptiveQualityEnabled(enabled) { __privateSet(this, _adaptiveQualityEnabled, enabled); __privateGet(this, _logger3).info("Adaptive quality", enabled ? "enabled" : "disabled"); } /** * Get current network quality information */ getNetworkQuality() { return { quality: __privateGet(this, _networkQualityMonitor).getCurrentQuality(), stats: __privateGet(this, _networkQualityMonitor).networkStats$.value, recommendedCameraQuality: __privateGet(this, _networkQualityMonitor).getRecommendedCameraQuality() }; } }; _ctx4 = new WeakMap(); _logger3 = new WeakMap(); _autoJoin = new WeakMap(); _networkQualityMonitor = new WeakMap(); _adaptiveQualityEnabled = new WeakMap(); _CallClient_instances = new WeakSet(); /** * Setup adaptive quality switching based on network conditions */ setupAdaptiveQuality_fn2 = function() { __privateGet(this, _networkQualityMonitor).recommendedCameraQuality$.pipe(takeUntil(__privateGet(this, _ctx4).socket.addEventListener("close", () => { }))).subscribe((recommendedQuality) => { if (!__privateGet(this, _adaptiveQualityEnabled)) { return; } const currentQuality = __privateGet(this, _ctx4).cameraRid$.value; if (recommendedQuality !== currentQuality) { __privateGet(this, _logger3).info("Adaptive quality: Switching camera quality", { from: currentQuality, to: recommendedQuality, networkQuality: __privateGet(this, _networkQualityMonitor).getCurrentQuality() }); this.setRemoteCameraTrackQuality(recommendedQuality); this.emit("adaptiveQualityChanged", recommendedQuality, currentQuality); } }); const partyTracks = __privateGet(this, _ctx4).partyTracks; if (partyTracks.session$) { partyTracks.session$.subscribe((session) => { if (session == null ? void 0 : session.pc) { __privateGet(this, _networkQualityMonitor).setPeerConnection(session.pc); __privateGet(this, _logger3).debug("Network quality monitor: PeerConnection attached"); } }); } }; onMessage_fn = function(event) { const ev = JSON.parse(event.data); __privateGet(this, _logger3).debug("\u{1F4E9} CallSocket:Event", ev); switch (ev.event) { case "connected": this.state = "connected"; this.emit("connected"); if (__privateGet(this, _autoJoin)) { this.join(); } break; case "room/init": { if (this.state !== "connected") return; const user_objects = ev.participants; runWithContext( __privateGet(this, _ctx4), () => user_objects.map((obj) => CallParticipant.fromJSON(obj)) ).forEach((participant) => { this.participants.addParticipant(participant); }); this.started_at = new Date(ev.started_at); this.chat.addMessagesInBulk(ev.chatMessages); this.state = "joined"; this.emit("joined"); break; } case "participant/joined": { const participant = runWithContext( __privateGet(this, _ctx4), () => CallParticipant.fromJSON(ev.participant) ); this.participants.addParticipant(participant); break; } case "participant/left": { this.participants.removeParticipantById(ev.participantId); break; } case "participant/mic-update": { const _a = ev.data, { participantId } = _a, updates = __objRest(_a, ["participantId"]); const participant = this.participants.joined.get(participantId); participant == null ? void 0 : participant.updateMicState(updates); break; } case "participant/camera-update": { const _b = ev.data, { participantId } = _b, updates = __objRest(_b, ["participantId"]); const participant = this.participants.joined.get(participantId); participant == null ? void 0 : participant.updateCameraState(updates); break; } case "participant/screenshare-update": { const _c = ev.data, { participantId } = _c, updates = __objRest(_c, ["participantId"]); const participant = this.participants.joined.get(participantId); participant == null ? void 0 : participant.updateScreenshareState(updates); break; } case "chat/new-message": { this.chat.addMessage(ev.message); break; } default: __privateGet(this, _logger3).info("Received unknown event:", ev); break; } }; // src/index.ts function createCallClient(options) { return __async(this, null, function* () { return new Promise((resolve, reject) => { const client = new CallClient(options); let socketConnected = false, mediaConnected = false; const checkConnection = () => { if (socketConnected && mediaConnected) { resolve(client); } }; client.once("connected", () => { socketConnected = true; checkConnection(); }); client.once("mediaC