evnty 5.0.0 → 5.1.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.
Files changed (69) hide show
  1. package/README.md +28 -28
  2. package/build/async.cjs +101 -0
  3. package/build/async.cjs.map +1 -0
  4. package/build/async.d.ts +37 -0
  5. package/build/async.js +83 -0
  6. package/build/async.js.map +1 -0
  7. package/build/broadcast.cjs +205 -0
  8. package/build/broadcast.cjs.map +1 -0
  9. package/build/broadcast.d.ts +164 -0
  10. package/build/broadcast.js +184 -0
  11. package/build/broadcast.js.map +1 -0
  12. package/build/dispatch-result.cjs +154 -0
  13. package/build/dispatch-result.cjs.map +1 -0
  14. package/build/dispatch-result.d.ts +49 -0
  15. package/build/dispatch-result.js +127 -0
  16. package/build/dispatch-result.js.map +1 -0
  17. package/build/event.cjs +92 -127
  18. package/build/event.cjs.map +1 -1
  19. package/build/event.d.ts +92 -167
  20. package/build/event.js +90 -122
  21. package/build/event.js.map +1 -1
  22. package/build/index.cjs +3 -1
  23. package/build/index.cjs.map +1 -1
  24. package/build/index.d.ts +3 -1
  25. package/build/index.js +3 -1
  26. package/build/index.js.map +1 -1
  27. package/build/iterator.cjs +578 -91
  28. package/build/iterator.cjs.map +1 -1
  29. package/build/iterator.d.ts +178 -7
  30. package/build/iterator.js +579 -92
  31. package/build/iterator.js.map +1 -1
  32. package/build/listener-registry.cjs +114 -0
  33. package/build/listener-registry.cjs.map +1 -0
  34. package/build/listener-registry.d.ts +54 -0
  35. package/build/listener-registry.js +104 -0
  36. package/build/listener-registry.js.map +1 -0
  37. package/build/ring-buffer.cjs +171 -0
  38. package/build/ring-buffer.cjs.map +1 -0
  39. package/build/ring-buffer.d.ts +80 -0
  40. package/build/ring-buffer.js +161 -0
  41. package/build/ring-buffer.js.map +1 -0
  42. package/build/sequence.cjs +34 -35
  43. package/build/sequence.cjs.map +1 -1
  44. package/build/sequence.d.ts +38 -24
  45. package/build/sequence.js +34 -35
  46. package/build/sequence.js.map +1 -1
  47. package/build/signal.cjs +26 -35
  48. package/build/signal.cjs.map +1 -1
  49. package/build/signal.d.ts +36 -39
  50. package/build/signal.js +26 -35
  51. package/build/signal.js.map +1 -1
  52. package/build/types.cjs +0 -11
  53. package/build/types.cjs.map +1 -1
  54. package/build/types.d.ts +86 -9
  55. package/build/types.js +1 -5
  56. package/build/types.js.map +1 -1
  57. package/build/utils.cjs +202 -22
  58. package/build/utils.cjs.map +1 -1
  59. package/build/utils.d.ts +85 -26
  60. package/build/utils.js +181 -22
  61. package/build/utils.js.map +1 -1
  62. package/package.json +27 -25
  63. package/src/__tests__/example.js +19 -24
  64. package/src/index.ts +3 -1
  65. package/build/callable.cjs +0 -72
  66. package/build/callable.cjs.map +0 -1
  67. package/build/callable.d.ts +0 -34
  68. package/build/callable.js +0 -51
  69. package/build/callable.js.map +0 -1
package/build/event.d.ts CHANGED
@@ -1,131 +1,89 @@
1
- import { MaybePromise, Callback, Listener, FilterFunction, Predicate, Mapper, Reducer } from './types.js';
2
- import { Callable, CallableAsyncIterator } from './callable.js';
1
+ import { Callback, Listener, FilterFunction, Predicate, Mapper, Reducer, Action, Fn, Emitter, MaybePromise, Promiseable } from './types.js';
2
+ import { Signal } from './signal.js';
3
+ import { DispatchResult } from './dispatch-result.js';
4
+ export type Unsubscribe = Action;
3
5
  /**
4
- * Represents an unsubscribe function that can be called to remove a listener.
5
- * Provides additional utilities for chaining and conditional unsubscription.
6
- *
7
6
  * @internal
8
7
  */
