actly 1.0.2 → 1.1.5

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 (70) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +612 -0
  3. package/dist/core/act.cjs +187 -37
  4. package/dist/core/act.d.ts +81 -10
  5. package/dist/core/act.d.ts.map +1 -1
  6. package/dist/core/act.js +184 -36
  7. package/dist/core/act.js.map +1 -1
  8. package/dist/core/executor.cjs +30 -4
  9. package/dist/core/executor.d.ts +33 -11
  10. package/dist/core/executor.d.ts.map +1 -1
  11. package/dist/core/executor.js +29 -4
  12. package/dist/core/executor.js.map +1 -1
  13. package/dist/index.cjs +15 -5
  14. package/dist/index.d.ts +9 -4
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +9 -5
  17. package/dist/index.js.map +1 -1
  18. package/dist/policies/cache.cjs +91 -7
  19. package/dist/policies/cache.d.ts +30 -3
  20. package/dist/policies/cache.d.ts.map +1 -1
  21. package/dist/policies/cache.js +91 -7
  22. package/dist/policies/cache.js.map +1 -1
  23. package/dist/policies/dedupe.cjs +79 -16
  24. package/dist/policies/dedupe.d.ts +39 -7
  25. package/dist/policies/dedupe.d.ts.map +1 -1
  26. package/dist/policies/dedupe.js +79 -16
  27. package/dist/policies/dedupe.js.map +1 -1
  28. package/dist/policies/retry.cjs +65 -25
  29. package/dist/policies/retry.d.ts +25 -2
  30. package/dist/policies/retry.d.ts.map +1 -1
  31. package/dist/policies/retry.js +65 -25
  32. package/dist/policies/retry.js.map +1 -1
  33. package/dist/policies/timeout.cjs +87 -17
  34. package/dist/policies/timeout.d.ts +28 -8
  35. package/dist/policies/timeout.d.ts.map +1 -1
  36. package/dist/policies/timeout.js +87 -17
  37. package/dist/policies/timeout.js.map +1 -1
  38. package/dist/state/store.cjs +11 -38
  39. package/dist/state/store.d.ts +10 -12
  40. package/dist/state/store.d.ts.map +1 -1
  41. package/dist/state/store.js +10 -37
  42. package/dist/state/store.js.map +1 -1
  43. package/dist/stores/base.cjs +20 -0
  44. package/dist/stores/base.d.ts +88 -0
  45. package/dist/stores/base.d.ts.map +1 -0
  46. package/dist/stores/base.js +17 -0
  47. package/dist/stores/base.js.map +1 -0
  48. package/dist/stores/memory.cjs +140 -0
  49. package/dist/stores/memory.d.ts +79 -0
  50. package/dist/stores/memory.d.ts.map +1 -0
  51. package/dist/stores/memory.js +137 -0
  52. package/dist/stores/memory.js.map +1 -0
  53. package/dist/types/index.d.ts +151 -34
  54. package/dist/types/index.d.ts.map +1 -1
  55. package/dist/utils/abort.cjs +142 -0
  56. package/dist/utils/abort.d.ts +55 -0
  57. package/dist/utils/abort.d.ts.map +1 -0
  58. package/dist/utils/abort.js +136 -0
  59. package/dist/utils/abort.js.map +1 -0
  60. package/dist/utils/backoff.cjs +43 -0
  61. package/dist/utils/backoff.d.ts +14 -0
  62. package/dist/utils/backoff.d.ts.map +1 -0
  63. package/dist/utils/backoff.js +41 -0
  64. package/dist/utils/backoff.js.map +1 -0
  65. package/dist/utils/validate.cjs +79 -0
  66. package/dist/utils/validate.d.ts +15 -0
  67. package/dist/utils/validate.d.ts.map +1 -0
  68. package/dist/utils/validate.js +72 -0
  69. package/dist/utils/validate.js.map +1 -0
  70. package/package.json +19 -37
@@ -3,8 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TotalTimeoutError = exports.TimeoutError = void 0;
4
4
  exports.timeoutPolicy = timeoutPolicy;
5
5
  exports.totalTimeoutPolicy = totalTimeoutPolicy;
6
+ const abort_js_1 = require("../utils/abort.js");
6
7
  // ─── Errors ───────────────────────────────────────────────────────────────────
