@youngspe/async-scope 0.1.0-dev.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.
@@ -0,0 +1,24 @@
1
+ import { Subscription } from './events/sub.js';
2
+ import type { ToScope } from './scope.ts';
3
+ import type * as Symbols from './symbols.ts';
4
+ import type { Awaitable, Falsy } from './types.ts';
5
+ export interface CancellableParent {
6
+ }
7
+ export interface Cancellable {
8
+ cancel(reason: Error): Awaitable<void>;
9
+ /** Return false to prevent the parent from adding this cancellable. */
10
+ [Symbols.cancellableAdded]?(key: CancellableParent, sub: Subscription): boolean | void;
11
+ [Symbols.cancellableRemoved]?(key: CancellableParent): void;
12
+ }
13
+ export type CancellableLike = Cancellable | Falsy | Disposable | AsyncDisposable | readonly CancellableLike[];
14
+ export type CancellationListener = (reason: Error) => Awaitable<void>;
15
+ export declare function cancelObject(target: CancellableOrDisposable, reason: Error): Promise<void>;
16
+ export declare function cancel(target: CancellableLike, reason?: Error): Promise<void>;
17
+ export interface CancellableOrDisposable extends Partial<Cancellable>, Partial<Disposable>, Partial<AsyncDisposable> {
18
+ }
19
+ export interface CancellableOptions {
20
+ scope?: ToScope;
21
+ token?: ToScope;
22
+ signal?: AbortSignal | undefined;
23
+ }
24
+ //# sourceMappingURL=cancel.d.ts.map
package/dist/cancel.js ADDED
@@ -0,0 +1,27 @@
1
+ import { CancellationError } from './error.js';
2
+ import { Subscription } from './events/sub.js';
3
+ import { whenAllSettled } from './join.js';
4
+ import { isArray } from './utils.js';
5
+ export async function cancelObject(target, reason) {
6
+ if (typeof target.cancel === 'function') {
7
+ await target.cancel(reason);
8
+ return;
9
+ }
10
+ if (typeof target[Symbol.asyncDispose] === 'function') {
11
+ await target[Symbol.asyncDispose]();
12
+ return;
13
+ }
14
+ if (typeof target[Symbol.dispose] === 'function') {
15
+ target[Symbol.dispose]();
16
+ return;
17
+ }
18
+ }
19
+ export function cancel(target, reason = new CancellationError()) {
20
+ if (!target)
21
+ return Promise.resolve();
22
+ if (isArray(target)) {
23
+ return whenAllSettled(target, t => cancel(t, reason));
24
+ }
25
+ return cancelObject(target, reason);
26
+ }
27
+ //# sourceMappingURL=cancel.js.map
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Represents an operation that has been cancelled but not necessarily due to a failure.
3
+ */
4
+ export declare class CancellationError extends Error {
5
+ static [Symbol.hasInstance](value: unknown): boolean;
6
+ /** An arbitrary value attached to the error */
7
+ value?: unknown;
8
+ }
9
+ type ToError<T> = Error extends T ? Error : T extends Error ? T : Error;
10
+ /**
11
+ * Converts `value` to an {@link Error}.
12
+ * This is intended for use during cancellation, so the fallback type is {@link CancellationError}.
13
+ *
14
+ * @param value - The value to convert.
15
+ * @returns An Error instance. When `value` is:
16
+ * - An {@link Error} instance, `value` itself.
17
+ * - `undefined` or an empty {@link Iterable}, a plain {@link CancellationError}
18
+ * - A `string`, a {@link CancellationError} with `value` as the message.
19
+ * - An {@link Iterable} yielding exactly one item, the output of this function applied to the item
20
+ * - Any other {@link Iterable}, an {@link AggregateError} containing the items with this function
21
+ * recursively applied to them.
22
+ * - Any other value, a {@link CancellationError} with the {@link CancellationError.value|value} property
23
+ * set to `value`.
24
+ */
25
+ export declare function toError<T>(value: T): ToError<T>;
26
+ /**
27
+ * Combines an iterator of errors into a single value.
28
+ *
29
+ * @param errors
30
+ * @returns
31
+ * - if `errors` contains zero non-`undefined` items, `undefined`.
32
+ * - if `errors` contains exactly one distinct non-undefined value, that value.
33
+ * - otherwise, an {@link AggregateError} of the items in `errors`, after removing `undefined` values and duplicates.
34
+ */
35
+ export declare function combineErrors(errors: Iterable<unknown>): unknown;
36
+ export {};
37
+ //# sourceMappingURL=error.d.ts.map
package/dist/error.js ADDED
@@ -0,0 +1,74 @@
1
+ import { isArray, isIterable } from './utils.js';
2
+ /**
3
+ * Represents an operation that has been cancelled but not necessarily due to a failure.
4
+ */
5
+ export class CancellationError extends Error {
6
+ static {
7
+ this.prototype.name = 'CancellationError';
8
+ }
9
+ static [Symbol.hasInstance](value) {
10
+ return ((value instanceof Error && value.name === this.prototype.name)
11
+ || Function.prototype[Symbol.hasInstance].call(this, value));
12
+ }
13
+ /** An arbitrary value attached to the error */
14
+ value;
15
+ }
16
+ export function toError(value) {
17
+ const errors = new Set();
18
+ const inner = (value) => {
19
+ if (value instanceof Error)
20
+ return value;
21
+ if (value === undefined)
22
+ return;
23
+ if (typeof value === 'string')
24
+ return new CancellationError(value);
25
+ if (isIterable(value)) {
26
+ for (const item of value) {
27
+ const ret = inner(item);
28
+ if (ret) {
29
+ errors.add(ret);
30
+ }
31
+ }
32
+ return;
33
+ }
34
+ let msg;
35
+ if (typeof value === 'function') {
36
+ msg = value.name;
37
+ }
38
+ else if (value === null
39
+ || typeof value !== 'object'
40
+ || (typeof value.toString === 'function' && value.toString !== Object.prototype.toString)) {
41
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
42
+ msg = String(value);
43
+ }
44
+ return Object.assign(new CancellationError(msg), { value });
45
+ };
46
+ let error = inner(value);
47
+ if (error)
48
+ return error;
49
+ if (errors.size > 1)
50
+ return new AggregateError(errors);
51
+ [error] = errors;
52
+ return error ?? new CancellationError();
53
+ }
54
+ /**
55
+ * Combines an iterator of errors into a single value.
56
+ *
57
+ * @param errors
58
+ * @returns
59
+ * - if `errors` contains zero non-`undefined` items, `undefined`.
60
+ * - if `errors` contains exactly one distinct non-undefined value, that value.
61
+ * - otherwise, an {@link AggregateError} of the items in `errors`, after removing `undefined` values and duplicates.
62
+ */
63
+ export function combineErrors(errors) {
64
+ if (isArray(errors) && errors.length <= 1)
65
+ return errors[0];
66
+ const errorSet = errors instanceof Set ? errors : new Set(errors);
67
+ errorSet.delete(undefined);
68
+ if (errorSet.size <= 1) {
69
+ const [error] = errorSet.values();
70
+ return error;
71
+ }
72
+ return new AggregateError(errorSet);
73
+ }
74
+ //# sourceMappingURL=error.js.map
@@ -0,0 +1,13 @@
1
+ import { type CancellableOrDisposable } from '../cancel.ts';
2
+ import { Token, type TokenController } from '../token.ts';
3
+ import type { Awaitable } from '../types.ts';
4
+ import { GenericEventEmitter } from './generic.ts';
5
+ import { type Subscription } from './sub.ts';
6
+ export declare class CancelEvent extends Token {
7
+ #private;
8
+ constructor(inner: GenericEventEmitter<Error, Awaitable<void>>);
9
+ get error(): Error | undefined;
10
+ protected addOne(listener: CancellableOrDisposable): Subscription | undefined;
11
+ static createController(this: void, params?: Token.CreateParams): TokenController;
12
+ }
13
+ //# sourceMappingURL=cancel.d.ts.map
@@ -0,0 +1,123 @@
1
+ import { cancelObject } from '../cancel.js';
2
+ import { combineErrors, toError } from '../error.js';
3
+ import * as Symbols from '../symbols.js';
4
+ import { joinPromises } from '../join.js';
5
+ import { Token } from '../token.js';
6
+ import { GenericEventEmitter } from './generic.js';
7
+ import {} from './sub.js';
8
+ export class CancelEvent extends Token {
9
+ #inner;
10
+ #error;
11
+ #key = {};
12
+ constructor(inner) {
13
+ super();
14
+ this.#inner = inner;
15
+ }
16
+ get error() {
17
+ return this.#error;
18
+ }
19
+ addOne(listener) {
20
+ if (this.#error)
21
+ return undefined;
22
+ const sub = this.#inner.add(e => cancelObject(listener, e), {
23
+ once: true,
24
+ onRemove: listener[Symbols.cancellableRemoved]
25
+ && (() => void listener[Symbols.cancellableRemoved]?.(this.#key)),
26
+ });
27
+ if (sub.isActive) {
28
+ listener[Symbols.cancellableAdded]?.(this.#key, sub);
29
+ }
30
+ return sub;
31
+ }
32
+ static #Controller = class Controller {
33
+ #inner;
34
+ #parentSubs = new Map();
35
+ #promise;
36
+ #callbacks;
37
+ constructor(inner, callbacks) {
38
+ this.#inner = inner;
39
+ this.#callbacks = callbacks;
40
+ this.token = new CancelEvent(inner.emitter);
41
+ }
42
+ token;
43
+ cancel = (reason) => (this.#promise ??= (async error => {
44
+ this.token.#error = error;
45
+ const callbacks = this.#callbacks;
46
+ const { onBeforeCancel, onAfterCancel } = callbacks;
47
+ callbacks.onBeforeCancel = callbacks.onAfterCancel = callbacks.onDefuse = undefined;
48
+ const errors = new Set();
49
+ try {
50
+ await onBeforeCancel?.(error);
51
+ }
52
+ catch (e) {
53
+ errors.add(e);
54
+ }
55
+ await joinPromises(this.#inner.getListeners().listeners(), l => l(error), undefined, e => errors.add(e));
56
+ try {
57
+ await onAfterCancel?.(error);
58
+ }
59
+ catch (e) {
60
+ errors.add(e);
61
+ }
62
+ if (errors.size)
63
+ throw combineErrors(errors);
64
+ })(toError(reason)));
65
+ defuse = () => this.#inner.dispose();
66
+ [Symbols.cancellableAdded] = (key, sub) => {
67
+ if (this.token.isDefused || this.token.isCancelled || this.#parentSubs.has(key))
68
+ return false;
69
+ this.#parentSubs.set(key, sub);
70
+ return true;
71
+ };
72
+ [Symbols.cancellableRemoved] = (key) => {
73
+ this.#parentSubs.delete(key);
74
+ };
75
+ [Symbol.asyncDispose] = this.cancel;
76
+ static createInner({ onAfterCancel, onBeforeCancel, onDefuse, init, dispose, }) {
77
+ const callbacks = { onBeforeCancel, onAfterCancel, onDefuse };
78
+ return GenericEventEmitter.createController({
79
+ context: ctrl => new CancelEvent.#Controller(ctrl, callbacks),
80
+ init: ({ context }) => {
81
+ const parents = context.#parentSubs;
82
+ const _init = init?.(context);
83
+ return {
84
+ resume: ({ context }) => {
85
+ const parents = context.#parentSubs;
86
+ for (const sub of parents.values()) {
87
+ sub.resume();
88
+ }
89
+ const _resume = _init?.resume?.(context);
90
+ return {
91
+ pause: () => {
92
+ try {
93
+ _resume?.pause?.();
94
+ }
95
+ finally {
96
+ parents.forEach(sub => sub.pause());
97
+ }
98
+ },
99
+ };
100
+ },
101
+ close: () => {
102
+ parents.forEach(sub => sub.dispose());
103
+ parents.clear();
104
+ try {
105
+ _init?.close?.();
106
+ }
107
+ finally {
108
+ const { onDefuse } = callbacks;
109
+ callbacks.onBeforeCancel = callbacks.onAfterCancel = callbacks.onDefuse = undefined;
110
+ onDefuse?.();
111
+ }
112
+ },
113
+ };
114
+ },
115
+ dispose,
116
+ });
117
+ }
118
+ };
119
+ static createController(params = {}) {
120
+ return CancelEvent.#Controller.createInner(params).context;
121
+ }
122
+ }
123
+ //# sourceMappingURL=cancel.js.map
@@ -0,0 +1,51 @@
1
+ import type { OptionalUndefinedProps } from '../types.ts';
2
+ import { type SubscriptionLifecycle, Subscription } from './sub.ts';
3
+ export interface EventListenerKey {
4
+ }
5
+ export type MaybePromise<T, Async extends boolean> = T | (Async extends true ? Promise<T> : never);
6
+ export type Awaitable<T> = T | Promise<T> | PromiseLike<T>;
7
+ export interface ListenerSet<T, Ret> extends Disposable {
8
+ get isEmpty(): boolean;
9
+ listeners(): Generator<(value: T) => Ret, void, void>;
10
+ listenersReversed(): Generator<(value: T) => Ret, void, void>;
11
+ call(value: T): Generator<Ret, void, void>;
12
+ callReversed(value: T): Generator<Ret, void, void>;
13
+ popFront(): ((value: T) => Ret) | undefined;
14
+ popBack(): ((value: T) => Ret) | undefined;
15
+ }
16
+ export interface GenericEventController<T, Ret = void, Async extends boolean = false, Context = void> extends Disposable {
17
+ readonly emitter: GenericEventEmitter<T, Ret>;
18
+ readonly getListeners: (this: void) => MaybePromise<ListenerSet<T, Ret>, Async>;
19
+ readonly dispose: (this: void) => void;
20
+ get context(): Context;
21
+ }
22
+ export type GenericEventEmitterParams<T, Ret, Async extends boolean = false, Context = void> = OptionalUndefinedProps<GenericEventEmitterParams.Base<T, Ret, Async, Context>>;
23
+ export declare namespace GenericEventEmitterParams {
24
+ interface Base<T, Ret, Async extends boolean, Context> extends GenericEventLifecycle<T, Ret, Async, Context> {
25
+ context: ((ctrl: GenericEventController<T, Ret, Async>) => Context) | (undefined extends Context ? undefined : never);
26
+ isAsync: Async | (false extends Async ? undefined : never);
27
+ }
28
+ }
29
+ type GenericEventLifecycle<T, Ret, Async extends boolean, Context> = SubscriptionLifecycle<[
30
+ controller: GenericEventController<T, Ret, Async, Context>
31
+ ], [
32
+ controller: GenericEventController<T, Ret, Async, Context>
33
+ ]>;
34
+ export interface AddListenerOptions {
35
+ signal?: AbortSignal | undefined;
36
+ once?: boolean | undefined;
37
+ key?: EventListenerKey | undefined;
38
+ onRemove?: (() => void) | undefined;
39
+ }
40
+ export declare class GenericEventEmitter<T, Ret = void> {
41
+ #private;
42
+ private constructor();
43
+ get isDisposed(): boolean;
44
+ add(listener: (this: void, value: T) => Ret, options?: AddListenerOptions): Subscription;
45
+ has(key: EventListenerKey | undefined | null): boolean;
46
+ remove(key: EventListenerKey | undefined | null): boolean;
47
+ static createController<T, Ret = void, Async extends boolean = false, Context = void>(this: void, params: GenericEventEmitterParams<T, Ret, Async, Context>): GenericEventController<T, Ret, Async, Context>;
48
+ static create<T, Ret = void, Async extends boolean = false>(this: void, params: GenericEventEmitterParams<T, Ret, Async>): GenericEventEmitter<T, Ret>;
49
+ }
50
+ export {};
51
+ //# sourceMappingURL=generic.d.ts.map
@@ -0,0 +1,316 @@
1
+ import { SubscriptionLifecycleManager, Subscription } from './sub.js';
2
+ function wrapListener(listener) {
3
+ let _listener = listener;
4
+ let out;
5
+ return value => {
6
+ const l = _listener;
7
+ _listener = undefined;
8
+ if (l) {
9
+ out = l(value);
10
+ }
11
+ return out;
12
+ };
13
+ }
14
+ class ListenerSetImpl {
15
+ #handlers;
16
+ #onRemove;
17
+ #onDispose;
18
+ #released = false;
19
+ constructor(handlers, onRemove, onDispose) {
20
+ this.#handlers = handlers;
21
+ this.#onRemove = onRemove;
22
+ this.#onDispose = onDispose;
23
+ }
24
+ static create(handlers, { onRemove, onDispose }) {
25
+ return new ListenerSetImpl(handlers?.filter(h => !h.paused), onRemove, onDispose);
26
+ }
27
+ static EMPTY = new this(undefined, undefined, undefined);
28
+ get isEmpty() {
29
+ return !this.#handlers?.length;
30
+ }
31
+ #removeIfNeeded(handler) {
32
+ if (handler.once) {
33
+ const { dispose } = handler;
34
+ handler.listener = handler.dispose = undefined;
35
+ this.#onRemove?.call(undefined, handler);
36
+ dispose?.();
37
+ }
38
+ }
39
+ #cleanupHandlersIfNeeded() {
40
+ if (this.#handlers?.length)
41
+ return;
42
+ this.#handlers = this.#onRemove = undefined;
43
+ }
44
+ *listeners() {
45
+ let listener;
46
+ while ((listener = this.popFront())) {
47
+ yield listener;
48
+ }
49
+ }
50
+ *listenersReversed() {
51
+ let listener;
52
+ while ((listener = this.popBack())) {
53
+ yield listener;
54
+ }
55
+ }
56
+ *call(value) {
57
+ let listener;
58
+ while ((listener = this.popFront())) {
59
+ yield listener(value);
60
+ }
61
+ }
62
+ *callReversed(value) {
63
+ let listener;
64
+ while ((listener = this.popBack())) {
65
+ yield listener(value);
66
+ }
67
+ }
68
+ release() {
69
+ if (this.#released)
70
+ return;
71
+ this.#released = true;
72
+ const onDispose = this.#onDispose;
73
+ this.#onDispose = undefined;
74
+ onDispose?.();
75
+ }
76
+ dispose() {
77
+ this.#handlers = this.#onRemove = undefined;
78
+ this.release();
79
+ }
80
+ [Symbol.dispose]() {
81
+ return this.dispose();
82
+ }
83
+ popFront() {
84
+ let handler;
85
+ while ((handler = this.#handlers?.shift())) {
86
+ const { listener, paused } = handler;
87
+ if (paused || !listener)
88
+ continue;
89
+ this.#removeIfNeeded(handler);
90
+ return wrapListener(listener);
91
+ }
92
+ this.#cleanupHandlersIfNeeded();
93
+ }
94
+ popBack() {
95
+ let handler;
96
+ while ((handler = this.#handlers?.pop())) {
97
+ const { listener, paused } = handler;
98
+ if (paused || !listener)
99
+ continue;
100
+ this.#removeIfNeeded(handler);
101
+ return listener;
102
+ }
103
+ this.#cleanupHandlersIfNeeded();
104
+ }
105
+ }
106
+ const controllerFinalizationRegistry = new FinalizationRegistry((state) => state.dispose());
107
+ class GenericEventEmitterState {
108
+ handlers = new Map();
109
+ #activeCount = 0;
110
+ #lifecycle;
111
+ controller;
112
+ constructor(lifecycle) {
113
+ this.#lifecycle = new SubscriptionLifecycleManager(lifecycle);
114
+ }
115
+ #getLifecycleArgs() {
116
+ const controller = this.controller?.deref();
117
+ if (!controller)
118
+ return undefined;
119
+ return [controller];
120
+ }
121
+ addHandler(handler) {
122
+ if (!this.handlers)
123
+ return 'disposed';
124
+ if (this.handlers.has(handler.key))
125
+ return;
126
+ const { paused } = handler;
127
+ if (!paused && this.#activeCount++ === 0) {
128
+ const args = this.#getLifecycleArgs();
129
+ if (!args)
130
+ return;
131
+ this.#lifecycle.init(...args).resume(...args);
132
+ }
133
+ this.handlers.set(handler.key, handler);
134
+ return;
135
+ }
136
+ removeHandler(key) {
137
+ if (!this.handlers)
138
+ return false;
139
+ const handler = this.handlers.get(key);
140
+ if (!handler)
141
+ return false;
142
+ this.handlers.delete(key);
143
+ const { paused } = handler;
144
+ handler.dispose?.();
145
+ if (!paused && --this.#activeCount === 0) {
146
+ this.#lifecycle.pause();
147
+ }
148
+ return true;
149
+ }
150
+ resumeHandler(key) {
151
+ const handler = this.handlers?.get(key);
152
+ if (!handler)
153
+ return;
154
+ const { paused } = handler;
155
+ handler.paused = false;
156
+ if (paused && this.#activeCount++ === 0) {
157
+ const args = this.#getLifecycleArgs();
158
+ if (!args)
159
+ return;
160
+ this.#lifecycle.init(...args).resume(...args);
161
+ }
162
+ }
163
+ pauseHandler(key) {
164
+ const handler = this.handlers?.get(key);
165
+ if (!handler)
166
+ return;
167
+ const { paused } = handler;
168
+ handler.paused = true;
169
+ if (!paused && --this.#activeCount === 0) {
170
+ this.#lifecycle.pause();
171
+ }
172
+ }
173
+ dispose() {
174
+ controllerFinalizationRegistry.unregister(this);
175
+ const handlers = this.handlers;
176
+ this.handlers = undefined;
177
+ if (!handlers)
178
+ return;
179
+ for (const handler of handlers.values()) {
180
+ const { dispose } = handler;
181
+ handler.dispose = handler.listener = undefined;
182
+ dispose?.();
183
+ }
184
+ handlers.clear();
185
+ this.#lifecycle.dispose();
186
+ }
187
+ }
188
+ export class GenericEventEmitter {
189
+ #state;
190
+ constructor(state) {
191
+ this.#state = state;
192
+ }
193
+ get isDisposed() {
194
+ return !this.#state?.deref()?.handlers;
195
+ }
196
+ add(listener, options) {
197
+ const { signal, onRemove, once = false } = options ?? {};
198
+ if (signal?.aborted)
199
+ return Subscription.noop;
200
+ let _state = this.#state;
201
+ const state = _state?.deref();
202
+ if (!state)
203
+ return Subscription.noop;
204
+ if (options?.key != null) {
205
+ state.removeHandler(options.key);
206
+ }
207
+ const key = options?.key ?? {};
208
+ const handler = { key, listener, once, paused: false };
209
+ const sub = Subscription.fromLifecycle({
210
+ init: () => ({
211
+ resume: first => {
212
+ if (!first) {
213
+ _state?.deref()?.resumeHandler(key);
214
+ }
215
+ return { pause: () => _state?.deref()?.pauseHandler(key) };
216
+ },
217
+ }),
218
+ dispose: () => {
219
+ const s = _state;
220
+ _state = undefined;
221
+ s?.deref()?.removeHandler(key);
222
+ },
223
+ isActive: () => !!_state?.deref()?.handlers?.has(key),
224
+ });
225
+ let removeSignal = undefined;
226
+ if (signal) {
227
+ const onAbort = () => sub.dispose();
228
+ signal.addEventListener('abort', onAbort, { once: true });
229
+ removeSignal = () => signal.removeEventListener('abort', onAbort);
230
+ }
231
+ handler.dispose =
232
+ onRemove && removeSignal ?
233
+ () => {
234
+ try {
235
+ removeSignal();
236
+ }
237
+ finally {
238
+ onRemove();
239
+ }
240
+ }
241
+ : onRemove || removeSignal;
242
+ state.addHandler(handler);
243
+ return sub;
244
+ }
245
+ has(key) {
246
+ return key != null && !!this.#state?.deref()?.handlers?.has(key);
247
+ }
248
+ remove(key) {
249
+ if (key == null)
250
+ return false;
251
+ return this.#state?.deref()?.removeHandler(key) ?? false;
252
+ }
253
+ static #Controller = class Controller {
254
+ #state;
255
+ #inUse = false;
256
+ #notifyQueue = [];
257
+ #isAsync;
258
+ #context;
259
+ #getContext;
260
+ constructor(params) {
261
+ const { context, isAsync, ...lifecycle } = params;
262
+ this.#isAsync = isAsync ?? false;
263
+ this.#state = new GenericEventEmitterState(lifecycle);
264
+ this.#state.controller = new WeakRef(this);
265
+ this.emitter = new GenericEventEmitter(new WeakRef(this.#state));
266
+ this.#getContext = context;
267
+ controllerFinalizationRegistry.register(this, this.#state, this.#state);
268
+ }
269
+ emitter;
270
+ getListeners = () => {
271
+ let onDispose;
272
+ const inner = () => {
273
+ this.#inUse = this.#isAsync;
274
+ const handlers = this.#state.handlers && Array.from(this.#state.handlers.values());
275
+ if (!handlers && !onDispose)
276
+ return ListenerSetImpl.EMPTY;
277
+ return ListenerSetImpl.create(handlers, {
278
+ onRemove: handlers && (({ key }) => void this.#state.removeHandler(key)),
279
+ onDispose,
280
+ });
281
+ };
282
+ if (this.#isAsync) {
283
+ onDispose = () => {
284
+ const next = this.#notifyQueue.shift();
285
+ if (next) {
286
+ next();
287
+ }
288
+ else {
289
+ this.#inUse = false;
290
+ }
291
+ };
292
+ if (this.#inUse) {
293
+ return new Promise(resolve => void this.#notifyQueue.push(resolve)).then(inner);
294
+ }
295
+ }
296
+ return inner();
297
+ };
298
+ dispose = () => this.#state.dispose();
299
+ [Symbol.dispose] = this.dispose;
300
+ get context() {
301
+ const cx = this.#getContext;
302
+ if (cx) {
303
+ this.#getContext = undefined;
304
+ this.#context = cx(this);
305
+ }
306
+ return this.#context;
307
+ }
308
+ };
309
+ static createController(params) {
310
+ return new GenericEventEmitter.#Controller(params);
311
+ }
312
+ static create(params) {
313
+ return GenericEventEmitter.createController(params).emitter;
314
+ }
315
+ }
316
+ //# sourceMappingURL=generic.js.map