@signalwire/js
Version:
1,557 lines (1,513 loc) • 151 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __reflectGet = Reflect.get;
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 __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
var __superGet = (cls, obj, key) => __reflectGet(__getProtoOf(cls), key, obj);
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/index.ts
var index_exports = {};
__export(index_exports, {
Chat: () => chat_exports,
Fabric: () => fabric_exports,
LocalVideoOverlay: () => LocalVideoOverlay,
PubSub: () => pubSub_exports,
SignalWire: () => SignalWire,
UserOverlay: () => UserOverlay,
Video: () => video_exports,
WebRTC: () => webrtc_exports,
buildVideoElement: () => buildVideoElement
});
module.exports = __toCommonJS(index_exports);
// src/chat/index.ts
var chat_exports = {};
__export(chat_exports, {
ChatMember: () => import_core35.ChatMember,
ChatMessage: () => import_core35.ChatMessage,
Client: () => Client2
});
// src/createClient.ts
var import_core34 = require("@signalwire/core");
// src/Client.ts
var import_core31 = require("@signalwire/core");
// src/features/mediaElements/mediaElementsSagas.ts
var import_core2 = require("@signalwire/core");
var import_webrtc = require("@signalwire/webrtc");
// src/utils/audioElement.ts
var setAudioMediaTrack = ({
track,
element
}) => {
element.autoplay = true;
element.playsinline = true;
element.srcObject = new MediaStream([track]);
track.addEventListener("ended", () => {
element.srcObject = null;
element.remove();
});
return element;
};
// src/features/actions.ts
var import_core = require("@signalwire/core");
var audioSetSpeakerAction = import_core.actions.createAction(
"swJs/audioSetSpeakerAction"
);
// src/features/mediaElements/mediaElementsSagas.ts
var makeAudioElementSaga = ({ speakerId }) => {
return function* audioElementSaga({
instance: room,
runSaga
}) {
if (typeof Audio === "undefined") {
(0, import_core2.getLogger)().warn("`Audio` is not supported on this environment.");
return;
}
try {
const audioEl = room.getAudioEl();
let audioTask;
const trackHandler = function(event) {
switch (event.track.kind) {
case "audio": {
audioTask = runSaga(audioElementSetupWorker, {
track: event.track,
element: audioEl,
speakerId,
room
});
break;
}
}
};
room.on("track", trackHandler);
room.once("destroy", () => {
audioTask == null ? void 0 : audioTask.cancel();
});
} catch (error) {
(0, import_core2.getLogger)().error("audioElementSaga", error);
}
};
};
function* audioElementActionsWatcher({
element,
room
}) {
const setSpeakerActionType = import_core2.actions.getCustomSagaActionType(
room.__uuid,
audioSetSpeakerAction
);
while (true) {
const action = yield import_core2.sagaEffects.take([setSpeakerActionType]);
try {
switch (action.type) {
case setSpeakerActionType:
const response = yield import_core2.sagaEffects.call(
import_webrtc.setMediaElementSinkId,
element,
action.payload
);
room.emit(
// @ts-expect-error
`${import_core2.LOCAL_EVENT_PREFIX}.speaker.updated`,
action.payload
);
room.settleCustomSagaTrigger({
dispatchId: action.dispatchId,
payload: response,
kind: "resolve"
});
break;
}
} catch (error) {
room.settleCustomSagaTrigger({
dispatchId: action.dispatchId,
payload: error,
kind: "reject"
});
(0, import_core2.getLogger)().error(error);
}
}
}
function* audioElementSetupWorker({
track,
element,
speakerId,
room
}) {
setAudioMediaTrack({ track, element });
if (speakerId) {
(0, import_webrtc.setMediaElementSinkId)(element, speakerId).catch(() => {
});
}
yield import_core2.sagaEffects.fork(audioElementActionsWatcher, {
element,
room
});
}
// src/cantina/VideoManager.ts
var import_core6 = require("@signalwire/core");
// src/cantina/workers/videoManagerWorker.ts
var import_core5 = require("@signalwire/core");
// src/cantina/workers/videoManagerRoomsWorker.ts
var import_core3 = require("@signalwire/core");
var videoManagerRoomsWorker = function* (options) {
(0, import_core3.getLogger)().trace("videoManagerRoomsWorker started");
const {
instance: client,
action: { type, payload }
} = options;
const modPayload = {
rooms: payload.rooms.map((row) => (0, import_core3.toExternalJSON)(row))
};
client.emit(
(0, import_core3.stripNamespacePrefix)(type),
// @ts-expect-error
modPayload
);
(0, import_core3.getLogger)().trace("videoManagerRoomsWorker ended");
};
// src/cantina/workers/videoManagerRoomWorker.ts
var import_core4 = require("@signalwire/core");
var videoManagerRoomWorker = function* (options) {
(0, import_core4.getLogger)().trace("videoManagerRoomWorker started");
const {
instance: client,
action: { type, payload }
} = options;
client.emit(
(0, import_core4.stripNamespacePrefix)(type),
(0, import_core4.toExternalJSON)(payload)
);
(0, import_core4.getLogger)().trace("videoManagerRoomWorker ended");
};
// src/cantina/workers/videoManagerWorker.ts
var videoManagerWorker = function* (options) {
(0, import_core5.getLogger)().trace("videoManagerWorker started");
const {
channels: { swEventChannel }
} = options;
function* worker(action) {
const { type } = action;
switch (type) {
case "video-manager.rooms.subscribed":
yield import_core5.sagaEffects.fork(videoManagerRoomsWorker, __spreadValues({
action
}, options));
break;
case "video-manager.room.added":
case "video-manager.room.deleted":
case "video-manager.room.ended":
case "video-manager.room.started":
case "video-manager.room.updated":
yield import_core5.sagaEffects.fork(videoManagerRoomWorker, __spreadValues({
action
}, options));
break;
default:
(0, import_core5.getLogger)().warn(`Unknown video-manager event: "${type}"`);
break;
}
}
while (true) {
const action = yield import_core5.sagaEffects.take(
swEventChannel,
(action2) => {
return action2.type.startsWith("video-manager.");
}
);
yield import_core5.sagaEffects.fork(worker, action);
}
(0, import_core5.getLogger)().trace("videoManagerWorker ended");
};
// src/cantina/VideoManager.ts
var VideoManagerAPI = class extends import_core6.BaseConsumer {
constructor(options) {
super(options);
this.runWorker("videoManagerWorker", {
worker: videoManagerWorker
});
}
/** @internal */
getSubscriptions() {
const eventNamesWithPrefix = this.eventNames().map(
(event) => `video-manager.${event}`
);
return (0, import_core6.validateEventsToSubscribe)(eventNamesWithPrefix);
}
};
var createVideoManagerObject = (params) => {
const manager = (0, import_core6.connect)({
store: params.store,
Component: VideoManagerAPI
})(params);
const proxy = new Proxy(manager, {
get(target, property, receiver) {
if (property === "_eventsNamespace") {
return "";
} else if (property === "eventChannel") {
return "video-manager.rooms";
}
return Reflect.get(target, property, receiver);
}
});
return proxy;
};
// src/buildVideoElement.ts
var import_core30 = require("@signalwire/core");
// src/utils/videoElement.ts
var import_core29 = require("@signalwire/core");
// src/VideoOverlays.ts
var import_core28 = require("@signalwire/core");
// src/video/VideoRoomSession.ts
var import_core17 = require("@signalwire/core");
// src/BaseRoomSession.ts
var import_core15 = require("@signalwire/core");
var import_webrtc3 = require("@signalwire/webrtc");
// src/utils/constants.ts
var SCREENSHARE_AUDIO_CONSTRAINTS = {
echoCancellation: true,
noiseSuppression: false,
autoGainControl: false,
// @ts-expect-error
googAutoGainControl: false
};
// src/utils/roomSession.ts
var import_core7 = require("@signalwire/core");
var getJoinMediaParams = (options) => {
const {
authState,
audio = true,
video = true,
sendAudio,
sendVideo,
receiveAudio,
receiveVideo
} = options;
(0, import_core7.getLogger)().debug("getJoinMediaParams options", __spreadValues({}, options));
const { audio_allowed, video_allowed, join_as } = authState;
const joinAs = join_as != null ? join_as : "member";
const canSend = joinAs === "member";
const canSendAudio = canSend && audio_allowed === "both";
const canSendVideo = canSend && video_allowed === "both";
const canReceiveAudio = audio_allowed !== "none";
const canReceiveVideo = video_allowed !== "none";
const reqToSendAudio = Boolean(sendAudio != null ? sendAudio : audio);
const reqToSendVideo = Boolean(sendVideo != null ? sendVideo : video);
const reqToReceiveAudio = Boolean(receiveAudio != null ? receiveAudio : audio);
const reqToReceiveVideo = Boolean(receiveVideo != null ? receiveVideo : video);
if (!canSendAudio && reqToSendAudio) {
(0, import_core7.getLogger)().info(
"Not allowed to send audio on this room. Default values will be used."
);
}
if (!canSendVideo && reqToSendVideo) {
(0, import_core7.getLogger)().info(
"Not allowed to send video on this room. Default values will be used."
);
}
if (!canReceiveAudio && reqToReceiveAudio) {
(0, import_core7.getLogger)().info(
"Not allowed to receive video from the room. Default values will be used."
);
}
if (!canReceiveVideo && reqToReceiveVideo) {
(0, import_core7.getLogger)().info(
"Not allowed to receive video from the room. Default values will be used."
);
}
return {
mustSendAudio: canSendAudio && reqToSendAudio,
mustSendVideo: canSendVideo && reqToSendVideo,
mustRecvAudio: canReceiveAudio && reqToReceiveAudio,
mustRecvVideo: canReceiveVideo && reqToReceiveVideo
};
};
var checkMediaParams = (options) => {
return Object.values(options).some(Boolean);
};
var SDK_PREFIX = "sw-sdk-";
var addSDKPrefix = (id) => {
return `${SDK_PREFIX}${id}`;
};
var OVERLAY_PREFIX = "sw-overlay-";
var addOverlayPrefix = (id) => {
return `${OVERLAY_PREFIX}${id}`;
};
// src/RoomSessionScreenShare.ts
var import_core8 = require("@signalwire/core");
var import_webrtc2 = require("@signalwire/webrtc");
var RoomSessionScreenShareConnection = class extends import_webrtc2.BaseConnection {
join() {
return super.invite();
}
leave() {
return super.hangup();
}
};
var RoomSessionScreenShareAPI = (0, import_core8.extendComponent)(RoomSessionScreenShareConnection, {
audioMute: import_core8.Rooms.audioMuteMember,
audioUnmute: import_core8.Rooms.audioUnmuteMember,
videoMute: import_core8.Rooms.videoMuteMember,
videoUnmute: import_core8.Rooms.videoUnmuteMember,
setMicrophoneVolume: import_core8.Rooms.setInputVolumeMember,
setInputVolume: import_core8.Rooms.setInputVolumeMember,
setInputSensitivity: import_core8.Rooms.setInputSensitivityMember
});
// src/video/workers/memberListUpdatedWorker.ts
var import_core9 = require("@signalwire/core");
var noop = () => {
};
var EXTERNAL_MEMBER_LIST_UPDATED_EVENT = "memberList.updated";
var INTERNAL_MEMBER_LIST_UPDATED_EVENT = (0, import_core9.toInternalEventName)({
event: EXTERNAL_MEMBER_LIST_UPDATED_EVENT
});
var SYNTHETIC_MEMBER_LIST_UPDATED_EVENT = (0, import_core9.toSyntheticEvent)(
INTERNAL_MEMBER_LIST_UPDATED_EVENT
);
var MEMBER_LIST_EVENTS = [
/** Alias to `video.room.subscribed` */
"video.room.joined",
"video.member.joined",
"video.member.left",
"video.member.updated"
];
var isMemberListEvent = (event) => {
return MEMBER_LIST_EVENTS.includes(event);
};
var getMemberListEventsToSubscribe = (subscriptions) => {
return (0, import_core9.validateEventsToSubscribe)(MEMBER_LIST_EVENTS).filter((event) => {
return !subscriptions.includes(event);
});
};
var shouldHandleMemberList = (subscriptions) => {
return subscriptions.some(
(event) => event.includes(EXTERNAL_MEMBER_LIST_UPDATED_EVENT)
);
};
var getMembersFromAction = (action) => {
if (action.type === "video.room.joined") {
return action.payload.room_session.members;
}
return [action.payload.member];
};
var getUpdatedMembers = ({
action,
memberList
}) => {
const actionMembers = getMembersFromAction(action);
switch (action.type) {
case "video.member.left":
actionMembers.forEach((member) => {
memberList.delete(member.id);
});
break;
default:
actionMembers.forEach((member) => {
memberList.set(member.id, member);
});
}
return Array.from(memberList.values());
};
var initMemberListSubscriptions = (room, subscriptions) => {
const events = getMemberListEventsToSubscribe(subscriptions);
events.forEach((event) => {
room.once(event, noop);
});
const eventBridgeHandler = ({ members }) => {
room.emit(EXTERNAL_MEMBER_LIST_UPDATED_EVENT, { members });
};
room.on(SYNTHETIC_MEMBER_LIST_UPDATED_EVENT, eventBridgeHandler);
const cleanup = () => {
room.off(SYNTHETIC_MEMBER_LIST_UPDATED_EVENT, eventBridgeHandler);
};
return {
cleanup
};
};
function* membersListUpdatedWatcher({
swEventChannel,
instance
}) {
const memberList = /* @__PURE__ */ new Map();
function* worker(pubSubAction) {
const roomSessionId = pubSubAction.type === "video.room.joined" ? pubSubAction.payload.room_session.id : pubSubAction.payload.room_session_id;
const members = getUpdatedMembers({ action: pubSubAction, memberList });
const memberListPayload = {
/**
* At this point it's needed to send the
* `room_session_id` so the pubSubSaga can properly
* infer the namespace for emitting the events to the
* appropiate room.
*/
room_session_id: roomSessionId,
members
};
instance.emit(SYNTHETIC_MEMBER_LIST_UPDATED_EVENT, memberListPayload);
}
while (true) {
const pubSubAction = yield import_core9.sagaEffects.take(
swEventChannel,
({ type }) => {
return isMemberListEvent(type);
}
);
yield import_core9.sagaEffects.fork(worker, pubSubAction);
}
}
var memberListUpdatedWorker = function* membersChangedWorker({
channels: { swEventChannel },
instance
}) {
const subscriptions = instance.getSubscriptions();
if (!shouldHandleMemberList(subscriptions)) {
return;
}
const { cleanup } = initMemberListSubscriptions(instance, subscriptions);
yield import_core9.sagaEffects.fork(membersListUpdatedWatcher, {
swEventChannel,
instance
});
instance.once("destroy", () => {
cleanup();
});
};
// src/video/workers/childMemberJoinedWorker.ts
var import_core10 = require("@signalwire/core");
var childMemberJoinedWorker = function* (options) {
(0, import_core10.getLogger)().trace("childMemberJoinedWorker started");
const { channels, instance, initialState, onDone, onFail } = options;
const { swEventChannel } = channels;
const { parentId } = initialState;
if (!parentId) {
throw new Error("Missing parentId for childMemberJoinedWorker");
}
const action = yield import_core10.sagaEffects.take(swEventChannel, (action2) => {
if (action2.type === "video.member.joined") {
return action2.payload.member.parent_id === parentId;
}
return false;
});
const { member } = action.payload;
if (member == null ? void 0 : member.parent_id) {
const byId = yield import_core10.sagaEffects.select(
import_core10.componentSelectors.getComponentsById
);
const parent = Object.values(byId).find((row) => {
return "memberId" in row && row.memberId === member.parent_id;
});
if (parent) {
yield import_core10.sagaEffects.put(
import_core10.componentActions.upsert({
id: instance.callId,
roomId: action.payload.room_id,
roomSessionId: action.payload.room_session_id,
memberId: member.id
})
);
onDone == null ? void 0 : onDone();
} else {
onFail == null ? void 0 : onFail({ error: new Error("Unknown parent_id") });
}
}
(0, import_core10.getLogger)().trace("childMemberJoinedWorker ended");
};
// src/video/workers/videoWorker.ts
var import_core14 = require("@signalwire/core");
// src/video/workers/videoStreamWorker.ts
var import_core11 = require("@signalwire/core");
var videoStreamWorker = function* (options) {
(0, import_core11.getLogger)().trace("videoStreamWorker started");
const {
instance: roomSession,
action: { type, payload },
instanceMap: { get, set, remove }
} = options;
let streamInstance = get(payload.stream.id);
if (!streamInstance) {
streamInstance = import_core11.Rooms.createRoomSessionStreamObject({
store: roomSession.store,
payload
});
} else {
streamInstance.setPayload(payload);
}
set(payload.stream.id, streamInstance);
switch (type) {
case "video.stream.started":
roomSession.emit("stream.started", streamInstance);
break;
case "video.stream.ended":
roomSession.emit("stream.ended", streamInstance);
remove(payload.stream.id);
break;
default:
(0, import_core11.getLogger)().warn(`Unknown video.stream event: "${type}"`);
break;
}
(0, import_core11.getLogger)().trace("videoStreamWorker ended");
};
// src/video/workers/videoRecordWorker.ts
var import_core12 = require("@signalwire/core");
var videoRecordWorker = function* (options) {
(0, import_core12.getLogger)().trace("videoRecordWorker started");
const {
instance: roomSession,
action: { type, payload },
instanceMap: { get, set, remove }
} = options;
let recordingInstance = get(payload.recording.id);
if (!recordingInstance) {
recordingInstance = import_core12.Rooms.createRoomSessionRecordingObject({
store: roomSession.store,
payload
});
} else {
recordingInstance.setPayload(payload);
}
set(payload.recording.id, recordingInstance);
const event = (0, import_core12.stripNamespacePrefix)(type);
switch (type) {
case "video.recording.started":
case "video.recording.updated": {
roomSession.emit(event, recordingInstance);
break;
}
case "video.recording.ended":
roomSession.emit(event, recordingInstance);
remove(payload.recording.id);
break;
default:
(0, import_core12.getLogger)().warn(`Unknown video.stream event: "${type}"`);
break;
}
(0, import_core12.getLogger)().trace("videoRecordWorker ended");
};
// src/video/workers/videoPlaybackWorker.ts
var import_core13 = require("@signalwire/core");
var videoPlaybackWorker = function* (options) {
(0, import_core13.getLogger)().trace("videoPlaybackWorker started");
const {
instance: roomSession,
action: { type, payload },
instanceMap: { get, set, remove }
} = options;
let playbackInstance = get(payload.playback.id);
if (!playbackInstance) {
playbackInstance = import_core13.Rooms.createRoomSessionPlaybackObject({
store: roomSession.store,
payload
});
} else {
playbackInstance.setPayload(payload);
}
set(payload.playback.id, playbackInstance);
const event = (0, import_core13.stripNamespacePrefix)(type);
switch (type) {
case "video.playback.started":
case "video.playback.updated": {
roomSession.emit(event, playbackInstance);
break;
}
case "video.playback.ended":
roomSession.emit(event, playbackInstance);
remove(payload.playback.id);
break;
default:
(0, import_core13.getLogger)().warn(`Unknown video.stream event: "${type}"`);
break;
}
(0, import_core13.getLogger)().trace("videoPlaybackWorker ended");
};
// src/video/workers/videoWorker.ts
var videoWorker = function* (options) {
(0, import_core14.getLogger)().trace("videoWorker started");
const { channels, instance: roomSession } = options;
const { swEventChannel } = channels;
function* worker(action) {
const { type, payload } = action;
switch (type) {
case "video.room.subscribed":
yield import_core14.sagaEffects.fork(import_core14.MemberPosition.memberPositionWorker, __spreadProps(__spreadValues({}, options), {
instance: roomSession,
initialState: payload
}));
return;
case "video.playback.started":
case "video.playback.updated":
case "video.playback.ended":
yield import_core14.sagaEffects.fork(videoPlaybackWorker, __spreadValues({
action
}, options));
return;
// Return since we don't need to handle the raw event for this
case "video.recording.started":
case "video.recording.updated":
case "video.recording.ended":
yield import_core14.sagaEffects.fork(videoRecordWorker, __spreadValues({
action
}, options));
return;
case "video.stream.ended":
case "video.stream.started":
yield import_core14.sagaEffects.fork(videoStreamWorker, __spreadValues({
action
}, options));
return;
case "video.room.audience_count": {
roomSession.emit("room.audienceCount", payload);
return;
}
case "video.member.talking": {
const { member } = payload;
if ("talking" in member) {
const suffix = member.talking ? "started" : "ended";
roomSession.emit(`member.talking.${suffix}`, payload);
const deprecatedSuffix = member.talking ? "start" : "stop";
roomSession.emit(`member.talking.${deprecatedSuffix}`, payload);
}
break;
}
case "video.layout.changed": {
roomSession.currentLayoutEvent = action.payload;
break;
}
default:
break;
}
const event = (0, import_core14.stripNamespacePrefix)(type, "video");
roomSession.emit(event, payload);
}
const isVideoEvent = (action) => {
return action.type.startsWith("video.");
};
while (true) {
const action = yield import_core14.sagaEffects.take(
swEventChannel,
isVideoEvent
);
yield import_core14.sagaEffects.fork(worker, action);
}
(0, import_core14.getLogger)().trace("videoWorker ended");
};
// src/BaseRoomSession.ts
var BaseRoomSessionConnection = class _BaseRoomSessionConnection extends import_webrtc3.BaseConnection {
constructor() {
super(...arguments);
__publicField(this, "_screenShareList", /* @__PURE__ */ new Set());
__publicField(this, "_audioEl");
__publicField(this, "_overlayMap");
__publicField(this, "_localVideoOverlay");
}
get audioEl() {
return this._audioEl;
}
set overlayMap(map) {
this._overlayMap = map;
}
get overlayMap() {
return this._overlayMap;
}
set localVideoOverlay(overlay) {
this._localVideoOverlay = overlay;
}
get localVideoOverlay() {
return this._localVideoOverlay;
}
get screenShareList() {
return Array.from(this._screenShareList);
}
_attachSpeakerTrackListener() {
if (!(0, import_webrtc3.supportsMediaOutput)()) return;
(0, import_webrtc3.createSpeakerDeviceWatcher)().then((deviceWatcher) => {
deviceWatcher.on("removed", (data) => __async(this, null, function* () {
var _a, _b;
const sinkId = this._audioEl.sinkId;
const disconnectedSpeaker = data.changes.find((device) => {
const payloadDeviceId = device.payload.deviceId;
return payloadDeviceId === sinkId || payloadDeviceId === "" && sinkId === "default" || payloadDeviceId === "default" && sinkId === "";
});
if (disconnectedSpeaker) {
this.emit("speaker.disconnected", {
deviceId: disconnectedSpeaker.payload.deviceId,
label: disconnectedSpeaker.payload.label
});
yield (_b = (_a = this._audioEl).setSinkId) == null ? void 0 : _b.call(_a, "");
const defaultSpeakers = yield (0, import_webrtc3.getSpeakerById)("default");
if (!(defaultSpeakers == null ? void 0 : defaultSpeakers.deviceId)) return;
this.emit("speaker.updated", {
previous: {
deviceId: disconnectedSpeaker.payload.deviceId,
label: disconnectedSpeaker.payload.label
},
current: {
deviceId: defaultSpeakers.deviceId,
label: defaultSpeakers.label
}
});
}
}));
});
}
/** @internal */
_finalize() {
this._screenShareList.clear();
super._finalize();
}
/** @internal */
hangup(id) {
return __async(this, null, function* () {
this._screenShareList.forEach((screenShare) => {
screenShare.leave();
});
return __superGet(_BaseRoomSessionConnection.prototype, this, "hangup").call(this, id);
});
}
leave() {
return this.hangup();
}
/**
* This method will be called by `join()` right before the
* `connect()` happens and it's a way for us to control
* exactly when the workers are attached.
* @internal
*/
attachPreConnectWorkers() {
this.runWorker("memberListUpdated", {
worker: memberListUpdatedWorker
});
}
/** @internal */
getAudioEl() {
if (this._audioEl) return this._audioEl;
this._audioEl = new Audio();
this._attachSpeakerTrackListener();
return this._audioEl;
}
getMemberOverlay(memberId) {
return this.overlayMap.get(addOverlayPrefix(memberId));
}
/**
* Allow sharing the screen within the room.
*/
startScreenShare() {
return __async(this, arguments, function* (opts = {}) {
return new Promise((resolve, reject) => __async(this, null, function* () {
var _a;
const {
autoJoin = true,
audio = false,
video = true,
layout,
positions
} = opts;
try {
const displayStream = yield (0, import_webrtc3.getDisplayMedia)({
audio: audio === true ? SCREENSHARE_AUDIO_CONSTRAINTS : audio,
video
});
const options = __spreadProps(__spreadValues({}, this.options), {
screenShare: true,
recoverCall: false,
localStream: displayStream,
remoteStream: void 0,
userVariables: __spreadProps(__spreadValues({}, ((_a = this.options) == null ? void 0 : _a.userVariables) || {}), {
memberCallId: this.callId,
memberId: this.memberId
}),
layout,
positions
});
const screenShare = (0, import_core15.connect)({
store: this.store,
Component: RoomSessionScreenShareAPI
})(options);
displayStream.getVideoTracks().forEach((t) => {
t.addEventListener("ended", () => {
if (screenShare && screenShare.active) {
screenShare.leave();
}
});
});
screenShare.once("destroy", () => {
screenShare.emit("room.left");
this._screenShareList.delete(screenShare);
});
screenShare.runWorker("childMemberJoinedWorker", {
worker: childMemberJoinedWorker,
onDone: () => resolve(screenShare),
onFail: reject,
initialState: {
parentId: this.memberId
}
});
this._screenShareList.add(screenShare);
if (autoJoin) {
return yield screenShare.join();
}
return resolve(screenShare);
} catch (error) {
this.logger.error("ScreenShare Error", error);
reject(error);
}
}));
});
}
updateSpeaker({ deviceId }) {
const prevId = this.audioEl.sinkId;
this.once(
// @ts-expect-error
`${import_core15.LOCAL_EVENT_PREFIX}.speaker.updated`,
(newId) => __async(this, null, function* () {
const prevSpeaker = yield (0, import_webrtc3.getSpeakerById)(prevId);
const newSpeaker = yield (0, import_webrtc3.getSpeakerById)(newId);
const isSame = (newSpeaker == null ? void 0 : newSpeaker.deviceId) === (prevSpeaker == null ? void 0 : prevSpeaker.deviceId);
if (!(newSpeaker == null ? void 0 : newSpeaker.deviceId) || isSame) return;
this.emit("speaker.updated", {
previous: {
deviceId: prevSpeaker == null ? void 0 : prevSpeaker.deviceId,
label: prevSpeaker == null ? void 0 : prevSpeaker.label
},
current: {
deviceId: newSpeaker.deviceId,
label: newSpeaker.label
}
});
})
);
return this.triggerCustomSaga(audioSetSpeakerAction(deviceId));
}
};
// src/RoomSessionDevice.ts
var import_core16 = require("@signalwire/core");
var import_webrtc4 = require("@signalwire/webrtc");
var RoomSessionDeviceConnection = class extends import_webrtc4.BaseConnection {
join() {
return super.invite();
}
leave() {
return super.hangup();
}
};
var RoomSessionDeviceAPI = (0, import_core16.extendComponent)(RoomSessionDeviceConnection, {
audioMute: import_core16.Rooms.audioMuteMember,
audioUnmute: import_core16.Rooms.audioUnmuteMember,
videoMute: import_core16.Rooms.videoMuteMember,
videoUnmute: import_core16.Rooms.videoUnmuteMember,
setInputVolume: import_core16.Rooms.setInputVolumeMember,
setMicrophoneVolume: import_core16.Rooms.setInputVolumeMember,
setInputSensitivity: import_core16.Rooms.setInputSensitivityMember
});
// src/video/VideoRoomSession.ts
var VideoRoomSessionConnection = class _VideoRoomSessionConnection extends BaseRoomSessionConnection {
constructor(options) {
super(options);
__publicField(this, "_deviceList", /* @__PURE__ */ new Set());
__publicField(this, "_currentLayoutEvent");
this.initWorker();
}
set currentLayoutEvent(event) {
this._currentLayoutEvent = event;
}
get currentLayoutEvent() {
return this._currentLayoutEvent;
}
get currentLayout() {
var _a;
return (_a = this._currentLayoutEvent) == null ? void 0 : _a.layout;
}
get currentPosition() {
var _a, _b;
return (_b = (_a = this._currentLayoutEvent) == null ? void 0 : _a.layout.layers.find(
(layer) => layer.member_id === this.memberId
)) == null ? void 0 : _b.position;
}
get deviceList() {
return Array.from(this._deviceList);
}
get interactivityMode() {
return this.select(({ session }) => {
var _a;
const { authState } = session;
return (_a = authState == null ? void 0 : authState.join_as) != null ? _a : "";
});
}
get permissions() {
return this.select(({ session }) => {
var _a, _b;
const { authState } = session;
return (_b = (_a = authState == null ? void 0 : authState.room) == null ? void 0 : _a.scopes) != null ? _b : [];
});
}
initWorker() {
this.runWorker("videoWorker", {
worker: videoWorker
});
}
/** @internal */
getSubscriptions() {
const eventNamesWithPrefix = this.eventNames().map((event) => {
return `video.${String(event)}`;
});
return (0, import_core17.validateEventsToSubscribe)(
eventNamesWithPrefix
);
}
/** @internal */
_finalize() {
this._deviceList.clear();
super._finalize();
}
/** @internal */
hangup(id) {
return __async(this, null, function* () {
this._deviceList.forEach((device) => {
device.leave();
});
return __superGet(_VideoRoomSessionConnection.prototype, this, "hangup").call(this, id);
});
}
join() {
return super.invite();
}
/**
* @deprecated Use {@link getLayouts} instead. `getLayoutList` will
* be removed in v3.0.0
*/
getLayoutList() {
return this.getLayouts();
}
/**
* @deprecated Use {@link getMembers} instead. `getMemberList` will
* be removed in v3.0.0
*/
getMemberList() {
return this.getMembers();
}
/** @deprecated Use {@link startScreenShare} instead. */
createScreenShareObject() {
return __async(this, arguments, function* (opts = {}) {
return this.startScreenShare(opts);
});
}
/**
* Allow to add a camera to the room.
*/
addCamera(opts = {}) {
const _a = opts, { autoJoin = true } = _a, video = __objRest(_a, ["autoJoin"]);
return this.addDevice({
autoJoin,
video
});
}
/**
* Allow to add a microphone to the room.
*/
addMicrophone(opts = {}) {
const _a = opts, { autoJoin = true } = _a, audio = __objRest(_a, ["autoJoin"]);
return this.addDevice({
autoJoin,
audio
});
}
/**
* Allow to add additional devices to the room like cameras or microphones.
*/
addDevice() {
return __async(this, arguments, function* (opts = {}) {
return new Promise((resolve, reject) => __async(this, null, function* () {
var _a;
const { autoJoin = true, audio = false, video = false } = opts;
if (!audio && !video) {
throw new TypeError(
"At least one of `audio` or `video` must be requested."
);
}
const options = __spreadProps(__spreadValues({}, this.options), {
localStream: void 0,
remoteStream: void 0,
audio,
video,
additionalDevice: true,
recoverCall: false,
userVariables: __spreadProps(__spreadValues({}, ((_a = this.options) == null ? void 0 : _a.userVariables) || {}), {
memberCallId: this.callId,
memberId: this.memberId
})
});
const roomDevice = (0, import_core17.connect)({
store: this.store,
Component: RoomSessionDeviceAPI
})(options);
roomDevice.once("destroy", () => {
roomDevice.emit("room.left");
this._deviceList.delete(roomDevice);
});
try {
roomDevice.runWorker("childMemberJoinedWorker", {
worker: childMemberJoinedWorker,
onDone: () => resolve(roomDevice),
onFail: reject,
initialState: {
parentId: this.memberId
}
});
this._deviceList.add(roomDevice);
if (autoJoin) {
return yield roomDevice.join();
}
return resolve(roomDevice);
} catch (error) {
this.logger.error("RoomDevice Error", error);
reject(error);
}
}));
});
}
};
var VideoRoomSessionAPI = (0, import_core17.extendComponent)(VideoRoomSessionConnection, {
audioMute: import_core17.Rooms.audioMuteMember,
audioUnmute: import_core17.Rooms.audioUnmuteMember,
videoMute: import_core17.Rooms.videoMuteMember,
videoUnmute: import_core17.Rooms.videoUnmuteMember,
deaf: import_core17.Rooms.deafMember,
undeaf: import_core17.Rooms.undeafMember,
setInputVolume: import_core17.Rooms.setInputVolumeMember,
setOutputVolume: import_core17.Rooms.setOutputVolumeMember,
setMicrophoneVolume: import_core17.Rooms.setInputVolumeMember,
setSpeakerVolume: import_core17.Rooms.setOutputVolumeMember,
setInputSensitivity: import_core17.Rooms.setInputSensitivityMember,
removeMember: import_core17.Rooms.removeMember,
removeAllMembers: import_core17.Rooms.removeAllMembers,
getMembers: import_core17.Rooms.getMembers,
getLayouts: import_core17.Rooms.getLayouts,
setLayout: import_core17.Rooms.setLayout,
setPositions: import_core17.Rooms.setPositions,
setMemberPosition: import_core17.Rooms.setMemberPosition,
hideVideoMuted: import_core17.Rooms.hideVideoMuted,
showVideoMuted: import_core17.Rooms.showVideoMuted,
getRecordings: import_core17.Rooms.getRecordings,
startRecording: import_core17.Rooms.startRecording,
getPlaybacks: import_core17.Rooms.getPlaybacks,
play: import_core17.Rooms.play,
setHideVideoMuted: import_core17.Rooms.setHideVideoMuted,
getMeta: import_core17.Rooms.getMeta,
setMeta: import_core17.Rooms.setMeta,
updateMeta: import_core17.Rooms.updateMeta,
deleteMeta: import_core17.Rooms.deleteMeta,
getMemberMeta: import_core17.Rooms.getMemberMeta,
setMemberMeta: import_core17.Rooms.setMemberMeta,
updateMemberMeta: import_core17.Rooms.updateMemberMeta,
deleteMemberMeta: import_core17.Rooms.deleteMemberMeta,
promote: import_core17.Rooms.promote,
demote: import_core17.Rooms.demote,
getStreams: import_core17.Rooms.getStreams,
startStream: import_core17.Rooms.startStream,
lock: import_core17.Rooms.lock,
unlock: import_core17.Rooms.unlock,
setRaisedHand: import_core17.Rooms.setRaisedHand,
setPrioritizeHandraise: import_core17.Rooms.setPrioritizeHandraise
});
var isVideoRoomSession = (room) => {
return room instanceof VideoRoomSessionConnection;
};
var createVideoRoomSessionObject = (params) => {
const room = (0, import_core17.connect)({
store: params.store,
customSagas: params.customSagas,
Component: VideoRoomSessionAPI
})(params);
return room;
};
// src/fabric/FabricRoomSession.ts
var import_core27 = require("@signalwire/core");
// src/utils/storage.ts
var import_jwt_decode = __toESM(require("jwt-decode"));
var import_core18 = require("@signalwire/core");
var getStorage = () => {
if (window && window.sessionStorage) {
return window.sessionStorage;
}
return void 0;
};
var sessionStorageManager = (token) => {
var _a;
let roomName = "";
try {
const jwtPayload = (0, import_jwt_decode.default)(token);
roomName = (_a = jwtPayload == null ? void 0 : jwtPayload.r) != null ? _a : "";
} catch (e) {
try {
const jwtPayload = (0, import_jwt_decode.default)(token, { header: true });
roomName = jwtPayload.typ || "";
} catch (e2) {
if (process.env.NODE_ENV !== "production") {
(0, import_core18.getLogger)().error("[sessionStorageManager] error decoding JWT", token);
}
roomName = "";
}
}
const valid = Boolean(roomName);
return {
authStateKey: valid && `as-${roomName}`,
protocolKey: valid && `pt-${roomName}`,
callIdKey: valid && `ci-${roomName}`
};
};
// src/fabric/utils/constants.ts
var PREVIOUS_CALLID_STORAGE_KEY = "ci-SAT";
// src/fabric/workers/wsClientWorker.ts
var import_core19 = require("@signalwire/core");
var wsClientWorker = function* (options) {
(0, import_core19.getLogger)().debug("wsClientWorker started");
const { channels, initialState, instance: client } = options;
const { swEventChannel } = channels;
const { handleIncomingInvite } = initialState;
function* fireHoseWorker(action) {
client.emit(action.type, action.payload);
}
function* vertoInviteWorker(action) {
handleIncomingInvite(action.payload.params);
}
const isVertoInvite = (action) => {
if (action.type === "webrtc.message") {
return action.payload.method === "verto.invite";
}
return false;
};
try {
while (true) {
const action = yield import_core19.sagaEffects.take(
swEventChannel,
() => true
);
yield import_core19.sagaEffects.fork(fireHoseWorker, action);
if (isVertoInvite(action)) {
(0, import_core19.getLogger)().debug("Receiving a call over WebSocket", action);
yield import_core19.sagaEffects.fork(
vertoInviteWorker,
action
);
}
}
} finally {
(0, import_core19.getLogger)().trace("wsClientWorker ended");
}
};
// src/fabric/workers/conversationWorker.ts
var import_core20 = require("@signalwire/core");
var conversationWorker = function* (options) {
(0, import_core20.getLogger)().debug("conversationWorker started");
const {
channels: { swEventChannel },
initialState
} = options;
const { conversation } = initialState;
const isConversationEvent = (action) => {
return action.type.startsWith("conversation.");
};
while (true) {
const action = yield import_core20.sagaEffects.take(
swEventChannel,
isConversationEvent
);
conversation.handleEvent(action.payload);
}
(0, import_core20.getLogger)().trace("conversationWorker ended");
};
// src/fabric/workers/fabricWorker.ts
var import_core26 = require("@signalwire/core");
// src/fabric/FabricRoomSessionMember.ts
var import_core21 = require("@signalwire/core");
var FabricRoomSessionMemberAPI = class extends import_core21.BaseComponent {
constructor(options) {
super(options);
__publicField(this, "_payload");
this._payload = options.payload;
}
get id() {
return this._payload.member.member_id;
}
get callId() {
return this._payload.member.call_id;
}
get nodeId() {
return this._payload.member.node_id;
}
get memberId() {
return this.id;
}
get roomSessionId() {
return this._payload.room_session_id;
}
get roomId() {
return this._payload.room_id;
}
get parentId() {
return this._payload.member.parent_id;
}
get name() {
return this._payload.member.name;
}
get type() {
return this._payload.member.type;
}
get requestedPosition() {
return this._payload.member.requested_position;
}
get currentPosition() {
return this._payload.member.current_position;
}
get meta() {
return this._payload.member.meta;
}
get handraised() {
return this._payload.member.handraised;
}
get talking() {
return this._payload.member.talking;
}
get audioMuted() {
return this._payload.member.audio_muted;
}
get videoMuted() {
return this._payload.member.video_muted;
}
get deaf() {
return this._payload.member.deaf;
}
get visible() {
return this._payload.member.visible;
}
get inputVolume() {
return this._payload.member.input_volume;
}
get outputVolume() {
return this._payload.member.output_volume;
}
get inputSensitivity() {
return this._payload.member.input_sensitivity;
}
get subscriberData() {
return this._payload.member.subscriber_data;
}
/** @internal */
setPayload(payload) {
const newPayload = __spreadProps(__spreadValues(__spreadValues({}, this._payload), payload), {
member: __spreadValues(__spreadValues({}, this._payload.member), payload.member)
});
this._payload = newPayload;
}
};
var createFabricRoomSessionMemberObject = (params) => {
const member = (0, import_core21.connect)({
store: params.store,
Component: FabricRoomSessionMemberAPI
})(params);
return member;
};
// src/fabric/workers/callSegmentWorker.ts
var import_core25 = require("@signalwire/core");
// src/fabric/workers/callLeftWorker.ts
var import_core22 = require("@signalwire/core");
var callLeftWorker = function* (options) {
(0, import_core22.getLogger)().trace("callLeftWorker started");
const {
action: { payload },
instance: cfRoomSession,
instanceMap
} = options;
const { room_session_id } = payload;
instanceMap.getAll().forEach(([key, obj]) => {
if (obj instanceof FabricRoomSessionMemberAPI && obj.roomSessionId === room_session_id) {
instanceMap.remove(key);
}
});
cfRoomSession.emit("call.left", payload);
cfRoomSession.emit("room.left", payload);
(0, import_core22.getLogger)().trace("callLeftWorker ended");
};
// src/fabric/workers/callJoinWorker.ts
var import_core24 = require("@signalwire/core");
// src/fabric/workers/fabricMemberWorker.ts
var import_core23 = require("@signalwire/core");
var fabricMemberWorker = function* (options) {
(0, import_core23.getLogger)().trace("fabricMemberWorker started");
const {
instance: roomSession,
action: { type, payload },
instanceMap: { get, set, remove }
} = options;
const memberId = payload.member.member_id;
let memberInstance = get(memberId);
if (!memberInstance && type !== "member.talking") {
memberInstance = createFabricRoomSessionMemberObject({
store: roomSession.store,
payload
});
}
if (memberInstance) {
memberInstance.setPayload(payload);
}
set(memberId, memberInstance);
if (type.startsWith("member.updated.")) {
const clientType = (0, import_core23.fromSnakeToCamelCase)(type);
roomSession.emit(clientType, payload);
}
switch (type) {
case "member.joined":
roomSession.emit(type, payload);
break;
case "member.updated":
roomSession.emit(type, payload);
break;
case "member.left":
roomSession.emit(type, payload);
remove(memberId);
break;
case "member.talking":
roomSession.emit(type, payload);
break;
default:
break;
}
(0, import_core23.getLogger)().trace("fabricMemberWorker ended");
};
// src/fabric/utils/helpers.ts
var mapInternalFabricMemberToInternalVideoMemberEntity = (params) => {
return {
id: params.member_id,
room_id: params.room_id,
room_session_id: params.room_session_id,
name: params.name,
type: params.type,
handraised: params.handraised,
visible: params.visible,
audio_muted: params.audio_muted,
video_muted: params.video_muted,
deaf: params.deaf,
input_volume: params.input_volume,
output_volume: params.output_volume,
input_sensitivity: params.input_sensitivity,
meta: params.meta,
talking: params.talking,
current_position: params.current_position,
requested_position: params.requested_position,
parent_id: params.parent_id
};
};
var mapInternalFabricMemberToInternalVideoMemberUpdatedEntity = (params) => {
return __spreadProps(__spreadValues({}, mapInternalFabricMemberToInternalVideoMemberEntity(params)), {
updated: params.updated.map(
(key) => key === "member_id" ? "id" : key
)
});
};
var mapInternalFabricRoomToInternalVideoRoomEntity = (params) => {
return {
room_id: params.room_id,
room_session_id: params.room_session_id,
event_channel: params.event_channel,
name: params.name,
recording: params.recording,
hide_video_muted: params.hide_video_muted,
preview_url: params.preview_url,
recordings: params.recordings,
playbacks: params.playbacks,
streams: params.streams,
prioritize_handraise: params.prioritize_handraise
};
};
var mapInternalFabricRoomToInternalVideoRoomSessionEntity = (params) => {
return {
id: params.id,
display_name: params.display_name,
room_id: params.room_id,
room_session_id: params.room_session_id,
event_channel: params.event_channel,
name: params.name,
recording: params.recording,
recordings: params.recordings,
playbacks: params.playbacks,
hide_video_muted: params.hide_video_muted,
preview_url: params.preview_url,
layout_name: params.layout_name,
locked: params.locked,
meta: params.meta,
members: params.members.map(
mapInternalFabricMemberToInternalVideoMemberEntity
),
streaming: params.streaming,
streams: params.streams,
prioritize_handrais