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.
- package/LICENSE +1 -1
- package/README.md +612 -0
- package/dist/core/act.cjs +187 -37
- package/dist/core/act.d.ts +81 -10
- package/dist/core/act.d.ts.map +1 -1
- package/dist/core/act.js +184 -36
- package/dist/core/act.js.map +1 -1
- package/dist/core/executor.cjs +30 -4
- package/dist/core/executor.d.ts +33 -11
- package/dist/core/executor.d.ts.map +1 -1
- package/dist/core/executor.js +29 -4
- package/dist/core/executor.js.map +1 -1
- package/dist/index.cjs +15 -5
- package/dist/index.d.ts +9 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/policies/cache.cjs +91 -7
- package/dist/policies/cache.d.ts +30 -3
- package/dist/policies/cache.d.ts.map +1 -1
- package/dist/policies/cache.js +91 -7
- package/dist/policies/cache.js.map +1 -1
- package/dist/policies/dedupe.cjs +79 -16
- package/dist/policies/dedupe.d.ts +39 -7
- package/dist/policies/dedupe.d.ts.map +1 -1
- package/dist/policies/dedupe.js +79 -16
- package/dist/policies/dedupe.js.map +1 -1
- package/dist/policies/retry.cjs +65 -25
- package/dist/policies/retry.d.ts +25 -2
- package/dist/policies/retry.d.ts.map +1 -1
- package/dist/policies/retry.js +65 -25
- package/dist/policies/retry.js.map +1 -1
- package/dist/policies/timeout.cjs +87 -17
- package/dist/policies/timeout.d.ts +28 -8
- package/dist/policies/timeout.d.ts.map +1 -1
- package/dist/policies/timeout.js +87 -17
- package/dist/policies/timeout.js.map +1 -1
- package/dist/state/store.cjs +11 -38
- package/dist/state/store.d.ts +10 -12
- package/dist/state/store.d.ts.map +1 -1
- package/dist/state/store.js +10 -37
- package/dist/state/store.js.map +1 -1
- package/dist/stores/base.cjs +20 -0
- package/dist/stores/base.d.ts +88 -0
- package/dist/stores/base.d.ts.map +1 -0
- package/dist/stores/base.js +17 -0
- package/dist/stores/base.js.map +1 -0
- package/dist/stores/memory.cjs +140 -0
- package/dist/stores/memory.d.ts +79 -0
- package/dist/stores/memory.d.ts.map +1 -0
- package/dist/stores/memory.js +137 -0
- package/dist/stores/memory.js.map +1 -0
- package/dist/types/index.d.ts +151 -34
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/abort.cjs +142 -0
- package/dist/utils/abort.d.ts +55 -0
- package/dist/utils/abort.d.ts.map +1 -0
- package/dist/utils/abort.js +136 -0
- package/dist/utils/abort.js.map +1 -0
- package/dist/utils/backoff.cjs +43 -0
- package/dist/utils/backoff.d.ts +14 -0
- package/dist/utils/backoff.d.ts.map +1 -0
- package/dist/utils/backoff.js +41 -0
- package/dist/utils/backoff.js.map +1 -0
- package/dist/utils/validate.cjs +79 -0
- package/dist/utils/validate.d.ts +15 -0
- package/dist/utils/validate.d.ts.map +1 -0
- package/dist/utils/validate.js +72 -0
- package/dist/utils/validate.js.map +1 -0
- package/package.json +19 -37
package/dist/core/act.cjs
CHANGED
|
@@ -1,32 +1,110 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.act = act;
|
|
4
|
+
exports.invalidate = invalidate;
|
|
5
|
+
exports.withStore = withStore;
|
|
4
6
|
const executor_js_1 = require("./executor.js");
|
|
5
7
|
const retry_js_1 = require("../policies/retry.js");
|
|
6
8
|
const timeout_js_1 = require("../policies/timeout.js");
|
|
7
9
|
const dedupe_js_1 = require("../policies/dedupe.js");
|
|
8
10
|
const cache_js_1 = require("../policies/cache.js");
|
|
9
|
-
const
|
|
11
|
+
const memory_js_1 = require("../stores/memory.js");
|
|
12
|
+
const base_js_1 = require("../stores/base.js");
|
|
13
|
+
const abort_js_1 = require("../utils/abort.js");
|
|
14
|
+
const validate_js_1 = require("../utils/validate.js");
|
|
10
15
|
// Module-level default store so cache and dedupe persist across calls.
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
|
|
16
|
+
// Always an InMemoryStore (SyncStateStore) — required because the default
|
|
17
|
+
// chain may include dedupePolicy, which mandates synchronous store access.
|
|
18
|
+
// For SSR isolation or per-test control, use `withStore()` or call
|
|
19
|
+
// `execute()` directly with an explicit store.
|
|
20
|
+
const defaultStore = new memory_js_1.InMemoryStore();
|
|
21
|
+
// Namespace prefixes used by policies. Kept here (not in policy files) so
|
|
22
|
+
// `invalidate()` can resolve cache keys without importing policy internals.
|
|
23
|
+
const CACHE_NS = 'cache:';
|
|
14
24
|
/**
|
|
15
|
-
*
|
|
25
|
+
* Normalise `dedupe: true` shorthand to `DedupeOptions`.
|
|
26
|
+
* Returns `undefined` if dedupe is disabled or absent.
|
|
27
|
+
*/
|
|
28
|
+
function normalizeDedupe(opt) {
|
|
29
|
+
if (opt === true)
|
|
30
|
+
return { enabled: true };
|
|
31
|
+
if (opt && typeof opt === 'object' && opt.enabled) {
|
|
32
|
+
return {
|
|
33
|
+
enabled: true,
|
|
34
|
+
...(opt.inflightTtl !== undefined ? { inflightTtl: opt.inflightTtl } : {}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build the policy chain from `ActOptions`. The order is fixed and
|
|
41
|
+
* documented in `executor.ts`. Policies with no effect (e.g. `retry.attempts: 1`)
|
|
42
|
+
* are skipped — they would be pure overhead.
|
|
43
|
+
*/
|
|
44
|
+
function buildPolicies(options) {
|
|
45
|
+
const dedupe = normalizeDedupe(options.dedupe);
|
|
46
|
+
const policies = [];
|
|
47
|
+
// 0. Outermost: hard wall-clock budget over the entire operation.
|
|
48
|
+
// If it fires, no inner policy can extend the deadline.
|
|
49
|
+
if (options.totalTimeout && options.totalTimeout.ms > 0) {
|
|
50
|
+
policies.push((0, timeout_js_1.totalTimeoutPolicy)(options.totalTimeout));
|
|
51
|
+
}
|
|
52
|
+
// 1. Cache: a hit short-circuits everything below it.
|
|
53
|
+
if (options.cache && options.cache.ttl > 0) {
|
|
54
|
+
policies.push((0, cache_js_1.cachePolicy)(options.cache));
|
|
55
|
+
}
|
|
56
|
+
// 2. Dedupe: collapses concurrent callers before retry fires.
|
|
57
|
+
if (dedupe) {
|
|
58
|
+
policies.push((0, dedupe_js_1.dedupePolicy)(dedupe));
|
|
59
|
+
}
|
|
60
|
+
// 3. Retry: owns the attempt loop.
|
|
61
|
+
// `attempts: 1` is a no-op — skip to avoid overhead.
|
|
62
|
+
if (options.retry && options.retry.attempts > 1) {
|
|
63
|
+
policies.push((0, retry_js_1.retryPolicy)(options.retry));
|
|
64
|
+
}
|
|
65
|
+
// 4. Innermost: per-attempt clock. Resets on every retry.
|
|
66
|
+
if (options.timeout && options.timeout.ms > 0) {
|
|
67
|
+
policies.push((0, timeout_js_1.timeoutPolicy)(options.timeout));
|
|
68
|
+
}
|
|
69
|
+
return policies;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Build a root AbortController from `options.signal`.
|
|
73
|
+
*
|
|
74
|
+
* - If no user signal: returns a fresh controller that never aborts unless
|
|
75
|
+
* an outer timeout policy aborts it.
|
|
76
|
+
* - If user signal is already aborted: returns a controller that is already
|
|
77
|
+
* aborted with the user's reason (so the operation rejects immediately).
|
|
78
|
+
* - Otherwise: links the user signal to the controller.
|
|
79
|
+
*/
|
|
80
|
+
function buildRootSignal(userSignal) {
|
|
81
|
+
const controller = new AbortController();
|
|
82
|
+
if (userSignal)
|
|
83
|
+
(0, abort_js_1.linkSignal)(userSignal, controller);
|
|
84
|
+
return controller;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Execute `fn` with the given reliability policies.
|
|
16
88
|
*
|
|
17
89
|
* @param key Stable identifier for this action. Scopes dedupe + cache.
|
|
18
|
-
* @param fn The async work to run.
|
|
90
|
+
* @param fn The async work to run. Receives an `AbortSignal` for
|
|
91
|
+
* cooperative cancellation (legacy `() => Promise<T>` is
|
|
92
|
+
* still accepted — the signal is simply ignored).
|
|
19
93
|
* @param options Which policies to apply and how. All fields are optional.
|
|
20
94
|
*
|
|
21
|
-
* @returns ActResult<T
|
|
22
|
-
* Check result.ok before reading result.value
|
|
95
|
+
* @returns `ActResult<T>` — always resolves, never throws.
|
|
96
|
+
* Check `result.ok` before reading `result.value`.
|
|
23
97
|
*
|
|
24
98
|
* @example
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
99
|
+
* // With cooperative cancellation
|
|
100
|
+
* const result = await act('user:42', async (signal) => {
|
|
101
|
+
* return fetch(`/api/users/42`, { signal })
|
|
102
|
+
* }, {
|
|
103
|
+
* retry: { attempts: 3, delayMs: 200, backoff: 'exponential' },
|
|
104
|
+
* timeout: { ms: 5_000 },
|
|
105
|
+
* totalTimeout: { ms: 12_000 },
|
|
106
|
+
* dedupe: true,
|
|
107
|
+
* cache: { ttl: 60_000 },
|
|
30
108
|
* })
|
|
31
109
|
*
|
|
32
110
|
* if (result.ok) {
|
|
@@ -36,36 +114,108 @@ const defaultStore = new store_js_1.InMemoryStore();
|
|
|
36
114
|
* }
|
|
37
115
|
*/
|
|
38
116
|
async function act(key, fn, options = {}) {
|
|
117
|
+
// Validate input upfront. Programmer errors throw — they should not be
|
|
118
|
+
// swallowed into an ActFailure because the caller's code is broken.
|
|
119
|
+
(0, validate_js_1.assertKey)(key);
|
|
120
|
+
(0, validate_js_1.assertOptions)(options);
|
|
39
121
|
const meta = { attempts: 1, source: 'fresh' };
|
|
40
|
-
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const policies = [];
|
|
47
|
-
// totalTimeout sits before everything — it's a hard wall-clock budget over
|
|
48
|
-
// the entire operation. If it fires, no inner policy can extend the deadline.
|
|
49
|
-
if (options.totalTimeout && options.totalTimeout.ms > 0) {
|
|
50
|
-
policies.push((0, timeout_js_1.totalTimeoutPolicy)(options.totalTimeout)); // 0. hardest outer wall
|
|
51
|
-
}
|
|
52
|
-
if (options.cache && options.cache.ttl > 0) {
|
|
53
|
-
policies.push((0, cache_js_1.cachePolicy)(options.cache)); // 1. skip all on hit
|
|
54
|
-
}
|
|
55
|
-
if (dedupe?.enabled) {
|
|
56
|
-
policies.push((0, dedupe_js_1.dedupePolicy)()); // 2. collapse concurrent callers
|
|
57
|
-
}
|
|
58
|
-
if (options.retry && options.retry.attempts > 1) {
|
|
59
|
-
policies.push((0, retry_js_1.retryPolicy)(options.retry)); // 3. own the attempt loop
|
|
60
|
-
}
|
|
61
|
-
if (options.timeout && options.timeout.ms > 0) {
|
|
62
|
-
policies.push((0, timeout_js_1.timeoutPolicy)(options.timeout)); // 4. innermost — per-attempt clock
|
|
122
|
+
const rootController = buildRootSignal(options.signal);
|
|
123
|
+
// Fast-fail if the user signal is already aborted. We do this after
|
|
124
|
+
// validation so the caller still gets a TypeError for bad options rather
|
|
125
|
+
// than a silent abort.
|
|
126
|
+
if (rootController.signal.aborted) {
|
|
127
|
+
return { ok: false, error: rootController.signal.reason, attempts: 0 };
|
|
63
128
|
}
|
|
129
|
+
const policies = buildPolicies(options);
|
|
64
130
|
try {
|
|
65
|
-
const value = await (0, executor_js_1.execute)({
|
|
131
|
+
const value = await (0, executor_js_1.execute)({
|
|
132
|
+
key,
|
|
133
|
+
fn,
|
|
134
|
+
policies,
|
|
135
|
+
store: defaultStore,
|
|
136
|
+
meta,
|
|
137
|
+
signal: rootController.signal,
|
|
138
|
+
});
|
|
66
139
|
return { ok: true, value, source: meta.source, attempts: meta.attempts };
|
|
67
140
|
}
|
|
68
141
|
catch (error) {
|
|
69
142
|
return { ok: false, error, attempts: meta.attempts };
|
|
70
143
|
}
|
|
71
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Invalidate the cached value for `key` on the default module-level store.
|
|
147
|
+
*
|
|
148
|
+
* Only clears the cache slot — does not affect in-flight dedupe entries
|
|
149
|
+
* (those will settle on their own). Returns `true` if a cache entry was
|
|
150
|
+
* removed, `false` otherwise.
|
|
151
|
+
*
|
|
152
|
+
* Useful when you know the underlying data has changed and you want the
|
|
153
|
+
* next `act()` call to re-run `fn` instead of serving stale cache:
|
|
154
|
+
*
|
|
155
|
+
* ```ts
|
|
156
|
+
* await act('user:42', () => fetchUser(42), { cache: { ttl: 60_000 } })
|
|
157
|
+
* // ... user updates their profile ...
|
|
158
|
+
* invalidate('user:42') // next call will re-fetch
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
function invalidate(key) {
|
|
162
|
+
const cacheKey = CACHE_NS + key;
|
|
163
|
+
const existed = defaultStore.has(cacheKey);
|
|
164
|
+
defaultStore.delete(cacheKey);
|
|
165
|
+
return existed;
|
|
166
|
+
}
|
|
167
|
+
function withStore(store) {
|
|
168
|
+
const scopedAct = async (key, fn, options = {}) => {
|
|
169
|
+
(0, validate_js_1.assertKey)(key);
|
|
170
|
+
(0, validate_js_1.assertOptions)(options);
|
|
171
|
+
const meta = { attempts: 1, source: 'fresh' };
|
|
172
|
+
const rootController = buildRootSignal(options.signal);
|
|
173
|
+
if (rootController.signal.aborted) {
|
|
174
|
+
return { ok: false, error: rootController.signal.reason, attempts: 0 };
|
|
175
|
+
}
|
|
176
|
+
const policies = buildPolicies(options);
|
|
177
|
+
try {
|
|
178
|
+
const value = await (0, executor_js_1.execute)({
|
|
179
|
+
key,
|
|
180
|
+
fn,
|
|
181
|
+
policies,
|
|
182
|
+
store,
|
|
183
|
+
meta,
|
|
184
|
+
signal: rootController.signal,
|
|
185
|
+
});
|
|
186
|
+
return { ok: true, value, source: meta.source, attempts: meta.attempts };
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
return { ok: false, error, attempts: meta.attempts };
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
// Build the `invalidate` implementation. The runtime branch on
|
|
193
|
+
// `isSyncStore` selects the correct path; the cast through `unknown`
|
|
194
|
+
// is required because TypeScript cannot narrow the union return type
|
|
195
|
+
// (`boolean | Promise<boolean>`) to match either overload signature
|
|
196
|
+
// individually. The overloads at the call site guarantee callers see
|
|
197
|
+
// the correct type.
|
|
198
|
+
const invalidateImpl = (key) => {
|
|
199
|
+
(0, validate_js_1.assertKey)(key);
|
|
200
|
+
const cacheKey = CACHE_NS + key;
|
|
201
|
+
if ((0, base_js_1.isSyncStore)(store)) {
|
|
202
|
+
const existed = store.has(cacheKey);
|
|
203
|
+
store.delete(cacheKey);
|
|
204
|
+
return existed;
|
|
205
|
+
}
|
|
206
|
+
// Async store branch.
|
|
207
|
+
return (async () => {
|
|
208
|
+
const existed = await store.has(cacheKey);
|
|
209
|
+
await store.delete(cacheKey);
|
|
210
|
+
return existed;
|
|
211
|
+
})();
|
|
212
|
+
};
|
|
213
|
+
// Attach `invalidate` and `store` to the function object. We use
|
|
214
|
+
// `Object.assign` rather than mutation so the types narrow cleanly at
|
|
215
|
+
// the call site. The cast through `unknown` is necessary because the
|
|
216
|
+
// implementation signature is wider than either overload.
|
|
217
|
+
return Object.assign(scopedAct, {
|
|
218
|
+
invalidate: invalidateImpl,
|
|
219
|
+
store,
|
|
220
|
+
});
|
|
221
|
+
}
|
package/dist/core/act.d.ts
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
|
-
import type { ActFn, ActOptions, ActResult } from '../types/index.js';
|
|
1
|
+
import type { ActFn, ActOptions, ActResult, AnyStateStore, SyncStateStore, AsyncStateStore } from '../types/index.js';
|
|
2
2
|
/**
|
|
3
|
-
* Execute fn with the given reliability policies.
|
|
3
|
+
* Execute `fn` with the given reliability policies.
|
|
4
4
|
*
|
|
5
5
|
* @param key Stable identifier for this action. Scopes dedupe + cache.
|
|
6
|
-
* @param fn The async work to run.
|
|
6
|
+
* @param fn The async work to run. Receives an `AbortSignal` for
|
|
7
|
+
* cooperative cancellation (legacy `() => Promise<T>` is
|
|
8
|
+
* still accepted — the signal is simply ignored).
|
|
7
9
|
* @param options Which policies to apply and how. All fields are optional.
|
|
8
10
|
*
|
|
9
|
-
* @returns ActResult<T
|
|
10
|
-
* Check result.ok before reading result.value
|
|
11
|
+
* @returns `ActResult<T>` — always resolves, never throws.
|
|
12
|
+
* Check `result.ok` before reading `result.value`.
|
|
11
13
|
*
|
|
12
14
|
* @example
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* // With cooperative cancellation
|
|
16
|
+
* const result = await act('user:42', async (signal) => {
|
|
17
|
+
* return fetch(`/api/users/42`, { signal })
|
|
18
|
+
* }, {
|
|
19
|
+
* retry: { attempts: 3, delayMs: 200, backoff: 'exponential' },
|
|
20
|
+
* timeout: { ms: 5_000 },
|
|
21
|
+
* totalTimeout: { ms: 12_000 },
|
|
22
|
+
* dedupe: true,
|
|
23
|
+
* cache: { ttl: 60_000 },
|
|
18
24
|
* })
|
|
19
25
|
*
|
|
20
26
|
* if (result.ok) {
|
|
@@ -24,4 +30,69 @@ import type { ActFn, ActOptions, ActResult } from '../types/index.js';
|
|
|
24
30
|
* }
|
|
25
31
|
*/
|
|
26
32
|
export declare function act<T>(key: string, fn: ActFn<T>, options?: ActOptions): Promise<ActResult<T>>;
|
|
33
|
+
/**
|
|
34
|
+
* Invalidate the cached value for `key` on the default module-level store.
|
|
35
|
+
*
|
|
36
|
+
* Only clears the cache slot — does not affect in-flight dedupe entries
|
|
37
|
+
* (those will settle on their own). Returns `true` if a cache entry was
|
|
38
|
+
* removed, `false` otherwise.
|
|
39
|
+
*
|
|
40
|
+
* Useful when you know the underlying data has changed and you want the
|
|
41
|
+
* next `act()` call to re-run `fn` instead of serving stale cache:
|
|
42
|
+
*
|
|
43
|
+
* ```ts
|
|
44
|
+
* await act('user:42', () => fetchUser(42), { cache: { ttl: 60_000 } })
|
|
45
|
+
* // ... user updates their profile ...
|
|
46
|
+
* invalidate('user:42') // next call will re-fetch
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare function invalidate(key: string): boolean;
|
|
50
|
+
/** Result of `withStore()` for a sync store: `act` + sync `invalidate`. */
|
|
51
|
+
export interface ScopedActSync {
|
|
52
|
+
<T>(key: string, fn: ActFn<T>, options?: ActOptions): Promise<ActResult<T>>;
|
|
53
|
+
invalidate(key: string): boolean;
|
|
54
|
+
/** The store this scope is bound to. Useful for `store.destroy()` etc. */
|
|
55
|
+
readonly store: AnyStateStore;
|
|
56
|
+
}
|
|
57
|
+
/** Result of `withStore()` for an async store: `act` + async `invalidate`. */
|
|
58
|
+
export interface ScopedActAsync {
|
|
59
|
+
<T>(key: string, fn: ActFn<T>, options?: ActOptions): Promise<ActResult<T>>;
|
|
60
|
+
invalidate(key: string): Promise<boolean>;
|
|
61
|
+
readonly store: AnyStateStore;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Create a scoped `act` function bound to an explicit store.
|
|
65
|
+
*
|
|
66
|
+
* Use this for:
|
|
67
|
+
* - **SSR request isolation**: one store per request, no cross-request
|
|
68
|
+
* cache/dedupe leakage.
|
|
69
|
+
* - **Multi-tenant scenarios**: one store per tenant, no cross-tenant
|
|
70
|
+
* data leakage.
|
|
71
|
+
* - **Test isolation**: each test gets a fresh store, no shared state.
|
|
72
|
+
*
|
|
73
|
+
* The returned function has the same signature as `act()`. It also exposes
|
|
74
|
+
* an `invalidate(key)` method and a `store` reference for cleanup.
|
|
75
|
+
*
|
|
76
|
+
* For sync stores, `invalidate` returns `boolean` synchronously.
|
|
77
|
+
* For async stores, `invalidate` returns `Promise<boolean>`.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* import { withStore, InMemoryStore } from 'actly'
|
|
82
|
+
*
|
|
83
|
+
* const store = new InMemoryStore({ maxSize: 1000, autoCleanup: true })
|
|
84
|
+
* const act = withStore(store)
|
|
85
|
+
*
|
|
86
|
+
* try {
|
|
87
|
+
* await act('user:42', () => fetchUser(42), { cache: { ttl: 60_000 } })
|
|
88
|
+
* // ... user updates their profile ...
|
|
89
|
+
* act.invalidate('user:42') // next call re-fetches
|
|
90
|
+
* } finally {
|
|
91
|
+
* store.destroy()
|
|
92
|
+
* }
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare function withStore(store: SyncStateStore): ScopedActSync;
|
|
96
|
+
export declare function withStore(store: AsyncStateStore): ScopedActAsync;
|
|
97
|
+
export declare function withStore(store: AnyStateStore): ScopedActSync | ScopedActAsync;
|
|
27
98
|
//# sourceMappingURL=act.d.ts.map
|
package/dist/core/act.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"act.d.ts","sourceRoot":"","sources":["../../src/core/act.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"act.d.ts","sourceRoot":"","sources":["../../src/core/act.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,KAAK,EACL,UAAU,EACV,SAAS,EAGT,aAAa,EACb,cAAc,EACd,eAAe,EAChB,MAAM,mBAAmB,CAAA;AAgG1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,GAAG,CAAC,CAAC,EACzB,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,EACZ,OAAO,GAAE,UAAe,GACvB,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CA+BvB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAK/C;AAID,2EAA2E;AAC3E,MAAM,WAAW,aAAa;IAC5B,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3E,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;IAChC,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAA;CAC9B;AAED,8EAA8E;AAC9E,MAAM,WAAW,cAAc;IAC7B,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3E,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACzC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAA;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,aAAa,CAAA;AAC/D,wBAAgB,SAAS,CAAC,KAAK,EAAE,eAAe,GAAG,cAAc,CAAA;AACjE,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,GAAG,cAAc,CAAA"}
|
package/dist/core/act.js
CHANGED
|
@@ -3,27 +3,103 @@ import { retryPolicy } from '../policies/retry.js';
|
|
|
3
3
|
import { timeoutPolicy, totalTimeoutPolicy } from '../policies/timeout.js';
|
|
4
4
|
import { dedupePolicy } from '../policies/dedupe.js';
|
|
5
5
|
import { cachePolicy } from '../policies/cache.js';
|
|
6
|
-
import { InMemoryStore } from '../
|
|
6
|
+
import { InMemoryStore } from '../stores/memory.js';
|
|
7
|
+
import { isSyncStore } from '../stores/base.js';
|
|
8
|
+
import { linkSignal } from '../utils/abort.js';
|
|
9
|
+
import { assertKey, assertOptions, } from '../utils/validate.js';
|
|
7
10
|
// Module-level default store so cache and dedupe persist across calls.
|
|
8
|
-
//
|
|
9
|
-
//
|
|
11
|
+
// Always an InMemoryStore (SyncStateStore) — required because the default
|
|
12
|
+
// chain may include dedupePolicy, which mandates synchronous store access.
|
|
13
|
+
// For SSR isolation or per-test control, use `withStore()` or call
|
|
14
|
+
// `execute()` directly with an explicit store.
|
|
10
15
|
const defaultStore = new InMemoryStore();
|
|
16
|
+
// Namespace prefixes used by policies. Kept here (not in policy files) so
|
|
17
|
+
// `invalidate()` can resolve cache keys without importing policy internals.
|
|
18
|
+
const CACHE_NS = 'cache:';
|
|
11
19
|
/**
|
|
12
|
-
*
|
|
20
|
+
* Normalise `dedupe: true` shorthand to `DedupeOptions`.
|
|
21
|
+
* Returns `undefined` if dedupe is disabled or absent.
|
|
22
|
+
*/
|
|
23
|
+
function normalizeDedupe(opt) {
|
|
24
|
+
if (opt === true)
|
|
25
|
+
return { enabled: true };
|
|
26
|
+
if (opt && typeof opt === 'object' && opt.enabled) {
|
|
27
|
+
return {
|
|
28
|
+
enabled: true,
|
|
29
|
+
...(opt.inflightTtl !== undefined ? { inflightTtl: opt.inflightTtl } : {}),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Build the policy chain from `ActOptions`. The order is fixed and
|
|
36
|
+
* documented in `executor.ts`. Policies with no effect (e.g. `retry.attempts: 1`)
|
|
37
|
+
* are skipped — they would be pure overhead.
|
|
38
|
+
*/
|
|
39
|
+
function buildPolicies(options) {
|
|
40
|
+
const dedupe = normalizeDedupe(options.dedupe);
|
|
41
|
+
const policies = [];
|
|
42
|
+
// 0. Outermost: hard wall-clock budget over the entire operation.
|
|
43
|
+
// If it fires, no inner policy can extend the deadline.
|
|
44
|
+
if (options.totalTimeout && options.totalTimeout.ms > 0) {
|
|
45
|
+
policies.push(totalTimeoutPolicy(options.totalTimeout));
|
|
46
|
+
}
|
|
47
|
+
// 1. Cache: a hit short-circuits everything below it.
|
|
48
|
+
if (options.cache && options.cache.ttl > 0) {
|
|
49
|
+
policies.push(cachePolicy(options.cache));
|
|
50
|
+
}
|
|
51
|
+
// 2. Dedupe: collapses concurrent callers before retry fires.
|
|
52
|
+
if (dedupe) {
|
|
53
|
+
policies.push(dedupePolicy(dedupe));
|
|
54
|
+
}
|
|
55
|
+
// 3. Retry: owns the attempt loop.
|
|
56
|
+
// `attempts: 1` is a no-op — skip to avoid overhead.
|
|
57
|
+
if (options.retry && options.retry.attempts > 1) {
|
|
58
|
+
policies.push(retryPolicy(options.retry));
|
|
59
|
+
}
|
|
60
|
+
// 4. Innermost: per-attempt clock. Resets on every retry.
|
|
61
|
+
if (options.timeout && options.timeout.ms > 0) {
|
|
62
|
+
policies.push(timeoutPolicy(options.timeout));
|
|
63
|
+
}
|
|
64
|
+
return policies;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build a root AbortController from `options.signal`.
|
|
68
|
+
*
|
|
69
|
+
* - If no user signal: returns a fresh controller that never aborts unless
|
|
70
|
+
* an outer timeout policy aborts it.
|
|
71
|
+
* - If user signal is already aborted: returns a controller that is already
|
|
72
|
+
* aborted with the user's reason (so the operation rejects immediately).
|
|
73
|
+
* - Otherwise: links the user signal to the controller.
|
|
74
|
+
*/
|
|
75
|
+
function buildRootSignal(userSignal) {
|
|
76
|
+
const controller = new AbortController();
|
|
77
|
+
if (userSignal)
|
|
78
|
+
linkSignal(userSignal, controller);
|
|
79
|
+
return controller;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Execute `fn` with the given reliability policies.
|
|
13
83
|
*
|
|
14
84
|
* @param key Stable identifier for this action. Scopes dedupe + cache.
|
|
15
|
-
* @param fn The async work to run.
|
|
85
|
+
* @param fn The async work to run. Receives an `AbortSignal` for
|
|
86
|
+
* cooperative cancellation (legacy `() => Promise<T>` is
|
|
87
|
+
* still accepted — the signal is simply ignored).
|
|
16
88
|
* @param options Which policies to apply and how. All fields are optional.
|
|
17
89
|
*
|
|
18
|
-
* @returns ActResult<T
|
|
19
|
-
* Check result.ok before reading result.value
|
|
90
|
+
* @returns `ActResult<T>` — always resolves, never throws.
|
|
91
|
+
* Check `result.ok` before reading `result.value`.
|
|
20
92
|
*
|
|
21
93
|
* @example
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
94
|
+
* // With cooperative cancellation
|
|
95
|
+
* const result = await act('user:42', async (signal) => {
|
|
96
|
+
* return fetch(`/api/users/42`, { signal })
|
|
97
|
+
* }, {
|
|
98
|
+
* retry: { attempts: 3, delayMs: 200, backoff: 'exponential' },
|
|
99
|
+
* timeout: { ms: 5_000 },
|
|
100
|
+
* totalTimeout: { ms: 12_000 },
|
|
101
|
+
* dedupe: true,
|
|
102
|
+
* cache: { ttl: 60_000 },
|
|
27
103
|
* })
|
|
28
104
|
*
|
|
29
105
|
* if (result.ok) {
|
|
@@ -33,37 +109,109 @@ const defaultStore = new InMemoryStore();
|
|
|
33
109
|
* }
|
|
34
110
|
*/
|
|
35
111
|
export async function act(key, fn, options = {}) {
|
|
112
|
+
// Validate input upfront. Programmer errors throw — they should not be
|
|
113
|
+
// swallowed into an ActFailure because the caller's code is broken.
|
|
114
|
+
assertKey(key);
|
|
115
|
+
assertOptions(options);
|
|
36
116
|
const meta = { attempts: 1, source: 'fresh' };
|
|
37
|
-
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const policies = [];
|
|
44
|
-
// totalTimeout sits before everything — it's a hard wall-clock budget over
|
|
45
|
-
// the entire operation. If it fires, no inner policy can extend the deadline.
|
|
46
|
-
if (options.totalTimeout && options.totalTimeout.ms > 0) {
|
|
47
|
-
policies.push(totalTimeoutPolicy(options.totalTimeout)); // 0. hardest outer wall
|
|
48
|
-
}
|
|
49
|
-
if (options.cache && options.cache.ttl > 0) {
|
|
50
|
-
policies.push(cachePolicy(options.cache)); // 1. skip all on hit
|
|
51
|
-
}
|
|
52
|
-
if (dedupe?.enabled) {
|
|
53
|
-
policies.push(dedupePolicy()); // 2. collapse concurrent callers
|
|
54
|
-
}
|
|
55
|
-
if (options.retry && options.retry.attempts > 1) {
|
|
56
|
-
policies.push(retryPolicy(options.retry)); // 3. own the attempt loop
|
|
57
|
-
}
|
|
58
|
-
if (options.timeout && options.timeout.ms > 0) {
|
|
59
|
-
policies.push(timeoutPolicy(options.timeout)); // 4. innermost — per-attempt clock
|
|
117
|
+
const rootController = buildRootSignal(options.signal);
|
|
118
|
+
// Fast-fail if the user signal is already aborted. We do this after
|
|
119
|
+
// validation so the caller still gets a TypeError for bad options rather
|
|
120
|
+
// than a silent abort.
|
|
121
|
+
if (rootController.signal.aborted) {
|
|
122
|
+
return { ok: false, error: rootController.signal.reason, attempts: 0 };
|
|
60
123
|
}
|
|
124
|
+
const policies = buildPolicies(options);
|
|
61
125
|
try {
|
|
62
|
-
const value = await execute({
|
|
126
|
+
const value = await execute({
|
|
127
|
+
key,
|
|
128
|
+
fn,
|
|
129
|
+
policies,
|
|
130
|
+
store: defaultStore,
|
|
131
|
+
meta,
|
|
132
|
+
signal: rootController.signal,
|
|
133
|
+
});
|
|
63
134
|
return { ok: true, value, source: meta.source, attempts: meta.attempts };
|
|
64
135
|
}
|
|
65
136
|
catch (error) {
|
|
66
137
|
return { ok: false, error, attempts: meta.attempts };
|
|
67
138
|
}
|
|
68
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Invalidate the cached value for `key` on the default module-level store.
|
|
142
|
+
*
|
|
143
|
+
* Only clears the cache slot — does not affect in-flight dedupe entries
|
|
144
|
+
* (those will settle on their own). Returns `true` if a cache entry was
|
|
145
|
+
* removed, `false` otherwise.
|
|
146
|
+
*
|
|
147
|
+
* Useful when you know the underlying data has changed and you want the
|
|
148
|
+
* next `act()` call to re-run `fn` instead of serving stale cache:
|
|
149
|
+
*
|
|
150
|
+
* ```ts
|
|
151
|
+
* await act('user:42', () => fetchUser(42), { cache: { ttl: 60_000 } })
|
|
152
|
+
* // ... user updates their profile ...
|
|
153
|
+
* invalidate('user:42') // next call will re-fetch
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export function invalidate(key) {
|
|
157
|
+
const cacheKey = CACHE_NS + key;
|
|
158
|
+
const existed = defaultStore.has(cacheKey);
|
|
159
|
+
defaultStore.delete(cacheKey);
|
|
160
|
+
return existed;
|
|
161
|
+
}
|
|
162
|
+
export function withStore(store) {
|
|
163
|
+
const scopedAct = async (key, fn, options = {}) => {
|
|
164
|
+
assertKey(key);
|
|
165
|
+
assertOptions(options);
|
|
166
|
+
const meta = { attempts: 1, source: 'fresh' };
|
|
167
|
+
const rootController = buildRootSignal(options.signal);
|
|
168
|
+
if (rootController.signal.aborted) {
|
|
169
|
+
return { ok: false, error: rootController.signal.reason, attempts: 0 };
|
|
170
|
+
}
|
|
171
|
+
const policies = buildPolicies(options);
|
|
172
|
+
try {
|
|
173
|
+
const value = await execute({
|
|
174
|
+
key,
|
|
175
|
+
fn,
|
|
176
|
+
policies,
|
|
177
|
+
store,
|
|
178
|
+
meta,
|
|
179
|
+
signal: rootController.signal,
|
|
180
|
+
});
|
|
181
|
+
return { ok: true, value, source: meta.source, attempts: meta.attempts };
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
return { ok: false, error, attempts: meta.attempts };
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
// Build the `invalidate` implementation. The runtime branch on
|
|
188
|
+
// `isSyncStore` selects the correct path; the cast through `unknown`
|
|
189
|
+
// is required because TypeScript cannot narrow the union return type
|
|
190
|
+
// (`boolean | Promise<boolean>`) to match either overload signature
|
|
191
|
+
// individually. The overloads at the call site guarantee callers see
|
|
192
|
+
// the correct type.
|
|
193
|
+
const invalidateImpl = (key) => {
|
|
194
|
+
assertKey(key);
|
|
195
|
+
const cacheKey = CACHE_NS + key;
|
|
196
|
+
if (isSyncStore(store)) {
|
|
197
|
+
const existed = store.has(cacheKey);
|
|
198
|
+
store.delete(cacheKey);
|
|
199
|
+
return existed;
|
|
200
|
+
}
|
|
201
|
+
// Async store branch.
|
|
202
|
+
return (async () => {
|
|
203
|
+
const existed = await store.has(cacheKey);
|
|
204
|
+
await store.delete(cacheKey);
|
|
205
|
+
return existed;
|
|
206
|
+
})();
|
|
207
|
+
};
|
|
208
|
+
// Attach `invalidate` and `store` to the function object. We use
|
|
209
|
+
// `Object.assign` rather than mutation so the types narrow cleanly at
|
|
210
|
+
// the call site. The cast through `unknown` is necessary because the
|
|
211
|
+
// implementation signature is wider than either overload.
|
|
212
|
+
return Object.assign(scopedAct, {
|
|
213
|
+
invalidate: invalidateImpl,
|
|
214
|
+
store,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
69
217
|
//# sourceMappingURL=act.js.map
|
package/dist/core/act.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"act.js","sourceRoot":"","sources":["../../src/core/act.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"act.js","sourceRoot":"","sources":["../../src/core/act.ts"],"names":[],"mappings":"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"}
|