@youngspe/async-scope 0.1.0-dev.0 → 0.1.0-dev.1
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/cancel.d.ts +1 -1
- package/dist/cancel.js +1 -2
- package/dist/commonResources.d.ts +3 -0
- package/dist/commonResources.js +3 -0
- package/dist/error.d.ts +2 -1
- package/dist/error.js +26 -4
- package/dist/events/cancel.d.ts +2 -1
- package/dist/events/cancel.js +59 -29
- package/dist/events/generic.d.ts +18 -14
- package/dist/events/generic.js +36 -26
- package/dist/events/sub.d.ts +3 -2
- package/dist/events/sub.js +5 -1
- package/dist/events/utils.d.ts +19 -0
- package/dist/events/utils.js +56 -0
- package/dist/events.d.ts +2 -1
- package/dist/events.js +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -0
- package/dist/lock.d.ts +18 -0
- package/dist/lock.js +200 -0
- package/dist/promise.d.ts +5 -0
- package/dist/promise.js +38 -0
- package/dist/scope/base.d.ts +99 -14
- package/dist/scope/base.js +163 -41
- package/dist/scope/context.d.ts +19 -0
- package/dist/scope/context.js +71 -0
- package/dist/scope/from.d.ts +1 -1
- package/dist/scope/from.js +32 -11
- package/dist/scope/standard.d.ts +4 -1
- package/dist/scope/standard.js +6 -0
- package/dist/scope.d.ts +1 -1
- package/dist/scopedResource.js +1 -2
- package/dist/timers.d.ts +23 -0
- package/dist/{timer.js → timers.js} +2 -4
- package/dist/token/base.d.ts +29 -16
- package/dist/token/base.js +39 -23
- package/dist/token.d.ts +1 -1
- package/dist/types.d.ts +19 -84
- package/package.json +6 -1
- package/dist/join.d.ts +0 -16
- package/dist/join.js +0 -85
- package/dist/timer.d.ts +0 -13
- package/dist/utils.d.ts +0 -51
- package/dist/utils.js +0 -20
package/dist/cancel.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export interface Cancellable {
|
|
|
10
10
|
[Symbols.cancellableAdded]?(key: CancellableParent, sub: Subscription): boolean | void;
|
|
11
11
|
[Symbols.cancellableRemoved]?(key: CancellableParent): void;
|
|
12
12
|
}
|
|
13
|
-
export type CancellableLike = Cancellable | Falsy | Disposable | AsyncDisposable | readonly CancellableLike[];
|
|
13
|
+
export type CancellableLike<Extra = never> = Cancellable | Falsy | Disposable | AsyncDisposable | readonly CancellableLike<Extra>[] | Extra;
|
|
14
14
|
export type CancellationListener = (reason: Error) => Awaitable<void>;
|
|
15
15
|
export declare function cancelObject(target: CancellableOrDisposable, reason: Error): Promise<void>;
|
|
16
16
|
export declare function cancel(target: CancellableLike, reason?: Error): Promise<void>;
|
package/dist/cancel.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { CancellationError } from './error.js';
|
|
2
2
|
import { Subscription } from './events/sub.js';
|
|
3
|
-
import { whenAllSettled } from '
|
|
4
|
-
import { isArray } from './utils.js';
|
|
3
|
+
import { isArray, whenAllSettled } from '@youngspe/common-async-utils';
|
|
5
4
|
export async function cancelObject(target, reason) {
|
|
6
5
|
if (typeof target.cancel === 'function') {
|
|
7
6
|
await target.cancel(reason);
|
package/dist/error.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ type ToError<T> = Error extends T ? Error : T extends Error ? T : Error;
|
|
|
22
22
|
* - Any other value, a {@link CancellationError} with the {@link CancellationError.value|value} property
|
|
23
23
|
* set to `value`.
|
|
24
24
|
*/
|
|
25
|
-
export declare function
|
|
25
|
+
export declare function toErrorForCancellation<T>(value: T): ToError<T>;
|
|
26
26
|
/**
|
|
27
27
|
* Combines an iterator of errors into a single value.
|
|
28
28
|
*
|
|
@@ -33,5 +33,6 @@ export declare function toError<T>(value: T): ToError<T>;
|
|
|
33
33
|
* - otherwise, an {@link AggregateError} of the items in `errors`, after removing `undefined` values and duplicates.
|
|
34
34
|
*/
|
|
35
35
|
export declare function combineErrors(errors: Iterable<unknown>): unknown;
|
|
36
|
+
export declare function unwrapCancellationError(error: unknown): CancellationError | undefined;
|
|
36
37
|
export {};
|
|
37
38
|
//# sourceMappingURL=error.d.ts.map
|
package/dist/error.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isArray, isIterable } from '
|
|
1
|
+
import { isArray, isIterable } from '@youngspe/common-async-utils';
|
|
2
2
|
/**
|
|
3
3
|
* Represents an operation that has been cancelled but not necessarily due to a failure.
|
|
4
4
|
*/
|
|
@@ -13,11 +13,22 @@ export class CancellationError extends Error {
|
|
|
13
13
|
/** An arbitrary value attached to the error */
|
|
14
14
|
value;
|
|
15
15
|
}
|
|
16
|
-
export function
|
|
16
|
+
export function toErrorForCancellation(value) {
|
|
17
17
|
const errors = new Set();
|
|
18
|
+
let isCancellation = true;
|
|
18
19
|
const inner = (value) => {
|
|
19
|
-
if (value instanceof Error)
|
|
20
|
+
if (value instanceof Error) {
|
|
21
|
+
if (value.name === 'AbortError') {
|
|
22
|
+
if (value.cause !== undefined)
|
|
23
|
+
return inner(value.cause);
|
|
24
|
+
return new CancellationError(undefined, { cause: value });
|
|
25
|
+
}
|
|
26
|
+
const cancellationError = unwrapCancellationError(value);
|
|
27
|
+
if (cancellationError)
|
|
28
|
+
return cancellationError;
|
|
29
|
+
isCancellation = false;
|
|
20
30
|
return value;
|
|
31
|
+
}
|
|
21
32
|
if (value === undefined)
|
|
22
33
|
return;
|
|
23
34
|
if (typeof value === 'string')
|
|
@@ -47,7 +58,9 @@ export function toError(value) {
|
|
|
47
58
|
if (error)
|
|
48
59
|
return error;
|
|
49
60
|
if (errors.size > 1)
|
|
50
|
-
return
|
|
61
|
+
return isCancellation ?
|
|
62
|
+
new CancellationError(undefined, { cause: new AggregateError(errors) })
|
|
63
|
+
: new AggregateError(errors);
|
|
51
64
|
[error] = errors;
|
|
52
65
|
return error ?? new CancellationError();
|
|
53
66
|
}
|
|
@@ -71,4 +84,13 @@ export function combineErrors(errors) {
|
|
|
71
84
|
}
|
|
72
85
|
return new AggregateError(errorSet);
|
|
73
86
|
}
|
|
87
|
+
export function unwrapCancellationError(error) {
|
|
88
|
+
while (true) {
|
|
89
|
+
if (error instanceof CancellationError)
|
|
90
|
+
return error;
|
|
91
|
+
if (!(error instanceof Error))
|
|
92
|
+
return undefined;
|
|
93
|
+
error = error.cause;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
74
96
|
//# sourceMappingURL=error.js.map
|
package/dist/events/cancel.d.ts
CHANGED
|
@@ -3,11 +3,12 @@ import { Token, type TokenController } from '../token.ts';
|
|
|
3
3
|
import type { Awaitable } from '../types.ts';
|
|
4
4
|
import { GenericEventEmitter } from './generic.ts';
|
|
5
5
|
import { type Subscription } from './sub.ts';
|
|
6
|
+
import type { AddCancellableOptions } from '../token/base.ts';
|
|
6
7
|
export declare class CancelEvent extends Token {
|
|
7
8
|
#private;
|
|
8
9
|
constructor(inner: GenericEventEmitter<Error, Awaitable<void>>);
|
|
9
10
|
get error(): Error | undefined;
|
|
10
|
-
protected addOne(listener: CancellableOrDisposable): Subscription | undefined;
|
|
11
|
+
protected addOne(listener: CancellableOrDisposable, { paused, passive }?: AddCancellableOptions): Subscription | undefined;
|
|
11
12
|
static createController(this: void, params?: Token.CreateParams): TokenController;
|
|
12
13
|
}
|
|
13
14
|
//# sourceMappingURL=cancel.d.ts.map
|
package/dist/events/cancel.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { joinPromises } from '@youngspe/common-async-utils';
|
|
1
2
|
import { cancelObject } from '../cancel.js';
|
|
2
|
-
import { combineErrors,
|
|
3
|
+
import { CancellationError, combineErrors, toErrorForCancellation, unwrapCancellationError, } from '../error.js';
|
|
3
4
|
import * as Symbols from '../symbols.js';
|
|
4
|
-
import { joinPromises } from '../join.js';
|
|
5
5
|
import { Token } from '../token.js';
|
|
6
6
|
import { GenericEventEmitter } from './generic.js';
|
|
7
7
|
import {} from './sub.js';
|
|
@@ -16,13 +16,15 @@ export class CancelEvent extends Token {
|
|
|
16
16
|
get error() {
|
|
17
17
|
return this.#error;
|
|
18
18
|
}
|
|
19
|
-
addOne(listener) {
|
|
19
|
+
addOne(listener, { paused, passive } = {}) {
|
|
20
20
|
if (this.#error)
|
|
21
21
|
return undefined;
|
|
22
22
|
const sub = this.#inner.add(e => cancelObject(listener, e), {
|
|
23
23
|
once: true,
|
|
24
24
|
onRemove: listener[Symbols.cancellableRemoved]
|
|
25
25
|
&& (() => void listener[Symbols.cancellableRemoved]?.(this.#key)),
|
|
26
|
+
paused,
|
|
27
|
+
passive,
|
|
26
28
|
});
|
|
27
29
|
if (sub.isActive) {
|
|
28
30
|
listener[Symbols.cancellableAdded]?.(this.#key, sub);
|
|
@@ -34,35 +36,60 @@ export class CancelEvent extends Token {
|
|
|
34
36
|
#parentSubs = new Map();
|
|
35
37
|
#promise;
|
|
36
38
|
#callbacks;
|
|
37
|
-
|
|
39
|
+
#filter;
|
|
40
|
+
#sealed;
|
|
41
|
+
constructor(inner, callbacks, sealed, filter) {
|
|
38
42
|
this.#inner = inner;
|
|
39
43
|
this.#callbacks = callbacks;
|
|
40
44
|
this.token = new CancelEvent(inner.emitter);
|
|
45
|
+
this.#sealed = sealed;
|
|
46
|
+
this.#filter = filter;
|
|
41
47
|
}
|
|
42
48
|
token;
|
|
43
|
-
cancel = (reason) =>
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
cancel = (reason) => {
|
|
50
|
+
let error = toErrorForCancellation(reason);
|
|
51
|
+
const filter = this.#filter;
|
|
52
|
+
if (filter) {
|
|
53
|
+
const result = filter(error);
|
|
54
|
+
if (!result)
|
|
55
|
+
return Promise.resolve();
|
|
56
|
+
this.#filter = undefined;
|
|
57
|
+
if (result === 'defuse') {
|
|
58
|
+
this.defuse();
|
|
59
|
+
return Promise.resolve();
|
|
60
|
+
}
|
|
61
|
+
else if (result instanceof Error) {
|
|
62
|
+
error = result;
|
|
63
|
+
}
|
|
51
64
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
return (this.#promise ??= (async () => {
|
|
66
|
+
this.token.#error = error;
|
|
67
|
+
const callbacks = this.#callbacks;
|
|
68
|
+
const { onBeforeCancel, onAfterCancel } = callbacks;
|
|
69
|
+
callbacks.onBeforeCancel = callbacks.onAfterCancel = callbacks.onDefuse = undefined;
|
|
70
|
+
const errors = new Set();
|
|
71
|
+
try {
|
|
72
|
+
await onBeforeCancel?.(error);
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
errors.add(e);
|
|
76
|
+
}
|
|
77
|
+
await joinPromises(this.#inner.getListeners().listeners(), l => l(error), undefined, e => void errors.add(e));
|
|
78
|
+
try {
|
|
79
|
+
await onAfterCancel?.(error);
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
errors.add(e);
|
|
83
|
+
}
|
|
84
|
+
if (errors.size)
|
|
85
|
+
throw toErrorForCancellation(errors);
|
|
86
|
+
})());
|
|
87
|
+
};
|
|
88
|
+
defuse = () => {
|
|
89
|
+
this.#filter = undefined;
|
|
90
|
+
this.#promise ??= Promise.resolve();
|
|
91
|
+
this.#inner.dispose();
|
|
92
|
+
};
|
|
66
93
|
[Symbols.cancellableAdded] = (key, sub) => {
|
|
67
94
|
if (this.token.isDefused || this.token.isCancelled || this.#parentSubs.has(key))
|
|
68
95
|
return false;
|
|
@@ -70,13 +97,16 @@ export class CancelEvent extends Token {
|
|
|
70
97
|
return true;
|
|
71
98
|
};
|
|
72
99
|
[Symbols.cancellableRemoved] = (key) => {
|
|
73
|
-
this.#parentSubs.delete(key);
|
|
100
|
+
const removed = this.#parentSubs.delete(key);
|
|
101
|
+
if (removed && this.#sealed && this.#parentSubs.size === 0) {
|
|
102
|
+
this.defuse();
|
|
103
|
+
}
|
|
74
104
|
};
|
|
75
105
|
[Symbol.asyncDispose] = this.cancel;
|
|
76
|
-
static createInner({ onAfterCancel, onBeforeCancel, onDefuse, init, dispose, }) {
|
|
106
|
+
static createInner({ onAfterCancel, onBeforeCancel, onDefuse, init, dispose, sealed = false, filter, }) {
|
|
77
107
|
const callbacks = { onBeforeCancel, onAfterCancel, onDefuse };
|
|
78
108
|
return GenericEventEmitter.createController({
|
|
79
|
-
context: ctrl => new CancelEvent.#Controller(ctrl, callbacks),
|
|
109
|
+
context: ctrl => new CancelEvent.#Controller(ctrl, callbacks, sealed, filter),
|
|
80
110
|
init: ({ context }) => {
|
|
81
111
|
const parents = context.#parentSubs;
|
|
82
112
|
const _init = init?.(context);
|
package/dist/events/generic.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import type { CancellableOptions } from '../cancel.ts';
|
|
1
2
|
import type { OptionalUndefinedProps } from '../types.ts';
|
|
2
3
|
import { type SubscriptionLifecycle, Subscription } from './sub.ts';
|
|
3
4
|
export interface EventListenerKey {
|
|
4
5
|
}
|
|
5
6
|
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
7
|
export interface ListenerSet<T, Ret> extends Disposable {
|
|
8
8
|
get isEmpty(): boolean;
|
|
9
9
|
listeners(): Generator<(value: T) => Ret, void, void>;
|
|
@@ -12,11 +12,15 @@ export interface ListenerSet<T, Ret> extends Disposable {
|
|
|
12
12
|
callReversed(value: T): Generator<Ret, void, void>;
|
|
13
13
|
popFront(): ((value: T) => Ret) | undefined;
|
|
14
14
|
popBack(): ((value: T) => Ret) | undefined;
|
|
15
|
+
release(): void;
|
|
16
|
+
dispose(): void;
|
|
15
17
|
}
|
|
16
|
-
export interface
|
|
17
|
-
readonly emitter: GenericEventEmitter<T, Ret>;
|
|
18
|
+
export interface EventControllerLike<in T, out Ret = void, Async extends boolean = false> {
|
|
18
19
|
readonly getListeners: (this: void) => MaybePromise<ListenerSet<T, Ret>, Async>;
|
|
19
20
|
readonly dispose: (this: void) => void;
|
|
21
|
+
}
|
|
22
|
+
export interface GenericEventController<T, Ret = void, Async extends boolean = false, Context = void> extends Disposable, EventControllerLike<T, Ret, Async> {
|
|
23
|
+
readonly emitter: GenericEventEmitter<T, Ret>;
|
|
20
24
|
get context(): Context;
|
|
21
25
|
}
|
|
22
26
|
export type GenericEventEmitterParams<T, Ret, Async extends boolean = false, Context = void> = OptionalUndefinedProps<GenericEventEmitterParams.Base<T, Ret, Async, Context>>;
|
|
@@ -31,21 +35,21 @@ type GenericEventLifecycle<T, Ret, Async extends boolean, Context> = Subscriptio
|
|
|
31
35
|
], [
|
|
32
36
|
controller: GenericEventController<T, Ret, Async, Context>
|
|
33
37
|
]>;
|
|
34
|
-
export interface AddListenerOptions {
|
|
35
|
-
signal?: AbortSignal | undefined;
|
|
38
|
+
export interface AddListenerOptions extends CancellableOptions {
|
|
36
39
|
once?: boolean | undefined;
|
|
37
40
|
key?: EventListenerKey | undefined;
|
|
38
41
|
onRemove?: (() => void) | undefined;
|
|
42
|
+
paused?: boolean | undefined;
|
|
43
|
+
/** If `true`, this listener will not keep the event source from pausing. */
|
|
44
|
+
passive?: boolean | undefined;
|
|
39
45
|
}
|
|
40
|
-
export declare class GenericEventEmitter<T, Ret = void> {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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>;
|
|
46
|
+
export declare abstract class GenericEventEmitter<out T, in Ret = void> {
|
|
47
|
+
abstract get isDisposed(): boolean;
|
|
48
|
+
abstract add(listener: (this: void, value: T) => Ret, options?: AddListenerOptions): Subscription;
|
|
49
|
+
abstract has(key: EventListenerKey | undefined | null): boolean;
|
|
50
|
+
abstract remove(key: EventListenerKey | undefined | null): boolean;
|
|
51
|
+
static createController<T, Ret = void, Async extends boolean = false, Context = undefined>(this: void, params: GenericEventEmitterParams<T, Ret, Async, Context>): GenericEventController<T, Ret, Async, Context>;
|
|
52
|
+
static create<T, Ret = void, Async extends boolean = false, Context = undefined>(this: void, params: GenericEventEmitterParams<T, Ret, Async, Context>): GenericEventEmitter<T, Ret>;
|
|
49
53
|
}
|
|
50
54
|
export {};
|
|
51
55
|
//# sourceMappingURL=generic.d.ts.map
|
package/dist/events/generic.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Token } from '../token.js';
|
|
1
2
|
import { SubscriptionLifecycleManager, Subscription } from './sub.js';
|
|
2
3
|
function wrapListener(listener) {
|
|
3
4
|
let _listener = listener;
|
|
@@ -123,8 +124,8 @@ class GenericEventEmitterState {
|
|
|
123
124
|
return 'disposed';
|
|
124
125
|
if (this.handlers.has(handler.key))
|
|
125
126
|
return;
|
|
126
|
-
const { paused } = handler;
|
|
127
|
-
if (!paused && this.#activeCount++ === 0) {
|
|
127
|
+
const { paused, passive } = handler;
|
|
128
|
+
if (!passive && !paused && this.#activeCount++ === 0) {
|
|
128
129
|
const args = this.#getLifecycleArgs();
|
|
129
130
|
if (!args)
|
|
130
131
|
return;
|
|
@@ -140,9 +141,9 @@ class GenericEventEmitterState {
|
|
|
140
141
|
if (!handler)
|
|
141
142
|
return false;
|
|
142
143
|
this.handlers.delete(key);
|
|
143
|
-
const { paused } = handler;
|
|
144
|
+
const { paused, passive } = handler;
|
|
144
145
|
handler.dispose?.();
|
|
145
|
-
if (!paused && --this.#activeCount === 0) {
|
|
146
|
+
if (!passive && !paused && --this.#activeCount === 0) {
|
|
146
147
|
this.#lifecycle.pause();
|
|
147
148
|
}
|
|
148
149
|
return true;
|
|
@@ -151,9 +152,9 @@ class GenericEventEmitterState {
|
|
|
151
152
|
const handler = this.handlers?.get(key);
|
|
152
153
|
if (!handler)
|
|
153
154
|
return;
|
|
154
|
-
const { paused } = handler;
|
|
155
|
+
const { paused, passive } = handler;
|
|
155
156
|
handler.paused = false;
|
|
156
|
-
if (paused && this.#activeCount++ === 0) {
|
|
157
|
+
if (!passive && paused && this.#activeCount++ === 0) {
|
|
157
158
|
const args = this.#getLifecycleArgs();
|
|
158
159
|
if (!args)
|
|
159
160
|
return;
|
|
@@ -164,9 +165,9 @@ class GenericEventEmitterState {
|
|
|
164
165
|
const handler = this.handlers?.get(key);
|
|
165
166
|
if (!handler)
|
|
166
167
|
return;
|
|
167
|
-
const { paused } = handler;
|
|
168
|
+
const { paused, passive } = handler;
|
|
168
169
|
handler.paused = true;
|
|
169
|
-
if (!paused && --this.#activeCount === 0) {
|
|
170
|
+
if (!passive && !paused && --this.#activeCount === 0) {
|
|
170
171
|
this.#lifecycle.pause();
|
|
171
172
|
}
|
|
172
173
|
}
|
|
@@ -186,16 +187,26 @@ class GenericEventEmitterState {
|
|
|
186
187
|
}
|
|
187
188
|
}
|
|
188
189
|
export class GenericEventEmitter {
|
|
190
|
+
static createController(params) {
|
|
191
|
+
return DefaultGenericEventEmitter._createController(params);
|
|
192
|
+
}
|
|
193
|
+
static create(params) {
|
|
194
|
+
return DefaultGenericEventEmitter._create(params);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
class DefaultGenericEventEmitter extends GenericEventEmitter {
|
|
189
198
|
#state;
|
|
190
199
|
constructor(state) {
|
|
200
|
+
super();
|
|
191
201
|
this.#state = state;
|
|
192
202
|
}
|
|
193
203
|
get isDisposed() {
|
|
194
204
|
return !this.#state?.deref()?.handlers;
|
|
195
205
|
}
|
|
196
206
|
add(listener, options) {
|
|
197
|
-
const {
|
|
198
|
-
|
|
207
|
+
const { onRemove, once = false, paused = false, passive = false } = options ?? {};
|
|
208
|
+
const token = Token.from(options);
|
|
209
|
+
if (token?.isCancelled)
|
|
199
210
|
return Subscription.noop;
|
|
200
211
|
let _state = this.#state;
|
|
201
212
|
const state = _state?.deref();
|
|
@@ -205,11 +216,11 @@ export class GenericEventEmitter {
|
|
|
205
216
|
state.removeHandler(options.key);
|
|
206
217
|
}
|
|
207
218
|
const key = options?.key ?? {};
|
|
208
|
-
const handler = { key, listener, once, paused
|
|
219
|
+
const handler = { key, listener, once, paused, passive };
|
|
209
220
|
const sub = Subscription.fromLifecycle({
|
|
210
221
|
init: () => ({
|
|
211
|
-
resume:
|
|
212
|
-
if (!
|
|
222
|
+
resume: initializing => {
|
|
223
|
+
if (!initializing) {
|
|
213
224
|
_state?.deref()?.resumeHandler(key);
|
|
214
225
|
}
|
|
215
226
|
return { pause: () => _state?.deref()?.pauseHandler(key) };
|
|
@@ -221,24 +232,23 @@ export class GenericEventEmitter {
|
|
|
221
232
|
s?.deref()?.removeHandler(key);
|
|
222
233
|
},
|
|
223
234
|
isActive: () => !!_state?.deref()?.handlers?.has(key),
|
|
235
|
+
paused,
|
|
224
236
|
});
|
|
225
|
-
let
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
signal.addEventListener('abort', onAbort, { once: true });
|
|
229
|
-
removeSignal = () => signal.removeEventListener('abort', onAbort);
|
|
237
|
+
let tokenSub = token?.isDefused === false ? token.add(sub) : undefined;
|
|
238
|
+
if (!tokenSub?.isActive) {
|
|
239
|
+
tokenSub = undefined;
|
|
230
240
|
}
|
|
231
241
|
handler.dispose =
|
|
232
|
-
onRemove &&
|
|
242
|
+
onRemove && tokenSub ?
|
|
233
243
|
() => {
|
|
234
244
|
try {
|
|
235
|
-
|
|
245
|
+
tokenSub.dispose();
|
|
236
246
|
}
|
|
237
247
|
finally {
|
|
238
248
|
onRemove();
|
|
239
249
|
}
|
|
240
250
|
}
|
|
241
|
-
: onRemove ||
|
|
251
|
+
: onRemove || tokenSub?.dispose.bind(tokenSub);
|
|
242
252
|
state.addHandler(handler);
|
|
243
253
|
return sub;
|
|
244
254
|
}
|
|
@@ -262,7 +272,7 @@ export class GenericEventEmitter {
|
|
|
262
272
|
this.#isAsync = isAsync ?? false;
|
|
263
273
|
this.#state = new GenericEventEmitterState(lifecycle);
|
|
264
274
|
this.#state.controller = new WeakRef(this);
|
|
265
|
-
this.emitter = new
|
|
275
|
+
this.emitter = new DefaultGenericEventEmitter(new WeakRef(this.#state));
|
|
266
276
|
this.#getContext = context;
|
|
267
277
|
controllerFinalizationRegistry.register(this, this.#state, this.#state);
|
|
268
278
|
}
|
|
@@ -306,11 +316,11 @@ export class GenericEventEmitter {
|
|
|
306
316
|
return this.#context;
|
|
307
317
|
}
|
|
308
318
|
};
|
|
309
|
-
static
|
|
310
|
-
return new
|
|
319
|
+
static _createController(params) {
|
|
320
|
+
return new DefaultGenericEventEmitter.#Controller(params);
|
|
311
321
|
}
|
|
312
|
-
static
|
|
313
|
-
return
|
|
322
|
+
static _create(params) {
|
|
323
|
+
return DefaultGenericEventEmitter.createController(params).emitter;
|
|
314
324
|
}
|
|
315
325
|
}
|
|
316
326
|
//# sourceMappingURL=generic.js.map
|
package/dist/events/sub.d.ts
CHANGED
|
@@ -55,9 +55,10 @@ export declare abstract class Subscription {
|
|
|
55
55
|
resume(): void;
|
|
56
56
|
[Symbol.dispose](): void;
|
|
57
57
|
}>;
|
|
58
|
-
static fromLifecycle(this: void, lifecycle: (SubscriptionLifecycle<[], [
|
|
58
|
+
static fromLifecycle(this: void, lifecycle: (SubscriptionLifecycle<[], [initializing: boolean]> & {
|
|
59
59
|
isActive?: ((this: void) => boolean) | undefined;
|
|
60
|
-
|
|
60
|
+
paused?: boolean | undefined;
|
|
61
|
+
}) | (() => SubscriptionLifecycle.Initialized<[initializing: boolean]>) | undefined): Subscription;
|
|
61
62
|
static collect(this: void, subscriptions: Iterable<Subscription | Falsy>): Subscription;
|
|
62
63
|
}
|
|
63
64
|
export {};
|
package/dist/events/sub.js
CHANGED
|
@@ -141,14 +141,18 @@ class LifecycleSubscription extends Subscription {
|
|
|
141
141
|
#isActive;
|
|
142
142
|
constructor(lifecycle) {
|
|
143
143
|
super();
|
|
144
|
+
let paused = false;
|
|
144
145
|
if (typeof lifecycle !== 'function') {
|
|
145
146
|
this.#isActive = lifecycle?.isActive;
|
|
147
|
+
paused = lifecycle?.paused ?? false;
|
|
146
148
|
}
|
|
147
149
|
this.#lifecycle =
|
|
148
150
|
lifecycle
|
|
149
151
|
&& new SubscriptionLifecycleManager(typeof lifecycle === 'function' ? { init: lifecycle } : lifecycle);
|
|
150
152
|
this.#initialized = this.#lifecycle?.init();
|
|
151
|
-
|
|
153
|
+
if (!paused) {
|
|
154
|
+
this.#initialized?.resume(true);
|
|
155
|
+
}
|
|
152
156
|
}
|
|
153
157
|
resume() {
|
|
154
158
|
this.#initialized?.resume(false);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CancellableOptions } from '../cancel.ts';
|
|
2
|
+
import { GenericEventEmitter, type AddListenerOptions } from '../events.ts';
|
|
3
|
+
import { type ScopeContext } from '../scope.ts';
|
|
4
|
+
import type { Awaitable, OptionalUndefinedParams, UndefinedIfDefault } from '../types.ts';
|
|
5
|
+
type OnNextEventListenerReturn<U, Ret> = UndefinedIfDefault<OptionalUndefinedParams<[Awaitable<U>, Ret]>, [
|
|
6
|
+
]>;
|
|
7
|
+
type OnNextEventListenerReturnAsync<U, Ret> = PromiseLike<UndefinedIfDefault<OptionalUndefinedParams<[Awaitable<U>, Awaitable<Ret>]>>>;
|
|
8
|
+
interface NextEventOptions<UCancel> extends CancellableOptions, Pick<AddListenerOptions, 'passive'> {
|
|
9
|
+
onDispose?: (() => Awaitable<UCancel>) | undefined;
|
|
10
|
+
}
|
|
11
|
+
export declare function onNextEvent<T, Ret, U, UCancel = never>(emitter: GenericEventEmitter<T, Promise<Ret>>, listener: (this: void, cx: ScopeContext<{
|
|
12
|
+
value: T;
|
|
13
|
+
}>) => OnNextEventListenerReturnAsync<U, Ret>, options?: NextEventOptions<UCancel>): Promise<U | UCancel>;
|
|
14
|
+
export declare function onNextEvent<T, Ret, U, UCancel = never>(emitter: GenericEventEmitter<T, Ret>, listener: (this: void, cx: ScopeContext<{
|
|
15
|
+
value: T;
|
|
16
|
+
}>) => OnNextEventListenerReturn<U, Ret>, options?: NextEventOptions<UCancel>): Promise<U | UCancel>;
|
|
17
|
+
export declare function nextEvent<T, UCancel = never>(emitter: GenericEventEmitter<T, undefined>, options?: NextEventOptions<UCancel>): Promise<T | UCancel>;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { isPromiseLike } from '@youngspe/common-async-utils';
|
|
2
|
+
import { GenericEventEmitter } from '../events.js';
|
|
3
|
+
import { Scope } from '../scope.js';
|
|
4
|
+
import { Token } from '../token.js';
|
|
5
|
+
export async function onNextEvent(emitter, listener, options) {
|
|
6
|
+
const scope = Scope.from(options);
|
|
7
|
+
scope.throwIfClosed();
|
|
8
|
+
let onDispose = options?.onDispose;
|
|
9
|
+
using _sub = onDispose
|
|
10
|
+
&& scope.token.add(() => {
|
|
11
|
+
onDispose = undefined;
|
|
12
|
+
});
|
|
13
|
+
return await scope.resolveOrCancel(new Promise((resolve, reject) => {
|
|
14
|
+
emitter.add(value => {
|
|
15
|
+
try {
|
|
16
|
+
_sub?.dispose();
|
|
17
|
+
onDispose = undefined;
|
|
18
|
+
const result = listener(scope.withContextValues({ value }).getContext());
|
|
19
|
+
if (isPromiseLike(result))
|
|
20
|
+
return Promise.resolve(result.then(args => {
|
|
21
|
+
resolve(args?.[0]);
|
|
22
|
+
return args?.[1];
|
|
23
|
+
})).catch(error => {
|
|
24
|
+
reject(error);
|
|
25
|
+
throw error;
|
|
26
|
+
});
|
|
27
|
+
resolve(result?.[0]);
|
|
28
|
+
return result?.[1];
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
reject(error);
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}, {
|
|
35
|
+
once: true,
|
|
36
|
+
scope,
|
|
37
|
+
passive: options?.passive,
|
|
38
|
+
onRemove: (_sub || onDispose)
|
|
39
|
+
&& (() => {
|
|
40
|
+
_sub?.dispose();
|
|
41
|
+
if (onDispose) {
|
|
42
|
+
try {
|
|
43
|
+
resolve(onDispose());
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
reject(error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}),
|
|
50
|
+
});
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
export function nextEvent(emitter, options) {
|
|
54
|
+
return onNextEvent(emitter, ({ value }) => [value], options);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=utils.js.map
|
package/dist/events.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { GenericEventEmitter, type GenericEventEmitterParams, type GenericEventController, type AddListenerOptions, type EventListenerKey, type ListenerSet, type MaybePromise, } from './events/generic.ts';
|
|
1
|
+
export { GenericEventEmitter, type EventControllerLike, type GenericEventEmitterParams, type GenericEventController, type AddListenerOptions, type EventListenerKey, type ListenerSet, type MaybePromise, } from './events/generic.ts';
|
|
2
2
|
export { Subscription, type SubscriptionLifecycle, SubscriptionLifecycleManager } from './events/sub.ts';
|
|
3
|
+
export { onNextEvent } from './events/utils.ts';
|
|
3
4
|
//# sourceMappingURL=events.d.ts.map
|
package/dist/events.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
export { Token, type TokenController } from './token.ts';
|
|
1
|
+
export { Token, type TokenController, type AddCancellableOptions, type ErrorFilter } from './token.ts';
|
|
2
2
|
export { Scope, type CommonScopeOptions, type ScopeContext, type ScopeRunOptions, type ToScope, } from './scope.ts';
|
|
3
3
|
export { cancel, type Cancellable, type CancellableLike, type CancellableOptions, type CancellableParent, type CancellationListener, } from './cancel.ts';
|
|
4
|
+
export { CancellationError, toErrorForCancellation, unwrapCancellationError } from './error.ts';
|
|
4
5
|
export * as Symbols from './symbols.ts';
|
|
5
6
|
export * from './events.ts';
|
|
6
7
|
export { ResourceError, ResourceKey, type ResourceReadKey, type ResourceWriteKey, ScopedResources, } from './scopedResource.ts';
|
|
8
|
+
export { Lock, type Guard, type SharedGuard } from './lock.ts';
|
|
9
|
+
export { delay, type Clock, GlobalClock, type TimerOptions } from './timers.ts';
|
|
7
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
export { Token } from './token.js';
|
|
2
2
|
export { Scope, } from './scope.js';
|
|
3
3
|
export { cancel, } from './cancel.js';
|
|
4
|
+
export { CancellationError, toErrorForCancellation, unwrapCancellationError } from './error.js';
|
|
4
5
|
export * as Symbols from './symbols.js';
|
|
5
6
|
export * from './events.js';
|
|
6
7
|
export { ResourceError, ResourceKey, ScopedResources, } from './scopedResource.js';
|
|
8
|
+
export { Lock } from './lock.js';
|
|
9
|
+
export { delay, GlobalClock } from './timers.js';
|
|
7
10
|
//# sourceMappingURL=index.js.map
|
package/dist/lock.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CancellableOptions } from './cancel.ts';
|
|
2
|
+
import type { OptionalUndefinedParams } from './types.ts';
|
|
3
|
+
export interface SharedGuard<out T> extends Disposable {
|
|
4
|
+
get value(): T;
|
|
5
|
+
release(): void;
|
|
6
|
+
}
|
|
7
|
+
export interface Guard<in out T> extends SharedGuard<T> {
|
|
8
|
+
set value(value: T);
|
|
9
|
+
}
|
|
10
|
+
export declare class Lock<T = void> {
|
|
11
|
+
#private;
|
|
12
|
+
constructor(...[value]: OptionalUndefinedParams<[value: T]>);
|
|
13
|
+
acquireShared(options?: CancellableOptions): Promise<SharedGuard<T>>;
|
|
14
|
+
acquire(options?: CancellableOptions): Promise<Guard<T>>;
|
|
15
|
+
tryAcquireShared(options?: CancellableOptions): SharedGuard<T> | undefined;
|
|
16
|
+
tryAcquire(options?: CancellableOptions): Guard<T> | undefined;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=lock.d.ts.map
|