mediasfu-shared 1.2.7 → 1.3.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.
Files changed (101) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/ProducerClient/producerClientEmits/updateRoomParametersClient.d.ts +2 -0
  3. package/dist/ProducerClient/producerClientEmits/updateRoomParametersClient.d.ts.map +1 -1
  4. package/dist/audioProcessing-Ca5wm1nV.js +150 -0
  5. package/dist/audioProcessing-Ca5wm1nV.js.map +1 -0
  6. package/dist/audioProcessing-VCbu70Jt.cjs +149 -0
  7. package/dist/audioProcessing-VCbu70Jt.cjs.map +1 -0
  8. package/dist/consumers/closeAndResize.d.ts +3 -0
  9. package/dist/consumers/closeAndResize.d.ts.map +1 -1
  10. package/dist/consumers/connectIps.d.ts +6 -0
  11. package/dist/consumers/connectIps.d.ts.map +1 -1
  12. package/dist/consumers/connectSendTransportScreenAudio.d.ts +65 -0
  13. package/dist/consumers/connectSendTransportScreenAudio.d.ts.map +1 -0
  14. package/dist/consumers/consumerBackupPreference.d.ts +14 -0
  15. package/dist/consumers/consumerBackupPreference.d.ts.map +1 -0
  16. package/dist/consumers/disconnectSendTransportScreen.d.ts.map +1 -1
  17. package/dist/consumers/getPipedProducersAlt.d.ts.map +1 -1
  18. package/dist/consumers/index.cjs +23 -1
  19. package/dist/consumers/index.cjs.map +1 -1
  20. package/dist/consumers/index.d.ts +3 -0
  21. package/dist/consumers/index.d.ts.map +1 -1
  22. package/dist/consumers/index.js +61 -39
  23. package/dist/consumers/index.js.map +1 -1
  24. package/dist/consumers/screenAudio.d.ts +64 -0
  25. package/dist/consumers/screenAudio.d.ts.map +1 -0
  26. package/dist/consumers/socketReceiveMethods/producerClosed.d.ts.map +1 -1
  27. package/dist/consumers/startShareScreen.d.ts +14 -0
  28. package/dist/consumers/startShareScreen.d.ts.map +1 -1
  29. package/dist/consumers/streamSuccessScreen.d.ts.map +1 -1
  30. package/dist/{getParticipantMedia-c2bbdT5f.cjs → getParticipantMedia-1okyF4H7.cjs} +4 -4
  31. package/dist/{getParticipantMedia-c2bbdT5f.cjs.map → getParticipantMedia-1okyF4H7.cjs.map} +1 -1
  32. package/dist/{getParticipantMedia-DTZg8n5U.js → getParticipantMedia-DEJwXXhU.js} +4 -4
  33. package/dist/{getParticipantMedia-DTZg8n5U.js.map → getParticipantMedia-DEJwXXhU.js.map} +1 -1
  34. package/dist/index.cjs +36 -6
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.ts +4 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +1523 -1493
  39. package/dist/index.native.cjs +36 -6
  40. package/dist/index.native.cjs.map +1 -1
  41. package/dist/index.native.d.ts +4 -0
  42. package/dist/index.native.d.ts.map +1 -1
  43. package/dist/index.native.js +105 -75
  44. package/dist/{joinLocalRoom-1dRd2PCN.js → joinLocalRoom-DS6_6lJL.js} +6 -4
  45. package/dist/joinLocalRoom-DS6_6lJL.js.map +1 -0
  46. package/dist/{joinLocalRoom-DwY-3ZsH.cjs → joinLocalRoom-Dsn39SwS.cjs} +6 -4
  47. package/dist/joinLocalRoom-Dsn39SwS.cjs.map +1 -0
  48. package/dist/{joinRoomOnMediaSFU-BnAWLciF.js → joinRoomOnMediaSFU-B6tTA0Mx.js} +17 -13
  49. package/dist/joinRoomOnMediaSFU-B6tTA0Mx.js.map +1 -0
  50. package/dist/{joinRoomOnMediaSFU-D2C7q1GK.cjs → joinRoomOnMediaSFU-CxwD9fBz.cjs} +17 -13
  51. package/dist/joinRoomOnMediaSFU-CxwD9fBz.cjs.map +1 -0
  52. package/dist/methods/index.cjs +11 -8
  53. package/dist/methods/index.cjs.map +1 -1
  54. package/dist/methods/index.js +13 -9
  55. package/dist/methods/index.js.map +1 -1
  56. package/dist/methods/socketReceive/getDomains.d.ts.map +1 -1
  57. package/dist/methods/utils/checkLimitsAndMakeRequest.d.ts.map +1 -1
  58. package/dist/methods/welcome/handleWelcomeRequest.d.ts.map +1 -1
  59. package/dist/sockets/SocketManager.d.ts.map +1 -1
  60. package/dist/sockets/mediaSocketErrors.d.ts +62 -0
  61. package/dist/sockets/mediaSocketErrors.d.ts.map +1 -0
  62. package/dist/types/index.cjs +4 -3
  63. package/dist/types/index.cjs.map +1 -1
  64. package/dist/types/index.js +39 -38
  65. package/dist/types/shared-base-types.d.ts +4 -0
  66. package/dist/types/shared-base-types.d.ts.map +1 -1
  67. package/dist/{updateParticipantAudioDecibels-DiJ_LFfH.js → updateParticipantAudioDecibels-BBfdHWQ0.js} +344 -61
  68. package/dist/updateParticipantAudioDecibels-BBfdHWQ0.js.map +1 -0
  69. package/dist/{updateParticipantAudioDecibels-BJ2PxJJD.cjs → updateParticipantAudioDecibels-Dni8i6AY.cjs} +306 -23
  70. package/dist/updateParticipantAudioDecibels-Dni8i6AY.cjs.map +1 -0
  71. package/package.json +3 -2
  72. package/src/ProducerClient/producerClientEmits/updateRoomParametersClient.ts +4 -0
  73. package/src/consumers/closeAndResize.ts +14 -0
  74. package/src/consumers/connectIps.ts +81 -8
  75. package/src/consumers/connectSendTransportScreenAudio.ts +184 -0
  76. package/src/consumers/consumerBackupPreference.ts +43 -0
  77. package/src/consumers/disconnectSendTransportScreen.ts +8 -0
  78. package/src/consumers/getPipedProducersAlt.ts +4 -0
  79. package/src/consumers/index.ts +3 -0
  80. package/src/consumers/screenAudio.ts +132 -0
  81. package/src/consumers/socketReceiveMethods/producerClosed.ts +3 -0
  82. package/src/consumers/startShareScreen.ts +44 -8
  83. package/src/consumers/streamSuccessScreen.ts +11 -0
  84. package/src/index.native.ts +4 -0
  85. package/src/index.ts +4 -0
  86. package/src/methods/socketReceive/getDomains.ts +4 -0
  87. package/src/methods/utils/checkLimitsAndMakeRequest.ts +24 -11
  88. package/src/methods/welcome/handleWelcomeRequest.ts +15 -6
  89. package/src/sockets/SocketManager.ts +76 -12
  90. package/src/sockets/mediaSocketErrors.ts +156 -0
  91. package/src/types/shared-base-types.ts +4 -0
  92. package/dist/audioProcessing-0z5KlcmO.js +0 -35
  93. package/dist/audioProcessing-0z5KlcmO.js.map +0 -1
  94. package/dist/audioProcessing-BpbrdCk-.cjs +0 -34
  95. package/dist/audioProcessing-BpbrdCk-.cjs.map +0 -1
  96. package/dist/joinLocalRoom-1dRd2PCN.js.map +0 -1
  97. package/dist/joinLocalRoom-DwY-3ZsH.cjs.map +0 -1
  98. package/dist/joinRoomOnMediaSFU-BnAWLciF.js.map +0 -1
  99. package/dist/joinRoomOnMediaSFU-D2C7q1GK.cjs.map +0 -1
  100. package/dist/updateParticipantAudioDecibels-BJ2PxJJD.cjs.map +0 -1
  101. package/dist/updateParticipantAudioDecibels-DiJ_LFfH.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.3.0 — 2026-10-09
4
+
5
+ - **Screen-share audio.** When the media server supports it (`supportsScreenAudio` in the join response), the audio of a shared tab or screen is published as its own producer (`appData.mediaTag: "screen-audio"`) next to the screen video. It pauses, resumes and stops with the screen share, never replaces or mixes into the microphone, and never marks the sharer as speaking. New: `connectSendTransportScreenAudio`, `disconnectSendTransportScreenAudio`, `toggleScreenAudio`, `isScreenAudioProducer`, `canCaptureDisplayAudio`, `buildDisplayMediaOptions`, `setScreenAudioVolume`. `startShareScreen` accepts `supportsScreenAudio`, `screenAudioEnabled`, `screenAudioMode` (`ask` | `always` | `never`) and a `screenAudioPrompt` callback for the SDK's own "share audio too?" dialog. Capture works in Chromium desktop browsers; elsewhere screen sharing stays video-only. On servers without support nothing changes.
6
+ - **Listeners recognise screen audio** from `new-pipe-producer` and `getProducersPipedAlt` (`mediaTag`) and from the participant's `ScreenAudioID`, and forget it on `producer-closed`.
7
+ - **Consumer backup preference.** When a busy consuming node has a connected backup (`alt_domains`), a new stream announced by the busy node waits 1.5 s so the backup's own announcement can be used instead. Requests are never sent to a backup that has not announced the stream.
8
+ - **Clear connection errors.** `connectSocket` now rejects with a `MediaSocketConnectError` that carries the failure `phase` (`before-open`, `after-open`, `rejected`, `timeout`), elapsed time, the browser's online state, and the server's `connection-rejected` `reason` and `stage` (`invalid-credentials`, `meeting-minutes-unavailable`, `api-check-failed`, `invalid-request`, `rejected-before-connection-success`). `describeMediaSocketError` turns it into a sentence for end users, and `retryable` is false when the same credentials cannot succeed. Consume endpoints that refuse a room's credentials are not retried for 60 seconds.
9
+ - Events that arrive before a consume socket has joined its consuming room are queued and replayed after the join instead of being dropped.
10
+ - Join requests (`checkLimitsAndMakeRequest`, `checkLimitsAndMakeRequestWithStorage`, `handleWelcomeRequest`) no longer count network-only failures (offline, or the media server never reached) toward the 10-attempt limit; `isNetworkOnlyMediaSocketError` tells them apart.
11
+ - `closeAndResize` removes the closed producer's audio-only player (microphone or screen audio) from `audioOnlyStreams`, so a rejoin never leaves a second player behind.
12
+
3
13
  ## 1.2.7 — 2026-09-30
