@dxos/async 0.7.5-main.9d26e3a → 0.7.5-main.b19bfc8
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/lib/browser/index.mjs +481 -330
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +475 -327
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +481 -330
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/events.d.ts +13 -2
- package/dist/types/src/events.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +6 -5
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/persistent-lifecycle.d.ts +45 -0
- package/dist/types/src/persistent-lifecycle.d.ts.map +1 -0
- package/dist/types/src/persistent-lifecycle.test.d.ts +2 -0
- package/dist/types/src/persistent-lifecycle.test.d.ts.map +1 -0
- package/dist/types/src/task-scheduling.d.ts +1 -0
- package/dist/types/src/task-scheduling.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -7
- package/src/events.test.ts +15 -0
- package/src/events.ts +49 -9
- package/src/index.ts +6 -5
- package/src/persistent-lifecycle.test.ts +77 -0
- package/src/persistent-lifecycle.ts +128 -0
- package/src/task-scheduling.ts +4 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Context } from '@dxos/context';
|
|
2
|
+
import type { MaybePromise } from '@dxos/util';
|
|
2
3
|
export type UnsubscribeCallback = () => void;
|
|
3
4
|
export type Effect = () => UnsubscribeCallback | undefined;
|
|
4
5
|
interface EventEmitterLike {
|
|
@@ -17,6 +18,7 @@ export type ListenerOptions = {
|
|
|
17
18
|
weak?: boolean;
|
|
18
19
|
once?: boolean;
|
|
19
20
|
};
|
|
21
|
+
type EventCallback<T> = (data: T) => MaybePromise<void>;
|
|
20
22
|
/**
|
|
21
23
|
* An EventEmitter variant that does not do event multiplexing and represents a single event.
|
|
22
24
|
*
|
|
@@ -63,6 +65,15 @@ export declare class Event<T = void> implements ReadOnlyEvent<T> {
|
|
|
63
65
|
* @param data param that will be passed to all listeners.
|
|
64
66
|
*/
|
|
65
67
|
emit(data: T): void;
|
|
68
|
+
/**
|
|
69
|
+
* Emit an event and wait for async listeners to complete.
|
|
70
|
+
* In most cases should only be called by the class or entity containing the event.
|
|
71
|
+
* All listeners are called in order of subscription with persistent ones first.
|
|
72
|
+
* Listeners are called sequentially.
|
|
73
|
+
*
|
|
74
|
+
* @param data param that will be passed to all listeners.
|
|
75
|
+
*/
|
|
76
|
+
emitAsync(data: T): Promise<void>;
|
|
66
77
|
/**
|
|
67
78
|
* Register an event listener.
|
|
68
79
|
* If provided callback was already registered as once-listener, it is made permanent.
|
|
@@ -71,8 +82,8 @@ export declare class Event<T = void> implements ReadOnlyEvent<T> {
|
|
|
71
82
|
* @param options.weak If true, the callback will be weakly referenced and will be garbage collected if no other references to it exist.
|
|
72
83
|
* @returns function that unsubscribes this event listener
|
|
73
84
|
*/
|
|
74
|
-
on(callback:
|
|
75
|
-
on(ctx: Context, callback:
|
|
85
|
+
on(callback: EventCallback<T>): UnsubscribeCallback;
|
|
86
|
+
on(ctx: Context, callback: EventCallback<T>, options?: ListenerOptions): UnsubscribeCallback;
|
|
76
87
|
/**
|
|
77
88
|
* Unsubscribe this callback from new events. Includes persistent and once-listeners.
|
|
78
89
|
* NOTE: It is recommended to use `Event.on`'s return value instead.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../src/events.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../src/events.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,MAAM,mBAAmB,GAAG,MAAM,IAAI,CAAC;AAE7C,MAAM,MAAM,MAAM,GAAG,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAU3D,UAAU,gBAAgB;IACxB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI,CAAC;IAClD,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI,CAAC;CACpD;AAED;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IAExD,GAAG,CAAC,EAAE,EAAE,mBAAmB;IAI3B,KAAK;CAIN;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,KAAK,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;AAKxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,qBAAa,KAAK,CAAC,CAAC,GAAG,IAAI,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;IACtD;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC;IAYtE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+B;IAC1D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiC;IAE1D;;;;;;;;OAQG;IACH,IAAI,CAAC,IAAI,EAAE,CAAC;IAUZ;;;;;;;OAOG;IACG,SAAS,CAAC,IAAI,EAAE,CAAC;IAUvB;;;;;;;OAOG;IACH,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,mBAAmB;IACnD,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,mBAAmB;IAe5F;;;;;;OAMG;IACH,GAAG,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI;IAQ/B;;;;;OAKG;IACH,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,GAAG,mBAAmB;IACtD,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,GAAG,mBAAmB;IAapE;;;OAGG;IACI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC;IAQjD;;;;OAIG;IACH,OAAO,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAWpD;;;;OAIG;IACH,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAK/C;;OAEG;IAEG,gBAAgB,CAAC,SAAS,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAM/D;;OAEG;IACH,aAAa;IAIb;;;;;;;;;;;;;;;;OAgBG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB;IAe9C;;;;;OAKG;IAEH,QAAQ,CAAC,OAAO,SAAI;IA0BpB;;OAEG;IACH,gBAAgB,IAAI,KAAK,CAAC,IAAI,CAAC;IAI/B;;OAEG;IACH,MAAM;;;IAMN,OAAO,CAAC,YAAY;IAoBpB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,eAAe;CAOxB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,IAAI;IACrC;;;;;;;OAOG;IACH,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,GAAG,mBAAmB,CAAC;IACrD,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,mBAAmB,CAAC;IAE9F;;;;;;OAMG;IACH,GAAG,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;IAEvC;;;;;OAKG;IACH,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,GAAG,mBAAmB,CAAC;IAEvD;;;OAGG;IACH,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC;IAE3C;;;;OAIG;IACH,OAAO,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAErD;;;;OAIG;IACH,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAEhD;;OAEG;IACH,gBAAgB,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC;IAEhC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;CACzC"}
|
|
@@ -5,19 +5,20 @@ export * from './errors';
|
|
|
5
5
|
export * from './event-emitter';
|
|
6
6
|
export * from './events';
|
|
7
7
|
export * from './latch';
|
|
8
|
+
export * from './mutex';
|
|
8
9
|
export * from './observable';
|
|
9
10
|
export * from './observable-value';
|
|
10
|
-
export * from './
|
|
11
|
+
export * from './persistent-lifecycle';
|
|
11
12
|
export * from './sink';
|
|
12
13
|
export * from './stream-to-array';
|
|
14
|
+
export * from './task-scheduling';
|
|
15
|
+
export * from './test-stream';
|
|
16
|
+
export * from './testing';
|
|
13
17
|
export * from './timeout';
|
|
14
18
|
export * from './timer';
|
|
15
|
-
export * from './
|
|
19
|
+
export * from './track-leaks';
|
|
16
20
|
export * from './trigger';
|
|
17
21
|
export * from './types';
|
|
18
22
|
export * from './until';
|
|
19
|
-
export * from './task-scheduling';
|
|
20
|
-
export * from './test-stream';
|
|
21
|
-
export * from './track-leaks';
|
|
22
23
|
export * from './update-scheduler';
|
|
23
24
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,QAAQ,CAAC;AACvB,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Resource } from '@dxos/context';
|
|
2
|
+
export type PersistentLifecycleParams<T> = {
|
|
3
|
+
/**
|
|
4
|
+
* Create connection.
|
|
5
|
+
* If promise resolves successfully, connection is considered established.
|
|
6
|
+
*/
|
|
7
|
+
start: () => Promise<T | undefined>;
|
|
8
|
+
/**
|
|
9
|
+
* Reset connection to initial state.
|
|
10
|
+
*/
|
|
11
|
+
stop: (state: T) => Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Called after successful start.
|
|
14
|
+
*/
|
|
15
|
+
onRestart?: () => Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Maximum delay between restartion attempts.
|
|
18
|
+
* Default: 5000ms
|
|
19
|
+
*/
|
|
20
|
+
maxRestartDelay?: number;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Handles restarts (e.g. persists connection).
|
|
24
|
+
* Restarts are scheduled with exponential backoff.
|
|
25
|
+
*/
|
|
26
|
+
export declare class PersistentLifecycle<T> extends Resource {
|
|
27
|
+
private readonly _start;
|
|
28
|
+
private readonly _stop;
|
|
29
|
+
private readonly _onRestart?;
|
|
30
|
+
private readonly _maxRestartDelay;
|
|
31
|
+
private _currentState;
|
|
32
|
+
private _restartTask?;
|
|
33
|
+
private _restartAfter;
|
|
34
|
+
constructor({ start, stop, onRestart, maxRestartDelay }: PersistentLifecycleParams<T>);
|
|
35
|
+
get state(): T | undefined;
|
|
36
|
+
protected _open(): Promise<void>;
|
|
37
|
+
protected _close(): Promise<void>;
|
|
38
|
+
private _restart;
|
|
39
|
+
private _stopCurrentState;
|
|
40
|
+
/**
|
|
41
|
+
* Scheduling restart should be done from outside.
|
|
42
|
+
*/
|
|
43
|
+
scheduleRestart(): void;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=persistent-lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistent-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/persistent-lifecycle.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkB,QAAQ,EAAqB,MAAM,eAAe,CAAC;AAW5E,MAAM,MAAM,yBAAyB,CAAC,CAAC,IAAI;IACzC;;;OAGG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAEpC;;OAEG;IACH,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAElC;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhC;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;;GAGG;AACH,qBAAa,mBAAmB,CAAC,CAAC,CAAE,SAAQ,QAAQ;IAClD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA8B;IACpD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAsB;IAClD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAE1C,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,YAAY,CAAC,CAA2B;IAChD,OAAO,CAAC,aAAa,CAAK;gBAEd,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,eAA2C,EAAE,EAAE,yBAAyB,CAAC,CAAC,CAAC;IAQjH,IAAI,KAAK,kBAER;cAGwB,KAAK;cAgBL,MAAM;YAMjB,QAAQ;YAkBR,iBAAiB;IAW/B;;OAEG;IAEH,eAAe;CAMhB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistent-lifecycle.test.d.ts","sourceRoot":"","sources":["../../../src/persistent-lifecycle.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-scheduling.d.ts","sourceRoot":"","sources":["../../../src/task-scheduling.ts"],"names":[],"mappings":"AAIA,OAAO,EAAwB,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAEnE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAK/C,MAAM,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC;AAEvC;;;;GAIG;AAGH,qBAAa,YAAY;IAMrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAN5B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,SAAS,CAAiB;gBAGf,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAGjD;;OAEG;IACH,QAAQ;IAuBR;;OAEG;IACG,WAAW;IASjB;;;OAGG;IACG,IAAI;CAGX;AAED,eAAO,MAAM,YAAY,QAAS,OAAO,MAAM,MAAM,IAAI,SAMxD,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAe,OAAO,MAAM,MAAM,YAAY,CAAC,IAAI,CAAC,kBAMjF,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAS,OAAO,MAAM,MAAM,YAAY,CAAC,IAAI,CAAC,SAO3E,CAAC;AAEF,eAAO,MAAM,YAAY,QAAS,OAAO,MAAM,MAAM,YAAY,CAAC,IAAI,CAAC,YAAY,MAAM,SAgBxF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAAS,OAAO,QAAQ,MAAM,OAAO,CAAC,IAAI,CAAC,YAAY,MAAM,SAqB7F,CAAC;AAEF,eAAO,MAAM,sCAAsC,QAC5C,OAAO,QACN,MAAM,OAAO,CAAC,IAAI,CAAC,mBACR,MAAM,SAwBxB,CAAC"}
|
|
1
|
+
{"version":3,"file":"task-scheduling.d.ts","sourceRoot":"","sources":["../../../src/task-scheduling.ts"],"names":[],"mappings":"AAIA,OAAO,EAAwB,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAEnE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAK/C,MAAM,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC;AAEvC;;;;GAIG;AAGH,qBAAa,YAAY;IAMrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAN5B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,SAAS,CAAiB;gBAGf,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAGjD,IAAI,SAAS,YAEZ;IAED;;OAEG;IACH,QAAQ;IAuBR;;OAEG;IACG,WAAW;IASjB;;;OAGG;IACG,IAAI;CAGX;AAED,eAAO,MAAM,YAAY,QAAS,OAAO,MAAM,MAAM,IAAI,SAMxD,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAe,OAAO,MAAM,MAAM,YAAY,CAAC,IAAI,CAAC,kBAMjF,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAS,OAAO,MAAM,MAAM,YAAY,CAAC,IAAI,CAAC,SAO3E,CAAC;AAEF,eAAO,MAAM,YAAY,QAAS,OAAO,MAAM,MAAM,YAAY,CAAC,IAAI,CAAC,YAAY,MAAM,SAgBxF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAAS,OAAO,QAAQ,MAAM,OAAO,CAAC,IAAI,CAAC,YAAY,MAAM,SAqB7F,CAAC;AAEF,eAAO,MAAM,sCAAsC,QAC5C,OAAO,QACN,MAAM,OAAO,CAAC,IAAI,CAAC,mBACR,MAAM,SAwBxB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"5.7.
|
|
1
|
+
{"version":"5.7.3"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/async",
|
|
3
|
-
"version": "0.7.5-main.
|
|
3
|
+
"version": "0.7.5-main.b19bfc8",
|
|
4
4
|
"description": "Async utilities.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"author": "DXOS.org",
|
|
9
9
|
"sideEffects": true,
|
|
10
|
+
"type": "module",
|
|
10
11
|
"exports": {
|
|
11
12
|
".": {
|
|
12
13
|
"browser": "./dist/lib/browser/index.mjs",
|
|
@@ -28,12 +29,12 @@
|
|
|
28
29
|
"dependencies": {
|
|
29
30
|
"zen-observable": "^0.10.0",
|
|
30
31
|
"zen-push": "^0.3.1",
|
|
31
|
-
"@dxos/context": "0.7.5-main.
|
|
32
|
-
"@dxos/
|
|
33
|
-
"@dxos/
|
|
34
|
-
"@dxos/
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/
|
|
32
|
+
"@dxos/context": "0.7.5-main.b19bfc8",
|
|
33
|
+
"@dxos/invariant": "0.7.5-main.b19bfc8",
|
|
34
|
+
"@dxos/node-std": "0.7.5-main.b19bfc8",
|
|
35
|
+
"@dxos/debug": "0.7.5-main.b19bfc8",
|
|
36
|
+
"@dxos/util": "0.7.5-main.b19bfc8",
|
|
37
|
+
"@dxos/log": "0.7.5-main.b19bfc8"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
40
|
"@types/zen-observable": "^0.8.3"
|
package/src/events.test.ts
CHANGED
|
@@ -89,6 +89,21 @@ describe('Event', () => {
|
|
|
89
89
|
expect(error.message).to.equal('test');
|
|
90
90
|
});
|
|
91
91
|
|
|
92
|
+
test('emitAsync', async () => {
|
|
93
|
+
const event = new Event<number>();
|
|
94
|
+
let called = 0;
|
|
95
|
+
event.on(async (num) => {
|
|
96
|
+
await sleep(10);
|
|
97
|
+
called++;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
await event.emitAsync(1);
|
|
101
|
+
expect(called).to.equal(1);
|
|
102
|
+
|
|
103
|
+
// TODO(dmaretskyi): Disabled for now.
|
|
104
|
+
// expect(() => event.emit(1)).to.throw(TypeError);
|
|
105
|
+
});
|
|
106
|
+
|
|
92
107
|
// test.skip('weak', async () => {
|
|
93
108
|
// setFlagsFromString('--expose_gc');
|
|
94
109
|
// const gc = runInNewContext('gc'); // nocommit
|
package/src/events.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Context } from '@dxos/context';
|
|
6
|
+
import type { MaybePromise } from '@dxos/util';
|
|
6
7
|
|
|
7
8
|
export type UnsubscribeCallback = () => void;
|
|
8
9
|
|
|
@@ -42,6 +43,11 @@ export type ListenerOptions = {
|
|
|
42
43
|
once?: boolean;
|
|
43
44
|
};
|
|
44
45
|
|
|
46
|
+
type EventCallback<T> = (data: T) => MaybePromise<void>;
|
|
47
|
+
|
|
48
|
+
// TODO(dmaretskyi): Remove this once the code is cleaned up.
|
|
49
|
+
const DO_NOT_ERROR_ON_ASYNC_CALLBACK = true;
|
|
50
|
+
|
|
45
51
|
/**
|
|
46
52
|
* An EventEmitter variant that does not do event multiplexing and represents a single event.
|
|
47
53
|
*
|
|
@@ -102,7 +108,25 @@ export class Event<T = void> implements ReadOnlyEvent<T> {
|
|
|
102
108
|
*/
|
|
103
109
|
emit(data: T) {
|
|
104
110
|
for (const listener of this._listeners) {
|
|
105
|
-
|
|
111
|
+
listener.trigger(data);
|
|
112
|
+
|
|
113
|
+
if (listener.once) {
|
|
114
|
+
this._listeners.delete(listener);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Emit an event and wait for async listeners to complete.
|
|
121
|
+
* In most cases should only be called by the class or entity containing the event.
|
|
122
|
+
* All listeners are called in order of subscription with persistent ones first.
|
|
123
|
+
* Listeners are called sequentially.
|
|
124
|
+
*
|
|
125
|
+
* @param data param that will be passed to all listeners.
|
|
126
|
+
*/
|
|
127
|
+
async emitAsync(data: T) {
|
|
128
|
+
for (const listener of this._listeners) {
|
|
129
|
+
await listener.triggerAsync(data);
|
|
106
130
|
|
|
107
131
|
if (listener.once) {
|
|
108
132
|
this._listeners.delete(listener);
|
|
@@ -118,9 +142,9 @@ export class Event<T = void> implements ReadOnlyEvent<T> {
|
|
|
118
142
|
* @param options.weak If true, the callback will be weakly referenced and will be garbage collected if no other references to it exist.
|
|
119
143
|
* @returns function that unsubscribes this event listener
|
|
120
144
|
*/
|
|
121
|
-
on(callback:
|
|
122
|
-
on(ctx: Context, callback:
|
|
123
|
-
on(_ctx: any, _callback?:
|
|
145
|
+
on(callback: EventCallback<T>): UnsubscribeCallback;
|
|
146
|
+
on(ctx: Context, callback: EventCallback<T>, options?: ListenerOptions): UnsubscribeCallback;
|
|
147
|
+
on(_ctx: any, _callback?: EventCallback<T>, options?: ListenerOptions): UnsubscribeCallback {
|
|
124
148
|
const [ctx, callback] = _ctx instanceof Context ? [_ctx, _callback] : [new Context(), _ctx];
|
|
125
149
|
const weak = !!options?.weak;
|
|
126
150
|
const once = !!options?.once;
|
|
@@ -408,13 +432,13 @@ export interface ReadOnlyEvent<T = void> {
|
|
|
408
432
|
}
|
|
409
433
|
|
|
410
434
|
class EventListener<T> {
|
|
411
|
-
public readonly callback:
|
|
435
|
+
public readonly callback: EventCallback<T> | WeakRef<EventCallback<T>>;
|
|
412
436
|
|
|
413
437
|
private readonly _clearDispose?: () => void = undefined;
|
|
414
438
|
|
|
415
439
|
constructor(
|
|
416
440
|
event: Event<T>,
|
|
417
|
-
listener:
|
|
441
|
+
listener: EventCallback<T>,
|
|
418
442
|
public readonly ctx: Context,
|
|
419
443
|
public readonly once: boolean,
|
|
420
444
|
public readonly weak: boolean,
|
|
@@ -438,11 +462,27 @@ class EventListener<T> {
|
|
|
438
462
|
}
|
|
439
463
|
}
|
|
440
464
|
|
|
441
|
-
derefCallback():
|
|
442
|
-
return this.weak ? (this.callback as WeakRef<
|
|
465
|
+
derefCallback(): EventCallback<T> | undefined {
|
|
466
|
+
return this.weak ? (this.callback as WeakRef<EventCallback<T>>).deref() : (this.callback as EventCallback<T>);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
trigger(data: T) {
|
|
470
|
+
let result!: MaybePromise<void>;
|
|
471
|
+
try {
|
|
472
|
+
const callback = this.derefCallback();
|
|
473
|
+
result = callback?.(data);
|
|
474
|
+
} catch (err: any) {
|
|
475
|
+
this.ctx.raise(err);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (!DO_NOT_ERROR_ON_ASYNC_CALLBACK) {
|
|
479
|
+
if (result instanceof Promise) {
|
|
480
|
+
throw new TypeError('Event has async callbacks, use emitAsync instead');
|
|
481
|
+
}
|
|
482
|
+
}
|
|
443
483
|
}
|
|
444
484
|
|
|
445
|
-
async
|
|
485
|
+
async triggerAsync(data: T) {
|
|
446
486
|
try {
|
|
447
487
|
const callback = this.derefCallback();
|
|
448
488
|
await callback?.(data);
|
package/src/index.ts
CHANGED
|
@@ -9,18 +9,19 @@ export * from './errors';
|
|
|
9
9
|
export * from './event-emitter';
|
|
10
10
|
export * from './events';
|
|
11
11
|
export * from './latch';
|
|
12
|
+
export * from './mutex';
|
|
12
13
|
export * from './observable';
|
|
13
14
|
export * from './observable-value';
|
|
14
|
-
export * from './
|
|
15
|
+
export * from './persistent-lifecycle';
|
|
15
16
|
export * from './sink';
|
|
16
17
|
export * from './stream-to-array';
|
|
18
|
+
export * from './task-scheduling';
|
|
19
|
+
export * from './test-stream';
|
|
20
|
+
export * from './testing';
|
|
17
21
|
export * from './timeout';
|
|
18
22
|
export * from './timer';
|
|
19
|
-
export * from './
|
|
23
|
+
export * from './track-leaks';
|
|
20
24
|
export * from './trigger';
|
|
21
25
|
export * from './types';
|
|
22
26
|
export * from './until';
|
|
23
|
-
export * from './task-scheduling';
|
|
24
|
-
export * from './test-stream';
|
|
25
|
-
export * from './track-leaks';
|
|
26
27
|
export * from './update-scheduler';
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test, onTestFinished } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { log } from '@dxos/log';
|
|
8
|
+
|
|
9
|
+
import { PersistentLifecycle } from './persistent-lifecycle';
|
|
10
|
+
import { sleep } from './timeout';
|
|
11
|
+
import { Trigger } from './trigger';
|
|
12
|
+
|
|
13
|
+
describe('ConnectionState', () => {
|
|
14
|
+
test('first reconnect fires immediately', async () => {
|
|
15
|
+
const triggerCall = new Trigger<number>();
|
|
16
|
+
const persistentLifecycle = new PersistentLifecycle({
|
|
17
|
+
start: async () => {
|
|
18
|
+
triggerCall.wake(Date.now());
|
|
19
|
+
},
|
|
20
|
+
stop: async () => {},
|
|
21
|
+
});
|
|
22
|
+
await persistentLifecycle.open();
|
|
23
|
+
onTestFinished(async () => {
|
|
24
|
+
await persistentLifecycle.close();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const triggerTimestamp = Date.now();
|
|
28
|
+
persistentLifecycle.scheduleRestart();
|
|
29
|
+
const timeToTrigger = (await triggerCall.wait({ timeout: 1000 })) - triggerTimestamp;
|
|
30
|
+
expect(timeToTrigger).to.be.lessThan(50);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('second reconnect fires after 100ms', async () => {
|
|
34
|
+
let called = 0;
|
|
35
|
+
const triggerCall = new Trigger<number>();
|
|
36
|
+
|
|
37
|
+
const persistentLifecycle = new PersistentLifecycle({
|
|
38
|
+
start: async () => {
|
|
39
|
+
called += 1;
|
|
40
|
+
log.info('called', { called });
|
|
41
|
+
if (called < 3) {
|
|
42
|
+
throw new Error('TEST ERROR');
|
|
43
|
+
}
|
|
44
|
+
triggerCall.wake(Date.now());
|
|
45
|
+
},
|
|
46
|
+
stop: async () => {},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await persistentLifecycle.open();
|
|
50
|
+
onTestFinished(async () => {
|
|
51
|
+
await persistentLifecycle.close();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const triggerTimestamp = Date.now();
|
|
55
|
+
await sleep(10);
|
|
56
|
+
const timeToTrigger = (await triggerCall.wait({ timeout: 1000 })) - triggerTimestamp;
|
|
57
|
+
expect(timeToTrigger).to.be.greaterThanOrEqual(100);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('finish `restart` before close', async () => {
|
|
61
|
+
let restarted = false;
|
|
62
|
+
const persistentLifecycle = new PersistentLifecycle({
|
|
63
|
+
start: async () => await sleep(100),
|
|
64
|
+
stop: async () => {},
|
|
65
|
+
onRestart: async () => {
|
|
66
|
+
restarted = true;
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await persistentLifecycle.open();
|
|
71
|
+
|
|
72
|
+
persistentLifecycle.scheduleRestart();
|
|
73
|
+
await sleep(10);
|
|
74
|
+
await persistentLifecycle.close();
|
|
75
|
+
expect(restarted).to.be.true;
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { LifecycleState, Resource, cancelWithContext } from '@dxos/context';
|
|
6
|
+
import { warnAfterTimeout } from '@dxos/debug';
|
|
7
|
+
import { log } from '@dxos/log';
|
|
8
|
+
|
|
9
|
+
import { synchronized } from './mutex';
|
|
10
|
+
import { DeferredTask } from './task-scheduling';
|
|
11
|
+
import { sleep } from './timeout';
|
|
12
|
+
|
|
13
|
+
const INIT_RESTART_DELAY = 100;
|
|
14
|
+
const DEFAULT_MAX_RESTART_DELAY = 5000;
|
|
15
|
+
|
|
16
|
+
export type PersistentLifecycleParams<T> = {
|
|
17
|
+
/**
|
|
18
|
+
* Create connection.
|
|
19
|
+
* If promise resolves successfully, connection is considered established.
|
|
20
|
+
*/
|
|
21
|
+
start: () => Promise<T | undefined>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Reset connection to initial state.
|
|
25
|
+
*/
|
|
26
|
+
stop: (state: T) => Promise<void>;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Called after successful start.
|
|
30
|
+
*/
|
|
31
|
+
onRestart?: () => Promise<void>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Maximum delay between restartion attempts.
|
|
35
|
+
* Default: 5000ms
|
|
36
|
+
*/
|
|
37
|
+
maxRestartDelay?: number;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Handles restarts (e.g. persists connection).
|
|
42
|
+
* Restarts are scheduled with exponential backoff.
|
|
43
|
+
*/
|
|
44
|
+
export class PersistentLifecycle<T> extends Resource {
|
|
45
|
+
private readonly _start: () => Promise<T | undefined>;
|
|
46
|
+
private readonly _stop: (state: T) => Promise<void>;
|
|
47
|
+
private readonly _onRestart?: () => Promise<void>;
|
|
48
|
+
private readonly _maxRestartDelay: number;
|
|
49
|
+
|
|
50
|
+
private _currentState: T | undefined = undefined;
|
|
51
|
+
private _restartTask?: DeferredTask = undefined;
|
|
52
|
+
private _restartAfter = 0;
|
|
53
|
+
|
|
54
|
+
constructor({ start, stop, onRestart, maxRestartDelay = DEFAULT_MAX_RESTART_DELAY }: PersistentLifecycleParams<T>) {
|
|
55
|
+
super();
|
|
56
|
+
this._start = start;
|
|
57
|
+
this._stop = stop;
|
|
58
|
+
this._onRestart = onRestart;
|
|
59
|
+
this._maxRestartDelay = maxRestartDelay;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get state() {
|
|
63
|
+
return this._currentState;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@synchronized
|
|
67
|
+
protected override async _open() {
|
|
68
|
+
this._restartTask = new DeferredTask(this._ctx, async () => {
|
|
69
|
+
try {
|
|
70
|
+
await this._restart();
|
|
71
|
+
} catch (err) {
|
|
72
|
+
log.warn('Restart failed', { err });
|
|
73
|
+
this._restartTask?.schedule();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
this._currentState = await this._start().catch((err) => {
|
|
77
|
+
log.warn('Start failed', { err });
|
|
78
|
+
this._restartTask?.schedule();
|
|
79
|
+
return undefined;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
protected override async _close() {
|
|
84
|
+
await this._restartTask?.join();
|
|
85
|
+
await this._stopCurrentState();
|
|
86
|
+
this._restartTask = undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private async _restart() {
|
|
90
|
+
log(`restarting in ${this._restartAfter}ms`, { state: this._lifecycleState });
|
|
91
|
+
await this._stopCurrentState();
|
|
92
|
+
if (this._lifecycleState !== LifecycleState.OPEN) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
await cancelWithContext(this._ctx!, sleep(this._restartAfter));
|
|
96
|
+
this._restartAfter = Math.min(Math.max(this._restartAfter * 2, INIT_RESTART_DELAY), this._maxRestartDelay);
|
|
97
|
+
|
|
98
|
+
// May fail if the connection is not established.
|
|
99
|
+
await warnAfterTimeout(5_000, 'Connection establishment takes too long', async () => {
|
|
100
|
+
this._currentState = await this._start();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
this._restartAfter = 0;
|
|
104
|
+
await this._onRestart?.();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private async _stopCurrentState() {
|
|
108
|
+
if (this._currentState) {
|
|
109
|
+
try {
|
|
110
|
+
await this._stop(this._currentState);
|
|
111
|
+
} catch (err) {
|
|
112
|
+
log.catch(err);
|
|
113
|
+
}
|
|
114
|
+
this._currentState = undefined;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Scheduling restart should be done from outside.
|
|
120
|
+
*/
|
|
121
|
+
@synchronized
|
|
122
|
+
scheduleRestart() {
|
|
123
|
+
if (this._lifecycleState !== LifecycleState.OPEN) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
this._restartTask!.schedule();
|
|
127
|
+
}
|
|
128
|
+
}
|