UNPKG

@socketsupply/socket

Version:

A Cross-Platform, Native Runtime for Desktop and Mobile Apps — Create apps using HTML, CSS, and JavaScript. Written from the ground up to be small and maintainable.

584 lines (486 loc) 16.8 kB
import { AsyncContext } from './async/context.js' // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. const R = typeof Reflect === 'object' ? Reflect : null const ReflectApply = R && typeof R.apply === 'function' ? R.apply : function ReflectApply (target, receiver, args) { return Function.prototype.apply.call(target, receiver, args) } let ReflectOwnKeys if (R && typeof R.ownKeys === 'function') { ReflectOwnKeys = R.ownKeys } else if (Object.getOwnPropertySymbols) { ReflectOwnKeys = function ReflectOwnKeys (target) { return Object.getOwnPropertyNames(target) .concat(Object.getOwnPropertySymbols(target)) } } else { ReflectOwnKeys = function ReflectOwnKeys (target) { return Object.getOwnPropertyNames(target) } } function ProcessEmitWarning (warning) { if (console && console.warn) console.warn(warning) } const NumberIsNaN = Number.isNaN function EventEmitter () { EventEmitter.init.call(this) } // Backwards-compat with node 0.10.x EventEmitter.EventEmitter = EventEmitter EventEmitter.prototype._events = undefined EventEmitter.prototype._contexts = undefined EventEmitter.prototype._eventsCount = 0 EventEmitter.prototype._maxListeners = undefined // By default EventEmitters will print a warning if more than 10 listeners are // added to it. This is a useful default which helps finding memory leaks. let defaultMaxListeners = 10 function checkListener (listener) { if (typeof listener !== 'function') { throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener) } } Object.defineProperty(EventEmitter, 'defaultMaxListeners', { enumerable: true, get: function () { return defaultMaxListeners }, set: function (arg) { if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.') } defaultMaxListeners = arg } }) EventEmitter.init = function () { if (this._events === undefined || this._events === Object.getPrototypeOf(this)._events) { this._events = Object.create(null) this._eventsCount = 0 } if ( this._contexts === undefined || this._contexts === Object.getPrototypeOf(this)._contexts ) { this._contexts = new Map() } this._maxListeners = this._maxListeners || undefined } // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. EventEmitter.prototype.setMaxListeners = function setMaxListeners (n) { if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.') } this._maxListeners = n return this } function _getMaxListeners (that) { if (that._maxListeners === undefined) { return EventEmitter.defaultMaxListeners } return that._maxListeners } EventEmitter.prototype.getMaxListeners = function getMaxListeners () { return _getMaxListeners(this) } EventEmitter.prototype.emit = function emit (type) { const args = [] for (let i = 1; i < arguments.length; i++) args.push(arguments[i]) let doError = (type === 'error') const events = this._events if (events !== undefined) { doError = (doError && events.error === undefined) } else if (!doError) { return false } // If there is no 'error' event listener then throw. if (doError) { let er if (args.length > 0) { er = args[0] } if (er instanceof Error) { // Note: The comments on the `throw` lines are intentional, they show // up in Node's output if this results in an unhandled exception. throw er // Unhandled 'error' event } // At least give some kind of context to the user const err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : '')) err.context = er throw err // Unhandled 'error' event } const handler = events[type] if (handler === undefined) { return false } if (typeof handler === 'function') { const context = this._contexts.get(handler) if (context) { context.run(() => { ReflectApply(handler, this, args) }) } else { ReflectApply(handler, this, args) } } else { const len = handler.length const listeners = arrayClone(handler, len) for (let i = 0; i < len; ++i) { const context = this._contexts.get(listeners[i]) if (context) { context.run(() => { ReflectApply(listeners[i], this, args) }) } else { ReflectApply(listeners[i], this, args) } } } return true } function _addListener (target, type, listener, prepend) { let m let events let existing checkListener(listener) events = target._events if (events === undefined) { events = target._events = Object.create(null) target._eventsCount = 0 } else { // To avoid recursion in the case that type === "newListener"! Before // adding it to the listeners, first emit "newListener". if (events.newListener !== undefined) { target.emit('newListener', type, listener.listener ? listener.listener : listener) // Re-assign `events` because a newListener handler could have caused the // this._events to be assigned to a new object events = target._events } existing = events[type] } if (existing === undefined) { // Optimize the case of one listener. Don't need the extra array object. existing = events[type] = listener ++target._eventsCount } else { if (typeof existing === 'function') { // Adding the second element, need to change to array. existing = events[type] = prepend ? [listener, existing] : [existing, listener] // If we've already got an array, just append. } else if (prepend) { existing.unshift(listener) } else { existing.push(listener) } // Check for listener leak m = _getMaxListeners(target) if (m > 0 && existing.length > m && !existing.warned) { existing.warned = true // No error code for this since it is a Warning // eslint-disable-next-line no-restricted-syntax const w = new Error('Possible EventEmitter memory leak detected. ' + existing.length + ' ' + String(type) + ' listeners ' + 'added. Use emitter.setMaxListeners() to ' + 'increase limit') w.name = 'MaxListenersExceededWarning' w.emitter = target w.type = type w.count = existing.length ProcessEmitWarning(w) } } target._contexts.set(listener, new AsyncContext.Snapshot()) return target } EventEmitter.prototype.addListener = function addListener (type, listener) { return _addListener(this, type, listener, false) } /** * @instance * @method on * @memberOf EventEmitter * @type {function(any, any): any} */ EventEmitter.prototype.on = function on (type, listener) { return this.addListener(type, listener) } EventEmitter.prototype.prependListener = function prependListener (type, listener) { return _addListener(this, type, listener, true) } function onceWrapper () { if (!this.fired) { this.target.removeListener(this.type, this.wrapFn) this.fired = true if (arguments.length === 0) { return this.listener.call(this.target) } return this.listener.apply(this.target, arguments) } } function _onceWrap (target, type, listener) { const state = { fired: false, wrapFn: undefined, target, type, listener } const wrapped = onceWrapper.bind(state) wrapped.listener = listener state.wrapFn = wrapped return wrapped } EventEmitter.prototype.once = function once (type, listener) { checkListener(listener) this.on(type, _onceWrap(this, type, listener)) return this } EventEmitter.prototype.prependOnceListener = function prependOnceListener (type, listener) { checkListener(listener) this.prependListener(type, _onceWrap(this, type, listener)) return this } // Emits a 'removeListener' event if and only if the listener was removed. EventEmitter.prototype.removeListener = function removeListener (type, listener) { let position, i, originalListener checkListener(listener) const events = this._events if (events === undefined) { return this } const list = events[type] if (list === undefined) { return this } if (list === listener || list.listener === listener) { if (--this._eventsCount === 0) { this._events = Object.create(null) } else { delete events[type] if (events.removeListener) { this.emit('removeListener', type, list.listener || listener) } } } else if (typeof list !== 'function') { position = -1 for (i = list.length - 1; i >= 0; i--) { if (list[i] === listener || list[i].listener === listener) { originalListener = list[i].listener position = i break } } if (position < 0) { return this } if (position === 0) { list.shift() } else { spliceOne(list, position) } if (list.length === 1) { events[type] = list[0] } if (events.removeListener !== undefined) { this.emit('removeListener', type, originalListener || listener) } } this._contexts.delete(listener) return this } EventEmitter.prototype.off = function off (type, listener) { return this.removeListener(type, listener) } EventEmitter.prototype.removeAllListeners = function removeAllListeners (type) { let i const events = this._events if (events === undefined) { return this } // not listening for removeListener, no need to emit if (events.removeListener === undefined) { if (arguments.length === 0) { this._events = Object.create(null) this._eventsCount = 0 } else if (events[type] !== undefined) { if (--this._eventsCount === 0) { this._events = Object.create(null) } else { delete events[type] } } return this } // emit removeListener for all listeners on all events if (arguments.length === 0) { const keys = Object.keys(events) let key for (i = 0; i < keys.length; ++i) { key = keys[i] if (key === 'removeListener') continue this.removeAllListeners(key) } this.removeAllListeners('removeListener') this._events = Object.create(null) this._contexts.clear() this._eventsCount = 0 return this } const listeners = events[type] if (typeof listeners === 'function') { this.removeListener(type, listeners) } else if (listeners !== undefined) { // LIFO order for (i = listeners.length - 1; i >= 0; i--) { this.removeListener(type, listeners[i]) } } return this } function _listeners (target, type, unwrap) { const events = target._events if (events === undefined) { return [] } const evlistener = events[type] if (evlistener === undefined) { return [] } if (typeof evlistener === 'function') { return unwrap ? [evlistener.listener || evlistener] : [evlistener] } return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length) } EventEmitter.prototype.listeners = function listeners (type) { return _listeners(this, type, true) } EventEmitter.prototype.rawListeners = function rawListeners (type) { return _listeners(this, type, false) } EventEmitter.listenerCount = function (emitter, type) { if (typeof emitter.listenerCount === 'function') { return emitter.listenerCount(type) } else { return listenerCount.call(emitter, type) } } EventEmitter.prototype.listenerCount = function (type) { return listenerCount.call(this, type) } function listenerCount (type) { const events = this._events if (events !== undefined) { const evlistener = events[type] if (typeof evlistener === 'function') { return 1 } else if (evlistener !== undefined) { return evlistener.length } } return 0 } EventEmitter.prototype.eventNames = function eventNames () { return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [] } function arrayClone (arr, n) { const copy = new Array(n) for (let i = 0; i < n; ++i) { copy[i] = arr[i] } return copy } function spliceOne (list, index) { for (; index + 1 < list.length; index++) { list[index] = list[index + 1] } list.pop() } function unwrapListeners (arr) { const ret = new Array(arr.length) for (let i = 0; i < ret.length; ++i) { ret[i] = arr[i].listener || arr[i] } return ret } function once (emitter, name) { return new Promise(function (resolve, reject) { function errorListener (err) { emitter.removeListener(name, resolver) reject(err) } function resolver () { if (typeof emitter.removeListener === 'function') { emitter.removeListener('error', errorListener) } resolve([].slice.call(arguments)) }; eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }) if (name !== 'error') { addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }) } }) } function addErrorHandlerIfEventEmitter (emitter, handler, flags) { if (typeof emitter.on === 'function') { eventTargetAgnosticAddListener(emitter, 'error', handler, flags) } } function eventTargetAgnosticAddListener (emitter, name, listener, flags) { if (typeof emitter.on === 'function') { if (flags.once) { emitter.once(name, listener) } else { emitter.on(name, listener) } } else if (typeof emitter.addEventListener === 'function') { // EventTarget does not have `error` event semantics like Node // EventEmitters, we do not listen for `error` events here. emitter.addEventListener(name, function wrapListener (arg) { // IE does not have builtin `{ once: true }` support so we // have to do it manually. if (flags.once) { emitter.removeEventListener(name, wrapListener) } listener(arg) }) } else { throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter) } } EventEmitter.EventEmitter = EventEmitter EventEmitter.once = once export const Event = globalThis.Event || class Event {} export const EventTarget = globalThis.EventTarget || class EventTarget {} export const CustomEvent = globalThis.CustomEvent || class CustomEvent extends Event { #detail = null constructor (type, options) { super(type, options) if (options?.detail) { this.#detail = options.detail } } get detail () { return this.#detail } } export const MessageEvent = globalThis.MessageEvent || class MessageEvent extends Event { #detail = null #data = null constructor (type, options) { super(type, options) if (options?.detail) { this.#detail = options.detail } if (options?.data) { this.#data = options.data } } get detail () { return this.#detail } get data () { return this.#data } } export const ErrorEvent = globalThis.ErrorEvent || class ErrorEvent extends Event { #detail = null #error = null constructor (type, options) { super(type, options) if (options?.detail) { this.#detail = options.detail } if (options?.error) { this.#error = options.error } } get detail () { return this.#detail } get error () { return this.#error } } export { EventEmitter, once } export default EventEmitter