livekit-client 2.22.2 → 2.22.3

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 (68) hide show
  1. package/dist/livekit-client.e2ee.worker.js +1 -1
  2. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  3. package/dist/livekit-client.e2ee.worker.mjs +486 -437
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +398 -100
  6. package/dist/livekit-client.esm.mjs.map +1 -1
  7. package/dist/livekit-client.fm.worker.js +1 -1
  8. package/dist/livekit-client.fm.worker.js.map +1 -1
  9. package/dist/livekit-client.fm.worker.mjs +8 -1
  10. package/dist/livekit-client.fm.worker.mjs.map +1 -1
  11. package/dist/livekit-client.umd.js +1 -1
  12. package/dist/livekit-client.umd.js.map +1 -1
  13. package/dist/src/api/WebSocketStream.d.ts.map +1 -1
  14. package/dist/src/api/utils.d.ts +1 -0
  15. package/dist/src/api/utils.d.ts.map +1 -1
  16. package/dist/src/e2ee/E2eeManager.d.ts +26 -0
  17. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  18. package/dist/src/e2ee/types.d.ts +15 -1
  19. package/dist/src/e2ee/types.d.ts.map +1 -1
  20. package/dist/src/e2ee/worker/DataCryptor.d.ts.map +1 -1
  21. package/dist/src/e2ee/worker/ErrorRateLimiter.d.ts +21 -0
  22. package/dist/src/e2ee/worker/ErrorRateLimiter.d.ts.map +1 -0
  23. package/dist/src/e2ee/worker/FrameCryptor.d.ts +1 -18
  24. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  25. package/dist/src/logger.d.ts +4 -0
  26. package/dist/src/logger.d.ts.map +1 -1
  27. package/dist/src/room/RTCEngine.d.ts +1 -0
  28. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  29. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  30. package/dist/src/room/participant/publishUtils.d.ts +16 -0
  31. package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
  32. package/dist/src/room/track/LocalVideoTrack.d.ts +7 -0
  33. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  34. package/dist/src/room/track/options.d.ts +1 -1
  35. package/dist/src/room/utils.d.ts +34 -0
  36. package/dist/src/room/utils.d.ts.map +1 -1
  37. package/dist/ts4.2/api/utils.d.ts +1 -0
  38. package/dist/ts4.2/e2ee/E2eeManager.d.ts +26 -0
  39. package/dist/ts4.2/e2ee/types.d.ts +15 -1
  40. package/dist/ts4.2/e2ee/worker/ErrorRateLimiter.d.ts +21 -0
  41. package/dist/ts4.2/e2ee/worker/FrameCryptor.d.ts +1 -18
  42. package/dist/ts4.2/logger.d.ts +4 -0
  43. package/dist/ts4.2/room/RTCEngine.d.ts +1 -0
  44. package/dist/ts4.2/room/participant/publishUtils.d.ts +16 -0
  45. package/dist/ts4.2/room/track/LocalVideoTrack.d.ts +7 -0
  46. package/dist/ts4.2/room/track/options.d.ts +1 -1
  47. package/dist/ts4.2/room/utils.d.ts +34 -0
  48. package/package.json +1 -1
  49. package/src/api/WebSocketStream.ts +3 -8
  50. package/src/api/utils.ts +10 -0
  51. package/src/e2ee/E2eeManager.test.ts +196 -0
  52. package/src/e2ee/E2eeManager.ts +114 -19
  53. package/src/e2ee/types.ts +19 -1
  54. package/src/e2ee/worker/DataCryptor.ts +2 -1
  55. package/src/e2ee/worker/ErrorRateLimiter.test.ts +53 -0
  56. package/src/e2ee/worker/ErrorRateLimiter.ts +52 -0
  57. package/src/e2ee/worker/FrameCryptor.ts +20 -70
  58. package/src/e2ee/worker/e2ee.worker.ts +34 -11
  59. package/src/logger.ts +22 -0
  60. package/src/room/RTCEngine.ts +28 -7
  61. package/src/room/Room.ts +1 -1
  62. package/src/room/participant/LocalParticipant.ts +30 -14
  63. package/src/room/participant/publishUtils.test.ts +133 -0
  64. package/src/room/participant/publishUtils.ts +54 -19
  65. package/src/room/track/LocalVideoTrack.ts +15 -5
  66. package/src/room/track/options.ts +1 -1
  67. package/src/room/utils.test.ts +87 -0
  68. package/src/room/utils.ts +59 -0
@@ -424,6 +424,13 @@ function wrapWithContext(base, ctxFn) {
424
424
  return proxy;
425
425
  }
426
426
  const workerLogger = loglevelExports.getLogger(LoggerNames.E2EE);
427
+ const workerLogLevelListeners = new Set();
428
+ const originalWorkerSetLevel = workerLogger.setLevel.bind(workerLogger);
429
+ workerLogger.setLevel = (level, persist) => {
430
+ originalWorkerSetLevel(level, persist);
431
+ const numeric = workerLogger.getLevel();
432
+ workerLogLevelListeners.forEach(cb => cb(numeric));
433
+ };
427
434
 
428
435
  var e = Object.defineProperty;
429
436
  var h = (i, s, t) => s in i ? e(i, s, {
@@ -4105,285 +4112,163 @@ var CryptorEvent;
4105
4112
  CryptorEvent["Error"] = "cryptorError";
4106
4113
  })(CryptorEvent || (CryptorEvent = {}));
4107
4114
 
