@polymorfa/browser 0.1.0-dev.20260922094209

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.
Files changed (139) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +248 -0
  3. package/dist/calls/api.d.ts +22 -0
  4. package/dist/calls/api.d.ts.map +1 -0
  5. package/dist/calls/api.js +104 -0
  6. package/dist/calls/api.js.map +1 -0
  7. package/dist/calls/backend.d.ts +85 -0
  8. package/dist/calls/backend.d.ts.map +1 -0
  9. package/dist/calls/backend.js +141 -0
  10. package/dist/calls/backend.js.map +1 -0
  11. package/dist/calls/client.d.ts +56 -0
  12. package/dist/calls/client.d.ts.map +1 -0
  13. package/dist/calls/client.js +260 -0
  14. package/dist/calls/client.js.map +1 -0
  15. package/dist/calls/controller.d.ts +329 -0
  16. package/dist/calls/controller.d.ts.map +1 -0
  17. package/dist/calls/controller.js +1328 -0
  18. package/dist/calls/controller.js.map +1 -0
  19. package/dist/calls/diagnostics.d.ts +52 -0
  20. package/dist/calls/diagnostics.d.ts.map +1 -0
  21. package/dist/calls/diagnostics.js +188 -0
  22. package/dist/calls/diagnostics.js.map +1 -0
  23. package/dist/calls/index.d.ts +3 -0
  24. package/dist/calls/index.d.ts.map +1 -0
  25. package/dist/calls/index.js +5 -0
  26. package/dist/calls/index.js.map +1 -0
  27. package/dist/calls/internal.d.ts +8 -0
  28. package/dist/calls/internal.d.ts.map +1 -0
  29. package/dist/calls/internal.js +8 -0
  30. package/dist/calls/internal.js.map +1 -0
  31. package/dist/calls/media.d.ts +144 -0
  32. package/dist/calls/media.d.ts.map +1 -0
  33. package/dist/calls/media.js +512 -0
  34. package/dist/calls/media.js.map +1 -0
  35. package/dist/calls/signaling.d.ts +74 -0
  36. package/dist/calls/signaling.d.ts.map +1 -0
  37. package/dist/calls/signaling.js +169 -0
  38. package/dist/calls/signaling.js.map +1 -0
  39. package/dist/calls/socket.d.ts +95 -0
  40. package/dist/calls/socket.d.ts.map +1 -0
  41. package/dist/calls/socket.js +267 -0
  42. package/dist/calls/socket.js.map +1 -0
  43. package/dist/chat/composer.d.ts +59 -0
  44. package/dist/chat/composer.d.ts.map +1 -0
  45. package/dist/chat/composer.js +217 -0
  46. package/dist/chat/composer.js.map +1 -0
  47. package/dist/chat/conversation.d.ts +71 -0
  48. package/dist/chat/conversation.d.ts.map +1 -0
  49. package/dist/chat/conversation.js +204 -0
  50. package/dist/chat/conversation.js.map +1 -0
  51. package/dist/chat/index.d.ts +4 -0
  52. package/dist/chat/index.d.ts.map +1 -0
  53. package/dist/chat/index.js +4 -0
  54. package/dist/chat/index.js.map +1 -0
  55. package/dist/chat/voice-recorder.d.ts +75 -0
  56. package/dist/chat/voice-recorder.d.ts.map +1 -0
  57. package/dist/chat/voice-recorder.js +346 -0
  58. package/dist/chat/voice-recorder.js.map +1 -0
  59. package/dist/controller.d.ts +16 -0
  60. package/dist/controller.d.ts.map +1 -0
  61. package/dist/controller.js +50 -0
  62. package/dist/controller.js.map +1 -0
  63. package/dist/diagnostics.d.ts +27 -0
  64. package/dist/diagnostics.d.ts.map +1 -0
  65. package/dist/diagnostics.js +2 -0
  66. package/dist/diagnostics.js.map +1 -0
  67. package/dist/dropin/client.d.ts +82 -0
  68. package/dist/dropin/client.d.ts.map +1 -0
  69. package/dist/dropin/client.js +314 -0
  70. package/dist/dropin/client.js.map +1 -0
  71. package/dist/dropin/connect.d.ts +23 -0
  72. package/dist/dropin/connect.d.ts.map +1 -0
  73. package/dist/dropin/connect.js +54 -0
  74. package/dist/dropin/connect.js.map +1 -0
  75. package/dist/dropin/inbox.d.ts +131 -0
  76. package/dist/dropin/inbox.d.ts.map +1 -0
  77. package/dist/dropin/inbox.js +406 -0
  78. package/dist/dropin/inbox.js.map +1 -0
  79. package/dist/dropin/index.d.ts +5 -0
  80. package/dist/dropin/index.d.ts.map +1 -0
  81. package/dist/dropin/index.js +5 -0
  82. package/dist/dropin/index.js.map +1 -0
  83. package/dist/dropin/permissions.d.ts +29 -0
  84. package/dist/dropin/permissions.d.ts.map +1 -0
  85. package/dist/dropin/permissions.js +53 -0
  86. package/dist/dropin/permissions.js.map +1 -0
  87. package/dist/errors.d.ts +39 -0
  88. package/dist/errors.d.ts.map +1 -0
  89. package/dist/errors.js +41 -0
  90. package/dist/errors.js.map +1 -0
  91. package/dist/index.d.ts +11 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +10 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/internal.d.ts +12 -0
  96. package/dist/internal.d.ts.map +1 -0
  97. package/dist/internal.js +13 -0
  98. package/dist/internal.js.map +1 -0
  99. package/dist/messaging/client.d.ts +54 -0
  100. package/dist/messaging/client.d.ts.map +1 -0
  101. package/dist/messaging/client.js +153 -0
  102. package/dist/messaging/client.js.map +1 -0
  103. package/dist/messaging/composer.d.ts +12 -0
  104. package/dist/messaging/composer.d.ts.map +1 -0
  105. package/dist/messaging/composer.js +36 -0
  106. package/dist/messaging/composer.js.map +1 -0
  107. package/dist/messaging/index.d.ts +4 -0
  108. package/dist/messaging/index.d.ts.map +1 -0
  109. package/dist/messaging/index.js +3 -0
  110. package/dist/messaging/index.js.map +1 -0
  111. package/dist/messaging/types.d.ts +209 -0
  112. package/dist/messaging/types.d.ts.map +1 -0
  113. package/dist/messaging/types.js +2 -0
  114. package/dist/messaging/types.js.map +1 -0
  115. package/dist/templates/controller.d.ts +45 -0
  116. package/dist/templates/controller.d.ts.map +1 -0
  117. package/dist/templates/controller.js +298 -0
  118. package/dist/templates/controller.js.map +1 -0
  119. package/dist/templates/index.d.ts +4 -0
  120. package/dist/templates/index.d.ts.map +1 -0
  121. package/dist/templates/index.js +3 -0
  122. package/dist/templates/index.js.map +1 -0
  123. package/dist/templates/transport.d.ts +7 -0
  124. package/dist/templates/transport.d.ts.map +1 -0
  125. package/dist/templates/transport.js +124 -0
  126. package/dist/templates/transport.js.map +1 -0
  127. package/dist/templates/types.d.ts +141 -0
  128. package/dist/templates/types.d.ts.map +1 -0
  129. package/dist/templates/types.js +2 -0
  130. package/dist/templates/types.js.map +1 -0
  131. package/dist/token.d.ts +37 -0
  132. package/dist/token.d.ts.map +1 -0
  133. package/dist/token.js +152 -0
  134. package/dist/token.js.map +1 -0
  135. package/dist/transport.d.ts +53 -0
  136. package/dist/transport.d.ts.map +1 -0
  137. package/dist/transport.js +328 -0
  138. package/dist/transport.js.map +1 -0
  139. package/package.json +45 -0