9
- export declare class Unsubscribe extends Callable<[], MaybePromise<void>> {
10
- private _done;
11
- constructor(callback: Callback);
12
- get done(): boolean;
13
- /**
14
- * Creates a new unsubscribe function that executes the given callback before this unsubscribe.
15
- *
16
- * @param callback - The callback to execute before unsubscribing.
17
- * @returns {Unsubscribe} A new Unsubscribe instance.
18
- */
19
- pre(callback: Callback): Unsubscribe;
20
- /**
21
- * Creates a new unsubscribe function that executes the given callback after this unsubscribe.
22
- *
23
- * @param callback - The callback to execute after unsubscribing.
24
- * @returns {Unsubscribe} A new Unsubscribe instance.
25
- */
26
- post(callback: Callback): Unsubscribe;
27
- /**
28
- * Creates a new unsubscribe function that only executes after being called a specified number of times.
29
- *
30
- * @param count - The number of times this must be called before actually unsubscribing.
31
- * @returns {Unsubscribe} A new Unsubscribe instance.
32
- */
33
- countdown(count: number): Unsubscribe;
34
- }
35
- /**
36
- * Wraps an array of values or promises (typically listener results) and provides batch resolution.
37
- *
38
- * @template T
39
- */
40
- export declare class EventResult<T> implements PromiseLike<T[]> {
8
+ export declare class EventIterator<T> implements AsyncIterator<T, void, void> {
41
9
  #private;
42
- readonly [Symbol.toStringTag] = "EventResult";
43
- /**
44
- * @param results - An array of values or Promise-returning listener calls.
45
- */
46
- constructor(results: MaybePromise<T>[]);
47
- then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T[]) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
48
- /**
49
- * Resolves all listener results, rejecting if any promise rejects.
50
- *
51
- * @returns {Promise<T[]>} A promise that fulfills with an array of all resolved values.
52
- */
53
- all(): Promise<T[]>;
54
- /**
55
- * Waits for all listener results to settle, regardless of fulfillment or rejection.
56
- *
57
- * @returns {Promise<PromiseSettledResult<T>[]>} A promise that fulfills with an array of each result's settled status and value/reason.
58
- */
59
- settled(): Promise<PromiseSettledResult<T>[]>;
10
+ constructor(signal: Signal<T>);
11
+ next(): Promise<IteratorResult<T, void>>;
12
+ return(): Promise<IteratorResult<T, void>>;
60
13
  }
61
14
  /**
62
- * A class representing a multi-listener event emitter with async support.
63
- * Events allow multiple listeners to react to emitted values, with each listener
64
- * potentially returning a result. All listeners are called for each emission.
65
- *
66
- * Key characteristics:
67
- * - Multiple listeners - all are called for each emission
68
- * - Listeners can return values collected in EventResult
69
- * - Supports async listeners and async iteration
70
- * - Provides lifecycle hooks for listener management
71
- * - Memory efficient using RingBuffer for storage
15
+ * Multi-listener event emitter with async support.
16
+ * All registered listeners are called for each emission, and their return
17
+ * values are collected in a DispatchResult. Supports async iteration and
18
+ * an `onDispose` callback for cleanup.
72
19
  *
73
20
  * Differs from:
74
- * - Signal: Events have multiple persistent listeners vs Signal's one-time resolution per consumer
75
- * - Sequence: Events broadcast to all listeners vs Sequence's single consumer queue
21
+ * - Signal: Event has persistent listeners; Signal is promise-based (receive per round)
22
+ * - Sequence: Event broadcasts to all listeners; Sequence is a single-consumer queue
76
23
  *
77
24
  * @template T - The type of value emitted to listeners (event payload)
78
25
  * @template R - The return type of listener functions
79
26
  */