4108
- function isVideoFrame(frame) {
4109
- return 'type' in frame;
4110
- }
4111
- function importKey(keyBytes_1) {
4112
- return __awaiter(this, arguments, void 0, function (keyBytes) {
4113
- let algorithm = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
4114
- name: ENCRYPTION_ALGORITHM
4115
- };
4116
- let usage = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'encrypt';
4117
- return function* () {
4118
- // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey
4119
- return crypto.subtle.importKey('raw', keyBytes, algorithm, false, usage === 'derive' ? ['deriveBits', 'deriveKey'] : ['encrypt', 'decrypt']);
4120
- }();
4121
- });
4122
- }
4123
- function getAlgoOptions(algorithmName, salt) {
4124
- const textEncoder = new TextEncoder();
4125
- const encodedSalt = textEncoder.encode(salt);
4126
- switch (algorithmName) {
4127
- case 'HKDF':
4128
- return {
4129
- name: 'HKDF',
4130
- salt: encodedSalt,
4131
- hash: 'SHA-256',
4132
- info: new ArrayBuffer(128)
4133
- };
4134
- case 'PBKDF2':
4135
- {
4136
- return {
4137
- name: 'PBKDF2',
4138
- salt: encodedSalt,
4139
- hash: 'SHA-256',
4140
- iterations: 100000
4141
- };
4142
- }
4143
- default:
4144
- throw new Error("algorithm ".concat(algorithmName, " is currently unsupported"));
4115
+ var _a, _b;
4116
+ class TypedPromise extends (_b = Promise) {
4117
+ // eslint-disable-next-line @typescript-eslint/no-useless-constructor
4118
+ constructor(executor) {
4119
+ super(executor);
4120
+ }
4121
+ catch(onrejected) {
4122
+ return super.catch(onrejected);
4123
+ }
4124
+ static reject(reason) {
4125
+ return super.reject(reason);
4126
+ }
4127
+ static all(values) {
4128
+ return super.all(values);
4129
+ }
4130
+ static race(values) {
4131
+ return super.race(values);
4145
4132
  }
4146
4133
  }
4134
+ _a = TypedPromise;
4135
+ TypedPromise.resolve = value => {
4136
+ return Reflect.get(_b, "resolve", _a).call(_a, value);
4137
+ };
4138
+
4139
+ // tiny, simplified version of https://github.com/lancedikson/bowser/blob/master/src/parser-browsers.js
4140
+ // reduced to only differentiate Chrome(ium) based browsers / Firefox / Safari
4141
+ const commonVersionIdentifier = /version\/(\d+(\.?_?\d+)+)/i;
4142
+ let browserDetails;
4147
4143
  /**
4148
- * Derives a set of keys from the master key.
4149
- * See https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.1
4144
+ * @internal
4150
4145
  */
4151
- function deriveKeys(material, options) {
4152
- return __awaiter(this, void 0, void 0, function* () {
4153
- const algorithmOptions = getAlgoOptions(material.algorithm.name, options.ratchetSalt);
4154
- // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey#HKDF
4155
- // https://developer.mozilla.org/en-US/docs/Web/API/HkdfParams
4156
- const encryptionKey = yield crypto.subtle.deriveKey(algorithmOptions, material, {
4157
- name: ENCRYPTION_ALGORITHM,
4158
- length: options.keySize
4159
- }, false, ['encrypt', 'decrypt']);
4160
- return {
4161
- material,
4162
- encryptionKey
4146
+ function getBrowser(userAgent) {
4147
+ let force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
4148
+ if (typeof navigator === 'undefined') {
4149
+ return;
4150
+ }
4151
+ const ua = (navigator.userAgent).toLowerCase();
4152
+ if (browserDetails === undefined || force) {
4153
+ const browser = browsersList.find(_ref => {
4154
+ let test = _ref.test;
4155
+ return test.test(ua);
4156
+ });
4157
+ browserDetails = browser === null || browser === void 0 ? void 0 : browser.describe(ua);
4158
+ }
4159
+ return browserDetails;
4160
+ }
4161
+ const browsersList = [{
4162
+ test: /firefox|iceweasel|fxios/i,
4163
+ describe(ua) {
4164
+ const browser = {
4165
+ name: 'Firefox',
4166
+ version: getMatch(/(?:firefox|iceweasel|fxios)[\s/](\d+(\.?_?\d+)+)/i, ua),
4167
+ os: ua.toLowerCase().includes('fxios') ? 'iOS' : undefined,
4168
+ osVersion: getOSVersion(ua)
4163
4169
  };
4164
- });
4170
+ return browser;
4171
+ }
4172
+ }, {
4173
+ test: /chrom|crios|crmo/i,
4174
+ describe(ua) {
4175
+ const browser = {
4176
+ name: 'Chrome',
4177
+ version: getMatch(/(?:chrome|chromium|crios|crmo)\/(\d+(\.?_?\d+)+)/i, ua),
4178
+ os: ua.toLowerCase().includes('crios') ? 'iOS' : undefined,
4179
+ osVersion: getOSVersion(ua)
4180
+ };
4181
+ return browser;
4182
+ }
4183
+ }, /* Safari */
4184
+ {
4185
+ test: /safari|applewebkit/i,
4186
+ describe(ua) {
4187
+ const browser = {
4188
+ name: 'Safari',
4189
+ version: getMatch(commonVersionIdentifier, ua),
4190
+ os: ua.includes('mobile/') ? 'iOS' : 'macOS',
4191
+ osVersion: getOSVersion(ua)
4192
+ };
4193
+ return browser;
4194
+ }
4195
+ }];
4196
+ function getMatch(exp, ua) {
4197
+ let id = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
4198
+ const match = ua.match(exp);
4199
+ return match && match.length >= id && match[id] || '';
4165
4200
  }
4166
- /**
4167
- * Ratchets a key. See
4168
- * https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.5.1
4169
- */
4170
- function ratchet(material, salt) {
4171
- return __awaiter(this, void 0, void 0, function* () {
4172
- const algorithmOptions = getAlgoOptions(material.algorithm.name, salt);
4173
- // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveBits
4174
- return crypto.subtle.deriveBits(algorithmOptions, material, 256);
4175
- });
4201
+ function getOSVersion(ua) {
4202
+ return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
4176
4203
  }
4177
- function needsRbspUnescaping(frameData) {
4178
- for (var i = 0; i < frameData.length - 3; i++) {
4179
- if (frameData[i] == 0 && frameData[i + 1] == 0 && frameData[i + 2] == 3) return true;
4204
+
4205
+ var events = {exports: {}};
4206
+
4207
+ var hasRequiredEvents;
4208
+ function requireEvents() {
4209
+ if (hasRequiredEvents) return events.exports;
4210
+ hasRequiredEvents = 1;
4211
+ var R = typeof Reflect === 'object' ? Reflect : null;
4212
+ var ReflectApply = R && typeof R.apply === 'function' ? R.apply : function ReflectApply(target, receiver, args) {
4213
+ return Function.prototype.apply.call(target, receiver, args);
4214
+ };
4215
+ var ReflectOwnKeys;
4216
+ if (R && typeof R.ownKeys === 'function') {
4217
+ ReflectOwnKeys = R.ownKeys;
4218
+ } else if (Object.getOwnPropertySymbols) {
4219
+ ReflectOwnKeys = function ReflectOwnKeys(target) {
4220
+ return Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target));
4221
+ };
4222
+ } else {
4223
+ ReflectOwnKeys = function ReflectOwnKeys(target) {
4224
+ return Object.getOwnPropertyNames(target);
4225
+ };
4180
4226
  }
4181
- return false;
4182
- }
4183
- function parseRbsp(stream) {
4184
- const dataOut = [];
4185
- var length = stream.length;
4186
- for (var i = 0; i < stream.length;) {
4187
- // Be careful about over/underflow here. byte_length_ - 3 can underflow, and
4188
- // i + 3 can overflow, but byte_length_ - i can't, because i < byte_length_
4189
- // above, and that expression will produce the number of bytes left in
4190
- // the stream including the byte at i.
4191
- if (length - i >= 3 && !stream[i] && !stream[i + 1] && stream[i + 2] == 3) {
4192
- // Two rbsp bytes.
4193
- dataOut.push(stream[i++]);
4194
- dataOut.push(stream[i++]);
4195
- // Skip the emulation byte.
4196
- i++;
4197
- } else {
4198
- // Single rbsp byte.
4199
- dataOut.push(stream[i++]);
4227
+ function ProcessEmitWarning(warning) {
4228
+ if (console && console.warn) console.warn(warning);
4229
+ }
4230
+ var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
4231
+ return value !== value;
4232
+ };
4233
+ function EventEmitter() {
4234
+ EventEmitter.init.call(this);
4235
+ }
4236
+ events.exports = EventEmitter;
4237
+ events.exports.once = once;
4238
+
4239
+ // Backwards-compat with node 0.10.x
4240
+ EventEmitter.EventEmitter = EventEmitter;
4241
+ EventEmitter.prototype._events = undefined;
4242
+ EventEmitter.prototype._eventsCount = 0;
4243
+ EventEmitter.prototype._maxListeners = undefined;
4244
+
4245
+ // By default EventEmitters will print a warning if more than 10 listeners are
4246
+ // added to it. This is a useful default which helps finding memory leaks.
4247
+ var defaultMaxListeners = 10;
4248
+ function checkListener(listener) {
4249
+ if (typeof listener !== 'function') {
4250
+ throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
4200
4251
  }
4201
4252
  }
4202
- return new Uint8Array(dataOut);
4203
- }
4204
- const kZerosInStartSequence = 2;
4205
- const kEmulationByte = 3;
4206
- function writeRbsp(data_in) {
4207
- const dataOut = [];
4208
- var numConsecutiveZeros = 0;
4209
- for (var i = 0; i < data_in.length; ++i) {
4210
- var byte = data_in[i];
4211
- if (byte <= kEmulationByte && numConsecutiveZeros >= kZerosInStartSequence) {
4212
- // Need to escape.
4213
- dataOut.push(kEmulationByte);
4214
- numConsecutiveZeros = 0;
4253
+ Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
4254
+ enumerable: true,
4255
+ get: function () {
4256
+ return defaultMaxListeners;
4257
+ },
4258
+ set: function (arg) {
4259
+ if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
4260
+ throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
4261
+ }
4262
+ defaultMaxListeners = arg;
4215
4263
  }
4216
- dataOut.push(byte);
4217
- if (byte == 0) {
4218
- ++numConsecutiveZeros;
4219
- } else {
4220
- numConsecutiveZeros = 0;
4264
+ });
4265
+ EventEmitter.init = function () {
4266
+ if (this._events === undefined || this._events === Object.getPrototypeOf(this)._events) {
4267
+ this._events = Object.create(null);
4268
+ this._eventsCount = 0;
4221
4269
  }
4222
- }
4223
- return new Uint8Array(dataOut);
4224
- }
4225
-
4226
- class DataCryptor {
4227
- static makeIV(timestamp) {
4228
- const iv = new ArrayBuffer(12);
4229
- const ivView = new DataView(iv);
4230
- const randomBytes = crypto.getRandomValues(new Uint32Array(1));
4231
- ivView.setUint32(0, randomBytes[0]);
4232
- ivView.setUint32(4, timestamp);
4233
- ivView.setUint32(8, timestamp - DataCryptor.sendCount % 0xffff);
4234
- DataCryptor.sendCount++;
4235
- return iv;
4236
- }
4237
- static encrypt(data, keys) {
4238
- return __awaiter(this, void 0, void 0, function* () {
4239
- const iv = DataCryptor.makeIV(performance.now());
4240
- const keySet = yield keys.getKeySet();
4241
- if (!keySet) {
4242
- throw new Error('No key set found');
4243
- }
4244
- const cipherText = yield crypto.subtle.encrypt({
4245
- name: ENCRYPTION_ALGORITHM,
4246
- iv
4247
- }, keySet.encryptionKey, new Uint8Array(data));
4248
- return {
4249
- payload: new Uint8Array(cipherText),
4250
- iv: new Uint8Array(iv),
4251
- keyIndex: keys.getCurrentKeyIndex()
4252
- };
4253
- });
4254
- }
4255
- static decrypt(data_1, iv_1, keys_1) {
4256
- return __awaiter(this, arguments, void 0, function (data, iv, keys) {
4257
- let keyIndex = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
4258
- let initialMaterial = arguments.length > 4 ? arguments[4] : undefined;
4259
- let ratchetOpts = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {
4260
- ratchetCount: 0
4261
- };
4262
- return function* () {
4263
- const keySet = yield keys.getKeySet(keyIndex);
4264
- if (!keySet) {
4265
- throw new Error('No key set found');
4266
- }
4267
- try {
4268
- const plainText = yield crypto.subtle.decrypt({
4269
- name: ENCRYPTION_ALGORITHM,
4270
- iv
4271
- }, keySet.encryptionKey, new Uint8Array(data));
4272
- return {
4273
- payload: new Uint8Array(plainText)
4274
- };
4275
- } catch (error) {
4276
- if (keys.keyProviderOptions.ratchetWindowSize > 0) {
4277
- if (ratchetOpts.ratchetCount < keys.keyProviderOptions.ratchetWindowSize) {
4278
- workerLogger.debug("DataCryptor: ratcheting key attempt ".concat(ratchetOpts.ratchetCount, " of ").concat(keys.keyProviderOptions.ratchetWindowSize, ", for data packet"));
4279
- let ratchetedKeySet;
4280
- let ratchetResult;
4281
- if ((initialMaterial !== null && initialMaterial !== void 0 ? initialMaterial : keySet) === keys.getKeySet(keyIndex)) {
4282
- // only ratchet if the currently set key is still the same as the one used to decrypt this frame
4283
- // if not, it might be that a different frame has already ratcheted and we try with that one first
4284
- ratchetResult = yield keys.ratchetKey(keyIndex, false);
4285
- ratchetedKeySet = yield deriveKeys(ratchetResult.cryptoKey, keys.keyProviderOptions);
4286
- }
4287
- const decryptedData = yield DataCryptor.decrypt(data, iv, keys, keyIndex, initialMaterial, {
4288
- ratchetCount: ratchetOpts.ratchetCount + 1,
4289
- encryptionKey: ratchetedKeySet === null || ratchetedKeySet === void 0 ? void 0 : ratchetedKeySet.encryptionKey
4290
- });
4291
- if (decryptedData && ratchetedKeySet) {
4292
- // before updating the keys, make sure that the keySet used for this frame is still the same as the currently set key
4293
- // if it's not, a new key might have been set already, which we don't want to override
4294
- if ((initialMaterial !== null && initialMaterial !== void 0 ? initialMaterial : keySet) === keys.getKeySet(keyIndex)) {
4295
- keys.setKeySet(ratchetedKeySet, keyIndex, ratchetResult);
4296
- // decryption was successful, set the new key index to reflect the ratcheted key set
4297
- keys.setCurrentKeyIndex(keyIndex);
4298
- }
4299
- }
4300
- return decryptedData;
4301
- } else {
4302
- /**
4303
- * Because we only set a new key once decryption has been successful,
4304
- * we can be sure that we don't need to reset the key to the initial material at this point
4305
- * as the key has not been updated on the keyHandler instance
4306
- */
4307
- workerLogger.warn('DataCryptor: maximum ratchet attempts exceeded');
4308
- throw new CryptorError("DataCryptor: valid key missing for participant ".concat(keys.participantIdentity), CryptorErrorReason.InvalidKey, keys.participantIdentity);
4309
- }
4310
- } else {
4311
- throw new CryptorError("DataCryptor: Decryption failed: ".concat(error.message), CryptorErrorReason.InvalidKey, keys.participantIdentity);
4312
- }
4313
- }
4314
- }();
4315
- });
4316
- }
4317
- }
4318
- DataCryptor.sendCount = 0;
4319
-
4320
- var events = {exports: {}};
4321
-
4322
- var hasRequiredEvents;
4323
- function requireEvents() {
4324
- if (hasRequiredEvents) return events.exports;
4325
- hasRequiredEvents = 1;
4326
- var R = typeof Reflect === 'object' ? Reflect : null;
4327
- var ReflectApply = R && typeof R.apply === 'function' ? R.apply : function ReflectApply(target, receiver, args) {
4328
- return Function.prototype.apply.call(target, receiver, args);
4329
- };
4330
- var ReflectOwnKeys;
4331
- if (R && typeof R.ownKeys === 'function') {
4332
- ReflectOwnKeys = R.ownKeys;
4333
- } else if (Object.getOwnPropertySymbols) {
4334
- ReflectOwnKeys = function ReflectOwnKeys(target) {
4335
- return Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target));
4336
- };
4337
- } else {
4338
- ReflectOwnKeys = function ReflectOwnKeys(target) {
4339
- return Object.getOwnPropertyNames(target);
4340
- };
4341
- }
4342
- function ProcessEmitWarning(warning) {
4343
- if (console && console.warn) console.warn(warning);
4344
- }
4345
- var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
4346
- return value !== value;
4347
- };
4348
- function EventEmitter() {
4349
- EventEmitter.init.call(this);
4350
- }
4351
- events.exports = EventEmitter;
4352
- events.exports.once = once;
4353
-
4354
- // Backwards-compat with node 0.10.x
4355
- EventEmitter.EventEmitter = EventEmitter;
4356
- EventEmitter.prototype._events = undefined;
4357
- EventEmitter.prototype._eventsCount = 0;
4358
- EventEmitter.prototype._maxListeners = undefined;
4359
-
4360
- // By default EventEmitters will print a warning if more than 10 listeners are
4361
- // added to it. This is a useful default which helps finding memory leaks.
4362
- var defaultMaxListeners = 10;
4363
- function checkListener(listener) {
4364
- if (typeof listener !== 'function') {
4365
- throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
4366
- }
4367
- }
4368
- Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
4369
- enumerable: true,
4370
- get: function () {
4371
- return defaultMaxListeners;
4372
- },
4373
- set: function (arg) {
4374
- if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
4375
- throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
4376
- }
4377
- defaultMaxListeners = arg;
4378
- }
4379
- });
4380
- EventEmitter.init = function () {
4381
- if (this._events === undefined || this._events === Object.getPrototypeOf(this)._events) {
4382
- this._events = Object.create(null);
4383
- this._eventsCount = 0;
4384
- }
4385
- this._maxListeners = this._maxListeners || undefined;
4386
- };
4270
+ this._maxListeners = this._maxListeners || undefined;
4271
+ };
4387
4272
 