@@ -0,0 +1,56 @@
1
+ import { type CallsClientOptions } from "@polymorfa/sdk/calls/internal";
2
+ import { type BrowserTransportOptions } from "../transport.js";
3
+ import { CallsController, type CallsControllerOptions } from "./controller.js";
4
+ import { type CallMediaFactory, type WebRtcMediaFactoryOptions } from "./media.js";
5
+ export interface BrowserCallsOptions extends BrowserTransportOptions {
6
+ /** Local call identity. Requests use the client token's bound session. */
7
+ readonly session: string;
8
+ readonly WebSocket?: CallsClientOptions["WebSocket"];
9
+ readonly controller?: CallsControllerOptions;
10
+ /**
11
+ * Send call diagnostics (default `true`): connection quality figures every
12
+ * 15 seconds and when a connection closes, and an error code when media
13
+ * fails. No personal data is sent. Same as `controller.diagnostics`.
14
+ */
15
+ readonly diagnostics?: boolean;
16
+ /**
17
+ * Failures that do not belong to one call. `code: "unauthorized"` means the
18
+ * platform stopped accepting the client token; the next attempt asks
19
+ * `getClientToken` for a new one.
20
+ */
21
+ readonly onError?: (error: {
22
+ code: string;
23
+ message: string;
24
+ }) => void;
25
+ }
26
+ /**
27
+ * Media overrides used by Polymorfa's own tests and embedded runtimes.
28
+ * @internal
29
+ */
30
+ export interface InternalBrowserCallsOptions extends BrowserCallsOptions {
31
+ readonly media?: Omit<WebRtcMediaFactoryOptions, "signaling">;
32
+ readonly mediaFactory?: CallMediaFactory;
33
+ }
34
+ /** @internal */
35
+ export declare function createInternalBrowserCalls(options: InternalBrowserCallsOptions): BrowserCalls;
36
+ export interface BrowserCalls {
37
+ readonly controller: CallsController;
38
+ readonly connected: boolean;
39
+ /** Connect the authenticated lifecycle stream; rejects if the first attempt fails. */
40
+ connect(): Promise<void>;
41
+ /**
42
+ * Release the widget, media and lifecycle stream. Joined calls are left,
43
+ * not ended; a placed call that is still ringing is ended. Create a new
44
+ * instance to reconnect.
45
+ */
46
+ dispose(): Promise<void>;
47
+ }
48
+ /**
49
+ * The browser calling component: incoming calls, placement, answer, join,
50
+ * leave and end, microphone, camera and device control, with a
51
+ * `CallsController` for the UI packages. The client token from
52
+ * `getClientToken` is the only credential. Incoming calls ring until the
53
+ * application answers, joins, or declines them.
54
+ */
55
+ export declare function createBrowserCalls(publicOptions: BrowserCallsOptions): BrowserCalls;
56
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/calls/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,kBAAkB,EAExB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,eAAe,EAGf,KAAK,sBAAsB,EAC5B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,yBAAyB,EAC/B,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,mBAAoB,SAAQ,uBAAuB;IAClE,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,kBAAkB,CAAC,WAAW,CAAC,CAAC;IACrD,QAAQ,CAAC,UAAU,CAAC,EAAE,sBAAsB,CAAC;IAC7C;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACvE;AAED;;;GAGG;AACH,MAAM,WAAW,2BAA4B,SAAQ,mBAAmB;IACtE,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,yBAAyB,EAAE,WAAW,CAAC,CAAC;IAC9D,QAAQ,CAAC,YAAY,CAAC,EAAE,gBAAgB,CAAC;CAC1C;AAED,gBAAgB;AAChB,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,2BAA2B,GACnC,YAAY,CAEd;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,sFAAsF;IACtF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB;;;;OAIG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,mBAAmB,GACjC,YAAY,CA0Pd"}
@@ -0,0 +1,260 @@
1
+ import { CallsClient, } from "@polymorfa/sdk/calls/internal";
2
+ import { BrowserTransport, } from "../transport.js";
3
+ import { BrowserCallsApi } from "./api.js";
4
+ import { CallsController, } from "./controller.js";
5
+ import { WebRtcMediaFactory, } from "./media.js";
6
+ import { CallsSignalingClient } from "./signaling.js";
7
+ /** @internal */
8
+ export function createInternalBrowserCalls(options) {
9
+ return createBrowserCalls(options);
10
+ }
11
+ /**
12
+ * The browser calling component: incoming calls, placement, answer, join,
13
+ * leave and end, microphone, camera and device control, with a
14
+ * `CallsController` for the UI packages. The client token from
15
+ * `getClientToken` is the only credential. Incoming calls ring until the
16
+ * application answers, joins, or declines them.
17
+ */
18
+ export function createBrowserCalls(publicOptions) {
19
+ const options = publicOptions;
20
+ const transport = new BrowserTransport(options);
21
+ const api = new BrowserCallsApi(transport);
22
+ const clientOptions = {
23
+ session: options.session,
24
+ api,
25
+ mediaMode: "external",
26
+ ...(options.WebSocket === undefined
27
+ ? {}
28
+ : { WebSocket: options.WebSocket }),
29
+ ...(options.now === undefined ? {} : { now: options.now }),
30
+ };
31
+ const client = new CallsClient(clientOptions);
32
+ const listeners = new Set();
33
+ const emit = (event) => {
34
+ for (const listener of [...listeners])
35
+ listener(event);
36
+ };
37
+ let disposed = false;
38
+ let placing = false;
39
+ let disposal;
40
+ let connecting;
41
+ const requireCall = (id) => {
42
+ const call = client.getCall(id);
43
+ if (!call)
44
+ throw new Error("Call is no longer available.");
45
+ return call;
46
+ };
47
+ const backend = {
48
+ dispose: () => {
49
+ void dispose();
50
+ },
51
+ getCall: (id) => client.getCall(id),
52
+ connectionId: (id) => client.getCall(id)?.connectionId,
53
+ subscribe: (listener) => {
54
+ listeners.add(listener);
55
+ return () => {
56
+ listeners.delete(listener);
57
+ };
58
+ },
59
+ place: async (input, signal) => {
60
+ if (disposed || !client.connected)
61
+ throw new Error("Connect browser calls before placing a call.");
62
+ if (placing || client.calls.some((call) => call.state !== "incoming"))
63
+ throw new Error("Finish the active call before placing another.");
64
+ placing = true;
65
+ try {
66
+ const call = await client.place(input.to, {
67
+ video: input.video,
68
+ idempotencyKey: input.idempotencyKey,
69
+ ...(input.exclusive === undefined
70
+ ? {}
71
+ : { exclusive: input.exclusive }),
72
+ signal,
73
+ });
74
+ return { callId: call.id };
75
+ }
76
+ finally {
77
+ placing = false;
78
+ }
79
+ },
80
+ answer: async (id, signal, input) => {
81
+ signal.throwIfAborted();
82
+ const call = requireCall(id);
83
+ await call.answer({
84
+ exclusive: input?.exclusive === true,
85
+ ...(input === undefined ? {} : { video: input.video }),
86
+ });
87
+ return {
88
+ answered: true,
89
+ answeredBy: call.claim.answeredBy ?? "",
90
+ exclusive: call.claim.exclusive,
91
+ };
92
+ },
93
+ join: async (id, signal, input) => {
94
+ signal.throwIfAborted();
95
+ const call = requireCall(id);
96
+ await call.join({ video: input.video });
97
+ return {
98
+ answered: true,
99
+ answeredBy: call.claim.answeredBy ?? "",
100
+ exclusive: call.claim.exclusive,
101
+ };
102
+ },
103
+ reject: async (id, signal) => {
104
+ signal.throwIfAborted();
105
+ await requireCall(id).reject();
106
+ },
107
+ leave: async (id, _connectionId, signal) => {
108
+ signal.throwIfAborted();
109
+ // The call's own connection id is the one the media session used.
110
+ await requireCall(id).leave();
111
+ },
112
+ hangup: async (id, signal) => {
113
+ signal.throwIfAborted();
114
+ // Keep the model live if the request fails, so the widget can retry.
115
+ await requireCall(id).end();
116
+ },
117
+ report: (id, report) => api.report(id, report),
118
+ };
119
+ const media = options.mediaFactory ??
120
+ new WebRtcMediaFactory({
121
+ ...options.media,
122
+ signaling: new CallsSignalingClient(transport),
123
+ });
124
+ const controller = new CallsController(backend, media, {
125
+ ...options.controller,
126
+ ...(options.diagnostics === undefined
127
+ ? {}
128
+ : { diagnostics: options.diagnostics }),
129
+ });
130
+ client.on("incoming", (call) => {
131
+ if (disposed || call.ended)
132
+ return;
133
+ // Every invitation is listed; none is declined on the application's behalf.
134
+ emit({
135
+ type: "incomingCall",
136
+ call: {
137
+ callId: call.id,
138
+ from: call.peer,
139
+ video: call.hasVideo,
140
+ capabilities: call.capabilities,
141
+ },
142
+ });
143
+ for (const participant of call.participants)
144
+ emit({ type: "participant", callId: call.id, participant });
145
+ });
146
+ client.on("call", (call) => {
147
+ call.on("connected", () => emit({ type: "connected", callId: call.id }));
148
+ call.on("error", (error) => options.onError?.(error));
149
+ call.on("claim", (claim) => {
150
+ if (call.direction === "outbound")
151
+ return;
152
+ emit({
153
+ type: "accepted",
154
+ callId: call.id,
155
+ ...(claim.answeredBy === undefined
156
+ ? {}
157
+ : { answeredBy: claim.answeredBy }),
158
+ exclusive: claim.exclusive,
159
+ claimedByOther: claim.claimedByOther,
160
+ });
161
+ });
162
+ call.on("state", (state, previous) => {
163
+ if (call.direction === "outbound" && previous === "ringing")
164
+ if (state === "connecting" || state === "connected")
165
+ emit({ type: "accepted", callId: call.id });
166
+ });
167
+ call.on("capabilities", (capabilities) => emit({ type: "capabilities", callId: call.id, capabilities }));
168
+ call.on("participantJoined", (participant) => emit({ type: "participant", callId: call.id, participant }));
169
+ call.on("participantState", (participant) => emit({ type: "participant", callId: call.id, participant }));
170
+ call.on("participantLeft", (participantId) => emit({ type: "participantLeft", callId: call.id, participantId }));
171
+ });
172
+ client.on("ended", (call, reason) => {
173
+ const snapshot = controller.getSnapshot();
174
+ const listed = snapshot.invitations.some((i) => i.callId === call.id);
175
+ if (snapshot.callId !== call.id && !listed)
176
+ return;
177
+ // Early outbound events are replayed before place() returns. The
178
+ // controller adopts the terminal Call after the HTTP response.
179
+ if (snapshot.callId === call.id &&
180
+ ["error", "ended"].includes(snapshot.status))
181
+ return;
182
+ emit({ type: "ended", callId: call.id, reason });
183
+ });
184
+ client.on("error", (error) => options.onError?.(error));
185
+ // Keep the shared call's terminal state in sync with browser media failures.
186
+ const unsubscribe = controller.subscribe(() => {
187
+ const snapshot = controller.getSnapshot();
188
+ const call = client.getCall(snapshot.callId ?? "");
189
+ if (call &&
190
+ !call.ended &&
191
+ call.state !== "incoming" &&
192
+ snapshot.error?.code !== "call_control_failed" &&
193
+ (snapshot.status === "ended" || snapshot.status === "error")) {
194
+ const reason = snapshot.endReason === "capacity"
195
+ ? "capacity"
196
+ : snapshot.endReason === "pod_lost"
197
+ ? "pod_lost"
198
+ : "connection_failed";
199
+ // Nobody else is on an outbound call that is still ringing, and nobody
200
+ // can take over a call this client claimed (exclusive answer or
201
+ // placement). Leaving either would strand the other party, so end it,
202
+ // as @polymorfa/sdk/calls does for socket media.
203
+ const release = (call.direction === "outbound" && call.state === "ringing") ||
204
+ call._claimedBySelf;
205
+ call._remoteEnded(reason);
206
+ if (release)
207
+ void api
208
+ .end(call.id)
209
+ .catch((cause) => options.onError?.({ code: "end_failed", message: message(cause) }));
210
+ // Media failed locally: leave the connection; the call continues for
211
+ // others. The platform requires a connection id; the call's own id is
212
+ // the one the media session offers with.
213
+ else
214
+ void api.leave(call.id, call.connectionId).catch((cause) => options.onError?.({
215
+ code: "leave_failed",
216
+ message: message(cause),
217
+ }));
218
+ }
219
+ });
220
+ function dispose() {
221
+ if (disposal)
222
+ return disposal;
223
+ if (disposed)
224
+ return Promise.resolve();
225
+ disposed = true;
226
+ unsubscribe();
227
+ listeners.clear();
228
+ controller.dispose();
229
+ disposal = client.disconnect();
230
+ return disposal;
231
+ }
232
+ return {
233
+ controller,
234
+ get connected() {
235
+ return client.connected;
236
+ },
237
+ connect: () => {
238
+ if (disposed)
239
+ return Promise.reject(new Error("Browser calls is disposed."));
240
+ if (connecting)
241
+ return connecting;
242
+ controller.initialize();
243
+ connecting = client
244
+ .connect()
245
+ .then(() => {
246
+ if (!client.connected)
247
+ throw new Error("Calls lifecycle stream is not connected; reconnection continues until disposal.");
248
+ })
249
+ .finally(() => {
250
+ connecting = undefined;
251
+ });
252
+ return connecting;
253
+ },
254
+ dispose,
255
+ };
256
+ }
257
+ function message(cause) {
258
+ return cause instanceof Error ? cause.message : "Call operation failed.";
259
+ }
260
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/calls/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,329 @@
1
+ import { type AcceptCallResult, type Call, type CallReport, type Participant } from "@polymorfa/sdk/calls/internal";
2
+ import { ObservableController, type ControllerSnapshot } from "../controller.js";
3
+ import type { CallMediaFactory } from "./media.js";
4
+ export type CallStatus = "idle" | "ready" | "incoming" | "ringing" | "accepted" | "connecting" | "connected" | "reconnecting" | "ended" | "error";
5
+ export type CallEndReason = "hangup" | "left" | "rejected" | "missed" | "busy" | "connection_failed" | "ice_timeout" | "capacity" | "remote_hangup" | "pod_lost" | (string & {});
6
+ /**
7
+ * What the displayed call supports. The platform reports video and invite
8
+ * support per call; muting is always available.
9
+ */
10
+ export interface CallCapabilities {
11
+ readonly video: boolean;
12
+ readonly mute: boolean;
13
+ readonly invite: boolean;
14
+ }
15
+ export type CallDeviceKind = "audioinput" | "videoinput" | "audiooutput";
16
+ export interface CallDevice {
17
+ readonly deviceId: string;
18
+ readonly kind: CallDeviceKind;
19
+ readonly label: string;
20
+ }
21
+ /** Capture/playback device choices. `audioOutput` needs `setSinkId` support. */
22
+ export interface SelectedCallDevices {
23
+ readonly audioInput?: string;
24
+ readonly videoInput?: string;
25
+ readonly audioOutput?: string;
26
+ }
27
+ export interface IncomingCall {
28
+ readonly callId: string;
29
+ readonly from: string;
30
+ readonly video: boolean;
31
+ /** Platform-reported capabilities; defaults allow video and invitations. */
32
+ readonly capabilities?: {
33
+ readonly video: boolean;
34
+ readonly invite: boolean;
35
+ };
36
+ }
37
+ /** A call participant as the platform reports it. */
38
+ export type CallParticipant = Participant;
39
+ /**
40
+ * An incoming call this client knows about. Several can ring at once; the
41
+ * controller never declines one on its own.
42
+ */
43
+ export interface CallInvitation {
44
+ readonly callId: string;
45
+ readonly from: string;
46
+ readonly video: boolean;
47
+ readonly capabilities: CallCapabilities;
48
+ /** Somebody answered the call. */
49
+ readonly answered: boolean;
50
+ /** Participant reference that answered first, when known. */
51
+ readonly answeredBy?: string;
52
+ /** The answer claimed the call. */
53
+ readonly exclusive: boolean;
54
+ /** Another participant claimed the call: stop ringing, do not decline. */
55
+ readonly claimedByOther: boolean;
56
+ /** Answered without a claim: this client can join. */
57
+ readonly canJoin: boolean;
58
+ }
59
+ /** One remote participant's video with its stream. */
60
+ export interface ParticipantVideo extends RemoteVideoInfo {
61
+ readonly stream: MediaStream;
62
+ }
63
+ /** A remote video tile, without its media stream (see `controller.remoteVideos`). */
64
+ export interface RemoteVideoInfo {
65
+ /** Stable key for rendering one participant's video. */
66
+ readonly key: string;
67
+ /** Display label: the participant's number or ID, or the other connection's participant reference. */
68
+ readonly label: string;
69
+ readonly connectionId?: string;
70
+ /** Participant reference behind `connectionId`, when known. */
71
+ readonly connectionParticipant?: string;
72
+ readonly participant?: CallParticipant;
73
+ }
74
+ export type CallLifecycleEvent = {
75
+ readonly type: "incomingCall";
76
+ readonly call: IncomingCall;
77
+ } | {
78
+ readonly type: "ringing" | "connected";
79
+ readonly callId: string;
80
+ } | {
81
+ /**
82
+ * The call was answered. On a call this client placed, the remote party
83
+ * picked up. On an incoming call, `answeredBy` and `exclusive` say who
84
+ * answered and whether they claimed it.
85
+ */
86
+ readonly type: "accepted";
87
+ readonly callId: string;
88
+ readonly answeredBy?: string;
89
+ readonly exclusive?: boolean;
90
+ /** Set by backends that know this client's participant reference. */
91
+ readonly claimedByOther?: boolean;
92
+ /**
93
+ * Capabilities the platform reported with the answer. Reported fields
94
+ * only: an absent field keeps the call's current value.
95
+ */
96
+ readonly capabilities?: {
97
+ readonly video?: boolean;
98
+ readonly invite?: boolean;
99
+ };
100
+ } | {
101
+ /** The platform reported different capabilities for a call. */
102
+ readonly type: "capabilities";
103
+ readonly callId: string;
104
+ /** Reported fields only; an absent field keeps the current value. */
105
+ readonly capabilities: {
106
+ readonly video?: boolean;
107
+ readonly invite?: boolean;
108
+ };
109
+ } | {
110
+ readonly type: "ended";
111
+ readonly callId: string;
112
+ readonly reason?: CallEndReason;
113
+ } | {
114
+ readonly type: "videostate";
115
+ readonly callId: string;
116
+ readonly video: boolean;
117
+ } | {
118
+ readonly type: "participant";
119
+ readonly callId: string;
120
+ readonly participant: CallParticipant;
121
+ } | {
122
+ readonly type: "participantLeft";
123
+ readonly callId: string;
124
+ readonly participantId: string;
125
+ };
126
+ export interface PlaceCallInput {
127
+ readonly to: string;
128
+ readonly video: boolean;
129
+ readonly idempotencyKey: string;
130
+ /** Claim the placed call. Default `false`. */
131
+ readonly exclusive?: boolean;
132
+ }
133
+ export interface AnswerCallInput {
134
+ /** Claim the call. `false` leaves other participants ringing so they can join. */
135
+ readonly exclusive: boolean;
136
+ readonly video: boolean;
137
+ }
138
+ export interface CallsBackend {
139
+ /** Shared call model, when this backend uses the Calls client. */
140
+ getCall?(callId: string): Call | undefined;
141
+ /** Media connection id to use for a call, when the backend owns one. */
142
+ connectionId?(callId: string): string | undefined;
143
+ /** Release an owned lifecycle client when the controller is disposed. */
144
+ dispose?(): void;
145
+ subscribe(listener: (event: CallLifecycleEvent) => void): () => void;
146
+ place(input: PlaceCallInput, signal: AbortSignal): Promise<{
147
+ readonly callId: string;
148
+ }>;
149
+ /**
150
+ * Answer a ringing call. Rejects with `CallClaimedError` when another
151
+ * participant claimed it.
152
+ */
153
+ answer(callId: string, signal: AbortSignal, input?: AnswerCallInput): Promise<void | AcceptCallResult>;
154
+ /** Join an answered, unclaimed call. Defaults to `answer` without a claim. */
155
+ join?(callId: string, signal: AbortSignal, input: {
156
+ readonly video: boolean;
157
+ }): Promise<void | AcceptCallResult>;
158
+ /** Decline a ringing call. Ends it for every participant. */
159
+ reject(callId: string, signal: AbortSignal): Promise<void>;
160
+ /** End the call for every participant. */
161
+ hangup(callId: string, signal: AbortSignal): Promise<void>;
162
+ /**
163
+ * Send a diagnostics report for one of this browser's connections.
164
+ * Optional: without it the controller sends none.
165
+ */
166
+ report?(callId: string, report: CallReport): Promise<void>;
167
+ /**
168
+ * Leave one media connection. When omitted, the media session leaves on
169
+ * its own when it closes.
170
+ */
171
+ leave?(callId: string, connectionId: string | undefined, signal: AbortSignal): Promise<void>;
172
+ }
173
+ export interface CallsSnapshot extends ControllerSnapshot {
174
+ readonly status: CallStatus;
175
+ readonly callId?: string;
176
+ readonly peer?: string;
177
+ readonly direction?: "incoming" | "outgoing";
178
+ readonly capabilities: CallCapabilities;
179
+ readonly video: boolean;
180
+ readonly audioMuted: boolean;
181
+ readonly videoMuted: boolean;
182
+ /** Set once media connected; drives the call duration display. */
183
+ readonly connectedAt?: number;
184
+ readonly devices: readonly CallDevice[];
185
+ readonly selectedDevices: SelectedCallDevices;
186
+ readonly endReason?: CallEndReason;
187
+ readonly error?: {
188
+ readonly code: string;
189
+ readonly message: string;
190
+ readonly recoverable: boolean;
191
+ /**
192
+ * The call the error is about. It differs from `callId` when a failed
193
+ * call gave way to a waiting invitation.
194
+ */
195
+ readonly callId?: string;
196
+ };
197
+ /** Every incoming call this client knows about, displayed one included. */
198
+ readonly invitations: readonly CallInvitation[];
199
+ /** The displayed incoming call was claimed by another participant. */
200
+ readonly claimedByOther: boolean;
201
+ /** The displayed incoming call is answered without a claim; `join()` it. */
202
+ readonly canJoin: boolean;
203
+ /** Who answered the displayed call, when known. */
204
+ readonly answeredBy?: string;
205
+ /** The displayed call is claimed. */
206
+ readonly exclusive: boolean;
207
+ /** Participants of the displayed call. */
208
+ readonly participants: readonly CallParticipant[];
209
+ /** Remote video tiles of the displayed call; streams are on the controller. */
210
+ readonly remoteVideos: readonly RemoteVideoInfo[];
211
+ /**
212
+ * An answer or join is in progress. Until it settles, `answer()`, `join()`,
213
+ * `reject()` and `place()` are refused; `select()` and `dismiss()` stay
214
+ * available.
215
+ */
216
+ readonly answering?: boolean;
217
+ }
218
+ export interface CallsControllerOptions {
219
+ readonly createIdempotencyKey?: () => string;
220
+ readonly now?: () => number;
221
+ /**
222
+ * Resumption window: how long media may stay disconnected before the call
223
+ * is given up (default 15 000 ms), and how long `disconnected` may last
224
+ * before an ICE restart is attempted (default 2 000 ms).
225
+ */
226
+ readonly resumptionWindowMs?: number;
227
+ readonly iceRestartAfterMs?: number;
228
+ readonly setTimeout?: typeof globalThis.setTimeout;
229
+ readonly clearTimeout?: typeof globalThis.clearTimeout;
230
+ /**
231
+ * Send call diagnostics for this browser's media connections (default
232
+ * `true`): round-trip time, jitter, packet counts, codecs, candidate type
233
+ * and reconnect count every 15 seconds and when the connection closes, and
234
+ * an error code when media fails. Reports carry no personal data. `false`
235
+ * sends none.
236
+ */
237
+ readonly diagnostics?: boolean;
238
+ }
239
+ export declare class CallsController extends ObservableController<CallsSnapshot> {
240
+ #private;
241
+ constructor(backend: CallsBackend, mediaFactory: CallMediaFactory, options?: CallsControllerOptions);
242
+ initialize(): void;
243
+ /** The shared call model for the displayed call, when supplied by the backend. */
244
+ get call(): Call | undefined;
245
+ place(to: string, options?: {
246
+ readonly video?: boolean;
247
+ /** Claim the placed call. Default `false`. */
248
+ readonly exclusive?: boolean;
249
+ }): Promise<void>;
250
+ /**
251
+ * Answer the displayed incoming call, or the invitation named by `callId`.
252
+ * `exclusive` defaults to `false`: other participants keep ringing and can
253
+ * join. A call claimed by another participant is not answered; the snapshot
254
+ * reports `claimedByOther`.
255
+ */
256
+ answer(options?: {
257
+ readonly video?: boolean;
258
+ readonly exclusive?: boolean;
259
+ readonly callId?: string;
260
+ }): Promise<void>;
261
+ /**
262
+ * Join the displayed (or named) call that another participant answered
263
+ * without claiming it. Never claims.
264
+ */
265
+ join(options?: {
266
+ readonly video?: boolean;
267
+ readonly callId?: string;
268
+ }): Promise<void>;
269
+ /**
270
+ * Decline the displayed (or named) ringing call. This ends the call for
271
+ * every participant; only call it on an explicit user action.
272
+ */
273
+ reject(options?: {
274
+ readonly callId?: string;
275
+ }): Promise<void>;
276
+ /**
277
+ * Stop showing an incoming call without declining it. Other participants
278
+ * keep ringing; the call is not affected.
279
+ */
280
+ dismiss(callId?: string): void;
281
+ /** Show a known invitation while no call is active. */
282
+ select(callId: string): void;
283
+ /**
284
+ * Leave the displayed call: close this client's media connection. The call
285
+ * continues for the other participants. An outgoing call that has not
286
+ * connected has no other participants, so leaving it ends it instead;
287
+ * otherwise the callee would keep ringing.
288
+ */
289
+ leave(): Promise<void>;
290
+ /** End the displayed call for every participant. */
291
+ end(): Promise<void>;
292
+ /** Same as {@link end}: ends the call for every participant. */
293
+ hangup(): Promise<void>;
294
+ setMuted(muted: {
295
+ readonly audio?: boolean;
296
+ readonly video?: boolean;
297
+ }): void;
298
+ /**
299
+ * Whether an audio→video upgrade is possible right now: the call carries
300
+ * video, the call is not already video, and the media session can
301
+ * renegotiate.
302
+ */
303
+ get canEnableVideo(): boolean;
304
+ /**
305
+ * Start sending the camera on an audio call and renegotiate on the same
306
+ * connection. No-op when the call has no video, when video is already on,
307
+ * or when the media session cannot renegotiate.
308
+ */
309
+ enableVideo(): Promise<void>;
310
+ /** Choose the devices the next call acquires (and the speaker the UI plays through). */
311
+ setPreferredDevices(devices: SelectedCallDevices): void;
312
+ /**
313
+ * Switch the live microphone or camera mid-call (the outgoing track is
314
+ * replaced in place, mute state carries over) and remember the choice for
315
+ * the next call.
316
+ */
317
+ switchDevice(kind: "audioInput" | "videoInput", deviceId: string): Promise<void>;
318
+ /** Re-enumerate capture/playback devices into the snapshot. */
319
+ refreshDevices(): Promise<void>;
320
+ get localStream(): MediaStream | undefined;
321
+ /** Merged call audio. */
322
+ get remoteStream(): MediaStream | undefined;
323
+ /** One video per remote source, keyed like `snapshot.remoteVideos`. */
324
+ get remoteVideos(): readonly ParticipantVideo[];
325
+ protected onDispose(): void;
326
+ /** Keeps `invitations` and `answering` current on every transition. */
327
+ protected transition(next: Omit<CallsSnapshot, "revision" | "updatedAt">): void;
328
+ }
329
+ //# sourceMappingURL=controller.d.ts.map