@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.
Files changed (44) hide show
  1. package/dist/cancel.d.ts +1 -1
  2. package/dist/cancel.js +1 -2
  3. package/dist/commonResources.d.ts +3 -0
  4. package/dist/commonResources.js +3 -0
  5. package/dist/error.d.ts +2 -1
  6. package/dist/error.js +26 -4
  7. package/dist/events/cancel.d.ts +2 -1
  8. package/dist/events/cancel.js +59 -29
  9. package/dist/events/generic.d.ts +18 -14
  10. package/dist/events/generic.js +36 -26
  11. package/dist/events/sub.d.ts +3 -2
  12. package/dist/events/sub.js +5 -1
  13. package/dist/events/utils.d.ts +19 -0
  14. package/dist/events/utils.js +56 -0
  15. package/dist/events.d.ts +2 -1
  16. package/dist/events.js +1 -0
  17. package/dist/index.d.ts +4 -1
  18. package/dist/index.js +3 -0
  19. package/dist/lock.d.ts +18 -0
  20. package/dist/lock.js +200 -0
  21. package/dist/promise.d.ts +5 -0
  22. package/dist/promise.js +38 -0
  23. package/dist/scope/base.d.ts +99 -14
  24. package/dist/scope/base.js +163 -41
  25. package/dist/scope/context.d.ts +19 -0
  26. package/dist/scope/context.js +71 -0
  27. package/dist/scope/from.d.ts +1 -1
  28. package/dist/scope/from.js +32 -11
  29. package/dist/scope/standard.d.ts +4 -1
  30. package/dist/scope/standard.js +6 -0
  31. package/dist/scope.d.ts +1 -1
  32. package/dist/scopedResource.js +1 -2
  33. package/dist/timers.d.ts +23 -0
  34. package/dist/{timer.js → timers.js} +2 -4
  35. package/dist/token/base.d.ts +29 -16
  36. package/dist/token/base.js +39 -23
  37. package/dist/token.d.ts +1 -1
  38. package/dist/types.d.ts +19 -84
  39. package/package.json +6 -1
  40. package/dist/join.d.ts +0 -16
  41. package/dist/join.js +0 -85
  42. package/dist/timer.d.ts +0 -13
  43. package/dist/utils.d.ts +0 -51
  44. 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 './join.js';
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);
@@ -0,0 +1,3 @@
1
+ import { ResourceKey } from "./scopedResource.ts";
2
+ export declare const clock: ResourceKey<unknown, unknown>;
3
+ //# sourceMappingURL=commonResources.d.ts.map
@@ -0,0 +1,3 @@
1
+ import { ResourceKey } from "./scopedResource.js";
2
+ export const clock = ResourceKey.create("clock");
3
+ //# sourceMappingURL=commonResources.js.map
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 toError<T>(value: T): ToError<T>;
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 './utils.js';
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 toError(value) {
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 new AggregateError(errors);
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
@@ -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
@@ -1,7 +1,7 @@
1
+ import { joinPromises } from '@youngspe/common-async-utils';
1
2
  import { cancelObject } from '../cancel.js';
2
- import { combineErrors, toError } from '../error.js';
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
- constructor(inner, callbacks) {
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) => (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);
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
- 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();
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);
@@ -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 GenericEventController<T, Ret = void, Async extends boolean = false, Context = void> extends Disposable {
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
- #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>;
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
@@ -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 { signal, onRemove, once = false } = options ?? {};
198
- if (signal?.aborted)
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: false };
219
+ const handler = { key, listener, once, paused, passive };
209
220
  const sub = Subscription.fromLifecycle({
210
221
  init: () => ({
211
- resume: first => {
212
- if (!first) {
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 removeSignal = undefined;
226
- if (signal) {
227
- const onAbort = () => sub.dispose();
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 && removeSignal ?
242
+ onRemove && tokenSub ?
233
243
  () => {
234
244
  try {
235
- removeSignal();
245
+ tokenSub.dispose();
236
246
  }
237
247
  finally {
238
248
  onRemove();
239
249
  }
240
250
  }
241
- : onRemove || removeSignal;
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 GenericEventEmitter(new WeakRef(this.#state));
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 createController(params) {
310
- return new GenericEventEmitter.#Controller(params);
319
+ static _createController(params) {
320
+ return new DefaultGenericEventEmitter.#Controller(params);
311
321
  }
312
- static create(params) {
313
- return GenericEventEmitter.createController(params).emitter;
322
+ static _create(params) {
323
+ return DefaultGenericEventEmitter.createController(params).emitter;
314
324
  }
315
325
  }
316
326
  //# sourceMappingURL=generic.js.map
@@ -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<[], [first: boolean]> & {
58
+ static fromLifecycle(this: void, lifecycle: (SubscriptionLifecycle<[], [initializing: boolean]> & {
59
59
  isActive?: ((this: void) => boolean) | undefined;
60
- }) | (() => SubscriptionLifecycle.Initialized<[first: boolean]>) | undefined): Subscription;
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 {};
@@ -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
- this.#initialized?.resume(true);
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
@@ -1,3 +1,4 @@
1
1
  export { GenericEventEmitter, } from './events/generic.js';
2
2
  export { Subscription, SubscriptionLifecycleManager } from './events/sub.js';
3
+ export { onNextEvent } from './events/utils.js';
3
4
  //# sourceMappingURL=events.js.map
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