@signalwire/js 4.0.0-dev-20260910191529 → 4.0.0-dev-20260911135809

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1166,6 +1166,14 @@ interface CallError {
1166
1166
  readonly error: Error;
1167
1167
  /** ID of the call that produced this error. */
1168
1168
  readonly callId: string;
1169
+ /**
1170
+ * Which peer connection failed. Auxiliary legs are never fatal, so a consumer
1171
+ * can surface "screen share failed, call continues". Absent for call- and
1172
+ * session-level errors.
1173
+ */
1174
+ readonly leg?: RTCPeerConnectionPropose;
1175
+ /** `callId` is always the call's id, never the leg's. Use this for the leg. */
1176
+ readonly legId?: string;
1169
1177
  }
1170
1178
  declare class CallCreateError extends Error {
1171
1179
  message: string;
@@ -1194,6 +1202,15 @@ declare class CollectionFetchError extends Error {
1194
1202
  originalError: unknown;
1195
1203
  constructor(operation: string, originalError: unknown);
1196
1204
  }
1205
+ /**
1206
+ * An auxiliary leg did not connect within its budget. Typed rather than a bare
1207
+ * RxJS `TimeoutError` so the leg and cause survive.
1208
+ */
1209
+ declare class AuxiliaryLegTimeoutError extends Error {
1210
+ readonly leg: RTCPeerConnectionPropose;
1211
+ readonly originalError?: Error | undefined;
1212
+ constructor(leg: RTCPeerConnectionPropose, originalError?: Error | undefined);
1213
+ }
1197
1214
  declare class MediaTrackError extends Error {
1198
1215
  operation: string;
1199
1216
  kind: string;
@@ -1444,7 +1461,7 @@ interface AudioConstraintsEvent {
1444
1461
  /** Timestamp when the event occurred (epoch ms). */
1445
1462
  readonly timestamp: number;
1446
1463
  }
1447
- /** Event emitted when server-pushed media params are applied. */
1464
+ /** Event emitted when the server pushes media params. */
1448
1465
  interface MediaParamsEvent {
1449
1466
  /** Audio constraints pushed by the server, if any. */
1450
1467
  readonly audio?: MediaTrackConstraints;
@@ -1452,6 +1469,15 @@ interface MediaParamsEvent {
1452
1469
  readonly video?: MediaTrackConstraints;
1453
1470
  /** Timestamp when the event occurred (epoch ms). */
1454
1471
  readonly timestamp: number;
1472
+ /**
1473
+ * `false` when the constraints did not reach every sender of a pushed kind:
1474
+ * the sender carries media the SDK did not capture, the browser refused the
1475
+ * constraints and re-acquisition failed, or the leg sends no media of that
1476
+ * kind at all. The event is emitted whether or not they were applied, but a
1477
+ * push naming a leg this call does not hold emits nothing at all — the event
1478
+ * carries no leg identity to report it against.
1479
+ */
1480
+ readonly applied: boolean;
1455
1481
  }
1456
1482
  /** Structured diagnostic bundle for a session. */
1457
1483
  interface SessionDiagnostics {
@@ -2081,6 +2107,9 @@ declare class SelfParticipant extends Participant implements CallSelfParticipant
2081
2107
  *
2082
2108
  * The call is unaffected when acquisition fails.
2083
2109
  *
2110
+ * Known issue: stopping the share before its leg connects can leave this
2111
+ * promise pending until the call ends, so do not gate UI on it.
2112
+ *
2084
2113
  * @throws The raw `getDisplayMedia` error. A dismissed picker or a
2085
2114
  * permission denial rejects with a `NotAllowedError` `DOMException` —
2086
2115
  * inspect `error.name` to tell benign cancels apart from real failures.
@@ -2097,8 +2126,12 @@ declare class SelfParticipant extends Participant implements CallSelfParticipant
2097
2126
  *
2098
2127
  * The call is unaffected when acquisition fails.
2099
2128
  *
2129
+ * Known issue: removing the device before its leg connects can leave this
2130
+ * promise pending until the call ends, so do not gate UI on it.
2131
+ *
2100
2132
  * @throws The raw `getUserMedia` error (e.g. `NotAllowedError` on
2101
- * permission denial) — inspect `error.name` to decide how to react.
2133
+ * permission denial) — inspect `error.name` to decide how to react — or
2134
+ * `AuxiliaryLegTimeoutError` if the leg does not connect in time.
2102
2135
  */
2103
2136
  addAdditionalDevice(options: MediaOptions): Promise<void>;
2104
2137
  /** Removes an additional media input device by ID. */
@@ -2896,56 +2929,6 @@ interface NetworkChangeEvent {
2896
2929
  networkType?: string;
2897
2930
  }
2898
2931
  //#endregion
2899
- //#region src/core/entities/Directory.d.ts
2900
- /**
2901
- * Directory interface for managing addresses
2902
- *
2903
- * This is the public API contract for address directory functionality.
2904
- * It provides access to addresses, loading capabilities, and search functionality.
2905
- *
2906
- * @public
2907
- */
2908
- interface Directory extends AddressProvider<Address> {
2909
- /**
2910
- * Observable stream of all addresses in the directory
2911
- * Emits a new array whenever addresses are added, removed, or updated
2912
- */
2913
- readonly addresses$: Observable<Address[]>;
2914
- /**
2915
- * Current snapshot of all addresses in the directory
2916
- */
2917
- readonly addresses: Address[];
2918
- /**
2919
- * Observable indicating whether more addresses can be loaded from the server
2920
- */
2921
- readonly hasMore$: Observable<boolean>;
2922
- /**
2923
- * Observable indicating the current loading state
2924
- * Emits `true` when loading, `false` when idle
2925
- */
2926
- readonly loading$: Observable<boolean>;
2927
- readonly loading: boolean;
2928
- /**
2929
- * Load more addresses from the server
2930
- * Only loads if `hasMore` is true
2931
- */
2932
- loadMore(): void;
2933
- /**
2934
- * Get a specific address by ID
2935
- *
2936
- * @param addressId - The address ID to retrieve
2937
- * @returns The address instance, or undefined if not found
2938
- */
2939
- get(addressId: string): Address | undefined;
2940
- /**
2941
- * Find an address ID by searching for a name
2942
- *
2943
- * @param uri - The address name to search for
2944
- * @returns Promise resolving to the address ID, or undefined if not found
2945
- */
2946
- findAddressIdByURI(uri: string): Promise<string | undefined>;
2947
- }
2948
- //#endregion
2949
2932
  //#region src/interfaces/ClientSession.d.ts
2950
2933
  /**
2951
2934
  * Minimal interface for what Call needs from session management
@@ -2986,2277 +2969,2361 @@ interface ClientSession {
2986
2969
  readonly callControl: 'routed' | 'in-dialog';
2987
2970
  }
2988
2971
  //#endregion
2989
- //#region src/interfaces/SessionState.d.ts
2972
+ //#region src/controllers/LocalAudioPipeline.d.ts
2990
2973
  /**
2991
- * Extended session interface that adds call management and authentication
2992
- * state on top of the narrow ClientSession contract.
2974
+ * Options for {@link LocalAudioPipeline}.
2975
+ */
2976
+ interface LocalAudioPipelineOptions {
2977
+ /** Factory for AudioContext — override for tests. Defaults to `new AudioContext()`. */
2978
+ audioContextFactory?: () => AudioContext;
2979
+ /** Initial gain (0..2, where 1 is unity). Defaults to 1. */
2980
+ initialGain?: number;
2981
+ /** RMS level [0..1] above which speaking$ emits true. Defaults to {@link VAD_THRESHOLD}. */
2982
+ speakingThreshold?: number;
2983
+ /**
2984
+ * Milliseconds of silence below the threshold before speaking$ flips back to
2985
+ * false. Prevents flicker on normal speech gaps. Defaults to {@link VAD_HOLD_MS}.
2986
+ */
2987
+ speakingHoldMs?: number;
2988
+ /** Polling interval for level$. Defaults to {@link AUDIO_LEVEL_POLL_INTERVAL_MS}. */
2989
+ pollIntervalMs?: number;
2990
+ }
2991
+ /**
2992
+ * Web Audio pipeline for the local microphone stream.
2993
2993
  *
2994
- * Accessible via `client.session`. Call and CallFactory continue to depend
2995
- * only on the narrow ClientSession interface.
2994
+ * Wraps the raw mic `MediaStreamTrack` in a graph of:
2995
+ *
2996
+ * ```
2997
+ * MediaStreamAudioSourceNode → GainNode → AnalyserNode → MediaStreamAudioDestinationNode
2998
+ * ```
2999
+ *
3000
+ * The {@link outputTrack} from the destination node is what callers should
3001
+ * attach to the `RTCRtpSender` in place of the raw mic track. The same
3002
+ * destination track is reused across input changes (device switch, mute /
3003
+ * unmute track replacement) so the sender reference stays stable — only the
3004
+ * source end of the graph is rebuilt.
3005
+ *
3006
+ * The pipeline owns a single {@link AudioContext}. Callers must invoke
3007
+ * {@link destroy} to release it when the call ends.
2996
3008
  */
2997
- interface SessionState extends ClientSession {
3009
+ declare class LocalAudioPipeline extends Destroyable {
3010
+ private readonly _audioContext;
3011
+ private readonly _gainNode;
3012
+ private readonly _analyser;
3013
+ private readonly _destination;
3014
+ private readonly _analyserBuffer;
3015
+ private readonly _speakingThreshold;
3016
+ private readonly _speakingHoldMs;
3017
+ private readonly _pollIntervalMs;
3018
+ private _inputSource;
3019
+ private _inputStream;
3020
+ private _lastSpokeAt;
3021
+ private _gain$;
3022
+ /** 1 when audio should pass through, 0 when silenced by PTT. */
3023
+ private _pttMultiplier;
3024
+ constructor(options?: LocalAudioPipelineOptions);
3025
+ /** Observable of the current gain value (0..2). */
3026
+ get gain$(): Observable<number>;
3027
+ /** Current gain value (0..2). */
3028
+ get gain(): number;
2998
3029
  /**
2999
- * Observable stream of currently active inbound calls.
3000
- * Filters `calls$` to only include calls with `direction === 'inbound'`.
3030
+ * Processed output track to attach to the RTCRtpSender. Stable reference
3031
+ * across input changes, so `sender.replaceTrack(pipeline.outputTrack)` only
3032
+ * needs to be called once.
3001
3033
  */
3002
- readonly incomingCalls$: Observable<Call[]>;
3034
+ get outputTrack(): MediaStreamTrack;
3003
3035
  /**
3004
- * Current snapshot of active inbound calls.
3036
+ * Root-mean-square audio level of the input signal, 0..1. Emits on a fixed
3037
+ * interval (~30fps by default).
3005
3038
  */
3006
- readonly incomingCalls: Call[];
3039
+ get level$(): Observable<number>;
3007
3040
  /**
3008
- * Observable stream of all currently active calls (both inbound and outbound).
3041
+ * Boolean VAD derived from {@link level$}. True while level ≥ threshold or
3042
+ * during the hold window after the last frame that crossed the threshold.
3009
3043
  */
3010
- readonly calls$: Observable<Call[]>;
3044
+ get speaking$(): Observable<boolean>;
3011
3045
  /**
3012
- * Current snapshot of all active calls.
3046
+ * Set gain multiplier applied to the input signal. 0 = silence,
3047
+ * 1 = unity, 2 = 2x. Values are clamped to [0, 2]. The effective gain on
3048
+ * the graph also respects the current PTT state.
3013
3049
  */
3014
- readonly calls: Call[];
3050
+ setGain(value: number): void;
3015
3051
  /**
3016
- * Observable that emits `true` once the session has been authenticated,
3017
- * and `false` after disconnect.
3052
+ * Silence the graph when `active = false`, otherwise restore the configured
3053
+ * gain. Use this from a PTT handler: released → `false`, held → `true`.
3054
+ * Orthogonal to {@link setGain} — once PTT returns to active, the last
3055
+ * configured gain reappears.
3018
3056
  */
3019
- readonly authenticated$: Observable<boolean>;
3057
+ setPTTActive(active: boolean): void;
3058
+ private applyEffectiveGain;
3020
3059
  /**
3021
- * Current authentication state.
3022
- * Returns `true` if the session is currently authenticated.
3060
+ * Wire a new raw mic track as the pipeline's input. Replaces any previous
3061
+ * input source and reconnects the graph so {@link outputTrack} continues
3062
+ * to emit the processed audio. Pass `null` to disconnect the input (the
3063
+ * output track stays alive but emits silence).
3064
+ *
3065
+ * Also resumes the underlying AudioContext on attach — Chrome creates it
3066
+ * in a suspended state and the graph won't process (the destination
3067
+ * track emits silence) until resume() succeeds.
3023
3068
  */
3024
- readonly authenticated: boolean;
3069
+ setInputTrack(track: MediaStreamTrack | null): void;
3070
+ destroy(): void;
3071
+ private computeLevel;
3072
+ private evaluateSpeaking;
3025
3073
  }
3026
3074
  //#endregion
3027
- //#region src/managers/ClientSessionManager.d.ts
3028
- /**
3029
- * Discriminated union for session authentication state.
3030
- * clientBound is tracked separately via _wasClientBound (sticky flag)
3031
- * to avoid dual sources of truth.
3032
- */
3033
- type SessionAuthState = {
3034
- kind: 'unauthenticated';
3035
- } | {
3036
- kind: 'authenticated';
3037
- };
3038
- declare class ClientSessionManager extends Destroyable implements SessionState {
3039
- private readonly getCredential;
3040
- private readonly transport;
3041
- private readonly storage;
3042
- private readonly authorizationStateKey;
3043
- private readonly attachManager;
3044
- private readonly dpopManager?;
3045
- private callFactory;
3046
- private callCreateTimeout;
3047
- private readonly agent;
3048
- private readonly eventAcks;
3049
- initialized$: Observable<boolean>;
3050
- private authorizationState$;
3051
- private connectVersion;
3052
- /**
3053
- * Optional hook called before a fresh connect on reconnect.
3054
- * Used by SignalWire to refresh expired credentials before re-authenticating.
3055
- * @internal
3056
- */
3057
- onBeforeReconnect?: () => Promise<void>;
3075
+ //#region src/controllers/RTCPeerConnectionController.d.ts
3076
+ interface RTCPeerConnectionControllerOptions extends MediaOptions {
3077
+ callId?: string;
3078
+ rtcConfiguration?: RTCConfiguration;
3079
+ simulcast?: boolean;
3080
+ sfu?: boolean;
3081
+ msStreamsNumber?: number;
3082
+ propose: RTCPeerConnectionPropose;
3083
+ iceServers?: RTCIceServer[];
3084
+ disableUdpIceServers?: boolean;
3085
+ relayOnly?: boolean;
3086
+ iceCandidateTimeout?: number;
3087
+ iceGatheringTimeout?: number;
3088
+ webRTCApiProvider?: WebRTCApiProvider;
3089
+ /** Per-call preferred video codecs (overrides global preferences). */
3090
+ preferredVideoCodecs?: string[];
3091
+ /** Per-call preferred audio codecs (overrides global preferences). */
3092
+ preferredAudioCodecs?: string[];
3093
+ /** Per-call stereo Opus setting (overrides global preferences). */
3094
+ stereo?: boolean;
3095
+ }
3096
+ type RTCPeerConnectionControllerOptionsPartial = Partial<RTCPeerConnectionControllerOptions>;
3097
+ interface UpdateSDPStatusParams {
3098
+ status: 'received' | 'sent' | 'failed';
3099
+ sdp?: string;
3100
+ }
3101
+ declare class RTCPeerConnectionController extends Destroyable {
3102
+ protected options: RTCPeerConnectionControllerOptionsPartial;
3103
+ readonly id: string;
3104
+ firstSDPExchangeCompleted: boolean;
3105
+ sdpInit?: RTCSessionDescriptionInit;
3106
+ private negotiationNeeded$;
3107
+ private deviceController;
3108
+ private localStreamController;
3109
+ private transceiverController?;
3110
+ readonly localDescription$: Observable<RTCSessionDescription | null>;
3111
+ peerConnection?: RTCPeerConnection;
3112
+ private initPromise?;
3113
+ private connectionTimeout;
3114
+ private connectionTimer?;
3115
+ private oniceconnectionstatechangeHandler;
3116
+ private onconnectionstatechangeHandler;
3117
+ private onsignalingstatechangeHandler;
3118
+ private onicegatheringstatechangeHandler;
3119
+ private onnegotiationneededHandler;
3120
+ private updateSelectedInputDevice;
3121
+ private _isNegotiating$;
3122
+ private _iceGatheringController?;
3123
+ private _memberId;
3124
+ private _type;
3125
+ private _iceConnectionState$;
3126
+ private _connectionState$;
3127
+ private _signalingState$;
3128
+ private _iceGatheringState$;
3129
+ private _errors$;
3130
+ private _iceCandidates$;
3058
3131
  /**
3059
- * Session-wide call control transport (see {@link ClientSession.callControl}).
3060
- * Set from {@link SignalWireOptions.callControl} by SignalWire after construction;
3061
- * defaults to `'routed'`. Read by every Call, so it needs no per-call persistence.
3132
+ * Emits once local media is settled: acquired, intentionally receive-only, or
3133
+ * failed and degraded. Separates the media and signalling phases of call
3134
+ * creation. Not `localStream$` — the receive-only paths never build a stream,
3135
+ * so that would hang exactly the calls with nothing to acquire.
3062
3136
  */
3063
- callControl: 'routed' | 'in-dialog';
3064
- private _authorization$;
3065
- private _errors$;
3066
- private _directory?;
3067
- private _authState$;
3068
- /** Sticky flag — once true, stays true for the session lifetime. */
3069
- private _wasClientBound;
3070
- private _userInfo$;
3071
- private _calls$;
3072
- private _iceServers$;
3073
- constructor(getCredential: () => SDKCredential, transport: TransportManager, storage: StorageManager, authorizationStateKey: string, deviceController: DeviceController, attachManager: AttachManager, webRTCApiProvider: WebRTCApiProvider, dpopManager?: CryptoController | undefined, networkChange$?: Observable<NetworkChangeEvent>);
3074
- get incomingCalls$(): Observable<Call[]>;
3075
- get incomingCalls(): Call[];
3076
- get userInfo$(): Observable<Address | null>;
3077
- get userInfo(): Address | null;
3078
- get calls$(): Observable<Call[]>;
3079
- get calls(): Call[];
3080
- get iceServers(): RTCIceServer[] | undefined;
3081
- get authorization$(): Observable<Authorization | undefined>;
3082
- get authorization(): Authorization | undefined;
3137
+ private _localMediaSettled$;
3138
+ private _initialized$;
3139
+ private _remoteDescription$;
3140
+ private _remoteStream$;
3141
+ private _remoteOfferMediaDirections;
3142
+ private _localAudioPipeline;
3143
+ constructor(options?: RTCPeerConnectionControllerOptionsPartial, remoteSessionDescription?: string, deviceController?: DeviceController);
3144
+ private get iceGatheringController();
3145
+ private get shouldEmitLocalDescription();
3146
+ private removeConnectionTimer;
3147
+ setMemberId(memberId: string | null): void;
3148
+ get memberId(): string | null;
3149
+ stopTrackSender(kind: 'audio' | 'video' | 'both', options?: {
3150
+ updateTransceiverDirection: boolean;
3151
+ }): void;
3152
+ private stopRawAudioInputForPipeline;
3153
+ get isNegotiating$(): Observable<boolean>;
3154
+ get isNegotiating(): boolean;
3155
+ updateMediaDevicesOptions(options: MediaOptions): void;
3156
+ get iceGatheringState$(): Observable<RTCIceGatheringState>;
3157
+ get mediaTrackEnded$(): Observable<MediaStreamTrack>;
3083
3158
  get errors$(): Observable<Error>;
3084
- get authenticated$(): Observable<boolean>;
3085
- get authenticated(): boolean;
3159
+ get iceCandidates$(): Observable<RTCIceCandidate[]>;
3160
+ get initialized$(): Observable<boolean>;
3161
+ get remoteDescription$(): Observable<RTCSessionDescription | null>;
3162
+ /** Emits once local media is settled — acquired, or knowingly receive-only. */
3163
+ get localMediaSettled$(): Observable<void>;
3164
+ get localStream$(): Observable<MediaStream | null>;
3165
+ get remoteStream$(): Observable<MediaStream | null>;
3166
+ get localAudioTracks$(): Observable<MediaStreamTrack[]>;
3167
+ get localVideoTracks$(): Observable<MediaStreamTrack[]>;
3168
+ get iceConnectionState$(): Observable<RTCIceConnectionState>;
3169
+ get connectionState$(): Observable<RTCPeerConnectionState>;
3170
+ get signalingState$(): Observable<RTCSignalingState>;
3171
+ get type(): RTCPeerConnectionType;
3172
+ get propose(): RTCPeerConnectionPropose;
3173
+ get isAdditionalDevice(): boolean;
3174
+ get isMainDevice(): boolean;
3175
+ get isScreenShare(): boolean;
3176
+ protected get iceServers(): RTCIceServer[];
3177
+ private get rtcConfiguration();
3178
+ get receiveVideo(): boolean;
3179
+ get receiveAudio(): boolean;
3180
+ get localStream(): MediaStream | null;
3181
+ get remoteStream(): MediaStream | null;
3182
+ private get inputAudioDeviceConstraints();
3183
+ private get inputVideoDeviceConstraints();
3184
+ private get WebRTCPeerConnectionConstructor();
3185
+ private get offerOptions();
3186
+ private get answerOptions();
3086
3187
  /**
3087
- * Whether this session is client-bound (using a Client Bound SAT).
3088
- * When client-bound, DPoP proof creation failures are treated as
3089
- * authentication errors rather than silently degraded.
3090
- * @internal
3188
+ * Initialize the RTCPeerConnection and setup event listeners.
3189
+ * Called automatically when localDescription$ is subscribed to (deferred pattern).
3190
+ * Uses Promise memoization to ensure initialization only happens once,
3191
+ * even if called concurrently.
3091
3192
  */
3092
- get clientBound(): boolean;
3093
- /** @internal Current auth state for debugging/testing. */
3094
- get authState(): SessionAuthState;
3193
+ private init;
3095
3194
  /**
3096
- * Set the directory instance
3097
- * Called by SignalWire after directory is created
3098
- * @internal
3195
+ * Internal initialization implementation.
3196
+ * Should only be called via init() to ensure single execution.
3099
3197
  */
3100
- setDirectory(directory: Directory): void;
3101
- execute<T extends JSONRPCResponse = JSONRPCResponse>(request: JSONRPCRequest, options?: PendingRPCOptions): Promise<T>;
3102
- send(message: JSONSerializable): void;
3103
- private init;
3104
- private setupMessageHandlers;
3105
- private loadAuthorizationStateFromStorage;
3106
- private updateAuthorizationStateInStorage;
3107
- private get authStateEvent$();
3108
- get signalingEvent$(): Observable<(Omit<{
3109
- event_type: "webrtc.message";
3110
- event_channel: EventChannel;
3111
- timestamp: number;
3112
- project_id?: string;
3113
- node_id?: string;
3114
- is_author?: boolean;
3115
- params: WebrtcMessagePayload;
3116
- }, "event_channel" | "project_id" | "node_id"> & {
3117
- event_channel: string;
3118
- project_id: string;
3119
- node_id: string;
3120
- }) | {
3121
- event_type: "signalwire.authorization.state";
3122
- params: SignalwireAuthorizationStatePayload;
3123
- } | (Omit<{
3124
- event_type: "call.joined";
3125
- event_channel: EventChannel;
3126
- timestamp: number;
3127
- project_id?: string;
3128
- node_id?: string;
3129
- is_author?: boolean;
3130
- params: CallJoinedPayload;
3131
- }, "event_channel"> & {
3132
- event_channel: string;
3133
- }) | (Omit<{
3134
- event_type: "call.left";
3135
- event_channel: EventChannel;
3136
- timestamp: number;
3137
- project_id?: string;
3138
- node_id?: string;
3139
- is_author?: boolean;
3140
- params: CallLeftPayload;
3141
- }, "event_channel"> & {
3142
- event_channel: string;
3143
- }) | (Omit<{
3144
- event_type: "call.updated";
3145
- event_channel: EventChannel;
3146
- timestamp: number;
3147
- project_id?: string;
3148
- node_id?: string;
3149
- is_author?: boolean;
3150
- params: CallUpdatedPayload;
3151
- }, "event_channel"> & {
3152
- event_channel: string;
3153
- }) | (Omit<{
3154
- event_type: "call.state";
3155
- event_channel: EventChannel;
3156
- timestamp: number;
3157
- project_id?: string;
3158
- node_id?: string;
3159
- is_author?: boolean;
3160
- params: CallStatePayload;
3161
- }, "event_channel"> & {
3162
- event_channel: string;
3163
- }) | (Omit<{
3164
- event_type: "call.play";
3165
- event_channel: EventChannel;
3166
- timestamp: number;
3167
- project_id?: string;
3168
- node_id?: string;
3169
- is_author?: boolean;
3170
- params: CallPlayPayload;
3171
- }, "event_channel"> & {
3172
- event_channel: string;
3173
- }) | (Omit<{
3174
- event_type: "call.connect";
3175
- event_channel: EventChannel;
3176
- timestamp: number;
3177
- project_id?: string;
3178
- node_id?: string;
3179
- is_author?: boolean;
3180
- params: CallConnectPayload;
3181
- }, "event_channel"> & {
3182
- event_channel: string;
3183
- }) | (Omit<{
3184
- event_type: "room.updated";
3185
- event_channel: EventChannel;
3186
- timestamp: number;
3187
- project_id?: string;
3188
- node_id?: string;
3189
- is_author?: boolean;
3190
- params: RoomUpdatedPayload;
3191
- }, "event_channel"> & {
3192
- event_channel: string;
3193
- }) | Omit<{
3194
- event_type: "member.updated";
3195
- event_channel: EventChannel;
3196
- timestamp: number;
3197
- project_id?: string;
3198
- node_id?: string;
3199
- is_author?: boolean;
3200
- params: MemberUpdatedPayload;
3201
- }, never> | Omit<{
3202
- event_type: "member.joined";
3203
- event_channel: EventChannel;
3204
- timestamp: number;
3205
- project_id?: string;
3206
- node_id?: string;
3207
- is_author?: boolean;
3208
- params: MemberJoinedPayload;
3209
- }, never> | Omit<{
3210
- event_type: "member.left";
3211
- event_channel: EventChannel;
3212
- timestamp: number;
3213
- project_id?: string;
3214
- node_id?: string;
3215
- is_author?: boolean;
3216
- params: MemberLeftPayload;
3217
- }, never> | Omit<{
3218
- event_type: "member.talking";
3219
- event_channel: EventChannel;
3220
- timestamp: number;
3221
- project_id?: string;
3222
- node_id?: string;
3223
- is_author?: boolean;
3224
- params: MemberTalkingPayload;
3225
- }, never> | Omit<{
3226
- event_type: "layout.changed";
3227
- event_channel: EventChannel;
3228
- timestamp: number;
3229
- project_id?: string;
3230
- node_id?: string;
3231
- is_author?: boolean;
3232
- params: LayoutChangedPayload;
3233
- }, never> | (Omit<{
3234
- event_type: "conversation.message";
3235
- event_channel: EventChannel;
3236
- timestamp: number;
3237
- project_id?: string;
3238
- node_id?: string;
3239
- is_author?: boolean;
3240
- params: ConversationMessagePayload;
3241
- }, "event_channel" | "timestamp" | "is_author"> & {
3242
- event_channel: string;
3243
- timestamp: string;
3244
- is_author: boolean;
3245
- }) | (Omit<{
3246
- event_type: "conversation.message.updated";
3247
- event_channel: EventChannel;
3248
- timestamp: number;
3249
- project_id?: string;
3250
- node_id?: string;
3251
- is_author?: boolean;
3252
- params: ConversationMessagePayload;
3253
- }, "event_channel" | "timestamp" | "is_author"> & {
3254
- event_channel: string;
3255
- timestamp: string;
3256
- is_author: boolean;
3257
- })>;
3258
- private get vertoInvite$();
3259
- private get vertoAttach$();
3260
- private get contexts();
3261
- private get eventing();
3262
- private get topics();
3263
- private get authentication();
3264
- connect(): Promise<void>;
3265
- private handleAuthenticationError;
3198
+ private doInit;
3199
+ private setupPeerConnection;
3200
+ private startNegotiation;
3266
3201
  /**
3267
- * Clear the resume state (authorization_state + protocol) and ask the
3268
- * transport to reconnect. The `connected` event re-triggers
3269
- * `authenticate()`, which now has no stored state and so performs a fresh
3270
- * connect.
3271
- *
3272
- * This is the stale-auth-state recovery helper used by handleAuthError:
3273
- * the server rejected a reconnect, so the resume state is discarded and a
3274
- * fresh connect follows. Attach records are deliberately preserved — the
3275
- * session lives on through the reconnect and reattachCalls() needs the
3276
- * stored call references afterwards. Do NOT add detachAll() here.
3202
+ * Create an SDP offer and set it as local description.
3203
+ */
3204
+ private createOffer;
3205
+ updateAnswerStatus({
3206
+ status,
3207
+ sdp
3208
+ }: UpdateSDPStatusParams): Promise<void>;
3209
+ updateOfferStatus({
3210
+ status,
3211
+ sdp
3212
+ }: UpdateSDPStatusParams): Promise<void>;
3213
+ /**
3214
+ * Accept an inbound call by creating the SDP answer.
3215
+ * Optionally override media options before the answer is generated.
3216
+ * Must be called after initialization for inbound (answer-type) connections.
3217
+ */
3218
+ acceptInbound(mediaOverrides?: MediaOptions): Promise<void>;
3219
+ private handleOfferReceived;
3220
+ private readyToConnect;
3221
+ private setRemoteDescriptionBefore;
3222
+ protected setLocalDescription(params: RTCSessionDescriptionInit): Promise<void>;
3223
+ setLocalDescriptionBefore(sdp?: string): Promise<string>;
3224
+ /**
3225
+ * Create an SDP answer and set it as local description.
3226
+ */
3227
+ private createAnswer;
3228
+ /**
3229
+ * Setup event listeners on RTCPeerConnection for state changes.
3230
+ */
3231
+ private setupEventListeners;
3232
+ private negotiationEnded;
3233
+ /**
3234
+ * Trigger an ICE restart through the existing negotiation pipeline.
3277
3235
  *
3278
- * Connect-time recovery only. A *request* refused on an already
3279
- * authenticated session is never healed here: dropping the resume state
3280
- * destroys the association between the socket and the previous session,
3281
- * which is what reattach depends on. That path mints a fresh credential and
3282
- * reauthenticates instead (see `SignalWire.recoverAndRetry`).
3236
+ * This creates an offer with iceRestart: true and goes through the full
3237
+ * SDP pipeline (setLocalDescription → ICE gathering → localDescription$ emission).
3238
+ * The caller should NOT send the SDP manually — the existing
3239
+ * setupLocalDescriptionHandler in VertoManager will pick up the emission
3240
+ * from localDescription$ and send it as a verto.modify.
3283
3241
  *
3284
- * For public teardown (disconnect/destroy), use {@link teardownSessionState}
3285
- * instead, which clears the attach records as well.
3242
+ * Unlike calling pc.createOffer/setLocalDescription directly, this method:
3243
+ * - Sets _isNegotiating$ so ICEGatheringController arms its timers
3244
+ * - Waits for ICE gathering to complete before localDescription$ emits
3245
+ * - Goes through setLocalDescriptionBefore() for any SDP munging
3286
3246
  */
3287
- private discardResumeStateAndReconnect;
3288
- cleanupStoredConnectionParams(): Promise<void>;
3247
+ triggerIceRestart(relayOnly?: boolean): Promise<void>;
3248
+ private restoreIceTransportPolicy;
3289
3249
  /**
3290
- * Public-teardown helper for disconnect()/destroy(). Clears the resume
3291
- * state (authorization_state + protocol) AND the attach records as one
3292
- * atomic unit.
3293
- *
3294
- * The two stores are coupled: the backend only honors attach records
3295
- * within the session identified by the resume state, so ending the
3296
- * session must clear both. Clearing one without the other strands records
3297
- * no future session can honor (disconnect) or revives a session the
3298
- * developer explicitly ended (destroy).
3250
+ * Setup track handling for remote tracks.
3299
3251
  *
3300
- * Distinct from {@link cleanupStoredConnectionParams}, which keeps the
3301
- * attach records for the stale-auth-state recovery path.
3252
+ * @returns `false` when the connection went away while local media was being
3253
+ * acquired — see {@link setupLocalTracks}.
3302
3254
  */
3303
- teardownSessionState(): Promise<void>;
3304
- protected updateAuthState(authorization_state: string): Promise<void>;
3305
- reauthenticate(token: string, dpopToken?: string, options?: {
3306
- clientBound?: boolean;
3307
- }): Promise<void>;
3308
- private authenticate;
3309
- disconnect(): Promise<void>;
3310
- private createInboundCall;
3255
+ private setupTrackHandling;
3311
3256
  /**
3312
- * Handle a server-pushed verto.attach event at the session level.
3257
+ * @returns `false` when the connection was torn down while getUserMedia was
3258
+ * in flight. The acquisition is not cancellable, so the caller must stop
3259
+ * rather than go on to touch a peer connection that is closed or gone.
3260
+ */
3261
+ private setupLocalTracks;
3262
+ /** True for a main connection with no local media to send. */
3263
+ private hasNoLocalMediaToSend;
3264
+ /** The media kinds this connection wants to send: 'audiovideo' | 'video' | 'audio'. */
3265
+ private get requestedMediaKinds();
3266
+ /**
3267
+ * Handle a local media acquisition failure with a typed, semantically
3268
+ * accurate MediaAccessError created at the acquisition site:
3269
+ * - Auxiliary connections (screenshare / additional-device) throw a
3270
+ * non-fatal error — VertoManager surfaces it and the call is unaffected.
3271
+ * - The main connection degrades to receive-only when allowed (default),
3272
+ * otherwise fails with a fatal error.
3273
+ */
3274
+ private handleLocalMediaFailure;
3275
+ /**
3276
+ * Negotiate receive-only m-lines when there are no local tracks to send.
3277
+ * Only offer-type connections add transceivers — answer-type connections
3278
+ * reuse the transceivers created from the remote offer.
3279
+ */
3280
+ private setupReceiveOnlyTransceivers;
3281
+ private getUserMedia;
3282
+ private getDisplayMedia;
3283
+ private setupRemoteTracks;
3284
+ restoreTrackSender(kind: 'audio' | 'video' | 'both'): Promise<void>;
3285
+ private restoreRawAudioInputForPipeline;
3286
+ /**
3287
+ * Return the lazily-created {@link LocalAudioPipeline}, constructing it on
3288
+ * first access. On creation the current audio sender's track is routed
3289
+ * through the pipeline (input → gain → analyser → destination) and the
3290
+ * sender is switched to emit the processed track. Returns `null` when no
3291
+ * audio sender exists yet (pre-negotiation).
3292
+ */
3293
+ ensureLocalAudioPipeline(): LocalAudioPipeline | null;
3294
+ /** The active LocalAudioPipeline, or null if it hasn't been created yet. */
3295
+ get localAudioPipeline(): LocalAudioPipeline | null;
3296
+ private applyLocalAudioPipelineToSender;
3297
+ /**
3298
+ * Add a local media track to the peer connection.
3299
+ * @param track - The MediaStreamTrack to add
3300
+ */
3301
+ addLocalTrack(track: MediaStreamTrack): void;
3302
+ /**
3303
+ * Remove a local media track from the peer connection.
3304
+ * @param trackId - The ID of the track to remove
3305
+ */
3306
+ removeLocalTrack(trackId: string): void;
3307
+ /**
3308
+ * Replace all existing media tracks with a new media track.
3309
+ * Convenience method for single-track scenarios.
3310
+ * @param track - The MediaStreamTrack to set
3311
+ */
3312
+ setLocalTrack(track: MediaStreamTrack): void;
3313
+ /**
3314
+ * @returns whether the constraints reached the media the leg is sending.
3313
3315
  *
3314
- * On page reload the server detects the reconnected session and pushes
3315
- * verto.attach for any active calls. If a call object already exists
3316
- * (network blip, no reload), the per-call handler in VertoManager deals
3317
- * with it. This method only creates a new call object when no existing
3318
- * one matches the callID.
3316
+ * With the pipeline engaged the audio sender carries the processed
3317
+ * destination track, so the sender scan would find nothing it may touch and
3318
+ * every audio constraint API would silently no-op. The constraints belong to
3319
+ * the pipeline's device source, which is the capture that sender ultimately
3320
+ * carries.
3319
3321
  */
3320
- private handleVertoAttach;
3321
- createOutboundCall(destination: string | Address, options?: CallOptions): Promise<Call>;
3322
- private createCall;
3323
- destroy(): void;
3324
- }
3325
- declare class ClientSessionWrapper implements SessionState {
3326
- private clientSessionManager;
3327
- constructor(clientSessionManager: ClientSessionManager);
3328
- get authenticated$(): Observable<boolean>;
3329
- get authenticated(): boolean;
3322
+ updateSendersConstraints(kind: 'audio' | 'video', constraints?: MediaTrackConstraints): Promise<boolean>;
3330
3323
  /**
3331
- * Whether the session is using a Client Bound SAT (DPoP). Sticky — set
3332
- * when the binding is established or restored from a resumed session's
3333
- * server authorization.
3324
+ * Mirror of the sender path for a piped audio leg: same merge, same fallback
3325
+ * ladder, same device-capture invariant — but the swap target is the pipeline
3326
+ * input, so the sender keeps emitting the pipeline's output track and its
3327
+ * identity survives the change.
3334
3328
  */
3335
- get clientBound(): boolean;
3336
- get signalingEvent$(): Observable<(Omit<{
3337
- event_type: "webrtc.message";
3338
- event_channel: EventChannel;
3339
- timestamp: number;
3340
- project_id?: string;
3341
- node_id?: string;
3342
- is_author?: boolean;
3343
- params: WebrtcMessagePayload;
3344
- }, "event_channel" | "project_id" | "node_id"> & {
3345
- event_channel: string;
3346
- project_id: string;
3347
- node_id: string;
3348
- }) | {
3349
- event_type: "signalwire.authorization.state";
3350
- params: SignalwireAuthorizationStatePayload;
3351
- } | (Omit<{
3352
- event_type: "call.joined";
3353
- event_channel: EventChannel;
3354
- timestamp: number;
3355
- project_id?: string;
3356
- node_id?: string;
3357
- is_author?: boolean;
3358
- params: CallJoinedPayload;
3359
- }, "event_channel"> & {
3360
- event_channel: string;
3361
- }) | (Omit<{
3362
- event_type: "call.left";
3363
- event_channel: EventChannel;
3364
- timestamp: number;
3365
- project_id?: string;
3366
- node_id?: string;
3367
- is_author?: boolean;
3368
- params: CallLeftPayload;
3369
- }, "event_channel"> & {
3370
- event_channel: string;
3371
- }) | (Omit<{
3372
- event_type: "call.updated";
3373
- event_channel: EventChannel;
3374
- timestamp: number;
3375
- project_id?: string;
3376
- node_id?: string;
3377
- is_author?: boolean;
3378
- params: CallUpdatedPayload;
3379
- }, "event_channel"> & {
3380
- event_channel: string;
3381
- }) | (Omit<{
3382
- event_type: "call.state";
3383
- event_channel: EventChannel;
3384
- timestamp: number;
3385
- project_id?: string;
3386
- node_id?: string;
3387
- is_author?: boolean;
3388
- params: CallStatePayload;
3389
- }, "event_channel"> & {
3390
- event_channel: string;
3391
- }) | (Omit<{
3392
- event_type: "call.play";
3393
- event_channel: EventChannel;
3394
- timestamp: number;
3395
- project_id?: string;
3396
- node_id?: string;
3397
- is_author?: boolean;
3398
- params: CallPlayPayload;
3399
- }, "event_channel"> & {
3400
- event_channel: string;
3401
- }) | (Omit<{
3402
- event_type: "call.connect";
3403
- event_channel: EventChannel;
3404
- timestamp: number;
3405
- project_id?: string;
3406
- node_id?: string;
3407
- is_author?: boolean;
3408
- params: CallConnectPayload;
3409
- }, "event_channel"> & {
3410
- event_channel: string;
3411
- }) | (Omit<{
3412
- event_type: "room.updated";
3413
- event_channel: EventChannel;
3414
- timestamp: number;
3415
- project_id?: string;
3416
- node_id?: string;
3417
- is_author?: boolean;
3418
- params: RoomUpdatedPayload;
3419
- }, "event_channel"> & {
3420
- event_channel: string;
3421
- }) | Omit<{
3422
- event_type: "member.updated";
3423
- event_channel: EventChannel;
3424
- timestamp: number;
3425
- project_id?: string;
3426
- node_id?: string;
3427
- is_author?: boolean;
3428
- params: MemberUpdatedPayload;
3429
- }, never> | Omit<{
3430
- event_type: "member.joined";
3431
- event_channel: EventChannel;
3432
- timestamp: number;
3433
- project_id?: string;
3434
- node_id?: string;
3435
- is_author?: boolean;
3436
- params: MemberJoinedPayload;
3437
- }, never> | Omit<{
3438
- event_type: "member.left";
3439
- event_channel: EventChannel;
3440
- timestamp: number;
3441
- project_id?: string;
3442
- node_id?: string;
3443
- is_author?: boolean;
3444
- params: MemberLeftPayload;
3445
- }, never> | Omit<{
3446
- event_type: "member.talking";
3447
- event_channel: EventChannel;
3448
- timestamp: number;
3449
- project_id?: string;
3450
- node_id?: string;
3451
- is_author?: boolean;
3452
- params: MemberTalkingPayload;
3453
- }, never> | Omit<{
3454
- event_type: "layout.changed";
3455
- event_channel: EventChannel;
3456
- timestamp: number;
3457
- project_id?: string;
3458
- node_id?: string;
3459
- is_author?: boolean;
3460
- params: LayoutChangedPayload;
3461
- }, never> | (Omit<{
3462
- event_type: "conversation.message";
3463
- event_channel: EventChannel;
3464
- timestamp: number;
3465
- project_id?: string;
3466
- node_id?: string;
3467
- is_author?: boolean;
3468
- params: ConversationMessagePayload;
3469
- }, "event_channel" | "timestamp" | "is_author"> & {
3470
- event_channel: string;
3471
- timestamp: string;
3472
- is_author: boolean;
3473
- }) | (Omit<{
3474
- event_type: "conversation.message.updated";
3475
- event_channel: EventChannel;
3476
- timestamp: number;
3477
- project_id?: string;
3478
- node_id?: string;
3479
- is_author?: boolean;
3480
- params: ConversationMessagePayload;
3481
- }, "event_channel" | "timestamp" | "is_author"> & {
3482
- event_channel: string;
3483
- timestamp: string;
3484
- is_author: boolean;
3485
- })>;
3486
- get iceServers(): RTCIceServer[] | undefined;
3487
- get callControl(): 'routed' | 'in-dialog';
3488
- execute<T extends JSONRPCResponse = JSONRPCResponse>(request: JSONRPCRequest, options?: PendingRPCOptions): Promise<T>;
3489
- get incomingCalls$(): Observable<Call[]>;
3490
- get incomingCalls(): Call[];
3491
- get calls$(): Observable<Call[]>;
3492
- get calls(): Call[];
3329
+ private applyPipelineSourceConstraints;
3330
+ /**
3331
+ * Clean up resources and close the peer connection.
3332
+ * Completes all observables to prevent memory leaks.
3333
+ */
3334
+ destroy(): void;
3335
+ private removeAllListeners;
3336
+ private stopRemoteTracks;
3337
+ get mediaDirections(): {
3338
+ audio: RTCRtpTransceiverDirection;
3339
+ video: RTCRtpTransceiverDirection;
3340
+ };
3341
+ protected _setRemoteDescription(params: RTCSessionDescriptionInit): Promise<void>;
3493
3342
  }
3494
3343
  //#endregion
3495
- //#region src/core/types/warnings.types.d.ts
3496
- /**
3497
- * Non-fatal warning emitted via {@link SignalWire.warnings$ | client.warnings$}.
3498
- *
3499
- * Use to detect SDK behaviors that affect session liveness or developer-facing
3500
- * contracts but do not warrant disconnection. Discriminated by `code`.
3501
- *
3502
- * Existing consumers of `errors$` are NOT notified — `warnings$` is a separate
3503
- * channel so application code can react to warnings without triggering
3504
- * error-handling code paths (e.g., disconnect cascades, user-facing toasts).
3505
- */
3506
- type SDKWarning = CredentialRefreshFallbackWarning | CredentialNoRefreshHandlerWarning;
3507
- /**
3508
- * Diagnostic detail for {@link CredentialRefreshFallbackWarning}. Stable
3509
- * values, but treat unknown strings as "fell back for an unspecified cause" —
3510
- * do not branch on this value for control flow. New values may be added in
3511
- * future releases.
3512
- */
3513
- type CredentialRefreshFallbackReason = 'no-scope' | 'no-dpop-support' | 'endpoint-failed' | 'activation-timeout' | (string & {});
3344
+ //#region src/interfaces/WebRTCVerto.d.ts
3514
3345
  /**
3515
- * Emitted when the SDK falls back to the developer-provided
3516
- * {@link CredentialProvider.refresh} because the Client Bound SAT path
3517
- * could not take over.
3518
- *
3519
- * Common causes:
3520
- * - The minted SAT lacks `sat:refresh` scope (`reason: 'no-scope'`).
3521
- * - The `/devices/token` exchange failed transiently (`reason: 'endpoint-failed'`).
3522
- *
3523
- * Subscribe to this warning to detect:
3524
- * - SDKs running with plain SATs that rely on developer-managed refresh
3525
- * - Deployments expected to use bound tokens that silently downgraded to bearer
3526
- * (a security-relevant signal for fleet observability)
3346
+ * Extended interface for WebRTC Verto Manager
3347
+ * Includes WebRTC-specific state and peer connection management
3527
3348
  */
3528
- interface CredentialRefreshFallbackWarning {
3529
- code: 'credential_refresh_fallback';
3530
- source: 'CredentialProvider';
3531
- reason: CredentialRefreshFallbackReason;
3532
- message: string;
3349
+ interface WebRTCVerto extends VertoManager {
3350
+ readonly selfId$: Observable<string | null>;
3351
+ /** Separates the media phase of call creation from the signalling phase. */
3352
+ readonly localMediaSettled$: Observable<void>;
3353
+ readonly selfId: string | null;
3354
+ readonly nodeId$: Observable<string | null>;
3355
+ readonly nodeId: string | null;
3356
+ readonly localStream$: Observable<MediaStream>;
3357
+ readonly localStream: MediaStream | null;
3358
+ readonly remoteStream$: Observable<MediaStream>;
3359
+ readonly remoteStream: MediaStream | null;
3360
+ readonly mediaDirections$: Observable<MediaDirections>;
3361
+ readonly mediaDirections: MediaDirections;
3362
+ readonly signalingStatus$: Observable<SignalingStatus>;
3363
+ readonly mainPeerConnection: RTCPeerConnectionController;
3364
+ bye(cause?: string): Promise<void>;
3365
+ sendDigits(dtmf: string): Promise<void>;
3366
+ /**
3367
+ * Send a member-control op in-dialog via verto.info (no self/target member
3368
+ * tuple). The payload rides in the verto.info `params.command` body — a
3369
+ * sibling of `dialogParams`, same level as `dtmf` — matched to this call's
3370
+ * channel by `dialogParams.callID`, so control lands on the call's own channel
3371
+ * without relying on the {node_id,call_id,member_id} addressing the routed
3372
+ * transport uses.
3373
+ */
3374
+ sendCallControl(method: string, params: Record<string, unknown>): Promise<unknown>;
3375
+ hold(): Promise<void>;
3376
+ unhold(): Promise<void>;
3377
+ destroy(): void;
3378
+ transfer(options: TransferOptions): Promise<void>;
3379
+ /** Request a video keyframe via verto.modify. */
3380
+ requestKeyframe?: () => void;
3381
+ /** Request an ICE restart via verto.modify with iceRestart offer. */
3382
+ requestIceRestart?: (relayOnly?: boolean) => Promise<void>;
3383
+ /** Request an ICE restart on all active peer connections (multi-leg). */
3384
+ requestIceRestartAll?: (relayOnly?: boolean) => Promise<void>;
3385
+ /** Request keyframes on all video-receiving legs (skips send-only screen share). */
3386
+ requestKeyframeAll?: () => void;
3387
+ /** Lazily create (or return) the local audio pipeline for the main peer connection. */
3388
+ ensureLocalAudioPipeline(): LocalAudioPipeline | null;
3389
+ /** Current local audio pipeline, or null if it has not been created yet. */
3390
+ readonly localAudioPipeline: LocalAudioPipeline | null;
3391
+ }
3392
+ //#endregion
3393
+ //#region src/managers/CallEventsManager.d.ts
3394
+ interface WebRTCCallEventManagerOptions {}
3395
+ /** @internal */
3396
+ declare class CallEventsManager extends Destroyable {
3397
+ protected webRtcCallSession: CallManager;
3398
+ protected options: WebRTCCallEventManagerOptions;
3399
+ private selfId?;
3400
+ private originCallId?;
3401
+ private callIds;
3402
+ private roomSessionIds;
3403
+ private _participants$;
3404
+ private _self$;
3405
+ private _sessionState$;
3406
+ constructor(webRtcCallSession: CallManager, options?: WebRTCCallEventManagerOptions);
3407
+ get participants$(): Observable<CallParticipant[]>;
3408
+ get participants(): CallParticipant[];
3409
+ get self$(): Observable<CallSelfParticipant>;
3410
+ isRoomSessionIdValid(roomSessionId: string): boolean;
3411
+ addCallId(callId: string): void;
3412
+ isCallIdValid(callId: string): boolean;
3413
+ get recording$(): Observable<boolean>;
3414
+ get recordings$(): Observable<Record<string, unknown>[]>;
3415
+ get streaming$(): Observable<boolean>;
3416
+ get streams$(): Observable<Record<string, unknown>[]>;
3417
+ get playbacks$(): Observable<Record<string, unknown>[]>;
3418
+ get raiseHandPriority$(): Observable<boolean>;
3419
+ get locked$(): Observable<boolean>;
3420
+ get meta$(): Observable<Record<string, unknown>>;
3421
+ get capabilities$(): Observable<Capability[]>;
3422
+ get layout$(): Observable<string>;
3423
+ get layouts$(): Observable<string[]>;
3424
+ get layoutLayers$(): Observable<LayoutLayer[]>;
3425
+ get self(): CallSelfParticipant | null;
3426
+ get layoutLayers(): LayoutLayer[];
3427
+ get recording(): boolean;
3428
+ get streaming(): boolean;
3429
+ get raiseHandPriority(): boolean;
3430
+ get locked(): boolean;
3431
+ get meta(): Record<string, unknown>;
3432
+ get layout(): string | undefined;
3433
+ get layouts(): string[];
3434
+ get capabilities(): Capability[];
3435
+ isSessionEvent(id: string): boolean;
3436
+ protected initSubscriptions(): void;
3437
+ private updateParticipantPositions;
3438
+ updateLayouts(): void;
3439
+ private updateParticipants;
3440
+ private upsertParticipant;
3441
+ private get callJoinedEvent$();
3442
+ private get layoutChangedEvent$();
3443
+ private get memberUpdates$();
3444
+ destroy(): void;
3445
+ }
3446
+ //#endregion
3447
+ //#region src/managers/CallRecoveryManager.d.ts
3448
+ type RecoveryState$1 = 'idle' | 'debouncing' | 'recovering' | 'cooldown';
3449
+ interface RecoveryEvent$1 {
3450
+ action: 'keyframe_requested' | 'reinvite_started' | 'reinvite_succeeded' | 'reinvite_failed' | 'reinvite_timeout' | 'max_attempts_reached' | 'signal_reconnect' | 'full_reconnect' | 'video_disabled' | 'video_restored';
3451
+ reason: string;
3452
+ attempt?: number;
3453
+ maxAttempts?: number;
3454
+ timestamp: number;
3533
3455
  }
3456
+ //#endregion
3457
+ //#region src/utils/qualityScore.d.ts
3534
3458
  /**
3535
- * Emitted when a credential has an `expiry_at` but the provider supplies no
3536
- * `refresh()` handler. The session will terminate at expiry with no fallback.
3459
+ * MOS (Mean Opinion Score) quality computation based on the simplified
3460
+ * ITU-T G.107 E-model.
3537
3461
  *
3538
- * Implementors who want long-lived sessions must provide a `refresh()` handler
3539
- * or mint tokens with the `sat:refresh` scope (Client Bound SAT path).
3462
+ * Provides a single 1-5 number that applications can use for a
3463
+ * green / yellow / red quality indicator without understanding raw
3464
+ * jitter and packet-loss values.
3540
3465
  */
3541
- interface CredentialNoRefreshHandlerWarning {
3542
- code: 'credential_no_refresh_handler';
3543
- source: 'CredentialProvider';
3544
- message: string;
3545
- /** Token expiry timestamp (epoch milliseconds). */
3546
- expiresAt: number;
3547
- }
3466
+ type QualityLevel$1 = 'excellent' | 'good' | 'fair' | 'poor' | 'critical';
3548
3467
  //#endregion
3549
- //#region src/utils/logger.d.ts
3550
- /** Log level names supported by the SDK. */
3551
- type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent';
3468
+ //#region src/core/entities/Call.d.ts
3552
3469
  /**
3553
- * Logger interface that consumers can implement to replace the built-in logger.
3554
- * All methods accept variadic arguments matching the browser console API.
3470
+ * Manager instances returned by initialization callback
3555
3471
  */
3556
- interface SDKLogger {
3557
- error(...args: unknown[]): void;
3558
- warn(...args: unknown[]): void;
3559
- info(...args: unknown[]): void;
3560
- debug(...args: unknown[]): void;
3561
- trace(...args: unknown[]): void;
3562
- }
3563
- /** Options for WebSocket traffic logging. */
3564
- interface WsTrafficOptions {
3565
- type: 'send' | 'recv' | 'http';
3566
- /** Parsed object or raw string — will be JSON.stringify'd for display if an object. */
3567
- payload: unknown;
3472
+ interface CallManagers {
3473
+ vertoManager: WebRTCVerto;
3474
+ callEventsManager: CallEventsManager;
3568
3475
  }
3569
3476
  /**
3570
- * Options for WebSocket traffic logging using raw strings.
3571
- * The string is only parsed when logging is enabled, avoiding
3572
- * unnecessary JSON.parse on every message.
3477
+ * Initialization callback that creates managers for a Call instance
3478
+ * @param call - The WebRTCCall instance being initialized
3479
+ * @returns Manager instances for the call
3573
3480
  */
3574
- interface WsTrafficRawOptions {
3575
- type: 'send' | 'recv';
3576
- raw: string;
3577
- }
3578
- /** Debug options that control verbose SDK logging. */
3579
- interface DebugOptions {
3580
- /** Log all WebSocket send/recv traffic to the console. */
3581
- logWsTraffic?: boolean;
3582
- }
3583
- /** Extended logger with SDK-internal helpers (wsTraffic). */
3584
- interface InternalSDKLogger extends SDKLogger {
3585
- wsTraffic: (options: WsTrafficOptions | WsTrafficRawOptions) => void;
3586
- }
3587
- /** Replace the built-in logger with a custom implementation. Pass `null` to restore defaults. */
3588
- declare const setLogger: (logger: SDKLogger | null) => void;
3589
- /** Configure debug options (e.g., `{ logWsTraffic: true }`). */
3590
- declare const setDebugOptions: (options: DebugOptions | null) => void;
3481
+ type ManagerInitializer = (call: WebRTCCall) => CallManagers;
3591
3482
  /**
3592
- * Set the log level for the built-in logger.
3593
- * Has no effect when a custom logger is set via `setLogger()`.
3483
+ * Required initialization configuration for Call constructor.
3484
+ * Calls must be created via {@link CallFactory} which provides these dependencies.
3594
3485
  */
3595
- declare const setLogLevel: (level: LogLevel) => void;
3596
- declare const getLogger: () => InternalSDKLogger;
3597
- //#endregion
3598
- //#region src/clients/SignalWire.d.ts
3599
- /** Options for constructing a {@link SignalWire}. */
3600
- interface SignalWireOptions {
3601
- /** Skip automatic WebSocket connection on construction. */
3602
- skipConnection?: boolean;
3603
- /** Skip automatic user registration on construction. */
3604
- skipRegister?: boolean;
3605
- /** Skip monitoring media device changes. */
3606
- skipDeviceMonitoring?: boolean;
3607
- /** Whether to reconnect to previously attached calls. */
3608
- reconnectAttachedCalls?: boolean;
3609
- /** Whether to save preferences. */
3610
- savePreferences?: boolean;
3611
- /**
3612
- * Persist the session across page reloads.
3613
- *
3614
- * When `true`, credential, authorization state, and protocol are stored in
3615
- * `localStorage` (survives reload). The DPoP key pair is persisted in
3616
- * IndexedDB. On reload, the SDK restores the session from cache
3617
- * without calling `credentialProvider.authenticate()`.
3618
- *
3619
- * When `false` (default), session data lives in `sessionStorage` and is
3620
- * lost on reload.
3621
- *
3622
- * Both {@link SignalWire.disconnect | disconnect()} and
3623
- * {@link SignalWire.destroy | destroy()} end the session and clear the
3624
- * persisted resume state and attach records; credentials and device
3625
- * preferences survive. Use `resetToDefaults()` for a full wipe, or
3626
- * `unregister()` to temporarily stop receiving inbound calls while keeping
3627
- * the session alive.
3628
- */
3629
- persistSession?: boolean;
3630
- /** Custom storage implementation for persistence. */
3631
- storageImplementation?: Storage;
3632
- /** Custom WebSocket constructor */
3633
- webSocketConstructor?: WebSocketAdapter | NodeSocketAdapter;
3634
- /** Custom WebRTC API provider */
3635
- webRTCApiProvider?: WebRTCApiProvider;
3636
- /**
3637
- * Custom logger implementation. Must implement the {@link SDKLogger} interface.
3638
- * Pass `null` to restore the built-in logger.
3639
- *
3640
- * **Note:** Logger configuration is global — setting it on one instance affects all instances.
3641
- */
3642
- logger?: SDKLogger | null;
3486
+ interface CallInitialization {
3643
3487
  /**
3644
- * Log level for the built-in logger.
3645
- * Default: `'warn'`. Set to `'debug'` for verbose SDK output.
3646
- * Has no effect when a custom `logger` is provided.
3647
- *
3648
- * **Note:** Logger configuration is global — setting it on one instance affects all instances.
3488
+ * Callback function that creates and wires manager instances
3649
3489
  */
3650
- logLevel?: LogLevel;
3651
- /** Debug options for verbose SDK diagnostics (e.g., `{ logWsTraffic: true }`). */
3652
- debug?: DebugOptions;
3490
+ initializeManagers: ManagerInitializer;
3653
3491
  /**
3654
- * Control transport for `call.*` verbs across ALL calls in this session:
3655
- * - `'routed'` (default) sends them on the client's session channel.
3656
- * - `'in-dialog'` carries them on each call's own signaling channel via `verto.info`,
3657
- * so control works without the client needing to know how the conference is hosted.
3658
- *
3659
- * `'in-dialog'` is opt-in because it does not reach everywhere `'routed'` does: use it
3660
- * only for calls that join a conference over SWML (e.g. an SWML `join_conference`).
3661
- *
3662
- * It is a client-wide setting rather than per-`dial()` because the SDK issues some
3663
- * control RPCs itself (fetching the layout list on join, for example), and those must
3664
- * travel the same way as the app's own, or reads and writes land on different transports.
3665
- *
3666
- * @experimental A rollout switch, not a long-term part of the API. Expected to
3667
- * disappear once `'in-dialog'` becomes the only transport, so it carries no semver
3668
- * promise and application code should not depend on it.
3492
+ * Device controller for media device access
3669
3493
  */
3670
- callControl?: 'routed' | 'in-dialog';
3671
- }
3672
- /** Options for {@link SignalWire.dial}. Extends {@link MediaOptions} with dial-specific settings. */
3673
- interface DialOptions extends MediaOptions {
3674
- /** Preferred video codecs for this call (overrides global preferences). */
3675
- preferredVideoCodecs?: string[];
3676
- /** Preferred audio codecs for this call (overrides global preferences). */
3677
- preferredAudioCodecs?: string[];
3678
- /** Enable stereo Opus for this call (overrides global preferences). */
3679
- stereo?: boolean;
3680
- /** Optional node ID for routing the call */
3681
- nodeId?: string;
3494
+ deviceController: DeviceController;
3682
3495
  /**
3683
- * Custom variables sent with the Verto invite. Merged with
3684
- * `client.preferences.userVariables` and any query-string variables on the
3685
- * destination URI; values here take precedence.
3496
+ * Network change events for feeding recovery pipeline
3686
3497
  */
3687
- userVariables?: Record<string, unknown>;
3498
+ networkChange$?: Observable<NetworkChangeEvent>;
3688
3499
  }
3689
3500
  /**
3690
- * Main entry point for the SignalWire Browser SDK.
3691
- *
3692
- * Manages authentication, WebSocket transport, call creation, and media devices.
3501
+ * Concrete WebRTC call implementation.
3693
3502
  *
3694
- * @example
3695
- * ```ts
3696
- * const client = new SignalWire(credentialProvider);
3697
- * client.isConnected$.subscribe(connected => console.log('Connected:', connected));
3698
- * const call = await client.dial('/public/my-room');
3699
- * ```
3503
+ * Manages the full lifecycle of a call including signaling, media streams,
3504
+ * participants, layout, and event routing. Created via {@link SignalWire.dial}
3505
+ * or received as an inbound call.
3700
3506
  */
3701
- declare class SignalWire extends Destroyable implements DeviceController {
3702
- /** Global SDK preferences (timeouts, ICE config, media defaults). */
3703
- preferences: ClientPreferences;
3704
- private _user$;
3705
- private _directory$;
3706
- private _transport;
3707
- private _clientSession;
3708
- private _publicSession;
3709
- private _deviceController;
3710
- private _attachManager?;
3711
- /**
3712
- * Set once a credential recovery has been *verified* — reauthenticated and
3713
- * then proven by the operation it was meant to unblock. Read by the attach
3714
- * path, which may only discard an attach record when a reattach is refused
3715
- * on a credential the server has already accepted.
3716
- */
3717
- private _credentialRecovered;
3718
- private _isConnected$;
3719
- private _isRegistered$;
3507
+ declare class WebRTCCall extends Destroyable implements CallManager {
3508
+ clientSession: ClientSession;
3509
+ options: CallOptions;
3510
+ address?: Address | undefined;
3511
+ /** Unique identifier for this call. */
3512
+ readonly id: string;
3513
+ /** Destination URI this call was placed to. */
3514
+ to?: string;
3515
+ private vertoManager;
3516
+ private callEventsManager;
3517
+ private participantFactory;
3720
3518
  private _errors$;
3721
- private _warnings$;
3722
- private _options;
3723
- private _dpopManager?;
3724
- private _refreshCoordinator?;
3725
- /** The refresh path's own HTTP controller — see resolveCredentials. */
3726
- private _refreshHttp?;
3727
- /** Host `_refreshHttp` was built against, so it can be rebuilt when the token's `ch` changes. */
3728
- private _refreshHttpHost?;
3729
- private _credentialProvider?;
3730
- private _deps;
3731
- private _networkMonitor?;
3732
- private _visibilityController?;
3733
- private _diagnosticsCollector?;
3734
- private _platformCapabilities?;
3735
- /**
3736
- * Creates a new SignalWire client and begins connecting.
3737
- *
3738
- * @param credentialProvider - Provider that supplies authentication credentials.
3739
- * @param options - Configuration options (connection, device monitoring, preferences).
3740
- */
3741
- constructor(credentialProvider: CredentialProvider | undefined, options?: SignalWireOptions);
3742
- /**
3743
- * Build the refresh path's own HTTP controller, against whatever host is current.
3744
- *
3745
- * Called on first use rather than up front, so `apiHost` already reflects the
3746
- * token's `ch` claim. Same credential source as the container's controller — only
3747
- * the instance, and therefore its observable streams, is separate.
3748
- */
3749
- private createRefreshHttpController;
3519
+ private _status$;
3520
+ private _lastMergedStatus;
3521
+ private _answered$;
3522
+ private _answerMediaOptions?;
3523
+ private _holdState;
3524
+ private _userVariables$;
3525
+ private _statsMonitor?;
3526
+ private _recoveryManager?;
3527
+ private _networkChange$?;
3528
+ private _networkIssues$;
3529
+ private _networkMetrics$;
3530
+ private _isNetworkHealthy$;
3531
+ private _qualityScore$;
3532
+ private _qualityLevel$;
3533
+ private _recoveryState$;
3534
+ private _recoveryEvent$;
3535
+ private _bandwidthConstrained$;
3536
+ private _mediaParamsUpdated$;
3537
+ private _customSubscriptions;
3538
+ private _pushToTalkEnabled;
3539
+ private _remoteAudioMeter;
3540
+ constructor(clientSession: ClientSession, options: CallOptions, initialization: CallInitialization, address?: Address | undefined);
3541
+ /** Observable stream of errors from media, signaling, and peer connection layers. */
3542
+ get errors$(): Observable<CallError>;
3750
3543
  /**
3751
- * Initializes DPoP if not already set up. Returns the fingerprint on success.
3544
+ * @internal Push an error to the call's error stream.
3545
+ * Fatal errors automatically transition the call to `'failed'` and destroy it.
3752
3546
  */
3753
- private initDPoP;
3547
+ emitError(callError: CallError): void;
3548
+ /** Notify the recovery manager that a verto.modify signaling exchange failed. */
3549
+ notifyModifyFailed(): void;
3550
+ /** Whether this call is `'inbound'` or `'outbound'`. */
3551
+ get direction(): CallDirection;
3552
+ /** Observable of the address associated with this call. */
3553
+ get address$(): Observable<Address | undefined>;
3554
+ /** Display name of the caller. */
3555
+ get fromName(): string | undefined;
3556
+ /** Address URI of the caller. */
3557
+ get from(): string | undefined;
3558
+ /** Display name of the callee. */
3559
+ get toName(): string | undefined;
3560
+ /** Toggles whether incoming video is received. @throws {UnimplementedError} Not yet implemented. */
3561
+ toggleIncomingVideo(): Promise<void>;
3562
+ /** Toggles whether incoming audio is received. @throws {UnimplementedError} Not yet implemented. */
3563
+ toggleIncomingAudio(): Promise<void>;
3564
+ /** @internal Registers an additional call ID for event routing. */
3565
+ addCallId(callId: string): void;
3566
+ /** List of capabilities available in the current call. */
3567
+ get capabilities(): Capability[];
3568
+ /** Current snapshot of all participants in the call. */
3569
+ get participants(): CallParticipant[];
3570
+ /** The local participant, or `null` if not yet joined. */
3571
+ get self(): CallSelfParticipant | null;
3572
+ /** Toggles the call lock state, preventing or allowing new participants from joining. */
3573
+ toggleLock(): Promise<void>;
3754
3574
  /**
3755
- * Resolves credentials using cache-first strategy when persistSession is enabled.
3575
+ * Toggles the hold state of the call (pauses/resumes local media transmission).
3756
3576
  *
3757
- * 1. If persistSession → check localStorage for cached credential
3758
- * 2. If cached and not expired → use it (skip provider.authenticate())
3759
- * 3. If no cache or expired → call provider.authenticate()
3760
- * 4. If no provider AND no cache → throw
3577
+ * Distinct from {@link Participant.toggleMute} which mutes individual tracks.
3761
3578
  */
3762
- private resolveCredentials;
3763
- private validateCredentials;
3579
+ toggleHold(): Promise<void>;
3580
+ /** @throws {UnimplementedError} Not yet implemented. Status tracked via {@link recording$}. */
3581
+ startRecording(): Promise<void>;
3582
+ /** @throws {UnimplementedError} Not yet implemented. Status tracked via {@link streaming$}. */
3583
+ startStreaming(): Promise<void>;
3764
3584
  /**
3765
- * Reauthenticate the currently-open session with a freshly obtained
3766
- * credential so the new token takes effect on the live socket immediately —
3767
- * not just on the next reconnect. No-op when the session is not
3768
- * connected/authenticated or the credential carries no token (e.g. an
3769
- * authorization-state-only refresh). Non-fatal: reauth failures surface on
3770
- * `errors$` without aborting the refresh that triggered this.
3585
+ * Replaces the call's custom metadata.
3586
+ * @param _meta - Metadata object to set.
3587
+ * @throws {UnimplementedError} Not yet implemented.
3771
3588
  */
3772
- private reauthenticateLiveSession;
3589
+ setMeta(_meta: Record<string, unknown>): Promise<void>;
3773
3590
  /**
3774
- * Recover a session the server is refusing: mint a fresh credential,
3775
- * reauthenticate the live session with it, and retry the operation.
3776
- *
3777
- * The connection is deliberately kept. A reload authenticates the new socket
3778
- * against the persisted `authorization_state`, and that handshake is what
3779
- * associates the socket with the previous session — the association reattach
3780
- * depends on. `signalwire.reauthenticate` swaps the credential *on that same
3781
- * session*, so recovery never touches the resume state. Discarding it would
3782
- * heal the credential by destroying the very thing the caller is trying to
3783
- * get back to.
3784
- *
3785
- * The operation is still the verdict, never the RPC. Reauthenticating with
3786
- * the in-memory token is accepted by a resume even while requests stay
3787
- * refused, because the persisted `authorization_state` short-circuits token
3788
- * validation — and `signalwire.reauthenticate` with a *freshly minted* token
3789
- * has also been observed accepted while `subscriber.online` keeps being
3790
- * refused (staging run 33826974634). Both look like success and are not.
3791
- *
3792
- * @returns the operation's value, or the reason recovery could not deliver
3793
- * one. `error` is undefined when there was no way to mint at all.
3591
+ * Merges values into the call's custom metadata (unlike {@link setMeta} which replaces).
3592
+ * @param _meta - Metadata to merge.
3593
+ * @throws {UnimplementedError} Not yet implemented.
3794
3594
  */
3795
- private recoverAndRetry;
3595
+ updateMeta(_meta: Record<string, unknown>): Promise<void>;
3596
+ /** Observable of layout layer positions for all participants. */
3597
+ get layoutLayers$(): Observable<LayoutLayer[]>;
3598
+ /** Current snapshot of layout layers. */
3599
+ get layoutLayers(): LayoutLayer[];
3796
3600
  /**
3797
- * Re-mint a credential and adopt it only if the live session accepts it.
3601
+ * Executes a Verto RPC method targeting a specific participant.
3798
3602
  *
3799
- * The mechanism follows the binding: a client-bound session re-mints a bound
3800
- * base SAT through `authenticate()` with the DPoP fingerprint, because the
3801
- * developer refresh handler would hand back an unbound token and silently
3802
- * degrade the session. An unbound session uses the refresh handler. Rotation
3803
- * cost is not a reason to skip this — the only reason is having no mechanism.
3603
+ * Constructs call context (node_id, call_id, member_id) and sends the RPC request.
3804
3604
  *
3805
- * @returns whether the session is now running on a freshly accepted credential.
3806
- */
3807
- private remintAndReauthenticate;
3808
- /**
3809
- * Re-mint a credential via `provider.refresh()`, routed through the
3810
- * coordinator's shared in-flight guard so concurrent re-mint paths (a
3811
- * scheduled/resume refresh, -32003 recovery, and reconnect) never fire a
3812
- * second `provider.refresh()` in parallel — which rotating one-time-use
3813
- * refresh tokens reject. Falls back to a direct call only if the coordinator
3814
- * has not been constructed yet.
3605
+ * @param target - Target {@link MemberTarget} triple, or the local member's
3606
+ * ID string for self-operations (any other string is rejected — a bare
3607
+ * member id cannot carry the remote member's own call context).
3608
+ * @param method - Verto method name (e.g. `'call.mute'`, `'call.member.remove'`).
3609
+ * @param args - Parameters for the RPC method.
3610
+ * @returns The RPC response.
3611
+ * @throws {CallNotReadyError} If the call has no self member context yet.
3612
+ * @throws {InvalidParams} If a string target is not the local member's ID.
3613
+ * @throws {JSONRPCError} If the RPC call returns an error.
3815
3614
  */
3816
- private remintCredential;
3615
+ executeMethod<T extends JSONRPCResponse = JSONRPCResponse>(target: string | MemberTarget, method: string, args: Record<string, unknown>): Promise<T>;
3817
3616
  /**
3818
- * Re-mint credentials before a fresh (re)connect (`onBeforeReconnect` hook).
3819
- * The session invokes this only when it is client-bound OR the in-memory
3820
- * token is expired. The re-mint mechanism depends on the binding:
3821
- * - Client-bound: `authenticate()` with the DPoP fingerprint to obtain a
3822
- * fresh base SAT the upcoming reconnect can re-bind (the
3823
- * DeviceTokenManager re-activates afterwards).
3824
- * - Unbound: the developer's non-interactive `refresh()` handler.
3825
- * `authenticate()` is deliberately NOT used here — it may be interactive
3826
- * (a login prompt) and must not fire on a background reconnect.
3617
+ * `executeMethod` for a call opened with `callControl: 'in-dialog'`.
3827
3618
  *
3828
- * Rejects on failure so the session aborts the reconnect rather than
3829
- * replaying a stale token.
3830
- */
3831
- private refreshCredentialForReconnect;
3832
- /** Persist credential to localStorage when persistSession is enabled. */
3833
- private persistCredential;
3834
- /**
3835
- * Persist whether the session is client-bound, mirroring the credential's
3836
- * storage scopes so it survives a reload. The preflight recovery reads it
3837
- * before any session exists to decide whether to re-bind via `authenticate()`
3838
- * or refresh an unbound token; the marker tracks the latest binding, so an
3839
- * unbound reconnect clears a stale marker from an earlier client-bound login.
3840
- */
3841
- private persistClientBoundMarker;
3842
- /** Read the persisted client-bound marker (see {@link persistClientBoundMarker}). */
3843
- private wasClientBound;
3844
- private init;
3845
- private handleAttachments;
3846
- /**
3847
- * Fetch the authenticated user profile, recovering a stale credential.
3619
+ * Translates the routed transport's calling convention into the in-dialog one. No
3620
+ * `self` tuple is sent, but a `target` is — the same {call_id, member_id} the routed
3621
+ * transport puts in `target` (minus node_id), for self-ops and cross-member ops alike.
3848
3622
  *
3849
- * On a reload the persisted credential can be expired. Unlike the WS resume —
3850
- * which the server accepts against the persisted `authorization_state` even
3851
- * with an expired token — this REST preflight has no such short-circuit and is
3852
- * refused (401). There is no session yet to reauthenticate, so recovery
3853
- * re-mints the credential through the provider ({@link refreshCredentialForReconnect})
3854
- * and retries with a FRESH {@link User}: Fetchable memoizes its result
3855
- * (shareReplay), so reusing the instance would replay the 401 instead of
3856
- * re-fetching with the new token. Without the user id the transport/session —
3857
- * and the reattach a reload is trying to preserve — cannot even be addressed.
3623
+ * Target shapes are per-verb and irregular, so they are centralised here rather
3624
+ * than left to callers: most verbs take a singular `target`, `call.member.remove`
3625
+ * takes a plural `targets` array, and `call.member.position.set` takes a flat
3626
+ * `targets` of `{call_id, position}` — the one verb keyed on call_id rather than
3627
+ * member_id, so the member triple `Participant.setPosition` built is unwrapped.
3858
3628
  */
3859
- private fetchUserOrRecover;
3629
+ private executeMethodInDialog;
3860
3630
  /**
3861
- * Establishes the WebSocket connection and authenticates the session.
3862
- *
3863
- * ## Reconnection behavior
3864
- *
3865
- * After a successful connection the underlying {@link WebSocketController}
3866
- * automatically attempts to reconnect whenever the socket closes
3867
- * unexpectedly (e.g. network change, server restart). Reconnection uses an
3868
- * **exponential back-off** strategy:
3869
- *
3870
- * - First retry after `reconnectDelayMin` (default **0.1 s**).
3871
- * - Each subsequent retry doubles the delay up to `reconnectDelayMax`
3872
- * (default **3 s**).
3873
- * - The delay resets to `reconnectDelayMin` once a connection succeeds.
3874
- * - A per-attempt `connectionTimeout` (default **10 s**) aborts the
3875
- * attempt and schedules the next retry if the server does not respond.
3876
- *
3877
- * Calling {@link disconnect} stops the reconnection loop entirely.
3878
- *
3879
- * ## Message handling during temporary disconnections
3631
+ * Sends a `call.*` control verb **in-dialog** via `verto.info`, as an alternative
3632
+ * to the routed {@link executeMethod} transport.
3880
3633
  *
3881
- * While the socket is not in the `connected` state, **outgoing messages
3882
- * are queued** in an internal buffer. Once the connection is
3883
- * re-established the queue is flushed in order so no outgoing RPC call is
3884
- * lost.
3634
+ * Why both exist: `executeMethod` addresses the member with an explicit
3635
+ * `{node_id, call_id, member_id}` tuple, which does not resolve for every conference,
3636
+ * so the op can fail. An in-dialog frame carries the verb on the member's own
3637
+ * signaling channel instead, so control works without the client needing to know how
3638
+ * the conference is hosted.
3885
3639
  *
3886
- * **Incoming** server-to-client messages that arrive while the socket is
3887
- * down are *not* buffered by the SDK — they are expected to be
3888
- * re-delivered by the server after the session is re-authenticated.
3889
- * Active RPC calls that were awaiting a response will time out
3890
- * (default **5 s**) and reject with an `RPCTimeoutError`; callers should
3891
- * handle this and retry if appropriate.
3640
+ * The trade-off is reach: the in-dialog transport is only accepted for calls that
3641
+ * join a conference over SWML (e.g. an SWML `join_conference`); use the routed
3642
+ * default otherwise.
3892
3643
  *
3893
- * The connection status can be observed via the `status$` observable on
3894
- * the transport layer, which emits `'connecting'`, `'connected'`,
3895
- * `'reconnecting'`, `'disconnecting'`, or `'disconnected'`.
3896
- */
3897
- connect(): Promise<void>;
3898
- /**
3899
- * Observable that emits the {@link User} profile once fetched,
3900
- * or `undefined` before authentication completes.
3644
+ * `params` are sent verbatim — nothing is built for you, which includes the target.
3645
+ * **A self-directed op still needs one**, or it is refused; name yourself explicitly:
3901
3646
  *
3902
- * @example
3903
3647
  * ```ts
3904
- * client.user$.subscribe(u => {
3905
- * if (u) console.log('Logged in as', u.email);
3906
- * });
3648
+ * const { call_id, member_id } = call.self.target;
3649
+ * await call.sendCommand('call.mute', { channels: ['audio'], target: { call_id, member_id } });
3907
3650
  * ```
3908
- */
3909
- get user$(): Observable<User | undefined>;
3910
- /** Current user snapshot, or `undefined` if not yet authenticated. */
3911
- get user(): User | undefined;
3912
- /**
3913
- * Observable that emits the {@link Directory} instance once the client is connected,
3914
- * or `undefined` while disconnected. Subscribe to this to safely wait for the directory
3915
- * to become available without risking an error.
3916
3651
  *
3917
- * @example
3918
- * ```ts
3919
- * client.directory$.subscribe(dir => {
3920
- * if (dir) dir.addresses$.subscribe(console.log);
3921
- * });
3922
- * ```
3923
- */
3924
- get directory$(): Observable<Directory | undefined>;
3925
- /**
3926
- * Current directory snapshot, or `undefined` if the client is not yet connected.
3927
- * Prefer {@link directory$} when you need to react to the directory becoming available.
3652
+ * Never include `node_id` — only the two ids. The shapes are per-verb: most take a
3653
+ * singular `target`, `call.member.remove` takes a plural `targets` array, and
3654
+ * `call.member.position.set` takes a flat `targets: [{call_id, position}]` (the one
3655
+ * verb keyed on `call_id` rather than `member_id`). Verbs that act on the call as a
3656
+ * whole, or that the SDK does not wrap at all, take no target.
3657
+ *
3658
+ * For the typed alternative that handles all of this, create the client with
3659
+ * `callControl: 'in-dialog'` and use the ordinary `Call`/`Participant` methods.
3660
+ *
3661
+ * @internal Not part of the supported surface while the in-dialog transport is still
3662
+ * rolling out. `WebRTCCall` is exported from the package entry, so without this tag
3663
+ * TypeDoc publishes the method — and the example above — as public API.
3664
+ *
3665
+ * @param method - A `call.*` method name (e.g. `'call.mute'`).
3666
+ * @param params - Method parameters, sent verbatim.
3667
+ * @returns The method's own reply, unwrapped from the `verto.info` envelope.
3668
+ * @throws {JSONRPCError} If the control op fails.
3928
3669
  */
3929
- get directory(): Directory | undefined;
3930
- /** Observable that emits when the user registration state changes. */
3931
- get isRegistered$(): Observable<boolean>;
3932
- /** Whether the user is currently registered. */
3933
- get isRegistered(): boolean;
3934
- /** Whether the client is currently connected. */
3935
- get isConnected(): boolean;
3936
- /** Observable that emits when the connection state changes. */
3937
- get isConnected$(): Observable<boolean>;
3938
- /** Observable that emits `true` when the client is both connected and authenticated. */
3939
- get ready$(): Observable<boolean>;
3940
- /** Observable stream of errors from transport, authentication, and devices. */
3941
- get errors$(): Observable<Error>;
3670
+ sendCommand<T extends JSONRPCResponse = JSONRPCResponse>(method: string, params?: Record<string, unknown>): Promise<T>;
3942
3671
  /**
3943
- * Observable stream of non-fatal SDK warnings.
3944
- *
3945
- * Subscribe to detect SDK behaviors that affect session liveness or developer-facing
3946
- * contracts but do not warrant disconnection — e.g., a fallback from Client Bound SAT
3947
- * refresh to the developer-provided `refresh()` because the SAT lacks `sat:refresh`
3948
- * scope. Discriminated by `code`.
3672
+ * The local leg's member triple — sent as `self` in every member RPC
3673
+ * envelope, and as the `target` of call-scoped self-operations (e.g. lock,
3674
+ * layout).
3949
3675
  *
3950
- * Independent from {@link errors$}: existing error consumers are not notified.
3676
+ * @throws {CallNotReadyError} Before `call.joined` delivers the self member
3677
+ * context (`selfId`/`nodeId`) — an RPC without it cannot be routed, so fail
3678
+ * fast instead of sending a doomed request.
3951
3679
  */
3952
- get warnings$(): Observable<SDKWarning>;
3953
- /** Platform WebRTC capabilities detected at construction time. */
3954
- get platformCapabilities(): PlatformCapabilities;
3955
- /** Observable that emits when the SDK auto-switches a device. */
3956
- get deviceRecovered$(): Observable<DeviceRecoveryEvent>;
3680
+ private get callSelf();
3681
+ /** Observable of the current call status (e.g. `'ringing'`, `'connected'`). */
3682
+ get status$(): Observable<CallStatus>;
3683
+ /** Observable of the participants list, emits on join/leave/update. */
3684
+ get participants$(): Observable<CallParticipant[]>;
3685
+ /** Observable of the local (self) participant. */
3686
+ get self$(): Observable<CallSelfParticipant>;
3687
+ /** Observable indicating whether the call is being recorded. */
3688
+ get recording$(): Observable<boolean>;
3689
+ /** Observable indicating whether the call is being streamed. */
3690
+ get streaming$(): Observable<boolean>;
3691
+ /** Observable indicating whether raise-hand priority is active. */
3692
+ get raiseHandPriority$(): Observable<boolean>;
3693
+ /** Observable indicating whether the call room is locked. */
3694
+ get locked$(): Observable<boolean>;
3695
+ /** Observable of custom metadata associated with the call. */
3696
+ get meta$(): Observable<Record<string, unknown>>;
3697
+ /** Observable of the call's capability flags. */
3698
+ get capabilities$(): Observable<Capability[]>;
3699
+ /** Observable of the current layout name. */
3700
+ get layout$(): Observable<string>;
3701
+ /** Current call status. */
3702
+ get status(): CallStatus;
3703
+ /** Whether the call is currently being recorded. */
3704
+ get recording(): boolean;
3705
+ /** Whether the call is currently being streamed. */
3706
+ get streaming(): boolean;
3707
+ /** Whether raise-hand priority is active. */
3708
+ get raiseHandPriority(): boolean;
3709
+ /** Whether the call room is locked. */
3710
+ get locked(): boolean;
3711
+ /** Current custom metadata of the call. */
3712
+ get meta(): Record<string, unknown>;
3713
+ /** Current layout name, or `undefined` if not set. */
3714
+ get layout(): string | undefined;
3715
+ /** Observable of available layout names. */
3716
+ get layouts$(): Observable<string[]>;
3717
+ /** Current snapshot of available layout names. */
3718
+ get layouts(): string[];
3719
+ /** Observable of the local media stream (camera/microphone). */
3720
+ get localStream$(): Observable<MediaStream>;
3721
+ /** Current local media stream, or `null` if not available. */
3722
+ get localStream(): MediaStream | null;
3723
+ /** Observable of the remote media stream from the far end. */
3724
+ get remoteStream$(): Observable<MediaStream>;
3725
+ /** Current remote media stream, or `null` if not available. */
3726
+ get remoteStream(): MediaStream | null;
3727
+ /** Observable of custom user variables associated with the call. */
3728
+ get userVariables$(): Observable<Record<string, unknown>>;
3729
+ /** a copy of the current custom user variables of the call. */
3730
+ get userVariables(): Record<string, unknown>;
3731
+ /** Merge current custom user variables of the call. */
3732
+ set userVariables(variables: Record<string, unknown>);
3733
+ /** Observable of current network health issues (empty array = healthy). */
3734
+ get networkIssues$(): Observable<NetworkIssue[]>;
3735
+ /** Current snapshot of network issues. */
3736
+ get networkIssues(): NetworkIssue[];
3737
+ /** Simple boolean health indicator derived from stats monitor. */
3738
+ get isNetworkHealthy$(): Observable<boolean>;
3739
+ /** Whether the network is currently healthy. */
3740
+ get isNetworkHealthy(): boolean;
3741
+ /** Rolling history of raw network metrics (RTT, jitter, packet loss, bitrate). */
3742
+ get networkMetrics$(): Observable<NetworkMetrics[]>;
3743
+ /** Current snapshot of the metrics rolling window. */
3744
+ get networkMetrics(): NetworkMetrics[];
3745
+ /** Observable of MOS quality score (1-5) computed from stats metrics. */
3746
+ get qualityScore$(): Observable<number>;
3747
+ /** Observable of simplified quality level (excellent/good/fair/poor/critical). */
3748
+ get qualityLevel$(): Observable<QualityLevel$1>;
3749
+ /** Observable of the recovery pipeline state machine. */
3750
+ get recoveryState$(): Observable<RecoveryState$1>;
3751
+ /** Observable of recovery events (keyframe requested, ICE restart, etc.). */
3752
+ get recoveryEvent$(): Observable<RecoveryEvent$1>;
3753
+ /** Observable indicating whether the call is bandwidth-constrained. */
3754
+ get bandwidthConstrained$(): Observable<boolean>;
3755
+ /** Observable that emits when the server pushes media params. */
3756
+ get mediaParamsUpdated$(): Observable<MediaParamsEvent>;
3957
3757
  /**
3958
- * Export a structured diagnostic bundle for support/debugging.
3959
- * Includes connection events, call summaries, and device changes.
3758
+ * @internal Emit a media params update event.
3759
+ * Called by the VertoManager when the server pushes media params.
3960
3760
  */
3961
- exportDiagnostics(): SessionDiagnostics;
3761
+ emitMediaParamsUpdated(event: MediaParamsEvent): void;
3762
+ /** Request a video keyframe via RTCP PLI/FIR. */
3763
+ requestKeyframe(): void;
3764
+ /** Force an ICE restart / re-INVITE. */
3765
+ requestIceRestart(): Promise<void>;
3962
3766
  /**
3963
- * Initialize resilience subsystems. Non-fatal: any failure is logged and
3964
- * the SDK continues working without the failing subsystem.
3767
+ * @internal Initialize resilience subsystems when the call reaches 'connected'.
3768
+ * Called from within the status subscription to wire stats and recovery.
3965
3769
  */
3966
3770
  private initResilienceSubsystems;
3967
3771
  /**
3968
- * Disconnects the WebSocket and tears down the current session.
3969
- *
3970
- * Ends the session identified by the protocol and clears its persisted
3971
- * resume state (`authorization_state` + protocol) and attach records
3972
- * together — a later {@link connect} with the same credentials starts a
3973
- * fresh session and cannot reattach to the ended session's calls.
3974
- * Credentials and device preferences are preserved. To temporarily stop
3975
- * receiving inbound calls while keeping the session alive, use
3976
- * `unregister()` instead.
3772
+ * Wait for the underlying RTCPeerConnection to reach 'connected' after
3773
+ * triggering an ICE restart. Resolves true on success, false on failure
3774
+ * or if the state doesn't transition within the configured timeout.
3977
3775
  *
3978
- * The client can be reconnected by calling {@link connect} again,
3979
- * which creates a fresh transport and session.
3776
+ * Polls connectionState directly because the recovery manager already
3777
+ * wraps this call in its own withTimeout(); a separate listener-based
3778
+ * implementation would race the outer timeout in subtle ways.
3980
3779
  */
3981
- disconnect(): Promise<void>;
3780
+ private waitForPeerConnectionConnected;
3982
3781
  /**
3983
- * Tear down the current transport / session / attach manager. Safe to call
3984
- * when nothing has been initialized yet (e.g. first connect()).
3782
+ * @internal Stop and destroy resilience subsystems (on disconnect/destroy).
3783
+ * Clears references so they can be re-created on reconnect.
3985
3784
  */
3986
- private teardownTransportAndSession;
3987
- private waitAuthentication;
3785
+ private stopResilienceSubsystems;
3786
+ /** @internal */
3787
+ createParticipant(memberId: string, selfId?: string | null): Participant | SelfParticipant;
3788
+ /** Observable of the current audio/video send/receive directions. */
3789
+ get mediaDirections$(): Observable<MediaDirections>;
3790
+ /** Current audio/video send/receive directions. */
3791
+ get mediaDirections(): MediaDirections;
3792
+ protected get participantsId$(): Observable<string[]>;
3988
3793
  /**
3989
- * Registers the user as online to receive inbound calls and events.
3794
+ * Executes a raw JSON-RPC request on the client session.
3990
3795
  *
3991
- * Waits for authentication to complete before sending the registration.
3992
- * If the initial attempt fails, reauthentication is attempted automatically.
3796
+ * Lower-level than {@link executeMethod} — allows full control over the RPC request structure.
3993
3797
  *
3994
- * @throws {InvalidCredentialsError} If registration and reauthentication both fail.
3798
+ * @param request - Complete JSON-RPC request object.
3799
+ * @param options - Optional RPC execution options (timeout, etc.).
3800
+ * @returns The RPC response.
3801
+ * @throws {JSONRPCError} If the RPC call returns an error response.
3995
3802
  */
3996
- register(): Promise<void>;
3803
+ execute<T extends JSONRPCResponse = JSONRPCResponse>(request: JSONRPCRequest, options?: PendingRPCOptions): Promise<T>;
3804
+ /** Observable of the local participant's member ID. */
3805
+ get selfId$(): Observable<string | null>;
3806
+ /** @internal Lets call creation bound the media and signalling phases apart. */
3807
+ get localMediaSettled$(): Observable<void>;
3808
+ /** Local participant's member ID, or `null` if not joined. */
3809
+ get selfId(): string | null;
3810
+ /** Observable of the server node ID handling this call. */
3811
+ get nodeId$(): Observable<string | null>;
3812
+ /** Server node ID handling this call, or `null`. */
3813
+ get nodeId(): string | null;
3814
+ private isCallSessionEvent;
3815
+ private get callSessionEvents$();
3816
+ /** Observable of call-updated events. */
3817
+ get callUpdated$(): Observable<CallUpdatedPayload>;
3818
+ /** Observable of member-joined events, emitted when a remote participant joins the call. */
3819
+ get memberJoined$(): Observable<MemberJoinedPayload>;
3820
+ /** Observable of member-left events, emitted when a participant leaves the call. */
3821
+ get memberLeft$(): Observable<MemberLeftPayload>;
3822
+ /** Observable of member-updated events (mute, volume, etc.). */
3823
+ get memberUpdated$(): Observable<MemberUpdatedPayload>;
3824
+ /** Observable of member-talking events (speech start/stop). */
3825
+ get memberTalking$(): Observable<MemberTalkingPayload>;
3826
+ /** Observable of call state-change events. */
3827
+ get callStates$(): Observable<CallStatePayload>;
3828
+ /** Observable of layout-changed events. */
3829
+ get layoutUpdates$(): Observable<LayoutChangedPayload>;
3830
+ /** Underlying `RTCPeerConnection`, for advanced use cases. */
3831
+ get rtcPeerConnection(): RTCPeerConnection | undefined;
3832
+ /** Observable of raw signaling events as plain objects. */
3833
+ get signalingEvent$(): Observable<Record<string, unknown>>;
3997
3834
  /**
3998
- * Unregisters the user, going offline for inbound calls.
3835
+ * Subscribe to a custom signaling event type on this call.
3999
3836
  *
4000
- * The WebSocket connection remains open; use {@link disconnect} to fully close it.
4001
- */
4002
- unregister(): Promise<void>;
4003
- /**
4004
- * Places an outbound call to the given destination.
3837
+ * Returns a cached observable that filters `callSessionEvents$` for events
3838
+ * whose `event_type` matches the given string. The observable completes
3839
+ * when the call is destroyed.
4005
3840
  *
4006
- * Waits for authentication before dialing. Media options are merged from
4007
- * saved preferences, destination query parameters (e.g. `?channel=video`),
4008
- * and the provided `options` (highest priority).
3841
+ * Unlike `signalingEvent$` (which only emits known call-level event types),
3842
+ * this method also matches custom/user-defined event types.
4009
3843
  *
4010
- * Returns a {@link Call} in `'ringing'` state. Subscribe to {@link Call.status$}
4011
- * to track progression through `'connected'` → `'disconnected'`.
3844
+ * The SDK does not validate event type strings --- the server decides
3845
+ * whether a given type is valid.
4012
3846
  *
4013
- * @param destination - Address URI string (e.g. `'/public/my-room'`) or {@link Address} instance.
4014
- * @param options - Media and dial options (audio/video, device constraints). Overrides defaults.
4015
- * @returns The created {@link Call} instance.
4016
- * @throws {Error} If authentication is not complete or call creation fails.
3847
+ * @param eventType - The event type to subscribe to (e.g. `'my.custom.event'`).
3848
+ * @returns An observable that emits matching signaling events.
4017
3849
  *
4018
3850
  * @example
4019
3851
  * ```ts
4020
- * const call = await client.dial('/public/conference', {
4021
- * audio: true,
4022
- * video: true,
3852
+ * call.subscribe('my.custom.event').subscribe(event => {
3853
+ * console.log('Custom event:', event);
4023
3854
  * });
4024
- * call.status$.subscribe(status => console.log('Call:', status));
4025
3855
  * ```
4026
3856
  */
4027
- dial(destination: string | Address, options?: DialOptions): Promise<Call>;
3857
+ subscribe(eventType: string): Observable<Record<string, unknown>>;
3858
+ get webrtcMessages$(): Observable<WebrtcMessagePayload>;
3859
+ get callEvent$(): Observable<WebrtcMessagePayload | CallJoinedPayload | CallLeftPayload | CallUpdatedPayload | CallStatePayload | CallPlayPayload | CallConnectPayload | RoomUpdatedPayload | MemberUpdatedPayload | MemberJoinedPayload | MemberLeftPayload | MemberTalkingPayload | LayoutChangedPayload | ConversationMessagePayload>;
3860
+ get layoutEvent$(): Observable<LayoutChangedPayload>;
4028
3861
  /**
4029
- * Runs a multi-phase connectivity test against the given destination.
3862
+ * Hangs up the call and releases all resources.
4030
3863
  *
4031
- * The test checks:
4032
- * 1. **Signaling** -- WebSocket connected, RTT measurement
4033
- * 2. **Devices** -- getUserMedia succeeds with selected (or specified) devices
4034
- * 3. **ICE/TURN** -- gathers ICE candidates to verify STUN/TURN reachability
4035
- * 4. **Media/bandwidth** (unless `skipMediaTest`) -- dials the destination,
4036
- * collects getStats() for `duration` seconds, computes bandwidth estimates
3864
+ * Sends a Verto `bye` to the server, transitions status to `'disconnecting'`,
3865
+ * then destroys the call. After this, the call instance is no longer usable.
3866
+ *
3867
+ * @example
3868
+ * ```ts
3869
+ * await call.hangup();
3870
+ * ```
3871
+ */
3872
+ hangup(): Promise<void>;
3873
+ /**
3874
+ * Sends DTMF digits on the call.
4037
3875
  *
4038
- * @param destination - A destination to dial for the media test (e.g. `'/private/network-test'`).
4039
- * @param options - Preflight options (duration, skipMediaTest, device overrides).
4040
- * @returns A {@link PreflightResult} describing connectivity health.
3876
+ * @param dtmf - The digit string to send (e.g. `'1234#'`).
4041
3877
  *
4042
3878
  * @example
4043
3879
  * ```ts
4044
- * const result = await client.preflight('/private/network-test', { duration: 5 });
4045
- * if (!result.ok) console.warn('Connectivity issues:', result.warnings);
3880
+ * await call.sendDigits('1234#');
4046
3881
  * ```
4047
3882
  */
4048
- preflight(destination: string, options?: PreflightOptions): Promise<PreflightResult>;
4049
- /** The underlying client session for advanced RPC operations. */
4050
- get session(): ClientSessionWrapper;
4051
- /** Observable list of available audio input (microphone) devices. */
4052
- get audioInputDevices$(): Observable<MediaDeviceInfo[]>;
4053
- /** Current snapshot of available audio input devices. */
4054
- get audioInputDevices(): MediaDeviceInfo[];
4055
- /** Observable list of available audio output (speaker) devices. */
4056
- get audioOutputDevices$(): Observable<MediaDeviceInfo[]>;
4057
- /** Current snapshot of available audio output devices. */
4058
- get audioOutputDevices(): MediaDeviceInfo[];
4059
- /** Observable list of available video input (camera) devices. */
4060
- get videoInputDevices$(): Observable<MediaDeviceInfo[]>;
4061
- /** Current snapshot of available video input devices. */
4062
- get videoInputDevices(): MediaDeviceInfo[];
4063
- /** Observable of the currently selected audio input device. */
4064
- get selectedAudioInputDevice$(): Observable<MediaDeviceInfo | null>;
4065
- /** Observable of the currently selected audio output device. */
4066
- get selectedAudioOutputDevice$(): Observable<MediaDeviceInfo | null>;
4067
- /** Observable of the currently selected video input device. */
4068
- get selectedVideoInputDevice$(): Observable<MediaDeviceInfo | null>;
4069
- /** Currently selected audio input device, or `null` if none. */
4070
- get selectedAudioInputDevice(): MediaDeviceInfo | null;
4071
- /** Currently selected audio output device, or `null` if none. */
4072
- get selectedAudioOutputDevice(): MediaDeviceInfo | null;
4073
- /** Currently selected video input device, or `null` if none. */
4074
- get selectedVideoInputDevice(): MediaDeviceInfo | null;
4075
- /** Media track constraints for the selected audio input device. Returns `false` when disabled. */
4076
- get selectedAudioInputDeviceConstraints(): MediaTrackConstraints | boolean;
4077
- /** Media track constraints for the selected video input device. Returns `false` when disabled. */
4078
- get selectedVideoInputDeviceConstraints(): MediaTrackConstraints | boolean;
4079
- /** Converts a `MediaDeviceInfo` to track constraints suitable for `getUserMedia`. */
4080
- deviceInfoToConstraints(deviceInfo: MediaDeviceInfo | null): MediaTrackConstraints;
4081
- /** Sets the preferred audio input device. */
4082
- selectAudioInputDevice(device: MediaDeviceInfo | null): void;
4083
- /** Sets the preferred video input device. */
4084
- selectVideoInputDevice(device: MediaDeviceInfo | null): void;
4085
- /** Sets the preferred audio output device. */
4086
- selectAudioOutputDevice(device: MediaDeviceInfo | null): void;
3883
+ sendDigits(dtmf: string): Promise<void>;
3884
+ /** Observable of WebRTC-specific signaling messages. */
3885
+ /** Observable of call-level signaling events. */
3886
+ /** Observable of layout-changed signaling events. */
4087
3887
  /**
4088
- * Apply the currently selected audio output device to an HTMLMediaElement
4089
- * (e.g. the `<audio>` or `<video>` element the consumer attached the
4090
- * remote stream to). Uses `HTMLMediaElement.setSinkId` under the hood.
4091
- * Returns a `Promise<boolean>`: `true` if the sink was applied,
4092
- * `false` if the browser doesn't support `setSinkId` or no device is
4093
- * selected.
3888
+ * Accepts an inbound call, optionally overriding media options for the answer.
3889
+ *
3890
+ * @param options - Optional media constraints for the answer (audio/video).
4094
3891
  *
4095
3892
  * @example
4096
3893
  * ```ts
4097
- * audioEl.srcObject = call.remoteStream;
4098
- * await client.applySelectedAudioOutputDevice(audioEl);
3894
+ * // Accept with defaults
3895
+ * call.answer();
3896
+ *
3897
+ * // Accept audio-only
3898
+ * call.answer({ audio: true, video: false });
4099
3899
  * ```
3900
+ * @see {@link reject} to decline the call instead.
3901
+ * @see {@link answered$} to observe the acceptance state.
4100
3902
  */
4101
- applySelectedAudioOutputDevice(element: HTMLMediaElement): Promise<boolean>;
4102
- /** Starts monitoring for media device changes (connect/disconnect). */
4103
- enableDeviceMonitoring(): void;
4104
- /** Stops monitoring for media device changes. */
4105
- disableDeviceMonitoring(): void;
4106
- /**
4107
- * Returns the capabilities of a media device.
4108
- * @param deviceInfo - The device to query.
4109
- * @returns The device capabilities, or `null` if unavailable.
4110
- */
4111
- getDeviceCapabilities(deviceInfo: MediaDeviceInfo): Promise<MediaTrackCapabilities | null>;
3903
+ answer(options?: MediaOptions): void;
3904
+ /** Media options provided when answering. Used internally by the VertoManager. */
3905
+ get answerMediaOptions(): MediaOptions | undefined;
4112
3906
  /**
4113
- * Checks whether a device is still available and usable.
4114
- * @param deviceInfo - The device to validate, or `null`.
4115
- * @returns `true` if the device is valid and available. Returns `false` for `null`, audio output devices, or unavailable devices.
3907
+ * Rejects an inbound call, preventing media negotiation.
3908
+ *
3909
+ * @see {@link answer} to accept the call instead.
3910
+ * @see {@link answered$} to observe the rejection state.
4116
3911
  */
4117
- isValidDevice(deviceInfo: MediaDeviceInfo | null): Promise<boolean>;
4118
- /** Injects a storage manager into the device controller for persistence. */
4119
- setStorageManager(storageManager: StorageManager): void;
4120
- /** Clears all device state and re-enumerates. */
4121
- clearDeviceState(): Promise<void>;
4122
- /** Forces a device re-enumeration. */
4123
- enumerateDevices(): Promise<void>;
4124
- /** Disables audio input (receive-only mode). No audio track will be acquired. */
4125
- disableAudioInput(): void;
4126
- /** Re-enables audio input, restoring the last selection or auto-selecting. */
4127
- enableAudioInput(): void;
4128
- /** Disables video input (receive-only mode). No video track will be acquired. */
4129
- disableVideoInput(): void;
4130
- /** Re-enables video input, restoring the last selection or auto-selecting. */
4131
- enableVideoInput(): void;
4132
- /** Observable that emits `true` when video input is disabled (receive-only). */
4133
- get videoInputDisabled$(): Observable<boolean>;
4134
- /** Observable that emits `true` when audio input is disabled (receive-only). */
4135
- get audioInputDisabled$(): Observable<boolean>;
4136
- /** Whether video input is currently disabled. */
4137
- get videoInputDisabled(): boolean;
4138
- /** Whether audio input is currently disabled. */
4139
- get audioInputDisabled(): boolean;
3912
+ reject(): void;
3913
+ /** Observable that emits `true` when answered, `false` when rejected. */
3914
+ get answered$(): Observable<boolean>;
4140
3915
  /**
4141
- * Triggers the browser's media permission dialog and captures the user's device selections.
3916
+ * Sets the call layout and, optionally, individual participant positions.
4142
3917
  *
4143
- * @param options - Which permissions to request.
4144
- * @param options.audio - Whether to request audio permission.
4145
- * @param options.video - Whether to request video permission.
4146
- * @returns The permission result with selected devices.
3918
+ * The gateway `call.layout.set` DTO has **no** `positions` member, so when
3919
+ * `positions` is provided this method issues a `call.member.position.set`
3920
+ * request per member (via {@link Participant.setPosition}, which keys each
3921
+ * position by that member's own call context) alongside `call.layout.set`
3922
+ * (issue #19400, Flag #6).
3923
+ *
3924
+ * **These operations are NOT atomic.** The layout is applied first, then each
3925
+ * member position sequentially, so members may briefly flash into their
3926
+ * default slots before being moved to the requested positions. Targeted
3927
+ * members are validated upfront, though: when any of them has no
3928
+ * {@link Participant.target | member call context} yet, the whole call
3929
+ * rejects before any request is sent and the layout is left unchanged.
3930
+ *
3931
+ * @param layout - Layout name (must be one of {@link layouts}).
3932
+ * @param positions - Optional map of member IDs to {@link VideoPosition} values.
3933
+ * When omitted or empty, only the layout is changed.
3934
+ * @throws {InvalidParams} If the layout is not in the available {@link layouts}.
3935
+ * @throws {ParticipantNotReadyError} If a targeted member's call context has
3936
+ * not been received yet — thrown before any request is sent.
3937
+ *
3938
+ * @example
3939
+ * ```ts
3940
+ * await call.setLayout('grid-responsive', {
3941
+ * [participantId]: 'reserved-0',
3942
+ * });
3943
+ * ```
4147
3944
  */
4148
- requestMediaPermissions(options?: {
4149
- audio?: boolean;
4150
- video?: boolean;
4151
- }): Promise<PermissionResult>;
3945
+ setLayout(layout: string, positions?: Record<string, VideoPosition>): Promise<void>;
4152
3946
  /**
4153
- * Clears all SDK-persisted state and resets to defaults.
3947
+ * Transfers the call to another destination.
4154
3948
  *
4155
- * This clears device preferences, device history, authorization state,
4156
- * attached call IDs, and all SDK storage keys, then re-enumerates devices.
3949
+ * @param options - Transfer configuration including the target destination.
3950
+ * @see {@link status$} to observe the transfer progress.
4157
3951
  */
4158
- resetToDefaults(): Promise<void>;
3952
+ transfer(options: TransferOptions): Promise<void>;
4159
3953
  /**
4160
- * Destroys the client, clearing timers and releasing all resources.
3954
+ * Set the local microphone gain as a percentage applied before transmission.
4161
3955
  *
4162
- * Intentionally destroying the client ends its session: the resume state
4163
- * (`authorization_state` + protocol) and the attach records are both
4164
- * cleared. Credentials and device preferences are preserved — use
4165
- * {@link resetToDefaults} for a full wipe. To temporarily stop receiving
4166
- * inbound calls while keeping the session alive, use `unregister()`.
3956
+ * - `0` = silent
3957
+ * - `100` = unity (no change, default)
3958
+ * - `200` = 2× digital boost (max; expect clipping / noise amplification)
3959
+ *
3960
+ * Values are clamped to [0, 200]. Engages the local audio pipeline on
3961
+ * first use (one-time cost).
3962
+ *
3963
+ * Note: this is a **digital** multiplier applied in a Web Audio GainNode
3964
+ * between your mic track and the RTCRtpSender — it does not change the
3965
+ * physical mic's hardware sensitivity. Browsers' autoGainControl can
3966
+ * fight the setting; call {@link setAutoGainControl}(false) for
3967
+ * predictable behaviour.
3968
+ *
3969
+ * @param value - Gain percentage (0..200; 100 = unity).
4167
3970
  */
4168
- destroy(): void;
4169
- }
4170
- //#endregion
4171
- //#region src/utils/embeddableCall.d.ts
4172
- /** Options for {@link embeddableCall}. */
4173
- interface EmbeddableCallOptions {
4174
- /** Destination URI to call. */
4175
- to: string;
4176
- /** Embed token for authentication. */
4177
- embedToken: string;
4178
- /** SignalWire host URL. */
4179
- host: string;
4180
- }
4181
- /**
4182
- * Creates a call using an embed token for simple, embeddable integrations.
4183
- *
4184
- * Handles client creation, authentication, and dialing in a single call.
4185
- *
4186
- * @param options - Embed token, host, and destination.
4187
- * @returns The created {@link Call} instance.
4188
- */
4189
- declare function embeddableCall(options: EmbeddableCallOptions): Promise<Call>;
4190
- //#endregion
4191
- //#region src/dependencies/StaticCredentialProvider.d.ts
4192
- /**
4193
- * Credential provider that returns a fixed set of credentials.
4194
- *
4195
- * Use when the token is already available (e.g. from a backend endpoint).
4196
- *
4197
- * @example
4198
- * ```ts
4199
- * const provider = new StaticCredentialProvider({ token: 'my-sat-token' });
4200
- * const client = new SignalWire(provider);
4201
- * ```
4202
- */
4203
- declare class StaticCredentialProvider implements CredentialProvider {
4204
- private credentials;
4205
- constructor(credentials: SDKCredential);
4206
- /** Returns the static credentials. */
4207
- authenticate(): Promise<SDKCredential>;
4208
- }
4209
- //#endregion
4210
- //#region src/dependencies/EmbedTokenCredentialProvider.d.ts
4211
- /** Credential provider that exchanges an embed token for a SAT via the host's token endpoint. */
4212
- declare class EmbedTokenCredentialProvider implements CredentialProvider {
4213
- private host;
4214
- private embedToken;
4215
- constructor(host: string, embedToken: string);
4216
- private fetchSAT;
4217
- authenticate(): Promise<{
4218
- token: string;
4219
- expiry_at: number;
4220
- }>;
4221
- refresh(): Promise<{
4222
- token: string;
4223
- expiry_at: number;
4224
- }>;
4225
- }
4226
- //#endregion
4227
- //#region src/controllers/LocalAudioPipeline.d.ts
4228
- /**
4229
- * Options for {@link LocalAudioPipeline}.
4230
- */
4231
- interface LocalAudioPipelineOptions {
4232
- /** Factory for AudioContext — override for tests. Defaults to `new AudioContext()`. */
4233
- audioContextFactory?: () => AudioContext;
4234
- /** Initial gain (0..2, where 1 is unity). Defaults to 1. */
4235
- initialGain?: number;
4236
- /** RMS level [0..1] above which speaking$ emits true. Defaults to {@link VAD_THRESHOLD}. */
4237
- speakingThreshold?: number;
3971
+ setLocalMicrophoneGain(value: number): void;
3972
+ /** Observable of the current local microphone gain (0..200, where 100 = unity). */
3973
+ get localMicrophoneGain$(): Observable<number>;
3974
+ /**
3975
+ * Observable of the RMS audio level of the local microphone, 0..1.
3976
+ * Emits at ~30fps while a mic track is active. Engages the local audio
3977
+ * pipeline on first subscription.
3978
+ */
3979
+ get localAudioLevel$(): Observable<number>;
4238
3980
  /**
4239
- * Milliseconds of silence below the threshold before speaking$ flips back to
4240
- * false. Prevents flicker on normal speech gaps. Defaults to {@link VAD_HOLD_MS}.
3981
+ * Observable that is `true` while the local participant is speaking
3982
+ * (RMS level above the VAD threshold, with hold time to avoid flicker).
4241
3983
  */
4242
- speakingHoldMs?: number;
4243
- /** Polling interval for level$. Defaults to {@link AUDIO_LEVEL_POLL_INTERVAL_MS}. */
4244
- pollIntervalMs?: number;
3984
+ get localSpeaking$(): Observable<boolean>;
3985
+ /**
3986
+ * Enable push-to-talk: while {@link setPushToTalkActive} has been called
3987
+ * with `false`, the microphone gain is forced to 0; calling
3988
+ * {@link setPushToTalkActive} with `true` restores the configured gain.
3989
+ * Use this instead of mute/unmute for instant talk/silence transitions
3990
+ * because it doesn't rebuild the track.
3991
+ *
3992
+ * This method installs the pipeline but does not attach any keyboard
3993
+ * listener — consumers bind the key themselves and call
3994
+ * {@link setPushToTalkActive} on keydown/keyup.
3995
+ */
3996
+ enablePushToTalk(): void;
3997
+ /** Disable push-to-talk; mic gain returns to the configured value. */
3998
+ disablePushToTalk(): void;
3999
+ /**
4000
+ * While push-to-talk is enabled, sets the talk state. `true` = transmitting,
4001
+ * `false` = silent. No-op if push-to-talk has not been enabled.
4002
+ */
4003
+ setPushToTalkActive(active: boolean): void;
4004
+ /**
4005
+ * Toggle echo cancellation on the local mic at runtime. Applied via
4006
+ * `track.applyConstraints`; browsers that don't honour runtime constraints
4007
+ * (notably iOS Safari) fall back to re-acquiring the track with the new
4008
+ * constraint set and plumbing the replacement through the local audio
4009
+ * pipeline if one is active.
4010
+ */
4011
+ setEchoCancellation(enabled: boolean): Promise<void>;
4012
+ /** Toggle browser noise suppression on the local mic at runtime. */
4013
+ setNoiseSuppression(enabled: boolean): Promise<void>;
4014
+ /** Toggle browser automatic gain control on the local mic at runtime. */
4015
+ setAutoGainControl(enabled: boolean): Promise<void>;
4016
+ /**
4017
+ * Observable of the aggregate remote audio level, 0..1 RMS. The server
4018
+ * delivers a single mixed audio stream for all remote participants — this
4019
+ * meter reports that mix. Per-participant audio is not available client-side.
4020
+ *
4021
+ * Engages a shared AudioContext on first subscription (cheap — one
4022
+ * AnalyserNode, no GainNode, no destination) so it does not affect the
4023
+ * caller's audio element playback.
4024
+ */
4025
+ get remoteAudioLevel$(): Observable<number>;
4026
+ /** Destroys the call, releasing all resources and subscriptions. */
4027
+ destroy(): void;
4028
+ /**
4029
+ * @internal Send a verto.subscribe message to add an event type to the
4030
+ * server's subscription list for this call. Returns the underlying RPC
4031
+ * promise so callers can decide whether to cache the observable on success
4032
+ * or retry on failure.
4033
+ */
4034
+ private _sendVertoSubscribe;
4245
4035
  }
4036
+ //#endregion
4037
+ //#region src/core/entities/Directory.d.ts
4246
4038
  /**
4247
- * Web Audio pipeline for the local microphone stream.
4248
- *
4249
- * Wraps the raw mic `MediaStreamTrack` in a graph of:
4250
- *
4251
- * ```
4252
- * MediaStreamAudioSourceNode → GainNode → AnalyserNode → MediaStreamAudioDestinationNode
4253
- * ```
4039
+ * Directory interface for managing addresses
4254
4040
  *
4255
- * The {@link outputTrack} from the destination node is what callers should
4256
- * attach to the `RTCRtpSender` in place of the raw mic track. The same
4257
- * destination track is reused across input changes (device switch, mute /
4258
- * unmute track replacement) so the sender reference stays stable — only the
4259
- * source end of the graph is rebuilt.
4041
+ * This is the public API contract for address directory functionality.
4042
+ * It provides access to addresses, loading capabilities, and search functionality.
4260
4043
  *
4261
- * The pipeline owns a single {@link AudioContext}. Callers must invoke
4262
- * {@link destroy} to release it when the call ends.
4044
+ * @public
4263
4045
  */
4264
- declare class LocalAudioPipeline extends Destroyable {
4265
- private readonly _audioContext;
4266
- private readonly _gainNode;
4267
- private readonly _analyser;
4268
- private readonly _destination;
4269
- private readonly _analyserBuffer;
4270
- private readonly _speakingThreshold;
4271
- private readonly _speakingHoldMs;
4272
- private readonly _pollIntervalMs;
4273
- private _inputSource;
4274
- private _inputStream;
4275
- private _lastSpokeAt;
4276
- private _gain$;
4277
- /** 1 when audio should pass through, 0 when silenced by PTT. */
4278
- private _pttMultiplier;
4279
- constructor(options?: LocalAudioPipelineOptions);
4280
- /** Observable of the current gain value (0..2). */
4281
- get gain$(): Observable<number>;
4282
- /** Current gain value (0..2). */
4283
- get gain(): number;
4046
+ interface Directory extends AddressProvider<Address> {
4284
4047
  /**
4285
- * Processed output track to attach to the RTCRtpSender. Stable reference
4286
- * across input changes, so `sender.replaceTrack(pipeline.outputTrack)` only
4287
- * needs to be called once.
4048
+ * Observable stream of all addresses in the directory
4049
+ * Emits a new array whenever addresses are added, removed, or updated
4288
4050
  */
4289
- get outputTrack(): MediaStreamTrack;
4051
+ readonly addresses$: Observable<Address[]>;
4290
4052
  /**
4291
- * Root-mean-square audio level of the input signal, 0..1. Emits on a fixed
4292
- * interval (~30fps by default).
4053
+ * Current snapshot of all addresses in the directory
4293
4054
  */
4294
- get level$(): Observable<number>;
4055
+ readonly addresses: Address[];
4295
4056
  /**
4296
- * Boolean VAD derived from {@link level$}. True while level ≥ threshold or
4297
- * during the hold window after the last frame that crossed the threshold.
4057
+ * Observable indicating whether more addresses can be loaded from the server
4298
4058
  */
4299
- get speaking$(): Observable<boolean>;
4059
+ readonly hasMore$: Observable<boolean>;
4300
4060
  /**
4301
- * Set gain multiplier applied to the input signal. 0 = silence,
4302
- * 1 = unity, 2 = 2x. Values are clamped to [0, 2]. The effective gain on
4303
- * the graph also respects the current PTT state.
4061
+ * Observable indicating the current loading state
4062
+ * Emits `true` when loading, `false` when idle
4304
4063
  */
4305
- setGain(value: number): void;
4064
+ readonly loading$: Observable<boolean>;
4065
+ readonly loading: boolean;
4306
4066
  /**
4307
- * Silence the graph when `active = false`, otherwise restore the configured
4308
- * gain. Use this from a PTT handler: released → `false`, held → `true`.
4309
- * Orthogonal to {@link setGain} — once PTT returns to active, the last
4310
- * configured gain reappears.
4067
+ * Load more addresses from the server
4068
+ * Only loads if `hasMore` is true
4311
4069
  */
4312
- setPTTActive(active: boolean): void;
4313
- private applyEffectiveGain;
4070
+ loadMore(): void;
4314
4071
  /**
4315
- * Wire a new raw mic track as the pipeline's input. Replaces any previous
4316
- * input source and reconnects the graph so {@link outputTrack} continues
4317
- * to emit the processed audio. Pass `null` to disconnect the input (the
4318
- * output track stays alive but emits silence).
4072
+ * Get a specific address by ID
4319
4073
  *
4320
- * Also resumes the underlying AudioContext on attach — Chrome creates it
4321
- * in a suspended state and the graph won't process (the destination
4322
- * track emits silence) until resume() succeeds.
4323
- */
4324
- setInputTrack(track: MediaStreamTrack | null): void;
4325
- destroy(): void;
4326
- private computeLevel;
4327
- private evaluateSpeaking;
4328
- }
4329
- //#endregion
4330
- //#region src/controllers/RTCPeerConnectionController.d.ts
4331
- interface RTCPeerConnectionControllerOptions extends MediaOptions {
4332
- callId?: string;
4333
- rtcConfiguration?: RTCConfiguration;
4334
- simulcast?: boolean;
4335
- sfu?: boolean;
4336
- msStreamsNumber?: number;
4337
- propose: RTCPeerConnectionPropose;
4338
- iceServers?: RTCIceServer[];
4339
- disableUdpIceServers?: boolean;
4340
- relayOnly?: boolean;
4341
- iceCandidateTimeout?: number;
4342
- iceGatheringTimeout?: number;
4343
- webRTCApiProvider?: WebRTCApiProvider;
4344
- /** Per-call preferred video codecs (overrides global preferences). */
4345
- preferredVideoCodecs?: string[];
4346
- /** Per-call preferred audio codecs (overrides global preferences). */
4347
- preferredAudioCodecs?: string[];
4348
- /** Per-call stereo Opus setting (overrides global preferences). */
4349
- stereo?: boolean;
4350
- }
4351
- type RTCPeerConnectionControllerOptionsPartial = Partial<RTCPeerConnectionControllerOptions>;
4352
- interface UpdateSDPStatusParams {
4353
- status: 'received' | 'sent' | 'failed';
4354
- sdp?: string;
4355
- }
4356
- declare class RTCPeerConnectionController extends Destroyable {
4357
- protected options: RTCPeerConnectionControllerOptionsPartial;
4358
- readonly id: string;
4359
- firstSDPExchangeCompleted: boolean;
4360
- sdpInit?: RTCSessionDescriptionInit;
4361
- private negotiationNeeded$;
4362
- private deviceController;
4363
- private localStreamController;
4364
- private transceiverController?;
4365
- readonly localDescription$: Observable<RTCSessionDescription | null>;
4366
- peerConnection?: RTCPeerConnection;
4367
- private initPromise?;
4368
- private connectionTimeout;
4369
- private connectionTimer?;
4370
- private oniceconnectionstatechangeHandler;
4371
- private onconnectionstatechangeHandler;
4372
- private onsignalingstatechangeHandler;
4373
- private onicegatheringstatechangeHandler;
4374
- private onnegotiationneededHandler;
4375
- private updateSelectedInputDevice;
4376
- private _isNegotiating$;
4377
- private _iceGatheringController?;
4378
- private _memberId;
4379
- private _type;
4380
- private _iceConnectionState$;
4381
- private _connectionState$;
4382
- private _signalingState$;
4383
- private _iceGatheringState$;
4384
- private _errors$;
4385
- private _iceCandidates$;
4386
- private _initialized$;
4387
- private _remoteDescription$;
4388
- private _remoteStream$;
4389
- private _remoteOfferMediaDirections;
4390
- private _localAudioPipeline;
4391
- constructor(options?: RTCPeerConnectionControllerOptionsPartial, remoteSessionDescription?: string, deviceController?: DeviceController);
4392
- private get iceGatheringController();
4393
- private get shouldEmitLocalDescription();
4394
- private removeConnectionTimer;
4395
- setMemberId(memberId: string | null): void;
4396
- get memberId(): string | null;
4397
- stopTrackSender(kind: 'audio' | 'video' | 'both', options?: {
4398
- updateTransceiverDirection: boolean;
4399
- }): void;
4400
- private stopRawAudioInputForPipeline;
4401
- get isNegotiating$(): Observable<boolean>;
4402
- get isNegotiating(): boolean;
4403
- updateMediaDevicesOptions(options: MediaOptions): void;
4404
- get iceGatheringState$(): Observable<RTCIceGatheringState>;
4405
- get mediaTrackEnded$(): Observable<MediaStreamTrack>;
4406
- get errors$(): Observable<Error>;
4407
- get iceCandidates$(): Observable<RTCIceCandidate[]>;
4408
- get initialized$(): Observable<boolean>;
4409
- get remoteDescription$(): Observable<RTCSessionDescription | null>;
4410
- get localStream$(): Observable<MediaStream | null>;
4411
- get remoteStream$(): Observable<MediaStream | null>;
4412
- get localAudioTracks$(): Observable<MediaStreamTrack[]>;
4413
- get localVideoTracks$(): Observable<MediaStreamTrack[]>;
4414
- get iceConnectionState$(): Observable<RTCIceConnectionState>;
4415
- get connectionState$(): Observable<RTCPeerConnectionState>;
4416
- get signalingState$(): Observable<RTCSignalingState>;
4417
- get type(): RTCPeerConnectionType;
4418
- get propose(): RTCPeerConnectionPropose;
4419
- get isAdditionalDevice(): boolean;
4420
- get isMainDevice(): boolean;
4421
- get isScreenShare(): boolean;
4422
- protected get iceServers(): RTCIceServer[];
4423
- private get rtcConfiguration();
4424
- get receiveVideo(): boolean;
4425
- get receiveAudio(): boolean;
4426
- get localStream(): MediaStream | null;
4427
- get remoteStream(): MediaStream | null;
4428
- private get inputAudioDeviceConstraints();
4429
- private get inputVideoDeviceConstraints();
4430
- private get WebRTCPeerConnectionConstructor();
4431
- private get offerOptions();
4432
- private get answerOptions();
4074
+ * @param addressId - The address ID to retrieve
4075
+ * @returns The address instance, or undefined if not found
4076
+ */
4077
+ get(addressId: string): Address | undefined;
4433
4078
  /**
4434
- * Initialize the RTCPeerConnection and setup event listeners.
4435
- * Called automatically when localDescription$ is subscribed to (deferred pattern).
4436
- * Uses Promise memoization to ensure initialization only happens once,
4437
- * even if called concurrently.
4079
+ * Find an address ID by searching for a name
4080
+ *
4081
+ * @param uri - The address name to search for
4082
+ * @returns Promise resolving to the address ID, or undefined if not found
4438
4083
  */
4439
- private init;
4084
+ findAddressIdByURI(uri: string): Promise<string | undefined>;
4085
+ }
4086
+ //#endregion
4087
+ //#region src/interfaces/SessionState.d.ts
4088
+ /**
4089
+ * Extended session interface that adds call management and authentication
4090
+ * state on top of the narrow ClientSession contract.
4091
+ *
4092
+ * Accessible via `client.session`. Call and CallFactory continue to depend
4093
+ * only on the narrow ClientSession interface.
4094
+ */
4095
+ interface SessionState extends ClientSession {
4440
4096
  /**
4441
- * Internal initialization implementation.
4442
- * Should only be called via init() to ensure single execution.
4097
+ * Observable stream of currently active inbound calls.
4098
+ * Filters `calls$` to only include calls with `direction === 'inbound'`.
4443
4099
  */
4444
- private doInit;
4445
- private setupPeerConnection;
4446
- private startNegotiation;
4100
+ readonly incomingCalls$: Observable<Call[]>;
4447
4101
  /**
4448
- * Create an SDP offer and set it as local description.
4102
+ * Current snapshot of active inbound calls.
4449
4103
  */
4450
- private createOffer;
4451
- updateAnswerStatus({
4452
- status,
4453
- sdp
4454
- }: UpdateSDPStatusParams): Promise<void>;
4455
- updateOfferStatus({
4456
- status,
4457
- sdp
4458
- }: UpdateSDPStatusParams): Promise<void>;
4104
+ readonly incomingCalls: Call[];
4459
4105
  /**
4460
- * Accept an inbound call by creating the SDP answer.
4461
- * Optionally override media options before the answer is generated.
4462
- * Must be called after initialization for inbound (answer-type) connections.
4106
+ * Observable stream of all currently active calls (both inbound and outbound).
4463
4107
  */
4464
- acceptInbound(mediaOverrides?: MediaOptions): Promise<void>;
4465
- private handleOfferReceived;
4466
- private readyToConnect;
4467
- private setRemoteDescriptionBefore;
4468
- protected setLocalDescription(params: RTCSessionDescriptionInit): Promise<void>;
4469
- setLocalDescriptionBefore(sdp?: string): Promise<string>;
4108
+ readonly calls$: Observable<Call[]>;
4470
4109
  /**
4471
- * Create an SDP answer and set it as local description.
4110
+ * Current snapshot of all active calls.
4472
4111
  */
4473
- private createAnswer;
4112
+ readonly calls: Call[];
4474
4113
  /**
4475
- * Setup event listeners on RTCPeerConnection for state changes.
4114
+ * Observable that emits `true` once the session has been authenticated,
4115
+ * and `false` after disconnect.
4476
4116
  */
4477
- private setupEventListeners;
4478
- private negotiationEnded;
4117
+ readonly authenticated$: Observable<boolean>;
4118
+ /**
4119
+ * Current authentication state.
4120
+ * Returns `true` if the session is currently authenticated.
4121
+ */
4122
+ readonly authenticated: boolean;
4123
+ }
4124
+ //#endregion
4125
+ //#region src/managers/ClientSessionManager.d.ts
4126
+ /**
4127
+ * Discriminated union for session authentication state.
4128
+ * clientBound is tracked separately via _wasClientBound (sticky flag)
4129
+ * to avoid dual sources of truth.
4130
+ */
4131
+ type SessionAuthState = {
4132
+ kind: 'unauthenticated';
4133
+ } | {
4134
+ kind: 'authenticated';
4135
+ };
4136
+ declare class ClientSessionManager extends Destroyable implements SessionState {
4137
+ private readonly getCredential;
4138
+ private readonly transport;
4139
+ private readonly storage;
4140
+ private readonly authorizationStateKey;
4141
+ private readonly attachManager;
4142
+ private readonly dpopManager?;
4143
+ private callFactory;
4144
+ private readonly agent;
4145
+ private readonly eventAcks;
4146
+ initialized$: Observable<boolean>;
4147
+ private authorizationState$;
4148
+ private connectVersion;
4149
+ /**
4150
+ * Optional hook called before a fresh connect on reconnect.
4151
+ * Used by SignalWire to refresh expired credentials before re-authenticating.
4152
+ * @internal
4153
+ */
4154
+ onBeforeReconnect?: () => Promise<void>;
4155
+ /**
4156
+ * Session-wide call control transport (see {@link ClientSession.callControl}).
4157
+ * Set from {@link SignalWireOptions.callControl} by SignalWire after construction;
4158
+ * defaults to `'routed'`. Read by every Call, so it needs no per-call persistence.
4159
+ */
4160
+ callControl: 'routed' | 'in-dialog';
4161
+ private _authorization$;
4162
+ private _errors$;
4163
+ private _directory?;
4164
+ private _authState$;
4165
+ /** Sticky flag — once true, stays true for the session lifetime. */
4166
+ private _wasClientBound;
4167
+ private _userInfo$;
4168
+ private _calls$;
4169
+ private _iceServers$;
4170
+ constructor(getCredential: () => SDKCredential, transport: TransportManager, storage: StorageManager, authorizationStateKey: string, deviceController: DeviceController, attachManager: AttachManager, webRTCApiProvider: WebRTCApiProvider, dpopManager?: CryptoController | undefined, networkChange$?: Observable<NetworkChangeEvent>);
4171
+ get incomingCalls$(): Observable<Call[]>;
4172
+ get incomingCalls(): Call[];
4173
+ get userInfo$(): Observable<Address | null>;
4174
+ get userInfo(): Address | null;
4175
+ get calls$(): Observable<Call[]>;
4176
+ get calls(): Call[];
4177
+ get iceServers(): RTCIceServer[] | undefined;
4178
+ get authorization$(): Observable<Authorization | undefined>;
4179
+ get authorization(): Authorization | undefined;
4180
+ get errors$(): Observable<Error>;
4181
+ get authenticated$(): Observable<boolean>;
4182
+ get authenticated(): boolean;
4183
+ /**
4184
+ * Whether this session is client-bound (using a Client Bound SAT).
4185
+ * When client-bound, DPoP proof creation failures are treated as
4186
+ * authentication errors rather than silently degraded.
4187
+ * @internal
4188
+ */
4189
+ get clientBound(): boolean;
4190
+ /** @internal Current auth state for debugging/testing. */
4191
+ get authState(): SessionAuthState;
4192
+ /**
4193
+ * Set the directory instance
4194
+ * Called by SignalWire after directory is created
4195
+ * @internal
4196
+ */
4197
+ setDirectory(directory: Directory): void;
4198
+ execute<T extends JSONRPCResponse = JSONRPCResponse>(request: JSONRPCRequest, options?: PendingRPCOptions): Promise<T>;
4199
+ send(message: JSONSerializable): void;
4200
+ private init;
4201
+ private setupMessageHandlers;
4202
+ private loadAuthorizationStateFromStorage;
4203
+ private updateAuthorizationStateInStorage;
4204
+ private get authStateEvent$();
4205
+ get signalingEvent$(): Observable<(Omit<{
4206
+ event_type: "webrtc.message";
4207
+ event_channel: EventChannel;
4208
+ timestamp: number;
4209
+ project_id?: string;
4210
+ node_id?: string;
4211
+ is_author?: boolean;
4212
+ params: WebrtcMessagePayload;
4213
+ }, "event_channel" | "project_id" | "node_id"> & {
4214
+ event_channel: string;
4215
+ project_id: string;
4216
+ node_id: string;
4217
+ }) | {
4218
+ event_type: "signalwire.authorization.state";
4219
+ params: SignalwireAuthorizationStatePayload;
4220
+ } | (Omit<{
4221
+ event_type: "call.joined";
4222
+ event_channel: EventChannel;
4223
+ timestamp: number;
4224
+ project_id?: string;
4225
+ node_id?: string;
4226
+ is_author?: boolean;
4227
+ params: CallJoinedPayload;
4228
+ }, "event_channel"> & {
4229
+ event_channel: string;
4230
+ }) | (Omit<{
4231
+ event_type: "call.left";
4232
+ event_channel: EventChannel;
4233
+ timestamp: number;
4234
+ project_id?: string;
4235
+ node_id?: string;
4236
+ is_author?: boolean;
4237
+ params: CallLeftPayload;
4238
+ }, "event_channel"> & {
4239
+ event_channel: string;
4240
+ }) | (Omit<{
4241
+ event_type: "call.updated";
4242
+ event_channel: EventChannel;
4243
+ timestamp: number;
4244
+ project_id?: string;
4245
+ node_id?: string;
4246
+ is_author?: boolean;
4247
+ params: CallUpdatedPayload;
4248
+ }, "event_channel"> & {
4249
+ event_channel: string;
4250
+ }) | (Omit<{
4251
+ event_type: "call.state";
4252
+ event_channel: EventChannel;
4253
+ timestamp: number;
4254
+ project_id?: string;
4255
+ node_id?: string;
4256
+ is_author?: boolean;
4257
+ params: CallStatePayload;
4258
+ }, "event_channel"> & {
4259
+ event_channel: string;
4260
+ }) | (Omit<{
4261
+ event_type: "call.play";
4262
+ event_channel: EventChannel;
4263
+ timestamp: number;
4264
+ project_id?: string;
4265
+ node_id?: string;
4266
+ is_author?: boolean;
4267
+ params: CallPlayPayload;
4268
+ }, "event_channel"> & {
4269
+ event_channel: string;
4270
+ }) | (Omit<{
4271
+ event_type: "call.connect";
4272
+ event_channel: EventChannel;
4273
+ timestamp: number;
4274
+ project_id?: string;
4275
+ node_id?: string;
4276
+ is_author?: boolean;
4277
+ params: CallConnectPayload;
4278
+ }, "event_channel"> & {
4279
+ event_channel: string;
4280
+ }) | (Omit<{
4281
+ event_type: "room.updated";
4282
+ event_channel: EventChannel;
4283
+ timestamp: number;
4284
+ project_id?: string;
4285
+ node_id?: string;
4286
+ is_author?: boolean;
4287
+ params: RoomUpdatedPayload;
4288
+ }, "event_channel"> & {
4289
+ event_channel: string;
4290
+ }) | Omit<{
4291
+ event_type: "member.updated";
4292
+ event_channel: EventChannel;
4293
+ timestamp: number;
4294
+ project_id?: string;
4295
+ node_id?: string;
4296
+ is_author?: boolean;
4297
+ params: MemberUpdatedPayload;
4298
+ }, never> | Omit<{
4299
+ event_type: "member.joined";
4300
+ event_channel: EventChannel;
4301
+ timestamp: number;
4302
+ project_id?: string;
4303
+ node_id?: string;
4304
+ is_author?: boolean;
4305
+ params: MemberJoinedPayload;
4306
+ }, never> | Omit<{
4307
+ event_type: "member.left";
4308
+ event_channel: EventChannel;
4309
+ timestamp: number;
4310
+ project_id?: string;
4311
+ node_id?: string;
4312
+ is_author?: boolean;
4313
+ params: MemberLeftPayload;
4314
+ }, never> | Omit<{
4315
+ event_type: "member.talking";
4316
+ event_channel: EventChannel;
4317
+ timestamp: number;
4318
+ project_id?: string;
4319
+ node_id?: string;
4320
+ is_author?: boolean;
4321
+ params: MemberTalkingPayload;
4322
+ }, never> | Omit<{
4323
+ event_type: "layout.changed";
4324
+ event_channel: EventChannel;
4325
+ timestamp: number;
4326
+ project_id?: string;
4327
+ node_id?: string;
4328
+ is_author?: boolean;
4329
+ params: LayoutChangedPayload;
4330
+ }, never> | (Omit<{
4331
+ event_type: "conversation.message";
4332
+ event_channel: EventChannel;
4333
+ timestamp: number;
4334
+ project_id?: string;
4335
+ node_id?: string;
4336
+ is_author?: boolean;
4337
+ params: ConversationMessagePayload;
4338
+ }, "event_channel" | "timestamp" | "is_author"> & {
4339
+ event_channel: string;
4340
+ timestamp: string;
4341
+ is_author: boolean;
4342
+ }) | (Omit<{
4343
+ event_type: "conversation.message.updated";
4344
+ event_channel: EventChannel;
4345
+ timestamp: number;
4346
+ project_id?: string;
4347
+ node_id?: string;
4348
+ is_author?: boolean;
4349
+ params: ConversationMessagePayload;
4350
+ }, "event_channel" | "timestamp" | "is_author"> & {
4351
+ event_channel: string;
4352
+ timestamp: string;
4353
+ is_author: boolean;
4354
+ })>;
4355
+ private get vertoInvite$();
4356
+ private get vertoAttach$();
4357
+ private get contexts();
4358
+ private get eventing();
4359
+ private get topics();
4360
+ private get authentication();
4361
+ connect(): Promise<void>;
4362
+ private handleAuthenticationError;
4479
4363
  /**
4480
- * Trigger an ICE restart through the existing negotiation pipeline.
4364
+ * Clear the resume state (authorization_state + protocol) and ask the
4365
+ * transport to reconnect. The `connected` event re-triggers
4366
+ * `authenticate()`, which now has no stored state and so performs a fresh
4367
+ * connect.
4481
4368
  *
4482
- * This creates an offer with iceRestart: true and goes through the full
4483
- * SDP pipeline (setLocalDescription → ICE gathering → localDescription$ emission).
4484
- * The caller should NOT send the SDP manually — the existing
4485
- * setupLocalDescriptionHandler in VertoManager will pick up the emission
4486
- * from localDescription$ and send it as a verto.modify.
4369
+ * This is the stale-auth-state recovery helper used by handleAuthError:
4370
+ * the server rejected a reconnect, so the resume state is discarded and a
4371
+ * fresh connect follows. Attach records are deliberately preserved — the
4372
+ * session lives on through the reconnect and reattachCalls() needs the
4373
+ * stored call references afterwards. Do NOT add detachAll() here.
4487
4374
  *
4488
- * Unlike calling pc.createOffer/setLocalDescription directly, this method:
4489
- * - Sets _isNegotiating$ so ICEGatheringController arms its timers
4490
- * - Waits for ICE gathering to complete before localDescription$ emits
4491
- * - Goes through setLocalDescriptionBefore() for any SDP munging
4492
- */
4493
- triggerIceRestart(relayOnly?: boolean): Promise<void>;
4494
- private restoreIceTransportPolicy;
4495
- /**
4496
- * Setup track handling for remote tracks.
4497
- */
4498
- private setupTrackHandling;
4499
- private setupLocalTracks;
4500
- /** True for a main connection with no local media to send. */
4501
- private hasNoLocalMediaToSend;
4502
- /** The media kinds this connection wants to send: 'audiovideo' | 'video' | 'audio'. */
4503
- private get requestedMediaKinds();
4504
- /**
4505
- * Handle a local media acquisition failure with a typed, semantically
4506
- * accurate MediaAccessError created at the acquisition site:
4507
- * - Auxiliary connections (screenshare / additional-device) throw a
4508
- * non-fatal error — VertoManager surfaces it and the call is unaffected.
4509
- * - The main connection degrades to receive-only when allowed (default),
4510
- * otherwise fails with a fatal error.
4511
- */
4512
- private handleLocalMediaFailure;
4513
- /**
4514
- * Negotiate receive-only m-lines when there are no local tracks to send.
4515
- * Only offer-type connections add transceivers — answer-type connections
4516
- * reuse the transceivers created from the remote offer.
4517
- */
4518
- private setupReceiveOnlyTransceivers;
4519
- private getUserMedia;
4520
- private getDisplayMedia;
4521
- private setupRemoteTracks;
4522
- restoreTrackSender(kind: 'audio' | 'video' | 'both'): Promise<void>;
4523
- private restoreRawAudioInputForPipeline;
4524
- /**
4525
- * Return the lazily-created {@link LocalAudioPipeline}, constructing it on
4526
- * first access. On creation the current audio sender's track is routed
4527
- * through the pipeline (input → gain → analyser → destination) and the
4528
- * sender is switched to emit the processed track. Returns `null` when no
4529
- * audio sender exists yet (pre-negotiation).
4530
- */
4531
- ensureLocalAudioPipeline(): LocalAudioPipeline | null;
4532
- /** The active LocalAudioPipeline, or null if it hasn't been created yet. */
4533
- get localAudioPipeline(): LocalAudioPipeline | null;
4534
- private applyLocalAudioPipelineToSender;
4535
- /**
4536
- * Add a local media track to the peer connection.
4537
- * @param track - The MediaStreamTrack to add
4538
- */
4539
- addLocalTrack(track: MediaStreamTrack): void;
4540
- /**
4541
- * Remove a local media track from the peer connection.
4542
- * @param trackId - The ID of the track to remove
4375
+ * Connect-time recovery only. A *request* refused on an already
4376
+ * authenticated session is never healed here: dropping the resume state
4377
+ * destroys the association between the socket and the previous session,
4378
+ * which is what reattach depends on. That path mints a fresh credential and
4379
+ * reauthenticates instead (see `SignalWire.recoverAndRetry`).
4380
+ *
4381
+ * For public teardown (disconnect/destroy), use {@link teardownSessionState}
4382
+ * instead, which clears the attach records as well.
4543
4383
  */
4544
- removeLocalTrack(trackId: string): void;
4384
+ private discardResumeStateAndReconnect;
4385
+ cleanupStoredConnectionParams(): Promise<void>;
4545
4386
  /**
4546
- * Replace all existing media tracks with a new media track.
4547
- * Convenience method for single-track scenarios.
4548
- * @param track - The MediaStreamTrack to set
4387
+ * Public-teardown helper for disconnect()/destroy(). Clears the resume
4388
+ * state (authorization_state + protocol) AND the attach records as one
4389
+ * atomic unit.
4390
+ *
4391
+ * The two stores are coupled: the backend only honors attach records
4392
+ * within the session identified by the resume state, so ending the
4393
+ * session must clear both. Clearing one without the other strands records
4394
+ * no future session can honor (disconnect) or revives a session the
4395
+ * developer explicitly ended (destroy).
4396
+ *
4397
+ * Distinct from {@link cleanupStoredConnectionParams}, which keeps the
4398
+ * attach records for the stale-auth-state recovery path.
4549
4399
  */
4550
- setLocalTrack(track: MediaStreamTrack): void;
4551
- updateSendersConstraints(kind: 'audio' | 'video', constraints?: MediaTrackConstraints): Promise<void>;
4400
+ teardownSessionState(): Promise<void>;
4401
+ protected updateAuthState(authorization_state: string): Promise<void>;
4402
+ reauthenticate(token: string, dpopToken?: string, options?: {
4403
+ clientBound?: boolean;
4404
+ }): Promise<void>;
4405
+ private authenticate;
4406
+ disconnect(): Promise<void>;
4407
+ private createInboundCall;
4552
4408
  /**
4553
- * Replace the current audio track with a new one using the given constraints.
4554
- * Used for server-pushed audio constraint changes where applyConstraints
4555
- * fails on iOS Safari. Stops the current track, acquires a new one via
4556
- * getUserMedia, and replaces the sender track.
4409
+ * Handle a server-pushed verto.attach event at the session level.
4410
+ *
4411
+ * On page reload the server detects the reconnected session and pushes
4412
+ * verto.attach for any active calls. If a call object already exists
4413
+ * (network blip, no reload), the per-call handler in VertoManager deals
4414
+ * with it. This method only creates a new call object when no existing
4415
+ * one matches the callID.
4557
4416
  */
4558
- replaceAudioTrackWithConstraints(constraints: MediaTrackConstraints): Promise<void>;
4417
+ private handleVertoAttach;
4418
+ createOutboundCall(destination: string | Address, options?: CallOptions): Promise<Call>;
4419
+ private createCall;
4420
+ destroy(): void;
4421
+ }
4422
+ declare class ClientSessionWrapper implements SessionState {
4423
+ private clientSessionManager;
4424
+ constructor(clientSessionManager: ClientSessionManager);
4425
+ get authenticated$(): Observable<boolean>;
4426
+ get authenticated(): boolean;
4559
4427
  /**
4560
- * Clean up resources and close the peer connection.
4561
- * Completes all observables to prevent memory leaks.
4428
+ * Whether the session is using a Client Bound SAT (DPoP). Sticky — set
4429
+ * when the binding is established or restored from a resumed session's
4430
+ * server authorization.
4562
4431
  */
4563
- destroy(): void;
4564
- private removeAllListeners;
4565
- private stopRemoteTracks;
4566
- get mediaDirections(): {
4567
- audio: RTCRtpTransceiverDirection;
4568
- video: RTCRtpTransceiverDirection;
4569
- };
4570
- protected _setRemoteDescription(params: RTCSessionDescriptionInit): Promise<void>;
4432
+ get clientBound(): boolean;
4433
+ get signalingEvent$(): Observable<(Omit<{
4434
+ event_type: "webrtc.message";
4435
+ event_channel: EventChannel;
4436
+ timestamp: number;
4437
+ project_id?: string;
4438
+ node_id?: string;
4439
+ is_author?: boolean;
4440
+ params: WebrtcMessagePayload;
4441
+ }, "event_channel" | "project_id" | "node_id"> & {
4442
+ event_channel: string;
4443
+ project_id: string;
4444
+ node_id: string;
4445
+ }) | {
4446
+ event_type: "signalwire.authorization.state";
4447
+ params: SignalwireAuthorizationStatePayload;
4448
+ } | (Omit<{
4449
+ event_type: "call.joined";
4450
+ event_channel: EventChannel;
4451
+ timestamp: number;
4452
+ project_id?: string;
4453
+ node_id?: string;
4454
+ is_author?: boolean;
4455
+ params: CallJoinedPayload;
4456
+ }, "event_channel"> & {
4457
+ event_channel: string;
4458
+ }) | (Omit<{
4459
+ event_type: "call.left";
4460
+ event_channel: EventChannel;
4461
+ timestamp: number;
4462
+ project_id?: string;
4463
+ node_id?: string;
4464
+ is_author?: boolean;
4465
+ params: CallLeftPayload;
4466
+ }, "event_channel"> & {
4467
+ event_channel: string;
4468
+ }) | (Omit<{
4469
+ event_type: "call.updated";
4470
+ event_channel: EventChannel;
4471
+ timestamp: number;
4472
+ project_id?: string;
4473
+ node_id?: string;
4474
+ is_author?: boolean;
4475
+ params: CallUpdatedPayload;
4476
+ }, "event_channel"> & {
4477
+ event_channel: string;
4478
+ }) | (Omit<{
4479
+ event_type: "call.state";
4480
+ event_channel: EventChannel;
4481
+ timestamp: number;
4482
+ project_id?: string;
4483
+ node_id?: string;
4484
+ is_author?: boolean;
4485
+ params: CallStatePayload;
4486
+ }, "event_channel"> & {
4487
+ event_channel: string;
4488
+ }) | (Omit<{
4489
+ event_type: "call.play";
4490
+ event_channel: EventChannel;
4491
+ timestamp: number;
4492
+ project_id?: string;
4493
+ node_id?: string;
4494
+ is_author?: boolean;
4495
+ params: CallPlayPayload;
4496
+ }, "event_channel"> & {
4497
+ event_channel: string;
4498
+ }) | (Omit<{
4499
+ event_type: "call.connect";
4500
+ event_channel: EventChannel;
4501
+ timestamp: number;
4502
+ project_id?: string;
4503
+ node_id?: string;
4504
+ is_author?: boolean;
4505
+ params: CallConnectPayload;
4506
+ }, "event_channel"> & {
4507
+ event_channel: string;
4508
+ }) | (Omit<{
4509
+ event_type: "room.updated";
4510
+ event_channel: EventChannel;
4511
+ timestamp: number;
4512
+ project_id?: string;
4513
+ node_id?: string;
4514
+ is_author?: boolean;
4515
+ params: RoomUpdatedPayload;
4516
+ }, "event_channel"> & {
4517
+ event_channel: string;
4518
+ }) | Omit<{
4519
+ event_type: "member.updated";
4520
+ event_channel: EventChannel;
4521
+ timestamp: number;
4522
+ project_id?: string;
4523
+ node_id?: string;
4524
+ is_author?: boolean;
4525
+ params: MemberUpdatedPayload;
4526
+ }, never> | Omit<{
4527
+ event_type: "member.joined";
4528
+ event_channel: EventChannel;
4529
+ timestamp: number;
4530
+ project_id?: string;
4531
+ node_id?: string;
4532
+ is_author?: boolean;
4533
+ params: MemberJoinedPayload;
4534
+ }, never> | Omit<{
4535
+ event_type: "member.left";
4536
+ event_channel: EventChannel;
4537
+ timestamp: number;
4538
+ project_id?: string;
4539
+ node_id?: string;
4540
+ is_author?: boolean;
4541
+ params: MemberLeftPayload;
4542
+ }, never> | Omit<{
4543
+ event_type: "member.talking";
4544
+ event_channel: EventChannel;
4545
+ timestamp: number;
4546
+ project_id?: string;
4547
+ node_id?: string;
4548
+ is_author?: boolean;
4549
+ params: MemberTalkingPayload;
4550
+ }, never> | Omit<{
4551
+ event_type: "layout.changed";
4552
+ event_channel: EventChannel;
4553
+ timestamp: number;
4554
+ project_id?: string;
4555
+ node_id?: string;
4556
+ is_author?: boolean;
4557
+ params: LayoutChangedPayload;
4558
+ }, never> | (Omit<{
4559
+ event_type: "conversation.message";
4560
+ event_channel: EventChannel;
4561
+ timestamp: number;
4562
+ project_id?: string;
4563
+ node_id?: string;
4564
+ is_author?: boolean;
4565
+ params: ConversationMessagePayload;
4566
+ }, "event_channel" | "timestamp" | "is_author"> & {
4567
+ event_channel: string;
4568
+ timestamp: string;
4569
+ is_author: boolean;
4570
+ }) | (Omit<{
4571
+ event_type: "conversation.message.updated";
4572
+ event_channel: EventChannel;
4573
+ timestamp: number;
4574
+ project_id?: string;
4575
+ node_id?: string;
4576
+ is_author?: boolean;
4577
+ params: ConversationMessagePayload;
4578
+ }, "event_channel" | "timestamp" | "is_author"> & {
4579
+ event_channel: string;
4580
+ timestamp: string;
4581
+ is_author: boolean;
4582
+ })>;
4583
+ get iceServers(): RTCIceServer[] | undefined;
4584
+ get callControl(): 'routed' | 'in-dialog';
4585
+ execute<T extends JSONRPCResponse = JSONRPCResponse>(request: JSONRPCRequest, options?: PendingRPCOptions): Promise<T>;
4586
+ get incomingCalls$(): Observable<Call[]>;
4587
+ get incomingCalls(): Call[];
4588
+ get calls$(): Observable<Call[]>;
4589
+ get calls(): Call[];
4571
4590
  }
4572
4591
  //#endregion
4573
- //#region src/interfaces/WebRTCVerto.d.ts
4592
+ //#region src/core/types/warnings.types.d.ts
4574
4593
  /**
4575
- * Extended interface for WebRTC Verto Manager
4576
- * Includes WebRTC-specific state and peer connection management
4594
+ * Non-fatal warning emitted via {@link SignalWire.warnings$ | client.warnings$}.
4595
+ *
4596
+ * Use to detect SDK behaviors that affect session liveness or developer-facing
4597
+ * contracts but do not warrant disconnection. Discriminated by `code`.
4598
+ *
4599
+ * Existing consumers of `errors$` are NOT notified — `warnings$` is a separate
4600
+ * channel so application code can react to warnings without triggering
4601
+ * error-handling code paths (e.g., disconnect cascades, user-facing toasts).
4577
4602
  */
4578
- interface WebRTCVerto extends VertoManager {
4579
- readonly selfId$: Observable<string | null>;
4580
- readonly selfId: string | null;
4581
- readonly nodeId$: Observable<string | null>;
4582
- readonly nodeId: string | null;
4583
- readonly localStream$: Observable<MediaStream>;
4584
- readonly localStream: MediaStream | null;
4585
- readonly remoteStream$: Observable<MediaStream>;
4586
- readonly remoteStream: MediaStream | null;
4587
- readonly mediaDirections$: Observable<MediaDirections>;
4588
- readonly mediaDirections: MediaDirections;
4589
- readonly signalingStatus$: Observable<SignalingStatus>;
4590
- readonly mainPeerConnection: RTCPeerConnectionController;
4591
- bye(cause?: string): Promise<void>;
4592
- sendDigits(dtmf: string): Promise<void>;
4593
- /**
4594
- * Send a member-control op in-dialog via verto.info (no self/target member
4595
- * tuple). The payload rides in the verto.info `params.command` body — a
4596
- * sibling of `dialogParams`, same level as `dtmf` — matched to this call's
4597
- * channel by `dialogParams.callID`, so control lands on the call's own channel
4598
- * without relying on the {node_id,call_id,member_id} addressing the routed
4599
- * transport uses.
4600
- */
4601
- sendCallControl(method: string, params: Record<string, unknown>): Promise<unknown>;
4602
- hold(): Promise<void>;
4603
- unhold(): Promise<void>;
4604
- destroy(): void;
4605
- transfer(options: TransferOptions): Promise<void>;
4606
- /** Request a video keyframe via verto.modify. */
4607
- requestKeyframe?: () => void;
4608
- /** Request an ICE restart via verto.modify with iceRestart offer. */
4609
- requestIceRestart?: (relayOnly?: boolean) => Promise<void>;
4610
- /** Request an ICE restart on all active peer connections (multi-leg). */
4611
- requestIceRestartAll?: (relayOnly?: boolean) => Promise<void>;
4612
- /** Request keyframes on all video-receiving legs (skips send-only screen share). */
4613
- requestKeyframeAll?: () => void;
4614
- /** Lazily create (or return) the local audio pipeline for the main peer connection. */
4615
- ensureLocalAudioPipeline(): LocalAudioPipeline | null;
4616
- /** Current local audio pipeline, or null if it has not been created yet. */
4617
- readonly localAudioPipeline: LocalAudioPipeline | null;
4618
- }
4619
- //#endregion
4620
- //#region src/managers/CallEventsManager.d.ts
4621
- interface WebRTCCallEventManagerOptions {}
4622
- /** @internal */
4623
- declare class CallEventsManager extends Destroyable {
4624
- protected webRtcCallSession: CallManager;
4625
- protected options: WebRTCCallEventManagerOptions;
4626
- private selfId?;
4627
- private originCallId?;
4628
- private callIds;
4629
- private roomSessionIds;
4630
- private _participants$;
4631
- private _self$;
4632
- private _sessionState$;
4633
- constructor(webRtcCallSession: CallManager, options?: WebRTCCallEventManagerOptions);
4634
- get participants$(): Observable<CallParticipant[]>;
4635
- get participants(): CallParticipant[];
4636
- get self$(): Observable<CallSelfParticipant>;
4637
- isRoomSessionIdValid(roomSessionId: string): boolean;
4638
- addCallId(callId: string): void;
4639
- isCallIdValid(callId: string): boolean;
4640
- get recording$(): Observable<boolean>;
4641
- get recordings$(): Observable<Record<string, unknown>[]>;
4642
- get streaming$(): Observable<boolean>;
4643
- get streams$(): Observable<Record<string, unknown>[]>;
4644
- get playbacks$(): Observable<Record<string, unknown>[]>;
4645
- get raiseHandPriority$(): Observable<boolean>;
4646
- get locked$(): Observable<boolean>;
4647
- get meta$(): Observable<Record<string, unknown>>;
4648
- get capabilities$(): Observable<Capability[]>;
4649
- get layout$(): Observable<string>;
4650
- get layouts$(): Observable<string[]>;
4651
- get layoutLayers$(): Observable<LayoutLayer[]>;
4652
- get self(): CallSelfParticipant | null;
4653
- get layoutLayers(): LayoutLayer[];
4654
- get recording(): boolean;
4655
- get streaming(): boolean;
4656
- get raiseHandPriority(): boolean;
4657
- get locked(): boolean;
4658
- get meta(): Record<string, unknown>;
4659
- get layout(): string | undefined;
4660
- get layouts(): string[];
4661
- get capabilities(): Capability[];
4662
- isSessionEvent(id: string): boolean;
4663
- protected initSubscriptions(): void;
4664
- private updateParticipantPositions;
4665
- updateLayouts(): void;
4666
- private updateParticipants;
4667
- private upsertParticipant;
4668
- private get callJoinedEvent$();
4669
- private get layoutChangedEvent$();
4670
- private get memberUpdates$();
4671
- destroy(): void;
4672
- }
4673
- //#endregion
4674
- //#region src/managers/CallRecoveryManager.d.ts
4675
- type RecoveryState$1 = 'idle' | 'debouncing' | 'recovering' | 'cooldown';
4676
- interface RecoveryEvent$1 {
4677
- action: 'keyframe_requested' | 'reinvite_started' | 'reinvite_succeeded' | 'reinvite_failed' | 'reinvite_timeout' | 'max_attempts_reached' | 'signal_reconnect' | 'full_reconnect' | 'video_disabled' | 'video_restored';
4678
- reason: string;
4679
- attempt?: number;
4680
- maxAttempts?: number;
4681
- timestamp: number;
4603
+ type SDKWarning = CredentialRefreshFallbackWarning | CredentialNoRefreshHandlerWarning;
4604
+ /**
4605
+ * Diagnostic detail for {@link CredentialRefreshFallbackWarning}. Stable
4606
+ * values, but treat unknown strings as "fell back for an unspecified cause" —
4607
+ * do not branch on this value for control flow. New values may be added in
4608
+ * future releases.
4609
+ */
4610
+ type CredentialRefreshFallbackReason = 'no-scope' | 'no-dpop-support' | 'endpoint-failed' | 'activation-timeout' | (string & {});
4611
+ /**
4612
+ * Emitted when the SDK falls back to the developer-provided
4613
+ * {@link CredentialProvider.refresh} because the Client Bound SAT path
4614
+ * could not take over.
4615
+ *
4616
+ * Common causes:
4617
+ * - The minted SAT lacks `sat:refresh` scope (`reason: 'no-scope'`).
4618
+ * - The `/devices/token` exchange failed transiently (`reason: 'endpoint-failed'`).
4619
+ *
4620
+ * Subscribe to this warning to detect:
4621
+ * - SDKs running with plain SATs that rely on developer-managed refresh
4622
+ * - Deployments expected to use bound tokens that silently downgraded to bearer
4623
+ * (a security-relevant signal for fleet observability)
4624
+ */
4625
+ interface CredentialRefreshFallbackWarning {
4626
+ code: 'credential_refresh_fallback';
4627
+ source: 'CredentialProvider';
4628
+ reason: CredentialRefreshFallbackReason;
4629
+ message: string;
4682
4630
  }
4683
- //#endregion
4684
- //#region src/utils/qualityScore.d.ts
4685
4631
  /**
4686
- * MOS (Mean Opinion Score) quality computation based on the simplified
4687
- * ITU-T G.107 E-model.
4632
+ * Emitted when a credential has an `expiry_at` but the provider supplies no
4633
+ * `refresh()` handler. The session will terminate at expiry with no fallback.
4688
4634
  *
4689
- * Provides a single 1-5 number that applications can use for a
4690
- * green / yellow / red quality indicator without understanding raw
4691
- * jitter and packet-loss values.
4635
+ * Implementors who want long-lived sessions must provide a `refresh()` handler
4636
+ * or mint tokens with the `sat:refresh` scope (Client Bound SAT path).
4692
4637
  */
4693
- type QualityLevel$1 = 'excellent' | 'good' | 'fair' | 'poor' | 'critical';
4638
+ interface CredentialNoRefreshHandlerWarning {
4639
+ code: 'credential_no_refresh_handler';
4640
+ source: 'CredentialProvider';
4641
+ message: string;
4642
+ /** Token expiry timestamp (epoch milliseconds). */
4643
+ expiresAt: number;
4644
+ }
4694
4645
  //#endregion
4695
- //#region src/core/entities/Call.d.ts
4646
+ //#region src/utils/logger.d.ts
4647
+ /** Log level names supported by the SDK. */
4648
+ type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent';
4696
4649
  /**
4697
- * Manager instances returned by initialization callback
4650
+ * Logger interface that consumers can implement to replace the built-in logger.
4651
+ * All methods accept variadic arguments matching the browser console API.
4698
4652
  */
4699
- interface CallManagers {
4700
- vertoManager: WebRTCVerto;
4701
- callEventsManager: CallEventsManager;
4653
+ interface SDKLogger {
4654
+ error(...args: unknown[]): void;
4655
+ warn(...args: unknown[]): void;
4656
+ info(...args: unknown[]): void;
4657
+ debug(...args: unknown[]): void;
4658
+ trace(...args: unknown[]): void;
4659
+ }
4660
+ /** Options for WebSocket traffic logging. */
4661
+ interface WsTrafficOptions {
4662
+ type: 'send' | 'recv' | 'http';
4663
+ /** Parsed object or raw string — will be JSON.stringify'd for display if an object. */
4664
+ payload: unknown;
4702
4665
  }
4703
4666
  /**
4704
- * Initialization callback that creates managers for a Call instance
4705
- * @param call - The WebRTCCall instance being initialized
4706
- * @returns Manager instances for the call
4667
+ * Options for WebSocket traffic logging using raw strings.
4668
+ * The string is only parsed when logging is enabled, avoiding
4669
+ * unnecessary JSON.parse on every message.
4707
4670
  */
4708
- type ManagerInitializer = (call: WebRTCCall) => CallManagers;
4671
+ interface WsTrafficRawOptions {
4672
+ type: 'send' | 'recv';
4673
+ raw: string;
4674
+ }
4675
+ /** Debug options that control verbose SDK logging. */
4676
+ interface DebugOptions {
4677
+ /** Log all WebSocket send/recv traffic to the console. */
4678
+ logWsTraffic?: boolean;
4679
+ }
4680
+ /** Extended logger with SDK-internal helpers (wsTraffic). */
4681
+ interface InternalSDKLogger extends SDKLogger {
4682
+ wsTraffic: (options: WsTrafficOptions | WsTrafficRawOptions) => void;
4683
+ }
4684
+ /** Replace the built-in logger with a custom implementation. Pass `null` to restore defaults. */
4685
+ declare const setLogger: (logger: SDKLogger | null) => void;
4686
+ /** Configure debug options (e.g., `{ logWsTraffic: true }`). */
4687
+ declare const setDebugOptions: (options: DebugOptions | null) => void;
4709
4688
  /**
4710
- * Required initialization configuration for Call constructor.
4711
- * Calls must be created via {@link CallFactory} which provides these dependencies.
4689
+ * Set the log level for the built-in logger.
4690
+ * Has no effect when a custom logger is set via `setLogger()`.
4712
4691
  */
4713
- interface CallInitialization {
4692
+ declare const setLogLevel: (level: LogLevel) => void;
4693
+ declare const getLogger: () => InternalSDKLogger;
4694
+ //#endregion
4695
+ //#region src/clients/SignalWire.d.ts
4696
+ /** Options for constructing a {@link SignalWire}. */
4697
+ interface SignalWireOptions {
4698
+ /** Skip automatic WebSocket connection on construction. */
4699
+ skipConnection?: boolean;
4700
+ /** Skip automatic user registration on construction. */
4701
+ skipRegister?: boolean;
4702
+ /** Skip monitoring media device changes. */
4703
+ skipDeviceMonitoring?: boolean;
4704
+ /** Whether to reconnect to previously attached calls. */
4705
+ reconnectAttachedCalls?: boolean;
4706
+ /** Whether to save preferences. */
4707
+ savePreferences?: boolean;
4714
4708
  /**
4715
- * Callback function that creates and wires manager instances
4709
+ * Persist the session across page reloads.
4710
+ *
4711
+ * When `true`, credential, authorization state, and protocol are stored in
4712
+ * `localStorage` (survives reload). The DPoP key pair is persisted in
4713
+ * IndexedDB. On reload, the SDK restores the session from cache
4714
+ * without calling `credentialProvider.authenticate()`.
4715
+ *
4716
+ * When `false` (default), session data lives in `sessionStorage` and is
4717
+ * lost on reload.
4718
+ *
4719
+ * Both {@link SignalWire.disconnect | disconnect()} and
4720
+ * {@link SignalWire.destroy | destroy()} end the session and clear the
4721
+ * persisted resume state and attach records; credentials and device
4722
+ * preferences survive. Use `resetToDefaults()` for a full wipe, or
4723
+ * `unregister()` to temporarily stop receiving inbound calls while keeping
4724
+ * the session alive.
4716
4725
  */
4717
- initializeManagers: ManagerInitializer;
4726
+ persistSession?: boolean;
4727
+ /** Custom storage implementation for persistence. */
4728
+ storageImplementation?: Storage;
4729
+ /** Custom WebSocket constructor */
4730
+ webSocketConstructor?: WebSocketAdapter | NodeSocketAdapter;
4731
+ /** Custom WebRTC API provider */
4732
+ webRTCApiProvider?: WebRTCApiProvider;
4718
4733
  /**
4719
- * Device controller for media device access
4734
+ * Custom logger implementation. Must implement the {@link SDKLogger} interface.
4735
+ * Pass `null` to restore the built-in logger.
4736
+ *
4737
+ * **Note:** Logger configuration is global — setting it on one instance affects all instances.
4720
4738
  */
4721
- deviceController: DeviceController;
4739
+ logger?: SDKLogger | null;
4722
4740
  /**
4723
- * Network change events for feeding recovery pipeline
4741
+ * Log level for the built-in logger.
4742
+ * Default: `'warn'`. Set to `'debug'` for verbose SDK output.
4743
+ * Has no effect when a custom `logger` is provided.
4744
+ *
4745
+ * **Note:** Logger configuration is global — setting it on one instance affects all instances.
4724
4746
  */
4725
- networkChange$?: Observable<NetworkChangeEvent>;
4747
+ logLevel?: LogLevel;
4748
+ /** Debug options for verbose SDK diagnostics (e.g., `{ logWsTraffic: true }`). */
4749
+ debug?: DebugOptions;
4750
+ /**
4751
+ * Control transport for `call.*` verbs across ALL calls in this session:
4752
+ * - `'routed'` (default) sends them on the client's session channel.
4753
+ * - `'in-dialog'` carries them on each call's own signaling channel via `verto.info`,
4754
+ * so control works without the client needing to know how the conference is hosted.
4755
+ *
4756
+ * `'in-dialog'` is opt-in because it does not reach everywhere `'routed'` does: use it
4757
+ * only for calls that join a conference over SWML (e.g. an SWML `join_conference`).
4758
+ *
4759
+ * It is a client-wide setting rather than per-`dial()` because the SDK issues some
4760
+ * control RPCs itself (fetching the layout list on join, for example), and those must
4761
+ * travel the same way as the app's own, or reads and writes land on different transports.
4762
+ *
4763
+ * @experimental A rollout switch, not a long-term part of the API. Expected to
4764
+ * disappear once `'in-dialog'` becomes the only transport, so it carries no semver
4765
+ * promise and application code should not depend on it.
4766
+ */
4767
+ callControl?: 'routed' | 'in-dialog';
4768
+ }
4769
+ /** Options for {@link SignalWire.dial}. Extends {@link MediaOptions} with dial-specific settings. */
4770
+ interface DialOptions extends MediaOptions {
4771
+ /** Preferred video codecs for this call (overrides global preferences). */
4772
+ preferredVideoCodecs?: string[];
4773
+ /** Preferred audio codecs for this call (overrides global preferences). */
4774
+ preferredAudioCodecs?: string[];
4775
+ /** Enable stereo Opus for this call (overrides global preferences). */
4776
+ stereo?: boolean;
4777
+ /** Optional node ID for routing the call */
4778
+ nodeId?: string;
4779
+ /**
4780
+ * Custom variables sent with the Verto invite. Merged with
4781
+ * `client.preferences.userVariables` and any query-string variables on the
4782
+ * destination URI; values here take precedence.
4783
+ */
4784
+ userVariables?: Record<string, unknown>;
4726
4785
  }
4727
4786
  /**
4728
- * Concrete WebRTC call implementation.
4787
+ * Main entry point for the SignalWire Browser SDK.
4729
4788
  *
4730
- * Manages the full lifecycle of a call including signaling, media streams,
4731
- * participants, layout, and event routing. Created via {@link SignalWire.dial}
4732
- * or received as an inbound call.
4789
+ * Manages authentication, WebSocket transport, call creation, and media devices.
4790
+ *
4791
+ * @example
4792
+ * ```ts
4793
+ * const client = new SignalWire(credentialProvider);
4794
+ * client.isConnected$.subscribe(connected => console.log('Connected:', connected));
4795
+ * const call = await client.dial('/public/my-room');
4796
+ * ```
4733
4797
  */
4734
- declare class WebRTCCall extends Destroyable implements CallManager {
4735
- clientSession: ClientSession;
4736
- options: CallOptions;
4737
- address?: Address | undefined;
4738
- /** Unique identifier for this call. */
4739
- readonly id: string;
4740
- /** Destination URI this call was placed to. */
4741
- to?: string;
4742
- private vertoManager;
4743
- private callEventsManager;
4744
- private participantFactory;
4745
- private _errors$;
4746
- private _status$;
4747
- private _lastMergedStatus;
4748
- private _answered$;
4749
- private _answerMediaOptions?;
4750
- private _holdState;
4751
- private _userVariables$;
4752
- private _statsMonitor?;
4753
- private _recoveryManager?;
4754
- private _networkChange$?;
4755
- private _networkIssues$;
4756
- private _networkMetrics$;
4757
- private _isNetworkHealthy$;
4758
- private _qualityScore$;
4759
- private _qualityLevel$;
4760
- private _recoveryState$;
4761
- private _recoveryEvent$;
4762
- private _bandwidthConstrained$;
4763
- private _mediaParamsUpdated$;
4764
- private _customSubscriptions;
4765
- private _pushToTalkEnabled;
4766
- private _remoteAudioMeter;
4767
- constructor(clientSession: ClientSession, options: CallOptions, initialization: CallInitialization, address?: Address | undefined);
4768
- /** Observable stream of errors from media, signaling, and peer connection layers. */
4769
- get errors$(): Observable<CallError>;
4798
+ declare class SignalWire extends Destroyable implements DeviceController {
4799
+ /** Global SDK preferences (timeouts, ICE config, media defaults). */
4800
+ preferences: ClientPreferences;
4801
+ private _user$;
4802
+ private _directory$;
4803
+ private _transport;
4804
+ private _clientSession;
4805
+ private _publicSession;
4806
+ private _deviceController;
4807
+ private _attachManager?;
4770
4808
  /**
4771
- * @internal Push an error to the call's error stream.
4772
- * Fatal errors automatically transition the call to `'failed'` and destroy it.
4809
+ * Set once a credential recovery has been *verified* — reauthenticated and
4810
+ * then proven by the operation it was meant to unblock. Read by the attach
4811
+ * path, which may only discard an attach record when a reattach is refused
4812
+ * on a credential the server has already accepted.
4773
4813
  */
4774
- emitError(callError: CallError): void;
4775
- /** Notify the recovery manager that a verto.modify signaling exchange failed. */
4776
- notifyModifyFailed(): void;
4777
- /** Whether this call is `'inbound'` or `'outbound'`. */
4778
- get direction(): CallDirection;
4779
- /** Observable of the address associated with this call. */
4780
- get address$(): Observable<Address | undefined>;
4781
- /** Display name of the caller. */
4782
- get fromName(): string | undefined;
4783
- /** Address URI of the caller. */
4784
- get from(): string | undefined;
4785
- /** Display name of the callee. */
4786
- get toName(): string | undefined;
4787
- /** Toggles whether incoming video is received. @throws {UnimplementedError} Not yet implemented. */
4788
- toggleIncomingVideo(): Promise<void>;
4789
- /** Toggles whether incoming audio is received. @throws {UnimplementedError} Not yet implemented. */
4790
- toggleIncomingAudio(): Promise<void>;
4791
- /** @internal Registers an additional call ID for event routing. */
4792
- addCallId(callId: string): void;
4793
- /** List of capabilities available in the current call. */
4794
- get capabilities(): Capability[];
4795
- /** Current snapshot of all participants in the call. */
4796
- get participants(): CallParticipant[];
4797
- /** The local participant, or `null` if not yet joined. */
4798
- get self(): CallSelfParticipant | null;
4799
- /** Toggles the call lock state, preventing or allowing new participants from joining. */
4800
- toggleLock(): Promise<void>;
4814
+ private _credentialRecovered;
4815
+ private _isConnected$;
4816
+ private _isRegistered$;
4817
+ private _errors$;
4818
+ private _warnings$;
4819
+ private _options;
4820
+ private _dpopManager?;
4821
+ private _refreshCoordinator?;
4822
+ /** The refresh path's own HTTP controller — see resolveCredentials. */
4823
+ private _refreshHttp?;
4824
+ /** Host `_refreshHttp` was built against, so it can be rebuilt when the token's `ch` changes. */
4825
+ private _refreshHttpHost?;
4826
+ private _credentialProvider?;
4827
+ private _deps;
4828
+ private _networkMonitor?;
4829
+ private _visibilityController?;
4830
+ private _diagnosticsCollector?;
4831
+ private _platformCapabilities?;
4801
4832
  /**
4802
- * Toggles the hold state of the call (pauses/resumes local media transmission).
4833
+ * Creates a new SignalWire client and begins connecting.
4803
4834
  *
4804
- * Distinct from {@link Participant.toggleMute} which mutes individual tracks.
4835
+ * @param credentialProvider - Provider that supplies authentication credentials.
4836
+ * @param options - Configuration options (connection, device monitoring, preferences).
4805
4837
  */
4806
- toggleHold(): Promise<void>;
4807
- /** @throws {UnimplementedError} Not yet implemented. Status tracked via {@link recording$}. */
4808
- startRecording(): Promise<void>;
4809
- /** @throws {UnimplementedError} Not yet implemented. Status tracked via {@link streaming$}. */
4810
- startStreaming(): Promise<void>;
4838
+ constructor(credentialProvider: CredentialProvider | undefined, options?: SignalWireOptions);
4811
4839
  /**
4812
- * Replaces the call's custom metadata.
4813
- * @param _meta - Metadata object to set.
4814
- * @throws {UnimplementedError} Not yet implemented.
4840
+ * Build the refresh path's own HTTP controller, against whatever host is current.
4841
+ *
4842
+ * Called on first use rather than up front, so `apiHost` already reflects the
4843
+ * token's `ch` claim. Same credential source as the container's controller — only
4844
+ * the instance, and therefore its observable streams, is separate.
4815
4845
  */
4816
- setMeta(_meta: Record<string, unknown>): Promise<void>;
4846
+ private createRefreshHttpController;
4817
4847
  /**
4818
- * Merges values into the call's custom metadata (unlike {@link setMeta} which replaces).
4819
- * @param _meta - Metadata to merge.
4820
- * @throws {UnimplementedError} Not yet implemented.
4848
+ * Initializes DPoP if not already set up. Returns the fingerprint on success.
4821
4849
  */
4822
- updateMeta(_meta: Record<string, unknown>): Promise<void>;
4823
- /** Observable of layout layer positions for all participants. */
4824
- get layoutLayers$(): Observable<LayoutLayer[]>;
4825
- /** Current snapshot of layout layers. */
4826
- get layoutLayers(): LayoutLayer[];
4850
+ private initDPoP;
4827
4851
  /**
4828
- * Executes a Verto RPC method targeting a specific participant.
4829
- *
4830
- * Constructs call context (node_id, call_id, member_id) and sends the RPC request.
4852
+ * Resolves credentials using cache-first strategy when persistSession is enabled.
4831
4853
  *
4832
- * @param target - Target {@link MemberTarget} triple, or the local member's
4833
- * ID string for self-operations (any other string is rejected — a bare
4834
- * member id cannot carry the remote member's own call context).
4835
- * @param method - Verto method name (e.g. `'call.mute'`, `'call.member.remove'`).
4836
- * @param args - Parameters for the RPC method.
4837
- * @returns The RPC response.
4838
- * @throws {CallNotReadyError} If the call has no self member context yet.
4839
- * @throws {InvalidParams} If a string target is not the local member's ID.
4840
- * @throws {JSONRPCError} If the RPC call returns an error.
4854
+ * 1. If persistSession → check localStorage for cached credential
4855
+ * 2. If cached and not expired → use it (skip provider.authenticate())
4856
+ * 3. If no cache or expired → call provider.authenticate()
4857
+ * 4. If no provider AND no cache → throw
4841
4858
  */
4842
- executeMethod<T extends JSONRPCResponse = JSONRPCResponse>(target: string | MemberTarget, method: string, args: Record<string, unknown>): Promise<T>;
4859
+ private resolveCredentials;
4860
+ private validateCredentials;
4843
4861
  /**
4844
- * `executeMethod` for a call opened with `callControl: 'in-dialog'`.
4845
- *
4846
- * Translates the routed transport's calling convention into the in-dialog one. No
4847
- * `self` tuple is sent, but a `target` is — the same {call_id, member_id} the routed
4848
- * transport puts in `target` (minus node_id), for self-ops and cross-member ops alike.
4849
- *
4850
- * Target shapes are per-verb and irregular, so they are centralised here rather
4851
- * than left to callers: most verbs take a singular `target`, `call.member.remove`
4852
- * takes a plural `targets` array, and `call.member.position.set` takes a flat
4853
- * `targets` of `{call_id, position}` — the one verb keyed on call_id rather than
4854
- * member_id, so the member triple `Participant.setPosition` built is unwrapped.
4862
+ * Reauthenticate the currently-open session with a freshly obtained
4863
+ * credential so the new token takes effect on the live socket immediately —
4864
+ * not just on the next reconnect. No-op when the session is not
4865
+ * connected/authenticated or the credential carries no token (e.g. an
4866
+ * authorization-state-only refresh). Non-fatal: reauth failures surface on
4867
+ * `errors$` without aborting the refresh that triggered this.
4855
4868
  */
4856
- private executeMethodInDialog;
4869
+ private reauthenticateLiveSession;
4857
4870
  /**
4858
- * Sends a `call.*` control verb **in-dialog** via `verto.info`, as an alternative
4859
- * to the routed {@link executeMethod} transport.
4860
- *
4861
- * Why both exist: `executeMethod` addresses the member with an explicit
4862
- * `{node_id, call_id, member_id}` tuple, which does not resolve for every conference,
4863
- * so the op can fail. An in-dialog frame carries the verb on the member's own
4864
- * signaling channel instead, so control works without the client needing to know how
4865
- * the conference is hosted.
4866
- *
4867
- * The trade-off is reach: the in-dialog transport is only accepted for calls that
4868
- * join a conference over SWML (e.g. an SWML `join_conference`); use the routed
4869
- * default otherwise.
4870
- *
4871
- * `params` are sent verbatim — nothing is built for you, which includes the target.
4872
- * **A self-directed op still needs one**, or it is refused; name yourself explicitly:
4873
- *
4874
- * ```ts
4875
- * const { call_id, member_id } = call.self.target;
4876
- * await call.sendCommand('call.mute', { channels: ['audio'], target: { call_id, member_id } });
4877
- * ```
4878
- *
4879
- * Never include `node_id` — only the two ids. The shapes are per-verb: most take a
4880
- * singular `target`, `call.member.remove` takes a plural `targets` array, and
4881
- * `call.member.position.set` takes a flat `targets: [{call_id, position}]` (the one
4882
- * verb keyed on `call_id` rather than `member_id`). Verbs that act on the call as a
4883
- * whole, or that the SDK does not wrap at all, take no target.
4871
+ * Recover a session the server is refusing: mint a fresh credential,
4872
+ * reauthenticate the live session with it, and retry the operation.
4884
4873
  *
4885
- * For the typed alternative that handles all of this, create the client with
4886
- * `callControl: 'in-dialog'` and use the ordinary `Call`/`Participant` methods.
4874
+ * The connection is deliberately kept. A reload authenticates the new socket
4875
+ * against the persisted `authorization_state`, and that handshake is what
4876
+ * associates the socket with the previous session — the association reattach
4877
+ * depends on. `signalwire.reauthenticate` swaps the credential *on that same
4878
+ * session*, so recovery never touches the resume state. Discarding it would
4879
+ * heal the credential by destroying the very thing the caller is trying to
4880
+ * get back to.
4887
4881
  *
4888
- * @internal Not part of the supported surface while the in-dialog transport is still
4889
- * rolling out. `WebRTCCall` is exported from the package entry, so without this tag
4890
- * TypeDoc publishes the method — and the example above — as public API.
4882
+ * The operation is still the verdict, never the RPC. Reauthenticating with
4883
+ * the in-memory token is accepted by a resume even while requests stay
4884
+ * refused, because the persisted `authorization_state` short-circuits token
4885
+ * validation — and `signalwire.reauthenticate` with a *freshly minted* token
4886
+ * has also been observed accepted while `subscriber.online` keeps being
4887
+ * refused (staging run 33826974634). Both look like success and are not.
4891
4888
  *
4892
- * @param method - A `call.*` method name (e.g. `'call.mute'`).
4893
- * @param params - Method parameters, sent verbatim.
4894
- * @returns The method's own reply, unwrapped from the `verto.info` envelope.
4895
- * @throws {JSONRPCError} If the control op fails.
4889
+ * @returns the operation's value, or the reason recovery could not deliver
4890
+ * one. `error` is undefined when there was no way to mint at all.
4896
4891
  */
4897
- sendCommand<T extends JSONRPCResponse = JSONRPCResponse>(method: string, params?: Record<string, unknown>): Promise<T>;
4892
+ private recoverAndRetry;
4898
4893
  /**
4899
- * The local leg's member triple — sent as `self` in every member RPC
4900
- * envelope, and as the `target` of call-scoped self-operations (e.g. lock,
4901
- * layout).
4894
+ * Re-mint a credential and adopt it only if the live session accepts it.
4902
4895
  *
4903
- * @throws {CallNotReadyError} Before `call.joined` delivers the self member
4904
- * context (`selfId`/`nodeId`) — an RPC without it cannot be routed, so fail
4905
- * fast instead of sending a doomed request.
4906
- */
4907
- private get callSelf();
4908
- /** Observable of the current call status (e.g. `'ringing'`, `'connected'`). */
4909
- get status$(): Observable<CallStatus>;
4910
- /** Observable of the participants list, emits on join/leave/update. */
4911
- get participants$(): Observable<CallParticipant[]>;
4912
- /** Observable of the local (self) participant. */
4913
- get self$(): Observable<CallSelfParticipant>;
4914
- /** Observable indicating whether the call is being recorded. */
4915
- get recording$(): Observable<boolean>;
4916
- /** Observable indicating whether the call is being streamed. */
4917
- get streaming$(): Observable<boolean>;
4918
- /** Observable indicating whether raise-hand priority is active. */
4919
- get raiseHandPriority$(): Observable<boolean>;
4920
- /** Observable indicating whether the call room is locked. */
4921
- get locked$(): Observable<boolean>;
4922
- /** Observable of custom metadata associated with the call. */
4923
- get meta$(): Observable<Record<string, unknown>>;
4924
- /** Observable of the call's capability flags. */
4925
- get capabilities$(): Observable<Capability[]>;
4926
- /** Observable of the current layout name. */
4927
- get layout$(): Observable<string>;
4928
- /** Current call status. */
4929
- get status(): CallStatus;
4930
- /** Whether the call is currently being recorded. */
4931
- get recording(): boolean;
4932
- /** Whether the call is currently being streamed. */
4933
- get streaming(): boolean;
4934
- /** Whether raise-hand priority is active. */
4935
- get raiseHandPriority(): boolean;
4936
- /** Whether the call room is locked. */
4937
- get locked(): boolean;
4938
- /** Current custom metadata of the call. */
4939
- get meta(): Record<string, unknown>;
4940
- /** Current layout name, or `undefined` if not set. */
4941
- get layout(): string | undefined;
4942
- /** Observable of available layout names. */
4943
- get layouts$(): Observable<string[]>;
4944
- /** Current snapshot of available layout names. */
4945
- get layouts(): string[];
4946
- /** Observable of the local media stream (camera/microphone). */
4947
- get localStream$(): Observable<MediaStream>;
4948
- /** Current local media stream, or `null` if not available. */
4949
- get localStream(): MediaStream | null;
4950
- /** Observable of the remote media stream from the far end. */
4951
- get remoteStream$(): Observable<MediaStream>;
4952
- /** Current remote media stream, or `null` if not available. */
4953
- get remoteStream(): MediaStream | null;
4954
- /** Observable of custom user variables associated with the call. */
4955
- get userVariables$(): Observable<Record<string, unknown>>;
4956
- /** a copy of the current custom user variables of the call. */
4957
- get userVariables(): Record<string, unknown>;
4958
- /** Merge current custom user variables of the call. */
4959
- set userVariables(variables: Record<string, unknown>);
4960
- /** Observable of current network health issues (empty array = healthy). */
4961
- get networkIssues$(): Observable<NetworkIssue[]>;
4962
- /** Current snapshot of network issues. */
4963
- get networkIssues(): NetworkIssue[];
4964
- /** Simple boolean health indicator derived from stats monitor. */
4965
- get isNetworkHealthy$(): Observable<boolean>;
4966
- /** Whether the network is currently healthy. */
4967
- get isNetworkHealthy(): boolean;
4968
- /** Rolling history of raw network metrics (RTT, jitter, packet loss, bitrate). */
4969
- get networkMetrics$(): Observable<NetworkMetrics[]>;
4970
- /** Current snapshot of the metrics rolling window. */
4971
- get networkMetrics(): NetworkMetrics[];
4972
- /** Observable of MOS quality score (1-5) computed from stats metrics. */
4973
- get qualityScore$(): Observable<number>;
4974
- /** Observable of simplified quality level (excellent/good/fair/poor/critical). */
4975
- get qualityLevel$(): Observable<QualityLevel$1>;
4976
- /** Observable of the recovery pipeline state machine. */
4977
- get recoveryState$(): Observable<RecoveryState$1>;
4978
- /** Observable of recovery events (keyframe requested, ICE restart, etc.). */
4979
- get recoveryEvent$(): Observable<RecoveryEvent$1>;
4980
- /** Observable indicating whether the call is bandwidth-constrained. */
4981
- get bandwidthConstrained$(): Observable<boolean>;
4982
- /** Observable that emits when server-pushed media params are applied. */
4983
- get mediaParamsUpdated$(): Observable<MediaParamsEvent>;
4984
- /**
4985
- * @internal Emit a media params update event.
4986
- * Called by the VertoManager when server-pushed media params are applied.
4896
+ * The mechanism follows the binding: a client-bound session re-mints a bound
4897
+ * base SAT through `authenticate()` with the DPoP fingerprint, because the
4898
+ * developer refresh handler would hand back an unbound token and silently
4899
+ * degrade the session. An unbound session uses the refresh handler. Rotation
4900
+ * cost is not a reason to skip this — the only reason is having no mechanism.
4901
+ *
4902
+ * @returns whether the session is now running on a freshly accepted credential.
4987
4903
  */
4988
- emitMediaParamsUpdated(event: MediaParamsEvent): void;
4989
- /** Request a video keyframe via RTCP PLI/FIR. */
4990
- requestKeyframe(): void;
4991
- /** Force an ICE restart / re-INVITE. */
4992
- requestIceRestart(): Promise<void>;
4904
+ private remintAndReauthenticate;
4993
4905
  /**
4994
- * @internal Initialize resilience subsystems when the call reaches 'connected'.
4995
- * Called from within the status subscription to wire stats and recovery.
4906
+ * Re-mint a credential via `provider.refresh()`, routed through the
4907
+ * coordinator's shared in-flight guard so concurrent re-mint paths (a
4908
+ * scheduled/resume refresh, -32003 recovery, and reconnect) never fire a
4909
+ * second `provider.refresh()` in parallel — which rotating one-time-use
4910
+ * refresh tokens reject. Falls back to a direct call only if the coordinator
4911
+ * has not been constructed yet.
4996
4912
  */
4997
- private initResilienceSubsystems;
4913
+ private remintCredential;
4998
4914
  /**
4999
- * Wait for the underlying RTCPeerConnection to reach 'connected' after
5000
- * triggering an ICE restart. Resolves true on success, false on failure
5001
- * or if the state doesn't transition within the configured timeout.
4915
+ * Re-mint credentials before a fresh (re)connect (`onBeforeReconnect` hook).
4916
+ * The session invokes this only when it is client-bound OR the in-memory
4917
+ * token is expired. The re-mint mechanism depends on the binding:
4918
+ * - Client-bound: `authenticate()` with the DPoP fingerprint to obtain a
4919
+ * fresh base SAT the upcoming reconnect can re-bind (the
4920
+ * DeviceTokenManager re-activates afterwards).
4921
+ * - Unbound: the developer's non-interactive `refresh()` handler.
4922
+ * `authenticate()` is deliberately NOT used here — it may be interactive
4923
+ * (a login prompt) and must not fire on a background reconnect.
5002
4924
  *
5003
- * Polls connectionState directly because the recovery manager already
5004
- * wraps this call in its own withTimeout(); a separate listener-based
5005
- * implementation would race the outer timeout in subtle ways.
4925
+ * Rejects on failure so the session aborts the reconnect rather than
4926
+ * replaying a stale token.
5006
4927
  */
5007
- private waitForPeerConnectionConnected;
4928
+ private refreshCredentialForReconnect;
4929
+ /** Persist credential to localStorage when persistSession is enabled. */
4930
+ private persistCredential;
5008
4931
  /**
5009
- * @internal Stop and destroy resilience subsystems (on disconnect/destroy).
5010
- * Clears references so they can be re-created on reconnect.
4932
+ * Persist whether the session is client-bound, mirroring the credential's
4933
+ * storage scopes so it survives a reload. The preflight recovery reads it
4934
+ * before any session exists to decide whether to re-bind via `authenticate()`
4935
+ * or refresh an unbound token; the marker tracks the latest binding, so an
4936
+ * unbound reconnect clears a stale marker from an earlier client-bound login.
5011
4937
  */
5012
- private stopResilienceSubsystems;
5013
- /** @internal */
5014
- createParticipant(memberId: string, selfId?: string | null): Participant | SelfParticipant;
5015
- /** Observable of the current audio/video send/receive directions. */
5016
- get mediaDirections$(): Observable<MediaDirections>;
5017
- /** Current audio/video send/receive directions. */
5018
- get mediaDirections(): MediaDirections;
5019
- protected get participantsId$(): Observable<string[]>;
4938
+ private persistClientBoundMarker;
4939
+ /** Read the persisted client-bound marker (see {@link persistClientBoundMarker}). */
4940
+ private wasClientBound;
4941
+ private init;
4942
+ private handleAttachments;
5020
4943
  /**
5021
- * Executes a raw JSON-RPC request on the client session.
5022
- *
5023
- * Lower-level than {@link executeMethod} — allows full control over the RPC request structure.
4944
+ * Fetch the authenticated user profile, recovering a stale credential.
5024
4945
  *
5025
- * @param request - Complete JSON-RPC request object.
5026
- * @param options - Optional RPC execution options (timeout, etc.).
5027
- * @returns The RPC response.
5028
- * @throws {JSONRPCError} If the RPC call returns an error response.
4946
+ * On a reload the persisted credential can be expired. Unlike the WS resume —
4947
+ * which the server accepts against the persisted `authorization_state` even
4948
+ * with an expired token — this REST preflight has no such short-circuit and is
4949
+ * refused (401). There is no session yet to reauthenticate, so recovery
4950
+ * re-mints the credential through the provider ({@link refreshCredentialForReconnect})
4951
+ * and retries with a FRESH {@link User}: Fetchable memoizes its result
4952
+ * (shareReplay), so reusing the instance would replay the 401 instead of
4953
+ * re-fetching with the new token. Without the user id the transport/session —
4954
+ * and the reattach a reload is trying to preserve — cannot even be addressed.
5029
4955
  */
5030
- execute<T extends JSONRPCResponse = JSONRPCResponse>(request: JSONRPCRequest, options?: PendingRPCOptions): Promise<T>;
5031
- /** Observable of the local participant's member ID. */
5032
- get selfId$(): Observable<string | null>;
5033
- /** Local participant's member ID, or `null` if not joined. */
5034
- get selfId(): string | null;
5035
- /** Observable of the server node ID handling this call. */
5036
- get nodeId$(): Observable<string | null>;
5037
- /** Server node ID handling this call, or `null`. */
5038
- get nodeId(): string | null;
5039
- private isCallSessionEvent;
5040
- private get callSessionEvents$();
5041
- /** Observable of call-updated events. */
5042
- get callUpdated$(): Observable<CallUpdatedPayload>;
5043
- /** Observable of member-joined events, emitted when a remote participant joins the call. */
5044
- get memberJoined$(): Observable<MemberJoinedPayload>;
5045
- /** Observable of member-left events, emitted when a participant leaves the call. */
5046
- get memberLeft$(): Observable<MemberLeftPayload>;
5047
- /** Observable of member-updated events (mute, volume, etc.). */
5048
- get memberUpdated$(): Observable<MemberUpdatedPayload>;
5049
- /** Observable of member-talking events (speech start/stop). */
5050
- get memberTalking$(): Observable<MemberTalkingPayload>;
5051
- /** Observable of call state-change events. */
5052
- get callStates$(): Observable<CallStatePayload>;
5053
- /** Observable of layout-changed events. */
5054
- get layoutUpdates$(): Observable<LayoutChangedPayload>;
5055
- /** Underlying `RTCPeerConnection`, for advanced use cases. */
5056
- get rtcPeerConnection(): RTCPeerConnection | undefined;
5057
- /** Observable of raw signaling events as plain objects. */
5058
- get signalingEvent$(): Observable<Record<string, unknown>>;
4956
+ private fetchUserOrRecover;
5059
4957
  /**
5060
- * Subscribe to a custom signaling event type on this call.
4958
+ * Establishes the WebSocket connection and authenticates the session.
5061
4959
  *
5062
- * Returns a cached observable that filters `callSessionEvents$` for events
5063
- * whose `event_type` matches the given string. The observable completes
5064
- * when the call is destroyed.
4960
+ * ## Reconnection behavior
5065
4961
  *
5066
- * Unlike `signalingEvent$` (which only emits known call-level event types),
5067
- * this method also matches custom/user-defined event types.
4962
+ * After a successful connection the underlying {@link WebSocketController}
4963
+ * automatically attempts to reconnect whenever the socket closes
4964
+ * unexpectedly (e.g. network change, server restart). Reconnection uses an
4965
+ * **exponential back-off** strategy:
5068
4966
  *
5069
- * The SDK does not validate event type strings --- the server decides
5070
- * whether a given type is valid.
4967
+ * - First retry after `reconnectDelayMin` (default **0.1 s**).
4968
+ * - Each subsequent retry doubles the delay up to `reconnectDelayMax`
4969
+ * (default **3 s**).
4970
+ * - The delay resets to `reconnectDelayMin` once a connection succeeds.
4971
+ * - A per-attempt `connectionTimeout` (default **10 s**) aborts the
4972
+ * attempt and schedules the next retry if the server does not respond.
5071
4973
  *
5072
- * @param eventType - The event type to subscribe to (e.g. `'my.custom.event'`).
5073
- * @returns An observable that emits matching signaling events.
4974
+ * Calling {@link disconnect} stops the reconnection loop entirely.
4975
+ *
4976
+ * ## Message handling during temporary disconnections
4977
+ *
4978
+ * While the socket is not in the `connected` state, **outgoing messages
4979
+ * are queued** in an internal buffer. Once the connection is
4980
+ * re-established the queue is flushed in order so no outgoing RPC call is
4981
+ * lost.
4982
+ *
4983
+ * **Incoming** server-to-client messages that arrive while the socket is
4984
+ * down are *not* buffered by the SDK — they are expected to be
4985
+ * re-delivered by the server after the session is re-authenticated.
4986
+ * Active RPC calls that were awaiting a response will time out
4987
+ * (default **5 s**) and reject with an `RPCTimeoutError`; callers should
4988
+ * handle this and retry if appropriate.
4989
+ *
4990
+ * The connection status can be observed via the `status$` observable on
4991
+ * the transport layer, which emits `'connecting'`, `'connected'`,
4992
+ * `'reconnecting'`, `'disconnecting'`, or `'disconnected'`.
4993
+ */
4994
+ connect(): Promise<void>;
4995
+ /**
4996
+ * Observable that emits the {@link User} profile once fetched,
4997
+ * or `undefined` before authentication completes.
5074
4998
  *
5075
4999
  * @example
5076
5000
  * ```ts
5077
- * call.subscribe('my.custom.event').subscribe(event => {
5078
- * console.log('Custom event:', event);
5001
+ * client.user$.subscribe(u => {
5002
+ * if (u) console.log('Logged in as', u.email);
5079
5003
  * });
5080
5004
  * ```
5081
5005
  */
5082
- subscribe(eventType: string): Observable<Record<string, unknown>>;
5083
- get webrtcMessages$(): Observable<WebrtcMessagePayload>;
5084
- get callEvent$(): Observable<WebrtcMessagePayload | CallJoinedPayload | CallLeftPayload | CallUpdatedPayload | CallStatePayload | CallPlayPayload | CallConnectPayload | RoomUpdatedPayload | MemberUpdatedPayload | MemberJoinedPayload | MemberLeftPayload | MemberTalkingPayload | LayoutChangedPayload | ConversationMessagePayload>;
5085
- get layoutEvent$(): Observable<LayoutChangedPayload>;
5006
+ get user$(): Observable<User | undefined>;
5007
+ /** Current user snapshot, or `undefined` if not yet authenticated. */
5008
+ get user(): User | undefined;
5086
5009
  /**
5087
- * Hangs up the call and releases all resources.
5088
- *
5089
- * Sends a Verto `bye` to the server, transitions status to `'disconnecting'`,
5090
- * then destroys the call. After this, the call instance is no longer usable.
5010
+ * Observable that emits the {@link Directory} instance once the client is connected,
5011
+ * or `undefined` while disconnected. Subscribe to this to safely wait for the directory
5012
+ * to become available without risking an error.
5091
5013
  *
5092
5014
  * @example
5093
5015
  * ```ts
5094
- * await call.hangup();
5016
+ * client.directory$.subscribe(dir => {
5017
+ * if (dir) dir.addresses$.subscribe(console.log);
5018
+ * });
5095
5019
  * ```
5096
5020
  */
5097
- hangup(): Promise<void>;
5021
+ get directory$(): Observable<Directory | undefined>;
5098
5022
  /**
5099
- * Sends DTMF digits on the call.
5023
+ * Current directory snapshot, or `undefined` if the client is not yet connected.
5024
+ * Prefer {@link directory$} when you need to react to the directory becoming available.
5025
+ */
5026
+ get directory(): Directory | undefined;
5027
+ /** Observable that emits when the user registration state changes. */
5028
+ get isRegistered$(): Observable<boolean>;
5029
+ /** Whether the user is currently registered. */
5030
+ get isRegistered(): boolean;
5031
+ /** Whether the client is currently connected. */
5032
+ get isConnected(): boolean;
5033
+ /** Observable that emits when the connection state changes. */
5034
+ get isConnected$(): Observable<boolean>;
5035
+ /** Observable that emits `true` when the client is both connected and authenticated. */
5036
+ get ready$(): Observable<boolean>;
5037
+ /** Observable stream of errors from transport, authentication, and devices. */
5038
+ get errors$(): Observable<Error>;
5039
+ /**
5040
+ * Observable stream of non-fatal SDK warnings.
5100
5041
  *
5101
- * @param dtmf - The digit string to send (e.g. `'1234#'`).
5042
+ * Subscribe to detect SDK behaviors that affect session liveness or developer-facing
5043
+ * contracts but do not warrant disconnection — e.g., a fallback from Client Bound SAT
5044
+ * refresh to the developer-provided `refresh()` because the SAT lacks `sat:refresh`
5045
+ * scope. Discriminated by `code`.
5102
5046
  *
5103
- * @example
5104
- * ```ts
5105
- * await call.sendDigits('1234#');
5106
- * ```
5047
+ * Independent from {@link errors$}: existing error consumers are not notified.
5107
5048
  */
5108
- sendDigits(dtmf: string): Promise<void>;
5109
- /** Observable of WebRTC-specific signaling messages. */
5110
- /** Observable of call-level signaling events. */
5111
- /** Observable of layout-changed signaling events. */
5049
+ get warnings$(): Observable<SDKWarning>;
5050
+ /** Platform WebRTC capabilities detected at construction time. */
5051
+ get platformCapabilities(): PlatformCapabilities;
5052
+ /** Observable that emits when the SDK auto-switches a device. */
5053
+ get deviceRecovered$(): Observable<DeviceRecoveryEvent>;
5112
5054
  /**
5113
- * Accepts an inbound call, optionally overriding media options for the answer.
5055
+ * Export a structured diagnostic bundle for support/debugging.
5056
+ * Includes connection events, call summaries, and device changes.
5057
+ */
5058
+ exportDiagnostics(): SessionDiagnostics;
5059
+ /**
5060
+ * Initialize resilience subsystems. Non-fatal: any failure is logged and
5061
+ * the SDK continues working without the failing subsystem.
5062
+ */
5063
+ private initResilienceSubsystems;
5064
+ /**
5065
+ * Disconnects the WebSocket and tears down the current session.
5114
5066
  *
5115
- * @param options - Optional media constraints for the answer (audio/video).
5067
+ * Ends the session identified by the protocol and clears its persisted
5068
+ * resume state (`authorization_state` + protocol) and attach records
5069
+ * together — a later {@link connect} with the same credentials starts a
5070
+ * fresh session and cannot reattach to the ended session's calls.
5071
+ * Credentials and device preferences are preserved. To temporarily stop
5072
+ * receiving inbound calls while keeping the session alive, use
5073
+ * `unregister()` instead.
5116
5074
  *
5117
- * @example
5118
- * ```ts
5119
- * // Accept with defaults
5120
- * call.answer();
5075
+ * The client can be reconnected by calling {@link connect} again,
5076
+ * which creates a fresh transport and session.
5077
+ */
5078
+ disconnect(): Promise<void>;
5079
+ /**
5080
+ * Tear down the current transport / session / attach manager. Safe to call
5081
+ * when nothing has been initialized yet (e.g. first connect()).
5082
+ */
5083
+ private teardownTransportAndSession;
5084
+ private waitAuthentication;
5085
+ /**
5086
+ * Registers the user as online to receive inbound calls and events.
5121
5087
  *
5122
- * // Accept audio-only
5123
- * call.answer({ audio: true, video: false });
5124
- * ```
5125
- * @see {@link reject} to decline the call instead.
5126
- * @see {@link answered$} to observe the acceptance state.
5088
+ * Waits for authentication to complete before sending the registration.
5089
+ * If the initial attempt fails, reauthentication is attempted automatically.
5090
+ *
5091
+ * @throws {InvalidCredentialsError} If registration and reauthentication both fail.
5127
5092
  */
5128
- answer(options?: MediaOptions): void;
5129
- /** Media options provided when answering. Used internally by the VertoManager. */
5130
- get answerMediaOptions(): MediaOptions | undefined;
5093
+ register(): Promise<void>;
5131
5094
  /**
5132
- * Rejects an inbound call, preventing media negotiation.
5095
+ * Unregisters the user, going offline for inbound calls.
5133
5096
  *
5134
- * @see {@link answer} to accept the call instead.
5135
- * @see {@link answered$} to observe the rejection state.
5097
+ * The WebSocket connection remains open; use {@link disconnect} to fully close it.
5136
5098
  */
5137
- reject(): void;
5138
- /** Observable that emits `true` when answered, `false` when rejected. */
5139
- get answered$(): Observable<boolean>;
5099
+ unregister(): Promise<void>;
5140
5100
  /**
5141
- * Sets the call layout and, optionally, individual participant positions.
5101
+ * Places an outbound call to the given destination.
5142
5102
  *
5143
- * The gateway `call.layout.set` DTO has **no** `positions` member, so when
5144
- * `positions` is provided this method issues a `call.member.position.set`
5145
- * request per member (via {@link Participant.setPosition}, which keys each
5146
- * position by that member's own call context) alongside `call.layout.set`
5147
- * (issue #19400, Flag #6).
5103
+ * Waits for authentication before dialing. Media options are merged from
5104
+ * saved preferences, destination query parameters (e.g. `?channel=video`),
5105
+ * and the provided `options` (highest priority).
5148
5106
  *
5149
- * **These operations are NOT atomic.** The layout is applied first, then each
5150
- * member position sequentially, so members may briefly flash into their
5151
- * default slots before being moved to the requested positions. Targeted
5152
- * members are validated upfront, though: when any of them has no
5153
- * {@link Participant.target | member call context} yet, the whole call
5154
- * rejects before any request is sent and the layout is left unchanged.
5107
+ * Returns a {@link Call} in `'ringing'` state. Subscribe to {@link Call.status$}
5108
+ * to track progression through `'connected'` → `'disconnected'`.
5155
5109
  *
5156
- * @param layout - Layout name (must be one of {@link layouts}).
5157
- * @param positions - Optional map of member IDs to {@link VideoPosition} values.
5158
- * When omitted or empty, only the layout is changed.
5159
- * @throws {InvalidParams} If the layout is not in the available {@link layouts}.
5160
- * @throws {ParticipantNotReadyError} If a targeted member's call context has
5161
- * not been received yet — thrown before any request is sent.
5110
+ * Local media acquisition is deliberately unbounded: an unanswered permission
5111
+ * prompt leaves this promise pending indefinitely, so apply your own bound if
5112
+ * your UI needs one. The 12 s signaling budget starts only once acquisition
5113
+ * settles.
5114
+ *
5115
+ * @param destination - Address URI string (e.g. `'/public/my-room'`) or {@link Address} instance.
5116
+ * @param options - Media and dial options (audio/video, device constraints). Overrides defaults.
5117
+ * @returns The created {@link Call} instance.
5118
+ * @throws {Error} If authentication is not complete or call creation fails.
5162
5119
  *
5163
5120
  * @example
5164
5121
  * ```ts
5165
- * await call.setLayout('grid-responsive', {
5166
- * [participantId]: 'reserved-0',
5122
+ * const call = await client.dial('/public/conference', {
5123
+ * audio: true,
5124
+ * video: true,
5167
5125
  * });
5126
+ * call.status$.subscribe(status => console.log('Call:', status));
5168
5127
  * ```
5169
5128
  */
5170
- setLayout(layout: string, positions?: Record<string, VideoPosition>): Promise<void>;
5171
- /**
5172
- * Transfers the call to another destination.
5173
- *
5174
- * @param options - Transfer configuration including the target destination.
5175
- * @see {@link status$} to observe the transfer progress.
5176
- */
5177
- transfer(options: TransferOptions): Promise<void>;
5129
+ dial(destination: string | Address, options?: DialOptions): Promise<Call>;
5178
5130
  /**
5179
- * Set the local microphone gain as a percentage applied before transmission.
5180
- *
5181
- * - `0` = silent
5182
- * - `100` = unity (no change, default)
5183
- * - `200` = 2× digital boost (max; expect clipping / noise amplification)
5131
+ * Runs a multi-phase connectivity test against the given destination.
5184
5132
  *
5185
- * Values are clamped to [0, 200]. Engages the local audio pipeline on
5186
- * first use (one-time cost).
5133
+ * The test checks:
5134
+ * 1. **Signaling** -- WebSocket connected, RTT measurement
5135
+ * 2. **Devices** -- getUserMedia succeeds with selected (or specified) devices
5136
+ * 3. **ICE/TURN** -- gathers ICE candidates to verify STUN/TURN reachability
5137
+ * 4. **Media/bandwidth** (unless `skipMediaTest`) -- dials the destination,
5138
+ * collects getStats() for `duration` seconds, computes bandwidth estimates
5187
5139
  *
5188
- * Note: this is a **digital** multiplier applied in a Web Audio GainNode
5189
- * between your mic track and the RTCRtpSender — it does not change the
5190
- * physical mic's hardware sensitivity. Browsers' autoGainControl can
5191
- * fight the setting; call {@link setAutoGainControl}(false) for
5192
- * predictable behaviour.
5140
+ * @param destination - A destination to dial for the media test (e.g. `'/private/network-test'`).
5141
+ * @param options - Preflight options (duration, skipMediaTest, device overrides).
5142
+ * @returns A {@link PreflightResult} describing connectivity health.
5193
5143
  *
5194
- * @param value - Gain percentage (0..200; 100 = unity).
5144
+ * @example
5145
+ * ```ts
5146
+ * const result = await client.preflight('/private/network-test', { duration: 5 });
5147
+ * if (!result.ok) console.warn('Connectivity issues:', result.warnings);
5148
+ * ```
5195
5149
  */
5196
- setLocalMicrophoneGain(value: number): void;
5197
- /** Observable of the current local microphone gain (0..200, where 100 = unity). */
5198
- get localMicrophoneGain$(): Observable<number>;
5150
+ preflight(destination: string, options?: PreflightOptions): Promise<PreflightResult>;
5151
+ /** The underlying client session for advanced RPC operations. */
5152
+ get session(): ClientSessionWrapper;
5153
+ /** Observable list of available audio input (microphone) devices. */
5154
+ get audioInputDevices$(): Observable<MediaDeviceInfo[]>;
5155
+ /** Current snapshot of available audio input devices. */
5156
+ get audioInputDevices(): MediaDeviceInfo[];
5157
+ /** Observable list of available audio output (speaker) devices. */
5158
+ get audioOutputDevices$(): Observable<MediaDeviceInfo[]>;
5159
+ /** Current snapshot of available audio output devices. */
5160
+ get audioOutputDevices(): MediaDeviceInfo[];
5161
+ /** Observable list of available video input (camera) devices. */
5162
+ get videoInputDevices$(): Observable<MediaDeviceInfo[]>;
5163
+ /** Current snapshot of available video input devices. */
5164
+ get videoInputDevices(): MediaDeviceInfo[];
5165
+ /** Observable of the currently selected audio input device. */
5166
+ get selectedAudioInputDevice$(): Observable<MediaDeviceInfo | null>;
5167
+ /** Observable of the currently selected audio output device. */
5168
+ get selectedAudioOutputDevice$(): Observable<MediaDeviceInfo | null>;
5169
+ /** Observable of the currently selected video input device. */
5170
+ get selectedVideoInputDevice$(): Observable<MediaDeviceInfo | null>;
5171
+ /** Currently selected audio input device, or `null` if none. */
5172
+ get selectedAudioInputDevice(): MediaDeviceInfo | null;
5173
+ /** Currently selected audio output device, or `null` if none. */
5174
+ get selectedAudioOutputDevice(): MediaDeviceInfo | null;
5175
+ /** Currently selected video input device, or `null` if none. */
5176
+ get selectedVideoInputDevice(): MediaDeviceInfo | null;
5177
+ /** Media track constraints for the selected audio input device. Returns `false` when disabled. */
5178
+ get selectedAudioInputDeviceConstraints(): MediaTrackConstraints | boolean;
5179
+ /** Media track constraints for the selected video input device. Returns `false` when disabled. */
5180
+ get selectedVideoInputDeviceConstraints(): MediaTrackConstraints | boolean;
5181
+ /** Converts a `MediaDeviceInfo` to track constraints suitable for `getUserMedia`. */
5182
+ deviceInfoToConstraints(deviceInfo: MediaDeviceInfo | null): MediaTrackConstraints;
5183
+ /** Sets the preferred audio input device. */
5184
+ selectAudioInputDevice(device: MediaDeviceInfo | null): void;
5185
+ /** Sets the preferred video input device. */
5186
+ selectVideoInputDevice(device: MediaDeviceInfo | null): void;
5187
+ /** Sets the preferred audio output device. */
5188
+ selectAudioOutputDevice(device: MediaDeviceInfo | null): void;
5199
5189
  /**
5200
- * Observable of the RMS audio level of the local microphone, 0..1.
5201
- * Emits at ~30fps while a mic track is active. Engages the local audio
5202
- * pipeline on first subscription.
5190
+ * Apply the currently selected audio output device to an HTMLMediaElement
5191
+ * (e.g. the `<audio>` or `<video>` element the consumer attached the
5192
+ * remote stream to). Uses `HTMLMediaElement.setSinkId` under the hood.
5193
+ * Returns a `Promise<boolean>`: `true` if the sink was applied,
5194
+ * `false` if the browser doesn't support `setSinkId` or no device is
5195
+ * selected.
5196
+ *
5197
+ * @example
5198
+ * ```ts
5199
+ * audioEl.srcObject = call.remoteStream;
5200
+ * await client.applySelectedAudioOutputDevice(audioEl);
5201
+ * ```
5203
5202
  */
5204
- get localAudioLevel$(): Observable<number>;
5203
+ applySelectedAudioOutputDevice(element: HTMLMediaElement): Promise<boolean>;
5204
+ /** Starts monitoring for media device changes (connect/disconnect). */
5205
+ enableDeviceMonitoring(): void;
5206
+ /** Stops monitoring for media device changes. */
5207
+ disableDeviceMonitoring(): void;
5205
5208
  /**
5206
- * Observable that is `true` while the local participant is speaking
5207
- * (RMS level above the VAD threshold, with hold time to avoid flicker).
5209
+ * Returns the capabilities of a media device.
5210
+ * @param deviceInfo - The device to query.
5211
+ * @returns The device capabilities, or `null` if unavailable.
5208
5212
  */
5209
- get localSpeaking$(): Observable<boolean>;
5213
+ getDeviceCapabilities(deviceInfo: MediaDeviceInfo): Promise<MediaTrackCapabilities | null>;
5210
5214
  /**
5211
- * Enable push-to-talk: while {@link setPushToTalkActive} has been called
5212
- * with `false`, the microphone gain is forced to 0; calling
5213
- * {@link setPushToTalkActive} with `true` restores the configured gain.
5214
- * Use this instead of mute/unmute for instant talk/silence transitions
5215
- * because it doesn't rebuild the track.
5216
- *
5217
- * This method installs the pipeline but does not attach any keyboard
5218
- * listener — consumers bind the key themselves and call
5219
- * {@link setPushToTalkActive} on keydown/keyup.
5215
+ * Checks whether a device is still available and usable.
5216
+ * @param deviceInfo - The device to validate, or `null`.
5217
+ * @returns `true` if the device is valid and available. Returns `false` for `null`, audio output devices, or unavailable devices.
5220
5218
  */
5221
- enablePushToTalk(): void;
5222
- /** Disable push-to-talk; mic gain returns to the configured value. */
5223
- disablePushToTalk(): void;
5219
+ isValidDevice(deviceInfo: MediaDeviceInfo | null): Promise<boolean>;
5220
+ /** Injects a storage manager into the device controller for persistence. */
5221
+ setStorageManager(storageManager: StorageManager): void;
5222
+ /** Clears all device state and re-enumerates. */
5223
+ clearDeviceState(): Promise<void>;
5224
+ /** Forces a device re-enumeration. */
5225
+ enumerateDevices(): Promise<void>;
5226
+ /** Disables audio input (receive-only mode). No audio track will be acquired. */
5227
+ disableAudioInput(): void;
5228
+ /** Re-enables audio input, restoring the last selection or auto-selecting. */
5229
+ enableAudioInput(): void;
5230
+ /** Disables video input (receive-only mode). No video track will be acquired. */
5231
+ disableVideoInput(): void;
5232
+ /** Re-enables video input, restoring the last selection or auto-selecting. */
5233
+ enableVideoInput(): void;
5234
+ /** Observable that emits `true` when video input is disabled (receive-only). */
5235
+ get videoInputDisabled$(): Observable<boolean>;
5236
+ /** Observable that emits `true` when audio input is disabled (receive-only). */
5237
+ get audioInputDisabled$(): Observable<boolean>;
5238
+ /** Whether video input is currently disabled. */
5239
+ get videoInputDisabled(): boolean;
5240
+ /** Whether audio input is currently disabled. */
5241
+ get audioInputDisabled(): boolean;
5224
5242
  /**
5225
- * While push-to-talk is enabled, sets the talk state. `true` = transmitting,
5226
- * `false` = silent. No-op if push-to-talk has not been enabled.
5243
+ * Triggers the browser's media permission dialog and captures the user's device selections.
5244
+ *
5245
+ * @param options - Which permissions to request.
5246
+ * @param options.audio - Whether to request audio permission.
5247
+ * @param options.video - Whether to request video permission.
5248
+ * @returns The permission result with selected devices.
5227
5249
  */
5228
- setPushToTalkActive(active: boolean): void;
5250
+ requestMediaPermissions(options?: {
5251
+ audio?: boolean;
5252
+ video?: boolean;
5253
+ }): Promise<PermissionResult>;
5229
5254
  /**
5230
- * Toggle echo cancellation on the local mic at runtime. Applied via
5231
- * `track.applyConstraints`; browsers that don't honour runtime constraints
5232
- * (notably iOS Safari) fall back to re-acquiring the track with the new
5233
- * constraint set and plumbing the replacement through the local audio
5234
- * pipeline if one is active.
5255
+ * Clears all SDK-persisted state and resets to defaults.
5256
+ *
5257
+ * This clears device preferences, device history, authorization state,
5258
+ * attached call IDs, and all SDK storage keys, then re-enumerates devices.
5235
5259
  */
5236
- setEchoCancellation(enabled: boolean): Promise<void>;
5237
- /** Toggle browser noise suppression on the local mic at runtime. */
5238
- setNoiseSuppression(enabled: boolean): Promise<void>;
5239
- /** Toggle browser automatic gain control on the local mic at runtime. */
5240
- setAutoGainControl(enabled: boolean): Promise<void>;
5260
+ resetToDefaults(): Promise<void>;
5241
5261
  /**
5242
- * Observable of the aggregate remote audio level, 0..1 RMS. The server
5243
- * delivers a single mixed audio stream for all remote participants — this
5244
- * meter reports that mix. Per-participant audio is not available client-side.
5262
+ * Destroys the client, clearing timers and releasing all resources.
5245
5263
  *
5246
- * Engages a shared AudioContext on first subscription (cheap — one
5247
- * AnalyserNode, no GainNode, no destination) so it does not affect the
5248
- * caller's audio element playback.
5264
+ * Intentionally destroying the client ends its session: the resume state
5265
+ * (`authorization_state` + protocol) and the attach records are both
5266
+ * cleared. Credentials and device preferences are preserved — use
5267
+ * {@link resetToDefaults} for a full wipe. To temporarily stop receiving
5268
+ * inbound calls while keeping the session alive, use `unregister()`.
5249
5269
  */
5250
- get remoteAudioLevel$(): Observable<number>;
5251
- /** Destroys the call, releasing all resources and subscriptions. */
5252
5270
  destroy(): void;
5253
- /**
5254
- * @internal Send a verto.subscribe message to add an event type to the
5255
- * server's subscription list for this call. Returns the underlying RPC
5256
- * promise so callers can decide whether to cache the observable on success
5257
- * or retry on failure.
5258
- */
5259
- private _sendVertoSubscribe;
5271
+ }
5272
+ //#endregion
5273
+ //#region src/utils/embeddableCall.d.ts
5274
+ /** Options for {@link embeddableCall}. */
5275
+ interface EmbeddableCallOptions {
5276
+ /** Destination URI to call. */
5277
+ to: string;
5278
+ /** Embed token for authentication. */
5279
+ embedToken: string;
5280
+ /** SignalWire host URL. */
5281
+ host: string;
5282
+ }
5283
+ /**
5284
+ * Creates a call using an embed token for simple, embeddable integrations.
5285
+ *
5286
+ * Handles client creation, authentication, and dialing in a single call.
5287
+ *
5288
+ * @param options - Embed token, host, and destination.
5289
+ * @returns The created {@link Call} instance.
5290
+ */
5291
+ declare function embeddableCall(options: EmbeddableCallOptions): Promise<Call>;
5292
+ //#endregion
5293
+ //#region src/dependencies/StaticCredentialProvider.d.ts
5294
+ /**
5295
+ * Credential provider that returns a fixed set of credentials.
5296
+ *
5297
+ * Use when the token is already available (e.g. from a backend endpoint).
5298
+ *
5299
+ * @example
5300
+ * ```ts
5301
+ * const provider = new StaticCredentialProvider({ token: 'my-sat-token' });
5302
+ * const client = new SignalWire(provider);
5303
+ * ```
5304
+ */
5305
+ declare class StaticCredentialProvider implements CredentialProvider {
5306
+ private credentials;
5307
+ constructor(credentials: SDKCredential);
5308
+ /** Returns the static credentials. */
5309
+ authenticate(): Promise<SDKCredential>;
5310
+ }
5311
+ //#endregion
5312
+ //#region src/dependencies/EmbedTokenCredentialProvider.d.ts
5313
+ /** Credential provider that exchanges an embed token for a SAT via the host's token endpoint. */
5314
+ declare class EmbedTokenCredentialProvider implements CredentialProvider {
5315
+ private host;
5316
+ private embedToken;
5317
+ constructor(host: string, embedToken: string);
5318
+ private fetchSAT;
5319
+ authenticate(): Promise<{
5320
+ token: string;
5321
+ expiry_at: number;
5322
+ }>;
5323
+ refresh(): Promise<{
5324
+ token: string;
5325
+ expiry_at: number;
5326
+ }>;
5260
5327
  }
5261
5328
  //#endregion
5262
5329
  //#region src/index.d.ts
@@ -5271,5 +5338,5 @@ declare const version: string;
5271
5338
  */
5272
5339
  declare const ready: boolean;
5273
5340
  //#endregion
5274
- export { Address, type AddressHistory, type AudioConstraintsEvent, type AuthenticateContext, type Call, type CallAddress, type CallCapabilitiesState, CallCreateError, type CallDiagnosticSummary, type CallDirection, type CallError, type CallErrorKind, type NetworkIssue as CallNetworkIssue, type NetworkIssue, type NetworkMetrics as CallNetworkMetrics, type NetworkMetrics, CallNotReadyError, type CallOptions, type CallParticipant, type CallSelfParticipant, type CallState, type CallStatus, type Capability, ClientPreferences, CollectionFetchError, type ConstraintFallbackEvent, type CredentialNoRefreshHandlerWarning, type CredentialProvider, type CredentialRefreshFallbackReason, type CredentialRefreshFallbackWarning, DPoPInitError, type DebugOptions, type DeviceController, type DeviceRecoveryEvent, DeviceTokenError, type DiagnosticEvent, type DialOptions, type Directory, EmbedTokenCredentialProvider, type ExecuteMethod, InvalidCredentialsError, type JSONRPCErrorResponse, type JSONRPCRequest, type JSONRPCResponse, type JSONRPCSuccessResponse, type LayoutLayer, type LogLevel, MediaAccessError, type MediaDirection, type MediaDirections, type MediaOptions, type MediaParamsEvent, MediaTrackError, type MemberCapabilities, MessageParseError, type NodeSocketAdapter, type OnOffCapability, OverconstrainedFallbackError, Participant, ParticipantNotReadyError, type PendingRPCOptions, type PermissionResult, type PlatformCapabilities, PreflightError, type PreflightOptions, type PreflightResult, type QualityLevel, RecoveryError, type RecoveryEvent, type RecoveryState, type ResilienceCallStatus, type SATClaims, type SDKCredential, type SDKLogger, type SDKWarning, type ScreenShareStatus, type SelectDeviceOptions, SelfCapabilities, SelfParticipant, type SessionDiagnostics, type SessionState, SignalWire, type SignalWireOptions, StaticCredentialProvider, type Storage, type StoredDevicePreference, type TextMessage, TokenRefreshError, type TransferOptions, UnexpectedError, User, type UserPresence, VertoPongError, type VideoPosition, type WebRTCApiProvider, WebRTCCall, type WebRTCMediaDevices, type WebSocketAdapter, embeddableCall, getLogger, isSelfParticipant, ready, setDebugOptions, setLogLevel, setLogger, version };
5341
+ export { Address, type AddressHistory, type AudioConstraintsEvent, type AuthenticateContext, AuxiliaryLegTimeoutError, type Call, type CallAddress, type CallCapabilitiesState, CallCreateError, type CallDiagnosticSummary, type CallDirection, type CallError, type CallErrorKind, type NetworkIssue as CallNetworkIssue, type NetworkIssue, type NetworkMetrics as CallNetworkMetrics, type NetworkMetrics, CallNotReadyError, type CallOptions, type CallParticipant, type CallSelfParticipant, type CallState, type CallStatus, type Capability, ClientPreferences, CollectionFetchError, type ConstraintFallbackEvent, type CredentialNoRefreshHandlerWarning, type CredentialProvider, type CredentialRefreshFallbackReason, type CredentialRefreshFallbackWarning, DPoPInitError, type DebugOptions, type DeviceController, type DeviceRecoveryEvent, DeviceTokenError, type DiagnosticEvent, type DialOptions, type Directory, EmbedTokenCredentialProvider, type ExecuteMethod, InvalidCredentialsError, type JSONRPCErrorResponse, type JSONRPCRequest, type JSONRPCResponse, type JSONRPCSuccessResponse, type LayoutLayer, type LogLevel, MediaAccessError, type MediaDirection, type MediaDirections, type MediaOptions, type MediaParamsEvent, MediaTrackError, type MemberCapabilities, MessageParseError, type NodeSocketAdapter, type OnOffCapability, OverconstrainedFallbackError, Participant, ParticipantNotReadyError, type PendingRPCOptions, type PermissionResult, type PlatformCapabilities, PreflightError, type PreflightOptions, type PreflightResult, type QualityLevel, RecoveryError, type RecoveryEvent, type RecoveryState, type ResilienceCallStatus, type SATClaims, type SDKCredential, type SDKLogger, type SDKWarning, type ScreenShareStatus, type SelectDeviceOptions, SelfCapabilities, SelfParticipant, type SessionDiagnostics, type SessionState, SignalWire, type SignalWireOptions, StaticCredentialProvider, type Storage, type StoredDevicePreference, type TextMessage, TokenRefreshError, type TransferOptions, UnexpectedError, User, type UserPresence, VertoPongError, type VideoPosition, type WebRTCApiProvider, WebRTCCall, type WebRTCMediaDevices, type WebSocketAdapter, embeddableCall, getLogger, isSelfParticipant, ready, setDebugOptions, setLogLevel, setLogger, version };
5275
5342
  //# sourceMappingURL=index.d.cts.map