@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.
Files changed (131) hide show
  1. package/cjs/future.cjs +2 -0
  2. package/cjs/future.cjs.map +1 -1
  3. package/cjs/future.d.ts +1 -0
  4. package/cjs/future.d.ts.map +1 -1
  5. package/cjs/future.test.cjs +5 -0
  6. package/cjs/future.test.cjs.map +1 -1
  7. package/cjs/index.cjs +1 -0
  8. package/cjs/index.cjs.map +1 -1
  9. package/cjs/index.d.ts +1 -0
  10. package/cjs/index.d.ts.map +1 -1
  11. package/cjs/poller.cjs +87 -16
  12. package/cjs/poller.cjs.map +1 -1
  13. package/cjs/poller.d.ts +20 -10
  14. package/cjs/poller.d.ts.map +1 -1
  15. package/cjs/poller.test.cjs +2 -2
  16. package/cjs/poller.test.cjs.map +1 -1
  17. package/cjs/timeouted.cjs +119 -0
  18. package/cjs/timeouted.cjs.map +1 -0
  19. package/cjs/timeouted.d.ts +45 -0
  20. package/cjs/timeouted.d.ts.map +1 -0
  21. package/cjs/timeouted.test.cjs +581 -0
  22. package/cjs/timeouted.test.cjs.map +1 -0
  23. package/cjs/timeouted.test.d.ts +2 -0
  24. package/cjs/timeouted.test.d.ts.map +1 -0
  25. package/cjs/utils/promise-sleep.cjs +27 -26
  26. package/cjs/utils/promise-sleep.cjs.map +1 -1
  27. package/cjs/utils/promise-sleep.d.ts +28 -2
  28. package/cjs/utils/promise-sleep.d.ts.map +1 -1
  29. package/cjs/utils/promise-sleep.test.cjs +13 -13
  30. package/cjs/utils/promise-sleep.test.cjs.map +1 -1
  31. package/cjs/version.cjs +1 -1
  32. package/deno.json +1 -1
  33. package/esm/future.d.ts +1 -0
  34. package/esm/future.d.ts.map +1 -1
  35. package/esm/future.js +2 -0
  36. package/esm/future.js.map +1 -1
  37. package/esm/future.test.js +5 -0
  38. package/esm/future.test.js.map +1 -1
  39. package/esm/index.d.ts +1 -0
  40. package/esm/index.d.ts.map +1 -1
  41. package/esm/index.js +1 -0
  42. package/esm/index.js.map +1 -1
  43. package/esm/poller.d.ts +20 -10
  44. package/esm/poller.d.ts.map +1 -1
  45. package/esm/poller.js +87 -16
  46. package/esm/poller.js.map +1 -1
  47. package/esm/poller.test.js +2 -2
  48. package/esm/poller.test.js.map +1 -1
  49. package/esm/timeouted.d.ts +45 -0
  50. package/esm/timeouted.d.ts.map +1 -0
  51. package/esm/timeouted.js +110 -0
  52. package/esm/timeouted.js.map +1 -0
  53. package/esm/timeouted.test.d.ts +2 -0
  54. package/esm/timeouted.test.d.ts.map +1 -0
  55. package/esm/timeouted.test.js +579 -0
  56. package/esm/timeouted.test.js.map +1 -0
  57. package/esm/utils/promise-sleep.d.ts +28 -2
  58. package/esm/utils/promise-sleep.d.ts.map +1 -1
  59. package/esm/utils/promise-sleep.js +27 -26
  60. package/esm/utils/promise-sleep.js.map +1 -1
  61. package/esm/utils/promise-sleep.test.js +13 -13
  62. package/esm/utils/promise-sleep.test.js.map +1 -1
  63. package/esm/version.js +1 -1
  64. package/package.json +1 -1
  65. package/src/future.ts +5 -0
  66. package/src/index.ts +1 -0
  67. package/src/poller.ts +128 -29
  68. package/src/timeouted.ts +186 -0
  69. package/src/utils/promise-sleep.ts +61 -31
  70. package/ts/cjs/future.d.ts +1 -0
  71. package/ts/cjs/future.d.ts.map +1 -1
  72. package/ts/cjs/future.js +2 -0
  73. package/ts/cjs/future.js.map +1 -1
  74. package/ts/cjs/future.test.js +5 -0
  75. package/ts/cjs/future.test.js.map +1 -1
  76. package/ts/cjs/index.d.ts +1 -0
  77. package/ts/cjs/index.d.ts.map +1 -1
  78. package/ts/cjs/index.js +1 -0
  79. package/ts/cjs/index.js.map +1 -1
  80. package/ts/cjs/poller.d.ts +20 -10
  81. package/ts/cjs/poller.d.ts.map +1 -1
  82. package/ts/cjs/poller.js +87 -16
  83. package/ts/cjs/poller.js.map +1 -1
  84. package/ts/cjs/poller.test.js +2 -2
  85. package/ts/cjs/poller.test.js.map +1 -1
  86. package/ts/cjs/timeouted.d.ts +45 -0
  87. package/ts/cjs/timeouted.d.ts.map +1 -0
  88. package/ts/cjs/timeouted.js +119 -0
  89. package/ts/cjs/timeouted.js.map +1 -0
  90. package/ts/cjs/timeouted.test.d.ts +2 -0
  91. package/ts/cjs/timeouted.test.d.ts.map +1 -0
  92. package/ts/cjs/timeouted.test.js +581 -0
  93. package/ts/cjs/timeouted.test.js.map +1 -0
  94. package/ts/cjs/utils/promise-sleep.d.ts +28 -2
  95. package/ts/cjs/utils/promise-sleep.d.ts.map +1 -1
  96. package/ts/cjs/utils/promise-sleep.js +27 -26
  97. package/ts/cjs/utils/promise-sleep.js.map +1 -1
  98. package/ts/cjs/utils/promise-sleep.test.js +13 -13
  99. package/ts/cjs/utils/promise-sleep.test.js.map +1 -1
  100. package/ts/cjs/version.js +1 -1
  101. package/ts/esm/future.d.ts +1 -0
  102. package/ts/esm/future.d.ts.map +1 -1
  103. package/ts/esm/future.js +2 -0
  104. package/ts/esm/future.js.map +1 -1
  105. package/ts/esm/future.test.js +5 -0
  106. package/ts/esm/future.test.js.map +1 -1
  107. package/ts/esm/index.d.ts +1 -0
  108. package/ts/esm/index.d.ts.map +1 -1
  109. package/ts/esm/index.js +1 -0
  110. package/ts/esm/index.js.map +1 -1
  111. package/ts/esm/poller.d.ts +20 -10
  112. package/ts/esm/poller.d.ts.map +1 -1
  113. package/ts/esm/poller.js +87 -16
  114. package/ts/esm/poller.js.map +1 -1
  115. package/ts/esm/poller.test.js +2 -2
  116. package/ts/esm/poller.test.js.map +1 -1
  117. package/ts/esm/timeouted.d.ts +45 -0
  118. package/ts/esm/timeouted.d.ts.map +1 -0
  119. package/ts/esm/timeouted.js +110 -0
  120. package/ts/esm/timeouted.js.map +1 -0
  121. package/ts/esm/timeouted.test.d.ts +2 -0
  122. package/ts/esm/timeouted.test.d.ts.map +1 -0
  123. package/ts/esm/timeouted.test.js +579 -0
  124. package/ts/esm/timeouted.test.js.map +1 -0
  125. package/ts/esm/utils/promise-sleep.d.ts +28 -2
  126. package/ts/esm/utils/promise-sleep.d.ts.map +1 -1
  127. package/ts/esm/utils/promise-sleep.js +27 -26
  128. package/ts/esm/utils/promise-sleep.js.map +1 -1
  129. package/ts/esm/utils/promise-sleep.test.js +13 -13
  130. package/ts/esm/utils/promise-sleep.test.js.map +1 -1
  131. package/ts/esm/version.js +1 -1
