@dialstack/sdk-native 0.1.0

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.
@@ -0,0 +1,2367 @@
1
+ import React from 'react';
2
+ import { CountryCode } from 'libphonenumber-js';
3
+
4
+ /** The minimal list-envelope shape auto-pagination needs. */
5
+ interface Page<I> {
6
+ data: I[];
7
+ next_page_url: string | null;
8
+ }
9
+ /** Item type carried by a page envelope. */
10
+ type PageItem<P> = P extends Page<infer I> ? I : never;
11
+ /**
12
+ * A promise of the first page, augmented with auto-pagination iterators that
13
+ * lazily fetch subsequent pages by following `next_page_url`.
14
+ */
15
+ interface PaginatedList<P extends Page<unknown>> extends Promise<P> {
16
+ autoPagingEach(): AsyncIterableIterator<PageItem<P>>;
17
+ autoPagingToArray(options?: {
18
+ limit?: number;
19
+ }): Promise<PageItem<P>[]>;
20
+ }
21
+
22
+ /**
23
+ * WebRTC platform primitives + types the call/signaling core builds on.
24
+ *
25
+ * There is no longer a `.native` counterpart to this module. The core is written
26
+ * to the standard browser WebRTC surface (`globalThis.RTCPeerConnection`,
27
+ * `MediaStream`, `navigator.mediaDevices`); React Native makes that surface exist
28
+ * at runtime by calling `react-native-webrtc`'s `registerGlobals()` at app
29
+ * startup (see the RN example apps). The two genuinely-RN-only gaps — ringback
30
+ * audio (WebAudio vs InCallManager) and key/value persistence (localStorage vs a
31
+ * host store) — are NOT resolved here; they are injected by the caller
32
+ * (`PhoneOptions.ringback` / `PhoneOptions.storage`).
33
+ *
34
+ * The factory functions read the relevant global *lazily* (at call time, not at
35
+ * module load) so a test harness that swaps `globalThis.RTCPeerConnection` /
36
+ * `MediaStream` / `navigator` in a `beforeEach` still takes effect — capturing
37
+ * the constructors in module-scope `const`s would bind whatever existed when
38
+ * this module was first imported (often `undefined` under jsdom).
39
+ *
40
+ * WebRTC TYPES are re-exported here so the core imports them from `./platform`
41
+ * rather than depending on the ambient `lib.dom.d.ts` names directly.
42
+ */
43
+ type RTCPeerConnection = globalThis.RTCPeerConnection;
44
+ type MediaStream = globalThis.MediaStream;
45
+ type RTCIceServer$1 = globalThis.RTCIceServer;
46
+ type RTCSessionDescriptionInit = globalThis.RTCSessionDescriptionInit;
47
+ /**
48
+ * Synchronous key/value persistence for the E911 address id. This web default is
49
+ * backed by `localStorage`, guarded so the core also works in non-browser hosts
50
+ * (Node/tests) where `localStorage` is absent — there, persistence is a no-op and
51
+ * the app supplies `PhoneOptions.emergencyAddressId` itself. React Native has no
52
+ * `localStorage`, so the RN softphone provider injects a host-backed adapter via
53
+ * `PhoneOptions.storage` (MMKV/AsyncStorage); this default is never reached there.
54
+ */
55
+ interface PlatformStorage {
56
+ getItem(key: string): string | null;
57
+ setItem(key: string, value: string): void;
58
+ removeItem(key: string): void;
59
+ }
60
+
61
+ /**
62
+ * Outbound ringback contract the call core drives. The core never guards the
63
+ * call site, so implementations must be idempotent and no-op silently when their
64
+ * audio backend is unavailable. Web is `RingbackTone` (WebAudio); React Native
65
+ * supplies its own (InCallManager-backed) via `PhoneOptions.ringback`, because
66
+ * WebAudio's `AudioContext` doesn't exist there.
67
+ */
68
+ interface Ringback {
69
+ readonly isPlaying: boolean;
70
+ start(): void;
71
+ stop(): void;
72
+ /**
73
+ * Route the tone to a specific output device, or null for the OS default.
74
+ *
75
+ * OPTIONAL: an implementation whose backend has no per-device routing simply
76
+ * omits it (React Native routes audio through InCallManager, not per-device),
77
+ * and the core calls it as `ringback.setSinkId?.(id)` so an omission is a no-op
78
+ * rather than a crash. Like start/stop, an implementation that has it must
79
+ * no-op instead of throwing when its backend can't honor the request.
80
+ */
81
+ setSinkId?(deviceId: string | null): void;
82
+ }
83
+
84
+ type PhoneErrorCode = 'auth_failed' | 'auth_expired' | 'invalid_message' | 'call_failed' | 'call_not_found' | 'emergency_address_required' | 'session_limit' | 'session_replaced' | 'session_revoked' | 'rate_limited' | 'presence_unavailable' | 'reachability_unavailable' | 'internal_error' | 'going_away' | 'idle_timeout' | 'slow_consumer' | 'transport_closed' | 'ice_fetch_failed' | 'mic_permission_denied' | 'audio_device_unavailable' | 'audio_device_in_use';
85
+ declare class PhoneError extends Error {
86
+ readonly code: PhoneErrorCode;
87
+ readonly callId: string | null;
88
+ readonly fatal: boolean;
89
+ constructor(opts: {
90
+ code: PhoneErrorCode;
91
+ message: string;
92
+ callId?: string | null;
93
+ fatal?: boolean;
94
+ });
95
+ }
96
+
97
+ type CallState = 'trying' | 'ringing' | 'active' | 'held' | 'ended';
98
+ type CallDirection = 'inbound' | 'outbound';
99
+ type CallEndReason = 'hangup' | 'no-answer' | 'busy' | 'failed' | 'transferred' | 'rejected';
100
+ type RejectReason = 'busy' | 'decline';
101
+ type HeldBy = 'local' | 'remote';
102
+ type PresenceStatus = 'available' | 'on_call' | 'dnd' | 'away' | 'offline';
103
+ type SettablePresenceStatus = 'available' | 'dnd' | 'away';
104
+ interface PhoneOptions {
105
+ token: string;
106
+ apiBaseUrl?: string;
107
+ /**
108
+ * Base URL of the signaling server the phone opens its WebSocket to, e.g.
109
+ * `wss://webrtc.dialstack.ai`. The signaling host is separate from
110
+ * `apiBaseUrl` (the REST API) and is served by region-aware infrastructure,
111
+ * so latency-based DNS routes each client to the nearest region
112
+ * automatically — no client-side region selection is needed.
113
+ *
114
+ * When omitted, it defaults to the standard signaling host derived from
115
+ * `apiBaseUrl` (the `api.` hostname label becomes `webrtc.`). Override only
116
+ * for self-hosting, staging, or a proxy. `ws`/`wss` and `http`/`https` are
117
+ * accepted; `http(s)` is upgraded to `ws(s)`. A trailing `/v1/webrtc` path
118
+ * is appended automatically if absent.
119
+ */
120
+ signalingBaseUrl?: string;
121
+ onTokenExpiring?: () => Promise<string>;
122
+ autoReconnect?: boolean;
123
+ iceServers?: RTCIceServer[];
124
+ /**
125
+ * The emergency-address resource id (emerg_…) this softphone uses for E911.
126
+ * Presented on the authenticate handshake so the server can bind
127
+ * it to the current network. When omitted, a previously persisted id (see
128
+ * `setEmergencyAddress`) is loaded from `storage` if available.
129
+ */
130
+ emergencyAddressId?: string;
131
+ /**
132
+ * Synchronous key/value store used to persist the selected E911 address id
133
+ * across launches. Optional at this layer: it defaults to the platform seam's
134
+ * store — `localStorage` on web, an in-memory (non-persisting) store on React
135
+ * Native. On React Native the softphone provider requires the host to supply a
136
+ * real store (e.g. an AsyncStorage- or MMKV-backed adapter), because the SDK
137
+ * takes no persistence dependency of its own.
138
+ */
139
+ storage?: PlatformStorage;
140
+ /**
141
+ * Outbound ringback tone. Defaults to the WebAudio `RingbackTone`. React Native
142
+ * has no `AudioContext`, so the RN softphone provider supplies an
143
+ * `InCallManager`-backed implementation here. One instance is shared across
144
+ * calls (outbound ringback plays one call at a time).
145
+ */
146
+ ringback?: Ringback;
147
+ /**
148
+ * Opens the signaling WebSocket. Defaults to a bare `new WebSocket(url,
149
+ * protocols)`. React Native supplies a variant that attaches a `User-Agent`
150
+ * header (the signaling ingress 403s a handshake without one, and iOS's
151
+ * WebSocket sends none by default); browsers forbid overriding it, so web uses
152
+ * the default.
153
+ */
154
+ createSignalingSocket?: SignalingSocketFactory;
155
+ /**
156
+ * Subscribe to "the app/tab returned to the foreground". Defaults to the web
157
+ * DOM lifecycle events (`visibilitychange`/`focus`/`online`/`pageshow`); React
158
+ * Native has no `document`, so the RN softphone provider supplies an
159
+ * `AppState`-backed variant here. On resume the transport actively re-verifies
160
+ * the connection (a backgrounded tab's throttled event loop may never have
161
+ * processed the socket's close), reconnecting if it's dead.
162
+ */
163
+ onAppResume?: AppResumeSubscribe;
164
+ /**
165
+ * Microphone `deviceId` for new calls; omitted means the OS default. Held for the
166
+ * phone's lifetime and NOT persisted — remembering a device is the host's call, so
167
+ * pass a saved value back in on construction (the React softphone does).
168
+ *
169
+ * An id that no longer resolves falls back to the default rather than failing the
170
+ * call, since ids are origin-scoped and rotate.
171
+ */
172
+ audioInputDeviceId?: string;
173
+ /**
174
+ * Speaker `deviceId` for the ringback tone only; not persisted. Remote call audio
175
+ * plays through a media element the host owns, and only its owner can route it.
176
+ */
177
+ audioOutputDeviceId?: string;
178
+ }
179
+ /** One selectable audio device, as reported by the host. */
180
+ interface AudioDevice {
181
+ /** Opaque, origin-scoped id to pass to `setAudioInputDevice` / `setSinkId`. */
182
+ deviceId: string;
183
+ /** Name, or `''` when the page has no mic permission yet. */
184
+ label: string;
185
+ /** Shared by the input and output halves of one physical device. */
186
+ groupId: string;
187
+ /** True for the entry whose id is literally 'default'. */
188
+ isDefault: boolean;
189
+ }
190
+ /** The host's audio devices, split by direction. */
191
+ interface AudioDeviceList {
192
+ inputs: AudioDevice[];
193
+ outputs: AudioDevice[];
194
+ /** False when devices exist but their names are withheld pending mic permission. */
195
+ labelsAvailable: boolean;
196
+ /** False when the host can't enumerate at all — distinct from "no devices". */
197
+ supported: boolean;
198
+ }
199
+ /**
200
+ * Civic address submitted to register an emergency (E911) location. Validated
201
+ * against the carrier MSAG; an invalid address is rejected.
202
+ */
203
+ interface EmergencyAddressInput {
204
+ address_number?: string;
205
+ street: string;
206
+ unit?: string;
207
+ city: string;
208
+ state: string;
209
+ postal_code: string;
210
+ country?: string;
211
+ }
212
+ /** A registered emergency address resource. */
213
+ interface EmergencyAddress {
214
+ id: string;
215
+ address: EmergencyAddressDetails;
216
+ /** Network this address is bound to, or null when not yet registered. */
217
+ registered_ip: string | null;
218
+ created_at: string;
219
+ }
220
+ /** Normalized civic address fields returned on an EmergencyAddress. */
221
+ interface EmergencyAddressDetails {
222
+ address_number?: string;
223
+ street?: string;
224
+ unit?: string;
225
+ city: string;
226
+ state: string;
227
+ postal_code: string;
228
+ country: string;
229
+ formatted_address?: string;
230
+ latitude?: number;
231
+ longitude?: number;
232
+ }
233
+ /** ListResponse envelope (the only place `object: "list"` appears). */
234
+ interface ListResponse<T> {
235
+ object: 'list';
236
+ url: string;
237
+ next_page_url: string | null;
238
+ previous_page_url: string | null;
239
+ data: T[];
240
+ }
241
+ interface PresenceEntry {
242
+ userId: string;
243
+ name: string;
244
+ status: PresenceStatus;
245
+ /**
246
+ * Whether the user has server-side Do Not Disturb enabled. A separate axis
247
+ * from `status`: a user can be `available` but decline calls because DND is on.
248
+ *
249
+ * The two answer different questions. DND is configuration — it persists until
250
+ * someone changes it. `status` is a lossy summary of currently observed
251
+ * signals, reduced to one value by the precedence on_call > available >
252
+ * offline, so a user on a call reports `on_call` even with no registered
253
+ * device. DND is never reported as a `status` value.
254
+ */
255
+ doNotDisturb: boolean;
256
+ statusText: string | null;
257
+ updatedAt: string;
258
+ }
259
+ interface PresenceUpdate {
260
+ userId: string;
261
+ name: string;
262
+ status: PresenceStatus;
263
+ /**
264
+ * Whether the user has server-side Do Not Disturb enabled. A separate axis
265
+ * from `status`: a user can be `available` but decline calls because DND is on.
266
+ *
267
+ * The two answer different questions. DND is configuration — it persists until
268
+ * someone changes it. `status` is a lossy summary of currently observed
269
+ * signals, reduced to one value by the precedence on_call > available >
270
+ * offline, so a user on a call reports `on_call` even with no registered
271
+ * device. DND is never reported as a `status` value.
272
+ */
273
+ doNotDisturb: boolean;
274
+ statusText: string | null;
275
+ updatedAt: string;
276
+ }
277
+ interface DirectoryEntry {
278
+ /** The colleague's user id (`user_…`). */
279
+ user: string;
280
+ /** Display name to show. */
281
+ displayName: string;
282
+ }
283
+ type ClientMessage = {
284
+ type: 'authenticate';
285
+ req_id?: string;
286
+ token: string;
287
+ emergency_address_id?: string;
288
+ } | {
289
+ type: 'auth.refresh';
290
+ req_id?: string;
291
+ token: string;
292
+ } | {
293
+ type: 'ping';
294
+ req_id?: string;
295
+ } | {
296
+ type: 'call.create';
297
+ req_id?: string;
298
+ destination: string;
299
+ sdp: string;
300
+ } | {
301
+ type: 'call.answer';
302
+ call_id: string;
303
+ } | {
304
+ type: 'call.reject';
305
+ call_id: string;
306
+ reason?: RejectReason;
307
+ } | {
308
+ type: 'call.hangup';
309
+ call_id: string;
310
+ } | {
311
+ type: 'call.hold';
312
+ call_id: string;
313
+ } | {
314
+ type: 'call.resume';
315
+ call_id: string;
316
+ } | {
317
+ type: 'call.mute';
318
+ call_id: string;
319
+ } | {
320
+ type: 'call.transfer';
321
+ call_id: string;
322
+ destination: string;
323
+ } | {
324
+ type: 'call.transfer.attended';
325
+ req_id?: string;
326
+ call_id: string;
327
+ step: 'consult' | 'complete';
328
+ destination?: string;
329
+ sdp?: string;
330
+ } | {
331
+ type: 'call.unmute';
332
+ call_id: string;
333
+ } | {
334
+ type: 'sdp.offer';
335
+ call_id: string;
336
+ sdp: string;
337
+ } | {
338
+ type: 'sdp.answer';
339
+ call_id: string;
340
+ sdp: string;
341
+ } | {
342
+ type: 'ice.candidate';
343
+ call_id: string;
344
+ candidate: string;
345
+ sdp_mid: string | null;
346
+ sdp_m_line_index: number | null;
347
+ } | {
348
+ type: 'ice.done';
349
+ call_id: string;
350
+ } | {
351
+ type: 'presence.subscribe';
352
+ req_id?: string;
353
+ users: string[];
354
+ };
355
+ interface PresenceEntryWire {
356
+ user_id: string;
357
+ name: string;
358
+ status: PresenceStatus;
359
+ do_not_disturb?: boolean;
360
+ status_text?: string | null;
361
+ updated_at: string;
362
+ }
363
+ type ServerMessage = {
364
+ type: 'authenticated';
365
+ req_id?: string | null;
366
+ user_id: string;
367
+ account_id: string;
368
+ reconnected?: boolean;
369
+ } | {
370
+ type: 'auth.refreshed';
371
+ req_id?: string | null;
372
+ } | {
373
+ type: 'pong';
374
+ req_id?: string | null;
375
+ } | {
376
+ type: 'network.changed';
377
+ } | {
378
+ type: 'error';
379
+ code: string;
380
+ message: string;
381
+ req_id?: string | null;
382
+ call_id?: string | null;
383
+ fatal?: boolean;
384
+ context?: {
385
+ users?: string[];
386
+ };
387
+ } | {
388
+ type: 'call.trying';
389
+ call_id: string;
390
+ req_id?: string | null;
391
+ } | {
392
+ type: 'call.ringing';
393
+ call_id: string;
394
+ } | {
395
+ type: 'call.incoming';
396
+ call_id: string;
397
+ from: string;
398
+ from_name: string | null;
399
+ to: string;
400
+ } | {
401
+ type: 'call.answered';
402
+ call_id: string;
403
+ answered_at?: string;
404
+ } | {
405
+ type: 'call.ended';
406
+ call_id: string;
407
+ reason: CallEndReason;
408
+ duration_seconds: number | null;
409
+ } | {
410
+ type: 'call.restored';
411
+ call_id: string;
412
+ state: 'ringing' | 'active' | 'held';
413
+ from: string;
414
+ from_name: string | null;
415
+ to: string;
416
+ direction: CallDirection;
417
+ answered_at: string | null;
418
+ } | {
419
+ type: 'call.held';
420
+ call_id: string;
421
+ held_by: HeldBy;
422
+ } | {
423
+ type: 'call.resumed';
424
+ call_id: string;
425
+ } | {
426
+ type: 'sdp.offer';
427
+ call_id: string;
428
+ sdp: string;
429
+ } | {
430
+ type: 'sdp.pranswer';
431
+ call_id: string;
432
+ sdp: string;
433
+ } | {
434
+ type: 'sdp.answer';
435
+ call_id: string;
436
+ sdp: string;
437
+ } | {
438
+ type: 'ice.candidate';
439
+ call_id: string;
440
+ candidate: string;
441
+ sdp_mid: string | null;
442
+ sdp_m_line_index: number | null;
443
+ } | {
444
+ type: 'ice.done';
445
+ call_id: string;
446
+ } | {
447
+ type: 'presence.list';
448
+ req_id?: string | null;
449
+ users: PresenceEntryWire[];
450
+ } | ({
451
+ type: 'presence.update';
452
+ } & PresenceEntryWire);
453
+
454
+ /**
455
+ * Opens the signaling WebSocket. Defaults to a bare `new WebSocket(url,
456
+ * protocols)` (web). React Native needs a variant that attaches a `User-Agent`
457
+ * header — the signaling ingress 403s a handshake without one, and iOS's
458
+ * WebSocket sends none by default — so the RN softphone provider supplies its
459
+ * own factory here. Browsers forbid overriding `User-Agent`, so web never does.
460
+ */
461
+ type SignalingSocketFactory = (url: string, protocols: string[]) => WebSocket;
462
+ /**
463
+ * Subscribe to "the environment came back to the foreground / regained the
464
+ * network" and return an unsubscribe fn. Injected like the socket factory: web
465
+ * uses the default below (DOM lifecycle events); React Native supplies an
466
+ * AppState-backed variant (there is no `document` on RN). The transport uses
467
+ * this to actively re-verify a connection the browser may have silently dropped
468
+ * while backgrounded.
469
+ */
470
+ type AppResumeSubscribe = (cb: () => void) => () => void;
471
+ type TransportEvents = {
472
+ open: () => void;
473
+ message: (msg: ServerMessage) => void;
474
+ closed: (reason: {
475
+ fatal: boolean;
476
+ error?: PhoneError;
477
+ }) => void;
478
+ reconnecting: (attempt: number, delayMs: number) => void;
479
+ };
480
+ declare class Transport {
481
+ private ws;
482
+ private url;
483
+ private listeners;
484
+ private reconnectAttempts;
485
+ private closedByUser;
486
+ private autoReconnect;
487
+ private createSocket;
488
+ private subscribeResume;
489
+ private terminalError;
490
+ private unsubscribeResume;
491
+ private probeTimer;
492
+ private probeInFlight;
493
+ private reconnectPending;
494
+ private stopped;
495
+ constructor(url: string, autoReconnect: boolean, createSocket?: SignalingSocketFactory, subscribeResume?: AppResumeSubscribe);
496
+ on<E extends keyof TransportEvents>(event: E, handler: TransportEvents[E]): void;
497
+ connect(): void;
498
+ /** True when the socket is open and a send() would succeed. */
499
+ isOpen(): boolean;
500
+ send(msg: ClientMessage): void;
501
+ /**
502
+ * Best-effort send for messages emitted on their own async timeline (ICE
503
+ * candidates fire from the peer connection, and can arrive while the socket is
504
+ * mid-reconnect or closed). Silently drops when the socket isn't open rather
505
+ * than throwing an uncaught error into an event handler. Trickle ICE tolerates
506
+ * loss, and a closed socket means the call is already tearing down.
507
+ */
508
+ trySend(msg: ClientMessage): void;
509
+ close(): void;
510
+ private openSocket;
511
+ private scheduleReconnect;
512
+ private onWake;
513
+ private forceReconnect;
514
+ private clearProbe;
515
+ }
516
+
517
+ type CallEventMap = {
518
+ trying: () => void;
519
+ ringing: () => void;
520
+ answered: () => void;
521
+ held: (by: HeldBy) => void;
522
+ resumed: () => void;
523
+ ended: (reason: CallEndReason) => void;
524
+ audioInputChanged: (deviceId: string | null) => void;
525
+ audioInputLost: () => void;
526
+ };
527
+ type Listener$1<K extends keyof CallEventMap> = CallEventMap[K];
528
+ interface CallInit {
529
+ id: string;
530
+ direction: CallDirection;
531
+ from: string;
532
+ fromName: string | null;
533
+ to: string;
534
+ initialState: CallState;
535
+ transport: Transport;
536
+ iceServers: RTCIceServer$1[];
537
+ startConsult: (parent: Call, destination: string) => Promise<Call>;
538
+ ringback?: Ringback;
539
+ audioInputDeviceId?: string;
540
+ audioOutputDeviceId?: string;
541
+ }
542
+ declare class Call {
543
+ id: string;
544
+ readonly direction: CallDirection;
545
+ readonly from: string;
546
+ readonly fromName: string | null;
547
+ readonly to: string;
548
+ state: CallState;
549
+ isMuted: boolean;
550
+ duration: number;
551
+ readonly peerConnection: RTCPeerConnection;
552
+ get isHeld(): boolean;
553
+ /**
554
+ * Whether the call is a live, connected conversation — answered and not ended.
555
+ * Hold is a media modifier, not a separate lifecycle, so BOTH `active` and
556
+ * `held` count as connected; `trying`/`ringing` (not yet up) and `ended` do
557
+ * not. This is the single source of truth for "is this call
558
+ * transferable / bridgeable / controllable" — every such gate (transfer,
559
+ * completeTransfer, the in-call control row, isCallActive) reads it so the
560
+ * rule can't drift between call sites.
561
+ */
562
+ get isConnected(): boolean;
563
+ /**
564
+ * Whether DTMF can be sent on this call — the gate the softphone UI uses for
565
+ * its in-call keypad. False when the audio sender exposes no `.dtmf` (e.g. a
566
+ * connected call that never got a usable sender). Only meaningful once the
567
+ * call is active — the sender is attached during answer/offer negotiation
568
+ * (attachDtmfSender). `sendDtmf()` still throws if called anyway. Both web and
569
+ * native go through the same sender: web via the browser RTCDTMFSender, native
570
+ * via react-native-webrtc's `RTCRtpSender.dtmf` (provided by the DialStack fork).
571
+ */
572
+ get canSendDtmf(): boolean;
573
+ private transport;
574
+ private readonly startConsult;
575
+ private localStream;
576
+ private remoteStream;
577
+ private dtmfSender;
578
+ private durationTimer;
579
+ private answeredAt;
580
+ private listeners;
581
+ private endedSettled;
582
+ private pendingAnswerSdp;
583
+ private answerSent;
584
+ private wantsAnswer;
585
+ private localMediaReady;
586
+ private pendingRemoteCandidates;
587
+ private remoteDescriptionSet;
588
+ private readonly gatheringAborts;
589
+ private readonly ringback;
590
+ private remoteAudioFlowing;
591
+ private audioInputDeviceId_;
592
+ private switchChain;
593
+ constructor(init: CallInit);
594
+ whenLocalMediaReady(): Promise<void>;
595
+ private acquireLocalMedia;
596
+ /** The selected mic, or null for the OS default. */
597
+ get audioInputDeviceId(): string | null;
598
+ /**
599
+ * The device actually captured. May differ from `audioInputDeviceId` at acquisition,
600
+ * where the constraint is `ideal` — how a caller detects a substitution.
601
+ */
602
+ get effectiveAudioInputDeviceId(): string | null;
603
+ /** Internal — the phone-level setter broadcasts this to every live call. */
604
+ setAudioInputDevice(deviceId: string | null): Promise<void>;
605
+ private switchAudioInput;
606
+ /** Route this call's ringback tone to `deviceId` (null = OS default). */
607
+ setAudioOutputDevice(deviceId: string | null): void;
608
+ private watchCaptureTrack;
609
+ private get isFinished();
610
+ on<K extends keyof CallEventMap>(event: K, handler: Listener$1<K>): void;
611
+ off<K extends keyof CallEventMap>(event: K, handler?: Listener$1<K>): void;
612
+ answer(): void;
613
+ private sendAnswer;
614
+ reject(reason?: RejectReason): void;
615
+ hangup(): void;
616
+ hold(): void;
617
+ resume(): void;
618
+ mute(): void;
619
+ unmute(): void;
620
+ sendDtmf(digits: string, duration?: number, interToneGap?: number): void;
621
+ /**
622
+ * Blind transfer: immediately redirect the remote party to `destination`.
623
+ * Fire-and-forget — on success the call ends with reason 'transferred';
624
+ * on failure a non-fatal error is emitted and the call stays active.
625
+ * The call must be active.
626
+ */
627
+ transfer(destination: string): void;
628
+ /**
629
+ * Attended (consultative) transfer, step 1: hold this call and dial
630
+ * `destination` as a consult leg. Resolves with the consult Call. Once the
631
+ * consult party answers, call `completeTransfer()` on THIS call to bridge
632
+ * them; or hang up the consult and `resume()` to abandon the transfer.
633
+ */
634
+ attendedTransfer(destination: string): Promise<Call>;
635
+ /**
636
+ * Attended transfer, step 2: bridge the remote party to the consult leg's
637
+ * party. Valid once the consult call (started via `attendedTransfer`) is
638
+ * answered; this call is held at that point. On success both calls end
639
+ * with reason 'transferred'.
640
+ */
641
+ completeTransfer(): void;
642
+ private assertTransferable;
643
+ get remoteMediaStream(): MediaStream;
644
+ startOutbound(): Promise<RTCSessionDescriptionInit>;
645
+ prepareAnswerForOffer(sdp: string): Promise<void>;
646
+ private buildAnswer;
647
+ acceptRemoteProvisionalAnswer(sdp: string): Promise<boolean>;
648
+ acceptRemoteAnswer(sdp: string): Promise<void>;
649
+ addRemoteCandidate(candidate: string, sdpMid: string | null, sdpMLineIndex: number | null): Promise<void>;
650
+ private flushPendingRemoteCandidates;
651
+ handleServerMessage(msg: ServerMessage): void;
652
+ dispose(): void;
653
+ private settleEnded;
654
+ private releaseMedia;
655
+ private onRemoteAudioFlowing;
656
+ private wirePeerConnection;
657
+ private waitForIceGatheringComplete;
658
+ private abortIceGathering;
659
+ private hasLocalCandidate;
660
+ private localDescriptionOr;
661
+ private attachDtmfSender;
662
+ private startDurationTimer;
663
+ private stopDurationTimer;
664
+ private emit;
665
+ }
666
+
667
+ type PhoneEventMap = {
668
+ connected: () => void;
669
+ disconnected: (error?: PhoneError) => void;
670
+ reconnected: () => void;
671
+ reconnecting: (attempt: number, delayMs: number) => void;
672
+ incoming: (call: Call) => void;
673
+ presenceList: (entries: PresenceEntry[]) => void;
674
+ presenceUpdate: (update: PresenceUpdate) => void;
675
+ 'network.changed': () => void;
676
+ audioDevicesChanged: (devices: AudioDeviceList) => void;
677
+ error: (err: PhoneError) => void;
678
+ };
679
+ type Listener<K extends keyof PhoneEventMap> = PhoneEventMap[K];
680
+ declare class DialStackPhone {
681
+ isConnected: boolean;
682
+ readonly activeCalls: Call[];
683
+ private token;
684
+ private apiBaseUrl;
685
+ private signalingUrl;
686
+ private autoReconnect;
687
+ private iceServersOverride;
688
+ private onTokenExpiring;
689
+ private ringback;
690
+ private createSignalingSocket;
691
+ private onAppResume;
692
+ private transport;
693
+ private readonly handshake;
694
+ private iceServers;
695
+ private listeners;
696
+ private pendingOutbound;
697
+ private pendingCall;
698
+ private hasConnectedOnce;
699
+ private reqSeq;
700
+ private tokenRefreshTimer;
701
+ private pendingRefresh;
702
+ private emergencyAddressId;
703
+ private presentedEmergencyAddressId_;
704
+ private storageUserId;
705
+ private storage;
706
+ private audioInputDeviceId_;
707
+ private audioOutputDeviceId_;
708
+ private deviceChangeUnsubscribe;
709
+ constructor(options: PhoneOptions);
710
+ /** The microphone `deviceId` new calls capture, or null for the OS default. */
711
+ get audioInputDeviceId(): string | null;
712
+ /** The speaker `deviceId` the ringback plays through, or null for the OS default. */
713
+ get audioOutputDeviceId(): string | null;
714
+ /**
715
+ * Record a microphone preference WITHOUT acquiring it — for a host restoring a saved
716
+ * selection at construction time. `setAudioInputDevice` probes the device, which
717
+ * prompts for permission when no call is up; this doesn't, so a returning user isn't
718
+ * asked for the mic on page load. An id that no longer resolves falls back to the
719
+ * default at the next call's acquisition.
720
+ */
721
+ seedAudioInputDevice(deviceId: string | null): void;
722
+ /**
723
+ * Select the microphone: seeds future calls and live-switches calls already up. Held
724
+ * for this phone's lifetime only — saving the choice across sessions is the host's
725
+ * (the React softphone does it). Rejects without recording the id when the device
726
+ * can't be acquired. A single live call's failure surfaces on `error` rather than
727
+ * rejecting, so one bad call can't leave the others behind.
728
+ */
729
+ setAudioInputDevice(deviceId: string | null): Promise<void>;
730
+ /**
731
+ * Select the speaker for the ringback tone. Does NOT route remote call audio — that
732
+ * plays through a media element the host owns, and only its owner can call
733
+ * `setSinkId`. Void-returning because nothing here is verified or awaited.
734
+ */
735
+ setAudioOutputDevice(deviceId: string | null): void;
736
+ /**
737
+ * The available audio devices. Deliberately does not prompt for mic access — opening
738
+ * a picker shouldn't spring a permission dialog — so names stay hidden until
739
+ * permission has been granted once (`labelsAvailable: false`).
740
+ */
741
+ listAudioDevices(): Promise<AudioDeviceList>;
742
+ on<K extends keyof PhoneEventMap>(event: K, handler: Listener<K>): void;
743
+ off<K extends keyof PhoneEventMap>(event: K, handler?: Listener<K>): void;
744
+ /**
745
+ * Reference-counted off the existing listener set.
746
+ *
747
+ * Deliberately NOT tied to connect/disconnect: `reconnect()` calls `disconnect()`, so
748
+ * releasing there would permanently kill device events after any reconnect.
749
+ */
750
+ private syncDeviceChangeSubscription;
751
+ connect(): Promise<void>;
752
+ disconnect(): void;
753
+ /**
754
+ * Tear down and reconnect, re-running the `authenticate` handshake. The new
755
+ * session presents the current `emergencyAddressId`, which is where the server
756
+ * binds the emergency address to the connection's network — so this is how an
757
+ * app applies a just-selected/created address (or re-binds after a network
758
+ * move). Safe to call while idle; any live calls are torn down.
759
+ */
760
+ reconnect(): Promise<void>;
761
+ /**
762
+ * Re-present a chosen `emergencyAddressId` on a fresh authenticate handshake, in
763
+ * one call. Rebinding requires a fresh socket (the server binds E911 once per
764
+ * connection, after REGISTER), so this tears down and reconnects like
765
+ * `reconnect()`; resolves once the server confirms the new binding.
766
+ */
767
+ reconnectWithEmergency(emergencyAddressId: string): Promise<void>;
768
+ call(destination: string): Promise<Call>;
769
+ private startConsult;
770
+ private placeOutbound;
771
+ getCall(callId: string): Call | undefined;
772
+ /**
773
+ * Subscribe to presence (BLF) for an explicit, bounded set of users. The
774
+ * watch set is required and non-empty — there is no whole-account default —
775
+ * and is capped at {@link MAX_PRESENCE_TARGETS}; both bounds mirror the
776
+ * server, which rejects a bad list with a non-fatal `invalid_message` error.
777
+ *
778
+ * The server replies with a `presence.list` snapshot (emitted as
779
+ * `presenceList`), always before any `presence.update` delta
780
+ * (`presenceUpdate`) for the users that snapshot introduces. Deltas follow as
781
+ * those users' status changes.
782
+ *
783
+ * A status is `available`, `on_call`, or `offline` — `offline` meaning the user
784
+ * has no registered device and so cannot currently receive calls. The first
785
+ * delta after a snapshot may restate a status the snapshot already reported, so
786
+ * key the roster by `userId` and upsert.
787
+ *
788
+ * Users whose subscription can't be established are omitted from the snapshot
789
+ * and named in a non-fatal `presence_unavailable` error (its `PhoneError`
790
+ * carries no user list; listen on `error` for the code).
791
+ */
792
+ subscribePresence(userIds: string[]): void;
793
+ setPresence(_status: SettablePresenceStatus, _statusText?: string): Promise<void>;
794
+ /**
795
+ * Register and validate an emergency (E911) address. Creates the
796
+ * resource via the REST API (validated against the carrier MSAG), persists
797
+ * its id, and presents it on the next connect so the server binds it to the
798
+ * device's network. This does not alter an already-authenticated session. To
799
+ * apply a new address there (e.g. responding to a `network.changed` event),
800
+ * pass the returned id to `reconnectWithEmergency()`.
801
+ *
802
+ * Rejects with a `PhoneError` (code `invalid_message`) when the address
803
+ * can't be validated.
804
+ */
805
+ setEmergencyAddress(address: EmergencyAddressInput): Promise<EmergencyAddress>;
806
+ /**
807
+ * Select an already-saved emergency address to present on the next
808
+ * (re)connect (sets + persists it). The server binds it at the authenticate
809
+ * handshake, so call `reconnect()` afterwards for it to take effect.
810
+ */
811
+ selectEmergencyAddress(id: string): void;
812
+ /**
813
+ * The emergency-address id actually sent in the CURRENT socket's authenticate
814
+ * frame (latched at send time), or null. This is what the E911 gate keys off —
815
+ * a saved address's `registered_ip`, or a selection made without a reconnect,
816
+ * does NOT mean this session bound it.
817
+ */
818
+ get presentedEmergencyAddressId(): string | null;
819
+ /** Fetch a saved emergency address (defaults to the one this phone uses). */
820
+ getEmergencyAddress(id?: string): Promise<EmergencyAddress>;
821
+ /**
822
+ * List the user's saved emergency addresses (location profiles).
823
+ *
824
+ * Auto-paginating: `await` it for the first page envelope, or iterate the
825
+ * full collection with `autoPagingEach()` / `autoPagingToArray()` —
826
+ * subsequent pages are fetched lazily.
827
+ */
828
+ listEmergencyAddresses(): PaginatedList<ListResponse<EmergencyAddress>>;
829
+ /**
830
+ * List the account directory — the colleagues this user may see, by name
831
+ * (`GET /v1/me/directory`). Resolved from the session token alone (no account
832
+ * context needed), so a softphone can discover who to watch for presence, or
833
+ * populate a contact / transfer picker, without its backend supplying the set.
834
+ *
835
+ * Auto-paginates the whole directory and returns a flat list. To watch every
836
+ * colleague's presence: `phone.subscribePresence((await phone.listDirectory()).map(e => e.user))`.
837
+ * Note presence subscription accepts at most 100 users at once, so watching a
838
+ * very large directory means subscribing to a subset.
839
+ */
840
+ listDirectory(): Promise<DirectoryEntry[]>;
841
+ /** Delete a saved emergency address. Clears the local selection if it matched. */
842
+ deleteEmergencyAddress(id: string): Promise<void>;
843
+ /**
844
+ * Clear an address's network binding (registered_ip) via the REST API. The
845
+ * next connect re-registers the current network. Useful after a move so the
846
+ * address re-binds where the device now is.
847
+ */
848
+ clearEmergencyAddressRegisteredIp(id: string): Promise<void>;
849
+ private nextReqId;
850
+ private adoptToken;
851
+ private scheduleTokenRefresh;
852
+ private fireTokenRefresh;
853
+ private clearTokenRefreshTimer;
854
+ private apiRequest;
855
+ private fetchIceServers;
856
+ private handleMessage;
857
+ private handleIncoming;
858
+ private emit;
859
+ }
860
+
861
+ /**
862
+ * `usePhone` — sole owner of the softphone's `DialStackPhone` instance and its
863
+ * connection lifecycle, web and React Native.
864
+ *
865
+ * It constructs the phone from credentials, subscribes to the connection events
866
+ * (connected / reconnecting / disconnected / fatal error / network.changed),
867
+ * maps them to a single `connection` state, and tears the phone down on unmount /
868
+ * credential change. It does NOT know about call legs — call state lives in
869
+ * `useCalls(phone, ...)`, and E911 provisioning in `useEmergencyBinding(phone, ...)`.
870
+ * Both are handed the live `phone` this hook owns.
871
+ *
872
+ * It is platform-agnostic: it imports only the headless core (`../../webrtc`),
873
+ * never the DOM or React Native.
874
+ */
875
+
876
+ /** Connection lifecycle surfaced to the softphone UI. */
877
+ type SoftphoneConnectionState = 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'disconnected' | 'error';
878
+
879
+ /**
880
+ * `useCallActions` — platform-agnostic per-call imperative actions, shared
881
+ * web ↔ RN.
882
+ *
883
+ * Wraps the foreground `Call`'s imperative actions (answer / reject / hangup /
884
+ * mute / hold / DTMF / transfer) so both softphones call them identically, with
885
+ * the same `PhoneError` → `onError` normalization. This is call CONTROL only —
886
+ * no view-state. In-call presentation state (the keypad/transfer overlay flags of
887
+ * the built-in `OngoingCall`) lives in `useCallOverlays`, so a consumer building a
888
+ * custom layout gets these actions without any built-in-UI plumbing. It
889
+ * deliberately does NOT render anything — it returns plain callbacks the platform
890
+ * UI binds to its own controls.
891
+ */
892
+
893
+ /**
894
+ * The imperative actions for one specific call, with core throws contained via
895
+ * `onError`. This is the per-call subset shared by every call surface (the
896
+ * foreground in-call controls AND an incoming/held card): each card binds these
897
+ * to a specific `Call` rather than only the foreground one.
898
+ */
899
+ interface CallActions {
900
+ answer: () => void;
901
+ reject: () => void;
902
+ hangup: () => void;
903
+ toggleMute: () => void;
904
+ toggleHold: () => void;
905
+ /** Send a DTMF digit (no-op when no call). */
906
+ sendDtmf: (digit: string) => void;
907
+ }
908
+ interface UseCallActions extends CallActions {
909
+ /**
910
+ * Blind-transfer the active call to `destination`. Returns `true` when the
911
+ * transfer was *initiated* (so the UI can close its transfer overlay), `false`
912
+ * on empty input or a synchronous failure routed to `onError`. `true` means
913
+ * initiated, not confirmed — the core call is fire-and-forget, so the outcome
914
+ * arrives later as the call ending ('transferred') or an `onError`.
915
+ */
916
+ transfer: (destination: string) => boolean;
917
+ /**
918
+ * Build the per-call action subset for a SPECIFIC call — used by the
919
+ * incoming/held cards, which act on a call that isn't the foreground one. The
920
+ * foreground actions above are exactly `callActionsFor(activeCall)`.
921
+ */
922
+ callActionsFor: (call: Call | null) => CallActions;
923
+ }
924
+
925
+ /**
926
+ * `useCallOverlays` — the in-call overlay flags, shared web ↔ RN.
927
+ *
928
+ * These are presentation state for ONE screen (the built-in `OngoingCall`): the
929
+ * DTMF keypad panel, the blind-transfer input, the add-call dial input, and the
930
+ * audio-device picker, which are mutually exclusive.
931
+ * They are deliberately kept OUT of `useCallActions` (which is platform-agnostic
932
+ * call control) so a consumer building a custom layout gets call control without
933
+ * this built-in-UI plumbing. It's a shared hook rather than local `OngoingCall`
934
+ * state only so web and React Native can't drift on WHEN the overlays reset.
935
+ */
936
+
937
+ interface UseCallOverlays {
938
+ /** Whether the in-call DTMF keypad overlay is showing. */
939
+ showKeypad: boolean;
940
+ /** Whether the in-call transfer input overlay is showing. */
941
+ showTransfer: boolean;
942
+ /** Whether the in-call audio-device picker is showing. */
943
+ showDevices: boolean;
944
+ /** Whether the in-call add-call dial input is showing. */
945
+ showAddCall: boolean;
946
+ /** Toggle the DTMF keypad (closes the other overlays — they're exclusive). */
947
+ toggleKeypad: () => void;
948
+ /** Toggle the transfer input (closes the other overlays). */
949
+ toggleTransfer: () => void;
950
+ /** Toggle the audio-device picker (closes the other overlays). */
951
+ toggleDevices: () => void;
952
+ /** Toggle the add-call dial input (closes the other overlays). */
953
+ toggleAddCall: () => void;
954
+ /** Close the transfer overlay (e.g. after a transfer is handed off). */
955
+ closeTransfer: () => void;
956
+ /** Close the add-call overlay (e.g. once the second call is placed). */
957
+ closeAddCall: () => void;
958
+ }
959
+
960
+ /**
961
+ * `useEmergencyBinding` — the Softphone's built-in E911 provisioning logic.
962
+ *
963
+ * Model: "always send it, the server decides." The server binds the presented
964
+ * emergency address to the connection's network at the `authenticate` handshake
965
+ * and is the authority on whether it's valid here — it rejects a mismatch via
966
+ * the `network.changed` signal.
967
+ *
968
+ * - On connect, if the session has a saved address but presented none (fresh
969
+ * browser), auto-present it (select + reconnect once). Same network → the
970
+ * server binds it silently (reused). Different network → the server denies
971
+ * (network.changed) → we prompt.
972
+ * - `bound` is true unless the server denied (network.changed) or there is no
973
+ * saved address to present at all.
974
+ *
975
+ * When the host supplies `emergencyAddressId`, it manages E911 itself and this
976
+ * hook stays dormant (`disabled`). 911/933 are never gated by any of this.
977
+ *
978
+ * It talks to the `DialStackPhone` (owned by `usePhone`) DIRECTLY via a narrow
979
+ * interface — the subset of phone methods E911 needs. That interface is the
980
+ * hook's testable seam: a test passes a small phone-shaped stub, no full mock.
981
+ */
982
+
983
+ interface UseEmergencyBinding {
984
+ /** True until the first post-connect status resolves. */
985
+ loading: boolean;
986
+ /**
987
+ * True when outbound PSTN is usable for this session — the server accepted the
988
+ * presented address for the current network. False when it denied
989
+ * (network.changed) or there's no saved address → show the prompt.
990
+ */
991
+ bound: boolean;
992
+ /** Saved addresses to offer as "Are you here?" choices. */
993
+ savedAddresses: EmergencyAddress[];
994
+ /** True while an address submit (confirm/create, which forces a reconnect) is in flight. */
995
+ submitting: boolean;
996
+ /** Last error (e.g. a carrier rejection). */
997
+ error: string | null;
998
+ /** Called from the Softphone when the phone emits `network.changed` (denied). */
999
+ onNetworkChanged: () => void;
1000
+ /** Confirm an existing saved address for this network (selects + reconnects). */
1001
+ confirm: (id: string) => Promise<void>;
1002
+ /** Create + validate a new address, then bind it (reconnect). */
1003
+ create: (input: EmergencyAddressInput) => Promise<void>;
1004
+ }
1005
+
1006
+ /**
1007
+ * Appearance and theming types for DialStack SDK
1008
+ */
1009
+ /**
1010
+ * Theme variant for components
1011
+ */
1012
+ type Theme = 'light' | 'dark' | 'auto';
1013
+ /**
1014
+ * CSS variable definitions for theming
1015
+ *
1016
+ * All values should be valid CSS values (colors, sizes, etc.)
1017
+ */
1018
+ interface AppearanceVariables {
1019
+ colorPrimary?: string;
1020
+ colorPrimaryHover?: string;
1021
+ colorBackground?: string;
1022
+ colorText?: string;
1023
+ colorTextSecondary?: string;
1024
+ /** Placeholder text in inputs. Defaults to `colorTextSecondary` at 50% so it
1025
+ * reads dimmer than real secondary text; override to control independently. */
1026
+ colorPlaceholder?: string;
1027
+ colorDanger?: string;
1028
+ colorSuccess?: string;
1029
+ colorWarning?: string;
1030
+ colorSurfaceSubtle?: string;
1031
+ colorBorder?: string;
1032
+ colorBorderSubtle?: string;
1033
+ /** Fill for form inputs. Defaults to `colorBackground`; set separately when
1034
+ * the input fill should differ from the surface the component sits on. */
1035
+ colorInputBackground?: string;
1036
+ fontFamily?: string;
1037
+ fontSizeBase?: string;
1038
+ fontSizeSmall?: string;
1039
+ fontSizeLarge?: string;
1040
+ fontSizeXLarge?: string;
1041
+ fontWeightNormal?: string;
1042
+ fontWeightMedium?: string;
1043
+ fontWeightBold?: string;
1044
+ lineHeight?: string;
1045
+ spacingUnit?: string;
1046
+ spacingXs?: string;
1047
+ spacingSm?: string;
1048
+ spacingMd?: string;
1049
+ spacingLg?: string;
1050
+ spacingXl?: string;
1051
+ borderRadius?: string;
1052
+ borderRadiusSmall?: string;
1053
+ borderRadiusLarge?: string;
1054
+ transitionDuration?: string;
1055
+ focusRingColor?: string;
1056
+ focusRingWidth?: string;
1057
+ /** Gap between the control edge and the focus ring. Defaults to `0` (flush). */
1058
+ focusRingOffset?: string;
1059
+ /** Height of form controls (inputs, tabs, pagination buttons). Defaults to
1060
+ * `auto` (font-derived); set to a fixed value for consistent control heights. */
1061
+ controlHeight?: string;
1062
+ iconSize?: string;
1063
+ iconSizeSmall?: string;
1064
+ playerButtonSize?: string;
1065
+ playerProgressHeight?: string;
1066
+ playerProgressHandleSize?: string;
1067
+ unreadIndicatorSize?: string;
1068
+ spinnerSize?: string;
1069
+ timeDisplayWidth?: string;
1070
+ }
1071
+ /**
1072
+ * Appearance options for theming components
1073
+ */
1074
+ interface AppearanceOptions$1 {
1075
+ /**
1076
+ * Theme variant
1077
+ */
1078
+ theme?: Theme;
1079
+ /**
1080
+ * Custom variables for styling
1081
+ */
1082
+ variables?: AppearanceVariables;
1083
+ }
1084
+
1085
+ /**
1086
+ * Shared design tokens for the DialStack Softphone.
1087
+ *
1088
+ * This module is intentionally framework-agnostic (no DOM, no React Native) so
1089
+ * that BOTH the web Softphone (Web Component, consumes these as CSS values) and the
1090
+ * React Native Softphone (consumes these in a StyleSheet) render from a single
1091
+ * source of truth — that is what makes the two look identical by default.
1092
+ *
1093
+ * Colors resolve from the same `AppearanceOptions` surface the rest of the SDK
1094
+ * uses (theme + variables), so theming through `DialstackComponentsProvider` /
1095
+ * the `appearance` prop flows through identically on both platforms.
1096
+ */
1097
+
1098
+ /** Flat color set the Softphone paints from, after appearance is resolved. */
1099
+ interface SoftphonePalette {
1100
+ /** Component background. */
1101
+ background: string;
1102
+ /** Dial-pad key / control surface fill. */
1103
+ surface: string;
1104
+ /** Surface fill while pressed/active. */
1105
+ surfaceActive: string;
1106
+ /** Primary text (digits, destination, peer name). */
1107
+ text: string;
1108
+ /** Secondary text (letter sub-labels, call state, hints). */
1109
+ textSecondary: string;
1110
+ /** Hairline borders/dividers. */
1111
+ border: string;
1112
+ /** Accent (links, focus ring, keypad/secondary control highlight). */
1113
+ accent: string;
1114
+ /** Call / Answer (green) action color. */
1115
+ success: string;
1116
+ /** Hang up / Decline (red) action color. */
1117
+ danger: string;
1118
+ /** Warning / caution (amber) — used by the E911 banner. */
1119
+ warning: string;
1120
+ /** Foreground used on top of accent/success/danger fills. */
1121
+ onAccent: string;
1122
+ }
1123
+ /**
1124
+ * Resolve the final palette from appearance: theme picks the base set, then any
1125
+ * `appearance.variables` overrides (the same keys other SDK components honor)
1126
+ * are layered on top. Unrecognized/absent variables fall back to the theme
1127
+ * default, so a bare `{ theme: 'dark' }` and a fully-specified palette both work.
1128
+ */
1129
+ declare function resolveSoftphonePalette(appearance?: AppearanceOptions$1): SoftphonePalette;
1130
+
1131
+ /**
1132
+ * English locale strings for DialStack SDK components
1133
+ */
1134
+ declare const en: {
1135
+ common: {
1136
+ loading: string;
1137
+ error: string;
1138
+ noResults: string;
1139
+ delete: string;
1140
+ call: string;
1141
+ cancel: string;
1142
+ confirm: string;
1143
+ previous: string;
1144
+ next: string;
1145
+ perPage: string;
1146
+ showing: string;
1147
+ play: string;
1148
+ pause: string;
1149
+ };
1150
+ softphone: {
1151
+ title: string;
1152
+ destinationPlaceholder: string;
1153
+ call: string;
1154
+ backspace: string;
1155
+ connecting: string;
1156
+ reconnecting: string;
1157
+ disconnected: string;
1158
+ connectionError: string;
1159
+ callError: string;
1160
+ micPermissionError: string;
1161
+ dismiss: string;
1162
+ unknownCaller: string;
1163
+ incomingCall: string;
1164
+ answer: string;
1165
+ decline: string;
1166
+ hangUp: string;
1167
+ mute: string;
1168
+ unmute: string;
1169
+ hold: string;
1170
+ resume: string;
1171
+ keypad: string;
1172
+ transfer: string;
1173
+ transferPlaceholder: string;
1174
+ transferSend: string;
1175
+ transferNow: string;
1176
+ transferConsult: string;
1177
+ transferComplete: string;
1178
+ transferConsulting: string;
1179
+ transferOriginalOnHold: string;
1180
+ addCall: string;
1181
+ addCallPlaceholder: string;
1182
+ addCallSend: string;
1183
+ cancel: string;
1184
+ stateTrying: string;
1185
+ stateRinging: string;
1186
+ stateActive: string;
1187
+ stateHeld: string;
1188
+ stateEnded: string;
1189
+ done: string;
1190
+ audioDevices: string;
1191
+ audioMicrophone: string;
1192
+ audioSpeaker: string;
1193
+ audioSystemDefault: string;
1194
+ audioUnnamedDevice: string;
1195
+ audioLabelsHidden: string;
1196
+ audioDevicesUnsupported: string;
1197
+ audioSpeakerUnsupported: string;
1198
+ audioMicrophoneLost: string;
1199
+ switchToCall: string;
1200
+ heldCallsLabel: string;
1201
+ callWaiting: string;
1202
+ emergencyPrompt: string;
1203
+ emergencyHint: string;
1204
+ emergencyConfirm: string;
1205
+ emergencyNewLocation: string;
1206
+ emergencyUseLocation: string;
1207
+ emergencyLocating: string;
1208
+ emergencyNumber: string;
1209
+ emergencyStreet: string;
1210
+ emergencyUnit: string;
1211
+ emergencyCity: string;
1212
+ emergencyState: string;
1213
+ emergencyPostalCode: string;
1214
+ emergencySave: string;
1215
+ emergencySaving: string;
1216
+ emergencyBack: string;
1217
+ };
1218
+ aiAgent: {
1219
+ loading: string;
1220
+ loadFailed: string;
1221
+ fields: {
1222
+ name: string;
1223
+ namePlaceholder: string;
1224
+ nameHint: string;
1225
+ extensionNumber: string;
1226
+ extensionNumberPlaceholder: string;
1227
+ extensionNumberHint: string;
1228
+ extensionChecking: string;
1229
+ personaName: string;
1230
+ personaNamePlaceholder: string;
1231
+ personaNameHint: string;
1232
+ greetingName: string;
1233
+ greetingNamePlaceholder: string;
1234
+ greetingNameHintWithName: string;
1235
+ greetingNameHintGeneric: string;
1236
+ instructions: string;
1237
+ instructionsPlaceholder: string;
1238
+ instructionsHint: string;
1239
+ faq: string;
1240
+ faqEmpty: string;
1241
+ faqEntry: string;
1242
+ faqQuestion: string;
1243
+ faqAnswer: string;
1244
+ faqQuestionLabel: string;
1245
+ faqAnswerLabel: string;
1246
+ faqRemove: string;
1247
+ faqRemoveLabel: string;
1248
+ faqAdd: string;
1249
+ scheduling: string;
1250
+ schedulingHint: string;
1251
+ webhookUrl: string;
1252
+ webhookUrlHint: string;
1253
+ webhookUrlPlaceholder: string;
1254
+ secret: string;
1255
+ secretHint: string;
1256
+ copySecret: string;
1257
+ rotateSecret: string;
1258
+ };
1259
+ save: string;
1260
+ saving: string;
1261
+ saved: string;
1262
+ errors: {
1263
+ nameRequired: string;
1264
+ nameMaxLength: string;
1265
+ personaNameMaxLength: string;
1266
+ greetingNameMaxLength: string;
1267
+ instructionsMaxLength: string;
1268
+ faqMaxItems: string;
1269
+ faqIncompleteEntry: string;
1270
+ faqFieldMaxLength: string;
1271
+ extensionNumeric: string;
1272
+ extensionUnavailable: string;
1273
+ schedulingUrlMaxLength: string;
1274
+ schedulingUrlHttpsRequired: string;
1275
+ hostCreateRequired: string;
1276
+ hostSaveRequired: string;
1277
+ };
1278
+ };
1279
+ voicemails: {
1280
+ title: string;
1281
+ loading: string;
1282
+ empty: string;
1283
+ noUserId: string;
1284
+ transcription: string;
1285
+ summary: string;
1286
+ sentiment: string;
1287
+ sentimentPositive: string;
1288
+ sentimentNeutral: string;
1289
+ sentimentNegative: string;
1290
+ sentimentMixed: string;
1291
+ transcript: string;
1292
+ transcriptLoading: string;
1293
+ transcriptNotAvailable: string;
1294
+ deleteConfirm: string;
1295
+ deleteTitle: string;
1296
+ progress: string;
1297
+ };
1298
+ callLogs: {
1299
+ title: string;
1300
+ loading: string;
1301
+ empty: string;
1302
+ columns: {
1303
+ date: string;
1304
+ direction: string;
1305
+ from: string;
1306
+ to: string;
1307
+ duration: string;
1308
+ status: string;
1309
+ quality: string;
1310
+ };
1311
+ directions: {
1312
+ inbound: string;
1313
+ outbound: string;
1314
+ internal: string;
1315
+ };
1316
+ routedTo: string;
1317
+ calledBy: string;
1318
+ statuses: {
1319
+ completed: string;
1320
+ noAnswer: string;
1321
+ busy: string;
1322
+ failed: string;
1323
+ voicemail: string;
1324
+ };
1325
+ quality: {
1326
+ good: string;
1327
+ fair: string;
1328
+ poor: string;
1329
+ unavailable: string;
1330
+ unavailableTooltip: string;
1331
+ };
1332
+ };
1333
+ callHistory: {
1334
+ title: string;
1335
+ loading: string;
1336
+ empty: string;
1337
+ noPhoneNumber: string;
1338
+ error: string;
1339
+ clickToCall: string;
1340
+ summaryNotAvailable: string;
1341
+ relativeTime: {
1342
+ justNow: string;
1343
+ minuteAgo: string;
1344
+ minutesAgo: string;
1345
+ hourAgo: string;
1346
+ hoursAgo: string;
1347
+ yesterday: string;
1348
+ };
1349
+ };
1350
+ phoneNumberOrdering: {
1351
+ title: string;
1352
+ steps: {
1353
+ search: string;
1354
+ select: string;
1355
+ confirm: string;
1356
+ route: string;
1357
+ done: string;
1358
+ stepOf: string;
1359
+ };
1360
+ search: {
1361
+ title: string;
1362
+ subtitle: string;
1363
+ searchType: string;
1364
+ areaCode: string;
1365
+ zip: string;
1366
+ areaCodeLabel: string;
1367
+ areaCodePlaceholder: string;
1368
+ zipLabel: string;
1369
+ zipPlaceholder: string;
1370
+ numberOfResults: string;
1371
+ search: string;
1372
+ };
1373
+ results: {
1374
+ title: string;
1375
+ phoneNumber: string;
1376
+ state: string;
1377
+ selectAll: string;
1378
+ selected: string;
1379
+ noResults: string;
1380
+ continue: string;
1381
+ backToSearch: string;
1382
+ };
1383
+ confirm: {
1384
+ title: string;
1385
+ subtitleOne: string;
1386
+ subtitleOther: string;
1387
+ description: string;
1388
+ placeOrder: string;
1389
+ continue: string;
1390
+ back: string;
1391
+ billing: {
1392
+ label: string;
1393
+ headline: string;
1394
+ detail: string;
1395
+ detail_one: string;
1396
+ detail_other: string;
1397
+ timing: string;
1398
+ taxesAndFees: string;
1399
+ noPrice: string;
1400
+ noPrice_one: string;
1401
+ noPrice_other: string;
1402
+ notBilled: string;
1403
+ };
1404
+ };
1405
+ route: {
1406
+ title: string;
1407
+ subtitle: string;
1408
+ later: string;
1409
+ laterHint: string;
1410
+ noTargets: string;
1411
+ unnamed: string;
1412
+ extension: string;
1413
+ back: string;
1414
+ type: {
1415
+ user: string;
1416
+ dial_plan: string;
1417
+ voice_app: string;
1418
+ queue: string;
1419
+ ring_group: string;
1420
+ shared_voicemail: string;
1421
+ };
1422
+ };
1423
+ ordering: {
1424
+ title: string;
1425
+ };
1426
+ complete: {
1427
+ titlePendingOne: string;
1428
+ titlePendingOther: string;
1429
+ titleCompleteOne: string;
1430
+ titleCompleteOther: string;
1431
+ titlePartialOne: string;
1432
+ titlePartialOther: string;
1433
+ descriptionPendingOne: string;
1434
+ descriptionPendingOther: string;
1435
+ descriptionCompleteOne: string;
1436
+ descriptionCompleteOther: string;
1437
+ descriptionPartialOne: string;
1438
+ descriptionPartialOther: string;
1439
+ checking: string;
1440
+ assignmentHint: string;
1441
+ orderMore: string;
1442
+ };
1443
+ error: {
1444
+ title: string;
1445
+ description: string;
1446
+ tryAgain: string;
1447
+ };
1448
+ statuses: {
1449
+ pending: string;
1450
+ complete: string;
1451
+ partial: string;
1452
+ failed: string;
1453
+ };
1454
+ };
1455
+ phoneNumbers: {
1456
+ title: string;
1457
+ loading: string;
1458
+ empty: string;
1459
+ filterLabel: string;
1460
+ search: {
1461
+ placeholder: string;
1462
+ noResults: string;
1463
+ };
1464
+ filters: {
1465
+ active: string;
1466
+ in_progress: string;
1467
+ cancelled: string;
1468
+ };
1469
+ columns: {
1470
+ phoneNumber: string;
1471
+ status: string;
1472
+ callerID: string;
1473
+ callerIDPrefix: string;
1474
+ outbound: string;
1475
+ carrier: string;
1476
+ transferDate: string;
1477
+ routingTarget: string;
1478
+ cancelledDate: string;
1479
+ };
1480
+ statuses: {
1481
+ active: string;
1482
+ inactive: string;
1483
+ released: string;
1484
+ ordering: string;
1485
+ orderFailed: string;
1486
+ portingDraft: string;
1487
+ portingApproved: string;
1488
+ portingSubmitted: string;
1489
+ portingException: string;
1490
+ portingFoc: string;
1491
+ };
1492
+ badges: {
1493
+ temporary: string;
1494
+ };
1495
+ outbound: {
1496
+ enabled: string;
1497
+ disabled: string;
1498
+ fax: string;
1499
+ };
1500
+ routingTarget: {
1501
+ notSet: string;
1502
+ setAction: string;
1503
+ faxNotApplicable: string;
1504
+ };
1505
+ routingTargetTypes: {
1506
+ user: string;
1507
+ dialPlan: string;
1508
+ voiceApp: string;
1509
+ ringGroup: string;
1510
+ };
1511
+ };
1512
+ accountOnboarding: {
1513
+ billing: {
1514
+ label: string;
1515
+ headline: string;
1516
+ detail: {
1517
+ userSeat: {
1518
+ one: string;
1519
+ other: string;
1520
+ };
1521
+ phoneNumber: {
1522
+ one: string;
1523
+ other: string;
1524
+ };
1525
+ };
1526
+ noPrice: {
1527
+ userSeat: {
1528
+ one: string;
1529
+ other: string;
1530
+ };
1531
+ phoneNumber: {
1532
+ one: string;
1533
+ other: string;
1534
+ };
1535
+ };
1536
+ rateHeadline: {
1537
+ userSeat: string;
1538
+ phoneNumber: string;
1539
+ };
1540
+ rateDetail: {
1541
+ userSeat: string;
1542
+ phoneNumber: string;
1543
+ };
1544
+ noPriceRate: {
1545
+ userSeat: string;
1546
+ phoneNumber: string;
1547
+ };
1548
+ startsOnActivation: string;
1549
+ taxesAndFees: string;
1550
+ notBilled: string;
1551
+ };
1552
+ title: string;
1553
+ breadcrumbAriaLabel: string;
1554
+ loading: string;
1555
+ error: {
1556
+ title: string;
1557
+ description: string;
1558
+ retry: string;
1559
+ };
1560
+ steps: {
1561
+ account: string;
1562
+ numbers: string;
1563
+ hardware: string;
1564
+ complete: string;
1565
+ };
1566
+ sidebar: {
1567
+ businessDetails: string;
1568
+ businessDetailsDesc: string;
1569
+ teamMembers: string;
1570
+ teamMembersDesc: string;
1571
+ numberOptions: string;
1572
+ numberOptionsDesc: string;
1573
+ callerId: string;
1574
+ callerIdDesc: string;
1575
+ directoryListing: string;
1576
+ directoryListingDesc: string;
1577
+ numberSetup: string;
1578
+ numberSetupDesc: string;
1579
+ verification: string;
1580
+ verificationDesc: string;
1581
+ deviceAssignment: string;
1582
+ deviceAssignmentDesc: string;
1583
+ };
1584
+ account: {
1585
+ title: string;
1586
+ subtitle: string;
1587
+ details: {
1588
+ heading: string;
1589
+ companyNameLabel: string;
1590
+ companyNamePlaceholder: string;
1591
+ companyNameRequired: string;
1592
+ emailLabel: string;
1593
+ emailPlaceholder: string;
1594
+ emailRequired: string;
1595
+ emailInvalid: string;
1596
+ phoneLabel: string;
1597
+ phonePlaceholder: string;
1598
+ phoneRequired: string;
1599
+ phoneInvalid: string;
1600
+ primaryContactLabel: string;
1601
+ primaryContactPlaceholder: string;
1602
+ primaryContactRequired: string;
1603
+ regionLabel: string;
1604
+ regionPlaceholder: string;
1605
+ timezoneLabel: string;
1606
+ timezonePlaceholder: string;
1607
+ timezoneRequired: string;
1608
+ };
1609
+ users: {
1610
+ heading: string;
1611
+ description: string;
1612
+ nameLabel: string;
1613
+ namePlaceholder: string;
1614
+ nameRequired: string;
1615
+ emailLabel: string;
1616
+ emailPlaceholder: string;
1617
+ extensionLabel: string;
1618
+ extensionPlaceholder: string;
1619
+ addUser: string;
1620
+ removeUser: string;
1621
+ noUsers: string;
1622
+ atLeastOne: string;
1623
+ duplicateEmail: string;
1624
+ };
1625
+ location: {
1626
+ heading: string;
1627
+ description: string;
1628
+ nameLabel: string;
1629
+ namePlaceholder: string;
1630
+ nameRequired: string;
1631
+ addressLabel: string;
1632
+ searchPlaceholder: string;
1633
+ searching: string;
1634
+ noResults: string;
1635
+ enterManually: string;
1636
+ searchInstead: string;
1637
+ edit: string;
1638
+ addressRequired: string;
1639
+ houseNumberLabel: string;
1640
+ houseNumberPlaceholder: string;
1641
+ streetLabel: string;
1642
+ streetPlaceholder: string;
1643
+ cityLabel: string;
1644
+ cityPlaceholder: string;
1645
+ stateLabel: string;
1646
+ statePlaceholder: string;
1647
+ postalCodeLabel: string;
1648
+ postalCodePlaceholder: string;
1649
+ };
1650
+ saving: string;
1651
+ saveError: string;
1652
+ };
1653
+ numbers: {
1654
+ title: string;
1655
+ subtitle: string;
1656
+ defaultDialPlanName: string;
1657
+ callerId: {
1658
+ title: string;
1659
+ subtitle: string;
1660
+ inputLabel: string;
1661
+ inputPlaceholder: string;
1662
+ inputHelp: string;
1663
+ charCount: string;
1664
+ submit: string;
1665
+ submitting: string;
1666
+ submittingAll: string;
1667
+ submitted: string;
1668
+ alreadySet: string;
1669
+ submitAll: string;
1670
+ skipCallerId: string;
1671
+ partialError: string;
1672
+ validation: {
1673
+ required: string;
1674
+ tooLong: string;
1675
+ invalidChars: string;
1676
+ };
1677
+ error: {
1678
+ submitFailed: string;
1679
+ conflict: string;
1680
+ };
1681
+ directoryListing: {
1682
+ title: string;
1683
+ info: string;
1684
+ };
1685
+ noDIDs: string;
1686
+ };
1687
+ directoryListing: {
1688
+ title: string;
1689
+ subtitle: string;
1690
+ selectPrompt: string;
1691
+ noneOption: string;
1692
+ noDIDs: string;
1693
+ businessName: string;
1694
+ businessNamePlaceholder: string;
1695
+ businessNameHelp: string;
1696
+ validation: {
1697
+ nameRequired: string;
1698
+ };
1699
+ };
1700
+ overview: {
1701
+ heading: string;
1702
+ empty: string;
1703
+ requestNew: string;
1704
+ requestNewDesc: string;
1705
+ portExisting: string;
1706
+ portExistingDesc: string;
1707
+ phoneNumber: string;
1708
+ type: string;
1709
+ status: string;
1710
+ source: string;
1711
+ temporaryBanner: string;
1712
+ loadError: string;
1713
+ retry: string;
1714
+ };
1715
+ status: {
1716
+ active: string;
1717
+ inactive: string;
1718
+ released: string;
1719
+ ordering: string;
1720
+ order_failed: string;
1721
+ porting_draft: string;
1722
+ porting_submitted: string;
1723
+ porting_exception: string;
1724
+ porting_foc: string;
1725
+ porting_approved: string;
1726
+ };
1727
+ source: {
1728
+ did: string;
1729
+ didTemporary: string;
1730
+ number_order: string;
1731
+ port_order: string;
1732
+ };
1733
+ order: {
1734
+ searchTitle: string;
1735
+ searchSubtitle: string;
1736
+ searchByAreaCode: string;
1737
+ searchByZip: string;
1738
+ areaCodeLabel: string;
1739
+ areaCodePlaceholder: string;
1740
+ zipLabel: string;
1741
+ zipPlaceholder: string;
1742
+ quantityLabel: string;
1743
+ search: string;
1744
+ searching: string;
1745
+ resultsTitle: string;
1746
+ resultsSubtitle: string;
1747
+ noResults: string;
1748
+ selectAll: string;
1749
+ deselectAll: string;
1750
+ selected: string;
1751
+ state: string;
1752
+ confirmTitle: string;
1753
+ confirmSubtitle: string;
1754
+ confirmSubtitle_one: string;
1755
+ confirmSubtitle_other: string;
1756
+ carrierNote: string;
1757
+ placeOrder: string;
1758
+ placing: string;
1759
+ ordering: string;
1760
+ statusTitle: string;
1761
+ statusPending: string;
1762
+ statusStalled: string;
1763
+ statusComplete: string;
1764
+ statusFailed: string;
1765
+ statusPartial: string;
1766
+ done: string;
1767
+ orderMore: string;
1768
+ continue: string;
1769
+ error: string;
1770
+ };
1771
+ port: {
1772
+ numbersTitle: string;
1773
+ numbersSubtitle: string;
1774
+ phoneLabel: string;
1775
+ phonePlaceholder: string;
1776
+ addAnother: string;
1777
+ numberLabel: string;
1778
+ removeNumber: string;
1779
+ duplicateOfRow: string;
1780
+ pasteHint: string;
1781
+ readyCountZero: string;
1782
+ readyCountOne: string;
1783
+ readyCountOther: string;
1784
+ needsAttentionOne: string;
1785
+ needsAttentionOther: string;
1786
+ reasonInvalid: string;
1787
+ reasonNotUs: string;
1788
+ reasonTollFree: string;
1789
+ reasonAmbiguous: string;
1790
+ reasonHasExtension: string;
1791
+ maxPerOrder: string;
1792
+ notPortableWithDetail: string;
1793
+ accountLimit: string;
1794
+ numbersUnavailable: string;
1795
+ conflictAlreadyOnAccount: string;
1796
+ conflictInServiceElsewhere: string;
1797
+ checkEligibility: string;
1798
+ checking: string;
1799
+ eligibilityTitle: string;
1800
+ eligibilitySubtitle: string;
1801
+ portable: string;
1802
+ notPortable: string;
1803
+ carrier: string;
1804
+ wireless: string;
1805
+ wirelessYes: string;
1806
+ wirelessNo: string;
1807
+ accountRequired: string;
1808
+ continueWithPortable: string;
1809
+ noPortable: string;
1810
+ subscriberTitle: string;
1811
+ subscriberSubtitle: string;
1812
+ btnLabel: string;
1813
+ btnPlaceholder: string;
1814
+ businessNameLabel: string;
1815
+ businessNamePlaceholder: string;
1816
+ approverNameLabel: string;
1817
+ approverNamePlaceholder: string;
1818
+ accountNumberLabel: string;
1819
+ accountNumberPlaceholder: string;
1820
+ pinLabel: string;
1821
+ pinPlaceholder: string;
1822
+ addressHeading: string;
1823
+ houseNumberLabel: string;
1824
+ houseNumberPlaceholder: string;
1825
+ streetNameLabel: string;
1826
+ streetNamePlaceholder: string;
1827
+ line2Label: string;
1828
+ line2Placeholder: string;
1829
+ cityLabel: string;
1830
+ cityPlaceholder: string;
1831
+ stateLabel: string;
1832
+ statePlaceholder: string;
1833
+ zipLabel: string;
1834
+ zipPlaceholder: string;
1835
+ focTitle: string;
1836
+ focSubtitle: string;
1837
+ focDateLabel: string;
1838
+ focTimeLabel: string;
1839
+ focTimePlaceholder: string;
1840
+ documentsTitle: string;
1841
+ documentsSubtitle: string;
1842
+ billCopyLabel: string;
1843
+ billCopyDesc: string;
1844
+ billCopyRequired: string;
1845
+ csrLabel: string;
1846
+ csrDesc: string;
1847
+ csrOptional: string;
1848
+ uploadFile: string;
1849
+ fileSelected: string;
1850
+ noFileSelected: string;
1851
+ uploading: string;
1852
+ reviewTitle: string;
1853
+ reviewSubtitle: string;
1854
+ numbersSection: string;
1855
+ subscriberSection: string;
1856
+ focSection: string;
1857
+ documentsSection: string;
1858
+ signatureLabel: string;
1859
+ signaturePlaceholder: string;
1860
+ signatureHelp: string;
1861
+ approve: string;
1862
+ submitting: string;
1863
+ submittedTitle: string;
1864
+ submittedSubtitle: string;
1865
+ submittedStatus: string;
1866
+ backToOverview: string;
1867
+ carrierGroupsTitle: string;
1868
+ carrierGroupsSubtitle: string;
1869
+ carrierStart: string;
1870
+ carrierContinue: string;
1871
+ carrierSubmitted: string;
1872
+ numberSingular: string;
1873
+ numberPlural: string;
1874
+ };
1875
+ validation: {
1876
+ areaCodeRequired: string;
1877
+ areaCodeFormat: string;
1878
+ cityRequired: string;
1879
+ stateRequired: string;
1880
+ zipRequired: string;
1881
+ zipFormat: string;
1882
+ selectAtLeastOne: string;
1883
+ phoneRequired: string;
1884
+ phoneInvalid: string;
1885
+ phoneDuplicate: string;
1886
+ btnRequired: string;
1887
+ btnInvalid: string;
1888
+ businessNameRequired: string;
1889
+ approverNameRequired: string;
1890
+ houseNumberRequired: string;
1891
+ streetNameRequired: string;
1892
+ focDateRequired: string;
1893
+ focDateTooSoon: string;
1894
+ focDateTooFar: string;
1895
+ focTimeRequired: string;
1896
+ billCopyRequired: string;
1897
+ signatureRequired: string;
1898
+ eligibilityError: string;
1899
+ submitError: string;
1900
+ };
1901
+ nav: {
1902
+ back: string;
1903
+ next: string;
1904
+ cancel: string;
1905
+ backToSearch: string;
1906
+ confirm: string;
1907
+ };
1908
+ gate: {
1909
+ noDIDsAvailable: string;
1910
+ primaryRequired: string;
1911
+ };
1912
+ };
1913
+ hardware: {
1914
+ title: string;
1915
+ shippingAddress: string;
1916
+ subtitle: string;
1917
+ noUsers: string;
1918
+ noDevices: string;
1919
+ isDectBase: string;
1920
+ userLabel: string;
1921
+ selectUser: string;
1922
+ selectUserRequired: string;
1923
+ macLabel: string;
1924
+ macPlaceholder: string;
1925
+ invalidMac: string;
1926
+ ipeiLabel: string;
1927
+ ipeiPlaceholder: string;
1928
+ invalidIpei: string;
1929
+ ipeiNotMac: string;
1930
+ removeDevice: string;
1931
+ unsupportedVendor: string;
1932
+ duplicateMac: string;
1933
+ deviceNotFound: string;
1934
+ handset: string;
1935
+ handsets: string;
1936
+ addDeviceButton: string;
1937
+ addHandsetButton: string;
1938
+ save: string;
1939
+ saving: string;
1940
+ cancel: string;
1941
+ dectBase: string;
1942
+ removeBaseFailed: string;
1943
+ tableHeaderName: string;
1944
+ tableHeaderExtension: string;
1945
+ tableHeaderDevice: string;
1946
+ availableDevices: string;
1947
+ allAssigned: string;
1948
+ dragDropHint: string;
1949
+ clickToAssign: string;
1950
+ submitOrder: string;
1951
+ assignAndComplete: string;
1952
+ unassign: string;
1953
+ deskPhone: string;
1954
+ cordless: string;
1955
+ submitting: string;
1956
+ gate: {
1957
+ noDevices: string;
1958
+ };
1959
+ };
1960
+ complete: {
1961
+ title: string;
1962
+ subtitle: string;
1963
+ e911: {
1964
+ loading: string;
1965
+ pollingStatus: string;
1966
+ verified: string;
1967
+ processing: string;
1968
+ pendingAfterPolling: string;
1969
+ addressStandardized: string;
1970
+ primaryAssigned: string;
1971
+ errorTitle: string;
1972
+ errorDescription: string;
1973
+ retryButton: string;
1974
+ deferred: string;
1975
+ };
1976
+ };
1977
+ stepComplete: {
1978
+ title: string;
1979
+ subtitle: string;
1980
+ done: string;
1981
+ };
1982
+ nav: {
1983
+ next: string;
1984
+ skip: string;
1985
+ back: string;
1986
+ exit: string;
1987
+ };
1988
+ legal: {
1989
+ prefix: string;
1990
+ and: string;
1991
+ termsOfService: string;
1992
+ recipientTerms: string;
1993
+ privacyPolicy: string;
1994
+ };
1995
+ ssa: {
1996
+ title: string;
1997
+ intro: string;
1998
+ pricingTitle: string;
1999
+ perUser: string;
2000
+ perNumber: string;
2001
+ perAiLocation: string;
2002
+ perMonthSuffix: string;
2003
+ agreementLabel: string;
2004
+ openInNewTab: string;
2005
+ accept: string;
2006
+ submitting: string;
2007
+ notPermitted: string;
2008
+ errors: {
2009
+ stale: string;
2010
+ pricingMissing: string;
2011
+ generic: string;
2012
+ retry: string;
2013
+ };
2014
+ loadError: {
2015
+ title: string;
2016
+ description: string;
2017
+ retry: string;
2018
+ retrying: string;
2019
+ };
2020
+ };
2021
+ };
2022
+ onboardingPortal: {
2023
+ onboardingFlows: string;
2024
+ saveAndExit: string;
2025
+ back: string;
2026
+ helpSupport: string;
2027
+ onboardingComplete: string;
2028
+ rates: {
2029
+ title: string;
2030
+ subtitle: string;
2031
+ perUser: string;
2032
+ perPhoneNumber: string;
2033
+ perVoiceAiLocation: string;
2034
+ perMonth: string;
2035
+ rateNotSet: string;
2036
+ pendingRate: string;
2037
+ pendingNote: string;
2038
+ taxesAndFees: string;
2039
+ notBilled: string;
2040
+ noRates: string;
2041
+ unavailable: string;
2042
+ };
2043
+ overview: {
2044
+ label: string;
2045
+ title: string;
2046
+ subtitle: string;
2047
+ progressTitle: string;
2048
+ flowsComplete: string;
2049
+ stepsTitle: string;
2050
+ complete: string;
2051
+ progress: string;
2052
+ completeSetup: string;
2053
+ continueSetup: string;
2054
+ review: string;
2055
+ needHelp: string;
2056
+ needHelpSubtitle: string;
2057
+ scheduleCall: string;
2058
+ viewDocs: string;
2059
+ accountDesc: string;
2060
+ numbersDesc: string;
2061
+ hardwareDesc: string;
2062
+ phoneStatusTitle: string;
2063
+ phoneStatusSubtitle: string;
2064
+ phoneStatusNumber: string;
2065
+ phoneStatusType: string;
2066
+ phoneStatusStatus: string;
2067
+ phoneStatusTypeNew: string;
2068
+ phoneStatusTypePort: string;
2069
+ phoneStatusProcessing: string;
2070
+ phoneStatusComplete: string;
2071
+ phoneStatusTemporary: string;
2072
+ };
2073
+ splash: {
2074
+ title: string;
2075
+ subtitle: string;
2076
+ start: string;
2077
+ step1: string;
2078
+ step2: string;
2079
+ step3: string;
2080
+ };
2081
+ };
2082
+ dialPlan: {
2083
+ title: string;
2084
+ loading: string;
2085
+ error: string;
2086
+ empty: string;
2087
+ nodeTypes: {
2088
+ start: string;
2089
+ schedule: string;
2090
+ internalDial: string;
2091
+ voicemail: string;
2092
+ externalDial: string;
2093
+ voiceApp: string;
2094
+ menu: string;
2095
+ audioClip: string;
2096
+ hangUp: string;
2097
+ };
2098
+ nodeDescriptions: {
2099
+ menu: string;
2100
+ audioClip: string;
2101
+ hangUp: string;
2102
+ };
2103
+ exits: {
2104
+ open: string;
2105
+ closed: string;
2106
+ holiday: string;
2107
+ next: string;
2108
+ timeout: string;
2109
+ invalid: string;
2110
+ };
2111
+ configLabels: {
2112
+ promptClip: string;
2113
+ audioClip: string;
2114
+ digit: string;
2115
+ options: string;
2116
+ addOption: string;
2117
+ removeOption: string;
2118
+ optionLabel: string;
2119
+ optionLabelPlaceholder: string;
2120
+ routeHolidaySeparately: string;
2121
+ routeHolidaySeparatelyHint: string;
2122
+ };
2123
+ resourceGroups: {
2124
+ audioClips: string;
2125
+ };
2126
+ };
2127
+ };
2128
+
2129
+ /**
2130
+ * Locale types and exports for DialStack SDK
2131
+ */
2132
+
2133
+ /**
2134
+ * Locale type derived from the English locale structure
2135
+ */
2136
+ type Locale = typeof en;
2137
+
2138
+ /**
2139
+ * Shared core of the softphone provider — the wiring both the web and native
2140
+ * providers reuse so it can't drift between them. It provides the context and
2141
+ * renders `children`; each platform supplies its own side-effects (web:
2142
+ * `<style>`/`<audio>`/ringtone; native: InCallManager) as ordinary child
2143
+ * components that read the context via `useSoftphoneBase`.
2144
+ *
2145
+ * Must stay DOM- and React-Native-free: it's part of the shared headless core
2146
+ * (src/react/softphone/core) that @dialstack/sdk-native inlines at build time.
2147
+ */
2148
+
2149
+ /** Context fields both platforms expose. Platforms add their own via `extra`. */
2150
+ interface SoftphoneContextBase {
2151
+ connection: SoftphoneConnectionState;
2152
+ /**
2153
+ * The live WebRTC phone, or null before the first one is constructed. For the
2154
+ * phone-scoped settings that aren't per-call — audio device selection, which
2155
+ * must work whether or not a call is up. Per-call actions belong on `actions`.
2156
+ */
2157
+ webrtcPhone: DialStackPhone | null;
2158
+ calls: Call[];
2159
+ activeCall: Call | null;
2160
+ incomingCalls: Call[];
2161
+ /** True while an unanswered inbound call should be audibly ringing. */
2162
+ incomingRinging: boolean;
2163
+ heldCalls: Call[];
2164
+ answerCall: (call: Call) => void;
2165
+ switchToCall: (call: Call) => void;
2166
+ actions: UseCallActions;
2167
+ /**
2168
+ * Internal built-in-UI machinery: the mutually-exclusive keypad/transfer
2169
+ * overlay flags the bundled `OngoingCall` renders. Not part of the public
2170
+ * softphone API — a custom layout owns its own presentation state and ignores
2171
+ * this. Kept on the shared context (rather than local to `OngoingCall`) only so
2172
+ * web and React Native can't drift on when the overlays reset.
2173
+ */
2174
+ overlays: UseCallOverlays;
2175
+ duration: string;
2176
+ consultCall: Call | null;
2177
+ transferOriginal: Call | null;
2178
+ startAttendedTransfer: (destination: string) => Promise<void>;
2179
+ completeAttendedTransfer: () => void;
2180
+ cancelAttendedTransfer: () => void;
2181
+ placeCall: (destination: string) => Promise<void>;
2182
+ emergency: UseEmergencyBinding;
2183
+ emergencyManagedByHost: boolean;
2184
+ lastError: {
2185
+ code: string;
2186
+ message: string;
2187
+ } | null;
2188
+ clearError: () => void;
2189
+ t: (key: keyof Locale['softphone']) => string;
2190
+ displayNumber: (value: string) => string;
2191
+ /** Resolved theme palette — computed on both platforms so mobile themes like web. */
2192
+ palette: SoftphonePalette;
2193
+ }
2194
+
2195
+ /**
2196
+ * SoftphoneProvider (React Native), same API as the web provider. Shared wiring
2197
+ * lives in SoftphoneProviderBase (from `@dialstack/sdk-react/core`); this
2198
+ * file adds only the native bits: the required `storage` adapter,
2199
+ * `locationProvider`, `appearance`/`defaultCountry`, and the InCallManager audio
2200
+ * session + ringtone.
2201
+ */
2202
+
2203
+ type AppearanceOptions = Parameters<typeof resolveSoftphonePalette>[0];
2204
+ type ConnectionState = SoftphoneConnectionState;
2205
+ interface SoftphoneProviderProps {
2206
+ /** WebRTC user session token. */
2207
+ token: string;
2208
+ /**
2209
+ * Persistence for the selected E911 address id, so it survives app restarts.
2210
+ * REQUIRED on React Native: the SDK takes no persistence dependency of its own
2211
+ * (react-native-mmkv and AsyncStorage vary by version/architecture and can't be
2212
+ * defaulted safely), so the host supplies a small `PlatformStorage` adapter —
2213
+ * typically MMKV- or AsyncStorage-backed. See the example apps for reference
2214
+ * adapters; a synchronous in-memory adapter is also valid if you don't need
2215
+ * persistence. It only stores the E911 id, nothing sensitive.
2216
+ */
2217
+ storage: PlatformStorage;
2218
+ /** API base URL (defaults to the SDK's production endpoint). */
2219
+ apiBaseUrl?: string;
2220
+ /**
2221
+ * Called shortly before the session token expires (about 60 seconds ahead).
2222
+ * Return a fresh token minted by your backend; the SDK delivers it in-band over
2223
+ * the existing connection — no reconnect and no call disruption. If it rejects,
2224
+ * the SDK keeps the (still-valid) connection open and surfaces an `error`.
2225
+ */
2226
+ onTokenExpiring?: () => Promise<string>;
2227
+ /** Emergency (E911) address id to present on connect; when supplied the host
2228
+ * manages E911 and the built-in prompt is disabled. */
2229
+ emergencyAddressId?: string;
2230
+ /**
2231
+ * Optional device-location source for the E911 form. When provided, the
2232
+ * built-in emergency-address form shows a "Use my current location" action
2233
+ * that calls this to prefill the address fields; the host owns the location
2234
+ * permission prompt, geolocation, and reverse-geocoding. Omit for manual entry.
2235
+ */
2236
+ locationProvider?: () => Promise<EmergencyAddressInput>;
2237
+ /** Connect automatically once mounted (default: true). */
2238
+ autoConnect?: boolean;
2239
+ /** Theming — the shared appearance surface (same as the web softphone). */
2240
+ appearance?: AppearanceOptions;
2241
+ /** Locale for UI strings (defaults to English), same surface as the web softphone. */
2242
+ locale?: Locale;
2243
+ /** Default country for number formatting. */
2244
+ defaultCountry?: CountryCode;
2245
+ onConnectionStateChange?: (event: {
2246
+ state: ConnectionState;
2247
+ }) => void;
2248
+ onIncomingCall?: (event: {
2249
+ from: string;
2250
+ fromName: string | null;
2251
+ }) => void;
2252
+ onCallStarted?: (event: {
2253
+ direction: 'inbound' | 'outbound';
2254
+ peer: string;
2255
+ }) => void;
2256
+ onCallEnded?: (event: {
2257
+ reason: CallEndReason;
2258
+ }) => void;
2259
+ onError?: (event: {
2260
+ code: string;
2261
+ message: string;
2262
+ }) => void;
2263
+ children: React.ReactNode;
2264
+ }
2265
+ /**
2266
+ * The native softphone context: the shared base plus the native-only
2267
+ * `locationProvider`. (`palette` is on the base — computed identically to web.)
2268
+ */
2269
+ interface SoftphoneContextValue extends SoftphoneContextBase {
2270
+ /** Host-supplied device-location source for the E911 form, or undefined. */
2271
+ locationProvider: (() => Promise<EmergencyAddressInput>) | undefined;
2272
+ }
2273
+ declare function SoftphoneProvider({ token, storage, apiBaseUrl, onTokenExpiring, emergencyAddressId, locationProvider, autoConnect, appearance, locale, defaultCountry, onConnectionStateChange, onIncomingCall, onCallStarted, onCallEnded, onError, children, }: SoftphoneProviderProps): React.JSX.Element;
2274
+ /** Access the full softphone context. Throws when used outside the provider. */
2275
+ declare function useSoftphone(): SoftphoneContextValue;
2276
+ /** The active foreground call + its actions (build-your-own convenience). */
2277
+ declare function useActiveCall(): {
2278
+ activeCall: Call | null;
2279
+ actions: UseCallActions;
2280
+ };
2281
+ /** The currently-ringing inbound call, or null. */
2282
+ declare function useIncomingCall(): Call | null;
2283
+
2284
+ /**
2285
+ * Softphone (RN) — the batteries-included softphone: dial pad, incoming-call
2286
+ * answer/decline, and in-call controls, switching between them based on call
2287
+ * state. The RN sibling of the web <Softphone>: a pure consumer of
2288
+ * <SoftphoneProvider>, which owns the connection and the `token` (the single
2289
+ * credential/connection entry point). Render it under a provider:
2290
+ *
2291
+ * ```tsx
2292
+ * <SoftphoneProvider token={webrtcToken} apiBaseUrl={apiBaseUrl}>
2293
+ * <Softphone />
2294
+ * </SoftphoneProvider>
2295
+ * ```
2296
+ *
2297
+ * For a bespoke UI, compose <DialPad> / <IncomingCall> / <OngoingCall> yourself
2298
+ * under the same provider.
2299
+ */
2300
+
2301
+ interface SoftphoneProps {
2302
+ autoFocusDestination?: boolean;
2303
+ }
2304
+ declare function Softphone({ autoFocusDestination }: SoftphoneProps): React.JSX.Element;
2305
+
2306
+ /**
2307
+ * DialPad (RN) — the idle/outbound screen: connection status chip, a destination
2308
+ * field, the keypad, and the call button. Reads connection + placeCall from the
2309
+ * softphone context; owns its own destination.
2310
+ *
2311
+ * The E911 prompt is NOT rendered here — <EmergencyBanner> is a separate
2312
+ * component so a modular consumer can place it anywhere (or omit it when the host
2313
+ * manages E911). The batteries-included <Softphone> renders the banner above this
2314
+ * pad itself, so its look is unchanged.
2315
+ *
2316
+ * Must be rendered inside a <SoftphoneProvider>.
2317
+ */
2318
+
2319
+ interface DialPadProps {
2320
+ /** Autofocus the destination field on mount. */
2321
+ autoFocusDestination?: boolean;
2322
+ }
2323
+ declare function DialPad({ autoFocusDestination }: DialPadProps): React.JSX.Element;
2324
+
2325
+ /**
2326
+ * IncomingCall / IncomingStack (RN) — the ringing inbound UI.
2327
+ *
2328
+ * `IncomingCall` is the single full-screen incoming screen (idle, one caller);
2329
+ * it renders the first ringing call as a full-size card, nothing when none is
2330
+ * ringing. `IncomingStack` renders ALL ringing calls, compact when >1 — what the
2331
+ * RN <Softphone> layers over the base for call-waiting / multiple inbound.
2332
+ *
2333
+ * Both read from context and must be rendered inside a <SoftphoneProvider>.
2334
+ */
2335
+
2336
+ declare function IncomingCall(): React.JSX.Element | null;
2337
+
2338
+ /**
2339
+ * OngoingCall (RN) — the in-call screen: peer + state + duration, optional DTMF
2340
+ * keypad / transfer / add-call overlays, the control row (mute/hold/keypad/
2341
+ * transfer/add call), and hang up. Renders nothing when there is no active call.
2342
+ * Owns its transient transfer + add-call text; reads the call/actions/duration
2343
+ * from context.
2344
+ *
2345
+ * Must be rendered inside a <SoftphoneProvider>.
2346
+ */
2347
+
2348
+ declare function OngoingCall(): React.JSX.Element | null;
2349
+
2350
+ /**
2351
+ * EmergencyBanner (RN) — the built-in "set your emergency location" (E911)
2352
+ * prompt shown above the dial pad while the session's emergency address is
2353
+ * unbound. A compact banner opens a mobile Modal form (the web's cramped inline
2354
+ * two-column layout is unusable on a phone under the keyboard).
2355
+ *
2356
+ * Hidden when the host manages E911, while binding is loading, or once bound.
2357
+ * Reads the E911 flow from the softphone context; owns only its modal/form state.
2358
+ *
2359
+ * If the host passed a `locationProvider`, the form offers "Use my current
2360
+ * location" to prefill the address (the host owns the permission + geolocation +
2361
+ * reverse-geocode; the SDK takes no geolocation dependency).
2362
+ */
2363
+
2364
+ declare function EmergencyBanner(): React.JSX.Element | null;
2365
+
2366
+ export { Call, DialPad, EmergencyBanner, IncomingCall, OngoingCall, Softphone, SoftphoneProvider, useActiveCall, useIncomingCall, useSoftphone };
2367
+ export type { CallEndReason, CallState, ConnectionState, DialPadProps, EmergencyAddressInput, PlatformStorage, SoftphoneContextValue, SoftphoneProps, SoftphoneProviderProps, UseCallActions };