@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.
- package/dist/cancel.d.ts +24 -0
- package/dist/cancel.js +27 -0
- package/dist/error.d.ts +37 -0
- package/dist/error.js +74 -0
- package/dist/events/cancel.d.ts +13 -0
- package/dist/events/cancel.js +123 -0
- package/dist/events/generic.d.ts +51 -0
- package/dist/events/generic.js +316 -0
- package/dist/events/sub.d.ts +64 -0
- package/dist/events/sub.js +207 -0
- package/dist/events.d.ts +3 -0
- package/dist/events.js +3 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/join.d.ts +16 -0
- package/dist/join.js +85 -0
- package/dist/scope/base.d.ts +44 -0
- package/dist/scope/base.js +125 -0
- package/dist/scope/from.d.ts +3 -0
- package/dist/scope/from.js +87 -0
- package/dist/scope/standard.d.ts +15 -0
- package/dist/scope/standard.js +24 -0
- package/dist/scope.d.ts +4 -0
- package/dist/scope.js +3 -0
- package/dist/scopedResource.d.ts +59 -0
- package/dist/scopedResource.js +242 -0
- package/dist/symbols.d.ts +3 -0
- package/dist/symbols.js +3 -0
- package/dist/timer.d.ts +13 -0
- package/dist/timer.js +28 -0
- package/dist/token/base.d.ts +135 -0
- package/dist/token/base.js +252 -0
- package/dist/token.d.ts +4 -0
- package/dist/token.js +3 -0
- package/dist/types.d.ts +117 -0
- package/dist/types.js +2 -0
- package/dist/utils.d.ts +51 -0
- package/dist/utils.js +20 -0
- package/package.json +21 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Falsy } from '../types.ts';
|
|
2
|
+
export interface SubscriptionLifecycle<Init extends any[] = [], Resume extends any[] = []> {
|
|
3
|
+
init?: ((this: void, ...args: Init) => SubscriptionLifecycle.Initialized<Resume> | void) | undefined;
|
|
4
|
+
dispose?: ((this: void) => void) | undefined;
|
|
5
|
+
}
|
|
6
|
+
export declare namespace SubscriptionLifecycle {
|
|
7
|
+
interface Initialized<Resume extends any[] = []> {
|
|
8
|
+
resume?: ((this: void, ...args: Resume) => Resumed | void) | undefined;
|
|
9
|
+
close?: ((this: void) => void) | undefined;
|
|
10
|
+
}
|
|
11
|
+
interface Resumed {
|
|
12
|
+
pause?: ((this: void) => void) | undefined;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export declare class SubscriptionLifecycleManager<Init extends any[] = [], Resume extends any[] = []> implements SubscriptionLifecycle<Init, Resume> {
|
|
16
|
+
#private;
|
|
17
|
+
constructor(lifecycle?: SubscriptionLifecycle<Init, Resume>);
|
|
18
|
+
init: (...args: Init) => SubscriptionLifecycleManagerInit<Resume>;
|
|
19
|
+
pause: () => void;
|
|
20
|
+
close: () => void;
|
|
21
|
+
dispose: () => void;
|
|
22
|
+
[Symbol.dispose]: () => void;
|
|
23
|
+
}
|
|
24
|
+
declare class SubscriptionLifecycleManagerInit<Resume extends any[] = []> implements SubscriptionLifecycle.Initialized {
|
|
25
|
+
#private;
|
|
26
|
+
constructor(init?: SubscriptionLifecycle.Initialized<Resume> | void, onClose?: (this: void) => void);
|
|
27
|
+
resume: (...args: Resume) => SubscriptionLifecycleManagerResume;
|
|
28
|
+
pause: () => void;
|
|
29
|
+
close: () => void;
|
|
30
|
+
[Symbol.dispose]: () => void;
|
|
31
|
+
}
|
|
32
|
+
declare class SubscriptionLifecycleManagerResume implements SubscriptionLifecycle.Resumed {
|
|
33
|
+
#private;
|
|
34
|
+
constructor(resumed?: SubscriptionLifecycle.Resumed | void, onPause?: (this: void) => void);
|
|
35
|
+
pause: () => void;
|
|
36
|
+
[Symbol.dispose]: () => void;
|
|
37
|
+
}
|
|
38
|
+
export declare abstract class Subscription {
|
|
39
|
+
get isActive(): boolean;
|
|
40
|
+
dispose(): void;
|
|
41
|
+
pause(): void;
|
|
42
|
+
resume(): void;
|
|
43
|
+
[Symbol.dispose](): void;
|
|
44
|
+
static readonly noop: Readonly<{
|
|
45
|
+
get isActive(): boolean;
|
|
46
|
+
dispose(): void;
|
|
47
|
+
pause(): void;
|
|
48
|
+
resume(): void;
|
|
49
|
+
[Symbol.dispose](): void;
|
|
50
|
+
}>;
|
|
51
|
+
static fromDispose(this: void, dispose: (() => void) | undefined): Readonly<{
|
|
52
|
+
get isActive(): boolean;
|
|
53
|
+
dispose(): void;
|
|
54
|
+
pause(): void;
|
|
55
|
+
resume(): void;
|
|
56
|
+
[Symbol.dispose](): void;
|
|
57
|
+
}>;
|
|
58
|
+
static fromLifecycle(this: void, lifecycle: (SubscriptionLifecycle<[], [first: boolean]> & {
|
|
59
|
+
isActive?: ((this: void) => boolean) | undefined;
|
|
60
|
+
}) | (() => SubscriptionLifecycle.Initialized<[first: boolean]>) | undefined): Subscription;
|
|
61
|
+
static collect(this: void, subscriptions: Iterable<Subscription | Falsy>): Subscription;
|
|
62
|
+
}
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=sub.d.ts.map
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
export class SubscriptionLifecycleManager {
|
|
2
|
+
#lifecycle;
|
|
3
|
+
#init;
|
|
4
|
+
constructor(lifecycle) {
|
|
5
|
+
this.#lifecycle = lifecycle || {};
|
|
6
|
+
}
|
|
7
|
+
init = (...args) => {
|
|
8
|
+
return (this.#init ??= new SubscriptionLifecycleManagerInit(this.#lifecycle?.init?.(...args), () => (this.#init = undefined)));
|
|
9
|
+
};
|
|
10
|
+
pause = () => {
|
|
11
|
+
this.#init?.pause();
|
|
12
|
+
};
|
|
13
|
+
close = () => {
|
|
14
|
+
try {
|
|
15
|
+
this.pause();
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
const init = this.#init;
|
|
19
|
+
this.#init = undefined;
|
|
20
|
+
init?.close?.();
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
dispose = () => {
|
|
24
|
+
try {
|
|
25
|
+
this.close();
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
const lifecycle = this.#lifecycle;
|
|
29
|
+
this.#lifecycle = undefined;
|
|
30
|
+
lifecycle?.dispose?.();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
[Symbol.dispose] = this.dispose;
|
|
34
|
+
}
|
|
35
|
+
class SubscriptionLifecycleManagerInit {
|
|
36
|
+
#init;
|
|
37
|
+
#resumed;
|
|
38
|
+
#onClose;
|
|
39
|
+
constructor(init, onClose) {
|
|
40
|
+
this.#init = init || {};
|
|
41
|
+
this.#onClose = onClose;
|
|
42
|
+
}
|
|
43
|
+
resume = (...args) => {
|
|
44
|
+
return (this.#resumed ??= new SubscriptionLifecycleManagerResume(this.#init?.resume?.(...args), () => (this.#resumed = undefined)));
|
|
45
|
+
};
|
|
46
|
+
pause = () => {
|
|
47
|
+
this.#resumed?.pause();
|
|
48
|
+
};
|
|
49
|
+
close = () => {
|
|
50
|
+
const onClose = this.#onClose;
|
|
51
|
+
this.#onClose = undefined;
|
|
52
|
+
try {
|
|
53
|
+
try {
|
|
54
|
+
this.pause();
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
const init = this.#init;
|
|
58
|
+
this.#init = undefined;
|
|
59
|
+
init?.close?.();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
onClose?.();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
[Symbol.dispose] = this.close;
|
|
67
|
+
}
|
|
68
|
+
class SubscriptionLifecycleManagerResume {
|
|
69
|
+
#resumed;
|
|
70
|
+
#onPause;
|
|
71
|
+
constructor(resumed, onPause) {
|
|
72
|
+
this.#resumed = resumed || {};
|
|
73
|
+
this.#onPause = onPause;
|
|
74
|
+
}
|
|
75
|
+
pause = () => {
|
|
76
|
+
const resumed = this.#resumed;
|
|
77
|
+
const onPause = this.#onPause;
|
|
78
|
+
this.#resumed = this.#onPause = undefined;
|
|
79
|
+
try {
|
|
80
|
+
resumed?.pause?.();
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
onPause?.();
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
[Symbol.dispose] = this.pause;
|
|
87
|
+
}
|
|
88
|
+
export class Subscription {
|
|
89
|
+
get isActive() {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
dispose() { }
|
|
93
|
+
pause() { }
|
|
94
|
+
resume() { }
|
|
95
|
+
[Symbol.dispose]() {
|
|
96
|
+
this.dispose();
|
|
97
|
+
}
|
|
98
|
+
static noop = Object.freeze(new (class NoopSubscription extends this {
|
|
99
|
+
})());
|
|
100
|
+
static fromDispose(dispose) {
|
|
101
|
+
if (!dispose)
|
|
102
|
+
return Subscription.noop;
|
|
103
|
+
return new DisposeSubscription(dispose);
|
|
104
|
+
}
|
|
105
|
+
static fromLifecycle(lifecycle) {
|
|
106
|
+
if (!lifecycle)
|
|
107
|
+
return Subscription.noop;
|
|
108
|
+
return new LifecycleSubscription(lifecycle);
|
|
109
|
+
}
|
|
110
|
+
static collect(subscriptions) {
|
|
111
|
+
const set = new Set();
|
|
112
|
+
for (const sub of subscriptions) {
|
|
113
|
+
if (sub && sub.isActive) {
|
|
114
|
+
set.add(sub);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (set.size > 1)
|
|
118
|
+
return new SubscriptionSet(set);
|
|
119
|
+
const [sub] = set;
|
|
120
|
+
return sub ?? Subscription.noop;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
class DisposeSubscription extends Subscription {
|
|
124
|
+
#dispose;
|
|
125
|
+
constructor(dispose) {
|
|
126
|
+
super();
|
|
127
|
+
this.#dispose = dispose;
|
|
128
|
+
}
|
|
129
|
+
get isActive() {
|
|
130
|
+
return !!this.#dispose;
|
|
131
|
+
}
|
|
132
|
+
dispose() {
|
|
133
|
+
const dispose = this.#dispose;
|
|
134
|
+
this.#dispose = undefined;
|
|
135
|
+
dispose?.();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
class LifecycleSubscription extends Subscription {
|
|
139
|
+
#lifecycle;
|
|
140
|
+
#initialized;
|
|
141
|
+
#isActive;
|
|
142
|
+
constructor(lifecycle) {
|
|
143
|
+
super();
|
|
144
|
+
if (typeof lifecycle !== 'function') {
|
|
145
|
+
this.#isActive = lifecycle?.isActive;
|
|
146
|
+
}
|
|
147
|
+
this.#lifecycle =
|
|
148
|
+
lifecycle
|
|
149
|
+
&& new SubscriptionLifecycleManager(typeof lifecycle === 'function' ? { init: lifecycle } : lifecycle);
|
|
150
|
+
this.#initialized = this.#lifecycle?.init();
|
|
151
|
+
this.#initialized?.resume(true);
|
|
152
|
+
}
|
|
153
|
+
resume() {
|
|
154
|
+
this.#initialized?.resume(false);
|
|
155
|
+
}
|
|
156
|
+
pause() {
|
|
157
|
+
this.#lifecycle?.pause();
|
|
158
|
+
}
|
|
159
|
+
dispose() {
|
|
160
|
+
const lifecycle = this.#lifecycle;
|
|
161
|
+
this.#lifecycle = this.#initialized = this.#isActive = undefined;
|
|
162
|
+
lifecycle?.dispose();
|
|
163
|
+
}
|
|
164
|
+
get isActive() {
|
|
165
|
+
return !!this.#lifecycle && this.#isActive?.() !== false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
class SubscriptionSet extends Subscription {
|
|
169
|
+
#items;
|
|
170
|
+
constructor(items) {
|
|
171
|
+
super();
|
|
172
|
+
this.#items = items;
|
|
173
|
+
}
|
|
174
|
+
#forEach(action) {
|
|
175
|
+
const errors = new Set();
|
|
176
|
+
for (const item of this.#items) {
|
|
177
|
+
if (!item.isActive) {
|
|
178
|
+
this.#items.delete(item);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
action?.(item);
|
|
183
|
+
}
|
|
184
|
+
catch (e) {
|
|
185
|
+
errors.add(e);
|
|
186
|
+
}
|
|
187
|
+
if (errors.size > 1)
|
|
188
|
+
throw new AggregateError(errors);
|
|
189
|
+
if (errors.size === 1)
|
|
190
|
+
throw errors.values().next().value;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
pause() {
|
|
194
|
+
this.#forEach(item => item.pause());
|
|
195
|
+
}
|
|
196
|
+
resume() {
|
|
197
|
+
this.#forEach(item => item.resume());
|
|
198
|
+
}
|
|
199
|
+
dispose() {
|
|
200
|
+
this.#forEach(item => item.dispose());
|
|
201
|
+
}
|
|
202
|
+
get isActive() {
|
|
203
|
+
this.#forEach();
|
|
204
|
+
return !!this.#items.size;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=sub.js.map
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { GenericEventEmitter, type GenericEventEmitterParams, type GenericEventController, type AddListenerOptions, type EventListenerKey, type ListenerSet, type MaybePromise, } from './events/generic.ts';
|
|
2
|
+
export { Subscription, type SubscriptionLifecycle, SubscriptionLifecycleManager } from './events/sub.ts';
|
|
3
|
+
//# sourceMappingURL=events.d.ts.map
|
package/dist/events.js
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { Token, type TokenController } from './token.ts';
|
|
2
|
+
export { Scope, type CommonScopeOptions, type ScopeContext, type ScopeRunOptions, type ToScope, } from './scope.ts';
|
|
3
|
+
export { cancel, type Cancellable, type CancellableLike, type CancellableOptions, type CancellableParent, type CancellationListener, } from './cancel.ts';
|
|
4
|
+
export * as Symbols from './symbols.ts';
|
|
5
|
+
export * from './events.ts';
|
|
6
|
+
export { ResourceError, ResourceKey, type ResourceReadKey, type ResourceWriteKey, ScopedResources, } from './scopedResource.ts';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { Token } from './token.js';
|
|
2
|
+
export { Scope, } from './scope.js';
|
|
3
|
+
export { cancel, } from './cancel.js';
|
|
4
|
+
export * as Symbols from './symbols.js';
|
|
5
|
+
export * from './events.js';
|
|
6
|
+
export { ResourceError, ResourceKey, ScopedResources, } from './scopedResource.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
package/dist/join.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Awaitable, Falsy, OptionalUndefinedParams } from './types.ts';
|
|
2
|
+
type Operation<T, U> = ((value: T, index: number) => Awaitable<U>) | ([T] extends [U] ? null | undefined : never);
|
|
3
|
+
export declare function joinPromises<T, U = T, Out = never, Out2 = never>(src: Iterable<Awaitable<T>> | AsyncIterable<T>, ...[operation, onResolve, onReject]: OptionalUndefinedParams<[
|
|
4
|
+
operation: Operation<T, U>,
|
|
5
|
+
onResolve: ((value: U, index: number) => Awaitable<Out | Falsy>) | null | undefined,
|
|
6
|
+
onReject: ((reason: unknown, index: number) => Awaitable<Out2 | Falsy>) | null | undefined
|
|
7
|
+
]>): Promise<NonNullable<Out | Out2> | undefined>;
|
|
8
|
+
/**
|
|
9
|
+
* Like `Promise.allSettled()`, this waits until all promises have settled. Like `Promise.all()`, this rejects if any promise rejects.
|
|
10
|
+
*/
|
|
11
|
+
export declare function whenAllSettled<T, U = T, Out = never>(src: Iterable<Awaitable<T>> | AsyncIterable<T>, ...[operation, onResolve]: OptionalUndefinedParams<[
|
|
12
|
+
operation: Operation<T, U>,
|
|
13
|
+
onResolve: ((value: U, index: number) => Awaitable<Out | Falsy>) | null | undefined
|
|
14
|
+
]>): Promise<Out | undefined>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=join.d.ts.map
|
package/dist/join.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { isIterable, isPromiseLike } from './utils.js';
|
|
2
|
+
export async function joinPromises(src, ...[operation, onResolve, onReject]) {
|
|
3
|
+
return new Promise((_resolve, _reject) => {
|
|
4
|
+
let done = false;
|
|
5
|
+
let count = 1;
|
|
6
|
+
const resolve = (value) => {
|
|
7
|
+
if (value || --count === 0) {
|
|
8
|
+
done = true;
|
|
9
|
+
_resolve(value || undefined);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
const reject = (e) => ((done = true), _reject(e));
|
|
13
|
+
const processItem = async (_value, index) => {
|
|
14
|
+
try {
|
|
15
|
+
if (done)
|
|
16
|
+
return;
|
|
17
|
+
let awaited;
|
|
18
|
+
try {
|
|
19
|
+
const value = isPromiseLike(_value) ? await _value : _value;
|
|
20
|
+
if (operation) {
|
|
21
|
+
if (done)
|
|
22
|
+
return;
|
|
23
|
+
const ret = operation(value, index);
|
|
24
|
+
awaited = isPromiseLike(ret) ? await ret : ret;
|
|
25
|
+
if (done)
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
awaited = value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
if (!onReject)
|
|
34
|
+
return reject(e);
|
|
35
|
+
if (done)
|
|
36
|
+
return;
|
|
37
|
+
return resolve(await onReject(e, index));
|
|
38
|
+
}
|
|
39
|
+
return resolve(await onResolve?.(awaited, index));
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
return reject(e);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
void (async () => {
|
|
46
|
+
try {
|
|
47
|
+
let i = 0;
|
|
48
|
+
if (isIterable(src)) {
|
|
49
|
+
for (const item of src) {
|
|
50
|
+
++count;
|
|
51
|
+
void processItem(item, i++);
|
|
52
|
+
if (done)
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
for await (const item of src) {
|
|
58
|
+
++count;
|
|
59
|
+
void processItem(item, i++);
|
|
60
|
+
if (done)
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return resolve(undefined);
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
return reject(e);
|
|
68
|
+
}
|
|
69
|
+
})();
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Like `Promise.allSettled()`, this waits until all promises have settled. Like `Promise.all()`, this rejects if any promise rejects.
|
|
74
|
+
*/
|
|
75
|
+
export async function whenAllSettled(src, ...[operation, onResolve]) {
|
|
76
|
+
const errors = new Set();
|
|
77
|
+
const out = await joinPromises(src, operation, onResolve, e => void errors.add(e));
|
|
78
|
+
if (out !== undefined)
|
|
79
|
+
return out;
|
|
80
|
+
if (errors.size > 1)
|
|
81
|
+
throw new AggregateError(errors);
|
|
82
|
+
if (errors.size === 1)
|
|
83
|
+
throw errors.values().next().value;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=join.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Awaitable, Falsy } from '../types.ts';
|
|
2
|
+
import { Token } from '../token.ts';
|
|
3
|
+
import { type CancellableOptions } from '../cancel.ts';
|
|
4
|
+
import type { CancellableLike } from '../cancel.ts';
|
|
5
|
+
import { ScopedResources } from '../scopedResource.ts';
|
|
6
|
+
export interface CommonScopeOptions extends CancellableOptions {
|
|
7
|
+
}
|
|
8
|
+
export interface ScopeRunOptions extends CommonScopeOptions {
|
|
9
|
+
}
|
|
10
|
+
export interface ScopeContextBase {
|
|
11
|
+
readonly scope: Scope;
|
|
12
|
+
readonly token: Token;
|
|
13
|
+
readonly resources: ScopedResources;
|
|
14
|
+
readonly signal: AbortSignal;
|
|
15
|
+
}
|
|
16
|
+
export type ScopeContext<T extends object = object> = Omit<T, keyof ScopeContextBase> & ScopeContextBase;
|
|
17
|
+
export declare abstract class Scope {
|
|
18
|
+
#private;
|
|
19
|
+
abstract get token(): Token;
|
|
20
|
+
abstract get resources(): ScopedResources;
|
|
21
|
+
get isClosed(): boolean;
|
|
22
|
+
get signal(): AbortSignal;
|
|
23
|
+
throwIfClosed(): void;
|
|
24
|
+
protected _onError(value: unknown): void;
|
|
25
|
+
use<T extends CancellableLike>(value: T): T;
|
|
26
|
+
tryUse<T extends CancellableLike>(value: T): T | undefined;
|
|
27
|
+
readonly scope: Scope;
|
|
28
|
+
resolveOrCancel<T>(promise: Awaitable<T>): Promise<T>;
|
|
29
|
+
/**
|
|
30
|
+
*
|
|
31
|
+
* @param block the function to run
|
|
32
|
+
* @param options
|
|
33
|
+
* @returns a promise that resolves to the result of `block`, or rejects if the scope is closed first.
|
|
34
|
+
*/
|
|
35
|
+
run<R>(block: (cx: ScopeContext) => Awaitable<R>, options?: ScopeRunOptions): Promise<R>;
|
|
36
|
+
function<R, Args extends readonly unknown[]>(block: (cx: ScopeContext, ...args: Args) => Awaitable<R>, options?: ScopeRunOptions): (...args: Args) => Promise<R>;
|
|
37
|
+
withResources(block: (builder: ScopedResources.Builder) => ScopedResources.Builder | void): Scope;
|
|
38
|
+
static from(this: void, src: ToScope): Scope;
|
|
39
|
+
/** An empty scope that will never be closed. */
|
|
40
|
+
static get static(): Scope;
|
|
41
|
+
}
|
|
42
|
+
export declare const STATIC_SCOPE: Scope;
|
|
43
|
+
export type ToScope = Scope | Token | readonly ToScope[] | Set<ToScope> | AbortSignal | CancellableOptions | Falsy;
|
|
44
|
+
//# sourceMappingURL=base.d.ts.map
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Token, STATIC_TOKEN } from '../token.js';
|
|
2
|
+
import {} from '../cancel.js';
|
|
3
|
+
import { scopeFrom } from './from.js';
|
|
4
|
+
import { isPromiseLike } from '../utils.js';
|
|
5
|
+
import { ScopedResources } from '../scopedResource.js';
|
|
6
|
+
import { StandardScope } from '../scope.js';
|
|
7
|
+
export class Scope {
|
|
8
|
+
get isClosed() {
|
|
9
|
+
return this.token.isCancelled;
|
|
10
|
+
}
|
|
11
|
+
get signal() {
|
|
12
|
+
return this.token.signal;
|
|
13
|
+
}
|
|
14
|
+
throwIfClosed() {
|
|
15
|
+
this.token.throwIfCancelled();
|
|
16
|
+
}
|
|
17
|
+
_onError(value) {
|
|
18
|
+
throw value;
|
|
19
|
+
}
|
|
20
|
+
use(value) {
|
|
21
|
+
return this.token.use(value);
|
|
22
|
+
}
|
|
23
|
+
tryUse(value) {
|
|
24
|
+
return this.token.tryUse(value);
|
|
25
|
+
}
|
|
26
|
+
scope = this;
|
|
27
|
+
resolveOrCancel(promise) {
|
|
28
|
+
const { token } = this;
|
|
29
|
+
const { error } = token;
|
|
30
|
+
if (error)
|
|
31
|
+
return Promise.reject(error);
|
|
32
|
+
if (!isPromiseLike(promise))
|
|
33
|
+
return Promise.resolve(promise);
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const sub = token.add(reject);
|
|
36
|
+
promise.then(x => {
|
|
37
|
+
sub?.dispose();
|
|
38
|
+
resolve(x);
|
|
39
|
+
}, e => {
|
|
40
|
+
sub?.dispose();
|
|
41
|
+
reject(e);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
#subScope(token, options) {
|
|
46
|
+
return scopeFrom([this, token, options]);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
*
|
|
50
|
+
* @param block the function to run
|
|
51
|
+
* @param options
|
|
52
|
+
* @returns a promise that resolves to the result of `block`, or rejects if the scope is closed first.
|
|
53
|
+
*/
|
|
54
|
+
async run(block, options) {
|
|
55
|
+
await using controller = this.use(Token.createController());
|
|
56
|
+
const subScope = this.#subScope(controller.token, options);
|
|
57
|
+
try {
|
|
58
|
+
return await subScope.resolveOrCancel(block(new Scope.#Context(subScope)));
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
await controller.cancel(error);
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function(block, options) {
|
|
66
|
+
let _block = block;
|
|
67
|
+
const controller = this.tryUse(Token.createController());
|
|
68
|
+
if (!controller)
|
|
69
|
+
return () => Promise.reject(this.token.error);
|
|
70
|
+
const subScope = this.#subScope(controller.token, options);
|
|
71
|
+
const sub = subScope.token.add(reason => {
|
|
72
|
+
_block = reason;
|
|
73
|
+
});
|
|
74
|
+
return async (...args) => {
|
|
75
|
+
sub?.dispose();
|
|
76
|
+
if (_block instanceof Error)
|
|
77
|
+
throw _block;
|
|
78
|
+
try {
|
|
79
|
+
const ret = await subScope.resolveOrCancel(_block(new Scope.#Context(subScope), ...args));
|
|
80
|
+
await controller.cancel();
|
|
81
|
+
return ret;
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
await controller.cancel(error);
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
withResources(block) {
|
|
90
|
+
const builder = ScopedResources.builder(this.token).inherit(this.resources);
|
|
91
|
+
block(builder);
|
|
92
|
+
const resources = builder.finish();
|
|
93
|
+
return new StandardScope({ ...this, resources });
|
|
94
|
+
}
|
|
95
|
+
static from(src) {
|
|
96
|
+
return scopeFrom(src);
|
|
97
|
+
}
|
|
98
|
+
/** An empty scope that will never be closed. */
|
|
99
|
+
static get static() {
|
|
100
|
+
return STATIC_SCOPE;
|
|
101
|
+
}
|
|
102
|
+
static #Context = class Context {
|
|
103
|
+
scope;
|
|
104
|
+
get token() {
|
|
105
|
+
return this.scope.token;
|
|
106
|
+
}
|
|
107
|
+
get signal() {
|
|
108
|
+
return this.scope.signal;
|
|
109
|
+
}
|
|
110
|
+
get resources() {
|
|
111
|
+
return this.scope.resources;
|
|
112
|
+
}
|
|
113
|
+
constructor(scope) {
|
|
114
|
+
this.scope = scope;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
class StaticScope extends Scope {
|
|
119
|
+
resources = ScopedResources.empty;
|
|
120
|
+
get token() {
|
|
121
|
+
return STATIC_TOKEN;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export const STATIC_SCOPE = new StaticScope();
|
|
125
|
+
//# sourceMappingURL=base.js.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Token } from '../token.js';
|
|
2
|
+
import { Scope, STATIC_SCOPE, StandardScope } from '../scope.js';
|
|
3
|
+
import { isArray } from '../utils.js';
|
|
4
|
+
import { ScopedResources } from '../scopedResource.js';
|
|
5
|
+
export function scopeFrom(src, onError) {
|
|
6
|
+
if (!src)
|
|
7
|
+
return STATIC_SCOPE;
|
|
8
|
+
if (src instanceof Scope)
|
|
9
|
+
return src;
|
|
10
|
+
if (src instanceof Token)
|
|
11
|
+
return new StandardScope({ token: src, onError });
|
|
12
|
+
if (src instanceof AbortSignal) {
|
|
13
|
+
return new StandardScope({ token: Token.fromAbortSignal(src, { onError }), onError });
|
|
14
|
+
}
|
|
15
|
+
let hasNonScopeToken = false;
|
|
16
|
+
const scopes = new Set();
|
|
17
|
+
const tokens = new Set();
|
|
18
|
+
const abortSignals = new Set();
|
|
19
|
+
const resources = new Set();
|
|
20
|
+
function flatten(src) {
|
|
21
|
+
if (!src)
|
|
22
|
+
return;
|
|
23
|
+
if (src instanceof Scope) {
|
|
24
|
+
if (src.token.isCancelled)
|
|
25
|
+
return src.token;
|
|
26
|
+
scopes.add(src);
|
|
27
|
+
if (!src.token.isDefused) {
|
|
28
|
+
tokens.add(src.token);
|
|
29
|
+
}
|
|
30
|
+
if (!src.resources.isEmpty) {
|
|
31
|
+
resources.add(src.resources);
|
|
32
|
+
}
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (src instanceof Token) {
|
|
36
|
+
hasNonScopeToken = true;
|
|
37
|
+
if (src.isCancelled)
|
|
38
|
+
return src;
|
|
39
|
+
if (!src.isDefused) {
|
|
40
|
+
tokens.add(src);
|
|
41
|
+
}
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (src instanceof AbortSignal) {
|
|
45
|
+
hasNonScopeToken = true;
|
|
46
|
+
if (src.aborted)
|
|
47
|
+
return Token.cancelled(src.reason);
|
|
48
|
+
abortSignals.add(src);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (isArray(src) || src instanceof Set) {
|
|
52
|
+
for (const child of src) {
|
|
53
|
+
const cancelled = flatten(child);
|
|
54
|
+
if (cancelled)
|
|
55
|
+
return cancelled;
|
|
56
|
+
}
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
return flatten(src.scope) || flatten(src.token) || flatten(src.signal);
|
|
60
|
+
}
|
|
61
|
+
const cancelled = flatten(src);
|
|
62
|
+
if (!hasNonScopeToken && scopes.size <= 1) {
|
|
63
|
+
// If only one scope was passed in with no other tokens, we can return the
|
|
64
|
+
// scope directly.
|
|
65
|
+
//
|
|
66
|
+
// If there are zero scopes and tokens, we can return the static scope
|
|
67
|
+
//
|
|
68
|
+
const [scope] = scopes;
|
|
69
|
+
if (!cancelled || scope?.token === cancelled)
|
|
70
|
+
return scope ?? Scope.static;
|
|
71
|
+
}
|
|
72
|
+
if (cancelled)
|
|
73
|
+
return new StandardScope({ token: cancelled });
|
|
74
|
+
let [signal] = abortSignals;
|
|
75
|
+
if (abortSignals.size > 1) {
|
|
76
|
+
signal = AbortSignal.any(Array.from(abortSignals));
|
|
77
|
+
}
|
|
78
|
+
if (signal) {
|
|
79
|
+
tokens.add(Token.fromAbortSignal(signal));
|
|
80
|
+
}
|
|
81
|
+
return new StandardScope({
|
|
82
|
+
token: Token.combine(tokens),
|
|
83
|
+
resources: ScopedResources.combine(resources),
|
|
84
|
+
onError,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=from.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Token } from '../token.ts';
|
|
2
|
+
import { Scope } from '../scope.ts';
|
|
3
|
+
import { ScopedResources } from '../scopedResource.js';
|
|
4
|
+
export declare class StandardScope extends Scope {
|
|
5
|
+
#private;
|
|
6
|
+
get token(): Token;
|
|
7
|
+
get resources(): ScopedResources;
|
|
8
|
+
protected _onError(value: unknown): void;
|
|
9
|
+
constructor(params: {
|
|
10
|
+
token?: Token | undefined;
|
|
11
|
+
resources?: ScopedResources | undefined;
|
|
12
|
+
onError?: ((e: unknown) => void) | undefined;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=standard.d.ts.map
|