@seedcord/event-emitter 0.0.1 → 0.1.0-next.0

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.
package/dist/index.cjs CHANGED
@@ -1,9 +1,199 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
+ //#region src/WaitForError.ts
4
+ /** Rejection thrown when a {@link TypedEventEmitter.waitFor} is aborted or times out. */
5
+ var WaitForError = class extends Error {
6
+ reason;
7
+ constructor(reason, timeoutMs) {
8
+ super(reason === "timeout" ? `waitFor timed out after ${timeoutMs}ms.` : "waitFor was aborted via its AbortSignal.");
9
+ this.name = "WaitForError";
10
+ this.reason = reason;
11
+ }
12
+ };
13
+
14
+ //#endregion
15
+ //#region src/TypedEventEmitter.ts
16
+ /**
17
+ * Typed, cross-runtime event emitter with per-event tuple payloads.
18
+ *
19
+ * @typeParam TEvents - Map of event names to readonly tuple payloads.
20
+ */
21
+ var TypedEventEmitter = class {
22
+ registrations = /* @__PURE__ */ new Map();
23
+ /** Registers a persistent listener for an event. */
24
+ on(event, listener) {
25
+ const original = listener;
26
+ this.slot(event).add({
27
+ call: original,
28
+ original
29
+ });
30
+ return this;
31
+ }
32
+ /** Registers a listener removed after its first invocation. */
33
+ once(event, listener) {
34
+ const original = listener;
35
+ let fired = false;
36
+ const registration = {
37
+ call: (...args) => {
38
+ if (fired) return;
39
+ fired = true;
40
+ this.removeRegistration(event, registration);
41
+ original(...args);
42
+ },
43
+ original
44
+ };
45
+ this.slot(event).add(registration);
46
+ return this;
47
+ }
48
+ /** Removes a previously registered listener. */
49
+ off(event, listener) {
50
+ this.drop(event, listener);
51
+ return this;
52
+ }
53
+ /** Alias of {@link TypedEventEmitter.off}. */
54
+ removeListener(event, listener) {
55
+ return this.off(event, listener);
56
+ }
57
+ /** Emits an event, invoking each listener with the payload tuple. Returns true when the event had listeners. */
58
+ emit(event, ...args) {
59
+ return this.dispatch(event, args);
60
+ }
61
+ /** @internal */
62
+ emitSafe(event, ...args) {
63
+ return this.dispatchSafe(event, args);
64
+ }
65
+ /** @internal */
66
+ emitSafeRaw(event, ...args) {
67
+ return this.dispatchSafe(event, args);
68
+ }
69
+ /** @internal */
70
+ onListenerError(error, _event) {
71
+ queueMicrotask(() => {
72
+ throw error;
73
+ });
74
+ }
75
+ dispatch(event, args) {
76
+ const slot = this.registrations.get(event);
77
+ if (!slot || slot.size === 0) return false;
78
+ const current = [...slot];
79
+ for (const registration of current) registration.call(...args);
80
+ return true;
81
+ }
82
+ dispatchSafe(event, args) {
83
+ const slot = this.registrations.get(event);
84
+ if (!slot || slot.size === 0) return false;
85
+ const current = [...slot];
86
+ for (const registration of current) try {
87
+ registration.call(...args);
88
+ } catch (error) {
89
+ this.onListenerError(error, event);
90
+ }
91
+ return true;
92
+ }
93
+ /** Removes every listener for an event, or for all events when no event is given. */
94
+ removeAllListeners(event) {
95
+ if (event === void 0) this.registrations.clear();
96
+ else this.registrations.delete(event);
97
+ return this;
98
+ }
99
+ /** Counts the listeners registered for an event. */
100
+ listenerCount(event) {
101
+ return this.registrations.get(event)?.size ?? 0;
102
+ }
103
+ /** Returns the event names with at least one listener. */
104
+ eventNames() {
105
+ return [...this.registrations.keys()];
106
+ }
107
+ /**
108
+ * Waits for the next emission of an event, resolving with its payload tuple.
109
+ * With a filter, resolves on the first payload the filter accepts.
110
+ *
111
+ * @param event - The event to wait for.
112
+ * @param options - An optional filter, abort signal, and timeout.
113
+ * @returns A promise for the matching payload. Rejects with a {@link WaitForError} when aborted or timed out.
114
+ * @example
115
+ * ```ts
116
+ * const ee = new TypedEventEmitter<{ ping: [n: number] }>();
117
+ * const pending = ee.waitFor('ping', { filter: n => n > 0, timeoutMs: 1000 });
118
+ * ee.emit('ping', -1); // ignored by filter
119
+ * ee.emit('ping', 42); // resolves pending with [42]
120
+ * ```
121
+ */
122
+ waitFor(event, options) {
123
+ return new Promise((resolve, reject) => {
124
+ const onEvent = (...args) => {
125
+ if (options?.filter) {
126
+ let matched;
127
+ try {
128
+ matched = options.filter(...args);
129
+ } catch (error) {
130
+ cleanup();
131
+ reject(Error.isError(error) ? error : new Error(String(error)));
132
+ return;
133
+ }
134
+ if (!matched) return;
135
+ }
136
+ cleanup();
137
+ resolve(args);
138
+ };
139
+ const onAbort = () => {
140
+ cleanup();
141
+ reject(new WaitForError("aborted"));
142
+ };
143
+ let timer;
144
+ const cleanup = () => {
145
+ this.off(event, onEvent);
146
+ options?.signal?.removeEventListener("abort", onAbort);
147
+ if (timer !== void 0) clearTimeout(timer);
148
+ };
149
+ this.on(event, onEvent);
150
+ if (options?.signal) {
151
+ if (options.signal.aborted) {
152
+ onAbort();
153
+ return;
154
+ }
155
+ options.signal.addEventListener("abort", onAbort, { once: true });
156
+ }
157
+ if (options?.timeoutMs !== void 0) {
158
+ const timeoutMs = options.timeoutMs;
159
+ timer = setTimeout(() => {
160
+ cleanup();
161
+ reject(new WaitForError("timeout", timeoutMs));
162
+ }, timeoutMs);
163
+ }
164
+ });
165
+ }
166
+ slot(event) {
167
+ let slot = this.registrations.get(event);
168
+ if (!slot) {
169
+ slot = /* @__PURE__ */ new Set();
170
+ this.registrations.set(event, slot);
171
+ }
172
+ return slot;
173
+ }
174
+ drop(event, original) {
175
+ const slot = this.registrations.get(event);
176
+ if (!slot) return;
177
+ for (const registration of slot) if (registration.original === original) {
178
+ this.removeRegistration(event, registration);
179
+ break;
180
+ }
181
+ }
182
+ removeRegistration(event, registration) {
183
+ const slot = this.registrations.get(event);
184
+ if (!slot) return;
185
+ slot.delete(registration);
186
+ if (slot.size === 0) this.registrations.delete(event);
187
+ }
188
+ };
189
+
190
+ //#endregion
3
191
  //#region src/index.ts
