actly 1.0.1 → 1.1.0
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 +380 -0
- package/dist/core/act.cjs +5 -3
- package/dist/core/act.d.ts.map +1 -1
- package/dist/core/act.js +4 -2
- package/dist/core/act.js.map +1 -1
- package/dist/core/executor.cjs +16 -0
- package/dist/core/executor.d.ts +9 -7
- package/dist/core/executor.d.ts.map +1 -1
- package/dist/core/executor.js +15 -0
- package/dist/core/executor.js.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/policies/cache.cjs +27 -7
- package/dist/policies/cache.d.ts +5 -0
- package/dist/policies/cache.d.ts.map +1 -1
- package/dist/policies/cache.js +27 -7
- package/dist/policies/cache.js.map +1 -1
- package/dist/policies/dedupe.cjs +24 -9
- package/dist/policies/dedupe.d.ts +7 -0
- package/dist/policies/dedupe.d.ts.map +1 -1
- package/dist/policies/dedupe.js +24 -9
- package/dist/policies/dedupe.js.map +1 -1
- package/dist/state/store.cjs +4 -38
- package/dist/state/store.d.ts +1 -12
- package/dist/state/store.d.ts.map +1 -1
- package/dist/state/store.js +3 -37
- package/dist/state/store.js.map +1 -1
- package/dist/stores/base.cjs +20 -0
- package/dist/stores/base.d.ts +112 -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 +91 -0
- package/dist/stores/memory.d.ts +58 -0
- package/dist/stores/memory.d.ts.map +1 -0
- package/dist/stores/memory.js +88 -0
- package/dist/stores/memory.js.map +1 -0
- package/dist/types/index.d.ts +20 -8
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/LICENSE
CHANGED
package/README.md
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# actly
|
|
2
|
+
|
|
3
|
+
**Reliability primitive for async functions.**
|
|
4
|
+
Retry. Timeout. Dedupe. Cache. Composable, typed, zero-throw.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install actly
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
> Requires Node 18+. Ships ESM + CJS. Zero dependencies.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## The problem
|
|
15
|
+
|
|
16
|
+
Every async call can fail. Networks blip. Services time out. The same UI mounts three times and fires the same fetch in parallel. You need retry logic, but not for 4xx errors. You need timeouts, but not the kind that let retry loops run forever.
|
|
17
|
+
|
|
18
|
+
These patterns are solved the same way every time. `actly` solves them once.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { act } from 'actly'
|
|
26
|
+
|
|
27
|
+
const result = await act('user:42', () => fetchUser(42))
|
|
28
|
+
|
|
29
|
+
if (result.ok) {
|
|
30
|
+
console.log(result.value) // T
|
|
31
|
+
console.log(result.source) // 'fresh' | 'cache'
|
|
32
|
+
console.log(result.attempts) // number
|
|
33
|
+
} else {
|
|
34
|
+
console.error(result.error) // unknown — never throws
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`act` always resolves. It never rejects. You check `.ok` and move on.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## API
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
act<T>(key: string, fn: () => Promise<T>, options?: ActOptions): Promise<ActResult<T>>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
| Argument | Type | Description |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| `key` | `string` | Stable, unique identifier. Scopes dedupe and cache state. |
|
|
51
|
+
| `fn` | `() => Promise<T>` | The async function to execute. |
|
|
52
|
+
| `options` | `ActOptions` | All optional. See policies below. |
|
|
53
|
+
|
|
54
|
+
### Result shape
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
type ActResult<T> =
|
|
58
|
+
| { ok: true; value: T; source: 'fresh' | 'cache'; attempts: number }
|
|
59
|
+
| { ok: false; error: unknown; attempts: number }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Policies
|
|
65
|
+
|
|
66
|
+
All policies are optional and compose freely. The execution order is **fixed**:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
totalTimeout → cache → dedupe → retry → timeout
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Retry
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
const result = await act('payments:charge', () => chargeCard(payload), {
|
|
76
|
+
retry: {
|
|
77
|
+
attempts: 3, // total attempts (including first)
|
|
78
|
+
delayMs: 200, // base delay between attempts (ms)
|
|
79
|
+
backoff: 'exponential' // 'none' | 'linear' | 'exponential'
|
|
80
|
+
},
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`result.attempts` tells you how many tries it took.
|
|
85
|
+
|
|
86
|
+
#### Selective retry with `shouldRetry`
|
|
87
|
+
|
|
88
|
+
By default, every error triggers a retry. Use `shouldRetry` to skip retrying permanent errors — a `404` will never recover, a `503` might.
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const result = await act('api:resource', () => fetchResource(id), {
|
|
92
|
+
retry: {
|
|
93
|
+
attempts: 4,
|
|
94
|
+
delayMs: 150,
|
|
95
|
+
backoff: 'exponential',
|
|
96
|
+
shouldRetry: (err, attempt) => {
|
|
97
|
+
if (err instanceof HttpError && err.status < 500) return false
|
|
98
|
+
return true
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
})
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`shouldRetry(error, attempt)` receives the error and the 1-based attempt number. Return `false` to surface the error immediately. Not called on the final attempt — that always surfaces.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
### Timeout
|
|
109
|
+
|
|
110
|
+
Per-attempt deadline. Each retry gets a fresh clock.
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
import { act, TimeoutError } from 'actly'
|
|
114
|
+
|
|
115
|
+
const result = await act('geo:lookup', () => lookupCoords(ip), {
|
|
116
|
+
timeout: { ms: 3_000 },
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
if (!result.ok && result.error instanceof TimeoutError) {
|
|
120
|
+
console.log(`timed out after ${result.error.ms}ms`)
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### Total timeout
|
|
127
|
+
|
|
128
|
+
Hard budget over the **entire** operation — all attempts, delays, and per-attempt timeouts combined. The per-attempt `timeout` resets on retry; `totalTimeout` does not.
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import { act, TimeoutError, TotalTimeoutError } from 'actly'
|
|
132
|
+
|
|
133
|
+
const result = await act('search:query', () => runQuery(q), {
|
|
134
|
+
retry: { attempts: 5, delayMs: 100, backoff: 'linear' },
|
|
135
|
+
timeout: { ms: 2_000 }, // per attempt
|
|
136
|
+
totalTimeout: { ms: 8_000 }, // whole operation
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
if (!result.ok) {
|
|
140
|
+
if (result.error instanceof TotalTimeoutError) {
|
|
141
|
+
console.log(`budget exhausted after ${result.error.ms}ms`)
|
|
142
|
+
} else if (result.error instanceof TimeoutError) {
|
|
143
|
+
console.log(`last attempt timed out after ${result.error.ms}ms`)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`TotalTimeoutError` and `TimeoutError` are separate classes — `instanceof` distinguishes which deadline fired.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### Dedupe
|
|
153
|
+
|
|
154
|
+
Concurrent calls with the same key collapse into one in-flight Promise. The first caller executes; the rest wait and receive the same result. After settlement, the next call starts fresh.
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
const result = await act('config:load', () => loadRemoteConfig(), {
|
|
158
|
+
dedupe: true,
|
|
159
|
+
})
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`dedupe: { enabled: true }` is also valid — object form for forward compatibility.
|
|
163
|
+
|
|
164
|
+
> **Note:** Deduped callers (those that joined an in-flight Promise) always receive `attempts: 1` in their result, because the retry counter belongs to the originating call. This is a known trade-off.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### Cache
|
|
169
|
+
|
|
170
|
+
Stores successful results for `ttl` milliseconds. Failures are never cached. A cache hit short-circuits everything — dedupe, retry, and timeout are all skipped.
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
const result = await act('flags:all', () => fetchFeatureFlags(), {
|
|
174
|
+
cache: { ttl: 60_000 },
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
console.log(result.source) // 'fresh' on miss, 'cache' on hit
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
### Combining policies
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
const result = await act('product:detail', () => fetchProduct(id), {
|
|
186
|
+
totalTimeout: { ms: 10_000 }, // outermost wall
|
|
187
|
+
cache: { ttl: 30_000 }, // short-circuits on hit
|
|
188
|
+
dedupe: true, // collapses concurrent calls
|
|
189
|
+
retry: { attempts: 3, delayMs: 100, backoff: 'linear' },
|
|
190
|
+
timeout: { ms: 3_000 }, // per attempt
|
|
191
|
+
})
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Options reference
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
interface ActOptions {
|
|
200
|
+
retry?: RetryOptions
|
|
201
|
+
timeout?: TimeoutOptions // per-attempt
|
|
202
|
+
totalTimeout?: TimeoutOptions // entire operation
|
|
203
|
+
dedupe?: boolean | DedupeOptions
|
|
204
|
+
cache?: CacheOptions
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
interface RetryOptions {
|
|
208
|
+
attempts: number
|
|
209
|
+
delayMs?: number
|
|
210
|
+
backoff?: 'none' | 'linear' | 'exponential'
|
|
211
|
+
shouldRetry?: (error: unknown, attempt: number) => boolean
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
interface TimeoutOptions { ms: number }
|
|
215
|
+
interface DedupeOptions { enabled: boolean }
|
|
216
|
+
interface CacheOptions { ttl: number }
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Exported values
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
import {
|
|
225
|
+
act, // primary function
|
|
226
|
+
InMemoryStore, // isolated store for SSR / tests
|
|
227
|
+
TimeoutError, // per-attempt deadline (.ms)
|
|
228
|
+
TotalTimeoutError, // total budget exhausted (.ms)
|
|
229
|
+
} from 'actly'
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Exported types
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
import type {
|
|
238
|
+
ActResult, ActSuccess, ActFailure, ActSource,
|
|
239
|
+
ActOptions, ActFn,
|
|
240
|
+
RetryOptions, TimeoutOptions, DedupeOptions, CacheOptions,
|
|
241
|
+
// v1.1 — store interfaces
|
|
242
|
+
SyncStateStore,
|
|
243
|
+
AsyncStateStore,
|
|
244
|
+
InMemoryStoreOptions,
|
|
245
|
+
// v1.0 alias — still valid, zero migration needed
|
|
246
|
+
StateStore,
|
|
247
|
+
} from 'actly'
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Custom store
|
|
253
|
+
|
|
254
|
+
By default `act` uses a module-level `InMemoryStore`. For SSR request isolation or test control, instantiate your own:
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
import { InMemoryStore } from 'actly'
|
|
258
|
+
|
|
259
|
+
// Basic
|
|
260
|
+
const store = new InMemoryStore()
|
|
261
|
+
|
|
262
|
+
// With background cleanup (useful for long-lived server-side stores)
|
|
263
|
+
const store = new InMemoryStore({
|
|
264
|
+
autoCleanup: true,
|
|
265
|
+
cleanupIntervalMs: 60_000, // sweep every 60s (default: 30s)
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
// Always call destroy() when done to prevent timer leaks
|
|
269
|
+
store.destroy()
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
`InMemoryStore` satisfies `SyncStateStore`. Use it with `execute()` directly for full control.
|
|
273
|
+
|
|
274
|
+
### Async store (v1.1+)
|
|
275
|
+
|
|
276
|
+
For external cache backends (Redis, Upstash, etc.), implement `AsyncStateStore`:
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
import type { AsyncStateStore } from 'actly'
|
|
280
|
+
|
|
281
|
+
class RedisStore implements AsyncStateStore {
|
|
282
|
+
readonly _sync = false as const
|
|
283
|
+
|
|
284
|
+
async get<T>(key: string): Promise<T | undefined> { /* ... */ }
|
|
285
|
+
async set<T>(key: string, value: T, ttlMs?: number): Promise<void> { /* ... */ }
|
|
286
|
+
async delete(key: string): Promise<void> { /* ... */ }
|
|
287
|
+
async has(key: string): Promise<boolean> { /* ... */ }
|
|
288
|
+
async clear(): Promise<void> { /* ... */ }
|
|
289
|
+
async size(): Promise<number> { /* ... */ }
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
> `AsyncStateStore` is compatible with `cache` only. Using it with `dedupe` is a TypeScript error and a runtime error — dedupe requires synchronous store access. See `SyncStateStore` docs for the reason.
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Store interfaces
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
interface SyncStateStore {
|
|
301
|
+
readonly _sync: true
|
|
302
|
+
get<T>(key: string): T | undefined
|
|
303
|
+
set<T>(key: string, value: T, ttlMs?: number): void
|
|
304
|
+
delete(key: string): void
|
|
305
|
+
has(key: string): boolean
|
|
306
|
+
clear(): void
|
|
307
|
+
size(): number
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
interface AsyncStateStore {
|
|
311
|
+
readonly _sync: false
|
|
312
|
+
get<T>(key: string): Promise<T | undefined>
|
|
313
|
+
set<T>(key: string, value: T, ttlMs?: number): Promise<void>
|
|
314
|
+
delete(key: string): Promise<void>
|
|
315
|
+
has(key: string): Promise<boolean>
|
|
316
|
+
clear(): Promise<void>
|
|
317
|
+
size(): Promise<number>
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
The `_sync` discriminant is read at runtime by the executor to enforce the dedupe constraint. Set it as a `readonly` literal — `true as const` or `false as const`.
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Zero-throw contract
|
|
326
|
+
|
|
327
|
+
`act()` always resolves. Under any condition — network error, thrown exception, timeout, total budget exhaustion — the result is an `ActFailure`, not a rejected promise. This contract holds across all versions.
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Philosophy
|
|
332
|
+
|
|
333
|
+
One function. One return type. No exceptions in userland.
|
|
334
|
+
|
|
335
|
+
The key is your responsibility. Make it stable and specific. `user:42` is good. `fetch` is not.
|
|
336
|
+
|
|
337
|
+
Policies are composable but intentionally constrained. There is no builder API, no middleware system, no hooks. If you need something `act` doesn't do, write a wrapper around it — that's the right boundary.
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## Changelog
|
|
342
|
+
|
|
343
|
+
### v1.1.0 — 2026-06-01
|
|
344
|
+
|
|
345
|
+
**New features**
|
|
346
|
+
|
|
347
|
+
- **`AsyncStateStore` interface** — plug in any async key-value backend (Redis, Upstash, Cloudflare KV). Compatible with `cache` policy. Using with `dedupe` is a compile-time and runtime error by design.
|
|
348
|
+
- **`SyncStateStore` interface** — the canonical public name for what was previously the internal store shape. `StateStore` is preserved as an alias with zero breakage for existing code.
|
|
349
|
+
- **`InMemoryStore` is now a public export** — construct isolated stores for SSR request isolation, per-test control, or multi-tenant scenarios. Accepts `InMemoryStoreOptions`.
|
|
350
|
+
- **`InMemoryStoreOptions`** — opt-in `autoCleanup` with configurable `cleanupIntervalMs`. Call `destroy()` to stop the background timer and prevent leaks.
|
|
351
|
+
- **`TotalTimeoutError`** — new error class thrown when `totalTimeout` fires. Distinct from `TimeoutError` (per-attempt) so `instanceof` tells you which deadline fired.
|
|
352
|
+
- **`totalTimeout` option** — hard wall-clock budget over the entire operation including all retry attempts and delays. Use alongside `timeout` to express both per-attempt and total constraints.
|
|
353
|
+
- **`dedupe: true` shorthand** — equivalent to `dedupe: { enabled: true }`. Object form still valid for forward compatibility.
|
|
354
|
+
- **`isSyncStore()` / `isAsyncStore()` type guards** — exported from `actly` for consumers building custom policy chains or store adapters.
|
|
355
|
+
|
|
356
|
+
**Internal improvements**
|
|
357
|
+
|
|
358
|
+
- Executor validates sync-store requirement at chain-build time — async store + `dedupePolicy` throws immediately with a clear message instead of producing silent correctness failures.
|
|
359
|
+
- `dedupePolicy` tagged with `REQUIRES_SYNC_STORE` symbol — allows executor to detect the constraint without importing the policy module (avoids circular deps).
|
|
360
|
+
- `cachePolicy` now branches sync/async paths — fast synchronous path for `InMemoryStore`, async-await path for external stores.
|
|
361
|
+
|
|
362
|
+
**Non-breaking changes**
|
|
363
|
+
|
|
364
|
+
- `StateStore` preserved as type alias for `SyncStateStore` — all v1.0 code compiles without changes.
|
|
365
|
+
- All new exports are purely additive.
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
### v1.0.1
|
|
370
|
+
|
|
371
|
+
- Initial stable release.
|
|
372
|
+
- `act()`, `retry`, `timeout`, `totalTimeout`, `dedupe`, `cache`.
|
|
373
|
+
- `TimeoutError` exported for `instanceof` checks.
|
|
374
|
+
- Zero dependencies. ESM + CJS. Node 18+.
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## License
|
|
379
|
+
|
|
380
|
+
MIT
|
package/dist/core/act.cjs
CHANGED
|
@@ -6,11 +6,13 @@ const retry_js_1 = require("../policies/retry.js");
|
|
|
6
6
|
const timeout_js_1 = require("../policies/timeout.js");
|
|
7
7
|
const dedupe_js_1 = require("../policies/dedupe.js");
|
|
8
8
|
const cache_js_1 = require("../policies/cache.js");
|
|
9
|
-
const
|
|
9
|
+
const memory_js_1 = require("../stores/memory.js");
|
|
10
10
|
// Module-level default store so cache and dedupe persist across calls.
|
|
11
|
+
// Always an InMemoryStore (SyncStateStore) — required because the default
|
|
12
|
+
// chain may include dedupePolicy, which mandates synchronous store access.
|
|
11
13
|
// For SSR isolation or per-test control, construct an InMemoryStore and
|
|
12
|
-
// call execute() directly
|
|
13
|
-
const defaultStore = new
|
|
14
|
+
// call execute() directly with an explicit store.
|
|
15
|
+
const defaultStore = new memory_js_1.InMemoryStore();
|
|
14
16
|
/**
|
|
15
17
|
* Execute fn with the given reliability policies.
|
|
16
18
|
*
|
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,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAA0B,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"act.d.ts","sourceRoot":"","sources":["../../src/core/act.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAA0B,MAAM,mBAAmB,CAAA;AAe7F;;;;;;;;;;;;;;;;;;;;;;;GAuBG;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,CAwCvB"}
|
package/dist/core/act.js
CHANGED
|
@@ -3,10 +3,12 @@ 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
7
|
// Module-level default store so cache and dedupe persist across calls.
|
|
8
|
+
// Always an InMemoryStore (SyncStateStore) — required because the default
|
|
9
|
+
// chain may include dedupePolicy, which mandates synchronous store access.
|
|
8
10
|
// For SSR isolation or per-test control, construct an InMemoryStore and
|
|
9
|
-
// call execute() directly
|
|
11
|
+
// call execute() directly with an explicit store.
|
|
10
12
|
const defaultStore = new InMemoryStore();
|
|
11
13
|
/**
|
|
12
14
|
* Execute fn with the given reliability policies.
|
package/dist/core/act.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"act.js","sourceRoot":"","sources":["../../src/core/act.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAa,eAAe,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAS,sBAAsB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAQ,uBAAuB,CAAA;AACtD,OAAO,EAAE,WAAW,EAAE,MAAS,sBAAsB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAO,
|
|
1
|
+
{"version":3,"file":"act.js","sourceRoot":"","sources":["../../src/core/act.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAa,eAAe,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAS,sBAAsB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAQ,uBAAuB,CAAA;AACtD,OAAO,EAAE,WAAW,EAAE,MAAS,sBAAsB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAO,qBAAqB,CAAA;AAEpD,uEAAuE;AACvE,0EAA0E;AAC1E,2EAA2E;AAC3E,wEAAwE;AACxE,kDAAkD;AAClD,MAAM,YAAY,GAAG,IAAI,aAAa,EAAE,CAAA;AAExC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,KAAK,UAAU,GAAG,CACvB,GAAW,EACX,EAAY,EACZ,UAAsB,EAAE;IAExB,MAAM,IAAI,GAAY,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAA;IAEtD,yEAAyE;IACzE,qCAAqC;IACrC,MAAM,MAAM,GAAG,OAAO,OAAO,CAAC,MAAM,KAAK,SAAS;QAChD,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE;QAC7B,CAAC,CAAC,OAAO,CAAC,MAAM,CAAA;IAElB,yEAAyE;IACzE,MAAM,QAAQ,GAA4B,EAAE,CAAA;IAE5C,2EAA2E;IAC3E,8EAA8E;IAC9E,IAAI,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;QACxD,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAAI,OAAO,CAAC,YAAY,CAAC,CAAC,CAAA,CAAC,wBAAwB;IACrF,CAAC;IAED,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;QAC3C,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA,CAAe,qBAAqB;IAClF,CAAC;IAED,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;QACpB,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAK,CAAC,CAAA,CAA2B,iCAAiC;IAC9F,CAAC;IAED,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;QAChD,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA,CAAe,0BAA0B;IACvF,CAAC;IAED,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;QAC9C,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAI,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA,CAAW,mCAAmC;IAChG,CAAC;IAED,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAA;QAC7E,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAA;IAC1E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAA;IACtD,CAAC;AACH,CAAC"}
|
package/dist/core/executor.cjs
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.REQUIRES_SYNC_STORE = void 0;
|
|
3
4
|
exports.execute = execute;
|
|
5
|
+
const base_js_1 = require("../stores/base.js");
|
|
6
|
+
// Symbol stamped onto PolicyApplier functions by dedupePolicy.
|
|
7
|
+
// Lets execute() detect a dedupe policy without importing the policy module
|
|
8
|
+
// (which would create a circular dep) or doing fragile name-sniffing.
|
|
9
|
+
exports.REQUIRES_SYNC_STORE = Symbol('actly.requiresSyncStore');
|
|
4
10
|
/**
|
|
5
11
|
* Pure execution engine.
|
|
6
12
|
*
|
|
@@ -9,6 +15,16 @@ exports.execute = execute;
|
|
|
9
15
|
* Policy implementations live in /policies and are wired in core/act.ts.
|
|
10
16
|
*/
|
|
11
17
|
async function execute(input) {
|
|
18
|
+
// Guard: if any policy in the chain requires a sync store, the provided
|
|
19
|
+
// store must be synchronous. An async store + dedupePolicy is a silent
|
|
20
|
+
// correctness failure, not just a performance issue — catch it here rather
|
|
21
|
+
// than letting it produce subtly wrong dedupe behaviour at runtime.
|
|
22
|
+
const needsSync = input.policies.some(p => p[exports.REQUIRES_SYNC_STORE]);
|
|
23
|
+
if (needsSync && !(0, base_js_1.isSyncStore)(input.store)) {
|
|
24
|
+
throw new Error('Actly: dedupePolicy requires a SyncStateStore (store._sync === true). ' +
|
|
25
|
+
'The provided store does not satisfy this constraint. ' +
|
|
26
|
+
'Either remove dedupe from the policy chain or use InMemoryStore.');
|
|
27
|
+
}
|
|
12
28
|
const ctx = {
|
|
13
29
|
key: input.key,
|
|
14
30
|
store: input.store,
|
package/dist/core/executor.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { ActFn, PolicyApplier,
|
|
1
|
+
import type { ActFn, PolicyApplier, AnyStateStore, RunMeta } from '../types/index.js';
|
|
2
|
+
export declare const REQUIRES_SYNC_STORE: unique symbol;
|
|
2
3
|
export interface ExecutorInput<T> {
|
|
3
4
|
key: string;
|
|
4
5
|
fn: ActFn<T>;
|
|
@@ -6,14 +7,15 @@ export interface ExecutorInput<T> {
|
|
|
6
7
|
* Policies ordered outermost -> innermost.
|
|
7
8
|
* policies[0] intercepts first; policies[last] is closest to fn.
|
|
8
9
|
*
|
|
9
|
-
* Canonical order: [cache, dedupe, retry, timeout]
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* Canonical order: [totalTimeout, cache, dedupe, retry, timeout]
|
|
11
|
+
* totalTimeout -> hard wall-clock budget over the entire operation
|
|
12
|
+
* cache -> a hit skips everything below it
|
|
13
|
+
* dedupe -> collapses concurrent callers before retry fires
|
|
14
|
+
* retry -> owns the attempt loop
|
|
15
|
+
* timeout -> each individual attempt races against the clock
|
|
14
16
|
*/
|
|
15
17
|
policies: ReadonlyArray<PolicyApplier<T>>;
|
|
16
|
-
store:
|
|
18
|
+
store: AnyStateStore;
|
|
17
19
|
meta: RunMeta;
|
|
18
20
|
}
|
|
19
21
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/core/executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,KAAK,EACL,aAAa,EAEb,
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/core/executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,KAAK,EACL,aAAa,EAEb,aAAa,EACb,OAAO,EACR,MAAM,mBAAmB,CAAA;AAM1B,eAAO,MAAM,mBAAmB,eAAoC,CAAA;AAEpE,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,GAAG,EAAO,MAAM,CAAA;IAChB,EAAE,EAAQ,KAAK,CAAC,CAAC,CAAC,CAAA;IAClB;;;;;;;;;;OAUG;IACH,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAA;IACzC,KAAK,EAAK,aAAa,CAAA;IACvB,IAAI,EAAM,OAAO,CAAA;CAClB;AAED;;;;;;GAMG;AACH,wBAAsB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CA8BpE"}
|
package/dist/core/executor.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { isSyncStore } from '../stores/base.js';
|
|
2
|
+
// Symbol stamped onto PolicyApplier functions by dedupePolicy.
|
|
3
|
+
// Lets execute() detect a dedupe policy without importing the policy module
|
|
4
|
+
// (which would create a circular dep) or doing fragile name-sniffing.
|
|
5
|
+
export const REQUIRES_SYNC_STORE = Symbol('actly.requiresSyncStore');
|
|
1
6
|
/**
|
|
2
7
|
* Pure execution engine.
|
|
3
8
|
*
|
|
@@ -6,6 +11,16 @@
|
|
|
6
11
|
* Policy implementations live in /policies and are wired in core/act.ts.
|
|
7
12
|
*/
|
|
8
13
|
export async function execute(input) {
|
|
14
|
+
// Guard: if any policy in the chain requires a sync store, the provided
|
|
15
|
+
// store must be synchronous. An async store + dedupePolicy is a silent
|
|
16
|
+
// correctness failure, not just a performance issue — catch it here rather
|
|
17
|
+
// than letting it produce subtly wrong dedupe behaviour at runtime.
|
|
18
|
+
const needsSync = input.policies.some(p => p[REQUIRES_SYNC_STORE]);
|
|
19
|
+
if (needsSync && !isSyncStore(input.store)) {
|
|
20
|
+
throw new Error('Actly: dedupePolicy requires a SyncStateStore (store._sync === true). ' +
|
|
21
|
+
'The provided store does not satisfy this constraint. ' +
|
|
22
|
+
'Either remove dedupe from the policy chain or use InMemoryStore.');
|
|
23
|
+
}
|
|
9
24
|
const ctx = {
|
|
10
25
|
key: input.key,
|
|
11
26
|
store: input.store,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../src/core/executor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../src/core/executor.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/C,+DAA+D;AAC/D,4EAA4E;AAC5E,sEAAsE;AACtE,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,yBAAyB,CAAC,CAAA;AAqBpE;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAI,KAAuB;IACtD,wEAAwE;IACxE,uEAAuE;IACvE,2EAA2E;IAC3E,oEAAoE;IACpE,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CACnC,CAAC,CAAC,EAAE,CAAE,CAA4D,CAAC,mBAAmB,CAAC,CACxF,CAAA;IACD,IAAI,SAAS,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,wEAAwE;YACxE,uDAAuD;YACvD,kEAAkE,CACnE,CAAA;IACH,CAAC;IAED,MAAM,GAAG,GAAkB;QACzB,GAAG,EAAI,KAAK,CAAC,GAAG;QAChB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,IAAI,EAAG,KAAK,CAAC,IAAI;KAClB,CAAA;IAED,wCAAwC;IACxC,8EAA8E;IAC9E,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,WAAW,CACxC,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,GAAG,CAAC,EAC/C,KAAK,CAAC,EAAE,CACT,CAAA;IAED,OAAO,OAAO,EAAE,CAAA;AAClB,CAAC"}
|
package/dist/index.cjs
CHANGED
|
@@ -4,8 +4,8 @@ exports.TotalTimeoutError = exports.TimeoutError = exports.InMemoryStore = expor
|
|
|
4
4
|
var act_js_1 = require("./core/act.js");
|
|
5
5
|
Object.defineProperty(exports, "act", { enumerable: true, get: function () { return act_js_1.act; } });
|
|
6
6
|
// Exported so consumers can build isolated stores (e.g. per-request in SSR)
|
|
7
|
-
var
|
|
8
|
-
Object.defineProperty(exports, "InMemoryStore", { enumerable: true, get: function () { return
|
|
7
|
+
var memory_js_1 = require("./stores/memory.js");
|
|
8
|
+
Object.defineProperty(exports, "InMemoryStore", { enumerable: true, get: function () { return memory_js_1.InMemoryStore; } });
|
|
9
9
|
// Exported so callers can instanceof-check against timeout failures
|
|
10
10
|
var timeout_js_1 = require("./policies/timeout.js");
|
|
11
11
|
Object.defineProperty(exports, "TimeoutError", { enumerable: true, get: function () { return timeout_js_1.TimeoutError; } });
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { act } from './core/act.js';
|
|
2
|
-
export type { ActFn, ActResult, ActSuccess, ActFailure, ActSource, ActOptions, RetryOptions, TimeoutOptions, DedupeOptions, CacheOptions, StateStore, } from './types/index.js';
|
|
3
|
-
export { InMemoryStore } from './
|
|
2
|
+
export type { ActFn, ActResult, ActSuccess, ActFailure, ActSource, ActOptions, RetryOptions, TimeoutOptions, DedupeOptions, CacheOptions, StateStore, SyncStateStore, AsyncStateStore, } from './types/index.js';
|
|
3
|
+
export { InMemoryStore } from './stores/memory.js';
|
|
4
|
+
export type { InMemoryStoreOptions } from './stores/memory.js';
|
|
4
5
|
export { TimeoutError, TotalTimeoutError } from './policies/timeout.js';
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AAEnC,YAAY,EACV,KAAK,EACL,SAAS,EACT,UAAU,EACV,UAAU,EACV,SAAS,EACT,UAAU,EACV,YAAY,EACZ,cAAc,EACd,aAAa,EACb,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AAEnC,YAAY,EACV,KAAK,EACL,SAAS,EACT,UAAU,EACV,UAAU,EACV,SAAS,EACT,UAAU,EACV,YAAY,EACZ,cAAc,EACd,aAAa,EACb,YAAY,EAEZ,UAAU,EAEV,cAAc,EACd,eAAe,GAChB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAG9D,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { act } from './core/act.js';
|
|
2
2
|
// Exported so consumers can build isolated stores (e.g. per-request in SSR)
|
|
3
|
-
export { InMemoryStore } from './
|
|
3
|
+
export { InMemoryStore } from './stores/memory.js';
|
|
4
4
|
// Exported so callers can instanceof-check against timeout failures
|
|
5
5
|
export { TimeoutError, TotalTimeoutError } from './policies/timeout.js';
|
|
6
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AAoBnC,4EAA4E;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAGlD,oEAAoE;AACpE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA"}
|
package/dist/policies/cache.cjs
CHANGED
|
@@ -1,23 +1,43 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.cachePolicy = cachePolicy;
|
|
4
|
+
const base_js_1 = require("../stores/base.js");
|
|
4
5
|
const NS = 'cache:';
|
|
5
6
|
/**
|
|
6
7
|
* Short-circuits the entire downstream chain on a cache hit.
|
|
7
8
|
* On a miss, runs fn and stores the result with TTL.
|
|
8
9
|
*
|
|
9
10
|
* Must be the OUTERMOST policy so a hit skips dedupe, timeout, and retry.
|
|
11
|
+
*
|
|
12
|
+
* Supports both SyncStateStore and AsyncStateStore. The async branch
|
|
13
|
+
* introduces two await points (get + set) but has no correctness
|
|
14
|
+
* requirement for same-tick execution — worst case is a cache stampede
|
|
15
|
+
* on a simultaneous miss, which is standard behaviour without locking.
|
|
10
16
|
*/
|
|
11
17
|
function cachePolicy(opts) {
|
|
12
18
|
return (fn, ctx) => async () => {
|
|
13
19
|
const key = NS + ctx.key;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
ctx.
|
|
17
|
-
|
|
20
|
+
if ((0, base_js_1.isSyncStore)(ctx.store)) {
|
|
21
|
+
// Fast path — synchronous store, no await needed
|
|
22
|
+
const hit = ctx.store.get(key);
|
|
23
|
+
if (hit) {
|
|
24
|
+
ctx.meta.source = 'cache';
|
|
25
|
+
return hit.value;
|
|
26
|
+
}
|
|
27
|
+
const value = await fn();
|
|
28
|
+
ctx.store.set(key, { value }, opts.ttl);
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// Async store path
|
|
33
|
+
const hit = await ctx.store.get(key);
|
|
34
|
+
if (hit) {
|
|
35
|
+
ctx.meta.source = 'cache';
|
|
36
|
+
return hit.value;
|
|
37
|
+
}
|
|
38
|
+
const value = await fn();
|
|
39
|
+
await ctx.store.set(key, { value }, opts.ttl);
|
|
40
|
+
return value;
|
|
18
41
|
}
|
|
19
|
-
const value = await fn();
|
|
20
|
-
ctx.store.set(key, { value }, opts.ttl);
|
|
21
|
-
return value;
|
|
22
42
|
};
|
|
23
43
|
}
|
package/dist/policies/cache.d.ts
CHANGED
|
@@ -4,6 +4,11 @@ import type { CacheOptions, PolicyApplier } from '../types/index.js';
|
|
|
4
4
|
* On a miss, runs fn and stores the result with TTL.
|
|
5
5
|
*
|
|
6
6
|
* Must be the OUTERMOST policy so a hit skips dedupe, timeout, and retry.
|
|
7
|
+
*
|
|
8
|
+
* Supports both SyncStateStore and AsyncStateStore. The async branch
|
|
9
|
+
* introduces two await points (get + set) but has no correctness
|
|
10
|
+
* requirement for same-tick execution — worst case is a cache stampede
|
|
11
|
+
* on a simultaneous miss, which is standard behaviour without locking.
|
|
7
12
|
*/
|
|
8
13
|
export declare function cachePolicy<T>(opts: CacheOptions): PolicyApplier<T>;
|
|
9
14
|
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/policies/cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,YAAY,EAAE,aAAa,EAAiB,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/policies/cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,YAAY,EAAE,aAAa,EAAiB,MAAM,mBAAmB,CAAA;AAU1F;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,GAAG,aAAa,CAAC,CAAC,CAAC,CA6BnE"}
|