@@ -1,3 +1,29 @@
1
- import { Result } from "../result.js";
2
- export declare function sleep(ms: number, signal?: AbortSignal): Promise<Result<void>>;
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":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAUtC,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAgC7E"}
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 { Result } from "../result.js";
2
- export function sleep(ms, signal) {
3
- return new Promise((resolve) => {
4
- if (ms <= 0) {
5
- resolve(Result.Ok());
6
- return;
7
- }
8
- if (signal?.aborted) {
9
- const err = new Error("sleep aborted");
10
- resolve(Result.Err(err));
11
- return;
12
- }
13
- function cleanup() {
14
- clearTimeout(id);
15
- signal?.removeEventListener("abort", onAbort);
16
- }
17
- const id = setTimeout(() => {
18
- cleanup();
19
- resolve(Result.Ok());
20
- }, ms);
21
- const onAbort = () => {
22
- cleanup();
23
- const err = new Error("sleep aborted");
24
- resolve(Result.Err(err));
25
- };
26
- signal?.addEventListener("abort", onAbort, { once: true });
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;AAUtC,MAAM,UAAU,KAAK,CAAC,EAAU,EAAE,MAAoB,EAAyB;IAC7E,OAAO,IAAI,OAAO,CAAe,CAAC,OAAO,EAAE,EAAE,CAAC;QAC5C,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YACZ,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACpB,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;YAEvC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;YACzB,OAAO;QACT,CAAC;QAED,SAAS,OAAO,GAAS;YACvB,YAAY,CAAC,EAAE,CAAC,CAAC;YACjB,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAAA,CAC/C;QAED,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC;YAC1B,OAAO,EAAE,CAAC;YACV,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;QAAA,CACtB,EAAE,EAAE,CAAC,CAAC;QAEP,MAAM,OAAO,GAAG,GAAS,EAAE,CAAC;YAC1B,OAAO,EAAE,CAAC;YACV,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;YAEvC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QAAA,CAC1B,CAAC;QAEF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAAA,CAC5D,CAAC,CAAC;AAAA,CACJ"}
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()).toBe(true);
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()).toBe(true);
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()).toBe(true);
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()).toBe(false);
30
- expect(result.Err().message).toBe("sleep aborted");
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()).toBe(false);
42
- expect(result.Err().message).toBe("sleep aborted");
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()).toBe(true);
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()).toBe(false);
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()).toBe(true);
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":"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"}
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.59";
1
+ export const VERSION = "0.4.61";
2
2
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adviser/cement",
3
- "version": "0.4.59",
3
+ "version": "0.4.61",
4
4
  "description": "better try/catch/finally handling",