8
+ /**
9
+ * Thrown when a per-attempt `timeout` deadline fires.
10
+ *
11
+ * Carries the configured `ms` so callers can log/alert precisely:
12
+ *
13
+ * ```ts
14
+ * if (!result.ok && result.error instanceof TimeoutError) {
15
+ * console.log(`attempt timed out after ${result.error.ms}ms`)
16
+ * }
17
+ * ```
18
+ */
7
19
  class TimeoutError extends Error {
20
+ ms;
8
21
  constructor(ms) {
9
22
  super(`ACT timed out after ${ms}ms`);
10
23
  this.name = 'TimeoutError';
@@ -13,11 +26,21 @@ class TimeoutError extends Error {
13
26
  }
14
27
  exports.TimeoutError = TimeoutError;
15
28
  /**
16
- * Thrown when the total budget across all attempts is exceeded.
17
- * Distinct from TimeoutError (per-attempt) so callers can instanceof-check
18
- * which deadline fired.
29
+ * Thrown when the operation-wide `totalTimeout` budget fires.
30
+ *
31
+ * Distinct from `TimeoutError` (per-attempt) so callers can `instanceof`-check
32
+ * which deadline fired:
33
+ *
34
+ * ```ts
35
+ * if (result.error instanceof TotalTimeoutError) {
36
+ * // whole operation budget exhausted
37
+ * } else if (result.error instanceof TimeoutError) {
38
+ * // last attempt's per-attempt deadline fired
39
+ * }
40
+ * ```
19
41
  */
20
42
  class TotalTimeoutError extends Error {
43
+ ms;
21
44
  constructor(ms) {
22
45
  super(`ACT total timeout exceeded after ${ms}ms`);
23
46
  this.name = 'TotalTimeoutError';
@@ -25,29 +48,76 @@ class TotalTimeoutError extends Error {
25
48
  }
26
49
  }
27
50
  exports.TotalTimeoutError = TotalTimeoutError;
28
- // ─── Helpers ──────────────────────────────────────────────────────────────────
29
- /** Shared race-against-a-timer logic. ErrorCtor lets callers pick the error type. */
51
+ // ─── Policy ───────────────────────────────────────────────────────────────────
52
+ /**
53
+ * Build a timeout policy that throws `ErrorCtor` on deadline.
54
+ *
55
+ * # Cancellation contract
56
+ *
57
+ * Each invocation:
58
+ * 1. Creates a fresh `AbortController` for this attempt.
59
+ * 2. Arms a `setTimeout` that aborts the controller with a fresh `ErrorCtor(ms)`.
60
+ * 3. Links the parent signal: if the parent aborts (e.g. `totalTimeout` or
61
+ * caller cancellation), the child aborts with the parent's reason.
62
+ * 4. Races `fn(childSignal)` against the abort event.
63
+ *
64
+ * The race is critical: it ensures `act()` returns promptly even if `fn`
65
+ * ignores the signal. The underlying `fn` may keep running in the background
66
+ * (resource leak), but the caller is unblocked. This is the best JavaScript
67
+ * can do without cooperation from `fn`.
68
+ *
69
+ * If `fn` cooperates (passes `signal` to `fetch`, `AbortController`, etc.),
70
+ * the underlying work is cancelled properly — no leak.
71
+ *
72
+ * # Error attribution
73
+ *
74
+ * If the per-attempt timer fires, we throw `ErrorCtor(ms)` regardless of
75
+ * what `fn` does. If the parent signal fires first, we throw the parent's
76
+ * reason (could be `TotalTimeoutError`, an `AbortError`, or anything else).
77
+ */
30
78
  function makeTimeoutPolicy(opts, ErrorCtor) {
31
- return (fn, _ctx) => () => new Promise((resolve, reject) => {
32
- const timer = setTimeout(() => reject(new ErrorCtor(opts.ms)), opts.ms);
33
- fn()
34
- .then((v) => { clearTimeout(timer); resolve(v); })
35
- .catch((e) => { clearTimeout(timer); reject(e); });
36
- });
79
+ return (fn, _ctx) => async (parentSignal) => {
80
+ const controller = new AbortController();
81
+ const timerError = new ErrorCtor(opts.ms);
82
+ // Arm the per-attempt timer. The error object is allocated once so the
83
+ // stack trace points here (the policy frame), not at setTimeout's
84
+ // internal callback.
85
+ const timer = setTimeout(() => controller.abort(timerError), opts.ms);
86
+ // Link parent -> child. If parent is already aborted, child aborts
87
+ // synchronously with parent's reason.
88
+ (0, abort_js_1.linkSignal)(parentSignal, controller);
89
+ try {
90
+ // Race fn against the abort event. If fn settles first, we get its
91
+ // result/error. If the signal aborts first, we reject with reason.
92
+ //
93
+ // We do NOT use AbortSignal.timeout() here because we need to throw
94
+ // our own ErrorCtor, not a DOMException named "TimeoutError".
95
+ return await new Promise((resolve, reject) => {
96
+ if (controller.signal.aborted) {
97
+ reject(controller.signal.reason);
98
+ return;
99
+ }
100
+ controller.signal.addEventListener('abort', () => reject(controller.signal.reason), { once: true });
101
+ Promise.resolve(fn(controller.signal)).then((value) => resolve(value), (error) => reject(error));
102
+ });
103
+ }
104
+ finally {
105
+ clearTimeout(timer);
106
+ }
107
+ };
37
108
  }
38
- // ─── Policies ─────────────────────────────────────────────────────────────────
39
109
  /**
40
- * Races fn against a hard deadline.
41
- * Rejects with TimeoutError if the deadline fires first.
110
+ * Per-attempt timeout. Races `fn` against a deadline that resets on retry.
42
111
  *
43
- * Place this INSIDE retryPolicy (closer to fn) so each attempt has its own clock.
112
+ * Place this INSIDE `retryPolicy` (closer to `fn`) so each attempt has its
113
+ * own clock.
44
114
  */
45
115
  function timeoutPolicy(opts) {
46
116
  return makeTimeoutPolicy(opts, TimeoutError);
47
117
  }
48
118
  /**
49
- * Races the ENTIRE operation (all retry attempts + delays) against a budget.
50
- * Rejects with TotalTimeoutError if the budget is exhausted.
119
+ * Operation-wide timeout. Races the ENTIRE chain (all retry attempts +
120
+ * delays) against a hard budget that does NOT reset.
51
121
  *
52
122
  * Place this as the OUTERMOST policy so the clock starts before any other
53
123
  * policy runs and stops regardless of what the inner chain is doing.
@@ -1,27 +1,47 @@
1
1
  import type { PolicyApplier, TimeoutOptions } from '../types/index.js';
2
+ /**
3
+ * Thrown when a per-attempt `timeout` deadline fires.
4
+ *
5
+ * Carries the configured `ms` so callers can log/alert precisely:
6
+ *
7
+ * ```ts
8
+ * if (!result.ok && result.error instanceof TimeoutError) {
9
+ * console.log(`attempt timed out after ${result.error.ms}ms`)
10
+ * }
11
+ * ```
12
+ */
2
13
  export declare class TimeoutError extends Error {
3
14
  readonly ms: number;
4
15
  constructor(ms: number);
5
16
  }
6
17
  /**
7
- * Thrown when the total budget across all attempts is exceeded.
8
- * Distinct from TimeoutError (per-attempt) so callers can instanceof-check
9
- * which deadline fired.
18
+ * Thrown when the operation-wide `totalTimeout` budget fires.
19
+ *
20
+ * Distinct from `TimeoutError` (per-attempt) so callers can `instanceof`-check
21
+ * which deadline fired:
22
+ *
23
+ * ```ts
24
+ * if (result.error instanceof TotalTimeoutError) {
25
+ * // whole operation budget exhausted
26
+ * } else if (result.error instanceof TimeoutError) {
27
+ * // last attempt's per-attempt deadline fired
28
+ * }
29
+ * ```
10
30
  */
11
31
  export declare class TotalTimeoutError extends Error {
12
32
  readonly ms: number;
13
33
  constructor(ms: number);
14
34
  }
15
35
  /**
16
- * Races fn against a hard deadline.
17
- * Rejects with TimeoutError if the deadline fires first.
36
+ * Per-attempt timeout. Races `fn` against a deadline that resets on retry.
18
37
  *
19
- * Place this INSIDE retryPolicy (closer to fn) so each attempt has its own clock.
38
+ * Place this INSIDE `retryPolicy` (closer to `fn`) so each attempt has its
39
+ * own clock.
20
40
  */
21
41
  export declare function timeoutPolicy<T>(opts: TimeoutOptions): PolicyApplier<T>;
22
42
  /**
23
- * Races the ENTIRE operation (all retry attempts + delays) against a budget.
24
- * Rejects with TotalTimeoutError if the budget is exhausted.
43
+ * Operation-wide timeout. Races the ENTIRE chain (all retry attempts +
44
+ * delays) against a hard budget that does NOT reset.
25
45
  *
26
46
  * Place this as the OUTERMOST policy so the clock starts before any other
27
47
  * policy runs and stops regardless of what the inner chain is doing.
@@ -1 +1 @@
1
- {"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../../src/policies/timeout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,aAAa,EAAiB,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAI5F,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;gBAEP,EAAE,EAAE,MAAM;CAKvB;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;gBAEP,EAAE,EAAE,MAAM;CAKvB;AAyBD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,GAAG,aAAa,CAAC,CAAC,CAAC,CAEvE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,GAAG,aAAa,CAAC,CAAC,CAAC,CAE5E"}
1
+ {"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../../src/policies/timeout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,aAAa,EAAiB,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAK5F;;;;;;;;;;GAUG;AACH,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;gBAEP,EAAE,EAAE,MAAM;CAKvB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;gBAEP,EAAE,EAAE,MAAM;CAKvB;AAgFD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,GAAG,aAAa,CAAC,CAAC,CAAC,CAEvE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,GAAG,aAAa,CAAC,CAAC,CAAC,CAE5E"}
@@ -1,5 +1,18 @@
1
+ import { linkSignal } from '../utils/abort.js';
1
2
  // ─── Errors ───────────────────────────────────────────────────────────────────
3
+ /**
4
+ * Thrown when a per-attempt `timeout` deadline fires.
5
+ *
6
+ * Carries the configured `ms` so callers can log/alert precisely:
7
+ *
8
+ * ```ts
9
+ * if (!result.ok && result.error instanceof TimeoutError) {
10
+ * console.log(`attempt timed out after ${result.error.ms}ms`)
11
+ * }
12
+ * ```
13
+ */
2
14
  export class TimeoutError extends Error {
15
+ ms;
3
16
  constructor(ms) {
4
17
  super(`ACT timed out after ${ms}ms`);
5
18
  this.name = 'TimeoutError';
@@ -7,40 +20,97 @@ export class TimeoutError extends Error {
7
20
  }
8
21
  }
9
22
  /**
10
- * Thrown when the total budget across all attempts is exceeded.
11
- * Distinct from TimeoutError (per-attempt) so callers can instanceof-check
12
- * which deadline fired.
23
+ * Thrown when the operation-wide `totalTimeout` budget fires.
24
+ *
25
+ * Distinct from `TimeoutError` (per-attempt) so callers can `instanceof`-check
26
+ * which deadline fired:
27
+ *
28
+ * ```ts
29
+ * if (result.error instanceof TotalTimeoutError) {
30
+ * // whole operation budget exhausted
31
+ * } else if (result.error instanceof TimeoutError) {
32
+ * // last attempt's per-attempt deadline fired
33
+ * }
34
+ * ```
13
35
  */
14
36
  export class TotalTimeoutError extends Error {
37
+ ms;
15
38
  constructor(ms) {
16
39
  super(`ACT total timeout exceeded after ${ms}ms`);
17
40
  this.name = 'TotalTimeoutError';
18
41
  this.ms = ms;
19
42
  }
20
43
  }
21
- // ─── Helpers ──────────────────────────────────────────────────────────────────
22
- /** Shared race-against-a-timer logic. ErrorCtor lets callers pick the error type. */
44
+ // ─── Policy ───────────────────────────────────────────────────────────────────
45
+ /**
46
+ * Build a timeout policy that throws `ErrorCtor` on deadline.
47
+ *
48
+ * # Cancellation contract
49
+ *
50
+ * Each invocation:
51
+ * 1. Creates a fresh `AbortController` for this attempt.
52
+ * 2. Arms a `setTimeout` that aborts the controller with a fresh `ErrorCtor(ms)`.
53
+ * 3. Links the parent signal: if the parent aborts (e.g. `totalTimeout` or
54
+ * caller cancellation), the child aborts with the parent's reason.
55
+ * 4. Races `fn(childSignal)` against the abort event.
56
+ *
57
+ * The race is critical: it ensures `act()` returns promptly even if `fn`
58
+ * ignores the signal. The underlying `fn` may keep running in the background
59
+ * (resource leak), but the caller is unblocked. This is the best JavaScript
60
+ * can do without cooperation from `fn`.
61
+ *
62
+ * If `fn` cooperates (passes `signal` to `fetch`, `AbortController`, etc.),
63
+ * the underlying work is cancelled properly — no leak.
64
+ *
65
+ * # Error attribution
66
+ *
67
+ * If the per-attempt timer fires, we throw `ErrorCtor(ms)` regardless of
68
+ * what `fn` does. If the parent signal fires first, we throw the parent's
69
+ * reason (could be `TotalTimeoutError`, an `AbortError`, or anything else).
70
+ */
23
71
  function makeTimeoutPolicy(opts, ErrorCtor) {
24
- return (fn, _ctx) => () => new Promise((resolve, reject) => {
25
- const timer = setTimeout(() => reject(new ErrorCtor(opts.ms)), opts.ms);
26
- fn()
27
- .then((v) => { clearTimeout(timer); resolve(v); })
28
- .catch((e) => { clearTimeout(timer); reject(e); });
29
- });
72
+ return (fn, _ctx) => async (parentSignal) => {
73
+ const controller = new AbortController();
74
+ const timerError = new ErrorCtor(opts.ms);
75
+ // Arm the per-attempt timer. The error object is allocated once so the
76
+ // stack trace points here (the policy frame), not at setTimeout's
77
+ // internal callback.
78
+ const timer = setTimeout(() => controller.abort(timerError), opts.ms);
79
+ // Link parent -> child. If parent is already aborted, child aborts
80
+ // synchronously with parent's reason.
81
+ linkSignal(parentSignal, controller);
82
+ try {
83
+ // Race fn against the abort event. If fn settles first, we get its
84
+ // result/error. If the signal aborts first, we reject with reason.
85
+ //
86
+ // We do NOT use AbortSignal.timeout() here because we need to throw
87
+ // our own ErrorCtor, not a DOMException named "TimeoutError".
88
+ return await new Promise((resolve, reject) => {
89
+ if (controller.signal.aborted) {
90
+ reject(controller.signal.reason);
91
+ return;
92
+ }
93
+ controller.signal.addEventListener('abort', () => reject(controller.signal.reason), { once: true });
94
+ Promise.resolve(fn(controller.signal)).then((value) => resolve(value), (error) => reject(error));
95
+ });
96
+ }
97
+ finally {
98
+ clearTimeout(timer);
99
+ }
100
+ };
30
101
  }
31
- // ─── Policies ─────────────────────────────────────────────────────────────────
32
102
  /**
33
- * Races fn against a hard deadline.
34
- * Rejects with TimeoutError if the deadline fires first.
103
+ * Per-attempt timeout. Races `fn` against a deadline that resets on retry.
35
104
  *
36
- * Place this INSIDE retryPolicy (closer to fn) so each attempt has its own clock.
105
+ * Place this INSIDE `retryPolicy` (closer to `fn`) so each attempt has its
106
+ * own clock.
37
107
  */
38
108
  export function timeoutPolicy(opts) {
39
109
  return makeTimeoutPolicy(opts, TimeoutError);
40
110
  }
41
111
  /**
42
- * Races the ENTIRE operation (all retry attempts + delays) against a budget.
43
- * Rejects with TotalTimeoutError if the budget is exhausted.
112
+ * Operation-wide timeout. Races the ENTIRE chain (all retry attempts +
113
+ * delays) against a hard budget that does NOT reset.
44
114
  *
45
115
  * Place this as the OUTERMOST policy so the clock starts before any other
46
116
  * policy runs and stops regardless of what the inner chain is doing.
@@ -1 +1 @@
1
- {"version":3,"file":"timeout.js","sourceRoot":"","sources":["../../src/policies/timeout.ts"],"names":[],"mappings":"AAEA,iFAAiF;AAEjF,MAAM,OAAO,YAAa,SAAQ,KAAK;IAGrC,YAAY,EAAU;QACpB,KAAK,CAAC,uBAAuB,EAAE,IAAI,CAAC,CAAA;QACpC,IAAI,CAAC,IAAI,GAAG,cAAc,CAAA;QAC1B,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;IACd,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAG1C,YAAY,EAAU;QACpB,KAAK,CAAC,oCAAoC,EAAE,IAAI,CAAC,CAAA;QACjD,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAA;QAC/B,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;IACd,CAAC;CACF;AAED,iFAAiF;AAEjF,qFAAqF;AACrF,SAAS,iBAAiB,CACxB,IAAoB,EACpB,SAAoC;IAEpC,OAAO,CAAC,EAAY,EAAE,IAAmB,EAAY,EAAE,CACrD,GAAG,EAAE,CACH,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACjC,MAAM,KAAK,GAAG,UAAU,CACtB,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EACpC,IAAI,CAAC,EAAE,CACR,CAAA;QAED,EAAE,EAAE;aACD,IAAI,CAAC,CAAC,CAAI,EAAS,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA,CAAC,CAAC,CAAC;aAC1D,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA,CAAE,CAAC,CAAC,CAAA;IAC/D,CAAC,CAAC,CAAA;AACR,CAAC;AAED,iFAAiF;AAEjF;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAI,IAAoB;IACnD,OAAO,iBAAiB,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;AAC9C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAI,IAAoB;IACxD,OAAO,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAA;AACnD,CAAC"}
1
+ {"version":3,"file":"timeout.js","sourceRoot":"","sources":["../../src/policies/timeout.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAE9C,iFAAiF;AAEjF;;;;;;;;;;GAUG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IAC5B,EAAE,CAAQ;IAEnB,YAAY,EAAU;QACpB,KAAK,CAAC,uBAAuB,EAAE,IAAI,CAAC,CAAA;QACpC,IAAI,CAAC,IAAI,GAAG,cAAc,CAAA;QAC1B,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;IACd,CAAC;CACF;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IACjC,EAAE,CAAQ;IAEnB,YAAY,EAAU;QACpB,KAAK,CAAC,oCAAoC,EAAE,IAAI,CAAC,CAAA;QACjD,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAA;QAC/B,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;IACd,CAAC;CACF;AAED,iFAAiF;AAEjF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,SAAS,iBAAiB,CACxB,IAAoB,EACpB,SAAoC;IAEpC,OAAO,CAAC,EAAY,EAAE,IAAmB,EAAY,EAAE,CACrD,KAAK,EAAE,YAAyB,EAAE,EAAE;QAClC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;QACxC,MAAM,UAAU,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAEzC,uEAAuE;QACvE,kEAAkE;QAClE,qBAAqB;QACrB,MAAM,KAAK,GAAG,UAAU,CACtB,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,EAClC,IAAI,CAAC,EAAE,CACR,CAAA;QAED,mEAAmE;QACnE,sCAAsC;QACtC,UAAU,CAAC,YAAY,EAAE,UAAU,CAAC,CAAA;QAEpC,IAAI,CAAC;YACH,mEAAmE;YACnE,mEAAmE;YACnE,EAAE;YACF,oEAAoE;YACpE,8DAA8D;YAC9D,OAAO,MAAM,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC9C,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;oBAC9B,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;oBAChC,OAAM;gBACR,CAAC;gBAED,UAAU,CAAC,MAAM,CAAC,gBAAgB,CAChC,OAAO,EACP,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,EACtC,EAAE,IAAI,EAAE,IAAI,EAAE,CACf,CAAA;gBAED,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CACzC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EACzB,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CACzB,CAAA;YACH,CAAC,CAAC,CAAA;QACJ,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;IACH,CAAC,CAAA;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAI,IAAoB;IACnD,OAAO,iBAAiB,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;AAC9C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAI,IAAoB;IACxD,OAAO,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAA;AACnD,CAAC"}
@@ -1,41 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.InMemoryStore = void 0;
4
- class InMemoryStore {
5
- constructor() {
6
- this.entries = new Map();
7
- }
8
- get(key) {
9
- const entry = this.entries.get(key);
10
- if (!entry)
11
- return undefined;
12
- if (entry.expiresAt !== null && Date.now() > entry.expiresAt) {
13
- this.entries.delete(key);
14
- return undefined;
15
- }
16
- return entry.value;
17
- }
18
- set(key, value, ttlMs) {
19
- // ttlMs = 0 or undefined -> no expiry (sentinel null)
20
- const expiresAt = ttlMs != null && ttlMs > 0 ? Date.now() + ttlMs : null;
21
- this.entries.set(key, { value, expiresAt });
22
- }
23
- delete(key) {
24
- this.entries.delete(key);
25
- }
26
- has(key) {
27
- // Reuse get() so expired entries are evicted on access
28
- return this.get(key) !== undefined;
29
- }
30
- /** Drop everything. Handy in tests or for manual cache invalidation. */
31
- clear() {
32
- this.entries.clear();
33
- }
34
- /** Count of live (non-expired) entries. */
35
- get size() {
36
- for (const key of this.entries.keys())
37
- this.has(key);
38
- return this.entries.size;
39
- }
40
- }
41
- exports.InMemoryStore = InMemoryStore;
4
+ /**
5
+ * @deprecated since v1.1.5 — import from `'actly'` directly.
6
+ *
7
+ * This re-export shim exists for backwards compatibility with code that
8
+ * imported `InMemoryStore` from `'actly/state/store'`. It will be removed
9
+ * in v2.0.0.
10
+ *
11
+ * The implementation has moved to `src/stores/memory.ts`.
12
+ */
13
+ var memory_js_1 = require("../stores/memory.js");
14
+ Object.defineProperty(exports, "InMemoryStore", { enumerable: true, get: function () { return memory_js_1.InMemoryStore; } });
@@ -1,13 +1,11 @@
1
- import type { StateStore } from '../types/index.js';
2
- export declare class InMemoryStore implements StateStore {
3
- private entries;
4
- get<T>(key: string): T | undefined;
5
- set<T>(key: string, value: T, ttlMs?: number): void;
6
- delete(key: string): void;
7
- has(key: string): boolean;
8
- /** Drop everything. Handy in tests or for manual cache invalidation. */
9
- clear(): void;
10
- /** Count of live (non-expired) entries. */
11
- get size(): number;
12
- }
1
+ /**
2
+ * @deprecated since v1.1.5 import from `'actly'` directly.
3
+ *
4
+ * This re-export shim exists for backwards compatibility with code that
5
+ * imported `InMemoryStore` from `'actly/state/store'`. It will be removed
6
+ * in v2.0.0.
7
+ *
8
+ * The implementation has moved to `src/stores/memory.ts`.
9
+ */
10
+ export { InMemoryStore } from '../stores/memory.js';
13
11
  //# sourceMappingURL=store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/state/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAQnD,qBAAa,aAAc,YAAW,UAAU;IAC9C,OAAO,CAAC,OAAO,CAAoC;IAEnD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAYlC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAOnD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAKzB,wEAAwE;IACxE,KAAK,IAAI,IAAI;IAIb,2CAA2C;IAC3C,IAAI,IAAI,IAAI,MAAM,CAGjB;CACF"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/state/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA"}
@@ -1,38 +1,11 @@
1
- export class InMemoryStore {
2
- constructor() {
3
- this.entries = new Map();
4
- }
5
- get(key) {
6
- const entry = this.entries.get(key);
7
- if (!entry)
8
- return undefined;
9
- if (entry.expiresAt !== null && Date.now() > entry.expiresAt) {
10
- this.entries.delete(key);
11
- return undefined;
12
- }
13
- return entry.value;
14
- }
15
- set(key, value, ttlMs) {
16
- // ttlMs = 0 or undefined -> no expiry (sentinel null)
17
- const expiresAt = ttlMs != null && ttlMs > 0 ? Date.now() + ttlMs : null;
18
- this.entries.set(key, { value, expiresAt });
19
- }
20
- delete(key) {
21
- this.entries.delete(key);
22
- }
23
- has(key) {
24
- // Reuse get() so expired entries are evicted on access
25
- return this.get(key) !== undefined;
26
- }
27
- /** Drop everything. Handy in tests or for manual cache invalidation. */
28
- clear() {
29
- this.entries.clear();
30
- }
31
- /** Count of live (non-expired) entries. */
32
- get size() {
33
- for (const key of this.entries.keys())
34
- this.has(key);
35
- return this.entries.size;
36
- }
37
- }
1
+ /**
2
+ * @deprecated since v1.1.5 — import from `'actly'` directly.
3
+ *
4
+ * This re-export shim exists for backwards compatibility with code that
5
+ * imported `InMemoryStore` from `'actly/state/store'`. It will be removed
6
+ * in v2.0.0.
7
+ *
8
+ * The implementation has moved to `src/stores/memory.ts`.
9
+ */
10
+ export { InMemoryStore } from '../stores/memory.js';
38
11
  //# sourceMappingURL=store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/state/store.ts"],"names":[],"mappings":"AAQA,MAAM,OAAO,aAAa;IAA1B;QACU,YAAO,GAAG,IAAI,GAAG,EAA0B,CAAA;IAwCrD,CAAC;IAtCC,GAAG,CAAI,GAAW;QAChB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACnC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAA;QAE5B,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YAC7D,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACxB,OAAO,SAAS,CAAA;QAClB,CAAC;QAED,OAAO,KAAK,CAAC,KAAU,CAAA;IACzB,CAAC;IAED,GAAG,CAAI,GAAW,EAAE,KAAQ,EAAE,KAAc;QAC1C,sDAAsD;QACtD,MAAM,SAAS,GACb,KAAK,IAAI,IAAI,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA;QACxD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAA;IAC7C,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IAC1B,CAAC;IAED,GAAG,CAAC,GAAW;QACb,uDAAuD;QACvD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS,CAAA;IACpC,CAAC;IAED,wEAAwE;IACxE,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;IACtB,CAAC;IAED,2CAA2C;IAC3C,IAAI,IAAI;QACN,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;YAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACpD,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAA;IAC1B,CAAC;CACF"}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/state/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ // ─── Store interfaces ─────────────────────────────────────────────────────────
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.isSyncStore = isSyncStore;
5
+ exports.isAsyncStore = isAsyncStore;
6
+ // ─── Type guards ──────────────────────────────────────────────────────────────
7
+ /**
8
+ * Narrows `AnyStateStore` to `SyncStateStore` via the `_sync` discriminant.
9
+ * Used by `execute()` and `cachePolicy` to branch between sync and async paths.
10
+ */
11
+ function isSyncStore(store) {
12
+ return store._sync === true;
13
+ }
14
+ /**
15
+ * Narrows `AnyStateStore` to `AsyncStateStore` via the `_sync` discriminant.
16
+ * Provided for symmetry; prefer `isSyncStore` for the common guard pattern.
17
+ */
18
+ function isAsyncStore(store) {
19
+ return store._sync === false;
20
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Synchronous key-value store. All operations complete in the same tick.
3
+ *
4
+ * # Why synchronous?
5
+ *
6
+ * `dedupePolicy` must read an in-flight Promise from the store and, if absent,
7
+ * write a new one — all within a single synchronous frame. If `get()` were
8
+ * async, two concurrent callers could both observe a miss before either
9
+ * write lands, defeating deduplication entirely. There is no lock primitive
10
+ * in JavaScript that can paper over this: the constraint is structural, not
11
+ * implementation-level.
12
+ *
13
+ * # The `_sync` discriminant
14
+ *
15
+ * `_sync: true` is a runtime tag that lets `execute()` enforce the dedupe
16
+ * constraint for plain-JS callers who bypass TypeScript. It is NOT part of
17
+ * the semantic contract and MUST NOT be used for anything beyond that guard.
18
+ * Implementations must set it as a `readonly` literal (`true as const`).
19
+ *
20
+ * # Lifecycle
21
+ *
22
+ * Implementations that hold background resources (timers, connections) should
23
+ * expose a `destroy()` method as a convention. See `InMemoryStore` for the
24
+ * reference pattern.
25
+ */
26
+ export interface SyncStateStore {
27
+ /** Runtime discriminant. Must be `true as const`. */
28
+ readonly _sync: true;
29
+ get<T>(key: string): T | undefined;
30
+ set<T>(key: string, value: T, ttlMs?: number): void;
31
+ delete(key: string): void;
32
+ has(key: string): boolean;
33
+ /**
34
+ * Remove all entries synchronously.
35
+ * After `clear()` returns, `size()` returns 0.
36
+ */
37
+ clear(): void;
38
+ /**
39
+ * Return the count of live (non-expired) entries.
40
+ * Side-effect free: does not mutate the store. Implementations MAY evict
41
+ * expired entries opportunistically during this call, but MUST NOT have
42
+ * observable side effects beyond internal cleanup.
43
+ */
44
+ size(): number;
45
+ }
46
+ /**
47
+ * Asynchronous key-value store. All operations return Promises.
48
+ *
49
+ * # Policy compatibility
50
+ *
51
+ * Compatible with `cachePolicy` only. Passing an `AsyncStateStore` to a
52
+ * policy chain that includes `dedupePolicy` is a TypeScript error and a
53
+ * runtime error — `execute()` throws at chain-build time. See
54
+ * `SyncStateStore` for why dedupe requires synchronous access.
55
+ *
56
+ * # TTL semantics
57
+ *
58
+ * The store is responsible for honouring `ttlMs`. Actly passes it as a hint.
59
+ * Implementations may delegate to a native TTL mechanism (e.g. Redis EXPIRE).
60
+ *
61
+ * # The `_sync` discriminant
62
+ *
63
+ * `_sync: false` mirrors the discriminant on `SyncStateStore`. Must be set
64
+ * as a `readonly` literal (`false as const`).
65
+ */
66
+ export interface AsyncStateStore {
67
+ /** Runtime discriminant. Must be `false as const`. */
68
+ readonly _sync: false;
69
+ get<T>(key: string): Promise<T | undefined>;
70
+ set<T>(key: string, value: T, ttlMs?: number): Promise<void>;
71
+ delete(key: string): Promise<void>;
72
+ has(key: string): Promise<boolean>;
73
+ /** Remove all entries managed by this store. */
74
+ clear(): Promise<void>;
75
+ /** Return the count of live (non-expired) entries. */
76
+ size(): Promise<number>;
77
+ }
78
+ /**
79
+ * Narrows `AnyStateStore` to `SyncStateStore` via the `_sync` discriminant.
80
+ * Used by `execute()` and `cachePolicy` to branch between sync and async paths.
81
+ */
82
+ export declare function isSyncStore(store: SyncStateStore | AsyncStateStore): store is SyncStateStore;
83
+ /**
84
+ * Narrows `AnyStateStore` to `AsyncStateStore` via the `_sync` discriminant.
85
+ * Provided for symmetry; prefer `isSyncStore` for the common guard pattern.
86
+ */
87
+ export declare function isAsyncStore(store: SyncStateStore | AsyncStateStore): store is AsyncStateStore;
88
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/stores/base.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,cAAc;IAC7B,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAA;IAEpB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAA;IAClC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACnD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;IAEzB;;;OAGG;IACH,KAAK,IAAI,IAAI,CAAA;IAEb;;;;;OAKG;IACH,IAAI,IAAI,MAAM,CAAA;CACf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IAErB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;IAC3C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5D,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAElC,gDAAgD;IAChD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAEtB,sDAAsD;IACtD,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;CACxB;AAID;;;GAGG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,cAAc,GAAG,eAAe,GACtC,KAAK,IAAI,cAAc,CAEzB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,cAAc,GAAG,eAAe,GACtC,KAAK,IAAI,eAAe,CAE1B"}
@@ -0,0 +1,17 @@
1
+ // ─── Store interfaces ─────────────────────────────────────────────────────────
2
+ // ─── Type guards ──────────────────────────────────────────────────────────────
3
+ /**
4
+ * Narrows `AnyStateStore` to `SyncStateStore` via the `_sync` discriminant.
5
+ * Used by `execute()` and `cachePolicy` to branch between sync and async paths.
6
+ */
7
+ export function isSyncStore(store) {
8
+ return store._sync === true;
9
+ }
10
+ /**
11
+ * Narrows `AnyStateStore` to `AsyncStateStore` via the `_sync` discriminant.
12
+ * Provided for symmetry; prefer `isSyncStore` for the common guard pattern.
13
+ */
14
+ export function isAsyncStore(store) {
15
+ return store._sync === false;
16
+ }
17
+ //# sourceMappingURL=base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/stores/base.ts"],"names":[],"mappings":"AAAA,iFAAiF;AAuFjF,iFAAiF;AAEjF;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,KAAuC;IAEvC,OAAO,KAAK,CAAC,KAAK,KAAK,IAAI,CAAA;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAuC;IAEvC,OAAO,KAAK,CAAC,KAAK,KAAK,KAAK,CAAA;AAC9B,CAAC"}