@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
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const contextProps = {
|
|
2
|
+
resources: undefined,
|
|
3
|
+
scope: undefined,
|
|
4
|
+
signal: undefined,
|
|
5
|
+
token: undefined,
|
|
6
|
+
};
|
|
7
|
+
export class ContextData {
|
|
8
|
+
#descriptors;
|
|
9
|
+
constructor(descriptors) {
|
|
10
|
+
this.#descriptors = descriptors;
|
|
11
|
+
}
|
|
12
|
+
updateContext(target) {
|
|
13
|
+
if (this.#descriptors) {
|
|
14
|
+
const descriptors = this.#descriptors;
|
|
15
|
+
for (const key of [...Object.keys(descriptors), ...Object.getOwnPropertySymbols(descriptors)]) {
|
|
16
|
+
const descriptor = descriptors[key];
|
|
17
|
+
if (!descriptor || key in contextProps)
|
|
18
|
+
continue;
|
|
19
|
+
Object.defineProperty(target, key, descriptor);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return target;
|
|
23
|
+
}
|
|
24
|
+
builder() {
|
|
25
|
+
return new ContextData.#Builder(Object.assign({}, this.#descriptors));
|
|
26
|
+
}
|
|
27
|
+
static builder() {
|
|
28
|
+
return new ContextData.#Builder(undefined);
|
|
29
|
+
}
|
|
30
|
+
static empty = Object.freeze(new ContextData(undefined));
|
|
31
|
+
static #Builder = class Builder {
|
|
32
|
+
#builderDescriptors;
|
|
33
|
+
constructor(descriptors) {
|
|
34
|
+
this.#builderDescriptors = descriptors;
|
|
35
|
+
}
|
|
36
|
+
values(props) {
|
|
37
|
+
const descriptors = (this.#builderDescriptors ??= {});
|
|
38
|
+
for (const key of [...Object.keys(props), ...Object.getOwnPropertySymbols(props)]) {
|
|
39
|
+
const value = props[key];
|
|
40
|
+
if (value === undefined)
|
|
41
|
+
continue;
|
|
42
|
+
descriptors[key] = { value };
|
|
43
|
+
}
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
getters(getters) {
|
|
47
|
+
const descriptors = (this.#builderDescriptors ??= {});
|
|
48
|
+
for (const key of [...Object.keys(getters), ...Object.getOwnPropertySymbols(getters)]) {
|
|
49
|
+
const value = getters[key];
|
|
50
|
+
if (value === undefined)
|
|
51
|
+
continue;
|
|
52
|
+
descriptors[key] = {
|
|
53
|
+
get() {
|
|
54
|
+
return value(this);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
merge(contextData) {
|
|
61
|
+
Object.assign((this.#builderDescriptors ??= {}), contextData.#descriptors);
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
finish() {
|
|
65
|
+
if (!this.#builderDescriptors)
|
|
66
|
+
return ContextData.empty;
|
|
67
|
+
return new ContextData(this.#builderDescriptors);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=context.js.map
|
package/dist/scope/from.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { Scope, type ToScope } from '../scope.ts';
|
|
2
|
-
export declare function scopeFrom(src: ToScope
|
|
2
|
+
export declare function scopeFrom<V extends object = object>(src: ToScope<V>, onError?: (error: unknown) => void): Scope<V>;
|
|
3
3
|
//# sourceMappingURL=from.d.ts.map
|
package/dist/scope/from.js
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
import { Token } from '../token.js';
|
|
2
2
|
import { Scope, STATIC_SCOPE, StandardScope } from '../scope.js';
|
|
3
|
-
import { isArray } from '../utils.js';
|
|
4
3
|
import { ScopedResources } from '../scopedResource.js';
|
|
4
|
+
import { isIterable } from '@youngspe/common-async-utils';
|
|
5
|
+
import { ContextData } from './context.js';
|
|
6
|
+
import { CancellationTokenBase, ScopeBase } from '@youngspe/async-scope-common';
|
|
7
|
+
function combineContextData(values) {
|
|
8
|
+
const firstResult = values.next();
|
|
9
|
+
if (firstResult.done)
|
|
10
|
+
return undefined;
|
|
11
|
+
const first = firstResult.value;
|
|
12
|
+
let builder;
|
|
13
|
+
for (const item of values) {
|
|
14
|
+
builder ??= first.builder();
|
|
15
|
+
builder.merge(item);
|
|
16
|
+
}
|
|
17
|
+
return builder?.finish() ?? first;
|
|
18
|
+
}
|
|
5
19
|
export function scopeFrom(src, onError) {
|
|
6
20
|
if (!src)
|
|
7
21
|
return STATIC_SCOPE;
|
|
8
|
-
if (src instanceof
|
|
22
|
+
if (src instanceof ScopeBase)
|
|
9
23
|
return src;
|
|
10
|
-
if (src instanceof
|
|
24
|
+
if (src instanceof CancellationTokenBase)
|
|
11
25
|
return new StandardScope({ token: src, onError });
|
|
12
26
|
if (src instanceof AbortSignal) {
|
|
13
27
|
return new StandardScope({ token: Token.fromAbortSignal(src, { onError }), onError });
|
|
@@ -17,12 +31,15 @@ export function scopeFrom(src, onError) {
|
|
|
17
31
|
const tokens = new Set();
|
|
18
32
|
const abortSignals = new Set();
|
|
19
33
|
const resources = new Set();
|
|
20
|
-
|
|
21
|
-
|
|
34
|
+
const contextData = new Set();
|
|
35
|
+
function flatten(src, includeScope) {
|
|
36
|
+
if (!src || typeof src !== 'object')
|
|
22
37
|
return;
|
|
23
|
-
if (src instanceof Scope) {
|
|
38
|
+
if (src instanceof Scope && includeScope) {
|
|
24
39
|
if (src.token.isCancelled)
|
|
25
40
|
return src.token;
|
|
41
|
+
if (src === STATIC_SCOPE)
|
|
42
|
+
return;
|
|
26
43
|
scopes.add(src);
|
|
27
44
|
if (!src.token.isDefused) {
|
|
28
45
|
tokens.add(src.token);
|
|
@@ -30,6 +47,9 @@ export function scopeFrom(src, onError) {
|
|
|
30
47
|
if (!src.resources.isEmpty) {
|
|
31
48
|
resources.add(src.resources);
|
|
32
49
|
}
|
|
50
|
+
if (src.contextData !== ContextData.empty) {
|
|
51
|
+
contextData.add(src.contextData);
|
|
52
|
+
}
|
|
33
53
|
return;
|
|
34
54
|
}
|
|
35
55
|
if (src instanceof Token) {
|
|
@@ -48,17 +68,17 @@ export function scopeFrom(src, onError) {
|
|
|
48
68
|
abortSignals.add(src);
|
|
49
69
|
return;
|
|
50
70
|
}
|
|
51
|
-
if (
|
|
71
|
+
if (isIterable(src)) {
|
|
52
72
|
for (const child of src) {
|
|
53
|
-
const cancelled = flatten(child);
|
|
73
|
+
const cancelled = flatten(child, includeScope);
|
|
54
74
|
if (cancelled)
|
|
55
75
|
return cancelled;
|
|
56
76
|
}
|
|
57
77
|
return;
|
|
58
78
|
}
|
|
59
|
-
return flatten(src.scope) || flatten(src.token) || flatten(src.signal);
|
|
79
|
+
return flatten(src.scope, true) || flatten(src.token, false) || flatten(src.signal, false);
|
|
60
80
|
}
|
|
61
|
-
const cancelled = flatten(src);
|
|
81
|
+
const cancelled = flatten(src, true);
|
|
62
82
|
if (!hasNonScopeToken && scopes.size <= 1) {
|
|
63
83
|
// If only one scope was passed in with no other tokens, we can return the
|
|
64
84
|
// scope directly.
|
|
@@ -67,7 +87,7 @@ export function scopeFrom(src, onError) {
|
|
|
67
87
|
//
|
|
68
88
|
const [scope] = scopes;
|
|
69
89
|
if (!cancelled || scope?.token === cancelled)
|
|
70
|
-
return scope ??
|
|
90
|
+
return (scope ?? STATIC_SCOPE);
|
|
71
91
|
}
|
|
72
92
|
if (cancelled)
|
|
73
93
|
return new StandardScope({ token: cancelled });
|
|
@@ -81,6 +101,7 @@ export function scopeFrom(src, onError) {
|
|
|
81
101
|
return new StandardScope({
|
|
82
102
|
token: Token.combine(tokens),
|
|
83
103
|
resources: ScopedResources.combine(resources),
|
|
104
|
+
contextData: combineContextData(contextData.values()),
|
|
84
105
|
onError,
|
|
85
106
|
});
|
|
86
107
|
}
|
package/dist/scope/standard.d.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { Token } from '../token.ts';
|
|
2
2
|
import { Scope } from '../scope.ts';
|
|
3
3
|
import { ScopedResources } from '../scopedResource.js';
|
|
4
|
-
|
|
4
|
+
import { ContextData } from './context.ts';
|
|
5
|
+
export declare class StandardScope<V extends object> extends Scope<V> {
|
|
5
6
|
#private;
|
|
6
7
|
get token(): Token;
|
|
7
8
|
get resources(): ScopedResources;
|
|
9
|
+
get contextData(): ContextData<V>;
|
|
8
10
|
protected _onError(value: unknown): void;
|
|
9
11
|
constructor(params: {
|
|
10
12
|
token?: Token | undefined;
|
|
11
13
|
resources?: ScopedResources | undefined;
|
|
14
|
+
contextData?: ContextData<V> | undefined;
|
|
12
15
|
onError?: ((e: unknown) => void) | undefined;
|
|
13
16
|
});
|
|
14
17
|
}
|
package/dist/scope/standard.js
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { Token } from '../token.js';
|
|
2
2
|
import { Scope } from '../scope.js';
|
|
3
3
|
import { ScopedResources } from '../scopedResource.js';
|
|
4
|
+
import { ContextData } from './context.js';
|
|
4
5
|
export class StandardScope extends Scope {
|
|
5
6
|
#resources;
|
|
6
7
|
#token;
|
|
7
8
|
#onError;
|
|
9
|
+
#contextData;
|
|
8
10
|
get token() {
|
|
9
11
|
return this.#token;
|
|
10
12
|
}
|
|
11
13
|
get resources() {
|
|
12
14
|
return this.#resources;
|
|
13
15
|
}
|
|
16
|
+
get contextData() {
|
|
17
|
+
return this.#contextData ?? super.contextData;
|
|
18
|
+
}
|
|
14
19
|
_onError(value) {
|
|
15
20
|
return this.#onError ? this.#onError(value) : super._onError(value);
|
|
16
21
|
}
|
|
@@ -18,6 +23,7 @@ export class StandardScope extends Scope {
|
|
|
18
23
|
super();
|
|
19
24
|
this.#token = params.token ?? Token.static;
|
|
20
25
|
this.#resources = (params.token?.isCancelled ? undefined : params.resources) ?? ScopedResources.empty;
|
|
26
|
+
this.#contextData = params.token?.isCancelled ? undefined : params.contextData;
|
|
21
27
|
this.#onError = params.onError;
|
|
22
28
|
}
|
|
23
29
|
}
|
package/dist/scope.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { Scope, STATIC_SCOPE } from './scope/base.ts';
|
|
2
2
|
export { StandardScope } from './scope/standard.ts';
|
|
3
|
-
export type { CommonScopeOptions, ScopeContext, ScopeRunOptions, ToScope } from './scope/base.ts';
|
|
3
|
+
export type { CommonScopeOptions, ScopeContext, ScopeLaunchOptions as ScopeRunOptions, ToScope, } from './scope/base.ts';
|
|
4
4
|
//# sourceMappingURL=scope.d.ts.map
|
package/dist/scopedResource.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { joinPromises } from '
|
|
1
|
+
import { isAsyncDisposable, isDisposable, joinPromises } from '@youngspe/common-async-utils';
|
|
2
2
|
import { Scope } from './scope.js';
|
|
3
|
-
import { isAsyncDisposable, isDisposable } from './utils.js';
|
|
4
3
|
export class ResourceError extends Error {
|
|
5
4
|
static {
|
|
6
5
|
this.prototype.name = this.name;
|
package/dist/timers.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CancellableOptions } from './cancel.ts';
|
|
2
|
+
declare const _timerId: unique symbol;
|
|
3
|
+
interface _TimerId {
|
|
4
|
+
[_timerId]: typeof _timerId;
|
|
5
|
+
}
|
|
6
|
+
type TimerId = typeof globalThis extends {
|
|
7
|
+
setTimeout(...args: any): infer X;
|
|
8
|
+
} ? X : _TimerId;
|
|
9
|
+
type SetTimerFunction = <A extends any[] = []>(action: (...args: A) => void, ms: number, ...args: A) => TimerId;
|
|
10
|
+
type ClearTimerFunction = (id: TimerId | undefined) => void;
|
|
11
|
+
export interface Clock {
|
|
12
|
+
setTimeout: SetTimerFunction;
|
|
13
|
+
setInterval: SetTimerFunction;
|
|
14
|
+
clearTimeout: ClearTimerFunction;
|
|
15
|
+
clearInterval: ClearTimerFunction;
|
|
16
|
+
}
|
|
17
|
+
export declare const GlobalClock: Clock;
|
|
18
|
+
export interface TimerOptions extends CancellableOptions {
|
|
19
|
+
clock?: Clock | undefined;
|
|
20
|
+
}
|
|
21
|
+
export declare function delay(ms: number, options?: TimerOptions): Promise<void>;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=timers.d.ts.map
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { Token } from './token.js';
|
|
2
|
-
const GlobalClock = {
|
|
3
|
-
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
2
|
+
export const GlobalClock = {
|
|
4
3
|
setTimeout: (f, ...args) => globalThis.setTimeout(f, ...args),
|
|
5
|
-
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
6
4
|
setInterval: (f, ...args) => globalThis.setInterval(f, ...args),
|
|
7
5
|
clearTimeout: id => globalThis.clearTimeout(id),
|
|
8
6
|
clearInterval: id => globalThis.clearInterval(id),
|
|
@@ -25,4 +23,4 @@ export function delay(ms, options) {
|
|
|
25
23
|
}, ms);
|
|
26
24
|
});
|
|
27
25
|
}
|
|
28
|
-
//# sourceMappingURL=
|
|
26
|
+
//# sourceMappingURL=timers.js.map
|
package/dist/token/base.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import { CancellationTokenBase } from '@youngspe/async-scope-common';
|
|
1
2
|
import type { CancellableOrDisposable, CancellableLike, CancellationListener, Cancellable } from '../cancel.ts';
|
|
2
3
|
import type { Awaitable, Falsy } from '../types.ts';
|
|
3
4
|
import { Subscription, type SubscriptionLifecycle } from '../events/sub.js';
|
|
4
5
|
import type { ToScope } from '../scope.ts';
|
|
5
|
-
export
|
|
6
|
+
export interface AddCancellableOptions {
|
|
7
|
+
paused?: boolean | undefined;
|
|
8
|
+
passive?: boolean | undefined;
|
|
9
|
+
}
|
|
10
|
+
export type ErrorFilter = (this: void, error: Error) => true | Error | 'defuse' | Falsy;
|
|
11
|
+
export declare abstract class Token extends CancellationTokenBase {
|
|
6
12
|
#private;
|
|
7
13
|
/**
|
|
8
14
|
* The reason for the cancellation if this token has already been cancelled, otherwise `undefined`.
|
|
@@ -13,7 +19,7 @@ export declare abstract class Token {
|
|
|
13
19
|
/** If `true`, this token has been _defused_, meaning it is guaranteed never to be cancelled. */
|
|
14
20
|
get isDefused(): boolean;
|
|
15
21
|
/**
|
|
16
|
-
* If `true`, this token has been _cancelled_, and trigger any additional listeners.
|
|
22
|
+
* If `true`, this token has been _cancelled_, and will not trigger any additional listeners.
|
|
17
23
|
*
|
|
18
24
|
* This is equivalent to `token.error !== undefined`
|
|
19
25
|
*
|
|
@@ -32,26 +38,25 @@ export declare abstract class Token {
|
|
|
32
38
|
* - `undefined` if the listener could not be safely added. This is typically due to the token being
|
|
33
39
|
* cancelled and indicates any future attempts will also fail.
|
|
34
40
|
*/
|
|
35
|
-
protected abstract addOne(listener: CancellableOrDisposable): Subscription | undefined;
|
|
41
|
+
protected abstract addOne(listener: CancellableOrDisposable, options?: AddCancellableOptions): Subscription | undefined;
|
|
36
42
|
/**
|
|
37
43
|
* Adds listeners
|
|
38
44
|
*
|
|
39
|
-
* @param
|
|
40
|
-
*
|
|
41
|
-
* - A {@link
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* - A falsy value, which will be ignored
|
|
46
|
-
* - A (possibly nested) array of any of the above.
|
|
45
|
+
* @param listener - a listener that should be added to this token.
|
|
46
|
+
* This may be:
|
|
47
|
+
* - A {@link Cancellable}
|
|
48
|
+
* - A {@link Disposable}
|
|
49
|
+
* - An {@link AsyncDisposable}
|
|
50
|
+
* - A falsy value, which will be ignored
|
|
47
51
|
* - A function that receives an {@link Error} and optionally returns a promise.
|
|
52
|
+
* - A (possibly nested) array of any of the above.
|
|
48
53
|
*
|
|
49
54
|
* @returns
|
|
50
55
|
* - A {@link Subscription} that may be used to pause, resume, or remove the provide listeners if either the listeners were safely added or the token is defused.
|
|
51
56
|
* - `undefined` if no listener could be safely added. This is typically due to the token being cancelled
|
|
52
57
|
* and indicates any future attempts will also fail.
|
|
53
58
|
*/
|
|
54
|
-
add(
|
|
59
|
+
add(listener: CancellableLike<CancellationListener>, options?: AddCancellableOptions): Subscription | undefined;
|
|
55
60
|
use<X extends CancellableLike>(target: X): X;
|
|
56
61
|
/**
|
|
57
62
|
* Attempts to add a listener to this token.
|
|
@@ -65,6 +70,7 @@ export declare abstract class Token {
|
|
|
65
70
|
*/
|
|
66
71
|
get signal(): AbortSignal;
|
|
67
72
|
throwIfCancelled(): void;
|
|
73
|
+
filter(filter: ErrorFilter): Token;
|
|
68
74
|
static createController(this: void, options?: Token.CreateParams): TokenController;
|
|
69
75
|
static create(this: void, options: Token.CreateParams): Token;
|
|
70
76
|
/** A token that will never be cancelled. */
|
|
@@ -91,10 +97,10 @@ export declare namespace Token {
|
|
|
91
97
|
*/
|
|
92
98
|
onDefuse?: ((this: void) => void) | undefined;
|
|
93
99
|
/**
|
|
94
|
-
* Called on {@link
|
|
100
|
+
* Called on {@link TokenController#cancel}. If a promise-like is returned, no listeners will be
|
|
95
101
|
* notified of the cancellation until after the promise-like resolves.
|
|
96
102
|
*
|
|
97
|
-
* If this callback throws or rejects, the {@link
|
|
103
|
+
* If this callback throws or rejects, the {@link TokenController#cancel} promise will also
|
|
98
104
|
* reject after all cancellation listeners and the {@link onBeforeCancel} promise is settled
|
|
99
105
|
* if present.
|
|
100
106
|
*/
|
|
@@ -102,15 +108,22 @@ export declare namespace Token {
|
|
|
102
108
|
/**
|
|
103
109
|
* Called after all promises returned by cancellation listeners have either resolved or rejected.
|
|
104
110
|
*
|
|
105
|
-
* If this callback exists and returns a promise-like, the {@link
|
|
111
|
+
* If this callback exists and returns a promise-like, the {@link TokenController#cancel} promise
|
|
106
112
|
* will not resolve until after this promise-like is settled.
|
|
107
113
|
*
|
|
108
|
-
* If this callback throws or rejects, the {@link
|
|
114
|
+
* If this callback throws or rejects, the {@link TokenController#cancel} promise will
|
|
109
115
|
* also reject.
|
|
110
116
|
*/
|
|
111
117
|
onAfterCancel?: ((this: void, error: Error) => Awaitable<void>) | undefined;
|
|
112
118
|
}
|
|
113
119
|
interface CreateParams extends SubscriptionLifecycle<[ctrl: TokenController], [ctrl: TokenController]>, Callbacks {
|
|
120
|
+
filter?: ErrorFilter;
|
|
121
|
+
/**
|
|
122
|
+
* If `true`, then when all parent tokens are defused, the token will be defused.
|
|
123
|
+
*
|
|
124
|
+
* @default false
|
|
125
|
+
*/
|
|
126
|
+
sealed?: boolean | undefined;
|
|
114
127
|
}
|
|
115
128
|
interface FromAbortSignalParams extends Callbacks {
|
|
116
129
|
/**
|
package/dist/token/base.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CancellationTokenBase } from '@youngspe/async-scope-common';
|
|
2
|
+
import { CancellationError, toErrorForCancellation } from '../error.js';
|
|
2
3
|
import * as Symbols from '../symbols.js';
|
|
3
|
-
import { isArray, isIterable } from '../utils.js';
|
|
4
4
|
import { Subscription } from '../events/sub.js';
|
|
5
5
|
import { CancelEvent } from '../token.js';
|
|
6
|
-
|
|
6
|
+
import { isArray, isIterable } from '@youngspe/common-async-utils';
|
|
7
|
+
export class Token extends CancellationTokenBase {
|
|
7
8
|
/** If `true`, this token has been _defused_, meaning it is guaranteed never to be cancelled. */
|
|
8
9
|
get isDefused() {
|
|
9
10
|
return false;
|
|
10
11
|
}
|
|
11
12
|
/**
|
|
12
|
-
* If `true`, this token has been _cancelled_, and trigger any additional listeners.
|
|
13
|
+
* If `true`, this token has been _cancelled_, and will not trigger any additional listeners.
|
|
13
14
|
*
|
|
14
15
|
* This is equivalent to `token.error !== undefined`
|
|
15
16
|
*
|
|
@@ -21,42 +22,47 @@ export class Token {
|
|
|
21
22
|
/**
|
|
22
23
|
* Adds listeners
|
|
23
24
|
*
|
|
24
|
-
* @param
|
|
25
|
-
*
|
|
26
|
-
* - A {@link
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* - A falsy value, which will be ignored
|
|
31
|
-
* - A (possibly nested) array of any of the above.
|
|
25
|
+
* @param listener - a listener that should be added to this token.
|
|
26
|
+
* This may be:
|
|
27
|
+
* - A {@link Cancellable}
|
|
28
|
+
* - A {@link Disposable}
|
|
29
|
+
* - An {@link AsyncDisposable}
|
|
30
|
+
* - A falsy value, which will be ignored
|
|
32
31
|
* - A function that receives an {@link Error} and optionally returns a promise.
|
|
32
|
+
* - A (possibly nested) array of any of the above.
|
|
33
33
|
*
|
|
34
34
|
* @returns
|
|
35
35
|
* - A {@link Subscription} that may be used to pause, resume, or remove the provide listeners if either the listeners were safely added or the token is defused.
|
|
36
36
|
* - `undefined` if no listener could be safely added. This is typically due to the token being cancelled
|
|
37
37
|
* and indicates any future attempts will also fail.
|
|
38
38
|
*/
|
|
39
|
-
add(
|
|
39
|
+
add(listener, options) {
|
|
40
40
|
if (this.isCancelled)
|
|
41
41
|
return undefined;
|
|
42
42
|
if (this.isDefused)
|
|
43
43
|
return Subscription.noop;
|
|
44
44
|
const subs = [];
|
|
45
|
-
|
|
45
|
+
const inner = (listener) => {
|
|
46
46
|
if (!listener)
|
|
47
|
-
|
|
47
|
+
return true;
|
|
48
48
|
if (isArray(listener)) {
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
for (const child of listener) {
|
|
50
|
+
if (!inner(child))
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
51
54
|
}
|
|
52
55
|
if (typeof listener === 'function') {
|
|
53
56
|
listener = { cancel: listener };
|
|
54
57
|
}
|
|
55
|
-
const sub = this.addOne(listener);
|
|
58
|
+
const sub = this.addOne(listener, options);
|
|
56
59
|
if (!sub)
|
|
57
|
-
return
|
|
60
|
+
return false;
|
|
58
61
|
subs.push(sub);
|
|
59
|
-
|
|
62
|
+
return true;
|
|
63
|
+
};
|
|
64
|
+
if (!inner(listener))
|
|
65
|
+
return undefined;
|
|
60
66
|
return Subscription.collect(subs);
|
|
61
67
|
}
|
|
62
68
|
use(target) {
|
|
@@ -106,6 +112,11 @@ export class Token {
|
|
|
106
112
|
if (error)
|
|
107
113
|
throw error;
|
|
108
114
|
}
|
|
115
|
+
filter(filter) {
|
|
116
|
+
const ctrl = Token.createController({ filter, sealed: true });
|
|
117
|
+
this.add(ctrl, { passive: true });
|
|
118
|
+
return ctrl.token;
|
|
119
|
+
}
|
|
109
120
|
static createController(options) {
|
|
110
121
|
return CancelEvent.createController(options);
|
|
111
122
|
}
|
|
@@ -118,7 +129,7 @@ export class Token {
|
|
|
118
129
|
}
|
|
119
130
|
/** @returns a token that has already been cancelled. */
|
|
120
131
|
static cancelled(reason = new CancellationError()) {
|
|
121
|
-
return new CancelledToken(
|
|
132
|
+
return new CancelledToken(toErrorForCancellation(reason));
|
|
122
133
|
}
|
|
123
134
|
/**
|
|
124
135
|
*
|
|
@@ -142,8 +153,9 @@ export class Token {
|
|
|
142
153
|
}
|
|
143
154
|
if (tokens.size > 1) {
|
|
144
155
|
return Token.create({
|
|
156
|
+
sealed: true,
|
|
145
157
|
init: ctrl => {
|
|
146
|
-
const sub = Subscription.collect(Array.from(tokens, t => t.add(ctrl)));
|
|
158
|
+
const sub = Subscription.collect(Array.from(tokens, t => t.add(ctrl, { passive: true })));
|
|
147
159
|
return {
|
|
148
160
|
resume: () => {
|
|
149
161
|
sub.resume();
|
|
@@ -162,7 +174,7 @@ export class Token {
|
|
|
162
174
|
/** @returns a {@link Token} that is cancelled when `signal` is aborted. */
|
|
163
175
|
static fromAbortSignal(signal, options) {
|
|
164
176
|
if (signal.aborted)
|
|
165
|
-
return new CancelledToken(
|
|
177
|
+
return new CancelledToken(toErrorForCancellation(signal.reason));
|
|
166
178
|
const { onError, ...callbacks } = options ?? {};
|
|
167
179
|
return Token.create({
|
|
168
180
|
...callbacks,
|
|
@@ -180,6 +192,7 @@ export class Token {
|
|
|
180
192
|
static from(src) {
|
|
181
193
|
const tokens = new Set();
|
|
182
194
|
const signals = new Set();
|
|
195
|
+
const visited = new Set();
|
|
183
196
|
const flatten = (src) => {
|
|
184
197
|
if (!src)
|
|
185
198
|
return undefined;
|
|
@@ -205,6 +218,9 @@ export class Token {
|
|
|
205
218
|
}
|
|
206
219
|
return undefined;
|
|
207
220
|
}
|
|
221
|
+
if (visited.has(src))
|
|
222
|
+
return undefined;
|
|
223
|
+
visited.add(src);
|
|
208
224
|
return flatten(src.scope) || flatten(src.token) || flatten(src.signal);
|
|
209
225
|
};
|
|
210
226
|
const cancelled = flatten(src);
|
package/dist/token.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { Token, STATIC_TOKEN } from './token/base.ts';
|
|
2
2
|
export { CancelEvent } from './events/cancel.ts';
|
|
3
|
-
export type { TokenController } from './token/base.ts';
|
|
3
|
+
export type { TokenController, AddCancellableOptions, ErrorFilter } from './token/base.ts';
|
|
4
4
|
//# sourceMappingURL=token.d.ts.map
|
package/dist/types.d.ts
CHANGED
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
* @template T The type of the value returned by `value` when awaited.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* function asyncMultiply(lhs: Awaitable<number>, rhs: Awaitable<number>) {
|
|
9
|
-
* const [l, r] = await Promise.all([lhs, rhs]);
|
|
10
|
-
* return l * r;
|
|
11
|
-
* }
|
|
12
|
-
*
|
|
13
|
-
*/
|
|
14
|
-
export type Awaitable<T> = T | Promise<T> | PromiseLike<T>;
|
|
1
|
+
import type { OptionalUndefinedProps } from '@youngspe/common-async-utils';
|
|
2
|
+
export type { Awaitable } from '@youngspe/async-scope-common';
|
|
3
|
+
export type { OptionalUndefinedParams, OptionalUndefinedProps } from '@youngspe/common-async-utils';
|
|
15
4
|
/**
|
|
16
5
|
* A value that's treated like `false` in conditional operations like `if` or `&&`
|
|
17
6
|
*
|
|
@@ -43,75 +32,21 @@ export type IfExactOptionalPropertiesEnabled<Then, Else> = {
|
|
|
43
32
|
} extends {
|
|
44
33
|
x?: never;
|
|
45
34
|
} ? Else : Then;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
* @example
|
|
52
|
-
*
|
|
53
|
-
* // This type may or may not contain an error value of type `E`:
|
|
54
|
-
* type State<E> = Ready | Failed<E>;
|
|
55
|
-
* type Ready = { status: 'ready', value: number }
|
|
56
|
-
* // When `E` is `never`, `Failed<E>` is also never thanks to `OrNever<E>`:
|
|
57
|
-
* type Failed<E> = { status: 'failed'; error: E } & OrNever<E>;
|
|
58
|
-
*
|
|
59
|
-
* // This means when `E` is `never`, the type `State<never>` evaluates to
|
|
60
|
-
* // `Ready` to correctly reflect that it can never be in the `Failed` state.
|
|
61
|
-
*
|
|
62
|
-
* // Given a function with this signature:
|
|
63
|
-
* function createState<E>(): State<E> {
|
|
64
|
-
* return { value: 0 }
|
|
65
|
-
* }
|
|
66
|
-
*
|
|
67
|
-
* // The following operation is valid because we have specified there will
|
|
68
|
-
* // never be an error:
|
|
69
|
-
* const { value } = createState<never>();
|
|
70
|
-
*/
|
|
71
|
-
export type OrNever<T, U = unknown> = IfNever<T, never, U>;
|
|
72
|
-
/** Evaluates to `T`, unless `T` is never, in which case it evaluates to `Else`. */
|
|
73
|
-
export type UnlessNeverElse<T, Else> = [T] extends [never] ? Else : T;
|
|
74
|
-
type _OptionalUndefinedParams<A extends any[], R extends any[]> = {
|
|
75
|
-
[K in keyof R]: undefined;
|
|
76
|
-
} extends R ? A extends [...infer L, ...R] ? [
|
|
77
|
-
...L,
|
|
78
|
-
...Partial<R>
|
|
79
|
-
] : A : R extends [any, ...infer Rest] ? _OptionalUndefinedParams<A, Rest> : A;
|
|
80
|
-
export type OptionalUndefinedParams<A extends any[]> = _OptionalUndefinedParams<A, A>;
|
|
81
|
-
type SimplifyObject<T> = {
|
|
82
|
-
[K in keyof T]: T[K];
|
|
35
|
+
export type PartialOrUndefined<T> = IfExactOptionalPropertiesEnabled<{
|
|
36
|
+
[P in keyof T]?: T[P] | undefined;
|
|
37
|
+
}, Partial<T>>;
|
|
38
|
+
type EraseProps<T> = {
|
|
39
|
+
[K in keyof T]: any;
|
|
83
40
|
};
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
* transform: ((value: T) => U) | (T extends U ? undefined : never);
|
|
96
|
-
* }
|
|
97
|
-
*
|
|
98
|
-
* function myMap<T, U = T>({
|
|
99
|
-
* items,
|
|
100
|
-
* transform,
|
|
101
|
-
* }: OptionalUndefinedProps<MyParams<T, U>>): U[] {
|
|
102
|
-
* return transform ? items.map(transform) : items as (T & U)[]
|
|
103
|
-
* }
|
|
104
|
-
*
|
|
105
|
-
* const strings = myMap({
|
|
106
|
-
* items: [1, 2, 3],
|
|
107
|
-
* transform: String,
|
|
108
|
-
* }); // ['1', '2', '3']
|
|
109
|
-
*
|
|
110
|
-
* // `transform` is not required because `T` and `U` are both `number`.
|
|
111
|
-
* const unchanged = myMap({ items: [1, 2, 3] }); // [1, 2, 3]
|
|
112
|
-
*/
|
|
113
|
-
export type OptionalUndefinedProps<T> = SimplifyObject<Partial<T> & {
|
|
114
|
-
[K in keyof T as undefined extends T[K] ? never : K]: T[K];
|
|
115
|
-
}>;
|
|
116
|
-
export {};
|
|
41
|
+
export type Defined<T = unknown> = NonNullable<T> | (T & null);
|
|
42
|
+
export type UpdateObject<T, U> = T extends Partial<U> ? T & U : U extends never ? never : {
|
|
43
|
+
[K in keyof (EraseProps<T> & EraseProps<OptionalUndefinedProps<U>>)]: K extends keyof U ? Extract<PropertyKey, K> extends never ? Defined<U[K]> | (undefined extends U[K] ? T[K & keyof T] : never) : T[K & keyof T] | U[K & keyof T] : T[K & keyof T];
|
|
44
|
+
};
|
|
45
|
+
export type SetProps<T, U> = T extends Partial<U> ? T & U : U extends never ? never : U & {
|
|
46
|
+
[K in keyof T as Exclude<K, keyof (EraseProps<T> | EraseProps<U>)>]: U extends Record<K, any> ? U[K] : U extends Partial<Record<K, infer X>> ? Extract<X, U[K]> | T[K & keyof T] : T[K & keyof T];
|
|
47
|
+
};
|
|
48
|
+
export type BetterOmit<T, K extends PropertyKey> = T extends never ? never : {
|
|
49
|
+
[_K in keyof T as Exclude<_K, K>]: T[_K];
|
|
50
|
+
};
|
|
51
|
+
export type UndefinedIfDefault<T, Def = object> = T | (Def extends T ? undefined : never);
|
|
117
52
|
//# sourceMappingURL=types.d.ts.map
|