5
5
  "main": "./cjs/index.js",
6
6
  "type": "module",
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
@@ -30,6 +30,7 @@ export * from "./load-asset.js";
30
30
  export * from "./mutable-url.js";
31
31
  export * from "./poller.js";
32
32
  export * from "./on-func.js";
33
+ export * from "./timeouted.js";
33
34
 
34
35
  // ugly
35
36
  export * as utils from "./utils/index.js";
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
- export interface PollWaitingActionResult {
11
- readonly state: "waiting";
11
+ interface PollWithStats {
12
12
  readonly stats: PollerStats;
13
13
  }
14
14
 
15
- export interface PollSuccessActionResult<T> {
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 interface PollErrorActionResult {
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 interface PollTimeoutActionResult {
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
- | Omit<PollWaitingActionResult, "stats">
34
- | Omit<PollSuccessActionResult<T>, "stats">
35
- | Omit<PollErrorActionResult, "stats">;
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
- result = await fn(options.abortSignal);
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 err = await sleep(options.intervalMs, options.abortSignal);
74
- if (err.isErr()) {
75
- return {
76
- state: "error",
77
- error: err.Err(),
78
- stats: doneStats(stats),
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
- return Promise.race([
117
- interPoller(fn, options, stats),
118
- sleep(options.timeoutMs, options.abortSignal)
119
- .then(() => ({ state: "timeout" as const, stats: doneStats(stats) }))
120
- .catch((e: Error) => ({ state: "error" as const, error: e, stats: doneStats(stats) })),
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
  }
@@ -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
+ }