livekit-client
Version:
JavaScript/TypeScript client SDK for LiveKit
1 lines • 207 kB
Source Map (JSON)
{"version":3,"file":"livekit-client.e2ee.worker.mjs","sources":["../node_modules/.pnpm/loglevel@1.9.2/node_modules/loglevel/lib/loglevel.js","../src/logger.ts","../node_modules/.pnpm/@livekit+mutex@1.1.1/node_modules/@livekit/mutex/dist/index.mjs","../src/utils/AsyncQueue.ts","../src/e2ee/constants.ts","../src/room/errors.ts","../src/e2ee/errors.ts","../src/e2ee/events.ts","../src/e2ee/utils.ts","../src/e2ee/worker/DataCryptor.ts","../node_modules/.pnpm/events@3.3.0/node_modules/events/events.js","../src/e2ee/worker/naluUtils.ts","../src/e2ee/worker/sifPayload.ts","../src/e2ee/worker/FrameCryptor.ts","../src/e2ee/worker/ParticipantKeyHandler.ts","../src/e2ee/worker/e2ee.worker.ts"],"sourcesContent":["/*\n* loglevel - https://github.com/pimterry/loglevel\n*\n* Copyright (c) 2013 Tim Perry\n* Licensed under the MIT license.\n*/\n(function (root, definition) {\n \"use strict\";\n if (typeof define === 'function' && define.amd) {\n define(definition);\n } else if (typeof module === 'object' && module.exports) {\n module.exports = definition();\n } else {\n root.log = definition();\n }\n}(this, function () {\n \"use strict\";\n\n // Slightly dubious tricks to cut down minimized file size\n var noop = function() {};\n var undefinedType = \"undefined\";\n var isIE = (typeof window !== undefinedType) && (typeof window.navigator !== undefinedType) && (\n /Trident\\/|MSIE /.test(window.navigator.userAgent)\n );\n\n var logMethods = [\n \"trace\",\n \"debug\",\n \"info\",\n \"warn\",\n \"error\"\n ];\n\n var _loggersByName = {};\n var defaultLogger = null;\n\n // Cross-browser bind equivalent that works at least back to IE6\n function bindMethod(obj, methodName) {\n var method = obj[methodName];\n if (typeof method.bind === 'function') {\n return method.bind(obj);\n } else {\n try {\n return Function.prototype.bind.call(method, obj);\n } catch (e) {\n // Missing bind shim or IE8 + Modernizr, fallback to wrapping\n return function() {\n return Function.prototype.apply.apply(method, [obj, arguments]);\n };\n }\n }\n }\n\n // Trace() doesn't print the message in IE, so for that case we need to wrap it\n function traceForIE() {\n if (console.log) {\n if (console.log.apply) {\n console.log.apply(console, arguments);\n } else {\n // In old IE, native console methods themselves don't have apply().\n Function.prototype.apply.apply(console.log, [console, arguments]);\n }\n }\n if (console.trace) console.trace();\n }\n\n // Build the best logging method possible for this env\n // Wherever possible we want to bind, not wrap, to preserve stack traces\n function realMethod(methodName) {\n if (methodName === 'debug') {\n methodName = 'log';\n }\n\n if (typeof console === undefinedType) {\n return false; // No method possible, for now - fixed later by enableLoggingWhenConsoleArrives\n } else if (methodName === 'trace' && isIE) {\n return traceForIE;\n } else if (console[methodName] !== undefined) {\n return bindMethod(console, methodName);\n } else if (console.log !== undefined) {\n return bindMethod(console, 'log');\n } else {\n return noop;\n }\n }\n\n // These private functions always need `this` to be set properly\n\n function replaceLoggingMethods() {\n /*jshint validthis:true */\n var level = this.getLevel();\n\n // Replace the actual methods.\n for (var i = 0; i < logMethods.length; i++) {\n var methodName = logMethods[i];\n this[methodName] = (i < level) ?\n noop :\n this.methodFactory(methodName, level, this.name);\n }\n\n // Define log.log as an alias for log.debug\n this.log = this.debug;\n\n // Return any important warnings.\n if (typeof console === undefinedType && level < this.levels.SILENT) {\n return \"No console available for logging\";\n }\n }\n\n // In old IE versions, the console isn't present until you first open it.\n // We build realMethod() replacements here that regenerate logging methods\n function enableLoggingWhenConsoleArrives(methodName) {\n return function () {\n if (typeof console !== undefinedType) {\n replaceLoggingMethods.call(this);\n this[methodName].apply(this, arguments);\n }\n };\n }\n\n // By default, we use closely bound real methods wherever possible, and\n // otherwise we wait for a console to appear, and then try again.\n function defaultMethodFactory(methodName, _level, _loggerName) {\n /*jshint validthis:true */\n return realMethod(methodName) ||\n enableLoggingWhenConsoleArrives.apply(this, arguments);\n }\n\n function Logger(name, factory) {\n // Private instance variables.\n var self = this;\n /**\n * The level inherited from a parent logger (or a global default). We\n * cache this here rather than delegating to the parent so that it stays\n * in sync with the actual logging methods that we have installed (the\n * parent could change levels but we might not have rebuilt the loggers\n * in this child yet).\n * @type {number}\n */\n var inheritedLevel;\n /**\n * The default level for this logger, if any. If set, this overrides\n * `inheritedLevel`.\n * @type {number|null}\n */\n var defaultLevel;\n /**\n * A user-specific level for this logger. If set, this overrides\n * `defaultLevel`.\n * @type {number|null}\n */\n var userLevel;\n\n var storageKey = \"loglevel\";\n if (typeof name === \"string\") {\n storageKey += \":\" + name;\n } else if (typeof name === \"symbol\") {\n storageKey = undefined;\n }\n\n function persistLevelIfPossible(levelNum) {\n var levelName = (logMethods[levelNum] || 'silent').toUpperCase();\n\n if (typeof window === undefinedType || !storageKey) return;\n\n // Use localStorage if available\n try {\n window.localStorage[storageKey] = levelName;\n return;\n } catch (ignore) {}\n\n // Use session cookie as fallback\n try {\n window.document.cookie =\n encodeURIComponent(storageKey) + \"=\" + levelName + \";\";\n } catch (ignore) {}\n }\n\n function getPersistedLevel() {\n var storedLevel;\n\n if (typeof window === undefinedType || !storageKey) return;\n\n try {\n storedLevel = window.localStorage[storageKey];\n } catch (ignore) {}\n\n // Fallback to cookies if local storage gives us nothing\n if (typeof storedLevel === undefinedType) {\n try {\n var cookie = window.document.cookie;\n var cookieName = encodeURIComponent(storageKey);\n var location = cookie.indexOf(cookieName + \"=\");\n if (location !== -1) {\n storedLevel = /^([^;]+)/.exec(\n cookie.slice(location + cookieName.length + 1)\n )[1];\n }\n } catch (ignore) {}\n }\n\n // If the stored level is not valid, treat it as if nothing was stored.\n if (self.levels[storedLevel] === undefined) {\n storedLevel = undefined;\n }\n\n return storedLevel;\n }\n\n function clearPersistedLevel() {\n if (typeof window === undefinedType || !storageKey) return;\n\n // Use localStorage if available\n try {\n window.localStorage.removeItem(storageKey);\n } catch (ignore) {}\n\n // Use session cookie as fallback\n try {\n window.document.cookie =\n encodeURIComponent(storageKey) + \"=; expires=Thu, 01 Jan 1970 00:00:00 UTC\";\n } catch (ignore) {}\n }\n\n function normalizeLevel(input) {\n var level = input;\n if (typeof level === \"string\" && self.levels[level.toUpperCase()] !== undefined) {\n level = self.levels[level.toUpperCase()];\n }\n if (typeof level === \"number\" && level >= 0 && level <= self.levels.SILENT) {\n return level;\n } else {\n throw new TypeError(\"log.setLevel() called with invalid level: \" + input);\n }\n }\n\n /*\n *\n * Public logger API - see https://github.com/pimterry/loglevel for details\n *\n */\n\n self.name = name;\n\n self.levels = { \"TRACE\": 0, \"DEBUG\": 1, \"INFO\": 2, \"WARN\": 3,\n \"ERROR\": 4, \"SILENT\": 5};\n\n self.methodFactory = factory || defaultMethodFactory;\n\n self.getLevel = function () {\n if (userLevel != null) {\n return userLevel;\n } else if (defaultLevel != null) {\n return defaultLevel;\n } else {\n return inheritedLevel;\n }\n };\n\n self.setLevel = function (level, persist) {\n userLevel = normalizeLevel(level);\n if (persist !== false) { // defaults to true\n persistLevelIfPossible(userLevel);\n }\n\n // NOTE: in v2, this should call rebuild(), which updates children.\n return replaceLoggingMethods.call(self);\n };\n\n self.setDefaultLevel = function (level) {\n defaultLevel = normalizeLevel(level);\n if (!getPersistedLevel()) {\n self.setLevel(level, false);\n }\n };\n\n self.resetLevel = function () {\n userLevel = null;\n clearPersistedLevel();\n replaceLoggingMethods.call(self);\n };\n\n self.enableAll = function(persist) {\n self.setLevel(self.levels.TRACE, persist);\n };\n\n self.disableAll = function(persist) {\n self.setLevel(self.levels.SILENT, persist);\n };\n\n self.rebuild = function () {\n if (defaultLogger !== self) {\n inheritedLevel = normalizeLevel(defaultLogger.getLevel());\n }\n replaceLoggingMethods.call(self);\n\n if (defaultLogger === self) {\n for (var childName in _loggersByName) {\n _loggersByName[childName].rebuild();\n }\n }\n };\n\n // Initialize all the internal levels.\n inheritedLevel = normalizeLevel(\n defaultLogger ? defaultLogger.getLevel() : \"WARN\"\n );\n var initialLevel = getPersistedLevel();\n if (initialLevel != null) {\n userLevel = normalizeLevel(initialLevel);\n }\n replaceLoggingMethods.call(self);\n }\n\n /*\n *\n * Top-level API\n *\n */\n\n defaultLogger = new Logger();\n\n defaultLogger.getLogger = function getLogger(name) {\n if ((typeof name !== \"symbol\" && typeof name !== \"string\") || name === \"\") {\n throw new TypeError(\"You must supply a name when creating a logger.\");\n }\n\n var logger = _loggersByName[name];\n if (!logger) {\n logger = _loggersByName[name] = new Logger(\n name,\n defaultLogger.methodFactory\n );\n }\n return logger;\n };\n\n // Grab the current global log variable in case of overwrite\n var _log = (typeof window !== undefinedType) ? window.log : undefined;\n defaultLogger.noConflict = function() {\n if (typeof window !== undefinedType &&\n window.log === defaultLogger) {\n window.log = _log;\n }\n\n return defaultLogger;\n };\n\n defaultLogger.getLoggers = function getLoggers() {\n return _loggersByName;\n };\n\n // ES6 default export, for compatibility\n defaultLogger['default'] = defaultLogger;\n\n return defaultLogger;\n}));\n","import * as log from 'loglevel';\n\nexport enum LogLevel {\n trace = 0,\n debug = 1,\n info = 2,\n warn = 3,\n error = 4,\n silent = 5,\n}\n\nexport enum LoggerNames {\n Default = 'livekit',\n Room = 'livekit-room',\n TokenSource = 'livekit-token-source',\n Participant = 'livekit-participant',\n Track = 'livekit-track',\n Publication = 'livekit-track-publication',\n Engine = 'livekit-engine',\n Signal = 'livekit-signal',\n PCManager = 'livekit-pc-manager',\n PCTransport = 'livekit-pc-transport',\n E2EE = 'lk-e2ee',\n DataTracks = 'livekit-data-tracks',\n}\n\ntype LogLevelString = keyof typeof LogLevel;\n\nexport type StructuredLogger = log.Logger & {\n trace: (msg: string, context?: object) => void;\n debug: (msg: string, context?: object) => void;\n info: (msg: string, context?: object) => void;\n warn: (msg: string, context?: object) => void;\n error: (msg: string, context?: object) => void;\n setDefaultLevel: (level: log.LogLevelDesc) => void;\n setLevel: (level: log.LogLevelDesc) => void;\n getLevel: () => number;\n};\n\nlet livekitLogger = log.getLogger('livekit');\nconst livekitLoggers = Object.values(LoggerNames).map((name) => log.getLogger(name));\n\nlivekitLogger.setDefaultLevel(LogLevel.info);\n\nexport default livekitLogger as StructuredLogger;\n\n/**\n * @internal\n */\nexport function getLogger(name: string) {\n const logger = log.getLogger(name);\n logger.setDefaultLevel(livekitLogger.getLevel());\n return logger as StructuredLogger;\n}\n\nexport function setLogLevel(level: LogLevel | LogLevelString, loggerName?: LoggerNames) {\n if (loggerName) {\n log.getLogger(loggerName).setLevel(level);\n } else {\n for (const logger of livekitLoggers) {\n logger.setLevel(level);\n }\n }\n}\n\nexport type LogExtension = (level: LogLevel, msg: string, context?: object) => void;\n\n/**\n * use this to hook into the logging function to allow sending internal livekit logs to third party services\n * if set, the browser logs will lose their stacktrace information (see https://github.com/pimterry/loglevel#writing-plugins)\n */\nexport function setLogExtension(extension: LogExtension, logger?: StructuredLogger) {\n const loggers = logger ? [logger] : livekitLoggers;\n\n loggers.forEach((logR) => {\n const originalFactory = logR.methodFactory;\n\n logR.methodFactory = (methodName, configLevel, loggerName) => {\n const rawMethod = originalFactory(methodName, configLevel, loggerName);\n\n const logLevel = LogLevel[methodName as LogLevelString];\n const needLog = logLevel >= configLevel && logLevel < LogLevel.silent;\n\n return (msg, context?: [msg: string, context: object]) => {\n if (context) rawMethod(msg, context);\n else rawMethod(msg);\n if (needLog) {\n extension(logLevel, msg, context);\n }\n };\n };\n logR.setLevel(logR.getLevel());\n });\n}\n\nexport const workerLogger = log.getLogger('lk-e2ee') as StructuredLogger;\n","var e = Object.defineProperty;\nvar h = (i, s, t) => s in i ? e(i, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : i[s] = t;\nvar o = (i, s, t) => h(i, typeof s != \"symbol\" ? s + \"\" : s, t);\nclass _ {\n constructor() {\n o(this, \"_locking\");\n o(this, \"_locks\");\n this._locking = Promise.resolve(), this._locks = 0;\n }\n isLocked() {\n return this._locks > 0;\n }\n lock() {\n this._locks += 1;\n let s;\n const t = new Promise(\n (l) => s = () => {\n this._locks -= 1, l();\n }\n ), c = this._locking.then(() => s);\n return this._locking = this._locking.then(() => t), c;\n }\n}\nclass n {\n constructor(s) {\n o(this, \"_queue\");\n o(this, \"_limit\");\n o(this, \"_locks\");\n this._queue = [], this._limit = s, this._locks = 0;\n }\n isLocked() {\n return this._locks >= this._limit;\n }\n async lock() {\n return this.isLocked() ? new Promise((s) => {\n this._queue.push(() => {\n this._locks++, s(this._unlock.bind(this));\n });\n }) : (this._locks++, this._unlock.bind(this));\n }\n _unlock() {\n if (this._locks--, this._queue.length && !this.isLocked()) {\n const s = this._queue.shift();\n s == null || s();\n }\n }\n}\nexport {\n n as MultiMutex,\n _ as Mutex\n};\n//# sourceMappingURL=index.mjs.map\n","import { Mutex } from '@livekit/mutex';\n\ntype QueueTask<T> = () => PromiseLike<T>;\n\nenum QueueTaskStatus {\n 'WAITING',\n 'RUNNING',\n 'COMPLETED',\n}\n\ntype QueueTaskInfo = {\n id: number;\n enqueuedAt: number;\n executedAt?: number;\n status: QueueTaskStatus;\n};\n\nexport class AsyncQueue {\n private pendingTasks: Map<number, QueueTaskInfo>;\n\n private taskMutex: Mutex;\n\n private nextTaskIndex: number;\n\n constructor() {\n this.pendingTasks = new Map();\n this.taskMutex = new Mutex();\n this.nextTaskIndex = 0;\n }\n\n async run<T>(task: QueueTask<T>) {\n const taskInfo: QueueTaskInfo = {\n id: this.nextTaskIndex++,\n enqueuedAt: Date.now(),\n status: QueueTaskStatus.WAITING,\n };\n this.pendingTasks.set(taskInfo.id, taskInfo);\n const unlock = await this.taskMutex.lock();\n try {\n taskInfo.executedAt = Date.now();\n taskInfo.status = QueueTaskStatus.RUNNING;\n return await task();\n } finally {\n taskInfo.status = QueueTaskStatus.COMPLETED;\n this.pendingTasks.delete(taskInfo.id);\n unlock();\n }\n }\n\n async flush() {\n return this.run(async () => {});\n }\n\n snapshot() {\n return Array.from(this.pendingTasks.values());\n }\n}\n","import type { KeyProviderOptions } from './types';\n\nexport const ENCRYPTION_ALGORITHM = 'AES-GCM';\n\n// How many consecutive frames can fail decrypting before a particular key gets marked as invalid\nexport const DECRYPTION_FAILURE_TOLERANCE = 10;\n\n// We copy the first bytes of the VP8 payload unencrypted.\n// For keyframes this is 10 bytes, for non-keyframes (delta) 3. See\n// https://tools.ietf.org/html/rfc6386#section-9.1\n// This allows the bridge to continue detecting keyframes (only one byte needed in the JVB)\n// and is also a bit easier for the VP8 decoder (i.e. it generates funny garbage pictures\n// instead of being unable to decode).\n// This is a bit for show and we might want to reduce to 1 unconditionally in the final version.\n//\n// For audio (where frame.type is not set) we do not encrypt the opus TOC byte:\n// https://tools.ietf.org/html/rfc6716#section-3.1\nexport const UNENCRYPTED_BYTES = {\n key: 10,\n delta: 3,\n audio: 1, // frame.type is not set on audio, so this is set manually\n empty: 0,\n} as const;\n\n/* We use a 12 byte bit IV. This is signalled in plain together with the\n packet. See https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt#parameters */\nexport const IV_LENGTH = 12;\n\n// flag set to indicate that e2ee has been setup for sender/receiver;\nexport const E2EE_FLAG = 'lk_e2ee';\n\nexport const SALT = 'LKFrameEncryptionKey';\n\nexport const KEY_PROVIDER_DEFAULTS: KeyProviderOptions = {\n sharedKey: false,\n ratchetSalt: SALT,\n ratchetWindowSize: 8,\n failureTolerance: DECRYPTION_FAILURE_TOLERANCE,\n keyringSize: 16,\n keySize: 128,\n} as const;\n\nexport const MAX_SIF_COUNT = 100;\nexport const MAX_SIF_DURATION = 2000;\n","import { DisconnectReason, RequestResponse_Reason } from '@livekit/protocol';\n\n/** Base error that all LiveKit specific custom errors inherit from. */\nexport class LivekitError extends Error {\n code: number;\n\n // More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause\n cause?: unknown;\n\n constructor(code: number, message?: string, options?: { cause?: unknown }) {\n super(message || 'an error has occurred');\n this.name = 'LiveKitError';\n this.code = code;\n\n if (typeof options?.cause !== 'undefined') {\n this.cause = options?.cause;\n }\n }\n}\n\n/**\n * LiveKit specific error type representing an error with an associated set of reasons.\n * Use this to represent an error with multiple different but contextually related variants.\n * */\nexport abstract class LivekitReasonedError<Reason> extends LivekitError {\n abstract reason: Reason;\n\n abstract reasonName: string;\n}\n\nexport class SimulatedError extends LivekitError {\n readonly name = 'simulated';\n\n constructor(message = 'Simulated failure') {\n super(-1, message);\n }\n}\n\nexport enum ConnectionErrorReason {\n NotAllowed,\n ServerUnreachable,\n InternalError,\n Cancelled,\n LeaveRequest,\n Timeout,\n WebSocket,\n ServiceNotFound,\n}\n\ntype NotAllowed = {\n reason: ConnectionErrorReason.NotAllowed;\n status: number;\n context?: unknown;\n};\n\ntype InternalError = {\n reason: ConnectionErrorReason.InternalError;\n status: never;\n context?: { status?: number; statusText?: string };\n};\n\ntype ConnectionTimeout = {\n reason: ConnectionErrorReason.Timeout;\n status: never;\n context: never;\n};\n\ntype LeaveRequest = {\n reason: ConnectionErrorReason.LeaveRequest;\n status: never;\n context: DisconnectReason;\n};\n\ntype Cancelled = {\n reason: ConnectionErrorReason.Cancelled;\n status: never;\n context: never;\n};\n\ntype ServerUnreachable = {\n reason: ConnectionErrorReason.ServerUnreachable;\n status?: number;\n context?: never;\n};\n\ntype WebSocket = {\n reason: ConnectionErrorReason.WebSocket;\n status?: number;\n context?: string;\n};\n\ntype ServiceNotFound = {\n reason: ConnectionErrorReason.ServiceNotFound;\n status: never;\n context: string;\n};\n\ntype ConnectionErrorVariants =\n | NotAllowed\n | ConnectionTimeout\n | LeaveRequest\n | InternalError\n | Cancelled\n | ServerUnreachable\n | WebSocket\n | ServiceNotFound;\n\nexport class ConnectionError<\n Variant extends ConnectionErrorVariants = ConnectionErrorVariants,\n> extends LivekitReasonedError<Variant['reason']> {\n status?: Variant['status'];\n\n context: Variant['context'];\n\n reason: Variant['reason'];\n\n reasonName: string;\n\n readonly name = 'ConnectionError';\n\n protected constructor(\n message: string,\n reason: Variant['reason'],\n status?: Variant['status'],\n context?: Variant['context'],\n ) {\n super(1, message);\n this.status = status;\n this.reason = reason;\n this.context = context;\n this.reasonName = ConnectionErrorReason[reason];\n }\n\n static notAllowed(message: string, status: number, context?: unknown) {\n return new ConnectionError<NotAllowed>(\n message,\n ConnectionErrorReason.NotAllowed,\n status,\n context,\n );\n }\n\n static timeout(message: string) {\n return new ConnectionError<ConnectionTimeout>(message, ConnectionErrorReason.Timeout);\n }\n\n static leaveRequest(message: string, context: DisconnectReason) {\n return new ConnectionError<LeaveRequest>(\n message,\n ConnectionErrorReason.LeaveRequest,\n undefined,\n context,\n );\n }\n\n static internal(message: string, context?: { status?: number; statusText?: string }) {\n return new ConnectionError<InternalError>(\n message,\n ConnectionErrorReason.InternalError,\n undefined,\n context,\n );\n }\n\n static cancelled(message: string) {\n return new ConnectionError<Cancelled>(message, ConnectionErrorReason.Cancelled);\n }\n\n static serverUnreachable(message: string, status?: number) {\n return new ConnectionError<ServerUnreachable>(\n message,\n ConnectionErrorReason.ServerUnreachable,\n status,\n );\n }\n\n static websocket(message: string, status?: number, reason?: string) {\n return new ConnectionError<WebSocket>(message, ConnectionErrorReason.WebSocket, status, reason);\n }\n\n static serviceNotFound(message: string, serviceName: 'v0-rtc') {\n return new ConnectionError<ServiceNotFound>(\n message,\n ConnectionErrorReason.ServiceNotFound,\n undefined,\n serviceName,\n );\n }\n}\n\nexport class DeviceUnsupportedError extends LivekitError {\n readonly name = 'DeviceUnsupportedError';\n\n constructor(message?: string) {\n super(21, message ?? 'device is unsupported');\n }\n}\n\nexport class TrackInvalidError extends LivekitError {\n readonly name = 'TrackInvalidError';\n\n constructor(message?: string) {\n super(20, message ?? 'track is invalid');\n }\n}\n\nexport class UnsupportedServer extends LivekitError {\n readonly name = 'UnsupportedServer';\n\n constructor(message?: string) {\n super(10, message ?? 'unsupported server');\n }\n}\n\nexport class UnexpectedConnectionState extends LivekitError {\n readonly name = 'UnexpectedConnectionState';\n\n constructor(message?: string) {\n super(12, message ?? 'unexpected connection state');\n }\n}\n\nexport class NegotiationError extends LivekitError {\n readonly name = 'NegotiationError';\n\n constructor(message?: string) {\n super(13, message ?? 'unable to negotiate');\n }\n}\n\nexport class PublishDataError extends LivekitError {\n readonly name = 'PublishDataError';\n\n constructor(message?: string) {\n super(14, message ?? 'unable to publish data');\n }\n}\n\nexport class PublishTrackError extends LivekitError {\n readonly name = 'PublishTrackError';\n\n status: number;\n\n constructor(message: string, status: number) {\n super(15, message);\n this.status = status;\n }\n}\n\nexport type RequestErrorReason =\n | Exclude<RequestResponse_Reason, RequestResponse_Reason.OK>\n | 'TimeoutError';\n\nexport class SignalRequestError extends LivekitReasonedError<RequestErrorReason> {\n readonly name = 'SignalRequestError';\n\n reason: RequestErrorReason;\n\n reasonName: string;\n\n constructor(message: string, reason: RequestErrorReason) {\n super(15, message);\n this.reason = reason;\n this.reasonName = typeof reason === 'string' ? reason : RequestResponse_Reason[reason];\n }\n}\n\n// NOTE: matches with https://github.com/livekit/client-sdk-swift/blob/f37bbd260d61e165084962db822c79f995f1a113/Sources/LiveKit/DataStream/StreamError.swift#L17\nexport enum DataStreamErrorReason {\n // Unable to open a stream with the same ID more than once.\n AlreadyOpened = 0,\n\n // Stream closed abnormally by remote participant.\n AbnormalEnd = 1,\n\n // Incoming chunk data could not be decoded.\n DecodeFailed = 2,\n\n // Read length exceeded total length specified in stream header.\n LengthExceeded = 3,\n\n // Read length less than total length specified in stream header.\n Incomplete = 4,\n\n // Unable to register a stream handler more than once.\n HandlerAlreadyRegistered = 7,\n\n // Encryption type mismatch.\n EncryptionTypeMismatch = 8,\n}\n\nexport class DataStreamError extends LivekitReasonedError<DataStreamErrorReason> {\n readonly name = 'DataStreamError';\n\n reason: DataStreamErrorReason;\n\n reasonName: string;\n\n constructor(message: string, reason: DataStreamErrorReason) {\n super(16, message);\n this.reason = reason;\n this.reasonName = DataStreamErrorReason[reason];\n }\n}\n\nexport class SignalReconnectError extends LivekitError {\n readonly name = 'SignalReconnectError';\n\n constructor(message?: string) {\n super(18, message);\n }\n}\n\nexport enum MediaDeviceFailure {\n // user rejected permissions\n PermissionDenied = 'PermissionDenied',\n // device is not available\n NotFound = 'NotFound',\n // device is in use. On Windows, only a single tab may get access to a device at a time.\n DeviceInUse = 'DeviceInUse',\n Other = 'Other',\n}\n\nexport namespace MediaDeviceFailure {\n export function getFailure(error: any): MediaDeviceFailure | undefined {\n if (error && 'name' in error) {\n if (error.name === 'NotFoundError' || error.name === 'DevicesNotFoundError') {\n return MediaDeviceFailure.NotFound;\n }\n if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') {\n return MediaDeviceFailure.PermissionDenied;\n }\n if (error.name === 'NotReadableError' || error.name === 'TrackStartError') {\n return MediaDeviceFailure.DeviceInUse;\n }\n return MediaDeviceFailure.Other;\n }\n }\n}\n","import { LivekitError } from '../room/errors';\n\nexport enum CryptorErrorReason {\n InvalidKey = 0,\n MissingKey = 1,\n InternalError = 2,\n}\n\nexport class CryptorError extends LivekitError {\n reason: CryptorErrorReason;\n\n participantIdentity?: string;\n\n constructor(\n message?: string,\n reason: CryptorErrorReason = CryptorErrorReason.InternalError,\n participantIdentity?: string,\n ) {\n super(40, message);\n this.reason = reason;\n this.participantIdentity = participantIdentity;\n }\n}\n","import type Participant from '../room/participant/Participant';\nimport type { CryptorError } from './errors';\nimport type { KeyInfo, RatchetResult } from './types';\n\nexport enum KeyProviderEvent {\n SetKey = 'setKey',\n /** Event for requesting to ratchet the key used to encrypt the stream */\n RatchetRequest = 'ratchetRequest',\n /** Emitted when a key is ratcheted. Could be after auto-ratcheting on decryption failure or\n * following a `RatchetRequest`, will contain the ratcheted key material */\n KeyRatcheted = 'keyRatcheted',\n}\n\nexport type KeyProviderCallbacks = {\n [KeyProviderEvent.SetKey]: (keyInfo: KeyInfo, updateCurrentKeyIndex: boolean) => void;\n [KeyProviderEvent.RatchetRequest]: (participantIdentity?: string, keyIndex?: number) => void;\n [KeyProviderEvent.KeyRatcheted]: (\n ratchetedResult: RatchetResult,\n participantIdentity?: string,\n keyIndex?: number,\n ) => void;\n};\n\nexport enum KeyHandlerEvent {\n /** Emitted when a key has been ratcheted. Is emitted when any key has been ratcheted\n * i.e. when the FrameCryptor tried to ratchet when decryption is failing */\n KeyRatcheted = 'keyRatcheted',\n}\n\nexport type ParticipantKeyHandlerCallbacks = {\n [KeyHandlerEvent.KeyRatcheted]: (\n ratchetResult: RatchetResult,\n participantIdentity: string,\n keyIndex?: number,\n ) => void;\n};\n\nexport enum EncryptionEvent {\n ParticipantEncryptionStatusChanged = 'participantEncryptionStatusChanged',\n EncryptionError = 'encryptionError',\n}\n\nexport type E2EEManagerCallbacks = {\n [EncryptionEvent.ParticipantEncryptionStatusChanged]: (\n enabled: boolean,\n participant: Participant,\n ) => void;\n [EncryptionEvent.EncryptionError]: (error: Error, participantIdentity?: string) => void;\n};\n\nexport type CryptorCallbacks = {\n [CryptorEvent.Error]: (error: CryptorError) => void;\n};\n\nexport enum CryptorEvent {\n Error = 'cryptorError',\n}\n","import { type DataPacket, EncryptedPacketPayload } from '@livekit/protocol';\nimport { ENCRYPTION_ALGORITHM } from './constants';\nimport type { KeyProviderOptions } from './types';\n\nexport function isE2EESupported() {\n return isInsertableStreamSupported() || isScriptTransformSupported();\n}\n\nexport function isScriptTransformSupported() {\n // @ts-ignore\n return typeof window.RTCRtpScriptTransform !== 'undefined';\n}\n\nexport function isInsertableStreamSupported() {\n return (\n typeof window.RTCRtpSender !== 'undefined' &&\n // @ts-ignore\n typeof window.RTCRtpSender.prototype.createEncodedStreams !== 'undefined'\n );\n}\n\nexport function isVideoFrame(\n frame: RTCEncodedAudioFrame | RTCEncodedVideoFrame,\n): frame is RTCEncodedVideoFrame {\n return 'type' in frame;\n}\n\nexport async function importKey(\n keyBytes: Uint8Array | ArrayBuffer,\n algorithm: string | { name: string } = { name: ENCRYPTION_ALGORITHM },\n usage: 'derive' | 'encrypt' = 'encrypt',\n) {\n // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey\n return crypto.subtle.importKey(\n 'raw',\n keyBytes,\n algorithm,\n false,\n usage === 'derive' ? ['deriveBits', 'deriveKey'] : ['encrypt', 'decrypt'],\n );\n}\n\nexport async function createKeyMaterialFromString(password: string) {\n let enc = new TextEncoder();\n\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n enc.encode(password),\n {\n name: 'PBKDF2',\n },\n false,\n ['deriveBits', 'deriveKey'],\n );\n\n return keyMaterial;\n}\n\nexport async function createKeyMaterialFromBuffer(cryptoBuffer: ArrayBuffer) {\n const keyMaterial = await crypto.subtle.importKey('raw', cryptoBuffer, 'HKDF', false, [\n 'deriveBits',\n 'deriveKey',\n ]);\n\n return keyMaterial;\n}\n\nfunction getAlgoOptions(algorithmName: string, salt: string) {\n const textEncoder = new TextEncoder();\n const encodedSalt = textEncoder.encode(salt);\n switch (algorithmName) {\n case 'HKDF':\n return {\n name: 'HKDF',\n salt: encodedSalt,\n hash: 'SHA-256',\n info: new ArrayBuffer(128),\n };\n case 'PBKDF2': {\n return {\n name: 'PBKDF2',\n salt: encodedSalt,\n hash: 'SHA-256',\n iterations: 100000,\n };\n }\n default:\n throw new Error(`algorithm ${algorithmName} is currently unsupported`);\n }\n}\n\n/**\n * Derives a set of keys from the master key.\n * See https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.1\n */\nexport async function deriveKeys(material: CryptoKey, options: KeyProviderOptions) {\n const algorithmOptions = getAlgoOptions(material.algorithm.name, options.ratchetSalt);\n\n // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey#HKDF\n // https://developer.mozilla.org/en-US/docs/Web/API/HkdfParams\n const encryptionKey = await crypto.subtle.deriveKey(\n algorithmOptions,\n material,\n {\n name: ENCRYPTION_ALGORITHM,\n length: options.keySize,\n },\n false,\n ['encrypt', 'decrypt'],\n );\n\n return { material, encryptionKey };\n}\n\nexport function createE2EEKey(): Uint8Array {\n return window.crypto.getRandomValues(new Uint8Array(32));\n}\n\n/**\n * Ratchets a key. See\n * https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.5.1\n */\nexport async function ratchet(material: CryptoKey, salt: string): Promise<ArrayBuffer> {\n const algorithmOptions = getAlgoOptions(material.algorithm.name, salt);\n\n // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveBits\n return crypto.subtle.deriveBits(algorithmOptions, material, 256);\n}\n\nexport function needsRbspUnescaping(frameData: Uint8Array) {\n for (var i = 0; i < frameData.length - 3; i++) {\n if (frameData[i] == 0 && frameData[i + 1] == 0 && frameData[i + 2] == 3) return true;\n }\n return false;\n}\n\nexport function parseRbsp(stream: Uint8Array): Uint8Array {\n const dataOut: number[] = [];\n var length = stream.length;\n for (var i = 0; i < stream.length; ) {\n // Be careful about over/underflow here. byte_length_ - 3 can underflow, and\n // i + 3 can overflow, but byte_length_ - i can't, because i < byte_length_\n // above, and that expression will produce the number of bytes left in\n // the stream including the byte at i.\n if (length - i >= 3 && !stream[i] && !stream[i + 1] && stream[i + 2] == 3) {\n // Two rbsp bytes.\n dataOut.push(stream[i++]);\n dataOut.push(stream[i++]);\n // Skip the emulation byte.\n i++;\n } else {\n // Single rbsp byte.\n dataOut.push(stream[i++]);\n }\n }\n return new Uint8Array(dataOut);\n}\n\nconst kZerosInStartSequence = 2;\nconst kEmulationByte = 3;\n\nexport function writeRbsp(data_in: Uint8Array): Uint8Array {\n const dataOut: number[] = [];\n var numConsecutiveZeros = 0;\n for (var i = 0; i < data_in.length; ++i) {\n var byte = data_in[i];\n if (byte <= kEmulationByte && numConsecutiveZeros >= kZerosInStartSequence) {\n // Need to escape.\n dataOut.push(kEmulationByte);\n numConsecutiveZeros = 0;\n }\n dataOut.push(byte);\n if (byte == 0) {\n ++numConsecutiveZeros;\n } else {\n numConsecutiveZeros = 0;\n }\n }\n return new Uint8Array(dataOut);\n}\n\nexport function asEncryptablePacket(packet: DataPacket): EncryptedPacketPayload | undefined {\n if (\n packet.value?.case !== 'sipDtmf' &&\n packet.value?.case !== 'metrics' &&\n packet.value?.case !== 'speaker' &&\n packet.value?.case !== 'transcription' &&\n packet.value?.case !== 'encryptedPacket'\n ) {\n return new EncryptedPacketPayload({\n value: packet.value,\n });\n }\n return undefined;\n}\n","import { workerLogger } from '../../logger';\nimport { ENCRYPTION_ALGORITHM } from '../constants';\nimport { CryptorError, CryptorErrorReason } from '../errors';\nimport type { DecodeRatchetOptions, KeySet, RatchetResult } from '../types';\nimport { deriveKeys } from '../utils';\nimport type { ParticipantKeyHandler } from './ParticipantKeyHandler';\n\nexport class DataCryptor {\n private static sendCount = 0;\n\n private static makeIV(timestamp: number) {\n const iv = new ArrayBuffer(12);\n const ivView = new DataView(iv);\n const randomBytes = crypto.getRandomValues(new Uint32Array(1));\n ivView.setUint32(0, randomBytes[0]);\n ivView.setUint32(4, timestamp);\n ivView.setUint32(8, timestamp - (DataCryptor.sendCount % 0xffff));\n DataCryptor.sendCount++;\n\n return iv;\n }\n\n static async encrypt(\n data: Uint8Array,\n keys: ParticipantKeyHandler,\n ): Promise<{\n payload: Uint8Array;\n iv: Uint8Array;\n keyIndex: number;\n }> {\n const iv = DataCryptor.makeIV(performance.now());\n const keySet = await keys.getKeySet();\n if (!keySet) {\n throw new Error('No key set found');\n }\n\n const cipherText = await crypto.subtle.encrypt(\n {\n name: ENCRYPTION_ALGORITHM,\n iv,\n },\n keySet.encryptionKey,\n new Uint8Array(data),\n );\n\n return {\n payload: new Uint8Array(cipherText),\n iv: new Uint8Array(iv),\n keyIndex: keys.getCurrentKeyIndex(),\n };\n }\n\n static async decrypt(\n data: Uint8Array,\n iv: Uint8Array,\n keys: ParticipantKeyHandler,\n keyIndex: number = 0,\n initialMaterial?: KeySet,\n ratchetOpts: DecodeRatchetOptions = { ratchetCount: 0 },\n ): Promise<{\n payload: Uint8Array;\n }> {\n const keySet = await keys.getKeySet(keyIndex);\n if (!keySet) {\n throw new Error('No key set found');\n }\n\n try {\n const plainText = await crypto.subtle.decrypt(\n {\n name: ENCRYPTION_ALGORITHM,\n iv,\n },\n keySet.encryptionKey,\n new Uint8Array(data),\n );\n return {\n payload: new Uint8Array(plainText),\n };\n } catch (error: any) {\n if (keys.keyProviderOptions.ratchetWindowSize > 0) {\n if (ratchetOpts.ratchetCount < keys.keyProviderOptions.ratchetWindowSize) {\n workerLogger.debug(\n `DataCryptor: ratcheting key attempt ${ratchetOpts.ratchetCount} of ${\n keys.keyProviderOptions.ratchetWindowSize\n }, for data packet`,\n );\n\n let ratchetedKeySet: KeySet | undefined;\n let ratchetResult: RatchetResult | undefined;\n if ((initialMaterial ?? keySet) === keys.getKeySet(keyIndex)) {\n // only ratchet if the currently set key is still the same as the one used to decrypt this frame\n // if not, it might be that a different frame has already ratcheted and we try with that one first\n ratchetResult = await keys.ratchetKey(keyIndex, false);\n\n ratchetedKeySet = await deriveKeys(ratchetResult.cryptoKey, keys.keyProviderOptions);\n }\n\n const decryptedData = await DataCryptor.decrypt(\n data,\n iv,\n keys,\n keyIndex,\n initialMaterial,\n {\n ratchetCount: ratchetOpts.ratchetCount + 1,\n encryptionKey: ratchetedKeySet?.encryptionKey,\n },\n );\n\n if (decryptedData && ratchetedKeySet) {\n // before updating the keys, make sure that the keySet used for this frame is still the same as the currently set key\n // if it's not, a new key might have been set already, which we don't want to override\n if ((initialMaterial ?? keySet) === keys.getKeySet(keyIndex)) {\n keys.setKeySet(ratchetedKeySet, keyIndex, ratchetResult);\n // decryption was successful, set the new key index to reflect the ratcheted key set\n keys.setCurrentKeyIndex(keyIndex);\n }\n }\n return decryptedData;\n } else {\n /**\n * Because we only set a new key once decryption has been successful,\n * we can be sure that we don't need to reset the key to the initial material at this point\n * as the key has not been updated on the keyHandler instance\n */\n\n workerLogger.warn('DataCryptor: maximum ratchet attempts exceeded');\n throw new CryptorError(\n `DataCryptor: valid key missing for participant ${keys.participantIdentity}`,\n CryptorErrorReason.InvalidKey,\n keys.participantIdentity,\n );\n }\n } else {\n throw new CryptorError(\n `DataCryptor: Decryption failed: ${error.message}`,\n CryptorErrorReason.InvalidKey,\n keys.participantIdentity,\n );\n }\n }\n }\n}\n","// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n'use strict';\n\nvar R = typeof Reflect === 'object' ? Reflect : null\nvar ReflectApply = R && typeof R.apply === 'function'\n ? R.apply\n : function ReflectApply(target, receiver, args) {\n return Function.prototype.apply.call(target, receiver, args);\n }\n\nvar ReflectOwnKeys\nif (R && typeof R.ownKeys === 'function') {\n ReflectOwnKeys = R.ownKeys\n} else if (Object.getOwnPropertySymbols) {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target)\n .concat(Object.getOwnPropertySymbols(target));\n };\n} else {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target);\n };\n}\n\nfunction ProcessEmitWarning(warning) {\n if (console && console.warn) console.warn(warning);\n}\n\nvar NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {\n return value !== value;\n}\n\nfunction EventEmitter() {\n EventEmitter.init.call(this);\n}\nmodule.exports = EventEmitter;\nmodule.exports.once = once;\n\n// Backwards-compat with node 0.10.x\nEventEmitter.EventEmitter = EventEmitter;\n\nEventEmitter.prototype._events = undefined;\nEventEmitter.prototype._eventsCount = 0;\nEventEmitter.prototype._maxListeners = undefined;\n\n// By default EventEmitters will print a warning if more than 10 listeners are\n// added to it. This is a useful default which helps finding memory leaks.\nvar defaultMaxListeners = 10;\n\nfunction checkListener(listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n}\n\nObject.defineProperty(EventEmitter, 'defaultMaxListeners', {\n enumerable: true,\n get: function() {\n return defaultMaxListeners;\n },\n set: function(arg) {\n if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {\n throw new RangeError('The value of \"defaultMaxListeners\" is out of range. It must be a non-negative number. Received ' + arg + '.');\n }\n defaultMaxListeners = arg;\n }\n});\n\nEventEmitter.init = function() {\n\n if (this._events === undefined ||\n this._events === Object.getPrototypeOf(this)._events) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n }\n\n this._maxListeners = this._maxListeners || undefined;\n};\n\n// Obviously not all Emitters should be limited to 10. This function allows\n// that to be increased. Set to zero for unlimited.\nEventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {\n if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {\n throw new RangeError('The value of \"n\" is out of range. It must be a non-negative number. Received ' + n + '.');\n }\n this._maxListeners = n;\n return this;\n};\n\nfunction _getMaxListeners(that) {\n if (that._maxListeners === undefined)\n return EventEmitter.defaultMaxListeners;\n return that._maxListeners;\n}\n\nEventEmitter.prototype.getMaxListeners = function getMaxListeners() {\n return _getMaxListeners(this);\n};\n\nEventEmitter.prototype.emit = function emit(type) {\n var args = [];\n for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);\n var doError = (type === 'error');\n\n var events = this._events;\n if (events !== undefined)\n doError = (doError && events.error === undefined);\n else if (!doError)\n return false;\n\n // If there is no 'error' event listener then throw.\n if (doError) {\n var er;\n if (args.length > 0)\n er = args[0];\n if (er instanceof Error) {\n // Note: The comments on the `throw` lines are intentional, they show\n // up in Node's output if this results in an unhandled exception.\n throw er; // Unhandled 'error' event\n }\n // At least give some kind of context to the user\n var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));\n err.context = er;\n throw err; // Unhandled 'error' event\n }\n\n var handler = events[type];\n\n if (handler === undefined)\n return false;\n\n if (typeof handler === 'function') {\n ReflectApply(handler, this, args);\n } else {\n var len = handler.length;\n var listeners = arrayClone(handler, len);\n for (var i = 0; i < len; ++i)\n ReflectApply(listeners[i], this, args);\n }\n\n return true;\n};\n\nfunction _addListener(target, type, listener, prepend) {\n var m;\n var events;\n var existing;\n\n checkListener(listener);\n\n events = target._events;\n if (events === undefined) {\n events = target._events = Object.create(null);\n target._eventsCount = 0;\n } else {\n // To avoid recursion in the case that type === \"newListener\"! Before\n // adding it to the listeners, first emit \"newListener\".\n if (events.newListener !== undefined) {\n target.emit('newListener', type,\n listener.listener ? listener.listener : listener);\n\n // Re-assign `events` because a newListener handler could have caused the\n // this._events to be assigned to a new object\n events = target._events;\n }\n existing = events[type];\n }\n\n if (existing === undefined) {\n // Optimize the case of one listener. Don't need the extra array object.\n existing = events[type] = listener;\n ++target._eventsCount;\n } else {\n if (typeof existing === 'function') {\n // Adding the second element, need to change to array.\n existing = events[type] =\n prepend ? [listener, existing] : [existing, listener];\n // If we've already got an array, just append.\n } else if (prepend) {\n existing.unshift(listener);\n } else {\n existing.push(listener);\n }\n\n // Check for listener leak\n m = _getMaxListeners(target);\n if (m > 0 && existing.length > m && !existing.warned) {\n existing.warned = true;\n // No error code for this since it is a Warning\n // eslint-disable-next-line no-restricted-syntax\n var w = new Error('Possible EventEmitter memory leak detected. ' +\n existing.length + ' ' + String(type) + ' listeners ' +\n 'added. Use emitter.setMaxListeners() to ' +\n 'increase limit');\n w.name = 'MaxListenersExceededWarning';\n w.emitter = target;\n w.type = type;\n w.count = existing.length;\n ProcessEmitWarning(w);\n }\n }\n\n return target;\n}\n\nEventEmitter.prototype.addListener = function addListener(type, listener) {\n return _addListener(this, type, listener, false);\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.prependListener =\n function prependListener(type, listener) {\n return _addListener(this, type, listener, true);\n };\n\nfunction onceWrapper() {\n if (!this.fired) {\n this.target.removeListener(this.type,