80
- export declare class Event<T = unknown, R = unknown> extends CallableAsyncIterator<T, EventResult<void | R>> {
81
- /**
82
- * The ring buffer containing all registered listeners for the event.
83
- */
84
- private listeners;
85
- /**
86
- * The ring buffer containing hook listeners that respond to listener lifecycle events.
87
- */
88
- private hooks;
89
- /**
90
- * Flag indicating whether this event has been disposed.
91
- */
92
- private _disposed;
93
- /**
94
- * A function that disposes of the event and its listeners.
95
- */
96
- readonly dispose: Callback;
27
+ export declare class Event<T = unknown, R = unknown> implements Emitter<T, DispatchResult<void | R>>, Promiseable<T>, Promise<T>, Disposable, AsyncIterable<T> {
28
+ #private;
97
29
  readonly [Symbol.toStringTag] = "Event";
98
30
  /**
99
31
  * Creates a new event.
100
32
  *
101
- * @param dispose - A function to call on the event disposal.
33
+ * @param onDispose - A function to call on the event disposal.
102
34
  *
35
+ * @example
103
36
  * ```typescript
104
37
  * // Create a click event.
105
38
  * const clickEvent = new Event<[x: number, y: number], void>();
106
39
  * clickEvent.on(([x, y]) => console.log(`Clicked at ${x}, ${y}`));
40
+ * clickEvent.emit([10, 20]);
107
41
  * ```
108
42
  */
109
- constructor(dispose?: Callback);
43
+ constructor(onDispose?: Callback);
110
44
  /**
111
- * The number of listeners for the event.
45
+ * Returns a bound emit function for use as a callback.
46
+ * Useful for passing to other APIs that expect a function.
112
47
  *
113
- * @readonly
114
- * @type {number}
48
+ * @example
49
+ * ```typescript
50
+ * const event = new Event<string>();
51
+ * someApi.onMessage(event.sink);
52
+ * ```
53
+ */
54
+ get sink(): Fn<[T], DispatchResult<void | R>>;
55
+ /**
56
+ * DOM EventListener interface compatibility.
57
+ * Allows the event to be used directly with addEventListener.
58
+ */
59
+ handleEvent(event: T): void;
60
+ /**
61
+ * The number of listeners for the event.
115
62
  */
116
63
  get size(): number;
117
64
  /**
118
- * Checks if the event has been disposed.
65
+ * Emits a value to all registered listeners.
66
+ * Each listener is called with the value and their return values are collected.
67
+ *
68
+ * @param value - The value to emit to all listeners.
69
+ * @returns A DispatchResult containing all listener return values.
119
70
  *
120
- * @returns {boolean} `true` if the event has been disposed; otherwise, `false`.
71
+ * @example
72
+ * ```typescript
73
+ * const event = new Event<string, number>();
74
+ * event.on(str => str.length);
75
+ * const result = event.emit('hello');
76
+ * await result.all(); // [5]
77
+ * ```
121
78
  */
122
- get disposed(): boolean;
79
+ emit(value: T): DispatchResult<void | R>;
123
80
  /**
124
81
  * Checks if the given listener is NOT registered for this event.
125
82
  *
126
83
  * @param listener - The listener function to check against the registered listeners.
127
- * @returns {boolean} `true` if the listener is not already registered; otherwise, `false`.
84
+ * @returns `true` if the listener is not already registered; otherwise, `false`.
128
85
  *
86
+ * @example
129
87
  * ```typescript
130
88
  * // Check if a listener is not already added
131
89
  * if (event.lacks(myListener)) {
@@ -138,8 +96,9 @@ export declare class Event<T = unknown, R = unknown> extends CallableAsyncIterat
138
96
  * Checks if the given listener is registered for this event.
139
97
  *
140
98
  * @param listener - The listener function to check.
141
- * @returns {boolean} `true` if the listener is currently registered; otherwise, `false`.
99
+ * @returns `true` if the listener is currently registered; otherwise, `false`.
142
100
  *
101
+ * @example
143
102
  * ```typescript
144
103
  * // Verify if a listener is registered
145
104
  * if (event.has(myListener)) {
@@ -152,8 +111,9 @@ export declare class Event<T = unknown, R = unknown> extends CallableAsyncIterat
152
111
  * Removes a specific listener from this event.
153
112
  *
154
113
  * @param listener - The listener to remove.
155
- * @returns {this} The event instance, allowing for method chaining.
114
+ * @returns The event instance for chaining.
156
115
  *
116
+ * @example
157
117
  * ```typescript
158
118
  * // Remove a listener
159
119
  * event.off(myListener);
@@ -161,14 +121,13 @@ export declare class Event<T = unknown, R = unknown> extends CallableAsyncIterat
161
121
  */
162
122
  off(listener: Listener<T, R>): this;
163
123
  /**
164
- * Registers a listener that gets triggered whenever the event is emitted.
165
- * This is the primary method for adding event handlers that will react to the event being triggered.
124
+ * Registers a listener that is called on every emission.
166
125
  *
167
126
  * @param listener - The function to call when the event occurs.
168
- * @returns {Unsubscribe} An object that can be used to unsubscribe the listener, ensuring easy cleanup.
127
+ * @returns A function that removes this listener when called.
169
128
  *
129
+ * @example
170
130
  * ```typescript
171
- * // Add a listener to an event
172
131
  * const unsubscribe = event.on((data) => {
173
132
  * console.log('Event data:', data);
174
133
  * });
@@ -176,75 +135,54 @@ export declare class Event<T = unknown, R = unknown> extends CallableAsyncIterat
176
135
  */
177
136
  on(listener: Listener<T, R>): Unsubscribe;
178
137
  /**
179
- * Adds a listener that will be called only once the next time the event is emitted.
180
- * This method is useful for one-time notifications or single-trigger scenarios.
138
+ * Registers a listener that is called only once on the next emission, then auto-removed.
181
139
  *
182
140
  * @param listener - The listener to trigger once.
183
- * @returns {Unsubscribe} An object that can be used to remove the listener if the event has not yet occurred.
141
+ * @returns A function that removes this listener when called (if it hasn't fired yet).
184
142
  *
143
+ * @example
185
144
  * ```typescript
186
- * // Register a one-time listener
187
- * const onceUnsubscribe = event.once((data) => {
145
+ * const cancel = event.once((data) => {
188
146
  * console.log('Received data once:', data);
189
147
  * });
190
148
  * ```
191
149
  */
192
150
  once(listener: Listener<T, R>): Unsubscribe;
193
151
  /**
194
- * Removes all listeners from the event, effectively resetting it. This is useful when you need to
195
- * cleanly dispose of all event handlers to prevent memory leaks or unwanted triggers after certain conditions.
152
+ * Removes all listeners from the event.
153
+ * Does not dispose the event - new listeners can still be added after clearing.
196
154
  *
197
- * @returns {this} The instance of the event, allowing for method chaining.
198
- *
199
- * ```typescript
200
- * const myEvent = new Event();
201
- * myEvent.on(data => console.log(data));
202
- * myEvent.clear(); // Clears all listeners
203
- * ```
155
+ * @returns The event instance for chaining.
204
156
  */
205
157
  clear(): this;
206
158
  /**
207
- * Waits for the next event emission and returns the emitted value.
208
- * This method allows the event to be used as a promise that resolves with the next emitted value.
159
+ * Waits for the next emission and returns the emitted value.
209
160
  *
210
- * @returns {Promise<T>} A promise that resolves with the next emitted event value.
161
+ * @returns A promise that resolves with the next emitted value.
211
162
  */
212
- next(): Promise<T>;
163
+ receive(): Promise<T>;
164
+ then<OK = T, ERR = never>(onfulfilled?: Fn<[T], MaybePromise<OK>> | null, onrejected?: Fn<[unknown], MaybePromise<ERR>> | null): Promise<OK | ERR>;
165
+ catch<ERR = never>(onrejected?: Fn<[unknown], MaybePromise<ERR>> | null): Promise<T | ERR>;
166
+ finally(onfinally?: Action | null): Promise<T>;
213
167
  /**
214
- * Waits for the event to settle, returning a `PromiseSettledResult`.
168
+ * Waits for the next emission via `receive()` and wraps the outcome in a
169
+ * `PromiseSettledResult` - always resolves, never rejects.
215
170
  *
216
- * @returns {Promise<PromiseSettledResult<T>>} A promise that resolves with the settled result.
171
+ * @returns A promise that resolves with the settled result.
217
172
  *
218
173
  * @example
219
174
  * ```typescript
220
175
  * const result = await event.settle();
221
176
  * if (result.status === 'fulfilled') {
222
- * console.log('Event fulfilled with value:', result.value);
177
+ * console.log('Value:', result.value);
223
178
  * } else {
224
- * console.error('Event rejected with reason:', result.reason);
179
+ * console.error('Reason:', result.reason);
225
180
  * }
226
181
  * ```
227
182
  */
228
183
  settle(): Promise<PromiseSettledResult<T>>;
229
- /**
230
- * Makes this event iterable using `for await...of` loops.
231
- *
232
- * @returns {AsyncIterator<T>} An async iterator that yields values as they are emitted by this event.
233
- *
234
- * ```typescript
235
- * // Assuming an event that emits numbers
236
- * const numberEvent = new Event<number>();
237
- * (async () => {
238
- * for await (const num of numberEvent) {
239
- * console.log('Number:', num);
240
- * }
241
- * })();
242
- * await numberEvent(1);
243
- * await numberEvent(2);
244
- * await numberEvent(3);
245
- * ```
246
- */
247
- [Symbol.asyncIterator](): AsyncIterator<T>;
184
+ [Symbol.asyncIterator](): AsyncIterator<T, void, void>;
185
+ dispose(): void;
248
186
  [Symbol.dispose](): void;
249
187
  }
250
188
  export type EventParameters<T> = T extends Event<infer P, any> ? P : never;
@@ -256,18 +194,14 @@ export type AllEventsResults<T extends Event<any, any>[]> = {
256
194
  [K in keyof T]: EventReturn<T[K]>;
257
195
  }[number];
258
196
  /**
259
- * Merges multiple events into a single event. This function takes any number of `Event` instances
260
- * and returns a new `Event` that triggers whenever any of the input events trigger. The parameters
261
- * and results of the merged event are derived from the input events, providing a flexible way to
262
- * handle multiple sources of events in a unified manner.
197
+ * Merges multiple events into a single event that triggers whenever any source triggers.
198
+ * Disposing the merged event unsubscribes from all sources.
263
199
  *
264
- * @template Events - An array of `Event` instances.
265
- * @param events - A rest parameter that takes multiple events to be merged.
266
- * @returns {Event<AllEventsParameters<Events>, AllEventsResults<Events>>} Returns a new `Event` instance
267
- * that triggers with the parameters and results of any of the merged input events.
200
+ * @param events - The events to merge.
201
+ * @returns A new Event that forwards emissions from all sources.
268
202
  *
203
+ * @example
269
204
  * ```typescript
270
- * // Merging mouse and keyboard events into a single event
271
205
  * const mouseEvent = createEvent<MouseEvent>();
272
206
  * const keyboardEvent = createEvent<KeyboardEvent>();
273
207
  * const inputEvent = merge(mouseEvent, keyboardEvent);
@@ -276,18 +210,14 @@ export type AllEventsResults<T extends Event<any, any>[]> = {
276
210
  */
277
211
  export declare const merge: <Events extends Event<any, any>[]>(...events: Events) => Event<AllEventsParameters<Events>, AllEventsResults<Events>>;
278
212
  /**
279
- * Creates a periodic event that triggers at a specified interval. The event will automatically emit
280
- * an incrementing counter value each time it triggers, starting from zero. This function is useful
281
- * for creating time-based triggers within an application, such as updating UI elements, polling,
282
- * or any other timed operation.
213
+ * Creates a periodic event that emits an incrementing counter (starting from 0) at a fixed interval.
214
+ * Disposing the event clears the interval.
283
215
  *
284
- * @template R - The return type of the event handler function, defaulting to `void`.
285
- * @param interval - The interval in milliseconds at which the event should trigger.
286
- * @returns {Event<number, R>} An `Event` instance that triggers at the specified interval,
287
- * emitting an incrementing counter value.
216
+ * @param interval - The interval in milliseconds.
217
+ * @returns An Event that triggers at the specified interval.
288
218
  *
219
+ * @example
289
220
  * ```typescript
290
- * // Creating an interval event that logs a message every second
291
221
  * const tickEvent = createInterval(1000);
292
222
  * tickEvent.on(tickNumber => console.log('Tick:', tickNumber));
293
223
  * ```
@@ -295,23 +225,18 @@ export declare const merge: <Events extends Event<any, any>[]>(...events: Events
295
225
  export declare const createInterval: <R = unknown>(interval: number) => Event<number, R>;
296
226
  /**
297
227
  * Creates a new Event instance for multi-listener event handling.
298
- * This is the primary way to create events in the library.
299
- *
300
- * @template T - The type of value emitted to listeners (event payload)
301
- * @template R - The return type of listener functions (collected in EventResult)
302
- * @returns {Event<T, R>} A new Event instance ready for listener registration
303
228
  *
229
+ * @example
304
230
  * ```typescript
305
- * // Create an event that accepts a string payload
306
231
  * const messageEvent = createEvent<string>();
307
232
  * messageEvent.on(msg => console.log('Received:', msg));
308
- * messageEvent('Hello'); // All listeners receive 'Hello'
233
+ * messageEvent.emit('Hello'); // All listeners receive 'Hello'
309
234
  *
310
- * // Create an event where listeners return values
235
+ * // Listeners can return values, collected via DispatchResult
311
236
  * const validateEvent = createEvent<string, boolean>();
312
237
  * validateEvent.on(str => str.length > 0);
313
238
  * validateEvent.on(str => str.length < 100);
314
- * const results = await validateEvent('test'); // EventResult with [true, true]
239
+ * const results = await validateEvent.emit('test').all(); // [true, true]
315
240
  * ```
316
241
  */
317
242
  export declare const createEvent: <T = unknown, R = unknown>() => Event<T, R>;
package/build/event.js CHANGED
@@ -1,162 +1,130 @@
1
- import { RingBuffer } from 'fastds';
2
- import { HookType } from "./types.js";
3
- import { Callable, CallableAsyncIterator } from "./callable.js";
4
- import { Sequence } from "./sequence.js";
5
- export class Unsubscribe extends Callable {
6
- _done = false;
7
- constructor(callback){
8
- super(async ()=>{
9
- this._done = true;
10
- await callback();
11
- });
12
- }
13
- get done() {
14
- return this._done;
15
- }
16
- pre(callback) {
17
- return new Unsubscribe(async ()=>{
18
- await callback();
19
- await this();
20
- });
21
- }
22
- post(callback) {
23
- return new Unsubscribe(async ()=>{
24
- await this();
25
- await callback();
26
- });
27
- }
28
- countdown(count) {
29
- return new Unsubscribe(async ()=>{
30
- if (!--count) {
31
- await this();
32
- }
33
- });
1
+ import { Disposer } from "./async.js";
2
+ import { Signal } from "./signal.js";
3
+ import { ListenerRegistry } from "./listener-registry.js";
4
+ import { DispatchResult } from "./dispatch-result.js";
5
+ export class EventIterator {
6
+ #signal;
7
+ constructor(signal){
8
+ this.#signal = signal;
34
9
  }
35
- }
36
- export class EventResult {
37
- #results;
38
- [Symbol.toStringTag] = 'EventResult';
39
- constructor(results){
40
- this.#results = results;
41
- }
42
- then(onfulfilled, onrejected) {
43
- return this.all().then(onfulfilled, onrejected);
44
- }
45
- all() {
46
- return Promise.all(this.#results);
10
+ async next() {
11
+ try {
12
+ const value = await this.#signal.receive();
13
+ return {
14
+ value,
15
+ done: false
16
+ };
17
+ } catch {
18
+ return {
19
+ value: undefined,
20
+ done: true
21
+ };
22
+ }
47
23
  }
48
- settled() {
49
- return Promise.allSettled(this.#results);
24
+ async return() {
25
+ return {
26
+ value: undefined,
27
+ done: true
28
+ };
50
29
  }
51
30
  }
52
- export class Event extends CallableAsyncIterator {
53
- listeners;
54
- hooks = new RingBuffer();
55
- _disposed = false;
56
- dispose;
31
+ export class Event {
32
+ #listeners = new ListenerRegistry();
33
+ #signal = new Signal();
34
+ #disposer;
35
+ #onDispose;
36
+ #sink;
57
37
  [Symbol.toStringTag] = 'Event';
58
- constructor(dispose){
59
- const listeners = new RingBuffer();
60
- super((value)=>{
61
- const results = listeners.toArray().map(async (listener)=>listener(await value));
62
- return new EventResult(results);
63
- });
64
- this.listeners = listeners;
65
- this.dispose = ()=>{
66
- this._disposed = true;
67
- void this.clear();
68
- void dispose?.();
69
- };
38
+ constructor(onDispose){
39
+ this.#onDispose = onDispose;
40
+ this.#disposer = new Disposer(this);
41
+ }
42
+ get sink() {
43
+ return this.#sink ??= this.emit.bind(this);
44
+ }
45
+ handleEvent(event) {
46
+ this.emit(event);
70
47
  }
71
48
  get size() {
72
- return this.listeners.length;
49
+ return this.#listeners.size;
73
50
  }
74
- get disposed() {
75
- return this._disposed;
51
+ emit(value) {
52
+ this.#signal.emit(value);
53
+ return new DispatchResult(this.#listeners.dispatch(value));
76
54
  }
77
55
  lacks(listener) {
78
- return !this.listeners.has(listener);
56
+ return !this.#listeners.has(listener);
79
57
  }
80
58
  has(listener) {
81
- return this.listeners.has(listener);
59
+ return this.#listeners.has(listener);
82
60
  }
83
61
  off(listener) {
84
- if (this.listeners.compact((l)=>l !== listener) && this.hooks.length) {
85
- [
86
- ...this.hooks
87
- ].forEach((hook)=>hook(listener, HookType.Remove));
88
- }
62
+ this.#listeners.off(listener);
89
63
  return this;
90
64
  }
91
65
  on(listener) {
92
- this.listeners.push(listener);
93
- if (this.hooks.length) {
94
- [
95
- ...this.hooks
96
- ].forEach((hook)=>hook(listener, HookType.Add));
97
- }
98
- return new Unsubscribe(()=>{
99
- void this.off(listener);
100
- });
66
+ this.#listeners.on(listener);
67
+ return ()=>void this.off(listener);
101
68
  }
102
69
  once(listener) {
103
- const oneTimeListener = (event)=>{
104
- void this.off(oneTimeListener);
105
- return listener(event);
106
- };
107
- return this.on(oneTimeListener);
70
+ this.#listeners.once(listener);
71
+ return ()=>void this.off(listener);
108
72
  }
109
73
  clear() {
110
- this.listeners.clear();
111
- if (this.hooks.length) {
112
- [
113
- ...this.hooks
114
- ].forEach((hook)=>hook(undefined, HookType.Remove));
115
- }
74
+ this.#listeners.clear();
116
75
  return this;
117
76
  }
118
- async next() {
119
- const { promise, resolve } = Promise.withResolvers();
120
- this.listeners.push(resolve);
121
- return promise.finally(()=>{
122
- this.listeners.removeFirst(resolve);
123
- });
77
+ receive() {
78
+ return this.#signal.receive();
79
+ }
80
+ then(onfulfilled, onrejected) {
81
+ return this.receive().then(onfulfilled, onrejected);
82
+ }
83
+ catch(onrejected) {
84
+ return this.receive().catch(onrejected);
85
+ }
86
+ finally(onfinally) {
87
+ return this.receive().finally(onfinally);
124
88
  }
125
- async settle() {
126
- return await Promise.allSettled([
127
- this.next()
128
- ]).then(([settled])=>settled);
89
+ settle() {
90
+ return this.receive().then((value)=>({
91
+ status: 'fulfilled',
92
+ value
93
+ }), (reason)=>({
94
+ status: 'rejected',
95
+ reason
96
+ }));
129
97
  }
130
98
  [Symbol.asyncIterator]() {
131
- const ctrl = new AbortController();
132
- const sequence = new Sequence(ctrl.signal);
133
- const emitEvent = (value)=>{
134
- sequence(value);
135
- };
136
- this.listeners.push(emitEvent);
137
- const hook = (target = emitEvent, action)=>{
138
- if (target === emitEvent && action === HookType.Remove) {
139
- ctrl.abort('done');
140
- this.listeners.removeFirst(emitEvent);
141
- }
142
- };
143
- this.hooks.push(hook);
144
- return sequence[Symbol.asyncIterator]();
99
+ return new EventIterator(this.#signal);
100
+ }
101
+ dispose() {
102
+ this[Symbol.dispose]();
145
103
  }
146
104
  [Symbol.dispose]() {
147
- void this.dispose();
105
+ if (this.#disposer[Symbol.dispose]()) {
106
+ this.#signal[Symbol.dispose]();
107
+ this.#listeners.clear();
108
+ void this.#onDispose?.();
109
+ }
148
110
  }
149
111
  }
150
112
  export const merge = (...events)=>{
151
- const mergedEvent = new Event();
152
- events.forEach((event)=>event.on(mergedEvent));
113
+ const mergedEvent = new Event(()=>{
114
+ for (const event of events){
115
+ event.off(mergedEvent.sink);
116
+ }
117
+ });
118
+ for (const event of events){
119
+ event.on(mergedEvent.sink);
120
+ }
153
121
  return mergedEvent;
154
122
  };
155
123
  export const createInterval = (interval)=>{
156
124
  let counter = 0;
157
125
  const intervalEvent = new Event(()=>clearInterval(timerId));
158
126
  const timerId = setInterval(()=>{
159
- void intervalEvent(counter++);
127
+ intervalEvent.emit(counter++);
160
128
  }, interval);
161
129
  return intervalEvent;
162
130
  };