@adviser/cement 0.4.59 → 0.4.61
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/cjs/future.cjs +2 -0
- package/cjs/future.cjs.map +1 -1
- package/cjs/future.d.ts +1 -0
- package/cjs/future.d.ts.map +1 -1
- package/cjs/future.test.cjs +5 -0
- package/cjs/future.test.cjs.map +1 -1
- package/cjs/index.cjs +1 -0
- package/cjs/index.cjs.map +1 -1
- package/cjs/index.d.ts +1 -0
- package/cjs/index.d.ts.map +1 -1
- package/cjs/poller.cjs +87 -16
- package/cjs/poller.cjs.map +1 -1
- package/cjs/poller.d.ts +20 -10
- package/cjs/poller.d.ts.map +1 -1
- package/cjs/poller.test.cjs +2 -2
- package/cjs/poller.test.cjs.map +1 -1
- package/cjs/timeouted.cjs +119 -0
- package/cjs/timeouted.cjs.map +1 -0
- package/cjs/timeouted.d.ts +45 -0
- package/cjs/timeouted.d.ts.map +1 -0
- package/cjs/timeouted.test.cjs +581 -0
- package/cjs/timeouted.test.cjs.map +1 -0
- package/cjs/timeouted.test.d.ts +2 -0
- package/cjs/timeouted.test.d.ts.map +1 -0
- package/cjs/utils/promise-sleep.cjs +27 -26
- package/cjs/utils/promise-sleep.cjs.map +1 -1
- package/cjs/utils/promise-sleep.d.ts +28 -2
- package/cjs/utils/promise-sleep.d.ts.map +1 -1
- package/cjs/utils/promise-sleep.test.cjs +13 -13
- package/cjs/utils/promise-sleep.test.cjs.map +1 -1
- package/cjs/version.cjs +1 -1
- package/deno.json +1 -1
- package/esm/future.d.ts +1 -0
- package/esm/future.d.ts.map +1 -1
- package/esm/future.js +2 -0
- package/esm/future.js.map +1 -1
- package/esm/future.test.js +5 -0
- package/esm/future.test.js.map +1 -1
- package/esm/index.d.ts +1 -0
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/poller.d.ts +20 -10
- package/esm/poller.d.ts.map +1 -1
- package/esm/poller.js +87 -16
- package/esm/poller.js.map +1 -1
- package/esm/poller.test.js +2 -2
- package/esm/poller.test.js.map +1 -1
- package/esm/timeouted.d.ts +45 -0
- package/esm/timeouted.d.ts.map +1 -0
- package/esm/timeouted.js +110 -0
- package/esm/timeouted.js.map +1 -0
- package/esm/timeouted.test.d.ts +2 -0
- package/esm/timeouted.test.d.ts.map +1 -0
- package/esm/timeouted.test.js +579 -0
- package/esm/timeouted.test.js.map +1 -0
- package/esm/utils/promise-sleep.d.ts +28 -2
- package/esm/utils/promise-sleep.d.ts.map +1 -1
- package/esm/utils/promise-sleep.js +27 -26
- package/esm/utils/promise-sleep.js.map +1 -1
- package/esm/utils/promise-sleep.test.js +13 -13
- package/esm/utils/promise-sleep.test.js.map +1 -1
- package/esm/version.js +1 -1
- package/package.json +1 -1
- package/src/future.ts +5 -0
- package/src/index.ts +1 -0
- package/src/poller.ts +128 -29
- package/src/timeouted.ts +186 -0
- package/src/utils/promise-sleep.ts +61 -31
- package/ts/cjs/future.d.ts +1 -0
- package/ts/cjs/future.d.ts.map +1 -1
- package/ts/cjs/future.js +2 -0
- package/ts/cjs/future.js.map +1 -1
- package/ts/cjs/future.test.js +5 -0
- package/ts/cjs/future.test.js.map +1 -1
- package/ts/cjs/index.d.ts +1 -0
- package/ts/cjs/index.d.ts.map +1 -1
- package/ts/cjs/index.js +1 -0
- package/ts/cjs/index.js.map +1 -1
- package/ts/cjs/poller.d.ts +20 -10
- package/ts/cjs/poller.d.ts.map +1 -1
- package/ts/cjs/poller.js +87 -16
- package/ts/cjs/poller.js.map +1 -1
- package/ts/cjs/poller.test.js +2 -2
- package/ts/cjs/poller.test.js.map +1 -1
- package/ts/cjs/timeouted.d.ts +45 -0
- package/ts/cjs/timeouted.d.ts.map +1 -0
- package/ts/cjs/timeouted.js +119 -0
- package/ts/cjs/timeouted.js.map +1 -0
- package/ts/cjs/timeouted.test.d.ts +2 -0
- package/ts/cjs/timeouted.test.d.ts.map +1 -0
- package/ts/cjs/timeouted.test.js +581 -0
- package/ts/cjs/timeouted.test.js.map +1 -0
- package/ts/cjs/utils/promise-sleep.d.ts +28 -2
- package/ts/cjs/utils/promise-sleep.d.ts.map +1 -1
- package/ts/cjs/utils/promise-sleep.js +27 -26
- package/ts/cjs/utils/promise-sleep.js.map +1 -1
- package/ts/cjs/utils/promise-sleep.test.js +13 -13
- package/ts/cjs/utils/promise-sleep.test.js.map +1 -1
- package/ts/cjs/version.js +1 -1
- package/ts/esm/future.d.ts +1 -0
- package/ts/esm/future.d.ts.map +1 -1
- package/ts/esm/future.js +2 -0
- package/ts/esm/future.js.map +1 -1
- package/ts/esm/future.test.js +5 -0
- package/ts/esm/future.test.js.map +1 -1
- package/ts/esm/index.d.ts +1 -0
- package/ts/esm/index.d.ts.map +1 -1
- package/ts/esm/index.js +1 -0
- package/ts/esm/index.js.map +1 -1
- package/ts/esm/poller.d.ts +20 -10
- package/ts/esm/poller.d.ts.map +1 -1
- package/ts/esm/poller.js +87 -16
- package/ts/esm/poller.js.map +1 -1
- package/ts/esm/poller.test.js +2 -2
- package/ts/esm/poller.test.js.map +1 -1
- package/ts/esm/timeouted.d.ts +45 -0
- package/ts/esm/timeouted.d.ts.map +1 -0
- package/ts/esm/timeouted.js +110 -0
- package/ts/esm/timeouted.js.map +1 -0
- package/ts/esm/timeouted.test.d.ts +2 -0
- package/ts/esm/timeouted.test.d.ts.map +1 -0
- package/ts/esm/timeouted.test.js +579 -0
- package/ts/esm/timeouted.test.js.map +1 -0
- package/ts/esm/utils/promise-sleep.d.ts +28 -2
- package/ts/esm/utils/promise-sleep.d.ts.map +1 -1
- package/ts/esm/utils/promise-sleep.js +27 -26
- package/ts/esm/utils/promise-sleep.js.map +1 -1
- package/ts/esm/utils/promise-sleep.test.js +13 -13
- package/ts/esm/utils/promise-sleep.test.js.map +1 -1
- package/ts/esm/version.js +1 -1
|
@@ -1,3 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export interface SleepBase {
|
|
2
|
+
readonly state: "sleeped" | "error" | "aborted";
|
|
3
|
+
readonly isOk: boolean;
|
|
4
|
+
readonly isErr: boolean;
|
|
5
|
+
readonly isAborted: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface SleepOk extends SleepBase {
|
|
8
|
+
readonly state: "sleeped";
|
|
9
|
+
readonly isOk: true;
|
|
10
|
+
readonly isErr: false;
|
|
11
|
+
readonly isAborted: false;
|
|
12
|
+
}
|
|
13
|
+
export interface SleepErr extends SleepBase {
|
|
14
|
+
readonly state: "error";
|
|
15
|
+
readonly error: Error;
|
|
16
|
+
readonly isOk: false;
|
|
17
|
+
readonly isErr: true;
|
|
18
|
+
readonly isAborted: false;
|
|
19
|
+
}
|
|
20
|
+
export interface SleepAbort extends SleepBase {
|
|
21
|
+
readonly state: "aborted";
|
|
22
|
+
readonly reason: Error;
|
|
23
|
+
readonly isOk: false;
|
|
24
|
+
readonly isErr: false;
|
|
25
|
+
readonly isAborted: true;
|
|
26
|
+
}
|
|
27
|
+
export type SleepResult = SleepOk | SleepErr | SleepAbort;
|
|
28
|
+
export declare function sleep(ms: number, signal?: AbortSignal): Promise<SleepResult>;
|
|
3
29
|
//# sourceMappingURL=promise-sleep.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promise-sleep.d.ts","sourceRoot":"","sources":["../../../../src/utils/promise-sleep.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"promise-sleep.d.ts","sourceRoot":"","sources":["../../../../src/utils/promise-sleep.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,OAAQ,SAAQ,SAAS;IACxC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC;CAC3B;AAED,MAAM,WAAW,QAAS,SAAQ,SAAS;IACzC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC;CAC3B;AAED,MAAM,WAAW,UAAW,SAAQ,SAAS;IAC3C,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;CAC1B;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC;AAU1D,wBAAsB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CA8BlF"}
|
|
@@ -1,29 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export function sleep(ms, signal) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
1
|
+
import { Future } from "../future.js";
|
|
2
|
+
export async function sleep(ms, signal) {
|
|
3
|
+
if (ms < 0) {
|
|
4
|
+
return { state: "sleeped", isOk: true, isErr: false, isAborted: false };
|
|
5
|
+
}
|
|
6
|
+
if (signal?.aborted) {
|
|
7
|
+
const err = new Error("sleep aborted");
|
|
8
|
+
return { state: "aborted", reason: err, isOk: false, isErr: false, isAborted: true };
|
|
9
|
+
}
|
|
10
|
+
const sleepFuture = new Future();
|
|
11
|
+
const id = setTimeout(() => {
|
|
12
|
+
sleepFuture.resolve({ state: "sleeped", isOk: true, isErr: false, isAborted: false });
|
|
13
|
+
}, ms);
|
|
14
|
+
const abortFuture = new Future();
|
|
15
|
+
function onAbort() {
|
|
16
|
+
abortFuture.resolve({
|
|
17
|
+
state: "aborted",
|
|
18
|
+
reason: new Error("sleep aborted"),
|
|
19
|
+
isOk: false,
|
|
20
|
+
isErr: false,
|
|
21
|
+
isAborted: true,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
25
|
+
return Promise.race([abortFuture.asPromise(), sleepFuture.asPromise()]).finally(() => {
|
|
26
|
+
clearTimeout(id);
|
|
27
|
+
signal?.removeEventListener("abort", onAbort);
|
|
27
28
|
});
|
|
28
29
|
}
|
|
29
30
|
//# sourceMappingURL=promise-sleep.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promise-sleep.js","sourceRoot":"","sources":["../../../../src/utils/promise-sleep.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"promise-sleep.js","sourceRoot":"","sources":["../../../../src/utils/promise-sleep.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AA0CtC,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,EAAU,EAAE,MAAoB,EAAwB;IAClF,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QACX,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAC1E,CAAC;IACD,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;QACpB,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;QAEvC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACvF,CAAC;IACD,MAAM,WAAW,GAAG,IAAI,MAAM,EAAW,CAAC;IAC1C,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC;QAE1B,WAAW,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IAAA,CACvF,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,WAAW,GAAG,IAAI,MAAM,EAAc,CAAC;IAC7C,SAAS,OAAO,GAAS;QACvB,WAAW,CAAC,OAAO,CAAC;YAClB,KAAK,EAAE,SAAS;YAChB,MAAM,EAAE,IAAI,KAAK,CAAC,eAAe,CAAC;YAClC,IAAI,EAAE,KAAK;YACX,KAAK,EAAE,KAAK;YACZ,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;IAAA,CACJ;IACD,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3D,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,SAAS,EAAE,EAAE,WAAW,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACpF,YAAY,CAAC,EAAE,CAAC,CAAC;QACjB,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAAA,CAC/C,CAAC,CAAC;AAAA,CACJ"}
|
|
@@ -5,58 +5,58 @@ it("sleeps for the specified duration", async () => {
|
|
|
5
5
|
const duration = performance.now() - start;
|
|
6
6
|
expect(duration).toBeGreaterThanOrEqual(95);
|
|
7
7
|
expect(duration).toBeLessThan(150);
|
|
8
|
-
expect(result.isOk
|
|
9
|
-
expect(result.unwrap()).toBeUndefined();
|
|
8
|
+
expect(result.isOk).toBe(true);
|
|
10
9
|
});
|
|
11
10
|
it("returns immediately for zero milliseconds", async () => {
|
|
12
11
|
const start = performance.now();
|
|
13
12
|
const result = await sleep(0);
|
|
14
13
|
const duration = performance.now() - start;
|
|
15
14
|
expect(duration).toBeLessThan(10);
|
|
16
|
-
expect(result.isOk
|
|
15
|
+
expect(result.isOk).toBe(true);
|
|
17
16
|
});
|
|
18
17
|
it("returns immediately for negative milliseconds", async () => {
|
|
19
18
|
const start = performance.now();
|
|
20
19
|
const result = await sleep(-100);
|
|
21
20
|
const duration = performance.now() - start;
|
|
22
21
|
expect(duration).toBeLessThan(10);
|
|
23
|
-
expect(result.isOk
|
|
22
|
+
expect(result.isOk).toBe(true);
|
|
24
23
|
});
|
|
25
24
|
it("returns error when aborted before sleep starts", async () => {
|
|
26
25
|
const controller = new AbortController();
|
|
27
26
|
controller.abort();
|
|
28
|
-
const result = await sleep(100, controller.signal);
|
|
29
|
-
expect(result.isOk
|
|
30
|
-
expect(result.
|
|
27
|
+
const result = (await sleep(100, controller.signal));
|
|
28
|
+
expect(result.isOk).toBe(false);
|
|
29
|
+
expect(result.isAborted).toBe(true);
|
|
30
|
+
expect(result.reason.message).toBe("sleep aborted");
|
|
31
31
|
});
|
|
32
32
|
it("returns error when aborted during sleep", async () => {
|
|
33
33
|
const controller = new AbortController();
|
|
34
34
|
const start = performance.now();
|
|
35
35
|
const sleepPromise = sleep(200, controller.signal);
|
|
36
36
|
setTimeout(() => controller.abort(), 50);
|
|
37
|
-
const result = await sleepPromise;
|
|
37
|
+
const result = (await sleepPromise);
|
|
38
38
|
const duration = performance.now() - start;
|
|
39
39
|
expect(duration).toBeGreaterThanOrEqual(45);
|
|
40
40
|
expect(duration).toBeLessThan(150);
|
|
41
|
-
expect(result.isOk
|
|
42
|
-
expect(result.
|
|
41
|
+
expect(result.isOk).toBe(false);
|
|
42
|
+
expect(result.reason.message).toBe("sleep aborted");
|
|
43
43
|
});
|
|
44
44
|
it("completes successfully if not aborted", async () => {
|
|
45
45
|
const controller = new AbortController();
|
|
46
46
|
const result = await sleep(50, controller.signal);
|
|
47
|
-
expect(result.isOk
|
|
47
|
+
expect(result.isOk).toBe(true);
|
|
48
48
|
});
|
|
49
49
|
it("cleans up timeout and event listener on abort", async () => {
|
|
50
50
|
const controller = new AbortController();
|
|
51
51
|
const sleepPromise = sleep(100, controller.signal);
|
|
52
52
|
controller.abort();
|
|
53
53
|
const result = await sleepPromise;
|
|
54
|
-
expect(result.isOk
|
|
54
|
+
expect(result.isOk).toBe(false);
|
|
55
55
|
});
|
|
56
56
|
it("cleans up timeout and event listener on success", async () => {
|
|
57
57
|
const controller = new AbortController();
|
|
58
58
|
const result = await sleep(50, controller.signal);
|
|
59
|
-
expect(result.isOk
|
|
59
|
+
expect(result.isOk).toBe(true);
|
|
60
60
|
controller.abort();
|
|
61
61
|
});
|
|
62
62
|
//# sourceMappingURL=promise-sleep.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promise-sleep.test.js","sourceRoot":"","sources":["../../../../src/utils/promise-sleep.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"promise-sleep.test.js","sourceRoot":"","sources":["../../../../src/utils/promise-sleep.test.ts"],"names":[],"mappings":"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"}
|
package/esm/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const VERSION = "0.4.
|
|
1
|
+
export const VERSION = "0.4.61";
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
package/package.json
CHANGED
package/src/future.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Lazy } from "./resolve-once.js";
|
|
2
|
+
|
|
1
3
|
export class Future<T, CTX = void> {
|
|
2
4
|
// readonly id = Math.random();
|
|
3
5
|
readonly #promise: Promise<T>;
|
|
@@ -17,6 +19,9 @@ export class Future<T, CTX = void> {
|
|
|
17
19
|
});
|
|
18
20
|
}
|
|
19
21
|
|
|
22
|
+
// the id is not cryptographically secure, but good enough for transaction id
|
|
23
|
+
readonly id: () => string = Lazy(() => Math.random().toString(36).substring(2) + Date.now().toString(36));
|
|
24
|
+
|
|
20
25
|
asPromise(): Promise<T> {
|
|
21
26
|
return this.#promise;
|
|
22
27
|
}
|
package/src/index.ts
CHANGED
package/src/poller.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Future } from "./future.js";
|
|
1
2
|
import { sleep } from "./utils/promise-sleep.js";
|
|
2
3
|
import { Writable } from "ts-essentials";
|
|
3
4
|
|
|
@@ -7,42 +8,59 @@ export interface PollerStats {
|
|
|
7
8
|
readonly totalElapsedMs: number;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
readonly state: "waiting";
|
|
11
|
+
interface PollWithStats {
|
|
12
12
|
readonly stats: PollerStats;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export interface
|
|
15
|
+
export interface PurPollWaitingActionResult {
|
|
16
|
+
readonly state: "waiting";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type PollWaitingActionResult = PurPollWaitingActionResult & PollWithStats;
|
|
20
|
+
|
|
21
|
+
export interface PurPollSuccessActionResult<T> {
|
|
16
22
|
readonly state: "success";
|
|
17
23
|
readonly result: T;
|
|
18
|
-
readonly stats: PollerStats;
|
|
19
24
|
}
|
|
20
25
|
|
|
21
|
-
export
|
|
26
|
+
export type PollSuccessActionResult<T> = PurPollSuccessActionResult<T> & PollWithStats;
|
|
27
|
+
|
|
28
|
+
export interface PurPollErrorActionResult {
|
|
22
29
|
readonly state: "error";
|
|
23
30
|
readonly error: Error;
|
|
24
|
-
readonly stats: PollerStats;
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
export
|
|
33
|
+
export type PollErrorActionResult = PurPollErrorActionResult & PollWithStats;
|
|
34
|
+
|
|
35
|
+
export interface PurPollTimeoutActionResult {
|
|
28
36
|
readonly state: "timeout";
|
|
29
|
-
readonly stats: PollerStats;
|
|
30
37
|
}
|
|
31
38
|
|
|
39
|
+
export type PollTimeoutActionResult = PurPollTimeoutActionResult & PollWithStats;
|
|
40
|
+
|
|
41
|
+
export interface PurPollAbortActionResult {
|
|
42
|
+
readonly state: "aborted";
|
|
43
|
+
readonly reason: unknown;
|
|
44
|
+
}
|
|
45
|
+
export type PollAbortActionResult = PurPollAbortActionResult & PollWithStats;
|
|
46
|
+
|
|
32
47
|
export type PollActionResult<T> =
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
48
|
+
| PurPollWaitingActionResult
|
|
49
|
+
| PurPollSuccessActionResult<T>
|
|
50
|
+
| PurPollErrorActionResult
|
|
51
|
+
| PurPollTimeoutActionResult
|
|
52
|
+
| PurPollAbortActionResult;
|
|
36
53
|
|
|
37
54
|
export const FOREVER = 2147483647; // Maximum setTimeout delay
|
|
38
55
|
export interface PollerOptions {
|
|
39
56
|
readonly intervalMs: number;
|
|
57
|
+
readonly actionTimeoutMs: number; // not -1 it means if a timeout occures the action is retried in the next interval
|
|
40
58
|
readonly timeoutMs: number;
|
|
41
59
|
readonly exponentialBackoff: boolean;
|
|
42
60
|
readonly abortSignal?: AbortSignal;
|
|
43
61
|
}
|
|
44
62
|
|
|
45
|
-
export type PollerResult<T> = PollSuccessActionResult<T> | PollErrorActionResult | PollTimeoutActionResult;
|
|
63
|
+
export type PollerResult<T> = PollSuccessActionResult<T> | PollErrorActionResult | PollTimeoutActionResult | PollAbortActionResult;
|
|
46
64
|
|
|
47
65
|
function doneStats(stats: Writable<PollerStats> & { startTime: number }): PollerStats {
|
|
48
66
|
stats.totalElapsedMs = Date.now() - stats.startTime;
|
|
@@ -55,14 +73,56 @@ function doneStats(stats: Writable<PollerStats> & { startTime: number }): Poller
|
|
|
55
73
|
|
|
56
74
|
async function interPoller<T>(
|
|
57
75
|
fn: (abortSignal?: AbortSignal) => Promise<PollActionResult<T>>,
|
|
58
|
-
options: Writable<PollerOptions
|
|
76
|
+
options: Writable<Omit<PollerOptions, "abortSignal">>,
|
|
59
77
|
stats: Writable<PollerStats> & { readonly startTime: number },
|
|
78
|
+
abortController: AbortController,
|
|
60
79
|
): Promise<PollerResult<T>> {
|
|
61
80
|
do {
|
|
62
|
-
let result: PollActionResult<T>;
|
|
81
|
+
// let result: PollActionResult<T>;
|
|
63
82
|
try {
|
|
64
83
|
stats.attempts += 1;
|
|
65
|
-
|
|
84
|
+
|
|
85
|
+
const abortCheck = new Future<PurPollAbortActionResult>();
|
|
86
|
+
function onAbort(): void {
|
|
87
|
+
abortCheck.resolve({
|
|
88
|
+
state: "aborted",
|
|
89
|
+
reason: abortController.signal.reason,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const fnAbortController = new AbortController();
|
|
93
|
+
fnAbortController.signal.addEventListener("abort", onAbort, { once: true });
|
|
94
|
+
|
|
95
|
+
const races = [
|
|
96
|
+
fn(fnAbortController.signal).finally(() => {
|
|
97
|
+
// abort all other waits
|
|
98
|
+
fnAbortController.abort();
|
|
99
|
+
}),
|
|
100
|
+
abortCheck.asPromise().finally(() => {
|
|
101
|
+
// abort all other waits
|
|
102
|
+
fnAbortController.abort();
|
|
103
|
+
}),
|
|
104
|
+
];
|
|
105
|
+
if (options.actionTimeoutMs > 0) {
|
|
106
|
+
races.push(
|
|
107
|
+
sleep(options.actionTimeoutMs, fnAbortController.signal).then((res): PollActionResult<T> => {
|
|
108
|
+
// abort all other waits
|
|
109
|
+
fnAbortController.abort(new Error("poller action timeout"));
|
|
110
|
+
switch (res.state) {
|
|
111
|
+
case "sleeped":
|
|
112
|
+
// timeout occurred but we retry
|
|
113
|
+
return { state: "waiting" as const };
|
|
114
|
+
case "error":
|
|
115
|
+
return { state: "error" as const, error: res.error };
|
|
116
|
+
case "aborted":
|
|
117
|
+
return { state: "aborted" as const, reason: res.reason };
|
|
118
|
+
}
|
|
119
|
+
throw new Error("Unreachable code in poller action timeout");
|
|
120
|
+
}),
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
const result = await Promise.race<PollActionResult<T>>(races).finally(() => {
|
|
124
|
+
fnAbortController.signal.removeEventListener("abort", onAbort);
|
|
125
|
+
});
|
|
66
126
|
switch (result.state) {
|
|
67
127
|
case "waiting":
|
|
68
128
|
{
|
|
@@ -70,16 +130,33 @@ async function interPoller<T>(
|
|
|
70
130
|
options.intervalMs = Math.min(options.intervalMs * 2, FOREVER);
|
|
71
131
|
}
|
|
72
132
|
stats.lastIntervalMs = options.intervalMs;
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
133
|
+
const res = await sleep(options.intervalMs, abortController.signal);
|
|
134
|
+
switch (true) {
|
|
135
|
+
case res.isAborted:
|
|
136
|
+
return {
|
|
137
|
+
state: "aborted" as const,
|
|
138
|
+
reason: res.reason,
|
|
139
|
+
stats: doneStats(stats),
|
|
140
|
+
};
|
|
141
|
+
case res.isOk:
|
|
142
|
+
break;
|
|
143
|
+
case res.isErr:
|
|
144
|
+
return {
|
|
145
|
+
state: "error",
|
|
146
|
+
error: res.error,
|
|
147
|
+
stats: doneStats(stats),
|
|
148
|
+
};
|
|
149
|
+
default:
|
|
150
|
+
throw new Error("poller interrupted during sleep");
|
|
80
151
|
}
|
|
81
152
|
}
|
|
82
153
|
break;
|
|
154
|
+
case "aborted":
|
|
155
|
+
return {
|
|
156
|
+
state: "aborted" as const,
|
|
157
|
+
reason: result.reason,
|
|
158
|
+
stats: doneStats(stats),
|
|
159
|
+
};
|
|
83
160
|
case "success":
|
|
84
161
|
return { ...result, stats: doneStats(stats) };
|
|
85
162
|
case "error":
|
|
@@ -102,7 +179,8 @@ export async function poller<T>(
|
|
|
102
179
|
): Promise<PollerResult<T>> {
|
|
103
180
|
const options = {
|
|
104
181
|
intervalMs: 1000,
|
|
105
|
-
timeoutMs: 30000,
|
|
182
|
+
timeoutMs: 30000, // -1 means forever
|
|
183
|
+
actionTimeoutMs: -1, // forever
|
|
106
184
|
...ioptions,
|
|
107
185
|
exponentialBackoff:
|
|
108
186
|
typeof ioptions.exponentialBackoff === "boolean" ? ioptions.exponentialBackoff : ioptions.timeoutMs === FOREVER,
|
|
@@ -113,10 +191,31 @@ export async function poller<T>(
|
|
|
113
191
|
lastIntervalMs: 0,
|
|
114
192
|
totalElapsedMs: 0,
|
|
115
193
|
};
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
.
|
|
121
|
-
|
|
194
|
+
const abortController = new AbortController();
|
|
195
|
+
const toRemoveEventListeners: (() => void)[] = [];
|
|
196
|
+
if (options.abortSignal) {
|
|
197
|
+
if (options.abortSignal.aborted) {
|
|
198
|
+
abortController.abort(options.abortSignal.reason);
|
|
199
|
+
} else {
|
|
200
|
+
function dispatchAbort(): void {
|
|
201
|
+
abortController.abort(options.abortSignal?.reason);
|
|
202
|
+
}
|
|
203
|
+
toRemoveEventListeners.push(() => {
|
|
204
|
+
options.abortSignal?.removeEventListener("abort", dispatchAbort);
|
|
205
|
+
});
|
|
206
|
+
options.abortSignal.addEventListener("abort", dispatchAbort, { once: true });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const races = [interPoller(fn, options, stats, abortController)];
|
|
210
|
+
if (options.timeoutMs > 0) {
|
|
211
|
+
races.push(
|
|
212
|
+
sleep(options.timeoutMs, abortController.signal)
|
|
213
|
+
.then(() => ({ state: "timeout" as const, stats: doneStats(stats) }))
|
|
214
|
+
.catch((e: Error) => ({ state: "error" as const, error: e, stats: doneStats(stats) })),
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
return Promise.race(races).finally(() => {
|
|
218
|
+
toRemoveEventListeners.forEach((fn) => fn());
|
|
219
|
+
toRemoveEventListeners.length = 0;
|
|
220
|
+
});
|
|
122
221
|
}
|
package/src/timeouted.ts
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { Future } from "./future.js";
|
|
2
|
+
import { isPromise } from "./is-promise.js";
|
|
3
|
+
import { sleep } from "./utils/promise-sleep.js";
|
|
4
|
+
|
|
5
|
+
export interface PurTimeoutResultSuccess<T> {
|
|
6
|
+
readonly state: "success";
|
|
7
|
+
readonly value: T;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface PurTimeoutResultTimeout {
|
|
11
|
+
readonly state: "timeout";
|
|
12
|
+
}
|
|
13
|
+
export interface PurTimeoutResultAborted {
|
|
14
|
+
readonly state: "aborted";
|
|
15
|
+
readonly reason: unknown;
|
|
16
|
+
}
|
|
17
|
+
export interface PurTimeoutResultError {
|
|
18
|
+
readonly state: "error";
|
|
19
|
+
readonly error: Error;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface TimeoutState<CTX> {
|
|
23
|
+
readonly duration: number;
|
|
24
|
+
readonly ctx: CTX;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type PurTimeoutResult<T> = PurTimeoutResultSuccess<T> | PurTimeoutResultTimeout | PurTimeoutResultAborted | PurTimeoutResultError;
|
|
28
|
+
|
|
29
|
+
export type TimeoutResultSuccess<T, CTX = unknown> = PurTimeoutResultSuccess<T> & TimeoutState<CTX>;
|
|
30
|
+
export type TimeoutResultTimeout<CTX = unknown> = PurTimeoutResultTimeout & TimeoutState<CTX>;
|
|
31
|
+
export type TimeoutResultAborted<CTX = unknown> = PurTimeoutResultAborted & TimeoutState<CTX>;
|
|
32
|
+
export type TimeoutResultError<CTX = unknown> = PurTimeoutResultError & TimeoutState<CTX>;
|
|
33
|
+
|
|
34
|
+
export type TimeoutResult<T, CTX = unknown> =
|
|
35
|
+
| TimeoutResultSuccess<T, CTX>
|
|
36
|
+
| TimeoutResultTimeout<CTX>
|
|
37
|
+
| TimeoutResultAborted<CTX>
|
|
38
|
+
| TimeoutResultError<CTX>;
|
|
39
|
+
|
|
40
|
+
export type ActionFunc<T> = (controller: AbortController) => Promise<T>;
|
|
41
|
+
|
|
42
|
+
// Action item in arrays - can be function or promise
|
|
43
|
+
export type ActionItem<T> = ActionFunc<T> | Promise<T>;
|
|
44
|
+
|
|
45
|
+
// Main action type - function, promise, or mixed array
|
|
46
|
+
export type TimeoutAction<T> = ActionItem<T>;
|
|
47
|
+
|
|
48
|
+
// Configuration options
|
|
49
|
+
export interface TimeoutActionOptions<CTX> {
|
|
50
|
+
readonly timeout: number;
|
|
51
|
+
readonly signal: AbortSignal;
|
|
52
|
+
readonly controller: AbortController;
|
|
53
|
+
readonly ctx: CTX;
|
|
54
|
+
onTimeout: () => void;
|
|
55
|
+
onAbort: (reason: unknown) => void;
|
|
56
|
+
onError: (error: Error) => void;
|
|
57
|
+
onAbortAction: (reason: unknown) => void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// TYPE GUARDS
|
|
62
|
+
// ============================================================================
|
|
63
|
+
|
|
64
|
+
export function isSuccess<T>(result: TimeoutResult<T>): result is TimeoutResultSuccess<T> {
|
|
65
|
+
return result.state === "success";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function isTimeout<T>(result: TimeoutResult<T>): result is TimeoutResultTimeout<T> {
|
|
69
|
+
return result.state === "timeout";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function isAborted<T>(result: TimeoutResult<T>): result is TimeoutResultAborted<T> {
|
|
73
|
+
return result.state === "aborted";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function isError<T>(result: TimeoutResult<T>): result is TimeoutResultError<T> {
|
|
77
|
+
return result.state === "error";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function unwrap<T>(result: TimeoutResult<T>): T {
|
|
81
|
+
if (isSuccess(result)) {
|
|
82
|
+
return result.value;
|
|
83
|
+
}
|
|
84
|
+
throw new Error(`TimeoutResult is not success: ${result.state}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function unwrapOr<T>(result: TimeoutResult<T>, defaultValue: T): T {
|
|
88
|
+
return isSuccess(result) ? result.value : defaultValue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function timeouted<T, CTX = void>(
|
|
92
|
+
action: TimeoutAction<T>,
|
|
93
|
+
options: Partial<TimeoutActionOptions<CTX>> = {},
|
|
94
|
+
): Promise<TimeoutResult<T>> {
|
|
95
|
+
const { timeout = 30000, signal, controller: externalController, ctx, onTimeout, onAbort, onError, onAbortAction } = options;
|
|
96
|
+
|
|
97
|
+
const controller = externalController || new AbortController();
|
|
98
|
+
const startTime = Date.now();
|
|
99
|
+
|
|
100
|
+
const toRemoveEventListeners: (() => void)[] = [];
|
|
101
|
+
|
|
102
|
+
// Link external signal to internal controller
|
|
103
|
+
if (signal) {
|
|
104
|
+
function abortAction(): void {
|
|
105
|
+
controller.abort(signal?.reason);
|
|
106
|
+
}
|
|
107
|
+
toRemoveEventListeners.push(() => {
|
|
108
|
+
signal?.removeEventListener("abort", abortAction);
|
|
109
|
+
});
|
|
110
|
+
signal.addEventListener("abort", abortAction);
|
|
111
|
+
}
|
|
112
|
+
function cleanup<X extends PurTimeoutResult<T>>(result: X, skipCleanupAction = false): TimeoutResult<T, CTX> {
|
|
113
|
+
for (const evtFn of toRemoveEventListeners) {
|
|
114
|
+
evtFn();
|
|
115
|
+
}
|
|
116
|
+
toRemoveEventListeners.length = 0;
|
|
117
|
+
if (!skipCleanupAction) {
|
|
118
|
+
void onAbortAction?.(signal?.reason);
|
|
119
|
+
controller.abort(new Error("Timeouted Abort Action"));
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
...result,
|
|
123
|
+
duration: Date.now() - startTime,
|
|
124
|
+
ctx: ctx as CTX,
|
|
125
|
+
} as TimeoutResult<T, CTX>;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let toAwait: Promise<T>;
|
|
129
|
+
if (isPromise(action)) {
|
|
130
|
+
toAwait = action;
|
|
131
|
+
} else {
|
|
132
|
+
try {
|
|
133
|
+
toAwait = action(controller);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
return cleanup({
|
|
136
|
+
state: "error",
|
|
137
|
+
error: error instanceof Error ? error : new Error(error as string),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const abortToAwait = new Future<PurTimeoutResultAborted>();
|
|
143
|
+
function onAbortHandler(): void {
|
|
144
|
+
abortToAwait.resolve({ state: "aborted" as const, reason: controller.signal.reason as unknown });
|
|
145
|
+
}
|
|
146
|
+
controller.signal.addEventListener("abort", onAbortHandler);
|
|
147
|
+
toRemoveEventListeners.push(() => {
|
|
148
|
+
controller.signal.removeEventListener("abort", onAbortHandler);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const toRace: Promise<PurTimeoutResult<T>>[] = [
|
|
152
|
+
toAwait.then((value) => ({ state: "success" as const, value })).catch((error: Error) => ({ state: "error" as const, error })),
|
|
153
|
+
abortToAwait.asPromise(),
|
|
154
|
+
];
|
|
155
|
+
if (timeout > 0) {
|
|
156
|
+
toRace.push(
|
|
157
|
+
sleep(timeout, controller.signal).then((r): PurTimeoutResult<T> => {
|
|
158
|
+
switch (true) {
|
|
159
|
+
case r.isOk:
|
|
160
|
+
return { state: "timeout" as const };
|
|
161
|
+
case r.isErr:
|
|
162
|
+
return { state: "error" as const, error: r.error };
|
|
163
|
+
case r.isAborted:
|
|
164
|
+
return { state: "aborted" as const, reason: r.reason };
|
|
165
|
+
}
|
|
166
|
+
throw new Error("Unreachable code in timeoutAction");
|
|
167
|
+
}),
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const res = await Promise.race(toRace);
|
|
172
|
+
switch (true) {
|
|
173
|
+
case res.state === "success":
|
|
174
|
+
return cleanup(res, true);
|
|
175
|
+
case res.state === "aborted":
|
|
176
|
+
onAbort?.(res.reason);
|
|
177
|
+
return cleanup(res);
|
|
178
|
+
case res.state === "error":
|
|
179
|
+
onError?.(res.error);
|
|
180
|
+
return cleanup(res);
|
|
181
|
+
case res.state === "timeout":
|
|
182
|
+
onTimeout?.();
|
|
183
|
+
return cleanup(res);
|
|
184
|
+
}
|
|
185
|
+
throw new Error("Unreachable code in timeoutAction");
|
|
186
|
+
}
|