deploy: v0.12 fix 存档key名

This commit is contained in:
2026-06-24 03:09:02 +00:00
parent 7a08d49fe3
commit e0a5281119
58743 changed files with 7297269 additions and 39505 deletions
+79
View File
@@ -0,0 +1,79 @@
# Strict Event Emitter
A type-safe implementation of `EventEmitter` for browser and Node.js.
## Motivation
Despite event emitters potentially accepting any runtime value, defining a strict event contract is crucial when developing complex event-driven architectures. Unfortunately, the native type definitions for Node's `EventEmitter` annotate event names as `string`, which forbids any stricter type validation.
```js
// index.js
const emitter = new EventEmitter()
// Let's say our application expects a "ping"
// event with the number payload.
emitter.on('ping', (n: number) => {})
// We can, however, emit a different event by mistake.
emitter.emit('pong', 1)
// Or even the correct event with the wrong data.
emitter.emit('ping', 'wait, not a number')
```
The purpose of this library is to provide an `EventEmitter` instance that can accept a generic describing the expected events contract.
```ts
import { Emitter } from 'strict-event-emitter'
// Define a strict events contract where keys
// represent event names and values represent
// the list of arguments expected in ".emit()".
type Events = {
ping: [number]
}
const emitter = new Emitter<Events>()
emitter.addListener('ping', (n) => {
// "n" argument type is inferred as "number'.
})
emitter.emit('ping', 10) // OK
emitter.emit('unknown', 10) // TypeError (invalid event name)
emitter.emit('ping', 'wait, not a number') // TypeError (invalid data)
```
This library is also a custom `EventEmitter` implementation, which makes it compatible with other environments, like browsers or React Native.
## Getting started
### Install
```bash
npm install strict-event-emitter
```
### Use
```ts
import { Emitter } from 'strict-event-emitter'
// 1. Define an interface that describes your events.
// Set event names as the keys, and their expected payloads as values.
interface Events {
connect: [id: string]
disconnect: [id: string]
}
// 2. Create a strict emitter and pass the previously defined "Events"
// as its first generic argument.
const emitter = new Emitter<Events>()
// 3. Use the "emitter" the same way you'd use the regular "EventEmitter" instance.
emitter.addListener('connect', (id) => {})
emitter.emit('connect', 'abc-123')
```
## License
MIT
+72
View File
@@ -0,0 +1,72 @@
export declare type EventMap = {
[eventName: string]: Array<unknown>;
};
export declare type InternalEventNames = 'newListener' | 'removeListener';
export declare type InternalListener<Events extends EventMap> = Listener<[
eventName: keyof Events,
listener: Listener<Array<unknown>>
]>;
export declare type Listener<Data extends Array<unknown>> = (...data: Data) => void;
/**
* Node.js-compatible implementation of `EventEmitter`.
*
* @example
* const emitter = new Emitter<{ hello: [string] }>()
* emitter.on('hello', (name) => console.log(name))
* emitter.emit('hello', 'John')
*/
export declare class Emitter<Events extends EventMap> {
private events;
private maxListeners;
private hasWarnedAboutPotentialMemoryLeak;
static defaultMaxListeners: number;
static listenerCount<Events extends EventMap>(emitter: Emitter<EventMap>, eventName: keyof Events): number;
constructor();
private _emitInternalEvent;
private _getListeners;
private _removeListener;
private _wrapOnceListener;
setMaxListeners(maxListeners: number): this;
/**
* Returns the current max listener value for the `Emitter` which is
* either set by `emitter.setMaxListeners(n)` or defaults to
* `Emitter.defaultMaxListeners`.
*/
getMaxListeners(): number;
/**
* Returns an array listing the events for which the emitter has registered listeners.
* The values in the array will be strings or Symbols.
*/
eventNames(): Array<keyof Events>;
/**
* Synchronously calls each of the listeners registered for the event named `eventName`,
* in the order they were registered, passing the supplied arguments to each.
* Returns `true` if the event has listeners, `false` otherwise.
*
* @example
* const emitter = new Emitter<{ hello: [string] }>()
* emitter.emit('hello', 'John')
*/
emit<EventName extends keyof Events>(eventName: EventName, ...data: Events[EventName]): boolean;
addListener(eventName: InternalEventNames, listener: InternalListener<Events>): this;
addListener<EventName extends keyof Events>(eventName: EventName, listener: Listener<Events[EventName]>): this;
on(eventName: InternalEventNames, listener: InternalListener<Events>): this;
on<EventName extends keyof Events>(eventName: EventName, listener: Listener<Events[EventName]>): this;
once(eventName: InternalEventNames, listener: InternalListener<Events>): this;
once<EventName extends keyof Events>(eventName: EventName, listener: Listener<Events[EventName]>): this;
prependListener(eventName: InternalEventNames, listener: InternalListener<Events>): this;
prependListener<EventName extends keyof Events>(eventName: EventName, listener: Listener<Events[EventName]>): this;
prependOnceListener(eventName: InternalEventNames, listener: InternalListener<Events>): this;
prependOnceListener<EventName extends keyof Events>(eventName: EventName, listener: Listener<Events[EventName]>): this;
removeListener(eventName: InternalEventNames, listener: InternalListener<Events>): this;
removeListener<EventName extends keyof Events>(eventName: EventName, listener: Listener<Events[EventName]>): this;
off(eventName: InternalEventNames, listener: InternalListener<Events>): this;
off<EventName extends keyof Events>(eventName: EventName, listener: Listener<Events[EventName]>): this;
removeAllListeners(eventName?: InternalEventNames): this;
removeAllListeners<EventName extends keyof Events>(eventName?: EventName): this;
listeners(eventName: InternalEventNames): Array<Listener<any>>;
listeners<EventName extends keyof Events>(eventName: EventName): Array<Listener<Events[EventName]>>;
listenerCount(eventName: InternalEventNames): number;
listenerCount<EventName extends keyof Events>(eventName: EventName): number;
rawListeners<EventName extends keyof Events>(eventName: EventName): Array<Listener<Events[EventName]>>;
}
+158
View File
@@ -0,0 +1,158 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Emitter = void 0;
const MemoryLeakError_1 = require("./MemoryLeakError");
/**
* Node.js-compatible implementation of `EventEmitter`.
*
* @example
* const emitter = new Emitter<{ hello: [string] }>()
* emitter.on('hello', (name) => console.log(name))
* emitter.emit('hello', 'John')
*/
class Emitter {
constructor() {
this.events = new Map();
this.maxListeners = Emitter.defaultMaxListeners;
this.hasWarnedAboutPotentialMemoryLeak = false;
}
static listenerCount(emitter, eventName) {
return emitter.listenerCount(eventName);
}
_emitInternalEvent(internalEventName, eventName, listener) {
this.emit(internalEventName,
// Anything to make TypeScript happy.
...[eventName, listener]);
}
_getListeners(eventName) {
return this.events.get(eventName) || [];
}
_removeListener(listeners, listener) {
const index = listeners.indexOf(listener);
if (index > -1) {
listeners.splice(index, 1);
}
return [];
}
_wrapOnceListener(eventName, listener) {
const onceListener = (...data) => {
this.removeListener(eventName, onceListener);
listener.apply(this, data);
};
return onceListener;
}
setMaxListeners(maxListeners) {
this.maxListeners = maxListeners;
return this;
}
/**
* Returns the current max listener value for the `Emitter` which is
* either set by `emitter.setMaxListeners(n)` or defaults to
* `Emitter.defaultMaxListeners`.
*/
getMaxListeners() {
return this.maxListeners;
}
/**
* Returns an array listing the events for which the emitter has registered listeners.
* The values in the array will be strings or Symbols.
*/
eventNames() {
return Array.from(this.events.keys());
}
/**
* Synchronously calls each of the listeners registered for the event named `eventName`,
* in the order they were registered, passing the supplied arguments to each.
* Returns `true` if the event has listeners, `false` otherwise.
*
* @example
* const emitter = new Emitter<{ hello: [string] }>()
* emitter.emit('hello', 'John')
*/
emit(eventName, ...data) {
const listeners = this._getListeners(eventName);
listeners.forEach((listener) => {
listener.apply(this, data);
});
return listeners.length > 0;
}
addListener(eventName, listener) {
// Emit the `newListener` event before adding the listener.
this._emitInternalEvent('newListener', eventName, listener);
const nextListeners = this._getListeners(eventName).concat(listener);
this.events.set(eventName, nextListeners);
if (this.maxListeners > 0 &&
this.listenerCount(eventName) > this.maxListeners &&
!this.hasWarnedAboutPotentialMemoryLeak) {
this.hasWarnedAboutPotentialMemoryLeak = true;
const memoryLeakWarning = new MemoryLeakError_1.MemoryLeakError(this, eventName, this.listenerCount(eventName));
console.warn(memoryLeakWarning);
}
return this;
}
on(eventName, listener) {
return this.addListener(eventName, listener);
}
once(eventName, listener) {
return this.addListener(eventName, this._wrapOnceListener(eventName, listener));
}
prependListener(eventName, listener) {
const listeners = this._getListeners(eventName);
if (listeners.length > 0) {
const nextListeners = [listener].concat(listeners);
this.events.set(eventName, nextListeners);
}
else {
this.events.set(eventName, listeners.concat(listener));
}
return this;
}
prependOnceListener(eventName, listener) {
return this.prependListener(eventName, this._wrapOnceListener(eventName, listener));
}
removeListener(eventName, listener) {
const listeners = this._getListeners(eventName);
if (listeners.length > 0) {
this._removeListener(listeners, listener);
this.events.set(eventName, listeners);
// Emit the `removeListener` event after removing the listener.
this._emitInternalEvent('removeListener', eventName, listener);
}
return this;
}
/**
* Alias for `emitter.removeListener()`.
*
* @example
* emitter.off('hello', listener)
*/
off(eventName, listener) {
return this.removeListener(eventName, listener);
}
removeAllListeners(eventName) {
if (eventName) {
this.events.delete(eventName);
}
else {
this.events.clear();
}
return this;
}
/**
* Returns a copy of the array of listeners for the event named `eventName`.
*/
listeners(eventName) {
return Array.from(this._getListeners(eventName));
}
/**
* Returns the number of listeners listening to the event named `eventName`.
*/
listenerCount(eventName) {
return this._getListeners(eventName).length;
}
rawListeners(eventName) {
return this.listeners(eventName);
}
}
exports.Emitter = Emitter;
Emitter.defaultMaxListeners = 10;
+7
View File
@@ -0,0 +1,7 @@
import type { Emitter } from './Emitter';
export declare class MemoryLeakError extends Error {
readonly emitter: Emitter<any>;
readonly type: string | number | symbol;
readonly count: number;
constructor(emitter: Emitter<any>, type: string | number | symbol, count: number);
}
+13
View File
@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MemoryLeakError = void 0;
class MemoryLeakError extends Error {
constructor(emitter, type, count) {
super(`Possible EventEmitter memory leak detected. ${count} ${type.toString()} listeners added. Use emitter.setMaxListeners() to increase limit`);
this.emitter = emitter;
this.type = type;
this.count = count;
this.name = 'MaxListenersExceededWarning';
}
}
exports.MemoryLeakError = MemoryLeakError;
+2
View File
@@ -0,0 +1,2 @@
export * from './Emitter';
export * from './MemoryLeakError';
+18
View File
@@ -0,0 +1,18 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./Emitter"), exports);
__exportStar(require("./MemoryLeakError"), exports);
+32
View File
@@ -0,0 +1,32 @@
{
"name": "strict-event-emitter",
"version": "0.4.6",
"description": "Type-safe implementation of EventEmitter for browser and Node.js",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"repository": "git@github.com:open-draft/strict-event-emitter.git",
"author": "Artem Zakharchenko <kettanaito@gmail.com>",
"license": "MIT",
"scripts": {
"dev": "tsc -w",
"test": "jest",
"test:ts": "tsc -p test/typings.tsconfig.json",
"clean": "rimraf ./lib",
"build": "yarn clean && tsc",
"release": "release publish"
},
"files": [
"lib"
],
"devDependencies": {
"@ossjs/release": "^0.4.0",
"@types/events": "^3.0.0",
"@types/jest": "^29.2.4",
"jest": "^29.3.1",
"jest-extended": "^3.2.0",
"rimraf": "^3.0.2",
"ts-jest": "^29.0.3",
"ts-node": "^9.1.1",
"typescript": "4.8"
}
}