4388
4273
  // Obviously not all Emitters should be limited to 10. This function allows
4389
4274
  // that to be increased. Set to zero for unlimited.
@@ -4704,96 +4589,6 @@ function requireEvents() {
4704
4589
 
4705
4590
  var eventsExports = requireEvents();
4706
4591
 
4707
- var _a, _b;
4708
- class TypedPromise extends (_b = Promise) {
4709
- // eslint-disable-next-line @typescript-eslint/no-useless-constructor
4710
- constructor(executor) {
4711
- super(executor);
4712
- }
4713
- catch(onrejected) {
4714
- return super.catch(onrejected);
4715
- }
4716
- static reject(reason) {
4717
- return super.reject(reason);
4718
- }
4719
- static all(values) {
4720
- return super.all(values);
4721
- }
4722
- static race(values) {
4723
- return super.race(values);
4724
- }
4725
- }
4726
- _a = TypedPromise;
4727
- TypedPromise.resolve = value => {
4728
- return Reflect.get(_b, "resolve", _a).call(_a, value);
4729
- };
4730
-
4731
- // tiny, simplified version of https://github.com/lancedikson/bowser/blob/master/src/parser-browsers.js
4732
- // reduced to only differentiate Chrome(ium) based browsers / Firefox / Safari
4733
- const commonVersionIdentifier = /version\/(\d+(\.?_?\d+)+)/i;
4734
- let browserDetails;
4735
- /**
4736
- * @internal
4737
- */
4738
- function getBrowser(userAgent) {
4739
- let force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
4740
- if (typeof navigator === 'undefined') {
4741
- return;
4742
- }
4743
- const ua = (navigator.userAgent).toLowerCase();
4744
- if (browserDetails === undefined || force) {
4745
- const browser = browsersList.find(_ref => {
4746
- let test = _ref.test;
4747
- return test.test(ua);
4748
- });
4749
- browserDetails = browser === null || browser === void 0 ? void 0 : browser.describe(ua);
4750
- }
4751
- return browserDetails;
4752
- }
4753
- const browsersList = [{
4754
- test: /firefox|iceweasel|fxios/i,
4755
- describe(ua) {
4756
- const browser = {
4757
- name: 'Firefox',
4758
- version: getMatch(/(?:firefox|iceweasel|fxios)[\s/](\d+(\.?_?\d+)+)/i, ua),
4759
- os: ua.toLowerCase().includes('fxios') ? 'iOS' : undefined,
4760
- osVersion: getOSVersion(ua)
4761
- };
4762
- return browser;
4763
- }
4764
- }, {
4765
- test: /chrom|crios|crmo/i,
4766
- describe(ua) {
4767
- const browser = {
4768
- name: 'Chrome',
4769
- version: getMatch(/(?:chrome|chromium|crios|crmo)\/(\d+(\.?_?\d+)+)/i, ua),
4770
- os: ua.toLowerCase().includes('crios') ? 'iOS' : undefined,
4771
- osVersion: getOSVersion(ua)
4772
- };
4773
- return browser;
4774
- }
4775
- }, /* Safari */
4776
- {
4777
- test: /safari|applewebkit/i,
4778
- describe(ua) {
4779
- const browser = {
4780
- name: 'Safari',
4781
- version: getMatch(commonVersionIdentifier, ua),
4782
- os: ua.includes('mobile/') ? 'iOS' : 'macOS',
4783
- osVersion: getOSVersion(ua)
4784
- };
4785
- return browser;
4786
- }
4787
- }];
4788
- function getMatch(exp, ua) {
4789
- let id = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
4790
- const match = ua.match(exp);
4791
- return match && match.length >= id && match[id] || '';
4792
- }
4793
- function getOSVersion(ua) {
4794
- return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
4795
- }
4796
-
4797
4592
  /**
4798
4593
  * Events are the primary way LiveKit notifies your application of changes.
4799
4594
  *
@@ -6156,16 +5951,290 @@ function isWeb() {
6156
5951
  return typeof document !== 'undefined';
6157
5952
  }
6158
5953
 
6159
- function hasFrameMetadataPublishOptions(options) {
6160
- return !!((options === null || options === void 0 ? void 0 : options.timestamp) || (options === null || options === void 0 ? void 0 : options.frameId));
5954
+ function getErrorDescription(error, errorCategory) {
5955
+ if (error instanceof Error) {
5956
+ if (error.name && error.message) {
5957
+ return "".concat(error.name, ": ").concat(error.message);
5958
+ }
5959
+ return error.name;
5960
+ }
5961
+ return "Encountered unknown ".concat(errorCategory, " error: ").concat(String(error));
6161
5962
  }
6162
5963
 
6163
- const PACKET_TRAILER_MAGIC = Uint8Array.from(['L'.charCodeAt(0), 'K'.charCodeAt(0), 'T'.charCodeAt(0), 'S'.charCodeAt(0)]);
6164
- const PACKET_TRAILER_TIMESTAMP_TAG = 0x01;
6165
- const PACKET_TRAILER_FRAME_ID_TAG = 0x02;
6166
- const PACKET_TRAILER_USER_DATA_TAG = 0x03;
6167
- const PACKET_TRAILER_ENVELOPE_SIZE = 5;
6168
- const TIMESTAMP_TLV_SIZE = 10;
5964
+ function isVideoFrame(frame) {
5965
+ return 'type' in frame;
5966
+ }
5967
+ function importKey(keyBytes_1) {
5968
+ return __awaiter(this, arguments, void 0, function (keyBytes) {
5969
+ let algorithm = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
5970
+ name: ENCRYPTION_ALGORITHM
5971
+ };
5972
+ let usage = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'encrypt';
5973
+ return function* () {
5974
+ // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey
5975
+ return crypto.subtle.importKey('raw', keyBytes, algorithm, false, usage === 'derive' ? ['deriveBits', 'deriveKey'] : ['encrypt', 'decrypt']);
5976
+ }();
5977
+ });
5978
+ }
5979
+ function getAlgoOptions(algorithmName, salt) {
5980
+ const textEncoder = new TextEncoder();
5981
+ const encodedSalt = textEncoder.encode(salt);
5982
+ switch (algorithmName) {
5983
+ case 'HKDF':
5984
+ return {
5985
+ name: 'HKDF',
5986
+ salt: encodedSalt,
5987
+ hash: 'SHA-256',
5988
+ info: new ArrayBuffer(128)
5989
+ };
5990
+ case 'PBKDF2':
5991
+ {
5992
+ return {
5993
+ name: 'PBKDF2',
5994
+ salt: encodedSalt,
5995
+ hash: 'SHA-256',
5996
+ iterations: 100000
5997
+ };
5998
+ }
5999
+ default:
6000
+ throw new Error("algorithm ".concat(algorithmName, " is currently unsupported"));
6001
+ }
6002
+ }
6003
+ /**
6004
+ * Derives a set of keys from the master key.
6005
+ * See https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.1
6006
+ */
6007
+ function deriveKeys(material, options) {
6008
+ return __awaiter(this, void 0, void 0, function* () {
6009
+ const algorithmOptions = getAlgoOptions(material.algorithm.name, options.ratchetSalt);
6010
+ // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey#HKDF
6011
+ // https://developer.mozilla.org/en-US/docs/Web/API/HkdfParams
6012
+ const encryptionKey = yield crypto.subtle.deriveKey(algorithmOptions, material, {
6013
+ name: ENCRYPTION_ALGORITHM,
6014
+ length: options.keySize
6015
+ }, false, ['encrypt', 'decrypt']);
6016
+ return {
6017
+ material,
6018
+ encryptionKey
6019
+ };
6020
+ });
6021
+ }
6022
+ /**
6023
+ * Ratchets a key. See
6024
+ * https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.5.1
6025
+ */
6026
+ function ratchet(material, salt) {
6027
+ return __awaiter(this, void 0, void 0, function* () {
6028
+ const algorithmOptions = getAlgoOptions(material.algorithm.name, salt);
6029
+ // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveBits
6030
+ return crypto.subtle.deriveBits(algorithmOptions, material, 256);
6031
+ });
6032
+ }
6033
+ function needsRbspUnescaping(frameData) {
6034
+ for (var i = 0; i < frameData.length - 3; i++) {
6035
+ if (frameData[i] == 0 && frameData[i + 1] == 0 && frameData[i + 2] == 3) return true;
6036
+ }
6037
+ return false;
6038
+ }
6039
+ function parseRbsp(stream) {
6040
+ const dataOut = [];
6041
+ var length = stream.length;
6042
+ for (var i = 0; i < stream.length;) {
6043
+ // Be careful about over/underflow here. byte_length_ - 3 can underflow, and
6044
+ // i + 3 can overflow, but byte_length_ - i can't, because i < byte_length_
6045
+ // above, and that expression will produce the number of bytes left in
6046
+ // the stream including the byte at i.
6047
+ if (length - i >= 3 && !stream[i] && !stream[i + 1] && stream[i + 2] == 3) {
6048
+ // Two rbsp bytes.
6049
+ dataOut.push(stream[i++]);
6050
+ dataOut.push(stream[i++]);
6051
+ // Skip the emulation byte.
6052
+ i++;
6053
+ } else {
6054
+ // Single rbsp byte.
6055
+ dataOut.push(stream[i++]);
6056
+ }
6057
+ }
6058
+ return new Uint8Array(dataOut);
6059
+ }
6060
+ const kZerosInStartSequence = 2;
6061
+ const kEmulationByte = 3;
6062
+ function writeRbsp(data_in) {
6063
+ const dataOut = [];
6064
+ var numConsecutiveZeros = 0;
6065
+ for (var i = 0; i < data_in.length; ++i) {
6066
+ var byte = data_in[i];
6067
+ if (byte <= kEmulationByte && numConsecutiveZeros >= kZerosInStartSequence) {
6068
+ // Need to escape.
6069
+ dataOut.push(kEmulationByte);
6070
+ numConsecutiveZeros = 0;
6071
+ }
6072
+ dataOut.push(byte);
6073
+ if (byte == 0) {
6074
+ ++numConsecutiveZeros;
6075
+ } else {
6076
+ numConsecutiveZeros = 0;
6077
+ }
6078
+ }
6079
+ return new Uint8Array(dataOut);
6080
+ }
6081
+
6082
+ class DataCryptor {
6083
+ static makeIV(timestamp) {
6084
+ const iv = new ArrayBuffer(12);
6085
+ const ivView = new DataView(iv);
6086
+ const randomBytes = crypto.getRandomValues(new Uint32Array(1));
6087
+ ivView.setUint32(0, randomBytes[0]);
6088
+ ivView.setUint32(4, timestamp);
6089
+ ivView.setUint32(8, timestamp - DataCryptor.sendCount % 0xffff);
6090
+ DataCryptor.sendCount++;
6091
+ return iv;
6092
+ }
6093
+ static encrypt(data, keys) {
6094
+ return __awaiter(this, void 0, void 0, function* () {
6095
+ const iv = DataCryptor.makeIV(performance.now());
6096
+ const keySet = yield keys.getKeySet();
6097
+ if (!keySet) {
6098
+ throw new Error('No key set found');
6099
+ }
6100
+ const cipherText = yield crypto.subtle.encrypt({
6101
+ name: ENCRYPTION_ALGORITHM,
6102
+ iv
6103
+ }, keySet.encryptionKey, new Uint8Array(data));
6104
+ return {
6105
+ payload: new Uint8Array(cipherText),
6106
+ iv: new Uint8Array(iv),
6107
+ keyIndex: keys.getCurrentKeyIndex()
6108
+ };
6109
+ });
6110
+ }
6111
+ static decrypt(data_1, iv_1, keys_1) {
6112
+ return __awaiter(this, arguments, void 0, function (data, iv, keys) {
6113
+ let keyIndex = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
6114
+ let initialMaterial = arguments.length > 4 ? arguments[4] : undefined;
6115
+ let ratchetOpts = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {
6116
+ ratchetCount: 0
6117
+ };
6118
+ return function* () {
6119
+ const keySet = yield keys.getKeySet(keyIndex);
6120
+ if (!keySet) {
6121
+ throw new Error('No key set found');
6122
+ }
6123
+ try {
6124
+ const plainText = yield crypto.subtle.decrypt({
6125
+ name: ENCRYPTION_ALGORITHM,
6126
+ iv
6127
+ }, keySet.encryptionKey, new Uint8Array(data));
6128
+ return {
6129
+ payload: new Uint8Array(plainText)
6130
+ };
6131
+ } catch (error) {
6132
+ if (keys.keyProviderOptions.ratchetWindowSize > 0) {
6133
+ if (ratchetOpts.ratchetCount < keys.keyProviderOptions.ratchetWindowSize) {
6134
+ workerLogger.debug("DataCryptor: ratcheting key attempt ".concat(ratchetOpts.ratchetCount, " of ").concat(keys.keyProviderOptions.ratchetWindowSize, ", for data packet"));
6135
+ let ratchetedKeySet;
6136
+ let ratchetResult;
6137
+ if ((initialMaterial !== null && initialMaterial !== void 0 ? initialMaterial : keySet) === keys.getKeySet(keyIndex)) {
6138
+ // only ratchet if the currently set key is still the same as the one used to decrypt this frame
6139
+ // if not, it might be that a different frame has already ratcheted and we try with that one first
6140
+ ratchetResult = yield keys.ratchetKey(keyIndex, false);
6141
+ ratchetedKeySet = yield deriveKeys(ratchetResult.cryptoKey, keys.keyProviderOptions);
6142
+ }
6143
+ const decryptedData = yield DataCryptor.decrypt(data, iv, keys, keyIndex, initialMaterial, {
6144
+ ratchetCount: ratchetOpts.ratchetCount + 1,
6145
+ encryptionKey: ratchetedKeySet === null || ratchetedKeySet === void 0 ? void 0 : ratchetedKeySet.encryptionKey
6146
+ });
6147
+ if (decryptedData && ratchetedKeySet) {
6148
+ // before updating the keys, make sure that the keySet used for this frame is still the same as the currently set key
6149
+ // if it's not, a new key might have been set already, which we don't want to override
6150
+ if ((initialMaterial !== null && initialMaterial !== void 0 ? initialMaterial : keySet) === keys.getKeySet(keyIndex)) {
6151
+ keys.setKeySet(ratchetedKeySet, keyIndex, ratchetResult);
6152
+ // decryption was successful, set the new key index to reflect the ratcheted key set
6153
+ keys.setCurrentKeyIndex(keyIndex);
6154
+ }
6155
+ }
6156
+ return decryptedData;
6157
+ } else {
6158
+ /**
6159
+ * Because we only set a new key once decryption has been successful,
6160
+ * we can be sure that we don't need to reset the key to the initial material at this point
6161
+ * as the key has not been updated on the keyHandler instance
6162
+ */
6163
+ workerLogger.warn('DataCryptor: maximum ratchet attempts exceeded');
6164
+ throw new CryptorError("DataCryptor: valid key missing for participant ".concat(keys.participantIdentity), CryptorErrorReason.InvalidKey, keys.participantIdentity);
6165
+ }
6166
+ } else {
6167
+ throw new CryptorError("DataCryptor: Decryption failed: ".concat(getErrorDescription(error, 'decryption')), CryptorErrorReason.InvalidKey, keys.participantIdentity);
6168
+ }
6169
+ }
6170
+ }();
6171
+ });
6172
+ }
6173
+ }
6174
+ DataCryptor.sendCount = 0;
6175
+
6176
+ /**
6177
+ * Per-key rate limiter for repeated errors. Prevents log/emit floods and the
6178
+ * unbounded map growth that a per-event log would cause when a broken key
6179
+ * keeps producing failures.
6180
+ */
6181
+ class ErrorRateLimiter {
6182
+ constructor() {
6183
+ let throttleMs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1000;
6184
+ let windowMs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 60000;
6185
+ let maxPerWindow = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 5;
6186
+ this.throttleMs = throttleMs;
6187
+ this.windowMs = windowMs;
6188
+ this.maxPerWindow = maxPerWindow;
6189
+ this.lastAt = new Map();
6190
+ this.counts = new Map();
6191
+ }
6192
+ reset() {
6193
+ this.lastAt.clear();
6194
+ this.counts.clear();
6195
+ }
6196
+ countFor(key) {
6197
+ var _a;
6198
+ return (_a = this.counts.get(key)) !== null && _a !== void 0 ? _a : 0;
6199
+ }
6200
+ /**
6201
+ * Returns true if the caller should emit for this key. Invokes `onSuppress`
6202
+ * exactly once per window when the per-window limit is first crossed.
6203
+ */
6204
+ shouldEmit(key, onSuppress) {
6205
+ var _a, _b;
6206
+ const now = Date.now();
6207
+ const last = (_a = this.lastAt.get(key)) !== null && _a !== void 0 ? _a : 0;
6208
+ const count = (_b = this.counts.get(key)) !== null && _b !== void 0 ? _b : 0;
6209
+ if (now - last > this.windowMs) {
6210
+ this.counts.set(key, 0);
6211
+ this.lastAt.set(key, now);
6212
+ return true;
6213
+ }
6214
+ if (now - last < this.throttleMs) return false;
6215
+ if (count >= this.maxPerWindow) {
6216
+ if (count === this.maxPerWindow) {
6217
+ onSuppress === null || onSuppress === void 0 ? void 0 : onSuppress();
6218
+ this.counts.set(key, count + 1);
6219
+ }
6220
+ return false;
6221
+ }
6222
+ this.lastAt.set(key, now);
6223
+ this.counts.set(key, count + 1);
6224
+ return true;
6225
+ }
6226
+ }
6227
+
6228
+ function hasFrameMetadataPublishOptions(options) {
6229
+ return !!((options === null || options === void 0 ? void 0 : options.timestamp) || (options === null || options === void 0 ? void 0 : options.frameId));
6230
+ }
6231
+
6232
+ const PACKET_TRAILER_MAGIC = Uint8Array.from(['L'.charCodeAt(0), 'K'.charCodeAt(0), 'T'.charCodeAt(0), 'S'.charCodeAt(0)]);
6233
+ const PACKET_TRAILER_TIMESTAMP_TAG = 0x01;
6234
+ const PACKET_TRAILER_FRAME_ID_TAG = 0x02;
6235
+ const PACKET_TRAILER_USER_DATA_TAG = 0x03;
6236
+ const PACKET_TRAILER_ENVELOPE_SIZE = 5;
6237
+ const TIMESTAMP_TLV_SIZE = 10;
6169
6238
  const FRAME_ID_TLV_SIZE = 6;
6170
6239
  function appendPacketTrailer(data, userTimestamp, frameId) {
6171
6240
  const hasTimestamp = userTimestamp !== BigInt(0);
@@ -6686,14 +6755,7 @@ class FrameCryptor extends BaseFrameCryptor {
6686
6755
  */
6687
6756
  this.hasFrameMetadata = false;
6688
6757
  this.frameMetadataFrameId = 0;
6689
- /**
6690
- * Throttling mechanism for decryption errors to prevent memory leaks
6691
- */
6692
- this.lastErrorTimestamp = new Map();
6693
- this.errorCounts = new Map();
6694
- this.ERROR_THROTTLE_MS = 1000; // Emit error at most once per second
6695
- this.MAX_ERRORS_PER_MINUTE = 5; // Maximum errors to emit per minute per key
6696
- this.ERROR_WINDOW_MS = 60000; // 1 minute window
6758
+ this.errorLimiter = new ErrorRateLimiter();
6697
6759
  /** grace period for a teardown or a resubscribe to land before we report a stalled track */
6698
6760
  this.UNDECRYPTED_TRACK_GRACE_MS = 2000;
6699
6761
  /**
@@ -6748,8 +6810,7 @@ class FrameCryptor extends BaseFrameCryptor {
6748
6810
  clearTimeout(this.undecryptedTrackTimeout);
6749
6811
  this.undecryptedTrackTimeout = undefined;
6750
6812
  this.participantIdentity = undefined;
6751
- this.lastErrorTimestamp = new Map();
6752
- this.errorCounts = new Map();
6813
+ this.errorLimiter.reset();
6753
6814
  }
6754
6815
  isEnabled() {
6755
6816
  if (this.participantIdentity) {
@@ -6939,58 +7000,21 @@ class FrameCryptor extends BaseFrameCryptor {
6939
7000
  }));
6940
7001
  this.sifTrailer = trailer;
6941
7002
  }
6942
- /**
6943
- * Checks if we should emit an error based on throttling rules to prevent memory leaks
6944
- * @param errorKey - unique key identifying the error context
6945
- * @returns true if the error should be emitted, false otherwise
6946
- */
6947
- shouldEmitError(errorKey) {
6948
- var _a, _b;
6949
- const now = Date.now();
6950
- const lastErrorTime = (_a = this.lastErrorTimestamp.get(errorKey)) !== null && _a !== void 0 ? _a : 0;
6951
- const errorCount = (_b = this.errorCounts.get(errorKey)) !== null && _b !== void 0 ? _b : 0;
6952
- // Reset count if we're in a new time window
6953
- if (now - lastErrorTime > this.ERROR_WINDOW_MS) {
6954
- this.errorCounts.set(errorKey, 0);
6955
- this.lastErrorTimestamp.set(errorKey, now);
6956
- return true;
6957
- }
6958
- // Check if we've exceeded the throttle time
6959
- if (now - lastErrorTime < this.ERROR_THROTTLE_MS) {
6960
- return false;
6961
- }
6962
- // Check if we've exceeded the max errors per window
6963
- if (errorCount >= this.MAX_ERRORS_PER_MINUTE) {
6964
- // Only log a warning once when hitting the limit
6965
- if (errorCount === this.MAX_ERRORS_PER_MINUTE) {
6966
- workerLogger.warn("Suppressing further decryption errors for ".concat(this.participantIdentity), Object.assign(Object.assign({}, this.logContext), {
6967
- errorKey
6968
- }));
6969
- this.errorCounts.set(errorKey, errorCount + 1);
6970
- }
6971
- return false;
6972
- }
6973
- // Update tracking
6974
- this.lastErrorTimestamp.set(errorKey, now);
6975
- this.errorCounts.set(errorKey, errorCount + 1);
6976
- return true;
6977
- }
6978
- /**
6979
- * Emits a throttled error to prevent memory leaks from repeated decryption failures
6980
- * @param error - the CryptorError to emit
6981
- */
6982
7003
  emitThrottledError(error) {
6983
- var _a;
6984
7004
  const errorKey = "".concat(this.participantIdentity, "-").concat(error.reason, "-decrypt");
6985
- if (this.shouldEmitError(errorKey)) {
6986
- const errorCount = (_a = this.errorCounts.get(errorKey)) !== null && _a !== void 0 ? _a : 0;
6987
- if (errorCount > 1) {
6988
- workerLogger.debug("Decryption error (".concat(errorCount, " occurrences in window)"), Object.assign(Object.assign({}, this.logContext), {
6989
- reason: CryptorErrorReason[error.reason]
6990
- }));
6991
- }
6992
- this.emit(CryptorEvent.Error, error);
7005
+ const emit = this.errorLimiter.shouldEmit(errorKey, () => {
7006
+ workerLogger.warn("Suppressing further decryption errors for ".concat(this.participantIdentity), Object.assign(Object.assign({}, this.logContext), {
7007
+ errorKey
7008
+ }));
7009
+ });
7010
+ if (!emit) return;
7011
+ const count = this.errorLimiter.countFor(errorKey);
7012
+ if (count > 1) {
7013
+ workerLogger.debug("Decryption error (".concat(count, " occurrences in window)"), Object.assign(Object.assign({}, this.logContext), {
7014
+ reason: CryptorErrorReason[error.reason]
7015
+ }));
6993
7016
  }
7017
+ this.emit(CryptorEvent.Error, error);
6994
7018
  }
6995
7019
  /**
6996
7020
  * Function that will be injected in a stream and will encrypt the given encoded frames.
@@ -7069,7 +7093,9 @@ class FrameCryptor extends BaseFrameCryptor {
7069
7093
  return controller.enqueue(encodedFrame);
7070
7094
  } catch (e) {
7071
7095
  // TODO: surface this to the app.
7072
- workerLogger.error(e);
7096
+ workerLogger.error("error while encrypting", Object.assign(Object.assign({}, this.logContext), {
7097
+ error: e
7098
+ }));
7073
7099
  }
7074
7100
  } else {
7075
7101
  workerLogger.debug('failed to encrypt, emitting error', this.logContext);
@@ -7268,7 +7294,7 @@ class FrameCryptor extends BaseFrameCryptor {
7268
7294
  throw new CryptorError("valid key missing for participant ".concat(_this.participantIdentity), CryptorErrorReason.InvalidKey, _this.participantIdentity);
7269
7295
  }
7270
7296
  } else {
7271
- throw new CryptorError("Decryption failed: ".concat(error.message), CryptorErrorReason.InvalidKey, _this.participantIdentity);
7297
+ throw new CryptorError("Decryption failed: ".concat(getErrorDescription(error, 'decryption')), CryptorErrorReason.InvalidKey, _this.participantIdentity);
7272
7298
  }
7273
7299
  }
7274
7300
  }();
@@ -7606,7 +7632,22 @@ let useSharedKey = false;
7606
7632
  let sifTrailer;
7607
7633
  let keyProviderOptions = KEY_PROVIDER_DEFAULTS;
7608
7634
  let rtpMap = new Map();
7635
+ const dataDecryptErrorLimiter = new ErrorRateLimiter();
7609
7636
  workerLogger.setDefaultLevel('info');
7637
+ // Forward worker log calls to the main thread so they reach any
7638
+ // setLogExtension consumer installed there. The main-thread workerLogger
7639
+ // re-emits them, which invokes both the console and the extension.
7640
+ workerLogger.methodFactory = methodName => (msg, context) => {
7641
+ postMessage({
7642
+ kind: 'log',
7643
+ data: {
7644
+ level: methodName,
7645
+ msg,
7646
+ context
7647
+ }
7648
+ });
7649
+ };
7650
+ workerLogger.setLevel(workerLogger.getLevel());
7610
7651
  onmessage = ev => {
7611
7652
  messageQueue.run(() => __awaiter(void 0, void 0, void 0, function* () {
7612
7653
  const _ev$data = ev.data,
@@ -7615,7 +7656,7 @@ onmessage = ev => {
7615
7656
  switch (kind) {
7616
7657
  case 'init':
7617
7658
  workerLogger.setLevel(data.loglevel);
7618
- workerLogger.info('worker initialized');
7659
+ workerLogger.info('e2ee worker initialized');
7619
7660
  keyProviderOptions = data.keyProviderOptions;
7620
7661
  useSharedKey = !!data.keyProviderOptions.sharedKey;
7621
7662
  // acknowledge init successful
@@ -7627,6 +7668,9 @@ onmessage = ev => {
7627
7668
  };
7628
7669
  postMessage(ackMsg);
7629
7670
  break;
7671
+ case 'setLogLevel':
7672
+ workerLogger.setLevel(data.level);
7673
+ break;
7630
7674
  case 'enable':
7631
7675
  setEncryptionEnabled(data.enabled, data.participantIdentity);
7632
7676
  workerLogger.info("updated e2ee enabled status for ".concat(data.participantIdentity, " to ").concat(data.enabled));
@@ -7648,11 +7692,6 @@ onmessage = ev => {
7648
7692
  encryptedPayload = _yield$DataCryptor$en.payload,
7649
7693
  iv = _yield$DataCryptor$en.iv,
7650
7694
  keyIndex = _yield$DataCryptor$en.keyIndex;
7651
- console.log('encrypted payload', {
7652
- original: data.payload,
7653
- encrypted: encryptedPayload,
7654
- iv
7655
- });
7656
7695
  postMessage({
7657
7696
  kind: 'encryptDataResponse',
7658
7697
  data: {
@@ -7675,12 +7714,22 @@ onmessage = ev => {
7675
7714
  }
7676
7715
  });
7677
7716
  } catch (error) {
7678
- // Send error back to main thread with uuid so it can reject the corresponding promise
7679
- workerLogger.error('DataCryptor decryption failed', {
7680
- error,
7681
- participantIdentity: data.participantIdentity,
7682
- uuid: data.uuid
7717
+ // Send error back to main thread with uuid so it can reject the corresponding promise.
7718
+ // The error response must always be posted so the awaiting future resolves; only the
7719
+ // log is throttled to avoid flooding when a broken key keeps producing failures.
7720
+ const errorKey = "".concat(data.participantIdentity, "-datadecrypt");
7721
+ const shouldLog = dataDecryptErrorLimiter.shouldEmit(errorKey, () => {
7722
+ workerLogger.warn("Suppressing further data decryption errors for ".concat(data.participantIdentity), {
7723
+ errorKey
7724
+ });
7683
7725
  });
7726
+ if (shouldLog) {
7727
+ workerLogger.error('DataCryptor decryption failed', {
7728
+ error,
7729
+ participantIdentity: data.participantIdentity,
7730
+ uuid: data.uuid
7731
+ });
7732
+ }
7684
7733
  postMessage({
7685
7734
  kind: 'error',
7686
7735
  data: {