@babelforce/babelconnect-sdk 0.9.1 → 0.10.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.
- package/dist/client.js +9 -3
- package/dist/gen/babelconnect/v1/babelconnect_pb.d.ts +61 -0
- package/dist/gen/babelconnect/v1/babelconnect_pb.js +80 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/media.d.ts +27 -0
- package/dist/media.js +65 -2
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createClient, } from "@connectrpc/connect";
|
|
|
2
2
|
import { createGrpcWebTransport } from "@connectrpc/connect-web";
|
|
3
3
|
import { Agent } from "./gen/babelconnect/v1/babelconnect_connect.js";
|
|
4
4
|
import { AddConferenceMember, AnswerCall, CallDirection, CallLifecycle, CallSource, Command, EndConference, Error as BcError, FlagRecording, Hangup, HistoryRequest, SmsThreadRequest, PhonebookRequest, HoldConferenceMember, Hold, KickConferenceMember, LeaveConference, MarkConversationRead, Mute, MuteConferenceMember, PlaceCall, Register, ResetLineStatus, SendDigits, SendSmsRequest, SetAgentNumber, SetConversationOpen, SetDisplayAs, SetPresence, SetWebrtc, StartConference, StartRecording, StopRecording, SetRecordingTags, SubscribeRequest, Transfer, WrapUpCancel, WrapUpExtend, } from "./gen/babelconnect/v1/babelconnect_pb.js";
|
|
5
|
-
import { browserMediaFactory, toRTCIceServers, } from "./media.js";
|
|
5
|
+
import { browserMediaFactory, MediaError, toRTCIceServers, } from "./media.js";
|
|
6
6
|
import { StateCache } from "./state-cache.js";
|
|
7
7
|
/**
|
|
8
8
|
* The TypeScript "dumb renderer" client: opens the `Subscribe`/`Send` gRPC-web
|
|
@@ -420,9 +420,15 @@ export class BabelconnectClient {
|
|
|
420
420
|
}));
|
|
421
421
|
}
|
|
422
422
|
catch (e) {
|
|
423
|
+
// CALL-M5: a Media impl classifies a mic-acquisition failure (no mic,
|
|
424
|
+
// permission denied, device busy) into a distinct code via MediaError;
|
|
425
|
+
// anything else stays the opaque catch-all. The distinct code lets the
|
|
426
|
+
// UI/host explain the cause instead of showing a raw WebRTC string.
|
|
427
|
+
const code = e instanceof MediaError ? e.code : "media_answer_failed";
|
|
428
|
+
const message = e instanceof MediaError ? e.message : String(e);
|
|
423
429
|
this.opts.onError?.(new BcError({
|
|
424
|
-
code
|
|
425
|
-
message
|
|
430
|
+
code,
|
|
431
|
+
message,
|
|
426
432
|
callId: call.id,
|
|
427
433
|
}));
|
|
428
434
|
}
|
|
@@ -332,6 +332,18 @@ export declare class Identity extends Message<Identity> {
|
|
|
332
332
|
* @generated from field: babelconnect.v1.AgentState state = 5;
|
|
333
333
|
*/
|
|
334
334
|
state: AgentState;
|
|
335
|
+
/**
|
|
336
|
+
* account / company display name, when known (ACC-E1)
|
|
337
|
+
*
|
|
338
|
+
* @generated from field: string account_name = 6;
|
|
339
|
+
*/
|
|
340
|
+
accountName: string;
|
|
341
|
+
/**
|
|
342
|
+
* accounts this agent may switch to (ACC-E2)
|
|
343
|
+
*
|
|
344
|
+
* @generated from field: repeated babelconnect.v1.Account accounts = 7;
|
|
345
|
+
*/
|
|
346
|
+
accounts: Account[];
|
|
335
347
|
constructor(data?: PartialMessage<Identity>);
|
|
336
348
|
static readonly runtime: typeof proto3;
|
|
337
349
|
static readonly typeName = "babelconnect.v1.Identity";
|
|
@@ -341,6 +353,41 @@ export declare class Identity extends Message<Identity> {
|
|
|
341
353
|
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Identity;
|
|
342
354
|
static equals(a: Identity | PlainMessage<Identity> | undefined, b: Identity | PlainMessage<Identity> | undefined): boolean;
|
|
343
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Account is one tenant the signed-in agent can operate in — the switchable set
|
|
358
|
+
* (agent-role only) surfaced for the account picker (ACC-E2). Sourced from
|
|
359
|
+
* GET /api/v2/user/accounts.
|
|
360
|
+
*
|
|
361
|
+
* @generated from message babelconnect.v1.Account
|
|
362
|
+
*/
|
|
363
|
+
export declare class Account extends Message<Account> {
|
|
364
|
+
/**
|
|
365
|
+
* customer / account id
|
|
366
|
+
*
|
|
367
|
+
* @generated from field: string id = 1;
|
|
368
|
+
*/
|
|
369
|
+
id: string;
|
|
370
|
+
/**
|
|
371
|
+
* company / display name
|
|
372
|
+
*
|
|
373
|
+
* @generated from field: string name = 2;
|
|
374
|
+
*/
|
|
375
|
+
name: string;
|
|
376
|
+
/**
|
|
377
|
+
* the account this session is currently scoped to
|
|
378
|
+
*
|
|
379
|
+
* @generated from field: bool current = 3;
|
|
380
|
+
*/
|
|
381
|
+
current: boolean;
|
|
382
|
+
constructor(data?: PartialMessage<Account>);
|
|
383
|
+
static readonly runtime: typeof proto3;
|
|
384
|
+
static readonly typeName = "babelconnect.v1.Account";
|
|
385
|
+
static readonly fields: FieldList;
|
|
386
|
+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Account;
|
|
387
|
+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Account;
|
|
388
|
+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Account;
|
|
389
|
+
static equals(a: Account | PlainMessage<Account> | undefined, b: Account | PlainMessage<Account> | undefined): boolean;
|
|
390
|
+
}
|
|
344
391
|
/**
|
|
345
392
|
* AgentView is the complete per-agent state the server owns and renders to. A
|
|
346
393
|
* snapshot carries the whole view; patches carry entity-level deltas.
|
|
@@ -532,6 +579,12 @@ export declare class FeatureAccount extends Message<FeatureAccount> {
|
|
|
532
579
|
* @generated from field: bool show_status = 3;
|
|
533
580
|
*/
|
|
534
581
|
showStatus: boolean;
|
|
582
|
+
/**
|
|
583
|
+
* the multi-account switcher (ACC-E2)
|
|
584
|
+
*
|
|
585
|
+
* @generated from field: bool allow_account_switch = 4;
|
|
586
|
+
*/
|
|
587
|
+
allowAccountSwitch: boolean;
|
|
535
588
|
constructor(data?: PartialMessage<FeatureAccount>);
|
|
536
589
|
static readonly runtime: typeof proto3;
|
|
537
590
|
static readonly typeName = "babelconnect.v1.FeatureAccount";
|
|
@@ -716,6 +769,14 @@ export declare class AgentInfo extends Message<AgentInfo> {
|
|
|
716
769
|
* @generated from field: bool line_blocked = 18;
|
|
717
770
|
*/
|
|
718
771
|
lineBlocked: boolean;
|
|
772
|
+
/**
|
|
773
|
+
* Accounts this agent may switch to without re-logging-in (agent-role only,
|
|
774
|
+
* resolved at auth from GET /api/v2/user/accounts). One is marked `current`.
|
|
775
|
+
* Drives the Account tab's switcher; empty/single ⇒ no picker (ACC-E2).
|
|
776
|
+
*
|
|
777
|
+
* @generated from field: repeated babelconnect.v1.Account accounts = 19;
|
|
778
|
+
*/
|
|
779
|
+
accounts: Account[];
|
|
719
780
|
constructor(data?: PartialMessage<AgentInfo>);
|
|
720
781
|
static readonly runtime: typeof proto3;
|
|
721
782
|
static readonly typeName = "babelconnect.v1.AgentInfo";
|
|
@@ -471,6 +471,18 @@ export class Identity extends Message {
|
|
|
471
471
|
* @generated from field: babelconnect.v1.AgentState state = 5;
|
|
472
472
|
*/
|
|
473
473
|
state = AgentState.UNSPECIFIED;
|
|
474
|
+
/**
|
|
475
|
+
* account / company display name, when known (ACC-E1)
|
|
476
|
+
*
|
|
477
|
+
* @generated from field: string account_name = 6;
|
|
478
|
+
*/
|
|
479
|
+
accountName = "";
|
|
480
|
+
/**
|
|
481
|
+
* accounts this agent may switch to (ACC-E2)
|
|
482
|
+
*
|
|
483
|
+
* @generated from field: repeated babelconnect.v1.Account accounts = 7;
|
|
484
|
+
*/
|
|
485
|
+
accounts = [];
|
|
474
486
|
constructor(data) {
|
|
475
487
|
super();
|
|
476
488
|
proto3.util.initPartial(data, this);
|
|
@@ -483,6 +495,8 @@ export class Identity extends Message {
|
|
|
483
495
|
{ no: 3, name: "number", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
484
496
|
{ no: 4, name: "username", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
485
497
|
{ no: 5, name: "state", kind: "enum", T: proto3.getEnumType(AgentState) },
|
|
498
|
+
{ no: 6, name: "account_name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
499
|
+
{ no: 7, name: "accounts", kind: "message", T: Account, repeated: true },
|
|
486
500
|
]);
|
|
487
501
|
static fromBinary(bytes, options) {
|
|
488
502
|
return new Identity().fromBinary(bytes, options);
|
|
@@ -497,6 +511,56 @@ export class Identity extends Message {
|
|
|
497
511
|
return proto3.util.equals(Identity, a, b);
|
|
498
512
|
}
|
|
499
513
|
}
|
|
514
|
+
/**
|
|
515
|
+
* Account is one tenant the signed-in agent can operate in — the switchable set
|
|
516
|
+
* (agent-role only) surfaced for the account picker (ACC-E2). Sourced from
|
|
517
|
+
* GET /api/v2/user/accounts.
|
|
518
|
+
*
|
|
519
|
+
* @generated from message babelconnect.v1.Account
|
|
520
|
+
*/
|
|
521
|
+
export class Account extends Message {
|
|
522
|
+
/**
|
|
523
|
+
* customer / account id
|
|
524
|
+
*
|
|
525
|
+
* @generated from field: string id = 1;
|
|
526
|
+
*/
|
|
527
|
+
id = "";
|
|
528
|
+
/**
|
|
529
|
+
* company / display name
|
|
530
|
+
*
|
|
531
|
+
* @generated from field: string name = 2;
|
|
532
|
+
*/
|
|
533
|
+
name = "";
|
|
534
|
+
/**
|
|
535
|
+
* the account this session is currently scoped to
|
|
536
|
+
*
|
|
537
|
+
* @generated from field: bool current = 3;
|
|
538
|
+
*/
|
|
539
|
+
current = false;
|
|
540
|
+
constructor(data) {
|
|
541
|
+
super();
|
|
542
|
+
proto3.util.initPartial(data, this);
|
|
543
|
+
}
|
|
544
|
+
static runtime = proto3;
|
|
545
|
+
static typeName = "babelconnect.v1.Account";
|
|
546
|
+
static fields = proto3.util.newFieldList(() => [
|
|
547
|
+
{ no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
548
|
+
{ no: 2, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
549
|
+
{ no: 3, name: "current", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
|
550
|
+
]);
|
|
551
|
+
static fromBinary(bytes, options) {
|
|
552
|
+
return new Account().fromBinary(bytes, options);
|
|
553
|
+
}
|
|
554
|
+
static fromJson(jsonValue, options) {
|
|
555
|
+
return new Account().fromJson(jsonValue, options);
|
|
556
|
+
}
|
|
557
|
+
static fromJsonString(jsonString, options) {
|
|
558
|
+
return new Account().fromJsonString(jsonString, options);
|
|
559
|
+
}
|
|
560
|
+
static equals(a, b) {
|
|
561
|
+
return proto3.util.equals(Account, a, b);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
500
564
|
/**
|
|
501
565
|
* AgentView is the complete per-agent state the server owns and renders to. A
|
|
502
566
|
* snapshot carries the whole view; patches carry entity-level deltas.
|
|
@@ -767,6 +831,12 @@ export class FeatureAccount extends Message {
|
|
|
767
831
|
* @generated from field: bool show_status = 3;
|
|
768
832
|
*/
|
|
769
833
|
showStatus = false;
|
|
834
|
+
/**
|
|
835
|
+
* the multi-account switcher (ACC-E2)
|
|
836
|
+
*
|
|
837
|
+
* @generated from field: bool allow_account_switch = 4;
|
|
838
|
+
*/
|
|
839
|
+
allowAccountSwitch = false;
|
|
770
840
|
constructor(data) {
|
|
771
841
|
super();
|
|
772
842
|
proto3.util.initPartial(data, this);
|
|
@@ -777,6 +847,7 @@ export class FeatureAccount extends Message {
|
|
|
777
847
|
{ no: 1, name: "enabled", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
|
778
848
|
{ no: 2, name: "allow_device_switch", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
|
779
849
|
{ no: 3, name: "show_status", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
|
850
|
+
{ no: 4, name: "allow_account_switch", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
|
780
851
|
]);
|
|
781
852
|
static fromBinary(bytes, options) {
|
|
782
853
|
return new FeatureAccount().fromBinary(bytes, options);
|
|
@@ -1007,6 +1078,14 @@ export class AgentInfo extends Message {
|
|
|
1007
1078
|
* @generated from field: bool line_blocked = 18;
|
|
1008
1079
|
*/
|
|
1009
1080
|
lineBlocked = false;
|
|
1081
|
+
/**
|
|
1082
|
+
* Accounts this agent may switch to without re-logging-in (agent-role only,
|
|
1083
|
+
* resolved at auth from GET /api/v2/user/accounts). One is marked `current`.
|
|
1084
|
+
* Drives the Account tab's switcher; empty/single ⇒ no picker (ACC-E2).
|
|
1085
|
+
*
|
|
1086
|
+
* @generated from field: repeated babelconnect.v1.Account accounts = 19;
|
|
1087
|
+
*/
|
|
1088
|
+
accounts = [];
|
|
1010
1089
|
constructor(data) {
|
|
1011
1090
|
super();
|
|
1012
1091
|
proto3.util.initPartial(data, this);
|
|
@@ -1032,6 +1111,7 @@ export class AgentInfo extends Message {
|
|
|
1032
1111
|
{ no: 16, name: "account_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
1033
1112
|
{ no: 17, name: "account_name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
1034
1113
|
{ no: 18, name: "line_blocked", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
|
1114
|
+
{ no: 19, name: "accounts", kind: "message", T: Account, repeated: true },
|
|
1035
1115
|
]);
|
|
1036
1116
|
static fromBinary(bytes, options) {
|
|
1037
1117
|
return new AgentInfo().fromBinary(bytes, options);
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,6 @@
|
|
|
12
12
|
export { BabelconnectClient, type ConnectOptions } from "./client.js";
|
|
13
13
|
export { StateCache } from "./state-cache.js";
|
|
14
14
|
export { passwordGrant, revokeToken, pkceChallenge, buildAuthorizeUrl, authorizationCodeGrant, DEFAULT_CLIENT_ID, type TokenResponse, type PkceChallenge, type AuthorizeUrlOptions, } from "./auth.js";
|
|
15
|
-
export { BrowserWebrtcMedia, browserMediaFactory, type Media, type MediaFactory } from "./media.js";
|
|
15
|
+
export { BrowserWebrtcMedia, browserMediaFactory, MediaError, micErrorCode, MIC_NOT_FOUND, MIC_PERMISSION_DENIED, MIC_IN_USE, MEDIA_ANSWER_FAILED, type Media, type MediaFactory, } from "./media.js";
|
|
16
16
|
export * from "./gen/babelconnect/v1/babelconnect_pb.js";
|
|
17
17
|
export { Agent } from "./gen/babelconnect/v1/babelconnect_connect.js";
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
export { BabelconnectClient } from "./client.js";
|
|
13
13
|
export { StateCache } from "./state-cache.js";
|
|
14
14
|
export { passwordGrant, revokeToken, pkceChallenge, buildAuthorizeUrl, authorizationCodeGrant, DEFAULT_CLIENT_ID, } from "./auth.js";
|
|
15
|
-
export { BrowserWebrtcMedia, browserMediaFactory } from "./media.js";
|
|
15
|
+
export { BrowserWebrtcMedia, browserMediaFactory, MediaError, micErrorCode, MIC_NOT_FOUND, MIC_PERMISSION_DENIED, MIC_IN_USE, MEDIA_ANSWER_FAILED, } from "./media.js";
|
|
16
16
|
// The generated babelconnect.v1 messages + enums (AgentView, CallState, Command, …)
|
|
17
17
|
// and the Agent service descriptor.
|
|
18
18
|
export * from "./gen/babelconnect/v1/babelconnect_pb.js";
|
package/dist/media.d.ts
CHANGED
|
@@ -19,6 +19,33 @@ export interface Media {
|
|
|
19
19
|
}
|
|
20
20
|
/** Builds the {@link Media} for a given call id. */
|
|
21
21
|
export type MediaFactory = (callId: string) => Media;
|
|
22
|
+
/** No microphone hardware is available (`NotFoundError`). */
|
|
23
|
+
export declare const MIC_NOT_FOUND = "mic_not_found";
|
|
24
|
+
/** Microphone access was denied (`NotAllowedError` / `SecurityError`). */
|
|
25
|
+
export declare const MIC_PERMISSION_DENIED = "mic_permission_denied";
|
|
26
|
+
/** The microphone is held by another application (`NotReadableError` / `AbortError`). */
|
|
27
|
+
export declare const MIC_IN_USE = "mic_in_use";
|
|
28
|
+
/** The pre-existing catch-all, kept as the fallback when the cause is unclear. */
|
|
29
|
+
export declare const MEDIA_ANSWER_FAILED = "media_answer_failed";
|
|
30
|
+
/**
|
|
31
|
+
* Thrown by a {@link Media} implementation when it cannot start audio for a
|
|
32
|
+
* call — e.g. the microphone is missing, permission was denied, or the device
|
|
33
|
+
* is held by another app (CALL-M5). {@link code} is a stable classification the
|
|
34
|
+
* SDK surfaces on `onError` (and hosts receive as `cti.error`) so the UI can
|
|
35
|
+
* show cause-specific help instead of a raw WebRTC string.
|
|
36
|
+
*/
|
|
37
|
+
export declare class MediaError extends Error {
|
|
38
|
+
readonly code: string;
|
|
39
|
+
constructor(code: string, message: string);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Classify a caught `getUserMedia` failure into one of the mic error codes,
|
|
43
|
+
* falling back to {@link MEDIA_ANSWER_FAILED} when the cause can't be told
|
|
44
|
+
* apart. On the web the real `DOMException` is available so we read its `.name`;
|
|
45
|
+
* the string form is also scanned as a fallback (and to stay in step with the
|
|
46
|
+
* Dart classifier, which only has the collapsed `'Unable to getUserMedia: …'`).
|
|
47
|
+
*/
|
|
48
|
+
export declare function micErrorCode(error: unknown): string;
|
|
22
49
|
/**
|
|
23
50
|
* The default browser WebRTC backend: capture the mic, answer the server's offer
|
|
24
51
|
* (non-trickle ICE), and play the remote audio through a hidden `<audio>` element.
|
package/dist/media.js
CHANGED
|
@@ -1,3 +1,57 @@
|
|
|
1
|
+
/** No microphone hardware is available (`NotFoundError`). */
|
|
2
|
+
export const MIC_NOT_FOUND = "mic_not_found";
|
|
3
|
+
/** Microphone access was denied (`NotAllowedError` / `SecurityError`). */
|
|
4
|
+
export const MIC_PERMISSION_DENIED = "mic_permission_denied";
|
|
5
|
+
/** The microphone is held by another application (`NotReadableError` / `AbortError`). */
|
|
6
|
+
export const MIC_IN_USE = "mic_in_use";
|
|
7
|
+
/** The pre-existing catch-all, kept as the fallback when the cause is unclear. */
|
|
8
|
+
export const MEDIA_ANSWER_FAILED = "media_answer_failed";
|
|
9
|
+
/**
|
|
10
|
+
* Thrown by a {@link Media} implementation when it cannot start audio for a
|
|
11
|
+
* call — e.g. the microphone is missing, permission was denied, or the device
|
|
12
|
+
* is held by another app (CALL-M5). {@link code} is a stable classification the
|
|
13
|
+
* SDK surfaces on `onError` (and hosts receive as `cti.error`) so the UI can
|
|
14
|
+
* show cause-specific help instead of a raw WebRTC string.
|
|
15
|
+
*/
|
|
16
|
+
export class MediaError extends Error {
|
|
17
|
+
code;
|
|
18
|
+
constructor(code, message) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.code = code;
|
|
21
|
+
this.name = "MediaError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Classify a caught `getUserMedia` failure into one of the mic error codes,
|
|
26
|
+
* falling back to {@link MEDIA_ANSWER_FAILED} when the cause can't be told
|
|
27
|
+
* apart. On the web the real `DOMException` is available so we read its `.name`;
|
|
28
|
+
* the string form is also scanned as a fallback (and to stay in step with the
|
|
29
|
+
* Dart classifier, which only has the collapsed `'Unable to getUserMedia: …'`).
|
|
30
|
+
*/
|
|
31
|
+
export function micErrorCode(error) {
|
|
32
|
+
const name = typeof DOMException !== "undefined" && error instanceof DOMException
|
|
33
|
+
? error.name
|
|
34
|
+
: "";
|
|
35
|
+
const s = `${name} ${String(error)}`.toLowerCase();
|
|
36
|
+
if (s.includes("notfound") || s.includes("not found") || s.includes("no device")) {
|
|
37
|
+
return MIC_NOT_FOUND;
|
|
38
|
+
}
|
|
39
|
+
if (s.includes("notallowed") ||
|
|
40
|
+
s.includes("permissiondenied") ||
|
|
41
|
+
s.includes("permission denied") ||
|
|
42
|
+
s.includes("security") ||
|
|
43
|
+
s.includes("denied")) {
|
|
44
|
+
return MIC_PERMISSION_DENIED;
|
|
45
|
+
}
|
|
46
|
+
if (s.includes("notreadable") ||
|
|
47
|
+
s.includes("trackstart") ||
|
|
48
|
+
s.includes("abort") ||
|
|
49
|
+
s.includes("in use") ||
|
|
50
|
+
s.includes("busy")) {
|
|
51
|
+
return MIC_IN_USE;
|
|
52
|
+
}
|
|
53
|
+
return MEDIA_ANSWER_FAILED;
|
|
54
|
+
}
|
|
1
55
|
/**
|
|
2
56
|
* The default browser WebRTC backend: capture the mic, answer the server's offer
|
|
3
57
|
* (non-trickle ICE), and play the remote audio through a hidden `<audio>` element.
|
|
@@ -35,8 +89,17 @@ export class BrowserWebrtcMedia {
|
|
|
35
89
|
el.srcObject = stream;
|
|
36
90
|
void el.play().catch(() => { });
|
|
37
91
|
};
|
|
38
|
-
// Capture the microphone and add it to the peer.
|
|
39
|
-
|
|
92
|
+
// Capture the microphone and add it to the peer. Classify a failure (no mic
|
|
93
|
+
// / permission denied / device busy) into a MediaError the SDK surfaces with
|
|
94
|
+
// a distinct code (CALL-M5) so the UI/host can explain it, instead of the
|
|
95
|
+
// opaque `media_answer_failed`.
|
|
96
|
+
let local;
|
|
97
|
+
try {
|
|
98
|
+
local = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
throw new MediaError(micErrorCode(e), String(e));
|
|
102
|
+
}
|
|
40
103
|
this.local = local;
|
|
41
104
|
for (const track of local.getAudioTracks())
|
|
42
105
|
pc.addTrack(track, local);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@babelforce/babelconnect-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "TypeScript SDK for babelconnect — server-authoritative agent state over gRPC-web, native WebRTC audio, and an embeddable widget (iframe + postMessage) for the Flutter web app.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://babelforce.github.io/babelconnect-sdk/",
|