recoder-code
Version:
🚀 AI-powered development platform - Chat with 32+ models, build projects, automate workflows. Free models included!
356 lines (264 loc) • 7.5 kB
JavaScript
const errors = require('./lib/errors')
class EventListener {
constructor() {
this.list = []
this.count = 0
}
append(ctx, name, fn, once) {
this.count++
ctx.emit('newListener', name, fn) // Emit BEFORE adding
this.list.push([fn, once])
}
prepend(ctx, name, fn, once) {
this.count++
ctx.emit('newListener', name, fn) // Emit BEFORE adding
this.list.unshift([fn, once])
}
remove(ctx, name, fn) {
for (let i = 0, n = this.list.length; i < n; i++) {
const l = this.list[i]
if (l[0] === fn) {
this.list.splice(i, 1)
if (this.count === 1) delete ctx._events[name]
ctx.emit('removeListener', name, fn) // Emit AFTER removing
this.count--
return
}
}
}
removeAll(ctx, name) {
const list = [...this.list]
this.list = []
if (this.count === list.length) delete ctx._events[name]
for (let i = list.length - 1; i >= 0; i--) {
ctx.emit('removeListener', name, list[i][0]) // Emit AFTER removing
}
this.count -= list.length
}
emit(ctx, name, ...args) {
const list = [...this.list]
for (let i = 0, n = list.length; i < n; i++) {
const l = list[i]
if (l[1] === true) this.remove(ctx, name, l[0])
Reflect.apply(l[0], ctx, args)
}
return list.length > 0
}
}
function appendListener(ctx, name, fn, once) {
const e = ctx._events[name] || (ctx._events[name] = new EventListener())
e.append(ctx, name, fn, once)
return ctx
}
function prependListener(ctx, name, fn, once) {
const e = ctx._events[name] || (ctx._events[name] = new EventListener())
e.prepend(ctx, name, fn, once)
return ctx
}
function removeListener(ctx, name, fn) {
const e = ctx._events[name]
if (e !== undefined) e.remove(ctx, name, fn)
return ctx
}
function throwUnhandledError(...args) {
let err
if (args.length > 0) err = args[0]
if (err instanceof Error === false) err = errors.UNHANDLED_ERROR(err)
if (Error.captureStackTrace) {
Error.captureStackTrace(err, exports.prototype.emit)
}
queueMicrotask(() => {
throw err
})
}
module.exports = exports = class EventEmitter {
constructor() {
this._events = Object.create(null)
}
addListener(name, fn) {
return appendListener(this, name, fn, false)
}
addOnceListener(name, fn) {
return appendListener(this, name, fn, true)
}
prependListener(name, fn) {
return prependListener(this, name, fn, false)
}
prependOnceListener(name, fn) {
return prependListener(this, name, fn, true)
}
removeListener(name, fn) {
return removeListener(this, name, fn)
}
on(name, fn) {
return appendListener(this, name, fn, false)
}
once(name, fn) {
return appendListener(this, name, fn, true)
}
off(name, fn) {
return removeListener(this, name, fn)
}
emit(name, ...args) {
if (name === 'error' && this._events.error === undefined) {
throwUnhandledError(...args)
}
const e = this._events[name]
return e === undefined ? false : e.emit(this, name, ...args)
}
listeners(name) {
const e = this._events[name]
return e === undefined ? [] : [...e.list]
}
listenerCount(name) {
const e = this._events[name]
return e === undefined ? 0 : e.list.length
}
getMaxListeners() {
return EventEmitter.defaultMaxListeners
}
setMaxListeners(n) {}
removeAllListeners(name) {
if (arguments.length === 0) {
for (const key of Reflect.ownKeys(this._events)) {
if (key === 'removeListener') continue
this.removeAllListeners(key)
}
this.removeAllListeners('removeListener')
} else {
const e = this._events[name]
if (e !== undefined) e.removeAll(this, name)
}
return this
}
}
exports.EventEmitter = exports
exports.errors = errors
exports.defaultMaxListeners = 10
exports.on = function on(emitter, name, opts = {}) {
const { signal } = opts
if (signal && signal.aborted) {
throw errors.OPERATION_ABORTED(signal.reason)
}
let error = null
let done = false
const events = []
const promises = []
emitter.on(name, onevent)
if (name !== 'error') emitter.on('error', onerror)
if (signal) signal.addEventListener('abort', onabort)
return {
next() {
if (events.length) {
return Promise.resolve({ value: events.shift(), done: false })
}
if (error) {
const err = error
error = null
return Promise.reject(err)
}
if (done) return onclose()
return new Promise((resolve, reject) =>
promises.push({ resolve, reject })
)
},
return() {
return onclose()
},
throw(err) {
return onerror(err)
},
[Symbol.asyncIterator]() {
return this
}
}
function onevent(...args) {
if (promises.length) {
promises.shift().resolve({ value: args, done: false })
} else {
events.push(args)
}
}
function onerror(err) {
if (promises.length) {
promises.shift().reject(err)
} else {
error = err
}
return Promise.resolve({ done: true })
}
function onabort() {
onerror(errors.OPERATION_ABORTED(signal.reason))
}
function onclose() {
emitter.off(name, onevent)
if (name !== 'error') emitter.off('error', onerror)
if (signal) signal.removeEventListener('abort', onabort)
done = true
if (promises.length) promises.shift().resolve({ done: true })
return Promise.resolve({ done: true })
}
}
exports.once = function once(emitter, name, opts = {}) {
const { signal } = opts
if (signal && signal.aborted) {
throw errors.OPERATION_ABORTED(signal.reason)
}
return new Promise((resolve, reject) => {
if (name !== 'error') emitter.on('error', onerror)
if (signal) signal.addEventListener('abort', onabort)
emitter.once(name, (...args) => {
if (name !== 'error') emitter.off('error', onerror)
if (signal) signal.removeEventListener('abort', onabort)
resolve(args)
})
function onerror(err) {
emitter.off('error', onerror)
reject(err)
}
function onabort() {
signal.removeEventListener('abort', onabort)
onerror(errors.OPERATION_ABORTED(signal.reason))
}
})
}
exports.forward = function forward(from, to, names, opts = {}) {
if (typeof names === 'string') names = [names]
const { emit = to.emit.bind(to) } = opts
const listeners = names.map(
(name) =>
function onevent(...args) {
emit(name, ...args)
}
)
to.on('newListener', (name) => {
const i = names.indexOf(name)
if (i !== -1 && to.listenerCount(name) === 0) {
from.on(name, listeners[i])
}
}).on('removeListener', (name) => {
const i = names.indexOf(name)
if (i !== -1 && to.listenerCount(name) === 0) {
from.off(name, listeners[i])
}
})
}
exports.listenerCount = function listenerCount(emitter, name) {
return emitter.listenerCount(name)
}
exports.getMaxListeners = function getMaxListeners(emitter) {
if (typeof emitter.getMaxListeners === 'function') {
return emitter.getMaxListeners()
}
return exports.defaultMaxListeners
}
exports.setMaxListeners = function setMaxListeners(n, ...emitters) {
if (emitters.length === 0) exports.defaultMaxListeners = n
else {
for (const emitter of emitters) {
if (typeof emitter.setMaxListeners === 'function') {
emitter.setMaxListeners(n)
}
}
}
}