4
14
 
5
15
  - Disable camera simulcast automatically for chat and capacity-two rooms, with an explicit `simulcastOff` opt-out for larger rooms and an optional `roomCapacity` hint.
@@ -78,6 +78,8 @@ export interface UpdateRoomParametersClientParameters {
78
78
  updateRecordingPreferredOrientation: (orientation: string) => void;
79
79
  updateRecordingSupportForOtherOrientation: (support: boolean) => void;
80
80
  updateRecordingMultiFormatsSupport: (support: boolean) => void;
81
+ /** Records whether the media server accepts screen-share audio (join response `supportsScreenAudio`). */
82
+ updateSupportsScreenAudio?: (supported: boolean) => void;
81
83
  updateRecordingVideoOptions: (options: string) => void;
82
84
  updateRecordingAudioOptions: (options: string) => void;
83
85
  }
@@ -1 +1 @@
1
- {"version":3,"file":"updateRoomParametersClient.d.ts","sourceRoot":"","sources":["../../../src/ProducerClient/producerClientEmits/updateRoomParametersClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"updateRoomParametersClient.d.ts","sourceRoot":"","sources":["../../../src/ProducerClient/producerClientEmits/updateRoomParametersClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,150 @@
1
+ const MEDIA_CONNECTION_REJECTION_REASONS = [
2
+ "invalid-credentials",
3
+ "meeting-minutes-unavailable",
4
+ "api-check-failed",
5
+ "invalid-request",
6
+ "rejected-before-connection-success"
7
+ ];
8
+ const PERMANENT_REJECTIONS = /* @__PURE__ */ new Set(["invalid-credentials", "meeting-minutes-unavailable", "invalid-request"]);
9
+ class MediaSocketConnectError extends Error {
10
+ phase;
11
+ reason;
12
+ stage;
13
+ elapsedMs;
14
+ online;
15
+ transportError;
16
+ link;
17
+ /** False when retrying with the same credentials cannot succeed (bad credentials, no minutes, bad request). */
18
+ retryable;
19
+ constructor(details) {
20
+ super(formatMediaSocketError(details));
21
+ Object.setPrototypeOf(this, MediaSocketConnectError.prototype);
22
+ this.name = "MediaSocketConnectError";
23
+ this.phase = details.phase;
24
+ this.reason = details.reason;
25
+ this.stage = details.stage;
26
+ this.elapsedMs = details.elapsedMs;
27
+ this.online = details.online;
28
+ this.transportError = details.transportError;
29
+ this.link = details.link;
30
+ this.retryable = !(details.phase === "rejected" && details.reason && PERMANENT_REJECTIONS.has(details.reason));
31
+ }
32
+ }
33
+ const normalizeRejectionReason = (reason) => MEDIA_CONNECTION_REJECTION_REASONS.includes(String(reason)) ? reason : "rejected-before-connection-success";
34
+ const currentOnlineState = () => {
35
+ try {
36
+ const nav = globalThis.navigator;
37
+ return typeof nav?.onLine === "boolean" ? nav.onLine : void 0;
38
+ } catch {
39
+ return void 0;
40
+ }
41
+ };
42
+ function formatMediaSocketError(details) {
43
+ const facts = [`phase: ${details.phase}`, `after ${Math.max(0, Math.round(details.elapsedMs))} ms`];
44
+ if (details.online !== void 0) facts.push(`online: ${details.online}`);
45
+ if (details.stage) facts.push(`stage: ${details.stage}`);
46
+ const tail = ` (${facts.join(", ")})`;
47
+ switch (details.phase) {
48
+ case "rejected":
49
+ return `Media server rejected the connection: ${details.reason || "rejected-before-connection-success"}${tail}`;
50
+ case "timeout":
51
+ return `Timed out connecting to media socket${tail}`;
52
+ case "after-open":
53
+ return `Media socket closed before the room was ready: ${details.transportError || "unknown"}${tail}`;
54
+ default:
55
+ return `Error connecting to media socket: ${details.transportError || "unknown"}${tail}`;
56
+ }
57
+ }
58
+ const isMediaSocketConnectError = (error) => error instanceof MediaSocketConnectError || typeof error === "object" && error !== null && error.name === "MediaSocketConnectError";
59
+ const isNetworkOnlyMediaSocketError = (error) => isMediaSocketConnectError(error) && error.phase !== "rejected" && (error.online === false || error.phase === "before-open");
60
+ const isRetryableMediaSocketError = (error) => !isMediaSocketConnectError(error) || error.retryable;
61
+ function describeMediaSocketError(error, fallback = "Unable to connect. Check your credentials and try again.") {
62
+ if (!isMediaSocketConnectError(error)) return fallback;
63
+ if (error.phase === "rejected") {
64
+ switch (error.reason) {
65
+ case "invalid-credentials":
66
+ return "The room did not accept these credentials. Check the room ID and your access, or rejoin the room.";
67
+ case "meeting-minutes-unavailable":
68
+ return "This room cannot start because its account has no meeting minutes left. Contact the room host.";
69
+ case "invalid-request":
70
+ return "The connection request was incomplete or invalid. Check the room details and try again.";
71
+ case "api-check-failed":
72
+ return "The media server could not verify this room right now. Please try again shortly.";
73
+ default:
74
+ return "The media server closed the connection before the room was ready. Please try again.";
75
+ }
76
+ }
77
+ if (error.online === false) return "You appear to be offline. Check your internet connection and try again.";
78
+ if (error.phase === "before-open") {
79
+ return "Could not reach the media server. Check your network or firewall and try again.";
80
+ }
81
+ if (error.phase === "timeout") return "Connecting to the media server timed out. Please try again.";
82
+ return "The media server closed the connection before the room was ready. Please try again.";
83
+ }
84
+ const validateAlphanumeric = async ({ str }) => {
85
+ let code, i, len;
86
+ for (i = 0, len = str.length; i < len; i++) {
87
+ code = str.charCodeAt(i);
88
+ if (!(code > 47 && code < 58) && // numeric (0-9)
89
+ !(code > 64 && code < 91) && // upper alpha (A-Z)
90
+ !(code > 96 && code < 123)) {
91
+ return false;
92
+ }
93
+ }
94
+ return true;
95
+ };
96
+ const backupByParent = /* @__PURE__ */ new Map();
97
+ const CONSUMER_BACKUP_PREFER_DELAY_MS = 1500;
98
+ const rememberConsumerBackups = (altDomains) => {
99
+ for (const [parent, backup] of Object.entries(altDomains || {})) {
100
+ if (parent && backup && parent !== backup) backupByParent.set(parent, backup);
101
+ }
102
+ };
103
+ const forgetConsumerBackups = () => backupByParent.clear();
104
+ const preferredConsumerBackup = (ip, consumeSockets) => {
105
+ const backup = backupByParent.get(ip);
106
+ if (!backup) return null;
107
+ const connected = (consumeSockets || []).some((entry) => {
108
+ const socket = entry?.[backup];
109
+ return Boolean(socket) && socket.connected !== false;
110
+ });
111
+ return connected ? backup : null;
112
+ };
113
+ const consumerBackupPreferDelay = (ip, consumeSockets) => preferredConsumerBackup(ip, consumeSockets) ? CONSUMER_BACKUP_PREFER_DELAY_MS : 0;
114
+ function audioProcessingConstraints(options) {
115
+ const result = {};
116
+ if (options === void 0) return result;
117
+ if (!options || typeof options !== "object" || Array.isArray(options)) throw new TypeError("Invalid audioProcessing");
118
+ for (const key of Object.keys(options)) {
119
+ if (!["echoCancellation", "noiseSuppression", "autoGainControl"].includes(key)) throw new TypeError("Unsupported audioProcessing option");
120
+ const value = options[key];
121
+ if (value !== void 0 && typeof value !== "boolean") throw new TypeError("Audio processing options must be boolean");
122
+ if (value !== void 0) result[key] = value;
123
+ }
124
+ return result;
125
+ }
126
+ async function applyAudioProcessing(track, options) {
127
+ const selected = audioProcessingConstraints(options);
128
+ if (!Object.keys(selected).length) return;
129
+ await track.applyConstraints({ ...track.getConstraints(), ...selected });
130
+ }
131
+ export {
132
+ CONSUMER_BACKUP_PREFER_DELAY_MS as C,
133
+ MEDIA_CONNECTION_REJECTION_REASONS as M,
134
+ MediaSocketConnectError as a,
135
+ currentOnlineState as b,
136
+ consumerBackupPreferDelay as c,
137
+ formatMediaSocketError as d,
138
+ isNetworkOnlyMediaSocketError as e,
139
+ forgetConsumerBackups as f,
140
+ isRetryableMediaSocketError as g,
141
+ describeMediaSocketError as h,
142
+ isMediaSocketConnectError as i,
143
+ applyAudioProcessing as j,
144
+ audioProcessingConstraints as k,
145
+ normalizeRejectionReason as n,
146
+ preferredConsumerBackup as p,
147
+ rememberConsumerBackups as r,
148
+ validateAlphanumeric as v
149
+ };
150
+ //# sourceMappingURL=audioProcessing-Ca5wm1nV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audioProcessing-Ca5wm1nV.js","sources":["../src/sockets/mediaSocketErrors.ts","../src/methods/utils/validateAlphanumeric.ts","../src/consumers/consumerBackupPreference.ts","../src/consumers/audioProcessing.ts"],"sourcesContent":["/**\n * Media socket connection errors.\n *\n * A media node that refuses a handshake emits `connection-rejected { reason, stage }` just before it closes the socket\n * (media admission v2). `connectSocket` turns that, and every other way a connection can fail, into a\n * `MediaSocketConnectError` that says what happened, when and whether trying again can help.\n */\n\n/** Reasons a media node sends with `connection-rejected`. */\nexport type MediaConnectionRejectionReason =\n | 'invalid-credentials'\n | 'meeting-minutes-unavailable'\n | 'api-check-failed'\n | 'invalid-request'\n | 'rejected-before-connection-success';\n\n/**\n * Where the attempt stopped:\n * - `before-open`: the transport never opened (offline, DNS, TLS, firewall, server down);\n * - `after-open`: the transport opened but closed or errored before `connection-success`;\n * - `rejected`: the server refused the handshake and said why (`reason`);\n * - `timeout`: nothing settled in time.\n */\nexport type MediaSocketConnectPhase = 'before-open' | 'after-open' | 'rejected' | 'timeout';\n\nexport const MEDIA_CONNECTION_REJECTION_REASONS: readonly MediaConnectionRejectionReason[] = [\n 'invalid-credentials',\n 'meeting-minutes-unavailable',\n 'api-check-failed',\n 'invalid-request',\n 'rejected-before-connection-success',\n];\n\n/** Rejections that will fail the same way if retried with the same credentials. */\nconst PERMANENT_REJECTIONS = new Set<string>(['invalid-credentials', 'meeting-minutes-unavailable', 'invalid-request']);\n\nexport interface MediaSocketConnectErrorDetails {\n phase: MediaSocketConnectPhase;\n /** Rejection reason from the server (phase `rejected`), else undefined. */\n reason?: MediaConnectionRejectionReason;\n /** Server-side admission stage from `connection-rejected`, e.g. `api-check-rejected`. */\n stage?: string;\n /** Milliseconds from the start of the attempt to the failure. */\n elapsedMs: number;\n /** `navigator.onLine` when available. `false` points at the device's network, not the server. */\n online?: boolean;\n /** Underlying transport error or disconnect reason, e.g. `websocket error`, `io server disconnect`. */\n transportError?: string;\n /** Socket link the attempt targeted. */\n link?: string;\n}\n\nexport class MediaSocketConnectError extends Error implements MediaSocketConnectErrorDetails {\n readonly phase: MediaSocketConnectPhase;\n readonly reason?: MediaConnectionRejectionReason;\n readonly stage?: string;\n readonly elapsedMs: number;\n readonly online?: boolean;\n readonly transportError?: string;\n readonly link?: string;\n /** False when retrying with the same credentials cannot succeed (bad credentials, no minutes, bad request). */\n readonly retryable: boolean;\n\n constructor(details: MediaSocketConnectErrorDetails) {\n super(formatMediaSocketError(details));\n Object.setPrototypeOf(this, MediaSocketConnectError.prototype);\n this.name = 'MediaSocketConnectError';\n this.phase = details.phase;\n this.reason = details.reason;\n this.stage = details.stage;\n this.elapsedMs = details.elapsedMs;\n this.online = details.online;\n this.transportError = details.transportError;\n this.link = details.link;\n this.retryable = !(details.phase === 'rejected' && details.reason && PERMANENT_REJECTIONS.has(details.reason));\n }\n}\n\n/** Maps any reason string from the server onto a known reason (unknown values become `rejected-before-connection-success`). */\nexport const normalizeRejectionReason = (reason: unknown): MediaConnectionRejectionReason =>\n (MEDIA_CONNECTION_REJECTION_REASONS as readonly string[]).includes(String(reason))\n ? (reason as MediaConnectionRejectionReason)\n : 'rejected-before-connection-success';\n\n/** `navigator.onLine` where it exists (browsers, some React Native runtimes), else undefined. */\nexport const currentOnlineState = (): boolean | undefined => {\n try {\n const nav = (globalThis as { navigator?: { onLine?: unknown } }).navigator;\n return typeof nav?.onLine === 'boolean' ? nav.onLine : undefined;\n } catch {\n return undefined;\n }\n};\n\n/** Technical one-liner for logs; keeps the historic \"Error connecting to media socket:\" prefix for transport failures. */\nexport function formatMediaSocketError(details: MediaSocketConnectErrorDetails): string {\n const facts = [`phase: ${details.phase}`, `after ${Math.max(0, Math.round(details.elapsedMs))} ms`];\n if (details.online !== undefined) facts.push(`online: ${details.online}`);\n if (details.stage) facts.push(`stage: ${details.stage}`);\n const tail = ` (${facts.join(', ')})`;\n switch (details.phase) {\n case 'rejected':\n return `Media server rejected the connection: ${details.reason || 'rejected-before-connection-success'}${tail}`;\n case 'timeout':\n return `Timed out connecting to media socket${tail}`;\n case 'after-open':\n return `Media socket closed before the room was ready: ${details.transportError || 'unknown'}${tail}`;\n default:\n return `Error connecting to media socket: ${details.transportError || 'unknown'}${tail}`;\n }\n}\n\nexport const isMediaSocketConnectError = (error: unknown): error is MediaSocketConnectError =>\n error instanceof MediaSocketConnectError ||\n (typeof error === 'object' && error !== null && (error as { name?: unknown }).name === 'MediaSocketConnectError');\n\n/**\n * True when the attempt failed because of the device's network (offline, or the transport never opened) rather than\n * a server refusal. Join pages do not count these toward the attempt limit: they are not credential guesses.\n */\nexport const isNetworkOnlyMediaSocketError = (error: unknown): boolean =>\n isMediaSocketConnectError(error) &&\n error.phase !== 'rejected' &&\n (error.online === false || error.phase === 'before-open');\n\n/** False only for rejections that cannot succeed on retry with the same credentials. */\nexport const isRetryableMediaSocketError = (error: unknown): boolean =>\n !isMediaSocketConnectError(error) || error.retryable;\n\n/** A sentence suitable for an end user (alert/toast). Falls back to `fallback` for unknown errors. */\nexport function describeMediaSocketError(\n error: unknown,\n fallback = 'Unable to connect. Check your credentials and try again.',\n): string {\n if (!isMediaSocketConnectError(error)) return fallback;\n if (error.phase === 'rejected') {\n switch (error.reason) {\n case 'invalid-credentials':\n return 'The room did not accept these credentials. Check the room ID and your access, or rejoin the room.';\n case 'meeting-minutes-unavailable':\n return 'This room cannot start because its account has no meeting minutes left. Contact the room host.';\n case 'invalid-request':\n return 'The connection request was incomplete or invalid. Check the room details and try again.';\n case 'api-check-failed':\n return 'The media server could not verify this room right now. Please try again shortly.';\n default:\n return 'The media server closed the connection before the room was ready. Please try again.';\n }\n }\n if (error.online === false) return 'You appear to be offline. Check your internet connection and try again.';\n if (error.phase === 'before-open') {\n return 'Could not reach the media server. Check your network or firewall and try again.';\n }\n if (error.phase === 'timeout') return 'Connecting to the media server timed out. Please try again.';\n return 'The media server closed the connection before the room was ready. Please try again.';\n}\n","\nexport interface ValidateAlphanumericOptions {\n str: string;\n}\n\n// Export the type definition for the function\nexport type ValidateAlphanumericType = (options: ValidateAlphanumericOptions) => Promise<boolean>;\n\n/**\n * Validates if the given string contains only alphanumeric characters.\n *\n * @param {ValidateAlphanumericOptions} options - The options containing the string to validate.\n * @param {string} options.str - The string to be validated.\n * @returns {Promise<boolean>} - A promise that resolves to `true` if the string is alphanumeric, otherwise `false`.\n * \n * @example\n * ```typescript \n * const isValid = await validateAlphanumeric({ str: \"abc123\" });\n * console.log(isValid);\n * // Output: true\n * ```\n */\n\n\nconst validateAlphanumeric = async ({ str }: ValidateAlphanumericOptions): Promise<boolean> => {\n let code: number, i: number, len: number;\n\n for (i = 0, len = str.length; i < len; i++) {\n code = str.charCodeAt(i);\n if (\n !(code > 47 && code < 58) && // numeric (0-9)\n !(code > 64 && code < 91) && // upper alpha (A-Z)\n !(code > 96 && code < 123) // lower alpha (a-z)\n ) {\n return false;\n }\n }\n return true;\n};\n\nexport { validateAlphanumeric };\n","/**\n * Consumer backup preference (server feature consumer-backup-v1).\n *\n * When a consuming node gets busy, the server attaches a backup node and tells every listener\n * `alt_domains: { <busy node>: <backup node> }`. Listeners stay on the busy node for what they already receive, but a\n * stream announced after that is taken from the backup: when the busy node announces it and the backup is connected,\n * the client waits briefly for the backup's own announcement (the backup gets each new stream a moment later) and only\n * falls back to the busy node if it does not come. Consumption is de-duplicated by producer id, so whichever node is\n * used first wins and the other announcement is ignored.\n *\n * The request is never sent to the backup for a stream the backup has not announced: a consume the backup cannot serve\n * fails without an error and the de-duplication would then skip the busy node's copy (staging test, Oct 9 2026).\n */\nconst backupByParent = new Map<string, string>();\n\n/** How long a busy node's announcement waits for the backup's (ms). */\nexport const CONSUMER_BACKUP_PREFER_DELAY_MS = 1500;\n\n/** Records the server's `{ busy node: backup node }` map (from `updateConsumingDomains` / `getDomains`). */\nexport const rememberConsumerBackups = (altDomains?: Record<string, string> | null): void => {\n for (const [parent, backup] of Object.entries(altDomains || {})) {\n if (parent && backup && parent !== backup) backupByParent.set(parent, backup);\n }\n};\n\nexport const forgetConsumerBackups = (): void => backupByParent.clear();\n\ntype SocketEntry = Record<string, { connected?: boolean } | undefined>;\n\n/** The connected backup to prefer over `ip`, or null. */\nexport const preferredConsumerBackup = (ip: string, consumeSockets?: SocketEntry[] | null): string | null => {\n const backup = backupByParent.get(ip);\n if (!backup) return null;\n const connected = (consumeSockets || []).some((entry) => {\n const socket = entry?.[backup];\n return Boolean(socket) && socket!.connected !== false;\n });\n return connected ? backup : null;\n};\n\n/** Delay (ms) before consuming a stream announced by `ip`: non-zero only when a connected backup should win. */\nexport const consumerBackupPreferDelay = (ip: string, consumeSockets?: SocketEntry[] | null): number =>\n preferredConsumerBackup(ip, consumeSockets) ? CONSUMER_BACKUP_PREFER_DELAY_MS : 0;\n","export type AudioProcessingOptions = {\n echoCancellation?: boolean;\n noiseSuppression?: boolean;\n autoGainControl?: boolean;\n};\n\nexport function audioProcessingConstraints(options?: AudioProcessingOptions): MediaTrackConstraints {\n const result: MediaTrackConstraints = {};\n if (options === undefined) return result;\n if (!options || typeof options !== 'object' || Array.isArray(options)) throw new TypeError('Invalid audioProcessing');\n for (const key of Object.keys(options)) {\n if (!['echoCancellation', 'noiseSuppression', 'autoGainControl'].includes(key)) throw new TypeError('Unsupported audioProcessing option');\n const value = options[key as keyof AudioProcessingOptions];\n if (value !== undefined && typeof value !== 'boolean') throw new TypeError('Audio processing options must be boolean');\n if (value !== undefined) result[key as keyof AudioProcessingOptions] = value;\n }\n return result;\n}\n\n// applyConstraints replaces the constraint set: merge to retain device and\n// capture choices unrelated to the three processing preferences.\nexport async function applyAudioProcessing(track: MediaStreamTrack, options?: AudioProcessingOptions): Promise<void> {\n const selected = audioProcessingConstraints(options);\n if (!Object.keys(selected).length) return;\n await track.applyConstraints({ ...track.getConstraints(), ...selected });\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ const MEDIA_CONNECTION_REJECTION_REASONS = [
3
+ "invalid-credentials",
4
+ "meeting-minutes-unavailable",
5
+ "api-check-failed",
6
+ "invalid-request",
7
+ "rejected-before-connection-success"
8
+ ];
9
+ const PERMANENT_REJECTIONS = /* @__PURE__ */ new Set(["invalid-credentials", "meeting-minutes-unavailable", "invalid-request"]);
10
+ class MediaSocketConnectError extends Error {
11
+ phase;
12
+ reason;
13
+ stage;
14
+ elapsedMs;
15
+ online;
16
+ transportError;
17
+ link;
18
+ /** False when retrying with the same credentials cannot succeed (bad credentials, no minutes, bad request). */
19
+ retryable;
20
+ constructor(details) {
21
+ super(formatMediaSocketError(details));
22
+ Object.setPrototypeOf(this, MediaSocketConnectError.prototype);
23
+ this.name = "MediaSocketConnectError";
24
+ this.phase = details.phase;
25
+ this.reason = details.reason;
26
+ this.stage = details.stage;
27
+ this.elapsedMs = details.elapsedMs;
28
+ this.online = details.online;
29
+ this.transportError = details.transportError;
30
+ this.link = details.link;
31
+ this.retryable = !(details.phase === "rejected" && details.reason && PERMANENT_REJECTIONS.has(details.reason));
32
+ }
33
+ }
34
+ const normalizeRejectionReason = (reason) => MEDIA_CONNECTION_REJECTION_REASONS.includes(String(reason)) ? reason : "rejected-before-connection-success";
35
+ const currentOnlineState = () => {
36
+ try {
37
+ const nav = globalThis.navigator;
38
+ return typeof nav?.onLine === "boolean" ? nav.onLine : void 0;
39
+ } catch {
40
+ return void 0;
41
+ }
42
+ };
43
+ function formatMediaSocketError(details) {
44
+ const facts = [`phase: ${details.phase}`, `after ${Math.max(0, Math.round(details.elapsedMs))} ms`];
45
+ if (details.online !== void 0) facts.push(`online: ${details.online}`);
46
+ if (details.stage) facts.push(`stage: ${details.stage}`);
47
+ const tail = ` (${facts.join(", ")})`;
48
+ switch (details.phase) {
49
+ case "rejected":
50
+ return `Media server rejected the connection: ${details.reason || "rejected-before-connection-success"}${tail}`;
51
+ case "timeout":
52
+ return `Timed out connecting to media socket${tail}`;
53
+ case "after-open":
54
+ return `Media socket closed before the room was ready: ${details.transportError || "unknown"}${tail}`;
55
+ default:
56
+ return `Error connecting to media socket: ${details.transportError || "unknown"}${tail}`;
57
+ }
58
+ }
59
+ const isMediaSocketConnectError = (error) => error instanceof MediaSocketConnectError || typeof error === "object" && error !== null && error.name === "MediaSocketConnectError";
60
+ const isNetworkOnlyMediaSocketError = (error) => isMediaSocketConnectError(error) && error.phase !== "rejected" && (error.online === false || error.phase === "before-open");
61
+ const isRetryableMediaSocketError = (error) => !isMediaSocketConnectError(error) || error.retryable;
62
+ function describeMediaSocketError(error, fallback = "Unable to connect. Check your credentials and try again.") {
63
+ if (!isMediaSocketConnectError(error)) return fallback;
64
+ if (error.phase === "rejected") {
65
+ switch (error.reason) {
66
+ case "invalid-credentials":
67
+ return "The room did not accept these credentials. Check the room ID and your access, or rejoin the room.";
68
+ case "meeting-minutes-unavailable":
69
+ return "This room cannot start because its account has no meeting minutes left. Contact the room host.";
70
+ case "invalid-request":
71
+ return "The connection request was incomplete or invalid. Check the room details and try again.";
72
+ case "api-check-failed":
73
+ return "The media server could not verify this room right now. Please try again shortly.";
74
+ default:
75
+ return "The media server closed the connection before the room was ready. Please try again.";
76
+ }
77
+ }
78
+ if (error.online === false) return "You appear to be offline. Check your internet connection and try again.";
79
+ if (error.phase === "before-open") {
80
+ return "Could not reach the media server. Check your network or firewall and try again.";
81
+ }
82
+ if (error.phase === "timeout") return "Connecting to the media server timed out. Please try again.";
83
+ return "The media server closed the connection before the room was ready. Please try again.";
84
+ }
85
+ const validateAlphanumeric = async ({ str }) => {
86
+ let code, i, len;
87
+ for (i = 0, len = str.length; i < len; i++) {
88
+ code = str.charCodeAt(i);
89
+ if (!(code > 47 && code < 58) && // numeric (0-9)
90
+ !(code > 64 && code < 91) && // upper alpha (A-Z)
91
+ !(code > 96 && code < 123)) {
92
+ return false;
93
+ }
94
+ }
95
+ return true;
96
+ };
97
+ const backupByParent = /* @__PURE__ */ new Map();
98
+ const CONSUMER_BACKUP_PREFER_DELAY_MS = 1500;
99
+ const rememberConsumerBackups = (altDomains) => {
100
+ for (const [parent, backup] of Object.entries(altDomains || {})) {
101
+ if (parent && backup && parent !== backup) backupByParent.set(parent, backup);
102
+ }
103
+ };
104
+ const forgetConsumerBackups = () => backupByParent.clear();
105
+ const preferredConsumerBackup = (ip, consumeSockets) => {
106
+ const backup = backupByParent.get(ip);
107
+ if (!backup) return null;
108
+ const connected = (consumeSockets || []).some((entry) => {
109
+ const socket = entry?.[backup];
110
+ return Boolean(socket) && socket.connected !== false;
111
+ });
112
+ return connected ? backup : null;
113
+ };
114
+ const consumerBackupPreferDelay = (ip, consumeSockets) => preferredConsumerBackup(ip, consumeSockets) ? CONSUMER_BACKUP_PREFER_DELAY_MS : 0;
115
+ function audioProcessingConstraints(options) {
116
+ const result = {};
117
+ if (options === void 0) return result;
118
+ if (!options || typeof options !== "object" || Array.isArray(options)) throw new TypeError("Invalid audioProcessing");
119
+ for (const key of Object.keys(options)) {
120
+ if (!["echoCancellation", "noiseSuppression", "autoGainControl"].includes(key)) throw new TypeError("Unsupported audioProcessing option");
121
+ const value = options[key];
122
+ if (value !== void 0 && typeof value !== "boolean") throw new TypeError("Audio processing options must be boolean");
123
+ if (value !== void 0) result[key] = value;
124
+ }
125
+ return result;
126
+ }
127
+ async function applyAudioProcessing(track, options) {
128
+ const selected = audioProcessingConstraints(options);
129
+ if (!Object.keys(selected).length) return;
130
+ await track.applyConstraints({ ...track.getConstraints(), ...selected });
131
+ }
132
+ exports.CONSUMER_BACKUP_PREFER_DELAY_MS = CONSUMER_BACKUP_PREFER_DELAY_MS;
133
+ exports.MEDIA_CONNECTION_REJECTION_REASONS = MEDIA_CONNECTION_REJECTION_REASONS;
134
+ exports.MediaSocketConnectError = MediaSocketConnectError;
135
+ exports.applyAudioProcessing = applyAudioProcessing;
136
+ exports.audioProcessingConstraints = audioProcessingConstraints;
137
+ exports.consumerBackupPreferDelay = consumerBackupPreferDelay;
138
+ exports.currentOnlineState = currentOnlineState;
139
+ exports.describeMediaSocketError = describeMediaSocketError;
140
+ exports.forgetConsumerBackups = forgetConsumerBackups;
141
+ exports.formatMediaSocketError = formatMediaSocketError;
142
+ exports.isMediaSocketConnectError = isMediaSocketConnectError;
143
+ exports.isNetworkOnlyMediaSocketError = isNetworkOnlyMediaSocketError;
144
+ exports.isRetryableMediaSocketError = isRetryableMediaSocketError;
145
+ exports.normalizeRejectionReason = normalizeRejectionReason;
146
+ exports.preferredConsumerBackup = preferredConsumerBackup;
147
+ exports.rememberConsumerBackups = rememberConsumerBackups;
148
+ exports.validateAlphanumeric = validateAlphanumeric;
149
+ //# sourceMappingURL=audioProcessing-VCbu70Jt.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audioProcessing-VCbu70Jt.cjs","sources":["../src/sockets/mediaSocketErrors.ts","../src/methods/utils/validateAlphanumeric.ts","../src/consumers/consumerBackupPreference.ts","../src/consumers/audioProcessing.ts"],"sourcesContent":["/**\n * Media socket connection errors.\n *\n * A media node that refuses a handshake emits `connection-rejected { reason, stage }` just before it closes the socket\n * (media admission v2). `connectSocket` turns that, and every other way a connection can fail, into a\n * `MediaSocketConnectError` that says what happened, when and whether trying again can help.\n */\n\n/** Reasons a media node sends with `connection-rejected`. */\nexport type MediaConnectionRejectionReason =\n | 'invalid-credentials'\n | 'meeting-minutes-unavailable'\n | 'api-check-failed'\n | 'invalid-request'\n | 'rejected-before-connection-success';\n\n/**\n * Where the attempt stopped:\n * - `before-open`: the transport never opened (offline, DNS, TLS, firewall, server down);\n * - `after-open`: the transport opened but closed or errored before `connection-success`;\n * - `rejected`: the server refused the handshake and said why (`reason`);\n * - `timeout`: nothing settled in time.\n */\nexport type MediaSocketConnectPhase = 'before-open' | 'after-open' | 'rejected' | 'timeout';\n\nexport const MEDIA_CONNECTION_REJECTION_REASONS: readonly MediaConnectionRejectionReason[] = [\n 'invalid-credentials',\n 'meeting-minutes-unavailable',\n 'api-check-failed',\n 'invalid-request',\n 'rejected-before-connection-success',\n];\n\n/** Rejections that will fail the same way if retried with the same credentials. */\nconst PERMANENT_REJECTIONS = new Set<string>(['invalid-credentials', 'meeting-minutes-unavailable', 'invalid-request']);\n\nexport interface MediaSocketConnectErrorDetails {\n phase: MediaSocketConnectPhase;\n /** Rejection reason from the server (phase `rejected`), else undefined. */\n reason?: MediaConnectionRejectionReason;\n /** Server-side admission stage from `connection-rejected`, e.g. `api-check-rejected`. */\n stage?: string;\n /** Milliseconds from the start of the attempt to the failure. */\n elapsedMs: number;\n /** `navigator.onLine` when available. `false` points at the device's network, not the server. */\n online?: boolean;\n /** Underlying transport error or disconnect reason, e.g. `websocket error`, `io server disconnect`. */\n transportError?: string;\n /** Socket link the attempt targeted. */\n link?: string;\n}\n\nexport class MediaSocketConnectError extends Error implements MediaSocketConnectErrorDetails {\n readonly phase: MediaSocketConnectPhase;\n readonly reason?: MediaConnectionRejectionReason;\n readonly stage?: string;\n readonly elapsedMs: number;\n readonly online?: boolean;\n readonly transportError?: string;\n readonly link?: string;\n /** False when retrying with the same credentials cannot succeed (bad credentials, no minutes, bad request). */\n readonly retryable: boolean;\n\n constructor(details: MediaSocketConnectErrorDetails) {\n super(formatMediaSocketError(details));\n Object.setPrototypeOf(this, MediaSocketConnectError.prototype);\n this.name = 'MediaSocketConnectError';\n this.phase = details.phase;\n this.reason = details.reason;\n this.stage = details.stage;\n this.elapsedMs = details.elapsedMs;\n this.online = details.online;\n this.transportError = details.transportError;\n this.link = details.link;\n this.retryable = !(details.phase === 'rejected' && details.reason && PERMANENT_REJECTIONS.has(details.reason));\n }\n}\n\n/** Maps any reason string from the server onto a known reason (unknown values become `rejected-before-connection-success`). */\nexport const normalizeRejectionReason = (reason: unknown): MediaConnectionRejectionReason =>\n (MEDIA_CONNECTION_REJECTION_REASONS as readonly string[]).includes(String(reason))\n ? (reason as MediaConnectionRejectionReason)\n : 'rejected-before-connection-success';\n\n/** `navigator.onLine` where it exists (browsers, some React Native runtimes), else undefined. */\nexport const currentOnlineState = (): boolean | undefined => {\n try {\n const nav = (globalThis as { navigator?: { onLine?: unknown } }).navigator;\n return typeof nav?.onLine === 'boolean' ? nav.onLine : undefined;\n } catch {\n return undefined;\n }\n};\n\n/** Technical one-liner for logs; keeps the historic \"Error connecting to media socket:\" prefix for transport failures. */\nexport function formatMediaSocketError(details: MediaSocketConnectErrorDetails): string {\n const facts = [`phase: ${details.phase}`, `after ${Math.max(0, Math.round(details.elapsedMs))} ms`];\n if (details.online !== undefined) facts.push(`online: ${details.online}`);\n if (details.stage) facts.push(`stage: ${details.stage}`);\n const tail = ` (${facts.join(', ')})`;\n switch (details.phase) {\n case 'rejected':\n return `Media server rejected the connection: ${details.reason || 'rejected-before-connection-success'}${tail}`;\n case 'timeout':\n return `Timed out connecting to media socket${tail}`;\n case 'after-open':\n return `Media socket closed before the room was ready: ${details.transportError || 'unknown'}${tail}`;\n default:\n return `Error connecting to media socket: ${details.transportError || 'unknown'}${tail}`;\n }\n}\n\nexport const isMediaSocketConnectError = (error: unknown): error is MediaSocketConnectError =>\n error instanceof MediaSocketConnectError ||\n (typeof error === 'object' && error !== null && (error as { name?: unknown }).name === 'MediaSocketConnectError');\n\n/**\n * True when the attempt failed because of the device's network (offline, or the transport never opened) rather than\n * a server refusal. Join pages do not count these toward the attempt limit: they are not credential guesses.\n */\nexport const isNetworkOnlyMediaSocketError = (error: unknown): boolean =>\n isMediaSocketConnectError(error) &&\n error.phase !== 'rejected' &&\n (error.online === false || error.phase === 'before-open');\n\n/** False only for rejections that cannot succeed on retry with the same credentials. */\nexport const isRetryableMediaSocketError = (error: unknown): boolean =>\n !isMediaSocketConnectError(error) || error.retryable;\n\n/** A sentence suitable for an end user (alert/toast). Falls back to `fallback` for unknown errors. */\nexport function describeMediaSocketError(\n error: unknown,\n fallback = 'Unable to connect. Check your credentials and try again.',\n): string {\n if (!isMediaSocketConnectError(error)) return fallback;\n if (error.phase === 'rejected') {\n switch (error.reason) {\n case 'invalid-credentials':\n return 'The room did not accept these credentials. Check the room ID and your access, or rejoin the room.';\n case 'meeting-minutes-unavailable':\n return 'This room cannot start because its account has no meeting minutes left. Contact the room host.';\n case 'invalid-request':\n return 'The connection request was incomplete or invalid. Check the room details and try again.';\n case 'api-check-failed':\n return 'The media server could not verify this room right now. Please try again shortly.';\n default:\n return 'The media server closed the connection before the room was ready. Please try again.';\n }\n }\n if (error.online === false) return 'You appear to be offline. Check your internet connection and try again.';\n if (error.phase === 'before-open') {\n return 'Could not reach the media server. Check your network or firewall and try again.';\n }\n if (error.phase === 'timeout') return 'Connecting to the media server timed out. Please try again.';\n return 'The media server closed the connection before the room was ready. Please try again.';\n}\n","\nexport interface ValidateAlphanumericOptions {\n str: string;\n}\n\n// Export the type definition for the function\nexport type ValidateAlphanumericType = (options: ValidateAlphanumericOptions) => Promise<boolean>;\n\n/**\n * Validates if the given string contains only alphanumeric characters.\n *\n * @param {ValidateAlphanumericOptions} options - The options containing the string to validate.\n * @param {string} options.str - The string to be validated.\n * @returns {Promise<boolean>} - A promise that resolves to `true` if the string is alphanumeric, otherwise `false`.\n * \n * @example\n * ```typescript \n * const isValid = await validateAlphanumeric({ str: \"abc123\" });\n * console.log(isValid);\n * // Output: true\n * ```\n */\n\n\nconst validateAlphanumeric = async ({ str }: ValidateAlphanumericOptions): Promise<boolean> => {\n let code: number, i: number, len: number;\n\n for (i = 0, len = str.length; i < len; i++) {\n code = str.charCodeAt(i);\n if (\n !(code > 47 && code < 58) && // numeric (0-9)\n !(code > 64 && code < 91) && // upper alpha (A-Z)\n !(code > 96 && code < 123) // lower alpha (a-z)\n ) {\n return false;\n }\n }\n return true;\n};\n\nexport { validateAlphanumeric };\n","/**\n * Consumer backup preference (server feature consumer-backup-v1).\n *\n * When a consuming node gets busy, the server attaches a backup node and tells every listener\n * `alt_domains: { <busy node>: <backup node> }`. Listeners stay on the busy node for what they already receive, but a\n * stream announced after that is taken from the backup: when the busy node announces it and the backup is connected,\n * the client waits briefly for the backup's own announcement (the backup gets each new stream a moment later) and only\n * falls back to the busy node if it does not come. Consumption is de-duplicated by producer id, so whichever node is\n * used first wins and the other announcement is ignored.\n *\n * The request is never sent to the backup for a stream the backup has not announced: a consume the backup cannot serve\n * fails without an error and the de-duplication would then skip the busy node's copy (staging test, Oct 9 2026).\n */\nconst backupByParent = new Map<string, string>();\n\n/** How long a busy node's announcement waits for the backup's (ms). */\nexport const CONSUMER_BACKUP_PREFER_DELAY_MS = 1500;\n\n/** Records the server's `{ busy node: backup node }` map (from `updateConsumingDomains` / `getDomains`). */\nexport const rememberConsumerBackups = (altDomains?: Record<string, string> | null): void => {\n for (const [parent, backup] of Object.entries(altDomains || {})) {\n if (parent && backup && parent !== backup) backupByParent.set(parent, backup);\n }\n};\n\nexport const forgetConsumerBackups = (): void => backupByParent.clear();\n\ntype SocketEntry = Record<string, { connected?: boolean } | undefined>;\n\n/** The connected backup to prefer over `ip`, or null. */\nexport const preferredConsumerBackup = (ip: string, consumeSockets?: SocketEntry[] | null): string | null => {\n const backup = backupByParent.get(ip);\n if (!backup) return null;\n const connected = (consumeSockets || []).some((entry) => {\n const socket = entry?.[backup];\n return Boolean(socket) && socket!.connected !== false;\n });\n return connected ? backup : null;\n};\n\n/** Delay (ms) before consuming a stream announced by `ip`: non-zero only when a connected backup should win. */\nexport const consumerBackupPreferDelay = (ip: string, consumeSockets?: SocketEntry[] | null): number =>\n preferredConsumerBackup(ip, consumeSockets) ? CONSUMER_BACKUP_PREFER_DELAY_MS : 0;\n","export type AudioProcessingOptions = {\n echoCancellation?: boolean;\n noiseSuppression?: boolean;\n autoGainControl?: boolean;\n};\n\nexport function audioProcessingConstraints(options?: AudioProcessingOptions): MediaTrackConstraints {\n const result: MediaTrackConstraints = {};\n if (options === undefined) return result;\n if (!options || typeof options !== 'object' || Array.isArray(options)) throw new TypeError('Invalid audioProcessing');\n for (const key of Object.keys(options)) {\n if (!['echoCancellation', 'noiseSuppression', 'autoGainControl'].includes(key)) throw new TypeError('Unsupported audioProcessing option');\n const value = options[key as keyof AudioProcessingOptions];\n if (value !== undefined && typeof value !== 'boolean') throw new TypeError('Audio processing options must be boolean');\n if (value !== undefined) result[key as keyof AudioProcessingOptions] = value;\n }\n return result;\n}\n\n// applyConstraints replaces the constraint set: merge to retain device and\n// capture choices unrelated to the three processing preferences.\nexport async function applyAudioProcessing(track: MediaStreamTrack, options?: AudioProcessingOptions): Promise<void> {\n const selected = audioProcessingConstraints(options);\n if (!Object.keys(selected).length) return;\n await track.applyConstraints({ ...track.getConstraints(), ...selected });\n}\n"],"names":[],"mappings":";AAyBO,MAAM,qCAAgF;AAAA,EAC3F;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGA,MAAM,uBAAuB,oBAAI,IAAY,CAAC,uBAAuB,+BAA+B,iBAAiB,CAAC;AAkB/G,MAAM,gCAAgC,MAAgD;AAAA,EAClF;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EAET,YAAY,SAAyC;AACnD,UAAM,uBAAuB,OAAO,CAAC;AACrC,WAAO,eAAe,MAAM,wBAAwB,SAAS;AAC7D,SAAK,OAAO;AACZ,SAAK,QAAQ,QAAQ;AACrB,SAAK,SAAS,QAAQ;AACtB,SAAK,QAAQ,QAAQ;AACrB,SAAK,YAAY,QAAQ;AACzB,SAAK,SAAS,QAAQ;AACtB,SAAK,iBAAiB,QAAQ;AAC9B,SAAK,OAAO,QAAQ;AACpB,SAAK,YAAY,EAAE,QAAQ,UAAU,cAAc,QAAQ,UAAU,qBAAqB,IAAI,QAAQ,MAAM;AAAA,EAC9G;AACF;AAGO,MAAM,2BAA2B,CAAC,WACtC,mCAAyD,SAAS,OAAO,MAAM,CAAC,IAC5E,SACD;AAGC,MAAM,qBAAqB,MAA2B;AAC3D,MAAI;AACF,UAAM,MAAO,WAAoD;AACjE,WAAO,OAAO,KAAK,WAAW,YAAY,IAAI,SAAS;AAAA,EACzD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,uBAAuB,SAAiD;AACtF,QAAM,QAAQ,CAAC,UAAU,QAAQ,KAAK,IAAI,SAAS,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,SAAS,CAAC,CAAC,KAAK;AAClG,MAAI,QAAQ,WAAW,OAAW,OAAM,KAAK,WAAW,QAAQ,MAAM,EAAE;AACxE,MAAI,QAAQ,MAAO,OAAM,KAAK,UAAU,QAAQ,KAAK,EAAE;AACvD,QAAM,OAAO,KAAK,MAAM,KAAK,IAAI,CAAC;AAClC,UAAQ,QAAQ,OAAA;AAAA,IACd,KAAK;AACH,aAAO,yCAAyC,QAAQ,UAAU,oCAAoC,GAAG,IAAI;AAAA,IAC/G,KAAK;AACH,aAAO,uCAAuC,IAAI;AAAA,IACpD,KAAK;AACH,aAAO,kDAAkD,QAAQ,kBAAkB,SAAS,GAAG,IAAI;AAAA,IACrG;AACE,aAAO,qCAAqC,QAAQ,kBAAkB,SAAS,GAAG,IAAI;AAAA,EAAA;AAE5F;AAEO,MAAM,4BAA4B,CAAC,UACxC,iBAAiB,2BAChB,OAAO,UAAU,YAAY,UAAU,QAAS,MAA6B,SAAS;AAMlF,MAAM,gCAAgC,CAAC,UAC5C,0BAA0B,KAAK,KAC/B,MAAM,UAAU,eACf,MAAM,WAAW,SAAS,MAAM,UAAU;AAGtC,MAAM,8BAA8B,CAAC,UAC1C,CAAC,0BAA0B,KAAK,KAAK,MAAM;AAGtC,SAAS,yBACd,OACA,WAAW,4DACH;AACR,MAAI,CAAC,0BAA0B,KAAK,EAAG,QAAO;AAC9C,MAAI,MAAM,UAAU,YAAY;AAC9B,YAAQ,MAAM,QAAA;AAAA,MACZ,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IAAA;AAAA,EAEb;AACA,MAAI,MAAM,WAAW,MAAO,QAAO;AACnC,MAAI,MAAM,UAAU,eAAe;AACjC,WAAO;AAAA,EACT;AACA,MAAI,MAAM,UAAU,UAAW,QAAO;AACtC,SAAO;AACT;ACnIA,MAAM,uBAAuB,OAAO,EAAE,UAAyD;AAC7F,MAAI,MAAc,GAAW;AAE7B,OAAK,IAAI,GAAG,MAAM,IAAI,QAAQ,IAAI,KAAK,KAAK;AAC1C,WAAO,IAAI,WAAW,CAAC;AACvB,QACE,EAAE,OAAO,MAAM,OAAO;AAAA,IACtB,EAAE,OAAO,MAAM,OAAO;AAAA,IACtB,EAAE,OAAO,MAAM,OAAO,MACtB;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;ACzBA,MAAM,qCAAqB,IAAA;AAGpB,MAAM,kCAAkC;AAGxC,MAAM,0BAA0B,CAAC,eAAqD;AAC3F,aAAW,CAAC,QAAQ,MAAM,KAAK,OAAO,QAAQ,cAAc,CAAA,CAAE,GAAG;AAC/D,QAAI,UAAU,UAAU,WAAW,OAAQ,gBAAe,IAAI,QAAQ,MAAM;AAAA,EAC9E;AACF;AAEO,MAAM,wBAAwB,MAAY,eAAe,MAAA;AAKzD,MAAM,0BAA0B,CAAC,IAAY,mBAAyD;AAC3G,QAAM,SAAS,eAAe,IAAI,EAAE;AACpC,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,aAAa,kBAAkB,CAAA,GAAI,KAAK,CAAC,UAAU;AACvD,UAAM,SAAS,QAAQ,MAAM;AAC7B,WAAO,QAAQ,MAAM,KAAK,OAAQ,cAAc;AAAA,EAClD,CAAC;AACD,SAAO,YAAY,SAAS;AAC9B;AAGO,MAAM,4BAA4B,CAAC,IAAY,mBACpD,wBAAwB,IAAI,cAAc,IAAI,kCAAkC;ACpC3E,SAAS,2BAA2B,SAAyD;AAClG,QAAM,SAAgC,CAAA;AACtC,MAAI,YAAY,OAAW,QAAO;AAClC,MAAI,CAAC,WAAW,OAAO,YAAY,YAAY,MAAM,QAAQ,OAAO,EAAG,OAAM,IAAI,UAAU,yBAAyB;AACpH,aAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,QAAI,CAAC,CAAC,oBAAoB,oBAAoB,iBAAiB,EAAE,SAAS,GAAG,EAAG,OAAM,IAAI,UAAU,oCAAoC;AACxI,UAAM,QAAQ,QAAQ,GAAmC;AACzD,QAAI,UAAU,UAAa,OAAO,UAAU,UAAW,OAAM,IAAI,UAAU,0CAA0C;AACrH,QAAI,UAAU,OAAW,QAAO,GAAmC,IAAI;AAAA,EACzE;AACA,SAAO;AACT;AAIA,eAAsB,qBAAqB,OAAyB,SAAiD;AACnH,QAAM,WAAW,2BAA2B,OAAO;AACnD,MAAI,CAAC,OAAO,KAAK,QAAQ,EAAE,OAAQ;AACnC,QAAM,MAAM,iBAAiB,EAAE,GAAG,MAAM,eAAA,GAAkB,GAAG,UAAU;AACzE;;;;;;;;;;;;;;;;;;"}
@@ -1,6 +1,8 @@
1
1
  import { Stream, Participant, ReorderStreamsType, ReorderStreamsParameters, PrepopulateUserMediaParameters, PrepopulateUserMediaType, GetVideosType, RePortType, RePortParameters, EventType } from '../types/types';
2
2
  export interface CloseAndResizeParameters extends ReorderStreamsParameters, PrepopulateUserMediaParameters, RePortParameters {
3
3
  allAudioStreams: (Stream | Participant)[];
4
+ /** Rendered audio-only players (React elements, Vue/Angular component descriptors). */
5
+ audioOnlyStreams?: any[];
4
6
  allVideoStreams: (Stream | Participant)[];
5
7
  activeNames: string[];
6
8
  participants: Participant[];
@@ -25,6 +27,7 @@ export interface CloseAndResizeParameters extends ReorderStreamsParameters, Prep
25
27
  updateMainWindow: boolean;
26
28
  updateActiveNames: (activeNames: string[]) => void;
27
29
  updateAllAudioStreams: (allAudioStreams: (Stream | Participant)[]) => void;
30
+ updateAudioOnlyStreams?: (audioOnlyStreams: any[]) => void;
28
31
  updateShareScreenStarted: (shareScreenStarted: boolean) => void;
29
32
  updateUpdateMainWindow: (updateMainWindow: boolean) => void;
30
33
  updateNewLimitedStreams: (newLimitedStreams: (Stream | Participant)[]) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"closeAndResize.d.ts","sourceRoot":"","sources":["../../src/consumers/closeAndResize.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EAAE,WAAW,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,8BAA8B,EACjG,wBAAwB,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,SAAS,EACjF,MAAM,gBAAgB,CAAC;AAKxB,MAAM,WAAW,wBAAyB,SAAQ,wBAAwB,EAAE,8BAA8B,EAAE,gBAAgB;IAC1H,eAAe,EAAE,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC;IAC1C,eAAe,EAAE,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC;IAC1C,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,oBAAoB,EAAE,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;IAChD,uBAAuB,EAAE,OAAO,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC;IAC5C,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,aAAa,EAAE,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC;IACxC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACnD,qBAAqB,EAAE,CAAC,eAAe,EAAE,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,KAAK,IAAI,CAAC;IAC3E,wBAAwB,EAAE,CAAC,kBAAkB,EAAE,OAAO,KAAK,IAAI,CAAC;IAChE,sBAAsB,EAAE,CAAC,gBAAgB,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5D,uBAAuB,EAAE,CAAC,iBAAiB,EAAE,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/E,mBAAmB,EAAE,CAAC,aAAa,EAAE,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,KAAK,IAAI,CAAC;IACvE,mBAAmB,EAAE,CAAC,aAAa,EAAE,OAAO,KAAK,IAAI,CAAC;IACtD,qBAAqB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,gBAAgB,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,IAAI,CAAC;IAChD,iBAAiB,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,cAAc,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,iBAAiB,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;IAGlD,cAAc,EAAE,kBAAkB,CAAC;IACnC,oBAAoB,EAAE,wBAAwB,CAAC;IAC/C,SAAS,EAAE,aAAa,CAAC;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;IACpD,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,wBAAwB,CAAC;CACtC;AAGD,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,qBAAqB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAGnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwGG;AAGH,eAAO,MAAM,cAAc,GAAU,kCAAkC,qBAAqB,KAAG,OAAO,CAAC,IAAI,CAqO1G,CAAA"}
1
+ {"version":3,"file":"closeAndResize.d.ts","sourceRoot":"","sources":["../../src/consumers/closeAndResize.ts"],"names":[],"mappings":"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"}
@@ -21,6 +21,12 @@ export interface ConnectIpsOptions {
21
21
  parameters: ConnectIpsParameters;
22
22
  }
23
23
  export type ConnectIpsType = (options: ConnectIpsOptions) => Promise<[Record<string, any>[], string[]]>;
24
+ /**
25
+ * Consume endpoints that refused these credentials (`connection-rejected` with a permanent reason). Domain updates call
26
+ * connectIps again and again; without this every update would retry the same refusal. Keyed by the caller's socket
27
+ * collection so another room (new credentials) starts clean.
28
+ */
29
+ export declare const CONSUME_REJECTION_COOLDOWN_MS = 60000;
24
30
  /**
25
31
  * Connects to remote IPs and manages socket connections.
26
32
  *
@@ -1 +1 @@
1
- {"version":3,"file":"connectIps.d.ts","sourceRoot":"","sources":["../../src/consumers/connectIps.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EACL,wBAAwB,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,kBAAkB,EAChH,wBAAwB,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,aAAa,EACxF,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,oBAAqB,SAAQ,wBAAwB,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,yBAAyB;IACpJ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,iBAAiB,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACnD,qBAAqB,EAAE,CAAC,eAAe,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;IAGlE,cAAc,EAAE,kBAAkB,CAAC;IACnC,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;IAChD,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,eAAe,EAAE,aAAa,EAAE,CAAC;IACjC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,mBAAmB,CAAC;IACxC,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;IAC1C,qBAAqB,CAAC,EAAE,mBAAmB,CAAC;IAC5C,UAAU,EAAE,oBAAoB,CAAC;CAClC;AAGD,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AA0ExG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,eAAO,MAAM,UAAU,GAAU,wIAY9B,iBAAiB,KAAG,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CA+G/D,CAAC"}
1
+ {"version":3,"file":"connectIps.d.ts","sourceRoot":"","sources":["../../src/consumers/connectIps.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EACL,wBAAwB,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,kBAAkB,EAChH,wBAAwB,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,aAAa,EACxF,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,oBAAqB,SAAQ,wBAAwB,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,yBAAyB;IACpJ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,iBAAiB,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACnD,qBAAqB,EAAE,CAAC,eAAe,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;IAGlE,cAAc,EAAE,kBAAkB,CAAC;IACnC,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;IAChD,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,eAAe,EAAE,aAAa,EAAE,CAAC;IACjC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,mBAAmB,CAAC;IACxC,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;IAC1C,qBAAqB,CAAC,EAAE,mBAAmB,CAAC;IAC5C,UAAU,EAAE,oBAAoB,CAAC;CAClC;AAGD,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AAaxG;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,QAAQ,CAAC;AA+EnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,eAAO,MAAM,UAAU,GAAU,wIAY9B,iBAAiB,KAAG,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CA8J/D,CAAC"}
@@ -0,0 +1,65 @@
1
+ import { Producer, Transport } from 'mediasoup-client/types';
2
+ import { Socket } from 'socket.io-client';
3
+ export interface ScreenAudioParameters {
4
+ socket: Socket;
5
+ producerTransport: Transport | null;
6
+ screenProducer?: Producer | null;
7
+ screenAudioProducer?: Producer | null;
8
+ supportsScreenAudio?: boolean;
9
+ screenAudioEnabled?: boolean;
10
+ updateScreenAudioProducer?: (producer: Producer | null) => void;
11
+ getUpdatedAllParams: () => ScreenAudioParameters;
12
+ [key: string]: any;
13
+ }
14
+ export interface ConnectSendTransportScreenAudioOptions {
15
+ stream: MediaStream;
16
+ parameters: ScreenAudioParameters;
17
+ /** Wait before the single retry when the server has not registered the screen share yet. */
18
+ retryDelayMs?: number;
19
+ }
20
+ export type ConnectSendTransportScreenAudioType = (options: ConnectSendTransportScreenAudioOptions) => Promise<Producer | null>;
21
+ export interface DisconnectSendTransportScreenAudioOptions {
22
+ parameters: ScreenAudioParameters;
23
+ /** Also tell the server (the screen share continues). Not needed when the whole screen share stops. */
24
+ notifyServer?: boolean;
25
+ }
26
+ export type DisconnectSendTransportScreenAudioType = (options: DisconnectSendTransportScreenAudioOptions) => Promise<void>;
27
+ /**
28
+ * Closes the screen-audio producer, if any. Safe to call when none exists.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * await disconnectSendTransportScreenAudio({ parameters, notifyServer: true });
33
+ * ```
34
+ */
35
+ export declare const disconnectSendTransportScreenAudio: DisconnectSendTransportScreenAudioType;
36
+ export interface ToggleScreenAudioOptions {
37
+ parameters: ScreenAudioParameters;
38
+ /** Force a state; omitted = toggle. */
39
+ paused?: boolean;
40
+ }
41
+ /**
42
+ * Mutes or unmutes the sharer's screen audio without stopping the screen share (listeners hear silence while muted).
43
+ * Returns the new muted state, or null when no screen audio is being shared.
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * const muted = toggleScreenAudio({ parameters });
48
+ * ```
49
+ */
50
+ export declare const toggleScreenAudio: ({ parameters, paused }: ToggleScreenAudioOptions) => boolean | null;
51
+ /**
52
+ * Publishes the audio track of a screen-share stream as a "screen-audio" producer on the existing send transport.
53
+ * Call it after the screen video producer exists. It returns null (and the screen share continues without audio)
54
+ * when the stream has no audio, the server does not support screen audio, or the app turned it off.
55
+ *
56
+ * The producer mirrors the screen producer: it pauses, resumes and closes with it, and it closes alone (telling the
57
+ * server) when its own track ends.
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * const producer = await connectSendTransportScreenAudio({ stream: screenStream, parameters });
62
+ * ```
63
+ */
64
+ export declare const connectSendTransportScreenAudio: ConnectSendTransportScreenAudioType;
65
+ //# sourceMappingURL=connectSendTransportScreenAudio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectSendTransportScreenAudio.d.ts","sourceRoot":"","sources":["../../src/consumers/connectSendTransportScreenAudio.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAS1C,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,SAAS,GAAG,IAAI,CAAC;IACpC,cAAc,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IACjC,mBAAmB,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,yBAAyB,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;IAChE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;IACjD,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,sCAAsC;IACrD,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,qBAAqB,CAAC;IAClC,4FAA4F;IAC5F,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,mCAAmC,GAAG,CAChD,OAAO,EAAE,sCAAsC,KAC5C,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;AAE9B,MAAM,WAAW,yCAAyC;IACxD,UAAU,EAAE,qBAAqB,CAAC;IAClC,uGAAuG;IACvG,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,sCAAsC,GAAG,CACnD,OAAO,EAAE,yCAAyC,KAC/C,OAAO,CAAC,IAAI,CAAC,CAAC;AAUnB;;;;;;;GAOG;AACH,eAAO,MAAM,kCAAkC,EAAE,sCAoBhD,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,qBAAqB,CAAC;IAClC,uCAAuC;IACvC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,GAAI,wBAAwB,wBAAwB,KAAG,OAAO,GAAG,IAS9F,CAAC;AAkBF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,+BAA+B,EAAE,mCA+C7C,CAAC"}
@@ -0,0 +1,14 @@
1
+ /** How long a busy node's announcement waits for the backup's (ms). */
2
+ export declare const CONSUMER_BACKUP_PREFER_DELAY_MS = 1500;
3
+ /** Records the server's `{ busy node: backup node }` map (from `updateConsumingDomains` / `getDomains`). */
4
+ export declare const rememberConsumerBackups: (altDomains?: Record<string, string> | null) => void;
5
+ export declare const forgetConsumerBackups: () => void;
6
+ type SocketEntry = Record<string, {
7
+ connected?: boolean;
8
+ } | undefined>;
9
+ /** The connected backup to prefer over `ip`, or null. */
10
+ export declare const preferredConsumerBackup: (ip: string, consumeSockets?: SocketEntry[] | null) => string | null;
11
+ /** Delay (ms) before consuming a stream announced by `ip`: non-zero only when a connected backup should win. */
12
+ export declare const consumerBackupPreferDelay: (ip: string, consumeSockets?: SocketEntry[] | null) => number;
13
+ export {};
14
+ //# sourceMappingURL=consumerBackupPreference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consumerBackupPreference.d.ts","sourceRoot":"","sources":["../../src/consumers/consumerBackupPreference.ts"],"names":[],"mappings":"AAeA,uEAAuE;AACvE,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAEpD,4GAA4G;AAC5G,eAAO,MAAM,uBAAuB,GAAI,aAAa,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,KAAG,IAIpF,CAAC;AAEF,eAAO,MAAM,qBAAqB,QAAO,IAA8B,CAAC;AAExE,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAAC,CAAC;AAEvE,yDAAyD;AACzD,eAAO,MAAM,uBAAuB,GAAI,IAAI,MAAM,EAAE,iBAAiB,WAAW,EAAE,GAAG,IAAI,KAAG,MAAM,GAAG,IAQpG,CAAC;AAEF,gHAAgH;AAChH,eAAO,MAAM,yBAAyB,GAAI,IAAI,MAAM,EAAE,iBAAiB,WAAW,EAAE,GAAG,IAAI,KAAG,MACX,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"disconnectSendTransportScreen.d.ts","sourceRoot":"","sources":["../../src/consumers/disconnectSendTransportScreen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,MAAM,WAAW,uCAAuC;IACpD,cAAc,EAAE,QAAQ,GAAG,IAAI,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,CAAC,cAAc,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;IAChE,yBAAyB,CAAC,EAAE,CAAC,mBAAmB,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;IAE3E,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;IACnE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACtB;AACD,MAAM,WAAW,oCAAoC;IACjD,UAAU,EAAE,uCAAuC,CAAC;CACvD;AAGD,MAAM,MAAM,iCAAiC,GAAG,CAAC,OAAO,EAAE,oCAAoC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AA4BjH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,eAAO,MAAM,6BAA6B,GAAU,gBAAgB,oCAAoC,KAAI,OAAO,CAAC,IAAI,CAsCvH,CAAC"}
1
+ {"version":3,"file":"disconnectSendTransportScreen.d.ts","sourceRoot":"","sources":["../../src/consumers/disconnectSendTransportScreen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG1C,MAAM,WAAW,uCAAuC;IACpD,cAAc,EAAE,QAAQ,GAAG,IAAI,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,CAAC,cAAc,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;IAChE,yBAAyB,CAAC,EAAE,CAAC,mBAAmB,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;IAE3E,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;IACnE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACtB;AACD,MAAM,WAAW,oCAAoC;IACjD,UAAU,EAAE,uCAAuC,CAAC;CACvD;AAGD,MAAM,MAAM,iCAAiC,GAAG,CAAC,OAAO,EAAE,oCAAoC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AA4BjH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,eAAO,MAAM,6BAA6B,GAAU,gBAAgB,oCAAoC,KAAI,OAAO,CAAC,IAAI,CA6CvH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"getPipedProducersAlt.d.ts","sourceRoot":"","sources":["../../src/consumers/getPipedProducersAlt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,oCAAoC,EAAE,8BAA8B,EAAE,MAAM,gBAAgB,CAAC;AAetG,MAAM,WAAW,8BAA+B,SAAQ,IAAI,CAAC,oCAAoC,EAAE,qBAAqB,CAAC;IACvH,MAAM,EAAE,MAAM,CAAC;IAEf,8BAA8B,CAAC,EAAE;QAC/B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;YAAC,YAAY,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;QAC/F,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/B,CAAC;IAGF,0BAA0B,EAAE,8BAA8B,CAAC;IAC3D,yBAAyB,CAAC,EAAE,CAC1B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,kBAAkB,CAAC,EAAE,MAAM,EAC3B,KAAK,CAAC,EAAE,MAAM,KACX,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,mBAAmB,CAAC,EAAE,MAAM,8BAA8B,CAAC;IAC3D,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,8BAA8B,CAAC;CAC5C;AAGD,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,EAAE,2BAA2B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAqC/F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,eAAO,MAAM,oBAAoB,GAAU,4CAKxC,2BAA2B,KAAG,OAAO,CAAC,IAAI,CAmE5C,CAAC"}
1
+ {"version":3,"file":"getPipedProducersAlt.d.ts","sourceRoot":"","sources":["../../src/consumers/getPipedProducersAlt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,oCAAoC,EAAE,8BAA8B,EAAE,MAAM,gBAAgB,CAAC;AAkBtG,MAAM,WAAW,8BAA+B,SAAQ,IAAI,CAAC,oCAAoC,EAAE,qBAAqB,CAAC;IACvH,MAAM,EAAE,MAAM,CAAC;IAEf,8BAA8B,CAAC,EAAE;QAC/B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;YAAC,YAAY,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;QAC/F,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/B,CAAC;IAGF,0BAA0B,EAAE,8BAA8B,CAAC;IAC3D,yBAAyB,CAAC,EAAE,CAC1B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,kBAAkB,CAAC,EAAE,MAAM,EAC3B,KAAK,CAAC,EAAE,MAAM,KACX,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,mBAAmB,CAAC,EAAE,MAAM,8BAA8B,CAAC;IAC3D,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,8BAA8B,CAAC;CAC5C;AAGD,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,EAAE,2BAA2B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAqC/F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,eAAO,MAAM,oBAAoB,GAAU,4CAKxC,2BAA2B,KAAG,OAAO,CAAC,IAAI,CAoE5C,CAAC"}