@signalwire/js 4.0.0-beta.10 → 4.0.0-beta.11

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["result: Awaited<T>","logger","baseURL: string","credential: SDKCredential","timeout","headers: HTTPHeaders","logger","STORED_NUMBER_KEYS: (keyof StoredPreferences & keyof Preferences)[]","STORED_BOOLEAN_KEYS: (keyof StoredPreferences & keyof Preferences)[]","logger","initialDevicesState: DevicesState","initialSelectedDevicesState: SelectedDevicesState","webRTCApiProvider: WebRTCApiProvider","device","devicesByKind: DevicesState","storageImpl: Storage","item: string | null","fromPath: string","http: HTTPRequestController","uuid","SDK_TO_VERTO_FIELD_MAP: Record<string, string>","VertoByeCauseCodes: Record<VertoByeCause, string>","logger","storage: StorageManager","deviceController: DeviceController","reconnectCallsTimeout: number","attachKey: string","timeout","DEFAULT_ON_OFF: OnOffCapability","DEFAULT_MEMBER_CAPABILITIES: MemberCapabilities","DEFAULT_CALL_CAPABILITIES: CallCapabilitiesState","logger","initialState: Partial<ParticipantState>","executeMethod: ExecuteMethod","deviceController: DeviceController","map","distinctUntilChanged","target: MemberTarget","vertoManager: VertoManager","logger","initialSessionState: Partial<SessionState>","webRtcCallSession: CallManager","options: WebRTCCallEventManagerOptions","SDP_DIRECTIONS: ReadonlySet<string>","result: MediaDirections","currentMediaKind: 'audio' | 'video' | null","currentDirection: MediaDirection | null","mediaSectionsValidCandidates: number[]","logger","peerConnection: RTCPeerConnection","peerConnectionControllerNegotiating$: Observable<boolean>","logger","options: LocalStreamControllerOptions","stream: MediaStream","constraints: MediaStreamConstraints","logger","transceiverParams: RTCRtpTransceiverInit","constraints: MediaStreamConstraints","constraintsToApply: MediaTrackConstraints","logger","options: RTCPeerConnectionControllerOptionsPartial","track","uuid","options","options: RTCOfferOptions","sender","answer: RTCSessionDescriptionInit","logger","webRtcCallSession: WebRTCCall","attachManager: AttachManager","deviceController: DeviceController","webRTCApiProvider: WebRTCApiProvider","vertoMethod: VertoMethod","vertoByeOrAccepted: boolean | VertoByeParams | null","answerOptions: MediaOptions | undefined","vertoByeOrAccepted: boolean | VertoByeParams","deviceKind: 'audio' | 'video' | 'both' | undefined","rtcPeerConnController: RTCPeerConnectionController | null","removeTrack: 'audio' | 'video' | 'both' | undefined","executeMethod: ExecuteMethod","vertoManager: VertoManager","deviceController: DeviceController","logger","params: Record<string, unknown>","clientSession: ClientSession","options: CallOptions","address?: Address","uuid","response: T","self: MemberTarget","sessionManager: ClientSession","deviceController: DeviceController","attachManager: AttachManager","webRTCApiProvider: WebRTCApiProvider","callError: CallError","logger","endpoint: string","http: HTTPRequestController","fetchController: FetchController<T>","update$: Observable<Partial<T>>","onError?: (error: Error) => void","originalCollection: EntityCollection<O>","filter: (i: unknown) => i is O","mapper: (item: O) => T","addressId: string","conversationManager: ConversationsProvider","addressProvider: AddressProvider<Address>","logger","uuid","logger","from","credential: SDKCredential","transport: TransportManager","storage: StorageManager","authorizationStateKey: string","attachManager: AttachManager","error","params: RPCConnectParams","callSession: WebRTCCall | undefined","address: Address | undefined","clientSessionManager: ClientSessionManager","logger","groupId: string","clientSession: ClientSessionManager","http: HTTPRequestController","getSubscriberAddressId: () => string","onError?: (error: Error) => void","logger","http: HTTPRequestController","conversationManager: ConversationsProvider","onError?: (error: Error) => void","logger","WebSocketConstructor: WebSocketAdapter | NodeSocketAdapter","endpoint: string","outgoingMessages$: Observable<string | ArrayBuffer | Blob>","logger","storage: StorageManager","protocolKey: string","onError?: (error: Error) => void","logger","decodeHeader: JWTHeader","error: unknown","ready","host: string","embedToken: string","timeout","credentials: SDKCredential","version: string","ready: boolean"],"sources":["../src/behaviors/Destroyable.ts","../src/utils/asyncRetry.ts","../src/controllers/HTTPRequestController.ts","../src/core/constants.ts","../src/utils/time.ts","../src/containers/PreferencesContainer.ts","../src/controllers/NavigatorDeviceController.ts","../src/dependencies/DefaultLocalStorage.ts","../src/managers/StorageManager.ts","../src/containers/DependencyContainer.ts","../src/behaviors/Fetchable.ts","../src/core/entities/Subscriber.ts","../src/core/RPCMessages/helpers.ts","../src/core/RPCMessages/RPCConnect.ts","../src/core/RPCMessages/RPCReauthenticate.ts","../src/core/RPCMessages/RPCPing.ts","../src/core/RPCMessages/RPCExecute.ts","../src/core/RPCMessages/VertoMessages.ts","../src/core/RPCMessages/RPCEventAck.ts","../src/managers/AttachManager.ts","../src/core/capabilities/types.ts","../src/core/capabilities/computeCapabilities.ts","../src/core/capabilities/SelfCapabilities.ts","../src/core/RPCMessages/utils.ts","../src/core/entities/Participant.ts","../src/core/RPCMessages/guards/base.guards.ts","../src/core/RPCMessages/guards/events.guards.ts","../src/managers/CallEventsManager.ts","../src/helpers/SDPHelper.ts","../src/controllers/ICEGatheringController.ts","../src/controllers/LocalStreamController.ts","../src/controllers/TransceiverController.ts","../src/controllers/RTCPeerConnectionController.ts","../src/core/RPCMessages/guards/verto.guards.ts","../src/managers/VertoManager.ts","../src/managers/ParticipantFactory.ts","../src/core/entities/Call.ts","../src/managers/CallFactory.ts","../src/behaviors/Collection.ts","../src/core/entities/Address.ts","../src/core/utils.ts","../src/managers/ClientSessionManager.ts","../src/utils/isString.ts","../src/managers/ConversationsManager.ts","../src/utils/arrays.ts","../src/utils/warnup.ts","../src/managers/DirectoryManager.ts","../src/controllers/WebSocketController.ts","../src/core/RPCMessages/guards/methods.guards.ts","../src/managers/TransportManager.ts","../src/clients/SignalWire.ts","../src/dependencies/EmbedTokenCredentialProvider.ts","../src/utils/embeddableCall.ts","../src/dependencies/StaticCredentialProvider.ts","../src/index.ts"],"sourcesContent":["import {\n Subject,\n ReplaySubject,\n BehaviorSubject,\n merge,\n map,\n skip,\n observeOn,\n asapScheduler\n} from 'rxjs';\n\nimport type { Observable, Subscription, Observer } from 'rxjs';\n\nexport abstract class Destroyable {\n protected subscriptions: Subscription[] = [];\n protected subjects: Subject<unknown>[] = [];\n protected _destroyed$ = new Subject<void>();\n private _observableCache?: Map<string, Observable<unknown>>;\n\n public destroy(): void {\n this._observableCache?.clear();\n this.subscriptions.forEach((sub) => sub.unsubscribe());\n this.subjects.forEach((subject) => subject.complete());\n this._destroyed$.next();\n this._destroyed$.complete();\n }\n\n protected cachedObservable<T>(key: string, factory: () => Observable<T>): Observable<T> {\n this._observableCache ??= new Map();\n let cached = this._observableCache.get(key) as Observable<T> | undefined;\n if (!cached) {\n cached = factory();\n this._observableCache.set(key, cached as Observable<unknown>);\n }\n return cached;\n }\n\n /**\n * Like `cachedObservable`, but defers emissions to the microtask queue\n * via `observeOn(asapScheduler)`.\n *\n * Use ONLY for public-facing observable getters that external consumers\n * subscribe to. Prevents a class of bugs where `BehaviorSubject` or\n * `ReplaySubject` replays synchronously during `subscribe()`, before\n * the subscription variable is assigned in the caller's scope.\n *\n * Do NOT use for observables consumed internally by the SDK — internal\n * code using `subscribeTo()`, `firstValueFrom()`, or `withLatestFrom()`\n * depends on synchronous emission delivery.\n */\n protected publicCachedObservable<T>(key: string, factory: () => Observable<T>): Observable<T> {\n const publicKey = `public:${key}`;\n this._observableCache ??= new Map();\n let cached = this._observableCache.get(publicKey) as Observable<T> | undefined;\n if (!cached) {\n cached = factory().pipe(observeOn(asapScheduler));\n this._observableCache.set(publicKey, cached as Observable<unknown>);\n }\n return cached;\n }\n\n /**\n * Wraps an observable so emissions are deferred to the microtask queue.\n *\n * Use ONLY for public-facing getters that expose a subject via\n * `.asObservable()` without going through `cachedObservable`.\n *\n * Do NOT use for observables consumed internally by the SDK.\n */\n protected deferEmission<T>(observable: Observable<T>): Observable<T> {\n return observable.pipe(observeOn(asapScheduler));\n }\n\n protected subscribeTo<T>(\n observable: Observable<T>,\n observerOrNext: Partial<Observer<T>> | ((value: T) => void) | undefined\n ): void {\n const subscription = observable.subscribe(observerOrNext);\n this.subscriptions.push(subscription);\n }\n\n protected createSubject<T>(): Subject<T> {\n const subject = new Subject<T>();\n this.subjects.push(subject as Subject<unknown>);\n return subject;\n }\n\n protected createReplaySubject<T>(bufferSize?: number, windowTime?: number): ReplaySubject<T> {\n const subject = new ReplaySubject<T>(bufferSize, windowTime);\n this.subjects.push(subject as Subject<unknown>);\n return subject;\n }\n\n protected createBehaviorSubject<T>(initialValue: T): BehaviorSubject<T> {\n const subject = new BehaviorSubject<T>(initialValue);\n this.subjects.push(subject as Subject<unknown>);\n return subject;\n }\n\n public get $(): Observable<this> {\n return this.cachedObservable('$', () =>\n merge(\n // skip a burst of initial values from BehaviorSubjects\n ...this.subjects.map((s) => (s instanceof BehaviorSubject ? s.pipe(skip(1)) : s))\n ).pipe(map((_) => this))\n );\n }\n\n /**\n * Observable that emits when the instance is destroyed\n */\n public get destroyed$(): Observable<void> {\n return this._destroyed$.asObservable();\n }\n}\n","import { getLogger } from './logger';\nimport { ValidationError } from '../core/errors';\n\nconst DEFAULT_MAX_RETRIES = 10;\nconst DEFAULT_INITIAL_DELAY = 100;\nconst DEFAULT_DELAY_VARIATION = 1;\n\ninterface AsyncRetryOptions<T> {\n asyncCallable: () => Promise<T>;\n maxRetries?: number;\n delayFn?: () => number;\n validator?: (promiseResult: T) => void | never;\n expectedErrorHandler?: (error: unknown) => boolean;\n}\n\ninterface DelayOptions {\n initialDelay?: number;\n variation?: number;\n delayLimit?: number;\n}\n\nexport const increasingDelay = ({\n delayLimit: upperDelayLimit = Number.MAX_SAFE_INTEGER,\n initialDelay = DEFAULT_INITIAL_DELAY,\n variation = DEFAULT_DELAY_VARIATION\n}: DelayOptions): (() => number) => {\n if (initialDelay < 0) {\n throw new ValidationError('initialDelay must be gte 0');\n }\n if (upperDelayLimit < 0) {\n throw new ValidationError('upperDelayLimit must be gte 0');\n }\n if (variation < 0) {\n throw new ValidationError('variation must be gte 0');\n }\n if (initialDelay > upperDelayLimit) {\n throw new ValidationError('initialDelay must be lte delayLimit');\n }\n\n let delay = Math.min(initialDelay, upperDelayLimit);\n return () => {\n if (delay === upperDelayLimit) {\n // stop incrementing the delay and just return upperDelayLimit\n return upperDelayLimit;\n }\n const currentDelay = delay;\n delay = Math.min(delay + variation, upperDelayLimit);\n\n return currentDelay;\n };\n};\n\nexport const decreasingDelay = ({\n delayLimit: bottomDelayLimit = 0,\n initialDelay = DEFAULT_INITIAL_DELAY,\n variation = DEFAULT_DELAY_VARIATION\n}: DelayOptions): (() => number) => {\n if (initialDelay < 0) {\n throw new ValidationError('initialDelay must be gte 0');\n }\n if (bottomDelayLimit < 0) {\n throw new ValidationError('bottomDelayLimit must be gte 0');\n }\n if (variation < 0) {\n throw new ValidationError('variation must be gte 0');\n }\n if (initialDelay < bottomDelayLimit) {\n throw new ValidationError('initialDelay must be gte delayLimit');\n }\n\n let delay = Math.max(initialDelay, bottomDelayLimit);\n\n return () => {\n if (delay === bottomDelayLimit) {\n // stop incrementing the delay and just return upperDelayLimit\n return bottomDelayLimit;\n }\n const currentDelay = delay;\n delay = Math.max(delay - variation, bottomDelayLimit);\n\n return currentDelay;\n };\n};\n\nexport const constDelay = ({\n initialDelay = DEFAULT_INITIAL_DELAY\n}: Pick<DelayOptions, 'initialDelay'>): (() => number) => {\n if (initialDelay < 0) {\n throw new ValidationError('initialDelay must be gte 0');\n }\n return () => initialDelay;\n};\n\nexport const asyncRetry = async <T>({\n asyncCallable,\n maxRetries: retries = DEFAULT_MAX_RETRIES,\n delayFn,\n validator,\n expectedErrorHandler\n}: AsyncRetryOptions<T>): Promise<T> => {\n let remainingAttempts = retries - 1; // the 1st call counts as an attempt\n let wait = 0;\n\n const promiseAttempt = async (): Promise<T> => {\n try {\n let result: Awaited<T>;\n\n // Should not defer the call when: wait <= 0\n if (wait <= 0) {\n result = await asyncCallable();\n } else {\n result = await new Promise<T>((resolve, reject) =>\n setTimeout(() => {\n asyncCallable().then(resolve).catch(reject);\n }, wait)\n );\n }\n\n if (remainingAttempts) {\n // avoid messing with the normal returns in the last attempt\n validator?.(result);\n }\n\n return result;\n } catch (error) {\n if (remainingAttempts-- > 0 && !expectedErrorHandler?.(error)) {\n wait = delayFn?.() ?? 0;\n getLogger().debug(`Retrying request: ${retries - remainingAttempts} of ${retries}`);\n return promiseAttempt();\n } else {\n throw error;\n }\n }\n };\n\n return promiseAttempt();\n};\n","import { BehaviorSubject, Subject } from 'rxjs';\n\nimport { RequestTimeoutError, UnexpectedError } from '../core/errors';\nimport { asyncRetry, increasingDelay } from '../utils/asyncRetry';\nimport { getLogger } from '../utils/logger';\n\nimport type {\n HTTPRequest,\n HTTPResponse,\n HTTPHeaders,\n SDKCredential\n} from '../core/types/common.types';\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\nexport type HTTPRequestStatus = 'idle' | 'requesting' | 'success' | 'error';\n\nexport interface HTTPRequestControllerOptions {\n maxRetries?: number;\n retryDelayMin?: number;\n retryDelayMax?: number;\n requestTimeout?: number;\n}\n\nexport const GET_PARAMS = {\n method: 'GET',\n headers: {\n Accept: 'application/json'\n }\n} as const;\n\nexport const POST_PARAMS = {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n\n 'Content-Type': 'application/json'\n }\n} as const;\n\nexport class HTTPRequestController {\n // Default configuration values\n private static readonly defaultMaxRetries = 3;\n private static readonly defaultRetryDelayMinMs = 1000;\n private static readonly defaultRetryDelayMaxMs = 30000;\n private static readonly defaultRequestTimeoutMs = 30000;\n\n // Configuration\n private readonly maxRetries: number;\n private readonly retryDelayMin: number;\n private readonly retryDelayMax: number;\n private readonly requestTimeout: number;\n private _responses$ = new Subject<HTTPResponse>();\n private _errors$ = new Subject<Error>();\n\n // Observable streams\n private _status$ = new BehaviorSubject<HTTPRequestStatus>('idle');\n\n constructor(\n private baseURL: string,\n private credential: SDKCredential,\n options: HTTPRequestControllerOptions = {}\n ) {\n this.maxRetries = options.maxRetries ?? HTTPRequestController.defaultMaxRetries;\n this.retryDelayMin = options.retryDelayMin ?? HTTPRequestController.defaultRetryDelayMinMs;\n this.retryDelayMax = options.retryDelayMax ?? HTTPRequestController.defaultRetryDelayMaxMs;\n this.requestTimeout = options.requestTimeout ?? HTTPRequestController.defaultRequestTimeoutMs;\n }\n\n public get status$(): Observable<HTTPRequestStatus> {\n return this._status$.asObservable();\n }\n\n public get status(): HTTPRequestStatus {\n return this._status$.value;\n }\n\n public get responses$(): Observable<HTTPResponse> {\n return this._responses$.asObservable();\n }\n\n public get errors$(): Observable<Error> {\n return this._errors$.asObservable();\n }\n\n public async request(request: HTTPRequest): Promise<HTTPResponse> {\n this._status$.next('requesting');\n\n try {\n const response = await this.executeWithRetry(request);\n this._status$.next('success');\n this._responses$.next(response);\n return response;\n } catch (error) {\n logger.error('[HTTPRequestController] Request error:', error);\n this._status$.next('error');\n const err =\n error instanceof Error ? error : new Error('HTTP request failed', { cause: error });\n this._errors$.next(err);\n throw err;\n }\n }\n\n private async executeWithRetry(request: HTTPRequest): Promise<HTTPResponse> {\n // Calculate variation to spread delays evenly across retry attempts\n const variation = Math.ceil(\n (this.retryDelayMax - this.retryDelayMin) / Math.max(this.maxRetries - 1, 1)\n );\n\n const delayFn = increasingDelay({\n initialDelay: this.retryDelayMin,\n variation,\n delayLimit: this.retryDelayMax\n });\n\n return asyncRetry({\n asyncCallable: async () => this.executeRequest(request),\n maxRetries: this.maxRetries,\n delayFn,\n validator: (response) => {\n // Retry on 5xx server errors\n if (response.status >= 500 && response.status < 600) {\n throw new UnexpectedError(`Server error: ${response.status} ${response.statusText}`);\n }\n }\n });\n }\n\n private async executeRequest(request: HTTPRequest): Promise<HTTPResponse> {\n const url = this.buildURL(request.url);\n const headers = this.buildHeaders(request.headers);\n const timeout = request.timeout ?? this.requestTimeout;\n\n logger.debug('[HTTPRequestController] Executing request:', {\n method: request.method,\n url,\n headers: Object.keys(headers).reduce((acc, key) => {\n // Mask Authorization header for security\n // eslint-disable-next-line no-param-reassign\n acc[key] = key === 'Authorization' ? `${headers[key].substring(0, 20)}...` : headers[key];\n return acc;\n }, {} as HTTPHeaders),\n body: request.body\n });\n // {\"from_fabric_address_id\":\"03a98611-d38f-405a-af49-fcb51e7f22ad\",\"fabric_address_ids\":[\"31c0afc2-93f3-4530-9a7d-55613d40850d\",\"03a98611-d38f-405a-af49-fcb51e7f22ad\"]}\n // {\"from_fabric_address_id\":\"060b5d3e-5df0-45d9-911e-660779e593da\",\"fabric_address_ids\":[\"31c0afc2-93f3-4530-9a7d-55613d40850d\",\"060b5d3e-5df0-45d9-911e-660779e593da\"]}\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n method: request.method,\n headers,\n body: request.body,\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n const httpResponse = await this.convertResponse(response);\n\n logger.debug('[HTTPRequestController] Response received:', {\n status: response.status,\n statusText: response.statusText,\n headers: [...response.headers.entries()],\n body: httpResponse.body ? httpResponse.body.substring(0, 200) : '(empty)' // Show first 200 chars\n });\n\n return httpResponse;\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof Error && error.name === 'AbortError') {\n throw new RequestTimeoutError(`Request timeout after ${timeout}ms`, { cause: error });\n }\n\n logger.error('[HTTPRequestController] Request failed:', error);\n throw error;\n }\n }\n\n private buildURL(url: string | URL): string {\n const urlString = typeof url === 'string' ? url : url.toString();\n\n // If URL is absolute, return as-is\n if (urlString.startsWith('http://') || urlString.startsWith('https://')) {\n return urlString;\n }\n\n // Ensure base URL doesn't end with '/' and path starts with '/'\n const base = this.baseURL.endsWith('/') ? this.baseURL.slice(0, -1) : this.baseURL;\n const path = urlString.startsWith('/') ? urlString : `/${urlString}`;\n\n return `${base}${path}`;\n }\n\n private buildHeaders(requestHeaders?: HTTPHeaders): HTTPHeaders {\n const headers: HTTPHeaders = { ...(requestHeaders ?? {}) };\n\n // Add authentication header\n if (this.credential.token) {\n headers.Authorization = `Bearer ${this.credential.token}`;\n logger.debug(\n '[HTTPRequestController] Using Bearer token auth, token length:',\n this.credential.token.length\n );\n } else {\n logger.warn('[HTTPRequestController] No credentials available for authentication');\n }\n\n return headers;\n }\n\n private async convertResponse(response: Response): Promise<HTTPResponse> {\n // Convert Headers to plain object\n const headers: HTTPHeaders = {};\n response.headers.forEach((value, key) => {\n headers[key] = value;\n });\n\n // Read response body as text\n const bodyText = await response.text();\n\n return {\n status: response.status,\n statusText: response.statusText,\n headers,\n body: bodyText,\n ok: response.ok,\n url: response.url\n };\n }\n}\n","export const INVITE_VERSION = 1000;\nexport const DEFAULT_ICE_CANDIDATE_TIMEOUT_MS = 600;\nexport const DEFAULT_ICE_GATHERING_TIMEOUT_MS = 6_000;\nexport const DEFAULT_RECONNECT_CALLS_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\nexport const DEFAULT_CONNECTION_TIMEOUT_MS = 10_000;\nexport const DEFAULT_RECONNECT_DELAY_MIN_MS = 100;\nexport const DEFAULT_RECONNECT_DELAY_MAX_MS = 3000;\nexport const DEFAULT_DEVICE_DEBOUNCE_TIME_MS = 1500;\nexport const DEFAULT_DEVICE_POLLING_INTERVAL_MS = 0; // Disabled by default\nexport const PREFERENCES_STORAGE_KEY = 'sw:preferences';\n","export function fromSecToMs(seconds: number): number {\n return seconds * 1000;\n}\n\nexport function fromMsToSec(milliseconds: number): number {\n return Math.round(milliseconds / 100) / 10;\n}\n","import {\n DEFAULT_CONNECTION_TIMEOUT_MS,\n DEFAULT_DEVICE_DEBOUNCE_TIME_MS,\n DEFAULT_DEVICE_POLLING_INTERVAL_MS,\n DEFAULT_ICE_CANDIDATE_TIMEOUT_MS,\n DEFAULT_ICE_GATHERING_TIMEOUT_MS,\n DEFAULT_RECONNECT_CALLS_TIMEOUT_MS,\n DEFAULT_RECONNECT_DELAY_MAX_MS,\n DEFAULT_RECONNECT_DELAY_MIN_MS,\n PREFERENCES_STORAGE_KEY\n} from '../core/constants';\nimport { getLogger } from '../utils/logger';\nimport { fromMsToSec, fromSecToMs } from '../utils/time';\n\nimport type { MediaOptions } from '../core/types/media.types';\nimport type { StorageManager } from '../managers/StorageManager';\n\nconst logger = getLogger();\n\nexport interface Preferences {\n connectionTimeout: number;\n reconnectDelayMin: number;\n reconnectDelayMax: number;\n reconnectCallsTimeout: number;\n relayHost?: string;\n receiveVideo: boolean;\n receiveAudio: boolean;\n preferredAudioInput: MediaDeviceInfo | null;\n preferredAudioOutput: MediaDeviceInfo | null;\n preferredVideoInput: MediaDeviceInfo | null;\n inputAudioDeviceConstraints: MediaTrackConstraints | undefined;\n inputVideoDeviceConstraints: MediaTrackConstraints | undefined;\n disableUdpIceServers: boolean;\n relayOnly: boolean;\n iceCandidateTimeout: number;\n iceGatheringTimeout: number;\n deviceDebounceTime: number;\n devicePollingInterval: number;\n iceServers?: RTCIceServer[];\n defaultSignalWireOptions: {\n skipConnection: boolean;\n skipRegister: boolean;\n reconnectAttachedCalls: boolean;\n skipDeviceMonitoring: boolean;\n savePreferences: boolean;\n };\n inviteSubscribeScreenshare: string[];\n inviteSubscribeAdditionalDevice: string[];\n inviteSubscribeMainDevice: string[];\n userVariables: Record<string, unknown>;\n readonly preferredMediaOptions: MediaOptions;\n}\nexport class PreferencesContainer implements Preferences {\n static get instance(): Preferences {\n this._instance ??= new PreferencesContainer();\n return this._instance;\n }\n\n deviceDebounceTime = DEFAULT_DEVICE_DEBOUNCE_TIME_MS;\n devicePollingInterval = DEFAULT_DEVICE_POLLING_INTERVAL_MS;\n\n reconnectCallsTimeout = DEFAULT_RECONNECT_CALLS_TIMEOUT_MS;\n // 5 minutes\n iceServers?: RTCIceServer[];\n connectionTimeout = DEFAULT_CONNECTION_TIMEOUT_MS;\n reconnectDelayMin = DEFAULT_RECONNECT_DELAY_MIN_MS;\n reconnectDelayMax = DEFAULT_RECONNECT_DELAY_MAX_MS;\n disableUdpIceServers = false;\n relayOnly = false;\n iceCandidateTimeout = DEFAULT_ICE_CANDIDATE_TIMEOUT_MS;\n iceGatheringTimeout = DEFAULT_ICE_GATHERING_TIMEOUT_MS;\n defaultSignalWireOptions = {\n skipConnection: false,\n skipRegister: false,\n reconnectAttachedCalls: false,\n skipDeviceMonitoring: false,\n savePreferences: false\n };\n relayHost?: string;\n receiveVideo = false;\n receiveAudio = true;\n preferredAudioInput: MediaDeviceInfo | null = null;\n preferredAudioOutput: MediaDeviceInfo | null = null;\n preferredVideoInput: MediaDeviceInfo | null = null;\n inviteSubscribeScreenshare: string[] = ['video.room.screenshare'];\n inviteSubscribeAdditionalDevice: string[] = [\n // FIXME verify what to subscribe to for additional devices\n ];\n inviteSubscribeMainDevice: string[] = [\n 'track',\n 'destroy',\n 'member.updated.videoMuted',\n 'layout.changed',\n 'room.subscribed',\n 'member.updated.audioMuted',\n 'media.connected',\n 'room.updated',\n 'call.joined'\n ];\n userVariables = {};\n\n private _inputAudioDeviceConstraints?: MediaTrackConstraints;\n private _inputVideoDeviceConstraints?: MediaTrackConstraints;\n private static _instance?: PreferencesContainer;\n\n private constructor() {\n // Private constructor to enforce singleton pattern\n }\n\n public get preferredMediaOptions(): MediaOptions {\n return {\n receiveVideo: this.receiveVideo,\n receiveAudio: this.receiveAudio,\n inputAudioDeviceConstraints: this.inputAudioDeviceConstraints,\n inputVideoDeviceConstraints: this.inputVideoDeviceConstraints\n };\n }\n\n get inputAudioDeviceConstraints(): MediaTrackConstraints | undefined {\n return this._inputAudioDeviceConstraints;\n }\n\n set inputAudioDeviceConstraints(value: MediaTrackConstraints | undefined) {\n this._inputAudioDeviceConstraints = value;\n }\n\n get inputVideoDeviceConstraints(): MediaTrackConstraints | undefined {\n return this._inputVideoDeviceConstraints;\n }\n\n set inputVideoDeviceConstraints(value: MediaTrackConstraints | undefined) {\n this._inputVideoDeviceConstraints = value;\n }\n}\n\n/** Serializable subset of preferences that can be persisted to storage. */\ninterface StoredPreferences {\n connectionTimeout?: number;\n reconnectCallsTimeout?: number;\n reconnectDelayMin?: number;\n reconnectDelayMax?: number;\n relayHost?: string;\n receiveVideo?: boolean;\n receiveAudio?: boolean;\n disableUdpIceServers?: boolean;\n relayOnly?: boolean;\n iceCandidateTimeout?: number;\n iceGatheringTimeout?: number;\n deviceDebounceTime?: number;\n devicePollingInterval?: number;\n iceServers?: RTCIceServer[];\n userVariables?: Record<string, unknown>;\n}\n\n/** Keys of StoredPreferences that map to number fields on PreferencesContainer. */\nconst STORED_NUMBER_KEYS: (keyof StoredPreferences & keyof Preferences)[] = [\n 'connectionTimeout',\n 'reconnectCallsTimeout',\n 'reconnectDelayMin',\n 'reconnectDelayMax',\n 'iceCandidateTimeout',\n 'iceGatheringTimeout',\n 'deviceDebounceTime',\n 'devicePollingInterval'\n];\n\n/** Keys of StoredPreferences that map to boolean fields on PreferencesContainer. */\nconst STORED_BOOLEAN_KEYS: (keyof StoredPreferences & keyof Preferences)[] = [\n 'receiveVideo',\n 'receiveAudio',\n 'disableUdpIceServers',\n 'relayOnly'\n];\n\n/** Collects the serializable preferences from the container. */\nfunction collectStoredPreferences(): StoredPreferences {\n const container = PreferencesContainer.instance;\n return {\n connectionTimeout: container.connectionTimeout,\n reconnectCallsTimeout: container.reconnectCallsTimeout,\n reconnectDelayMin: container.reconnectDelayMin,\n reconnectDelayMax: container.reconnectDelayMax,\n relayHost: container.relayHost,\n receiveVideo: container.receiveVideo,\n receiveAudio: container.receiveAudio,\n disableUdpIceServers: container.disableUdpIceServers,\n relayOnly: container.relayOnly,\n iceCandidateTimeout: container.iceCandidateTimeout,\n iceGatheringTimeout: container.iceGatheringTimeout,\n deviceDebounceTime: container.deviceDebounceTime,\n devicePollingInterval: container.devicePollingInterval,\n iceServers: container.iceServers,\n userVariables: container.userVariables\n };\n}\n\n/** Applies stored preferences to the container. */\nfunction applyStoredPreferences(stored: StoredPreferences): void {\n const container = PreferencesContainer.instance as unknown as Record<string, unknown>;\n for (const key of STORED_NUMBER_KEYS) {\n if (stored[key] !== undefined) container[key] = stored[key];\n }\n for (const key of STORED_BOOLEAN_KEYS) {\n if (stored[key] !== undefined) container[key] = stored[key];\n }\n if (stored.relayHost !== undefined) container.relayHost = stored.relayHost;\n if (stored.iceServers !== undefined) container.iceServers = stored.iceServers;\n if (stored.userVariables !== undefined) container.userVariables = stored.userVariables;\n}\n\n/**\n * Public preferences API for configuring SDK behavior.\n *\n * Exposed as {@link SignalWire.preferences}. All timeout values\n * are in seconds when accessed through this class.\n *\n * When {@link enableSavePreferences} is called, preferences are\n * automatically loaded from storage and synced back on every change.\n */\nexport class ClientPreferences {\n private _storage: StorageManager | null = null;\n\n /**\n * Enables persistence of preferences to storage.\n * Loads any previously saved preferences and syncs future changes.\n */\n public enableSavePreferences(storage: StorageManager): void {\n this._storage = storage;\n this._loadFromStorage();\n }\n\n /** WebSocket connection timeout in seconds. */\n public get connectionTimeout(): number {\n return fromMsToSec(PreferencesContainer.instance.connectionTimeout);\n }\n public set connectionTimeout(seconds: number) {\n PreferencesContainer.instance.connectionTimeout = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Timeout for reconnecting to previously attached calls, in seconds. */\n public get reconnectCallsTimeout(): number {\n return fromMsToSec(PreferencesContainer.instance.reconnectCallsTimeout);\n }\n public set reconnectCallsTimeout(seconds: number) {\n PreferencesContainer.instance.reconnectCallsTimeout = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Minimum reconnection backoff delay in seconds. */\n public get reconnectDelayMin(): number {\n return fromMsToSec(PreferencesContainer.instance.reconnectDelayMin);\n }\n public set reconnectDelayMin(seconds: number) {\n PreferencesContainer.instance.reconnectDelayMin = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Maximum reconnection backoff delay in seconds. */\n public get reconnectDelayMax(): number {\n return fromMsToSec(PreferencesContainer.instance.reconnectDelayMax);\n }\n public set reconnectDelayMax(seconds: number) {\n PreferencesContainer.instance.reconnectDelayMax = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Custom relay host URL. Empty string uses the default. */\n public get relayHost(): string {\n return PreferencesContainer.instance.relayHost ?? '';\n }\n public set relayHost(value: string) {\n PreferencesContainer.instance.relayHost = value;\n this._saveToStorage();\n }\n\n /** Whether to receive remote video by default. */\n public get receiveVideo(): boolean {\n return PreferencesContainer.instance.receiveVideo;\n }\n public set receiveVideo(value: boolean) {\n PreferencesContainer.instance.receiveVideo = value;\n this._saveToStorage();\n }\n\n /** Whether to receive remote audio by default. */\n public get receiveAudio(): boolean {\n return PreferencesContainer.instance.receiveAudio;\n }\n public set receiveAudio(value: boolean) {\n PreferencesContainer.instance.receiveAudio = value;\n this._saveToStorage();\n }\n\n /** Preferred audio input device for new calls. */\n public get preferredAudioInput(): MediaDeviceInfo | null {\n return PreferencesContainer.instance.preferredAudioInput;\n }\n public set preferredAudioInput(value: MediaDeviceInfo | null) {\n PreferencesContainer.instance.preferredAudioInput = value;\n }\n\n /** Preferred audio output device for new calls. */\n public get preferredAudioOutput(): MediaDeviceInfo | null {\n return PreferencesContainer.instance.preferredAudioOutput;\n }\n public set preferredAudioOutput(value: MediaDeviceInfo | null) {\n PreferencesContainer.instance.preferredAudioOutput = value;\n }\n\n /** Preferred video input device for new calls. */\n public get preferredVideoInput(): MediaDeviceInfo | null {\n return PreferencesContainer.instance.preferredVideoInput;\n }\n public set preferredVideoInput(value: MediaDeviceInfo | null) {\n PreferencesContainer.instance.preferredVideoInput = value;\n }\n\n /** Default audio input track constraints. */\n public get inputAudioConstraints(): MediaTrackConstraints | undefined {\n return PreferencesContainer.instance.inputAudioDeviceConstraints;\n }\n public set inputAudioConstraints(value: MediaTrackConstraints | undefined) {\n PreferencesContainer.instance.inputAudioDeviceConstraints = value;\n }\n\n /** Default video input track constraints. */\n public get inputVideoConstraints(): MediaTrackConstraints | undefined {\n return PreferencesContainer.instance.inputVideoDeviceConstraints;\n }\n public set inputVideoConstraints(value: MediaTrackConstraints | undefined) {\n PreferencesContainer.instance.inputVideoDeviceConstraints = value;\n }\n\n /** Debounce time for device change events, in seconds. */\n public get deviceDebounceTime(): number {\n return fromMsToSec(PreferencesContainer.instance.deviceDebounceTime);\n }\n public set deviceDebounceTime(seconds: number) {\n PreferencesContainer.instance.deviceDebounceTime = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Polling interval for device enumeration, in seconds. */\n public get devicePollingInterval(): number {\n return fromMsToSec(PreferencesContainer.instance.devicePollingInterval);\n }\n public set devicePollingInterval(seconds: number) {\n PreferencesContainer.instance.devicePollingInterval = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Whether to filter out UDP-based ICE servers. */\n public get disableUdpIceServers(): boolean {\n return PreferencesContainer.instance.disableUdpIceServers;\n }\n public set disableUdpIceServers(value: boolean) {\n PreferencesContainer.instance.disableUdpIceServers = value;\n this._saveToStorage();\n }\n\n /** Whether to force TURN relay-only ICE candidates. */\n public get relayOnly(): boolean {\n return PreferencesContainer.instance.relayOnly;\n }\n public set relayOnly(value: boolean) {\n PreferencesContainer.instance.relayOnly = value;\n this._saveToStorage();\n }\n\n /** Timeout for individual ICE candidate gathering, in seconds. */\n public get iceCandidateTimeout(): number {\n return fromMsToSec(PreferencesContainer.instance.iceCandidateTimeout);\n }\n public set iceCandidateTimeout(seconds: number) {\n PreferencesContainer.instance.iceCandidateTimeout = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Timeout for the entire ICE gathering phase, in seconds. */\n public get iceGatheringTimeout(): number {\n return fromMsToSec(PreferencesContainer.instance.iceGatheringTimeout);\n }\n public set iceGatheringTimeout(seconds: number) {\n PreferencesContainer.instance.iceGatheringTimeout = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Custom ICE servers for TURN/STUN configuration. */\n public get iceServers(): RTCIceServer[] | undefined {\n return PreferencesContainer.instance.iceServers;\n }\n public set iceServers(value: RTCIceServer[] | undefined) {\n PreferencesContainer.instance.iceServers = value;\n this._saveToStorage();\n }\n\n /** Custom user variables attached to calls. */\n public get userVariables(): Record<string, unknown> {\n return PreferencesContainer.instance.userVariables;\n }\n public set userVariables(value: Record<string, unknown>) {\n PreferencesContainer.instance.userVariables = value;\n this._saveToStorage();\n }\n\n /** Saves current preferences to storage (fire-and-forget). */\n private _saveToStorage(): void {\n if (!this._storage) return;\n const data = collectStoredPreferences();\n this._storage.setItem(PREFERENCES_STORAGE_KEY, data, 'local').catch((error: unknown) => {\n logger.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);\n });\n }\n\n /** Loads preferences from storage and applies them to the container. */\n private _loadFromStorage(): void {\n if (!this._storage) return;\n this._storage\n .getItem<StoredPreferences>(PREFERENCES_STORAGE_KEY, 'local')\n .then((stored) => {\n if (stored) {\n applyStoredPreferences(stored);\n }\n })\n .catch((error: unknown) => {\n logger.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);\n });\n }\n}\n","import { debounceTime, distinctUntilChanged, interval, map, takeUntil, tap } from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { PreferencesContainer } from '../containers/PreferencesContainer';\nimport { getLogger } from '../utils/logger';\n\nimport type { WebRTCApiProvider } from '../dependencies/interfaces';\nimport type { DeviceController } from '../interfaces/DeviceController';\nimport type { Observable, Subscription } from 'rxjs';\n\nconst logger = getLogger();\n\ninterface DevicesState {\n audioinput: MediaDeviceInfo[];\n audiooutput: MediaDeviceInfo[];\n videoinput: MediaDeviceInfo[];\n}\n\ninterface SelectedDevicesState {\n audioinput: MediaDeviceInfo | null;\n audiooutput: MediaDeviceInfo | null;\n videoinput: MediaDeviceInfo | null;\n}\n\nconst initialDevicesState: DevicesState = {\n audioinput: [],\n audiooutput: [],\n videoinput: []\n};\n\nconst initialSelectedDevicesState: SelectedDevicesState = {\n audioinput: null,\n audiooutput: null,\n videoinput: null\n};\n\nconst selectDevice = (\n devices: MediaDeviceInfo[] = [],\n selected: MediaDeviceInfo | null,\n preferred: MediaDeviceInfo | null\n): MediaDeviceInfo | null => {\n const available = selected\n ? Boolean(\n devices.find(\n // if the selected device was a default device(changed), a new default device will be selected automatically\n (device) => device.deviceId === selected.deviceId || device.label === selected.label\n )\n )\n : true;\n\n if ((!selected || !available) && devices.length > 0) {\n const preferredDevice = preferred\n ? devices.find(\n (device) => device.deviceId === preferred.deviceId || device.label === preferred.label\n )\n : undefined;\n return preferredDevice ?? devices[0];\n }\n\n return selected;\n};\n\nexport class NavigatorDeviceController extends Destroyable implements DeviceController {\n private deviceChangeHandler = () => {\n logger.debug('[DeviceController] Device change detected');\n void this.enumerateDevices();\n };\n\n private _devicesPoolingSubscription?: Subscription;\n private _devicesState$ = this.createBehaviorSubject<DevicesState>(initialDevicesState);\n private _selectedDevicesState$ = this.createBehaviorSubject<SelectedDevicesState>(\n initialSelectedDevicesState\n );\n\n // Error stream\n private _errors$ = this.createReplaySubject<Error>(1);\n constructor(private readonly webRTCApiProvider: WebRTCApiProvider) {\n super();\n this.init();\n }\n public get selectedAudioInputDeviceConstraints(): MediaTrackConstraints {\n return this.deviceInfoToConstraints(this.selectedAudioInputDevice);\n }\n\n public get selectedVideoInputDeviceConstraints(): MediaTrackConstraints {\n return this.deviceInfoToConstraints(this.selectedVideoInputDevice);\n }\n\n public deviceInfoToConstraints(deviceInfo: MediaDeviceInfo | null): MediaTrackConstraints {\n if (!deviceInfo?.deviceId || deviceInfo.deviceId.trim() === '') {\n return {};\n }\n const devices =\n deviceInfo.kind === 'audioinput' ? this.audioInputDevices : this.videoInputDevices;\n const device =\n devices.find((device) => device.deviceId === deviceInfo.deviceId) ??\n devices.find((device) => device.label === deviceInfo.label);\n if (device) {\n return { deviceId: { exact: device.deviceId } };\n }\n return {};\n }\n\n public get errors$(): Observable<Error> {\n return this.cachedObservable('errors$', () =>\n this._errors$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n // Observable getters for device lists by kind\n public get audioInputDevices$(): Observable<MediaDeviceInfo[]> {\n return this.cachedObservable('audioInputDevices$', () =>\n this._devicesState$.pipe(\n map((state) => state.audioinput),\n distinctUntilChanged(),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n public get audioOutputDevices$(): Observable<MediaDeviceInfo[]> {\n return this.cachedObservable('audioOutputDevices$', () =>\n this._devicesState$.pipe(\n map((state) => state.audiooutput),\n distinctUntilChanged(),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n public get videoInputDevices$(): Observable<MediaDeviceInfo[]> {\n return this.cachedObservable('videoInputDevices$', () =>\n this._devicesState$.pipe(\n map((state) => state.videoinput),\n distinctUntilChanged(),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n public get selectedAudioInputDevice$(): Observable<MediaDeviceInfo | null> {\n return this.cachedObservable('selectedAudioInputDevice$', () =>\n this._selectedDevicesState$.asObservable().pipe(\n map((state) => state.audioinput),\n distinctUntilChanged(),\n takeUntil(this.destroyed$),\n tap((info) => logger.debug('[DeviceController] Selected audio input device changed:', info))\n )\n );\n }\n\n public get selectedAudioOutputDevice$(): Observable<MediaDeviceInfo | null> {\n return this.cachedObservable('selectedAudioOutputDevice$', () =>\n this._selectedDevicesState$.asObservable().pipe(\n map((state) => state.audiooutput),\n distinctUntilChanged(),\n takeUntil(this.destroyed$),\n tap((info) =>\n logger.debug('[DeviceController] Selected audio output device changed:', info)\n )\n )\n );\n }\n\n public get selectedVideoInputDevice$(): Observable<MediaDeviceInfo | null> {\n return this.cachedObservable('selectedVideoInputDevice$', () =>\n this._selectedDevicesState$.asObservable().pipe(\n map((state) => state.videoinput),\n distinctUntilChanged(),\n takeUntil(this.destroyed$),\n tap((info) => logger.debug('[DeviceController] Selected video input device changed:', info))\n )\n );\n }\n\n // Current value getters for selected devices\n public get selectedAudioInputDevice(): MediaDeviceInfo | null {\n return this._selectedDevicesState$.value.audioinput;\n }\n\n public get selectedAudioOutputDevice(): MediaDeviceInfo | null {\n return this._selectedDevicesState$.value.audiooutput;\n }\n\n public get selectedVideoInputDevice(): MediaDeviceInfo | null {\n return this._selectedDevicesState$.value.videoinput;\n }\n\n public get audioInputDevices(): MediaDeviceInfo[] {\n return this._devicesState$.value.audioinput;\n }\n\n public get audioOutputDevices(): MediaDeviceInfo[] {\n return this._devicesState$.value.audiooutput;\n }\n\n public get videoInputDevices(): MediaDeviceInfo[] {\n return this._devicesState$.value.videoinput;\n }\n\n // Setters for selected devices\n public selectAudioInputDevice(device: MediaDeviceInfo | null): void {\n this._selectedDevicesState$.next({\n ...this._selectedDevicesState$.value,\n audioinput: device\n });\n }\n\n public selectVideoInputDevice(device: MediaDeviceInfo | null): void {\n logger.debug('[DeviceController] Setting selected video input device:', device);\n this._selectedDevicesState$.next({\n ...this._selectedDevicesState$.value,\n videoinput: device\n });\n }\n\n public selectAudioOutputDevice(device: MediaDeviceInfo | null): void {\n this._selectedDevicesState$.next({\n ...this._selectedDevicesState$.value,\n audiooutput: device\n });\n }\n\n private init(): void {\n // Subscribe to device state changes and auto-select devices\n this.subscribeTo(\n this._devicesState$.pipe(debounceTime(PreferencesContainer.instance.deviceDebounceTime)),\n (devicesState) => {\n const currentSelected = this._selectedDevicesState$.value;\n\n const newAudioInput = selectDevice(\n devicesState.audioinput,\n currentSelected.audioinput,\n PreferencesContainer.instance.preferredAudioInput\n );\n\n const newAudioOutput = selectDevice(\n devicesState.audiooutput,\n currentSelected.audiooutput,\n PreferencesContainer.instance.preferredAudioOutput\n );\n\n const newVideoInput = selectDevice(\n devicesState.videoinput,\n currentSelected.videoinput,\n PreferencesContainer.instance.preferredVideoInput\n );\n\n // Only update if something changed\n if (\n newAudioInput !== currentSelected.audioinput ||\n newAudioOutput !== currentSelected.audiooutput ||\n newVideoInput !== currentSelected.videoinput\n ) {\n this._selectedDevicesState$.next({\n audioinput: newAudioInput,\n audiooutput: newAudioOutput,\n videoinput: newVideoInput\n });\n }\n }\n );\n\n void this.enumerateDevices();\n }\n\n public enableDeviceMonitoring(): void {\n this.disableDeviceMonitoring();\n this.webRTCApiProvider.mediaDevices.addEventListener('devicechange', this.deviceChangeHandler);\n\n if (PreferencesContainer.instance.devicePollingInterval > 0) {\n this._devicesPoolingSubscription = interval(\n PreferencesContainer.instance.devicePollingInterval\n ).subscribe(() => {\n logger.debug('[DeviceController] Polling devices due to interval');\n void this.enumerateDevices();\n });\n }\n\n void this.enumerateDevices();\n }\n\n public disableDeviceMonitoring(): void {\n this.webRTCApiProvider.mediaDevices.removeEventListener(\n 'devicechange',\n this.deviceChangeHandler\n );\n if (this._devicesPoolingSubscription) {\n this._devicesPoolingSubscription.unsubscribe();\n this._devicesPoolingSubscription = undefined;\n }\n }\n\n private async enumerateDevices(): Promise<void> {\n try {\n const devices = await this.webRTCApiProvider.mediaDevices.enumerateDevices();\n\n const devicesByKind: DevicesState = devices.reduce(\n (acc, device) => {\n const kind = device.kind as keyof DevicesState;\n acc[kind].push(device);\n return acc;\n },\n {\n audioinput: [],\n audiooutput: [],\n videoinput: []\n } as DevicesState\n );\n\n // Update state in a single emission\n this._devicesState$.next(devicesByKind);\n\n logger.debug('[DeviceController] Devices enumerated:', {\n audioInputs: devicesByKind.audioinput.length,\n audioOutputs: devicesByKind.audiooutput.length,\n videoInputs: devicesByKind.videoinput.length\n });\n } catch (error) {\n logger.error('[DeviceController] Failed to enumerate devices:', error);\n this._errors$.next(error as Error);\n }\n }\n\n public async getDeviceCapabilities(\n deviceInfo: MediaDeviceInfo\n ): Promise<MediaTrackCapabilities | null> {\n if (deviceInfo.kind === 'audiooutput') {\n return null;\n }\n\n try {\n const constraints = this.deviceInfoToConstraints(deviceInfo);\n const stream = await this.webRTCApiProvider.mediaDevices.getUserMedia({\n audio: deviceInfo.kind === 'audioinput' ? constraints : false,\n video: deviceInfo.kind === 'videoinput' ? constraints : false\n });\n\n const track =\n deviceInfo.kind === 'audioinput' ? stream.getAudioTracks()[0] : stream.getVideoTracks()[0];\n\n const capabilities = track.getCapabilities();\n\n // Stop all tracks to release devices\n stream.getTracks().forEach((t) => t.stop());\n\n return capabilities;\n } catch (error) {\n logger.error('[DeviceController] Failed to get device capabilities:', error);\n this._errors$.next(error as Error);\n throw error;\n }\n }\n\n public async isValidDevice(deviceInfo: MediaDeviceInfo | null): Promise<boolean> {\n if (!deviceInfo || deviceInfo.kind === 'audiooutput') {\n return false;\n }\n try {\n const capabilities = await this.getDeviceCapabilities(deviceInfo);\n return capabilities !== null;\n } catch {\n return false;\n }\n }\n\n public destroy(): void {\n this.disableDeviceMonitoring();\n super.destroy();\n }\n}\n","import { StorageNotAvailableError } from '../core/errors';\nimport { getLogger } from '../utils/logger';\n\nimport type { Storage, StorageScope } from './interfaces';\n\nexport class DefaultLocalStorage implements Storage {\n constructor() {\n // Check if localStorage is available\n if (typeof localStorage === 'undefined') {\n throw new StorageNotAvailableError('localStorage');\n }\n if (typeof sessionStorage === 'undefined') {\n throw new StorageNotAvailableError('sessionStorage');\n }\n\n // Test if localStorage is actually accessible (some browsers block it)\n try {\n const testKey = '__storage_test__';\n localStorage.setItem(testKey, 'test');\n localStorage.removeItem(testKey);\n } catch (error) {\n getLogger().error('LocalStorage is not accessible:', error);\n throw new StorageNotAvailableError('localStorage');\n }\n }\n\n private storage(scope: StorageScope) {\n return scope === 'local' ? localStorage : sessionStorage;\n }\n\n async setItem(key: string, value: string, scope: StorageScope = 'session'): Promise<void> {\n this.storage(scope).setItem(key, value);\n return Promise.resolve();\n }\n\n async getItem(key: string, scope: StorageScope = 'session'): Promise<string | null> {\n return Promise.resolve(this.storage(scope).getItem(key));\n }\n\n async removeItem(key: string, scope: StorageScope = 'session'): Promise<void> {\n this.storage(scope).removeItem(key);\n return Promise.resolve();\n }\n}\n","import {\n SerializationError,\n StorageWriteError,\n DeserializationError,\n StorageReadError\n} from '../core/errors';\nimport { DefaultLocalStorage } from '../dependencies/DefaultLocalStorage';\n\nimport type { Storage, StorageScope } from '../dependencies/interfaces';\n\nexport class StorageManager {\n constructor(private storageImpl: Storage = new DefaultLocalStorage()) {}\n\n /**\n * Validates that a value can be safely serialized to JSON\n * @throws SerializationError if value contains non-serializable types\n */\n private serialize(value: unknown, key?: string): string | null {\n if (value === undefined || value === null) {\n // undefined is acceptable, will be stored as null\n return null;\n }\n\n try {\n return JSON.stringify(value);\n } catch (e) {\n throw new SerializationError(key ?? 'unknown', e as Error);\n }\n }\n\n /**\n * Stores a value in storage\n * @throws InvalidStorageValueError if value contains non-serializable types\n * @throws SerializationError if JSON serialization fails\n * @throws StorageWriteError if writing to storage fails\n */\n public async setItem(\n key: string,\n value: unknown,\n scope: StorageScope = 'session'\n ): Promise<void> {\n const serialized = this.serialize(value, key);\n\n try {\n await this.storageImpl.setItem(key, serialized, scope);\n } catch (error) {\n throw new StorageWriteError(key, error as Error);\n }\n }\n\n /**\n * Retrieves a value from storage\n *\n * This method distinguishes between:\n * - Storage errors (network, permission, etc.) - these are thrown\n * - JSON parse errors - these trigger onParseError and return raw string\n * - Missing keys - returns null\n *\n * @returns The parsed value, raw string (on parse error), or null\n * @throws StorageReadError\n */\n public async getItem<T = unknown>(\n key: string,\n scope: StorageScope = 'session'\n ): Promise<T | null> {\n let item: string | null;\n\n try {\n item = await this.storageImpl.getItem(key, scope);\n } catch (error) {\n throw new StorageReadError(key, error as Error);\n }\n\n if (!item) {\n return null;\n }\n\n try {\n return JSON.parse(item) as T;\n } catch (error) {\n throw new DeserializationError(key, error as Error);\n }\n }\n\n /**\n * Removes a value from storage\n * @throws Error from underlying storage implementation\n */\n public async removeItem(key: string, scope: StorageScope = 'session'): Promise<void> {\n try {\n await this.storageImpl.removeItem(key, scope);\n } catch (error) {\n throw new StorageWriteError(key, error as Error);\n }\n }\n}\n","import { HTTPRequestController } from '../controllers/HTTPRequestController';\nimport { NavigatorDeviceController } from '../controllers/NavigatorDeviceController';\nimport { DependencyError } from '../core/errors';\nimport { DefaultLocalStorage } from '../dependencies/DefaultLocalStorage';\nimport { StorageManager } from '../managers/StorageManager';\n\nimport type { Subscriber } from '../core/entities/Subscriber';\nimport type {\n NodeSocketAdapter,\n SDKCredential,\n WebSocketAdapter\n} from '../core/types/common.types';\nimport type { Storage, WebRTCApiProvider } from '../dependencies/interfaces';\nimport type { ConversationsProvider } from '../interfaces/Conversations';\nimport type { Dependency } from '../interfaces/Dependency';\nimport type { DeviceController } from '../interfaces/DeviceController';\n\nexport class DependencyContainer implements Dependency {\n private _conversationManager?: ConversationsProvider;\n\n private _subscriber?: Subscriber;\n\n private _host?: string;\n\n private _domain?: string;\n\n private _storageManager?: StorageManager;\n private _storageImpl?: Storage;\n private _webSocketConstructor?: WebSocketAdapter | NodeSocketAdapter =\n typeof WebSocket !== 'undefined' ? WebSocket : undefined;\n private _baseURL: string = this.apiHost;\n private _credential: SDKCredential = {};\n private _httpRequestController?: HTTPRequestController;\n private _deviceController?: NavigatorDeviceController;\n private _webRTCApiProvider?: WebRTCApiProvider;\n public get subscriberId(): string {\n return this.subscriber.id;\n }\n\n public get subscriber(): Subscriber {\n if (!this._subscriber) {\n throw new DependencyError('Subscriber');\n }\n return this._subscriber;\n }\n public set subscriber(subscriber: Subscriber) {\n this._subscriber = subscriber;\n }\n\n public get storage(): StorageManager {\n if (!this._storageManager) {\n // Lazily initialize storage implementation if not already set\n this._storageImpl ??= new DefaultLocalStorage();\n this._storageManager = new StorageManager(this._storageImpl);\n }\n return this._storageManager;\n }\n\n public get http(): HTTPRequestController {\n this._httpRequestController ??= new HTTPRequestController(this._baseURL, this._credential);\n return this._httpRequestController;\n }\n\n public get conversationManager(): ConversationsProvider {\n if (!this._conversationManager) {\n throw new DependencyError('ConversationsManager');\n }\n return this._conversationManager;\n }\n\n public set conversationManager(conversationManager: ConversationsProvider) {\n this._conversationManager = conversationManager;\n }\n\n public get WebSocket(): WebSocketAdapter | NodeSocketAdapter {\n if (!this._webSocketConstructor) {\n throw new DependencyError('WebSocket constructor');\n }\n return this._webSocketConstructor;\n }\n\n public set WebSocket(WebSocketConstructor: WebSocketAdapter | NodeSocketAdapter) {\n this._webSocketConstructor = WebSocketConstructor;\n }\n\n public get deviceController(): DeviceController {\n this._deviceController ??= new NavigatorDeviceController(this.webRTCApiProvider);\n return this._deviceController;\n }\n\n public get webRTCApiProvider(): WebRTCApiProvider {\n if (!this._webRTCApiProvider) {\n if (typeof RTCPeerConnection === 'undefined' || typeof navigator === 'undefined') {\n throw new DependencyError(\n 'WebRTCApiProvider: RTCPeerConnection or navigator.mediaDevices is not available. ' +\n 'Please provide a custom webRTCApiProvider in SignalWireOptions.'\n );\n }\n this._webRTCApiProvider = {\n RTCPeerConnection,\n mediaDevices: navigator.mediaDevices\n };\n }\n return this._webRTCApiProvider;\n }\n\n public set webRTCApiProvider(webRTCApiProvider: WebRTCApiProvider) {\n this._webRTCApiProvider = webRTCApiProvider;\n // Reset device controller so it picks up the new provider\n this._deviceController = undefined;\n }\n\n public get authorizationStateKey(): string {\n return `sw:${this.subscriberId}:as`;\n }\n\n public get protocolKey(): string {\n return `sw:${this.subscriberId}:pt`;\n }\n\n public get attachedCallsKey(): string {\n return `sw:${this.subscriberId}:att`;\n }\n\n public getSubscriberFromAddressId(): string {\n return this.subscriber.addresses[0]?.id ?? '';\n }\n\n public set baseURL(baseURL: string) {\n this._baseURL = baseURL;\n this._httpRequestController = undefined;\n }\n\n public get credential(): SDKCredential {\n return this._credential;\n }\n\n public set credential(credential: SDKCredential) {\n this._credential = credential;\n this._httpRequestController = undefined;\n }\n\n public set storageImpl(storageImpl: Storage) {\n this._storageImpl = storageImpl;\n this._storageManager = undefined;\n }\n\n public set ch(ch: string | undefined) {\n if (!ch) {\n return;\n }\n\n const firstDot = ch.indexOf('.');\n if (firstDot !== -1) {\n this._host = ch.substring(0, firstDot);\n this._domain = ch.substring(firstDot + 1);\n }\n }\n\n public get relayHost(): string {\n return `wss://${this._host ?? 'puc'}.${this._domain ?? 'signalwire.com'}`;\n }\n\n public get apiHost(): string {\n return `https://${this._host ?? 'fabric'}.${this._domain ?? 'signalwire.com'}`;\n }\n}\n","import { defer, from, shareReplay, takeUntil } from 'rxjs';\n\nimport { Destroyable } from './Destroyable';\n\nimport type { HTTPRequestController } from '../controllers/HTTPRequestController';\nimport type { Observable } from 'rxjs';\n\nexport abstract class Fetchable<T = unknown> extends Destroyable {\n public fetched$: Observable<boolean>;\n\n constructor(\n public fromPath: string,\n protected http: HTTPRequestController\n ) {\n super();\n this.fetched$ = defer(() => from(this.fetch())).pipe(\n shareReplay(1),\n takeUntil(this.destroyed$)\n );\n }\n\n protected abstract populateInstance(data: T): void;\n\n private async fetch(): Promise<boolean> {\n const response = await this.http.request({\n url: this.fromPath,\n method: 'GET',\n headers: {\n Accept: 'application/json'\n }\n });\n if (response.ok && response.body) {\n const data = JSON.parse(response.body) as T;\n this.populateInstance(data);\n return true;\n }\n return false;\n }\n}\n","import { Fetchable } from '../../behaviors/Fetchable';\n\nimport type { HTTPRequestController } from '../../controllers/HTTPRequestController';\nimport type { GetAddressResponse } from '../types/address.types';\nimport type { GetSubscriberInfoResponse } from '../types/subscriber.types';\n\n/** Subscriber online presence state. */\nexport type SubscriberPresence = 'online' | 'offline' | 'busy';\n\n/**\n * Authenticated subscriber profile.\n *\n * Fetched automatically when a {@link SignalWire} connects.\n * Contains identity, contact, and organization details.\n */\nexport class Subscriber extends Fetchable<GetSubscriberInfoResponse> {\n /** Unique subscriber identifier. */\n public id!: string;\n /** Subscriber email address. */\n public email!: string;\n /** First name. */\n public firstName?: string;\n /** Last name. */\n public lastName?: string;\n /** Display name shown to other participants. */\n public displayName?: string;\n /** Job title. */\n public jobTitle?: string;\n /** Time zone offset. */\n public timeZone?: number;\n /** Country code. */\n public country?: string;\n /** Region/state. */\n public region?: string;\n /** Company name. */\n public companyName?: string;\n /** Push notification key for mobile/web push. */\n public pushNotificationKey!: string;\n /** Application-level settings (display name, permission scopes). */\n public appSettings?: {\n displayName: string;\n scopes: string[];\n };\n /** Fabric addresses associated with this subscriber. */\n public addresses!: GetAddressResponse[];\n\n constructor(http: HTTPRequestController) {\n super('/api/fabric/subscriber/info', http);\n }\n\n protected populateInstance(data: GetSubscriberInfoResponse): void {\n this.id = data.id;\n this.email = data.email;\n this.firstName = data.first_name;\n this.lastName = data.last_name;\n this.displayName = data.display_name;\n this.jobTitle = data.job_title;\n this.timeZone = data.time_zone;\n this.country = data.country;\n this.region = data.region;\n this.companyName = data.company_name;\n this.pushNotificationKey = data.push_notification_key;\n this.appSettings = data.app_settings\n ? {\n displayName: data.app_settings.display_name,\n scopes: data.app_settings.scopes\n }\n : undefined;\n this.addresses = data.fabric_addresses;\n }\n}\n","import { v4 as uuid } from 'uuid';\n\nimport type { JSONRPCRequest, JSONRPCSuccessResponse } from './types/base';\n\ninterface MakeRPCRequestParams<\n T extends string = 'execute',\n P extends object = Record<string, unknown>\n> {\n id?: string;\n method: T;\n params: P;\n}\nexport const buildRPCRequest = <\n T extends string = 'execute',\n P extends object = Record<string, unknown>\n>(\n params: MakeRPCRequestParams<T, P>\n): JSONRPCRequest<P> & { method: T; params: P } => {\n return {\n jsonrpc: '2.0' as const,\n id: params.id ?? uuid(),\n ...params\n };\n};\n\ninterface MakeRPCResponseParams<TResult = unknown> {\n id: string;\n result: TResult;\n}\nexport const makeRPCResponse = <TResult = unknown>(\n params: MakeRPCResponseParams<TResult>\n): JSONRPCSuccessResponse<TResult> => {\n return {\n jsonrpc: '2.0' as const,\n ...params\n };\n};\n","import { buildRPCRequest } from './helpers';\n\nimport type { JSONRPCRequest } from './types/base';\n\ninterface WithToken {\n token: string;\n\n jwt_token?: never;\n}\ninterface WithJWT {\n token?: never;\n\n jwt_token: string;\n}\nexport type RPCConnectAuthentication = { project?: string } & (WithToken | WithJWT);\nexport interface RPCConnectParams {\n authentication: RPCConnectAuthentication;\n version?: typeof DEFAULT_CONNECT_VERSION;\n agent?: string;\n protocol?: string;\n\n authorization_state?: string;\n contexts?: string[];\n topics?: string[];\n eventing?: string[];\n\n event_acks?: boolean;\n}\n\nexport interface Authorization {\n jti: string;\n\n project_id: string;\n\n fabric_subscriber: {\n version: number;\n\n expires_at: number;\n\n subscriber_id: string;\n\n application_id: string;\n\n project_id: string;\n\n space_id: string;\n };\n}\n\nexport interface RPCConnectResult {\n identity: string;\n authorization: Authorization;\n protocol: string;\n\n ice_servers?: RTCIceServer[];\n}\n\nexport const DEFAULT_CONNECT_VERSION = {\n major: 4,\n minor: 0,\n revision: 0\n};\n\nexport const RPCConnect = (params: RPCConnectParams): JSONRPCRequest => {\n return buildRPCRequest({\n method: 'signalwire.connect',\n params: {\n version: DEFAULT_CONNECT_VERSION,\n\n event_acks: true,\n ...params\n }\n });\n};\n","import { buildRPCRequest } from './helpers';\n\nimport type { JSONRPCRequest } from './types/base';\n\nexport interface RPCReauthenticateParams {\n project: string;\n\n jwt_token: string;\n}\n\nexport const RPCReauthenticate = (authentication: RPCReauthenticateParams): JSONRPCRequest => {\n return buildRPCRequest({\n method: 'signalwire.reauthenticate',\n params: {\n authentication\n }\n });\n};\n","import { buildRPCRequest, makeRPCResponse } from './helpers';\n\nimport type { JSONRPCSuccessResponse } from './types/base';\nimport type { SignalwirePingRequest } from './types/events';\nimport type { SignalwirePingResult } from './types/methods';\n\nexport const RPCPing = (): SignalwirePingRequest => {\n return buildRPCRequest({\n method: 'signalwire.ping',\n params: {\n timestamp: Date.now() / 1000\n }\n });\n};\n\nexport const RPCPingResponse = (\n id: string,\n timestamp?: number\n): JSONRPCSuccessResponse<SignalwirePingResult> => {\n return makeRPCResponse<SignalwirePingResult>({\n id,\n result: {\n timestamp: timestamp ?? Date.now() / 1000\n }\n });\n};\n","import { buildRPCRequest } from './helpers';\n\nimport type { JSONRPCRequest, JSONRPCMethod } from './types/base';\n\ninterface RPCExecuteParams {\n id?: string;\n method: JSONRPCMethod;\n params: Record<string, unknown>;\n}\n\nexport const RPCExecute = ({ method, params }: RPCExecuteParams): JSONRPCRequest => {\n return buildRPCRequest({\n method,\n params\n });\n};\n","import { buildRPCRequest, makeRPCResponse } from './helpers';\n\nimport type { VertoMethod } from '../types/rpc.types';\nimport type { JSONRPCSuccessResponse } from './types/base';\nimport type { WebrtcVertoParams, WebrtcVertoRequest } from './types/methods';\nimport type { VertoByeCause } from './types/verto';\n\ntype VertoParams = Record<string, unknown>;\n\nconst SDK_TO_VERTO_FIELD_MAP: Record<string, string> = {\n id: 'callID',\n destinationNumber: 'destination_number',\n remoteCallerName: 'remote_caller_id_name',\n remoteCallerNumber: 'remote_caller_id_number',\n callerName: 'caller_id_name',\n callerNumber: 'caller_id_number',\n fromCallAddressId: 'from_fabric_address_id'\n};\n\nconst EXCLUDED_DIALOG_PARAMS = new Set(['remoteSdp', 'localStream', 'remoteStream']);\n\n/**\n * Translate SDK fields into verto variables.\n * Returns a new object — the input is never mutated.\n */\n/** @internal Exported for testing only. */\nexport const filterVertoParams = (params: VertoParams): VertoParams => {\n if (!Object.prototype.hasOwnProperty.call(params, 'dialogParams')) {\n return params;\n }\n\n const sourceDialogParams = params.dialogParams as Record<string, unknown>;\n\n const filteredDialogParams = Object.entries(sourceDialogParams).reduce<Record<string, unknown>>(\n (acc, [key, value]) => {\n if (EXCLUDED_DIALOG_PARAMS.has(key)) {\n return acc;\n }\n const mappedKey = SDK_TO_VERTO_FIELD_MAP[key] ?? key;\n return { ...acc, [mappedKey]: value };\n },\n {}\n );\n\n return {\n ...params,\n dialogParams: filteredDialogParams\n };\n};\n\nconst buildVertoRPCMessage = (method: VertoMethod) => {\n return (params: VertoParams = {}) => {\n return buildRPCRequest({\n method,\n params: filterVertoParams(params)\n });\n };\n};\n\nexport type VertoRPCMessage = ReturnType<ReturnType<typeof buildVertoRPCMessage>>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type JSONRPCParams = Record<string, any>;\n\nexport const WebrtcVerto = (params: WebrtcVertoParams): WebrtcVertoRequest => {\n return buildRPCRequest({\n method: 'webrtc.verto',\n params\n });\n};\n\nexport type WebrtcVertoRPCMessage = ReturnType<ReturnType<typeof buildVertoRPCMessage>>;\n\nexport const VertoInvite = buildVertoRPCMessage('verto.invite');\nexport const VertoBye = buildVertoRPCMessage('verto.bye');\nexport const VertoAttach = buildVertoRPCMessage('verto.attach');\nexport const VertoModify = buildVertoRPCMessage('verto.modify');\nexport const VertoInfo = buildVertoRPCMessage('verto.info');\nexport const VertoAnswer = buildVertoRPCMessage('verto.answer');\nexport const VertoSubscribe = buildVertoRPCMessage('verto.subscribe');\nexport const VertoPong = buildVertoRPCMessage('verto.pong');\nexport const VertoResult = (id: string, method: VertoMethod): JSONRPCSuccessResponse => {\n return makeRPCResponse({\n id,\n result: {\n method\n }\n });\n};\n\nexport interface VertoModifyResponse {\n action: string;\n callID: string;\n holdState: 'held' | 'active';\n\n node_id?: string;\n sdp?: string;\n}\n\nexport const VertoByeCauseCodes: Record<VertoByeCause, string> = {\n NORMAL_CLEARING: '16',\n\n USER_BUSY: '17',\n\n MEDIA_TIMEOUT: '804'\n} as const;\n","import { makeRPCResponse } from './helpers';\n\nimport type { JSONRPCSuccessResponse } from './types/base';\n\nexport const RPCEventAckResponse = (id: string): JSONRPCSuccessResponse =>\n makeRPCResponse({ id, result: {} });\n","import { getLogger } from '../utils/logger';\n\nimport type { StorageManager } from './StorageManager';\nimport type { Address } from '../core/entities/Address';\nimport type { Call, CallOptions } from '../core/entities/types/call.types';\nimport type { MediaDirections } from '../core/types/media.types';\nimport type { DeviceController } from '../interfaces/DeviceController';\n\nconst logger = getLogger();\ninterface AttachableCall {\n id: string;\n to?: string;\n mediaDirections: MediaDirections;\n}\n\nexport interface OutboundCallProvider {\n createOutboundCall(destination: string | Address, options?: CallOptions): Promise<Call>;\n}\n\ninterface Attachment {\n destination: string;\n mediaDirections: MediaDirections;\n audioInputDevice: MediaDeviceInfo | null;\n videoInputDevice: MediaDeviceInfo | null;\n attachedAt: number;\n}\n\nexport class AttachManager {\n private session!: OutboundCallProvider;\n constructor(\n private readonly storage: StorageManager,\n\n private readonly deviceController: DeviceController,\n private readonly reconnectCallsTimeout: number,\n private attachKey: string\n ) {}\n\n async detachAll(): Promise<void> {\n const attached = await this.readAttached();\n for (const callId of Object.keys(attached)) {\n await this.detach({ id: callId, mediaDirections: attached[callId].mediaDirections });\n }\n }\n\n public setSession(session: OutboundCallProvider): void {\n this.session = session;\n }\n\n private async readAttached(): Promise<Record<string, Attachment>> {\n try {\n return (await this.storage.getItem(this.attachKey)) ?? {};\n } catch (error) {\n logger.warn('[AttachManager] Failed to retrieve attached calls from storage', error);\n return {};\n }\n }\n\n private async writeAttached(attached: Record<string, Attachment>): Promise<void> {\n try {\n await this.storage.setItem(this.attachKey, attached);\n } catch (error) {\n logger.warn('[AttachManager] Failed to write attached calls to storage', error);\n }\n }\n\n public async attach(call: AttachableCall): Promise<void> {\n if (!call.to) {\n logger.warn('[AttachManager] Skip attach for calls with no destination');\n return;\n }\n const attachment = {\n destination: call.to,\n mediaDirections: call.mediaDirections,\n audioInputDevice:\n call.mediaDirections.audio !== 'inactive'\n ? this.deviceController.selectedAudioInputDevice\n : null,\n videoInputDevice:\n call.mediaDirections.video !== 'inactive'\n ? this.deviceController.selectedVideoInputDevice\n : null,\n attachedAt: Date.now()\n };\n const attached = await this.readAttached();\n attached[call.id] = attachment;\n await this.writeAttached(attached);\n }\n\n public async detach(call: AttachableCall): Promise<void> {\n const attached = await this.readAttached();\n delete attached[call.id];\n await this.writeAttached(attached);\n }\n\n public async flush(): Promise<void> {\n await this.writeAttached({});\n }\n\n public async reattachCalls(): Promise<void> {\n const attached = await this.readAttached();\n\n await this.detachExpired();\n\n for (const [callId, attachment] of Object.entries(attached)) {\n const { destination } = attachment;\n\n const options = this.buildCallOptions(attachment);\n\n await this.session.createOutboundCall(destination, { callId, ...options });\n }\n }\n\n private buildCallOptions(attachment: Attachment): CallOptions {\n const { audio: audioDirection, video: videoDirection } = attachment.mediaDirections;\n const { audioInputDevice, videoInputDevice } = attachment;\n const receiveAudio = audioDirection.includes('recv');\n const receiveVideo = videoDirection.includes('recv');\n const sendAudio = audioDirection.includes('send');\n const sendVideo = videoDirection.includes('send');\n const inputAudioDeviceConstraints = sendAudio\n ? { audio: true, ...this.deviceController.deviceInfoToConstraints(audioInputDevice) }\n : undefined;\n const inputVideoDeviceConstraints = sendVideo\n ? { video: true, ...this.deviceController.deviceInfoToConstraints(videoInputDevice) }\n : undefined;\n return {\n receiveAudio,\n receiveVideo,\n inputAudioDeviceConstraints,\n inputVideoDeviceConstraints,\n reattach: true\n };\n }\n\n private async detachExpired() {\n const attached = await this.readAttached();\n\n const expired = Object.entries(attached).filter(([, attachment]) => {\n const now = Date.now();\n const timeout = this.reconnectCallsTimeout;\n return now - attachment.attachedAt > timeout;\n });\n expired.forEach(([callId]) => {\n delete attached[callId];\n });\n\n if (expired.length > 0) {\n await this.writeAttached(attached);\n }\n }\n}\n","/**\n * Represents an on/off capability state\n * Both `on` and `off` can be true if the parent permission grants both\n */\nexport interface OnOffCapability {\n readonly on: boolean;\n readonly off: boolean;\n}\n\n/**\n * Member-level capabilities for self or other members\n */\nexport interface MemberCapabilities {\n readonly muteAudio: OnOffCapability;\n readonly muteVideo: OnOffCapability;\n readonly deaf: OnOffCapability;\n readonly raisehand: OnOffCapability;\n readonly microphoneVolume: boolean;\n readonly microphoneSensitivity: boolean;\n readonly speakerVolume: boolean;\n readonly position: boolean;\n readonly meta: boolean;\n readonly remove: boolean;\n readonly audioFlags: boolean;\n}\n\n/**\n * Call-level capabilities state\n */\nexport interface CallCapabilitiesState {\n readonly self: MemberCapabilities;\n readonly member: MemberCapabilities;\n readonly end: boolean;\n readonly setLayout: boolean;\n readonly sendDigit: boolean;\n readonly vmutedHide: OnOffCapability;\n readonly lock: OnOffCapability;\n readonly device: boolean;\n readonly screenshare: boolean;\n}\n\n/**\n * Default on/off state with no permissions\n */\nexport const DEFAULT_ON_OFF: OnOffCapability = {\n on: false,\n off: false\n};\n\n/**\n * Default member capabilities with no permissions\n */\nexport const DEFAULT_MEMBER_CAPABILITIES: MemberCapabilities = {\n muteAudio: DEFAULT_ON_OFF,\n muteVideo: DEFAULT_ON_OFF,\n deaf: DEFAULT_ON_OFF,\n raisehand: DEFAULT_ON_OFF,\n microphoneVolume: false,\n microphoneSensitivity: false,\n speakerVolume: false,\n position: false,\n meta: false,\n remove: false,\n audioFlags: false\n};\n\n/**\n * Default call capabilities with no permissions\n */\nexport const DEFAULT_CALL_CAPABILITIES: CallCapabilitiesState = {\n self: DEFAULT_MEMBER_CAPABILITIES,\n member: DEFAULT_MEMBER_CAPABILITIES,\n end: false,\n setLayout: false,\n sendDigit: false,\n vmutedHide: DEFAULT_ON_OFF,\n lock: DEFAULT_ON_OFF,\n device: false,\n screenshare: false\n};\n","import { DEFAULT_CALL_CAPABILITIES, DEFAULT_MEMBER_CAPABILITIES, DEFAULT_ON_OFF } from './types';\n\nimport type { CallCapabilitiesState, MemberCapabilities, OnOffCapability } from './types';\n\ntype MemberType = 'self' | 'member';\n\n/**\n * Computes an on/off capability state from filtered flags\n *\n * Logic:\n * - `on` is true if any flag does NOT end with `.off`\n * - `off` is true if any flag does NOT end with `.on`\n *\n * This means parent permissions (without .on/.off suffix) grant both on and off\n */\nfunction computeOnOffCapability(filteredFlags: string[]): OnOffCapability {\n if (filteredFlags.length === 0) {\n return DEFAULT_ON_OFF;\n }\n\n return {\n on: filteredFlags.some((flag) => !flag.endsWith('.off')),\n off: filteredFlags.some((flag) => !flag.endsWith('.on'))\n };\n}\n\n/**\n * Filters flags for a specific member capability with on/off support\n *\n * Matches:\n * - The member type itself (e.g., \"self\" grants all self capabilities)\n * - The parent capability (e.g., \"self.mute\" grants all mute capabilities)\n * - The specific capability and its on/off variants (e.g., \"self.mute.audio*\")\n */\nfunction filterMemberOnOffFlags(\n flags: string[],\n memberType: MemberType,\n parent: string,\n capability: string\n): string[] {\n return flags.filter(\n (flag) =>\n flag === memberType ||\n flag === `${memberType}.${parent}` ||\n flag.startsWith(`${memberType}.${parent}.${capability}`)\n );\n}\n\n/**\n * Checks if a boolean capability is granted\n *\n * Matches:\n * - The member type itself (e.g., \"self\" grants all self capabilities)\n * - The parent capability if provided (e.g., \"self.microphone\" grants volume and sensitivity)\n * - The specific capability (e.g., \"self.microphone.volume.set\")\n */\nfunction hasBooleanCapability(\n flags: string[],\n memberType: MemberType,\n parent: string | null,\n capability: string\n): boolean {\n return flags.some(\n (flag) =>\n flag === memberType ||\n (parent !== null && flag === `${memberType}.${parent}`) ||\n flag.startsWith(`${memberType}.${parent ? `${parent}.` : ''}${capability}`)\n );\n}\n\n/**\n * Computes member-level capabilities (self or member) from raw flags\n */\nfunction computeMemberCapabilities(flags: string[], memberType: MemberType): MemberCapabilities {\n // Filter only flags relevant to this member type\n const memberFlags = flags.filter((flag) => flag.startsWith(memberType) || flag === memberType);\n\n if (memberFlags.length === 0) {\n return DEFAULT_MEMBER_CAPABILITIES;\n }\n\n return {\n muteAudio: computeOnOffCapability(filterMemberOnOffFlags(flags, memberType, 'mute', 'audio')),\n muteVideo: computeOnOffCapability(filterMemberOnOffFlags(flags, memberType, 'mute', 'video')),\n deaf: computeOnOffCapability(\n flags.filter((flag) => flag === memberType || flag.startsWith(`${memberType}.deaf`))\n ),\n raisehand: computeOnOffCapability(\n flags.filter((flag) => flag === memberType || flag.startsWith(`${memberType}.raisehand`))\n ),\n microphoneVolume: hasBooleanCapability(flags, memberType, 'microphone', 'volume'),\n microphoneSensitivity: hasBooleanCapability(flags, memberType, 'microphone', 'sensitivity'),\n speakerVolume: hasBooleanCapability(flags, memberType, 'speaker', 'volume'),\n position: hasBooleanCapability(flags, memberType, null, 'position'),\n meta: hasBooleanCapability(flags, memberType, null, 'meta'),\n remove: hasBooleanCapability(flags, memberType, null, 'remove'),\n audioFlags: hasBooleanCapability(flags, memberType, null, 'audioflags')\n };\n}\n\n/**\n * Computes all call capabilities from raw capability strings\n *\n * This is a pure function that transforms the raw capability strings\n * from the `call.joined` event into a structured capabilities state.\n *\n * @param capabilities - Raw capability strings from the server\n * @returns Computed capabilities state\n */\nexport function computeCapabilities(capabilities: string[]): CallCapabilitiesState {\n if (capabilities.length === 0) {\n return DEFAULT_CALL_CAPABILITIES;\n }\n\n return {\n self: computeMemberCapabilities(capabilities, 'self'),\n member: computeMemberCapabilities(capabilities, 'member'),\n end: capabilities.some((cap) => cap === 'end'),\n setLayout: capabilities.some((cap) => cap.startsWith('layout')),\n sendDigit: capabilities.some((cap) => cap.startsWith('digit')),\n vmutedHide: computeOnOffCapability(capabilities.filter((flag) => flag.startsWith('vmuted'))),\n lock: computeOnOffCapability(capabilities.filter((flag) => flag.startsWith('lock'))),\n device: capabilities.some((cap) => cap === 'device'),\n screenshare: capabilities.some((cap) => cap === 'screenshare')\n };\n}\n","import { distinctUntilChanged, map } from 'rxjs';\n\nimport { computeCapabilities } from './computeCapabilities';\nimport { DEFAULT_CALL_CAPABILITIES } from './types';\nimport { Destroyable } from '../../behaviors/Destroyable';\n\nimport type { CallCapabilitiesState, MemberCapabilities, OnOffCapability } from './types';\nimport type { Observable } from 'rxjs';\n\n/**\n * SelfCapabilities manages the capability state for the self participant.\n *\n * Capabilities are received from the server via `call.joined` events and determine\n * what actions the current participant is allowed to perform.\n *\n * Each capability is exposed as both:\n * - An observable (e.g., `end$`) for reactive state management\n * - A synchronous getter (e.g., `end`) for immediate access\n *\n * Member-level capabilities are accessed via the grouped `self` / `member` accessors:\n * - `capabilities.self.muteAudio` (sync)\n * - `capabilities.self$` (observable)\n *\n * When a new `call.joined` event is received, the capabilities state is\n * completely replaced (not merged).\n */\nexport class SelfCapabilities extends Destroyable {\n private _state$ = this.createBehaviorSubject<CallCapabilitiesState>(DEFAULT_CALL_CAPABILITIES);\n\n /**\n * Updates the capabilities state from raw capability strings.\n * This completely replaces the current state.\n *\n * @param capabilities - Raw capability strings from the server\n * @internal\n */\n public updateFromRaw(capabilities: string[]): void {\n const newState = computeCapabilities(capabilities);\n this._state$.next(newState);\n }\n\n // ============================================\n // Self member capabilities\n // ============================================\n\n /** Observable for self member capabilities */\n public get self$(): Observable<MemberCapabilities> {\n return this.cachedObservable('self$', () =>\n this._state$.pipe(\n map((state) => state.self),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current self member capabilities */\n public get self(): MemberCapabilities {\n return this._state$.value.self;\n }\n\n // ============================================\n // Member capabilities (other participants)\n // ============================================\n\n /** Observable for other member capabilities */\n public get member$(): Observable<MemberCapabilities> {\n return this.cachedObservable('member$', () =>\n this._state$.pipe(\n map((state) => state.member),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current other member capabilities */\n public get member(): MemberCapabilities {\n return this._state$.value.member;\n }\n\n // ============================================\n // Call-level capabilities\n // ============================================\n\n /** Observable for end call capability */\n public get end$(): Observable<boolean> {\n return this.cachedObservable('end$', () =>\n this._state$.pipe(\n map((state) => state.end),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current end call capability */\n public get end(): boolean {\n return this._state$.value.end;\n }\n\n /** Observable for set layout capability */\n public get setLayout$(): Observable<boolean> {\n return this.cachedObservable('setLayout$', () =>\n this._state$.pipe(\n map((state) => state.setLayout),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current set layout capability */\n public get setLayout(): boolean {\n return this._state$.value.setLayout;\n }\n\n /** Observable for send digit capability */\n public get sendDigit$(): Observable<boolean> {\n return this.cachedObservable('sendDigit$', () =>\n this._state$.pipe(\n map((state) => state.sendDigit),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current send digit capability */\n public get sendDigit(): boolean {\n return this._state$.value.sendDigit;\n }\n\n /** Observable for vmuted hide capability */\n public get vmutedHide$(): Observable<OnOffCapability> {\n return this.cachedObservable('vmutedHide$', () =>\n this._state$.pipe(\n map((state) => state.vmutedHide),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current vmuted hide capability */\n public get vmutedHide(): OnOffCapability {\n return this._state$.value.vmutedHide;\n }\n\n /** Observable for lock capability */\n public get lock$(): Observable<OnOffCapability> {\n return this.cachedObservable('lock$', () =>\n this._state$.pipe(\n map((state) => state.lock),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current lock capability */\n public get lock(): OnOffCapability {\n return this._state$.value.lock;\n }\n\n /** Observable for device capability */\n public get device$(): Observable<boolean> {\n return this.cachedObservable('device$', () =>\n this._state$.pipe(\n map((state) => state.device),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current device capability */\n public get device(): boolean {\n return this._state$.value.device;\n }\n\n /** Observable for screenshare capability */\n public get screenshare$(): Observable<boolean> {\n return this.cachedObservable('screenshare$', () =>\n this._state$.pipe(\n map((state) => state.screenshare),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current screenshare capability */\n public get screenshare(): boolean {\n return this._state$.value.screenshare;\n }\n\n // ============================================\n // Full state access\n // ============================================\n\n /** Observable for the full capabilities state */\n public get state$(): Observable<CallCapabilitiesState> {\n return this._state$.asObservable();\n }\n\n /** Current full capabilities state */\n public get state(): CallCapabilitiesState {\n return this._state$.value;\n }\n}\n","export function toggleDeafMethod(is: boolean): 'call.undeaf' | 'call.deaf' {\n return is ? 'call.undeaf' : 'call.deaf';\n}\n\nexport function toggleHandraiseMethod(is: boolean): 'call.lowerhand' | 'call.raisehand' {\n return is ? 'call.lowerhand' : 'call.raisehand';\n}\n","import { distinctUntilChanged, map } from 'rxjs/operators';\n\nimport { Destroyable } from '../../behaviors/Destroyable';\nimport { PreferencesContainer } from '../../containers/PreferencesContainer';\nimport { filterNull } from '../../operators/filterNull';\nimport { getLogger } from '../../utils/logger';\nimport { SelfCapabilities } from '../capabilities';\nimport { UnimplementedError } from '../errors';\nimport { toggleDeafMethod, toggleHandraiseMethod } from '../RPCMessages/utils';\n\nimport type { CallParticipant, CallSelfParticipant } from './types/call.types';\nimport type { SelectDeviceOptions } from './types/participant.types';\nimport type { DeviceController } from '../../interfaces/DeviceController';\nimport type { VertoManager } from '../../interfaces/VertoManager';\nimport type { ScreenShareStatus } from '../../managers/types/verto-manager.types';\nimport type { JSONRPCResponse } from '../RPCMessages/types/base';\nimport type { Member, LayoutLayer, MemberTarget } from '../RPCMessages/types/common';\nimport type { VideoPosition } from '../types/call.types';\nimport type { MediaOptions } from '../types/media.types';\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\n/**\n * Callback type for executing call methods\n * Injected to avoid circular dependency with Call class\n */\nexport type ExecuteMethod = <T extends JSONRPCResponse = JSONRPCResponse>(\n target: string | MemberTarget,\n method: string,\n args: Record<string, unknown>\n) => Promise<T>;\n\ntype ParticipantState = Member & { position: LayoutLayer };\n\nconst initialState: Partial<ParticipantState> = {};\n\n/**\n * Represents a participant in a call.\n *\n * Provides observable state (audio/video mute, hand raise, volume, etc.)\n * and control methods for the participant. See {@link SelfParticipant} for\n * the local participant with additional device control.\n */\nexport class Participant extends Destroyable implements CallParticipant {\n /** Unique member ID of this participant. */\n public readonly id!: string;\n private _state$ = this.createBehaviorSubject<Partial<ParticipantState>>(initialState);\n constructor(\n id: string,\n protected executeMethod: ExecuteMethod,\n protected deviceController: DeviceController\n ) {\n super();\n this.id = id;\n }\n /** @internal */\n public upnext(data: Partial<ParticipantState>): void {\n this._state$.next({ ...this._state$.value, ...data });\n }\n\n /** Observable of the participant's display name. */\n public get name$(): Observable<string> {\n return this.cachedObservable('name$', () =>\n this._state$.pipe(\n map((state) => state.name),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable of the participant type (e.g. `'member'`, `'screen'`). */\n public get type$(): Observable<string> {\n return this.cachedObservable('type$', () =>\n this._state$.pipe(\n map((state) => state.type),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable indicating whether the participant has raised their hand. */\n public get handraised$(): Observable<boolean> {\n return this.cachedObservable('handraised$', () =>\n this._state$.pipe(\n map((state) => state.handraised),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable indicating whether the participant is visible in the layout. */\n public get visible$(): Observable<boolean> {\n return this.cachedObservable('visible$', () =>\n this._state$.pipe(\n map((state) => state.visible),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable indicating whether the participant's audio is muted. */\n public get audioMuted$(): Observable<boolean> {\n return this.cachedObservable('audioMuted$', () =>\n this._state$.pipe(\n map((state) => state.audio_muted),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable indicating whether the participant's video is muted. */\n public get videoMuted$(): Observable<boolean> {\n return this.cachedObservable('videoMuted$', () =>\n this._state$.pipe(\n map((state) => state.video_muted),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable indicating whether the participant is deafened. */\n public get deaf$(): Observable<boolean> {\n return this.cachedObservable('deaf$', () =>\n this._state$.pipe(\n map((state) => state.deaf),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable of the participant's microphone input volume. */\n public get inputVolume$(): Observable<number> {\n return this.cachedObservable('inputVolume$', () =>\n this._state$.pipe(\n map((state) => state.input_volume),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable of the participant's speaker output volume. */\n public get outputVolume$(): Observable<number> {\n return this.cachedObservable('outputVolume$', () =>\n this._state$.pipe(\n map((state) => state.output_volume),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable of the microphone input sensitivity level. */\n public get inputSensitivity$(): Observable<number> {\n return this.cachedObservable('inputSensitivity$', () =>\n this._state$.pipe(\n map((state) => state.input_sensitivity),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable indicating whether echo cancellation is enabled. */\n public get echoCancellation$(): Observable<boolean> {\n return this.cachedObservable('echoCancellation$', () =>\n this._state$.pipe(\n map((state) => state.echo_cancellation),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable indicating whether auto-gain control is enabled. */\n public get autoGain$(): Observable<boolean> {\n return this.cachedObservable('autoGain$', () =>\n this._state$.pipe(\n map((state) => state.auto_gain),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable indicating whether noise suppression is enabled. */\n public get noiseSuppression$(): Observable<boolean> {\n return this.cachedObservable('noiseSuppression$', () =>\n this._state$.pipe(\n map((state) => state.noise_suppression),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable indicating whether low-bitrate mode is active. */\n public get lowbitrate$(): Observable<boolean> {\n return this.cachedObservable('lowbitrate$', () =>\n this._state$.pipe(\n map((state) => state.lowbitrate),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable indicating whether noise reduction is active. */\n public get denoise$(): Observable<boolean> {\n return this.cachedObservable('denoise$', () =>\n this._state$.pipe(\n map((state) => state.denoise),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable of custom metadata for this participant. */\n public get meta$(): Observable<Record<string, unknown>> {\n return this.cachedObservable('meta$', () =>\n this._state$.pipe(\n map((state) => state.meta),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable of the participant's subscriber ID. */\n public get subscriberId$(): Observable<string> {\n return this.cachedObservable('subscriberId$', () =>\n this._state$.pipe(\n map((state) => state.subscriber_id),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable of the participant's address ID. */\n public get addressId$(): Observable<string> {\n return this.cachedObservable('addressId$', () =>\n this._state$.pipe(\n map((state) => state.address_id),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable of the server node ID for this participant. */\n public get nodeId$(): Observable<string> {\n return this.cachedObservable('nodeId$', () =>\n this._state$.pipe(\n map((state) => state.node_id),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Observable indicating whether the participant is currently speaking. */\n public get isTalking$(): Observable<boolean> {\n return this.cachedObservable('isTalking$', () =>\n this._state$.pipe(\n map((state) => state.talking),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Whether the participant is currently speaking. */\n public get isTalking(): boolean {\n return this._state$.value.talking ?? false;\n }\n\n /** Observable of the participant's layout position. */\n public get position$(): Observable<LayoutLayer | undefined> {\n return this.cachedObservable('position$', () =>\n this._state$.pipe(\n map((state) => state.position),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n /** Current layout position. */\n public get position(): LayoutLayer | undefined {\n return this._state$.value.position;\n }\n\n /** Whether the participant is an audience member (view-only). */\n public get isAudience(): boolean {\n return this._state$.value.isAudience ?? false;\n }\n\n /** Display name of this participant. */\n public get name(): string | undefined {\n return this._state$.value.name;\n }\n\n /** Participant type (e.g. `'member'`, `'screen'`). */\n public get type(): string | undefined {\n return this._state$.value.type;\n }\n\n public get handraised(): boolean {\n return this._state$.value.handraised ?? false;\n }\n\n public get visible(): boolean {\n return this._state$.value.visible ?? false;\n }\n\n public get audioMuted(): boolean {\n return this._state$.value.audio_muted ?? false;\n }\n\n public get videoMuted(): boolean {\n return this._state$.value.video_muted ?? false;\n }\n\n public get deaf(): boolean {\n return this._state$.value.deaf ?? false;\n }\n\n public get inputVolume(): number | undefined {\n return this._state$.value.input_volume;\n }\n\n public get outputVolume(): number | undefined {\n return this._state$.value.output_volume;\n }\n\n public get inputSensitivity(): number | undefined {\n return this._state$.value.input_sensitivity;\n }\n\n public get echoCancellation(): boolean {\n return this._state$.value.echo_cancellation ?? false;\n }\n\n public get autoGain(): boolean {\n return this._state$.value.auto_gain ?? false;\n }\n\n public get noiseSuppression(): boolean {\n return this._state$.value.noise_suppression ?? false;\n }\n\n public get lowbitrate(): boolean {\n return this._state$.value.lowbitrate ?? false;\n }\n\n public get denoise(): boolean {\n return this._state$.value.denoise ?? false;\n }\n\n public get meta(): Record<string, unknown> | undefined {\n return this._state$.value.meta;\n }\n\n public get subscriberId(): string | undefined {\n return this._state$.value.subscriber_id;\n }\n\n public get addressId(): string | undefined {\n return this._state$.value.address_id;\n }\n\n public get nodeId(): string | undefined {\n return this._state$.value.node_id;\n }\n\n /** @internal */\n public get value(): Partial<Member> {\n return this._state$.value;\n }\n\n /** Toggles the deafened state (mutes/unmutes incoming audio). */\n public async toggleDeaf(): Promise<void> {\n const method = toggleDeafMethod(this.deaf);\n const params = {};\n await this.executeMethod(this.id, method, params);\n }\n\n /** Toggles the hand-raised state. */\n public async toggleHandraise(): Promise<void> {\n await this.executeMethod(this.id, toggleHandraiseMethod(this.handraised), {});\n }\n\n /** Mutes the participant's audio. */\n public async mute(): Promise<void> {\n await this.executeMethod(this.id, 'call.mute', { channels: ['audio'] });\n }\n\n /** Unmutes the participant's audio. */\n public async unmute(): Promise<void> {\n await this.executeMethod(this.id, 'call.unmute', { channels: ['audio'] });\n }\n\n /** Toggles the participant's audio mute state. */\n public async toggleMute(): Promise<void> {\n return this.audioMuted ? this.unmute() : this.mute();\n }\n\n /** Mutes the participant's video. */\n public async muteVideo(): Promise<void> {\n await this.executeMethod(this.id, 'call.mute', { channels: ['video'] });\n }\n\n /** Unmutes the participant's video. */\n public async unmuteVideo(): Promise<void> {\n await this.executeMethod(this.id, 'call.unmute', { channels: ['video'] });\n }\n\n /** Toggles the participant's video mute state. */\n public async toggleMuteVideo(): Promise<void> {\n return this.videoMuted ? this.unmuteVideo() : this.muteVideo();\n }\n\n /** Toggles echo cancellation on the audio input. */\n public async toggleEchoCancellation(): Promise<void> {\n await this.executeMethod(this.id, 'call.audioflags.set', {\n echo_cancellation: !this.echoCancellation,\n auto_gain: this.autoGain,\n noise_suppression: this.noiseSuppression\n });\n }\n\n /** Toggles automatic gain control on the audio input. */\n public async toggleAudioInputAutoGain(): Promise<void> {\n await this.executeMethod(this.id, 'call.audioflags.set', {\n echo_cancellation: this.echoCancellation,\n auto_gain: !this.autoGain,\n noise_suppression: this.noiseSuppression\n });\n }\n\n /** Toggles noise suppression on the audio input. */\n public async toggleNoiseSuppression(): Promise<void> {\n await this.executeMethod(this.id, 'call.audioflags.set', {\n echo_cancellation: this.echoCancellation,\n auto_gain: this.autoGain,\n noise_suppression: !this.noiseSuppression\n });\n }\n\n // eslint-disable-next-line @typescript-eslint/require-await\n public async toggleLowbitrate(): Promise<void> {\n // NEEDS check backend implementation\n throw new UnimplementedError();\n }\n\n /** Sets the microphone input sensitivity level. */\n public async setAudioInputSensitivity(value: number): Promise<void> {\n await this.executeMethod(this.id, 'call.microphone.sensitivity.set', {\n sensitivity: value\n });\n }\n\n /** Sets the microphone input volume level. */\n public async setAudioInputVolume(value: number): Promise<void> {\n await this.executeMethod(this.id, 'call.microphone.volume.set', {\n volume: value\n });\n }\n\n /** Sets the speaker output volume level. */\n public async setAudioOutputVolume(value: number): Promise<void> {\n await this.executeMethod(this.id, 'call.speaker.volume.set', {\n volume: value\n });\n }\n\n /** Sets the participant's position in the video layout. */\n public async setPosition(value: VideoPosition): Promise<void> {\n await this.executeMethod(this.id, 'call.member.position.set', {\n position: value\n });\n }\n\n /** Removes this participant from the call. */\n public async remove(): Promise<void> {\n const state = this._state$.value;\n const target: MemberTarget = {\n member_id: this.id,\n call_id: state.call_id ?? '',\n node_id: state.node_id ?? ''\n };\n await this.executeMethod(target, 'call.member.remove', {});\n }\n\n /** Ends the call for this participant. */\n public async end(): Promise<void> {\n await this.executeMethod(this.id, 'call.end', {});\n }\n\n // eslint-disable-next-line @typescript-eslint/require-await\n public async setMeta(_meta: Record<string, unknown>): Promise<void> {\n // NEEDS backend implementation\n throw new UnimplementedError();\n }\n // eslint-disable-next-line @typescript-eslint/require-await\n public async updateMeta(_meta: Record<string, unknown>): Promise<void> {\n // NEEDS backend implementation\n throw new UnimplementedError();\n }\n\n public destroy(): void {\n // Cleanup callback reference - intentionally breaking type safety for cleanup\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any\n this.executeMethod = undefined as any;\n super.destroy();\n }\n}\n\n/**\n * The local participant in a call, with additional device and media control.\n *\n * Extends {@link Participant} with screen sharing, device selection,\n * and local media stream management.\n */\nexport class SelfParticipant extends Participant implements CallSelfParticipant {\n /**\n * Capabilities for this participant.\n * Contains all capability flags as both observables and values.\n */\n public readonly capabilities: SelfCapabilities;\n\n constructor(\n id: string,\n executeMethod: ExecuteMethod,\n private vertoManager: VertoManager,\n deviceController: DeviceController\n ) {\n super(id, executeMethod, deviceController);\n this.capabilities = new SelfCapabilities();\n }\n\n public override destroy(): void {\n this.capabilities.destroy();\n super.destroy();\n }\n\n /** Starts sharing the local screen. */\n public async startScreenShare(): Promise<void> {\n try {\n await this.vertoManager.addScreenMedia();\n } catch (error) {\n logger.error('[Participant.startScreenShare] Screen share error:', error);\n }\n }\n\n /** Observable of the current screen share status. */\n public get screenShareStatus$(): Observable<ScreenShareStatus> {\n return this.vertoManager.screenShareStatus$;\n }\n\n /** Current screen share status. */\n public get screenShareStatus(): ScreenShareStatus {\n return this.vertoManager.screenShareStatus;\n }\n\n /** Stops the current screen share. */\n public async stopScreenShare(): Promise<void> {\n return this.vertoManager.removeScreenMedia();\n }\n\n /** Adds an additional media input device to the call. */\n public async addAdditionalDevice(options: MediaOptions): Promise<void> {\n try {\n await this.vertoManager.addInputDevice(options);\n } catch (error) {\n logger.error('[Participant.startScreenShare] Screen share error:', error);\n }\n }\n\n /** Removes an additional media input device by ID. */\n public async removeAdditionalDevice(id: string): Promise<void> {\n return this.vertoManager.removeInputDevices(id);\n }\n\n /** Adds or replaces the primary audio input device with optional constraints or stream. */\n public async addAudioInputDevice({\n constraints,\n stream\n }: {\n constraints?: MediaTrackConstraints;\n stream?: MediaStream;\n } = {}): Promise<void> {\n const audio = (constraints ?? stream) ? undefined : true;\n return this.vertoManager.addMainInputDevices({\n audio,\n inputAudioDeviceConstraints: constraints,\n inputAudioStream: stream\n });\n }\n\n /** Adds or replaces the primary video input device with optional constraints or stream. */\n public async addVideoInputDevice({\n constraints,\n stream\n }: {\n constraints?: MediaTrackConstraints;\n stream?: MediaStream;\n } = {}): Promise<void> {\n const video = (constraints ?? stream) ? undefined : true;\n return this.vertoManager.addMainInputDevices({\n video,\n inputVideoDeviceConstraints: constraints,\n inputVideoStream: stream\n });\n }\n\n /** Adds or replaces primary input devices (audio and/or video). */\n public async addInputDevices(options: MediaOptions = {}): Promise<void> {\n await this.vertoManager.addMainInputDevices(options);\n }\n\n /** Selects the audio input device for future calls. Optionally saves as a preference. */\n public selectAudioInputDevice(device: MediaDeviceInfo, options: SelectDeviceOptions = {}): void {\n this.deviceController.selectAudioInputDevice(device);\n if (options.savePreference) {\n PreferencesContainer.instance.preferredAudioInput = device;\n }\n }\n\n /** Updates the audio input track constraints for the active call. */\n public async setAudioInputDeviceConstraints(constraints: MediaTrackConstraints): Promise<void> {\n await this.vertoManager.updateMediaConstraints({ audio: constraints });\n }\n\n /** Updates both audio and video input track constraints for the active call. */\n public async setInputDevicesConstraints(constraints: {\n audio: MediaTrackConstraints;\n video: MediaTrackConstraints;\n }): Promise<void> {\n await this.vertoManager.updateMediaConstraints(constraints);\n }\n\n /** Selects the video input device for future calls. Optionally saves as a preference. */\n public selectVideoInputDevice(device: MediaDeviceInfo, options: SelectDeviceOptions = {}): void {\n this.deviceController.selectVideoInputDevice(device);\n if (options.savePreference) {\n PreferencesContainer.instance.preferredVideoInput = device;\n }\n }\n\n /** Updates the video input track constraints for the active call. */\n public async setVideoInputDeviceConstraints(constraints: MediaTrackConstraints): Promise<void> {\n await this.vertoManager.updateMediaConstraints({ video: constraints });\n }\n\n /** Selects the audio output device. Optionally saves as a preference. */\n public selectAudioOutputDevice(device: MediaDeviceInfo, options: SelectDeviceOptions = {}): void {\n this.deviceController.selectAudioOutputDevice(device);\n if (options.savePreference) {\n PreferencesContainer.instance.preferredAudioOutput = device;\n }\n }\n\n public async mute(): Promise<void> {\n try {\n await super.mute();\n } catch (error) {\n logger.warn(\n '[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway',\n error\n );\n } finally {\n this.vertoManager.muteMainAudioInputDevice();\n }\n }\n\n public async unmute(): Promise<void> {\n try {\n await super.unmute();\n } catch (error) {\n logger.warn(\n '[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway',\n error\n );\n } finally {\n await this.vertoManager.unmuteMainAudioInputDevice();\n }\n }\n\n public async muteVideo(): Promise<void> {\n try {\n await super.muteVideo();\n } catch (error) {\n logger.warn(\n '[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway',\n error\n );\n } finally {\n this.vertoManager.muteMainVideoInputDevice();\n }\n }\n\n public async unmuteVideo(): Promise<void> {\n try {\n await super.unmuteVideo();\n } catch (error) {\n logger.warn(\n '[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway',\n error\n );\n } finally {\n await this.vertoManager.unmuteMainVideoInputDevice();\n }\n }\n}\n\n/** Type guard that checks if a participant is the local {@link SelfParticipant}. */\nexport const isSelfParticipant = (participant: Participant): participant is SelfParticipant => {\n return participant instanceof SelfParticipant;\n};\n","// =============================================================================\n// BASE TYPE GUARDS\n// =============================================================================\n// This file contains type guards for base JSON-RPC types.\n\nimport type { JSONRPCErrorResponse, JSONRPCRequest, JSONRPCResponse } from '../types/base';\n\n// =============================================================================\n// TYPE GUARD HELPERS\n// =============================================================================\n\nexport function isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nexport function hasProperty<T extends object, K extends string>(\n obj: T,\n key: K\n): obj is T & Record<K, unknown> {\n return key in obj;\n}\n\nexport function hasStringProperty<T extends object, K extends string>(\n obj: T,\n key: K\n): obj is T & Record<K, string> {\n return key in obj && typeof (obj as Record<string, unknown>)[key] === 'string';\n}\n\n// =============================================================================\n// BASE TYPE GUARDS\n// =============================================================================\n\nexport function isJSONRPCRequest(value: unknown): value is JSONRPCRequest {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'id') &&\n typeof value.id === 'string' &&\n hasProperty(value, 'method') &&\n typeof value.method === 'string'\n );\n}\n\nexport function isJSONRPCResponse(value: unknown): value is JSONRPCResponse {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'id') &&\n typeof value.id === 'string' &&\n (hasProperty(value, 'result') || hasProperty(value, 'error'))\n );\n}\n\nexport function isJSONRPCErrorResponse(value: unknown): value is JSONRPCErrorResponse {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'id') &&\n typeof value.id === 'string' &&\n // Standard JSON-RPC error: has error field\n ((hasProperty(value, 'error') &&\n isObject(value.error) &&\n hasProperty(value.error, 'code') &&\n hasProperty(value.error, 'message')) ||\n (hasProperty(value, 'result') &&\n isObject(value.result) &&\n hasProperty(value.result, 'code') &&\n value.result.code !== '200' &&\n hasProperty(value.result, 'message')))\n );\n}\n","// =============================================================================\n// SIGNALWIRE EVENT TYPE GUARDS\n// =============================================================================\n// This file contains type guards for SignalWire event types.\n\nimport { hasProperty, isJSONRPCRequest, isObject } from './base.guards';\n\nimport type { TypeGuard, ExtractParams } from '../types/base';\nimport type {\n CallConnectPayload,\n CallJoinedPayload,\n CallJoinedRequest,\n CallLeftPayload,\n CallLeftRequest,\n CallPlayPayload,\n CallPlayRequest,\n CallStatePayload,\n CallStateRequest,\n CallUpdatedPayload,\n CallUpdatedRequest,\n CallConnectRequest,\n ConversationMessagePayload,\n ConversationMessageRequest,\n ConversationMessageUpdatedPayload,\n ConversationMessageUpdatedRequest,\n LayoutChangedPayload,\n LayoutChangedRequest,\n MemberJoinedPayload,\n MemberJoinedRequest,\n MemberLeftPayload,\n MemberLeftRequest,\n MemberTalkingPayload,\n MemberTalkingRequest,\n MemberUpdatedPayload,\n MemberUpdatedRequest,\n SignalwireAuthorizationStatePayload,\n SignalwireAuthorizationStateRequest,\n SignalwireCallMetadata,\n SignalwireCallRequest,\n SignalwireEventRequestBase,\n SignalwireMetadata,\n SignalwireRequest,\n WebrtcMessagePayload,\n WebrtcMessageRequest\n} from '../types/events';\n\n// =============================================================================\n// TYPE GUARD FACTORY SYSTEM\n// =============================================================================\n\n/**\n * Registry of all SignalWire event types.\n * This is the single source of truth for event type strings.\n */\n// eslint-disable-next-line unused-imports/no-unused-vars\nconst EVENT_TYPE_REGISTRY = {\n 'signalwire.authorization.state': true,\n 'webrtc.message': true,\n 'call.joined': true,\n 'call.left': true,\n 'call.updated': true,\n 'call.state': true,\n 'call.play': true,\n 'call.connect': true,\n 'member.updated': true,\n 'member.joined': true,\n 'member.left': true,\n 'member.talking': true,\n 'layout.changed': true,\n 'conversation.message': true,\n 'conversation.message.updated': true\n} as const;\n\nexport type RegisteredEventType = keyof typeof EVENT_TYPE_REGISTRY;\n\n/**\n * Factory function to create Request-level type guards.\n */\nexport function createEventRequestGuard<T extends SignalwireEventRequestBase>(\n eventType: RegisteredEventType\n): TypeGuard<T> {\n return (value: unknown): value is T =>\n isSignalwireRequest(value) && value.params.event_type === eventType;\n}\n\n/**\n * Factory function to create Metadata-level type guards.\n */\nexport function createEventMetadataGuard<T extends SignalwireMetadata>(\n eventType: RegisteredEventType\n): TypeGuard<T> {\n return (value: unknown): value is T =>\n isSignalwireMetadata(value) && value.event_type === eventType;\n}\n\n// =============================================================================\n// SIGNALWIRE EVENT TYPE GUARDS\n// =============================================================================\n\nexport function isSignalwireRequest(value: unknown): value is SignalwireRequest {\n return (\n isJSONRPCRequest(value) &&\n value.method === 'signalwire.event' &&\n isObject(value.params) &&\n hasProperty(value.params, 'event_type')\n );\n}\n\nexport function isSignalwireCallRequest(value: unknown): value is SignalwireCallRequest {\n return (\n isSignalwireRequest(value) &&\n (isCallJoinedRequest(value) ||\n isCallLeftRequest(value) ||\n isCallUpdatedRequest(value) ||\n isCallStateRequest(value) ||\n isCallPlayRequest(value) ||\n isCallConnectRequest(value) ||\n isMemberUpdatedRequest(value) ||\n isMemberJoinedRequest(value) ||\n isMemberLeftRequest(value) ||\n isMemberTalkingRequest(value) ||\n isLayoutChangedRequest(value) ||\n isWebrtcMessageRequest(value) ||\n isConversationMessageRequest(value) ||\n isConversationMessageUpdatedRequest(value))\n );\n}\n\n// Generated Request guards using factory pattern\nexport const isSignalwireAuthorizationStateRequest =\n createEventRequestGuard<SignalwireAuthorizationStateRequest>('signalwire.authorization.state');\n\nexport const isWebrtcMessageRequest =\n createEventRequestGuard<WebrtcMessageRequest>('webrtc.message');\n\nexport const isCallJoinedRequest = createEventRequestGuard<CallJoinedRequest>('call.joined');\n\nexport const isCallLeftRequest = createEventRequestGuard<CallLeftRequest>('call.left');\n\nexport const isCallUpdatedRequest = createEventRequestGuard<CallUpdatedRequest>('call.updated');\n\nexport const isCallStateRequest = createEventRequestGuard<CallStateRequest>('call.state');\n\nexport const isCallPlayRequest = createEventRequestGuard<CallPlayRequest>('call.play');\n\nexport const isCallConnectRequest = createEventRequestGuard<CallConnectRequest>('call.connect');\n\nexport const isMemberUpdatedRequest =\n createEventRequestGuard<MemberUpdatedRequest>('member.updated');\n\nexport const isMemberJoinedRequest = createEventRequestGuard<MemberJoinedRequest>('member.joined');\n\nexport const isMemberLeftRequest = createEventRequestGuard<MemberLeftRequest>('member.left');\n\nexport const isMemberTalkingRequest =\n createEventRequestGuard<MemberTalkingRequest>('member.talking');\n\nexport const isLayoutChangedRequest =\n createEventRequestGuard<LayoutChangedRequest>('layout.changed');\n\nexport const isConversationMessageRequest =\n createEventRequestGuard<ConversationMessageRequest>('conversation.message');\n\nexport const isConversationMessageUpdatedRequest =\n createEventRequestGuard<ConversationMessageUpdatedRequest>('conversation.message.updated');\n\n// =============================================================================\n// SIGNALWIRE EVENT METADATA TYPE GUARDS (outer wrapper with event_type)\n// =============================================================================\n\nexport function isSignalwireMetadata(value: unknown): value is SignalwireMetadata {\n return (\n isObject(value) &&\n hasProperty(value, 'event_type') &&\n typeof value.event_type === 'string' &&\n hasProperty(value, 'params')\n );\n}\n\nexport function isSignalwireCallMetadata(value: unknown): value is SignalwireCallMetadata {\n return (\n isSignalwireMetadata(value) &&\n (isCallJoinedMetadata(value) ||\n isCallLeftMetadata(value) ||\n isCallUpdatedMetadata(value) ||\n isCallStateMetadata(value) ||\n isCallPlayMetadata(value) ||\n isCallConnectMetadata(value) ||\n isMemberUpdatedMetadata(value) ||\n isMemberJoinedMetadata(value) ||\n isMemberLeftMetadata(value) ||\n isMemberTalkingMetadata(value) ||\n isLayoutChangedMetadata(value) ||\n isWebrtcMessageMetadata(value) ||\n isConversationMessageMetadata(value) ||\n isConversationMessageUpdatedMetadata(value))\n );\n}\n\n// Generated Metadata guards using factory pattern\nexport const isSignalwireAuthorizationStateMetadata = createEventMetadataGuard<\n ExtractParams<SignalwireAuthorizationStateRequest>\n>('signalwire.authorization.state');\n\nexport const isWebrtcMessageMetadata =\n createEventMetadataGuard<ExtractParams<WebrtcMessageRequest>>('webrtc.message');\n\nexport const isCallJoinedMetadata =\n createEventMetadataGuard<ExtractParams<CallJoinedRequest>>('call.joined');\n\nexport const isCallLeftMetadata =\n createEventMetadataGuard<ExtractParams<CallLeftRequest>>('call.left');\n\nexport const isCallUpdatedMetadata =\n createEventMetadataGuard<ExtractParams<CallUpdatedRequest>>('call.updated');\n\nexport const isCallStateMetadata =\n createEventMetadataGuard<ExtractParams<CallStateRequest>>('call.state');\n\nexport const isCallPlayMetadata =\n createEventMetadataGuard<ExtractParams<CallPlayRequest>>('call.play');\n\nexport const isCallConnectMetadata =\n createEventMetadataGuard<ExtractParams<CallConnectRequest>>('call.connect');\n\nexport const isMemberUpdatedMetadata =\n createEventMetadataGuard<ExtractParams<MemberUpdatedRequest>>('member.updated');\n\nexport const isMemberJoinedMetadata =\n createEventMetadataGuard<ExtractParams<MemberJoinedRequest>>('member.joined');\n\nexport const isMemberLeftMetadata =\n createEventMetadataGuard<ExtractParams<MemberLeftRequest>>('member.left');\n\nexport const isMemberTalkingMetadata =\n createEventMetadataGuard<ExtractParams<MemberTalkingRequest>>('member.talking');\n\nexport const isLayoutChangedMetadata =\n createEventMetadataGuard<ExtractParams<LayoutChangedRequest>>('layout.changed');\n\nexport const isConversationMessageMetadata =\n createEventMetadataGuard<ExtractParams<ConversationMessageRequest>>('conversation.message');\n\nexport const isConversationMessageUpdatedMetadata = createEventMetadataGuard<\n ExtractParams<ConversationMessageUpdatedRequest>\n>('conversation.message.updated');\n\n// =============================================================================\n// EVENT PAYLOAD TYPE GUARDS\n// =============================================================================\n\nexport function isSignalwireAuthorizationStatePayload(\n value: unknown\n): value is SignalwireAuthorizationStatePayload {\n return isObject(value) && hasProperty(value, 'authorization_state');\n}\n\nexport function isCallJoinedPayload(value: unknown): value is CallJoinedPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'room_session') &&\n hasProperty(value, 'call_id') &&\n hasProperty(value, 'member_id') &&\n hasProperty(value, 'capabilities')\n );\n}\n\nexport function isCallLeftPayload(value: unknown): value is CallLeftPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'room_session') &&\n hasProperty(value, 'call_id') &&\n hasProperty(value, 'reason')\n );\n}\n\n/**\n * Payload-level guard for MemberUpdatedPayload.\n *\n * NOTE: This guard uses negative checks (`!hasProperty(value, 'reason')` and\n * `!hasProperty(member, 'talking')`) to distinguish from MemberLeftPayload and\n * MemberTalkingPayload, which share the same base shape (member, room_id,\n * room_session_id). These negative checks are fragile — if MemberUpdatedPayload\n * ever gains a `reason` or `talking` field, this guard will break.\n *\n * Prefer the metadata-level guard `isMemberUpdatedMetadata` which checks\n * `event_type === 'member.updated'` and is not susceptible to payload shape\n * overlap.\n */\nexport function isMemberUpdatedPayload(value: unknown): value is MemberUpdatedPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'member') &&\n hasProperty(value, 'room_id') &&\n hasProperty(value, 'room_session_id') &&\n !hasProperty(value, 'reason') &&\n !hasProperty((value as MemberUpdatedPayload).member, 'talking')\n );\n}\n\nexport function isMemberJoinedPayload(value: unknown): value is MemberJoinedPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'member') &&\n hasProperty(value, 'room_id') &&\n !hasProperty(value, 'reason')\n );\n}\n\nexport function isMemberLeftPayload(value: unknown): value is MemberLeftPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'member') &&\n hasProperty(value, 'room_id') &&\n hasProperty(value, 'reason')\n );\n}\n\nexport function isMemberTalkingPayload(value: unknown): value is MemberTalkingPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'member') &&\n hasProperty(value, 'room_id') &&\n hasProperty(value, 'room_session_id') &&\n !hasProperty(value, 'reason') &&\n hasProperty((value as MemberTalkingPayload).member, 'talking')\n );\n}\n\nexport function isLayoutChangedPayload(value: unknown): value is LayoutChangedPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'room_id') &&\n hasProperty(value, 'room_session_id') &&\n hasProperty(value, 'layout')\n );\n}\n\nexport function isCallUpdatedPayload(value: unknown): value is CallUpdatedPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'room_session') &&\n hasProperty(value, 'room_id') &&\n hasProperty(value, 'room_session_id')\n );\n}\n\nexport function isCallStatePayload(value: unknown): value is CallStatePayload {\n return (\n isObject(value) &&\n hasProperty(value, 'call_id') &&\n hasProperty(value, 'call_state') &&\n hasProperty(value, 'direction')\n );\n}\n\nexport function isCallPlayPayload(value: unknown): value is CallPlayPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'control_id') &&\n hasProperty(value, 'call_id') &&\n hasProperty(value, 'state')\n );\n}\n\nexport function isCallConnectPayload(value: unknown): value is CallConnectPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'connect_state') &&\n hasProperty(value, 'call_id') &&\n hasProperty(value, 'segment_id')\n );\n}\n\nexport function isConversationMessagePayload(value: unknown): value is ConversationMessagePayload {\n return (\n isObject(value) &&\n hasProperty(value, 'id') &&\n hasProperty(value, 'type') &&\n hasProperty(value, 'kind') &&\n hasProperty(value, 'conversation_name')\n );\n}\n\nexport function isConversationMessageUpdatedPayload(\n value: unknown\n): value is ConversationMessageUpdatedPayload {\n return isConversationMessagePayload(value);\n}\n\nexport function isWebrtcMessageEventInnerParams(value: unknown): value is WebrtcMessagePayload {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'id') &&\n hasProperty(value, 'method') &&\n typeof value.method === 'string'\n );\n}\n\n// =============================================================================\n// EVENT TYPE MAPPING\n// =============================================================================\n\nexport const EventTypeMap = {\n 'signalwire.authorization.state': isSignalwireAuthorizationStateRequest,\n 'webrtc.message': isWebrtcMessageRequest,\n 'call.joined': isCallJoinedRequest,\n 'call.left': isCallLeftRequest,\n 'call.updated': isCallUpdatedRequest,\n 'call.state': isCallStateRequest,\n 'call.play': isCallPlayRequest,\n 'call.connect': isCallConnectRequest,\n 'member.updated': isMemberUpdatedRequest,\n 'member.joined': isMemberJoinedRequest,\n 'member.left': isMemberLeftRequest,\n 'member.talking': isMemberTalkingRequest,\n 'layout.changed': isLayoutChangedRequest,\n 'conversation.message': isConversationMessageRequest,\n 'conversation.message.updated': isConversationMessageUpdatedRequest\n} as const;\n\nexport type EventType = keyof typeof EventTypeMap;\n\n/**\n * Gets the appropriate type guard for an event type.\n */\nexport function getEventGuard(eventType: string): TypeGuard<SignalwireRequest> | undefined {\n return EventTypeMap[eventType as EventType];\n}\n\n// =============================================================================\n// EVENT METADATA TYPE MAPPING\n// =============================================================================\n\nexport const EventMetadataTypeMap = {\n 'signalwire.authorization.state': isSignalwireAuthorizationStateMetadata,\n 'webrtc.message': isWebrtcMessageMetadata,\n 'call.joined': isCallJoinedMetadata,\n 'call.left': isCallLeftMetadata,\n 'call.updated': isCallUpdatedMetadata,\n 'call.state': isCallStateMetadata,\n 'call.play': isCallPlayMetadata,\n 'call.connect': isCallConnectMetadata,\n 'member.updated': isMemberUpdatedMetadata,\n 'member.joined': isMemberJoinedMetadata,\n 'member.left': isMemberLeftMetadata,\n 'member.talking': isMemberTalkingMetadata,\n 'layout.changed': isLayoutChangedMetadata,\n 'conversation.message': isConversationMessageMetadata,\n 'conversation.message.updated': isConversationMessageUpdatedMetadata\n} as const;\n\n/**\n * Gets the appropriate metadata type guard for an event type.\n */\nexport function getEventMetadataGuard(\n eventType: string\n): TypeGuard<SignalwireMetadata> | undefined {\n return EventMetadataTypeMap[eventType as EventType];\n}\n","import { distinctUntilChanged, filter, map, merge, tap } from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { isSelfParticipant } from '../core/entities/Participant';\nimport { DependencyError } from '../core/errors';\nimport {\n isCallJoinedPayload,\n isLayoutChangedPayload\n} from '../core/RPCMessages/guards/events.guards';\nimport { filterAs } from '../operators';\nimport { filterNull } from '../operators/filterNull';\nimport { getLogger } from '../utils/logger';\n\nimport type { Participant, SelfParticipant } from '../core/entities/Participant';\nimport type {\n CallManager,\n CallParticipant,\n CallSelfParticipant\n} from '../core/entities/types/call.types';\nimport type {\n LayoutLayer,\n Member,\n RoomSession,\n Layout,\n MemberTalkingInfo\n} from '../core/RPCMessages/types/common';\nimport type { CallLayoutListResponse } from '../core/RPCMessages/types/methods';\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\ntype SessionState = RoomSession & { capabilities: string[] } & {\n layouts: string[];\n} & { layout_layers: LayoutLayer[] };\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface WebRTCCallEventManagerOptions {}\n\nconst initialSessionState: Partial<SessionState> = {};\n\nexport class CallEventsManager extends Destroyable {\n private selfId?: string;\n private originCallId?: string;\n private callIds = new Set<string>();\n private roomSessionIds = new Set<string>();\n private _participants$ = this.createBehaviorSubject<Record<string, Participant>>({});\n\n private _self$ = this.createBehaviorSubject<SelfParticipant | null>(null);\n private _sessionState$ = this.createBehaviorSubject<Partial<SessionState>>(initialSessionState);\n\n constructor(\n protected webRtcCallSession: CallManager,\n protected options: WebRTCCallEventManagerOptions = {}\n ) {\n super();\n this.initSubscriptions();\n }\n public get participants$(): Observable<CallParticipant[]> {\n return this.cachedObservable('participants$', () =>\n this._participants$\n .asObservable()\n .pipe(map((participantsRecord) => Object.values(participantsRecord)))\n );\n }\n\n public get participants(): CallParticipant[] {\n return Object.values(this._participants$.value);\n }\n\n public get self$(): Observable<CallSelfParticipant> {\n return this.cachedObservable('self$', () => this._self$.asObservable().pipe(filterNull()));\n }\n\n // check if this call session has joined that same room session\n public isRoomSessionIdValid(roomSessionId: string): boolean {\n return this.roomSessionIds.has(roomSessionId);\n }\n\n public addCallId(callId: string): void {\n this.callIds.add(callId);\n }\n\n public isCallIdValid(callId: string): boolean {\n return this.callIds.has(callId);\n }\n\n public get recording$(): Observable<boolean> {\n return this.cachedObservable('recording$', () =>\n this._sessionState$.pipe(\n map((state) => state.recording),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get recordings$(): Observable<Record<string, unknown>[]> {\n return this.cachedObservable('recordings$', () =>\n this._sessionState$.pipe(\n map((state) => state.recordings),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get streaming$(): Observable<boolean> {\n return this.cachedObservable('streaming$', () =>\n this._sessionState$.pipe(\n map((state) => state.streaming),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get streams$(): Observable<Record<string, unknown>[]> {\n return this.cachedObservable('streams$', () =>\n this._sessionState$.pipe(\n map((state) => state.streams),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get playbacks$(): Observable<Record<string, unknown>[]> {\n return this.cachedObservable('playbacks$', () =>\n this._sessionState$.pipe(\n map((state) => state.playbacks),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get raiseHandPriority$(): Observable<boolean> {\n return this.cachedObservable('raiseHandPriority$', () =>\n this._sessionState$.pipe(\n map((state) => state.prioritize_handraise),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get locked$(): Observable<boolean> {\n return this.cachedObservable('locked$', () =>\n this._sessionState$.pipe(\n map((state) => state.locked),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get meta$(): Observable<Record<string, unknown>> {\n return this.cachedObservable('meta$', () =>\n this._sessionState$.pipe(\n map((state) => state.meta),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get capabilities$(): Observable<string[]> {\n return this.cachedObservable('capabilities$', () =>\n this._sessionState$.pipe(\n map((state) => state.capabilities),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get layout$(): Observable<string> {\n return this.cachedObservable('layout$', () =>\n this._sessionState$.pipe(\n map((state) => state.layout_name),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get layouts$(): Observable<string[]> {\n return this.cachedObservable('layouts$', () =>\n this._sessionState$.pipe(\n map((state) => state.layouts),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get layoutLayers$(): Observable<LayoutLayer[]> {\n return this.cachedObservable('layoutLayers$', () =>\n this._sessionState$.pipe(\n map((state) => state.layout_layers),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get self(): CallSelfParticipant | null {\n return this._self$.value;\n }\n\n public get layoutLayers(): LayoutLayer[] {\n return this._sessionState$.value.layout_layers ?? [];\n }\n\n public get recording(): boolean {\n return this._sessionState$.value.recording ?? false;\n }\n\n public get streaming(): boolean {\n return this._sessionState$.value.streaming ?? false;\n }\n\n public get raiseHandPriority(): boolean {\n return this._sessionState$.value.prioritize_handraise ?? false;\n }\n\n public get locked(): boolean {\n return this._sessionState$.value.locked ?? false;\n }\n\n public get meta(): Record<string, unknown> {\n return this._sessionState$.value.meta ?? {};\n }\n\n public get layout(): string | undefined {\n return this._sessionState$.value.layout_name;\n }\n\n public get layouts(): string[] {\n return this._sessionState$.value.layouts ?? [];\n }\n\n public get capabilities(): string[] {\n return this._sessionState$.value.capabilities ?? [];\n }\n\n public isSessionEvent(id: string): boolean {\n return this.callIds.has(id) || this.roomSessionIds.has(id);\n }\n\n protected initSubscriptions(): void {\n this.subscribeTo(this.callJoinedEvent$, (callJoinedEvent) => {\n logger.debug('[CallEventsManager] Handling call.joined event for call/session IDs:', {\n callId: callJoinedEvent.call_id,\n roomSessionId: callJoinedEvent.room_session_id\n });\n const sessionState = callJoinedEvent.room_session;\n const { capabilities } = callJoinedEvent;\n // Don't update selfId and originCallId from nested call.joined events\n this.selfId = this.selfId ?? callJoinedEvent.member_id;\n this.originCallId = this.originCallId ?? callJoinedEvent.origin_call_id;\n this.callIds.add(callJoinedEvent.call_id);\n this.roomSessionIds.add(callJoinedEvent.room_session_id);\n\n this._sessionState$.next({\n ...this._sessionState$.value,\n recording: sessionState.recording,\n recordings: sessionState.recordings,\n streaming: sessionState.streaming,\n streams: sessionState.streams,\n playbacks: sessionState.playbacks,\n\n prioritize_handraise: sessionState.prioritize_handraise,\n locked: sessionState.locked,\n meta: sessionState.meta,\n\n capabilities\n });\n\n this.updateParticipants(sessionState.members);\n\n // Update capabilities on the self participant\n // This must happen after updateParticipants to ensure self exists\n this._self$.value?.capabilities.updateFromRaw(capabilities);\n\n if (this._self$.value?.capabilities.setLayout) {\n this.updateLayouts();\n }\n });\n this.subscribeTo(this.memberUpdates$, (member) => {\n logger.debug('[CallEventsManager] Handling member update event for member ID:', member);\n this.upsertParticipant(member);\n });\n this.subscribeTo(this.webRtcCallSession.memberLeft$, (memberLeftEvent) => {\n logger.debug(\n '[CallEventsManager] Handling member.left event for member ID:',\n memberLeftEvent.member.member_id\n );\n const participants = { ...this._participants$.value };\n if (memberLeftEvent.member.member_id in participants) {\n delete participants[memberLeftEvent.member.member_id];\n // in case in subscription is observing the participants collection\n this._participants$.next(participants);\n } else {\n logger.warn(\n `[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`\n );\n }\n });\n this.subscribeTo(this.layoutChangedEvent$, (layoutChangedEvent) => {\n logger.debug('[CallEventsManager] Handling layout.changed event:', layoutChangedEvent);\n this._sessionState$.next({\n ...this._sessionState$.value,\n\n layout_name: layoutChangedEvent.id,\n\n layout_layers: layoutChangedEvent.layers\n });\n\n this.updateParticipantPositions(layoutChangedEvent);\n });\n }\n private updateParticipantPositions(layoutChangedEvent: Layout) {\n if (\n Object.keys(this._participants$.value).length > 0 &&\n !layoutChangedEvent.layers.some((layer) => !!layer.member_id)\n ) {\n logger.warn(\n '[CallEventsManager] No layers with member_id found in layout.changed event. Nothing to update.'\n );\n }\n layoutChangedEvent.layers\n .filter((layer) => Boolean(layer.member_id))\n .map((layer) => {\n // update participant state with new position\n if (!layer.member_id) {\n throw new DependencyError('Layer member_id is required');\n }\n this._participants$.value[layer.member_id].upnext({\n position: layer\n });\n return this._participants$.value[layer.member_id];\n })\n .forEach((participant) => {\n if (isSelfParticipant(participant)) {\n this._self$.next(participant);\n }\n // update the collection observable to notify changes\n this._participants$.next({\n ...this._participants$.value,\n [participant.id]: participant\n });\n });\n }\n\n updateLayouts(): void {\n if (!this.selfId) return;\n\n this.webRtcCallSession\n .executeMethod<CallLayoutListResponse>(this.selfId, 'call.layout.list', {})\n .then((response) => {\n this._sessionState$.next({\n ...this._sessionState$.value,\n layouts: response.result.layouts\n });\n })\n .catch((error) => {\n logger.error('[CallEventsManager] Error fetching layouts:', error);\n });\n }\n\n private updateParticipants(members: Member[]) {\n members.forEach((member) => this.upsertParticipant(member));\n }\n\n private upsertParticipant(member: Member | MemberTalkingInfo) {\n if (!(member.member_id in this._participants$.value)) {\n // Pass selfId from call.joined event to ensure correct self participant identification\n const newParticipant = this.webRtcCallSession.createParticipant(\n member.member_id,\n this.selfId\n );\n\n this._participants$.next({\n ...this._participants$.value,\n [member.member_id]: newParticipant\n });\n }\n\n const participant = this._participants$.value[member.member_id];\n\n const oldValue = participant.value;\n logger.debug('[CallEventsManager] Updating participant:', member.member_id, {\n oldValue,\n newValue: member\n });\n participant.upnext({\n ...oldValue,\n ...member\n });\n\n if (isSelfParticipant(participant)) {\n this._self$.next(participant);\n }\n // in case in subscription is observing the participants collection\n this._participants$.next(this._participants$.value);\n }\n\n private get callJoinedEvent$() {\n return this.cachedObservable('callJoinedEvent$', () =>\n this.webRtcCallSession.callEvent$.pipe(\n filter(isCallJoinedPayload),\n tap((event) => {\n logger.debug('[CallEventsManager] Call joined event:', event);\n })\n )\n );\n }\n\n private get layoutChangedEvent$() {\n return this.cachedObservable('layoutChangedEvent$', () =>\n this.webRtcCallSession.callEvent$.pipe(\n filterAs(isLayoutChangedPayload, 'layout'),\n tap((event) => {\n logger.debug('[CallEventsManager] Layout changed event:', event);\n })\n )\n );\n }\n\n private get memberUpdates$() {\n return this.cachedObservable('memberUpdates$', () =>\n merge(\n this.webRtcCallSession.memberJoined$,\n this.webRtcCallSession.memberUpdated$,\n this.webRtcCallSession.memberTalking$\n ).pipe(\n map((event) => event.member),\n tap((event) => {\n logger.debug('[CallEventsManager] Member update event:', event);\n })\n )\n );\n }\n\n public override destroy(): void {\n const participants = Object.values(this._participants$.value);\n participants.forEach((participant) => {\n participant.destroy();\n });\n this._participants$.next({});\n this._self$.next(null);\n\n this.callIds.clear();\n this.roomSessionIds.clear();\n this.selfId = undefined;\n this.originCallId = undefined;\n //@ts-expect-error -- avoiding circular references\n this.webRtcCallSession = undefined;\n //@ts-expect-error -- avoiding circular references\n this.callSession = undefined;\n\n super.destroy();\n }\n}\n","/**\n * SDPHelper - Utility functions for SDP (Session Description Protocol) parsing and validation.\n *\n * This module provides helper functions to analyze and validate SDP content,\n * particularly for ICE candidate validation in WebRTC connections.\n */\n\nimport type { MediaDirections, MediaDirection } from '../core/types/media.types';\n\n/** Valid SDP direction attribute values. */\nconst SDP_DIRECTIONS: ReadonlySet<string> = new Set([\n 'sendrecv',\n 'sendonly',\n 'recvonly',\n 'inactive'\n]);\n\n/**\n * Extracts the media directions (audio/video) from an SDP string.\n *\n * Parses each media section (`m=audio` / `m=video`) and reads the `a=` direction\n * attribute (`sendrecv`, `sendonly`, `recvonly`, `inactive`).\n * If no explicit direction attribute is found for a media section, defaults to `sendrecv`\n * per RFC 4566.\n *\n * @param sdp - The SDP string to parse\n * @returns The extracted audio and video directions\n *\n * @example\n * ```typescript\n * const sdp = `v=0\\r\\nm=audio 9 UDP/TLS/RTP/SAVPF 111\\r\\na=sendrecv\\r\\nm=video 9 UDP/TLS/RTP/SAVPF 96\\r\\na=recvonly`;\n * extractMediaDirectionsFromSDP(sdp);\n * // { audio: 'sendrecv', video: 'recvonly' }\n * ```\n */\nexport function extractMediaDirectionsFromSDP(sdp: string): MediaDirections {\n const result: MediaDirections = {\n audio: 'inactive',\n video: 'inactive'\n };\n\n if (!sdp) {\n return result;\n }\n\n const lines = sdp.split(/\\r?\\n/);\n let currentMediaKind: 'audio' | 'video' | null = null;\n let currentDirection: MediaDirection | null = null;\n\n for (const line of lines) {\n if (line.startsWith('m=')) {\n // Flush direction for previous section\n if (currentMediaKind) {\n result[currentMediaKind] = currentDirection ?? 'sendrecv';\n }\n\n // Determine new media kind\n if (line.startsWith('m=audio')) {\n currentMediaKind = 'audio';\n } else if (line.startsWith('m=video')) {\n currentMediaKind = 'video';\n } else {\n currentMediaKind = null;\n }\n currentDirection = null;\n } else if (currentMediaKind && line.startsWith('a=')) {\n const attr = line.substring(2).trim();\n if (SDP_DIRECTIONS.has(attr)) {\n currentDirection = attr as MediaDirection;\n }\n }\n }\n\n // Flush last section\n if (currentMediaKind) {\n result[currentMediaKind] = currentDirection ?? 'sendrecv';\n }\n\n return result;\n}\n\n/**\n * Validates that an SDP string has at least one non-host ICE candidate\n * for each media section (m= line).\n *\n * Non-host candidates (srflx, prflx, relay) indicate that the SDP has\n * gathered candidates that can be used for connectivity through NAT\n * traversal mechanisms.\n *\n * @param sdp - The SDP string to validate\n * @returns true if the SDP is valid (has non-host candidates for all media sections,\n * or has no media sections), false otherwise\n *\n * @example\n * ```typescript\n * const sdp = `v=0\n * m=audio 9 UDP/TLS/RTP/SAVPF 111\n * a=candidate:1 1 UDP 1694498815 203.0.113.1 50001 typ srflx\n * m=video 9 UDP/TLS/RTP/SAVPF 96\n * a=candidate:2 1 UDP 1694498815 203.0.113.1 50002 typ relay`;\n *\n * isValidLocalDescription(sdp); // returns true\n * ```\n */\nexport function isValidLocalDescription(sdp: string): boolean {\n if (!sdp) {\n return false;\n }\n\n // Parse SDP to find media sections (m= lines)\n const lines = sdp.split('\\r\\n');\n const mediaSectionsValidCandidates: number[] = [];\n let currentSection = -1;\n\n for (const line of lines) {\n if (line.startsWith('m=')) {\n // New media section\n currentSection += 1;\n mediaSectionsValidCandidates[currentSection] = 0;\n } else if (line.startsWith('a=candidate:')) {\n const typeMatch = /\\styp\\s+(host|srflx|prflx|relay)/.exec(line);\n if (typeMatch && typeMatch[1] !== 'host') {\n // count only non-host candidates\n mediaSectionsValidCandidates[currentSection] += 1;\n }\n }\n }\n\n return (\n !mediaSectionsValidCandidates.length ||\n // Check if localDescription has at least one non-host candidate for each media section.\n mediaSectionsValidCandidates.every((count) => count > 0)\n );\n}\n","import { filter, map, withLatestFrom } from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport {\n DEFAULT_ICE_CANDIDATE_TIMEOUT_MS,\n DEFAULT_ICE_GATHERING_TIMEOUT_MS\n} from '../core/constants';\nimport { isValidLocalDescription } from '../helpers/SDPHelper';\nimport { getLogger } from '../utils/logger';\n\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\nexport interface ICEGatheringControllerOptions {\n iceCandidateTimeout?: number;\n iceGatheringTimeout?: number;\n relayOnly?: boolean;\n}\n\nexport type ICEGatheringStates = 'new' | 'gathering' | 'complete' | 'timeout';\nexport interface ICECandidateState {\n state: ICEGatheringStates;\n validSDP: boolean;\n}\n\nexport class ICEGatheringController extends Destroyable {\n private iceCandidateTimeout: number;\n private iceGatheringTimeout: number;\n private iceCandidateTimer?: ReturnType<typeof setTimeout>;\n private iceGatheringTimer?: ReturnType<typeof setTimeout>;\n private relayOnly: boolean;\n\n // Event handlers (bound to this instance)\n private onicegatheringstatechangeHandler = () => {\n const { iceGatheringState } = this.peerConnection;\n logger.debug(`[ICEGatheringController] ICE gathering state changed to: ${iceGatheringState}`);\n if (iceGatheringState === 'gathering') {\n this._iceCandidatesState.next({\n state: 'gathering',\n validSDP: false\n });\n }\n };\n\n private onicecandidateHandler = (event: RTCPeerConnectionIceEvent) => {\n logger.debug('[ICEGatheringController] ICE candidate event received:', event.candidate);\n this.removeTimer('iceCandidateTimer');\n\n if (event.candidate) {\n this.iceCandidateTimer = setTimeout(() => {\n if (this.peerConnection.iceGatheringState !== 'complete') {\n logger.warn('[ICEGatheringController] ICE candidate timeout, using current SDP');\n this.handleICECandidateTimeout();\n }\n }, this.iceCandidateTimeout);\n } else {\n logger.debug('[ICEGatheringController] ICE gathering completed: null candidate received');\n this.removeTimer('iceGatheringTimer');\n\n this.handleICEGatheringComplete();\n }\n };\n\n private _iceCandidatesState = this.createBehaviorSubject<ICECandidateState>({\n state: 'new',\n validSDP: false\n });\n constructor(\n private peerConnection: RTCPeerConnection,\n private peerConnectionControllerNegotiating$: Observable<boolean>,\n options: ICEGatheringControllerOptions = {}\n ) {\n super();\n this.iceCandidateTimeout = options.iceCandidateTimeout ?? DEFAULT_ICE_CANDIDATE_TIMEOUT_MS;\n this.iceGatheringTimeout = options.iceGatheringTimeout ?? DEFAULT_ICE_GATHERING_TIMEOUT_MS;\n this.relayOnly = options.relayOnly ?? false;\n // Setup ICE candidate handling\n this.setupEventListeners();\n this.subscribeTo(\n this.peerConnectionControllerNegotiating$.pipe(filter((isNegotiating) => isNegotiating)),\n (isNegotiating) => {\n if (isNegotiating) {\n this.setupEventListeners();\n this.iceGatheringTimer = setTimeout(() => {\n if (this.peerConnection.iceGatheringState !== 'complete') {\n logger.warn('[ICEGatheringController] ICE gathering timeout, using current SDP');\n this.handleICEGatheringTimeout();\n }\n }, this.iceGatheringTimeout);\n }\n }\n );\n }\n\n private setupEventListeners() {\n this.peerConnection.removeEventListener('icecandidate', this.onicecandidateHandler);\n this.peerConnection.addEventListener('icecandidate', this.onicecandidateHandler);\n\n // Setup ICE gathering state change handling\n this.peerConnection.removeEventListener(\n 'icegatheringstatechange',\n this.onicegatheringstatechangeHandler\n );\n this.peerConnection.addEventListener(\n 'icegatheringstatechange',\n this.onicegatheringstatechangeHandler\n );\n }\n\n public get iceCandidatesState$(): Observable<ICEGatheringStates> {\n return this._iceCandidatesState.pipe(\n withLatestFrom(this.peerConnectionControllerNegotiating$),\n filter(([_, isNegotiating]) => isNegotiating),\n map(([state, _]) => state.state)\n );\n }\n\n public get hasValidLocalDescriptionSDP(): boolean {\n const sdp = this.peerConnection.localDescription?.sdp;\n return isValidLocalDescription(sdp ?? '');\n }\n\n public get isRelayOnly(): boolean {\n return this.relayOnly;\n }\n\n public setRelayOnly(value: boolean): void {\n this.relayOnly = value;\n }\n\n private handleICEGatheringComplete(): void {\n logger.debug('[ICEGatheringController] Handling ICE gathering complete');\n\n logger.debug(\n `[ICEGatheringController] Checking ICE gathering state: ${this.peerConnection.iceGatheringState}`\n );\n\n logger.debug('[ICEGatheringController] ICE gathering complete');\n\n this._iceCandidatesState.next({\n state: 'complete',\n validSDP: this.hasValidLocalDescriptionSDP\n });\n this.stopGathering();\n }\n\n private stopGathering(): void {\n this.peerConnection.removeEventListener(\n 'icegatheringstatechange',\n this.onicegatheringstatechangeHandler\n );\n this.peerConnection.removeEventListener('icecandidate', this.onicecandidateHandler);\n this.clearAllTimers();\n }\n\n private handleICEGatheringTimeout(): void {\n this.removeTimer('iceGatheringTimer');\n\n const validSDP = this.hasValidLocalDescriptionSDP;\n if (validSDP) {\n logger.debug('[ICEGatheringController] Local SDP is valid');\n this._iceCandidatesState.next({\n state: 'timeout',\n validSDP: validSDP\n });\n this.stopGathering();\n } else {\n logger.debug('### ICE gathering timeout\\n', this.peerConnection.localDescription?.sdp);\n }\n }\n\n public handleICECandidateTimeout(): void {\n if (this.iceCandidateTimer) {\n this.removeTimer('iceCandidateTimer');\n }\n\n logger.warn('[ICEGatheringController] ICE candidate timeout');\n\n const validSDP = this.hasValidLocalDescriptionSDP;\n if (!validSDP && !this.relayOnly) {\n this.restartICEGatheringWithRelayOnly();\n } else {\n logger.debug('[ICEGatheringController] Using current SDP due to ICE candidate timeout');\n this._iceCandidatesState.next({\n state: 'timeout',\n validSDP: validSDP\n });\n this.stopGathering();\n }\n }\n\n public restartICEGatheringWithRelayOnly(): void {\n logger.debug('[ICEGatheringController] Restarting ICE gathering with relay-only candidates');\n this.relayOnly = true;\n this.peerConnection.setConfiguration({\n ...this.peerConnection.getConfiguration(),\n iceTransportPolicy: 'relay'\n });\n if (!(this.peerConnection.connectionState === 'connected')) {\n this.peerConnection.restartIce();\n }\n }\n\n public removeTimer(timer: 'iceGatheringTimer' | 'iceCandidateTimer'): void {\n if (this[timer]) {\n clearTimeout(this[timer]);\n this[timer] = undefined;\n }\n }\n\n private clearAllTimers(): void {\n logger.debug('[ICEGatheringController] Clearing all timers');\n\n this.removeTimer('iceGatheringTimer');\n this.removeTimer('iceCandidateTimer');\n }\n\n public removeEventListeners(): void {\n this.peerConnection.removeEventListener(\n 'icegatheringstatechange',\n this.onicegatheringstatechangeHandler\n );\n this.peerConnection.removeEventListener('icecandidate', this.onicecandidateHandler);\n }\n\n public destroy(): void {\n logger.debug('[ICEGatheringController] Destroying ICEGatheringController');\n this.clearAllTimers();\n this.removeEventListeners();\n super.destroy();\n }\n}\n","import { takeUntil } from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { getLogger } from '../utils/logger';\n\nimport type { MediaOptions } from '../core/types/media.types';\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\nexport interface LocalStreamControllerOptions extends Omit<\n MediaOptions,\n 'inputAudioDeviceConstraints' | 'inputVideoDeviceConstraints'\n> {\n getUserMedia: (constraints: MediaStreamConstraints) => Promise<MediaStream>;\n getDisplayMedia: (options: DisplayMediaStreamOptions) => Promise<MediaStream>;\n propose: 'main' | 'screenshare' | 'additional-device';\n inputAudioDeviceConstraints?: MediaTrackConstraints | boolean;\n inputVideoDeviceConstraints?: MediaTrackConstraints | boolean;\n}\n\nexport class LocalStreamController extends Destroyable {\n private mediaTrackEndedHandler = (event: unknown) => {\n this._mediaTrackEnded$.next(event as MediaStreamTrack);\n };\n private _localStream$ = this.createBehaviorSubject<MediaStream | null>(null);\n private _localAudioTracks$ = this.createBehaviorSubject<MediaStreamTrack[]>([]);\n private _localVideoTracks$ = this.createBehaviorSubject<MediaStreamTrack[]>([]);\n private _mediaTrackEnded$ = this.createSubject<MediaStreamTrack>();\n\n constructor(private options: LocalStreamControllerOptions) {\n super();\n }\n\n public get localStream$(): Observable<MediaStream | null> {\n return this._localStream$.asObservable().pipe(takeUntil(this.destroyed$));\n }\n\n public get localAudioTracks$(): Observable<MediaStreamTrack[]> {\n return this._localAudioTracks$.asObservable().pipe(takeUntil(this.destroyed$));\n }\n\n public get localVideoTracks$(): Observable<MediaStreamTrack[]> {\n return this._localVideoTracks$.asObservable().pipe(takeUntil(this.destroyed$));\n }\n\n public get mediaTrackEnded$(): Observable<MediaStreamTrack> {\n return this._mediaTrackEnded$.asObservable().pipe(takeUntil(this.destroyed$));\n }\n\n public get localStream(): MediaStream | null {\n return this._localStream$.value;\n }\n\n public get localAudioTracks(): MediaStreamTrack[] {\n return this._localAudioTracks$.value;\n }\n\n public get localVideoTracks(): MediaStreamTrack[] {\n return this._localVideoTracks$.value;\n }\n\n /**\n * Build the local media stream based on the provided options.\n */\n public async buildLocalStream(): Promise<MediaStream> {\n logger.debug('[LocalStreamController] Building local media stream.');\n let stream: MediaStream;\n if (this.options.inputAudioStream ?? this.options.inputVideoStream) {\n const tracks = [\n ...(this.options.inputAudioStream?.getTracks() ?? []),\n ...(this.options.inputVideoStream?.getTracks() ?? [])\n ];\n stream = new MediaStream(tracks);\n } else if (this.options.propose === 'screenshare') {\n logger.debug(\n '[LocalStreamController] Requesting display media for screen sharing with audio:',\n Boolean(this.options.inputAudioDeviceConstraints)\n );\n stream = await this.options.getDisplayMedia({\n video: true,\n audio: Boolean(this.options.inputAudioDeviceConstraints)\n });\n logger.debug('[LocalStreamController] Screen share media obtained:', stream);\n } else {\n const constraints: MediaStreamConstraints = {\n audio: this.options.inputAudioDeviceConstraints,\n video: this.options.inputVideoDeviceConstraints\n };\n logger.debug('[LocalStreamController] Requesting user media with constraints:', constraints);\n stream = await this.options.getUserMedia(constraints);\n logger.debug('[LocalStreamController] User media obtained:', stream);\n }\n this._localStream$.next(stream);\n return stream;\n }\n\n /**\n * Add a local media track to the local stream.\n * @param track - The MediaStreamTrack to add\n * @returns The MediaStream (either existing or newly created)\n */\n public addTrack(track: MediaStreamTrack): MediaStream {\n const localStream = this._localStream$.value ?? new MediaStream();\n\n track.addEventListener('ended', this.mediaTrackEndedHandler);\n localStream.addTrack(track);\n this._localStream$.next(localStream);\n\n if (track.kind === 'video') {\n this._localVideoTracks$.next(localStream.getVideoTracks());\n } else {\n this._localAudioTracks$.next(localStream.getAudioTracks());\n }\n\n logger.debug(`[LocalStreamController] ${track.kind} track added:`, track.id);\n return localStream;\n }\n\n /**\n * Remove a local media track from the local stream.\n * @param trackId - The ID of the track to remove\n * @returns The removed track, or undefined if not found\n */\n public removeTrack(trackId: string): MediaStreamTrack | undefined {\n const stream = this._localStream$.value;\n const track = stream?.getTracks().find((t: MediaStreamTrack) => t.id === trackId);\n\n if (!track) {\n logger.debug(`[LocalStreamController] track not found: ${trackId}`);\n return undefined;\n }\n\n track.removeEventListener('ended', this.mediaTrackEndedHandler);\n stream?.removeTrack(track);\n track.stop();\n this._localStream$.next(stream);\n\n if (track.kind === 'video') {\n this._localVideoTracks$.next(stream?.getVideoTracks() ?? []);\n } else {\n this._localAudioTracks$.next(stream?.getAudioTracks() ?? []);\n }\n\n logger.debug(`[LocalStreamController] ${track.kind} track removed:`, trackId);\n return track;\n }\n\n /**\n * Get or create a local stream.\n */\n public getOrCreateLocalStream(): MediaStream {\n return this._localStream$.value ?? new MediaStream();\n }\n\n /**\n * Set the local stream directly.\n */\n public setLocalStream(stream: MediaStream | null): void {\n this._localStream$.next(stream);\n }\n\n /**\n * Add a track ended event listener to a track.\n */\n public addTrackEndedListener(track: MediaStreamTrack): void {\n track.addEventListener('ended', this.mediaTrackEndedHandler);\n }\n\n /**\n * Update the controller options (e.g., when media overrides are applied).\n */\n public updateOptions(options: Partial<LocalStreamControllerOptions>): void {\n this.options = {\n ...this.options,\n ...options\n };\n }\n\n /**\n * Stop all local tracks and clean up.\n */\n public stopAllTracks(): void {\n const localStream = this._localStream$.value;\n localStream?.getTracks().forEach((track: MediaStreamTrack) => {\n logger.debug(`[LocalStreamController] Stopping local track: ${track.kind}`);\n track.removeEventListener('ended', this.mediaTrackEndedHandler);\n track.stop();\n });\n }\n\n /**\n * Clean up resources.\n */\n public override destroy(): void {\n this.stopAllTracks();\n super.destroy();\n }\n}\n","import { Destroyable } from '../behaviors/Destroyable';\nimport { MediaTrackError } from '../core/errors';\nimport { getLogger } from '../utils/logger';\n\nimport type { LocalStreamController } from './LocalStreamController';\nimport type { RTCPeerConnectionPropose } from '../core/types/call.types';\n\nconst logger = getLogger();\n\nconst getDirection = (send: boolean, recv: boolean): RTCRtpTransceiverDirection => {\n if (send && recv) {\n return 'sendrecv';\n } else if (send && !recv) {\n return 'sendonly';\n } else if (!send && recv) {\n return 'recvonly';\n }\n\n return 'inactive';\n};\n\nexport interface TransceiverControllerOptions {\n peerConnection: RTCPeerConnection;\n propose: RTCPeerConnectionPropose;\n simulcast?: boolean;\n sfu?: boolean;\n msStreamsNumber?: number;\n receiveAudio?: boolean;\n receiveVideo?: boolean;\n localStreamController: LocalStreamController;\n getInputAudioDeviceConstraints: () => MediaTrackConstraints | boolean;\n getInputVideoDeviceConstraints: () => MediaTrackConstraints | boolean;\n getUserMedia: (constraints: MediaStreamConstraints) => Promise<MediaStream>;\n onError?: (error: Error) => void;\n}\n\nexport class TransceiverController extends Destroyable {\n private peerConnection: RTCPeerConnection;\n private options: TransceiverControllerOptions;\n\n constructor(options: TransceiverControllerOptions) {\n super();\n this.peerConnection = options.peerConnection;\n this.options = options;\n }\n\n public get useAddTransceivers(): boolean {\n return typeof this.peerConnection.addTransceiver === 'function';\n }\n\n public get useAddTrack(): boolean {\n return typeof this.peerConnection.addTrack === 'function';\n }\n\n public get useAddStream(): boolean {\n return (\n // @ts-expect-error -- Ignore ---\n typeof this.peerConnection.addStream === 'function' &&\n !this.useAddTransceivers &&\n !this.useAddTrack\n );\n }\n\n private get propose(): RTCPeerConnectionPropose {\n return this.options.propose;\n }\n\n private get isAdditionalDevice(): boolean {\n return this.propose === 'additional-device';\n }\n\n private get isScreenShare(): boolean {\n return this.propose === 'screenshare';\n }\n\n private get isSimulcast(): boolean {\n return Boolean(this.options.simulcast);\n }\n\n private get isSFU(): boolean {\n return Boolean(this.options.sfu);\n }\n\n private get receiveVideo(): boolean {\n return Boolean(this.options.receiveVideo);\n }\n\n private get receiveAudio(): boolean {\n return Boolean(this.options.receiveAudio);\n }\n\n private get localStream(): MediaStream | null {\n return this.options.localStreamController.localStream;\n }\n\n private get inputAudioDeviceConstraints(): MediaTrackConstraints | boolean {\n return this.options.getInputAudioDeviceConstraints();\n }\n\n private get inputVideoDeviceConstraints(): MediaTrackConstraints | boolean {\n return this.options.getInputVideoDeviceConstraints();\n }\n\n public get audioDirection(): RTCRtpTransceiverDirection {\n if (this.isAdditionalDevice) {\n return 'sendonly';\n }\n const { localStream } = this;\n const hasAudioTrack = localStream?.getAudioTracks().some((track) => track.enabled);\n const wantsToSendAudio = Boolean(this.inputAudioDeviceConstraints);\n const wantsToReceiveAudio = Boolean(this.receiveAudio);\n const send = hasAudioTrack ?? wantsToSendAudio;\n const recv = wantsToReceiveAudio;\n return getDirection(send, recv);\n }\n\n public get videoDirection(): RTCRtpTransceiverDirection {\n if (this.isAdditionalDevice || this.isScreenShare) {\n return 'sendonly';\n }\n\n if (this.isSFU) {\n return 'recvonly';\n }\n\n const { localStream } = this;\n const hasVideoTrack = localStream?.getVideoTracks().some((track) => track.enabled);\n const wantsToSendVideo = Boolean(this.inputVideoDeviceConstraints);\n const wantsToReceiveVideo = Boolean(this.receiveVideo);\n const send = hasVideoTrack ?? wantsToSendVideo;\n const recv = wantsToReceiveVideo;\n return getDirection(send, recv);\n }\n\n private get sendEncodings(): RTCRtpEncodingParameters[] | undefined {\n if (!this.isSimulcast) {\n return undefined;\n }\n\n return ['0', '1', '2'].map((rid) => ({\n active: true,\n rid,\n scaleResolutionDownBy: Number(rid) * 6 || 1.0\n }));\n }\n\n private getConstraintsFor(kind: 'audio' | 'video'): MediaTrackConstraints {\n const constraints =\n kind === 'audio' ? this.inputAudioDeviceConstraints : this.inputVideoDeviceConstraints;\n\n // If constraints is a boolean (true/false), return empty object for track constraints\n // false case means no media of this kind is requested, but that's handled elsewhere\n return typeof constraints === 'boolean' ? {} : constraints;\n }\n\n public transceiverByKind(kind: 'audio' | 'video' | 'both'): RTCRtpTransceiver[] {\n return this.peerConnection\n .getTransceivers()\n .filter((t) => kind === 'both' || t.receiver.track.kind === kind);\n }\n\n public get audioTransceivers(): RTCRtpTransceiver[] {\n return this.transceiverByKind('audio');\n }\n\n public get videoTransceivers(): RTCRtpTransceiver[] {\n return this.transceiverByKind('video');\n }\n\n public async setupTransceiverSender(\n track: MediaStreamTrack,\n localStream: MediaStream,\n transceiver?: RTCRtpTransceiver\n ): Promise<void> {\n const isAudio = track.kind === 'audio';\n const direction = isAudio ? this.audioDirection : this.videoDirection;\n const transceiverParams: RTCRtpTransceiverInit = {\n direction,\n sendEncodings: isAudio ? undefined : this.sendEncodings,\n streams: direction === 'recvonly' ? undefined : [localStream]\n };\n logger.debug(\n `[TransceiverController] Setting up transceiver sender for local ${track.kind} track:`,\n { transceiver, transceiverParams }\n );\n if (\n transceiverParams.direction &&\n ['sendonly', 'sendrecv'].includes(transceiverParams.direction)\n ) {\n if (transceiver) {\n await transceiver.sender.replaceTrack(track);\n // eslint-disable-next-line no-param-reassign\n transceiver.direction = transceiverParams.direction;\n if (transceiverParams.streams?.some((stream) => Boolean(stream))) {\n logger.debug(\n `[TransceiverController] Setting streams for transceiver sender for local ${track.kind} track:`,\n transceiverParams.streams\n );\n transceiver.sender.setStreams(...transceiverParams.streams);\n }\n } else {\n logger.debug(\n `[TransceiverController] Adding new transceiver for local ${track.kind} track:`,\n track.id\n );\n this.peerConnection.addTransceiver(track, transceiverParams);\n }\n }\n }\n\n public stopTrackSender(\n kind: 'audio' | 'video' | 'both',\n options = { updateTransceiverDirection: false }\n ): void {\n try {\n const transceivers = this.transceiverByKind(kind);\n for (const transceiver of transceivers) {\n if (transceiver.sender.track?.readyState === 'live') {\n const trackId = transceiver.sender.track.id;\n transceiver.sender.track.stop();\n this.options.localStreamController.removeTrack(trackId);\n if (options.updateTransceiverDirection) {\n transceiver.direction = 'inactive';\n }\n }\n }\n } catch (error) {\n logger.error('[TransceiverController] stopTrackSender error', kind, error);\n this.options.onError?.(new MediaTrackError('stopTrackSender', kind, error));\n }\n }\n\n public async restoreTrackSender(kind: 'audio' | 'video' | 'both'): Promise<void> {\n try {\n logger.debug('[TransceiverController] restoreTrackSender called', kind);\n const constraints: MediaStreamConstraints = {};\n const transceivers = this.transceiverByKind(kind);\n for (const transceiver of transceivers) {\n const { track } = transceiver.sender;\n // Check if track is null, ended - all need restoration\n const needsRestore = !track || track.readyState === 'ended';\n if (needsRestore) {\n const trackKind = track?.kind ?? transceiver.receiver.track.kind;\n if (trackKind === 'audio' || trackKind === 'video') {\n constraints[trackKind] = this.getConstraintsFor(trackKind);\n }\n }\n }\n\n logger.debug('[TransceiverController] restoreTrackSender constraints:', constraints);\n\n // Don't call getUserMedia if no tracks need restoration\n if (Object.keys(constraints).length === 0) {\n logger.warn('[TransceiverController] restoreTrackSender: no tracks need restoration', kind);\n return;\n }\n\n const stream = await this.options.getUserMedia(constraints);\n const newTracks = stream.getTracks();\n\n logger.debug('[TransceiverController] restoreTrackSender new tracks:', newTracks);\n for (const newTrack of newTracks) {\n this.options.localStreamController.addTrack(newTrack);\n const trackKind = newTrack.kind as 'audio' | 'video';\n const transceiverOfKind = this.transceiverByKind(trackKind)[0];\n transceiverOfKind.direction =\n trackKind === 'audio' ? this.audioDirection : this.videoDirection;\n logger.debug(\n '[TransceiverController] restoreTrackSender setting direction for',\n trackKind,\n transceiverOfKind.direction\n );\n await transceiverOfKind.sender.replaceTrack(newTrack);\n }\n } catch (error) {\n logger.error('[TransceiverController] restoreTrackSender error', kind, error);\n this.options.onError?.(new MediaTrackError('restoreTrackSender', kind, error));\n }\n }\n\n public async replaceSenderTrack(kind: 'audio' | 'video', track: MediaStreamTrack): Promise<void> {\n const transceivers = kind === 'audio' ? this.audioTransceivers : this.videoTransceivers;\n for (const transceiver of transceivers) {\n await transceiver.sender.replaceTrack(track);\n }\n }\n\n public async setupRemoteTransceivers(type: 'offer' | 'answer'): Promise<void> {\n if (type === 'answer') {\n // remote setup was made by the offerer\n return;\n }\n\n for (const kind of ['audio', 'video']) {\n const transceivers = kind === 'audio' ? this.audioTransceivers : this.videoTransceivers;\n for (const transceiver of transceivers) {\n const direction = kind === 'audio' ? this.audioDirection : this.videoDirection;\n\n if (['inactive', 'recvonly'].includes(direction)) {\n transceiver.direction = direction;\n await transceiver.sender.replaceTrack(null);\n transceiver.sender.setStreams();\n }\n }\n }\n\n if (this.videoDirection === 'recvonly' && this.isSFU && this.useAddTransceivers) {\n const { msStreamsNumber = 5 } = this.options;\n for (let i = 0; i < Number(msStreamsNumber); i++) {\n this.peerConnection.addTransceiver('video', { direction: 'recvonly' });\n }\n }\n }\n\n public async updateSendersConstraints(\n kind: 'audio' | 'video',\n constraints?: MediaTrackConstraints\n ): Promise<void> {\n if (!constraints) {\n this.stopTrackSender(kind);\n return Promise.resolve();\n }\n\n const senders = this.peerConnection\n .getSenders()\n .filter((sender) => sender.track?.kind === kind && sender.track.readyState === 'live');\n\n for (const sender of senders) {\n const { track } = sender;\n if (track) {\n const constraintsToApply: MediaTrackConstraints = {\n ...track.getConstraints(),\n ...constraints\n };\n try {\n await track.applyConstraints(constraintsToApply);\n logger.debug(\n `[TransceiverController] Updated ${kind} sender constraints:`,\n constraintsToApply\n );\n logger.debug(\n `[TransceiverController] Updated ${kind} sender constraints:`,\n track.getConstraints()\n );\n } catch (error) {\n logger.warn(\n `[TransceiverController] Failed to apply constraints to ${kind} track ${track.id}:`,\n error\n );\n this.options.onError?.(new MediaTrackError('updateSendersConstraints', kind, error));\n }\n }\n }\n }\n\n public getMediaDirections(): {\n audio: RTCRtpTransceiverDirection;\n video: RTCRtpTransceiverDirection;\n } {\n if (this.peerConnection.connectionState === 'connected') {\n // If we are connected let's get the actual directions from the transceivers\n return this.peerConnection.getTransceivers().reduce(\n (acc, transceiver) => {\n return {\n ...acc,\n [transceiver.receiver.track.kind]: transceiver.direction\n };\n },\n { audio: 'inactive', video: 'inactive' }\n );\n }\n\n return {\n audio: this.audioDirection,\n video: this.videoDirection\n };\n }\n\n public updatePeerConnection(peerConnection: RTCPeerConnection): void {\n this.peerConnection = peerConnection;\n }\n\n public updateOptions(options: Partial<TransceiverControllerOptions>): void {\n this.options = {\n ...this.options,\n ...options\n };\n }\n}\n","/* eslint-disable max-lines */\nimport {\n auditTime,\n defer,\n exhaustMap,\n filter,\n from,\n map,\n shareReplay,\n switchMap,\n takeUntil,\n tap,\n skipWhile,\n merge\n} from 'rxjs';\nimport { v4 as uuid } from 'uuid';\n\nimport { ICEGatheringController } from './ICEGatheringController';\nimport { LocalStreamController } from './LocalStreamController';\nimport { TransceiverController } from './TransceiverController';\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { PreferencesContainer } from '../containers/PreferencesContainer';\nimport { DependencyError } from '../core/errors';\nimport { extractMediaDirectionsFromSDP, isValidLocalDescription } from '../helpers/SDPHelper';\nimport { filterNull } from '../operators';\nimport { getLogger } from '../utils/logger';\n\nimport type { RTCPeerConnectionPropose, RTCPeerConnectionType } from '../core/types/call.types';\nimport type { MediaDirections, MediaOptions } from '../core/types/media.types';\nimport type { WebRTCApiProvider } from '../dependencies/interfaces';\nimport type { DeviceController } from '../interfaces/DeviceController';\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\nexport interface RTCPeerConnectionControllerOptions extends MediaOptions {\n callId?: string;\n rtcConfiguration?: RTCConfiguration;\n simulcast?: boolean;\n sfu?: boolean;\n msStreamsNumber?: number;\n propose: RTCPeerConnectionPropose;\n iceServers?: RTCIceServer[];\n disableUdpIceServers?: boolean;\n relayOnly?: boolean;\n iceCandidateTimeout?: number;\n iceGatheringTimeout?: number;\n webRTCApiProvider?: WebRTCApiProvider;\n}\n\nexport type RTCPeerConnectionControllerOptionsPartial = Partial<RTCPeerConnectionControllerOptions>;\n\nexport interface UpdateSDPStatusParams {\n status: 'received' | 'sent' | 'failed';\n sdp?: string;\n}\n\nexport class RTCPeerConnectionController extends Destroyable {\n public readonly id: string;\n public firstSDPExchangeCompleted = false;\n public sdpInit?: RTCSessionDescriptionInit;\n private negotiationNeeded$ = this.createSubject<void>();\n private deviceController: DeviceController;\n private localStreamController: LocalStreamController;\n // Transceiver controller - initialized lazily when peerConnection is created\n private transceiverController?: TransceiverController;\n public readonly localDescription$: Observable<RTCSessionDescription | null> = defer(() =>\n from(this.init())\n ).pipe(\n switchMap(() =>\n // Wait for ICE negotiation before emitting localDescription\n this.iceGatheringController.iceCandidatesState$.pipe(\n filter((iceCandidateState) => !['new', 'gathering'].includes(iceCandidateState)),\n tap(() => {\n this.negotiationEnded();\n }),\n // Only emit when signaling state is 'have-local-offer'\n filter(() => this.shouldEmitLocalDescription),\n map(() => this.peerConnection?.localDescription),\n filterNull(),\n tap((desc) => {\n if (desc.type === 'answer') {\n // Once the answer is emitted, switch type to offer for future negotiations\n this._type = 'offer';\n }\n })\n )\n ),\n shareReplay(1),\n takeUntil(this.destroyed$)\n );\n public peerConnection?: RTCPeerConnection;\n private initPromise?: Promise<void>;\n private connectionTimeout = 3_000;\n private connectionTimer?: ReturnType<typeof setTimeout>;\n private oniceconnectionstatechangeHandler = () => {\n if (this.peerConnection) {\n const { iceConnectionState } = this.peerConnection;\n logger.debug(\n `[RTCPeerConnectionController] ICE connection state changed to: ${iceConnectionState}`\n );\n this._iceConnectionState$.next(this.peerConnection.iceConnectionState);\n }\n };\n private onconnectionstatechangeHandler = () => {\n if (this.peerConnection) {\n const { connectionState } = this.peerConnection;\n logger.debug(`[RTCPeerConnectionController] Connection state changed to: ${connectionState}`);\n if (connectionState === 'connected') {\n this.removeConnectionTimer();\n }\n this._connectionState$.next(this.peerConnection.connectionState);\n }\n };\n private onsignalingstatechangeHandler = () => {\n logger.debug(\n `[RTCPeerConnectionController] Signaling state changed to: ${this.peerConnection?.signalingState}`\n );\n };\n private onicegatheringstatechangeHandler = () => {\n if (this.peerConnection) {\n this._iceGatheringState$.next(this.peerConnection.iceGatheringState);\n }\n };\n private onnegotiationneededHandler = (event: unknown) => {\n logger.debug('[RTCPeerConnectionController] Negotiation needed event received.', event);\n this.negotiationNeeded$.next();\n };\n private updateSelectedInputDevice = async (\n kind: 'audio' | 'video',\n deviceInfo: MediaDeviceInfo | null\n ): Promise<void> => {\n try {\n const { localStream } = this;\n if (!localStream) {\n logger.warn(\n '[RTCPeerConnectionController] No local stream available to update input device.'\n );\n return;\n }\n\n logger.debug(\n `[RTCPeerConnectionController] Updating selected ${kind} input device:`,\n localStream.getTracks()\n );\n // Stop existing audio tracks\n const track = localStream.getTracks().find((track: MediaStreamTrack) => track.kind === kind);\n\n if (track) {\n this.transceiverController?.stopTrackSender(kind);\n this.localStream?.removeTrack(track);\n logger.debug(\n `[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`,\n localStream.getTracks()\n );\n\n if (!deviceInfo) {\n logger.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);\n return;\n }\n\n const stream = await this.getUserMedia({\n [kind]: {\n ...track.getConstraints(),\n ...this.deviceController.deviceInfoToConstraints(deviceInfo)\n }\n });\n\n const streamTrack = stream.getTracks().find((t) => t.kind === kind);\n\n if (streamTrack) {\n logger.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);\n this.localStream?.addTrack(streamTrack);\n await this.transceiverController?.replaceSenderTrack(kind, streamTrack);\n logger.debug(\n `[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`,\n this.localStream?.getTracks()\n );\n }\n }\n\n logger.debug(\n `[RTCPeerConnectionController] ${kind} input device selected:`,\n deviceInfo?.label\n );\n } catch (error) {\n logger.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);\n this._errors$.next(error as Error);\n throw error;\n }\n };\n private _isNegotiating$ = this.createBehaviorSubject<boolean>(false);\n private _iceGatheringController?: ICEGatheringController;\n private _memberId: string | null = null;\n private _type: RTCPeerConnectionType;\n // Observable state streams - exposed as public observables\n private _iceConnectionState$ = this.createReplaySubject<RTCIceConnectionState>(1);\n private _connectionState$ = this.createReplaySubject<RTCPeerConnectionState>(1);\n private _signalingState$ = this.createReplaySubject<RTCSignalingState>(1);\n private _iceGatheringState$ = this.createReplaySubject<RTCIceGatheringState>(1);\n // Error stream\n private _errors$ = this.createReplaySubject<Error>(1);\n // ICE candidates stream\n private _iceCandidates$ = this.createReplaySubject<RTCIceCandidate[]>(1);\n // Initialization state\n private _initialized$ = this.createReplaySubject<boolean>(1);\n // Remote description\n private _remoteDescription$ = this.createReplaySubject<RTCSessionDescription | null>(1);\n private _remoteStream$ = this.createBehaviorSubject<MediaStream | null>(null);\n private _remoteOfferMediaDirections: MediaDirections | null = null;\n constructor(\n protected options: RTCPeerConnectionControllerOptionsPartial = {},\n remoteSessionDescription?: string,\n deviceController?: DeviceController\n ) {\n super();\n this.deviceController = deviceController ?? ({} as DeviceController);\n this.id = options.callId ?? uuid();\n this._type = remoteSessionDescription ? 'answer' : 'offer';\n\n this.sdpInit = remoteSessionDescription\n ? {\n type: 'offer',\n sdp: remoteSessionDescription\n }\n : undefined;\n\n this._remoteOfferMediaDirections = remoteSessionDescription\n ? extractMediaDirectionsFromSDP(remoteSessionDescription)\n : null;\n\n // For inbound calls, derive send/receive defaults from the remote offer directions.\n // Remote 'sendrecv' → remote sends (we receive) AND remote receives (we send)\n // Remote 'sendonly' → remote sends (we receive) but doesn't receive (we don't send)\n // Remote 'recvonly' → remote receives (we send) but doesn't send (we don't receive)\n const offerDefaults = this._remoteOfferMediaDirections\n ? {\n audio: this._remoteOfferMediaDirections.audio.includes('recv'),\n video: this._remoteOfferMediaDirections.video.includes('recv'),\n receiveAudio: this._remoteOfferMediaDirections.audio.includes('send'),\n receiveVideo: this._remoteOfferMediaDirections.video.includes('send')\n }\n : {};\n\n this.options = {\n ...options,\n audio: options.audio ?? offerDefaults.audio,\n video: options.video ?? offerDefaults.video,\n receiveAudio:\n options.receiveAudio ??\n offerDefaults.receiveAudio ??\n PreferencesContainer.instance.receiveAudio,\n receiveVideo:\n options.receiveVideo ??\n offerDefaults.receiveVideo ??\n PreferencesContainer.instance.receiveVideo\n };\n\n // Initialize the local stream controller\n this.localStreamController = new LocalStreamController({\n propose: this.propose,\n inputAudioStream: this.options.inputAudioStream,\n inputVideoStream: this.options.inputVideoStream,\n inputAudioDeviceConstraints: this.inputAudioDeviceConstraints,\n inputVideoDeviceConstraints: this.inputVideoDeviceConstraints,\n getUserMedia: async (constraints: MediaStreamConstraints) => this.getUserMedia(constraints),\n getDisplayMedia: async (options: DisplayMediaStreamOptions) => this.getDisplayMedia(options)\n });\n }\n\n private get iceGatheringController(): ICEGatheringController {\n if (!this._iceGatheringController) {\n throw new DependencyError('ICEGatheringController is not initialized');\n }\n return this._iceGatheringController;\n }\n\n private get shouldEmitLocalDescription(): boolean {\n if (!this.peerConnection) {\n return false;\n }\n\n const { localDescription, signalingState } = this.peerConnection;\n\n if (!localDescription || !isValidLocalDescription(localDescription.sdp)) {\n return false;\n }\n\n return (\n (localDescription.type === 'offer' && signalingState === 'have-local-offer') ||\n (localDescription.type === 'answer' && signalingState === 'stable')\n );\n }\n\n private removeConnectionTimer(): void {\n if (this.connectionTimer) {\n clearTimeout(this.connectionTimer);\n this.connectionTimer = undefined;\n }\n }\n\n public setMemberId(memberId: string | null): void {\n this._memberId = memberId;\n }\n\n public get memberId(): string | null {\n return this._memberId;\n }\n\n public stopTrackSender(\n kind: 'audio' | 'video' | 'both',\n options = { updateTransceiverDirection: false }\n ): void {\n this.transceiverController?.stopTrackSender(kind, options);\n }\n\n public get isNegotiating$(): Observable<boolean> {\n return this._isNegotiating$.asObservable();\n }\n\n public get isNegotiating(): boolean {\n return this._isNegotiating$.value;\n }\n\n public updateMediaDevicesOptions(options: MediaOptions): void {\n this.options = {\n ...this.options,\n ...options\n };\n }\n\n public get iceGatheringState$(): Observable<RTCIceGatheringState> {\n return this.cachedObservable('iceGatheringState$', () =>\n this._iceGatheringState$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get mediaTrackEnded$(): Observable<MediaStreamTrack> {\n return this.cachedObservable('mediaTrackEnded$', () =>\n this.localStreamController.mediaTrackEnded$.pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get errors$(): Observable<Error> {\n return this.cachedObservable('errors$', () =>\n this._errors$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get iceCandidates$(): Observable<RTCIceCandidate[]> {\n return this.cachedObservable('iceCandidates$', () =>\n this._iceCandidates$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get initialized$(): Observable<boolean> {\n return this.cachedObservable('initialized$', () =>\n this._initialized$.asObservable().pipe(\n filter((initialized) => initialized),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n public get remoteDescription$(): Observable<RTCSessionDescription | null> {\n return this.cachedObservable('remoteDescription$', () =>\n this._remoteDescription$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get localStream$(): Observable<MediaStream | null> {\n return this.cachedObservable('localStream$', () =>\n this.localStreamController.localStream$.pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get remoteStream$(): Observable<MediaStream | null> {\n return this.cachedObservable('remoteStream$', () =>\n this._remoteStream$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get localAudioTracks$(): Observable<MediaStreamTrack[]> {\n return this.cachedObservable('localAudioTracks$', () =>\n this.localStreamController.localAudioTracks$.pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get localVideoTracks$(): Observable<MediaStreamTrack[]> {\n return this.cachedObservable('localVideoTracks$', () =>\n this.localStreamController.localVideoTracks$.pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get iceConnectionState$(): Observable<RTCIceConnectionState> {\n return this.cachedObservable('iceConnectionState$', () =>\n this._iceConnectionState$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get connectionState$(): Observable<RTCPeerConnectionState> {\n return this.cachedObservable('connectionState$', () =>\n this._connectionState$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get signalingState$(): Observable<RTCSignalingState> {\n return this.cachedObservable('signalingState$', () =>\n this._signalingState$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get type(): RTCPeerConnectionType {\n return this._type;\n }\n\n public get propose(): RTCPeerConnectionPropose {\n return this.options.propose ?? 'main';\n }\n\n public get isAdditionalDevice(): boolean {\n return this.propose === 'additional-device';\n }\n\n public get isMainDevice(): boolean {\n return this.propose === 'main';\n }\n\n public get isScreenShare(): boolean {\n return this.propose === 'screenshare';\n }\n\n protected get iceServers(): RTCIceServer[] {\n if (!this.options.disableUdpIceServers) {\n return this.options.iceServers ?? [];\n }\n const tcpTransportParam = 'transport=tcp';\n\n // When disabling UDP, keep only URLs that explicitly specify transport=tcp\n // URLs without a transport parameter default to UDP and should be filtered out\n return (this.options.iceServers ?? []).map((server) => {\n const urls = Array.isArray(server.urls) ? server.urls : [server.urls];\n return {\n ...server,\n urls: urls.filter((url) => url.includes(tcpTransportParam))\n } as RTCIceServer;\n });\n }\n\n private get rtcConfiguration(): RTCConfiguration {\n // Destructure to exclude iceServers from options spread, since we use the filtered this.iceServers\n const { iceServers: _iceServers, ...restOptions } = this.options;\n return {\n bundlePolicy: 'max-compat',\n iceCandidatePoolSize: 10,\n iceServers: this.iceServers,\n iceTransportPolicy: this.options.relayOnly ? 'relay' : 'all',\n //@ts-expect-error -- Ignore ---\n sdpSemantics: 'unified-plan',\n ...restOptions\n };\n }\n\n public get receiveVideo(): boolean {\n return Boolean(this.options.receiveVideo);\n }\n\n public get receiveAudio(): boolean {\n return Boolean(this.options.receiveAudio);\n }\n\n public get localStream(): MediaStream | null {\n return this.localStreamController.localStream;\n }\n\n public get remoteStream(): MediaStream | null {\n return this._remoteStream$.value;\n }\n\n private get inputAudioDeviceConstraints(): MediaTrackConstraints | boolean {\n if (this.options.audio === false && !this.options.inputAudioDeviceConstraints) {\n return false;\n }\n return {\n ...this.options.inputAudioDeviceConstraints,\n ...this.deviceController.selectedAudioInputDeviceConstraints\n };\n }\n\n private get inputVideoDeviceConstraints(): MediaTrackConstraints | boolean {\n if (!this.options.video && !this.options.inputVideoDeviceConstraints) {\n return false;\n }\n return {\n ...this.options.inputVideoDeviceConstraints,\n ...this.deviceController.selectedVideoInputDeviceConstraints\n };\n }\n\n private get WebRTCPeerConnectionConstructor(): typeof RTCPeerConnection {\n return this.options.webRTCApiProvider?.RTCPeerConnection ?? RTCPeerConnection;\n }\n\n private get offerOptions(): RTCOfferOptions {\n const options: RTCOfferOptions = {\n iceRestart: this.firstSDPExchangeCompleted ? true : undefined\n };\n switch (this.propose) {\n case 'screenshare':\n case 'additional-device':\n return {\n ...options,\n offerToReceiveAudio: false,\n offerToReceiveVideo: false\n };\n case 'main':\n default:\n return {\n ...options,\n offerToReceiveAudio: true,\n offerToReceiveVideo:\n this.options.receiveVideo ?? Boolean(this.inputVideoDeviceConstraints)\n };\n }\n }\n\n private get answerOptions(): RTCAnswerOptions {\n return {\n iceRestart: this.firstSDPExchangeCompleted ? true : undefined\n };\n }\n\n /**\n * Initialize the RTCPeerConnection and setup event listeners.\n * Called automatically when localDescription$ is subscribed to (deferred pattern).\n * Uses Promise memoization to ensure initialization only happens once,\n * even if called concurrently.\n */\n private async init(): Promise<void> {\n this.initPromise ??= this.doInit();\n return this.initPromise;\n }\n\n /**\n * Internal initialization implementation.\n * Should only be called via init() to ensure single execution.\n */\n private async doInit(): Promise<void> {\n try {\n this.setupPeerConnection();\n\n this.subscribeTo(\n this.negotiationNeeded$.pipe(\n auditTime(0), //When updating multiple tracks, batches all the events together\n exhaustMap(async () => this.startNegotiation()) // Ignore new events while negotiation is ongoing\n ),\n {\n next: () => {\n logger.debug('[RTCPeerConnectionController] Start Negotiation completed successfully');\n },\n error: (error) => {\n logger.error('[RTCPeerConnectionController] Start Negotiation error:', error);\n this._errors$.next(error as Error);\n }\n }\n );\n\n this.subscribeTo(\n merge(\n this.deviceController.selectedAudioInputDevice$.pipe(\n map((deviceInfo) => ['audio', deviceInfo] as const)\n ),\n this.deviceController.selectedVideoInputDevice$.pipe(\n map((deviceInfo) => ['video', deviceInfo] as const)\n )\n ).pipe(\n // we only want to react to changes after the localstream is created\n // before that the device selection is handle int the localstream creation\n skipWhile(() => !this.localStreamController.localStream)\n ),\n async ([kind, deviceInfo]) => {\n logger.debug(`[RTCPeerConnectionController] Selected input device changed for:`, {\n kind,\n deviceInfo\n });\n await this.updateSelectedInputDevice(kind, deviceInfo);\n }\n );\n\n // For inbound calls: only setup remote tracks (ontrack handler) during init.\n // Local track setup is deferred to acceptInbound() so that:\n // 1. Remote description is set first, creating transceivers from the offer\n // 2. Local tracks reuse those transceivers instead of creating duplicates\n // 3. Media overrides can be applied before tracks are acquired\n if (this.type === 'answer' && this.sdpInit) {\n await this.setupRemoteTracks();\n\n this._initialized$.next(true);\n\n this.setupEventListeners();\n this._isNegotiating$.next(true);\n await this._setRemoteDescription(this.sdpInit);\n } else {\n await this.setupTrackHandling();\n\n this._initialized$.next(true);\n }\n } catch (error) {\n logger.error('[RTCPeerConnectionController] Initialization error:', error);\n this._errors$.next(error as Error);\n this.destroy();\n }\n }\n\n private setupPeerConnection() {\n this.peerConnection = new this.WebRTCPeerConnectionConstructor(this.rtcConfiguration);\n this.peerConnection.addEventListener('negotiationneeded', this.onnegotiationneededHandler);\n this._iceGatheringController = new ICEGatheringController(\n this.peerConnection,\n this.isNegotiating$,\n {\n iceCandidateTimeout: this.options.iceCandidateTimeout,\n iceGatheringTimeout: this.options.iceGatheringTimeout,\n relayOnly: this.options.relayOnly\n }\n );\n\n // Initialize the transceiver controller\n this.transceiverController = new TransceiverController({\n peerConnection: this.peerConnection,\n propose: this.propose,\n simulcast: this.options.simulcast,\n sfu: this.options.sfu,\n msStreamsNumber: this.options.msStreamsNumber,\n receiveAudio: this.receiveAudio,\n receiveVideo: this.receiveVideo,\n localStreamController: this.localStreamController,\n getInputAudioDeviceConstraints: () => this.inputAudioDeviceConstraints,\n getInputVideoDeviceConstraints: () => this.inputVideoDeviceConstraints,\n getUserMedia: async (constraints: MediaStreamConstraints) => this.getUserMedia(constraints),\n onError: (error: Error) => {\n this._errors$.next(error);\n }\n });\n }\n\n private async startNegotiation() {\n if (this.isNegotiating) {\n logger.debug('[RTCPeerConnectionController] Negotiation already in progress, skipping.');\n return;\n }\n\n this.setupEventListeners();\n\n if (this.type === 'answer') {\n logger.debug(\n '[RTCPeerConnectionController] This is an answer type still, skipping offer creation.'\n );\n return;\n }\n\n this._isNegotiating$.next(true);\n logger.debug('[RTCPeerConnectionController] Starting negotiation.');\n\n try {\n const { offerOptions } = this;\n logger.debug('[RTCPeerConnectionController] Creating offer with options:', offerOptions);\n await this.createOffer(offerOptions);\n } catch (error) {\n logger.error('[RTCPeerConnectionController] Error during negotiation:', error);\n this._errors$.next(error as Error);\n }\n }\n\n /**\n * Create an SDP offer and set it as local description.\n */\n private async createOffer(options?: RTCOfferOptions): Promise<void> {\n if (!this.peerConnection) {\n throw new DependencyError('RTCPeerConnection is not initialized');\n }\n\n const offer = await this.peerConnection.createOffer(options);\n await this.setLocalDescription(offer);\n\n // Note: localDescription will be emitted by setupEventListeners initial emission\n // and updated when ICE gathering state changes\n }\n\n public async updateAnswerStatus({ status, sdp }: UpdateSDPStatusParams): Promise<void> {\n let readyToConnect = status !== 'failed';\n\n try {\n if (status === 'received' && sdp) {\n logger.debug('[RTCPeerConnectionController] Received answer SDP:', sdp);\n await this._setRemoteDescription({\n type: 'answer',\n sdp\n });\n }\n } catch (error) {\n logger.error('[RTCPeerConnectionController] Error updating answer status:', error);\n this._errors$.next(error as Error);\n readyToConnect = false;\n } finally {\n if (readyToConnect) {\n this.readyToConnect();\n } else {\n this.iceGatheringController.restartICEGatheringWithRelayOnly();\n }\n }\n }\n\n public async updateOfferStatus({ status, sdp }: UpdateSDPStatusParams): Promise<void> {\n switch (status) {\n case 'received':\n this._type = 'answer';\n this.sdpInit = {\n type: 'offer',\n sdp: sdp\n };\n await this.handleOfferReceived();\n break;\n case 'failed':\n logger.error('[RTCPeerConnectionController] Offer failed to be processed by remote.');\n break;\n case 'sent':\n default:\n // No action needed for sent offers\n }\n }\n\n /**\n * Accept an inbound call by creating the SDP answer.\n * Optionally override media options before the answer is generated.\n * Must be called after initialization for inbound (answer-type) connections.\n */\n public async acceptInbound(mediaOverrides?: MediaOptions): Promise<void> {\n if (mediaOverrides) {\n const { audio, video, receiveAudio, receiveVideo } = mediaOverrides;\n this.options = {\n ...this.options,\n ...(audio !== undefined ? { audio } : {}),\n ...(video !== undefined ? { video } : {}),\n ...(receiveAudio !== undefined ? { receiveAudio } : {}),\n ...(receiveVideo !== undefined ? { receiveVideo } : {})\n };\n this.transceiverController?.updateOptions({\n receiveAudio: this.receiveAudio,\n receiveVideo: this.receiveVideo\n });\n this.localStreamController.updateOptions({\n inputAudioDeviceConstraints: this.inputAudioDeviceConstraints,\n inputVideoDeviceConstraints: this.inputVideoDeviceConstraints\n });\n }\n\n // Setup local tracks after remote description is set and media overrides applied.\n // This ensures local tracks reuse transceivers from the remote offer\n // instead of creating duplicate transceivers via addTransceiver().\n await this.setupLocalTracks();\n\n const { answerOptions } = this;\n logger.debug(\n '[RTCPeerConnectionController] Creating inbound answer with options:',\n answerOptions\n );\n await this.createAnswer(answerOptions);\n }\n\n private async handleOfferReceived() {\n if (!this.sdpInit) {\n throw new DependencyError('SDP initialization parameters are not set');\n }\n\n this._isNegotiating$.next(true);\n await this._setRemoteDescription(this.sdpInit);\n\n const { answerOptions } = this;\n logger.debug('[RTCPeerConnectionController] Creating answer with options:', answerOptions);\n await this.createAnswer(answerOptions);\n }\n\n private readyToConnect() {\n this.firstSDPExchangeCompleted = true;\n this.connectionTimer = setTimeout(() => {\n this.removeConnectionTimer();\n if (this.peerConnection?.connectionState !== 'connected') {\n logger.debug(\n '[RTCPeerConnectionController] Connection timeout, restarting ICE gathering with relay only.'\n );\n this.iceGatheringController.restartICEGatheringWithRelayOnly();\n }\n }, this.connectionTimeout);\n }\n\n private async setRemoteDescriptionBefore(sdp: string = ''): Promise<string> {\n // TODO use the options hooks\n return Promise.resolve(sdp);\n }\n protected async setLocalDescription(params: RTCSessionDescriptionInit): Promise<void> {\n const finalLocal = await this.setLocalDescriptionBefore(params.sdp);\n return this.peerConnection?.setLocalDescription({\n ...params,\n sdp: finalLocal\n });\n }\n async setLocalDescriptionBefore(sdp: string = ''): Promise<string> {\n // TODO use the options hooks\n return Promise.resolve(sdp);\n }\n /**\n * Create an SDP answer and set it as local description.\n */\n private async createAnswer(options?: RTCAnswerOptions): Promise<void> {\n if (!this.peerConnection) {\n throw new DependencyError('RTCPeerConnection is not initialized');\n }\n\n const answer = await this.peerConnection.createAnswer(options);\n await this.setLocalDescription(answer);\n // Note: localDescription will be emitted by setupEventListeners initial emission\n // and updated when ICE gathering state changes\n }\n /**\n * Setup event listeners on RTCPeerConnection for state changes.\n */\n private setupEventListeners(): void {\n if (!this.peerConnection) {\n throw new DependencyError('RTCPeerConnection is not initialized');\n }\n\n // Emit initial states from the actual RTCPeerConnection\n this._iceConnectionState$.next(this.peerConnection.iceConnectionState);\n this._connectionState$.next(this.peerConnection.connectionState);\n this._signalingState$.next(this.peerConnection.signalingState);\n this._iceGatheringState$.next(this.peerConnection.iceGatheringState);\n // Note: localDescription is NOT emitted here - it will be emitted when ICE gathering completes\n this._remoteDescription$.next(this.peerConnection.remoteDescription);\n\n this.peerConnection.removeEventListener(\n 'icegatheringstatechange',\n this.onicegatheringstatechangeHandler\n );\n this.peerConnection.addEventListener(\n 'icegatheringstatechange',\n this.onicegatheringstatechangeHandler\n );\n this.peerConnection.removeEventListener(\n 'iceconnectionstatechange',\n this.oniceconnectionstatechangeHandler\n );\n this.peerConnection.addEventListener(\n 'iceconnectionstatechange',\n this.oniceconnectionstatechangeHandler\n );\n\n // Signaling state changes\n this.peerConnection.removeEventListener(\n 'connectionstatechange',\n this.onconnectionstatechangeHandler\n );\n this.peerConnection.addEventListener(\n 'connectionstatechange',\n this.onconnectionstatechangeHandler\n );\n\n this.peerConnection.removeEventListener(\n 'signalingstatechange',\n this.onsignalingstatechangeHandler\n );\n\n this.peerConnection.addEventListener(\n 'signalingstatechange',\n this.onsignalingstatechangeHandler\n );\n }\n\n private negotiationEnded() {\n this._isNegotiating$.next(false);\n }\n\n public restarIce(): void {\n this.peerConnection?.restartIce();\n }\n /**\n * Setup track handling for remote tracks.\n */\n private async setupTrackHandling(): Promise<void> {\n if (!this.peerConnection) {\n throw new DependencyError('RTCPeerConnection is not initialized');\n }\n\n await this.setupLocalTracks();\n\n await this.setupRemoteTracks();\n }\n\n // eslint-disable-next-line complexity\n private async setupLocalTracks(): Promise<void> {\n logger.debug('[RTCPeerConnectionController] Setting up local tracks/transceivers.');\n const localStream = this.localStream ?? (await this.localStreamController.buildLocalStream());\n\n if (this.transceiverController?.useAddStream ?? false) {\n logger.warn(\n '[RTCPeerConnectionController] Using deprecated addStream API to add local stream.'\n );\n //@ts-expect-error -- Ignore -- useAddStream checked if the deprecated API should be used\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n this.peerConnection?.addStream(localStream);\n // In case the browser doesn't fire negotiationneeded automatically\n if (!this.isNegotiating) {\n logger.debug(\n '[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.'\n );\n this.negotiationNeeded$.next();\n }\n return;\n }\n\n for (const kind of ['audio', 'video']) {\n const tracks = (\n kind === 'audio' ? localStream.getAudioTracks() : localStream.getVideoTracks()\n ).map((track, index) => ({ index, track }));\n for (const { index, track } of tracks) {\n this.localStreamController.addTrackEndedListener(track);\n if (this.transceiverController?.useAddTransceivers ?? false) {\n const transceivers =\n (kind === 'audio'\n ? this.transceiverController?.audioTransceivers\n : this.transceiverController?.videoTransceivers) ?? [];\n await this.transceiverController?.setupTransceiverSender(\n track,\n localStream,\n transceivers[index]\n );\n } else {\n logger.debug(\n `[RTCPeerConnectionController] Using addTrack for local ${kind} track:`,\n track.id\n );\n this.peerConnection?.addTrack(track, localStream);\n }\n }\n }\n }\n private async getUserMedia(constraints: MediaStreamConstraints): Promise<MediaStream> {\n const mediaDevices = this.options.webRTCApiProvider?.mediaDevices ?? navigator.mediaDevices;\n return mediaDevices.getUserMedia(constraints);\n }\n\n private async getDisplayMedia(options: DisplayMediaStreamOptions): Promise<MediaStream> {\n const mediaDevices = this.options.webRTCApiProvider?.mediaDevices ?? navigator.mediaDevices;\n if (!mediaDevices.getDisplayMedia) {\n throw new DependencyError('getDisplayMedia is not supported by the current WebRTC provider');\n }\n return mediaDevices.getDisplayMedia(options);\n }\n private async setupRemoteTracks(): Promise<void> {\n if (!this.peerConnection) {\n throw new DependencyError('RTCPeerConnection is not initialized');\n }\n this.peerConnection.ontrack = (event) => {\n logger.debug('[RTCPeerConnectionController] Remote track received:', event.track.kind);\n\n if (event.streams[0]) {\n this._remoteStream$.next(event.streams[0]);\n } else {\n const existingTracks = this._remoteStream$.value?.getTracks() ?? [];\n const newStream = new MediaStream([...existingTracks, event.track]);\n this._remoteStream$.next(newStream);\n }\n };\n\n await this.transceiverController?.setupRemoteTransceivers(this.type);\n }\n\n public async restoreTrackSender(kind: 'audio' | 'video' | 'both'): Promise<void> {\n await this.transceiverController?.restoreTrackSender(kind);\n }\n /**\n * Add a local media track to the peer connection.\n * @param track - The MediaStreamTrack to add\n */\n public addLocalTrack(track: MediaStreamTrack): void {\n if (!this.peerConnection) {\n const error = new DependencyError('RTCPeerConnection is not initialized');\n this._errors$.next(error);\n throw error;\n }\n\n try {\n // Add track to local stream controller\n const localStream = this.localStreamController.addTrack(track);\n\n // Add track to peer connection\n this.peerConnection.addTrack(track, localStream);\n\n logger.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);\n } catch (error) {\n logger.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);\n this._errors$.next(error as Error);\n throw error;\n }\n }\n /**\n * Remove a local media track from the peer connection.\n * @param trackId - The ID of the track to remove\n */\n public removeLocalTrack(trackId: string): void {\n if (!this.peerConnection) {\n const error = new DependencyError('RTCPeerConnection is not initialized');\n this._errors$.next(error);\n throw error;\n }\n\n const sender = this.peerConnection.getSenders().find((sender) => sender.track?.id === trackId);\n if (!sender) {\n logger.debug(`[RTCPeerConnectionController] track not found: ${trackId}`);\n return;\n }\n\n try {\n // Remove sender from peer connection\n this.peerConnection.removeTrack(sender);\n\n // Remove track from local stream controller\n this.localStreamController.removeTrack(trackId);\n\n logger.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);\n } catch (error) {\n logger.error(\n `[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`,\n error\n );\n this._errors$.next(error as Error);\n throw error;\n }\n }\n /**\n * Replace all existing media tracks with a new media track.\n * Convenience method for single-track scenarios.\n * @param track - The MediaStreamTrack to set\n */\n public setLocalTrack(track: MediaStreamTrack): void {\n // Remove all existing media tracks\n const existingTracks = [\n ...(track.kind === 'audio'\n ? this.localStreamController.localAudioTracks\n : this.localStreamController.localVideoTracks)\n ];\n for (const existingTrack of existingTracks) {\n this.removeLocalTrack(existingTrack.id);\n }\n\n // Add the new track\n this.addLocalTrack(track);\n }\n public async updateSendersConstraints(\n kind: 'audio' | 'video',\n constraints?: MediaTrackConstraints\n ): Promise<void> {\n await this.transceiverController?.updateSendersConstraints(kind, constraints);\n }\n\n /**\n * Clean up resources and close the peer connection.\n * Completes all observables to prevent memory leaks.\n */\n public destroy(): void {\n logger.debug(\n `[RTCPeerConnectionController] Destroying RTCPeerConnectionController. ${this.propose}`\n );\n this.removeConnectionTimer();\n this._iceGatheringController?.destroy();\n this.localStreamController.destroy();\n this.transceiverController?.destroy();\n\n // Close peer connection\n if (this.peerConnection) {\n this.stopRemoteTracks();\n this.removeAllListeners();\n this.peerConnection.close();\n this.peerConnection = undefined;\n }\n\n // Call parent destroy to clean up subscriptions and complete destroyed$\n super.destroy();\n }\n private removeAllListeners() {\n if (this.peerConnection) {\n this.peerConnection.removeEventListener(\n 'icegatheringstatechange',\n this.onicegatheringstatechangeHandler\n );\n this.peerConnection.removeEventListener(\n 'iceconnectionstatechange',\n this.oniceconnectionstatechangeHandler\n );\n this.peerConnection.removeEventListener(\n 'connectionstatechange',\n this.onconnectionstatechangeHandler\n );\n this.peerConnection.removeEventListener(\n 'signalingstatechange',\n this.onsignalingstatechangeHandler\n );\n this.peerConnection.removeEventListener('negotiationneeded', this.onnegotiationneededHandler);\n }\n }\n\n private stopRemoteTracks() {\n const remoteStream = this._remoteStream$.value;\n remoteStream?.getTracks().forEach((track: MediaStreamTrack) => {\n logger.debug(`[RTCPeerConnectionController] Stopping remote track: ${track.kind}`);\n track.stop();\n });\n }\n\n public get mediaDirections(): {\n audio: RTCRtpTransceiverDirection;\n video: RTCRtpTransceiverDirection;\n } {\n return (\n this.transceiverController?.getMediaDirections() ??\n this._remoteOfferMediaDirections ?? {\n audio: 'inactive',\n video: 'inactive'\n }\n );\n }\n protected async _setRemoteDescription(params: RTCSessionDescriptionInit): Promise<void> {\n if (!this.peerConnection) {\n throw new DependencyError('RTCPeerConnection is not initialized');\n }\n\n const finalRemote = await this.setRemoteDescriptionBefore(params.sdp);\n\n const answer: RTCSessionDescriptionInit = {\n ...params,\n sdp: finalRemote\n };\n logger.debug('[RTCPeerConnectionController] Setting remote description:', answer);\n return this.peerConnection.setRemoteDescription(answer);\n }\n}\n","// =============================================================================\n// VERTO TYPE GUARDS\n// =============================================================================\n// This file contains type guards for Verto protocol types.\n\nimport { hasProperty, isObject } from './base.guards';\n\nimport type { VertoMethod } from '../../types/rpc.types';\nimport type { TypeGuard } from '../types/base';\nimport type { WebrtcMessagePayload } from '../types/events';\nimport type {\n VertoAnswerMessage,\n VertoAnswerParams,\n VertoAnswerResultMessage,\n VertoAttachMessage,\n VertoAttachParams,\n VertoByeMessage,\n VertoByeParams,\n VertoInviteMessage,\n VertoInviteParams,\n VertoMediaMessage,\n VertoMediaParams,\n VertoMediaParamsMessage,\n VertoMediaParamsParams,\n VertoMethodMessage,\n VertoPingMessage,\n VertoPingParams,\n VertoPongMessage,\n VertoPongParams\n} from '../types/verto';\n\n// =============================================================================\n// VERTO MESSAGE TYPE GUARDS\n// =============================================================================\n\nexport function isVertoMethodMessage(value: unknown): value is VertoMethodMessage {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'id')\n );\n}\n\nexport function isVertoAnswerMessage(value: unknown): value is VertoAnswerMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return msg.method === 'verto.answer' && isObject(msg.params) && hasProperty(msg.params, 'callID');\n}\n\nexport function isVertoMediaMessage(value: unknown): value is VertoMediaMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return (\n msg.method === 'verto.media' &&\n isObject(msg.params) &&\n hasProperty(msg.params, 'callID') &&\n hasProperty(msg.params, 'sdp')\n );\n}\n\nexport function isVertoMediaParamsMessage(value: unknown): value is VertoMediaParamsMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return (\n msg.method === 'verto.mediaParams' &&\n isObject(msg.params) &&\n hasProperty(msg.params, 'mediaParams')\n );\n}\n\nexport function isVertoPingMessage(value: unknown): value is VertoPingMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return msg.method === 'verto.ping';\n}\n\nexport function isVertoPongMessage(value: unknown): value is VertoPongMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return msg.method === 'verto.pong';\n}\n\nexport function isVertoInviteMessage(value: unknown): value is VertoInviteMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return (\n msg.method === 'verto.invite' &&\n isObject(msg.params) &&\n hasProperty(msg.params, 'sdp') &&\n hasProperty(msg.params, 'callID')\n );\n}\n\nexport function isVertoByeMessage(value: unknown): value is VertoByeMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return msg.method === 'verto.bye';\n}\n\nexport function isVertoAnswerResultMessage(value: unknown): value is VertoAnswerResultMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return (\n isObject(msg.result) &&\n hasProperty(msg.result, 'method') &&\n msg.result.method === 'verto.answer'\n );\n}\n\nexport function isVertoAttachMessage(value: unknown): value is VertoAttachMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return msg.method === 'verto.attach';\n}\n\n// =============================================================================\n// WEBRTC MESSAGE EVENT DATA (inner params.params) TYPE GUARDS\n// =============================================================================\n\nexport function isVertoAnswerInnerParams(value: unknown): value is WebrtcMessagePayload & {\n method: 'verto.answer';\n params: VertoAnswerParams;\n} {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'method') &&\n value.method === 'verto.answer' &&\n isObject(value.params) &&\n hasProperty(value.params, 'callID')\n );\n}\n\nexport function isVertoMediaInnerParams(value: unknown): value is WebrtcMessagePayload & {\n method: 'verto.media';\n params: VertoMediaParams;\n} {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'method') &&\n value.method === 'verto.media' &&\n isObject(value.params) &&\n hasProperty(value.params, 'callID') &&\n hasProperty(value.params, 'sdp')\n );\n}\n\nexport function isVertoMediaParamsInnerParams(value: unknown): value is WebrtcMessagePayload & {\n method: 'verto.mediaParams';\n params: VertoMediaParamsParams;\n} {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'method') &&\n value.method === 'verto.mediaParams' &&\n isObject(value.params) &&\n hasProperty(value.params, 'mediaParams')\n );\n}\n\nexport function isVertoPingInnerParams(value: unknown): value is WebrtcMessagePayload & {\n method: 'verto.ping';\n params: VertoPingParams;\n} {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'method') &&\n value.method === 'verto.ping'\n );\n}\n\n// =============================================================================\n// VERTO PARAMS TYPE GUARDS (for use with filterAs on Verto method params)\n// =============================================================================\n\nexport function isVertoAnswerParamsGuard(value: unknown): value is VertoAnswerParams {\n return isObject(value) && hasProperty(value, 'callID') && typeof value.callID === 'string';\n}\n\nexport function isVertoMediaSdpParamsGuard(value: unknown): value is VertoMediaParams {\n return (\n isObject(value) &&\n hasProperty(value, 'callID') &&\n typeof value.callID === 'string' &&\n hasProperty(value, 'sdp') &&\n typeof value.sdp === 'string'\n );\n}\n\nexport function isVertoMediaParamsParamsGuard(value: unknown): value is VertoMediaParamsParams {\n return (\n isObject(value) &&\n hasProperty(value, 'callID') &&\n typeof value.callID === 'string' &&\n hasProperty(value, 'mediaParams') &&\n isObject(value.mediaParams)\n );\n}\n\nexport function isVertoPingParamsGuard(value: unknown): value is VertoPingParams {\n return (\n isObject(value) &&\n hasProperty(value, 'callID') &&\n typeof value.callID === 'string' &&\n hasProperty(value, 'dialogParams') &&\n isObject(value.dialogParams)\n );\n}\n\nexport function isVertoPongParamsGuard(value: unknown): value is VertoPongParams {\n return isVertoPingParamsGuard(value);\n}\n\nexport function isVertoInviteParamsGuard(value: unknown): value is VertoInviteParams {\n return (\n isObject(value) &&\n hasProperty(value, 'dialogParams') &&\n isObject(value.dialogParams) &&\n hasProperty(value, 'sdp') &&\n typeof value.sdp === 'string'\n );\n}\n\nexport function isVertoByeParamsGuard(value: unknown): value is VertoByeParams {\n return (\n isObject(value) &&\n hasProperty(value, 'callID') &&\n typeof value.callID === 'string' &&\n hasProperty(value, 'cause') &&\n typeof value.cause === 'string'\n );\n}\n\nexport function isVertoAttachParamsGuard(value: unknown): value is VertoAttachParams {\n return (\n isObject(value) &&\n hasProperty(value, 'callID') &&\n typeof value.callID === 'string' &&\n hasProperty(value, 'callee_id_number') &&\n typeof value.callee_id_number === 'string' &&\n hasProperty(value, 'callee_id_name') &&\n typeof value.callee_id_name === 'string' &&\n hasProperty(value, 'caller_id_number') &&\n typeof value.caller_id_number === 'string' &&\n hasProperty(value, 'caller_id_name') &&\n typeof value.caller_id_name === 'string'\n );\n}\n\n// =============================================================================\n// VERTO METHOD TYPE MAPPING\n// =============================================================================\n\nexport const VertoMethodTypeMap = {\n 'verto.answer': isVertoAnswerMessage,\n 'verto.media': isVertoMediaMessage,\n 'verto.mediaParams': isVertoMediaParamsMessage,\n 'verto.ping': isVertoPingMessage,\n 'verto.pong': isVertoPongMessage,\n 'verto.invite': isVertoInviteMessage,\n 'verto.bye': isVertoByeMessage\n} as const satisfies Partial<Record<VertoMethod, TypeGuard<VertoMethodMessage>>>;\n\nexport type VertoMethodType = keyof typeof VertoMethodTypeMap;\n\n/**\n * Gets the appropriate type guard for a Verto method.\n */\nexport function getVertoMethodGuard(method: string): TypeGuard<VertoMethodMessage> | undefined {\n return VertoMethodTypeMap[method as VertoMethodType];\n}\n\n// =============================================================================\n// VERTO PARAMS TYPE MAPPING\n// =============================================================================\n\nexport const VertoParamsTypeMap = {\n 'verto.answer': isVertoAnswerParamsGuard,\n 'verto.media': isVertoMediaSdpParamsGuard,\n 'verto.mediaParams': isVertoMediaParamsParamsGuard,\n 'verto.ping': isVertoPingParamsGuard,\n 'verto.pong': isVertoPongParamsGuard,\n 'verto.invite': isVertoInviteParamsGuard,\n 'verto.attach': isVertoAttachParamsGuard,\n 'verto.bye': isVertoByeParamsGuard\n} as const satisfies Partial<Record<VertoMethod, TypeGuard<unknown>>>;\n\n/**\n * Gets the appropriate params type guard for a Verto method.\n */\nexport function getVertoParamsGuard(\n method: string\n):\n | TypeGuard<\n | VertoAnswerParams\n | VertoMediaParams\n | VertoMediaParamsParams\n | VertoPingParams\n | VertoPongParams\n | VertoInviteParams\n | VertoByeParams\n | VertoAttachParams\n >\n | undefined {\n return VertoParamsTypeMap[method as VertoMethodType];\n}\n","/* eslint-disable max-lines */\nimport {\n filter,\n firstValueFrom,\n map,\n merge,\n race,\n startWith,\n take,\n takeUntil,\n timeout\n} from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { PreferencesContainer } from '../containers/PreferencesContainer';\nimport { RTCPeerConnectionController } from '../controllers/RTCPeerConnectionController';\nimport { INVITE_VERSION } from '../core/constants';\nimport { DependencyError, InvalidParams, JSONRPCError, VertoPongError } from '../core/errors';\nimport {\n VertoAnswer,\n VertoBye,\n VertoByeCauseCodes,\n VertoInfo,\n VertoInvite,\n VertoModify,\n VertoPong,\n WebrtcVerto\n} from '../core/RPCMessages';\nimport { isCallJoinedPayload } from '../core/RPCMessages/guards/events.guards';\nimport {\n isVertoAnswerInnerParams,\n isVertoAttachMessage,\n isVertoByeMessage,\n isVertoMediaInnerParams,\n isVertoMediaParamsInnerParams,\n isVertoPingInnerParams\n} from '../core/RPCMessages/guards/verto.guards';\nimport { filterAs } from '../operators';\nimport { filterNull } from '../operators/filterNull';\nimport { getValueFrom } from '../utils/getValueFrom';\nimport { getLogger } from '../utils/logger';\n\nimport type { AttachManager } from './AttachManager';\nimport type { WebRTCCall } from '../core/entities/Call';\nimport type { VertoRPCMessage } from '../core/RPCMessages';\nimport type {\n ExecuteVertoOptions,\n ScreenShareStatus,\n SignalingStatus,\n TransferOptions,\n WebRTCVertoManagerOptions\n} from './types/verto-manager.types';\nimport type { JSONRPCResponse } from '../core/RPCMessages/types/base';\nimport type { CallJoinedPayload } from '../core/RPCMessages/types/events';\nimport type {\n VertoAnswerParams,\n VertoAttachParams,\n VertoByeCause,\n VertoByeParams,\n VertoMediaParams,\n VertoMediaParamsParams,\n VertoPingParams\n} from '../core/RPCMessages/types/verto';\nimport type { RTCPeerConnectionPropose } from '../core/types/call.types';\nimport type { MediaOptions, MediaDirections } from '../core/types/media.types';\nimport type { VertoMethod } from '../core/types/rpc.types';\nimport type { WebRTCApiProvider } from '../dependencies/interfaces';\nimport type { DeviceController } from '../interfaces/DeviceController';\nimport type { WebRTCVerto } from '../interfaces/WebRTCVerto';\nimport type { BehaviorSubject, Observable } from 'rxjs';\n\nconst logger = getLogger();\nexport abstract class VertoManager extends Destroyable {\n protected callSession?: WebRTCCall;\n\n constructor(callSession?: WebRTCCall) {\n super();\n this.callSession = callSession;\n }\n\n public destroy(): void {\n this.callSession = undefined;\n super.destroy();\n }\n}\nexport class WebRTCVertoManager extends VertoManager implements WebRTCVerto {\n public mediaDirections$!: Observable<MediaDirections>;\n public localStream$!: Observable<MediaStream>;\n public remoteStream$!: Observable<MediaStream>;\n private readonly onError?: (error: Error) => void;\n private _rtcPeerConnections$ = this.createBehaviorSubject<RTCPeerConnectionController[]>([]);\n\n private _nodeId$: BehaviorSubject<string | null>;\n private _selfId$ = this.createBehaviorSubject<string | null>(null);\n private _signalingStatus$ = this.createBehaviorSubject<SignalingStatus | null>(null);\n private _screenShareStatus$ = this.createBehaviorSubject<ScreenShareStatus>('none');\n private _rtcPeerConnectionsMap = new Map<string, RTCPeerConnectionController>();\n private _screenShareId?: string;\n private _screenShareTimeoutMs = 50000;\n\n constructor(\n protected webRtcCallSession: WebRTCCall,\n private readonly attachManager: AttachManager,\n private readonly deviceController: DeviceController,\n private readonly webRTCApiProvider: WebRTCApiProvider,\n options: WebRTCVertoManagerOptions = {}\n ) {\n super(webRtcCallSession);\n this._nodeId$ = this.createBehaviorSubject<string | null>(options.nodeId ?? null);\n this.onError = options.onError;\n this.initSubscriptions();\n this.initMainPeerConnection();\n }\n async hold(): Promise<void> {\n const vertoModifyMessage = VertoModify({\n sessid: this.webRtcCallSession.id,\n dialogParams: {\n callID: this.webRtcCallSession.id\n },\n action: 'hold'\n });\n\n try {\n await this.executeVerto(vertoModifyMessage);\n } catch (error) {\n logger.warn(\n '[WebRTCManager] Call might already be disconnected, error sending Verto hold:',\n error\n );\n throw error;\n }\n }\n async unhold(): Promise<void> {\n const vertoModifyMessage = VertoModify({\n sessid: this.webRtcCallSession.id,\n dialogParams: {\n callID: this.webRtcCallSession.id\n },\n action: 'unhold'\n });\n try {\n await this.executeVerto(vertoModifyMessage);\n } catch (error) {\n logger.warn(\n '[WebRTCManager] Call might already be disconnected, error sending Verto unhold:',\n error\n );\n throw error;\n }\n }\n\n public get mediaDirections(): MediaDirections {\n return this.mainPeerConnection.mediaDirections;\n }\n\n public get rtcPeerConnections$(): Observable<RTCPeerConnectionController[]> {\n return this._rtcPeerConnections$.asObservable();\n }\n\n public get rtcPeerConnections(): RTCPeerConnectionController[] {\n return this._rtcPeerConnections$.value;\n }\n\n public get nodeId$(): Observable<string | null> {\n return this._nodeId$.asObservable();\n }\n\n public get selfId$(): Observable<string | null> {\n return this._selfId$.asObservable();\n }\n\n public get localStream(): MediaStream | null {\n return this._rtcPeerConnectionsMap.get(this.webRtcCallSession.id)?.localStream ?? null;\n }\n\n public get remoteStream(): MediaStream | null {\n return this._rtcPeerConnectionsMap.get(this.webRtcCallSession.id)?.remoteStream ?? null;\n }\n\n public get nodeId(): string | null {\n return this._nodeId$.value;\n }\n\n public get screenShareStatus(): ScreenShareStatus {\n return this._screenShareStatus$.value;\n }\n\n public get screenShareStatus$(): Observable<ScreenShareStatus> {\n return this._screenShareStatus$.asObservable();\n }\n\n public get mainPeerConnection(): RTCPeerConnectionController {\n const rtcPeerConnection = this._rtcPeerConnectionsMap.get(this.webRtcCallSession.id);\n if (!rtcPeerConnection) {\n throw new DependencyError('Main peer connection not found');\n }\n return rtcPeerConnection;\n }\n\n public get signalingStatus$(): Observable<SignalingStatus> {\n return this.cachedObservable('signalingStatus$', () =>\n merge(\n this._signalingStatus$.pipe(filterNull()),\n this.mainPeerConnection.connectionState$.pipe(\n filter((connectionState) =>\n ['connected', 'disconnected', 'failed'].includes(connectionState)\n )\n ) as Observable<SignalingStatus>\n )\n );\n }\n\n private initSubscriptions() {\n // Eagerly populate node_id and selfId from call.joined events.\n // During reattach, call.joined often arrives before the verto.invite\n // RPC response (CALL CREATED) which is the authoritative source for\n // these values. Populating them early prevents downstream RPCs\n // (e.g. call.layout.list) from failing with empty identifiers.\n this.subscribeTo(this.callJoinedEvent$, (event: CallJoinedPayload) => {\n const memberNodeId = event.room_session.members.find(\n (m) => m.call_id === event.call_id\n )?.node_id;\n if (memberNodeId) {\n this.setNodeIdIfNull(memberNodeId);\n }\n if (event.member_id) {\n this.setSelfIdIfNull(event.member_id);\n }\n });\n\n this.subscribeTo(this.vertoMedia$, (event: VertoMediaParams) => {\n logger.debug('[WebRTCManager] Received Verto media event (early media SDP):', event);\n this._signalingStatus$.next('ringing');\n const { sdp, callID } = event;\n const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callID);\n void rtcPeerConnController?.updateAnswerStatus({\n status: 'received',\n sdp: sdp\n });\n });\n\n this.subscribeTo(this.vertoAnswer$, (event: VertoAnswerParams) => {\n logger.debug('[WebRTCManager] Received Verto answer event:', event);\n this._signalingStatus$.next('connecting');\n const { sdp, callID } = event;\n const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callID);\n void rtcPeerConnController?.updateAnswerStatus({\n status: 'received',\n sdp: sdp\n });\n });\n\n this.subscribeTo(this.vertoMediaParams$, (event: VertoMediaParamsParams) => {\n logger.debug('[WebRTCManager] Received Verto mediaParams event:', event);\n\n const { mediaParams, callID } = event;\n const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callID);\n const { audio, video } = mediaParams;\n if (audio) {\n void rtcPeerConnController?.updateSendersConstraints('audio', audio);\n }\n if (video) {\n void rtcPeerConnController?.updateSendersConstraints('video', video);\n }\n });\n\n this.subscribeTo(this.vertoPing$, (vertoPing: VertoPingParams) => {\n void this.attachManager.attach(this.webRtcCallSession);\n void this.sendVertoPong(vertoPing);\n });\n }\n\n /**\n * Set node_id/selfId only when the current value is null.\n *\n * During reattach, `call.joined` and `verto.answer` events can deliver\n * these identifiers before the `verto.invite` RPC response (`CALL CREATED`)\n * arrives. These methods let early events populate them eagerly so that\n * downstream RPC calls (e.g. `call.layout.list`) don't fail with empty\n * identifiers. `processInviteResponse()` remains the authoritative source\n * and always overwrites unconditionally.\n */\n private setNodeIdIfNull(nodeId: string): void {\n if (!this._nodeId$.value && nodeId) {\n logger.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);\n this._nodeId$.next(nodeId);\n }\n }\n\n private setSelfIdIfNull(selfId: string): void {\n if (!this._selfId$.value && selfId) {\n logger.debug(`[WebRTCManager] Early selfId set: ${selfId}`);\n this._selfId$.next(selfId);\n }\n }\n\n private async sendVertoPong(vertoPing: VertoPingParams) {\n try {\n const vertoPongMessage = VertoPong({\n ...vertoPing\n });\n await this.executeVerto(vertoPongMessage);\n } catch (error) {\n logger.warn('[WebRTCManager] Call might disconnect, error sending Verto pong:', error);\n this.onError?.(new VertoPongError(error));\n }\n }\n\n public async updateMediaConstraints(\n options: {\n audio?: MediaTrackConstraints;\n video?: MediaTrackConstraints;\n } = {}\n ): Promise<void> {\n const { audio, video } = options;\n try {\n if (audio) {\n await this.mainPeerConnection.updateSendersConstraints('audio', audio);\n }\n if (video) {\n await this.mainPeerConnection.updateSendersConstraints('video', video);\n }\n } catch (error) {\n logger.warn('[WebRTCManager] Error updating media constraints:', error);\n this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));\n throw error;\n }\n }\n\n public get selfId(): string | null {\n return this._selfId$.value;\n }\n\n private get callJoinedEvent$() {\n return this.webRtcCallSession.callEvent$.pipe(\n filter(isCallJoinedPayload),\n takeUntil(this.destroyed$)\n );\n }\n\n private get vertoMedia$() {\n return this.webRtcCallSession.webrtcMessages$.pipe(\n filterAs(isVertoMediaInnerParams, 'params'),\n takeUntil(this.destroyed$)\n );\n }\n\n private get vertoAnswer$() {\n return this.cachedObservable('vertoAnswer$', () =>\n this.webRtcCallSession.webrtcMessages$.pipe(\n filterAs(isVertoAnswerInnerParams, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n private get vertoMediaParams$() {\n return this.cachedObservable('vertoMediaParams$', () =>\n this.webRtcCallSession.webrtcMessages$.pipe(\n filterAs(isVertoMediaParamsInnerParams, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n private get vertoBye$() {\n return this.cachedObservable('vertoBye$', () =>\n this.webRtcCallSession.webrtcMessages$.pipe(\n filterAs(isVertoByeMessage, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n private get vertoAttach$() {\n return this.cachedObservable('vertoAttach$', () =>\n this.webRtcCallSession.webrtcMessages$.pipe(\n filterAs(isVertoAttachMessage, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n private get vertoPing$() {\n return this.cachedObservable('vertoPing$', () =>\n this.webRtcCallSession.webrtcMessages$.pipe(\n filterAs(isVertoPingInnerParams, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n private async executeVerto(\n message: VertoRPCMessage,\n optionals: ExecuteVertoOptions = {}\n ): Promise<JSONRPCResponse<unknown>> {\n const params = {\n callID: optionals.callID ?? this.webRtcCallSession.id,\n\n node_id: optionals.node_id ?? this._nodeId$.value ?? '',\n message,\n subscribe: optionals.subscribe\n };\n\n const webrtcVertoMessage = WebrtcVerto(params);\n\n const response = await this.webRtcCallSession.execute(webrtcVertoMessage);\n\n // Check for error at top level\n if (response.error) {\n const error = new JSONRPCError(\n response.error.code,\n response.error.message,\n response.error.data\n );\n this.onError?.(error);\n return response;\n }\n\n // Check for nested error in result.result (webrtc.verto wraps the inner response)\n const innerResult = getValueFrom<{ error?: { code: number; message: string; data?: unknown } }>(\n response,\n 'result.result'\n );\n if (innerResult?.error) {\n const error = new JSONRPCError(\n innerResult.error.code,\n innerResult.error.message,\n innerResult.error.data\n );\n this.onError?.(error);\n return response;\n }\n\n return response;\n }\n\n private async sendLocalDescription(\n message: VertoRPCMessage,\n rtcPeerConnController: RTCPeerConnectionController\n ): Promise<void> {\n const vertoMethod: VertoMethod = message.method;\n\n const optionalsParams = this.getSendLocalSDPOptionalParams(rtcPeerConnController, vertoMethod);\n\n try {\n const response = await this.executeVerto(message, optionalsParams);\n\n switch (vertoMethod) {\n case 'verto.invite':\n this.processInviteResponse(response, rtcPeerConnController);\n break;\n case 'verto.modify':\n await this.processModifyResponse(response, rtcPeerConnController);\n break;\n default:\n }\n } catch (error) {\n // execute() can reject before executeVerto checks the response.\n // Route the error through onError so it reaches Call.emitError.\n logger.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);\n this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));\n }\n }\n private async processModifyResponse(\n response: JSONRPCResponse<unknown>,\n rtcPeerConnController: RTCPeerConnectionController\n ) {\n if (!response.error) {\n const action = getValueFrom<string>(response, 'result.result.result.action');\n const sdp = getValueFrom<string>(response, 'result.result.result.sdp');\n if (action === 'updateMedia' && !!sdp) {\n try {\n await rtcPeerConnController.updateAnswerStatus({\n status: 'received',\n sdp\n });\n } catch (error) {\n logger.warn('[WebRTCManager] Error processing modify response:', error);\n const modifyError =\n error instanceof Error ? error : new Error(String(error), { cause: error });\n this.onError?.(modifyError);\n }\n }\n }\n }\n\n private processInviteResponse(\n response: JSONRPCResponse<unknown>,\n rtcPeerConnController: RTCPeerConnectionController\n ) {\n if (\n !response.error &&\n getValueFrom(response, 'result.result.result.message') === 'CALL CREATED'\n ) {\n this._signalingStatus$.next('trying');\n this._nodeId$.next(getValueFrom<string>(response, 'result.node_id') ?? null);\n const memberId = getValueFrom<string>(response, 'result.result.result.memberID') ?? null;\n const callId = getValueFrom<string>(response, 'result.result.result.callID') ?? null;\n logger.debug('[WebRTCManager] Verto invite response:', { callId, memberId, response });\n\n this._selfId$.next(memberId);\n rtcPeerConnController.setMemberId(memberId);\n if (callId) {\n this.webRtcCallSession.addCallId(callId);\n }\n void this.attachManager.attach(this.webRtcCallSession);\n logger.info('[WebRTCManager] Verto invite successful');\n logger.debug(\n `[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`\n );\n } else {\n logger.error('[WebRTCManager] Verto invite failed:', response);\n const inviteError = response.error\n ? new JSONRPCError(response.error.code, response.error.message, response.error.data)\n : new Error('Verto invite failed: unexpected response');\n this.onError?.(inviteError);\n }\n }\n\n private get RTCPeerConnectionConfig() {\n return {\n iceServers:\n this.webRtcCallSession.clientSession.iceServers ?? PreferencesContainer.instance.iceServers,\n relayOnly:\n PreferencesContainer.instance.relayOnly ||\n PreferencesContainer.instance.disableUdpIceServers,\n disableUdpIceServers: PreferencesContainer.instance.disableUdpIceServers,\n iceCandidateTimeout: PreferencesContainer.instance.iceCandidateTimeout,\n iceGatheringTimeout: PreferencesContainer.instance.iceGatheringTimeout\n };\n }\n\n private initMainPeerConnection() {\n //if (this.webRtcCallSession.direction === 'outbound') {\n const { options } = this.webRtcCallSession;\n const rtcPeerConnController = new RTCPeerConnectionController(\n {\n propose: 'main',\n callId: this.webRtcCallSession.id,\n audio: options.audio,\n video: options.video,\n inputAudioDeviceConstraints: options.inputAudioDeviceConstraints,\n inputVideoDeviceConstraints: options.inputVideoDeviceConstraints,\n inputAudioStream: options.inputAudioStream,\n inputVideoStream: options.inputVideoStream,\n receiveAudio: options.receiveAudio,\n receiveVideo: options.receiveVideo,\n webRTCApiProvider: this.webRTCApiProvider,\n ...this.RTCPeerConnectionConfig\n },\n options.initOffer,\n this.deviceController\n );\n this.setupLocalDescriptionHandler(rtcPeerConnController);\n this.setupVertoByeHandler();\n this.setupVertoAttachHandler();\n this.initObservables(rtcPeerConnController);\n this._rtcPeerConnectionsMap.set(rtcPeerConnController.id, rtcPeerConnController);\n this._rtcPeerConnections$.next(Array.from(this._rtcPeerConnectionsMap.values()));\n this.subscribeTo(rtcPeerConnController.errors$, (error) => {\n this.onError?.(error);\n });\n\n // For inbound calls, wait for answer()/reject() then trigger SDP answer creation\n if (options.initOffer) {\n void this.handleInboundAnswer(rtcPeerConnController);\n }\n }\n\n private async handleInboundAnswer(\n rtcPeerConnController: RTCPeerConnectionController\n ): Promise<void> {\n logger.debug('[WebRTCManager] Waiting for inbound call to be accepted or rejected');\n const vertoByeOrAccepted: boolean | VertoByeParams | null = await firstValueFrom(\n race(this.vertoBye$, this.webRtcCallSession.answered$).pipe(takeUntil(this.destroyed$))\n ).catch(() => null);\n\n if (vertoByeOrAccepted === null) {\n logger.debug('[WebRTCManager] Inbound answer handler aborted (destroyed).');\n return;\n }\n\n if (isVertoByeMessage(vertoByeOrAccepted)) {\n logger.info('[WebRTCManager] Inbound call ended by remote before answer.');\n this.callSession?.destroy();\n } else if (!vertoByeOrAccepted) {\n logger.info('[WebRTCManager] Inbound call rejected by user.');\n try {\n await this.bye('USER_BUSY');\n } finally {\n this._signalingStatus$.next('disconnected');\n this.callSession?.destroy();\n }\n } else {\n logger.debug('[WebRTCManager] Inbound call accepted, creating SDP answer');\n const answerOptions: MediaOptions | undefined = this.webRtcCallSession.answerMediaOptions;\n try {\n await rtcPeerConnController.acceptInbound(answerOptions);\n } catch (error) {\n logger.error('[WebRTCManager] Error creating inbound answer:', error);\n this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));\n }\n }\n }\n\n private setupVertoAttachHandler(): void {\n this.subscribeTo(this.vertoAttach$, async (vertoAttach: VertoAttachParams) => {\n logger.debug('[WebRTCManager] Received Verto attach event:', vertoAttach);\n const { callID } = vertoAttach;\n await this.attachManager.attach({\n id: callID,\n to: vertoAttach.callee_id_number,\n mediaDirections: {\n audio: 'sendrecv',\n // this might be changed in future to support video attach, but this feature was originally supposed in the non-video SDK.\n video: 'inactive'\n }\n });\n await this.attachManager.reattachCalls();\n });\n }\n\n private initObservables(rtcPeerConnController: RTCPeerConnectionController): void {\n this.mediaDirections$ = rtcPeerConnController.connectionState$.pipe(\n filter((state) => state === 'connected'),\n map(() => rtcPeerConnController.mediaDirections),\n startWith(rtcPeerConnController.mediaDirections),\n takeUntil(this.destroyed$)\n );\n this.localStream$ = rtcPeerConnController.localStream$.pipe(\n filterNull(),\n takeUntil(this.destroyed$)\n );\n this.remoteStream$ = rtcPeerConnController.remoteStream$.pipe(\n filterNull(),\n takeUntil(this.destroyed$)\n );\n }\n private setupLocalDescriptionHandler(rtcPeerConnController: RTCPeerConnectionController): void {\n this.subscribeTo(\n // watch for local description from the RTCPeerConnection and send it to remote peer\n rtcPeerConnController.localDescription$.pipe(\n // Filter out null descriptions\n filter((description): description is RTCSessionDescription => description !== null),\n takeUntil(this.destroyed$)\n ),\n (description) => {\n const { type, sdp } = description;\n const dialogParams = this.dialogParams(rtcPeerConnController);\n const initial = !rtcPeerConnController.firstSDPExchangeCompleted;\n if (type === 'answer') {\n {\n const vertoMessageRequest = VertoAnswer({\n dialogParams,\n sdp: sdp\n });\n void this.sendLocalDescriptionOnceAccepted(vertoMessageRequest, rtcPeerConnController);\n }\n } else if (initial) {\n const vertoMessageRequest = VertoInvite({\n dialogParams,\n sdp\n });\n void this.sendLocalDescription(vertoMessageRequest, rtcPeerConnController);\n } else {\n const vertoMessageRequest = VertoModify({\n dialogParams,\n sdp,\n action: 'updateMedia'\n });\n void this.sendLocalDescription(vertoMessageRequest, rtcPeerConnController);\n }\n }\n );\n }\n\n private setupVertoByeHandler() {\n this.subscribeTo(this.vertoBye$, () => {\n this._signalingStatus$.next('disconnected');\n void this.attachManager.detach(this.webRtcCallSession);\n this.callSession?.destroy();\n });\n }\n\n private getSendLocalSDPOptionalParams(\n rtcPeerConnController: RTCPeerConnectionController,\n vertoMethod: VertoMethod\n ): ExecuteVertoOptions {\n let subscribe = undefined;\n const initial = !rtcPeerConnController.firstSDPExchangeCompleted;\n if (initial) {\n subscribe = [];\n if (rtcPeerConnController.isMainDevice) {\n subscribe.push(...PreferencesContainer.instance.inviteSubscribeMainDevice);\n } else if (rtcPeerConnController.isAdditionalDevice) {\n subscribe.push(...PreferencesContainer.instance.inviteSubscribeAdditionalDevice);\n } else if (rtcPeerConnController.isScreenShare) {\n subscribe.push(...PreferencesContainer.instance.inviteSubscribeScreenshare);\n }\n }\n const isInvite = vertoMethod === 'verto.invite';\n const optionalsParams = {\n callID: rtcPeerConnController.id,\n // verto.invite must never include node_id; all other methods must\n node_id: isInvite ? '' : (this._nodeId$.value ?? ''),\n subscribe\n };\n return optionalsParams;\n }\n\n async sendLocalDescriptionOnceAccepted(\n vertoMessageRequest: VertoRPCMessage,\n rtcPeerConnectionController: RTCPeerConnectionController\n ): Promise<void> {\n logger.debug('[WebRTCManager] Waiting for call to be accepted or ended before sending answer');\n const vertoByeOrAccepted: boolean | VertoByeParams = await firstValueFrom(\n race(this.vertoBye$, this.webRtcCallSession.answered$)\n );\n\n if (isVertoByeMessage(vertoByeOrAccepted)) {\n logger.info('[WebRTCManager] Call ended before answer was sent.');\n this.callSession?.destroy();\n } else if (!vertoByeOrAccepted) {\n logger.info('[WebRTCManager] Call was not accepted, sending verto.bye.');\n try {\n await this.bye('USER_BUSY');\n } finally {\n this._signalingStatus$.next('disconnected');\n this.callSession?.destroy();\n }\n } else {\n logger.debug('[WebRTCManager] Call accepted, sending answer');\n try {\n this._signalingStatus$.next('connecting');\n await this.sendLocalDescription(vertoMessageRequest, rtcPeerConnectionController);\n await rtcPeerConnectionController.updateAnswerStatus({\n status: 'sent'\n });\n await this.attachManager.attach(this.webRtcCallSession);\n } catch (error) {\n logger.error('[WebRTCManager] Error sending Verto answer:', error);\n this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));\n await rtcPeerConnectionController.updateAnswerStatus({\n status: 'failed'\n });\n }\n }\n }\n\n dialogParams(rtcPeerConnectionController: RTCPeerConnectionController): Record<string, unknown> {\n const memberId = rtcPeerConnectionController.memberId ?? this._selfId$.value ?? undefined;\n const attach =\n rtcPeerConnectionController.propose === 'main' &&\n !rtcPeerConnectionController.firstSDPExchangeCompleted &&\n this.webRtcCallSession.options.reattach;\n\n return {\n id: rtcPeerConnectionController.isMainDevice\n ? this.webRtcCallSession.id\n : rtcPeerConnectionController.id,\n destinationNumber: this.webRtcCallSession.to ?? this.webRtcCallSession.from,\n attach,\n reattaching: attach,\n callerName: this.webRtcCallSession.fromName,\n callerNumber: this.webRtcCallSession.from,\n remoteCallerName: this.webRtcCallSession.toName,\n remoteCallerNumber: this.webRtcCallSession.to,\n userVariables: {\n memberCallId: this.webRtcCallSession.id,\n memberId,\n ...this.webRtcCallSession.userVariables\n },\n screenShare: rtcPeerConnectionController.isScreenShare,\n additionalDevice: rtcPeerConnectionController.isAdditionalDevice,\n pingSupported: true,\n version: INVITE_VERSION\n };\n }\n\n public muteMainAudioInputDevice(): void {\n return this.mainPeerConnection.stopTrackSender('audio');\n }\n\n public muteMainVideoInputDevice(): void {\n return this.mainPeerConnection.stopTrackSender('video');\n }\n\n public async unmuteMainAudioInputDevice(): Promise<void> {\n return this.mainPeerConnection.restoreTrackSender('audio');\n }\n\n public async unmuteMainVideoInputDevice(): Promise<void> {\n return this.mainPeerConnection.restoreTrackSender('video');\n }\n\n public async addInputDevice(\n options: MediaOptions = { audio: false, video: true }\n ): Promise<string | undefined> {\n return this.initAdditionalPeerConnection('additional-device', options);\n }\n\n /**\n * Add a new input device to the main peer connection,\n * only if a device of the same kind is not present already.\n *\n * @see selectAudioInputDevice\n * @see selectVideoInputDevice\n * @param options\n */\n public async addMainInputDevices(options: MediaOptions = { audio: true }): Promise<void> {\n let deviceKind: 'audio' | 'video' | 'both' | undefined = undefined;\n\n const { mediaDirections } = this.mainPeerConnection;\n\n if (\n options.audio ??\n options.inputAudioDeviceConstraints ??\n (options.inputAudioStream && mediaDirections.audio.startsWith('send'))\n ) {\n deviceKind = 'audio';\n }\n if (\n options.video ??\n options.inputVideoDeviceConstraints ??\n (options.inputVideoStream && !mediaDirections.video.startsWith('send'))\n ) {\n deviceKind = deviceKind === 'audio' ? 'both' : 'video';\n }\n if (deviceKind) {\n this.mainPeerConnection.updateMediaDevicesOptions(options);\n await this.mainPeerConnection.restoreTrackSender(deviceKind);\n } else {\n const error = new InvalidParams('No valid device to be added');\n this.onError?.(error);\n throw error;\n }\n }\n\n public async addScreenMedia(options: MediaOptions = { audio: false }): Promise<void> {\n await this.initAdditionalPeerConnection('screenshare', options);\n }\n\n private async initAdditionalPeerConnection(\n propose: RTCPeerConnectionPropose,\n options: MediaOptions\n ): Promise<string | undefined> {\n let rtcPeerConnController: RTCPeerConnectionController | null = null;\n try {\n this._screenShareStatus$.next('starting');\n rtcPeerConnController = new RTCPeerConnectionController(\n {\n ...options,\n ...this.RTCPeerConnectionConfig,\n propose,\n webRTCApiProvider: this.webRTCApiProvider\n },\n undefined,\n this.deviceController\n );\n this.setupLocalDescriptionHandler(rtcPeerConnController);\n if (propose === 'screenshare') {\n this._screenShareId = rtcPeerConnController.id;\n }\n this._rtcPeerConnectionsMap.set(rtcPeerConnController.id, rtcPeerConnController);\n this._rtcPeerConnections$.next(Array.from(this._rtcPeerConnectionsMap.values()));\n this.subscribeTo(rtcPeerConnController.errors$, (error) => {\n this.onError?.(error);\n });\n await firstValueFrom(\n rtcPeerConnController.connectionState$.pipe(\n filter((state) => state === 'connected'),\n take(1),\n timeout(this._screenShareTimeoutMs)\n )\n );\n this._screenShareStatus$.next('started');\n logger.info('[WebRTCManager] Screen share started successfully.');\n return rtcPeerConnController.id;\n } catch (error) {\n logger.warn('[WebRTCManager] Error initializing additional peer connection:', error);\n this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));\n if (rtcPeerConnController) {\n rtcPeerConnController.destroy();\n }\n this._screenShareStatus$.next('none');\n }\n }\n\n public async removeInputDevices(id: string): Promise<void> {\n return this.removeAdditionalPeerConnection(id);\n }\n\n public removeMainInputDevice(options = { removeAudio: false, removeVideo: true }): void {\n let removeTrack: 'audio' | 'video' | 'both' | undefined = undefined;\n if (options.removeAudio) {\n removeTrack = 'audio';\n }\n if (options.removeVideo) {\n removeTrack = removeTrack === 'audio' ? 'both' : 'video';\n }\n\n if (removeTrack) {\n return this.mainPeerConnection.stopTrackSender(removeTrack, {\n updateTransceiverDirection: true\n });\n }\n }\n\n public async removeScreenMedia(): Promise<void> {\n if (!['starting', 'started'].includes(this._screenShareStatus$.value)) {\n logger.warn('[WebRTCManager] No active screen share to stop.');\n }\n if (!this._screenShareId) {\n logger.debug('[WebRTCManager] No screen share peer connection found.');\n return;\n }\n this._screenShareStatus$.next('stopping');\n await this.removeAdditionalPeerConnection(this._screenShareId);\n this._screenShareId = undefined;\n this._screenShareStatus$.next('none');\n }\n\n public async removeAdditionalPeerConnection(id: string): Promise<void> {\n const rtcPeerConnController = this._rtcPeerConnectionsMap.get(id);\n try {\n if (rtcPeerConnController) {\n await this.executeVertoBye(rtcPeerConnController);\n }\n } finally {\n rtcPeerConnController?.destroy();\n this._rtcPeerConnectionsMap.delete(id);\n this._rtcPeerConnections$.next(Array.from(this._rtcPeerConnectionsMap.values()));\n }\n }\n\n private async executeVertoBye(\n rtcPeerConnController: RTCPeerConnectionController,\n cause?: VertoByeCause\n ): Promise<void> {\n try {\n const causeParams = cause\n ? {\n cause: cause,\n causeCode: VertoByeCauseCodes[cause]\n }\n : {};\n\n await this.executeVerto(\n VertoBye({\n ...causeParams,\n dialogParams: this.dialogParams(rtcPeerConnController)\n })\n );\n } catch (error) {\n logger.warn(\n '[WebRTCManager] Call might already be disconnected, error sending Verto bye:',\n error\n );\n throw error;\n }\n }\n public async bye(cause?: VertoByeCause): Promise<void> {\n void this.attachManager.detach(this.webRtcCallSession);\n const rtcPeerConnController = this._rtcPeerConnectionsMap.get(this.webRtcCallSession.id);\n if (rtcPeerConnController) {\n await this.executeVertoBye(rtcPeerConnController, cause);\n }\n }\n\n public async sendDigits(dtmf: string): Promise<void> {\n const vertoInfoMessage = VertoInfo({\n sessid: this.webRtcCallSession.id,\n dialogParams: {\n callID: this.webRtcCallSession.id\n },\n dtmf\n });\n\n try {\n await this.executeVerto(vertoInfoMessage);\n } catch (error) {\n logger.warn('[WebRTCManager] Error sending DTMF digits:', error);\n throw error;\n }\n }\n\n public async transfer(options: TransferOptions): Promise<void> {\n const message = VertoModify({\n ...options,\n dialogParams: this.dialogParams(this.mainPeerConnection),\n action: 'transfer'\n });\n try {\n logger.debug('[WebRTCManager] Transferring call with options:', options);\n await this.executeVerto(message);\n } catch (error) {\n logger.error('[WebRTCManager] Error transferring call:', error);\n throw error;\n }\n }\n\n public destroy(): void {\n this._rtcPeerConnectionsMap.forEach((rtcPeerConnController) => {\n rtcPeerConnController.destroy();\n });\n this._rtcPeerConnectionsMap.clear();\n this._rtcPeerConnections$.complete();\n super.destroy();\n }\n}\n","import { Participant, SelfParticipant, type ExecuteMethod } from '../core/entities/Participant';\n\nimport type { DeviceController } from '../interfaces/DeviceController';\nimport type { VertoManager } from '../interfaces/VertoManager';\n\n/**\n * Factory for creating Participant instances with proper dependency injection\n * Eliminates circular dependency between Call and Participant\n */\nexport class ParticipantFactory {\n constructor(\n private executeMethod: ExecuteMethod,\n private vertoManager: VertoManager,\n private deviceController: DeviceController\n ) {}\n\n /**\n * Create a self participant (the local user in the call)\n */\n public createSelfParticipant(id: string): SelfParticipant {\n return new SelfParticipant(id, this.executeMethod, this.vertoManager, this.deviceController);\n }\n\n /**\n * Create a remote participant\n */\n public createParticipant(id: string): Participant {\n return new Participant(id, this.executeMethod, this.deviceController);\n }\n}\n","import {\n distinctUntilChanged,\n filter,\n firstValueFrom,\n from,\n map,\n merge,\n share,\n takeUntil,\n tap\n} from 'rxjs';\nimport { v4 as uuid } from 'uuid';\n\nimport { Destroyable } from '../../behaviors/Destroyable';\nimport { PreferencesContainer } from '../../containers/PreferencesContainer';\nimport { ParticipantFactory } from '../../managers/ParticipantFactory';\nimport { filterAs } from '../../operators';\nimport { getValueFrom } from '../../utils/getValueFrom';\nimport { getLogger } from '../../utils/logger';\nimport { InvalidParams, JSONRPCError, UnimplementedError } from '../errors';\nimport { buildRPCRequest } from '../RPCMessages';\nimport { isJSONRPCErrorResponse } from '../RPCMessages/guards/base.guards';\nimport {\n isCallStateMetadata,\n isCallUpdatedMetadata,\n isLayoutChangedMetadata,\n isMemberJoinedMetadata,\n isMemberLeftMetadata,\n isMemberTalkingMetadata,\n isMemberUpdatedMetadata,\n isSignalwireCallMetadata,\n isWebrtcMessageMetadata\n} from '../RPCMessages/guards/events.guards';\n\nimport type { Address } from './Address';\nimport type { Participant, SelfParticipant } from './Participant';\nimport type { ClientSession } from '../../interfaces/ClientSession';\nimport type { DeviceController } from '../../interfaces/DeviceController';\nimport type { JSONRPCParams } from '../RPCMessages';\nimport type {\n CallStatus,\n CallOptions,\n CallManager,\n CallParticipant,\n CallSelfParticipant\n} from './types/call.types';\nimport type { WebRTCVerto } from '../../interfaces/WebRTCVerto';\nimport type { CallEventsManager } from '../../managers/CallEventsManager';\nimport type { TransferOptions } from '../../managers/types/verto-manager.types';\nimport type { CallError } from '../errors';\nimport type { JSONRPCRequest, JSONRPCResponse } from '../RPCMessages/types/base';\nimport type { LayoutLayer, MemberTarget } from '../RPCMessages/types/common';\nimport type {\n CallStatePayload,\n CallUpdatedPayload,\n LayoutChangedPayload,\n MemberJoinedPayload,\n MemberLeftPayload,\n MemberTalkingPayload,\n MemberUpdatedPayload\n} from '../RPCMessages/types/events';\nimport type { CallDirection, VideoPosition } from '../types/call.types';\nimport type { MediaOptions, MediaDirections } from '../types/media.types';\nimport type { PendingRPCOptions } from '../utils';\nimport type { Observable, BehaviorSubject } from 'rxjs';\n\nconst logger = getLogger();\n\n/**\n * Manager instances returned by initialization callback\n */\nexport interface CallManagers {\n vertoManager: WebRTCVerto;\n callEventsManager: CallEventsManager;\n}\n\n/**\n * Initialization callback that creates managers for a Call instance\n * @param call - The WebRTCCall instance being initialized\n * @returns Manager instances for the call\n */\nexport type ManagerInitializer = (call: WebRTCCall) => CallManagers;\n\n/**\n * Required initialization configuration for Call constructor.\n * Calls must be created via {@link CallFactory} which provides these dependencies.\n */\nexport interface CallInitialization {\n /**\n * Callback function that creates and wires manager instances\n */\n initializeManagers: ManagerInitializer;\n /**\n * Device controller for media device access\n */\n deviceController: DeviceController;\n}\n\nconst fromDestinationParams = (destination?: string): Record<string, unknown> => {\n if (!destination) return {};\n try {\n const url = new URL(`destination:${destination}`);\n const params: Record<string, unknown> = {};\n url.searchParams.forEach((value, key) => {\n params[key] = value;\n });\n return params;\n } catch (error) {\n logger.warn(`Failed to parse destination URI: ${destination}`, error);\n return {};\n }\n};\n\n/**\n * Concrete WebRTC call implementation.\n *\n * Manages the full lifecycle of a call including signaling, media streams,\n * participants, layout, and event routing. Created via {@link SignalWire.dial}\n * or received as an inbound call.\n */\nexport class WebRTCCall extends Destroyable implements CallManager {\n /** Unique identifier for this call. */\n public readonly id: string;\n /** Destination URI this call was placed to. */\n public to?: string;\n private vertoManager!: WebRTCVerto;\n private callEventsManager!: CallEventsManager;\n private participantFactory: ParticipantFactory;\n private _errors$ = this.createReplaySubject<CallError>(1);\n private _status$: BehaviorSubject<CallStatus>;\n private _lastMergedStatus: CallStatus = 'new';\n private _answered$ = this.createReplaySubject<boolean>();\n private _answerMediaOptions?: MediaOptions;\n private _holdState = false;\n private _userVariables$ = this.createBehaviorSubject<Record<string, unknown>>({\n ...PreferencesContainer.instance.userVariables\n });\n\n constructor(\n public clientSession: ClientSession,\n public options: CallOptions,\n initialization: CallInitialization,\n public address?: Address\n ) {\n super();\n this.id = options.callId ?? uuid();\n this.to = options.to;\n this._userVariables$.next({\n ...this._userVariables$.value,\n ...fromDestinationParams(options.to),\n ...options.userVariables\n });\n\n this.subscribeTo(this.webrtcMessages$, (message) => {\n const userVars = getValueFrom<Record<string, unknown>>(message, 'params.userVariables');\n if (userVars) {\n this._userVariables$.next({\n ...this._userVariables$.value,\n ...userVars\n });\n }\n });\n\n const managers = initialization.initializeManagers(this);\n this.vertoManager = managers.vertoManager;\n this.callEventsManager = managers.callEventsManager;\n\n if (options.initOffer) {\n this._status$ = this.createBehaviorSubject<CallStatus>('ringing');\n this._lastMergedStatus = 'ringing';\n } else {\n this._status$ = this.createBehaviorSubject<CallStatus>('new');\n }\n\n const { deviceController } = initialization;\n\n // Create participant factory with bound executeMethod\n this.participantFactory = new ParticipantFactory(\n this.executeMethod.bind(this),\n this.vertoManager,\n deviceController\n );\n }\n\n /** Observable stream of errors from media, signaling, and peer connection layers. */\n public get errors$(): Observable<CallError> {\n return this.deferEmission(this._errors$.asObservable());\n }\n\n /**\n * @internal Push an error to the call's error stream.\n * Fatal errors automatically transition the call to `'failed'` and destroy it.\n */\n public emitError(callError: CallError): void {\n if (this._status$.value === 'destroyed' || this._status$.value === 'failed') return;\n this._errors$.next(callError);\n if (callError.fatal) {\n this._status$.next('failed');\n this.destroy();\n }\n }\n\n /** Whether this call is `'inbound'` or `'outbound'`. */\n public get direction(): CallDirection {\n return this.options.initOffer ? 'inbound' : 'outbound';\n }\n\n /** Observable of the address associated with this call. */\n public get address$(): Observable<Address | undefined> {\n return this.deferEmission(from([this.address])).pipe(takeUntil(this._destroyed$));\n }\n\n /** Display name of the caller. */\n public get fromName(): string | undefined {\n return this.options.fromName;\n }\n\n /** Address URI of the caller. */\n public get from(): string | undefined {\n return this.options.from;\n }\n\n /** Display name of the callee. */\n public get toName(): string | undefined {\n return this.options.toName;\n }\n\n /** Toggles whether incoming video is received. @throws {UnimplementedError} Not yet implemented. */\n // eslint-disable-next-line @typescript-eslint/require-await\n public async toggleIncomingVideo(): Promise<void> {\n throw new UnimplementedError();\n }\n\n /** Toggles whether incoming audio is received. @throws {UnimplementedError} Not yet implemented. */\n // eslint-disable-next-line @typescript-eslint/require-await\n public async toggleIncomingAudio(): Promise<void> {\n throw new UnimplementedError();\n }\n\n /** @internal Registers an additional call ID for event routing. */\n public addCallId(callId: string): void {\n this.callEventsManager.addCallId(callId);\n }\n\n /** List of capabilities available in the current call. */\n public get capabilities(): string[] {\n return this.callEventsManager.capabilities;\n }\n\n /** Current snapshot of all participants in the call. */\n public get participants(): CallParticipant[] {\n return this.callEventsManager.participants;\n }\n\n /** The local participant, or `null` if not yet joined. */\n public get self(): CallSelfParticipant | null {\n return this.callEventsManager.self;\n }\n\n async toggleLock(): Promise<void> {\n const method = this.locked ? 'call.unlock' : 'call.lock';\n await this.executeMethod(this.selfId ?? '', method, {});\n }\n\n async toggleHold(): Promise<void> {\n if (this._holdState) {\n await this.vertoManager.unhold();\n } else {\n await this.vertoManager.hold();\n }\n this._holdState = !this._holdState;\n }\n\n // eslint-disable-next-line @typescript-eslint/require-await\n async startRecording(): Promise<void> {\n // NEEDS check backend API status\n throw new UnimplementedError();\n }\n\n // eslint-disable-next-line @typescript-eslint/require-await\n async startStreaming(): Promise<void> {\n // V3 VIDEO\n // NEEDS check backend API status\n throw new UnimplementedError();\n }\n\n // eslint-disable-next-line @typescript-eslint/require-await\n async setMeta(_meta: Record<string, unknown>): Promise<void> {\n // NEEDS backend implementation\n throw new UnimplementedError();\n }\n // eslint-disable-next-line @typescript-eslint/require-await\n async updateMeta(_meta: Record<string, unknown>): Promise<void> {\n // NEEDS backend implementation\n throw new UnimplementedError();\n }\n\n /** Observable of layout layer positions for all participants. */\n public get layoutLayers$(): Observable<LayoutLayer[]> {\n return this.deferEmission(this.callEventsManager.layoutLayers$).pipe(\n takeUntil(this._destroyed$)\n );\n }\n\n /** Current snapshot of layout layers. */\n public get layoutLayers(): LayoutLayer[] {\n return this.callEventsManager.layoutLayers;\n }\n\n /** Executes a Verto RPC method targeting a specific participant. */\n public async executeMethod<T extends JSONRPCResponse = JSONRPCResponse>(\n target: string | MemberTarget,\n method: string,\n args: Record<string, unknown>\n ): Promise<T> {\n const params = this.buildMethodParams(target, args);\n\n const request = buildRPCRequest({\n method,\n params\n });\n\n try {\n const response: T = await this.clientSession.execute(request);\n if (isJSONRPCErrorResponse(response)) {\n throw new JSONRPCError(\n parseInt(response.result?.code ?? '0'),\n `Error response from method ${method}: ${response.result?.code} ${response.result?.message}`,\n undefined,\n undefined,\n request.id\n );\n }\n return response;\n } catch (error) {\n logger.error(`[Call] Error executing method ${method} with params`, params, error);\n throw error;\n }\n }\n\n private buildMethodParams(\n target: string | MemberTarget,\n args: Record<string, unknown>\n ): JSONRPCParams {\n const self: MemberTarget = {\n node_id: this.nodeId ?? '',\n call_id: this.id,\n member_id: this.vertoManager.selfId ?? ''\n };\n\n if (typeof target === 'object') {\n // Full MemberTarget provided — use targets array with the member's actual call_id\n return { ...args, self, targets: [target] };\n }\n\n // String member_id provided — use target singular with the call's node/call reference\n return {\n ...args,\n self,\n target: { node_id: this.nodeId ?? '', call_id: this.id, member_id: target }\n };\n }\n\n /** Observable of the current call status (e.g. `'ringing'`, `'connected'`). */\n public get status$(): Observable<CallStatus> {\n return this.publicCachedObservable('status$', () =>\n merge(this._status$.asObservable(), this.vertoManager.signalingStatus$).pipe(\n distinctUntilChanged(),\n tap((status) => {\n this._lastMergedStatus = status;\n })\n )\n );\n }\n /** Observable of the participants list, emits on join/leave/update. */\n public get participants$(): Observable<CallParticipant[]> {\n return this.deferEmission(this.callEventsManager.participants$).pipe(\n takeUntil(this._destroyed$)\n );\n }\n /** Observable of the local (self) participant. */\n public get self$(): Observable<CallSelfParticipant> {\n return this.deferEmission(this.callEventsManager.self$).pipe(takeUntil(this._destroyed$));\n }\n /** Observable indicating whether the call is being recorded. */\n public get recording$(): Observable<boolean> {\n return this.deferEmission(this.callEventsManager.recording$).pipe(takeUntil(this._destroyed$));\n }\n\n /** Observable indicating whether the call is being streamed. */\n public get streaming$(): Observable<boolean> {\n return this.deferEmission(this.callEventsManager.streaming$).pipe(takeUntil(this._destroyed$));\n }\n\n /** Observable indicating whether raise-hand priority is active. */\n public get raiseHandPriority$(): Observable<boolean> {\n return this.deferEmission(this.callEventsManager.raiseHandPriority$).pipe(\n takeUntil(this._destroyed$)\n );\n }\n\n /** Observable indicating whether the call room is locked. */\n public get locked$(): Observable<boolean> {\n return this.deferEmission(this.callEventsManager.locked$).pipe(takeUntil(this._destroyed$));\n }\n\n /** Observable of custom metadata associated with the call. */\n public get meta$(): Observable<Record<string, unknown>> {\n return this.deferEmission(this.callEventsManager.meta$).pipe(takeUntil(this._destroyed$));\n }\n\n /** Observable of the call's capability flags. */\n public get capabilities$(): Observable<string[]> {\n return this.deferEmission(this.callEventsManager.capabilities$).pipe(\n takeUntil(this._destroyed$)\n );\n }\n\n /** Observable of the current layout name. */\n public get layout$(): Observable<string> {\n return this.deferEmission(this.callEventsManager.layout$).pipe(takeUntil(this._destroyed$));\n }\n\n /** Current call status. */\n public get status(): CallStatus {\n return this._lastMergedStatus;\n }\n\n /** Whether the call is currently being recorded. */\n public get recording(): boolean {\n return this.callEventsManager.recording;\n }\n\n /** Whether the call is currently being streamed. */\n public get streaming(): boolean {\n return this.callEventsManager.streaming;\n }\n\n /** Whether raise-hand priority is active. */\n public get raiseHandPriority(): boolean {\n return this.callEventsManager.raiseHandPriority;\n }\n\n /** Whether the call room is locked. */\n public get locked(): boolean {\n return this.callEventsManager.locked;\n }\n\n /** Current custom metadata of the call. */\n public get meta(): Record<string, unknown> {\n return this.callEventsManager.meta;\n }\n\n /** Current layout name, or `undefined` if not set. */\n public get layout(): string | undefined {\n return this.callEventsManager.layout;\n }\n\n /** Observable of available layout names. */\n public get layouts$(): Observable<string[]> {\n return this.deferEmission(this.callEventsManager.layouts$).pipe(takeUntil(this._destroyed$));\n }\n\n /** Current snapshot of available layout names. */\n public get layouts(): string[] {\n return this.callEventsManager.layouts;\n }\n\n /** Observable of the local media stream (camera/microphone). */\n public get localStream$(): Observable<MediaStream> {\n return this.deferEmission(this.vertoManager.localStream$).pipe(takeUntil(this._destroyed$));\n }\n /** Current local media stream, or `null` if not available. */\n public get localStream(): MediaStream | null {\n return this.vertoManager.localStream;\n }\n /** Observable of the remote media stream from the far end. */\n public get remoteStream$(): Observable<MediaStream> {\n return this.deferEmission(this.vertoManager.remoteStream$).pipe(takeUntil(this._destroyed$));\n }\n /** Current remote media stream, or `null` if not available. */\n public get remoteStream(): MediaStream | null {\n return this.vertoManager.remoteStream;\n }\n\n /** Observable of custom user variables associated with the call. */\n public get userVariables$(): Observable<Record<string, unknown>> {\n return this.deferEmission(this._userVariables$.asObservable());\n }\n\n /** a copy of the current custom user variables of the call. */\n public get userVariables(): Record<string, unknown> {\n return { ...this._userVariables$.value };\n }\n\n /** Merge current custom user variables of the call. */\n public set userVariables(variables: Record<string, unknown>) {\n this._userVariables$.next({ ...this._userVariables$.value, ...variables });\n }\n\n /** @internal */\n public createParticipant(\n memberId: string,\n selfId?: string | null\n ): Participant | SelfParticipant {\n // Use provided selfId (from call.joined event) or fall back to vertoManager.selfId\n const effectiveSelfId = selfId ?? this.vertoManager.selfId;\n if (memberId === effectiveSelfId) {\n return this.participantFactory.createSelfParticipant(memberId);\n }\n return this.participantFactory.createParticipant(memberId);\n }\n\n /** Observable of the current audio/video send/receive directions. */\n public get mediaDirections$(): Observable<MediaDirections> {\n return this.deferEmission(this.vertoManager.mediaDirections$).pipe(takeUntil(this._destroyed$));\n }\n\n /** Current audio/video send/receive directions. */\n public get mediaDirections(): MediaDirections {\n return this.vertoManager.mediaDirections;\n }\n\n protected get participantsId$(): Observable<string[]> {\n return this.cachedObservable('participantsId$', () =>\n this.participants$.pipe(\n map((participants) => participants.map((participant) => participant.id))\n )\n );\n }\n\n /** Executes a raw JSON-RPC request on the client session. */\n public async execute<T extends JSONRPCResponse = JSONRPCResponse>(\n request: JSONRPCRequest,\n options?: PendingRPCOptions\n ): Promise<T> {\n return this.clientSession.execute(request, options);\n }\n\n /** Observable of the local participant's member ID. */\n public get selfId$(): Observable<string | null> {\n return this.vertoManager.selfId$;\n }\n\n /** Local participant's member ID, or `null` if not joined. */\n public get selfId(): string | null {\n return this.vertoManager.selfId;\n }\n\n /** Observable of the server node ID handling this call. */\n public get nodeId$(): Observable<string | null> {\n return this.vertoManager.nodeId$;\n }\n\n /** Server node ID handling this call, or `null`. */\n public get nodeId(): string | null {\n return this.vertoManager.nodeId;\n }\n\n private isCallSessionEvent(event: unknown): event is Event {\n try {\n logger.debug('[Call] Checking if event is for this call session:', event);\n const callId =\n getValueFrom<string>(event, 'params.params.callID') ??\n getValueFrom<string>(event, 'params.call_id');\n const roomSessionId = getValueFrom<string>(event, 'params.room_session_id');\n logger.debug(\n `[Call] Extracted session identifiers callID: ${callId} and roomSessionID: ${roomSessionId} from event:`\n );\n return (\n callId === this.id ||\n (!!callId && this.callEventsManager.isCallIdValid(callId)) ||\n (!!roomSessionId && this.callEventsManager.isRoomSessionIdValid(roomSessionId))\n );\n } catch (error) {\n logger.error('[Call] Error checking if event is for this call session:', error);\n return false;\n }\n }\n\n private get callSessionEvents$() {\n return this.cachedObservable('callSessionEvents$', () =>\n this.clientSession.signalingEvent$.pipe(\n filter((event) => this.isCallSessionEvent(event)),\n tap((event) => logger.debug('[Call] Received call session event:', event)),\n takeUntil(this.destroyed$),\n share()\n )\n );\n }\n\n /** Observable of call-updated events. */\n public get callUpdated$(): Observable<CallUpdatedPayload> {\n return this.publicCachedObservable('callUpdated$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isCallUpdatedMetadata, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n /** Observable of member-joined events. */\n public get memberJoined$(): Observable<MemberJoinedPayload> {\n return this.publicCachedObservable('memberJoined$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isMemberJoinedMetadata, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Observable of member-left events. */\n public get memberLeft$(): Observable<MemberLeftPayload> {\n return this.publicCachedObservable('memberLeft$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isMemberLeftMetadata, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n /** Observable of member-updated events (mute, volume, etc.). */\n public get memberUpdated$(): Observable<MemberUpdatedPayload> {\n return this.publicCachedObservable('memberUpdated$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isMemberUpdatedMetadata, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Observable of member-talking events (speech start/stop). */\n public get memberTalking$(): Observable<MemberTalkingPayload> {\n return this.publicCachedObservable('memberTalking$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isMemberTalkingMetadata, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Observable of call state-change events. */\n public get callStates$(): Observable<CallStatePayload> {\n return this.publicCachedObservable('callStates$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isCallStateMetadata, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Observable of layout-changed events. */\n public get layoutUpdates$(): Observable<LayoutChangedPayload> {\n return this.publicCachedObservable('layoutUpdates$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isLayoutChangedMetadata, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Underlying `RTCPeerConnection`, for advanced use cases. */\n public get rtcPeerConnection(): RTCPeerConnection | undefined {\n return this.vertoManager.mainPeerConnection.peerConnection;\n }\n /** Observable of raw signaling events as plain objects. */\n public get signalingEvent$(): Observable<Record<string, unknown>> {\n return this.publicCachedObservable('signalingEvent$', () =>\n this.callEvent$.pipe(\n map((event) => JSON.parse(JSON.stringify(event)) as Record<string, unknown>)\n )\n );\n }\n\n /** Observable of WebRTC-specific signaling messages. */\n // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\n public get webrtcMessages$() {\n return this.cachedObservable('webrtcMessages$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isWebrtcMessageMetadata, 'params'),\n tap((event) => logger.debug('[Call] Event is a WebRTC message event:', event)),\n takeUntil(this.destroyed$),\n share()\n )\n );\n }\n\n /** Observable of call-level signaling events. */\n // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\n public get callEvent$() {\n return this.cachedObservable('callEvent$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isSignalwireCallMetadata, 'params'),\n tap((event) => logger.debug('[Call] Event is a call event:', event)),\n takeUntil(this.destroyed$),\n share()\n )\n );\n }\n\n /** Observable of layout-changed signaling events. */\n // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\n public get layoutEvent$() {\n return this.cachedObservable('layoutEvent$', () =>\n this.callEvent$.pipe(filterAs(isLayoutChangedMetadata, 'params'))\n );\n }\n\n /** Hangs up the call and releases all resources. */\n async hangup(): Promise<void> {\n this._status$.next('disconnecting');\n try {\n await this.vertoManager.bye();\n } finally {\n this.destroy();\n }\n }\n\n /** Sends DTMF digits (e.g. `'1234#'`) on the call. */\n async sendDigits(dtmf: string): Promise<void> {\n return this.vertoManager.sendDigits(dtmf);\n }\n\n /** Accepts an inbound call, optionally overriding media options for the answer. */\n public answer(options?: MediaOptions): void {\n this._answerMediaOptions = options;\n this._answered$.next(true);\n }\n\n /** Media options provided when answering. Used internally by the VertoManager. */\n public get answerMediaOptions(): MediaOptions | undefined {\n return this._answerMediaOptions;\n }\n\n /** Rejects an inbound call. */\n public reject(): void {\n this._answered$.next(false);\n }\n\n /** Observable that emits `true` when answered, `false` when rejected. */\n public get answered$(): Observable<boolean> {\n return this.deferEmission(this._answered$.asObservable());\n }\n\n /**\n * Sets the call layout and participant positions.\n * @param layout - Layout name (must be one of {@link layouts}).\n * @param positions - Map of member IDs to video positions.\n */\n async setLayout(layout: string, positions: Record<string, VideoPosition>): Promise<void> {\n if (!this.layouts.includes(layout)) {\n throw new InvalidParams(\n `Layout ${layout} is not available in the current call layouts: ${this.layouts.join(', ')}`\n );\n }\n\n const selfId = await firstValueFrom(\n this.selfId$.pipe(filter((id): id is string => id !== null))\n );\n\n await this.executeMethod(selfId, 'call.layout.set', {\n layout,\n positions\n });\n }\n\n /** Transfers the call to other participants. */\n public async transfer(options: TransferOptions): Promise<void> {\n return this.vertoManager.transfer(options);\n }\n\n /** Destroys the call, releasing all resources and subscriptions. */\n public destroy(): void {\n if (this._status$.value === 'destroyed') return;\n this._status$.next('destroyed');\n this.vertoManager.destroy();\n this.callEventsManager.destroy();\n super.destroy();\n }\n}\n","import { CallEventsManager } from './CallEventsManager';\nimport { WebRTCVertoManager } from './VertoManager';\nimport { WebRTCCall } from '../core/entities/Call';\nimport {\n JSONRPCError,\n MediaTrackError,\n RPCTimeoutError,\n TransportConnectionError,\n VertoPongError,\n WebSocketConnectionError\n} from '../core/errors';\n\nimport type { AttachManager } from './AttachManager';\nimport type { Address } from '../core/entities/Address';\nimport type { CallOptions } from '../core/entities/types/call.types';\nimport type { CallError } from '../core/errors';\nimport type { WebRTCApiProvider } from '../dependencies/interfaces';\nimport type { ClientSession } from '../interfaces/ClientSession';\nimport type { DeviceController } from '../interfaces/DeviceController';\n\n/**\n * Infers the semantic error category from a raw Error thrown by VertoManager\n * or an RTCPeerConnection layer.\n */\nfunction inferCallErrorKind(error: Error): CallError['kind'] {\n if (error instanceof RPCTimeoutError) return 'timeout';\n if (error instanceof JSONRPCError) return 'signaling';\n if (error instanceof MediaTrackError) return 'media';\n if (error instanceof WebSocketConnectionError || error instanceof TransportConnectionError)\n return 'network';\n return 'internal';\n}\n\n/** Determines whether an error should be fatal (destroy the call). */\nfunction isFatalError(error: Error): boolean {\n // Transient signaling issues — the call may survive\n if (error instanceof VertoPongError) return false;\n // Media device errors degrade quality but don't end the call\n if (error instanceof MediaTrackError) return false;\n // All other errors (JSONRPCError on invite, connection failures, etc.) are fatal\n return true;\n}\n\n/**\n * Factory for creating WebRTCCall instances with proper manager wiring.\n * Eliminates circular dependencies by centralizing Call and Manager creation.\n */\nexport class CallFactory {\n constructor(\n private sessionManager: ClientSession,\n private deviceController: DeviceController,\n private attachManager: AttachManager,\n private webRTCApiProvider: WebRTCApiProvider\n ) {}\n\n /**\n * Create a new WebRTCCall with properly initialized managers\n */\n createCall(address: Address | undefined, options: CallOptions): WebRTCCall {\n const call = new WebRTCCall(\n this.sessionManager,\n options,\n {\n initializeManagers: (callInstance: WebRTCCall) => {\n const vertoManager = new WebRTCVertoManager(\n callInstance,\n this.attachManager,\n this.deviceController,\n this.webRTCApiProvider,\n {\n nodeId: options.nodeId,\n onError: (error: Error) => {\n const callError: CallError = {\n kind: inferCallErrorKind(error),\n fatal: isFatalError(error),\n error,\n callId: callInstance.id\n };\n callInstance.emitError(callError);\n }\n }\n );\n\n const callEventsManager = new CallEventsManager(callInstance);\n\n return {\n vertoManager,\n callEventsManager\n };\n },\n deviceController: this.deviceController\n },\n address\n );\n\n return call;\n }\n}\n","import {\n defer,\n distinctUntilChanged,\n filter,\n from,\n map,\n ReplaySubject,\n shareReplay,\n Subject,\n switchMap,\n takeUntil,\n skip,\n pipe\n} from 'rxjs';\n\nimport { Destroyable } from './Destroyable';\nimport { GET_PARAMS } from '../controllers/HTTPRequestController';\nimport { CollectionFetchError } from '../core/errors';\nimport { getLogger } from '../utils/logger';\n\nimport type {\n PaginatedResponse,\n Entity,\n FetchController,\n Collection\n} from './types/collection.types';\nimport type { HTTPRequestController } from '../controllers/HTTPRequestController';\nimport type { Observable, Subscription } from 'rxjs';\n\nconst logger = getLogger();\n\nexport class Fetcher<T extends Entity = Entity> implements FetchController<Entity> {\n private nextUrl?: string;\n public hasMore: boolean | undefined;\n public filter = (_item: Entity): _item is T => true;\n public mapper = (item: unknown): T => item as T;\n\n constructor(\n protected endpoint: string,\n params: string,\n protected http: HTTPRequestController\n ) {\n this.nextUrl = `${this.endpoint}?${params}`;\n }\n\n public async next(): Promise<T[]> {\n if (!this.nextUrl) {\n this.hasMore = false;\n return [];\n }\n\n const response = await this.http.request({\n ...GET_PARAMS,\n url: this.nextUrl\n });\n if (response.ok && !!response.body) {\n const result = JSON.parse(response.body) as PaginatedResponse<T>;\n this.nextUrl = result.links.next;\n this.hasMore = !!this.nextUrl;\n const filtered = result.data.filter(this.filter);\n return filtered.map(this.mapper);\n }\n logger.error('Failed to fetch entity');\n return [];\n }\n\n public async id(v: unknown): Promise<T | undefined> {\n const response = await this.http.request({\n ...GET_PARAMS,\n url: `${this.endpoint}/${String(v)}`\n });\n if (response.ok && !!response.body) {\n return JSON.parse(response.body) as T;\n }\n }\n}\n\nexport class EntityCollection<T extends Entity = Entity>\n extends Destroyable\n implements Collection<T>\n{\n public loading$ = this.createBehaviorSubject<boolean>(false);\n public values$ = this.createReplaySubject<T[]>(1);\n public hasMore$: Observable<boolean>;\n private collectionData = new Map<string, T>();\n private observablesRegistry = new Map<string, ReplaySubject<T>>();\n private updateSubscription: Subscription;\n private upsertData = (data: Partial<T>) => {\n if (!data.id) return;\n const existing = this.collectionData.get(data.id) ?? {};\n const updated = { ...existing, ...data } as T;\n this.collectionData.set(data.id, updated);\n this.observablesRegistry.get(data.id)?.next(updated);\n this.values$.next(Array.from(this.collectionData.values()));\n };\n private _hasMore$ = this.createBehaviorSubject<boolean>(true);\n\n private _destroy$ = new Subject<void>();\n constructor(\n private fetchController: FetchController<T>,\n private update$: Observable<Partial<T>>,\n private readonly onError?: (error: Error) => void\n ) {\n super();\n this.updateSubscription = this.update$.subscribe(this.upsertData);\n this.loading$.next(false);\n this.hasMore$ = defer(() => from(this.init())).pipe(\n switchMap(() => this._hasMore$),\n distinctUntilChanged(),\n shareReplay(1),\n takeUntil(this._destroy$)\n );\n }\n public get loading(): boolean {\n return this.loading$.value;\n }\n\n public get hasMore(): boolean {\n return this.fetchController.hasMore ?? true;\n }\n\n public get updated$(): Observable<void> {\n return this.cachedObservable('updated$', () =>\n this.loading$.pipe(\n distinctUntilChanged(),\n skip(1), // skiping the loading === true event\n filter((loading) => !loading),\n map(() => void 0),\n takeUntil(this._destroy$)\n )\n );\n }\n\n public get values(): T[] {\n return Array.from(this.collectionData.values());\n }\n\n private async init(): Promise<void> {\n if (this.fetchController.hasMore === false) {\n this._hasMore$.next(false);\n return;\n }\n await this.fetchMore();\n }\n\n private async fetchMore(): Promise<void> {\n try {\n this.loading$.next(true);\n const datas = await this.fetchController.next();\n datas.forEach(this.upsertData);\n this._hasMore$.next(this.fetchController.hasMore ?? false);\n this.loading$.next(false);\n } catch (error) {\n logger.error(`Failed to fetch initial collection data`, error);\n this._hasMore$.next(this.fetchController.hasMore ?? false);\n this.loading$.next(false);\n this.onError?.(new CollectionFetchError('fetchMore', error));\n }\n }\n\n private async tryFetch(key: keyof FetchController<T>, value: unknown): Promise<T | undefined> {\n try {\n this.loading$.next(true);\n const data = await this.fetchController[key]?.(value);\n this.loading$.next(false);\n if (data) {\n this.upsertData(data);\n }\n return data;\n } catch (error) {\n logger.error(`Failed to fetch data for (${String(key)}:${String(value)}) :`, error);\n this.loading$.next(false);\n this.onError?.(new CollectionFetchError(`tryFetch(${String(key)})`, error));\n }\n }\n\n public get$(id: string): Observable<T> | undefined {\n if (!this.observablesRegistry.has(id)) {\n this.observablesRegistry.set(id, new ReplaySubject<T>(1));\n const data = this.collectionData.get(id);\n if (data) {\n this.observablesRegistry.get(id)?.next(data);\n } else {\n void this.tryFetch('id', id);\n }\n }\n return this.observablesRegistry.get(id)?.asObservable();\n }\n\n public async find$(key: keyof T, value: unknown): Promise<Observable<T> | undefined> {\n const data =\n Array.from(this.collectionData.values()).find((item) => item[key] === value) ??\n (await this.tryFetch(key, value));\n\n return data ? this.get$(data.id) : undefined;\n }\n\n public loadMore(): void {\n if (this.fetchController.hasMore !== false) {\n void this.fetchMore();\n }\n }\n\n public destroy(): void {\n this._destroy$.next();\n this._destroy$.complete();\n this.updateSubscription.unsubscribe();\n this.loading$.complete();\n this.observablesRegistry.forEach((subject) => subject.complete());\n }\n}\n\nexport class EntityCollectionTransformed<\n O extends Entity = Entity,\n T extends Entity = Entity\n> implements Collection<T> {\n private _values$?: Observable<T[]>;\n\n constructor(\n private originalCollection: EntityCollection<O>,\n private filter: (i: unknown) => i is O = (i): i is O => !!i,\n private mapper: (item: O) => T = (item) => item as unknown as T\n ) {}\n\n public get loading$(): Observable<boolean> {\n return this.originalCollection.loading$;\n }\n\n public get loading(): boolean {\n return this.originalCollection.loading;\n }\n public get hasMore$(): Observable<boolean> {\n return this.originalCollection.hasMore$;\n }\n public get hasMore(): boolean {\n return this.originalCollection.hasMore;\n }\n public get values(): T[] {\n return this.originalCollection.values.filter(this.filter).map(this.mapper);\n }\n public get values$(): Observable<T[]> {\n return (this._values$ ??= this.originalCollection.values$.pipe(\n map((values) => values.filter(this.filter).map(this.mapper))\n ));\n }\n\n public get$(id: string): Observable<T> | undefined {\n const original$ = this.originalCollection.get$(id);\n return !original$ ? original$ : original$.pipe(pipe(filter(this.filter), map(this.mapper)));\n }\n\n public async find$(key: keyof T, value: unknown): Promise<Observable<T> | undefined> {\n const original$ = await this.originalCollection.find$(key as keyof O, value);\n return !original$ ? original$ : original$.pipe(pipe(filter(this.filter), map(this.mapper)));\n }\n public loadMore(): void {\n this.originalCollection.loadMore();\n }\n public destroy(): void {\n this.originalCollection.destroy();\n }\n}\n","import { defer, map, shareReplay, takeUntil, type Observable } from 'rxjs';\n\nimport { EntityCollectionTransformed } from '../../behaviors/Collection';\nimport { Destroyable } from '../../behaviors/Destroyable';\nimport { filterNull } from '../../operators';\nimport { DependencyError, UnimplementedError } from '../errors';\n\nimport type { CallState } from './types/call.types';\nimport type { AddressProvider } from '../../interfaces/AddressProvider';\nimport type {\n ConversationMessageCollection,\n ConversationsProvider\n} from '../../interfaces/Conversations';\nimport type { GetAddressResponse } from '../types/address.types';\nimport type { ResourceType } from '../types/common.types';\nimport type {\n AddressHistory,\n AddressHistoryCollection,\n GetConversationMessageResponse,\n TextMessage,\n TextMessageCollection\n} from '../types/conversation.types';\n\ntype AddressState = GetAddressResponse;\n/**\n * Represents a contact or room in the directory.\n *\n * Provides identity metadata, conversation history, text messaging,\n * and activity state for an address entry.\n */\nexport class Address extends Destroyable {\n private initConversationMessages = async (): Promise<ConversationMessageCollection> => {\n this._conversationMessages =\n this._conversationMessages ??\n (await this.conversationManager.getConversationMessageCollection(this.id));\n if (this._conversationMessages.hasMore) {\n this._conversationMessages.loadMore();\n }\n return this._conversationMessages;\n };\n\n /** Observable of text messages for this address. Lazily loads conversation data. */\n public textMessages$ = defer(this.initConversationMessages).pipe(\n map(() => this.textMessage),\n shareReplay(1),\n takeUntil(this.destroyed$)\n );\n /** Observable of call history for this address. Lazily loads conversation data. */\n public history$ = defer(this.initConversationMessages).pipe(\n map(() => this.history),\n shareReplay(1),\n takeUntil(this.destroyed$)\n );\n private _conversationMessages?: ConversationMessageCollection;\n\n private _state$ = this.createBehaviorSubject<AddressState | null>(null);\n\n private _history$?: AddressHistoryCollection<Address>;\n\n private _textMessages$?: TextMessageCollection<Address>;\n\n // FIXME after presence API is available\n // this should be a dynamic view of the address existing calls,\n // independent if the user is on the call or not.\n // private _callsStates$ = this.createBehaviorSubject<CallState[]>([]);\n\n constructor(\n private readonly addressId: string,\n private conversationManager: ConversationsProvider,\n private addressProvider: AddressProvider<Address>\n ) {\n super();\n }\n\n /** @internal */\n public upnext(state: Partial<AddressState>): void {\n const update = {\n ...this._state$.value,\n ...state\n } as AddressState;\n this._state$.next(update);\n }\n\n /** @internal */\n public get state(): AddressState | null {\n return this._state$.value;\n }\n\n /** Unique address identifier. */\n public get id(): string {\n return this.addressId;\n }\n /** Address name (resource identifier). */\n public get name(): string {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.name;\n }\n\n /** ISO timestamp of when the address was created. */\n public get createdAt(): string {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.created_at;\n }\n\n /** Default communication channel URI (video for rooms, audio otherwise). */\n public get defaultChannel(): string | undefined {\n return this.type === 'room' ? this.channels.video : this.channels.audio;\n }\n\n /** Observable of the human-readable display name. */\n public get displayName$(): Observable<string> {\n return this.cachedObservable('displayName$', () =>\n this._state$.pipe(\n filterNull(),\n map((state) => state.display_name),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Human-readable display name. */\n public get displayName(): string {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.display_name;\n }\n\n /** Observable of the preview image URL. */\n public get previewUrl$(): Observable<string | undefined> {\n return this.cachedObservable('previewUrl$', () =>\n this._state$.pipe(\n filterNull(),\n map((state) => state.preview_url),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Preview image URL. */\n public get previewUrl(): string | undefined {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.preview_url;\n }\n\n /** Observable of the cover image URL. */\n public get coverUrl$(): Observable<string | undefined> {\n return this.cachedObservable('coverUrl$', () =>\n this._state$.pipe(\n filterNull(),\n shareReplay(1),\n map((state) => state.cover_url),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Cover image URL. */\n public get coverUrl(): string | undefined {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.cover_url;\n }\n\n /** Observable of the underlying resource ID. */\n public get resourceId$(): Observable<string> {\n return this.cachedObservable('resourceId$', () =>\n this._state$.pipe(\n filterNull(),\n shareReplay(1),\n map((state) => state.resource_id),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Underlying resource ID. */\n public get resourceId(): string {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.resource_id;\n }\n\n /** Observable of the resource type (e.g. `'room'`, `'subscriber'`). */\n public get type$(): Observable<ResourceType> {\n return this.cachedObservable('type$', () =>\n this._state$.pipe(\n filterNull(),\n shareReplay(1),\n map((state) => state.type),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Resource type (e.g. `'room'`, `'subscriber'`). */\n public get type(): ResourceType {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.type;\n }\n\n /** Observable of available communication channels (audio, video, messaging). */\n public get channels$(): Observable<{\n audio?: string;\n messaging?: string;\n video?: string;\n }> {\n return this.cachedObservable('channels$', () =>\n this._state$.pipe(\n filterNull(),\n shareReplay(1),\n map((state) => state.channels),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Available communication channels. */\n public get channels(): {\n audio?: string;\n messaging?: string;\n video?: string;\n } {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.channels;\n }\n\n /** Whether the address (room) is locked. */\n public get locked(): boolean {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.locked;\n }\n\n /** Observable indicating whether the address (room) is locked. */\n public get locked$(): Observable<boolean> {\n return this.cachedObservable('locked$', () =>\n this._state$.pipe(\n filterNull(),\n shareReplay(1),\n map((state) => state.locked),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Sends a text message to this address. */\n public async sendText(text: string): Promise<void> {\n return this.conversationManager.sendText(text, this.id);\n }\n\n /** Collection of text messages for this address, with pagination support. */\n public get textMessage():\n | EntityCollectionTransformed<GetConversationMessageResponse, TextMessage<Address>>\n | undefined {\n if (!this._conversationMessages) {\n return;\n }\n this._textMessages$ =\n this._textMessages$ ??\n new EntityCollectionTransformed<GetConversationMessageResponse, TextMessage<Address>>(\n this._conversationMessages,\n (item): item is GetConversationMessageResponse =>\n (item as GetConversationMessageResponse).subtype === 'chat',\n (item) =>\n ({\n id: item.id,\n text: item.text,\n created: item.ts,\n fromAddress$: this.addressProvider.get$(item.from_fabric_address_id)\n }) as TextMessage<Address>\n );\n return this._textMessages$;\n }\n\n /** Collection of call history entries for this address, with pagination support. */\n public get history():\n | EntityCollectionTransformed<GetConversationMessageResponse, AddressHistory<Address>>\n | undefined {\n if (!this._conversationMessages) {\n return;\n }\n this._history$ =\n this._history$ ??\n new EntityCollectionTransformed<GetConversationMessageResponse, AddressHistory<Address>>(\n this._conversationMessages,\n (item): item is GetConversationMessageResponse =>\n (item as GetConversationMessageResponse).subtype === 'log',\n (item) =>\n ({\n id: item.id,\n kind: item.kind,\n status: item.details.status,\n started: item.details.start_time,\n ended: item.details.end_time,\n fromAddress$: this.addressProvider.get$(item.from_fabric_address_id)\n }) as AddressHistory<Address>\n );\n return this._history$;\n }\n\n /** Observable of active call states for this address. @throws {UnimplementedError} Requires presence support. */\n public get activity$(): Observable<CallState[]> {\n // NEEDS Presence\n throw new UnimplementedError();\n }\n\n /** Active call states for this address. @throws {UnimplementedError} Requires presence support. */\n public get activity(): CallState[] {\n // NEEDS Presence\n throw new UnimplementedError();\n }\n}\n","import { filter, NEVER, Observable, race, take } from 'rxjs';\nimport { v4 as uuid } from 'uuid';\n\nimport { InvalidListenerError, JSONRPCError, RPCTimeoutError } from './errors';\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { getLogger } from '../utils/logger';\n\nimport type { RPCConnectResult } from './RPCMessages';\nimport type { JSONRPCRequest, JSONRPCResponse } from './RPCMessages/types/base';\n\nconst logger = getLogger();\n\nexport async function callListener<T>(\n listener: ((value: T) => void) | ((value: T) => Promise<void>),\n value: T,\n onError?: (error: unknown) => void\n): Promise<void> {\n try {\n if (typeof listener !== 'function') {\n throw new InvalidListenerError();\n }\n await listener(value);\n } catch (error) {\n if (error instanceof InvalidListenerError) {\n logger.error(error.message);\n } else {\n logger.warn('Error calling listener:', error);\n }\n onError?.(error);\n }\n}\n\nexport const isRPCConnectResult = (e: unknown): e is RPCConnectResult => {\n logger.debug('isRPCConnectResult check:', e);\n if (!e || typeof e !== 'object') return false;\n\n // Check if this is a JSON-RPC response with a result property\n\n const result = e as RPCConnectResult;\n\n const is =\n typeof result.identity === 'string' &&\n typeof result.protocol === 'string' &&\n typeof result.authorization === 'object' &&\n typeof result.authorization.jti === 'string' &&\n typeof result.authorization.project_id === 'string' &&\n typeof result.authorization.fabric_subscriber === 'object';\n\n logger.debug('isRPCConnectResult check result:', is);\n return is;\n};\n\nexport interface PendingRPCOptions {\n /**\n * Timeout in milliseconds. Defaults to 5000ms (5 seconds).\n * If the response is not received within this time, the promise will reject with RPCTimeoutError.\n */\n timeoutMs?: number;\n\n /**\n * Optional AbortSignal for cancellation support.\n * If the signal is aborted, the promise will reject with an AbortError.\n */\n signal?: AbortSignal;\n}\n\nexport class PendingRPC<T extends JSONRPCResponse = JSONRPCResponse> {\n private static readonly defaultTimeoutMs = 5000;\n private id = uuid();\n\n public readonly request: JSONRPCRequest;\n public promise: Promise<T>;\n\n constructor(request: JSONRPCRequest, responses$: Observable<T>, options?: PendingRPCOptions) {\n logger.debug(\n `[PendingRPC(${this.id}) request:${request.id}: method:${request.method}] Creating PendingRPC`\n );\n this.request = request;\n\n const timeoutMs = options?.timeoutMs ?? PendingRPC.defaultTimeoutMs;\n const signal = options?.signal;\n\n this.promise = new Promise<T>((resolve, reject) => {\n // Check if already aborted\n if (signal?.aborted) {\n reject(new DOMException('The operation was aborted', 'AbortError'));\n return;\n }\n\n // Track if promise has been settled to prevent unhandled rejections\n let isSettled = false;\n\n // Create the main response observable\n const response$ = responses$.pipe(\n filter((result) => result.id === request.id),\n take(1)\n );\n\n // Create timeout observable\n const timeout$ = new Observable<never>((subscriber) => {\n const timer = setTimeout(() => {\n subscriber.error(new RPCTimeoutError(request.id, timeoutMs));\n }, timeoutMs);\n\n return () => clearTimeout(timer);\n });\n\n // Create abort observable if signal provided\n const abort$ = signal\n ? new Observable<never>((subscriber) => {\n const abortHandler = () => {\n subscriber.error(new DOMException('The operation was aborted', 'AbortError'));\n };\n signal.addEventListener('abort', abortHandler);\n\n return () => signal.removeEventListener('abort', abortHandler);\n })\n : NEVER; // Observable that never emits\n\n // Race between response, timeout, and abort\n const subscription = race(response$, timeout$, abort$).subscribe({\n next: (response) => {\n isSettled = true;\n if (response.error) {\n const rpcError = new JSONRPCError(\n response.error.code,\n response.error.message,\n response.error.data,\n undefined,\n request.id\n );\n logger.debug(\n `[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with RPC error:`,\n rpcError\n );\n reject(rpcError);\n } else {\n logger.debug(\n `[PendingRPC(${this.id}) request:${request.id}] Resolving promise with response:`,\n response\n );\n resolve(response);\n }\n subscription.unsubscribe();\n },\n error: (error) => {\n logger.debug(\n `[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with error:`,\n error\n );\n isSettled = true;\n reject(error as Error);\n subscription.unsubscribe();\n },\n complete: () => {\n logger.debug(`[PendingRPC(${this.id}) request:${request.id}] Observable completed`);\n if (!isSettled) {\n reject(new RPCTimeoutError(request.id, timeoutMs));\n }\n subscription.unsubscribe();\n }\n });\n });\n }\n\n // Make it thenable (Promise-like)\n async then<TResult1 = JSONRPCResponse, TResult2 = never>(\n onfulfilled?: ((value: JSONRPCResponse) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null\n ): Promise<TResult1 | TResult2> {\n return this.promise.then(onfulfilled, onrejected);\n }\n\n async catch<TResult = never>(\n onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null\n ): Promise<JSONRPCResponse | TResult> {\n return this.promise.catch(onrejected);\n }\n\n async finally(onfinally?: (() => void) | null): Promise<JSONRPCResponse> {\n return this.promise.finally(onfinally);\n }\n}\n\n// Re-export Destroyable for backward compatibility\nexport { Destroyable };\n","import {\n catchError,\n combineLatest,\n defer,\n filter,\n firstValueFrom,\n from,\n lastValueFrom,\n map,\n race,\n share,\n shareReplay,\n switchMap,\n take,\n takeUntil,\n tap,\n throwError,\n timeout,\n TimeoutError\n} from 'rxjs';\n\nimport { CallFactory } from './CallFactory';\nimport { Address } from '../core/entities/Address';\nimport {\n AuthStateHandlerError,\n CallCreateError,\n DependencyError,\n UnexpectedError,\n VertoInviteHandlerError\n} from '../core/errors';\nimport { RPCConnect, RPCReauthenticate } from '../core/RPCMessages';\nimport {\n isSignalwireAuthorizationStateMetadata,\n isSignalwireRequest,\n isWebrtcMessageMetadata\n} from '../core/RPCMessages/guards/events.guards';\nimport { isVertoInviteMessage } from '../core/RPCMessages/guards/verto.guards';\nimport { Destroyable, isRPCConnectResult } from '../core/utils';\nimport { filterAs } from '../operators/filterEventAs';\nimport { throwOnRPCError } from '../operators/throwOnRPCError';\nimport { getLogger } from '../utils/logger';\n\nimport type { AttachManager } from './AttachManager';\nimport type { StorageManager } from './StorageManager';\nimport type { TransportManager } from './TransportManager';\nimport type { WebRTCCall } from '../core/entities/Call';\nimport type { Directory } from '../core/entities/Directory';\nimport type { Call, CallOptions } from '../core/entities/types/call.types';\nimport type {\n RPCConnectParams,\n RPCConnectAuthentication,\n Authorization,\n RPCReauthenticateParams\n} from '../core/RPCMessages';\nimport type { JSONRPCRequest, JSONRPCResponse } from '../core/RPCMessages/types/base';\nimport type { VertoInviteParams } from '../core/RPCMessages/types/verto';\nimport type { SDKCredential, JSONSerializable } from '../core/types/common.types';\nimport type { PendingRPCOptions } from '../core/utils';\nimport type { WebRTCApiProvider } from '../dependencies/interfaces';\nimport type { DeviceController } from '../interfaces/DeviceController';\nimport type { SessionState } from '../interfaces/SessionState';\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\nconst getAddressSearchURI = (options: CallOptions): string => {\n const to = options.to?.split('?')[0];\n const from = options.from?.startsWith('subscriber://')\n ? options.from.replace('subscriber://', '')\n : options.from;\n const name = to ?? from;\n if (!name) {\n throw new UnexpectedError('Error building Address name');\n }\n return name;\n};\n\nexport class ClientSessionManager extends Destroyable implements SessionState {\n private callFactory: CallFactory;\n private callCreateTimeout = 6000;\n private readonly agent = `signalwire-typescript-sdk/1.0.0`;\n private readonly eventAcks = true;\n public initialized$: Observable<boolean>;\n private authorizationState$ = this.createReplaySubject<string | undefined>(1);\n private connectVersion = {\n major: 4,\n minor: 0,\n revision: 0\n };\n private _authorization$ = this.createBehaviorSubject<Authorization | undefined>(undefined);\n private _errors$ = this.createReplaySubject<Error>(1);\n private _directory?: Directory;\n\n private _authenticated$ = this.createBehaviorSubject<boolean>(false);\n\n private _subscriberInfo$ = this.createBehaviorSubject<Address | null>(null);\n private _calls$ = this.createBehaviorSubject<Record<string, Call>>({});\n private _iceServers$ = this.createBehaviorSubject<RTCIceServer[]>([]);\n\n constructor(\n private credential: SDKCredential,\n private readonly transport: TransportManager,\n private readonly storage: StorageManager,\n private readonly authorizationStateKey: string,\n deviceController: DeviceController,\n private readonly attachManager: AttachManager,\n webRTCApiProvider: WebRTCApiProvider\n ) {\n super();\n attachManager.setSession(this);\n this.callFactory = new CallFactory(this, deviceController, attachManager, webRTCApiProvider);\n this.initialized$ = defer(() => from(this.init())).pipe(\n shareReplay(1),\n takeUntil(this.destroyed$)\n );\n }\n public get incomingCalls$(): Observable<Call[]> {\n return this.cachedObservable('incomingCalls$', () =>\n this.calls$.pipe(map((calls) => calls.filter((call) => call.direction === 'inbound')))\n );\n }\n\n public get incomingCalls(): Call[] {\n const calls = Object.values(this._calls$.value);\n return calls.filter((call) => call.direction === 'inbound');\n }\n\n public get subscriberInfo$(): Observable<Address | null> {\n return this._subscriberInfo$.asObservable();\n }\n\n public get subscriberInfo(): Address | null {\n return this._subscriberInfo$.value;\n }\n\n public get calls$(): Observable<Call[]> {\n return this.cachedObservable('calls$', () =>\n this._calls$.pipe(map((calls) => Object.values(calls)))\n );\n }\n\n public get calls(): Call[] {\n return Object.values(this._calls$.value);\n }\n\n public get iceServers(): RTCIceServer[] | undefined {\n return this._iceServers$.value;\n }\n\n public get authorization$(): Observable<Authorization | undefined> {\n return this._authorization$.asObservable();\n }\n\n public get authorization(): Authorization | undefined {\n return this._authorization$.value;\n }\n\n public get errors$(): Observable<Error> {\n return this._errors$.asObservable();\n }\n\n public get authenticated$(): Observable<boolean> {\n return this._authenticated$.asObservable();\n }\n\n public get authenticated(): boolean {\n return this._authenticated$.value;\n }\n\n /**\n * Set the directory instance\n * Called by SignalWire after directory is created\n * @internal\n */\n public setDirectory(directory: Directory): void {\n this._directory = directory;\n }\n\n public async execute<T extends JSONRPCResponse = JSONRPCResponse>(\n request: JSONRPCRequest,\n options?: PendingRPCOptions\n ): Promise<T> {\n try {\n return await this.transport.execute(request, options);\n } catch (error) {\n logger.debug('[Session] Execute Error', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n throw error;\n }\n }\n\n public send(message: JSONSerializable): void {\n this.transport.send(message);\n }\n\n private async init(): Promise<boolean> {\n await this.loadAuthorizationStateFromStorage();\n this.setupMessageHandlers();\n return true;\n }\n\n private setupMessageHandlers(): void {\n logger.debug('[Session] Setting up message handlers');\n\n this.subscribeTo(this.authStateEvent$, async (authStateEvent) => {\n logger.debug('[Session] Authorization state event received:', authStateEvent);\n try {\n await this.updateAuthorizationStateInStorage(authStateEvent.authorization_state);\n } catch (error) {\n logger.error('[Session] Failed to handle authorization state update:', error);\n this._errors$.next(new AuthStateHandlerError(error));\n }\n });\n\n this.subscribeTo(\n this.transport.connectionStatus$.pipe(filter((status) => status === 'connected')),\n async () => {\n try {\n logger.debug('[Session] Connection established, initiating authentication');\n await this.authenticate();\n } catch (error) {\n this.handleAuthenticationError(error as Error).catch((err) => {\n logger.error('[Session] Error handling authentication failure:', err);\n });\n }\n }\n );\n\n this.subscribeTo(this.vertoInvite$, async (invite) => {\n logger.debug('[Session] Verto invite received:', invite);\n try {\n await this.createInboundCall(invite);\n } catch (error) {\n logger.error('[Session] Error handling Verto invite:', error);\n this._errors$.next(new VertoInviteHandlerError(error));\n }\n });\n }\n\n private async loadAuthorizationStateFromStorage(): Promise<void> {\n try {\n const storedState = await this.storage.getItem<string>(this.authorizationStateKey);\n // Always emit a value, even if undefined, so combineLatest can proceed\n this.authorizationState$.next(storedState ?? undefined);\n } catch (error) {\n logger.error('Failed to retrieve authorization state from storage:', error);\n // Emit undefined on error so authentication can proceed without stored state\n this.authorizationState$.next(undefined);\n }\n }\n\n private async updateAuthorizationStateInStorage(authorizationState?: string): Promise<void> {\n if (!authorizationState) {\n logger.debug('[Session] Removing authorization state from storage');\n try {\n await this.storage.removeItem(this.authorizationStateKey);\n } catch (error) {\n logger.error('Failed to remove authorization state from storage:', error);\n throw error;\n }\n return;\n }\n\n try {\n logger.debug('[Session] Updating authorization state in storage');\n await this.storage.setItem(this.authorizationStateKey, authorizationState);\n this.authorizationState$.next(authorizationState);\n } catch (error) {\n logger.error('Failed to retrieve authorization state from storage:', error);\n throw error;\n }\n }\n\n private get authStateEvent$() {\n return this.cachedObservable('authStateEvent$', () =>\n this.signalingEvent$.pipe(\n tap((msg) => {\n logger.debug('[Session] Received incoming message:', msg);\n }),\n filterAs(isSignalwireAuthorizationStateMetadata, 'params'),\n tap((event) => {\n logger.debug('[Session] Authorization state event received:', event.authorization_state);\n })\n )\n );\n }\n\n // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\n public get signalingEvent$() {\n return this.cachedObservable('signalingEvent$', () =>\n this.transport.incomingEvent$.pipe(filterAs(isSignalwireRequest, 'params'), share())\n );\n }\n\n private get vertoInvite$() {\n return this.cachedObservable('vertoInvite$', () =>\n this.signalingEvent$.pipe(\n filter(isWebrtcMessageMetadata),\n filter((event) => isVertoInviteMessage(event.params)),\n map((event) => ({\n node_id: event.node_id,\n ...(event.params.params as VertoInviteParams)\n }))\n )\n );\n }\n\n private get contexts(): string[] {\n return [];\n }\n\n private get eventing(): string[] {\n return [];\n }\n\n private get topics(): string[] {\n return [];\n }\n\n private get authentication(): RPCConnectAuthentication {\n if (!this.credential.token) {\n throw new DependencyError('Credential token is undefined');\n }\n return {\n jwt_token: this.credential.token\n };\n }\n\n async connect(): Promise<void> {\n // Ensure session is initialized before proceeding\n await firstValueFrom(this.initialized$);\n\n // Now initiate the connection - the subscription above will handle authentication\n await this.transport.connect();\n }\n\n private async handleAuthenticationError(error: Error): Promise<void> {\n logger.error('Authentication error:', error);\n this._errors$.next(error);\n if (error.message === 'Requester validation failed') {\n // the server is not accepting our credentials, potentially corrupted authorizationState or protocol\n try {\n await this.cleanupStoredConnectionParams();\n } catch (error) {\n logger.error('Failed to cleanup stored connection params:', error);\n } finally {\n this.transport.reconnect();\n }\n }\n }\n\n async cleanupStoredConnectionParams(): Promise<void> {\n await this.transport.setProtocol(undefined);\n await this.updateAuthorizationStateInStorage(undefined);\n await this.attachManager.detachAll();\n }\n\n protected async updateAuthState(authorization_state: string): Promise<void> {\n try {\n await this.storage.setItem(this.authorizationStateKey, authorization_state);\n } catch (error) {\n logger.error('Failed to update authorization state in storage:', error);\n this._errors$.next(new AuthStateHandlerError(error));\n }\n }\n\n public async reauthenticate(token: string): Promise<void> {\n logger.debug('[Session] Re-authenticating session');\n try {\n const params: RPCReauthenticateParams = {\n project: this._authorization$.value?.project_id ?? '',\n jwt_token: token\n };\n\n const request = RPCReauthenticate(params);\n\n await lastValueFrom(\n from(this.transport.execute(request)).pipe(\n throwOnRPCError(),\n take(1),\n catchError((err) => {\n logger.error('[Session] Re-authentication RPC failed:', err);\n throw err;\n })\n )\n );\n\n logger.debug('[Session] Re-authentication successful, updating stored auth state');\n } catch (error) {\n logger.error('[Session] Re-authentication failed:', error);\n this._errors$.next(new AuthStateHandlerError(error));\n throw error;\n }\n }\n\n private async authenticate(): Promise<void> {\n logger.debug('[Session] Starting authentication process');\n\n const params: RPCConnectParams = {\n authentication: this.authentication,\n version: this.connectVersion,\n agent: this.agent,\n contexts: this.contexts,\n eventing: this.eventing,\n topics: this.topics,\n\n event_acks: this.eventAcks\n };\n\n const persistedParams = await firstValueFrom(\n combineLatest({\n protocol: this.transport.protocol$,\n\n authorization_state: this.authorizationState$\n }).pipe(take(1))\n );\n\n logger.debug('[Session] Persisted params:\\n', {\n protocol: persistedParams.protocol,\n authStateLength: persistedParams.authorization_state?.length\n });\n\n const rpcConnectRequest = RPCConnect({ ...params, ...persistedParams });\n\n // Reset session epoch so the first event after authentication\n // establishes the new baseline for stale event detection.\n this.transport.resetSessionEpoch();\n\n const response = await lastValueFrom(\n from(this.transport.execute(rpcConnectRequest)).pipe(\n throwOnRPCError(),\n map((res) => res.result),\n filter(isRPCConnectResult),\n tap(() => {\n logger.debug('[Session] Response passed filter, processing authentication result');\n }),\n take(1),\n catchError((err) => {\n logger.error('[Session] Authentication RPC failed:', err);\n throw err;\n })\n )\n );\n\n logger.debug('[Session] Processing authentication result:', {\n hasProtocol: !!response.protocol,\n hasAuthorization: !!response.authorization,\n hasIceServers: !!response.ice_servers\n });\n\n if (response.protocol) {\n await this.transport.setProtocol(response.protocol);\n }\n this._authorization$.next(response.authorization);\n this._iceServers$.next(response.ice_servers ?? []);\n this._authenticated$.next(true);\n\n logger.debug('[Session] Authentication completed successfully');\n }\n\n async disconnect(): Promise<void> {\n this.transport.disconnect();\n this._authenticated$.next(false);\n await this.cleanupStoredConnectionParams();\n }\n\n private async createInboundCall(invite: VertoInviteParams & { node_id: string }): Promise<void> {\n const callSession = await this.createCall({\n nodeId: invite.node_id,\n callId: invite.callID,\n initOffer: invite.sdp,\n toName: invite.callee_id_name,\n to: invite.callee_id_number,\n fromName: invite.caller_id_name,\n from: invite.caller_id_number,\n displayDirection: invite.display_direction,\n userVariables: invite.userVariables\n });\n\n await firstValueFrom(callSession.status$);\n\n this._calls$.next({\n [`${callSession.id}`]: callSession,\n ...this._calls$.value\n });\n }\n\n public async createOutboundCall(\n destination: string | Address,\n options: CallOptions = {}\n ): Promise<Call> {\n const destinationURI =\n destination instanceof Address ? destination.defaultChannel : destination;\n let callSession: WebRTCCall | undefined;\n try {\n callSession = await this.createCall({\n to: destinationURI,\n ...options\n });\n\n await firstValueFrom(\n race(\n callSession.selfId$.pipe(\n filter((id) => Boolean(id)),\n take(1),\n timeout(this.callCreateTimeout)\n ),\n callSession.errors$.pipe(\n take(1),\n switchMap((callError) => throwError(() => callError.error))\n )\n )\n );\n\n this._calls$.next({\n [`${callSession.id}`]: callSession,\n ...this._calls$.value\n });\n\n return callSession;\n } catch (error) {\n logger.error('[Session] Error creating outbound call:', error);\n callSession?.destroy();\n const message =\n error instanceof TimeoutError ? 'Call create timeout' : 'Call creation failed';\n const callError = new CallCreateError(message, error, 'outbound');\n this._errors$.next(callError);\n throw callError;\n }\n }\n\n private async createCall(options: CallOptions = {}): Promise<WebRTCCall> {\n try {\n const addressURI = getAddressSearchURI(options);\n\n // For PSTN numbers (starting with +), skip the directory lookup\n // and create the call directly with the phone number as destination.\n let address: Address | undefined;\n try {\n if (!this._directory) {\n throw new DependencyError('Directory not initialized');\n }\n\n const addressId = await this._directory.findAddressIdByURI(addressURI);\n if (!addressId) {\n throw new DependencyError(`Address name: ${addressURI} not found`);\n }\n\n address = this._directory.get(addressId);\n if (!address) {\n throw new DependencyError(`Address ID: ${addressId} not found`);\n }\n } catch (error) {\n logger.warn(`[Session] Directory lookup failed for ${addressURI}, proceeding with raw URI`);\n }\n\n const callSession = this.callFactory.createCall(address, {\n ...options\n });\n\n callSession.status$\n .pipe(\n filter((status) => status === 'destroyed'),\n take(1)\n )\n .subscribe(() => {\n const { [`${callSession.id}`]: _, ...remainingCalls } = this._calls$.value;\n this._calls$.next(remainingCalls);\n });\n\n return callSession;\n } catch (error) {\n logger.error('[Session] Error creating call session:', error);\n const direction = options.initOffer ? 'inbound' : 'outbound';\n throw new CallCreateError('Call create error', error, direction);\n }\n }\n\n public destroy(): void {\n for (const call of Object.values(this._calls$.value)) {\n void call.hangup();\n }\n super.destroy();\n }\n}\n\nexport class ClientSessionWrapper implements SessionState {\n constructor(private clientSessionManager: ClientSessionManager) {}\n\n public get authenticated$(): Observable<boolean> {\n return this.clientSessionManager.authenticated$;\n }\n\n public get authenticated(): boolean {\n return this.clientSessionManager.authenticated;\n }\n\n // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\n public get signalingEvent$() {\n return this.clientSessionManager.signalingEvent$;\n }\n\n public get iceServers(): RTCIceServer[] | undefined {\n return this.clientSessionManager.iceServers;\n }\n\n public async execute<T extends JSONRPCResponse = JSONRPCResponse>(\n request: JSONRPCRequest,\n options?: PendingRPCOptions\n ): Promise<T> {\n return this.clientSessionManager.execute(request, options);\n }\n\n public get incomingCalls$(): Observable<Call[]> {\n return this.clientSessionManager.incomingCalls$;\n }\n\n public get incomingCalls(): Call[] {\n return this.clientSessionManager.incomingCalls;\n }\n\n public get calls$(): Observable<Call[]> {\n return this.clientSessionManager.calls$;\n }\n\n public get calls(): Call[] {\n return this.clientSessionManager.calls;\n }\n}\n","export const isString = (obj: unknown): obj is string => typeof obj === 'string';\n","import { map, tap, type Observable } from 'rxjs';\n\nimport { EntityCollection, Fetcher } from '../behaviors/Collection';\nimport { POST_PARAMS } from '../controllers/HTTPRequestController';\nimport { ConversationError } from '../core/errors';\nimport { isConversationMessageMetadata } from '../core/RPCMessages/guards/events.guards';\nimport { filterAs } from '../operators/filterEventAs';\nimport { isString } from '../utils/isString';\nimport { getLogger } from '../utils/logger';\n\nimport type { ClientSessionManager } from './ClientSessionManager';\nimport type { HTTPRequestController } from '../controllers/HTTPRequestController';\nimport type {\n GetConversationMessageResponse,\n GetConversationResponse\n} from '../core/types/conversation.types';\nimport type { ConversationsProvider } from '../interfaces/Conversations';\n\nconst logger = getLogger();\n\nconst toAddressId = (groupId: string): string => {\n const [, toAddressId] = groupId.split('_');\n return toAddressId;\n};\n\nclass ConversationMessagesFetcher extends Fetcher<GetConversationMessageResponse> {\n constructor(\n public readonly groupId: string,\n http: HTTPRequestController\n ) {\n super(`/api/fabric/conversations/${groupId}/messages`, 'page_size=100', http);\n }\n}\n\nclass ConversationsFetcher extends Fetcher<GetConversationResponse> {\n filter = (item: unknown): item is GetConversationResponse =>\n !!(item as GetConversationResponse).from_fabric_address_id;\n mapper = (item: unknown) => ({\n ...(item as GetConversationResponse),\n id: (item as GetConversationResponse).group_id,\n\n address_id: toAddressId((item as GetConversationResponse).group_id)\n });\n constructor(http: HTTPRequestController) {\n super(`/api/fabric/conversations`, 'page_size=100', http);\n }\n}\n\nexport class ConversationMessageCollection extends EntityCollection<GetConversationMessageResponse> {\n constructor(\n groupId: string,\n update$: Observable<Partial<GetConversationMessageResponse>>,\n http: HTTPRequestController,\n onError?: (error: Error) => void\n ) {\n super(new ConversationMessagesFetcher(groupId, http), update$, onError);\n }\n}\n\nexport class ConversationCollection extends EntityCollection<GetConversationResponse> {\n constructor(\n update$: Observable<Partial<GetConversationResponse>>,\n http: HTTPRequestController,\n onError?: (error: Error) => void\n ) {\n super(new ConversationsFetcher(http), update$, onError);\n }\n}\n\nexport class ConversationsManager implements ConversationsProvider {\n private groupIds = new Map<string, string>();\n\n constructor(\n private clientSession: ClientSessionManager,\n private http: HTTPRequestController,\n private getSubscriberAddressId: () => string,\n private readonly onError?: (error: Error) => void\n ) {}\n private async join(addressId: string): Promise<string> {\n const subscriberFromAddressId = this.getSubscriberAddressId();\n\n try {\n const response = await this.http.request({\n ...POST_PARAMS,\n url: `/api/fabric/conversations/join`,\n body: JSON.stringify({\n from_fabric_address_id: subscriberFromAddressId,\n fabric_address_ids: [addressId, subscriberFromAddressId]\n })\n });\n\n if (response.ok && !!response.body) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const data = JSON.parse(response.body);\n /* eslint-disable @typescript-eslint/no-unsafe-member-access */\n\n if (isString(data.group_id)) {\n this.groupIds.set(addressId, data.group_id as string);\n return data.group_id as string;\n }\n /* eslint-enable @typescript-eslint/no-unsafe-member-access */\n }\n throw new ConversationError('Join Failed - Unexpected response');\n } catch (error) {\n logger.error('[ConversationsManager] Failed to join conversation:', error);\n throw error;\n }\n }\n\n public async getConversationMessageCollection(\n addressId: string\n ): Promise<ConversationMessageCollection> {\n const groupId = this.groupIds.get(addressId) ?? (await this.join(addressId));\n\n return Promise.resolve(\n new ConversationMessageCollection(\n groupId,\n this.clientSession.signalingEvent$.pipe(\n filterAs(isConversationMessageMetadata, 'params'),\n tap((event) => logger.debug('[ConversationsManager ] Conversation Event:', event)),\n // FIXME after Conversation API Fixes\n map(\n (params) =>\n ({\n ...params\n }) as Partial<GetConversationMessageResponse>\n )\n ),\n this.http,\n this.onError\n )\n );\n }\n\n public async sendText(text: string, destinationAddressId: string): Promise<void> {\n const groupId =\n this.groupIds.get(destinationAddressId) ?? (await this.join(destinationAddressId));\n const subscriberFromAddressId = this.getSubscriberAddressId();\n\n try {\n const response = await this.http.request({\n ...POST_PARAMS,\n url: '/api/fabric/messages',\n body: JSON.stringify({\n group_id: groupId,\n from_fabric_address_id: subscriberFromAddressId,\n text\n })\n });\n if (response.ok) {\n return;\n }\n throw new ConversationError('Send Text Failed - Unexpected response');\n } catch (error) {\n logger.error('[ConversationsManager] Failed to send text message:', error);\n }\n }\n}\n","export const isEmptyArray = (a?: unknown[]): boolean => {\n return (a?.length ?? 0) === 0;\n};\n","import { take } from 'rxjs';\n\nimport type { Observable } from 'rxjs';\n\nexport const warnup = (observable: Observable<unknown>): void => {\n observable.pipe(take(1)).subscribe();\n};\n","import { firstValueFrom, map, type Observable } from 'rxjs';\n\nimport { EntityCollection, Fetcher } from '../behaviors/Collection';\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { GET_PARAMS } from '../controllers/HTTPRequestController';\nimport { Address } from '../core/entities/Address';\nimport { isConversationMessageUpdatedMetadata } from '../core/RPCMessages/guards/events.guards';\nimport { filterAs, filterNull } from '../operators';\nimport { isEmptyArray } from '../utils/arrays';\nimport { getLogger } from '../utils/logger';\nimport { warnup } from '../utils/warnup';\n\nimport type { ClientSessionManager } from './ClientSessionManager';\nimport type { PaginatedResponse } from '../behaviors/types/collection.types';\nimport type { HTTPRequestController } from '../controllers/HTTPRequestController';\nimport type { Directory } from '../core/entities/Directory';\nimport type { GetAddressResponse } from '../core/types/address.types';\nimport type { ConversationsProvider } from '../interfaces/Conversations';\n\nconst logger = getLogger();\n\nclass AddressFetcher extends Fetcher<GetAddressResponse> {\n constructor(http: HTTPRequestController) {\n super('/api/fabric/addresses', 'sort_by=name&sort_order=asc', http);\n }\n\n async name(name: unknown): Promise<GetAddressResponse | undefined> {\n const response = await this.http.request({\n ...GET_PARAMS,\n url: `${this.endpoint}?name=${encodeURIComponent(name as string)}`\n });\n if (response.ok && !!response.body) {\n const result = JSON.parse(response.body) as PaginatedResponse<GetAddressResponse>;\n if (!isEmptyArray(result.data)) {\n return result.data[0];\n }\n }\n logger.error('Failed to fetch addresses');\n }\n}\n\nexport class AddressStateCollection extends EntityCollection<GetAddressResponse> {\n constructor(\n update$: Observable<Partial<GetAddressResponse>>,\n http: HTTPRequestController,\n onError?: (error: Error) => void\n ) {\n super(new AddressFetcher(http), update$, onError);\n }\n}\n\nexport class DirectoryManager extends Destroyable implements Directory {\n private addNewAddress = (id: string): void => {\n const address = new Address(id, this.conversationManager, this);\n const observable = this._statesCollection.get$(id)?.pipe(\n filterNull(),\n map((data) => {\n address.upnext(data);\n return address;\n })\n );\n if (observable) {\n warnup(observable);\n this._observableRegistry.set(id, observable);\n }\n this._addressesInstances.set(id, address);\n };\n private _addresses$ = this.createBehaviorSubject<Address[]>([]);\n private _statesCollection: AddressStateCollection;\n private _addressesInstances = new Map<string, Address>();\n private _observableRegistry = new Map<string, Observable<Address>>();\n\n constructor(\n private http: HTTPRequestController,\n clientSession: ClientSessionManager,\n private conversationManager: ConversationsProvider,\n private readonly onError?: (error: Error) => void\n ) {\n super();\n this._statesCollection = new AddressStateCollection(\n clientSession.signalingEvent$.pipe(\n filterAs(isConversationMessageUpdatedMetadata, 'params'),\n // FIXME after Conversation API Fixes\n map((_) => ({}) as Partial<GetAddressResponse>)\n ),\n this.http,\n this.onError\n );\n this.initSubscriptions();\n }\n\n public get loading(): boolean {\n return this._statesCollection.loading;\n }\n\n private initSubscriptions(): void {\n this.subscribeTo(this._statesCollection.updated$, () => {\n const existing = Array.from(this._addressesInstances.values().map((address) => address.id));\n const newStates = this._statesCollection.values.filter(\n (state) => !existing.includes(state.id)\n );\n if (!isEmptyArray(newStates)) {\n newStates.forEach((state) => this.addNewAddress(state.id));\n this._addresses$.next(Array.from(this._addressesInstances.values()));\n }\n });\n }\n\n public get addresses$(): Observable<Address[]> {\n return this._addresses$.asObservable();\n }\n\n public get addresses(): Address[] {\n return this._addresses$.value;\n }\n\n public get hasMore$(): Observable<boolean> {\n return this._statesCollection.hasMore$;\n }\n\n public get loading$(): Observable<boolean> {\n return this._statesCollection.loading$;\n }\n\n public loadMore(): void {\n if (this._statesCollection.hasMore) {\n this._statesCollection.loadMore();\n }\n }\n\n public get$(id: string): Observable<Address> | undefined {\n if (!this._observableRegistry.has(id)) {\n this.addNewAddress(id);\n }\n return this._observableRegistry.get(id);\n }\n\n public get(addressId: string): Address | undefined {\n return this._addressesInstances.get(addressId);\n }\n\n public async findAddressIdByURI(name: string): Promise<string | undefined> {\n let addressId = this._addressesInstances.values().find((addr) => addr.name === name)?.id;\n if (!addressId) {\n const found$ = await this._statesCollection.find$('name', name);\n if (found$) {\n const state = await firstValueFrom(found$);\n this.addNewAddress(state.id);\n addressId = state.id;\n }\n }\n return addressId;\n }\n}\n","import { Destroyable } from '../behaviors/Destroyable';\nimport { UnexpectedError, WebSocketConnectionError, WebSocketTimeoutError } from '../core/errors';\nimport { getLogger } from '../utils/logger';\n\nimport type {\n NodeSocketAdapter,\n NodeSocketClient,\n WebSocketAdapter,\n WebSocketClient\n} from '../core/types/common.types';\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\nexport type WebSocketConnectionStatus =\n | 'disconnected'\n | 'disconnecting'\n | 'reconnecting'\n | 'connected'\n | 'connecting';\n\nexport interface WebSocketControllerOptions {\n reconnectDelayMin?: number;\n reconnectDelayMax?: number;\n connectionTimeout?: number;\n}\n\nexport class WebSocketController extends Destroyable {\n // Default configuration values\n\n private static readonly DEFAULT_RECONNECT_DELAY_MIN_MS = 1_000;\n\n private static readonly DEFAULT_RECONNECT_DELAY_MAX_MS = 30_000;\n\n private static readonly DEFAULT_CONNECTION_TIMEOUT_MS = 10_000;\n\n // Private state\n private socket?: WebSocketClient | NodeSocketClient;\n private messageQueue: (string | ArrayBuffer | Blob)[] = [];\n private reconnectTimer?: ReturnType<typeof setTimeout>;\n private connectionTimeoutTimer?: ReturnType<typeof setTimeout>;\n private currentReconnectDelay: number;\n private shouldReconnect = false;\n // Configuration\n private readonly reconnectDelayMin: number;\n private readonly reconnectDelayMax: number;\n private readonly connectionTimeout: number;\n // Observable streams\n private _status$ = this.createBehaviorSubject<WebSocketConnectionStatus>('disconnected');\n\n private _incomingMessages$ = this.createSubject<MessageEvent>();\n\n private _errors$ = this.createReplaySubject<Error>(1);\n\n constructor(\n private WebSocketConstructor: WebSocketAdapter | NodeSocketAdapter,\n private endpoint: string,\n private outgoingMessages$: Observable<string | ArrayBuffer | Blob>,\n options: WebSocketControllerOptions = {}\n ) {\n super();\n this.reconnectDelayMin =\n options.reconnectDelayMin ?? WebSocketController.DEFAULT_RECONNECT_DELAY_MIN_MS;\n this.reconnectDelayMax =\n options.reconnectDelayMax ?? WebSocketController.DEFAULT_RECONNECT_DELAY_MAX_MS;\n this.connectionTimeout =\n options.connectionTimeout ?? WebSocketController.DEFAULT_CONNECTION_TIMEOUT_MS;\n this.currentReconnectDelay = this.reconnectDelayMin;\n\n // Subscribe to send$ to handle message sending\n this.subscriptions.push(\n this.outgoingMessages$.subscribe((data) => {\n this.send(data);\n })\n );\n }\n\n public get status$(): Observable<WebSocketConnectionStatus> {\n return this._status$.asObservable();\n }\n public get incomingMessages$(): Observable<MessageEvent> {\n return this._incomingMessages$.asObservable();\n }\n public get errors$(): Observable<Error> {\n return this._errors$.asObservable();\n }\n public connect(): void {\n if (this._status$.value === 'connecting' || this._status$.value === 'connected') {\n return;\n }\n\n this.shouldReconnect = true;\n this._status$.next('connecting');\n this.createWebSocket();\n }\n\n public disconnect(): void {\n this.shouldReconnect = false;\n this.clearReconnectTimer();\n this.clearConnectionTimeout();\n\n const currentStatus = this._status$.value;\n\n if (\n currentStatus === 'connected' ||\n currentStatus === 'connecting' ||\n currentStatus === 'reconnecting'\n ) {\n if (this.socket) {\n this._status$.next('disconnecting');\n this.socket.close();\n } else {\n this._status$.next('disconnected');\n }\n } else {\n this._status$.next('disconnected');\n }\n }\n\n reconnect(): void {\n if (this.shouldReconnect) {\n this._status$.next('reconnecting');\n this.scheduleReconnection();\n } else {\n this._status$.next('disconnected');\n }\n }\n\n public send(data: string | ArrayBuffer | Blob): void {\n if (\n this._status$.value === 'connected' &&\n this.socket?.readyState === 1 // WebSocket.OPEN\n ) {\n try {\n logger.debug(\n `[WebSocketConnectionManager] Sending message:\\n${JSON.stringify(\n JSON.parse(data as string),\n null,\n 2\n )}`\n );\n } catch {\n logger.warn(\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-base-to-string\n `[WebSocketConnectionManager] Sending non-JSON message:\\n${data}`\n );\n }\n this.socket.send(data);\n } else {\n this.messageQueue.push(data);\n }\n }\n\n private createWebSocket(): void {\n try {\n this.socket = new this.WebSocketConstructor(this.endpoint);\n this.setupWebSocketListeners();\n this.startConnectionTimeout();\n } catch (error) {\n const err =\n error instanceof Error ? error : new UnexpectedError('Failed to create WebSocket');\n this._errors$.next(err);\n this.handleConnectionError();\n }\n }\n\n private setupWebSocketListeners(): void {\n if (!this.socket) return;\n\n this.socket.addEventListener('open', () => this.handleOpen());\n // @ts-expect-error -- Ignore ---\n this.socket.addEventListener('close', (event: CloseEvent) => this.handleClose(event));\n this.socket.addEventListener('error', () => this.handleError());\n // @ts-expect-error -- Ignore ---\n this.socket.addEventListener('message', (event: MessageEvent) => this.handleMessage(event));\n }\n\n private handleOpen(): void {\n this.clearConnectionTimeout();\n this._status$.next('connected');\n this.currentReconnectDelay = this.reconnectDelayMin;\n this.flushMessageQueue();\n }\n\n private handleClose(_event: CloseEvent): void {\n this.clearConnectionTimeout();\n\n if (this.shouldReconnect) {\n this._status$.next('reconnecting');\n this.scheduleReconnection();\n } else {\n this._status$.next('disconnected');\n }\n }\n\n private handleError(): void {\n const error = new WebSocketConnectionError('WebSocket connection error');\n this._errors$.next(error);\n this.handleConnectionError();\n }\n\n private handleMessage(event: MessageEvent): void {\n try {\n logger.debug(\n `[WebSocketConnectionManager] Received message:\\n${JSON.stringify(\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n JSON.parse(event.data),\n null,\n 2\n )}`\n );\n } catch {\n logger.warn(`[WebSocketConnectionManager] Received non-JSON message:\\n${event.data}`);\n }\n this._incomingMessages$.next(event);\n }\n\n private handleConnectionError(): void {\n this.reconnect();\n }\n\n private scheduleReconnection(): void {\n this.clearReconnectTimer();\n\n this.reconnectTimer = setTimeout(() => {\n if (this.shouldReconnect) {\n this._status$.next('connecting');\n this.createWebSocket();\n this.increaseReconnectDelay();\n }\n }, this.currentReconnectDelay);\n }\n\n private increaseReconnectDelay(): void {\n this.currentReconnectDelay = Math.min(this.currentReconnectDelay * 2, this.reconnectDelayMax);\n }\n\n private startConnectionTimeout(): void {\n this.clearConnectionTimeout();\n\n this.connectionTimeoutTimer = setTimeout(() => {\n if (this._status$.value === 'connecting') {\n const error = new WebSocketTimeoutError('WebSocket connection timeout');\n this._errors$.next(error);\n\n if (this.socket) {\n this.socket.close();\n }\n }\n }, this.connectionTimeout);\n }\n\n private clearConnectionTimeout(): void {\n if (this.connectionTimeoutTimer) {\n clearTimeout(this.connectionTimeoutTimer);\n this.connectionTimeoutTimer = undefined;\n }\n }\n\n private clearReconnectTimer(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = undefined;\n }\n }\n\n private flushMessageQueue(): void {\n while (this.messageQueue.length > 0 && this.socket?.readyState === 1) {\n const message = this.messageQueue.shift();\n if (message !== undefined) {\n this.socket.send(message);\n }\n }\n }\n}\n","// =============================================================================\n// CLIENT/SERVER METHOD TYPE GUARDS\n// =============================================================================\n// This file contains type guards for client requests, server responses,\n// and method params/results.\n\nimport { hasProperty, isJSONRPCRequest, isJSONRPCResponse, isObject } from './base.guards';\n\nimport type { JSONRPCRequest, TypeGuard } from '../types/base';\nimport type { SignalwirePingParams, SignalwirePingRequest } from '../types/events';\nimport type {\n CallLayoutListRequest,\n CallLayoutListResponse,\n CallMuteParams,\n CallMuteRequest,\n CallMuteResponse,\n CallTargetParams,\n EmptyResponse,\n SignalwireConnectParams,\n SignalwireConnectRequest,\n SignalwireConnectResponse,\n SignalwirePingResponse,\n WebrtcVertoParams,\n WebrtcVertoRequest,\n WebrtcVertoResponse\n} from '../types/methods';\n\n// =============================================================================\n// CLIENT REQUEST TYPE GUARDS\n// =============================================================================\n\nexport function isSignalwireConnectRequest(value: unknown): value is SignalwireConnectRequest {\n return isJSONRPCRequest(value) && value.method === 'signalwire.connect';\n}\n\nexport function isSignalwirePingRequest(value: unknown): value is SignalwirePingRequest {\n return isJSONRPCRequest(value) && value.method === 'signalwire.ping';\n}\n\nexport function isWebrtcVertoRequest(value: unknown): value is WebrtcVertoRequest {\n return isJSONRPCRequest(value) && value.method === 'webrtc.verto';\n}\n\nexport function isCallLayoutListRequest(value: unknown): value is CallLayoutListRequest {\n return isJSONRPCRequest(value) && value.method === 'call.layout.list';\n}\n\nexport function isCallMuteRequest(value: unknown): value is CallMuteRequest {\n return isJSONRPCRequest(value) && value.method === 'call.mute';\n}\n\n// =============================================================================\n// RESPONSE TYPE GUARDS\n// =============================================================================\n\nexport function isSignalwireConnectResponse(value: unknown): value is SignalwireConnectResponse {\n return (\n isJSONRPCResponse(value) &&\n isObject(value.result) &&\n hasProperty(value.result, 'identity') &&\n hasProperty(value.result, 'authorization') &&\n hasProperty(value.result, 'protocol')\n );\n}\n\nexport function isSignalwirePingResponse(value: unknown): value is SignalwirePingResponse {\n return (\n isJSONRPCResponse(value) && isObject(value.result) && hasProperty(value.result, 'timestamp')\n );\n}\n\nexport function isWebrtcVertoResponse(value: unknown): value is WebrtcVertoResponse {\n return (\n isJSONRPCResponse(value) &&\n isObject(value.result) &&\n hasProperty(value.result, 'node_id') &&\n hasProperty(value.result, 'code')\n );\n}\n\nexport function isCallLayoutListResponse(value: unknown): value is CallLayoutListResponse {\n return (\n isJSONRPCResponse(value) &&\n isObject(value.result) &&\n hasProperty(value.result, 'layouts') &&\n Array.isArray(value.result.layouts)\n );\n}\n\nexport function isCallMuteResponse(value: unknown): value is CallMuteResponse {\n return (\n isJSONRPCResponse(value) &&\n isObject(value.result) &&\n hasProperty(value.result, 'code') &&\n hasProperty(value.result, 'message') &&\n !hasProperty(value.result, 'layouts') &&\n !hasProperty(value.result, 'node_id')\n );\n}\n\nexport function isEmptyResponse(value: unknown): value is EmptyResponse {\n return (\n isJSONRPCResponse(value) && isObject(value.result) && Object.keys(value.result).length === 0\n );\n}\n\n// =============================================================================\n// PARAMS TYPE GUARDS\n// =============================================================================\n\nexport function isSignalwireConnectParams(value: unknown): value is SignalwireConnectParams {\n return (\n isObject(value) &&\n hasProperty(value, 'version') &&\n hasProperty(value, 'event_acks') &&\n hasProperty(value, 'agent') &&\n hasProperty(value, 'authentication')\n );\n}\n\nexport function isSignalwirePingParams(value: unknown): value is SignalwirePingParams {\n return isObject(value) && hasProperty(value, 'timestamp') && typeof value.timestamp === 'number';\n}\n\nexport function isWebrtcVertoParams(value: unknown): value is WebrtcVertoParams {\n return (\n isObject(value) &&\n hasProperty(value, 'message') &&\n hasProperty(value, 'callID') &&\n hasProperty(value, 'node_id')\n );\n}\n\nexport function isCallTargetParams(value: unknown): value is CallTargetParams {\n return isObject(value) && hasProperty(value, 'self') && hasProperty(value, 'target');\n}\n\nexport function isCallMuteParams(value: unknown): value is CallMuteParams {\n if (!isCallTargetParams(value)) return false;\n return 'channels' in value;\n}\n\n// =============================================================================\n// METHOD TYPE MAPPING\n// =============================================================================\n\nexport const MethodTypeMap = {\n 'signalwire.connect': isSignalwireConnectRequest,\n 'signalwire.ping': isSignalwirePingRequest,\n 'webrtc.verto': isWebrtcVertoRequest,\n 'call.layout.list': isCallLayoutListRequest,\n 'call.mute': isCallMuteRequest\n} as const;\n\nexport type MethodType = keyof typeof MethodTypeMap;\n\n/**\n * Gets the appropriate type guard for a method.\n */\nexport function getMethodGuard(method: string): TypeGuard<JSONRPCRequest> | undefined {\n return MethodTypeMap[method as MethodType];\n}\n","import {\n EMPTY,\n catchError,\n defer,\n filter,\n from,\n map,\n share,\n shareReplay,\n take,\n takeUntil,\n tap,\n timeout\n} from 'rxjs';\n\nimport { PreferencesContainer } from '../containers/PreferencesContainer';\nimport { WebSocketController } from '../controllers/WebSocketController';\nimport { MessageParseError, TransportConnectionError } from '../core/errors';\nimport { RPCEventAckResponse, RPCPingResponse } from '../core/RPCMessages';\nimport { isJSONRPCRequest, isJSONRPCResponse } from '../core/RPCMessages/guards/base.guards';\nimport { isSignalwireRequest } from '../core/RPCMessages/guards/events.guards';\nimport { isSignalwirePingRequest } from '../core/RPCMessages/guards/methods.guards';\nimport { Destroyable, PendingRPC } from '../core/utils';\nimport { getLogger } from '../utils/logger';\n\nimport type { StorageManager } from './StorageManager';\nimport type { JSONRPCRequest, JSONRPCResponse } from '../core/RPCMessages/types/base';\nimport type {\n JSONSerializable,\n WebSocketAdapter,\n NodeSocketAdapter\n} from '../core/types/common.types';\nimport type { PendingRPCOptions } from '../core/utils';\nimport type { Observable, OperatorFunction } from 'rxjs';\n\nconst logger = getLogger();\n\nexport class TransportManager extends Destroyable {\n /**\n * Normalise a server event timestamp to epoch seconds.\n *\n * The server uses two formats:\n * - `webrtc.message`: float epoch seconds (e.g. 1774372099.022817)\n * - all other events: int epoch microseconds (e.g. 1774372099925857)\n *\n * Values above 1e12 are treated as microseconds and divided by 1e6.\n */\n private static toEpochSeconds(ts: number | string): number {\n const n = typeof ts === 'string' ? Number(ts) : ts;\n return n > 1e12 ? n / 1e6 : n;\n }\n /**\n * Extract the event timestamp from a signalwire.event message.\n * Returns `null` for messages that have no timestamp\n * (e.g. signalwire.authorization.state, RPC responses).\n */\n private static extractEventTimestamp(message: JSONRPCRequest | JSONRPCResponse): number | null {\n if (!isSignalwireRequest(message)) return null;\n // signalwire.authorization.state is the only event without a timestamp\n if (message.params.event_type === 'signalwire.authorization.state') return null;\n return TransportManager.toEpochSeconds(message.params.timestamp);\n }\n private initialized$: Observable<boolean>;\n\n public protocol$ = this.createReplaySubject<string | undefined>(1);\n // Connection state tracking\n private isConnecting = false;\n private isConnected = false;\n\n // Session epoch for stale event detection (epoch seconds).\n // Set from the first timestamped event after each signalwire.connect.\n private ackEvent = <T extends JSONRPCRequest | JSONRPCResponse>(): OperatorFunction<T, T> => {\n return tap((message) => {\n if (isSignalwireRequest(message)) {\n try {\n logger.debug('[Transport] Sending event ack', {\n eventId: message.id\n });\n this.send(RPCEventAckResponse(message.id));\n } catch (error) {\n logger.error('[Transport] Failed to send event acknowledgment:', error);\n }\n }\n });\n };\n private replySignalwirePing = <T extends JSONRPCRequest | JSONRPCResponse>(): OperatorFunction<\n T,\n T\n > => {\n return filter((message) => {\n if (isSignalwirePingRequest(message)) {\n try {\n logger.debug('[Transport] Received ping, sending pong', {\n pingId: message.id\n });\n this.send(RPCPingResponse(message.id));\n } catch (error) {\n logger.error('[Transport] Failed to send ping response:', error);\n }\n return false;\n }\n return true;\n });\n };\n /**\n * Filter that drops events whose timestamp predates the current session.\n *\n * On each new connection the session epoch is reset (see `resetSessionEpoch`).\n * The first timestamped event after reset establishes the epoch.\n * Subsequent events with timestamps older than the epoch are discarded —\n * this guards against stale events replayed by the server after a reconnect.\n *\n * Events without a timestamp (e.g. signalwire.authorization.state) and\n * non-event messages (RPC responses) always pass through.\n */\n private discardStaleEvents = <T extends JSONRPCRequest | JSONRPCResponse>(): OperatorFunction<\n T,\n T\n > => {\n return filter((message) => {\n const ts = TransportManager.extractEventTimestamp(message);\n\n // No timestamp → let it through (auth state events, RPC responses)\n if (ts === null) return true;\n\n // First timestamped event after connect → establish epoch\n if (this._sessionEpoch === null) {\n this._sessionEpoch = ts;\n return true;\n }\n\n // Discard events older than the session epoch\n if (ts < this._sessionEpoch) {\n const eventType = isSignalwireRequest(message) ? message.params.event_type : 'unknown';\n logger.warn(\n `[Transport] Discarding stale event: ${eventType}` +\n ` (timestamp=${ts.toFixed(3)}, sessionEpoch=${this._sessionEpoch.toFixed(3)},` +\n ` delta=${(this._sessionEpoch - ts).toFixed(3)}s)`\n );\n return false;\n }\n\n return true;\n });\n };\n // Events with timestamps older than this are discarded.\n private _sessionEpoch: number | null = null;\n\n private _outgoingMessages$ = this.createSubject<string | ArrayBuffer | Blob>();\n private _webSocketConnections!: WebSocketController;\n private _jsonRPCMessage$!: Observable<JSONRPCResponse | JSONRPCRequest>;\n private _jsonRPCResponse$!: Observable<JSONRPCResponse>;\n private _incomingEvent$!: Observable<JSONRPCRequest | JSONRPCResponse>;\n\n constructor(\n private readonly storage: StorageManager,\n private readonly protocolKey: string,\n webSocketConstructor: WebSocketAdapter | NodeSocketAdapter,\n relayHost: string,\n private readonly onError?: (error: Error) => void\n ) {\n super();\n this._webSocketConnections = new WebSocketController(\n webSocketConstructor,\n relayHost,\n this._outgoingMessages$.asObservable(),\n {\n connectionTimeout: PreferencesContainer.instance.connectionTimeout,\n reconnectDelayMin: PreferencesContainer.instance.reconnectDelayMin,\n reconnectDelayMax: PreferencesContainer.instance.reconnectDelayMax\n }\n );\n this.subscribeTo(this._webSocketConnections.errors$, (error) => {\n this.onError?.(error);\n });\n this.initialized$ = defer(() => from(this._init())).pipe(\n shareReplay(1),\n takeUntil(this.destroyed$)\n );\n\n this._jsonRPCMessage$ = this._webSocketConnections.incomingMessages$.pipe(\n map((event: MessageEvent) => {\n try {\n return JSON.parse(event.data as string) as object;\n } catch (error) {\n logger.error('[Transport] Failed to parse incoming message:', error);\n this.onError?.(new MessageParseError(error));\n return null;\n }\n }),\n filter(\n (message): message is JSONRPCResponse | JSONRPCRequest =>\n message !== null && (isJSONRPCResponse(message) || isJSONRPCRequest(message))\n ),\n catchError((error) => {\n logger.error('[Transport] Message processing error:', error);\n this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));\n return EMPTY;\n }),\n share(),\n takeUntil(this.destroyed$)\n );\n\n this._jsonRPCResponse$ = this._jsonRPCMessage$.pipe(filter(isJSONRPCResponse));\n\n this._incomingEvent$ = this._jsonRPCMessage$.pipe(\n this.ackEvent(),\n this.replySignalwirePing(),\n filter((message) => !isJSONRPCResponse(message)),\n this.discardStaleEvents(),\n share(),\n takeUntil(this.destroyed$)\n );\n }\n\n /**\n * Reset the session epoch. Call this before each signalwire.connect\n * so that the first event after authentication establishes the new baseline.\n */\n public resetSessionEpoch(): void {\n this._sessionEpoch = null;\n }\n\n public async setProtocol(protocol: string | undefined): Promise<void> {\n this.protocol$.next(protocol);\n await this._updateProtocolInStorage(protocol);\n }\n public get incomingEvent$(): Observable<JSONRPCRequest | JSONRPCResponse> {\n return this._incomingEvent$;\n }\n\n public get connectionStatus$(): Observable<string> {\n return this._webSocketConnections.status$;\n }\n\n public async connect(): Promise<void> {\n // Prevent duplicate connections\n if (this.isConnecting || this.isConnected) {\n logger.warn('[Transport] Already connecting or connected');\n return Promise.resolve();\n }\n\n return new Promise<void>((resolve, reject) => {\n this.isConnecting = true;\n\n this.subscribeTo(this.initialized$, () => {\n this._webSocketConnections.connect();\n\n // Wait for actual connection\n const connectionSub = this._webSocketConnections.status$\n .pipe(\n filter((status) => status === 'connected' || status === 'disconnected'),\n take(1),\n timeout(10000) // 10 second timeout\n )\n .subscribe({\n next: (status) => {\n if (status === 'connected') {\n this.isConnecting = false;\n this.isConnected = true;\n logger.debug('[Transport] Connection established');\n resolve();\n } else {\n this.isConnecting = false;\n const error = new TransportConnectionError('Failed to connect');\n logger.error('[Transport] Connection failed');\n this.onError?.(error);\n reject(error);\n }\n },\n error: (err) => {\n this.isConnecting = false;\n logger.error('[Transport] Connection error:', err);\n this.onError?.(err instanceof Error ? err : new Error(String(err), { cause: err }));\n reject(err as Error);\n }\n });\n\n this.subscriptions.push(connectionSub);\n\n // Track disconnection\n this.subscribeTo(\n this._webSocketConnections.status$.pipe(filter((status) => status === 'disconnected')),\n () => {\n logger.debug('[Transport] Disconnected');\n this.isConnected = false;\n }\n );\n });\n });\n }\n\n public reconnect(): void {\n this._webSocketConnections.reconnect();\n }\n\n public async execute<T extends JSONRPCResponse = JSONRPCResponse>(\n request: JSONRPCRequest,\n options?: PendingRPCOptions\n ): Promise<T> {\n // Send the request through the WebSocket\n this.send(request as unknown as JSONSerializable);\n\n // Create and return a PendingRPC promise that will resolve when the matching response arrives\n return new PendingRPC<T>(request, this._jsonRPCResponse$ as Observable<T>, options).promise;\n }\n public send(message: unknown): void {\n const payload = JSON.stringify(message);\n this._outgoingMessages$.next(payload);\n }\n // request(request: HTTPRequest): Promise<HTTPResponse> {}\n public disconnect(): void {\n logger.debug('[Transport] Disconnecting');\n this.isConnected = false;\n this.isConnecting = false;\n\n // Disconnect WebSocket\n this._webSocketConnections.disconnect();\n }\n public destroy(): void {\n logger.debug('[Transport] Destroying');\n this.disconnect();\n super.destroy();\n this._webSocketConnections.destroy();\n }\n private async _loadProtocolFromStorage(): Promise<void> {\n try {\n const storedProtocol = await this.storage.getItem<string>(this.protocolKey);\n this.protocol$.next(storedProtocol ?? undefined);\n } catch (error) {\n logger.error('Failed to retrieve protocol from storage:', error);\n throw error;\n }\n }\n\n private async _updateProtocolInStorage(protocol: string | undefined): Promise<void> {\n if (!protocol) {\n try {\n await this.storage.removeItem(this.protocolKey);\n } catch (error) {\n logger.error('Failed to remove protocol from storage:', error);\n throw error;\n }\n return;\n }\n\n try {\n const storedProtocol = await this.storage.getItem<string>(this.protocolKey);\n if (!storedProtocol || storedProtocol !== protocol) {\n await this.storage.setItem(this.protocolKey, protocol);\n }\n } catch (error) {\n logger.error('Failed to update protocol in storage:', error);\n throw error;\n }\n }\n\n private async _init(): Promise<boolean> {\n await this._loadProtocolFromStorage();\n return true;\n }\n}\n","import { jwtDecode } from 'jwt-decode';\nimport { filter, firstValueFrom, of, switchMap, type Observable } from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { DependencyContainer } from '../containers/DependencyContainer';\nimport { ClientPreferences, PreferencesContainer } from '../containers/PreferencesContainer';\nimport { Subscriber } from '../core/entities/Subscriber';\nimport { InvalidCredentialsError, UnexpectedError } from '../core/errors';\nimport { RPCExecute } from '../core/RPCMessages';\nimport { AttachManager } from '../managers/AttachManager';\nimport { ClientSessionManager, ClientSessionWrapper } from '../managers/ClientSessionManager';\nimport { ConversationsManager } from '../managers/ConversationsManager';\nimport { DirectoryManager } from '../managers/DirectoryManager';\nimport { TransportManager } from '../managers/TransportManager';\nimport { getLogger } from '../utils/logger';\n\nimport type { Address } from '../core/entities/Address';\nimport type { Directory } from '../core/entities/Directory';\nimport type { Call } from '../core/entities/types/call.types';\nimport type {\n NodeSocketAdapter,\n SDKCredential,\n WebSocketAdapter\n} from '../core/types/common.types';\nimport type { MediaOptions } from '../core/types/media.types';\nimport type { CredentialProvider, Storage, WebRTCApiProvider } from '../dependencies/interfaces';\nimport type { DeviceController } from '../interfaces/DeviceController';\n\nconst logger = getLogger();\n\ninterface JWTHeader {\n ch?: string;\n typ?: string;\n}\n/** Options for constructing a {@link SignalWire}. */\nexport interface SignalWireOptions {\n /** Skip automatic WebSocket connection on construction. */\n skipConnection?: boolean;\n /** Skip automatic subscriber registration on construction. */\n skipRegister?: boolean;\n /** Skip monitoring media device changes. */\n skipDeviceMonitoring?: boolean;\n /** Whether to reconnect to previously attached calls. */\n reconnectAttachedCalls?: boolean;\n /** Whether to save preferences. */\n savePreferences?: boolean;\n /** Custom storage implementation for persistence. */\n storageImplementation?: Storage;\n /** Custom WebSocket constructor */\n webSocketConstructor?: WebSocketAdapter | NodeSocketAdapter;\n /** Custom WebRTC API provider */\n webRTCApiProvider?: WebRTCApiProvider;\n}\n\n/** Options for {@link SignalWire.dial}. Extends {@link MediaOptions} with dial-specific settings. */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface DialOptions extends MediaOptions {\n // Define any options for dialing here\n}\n\nconst buildOptionsFromDestination = (destination: string | Address): DialOptions => {\n if (typeof destination === 'string') {\n const queryStartIndex = destination.indexOf('?');\n if (queryStartIndex !== -1) {\n const queryString = destination.substring(queryStartIndex + 1);\n const params = new URLSearchParams(queryString);\n const channel = params.get('channel');\n\n if (channel === 'video') {\n return { audio: true, video: true, receiveVideo: true };\n } else if (channel === 'audio') {\n return { audio: true, video: false };\n }\n }\n }\n\n return {};\n};\n/**\n * Main entry point for the SignalWire Browser SDK.\n *\n * Manages authentication, WebSocket transport, call creation, and media devices.\n *\n * @example\n * ```ts\n * const client = new SignalWire(credentialProvider);\n * client.isConnected$.subscribe(connected => console.log('Connected:', connected));\n * const call = await client.dial('/public/my-room');\n * ```\n */\nclass SignalWire extends Destroyable implements DeviceController {\n /** Global SDK preferences (timeouts, ICE config, media defaults). */\n public preferences = new ClientPreferences();\n private _subscriber$ = this.createBehaviorSubject<Subscriber | undefined>(undefined);\n private _directory$ = this.createBehaviorSubject<Directory | undefined>(undefined);\n private _transport!: TransportManager;\n private _clientSession!: ClientSessionManager;\n private _publicSession!: ClientSessionWrapper;\n private _deviceController!: DeviceController;\n private _attachManager?: AttachManager;\n private _isConnected$ = this.createBehaviorSubject<boolean>(false);\n private _isRegistered$ = this.createBehaviorSubject<boolean>(false);\n private _errors$ = this.createReplaySubject<Error>(1);\n private _options: SignalWireOptions = {};\n private _refreshTimerId?: ReturnType<typeof setTimeout>;\n private _deps = new DependencyContainer();\n\n constructor(credentialProvider: CredentialProvider, options: SignalWireOptions = {}) {\n super();\n this._options = {\n ...PreferencesContainer.instance.defaultSignalWireOptions,\n ...options\n };\n\n // Set custom storage implementation if provided\n if (this._options.storageImplementation) {\n this._deps.storageImpl = this._options.storageImplementation;\n }\n\n if (this._options.webSocketConstructor) {\n this._deps.WebSocket = this._options.webSocketConstructor;\n }\n\n if (this._options.savePreferences) {\n this.preferences.enableSavePreferences(this._deps.storage);\n }\n\n if (this._options.webRTCApiProvider) {\n this._deps.webRTCApiProvider = this._options.webRTCApiProvider;\n }\n\n this._deviceController = this._deps.deviceController;\n if (!this._options.skipDeviceMonitoring) {\n this._deviceController.enableDeviceMonitoring();\n }\n\n this.subscribeTo(this._deviceController.errors$, (error) => {\n this._errors$.next(error);\n });\n\n this.validateCredentials(credentialProvider)\n .then(() => {\n this.init().catch((error: unknown) => {\n logger.error('[SignalWire] Initialization error:', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n });\n })\n .catch((error: unknown) => {\n logger.error('[SignalWire] Initialization error:', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n });\n }\n\n private async validateCredentials(\n credentialProvider: CredentialProvider,\n credentials?: SDKCredential\n ): Promise<void> {\n const _credentials = credentials ?? (await credentialProvider.authenticate());\n if (_credentials.token) {\n try {\n const decodeHeader: JWTHeader = jwtDecode(_credentials.token, { header: true });\n this._deps.ch = decodeHeader.ch;\n } catch (error) {\n logger.error('[SignalWire] Invalid JWT token provided in credentials:', error);\n throw new InvalidCredentialsError('Invalid JWT token provided in credentials.', {\n cause: error\n });\n }\n }\n if (!_credentials.token && !_credentials.authorizationState) {\n logger.error('[SignalWire] No valid authentication credentials provided.');\n throw new InvalidCredentialsError('No valid authentication credentials provided.');\n }\n\n if (_credentials.expiry_at && _credentials.expiry_at < Date.now()) {\n logger.error('[SignalWire] Provided credentials have expired.');\n throw new InvalidCredentialsError('Provided credentials have expired.');\n }\n\n if (_credentials.expiry_at && credentialProvider.refresh) {\n const refreshFn = async () => {\n if (!credentialProvider.refresh) {\n throw new InvalidCredentialsError('Credential provider does not support refresh');\n }\n return credentialProvider.refresh();\n };\n const refreshInterval = Math.max(_credentials.expiry_at - Date.now() - 5000, 1000);\n this._refreshTimerId = setTimeout(async () => {\n try {\n const newCredentials = await refreshFn();\n this._deps.credential = newCredentials;\n logger.info('[SignalWire] Credentials refreshed successfully.');\n // Schedule next refresh\n this.validateCredentials(credentialProvider, newCredentials).catch((error: unknown) => {\n logger.error('[SignalWire] Credential refresh error:', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n });\n } catch (error: unknown) {\n logger.error('[SignalWire] Credential refresh failed:', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n }\n }, refreshInterval);\n }\n\n this._deps.credential = _credentials;\n\n if (this.isConnected && this._clientSession.authenticated && _credentials.token) {\n try {\n await this._clientSession.reauthenticate(_credentials.token);\n logger.info('[SignalWire] Session refreshed with new credentials.');\n } catch (error: unknown) {\n logger.error('[SignalWire] Failed to refresh session with new credentials:', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n }\n }\n }\n\n private async init() {\n // Initialize subscriber first (before transport) to fetch subscriber info\n this._subscriber$.next(new Subscriber(this._deps.http));\n\n if (!this._options.skipConnection) {\n await this.connect();\n }\n\n // Flush attached calls after connect creates the AttachManager\n if (!this._options.reconnectAttachedCalls && this._attachManager) {\n await this._attachManager.flush();\n }\n\n if (!this._options.skipRegister) {\n // eventually register after the authentication\n try {\n await this.register();\n } catch (error) {\n logger.error('[SignalWire] Registration failed:', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n }\n }\n\n // eventually reconnect to attached calls after the authentication\n void this.handleAttachments();\n }\n\n private async handleAttachments() {\n if (!this._attachManager) {\n logger.error('[SignalWire] AttachManager not initialized');\n return;\n }\n if (!this._options.reconnectAttachedCalls) {\n return;\n }\n try {\n await this._attachManager.reattachCalls();\n } catch (error) {\n logger.error('[SignalWire] Failed to reattach calls:', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n }\n }\n\n /**\n * Establishes the WebSocket connection and authenticates the session.\n *\n * ## Reconnection behavior\n *\n * After a successful connection the underlying {@link WebSocketController}\n * automatically attempts to reconnect whenever the socket closes\n * unexpectedly (e.g. network change, server restart). Reconnection uses an\n * **exponential back-off** strategy:\n *\n * - First retry after `reconnectDelayMin` (default **1 s**).\n * - Each subsequent retry doubles the delay up to `reconnectDelayMax`\n * (default **30 s**).\n * - The delay resets to `reconnectDelayMin` once a connection succeeds.\n * - A per-attempt `connectionTimeout` (default **10 s**) aborts the\n * attempt and schedules the next retry if the server does not respond.\n *\n * Calling {@link disconnect} stops the reconnection loop entirely.\n *\n * ## Message handling during temporary disconnections\n *\n * While the socket is not in the `connected` state, **outgoing messages\n * are queued** in an internal buffer. Once the connection is\n * re-established the queue is flushed in order so no outgoing RPC call is\n * lost.\n *\n * **Incoming** server-to-client messages that arrive while the socket is\n * down are *not* buffered by the SDK — they are expected to be\n * re-delivered by the server after the session is re-authenticated.\n * Active RPC calls that were awaiting a response will time out\n * (default **5 s**) and reject with an `RPCTimeoutError`; callers should\n * handle this and retry if appropriate.\n *\n * The connection status can be observed via the `status$` observable on\n * the transport layer, which emits `'connecting'`, `'connected'`,\n * `'reconnecting'`, `'disconnecting'`, or `'disconnected'`.\n */\n public async connect(): Promise<void> {\n // Wait for subscriber to be fetched first to get the subscriber ID\n try {\n const subscriber = this._subscriber$.value;\n if (!subscriber) {\n throw new UnexpectedError('Subscriber not initialized before connect');\n }\n\n const fetched = await firstValueFrom(subscriber.fetched$);\n\n if (!fetched) {\n throw new UnexpectedError(\n 'Failed to fetch subscriber information - fetched$ emitted false'\n );\n }\n\n // Set the subscriber ID in the dependency container\n this._deps.subscriber = subscriber;\n } catch (error) {\n logger.error(\n `[SignalWire] Failed to fetch subscriber information: ${error instanceof Error ? error.message : 'Unknown error'}. ` +\n `This usually means the subscriber token is invalid or expired.`\n );\n throw new UnexpectedError('Error fetching subscriber information', { cause: error });\n }\n\n const errorHandler = (error: Error) => {\n this._errors$.next(error);\n };\n\n // Now initialize transport and session with subscriber ID available\n this._transport = new TransportManager(\n this._deps.storage,\n this._deps.protocolKey,\n this._deps.WebSocket,\n PreferencesContainer.instance.relayHost ?? this._deps.relayHost,\n errorHandler\n );\n\n // Create AttachManager (needed by CallFactory -> VertoManager)\n this._attachManager = new AttachManager(\n this._deps.storage,\n this._deps.deviceController,\n PreferencesContainer.instance.reconnectCallsTimeout,\n this._deps.attachedCallsKey\n );\n\n this._clientSession = new ClientSessionManager(\n this._deps.credential,\n this._transport,\n this._deps.storage,\n this._deps.authorizationStateKey,\n this._deps.deviceController,\n this._attachManager,\n this._deps.webRTCApiProvider\n );\n this._publicSession = new ClientSessionWrapper(this._clientSession);\n\n this.subscribeTo(this._clientSession.errors$, (error) => {\n this._errors$.next(error);\n });\n\n await this._clientSession.connect();\n\n // Create conversationManager first to inject into DirectoryManager\n const conversationManager = new ConversationsManager(\n this._clientSession,\n this._deps.http,\n () => this._deps.getSubscriberFromAddressId(),\n errorHandler\n );\n\n // Create directory with all dependencies injected\n const directory = new DirectoryManager(\n this._deps.http,\n this._clientSession,\n conversationManager,\n errorHandler\n );\n this._directory$.next(directory);\n this._clientSession.setDirectory(directory);\n\n this._isConnected$.next(true);\n }\n\n /**\n * Observable that emits the {@link Subscriber} profile once fetched,\n * or `undefined` before authentication completes.\n *\n * @example\n * ```ts\n * client.subscriber$.subscribe(sub => {\n * if (sub) console.log('Logged in as', sub.email);\n * });\n * ```\n */\n public get subscriber$(): Observable<Subscriber | undefined> {\n return this.deferEmission(this._subscriber$.asObservable());\n }\n\n /** Current subscriber snapshot, or `undefined` if not yet authenticated. */\n public get subscriber(): Subscriber | undefined {\n return this._subscriber$.value;\n }\n\n /**\n * Observable that emits the {@link Directory} instance once the client is connected,\n * or `undefined` while disconnected. Subscribe to this to safely wait for the directory\n * to become available without risking an error.\n *\n * @example\n * ```ts\n * client.directory$.subscribe(dir => {\n * if (dir) dir.addresses$.subscribe(console.log);\n * });\n * ```\n */\n public get directory$(): Observable<Directory | undefined> {\n return this.deferEmission(this._directory$.asObservable());\n }\n\n /**\n * Current directory snapshot, or `undefined` if the client is not yet connected.\n * Prefer {@link directory$} when you need to react to the directory becoming available.\n */\n public get directory(): Directory | undefined {\n return this._directory$.value;\n }\n\n /** Observable that emits when the subscriber registration state changes. */\n public get isRegistered$(): Observable<boolean> {\n return this.deferEmission(this._isRegistered$.asObservable());\n }\n\n /** Whether the subscriber is currently registered. */\n public get isRegistered(): boolean {\n return this._isRegistered$.value;\n }\n\n /** Whether the client is currently connected. */\n public get isConnected(): boolean {\n return this._isConnected$.value;\n }\n\n /** Observable that emits when the connection state changes. */\n public get isConnected$(): Observable<boolean> {\n return this.deferEmission(this._isConnected$.asObservable());\n }\n\n /** Observable that emits `true` when the client is both connected and authenticated. */\n public get ready$(): Observable<boolean> {\n return this.publicCachedObservable('ready$', () =>\n this._isConnected$.pipe(\n switchMap((connected) => (connected ? this._clientSession.authenticated$ : of(false)))\n )\n );\n }\n\n /** Observable stream of errors from transport, authentication, and devices. */\n public get errors$(): Observable<Error> {\n return this.deferEmission(this._errors$.asObservable());\n }\n\n /** Disconnects the WebSocket and tears down the session. */\n public async disconnect(): Promise<void> {\n await this._clientSession.disconnect();\n this._clientSession.destroy();\n this._isConnected$.next(false);\n }\n\n private async waitAuthentication(): Promise<void> {\n // Wait for client to be ready (authenticated)\n await firstValueFrom(this.ready$.pipe(filter((ready) => ready === true)));\n }\n\n /** Registers the subscriber as online to receive inbound calls and events. */\n public async register(): Promise<void> {\n try {\n // Wait for client session to be authenticated before registering subscriber\n await this.waitAuthentication();\n await this._transport.execute(RPCExecute({ method: 'subscriber.online', params: {} }));\n this._isRegistered$.next(true);\n } catch (error) {\n logger.debug('[SignalWire] Failed to register subscriber, trying reauthentication...');\n if (this._deps.credential.token) {\n this._clientSession\n .reauthenticate(this._deps.credential.token)\n .then(async () => {\n logger.debug('[SignalWire] Reauthentication successful, retrying register()');\n await this._transport.execute(RPCExecute({ method: 'subscriber.online', params: {} }));\n this._isRegistered$.next(true);\n })\n .catch((reauthError: unknown) => {\n logger.error('[SignalWire] Reauthentication failed during register():', reauthError);\n const registerError = new InvalidCredentialsError(\n 'Failed to register subscriber, and reauthentication attempt also failed. Please check your credentials.',\n {\n cause:\n reauthError instanceof Error\n ? reauthError\n : new Error(String(reauthError), { cause: reauthError })\n }\n );\n this._errors$.next(registerError);\n throw registerError;\n });\n }\n\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n throw error;\n }\n }\n\n /** Unregisters the subscriber, going offline for inbound calls. */\n public async unregister(): Promise<void> {\n try {\n await this._transport.execute(RPCExecute({ method: 'subscriber.offline', params: {} }));\n this._isRegistered$.next(false);\n } catch (error) {\n logger.error('[SignalWire] Failed to unregister subscriber:', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n throw error;\n }\n }\n\n /**\n * Places an outbound call to the given destination.\n * @param destination - Address URI string or {@link Address} instance to call.\n * @param options - Media and dial options (audio/video, constraints).\n * @returns The created {@link Call} instance.\n */\n public async dial(destination: string | Address, options: DialOptions = {}): Promise<Call> {\n const computed_options = {\n ...PreferencesContainer.instance.preferredMediaOptions,\n ...buildOptionsFromDestination(destination),\n ...options\n };\n\n // Wait for client to be ready\n await this.waitAuthentication();\n\n logger.debug('[SignalWire] Dialing with options:', computed_options);\n return this._clientSession.createOutboundCall(destination, computed_options);\n }\n\n /** The underlying client session for advanced RPC operations. */\n public get session(): ClientSessionWrapper {\n return this._publicSession;\n }\n\n // DeviceController interface implementation\n\n /** Observable list of available audio input (microphone) devices. */\n public get audioInputDevices$(): Observable<MediaDeviceInfo[]> {\n return this.deferEmission(this._deviceController.audioInputDevices$);\n }\n\n /** Current snapshot of available audio input devices. */\n public get audioInputDevices(): MediaDeviceInfo[] {\n return this._deviceController.audioInputDevices;\n }\n\n /** Observable list of available audio output (speaker) devices. */\n public get audioOutputDevices$(): Observable<MediaDeviceInfo[]> {\n return this.deferEmission(this._deviceController.audioOutputDevices$);\n }\n\n /** Current snapshot of available audio output devices. */\n public get audioOutputDevices(): MediaDeviceInfo[] {\n return this._deviceController.audioOutputDevices;\n }\n\n /** Observable list of available video input (camera) devices. */\n public get videoInputDevices$(): Observable<MediaDeviceInfo[]> {\n return this.deferEmission(this._deviceController.videoInputDevices$);\n }\n\n /** Current snapshot of available video input devices. */\n public get videoInputDevices(): MediaDeviceInfo[] {\n return this._deviceController.videoInputDevices;\n }\n\n /** Observable of the currently selected audio input device. */\n public get selectedAudioInputDevice$(): Observable<MediaDeviceInfo | null> {\n return this.deferEmission(this._deviceController.selectedAudioInputDevice$);\n }\n /** Observable of the currently selected audio output device. */\n public get selectedAudioOutputDevice$(): Observable<MediaDeviceInfo | null> {\n return this.deferEmission(this._deviceController.selectedAudioOutputDevice$);\n }\n /** Observable of the currently selected video input device. */\n public get selectedVideoInputDevice$(): Observable<MediaDeviceInfo | null> {\n return this.deferEmission(this._deviceController.selectedVideoInputDevice$);\n }\n /** Currently selected audio input device, or `null` if none. */\n public get selectedAudioInputDevice(): MediaDeviceInfo | null {\n return this._deviceController.selectedAudioInputDevice;\n }\n /** Currently selected audio output device, or `null` if none. */\n public get selectedAudioOutputDevice(): MediaDeviceInfo | null {\n return this._deviceController.selectedAudioOutputDevice;\n }\n /** Currently selected video input device, or `null` if none. */\n public get selectedVideoInputDevice(): MediaDeviceInfo | null {\n return this._deviceController.selectedVideoInputDevice;\n }\n /** Media track constraints for the selected audio input device. */\n public get selectedAudioInputDeviceConstraints(): MediaTrackConstraints {\n return this._deviceController.selectedAudioInputDeviceConstraints;\n }\n /** Media track constraints for the selected video input device. */\n public get selectedVideoInputDeviceConstraints(): MediaTrackConstraints {\n return this._deviceController.selectedVideoInputDeviceConstraints;\n }\n\n /** Converts a `MediaDeviceInfo` to track constraints suitable for `getUserMedia`. */\n public deviceInfoToConstraints(deviceInfo: MediaDeviceInfo | null): MediaTrackConstraints {\n return this._deviceController.deviceInfoToConstraints(deviceInfo);\n }\n\n /** Sets the preferred audio input device. */\n public selectAudioInputDevice(device: MediaDeviceInfo | null): void {\n this._deviceController.selectAudioInputDevice(device);\n }\n\n /** Sets the preferred video input device. */\n public selectVideoInputDevice(device: MediaDeviceInfo | null): void {\n this._deviceController.selectVideoInputDevice(device);\n }\n\n /** Sets the preferred audio output device. */\n public selectAudioOutputDevice(device: MediaDeviceInfo | null): void {\n this._deviceController.selectAudioOutputDevice(device);\n }\n\n /** Starts monitoring for media device changes (connect/disconnect). */\n public enableDeviceMonitoring(): void {\n this._deviceController.enableDeviceMonitoring();\n }\n\n /** Stops monitoring for media device changes. */\n public disableDeviceMonitoring(): void {\n this._deviceController.disableDeviceMonitoring();\n }\n\n public async getDeviceCapabilities(\n deviceInfo: MediaDeviceInfo\n ): Promise<MediaTrackCapabilities | null> {\n return this._deviceController.getDeviceCapabilities(deviceInfo);\n }\n\n public async isValidDevice(deviceInfo: MediaDeviceInfo | null): Promise<boolean> {\n return this._deviceController.isValidDevice(deviceInfo);\n }\n\n public override destroy(): void {\n if (this._refreshTimerId) {\n clearTimeout(this._refreshTimerId);\n this._refreshTimerId = undefined;\n }\n super.destroy();\n }\n}\nexport { SignalWire };\n","import { RequestError, RequestTimeoutError } from '../core/errors';\nimport { getLogger } from '../utils/logger';\n\nimport type { CredentialProvider } from './interfaces';\n\nconst logger = getLogger();\n\nexport class EmbedTokenCredentialProvider implements CredentialProvider {\n constructor(\n private host: string,\n private embedToken: string\n ) {}\n\n private async fetchSAT(): Promise<string> {\n const url = `https://${this.host}/api/fabric/embeds/tokens`;\n\n const timeout = 10000; // 10 seconds\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n body: JSON.stringify({ token: this.embedToken }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n const data = (await response.json()) as { token: string };\n return data.token;\n }\n\n throw new RequestError(\n `Failed to fetch SAT using embed token: ${response.status} ${response.statusText}`\n );\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof Error && error.name === 'AbortError') {\n throw new RequestTimeoutError(`Request timeout after ${timeout}ms`, { cause: error });\n }\n\n logger.error('[EmbedCredentialProvider] Request failed:', error);\n throw error;\n }\n }\n\n public async authenticate(): Promise<{ token: string; expiry_at: number }> {\n const sat = await this.fetchSAT();\n const expiryAt = Date.now() + 3600 * 1000;\n return { token: sat, expiry_at: expiryAt };\n }\n\n public async refresh(): Promise<{ token: string; expiry_at: number }> {\n return this.authenticate();\n }\n}\n","import { SignalWire } from '../clients/SignalWire';\nimport { DependencyError } from '../core/errors';\nimport { EmbedTokenCredentialProvider } from '../dependencies/EmbedTokenCredentialProvider';\n\nimport type { Call } from '../core/entities/types/call.types';\n\n/** Options for {@link embeddableCall}. */\nexport interface EmbeddableCallOptions {\n /** Destination URI to call. */\n to: string;\n /** Embed token for authentication. */\n embedToken: string;\n /** SignalWire host URL. */\n host: string;\n}\n\n/**\n * Creates a call using an embed token for simple, embeddable integrations.\n *\n * Handles client creation, authentication, and dialing in a single call.\n *\n * @param options - Embed token, host, and destination.\n * @returns The created {@link Call} instance.\n */\nexport async function embeddableCall(options: EmbeddableCallOptions): Promise<Call> {\n const { to, embedToken, host } = options;\n const requiredFailed = [];\n\n if (!to) {\n requiredFailed.push('to');\n }\n\n if (!embedToken) {\n requiredFailed.push('embedToken');\n }\n\n if (!host) {\n requiredFailed.push('host');\n }\n\n if (requiredFailed.length > 0) {\n return Promise.reject(\n new DependencyError(`Missing required options: ${requiredFailed.join(', ')}`)\n );\n }\n\n const credentialProvider = new EmbedTokenCredentialProvider(host, embedToken);\n const client = new SignalWire(credentialProvider);\n\n const call = await client.dial(to);\n\n return call;\n}\n","import type { CredentialProvider } from './interfaces';\nimport type { SDKCredential } from '../core/types/common.types';\n\n/**\n * Credential provider that returns a fixed set of credentials.\n *\n * Use when the token is already available (e.g. from a backend endpoint).\n *\n * @example\n * ```ts\n * const provider = new StaticCredentialProvider({ token: 'my-sat-token' });\n * const client = new SignalWire(provider);\n * ```\n */\nexport class StaticCredentialProvider implements CredentialProvider {\n constructor(private credentials: SDKCredential) {}\n\n /** Returns the static credentials. */\n public async authenticate(): Promise<SDKCredential> {\n return Promise.resolve(this.credentials);\n }\n}\n","/**\n * Public API entry point for @signalwire/js\n *\n * IMPORTANT: This file should NEVER be imported by internal modules.\n * Internal modules must import directly from source files.\n *\n * This file defines the minimal public API surface for external consumers.\n */\n\n// ============================================================================\n// Main Entry Point\n// ============================================================================\n\nexport { SignalWire } from './clients/SignalWire';\n\nexport { embeddableCall } from './utils/embeddableCall';\n\nexport { StaticCredentialProvider } from './dependencies/StaticCredentialProvider';\nexport type {\n CredentialProvider,\n WebRTCApiProvider,\n WebRTCMediaDevices,\n Storage\n} from './dependencies/interfaces';\n// ============================================================================\n// Error Types (exported as values for instanceof checks)\n// ============================================================================\n\nexport {\n CallCreateError,\n CollectionFetchError,\n InvalidCredentialsError,\n MediaTrackError,\n MessageParseError,\n UnexpectedError,\n VertoPongError\n} from './core/errors';\nexport type { CallError, CallErrorKind } from './core/errors';\n\n// ============================================================================\n// Domain Model Classes (exported as values for instanceof and type guards)\n// ============================================================================\n\nexport { Address } from './core/entities/Address';\nexport { WebRTCCall } from './core/entities/Call';\nexport { Participant, SelfParticipant } from './core/entities/Participant';\nexport { Subscriber } from './core/entities/Subscriber';\nexport { SelfCapabilities } from './core/capabilities';\n\n// ============================================================================\n// Domain Model Interfaces\n// ============================================================================\n\nexport type { Directory } from './core/entities/Directory';\nexport type { SessionState } from './interfaces/SessionState';\n\n// ============================================================================\n// Type Guards (exported as values for runtime checks)\n// ============================================================================\n\nexport { isSelfParticipant } from './core/entities/Participant';\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nexport { ClientPreferences } from './containers/PreferencesContainer';\n\n// ============================================================================\n// Essential Types (type-only exports)\n// ============================================================================\n\n// Client types\nexport type { SignalWireOptions, DialOptions } from './clients/SignalWire';\n\n// Call types\nexport type {\n Call,\n CallOptions,\n CallStatus,\n CallState,\n CallParticipant,\n CallSelfParticipant,\n CallAddress\n} from './core/entities/types/call.types';\n\n// Types used in Call interface methods\nexport type { TransferOptions } from './managers/types/verto-manager.types';\nexport type { VideoPosition } from './core/types/call.types';\nexport type {\n JSONRPCRequest,\n JSONRPCResponse,\n JSONRPCSuccessResponse,\n JSONRPCErrorResponse\n} from './core/RPCMessages/types/base';\nexport type { PendingRPCOptions } from './core/utils';\n\n// Types needed by web-components\nexport type { LayoutLayer } from './core/RPCMessages/types/common';\n\n// Media types\nexport type { MediaOptions, MediaDirections, MediaDirection } from './core/types/media.types';\n\n// Conversation types\nexport type { AddressHistory, TextMessage } from './core/types/conversation.types';\n\n// Participant types\nexport type { ExecuteMethod } from './core/entities/Participant';\n\n// Common types (credentials, adapters)\nexport type { SDKCredential, WebSocketAdapter, NodeSocketAdapter } from './core/types/common.types';\n\n// Capability types\nexport type {\n OnOffCapability,\n MemberCapabilities,\n CallCapabilitiesState\n} from './core/capabilities/types';\n\n// Device types\nexport type { DeviceController } from './interfaces/DeviceController';\n\n// ============================================================================\n// DO NOT EXPORT (Internal Implementation):\n// - Behaviors (Destroyable, Fetchable)\n// - Controllers (RTCPeerConnectionController, etc.)\n// - Managers (DirectoryManager, TransportManager, ConversationsManager, etc.)\n// - Containers (DependencyContainer, PreferencesContainer singleton)\n// - Internal utilities and helpers\n// - RPC Messages and protocol internals\n// ============================================================================\n\n// ============================================================================\n// Library Ready Event (for async/dynamic script loading)\n// ============================================================================\n\ndeclare const __VERSION__: string;\n\n/**\n * Library version from package.json, injected at build time.\n */\nexport const version: string = __VERSION__;\n\n/**\n * Flag indicating the library has been loaded and is ready to use.\n * For UMD builds: `window.SignalWire.ready`\n * For ES modules: `import { ready } from '@signalwire/js'`\n */\nexport const ready: boolean = true;\n\n/**\n * Emits 'signalwire:js:ready' event when the library is loaded.\n *\n * Scripts that might load BEFORE the library (check flag first):\n * ```js\n * if (window.SignalWire?.ready) {\n * // Library already loaded, use it directly\n * initApp();\n * } else {\n * window.addEventListener('signalwire:js:ready', () => initApp());\n * }\n * ```\n */\nconst emitReadyEvent = (): void => {\n if (typeof window !== 'undefined') {\n const event = new CustomEvent('signalwire:js:ready', {\n detail: { version: __VERSION__ }\n });\n window.dispatchEvent(event);\n }\n};\n\nemitReadyEvent();\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["result: Awaited<T>","logger","baseURL: string","getCredential: () => SDKCredential","timeout","headers: HTTPHeaders","DEFAULT_KEYFRAME_MAX_BURST","DEFAULT_KEYFRAME_BURST_WINDOW_MS","DEFAULT_KEYFRAME_COOLDOWN_MS","DEFAULT_ICE_RESTART_TIMEOUT_MS","DEFAULT_VIDEO_CONSTRAINTS: MediaTrackConstraints","logger","DEFAULT_KEYFRAME_MAX_BURST","DEFAULT_KEYFRAME_BURST_WINDOW_MS","DEFAULT_KEYFRAME_COOLDOWN_MS","DEFAULT_ICE_RESTART_TIMEOUT_MS","STORED_NUMBER_KEYS: (keyof StoredPreferences & keyof Preferences)[]","STORED_BOOLEAN_KEYS: (keyof StoredPreferences & keyof Preferences)[]","logger","DEVICE_STORAGE_KEYS: Record<DeviceKind, string>","initialDevicesState: DevicesState","initialSelectedDevicesState: SelectedDevicesState","webRTCApiProvider: WebRTCApiProvider","device","stored: StoredDevicePreference","devicesByKind: DevicesState","keysToRemove: string[]","storageImpl: Storage","item: string | null","logger","payload: Record<string, unknown>","logger","codecs: string[]","capabilities: PlatformCapabilities","logger","deviceController: DeviceController","iceServers: RTCIceServer[]","isConnected: boolean","transportRttMs: number","dialFn: DialFn","warnings: string[]","bandwidth: PreflightResult['bandwidth']","audioStream: MediaStream | undefined","constraints: MediaStreamConstraints","pc: RTCPeerConnection | undefined","timer","type: IceTestResult['type']","call: Call | undefined","logger","changeEvent: VisibilityChangeEvent","fromPath: string","http: HTTPRequestController","uuid","SDK_TO_VERTO_FIELD_MAP: Record<string, string>","VertoByeCauseCodes: Record<VertoByeCause, string>","logger","storage: StorageManager","deviceController: DeviceController","reconnectCallsTimeout: number","attachKey: string","timeout","DEFAULT_ON_OFF: OnOffCapability","DEFAULT_MEMBER_CAPABILITIES: MemberCapabilities","DEFAULT_CALL_CAPABILITIES: CallCapabilitiesState","logger","initialState: Partial<ParticipantState>","executeMethod: ExecuteMethod","deviceController: DeviceController","map","distinctUntilChanged","target: MemberTarget","vertoManager: VertoManager","logger","initialSessionState: Partial<SessionState>","webRtcCallSession: CallManager","options: WebRTCCallEventManagerOptions","SDP_DIRECTIONS: ReadonlySet<string>","result: MediaDirections","currentMediaKind: 'audio' | 'video' | null","currentDirection: MediaDirection | null","mediaSectionsValidCandidates: number[]","result: string[]","updatedLines: string[]","withFmtp: string[]","preferred: string[]","remaining: string[]","logger","peerConnection: RTCPeerConnection","peerConnectionControllerNegotiating$: Observable<boolean>","timer","logger","options: LocalStreamControllerOptions","stream: MediaStream","constraints: MediaStreamConstraints","logger","transceiverParams: RTCRtpTransceiverInit","constraints: MediaStreamConstraints","constraintsToApply: MediaTrackConstraints","constraintsWithDevice: MediaTrackConstraints","logger","options: RTCPeerConnectionControllerOptionsPartial","track","uuid","options","options: RTCOfferOptions","sender","mergedConstraints: MediaTrackConstraints","answer: RTCSessionDescriptionInit","logger","webRtcCallSession: WebRTCCall","attachManager: AttachManager","deviceController: DeviceController","webRTCApiProvider: WebRTCApiProvider","vertoMethod: VertoMethod","vertoByeOrAccepted: boolean | VertoByeParams | null","answerOptions: MediaOptions | undefined","deviceKind: 'audio' | 'video' | 'both' | undefined","rtcPeerConnController: RTCPeerConnectionController | null","removeTrack: 'audio' | 'video' | 'both' | undefined","logger","peerConnection: RTCPeerConnection","now","metrics: NetworkMetrics","availableOutgoingBitrate: number | undefined","issues: NetworkIssue[]","logger","DEGRADATION_ONLY_ISSUES: ReadonlySet<string>","executeMethod: ExecuteMethod","vertoManager: VertoManager","deviceController: DeviceController","logger","params: Record<string, unknown>","clientSession: ClientSession","options: CallOptions","address?: Address","uuid","response: T","self: MemberTarget","sessionManager: ClientSession","deviceController: DeviceController","attachManager: AttachManager","webRTCApiProvider: WebRTCApiProvider","networkChange$?: Observable<NetworkChangeEvent>","callError: CallError","logger","endpoint: string","http: HTTPRequestController","fetchController: FetchController<T>","update$: Observable<Partial<T>>","onError?: (error: Error) => void","originalCollection: EntityCollection<O>","filter: (i: unknown) => i is O","mapper: (item: O) => T","addressId: string","conversationManager: ConversationsProvider","addressProvider: AddressProvider<Address>","logger","uuid","timer","logger","from","getCredential: () => SDKCredential","transport: TransportManager","storage: StorageManager","authorizationStateKey: string","attachManager: AttachManager","dpopManager?: CryptoController","dpopToken: string | undefined","callSession: WebRTCCall | undefined","address: Address | undefined","clientSessionManager: ClientSessionManager","logger","groupId: string","clientSession: ClientSessionManager","http: HTTPRequestController","getSubscriberAddressId: () => string","onError?: (error: Error) => void","logger","dpopManager: CryptoController","http: HTTPRequestController","errorHandler: (error: Error) => void","getCredential: () => SDKCredential","lastError: unknown","logger","entry: DiagnosticEvent","logger","http: HTTPRequestController","conversationManager: ConversationsProvider","onError?: (error: Error) => void","logger","WebSocketConstructor: WebSocketAdapter | NodeSocketAdapter","endpoint: string","outgoingMessages$: Observable<string | ArrayBuffer | Blob>","logger","storage: StorageManager","protocolKey: string","onError?: (error: Error) => void","eventChannel: string | undefined","logger","decodeHeader: JWTHeader","error: unknown","base: SessionDiagnostics","ready","constraints: MediaStreamConstraints","selectedAudioDevice: MediaDeviceInfo | undefined","selectedVideoDevice: MediaDeviceInfo | undefined","host: string","embedToken: string","timeout","credentials: SDKCredential","version: string","ready: boolean"],"sources":["../src/behaviors/Destroyable.ts","../src/utils/asyncRetry.ts","../src/controllers/HTTPRequestController.ts","../src/controllers/DeviceHistoryManager.ts","../src/core/constants.ts","../src/utils/time.ts","../src/containers/PreferencesContainer.ts","../src/utils/toError.ts","../src/controllers/NavigatorDeviceController.ts","../src/dependencies/DefaultLocalStorage.ts","../src/managers/StorageManager.ts","../src/containers/DependencyContainer.ts","../src/controllers/CryptoController.ts","../src/controllers/NetworkMonitor.ts","../src/controllers/PlatformCapabilities.ts","../src/controllers/PreflightRunner.ts","../src/controllers/VisibilityController.ts","../src/behaviors/Fetchable.ts","../src/core/entities/Subscriber.ts","../src/core/RPCMessages/helpers.ts","../src/core/RPCMessages/RPCConnect.ts","../src/core/RPCMessages/RPCReauthenticate.ts","../src/core/RPCMessages/RPCPing.ts","../src/core/RPCMessages/RPCExecute.ts","../src/core/RPCMessages/VertoMessages.ts","../src/core/RPCMessages/RPCEventAck.ts","../src/managers/AttachManager.ts","../src/core/capabilities/types.ts","../src/core/capabilities/computeCapabilities.ts","../src/core/capabilities/SelfCapabilities.ts","../src/core/RPCMessages/utils.ts","../src/core/entities/Participant.ts","../src/core/RPCMessages/guards/base.guards.ts","../src/core/RPCMessages/guards/events.guards.ts","../src/managers/CallEventsManager.ts","../src/helpers/SDPHelper.ts","../src/controllers/ICEGatheringController.ts","../src/controllers/LocalStreamController.ts","../src/controllers/TransceiverController.ts","../src/controllers/RTCPeerConnectionController.ts","../src/core/RPCMessages/guards/verto.guards.ts","../src/managers/VertoManager.ts","../src/controllers/RTCStatsMonitor.ts","../src/managers/CallRecoveryManager.ts","../src/managers/ParticipantFactory.ts","../src/utils/qualityScore.ts","../src/core/entities/Call.ts","../src/managers/CallFactory.ts","../src/behaviors/Collection.ts","../src/core/entities/Address.ts","../src/core/utils.ts","../src/managers/ClientSessionManager.ts","../src/utils/isString.ts","../src/managers/ConversationsManager.ts","../src/managers/DeviceTokenManager.ts","../src/managers/DiagnosticsCollector.ts","../src/utils/arrays.ts","../src/utils/warnup.ts","../src/managers/DirectoryManager.ts","../src/controllers/WebSocketController.ts","../src/core/RPCMessages/guards/methods.guards.ts","../src/managers/TransportManager.ts","../src/clients/SignalWire.ts","../src/dependencies/EmbedTokenCredentialProvider.ts","../src/utils/embeddableCall.ts","../src/dependencies/StaticCredentialProvider.ts","../src/index.ts"],"sourcesContent":["import { Subject, ReplaySubject, BehaviorSubject, observeOn, asapScheduler } from 'rxjs';\n\nimport type { Observable, Subscription, Observer } from 'rxjs';\n\nexport abstract class Destroyable {\n protected subscriptions: Subscription[] = [];\n protected subjects: Subject<unknown>[] = [];\n protected _destroyed$ = new Subject<void>();\n private _observableCache?: Map<string, Observable<unknown>>;\n\n public destroy(): void {\n this._observableCache?.clear();\n this.subscriptions.forEach((sub) => sub.unsubscribe());\n this.subjects.forEach((subject) => subject.complete());\n this._destroyed$.next();\n this._destroyed$.complete();\n }\n\n protected cachedObservable<T>(key: string, factory: () => Observable<T>): Observable<T> {\n this._observableCache ??= new Map();\n let cached = this._observableCache.get(key) as Observable<T> | undefined;\n if (!cached) {\n cached = factory();\n this._observableCache.set(key, cached as Observable<unknown>);\n }\n return cached;\n }\n\n /**\n * Like `cachedObservable`, but defers emissions to the microtask queue\n * via `observeOn(asapScheduler)`.\n *\n * Use ONLY for public-facing observable getters that external consumers\n * subscribe to. Prevents a class of bugs where `BehaviorSubject` or\n * `ReplaySubject` replays synchronously during `subscribe()`, before\n * the subscription variable is assigned in the caller's scope.\n *\n * Do NOT use for observables consumed internally by the SDK — internal\n * code using `subscribeTo()`, `firstValueFrom()`, or `withLatestFrom()`\n * depends on synchronous emission delivery.\n */\n protected publicCachedObservable<T>(key: string, factory: () => Observable<T>): Observable<T> {\n const publicKey = `public:${key}`;\n this._observableCache ??= new Map();\n let cached = this._observableCache.get(publicKey) as Observable<T> | undefined;\n if (!cached) {\n cached = factory().pipe(observeOn(asapScheduler));\n this._observableCache.set(publicKey, cached as Observable<unknown>);\n }\n return cached;\n }\n\n /**\n * Wraps an observable so emissions are deferred to the microtask queue.\n *\n * Use ONLY for public-facing getters that expose a subject via\n * `.asObservable()` without going through `cachedObservable`.\n *\n * Do NOT use for observables consumed internally by the SDK.\n */\n protected deferEmission<T>(observable: Observable<T>): Observable<T> {\n return observable.pipe(observeOn(asapScheduler));\n }\n\n protected subscribeTo<T>(\n observable: Observable<T>,\n observerOrNext: Partial<Observer<T>> | ((value: T) => void) | undefined\n ): void {\n const subscription = observable.subscribe(observerOrNext);\n this.subscriptions.push(subscription);\n }\n\n protected createSubject<T>(): Subject<T> {\n const subject = new Subject<T>();\n this.subjects.push(subject as Subject<unknown>);\n return subject;\n }\n\n protected createReplaySubject<T>(bufferSize?: number, windowTime?: number): ReplaySubject<T> {\n const subject = new ReplaySubject<T>(bufferSize, windowTime);\n this.subjects.push(subject as Subject<unknown>);\n return subject;\n }\n\n protected createBehaviorSubject<T>(initialValue: T): BehaviorSubject<T> {\n const subject = new BehaviorSubject<T>(initialValue);\n this.subjects.push(subject as Subject<unknown>);\n return subject;\n }\n\n /**\n * Observable that emits when the instance is destroyed\n */\n public get destroyed$(): Observable<void> {\n return this._destroyed$.asObservable();\n }\n}\n","import { getLogger } from './logger';\nimport { ValidationError } from '../core/errors';\n\nconst DEFAULT_MAX_RETRIES = 10;\nconst DEFAULT_INITIAL_DELAY = 100;\nconst DEFAULT_DELAY_VARIATION = 1;\n\ninterface AsyncRetryOptions<T> {\n asyncCallable: () => Promise<T>;\n maxRetries?: number;\n delayFn?: () => number;\n validator?: (promiseResult: T) => void | never;\n expectedErrorHandler?: (error: unknown) => boolean;\n}\n\ninterface DelayOptions {\n initialDelay?: number;\n variation?: number;\n delayLimit?: number;\n}\n\nexport const increasingDelay = ({\n delayLimit: upperDelayLimit = Number.MAX_SAFE_INTEGER,\n initialDelay = DEFAULT_INITIAL_DELAY,\n variation = DEFAULT_DELAY_VARIATION\n}: DelayOptions): (() => number) => {\n if (initialDelay < 0) {\n throw new ValidationError('initialDelay must be gte 0');\n }\n if (upperDelayLimit < 0) {\n throw new ValidationError('upperDelayLimit must be gte 0');\n }\n if (variation < 0) {\n throw new ValidationError('variation must be gte 0');\n }\n if (initialDelay > upperDelayLimit) {\n throw new ValidationError('initialDelay must be lte delayLimit');\n }\n\n let delay = Math.min(initialDelay, upperDelayLimit);\n return () => {\n if (delay === upperDelayLimit) {\n // stop incrementing the delay and just return upperDelayLimit\n return upperDelayLimit;\n }\n const currentDelay = delay;\n delay = Math.min(delay + variation, upperDelayLimit);\n\n return currentDelay;\n };\n};\n\nexport const decreasingDelay = ({\n delayLimit: bottomDelayLimit = 0,\n initialDelay = DEFAULT_INITIAL_DELAY,\n variation = DEFAULT_DELAY_VARIATION\n}: DelayOptions): (() => number) => {\n if (initialDelay < 0) {\n throw new ValidationError('initialDelay must be gte 0');\n }\n if (bottomDelayLimit < 0) {\n throw new ValidationError('bottomDelayLimit must be gte 0');\n }\n if (variation < 0) {\n throw new ValidationError('variation must be gte 0');\n }\n if (initialDelay < bottomDelayLimit) {\n throw new ValidationError('initialDelay must be gte delayLimit');\n }\n\n let delay = Math.max(initialDelay, bottomDelayLimit);\n\n return () => {\n if (delay === bottomDelayLimit) {\n // stop incrementing the delay and just return upperDelayLimit\n return bottomDelayLimit;\n }\n const currentDelay = delay;\n delay = Math.max(delay - variation, bottomDelayLimit);\n\n return currentDelay;\n };\n};\n\nexport const constDelay = ({\n initialDelay = DEFAULT_INITIAL_DELAY\n}: Pick<DelayOptions, 'initialDelay'>): (() => number) => {\n if (initialDelay < 0) {\n throw new ValidationError('initialDelay must be gte 0');\n }\n return () => initialDelay;\n};\n\nexport const asyncRetry = async <T>({\n asyncCallable,\n maxRetries: retries = DEFAULT_MAX_RETRIES,\n delayFn,\n validator,\n expectedErrorHandler\n}: AsyncRetryOptions<T>): Promise<T> => {\n let remainingAttempts = retries - 1; // the 1st call counts as an attempt\n let wait = 0;\n\n const promiseAttempt = async (): Promise<T> => {\n try {\n let result: Awaited<T>;\n\n // Should not defer the call when: wait <= 0\n if (wait <= 0) {\n result = await asyncCallable();\n } else {\n result = await new Promise<T>((resolve, reject) =>\n setTimeout(() => {\n asyncCallable().then(resolve).catch(reject);\n }, wait)\n );\n }\n\n if (remainingAttempts) {\n // avoid messing with the normal returns in the last attempt\n validator?.(result);\n }\n\n return result;\n } catch (error) {\n if (remainingAttempts-- > 0 && !expectedErrorHandler?.(error)) {\n wait = delayFn?.() ?? 0;\n getLogger().debug(`Retrying request: ${retries - remainingAttempts} of ${retries}`);\n return promiseAttempt();\n } else {\n throw error;\n }\n }\n };\n\n return promiseAttempt();\n};\n","import { BehaviorSubject, Subject } from 'rxjs';\n\nimport { RequestTimeoutError, UnexpectedError } from '../core/errors';\nimport { asyncRetry, increasingDelay } from '../utils/asyncRetry';\nimport { getLogger } from '../utils/logger';\n\nimport type {\n HTTPRequest,\n HTTPResponse,\n HTTPHeaders,\n SDKCredential\n} from '../core/types/common.types';\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\nexport type HTTPRequestStatus = 'idle' | 'requesting' | 'success' | 'error';\n\nexport interface HTTPRequestControllerOptions {\n maxRetries?: number;\n retryDelayMin?: number;\n retryDelayMax?: number;\n requestTimeout?: number;\n}\n\nexport const GET_PARAMS = {\n method: 'GET',\n headers: {\n Accept: 'application/json'\n }\n} as const;\n\nexport const POST_PARAMS = {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n\n 'Content-Type': 'application/json'\n }\n} as const;\n\nexport class HTTPRequestController {\n // Default configuration values\n private static readonly defaultMaxRetries = 3;\n private static readonly defaultRetryDelayMinMs = 1000;\n private static readonly defaultRetryDelayMaxMs = 30000;\n private static readonly defaultRequestTimeoutMs = 30000;\n\n /** Sensitive field names to mask in debug logs. */\n private static readonly SENSITIVE_BODY_FIELDS = new Set(['dpop_token', 'token', 'jwt_token']);\n // Configuration\n private readonly maxRetries: number;\n private readonly retryDelayMin: number;\n private readonly retryDelayMax: number;\n private readonly requestTimeout: number;\n private _responses$ = new Subject<HTTPResponse>();\n private _errors$ = new Subject<Error>();\n\n // Observable streams\n private _status$ = new BehaviorSubject<HTTPRequestStatus>('idle');\n\n constructor(\n private baseURL: string,\n private readonly getCredential: () => SDKCredential,\n options: HTTPRequestControllerOptions = {}\n ) {\n this.maxRetries = options.maxRetries ?? HTTPRequestController.defaultMaxRetries;\n this.retryDelayMin = options.retryDelayMin ?? HTTPRequestController.defaultRetryDelayMinMs;\n this.retryDelayMax = options.retryDelayMax ?? HTTPRequestController.defaultRetryDelayMaxMs;\n this.requestTimeout = options.requestTimeout ?? HTTPRequestController.defaultRequestTimeoutMs;\n }\n\n public get status$(): Observable<HTTPRequestStatus> {\n return this._status$.asObservable();\n }\n\n public get status(): HTTPRequestStatus {\n return this._status$.value;\n }\n\n public get responses$(): Observable<HTTPResponse> {\n return this._responses$.asObservable();\n }\n\n public get errors$(): Observable<Error> {\n return this._errors$.asObservable();\n }\n\n public async request(request: HTTPRequest): Promise<HTTPResponse> {\n this._status$.next('requesting');\n\n try {\n const response = await this.executeWithRetry(request);\n this._status$.next('success');\n this._responses$.next(response);\n return response;\n } catch (error) {\n logger.error('[HTTPRequestController] Request error:', error);\n this._status$.next('error');\n const err =\n error instanceof Error ? error : new Error('HTTP request failed', { cause: error });\n this._errors$.next(err);\n throw err;\n }\n }\n\n private async executeWithRetry(request: HTTPRequest): Promise<HTTPResponse> {\n // Calculate variation to spread delays evenly across retry attempts\n const variation = Math.ceil(\n (this.retryDelayMax - this.retryDelayMin) / Math.max(this.maxRetries - 1, 1)\n );\n\n const delayFn = increasingDelay({\n initialDelay: this.retryDelayMin,\n variation,\n delayLimit: this.retryDelayMax\n });\n\n return asyncRetry({\n asyncCallable: async () => this.executeRequest(request),\n maxRetries: this.maxRetries,\n delayFn,\n validator: (response) => {\n // Retry on 5xx server errors\n if (response.status >= 500 && response.status < 600) {\n throw new UnexpectedError(`Server error: ${response.status} ${response.statusText}`);\n }\n }\n });\n }\n\n private async executeRequest(request: HTTPRequest): Promise<HTTPResponse> {\n const url = this.buildURL(request.url);\n const headers = this.buildHeaders(request.headers);\n const timeout = request.timeout ?? this.requestTimeout;\n\n logger.debug('[HTTPRequestController] Executing request:', {\n method: request.method,\n url,\n headers: Object.keys(headers).reduce((acc, key) => {\n // Mask Authorization header for security\n // eslint-disable-next-line no-param-reassign\n acc[key] = key === 'Authorization' ? `${headers[key].substring(0, 20)}...` : headers[key];\n return acc;\n }, {} as HTTPHeaders),\n body: this.sanitizeBody(request.body)\n });\n // {\"from_fabric_address_id\":\"03a98611-d38f-405a-af49-fcb51e7f22ad\",\"fabric_address_ids\":[\"31c0afc2-93f3-4530-9a7d-55613d40850d\",\"03a98611-d38f-405a-af49-fcb51e7f22ad\"]}\n // {\"from_fabric_address_id\":\"060b5d3e-5df0-45d9-911e-660779e593da\",\"fabric_address_ids\":[\"31c0afc2-93f3-4530-9a7d-55613d40850d\",\"060b5d3e-5df0-45d9-911e-660779e593da\"]}\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n method: request.method,\n headers,\n body: request.body,\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n const httpResponse = await this.convertResponse(response);\n\n logger.debug('[HTTPRequestController] Response received:', {\n status: response.status,\n statusText: response.statusText,\n headers: [...response.headers.entries()],\n body: httpResponse.body ? httpResponse.body.substring(0, 200) : '(empty)' // Show first 200 chars\n });\n\n return httpResponse;\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof Error && error.name === 'AbortError') {\n throw new RequestTimeoutError(`Request timeout after ${timeout}ms`, { cause: error });\n }\n\n logger.error('[HTTPRequestController] Request failed:', error);\n throw error;\n }\n }\n\n private buildURL(url: string | URL): string {\n const urlString = typeof url === 'string' ? url : url.toString();\n\n // If URL is absolute, return as-is\n if (urlString.startsWith('http://') || urlString.startsWith('https://')) {\n return urlString;\n }\n\n // Ensure base URL doesn't end with '/' and path starts with '/'\n const base = this.baseURL.endsWith('/') ? this.baseURL.slice(0, -1) : this.baseURL;\n const path = urlString.startsWith('/') ? urlString : `/${urlString}`;\n\n return `${base}${path}`;\n }\n\n private buildHeaders(requestHeaders?: HTTPHeaders): HTTPHeaders {\n const headers: HTTPHeaders = { ...(requestHeaders ?? {}) };\n\n // Add authentication header\n const credential = this.getCredential();\n if (credential.token) {\n headers.Authorization = `Bearer ${credential.token}`;\n logger.debug(\n '[HTTPRequestController] Using Bearer token auth, token length:',\n credential.token.length\n );\n } else {\n logger.warn('[HTTPRequestController] No credentials available for authentication');\n }\n\n return headers;\n }\n\n /**\n * Sanitizes a request body for debug logging by masking sensitive fields.\n */\n private sanitizeBody(\n body:\n | string\n | Blob\n | ArrayBuffer\n | FormData\n | URLSearchParams\n | ReadableStream\n | null\n | undefined\n ): string | undefined {\n if (!body || typeof body !== 'string') {\n return body ? '(non-string body)' : undefined;\n }\n\n try {\n const parsed = JSON.parse(body) as Record<string, unknown>;\n const sanitized = { ...parsed };\n for (const key of Object.keys(sanitized)) {\n if (\n HTTPRequestController.SENSITIVE_BODY_FIELDS.has(key) &&\n typeof sanitized[key] === 'string'\n ) {\n sanitized[key] = `${sanitized[key].substring(0, 20)}...[redacted]`;\n }\n }\n return JSON.stringify(sanitized);\n } catch {\n // Not JSON — return truncated\n return body.length > 200 ? `${body.substring(0, 200)}...` : body;\n }\n }\n\n private async convertResponse(response: Response): Promise<HTTPResponse> {\n // Convert Headers to plain object\n const headers: HTTPHeaders = {};\n response.headers.forEach((value, key) => {\n headers[key] = value;\n });\n\n // Read response body as text\n const bodyText = await response.text();\n\n return {\n status: response.status,\n statusText: response.statusText,\n headers,\n body: bodyText,\n ok: response.ok,\n url: response.url\n };\n }\n}\n","/**\n * DeviceHistoryManager - Maintains a per-kind stack of recently used devices.\n *\n * When a device disappears during a call, the SDK uses this history to fall\n * back to the previously used device rather than picking an arbitrary default.\n * This handles the common scenario of AirPods disconnecting and the user\n * expecting to return to their built-in microphone.\n *\n * This is a pure data structure -- it does not extend Destroyable since it\n * holds no subscriptions or subjects.\n *\n * @see Section 5.2 of the Implementation Guide\n */\n\n/** Maximum number of entries retained per device kind. */\nconst MAX_HISTORY_SIZE = 5;\n\n/** Device kinds tracked by the history manager. */\ntype DeviceKind = 'audioinput' | 'audiooutput' | 'videoinput';\n\n/**\n * Serializable subset of MediaDeviceInfo stored in the history stack.\n *\n * The browser's MediaDeviceInfo is not serializable, so we store only the\n * fields needed for matching.\n */\ninterface HistoryEntry {\n readonly deviceId: string;\n readonly label: string;\n readonly groupId: string;\n readonly kind: DeviceKind;\n}\n\n/**\n * Converts a MediaDeviceInfo to a serializable HistoryEntry.\n */\nconst toHistoryEntry = (device: MediaDeviceInfo, kind: DeviceKind): HistoryEntry => ({\n deviceId: device.deviceId,\n label: device.label,\n groupId: device.groupId,\n kind\n});\n\nexport class DeviceHistoryManager {\n private readonly _stacks: Record<DeviceKind, HistoryEntry[]> = {\n audioinput: [],\n audiooutput: [],\n videoinput: []\n };\n\n /**\n * Push a device onto the history stack for the given kind.\n *\n * If the device is already at the top of the stack, this is a no-op.\n * Duplicate entries deeper in the stack are removed to keep the stack clean.\n * The stack is capped at {@link MAX_HISTORY_SIZE} entries.\n *\n * @param kind - The device kind (audioinput, audiooutput, videoinput)\n * @param device - The MediaDeviceInfo to record\n */\n public push(kind: DeviceKind, device: MediaDeviceInfo): void {\n const entry = toHistoryEntry(device, kind);\n const current = this._stacks[kind];\n\n // Skip if the device is already at the top\n if (current.length > 0 && current[0].deviceId === entry.deviceId) {\n return;\n }\n\n // Remove any existing entry for this device (dedup)\n const filtered = current.filter((e) => e.deviceId !== entry.deviceId);\n\n // Prepend the new entry and cap at MAX_HISTORY_SIZE\n this._stacks[kind] = [entry, ...filtered].slice(0, MAX_HISTORY_SIZE);\n }\n\n /**\n * Pop the most recent device from the history stack for the given kind.\n *\n * @param kind - The device kind\n * @returns The most recent HistoryEntry, or undefined if the stack is empty\n */\n public pop(kind: DeviceKind): HistoryEntry | undefined {\n const current = this._stacks[kind];\n\n if (current.length === 0) {\n return undefined;\n }\n\n const [top, ...rest] = current;\n this._stacks[kind] = rest;\n return top;\n }\n\n /**\n * Find the most recent device in the history that is still present in the\n * available device list.\n *\n * Searches the history stack from most recent to oldest. A device is\n * considered a match if its deviceId or (groupId + label) match an\n * available device.\n *\n * @param kind - The device kind\n * @param availableDevices - The current list of available devices\n * @returns The matching MediaDeviceInfo from availableDevices, or undefined\n */\n public findInHistory(\n kind: DeviceKind,\n availableDevices: readonly MediaDeviceInfo[]\n ): MediaDeviceInfo | undefined {\n const history = this._stacks[kind];\n\n for (const entry of history) {\n // Exact deviceId match\n const exactMatch = availableDevices.find((d) => d.deviceId === entry.deviceId);\n if (exactMatch) {\n return exactMatch;\n }\n\n // groupId + label match (same physical device, ID may have changed)\n const groupMatch = availableDevices.find(\n (d) => d.groupId === entry.groupId && d.label === entry.label\n );\n if (groupMatch) {\n return groupMatch;\n }\n }\n\n return undefined;\n }\n\n /**\n * Return a read-only snapshot of the history stack for a given kind.\n *\n * @param kind - The device kind\n * @returns A read-only array of HistoryEntry objects, most recent first\n */\n public getHistory(kind: DeviceKind): readonly HistoryEntry[] {\n return this._stacks[kind];\n }\n\n /**\n * Clear all history stacks.\n */\n public clear(): void {\n this._stacks.audioinput = [];\n this._stacks.audiooutput = [];\n this._stacks.videoinput = [];\n }\n}\n","export const INVITE_VERSION = 1000;\nexport const DEFAULT_ICE_CANDIDATE_TIMEOUT_MS = 600;\nexport const DEFAULT_ICE_GATHERING_TIMEOUT_MS = 6_000;\nexport const DEFAULT_RECONNECT_CALLS_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\nexport const DEFAULT_REATTACH_WAIT_TIMEOUT_MS = 10_000; // 10 seconds to wait for server verto.attach\nexport const DEFAULT_CONNECTION_TIMEOUT_MS = 10_000;\nexport const DEFAULT_RECONNECT_DELAY_MIN_MS = 100;\nexport const DEFAULT_RECONNECT_DELAY_MAX_MS = 3000;\nexport const DEFAULT_DEVICE_DEBOUNCE_TIME_MS = 1500;\nexport const DEFAULT_DEVICE_POLLING_INTERVAL_MS = 0; // Disabled by default\nexport const PREFERENCES_STORAGE_KEY = 'sw:preferences';\n\n/** Scope value that enables automatic token refresh. */\nexport const SAT_REFRESH_SCOPE = 'sat:refresh';\n\n/** API endpoints for device token operations. */\nexport const DEVICE_TOKEN_ENDPOINT = '/api/fabric/subscriber/devices/token';\nexport const DEVICE_REFRESH_ENDPOINT = '/api/fabric/subscriber/devices/refresh';\n\n/** Default device token TTL in seconds (15 minutes). */\nexport const DEVICE_TOKEN_DEFAULT_EXPIRE_IN = 900;\n\n/** Buffer time in milliseconds before expiry to trigger refresh. */\nexport const DEVICE_TOKEN_REFRESH_BUFFER_MS = 30_000;\n\n/** Maximum retry attempts for device token refresh on transient failure. */\nexport const DEVICE_TOKEN_REFRESH_MAX_RETRIES = 3;\n\n/** Base delay in milliseconds for exponential backoff on refresh retry. */\nexport const DEVICE_TOKEN_REFRESH_RETRY_BASE_MS = 1000;\n\n/** Maximum retry attempts for developer credential refresh on transient failure. */\nexport const CREDENTIAL_REFRESH_MAX_RETRIES = 5;\n\n/** Base delay in milliseconds for exponential backoff on credential refresh retry. */\nexport const CREDENTIAL_REFRESH_RETRY_BASE_MS = 1000;\n\n/** Maximum delay in milliseconds for credential refresh backoff. */\nexport const CREDENTIAL_REFRESH_MAX_DELAY_MS = 30_000;\n\n/** Buffer in milliseconds before token expiry to trigger refresh. */\nexport const CREDENTIAL_REFRESH_BUFFER_MS = 5000;\n\n/** JSON-RPC error code for requester validation failure (corrupted auth state). */\nexport const RPC_ERROR_REQUESTER_VALIDATION_FAILED = -32003;\n\n/** JSON-RPC error code for invalid params (e.g., missing authentication block). */\nexport const RPC_ERROR_INVALID_PARAMS = -32602;\n\n/** JSON-RPC error code for authentication failure (invalid token, missing DPoP, etc.). */\nexport const RPC_ERROR_AUTHENTICATION_FAILED = -32002;\n\n// =============================================================================\n// STATS MONITORING DEFAULTS (Section 1)\n// =============================================================================\n\n/** Default polling interval for RTCPeerConnection.getStats() in milliseconds. */\nexport const DEFAULT_STATS_POLLING_INTERVAL_MS = 1000;\n\n/** Number of initial samples used to build a baseline for spike detection. */\nexport const DEFAULT_STATS_BASELINE_SAMPLES = 10;\n\n/** Duration in ms with no inbound audio packets before emitting a critical issue. */\nexport const DEFAULT_STATS_NO_PACKET_THRESHOLD_MS = 2000;\n\n/** Multiplier applied to baseline RTT to detect a warning-level RTT spike. */\nexport const DEFAULT_STATS_RTT_SPIKE_MULTIPLIER = 3;\n\n/** Packet loss fraction (0-1) above which a warning is emitted. */\nexport const DEFAULT_STATS_PACKET_LOSS_THRESHOLD = 0.05;\n\n/** Multiplier applied to baseline jitter to detect a jitter spike. */\nexport const DEFAULT_STATS_JITTER_SPIKE_MULTIPLIER = 4;\n\n/** Number of seconds of metrics history to retain. */\nexport const DEFAULT_STATS_HISTORY_SIZE = 30;\n\n// =============================================================================\n// KEYFRAME THROTTLING DEFAULTS (Section 2)\n// =============================================================================\n\n/** Maximum keyframe requests allowed within a single burst window. */\nexport const DEFAULT_KEYFRAME_MAX_BURST = 3;\n\n/** Duration of the keyframe burst window in milliseconds. */\nexport const DEFAULT_KEYFRAME_BURST_WINDOW_MS = 3000;\n\n/** Cooldown period in ms after burst limit is reached before allowing more keyframes. */\nexport const DEFAULT_KEYFRAME_COOLDOWN_MS = 10_000;\n\n// =============================================================================\n// RE-INVITE / ICE RESTART DEFAULTS (Section 2 & 19)\n// =============================================================================\n\n/** Minimum time between re-INVITE attempts in milliseconds. */\nexport const DEFAULT_REINVITE_DEBOUNCE_TIME_MS = 10_000;\n\n/** Maximum number of re-INVITE attempts per call. */\nexport const DEFAULT_REINVITE_MAX_ATTEMPTS = 3;\n\n/** Timeout for a single re-INVITE attempt in milliseconds. */\nexport const DEFAULT_REINVITE_TIMEOUT_MS = 5000;\n\n// =============================================================================\n// RECOVERY PIPELINE DEFAULTS (Section 19)\n// =============================================================================\n\n/** Debounce window in ms to collapse multiple detection signals into one trigger. */\nexport const DEFAULT_RECOVERY_DEBOUNCE_TIME_MS = 2000;\n\n/** Cooldown period in ms between recovery attempts. */\nexport const DEFAULT_RECOVERY_COOLDOWN_MS = 10_000;\n\n/** Grace period in ms before treating ICE 'disconnected' as a failure. */\nexport const DEFAULT_ICE_DISCONNECTED_GRACE_PERIOD_MS = 3000;\n\n/** Timeout for a single ICE restart attempt in milliseconds. */\nexport const DEFAULT_ICE_RESTART_TIMEOUT_MS = 5000;\n\n/** Maximum recovery attempts before emitting 'max_attempts_reached'. */\nexport const DEFAULT_MAX_RECOVERY_ATTEMPTS = 3;\n\n// =============================================================================\n// DEVICE MANAGEMENT DEFAULTS (Section 5)\n// =============================================================================\n\n/** Whether to persist device selections to storage by default. */\nexport const DEFAULT_PERSIST_DEVICE_SELECTION = true;\n\n/** Whether to auto-apply device changes to active calls by default. */\nexport const DEFAULT_SYNC_DEVICES_TO_ACTIVE_CALLS = true;\n\n/** Storage keys for persisted device selections. */\nexport const DEVICE_STORAGE_KEY_AUDIO_INPUT = 'sw:device:audioinput';\nexport const DEVICE_STORAGE_KEY_AUDIO_OUTPUT = 'sw:device:audiooutput';\nexport const DEVICE_STORAGE_KEY_VIDEO_INPUT = 'sw:device:videoinput';\n\n/** SDK storage key prefix used for targeted cleanup (factory reset). */\nexport const SDK_STORAGE_KEY_PREFIX = 'sw:';\n\n// =============================================================================\n// VISIBILITY DEFAULTS (Section 4)\n// =============================================================================\n\n/** Whether to auto-mute video when the tab becomes hidden. */\nexport const DEFAULT_AUTO_MUTE_VIDEO_ON_HIDDEN = false;\n\n/** Whether to re-enumerate devices when the page becomes visible. */\nexport const DEFAULT_REFRESH_DEVICES_ON_VISIBLE = true;\n\n/** Whether to check peer connection health when the page becomes visible. */\nexport const DEFAULT_CHECK_CONNECTION_ON_VISIBLE = true;\n\n// =============================================================================\n// DEGRADATION THRESHOLDS (Section 22)\n// =============================================================================\n\n/** Whether automatic video degradation on low bandwidth is enabled. */\nexport const DEFAULT_ENABLE_AUTO_DEGRADATION = true;\n\n/** Bitrate in kbps below which video is automatically disabled. */\nexport const DEFAULT_DEGRADATION_BITRATE_THRESHOLD_KBPS = 150;\n\n/** Bitrate in kbps above which video is automatically re-enabled (hysteresis). */\nexport const DEFAULT_DEGRADATION_RECOVERY_THRESHOLD_KBPS = 300;\n\n// =============================================================================\n// NETWORK RECOVERY FEATURE FLAGS (Section 19)\n// =============================================================================\n\n/** Whether relay-only escalation is enabled as a last-resort recovery tier. */\nexport const DEFAULT_ENABLE_RELAY_FALLBACK = true;\n\n/** Whether to listen for browser online/offline/connection events. */\nexport const DEFAULT_ENABLE_NETWORK_CHANGE_DETECTION = true;\n\n/** Whether to intercept server-sent media-timeout hangups and attempt recovery. */\nexport const DEFAULT_ENABLE_SERVER_HANGUP_INTERCEPTION = true;\n\n// =============================================================================\n// DEFAULT AUDIO/VIDEO CONSTRAINTS (Section 16.5 & 16.6)\n// =============================================================================\n\n/** Default audio track constraints applied when no explicit constraints are provided. */\nexport const DEFAULT_AUDIO_CONSTRAINTS: MediaTrackConstraints = {\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true\n};\n\n/** Default video track constraints applied when video is enabled without explicit constraints. */\nexport const DEFAULT_VIDEO_CONSTRAINTS: MediaTrackConstraints = {\n width: { ideal: 1280 },\n height: { ideal: 720 },\n aspectRatio: 16 / 9\n};\n\n/** Whether stereo Opus is enabled by default. */\nexport const DEFAULT_STEREO_AUDIO = false;\n\n/** Max average bitrate for stereo Opus in bits per second. */\nexport const DEFAULT_STEREO_MAX_AVERAGE_BITRATE = 510_000;\n\n// =============================================================================\n// QUALITY LEVEL THRESHOLDS (Section 21)\n// =============================================================================\n\n/** MOS score threshold: at or above this is 'excellent'. */\nexport const QUALITY_THRESHOLD_EXCELLENT = 4.0;\n\n/** MOS score threshold: at or above this is 'good'. */\nexport const QUALITY_THRESHOLD_GOOD = 3.5;\n\n/** MOS score threshold: at or above this is 'fair'. */\nexport const QUALITY_THRESHOLD_FAIR = 3.0;\n\n/** MOS score threshold: at or above this is 'poor'. */\nexport const QUALITY_THRESHOLD_POOR = 2.0;\n\n/** MOS score at or below QUALITY_THRESHOLD_POOR is 'critical'. */\n\n// =============================================================================\n// PREFLIGHT DEFAULTS (Section 20)\n// =============================================================================\n\n/** Default duration for the preflight media/bandwidth test in seconds. */\nexport const DEFAULT_PREFLIGHT_DURATION_SEC = 10;\n","export function fromSecToMs(seconds: number): number {\n return seconds * 1000;\n}\n\nexport function fromMsToSec(milliseconds: number): number {\n return Math.round(milliseconds / 100) / 10;\n}\n","import {\n DEFAULT_CONNECTION_TIMEOUT_MS,\n DEFAULT_DEVICE_DEBOUNCE_TIME_MS,\n DEFAULT_DEVICE_POLLING_INTERVAL_MS,\n DEFAULT_ICE_CANDIDATE_TIMEOUT_MS,\n DEFAULT_ICE_GATHERING_TIMEOUT_MS,\n DEFAULT_RECONNECT_CALLS_TIMEOUT_MS,\n DEFAULT_RECONNECT_DELAY_MAX_MS,\n DEFAULT_RECONNECT_DELAY_MIN_MS,\n PREFERENCES_STORAGE_KEY,\n DEFAULT_STATS_POLLING_INTERVAL_MS,\n DEFAULT_STATS_BASELINE_SAMPLES,\n DEFAULT_STATS_NO_PACKET_THRESHOLD_MS,\n DEFAULT_STATS_RTT_SPIKE_MULTIPLIER,\n DEFAULT_STATS_PACKET_LOSS_THRESHOLD,\n DEFAULT_STATS_JITTER_SPIKE_MULTIPLIER,\n DEFAULT_STATS_HISTORY_SIZE,\n DEFAULT_KEYFRAME_MAX_BURST,\n DEFAULT_KEYFRAME_BURST_WINDOW_MS,\n DEFAULT_KEYFRAME_COOLDOWN_MS,\n DEFAULT_REINVITE_DEBOUNCE_TIME_MS,\n DEFAULT_REINVITE_MAX_ATTEMPTS,\n DEFAULT_REINVITE_TIMEOUT_MS,\n DEFAULT_RECOVERY_DEBOUNCE_TIME_MS,\n DEFAULT_RECOVERY_COOLDOWN_MS,\n DEFAULT_ICE_DISCONNECTED_GRACE_PERIOD_MS,\n DEFAULT_ICE_RESTART_TIMEOUT_MS,\n DEFAULT_MAX_RECOVERY_ATTEMPTS,\n DEFAULT_PERSIST_DEVICE_SELECTION,\n DEFAULT_SYNC_DEVICES_TO_ACTIVE_CALLS,\n DEFAULT_AUTO_MUTE_VIDEO_ON_HIDDEN,\n DEFAULT_REFRESH_DEVICES_ON_VISIBLE,\n DEFAULT_CHECK_CONNECTION_ON_VISIBLE,\n DEFAULT_ENABLE_AUTO_DEGRADATION,\n DEFAULT_DEGRADATION_BITRATE_THRESHOLD_KBPS,\n DEFAULT_DEGRADATION_RECOVERY_THRESHOLD_KBPS,\n DEFAULT_ENABLE_RELAY_FALLBACK,\n DEFAULT_ENABLE_NETWORK_CHANGE_DETECTION,\n DEFAULT_ENABLE_SERVER_HANGUP_INTERCEPTION,\n DEFAULT_STEREO_AUDIO\n} from '../core/constants';\nimport { getLogger } from '../utils/logger';\nimport { fromMsToSec, fromSecToMs } from '../utils/time';\n\nimport type { MediaOptions } from '../core/types/media.types';\nimport type { StorageManager } from '../managers/StorageManager';\n\nconst logger = getLogger();\n\nexport interface Preferences {\n connectionTimeout: number;\n reconnectDelayMin: number;\n reconnectDelayMax: number;\n reconnectCallsTimeout: number;\n relayHost?: string;\n receiveVideo: boolean;\n receiveAudio: boolean;\n preferredAudioInput: MediaDeviceInfo | null;\n preferredAudioOutput: MediaDeviceInfo | null;\n preferredVideoInput: MediaDeviceInfo | null;\n inputAudioDeviceConstraints: MediaTrackConstraints | undefined;\n inputVideoDeviceConstraints: MediaTrackConstraints | undefined;\n disableUdpIceServers: boolean;\n relayOnly: boolean;\n iceCandidateTimeout: number;\n iceGatheringTimeout: number;\n deviceDebounceTime: number;\n devicePollingInterval: number;\n iceServers?: RTCIceServer[];\n defaultSignalWireOptions: {\n skipConnection: boolean;\n skipRegister: boolean;\n reconnectAttachedCalls: boolean;\n skipDeviceMonitoring: boolean;\n savePreferences: boolean;\n };\n inviteSubscribeScreenshare: string[];\n inviteSubscribeAdditionalDevice: string[];\n inviteSubscribeMainDevice: string[];\n userVariables: Record<string, unknown>;\n readonly preferredMediaOptions: MediaOptions;\n\n // Stats monitoring (Section 1)\n statsPollingInterval: number;\n statsBaselineSamples: number;\n statsNoPacketThreshold: number;\n statsRttSpikeMultiplier: number;\n statsPacketLossThreshold: number;\n statsJitterSpikeMultiplier: number;\n statsHistorySize: number;\n\n // Keyframe throttling (Section 2)\n keyframeMaxBurst: number;\n keyframeBurstWindow: number;\n keyframeCooldown: number;\n\n // Re-INVITE / ICE restart (Section 2 & 19)\n reinviteDebounceTime: number;\n reinviteMaxAttempts: number;\n reinviteTimeout: number;\n\n // Recovery pipeline (Section 19)\n recoveryDebounceTime: number;\n recoveryCooldown: number;\n iceDisconnectedGracePeriod: number;\n iceRestartTimeout: number;\n maxRecoveryAttempts: number;\n enableRelayFallback: boolean;\n enableNetworkChangeDetection: boolean;\n enableServerHangupInterception: boolean;\n\n // Device management (Section 5)\n persistDeviceSelection: boolean;\n syncDevicesToActiveCalls: boolean;\n\n // Visibility (Section 4)\n autoMuteVideoOnHidden: boolean;\n refreshDevicesOnVisible: boolean;\n checkConnectionOnVisible: boolean;\n\n // Media defaults (Section 16.5 & 16.6)\n defaultAudioConstraints: MediaTrackConstraints | undefined;\n defaultVideoConstraints: MediaTrackConstraints | undefined;\n stereoAudio: boolean;\n\n // Degradation (Section 22)\n enableAutoDegradation: boolean;\n degradationBitrateThreshold: number;\n degradationRecoveryThreshold: number;\n\n // Codec preferences (Section 23)\n preferredVideoCodecs: string[];\n preferredAudioCodecs: string[];\n}\nexport class PreferencesContainer implements Preferences {\n static get instance(): Preferences {\n this._instance ??= new PreferencesContainer();\n return this._instance;\n }\n\n deviceDebounceTime = DEFAULT_DEVICE_DEBOUNCE_TIME_MS;\n devicePollingInterval = DEFAULT_DEVICE_POLLING_INTERVAL_MS;\n\n reconnectCallsTimeout = DEFAULT_RECONNECT_CALLS_TIMEOUT_MS;\n // 5 minutes\n iceServers?: RTCIceServer[];\n connectionTimeout = DEFAULT_CONNECTION_TIMEOUT_MS;\n reconnectDelayMin = DEFAULT_RECONNECT_DELAY_MIN_MS;\n reconnectDelayMax = DEFAULT_RECONNECT_DELAY_MAX_MS;\n disableUdpIceServers = false;\n relayOnly = false;\n iceCandidateTimeout = DEFAULT_ICE_CANDIDATE_TIMEOUT_MS;\n iceGatheringTimeout = DEFAULT_ICE_GATHERING_TIMEOUT_MS;\n defaultSignalWireOptions = {\n skipConnection: false,\n skipRegister: false,\n reconnectAttachedCalls: false,\n skipDeviceMonitoring: false,\n savePreferences: false\n };\n relayHost?: string;\n receiveVideo = false;\n receiveAudio = true;\n preferredAudioInput: MediaDeviceInfo | null = null;\n preferredAudioOutput: MediaDeviceInfo | null = null;\n preferredVideoInput: MediaDeviceInfo | null = null;\n inviteSubscribeScreenshare: string[] = ['video.room.screenshare'];\n inviteSubscribeAdditionalDevice: string[] = [\n // FIXME verify what to subscribe to for additional devices\n ];\n inviteSubscribeMainDevice: string[] = [\n 'track',\n 'destroy',\n 'member.updated.videoMuted',\n 'layout.changed',\n 'room.subscribed',\n 'member.updated.audioMuted',\n 'media.connected',\n 'room.updated',\n 'call.joined'\n ];\n userVariables = {};\n\n // Stats monitoring\n statsPollingInterval = DEFAULT_STATS_POLLING_INTERVAL_MS;\n statsBaselineSamples = DEFAULT_STATS_BASELINE_SAMPLES;\n statsNoPacketThreshold = DEFAULT_STATS_NO_PACKET_THRESHOLD_MS;\n statsRttSpikeMultiplier = DEFAULT_STATS_RTT_SPIKE_MULTIPLIER;\n statsPacketLossThreshold = DEFAULT_STATS_PACKET_LOSS_THRESHOLD;\n statsJitterSpikeMultiplier = DEFAULT_STATS_JITTER_SPIKE_MULTIPLIER;\n statsHistorySize = DEFAULT_STATS_HISTORY_SIZE;\n\n // Keyframe throttling\n keyframeMaxBurst = DEFAULT_KEYFRAME_MAX_BURST;\n keyframeBurstWindow = DEFAULT_KEYFRAME_BURST_WINDOW_MS;\n keyframeCooldown = DEFAULT_KEYFRAME_COOLDOWN_MS;\n\n // Re-INVITE / ICE restart\n reinviteDebounceTime = DEFAULT_REINVITE_DEBOUNCE_TIME_MS;\n reinviteMaxAttempts = DEFAULT_REINVITE_MAX_ATTEMPTS;\n reinviteTimeout = DEFAULT_REINVITE_TIMEOUT_MS;\n\n // Recovery pipeline\n recoveryDebounceTime = DEFAULT_RECOVERY_DEBOUNCE_TIME_MS;\n recoveryCooldown = DEFAULT_RECOVERY_COOLDOWN_MS;\n iceDisconnectedGracePeriod = DEFAULT_ICE_DISCONNECTED_GRACE_PERIOD_MS;\n iceRestartTimeout = DEFAULT_ICE_RESTART_TIMEOUT_MS;\n maxRecoveryAttempts = DEFAULT_MAX_RECOVERY_ATTEMPTS;\n enableRelayFallback = DEFAULT_ENABLE_RELAY_FALLBACK;\n enableNetworkChangeDetection = DEFAULT_ENABLE_NETWORK_CHANGE_DETECTION;\n enableServerHangupInterception = DEFAULT_ENABLE_SERVER_HANGUP_INTERCEPTION;\n\n // Device management\n persistDeviceSelection = DEFAULT_PERSIST_DEVICE_SELECTION;\n syncDevicesToActiveCalls = DEFAULT_SYNC_DEVICES_TO_ACTIVE_CALLS;\n\n // Visibility\n autoMuteVideoOnHidden = DEFAULT_AUTO_MUTE_VIDEO_ON_HIDDEN;\n refreshDevicesOnVisible = DEFAULT_REFRESH_DEVICES_ON_VISIBLE;\n checkConnectionOnVisible = DEFAULT_CHECK_CONNECTION_ON_VISIBLE;\n\n // Media defaults\n defaultAudioConstraints: MediaTrackConstraints | undefined = undefined;\n defaultVideoConstraints: MediaTrackConstraints | undefined = undefined;\n stereoAudio = DEFAULT_STEREO_AUDIO;\n\n // Degradation\n enableAutoDegradation = DEFAULT_ENABLE_AUTO_DEGRADATION;\n degradationBitrateThreshold = DEFAULT_DEGRADATION_BITRATE_THRESHOLD_KBPS;\n degradationRecoveryThreshold = DEFAULT_DEGRADATION_RECOVERY_THRESHOLD_KBPS;\n\n // Codec preferences\n preferredVideoCodecs: string[] = [];\n preferredAudioCodecs: string[] = [];\n\n private _inputAudioDeviceConstraints?: MediaTrackConstraints;\n private _inputVideoDeviceConstraints?: MediaTrackConstraints;\n private static _instance?: PreferencesContainer;\n\n private constructor() {\n // Private constructor to enforce singleton pattern\n }\n\n public get preferredMediaOptions(): MediaOptions {\n return {\n receiveVideo: this.receiveVideo,\n receiveAudio: this.receiveAudio,\n inputAudioDeviceConstraints: this.inputAudioDeviceConstraints,\n inputVideoDeviceConstraints: this.inputVideoDeviceConstraints\n };\n }\n\n get inputAudioDeviceConstraints(): MediaTrackConstraints | undefined {\n return this._inputAudioDeviceConstraints;\n }\n\n set inputAudioDeviceConstraints(value: MediaTrackConstraints | undefined) {\n this._inputAudioDeviceConstraints = value;\n }\n\n get inputVideoDeviceConstraints(): MediaTrackConstraints | undefined {\n return this._inputVideoDeviceConstraints;\n }\n\n set inputVideoDeviceConstraints(value: MediaTrackConstraints | undefined) {\n this._inputVideoDeviceConstraints = value;\n }\n}\n\n/** Serializable subset of preferences that can be persisted to storage. */\ninterface StoredPreferences {\n connectionTimeout?: number;\n reconnectCallsTimeout?: number;\n reconnectDelayMin?: number;\n reconnectDelayMax?: number;\n relayHost?: string;\n receiveVideo?: boolean;\n receiveAudio?: boolean;\n disableUdpIceServers?: boolean;\n relayOnly?: boolean;\n iceCandidateTimeout?: number;\n iceGatheringTimeout?: number;\n deviceDebounceTime?: number;\n devicePollingInterval?: number;\n iceServers?: RTCIceServer[];\n userVariables?: Record<string, unknown>;\n\n // Stats monitoring\n statsPollingInterval?: number;\n statsBaselineSamples?: number;\n statsNoPacketThreshold?: number;\n statsRttSpikeMultiplier?: number;\n statsPacketLossThreshold?: number;\n statsJitterSpikeMultiplier?: number;\n statsHistorySize?: number;\n\n // Keyframe throttling\n keyframeMaxBurst?: number;\n keyframeBurstWindow?: number;\n keyframeCooldown?: number;\n\n // Re-INVITE / ICE restart\n reinviteDebounceTime?: number;\n reinviteMaxAttempts?: number;\n reinviteTimeout?: number;\n\n // Recovery pipeline\n recoveryDebounceTime?: number;\n recoveryCooldown?: number;\n iceDisconnectedGracePeriod?: number;\n iceRestartTimeout?: number;\n maxRecoveryAttempts?: number;\n enableRelayFallback?: boolean;\n enableNetworkChangeDetection?: boolean;\n enableServerHangupInterception?: boolean;\n\n // Device management\n persistDeviceSelection?: boolean;\n syncDevicesToActiveCalls?: boolean;\n\n // Visibility\n autoMuteVideoOnHidden?: boolean;\n refreshDevicesOnVisible?: boolean;\n checkConnectionOnVisible?: boolean;\n\n // Media defaults\n stereoAudio?: boolean;\n\n // Degradation\n enableAutoDegradation?: boolean;\n degradationBitrateThreshold?: number;\n degradationRecoveryThreshold?: number;\n\n // Codec preferences\n preferredVideoCodecs?: string[];\n preferredAudioCodecs?: string[];\n}\n\n/** Keys of StoredPreferences that map to number fields on PreferencesContainer. */\nconst STORED_NUMBER_KEYS: (keyof StoredPreferences & keyof Preferences)[] = [\n 'connectionTimeout',\n 'reconnectCallsTimeout',\n 'reconnectDelayMin',\n 'reconnectDelayMax',\n 'iceCandidateTimeout',\n 'iceGatheringTimeout',\n 'deviceDebounceTime',\n 'devicePollingInterval',\n 'statsPollingInterval',\n 'statsBaselineSamples',\n 'statsNoPacketThreshold',\n 'statsRttSpikeMultiplier',\n 'statsPacketLossThreshold',\n 'statsJitterSpikeMultiplier',\n 'statsHistorySize',\n 'keyframeMaxBurst',\n 'keyframeBurstWindow',\n 'keyframeCooldown',\n 'reinviteDebounceTime',\n 'reinviteMaxAttempts',\n 'reinviteTimeout',\n 'recoveryDebounceTime',\n 'recoveryCooldown',\n 'iceDisconnectedGracePeriod',\n 'iceRestartTimeout',\n 'maxRecoveryAttempts',\n 'degradationBitrateThreshold',\n 'degradationRecoveryThreshold'\n];\n\n/** Keys of StoredPreferences that map to boolean fields on PreferencesContainer. */\nconst STORED_BOOLEAN_KEYS: (keyof StoredPreferences & keyof Preferences)[] = [\n 'receiveVideo',\n 'receiveAudio',\n 'disableUdpIceServers',\n 'relayOnly',\n 'enableRelayFallback',\n 'enableNetworkChangeDetection',\n 'enableServerHangupInterception',\n 'persistDeviceSelection',\n 'syncDevicesToActiveCalls',\n 'autoMuteVideoOnHidden',\n 'refreshDevicesOnVisible',\n 'checkConnectionOnVisible',\n 'stereoAudio',\n 'enableAutoDegradation'\n];\n\n/** Collects the serializable preferences from the container. */\nfunction collectStoredPreferences(): StoredPreferences {\n const container = PreferencesContainer.instance;\n return {\n connectionTimeout: container.connectionTimeout,\n reconnectCallsTimeout: container.reconnectCallsTimeout,\n reconnectDelayMin: container.reconnectDelayMin,\n reconnectDelayMax: container.reconnectDelayMax,\n relayHost: container.relayHost,\n receiveVideo: container.receiveVideo,\n receiveAudio: container.receiveAudio,\n disableUdpIceServers: container.disableUdpIceServers,\n relayOnly: container.relayOnly,\n iceCandidateTimeout: container.iceCandidateTimeout,\n iceGatheringTimeout: container.iceGatheringTimeout,\n deviceDebounceTime: container.deviceDebounceTime,\n devicePollingInterval: container.devicePollingInterval,\n iceServers: container.iceServers,\n userVariables: container.userVariables,\n // Stats monitoring\n statsPollingInterval: container.statsPollingInterval,\n statsBaselineSamples: container.statsBaselineSamples,\n statsNoPacketThreshold: container.statsNoPacketThreshold,\n statsRttSpikeMultiplier: container.statsRttSpikeMultiplier,\n statsPacketLossThreshold: container.statsPacketLossThreshold,\n statsJitterSpikeMultiplier: container.statsJitterSpikeMultiplier,\n statsHistorySize: container.statsHistorySize,\n // Keyframe throttling\n keyframeMaxBurst: container.keyframeMaxBurst,\n keyframeBurstWindow: container.keyframeBurstWindow,\n keyframeCooldown: container.keyframeCooldown,\n // Re-INVITE / ICE restart\n reinviteDebounceTime: container.reinviteDebounceTime,\n reinviteMaxAttempts: container.reinviteMaxAttempts,\n reinviteTimeout: container.reinviteTimeout,\n // Recovery pipeline\n recoveryDebounceTime: container.recoveryDebounceTime,\n recoveryCooldown: container.recoveryCooldown,\n iceDisconnectedGracePeriod: container.iceDisconnectedGracePeriod,\n iceRestartTimeout: container.iceRestartTimeout,\n maxRecoveryAttempts: container.maxRecoveryAttempts,\n enableRelayFallback: container.enableRelayFallback,\n enableNetworkChangeDetection: container.enableNetworkChangeDetection,\n enableServerHangupInterception: container.enableServerHangupInterception,\n // Device management\n persistDeviceSelection: container.persistDeviceSelection,\n syncDevicesToActiveCalls: container.syncDevicesToActiveCalls,\n // Visibility\n autoMuteVideoOnHidden: container.autoMuteVideoOnHidden,\n refreshDevicesOnVisible: container.refreshDevicesOnVisible,\n checkConnectionOnVisible: container.checkConnectionOnVisible,\n // Media defaults\n stereoAudio: container.stereoAudio,\n // Degradation\n enableAutoDegradation: container.enableAutoDegradation,\n degradationBitrateThreshold: container.degradationBitrateThreshold,\n degradationRecoveryThreshold: container.degradationRecoveryThreshold,\n // Codec preferences\n preferredVideoCodecs: container.preferredVideoCodecs,\n preferredAudioCodecs: container.preferredAudioCodecs\n };\n}\n\n/** Applies stored preferences to the container. */\nfunction applyStoredPreferences(stored: StoredPreferences): void {\n const container = PreferencesContainer.instance as unknown as Record<string, unknown>;\n for (const key of STORED_NUMBER_KEYS) {\n if (stored[key] !== undefined) container[key] = stored[key];\n }\n for (const key of STORED_BOOLEAN_KEYS) {\n if (stored[key] !== undefined) container[key] = stored[key];\n }\n if (stored.relayHost !== undefined) container.relayHost = stored.relayHost;\n if (stored.iceServers !== undefined) container.iceServers = stored.iceServers;\n if (stored.userVariables !== undefined) container.userVariables = stored.userVariables;\n if (stored.preferredVideoCodecs !== undefined)\n container.preferredVideoCodecs = stored.preferredVideoCodecs;\n if (stored.preferredAudioCodecs !== undefined)\n container.preferredAudioCodecs = stored.preferredAudioCodecs;\n}\n\n/**\n * Public preferences API for configuring SDK behavior.\n *\n * Exposed as {@link SignalWire.preferences}. All timeout values\n * are in seconds when accessed through this class.\n *\n * When {@link enableSavePreferences} is called, preferences are\n * automatically loaded from storage and synced back on every change.\n */\nexport class ClientPreferences {\n private _storage: StorageManager | null = null;\n\n /**\n * Enables persistence of preferences to storage.\n * Loads any previously saved preferences and syncs future changes.\n */\n public enableSavePreferences(storage: StorageManager): void {\n this._storage = storage;\n this._loadFromStorage();\n }\n\n /** WebSocket connection timeout in seconds. */\n public get connectionTimeout(): number {\n return fromMsToSec(PreferencesContainer.instance.connectionTimeout);\n }\n public set connectionTimeout(seconds: number) {\n PreferencesContainer.instance.connectionTimeout = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Timeout for reconnecting to previously attached calls, in seconds. */\n public get reconnectCallsTimeout(): number {\n return fromMsToSec(PreferencesContainer.instance.reconnectCallsTimeout);\n }\n public set reconnectCallsTimeout(seconds: number) {\n PreferencesContainer.instance.reconnectCallsTimeout = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Minimum reconnection backoff delay in seconds. */\n public get reconnectDelayMin(): number {\n return fromMsToSec(PreferencesContainer.instance.reconnectDelayMin);\n }\n public set reconnectDelayMin(seconds: number) {\n PreferencesContainer.instance.reconnectDelayMin = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Maximum reconnection backoff delay in seconds. */\n public get reconnectDelayMax(): number {\n return fromMsToSec(PreferencesContainer.instance.reconnectDelayMax);\n }\n public set reconnectDelayMax(seconds: number) {\n PreferencesContainer.instance.reconnectDelayMax = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Custom relay host URL. Empty string uses the default. */\n public get relayHost(): string {\n return PreferencesContainer.instance.relayHost ?? '';\n }\n public set relayHost(value: string) {\n PreferencesContainer.instance.relayHost = value;\n this._saveToStorage();\n }\n\n /** Whether to receive remote video by default. */\n public get receiveVideo(): boolean {\n return PreferencesContainer.instance.receiveVideo;\n }\n public set receiveVideo(value: boolean) {\n PreferencesContainer.instance.receiveVideo = value;\n this._saveToStorage();\n }\n\n /** Whether to receive remote audio by default. */\n public get receiveAudio(): boolean {\n return PreferencesContainer.instance.receiveAudio;\n }\n public set receiveAudio(value: boolean) {\n PreferencesContainer.instance.receiveAudio = value;\n this._saveToStorage();\n }\n\n /** Preferred audio input device for new calls. */\n public get preferredAudioInput(): MediaDeviceInfo | null {\n return PreferencesContainer.instance.preferredAudioInput;\n }\n public set preferredAudioInput(value: MediaDeviceInfo | null) {\n PreferencesContainer.instance.preferredAudioInput = value;\n }\n\n /** Preferred audio output device for new calls. */\n public get preferredAudioOutput(): MediaDeviceInfo | null {\n return PreferencesContainer.instance.preferredAudioOutput;\n }\n public set preferredAudioOutput(value: MediaDeviceInfo | null) {\n PreferencesContainer.instance.preferredAudioOutput = value;\n }\n\n /** Preferred video input device for new calls. */\n public get preferredVideoInput(): MediaDeviceInfo | null {\n return PreferencesContainer.instance.preferredVideoInput;\n }\n public set preferredVideoInput(value: MediaDeviceInfo | null) {\n PreferencesContainer.instance.preferredVideoInput = value;\n }\n\n /** Default audio input track constraints. */\n public get inputAudioConstraints(): MediaTrackConstraints | undefined {\n return PreferencesContainer.instance.inputAudioDeviceConstraints;\n }\n public set inputAudioConstraints(value: MediaTrackConstraints | undefined) {\n PreferencesContainer.instance.inputAudioDeviceConstraints = value;\n }\n\n /** Default video input track constraints. */\n public get inputVideoConstraints(): MediaTrackConstraints | undefined {\n return PreferencesContainer.instance.inputVideoDeviceConstraints;\n }\n public set inputVideoConstraints(value: MediaTrackConstraints | undefined) {\n PreferencesContainer.instance.inputVideoDeviceConstraints = value;\n }\n\n /** Debounce time for device change events, in seconds. */\n public get deviceDebounceTime(): number {\n return fromMsToSec(PreferencesContainer.instance.deviceDebounceTime);\n }\n public set deviceDebounceTime(seconds: number) {\n PreferencesContainer.instance.deviceDebounceTime = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Polling interval for device enumeration, in seconds. */\n public get devicePollingInterval(): number {\n return fromMsToSec(PreferencesContainer.instance.devicePollingInterval);\n }\n public set devicePollingInterval(seconds: number) {\n PreferencesContainer.instance.devicePollingInterval = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Whether to filter out UDP-based ICE servers. */\n public get disableUdpIceServers(): boolean {\n return PreferencesContainer.instance.disableUdpIceServers;\n }\n public set disableUdpIceServers(value: boolean) {\n PreferencesContainer.instance.disableUdpIceServers = value;\n this._saveToStorage();\n }\n\n /** Whether to force TURN relay-only ICE candidates. */\n public get relayOnly(): boolean {\n return PreferencesContainer.instance.relayOnly;\n }\n public set relayOnly(value: boolean) {\n PreferencesContainer.instance.relayOnly = value;\n this._saveToStorage();\n }\n\n /** Timeout for individual ICE candidate gathering, in seconds. */\n public get iceCandidateTimeout(): number {\n return fromMsToSec(PreferencesContainer.instance.iceCandidateTimeout);\n }\n public set iceCandidateTimeout(seconds: number) {\n PreferencesContainer.instance.iceCandidateTimeout = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Timeout for the entire ICE gathering phase, in seconds. */\n public get iceGatheringTimeout(): number {\n return fromMsToSec(PreferencesContainer.instance.iceGatheringTimeout);\n }\n public set iceGatheringTimeout(seconds: number) {\n PreferencesContainer.instance.iceGatheringTimeout = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Custom ICE servers for TURN/STUN configuration. */\n public get iceServers(): RTCIceServer[] | undefined {\n return PreferencesContainer.instance.iceServers;\n }\n public set iceServers(value: RTCIceServer[] | undefined) {\n PreferencesContainer.instance.iceServers = value;\n this._saveToStorage();\n }\n\n /** Custom user variables attached to calls. */\n public get userVariables(): Record<string, unknown> {\n return PreferencesContainer.instance.userVariables;\n }\n public set userVariables(value: Record<string, unknown>) {\n PreferencesContainer.instance.userVariables = value;\n this._saveToStorage();\n }\n\n // ===========================================================================\n // Stats monitoring (Section 1)\n // ===========================================================================\n\n /** Stats polling interval in milliseconds. */\n public get statsPollingInterval(): number {\n return PreferencesContainer.instance.statsPollingInterval;\n }\n public set statsPollingInterval(value: number) {\n PreferencesContainer.instance.statsPollingInterval = value;\n this._saveToStorage();\n }\n\n /** Number of baseline samples for stats monitoring. */\n public get statsBaselineSamples(): number {\n return PreferencesContainer.instance.statsBaselineSamples;\n }\n public set statsBaselineSamples(value: number) {\n PreferencesContainer.instance.statsBaselineSamples = value;\n this._saveToStorage();\n }\n\n /** Duration in ms with no inbound packets before a critical issue is emitted. */\n public get statsNoPacketThreshold(): number {\n return PreferencesContainer.instance.statsNoPacketThreshold;\n }\n public set statsNoPacketThreshold(value: number) {\n PreferencesContainer.instance.statsNoPacketThreshold = value;\n this._saveToStorage();\n }\n\n /** Multiplier for RTT spike detection relative to baseline. */\n public get statsRttSpikeMultiplier(): number {\n return PreferencesContainer.instance.statsRttSpikeMultiplier;\n }\n public set statsRttSpikeMultiplier(value: number) {\n PreferencesContainer.instance.statsRttSpikeMultiplier = value;\n this._saveToStorage();\n }\n\n /** Packet loss fraction threshold (0-1) for issue detection. */\n public get statsPacketLossThreshold(): number {\n return PreferencesContainer.instance.statsPacketLossThreshold;\n }\n public set statsPacketLossThreshold(value: number) {\n PreferencesContainer.instance.statsPacketLossThreshold = value;\n this._saveToStorage();\n }\n\n /** Multiplier for jitter spike detection relative to baseline. */\n public get statsJitterSpikeMultiplier(): number {\n return PreferencesContainer.instance.statsJitterSpikeMultiplier;\n }\n public set statsJitterSpikeMultiplier(value: number) {\n PreferencesContainer.instance.statsJitterSpikeMultiplier = value;\n this._saveToStorage();\n }\n\n /** Number of seconds of metrics history to retain. */\n public get statsHistorySize(): number {\n return PreferencesContainer.instance.statsHistorySize;\n }\n public set statsHistorySize(value: number) {\n PreferencesContainer.instance.statsHistorySize = value;\n this._saveToStorage();\n }\n\n // ===========================================================================\n // Keyframe throttling (Section 2)\n // ===========================================================================\n\n /** Maximum keyframe requests in a burst window. */\n public get keyframeMaxBurst(): number {\n return PreferencesContainer.instance.keyframeMaxBurst;\n }\n public set keyframeMaxBurst(value: number) {\n PreferencesContainer.instance.keyframeMaxBurst = value;\n this._saveToStorage();\n }\n\n /** Keyframe burst window duration in milliseconds. */\n public get keyframeBurstWindow(): number {\n return PreferencesContainer.instance.keyframeBurstWindow;\n }\n public set keyframeBurstWindow(value: number) {\n PreferencesContainer.instance.keyframeBurstWindow = value;\n this._saveToStorage();\n }\n\n /** Cooldown period in ms after keyframe burst limit is reached. */\n public get keyframeCooldown(): number {\n return PreferencesContainer.instance.keyframeCooldown;\n }\n public set keyframeCooldown(value: number) {\n PreferencesContainer.instance.keyframeCooldown = value;\n this._saveToStorage();\n }\n\n // ===========================================================================\n // Re-INVITE / ICE restart (Section 2 & 19)\n // ===========================================================================\n\n /** Minimum time in ms between re-INVITE attempts. */\n public get reinviteDebounceTime(): number {\n return PreferencesContainer.instance.reinviteDebounceTime;\n }\n public set reinviteDebounceTime(value: number) {\n PreferencesContainer.instance.reinviteDebounceTime = value;\n this._saveToStorage();\n }\n\n /** Maximum re-INVITE attempts per call. */\n public get reinviteMaxAttempts(): number {\n return PreferencesContainer.instance.reinviteMaxAttempts;\n }\n public set reinviteMaxAttempts(value: number) {\n PreferencesContainer.instance.reinviteMaxAttempts = value;\n this._saveToStorage();\n }\n\n /** Timeout in ms for a single re-INVITE attempt. */\n public get reinviteTimeout(): number {\n return PreferencesContainer.instance.reinviteTimeout;\n }\n public set reinviteTimeout(value: number) {\n PreferencesContainer.instance.reinviteTimeout = value;\n this._saveToStorage();\n }\n\n // ===========================================================================\n // Recovery pipeline (Section 19)\n // ===========================================================================\n\n /** Recovery signal debounce window in seconds. */\n public get recoveryDebounceTime(): number {\n return fromMsToSec(PreferencesContainer.instance.recoveryDebounceTime);\n }\n public set recoveryDebounceTime(seconds: number) {\n PreferencesContainer.instance.recoveryDebounceTime = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Cooldown period between recovery attempts in seconds. */\n public get recoveryCooldown(): number {\n return fromMsToSec(PreferencesContainer.instance.recoveryCooldown);\n }\n public set recoveryCooldown(seconds: number) {\n PreferencesContainer.instance.recoveryCooldown = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Grace period before treating ICE 'disconnected' as failure, in seconds. */\n public get iceDisconnectedGracePeriod(): number {\n return fromMsToSec(PreferencesContainer.instance.iceDisconnectedGracePeriod);\n }\n public set iceDisconnectedGracePeriod(seconds: number) {\n PreferencesContainer.instance.iceDisconnectedGracePeriod = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Timeout for a single ICE restart attempt in seconds. */\n public get iceRestartTimeout(): number {\n return fromMsToSec(PreferencesContainer.instance.iceRestartTimeout);\n }\n public set iceRestartTimeout(seconds: number) {\n PreferencesContainer.instance.iceRestartTimeout = fromSecToMs(seconds);\n this._saveToStorage();\n }\n\n /** Maximum recovery attempts before giving up. */\n public get maxRecoveryAttempts(): number {\n return PreferencesContainer.instance.maxRecoveryAttempts;\n }\n public set maxRecoveryAttempts(value: number) {\n PreferencesContainer.instance.maxRecoveryAttempts = value;\n this._saveToStorage();\n }\n\n /** Whether relay-only escalation is enabled as a last-resort recovery tier. */\n public get enableRelayFallback(): boolean {\n return PreferencesContainer.instance.enableRelayFallback;\n }\n public set enableRelayFallback(value: boolean) {\n PreferencesContainer.instance.enableRelayFallback = value;\n this._saveToStorage();\n }\n\n /** Whether browser network change detection (online/offline) is enabled. */\n public get enableNetworkChangeDetection(): boolean {\n return PreferencesContainer.instance.enableNetworkChangeDetection;\n }\n public set enableNetworkChangeDetection(value: boolean) {\n PreferencesContainer.instance.enableNetworkChangeDetection = value;\n this._saveToStorage();\n }\n\n /** Whether server-sent media-timeout hangups are intercepted for recovery. */\n public get enableServerHangupInterception(): boolean {\n return PreferencesContainer.instance.enableServerHangupInterception;\n }\n public set enableServerHangupInterception(value: boolean) {\n PreferencesContainer.instance.enableServerHangupInterception = value;\n this._saveToStorage();\n }\n\n // ===========================================================================\n // Device management (Section 5)\n // ===========================================================================\n\n /** Whether device selections are persisted to storage. */\n public get persistDeviceSelection(): boolean {\n return PreferencesContainer.instance.persistDeviceSelection;\n }\n public set persistDeviceSelection(value: boolean) {\n PreferencesContainer.instance.persistDeviceSelection = value;\n this._saveToStorage();\n }\n\n /** Whether device changes are auto-applied to active calls. */\n public get syncDevicesToActiveCalls(): boolean {\n return PreferencesContainer.instance.syncDevicesToActiveCalls;\n }\n public set syncDevicesToActiveCalls(value: boolean) {\n PreferencesContainer.instance.syncDevicesToActiveCalls = value;\n this._saveToStorage();\n }\n\n // ===========================================================================\n // Visibility (Section 4)\n // ===========================================================================\n\n /** Whether to auto-mute video when the tab becomes hidden. */\n public get autoMuteVideoOnHidden(): boolean {\n return PreferencesContainer.instance.autoMuteVideoOnHidden;\n }\n public set autoMuteVideoOnHidden(value: boolean) {\n PreferencesContainer.instance.autoMuteVideoOnHidden = value;\n this._saveToStorage();\n }\n\n /** Whether to re-enumerate devices when the page becomes visible. */\n public get refreshDevicesOnVisible(): boolean {\n return PreferencesContainer.instance.refreshDevicesOnVisible;\n }\n public set refreshDevicesOnVisible(value: boolean) {\n PreferencesContainer.instance.refreshDevicesOnVisible = value;\n this._saveToStorage();\n }\n\n /** Whether to check peer connection health when the page becomes visible. */\n public get checkConnectionOnVisible(): boolean {\n return PreferencesContainer.instance.checkConnectionOnVisible;\n }\n public set checkConnectionOnVisible(value: boolean) {\n PreferencesContainer.instance.checkConnectionOnVisible = value;\n this._saveToStorage();\n }\n\n // ===========================================================================\n // Media defaults (Section 16.5 & 16.6)\n // ===========================================================================\n\n /** Default audio track constraints applied when no explicit constraints are provided. */\n public get defaultAudioConstraints(): MediaTrackConstraints | undefined {\n return PreferencesContainer.instance.defaultAudioConstraints;\n }\n public set defaultAudioConstraints(value: MediaTrackConstraints | undefined) {\n PreferencesContainer.instance.defaultAudioConstraints = value;\n }\n\n /** Default video track constraints applied when video is enabled without explicit constraints. */\n public get defaultVideoConstraints(): MediaTrackConstraints | undefined {\n return PreferencesContainer.instance.defaultVideoConstraints;\n }\n public set defaultVideoConstraints(value: MediaTrackConstraints | undefined) {\n PreferencesContainer.instance.defaultVideoConstraints = value;\n }\n\n /** Whether stereo Opus is enabled globally. */\n public get stereoAudio(): boolean {\n return PreferencesContainer.instance.stereoAudio;\n }\n public set stereoAudio(value: boolean) {\n PreferencesContainer.instance.stereoAudio = value;\n this._saveToStorage();\n }\n\n // ===========================================================================\n // Degradation (Section 22)\n // ===========================================================================\n\n /** Whether automatic video degradation on low bandwidth is enabled. */\n public get enableAutoDegradation(): boolean {\n return PreferencesContainer.instance.enableAutoDegradation;\n }\n public set enableAutoDegradation(value: boolean) {\n PreferencesContainer.instance.enableAutoDegradation = value;\n this._saveToStorage();\n }\n\n /** Bitrate in kbps below which video is automatically disabled. */\n public get degradationBitrateThreshold(): number {\n return PreferencesContainer.instance.degradationBitrateThreshold;\n }\n public set degradationBitrateThreshold(value: number) {\n PreferencesContainer.instance.degradationBitrateThreshold = value;\n this._saveToStorage();\n }\n\n /** Bitrate in kbps above which video is automatically re-enabled. */\n public get degradationRecoveryThreshold(): number {\n return PreferencesContainer.instance.degradationRecoveryThreshold;\n }\n public set degradationRecoveryThreshold(value: number) {\n PreferencesContainer.instance.degradationRecoveryThreshold = value;\n this._saveToStorage();\n }\n\n // ===========================================================================\n // Codec preferences (Section 23)\n // ===========================================================================\n\n /** Preferred video codecs in priority order. */\n public get preferredVideoCodecs(): string[] {\n return PreferencesContainer.instance.preferredVideoCodecs;\n }\n public set preferredVideoCodecs(value: string[]) {\n PreferencesContainer.instance.preferredVideoCodecs = value;\n this._saveToStorage();\n }\n\n /** Preferred audio codecs in priority order. */\n public get preferredAudioCodecs(): string[] {\n return PreferencesContainer.instance.preferredAudioCodecs;\n }\n public set preferredAudioCodecs(value: string[]) {\n PreferencesContainer.instance.preferredAudioCodecs = value;\n this._saveToStorage();\n }\n\n /** Saves current preferences to storage (fire-and-forget). */\n private _saveToStorage(): void {\n if (!this._storage) return;\n const data = collectStoredPreferences();\n this._storage.setItem(PREFERENCES_STORAGE_KEY, data, 'local').catch((error: unknown) => {\n logger.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);\n });\n }\n\n /** Loads preferences from storage and applies them to the container. */\n private _loadFromStorage(): void {\n if (!this._storage) return;\n this._storage\n .getItem<StoredPreferences>(PREFERENCES_STORAGE_KEY, 'local')\n .then((stored) => {\n if (stored) {\n applyStoredPreferences(stored);\n }\n })\n .catch((error: unknown) => {\n logger.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);\n });\n }\n}\n","/**\n * Normalizes an unknown caught value into a proper Error instance.\n *\n * In catch blocks, the caught value is `unknown` — it could be an Error,\n * string, number, or any other value. This utility ensures a consistent\n * Error object is produced.\n */\nexport function toError(value: unknown): Error {\n if (value instanceof Error) return value;\n return new Error(String(value));\n}\n","import { debounceTime, distinctUntilChanged, interval, map, takeUntil, tap } from 'rxjs';\n\nimport { DeviceHistoryManager } from './DeviceHistoryManager';\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { PreferencesContainer } from '../containers/PreferencesContainer';\nimport {\n DEVICE_STORAGE_KEY_AUDIO_INPUT,\n DEVICE_STORAGE_KEY_AUDIO_OUTPUT,\n DEVICE_STORAGE_KEY_VIDEO_INPUT\n} from '../core/constants';\nimport { getLogger } from '../utils/logger';\nimport { toError } from '../utils/toError';\n\nimport type { StoredDevicePreference, DeviceRecoveryEvent } from '../core/types/resilience.types';\nimport type { WebRTCApiProvider } from '../dependencies/interfaces';\nimport type { DeviceController } from '../interfaces/DeviceController';\nimport type { StorageManager } from '../managers/StorageManager';\nimport type { Observable, Subscription } from 'rxjs';\n\nconst logger = getLogger();\n\ntype DeviceKind = 'audioinput' | 'audiooutput' | 'videoinput';\n\ninterface DevicesState {\n audioinput: MediaDeviceInfo[];\n audiooutput: MediaDeviceInfo[];\n videoinput: MediaDeviceInfo[];\n}\n\ninterface SelectedDevicesState {\n audioinput: MediaDeviceInfo | null;\n audiooutput: MediaDeviceInfo | null;\n videoinput: MediaDeviceInfo | null;\n}\n\n/** Maps a device kind to its storage key. */\nconst DEVICE_STORAGE_KEYS: Record<DeviceKind, string> = {\n audioinput: DEVICE_STORAGE_KEY_AUDIO_INPUT,\n audiooutput: DEVICE_STORAGE_KEY_AUDIO_OUTPUT,\n videoinput: DEVICE_STORAGE_KEY_VIDEO_INPUT\n};\n\nconst initialDevicesState: DevicesState = {\n audioinput: [],\n audiooutput: [],\n videoinput: []\n};\n\nconst initialSelectedDevicesState: SelectedDevicesState = {\n audioinput: null,\n audiooutput: null,\n videoinput: null\n};\n\nexport class NavigatorDeviceController extends Destroyable implements DeviceController {\n private deviceChangeHandler = () => {\n logger.debug('[DeviceController] Device change detected');\n void this.enumerateDevices();\n };\n\n private _devicesPoolingSubscription?: Subscription;\n private _devicesState$ = this.createBehaviorSubject<DevicesState>(initialDevicesState);\n private _selectedDevicesState$ = this.createBehaviorSubject<SelectedDevicesState>(\n initialSelectedDevicesState\n );\n\n // Error stream\n private _errors$ = this.createReplaySubject<Error>(1);\n\n // Device history for succession chain (Section 5.2)\n private _deviceHistory = new DeviceHistoryManager();\n private _deviceRecovered$ = this.createSubject<DeviceRecoveryEvent>();\n\n // Section 5.9: Intentional device disable\n private _audioInputDisabled$ = this.createBehaviorSubject<boolean>(false);\n private _videoInputDisabled$ = this.createBehaviorSubject<boolean>(false);\n\n // Section 5.9: Last selection before disable, for restore on enable\n private _lastAudioInputBeforeDisable: MediaDeviceInfo | null = null;\n private _lastVideoInputBeforeDisable: MediaDeviceInfo | null = null;\n\n // Section 5.1: Loaded persisted device preferences (populated on init)\n private _persistedDevices: Partial<Record<DeviceKind, StoredDevicePreference>> = {};\n\n // Storage manager (optional) for device persistence (Section 5.1)\n private _storageManager?: StorageManager;\n\n constructor(\n private readonly webRTCApiProvider: WebRTCApiProvider,\n storageManager?: StorageManager\n ) {\n super();\n this._storageManager = storageManager;\n this.init();\n }\n\n // ---- Section 5.1: Storage Manager access ----\n\n /** Sets the storage manager for device persistence. */\n public setStorageManager(storageManager: StorageManager): void {\n this._storageManager = storageManager;\n // Reload persisted devices when storage becomes available\n void this.loadPersistedDevices();\n }\n\n // ---- Constraints ----\n\n public get selectedAudioInputDeviceConstraints(): MediaTrackConstraints | boolean {\n if (this._audioInputDisabled$.value) {\n return false;\n }\n return this.deviceInfoToConstraints(this.selectedAudioInputDevice);\n }\n\n public get selectedVideoInputDeviceConstraints(): MediaTrackConstraints | boolean {\n if (this._videoInputDisabled$.value) {\n return false;\n }\n return this.deviceInfoToConstraints(this.selectedVideoInputDevice);\n }\n\n public deviceInfoToConstraints(deviceInfo: MediaDeviceInfo | null): MediaTrackConstraints {\n if (!deviceInfo?.deviceId || deviceInfo.deviceId.trim() === '') {\n return {};\n }\n const devices =\n deviceInfo.kind === 'audioinput' ? this.audioInputDevices : this.videoInputDevices;\n const device =\n devices.find((device) => device.deviceId === deviceInfo.deviceId) ??\n devices.find((device) => device.label === deviceInfo.label);\n if (device) {\n return { deviceId: { exact: device.deviceId } };\n }\n return {};\n }\n\n public get errors$(): Observable<Error> {\n return this.cachedObservable('errors$', () =>\n this._errors$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n /** Observable that emits when the SDK auto-switches a device. */\n public get deviceRecovered$(): Observable<DeviceRecoveryEvent> {\n return this._deviceRecovered$.asObservable().pipe(takeUntil(this.destroyed$));\n }\n\n // ---- Section 5.9: Disable/enable observables ----\n\n public get videoInputDisabled$(): Observable<boolean> {\n return this.cachedObservable('videoInputDisabled$', () =>\n this._videoInputDisabled$\n .asObservable()\n .pipe(distinctUntilChanged(), takeUntil(this.destroyed$))\n );\n }\n\n public get audioInputDisabled$(): Observable<boolean> {\n return this.cachedObservable('audioInputDisabled$', () =>\n this._audioInputDisabled$\n .asObservable()\n .pipe(distinctUntilChanged(), takeUntil(this.destroyed$))\n );\n }\n\n public get videoInputDisabled(): boolean {\n return this._videoInputDisabled$.value;\n }\n\n public get audioInputDisabled(): boolean {\n return this._audioInputDisabled$.value;\n }\n\n // ---- Observable getters for device lists by kind ----\n\n public get audioInputDevices$(): Observable<MediaDeviceInfo[]> {\n return this.cachedObservable('audioInputDevices$', () =>\n this._devicesState$.pipe(\n map((state) => state.audioinput),\n distinctUntilChanged(),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n public get audioOutputDevices$(): Observable<MediaDeviceInfo[]> {\n return this.cachedObservable('audioOutputDevices$', () =>\n this._devicesState$.pipe(\n map((state) => state.audiooutput),\n distinctUntilChanged(),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n public get videoInputDevices$(): Observable<MediaDeviceInfo[]> {\n return this.cachedObservable('videoInputDevices$', () =>\n this._devicesState$.pipe(\n map((state) => state.videoinput),\n distinctUntilChanged(),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n public get selectedAudioInputDevice$(): Observable<MediaDeviceInfo | null> {\n return this.cachedObservable('selectedAudioInputDevice$', () =>\n this._selectedDevicesState$.asObservable().pipe(\n map((state) => state.audioinput),\n distinctUntilChanged(),\n takeUntil(this.destroyed$),\n tap((info) => logger.debug('[DeviceController] Selected audio input device changed:', info))\n )\n );\n }\n\n public get selectedAudioOutputDevice$(): Observable<MediaDeviceInfo | null> {\n return this.cachedObservable('selectedAudioOutputDevice$', () =>\n this._selectedDevicesState$.asObservable().pipe(\n map((state) => state.audiooutput),\n distinctUntilChanged(),\n takeUntil(this.destroyed$),\n tap((info) =>\n logger.debug('[DeviceController] Selected audio output device changed:', info)\n )\n )\n );\n }\n\n public get selectedVideoInputDevice$(): Observable<MediaDeviceInfo | null> {\n return this.cachedObservable('selectedVideoInputDevice$', () =>\n this._selectedDevicesState$.asObservable().pipe(\n map((state) => state.videoinput),\n distinctUntilChanged(),\n takeUntil(this.destroyed$),\n tap((info) => logger.debug('[DeviceController] Selected video input device changed:', info))\n )\n );\n }\n\n // Current value getters for selected devices\n public get selectedAudioInputDevice(): MediaDeviceInfo | null {\n if (this._audioInputDisabled$.value) {\n return null;\n }\n return this._selectedDevicesState$.value.audioinput;\n }\n\n public get selectedAudioOutputDevice(): MediaDeviceInfo | null {\n return this._selectedDevicesState$.value.audiooutput;\n }\n\n public get selectedVideoInputDevice(): MediaDeviceInfo | null {\n if (this._videoInputDisabled$.value) {\n return null;\n }\n return this._selectedDevicesState$.value.videoinput;\n }\n\n public get audioInputDevices(): MediaDeviceInfo[] {\n return this._devicesState$.value.audioinput;\n }\n\n public get audioOutputDevices(): MediaDeviceInfo[] {\n return this._devicesState$.value.audiooutput;\n }\n\n public get videoInputDevices(): MediaDeviceInfo[] {\n return this._devicesState$.value.videoinput;\n }\n\n // ---- Section 5.9: Disable/enable methods ----\n\n public disableAudioInput(): void {\n if (!this._audioInputDisabled$.value) {\n this._lastAudioInputBeforeDisable = this._selectedDevicesState$.value.audioinput;\n this._audioInputDisabled$.next(true);\n // Emit null on selected device when disabled\n this._selectedDevicesState$.next({\n ...this._selectedDevicesState$.value,\n audioinput: null\n });\n }\n }\n\n public enableAudioInput(): void {\n if (this._audioInputDisabled$.value) {\n this._audioInputDisabled$.next(false);\n // Restore last selection or auto-select\n const restored = this._lastAudioInputBeforeDisable ?? this.audioInputDevices[0];\n this._selectedDevicesState$.next({\n ...this._selectedDevicesState$.value,\n audioinput: restored\n });\n this._lastAudioInputBeforeDisable = null;\n }\n }\n\n public disableVideoInput(): void {\n if (!this._videoInputDisabled$.value) {\n this._lastVideoInputBeforeDisable = this._selectedDevicesState$.value.videoinput;\n this._videoInputDisabled$.next(true);\n this._selectedDevicesState$.next({\n ...this._selectedDevicesState$.value,\n videoinput: null\n });\n }\n }\n\n public enableVideoInput(): void {\n if (this._videoInputDisabled$.value) {\n this._videoInputDisabled$.next(false);\n const restored = this._lastVideoInputBeforeDisable ?? this.videoInputDevices[0];\n this._selectedDevicesState$.next({\n ...this._selectedDevicesState$.value,\n videoinput: restored\n });\n this._lastVideoInputBeforeDisable = null;\n }\n }\n\n // ---- Setters for selected devices ----\n\n public selectAudioInputDevice(device: MediaDeviceInfo | null): void {\n // If user selects while disabled, re-enable\n if (this._audioInputDisabled$.value && device) {\n this._audioInputDisabled$.next(false);\n }\n const previous = this._selectedDevicesState$.value.audioinput;\n if (previous && previous.deviceId !== device?.deviceId) {\n this._deviceHistory.push('audioinput', previous);\n }\n this._selectedDevicesState$.next({\n ...this._selectedDevicesState$.value,\n audioinput: device\n });\n if (device) {\n void this.persistDeviceSelection('audioinput', device);\n }\n }\n\n public selectVideoInputDevice(device: MediaDeviceInfo | null): void {\n logger.debug('[DeviceController] Setting selected video input device:', device);\n if (this._videoInputDisabled$.value && device) {\n this._videoInputDisabled$.next(false);\n }\n const previous = this._selectedDevicesState$.value.videoinput;\n if (previous && previous.deviceId !== device?.deviceId) {\n this._deviceHistory.push('videoinput', previous);\n }\n this._selectedDevicesState$.next({\n ...this._selectedDevicesState$.value,\n videoinput: device\n });\n if (device) {\n void this.persistDeviceSelection('videoinput', device);\n }\n }\n\n public selectAudioOutputDevice(device: MediaDeviceInfo | null): void {\n const previous = this._selectedDevicesState$.value.audiooutput;\n if (previous && previous.deviceId !== device?.deviceId) {\n this._deviceHistory.push('audiooutput', previous);\n }\n this._selectedDevicesState$.next({\n ...this._selectedDevicesState$.value,\n audiooutput: device\n });\n if (device) {\n void this.persistDeviceSelection('audiooutput', device);\n }\n }\n\n private init(): void {\n // Load persisted device preferences (Section 5.1)\n void this.loadPersistedDevices();\n\n // Subscribe to device state changes and auto-select devices\n this.subscribeTo(\n this._devicesState$.pipe(debounceTime(PreferencesContainer.instance.deviceDebounceTime)),\n (devicesState) => {\n // Skip auto-resolution if syncDevicesToActiveCalls is disabled (Section 5.5)\n const currentSelected = this._selectedDevicesState$.value;\n\n const newAudioInput = this._audioInputDisabled$.value\n ? null\n : this.resolveDevice(\n 'audioinput',\n devicesState.audioinput,\n currentSelected.audioinput,\n PreferencesContainer.instance.preferredAudioInput\n );\n\n const newAudioOutput = this.resolveDevice(\n 'audiooutput',\n devicesState.audiooutput,\n currentSelected.audiooutput,\n PreferencesContainer.instance.preferredAudioOutput\n );\n\n const newVideoInput = this._videoInputDisabled$.value\n ? null\n : this.resolveDevice(\n 'videoinput',\n devicesState.videoinput,\n currentSelected.videoinput,\n PreferencesContainer.instance.preferredVideoInput\n );\n\n // Only update if something changed\n if (\n newAudioInput !== currentSelected.audioinput ||\n newAudioOutput !== currentSelected.audiooutput ||\n newVideoInput !== currentSelected.videoinput\n ) {\n // Section 5.5: Gate auto-switch on syncDevicesToActiveCalls preference.\n // When disabled, only update if there is no current selection (first init)\n // or the device was explicitly null already.\n const shouldSync = PreferencesContainer.instance.syncDevicesToActiveCalls;\n this._selectedDevicesState$.next({\n audioinput:\n shouldSync || !currentSelected.audioinput\n ? newAudioInput\n : currentSelected.audioinput,\n audiooutput:\n shouldSync || !currentSelected.audiooutput\n ? newAudioOutput\n : currentSelected.audiooutput,\n videoinput:\n shouldSync || !currentSelected.videoinput ? newVideoInput : currentSelected.videoinput\n });\n }\n }\n );\n\n void this.enumerateDevices();\n }\n\n /**\n * Resolves the best device for a given kind, using device history\n * for succession when the selected device disappears.\n * Section 5.1 adds persisted device resolution.\n * Section 5.4 adds duplicate-named device handling.\n */\n private resolveDevice(\n kind: DeviceKind,\n devices: MediaDeviceInfo[],\n selected: MediaDeviceInfo | null,\n preferred: MediaDeviceInfo | null\n ): MediaDeviceInfo | null {\n // If no devices, return null\n if (devices.length === 0) {\n return null;\n }\n\n // If selected device is still available, keep it\n if (selected) {\n const exactMatch = devices.find((device) => device.deviceId === selected.deviceId);\n if (exactMatch) {\n return selected;\n }\n\n // Section 5.4: Label match with duplicate handling\n const labelMatches = devices.filter((device) => device.label === selected.label);\n if (labelMatches.length === 1) {\n return labelMatches[0];\n }\n if (labelMatches.length > 1) {\n // Try groupId match among the label matches\n const groupMatch = labelMatches.find((device) => device.groupId === selected.groupId);\n if (groupMatch) {\n return groupMatch;\n }\n // Ambiguous - emit event and don't auto-switch\n this.emitDeviceRecovered(kind, selected, null, 'ambiguous_match');\n return null;\n }\n\n // Selected device disappeared - try device history succession\n const fromHistory = this._deviceHistory.findInHistory(kind, devices);\n if (fromHistory) {\n logger.debug(\n `[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`\n );\n this.emitDeviceRecovered(kind, selected, fromHistory, 'device_disconnected');\n return fromHistory;\n }\n\n // No history match - fall back to preferred or first available\n const replacement = preferred\n ? devices.find(\n (device) => device.deviceId === preferred.deviceId || device.label === preferred.label\n )\n : undefined;\n const newDevice = replacement ?? devices[0];\n this.emitDeviceRecovered(kind, selected, newDevice, 'fallback_to_default');\n return newDevice;\n }\n\n // No selected device - try persisted device (Section 5.1)\n const persisted = this._persistedDevices[kind];\n if (persisted) {\n const fromPersisted = this.resolvePersistedDevice(kind, persisted, devices);\n if (fromPersisted) {\n return fromPersisted;\n }\n }\n\n // No selected device - use preferred or first available\n if (preferred) {\n const preferredDevice = devices.find(\n (device) => device.deviceId === preferred.deviceId || device.label === preferred.label\n );\n if (preferredDevice) {\n return preferredDevice;\n }\n }\n return devices[0];\n }\n\n /**\n * Section 5.1: Resolves a stored device preference against the current device list.\n * Priority: exact deviceId > groupId+label > label (single match only)\n */\n private resolvePersistedDevice(\n _kind: DeviceKind,\n stored: StoredDevicePreference,\n devices: MediaDeviceInfo[]\n ): MediaDeviceInfo | null {\n // 1. Exact deviceId match\n const exactMatch = devices.find((d) => d.deviceId === stored.deviceId);\n if (exactMatch) {\n return exactMatch;\n }\n\n // 2. groupId + label match\n const groupLabelMatch = devices.find(\n (d) => d.groupId === stored.groupId && d.label === stored.label\n );\n if (groupLabelMatch) {\n return groupLabelMatch;\n }\n\n // 3. label match (single match only to avoid ambiguity)\n const labelMatches = devices.filter((d) => d.label === stored.label);\n if (labelMatches.length === 1) {\n return labelMatches[0];\n }\n\n return null;\n }\n\n private emitDeviceRecovered(\n kind: DeviceKind,\n previousDevice: MediaDeviceInfo | null,\n newDevice: MediaDeviceInfo | null,\n reason: DeviceRecoveryEvent['reason']\n ): void {\n try {\n this._deviceRecovered$.next({\n kind,\n previousDevice,\n newDevice,\n reason\n });\n } catch {\n // Non-fatal: observer might already be completed\n }\n }\n\n // ---- Section 5.1: Device persistence ----\n\n private async persistDeviceSelection(kind: DeviceKind, device: MediaDeviceInfo): Promise<void> {\n if (!this._storageManager || !PreferencesContainer.instance.persistDeviceSelection) {\n return;\n }\n const stored: StoredDevicePreference = {\n deviceId: device.deviceId,\n label: device.label,\n kind: device.kind,\n groupId: device.groupId\n };\n try {\n await this._storageManager.setItem(DEVICE_STORAGE_KEYS[kind], stored, 'local');\n } catch (error) {\n logger.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);\n }\n }\n\n private async loadPersistedDevices(): Promise<void> {\n if (!this._storageManager || !PreferencesContainer.instance.persistDeviceSelection) {\n return;\n }\n const kinds: DeviceKind[] = ['audioinput', 'audiooutput', 'videoinput'];\n for (const kind of kinds) {\n try {\n const stored = await this._storageManager.getItem<StoredDevicePreference>(\n DEVICE_STORAGE_KEYS[kind],\n 'local'\n );\n if (stored) {\n this._persistedDevices = { ...this._persistedDevices, [kind]: stored };\n }\n } catch (error) {\n logger.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);\n }\n }\n }\n\n // ---- Section 5.11: Clear device state for factory reset ----\n\n /** Clears device history, persisted selections, and re-enumerates devices. */\n public async clearDeviceState(): Promise<void> {\n this._deviceHistory.clear();\n this._persistedDevices = {};\n this._lastAudioInputBeforeDisable = null;\n this._lastVideoInputBeforeDisable = null;\n this._audioInputDisabled$.next(false);\n this._videoInputDisabled$.next(false);\n this._selectedDevicesState$.next(initialSelectedDevicesState);\n await this.enumerateDevices();\n }\n\n public enableDeviceMonitoring(): void {\n this.disableDeviceMonitoring();\n this.webRTCApiProvider.mediaDevices.addEventListener('devicechange', this.deviceChangeHandler);\n\n if (PreferencesContainer.instance.devicePollingInterval > 0) {\n this._devicesPoolingSubscription = interval(\n PreferencesContainer.instance.devicePollingInterval\n ).subscribe(() => {\n logger.debug('[DeviceController] Polling devices due to interval');\n void this.enumerateDevices();\n });\n }\n\n void this.enumerateDevices();\n }\n\n public disableDeviceMonitoring(): void {\n this.webRTCApiProvider.mediaDevices.removeEventListener(\n 'devicechange',\n this.deviceChangeHandler\n );\n if (this._devicesPoolingSubscription) {\n this._devicesPoolingSubscription.unsubscribe();\n this._devicesPoolingSubscription = undefined;\n }\n }\n\n public async enumerateDevices(): Promise<void> {\n try {\n const devices = await this.webRTCApiProvider.mediaDevices.enumerateDevices();\n\n const devicesByKind: DevicesState = devices.reduce(\n (acc, device) => {\n const kind = device.kind as keyof DevicesState;\n return {\n ...acc,\n [kind]: [...acc[kind], device]\n };\n },\n {\n audioinput: [],\n audiooutput: [],\n videoinput: []\n } as DevicesState\n );\n\n // Update state in a single emission\n this._devicesState$.next(devicesByKind);\n\n logger.debug('[DeviceController] Devices enumerated:', {\n audioInputs: devicesByKind.audioinput.length,\n audioOutputs: devicesByKind.audiooutput.length,\n videoInputs: devicesByKind.videoinput.length\n });\n } catch (error) {\n logger.error('[DeviceController] Failed to enumerate devices:', error);\n this._errors$.next(toError(error));\n }\n }\n\n public async getDeviceCapabilities(\n deviceInfo: MediaDeviceInfo\n ): Promise<MediaTrackCapabilities | null> {\n if (deviceInfo.kind === 'audiooutput') {\n return null;\n }\n\n try {\n const constraints = this.deviceInfoToConstraints(deviceInfo);\n const stream = await this.webRTCApiProvider.mediaDevices.getUserMedia({\n audio: deviceInfo.kind === 'audioinput' ? constraints : false,\n video: deviceInfo.kind === 'videoinput' ? constraints : false\n });\n\n const track =\n deviceInfo.kind === 'audioinput' ? stream.getAudioTracks()[0] : stream.getVideoTracks()[0];\n\n const capabilities = track.getCapabilities();\n\n // Stop all tracks to release devices\n stream.getTracks().forEach((t) => t.stop());\n\n return capabilities;\n } catch (error) {\n logger.error('[DeviceController] Failed to get device capabilities:', error);\n this._errors$.next(toError(error));\n throw error;\n }\n }\n\n public async isValidDevice(deviceInfo: MediaDeviceInfo | null): Promise<boolean> {\n if (!deviceInfo || deviceInfo.kind === 'audiooutput') {\n return false;\n }\n try {\n const capabilities = await this.getDeviceCapabilities(deviceInfo);\n return capabilities !== null;\n } catch {\n return false;\n }\n }\n\n public destroy(): void {\n this.disableDeviceMonitoring();\n super.destroy();\n }\n}\n","import { StorageNotAvailableError } from '../core/errors';\nimport { getLogger } from '../utils/logger';\n\nimport type { Storage, StorageScope } from './interfaces';\n\n/** Default storage implementation backed by browser `localStorage` and `sessionStorage`. */\nexport class DefaultLocalStorage implements Storage {\n constructor() {\n // Check if localStorage is available\n if (typeof localStorage === 'undefined') {\n throw new StorageNotAvailableError('localStorage');\n }\n if (typeof sessionStorage === 'undefined') {\n throw new StorageNotAvailableError('sessionStorage');\n }\n\n // Test if localStorage is actually accessible (some browsers block it)\n try {\n const testKey = '__storage_test__';\n localStorage.setItem(testKey, 'test');\n localStorage.removeItem(testKey);\n } catch (error) {\n getLogger().error('LocalStorage is not accessible:', error);\n throw new StorageNotAvailableError('localStorage');\n }\n }\n\n private storage(scope: StorageScope) {\n return scope === 'local' ? localStorage : sessionStorage;\n }\n\n async setItem(key: string, value: string, scope: StorageScope = 'session'): Promise<void> {\n this.storage(scope).setItem(key, value);\n return Promise.resolve();\n }\n\n async getItem(key: string, scope: StorageScope = 'session'): Promise<string | null> {\n return Promise.resolve(this.storage(scope).getItem(key));\n }\n\n async removeItem(key: string, scope: StorageScope = 'session'): Promise<void> {\n this.storage(scope).removeItem(key);\n return Promise.resolve();\n }\n\n async clear(scope: StorageScope = 'session'): Promise<void> {\n const store = this.storage(scope);\n const keysToRemove: string[] = [];\n for (let i = 0; i < store.length; i++) {\n const key = store.key(i);\n if (key?.startsWith('sw:')) {\n keysToRemove.push(key);\n }\n }\n for (const key of keysToRemove) {\n store.removeItem(key);\n }\n return Promise.resolve();\n }\n}\n","import {\n SerializationError,\n StorageWriteError,\n DeserializationError,\n StorageReadError\n} from '../core/errors';\nimport { DefaultLocalStorage } from '../dependencies/DefaultLocalStorage';\n\nimport type { Storage, StorageScope } from '../dependencies/interfaces';\n\nexport class StorageManager {\n constructor(private storageImpl: Storage = new DefaultLocalStorage()) {}\n\n /**\n * Validates that a value can be safely serialized to JSON\n * @throws SerializationError if value contains non-serializable types\n */\n private serialize(value: unknown, key?: string): string | null {\n if (value === undefined || value === null) {\n // undefined is acceptable, will be stored as null\n return null;\n }\n\n try {\n return JSON.stringify(value);\n } catch (e) {\n throw new SerializationError(key ?? 'unknown', e as Error);\n }\n }\n\n /**\n * Stores a value in storage\n * @throws InvalidStorageValueError if value contains non-serializable types\n * @throws SerializationError if JSON serialization fails\n * @throws StorageWriteError if writing to storage fails\n */\n public async setItem(\n key: string,\n value: unknown,\n scope: StorageScope = 'session'\n ): Promise<void> {\n const serialized = this.serialize(value, key);\n\n try {\n await this.storageImpl.setItem(key, serialized, scope);\n } catch (error) {\n throw new StorageWriteError(key, error as Error);\n }\n }\n\n /**\n * Retrieves a value from storage\n *\n * This method distinguishes between:\n * - Storage errors (network, permission, etc.) - these are thrown\n * - JSON parse errors - these trigger onParseError and return raw string\n * - Missing keys - returns null\n *\n * @returns The parsed value, raw string (on parse error), or null\n * @throws StorageReadError\n */\n public async getItem<T = unknown>(\n key: string,\n scope: StorageScope = 'session'\n ): Promise<T | null> {\n let item: string | null;\n\n try {\n item = await this.storageImpl.getItem(key, scope);\n } catch (error) {\n throw new StorageReadError(key, error as Error);\n }\n\n if (!item) {\n return null;\n }\n\n try {\n return JSON.parse(item) as T;\n } catch (error) {\n throw new DeserializationError(key, error as Error);\n }\n }\n\n /**\n * Removes a value from storage\n * @throws Error from underlying storage implementation\n */\n public async removeItem(key: string, scope: StorageScope = 'session'): Promise<void> {\n try {\n await this.storageImpl.removeItem(key, scope);\n } catch (error) {\n throw new StorageWriteError(key, error as Error);\n }\n }\n\n /**\n * Clears all SDK keys from both 'local' and 'session' scopes.\n * @throws StorageWriteError if clearing fails\n */\n public async clearAll(): Promise<void> {\n try {\n await this.storageImpl.clear('local');\n await this.storageImpl.clear('session');\n } catch (error) {\n throw new StorageWriteError('clearAll', error as Error);\n }\n }\n}\n","import { HTTPRequestController } from '../controllers/HTTPRequestController';\nimport { NavigatorDeviceController } from '../controllers/NavigatorDeviceController';\nimport { DependencyError } from '../core/errors';\nimport { DefaultLocalStorage } from '../dependencies/DefaultLocalStorage';\nimport { StorageManager } from '../managers/StorageManager';\n\nimport type { Subscriber } from '../core/entities/Subscriber';\nimport type {\n NodeSocketAdapter,\n SDKCredential,\n WebSocketAdapter\n} from '../core/types/common.types';\nimport type { Storage, WebRTCApiProvider } from '../dependencies/interfaces';\nimport type { ConversationsProvider } from '../interfaces/Conversations';\nimport type { Dependency } from '../interfaces/Dependency';\nimport type { DeviceController } from '../interfaces/DeviceController';\n\nexport class DependencyContainer implements Dependency {\n /**\n * When true, credential + auth state + protocol are stored in localStorage\n * (survives page reload). When false (default), sessionStorage is used.\n */\n public persistSession = false;\n private _conversationManager?: ConversationsProvider;\n\n private _subscriber?: Subscriber;\n\n private _host?: string;\n\n private _domain?: string;\n\n private _storageManager?: StorageManager;\n private _storageImpl?: Storage;\n private _webSocketConstructor?: WebSocketAdapter | NodeSocketAdapter =\n typeof WebSocket !== 'undefined' ? WebSocket : undefined;\n private _baseURL: string = this.apiHost;\n private _credential: SDKCredential = {};\n private _httpRequestController?: HTTPRequestController;\n private _deviceController?: NavigatorDeviceController;\n private _webRTCApiProvider?: WebRTCApiProvider;\n\n public get subscriberId(): string {\n return this.subscriber.id;\n }\n\n public get subscriber(): Subscriber {\n if (!this._subscriber) {\n throw new DependencyError('Subscriber');\n }\n return this._subscriber;\n }\n public set subscriber(subscriber: Subscriber) {\n this._subscriber = subscriber;\n }\n\n public get storage(): StorageManager {\n if (!this._storageManager) {\n // Lazily initialize storage implementation if not already set\n this._storageImpl ??= new DefaultLocalStorage();\n this._storageManager = new StorageManager(this._storageImpl);\n }\n return this._storageManager;\n }\n\n public get http(): HTTPRequestController {\n this._httpRequestController ??= new HTTPRequestController(\n this._baseURL,\n () => this._credential\n );\n return this._httpRequestController;\n }\n\n public get conversationManager(): ConversationsProvider {\n if (!this._conversationManager) {\n throw new DependencyError('ConversationsManager');\n }\n return this._conversationManager;\n }\n\n public set conversationManager(conversationManager: ConversationsProvider) {\n this._conversationManager = conversationManager;\n }\n\n public get WebSocket(): WebSocketAdapter | NodeSocketAdapter {\n if (!this._webSocketConstructor) {\n throw new DependencyError('WebSocket constructor');\n }\n return this._webSocketConstructor;\n }\n\n public set WebSocket(WebSocketConstructor: WebSocketAdapter | NodeSocketAdapter) {\n this._webSocketConstructor = WebSocketConstructor;\n }\n\n public get deviceController(): DeviceController {\n this._deviceController ??= new NavigatorDeviceController(this.webRTCApiProvider, this.storage);\n return this._deviceController;\n }\n\n public get webRTCApiProvider(): WebRTCApiProvider {\n if (!this._webRTCApiProvider) {\n if (typeof RTCPeerConnection === 'undefined' || typeof navigator === 'undefined') {\n throw new DependencyError(\n 'WebRTCApiProvider: RTCPeerConnection or navigator.mediaDevices is not available. ' +\n 'Please provide a custom webRTCApiProvider in SignalWireOptions.'\n );\n }\n this._webRTCApiProvider = {\n RTCPeerConnection,\n mediaDevices: navigator.mediaDevices\n };\n }\n return this._webRTCApiProvider;\n }\n\n public set webRTCApiProvider(webRTCApiProvider: WebRTCApiProvider) {\n this._webRTCApiProvider = webRTCApiProvider;\n // Reset device controller so it picks up the new provider\n this._deviceController = undefined;\n }\n\n public get authorizationStateKey(): string {\n return `sw:${this.subscriberId}:as`;\n }\n\n public get protocolKey(): string {\n return `sw:${this.subscriberId}:pt`;\n }\n\n public get attachedCallsKey(): string {\n return `sw:${this.subscriberId}:att`;\n }\n\n public getSubscriberFromAddressId(): string {\n return this.subscriber.addresses[0]?.id ?? '';\n }\n\n public set baseURL(baseURL: string) {\n this._baseURL = baseURL;\n this._httpRequestController = undefined;\n }\n\n public get credential(): SDKCredential {\n return this._credential;\n }\n\n public set credential(credential: SDKCredential) {\n this._credential = credential;\n }\n\n public set storageImpl(storageImpl: Storage) {\n this._storageImpl = storageImpl;\n this._storageManager = undefined;\n }\n\n public set ch(ch: string | undefined) {\n if (!ch) {\n return;\n }\n\n const firstDot = ch.indexOf('.');\n if (firstDot !== -1) {\n this._host = ch.substring(0, firstDot);\n this._domain = ch.substring(firstDot + 1);\n }\n\n // Update baseURL and reset HTTP controller so it picks up the new host\n this._baseURL = this.apiHost;\n this._httpRequestController = undefined;\n }\n\n public get relayHost(): string {\n return `wss://${this._host ?? 'puc'}.${this._domain ?? 'signalwire.com'}`;\n }\n\n public get apiHost(): string {\n return `https://fabric.${this._domain ?? 'signalwire.com'}`;\n }\n}\n","import { DPoPInitError } from '../core/errors';\nimport { getLogger } from '../utils/logger';\n\nimport type { DPoPHttpProofParams, DPoPRpcProofParams } from '../core/types/crypto.types';\n\nconst logger = getLogger();\n\nconst DPOP_DB_NAME = 'sw-dpop';\nconst DPOP_DB_VERSION = 1;\nconst DPOP_STORE_NAME = 'keys';\nconst DPOP_KEY_ID = 'dpop-keypair';\n\n/**\n * Base64url-encodes an ArrayBuffer (no padding, URL-safe).\n */\nconst base64url = (buffer: ArrayBuffer): string => {\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n};\n\n/**\n * Base64url-encodes a UTF-8 string.\n */\nconst base64urlEncode = (str: string): string => {\n const encoded = new TextEncoder().encode(str);\n return base64url(encoded.buffer);\n};\n\n/**\n * Computes the JWK Thumbprint per RFC 7638.\n *\n * Only supports RSA keys — the canonical JSON uses lexicographic member ordering: { e, kty, n }.\n * The thumbprint is SHA-256 of the canonical JSON, base64url-encoded.\n *\n * @throws {Error} If the JWK is not an RSA key.\n */\nconst computeJwkThumbprint = async (jwk: JsonWebKey): Promise<string> => {\n if (jwk.kty !== 'RSA') {\n throw new Error(`Unsupported key type for JWK Thumbprint: ${jwk.kty}. Only RSA is supported.`);\n }\n const canonical = JSON.stringify({ e: jwk.e, kty: jwk.kty, n: jwk.n });\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(canonical));\n return base64url(digest);\n};\n\n// ============================================================\n// IndexedDB helpers for persisting non-extractable CryptoKeys\n// ============================================================\n\nasync function openDpopDB(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(DPOP_DB_NAME, DPOP_DB_VERSION);\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(DPOP_STORE_NAME)) {\n db.createObjectStore(DPOP_STORE_NAME);\n }\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new Error('Failed to open IndexedDB'));\n });\n}\n\nasync function loadKeyPairFromDB(): Promise<CryptoKeyPair | null> {\n try {\n const db = await openDpopDB();\n return await new Promise((resolve, reject) => {\n const tx = db.transaction(DPOP_STORE_NAME, 'readonly');\n const req = tx.objectStore(DPOP_STORE_NAME).get(DPOP_KEY_ID);\n req.onsuccess = () => resolve((req.result as CryptoKeyPair | undefined) ?? null);\n req.onerror = () => reject(req.error ?? new Error('Failed to load key pair from IndexedDB'));\n tx.oncomplete = () => db.close();\n });\n } catch (error) {\n logger.warn('[DPoP] Failed to load key pair from IndexedDB:', error);\n return null;\n }\n}\n\nasync function saveKeyPairToDB(keyPair: CryptoKeyPair): Promise<void> {\n try {\n const db = await openDpopDB();\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(DPOP_STORE_NAME, 'readwrite');\n tx.objectStore(DPOP_STORE_NAME).put(keyPair, DPOP_KEY_ID);\n tx.oncomplete = () => {\n db.close();\n resolve();\n };\n tx.onerror = () => {\n db.close();\n reject(tx.error ?? new Error('Failed to save key pair to IndexedDB'));\n };\n });\n } catch (error) {\n logger.warn('[DPoP] Failed to save key pair to IndexedDB:', error);\n }\n}\n\nasync function deleteKeyPairFromDB(): Promise<void> {\n try {\n const db = await openDpopDB();\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(DPOP_STORE_NAME, 'readwrite');\n tx.objectStore(DPOP_STORE_NAME).delete(DPOP_KEY_ID);\n tx.oncomplete = () => {\n db.close();\n resolve();\n };\n tx.onerror = () => {\n db.close();\n reject(tx.error ?? new Error('Failed to delete key pair from IndexedDB'));\n };\n });\n } catch (error) {\n logger.warn('[DPoP] Failed to delete key pair from IndexedDB:', error);\n }\n}\n\n/**\n * Controls DPoP (Demonstrating Proof-of-Possession) cryptographic operations.\n *\n * Generates an RSA-2048 key pair where the private key is non-extractable,\n * computes the JWK Thumbprint (RFC 7638) as the fingerprint, and creates\n * signed DPoP proof JWTs for both HTTP API requests and WebSocket RPC calls.\n *\n * The key pair is persisted in IndexedDB so the same fingerprint survives\n * page reloads. This keeps the Client Bound SAT and stored authorization_state\n * valid across reloads without needing to re-authenticate.\n *\n * @example\n * ```typescript\n * const crypto = new CryptoController();\n * await crypto.init();\n *\n * // Get fingerprint for SAT issuance\n * const fingerprint = crypto.fingerprint;\n *\n * // Create proof for HTTP endpoint\n * const httpProof = await crypto.createHttpProof({\n * method: 'POST',\n * uri: '/api/fabric/subscriber/devices/token'\n * });\n *\n * // Create proof for RPC call\n * const rpcProof = await crypto.createRpcProof({\n * method: 'signalwire.connect'\n * });\n * ```\n */\nexport class CryptoController {\n private _keyPair: CryptoKeyPair | null = null;\n private _publicJwk: JsonWebKey | null = null;\n private _fingerprint: string | null = null;\n private _initialized = false;\n\n /**\n * Initializes the DPoP key pair. Loads an existing key from IndexedDB\n * if available, otherwise generates a new one and persists it.\n *\n * The private key is non-extractable — IndexedDB stores the CryptoKey\n * handle via the structured clone algorithm without exposing key material.\n *\n * @returns The JWK Thumbprint (fingerprint) for the key.\n */\n public async init(): Promise<string> {\n if (this._initialized) {\n return this.fingerprint;\n }\n\n // Try to restore key pair from IndexedDB (survives page reload)\n const stored = await loadKeyPairFromDB();\n if (stored) {\n try {\n // Verify the stored key is still usable (sign a test payload)\n const testData = new TextEncoder().encode('dpop-key-check');\n await crypto.subtle.sign('RSASSA-PKCS1-v1_5', stored.privateKey, testData);\n\n this._keyPair = stored;\n this._publicJwk = await crypto.subtle.exportKey('jwk', stored.publicKey);\n this._fingerprint = await computeJwkThumbprint(this._publicJwk);\n this._initialized = true;\n\n logger.debug('[DPoP] Key pair restored from IndexedDB, fingerprint:', this._fingerprint);\n return this._fingerprint;\n } catch (error) {\n logger.warn('[DPoP] Stored key pair unusable, generating new one:', error);\n await deleteKeyPairFromDB();\n }\n }\n\n logger.debug('[DPoP] Generating RSA key pair');\n\n this._keyPair = await crypto.subtle.generateKey(\n {\n name: 'RSASSA-PKCS1-v1_5',\n modulusLength: 2048,\n publicExponent: new Uint8Array([1, 0, 1]),\n hash: 'SHA-256'\n },\n false, // Private key is NOT extractable\n ['sign', 'verify']\n );\n\n this._publicJwk = await crypto.subtle.exportKey('jwk', this._keyPair.publicKey);\n this._fingerprint = await computeJwkThumbprint(this._publicJwk);\n this._initialized = true;\n\n // Persist to IndexedDB so the same key survives page reload\n await saveKeyPairToDB(this._keyPair);\n\n logger.debug('[DPoP] Key pair generated and persisted, fingerprint:', this._fingerprint);\n return this._fingerprint;\n }\n\n /**\n * The JWK Thumbprint (RFC 7638) of the public key.\n * Used as the `fingerprint` parameter when requesting scoped SATs.\n *\n * @throws {DPoPInitError} If {@link init} has not been called.\n */\n public get fingerprint(): string {\n if (!this._fingerprint) {\n throw new DPoPInitError('CryptoController not initialized. Call init() first.');\n }\n return this._fingerprint;\n }\n\n /**\n * Whether the controller has been initialized with a key pair.\n */\n public get initialized(): boolean {\n return this._initialized;\n }\n\n /**\n * Creates a DPoP proof JWT for an HTTP API request.\n *\n * Used for Prime API endpoints like `/api/fabric/subscriber/devices/token`\n * and `/api/fabric/subscriber/devices/refresh`.\n *\n * @param params - HTTP method and URI for the proof.\n * @returns Signed DPoP proof JWT string.\n */\n public async createHttpProof(params: DPoPHttpProofParams): Promise<string> {\n const payload: Record<string, unknown> = {\n jti: crypto.randomUUID(),\n htm: params.method,\n htu: params.uri,\n iat: Math.floor(Date.now() / 1000)\n };\n\n // RFC 9449 Section 4.2: include ath (access token hash) for resource endpoints\n if (params.accessToken) {\n const digest = await crypto.subtle.digest(\n 'SHA-256',\n new TextEncoder().encode(params.accessToken)\n );\n payload.ath = base64url(digest);\n }\n\n return this.signProof(payload);\n }\n\n /**\n * Creates a DPoP proof JWT for a WebSocket RPC call.\n *\n * Used for switchblade RPC methods like `signalwire.connect` and\n * `signalwire.reauthenticate`.\n *\n * @param params - RPC method name for the proof.\n * @returns Signed DPoP proof JWT string.\n */\n public async createRpcProof(params: DPoPRpcProofParams): Promise<string> {\n const payload = {\n jti: crypto.randomUUID(),\n rpc: 'request' as const,\n mth: params.method,\n iat: Math.floor(Date.now() / 1000)\n };\n\n return this.signProof(payload);\n }\n\n /**\n * Releases the key pair references and removes the persisted key from IndexedDB.\n * After calling destroy, the controller must be re-initialized to be used again.\n */\n public destroy(): void {\n this._keyPair = null;\n this._publicJwk = null;\n this._fingerprint = null;\n this._initialized = false;\n // Fire-and-forget — IndexedDB cleanup is best-effort\n void deleteKeyPairFromDB();\n logger.debug('[DPoP] Controller destroyed');\n }\n\n private get publicJwk(): JsonWebKey {\n if (!this._publicJwk) {\n throw new DPoPInitError('CryptoController not initialized. Call init() first.');\n }\n return this._publicJwk;\n }\n\n private get privateKey(): CryptoKey {\n if (!this._keyPair) {\n throw new DPoPInitError('CryptoController not initialized. Call init() first.');\n }\n return this._keyPair.privateKey;\n }\n\n private async signProof(payload: Record<string, unknown>): Promise<string> {\n const header = {\n typ: 'dpop+jwt' as const,\n alg: 'RS256' as const,\n jwk: this.publicJwk\n };\n\n const headerB64 = base64urlEncode(JSON.stringify(header));\n const payloadB64 = base64urlEncode(JSON.stringify(payload));\n const signingInput = `${headerB64}.${payloadB64}`;\n\n const signature = await crypto.subtle.sign(\n 'RSASSA-PKCS1-v1_5',\n this.privateKey,\n new TextEncoder().encode(signingInput)\n );\n\n return `${signingInput}.${base64url(signature)}`;\n }\n}\n","import { takeUntil } from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { getLogger } from '../utils/logger';\n\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface NetworkChangeEvent {\n type: 'online' | 'offline' | 'connection_change';\n timestamp: number;\n networkType?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Safely check whether we are running in a browser environment\n * with `window` and the relevant event targets.\n */\nfunction hasBrowserNetworkEvents(): boolean {\n return typeof window !== 'undefined' && typeof window.addEventListener === 'function';\n}\n\n/**\n * Attempt to read the current effective type from the Network Information API.\n * Returns `undefined` when the API is not available.\n */\nfunction getNetworkType(): string | undefined {\n if (typeof navigator === 'undefined') {\n return undefined;\n }\n // Network Information API is not universally typed\n const { connection } = navigator as NavigatorWithConnection;\n return connection?.effectiveType ?? undefined;\n}\n\n/**\n * Returns the `NetworkInformation` object when available, or `undefined`.\n */\nfunction getNetworkConnection(): NetworkInformation | undefined {\n if (typeof navigator === 'undefined') {\n return undefined;\n }\n return (navigator as NavigatorWithConnection).connection ?? undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Navigator extension for Network Information API\n// ---------------------------------------------------------------------------\n\ninterface NetworkInformation extends EventTarget {\n readonly effectiveType?: string;\n readonly type?: string;\n}\n\ninterface NavigatorWithConnection extends Navigator {\n readonly connection?: NetworkInformation;\n}\n\n// ---------------------------------------------------------------------------\n// NetworkMonitor\n// ---------------------------------------------------------------------------\n\n/**\n * Monitors browser-level network connectivity events (online / offline)\n * and, where supported, the Network Information API for connection type changes.\n *\n * Safe for non-browser environments — all event listeners are guarded by\n * feature checks and no-op gracefully when `window` is unavailable.\n */\nexport class NetworkMonitor extends Destroyable {\n // --- Observables (private subjects) ---\n private readonly _isOnline$ = this.createBehaviorSubject<boolean>(\n typeof navigator !== 'undefined' ? navigator.onLine : true\n );\n\n private readonly _networkChange$ = this.createSubject<NetworkChangeEvent>();\n\n // --- Bound listeners (for removal on destroy) ---\n private readonly _onOnline = this.handleOnline.bind(this);\n private readonly _onOffline = this.handleOffline.bind(this);\n private readonly _onConnectionChange = this.handleConnectionChange.bind(this);\n\n // Track whether listeners are attached\n private _listenersAttached = false;\n\n constructor() {\n super();\n this.attachListeners();\n }\n\n // --- Public observable getters ---\n\n public get isOnline$(): Observable<boolean> {\n return this._isOnline$.asObservable().pipe(takeUntil(this._destroyed$));\n }\n\n public get isOnline(): boolean {\n return this._isOnline$.value;\n }\n\n public get networkChange$(): Observable<NetworkChangeEvent> {\n return this._networkChange$.asObservable().pipe(takeUntil(this._destroyed$));\n }\n\n // --- Lifecycle ---\n\n public override destroy(): void {\n this.removeListeners();\n super.destroy();\n }\n\n // --- Private helpers ---\n\n private attachListeners(): void {\n if (!hasBrowserNetworkEvents()) {\n logger.debug('NetworkMonitor: no browser environment detected, skipping event listeners');\n return;\n }\n\n window.addEventListener('online', this._onOnline);\n window.addEventListener('offline', this._onOffline);\n\n const connection = getNetworkConnection();\n if (connection) {\n connection.addEventListener('change', this._onConnectionChange);\n }\n\n this._listenersAttached = true;\n logger.debug('NetworkMonitor: event listeners attached');\n }\n\n private removeListeners(): void {\n if (!this._listenersAttached) {\n return;\n }\n\n if (hasBrowserNetworkEvents()) {\n window.removeEventListener('online', this._onOnline);\n window.removeEventListener('offline', this._onOffline);\n\n const connection = getNetworkConnection();\n if (connection) {\n connection.removeEventListener('change', this._onConnectionChange);\n }\n }\n\n this._listenersAttached = false;\n logger.debug('NetworkMonitor: event listeners removed');\n }\n\n private handleOnline(): void {\n logger.info('NetworkMonitor: browser went online');\n this._isOnline$.next(true);\n this._networkChange$.next({\n type: 'online',\n timestamp: Date.now(),\n networkType: getNetworkType()\n });\n }\n\n private handleOffline(): void {\n logger.info('NetworkMonitor: browser went offline');\n this._isOnline$.next(false);\n this._networkChange$.next({\n type: 'offline',\n timestamp: Date.now()\n });\n }\n\n private handleConnectionChange(): void {\n const networkType = getNetworkType();\n logger.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? 'unknown'}`);\n this._networkChange$.next({\n type: 'connection_change',\n timestamp: Date.now(),\n networkType\n });\n }\n}\n","/**\n * PlatformCapabilities - Detects browser/platform WebRTC capabilities.\n *\n * Probes the runtime environment at construction time and exposes a\n * read-only {@link PlatformCapabilities} object. Works with the\n * {@link WebRTCApiProvider} pattern so custom WebRTC implementations\n * (Citrix HDX, React Native, etc.) are supported.\n *\n * @example\n * ```typescript\n * const caps = detectPlatformCapabilities(webRTCApiProvider);\n * if (!caps.screenShare) hideScreenShareButton();\n * ```\n */\n\nimport type { PlatformCapabilities } from '../core/types/resilience.types';\nimport type { WebRTCApiProvider } from '../dependencies/interfaces';\n\n/**\n * Detects the list of supported codecs for a given media kind\n * by calling RTCRtpSender.getCapabilities when available.\n *\n * @param kind - 'audio' or 'video'\n * @returns Array of unique codec names (e.g., ['VP8', 'VP9', 'H264'])\n */\nfunction detectCodecs(kind: 'audio' | 'video'): string[] {\n if (typeof RTCRtpSender === 'undefined' || typeof RTCRtpSender.getCapabilities !== 'function') {\n return [];\n }\n\n const capabilities = RTCRtpSender.getCapabilities(kind);\n if (!capabilities) {\n return [];\n }\n\n const seen = new Set<string>();\n const codecs: string[] = [];\n for (const codec of capabilities.codecs) {\n const name = extractCodecName(codec.mimeType);\n if (name && !seen.has(name)) {\n seen.add(name);\n codecs.push(name);\n }\n }\n return codecs;\n}\n\n/**\n * Extracts the codec name from a MIME type string (e.g., 'video/VP8' -> 'VP8').\n */\nfunction extractCodecName(mimeType: string): string {\n const slashIndex = mimeType.indexOf('/');\n return slashIndex >= 0 ? mimeType.substring(slashIndex + 1) : mimeType;\n}\n\n/**\n * Checks whether insertable streams / encoded transforms are available.\n *\n * Tests for both the newer RTCRtpScriptTransform (Safari 15.4+, Chrome 128+)\n * and the older MediaStreamTrackProcessor (Chrome 94+).\n */\nfunction detectInsertableStreams(): boolean {\n if (typeof globalThis === 'undefined') {\n return false;\n }\n return 'RTCRtpScriptTransform' in globalThis || 'MediaStreamTrackProcessor' in globalThis;\n}\n\n/**\n * Checks whether setSinkId (audio output device selection) is available.\n */\nfunction detectAudioOutputSelection(): boolean {\n if (typeof HTMLMediaElement === 'undefined') {\n return false;\n }\n return 'setSinkId' in HTMLMediaElement.prototype;\n}\n\n/**\n * Checks whether simulcast is supported by checking for RTCRtpSender.setParameters\n * and the presence of encoding parameters support.\n */\nfunction detectSimulcast(): boolean {\n if (typeof RTCRtpSender === 'undefined') {\n return false;\n }\n return typeof RTCRtpSender.prototype.setParameters === 'function';\n}\n\n/**\n * Detects platform WebRTC capabilities at call time.\n *\n * If a {@link WebRTCApiProvider} is supplied, its mediaDevices are used\n * for `getUserMedia` / `getDisplayMedia` detection. Otherwise the\n * standard `navigator.mediaDevices` is probed.\n *\n * @param provider - Optional custom WebRTC API provider\n * @returns A frozen {@link PlatformCapabilities} object\n */\nexport function detectPlatformCapabilities(provider?: WebRTCApiProvider): PlatformCapabilities {\n const mediaDevices = provider?.mediaDevices ?? getNavigatorMediaDevices();\n\n const hasGetUserMedia = typeof mediaDevices?.getUserMedia === 'function';\n const hasGetDisplayMedia = typeof mediaDevices?.getDisplayMedia === 'function';\n\n const hasWebRTC = provider\n ? typeof provider.RTCPeerConnection === 'function'\n : typeof globalThis !== 'undefined' && 'RTCPeerConnection' in globalThis;\n\n const capabilities: PlatformCapabilities = {\n webrtc: hasWebRTC,\n getUserMedia: hasGetUserMedia,\n getDisplayMedia: hasGetDisplayMedia,\n screenShare: hasGetDisplayMedia,\n screenShareAudio: detectScreenShareAudio(),\n simulcast: detectSimulcast(),\n insertableStreams: detectInsertableStreams(),\n audioOutputSelection: detectAudioOutputSelection(),\n videoCodecs: detectCodecs('video'),\n audioCodecs: detectCodecs('audio')\n };\n\n return Object.freeze(capabilities);\n}\n\n/**\n * Detects whether screen share with system audio is supported.\n *\n * This is primarily a Chromium feature. We detect it by checking\n * whether getDisplayMedia exists and the browser is Chromium-based.\n */\nfunction detectScreenShareAudio(): boolean {\n if (typeof navigator === 'undefined') {\n return false;\n }\n const mediaDevices = getNavigatorMediaDevices();\n if (!mediaDevices || typeof mediaDevices.getDisplayMedia !== 'function') {\n return false;\n }\n\n // All Chromium-based browsers support system audio in screen share.\n // Modern Chromium Edge uses \"Edg/\" (not \"Edge/\"), and old EdgeHTML Edge\n // (which used \"Edge/\") is effectively dead. Chrome/ is sufficient.\n const ua = navigator.userAgent;\n return ua.includes('Chrome/');\n}\n\n/**\n * Safely retrieves navigator.mediaDevices, returning null if unavailable.\n */\nfunction getNavigatorMediaDevices(): MediaDevices | null {\n if (typeof navigator !== 'undefined') {\n return navigator.mediaDevices;\n }\n return null;\n}\n","import { filter, firstValueFrom, take, timeout } from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { PreflightError } from '../core/errors';\nimport { getLogger } from '../utils/logger';\n\nimport type { Call } from '../core/entities/types/call.types';\nimport type { PreflightOptions, PreflightResult } from '../core/types/resilience.types';\nimport type { DeviceController } from '../interfaces/DeviceController';\n\nconst logger = getLogger();\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_MEDIA_TEST_DURATION_S = 10;\nconst ICE_GATHERING_TIMEOUT_MS = 10_000;\nconst SIGNALING_RTT_TIMEOUT_MS = 5_000;\nconst DEVICE_TEST_TIMEOUT_MS = 5_000;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\ntype DialFn = (destination: string, options?: Record<string, unknown>) => Promise<Call>;\n\ninterface IceTestResult {\n type: 'direct' | 'relay' | 'failed';\n turnReachable: boolean;\n stunReachable: boolean;\n rttMs: number;\n}\n\n// ---------------------------------------------------------------------------\n// PreflightRunner\n// ---------------------------------------------------------------------------\n\n/**\n * Runs a multi-phase connectivity test:\n * 1. Signaling -- verify WebSocket is connected, measure RTT\n * 2. Devices -- verify getUserMedia works with selected devices\n * 3. ICE/TURN -- gather ICE candidates and check reachability\n * 4. Media/bandwidth -- dial, collect getStats(), compute bandwidth\n *\n * Extends Destroyable so all resources are cleaned up when done.\n */\nexport class PreflightRunner extends Destroyable {\n private readonly _options: Required<Pick<PreflightOptions, 'duration' | 'skipMediaTest'>> &\n PreflightOptions;\n\n constructor(\n private readonly deviceController: DeviceController,\n private readonly iceServers: RTCIceServer[],\n private readonly isConnected: boolean,\n private readonly transportRttMs: number,\n private readonly dialFn: DialFn,\n options: PreflightOptions = {}\n ) {\n super();\n this._options = {\n duration: options.duration ?? DEFAULT_MEDIA_TEST_DURATION_S,\n skipMediaTest: options.skipMediaTest ?? false,\n audioDevice: options.audioDevice,\n videoDevice: options.videoDevice\n };\n }\n\n /**\n * Execute the full preflight test and return the result.\n * Always cleans up resources on completion or error.\n */\n public async run(destination: string): Promise<PreflightResult> {\n const warnings: string[] = [];\n\n try {\n // Phase 1: Signaling test\n const signaling = this.testSignaling();\n if (!signaling.reachable) {\n warnings.push('WebSocket not connected');\n }\n\n // Phase 2: Device test\n const devices = await this.testDevices();\n if (!devices.audioInput.working) {\n warnings.push('Audio input device not working');\n }\n if (!devices.videoInput.working) {\n warnings.push('Video input device not working');\n }\n\n // Phase 3: ICE/TURN test\n const connectivity = await this.testIceConnectivity();\n if (connectivity.type === 'failed') {\n warnings.push('No ICE connectivity (neither STUN nor TURN reachable)');\n } else if (connectivity.type === 'relay') {\n warnings.push('Only TURN relay connectivity available (no direct path)');\n }\n if (!connectivity.turnReachable) {\n warnings.push('TURN servers not reachable');\n }\n\n // Phase 4: Media/bandwidth test (optional)\n let bandwidth: PreflightResult['bandwidth'] = null;\n if (!this._options.skipMediaTest) {\n try {\n bandwidth = await this.testMediaBandwidth(destination);\n } catch (error) {\n logger.warn('[PreflightRunner] Media bandwidth test failed:', error);\n warnings.push('Media bandwidth test failed');\n }\n }\n\n const ok =\n signaling.reachable && connectivity.type !== 'failed' && devices.audioInput.working;\n\n return {\n ok,\n signaling,\n connectivity,\n bandwidth,\n devices,\n warnings\n };\n } catch (error) {\n logger.error('[PreflightRunner] Preflight test failed:', error);\n throw new PreflightError(\n 'preflight',\n error instanceof Error ? error : new Error(String(error))\n );\n } finally {\n this.destroy();\n }\n }\n\n // -------------------------------------------------------------------------\n // Phase 1: Signaling test\n // -------------------------------------------------------------------------\n\n private testSignaling(): PreflightResult['signaling'] {\n return {\n reachable: this.isConnected,\n rttMs: this.transportRttMs\n };\n }\n\n // -------------------------------------------------------------------------\n // Phase 2: Device test\n // -------------------------------------------------------------------------\n\n private async testDevices(): Promise<PreflightResult['devices']> {\n const audioDevice = this._options.audioDevice ?? this.deviceController.selectedAudioInputDevice;\n const videoDevice = this._options.videoDevice ?? this.deviceController.selectedVideoInputDevice;\n const audioOutputDevice = this.deviceController.selectedAudioOutputDevice;\n\n let audioWorking = false;\n let videoWorking = false;\n let audioStream: MediaStream | undefined;\n\n try {\n const constraints: MediaStreamConstraints = {};\n if (audioDevice) {\n constraints.audio = { deviceId: { exact: audioDevice.deviceId } };\n } else {\n constraints.audio = true;\n }\n if (videoDevice) {\n constraints.video = { deviceId: { exact: videoDevice.deviceId } };\n } else {\n constraints.video = true;\n }\n\n audioStream = await Promise.race([\n navigator.mediaDevices.getUserMedia(constraints),\n new Promise<MediaStream>((_, reject) =>\n setTimeout(() => reject(new Error('getUserMedia timeout')), DEVICE_TEST_TIMEOUT_MS)\n )\n ]);\n\n for (const track of audioStream.getTracks()) {\n if (track.kind === 'audio' && track.readyState === 'live') {\n audioWorking = true;\n }\n if (track.kind === 'video' && track.readyState === 'live') {\n videoWorking = true;\n }\n }\n } catch (error) {\n logger.warn('[PreflightRunner] Device test failed:', error);\n } finally {\n // Release tracks\n if (audioStream) {\n audioStream.getTracks().forEach((t) => t.stop());\n }\n }\n\n return {\n audioInput: { working: audioWorking, device: audioDevice },\n videoInput: { working: videoWorking, device: videoDevice },\n audioOutput: { available: !!audioOutputDevice, device: audioOutputDevice }\n };\n }\n\n // -------------------------------------------------------------------------\n // Phase 3: ICE/TURN connectivity test\n // -------------------------------------------------------------------------\n\n private async testIceConnectivity(): Promise<IceTestResult> {\n let pc: RTCPeerConnection | undefined;\n try {\n pc = new RTCPeerConnection({ iceServers: this.iceServers });\n const peerConnection = pc;\n\n const candidateTypes = new Set<string>();\n const startTime = Date.now();\n\n const gatheringComplete = new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, ICE_GATHERING_TIMEOUT_MS);\n\n peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n const candidateStr = event.candidate.candidate;\n if (candidateStr.includes('typ host')) candidateTypes.add('host');\n if (candidateStr.includes('typ srflx')) candidateTypes.add('srflx');\n if (candidateStr.includes('typ relay')) candidateTypes.add('relay');\n } else {\n clearTimeout(timer);\n resolve();\n }\n };\n });\n\n // Create a dummy data channel to trigger ICE gathering\n pc.createDataChannel('preflight-test');\n const offer = await pc.createOffer();\n await pc.setLocalDescription(offer);\n\n await gatheringComplete;\n const rttMs = Date.now() - startTime;\n\n const stunReachable = candidateTypes.has('srflx');\n const turnReachable = candidateTypes.has('relay');\n const hasHost = candidateTypes.has('host');\n\n let type: IceTestResult['type'] = 'failed';\n if (hasHost || stunReachable) {\n type = 'direct';\n } else if (turnReachable) {\n type = 'relay';\n }\n\n return { type, turnReachable, stunReachable, rttMs };\n } catch (error) {\n logger.warn('[PreflightRunner] ICE connectivity test failed:', error);\n return { type: 'failed', turnReachable: false, stunReachable: false, rttMs: 0 };\n } finally {\n if (pc) {\n pc.close();\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Phase 4: Media/bandwidth test\n // -------------------------------------------------------------------------\n\n private async testMediaBandwidth(\n destination: string\n ): Promise<{ uploadKbps: number; downloadKbps: number }> {\n let call: Call | undefined;\n try {\n call = await this.dialFn(destination, {\n audio: true,\n video: false\n });\n\n // Wait for call to connect\n await firstValueFrom(\n call.status$.pipe(\n filter((s) => s === 'connected'),\n take(1),\n timeout(SIGNALING_RTT_TIMEOUT_MS)\n )\n );\n\n // Collect stats for the test duration\n const durationMs = this._options.duration * 1000;\n await new Promise((resolve) => setTimeout(resolve, durationMs));\n\n // Read final metrics\n const metrics = call.networkMetrics;\n let uploadKbps = 0;\n let downloadKbps = 0;\n\n if (metrics.length > 0) {\n const latest = metrics[metrics.length - 1];\n if (latest.availableOutgoingBitrate !== undefined) {\n uploadKbps = Math.round(latest.availableOutgoingBitrate / 1000);\n }\n // Estimate download from total packets received (rough approximation)\n const totalPackets = latest.audio.packetsReceived + latest.video.packetsReceived;\n if (durationMs > 0 && totalPackets > 0) {\n // Use upload as rough proxy for download when per-track bytes unavailable\n downloadKbps = uploadKbps;\n }\n }\n\n return { uploadKbps, downloadKbps };\n } finally {\n // Always hang up the test call\n if (call) {\n try {\n await call.hangup();\n } catch {\n // Best-effort cleanup\n }\n }\n }\n }\n\n public override destroy(): void {\n super.destroy();\n }\n}\n","import { takeUntil } from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { getLogger } from '../utils/logger';\n\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\n/**\n * Visibility state of the document tab.\n */\nexport type VisibilityState = 'visible' | 'hidden';\n\n/**\n * Event emitted when the document visibility changes.\n */\nexport interface VisibilityChangeEvent {\n readonly from: VisibilityState;\n readonly to: VisibilityState;\n readonly timestamp: number;\n}\n\n/**\n * Checks whether the document visibility API is available.\n */\nfunction isVisibilityApiAvailable(): boolean {\n try {\n return (\n typeof document !== 'undefined' &&\n typeof document.addEventListener === 'function' &&\n typeof document.visibilityState === 'string'\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Returns the current document visibility state, defaulting to 'visible'\n * in non-browser environments.\n */\nfunction getCurrentVisibility(): VisibilityState {\n try {\n if (typeof document !== 'undefined' && typeof document.visibilityState === 'string') {\n return document.visibilityState === 'visible' ? 'visible' : 'hidden';\n }\n } catch {\n // Non-browser environment\n }\n return 'visible';\n}\n\n/**\n * VisibilityController tracks browser tab visibility state and exposes\n * it as RxJS observables. Safe for use in non-browser environments\n * where the document API is not available.\n *\n * Extends Destroyable for lifecycle management and automatic cleanup.\n */\nexport class VisibilityController extends Destroyable {\n private readonly _visibility$ =\n this.createBehaviorSubject<VisibilityState>(getCurrentVisibility());\n\n private readonly _visibilityChange$ = this.createSubject<VisibilityChangeEvent>();\n\n private readonly _boundHandler: () => void;\n private readonly _hasVisibilityApi: boolean;\n\n constructor() {\n super();\n\n this._hasVisibilityApi = isVisibilityApiAvailable();\n this._boundHandler = this._handleVisibilityChange.bind(this);\n\n if (this._hasVisibilityApi) {\n document.addEventListener('visibilitychange', this._boundHandler);\n logger.debug('VisibilityController: listening for visibilitychange events');\n } else {\n logger.debug(\n 'VisibilityController: document visibility API not available, defaulting to visible'\n );\n }\n }\n\n /**\n * Observable of the current visibility state.\n * Emits 'visible' or 'hidden'. Always starts with the current state.\n */\n public get visibility$(): Observable<VisibilityState> {\n return this._visibility$.pipe(takeUntil(this._destroyed$));\n }\n\n /**\n * The current visibility state value.\n */\n public get visibility(): VisibilityState {\n return this._visibility$.value;\n }\n\n /**\n * Observable that emits transition events when visibility changes.\n * Each event includes the previous state, new state, and timestamp.\n */\n public get visibilityChange$(): Observable<VisibilityChangeEvent> {\n return this._visibilityChange$.pipe(takeUntil(this._destroyed$));\n }\n\n public override destroy(): void {\n if (this._hasVisibilityApi) {\n document.removeEventListener('visibilitychange', this._boundHandler);\n logger.debug('VisibilityController: removed visibilitychange listener');\n }\n super.destroy();\n }\n /**\n * Handle the browser's visibilitychange event.\n */\n private _handleVisibilityChange(): void {\n const newState = getCurrentVisibility();\n const previousState = this._visibility$.value;\n\n if (newState === previousState) {\n return;\n }\n\n this._visibility$.next(newState);\n\n const changeEvent: VisibilityChangeEvent = {\n from: previousState,\n to: newState,\n timestamp: Date.now()\n };\n\n this._visibilityChange$.next(changeEvent);\n\n logger.debug('VisibilityController: visibility changed', {\n from: previousState,\n to: newState\n });\n }\n}\n","import { defer, from, shareReplay, takeUntil } from 'rxjs';\n\nimport { Destroyable } from './Destroyable';\n\nimport type { HTTPRequestController } from '../controllers/HTTPRequestController';\nimport type { Observable } from 'rxjs';\n\nexport abstract class Fetchable<T = unknown> extends Destroyable {\n public fetched$: Observable<boolean>;\n\n constructor(\n public fromPath: string,\n protected http: HTTPRequestController\n ) {\n super();\n this.fetched$ = defer(() => from(this.fetch())).pipe(\n shareReplay(1),\n takeUntil(this.destroyed$)\n );\n }\n\n protected abstract populateInstance(data: T): void;\n\n private async fetch(): Promise<boolean> {\n const response = await this.http.request({\n url: this.fromPath,\n method: 'GET',\n headers: {\n Accept: 'application/json'\n }\n });\n if (response.ok && response.body) {\n const data = JSON.parse(response.body) as T;\n this.populateInstance(data);\n return true;\n }\n return false;\n }\n}\n","import { Fetchable } from '../../behaviors/Fetchable';\n\nimport type { HTTPRequestController } from '../../controllers/HTTPRequestController';\nimport type { GetAddressResponse } from '../types/address.types';\nimport type { SATClaims } from '../types/crypto.types';\nimport type { GetSubscriberInfoResponse } from '../types/subscriber.types';\n\n/** Subscriber online presence state. */\nexport type SubscriberPresence = 'online' | 'offline' | 'busy';\n\n/**\n * Authenticated subscriber profile.\n *\n * Fetched automatically when a {@link SignalWire} connects.\n * Contains identity, contact, and organization details.\n */\nexport class Subscriber extends Fetchable<GetSubscriberInfoResponse> {\n /** Unique subscriber identifier. */\n public id!: string;\n /** Subscriber email address. */\n public email!: string;\n /** First name. */\n public firstName?: string;\n /** Last name. */\n public lastName?: string;\n /** Display name shown to other participants. */\n public displayName?: string;\n /** Job title. */\n public jobTitle?: string;\n /** Time zone offset. */\n public timeZone?: number;\n /** Country code. */\n public country?: string;\n /** Region/state. */\n public region?: string;\n /** Company name. */\n public companyName?: string;\n /** Push notification key for mobile/web push. */\n public pushNotificationKey!: string;\n /** Application-level settings (display name, permission scopes). */\n public appSettings?: {\n displayName: string;\n scopes: string[];\n };\n /** Fabric addresses associated with this subscriber. */\n public addresses!: GetAddressResponse[];\n /** Filtered SAT claims when the token has special capabilities (e.g., refresh scope). */\n public satClaims?: SATClaims;\n\n constructor(http: HTTPRequestController) {\n super('/api/fabric/subscriber/info', http);\n }\n\n protected populateInstance(data: GetSubscriberInfoResponse): void {\n this.id = data.id;\n this.email = data.email;\n this.firstName = data.first_name;\n this.lastName = data.last_name;\n this.displayName = data.display_name;\n this.jobTitle = data.job_title;\n this.timeZone = data.time_zone;\n this.country = data.country;\n this.region = data.region;\n this.companyName = data.company_name;\n this.pushNotificationKey = data.push_notification_key;\n this.appSettings = data.app_settings\n ? {\n displayName: data.app_settings.display_name,\n scopes: data.app_settings.scopes\n }\n : undefined;\n this.addresses = data.fabric_addresses;\n this.satClaims = data.sat_claims;\n }\n}\n","import { v4 as uuid } from 'uuid';\n\nimport type { JSONRPCRequest, JSONRPCSuccessResponse } from './types/base';\n\ninterface MakeRPCRequestParams<\n T extends string = 'execute',\n P extends object = Record<string, unknown>\n> {\n id?: string;\n method: T;\n params: P;\n}\nexport const buildRPCRequest = <\n T extends string = 'execute',\n P extends object = Record<string, unknown>\n>(\n params: MakeRPCRequestParams<T, P>\n): JSONRPCRequest<P> & { method: T; params: P } => {\n return {\n jsonrpc: '2.0' as const,\n id: params.id ?? uuid(),\n ...params\n };\n};\n\ninterface MakeRPCResponseParams<TResult = unknown> {\n id: string;\n result: TResult;\n}\nexport const makeRPCResponse = <TResult = unknown>(\n params: MakeRPCResponseParams<TResult>\n): JSONRPCSuccessResponse<TResult> => {\n return {\n jsonrpc: '2.0' as const,\n ...params\n };\n};\n","import { buildRPCRequest } from './helpers';\n\nimport type { JSONRPCRequest } from './types/base';\n\ninterface WithToken {\n token: string;\n jwt_token?: never;\n}\ninterface WithJWT {\n token?: never;\n jwt_token: string;\n}\nexport type RPCConnectAuthentication = { project?: string } & (WithToken | WithJWT);\n\n/**\n * Parameters for `signalwire.connect`.\n *\n * **Fresh connect**: `authentication: { jwt_token }` + `dpop_token` (when client-bound).\n * **Reconnect**: `authentication: { jwt_token: \"<stored SAT, even if expired>\" }` +\n * `authorization_state` + `protocol` + `dpop_token`.\n * The jwt_token is required for session classification even when expired;\n * authorization_state short-circuits actual token validation.\n */\nexport interface RPCConnectParams {\n authentication: RPCConnectAuthentication;\n version?: typeof DEFAULT_CONNECT_VERSION;\n agent?: string;\n /** DPoP proof JWT for Client Bound SAT verification. */\n dpop_token?: string;\n /** Encrypted state from a prior session — reconnection optimization. */\n authorization_state?: string;\n /** Required when authorization_state is provided. */\n protocol?: string;\n contexts?: string[];\n topics?: string[];\n eventing?: string[];\n event_acks?: boolean;\n}\n\nexport interface Authorization {\n jti: string;\n project_id: string;\n data_zone?: string;\n scope?: string[];\n fabric_subscriber: {\n version: number;\n expires_at: number;\n subscriber_id: string;\n application_id: string | null;\n project_id: string;\n space_id: string;\n };\n cnf?: { jkt: string };\n}\n\nexport interface RPCConnectResult {\n identity: string;\n authorization: Authorization;\n protocol: string;\n\n ice_servers?: RTCIceServer[];\n}\n\nexport const DEFAULT_CONNECT_VERSION = {\n major: 4,\n minor: 0,\n revision: 0\n};\n\nexport const RPCConnect = (params: RPCConnectParams): JSONRPCRequest => {\n return buildRPCRequest({\n method: 'signalwire.connect',\n params: {\n version: DEFAULT_CONNECT_VERSION,\n\n event_acks: true,\n ...params\n }\n });\n};\n","import { buildRPCRequest } from './helpers';\n\nimport type { JSONRPCRequest } from './types/base';\n\nexport interface RPCReauthenticateParams {\n project: string;\n\n jwt_token: string;\n\n /** DPoP proof JWT for Client Bound SAT verification. */\n dpop_token?: string;\n}\n\nexport const RPCReauthenticate = (params: RPCReauthenticateParams): JSONRPCRequest => {\n const { dpop_token, ...authFields } = params;\n return buildRPCRequest({\n method: 'signalwire.reauthenticate',\n params: {\n authentication: authFields,\n ...(dpop_token ? { dpop_token } : {})\n }\n });\n};\n","import { buildRPCRequest, makeRPCResponse } from './helpers';\n\nimport type { JSONRPCSuccessResponse } from './types/base';\nimport type { SignalwirePingRequest } from './types/events';\nimport type { SignalwirePingResult } from './types/methods';\n\nexport const RPCPing = (): SignalwirePingRequest => {\n return buildRPCRequest({\n method: 'signalwire.ping',\n params: {\n timestamp: Date.now() / 1000\n }\n });\n};\n\nexport const RPCPingResponse = (\n id: string,\n timestamp?: number\n): JSONRPCSuccessResponse<SignalwirePingResult> => {\n return makeRPCResponse<SignalwirePingResult>({\n id,\n result: {\n timestamp: timestamp ?? Date.now() / 1000\n }\n });\n};\n","import { buildRPCRequest } from './helpers';\n\nimport type { JSONRPCRequest, JSONRPCMethod } from './types/base';\n\ninterface RPCExecuteParams {\n id?: string;\n method: JSONRPCMethod;\n params: Record<string, unknown>;\n}\n\nexport const RPCExecute = ({ method, params }: RPCExecuteParams): JSONRPCRequest => {\n return buildRPCRequest({\n method,\n params\n });\n};\n","import { buildRPCRequest, makeRPCResponse } from './helpers';\n\nimport type { VertoMethod } from '../types/rpc.types';\nimport type { JSONRPCSuccessResponse } from './types/base';\nimport type { WebrtcVertoParams, WebrtcVertoRequest } from './types/methods';\nimport type { VertoByeCause } from './types/verto';\n\ntype VertoParams = Record<string, unknown>;\n\nconst SDK_TO_VERTO_FIELD_MAP: Record<string, string> = {\n id: 'callID',\n destinationNumber: 'destination_number',\n remoteCallerName: 'remote_caller_id_name',\n remoteCallerNumber: 'remote_caller_id_number',\n callerName: 'caller_id_name',\n callerNumber: 'caller_id_number',\n fromCallAddressId: 'from_fabric_address_id'\n};\n\nconst EXCLUDED_DIALOG_PARAMS = new Set(['remoteSdp', 'localStream', 'remoteStream']);\n\n/**\n * Translate SDK fields into verto variables.\n * Returns a new object — the input is never mutated.\n */\n/** @internal Exported for testing only. */\nexport const filterVertoParams = (params: VertoParams): VertoParams => {\n if (!Object.prototype.hasOwnProperty.call(params, 'dialogParams')) {\n return params;\n }\n\n const sourceDialogParams = params.dialogParams as Record<string, unknown>;\n\n const filteredDialogParams = Object.entries(sourceDialogParams).reduce<Record<string, unknown>>(\n (acc, [key, value]) => {\n if (EXCLUDED_DIALOG_PARAMS.has(key)) {\n return acc;\n }\n const mappedKey = SDK_TO_VERTO_FIELD_MAP[key] ?? key;\n return { ...acc, [mappedKey]: value };\n },\n {}\n );\n\n return {\n ...params,\n dialogParams: filteredDialogParams\n };\n};\n\nconst buildVertoRPCMessage = (method: VertoMethod) => {\n return (params: VertoParams = {}) => {\n return buildRPCRequest({\n method,\n params: filterVertoParams(params)\n });\n };\n};\n\nexport type VertoRPCMessage = ReturnType<ReturnType<typeof buildVertoRPCMessage>>;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type JSONRPCParams = Record<string, any>;\n\nexport const WebrtcVerto = (params: WebrtcVertoParams): WebrtcVertoRequest => {\n return buildRPCRequest({\n method: 'webrtc.verto',\n params\n });\n};\n\nexport type WebrtcVertoRPCMessage = ReturnType<ReturnType<typeof buildVertoRPCMessage>>;\n\nexport const VertoInvite = buildVertoRPCMessage('verto.invite');\nexport const VertoBye = buildVertoRPCMessage('verto.bye');\nexport const VertoAttach = buildVertoRPCMessage('verto.attach');\nexport const VertoModify = buildVertoRPCMessage('verto.modify');\nexport const VertoInfo = buildVertoRPCMessage('verto.info');\nexport const VertoAnswer = buildVertoRPCMessage('verto.answer');\nexport const VertoSubscribe = buildVertoRPCMessage('verto.subscribe');\nexport const VertoPong = buildVertoRPCMessage('verto.pong');\nexport const VertoResult = (id: string, method: VertoMethod): JSONRPCSuccessResponse => {\n return makeRPCResponse({\n id,\n result: {\n method\n }\n });\n};\n\nexport interface VertoModifyResponse {\n action: string;\n callID: string;\n holdState: 'held' | 'active';\n\n node_id?: string;\n sdp?: string;\n}\n\nexport const VertoByeCauseCodes: Record<VertoByeCause, string> = {\n NORMAL_CLEARING: '16',\n\n USER_BUSY: '17',\n\n MEDIA_TIMEOUT: '804'\n} as const;\n","import { makeRPCResponse } from './helpers';\n\nimport type { JSONRPCSuccessResponse } from './types/base';\n\nexport const RPCEventAckResponse = (id: string): JSONRPCSuccessResponse =>\n makeRPCResponse({ id, result: {} });\n","import { getLogger } from '../utils/logger';\n\nimport type { StorageManager } from './StorageManager';\nimport type { Address } from '../core/entities/Address';\nimport type { Call, CallOptions } from '../core/entities/types/call.types';\nimport type { MediaDirections } from '../core/types/media.types';\nimport type { DeviceController } from '../interfaces/DeviceController';\n\nconst logger = getLogger();\ninterface AttachableCall {\n id: string;\n to?: string;\n mediaDirections: MediaDirections;\n nodeId?: string;\n}\n\nexport interface OutboundCallProvider {\n createOutboundCall(destination: string | Address, options?: CallOptions): Promise<Call>;\n}\n\ninterface Attachment {\n destination: string;\n mediaDirections: MediaDirections;\n audioInputDevice: MediaDeviceInfo | null;\n videoInputDevice: MediaDeviceInfo | null;\n nodeId?: string;\n attachedAt: number;\n}\n\nexport class AttachManager {\n private session!: OutboundCallProvider;\n constructor(\n private readonly storage: StorageManager,\n\n private readonly deviceController: DeviceController,\n private readonly reconnectCallsTimeout: number,\n private attachKey: string\n ) {}\n\n async detachAll(): Promise<void> {\n const attached = await this.readAttached();\n for (const callId of Object.keys(attached)) {\n await this.detach({\n id: callId,\n nodeId: attached[callId].nodeId,\n mediaDirections: attached[callId].mediaDirections\n });\n }\n }\n\n public setSession(session: OutboundCallProvider): void {\n this.session = session;\n }\n\n private async readAttached(): Promise<Record<string, Attachment>> {\n try {\n return (await this.storage.getItem(this.attachKey)) ?? {};\n } catch (error) {\n logger.warn('[AttachManager] Failed to retrieve attached calls from storage', error);\n return {};\n }\n }\n\n private async writeAttached(attached: Record<string, Attachment>): Promise<void> {\n try {\n await this.storage.setItem(this.attachKey, attached);\n } catch (error) {\n logger.warn('[AttachManager] Failed to write attached calls to storage', error);\n }\n }\n\n public async attach(call: AttachableCall): Promise<void> {\n if (!call.to) {\n logger.warn('[AttachManager] Skip attach for calls with no destination');\n return;\n }\n const attachment = {\n nodeId: call.nodeId,\n destination: call.to,\n mediaDirections: call.mediaDirections,\n audioInputDevice:\n call.mediaDirections.audio !== 'inactive'\n ? this.deviceController.selectedAudioInputDevice\n : null,\n videoInputDevice:\n call.mediaDirections.video !== 'inactive'\n ? this.deviceController.selectedVideoInputDevice\n : null,\n attachedAt: Date.now()\n };\n const attached = await this.readAttached();\n const updated = { ...attached, [call.id]: attachment };\n await this.writeAttached(updated);\n }\n\n public async detach(call: AttachableCall): Promise<void> {\n const attached = await this.readAttached();\n const { [call.id]: _, ...remaining } = attached;\n await this.writeAttached(remaining);\n }\n\n public async flush(): Promise<void> {\n await this.writeAttached({});\n }\n\n /**\n * Reattach to previously active calls by sending verto.invite with\n * reattaching: true.\n *\n * NOTE: This currently fails with INVALID_CALL_REFERENCE because the\n * server's jsock UUID check rejects the new connection's UUID. A\n * server-side fix is needed: when reattaching: true is explicitly set\n * in dialogParams, FreeSWITCH's attempt_reattach() should update the\n * call's jsock reference to the new connection's UUID instead of\n * rejecting. Once that fix is deployed, this will work for both\n * page reloads and WebSocket reconnects.\n *\n * Failed reattach attempts are handled gracefully — the stale call\n * reference is cleaned up from storage.\n */\n public async reattachCalls(): Promise<void> {\n const attached = await this.readAttached();\n\n await this.detachExpired();\n\n for (const [callId, attachment] of Object.entries(attached)) {\n const { destination } = attachment;\n const options = this.buildCallOptions(attachment);\n\n let succeeded = false;\n for (let attempt = 1; attempt <= 3; attempt++) {\n try {\n await this.session.createOutboundCall(destination, { callId, ...options });\n logger.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);\n succeeded = true;\n break;\n } catch (error) {\n logger.warn(\n `[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`,\n error\n );\n if (attempt < 3) {\n await new Promise((r) => setTimeout(r, (attempt + 1) * 1000));\n }\n }\n }\n\n if (!succeeded) {\n logger.warn(\n `[AttachManager] Reattach failed after 3 attempts for call ${callId}, removing reference`\n );\n await this.detach({ id: callId, mediaDirections: attachment.mediaDirections });\n }\n }\n }\n\n /**\n * Build CallOptions from stored attachment data for a call being reattached.\n * Also used by the session-level verto.attach handler.\n */\n public buildCallOptions(attachment: Attachment): CallOptions {\n const { audio: audioDirection, video: videoDirection } = attachment.mediaDirections;\n const { audioInputDevice, videoInputDevice, nodeId } = attachment;\n const receiveAudio = audioDirection.includes('recv');\n const receiveVideo = videoDirection.includes('recv');\n const sendAudio = audioDirection.includes('send');\n const sendVideo = videoDirection.includes('send');\n const inputAudioDeviceConstraints = sendAudio\n ? { audio: true, ...this.deviceController.deviceInfoToConstraints(audioInputDevice) }\n : undefined;\n const inputVideoDeviceConstraints = sendVideo\n ? { video: true, ...this.deviceController.deviceInfoToConstraints(videoInputDevice) }\n : undefined;\n return {\n nodeId,\n receiveAudio,\n receiveVideo,\n inputAudioDeviceConstraints,\n inputVideoDeviceConstraints,\n reattach: true\n };\n }\n\n /**\n * Consume stored attachment data for a pending call (used by session-level\n * verto.attach handler as a future path when server supports it).\n */\n public consumePendingAttachment(_callId: string): CallOptions | undefined {\n // Read synchronously from the last known attached state\n // This is a best-effort lookup — the call may already be detached\n return undefined; // Will be implemented when server pushes verto.attach\n }\n\n private async detachExpired(): Promise<void> {\n const attached = await this.readAttached();\n\n const now = Date.now();\n const timeout = this.reconnectCallsTimeout;\n const expired = Object.entries(attached).filter(\n ([, attachment]) => now - attachment.attachedAt > timeout\n );\n\n if (expired.length > 0) {\n const remaining = { ...attached };\n for (const [callId] of expired) {\n delete remaining[callId];\n }\n await this.writeAttached(remaining);\n }\n }\n}\n","/**\n * Represents an on/off capability state\n * Both `on` and `off` can be true if the parent permission grants both\n */\nexport interface OnOffCapability {\n readonly on: boolean;\n readonly off: boolean;\n}\n\n/**\n * Member-level capabilities for self or other members\n */\nexport interface MemberCapabilities {\n readonly muteAudio: OnOffCapability;\n readonly muteVideo: OnOffCapability;\n readonly deaf: OnOffCapability;\n readonly raisehand: OnOffCapability;\n readonly microphoneVolume: boolean;\n readonly microphoneSensitivity: boolean;\n readonly speakerVolume: boolean;\n readonly position: boolean;\n readonly meta: boolean;\n readonly remove: boolean;\n readonly audioFlags: boolean;\n}\n\n/**\n * Call-level capabilities state\n */\nexport interface CallCapabilitiesState {\n readonly self: MemberCapabilities;\n readonly member: MemberCapabilities;\n readonly end: boolean;\n readonly setLayout: boolean;\n readonly sendDigit: boolean;\n readonly vmutedHide: OnOffCapability;\n readonly lock: OnOffCapability;\n readonly device: boolean;\n readonly screenshare: boolean;\n}\n\n/**\n * Default on/off state with no permissions\n */\nexport const DEFAULT_ON_OFF: OnOffCapability = {\n on: false,\n off: false\n};\n\n/**\n * Default member capabilities with no permissions\n */\nexport const DEFAULT_MEMBER_CAPABILITIES: MemberCapabilities = {\n muteAudio: DEFAULT_ON_OFF,\n muteVideo: DEFAULT_ON_OFF,\n deaf: DEFAULT_ON_OFF,\n raisehand: DEFAULT_ON_OFF,\n microphoneVolume: false,\n microphoneSensitivity: false,\n speakerVolume: false,\n position: false,\n meta: false,\n remove: false,\n audioFlags: false\n};\n\n/**\n * Default call capabilities with no permissions\n */\nexport const DEFAULT_CALL_CAPABILITIES: CallCapabilitiesState = {\n self: DEFAULT_MEMBER_CAPABILITIES,\n member: DEFAULT_MEMBER_CAPABILITIES,\n end: false,\n setLayout: false,\n sendDigit: false,\n vmutedHide: DEFAULT_ON_OFF,\n lock: DEFAULT_ON_OFF,\n device: false,\n screenshare: false\n};\n","import { DEFAULT_CALL_CAPABILITIES, DEFAULT_MEMBER_CAPABILITIES, DEFAULT_ON_OFF } from './types';\n\nimport type { CallCapabilitiesState, MemberCapabilities, OnOffCapability } from './types';\n\ntype MemberType = 'self' | 'member';\n\n/**\n * Computes an on/off capability state from filtered flags\n *\n * Logic:\n * - `on` is true if any flag does NOT end with `.off`\n * - `off` is true if any flag does NOT end with `.on`\n *\n * This means parent permissions (without .on/.off suffix) grant both on and off\n */\nfunction computeOnOffCapability(filteredFlags: string[]): OnOffCapability {\n if (filteredFlags.length === 0) {\n return DEFAULT_ON_OFF;\n }\n\n return {\n on: filteredFlags.some((flag) => !flag.endsWith('.off')),\n off: filteredFlags.some((flag) => !flag.endsWith('.on'))\n };\n}\n\n/**\n * Filters flags for a specific member capability with on/off support\n *\n * Matches:\n * - The member type itself (e.g., \"self\" grants all self capabilities)\n * - The parent capability (e.g., \"self.mute\" grants all mute capabilities)\n * - The specific capability and its on/off variants (e.g., \"self.mute.audio*\")\n */\nfunction filterMemberOnOffFlags(\n flags: string[],\n memberType: MemberType,\n parent: string,\n capability: string\n): string[] {\n return flags.filter(\n (flag) =>\n flag === memberType ||\n flag === `${memberType}.${parent}` ||\n flag.startsWith(`${memberType}.${parent}.${capability}`)\n );\n}\n\n/**\n * Checks if a boolean capability is granted\n *\n * Matches:\n * - The member type itself (e.g., \"self\" grants all self capabilities)\n * - The parent capability if provided (e.g., \"self.microphone\" grants volume and sensitivity)\n * - The specific capability (e.g., \"self.microphone.volume.set\")\n */\nfunction hasBooleanCapability(\n flags: string[],\n memberType: MemberType,\n parent: string | null,\n capability: string\n): boolean {\n return flags.some(\n (flag) =>\n flag === memberType ||\n (parent !== null && flag === `${memberType}.${parent}`) ||\n flag.startsWith(`${memberType}.${parent ? `${parent}.` : ''}${capability}`)\n );\n}\n\n/**\n * Computes member-level capabilities (self or member) from raw flags\n */\nfunction computeMemberCapabilities(flags: string[], memberType: MemberType): MemberCapabilities {\n // Filter only flags relevant to this member type\n const memberFlags = flags.filter((flag) => flag.startsWith(memberType) || flag === memberType);\n\n if (memberFlags.length === 0) {\n return DEFAULT_MEMBER_CAPABILITIES;\n }\n\n return {\n muteAudio: computeOnOffCapability(filterMemberOnOffFlags(flags, memberType, 'mute', 'audio')),\n muteVideo: computeOnOffCapability(filterMemberOnOffFlags(flags, memberType, 'mute', 'video')),\n deaf: computeOnOffCapability(\n flags.filter((flag) => flag === memberType || flag.startsWith(`${memberType}.deaf`))\n ),\n raisehand: computeOnOffCapability(\n flags.filter((flag) => flag === memberType || flag.startsWith(`${memberType}.raisehand`))\n ),\n microphoneVolume: hasBooleanCapability(flags, memberType, 'microphone', 'volume'),\n microphoneSensitivity: hasBooleanCapability(flags, memberType, 'microphone', 'sensitivity'),\n speakerVolume: hasBooleanCapability(flags, memberType, 'speaker', 'volume'),\n position: hasBooleanCapability(flags, memberType, null, 'position'),\n meta: hasBooleanCapability(flags, memberType, null, 'meta'),\n remove: hasBooleanCapability(flags, memberType, null, 'remove'),\n audioFlags: hasBooleanCapability(flags, memberType, null, 'audioflags')\n };\n}\n\n/**\n * Computes all call capabilities from raw capability strings\n *\n * This is a pure function that transforms the raw capability strings\n * from the `call.joined` event into a structured capabilities state.\n *\n * @param capabilities - Raw capability strings from the server\n * @returns Computed capabilities state\n */\nexport function computeCapabilities(capabilities: string[]): CallCapabilitiesState {\n if (capabilities.length === 0) {\n return DEFAULT_CALL_CAPABILITIES;\n }\n\n return {\n self: computeMemberCapabilities(capabilities, 'self'),\n member: computeMemberCapabilities(capabilities, 'member'),\n end: capabilities.some((cap) => cap === 'end'),\n setLayout: capabilities.some((cap) => cap.startsWith('layout')),\n sendDigit: capabilities.some((cap) => cap.startsWith('digit')),\n vmutedHide: computeOnOffCapability(capabilities.filter((flag) => flag.startsWith('vmuted'))),\n lock: computeOnOffCapability(capabilities.filter((flag) => flag.startsWith('lock'))),\n device: capabilities.some((cap) => cap === 'device'),\n screenshare: capabilities.some((cap) => cap === 'screenshare')\n };\n}\n","import { distinctUntilChanged, map } from 'rxjs';\n\nimport { computeCapabilities } from './computeCapabilities';\nimport { DEFAULT_CALL_CAPABILITIES } from './types';\nimport { Destroyable } from '../../behaviors/Destroyable';\n\nimport type { CallCapabilitiesState, MemberCapabilities, OnOffCapability } from './types';\nimport type { Observable } from 'rxjs';\n\n/**\n * SelfCapabilities manages the capability state for the self participant.\n *\n * Capabilities are received from the server via `call.joined` events and determine\n * what actions the current participant is allowed to perform.\n *\n * Each capability is exposed as both:\n * - An observable (e.g., `end$`) for reactive state management\n * - A synchronous getter (e.g., `end`) for immediate access\n *\n * Member-level capabilities are accessed via the grouped `self` / `member` accessors:\n * - `capabilities.self.muteAudio` (sync)\n * - `capabilities.self$` (observable)\n *\n * When a new `call.joined` event is received, the capabilities state is\n * completely replaced (not merged).\n */\nexport class SelfCapabilities extends Destroyable {\n private _state$ = this.createBehaviorSubject<CallCapabilitiesState>(DEFAULT_CALL_CAPABILITIES);\n\n /**\n * Updates the capabilities state from raw capability strings.\n * This completely replaces the current state.\n *\n * @param capabilities - Raw capability strings from the server\n * @internal\n */\n public updateFromRaw(capabilities: string[]): void {\n const newState = computeCapabilities(capabilities);\n this._state$.next(newState);\n }\n\n // ============================================\n // Self member capabilities\n // ============================================\n\n /** Observable for self member capabilities */\n public get self$(): Observable<MemberCapabilities> {\n return this.cachedObservable('self$', () =>\n this._state$.pipe(\n map((state) => state.self),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current self member capabilities */\n public get self(): MemberCapabilities {\n return this._state$.value.self;\n }\n\n // ============================================\n // Member capabilities (other participants)\n // ============================================\n\n /** Observable for other member capabilities */\n public get member$(): Observable<MemberCapabilities> {\n return this.cachedObservable('member$', () =>\n this._state$.pipe(\n map((state) => state.member),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current other member capabilities */\n public get member(): MemberCapabilities {\n return this._state$.value.member;\n }\n\n // ============================================\n // Call-level capabilities\n // ============================================\n\n /** Observable for end call capability */\n public get end$(): Observable<boolean> {\n return this.cachedObservable('end$', () =>\n this._state$.pipe(\n map((state) => state.end),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current end call capability */\n public get end(): boolean {\n return this._state$.value.end;\n }\n\n /** Observable for set layout capability */\n public get setLayout$(): Observable<boolean> {\n return this.cachedObservable('setLayout$', () =>\n this._state$.pipe(\n map((state) => state.setLayout),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current set layout capability */\n public get setLayout(): boolean {\n return this._state$.value.setLayout;\n }\n\n /** Observable for send digit capability */\n public get sendDigit$(): Observable<boolean> {\n return this.cachedObservable('sendDigit$', () =>\n this._state$.pipe(\n map((state) => state.sendDigit),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current send digit capability */\n public get sendDigit(): boolean {\n return this._state$.value.sendDigit;\n }\n\n /** Observable for vmuted hide capability */\n public get vmutedHide$(): Observable<OnOffCapability> {\n return this.cachedObservable('vmutedHide$', () =>\n this._state$.pipe(\n map((state) => state.vmutedHide),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current vmuted hide capability */\n public get vmutedHide(): OnOffCapability {\n return this._state$.value.vmutedHide;\n }\n\n /** Observable for lock capability */\n public get lock$(): Observable<OnOffCapability> {\n return this.cachedObservable('lock$', () =>\n this._state$.pipe(\n map((state) => state.lock),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current lock capability */\n public get lock(): OnOffCapability {\n return this._state$.value.lock;\n }\n\n /** Observable for device capability */\n public get device$(): Observable<boolean> {\n return this.cachedObservable('device$', () =>\n this._state$.pipe(\n map((state) => state.device),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current device capability */\n public get device(): boolean {\n return this._state$.value.device;\n }\n\n /** Observable for screenshare capability */\n public get screenshare$(): Observable<boolean> {\n return this.cachedObservable('screenshare$', () =>\n this._state$.pipe(\n map((state) => state.screenshare),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current screenshare capability */\n public get screenshare(): boolean {\n return this._state$.value.screenshare;\n }\n\n // ============================================\n // Full state access\n // ============================================\n\n /** Observable for the full capabilities state */\n public get state$(): Observable<CallCapabilitiesState> {\n return this._state$.asObservable();\n }\n\n /** Current full capabilities state */\n public get state(): CallCapabilitiesState {\n return this._state$.value;\n }\n}\n","export function toggleDeafMethod(is: boolean): 'call.undeaf' | 'call.deaf' {\n return is ? 'call.undeaf' : 'call.deaf';\n}\n\nexport function toggleHandraiseMethod(is: boolean): 'call.lowerhand' | 'call.raisehand' {\n return is ? 'call.lowerhand' : 'call.raisehand';\n}\n","import { distinctUntilChanged, map } from 'rxjs/operators';\n\nimport { Destroyable } from '../../behaviors/Destroyable';\nimport { PreferencesContainer } from '../../containers/PreferencesContainer';\nimport { getLogger } from '../../utils/logger';\nimport { SelfCapabilities } from '../capabilities';\nimport { UnimplementedError } from '../errors';\nimport { toggleDeafMethod, toggleHandraiseMethod } from '../RPCMessages/utils';\n\nimport type { CallParticipant, CallSelfParticipant } from './types/call.types';\nimport type { SelectDeviceOptions } from './types/participant.types';\nimport type { DeviceController } from '../../interfaces/DeviceController';\nimport type { VertoManager } from '../../interfaces/VertoManager';\nimport type { ScreenShareStatus } from '../../managers/types/verto-manager.types';\nimport type { JSONRPCResponse } from '../RPCMessages/types/base';\nimport type { Member, LayoutLayer, MemberTarget } from '../RPCMessages/types/common';\nimport type { VideoPosition } from '../types/call.types';\nimport type { MediaOptions } from '../types/media.types';\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\n/**\n * Callback type for executing call methods\n * Injected to avoid circular dependency with Call class\n */\nexport type ExecuteMethod = <T extends JSONRPCResponse = JSONRPCResponse>(\n target: string | MemberTarget,\n method: string,\n args: Record<string, unknown>\n) => Promise<T>;\n\ntype ParticipantState = Member & { position: LayoutLayer };\n\nconst initialState: Partial<ParticipantState> = {};\n\n/**\n * Represents a participant in a call.\n *\n * Provides observable state (audio/video mute, hand raise, volume, etc.)\n * and control methods for the participant. See {@link SelfParticipant} for\n * the local participant with additional device control.\n */\nexport class Participant extends Destroyable implements CallParticipant {\n /** Unique member ID of this participant. */\n public readonly id!: string;\n private _state$ = this.createBehaviorSubject<Partial<ParticipantState>>(initialState);\n constructor(\n id: string,\n protected executeMethod: ExecuteMethod,\n protected deviceController: DeviceController\n ) {\n super();\n this.id = id;\n }\n /** @internal */\n public upnext(data: Partial<ParticipantState>): void {\n this._state$.next({ ...this._state$.value, ...data });\n }\n\n /** Observable of the participant's display name. */\n public get name$(): Observable<string | undefined> {\n return this.cachedObservable('name$', () =>\n this._state$.pipe(\n map((state) => state.name),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable of the participant type (e.g. `'member'`, `'screen'`). */\n public get type$(): Observable<string | undefined> {\n return this.cachedObservable('type$', () =>\n this._state$.pipe(\n map((state) => state.type),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable indicating whether the participant has raised their hand. */\n public get handraised$(): Observable<boolean | undefined> {\n return this.cachedObservable('handraised$', () =>\n this._state$.pipe(\n map((state) => state.handraised),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable indicating whether the participant is visible in the layout. */\n public get visible$(): Observable<boolean | undefined> {\n return this.cachedObservable('visible$', () =>\n this._state$.pipe(\n map((state) => state.visible),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable indicating whether the participant's audio is muted. */\n public get audioMuted$(): Observable<boolean | undefined> {\n return this.cachedObservable('audioMuted$', () =>\n this._state$.pipe(\n map((state) => state.audio_muted),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable indicating whether the participant's video is muted. */\n public get videoMuted$(): Observable<boolean | undefined> {\n return this.cachedObservable('videoMuted$', () =>\n this._state$.pipe(\n map((state) => state.video_muted),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable indicating whether the participant is deafened. */\n public get deaf$(): Observable<boolean | undefined> {\n return this.cachedObservable('deaf$', () =>\n this._state$.pipe(\n map((state) => state.deaf),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable of the participant's microphone input volume. */\n public get inputVolume$(): Observable<number | undefined> {\n return this.cachedObservable('inputVolume$', () =>\n this._state$.pipe(\n map((state) => state.input_volume),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable of the participant's speaker output volume. */\n public get outputVolume$(): Observable<number | undefined> {\n return this.cachedObservable('outputVolume$', () =>\n this._state$.pipe(\n map((state) => state.output_volume),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable of the microphone input sensitivity level. */\n public get inputSensitivity$(): Observable<number | undefined> {\n return this.cachedObservable('inputSensitivity$', () =>\n this._state$.pipe(\n map((state) => state.input_sensitivity),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable indicating whether echo cancellation is enabled. */\n public get echoCancellation$(): Observable<boolean | undefined> {\n return this.cachedObservable('echoCancellation$', () =>\n this._state$.pipe(\n map((state) => state.echo_cancellation),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable indicating whether auto-gain control is enabled. */\n public get autoGain$(): Observable<boolean | undefined> {\n return this.cachedObservable('autoGain$', () =>\n this._state$.pipe(\n map((state) => state.auto_gain),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable indicating whether noise suppression is enabled. */\n public get noiseSuppression$(): Observable<boolean | undefined> {\n return this.cachedObservable('noiseSuppression$', () =>\n this._state$.pipe(\n map((state) => state.noise_suppression),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable indicating whether low-bitrate mode is active. */\n public get lowbitrate$(): Observable<boolean | undefined> {\n return this.cachedObservable('lowbitrate$', () =>\n this._state$.pipe(\n map((state) => state.lowbitrate),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable indicating whether noise reduction is active. */\n public get denoise$(): Observable<boolean | undefined> {\n return this.cachedObservable('denoise$', () =>\n this._state$.pipe(\n map((state) => state.denoise),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable of custom metadata for this participant. */\n public get meta$(): Observable<Record<string, unknown> | undefined> {\n return this.cachedObservable('meta$', () =>\n this._state$.pipe(\n map((state) => state.meta),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable of the participant's subscriber ID. */\n public get subscriberId$(): Observable<string | undefined> {\n return this.cachedObservable('subscriberId$', () =>\n this._state$.pipe(\n map((state) => state.subscriber_id),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable of the participant's address ID. */\n public get addressId$(): Observable<string | undefined> {\n return this.cachedObservable('addressId$', () =>\n this._state$.pipe(\n map((state) => state.address_id),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable of the server node ID for this participant. */\n public get nodeId$(): Observable<string | undefined> {\n return this.cachedObservable('nodeId$', () =>\n this._state$.pipe(\n map((state) => state.node_id),\n distinctUntilChanged()\n )\n );\n }\n\n /** Observable indicating whether the participant is currently speaking. */\n public get isTalking$(): Observable<boolean | undefined> {\n return this.cachedObservable('isTalking$', () =>\n this._state$.pipe(\n map((state) => state.talking),\n distinctUntilChanged()\n )\n );\n }\n\n /** Whether the participant is currently speaking. */\n public get isTalking(): boolean {\n return this._state$.value.talking ?? false;\n }\n\n /** Observable of the participant's layout position. */\n public get position$(): Observable<LayoutLayer | undefined> {\n return this.cachedObservable('position$', () =>\n this._state$.pipe(\n map((state) => state.position),\n distinctUntilChanged()\n )\n );\n }\n\n /** Current layout position. */\n public get position(): LayoutLayer | undefined {\n return this._state$.value.position;\n }\n\n /** Whether the participant is an audience member (view-only). */\n public get isAudience(): boolean {\n return this._state$.value.isAudience ?? false;\n }\n\n /** Display name of this participant. */\n public get name(): string | undefined {\n return this._state$.value.name;\n }\n\n /** Participant type (e.g. `'member'`, `'screen'`). */\n public get type(): string | undefined {\n return this._state$.value.type;\n }\n\n /** Whether the participant has raised their hand. */\n public get handraised(): boolean {\n return this._state$.value.handraised ?? false;\n }\n\n /** Whether the participant is visible in the layout. */\n public get visible(): boolean {\n return this._state$.value.visible ?? false;\n }\n\n /** Whether the participant's audio is muted. */\n public get audioMuted(): boolean {\n return this._state$.value.audio_muted ?? false;\n }\n\n /** Whether the participant's video is muted. */\n public get videoMuted(): boolean {\n return this._state$.value.video_muted ?? false;\n }\n\n /** Whether the participant is deafened (incoming audio muted). */\n public get deaf(): boolean {\n return this._state$.value.deaf ?? false;\n }\n\n /** Current microphone input volume level, or `undefined` if not set. */\n public get inputVolume(): number | undefined {\n return this._state$.value.input_volume;\n }\n\n /** Current speaker output volume level, or `undefined` if not set. */\n public get outputVolume(): number | undefined {\n return this._state$.value.output_volume;\n }\n\n /** Current microphone input sensitivity level, or `undefined` if not set. */\n public get inputSensitivity(): number | undefined {\n return this._state$.value.input_sensitivity;\n }\n\n /** Whether echo cancellation is enabled. */\n public get echoCancellation(): boolean {\n return this._state$.value.echo_cancellation ?? false;\n }\n\n /** Whether automatic gain control is enabled. */\n public get autoGain(): boolean {\n return this._state$.value.auto_gain ?? false;\n }\n\n /** Whether noise suppression is enabled. */\n public get noiseSuppression(): boolean {\n return this._state$.value.noise_suppression ?? false;\n }\n\n /** Whether low-bitrate mode is active. */\n public get lowbitrate(): boolean {\n return this._state$.value.lowbitrate ?? false;\n }\n\n /** Whether noise reduction (denoise) is active. */\n public get denoise(): boolean {\n return this._state$.value.denoise ?? false;\n }\n\n /** Custom metadata for this participant, or `undefined` if not set. */\n public get meta(): Record<string, unknown> | undefined {\n return this._state$.value.meta;\n }\n\n /** Subscriber ID of this participant, or `undefined` if not available. */\n public get subscriberId(): string | undefined {\n return this._state$.value.subscriber_id;\n }\n\n /** Address ID of this participant, or `undefined` if not available. */\n public get addressId(): string | undefined {\n return this._state$.value.address_id;\n }\n\n /** Server node ID for this participant, or `undefined` if not available. */\n public get nodeId(): string | undefined {\n return this._state$.value.node_id;\n }\n\n /** @internal */\n public get value(): Partial<Member> {\n return this._state$.value;\n }\n\n /** Toggles the deafened state (mutes/unmutes incoming audio). */\n public async toggleDeaf(): Promise<void> {\n const method = toggleDeafMethod(this.deaf);\n const params = {};\n await this.executeMethod(this.id, method, params);\n }\n\n /** Toggles the hand-raised state. */\n public async toggleHandraise(): Promise<void> {\n await this.executeMethod(this.id, toggleHandraiseMethod(this.handraised), {});\n }\n\n /** Mutes the participant's audio. */\n public async mute(): Promise<void> {\n await this.executeMethod(this.id, 'call.mute', { channels: ['audio'] });\n }\n\n /** Unmutes the participant's audio. */\n public async unmute(): Promise<void> {\n await this.executeMethod(this.id, 'call.unmute', { channels: ['audio'] });\n }\n\n /** Toggles the participant's audio mute state. */\n public async toggleMute(): Promise<void> {\n return this.audioMuted ? this.unmute() : this.mute();\n }\n\n /** Mutes the participant's video. */\n public async muteVideo(): Promise<void> {\n await this.executeMethod(this.id, 'call.mute', { channels: ['video'] });\n }\n\n /** Unmutes the participant's video. */\n public async unmuteVideo(): Promise<void> {\n await this.executeMethod(this.id, 'call.unmute', { channels: ['video'] });\n }\n\n /** Toggles the participant's video mute state. */\n public async toggleMuteVideo(): Promise<void> {\n return this.videoMuted ? this.unmuteVideo() : this.muteVideo();\n }\n\n /** Toggles echo cancellation on the audio input. */\n public async toggleEchoCancellation(): Promise<void> {\n await this.executeMethod(this.id, 'call.audioflags.set', {\n echo_cancellation: !this.echoCancellation,\n auto_gain: this.autoGain,\n noise_suppression: this.noiseSuppression\n });\n }\n\n /** Toggles automatic gain control on the audio input. */\n public async toggleAudioInputAutoGain(): Promise<void> {\n await this.executeMethod(this.id, 'call.audioflags.set', {\n echo_cancellation: this.echoCancellation,\n auto_gain: !this.autoGain,\n noise_suppression: this.noiseSuppression\n });\n }\n\n /** Toggles noise suppression on the audio input. */\n public async toggleNoiseSuppression(): Promise<void> {\n await this.executeMethod(this.id, 'call.audioflags.set', {\n echo_cancellation: this.echoCancellation,\n auto_gain: this.autoGain,\n noise_suppression: !this.noiseSuppression\n });\n }\n\n // eslint-disable-next-line @typescript-eslint/require-await\n public async toggleLowbitrate(): Promise<void> {\n // NEEDS check backend implementation\n throw new UnimplementedError();\n }\n\n /** Sets the microphone input sensitivity level. */\n public async setAudioInputSensitivity(value: number): Promise<void> {\n await this.executeMethod(this.id, 'call.microphone.sensitivity.set', {\n sensitivity: value\n });\n }\n\n /**\n * Sets the microphone input volume level.\n * @param value - Volume level (0-100).\n */\n public async setAudioInputVolume(value: number): Promise<void> {\n await this.executeMethod(this.id, 'call.microphone.volume.set', {\n volume: value\n });\n }\n\n /**\n * Sets the speaker output volume level.\n * @param value - Volume level (0-100).\n */\n public async setAudioOutputVolume(value: number): Promise<void> {\n await this.executeMethod(this.id, 'call.speaker.volume.set', {\n volume: value\n });\n }\n\n /**\n * Sets the participant's position in the video layout.\n * @param value - The {@link VideoPosition} to assign (e.g. `'auto'`, `'reserved-0'`).\n */\n public async setPosition(value: VideoPosition): Promise<void> {\n await this.executeMethod(this.id, 'call.member.position.set', {\n position: value\n });\n }\n\n /** Removes this participant from the call. */\n public async remove(): Promise<void> {\n const state = this._state$.value;\n const target: MemberTarget = {\n member_id: this.id,\n call_id: state.call_id ?? '',\n node_id: state.node_id ?? ''\n };\n await this.executeMethod(target, 'call.member.remove', {});\n }\n\n /** Ends the call for this participant. */\n public async end(): Promise<void> {\n await this.executeMethod(this.id, 'call.end', {});\n }\n\n /**\n * Replaces custom metadata for this participant.\n * @param _meta - Metadata object to set.\n * @throws {UnimplementedError} Not yet implemented.\n */\n // eslint-disable-next-line @typescript-eslint/require-await\n public async setMeta(_meta: Record<string, unknown>): Promise<void> {\n // NEEDS backend implementation\n throw new UnimplementedError();\n }\n /**\n * Merges values into custom metadata (unlike {@link setMeta} which replaces).\n * @param _meta - Metadata to merge.\n * @throws {UnimplementedError} Not yet implemented.\n */\n // eslint-disable-next-line @typescript-eslint/require-await\n public async updateMeta(_meta: Record<string, unknown>): Promise<void> {\n // NEEDS backend implementation\n throw new UnimplementedError();\n }\n\n /** Destroys the participant, releasing all subscriptions and references. */\n public destroy(): void {\n // Cleanup callback reference - intentionally breaking type safety for cleanup\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any\n this.executeMethod = undefined as any;\n super.destroy();\n }\n}\n\n/**\n * The local participant in a call, with additional device and media control.\n *\n * Extends {@link Participant} with screen sharing, device selection,\n * and local media stream management.\n */\nexport class SelfParticipant extends Participant implements CallSelfParticipant {\n /**\n * Capabilities for this participant.\n * Contains all capability flags as both observables and values.\n */\n public readonly capabilities: SelfCapabilities;\n\n /**\n * Studio audio mode state. When enabled, all audio processing\n * (echo cancellation, noise suppression, auto gain control) is disabled\n * to provide raw/unprocessed audio for musicians, podcasters, etc.\n */\n private _studioAudio$ = this.createBehaviorSubject<boolean>(false);\n\n /** @internal */\n constructor(\n id: string,\n executeMethod: ExecuteMethod,\n private vertoManager: VertoManager,\n deviceController: DeviceController\n ) {\n super(id, executeMethod, deviceController);\n this.capabilities = new SelfCapabilities();\n }\n\n public override destroy(): void {\n this.capabilities.destroy();\n super.destroy();\n }\n\n /** Observable indicating whether studio audio (raw/unprocessed audio) mode is enabled. */\n public get studioAudio$(): Observable<boolean> {\n return this._studioAudio$.asObservable();\n }\n\n /** Whether studio audio (raw/unprocessed audio) mode is currently enabled. */\n public get studioAudio(): boolean {\n return this._studioAudio$.value;\n }\n\n /**\n * Enables studio audio mode by disabling all audio processing.\n * Sets echoCancellation, noiseSuppression, and autoGainControl to false.\n */\n public async enableStudioAudio(): Promise<void> {\n if (this._studioAudio$.value) {\n return;\n }\n this._studioAudio$.next(true);\n await this.executeMethod(this.id, 'call.audioflags.set', {\n echo_cancellation: false,\n auto_gain: false,\n noise_suppression: false\n });\n }\n\n /**\n * Disables studio audio mode by restoring all audio processing to enabled.\n * Sets echoCancellation, noiseSuppression, and autoGainControl to true.\n */\n public async disableStudioAudio(): Promise<void> {\n if (!this._studioAudio$.value) {\n return;\n }\n this._studioAudio$.next(false);\n await this.executeMethod(this.id, 'call.audioflags.set', {\n echo_cancellation: true,\n auto_gain: true,\n noise_suppression: true\n });\n }\n\n /** Starts sharing the local screen. */\n public async startScreenShare(): Promise<void> {\n try {\n await this.vertoManager.addScreenMedia();\n } catch (error) {\n logger.error('[Participant.startScreenShare] Screen share error:', error);\n }\n }\n\n /** Observable of the current screen share status. */\n public get screenShareStatus$(): Observable<ScreenShareStatus> {\n return this.vertoManager.screenShareStatus$;\n }\n\n /** Current screen share status. */\n public get screenShareStatus(): ScreenShareStatus {\n return this.vertoManager.screenShareStatus;\n }\n\n /** Stops the current screen share. */\n public async stopScreenShare(): Promise<void> {\n return this.vertoManager.removeScreenMedia();\n }\n\n /** Adds an additional media input device to the call. */\n public async addAdditionalDevice(options: MediaOptions): Promise<void> {\n try {\n await this.vertoManager.addInputDevice(options);\n } catch (error) {\n logger.error('[Participant.startScreenShare] Screen share error:', error);\n }\n }\n\n /** Removes an additional media input device by ID. */\n public async removeAdditionalDevice(id: string): Promise<void> {\n return this.vertoManager.removeInputDevices(id);\n }\n\n /** Adds or replaces the primary audio input device with optional constraints or stream. */\n public async addAudioInputDevice({\n constraints,\n stream\n }: {\n constraints?: MediaTrackConstraints;\n stream?: MediaStream;\n } = {}): Promise<void> {\n const audio = (constraints ?? stream) ? undefined : true;\n return this.vertoManager.addMainInputDevices({\n audio,\n inputAudioDeviceConstraints: constraints,\n inputAudioStream: stream\n });\n }\n\n /** Adds or replaces the primary video input device with optional constraints or stream. */\n public async addVideoInputDevice({\n constraints,\n stream\n }: {\n constraints?: MediaTrackConstraints;\n stream?: MediaStream;\n } = {}): Promise<void> {\n const video = (constraints ?? stream) ? undefined : true;\n return this.vertoManager.addMainInputDevices({\n video,\n inputVideoDeviceConstraints: constraints,\n inputVideoStream: stream\n });\n }\n\n /** Adds or replaces primary input devices (audio and/or video). */\n public async addInputDevices(options: MediaOptions = {}): Promise<void> {\n await this.vertoManager.addMainInputDevices(options);\n }\n\n /** Selects the audio input device for future calls. Optionally saves as a preference. */\n public selectAudioInputDevice(device: MediaDeviceInfo, options: SelectDeviceOptions = {}): void {\n this.deviceController.selectAudioInputDevice(device);\n if (options.savePreference) {\n PreferencesContainer.instance.preferredAudioInput = device;\n }\n }\n\n /** Updates the audio input track constraints for the active call. */\n public async setAudioInputDeviceConstraints(constraints: MediaTrackConstraints): Promise<void> {\n await this.vertoManager.updateMediaConstraints({ audio: constraints });\n }\n\n /** Updates both audio and video input track constraints for the active call. */\n public async setInputDevicesConstraints(constraints: {\n audio: MediaTrackConstraints;\n video: MediaTrackConstraints;\n }): Promise<void> {\n await this.vertoManager.updateMediaConstraints(constraints);\n }\n\n /** Selects the video input device for future calls. Optionally saves as a preference. */\n public selectVideoInputDevice(device: MediaDeviceInfo, options: SelectDeviceOptions = {}): void {\n this.deviceController.selectVideoInputDevice(device);\n if (options.savePreference) {\n PreferencesContainer.instance.preferredVideoInput = device;\n }\n }\n\n /** Updates the video input track constraints for the active call. */\n public async setVideoInputDeviceConstraints(constraints: MediaTrackConstraints): Promise<void> {\n await this.vertoManager.updateMediaConstraints({ video: constraints });\n }\n\n /** Selects the audio output device. Optionally saves as a preference. */\n public selectAudioOutputDevice(device: MediaDeviceInfo, options: SelectDeviceOptions = {}): void {\n this.deviceController.selectAudioOutputDevice(device);\n if (options.savePreference) {\n PreferencesContainer.instance.preferredAudioOutput = device;\n }\n }\n\n /**\n * Exits studio audio mode without restoring defaults.\n * Called internally before individual audio flag toggles.\n */\n private exitStudioModeIfActive(): void {\n if (this._studioAudio$.value) {\n logger.debug('[SelfParticipant] Exiting studio audio mode due to individual flag toggle');\n this._studioAudio$.next(false);\n }\n }\n\n /** Toggles echo cancellation. Exits studio mode if active. */\n public override async toggleEchoCancellation(): Promise<void> {\n this.exitStudioModeIfActive();\n await super.toggleEchoCancellation();\n }\n\n /** Toggles automatic gain control. Exits studio mode if active. */\n public override async toggleAudioInputAutoGain(): Promise<void> {\n this.exitStudioModeIfActive();\n await super.toggleAudioInputAutoGain();\n }\n\n /** Toggles noise suppression. Exits studio mode if active. */\n public override async toggleNoiseSuppression(): Promise<void> {\n this.exitStudioModeIfActive();\n await super.toggleNoiseSuppression();\n }\n\n /** Mutes local audio. Falls back to local device mute if the server RPC fails. */\n public async mute(): Promise<void> {\n try {\n await super.mute();\n } catch (error) {\n logger.warn(\n '[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway',\n error\n );\n } finally {\n this.vertoManager.muteMainAudioInputDevice();\n }\n }\n\n /** Unmutes local audio. Falls back to local device unmute if the server RPC fails. */\n public async unmute(): Promise<void> {\n try {\n await super.unmute();\n } catch (error) {\n logger.warn(\n '[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway',\n error\n );\n } finally {\n await this.vertoManager.unmuteMainAudioInputDevice();\n }\n }\n\n /** Mutes local video. Falls back to local device mute if the server RPC fails. */\n public async muteVideo(): Promise<void> {\n try {\n await super.muteVideo();\n } catch (error) {\n logger.warn(\n '[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway',\n error\n );\n } finally {\n this.vertoManager.muteMainVideoInputDevice();\n }\n }\n\n /** Unmutes local video. Falls back to local device unmute if the server RPC fails. */\n public async unmuteVideo(): Promise<void> {\n try {\n await super.unmuteVideo();\n } catch (error) {\n logger.warn(\n '[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway',\n error\n );\n } finally {\n await this.vertoManager.unmuteMainVideoInputDevice();\n }\n }\n}\n\n/** Type guard that checks if a participant is the local {@link SelfParticipant}. */\nexport const isSelfParticipant = (participant: Participant): participant is SelfParticipant => {\n return participant instanceof SelfParticipant;\n};\n","// =============================================================================\n// BASE TYPE GUARDS\n// =============================================================================\n// This file contains type guards for base JSON-RPC types.\n\nimport type { JSONRPCErrorResponse, JSONRPCRequest, JSONRPCResponse } from '../types/base';\n\n// =============================================================================\n// TYPE GUARD HELPERS\n// =============================================================================\n\nexport function isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nexport function hasProperty<T extends object, K extends string>(\n obj: T,\n key: K\n): obj is T & Record<K, unknown> {\n return key in obj;\n}\n\nexport function hasStringProperty<T extends object, K extends string>(\n obj: T,\n key: K\n): obj is T & Record<K, string> {\n return key in obj && typeof (obj as Record<string, unknown>)[key] === 'string';\n}\n\n// =============================================================================\n// BASE TYPE GUARDS\n// =============================================================================\n\nexport function isJSONRPCRequest(value: unknown): value is JSONRPCRequest {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'id') &&\n typeof value.id === 'string' &&\n hasProperty(value, 'method') &&\n typeof value.method === 'string'\n );\n}\n\nexport function isJSONRPCResponse(value: unknown): value is JSONRPCResponse {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'id') &&\n typeof value.id === 'string' &&\n (hasProperty(value, 'result') || hasProperty(value, 'error'))\n );\n}\n\nexport function isJSONRPCErrorResponse(value: unknown): value is JSONRPCErrorResponse {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'id') &&\n typeof value.id === 'string' &&\n // Standard JSON-RPC error: has error field\n ((hasProperty(value, 'error') &&\n isObject(value.error) &&\n hasProperty(value.error, 'code') &&\n hasProperty(value.error, 'message')) ||\n (hasProperty(value, 'result') &&\n isObject(value.result) &&\n hasProperty(value.result, 'code') &&\n value.result.code !== '200' &&\n hasProperty(value.result, 'message')))\n );\n}\n","// =============================================================================\n// SIGNALWIRE EVENT TYPE GUARDS\n// =============================================================================\n// This file contains type guards for SignalWire event types.\n\n/** @internal @packageDocumentation */\n\nimport { hasProperty, isJSONRPCRequest, isObject } from './base.guards';\n\nimport type { TypeGuard, ExtractParams } from '../types/base';\nimport type {\n CallConnectPayload,\n CallJoinedPayload,\n CallJoinedRequest,\n CallLeftPayload,\n CallLeftRequest,\n CallPlayPayload,\n CallPlayRequest,\n CallStatePayload,\n CallStateRequest,\n CallUpdatedPayload,\n CallUpdatedRequest,\n CallConnectRequest,\n ConversationMessagePayload,\n ConversationMessageRequest,\n ConversationMessageUpdatedPayload,\n ConversationMessageUpdatedRequest,\n LayoutChangedPayload,\n LayoutChangedRequest,\n MemberJoinedPayload,\n MemberJoinedRequest,\n MemberLeftPayload,\n MemberLeftRequest,\n MemberTalkingPayload,\n MemberTalkingRequest,\n MemberUpdatedPayload,\n MemberUpdatedRequest,\n RoomUpdatedPayload,\n RoomUpdatedRequest,\n SignalwireAuthorizationStatePayload,\n SignalwireAuthorizationStateRequest,\n SignalwireCallMetadata,\n SignalwireCallRequest,\n SignalwireEventRequestBase,\n SignalwireMetadata,\n SignalwireRequest,\n WebrtcMessagePayload,\n WebrtcMessageRequest\n} from '../types/events';\n\n// =============================================================================\n// TYPE GUARD FACTORY SYSTEM\n// =============================================================================\n\n/**\n * Registry of all SignalWire event types.\n * This is the single source of truth for event type strings.\n */\n// eslint-disable-next-line unused-imports/no-unused-vars\nconst EVENT_TYPE_REGISTRY = {\n 'signalwire.authorization.state': true,\n 'webrtc.message': true,\n 'call.joined': true,\n 'call.left': true,\n 'call.updated': true,\n 'call.state': true,\n 'call.play': true,\n 'call.connect': true,\n 'room.updated': true,\n 'member.updated': true,\n 'member.joined': true,\n 'member.left': true,\n 'member.talking': true,\n 'layout.changed': true,\n 'conversation.message': true,\n 'conversation.message.updated': true\n} as const;\n\nexport type RegisteredEventType = keyof typeof EVENT_TYPE_REGISTRY;\n\n/**\n * Factory function to create Request-level type guards.\n */\nexport function createEventRequestGuard<T extends SignalwireEventRequestBase>(\n eventType: RegisteredEventType\n): TypeGuard<T> {\n return (value: unknown): value is T =>\n isSignalwireRequest(value) && value.params.event_type === eventType;\n}\n\n/**\n * Factory function to create Metadata-level type guards.\n */\nexport function createEventMetadataGuard<T extends SignalwireMetadata>(\n eventType: RegisteredEventType\n): TypeGuard<T> {\n return (value: unknown): value is T =>\n isSignalwireMetadata(value) && value.event_type === eventType;\n}\n\n// =============================================================================\n// SIGNALWIRE EVENT TYPE GUARDS\n// =============================================================================\n\nexport function isSignalwireRequest(value: unknown): value is SignalwireRequest {\n return (\n isJSONRPCRequest(value) &&\n value.method === 'signalwire.event' &&\n isObject(value.params) &&\n hasProperty(value.params, 'event_type')\n );\n}\n\nexport function isSignalwireCallRequest(value: unknown): value is SignalwireCallRequest {\n return (\n isSignalwireRequest(value) &&\n (isCallJoinedRequest(value) ||\n isCallLeftRequest(value) ||\n isCallUpdatedRequest(value) ||\n isCallStateRequest(value) ||\n isCallPlayRequest(value) ||\n isCallConnectRequest(value) ||\n isRoomUpdatedRequest(value) ||\n isMemberUpdatedRequest(value) ||\n isMemberJoinedRequest(value) ||\n isMemberLeftRequest(value) ||\n isMemberTalkingRequest(value) ||\n isLayoutChangedRequest(value) ||\n isWebrtcMessageRequest(value) ||\n isConversationMessageRequest(value) ||\n isConversationMessageUpdatedRequest(value))\n );\n}\n\n// Generated Request guards using factory pattern\nexport const isSignalwireAuthorizationStateRequest =\n createEventRequestGuard<SignalwireAuthorizationStateRequest>('signalwire.authorization.state');\n\nexport const isWebrtcMessageRequest =\n createEventRequestGuard<WebrtcMessageRequest>('webrtc.message');\n\nexport const isCallJoinedRequest = createEventRequestGuard<CallJoinedRequest>('call.joined');\n\nexport const isCallLeftRequest = createEventRequestGuard<CallLeftRequest>('call.left');\n\nexport const isCallUpdatedRequest = createEventRequestGuard<CallUpdatedRequest>('call.updated');\n\nexport const isCallStateRequest = createEventRequestGuard<CallStateRequest>('call.state');\n\nexport const isCallPlayRequest = createEventRequestGuard<CallPlayRequest>('call.play');\n\nexport const isCallConnectRequest = createEventRequestGuard<CallConnectRequest>('call.connect');\n\nexport const isRoomUpdatedRequest = createEventRequestGuard<RoomUpdatedRequest>('room.updated');\n\nexport const isMemberUpdatedRequest =\n createEventRequestGuard<MemberUpdatedRequest>('member.updated');\n\nexport const isMemberJoinedRequest = createEventRequestGuard<MemberJoinedRequest>('member.joined');\n\nexport const isMemberLeftRequest = createEventRequestGuard<MemberLeftRequest>('member.left');\n\nexport const isMemberTalkingRequest =\n createEventRequestGuard<MemberTalkingRequest>('member.talking');\n\nexport const isLayoutChangedRequest =\n createEventRequestGuard<LayoutChangedRequest>('layout.changed');\n\nexport const isConversationMessageRequest =\n createEventRequestGuard<ConversationMessageRequest>('conversation.message');\n\nexport const isConversationMessageUpdatedRequest =\n createEventRequestGuard<ConversationMessageUpdatedRequest>('conversation.message.updated');\n\n// =============================================================================\n// SIGNALWIRE EVENT METADATA TYPE GUARDS (outer wrapper with event_type)\n// =============================================================================\n\nexport function isSignalwireMetadata(value: unknown): value is SignalwireMetadata {\n return (\n isObject(value) &&\n hasProperty(value, 'event_type') &&\n typeof value.event_type === 'string' &&\n hasProperty(value, 'params')\n );\n}\n\nexport function isSignalwireCallMetadata(value: unknown): value is SignalwireCallMetadata {\n return (\n isSignalwireMetadata(value) &&\n (isCallJoinedMetadata(value) ||\n isCallLeftMetadata(value) ||\n isCallUpdatedMetadata(value) ||\n isCallStateMetadata(value) ||\n isCallPlayMetadata(value) ||\n isCallConnectMetadata(value) ||\n isRoomUpdatedMetadata(value) ||\n isMemberUpdatedMetadata(value) ||\n isMemberJoinedMetadata(value) ||\n isMemberLeftMetadata(value) ||\n isMemberTalkingMetadata(value) ||\n isLayoutChangedMetadata(value) ||\n isWebrtcMessageMetadata(value) ||\n isConversationMessageMetadata(value) ||\n isConversationMessageUpdatedMetadata(value))\n );\n}\n\n// Generated Metadata guards using factory pattern\nexport const isSignalwireAuthorizationStateMetadata = createEventMetadataGuard<\n ExtractParams<SignalwireAuthorizationStateRequest>\n>('signalwire.authorization.state');\n\nexport const isWebrtcMessageMetadata =\n createEventMetadataGuard<ExtractParams<WebrtcMessageRequest>>('webrtc.message');\n\nexport const isCallJoinedMetadata =\n createEventMetadataGuard<ExtractParams<CallJoinedRequest>>('call.joined');\n\nexport const isCallLeftMetadata =\n createEventMetadataGuard<ExtractParams<CallLeftRequest>>('call.left');\n\nexport const isCallUpdatedMetadata =\n createEventMetadataGuard<ExtractParams<CallUpdatedRequest>>('call.updated');\n\nexport const isCallStateMetadata =\n createEventMetadataGuard<ExtractParams<CallStateRequest>>('call.state');\n\nexport const isCallPlayMetadata =\n createEventMetadataGuard<ExtractParams<CallPlayRequest>>('call.play');\n\nexport const isCallConnectMetadata =\n createEventMetadataGuard<ExtractParams<CallConnectRequest>>('call.connect');\n\nexport const isRoomUpdatedMetadata =\n createEventMetadataGuard<ExtractParams<RoomUpdatedRequest>>('room.updated');\n\nexport const isMemberUpdatedMetadata =\n createEventMetadataGuard<ExtractParams<MemberUpdatedRequest>>('member.updated');\n\nexport const isMemberJoinedMetadata =\n createEventMetadataGuard<ExtractParams<MemberJoinedRequest>>('member.joined');\n\nexport const isMemberLeftMetadata =\n createEventMetadataGuard<ExtractParams<MemberLeftRequest>>('member.left');\n\nexport const isMemberTalkingMetadata =\n createEventMetadataGuard<ExtractParams<MemberTalkingRequest>>('member.talking');\n\nexport const isLayoutChangedMetadata =\n createEventMetadataGuard<ExtractParams<LayoutChangedRequest>>('layout.changed');\n\nexport const isConversationMessageMetadata =\n createEventMetadataGuard<ExtractParams<ConversationMessageRequest>>('conversation.message');\n\nexport const isConversationMessageUpdatedMetadata = createEventMetadataGuard<\n ExtractParams<ConversationMessageUpdatedRequest>\n>('conversation.message.updated');\n\n// =============================================================================\n// EVENT PAYLOAD TYPE GUARDS\n// =============================================================================\n\nexport function isSignalwireAuthorizationStatePayload(\n value: unknown\n): value is SignalwireAuthorizationStatePayload {\n return isObject(value) && hasProperty(value, 'authorization_state');\n}\n\nexport function isCallJoinedPayload(value: unknown): value is CallJoinedPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'room_session') &&\n hasProperty(value, 'call_id') &&\n hasProperty(value, 'member_id') &&\n hasProperty(value, 'capabilities')\n );\n}\n\nexport function isCallLeftPayload(value: unknown): value is CallLeftPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'room_session') &&\n hasProperty(value, 'call_id') &&\n hasProperty(value, 'reason')\n );\n}\n\n/**\n * Payload-level guard for MemberUpdatedPayload.\n *\n * NOTE: This guard uses negative checks (`!hasProperty(value, 'reason')` and\n * `!hasProperty(member, 'talking')`) to distinguish from MemberLeftPayload and\n * MemberTalkingPayload, which share the same base shape (member, room_id,\n * room_session_id). These negative checks are fragile — if MemberUpdatedPayload\n * ever gains a `reason` or `talking` field, this guard will break.\n *\n * Prefer the metadata-level guard `isMemberUpdatedMetadata` which checks\n * `event_type === 'member.updated'` and is not susceptible to payload shape\n * overlap.\n */\nexport function isMemberUpdatedPayload(value: unknown): value is MemberUpdatedPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'member') &&\n hasProperty(value, 'room_id') &&\n hasProperty(value, 'room_session_id') &&\n !hasProperty(value, 'reason') &&\n !hasProperty((value as MemberUpdatedPayload).member, 'talking')\n );\n}\n\nexport function isMemberJoinedPayload(value: unknown): value is MemberJoinedPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'member') &&\n hasProperty(value, 'room_id') &&\n !hasProperty(value, 'reason')\n );\n}\n\nexport function isMemberLeftPayload(value: unknown): value is MemberLeftPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'member') &&\n hasProperty(value, 'room_id') &&\n hasProperty(value, 'reason')\n );\n}\n\nexport function isMemberTalkingPayload(value: unknown): value is MemberTalkingPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'member') &&\n hasProperty(value, 'room_id') &&\n hasProperty(value, 'room_session_id') &&\n !hasProperty(value, 'reason') &&\n hasProperty((value as MemberTalkingPayload).member, 'talking')\n );\n}\n\nexport function isLayoutChangedPayload(value: unknown): value is LayoutChangedPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'room_id') &&\n hasProperty(value, 'room_session_id') &&\n hasProperty(value, 'layout')\n );\n}\n\nexport function isCallUpdatedPayload(value: unknown): value is CallUpdatedPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'room_session') &&\n hasProperty(value, 'room_id') &&\n hasProperty(value, 'room_session_id')\n );\n}\n\n/**\n * Payload-level guard for RoomUpdatedPayload.\n *\n * NOTE: RoomUpdatedPayload is structurally identical to CallUpdatedPayload\n * (both have `room_session`, `room_id`, `room_session_id`). This guard cannot\n * distinguish between them at the payload level.\n *\n * Prefer the metadata-level guard `isRoomUpdatedMetadata` which checks\n * `event_type === 'room.updated'` and is not susceptible to payload shape overlap.\n */\nexport function isRoomUpdatedPayload(value: unknown): value is RoomUpdatedPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'room_session') &&\n hasProperty(value, 'room_id') &&\n hasProperty(value, 'room_session_id')\n );\n}\n\nexport function isCallStatePayload(value: unknown): value is CallStatePayload {\n return (\n isObject(value) &&\n hasProperty(value, 'call_id') &&\n hasProperty(value, 'call_state') &&\n hasProperty(value, 'direction')\n );\n}\n\nexport function isCallPlayPayload(value: unknown): value is CallPlayPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'control_id') &&\n hasProperty(value, 'call_id') &&\n hasProperty(value, 'state')\n );\n}\n\nexport function isCallConnectPayload(value: unknown): value is CallConnectPayload {\n return (\n isObject(value) &&\n hasProperty(value, 'connect_state') &&\n hasProperty(value, 'call_id') &&\n hasProperty(value, 'segment_id')\n );\n}\n\nexport function isConversationMessagePayload(value: unknown): value is ConversationMessagePayload {\n return (\n isObject(value) &&\n hasProperty(value, 'id') &&\n hasProperty(value, 'type') &&\n hasProperty(value, 'kind') &&\n hasProperty(value, 'conversation_name')\n );\n}\n\nexport function isConversationMessageUpdatedPayload(\n value: unknown\n): value is ConversationMessageUpdatedPayload {\n return isConversationMessagePayload(value);\n}\n\nexport function isWebrtcMessageEventInnerParams(value: unknown): value is WebrtcMessagePayload {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'id') &&\n hasProperty(value, 'method') &&\n typeof value.method === 'string'\n );\n}\n\n// =============================================================================\n// EVENT TYPE MAPPING\n// =============================================================================\n\nexport const EventTypeMap = {\n 'signalwire.authorization.state': isSignalwireAuthorizationStateRequest,\n 'webrtc.message': isWebrtcMessageRequest,\n 'call.joined': isCallJoinedRequest,\n 'call.left': isCallLeftRequest,\n 'call.updated': isCallUpdatedRequest,\n 'call.state': isCallStateRequest,\n 'call.play': isCallPlayRequest,\n 'call.connect': isCallConnectRequest,\n 'room.updated': isRoomUpdatedRequest,\n 'member.updated': isMemberUpdatedRequest,\n 'member.joined': isMemberJoinedRequest,\n 'member.left': isMemberLeftRequest,\n 'member.talking': isMemberTalkingRequest,\n 'layout.changed': isLayoutChangedRequest,\n 'conversation.message': isConversationMessageRequest,\n 'conversation.message.updated': isConversationMessageUpdatedRequest\n} as const;\n\nexport type EventType = keyof typeof EventTypeMap;\n\n/**\n * Gets the appropriate type guard for an event type.\n */\nexport function getEventGuard(eventType: string): TypeGuard<SignalwireRequest> | undefined {\n return EventTypeMap[eventType as EventType];\n}\n\n// =============================================================================\n// EVENT METADATA TYPE MAPPING\n// =============================================================================\n\nexport const EventMetadataTypeMap = {\n 'signalwire.authorization.state': isSignalwireAuthorizationStateMetadata,\n 'webrtc.message': isWebrtcMessageMetadata,\n 'call.joined': isCallJoinedMetadata,\n 'call.left': isCallLeftMetadata,\n 'call.updated': isCallUpdatedMetadata,\n 'call.state': isCallStateMetadata,\n 'call.play': isCallPlayMetadata,\n 'call.connect': isCallConnectMetadata,\n 'room.updated': isRoomUpdatedMetadata,\n 'member.updated': isMemberUpdatedMetadata,\n 'member.joined': isMemberJoinedMetadata,\n 'member.left': isMemberLeftMetadata,\n 'member.talking': isMemberTalkingMetadata,\n 'layout.changed': isLayoutChangedMetadata,\n 'conversation.message': isConversationMessageMetadata,\n 'conversation.message.updated': isConversationMessageUpdatedMetadata\n} as const;\n\n/**\n * Gets the appropriate metadata type guard for an event type.\n */\nexport function getEventMetadataGuard(\n eventType: string\n): TypeGuard<SignalwireMetadata> | undefined {\n return EventMetadataTypeMap[eventType as EventType];\n}\n","import { distinctUntilChanged, filter, map, merge, tap } from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { isSelfParticipant } from '../core/entities/Participant';\nimport {\n isCallJoinedPayload,\n isLayoutChangedPayload\n} from '../core/RPCMessages/guards/events.guards';\nimport { filterAs } from '../operators';\nimport { filterNull } from '../operators/filterNull';\nimport { getLogger } from '../utils/logger';\n\nimport type { Participant, SelfParticipant } from '../core/entities/Participant';\nimport type {\n CallManager,\n CallParticipant,\n CallSelfParticipant\n} from '../core/entities/types/call.types';\nimport type {\n LayoutLayer,\n Member,\n RoomSession,\n Layout,\n MemberTalkingInfo\n} from '../core/RPCMessages/types/common';\nimport type { CallLayoutListResponse } from '../core/RPCMessages/types/methods';\nimport type { Capability } from '../core/types/call.types';\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\ntype SessionState = RoomSession & { capabilities: Capability[] } & {\n layouts: string[];\n} & { layout_layers: LayoutLayer[] };\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface WebRTCCallEventManagerOptions {}\n\nconst initialSessionState: Partial<SessionState> = {};\n\n/** @internal */\nexport class CallEventsManager extends Destroyable {\n private selfId?: string;\n private originCallId?: string;\n private callIds = new Set<string>();\n private roomSessionIds = new Set<string>();\n private _participants$ = this.createBehaviorSubject<Record<string, Participant>>({});\n\n private _self$ = this.createBehaviorSubject<SelfParticipant | null>(null);\n private _sessionState$ = this.createBehaviorSubject<Partial<SessionState>>(initialSessionState);\n\n constructor(\n protected webRtcCallSession: CallManager,\n protected options: WebRTCCallEventManagerOptions = {}\n ) {\n super();\n this.initSubscriptions();\n }\n public get participants$(): Observable<CallParticipant[]> {\n return this.cachedObservable('participants$', () =>\n this._participants$\n .asObservable()\n .pipe(map((participantsRecord) => Object.values(participantsRecord)))\n );\n }\n\n public get participants(): CallParticipant[] {\n return Object.values(this._participants$.value);\n }\n\n public get self$(): Observable<CallSelfParticipant> {\n return this.cachedObservable('self$', () => this._self$.asObservable().pipe(filterNull()));\n }\n\n // check if this call session has joined that same room session\n public isRoomSessionIdValid(roomSessionId: string): boolean {\n return this.roomSessionIds.has(roomSessionId);\n }\n\n public addCallId(callId: string): void {\n this.callIds.add(callId);\n }\n\n public isCallIdValid(callId: string): boolean {\n return this.callIds.has(callId);\n }\n\n public get recording$(): Observable<boolean> {\n return this.cachedObservable('recording$', () =>\n this._sessionState$.pipe(\n map((state) => state.recording),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get recordings$(): Observable<Record<string, unknown>[]> {\n return this.cachedObservable('recordings$', () =>\n this._sessionState$.pipe(\n map((state) => state.recordings),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get streaming$(): Observable<boolean> {\n return this.cachedObservable('streaming$', () =>\n this._sessionState$.pipe(\n map((state) => state.streaming),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get streams$(): Observable<Record<string, unknown>[]> {\n return this.cachedObservable('streams$', () =>\n this._sessionState$.pipe(\n map((state) => state.streams),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get playbacks$(): Observable<Record<string, unknown>[]> {\n return this.cachedObservable('playbacks$', () =>\n this._sessionState$.pipe(\n map((state) => state.playbacks),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get raiseHandPriority$(): Observable<boolean> {\n return this.cachedObservable('raiseHandPriority$', () =>\n this._sessionState$.pipe(\n map((state) => state.prioritize_handraise),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get locked$(): Observable<boolean> {\n return this.cachedObservable('locked$', () =>\n this._sessionState$.pipe(\n map((state) => state.locked),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get meta$(): Observable<Record<string, unknown>> {\n return this.cachedObservable('meta$', () =>\n this._sessionState$.pipe(\n map((state) => state.meta),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get capabilities$(): Observable<Capability[]> {\n return this.cachedObservable('capabilities$', () =>\n this._sessionState$.pipe(\n map((state) => state.capabilities),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get layout$(): Observable<string> {\n return this.cachedObservable('layout$', () =>\n this._sessionState$.pipe(\n map((state) => state.layout_name),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get layouts$(): Observable<string[]> {\n return this.cachedObservable('layouts$', () =>\n this._sessionState$.pipe(\n map((state) => state.layouts),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get layoutLayers$(): Observable<LayoutLayer[]> {\n return this.cachedObservable('layoutLayers$', () =>\n this._sessionState$.pipe(\n map((state) => state.layout_layers),\n distinctUntilChanged(),\n filterNull()\n )\n );\n }\n\n public get self(): CallSelfParticipant | null {\n return this._self$.value;\n }\n\n public get layoutLayers(): LayoutLayer[] {\n return this._sessionState$.value.layout_layers ?? [];\n }\n\n public get recording(): boolean {\n return this._sessionState$.value.recording ?? false;\n }\n\n public get streaming(): boolean {\n return this._sessionState$.value.streaming ?? false;\n }\n\n public get raiseHandPriority(): boolean {\n return this._sessionState$.value.prioritize_handraise ?? false;\n }\n\n public get locked(): boolean {\n return this._sessionState$.value.locked ?? false;\n }\n\n public get meta(): Record<string, unknown> {\n return this._sessionState$.value.meta ?? {};\n }\n\n public get layout(): string | undefined {\n return this._sessionState$.value.layout_name;\n }\n\n public get layouts(): string[] {\n return this._sessionState$.value.layouts ?? [];\n }\n\n public get capabilities(): Capability[] {\n return this._sessionState$.value.capabilities ?? [];\n }\n\n public isSessionEvent(id: string): boolean {\n return this.callIds.has(id) || this.roomSessionIds.has(id);\n }\n\n protected initSubscriptions(): void {\n this.subscribeTo(this.callJoinedEvent$, (callJoinedEvent) => {\n logger.debug('[CallEventsManager] Handling call.joined event for call/session IDs:', {\n callId: callJoinedEvent.call_id,\n roomSessionId: callJoinedEvent.room_session_id\n });\n const sessionState = callJoinedEvent.room_session;\n const capabilities = callJoinedEvent.capabilities as Capability[];\n // Don't update selfId and originCallId from nested call.joined events\n this.selfId = this.selfId ?? callJoinedEvent.member_id;\n this.originCallId = this.originCallId ?? callJoinedEvent.origin_call_id;\n this.callIds.add(callJoinedEvent.call_id);\n this.roomSessionIds.add(callJoinedEvent.room_session_id);\n\n this._sessionState$.next({\n ...this._sessionState$.value,\n recording: sessionState.recording,\n recordings: sessionState.recordings,\n streaming: sessionState.streaming,\n streams: sessionState.streams,\n playbacks: sessionState.playbacks,\n\n prioritize_handraise: sessionState.prioritize_handraise,\n locked: sessionState.locked,\n meta: sessionState.meta,\n\n capabilities\n });\n\n this.updateParticipants(sessionState.members);\n\n // Update capabilities on the self participant\n // This must happen after updateParticipants to ensure self exists\n this._self$.value?.capabilities.updateFromRaw(capabilities);\n\n if (this._self$.value?.capabilities.setLayout) {\n this.updateLayouts();\n }\n });\n this.subscribeTo(this.memberUpdates$, (member) => {\n logger.debug('[CallEventsManager] Handling member update event for member ID:', member);\n this.upsertParticipant(member);\n });\n this.subscribeTo(this.webRtcCallSession.memberLeft$, (memberLeftEvent) => {\n logger.debug(\n '[CallEventsManager] Handling member.left event for member ID:',\n memberLeftEvent.member.member_id\n );\n const participants = { ...this._participants$.value };\n if (memberLeftEvent.member.member_id in participants) {\n delete participants[memberLeftEvent.member.member_id];\n // in case in subscription is observing the participants collection\n this._participants$.next(participants);\n } else {\n logger.warn(\n `[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`\n );\n }\n });\n this.subscribeTo(this.webRtcCallSession.callUpdated$, (callUpdatedEvent) => {\n logger.debug('[CallEventsManager] Handling call.updated event:', callUpdatedEvent);\n const roomSession = callUpdatedEvent.room_session;\n\n this._sessionState$.next({\n ...this._sessionState$.value,\n recording: roomSession.recording,\n recordings: roomSession.recordings,\n streaming: roomSession.streaming,\n streams: roomSession.streams,\n playbacks: roomSession.playbacks,\n prioritize_handraise: roomSession.prioritize_handraise,\n locked: roomSession.locked,\n meta: roomSession.meta\n });\n });\n this.subscribeTo(this.layoutChangedEvent$, (layoutChangedEvent) => {\n logger.debug('[CallEventsManager] Handling layout.changed event:', layoutChangedEvent);\n this._sessionState$.next({\n ...this._sessionState$.value,\n\n layout_name: layoutChangedEvent.id,\n\n layout_layers: layoutChangedEvent.layers\n });\n\n this.updateParticipantPositions(layoutChangedEvent);\n });\n }\n private updateParticipantPositions(layoutChangedEvent: Layout) {\n if (\n Object.keys(this._participants$.value).length > 0 &&\n !layoutChangedEvent.layers.some((layer) => !!layer.member_id)\n ) {\n logger.warn(\n '[CallEventsManager] No layers with member_id found in layout.changed event. Nothing to update.'\n );\n }\n layoutChangedEvent.layers\n .filter((layer): layer is LayoutLayer & { member_id: string } => !!layer.member_id)\n .filter((layer) => {\n if (!(layer.member_id in this._participants$.value)) {\n logger.warn(\n `[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`\n );\n return false;\n }\n return true;\n })\n .map((layer) => {\n // update participant state with new position\n this._participants$.value[layer.member_id].upnext({\n position: layer\n });\n return this._participants$.value[layer.member_id];\n })\n .forEach((participant) => {\n if (isSelfParticipant(participant)) {\n this._self$.next(participant);\n }\n // update the collection observable to notify changes\n this._participants$.next({\n ...this._participants$.value,\n [participant.id]: participant\n });\n });\n }\n\n updateLayouts(): void {\n if (!this.selfId) return;\n\n this.webRtcCallSession\n .executeMethod<CallLayoutListResponse>(this.selfId, 'call.layout.list', {})\n .then((response) => {\n this._sessionState$.next({\n ...this._sessionState$.value,\n layouts: response.result.layouts\n });\n })\n .catch((error) => {\n logger.error('[CallEventsManager] Error fetching layouts:', error);\n });\n }\n\n private updateParticipants(members: Member[]) {\n members.forEach((member) => this.upsertParticipant(member));\n }\n\n private upsertParticipant(member: Member | MemberTalkingInfo) {\n if (!(member.member_id in this._participants$.value)) {\n // Pass selfId from call.joined event to ensure correct self participant identification\n const newParticipant = this.webRtcCallSession.createParticipant(\n member.member_id,\n this.selfId\n );\n\n this._participants$.next({\n ...this._participants$.value,\n [member.member_id]: newParticipant\n });\n }\n\n const participant = this._participants$.value[member.member_id];\n\n const oldValue = participant.value;\n logger.debug('[CallEventsManager] Updating participant:', member.member_id, {\n oldValue,\n newValue: member\n });\n participant.upnext({\n ...oldValue,\n ...member\n });\n\n if (isSelfParticipant(participant)) {\n this._self$.next(participant);\n }\n // in case in subscription is observing the participants collection\n this._participants$.next(this._participants$.value);\n }\n\n private get callJoinedEvent$() {\n return this.cachedObservable('callJoinedEvent$', () =>\n this.webRtcCallSession.callEvent$.pipe(\n filter(isCallJoinedPayload),\n tap((event) => {\n logger.debug('[CallEventsManager] Call joined event:', event);\n })\n )\n );\n }\n\n private get layoutChangedEvent$() {\n return this.cachedObservable('layoutChangedEvent$', () =>\n this.webRtcCallSession.callEvent$.pipe(\n filterAs(isLayoutChangedPayload, 'layout'),\n tap((event) => {\n logger.debug('[CallEventsManager] Layout changed event:', event);\n })\n )\n );\n }\n\n private get memberUpdates$() {\n return this.cachedObservable('memberUpdates$', () =>\n merge(\n this.webRtcCallSession.memberJoined$,\n this.webRtcCallSession.memberUpdated$,\n this.webRtcCallSession.memberTalking$\n ).pipe(\n map((event) => event.member),\n tap((event) => {\n logger.debug('[CallEventsManager] Member update event:', event);\n })\n )\n );\n }\n\n public override destroy(): void {\n const participants = Object.values(this._participants$.value);\n participants.forEach((participant) => {\n participant.destroy();\n });\n this._participants$.next({});\n this._self$.next(null);\n\n this.callIds.clear();\n this.roomSessionIds.clear();\n this.selfId = undefined;\n this.originCallId = undefined;\n //@ts-expect-error -- avoiding circular references\n this.webRtcCallSession = undefined;\n //@ts-expect-error -- avoiding circular references\n this.callSession = undefined;\n\n super.destroy();\n }\n}\n","/**\n * SDPHelper - Utility functions for SDP (Session Description Protocol) parsing and validation.\n *\n * This module provides helper functions to analyze and validate SDP content,\n * particularly for ICE candidate validation in WebRTC connections.\n */\n\nimport { DEFAULT_STEREO_MAX_AVERAGE_BITRATE } from '../core/constants';\n\nimport type { MediaDirections, MediaDirection } from '../core/types/media.types';\n\n/** Valid SDP direction attribute values. */\nconst SDP_DIRECTIONS: ReadonlySet<string> = new Set([\n 'sendrecv',\n 'sendonly',\n 'recvonly',\n 'inactive'\n]);\n\n/**\n * Extracts the media directions (audio/video) from an SDP string.\n *\n * Parses each media section (`m=audio` / `m=video`) and reads the `a=` direction\n * attribute (`sendrecv`, `sendonly`, `recvonly`, `inactive`).\n * If no explicit direction attribute is found for a media section, defaults to `sendrecv`\n * per RFC 4566.\n *\n * @param sdp - The SDP string to parse\n * @returns The extracted audio and video directions\n *\n * @example\n * ```typescript\n * const sdp = `v=0\\r\\nm=audio 9 UDP/TLS/RTP/SAVPF 111\\r\\na=sendrecv\\r\\nm=video 9 UDP/TLS/RTP/SAVPF 96\\r\\na=recvonly`;\n * extractMediaDirectionsFromSDP(sdp);\n * // { audio: 'sendrecv', video: 'recvonly' }\n * ```\n */\nexport function extractMediaDirectionsFromSDP(sdp: string): MediaDirections {\n const result: MediaDirections = {\n audio: 'inactive',\n video: 'inactive'\n };\n\n if (!sdp) {\n return result;\n }\n\n const lines = sdp.split(/\\r?\\n/);\n let currentMediaKind: 'audio' | 'video' | null = null;\n let currentDirection: MediaDirection | null = null;\n\n for (const line of lines) {\n if (line.startsWith('m=')) {\n // Flush direction for previous section\n if (currentMediaKind) {\n result[currentMediaKind] = currentDirection ?? 'sendrecv';\n }\n\n // Determine new media kind\n if (line.startsWith('m=audio')) {\n currentMediaKind = 'audio';\n } else if (line.startsWith('m=video')) {\n currentMediaKind = 'video';\n } else {\n currentMediaKind = null;\n }\n currentDirection = null;\n } else if (currentMediaKind && line.startsWith('a=')) {\n const attr = line.substring(2).trim();\n if (SDP_DIRECTIONS.has(attr)) {\n currentDirection = attr as MediaDirection;\n }\n }\n }\n\n // Flush last section\n if (currentMediaKind) {\n result[currentMediaKind] = currentDirection ?? 'sendrecv';\n }\n\n return result;\n}\n\n/**\n * Validates that an SDP string has at least one non-host ICE candidate\n * for each media section (m= line).\n *\n * Non-host candidates (srflx, prflx, relay) indicate that the SDP has\n * gathered candidates that can be used for connectivity through NAT\n * traversal mechanisms.\n *\n * @param sdp - The SDP string to validate\n * @returns true if the SDP is valid (has non-host candidates for all media sections,\n * or has no media sections), false otherwise\n *\n * @example\n * ```typescript\n * const sdp = `v=0\n * m=audio 9 UDP/TLS/RTP/SAVPF 111\n * a=candidate:1 1 UDP 1694498815 203.0.113.1 50001 typ srflx\n * m=video 9 UDP/TLS/RTP/SAVPF 96\n * a=candidate:2 1 UDP 1694498815 203.0.113.1 50002 typ relay`;\n *\n * isValidLocalDescription(sdp); // returns true\n * ```\n */\nexport function isValidLocalDescription(sdp: string): boolean {\n if (!sdp) {\n return false;\n }\n\n // Parse SDP to find media sections (m= lines)\n const lines = sdp.split('\\r\\n');\n const mediaSectionsValidCandidates: number[] = [];\n let currentSection = -1;\n\n for (const line of lines) {\n if (line.startsWith('m=')) {\n // New media section\n currentSection += 1;\n mediaSectionsValidCandidates[currentSection] = 0;\n } else if (line.startsWith('a=candidate:')) {\n const typeMatch = /\\styp\\s+(host|srflx|prflx|relay)/.exec(line);\n if (typeMatch && typeMatch[1] !== 'host') {\n // count only non-host candidates\n mediaSectionsValidCandidates[currentSection] += 1;\n }\n }\n }\n\n return (\n !mediaSectionsValidCandidates.length ||\n // Check if localDescription has at least one non-host candidate for each media section.\n mediaSectionsValidCandidates.every((count) => count > 0)\n );\n}\n\n// =============================================================================\n// CODEC REORDERING (Section 23)\n// =============================================================================\n\n/**\n * Reorders codec payload types in an SDP m= line to match the preferred order.\n *\n * For each media section (`m=audio` / `m=video`), this function parses the\n * `a=rtpmap` lines to build a payload-type-to-codec-name map, then rewrites\n * the m= line so preferred codecs appear first.\n *\n * Codecs not in the preferred list retain their original relative order after\n * the preferred ones. Preferred codecs that do not appear in the SDP are ignored.\n *\n * @param sdp - The SDP string to modify\n * @param preferredVideo - Preferred video codec names in priority order (e.g., ['VP9', 'VP8'])\n * @param preferredAudio - Preferred audio codec names in priority order (e.g., ['opus'])\n * @returns The modified SDP string with reordered codec payload types\n *\n * @example\n * ```typescript\n * const reordered = reorderCodecs(sdp, ['VP9', 'H264'], ['opus']);\n * ```\n */\nexport function reorderCodecs(\n sdp: string,\n preferredVideo: readonly string[] = [],\n preferredAudio: readonly string[] = []\n): string {\n if (!sdp || (preferredVideo.length === 0 && preferredAudio.length === 0)) {\n return sdp;\n }\n\n const sections = splitIntoMediaSections(sdp);\n const result: string[] = [sections[0]]; // session-level section\n\n for (let i = 1; i < sections.length; i++) {\n const section = sections[i];\n const mLine = section.split(/\\r?\\n/)[0];\n\n if (mLine.startsWith('m=video') && preferredVideo.length > 0) {\n result.push(reorderSectionCodecs(section, preferredVideo));\n } else if (mLine.startsWith('m=audio') && preferredAudio.length > 0) {\n result.push(reorderSectionCodecs(section, preferredAudio));\n } else {\n result.push(section);\n }\n }\n\n return result.join('');\n}\n\n/**\n * Adds stereo=1 and sprop-stereo=1 to the Opus fmtp line in the SDP.\n *\n * This enables stereo Opus negotiation for music/podcast use cases.\n * Also sets maxaveragebitrate to accommodate stereo audio (default: 510000 bps).\n *\n * @param sdp - The SDP string to modify\n * @param maxBitrate - Maximum average bitrate in bps (default: 510000)\n * @returns The modified SDP string with stereo Opus parameters\n *\n * @example\n * ```typescript\n * const stereoSdp = enableStereoOpus(sdp);\n * // a=fmtp:111 minptime=10;useinbandfec=1;stereo=1;sprop-stereo=1;maxaveragebitrate=510000\n * ```\n */\nexport function enableStereoOpus(\n sdp: string,\n maxBitrate: number = DEFAULT_STEREO_MAX_AVERAGE_BITRATE\n): string {\n if (!sdp) {\n return sdp;\n }\n\n // Find the Opus payload type from rtpmap\n const opusPayloadType = findOpusPayloadType(sdp);\n if (opusPayloadType === null) {\n return sdp;\n }\n\n const lines = sdp.split(/\\r?\\n/);\n const updatedLines: string[] = [];\n const fmtpPrefix = `a=fmtp:${opusPayloadType} `;\n\n let foundFmtp = false;\n\n for (const line of lines) {\n if (line.startsWith(fmtpPrefix)) {\n foundFmtp = true;\n updatedLines.push(appendStereoParams(line, maxBitrate));\n } else {\n updatedLines.push(line);\n }\n }\n\n // If no fmtp line exists for Opus, add one after the rtpmap line\n if (!foundFmtp) {\n const rtpmapLine = `a=rtpmap:${opusPayloadType} `;\n const withFmtp: string[] = [];\n for (const line of updatedLines) {\n withFmtp.push(line);\n if (line.startsWith(rtpmapLine)) {\n withFmtp.push(\n `a=fmtp:${opusPayloadType} stereo=1;sprop-stereo=1;maxaveragebitrate=${maxBitrate}`\n );\n }\n }\n return withFmtp.join('\\r\\n');\n }\n\n return updatedLines.join('\\r\\n');\n}\n\n/**\n * Convenience wrapper that applies both codec reordering and stereo Opus.\n *\n * @param sdp - The SDP string to modify\n * @param audioCodecs - Preferred audio codecs in priority order\n * @param videoCodecs - Preferred video codecs in priority order\n * @returns The modified SDP string\n */\nexport function setCodecPreferences(\n sdp: string,\n audioCodecs: readonly string[] = [],\n videoCodecs: readonly string[] = []\n): string {\n return reorderCodecs(sdp, videoCodecs, audioCodecs);\n}\n\n// =============================================================================\n// INTERNAL HELPERS\n// =============================================================================\n\n/**\n * Splits an SDP string into session-level + per-media sections.\n *\n * The first element is the session-level content (before the first m= line).\n * Subsequent elements each start with an m= line.\n */\nfunction splitIntoMediaSections(sdp: string): string[] {\n // Split on the boundary just before each m= line.\n // String.split always returns at least one element, so no fallback needed.\n return sdp.split(/(?=m=)/);\n}\n\n/**\n * Reorders codecs within a single media section based on preferred names.\n */\nfunction reorderSectionCodecs(section: string, preferredNames: readonly string[]): string {\n const lines = section.split(/\\r?\\n/);\n const mLine = lines[0];\n\n // Parse the m= line: m=<kind> <port> <proto> <payload types...>\n const mLineParts = mLine.split(' ');\n if (mLineParts.length < 4) {\n return section;\n }\n\n const payloadTypes = mLineParts.slice(3);\n\n // Build payload-type-to-codec-name map from a=rtpmap lines\n const ptToCodec = new Map<string, string>();\n for (const line of lines) {\n const match = /^a=rtpmap:(\\d+)\\s+([^\\s/]+)/.exec(line);\n if (match) {\n ptToCodec.set(match[1], match[2]);\n }\n }\n\n // Build the preferred list: PTs whose codec name matches a preferred name\n const preferredNamesUpper = preferredNames.map((n) => n.toUpperCase());\n const preferred: string[] = [];\n const remaining: string[] = [];\n\n for (const pt of payloadTypes) {\n const codecName = ptToCodec.get(pt)?.toUpperCase() ?? '';\n const preferIndex = preferredNamesUpper.indexOf(codecName);\n if (preferIndex >= 0) {\n preferred.push(pt);\n } else {\n remaining.push(pt);\n }\n }\n\n // Sort preferred PTs by the order in preferredNames\n preferred.sort((a, b) => {\n const nameA = ptToCodec.get(a)?.toUpperCase() ?? '';\n const nameB = ptToCodec.get(b)?.toUpperCase() ?? '';\n return preferredNamesUpper.indexOf(nameA) - preferredNamesUpper.indexOf(nameB);\n });\n\n const reorderedPTs = [...preferred, ...remaining];\n const newMLine = [...mLineParts.slice(0, 3), ...reorderedPTs].join(' ');\n\n // Replace the m= line in the section\n const newLines = [newMLine, ...lines.slice(1)];\n return newLines.join('\\r\\n');\n}\n\n/**\n * Finds the Opus payload type number from a=rtpmap lines in the SDP.\n *\n * @returns The payload type number as a string, or null if Opus is not found\n */\nfunction findOpusPayloadType(sdp: string): string | null {\n const match = /a=rtpmap:(\\d+)\\s+opus\\/48000/i.exec(sdp);\n return match ? match[1] : null;\n}\n\n/**\n * Appends stereo and bitrate parameters to an existing Opus fmtp line.\n *\n * Avoids duplicating parameters if they already exist.\n */\nfunction appendStereoParams(fmtpLine: string, maxBitrate: number): string {\n let result = fmtpLine;\n\n if (!result.includes('stereo=')) {\n result += ';stereo=1';\n }\n if (!result.includes('sprop-stereo=')) {\n result += ';sprop-stereo=1';\n }\n if (!result.includes('maxaveragebitrate=')) {\n result += `;maxaveragebitrate=${maxBitrate}`;\n }\n\n return result;\n}\n","import { filter, map, withLatestFrom } from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport {\n DEFAULT_ICE_CANDIDATE_TIMEOUT_MS,\n DEFAULT_ICE_GATHERING_TIMEOUT_MS\n} from '../core/constants';\nimport { isValidLocalDescription } from '../helpers/SDPHelper';\nimport { getLogger } from '../utils/logger';\n\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\nexport interface ICEGatheringControllerOptions {\n iceCandidateTimeout?: number;\n iceGatheringTimeout?: number;\n relayOnly?: boolean;\n}\n\nexport type ICEGatheringStates = 'new' | 'gathering' | 'complete' | 'timeout';\nexport interface ICECandidateState {\n state: ICEGatheringStates;\n validSDP: boolean;\n}\n\nexport class ICEGatheringController extends Destroyable {\n private iceCandidateTimeout: number;\n private iceGatheringTimeout: number;\n private iceCandidateTimer?: ReturnType<typeof setTimeout>;\n private iceGatheringTimer?: ReturnType<typeof setTimeout>;\n private relayOnly: boolean;\n\n // Event handlers (bound to this instance)\n private onicegatheringstatechangeHandler = () => {\n const { iceGatheringState } = this.peerConnection;\n logger.debug(`[ICEGatheringController] ICE gathering state changed to: ${iceGatheringState}`);\n if (iceGatheringState === 'gathering') {\n this._iceCandidatesState.next({\n state: 'gathering',\n validSDP: false\n });\n }\n };\n\n private onicecandidateHandler = (event: RTCPeerConnectionIceEvent) => {\n logger.debug('[ICEGatheringController] ICE candidate event received:', event.candidate);\n this.removeTimer('iceCandidateTimer');\n\n if (event.candidate) {\n this.iceCandidateTimer = setTimeout(() => {\n if (this.peerConnection.iceGatheringState !== 'complete') {\n logger.warn('[ICEGatheringController] ICE candidate timeout, using current SDP');\n this.handleICECandidateTimeout();\n }\n }, this.iceCandidateTimeout);\n } else {\n logger.debug('[ICEGatheringController] ICE gathering completed: null candidate received');\n this.removeTimer('iceGatheringTimer');\n\n this.handleICEGatheringComplete();\n }\n };\n\n private _iceCandidatesState = this.createBehaviorSubject<ICECandidateState>({\n state: 'new',\n validSDP: false\n });\n constructor(\n private peerConnection: RTCPeerConnection,\n private peerConnectionControllerNegotiating$: Observable<boolean>,\n options: ICEGatheringControllerOptions = {}\n ) {\n super();\n this.iceCandidateTimeout = options.iceCandidateTimeout ?? DEFAULT_ICE_CANDIDATE_TIMEOUT_MS;\n this.iceGatheringTimeout = options.iceGatheringTimeout ?? DEFAULT_ICE_GATHERING_TIMEOUT_MS;\n this.relayOnly = options.relayOnly ?? false;\n // Setup ICE candidate handling\n this.setupEventListeners();\n this.subscribeTo(\n this.peerConnectionControllerNegotiating$.pipe(filter((isNegotiating) => isNegotiating)),\n (isNegotiating) => {\n if (isNegotiating) {\n this.setupEventListeners();\n this.iceGatheringTimer = setTimeout(() => {\n if (this.peerConnection.iceGatheringState !== 'complete') {\n logger.warn('[ICEGatheringController] ICE gathering timeout, using current SDP');\n this.handleICEGatheringTimeout();\n }\n }, this.iceGatheringTimeout);\n }\n }\n );\n }\n\n private setupEventListeners() {\n this.peerConnection.removeEventListener('icecandidate', this.onicecandidateHandler);\n this.peerConnection.addEventListener('icecandidate', this.onicecandidateHandler);\n\n // Setup ICE gathering state change handling\n this.peerConnection.removeEventListener(\n 'icegatheringstatechange',\n this.onicegatheringstatechangeHandler\n );\n this.peerConnection.addEventListener(\n 'icegatheringstatechange',\n this.onicegatheringstatechangeHandler\n );\n }\n\n public get iceCandidatesState$(): Observable<ICEGatheringStates> {\n return this._iceCandidatesState.pipe(\n withLatestFrom(this.peerConnectionControllerNegotiating$),\n filter(([_, isNegotiating]) => isNegotiating),\n map(([state, _]) => state.state)\n );\n }\n\n public get hasValidLocalDescriptionSDP(): boolean {\n const sdp = this.peerConnection.localDescription?.sdp;\n return isValidLocalDescription(sdp ?? '');\n }\n\n public get isRelayOnly(): boolean {\n return this.relayOnly;\n }\n\n public setRelayOnly(value: boolean): void {\n this.relayOnly = value;\n }\n\n private handleICEGatheringComplete(): void {\n logger.debug('[ICEGatheringController] Handling ICE gathering complete');\n\n logger.debug(\n `[ICEGatheringController] Checking ICE gathering state: ${this.peerConnection.iceGatheringState}`\n );\n\n logger.debug('[ICEGatheringController] ICE gathering complete');\n\n this._iceCandidatesState.next({\n state: 'complete',\n validSDP: this.hasValidLocalDescriptionSDP\n });\n this.stopGathering();\n }\n\n private stopGathering(): void {\n this.peerConnection.removeEventListener(\n 'icegatheringstatechange',\n this.onicegatheringstatechangeHandler\n );\n this.peerConnection.removeEventListener('icecandidate', this.onicecandidateHandler);\n this.clearAllTimers();\n }\n\n private handleICEGatheringTimeout(): void {\n this.removeTimer('iceGatheringTimer');\n\n const validSDP = this.hasValidLocalDescriptionSDP;\n if (validSDP) {\n logger.debug('[ICEGatheringController] Local SDP is valid');\n this._iceCandidatesState.next({\n state: 'timeout',\n validSDP: validSDP\n });\n this.stopGathering();\n } else {\n logger.debug('### ICE gathering timeout\\n', this.peerConnection.localDescription?.sdp);\n }\n }\n\n public handleICECandidateTimeout(): void {\n if (this.iceCandidateTimer) {\n this.removeTimer('iceCandidateTimer');\n }\n\n logger.warn('[ICEGatheringController] ICE candidate timeout');\n\n const validSDP = this.hasValidLocalDescriptionSDP;\n if (!validSDP && !this.relayOnly) {\n this.restartICEGatheringWithRelayOnly();\n } else {\n logger.debug('[ICEGatheringController] Using current SDP due to ICE candidate timeout');\n this._iceCandidatesState.next({\n state: 'timeout',\n validSDP: validSDP\n });\n this.stopGathering();\n }\n }\n\n public restartICEGatheringWithRelayOnly(): void {\n logger.debug('[ICEGatheringController] Restarting ICE gathering with relay-only candidates');\n this.relayOnly = true;\n this.peerConnection.setConfiguration({\n ...this.peerConnection.getConfiguration(),\n iceTransportPolicy: 'relay'\n });\n if (!(this.peerConnection.connectionState === 'connected')) {\n this.peerConnection.restartIce();\n }\n }\n\n public removeTimer(timer: 'iceGatheringTimer' | 'iceCandidateTimer'): void {\n if (this[timer]) {\n clearTimeout(this[timer]);\n this[timer] = undefined;\n }\n }\n\n private clearAllTimers(): void {\n logger.debug('[ICEGatheringController] Clearing all timers');\n\n this.removeTimer('iceGatheringTimer');\n this.removeTimer('iceCandidateTimer');\n }\n\n public removeEventListeners(): void {\n this.peerConnection.removeEventListener(\n 'icegatheringstatechange',\n this.onicegatheringstatechangeHandler\n );\n this.peerConnection.removeEventListener('icecandidate', this.onicecandidateHandler);\n }\n\n public destroy(): void {\n logger.debug('[ICEGatheringController] Destroying ICEGatheringController');\n this.clearAllTimers();\n this.removeEventListeners();\n super.destroy();\n }\n}\n","import { takeUntil } from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { getLogger } from '../utils/logger';\n\nimport type { MediaOptions } from '../core/types/media.types';\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\nexport interface LocalStreamControllerOptions extends Omit<\n MediaOptions,\n 'inputAudioDeviceConstraints' | 'inputVideoDeviceConstraints'\n> {\n getUserMedia: (constraints: MediaStreamConstraints) => Promise<MediaStream>;\n getDisplayMedia: (options: DisplayMediaStreamOptions) => Promise<MediaStream>;\n propose: 'main' | 'screenshare' | 'additional-device';\n inputAudioDeviceConstraints?: MediaTrackConstraints | boolean;\n inputVideoDeviceConstraints?: MediaTrackConstraints | boolean;\n}\n\nexport class LocalStreamController extends Destroyable {\n private mediaTrackEndedHandler = (event: unknown) => {\n this._mediaTrackEnded$.next(event as MediaStreamTrack);\n };\n private _localStream$ = this.createBehaviorSubject<MediaStream | null>(null);\n private _localAudioTracks$ = this.createBehaviorSubject<MediaStreamTrack[]>([]);\n private _localVideoTracks$ = this.createBehaviorSubject<MediaStreamTrack[]>([]);\n private _mediaTrackEnded$ = this.createSubject<MediaStreamTrack>();\n\n constructor(private options: LocalStreamControllerOptions) {\n super();\n }\n\n public get localStream$(): Observable<MediaStream | null> {\n return this._localStream$.asObservable().pipe(takeUntil(this.destroyed$));\n }\n\n public get localAudioTracks$(): Observable<MediaStreamTrack[]> {\n return this._localAudioTracks$.asObservable().pipe(takeUntil(this.destroyed$));\n }\n\n public get localVideoTracks$(): Observable<MediaStreamTrack[]> {\n return this._localVideoTracks$.asObservable().pipe(takeUntil(this.destroyed$));\n }\n\n public get mediaTrackEnded$(): Observable<MediaStreamTrack> {\n return this._mediaTrackEnded$.asObservable().pipe(takeUntil(this.destroyed$));\n }\n\n public get localStream(): MediaStream | null {\n return this._localStream$.value;\n }\n\n public get localAudioTracks(): MediaStreamTrack[] {\n return this._localAudioTracks$.value;\n }\n\n public get localVideoTracks(): MediaStreamTrack[] {\n return this._localVideoTracks$.value;\n }\n\n /**\n * Build the local media stream based on the provided options.\n */\n public async buildLocalStream(): Promise<MediaStream> {\n logger.debug('[LocalStreamController] Building local media stream.');\n let stream: MediaStream;\n if (this.options.inputAudioStream ?? this.options.inputVideoStream) {\n const tracks = [\n ...(this.options.inputAudioStream?.getTracks() ?? []),\n ...(this.options.inputVideoStream?.getTracks() ?? [])\n ];\n stream = new MediaStream(tracks);\n } else if (this.options.propose === 'screenshare') {\n logger.debug(\n '[LocalStreamController] Requesting display media for screen sharing with audio:',\n Boolean(this.options.inputAudioDeviceConstraints)\n );\n stream = await this.options.getDisplayMedia({\n video: true,\n audio: Boolean(this.options.inputAudioDeviceConstraints)\n });\n logger.debug('[LocalStreamController] Screen share media obtained:', stream);\n } else {\n const constraints: MediaStreamConstraints = {\n audio: this.options.inputAudioDeviceConstraints,\n video: this.options.inputVideoDeviceConstraints\n };\n logger.debug('[LocalStreamController] Requesting user media with constraints:', constraints);\n stream = await this.options.getUserMedia(constraints);\n logger.debug('[LocalStreamController] User media obtained:', stream);\n }\n this._localStream$.next(stream);\n return stream;\n }\n\n /**\n * Add a local media track to the local stream.\n * @param track - The MediaStreamTrack to add\n * @returns The MediaStream (either existing or newly created)\n */\n public addTrack(track: MediaStreamTrack): MediaStream {\n const localStream = this._localStream$.value ?? new MediaStream();\n\n track.addEventListener('ended', this.mediaTrackEndedHandler);\n localStream.addTrack(track);\n this._localStream$.next(localStream);\n\n if (track.kind === 'video') {\n this._localVideoTracks$.next(localStream.getVideoTracks());\n } else {\n this._localAudioTracks$.next(localStream.getAudioTracks());\n }\n\n logger.debug(`[LocalStreamController] ${track.kind} track added:`, track.id);\n return localStream;\n }\n\n /**\n * Remove a local media track from the local stream.\n * @param trackId - The ID of the track to remove\n * @returns The removed track, or undefined if not found\n */\n public removeTrack(trackId: string): MediaStreamTrack | undefined {\n const stream = this._localStream$.value;\n const track = stream?.getTracks().find((t: MediaStreamTrack) => t.id === trackId);\n\n if (!track) {\n logger.debug(`[LocalStreamController] track not found: ${trackId}`);\n return undefined;\n }\n\n track.removeEventListener('ended', this.mediaTrackEndedHandler);\n stream?.removeTrack(track);\n track.stop();\n this._localStream$.next(stream);\n\n if (track.kind === 'video') {\n this._localVideoTracks$.next(stream?.getVideoTracks() ?? []);\n } else {\n this._localAudioTracks$.next(stream?.getAudioTracks() ?? []);\n }\n\n logger.debug(`[LocalStreamController] ${track.kind} track removed:`, trackId);\n return track;\n }\n\n /**\n * Get or create a local stream.\n */\n public getOrCreateLocalStream(): MediaStream {\n return this._localStream$.value ?? new MediaStream();\n }\n\n /**\n * Set the local stream directly.\n */\n public setLocalStream(stream: MediaStream | null): void {\n this._localStream$.next(stream);\n }\n\n /**\n * Add a track ended event listener to a track.\n */\n public addTrackEndedListener(track: MediaStreamTrack): void {\n track.addEventListener('ended', this.mediaTrackEndedHandler);\n }\n\n /**\n * Update the controller options (e.g., when media overrides are applied).\n */\n public updateOptions(options: Partial<LocalStreamControllerOptions>): void {\n this.options = {\n ...this.options,\n ...options\n };\n }\n\n /**\n * Stop all local tracks and clean up.\n */\n public stopAllTracks(): void {\n const localStream = this._localStream$.value;\n localStream?.getTracks().forEach((track: MediaStreamTrack) => {\n logger.debug(`[LocalStreamController] Stopping local track: ${track.kind}`);\n track.removeEventListener('ended', this.mediaTrackEndedHandler);\n track.stop();\n });\n }\n\n /**\n * Clean up resources.\n */\n public override destroy(): void {\n this.stopAllTracks();\n super.destroy();\n }\n}\n","import { Destroyable } from '../behaviors/Destroyable';\nimport { MediaTrackError } from '../core/errors';\nimport { getLogger } from '../utils/logger';\n\nimport type { LocalStreamController } from './LocalStreamController';\nimport type { RTCPeerConnectionPropose } from '../core/types/call.types';\n\nconst logger = getLogger();\n\nconst getDirection = (send: boolean, recv: boolean): RTCRtpTransceiverDirection => {\n if (send && recv) {\n return 'sendrecv';\n } else if (send && !recv) {\n return 'sendonly';\n } else if (!send && recv) {\n return 'recvonly';\n }\n\n return 'inactive';\n};\n\nexport interface TransceiverControllerOptions {\n peerConnection: RTCPeerConnection;\n propose: RTCPeerConnectionPropose;\n simulcast?: boolean;\n sfu?: boolean;\n msStreamsNumber?: number;\n receiveAudio?: boolean;\n receiveVideo?: boolean;\n localStreamController: LocalStreamController;\n getInputAudioDeviceConstraints: () => MediaTrackConstraints | boolean;\n getInputVideoDeviceConstraints: () => MediaTrackConstraints | boolean;\n getUserMedia: (constraints: MediaStreamConstraints) => Promise<MediaStream>;\n onError?: (error: Error) => void;\n}\n\nexport class TransceiverController extends Destroyable {\n private peerConnection: RTCPeerConnection;\n private options: TransceiverControllerOptions;\n\n constructor(options: TransceiverControllerOptions) {\n super();\n this.peerConnection = options.peerConnection;\n this.options = options;\n }\n\n public get useAddTransceivers(): boolean {\n return typeof this.peerConnection.addTransceiver === 'function';\n }\n\n public get useAddTrack(): boolean {\n return typeof this.peerConnection.addTrack === 'function';\n }\n\n public get useAddStream(): boolean {\n return (\n // @ts-expect-error -- Ignore ---\n typeof this.peerConnection.addStream === 'function' &&\n !this.useAddTransceivers &&\n !this.useAddTrack\n );\n }\n\n private get propose(): RTCPeerConnectionPropose {\n return this.options.propose;\n }\n\n private get isAdditionalDevice(): boolean {\n return this.propose === 'additional-device';\n }\n\n private get isScreenShare(): boolean {\n return this.propose === 'screenshare';\n }\n\n private get isSimulcast(): boolean {\n return Boolean(this.options.simulcast);\n }\n\n private get isSFU(): boolean {\n return Boolean(this.options.sfu);\n }\n\n private get receiveVideo(): boolean {\n return Boolean(this.options.receiveVideo);\n }\n\n private get receiveAudio(): boolean {\n return Boolean(this.options.receiveAudio);\n }\n\n private get localStream(): MediaStream | null {\n return this.options.localStreamController.localStream;\n }\n\n private get inputAudioDeviceConstraints(): MediaTrackConstraints | boolean {\n return this.options.getInputAudioDeviceConstraints();\n }\n\n private get inputVideoDeviceConstraints(): MediaTrackConstraints | boolean {\n return this.options.getInputVideoDeviceConstraints();\n }\n\n public get audioDirection(): RTCRtpTransceiverDirection {\n if (this.isAdditionalDevice) {\n return 'sendonly';\n }\n const { localStream } = this;\n const hasAudioTrack = localStream?.getAudioTracks().some((track) => track.enabled);\n const wantsToSendAudio = Boolean(this.inputAudioDeviceConstraints);\n const wantsToReceiveAudio = Boolean(this.receiveAudio);\n // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- .some() returns boolean; ?? doesn't fall through on false\n const send = hasAudioTrack || wantsToSendAudio;\n const recv = wantsToReceiveAudio;\n return getDirection(send, recv);\n }\n\n public get videoDirection(): RTCRtpTransceiverDirection {\n if (this.isAdditionalDevice || this.isScreenShare) {\n return 'sendonly';\n }\n\n if (this.isSFU) {\n return 'recvonly';\n }\n\n const { localStream } = this;\n const hasVideoTrack = localStream?.getVideoTracks().some((track) => track.enabled);\n const wantsToSendVideo = Boolean(this.inputVideoDeviceConstraints);\n const wantsToReceiveVideo = Boolean(this.receiveVideo);\n // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- .some() returns boolean; ?? doesn't fall through on false\n const send = hasVideoTrack || wantsToSendVideo;\n const recv = wantsToReceiveVideo;\n return getDirection(send, recv);\n }\n\n private get sendEncodings(): RTCRtpEncodingParameters[] | undefined {\n if (!this.isSimulcast) {\n return undefined;\n }\n\n return ['0', '1', '2'].map((rid) => ({\n active: true,\n rid,\n scaleResolutionDownBy: Number(rid) * 6 || 1.0\n }));\n }\n\n private getConstraintsFor(kind: 'audio' | 'video'): MediaTrackConstraints {\n const constraints =\n kind === 'audio' ? this.inputAudioDeviceConstraints : this.inputVideoDeviceConstraints;\n\n // If constraints is a boolean (true/false), return empty object for track constraints\n // false case means no media of this kind is requested, but that's handled elsewhere\n return typeof constraints === 'boolean' ? {} : constraints;\n }\n\n public transceiverByKind(kind: 'audio' | 'video' | 'both'): RTCRtpTransceiver[] {\n return this.peerConnection\n .getTransceivers()\n .filter((t) => kind === 'both' || t.receiver.track.kind === kind);\n }\n\n public get audioTransceivers(): RTCRtpTransceiver[] {\n return this.transceiverByKind('audio');\n }\n\n public get videoTransceivers(): RTCRtpTransceiver[] {\n return this.transceiverByKind('video');\n }\n\n public async setupTransceiverSender(\n track: MediaStreamTrack,\n localStream: MediaStream,\n transceiver?: RTCRtpTransceiver\n ): Promise<void> {\n const isAudio = track.kind === 'audio';\n const direction = isAudio ? this.audioDirection : this.videoDirection;\n const transceiverParams: RTCRtpTransceiverInit = {\n direction,\n sendEncodings: isAudio ? undefined : this.sendEncodings,\n streams: direction === 'recvonly' ? undefined : [localStream]\n };\n logger.debug(\n `[TransceiverController] Setting up transceiver sender for local ${track.kind} track:`,\n { transceiver, transceiverParams }\n );\n if (\n transceiverParams.direction &&\n ['sendonly', 'sendrecv'].includes(transceiverParams.direction)\n ) {\n if (transceiver) {\n await transceiver.sender.replaceTrack(track);\n // eslint-disable-next-line no-param-reassign\n transceiver.direction = transceiverParams.direction;\n if (transceiverParams.streams?.some((stream) => Boolean(stream))) {\n logger.debug(\n `[TransceiverController] Setting streams for transceiver sender for local ${track.kind} track:`,\n transceiverParams.streams\n );\n transceiver.sender.setStreams(...transceiverParams.streams);\n }\n } else {\n logger.debug(\n `[TransceiverController] Adding new transceiver for local ${track.kind} track:`,\n track.id\n );\n this.peerConnection.addTransceiver(track, transceiverParams);\n }\n }\n }\n\n public stopTrackSender(\n kind: 'audio' | 'video' | 'both',\n options = { updateTransceiverDirection: false }\n ): void {\n try {\n const transceivers = this.transceiverByKind(kind);\n for (const transceiver of transceivers) {\n if (transceiver.sender.track?.readyState === 'live') {\n const trackId = transceiver.sender.track.id;\n transceiver.sender.track.stop();\n this.options.localStreamController.removeTrack(trackId);\n if (options.updateTransceiverDirection) {\n transceiver.direction = 'inactive';\n }\n }\n }\n } catch (error) {\n logger.error('[TransceiverController] stopTrackSender error', kind, error);\n this.options.onError?.(new MediaTrackError('stopTrackSender', kind, error));\n }\n }\n\n public async restoreTrackSender(kind: 'audio' | 'video' | 'both'): Promise<void> {\n try {\n logger.debug('[TransceiverController] restoreTrackSender called', kind);\n const constraints: MediaStreamConstraints = {};\n const transceivers = this.transceiverByKind(kind);\n for (const transceiver of transceivers) {\n const { track } = transceiver.sender;\n // Check if track is null, ended - all need restoration\n const needsRestore = !track || track.readyState === 'ended';\n if (needsRestore) {\n const trackKind = track?.kind ?? transceiver.receiver.track.kind;\n if (trackKind === 'audio' || trackKind === 'video') {\n constraints[trackKind] = this.getConstraintsFor(trackKind);\n }\n }\n }\n\n logger.debug('[TransceiverController] restoreTrackSender constraints:', constraints);\n\n // Don't call getUserMedia if no tracks need restoration\n if (Object.keys(constraints).length === 0) {\n logger.warn('[TransceiverController] restoreTrackSender: no tracks need restoration', kind);\n return;\n }\n\n const stream = await this.options.getUserMedia(constraints);\n const newTracks = stream.getTracks();\n\n logger.debug('[TransceiverController] restoreTrackSender new tracks:', newTracks);\n for (const newTrack of newTracks) {\n this.options.localStreamController.addTrack(newTrack);\n const trackKind = newTrack.kind as 'audio' | 'video';\n const transceiverOfKind = this.transceiverByKind(trackKind)[0];\n transceiverOfKind.direction =\n trackKind === 'audio' ? this.audioDirection : this.videoDirection;\n logger.debug(\n '[TransceiverController] restoreTrackSender setting direction for',\n trackKind,\n transceiverOfKind.direction\n );\n await transceiverOfKind.sender.replaceTrack(newTrack);\n }\n } catch (error) {\n logger.error('[TransceiverController] restoreTrackSender error', kind, error);\n this.options.onError?.(new MediaTrackError('restoreTrackSender', kind, error));\n }\n }\n\n public async replaceSenderTrack(kind: 'audio' | 'video', track: MediaStreamTrack): Promise<void> {\n const transceivers = kind === 'audio' ? this.audioTransceivers : this.videoTransceivers;\n for (const transceiver of transceivers) {\n await transceiver.sender.replaceTrack(track);\n }\n }\n\n public async setupRemoteTransceivers(type: 'offer' | 'answer'): Promise<void> {\n if (type === 'answer') {\n // remote setup was made by the offerer\n return;\n }\n\n for (const kind of ['audio', 'video']) {\n const transceivers = kind === 'audio' ? this.audioTransceivers : this.videoTransceivers;\n for (const transceiver of transceivers) {\n const direction = kind === 'audio' ? this.audioDirection : this.videoDirection;\n\n if (['inactive', 'recvonly'].includes(direction)) {\n transceiver.direction = direction;\n await transceiver.sender.replaceTrack(null);\n transceiver.sender.setStreams();\n }\n }\n }\n\n if (this.videoDirection === 'recvonly' && this.isSFU && this.useAddTransceivers) {\n const { msStreamsNumber = 5 } = this.options;\n for (let i = 0; i < Number(msStreamsNumber); i++) {\n this.peerConnection.addTransceiver('video', { direction: 'recvonly' });\n }\n }\n }\n\n public async updateSendersConstraints(\n kind: 'audio' | 'video',\n constraints?: MediaTrackConstraints\n ): Promise<void> {\n if (!constraints) {\n this.stopTrackSender(kind);\n return Promise.resolve();\n }\n\n const senders = this.peerConnection\n .getSenders()\n .filter((sender) => sender.track?.kind === kind && sender.track.readyState === 'live');\n\n for (const sender of senders) {\n const { track } = sender;\n if (track) {\n const constraintsToApply: MediaTrackConstraints = {\n ...track.getConstraints(),\n ...constraints\n };\n try {\n await track.applyConstraints(constraintsToApply);\n logger.debug(\n `[TransceiverController] Updated ${kind} sender constraints:`,\n constraintsToApply\n );\n logger.debug(\n `[TransceiverController] Updated ${kind} sender constraints:`,\n track.getConstraints()\n );\n } catch (error) {\n logger.warn(\n `[TransceiverController] applyConstraints failed for ${kind} track ${track.id}, attempting track replacement fallback:`,\n error\n );\n try {\n await this.replaceTrackFallback(sender, track, kind, constraintsToApply);\n } catch (fallbackError) {\n logger.warn(\n `[TransceiverController] Track replacement fallback also failed for ${kind} track:`,\n fallbackError\n );\n this.options.onError?.(\n new MediaTrackError('updateSendersConstraints', kind, fallbackError)\n );\n }\n }\n }\n }\n }\n\n /**\n * Fallback when applyConstraints fails: stop the current track, acquire a new\n * one via getUserMedia with the merged constraints (preserving the current\n * deviceId), replace the sender track, and update the localStream.\n *\n * This is critical for iOS Safari where applyConstraints on audio tracks\n * silently fails or throws.\n */\n private async replaceTrackFallback(\n sender: RTCRtpSender,\n oldTrack: MediaStreamTrack,\n kind: 'audio' | 'video',\n mergedConstraints: MediaTrackConstraints\n ): Promise<void> {\n // Preserve the current deviceId so we stay on the same physical device\n const currentSettings = oldTrack.getSettings();\n const { deviceId } = currentSettings;\n const constraintsWithDevice: MediaTrackConstraints = {\n ...mergedConstraints,\n ...(deviceId ? { deviceId: { exact: deviceId } } : {})\n };\n\n // Stop the old track\n const trackId = oldTrack.id;\n oldTrack.stop();\n this.options.localStreamController.removeTrack(trackId);\n\n // Acquire a replacement track\n const stream = await this.options.getUserMedia({ [kind]: constraintsWithDevice });\n const newTrack = stream.getTracks().find((t) => t.kind === kind);\n\n if (!newTrack) {\n throw new MediaTrackError(\n 'replaceTrackFallback',\n kind,\n new Error('getUserMedia returned no track of the requested kind')\n );\n }\n\n // Replace on the sender and update localStream\n await sender.replaceTrack(newTrack);\n this.options.localStreamController.addTrack(newTrack);\n\n logger.debug(\n `[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`\n );\n }\n\n public getMediaDirections(): {\n audio: RTCRtpTransceiverDirection;\n video: RTCRtpTransceiverDirection;\n } {\n if (this.peerConnection.connectionState === 'connected') {\n // If we are connected let's get the actual directions from the transceivers\n return this.peerConnection.getTransceivers().reduce(\n (acc, transceiver) => {\n return {\n ...acc,\n [transceiver.receiver.track.kind]: transceiver.direction\n };\n },\n { audio: 'inactive', video: 'inactive' }\n );\n }\n\n return {\n audio: this.audioDirection,\n video: this.videoDirection\n };\n }\n\n public updatePeerConnection(peerConnection: RTCPeerConnection): void {\n this.peerConnection = peerConnection;\n }\n\n public updateOptions(options: Partial<TransceiverControllerOptions>): void {\n this.options = {\n ...this.options,\n ...options\n };\n }\n}\n","/* eslint-disable max-lines */\nimport {\n auditTime,\n defer,\n exhaustMap,\n filter,\n from,\n map,\n shareReplay,\n switchMap,\n takeUntil,\n tap,\n skipWhile,\n merge\n} from 'rxjs';\nimport { v4 as uuid } from 'uuid';\n\nimport { ICEGatheringController } from './ICEGatheringController';\nimport { LocalStreamController } from './LocalStreamController';\nimport { TransceiverController } from './TransceiverController';\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { PreferencesContainer } from '../containers/PreferencesContainer';\nimport { DependencyError } from '../core/errors';\nimport {\n enableStereoOpus,\n extractMediaDirectionsFromSDP,\n isValidLocalDescription,\n setCodecPreferences\n} from '../helpers/SDPHelper';\nimport { filterNull } from '../operators';\nimport { getLogger } from '../utils/logger';\nimport { toError } from '../utils/toError';\n\nimport type { RTCPeerConnectionPropose, RTCPeerConnectionType } from '../core/types/call.types';\nimport type { MediaDirections, MediaOptions } from '../core/types/media.types';\nimport type { WebRTCApiProvider } from '../dependencies/interfaces';\nimport type { DeviceController } from '../interfaces/DeviceController';\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\nexport interface RTCPeerConnectionControllerOptions extends MediaOptions {\n callId?: string;\n rtcConfiguration?: RTCConfiguration;\n simulcast?: boolean;\n sfu?: boolean;\n msStreamsNumber?: number;\n propose: RTCPeerConnectionPropose;\n iceServers?: RTCIceServer[];\n disableUdpIceServers?: boolean;\n relayOnly?: boolean;\n iceCandidateTimeout?: number;\n iceGatheringTimeout?: number;\n webRTCApiProvider?: WebRTCApiProvider;\n /** Per-call preferred video codecs (overrides global preferences). */\n preferredVideoCodecs?: string[];\n /** Per-call preferred audio codecs (overrides global preferences). */\n preferredAudioCodecs?: string[];\n /** Per-call stereo Opus setting (overrides global preferences). */\n stereo?: boolean;\n}\n\nexport type RTCPeerConnectionControllerOptionsPartial = Partial<RTCPeerConnectionControllerOptions>;\n\nexport interface UpdateSDPStatusParams {\n status: 'received' | 'sent' | 'failed';\n sdp?: string;\n}\n\nexport class RTCPeerConnectionController extends Destroyable {\n public readonly id: string;\n public firstSDPExchangeCompleted = false;\n public sdpInit?: RTCSessionDescriptionInit;\n private negotiationNeeded$ = this.createSubject<void>();\n private deviceController: DeviceController;\n private localStreamController: LocalStreamController;\n // Transceiver controller - initialized lazily when peerConnection is created\n private transceiverController?: TransceiverController;\n public readonly localDescription$: Observable<RTCSessionDescription | null> = defer(() =>\n from(this.init())\n ).pipe(\n switchMap(() =>\n // Wait for ICE negotiation before emitting localDescription\n this.iceGatheringController.iceCandidatesState$.pipe(\n filter((iceCandidateState) => !['new', 'gathering'].includes(iceCandidateState)),\n tap(() => {\n this.negotiationEnded();\n }),\n // Only emit when signaling state is 'have-local-offer'\n filter(() => this.shouldEmitLocalDescription),\n map(() => this.peerConnection?.localDescription),\n filterNull(),\n tap((desc) => {\n if (desc.type === 'answer') {\n // Once the answer is emitted, switch type to offer for future negotiations\n this._type = 'offer';\n }\n })\n )\n ),\n shareReplay(1),\n takeUntil(this.destroyed$)\n );\n public peerConnection?: RTCPeerConnection;\n private initPromise?: Promise<void>;\n private connectionTimeout = 3_000;\n private connectionTimer?: ReturnType<typeof setTimeout>;\n private oniceconnectionstatechangeHandler = () => {\n if (this.peerConnection) {\n const { iceConnectionState } = this.peerConnection;\n logger.debug(\n `[RTCPeerConnectionController] ICE connection state changed to: ${iceConnectionState}`\n );\n this._iceConnectionState$.next(this.peerConnection.iceConnectionState);\n }\n };\n private onconnectionstatechangeHandler = () => {\n if (this.peerConnection) {\n const { connectionState } = this.peerConnection;\n logger.debug(`[RTCPeerConnectionController] Connection state changed to: ${connectionState}`);\n if (connectionState === 'connected') {\n this.removeConnectionTimer();\n }\n this._connectionState$.next(this.peerConnection.connectionState);\n }\n };\n private onsignalingstatechangeHandler = () => {\n logger.debug(\n `[RTCPeerConnectionController] Signaling state changed to: ${this.peerConnection?.signalingState}`\n );\n };\n private onicegatheringstatechangeHandler = () => {\n if (this.peerConnection) {\n this._iceGatheringState$.next(this.peerConnection.iceGatheringState);\n }\n };\n private onnegotiationneededHandler = (event: unknown) => {\n logger.debug('[RTCPeerConnectionController] Negotiation needed event received.', event);\n this.negotiationNeeded$.next();\n };\n private updateSelectedInputDevice = async (\n kind: 'audio' | 'video',\n deviceInfo: MediaDeviceInfo | null\n ): Promise<void> => {\n try {\n const { localStream } = this;\n if (!localStream) {\n logger.warn(\n '[RTCPeerConnectionController] No local stream available to update input device.'\n );\n return;\n }\n\n logger.debug(\n `[RTCPeerConnectionController] Updating selected ${kind} input device:`,\n localStream.getTracks()\n );\n // Stop existing audio tracks\n const track = localStream.getTracks().find((track: MediaStreamTrack) => track.kind === kind);\n\n if (track) {\n this.transceiverController?.stopTrackSender(kind);\n this.localStream?.removeTrack(track);\n logger.debug(\n `[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`,\n localStream.getTracks()\n );\n\n if (!deviceInfo) {\n logger.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);\n return;\n }\n\n const stream = await this.getUserMedia({\n [kind]: {\n ...track.getConstraints(),\n ...this.deviceController.deviceInfoToConstraints(deviceInfo)\n }\n });\n\n const streamTrack = stream.getTracks().find((t) => t.kind === kind);\n\n if (streamTrack) {\n logger.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);\n this.localStream?.addTrack(streamTrack);\n await this.transceiverController?.replaceSenderTrack(kind, streamTrack);\n logger.debug(\n `[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`,\n this.localStream?.getTracks()\n );\n }\n }\n\n logger.debug(\n `[RTCPeerConnectionController] ${kind} input device selected:`,\n deviceInfo?.label\n );\n } catch (error) {\n logger.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);\n this._errors$.next(toError(error));\n throw error;\n }\n };\n private _isNegotiating$ = this.createBehaviorSubject<boolean>(false);\n private _iceGatheringController?: ICEGatheringController;\n private _memberId: string | null = null;\n private _type: RTCPeerConnectionType;\n // Observable state streams - exposed as public observables\n private _iceConnectionState$ = this.createReplaySubject<RTCIceConnectionState>(1);\n private _connectionState$ = this.createReplaySubject<RTCPeerConnectionState>(1);\n private _signalingState$ = this.createReplaySubject<RTCSignalingState>(1);\n private _iceGatheringState$ = this.createReplaySubject<RTCIceGatheringState>(1);\n // Error stream\n private _errors$ = this.createReplaySubject<Error>(1);\n // ICE candidates stream\n private _iceCandidates$ = this.createReplaySubject<RTCIceCandidate[]>(1);\n // Initialization state\n private _initialized$ = this.createReplaySubject<boolean>(1);\n // Remote description\n private _remoteDescription$ = this.createReplaySubject<RTCSessionDescription | null>(1);\n private _remoteStream$ = this.createBehaviorSubject<MediaStream | null>(null);\n private _remoteOfferMediaDirections: MediaDirections | null = null;\n constructor(\n protected options: RTCPeerConnectionControllerOptionsPartial = {},\n remoteSessionDescription?: string,\n deviceController?: DeviceController\n ) {\n super();\n this.deviceController = deviceController ?? ({} as DeviceController);\n this.id = options.callId ?? uuid();\n this._type = remoteSessionDescription ? 'answer' : 'offer';\n\n this.sdpInit = remoteSessionDescription\n ? {\n type: 'offer',\n sdp: remoteSessionDescription\n }\n : undefined;\n\n this._remoteOfferMediaDirections = remoteSessionDescription\n ? extractMediaDirectionsFromSDP(remoteSessionDescription)\n : null;\n\n // For inbound calls, derive send/receive defaults from the remote offer directions.\n // Remote 'sendrecv' → remote sends (we receive) AND remote receives (we send)\n // Remote 'sendonly' → remote sends (we receive) but doesn't receive (we don't send)\n // Remote 'recvonly' → remote receives (we send) but doesn't send (we don't receive)\n const offerDefaults = this._remoteOfferMediaDirections\n ? {\n audio: this._remoteOfferMediaDirections.audio.includes('recv'),\n video: this._remoteOfferMediaDirections.video.includes('recv'),\n receiveAudio: this._remoteOfferMediaDirections.audio.includes('send'),\n receiveVideo: this._remoteOfferMediaDirections.video.includes('send')\n }\n : {};\n\n this.options = {\n ...options,\n audio: options.audio ?? offerDefaults.audio,\n video: options.video ?? offerDefaults.video,\n receiveAudio:\n options.receiveAudio ??\n offerDefaults.receiveAudio ??\n PreferencesContainer.instance.receiveAudio,\n receiveVideo:\n options.receiveVideo ??\n offerDefaults.receiveVideo ??\n PreferencesContainer.instance.receiveVideo\n };\n\n // Initialize the local stream controller\n this.localStreamController = new LocalStreamController({\n propose: this.propose,\n inputAudioStream: this.options.inputAudioStream,\n inputVideoStream: this.options.inputVideoStream,\n inputAudioDeviceConstraints: this.inputAudioDeviceConstraints,\n inputVideoDeviceConstraints: this.inputVideoDeviceConstraints,\n getUserMedia: async (constraints: MediaStreamConstraints) => this.getUserMedia(constraints),\n getDisplayMedia: async (options: DisplayMediaStreamOptions) => this.getDisplayMedia(options)\n });\n }\n\n private get iceGatheringController(): ICEGatheringController {\n if (!this._iceGatheringController) {\n throw new DependencyError('ICEGatheringController is not initialized');\n }\n return this._iceGatheringController;\n }\n\n private get shouldEmitLocalDescription(): boolean {\n if (!this.peerConnection) {\n return false;\n }\n\n const { localDescription, signalingState } = this.peerConnection;\n\n if (!localDescription || !isValidLocalDescription(localDescription.sdp)) {\n return false;\n }\n\n return (\n (localDescription.type === 'offer' && signalingState === 'have-local-offer') ||\n (localDescription.type === 'answer' && signalingState === 'stable')\n );\n }\n\n private removeConnectionTimer(): void {\n if (this.connectionTimer) {\n clearTimeout(this.connectionTimer);\n this.connectionTimer = undefined;\n }\n }\n\n public setMemberId(memberId: string | null): void {\n this._memberId = memberId;\n }\n\n public get memberId(): string | null {\n return this._memberId;\n }\n\n public stopTrackSender(\n kind: 'audio' | 'video' | 'both',\n options = { updateTransceiverDirection: false }\n ): void {\n this.transceiverController?.stopTrackSender(kind, options);\n }\n\n public get isNegotiating$(): Observable<boolean> {\n return this._isNegotiating$.asObservable();\n }\n\n public get isNegotiating(): boolean {\n return this._isNegotiating$.value;\n }\n\n public updateMediaDevicesOptions(options: MediaOptions): void {\n this.options = {\n ...this.options,\n ...options\n };\n }\n\n public get iceGatheringState$(): Observable<RTCIceGatheringState> {\n return this.cachedObservable('iceGatheringState$', () =>\n this._iceGatheringState$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get mediaTrackEnded$(): Observable<MediaStreamTrack> {\n return this.cachedObservable('mediaTrackEnded$', () =>\n this.localStreamController.mediaTrackEnded$.pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get errors$(): Observable<Error> {\n return this.cachedObservable('errors$', () =>\n this._errors$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get iceCandidates$(): Observable<RTCIceCandidate[]> {\n return this.cachedObservable('iceCandidates$', () =>\n this._iceCandidates$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get initialized$(): Observable<boolean> {\n return this.cachedObservable('initialized$', () =>\n this._initialized$.asObservable().pipe(\n filter((initialized) => initialized),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n public get remoteDescription$(): Observable<RTCSessionDescription | null> {\n return this.cachedObservable('remoteDescription$', () =>\n this._remoteDescription$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get localStream$(): Observable<MediaStream | null> {\n return this.cachedObservable('localStream$', () =>\n this.localStreamController.localStream$.pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get remoteStream$(): Observable<MediaStream | null> {\n return this.cachedObservable('remoteStream$', () =>\n this._remoteStream$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get localAudioTracks$(): Observable<MediaStreamTrack[]> {\n return this.cachedObservable('localAudioTracks$', () =>\n this.localStreamController.localAudioTracks$.pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get localVideoTracks$(): Observable<MediaStreamTrack[]> {\n return this.cachedObservable('localVideoTracks$', () =>\n this.localStreamController.localVideoTracks$.pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get iceConnectionState$(): Observable<RTCIceConnectionState> {\n return this.cachedObservable('iceConnectionState$', () =>\n this._iceConnectionState$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get connectionState$(): Observable<RTCPeerConnectionState> {\n return this.cachedObservable('connectionState$', () =>\n this._connectionState$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get signalingState$(): Observable<RTCSignalingState> {\n return this.cachedObservable('signalingState$', () =>\n this._signalingState$.asObservable().pipe(takeUntil(this.destroyed$))\n );\n }\n\n public get type(): RTCPeerConnectionType {\n return this._type;\n }\n\n public get propose(): RTCPeerConnectionPropose {\n return this.options.propose ?? 'main';\n }\n\n public get isAdditionalDevice(): boolean {\n return this.propose === 'additional-device';\n }\n\n public get isMainDevice(): boolean {\n return this.propose === 'main';\n }\n\n public get isScreenShare(): boolean {\n return this.propose === 'screenshare';\n }\n\n protected get iceServers(): RTCIceServer[] {\n if (!this.options.disableUdpIceServers) {\n return this.options.iceServers ?? [];\n }\n const tcpTransportParam = 'transport=tcp';\n\n // When disabling UDP, keep only URLs that explicitly specify transport=tcp\n // URLs without a transport parameter default to UDP and should be filtered out\n return (this.options.iceServers ?? []).map((server) => {\n const urls = Array.isArray(server.urls) ? server.urls : [server.urls];\n return {\n ...server,\n urls: urls.filter((url) => url.includes(tcpTransportParam))\n } as RTCIceServer;\n });\n }\n\n private get rtcConfiguration(): RTCConfiguration {\n // Destructure to exclude iceServers from options spread, since we use the filtered this.iceServers\n const { iceServers: _iceServers, ...restOptions } = this.options;\n return {\n bundlePolicy: 'max-compat',\n iceCandidatePoolSize: 10,\n iceServers: this.iceServers,\n iceTransportPolicy: this.options.relayOnly ? 'relay' : 'all',\n //@ts-expect-error -- Ignore ---\n sdpSemantics: 'unified-plan',\n ...restOptions\n };\n }\n\n public get receiveVideo(): boolean {\n return Boolean(this.options.receiveVideo);\n }\n\n public get receiveAudio(): boolean {\n return Boolean(this.options.receiveAudio);\n }\n\n public get localStream(): MediaStream | null {\n return this.localStreamController.localStream;\n }\n\n public get remoteStream(): MediaStream | null {\n return this._remoteStream$.value;\n }\n\n private get inputAudioDeviceConstraints(): MediaTrackConstraints | boolean {\n if (this.options.audio === false && !this.options.inputAudioDeviceConstraints) {\n return false;\n }\n // Section 5.9: When device is disabled, return false (receive-only)\n const deviceConstraints = this.deviceController.selectedAudioInputDeviceConstraints;\n if (deviceConstraints === false) {\n return false;\n }\n const audioBase =\n typeof this.options.inputAudioDeviceConstraints === 'object'\n ? this.options.inputAudioDeviceConstraints\n : {};\n const audioDevice = typeof deviceConstraints === 'object' ? deviceConstraints : {};\n return {\n ...audioBase,\n ...audioDevice\n };\n }\n\n private get inputVideoDeviceConstraints(): MediaTrackConstraints | boolean {\n if (!this.options.video && !this.options.inputVideoDeviceConstraints) {\n return false;\n }\n // Section 5.9: When device is disabled, return false (receive-only)\n const deviceConstraints = this.deviceController.selectedVideoInputDeviceConstraints;\n if (deviceConstraints === false) {\n return false;\n }\n const videoBase =\n typeof this.options.inputVideoDeviceConstraints === 'object'\n ? this.options.inputVideoDeviceConstraints\n : {};\n const videoDevice = typeof deviceConstraints === 'object' ? deviceConstraints : {};\n return {\n ...videoBase,\n ...videoDevice\n };\n }\n\n private get WebRTCPeerConnectionConstructor(): typeof RTCPeerConnection {\n return this.options.webRTCApiProvider?.RTCPeerConnection ?? RTCPeerConnection;\n }\n\n private get offerOptions(): RTCOfferOptions {\n const options: RTCOfferOptions = {\n iceRestart: this.firstSDPExchangeCompleted ? true : undefined\n };\n switch (this.propose) {\n case 'screenshare':\n case 'additional-device':\n return {\n ...options,\n offerToReceiveAudio: false,\n offerToReceiveVideo: false\n };\n case 'main':\n default:\n return {\n ...options,\n offerToReceiveAudio: true,\n offerToReceiveVideo:\n this.options.receiveVideo ?? Boolean(this.inputVideoDeviceConstraints)\n };\n }\n }\n\n private get answerOptions(): RTCAnswerOptions {\n return {\n iceRestart: this.firstSDPExchangeCompleted ? true : undefined\n };\n }\n\n /**\n * Initialize the RTCPeerConnection and setup event listeners.\n * Called automatically when localDescription$ is subscribed to (deferred pattern).\n * Uses Promise memoization to ensure initialization only happens once,\n * even if called concurrently.\n */\n private async init(): Promise<void> {\n this.initPromise ??= this.doInit();\n return this.initPromise;\n }\n\n /**\n * Internal initialization implementation.\n * Should only be called via init() to ensure single execution.\n */\n private async doInit(): Promise<void> {\n try {\n this.setupPeerConnection();\n\n this.subscribeTo(\n this.negotiationNeeded$.pipe(\n auditTime(0), //When updating multiple tracks, batches all the events together\n exhaustMap(async () => this.startNegotiation()) // Ignore new events while negotiation is ongoing\n ),\n {\n next: () => {\n logger.debug('[RTCPeerConnectionController] Start Negotiation completed successfully');\n },\n error: (error) => {\n logger.error('[RTCPeerConnectionController] Start Negotiation error:', error);\n this._errors$.next(toError(error));\n }\n }\n );\n\n this.subscribeTo(\n merge(\n this.deviceController.selectedAudioInputDevice$.pipe(\n map((deviceInfo) => ['audio', deviceInfo] as const)\n ),\n this.deviceController.selectedVideoInputDevice$.pipe(\n map((deviceInfo) => ['video', deviceInfo] as const)\n )\n ).pipe(\n // we only want to react to changes after the localstream is created\n // before that the device selection is handle int the localstream creation\n skipWhile(() => !this.localStreamController.localStream)\n ),\n async ([kind, deviceInfo]) => {\n logger.debug(`[RTCPeerConnectionController] Selected input device changed for:`, {\n kind,\n deviceInfo\n });\n await this.updateSelectedInputDevice(kind, deviceInfo);\n }\n );\n\n // For inbound calls: only setup remote tracks (ontrack handler) during init.\n // Local track setup is deferred to acceptInbound() so that:\n // 1. Remote description is set first, creating transceivers from the offer\n // 2. Local tracks reuse those transceivers instead of creating duplicates\n // 3. Media overrides can be applied before tracks are acquired\n if (this.type === 'answer' && this.sdpInit) {\n await this.setupRemoteTracks();\n\n this._initialized$.next(true);\n\n this.setupEventListeners();\n this._isNegotiating$.next(true);\n await this._setRemoteDescription(this.sdpInit);\n } else {\n await this.setupTrackHandling();\n\n this._initialized$.next(true);\n }\n } catch (error) {\n logger.error('[RTCPeerConnectionController] Initialization error:', error);\n this._errors$.next(toError(error));\n this.destroy();\n }\n }\n\n private setupPeerConnection() {\n this.peerConnection = new this.WebRTCPeerConnectionConstructor(this.rtcConfiguration);\n this.peerConnection.addEventListener('negotiationneeded', this.onnegotiationneededHandler);\n this._iceGatheringController = new ICEGatheringController(\n this.peerConnection,\n this.isNegotiating$,\n {\n iceCandidateTimeout: this.options.iceCandidateTimeout,\n iceGatheringTimeout: this.options.iceGatheringTimeout,\n relayOnly: this.options.relayOnly\n }\n );\n\n // Initialize the transceiver controller\n this.transceiverController = new TransceiverController({\n peerConnection: this.peerConnection,\n propose: this.propose,\n simulcast: this.options.simulcast,\n sfu: this.options.sfu,\n msStreamsNumber: this.options.msStreamsNumber,\n receiveAudio: this.receiveAudio,\n receiveVideo: this.receiveVideo,\n localStreamController: this.localStreamController,\n getInputAudioDeviceConstraints: () => this.inputAudioDeviceConstraints,\n getInputVideoDeviceConstraints: () => this.inputVideoDeviceConstraints,\n getUserMedia: async (constraints: MediaStreamConstraints) => this.getUserMedia(constraints),\n onError: (error: Error) => {\n this._errors$.next(error);\n }\n });\n }\n\n private async startNegotiation() {\n if (this.isNegotiating) {\n logger.debug('[RTCPeerConnectionController] Negotiation already in progress, skipping.');\n return;\n }\n\n this.setupEventListeners();\n\n if (this.type === 'answer') {\n logger.debug(\n '[RTCPeerConnectionController] This is an answer type still, skipping offer creation.'\n );\n return;\n }\n\n this._isNegotiating$.next(true);\n logger.debug('[RTCPeerConnectionController] Starting negotiation.');\n\n try {\n const { offerOptions } = this;\n logger.debug('[RTCPeerConnectionController] Creating offer with options:', offerOptions);\n await this.createOffer(offerOptions);\n } catch (error) {\n logger.error('[RTCPeerConnectionController] Error during negotiation:', error);\n this._errors$.next(toError(error));\n }\n }\n\n /**\n * Create an SDP offer and set it as local description.\n */\n private async createOffer(options?: RTCOfferOptions): Promise<void> {\n if (!this.peerConnection) {\n throw new DependencyError('RTCPeerConnection is not initialized');\n }\n\n const offer = await this.peerConnection.createOffer(options);\n await this.setLocalDescription(offer);\n\n // Note: localDescription will be emitted by setupEventListeners initial emission\n // and updated when ICE gathering state changes\n }\n\n public async updateAnswerStatus({ status, sdp }: UpdateSDPStatusParams): Promise<void> {\n let readyToConnect = status !== 'failed';\n\n try {\n if (status === 'received' && sdp) {\n logger.debug('[RTCPeerConnectionController] Received answer SDP:', sdp);\n await this._setRemoteDescription({\n type: 'answer',\n sdp\n });\n }\n } catch (error) {\n logger.error('[RTCPeerConnectionController] Error updating answer status:', error);\n this._errors$.next(toError(error));\n readyToConnect = false;\n } finally {\n if (readyToConnect) {\n this.readyToConnect();\n } else {\n this.iceGatheringController.restartICEGatheringWithRelayOnly();\n }\n }\n }\n\n public async updateOfferStatus({ status, sdp }: UpdateSDPStatusParams): Promise<void> {\n switch (status) {\n case 'received':\n this._type = 'answer';\n this.sdpInit = {\n type: 'offer',\n sdp: sdp\n };\n await this.handleOfferReceived();\n break;\n case 'failed':\n logger.error('[RTCPeerConnectionController] Offer failed to be processed by remote.');\n break;\n case 'sent':\n default:\n // No action needed for sent offers\n }\n }\n\n /**\n * Accept an inbound call by creating the SDP answer.\n * Optionally override media options before the answer is generated.\n * Must be called after initialization for inbound (answer-type) connections.\n */\n public async acceptInbound(mediaOverrides?: MediaOptions): Promise<void> {\n if (mediaOverrides) {\n const { audio, video, receiveAudio, receiveVideo } = mediaOverrides;\n this.options = {\n ...this.options,\n ...(audio !== undefined ? { audio } : {}),\n ...(video !== undefined ? { video } : {}),\n ...(receiveAudio !== undefined ? { receiveAudio } : {}),\n ...(receiveVideo !== undefined ? { receiveVideo } : {})\n };\n this.transceiverController?.updateOptions({\n receiveAudio: this.receiveAudio,\n receiveVideo: this.receiveVideo\n });\n this.localStreamController.updateOptions({\n inputAudioDeviceConstraints: this.inputAudioDeviceConstraints,\n inputVideoDeviceConstraints: this.inputVideoDeviceConstraints\n });\n }\n\n // Setup local tracks after remote description is set and media overrides applied.\n // This ensures local tracks reuse transceivers from the remote offer\n // instead of creating duplicate transceivers via addTransceiver().\n await this.setupLocalTracks();\n\n const { answerOptions } = this;\n logger.debug(\n '[RTCPeerConnectionController] Creating inbound answer with options:',\n answerOptions\n );\n await this.createAnswer(answerOptions);\n }\n\n private async handleOfferReceived() {\n if (!this.sdpInit) {\n throw new DependencyError('SDP initialization parameters are not set');\n }\n\n this._isNegotiating$.next(true);\n await this._setRemoteDescription(this.sdpInit);\n\n const { answerOptions } = this;\n logger.debug('[RTCPeerConnectionController] Creating answer with options:', answerOptions);\n await this.createAnswer(answerOptions);\n }\n\n private readyToConnect() {\n this.firstSDPExchangeCompleted = true;\n this.connectionTimer = setTimeout(() => {\n this.removeConnectionTimer();\n if (this.peerConnection?.connectionState !== 'connected') {\n logger.debug(\n '[RTCPeerConnectionController] Connection timeout, restarting ICE gathering with relay only.'\n );\n this.iceGatheringController.restartICEGatheringWithRelayOnly();\n }\n }, this.connectionTimeout);\n }\n\n private async setRemoteDescriptionBefore(sdp: string = ''): Promise<string> {\n // TODO use the options hooks\n return Promise.resolve(sdp);\n }\n protected async setLocalDescription(params: RTCSessionDescriptionInit): Promise<void> {\n const finalLocal = await this.setLocalDescriptionBefore(params.sdp);\n return this.peerConnection?.setLocalDescription({\n ...params,\n sdp: finalLocal\n });\n }\n async setLocalDescriptionBefore(sdp: string = ''): Promise<string> {\n let result = sdp;\n\n // Per-call overrides take precedence, then global preferences\n const preferredAudioCodecs =\n this.options.preferredAudioCodecs ?? PreferencesContainer.instance.preferredAudioCodecs;\n const preferredVideoCodecs =\n this.options.preferredVideoCodecs ?? PreferencesContainer.instance.preferredVideoCodecs;\n const stereo = this.options.stereo ?? PreferencesContainer.instance.stereoAudio;\n\n // Apply codec reordering if preferences are set\n if (preferredAudioCodecs.length > 0 || preferredVideoCodecs.length > 0) {\n result = setCodecPreferences(result, preferredAudioCodecs, preferredVideoCodecs);\n logger.debug('[RTCPeerConnectionController] Applied codec preferences to SDP', {\n preferredAudioCodecs,\n preferredVideoCodecs\n });\n }\n\n // Apply stereo Opus if enabled\n if (stereo) {\n result = enableStereoOpus(result);\n logger.debug('[RTCPeerConnectionController] Applied stereo Opus to SDP');\n }\n\n return Promise.resolve(result);\n }\n /**\n * Create an SDP answer and set it as local description.\n */\n private async createAnswer(options?: RTCAnswerOptions): Promise<void> {\n if (!this.peerConnection) {\n throw new DependencyError('RTCPeerConnection is not initialized');\n }\n\n const answer = await this.peerConnection.createAnswer(options);\n await this.setLocalDescription(answer);\n // Note: localDescription will be emitted by setupEventListeners initial emission\n // and updated when ICE gathering state changes\n }\n /**\n * Setup event listeners on RTCPeerConnection for state changes.\n */\n private setupEventListeners(): void {\n if (!this.peerConnection) {\n throw new DependencyError('RTCPeerConnection is not initialized');\n }\n\n // Emit initial states from the actual RTCPeerConnection\n this._iceConnectionState$.next(this.peerConnection.iceConnectionState);\n this._connectionState$.next(this.peerConnection.connectionState);\n this._signalingState$.next(this.peerConnection.signalingState);\n this._iceGatheringState$.next(this.peerConnection.iceGatheringState);\n // Note: localDescription is NOT emitted here - it will be emitted when ICE gathering completes\n this._remoteDescription$.next(this.peerConnection.remoteDescription);\n\n this.peerConnection.removeEventListener(\n 'icegatheringstatechange',\n this.onicegatheringstatechangeHandler\n );\n this.peerConnection.addEventListener(\n 'icegatheringstatechange',\n this.onicegatheringstatechangeHandler\n );\n this.peerConnection.removeEventListener(\n 'iceconnectionstatechange',\n this.oniceconnectionstatechangeHandler\n );\n this.peerConnection.addEventListener(\n 'iceconnectionstatechange',\n this.oniceconnectionstatechangeHandler\n );\n\n // Signaling state changes\n this.peerConnection.removeEventListener(\n 'connectionstatechange',\n this.onconnectionstatechangeHandler\n );\n this.peerConnection.addEventListener(\n 'connectionstatechange',\n this.onconnectionstatechangeHandler\n );\n\n this.peerConnection.removeEventListener(\n 'signalingstatechange',\n this.onsignalingstatechangeHandler\n );\n\n this.peerConnection.addEventListener(\n 'signalingstatechange',\n this.onsignalingstatechangeHandler\n );\n }\n\n private negotiationEnded() {\n this._isNegotiating$.next(false);\n }\n\n public restarIce(): void {\n this.peerConnection?.restartIce();\n }\n\n /**\n * Trigger an ICE restart through the existing negotiation pipeline.\n *\n * This creates an offer with iceRestart: true and goes through the full\n * SDP pipeline (setLocalDescription → ICE gathering → localDescription$ emission).\n * The caller should NOT send the SDP manually — the existing\n * setupLocalDescriptionHandler in VertoManager will pick up the emission\n * from localDescription$ and send it as a verto.modify.\n *\n * Unlike calling pc.createOffer/setLocalDescription directly, this method:\n * - Sets _isNegotiating$ so ICEGatheringController arms its timers\n * - Waits for ICE gathering to complete before localDescription$ emits\n * - Goes through setLocalDescriptionBefore() for any SDP munging\n */\n public async triggerIceRestart(relayOnly?: boolean): Promise<void> {\n if (!this.peerConnection) {\n throw new DependencyError('RTCPeerConnection is not initialized');\n }\n\n // Tier 3: constrain to relay candidates only (TURN servers).\n // setConfiguration() updates the ICE transport policy for the next gather.\n const policyChanged = relayOnly && !this.options.relayOnly;\n if (policyChanged) {\n try {\n this.peerConnection.setConfiguration({\n ...this.peerConnection.getConfiguration(),\n iceTransportPolicy: 'relay'\n });\n logger.debug('[RTCPeerConnectionController] ICE transport policy set to relay-only');\n } catch (error) {\n logger.warn('[RTCPeerConnectionController] Failed to set relay-only policy:', error);\n }\n }\n\n this.setupEventListeners();\n this._isNegotiating$.next(true);\n\n logger.debug(\n `[RTCPeerConnectionController] Triggering ICE restart${relayOnly ? ' (relay-only)' : ''}.`\n );\n\n try {\n const offer = await this.peerConnection.createOffer({ iceRestart: true });\n await this.setLocalDescription(offer);\n } catch (error) {\n logger.error('[RTCPeerConnectionController] ICE restart offer failed:', error);\n this._errors$.next(toError(error));\n this.negotiationEnded();\n // Restore original policy on failure\n if (policyChanged) {\n this.restoreIceTransportPolicy();\n }\n throw error;\n }\n // localDescription$ will emit after ICE gathering completes.\n // Restore original policy after gathering so future restarts aren't stuck on relay.\n if (policyChanged) {\n this.restoreIceTransportPolicy();\n }\n }\n\n private restoreIceTransportPolicy(): void {\n try {\n this.peerConnection?.setConfiguration({\n ...this.peerConnection.getConfiguration(),\n iceTransportPolicy: this.options.relayOnly ? 'relay' : 'all'\n });\n logger.debug('[RTCPeerConnectionController] ICE transport policy restored');\n } catch (error) {\n logger.warn('[RTCPeerConnectionController] Failed to restore ICE transport policy:', error);\n }\n }\n /**\n * Setup track handling for remote tracks.\n */\n private async setupTrackHandling(): Promise<void> {\n if (!this.peerConnection) {\n throw new DependencyError('RTCPeerConnection is not initialized');\n }\n\n await this.setupLocalTracks();\n\n await this.setupRemoteTracks();\n }\n\n // eslint-disable-next-line complexity\n private async setupLocalTracks(): Promise<void> {\n logger.debug('[RTCPeerConnectionController] Setting up local tracks/transceivers.');\n const localStream = this.localStream ?? (await this.localStreamController.buildLocalStream());\n\n if (this.transceiverController?.useAddStream ?? false) {\n logger.warn(\n '[RTCPeerConnectionController] Using deprecated addStream API to add local stream.'\n );\n //@ts-expect-error -- Ignore -- useAddStream checked if the deprecated API should be used\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n this.peerConnection?.addStream(localStream);\n // In case the browser doesn't fire negotiationneeded automatically\n if (!this.isNegotiating) {\n logger.debug(\n '[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.'\n );\n this.negotiationNeeded$.next();\n }\n return;\n }\n\n for (const kind of ['audio', 'video']) {\n const tracks = (\n kind === 'audio' ? localStream.getAudioTracks() : localStream.getVideoTracks()\n ).map((track, index) => ({ index, track }));\n for (const { index, track } of tracks) {\n this.localStreamController.addTrackEndedListener(track);\n if (this.transceiverController?.useAddTransceivers ?? false) {\n const transceivers =\n (kind === 'audio'\n ? this.transceiverController?.audioTransceivers\n : this.transceiverController?.videoTransceivers) ?? [];\n await this.transceiverController?.setupTransceiverSender(\n track,\n localStream,\n transceivers[index]\n );\n } else {\n logger.debug(\n `[RTCPeerConnectionController] Using addTrack for local ${kind} track:`,\n track.id\n );\n this.peerConnection?.addTrack(track, localStream);\n }\n }\n }\n }\n private async getUserMedia(constraints: MediaStreamConstraints): Promise<MediaStream> {\n const mediaDevices = this.options.webRTCApiProvider?.mediaDevices ?? navigator.mediaDevices;\n return mediaDevices.getUserMedia(constraints);\n }\n\n private async getDisplayMedia(options: DisplayMediaStreamOptions): Promise<MediaStream> {\n const mediaDevices = this.options.webRTCApiProvider?.mediaDevices ?? navigator.mediaDevices;\n if (!mediaDevices.getDisplayMedia) {\n throw new DependencyError('getDisplayMedia is not supported by the current WebRTC provider');\n }\n return mediaDevices.getDisplayMedia(options);\n }\n private async setupRemoteTracks(): Promise<void> {\n if (!this.peerConnection) {\n throw new DependencyError('RTCPeerConnection is not initialized');\n }\n this.peerConnection.ontrack = (event) => {\n logger.debug('[RTCPeerConnectionController] Remote track received:', event.track.kind);\n\n if (event.streams[0]) {\n this._remoteStream$.next(event.streams[0]);\n } else {\n const existingTracks = this._remoteStream$.value?.getTracks() ?? [];\n const newStream = new MediaStream([...existingTracks, event.track]);\n this._remoteStream$.next(newStream);\n }\n };\n\n await this.transceiverController?.setupRemoteTransceivers(this.type);\n }\n\n public async restoreTrackSender(kind: 'audio' | 'video' | 'both'): Promise<void> {\n await this.transceiverController?.restoreTrackSender(kind);\n }\n /**\n * Add a local media track to the peer connection.\n * @param track - The MediaStreamTrack to add\n */\n public addLocalTrack(track: MediaStreamTrack): void {\n if (!this.peerConnection) {\n const error = new DependencyError('RTCPeerConnection is not initialized');\n this._errors$.next(error);\n throw error;\n }\n\n try {\n // Add track to local stream controller\n const localStream = this.localStreamController.addTrack(track);\n\n // Add track to peer connection\n this.peerConnection.addTrack(track, localStream);\n\n logger.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);\n } catch (error) {\n logger.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);\n this._errors$.next(toError(error));\n throw error;\n }\n }\n /**\n * Remove a local media track from the peer connection.\n * @param trackId - The ID of the track to remove\n */\n public removeLocalTrack(trackId: string): void {\n if (!this.peerConnection) {\n const error = new DependencyError('RTCPeerConnection is not initialized');\n this._errors$.next(error);\n throw error;\n }\n\n const sender = this.peerConnection.getSenders().find((sender) => sender.track?.id === trackId);\n if (!sender) {\n logger.debug(`[RTCPeerConnectionController] track not found: ${trackId}`);\n return;\n }\n\n try {\n // Remove sender from peer connection\n this.peerConnection.removeTrack(sender);\n\n // Remove track from local stream controller\n this.localStreamController.removeTrack(trackId);\n\n logger.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);\n } catch (error) {\n logger.error(\n `[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`,\n error\n );\n this._errors$.next(toError(error));\n throw error;\n }\n }\n /**\n * Replace all existing media tracks with a new media track.\n * Convenience method for single-track scenarios.\n * @param track - The MediaStreamTrack to set\n */\n public setLocalTrack(track: MediaStreamTrack): void {\n // Remove all existing media tracks\n const existingTracks = [\n ...(track.kind === 'audio'\n ? this.localStreamController.localAudioTracks\n : this.localStreamController.localVideoTracks)\n ];\n for (const existingTrack of existingTracks) {\n this.removeLocalTrack(existingTrack.id);\n }\n\n // Add the new track\n this.addLocalTrack(track);\n }\n public async updateSendersConstraints(\n kind: 'audio' | 'video',\n constraints?: MediaTrackConstraints\n ): Promise<void> {\n await this.transceiverController?.updateSendersConstraints(kind, constraints);\n }\n\n /**\n * Replace the current audio track with a new one using the given constraints.\n * Used for server-pushed audio constraint changes where applyConstraints\n * fails on iOS Safari. Stops the current track, acquires a new one via\n * getUserMedia, and replaces the sender track.\n */\n public async replaceAudioTrackWithConstraints(constraints: MediaTrackConstraints): Promise<void> {\n const senders = this.peerConnection\n ?.getSenders()\n .filter((s) => s.track?.kind === 'audio' && s.track.readyState === 'live');\n\n if (!senders || senders.length === 0) {\n logger.warn('[RTCPeerConnectionController] No live audio sender to replace');\n return;\n }\n\n for (const sender of senders) {\n const oldTrack = sender.track;\n if (!oldTrack) continue;\n\n // Merge new constraints with current deviceId\n const currentSettings = oldTrack.getSettings();\n const { deviceId } = currentSettings;\n const mergedConstraints: MediaTrackConstraints = {\n ...oldTrack.getConstraints(),\n ...constraints,\n ...(deviceId ? { deviceId: { exact: deviceId } } : {})\n };\n\n // Stop old track\n const trackId = oldTrack.id;\n oldTrack.stop();\n this.localStreamController.removeTrack(trackId);\n\n // Acquire new track\n const stream = await this.getUserMedia({ audio: mergedConstraints });\n const newTrack = stream.getAudioTracks()[0];\n\n await sender.replaceTrack(newTrack);\n this.localStreamController.addTrack(newTrack);\n logger.debug(\n `[RTCPeerConnectionController] Audio track replaced for server-pushed params. New track: ${newTrack.id}`\n );\n }\n }\n\n /**\n * Clean up resources and close the peer connection.\n * Completes all observables to prevent memory leaks.\n */\n public destroy(): void {\n logger.debug(\n `[RTCPeerConnectionController] Destroying RTCPeerConnectionController. ${this.propose}`\n );\n this.removeConnectionTimer();\n this._iceGatheringController?.destroy();\n this.localStreamController.destroy();\n this.transceiverController?.destroy();\n\n // Close peer connection\n if (this.peerConnection) {\n this.stopRemoteTracks();\n this.removeAllListeners();\n this.peerConnection.close();\n this.peerConnection = undefined;\n }\n\n // Call parent destroy to clean up subscriptions and complete destroyed$\n super.destroy();\n }\n private removeAllListeners() {\n if (this.peerConnection) {\n this.peerConnection.removeEventListener(\n 'icegatheringstatechange',\n this.onicegatheringstatechangeHandler\n );\n this.peerConnection.removeEventListener(\n 'iceconnectionstatechange',\n this.oniceconnectionstatechangeHandler\n );\n this.peerConnection.removeEventListener(\n 'connectionstatechange',\n this.onconnectionstatechangeHandler\n );\n this.peerConnection.removeEventListener(\n 'signalingstatechange',\n this.onsignalingstatechangeHandler\n );\n this.peerConnection.removeEventListener('negotiationneeded', this.onnegotiationneededHandler);\n }\n }\n\n private stopRemoteTracks() {\n const remoteStream = this._remoteStream$.value;\n remoteStream?.getTracks().forEach((track: MediaStreamTrack) => {\n logger.debug(`[RTCPeerConnectionController] Stopping remote track: ${track.kind}`);\n track.stop();\n });\n }\n\n public get mediaDirections(): {\n audio: RTCRtpTransceiverDirection;\n video: RTCRtpTransceiverDirection;\n } {\n return (\n this.transceiverController?.getMediaDirections() ??\n this._remoteOfferMediaDirections ?? {\n audio: 'inactive',\n video: 'inactive'\n }\n );\n }\n protected async _setRemoteDescription(params: RTCSessionDescriptionInit): Promise<void> {\n if (!this.peerConnection) {\n throw new DependencyError('RTCPeerConnection is not initialized');\n }\n\n const finalRemote = await this.setRemoteDescriptionBefore(params.sdp);\n\n const answer: RTCSessionDescriptionInit = {\n ...params,\n sdp: finalRemote\n };\n logger.debug('[RTCPeerConnectionController] Setting remote description:', answer);\n return this.peerConnection.setRemoteDescription(answer);\n }\n}\n","// =============================================================================\n// VERTO TYPE GUARDS\n// =============================================================================\n// This file contains type guards for Verto protocol types.\n\nimport { hasProperty, isObject } from './base.guards';\n\nimport type { VertoMethod } from '../../types/rpc.types';\nimport type { TypeGuard } from '../types/base';\nimport type { WebrtcMessagePayload } from '../types/events';\nimport type {\n VertoAnswerMessage,\n VertoAnswerParams,\n VertoAnswerResultMessage,\n VertoAttachMessage,\n VertoAttachParams,\n VertoByeMessage,\n VertoByeParams,\n VertoInviteMessage,\n VertoInviteParams,\n VertoMediaMessage,\n VertoMediaParams,\n VertoMediaParamsMessage,\n VertoMediaParamsParams,\n VertoMethodMessage,\n VertoPingMessage,\n VertoPingParams,\n VertoPongMessage,\n VertoPongParams\n} from '../types/verto';\n\n// =============================================================================\n// VERTO MESSAGE TYPE GUARDS\n// =============================================================================\n\nexport function isVertoMethodMessage(value: unknown): value is VertoMethodMessage {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'id')\n );\n}\n\nexport function isVertoAnswerMessage(value: unknown): value is VertoAnswerMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return msg.method === 'verto.answer' && isObject(msg.params) && hasProperty(msg.params, 'callID');\n}\n\nexport function isVertoMediaMessage(value: unknown): value is VertoMediaMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return (\n msg.method === 'verto.media' &&\n isObject(msg.params) &&\n hasProperty(msg.params, 'callID') &&\n hasProperty(msg.params, 'sdp')\n );\n}\n\nexport function isVertoMediaParamsMessage(value: unknown): value is VertoMediaParamsMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return (\n msg.method === 'verto.mediaParams' &&\n isObject(msg.params) &&\n hasProperty(msg.params, 'mediaParams')\n );\n}\n\nexport function isVertoPingMessage(value: unknown): value is VertoPingMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return msg.method === 'verto.ping';\n}\n\nexport function isVertoPongMessage(value: unknown): value is VertoPongMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return msg.method === 'verto.pong';\n}\n\nexport function isVertoInviteMessage(value: unknown): value is VertoInviteMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return (\n msg.method === 'verto.invite' &&\n isObject(msg.params) &&\n hasProperty(msg.params, 'sdp') &&\n hasProperty(msg.params, 'callID')\n );\n}\n\nexport function isVertoByeMessage(value: unknown): value is VertoByeMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return msg.method === 'verto.bye';\n}\n\nexport function isVertoAnswerResultMessage(value: unknown): value is VertoAnswerResultMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return (\n isObject(msg.result) &&\n hasProperty(msg.result, 'method') &&\n msg.result.method === 'verto.answer'\n );\n}\n\nexport function isVertoAttachMessage(value: unknown): value is VertoAttachMessage {\n if (!isVertoMethodMessage(value)) return false;\n const msg = value as unknown as Record<string, unknown>;\n return msg.method === 'verto.attach';\n}\n\n// =============================================================================\n// WEBRTC MESSAGE EVENT DATA (inner params.params) TYPE GUARDS\n// =============================================================================\n\nexport function isVertoAnswerInnerParams(value: unknown): value is WebrtcMessagePayload & {\n method: 'verto.answer';\n params: VertoAnswerParams;\n} {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'method') &&\n value.method === 'verto.answer' &&\n isObject(value.params) &&\n hasProperty(value.params, 'callID')\n );\n}\n\nexport function isVertoMediaInnerParams(value: unknown): value is WebrtcMessagePayload & {\n method: 'verto.media';\n params: VertoMediaParams;\n} {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'method') &&\n value.method === 'verto.media' &&\n isObject(value.params) &&\n hasProperty(value.params, 'callID') &&\n hasProperty(value.params, 'sdp')\n );\n}\n\nexport function isVertoMediaParamsInnerParams(value: unknown): value is WebrtcMessagePayload & {\n method: 'verto.mediaParams';\n params: VertoMediaParamsParams;\n} {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'method') &&\n value.method === 'verto.mediaParams' &&\n isObject(value.params) &&\n hasProperty(value.params, 'mediaParams')\n );\n}\n\nexport function isVertoPingInnerParams(value: unknown): value is WebrtcMessagePayload & {\n method: 'verto.ping';\n params: VertoPingParams;\n} {\n return (\n isObject(value) &&\n hasProperty(value, 'jsonrpc') &&\n value.jsonrpc === '2.0' &&\n hasProperty(value, 'method') &&\n value.method === 'verto.ping'\n );\n}\n\n// =============================================================================\n// VERTO PARAMS TYPE GUARDS (for use with filterAs on Verto method params)\n// =============================================================================\n\nexport function isVertoAnswerParamsGuard(value: unknown): value is VertoAnswerParams {\n return isObject(value) && hasProperty(value, 'callID') && typeof value.callID === 'string';\n}\n\nexport function isVertoMediaSdpParamsGuard(value: unknown): value is VertoMediaParams {\n return (\n isObject(value) &&\n hasProperty(value, 'callID') &&\n typeof value.callID === 'string' &&\n hasProperty(value, 'sdp') &&\n typeof value.sdp === 'string'\n );\n}\n\nexport function isVertoMediaParamsParamsGuard(value: unknown): value is VertoMediaParamsParams {\n return (\n isObject(value) &&\n hasProperty(value, 'callID') &&\n typeof value.callID === 'string' &&\n hasProperty(value, 'mediaParams') &&\n isObject(value.mediaParams)\n );\n}\n\nexport function isVertoPingParamsGuard(value: unknown): value is VertoPingParams {\n return (\n isObject(value) &&\n hasProperty(value, 'callID') &&\n typeof value.callID === 'string' &&\n hasProperty(value, 'dialogParams') &&\n isObject(value.dialogParams)\n );\n}\n\nexport function isVertoPongParamsGuard(value: unknown): value is VertoPongParams {\n return isVertoPingParamsGuard(value);\n}\n\nexport function isVertoInviteParamsGuard(value: unknown): value is VertoInviteParams {\n return (\n isObject(value) &&\n hasProperty(value, 'dialogParams') &&\n isObject(value.dialogParams) &&\n hasProperty(value, 'sdp') &&\n typeof value.sdp === 'string'\n );\n}\n\nexport function isVertoByeParamsGuard(value: unknown): value is VertoByeParams {\n return (\n isObject(value) &&\n hasProperty(value, 'callID') &&\n typeof value.callID === 'string' &&\n hasProperty(value, 'cause') &&\n typeof value.cause === 'string'\n );\n}\n\nexport function isVertoAttachParamsGuard(value: unknown): value is VertoAttachParams {\n return (\n isObject(value) &&\n hasProperty(value, 'callID') &&\n typeof value.callID === 'string' &&\n hasProperty(value, 'callee_id_number') &&\n typeof value.callee_id_number === 'string' &&\n hasProperty(value, 'callee_id_name') &&\n typeof value.callee_id_name === 'string' &&\n hasProperty(value, 'caller_id_number') &&\n typeof value.caller_id_number === 'string' &&\n hasProperty(value, 'caller_id_name') &&\n typeof value.caller_id_name === 'string'\n );\n}\n\n// =============================================================================\n// VERTO METHOD TYPE MAPPING\n// =============================================================================\n\nexport const VertoMethodTypeMap = {\n 'verto.answer': isVertoAnswerMessage,\n 'verto.media': isVertoMediaMessage,\n 'verto.mediaParams': isVertoMediaParamsMessage,\n 'verto.ping': isVertoPingMessage,\n 'verto.pong': isVertoPongMessage,\n 'verto.invite': isVertoInviteMessage,\n 'verto.bye': isVertoByeMessage\n} as const satisfies Partial<Record<VertoMethod, TypeGuard<VertoMethodMessage>>>;\n\nexport type VertoMethodType = keyof typeof VertoMethodTypeMap;\n\n/**\n * Gets the appropriate type guard for a Verto method.\n */\nexport function getVertoMethodGuard(method: string): TypeGuard<VertoMethodMessage> | undefined {\n return VertoMethodTypeMap[method as VertoMethodType];\n}\n\n// =============================================================================\n// VERTO PARAMS TYPE MAPPING\n// =============================================================================\n\nexport const VertoParamsTypeMap = {\n 'verto.answer': isVertoAnswerParamsGuard,\n 'verto.media': isVertoMediaSdpParamsGuard,\n 'verto.mediaParams': isVertoMediaParamsParamsGuard,\n 'verto.ping': isVertoPingParamsGuard,\n 'verto.pong': isVertoPongParamsGuard,\n 'verto.invite': isVertoInviteParamsGuard,\n 'verto.attach': isVertoAttachParamsGuard,\n 'verto.bye': isVertoByeParamsGuard\n} as const satisfies Partial<Record<VertoMethod, TypeGuard<unknown>>>;\n\n/**\n * Gets the appropriate params type guard for a Verto method.\n */\nexport function getVertoParamsGuard(\n method: string\n):\n | TypeGuard<\n | VertoAnswerParams\n | VertoMediaParams\n | VertoMediaParamsParams\n | VertoPingParams\n | VertoPongParams\n | VertoInviteParams\n | VertoByeParams\n | VertoAttachParams\n >\n | undefined {\n return VertoParamsTypeMap[method as VertoMethodType];\n}\n","/* eslint-disable max-lines */\nimport {\n filter,\n firstValueFrom,\n map,\n merge,\n race,\n startWith,\n take,\n takeUntil,\n timeout\n} from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { PreferencesContainer } from '../containers/PreferencesContainer';\nimport { RTCPeerConnectionController } from '../controllers/RTCPeerConnectionController';\nimport { INVITE_VERSION } from '../core/constants';\nimport { DependencyError, InvalidParams, JSONRPCError, VertoPongError } from '../core/errors';\nimport {\n VertoAnswer,\n VertoBye,\n VertoByeCauseCodes,\n VertoInfo,\n VertoInvite,\n VertoModify,\n VertoPong,\n WebrtcVerto\n} from '../core/RPCMessages';\nimport { isCallJoinedPayload } from '../core/RPCMessages/guards/events.guards';\nimport {\n isVertoAnswerInnerParams,\n isVertoAttachMessage,\n isVertoByeMessage,\n isVertoInviteMessage,\n isVertoMediaInnerParams,\n isVertoMediaParamsInnerParams,\n isVertoPingInnerParams\n} from '../core/RPCMessages/guards/verto.guards';\nimport { filterAs } from '../operators';\nimport { filterNull } from '../operators/filterNull';\nimport { getValueFrom } from '../utils/getValueFrom';\nimport { getLogger } from '../utils/logger';\n\nimport type { AttachManager } from './AttachManager';\nimport type { WebRTCCall } from '../core/entities/Call';\nimport type { VertoRPCMessage } from '../core/RPCMessages';\nimport type {\n ExecuteVertoOptions,\n ScreenShareStatus,\n SignalingStatus,\n TransferOptions,\n WebRTCVertoManagerOptions\n} from './types/verto-manager.types';\nimport type { JSONRPCResponse } from '../core/RPCMessages/types/base';\nimport type { CallJoinedPayload } from '../core/RPCMessages/types/events';\nimport type {\n VertoAnswerParams,\n VertoAttachParams,\n VertoByeCause,\n VertoByeParams,\n VertoMediaParams,\n VertoMediaParamsParams,\n VertoPingParams\n} from '../core/RPCMessages/types/verto';\nimport type { RTCPeerConnectionPropose } from '../core/types/call.types';\nimport type { MediaOptions, MediaDirections } from '../core/types/media.types';\nimport type { VertoMethod } from '../core/types/rpc.types';\nimport type { WebRTCApiProvider } from '../dependencies/interfaces';\nimport type { DeviceController } from '../interfaces/DeviceController';\nimport type { WebRTCVerto } from '../interfaces/WebRTCVerto';\nimport type { BehaviorSubject, Observable } from 'rxjs';\n\nconst logger = getLogger();\nexport abstract class VertoManager extends Destroyable {\n protected callSession?: WebRTCCall;\n\n constructor(callSession?: WebRTCCall) {\n super();\n this.callSession = callSession;\n }\n\n public destroy(): void {\n this.callSession = undefined;\n super.destroy();\n }\n}\nexport class WebRTCVertoManager extends VertoManager implements WebRTCVerto {\n public mediaDirections$!: Observable<MediaDirections>;\n public localStream$!: Observable<MediaStream>;\n public remoteStream$!: Observable<MediaStream>;\n private readonly onError?: (error: Error) => void;\n private readonly onModifyFailed?: () => void;\n private _rtcPeerConnections$ = this.createBehaviorSubject<RTCPeerConnectionController[]>([]);\n\n private _nodeId$: BehaviorSubject<string | null>;\n private _selfId$ = this.createBehaviorSubject<string | null>(null);\n private _signalingStatus$ = this.createReplaySubject<SignalingStatus>(1);\n private _screenShareStatus$ = this.createBehaviorSubject<ScreenShareStatus>('none');\n private _rtcPeerConnectionsMap = new Map<string, RTCPeerConnectionController>();\n private _screenShareId?: string;\n private _screenShareTimeoutMs = 50000;\n\n constructor(\n protected webRtcCallSession: WebRTCCall,\n private readonly attachManager: AttachManager,\n private readonly deviceController: DeviceController,\n private readonly webRTCApiProvider: WebRTCApiProvider,\n options: WebRTCVertoManagerOptions = {}\n ) {\n super(webRtcCallSession);\n this._nodeId$ = this.createBehaviorSubject<string | null>(options.nodeId ?? null);\n this.onError = options.onError;\n this.onModifyFailed = options.onModifyFailed;\n this.initSubscriptions();\n this.initMainPeerConnection();\n }\n async hold(): Promise<void> {\n const vertoModifyMessage = VertoModify({\n sessid: this.webRtcCallSession.id,\n dialogParams: {\n callID: this.webRtcCallSession.id\n },\n action: 'hold'\n });\n\n try {\n await this.executeVerto(vertoModifyMessage);\n } catch (error) {\n logger.warn(\n '[WebRTCManager] Call might already be disconnected, error sending Verto hold:',\n error\n );\n throw error;\n }\n }\n async unhold(): Promise<void> {\n const vertoModifyMessage = VertoModify({\n sessid: this.webRtcCallSession.id,\n dialogParams: {\n callID: this.webRtcCallSession.id\n },\n action: 'unhold'\n });\n try {\n await this.executeVerto(vertoModifyMessage);\n } catch (error) {\n logger.warn(\n '[WebRTCManager] Call might already be disconnected, error sending Verto unhold:',\n error\n );\n throw error;\n }\n }\n\n public get mediaDirections(): MediaDirections {\n return this.mainPeerConnection.mediaDirections;\n }\n\n public get rtcPeerConnections$(): Observable<RTCPeerConnectionController[]> {\n return this._rtcPeerConnections$.asObservable();\n }\n\n public get rtcPeerConnections(): RTCPeerConnectionController[] {\n return this._rtcPeerConnections$.value;\n }\n\n public get nodeId$(): Observable<string | null> {\n return this._nodeId$.asObservable();\n }\n\n public get selfId$(): Observable<string | null> {\n return this._selfId$.asObservable();\n }\n\n public get localStream(): MediaStream | null {\n return this._rtcPeerConnectionsMap.get(this.webRtcCallSession.id)?.localStream ?? null;\n }\n\n public get remoteStream(): MediaStream | null {\n return this._rtcPeerConnectionsMap.get(this.webRtcCallSession.id)?.remoteStream ?? null;\n }\n\n public get nodeId(): string | null {\n return this._nodeId$.value;\n }\n\n public get screenShareStatus(): ScreenShareStatus {\n return this._screenShareStatus$.value;\n }\n\n public get screenShareStatus$(): Observable<ScreenShareStatus> {\n return this._screenShareStatus$.asObservable();\n }\n\n public get mainPeerConnection(): RTCPeerConnectionController {\n const rtcPeerConnection = this._rtcPeerConnectionsMap.get(this.webRtcCallSession.id);\n if (!rtcPeerConnection) {\n throw new DependencyError('Main peer connection not found');\n }\n return rtcPeerConnection;\n }\n\n public get signalingStatus$(): Observable<SignalingStatus> {\n return this.cachedObservable('signalingStatus$', () =>\n merge(\n this._signalingStatus$.asObservable(),\n this.mainPeerConnection.connectionState$.pipe(\n filter((connectionState) =>\n ['connected', 'disconnected', 'failed'].includes(connectionState)\n )\n ) as Observable<SignalingStatus>\n )\n );\n }\n\n private initSubscriptions() {\n // Eagerly populate node_id and selfId from call.joined events.\n // During reattach, call.joined often arrives before the verto.invite\n // RPC response (CALL CREATED) which is the authoritative source for\n // these values. Populating them early prevents downstream RPCs\n // (e.g. call.layout.list) from failing with empty identifiers.\n this.subscribeTo(this.callJoinedEvent$, (event: CallJoinedPayload) => {\n const memberNodeId = event.room_session.members.find(\n (m) => m.call_id === event.call_id\n )?.node_id;\n if (memberNodeId) {\n this.setNodeIdIfNull(memberNodeId);\n }\n if (event.member_id) {\n this.setSelfIdIfNull(event.member_id);\n }\n });\n\n this.subscribeTo(this.vertoMedia$, (event: VertoMediaParams) => {\n logger.debug('[WebRTCManager] Received Verto media event (early media SDP):', event);\n this._signalingStatus$.next('ringing');\n const { sdp, callID } = event;\n const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callID);\n void rtcPeerConnController?.updateAnswerStatus({\n status: 'received',\n sdp: sdp\n });\n });\n\n this.subscribeTo(this.vertoAnswer$, (event: VertoAnswerParams) => {\n logger.debug('[WebRTCManager] Received Verto answer event:', event);\n this._signalingStatus$.next('connecting');\n const { sdp, callID } = event;\n const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callID);\n void rtcPeerConnController?.updateAnswerStatus({\n status: 'received',\n sdp: sdp\n });\n });\n\n this.subscribeTo(this.vertoMediaParams$, (event: VertoMediaParamsParams) => {\n logger.debug('[WebRTCManager] Received Verto mediaParams event:', event);\n\n const { mediaParams, callID } = event;\n const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callID);\n const { audio, video } = mediaParams;\n\n void (async () => {\n try {\n // For audio: use track replacement because applyConstraints fails on iOS Safari\n if (audio && rtcPeerConnController) {\n await rtcPeerConnController.replaceAudioTrackWithConstraints(audio);\n }\n // For video: use applyConstraints with fallback (from Section 16.2)\n if (video) {\n await rtcPeerConnController?.updateSendersConstraints('video', video);\n }\n // Emit mediaParamsUpdated on the Call\n this.webRtcCallSession.emitMediaParamsUpdated({\n audio,\n video,\n timestamp: Date.now()\n });\n } catch (error) {\n logger.warn('[WebRTCManager] Error applying server-pushed media params:', error);\n this.onError?.(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n }\n })();\n });\n\n this.subscribeTo(this.vertoPing$, (vertoPing: VertoPingParams) => {\n void this.attachManager.attach(this.buildAttachableCall());\n void this.sendVertoPong(vertoPing);\n });\n }\n\n /**\n * Set node_id/selfId only when the current value is null.\n *\n * During reattach, `call.joined` and `verto.answer` events can deliver\n * these identifiers before the `verto.invite` RPC response (`CALL CREATED`)\n * arrives. These methods let early events populate them eagerly so that\n * downstream RPC calls (e.g. `call.layout.list`) don't fail with empty\n * identifiers. `processInviteResponse()` remains the authoritative source\n * and always overwrites unconditionally.\n */\n private setNodeIdIfNull(nodeId: string): void {\n if (!this._nodeId$.value && nodeId) {\n logger.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);\n this._nodeId$.next(nodeId);\n }\n }\n\n private setSelfIdIfNull(selfId: string): void {\n if (!this._selfId$.value && selfId) {\n logger.debug(`[WebRTCManager] Early selfId set: ${selfId}`);\n this._selfId$.next(selfId);\n }\n }\n\n private async sendVertoPong(vertoPing: VertoPingParams) {\n try {\n const vertoPongMessage = VertoPong({\n ...vertoPing\n });\n await this.executeVerto(vertoPongMessage);\n } catch (error) {\n logger.warn('[WebRTCManager] Call might disconnect, error sending Verto pong:', error);\n this.onError?.(new VertoPongError(error));\n }\n }\n\n public async updateMediaConstraints(\n options: {\n audio?: MediaTrackConstraints;\n video?: MediaTrackConstraints;\n } = {}\n ): Promise<void> {\n const { audio, video } = options;\n try {\n if (audio) {\n await this.mainPeerConnection.updateSendersConstraints('audio', audio);\n }\n if (video) {\n await this.mainPeerConnection.updateSendersConstraints('video', video);\n }\n } catch (error) {\n logger.warn('[WebRTCManager] Error updating media constraints:', error);\n this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));\n throw error;\n }\n }\n\n public get selfId(): string | null {\n return this._selfId$.value;\n }\n\n /** Build an AttachableCall from the current call state. */\n private buildAttachableCall(idOverride?: string) {\n return {\n nodeId: this.nodeId ?? undefined,\n id: idOverride ?? this.webRtcCallSession.id,\n to: this.webRtcCallSession.to,\n mediaDirections: this.webRtcCallSession.mediaDirections\n };\n }\n\n /**\n * Request a video keyframe via RTCP PLI/FIR.\n *\n * Uses RTCRtpReceiver.requestKeyFrame() (Chrome 124+) to send a\n * Picture Loss Indication to the remote sender. This is a client-side\n * WebRTC operation — no server RPC is needed.\n *\n * Best-effort: logs a warning on failure, never emits on errors$.\n */\n public requestKeyframe(): void {\n try {\n const pc = this.mainPeerConnection.peerConnection;\n if (!pc) {\n logger.warn('[WebRTCManager] No peer connection for keyframe request');\n return;\n }\n\n const videoReceiver = pc.getReceivers().find((r) => r.track.kind === 'video');\n if (!videoReceiver) {\n logger.warn('[WebRTCManager] No video receiver for keyframe request');\n return;\n }\n\n // RTCRtpReceiver.requestKeyFrame() sends a PLI/FIR via RTCP (Chrome 124+)\n if (\n typeof (videoReceiver as unknown as { requestKeyFrame?: () => void }).requestKeyFrame ===\n 'function'\n ) {\n (videoReceiver as unknown as { requestKeyFrame: () => void }).requestKeyFrame();\n logger.debug('[WebRTCManager] Keyframe requested via RTCRtpReceiver.requestKeyFrame()');\n } else {\n logger.debug('[WebRTCManager] requestKeyFrame() not supported, skipping');\n }\n } catch (error) {\n logger.warn('[WebRTCManager] Keyframe request failed (non-fatal):', error);\n }\n }\n\n /**\n * Request an ICE restart via the controller's negotiation pipeline.\n *\n * Triggers an ICE restart offer on the controller, which goes through the\n * full SDP pipeline: createOffer → setLocalDescription → ICE gathering →\n * localDescription$ emission → setupLocalDescriptionHandler sends verto.modify.\n *\n * This ensures the SDP sent to the server has fully gathered ICE candidates,\n * real ports/IPs, and any configured SDP munging applied — matching the\n * same pipeline used for the initial verto.invite.\n */\n public async requestIceRestart(relayOnly?: boolean): Promise<void> {\n try {\n const controller = this.mainPeerConnection;\n if (!controller.peerConnection) {\n logger.warn('[WebRTCManager] No peer connection for ICE restart');\n return;\n }\n\n await controller.triggerIceRestart(relayOnly);\n logger.info(`[WebRTCManager] ICE restart initiated${relayOnly ? ' (relay-only)' : ''}`);\n } catch (error) {\n logger.error('[WebRTCManager] ICE restart failed:', error);\n throw error;\n }\n }\n\n /**\n * Request an ICE restart on ALL active peer connections (main + additional legs).\n *\n * Screen share and additional device legs each get their own ICE restart\n * via the controller's negotiation pipeline. The SDP flows through\n * localDescription$ → setupLocalDescriptionHandler → verto.modify,\n * ensuring ICE gathering completes before the offer is sent.\n *\n * @param relayOnly - If true, constrain to TURN relay candidates only (Tier 3).\n */\n public async requestIceRestartAll(relayOnly?: boolean): Promise<void> {\n const entries = Array.from(this._rtcPeerConnectionsMap.entries());\n for (const [id, controller] of entries) {\n try {\n if (!controller.peerConnection) {\n logger.debug(`[WebRTCManager] No peer connection for leg ${id}, skipping ICE restart`);\n continue;\n }\n\n await controller.triggerIceRestart(relayOnly);\n logger.info(\n `[WebRTCManager] ICE restart initiated for leg ${id}${relayOnly ? ' (relay-only)' : ''}`\n );\n } catch (error) {\n logger.warn(`[WebRTCManager] ICE restart failed for leg ${id}:`, error);\n }\n }\n }\n\n /**\n * Request a keyframe on video-receiving legs only.\n *\n * Screen share legs are send-only (getDisplayMedia) so they have no\n * video receiver to request a keyframe from — they are skipped.\n */\n public requestKeyframeAll(): void {\n for (const [id, controller] of this._rtcPeerConnectionsMap) {\n if (controller.isScreenShare) {\n logger.debug(`[WebRTCManager] Skipping keyframe for send-only screen share leg ${id}`);\n continue;\n }\n\n try {\n const pc = controller.peerConnection;\n if (!pc) continue;\n\n const videoReceiver = pc.getReceivers().find((r) => r.track.kind === 'video');\n if (!videoReceiver) continue;\n\n if (\n typeof (videoReceiver as unknown as { requestKeyFrame?: () => void }).requestKeyFrame ===\n 'function'\n ) {\n (videoReceiver as unknown as { requestKeyFrame: () => void }).requestKeyFrame();\n logger.debug(`[WebRTCManager] Keyframe requested for leg ${id}`);\n }\n } catch (error) {\n logger.warn(`[WebRTCManager] Keyframe request failed for leg ${id} (non-fatal):`, error);\n }\n }\n }\n\n private get callJoinedEvent$() {\n return this.webRtcCallSession.callEvent$.pipe(\n filter(isCallJoinedPayload),\n takeUntil(this.destroyed$)\n );\n }\n\n private get vertoMedia$() {\n return this.webRtcCallSession.webrtcMessages$.pipe(\n filterAs(isVertoMediaInnerParams, 'params'),\n takeUntil(this.destroyed$)\n );\n }\n\n private get vertoAnswer$() {\n return this.cachedObservable('vertoAnswer$', () =>\n this.webRtcCallSession.webrtcMessages$.pipe(\n filterAs(isVertoAnswerInnerParams, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n private get vertoMediaParams$() {\n return this.cachedObservable('vertoMediaParams$', () =>\n this.webRtcCallSession.webrtcMessages$.pipe(\n filterAs(isVertoMediaParamsInnerParams, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n private get vertoBye$() {\n return this.cachedObservable('vertoBye$', () =>\n this.webRtcCallSession.webrtcMessages$.pipe(\n filterAs(isVertoByeMessage, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n private get vertoAttach$() {\n return this.cachedObservable('vertoAttach$', () =>\n this.webRtcCallSession.webrtcMessages$.pipe(\n filterAs(isVertoAttachMessage, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n private get vertoPing$() {\n return this.cachedObservable('vertoPing$', () =>\n this.webRtcCallSession.webrtcMessages$.pipe(\n filterAs(isVertoPingInnerParams, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n private async executeVerto(\n message: VertoRPCMessage,\n optionals: ExecuteVertoOptions = {}\n ): Promise<JSONRPCResponse<unknown>> {\n const params = {\n callID: optionals.callID ?? this.webRtcCallSession.id,\n\n node_id: optionals.node_id ?? this._nodeId$.value ?? '',\n message,\n subscribe: optionals.subscribe\n };\n\n const webrtcVertoMessage = WebrtcVerto(params);\n\n const response = await this.webRtcCallSession.execute(webrtcVertoMessage);\n\n // Check for error at top level\n if (response.error) {\n const error = new JSONRPCError(\n response.error.code,\n response.error.message,\n response.error.data\n );\n this.onError?.(error);\n return response;\n }\n\n // Check for nested error in result.result (webrtc.verto wraps the inner response)\n const innerResult = getValueFrom<{ error?: { code: number; message: string; data?: unknown } }>(\n response,\n 'result.result'\n );\n if (innerResult?.error) {\n const error = new JSONRPCError(\n innerResult.error.code,\n innerResult.error.message,\n innerResult.error.data\n );\n this.onError?.(error);\n return response;\n }\n\n return response;\n }\n\n private async sendLocalDescription(\n message: VertoRPCMessage,\n rtcPeerConnController: RTCPeerConnectionController\n ): Promise<void> {\n const vertoMethod: VertoMethod = message.method;\n\n const optionalsParams = this.getSendLocalSDPOptionalParams(rtcPeerConnController, message);\n\n try {\n const response = await this.executeVerto(message, optionalsParams);\n\n switch (vertoMethod) {\n case 'verto.invite':\n this.processInviteResponse(response, rtcPeerConnController);\n break;\n case 'verto.modify':\n await this.processModifyResponse(response, rtcPeerConnController);\n break;\n default:\n }\n } catch (error) {\n // execute() can reject before executeVerto checks the response.\n // Route the error through onError so it reaches Call.emitError.\n logger.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);\n this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));\n if (vertoMethod === 'verto.modify') {\n this.onModifyFailed?.();\n }\n }\n }\n private async processModifyResponse(\n response: JSONRPCResponse<unknown>,\n rtcPeerConnController: RTCPeerConnectionController\n ) {\n if (!response.error) {\n const action = getValueFrom<string>(response, 'result.result.result.action');\n const sdp = getValueFrom<string>(response, 'result.result.result.sdp');\n if (action === 'updateMedia' && !!sdp) {\n try {\n await rtcPeerConnController.updateAnswerStatus({\n status: 'received',\n sdp\n });\n } catch (error) {\n logger.warn('[WebRTCManager] Error processing modify response:', error);\n const modifyError =\n error instanceof Error ? error : new Error(String(error), { cause: error });\n this.onError?.(modifyError);\n }\n }\n }\n }\n\n private processInviteResponse(\n response: JSONRPCResponse<unknown>,\n rtcPeerConnController: RTCPeerConnectionController\n ) {\n if (\n !response.error &&\n getValueFrom(response, 'result.result.result.message') === 'CALL CREATED'\n ) {\n this._signalingStatus$.next('trying');\n this._nodeId$.next(getValueFrom<string>(response, 'result.node_id') ?? null);\n const memberId = getValueFrom<string>(response, 'result.result.result.memberID') ?? null;\n const callId = getValueFrom<string>(response, 'result.result.result.callID') ?? null;\n logger.debug('[WebRTCManager] Verto invite response:', { callId, memberId, response });\n\n this._selfId$.next(memberId);\n rtcPeerConnController.setMemberId(memberId);\n if (callId) {\n this.webRtcCallSession.addCallId(callId);\n void this.attachManager.attach(this.buildAttachableCall(callId));\n } else {\n logger.warn('[WebRTCManager] Cannot attach call, missing callId:', {\n nodeId: this.nodeId,\n callId\n });\n }\n logger.info('[WebRTCManager] Verto invite successful');\n logger.debug(\n `[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`\n );\n } else {\n logger.error('[WebRTCManager] Verto invite failed:', response);\n const inviteError = response.error\n ? new JSONRPCError(response.error.code, response.error.message, response.error.data)\n : new Error('Verto invite failed: unexpected response');\n this.onError?.(inviteError);\n }\n }\n\n private get RTCPeerConnectionConfig() {\n return {\n iceServers:\n this.webRtcCallSession.clientSession.iceServers ?? PreferencesContainer.instance.iceServers,\n relayOnly:\n PreferencesContainer.instance.relayOnly ||\n PreferencesContainer.instance.disableUdpIceServers,\n disableUdpIceServers: PreferencesContainer.instance.disableUdpIceServers,\n iceCandidateTimeout: PreferencesContainer.instance.iceCandidateTimeout,\n iceGatheringTimeout: PreferencesContainer.instance.iceGatheringTimeout\n };\n }\n\n private initMainPeerConnection() {\n //if (this.webRtcCallSession.direction === 'outbound') {\n const { options } = this.webRtcCallSession;\n const rtcPeerConnController = new RTCPeerConnectionController(\n {\n propose: 'main',\n callId: this.webRtcCallSession.id,\n audio: options.audio,\n video: options.video,\n inputAudioDeviceConstraints: options.inputAudioDeviceConstraints,\n inputVideoDeviceConstraints: options.inputVideoDeviceConstraints,\n inputAudioStream: options.inputAudioStream,\n inputVideoStream: options.inputVideoStream,\n receiveAudio: options.receiveAudio,\n receiveVideo: options.receiveVideo,\n webRTCApiProvider: this.webRTCApiProvider,\n preferredVideoCodecs: options.preferredVideoCodecs,\n preferredAudioCodecs: options.preferredAudioCodecs,\n stereo: options.stereo,\n ...this.RTCPeerConnectionConfig\n },\n options.initOffer,\n this.deviceController\n );\n this.setupLocalDescriptionHandler(rtcPeerConnController);\n this.setupVertoByeHandler();\n this.setupVertoAttachHandler();\n this.initObservables(rtcPeerConnController);\n this._rtcPeerConnectionsMap.set(rtcPeerConnController.id, rtcPeerConnController);\n this._rtcPeerConnections$.next(Array.from(this._rtcPeerConnectionsMap.values()));\n this.subscribeTo(rtcPeerConnController.errors$, (error) => {\n this.onError?.(error);\n });\n\n // For inbound calls, wait for answer()/reject() then trigger SDP answer creation\n if (options.initOffer) {\n void this.handleInboundAnswer(rtcPeerConnController);\n }\n }\n\n private async handleInboundAnswer(\n rtcPeerConnController: RTCPeerConnectionController\n ): Promise<void> {\n logger.debug('[WebRTCManager] Waiting for inbound call to be accepted or rejected');\n const vertoByeOrAccepted: boolean | VertoByeParams | null = await firstValueFrom(\n race(this.vertoBye$, this.webRtcCallSession.answered$).pipe(takeUntil(this.destroyed$))\n ).catch(() => null);\n\n if (vertoByeOrAccepted === null) {\n logger.debug('[WebRTCManager] Inbound answer handler aborted (destroyed).');\n return;\n }\n\n if (isVertoByeMessage(vertoByeOrAccepted)) {\n logger.info('[WebRTCManager] Inbound call ended by remote before answer.');\n this.callSession?.destroy();\n } else if (!vertoByeOrAccepted) {\n logger.info('[WebRTCManager] Inbound call rejected by user.');\n try {\n await this.bye('USER_BUSY');\n } finally {\n this._signalingStatus$.next('disconnected');\n this.callSession?.destroy();\n }\n } else {\n logger.debug('[WebRTCManager] Inbound call accepted, creating SDP answer');\n const answerOptions: MediaOptions | undefined = this.webRtcCallSession.answerMediaOptions;\n try {\n await rtcPeerConnController.acceptInbound(answerOptions);\n } catch (error) {\n logger.error('[WebRTCManager] Error creating inbound answer:', error);\n this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));\n }\n }\n }\n\n private setupVertoAttachHandler(): void {\n this.subscribeTo(this.vertoAttach$, async (vertoAttach: VertoAttachParams) => {\n logger.debug('[WebRTCManager] Received Verto attach event for existing call:', vertoAttach);\n const { callID } = vertoAttach;\n await this.attachManager.attach({\n nodeId: this.nodeId ?? undefined,\n id: callID,\n to: vertoAttach.callee_id_number,\n mediaDirections: {\n audio: 'sendrecv',\n // this might be changed in future to support video attach, but this feature was originally supposed in the non-video SDK.\n video: 'inactive'\n }\n });\n });\n }\n\n private initObservables(rtcPeerConnController: RTCPeerConnectionController): void {\n this.mediaDirections$ = rtcPeerConnController.connectionState$.pipe(\n filter((state) => state === 'connected'),\n map(() => rtcPeerConnController.mediaDirections),\n startWith(rtcPeerConnController.mediaDirections),\n takeUntil(this.destroyed$)\n );\n this.localStream$ = rtcPeerConnController.localStream$.pipe(\n filterNull(),\n takeUntil(this.destroyed$)\n );\n this.remoteStream$ = rtcPeerConnController.remoteStream$.pipe(\n filterNull(),\n takeUntil(this.destroyed$)\n );\n }\n private setupLocalDescriptionHandler(rtcPeerConnController: RTCPeerConnectionController): void {\n this.subscribeTo(\n // watch for local description from the RTCPeerConnection and send it to remote peer\n rtcPeerConnController.localDescription$.pipe(\n // Filter out null descriptions\n filter((description): description is RTCSessionDescription => description !== null),\n takeUntil(this.destroyed$)\n ),\n (description) => {\n const { type, sdp } = description;\n const dialogParams = this.dialogParams(rtcPeerConnController);\n const initial = !rtcPeerConnController.firstSDPExchangeCompleted;\n if (type === 'answer') {\n {\n const vertoMessageRequest = VertoAnswer({\n dialogParams,\n sdp: sdp\n });\n void this.sendLocalDescriptionOnceAccepted(vertoMessageRequest, rtcPeerConnController);\n }\n } else if (initial) {\n const vertoMessageRequest = VertoInvite({\n dialogParams,\n sdp\n });\n void this.sendLocalDescription(vertoMessageRequest, rtcPeerConnController);\n } else {\n const vertoMessageRequest = VertoModify({\n dialogParams,\n sdp,\n action: 'updateMedia'\n });\n void this.sendLocalDescription(vertoMessageRequest, rtcPeerConnController);\n }\n }\n );\n }\n\n private setupVertoByeHandler() {\n this.subscribeTo(this.vertoBye$, () => {\n this._signalingStatus$.next('disconnected');\n void this.attachManager.detach(this.buildAttachableCall());\n this.callSession?.destroy();\n });\n }\n\n private getSendLocalSDPOptionalParams(\n rtcPeerConnController: RTCPeerConnectionController,\n vertoMessage: VertoRPCMessage\n ): ExecuteVertoOptions {\n let subscribe = undefined;\n const initial = !rtcPeerConnController.firstSDPExchangeCompleted;\n if (initial) {\n subscribe = [];\n if (rtcPeerConnController.isMainDevice) {\n subscribe.push(...PreferencesContainer.instance.inviteSubscribeMainDevice);\n } else if (rtcPeerConnController.isAdditionalDevice) {\n subscribe.push(...PreferencesContainer.instance.inviteSubscribeAdditionalDevice);\n } else if (rtcPeerConnController.isScreenShare) {\n subscribe.push(...PreferencesContainer.instance.inviteSubscribeScreenshare);\n }\n }\n const isInvite = isVertoInviteMessage(vertoMessage);\n const isReattach = isInvite && this.webRtcCallSession.options.reattach;\n const optionalsParams = {\n callID: rtcPeerConnController.id,\n // Fresh verto.invite must never include node_id (server assigns it).\n // Reattach invites MUST include node_id to route to the correct\n // FreeSWITCH instance that holds the existing call.\n node_id: isInvite && !isReattach ? '' : (this._nodeId$.value ?? ''),\n subscribe\n };\n return optionalsParams;\n }\n\n async sendLocalDescriptionOnceAccepted(\n vertoMessageRequest: VertoRPCMessage,\n rtcPeerConnectionController: RTCPeerConnectionController\n ): Promise<void> {\n logger.debug('[WebRTCManager] Waiting for call to be accepted or ended before sending answer');\n const vertoByeOrAccepted: boolean | VertoByeParams | null = await firstValueFrom(\n race(this.vertoBye$, this.webRtcCallSession.answered$).pipe(takeUntil(this.destroyed$))\n ).catch(() => null);\n\n if (vertoByeOrAccepted === null) {\n logger.debug('[WebRTCManager] Destroyed while waiting for call acceptance');\n return;\n }\n\n if (isVertoByeMessage(vertoByeOrAccepted)) {\n logger.info('[WebRTCManager] Call ended before answer was sent.');\n this.callSession?.destroy();\n } else if (!vertoByeOrAccepted) {\n logger.info('[WebRTCManager] Call was not accepted, sending verto.bye.');\n try {\n await this.bye('USER_BUSY');\n } finally {\n this._signalingStatus$.next('disconnected');\n this.callSession?.destroy();\n }\n } else {\n logger.debug('[WebRTCManager] Call accepted, sending answer');\n try {\n this._signalingStatus$.next('connecting');\n await this.sendLocalDescription(vertoMessageRequest, rtcPeerConnectionController);\n await rtcPeerConnectionController.updateAnswerStatus({\n status: 'sent'\n });\n await this.attachManager.attach(this.buildAttachableCall());\n } catch (error) {\n logger.error('[WebRTCManager] Error sending Verto answer:', error);\n this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));\n await rtcPeerConnectionController.updateAnswerStatus({\n status: 'failed'\n });\n }\n }\n }\n\n dialogParams(rtcPeerConnectionController: RTCPeerConnectionController): Record<string, unknown> {\n const memberId = rtcPeerConnectionController.memberId ?? this._selfId$.value ?? undefined;\n const attach =\n rtcPeerConnectionController.propose === 'main' &&\n !rtcPeerConnectionController.firstSDPExchangeCompleted &&\n this.webRtcCallSession.options.reattach;\n\n return {\n id: rtcPeerConnectionController.isMainDevice\n ? this.webRtcCallSession.id\n : rtcPeerConnectionController.id,\n destinationNumber: this.webRtcCallSession.to ?? this.webRtcCallSession.from,\n attach,\n reattaching: attach,\n callerName: this.webRtcCallSession.fromName,\n callerNumber: this.webRtcCallSession.from,\n remoteCallerName: this.webRtcCallSession.toName,\n remoteCallerNumber: this.webRtcCallSession.to,\n userVariables: {\n memberCallId: this.webRtcCallSession.id,\n memberId,\n ...this.webRtcCallSession.userVariables\n },\n screenShare: rtcPeerConnectionController.isScreenShare,\n additionalDevice: rtcPeerConnectionController.isAdditionalDevice,\n pingSupported: true,\n version: INVITE_VERSION\n };\n }\n\n public muteMainAudioInputDevice(): void {\n return this.mainPeerConnection.stopTrackSender('audio');\n }\n\n public muteMainVideoInputDevice(): void {\n return this.mainPeerConnection.stopTrackSender('video');\n }\n\n public async unmuteMainAudioInputDevice(): Promise<void> {\n return this.mainPeerConnection.restoreTrackSender('audio');\n }\n\n public async unmuteMainVideoInputDevice(): Promise<void> {\n return this.mainPeerConnection.restoreTrackSender('video');\n }\n\n public async addInputDevice(\n options: MediaOptions = { audio: false, video: true }\n ): Promise<string | undefined> {\n return this.initAdditionalPeerConnection('additional-device', options);\n }\n\n /**\n * Add a new input device to the main peer connection,\n * only if a device of the same kind is not present already.\n *\n * @see selectAudioInputDevice\n * @see selectVideoInputDevice\n * @param options - Media options specifying which input devices to add (defaults to audio only).\n */\n public async addMainInputDevices(options: MediaOptions = { audio: true }): Promise<void> {\n let deviceKind: 'audio' | 'video' | 'both' | undefined = undefined;\n\n const { mediaDirections } = this.mainPeerConnection;\n\n if (\n options.audio ??\n options.inputAudioDeviceConstraints ??\n (options.inputAudioStream && mediaDirections.audio.startsWith('send'))\n ) {\n deviceKind = 'audio';\n }\n if (\n options.video ??\n options.inputVideoDeviceConstraints ??\n (options.inputVideoStream && !mediaDirections.video.startsWith('send'))\n ) {\n deviceKind = deviceKind === 'audio' ? 'both' : 'video';\n }\n if (deviceKind) {\n this.mainPeerConnection.updateMediaDevicesOptions(options);\n await this.mainPeerConnection.restoreTrackSender(deviceKind);\n } else {\n const error = new InvalidParams('No valid device to be added');\n this.onError?.(error);\n throw error;\n }\n }\n\n public async addScreenMedia(options: MediaOptions = { audio: false }): Promise<void> {\n await this.initAdditionalPeerConnection('screenshare', options);\n }\n\n private async initAdditionalPeerConnection(\n propose: RTCPeerConnectionPropose,\n options: MediaOptions\n ): Promise<string | undefined> {\n let rtcPeerConnController: RTCPeerConnectionController | null = null;\n try {\n this._screenShareStatus$.next('starting');\n rtcPeerConnController = new RTCPeerConnectionController(\n {\n ...options,\n ...this.RTCPeerConnectionConfig,\n propose,\n webRTCApiProvider: this.webRTCApiProvider\n },\n undefined,\n this.deviceController\n );\n this.setupLocalDescriptionHandler(rtcPeerConnController);\n if (propose === 'screenshare') {\n this._screenShareId = rtcPeerConnController.id;\n }\n this._rtcPeerConnectionsMap.set(rtcPeerConnController.id, rtcPeerConnController);\n this._rtcPeerConnections$.next(Array.from(this._rtcPeerConnectionsMap.values()));\n this.subscribeTo(rtcPeerConnController.errors$, (error) => {\n this.onError?.(error);\n });\n await firstValueFrom(\n rtcPeerConnController.connectionState$.pipe(\n filter((state) => state === 'connected'),\n take(1),\n timeout(this._screenShareTimeoutMs),\n takeUntil(this.destroyed$)\n )\n );\n this._screenShareStatus$.next('started');\n logger.info('[WebRTCManager] Screen share started successfully.');\n return rtcPeerConnController.id;\n } catch (error) {\n logger.warn('[WebRTCManager] Error initializing additional peer connection:', error);\n this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));\n if (rtcPeerConnController) {\n rtcPeerConnController.destroy();\n }\n this._screenShareStatus$.next('none');\n }\n }\n\n public async removeInputDevices(id: string): Promise<void> {\n return this.removeAdditionalPeerConnection(id);\n }\n\n public removeMainInputDevice(options = { removeAudio: false, removeVideo: true }): void {\n let removeTrack: 'audio' | 'video' | 'both' | undefined = undefined;\n if (options.removeAudio) {\n removeTrack = 'audio';\n }\n if (options.removeVideo) {\n removeTrack = removeTrack === 'audio' ? 'both' : 'video';\n }\n\n if (removeTrack) {\n return this.mainPeerConnection.stopTrackSender(removeTrack, {\n updateTransceiverDirection: true\n });\n }\n }\n\n public async removeScreenMedia(): Promise<void> {\n if (!['starting', 'started'].includes(this._screenShareStatus$.value)) {\n logger.warn('[WebRTCManager] No active screen share to stop.');\n }\n if (!this._screenShareId) {\n logger.debug('[WebRTCManager] No screen share peer connection found.');\n return;\n }\n this._screenShareStatus$.next('stopping');\n await this.removeAdditionalPeerConnection(this._screenShareId);\n this._screenShareId = undefined;\n this._screenShareStatus$.next('none');\n }\n\n public async removeAdditionalPeerConnection(id: string): Promise<void> {\n const rtcPeerConnController = this._rtcPeerConnectionsMap.get(id);\n try {\n if (rtcPeerConnController) {\n await this.executeVertoBye(rtcPeerConnController);\n }\n } finally {\n rtcPeerConnController?.destroy();\n this._rtcPeerConnectionsMap.delete(id);\n this._rtcPeerConnections$.next(Array.from(this._rtcPeerConnectionsMap.values()));\n }\n }\n\n private async executeVertoBye(\n rtcPeerConnController: RTCPeerConnectionController,\n cause?: VertoByeCause\n ): Promise<void> {\n try {\n const causeParams = cause\n ? {\n cause: cause,\n causeCode: VertoByeCauseCodes[cause]\n }\n : {};\n\n await this.executeVerto(\n VertoBye({\n ...causeParams,\n dialogParams: this.dialogParams(rtcPeerConnController)\n })\n );\n } catch (error) {\n logger.warn(\n '[WebRTCManager] Call might already be disconnected, error sending Verto bye:',\n error\n );\n throw error;\n }\n }\n public async bye(cause?: VertoByeCause): Promise<void> {\n void this.attachManager.detach(this.buildAttachableCall());\n const rtcPeerConnController = this._rtcPeerConnectionsMap.get(this.webRtcCallSession.id);\n if (rtcPeerConnController) {\n await this.executeVertoBye(rtcPeerConnController, cause);\n }\n }\n\n public async sendDigits(dtmf: string): Promise<void> {\n const vertoInfoMessage = VertoInfo({\n sessid: this.webRtcCallSession.id,\n dialogParams: {\n callID: this.webRtcCallSession.id\n },\n dtmf\n });\n\n try {\n await this.executeVerto(vertoInfoMessage);\n } catch (error) {\n logger.warn('[WebRTCManager] Error sending DTMF digits:', error);\n throw error;\n }\n }\n\n public async transfer(options: TransferOptions): Promise<void> {\n const message = VertoModify({\n ...options,\n dialogParams: this.dialogParams(this.mainPeerConnection),\n action: 'transfer'\n });\n try {\n logger.debug('[WebRTCManager] Transferring call with options:', options);\n await this.executeVerto(message);\n } catch (error) {\n logger.error('[WebRTCManager] Error transferring call:', error);\n throw error;\n }\n }\n\n public destroy(): void {\n this._rtcPeerConnectionsMap.forEach((rtcPeerConnController) => {\n rtcPeerConnController.destroy();\n });\n this._rtcPeerConnectionsMap.clear();\n this._rtcPeerConnections$.complete();\n super.destroy();\n }\n}\n","/**\n * RTCStatsMonitor — per-call WebRTC stats polling, baseline calculation,\n * and network issue detection (audio/video packets, RTT, jitter, packet loss).\n *\n * Extends Destroyable for automatic RxJS cleanup.\n */\n\nimport {\n map,\n distinctUntilChanged,\n takeUntil,\n interval,\n switchMap,\n from,\n filter,\n EMPTY,\n catchError,\n scan,\n take,\n toArray,\n mergeMap\n} from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { getLogger } from '../utils/logger';\n\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface NetworkIssue {\n type:\n | 'no_inbound_audio'\n | 'no_inbound_video'\n | 'high_rtt'\n | 'high_packet_loss'\n | 'high_jitter'\n | 'ice_disconnected';\n severity: 'warning' | 'critical';\n timestamp: number;\n value?: number;\n threshold?: number;\n}\n\nexport interface NetworkMetrics {\n timestamp: number;\n audio: {\n packetsReceived: number;\n packetsLost: number;\n jitter: number;\n };\n video: {\n packetsReceived: number;\n packetsLost: number;\n };\n roundTripTime: number;\n availableOutgoingBitrate?: number;\n}\n\nexport interface RTCStatsMonitorConfig {\n /** Polling interval in milliseconds. */\n pollingIntervalMs?: number;\n /** Number of initial samples used to build baselines. */\n baselineSamples?: number;\n /** Max duration (ms) of no inbound audio packets before critical. */\n noAudioPacketThresholdMs?: number;\n /** Max duration (ms) of no inbound video packets before warning. */\n noVideoPacketThresholdMs?: number;\n /** RTT multiplier over baseline for warning. */\n rttSpikeWarningMultiplier?: number;\n /** RTT multiplier over baseline for critical. */\n rttSpikeCriticalMultiplier?: number;\n /** Packet-loss percentage for warning (e.g. 5 means 5%). */\n packetLossWarningPercent?: number;\n /** Packet-loss percentage for critical. */\n packetLossCriticalPercent?: number;\n /** Jitter multiplier over baseline for warning. */\n jitterSpikeMultiplier?: number;\n /** Rolling history window in seconds. */\n historyWindowSeconds?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_POLLING_INTERVAL_MS = 1000;\nconst DEFAULT_BASELINE_SAMPLES = 10;\nconst DEFAULT_NO_AUDIO_PACKET_THRESHOLD_MS = 2000;\nconst DEFAULT_NO_VIDEO_PACKET_THRESHOLD_MS = 3000;\nconst DEFAULT_RTT_SPIKE_WARNING_MULTIPLIER = 3;\nconst DEFAULT_RTT_SPIKE_CRITICAL_MULTIPLIER = 5;\nconst DEFAULT_PACKET_LOSS_WARNING_PERCENT = 5;\nconst DEFAULT_PACKET_LOSS_CRITICAL_PERCENT = 10;\nconst DEFAULT_JITTER_SPIKE_MULTIPLIER = 4;\nconst DEFAULT_HISTORY_WINDOW_SECONDS = 30;\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\ninterface RawSample {\n audioPacketsReceived: number;\n audioPacketsLost: number;\n audioJitter: number;\n videoPacketsReceived: number;\n videoPacketsLost: number;\n roundTripTime: number;\n availableOutgoingBitrate: number | undefined;\n timestamp: number;\n}\n\ninterface Baseline {\n rtt: number;\n jitter: number;\n ready: boolean;\n}\n\nfunction computePacketLossPercent(lost: number, received: number): number {\n const total = lost + received;\n if (total === 0) {\n return 0;\n }\n return (lost / total) * 100;\n}\n\n// Typed subsets of W3C RTCStats — only the fields we actually read.\n\ninterface InboundRtpStat {\n type: 'inbound-rtp';\n kind: 'audio' | 'video';\n packetsReceived?: number;\n packetsLost?: number;\n jitter?: number;\n}\n\ninterface CandidatePairStat {\n type: 'candidate-pair';\n state: string;\n nominated?: boolean;\n currentRoundTripTime?: number;\n availableOutgoingBitrate?: number;\n}\n\nfunction isInboundRtpStat(stat: unknown): stat is InboundRtpStat {\n return typeof stat === 'object' && stat !== null && 'type' in stat && stat.type === 'inbound-rtp';\n}\n\nfunction isCandidatePairStat(stat: unknown): stat is CandidatePairStat {\n return (\n typeof stat === 'object' && stat !== null && 'type' in stat && stat.type === 'candidate-pair'\n );\n}\n\n// ---------------------------------------------------------------------------\n// RTCStatsMonitor\n// ---------------------------------------------------------------------------\n\nexport class RTCStatsMonitor extends Destroyable {\n // Config (immutable after construction)\n private readonly pollingIntervalMs: number;\n private readonly baselineSampleCount: number;\n private readonly noAudioPacketThresholdMs: number;\n private readonly noVideoPacketThresholdMs: number;\n private readonly rttSpikeWarningMultiplier: number;\n private readonly rttSpikeCriticalMultiplier: number;\n private readonly packetLossWarningPercent: number;\n private readonly packetLossCriticalPercent: number;\n private readonly jitterSpikeMultiplier: number;\n private readonly historyWindowSeconds: number;\n\n // Polling state\n private running = false;\n\n // Packet-tracking for \"no packets\" detection\n private lastAudioPacketsReceived = 0;\n private lastAudioPacketChangeTime = 0;\n private lastVideoPacketsReceived = 0;\n private lastVideoPacketChangeTime = 0;\n private lastRoundTripTime = 0;\n private lastAvailableOutgoingBitrate: number | undefined = undefined;\n\n // Subjects\n private _sample$ = this.createReplaySubject<RawSample>(1);\n private _baseline$ = this.createBehaviorSubject<Baseline>({ rtt: 0, jitter: 0, ready: false });\n private _networkIssues$ = this.createBehaviorSubject<NetworkIssue[]>([]);\n private _networkMetrics$ = this.createBehaviorSubject<NetworkMetrics[]>([]);\n\n constructor(\n private readonly peerConnection: RTCPeerConnection,\n config: RTCStatsMonitorConfig = {}\n ) {\n super();\n\n this.pollingIntervalMs = config.pollingIntervalMs ?? DEFAULT_POLLING_INTERVAL_MS;\n this.baselineSampleCount = config.baselineSamples ?? DEFAULT_BASELINE_SAMPLES;\n this.noAudioPacketThresholdMs =\n config.noAudioPacketThresholdMs ?? DEFAULT_NO_AUDIO_PACKET_THRESHOLD_MS;\n this.noVideoPacketThresholdMs =\n config.noVideoPacketThresholdMs ?? DEFAULT_NO_VIDEO_PACKET_THRESHOLD_MS;\n this.rttSpikeWarningMultiplier =\n config.rttSpikeWarningMultiplier ?? DEFAULT_RTT_SPIKE_WARNING_MULTIPLIER;\n this.rttSpikeCriticalMultiplier =\n config.rttSpikeCriticalMultiplier ?? DEFAULT_RTT_SPIKE_CRITICAL_MULTIPLIER;\n this.packetLossWarningPercent =\n config.packetLossWarningPercent ?? DEFAULT_PACKET_LOSS_WARNING_PERCENT;\n this.packetLossCriticalPercent =\n config.packetLossCriticalPercent ?? DEFAULT_PACKET_LOSS_CRITICAL_PERCENT;\n this.jitterSpikeMultiplier = config.jitterSpikeMultiplier ?? DEFAULT_JITTER_SPIKE_MULTIPLIER;\n this.historyWindowSeconds = config.historyWindowSeconds ?? DEFAULT_HISTORY_WINDOW_SECONDS;\n }\n\n // -----------------------------------------------------------------------\n // Public observables\n // -----------------------------------------------------------------------\n\n /** Current list of detected network issues (empty array = healthy). */\n public get networkIssues$(): Observable<NetworkIssue[]> {\n return this._networkIssues$.asObservable();\n }\n\n /** Snapshot of current issues (defensive copy). */\n public get networkIssues(): NetworkIssue[] {\n return [...this._networkIssues$.value];\n }\n\n /** Simple boolean health indicator. */\n public get isNetworkHealthy$(): Observable<boolean> {\n return this.cachedObservable('isNetworkHealthy$', () =>\n this._networkIssues$.pipe(\n map((issues) => issues.length === 0),\n distinctUntilChanged(),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Whether the network is currently healthy. */\n public get isNetworkHealthy(): boolean {\n return this._networkIssues$.value.length === 0;\n }\n\n /** Rolling metrics history. */\n public get networkMetrics$(): Observable<NetworkMetrics[]> {\n return this._networkMetrics$.asObservable();\n }\n\n /** Snapshot of rolling metrics (defensive copy). */\n public get networkMetrics(): NetworkMetrics[] {\n return [...this._networkMetrics$.value];\n }\n\n /** Emits individual critical issues for the recovery pipeline. */\n public get criticalIssue$(): Observable<NetworkIssue> {\n return this.cachedObservable('criticalIssue$', () =>\n this._networkIssues$.pipe(\n mergeMap((issues) => from(issues.filter((i) => i.severity === 'critical'))),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Emits each raw stats sample extracted from the peer connection. */\n public get sample$(): Observable<RawSample> {\n return this._sample$.asObservable();\n }\n\n // -----------------------------------------------------------------------\n // Lifecycle\n // -----------------------------------------------------------------------\n\n public start(): void {\n if (this.running) {\n return;\n }\n this.running = true;\n const now = Date.now();\n this.lastAudioPacketChangeTime = now;\n this.lastVideoPacketChangeTime = now;\n\n logger.debug('[RTCStatsMonitor] Starting stats monitoring');\n\n // Poll peer connection stats on a fixed interval → extract raw samples\n this.subscribeTo(\n interval(this.pollingIntervalMs).pipe(\n filter(() => this.running),\n switchMap(() =>\n from(this.peerConnection.getStats()).pipe(\n catchError((err) => {\n logger.warn('[RTCStatsMonitor] Failed to get stats:', err);\n return EMPTY;\n })\n )\n ),\n filter(() => this.running),\n map((report) => this.extractSample(report))\n ),\n (sample) => this._sample$.next(sample)\n );\n\n // Baseline: collect first N samples, compute averages, emit once\n this.subscribeTo(\n this._sample$.pipe(\n take(this.baselineSampleCount),\n toArray(),\n map((samples) => ({\n rtt: samples.reduce((s, b) => s + b.roundTripTime, 0) / samples.length,\n jitter: samples.reduce((s, b) => s + b.audioJitter, 0) / samples.length,\n ready: true as const\n }))\n ),\n (baseline) => {\n logger.debug(\n `[RTCStatsMonitor] Baseline established: rtt=${baseline.rtt.toFixed(1)}ms, jitter=${baseline.jitter.toFixed(1)}ms`\n );\n this._baseline$.next(baseline);\n }\n );\n\n // Issue detection: carry previous sample via scan for delta-based packet loss\n this.subscribeTo(\n this._sample$.pipe(\n scan((acc, sample) => ({ prev: acc.current, current: sample }), {\n prev: null as RawSample | null,\n current: null as RawSample | null\n }),\n filter(\n (pair): pair is { prev: RawSample | null; current: RawSample } => pair.current !== null\n )\n ),\n ({ prev, current }) => {\n const now = current.timestamp;\n this.updatePacketTracking(current, now);\n const issues = this.detectIssues(current, prev, now);\n this._networkIssues$.next(issues);\n }\n );\n\n // Rolling metrics history via scan accumulator\n this.subscribeTo(\n this._sample$.pipe(\n scan((history, sample) => {\n const cutoff = sample.timestamp - this.historyWindowSeconds * 1000;\n const metrics: NetworkMetrics = {\n timestamp: sample.timestamp,\n audio: {\n packetsReceived: sample.audioPacketsReceived,\n packetsLost: sample.audioPacketsLost,\n jitter: sample.audioJitter\n },\n video: {\n packetsReceived: sample.videoPacketsReceived,\n packetsLost: sample.videoPacketsLost\n },\n roundTripTime: sample.roundTripTime,\n availableOutgoingBitrate: sample.availableOutgoingBitrate\n };\n return [...history.filter((m) => m.timestamp > cutoff), metrics];\n }, [] as NetworkMetrics[])\n ),\n (metrics) => this._networkMetrics$.next(metrics)\n );\n }\n\n public stop(): void {\n if (!this.running) {\n return;\n }\n this.running = false;\n logger.debug('[RTCStatsMonitor] Stopping stats monitoring');\n }\n\n public override destroy(): void {\n logger.debug('[RTCStatsMonitor] Destroying RTCStatsMonitor');\n this.stop();\n super.destroy();\n }\n\n // -----------------------------------------------------------------------\n // Sample extraction\n // -----------------------------------------------------------------------\n\n private extractSample(report: RTCStatsReport): RawSample {\n let audioPacketsReceived = 0;\n let audioPacketsLost = 0;\n let audioJitter = 0;\n let videoPacketsReceived = 0;\n let videoPacketsLost = 0;\n let roundTripTime = 0;\n let availableOutgoingBitrate: number | undefined;\n\n report.forEach((stat: unknown) => {\n if (isInboundRtpStat(stat)) {\n if (stat.kind === 'audio') {\n // Accumulate across all audio inbound-rtp entries\n audioPacketsReceived += stat.packetsReceived ?? this.lastAudioPacketsReceived;\n audioPacketsLost += stat.packetsLost ?? 0;\n audioJitter = Math.max(audioJitter, (stat.jitter ?? 0) * 1000); // seconds → ms, take worst\n } else {\n // Accumulate across all video inbound-rtp entries (simulcast layers)\n videoPacketsReceived += stat.packetsReceived ?? this.lastVideoPacketsReceived;\n videoPacketsLost += stat.packetsLost ?? 0;\n }\n }\n\n if (isCandidatePairStat(stat) && stat.state === 'succeeded' && stat.nominated) {\n roundTripTime = stat.currentRoundTripTime\n ? stat.currentRoundTripTime * 1000\n : this.lastRoundTripTime; // seconds → ms\n availableOutgoingBitrate =\n stat.availableOutgoingBitrate ?? this.lastAvailableOutgoingBitrate;\n }\n });\n\n return {\n audioPacketsReceived,\n audioPacketsLost,\n audioJitter,\n videoPacketsReceived,\n videoPacketsLost,\n roundTripTime,\n availableOutgoingBitrate,\n timestamp: Date.now()\n };\n }\n\n // -----------------------------------------------------------------------\n // Packet tracking\n // -----------------------------------------------------------------------\n\n private updatePacketTracking(sample: RawSample, now: number): void {\n if (sample.audioPacketsReceived !== this.lastAudioPacketsReceived) {\n this.lastAudioPacketsReceived = sample.audioPacketsReceived;\n this.lastAudioPacketChangeTime = now;\n }\n\n if (sample.videoPacketsReceived !== this.lastVideoPacketsReceived) {\n this.lastVideoPacketsReceived = sample.videoPacketsReceived;\n this.lastVideoPacketChangeTime = now;\n }\n\n if (sample.roundTripTime > 0) {\n this.lastRoundTripTime = sample.roundTripTime;\n }\n if (sample.availableOutgoingBitrate !== undefined) {\n this.lastAvailableOutgoingBitrate = sample.availableOutgoingBitrate;\n }\n }\n\n // -----------------------------------------------------------------------\n // Issue detection\n // -----------------------------------------------------------------------\n\n private detectIssues(\n sample: RawSample,\n prevSample: RawSample | null,\n now: number\n ): NetworkIssue[] {\n const issues: NetworkIssue[] = [];\n const baseline = this._baseline$.value;\n\n // --- No inbound audio packets ---\n const audioSilenceMs = now - this.lastAudioPacketChangeTime;\n if (audioSilenceMs > this.noAudioPacketThresholdMs) {\n issues.push({\n type: 'no_inbound_audio',\n severity: 'critical',\n timestamp: now,\n value: audioSilenceMs,\n threshold: this.noAudioPacketThresholdMs\n });\n }\n\n // --- No inbound video packets ---\n const videoSilenceMs = now - this.lastVideoPacketChangeTime;\n if (videoSilenceMs > this.noVideoPacketThresholdMs) {\n issues.push({\n type: 'no_inbound_video',\n severity: 'warning',\n timestamp: now,\n value: videoSilenceMs,\n threshold: this.noVideoPacketThresholdMs\n });\n }\n\n // Baseline-dependent checks\n if (baseline.ready) {\n // --- RTT spike ---\n const rtt = sample.roundTripTime;\n const baselineRtt = baseline.rtt;\n if (baselineRtt > 0) {\n const rttRatio = rtt / baselineRtt;\n if (rttRatio > this.rttSpikeCriticalMultiplier) {\n issues.push({\n type: 'high_rtt',\n severity: 'critical',\n timestamp: now,\n value: rtt,\n threshold: baselineRtt * this.rttSpikeCriticalMultiplier\n });\n } else if (rttRatio > this.rttSpikeWarningMultiplier) {\n issues.push({\n type: 'high_rtt',\n severity: 'warning',\n timestamp: now,\n value: rtt,\n threshold: baselineRtt * this.rttSpikeWarningMultiplier\n });\n }\n }\n\n // --- Jitter spike ---\n const jitter = sample.audioJitter;\n const baselineJitter = baseline.jitter;\n if (baselineJitter > 0) {\n const jitterRatio = jitter / baselineJitter;\n if (jitterRatio > this.jitterSpikeMultiplier) {\n issues.push({\n type: 'high_jitter',\n severity: 'warning',\n timestamp: now,\n value: jitter,\n threshold: baselineJitter * this.jitterSpikeMultiplier\n });\n }\n }\n }\n\n // --- Packet loss (delta-based via previous sample) ---\n if (prevSample) {\n // Clamp deltas to 0 — counters can reset to lower values after ICE restart\n const deltaAudioReceived = Math.max(\n 0,\n sample.audioPacketsReceived - prevSample.audioPacketsReceived\n );\n const deltaAudioLost = Math.max(0, sample.audioPacketsLost - prevSample.audioPacketsLost);\n const deltaVideoReceived = Math.max(\n 0,\n sample.videoPacketsReceived - prevSample.videoPacketsReceived\n );\n const deltaVideoLost = Math.max(0, sample.videoPacketsLost - prevSample.videoPacketsLost);\n\n const totalDeltaReceived = deltaAudioReceived + deltaVideoReceived;\n const totalDeltaLost = deltaAudioLost + deltaVideoLost;\n const lossPercent = computePacketLossPercent(totalDeltaLost, totalDeltaReceived);\n\n if (lossPercent > this.packetLossCriticalPercent) {\n issues.push({\n type: 'high_packet_loss',\n severity: 'critical',\n timestamp: now,\n value: lossPercent,\n threshold: this.packetLossCriticalPercent\n });\n } else if (lossPercent > this.packetLossWarningPercent) {\n issues.push({\n type: 'high_packet_loss',\n severity: 'warning',\n timestamp: now,\n value: lossPercent,\n threshold: this.packetLossWarningPercent\n });\n }\n }\n\n return issues;\n }\n}\n","import {\n merge,\n debounceTime,\n exhaustMap,\n filter,\n takeUntil,\n timer,\n from,\n EMPTY,\n take,\n tap,\n catchError,\n combineLatest,\n switchMap,\n withLatestFrom,\n map\n} from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { getLogger } from '../utils/logger';\n\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\n// ---------------------------------------------------------------------------\n// Constants (sensible defaults)\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_DEBOUNCE_TIME_MS = 2_000;\nconst DEFAULT_COOLDOWN_MS = 10_000;\nconst DEFAULT_ICE_GRACE_PERIOD_MS = 3_000;\nconst DEFAULT_ICE_RESTART_TIMEOUT_MS = 5_000;\nconst DEFAULT_MAX_ATTEMPTS = 3;\n\nconst DEFAULT_KEYFRAME_MAX_BURST = 3;\nconst DEFAULT_KEYFRAME_BURST_WINDOW_MS = 3_000;\nconst DEFAULT_KEYFRAME_COOLDOWN_MS = 10_000;\n\nconst DEFAULT_DEGRADATION_BITRATE_THRESHOLD = 150; // kbps\nconst DEFAULT_DEGRADATION_RECOVERY_THRESHOLD = 300; // kbps\nconst DEFAULT_PACKET_LOSS_RECOVERY_DELAY_SEC = 5;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type RecoveryState = 'idle' | 'debouncing' | 'recovering' | 'cooldown';\n\nexport interface RecoveryEvent {\n action:\n | 'keyframe_requested'\n | 'reinvite_started'\n | 'reinvite_succeeded'\n | 'reinvite_failed'\n | 'reinvite_timeout'\n | 'max_attempts_reached'\n | 'signal_reconnect'\n | 'full_reconnect'\n | 'video_disabled'\n | 'video_restored';\n reason: string;\n attempt?: number;\n maxAttempts?: number;\n timestamp: number;\n}\n\nexport interface RecoveryConfig {\n debounceTimeMs?: number;\n cooldownMs?: number;\n iceGracePeriodMs?: number;\n iceRestartTimeoutMs?: number;\n maxAttempts?: number;\n enableRelayFallback?: boolean;\n keyframeMaxBurst?: number;\n keyframeBurstWindowMs?: number;\n keyframeCooldownMs?: number;\n degradationBitrateThreshold?: number;\n degradationRecoveryThreshold?: number;\n enableAutoDegradation?: boolean;\n /** Seconds without packet loss before restoring video after degradation. */\n packetLossRecoveryDelaySec?: number;\n}\n\nexport interface RecoveryCallbacks {\n requestKeyframe: () => void;\n requestIceRestart: (relayOnly?: boolean) => Promise<boolean>;\n disableVideo: () => void;\n enableVideo: () => void;\n isNegotiating: () => boolean;\n isCallConnected: () => boolean;\n getPeerConnectionState: () => string | null;\n}\n\nexport interface RecoveryInputs {\n /** Transport-level signaling readiness (true after WebSocket auth, false on disconnect). */\n signalingReady$: Observable<boolean>;\n}\n\nexport interface RecoveryTrigger {\n source: 'stats' | 'ice' | 'network';\n detail: string;\n /**\n * Structured issue type from the stats monitor or ICE state machine.\n * Used by runTiers() to route degradation-only triggers (high_rtt, high_jitter,\n * high_packet_loss) to Tier 1 only, and connectivity-breaking triggers\n * (no_inbound_audio, ice_disconnected) to the full tiered pipeline.\n */\n issueType?: string;\n}\n\n/**\n * Issue types that indicate quality degradation but NOT connectivity loss.\n * These should only trigger Tier 1 (keyframe) — never ICE restart.\n * The network path is slow or lossy, but media is still flowing.\n */\nconst DEGRADATION_ONLY_ISSUES: ReadonlySet<string> = new Set([\n 'high_rtt',\n 'high_jitter',\n 'high_packet_loss'\n]);\n\n// ---------------------------------------------------------------------------\n// CallRecoveryManager\n// ---------------------------------------------------------------------------\n\n/**\n * Implements the tiered recovery pipeline described in Sections 2, 19, 22,\n * and 25 of the implementation guide.\n *\n * Three detection signals (stats critical, ICE state, network events) are\n * merged, debounced, and processed through tiered recovery:\n * Tier 1 - Request video keyframe (throttled burst)\n * Tier 2 - ICE restart via verto.modify\n * Tier 3 - Relay-only ICE restart\n *\n * Gate checks prevent recovery when the call is not in a recoverable state.\n * A state machine tracks the pipeline: IDLE -> DEBOUNCING -> RECOVERING -> COOLDOWN.\n */\nexport class CallRecoveryManager extends Destroyable {\n // --- Configuration (immutable after construction) ---\n private readonly _config: Required<RecoveryConfig>;\n private readonly _callbacks: RecoveryCallbacks;\n private readonly _inputs: RecoveryInputs;\n\n // --- Observables (private subjects) ---\n private readonly _recoveryState$ = this.createBehaviorSubject<RecoveryState>('idle');\n private readonly _recoveryEvent$ = this.createSubject<RecoveryEvent>();\n private readonly _bandwidthConstrained$ = this.createBehaviorSubject<boolean>(false);\n private readonly _hasPacketLoss$ = this.createBehaviorSubject<boolean>(false);\n\n // --- Input signals (callers push triggers here) ---\n private readonly _trigger$ = this.createSubject<RecoveryTrigger>();\n\n // --- Internal state ---\n private _attemptCount = 0;\n private _keyframeBurstCount = 0;\n private _keyframeBurstStart = 0;\n private _keyframeCooldownUntil = 0;\n private _cooldownUntil = 0;\n private _cooldownSubscription?: { unsubscribe(): void };\n\n // --- Pipeline subscription lifecycle ---\n private readonly _pipelineStop$ = this.createSubject<void>();\n\n constructor(callbacks: RecoveryCallbacks, inputs: RecoveryInputs, config: RecoveryConfig = {}) {\n super();\n this._callbacks = callbacks;\n this._inputs = inputs;\n this._config = {\n debounceTimeMs: config.debounceTimeMs ?? DEFAULT_DEBOUNCE_TIME_MS,\n cooldownMs: config.cooldownMs ?? DEFAULT_COOLDOWN_MS,\n iceGracePeriodMs: config.iceGracePeriodMs ?? DEFAULT_ICE_GRACE_PERIOD_MS,\n iceRestartTimeoutMs: config.iceRestartTimeoutMs ?? DEFAULT_ICE_RESTART_TIMEOUT_MS,\n maxAttempts: config.maxAttempts ?? DEFAULT_MAX_ATTEMPTS,\n enableRelayFallback: config.enableRelayFallback ?? true,\n keyframeMaxBurst: config.keyframeMaxBurst ?? DEFAULT_KEYFRAME_MAX_BURST,\n keyframeBurstWindowMs: config.keyframeBurstWindowMs ?? DEFAULT_KEYFRAME_BURST_WINDOW_MS,\n keyframeCooldownMs: config.keyframeCooldownMs ?? DEFAULT_KEYFRAME_COOLDOWN_MS,\n degradationBitrateThreshold:\n config.degradationBitrateThreshold ?? DEFAULT_DEGRADATION_BITRATE_THRESHOLD,\n degradationRecoveryThreshold:\n config.degradationRecoveryThreshold ?? DEFAULT_DEGRADATION_RECOVERY_THRESHOLD,\n enableAutoDegradation: config.enableAutoDegradation ?? true,\n packetLossRecoveryDelaySec:\n config.packetLossRecoveryDelaySec ?? DEFAULT_PACKET_LOSS_RECOVERY_DELAY_SEC\n };\n\n this.initPipeline();\n this.initDegradationRecoveryPipeline();\n }\n\n // --- Public observable getters ---\n\n public get recoveryState$(): Observable<RecoveryState> {\n return this._recoveryState$.asObservable().pipe(takeUntil(this._destroyed$));\n }\n\n public get recoveryState(): RecoveryState {\n return this._recoveryState$.value;\n }\n\n public get recoveryEvent$(): Observable<RecoveryEvent> {\n return this._recoveryEvent$.asObservable().pipe(takeUntil(this._destroyed$));\n }\n\n public get bandwidthConstrained$(): Observable<boolean> {\n return this._bandwidthConstrained$.asObservable().pipe(takeUntil(this._destroyed$));\n }\n\n public get bandwidthConstrained(): boolean {\n return this._bandwidthConstrained$.value;\n }\n\n // --- Public API ---\n\n /**\n * Feed an external detection signal into the recovery pipeline.\n * Multiple signals are debounced and collapsed automatically.\n */\n public pushTrigger(trigger: RecoveryTrigger): void {\n this._trigger$.next(trigger);\n }\n\n /**\n * Manual ICE restart request — bypasses cooldown and gate checks,\n * but skips if the pipeline is already recovering to avoid races.\n */\n public async requestIceRestart(): Promise<void> {\n if (this._recoveryState$.value === 'recovering') {\n logger.info('CallRecoveryManager: manual ICE restart skipped — recovery already in progress');\n return;\n }\n logger.info('CallRecoveryManager: manual ICE restart requested');\n this.transitionTo('recovering');\n await this.executeIceRestart(false);\n this.startCooldown();\n }\n\n /**\n * Manual keyframe request — subject to burst throttling only.\n */\n public requestKeyframe(): void {\n this.executeKeyframe('manual request');\n }\n\n /**\n * Reset all attempt counters and cooldowns. Should be called after\n * WebSocket reconnect or call state recovers to 'connected'.\n */\n public reset(): void {\n logger.info('CallRecoveryManager: resetting counters');\n this._attemptCount = 0;\n this._keyframeBurstCount = 0;\n this._keyframeBurstStart = 0;\n this._keyframeCooldownUntil = 0;\n this._cooldownUntil = 0;\n this.transitionTo('idle');\n }\n\n /**\n * Notify the recovery manager that a verto.modify (ICE restart SDP exchange)\n * failed at the signaling layer. Resets cooldown and pushes a new trigger\n * so recovery can re-attempt.\n */\n public notifyModifyFailed(): void {\n if (this._recoveryState$.value === 'cooldown' || this._recoveryState$.value === 'idle') {\n logger.info('CallRecoveryManager: verto.modify failed — re-entering recovery');\n this._cooldownUntil = 0;\n this.transitionTo('idle');\n this.pushTrigger({ source: 'network', detail: 'modify_failed_during_recovery' });\n }\n }\n\n /**\n * Feed bandwidth information for graceful degradation (Section 22).\n * Call this from the stats monitor with current available outgoing bitrate.\n */\n public reportBandwidth(bitrateKbps: number): void {\n if (!this._config.enableAutoDegradation) {\n return;\n }\n\n const wasConstrained = this._bandwidthConstrained$.value;\n\n if (!wasConstrained && bitrateKbps < this._config.degradationBitrateThreshold) {\n this._bandwidthConstrained$.next(true);\n this._callbacks.disableVideo();\n this.emitEvent({\n action: 'video_disabled',\n reason: `bandwidth ${bitrateKbps}kbps below threshold ${this._config.degradationBitrateThreshold}kbps`,\n timestamp: Date.now()\n });\n logger.warn(\n `CallRecoveryManager: disabling video — bandwidth ${bitrateKbps}kbps < ${this._config.degradationBitrateThreshold}kbps`\n );\n } else if (wasConstrained && bitrateKbps >= this._config.degradationRecoveryThreshold) {\n this._bandwidthConstrained$.next(false);\n this._callbacks.enableVideo();\n this.emitEvent({\n action: 'video_restored',\n reason: `bandwidth ${bitrateKbps}kbps recovered above ${this._config.degradationRecoveryThreshold}kbps`,\n timestamp: Date.now()\n });\n logger.info(\n `CallRecoveryManager: restoring video — bandwidth ${bitrateKbps}kbps >= ${this._config.degradationRecoveryThreshold}kbps`\n );\n }\n }\n\n /**\n * Feed current network issues for degradation recovery.\n * When video was disabled due to bandwidth constraints, this pipeline\n * monitors packet loss clearance to trigger video restoration.\n */\n public reportNetworkIssues(issues: { type: string }[]): void {\n const hasPacketLoss = issues.some((i) => i.type === 'high_packet_loss');\n if (hasPacketLoss !== this._hasPacketLoss$.value) {\n this._hasPacketLoss$.next(hasPacketLoss);\n }\n }\n\n /**\n * Signal-only vs full reconnect distinction (Section 25).\n * Call this when WebSocket reconnects to determine the appropriate action.\n */\n public handleWebSocketReconnect(): void {\n const pcState = this._callbacks.getPeerConnectionState();\n const isMediaAlive = pcState === 'connected' || pcState === 'completed';\n\n if (isMediaAlive) {\n logger.info('CallRecoveryManager: signal-only reconnect — peer connection still alive');\n this.emitEvent({\n action: 'signal_reconnect',\n reason: 'WebSocket reconnected, peer connection still connected',\n timestamp: Date.now()\n });\n } else {\n logger.info('CallRecoveryManager: full reconnect — peer connection also down');\n this.emitEvent({\n action: 'full_reconnect',\n reason: 'WebSocket reconnected, peer connection not connected — ICE restart needed',\n timestamp: Date.now()\n });\n this.pushTrigger({ source: 'network', detail: 'full_reconnect_after_ws' });\n }\n this.reset();\n }\n\n // --- Lifecycle ---\n\n public override destroy(): void {\n this._pipelineStop$.next();\n this._pipelineStop$.complete();\n super.destroy();\n }\n\n // --- Pipeline initialization ---\n\n private initPipeline(): void {\n this.subscribeTo(\n this._trigger$.pipe(\n tap(() => {\n if (this._recoveryState$.value === 'idle') {\n this.transitionTo('debouncing');\n }\n }),\n debounceTime(this._config.debounceTimeMs),\n withLatestFrom(this._inputs.signalingReady$),\n filter(([, signalingReady]) => this.passGateChecks(signalingReady)),\n map(([trigger]) => trigger),\n exhaustMap((trigger) => this.executeTieredRecovery(trigger)),\n takeUntil(merge(this._destroyed$, this._pipelineStop$))\n ),\n {\n next: () => {\n /* side effects handled inside executeTieredRecovery */\n },\n error: (err) => {\n logger.error('CallRecoveryManager: pipeline error', err);\n this.transitionTo('idle');\n }\n }\n );\n }\n\n /**\n * When video was disabled due to bandwidth constraints, the browser's\n * bandwidth estimator loses its primary probe signal (video traffic).\n * This means availableOutgoingBitrate may never recover to the restoration\n * threshold. Instead, we watch for packet loss clearance as a proxy for\n * network recovery: if high_packet_loss stays absent for a configurable\n * duration while video is degraded, we restore the video track.\n */\n private initDegradationRecoveryPipeline(): void {\n if (!this._config.enableAutoDegradation) {\n return;\n }\n\n const delayMs = this._config.packetLossRecoveryDelaySec * 1000;\n\n this.subscribeTo(\n combineLatest([this._bandwidthConstrained$, this._hasPacketLoss$]).pipe(\n switchMap(([constrained, hasPacketLoss]) => {\n if (constrained && !hasPacketLoss) {\n return timer(delayMs);\n }\n return EMPTY;\n }),\n takeUntil(this._destroyed$)\n ),\n () => {\n this._bandwidthConstrained$.next(false);\n this._callbacks.enableVideo();\n this.emitEvent({\n action: 'video_restored',\n reason: `no packet loss for ${this._config.packetLossRecoveryDelaySec}s — restoring video`,\n timestamp: Date.now()\n });\n logger.info(\n `CallRecoveryManager: restoring video — no packet loss for ${this._config.packetLossRecoveryDelaySec}s`\n );\n }\n );\n }\n\n // --- Gate checks ---\n\n private passGateChecks(signalingReady: boolean): boolean {\n if (this._callbacks.isNegotiating()) {\n logger.debug('CallRecoveryManager: gate blocked — negotiation in progress');\n this.transitionTo('idle');\n return false;\n }\n\n if (!signalingReady) {\n logger.debug('CallRecoveryManager: gate blocked — signaling not ready');\n this.transitionTo('idle');\n return false;\n }\n\n if (!this._callbacks.isCallConnected()) {\n logger.debug('CallRecoveryManager: gate blocked — call not connected');\n this.transitionTo('idle');\n return false;\n }\n\n if (this.isCooldownActive()) {\n logger.debug('CallRecoveryManager: gate blocked — cooldown active');\n this.transitionTo('cooldown');\n return false;\n }\n\n return true;\n }\n\n private isCooldownActive(): boolean {\n return Date.now() < this._cooldownUntil;\n }\n\n // --- Tiered recovery ---\n\n private executeTieredRecovery(trigger: RecoveryTrigger): Observable<void> {\n this.transitionTo('recovering');\n logger.info(\n `CallRecoveryManager: starting tiered recovery — source=${trigger.source} detail=${trigger.detail}`\n );\n\n return from(this.runTiers(trigger)).pipe(\n tap(() => this.startCooldown()),\n catchError((err) => {\n logger.error('CallRecoveryManager: tiered recovery failed', err);\n this.startCooldown();\n return EMPTY;\n })\n );\n }\n\n private async runTiers(trigger: RecoveryTrigger): Promise<void> {\n // Tier 1: keyframe request (always executed)\n this.executeKeyframe(trigger.detail);\n\n // Degradation-only issues (high_rtt, high_jitter, high_packet_loss) stop at Tier 1.\n // The network path is slow/lossy but media is still flowing — ICE restart would\n // cause an unnecessary media interruption on a connection that isn't broken.\n if (trigger.issueType && DEGRADATION_ONLY_ISSUES.has(trigger.issueType)) {\n logger.debug(\n `CallRecoveryManager: degradation-only issue (${trigger.issueType}) — Tier 1 only, skipping ICE restart`\n );\n return;\n }\n\n // Tier 2: ICE restart (standard) — only for connectivity-breaking issues\n if (this._attemptCount < this._config.maxAttempts) {\n const tier2Success = await this.executeIceRestart(false);\n if (tier2Success) {\n return;\n }\n }\n\n // Tier 3: Relay-only ICE restart (when enabled)\n if (this._config.enableRelayFallback && this._attemptCount < this._config.maxAttempts) {\n const tier3Success = await this.executeIceRestart(true);\n if (tier3Success) {\n return;\n }\n }\n\n // All tiers exhausted\n if (this._attemptCount >= this._config.maxAttempts) {\n this.emitEvent({\n action: 'max_attempts_reached',\n reason: `all ${this._config.maxAttempts} recovery attempts exhausted`,\n attempt: this._attemptCount,\n maxAttempts: this._config.maxAttempts,\n timestamp: Date.now()\n });\n logger.warn('CallRecoveryManager: max recovery attempts reached');\n }\n }\n\n // --- Tier 1: Keyframe ---\n\n private executeKeyframe(reason: string): void {\n const now = Date.now();\n\n // Check cooldown\n if (now < this._keyframeCooldownUntil) {\n logger.debug('CallRecoveryManager: keyframe request skipped — cooldown active');\n return;\n }\n\n // Check burst window\n if (now - this._keyframeBurstStart > this._config.keyframeBurstWindowMs) {\n this._keyframeBurstCount = 0;\n this._keyframeBurstStart = now;\n }\n\n if (this._keyframeBurstCount >= this._config.keyframeMaxBurst) {\n // Burst limit reached — start cooldown\n this._keyframeCooldownUntil = now + this._config.keyframeCooldownMs;\n logger.debug(\n `CallRecoveryManager: keyframe burst limit reached (${this._config.keyframeMaxBurst}), cooldown until ${this._keyframeCooldownUntil}`\n );\n return;\n }\n\n this._keyframeBurstCount += 1;\n this._callbacks.requestKeyframe();\n this.emitEvent({\n action: 'keyframe_requested',\n reason,\n timestamp: now\n });\n logger.debug(\n `CallRecoveryManager: keyframe requested (burst ${this._keyframeBurstCount}/${this._config.keyframeMaxBurst})`\n );\n }\n\n // --- Tier 2 & 3: ICE restart ---\n\n private async executeIceRestart(relayOnly: boolean): Promise<boolean> {\n this._attemptCount += 1;\n const tier = relayOnly ? 'Tier 3 (relay-only)' : 'Tier 2 (standard)';\n logger.info(\n `CallRecoveryManager: ${tier} ICE restart — attempt ${this._attemptCount}/${this._config.maxAttempts}`\n );\n\n this.emitEvent({\n action: 'reinvite_started',\n reason: `${tier} ICE restart`,\n attempt: this._attemptCount,\n maxAttempts: this._config.maxAttempts,\n timestamp: Date.now()\n });\n\n try {\n const success = await this.withTimeout(\n this._callbacks.requestIceRestart(relayOnly),\n this._config.iceRestartTimeoutMs\n );\n\n if (success) {\n this.emitEvent({\n action: 'reinvite_succeeded',\n reason: `${tier} ICE restart succeeded`,\n attempt: this._attemptCount,\n maxAttempts: this._config.maxAttempts,\n timestamp: Date.now()\n });\n logger.info(`CallRecoveryManager: ${tier} ICE restart succeeded`);\n this._attemptCount = 0;\n return true;\n }\n\n this.emitEvent({\n action: 'reinvite_failed',\n reason: `${tier} ICE restart returned false`,\n attempt: this._attemptCount,\n maxAttempts: this._config.maxAttempts,\n timestamp: Date.now()\n });\n logger.warn(`CallRecoveryManager: ${tier} ICE restart failed`);\n return false;\n } catch {\n this.emitEvent({\n action: 'reinvite_timeout',\n reason: `${tier} ICE restart timed out after ${this._config.iceRestartTimeoutMs}ms`,\n attempt: this._attemptCount,\n maxAttempts: this._config.maxAttempts,\n timestamp: Date.now()\n });\n logger.warn(`CallRecoveryManager: ${tier} ICE restart timed out`);\n return false;\n }\n }\n\n // --- Utility ---\n\n private async withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n reject(new Error(`Timeout after ${timeoutMs}ms`));\n }, timeoutMs);\n\n promise.then(\n (value) => {\n clearTimeout(timeoutId);\n resolve(value);\n },\n (err: unknown) => {\n clearTimeout(timeoutId);\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n );\n });\n }\n\n private transitionTo(state: RecoveryState): void {\n const prev = this._recoveryState$.value;\n if (prev !== state) {\n logger.debug(`CallRecoveryManager: state ${prev} -> ${state}`);\n this._recoveryState$.next(state);\n }\n }\n\n private startCooldown(): void {\n this._cooldownUntil = Date.now() + this._config.cooldownMs;\n this.transitionTo('cooldown');\n\n // Cancel any existing cooldown timer to prevent accumulation\n if (this._cooldownSubscription) {\n this._cooldownSubscription.unsubscribe();\n }\n\n // Schedule transition back to idle after cooldown\n this._cooldownSubscription = timer(this._config.cooldownMs)\n .pipe(\n take(1),\n takeUntil(this._destroyed$),\n filter(() => this._recoveryState$.value === 'cooldown')\n )\n .subscribe(() => this.transitionTo('idle'));\n }\n\n private emitEvent(event: RecoveryEvent): void {\n this._recoveryEvent$.next(event);\n }\n}\n","import { Participant, SelfParticipant, type ExecuteMethod } from '../core/entities/Participant';\n\nimport type { DeviceController } from '../interfaces/DeviceController';\nimport type { VertoManager } from '../interfaces/VertoManager';\n\n/**\n * Factory for creating Participant instances with proper dependency injection\n * Eliminates circular dependency between Call and Participant\n */\nexport class ParticipantFactory {\n constructor(\n private executeMethod: ExecuteMethod,\n private vertoManager: VertoManager,\n private deviceController: DeviceController\n ) {}\n\n /**\n * Create a self participant (the local user in the call)\n */\n public createSelfParticipant(id: string): SelfParticipant {\n return new SelfParticipant(id, this.executeMethod, this.vertoManager, this.deviceController);\n }\n\n /**\n * Create a remote participant\n */\n public createParticipant(id: string): Participant {\n return new Participant(id, this.executeMethod, this.deviceController);\n }\n}\n","/**\n * MOS (Mean Opinion Score) quality computation based on the simplified\n * ITU-T G.107 E-model.\n *\n * Provides a single 1-5 number that applications can use for a\n * green / yellow / red quality indicator without understanding raw\n * jitter and packet-loss values.\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type QualityLevel = 'excellent' | 'good' | 'fair' | 'poor' | 'critical';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** E-model base R factor for a perfect channel. */\nconst E_MODEL_BASE_R = 93.2;\n\n/** Delay/jitter weight factor. */\nconst E_MODEL_DELAY_WEIGHT = 0.024;\n\n/** Packet-loss weight factor. */\nconst E_MODEL_LOSS_WEIGHT = 2.5;\n\n/** R-factor valid range for the E-model polynomial. */\nconst R_FACTOR_MIN = 0;\nconst R_FACTOR_MAX = 100;\n\n/** Lower bound for MOS. */\nconst MOS_MIN = 1.0;\n\n/** Upper bound for MOS. */\nconst MOS_MAX = 5.0;\n\n/** MOS threshold boundaries (lower bound of each level). */\nconst MOS_EXCELLENT_THRESHOLD = 4.0;\nconst MOS_GOOD_THRESHOLD = 3.5;\nconst MOS_FAIR_THRESHOLD = 3.0;\nconst MOS_POOR_THRESHOLD = 2.0;\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Compute a Mean Opinion Score from network metrics using the simplified\n * E-model formula (ITU-T G.107).\n *\n * ```\n * R = 93.2 - (rtt/2 + jitter) * 0.024 - packetLoss * 2.5\n * MOS = 1 + 0.035 * R + R * (R - 60) * (100 - R) * 7e-6\n * ```\n *\n * @param rtt Round-trip time in milliseconds\n * @param jitter Jitter in milliseconds\n * @param packetLoss Packet loss as a percentage (e.g. 5 means 5%)\n * @returns MOS value clamped to [1.0, 5.0]\n */\nexport function computeMOS(rtt: number, jitter: number, packetLoss: number): number {\n const rawR =\n E_MODEL_BASE_R - (rtt / 2 + jitter) * E_MODEL_DELAY_WEIGHT - packetLoss * E_MODEL_LOSS_WEIGHT;\n\n // Clamp R to [0, 100] — the MOS polynomial is only valid in this range\n const r = Math.min(R_FACTOR_MAX, Math.max(R_FACTOR_MIN, rawR));\n\n const mos = 1 + 0.035 * r + r * (r - 60) * (100 - r) * 7e-6;\n\n return Math.min(MOS_MAX, Math.max(MOS_MIN, mos));\n}\n\n/**\n * Map a numeric MOS score to a human-readable quality level.\n *\n * | MOS Range | Level |\n * |-------------|-------------|\n * | 4.0 - 5.0 | excellent |\n * | 3.5 - 4.0 | good |\n * | 3.0 - 3.5 | fair |\n * | 2.0 - 3.0 | poor |\n * | 1.0 - 2.0 | critical |\n */\nexport function mosToQualityLevel(mos: number): QualityLevel {\n if (mos >= MOS_EXCELLENT_THRESHOLD) {\n return 'excellent';\n }\n if (mos >= MOS_GOOD_THRESHOLD) {\n return 'good';\n }\n if (mos >= MOS_FAIR_THRESHOLD) {\n return 'fair';\n }\n if (mos >= MOS_POOR_THRESHOLD) {\n return 'poor';\n }\n return 'critical';\n}\n","import {\n distinctUntilChanged,\n filter,\n firstValueFrom,\n from,\n map,\n merge,\n share,\n skip,\n takeUntil,\n tap\n} from 'rxjs';\nimport { v4 as uuid } from 'uuid';\n\nimport { Destroyable } from '../../behaviors/Destroyable';\nimport { PreferencesContainer } from '../../containers/PreferencesContainer';\nimport { RTCStatsMonitor } from '../../controllers/RTCStatsMonitor';\nimport { CallRecoveryManager } from '../../managers/CallRecoveryManager';\nimport { ParticipantFactory } from '../../managers/ParticipantFactory';\nimport { filterAs } from '../../operators';\nimport { getValueFrom } from '../../utils/getValueFrom';\nimport { getLogger } from '../../utils/logger';\nimport { computeMOS, mosToQualityLevel } from '../../utils/qualityScore';\nimport { InvalidParams, JSONRPCError, UnimplementedError } from '../errors';\nimport { buildRPCRequest, VertoSubscribe, WebrtcVerto } from '../RPCMessages';\nimport { isJSONRPCErrorResponse } from '../RPCMessages/guards/base.guards';\nimport {\n isCallStateMetadata,\n isCallUpdatedMetadata,\n isLayoutChangedMetadata,\n isMemberJoinedMetadata,\n isMemberLeftMetadata,\n isMemberTalkingMetadata,\n isMemberUpdatedMetadata,\n isSignalwireCallMetadata,\n isWebrtcMessageMetadata\n} from '../RPCMessages/guards/events.guards';\n\nimport type { Address } from './Address';\nimport type { Participant, SelfParticipant } from './Participant';\nimport type {\n CallStatus,\n CallOptions,\n CallManager,\n CallParticipant,\n CallSelfParticipant\n} from './types/call.types';\nimport type { NetworkChangeEvent } from '../../controllers/NetworkMonitor';\nimport type {\n NetworkIssue as StatsNetworkIssue,\n NetworkMetrics as StatsNetworkMetrics\n} from '../../controllers/RTCStatsMonitor';\nimport type { ClientSession } from '../../interfaces/ClientSession';\nimport type { DeviceController } from '../../interfaces/DeviceController';\nimport type { WebRTCVerto } from '../../interfaces/WebRTCVerto';\nimport type { CallEventsManager } from '../../managers/CallEventsManager';\nimport type {\n RecoveryState,\n RecoveryEvent,\n RecoveryCallbacks\n} from '../../managers/CallRecoveryManager';\nimport type { TransferOptions } from '../../managers/types/verto-manager.types';\nimport type { QualityLevel } from '../../utils/qualityScore';\nimport type { CallError } from '../errors';\nimport type { JSONRPCParams } from '../RPCMessages';\nimport type { JSONRPCRequest, JSONRPCResponse } from '../RPCMessages/types/base';\nimport type { LayoutLayer, MemberTarget } from '../RPCMessages/types/common';\nimport type {\n CallStatePayload,\n CallUpdatedPayload,\n LayoutChangedPayload,\n MemberJoinedPayload,\n MemberLeftPayload,\n MemberTalkingPayload,\n MemberUpdatedPayload\n} from '../RPCMessages/types/events';\nimport type { Capability, CallDirection, VideoPosition } from '../types/call.types';\nimport type { MediaOptions, MediaDirections } from '../types/media.types';\nimport type { MediaParamsEvent } from '../types/resilience.types';\nimport type { PendingRPCOptions } from '../utils';\nimport type { Observable, BehaviorSubject } from 'rxjs';\n\nconst logger = getLogger();\n\n/**\n * Ratio between the critical and warning RTT spike multipliers.\n * Warning threshold = baseline * warningMultiplier (default 3x)\n * Critical threshold = baseline * warningMultiplier * RTT_CRITICAL_TO_WARNING_RATIO\n * With default 3x warning: critical = 3 * 5/3 = 5x baseline.\n */\nconst RTT_CRITICAL_TO_WARNING_RATIO = 5 / 3;\n\n/**\n * Manager instances returned by initialization callback\n */\nexport interface CallManagers {\n vertoManager: WebRTCVerto;\n callEventsManager: CallEventsManager;\n}\n\n/**\n * Initialization callback that creates managers for a Call instance\n * @param call - The WebRTCCall instance being initialized\n * @returns Manager instances for the call\n */\nexport type ManagerInitializer = (call: WebRTCCall) => CallManagers;\n\n/**\n * Required initialization configuration for Call constructor.\n * Calls must be created via {@link CallFactory} which provides these dependencies.\n */\nexport interface CallInitialization {\n /**\n * Callback function that creates and wires manager instances\n */\n initializeManagers: ManagerInitializer;\n /**\n * Device controller for media device access\n */\n deviceController: DeviceController;\n /**\n * Network change events for feeding recovery pipeline\n */\n networkChange$?: Observable<NetworkChangeEvent>;\n}\n\nconst fromDestinationParams = (destination?: string): Record<string, unknown> => {\n if (!destination) return {};\n try {\n const url = new URL(`destination:${destination}`);\n const params: Record<string, unknown> = {};\n url.searchParams.forEach((value, key) => {\n params[key] = value;\n });\n return params;\n } catch (error) {\n logger.warn(`Failed to parse destination URI: ${destination}`, error);\n return {};\n }\n};\n\n/**\n * Concrete WebRTC call implementation.\n *\n * Manages the full lifecycle of a call including signaling, media streams,\n * participants, layout, and event routing. Created via {@link SignalWire.dial}\n * or received as an inbound call.\n */\nexport class WebRTCCall extends Destroyable implements CallManager {\n /** Unique identifier for this call. */\n public readonly id: string;\n /** Destination URI this call was placed to. */\n public to?: string;\n private vertoManager!: WebRTCVerto;\n private callEventsManager!: CallEventsManager;\n private participantFactory: ParticipantFactory;\n private _errors$ = this.createReplaySubject<CallError>(1);\n private _status$: BehaviorSubject<CallStatus>;\n private _lastMergedStatus: CallStatus = 'new';\n private _answered$ = this.createReplaySubject<boolean>();\n private _answerMediaOptions?: MediaOptions;\n private _holdState = false;\n private _userVariables$ = this.createBehaviorSubject<Record<string, unknown>>({\n ...PreferencesContainer.instance.userVariables\n });\n\n // Resilience subsystems (created lazily on 'connected')\n private _statsMonitor?: RTCStatsMonitor;\n private _recoveryManager?: CallRecoveryManager;\n private _networkChange$?: Observable<NetworkChangeEvent>;\n\n // Resilience subjects — created eagerly so subscribers can attach before 'connected'.\n // Fed by stats monitor and recovery manager once they're initialized.\n private _networkIssues$ = this.createBehaviorSubject<StatsNetworkIssue[]>([]);\n private _networkMetrics$ = this.createBehaviorSubject<StatsNetworkMetrics[]>([]);\n private _isNetworkHealthy$ = this.createBehaviorSubject<boolean>(true);\n private _qualityScore$ = this.createBehaviorSubject<number>(5.0);\n private _qualityLevel$ = this.createBehaviorSubject<QualityLevel>('excellent');\n private _recoveryState$ = this.createBehaviorSubject<RecoveryState>('idle');\n private _recoveryEvent$ = this.createSubject<RecoveryEvent>();\n private _bandwidthConstrained$ = this.createBehaviorSubject<boolean>(false);\n private _mediaParamsUpdated$ = this.createSubject<MediaParamsEvent>();\n\n // ===========================================================================\n private _customSubscriptions = new Map<string, Observable<Record<string, unknown>>>();\n constructor(\n public clientSession: ClientSession,\n public options: CallOptions,\n initialization: CallInitialization,\n public address?: Address\n ) {\n super();\n this.id = options.callId ?? uuid();\n this.to = options.to;\n this._userVariables$.next({\n ...this._userVariables$.value,\n ...fromDestinationParams(options.to),\n ...options.userVariables\n });\n\n this.subscribeTo(this.webrtcMessages$, (message) => {\n const userVars = getValueFrom<Record<string, unknown>>(message, 'params.userVariables');\n if (userVars) {\n this._userVariables$.next({\n ...this._userVariables$.value,\n ...userVars\n });\n }\n });\n\n const managers = initialization.initializeManagers(this);\n this.vertoManager = managers.vertoManager;\n this.callEventsManager = managers.callEventsManager;\n\n if (options.initOffer) {\n this._status$ = this.createBehaviorSubject<CallStatus>('ringing');\n this._lastMergedStatus = 'ringing';\n } else {\n this._status$ = this.createBehaviorSubject<CallStatus>('new');\n }\n\n const { deviceController, networkChange$ } = initialization;\n this._networkChange$ = networkChange$;\n\n // Create participant factory with bound executeMethod\n this.participantFactory = new ParticipantFactory(\n this.executeMethod.bind(this),\n this.vertoManager,\n deviceController\n );\n\n // Wire resilience lifecycle to call status.\n // IMPORTANT: Use the raw internal merge (not the public status$ getter)\n // because publicCachedObservable defers emissions via asapScheduler,\n // which can cause us to miss the 'connected' transition.\n this.subscribeTo(\n merge(this._status$.asObservable(), this.vertoManager.signalingStatus$).pipe(\n distinctUntilChanged(),\n takeUntil(this._destroyed$)\n ),\n (status) => {\n // Keep _lastMergedStatus in sync so recovery callbacks (isCallConnected)\n // work even if nobody subscribes to the public status$ getter.\n this._lastMergedStatus = status;\n\n if (status === 'connected' && !this._statsMonitor) {\n this.initResilienceSubsystems();\n } else if (status === 'disconnected') {\n // Stop stats polling (no useful metrics during disconnect) but keep\n // the recovery manager alive — it's needed to restore the call.\n this._statsMonitor?.destroy();\n this._statsMonitor = undefined;\n } else if (status === 'destroyed' || status === 'failed') {\n this.stopResilienceSubsystems();\n }\n }\n );\n }\n\n /** Observable stream of errors from media, signaling, and peer connection layers. */\n public get errors$(): Observable<CallError> {\n return this.deferEmission(this._errors$.asObservable());\n }\n\n /**\n * @internal Push an error to the call's error stream.\n * Fatal errors automatically transition the call to `'failed'` and destroy it.\n */\n public emitError(callError: CallError): void {\n if (this._status$.value === 'destroyed' || this._status$.value === 'failed') return;\n this._errors$.next(callError);\n if (callError.fatal) {\n this._status$.next('failed');\n this.destroy();\n }\n }\n\n /** Notify the recovery manager that a verto.modify signaling exchange failed. */\n public notifyModifyFailed(): void {\n this._recoveryManager?.notifyModifyFailed();\n }\n\n /** Whether this call is `'inbound'` or `'outbound'`. */\n public get direction(): CallDirection {\n return this.options.initOffer ? 'inbound' : 'outbound';\n }\n\n /** Observable of the address associated with this call. */\n public get address$(): Observable<Address | undefined> {\n return this.deferEmission(from([this.address])).pipe(takeUntil(this._destroyed$));\n }\n\n /** Display name of the caller. */\n public get fromName(): string | undefined {\n return this.options.fromName;\n }\n\n /** Address URI of the caller. */\n public get from(): string | undefined {\n return this.options.from;\n }\n\n /** Display name of the callee. */\n public get toName(): string | undefined {\n return this.options.toName;\n }\n\n /** Toggles whether incoming video is received. @throws {UnimplementedError} Not yet implemented. */\n // eslint-disable-next-line @typescript-eslint/require-await\n public async toggleIncomingVideo(): Promise<void> {\n throw new UnimplementedError();\n }\n\n /** Toggles whether incoming audio is received. @throws {UnimplementedError} Not yet implemented. */\n // eslint-disable-next-line @typescript-eslint/require-await\n public async toggleIncomingAudio(): Promise<void> {\n throw new UnimplementedError();\n }\n\n /** @internal Registers an additional call ID for event routing. */\n public addCallId(callId: string): void {\n this.callEventsManager.addCallId(callId);\n }\n\n /** List of capabilities available in the current call. */\n public get capabilities(): Capability[] {\n return this.callEventsManager.capabilities;\n }\n\n /** Current snapshot of all participants in the call. */\n public get participants(): CallParticipant[] {\n return this.callEventsManager.participants;\n }\n\n /** The local participant, or `null` if not yet joined. */\n public get self(): CallSelfParticipant | null {\n return this.callEventsManager.self;\n }\n\n /** Toggles the call lock state, preventing or allowing new participants from joining. */\n async toggleLock(): Promise<void> {\n const method = this.locked ? 'call.unlock' : 'call.lock';\n await this.executeMethod(this.selfId ?? '', method, {});\n }\n\n /**\n * Toggles the hold state of the call (pauses/resumes local media transmission).\n *\n * Distinct from {@link Participant.toggleMute} which mutes individual tracks.\n */\n async toggleHold(): Promise<void> {\n if (this._holdState) {\n await this.vertoManager.unhold();\n } else {\n await this.vertoManager.hold();\n }\n this._holdState = !this._holdState;\n }\n\n /** @throws {UnimplementedError} Not yet implemented. Status tracked via {@link recording$}. */\n // eslint-disable-next-line @typescript-eslint/require-await\n async startRecording(): Promise<void> {\n // NEEDS check backend API status\n throw new UnimplementedError();\n }\n\n /** @throws {UnimplementedError} Not yet implemented. Status tracked via {@link streaming$}. */\n // eslint-disable-next-line @typescript-eslint/require-await\n async startStreaming(): Promise<void> {\n // V3 VIDEO\n // NEEDS check backend API status\n throw new UnimplementedError();\n }\n\n /**\n * Replaces the call's custom metadata.\n * @param _meta - Metadata object to set.\n * @throws {UnimplementedError} Not yet implemented.\n */\n // eslint-disable-next-line @typescript-eslint/require-await\n async setMeta(_meta: Record<string, unknown>): Promise<void> {\n // NEEDS backend implementation\n throw new UnimplementedError();\n }\n /**\n * Merges values into the call's custom metadata (unlike {@link setMeta} which replaces).\n * @param _meta - Metadata to merge.\n * @throws {UnimplementedError} Not yet implemented.\n */\n // eslint-disable-next-line @typescript-eslint/require-await\n async updateMeta(_meta: Record<string, unknown>): Promise<void> {\n // NEEDS backend implementation\n throw new UnimplementedError();\n }\n\n /** Observable of layout layer positions for all participants. */\n public get layoutLayers$(): Observable<LayoutLayer[]> {\n return this.deferEmission(this.callEventsManager.layoutLayers$).pipe(\n takeUntil(this._destroyed$)\n );\n }\n\n /** Current snapshot of layout layers. */\n public get layoutLayers(): LayoutLayer[] {\n return this.callEventsManager.layoutLayers;\n }\n\n /**\n * Executes a Verto RPC method targeting a specific participant.\n *\n * Constructs call context (node_id, call_id, member_id) and sends the RPC request.\n *\n * @param target - Target member ID string, or a {@link MemberTarget} object.\n * @param method - Verto method name (e.g. `'call.mute'`, `'call.member.remove'`).\n * @param args - Parameters for the RPC method.\n * @returns The RPC response.\n * @throws {JSONRPCError} If the RPC call returns an error.\n */\n public async executeMethod<T extends JSONRPCResponse = JSONRPCResponse>(\n target: string | MemberTarget,\n method: string,\n args: Record<string, unknown>\n ): Promise<T> {\n const params = this.buildMethodParams(target, args);\n\n const request = buildRPCRequest({\n method,\n params\n });\n\n try {\n const response: T = await this.clientSession.execute(request);\n if (isJSONRPCErrorResponse(response)) {\n throw new JSONRPCError(\n parseInt(response.result?.code ?? '0'),\n `Error response from method ${method}: ${response.result?.code} ${response.result?.message}`,\n undefined,\n undefined,\n request.id\n );\n }\n return response;\n } catch (error) {\n logger.error(`[Call] Error executing method ${method} with params`, params, error);\n throw error;\n }\n }\n\n private buildMethodParams(\n target: string | MemberTarget,\n args: Record<string, unknown>\n ): JSONRPCParams {\n const self: MemberTarget = {\n node_id: this.nodeId ?? '',\n call_id: this.id,\n member_id: this.vertoManager.selfId ?? ''\n };\n\n if (typeof target === 'object') {\n // Full MemberTarget provided — use targets array with the member's actual call_id\n return { ...args, self, targets: [target] };\n }\n\n // String member_id provided — use target singular with the call's node/call reference\n return {\n ...args,\n self,\n target: { node_id: this.nodeId ?? '', call_id: this.id, member_id: target }\n };\n }\n\n /** Observable of the current call status (e.g. `'ringing'`, `'connected'`). */\n public get status$(): Observable<CallStatus> {\n return this.publicCachedObservable('status$', () =>\n merge(this._status$.asObservable(), this.vertoManager.signalingStatus$).pipe(\n distinctUntilChanged(),\n tap((status) => {\n this._lastMergedStatus = status;\n })\n )\n );\n }\n /** Observable of the participants list, emits on join/leave/update. */\n public get participants$(): Observable<CallParticipant[]> {\n return this.deferEmission(this.callEventsManager.participants$).pipe(\n takeUntil(this._destroyed$)\n );\n }\n /** Observable of the local (self) participant. */\n public get self$(): Observable<CallSelfParticipant> {\n return this.deferEmission(this.callEventsManager.self$).pipe(takeUntil(this._destroyed$));\n }\n /** Observable indicating whether the call is being recorded. */\n public get recording$(): Observable<boolean> {\n return this.deferEmission(this.callEventsManager.recording$).pipe(takeUntil(this._destroyed$));\n }\n\n /** Observable indicating whether the call is being streamed. */\n public get streaming$(): Observable<boolean> {\n return this.deferEmission(this.callEventsManager.streaming$).pipe(takeUntil(this._destroyed$));\n }\n\n /** Observable indicating whether raise-hand priority is active. */\n public get raiseHandPriority$(): Observable<boolean> {\n return this.deferEmission(this.callEventsManager.raiseHandPriority$).pipe(\n takeUntil(this._destroyed$)\n );\n }\n\n /** Observable indicating whether the call room is locked. */\n public get locked$(): Observable<boolean> {\n return this.deferEmission(this.callEventsManager.locked$).pipe(takeUntil(this._destroyed$));\n }\n\n /** Observable of custom metadata associated with the call. */\n public get meta$(): Observable<Record<string, unknown>> {\n return this.deferEmission(this.callEventsManager.meta$).pipe(takeUntil(this._destroyed$));\n }\n\n /** Observable of the call's capability flags. */\n public get capabilities$(): Observable<Capability[]> {\n return this.deferEmission(this.callEventsManager.capabilities$).pipe(\n takeUntil(this._destroyed$)\n );\n }\n\n /** Observable of the current layout name. */\n public get layout$(): Observable<string> {\n return this.deferEmission(this.callEventsManager.layout$).pipe(takeUntil(this._destroyed$));\n }\n\n /** Current call status. */\n public get status(): CallStatus {\n return this._lastMergedStatus;\n }\n\n /** Whether the call is currently being recorded. */\n public get recording(): boolean {\n return this.callEventsManager.recording;\n }\n\n /** Whether the call is currently being streamed. */\n public get streaming(): boolean {\n return this.callEventsManager.streaming;\n }\n\n /** Whether raise-hand priority is active. */\n public get raiseHandPriority(): boolean {\n return this.callEventsManager.raiseHandPriority;\n }\n\n /** Whether the call room is locked. */\n public get locked(): boolean {\n return this.callEventsManager.locked;\n }\n\n /** Current custom metadata of the call. */\n public get meta(): Record<string, unknown> {\n return this.callEventsManager.meta;\n }\n\n /** Current layout name, or `undefined` if not set. */\n public get layout(): string | undefined {\n return this.callEventsManager.layout;\n }\n\n /** Observable of available layout names. */\n public get layouts$(): Observable<string[]> {\n return this.deferEmission(this.callEventsManager.layouts$).pipe(takeUntil(this._destroyed$));\n }\n\n /** Current snapshot of available layout names. */\n public get layouts(): string[] {\n return this.callEventsManager.layouts;\n }\n\n /** Observable of the local media stream (camera/microphone). */\n public get localStream$(): Observable<MediaStream> {\n return this.deferEmission(this.vertoManager.localStream$).pipe(takeUntil(this._destroyed$));\n }\n /** Current local media stream, or `null` if not available. */\n public get localStream(): MediaStream | null {\n return this.vertoManager.localStream;\n }\n /** Observable of the remote media stream from the far end. */\n public get remoteStream$(): Observable<MediaStream> {\n return this.deferEmission(this.vertoManager.remoteStream$).pipe(takeUntil(this._destroyed$));\n }\n /** Current remote media stream, or `null` if not available. */\n public get remoteStream(): MediaStream | null {\n return this.vertoManager.remoteStream;\n }\n\n /** Observable of custom user variables associated with the call. */\n public get userVariables$(): Observable<Record<string, unknown>> {\n return this.deferEmission(this._userVariables$.asObservable());\n }\n\n /** a copy of the current custom user variables of the call. */\n public get userVariables(): Record<string, unknown> {\n return { ...this._userVariables$.value };\n }\n\n /** Merge current custom user variables of the call. */\n public set userVariables(variables: Record<string, unknown>) {\n this._userVariables$.next({ ...this._userVariables$.value, ...variables });\n }\n\n // ===========================================================================\n // Resilience observables & methods\n //\n // These use eagerly-created BehaviorSubjects (not publicCachedObservable)\n // so subscribers can attach before 'connected' and still receive updates\n // once the stats monitor and recovery manager are initialized.\n // ===========================================================================\n\n /** Observable of current network health issues (empty array = healthy). */\n public get networkIssues$(): Observable<StatsNetworkIssue[]> {\n return this.deferEmission(this._networkIssues$.asObservable());\n }\n\n /** Current snapshot of network issues. */\n public get networkIssues(): StatsNetworkIssue[] {\n return this._networkIssues$.value;\n }\n\n /** Simple boolean health indicator derived from stats monitor. */\n public get isNetworkHealthy$(): Observable<boolean> {\n return this.deferEmission(this._isNetworkHealthy$.asObservable());\n }\n\n /** Whether the network is currently healthy. */\n public get isNetworkHealthy(): boolean {\n return this._isNetworkHealthy$.value;\n }\n\n /** Rolling history of raw network metrics (RTT, jitter, packet loss, bitrate). */\n public get networkMetrics$(): Observable<StatsNetworkMetrics[]> {\n return this.deferEmission(this._networkMetrics$.asObservable());\n }\n\n /** Current snapshot of the metrics rolling window. */\n public get networkMetrics(): StatsNetworkMetrics[] {\n return this._networkMetrics$.value;\n }\n\n /** Observable of MOS quality score (1-5) computed from stats metrics. */\n public get qualityScore$(): Observable<number> {\n return this.deferEmission(this._qualityScore$.asObservable());\n }\n\n /** Observable of simplified quality level (excellent/good/fair/poor/critical). */\n public get qualityLevel$(): Observable<QualityLevel> {\n return this.deferEmission(this._qualityLevel$.asObservable());\n }\n\n /** Observable of the recovery pipeline state machine. */\n public get recoveryState$(): Observable<RecoveryState> {\n return this.deferEmission(this._recoveryState$.asObservable());\n }\n\n /** Observable of recovery events (keyframe requested, ICE restart, etc.). */\n public get recoveryEvent$(): Observable<RecoveryEvent> {\n return this.deferEmission(this._recoveryEvent$.asObservable());\n }\n\n /** Observable indicating whether the call is bandwidth-constrained. */\n public get bandwidthConstrained$(): Observable<boolean> {\n return this.deferEmission(this._bandwidthConstrained$.asObservable());\n }\n\n /** Observable that emits when server-pushed media params are applied. */\n public get mediaParamsUpdated$(): Observable<MediaParamsEvent> {\n return this.deferEmission(this._mediaParamsUpdated$.asObservable());\n }\n\n /**\n * @internal Emit a media params update event.\n * Called by the VertoManager when server-pushed media params are applied.\n */\n public emitMediaParamsUpdated(event: MediaParamsEvent): void {\n this._mediaParamsUpdated$.next(event);\n }\n\n /** Request a video keyframe via RTCP PLI/FIR. */\n public requestKeyframe(): void {\n this.vertoManager.requestKeyframe?.();\n }\n\n /** Force an ICE restart / re-INVITE. */\n public async requestIceRestart(): Promise<void> {\n await this.vertoManager.requestIceRestart?.();\n }\n\n /**\n * @internal Initialize resilience subsystems when the call reaches 'connected'.\n * Called from within the status subscription to wire stats and recovery.\n */\n private initResilienceSubsystems(): void {\n const pc = this.rtcPeerConnection;\n logger.debug(\n `[Call] initResilienceSubsystems: pc=${pc ? 'exists' : 'undefined'}, connectionState=${pc?.connectionState}`\n );\n if (!pc) {\n logger.warn('[Call] No peer connection available, skipping resilience init');\n return;\n }\n\n try {\n const prefs = PreferencesContainer.instance;\n\n // Create stats monitor\n this._statsMonitor = new RTCStatsMonitor(pc, {\n pollingIntervalMs: prefs.statsPollingInterval,\n baselineSamples: prefs.statsBaselineSamples,\n noAudioPacketThresholdMs: prefs.statsNoPacketThreshold,\n rttSpikeWarningMultiplier: prefs.statsRttSpikeMultiplier,\n rttSpikeCriticalMultiplier: prefs.statsRttSpikeMultiplier * RTT_CRITICAL_TO_WARNING_RATIO,\n packetLossWarningPercent: prefs.statsPacketLossThreshold * 100,\n packetLossCriticalPercent: prefs.statsPacketLossThreshold * 200,\n jitterSpikeMultiplier: prefs.statsJitterSpikeMultiplier,\n historyWindowSeconds: prefs.statsHistorySize\n });\n\n // Create recovery manager with callbacks\n const callbacks: RecoveryCallbacks = {\n requestKeyframe: () => {\n try {\n // Use multi-leg keyframe when available (skips send-only screen share legs)\n if (this.vertoManager.requestKeyframeAll) {\n this.vertoManager.requestKeyframeAll();\n } else {\n this.vertoManager.requestKeyframe?.();\n }\n } catch {\n // Non-fatal: keyframe request is best-effort\n }\n },\n requestIceRestart: async (relayOnly?: boolean) => {\n try {\n if (this.vertoManager.requestIceRestartAll) {\n await this.vertoManager.requestIceRestartAll(relayOnly);\n } else {\n await this.vertoManager.requestIceRestart?.(relayOnly);\n }\n return true;\n } catch {\n return false;\n }\n },\n disableVideo: () => {\n try {\n this.vertoManager.muteMainVideoInputDevice();\n logger.debug('[Call] Recovery manager disabled video');\n } catch {\n logger.debug('[Call] Recovery manager failed to disable video');\n }\n },\n enableVideo: () => {\n this.vertoManager.unmuteMainVideoInputDevice().catch(() => {\n logger.debug('[Call] Recovery manager failed to enable video');\n });\n },\n isNegotiating: () => this.vertoManager.mainPeerConnection.isNegotiating,\n isCallConnected: () => this._lastMergedStatus === 'connected',\n getPeerConnectionState: () => pc.connectionState\n };\n\n const inputs = {\n signalingReady$: this.clientSession.authenticated$\n };\n\n this._recoveryManager = new CallRecoveryManager(callbacks, inputs, {\n debounceTimeMs: prefs.recoveryDebounceTime,\n cooldownMs: prefs.recoveryCooldown,\n iceGracePeriodMs: prefs.iceDisconnectedGracePeriod,\n iceRestartTimeoutMs: prefs.iceRestartTimeout,\n maxAttempts: prefs.maxRecoveryAttempts,\n enableRelayFallback: prefs.enableRelayFallback,\n keyframeMaxBurst: prefs.keyframeMaxBurst,\n keyframeBurstWindowMs: prefs.keyframeBurstWindow,\n keyframeCooldownMs: prefs.keyframeCooldown\n });\n\n // Wire stats monitor → internal subjects so subscribers get live data\n this.subscribeTo(this._statsMonitor.networkIssues$, (issues) => {\n this._networkIssues$.next(issues);\n this._recoveryManager?.reportNetworkIssues(issues);\n });\n this.subscribeTo(this._statsMonitor.isNetworkHealthy$, (healthy) => {\n this._isNetworkHealthy$.next(healthy);\n });\n this.subscribeTo(this._statsMonitor.networkMetrics$, (metrics) => {\n this._networkMetrics$.next(metrics);\n\n // Compute MOS from latest metrics\n if (metrics.length > 0) {\n const latest = metrics[metrics.length - 1];\n const totalRecv = latest.audio.packetsReceived + latest.video.packetsReceived;\n const totalLost = latest.audio.packetsLost + latest.video.packetsLost;\n const lossPct =\n totalRecv + totalLost > 0 ? (totalLost / (totalRecv + totalLost)) * 100 : 0;\n const mos = computeMOS(latest.roundTripTime, latest.audio.jitter, lossPct);\n this._qualityScore$.next(mos);\n this._qualityLevel$.next(mosToQualityLevel(mos));\n\n // Feed bandwidth to recovery manager\n if (latest.availableOutgoingBitrate !== undefined) {\n this._recoveryManager?.reportBandwidth(latest.availableOutgoingBitrate / 1000);\n }\n }\n });\n\n // Wire stats critical issues into recovery triggers\n this.subscribeTo(this._statsMonitor.criticalIssue$, (issue) => {\n this._recoveryManager?.pushTrigger({\n source: 'stats',\n detail: `${issue.type}: ${issue.severity}`,\n issueType: issue.type\n });\n });\n\n // Wire recovery manager → internal subjects\n this.subscribeTo(this._recoveryManager.recoveryState$, (state) => {\n this._recoveryState$.next(state);\n });\n this.subscribeTo(this._recoveryManager.recoveryEvent$, (event) => {\n this._recoveryEvent$.next(event);\n if (event.action === 'max_attempts_reached') {\n logger.warn('[Call] All recovery attempts exhausted, terminating call');\n this.emitError({\n kind: 'network',\n fatal: true,\n error: new Error('Call recovery failed: all attempts exhausted'),\n callId: this.id\n });\n }\n });\n this.subscribeTo(this._recoveryManager.bandwidthConstrained$, (constrained) => {\n this._bandwidthConstrained$.next(constrained);\n });\n\n // Feed browser network events (online/offline/connection_change) into recovery\n if (this._networkChange$) {\n this.subscribeTo(this._networkChange$, (event) => {\n if (event.type === 'offline') {\n this._recoveryManager?.pushTrigger({\n source: 'network',\n detail: 'browser went offline'\n });\n } else if (event.type === 'online') {\n this._recoveryManager?.handleWebSocketReconnect();\n }\n // connection_change (e.g. wifi→cellular) is informational, logged by NetworkMonitor\n });\n }\n\n // Detect WebSocket reconnections: authenticated$ emits true again after\n // the initial connect. Signal the recovery manager so it can distinguish\n // signaling loss (reset counters) from peer connection loss (ICE restart).\n this.subscribeTo(this.clientSession.authenticated$.pipe(skip(1), filter(Boolean)), () => {\n logger.debug('[Call] WebSocket reconnected — notifying recovery manager');\n this._recoveryManager?.handleWebSocketReconnect();\n });\n\n // Start monitoring\n this._statsMonitor.start();\n logger.debug('[Call] Resilience subsystems initialized for call', this.id);\n } catch (error) {\n // Non-fatal: call should still work without resilience features\n logger.warn('[Call] Failed to initialize resilience subsystems:', error);\n }\n }\n\n /**\n * @internal Stop and destroy resilience subsystems (on disconnect/destroy).\n * Clears references so they can be re-created on reconnect.\n */\n private stopResilienceSubsystems(): void {\n try {\n this._statsMonitor?.destroy();\n this._recoveryManager?.destroy();\n } catch {\n // Non-fatal\n }\n this._statsMonitor = undefined;\n this._recoveryManager = undefined;\n }\n\n /** @internal */\n public createParticipant(\n memberId: string,\n selfId?: string | null\n ): Participant | SelfParticipant {\n // Use provided selfId (from call.joined event) or fall back to vertoManager.selfId\n const effectiveSelfId = selfId ?? this.vertoManager.selfId;\n if (memberId === effectiveSelfId) {\n return this.participantFactory.createSelfParticipant(memberId);\n }\n return this.participantFactory.createParticipant(memberId);\n }\n\n /** Observable of the current audio/video send/receive directions. */\n public get mediaDirections$(): Observable<MediaDirections> {\n return this.deferEmission(this.vertoManager.mediaDirections$).pipe(takeUntil(this._destroyed$));\n }\n\n /** Current audio/video send/receive directions. */\n public get mediaDirections(): MediaDirections {\n return this.vertoManager.mediaDirections;\n }\n\n protected get participantsId$(): Observable<string[]> {\n return this.cachedObservable('participantsId$', () =>\n this.participants$.pipe(\n map((participants) => participants.map((participant) => participant.id))\n )\n );\n }\n\n /**\n * Executes a raw JSON-RPC request on the client session.\n *\n * Lower-level than {@link executeMethod} — allows full control over the RPC request structure.\n *\n * @param request - Complete JSON-RPC request object.\n * @param options - Optional RPC execution options (timeout, etc.).\n * @returns The RPC response.\n * @throws {JSONRPCError} If the RPC call returns an error response.\n */\n public async execute<T extends JSONRPCResponse = JSONRPCResponse>(\n request: JSONRPCRequest,\n options?: PendingRPCOptions\n ): Promise<T> {\n return this.clientSession.execute(request, options);\n }\n\n /** Observable of the local participant's member ID. */\n public get selfId$(): Observable<string | null> {\n return this.vertoManager.selfId$;\n }\n\n /** Local participant's member ID, or `null` if not joined. */\n public get selfId(): string | null {\n return this.vertoManager.selfId;\n }\n\n /** Observable of the server node ID handling this call. */\n public get nodeId$(): Observable<string | null> {\n return this.vertoManager.nodeId$;\n }\n\n /** Server node ID handling this call, or `null`. */\n public get nodeId(): string | null {\n return this.vertoManager.nodeId;\n }\n\n private isCallSessionEvent(event: unknown): event is Event {\n try {\n logger.debug('[Call] Checking if event is for this call session:', event);\n const callId =\n getValueFrom<string>(event, 'params.params.callID') ??\n getValueFrom<string>(event, 'params.call_id');\n const roomSessionId = getValueFrom<string>(event, 'params.room_session_id');\n logger.debug(\n `[Call] Extracted session identifiers callID: ${callId} and roomSessionID: ${roomSessionId} from event:`\n );\n return (\n callId === this.id ||\n (!!callId && this.callEventsManager.isCallIdValid(callId)) ||\n (!!roomSessionId && this.callEventsManager.isRoomSessionIdValid(roomSessionId))\n );\n } catch (error) {\n logger.error('[Call] Error checking if event is for this call session:', error);\n return false;\n }\n }\n\n private get callSessionEvents$() {\n return this.cachedObservable('callSessionEvents$', () =>\n this.clientSession.signalingEvent$.pipe(\n filter((event) => this.isCallSessionEvent(event)),\n tap((event) => {\n logger.debug('[Call] Received call session event:', event);\n }),\n takeUntil(this.destroyed$),\n share()\n )\n );\n }\n\n /** Observable of call-updated events. */\n public get callUpdated$(): Observable<CallUpdatedPayload> {\n return this.publicCachedObservable('callUpdated$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isCallUpdatedMetadata, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Observable of member-joined events, emitted when a remote participant joins the call. */\n public get memberJoined$(): Observable<MemberJoinedPayload> {\n return this.publicCachedObservable('memberJoined$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isMemberJoinedMetadata, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Observable of member-left events, emitted when a participant leaves the call. */\n public get memberLeft$(): Observable<MemberLeftPayload> {\n return this.publicCachedObservable('memberLeft$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isMemberLeftMetadata, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n /** Observable of member-updated events (mute, volume, etc.). */\n public get memberUpdated$(): Observable<MemberUpdatedPayload> {\n return this.publicCachedObservable('memberUpdated$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isMemberUpdatedMetadata, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Observable of member-talking events (speech start/stop). */\n public get memberTalking$(): Observable<MemberTalkingPayload> {\n return this.publicCachedObservable('memberTalking$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isMemberTalkingMetadata, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Observable of call state-change events. */\n public get callStates$(): Observable<CallStatePayload> {\n return this.publicCachedObservable('callStates$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isCallStateMetadata, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Observable of layout-changed events. */\n public get layoutUpdates$(): Observable<LayoutChangedPayload> {\n return this.publicCachedObservable('layoutUpdates$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isLayoutChangedMetadata, 'params'),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Underlying `RTCPeerConnection`, for advanced use cases. */\n public get rtcPeerConnection(): RTCPeerConnection | undefined {\n return this.vertoManager.mainPeerConnection.peerConnection;\n }\n /** Observable of raw signaling events as plain objects. */\n public get signalingEvent$(): Observable<Record<string, unknown>> {\n return this.publicCachedObservable('signalingEvent$', () =>\n this.callEvent$.pipe(\n map((event) => JSON.parse(JSON.stringify(event)) as Record<string, unknown>)\n )\n );\n }\n\n // ===========================================================================\n // Custom event subscriptions (Section 18)\n\n /**\n * Subscribe to a custom signaling event type on this call.\n *\n * Returns a cached observable that filters `callSessionEvents$` for events\n * whose `event_type` matches the given string. The observable completes\n * when the call is destroyed.\n *\n * Unlike `signalingEvent$` (which only emits known call-level event types),\n * this method also matches custom/user-defined event types.\n *\n * The SDK does not validate event type strings --- the server decides\n * whether a given type is valid.\n *\n * @param eventType - The event type to subscribe to (e.g. `'my.custom.event'`).\n * @returns An observable that emits matching signaling events.\n *\n * @example\n * ```ts\n * call.subscribe('my.custom.event').subscribe(event => {\n * console.log('Custom event:', event);\n * });\n * ```\n */\n public subscribe(eventType: string): Observable<Record<string, unknown>> {\n const cached = this._customSubscriptions.get(eventType);\n if (cached) {\n return cached;\n }\n\n const filtered$ = this.callSessionEvents$.pipe(\n filter((event) => (event as { event_type?: string }).event_type === eventType),\n map((event) => JSON.parse(JSON.stringify(event)) as Record<string, unknown>),\n takeUntil(this._destroyed$)\n );\n\n this._customSubscriptions.set(eventType, filtered$);\n\n // Send verto.subscribe to the server to add this event type mid-call.\n // The server decides whether the event type is valid — we don't validate.\n this._sendVertoSubscribe(eventType);\n\n return filtered$;\n }\n\n // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\n public get webrtcMessages$() {\n return this.cachedObservable('webrtcMessages$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isWebrtcMessageMetadata, 'params'),\n tap((event) => logger.debug('[Call] Event is a WebRTC message event:', event)),\n takeUntil(this.destroyed$),\n share()\n )\n );\n }\n // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\n public get callEvent$() {\n return this.cachedObservable('callEvent$', () =>\n this.callSessionEvents$.pipe(\n filterAs(isSignalwireCallMetadata, 'params'),\n tap((event) => logger.debug('[Call] Event is a call event:', event)),\n takeUntil(this.destroyed$),\n share()\n )\n );\n }\n // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\n public get layoutEvent$() {\n return this.cachedObservable('layoutEvent$', () =>\n this.callEvent$.pipe(filterAs(isLayoutChangedMetadata, 'params'))\n );\n }\n /**\n * Hangs up the call and releases all resources.\n *\n * Sends a Verto `bye` to the server, transitions status to `'disconnecting'`,\n * then destroys the call. After this, the call instance is no longer usable.\n *\n * @example\n * ```ts\n * await call.hangup();\n * ```\n */\n async hangup(): Promise<void> {\n this._status$.next('disconnecting');\n try {\n await this.vertoManager.bye();\n } finally {\n this.destroy();\n }\n }\n /**\n * Sends DTMF digits on the call.\n *\n * @param dtmf - The digit string to send (e.g. `'1234#'`).\n *\n * @example\n * ```ts\n * await call.sendDigits('1234#');\n * ```\n */\n async sendDigits(dtmf: string): Promise<void> {\n return this.vertoManager.sendDigits(dtmf);\n }\n\n /** Observable of WebRTC-specific signaling messages. */\n\n /** Observable of call-level signaling events. */\n\n /** Observable of layout-changed signaling events. */\n\n /**\n * Accepts an inbound call, optionally overriding media options for the answer.\n *\n * @param options - Optional media constraints for the answer (audio/video).\n *\n * @example\n * ```ts\n * // Accept with defaults\n * call.answer();\n *\n * // Accept audio-only\n * call.answer({ audio: true, video: false });\n * ```\n * @see {@link reject} to decline the call instead.\n * @see {@link answered$} to observe the acceptance state.\n */\n public answer(options?: MediaOptions): void {\n this._answerMediaOptions = options;\n this._answered$.next(true);\n }\n\n /** Media options provided when answering. Used internally by the VertoManager. */\n public get answerMediaOptions(): MediaOptions | undefined {\n return this._answerMediaOptions;\n }\n\n /**\n * Rejects an inbound call, preventing media negotiation.\n *\n * @see {@link answer} to accept the call instead.\n * @see {@link answered$} to observe the rejection state.\n */\n public reject(): void {\n this._answered$.next(false);\n }\n\n /** Observable that emits `true` when answered, `false` when rejected. */\n public get answered$(): Observable<boolean> {\n return this.deferEmission(this._answered$.asObservable());\n }\n\n /**\n * Sets the call layout and participant positions.\n *\n * @param layout - Layout name (must be one of {@link layouts}).\n * @param positions - Map of member IDs to {@link VideoPosition} values.\n * @throws {InvalidParams} If the layout is not in the available {@link layouts}.\n *\n * @example\n * ```ts\n * await call.setLayout('grid-responsive', {\n * [participantId]: 'reserved-0',\n * });\n * ```\n */\n async setLayout(layout: string, positions: Record<string, VideoPosition>): Promise<void> {\n if (!this.layouts.includes(layout)) {\n throw new InvalidParams(\n `Layout ${layout} is not available in the current call layouts: ${this.layouts.join(', ')}`\n );\n }\n\n const selfId = await firstValueFrom(\n this.selfId$.pipe(filter((id): id is string => id !== null))\n );\n\n await this.executeMethod(selfId, 'call.layout.set', {\n layout,\n positions\n });\n }\n\n /**\n * Transfers the call to another destination.\n *\n * @param options - Transfer configuration including the target destination.\n * @see {@link status$} to observe the transfer progress.\n */\n public async transfer(options: TransferOptions): Promise<void> {\n return this.vertoManager.transfer(options);\n }\n\n /** Destroys the call, releasing all resources and subscriptions. */\n public destroy(): void {\n if (this._status$.value === 'destroyed') return;\n this._status$.next('destroyed');\n\n // Destroy resilience subsystems\n this.stopResilienceSubsystems();\n\n this.vertoManager.destroy();\n this.callEventsManager.destroy();\n super.destroy();\n }\n\n /**\n * @internal Send a verto.subscribe message to add an event type to the\n * server's subscription list for this call. Best-effort — failures are\n * logged but don't prevent the filtered observable from being returned.\n */\n private _sendVertoSubscribe(eventType: string): void {\n try {\n const message = VertoSubscribe({\n sessid: this.id,\n eventChannel: [eventType]\n });\n const params = {\n callID: this.id,\n node_id: this.vertoManager.nodeId ?? '',\n message\n };\n void this.clientSession.execute(WebrtcVerto(params)).catch((error: unknown) => {\n logger.warn(`[Call] verto.subscribe for '${eventType}' failed (non-fatal):`, error);\n });\n } catch (error) {\n logger.warn(`[Call] Failed to send verto.subscribe for '${eventType}':`, error);\n }\n }\n}\n","import { CallEventsManager } from './CallEventsManager';\nimport { WebRTCVertoManager } from './VertoManager';\nimport { WebRTCCall } from '../core/entities/Call';\nimport {\n JSONRPCError,\n MediaTrackError,\n RPCTimeoutError,\n TransportConnectionError,\n VertoPongError,\n WebSocketConnectionError\n} from '../core/errors';\n\nimport type { AttachManager } from './AttachManager';\nimport type { NetworkChangeEvent } from '../controllers/NetworkMonitor';\nimport type { Address } from '../core/entities/Address';\nimport type { CallOptions } from '../core/entities/types/call.types';\nimport type { CallError } from '../core/errors';\nimport type { WebRTCApiProvider } from '../dependencies/interfaces';\nimport type { ClientSession } from '../interfaces/ClientSession';\nimport type { DeviceController } from '../interfaces/DeviceController';\nimport type { Observable } from 'rxjs';\n\n/**\n * Infers the semantic error category from a raw Error thrown by VertoManager\n * or an RTCPeerConnection layer.\n */\nfunction inferCallErrorKind(error: Error): CallError['kind'] {\n if (error instanceof RPCTimeoutError) return 'timeout';\n if (error instanceof JSONRPCError) return 'signaling';\n if (error instanceof MediaTrackError) return 'media';\n if (error instanceof WebSocketConnectionError || error instanceof TransportConnectionError)\n return 'network';\n return 'internal';\n}\n\n/** Determines whether an error should be fatal (destroy the call). */\nfunction isFatalError(error: Error): boolean {\n // Transient signaling issues — the call may survive\n if (error instanceof VertoPongError) return false;\n // Media device errors degrade quality but don't end the call\n if (error instanceof MediaTrackError) return false;\n // RPC timeouts during active calls are transient (network outage) — recovery may retry\n if (error instanceof RPCTimeoutError) return false;\n // All other errors (JSONRPCError on invite, connection failures, etc.) are fatal\n return true;\n}\n\n/**\n * Factory for creating WebRTCCall instances with proper manager wiring.\n * Eliminates circular dependencies by centralizing Call and Manager creation.\n */\nexport class CallFactory {\n constructor(\n private sessionManager: ClientSession,\n private deviceController: DeviceController,\n private attachManager: AttachManager,\n private webRTCApiProvider: WebRTCApiProvider,\n private networkChange$?: Observable<NetworkChangeEvent>\n ) {}\n\n /**\n * Create a new WebRTCCall with properly initialized managers\n */\n createCall(address: Address | undefined, options: CallOptions): WebRTCCall {\n const call = new WebRTCCall(\n this.sessionManager,\n options,\n {\n initializeManagers: (callInstance: WebRTCCall) => {\n const vertoManager = new WebRTCVertoManager(\n callInstance,\n this.attachManager,\n this.deviceController,\n this.webRTCApiProvider,\n {\n nodeId: options.nodeId,\n onError: (error: Error) => {\n const callError: CallError = {\n kind: inferCallErrorKind(error),\n fatal: isFatalError(error),\n error,\n callId: callInstance.id\n };\n callInstance.emitError(callError);\n },\n onModifyFailed: () => {\n callInstance.notifyModifyFailed();\n }\n }\n );\n\n const callEventsManager = new CallEventsManager(callInstance);\n\n return {\n vertoManager,\n callEventsManager\n };\n },\n deviceController: this.deviceController,\n networkChange$: this.networkChange$\n },\n address\n );\n\n return call;\n }\n}\n","import {\n defer,\n distinctUntilChanged,\n filter,\n from,\n map,\n ReplaySubject,\n shareReplay,\n switchMap,\n takeUntil,\n skip,\n pipe\n} from 'rxjs';\n\nimport { Destroyable } from './Destroyable';\nimport { GET_PARAMS } from '../controllers/HTTPRequestController';\nimport { CollectionFetchError } from '../core/errors';\nimport { getLogger } from '../utils/logger';\n\nimport type {\n PaginatedResponse,\n Entity,\n FetchController,\n Collection\n} from './types/collection.types';\nimport type { HTTPRequestController } from '../controllers/HTTPRequestController';\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\nexport class Fetcher<T extends Entity = Entity> implements FetchController<Entity> {\n private nextUrl?: string;\n public hasMore: boolean | undefined;\n public filter = (_item: Entity): _item is T => true;\n public mapper = (item: unknown): T => item as T;\n\n constructor(\n protected endpoint: string,\n params: string,\n protected http: HTTPRequestController\n ) {\n this.nextUrl = `${this.endpoint}?${params}`;\n }\n\n public async next(): Promise<T[]> {\n if (!this.nextUrl) {\n this.hasMore = false;\n return [];\n }\n\n const response = await this.http.request({\n ...GET_PARAMS,\n url: this.nextUrl\n });\n if (response.ok && !!response.body) {\n const result = JSON.parse(response.body) as PaginatedResponse<T>;\n this.nextUrl = result.links.next;\n this.hasMore = !!this.nextUrl;\n const filtered = result.data.filter(this.filter);\n return filtered.map(this.mapper);\n }\n logger.error('Failed to fetch entity');\n return [];\n }\n\n public async id(v: unknown): Promise<T | undefined> {\n const response = await this.http.request({\n ...GET_PARAMS,\n url: `${this.endpoint}/${String(v)}`\n });\n if (response.ok && !!response.body) {\n return JSON.parse(response.body) as T;\n }\n }\n}\n\nexport class EntityCollection<T extends Entity = Entity>\n extends Destroyable\n implements Collection<T>\n{\n public hasMore$: Observable<boolean>;\n private collectionData = new Map<string, T>();\n private observablesRegistry = new Map<string, ReplaySubject<T>>();\n private upsertData = (data: Partial<T>) => {\n if (!data.id) return;\n const existing = this.collectionData.get(data.id) ?? {};\n const updated = {} as Record<string, unknown>;\n const allKeys = new Set([...Object.keys(existing), ...Object.keys(data)]);\n for (const key of allKeys) {\n const existingVal = (existing as Record<string, unknown>)[key];\n const newVal = (data as Record<string, unknown>)[key];\n if (\n newVal !== undefined &&\n existingVal !== undefined &&\n typeof existingVal === 'object' &&\n existingVal !== null &&\n !Array.isArray(existingVal) &&\n typeof newVal === 'object' &&\n newVal !== null &&\n !Array.isArray(newVal)\n ) {\n updated[key] = { ...existingVal, ...newVal };\n } else if (newVal !== undefined) {\n updated[key] = newVal;\n } else {\n updated[key] = existingVal;\n }\n }\n this.collectionData.set(data.id, updated as T);\n this.observablesRegistry.get(data.id)?.next(updated as T);\n this._values$.next(Array.from(this.collectionData.values()));\n };\n private _loading$ = this.createBehaviorSubject<boolean>(false);\n private _values$ = this.createReplaySubject<T[]>(1);\n private _hasMore$ = this.createBehaviorSubject<boolean>(true);\n\n constructor(\n private fetchController: FetchController<T>,\n private update$: Observable<Partial<T>>,\n private readonly onError?: (error: Error) => void\n ) {\n super();\n this.subscribeTo(this.update$, this.upsertData);\n this.hasMore$ = defer(() => from(this.init())).pipe(\n switchMap(() => this._hasMore$),\n distinctUntilChanged(),\n shareReplay(1),\n takeUntil(this.destroyed$)\n );\n }\n\n public get loading$(): Observable<boolean> {\n return this._loading$.asObservable();\n }\n\n public get loading(): boolean {\n return this._loading$.value;\n }\n\n public get values$(): Observable<T[]> {\n return this._values$.asObservable();\n }\n\n public get hasMore(): boolean {\n return this.fetchController.hasMore ?? true;\n }\n\n public get updated$(): Observable<void> {\n return this.cachedObservable('updated$', () =>\n this._loading$.pipe(\n distinctUntilChanged(),\n skip(1), // skipping the loading === true event\n filter((loading) => !loading),\n map(() => void 0),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n public get values(): T[] {\n return Array.from(this.collectionData.values());\n }\n\n private async init(): Promise<void> {\n if (this.fetchController.hasMore === false) {\n this._hasMore$.next(false);\n return;\n }\n await this.fetchMore();\n }\n\n private async fetchMore(): Promise<void> {\n try {\n this._loading$.next(true);\n const datas = await this.fetchController.next();\n datas.forEach(this.upsertData);\n this._hasMore$.next(this.fetchController.hasMore ?? false);\n this._loading$.next(false);\n } catch (error) {\n logger.error(`Failed to fetch initial collection data`, error);\n this._hasMore$.next(this.fetchController.hasMore ?? false);\n this._loading$.next(false);\n this.onError?.(new CollectionFetchError('fetchMore', error));\n }\n }\n\n private async tryFetch(key: keyof FetchController<T>, value: unknown): Promise<T | undefined> {\n try {\n this._loading$.next(true);\n const data = await this.fetchController[key]?.(value);\n this._loading$.next(false);\n if (data) {\n this.upsertData(data);\n }\n return data;\n } catch (error) {\n logger.error(`Failed to fetch data for (${String(key)}:${String(value)}) :`, error);\n this._loading$.next(false);\n this.onError?.(new CollectionFetchError(`tryFetch(${String(key)})`, error));\n }\n }\n\n public get$(id: string): Observable<T> | undefined {\n if (!this.observablesRegistry.has(id)) {\n this.observablesRegistry.set(id, new ReplaySubject<T>(1));\n const data = this.collectionData.get(id);\n if (data) {\n this.observablesRegistry.get(id)?.next(data);\n } else {\n void this.tryFetch('id', id);\n }\n }\n return this.observablesRegistry.get(id)?.asObservable();\n }\n\n public async find$(key: keyof T, value: unknown): Promise<Observable<T> | undefined> {\n const data =\n Array.from(this.collectionData.values()).find((item) => item[key] === value) ??\n (await this.tryFetch(key, value));\n\n return data ? this.get$(data.id) : undefined;\n }\n\n public loadMore(): void {\n if (this.fetchController.hasMore !== false) {\n void this.fetchMore();\n }\n }\n\n public override destroy(): void {\n this.observablesRegistry.forEach((subject) => subject.complete());\n this.observablesRegistry.clear();\n super.destroy();\n }\n}\n\nexport class EntityCollectionTransformed<\n O extends Entity = Entity,\n T extends Entity = Entity\n> implements Collection<T> {\n private _values$?: Observable<T[]>;\n\n constructor(\n private originalCollection: EntityCollection<O>,\n private filter: (i: unknown) => i is O = (i): i is O => !!i,\n private mapper: (item: O) => T = (item) => item as unknown as T\n ) {}\n\n public get loading$(): Observable<boolean> {\n return this.originalCollection.loading$;\n }\n\n public get loading(): boolean {\n return this.originalCollection.loading;\n }\n public get hasMore$(): Observable<boolean> {\n return this.originalCollection.hasMore$;\n }\n public get hasMore(): boolean {\n return this.originalCollection.hasMore;\n }\n public get values(): T[] {\n return this.originalCollection.values.filter(this.filter).map(this.mapper);\n }\n public get values$(): Observable<T[]> {\n return (this._values$ ??= this.originalCollection.values$.pipe(\n map((values) => values.filter(this.filter).map(this.mapper))\n ));\n }\n\n public get$(id: string): Observable<T> | undefined {\n const original$ = this.originalCollection.get$(id);\n return !original$ ? original$ : original$.pipe(pipe(filter(this.filter), map(this.mapper)));\n }\n\n public async find$(key: keyof T, value: unknown): Promise<Observable<T> | undefined> {\n const original$ = await this.originalCollection.find$(key as keyof O, value);\n return !original$ ? original$ : original$.pipe(pipe(filter(this.filter), map(this.mapper)));\n }\n public loadMore(): void {\n this.originalCollection.loadMore();\n }\n public destroy(): void {\n this.originalCollection.destroy();\n }\n}\n","import { defer, map, shareReplay, takeUntil, type Observable } from 'rxjs';\n\nimport { EntityCollectionTransformed } from '../../behaviors/Collection';\nimport { Destroyable } from '../../behaviors/Destroyable';\nimport { filterNull } from '../../operators';\nimport { DependencyError, UnimplementedError } from '../errors';\n\nimport type { CallState } from './types/call.types';\nimport type { AddressProvider } from '../../interfaces/AddressProvider';\nimport type {\n ConversationMessageCollection,\n ConversationsProvider\n} from '../../interfaces/Conversations';\nimport type { GetAddressResponse } from '../types/address.types';\nimport type { ResourceType } from '../types/common.types';\nimport type {\n AddressHistory,\n AddressHistoryCollection,\n GetConversationMessageResponse,\n TextMessage,\n TextMessageCollection\n} from '../types/conversation.types';\n\ntype AddressState = GetAddressResponse;\n/**\n * Represents a contact or room in the directory.\n *\n * Provides identity metadata, conversation history, text messaging,\n * and activity state for an address entry.\n */\nexport class Address extends Destroyable {\n private initConversationMessages = async (): Promise<ConversationMessageCollection> => {\n this._conversationMessages =\n this._conversationMessages ??\n (await this.conversationManager.getConversationMessageCollection(this.id));\n if (this._conversationMessages.hasMore) {\n this._conversationMessages.loadMore();\n }\n return this._conversationMessages;\n };\n\n /** Observable of text messages for this address. Lazily loads conversation data. */\n public textMessages$ = defer(this.initConversationMessages).pipe(\n map(() => this.textMessage),\n shareReplay(1),\n takeUntil(this.destroyed$)\n );\n /** Observable of call history for this address. Lazily loads conversation data. */\n public history$ = defer(this.initConversationMessages).pipe(\n map(() => this.history),\n shareReplay(1),\n takeUntil(this.destroyed$)\n );\n private _conversationMessages?: ConversationMessageCollection;\n\n private _state$ = this.createBehaviorSubject<AddressState | null>(null);\n\n private _history$?: AddressHistoryCollection<Address>;\n\n private _textMessages$?: TextMessageCollection<Address>;\n\n // FIXME after presence API is available\n // this should be a dynamic view of the address existing calls,\n // independent if the user is on the call or not.\n // private _callsStates$ = this.createBehaviorSubject<CallState[]>([]);\n\n constructor(\n private readonly addressId: string,\n private conversationManager: ConversationsProvider,\n private addressProvider: AddressProvider<Address>\n ) {\n super();\n }\n\n /** @internal */\n public upnext(state: Partial<AddressState>): void {\n const update = {\n ...this._state$.value,\n ...state\n } as AddressState;\n this._state$.next(update);\n }\n\n /** @internal */\n public get state(): AddressState | null {\n return this._state$.value;\n }\n\n /** Unique address identifier. */\n public get id(): string {\n return this.addressId;\n }\n /** Address name (resource identifier). */\n public get name(): string {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.name;\n }\n\n /** ISO timestamp of when the address was created. */\n public get createdAt(): string {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.created_at;\n }\n\n /** Default communication channel URI (video for rooms, audio otherwise). */\n public get defaultChannel(): string | undefined {\n return this.type === 'room' ? this.channels.video : this.channels.audio;\n }\n\n /** Observable of the human-readable display name. */\n public get displayName$(): Observable<string> {\n return this.cachedObservable('displayName$', () =>\n this._state$.pipe(\n filterNull(),\n map((state) => state.display_name),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Human-readable display name. */\n public get displayName(): string {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.display_name;\n }\n\n /** Observable of the preview image URL. */\n public get previewUrl$(): Observable<string | undefined> {\n return this.cachedObservable('previewUrl$', () =>\n this._state$.pipe(\n filterNull(),\n map((state) => state.preview_url),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Preview image URL. */\n public get previewUrl(): string | undefined {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.preview_url;\n }\n\n /** Observable of the cover image URL. */\n public get coverUrl$(): Observable<string | undefined> {\n return this.cachedObservable('coverUrl$', () =>\n this._state$.pipe(\n filterNull(),\n shareReplay(1),\n map((state) => state.cover_url),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Cover image URL. */\n public get coverUrl(): string | undefined {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.cover_url;\n }\n\n /** Observable of the underlying resource ID. */\n public get resourceId$(): Observable<string> {\n return this.cachedObservable('resourceId$', () =>\n this._state$.pipe(\n filterNull(),\n shareReplay(1),\n map((state) => state.resource_id),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Underlying resource ID. */\n public get resourceId(): string {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.resource_id;\n }\n\n /** Observable of the resource type (e.g. `'room'`, `'subscriber'`). */\n public get type$(): Observable<ResourceType> {\n return this.cachedObservable('type$', () =>\n this._state$.pipe(\n filterNull(),\n shareReplay(1),\n map((state) => state.type),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Resource type (e.g. `'room'`, `'subscriber'`). */\n public get type(): ResourceType {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.type;\n }\n\n /** Observable of available communication channels (audio, video, messaging). */\n public get channels$(): Observable<{\n audio?: string;\n messaging?: string;\n video?: string;\n }> {\n return this.cachedObservable('channels$', () =>\n this._state$.pipe(\n filterNull(),\n shareReplay(1),\n map((state) => state.channels),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /** Available communication channels. */\n public get channels(): {\n audio?: string;\n messaging?: string;\n video?: string;\n } {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.channels;\n }\n\n /** Whether the address (room) is locked. */\n public get locked(): boolean {\n if (!this._state$.value) {\n throw new DependencyError('state not initialized');\n }\n return this._state$.value.locked;\n }\n\n /** Observable indicating whether the address (room) is locked. */\n public get locked$(): Observable<boolean> {\n return this.cachedObservable('locked$', () =>\n this._state$.pipe(\n filterNull(),\n shareReplay(1),\n map((state) => state.locked),\n takeUntil(this.destroyed$)\n )\n );\n }\n\n /**\n * Sends a text message to this address.\n *\n * @param text - The message text to send.\n *\n * @example\n * ```ts\n * await address.sendText('Hello!');\n * ```\n */\n public async sendText(text: string): Promise<void> {\n return this.conversationManager.sendText(text, this.id);\n }\n\n /**\n * Collection of text messages for this address, with pagination support.\n *\n * Returns `undefined` until {@link textMessages$} has been subscribed to (lazy-loaded).\n * Filters to `'chat'` subtype messages from the conversation.\n *\n * @see {@link textMessages$} to trigger lazy loading.\n * @see {@link sendText} to send a new message.\n */\n public get textMessage():\n | EntityCollectionTransformed<GetConversationMessageResponse, TextMessage<Address>>\n | undefined {\n if (!this._conversationMessages) {\n return;\n }\n this._textMessages$ =\n this._textMessages$ ??\n new EntityCollectionTransformed<GetConversationMessageResponse, TextMessage<Address>>(\n this._conversationMessages,\n (item): item is GetConversationMessageResponse =>\n (item as GetConversationMessageResponse).subtype === 'chat',\n (item) =>\n ({\n id: item.id,\n text: item.text,\n created: item.ts,\n fromAddress$: this.addressProvider.get$(item.from_fabric_address_id)\n }) as TextMessage<Address>\n );\n return this._textMessages$;\n }\n\n /**\n * Collection of call history entries for this address, with pagination support.\n *\n * Returns `undefined` until {@link history$} has been subscribed to (lazy-loaded).\n * Filters to `'log'` subtype messages including kind, status, start/end times.\n *\n * @see {@link history$} to trigger lazy loading.\n */\n public get history():\n | EntityCollectionTransformed<GetConversationMessageResponse, AddressHistory<Address>>\n | undefined {\n if (!this._conversationMessages) {\n return;\n }\n this._history$ =\n this._history$ ??\n new EntityCollectionTransformed<GetConversationMessageResponse, AddressHistory<Address>>(\n this._conversationMessages,\n (item): item is GetConversationMessageResponse =>\n (item as GetConversationMessageResponse).subtype === 'log',\n (item) =>\n ({\n id: item.id,\n kind: item.kind,\n status: item.details.status,\n started: item.details.start_time,\n ended: item.details.end_time,\n fromAddress$: this.addressProvider.get$(item.from_fabric_address_id)\n }) as AddressHistory<Address>\n );\n return this._history$;\n }\n\n /** Observable of active call states for this address. @throws {UnimplementedError} Requires presence support. */\n public get activity$(): Observable<CallState[]> {\n // NEEDS Presence\n throw new UnimplementedError();\n }\n\n /** Active call states for this address. @throws {UnimplementedError} Requires presence support. */\n public get activity(): CallState[] {\n // NEEDS Presence\n throw new UnimplementedError();\n }\n}\n","import { filter, NEVER, Observable, race, take } from 'rxjs';\nimport { v4 as uuid } from 'uuid';\n\nimport { InvalidListenerError, JSONRPCError, RPCTimeoutError } from './errors';\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { getLogger } from '../utils/logger';\n\nimport type { RPCConnectResult } from './RPCMessages';\nimport type { JSONRPCRequest, JSONRPCResponse } from './RPCMessages/types/base';\n\nconst logger = getLogger();\n\nexport async function callListener<T>(\n listener: ((value: T) => void) | ((value: T) => Promise<void>),\n value: T,\n onError?: (error: unknown) => void\n): Promise<void> {\n try {\n if (typeof listener !== 'function') {\n throw new InvalidListenerError();\n }\n await listener(value);\n } catch (error) {\n if (error instanceof InvalidListenerError) {\n logger.error(error.message);\n } else {\n logger.warn('Error calling listener:', error);\n }\n onError?.(error);\n }\n}\n\nexport const isRPCConnectResult = (e: unknown): e is RPCConnectResult => {\n logger.debug('isRPCConnectResult check:', e);\n if (!e || typeof e !== 'object') return false;\n\n // Check if this is a JSON-RPC response with a result property\n\n const result = e as RPCConnectResult;\n\n const is =\n typeof result.identity === 'string' &&\n typeof result.protocol === 'string' &&\n typeof result.authorization === 'object' &&\n typeof result.authorization.jti === 'string' &&\n typeof result.authorization.project_id === 'string' &&\n typeof result.authorization.fabric_subscriber === 'object';\n\n logger.debug('isRPCConnectResult check result:', is);\n return is;\n};\n\nexport interface PendingRPCOptions {\n /**\n * Timeout in milliseconds. Defaults to 5000ms (5 seconds).\n * If the response is not received within this time, the promise will reject with RPCTimeoutError.\n */\n timeoutMs?: number;\n\n /**\n * Optional AbortSignal for cancellation support.\n * If the signal is aborted, the promise will reject with an AbortError.\n */\n signal?: AbortSignal;\n}\n\nexport class PendingRPC<T extends JSONRPCResponse = JSONRPCResponse> {\n private static readonly defaultTimeoutMs = 5000;\n private id = uuid();\n\n public readonly request: JSONRPCRequest;\n public promise: Promise<T>;\n\n constructor(request: JSONRPCRequest, responses$: Observable<T>, options?: PendingRPCOptions) {\n logger.debug(\n `[PendingRPC(${this.id}) request:${request.id}: method:${request.method}] Creating PendingRPC`\n );\n this.request = request;\n\n const timeoutMs = options?.timeoutMs ?? PendingRPC.defaultTimeoutMs;\n const signal = options?.signal;\n\n this.promise = new Promise<T>((resolve, reject) => {\n // Check if already aborted\n if (signal?.aborted) {\n reject(new DOMException('The operation was aborted', 'AbortError'));\n return;\n }\n\n // Track if promise has been settled to prevent unhandled rejections\n let isSettled = false;\n\n // Create the main response observable\n const response$ = responses$.pipe(\n filter((result) => result.id === request.id),\n take(1)\n );\n\n // Create timeout observable\n const timeout$ = new Observable<never>((subscriber) => {\n const timer = setTimeout(() => {\n subscriber.error(new RPCTimeoutError(request.id, timeoutMs));\n }, timeoutMs);\n\n return () => clearTimeout(timer);\n });\n\n // Create abort observable if signal provided\n const abort$ = signal\n ? new Observable<never>((subscriber) => {\n const abortHandler = () => {\n subscriber.error(new DOMException('The operation was aborted', 'AbortError'));\n };\n signal.addEventListener('abort', abortHandler);\n\n return () => signal.removeEventListener('abort', abortHandler);\n })\n : NEVER; // Observable that never emits\n\n // Race between response, timeout, and abort\n const subscription = race(response$, timeout$, abort$).subscribe({\n next: (response) => {\n isSettled = true;\n if (response.error) {\n const rpcError = new JSONRPCError(\n response.error.code,\n response.error.message,\n response.error.data,\n undefined,\n request.id\n );\n logger.debug(\n `[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with RPC error:`,\n rpcError\n );\n reject(rpcError);\n } else {\n logger.debug(\n `[PendingRPC(${this.id}) request:${request.id}] Resolving promise with response:`,\n response\n );\n resolve(response);\n }\n subscription.unsubscribe();\n },\n error: (error) => {\n logger.debug(\n `[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with error:`,\n error\n );\n isSettled = true;\n reject(error as Error);\n subscription.unsubscribe();\n },\n complete: () => {\n logger.debug(`[PendingRPC(${this.id}) request:${request.id}] Observable completed`);\n if (!isSettled) {\n reject(new RPCTimeoutError(request.id, timeoutMs));\n }\n subscription.unsubscribe();\n }\n });\n });\n }\n\n // Make it thenable (Promise-like)\n async then<TResult1 = JSONRPCResponse, TResult2 = never>(\n onfulfilled?: ((value: JSONRPCResponse) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null\n ): Promise<TResult1 | TResult2> {\n return this.promise.then(onfulfilled, onrejected);\n }\n\n async catch<TResult = never>(\n onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null\n ): Promise<JSONRPCResponse | TResult> {\n return this.promise.catch(onrejected);\n }\n\n async finally(onfinally?: (() => void) | null): Promise<JSONRPCResponse> {\n return this.promise.finally(onfinally);\n }\n}\n\n// Re-export Destroyable for backward compatibility\nexport { Destroyable };\n","import {\n catchError,\n combineLatest,\n defer,\n distinctUntilChanged,\n EMPTY,\n exhaustMap,\n filter,\n firstValueFrom,\n from,\n lastValueFrom,\n map,\n race,\n share,\n shareReplay,\n switchMap,\n take,\n takeUntil,\n tap,\n throwError,\n timeout,\n TimeoutError\n} from 'rxjs';\n\nimport { CallFactory } from './CallFactory';\nimport {\n RPC_ERROR_REQUESTER_VALIDATION_FAILED,\n RPC_ERROR_INVALID_PARAMS,\n RPC_ERROR_AUTHENTICATION_FAILED\n} from '../core/constants';\nimport { Address } from '../core/entities/Address';\nimport {\n AuthStateHandlerError,\n CallCreateError,\n DependencyError,\n JSONRPCError,\n UnexpectedError,\n VertoAttachHandlerError,\n VertoInviteHandlerError\n} from '../core/errors';\nimport { RPCConnect, RPCReauthenticate } from '../core/RPCMessages';\nimport {\n isSignalwireAuthorizationStateMetadata,\n isSignalwireRequest,\n isWebrtcMessageMetadata\n} from '../core/RPCMessages/guards/events.guards';\nimport {\n isVertoAttachMessage,\n isVertoInviteMessage\n} from '../core/RPCMessages/guards/verto.guards';\nimport { Destroyable, isRPCConnectResult } from '../core/utils';\nimport { filterAs } from '../operators/filterEventAs';\nimport { throwOnRPCError } from '../operators/throwOnRPCError';\nimport { getLogger } from '../utils/logger';\n\nimport type { AttachManager } from './AttachManager';\nimport type { StorageManager } from './StorageManager';\nimport type { TransportManager } from './TransportManager';\nimport type { CryptoController } from '../controllers/CryptoController';\nimport type { NetworkChangeEvent } from '../controllers/NetworkMonitor';\nimport type { WebRTCCall } from '../core/entities/Call';\nimport type { Directory } from '../core/entities/Directory';\nimport type { Call, CallOptions } from '../core/entities/types/call.types';\nimport type {\n RPCConnectParams,\n RPCConnectAuthentication,\n Authorization,\n RPCReauthenticateParams\n} from '../core/RPCMessages';\nimport type { JSONRPCRequest, JSONRPCResponse } from '../core/RPCMessages/types/base';\nimport type { VertoAttachParams, VertoInviteParams } from '../core/RPCMessages/types/verto';\nimport type { SDKCredential, JSONSerializable } from '../core/types/common.types';\nimport type { PendingRPCOptions } from '../core/utils';\nimport type { WebRTCApiProvider } from '../dependencies/interfaces';\nimport type { DeviceController } from '../interfaces/DeviceController';\nimport type { SessionState } from '../interfaces/SessionState';\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\nconst getAddressSearchURI = (options: CallOptions): string => {\n const to = options.to?.split('?')[0];\n const from = options.from?.startsWith('subscriber://')\n ? options.from.replace('subscriber://', '')\n : options.from;\n const name = to ?? from;\n if (!name) {\n throw new UnexpectedError('Error building Address name');\n }\n return name;\n};\n\n/**\n * Discriminated union for session authentication state.\n * clientBound is tracked separately via _wasClientBound (sticky flag)\n * to avoid dual sources of truth.\n */\nexport type SessionAuthState = { kind: 'unauthenticated' } | { kind: 'authenticated' };\n\nexport class ClientSessionManager extends Destroyable implements SessionState {\n private callFactory: CallFactory;\n private callCreateTimeout = 6000;\n private readonly agent = `signalwire-typescript-sdk/1.0.0`;\n private readonly eventAcks = true;\n public initialized$: Observable<boolean>;\n private authorizationState$ = this.createReplaySubject<string | undefined>(1);\n private connectVersion = {\n major: 4,\n minor: 0,\n revision: 0\n };\n /**\n * Optional hook called before a fresh connect on reconnect.\n * Used by SignalWire to refresh expired credentials before re-authenticating.\n * @internal\n */\n public onBeforeReconnect?: () => Promise<void>;\n private _authorization$ = this.createBehaviorSubject<Authorization | undefined>(undefined);\n private _errors$ = this.createReplaySubject<Error>(1);\n private _directory?: Directory;\n\n private _authState$ = this.createBehaviorSubject<SessionAuthState>({ kind: 'unauthenticated' });\n /** Sticky flag — once true, stays true for the session lifetime. */\n private _wasClientBound = false;\n\n private _subscriberInfo$ = this.createBehaviorSubject<Address | null>(null);\n private _calls$ = this.createBehaviorSubject<Record<string, Call>>({});\n private _iceServers$ = this.createBehaviorSubject<RTCIceServer[]>([]);\n\n constructor(\n private readonly getCredential: () => SDKCredential,\n private readonly transport: TransportManager,\n private readonly storage: StorageManager,\n private readonly authorizationStateKey: string,\n deviceController: DeviceController,\n private readonly attachManager: AttachManager,\n webRTCApiProvider: WebRTCApiProvider,\n private readonly dpopManager?: CryptoController,\n networkChange$?: Observable<NetworkChangeEvent>\n ) {\n super();\n attachManager.setSession(this);\n this.callFactory = new CallFactory(\n this,\n deviceController,\n attachManager,\n webRTCApiProvider,\n networkChange$\n );\n this.initialized$ = defer(() => from(this.init())).pipe(\n shareReplay(1),\n takeUntil(this.destroyed$)\n );\n }\n public get incomingCalls$(): Observable<Call[]> {\n return this.cachedObservable('incomingCalls$', () =>\n this.calls$.pipe(map((calls) => calls.filter((call) => call.direction === 'inbound')))\n );\n }\n\n public get incomingCalls(): Call[] {\n const calls = Object.values(this._calls$.value);\n return calls.filter((call) => call.direction === 'inbound');\n }\n\n public get subscriberInfo$(): Observable<Address | null> {\n return this._subscriberInfo$.asObservable();\n }\n\n public get subscriberInfo(): Address | null {\n return this._subscriberInfo$.value;\n }\n\n public get calls$(): Observable<Call[]> {\n return this.cachedObservable('calls$', () =>\n this._calls$.pipe(map((calls) => Object.values(calls)))\n );\n }\n\n public get calls(): Call[] {\n return Object.values(this._calls$.value);\n }\n\n public get iceServers(): RTCIceServer[] | undefined {\n return this._iceServers$.value;\n }\n\n public get authorization$(): Observable<Authorization | undefined> {\n return this._authorization$.asObservable();\n }\n\n public get authorization(): Authorization | undefined {\n return this._authorization$.value;\n }\n\n public get errors$(): Observable<Error> {\n return this._errors$.asObservable();\n }\n\n public get authenticated$(): Observable<boolean> {\n return this._authState$.pipe(\n map((state) => state.kind === 'authenticated'),\n distinctUntilChanged()\n );\n }\n\n public get authenticated(): boolean {\n return this._authState$.value.kind === 'authenticated';\n }\n\n /**\n * Whether this session is client-bound (using a Client Bound SAT).\n * When client-bound, DPoP proof creation failures are treated as\n * authentication errors rather than silently degraded.\n * @internal\n */\n public get clientBound(): boolean {\n return this._wasClientBound;\n }\n\n /** @internal Current auth state for debugging/testing. */\n public get authState(): SessionAuthState {\n return this._authState$.value;\n }\n\n /**\n * Set the directory instance\n * Called by SignalWire after directory is created\n * @internal\n */\n public setDirectory(directory: Directory): void {\n this._directory = directory;\n }\n\n public async execute<T extends JSONRPCResponse = JSONRPCResponse>(\n request: JSONRPCRequest,\n options?: PendingRPCOptions\n ): Promise<T> {\n try {\n return await this.transport.execute(request, options);\n } catch (error) {\n logger.debug('[Session] Execute Error', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n throw error;\n }\n }\n\n public send(message: JSONSerializable): void {\n this.transport.send(message);\n }\n\n private async init(): Promise<boolean> {\n await this.loadAuthorizationStateFromStorage();\n this.setupMessageHandlers();\n return true;\n }\n\n private setupMessageHandlers(): void {\n logger.debug('[Session] Setting up message handlers');\n\n this.subscribeTo(this.authStateEvent$, async (authStateEvent) => {\n logger.debug('[Session] Authorization state event received:', authStateEvent);\n try {\n await this.updateAuthorizationStateInStorage(authStateEvent.authorization_state);\n } catch (error) {\n logger.error('[Session] Failed to handle authorization state update:', error);\n this._errors$.next(new AuthStateHandlerError(error));\n }\n });\n\n // Reset auth state when transport disconnects or starts reconnecting,\n // so that authenticated$ transitions through false → true on reconnect.\n // This makes the state model truthful and allows distinctUntilChanged()\n // to work correctly for subscribers that detect reconnect.\n // Note: WebSocket close with auto-reconnect emits 'reconnecting', not\n // 'disconnected'. Only explicit disconnect() emits 'disconnected'.\n this.subscribeTo(\n this.transport.connectionStatus$.pipe(\n filter((status) => status === 'disconnected' || status === 'reconnecting')\n ),\n () => {\n if (this._authState$.value.kind === 'authenticated') {\n this._authState$.next({ kind: 'unauthenticated' });\n }\n }\n );\n\n this.subscribeTo(\n this.transport.connectionStatus$.pipe(\n filter((status) => status === 'connected'),\n exhaustMap(() => {\n logger.debug('[Session] Connection established, initiating authentication');\n return from(this.authenticate()).pipe(\n catchError((error) => {\n this.handleAuthenticationError(error as Error).catch((err) => {\n logger.error('[Session] Error handling authentication failure:', err);\n });\n return EMPTY;\n })\n );\n })\n ),\n undefined\n );\n\n this.subscribeTo(this.vertoInvite$, async (invite) => {\n logger.debug('[Session] Verto invite received:', invite);\n try {\n await this.createInboundCall(invite);\n } catch (error) {\n logger.error('[Session] Error handling Verto invite:', error);\n this._errors$.next(new VertoInviteHandlerError(error));\n }\n });\n\n this.subscribeTo(this.vertoAttach$, async (attach) => {\n logger.debug('[Session] Verto attach received:', attach);\n try {\n await this.handleVertoAttach(attach);\n } catch (error) {\n logger.error('[Session] Error handling Verto attach:', error);\n this._errors$.next(new VertoAttachHandlerError(error));\n }\n });\n }\n\n private async loadAuthorizationStateFromStorage(): Promise<void> {\n try {\n const storedState = await this.storage.getItem<string>(this.authorizationStateKey);\n // Always emit a value, even if undefined, so combineLatest can proceed\n this.authorizationState$.next(storedState ?? undefined);\n } catch (error) {\n logger.error('Failed to retrieve authorization state from storage:', error);\n // Emit undefined on error so authentication can proceed without stored state\n this.authorizationState$.next(undefined);\n }\n }\n\n private async updateAuthorizationStateInStorage(authorizationState?: string): Promise<void> {\n if (!authorizationState) {\n logger.debug('[Session] Removing authorization state from storage');\n try {\n await this.storage.removeItem(this.authorizationStateKey);\n this.authorizationState$.next(undefined);\n } catch (error) {\n logger.error('Failed to remove authorization state from storage:', error);\n throw error;\n }\n return;\n }\n\n try {\n logger.debug('[Session] Updating authorization state in storage');\n await this.storage.setItem(this.authorizationStateKey, authorizationState);\n this.authorizationState$.next(authorizationState);\n } catch (error) {\n logger.error('Failed to retrieve authorization state from storage:', error);\n throw error;\n }\n }\n\n private get authStateEvent$() {\n return this.cachedObservable('authStateEvent$', () =>\n this.signalingEvent$.pipe(\n tap((msg) => {\n logger.debug('[Session] Received incoming message:', msg);\n }),\n filterAs(isSignalwireAuthorizationStateMetadata, 'params'),\n tap((event) => {\n logger.debug('[Session] Authorization state event received:', event.authorization_state);\n })\n )\n );\n }\n\n // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\n public get signalingEvent$() {\n return this.cachedObservable('signalingEvent$', () =>\n this.transport.incomingEvent$.pipe(filterAs(isSignalwireRequest, 'params'), share())\n );\n }\n\n private get vertoInvite$() {\n return this.cachedObservable('vertoInvite$', () =>\n this.signalingEvent$.pipe(\n filter(isWebrtcMessageMetadata),\n filter((event) => isVertoInviteMessage(event.params)),\n map((event) => ({\n node_id: event.node_id,\n ...(event.params.params as VertoInviteParams)\n }))\n )\n );\n }\n\n private get vertoAttach$() {\n return this.cachedObservable('vertoAttach$', () =>\n this.signalingEvent$.pipe(\n filter(isWebrtcMessageMetadata),\n filter((event) => isVertoAttachMessage(event.params)),\n map((event) => ({\n node_id: event.node_id,\n ...(event.params.params as VertoAttachParams)\n }))\n )\n );\n }\n\n private get contexts(): string[] {\n return [];\n }\n\n private get eventing(): string[] {\n return [];\n }\n\n private get topics(): string[] {\n return [];\n }\n\n private get authentication(): RPCConnectAuthentication {\n const credential = this.getCredential();\n if (!credential.token) {\n throw new DependencyError('Credential token is undefined');\n }\n return {\n jwt_token: credential.token\n };\n }\n\n async connect(): Promise<void> {\n // Ensure session is initialized before proceeding\n await firstValueFrom(this.initialized$);\n\n // Initiate the WebSocket connection. The exhaustMap subscription in\n // setupMessageHandlers triggers authenticate() when the WS opens.\n await this.transport.connect();\n\n // Gate: don't return until authentication succeeds or fails permanently.\n // If auth fails with stale authorization_state, handleAuthenticationError\n // cleans up and reconnects. The exhaustMap fires authenticate() again on\n // the new 'connected' event. When it succeeds, authenticated$ emits true.\n // takeUntil(destroyed$) terminates if the session is destroyed mid-auth.\n // timeout prevents hanging when the server never responds (e.g. test mocks).\n await firstValueFrom(\n this.authenticated$.pipe(\n takeUntil(this.destroyed$),\n filter(Boolean),\n take(1),\n timeout({ first: 15000 })\n )\n );\n }\n\n private async handleAuthenticationError(error: Error): Promise<void> {\n logger.error('Authentication error:', error);\n\n const isRecoverableAuthError =\n error instanceof JSONRPCError &&\n (error.code === RPC_ERROR_REQUESTER_VALIDATION_FAILED ||\n error.code === RPC_ERROR_INVALID_PARAMS ||\n error.code === RPC_ERROR_AUTHENTICATION_FAILED);\n\n // Only fall back if there was stored authorization_state to clear (reconnect attempt).\n // If this was already a fresh connect, don't loop — the credentials are genuinely bad.\n // Check authorizationState$ (the string we sent) not _authorization$ (the response object).\n const sentStoredState = await firstValueFrom(this.authorizationState$.pipe(take(1)));\n const hasStoredState = sentStoredState !== undefined;\n\n if (isRecoverableAuthError && hasStoredState) {\n // Server rejected reconnect — expired/corrupted authorization_state or jti drift.\n // Clean up stored state and reconnect fresh with SAT + DPoP.\n // Don't push to _errors$ — this is a transient error the SDK will self-heal.\n logger.debug(\n '[Session] Recoverable auth error — cleaning up stored state and reconnecting fresh'\n );\n try {\n await this.cleanupStoredConnectionParams();\n } catch (cleanupError) {\n logger.error('Failed to cleanup stored connection params:', cleanupError);\n } finally {\n this.transport.reconnect();\n }\n } else {\n // Fatal auth error — surface to consumers\n this._errors$.next(error);\n }\n }\n\n async cleanupStoredConnectionParams(): Promise<void> {\n await this.transport.setProtocol(undefined);\n await this.updateAuthorizationStateInStorage(undefined);\n this._authorization$.next(undefined);\n // Do NOT detachAll here — attached calls are still valid.\n // Only the authorization_state and protocol are stale.\n // reattachCalls() needs the stored call references after recovery.\n }\n\n protected async updateAuthState(authorization_state: string): Promise<void> {\n try {\n await this.storage.setItem(this.authorizationStateKey, authorization_state);\n } catch (error) {\n logger.error('Failed to update authorization state in storage:', error);\n this._errors$.next(new AuthStateHandlerError(error));\n }\n }\n\n public async reauthenticate(\n token: string,\n dpopToken?: string,\n options?: { clientBound?: boolean }\n ): Promise<void> {\n logger.debug('[Session] Re-authenticating session');\n try {\n // Resolve DPoP proof: use provided, generate fresh, or omit\n let resolvedDpopToken = dpopToken;\n if (!resolvedDpopToken && this.dpopManager?.initialized) {\n try {\n resolvedDpopToken = await this.dpopManager.createRpcProof({\n method: 'signalwire.reauthenticate'\n });\n } catch (error) {\n if (this.clientBound) {\n // Client-bound sessions must not silently degrade to unbound\n throw error;\n }\n logger.warn('[Session] Failed to create DPoP proof for reauthenticate:', error);\n }\n }\n\n const params: RPCReauthenticateParams = {\n project: this._authorization$.value?.project_id ?? '',\n jwt_token: token,\n ...(resolvedDpopToken ? { dpop_token: resolvedDpopToken } : {})\n };\n\n const request = RPCReauthenticate(params);\n\n await lastValueFrom(\n from(this.transport.execute(request)).pipe(\n throwOnRPCError(),\n take(1),\n catchError((err) => {\n logger.error('[Session] Re-authentication RPC failed:', err);\n throw err;\n })\n )\n );\n\n // Mark session as client-bound if requested (set-once semantics)\n if (options?.clientBound) {\n this._wasClientBound = true;\n }\n\n logger.debug('[Session] Re-authentication successful, updating stored auth state');\n } catch (error) {\n logger.error('[Session] Re-authentication failed:', error);\n this._errors$.next(new AuthStateHandlerError(error));\n throw error;\n }\n }\n\n private async authenticate(): Promise<void> {\n logger.debug('[Session] Starting authentication process');\n\n const persistedParams = await firstValueFrom(\n combineLatest({\n protocol: this.transport.protocol$,\n authorization_state: this.authorizationState$\n }).pipe(take(1))\n );\n\n logger.debug('[Session] Persisted params:\\n', {\n protocol: persistedParams.protocol,\n authStateLength: persistedParams.authorization_state?.length\n });\n\n const hasReconnectState = persistedParams.authorization_state && persistedParams.protocol;\n const storedToken = this.getCredential().token;\n // Reconnect if we have stored state AND a stored token\n const isReconnect = hasReconnectState && storedToken;\n\n let dpopToken: string | undefined;\n\n if (isReconnect) {\n // RECONNECT: send stored jwt_token (even if expired) + authorization_state + protocol + DPoP\n // authorization_state short-circuits token validation; jwt_token is needed for session classification\n logger.debug('[Session] Reconnecting with stored jwt_token + authorization_state');\n } else {\n // FRESH CONNECT: refresh credentials if needed, then use fresh SAT + DPoP\n if (this.onBeforeReconnect && this.clientBound) {\n logger.debug('[Session] Refreshing credentials before fresh connect');\n await this.onBeforeReconnect();\n }\n }\n\n // DPoP proof:\n // - Fresh connect: always send (server needs it for DPoP binding)\n // - Live WS reconnect (_clientBound=true): send (session expects DPoP)\n // - Page reload reconnect (_clientBound=false): skip (original SAT has no cnf.jkt,\n // DeviceTokenManager re-activates after reconnect to restore DPoP binding)\n if ((!isReconnect || this.clientBound) && this.dpopManager?.initialized) {\n try {\n dpopToken = await this.dpopManager.createRpcProof({\n method: 'signalwire.connect'\n });\n } catch (error) {\n if (this.clientBound) {\n throw error;\n }\n logger.warn(\n '[Session] Failed to create DPoP proof for connect, proceeding without:',\n error\n );\n }\n }\n\n const params: RPCConnectParams = {\n authentication: isReconnect ? { jwt_token: storedToken } : this.authentication,\n version: this.connectVersion,\n agent: this.agent,\n contexts: this.contexts,\n eventing: this.eventing,\n topics: this.topics,\n event_acks: this.eventAcks,\n ...(dpopToken ? { dpop_token: dpopToken } : {}),\n ...(isReconnect\n ? {\n authorization_state: persistedParams.authorization_state,\n protocol: persistedParams.protocol\n }\n : {})\n };\n\n const rpcConnectRequest = RPCConnect(params);\n\n const response = await lastValueFrom(\n from(this.transport.execute(rpcConnectRequest)).pipe(\n throwOnRPCError(),\n map((res) => res.result),\n filter(isRPCConnectResult),\n tap(() => {\n logger.debug('[Session] Response passed filter, processing authentication result');\n }),\n take(1),\n catchError((err) => {\n logger.error('[Session] Authentication RPC failed:', err);\n throw err;\n })\n )\n );\n\n logger.debug('[Session] Processing authentication result:', {\n hasProtocol: !!response.protocol,\n hasAuthorization: !!response.authorization,\n hasIceServers: !!response.ice_servers\n });\n\n if (response.protocol) {\n await this.transport.setProtocol(response.protocol);\n }\n this._authorization$.next(response.authorization);\n this._iceServers$.next(response.ice_servers ?? []);\n this._authState$.next({ kind: 'authenticated' });\n\n logger.debug('[Session] Authentication completed successfully');\n }\n\n async disconnect(): Promise<void> {\n this.transport.disconnect();\n this._authState$.next({ kind: 'unauthenticated' });\n await this.cleanupStoredConnectionParams();\n }\n\n private async createInboundCall(invite: VertoInviteParams & { node_id: string }): Promise<void> {\n const callSession = await this.createCall({\n nodeId: invite.node_id,\n callId: invite.callID,\n initOffer: invite.sdp,\n toName: invite.callee_id_name,\n to: invite.callee_id_number,\n fromName: invite.caller_id_name,\n from: invite.caller_id_number,\n displayDirection: invite.display_direction,\n userVariables: invite.userVariables\n });\n\n await firstValueFrom(callSession.status$);\n\n this._calls$.next({\n [`${callSession.id}`]: callSession,\n ...this._calls$.value\n });\n }\n\n /**\n * Handle a server-pushed verto.attach event at the session level.\n *\n * On page reload the server detects the reconnected session and pushes\n * verto.attach for any active calls. If a call object already exists\n * (network blip, no reload), the per-call handler in VertoManager deals\n * with it. This method only creates a new call object when no existing\n * one matches the callID.\n */\n private async handleVertoAttach(attach: VertoAttachParams & { node_id: string }): Promise<void> {\n const { callID } = attach;\n\n // If a call object already exists, the per-call VertoManager handler\n // will handle the attach event. Skip session-level creation.\n const existingCalls = this._calls$.value;\n if (callID in existingCalls) {\n logger.debug(\n `[Session] Verto attach for existing call ${callID}, deferring to per-call handler`\n );\n return;\n }\n\n // Look up stored attachment data for media options\n const storedOptions = this.attachManager.consumePendingAttachment(callID);\n\n logger.debug(`[Session] Creating reattached call for callID: ${callID}`);\n\n const callSession = await this.createCall({\n nodeId: attach.node_id,\n callId: callID,\n toName: attach.callee_id_name,\n to: attach.callee_id_number,\n fromName: attach.caller_id_name,\n from: attach.caller_id_number,\n reattach: true,\n ...storedOptions\n });\n\n await firstValueFrom(callSession.status$);\n\n this._calls$.next({\n [`${callSession.id}`]: callSession,\n ...this._calls$.value\n });\n }\n\n public async createOutboundCall(\n destination: string | Address,\n options: CallOptions = {}\n ): Promise<Call> {\n const destinationURI =\n destination instanceof Address ? destination.defaultChannel : destination;\n let callSession: WebRTCCall | undefined;\n try {\n callSession = await this.createCall({\n to: destinationURI,\n ...options\n });\n\n await firstValueFrom(\n race(\n callSession.selfId$.pipe(\n filter((id) => Boolean(id)),\n take(1),\n timeout(this.callCreateTimeout)\n ),\n callSession.errors$.pipe(\n take(1),\n switchMap((callError) => throwError(() => callError.error))\n )\n )\n );\n\n this._calls$.next({\n [`${callSession.id}`]: callSession,\n ...this._calls$.value\n });\n\n return callSession;\n } catch (error) {\n logger.error('[Session] Error creating outbound call:', error);\n callSession?.destroy();\n const message =\n error instanceof TimeoutError ? 'Call create timeout' : 'Call creation failed';\n const callError = new CallCreateError(message, error, 'outbound');\n this._errors$.next(callError);\n throw callError;\n }\n }\n\n private async createCall(options: CallOptions = {}): Promise<WebRTCCall> {\n try {\n const addressURI = getAddressSearchURI(options);\n\n // For PSTN numbers (starting with +), skip the directory lookup\n // and create the call directly with the phone number as destination.\n let address: Address | undefined;\n try {\n if (!this._directory) {\n throw new DependencyError('Directory not initialized');\n }\n\n const addressId = await this._directory.findAddressIdByURI(addressURI);\n if (!addressId) {\n throw new DependencyError(`Address name: ${addressURI} not found`);\n }\n\n address = this._directory.get(addressId);\n if (!address) {\n throw new DependencyError(`Address ID: ${addressId} not found`);\n }\n } catch {\n logger.warn(`[Session] Directory lookup failed for ${addressURI}, proceeding with raw URI`);\n }\n\n const callSession = this.callFactory.createCall(address, {\n ...options\n });\n\n // Use subscribeTo() so this subscription is tracked by Destroyable\n // and cleaned up when the session is destroyed. Previously used raw\n // .subscribe() which leaked on every call created.\n this.subscribeTo(\n callSession.status$.pipe(\n filter((status) => status === 'destroyed'),\n take(1)\n ),\n () => {\n const { [`${callSession.id}`]: _, ...remainingCalls } = this._calls$.value;\n this._calls$.next(remainingCalls);\n }\n );\n\n return callSession;\n } catch (error) {\n logger.error('[Session] Error creating call session:', error);\n const direction = options.initOffer ? 'inbound' : 'outbound';\n throw new CallCreateError('Call create error', error, direction);\n }\n }\n\n public destroy(): void {\n for (const call of Object.values(this._calls$.value)) {\n void call.hangup();\n }\n super.destroy();\n }\n}\n\nexport class ClientSessionWrapper implements SessionState {\n constructor(private clientSessionManager: ClientSessionManager) {}\n\n public get authenticated$(): Observable<boolean> {\n return this.clientSessionManager.authenticated$;\n }\n\n public get authenticated(): boolean {\n return this.clientSessionManager.authenticated;\n }\n\n // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\n public get signalingEvent$() {\n return this.clientSessionManager.signalingEvent$;\n }\n\n public get iceServers(): RTCIceServer[] | undefined {\n return this.clientSessionManager.iceServers;\n }\n\n public async execute<T extends JSONRPCResponse = JSONRPCResponse>(\n request: JSONRPCRequest,\n options?: PendingRPCOptions\n ): Promise<T> {\n return this.clientSessionManager.execute(request, options);\n }\n\n public get incomingCalls$(): Observable<Call[]> {\n return this.clientSessionManager.incomingCalls$;\n }\n\n public get incomingCalls(): Call[] {\n return this.clientSessionManager.incomingCalls;\n }\n\n public get calls$(): Observable<Call[]> {\n return this.clientSessionManager.calls$;\n }\n\n public get calls(): Call[] {\n return this.clientSessionManager.calls;\n }\n}\n","export const isString = (obj: unknown): obj is string => typeof obj === 'string';\n","import { map, tap, type Observable } from 'rxjs';\n\nimport { EntityCollection, Fetcher } from '../behaviors/Collection';\nimport { POST_PARAMS } from '../controllers/HTTPRequestController';\nimport { ConversationError } from '../core/errors';\nimport { isConversationMessageMetadata } from '../core/RPCMessages/guards/events.guards';\nimport { filterAs } from '../operators/filterEventAs';\nimport { isString } from '../utils/isString';\nimport { getLogger } from '../utils/logger';\n\nimport type { ClientSessionManager } from './ClientSessionManager';\nimport type { HTTPRequestController } from '../controllers/HTTPRequestController';\nimport type {\n GetConversationMessageResponse,\n GetConversationResponse\n} from '../core/types/conversation.types';\nimport type { ConversationsProvider } from '../interfaces/Conversations';\n\nconst logger = getLogger();\n\nconst toAddressId = (groupId: string): string => {\n const [, toAddressId] = groupId.split('_');\n return toAddressId;\n};\n\nclass ConversationMessagesFetcher extends Fetcher<GetConversationMessageResponse> {\n constructor(\n public readonly groupId: string,\n http: HTTPRequestController\n ) {\n super(`/api/fabric/conversations/${groupId}/messages`, 'page_size=100', http);\n }\n}\n\nclass ConversationsFetcher extends Fetcher<GetConversationResponse> {\n filter = (item: unknown): item is GetConversationResponse =>\n !!(item as GetConversationResponse).from_fabric_address_id;\n mapper = (item: unknown) => ({\n ...(item as GetConversationResponse),\n id: (item as GetConversationResponse).group_id,\n\n address_id: toAddressId((item as GetConversationResponse).group_id)\n });\n constructor(http: HTTPRequestController) {\n super(`/api/fabric/conversations`, 'page_size=100', http);\n }\n}\n\nexport class ConversationMessageCollection extends EntityCollection<GetConversationMessageResponse> {\n constructor(\n groupId: string,\n update$: Observable<Partial<GetConversationMessageResponse>>,\n http: HTTPRequestController,\n onError?: (error: Error) => void\n ) {\n super(new ConversationMessagesFetcher(groupId, http), update$, onError);\n }\n}\n\nexport class ConversationCollection extends EntityCollection<GetConversationResponse> {\n constructor(\n update$: Observable<Partial<GetConversationResponse>>,\n http: HTTPRequestController,\n onError?: (error: Error) => void\n ) {\n super(new ConversationsFetcher(http), update$, onError);\n }\n}\n\nexport class ConversationsManager implements ConversationsProvider {\n private groupIds = new Map<string, string>();\n\n constructor(\n private clientSession: ClientSessionManager,\n private http: HTTPRequestController,\n private getSubscriberAddressId: () => string,\n private readonly onError?: (error: Error) => void\n ) {}\n private async join(addressId: string): Promise<string> {\n const subscriberFromAddressId = this.getSubscriberAddressId();\n\n try {\n const response = await this.http.request({\n ...POST_PARAMS,\n url: `/api/fabric/conversations/join`,\n body: JSON.stringify({\n from_fabric_address_id: subscriberFromAddressId,\n fabric_address_ids: [addressId, subscriberFromAddressId]\n })\n });\n\n if (response.ok && !!response.body) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const data = JSON.parse(response.body);\n /* eslint-disable @typescript-eslint/no-unsafe-member-access */\n\n if (isString(data.group_id)) {\n this.groupIds.set(addressId, data.group_id as string);\n return data.group_id as string;\n }\n /* eslint-enable @typescript-eslint/no-unsafe-member-access */\n }\n throw new ConversationError('Join Failed - Unexpected response');\n } catch (error) {\n logger.error('[ConversationsManager] Failed to join conversation:', error);\n throw error;\n }\n }\n\n public async getConversationMessageCollection(\n addressId: string\n ): Promise<ConversationMessageCollection> {\n const groupId = this.groupIds.get(addressId) ?? (await this.join(addressId));\n\n return Promise.resolve(\n new ConversationMessageCollection(\n groupId,\n this.clientSession.signalingEvent$.pipe(\n filterAs(isConversationMessageMetadata, 'params'),\n tap((event) => logger.debug('[ConversationsManager ] Conversation Event:', event)),\n // FIXME after Conversation API Fixes\n map(\n (params) =>\n ({\n ...params\n }) as Partial<GetConversationMessageResponse>\n )\n ),\n this.http,\n this.onError\n )\n );\n }\n\n public async sendText(text: string, destinationAddressId: string): Promise<void> {\n const groupId =\n this.groupIds.get(destinationAddressId) ?? (await this.join(destinationAddressId));\n const subscriberFromAddressId = this.getSubscriberAddressId();\n\n try {\n const response = await this.http.request({\n ...POST_PARAMS,\n url: '/api/fabric/messages',\n body: JSON.stringify({\n group_id: groupId,\n from_fabric_address_id: subscriberFromAddressId,\n text\n })\n });\n if (response.ok) {\n return;\n }\n throw new ConversationError('Send Text Failed - Unexpected response');\n } catch (error) {\n logger.error('[ConversationsManager] Failed to send text message:', error);\n throw error;\n }\n }\n}\n","import { filter, switchMap, timer } from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { POST_PARAMS } from '../controllers/HTTPRequestController';\nimport {\n SAT_REFRESH_SCOPE,\n DEVICE_TOKEN_ENDPOINT,\n DEVICE_REFRESH_ENDPOINT,\n DEVICE_TOKEN_DEFAULT_EXPIRE_IN,\n DEVICE_TOKEN_REFRESH_BUFFER_MS,\n DEVICE_TOKEN_REFRESH_MAX_RETRIES,\n DEVICE_TOKEN_REFRESH_RETRY_BASE_MS\n} from '../core/constants';\nimport { DeviceTokenError, DPoPInitError, TokenRefreshError } from '../core/errors';\nimport { getLogger } from '../utils/logger';\n\nimport type { ClientSessionManager } from './ClientSessionManager';\nimport type { CryptoController } from '../controllers/CryptoController';\nimport type { HTTPRequestController } from '../controllers/HTTPRequestController';\nimport type { Subscriber } from '../core/entities/Subscriber';\nimport type { SDKCredential } from '../core/types/common.types';\nimport type { DeviceTokenResponse } from '../core/types/crypto.types';\n\nconst logger = getLogger();\n\n/** Callback to update the credential in the dependency container. */\nexport type CredentialUpdater = (credential: Partial<SDKCredential>) => void;\n\n/**\n * Resolves the token expiry timestamp (epoch seconds) using a 3-tier priority chain:\n * 1. `data.expires_at` — server-provided absolute timestamp\n * 2. `data.expires_in` — server-provided relative lifetime\n * 3. Fallback to `DEVICE_TOKEN_DEFAULT_EXPIRE_IN` with a warning\n */\nexport function resolveExpiresAt(data: DeviceTokenResponse): number {\n if (data.expires_at) return data.expires_at;\n if (data.expires_in) return Math.floor(Date.now() / 1000) + data.expires_in;\n\n logger.warn('[DeviceToken] Could not determine token expiry, using default');\n return Math.floor(Date.now() / 1000) + DEVICE_TOKEN_DEFAULT_EXPIRE_IN;\n}\n\n/**\n * Resolves the token TTL in seconds from a fresh response.\n * Called at token receive time so `expires_at - now` reflects the original lifetime.\n *\n * 1. `data.expires_in` — server-provided TTL directly\n * 2. `data.expires_at - now` — derive TTL from absolute timestamp\n * 3. Fallback to `DEVICE_TOKEN_DEFAULT_EXPIRE_IN`\n */\nexport function resolveExpireIn(data: DeviceTokenResponse): number {\n if (data.expires_in) return data.expires_in;\n if (data.expires_at) return Math.max(data.expires_at - Math.floor(Date.now() / 1000), 1);\n return DEVICE_TOKEN_DEFAULT_EXPIRE_IN;\n}\n\n/**\n * Manages the Client Bound SAT lifecycle: activation, token exchange,\n * reauthentication, and automatic refresh scheduling.\n *\n * Extends {@link Destroyable} for automatic RxJS subscription and subject cleanup.\n * Uses a reactive pipeline (`BehaviorSubject` + `switchMap(timer())`) instead of\n * raw `setTimeout` for refresh scheduling.\n */\nexport class DeviceTokenManager extends Destroyable {\n private _currentToken$ = this.createBehaviorSubject<DeviceTokenResponse | null>(null);\n private _refreshInProgress = false;\n private _effectiveExpireIn = DEVICE_TOKEN_DEFAULT_EXPIRE_IN;\n private _session?: ClientSessionManager;\n private _updateCredential?: CredentialUpdater;\n\n constructor(\n private readonly dpopManager: CryptoController,\n private readonly http: HTTPRequestController,\n private readonly errorHandler: (error: Error) => void,\n private readonly getCredential: () => SDKCredential\n ) {\n super();\n\n // Reactive refresh pipeline: whenever a new token is emitted, schedule\n // the next refresh using switchMap (auto-cancels previous timer).\n this.subscribeTo(\n this._currentToken$.pipe(\n filter(Boolean),\n switchMap((tokenData) => {\n const expiresAt = resolveExpiresAt(tokenData);\n const refreshIn = Math.max(\n expiresAt * 1000 - Date.now() - DEVICE_TOKEN_REFRESH_BUFFER_MS,\n 1000\n );\n logger.debug(`[DeviceToken] Scheduling Client Bound SAT refresh in ${refreshIn}ms`);\n return timer(refreshIn);\n })\n ),\n () => {\n void this.executeRefresh();\n }\n );\n }\n /** Current token TTL in milliseconds. Used to extend cached credential expiry on refresh. */\n public get effectiveExpireIn(): number {\n return this._effectiveExpireIn;\n }\n\n /**\n * Activates the Client Bound SAT flow when the subscriber's token has\n * `sat:refresh` scope.\n *\n * Steps:\n * 1. Check subscriber's `sat_claims` for `sat:refresh` scope\n * 2. Call `/api/fabric/subscriber/devices/token` with a DPoP proof\n * 3. Reauthenticate the session with the Client Bound SAT + DPoP proof\n * 4. Emit token to trigger the reactive refresh pipeline\n */\n public async activate(\n subscriber: Subscriber,\n session: ClientSessionManager,\n updateCredential: CredentialUpdater\n ): Promise<void> {\n const { satClaims } = subscriber;\n if (!satClaims?.scope?.includes(SAT_REFRESH_SCOPE)) {\n logger.debug('[DeviceToken] No sat:refresh scope, skipping Client Bound SAT activation');\n return;\n }\n\n this._session = session;\n this._updateCredential = updateCredential;\n\n try {\n const tokenData = await this.obtainToken();\n\n // Enrich token data with SAT claims expiry when the server response\n // lacks expiry fields (common with JWE tokens that can't be decoded).\n if (!tokenData.expires_at && !tokenData.expires_in && satClaims.expires_at) {\n tokenData.expires_at = satClaims.expires_at;\n }\n\n // Store the effective TTL so refresh requests use the same lifetime\n this._effectiveExpireIn = resolveExpireIn(tokenData);\n\n // Reauthenticate with the Client Bound SAT and mark session as client-bound\n const rpcProof = await this.dpopManager.createRpcProof({\n method: 'signalwire.reauthenticate'\n });\n await session.reauthenticate(tokenData.token, rpcProof, { clientBound: true });\n\n // Update the stored credential, merging with existing fields\n updateCredential({ token: tokenData.token });\n\n logger.info('[DeviceToken] Client Bound SAT activated successfully');\n\n // Emit token to trigger reactive refresh pipeline\n this._currentToken$.next(tokenData);\n } catch (error) {\n logger.error('[DeviceToken] Failed to activate Client Bound SAT:', error);\n this.errorHandler(new DPoPInitError(error, 'Failed to activate Client Bound SAT'));\n\n // Seed the refresh pipeline so it retries on the credential's expiry schedule.\n // Without this, a transient obtainToken failure would leave no refresh mechanism.\n const credential = this.getCredential();\n const expiresAt =\n satClaims.expires_at ??\n (credential.expiry_at\n ? credential.expiry_at / 1000\n : Date.now() / 1000 + DEVICE_TOKEN_DEFAULT_EXPIRE_IN / 1000);\n\n this._currentToken$.next({\n token: credential.token ?? '',\n expires_at: expiresAt\n });\n }\n }\n\n /**\n * Obtains a Client Bound SAT from `/api/fabric/subscriber/devices/token`.\n * Returns the full {@link DeviceTokenResponse} including expiry metadata.\n */\n public async obtainToken(): Promise<DeviceTokenResponse> {\n const dpopProof = await this.dpopManager.createHttpProof({\n method: 'POST',\n uri: DEVICE_TOKEN_ENDPOINT\n });\n\n const response = await this.http.request({\n url: DEVICE_TOKEN_ENDPOINT,\n ...POST_PARAMS,\n body: JSON.stringify({\n dpop_token: dpopProof,\n expire_in: DEVICE_TOKEN_DEFAULT_EXPIRE_IN\n })\n });\n\n if (!response.ok || !response.body) {\n throw new DeviceTokenError(\n `Failed to obtain device token: ${response.status} ${response.statusText}`\n );\n }\n\n const data = JSON.parse(response.body) as DeviceTokenResponse;\n if (!data.token) {\n throw new DeviceTokenError('Device token response missing token field');\n }\n\n return data;\n }\n\n /**\n * Refreshes the Client Bound SAT via `/api/fabric/subscriber/devices/refresh`.\n *\n * Creates a fresh DPoP proof, calls the refresh endpoint, reauthenticates\n * the WebSocket session, and returns the new token data (scheduling is\n * handled by the reactive pipeline).\n */\n public async refreshToken(\n session: ClientSessionManager,\n currentToken: string,\n updateCredential: CredentialUpdater\n ): Promise<DeviceTokenResponse> {\n logger.debug('[DeviceToken] Refreshing Client Bound SAT');\n\n const dpopProof = await this.dpopManager.createHttpProof({\n method: 'POST',\n uri: DEVICE_REFRESH_ENDPOINT,\n accessToken: currentToken\n });\n\n const response = await this.http.request({\n url: DEVICE_REFRESH_ENDPOINT,\n ...POST_PARAMS,\n body: JSON.stringify({\n dpop_token: dpopProof,\n expire_in: this._effectiveExpireIn\n })\n });\n\n if (!response.ok || !response.body) {\n throw new TokenRefreshError(\n `Failed to refresh device token: ${response.status} ${response.statusText}`\n );\n }\n\n const data = JSON.parse(response.body) as DeviceTokenResponse;\n if (!data.token) {\n throw new TokenRefreshError('Device token refresh response missing token field');\n }\n\n // When the refresh response lacks expiry fields (common with JWE tokens),\n // enrich it with the effective TTL from the previous token cycle.\n if (!data.expires_at && !data.expires_in) {\n data.expires_in = this._effectiveExpireIn;\n }\n\n // Update effective TTL from the refreshed token's actual expiry\n this._effectiveExpireIn = resolveExpireIn(data);\n\n // Reauthenticate with the refreshed Client Bound SAT\n const rpcProof = await this.dpopManager.createRpcProof({\n method: 'signalwire.reauthenticate'\n });\n await session.reauthenticate(data.token, rpcProof);\n\n // Update the stored credential with the refreshed token (merge via callback)\n updateCredential({ token: data.token });\n\n logger.info('[DeviceToken] Client Bound SAT refreshed successfully');\n\n return data;\n }\n\n /**\n * Executes a refresh with retry and exponential backoff.\n * On success, emits to `_currentToken$` to schedule the next refresh.\n * On all retries exhausted, emits to `errorHandler`.\n */\n private async executeRefresh(): Promise<void> {\n if (this._refreshInProgress) {\n logger.debug('[DeviceToken] Refresh already in progress, skipping');\n return;\n }\n\n const session = this._session;\n const updateCredential = this._updateCredential;\n if (!session || !updateCredential) {\n logger.warn('[DeviceToken] Cannot refresh: session or updateCredential not set');\n return;\n }\n\n // Skip refresh if the session is not authenticated (e.g., during disconnect).\n // The refresh will be rescheduled when a new token is emitted after reconnect.\n if (!session.authenticated) {\n logger.debug('[DeviceToken] Session not authenticated, deferring refresh');\n return;\n }\n\n this._refreshInProgress = true;\n\n try {\n const currentToken = this.getCredential().token;\n if (!currentToken) {\n throw new TokenRefreshError('No current token available for refresh');\n }\n\n const newTokenData = await this.retryRefresh(session, currentToken, updateCredential);\n this._currentToken$.next(newTokenData);\n } catch (error) {\n logger.error('[DeviceToken] Automatic Client Bound SAT refresh failed:', error);\n this.errorHandler(\n error instanceof TokenRefreshError\n ? error\n : new TokenRefreshError('Automatic token refresh failed', error)\n );\n } finally {\n this._refreshInProgress = false;\n }\n }\n\n /**\n * Retries `refreshToken()` up to `DEVICE_TOKEN_REFRESH_MAX_RETRIES` times\n * with exponential backoff (1s, 2s, 4s).\n */\n private async retryRefresh(\n session: ClientSessionManager,\n currentToken: string,\n updateCredential: CredentialUpdater\n ): Promise<DeviceTokenResponse> {\n let lastError: unknown;\n\n for (let attempt = 0; attempt < DEVICE_TOKEN_REFRESH_MAX_RETRIES; attempt++) {\n try {\n return await this.refreshToken(session, currentToken, updateCredential);\n } catch (error) {\n lastError = error;\n if (attempt < DEVICE_TOKEN_REFRESH_MAX_RETRIES - 1) {\n const delay = DEVICE_TOKEN_REFRESH_RETRY_BASE_MS * Math.pow(2, attempt);\n logger.warn(\n `[DeviceToken] Refresh attempt ${attempt + 1} failed, retrying in ${delay}ms`\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n\n throw lastError instanceof Error\n ? lastError\n : new TokenRefreshError('All refresh retries exhausted', lastError);\n }\n\n /** Cleans up the manager, cancelling the reactive pipeline and all subscriptions. */\n public override destroy(): void {\n super.destroy();\n }\n}\n","import { Destroyable } from '../behaviors/Destroyable';\nimport { getLogger } from '../utils/logger';\n\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\n/** Default maximum number of diagnostic events to retain in the ring buffer. */\nconst DEFAULT_MAX_EVENTS = 1000;\n\n/**\n * Categories for diagnostic events, covering the main areas of SDK operation.\n */\nexport type DiagnosticCategory = 'connection' | 'call' | 'device' | 'recovery' | 'error';\n\n/**\n * A single diagnostic event recorded during a session.\n */\nexport interface DiagnosticEvent {\n readonly timestamp: number;\n readonly category: DiagnosticCategory;\n readonly event: string;\n readonly details?: Record<string, unknown>;\n}\n\n/**\n * Summary of a single call for diagnostic export.\n */\nexport interface CallDiagnosticSummary {\n readonly callId: string;\n readonly direction: 'inbound' | 'outbound';\n readonly destination?: string;\n readonly startTime: number;\n readonly endTime?: number;\n readonly duration: number;\n readonly finalStatus: string;\n readonly avgQualityScore?: number;\n readonly minQualityScore?: number;\n readonly recoveryAttempts: number;\n readonly iceCandidateTypes: string[];\n}\n\n/**\n * Structured diagnostic bundle exported for support/debugging.\n */\nexport interface SessionDiagnostics {\n readonly sdkVersion: string;\n readonly userAgent: string;\n readonly exportedAt: number;\n readonly events: readonly DiagnosticEvent[];\n readonly calls: readonly CallDiagnosticSummary[];\n readonly deviceChanges: readonly DiagnosticEvent[];\n}\n\n/**\n * Options for constructing a DiagnosticsCollector.\n */\nexport interface DiagnosticsCollectorOptions {\n /** SDK version string to include in exports. */\n readonly sdkVersion: string;\n /** Maximum number of events to retain in the ring buffer. Defaults to 1000. */\n readonly maxEvents?: number;\n /** Optional callback to retrieve the current device list. */\n readonly getDevices?: () => MediaDeviceInfo[];\n}\n\n/**\n * Returns the user agent string, safely handling non-browser environments.\n */\nfunction getUserAgent(): string {\n try {\n if (typeof navigator !== 'undefined' && navigator.userAgent) {\n return navigator.userAgent;\n }\n } catch {\n // Non-browser environment\n }\n return 'unknown';\n}\n\n/**\n * DiagnosticsCollector maintains a ring buffer of diagnostic events\n * for structured export. It records connection, call, device, recovery,\n * and error events during a session, and can serialize them into a\n * plain object for support tickets or debugging.\n *\n * Extends Destroyable for lifecycle management.\n */\nexport class DiagnosticsCollector extends Destroyable {\n private _events: DiagnosticEvent[] = [];\n private _calls: CallDiagnosticSummary[] = [];\n private _deviceChanges: DiagnosticEvent[] = [];\n private readonly _sdkVersion: string;\n private readonly _maxEvents: number;\n\n private readonly _eventRecorded$ = this.createSubject<DiagnosticEvent>();\n\n constructor(options: DiagnosticsCollectorOptions) {\n super();\n this._sdkVersion = options.sdkVersion;\n this._maxEvents = options.maxEvents ?? DEFAULT_MAX_EVENTS;\n\n logger.debug('DiagnosticsCollector initialized', {\n sdkVersion: this._sdkVersion,\n maxEvents: this._maxEvents\n });\n }\n\n /**\n * Observable that emits each time a diagnostic event is recorded.\n */\n public get eventRecorded$(): Observable<DiagnosticEvent> {\n return this._eventRecorded$.asObservable();\n }\n\n /**\n * Record a diagnostic event into the ring buffer.\n *\n * @param category - The event category (connection, call, device, recovery, error).\n * @param event - A short description of the event.\n * @param details - Optional additional details as a key-value map.\n */\n public record(\n category: DiagnosticCategory,\n event: string,\n details?: Record<string, unknown>\n ): void {\n const entry: DiagnosticEvent = {\n timestamp: Date.now(),\n category,\n event,\n ...(details !== undefined ? { details } : {})\n };\n\n this._events = this._appendToBuffer(this._events, entry);\n this._eventRecorded$.next(entry);\n }\n\n /**\n * Shorthand to record a device change event.\n *\n * @param event - Description of the device change.\n * @param details - Optional additional details.\n */\n public recordDeviceChange(event: string, details?: Record<string, unknown>): void {\n const entry: DiagnosticEvent = {\n timestamp: Date.now(),\n category: 'device',\n event,\n ...(details !== undefined ? { details } : {})\n };\n\n this._deviceChanges = this._appendToBuffer(this._deviceChanges, entry);\n this._events = this._appendToBuffer(this._events, entry);\n this._eventRecorded$.next(entry);\n }\n\n /**\n * Record a call summary for diagnostic export.\n *\n * @param summary - The call diagnostic summary to add.\n */\n public recordCallSummary(summary: CallDiagnosticSummary): void {\n this._calls = this._appendCallToBuffer(this._calls, summary);\n\n this.record('call', 'call_summary', {\n callId: summary.callId,\n direction: summary.direction,\n duration: summary.duration,\n finalStatus: summary.finalStatus\n });\n }\n\n /**\n * Export the current diagnostic buffer as a structured plain object.\n * This can be JSON.stringify'd and attached to support tickets.\n */\n public export(): SessionDiagnostics {\n return {\n sdkVersion: this._sdkVersion,\n userAgent: getUserAgent(),\n exportedAt: Date.now(),\n events: [...this._events],\n calls: [...this._calls],\n deviceChanges: [...this._deviceChanges]\n };\n }\n\n /**\n * Clear all diagnostic buffers, resetting to empty state.\n */\n public clear(): void {\n this._events = [];\n this._calls = [];\n this._deviceChanges = [];\n logger.debug('DiagnosticsCollector buffers cleared');\n }\n\n public override destroy(): void {\n logger.debug('DiagnosticsCollector destroyed');\n super.destroy();\n }\n /**\n * Append an entry to a buffer array, enforcing the max size via ring buffer semantics.\n * Returns a new array (immutable pattern).\n */\n private _appendToBuffer(buffer: DiagnosticEvent[], entry: DiagnosticEvent): DiagnosticEvent[] {\n const updated = [...buffer, entry];\n if (updated.length > this._maxEvents) {\n return updated.slice(updated.length - this._maxEvents);\n }\n return updated;\n }\n\n /**\n * Append a call summary to the calls buffer, enforcing max size.\n * Returns a new array (immutable pattern).\n */\n private _appendCallToBuffer(\n buffer: CallDiagnosticSummary[],\n entry: CallDiagnosticSummary\n ): CallDiagnosticSummary[] {\n const updated = [...buffer, entry];\n if (updated.length > this._maxEvents) {\n return updated.slice(updated.length - this._maxEvents);\n }\n return updated;\n }\n}\n","export const isEmptyArray = (a?: unknown[]): boolean => {\n return (a?.length ?? 0) === 0;\n};\n","import { take } from 'rxjs';\n\nimport type { Observable } from 'rxjs';\n\nexport const warnup = (observable: Observable<unknown>): void => {\n observable.pipe(take(1)).subscribe();\n};\n","import { firstValueFrom, map, type Observable } from 'rxjs';\n\nimport { EntityCollection, Fetcher } from '../behaviors/Collection';\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { GET_PARAMS } from '../controllers/HTTPRequestController';\nimport { Address } from '../core/entities/Address';\nimport { isConversationMessageUpdatedMetadata } from '../core/RPCMessages/guards/events.guards';\nimport { filterAs, filterNull } from '../operators';\nimport { isEmptyArray } from '../utils/arrays';\nimport { getLogger } from '../utils/logger';\nimport { warnup } from '../utils/warnup';\n\nimport type { ClientSessionManager } from './ClientSessionManager';\nimport type { PaginatedResponse } from '../behaviors/types/collection.types';\nimport type { HTTPRequestController } from '../controllers/HTTPRequestController';\nimport type { Directory } from '../core/entities/Directory';\nimport type { GetAddressResponse } from '../core/types/address.types';\nimport type { ConversationsProvider } from '../interfaces/Conversations';\n\nconst logger = getLogger();\n\nclass AddressFetcher extends Fetcher<GetAddressResponse> {\n constructor(http: HTTPRequestController) {\n super('/api/fabric/addresses', 'sort_by=name&sort_order=asc', http);\n }\n\n async name(name: unknown): Promise<GetAddressResponse | undefined> {\n const response = await this.http.request({\n ...GET_PARAMS,\n url: `${this.endpoint}?name=${encodeURIComponent(name as string)}`\n });\n if (response.ok && !!response.body) {\n const result = JSON.parse(response.body) as PaginatedResponse<GetAddressResponse>;\n if (!isEmptyArray(result.data)) {\n return result.data[0];\n }\n }\n logger.error('Failed to fetch addresses');\n }\n}\n\n/** Collection of address states with reactive updates and pagination. */\nexport class AddressStateCollection extends EntityCollection<GetAddressResponse> {\n constructor(\n update$: Observable<Partial<GetAddressResponse>>,\n http: HTTPRequestController,\n onError?: (error: Error) => void\n ) {\n super(new AddressFetcher(http), update$, onError);\n }\n}\n\n/**\n * Manages the directory of {@link Address} entries with paginated loading.\n *\n * Implements the {@link Directory} interface and provides reactive access to\n * addresses via observables, along with pagination and lookup methods.\n */\nexport class DirectoryManager extends Destroyable implements Directory {\n private addNewAddress = (id: string): void => {\n const address = new Address(id, this.conversationManager, this);\n const observable = this._statesCollection.get$(id)?.pipe(\n filterNull(),\n map((data) => {\n address.upnext(data);\n return address;\n })\n );\n if (observable) {\n warnup(observable);\n this._observableRegistry.set(id, observable);\n }\n this._addressesInstances.set(id, address);\n };\n private _addresses$ = this.createBehaviorSubject<Address[]>([]);\n private _statesCollection: AddressStateCollection;\n private _addressesInstances = new Map<string, Address>();\n private _observableRegistry = new Map<string, Observable<Address>>();\n\n constructor(\n private http: HTTPRequestController,\n clientSession: ClientSessionManager,\n private conversationManager: ConversationsProvider,\n private readonly onError?: (error: Error) => void\n ) {\n super();\n this._statesCollection = new AddressStateCollection(\n clientSession.signalingEvent$.pipe(\n filterAs(isConversationMessageUpdatedMetadata, 'params'),\n // FIXME after Conversation API Fixes\n map((_) => ({}) as Partial<GetAddressResponse>)\n ),\n this.http,\n this.onError\n );\n this.initSubscriptions();\n }\n\n /** Whether addresses are currently being loaded from the server. */\n public get loading(): boolean {\n return this._statesCollection.loading;\n }\n\n private initSubscriptions(): void {\n this.subscribeTo(this._statesCollection.updated$, () => {\n const existing = Array.from(this._addressesInstances.values().map((address) => address.id));\n const newStates = this._statesCollection.values.filter(\n (state) => !existing.includes(state.id)\n );\n if (!isEmptyArray(newStates)) {\n newStates.forEach((state) => this.addNewAddress(state.id));\n this._addresses$.next(Array.from(this._addressesInstances.values()));\n }\n });\n }\n\n /** Observable stream of all addresses in the directory. */\n public get addresses$(): Observable<Address[]> {\n return this._addresses$.asObservable();\n }\n\n /** Current snapshot of all loaded addresses. */\n public get addresses(): Address[] {\n return this._addresses$.value;\n }\n\n /** Observable indicating whether more addresses can be loaded. */\n public get hasMore$(): Observable<boolean> {\n return this._statesCollection.hasMore$;\n }\n\n /** Observable of the current loading state. */\n public get loading$(): Observable<boolean> {\n return this._statesCollection.loading$;\n }\n\n /**\n * Loads the next page of addresses from the server.\n *\n * No-op if {@link hasMore$} is `false`. Loading state is observable via {@link loading$}.\n * New addresses are appended to {@link addresses$} when the page loads.\n */\n public loadMore(): void {\n if (this._statesCollection.hasMore) {\n this._statesCollection.loadMore();\n }\n }\n\n /**\n * Returns a reactive observable for a specific address by ID.\n * @param id - The address ID to observe.\n * @returns An observable of the address, or `undefined` if not found.\n */\n public get$(id: string): Observable<Address> | undefined {\n if (!this._observableRegistry.has(id)) {\n this.addNewAddress(id);\n }\n return this._observableRegistry.get(id);\n }\n\n /**\n * Returns an address by ID from the local cache.\n * @param addressId - The address ID to look up.\n * @returns The address, or `undefined` if not found.\n */\n public get(addressId: string): Address | undefined {\n return this._addressesInstances.get(addressId);\n }\n\n /**\n * Finds an address ID by its resource name (URI).\n *\n * Searches locally loaded addresses first, then queries the server if not found.\n * The resource name is the {@link Address.name} identifier, distinct from display name.\n *\n * @param name - The resource name to search for (exact match, e.g. `'/public/conference'`).\n * @returns The address ID, or `undefined` if no match is found locally or on server.\n */\n public async findAddressIdByURI(name: string): Promise<string | undefined> {\n let addressId = this._addressesInstances.values().find((addr) => addr.name === name)?.id;\n if (!addressId) {\n const found$ = await this._statesCollection.find$('name', name);\n if (found$) {\n const state = await firstValueFrom(found$);\n this.addNewAddress(state.id);\n addressId = state.id;\n }\n }\n return addressId;\n }\n}\n","import { Destroyable } from '../behaviors/Destroyable';\nimport { UnexpectedError, WebSocketConnectionError, WebSocketTimeoutError } from '../core/errors';\nimport { getLogger } from '../utils/logger';\n\nimport type {\n NodeSocketAdapter,\n NodeSocketClient,\n WebSocketAdapter,\n WebSocketClient\n} from '../core/types/common.types';\nimport type { Observable } from 'rxjs';\n\nconst logger = getLogger();\n\nexport type WebSocketConnectionStatus =\n | 'disconnected'\n | 'disconnecting'\n | 'reconnecting'\n | 'connected'\n | 'connecting';\n\nexport interface WebSocketControllerOptions {\n reconnectDelayMin?: number;\n reconnectDelayMax?: number;\n connectionTimeout?: number;\n}\n\nexport class WebSocketController extends Destroyable {\n // Default configuration values\n\n private static readonly DEFAULT_RECONNECT_DELAY_MIN_MS = 1_000;\n\n private static readonly DEFAULT_RECONNECT_DELAY_MAX_MS = 30_000;\n\n private static readonly DEFAULT_CONNECTION_TIMEOUT_MS = 10_000;\n\n // Private state\n private socket?: WebSocketClient | NodeSocketClient;\n private messageQueue: (string | ArrayBuffer | Blob)[] = [];\n private reconnectTimer?: ReturnType<typeof setTimeout>;\n private connectionTimeoutTimer?: ReturnType<typeof setTimeout>;\n private currentReconnectDelay: number;\n private shouldReconnect = false;\n // Configuration\n private readonly reconnectDelayMin: number;\n private readonly reconnectDelayMax: number;\n private readonly connectionTimeout: number;\n // Bound event handlers for proper addEventListener/removeEventListener pairing\n private readonly boundHandleOpen = (): void => this.handleOpen();\n private readonly boundHandleClose = (event: CloseEvent): void => this.handleClose(event);\n private readonly boundHandleError = (): void => this.handleError();\n private readonly boundHandleMessage = (event: MessageEvent): void => this.handleMessage(event);\n\n // Observable streams\n private _status$ = this.createBehaviorSubject<WebSocketConnectionStatus>('disconnected');\n\n private _incomingMessages$ = this.createSubject<MessageEvent>();\n\n private _errors$ = this.createReplaySubject<Error>(1);\n\n constructor(\n private WebSocketConstructor: WebSocketAdapter | NodeSocketAdapter,\n private endpoint: string,\n private outgoingMessages$: Observable<string | ArrayBuffer | Blob>,\n options: WebSocketControllerOptions = {}\n ) {\n super();\n this.reconnectDelayMin =\n options.reconnectDelayMin ?? WebSocketController.DEFAULT_RECONNECT_DELAY_MIN_MS;\n this.reconnectDelayMax =\n options.reconnectDelayMax ?? WebSocketController.DEFAULT_RECONNECT_DELAY_MAX_MS;\n this.connectionTimeout =\n options.connectionTimeout ?? WebSocketController.DEFAULT_CONNECTION_TIMEOUT_MS;\n this.currentReconnectDelay = this.reconnectDelayMin;\n\n // Subscribe to send$ to handle message sending\n this.subscriptions.push(\n this.outgoingMessages$.subscribe((data) => {\n this.send(data);\n })\n );\n }\n\n public get status$(): Observable<WebSocketConnectionStatus> {\n return this._status$.asObservable();\n }\n public get incomingMessages$(): Observable<MessageEvent> {\n return this._incomingMessages$.asObservable();\n }\n public get errors$(): Observable<Error> {\n return this._errors$.asObservable();\n }\n public connect(): void {\n if (this._status$.value === 'connecting' || this._status$.value === 'connected') {\n return;\n }\n\n this.shouldReconnect = true;\n this._status$.next('connecting');\n this.createWebSocket();\n }\n\n public disconnect(): void {\n this.shouldReconnect = false;\n this.clearReconnectTimer();\n this.clearConnectionTimeout();\n\n const currentStatus = this._status$.value;\n\n if (\n currentStatus === 'connected' ||\n currentStatus === 'connecting' ||\n currentStatus === 'reconnecting'\n ) {\n if (this.socket) {\n this._status$.next('disconnecting');\n this.socket.close();\n } else {\n this._status$.next('disconnected');\n }\n } else {\n this._status$.next('disconnected');\n }\n }\n\n reconnect(): void {\n if (this.shouldReconnect) {\n this._status$.next('reconnecting');\n this.scheduleReconnection();\n } else {\n this._status$.next('disconnected');\n }\n }\n\n public send(data: string | ArrayBuffer | Blob): void {\n if (\n this._status$.value === 'connected' &&\n this.socket?.readyState === 1 // WebSocket.OPEN\n ) {\n logger.wsTraffic({ type: 'send', raw: data as string });\n this.socket.send(data);\n } else {\n this.messageQueue.push(data);\n }\n }\n\n private createWebSocket(): void {\n try {\n this.closeExistingSocket();\n this.socket = new this.WebSocketConstructor(this.endpoint);\n this.setupWebSocketListeners();\n this.startConnectionTimeout();\n } catch (error) {\n const err =\n error instanceof Error ? error : new UnexpectedError('Failed to create WebSocket');\n this._errors$.next(err);\n this.handleConnectionError();\n }\n }\n\n /**\n * Closes the existing socket and removes its event listeners to prevent\n * phantom 'open'/'close' events from firing on the orphaned socket.\n */\n private closeExistingSocket(): void {\n if (!this.socket) return;\n\n const oldSocket = this.socket;\n this.socket = undefined;\n\n this.removeWebSocketListeners(oldSocket);\n\n try {\n oldSocket.close();\n } catch {\n // Ignore errors closing already-closed sockets\n }\n }\n\n private setupWebSocketListeners(): void {\n if (!this.socket) return;\n\n this.socket.addEventListener('open', this.boundHandleOpen);\n // @ts-expect-error -- CloseEvent type mismatch between browser/node adapters\n this.socket.addEventListener('close', this.boundHandleClose);\n this.socket.addEventListener('error', this.boundHandleError);\n // @ts-expect-error -- MessageEvent type mismatch between browser/node adapters\n this.socket.addEventListener('message', this.boundHandleMessage);\n }\n\n private removeWebSocketListeners(socket: WebSocketClient | NodeSocketClient): void {\n try {\n socket.removeEventListener('open', this.boundHandleOpen);\n // @ts-expect-error -- WebSocket listener type mismatch between browser/node adapters\n socket.removeEventListener('close', this.boundHandleClose);\n socket.removeEventListener('error', this.boundHandleError);\n // @ts-expect-error -- WebSocket listener type mismatch between browser/node adapters\n socket.removeEventListener('message', this.boundHandleMessage);\n } catch {\n // Some environments may not support removeEventListener on WebSocket\n }\n }\n\n private handleOpen(): void {\n this.clearConnectionTimeout();\n this._status$.next('connected');\n this.currentReconnectDelay = this.reconnectDelayMin;\n this.flushMessageQueue();\n }\n\n private handleClose(_event: CloseEvent): void {\n this.clearConnectionTimeout();\n\n if (this.shouldReconnect) {\n this._status$.next('reconnecting');\n this.scheduleReconnection();\n } else {\n this._status$.next('disconnected');\n }\n }\n\n private handleError(): void {\n const error = new WebSocketConnectionError('WebSocket connection error');\n this._errors$.next(error);\n this.handleConnectionError();\n }\n\n private handleMessage(event: MessageEvent): void {\n logger.wsTraffic({ type: 'recv', raw: event.data as string });\n this._incomingMessages$.next(event);\n }\n\n private handleConnectionError(): void {\n this.reconnect();\n }\n\n private scheduleReconnection(): void {\n this.clearReconnectTimer();\n\n // Equal jitter: use half the ceiling as a guaranteed base, plus a random\n // portion of the other half. This prevents thundering herd when many clients\n // reconnect simultaneously (e.g., after a server restart) while still\n // respecting the exponential backoff envelope.\n // Range: [currentDelay * 0.5, currentDelay * 1.0]\n // Reference: https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/\n const jitteredDelay = this.currentReconnectDelay * (0.5 + Math.random() * 0.5);\n\n this.reconnectTimer = setTimeout(() => {\n if (this.shouldReconnect) {\n this._status$.next('connecting');\n this.createWebSocket();\n this.increaseReconnectDelay();\n }\n }, jitteredDelay);\n }\n\n private increaseReconnectDelay(): void {\n this.currentReconnectDelay = Math.min(this.currentReconnectDelay * 2, this.reconnectDelayMax);\n }\n\n private startConnectionTimeout(): void {\n this.clearConnectionTimeout();\n\n this.connectionTimeoutTimer = setTimeout(() => {\n if (this._status$.value === 'connecting') {\n const error = new WebSocketTimeoutError('WebSocket connection timeout');\n this._errors$.next(error);\n\n if (this.socket) {\n this.socket.close();\n }\n }\n }, this.connectionTimeout);\n }\n\n private clearConnectionTimeout(): void {\n if (this.connectionTimeoutTimer) {\n clearTimeout(this.connectionTimeoutTimer);\n this.connectionTimeoutTimer = undefined;\n }\n }\n\n private clearReconnectTimer(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = undefined;\n }\n }\n\n private flushMessageQueue(): void {\n while (this.messageQueue.length > 0 && this.socket?.readyState === 1) {\n const message = this.messageQueue.shift();\n if (message !== undefined) {\n this.socket.send(message);\n }\n }\n }\n}\n","// =============================================================================\n// CLIENT/SERVER METHOD TYPE GUARDS\n// =============================================================================\n// This file contains type guards for client requests, server responses,\n// and method params/results.\n\nimport { hasProperty, isJSONRPCRequest, isJSONRPCResponse, isObject } from './base.guards';\n\nimport type { JSONRPCRequest, TypeGuard } from '../types/base';\nimport type { SignalwirePingParams, SignalwirePingRequest } from '../types/events';\nimport type {\n CallLayoutListRequest,\n CallLayoutListResponse,\n CallMuteParams,\n CallMuteRequest,\n CallMuteResponse,\n CallTargetParams,\n EmptyResponse,\n SignalwireConnectParams,\n SignalwireConnectRequest,\n SignalwireConnectResponse,\n SignalwirePingResponse,\n WebrtcVertoParams,\n WebrtcVertoRequest,\n WebrtcVertoResponse\n} from '../types/methods';\n\n// =============================================================================\n// CLIENT REQUEST TYPE GUARDS\n// =============================================================================\n\nexport function isSignalwireConnectRequest(value: unknown): value is SignalwireConnectRequest {\n return isJSONRPCRequest(value) && value.method === 'signalwire.connect';\n}\n\nexport function isSignalwirePingRequest(value: unknown): value is SignalwirePingRequest {\n return isJSONRPCRequest(value) && value.method === 'signalwire.ping';\n}\n\nexport function isWebrtcVertoRequest(value: unknown): value is WebrtcVertoRequest {\n return isJSONRPCRequest(value) && value.method === 'webrtc.verto';\n}\n\nexport function isCallLayoutListRequest(value: unknown): value is CallLayoutListRequest {\n return isJSONRPCRequest(value) && value.method === 'call.layout.list';\n}\n\nexport function isCallMuteRequest(value: unknown): value is CallMuteRequest {\n return isJSONRPCRequest(value) && value.method === 'call.mute';\n}\n\n// =============================================================================\n// RESPONSE TYPE GUARDS\n// =============================================================================\n\nexport function isSignalwireConnectResponse(value: unknown): value is SignalwireConnectResponse {\n return (\n isJSONRPCResponse(value) &&\n isObject(value.result) &&\n hasProperty(value.result, 'identity') &&\n hasProperty(value.result, 'authorization') &&\n hasProperty(value.result, 'protocol')\n );\n}\n\nexport function isSignalwirePingResponse(value: unknown): value is SignalwirePingResponse {\n return (\n isJSONRPCResponse(value) && isObject(value.result) && hasProperty(value.result, 'timestamp')\n );\n}\n\nexport function isWebrtcVertoResponse(value: unknown): value is WebrtcVertoResponse {\n return (\n isJSONRPCResponse(value) &&\n isObject(value.result) &&\n hasProperty(value.result, 'node_id') &&\n hasProperty(value.result, 'code')\n );\n}\n\nexport function isCallLayoutListResponse(value: unknown): value is CallLayoutListResponse {\n return (\n isJSONRPCResponse(value) &&\n isObject(value.result) &&\n hasProperty(value.result, 'layouts') &&\n Array.isArray(value.result.layouts)\n );\n}\n\nexport function isCallMuteResponse(value: unknown): value is CallMuteResponse {\n return (\n isJSONRPCResponse(value) &&\n isObject(value.result) &&\n hasProperty(value.result, 'code') &&\n hasProperty(value.result, 'message') &&\n !hasProperty(value.result, 'layouts') &&\n !hasProperty(value.result, 'node_id')\n );\n}\n\nexport function isEmptyResponse(value: unknown): value is EmptyResponse {\n return (\n isJSONRPCResponse(value) && isObject(value.result) && Object.keys(value.result).length === 0\n );\n}\n\n// =============================================================================\n// PARAMS TYPE GUARDS\n// =============================================================================\n\nexport function isSignalwireConnectParams(value: unknown): value is SignalwireConnectParams {\n return (\n isObject(value) &&\n hasProperty(value, 'version') &&\n hasProperty(value, 'event_acks') &&\n hasProperty(value, 'agent') &&\n hasProperty(value, 'authentication')\n );\n}\n\nexport function isSignalwirePingParams(value: unknown): value is SignalwirePingParams {\n return isObject(value) && hasProperty(value, 'timestamp') && typeof value.timestamp === 'number';\n}\n\nexport function isWebrtcVertoParams(value: unknown): value is WebrtcVertoParams {\n return (\n isObject(value) &&\n hasProperty(value, 'message') &&\n hasProperty(value, 'callID') &&\n hasProperty(value, 'node_id')\n );\n}\n\nexport function isCallTargetParams(value: unknown): value is CallTargetParams {\n return isObject(value) && hasProperty(value, 'self') && hasProperty(value, 'target');\n}\n\nexport function isCallMuteParams(value: unknown): value is CallMuteParams {\n if (!isCallTargetParams(value)) return false;\n return 'channels' in value;\n}\n\n// =============================================================================\n// METHOD TYPE MAPPING\n// =============================================================================\n\nexport const MethodTypeMap = {\n 'signalwire.connect': isSignalwireConnectRequest,\n 'signalwire.ping': isSignalwirePingRequest,\n 'webrtc.verto': isWebrtcVertoRequest,\n 'call.layout.list': isCallLayoutListRequest,\n 'call.mute': isCallMuteRequest\n} as const;\n\nexport type MethodType = keyof typeof MethodTypeMap;\n\n/**\n * Gets the appropriate type guard for a method.\n */\nexport function getMethodGuard(method: string): TypeGuard<JSONRPCRequest> | undefined {\n return MethodTypeMap[method as MethodType];\n}\n","import {\n EMPTY,\n catchError,\n defer,\n filter,\n from,\n map,\n share,\n shareReplay,\n take,\n takeUntil,\n tap,\n timeout\n} from 'rxjs';\n\nimport { PreferencesContainer } from '../containers/PreferencesContainer';\nimport { WebSocketController } from '../controllers/WebSocketController';\nimport { MessageParseError, TransportConnectionError } from '../core/errors';\nimport { RPCEventAckResponse, RPCPingResponse } from '../core/RPCMessages';\nimport { isJSONRPCRequest, isJSONRPCResponse } from '../core/RPCMessages/guards/base.guards';\nimport { isSignalwireRequest } from '../core/RPCMessages/guards/events.guards';\nimport { isSignalwirePingRequest } from '../core/RPCMessages/guards/methods.guards';\nimport { Destroyable, PendingRPC } from '../core/utils';\nimport { getLogger } from '../utils/logger';\n\nimport type { StorageManager } from './StorageManager';\nimport type { JSONRPCRequest, JSONRPCResponse } from '../core/RPCMessages/types/base';\nimport type {\n JSONSerializable,\n WebSocketAdapter,\n NodeSocketAdapter\n} from '../core/types/common.types';\nimport type { PendingRPCOptions } from '../core/utils';\nimport type { Observable, OperatorFunction } from 'rxjs';\n\nconst logger = getLogger();\n\nexport class TransportManager extends Destroyable {\n private initialized$: Observable<boolean>;\n\n public protocol$ = this.createReplaySubject<string | undefined>(1);\n // Connection state tracking\n private isConnecting = false;\n private isConnected = false;\n\n // Session epoch for stale event detection (epoch seconds).\n // Set from the first timestamped event after each signalwire.connect.\n private ackEvent = <T extends JSONRPCRequest | JSONRPCResponse>(): OperatorFunction<T, T> => {\n return tap((message) => {\n if (isSignalwireRequest(message)) {\n try {\n logger.debug('[Transport] Sending event ack', {\n eventId: message.id\n });\n this.send(RPCEventAckResponse(message.id));\n } catch (error) {\n logger.error('[Transport] Failed to send event acknowledgment:', error);\n }\n }\n });\n };\n private replySignalwirePing = <T extends JSONRPCRequest | JSONRPCResponse>(): OperatorFunction<\n T,\n T\n > => {\n return filter((message) => {\n if (isSignalwirePingRequest(message)) {\n try {\n logger.debug('[Transport] Received ping, sending pong', {\n pingId: message.id\n });\n this.send(RPCPingResponse(message.id));\n } catch (error) {\n logger.error('[Transport] Failed to send ping response:', error);\n }\n return false;\n }\n return true;\n });\n };\n /**\n * Filter that drops events from a previous session after reconnect.\n *\n * Compares the event's `event_channel` against the current protocol.\n * Events whose channel doesn't contain the current protocol are from\n * a stale session and are discarded. Events without an event_channel\n * (auth state events, RPC responses) always pass through.\n */\n private discardStaleEvents = <T extends JSONRPCRequest | JSONRPCResponse>(): OperatorFunction<\n T,\n T\n > => {\n return filter((message) => {\n if (!isSignalwireRequest(message)) return true;\n\n const eventChannel: string | undefined = (message.params as Record<string, unknown>)\n .event_channel as string | undefined;\n if (!eventChannel) return true;\n\n const currentProtocol = this._currentProtocol;\n if (!currentProtocol) return true;\n\n if (!eventChannel.includes(currentProtocol)) {\n const eventType = message.params.event_type;\n logger.warn(\n `[Transport] Discarding stale event: ${eventType}` +\n ` (event_channel does not match current protocol)`\n );\n return false;\n }\n\n return true;\n });\n };\n\n private _currentProtocol: string | undefined;\n private _outgoingMessages$ = this.createSubject<string | ArrayBuffer | Blob>();\n private _webSocketConnections!: WebSocketController;\n private _jsonRPCMessage$!: Observable<JSONRPCResponse | JSONRPCRequest>;\n private _jsonRPCResponse$!: Observable<JSONRPCResponse>;\n private _incomingEvent$!: Observable<JSONRPCRequest | JSONRPCResponse>;\n\n constructor(\n private readonly storage: StorageManager,\n private readonly protocolKey: string,\n webSocketConstructor: WebSocketAdapter | NodeSocketAdapter,\n relayHost: string,\n private readonly onError?: (error: Error) => void\n ) {\n super();\n this._webSocketConnections = new WebSocketController(\n webSocketConstructor,\n relayHost,\n this._outgoingMessages$.asObservable(),\n {\n connectionTimeout: PreferencesContainer.instance.connectionTimeout,\n reconnectDelayMin: PreferencesContainer.instance.reconnectDelayMin,\n reconnectDelayMax: PreferencesContainer.instance.reconnectDelayMax\n }\n );\n this.subscribeTo(this._webSocketConnections.errors$, (error) => {\n this.onError?.(error);\n });\n this.initialized$ = defer(() => from(this._init())).pipe(\n shareReplay(1),\n takeUntil(this.destroyed$)\n );\n\n this._jsonRPCMessage$ = this._webSocketConnections.incomingMessages$.pipe(\n map((event: MessageEvent) => {\n try {\n return JSON.parse(event.data as string) as object;\n } catch (error) {\n logger.error('[Transport] Failed to parse incoming message:', error);\n this.onError?.(new MessageParseError(error));\n return null;\n }\n }),\n filter(\n (message): message is JSONRPCResponse | JSONRPCRequest =>\n message !== null && (isJSONRPCResponse(message) || isJSONRPCRequest(message))\n ),\n catchError((error) => {\n logger.error('[Transport] Message processing error:', error);\n this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));\n return EMPTY;\n }),\n share(),\n takeUntil(this.destroyed$)\n );\n\n this._jsonRPCResponse$ = this._jsonRPCMessage$.pipe(filter(isJSONRPCResponse));\n\n this._incomingEvent$ = this._jsonRPCMessage$.pipe(\n this.ackEvent(),\n this.replySignalwirePing(),\n filter((message) => !isJSONRPCResponse(message)),\n this.discardStaleEvents(),\n share(),\n takeUntil(this.destroyed$)\n );\n }\n\n public async setProtocol(protocol: string | undefined): Promise<void> {\n this._currentProtocol = protocol;\n this.protocol$.next(protocol);\n await this._updateProtocolInStorage(protocol);\n }\n public get incomingEvent$(): Observable<JSONRPCRequest | JSONRPCResponse> {\n return this._incomingEvent$;\n }\n\n public get connectionStatus$(): Observable<string> {\n return this._webSocketConnections.status$;\n }\n\n public async connect(): Promise<void> {\n // Prevent duplicate connections\n if (this.isConnecting || this.isConnected) {\n logger.warn('[Transport] Already connecting or connected');\n return Promise.resolve();\n }\n\n return new Promise<void>((resolve, reject) => {\n this.isConnecting = true;\n\n this.subscribeTo(this.initialized$, () => {\n this._webSocketConnections.connect();\n\n // Wait for actual connection\n const connectionSub = this._webSocketConnections.status$\n .pipe(\n filter((status) => status === 'connected' || status === 'disconnected'),\n take(1),\n timeout(10000) // 10 second timeout\n )\n .subscribe({\n next: (status) => {\n if (status === 'connected') {\n this.isConnecting = false;\n this.isConnected = true;\n logger.debug('[Transport] Connection established');\n resolve();\n } else {\n this.isConnecting = false;\n const error = new TransportConnectionError('Failed to connect');\n logger.error('[Transport] Connection failed');\n this.onError?.(error);\n reject(error);\n }\n },\n error: (err) => {\n this.isConnecting = false;\n logger.error('[Transport] Connection error:', err);\n this.onError?.(err instanceof Error ? err : new Error(String(err), { cause: err }));\n reject(err as Error);\n }\n });\n\n this.subscriptions.push(connectionSub);\n\n // Track disconnection\n this.subscribeTo(\n this._webSocketConnections.status$.pipe(filter((status) => status === 'disconnected')),\n () => {\n logger.debug('[Transport] Disconnected');\n this.isConnected = false;\n }\n );\n });\n });\n }\n\n public reconnect(): void {\n this._webSocketConnections.reconnect();\n }\n\n public async execute<T extends JSONRPCResponse = JSONRPCResponse>(\n request: JSONRPCRequest,\n options?: PendingRPCOptions\n ): Promise<T> {\n // Send the request through the WebSocket\n this.send(request as unknown as JSONSerializable);\n\n // Create and return a PendingRPC promise that will resolve when the matching response arrives\n return new PendingRPC<T>(request, this._jsonRPCResponse$ as Observable<T>, options).promise;\n }\n public send(message: unknown): void {\n const payload = JSON.stringify(message);\n this._outgoingMessages$.next(payload);\n }\n // request(request: HTTPRequest): Promise<HTTPResponse> {}\n public disconnect(): void {\n logger.debug('[Transport] Disconnecting');\n this.isConnected = false;\n this.isConnecting = false;\n\n // Disconnect WebSocket\n this._webSocketConnections.disconnect();\n }\n public destroy(): void {\n logger.debug('[Transport] Destroying');\n this.disconnect();\n super.destroy();\n this._webSocketConnections.destroy();\n }\n private async _loadProtocolFromStorage(): Promise<void> {\n try {\n const storedProtocol = await this.storage.getItem<string>(this.protocolKey);\n this._currentProtocol = storedProtocol ?? undefined;\n this.protocol$.next(storedProtocol ?? undefined);\n } catch (error) {\n logger.error('Failed to retrieve protocol from storage:', error);\n throw error;\n }\n }\n\n private async _updateProtocolInStorage(protocol: string | undefined): Promise<void> {\n if (!protocol) {\n try {\n await this.storage.removeItem(this.protocolKey);\n } catch (error) {\n logger.error('Failed to remove protocol from storage:', error);\n throw error;\n }\n return;\n }\n\n try {\n const storedProtocol = await this.storage.getItem<string>(this.protocolKey);\n if (!storedProtocol || storedProtocol !== protocol) {\n await this.storage.setItem(this.protocolKey, protocol);\n }\n } catch (error) {\n logger.error('Failed to update protocol in storage:', error);\n throw error;\n }\n }\n\n private async _init(): Promise<boolean> {\n await this._loadProtocolFromStorage();\n return true;\n }\n}\n","import { jwtDecode } from 'jwt-decode';\nimport { filter, firstValueFrom, of, skip, switchMap, type Observable } from 'rxjs';\n\nimport { Destroyable } from '../behaviors/Destroyable';\nimport { DependencyContainer } from '../containers/DependencyContainer';\nimport { ClientPreferences, PreferencesContainer } from '../containers/PreferencesContainer';\nimport { CryptoController } from '../controllers/CryptoController';\nimport { NetworkMonitor } from '../controllers/NetworkMonitor';\nimport { detectPlatformCapabilities } from '../controllers/PlatformCapabilities';\nimport { PreflightRunner } from '../controllers/PreflightRunner';\nimport { VisibilityController } from '../controllers/VisibilityController';\nimport {\n CREDENTIAL_REFRESH_MAX_RETRIES,\n CREDENTIAL_REFRESH_RETRY_BASE_MS,\n CREDENTIAL_REFRESH_MAX_DELAY_MS,\n CREDENTIAL_REFRESH_BUFFER_MS\n} from '../core/constants';\nimport { Subscriber } from '../core/entities/Subscriber';\nimport { InvalidCredentialsError, TokenRefreshError, UnexpectedError } from '../core/errors';\nimport { RPCExecute } from '../core/RPCMessages';\nimport { AttachManager } from '../managers/AttachManager';\nimport { ClientSessionManager, ClientSessionWrapper } from '../managers/ClientSessionManager';\nimport { ConversationsManager } from '../managers/ConversationsManager';\nimport { DeviceTokenManager } from '../managers/DeviceTokenManager';\nimport { DiagnosticsCollector } from '../managers/DiagnosticsCollector';\nimport { DirectoryManager } from '../managers/DirectoryManager';\nimport { TransportManager } from '../managers/TransportManager';\nimport { getLogger, setLogger, setDebugOptions, setLogLevel } from '../utils/logger';\n\nimport type { Address } from '../core/entities/Address';\nimport type { Directory } from '../core/entities/Directory';\nimport type { Call } from '../core/entities/types/call.types';\nimport type {\n NodeSocketAdapter,\n SDKCredential,\n WebSocketAdapter\n} from '../core/types/common.types';\nimport type { MediaOptions } from '../core/types/media.types';\nimport type {\n PlatformCapabilities,\n DeviceRecoveryEvent,\n PermissionResult,\n PreflightOptions,\n PreflightResult,\n SessionDiagnostics\n} from '../core/types/resilience.types';\nimport type { CredentialProvider, Storage, WebRTCApiProvider } from '../dependencies/interfaces';\nimport type { DeviceController } from '../interfaces/DeviceController';\nimport type { SDKLogger, LogLevel, DebugOptions } from '../utils/logger';\n\nconst logger = getLogger();\n\ninterface JWTHeader {\n ch?: string;\n typ?: string;\n}\n/** Options for constructing a {@link SignalWire}. */\nexport interface SignalWireOptions {\n /** Skip automatic WebSocket connection on construction. */\n skipConnection?: boolean;\n /** Skip automatic subscriber registration on construction. */\n skipRegister?: boolean;\n /** Skip monitoring media device changes. */\n skipDeviceMonitoring?: boolean;\n /** Whether to reconnect to previously attached calls. */\n reconnectAttachedCalls?: boolean;\n /** Whether to save preferences. */\n savePreferences?: boolean;\n /**\n * Persist the session across page reloads.\n *\n * When `true`, credential, authorization state, and protocol are stored in\n * `localStorage` (survives reload). The DPoP key pair is persisted in\n * IndexedDB. On reload, the SDK restores the session from cache\n * without calling `credentialProvider.authenticate()`.\n *\n * When `false` (default), session data lives in `sessionStorage` and is\n * lost on reload.\n *\n * Call {@link SignalWire.destroy | destroy()} to clear all persisted state\n * (explicit logout).\n */\n persistSession?: boolean;\n /** Custom storage implementation for persistence. */\n storageImplementation?: Storage;\n /** Custom WebSocket constructor */\n webSocketConstructor?: WebSocketAdapter | NodeSocketAdapter;\n /** Custom WebRTC API provider */\n webRTCApiProvider?: WebRTCApiProvider;\n /**\n * Custom logger implementation. Must implement the {@link SDKLogger} interface.\n * Pass `null` to restore the built-in logger.\n *\n * **Note:** Logger configuration is global — setting it on one instance affects all instances.\n */\n logger?: SDKLogger | null;\n /**\n * Log level for the built-in logger.\n * Default: `'warn'`. Set to `'debug'` for verbose SDK output.\n * Has no effect when a custom `logger` is provided.\n *\n * **Note:** Logger configuration is global — setting it on one instance affects all instances.\n */\n logLevel?: LogLevel;\n /** Debug options for verbose SDK diagnostics (e.g., `{ logWsTraffic: true }`). */\n debug?: DebugOptions;\n}\n\n/** Options for {@link SignalWire.dial}. Extends {@link MediaOptions} with dial-specific settings. */\nexport interface DialOptions extends MediaOptions {\n /** Preferred video codecs for this call (overrides global preferences). */\n preferredVideoCodecs?: string[];\n /** Preferred audio codecs for this call (overrides global preferences). */\n preferredAudioCodecs?: string[];\n /** Enable stereo Opus for this call (overrides global preferences). */\n stereo?: boolean;\n /** Optional node ID for routing the call */\n nodeId?: string;\n}\n\nconst buildOptionsFromDestination = (destination: string | Address): DialOptions => {\n if (typeof destination === 'string') {\n const queryStartIndex = destination.indexOf('?');\n if (queryStartIndex !== -1) {\n const queryString = destination.substring(queryStartIndex + 1);\n const params = new URLSearchParams(queryString);\n const channel = params.get('channel');\n\n if (channel === 'video') {\n return { audio: true, video: true, receiveVideo: true };\n } else if (channel === 'audio') {\n return { audio: true, video: false };\n }\n }\n }\n\n return {};\n};\n/**\n * Main entry point for the SignalWire Browser SDK.\n *\n * Manages authentication, WebSocket transport, call creation, and media devices.\n *\n * @example\n * ```ts\n * const client = new SignalWire(credentialProvider);\n * client.isConnected$.subscribe(connected => console.log('Connected:', connected));\n * const call = await client.dial('/public/my-room');\n * ```\n */\nclass SignalWire extends Destroyable implements DeviceController {\n /** Global SDK preferences (timeouts, ICE config, media defaults). */\n public preferences = new ClientPreferences();\n private _subscriber$ = this.createBehaviorSubject<Subscriber | undefined>(undefined);\n private _directory$ = this.createBehaviorSubject<Directory | undefined>(undefined);\n private _transport!: TransportManager;\n private _clientSession!: ClientSessionManager;\n private _publicSession!: ClientSessionWrapper;\n private _deviceController!: DeviceController;\n private _attachManager?: AttachManager;\n private _isConnected$ = this.createBehaviorSubject<boolean>(false);\n private _isRegistered$ = this.createBehaviorSubject<boolean>(false);\n private _errors$ = this.createReplaySubject<Error>(1);\n private _options: SignalWireOptions = {};\n private _refreshTimerId?: ReturnType<typeof setTimeout>;\n private _dpopManager?: CryptoController;\n private _deviceTokenManager?: DeviceTokenManager;\n private _credentialProvider?: CredentialProvider;\n private _deps = new DependencyContainer();\n\n // Resilience subsystems\n private _networkMonitor?: NetworkMonitor;\n private _visibilityController?: VisibilityController;\n private _diagnosticsCollector?: DiagnosticsCollector;\n private _platformCapabilities?: PlatformCapabilities;\n\n /**\n * Creates a new SignalWire client and begins connecting.\n *\n * @param credentialProvider - Provider that supplies authentication credentials.\n * @param options - Configuration options (connection, device monitoring, preferences).\n */\n constructor(credentialProvider: CredentialProvider | undefined, options: SignalWireOptions = {}) {\n super();\n this._credentialProvider = credentialProvider;\n this._options = {\n ...PreferencesContainer.instance.defaultSignalWireOptions,\n ...options\n };\n\n // Set custom storage implementation if provided\n if (this._options.storageImplementation) {\n this._deps.storageImpl = this._options.storageImplementation;\n }\n\n if (this._options.persistSession) {\n this._deps.persistSession = true;\n }\n\n if (this._options.webSocketConstructor) {\n this._deps.WebSocket = this._options.webSocketConstructor;\n }\n\n if (this._options.savePreferences) {\n this.preferences.enableSavePreferences(this._deps.storage);\n }\n\n if (this._options.webRTCApiProvider) {\n this._deps.webRTCApiProvider = this._options.webRTCApiProvider;\n }\n\n // Configure logger from options\n if (this._options.logger !== undefined) {\n setLogger(this._options.logger);\n }\n if (this._options.logLevel) {\n setLogLevel(this._options.logLevel);\n }\n if (this._options.debug) {\n setDebugOptions(this._options.debug);\n }\n\n this._deviceController = this._deps.deviceController;\n if (!this._options.skipDeviceMonitoring) {\n this._deviceController.enableDeviceMonitoring();\n }\n\n this.subscribeTo(this._deviceController.errors$, (error) => {\n this._errors$.next(error);\n });\n\n // Initialize resilience subsystems (non-fatal)\n this.initResilienceSubsystems();\n\n this.resolveCredentials()\n .then(() => {\n this.init().catch((error: unknown) => {\n logger.error('[SignalWire] Initialization error:', error);\n // Clear stale cached credential so next attempt starts fresh\n void this._deps.storage.removeItem('sw:cached_credential');\n void this._deps.storage.removeItem('sw:cached_credential', 'local');\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n });\n })\n .catch((error: unknown) => {\n logger.error('[SignalWire] Initialization error:', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n });\n }\n\n /**\n * Initializes DPoP if not already set up. Returns the fingerprint on success.\n */\n private async initDPoP(): Promise<string | undefined> {\n if (this._dpopManager?.initialized) {\n return this._dpopManager.fingerprint;\n }\n try {\n this._dpopManager = new CryptoController();\n const fingerprint = await this._dpopManager.init();\n logger.debug('[SignalWire] DPoP initialized, fingerprint available');\n return fingerprint;\n } catch (error) {\n logger.warn('[SignalWire] DPoP initialization failed, proceeding without DPoP:', error);\n this._dpopManager = undefined;\n return undefined;\n }\n }\n\n /**\n * Resolves credentials using cache-first strategy when persistSession is enabled.\n *\n * 1. If persistSession → check localStorage for cached credential\n * 2. If cached and not expired → use it (skip provider.authenticate())\n * 3. If no cache or expired → call provider.authenticate()\n * 4. If no provider AND no cache → throw\n */\n private async resolveCredentials(): Promise<void> {\n const fingerprint = await this.initDPoP();\n\n // If a provider is given, use it — fresh login always wins over stale cache.\n // Cache is only used when there's no provider (reload / session restore).\n if (this._credentialProvider) {\n return this.validateCredentials(this._credentialProvider, undefined, fingerprint);\n }\n\n // No provider — check for cached credential: localStorage first (persistSession),\n // then sessionStorage (reload within same tab).\n for (const scope of this._deps.persistSession\n ? (['local', 'session'] as const)\n : (['session'] as const)) {\n try {\n const cached = await this._deps.storage.getItem<SDKCredential>(\n 'sw:cached_credential',\n scope\n );\n if (cached?.token) {\n logger.debug(`[SignalWire] Using cached credential from ${scope}Storage`);\n return await this.validateCredentials(undefined, cached);\n }\n } catch {\n // continue to next scope\n }\n }\n\n throw new InvalidCredentialsError(\n 'No credential provider and no cached session. Provide a CredentialProvider or enable persistSession with a prior login.'\n );\n\n return this.validateCredentials(this._credentialProvider, undefined, fingerprint);\n }\n\n private async validateCredentials(\n credentialProvider: CredentialProvider | undefined,\n credentials?: SDKCredential,\n fingerprint?: string\n ): Promise<void> {\n const _fingerprint =\n fingerprint ?? (this._dpopManager?.initialized ? this._dpopManager.fingerprint : undefined);\n\n const _credentials =\n credentials ??\n (credentialProvider\n ? await credentialProvider.authenticate(\n _fingerprint ? { fingerprint: _fingerprint } : undefined\n )\n : undefined);\n\n if (!_credentials) {\n throw new InvalidCredentialsError('No credentials available.');\n }\n if (_credentials.token) {\n try {\n const decodeHeader: JWTHeader = jwtDecode(_credentials.token, { header: true });\n this._deps.ch = decodeHeader.ch;\n } catch (error) {\n logger.error('[SignalWire] Invalid JWT token provided in credentials:', error);\n throw new InvalidCredentialsError('Invalid JWT token provided in credentials.', {\n cause: error\n });\n }\n }\n if (!_credentials.token && !_credentials.authorizationState) {\n logger.error('[SignalWire] No valid authentication credentials provided.');\n throw new InvalidCredentialsError('No valid authentication credentials provided.');\n }\n\n // Skip client-side expiry check when using cached credentials for reconnect.\n // The server validates via authorization_state — even expired tokens work\n // when authorization_state is present (session continuity).\n if (\n !this._deps.persistSession &&\n _credentials.expiry_at &&\n _credentials.expiry_at < Date.now()\n ) {\n logger.error('[SignalWire] Provided credentials have expired.');\n throw new InvalidCredentialsError('Provided credentials have expired.');\n }\n\n if (_credentials.expiry_at && credentialProvider?.refresh) {\n this.scheduleCredentialRefresh(credentialProvider, _credentials.expiry_at);\n }\n\n this._deps.credential = _credentials;\n this.persistCredential(_credentials);\n\n if (this.isConnected && this._clientSession.authenticated && _credentials.token) {\n try {\n await this._clientSession.reauthenticate(_credentials.token);\n logger.info('[SignalWire] Session refreshed with new credentials.');\n } catch (error: unknown) {\n logger.error('[SignalWire] Failed to refresh session with new credentials:', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n }\n }\n }\n\n /**\n * Schedules credential refresh with exponential backoff retry on failure.\n * On success, resets attempt counter and schedules the next refresh.\n * After exhausting retries, emits TokenRefreshError and disconnects.\n */\n private scheduleCredentialRefresh(\n credentialProvider: CredentialProvider,\n expiresAt: number,\n attempt = 0\n ): void {\n // Clear any existing timer to prevent stacking\n if (this._refreshTimerId !== undefined) {\n clearTimeout(this._refreshTimerId);\n }\n\n const refreshInterval =\n attempt === 0\n ? Math.max(expiresAt - Date.now() - CREDENTIAL_REFRESH_BUFFER_MS, 1000)\n : Math.min(\n CREDENTIAL_REFRESH_RETRY_BASE_MS * Math.pow(2, attempt) * (0.5 + Math.random() * 0.5), // equal-jitter to prevent thundering herd\n CREDENTIAL_REFRESH_MAX_DELAY_MS\n );\n\n this._refreshTimerId = setTimeout(async () => {\n try {\n if (!credentialProvider.refresh) {\n throw new InvalidCredentialsError('Credential provider does not support refresh');\n }\n const newCredentials = await credentialProvider.refresh();\n this._deps.credential = newCredentials;\n this.persistCredential(newCredentials);\n logger.info('[SignalWire] Credentials refreshed successfully.');\n // Reset attempt counter on success and schedule next refresh\n if (newCredentials.expiry_at) {\n this.scheduleCredentialRefresh(credentialProvider, newCredentials.expiry_at, 0);\n }\n } catch (error: unknown) {\n const nextAttempt = attempt + 1;\n logger.error(\n `[SignalWire] Credential refresh failed (attempt ${nextAttempt}/${CREDENTIAL_REFRESH_MAX_RETRIES}):`,\n error\n );\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n if (nextAttempt < CREDENTIAL_REFRESH_MAX_RETRIES) {\n // expiresAt is unused on retry (delay is backoff-based), but passed through\n // so a successful retry can schedule the next refresh from the new expiry\n this.scheduleCredentialRefresh(credentialProvider, expiresAt, nextAttempt);\n } else {\n logger.error('[SignalWire] Credential refresh exhausted all retries. Disconnecting.');\n this._errors$.next(new TokenRefreshError('Credential refresh failed after max retries'));\n void this.disconnect();\n }\n }\n }, refreshInterval);\n }\n\n /** Persist credential to localStorage when persistSession is enabled. */\n private persistCredential(credential: SDKCredential): void {\n if (!credential.token) return;\n // Always write to sessionStorage (reload within same tab)\n void this._deps.storage.setItem('sw:cached_credential', credential);\n // Also write to localStorage when persistSession (survives new tabs)\n if (this._deps.persistSession) {\n void this._deps.storage.setItem('sw:cached_credential', credential, 'local');\n }\n }\n\n private async init() {\n // Initialize subscriber first (before transport) to fetch subscriber info\n this._subscriber$.next(new Subscriber(this._deps.http));\n\n if (!this._options.skipConnection) {\n await this.connect();\n }\n\n // Flush attached calls after connect creates the AttachManager\n if (!this._options.reconnectAttachedCalls && this._attachManager) {\n await this._attachManager.flush();\n }\n\n if (!this._options.skipRegister) {\n // eventually register after the authentication\n try {\n await this.register();\n } catch (error) {\n logger.error('[SignalWire] Registration failed:', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n }\n }\n\n // eventually reconnect to attached calls after the authentication\n void this.handleAttachments();\n }\n\n private async handleAttachments() {\n if (!this._attachManager) {\n logger.error('[SignalWire] AttachManager not initialized');\n return;\n }\n if (!this._options.reconnectAttachedCalls) {\n return;\n }\n try {\n await this._attachManager.reattachCalls();\n } catch (error) {\n logger.error('[SignalWire] Failed to reattach calls:', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n }\n }\n\n /**\n * Establishes the WebSocket connection and authenticates the session.\n *\n * ## Reconnection behavior\n *\n * After a successful connection the underlying {@link WebSocketController}\n * automatically attempts to reconnect whenever the socket closes\n * unexpectedly (e.g. network change, server restart). Reconnection uses an\n * **exponential back-off** strategy:\n *\n * - First retry after `reconnectDelayMin` (default **0.1 s**).\n * - Each subsequent retry doubles the delay up to `reconnectDelayMax`\n * (default **3 s**).\n * - The delay resets to `reconnectDelayMin` once a connection succeeds.\n * - A per-attempt `connectionTimeout` (default **10 s**) aborts the\n * attempt and schedules the next retry if the server does not respond.\n *\n * Calling {@link disconnect} stops the reconnection loop entirely.\n *\n * ## Message handling during temporary disconnections\n *\n * While the socket is not in the `connected` state, **outgoing messages\n * are queued** in an internal buffer. Once the connection is\n * re-established the queue is flushed in order so no outgoing RPC call is\n * lost.\n *\n * **Incoming** server-to-client messages that arrive while the socket is\n * down are *not* buffered by the SDK — they are expected to be\n * re-delivered by the server after the session is re-authenticated.\n * Active RPC calls that were awaiting a response will time out\n * (default **5 s**) and reject with an `RPCTimeoutError`; callers should\n * handle this and retry if appropriate.\n *\n * The connection status can be observed via the `status$` observable on\n * the transport layer, which emits `'connecting'`, `'connected'`,\n * `'reconnecting'`, `'disconnecting'`, or `'disconnected'`.\n */\n public async connect(): Promise<void> {\n // Wait for subscriber to be fetched first to get the subscriber ID\n try {\n const subscriber = this._subscriber$.value;\n if (!subscriber) {\n throw new UnexpectedError('Subscriber not initialized before connect');\n }\n\n const fetched = await firstValueFrom(subscriber.fetched$);\n\n if (!fetched) {\n throw new UnexpectedError(\n 'Failed to fetch subscriber information - fetched$ emitted false'\n );\n }\n\n // Set the subscriber ID in the dependency container\n this._deps.subscriber = subscriber;\n } catch (error) {\n logger.error(\n `[SignalWire] Failed to fetch subscriber information: ${error instanceof Error ? error.message : 'Unknown error'}. ` +\n `This usually means the subscriber token is invalid or expired.`\n );\n throw new UnexpectedError('Error fetching subscriber information', { cause: error });\n }\n\n const errorHandler = (error: Error) => {\n this._errors$.next(error);\n };\n\n // Now initialize transport and session with subscriber ID available\n this._transport = new TransportManager(\n this._deps.storage,\n this._deps.protocolKey,\n this._deps.WebSocket,\n PreferencesContainer.instance.relayHost ?? this._deps.relayHost,\n errorHandler\n );\n\n // Create AttachManager (needed by CallFactory -> VertoManager)\n this._attachManager = new AttachManager(\n this._deps.storage,\n this._deps.deviceController,\n PreferencesContainer.instance.reconnectCallsTimeout,\n this._deps.attachedCallsKey\n );\n\n this._clientSession = new ClientSessionManager(\n () => this._deps.credential,\n this._transport,\n this._deps.storage,\n this._deps.authorizationStateKey,\n this._deps.deviceController,\n this._attachManager,\n this._deps.webRTCApiProvider,\n this._dpopManager,\n this._networkMonitor?.networkChange$\n );\n this._publicSession = new ClientSessionWrapper(this._clientSession);\n\n // Hook: refresh credentials before fresh reconnect when token may be expired\n this._clientSession.onBeforeReconnect = async () => {\n if (!this._credentialProvider) return;\n try {\n const fingerprint = this._dpopManager?.initialized\n ? this._dpopManager.fingerprint\n : undefined;\n logger.debug('[SignalWire] Credential expired, refreshing before reconnect');\n const newCredentials = await this._credentialProvider.authenticate(\n fingerprint ? { fingerprint } : undefined\n );\n this._deps.credential = newCredentials;\n logger.debug('[SignalWire] Credential refreshed successfully for reconnect');\n } catch (error) {\n logger.error('[SignalWire] Failed to refresh credentials for reconnect:', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n // Propagate to prevent reconnect with stale credentials\n throw error;\n }\n };\n\n this.subscribeTo(this._clientSession.errors$, (error) => {\n this._errors$.next(error);\n });\n\n await this._clientSession.connect();\n\n // Activate Client Bound SAT if subscriber has refresh scope\n if (this._dpopManager?.initialized) {\n if (this._refreshTimerId) {\n clearTimeout(this._refreshTimerId);\n this._refreshTimerId = undefined;\n logger.debug(\n '[SignalWire] Developer refresh disabled — Client Bound SAT activation starting'\n );\n }\n\n this._deviceTokenManager = new DeviceTokenManager(\n this._dpopManager,\n this._deps.http,\n (error) => this._errors$.next(error),\n () => this._deps.credential\n );\n await this._deviceTokenManager.activate(\n this._deps.subscriber,\n this._clientSession,\n (cred) => {\n this._deps.credential = { ...this._deps.credential, ...cred };\n }\n );\n }\n\n // Re-register subscriber (and re-activate Client Bound SAT) after WebSocket reconnect.\n // activate() must complete before register() so the fresh Client Bound SAT\n // is used for the subscriber.online RPC.\n this.subscribeTo(\n this._clientSession.authenticated$.pipe(skip(1), filter(Boolean)),\n async () => {\n try {\n if (this._deviceTokenManager) {\n await this._deviceTokenManager.activate(\n this._deps.subscriber,\n this._clientSession,\n (cred) => {\n this._deps.credential = { ...this._deps.credential, ...cred };\n }\n );\n logger.debug('[SignalWire] Client Bound SAT re-activated after reconnect');\n }\n } catch (error) {\n logger.error('[SignalWire] Client Bound SAT re-activation failed (non-fatal):', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n }\n\n try {\n logger.debug('[SignalWire] Re-registering subscriber after reconnect');\n await this.register();\n logger.debug('[SignalWire] Subscriber re-registered successfully after reconnect');\n } catch (error) {\n logger.error('[SignalWire] Re-registration failed after reconnect:', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n }\n }\n );\n\n // Create conversationManager first to inject into DirectoryManager\n const conversationManager = new ConversationsManager(\n this._clientSession,\n this._deps.http,\n () => this._deps.getSubscriberFromAddressId(),\n errorHandler\n );\n\n // Create directory with all dependencies injected\n const directory = new DirectoryManager(\n this._deps.http,\n this._clientSession,\n conversationManager,\n errorHandler\n );\n this._directory$.next(directory);\n this._clientSession.setDirectory(directory);\n\n this._isConnected$.next(true);\n\n // Record connection event in diagnostics\n this._diagnosticsCollector?.record('connection', 'connected');\n\n // Wire reconnect events to diagnostics.\n // skip(1) skips the replayed BehaviorSubject value (false).\n // skip(2) also skips the initial auth success (true).\n // Only subsequent true emissions (re-authentication after reconnect) trigger this.\n this.subscribeTo(this._clientSession.authenticated$.pipe(skip(2), filter(Boolean)), () => {\n this._diagnosticsCollector?.record('connection', 'reconnected');\n });\n }\n\n /**\n * Observable that emits the {@link Subscriber} profile once fetched,\n * or `undefined` before authentication completes.\n *\n * @example\n * ```ts\n * client.subscriber$.subscribe(sub => {\n * if (sub) console.log('Logged in as', sub.email);\n * });\n * ```\n */\n public get subscriber$(): Observable<Subscriber | undefined> {\n return this.deferEmission(this._subscriber$.asObservable());\n }\n\n /** Current subscriber snapshot, or `undefined` if not yet authenticated. */\n public get subscriber(): Subscriber | undefined {\n return this._subscriber$.value;\n }\n\n /**\n * Observable that emits the {@link Directory} instance once the client is connected,\n * or `undefined` while disconnected. Subscribe to this to safely wait for the directory\n * to become available without risking an error.\n *\n * @example\n * ```ts\n * client.directory$.subscribe(dir => {\n * if (dir) dir.addresses$.subscribe(console.log);\n * });\n * ```\n */\n public get directory$(): Observable<Directory | undefined> {\n return this.deferEmission(this._directory$.asObservable());\n }\n\n /**\n * Current directory snapshot, or `undefined` if the client is not yet connected.\n * Prefer {@link directory$} when you need to react to the directory becoming available.\n */\n public get directory(): Directory | undefined {\n return this._directory$.value;\n }\n\n /** Observable that emits when the subscriber registration state changes. */\n public get isRegistered$(): Observable<boolean> {\n return this.deferEmission(this._isRegistered$.asObservable());\n }\n\n /** Whether the subscriber is currently registered. */\n public get isRegistered(): boolean {\n return this._isRegistered$.value;\n }\n\n /** Whether the client is currently connected. */\n public get isConnected(): boolean {\n return this._isConnected$.value;\n }\n\n /** Observable that emits when the connection state changes. */\n public get isConnected$(): Observable<boolean> {\n return this.deferEmission(this._isConnected$.asObservable());\n }\n\n /** Observable that emits `true` when the client is both connected and authenticated. */\n public get ready$(): Observable<boolean> {\n return this.publicCachedObservable('ready$', () =>\n this._isConnected$.pipe(\n switchMap((connected) => (connected ? this._clientSession.authenticated$ : of(false)))\n )\n );\n }\n\n /** Observable stream of errors from transport, authentication, and devices. */\n public get errors$(): Observable<Error> {\n return this.deferEmission(this._errors$.asObservable());\n }\n\n // ===========================================================================\n // Resilience API\n // ===========================================================================\n\n /** Platform WebRTC capabilities detected at construction time. */\n public get platformCapabilities(): PlatformCapabilities {\n this._platformCapabilities ??= detectPlatformCapabilities(this._options.webRTCApiProvider);\n return this._platformCapabilities;\n }\n\n /** Observable that emits when the SDK auto-switches a device. */\n public get deviceRecovered$(): Observable<DeviceRecoveryEvent> {\n return this.deferEmission(this._deviceController.deviceRecovered$);\n }\n\n /**\n * Export a structured diagnostic bundle for support/debugging.\n * Includes connection events, call summaries, and device changes.\n */\n public exportDiagnostics(): SessionDiagnostics {\n const devices = {\n audioInput: this.audioInputDevices,\n audioOutput: this.audioOutputDevices,\n videoInput: this.videoInputDevices\n };\n\n const base: SessionDiagnostics = {\n sdkVersion: 'unknown',\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'unknown',\n capabilities: this.platformCapabilities,\n events: [],\n calls: [],\n deviceChanges: [],\n devices\n };\n\n if (!this._diagnosticsCollector) {\n return base;\n }\n\n const raw = this._diagnosticsCollector.export();\n // The collector's internal types (CallDiagnosticSummary, DiagnosticEvent)\n // are structurally compatible with the public SessionDiagnostics shape\n // for JSON serialization / support ticket purposes.\n return {\n ...base,\n sdkVersion: raw.sdkVersion,\n userAgent: raw.userAgent,\n events: raw.events as unknown as SessionDiagnostics['events'],\n calls: raw.calls as unknown as SessionDiagnostics['calls'],\n deviceChanges: raw.deviceChanges as unknown as SessionDiagnostics['deviceChanges'],\n devices\n };\n }\n\n /**\n * Initialize resilience subsystems. Non-fatal: any failure is logged and\n * the SDK continues working without the failing subsystem.\n */\n private initResilienceSubsystems(): void {\n try {\n this._platformCapabilities = detectPlatformCapabilities(this._options.webRTCApiProvider);\n } catch (error) {\n logger.warn('[SignalWire] Failed to detect platform capabilities:', error);\n }\n\n try {\n this._networkMonitor = new NetworkMonitor();\n } catch (error) {\n logger.warn('[SignalWire] Failed to initialize NetworkMonitor:', error);\n }\n\n try {\n this._visibilityController = new VisibilityController();\n\n // Re-enumerate devices when page becomes visible (if pref enabled)\n this.subscribeTo(\n this._visibilityController.visibilityChange$.pipe(\n filter(\n (event) =>\n event.to === 'visible' && PreferencesContainer.instance.refreshDevicesOnVisible\n )\n ),\n () => {\n logger.debug('[SignalWire] Page visible, re-enumerating devices');\n // Trigger device re-enumeration by disabling/re-enabling monitoring\n try {\n this._deviceController.disableDeviceMonitoring();\n this._deviceController.enableDeviceMonitoring();\n } catch {\n // Non-fatal\n }\n }\n );\n } catch (error) {\n logger.warn('[SignalWire] Failed to initialize VisibilityController:', error);\n }\n\n try {\n const sdkVersion: string = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'unknown';\n this._diagnosticsCollector = new DiagnosticsCollector({\n sdkVersion\n });\n } catch (error) {\n logger.warn('[SignalWire] Failed to initialize DiagnosticsCollector:', error);\n }\n }\n\n /**\n * Disconnects the WebSocket and tears down the current session.\n *\n * The client can be reconnected by calling {@link connect} again,\n * which creates a fresh transport and session.\n */\n public async disconnect(): Promise<void> {\n // Clear credential refresh timer to prevent zombie refresh on disconnected client\n if (this._refreshTimerId) {\n clearTimeout(this._refreshTimerId);\n this._refreshTimerId = undefined;\n }\n this._diagnosticsCollector?.record('connection', 'disconnected');\n await this._clientSession.disconnect();\n this._clientSession.destroy();\n this._isConnected$.next(false);\n }\n\n private async waitAuthentication(): Promise<void> {\n // Wait for client to be ready (authenticated)\n await firstValueFrom(this.ready$.pipe(filter((ready) => ready === true)));\n }\n\n /**\n * Registers the subscriber as online to receive inbound calls and events.\n *\n * Waits for authentication to complete before sending the registration.\n * If the initial attempt fails, reauthentication is attempted automatically.\n *\n * @throws {InvalidCredentialsError} If registration and reauthentication both fail.\n */\n public async register(): Promise<void> {\n try {\n // Wait for client session to be authenticated before registering subscriber\n await this.waitAuthentication();\n await this._transport.execute(RPCExecute({ method: 'subscriber.online', params: {} }));\n this._isRegistered$.next(true);\n } catch (error) {\n logger.debug('[SignalWire] Failed to register subscriber, trying reauthentication...');\n if (this._deps.credential.token) {\n // Fire-and-forget: attempt reauthentication and retry registration.\n // Errors are emitted on errors$ — do NOT throw inside .catch() as\n // this is an unattached promise chain and would cause an unhandled rejection.\n void this._clientSession\n .reauthenticate(this._deps.credential.token)\n .then(async () => {\n logger.debug('[SignalWire] Reauthentication successful, retrying register()');\n await this._transport.execute(RPCExecute({ method: 'subscriber.online', params: {} }));\n this._isRegistered$.next(true);\n })\n .catch((reauthError: unknown) => {\n logger.error('[SignalWire] Reauthentication failed during register():', reauthError);\n const registerError = new InvalidCredentialsError(\n 'Failed to register subscriber, and reauthentication attempt also failed. Please check your credentials.',\n {\n cause:\n reauthError instanceof Error\n ? reauthError\n : new Error(String(reauthError), { cause: reauthError })\n }\n );\n this._errors$.next(registerError);\n });\n }\n\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n throw error;\n }\n }\n\n /**\n * Unregisters the subscriber, going offline for inbound calls.\n *\n * The WebSocket connection remains open; use {@link disconnect} to fully close it.\n */\n public async unregister(): Promise<void> {\n try {\n await this._transport.execute(RPCExecute({ method: 'subscriber.offline', params: {} }));\n this._isRegistered$.next(false);\n } catch (error) {\n logger.error('[SignalWire] Failed to unregister subscriber:', error);\n this._errors$.next(\n error instanceof Error ? error : new Error(String(error), { cause: error })\n );\n throw error;\n }\n }\n\n /**\n * Places an outbound call to the given destination.\n *\n * Waits for authentication before dialing. Media options are merged from\n * saved preferences, destination query parameters (e.g. `?channel=video`),\n * and the provided `options` (highest priority).\n *\n * Returns a {@link Call} in `'ringing'` state. Subscribe to {@link Call.status$}\n * to track progression through `'connected'` → `'disconnected'`.\n *\n * @param destination - Address URI string (e.g. `'/public/my-room'`) or {@link Address} instance.\n * @param options - Media and dial options (audio/video, device constraints). Overrides defaults.\n * @returns The created {@link Call} instance.\n * @throws {Error} If authentication is not complete or call creation fails.\n *\n * @example\n * ```ts\n * const call = await client.dial('/public/conference', {\n * audio: true,\n * video: true,\n * });\n * call.status$.subscribe(status => console.log('Call:', status));\n * ```\n */\n public async dial(destination: string | Address, options: DialOptions = {}): Promise<Call> {\n const computed_options = {\n ...PreferencesContainer.instance.preferredMediaOptions,\n ...buildOptionsFromDestination(destination),\n ...options\n };\n\n // Wait for client to be ready\n await this.waitAuthentication();\n\n logger.debug('[SignalWire] Dialing with options:', computed_options);\n return this._clientSession.createOutboundCall(destination, computed_options);\n }\n\n /**\n * Runs a multi-phase connectivity test against the given destination.\n *\n * The test checks:\n * 1. **Signaling** -- WebSocket connected, RTT measurement\n * 2. **Devices** -- getUserMedia succeeds with selected (or specified) devices\n * 3. **ICE/TURN** -- gathers ICE candidates to verify STUN/TURN reachability\n * 4. **Media/bandwidth** (unless `skipMediaTest`) -- dials the destination,\n * collects getStats() for `duration` seconds, computes bandwidth estimates\n *\n * @param destination - A destination to dial for the media test (e.g. `'/private/network-test'`).\n * @param options - Preflight options (duration, skipMediaTest, device overrides).\n * @returns A {@link PreflightResult} describing connectivity health.\n *\n * @example\n * ```ts\n * const result = await client.preflight('/private/network-test', { duration: 5 });\n * if (!result.ok) console.warn('Connectivity issues:', result.warnings);\n * ```\n */\n public async preflight(\n destination: string,\n options?: PreflightOptions\n ): Promise<PreflightResult> {\n const iceServers =\n this._clientSession.iceServers ?? PreferencesContainer.instance.iceServers ?? [];\n const isConnected = this._isConnected$.value;\n const transportRtt = 0; // RTT is best-effort; 0 when not measurable\n\n const runner = new PreflightRunner(\n this._deviceController,\n iceServers,\n isConnected,\n transportRtt,\n async (dest, opts) => this.dial(dest, opts),\n options\n );\n\n return runner.run(destination);\n }\n\n /** The underlying client session for advanced RPC operations. */\n public get session(): ClientSessionWrapper {\n return this._publicSession;\n }\n\n // DeviceController interface implementation\n\n /** Observable list of available audio input (microphone) devices. */\n public get audioInputDevices$(): Observable<MediaDeviceInfo[]> {\n return this.deferEmission(this._deviceController.audioInputDevices$);\n }\n\n /** Current snapshot of available audio input devices. */\n public get audioInputDevices(): MediaDeviceInfo[] {\n return this._deviceController.audioInputDevices;\n }\n\n /** Observable list of available audio output (speaker) devices. */\n public get audioOutputDevices$(): Observable<MediaDeviceInfo[]> {\n return this.deferEmission(this._deviceController.audioOutputDevices$);\n }\n\n /** Current snapshot of available audio output devices. */\n public get audioOutputDevices(): MediaDeviceInfo[] {\n return this._deviceController.audioOutputDevices;\n }\n\n /** Observable list of available video input (camera) devices. */\n public get videoInputDevices$(): Observable<MediaDeviceInfo[]> {\n return this.deferEmission(this._deviceController.videoInputDevices$);\n }\n\n /** Current snapshot of available video input devices. */\n public get videoInputDevices(): MediaDeviceInfo[] {\n return this._deviceController.videoInputDevices;\n }\n\n /** Observable of the currently selected audio input device. */\n public get selectedAudioInputDevice$(): Observable<MediaDeviceInfo | null> {\n return this.deferEmission(this._deviceController.selectedAudioInputDevice$);\n }\n /** Observable of the currently selected audio output device. */\n public get selectedAudioOutputDevice$(): Observable<MediaDeviceInfo | null> {\n return this.deferEmission(this._deviceController.selectedAudioOutputDevice$);\n }\n /** Observable of the currently selected video input device. */\n public get selectedVideoInputDevice$(): Observable<MediaDeviceInfo | null> {\n return this.deferEmission(this._deviceController.selectedVideoInputDevice$);\n }\n /** Currently selected audio input device, or `null` if none. */\n public get selectedAudioInputDevice(): MediaDeviceInfo | null {\n return this._deviceController.selectedAudioInputDevice;\n }\n /** Currently selected audio output device, or `null` if none. */\n public get selectedAudioOutputDevice(): MediaDeviceInfo | null {\n return this._deviceController.selectedAudioOutputDevice;\n }\n /** Currently selected video input device, or `null` if none. */\n public get selectedVideoInputDevice(): MediaDeviceInfo | null {\n return this._deviceController.selectedVideoInputDevice;\n }\n /** Media track constraints for the selected audio input device. Returns `false` when disabled. */\n public get selectedAudioInputDeviceConstraints(): MediaTrackConstraints | boolean {\n return this._deviceController.selectedAudioInputDeviceConstraints;\n }\n /** Media track constraints for the selected video input device. Returns `false` when disabled. */\n public get selectedVideoInputDeviceConstraints(): MediaTrackConstraints | boolean {\n return this._deviceController.selectedVideoInputDeviceConstraints;\n }\n\n /** Converts a `MediaDeviceInfo` to track constraints suitable for `getUserMedia`. */\n public deviceInfoToConstraints(deviceInfo: MediaDeviceInfo | null): MediaTrackConstraints {\n return this._deviceController.deviceInfoToConstraints(deviceInfo);\n }\n\n /** Sets the preferred audio input device. */\n public selectAudioInputDevice(device: MediaDeviceInfo | null): void {\n this._deviceController.selectAudioInputDevice(device);\n }\n\n /** Sets the preferred video input device. */\n public selectVideoInputDevice(device: MediaDeviceInfo | null): void {\n this._deviceController.selectVideoInputDevice(device);\n }\n\n /** Sets the preferred audio output device. */\n public selectAudioOutputDevice(device: MediaDeviceInfo | null): void {\n this._deviceController.selectAudioOutputDevice(device);\n }\n\n /** Starts monitoring for media device changes (connect/disconnect). */\n public enableDeviceMonitoring(): void {\n this._deviceController.enableDeviceMonitoring();\n }\n\n /** Stops monitoring for media device changes. */\n public disableDeviceMonitoring(): void {\n this._deviceController.disableDeviceMonitoring();\n }\n\n /**\n * Returns the capabilities of a media device.\n * @param deviceInfo - The device to query.\n * @returns The device capabilities, or `null` if unavailable.\n */\n public async getDeviceCapabilities(\n deviceInfo: MediaDeviceInfo\n ): Promise<MediaTrackCapabilities | null> {\n return this._deviceController.getDeviceCapabilities(deviceInfo);\n }\n\n /**\n * Checks whether a device is still available and usable.\n * @param deviceInfo - The device to validate, or `null`.\n * @returns `true` if the device is valid and available. Returns `false` for `null`, audio output devices, or unavailable devices.\n */\n public async isValidDevice(deviceInfo: MediaDeviceInfo | null): Promise<boolean> {\n return this._deviceController.isValidDevice(deviceInfo);\n }\n\n // ===========================================================================\n // DeviceController passthrough: storage and device state\n // ===========================================================================\n\n /** Injects a storage manager into the device controller for persistence. */\n public setStorageManager(\n storageManager: import('../managers/StorageManager').StorageManager\n ): void {\n this._deviceController.setStorageManager(storageManager);\n }\n\n /** Clears all device state and re-enumerates. */\n public async clearDeviceState(): Promise<void> {\n return this._deviceController.clearDeviceState();\n }\n\n /** Forces a device re-enumeration. */\n public async enumerateDevices(): Promise<void> {\n return this._deviceController.enumerateDevices();\n }\n\n // ===========================================================================\n // Section 5.9: Intentional device disable\n // ===========================================================================\n\n /** Disables audio input (receive-only mode). No audio track will be acquired. */\n public disableAudioInput(): void {\n this._deviceController.disableAudioInput();\n }\n\n /** Re-enables audio input, restoring the last selection or auto-selecting. */\n public enableAudioInput(): void {\n this._deviceController.enableAudioInput();\n }\n\n /** Disables video input (receive-only mode). No video track will be acquired. */\n public disableVideoInput(): void {\n this._deviceController.disableVideoInput();\n }\n\n /** Re-enables video input, restoring the last selection or auto-selecting. */\n public enableVideoInput(): void {\n this._deviceController.enableVideoInput();\n }\n\n /** Observable that emits `true` when video input is disabled (receive-only). */\n public get videoInputDisabled$(): Observable<boolean> {\n return this.deferEmission(this._deviceController.videoInputDisabled$);\n }\n\n /** Observable that emits `true` when audio input is disabled (receive-only). */\n public get audioInputDisabled$(): Observable<boolean> {\n return this.deferEmission(this._deviceController.audioInputDisabled$);\n }\n\n /** Whether video input is currently disabled. */\n public get videoInputDisabled(): boolean {\n return this._deviceController.videoInputDisabled;\n }\n\n /** Whether audio input is currently disabled. */\n public get audioInputDisabled(): boolean {\n return this._deviceController.audioInputDisabled;\n }\n\n // ===========================================================================\n // Section 5.10: Permissions bootstrapping\n // ===========================================================================\n\n /**\n * Triggers the browser's media permission dialog and captures the user's device selections.\n *\n * @param options - Which permissions to request.\n * @param options.audio - Whether to request audio permission.\n * @param options.video - Whether to request video permission.\n * @returns The permission result with selected devices.\n */\n public async requestMediaPermissions(\n options: { audio?: boolean; video?: boolean } = { audio: true, video: true }\n ): Promise<PermissionResult> {\n const constraints: MediaStreamConstraints = {\n audio: options.audio ?? false,\n video: options.video ?? false\n };\n\n let audioGranted = false;\n let videoGranted = false;\n let selectedAudioDevice: MediaDeviceInfo | undefined;\n let selectedVideoDevice: MediaDeviceInfo | undefined;\n\n try {\n const stream = await this._deps.webRTCApiProvider.mediaDevices.getUserMedia(constraints);\n const tracks = stream.getTracks();\n\n for (const track of tracks) {\n const settings = track.getSettings();\n if (track.kind === 'audio') {\n audioGranted = true;\n if (settings.deviceId) {\n selectedAudioDevice = this.audioInputDevices.find(\n (d) => d.deviceId === settings.deviceId\n );\n }\n } else if (track.kind === 'video') {\n videoGranted = true;\n if (settings.deviceId) {\n selectedVideoDevice = this.videoInputDevices.find(\n (d) => d.deviceId === settings.deviceId\n );\n }\n }\n track.stop();\n }\n } catch (error) {\n logger.warn('[SignalWire] Media permission request failed:', error);\n }\n\n // Re-enumerate devices (labels are now available after permission grant)\n await this._deviceController.enumerateDevices();\n\n // Re-resolve selected devices after re-enumeration\n if (audioGranted && selectedAudioDevice) {\n const audioDeviceId = selectedAudioDevice.deviceId;\n const resolved = this.audioInputDevices.find((d) => d.deviceId === audioDeviceId);\n selectedAudioDevice = resolved ?? selectedAudioDevice;\n }\n if (videoGranted && selectedVideoDevice) {\n const videoDeviceId = selectedVideoDevice.deviceId;\n const resolved = this.videoInputDevices.find((d) => d.deviceId === videoDeviceId);\n selectedVideoDevice = resolved ?? selectedVideoDevice;\n }\n\n // Auto-select the browser-picked devices if no explicit selection yet\n if (audioGranted && selectedAudioDevice && !this.selectedAudioInputDevice) {\n this.selectAudioInputDevice(selectedAudioDevice);\n }\n if (videoGranted && selectedVideoDevice && !this.selectedVideoInputDevice) {\n this.selectVideoInputDevice(selectedVideoDevice);\n }\n\n return {\n audio: audioGranted,\n video: videoGranted,\n selectedAudioDevice,\n selectedVideoDevice\n };\n }\n\n // ===========================================================================\n // Section 5.11: Factory reset\n // ===========================================================================\n\n /**\n * Clears all SDK-persisted state and resets to defaults.\n *\n * This clears device preferences, device history, authorization state,\n * attached call IDs, and all SDK storage keys, then re-enumerates devices.\n */\n public async resetToDefaults(): Promise<void> {\n // 1. Clear all SDK storage keys\n await this._deps.storage.clearAll();\n\n // 2. Reset PreferencesContainer to defaults\n const prefs = PreferencesContainer.instance;\n prefs.preferredAudioInput = null;\n prefs.preferredAudioOutput = null;\n prefs.preferredVideoInput = null;\n\n // 3. Clear device history and selection\n await this._deviceController.clearDeviceState();\n }\n\n /** Destroys the client, clearing timers and releasing all resources. */\n public override destroy(): void {\n if (this._refreshTimerId) {\n clearTimeout(this._refreshTimerId);\n this._refreshTimerId = undefined;\n }\n this._deviceTokenManager?.destroy();\n this._dpopManager?.destroy();\n\n // Clear attached call references (logout — calls won't survive)\n if (this._attachManager) {\n void this._attachManager.detachAll();\n }\n\n // Stop the transport to prevent reconnection loops\n this._transport.destroy();\n this._clientSession.destroy();\n\n // Destroy resilience subsystems\n try {\n this._networkMonitor?.destroy();\n } catch {\n /* non-fatal */\n }\n try {\n this._visibilityController?.destroy();\n } catch {\n /* non-fatal */\n }\n try {\n this._diagnosticsCollector?.destroy();\n } catch {\n /* non-fatal */\n }\n this._networkMonitor = undefined;\n this._visibilityController = undefined;\n this._diagnosticsCollector = undefined;\n\n // Note: persisted session state (cached credential, authorization_state,\n // protocol) is NOT cleared here. destroy() stops the client but preserves\n // storage for reload reconnects. Use disconnect() for explicit logout\n // which clears stored state via cleanupStoredConnectionParams().\n\n super.destroy();\n }\n}\nexport { SignalWire };\n","import { RequestError, RequestTimeoutError } from '../core/errors';\nimport { getLogger } from '../utils/logger';\n\nimport type { CredentialProvider } from './interfaces';\n\nconst logger = getLogger();\n\n/** Credential provider that exchanges an embed token for a SAT via the host's token endpoint. */\nexport class EmbedTokenCredentialProvider implements CredentialProvider {\n constructor(\n private host: string,\n private embedToken: string\n ) {}\n\n private async fetchSAT(): Promise<string> {\n const url = `https://${this.host}/api/fabric/embeds/tokens`;\n\n const timeout = 10000; // 10 seconds\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n body: JSON.stringify({ token: this.embedToken }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n const data = (await response.json()) as { token: string };\n return data.token;\n }\n\n throw new RequestError(\n `Failed to fetch SAT using embed token: ${response.status} ${response.statusText}`\n );\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof Error && error.name === 'AbortError') {\n throw new RequestTimeoutError(`Request timeout after ${timeout}ms`, { cause: error });\n }\n\n logger.error('[EmbedCredentialProvider] Request failed:', error);\n throw error;\n }\n }\n\n public async authenticate(): Promise<{ token: string; expiry_at: number }> {\n const sat = await this.fetchSAT();\n const expiryAt = Date.now() + 3600 * 1000;\n return { token: sat, expiry_at: expiryAt };\n }\n\n public async refresh(): Promise<{ token: string; expiry_at: number }> {\n return this.authenticate();\n }\n}\n","import { first } from 'rxjs';\n\nimport { SignalWire } from '../clients/SignalWire';\nimport { DependencyError } from '../core/errors';\nimport { EmbedTokenCredentialProvider } from '../dependencies/EmbedTokenCredentialProvider';\n\nimport type { Call } from '../core/entities/types/call.types';\n\n/** Options for {@link embeddableCall}. */\nexport interface EmbeddableCallOptions {\n /** Destination URI to call. */\n to: string;\n /** Embed token for authentication. */\n embedToken: string;\n /** SignalWire host URL. */\n host: string;\n}\n\n/**\n * Creates a call using an embed token for simple, embeddable integrations.\n *\n * Handles client creation, authentication, and dialing in a single call.\n *\n * @param options - Embed token, host, and destination.\n * @returns The created {@link Call} instance.\n */\nexport async function embeddableCall(options: EmbeddableCallOptions): Promise<Call> {\n const { to, embedToken, host } = options;\n const requiredFailed = [];\n\n if (!to) {\n requiredFailed.push('to');\n }\n\n if (!embedToken) {\n requiredFailed.push('embedToken');\n }\n\n if (!host) {\n requiredFailed.push('host');\n }\n\n if (requiredFailed.length > 0) {\n return Promise.reject(\n new DependencyError(`Missing required options: ${requiredFailed.join(', ')}`)\n );\n }\n\n const credentialProvider = new EmbedTokenCredentialProvider(host, embedToken);\n const client = new SignalWire(credentialProvider);\n\n const call = await client.dial(to);\n\n // Destroy the client when the call ends to clean up WebSocket, timers, and session\n call.status$.pipe(first((status) => status === 'destroyed')).subscribe(() => {\n void client.disconnect();\n });\n\n return call;\n}\n","import type { CredentialProvider } from './interfaces';\nimport type { SDKCredential } from '../core/types/common.types';\n\n/**\n * Credential provider that returns a fixed set of credentials.\n *\n * Use when the token is already available (e.g. from a backend endpoint).\n *\n * @example\n * ```ts\n * const provider = new StaticCredentialProvider({ token: 'my-sat-token' });\n * const client = new SignalWire(provider);\n * ```\n */\nexport class StaticCredentialProvider implements CredentialProvider {\n constructor(private credentials: SDKCredential) {}\n\n /** Returns the static credentials. */\n public async authenticate(): Promise<SDKCredential> {\n return Promise.resolve(this.credentials);\n }\n}\n","/**\n * Public API entry point for @signalwire/js\n *\n * IMPORTANT: This file should NEVER be imported by internal modules.\n * Internal modules must import directly from source files.\n *\n * This file defines the minimal public API surface for external consumers.\n */\n\n// ============================================================================\n// Main Entry Point\n// ============================================================================\n\nexport { SignalWire } from './clients/SignalWire';\n\nexport { embeddableCall } from './utils/embeddableCall';\n\nexport { StaticCredentialProvider } from './dependencies/StaticCredentialProvider';\nexport type {\n CredentialProvider,\n AuthenticateContext,\n WebRTCApiProvider,\n WebRTCMediaDevices,\n Storage\n} from './dependencies/interfaces';\n// ============================================================================\n// Error Types (exported as values for instanceof checks)\n// ============================================================================\n\nexport {\n CallCreateError,\n CollectionFetchError,\n DeviceTokenError,\n DPoPInitError,\n InvalidCredentialsError,\n MediaTrackError,\n MessageParseError,\n TokenRefreshError,\n UnexpectedError,\n VertoPongError,\n RecoveryError,\n OverconstrainedFallbackError,\n PreflightError\n} from './core/errors';\nexport type { CallError, CallErrorKind } from './core/errors';\n\n// ============================================================================\n// Domain Model Classes (exported as values for instanceof and type guards)\n// ============================================================================\n\nexport { Address } from './core/entities/Address';\nexport { WebRTCCall } from './core/entities/Call';\nexport { Participant, SelfParticipant } from './core/entities/Participant';\nexport { Subscriber } from './core/entities/Subscriber';\nexport { SelfCapabilities } from './core/capabilities';\n\n// ============================================================================\n// Domain Model Interfaces\n// ============================================================================\n\nexport type { Directory } from './core/entities/Directory';\nexport type { SessionState } from './interfaces/SessionState';\n\n// ============================================================================\n// Type Guards (exported as values for runtime checks)\n// ============================================================================\n\nexport { isSelfParticipant } from './core/entities/Participant';\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nexport { ClientPreferences } from './containers/PreferencesContainer';\n\n// ============================================================================\n// Logging\n// ============================================================================\n\nexport { setLogger, setLogLevel, setDebugOptions } from './utils/logger';\nexport type { SDKLogger, LogLevel, DebugOptions } from './utils/logger';\n\n// ============================================================================\n// Essential Types (type-only exports)\n// ============================================================================\n\n// Client types\nexport type { SignalWireOptions, DialOptions } from './clients/SignalWire';\n\n// Call types\nexport type {\n Call,\n CallOptions,\n CallStatus,\n CallState,\n CallParticipant,\n CallSelfParticipant,\n CallAddress\n} from './core/entities/types/call.types';\n\n// Types used in Call interface methods\nexport type { TransferOptions, ScreenShareStatus } from './managers/types/verto-manager.types';\nexport type { VideoPosition, CallDirection, Capability } from './core/types/call.types';\nexport type {\n JSONRPCRequest,\n JSONRPCResponse,\n JSONRPCSuccessResponse,\n JSONRPCErrorResponse\n} from './core/RPCMessages/types/base';\nexport type { PendingRPCOptions } from './core/utils';\n\n// Types needed by web-components\nexport type { LayoutLayer } from './core/RPCMessages/types/common';\n\n// Media types\nexport type { MediaOptions, MediaDirections, MediaDirection } from './core/types/media.types';\n\n// Conversation types\nexport type { AddressHistory, TextMessage } from './core/types/conversation.types';\n\n// Participant types\nexport type { ExecuteMethod } from './core/entities/Participant';\nexport type { SelectDeviceOptions } from './core/entities/types/participant.types';\nexport type { SubscriberPresence } from './core/entities/Subscriber';\n\n// Common types (credentials, adapters)\nexport type { SDKCredential, WebSocketAdapter, NodeSocketAdapter } from './core/types/common.types';\n\n// DPoP types (for consumers inspecting subscriber.satClaims)\nexport type { SATClaims } from './core/types/crypto.types';\n\n// Capability types\nexport type {\n OnOffCapability,\n MemberCapabilities,\n CallCapabilitiesState\n} from './core/capabilities/types';\n\n// Device types\nexport type { DeviceController } from './interfaces/DeviceController';\n\n// Stats monitor types — backward-compatible aliases (now the same types as NetworkIssue/NetworkMetrics)\nexport type {\n NetworkIssue as CallNetworkIssue,\n NetworkMetrics as CallNetworkMetrics\n} from './controllers/RTCStatsMonitor';\n\n// Resilience types (NetworkIssue and NetworkMetrics are canonical re-exports from RTCStatsMonitor)\nexport type {\n NetworkIssue,\n NetworkMetrics,\n RecoveryEvent,\n RecoveryState,\n QualityLevel,\n DeviceRecoveryEvent,\n StoredDevicePreference,\n ConstraintFallbackEvent,\n PermissionResult,\n PlatformCapabilities,\n PreflightOptions,\n PreflightResult,\n AudioConstraintsEvent,\n MediaParamsEvent,\n SessionDiagnostics,\n DiagnosticEvent,\n CallDiagnosticSummary,\n ResilienceCallStatus\n} from './core/types/resilience.types';\n\n// ============================================================================\n// DO NOT EXPORT (Internal Implementation):\n// - Behaviors (Destroyable, Fetchable)\n// - Controllers (RTCPeerConnectionController, etc.)\n// - Managers (DirectoryManager, TransportManager, ConversationsManager, etc.)\n// - Containers (DependencyContainer, PreferencesContainer singleton)\n// - Internal utilities and helpers\n// - RPC Messages and protocol internals\n// ============================================================================\n\n// ============================================================================\n// Library Ready Event (for async/dynamic script loading)\n// ============================================================================\n\n/**\n * Library version from package.json, injected at build time.\n */\nexport const version: string = __VERSION__;\n\n/**\n * Flag indicating the library has been loaded and is ready to use.\n * For UMD builds: `window.SignalWire.ready`\n * For ES modules: `import { ready } from '@signalwire/js'`\n */\nexport const ready: boolean = true;\n\n/**\n * Emits 'signalwire:js:ready' event when the library is loaded.\n *\n * Scripts that might load BEFORE the library (check flag first):\n * ```js\n * if (window.SignalWire?.ready) {\n * // Library already loaded, use it directly\n * initApp();\n * } else {\n * window.addEventListener('signalwire:js:ready', () => initApp());\n * }\n * ```\n */\nconst emitReadyEvent = (): void => {\n if (typeof window !== 'undefined') {\n const event = new CustomEvent('signalwire:js:ready', {\n detail: { version: __VERSION__ }\n });\n window.dispatchEvent(event);\n }\n};\n\nemitReadyEvent();\n"],"mappings":"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"}