4
192
  /** Package version */
5
- const version = "0.0.1";
193
+ const version = "0.1.0-next.0";
6
194
 
7
195
  //#endregion
196
+ exports.TypedEventEmitter = TypedEventEmitter;
197
+ exports.WaitForError = WaitForError;
8
198
  exports.version = version;
9
199
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":";;;;AACA,MAAa"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/WaitForError.ts","../src/TypedEventEmitter.ts","../src/index.ts"],"sourcesContent":["/** Rejection thrown when a {@link TypedEventEmitter.waitFor} is aborted or times out. */\nexport class WaitForError extends Error {\n public readonly reason: 'aborted' | 'timeout';\n\n constructor(reason: 'aborted' | 'timeout', timeoutMs?: number) {\n super(\n reason === 'timeout'\n ? `waitFor timed out after ${timeoutMs}ms.`\n : 'waitFor was aborted via its AbortSignal.'\n );\n this.name = 'WaitForError';\n this.reason = reason;\n }\n}\n","import { WaitForError } from './WaitForError';\n\nimport type { EventKey, EventMap, WaitForOptions } from './types';\n\ntype BroadListener = (...args: readonly unknown[]) => void;\n\ninterface Registration {\n readonly call: BroadListener;\n // off() matches by original, so off(userListener) removes a once registration whose call is the wrapper\n readonly original: BroadListener;\n}\n\n/**\n * Typed, cross-runtime event emitter with per-event tuple payloads.\n *\n * @typeParam TEvents - Map of event names to readonly tuple payloads.\n */\nexport class TypedEventEmitter<TEvents extends EventMap<TEvents>> {\n private readonly registrations = new Map<EventKey<TEvents>, Set<Registration>>();\n\n /** Registers a persistent listener for an event. */\n public on<TEventKey extends EventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n // justified: the public per-event tuple erases to the common callable stored internally.\n const original = listener as BroadListener;\n this.slot(event).add({ call: original, original });\n return this;\n }\n\n /** Registers a listener removed after its first invocation. */\n public once<TEventKey extends EventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n // justified: same erase as on().\n const original = listener as BroadListener;\n let fired = false;\n const registration: Registration = {\n // fired guards a re-entrant emit that re-invokes this registration from the outer snapshot\n call: (...args) => {\n if (fired) return;\n fired = true;\n this.removeRegistration(event, registration);\n original(...args);\n },\n original\n };\n this.slot(event).add(registration);\n return this;\n }\n\n /** Removes a previously registered listener. */\n public off<TEventKey extends EventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n // justified: same erase as on().\n this.drop(event, listener as BroadListener);\n return this;\n }\n\n /** Alias of {@link TypedEventEmitter.off}. */\n public removeListener<TEventKey extends EventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return this.off(event, listener);\n }\n\n /** Emits an event, invoking each listener with the payload tuple. Returns true when the event had listeners. */\n public emit<TEventKey extends EventKey<TEvents>>(event: TEventKey, ...args: TEvents[TEventKey]): boolean {\n return this.dispatch(event, args);\n }\n\n /** @internal */\n protected emitSafe<TEventKey extends EventKey<TEvents>>(event: TEventKey, ...args: TEvents[TEventKey]): boolean {\n return this.dispatchSafe(event, args);\n }\n\n /** @internal */\n protected emitSafeRaw(event: string | symbol, ...args: readonly unknown[]): boolean {\n return this.dispatchSafe(event as EventKey<TEvents>, args);\n }\n\n // safe-emit error hook. the default re-throws the error on a microtask. override to log\n /** @internal */\n protected onListenerError(error: unknown, _event: string | symbol): void {\n queueMicrotask(() => {\n throw error;\n });\n }\n\n private dispatch(event: EventKey<TEvents>, args: readonly unknown[]): boolean {\n const slot = this.registrations.get(event);\n if (!slot || slot.size === 0) return false;\n // snapshot so a listener that adds or removes mid-dispatch does not change this emit\n const current = [...slot];\n for (const registration of current) {\n registration.call(...args);\n }\n return true;\n }\n\n private dispatchSafe(event: EventKey<TEvents>, args: readonly unknown[]): boolean {\n const slot = this.registrations.get(event);\n if (!slot || slot.size === 0) return false;\n const current = [...slot];\n for (const registration of current) {\n try {\n registration.call(...args);\n } catch (error) {\n this.onListenerError(error, event);\n }\n }\n return true;\n }\n\n /** Removes every listener for an event, or for all events when no event is given. */\n public removeAllListeners(event?: EventKey<TEvents>): this {\n if (event === undefined) this.registrations.clear();\n else this.registrations.delete(event);\n return this;\n }\n\n /** Counts the listeners registered for an event. */\n public listenerCount<TEventKey extends EventKey<TEvents>>(event: TEventKey): number {\n return this.registrations.get(event)?.size ?? 0;\n }\n\n /** Returns the event names with at least one listener. */\n public eventNames(): EventKey<TEvents>[] {\n return [...this.registrations.keys()];\n }\n\n /**\n * Waits for the next emission of an event, resolving with its payload tuple.\n * With a filter, resolves on the first payload the filter accepts.\n *\n * @param event - The event to wait for.\n * @param options - An optional filter, abort signal, and timeout.\n * @returns A promise for the matching payload. Rejects with a {@link WaitForError} when aborted or timed out.\n * @example\n * ```ts\n * const ee = new TypedEventEmitter<{ ping: [n: number] }>();\n * const pending = ee.waitFor('ping', { filter: n => n > 0, timeoutMs: 1000 });\n * ee.emit('ping', -1); // ignored by filter\n * ee.emit('ping', 42); // resolves pending with [42]\n * ```\n */\n public waitFor<TEventKey extends EventKey<TEvents>>(\n event: TEventKey,\n options?: WaitForOptions<TEvents[TEventKey]>\n ): Promise<TEvents[TEventKey]> {\n return new Promise<TEvents[TEventKey]>((resolve, reject) => {\n const onEvent = (...args: TEvents[TEventKey]): void => {\n if (options?.filter) {\n let matched: boolean;\n try {\n matched = options.filter(...args);\n } catch (error) {\n cleanup();\n reject(Error.isError(error) ? error : new Error(String(error)));\n return;\n }\n if (!matched) return;\n }\n cleanup();\n resolve(args);\n };\n\n const onAbort = (): void => {\n cleanup();\n reject(new WaitForError('aborted'));\n };\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const cleanup = (): void => {\n this.off(event, onEvent);\n options?.signal?.removeEventListener('abort', onAbort);\n if (timer !== undefined) clearTimeout(timer);\n };\n\n this.on(event, onEvent);\n\n if (options?.signal) {\n if (options.signal.aborted) {\n onAbort();\n return;\n }\n options.signal.addEventListener('abort', onAbort, { once: true });\n }\n\n if (options?.timeoutMs !== undefined) {\n const timeoutMs = options.timeoutMs;\n timer = setTimeout(() => {\n cleanup();\n reject(new WaitForError('timeout', timeoutMs));\n }, timeoutMs);\n }\n });\n }\n\n private slot(event: EventKey<TEvents>): Set<Registration> {\n let slot = this.registrations.get(event);\n if (!slot) {\n slot = new Set();\n this.registrations.set(event, slot);\n }\n return slot;\n }\n\n private drop(event: EventKey<TEvents>, original: BroadListener): void {\n const slot = this.registrations.get(event);\n if (!slot) return;\n for (const registration of slot) {\n if (registration.original === original) {\n this.removeRegistration(event, registration);\n break;\n }\n }\n }\n\n // deletes the empty slot so eventNames() excludes the event and dead keys do not accumulate\n private removeRegistration(event: EventKey<TEvents>, registration: Registration): void {\n const slot = this.registrations.get(event);\n if (!slot) return;\n slot.delete(registration);\n if (slot.size === 0) this.registrations.delete(event);\n }\n}\n","export { TypedEventEmitter } from './TypedEventEmitter';\nexport { WaitForError } from './WaitForError';\nexport type { EventMap, NoEvents, WaitForOptions } from './types';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -1,6 +1,91 @@
1
+ //#region src/types.d.ts
2
+ /** Event map with no events, the default for an emitter that emits nothing. */
3
+ type NoEvents = Record<never, readonly unknown[]>;
4
+ /**
5
+ * Constrains an event map so every value is a payload tuple.
6
+ *
7
+ * @typeParam TEvents - Map of event names to readonly tuple payloads.
8
+ */
9
+ type EventMap<TEvents extends object> = { [K in keyof TEvents]: readonly unknown[] };
10
+ /** @internal */
11
+ type EventKey<TEvents extends object> = Extract<keyof TEvents, string | symbol>;
12
+ /**
13
+ * Options for {@link TypedEventEmitter.waitFor}.
14
+ *
15
+ * @typeParam TArgs - The awaited event's payload tuple.
16
+ */
17
+ interface WaitForOptions<TArgs extends readonly unknown[] = readonly unknown[]> {
18
+ /** Resolves only on a payload this predicate accepts. */
19
+ filter?: (...args: TArgs) => boolean;
20
+ /** Rejects the wait when this signal aborts. */
21
+ signal?: AbortSignal;
22
+ /** Rejects the wait after this many milliseconds. */
23
+ timeoutMs?: number;
24
+ }
25
+ //#endregion
26
+ //#region src/TypedEventEmitter.d.ts
27
+ /**
28
+ * Typed, cross-runtime event emitter with per-event tuple payloads.
29
+ *
30
+ * @typeParam TEvents - Map of event names to readonly tuple payloads.
31
+ */
32
+ declare class TypedEventEmitter<TEvents extends EventMap<TEvents>> {
33
+ private readonly registrations;
34
+ /** Registers a persistent listener for an event. */
35
+ on<TEventKey extends EventKey<TEvents>>(event: TEventKey, listener: (...args: TEvents[TEventKey]) => void): this;
36
+ /** Registers a listener removed after its first invocation. */
37
+ once<TEventKey extends EventKey<TEvents>>(event: TEventKey, listener: (...args: TEvents[TEventKey]) => void): this;
38
+ /** Removes a previously registered listener. */
39
+ off<TEventKey extends EventKey<TEvents>>(event: TEventKey, listener: (...args: TEvents[TEventKey]) => void): this;
40
+ /** Alias of {@link TypedEventEmitter.off}. */
41
+ removeListener<TEventKey extends EventKey<TEvents>>(event: TEventKey, listener: (...args: TEvents[TEventKey]) => void): this;
42
+ /** Emits an event, invoking each listener with the payload tuple. Returns true when the event had listeners. */
43
+ emit<TEventKey extends EventKey<TEvents>>(event: TEventKey, ...args: TEvents[TEventKey]): boolean;
44
+ /** @internal */
45
+ protected emitSafe<TEventKey extends EventKey<TEvents>>(event: TEventKey, ...args: TEvents[TEventKey]): boolean;
46
+ /** @internal */
47
+ protected emitSafeRaw(event: string | symbol, ...args: readonly unknown[]): boolean;
48
+ /** @internal */
49
+ protected onListenerError(error: unknown, _event: string | symbol): void;
50
+ private dispatch;
51
+ private dispatchSafe;
52
+ /** Removes every listener for an event, or for all events when no event is given. */
53
+ removeAllListeners(event?: EventKey<TEvents>): this;
54
+ /** Counts the listeners registered for an event. */
55
+ listenerCount<TEventKey extends EventKey<TEvents>>(event: TEventKey): number;
56
+ /** Returns the event names with at least one listener. */
57
+ eventNames(): EventKey<TEvents>[];
58
+ /**
59
+ * Waits for the next emission of an event, resolving with its payload tuple.
60
+ * With a filter, resolves on the first payload the filter accepts.
61
+ *
62
+ * @param event - The event to wait for.
63
+ * @param options - An optional filter, abort signal, and timeout.
64
+ * @returns A promise for the matching payload. Rejects with a {@link WaitForError} when aborted or timed out.
65
+ * @example
66
+ * ```ts
67
+ * const ee = new TypedEventEmitter<{ ping: [n: number] }>();
68
+ * const pending = ee.waitFor('ping', { filter: n => n > 0, timeoutMs: 1000 });
69
+ * ee.emit('ping', -1); // ignored by filter
70
+ * ee.emit('ping', 42); // resolves pending with [42]
71
+ * ```
72
+ */
73
+ waitFor<TEventKey extends EventKey<TEvents>>(event: TEventKey, options?: WaitForOptions<TEvents[TEventKey]>): Promise<TEvents[TEventKey]>;
74
+ private slot;
75
+ private drop;
76
+ private removeRegistration;
77
+ }
78
+ //#endregion
79
+ //#region src/WaitForError.d.ts
80
+ /** Rejection thrown when a {@link TypedEventEmitter.waitFor} is aborted or times out. */
81
+ declare class WaitForError extends Error {
82
+ readonly reason: 'aborted' | 'timeout';
83
+ constructor(reason: 'aborted' | 'timeout', timeoutMs?: number);
84
+ }
85
+ //#endregion
1
86
  //#region src/index.d.ts
2
87
  /** Package version */
3
88
  declare const version: string;
4
89
  //#endregion
5
- export { version };
90
+ export { type EventMap, type NoEvents, TypedEventEmitter, WaitForError, type WaitForOptions, version };
6
91
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,7 +1,195 @@
1
+ //#region src/WaitForError.ts
2
+ /** Rejection thrown when a {@link TypedEventEmitter.waitFor} is aborted or times out. */
3
+ var WaitForError = class extends Error {
4
+ reason;
5
+ constructor(reason, timeoutMs) {
6
+ super(reason === "timeout" ? `waitFor timed out after ${timeoutMs}ms.` : "waitFor was aborted via its AbortSignal.");
7
+ this.name = "WaitForError";
8
+ this.reason = reason;
9
+ }
10
+ };
11
+
12
+ //#endregion
13
+ //#region src/TypedEventEmitter.ts
14
+ /**
15
+ * Typed, cross-runtime event emitter with per-event tuple payloads.
16
+ *
17
+ * @typeParam TEvents - Map of event names to readonly tuple payloads.
18
+ */
19
+ var TypedEventEmitter = class {
20
+ registrations = /* @__PURE__ */ new Map();
21
+ /** Registers a persistent listener for an event. */
22
+ on(event, listener) {
23
+ const original = listener;
24
+ this.slot(event).add({
25
+ call: original,
26
+ original
27
+ });
28
+ return this;
29
+ }
30
+ /** Registers a listener removed after its first invocation. */
31
+ once(event, listener) {
32
+ const original = listener;
33
+ let fired = false;
34
+ const registration = {
35
+ call: (...args) => {
36
+ if (fired) return;
37
+ fired = true;
38
+ this.removeRegistration(event, registration);
39
+ original(...args);
40
+ },
41
+ original
42
+ };
43
+ this.slot(event).add(registration);
44
+ return this;
45
+ }
46
+ /** Removes a previously registered listener. */
47
+ off(event, listener) {
48
+ this.drop(event, listener);
49
+ return this;
50
+ }
51
+ /** Alias of {@link TypedEventEmitter.off}. */
52
+ removeListener(event, listener) {
53
+ return this.off(event, listener);
54
+ }
55
+ /** Emits an event, invoking each listener with the payload tuple. Returns true when the event had listeners. */
56
+ emit(event, ...args) {
57
+ return this.dispatch(event, args);
58
+ }
59
+ /** @internal */
60
+ emitSafe(event, ...args) {
61
+ return this.dispatchSafe(event, args);
62
+ }
63
+ /** @internal */
64
+ emitSafeRaw(event, ...args) {
65
+ return this.dispatchSafe(event, args);
66
+ }
67
+ /** @internal */
68
+ onListenerError(error, _event) {
69
+ queueMicrotask(() => {
70
+ throw error;
71
+ });
72
+ }
73
+ dispatch(event, args) {
74
+ const slot = this.registrations.get(event);
75
+ if (!slot || slot.size === 0) return false;
76
+ const current = [...slot];
77
+ for (const registration of current) registration.call(...args);
78
+ return true;
79
+ }
80
+ dispatchSafe(event, args) {
81
+ const slot = this.registrations.get(event);
82
+ if (!slot || slot.size === 0) return false;
83
+ const current = [...slot];
84
+ for (const registration of current) try {
85
+ registration.call(...args);
86
+ } catch (error) {
87
+ this.onListenerError(error, event);
88
+ }
89
+ return true;
90
+ }
91
+ /** Removes every listener for an event, or for all events when no event is given. */
92
+ removeAllListeners(event) {
93
+ if (event === void 0) this.registrations.clear();
94
+ else this.registrations.delete(event);
95
+ return this;
96
+ }
97
+ /** Counts the listeners registered for an event. */
98
+ listenerCount(event) {
99
+ return this.registrations.get(event)?.size ?? 0;
100
+ }
101
+ /** Returns the event names with at least one listener. */
102
+ eventNames() {
103
+ return [...this.registrations.keys()];
104
+ }
105
+ /**
106
+ * Waits for the next emission of an event, resolving with its payload tuple.
107
+ * With a filter, resolves on the first payload the filter accepts.
108
+ *
109
+ * @param event - The event to wait for.
110
+ * @param options - An optional filter, abort signal, and timeout.
111
+ * @returns A promise for the matching payload. Rejects with a {@link WaitForError} when aborted or timed out.
112
+ * @example
113
+ * ```ts
114
+ * const ee = new TypedEventEmitter<{ ping: [n: number] }>();
115
+ * const pending = ee.waitFor('ping', { filter: n => n > 0, timeoutMs: 1000 });
116
+ * ee.emit('ping', -1); // ignored by filter
117
+ * ee.emit('ping', 42); // resolves pending with [42]
118
+ * ```
119
+ */
120
+ waitFor(event, options) {
121
+ return new Promise((resolve, reject) => {
122
+ const onEvent = (...args) => {
123
+ if (options?.filter) {
124
+ let matched;
125
+ try {
126
+ matched = options.filter(...args);
127
+ } catch (error) {
128
+ cleanup();
129
+ reject(Error.isError(error) ? error : new Error(String(error)));
130
+ return;
131
+ }
132
+ if (!matched) return;
133
+ }
134
+ cleanup();
135
+ resolve(args);
136
+ };
137
+ const onAbort = () => {
138
+ cleanup();
139
+ reject(new WaitForError("aborted"));
140
+ };
141
+ let timer;
142
+ const cleanup = () => {
143
+ this.off(event, onEvent);
144
+ options?.signal?.removeEventListener("abort", onAbort);
145
+ if (timer !== void 0) clearTimeout(timer);
146
+ };
147
+ this.on(event, onEvent);
148
+ if (options?.signal) {
149
+ if (options.signal.aborted) {
150
+ onAbort();
151
+ return;
152
+ }
153
+ options.signal.addEventListener("abort", onAbort, { once: true });
154
+ }
155
+ if (options?.timeoutMs !== void 0) {
156
+ const timeoutMs = options.timeoutMs;
157
+ timer = setTimeout(() => {
158
+ cleanup();
159
+ reject(new WaitForError("timeout", timeoutMs));
160
+ }, timeoutMs);
161
+ }
162
+ });
163
+ }
164
+ slot(event) {
165
+ let slot = this.registrations.get(event);
166
+ if (!slot) {
167
+ slot = /* @__PURE__ */ new Set();
168
+ this.registrations.set(event, slot);
169
+ }
170
+ return slot;
171
+ }
172
+ drop(event, original) {
173
+ const slot = this.registrations.get(event);
174
+ if (!slot) return;
175
+ for (const registration of slot) if (registration.original === original) {
176
+ this.removeRegistration(event, registration);
177
+ break;
178
+ }
179
+ }
180
+ removeRegistration(event, registration) {
181
+ const slot = this.registrations.get(event);
182
+ if (!slot) return;
183
+ slot.delete(registration);
184
+ if (slot.size === 0) this.registrations.delete(event);
185
+ }
186
+ };
187
+
188
+ //#endregion
1
189
  //#region src/index.ts
2
190
  /** Package version */
3
- const version = "0.0.1";
191
+ const version = "0.1.0-next.0";
4
192
 
5
193
  //#endregion
6
- export { version };
194
+ export { TypedEventEmitter, WaitForError, version };
7
195
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":";;AACA,MAAa"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/WaitForError.ts","../src/TypedEventEmitter.ts","../src/index.ts"],"sourcesContent":["/** Rejection thrown when a {@link TypedEventEmitter.waitFor} is aborted or times out. */\nexport class WaitForError extends Error {\n public readonly reason: 'aborted' | 'timeout';\n\n constructor(reason: 'aborted' | 'timeout', timeoutMs?: number) {\n super(\n reason === 'timeout'\n ? `waitFor timed out after ${timeoutMs}ms.`\n : 'waitFor was aborted via its AbortSignal.'\n );\n this.name = 'WaitForError';\n this.reason = reason;\n }\n}\n","import { WaitForError } from './WaitForError';\n\nimport type { EventKey, EventMap, WaitForOptions } from './types';\n\ntype BroadListener = (...args: readonly unknown[]) => void;\n\ninterface Registration {\n readonly call: BroadListener;\n // off() matches by original, so off(userListener) removes a once registration whose call is the wrapper\n readonly original: BroadListener;\n}\n\n/**\n * Typed, cross-runtime event emitter with per-event tuple payloads.\n *\n * @typeParam TEvents - Map of event names to readonly tuple payloads.\n */\nexport class TypedEventEmitter<TEvents extends EventMap<TEvents>> {\n private readonly registrations = new Map<EventKey<TEvents>, Set<Registration>>();\n\n /** Registers a persistent listener for an event. */\n public on<TEventKey extends EventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n // justified: the public per-event tuple erases to the common callable stored internally.\n const original = listener as BroadListener;\n this.slot(event).add({ call: original, original });\n return this;\n }\n\n /** Registers a listener removed after its first invocation. */\n public once<TEventKey extends EventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n // justified: same erase as on().\n const original = listener as BroadListener;\n let fired = false;\n const registration: Registration = {\n // fired guards a re-entrant emit that re-invokes this registration from the outer snapshot\n call: (...args) => {\n if (fired) return;\n fired = true;\n this.removeRegistration(event, registration);\n original(...args);\n },\n original\n };\n this.slot(event).add(registration);\n return this;\n }\n\n /** Removes a previously registered listener. */\n public off<TEventKey extends EventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n // justified: same erase as on().\n this.drop(event, listener as BroadListener);\n return this;\n }\n\n /** Alias of {@link TypedEventEmitter.off}. */\n public removeListener<TEventKey extends EventKey<TEvents>>(\n event: TEventKey,\n listener: (...args: TEvents[TEventKey]) => void\n ): this {\n return this.off(event, listener);\n }\n\n /** Emits an event, invoking each listener with the payload tuple. Returns true when the event had listeners. */\n public emit<TEventKey extends EventKey<TEvents>>(event: TEventKey, ...args: TEvents[TEventKey]): boolean {\n return this.dispatch(event, args);\n }\n\n /** @internal */\n protected emitSafe<TEventKey extends EventKey<TEvents>>(event: TEventKey, ...args: TEvents[TEventKey]): boolean {\n return this.dispatchSafe(event, args);\n }\n\n /** @internal */\n protected emitSafeRaw(event: string | symbol, ...args: readonly unknown[]): boolean {\n return this.dispatchSafe(event as EventKey<TEvents>, args);\n }\n\n // safe-emit error hook. the default re-throws the error on a microtask. override to log\n /** @internal */\n protected onListenerError(error: unknown, _event: string | symbol): void {\n queueMicrotask(() => {\n throw error;\n });\n }\n\n private dispatch(event: EventKey<TEvents>, args: readonly unknown[]): boolean {\n const slot = this.registrations.get(event);\n if (!slot || slot.size === 0) return false;\n // snapshot so a listener that adds or removes mid-dispatch does not change this emit\n const current = [...slot];\n for (const registration of current) {\n registration.call(...args);\n }\n return true;\n }\n\n private dispatchSafe(event: EventKey<TEvents>, args: readonly unknown[]): boolean {\n const slot = this.registrations.get(event);\n if (!slot || slot.size === 0) return false;\n const current = [...slot];\n for (const registration of current) {\n try {\n registration.call(...args);\n } catch (error) {\n this.onListenerError(error, event);\n }\n }\n return true;\n }\n\n /** Removes every listener for an event, or for all events when no event is given. */\n public removeAllListeners(event?: EventKey<TEvents>): this {\n if (event === undefined) this.registrations.clear();\n else this.registrations.delete(event);\n return this;\n }\n\n /** Counts the listeners registered for an event. */\n public listenerCount<TEventKey extends EventKey<TEvents>>(event: TEventKey): number {\n return this.registrations.get(event)?.size ?? 0;\n }\n\n /** Returns the event names with at least one listener. */\n public eventNames(): EventKey<TEvents>[] {\n return [...this.registrations.keys()];\n }\n\n /**\n * Waits for the next emission of an event, resolving with its payload tuple.\n * With a filter, resolves on the first payload the filter accepts.\n *\n * @param event - The event to wait for.\n * @param options - An optional filter, abort signal, and timeout.\n * @returns A promise for the matching payload. Rejects with a {@link WaitForError} when aborted or timed out.\n * @example\n * ```ts\n * const ee = new TypedEventEmitter<{ ping: [n: number] }>();\n * const pending = ee.waitFor('ping', { filter: n => n > 0, timeoutMs: 1000 });\n * ee.emit('ping', -1); // ignored by filter\n * ee.emit('ping', 42); // resolves pending with [42]\n * ```\n */\n public waitFor<TEventKey extends EventKey<TEvents>>(\n event: TEventKey,\n options?: WaitForOptions<TEvents[TEventKey]>\n ): Promise<TEvents[TEventKey]> {\n return new Promise<TEvents[TEventKey]>((resolve, reject) => {\n const onEvent = (...args: TEvents[TEventKey]): void => {\n if (options?.filter) {\n let matched: boolean;\n try {\n matched = options.filter(...args);\n } catch (error) {\n cleanup();\n reject(Error.isError(error) ? error : new Error(String(error)));\n return;\n }\n if (!matched) return;\n }\n cleanup();\n resolve(args);\n };\n\n const onAbort = (): void => {\n cleanup();\n reject(new WaitForError('aborted'));\n };\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const cleanup = (): void => {\n this.off(event, onEvent);\n options?.signal?.removeEventListener('abort', onAbort);\n if (timer !== undefined) clearTimeout(timer);\n };\n\n this.on(event, onEvent);\n\n if (options?.signal) {\n if (options.signal.aborted) {\n onAbort();\n return;\n }\n options.signal.addEventListener('abort', onAbort, { once: true });\n }\n\n if (options?.timeoutMs !== undefined) {\n const timeoutMs = options.timeoutMs;\n timer = setTimeout(() => {\n cleanup();\n reject(new WaitForError('timeout', timeoutMs));\n }, timeoutMs);\n }\n });\n }\n\n private slot(event: EventKey<TEvents>): Set<Registration> {\n let slot = this.registrations.get(event);\n if (!slot) {\n slot = new Set();\n this.registrations.set(event, slot);\n }\n return slot;\n }\n\n private drop(event: EventKey<TEvents>, original: BroadListener): void {\n const slot = this.registrations.get(event);\n if (!slot) return;\n for (const registration of slot) {\n if (registration.original === original) {\n this.removeRegistration(event, registration);\n break;\n }\n }\n }\n\n // deletes the empty slot so eventNames() excludes the event and dead keys do not accumulate\n private removeRegistration(event: EventKey<TEvents>, registration: Registration): void {\n const slot = this.registrations.get(event);\n if (!slot) return;\n slot.delete(registration);\n if (slot.size === 0) this.registrations.delete(event);\n }\n}\n","export { TypedEventEmitter } from './TypedEventEmitter';\nexport { WaitForError } from './WaitForError';\nexport type { EventMap, NoEvents, WaitForOptions } from './types';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,54 +1,58 @@
1
1
  {
2
- "name": "@seedcord/event-emitter",
3
- "type": "module",
4
- "version": "0.0.1",
5
- "description": "Typed, cross-runtime event emitter for Seedcord",
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/seedcord/seedcord.git",
9
- "directory": "packages/event-emitter"
10
- },
11
- "types": "./dist/index.d.mts",
12
- "exports": {
13
- ".": {
14
- "import": {
15
- "types": "./dist/index.d.mts",
16
- "default": "./dist/index.mjs"
17
- },
18
- "require": {
19
- "types": "./dist/index.d.cts",
20
- "default": "./dist/index.cjs"
21
- }
22
- }
23
- },
24
- "files": [
25
- "dist",
26
- "package.json",
27
- "README.md",
28
- "LICENSE"
29
- ],
30
- "scripts": {
31
- "build": "tsdown",
32
- "clean": "rm -rf dist",
33
- "lint": "eslint 'src/**/*.{ts,tsx}' 'tests/**/*.{ts,tsx}' --cache",
34
- "lint:fix": "eslint 'src/**/*.{ts,tsx}' 'tests/**/*.{ts,tsx}' --fix --cache",
35
- "fmt": "prettier --write 'src/**/*.{ts,tsx,json,md}' 'tests/**/*.{ts,tsx,json,md}' --cache",
36
- "fmt:check": "prettier --check 'src/**/*.{ts,tsx,json,md}' 'tests/**/*.{ts,tsx,json,md}' --cache",
37
- "tc": "tsgo --noEmit",
38
- "test": "vitest run",
39
- "test:watch": "vitest dev",
40
- "coverage": "vitest run --coverage"
41
- },
42
- "peerDependencies": {
43
- "typescript": "catalog:peer"
44
- },
45
- "devDependencies": {
46
- "@seedcord/eslint-config": "workspace:*",
47
- "@seedcord/tsconfig": "workspace:*",
48
- "@seedcord/tsdown-config": "workspace:*"
49
- },
50
- "publishConfig": {
51
- "access": "public",
52
- "provenance": true
2
+ "name": "@seedcord/event-emitter",
3
+ "type": "module",
4
+ "version": "0.1.0-next.0",
5
+ "engines": {
6
+ "node": ">=24.3"
7
+ },
8
+ "description": "Typed, cross-runtime event emitter for Seedcord",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/seedcord/seedcord.git",
12
+ "directory": "packages/event-emitter"
13
+ },
14
+ "types": "./dist/index.d.mts",
15
+ "exports": {
16
+ ".": {
17
+ "import": {
18
+ "types": "./dist/index.d.mts",
19
+ "default": "./dist/index.mjs"
20
+ },
21
+ "require": {
22
+ "types": "./dist/index.d.cts",
23
+ "default": "./dist/index.cjs"
24
+ }
53
25
  }
54
- }
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "package.json",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "peerDependencies": {
34
+ "typescript": "^6.0.3"
35
+ },
36
+ "devDependencies": {
37
+ "@seedcord/eslint-config": "1.5.0-next.3",
38
+ "@seedcord/tsconfig": "2.0.1",
39
+ "@seedcord/tsdown-config": "2.0.1"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public",
43
+ "provenance": true
44
+ },
45
+ "scripts": {
46
+ "build": "tsdown",
47
+ "clean": "rm -rf dist",
48
+ "lint": "eslint 'src/**/*.{ts,tsx}' 'tests/**/*.{ts,tsx}' --cache",
49
+ "lint:fix": "eslint 'src/**/*.{ts,tsx}' 'tests/**/*.{ts,tsx}' --fix --cache",
50
+ "fmt": "prettier --write 'src/**/*.{ts,tsx,json,md}' 'tests/**/*.{ts,tsx,json,md}' --cache",
51
+ "fmt:check": "prettier --check 'src/**/*.{ts,tsx,json,md}' 'tests/**/*.{ts,tsx,json,md}' --cache",
52
+ "tc": "tsgo --noEmit",
53
+ "test": "vitest run",
54
+ "test:watch": "vitest dev",
55
+ "coverage": "vitest run --coverage"
56
+ },
57
+ "readme": "<p align=\"center\">\n <picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://cdn.seedcord.org/assets/wordmark-dark.webp\" />\n <img src=\"https://cdn.seedcord.org/assets/wordmark-light.webp\" alt=\"seedcord\" width=\"440\" />\n </picture>\n</p>\n\n# @seedcord/event-emitter\n\nTyped, cross-runtime event emitter for Seedcord\n\nPart of the [seedcord](https://github.com/seedcord/seedcord) framework. Until a major v1.0.0 release, expect breaking changes in minor versions.\n"
58
+ }