@demlik/tea 0.14.0 → 0.16.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/README.md +30 -7
- package/dist/agent/index.d.ts +49 -36
- package/dist/agent/index.js +12 -8
- package/dist/chunk-4LPY67E4.js +18 -0
- package/dist/chunk-4LPY67E4.js.map +1 -0
- package/dist/chunk-5XU6ZJO2.js +533 -0
- package/dist/chunk-5XU6ZJO2.js.map +1 -0
- package/dist/{chunk-AQ6ZIMEA.js → chunk-6NG4V5SB.js} +89 -115
- package/dist/chunk-6NG4V5SB.js.map +1 -0
- package/dist/{chunk-HYDWG35Q.js → chunk-7MGXQZPW.js} +11 -9
- package/dist/chunk-7MGXQZPW.js.map +1 -0
- package/dist/{chunk-BLJ2JQSH.js → chunk-BCTEOVOU.js} +3 -3
- package/dist/{chunk-BLJ2JQSH.js.map → chunk-BCTEOVOU.js.map} +1 -1
- package/dist/chunk-CH5KTOEZ.js +710 -0
- package/dist/chunk-CH5KTOEZ.js.map +1 -0
- package/dist/chunk-CIHSHLQX.js +64 -0
- package/dist/chunk-CIHSHLQX.js.map +1 -0
- package/dist/{chunk-Z2L3LSNV.js → chunk-EHUDXAKC.js} +246 -210
- package/dist/chunk-EHUDXAKC.js.map +1 -0
- package/dist/chunk-GOX2CYIM.js +380 -0
- package/dist/chunk-GOX2CYIM.js.map +1 -0
- package/dist/chunk-IVJP3GKJ.js +104 -0
- package/dist/chunk-IVJP3GKJ.js.map +1 -0
- package/dist/{chunk-F7FMAWLA.js → chunk-MVZS5FDM.js} +13 -6
- package/dist/chunk-MVZS5FDM.js.map +1 -0
- package/dist/{chunk-LYLTKJWM.js → chunk-MYNLY3HP.js} +9 -8
- package/dist/chunk-MYNLY3HP.js.map +1 -0
- package/dist/{chunk-I5MQMJDW.js → chunk-NSIAORRY.js} +20 -30
- package/dist/chunk-NSIAORRY.js.map +1 -0
- package/dist/{chunk-MYXFNWAJ.js → chunk-QNGYQMZB.js} +4 -4
- package/dist/chunk-QNGYQMZB.js.map +1 -0
- package/dist/chunk-WT5PBPLB.js +98 -0
- package/dist/chunk-WT5PBPLB.js.map +1 -0
- package/dist/{core-CTzMJ82M.d.ts → core-BbgtiXdg.d.ts} +231 -139
- package/dist/do/index.d.ts +37 -32
- package/dist/do/index.js +21 -14
- package/dist/do/index.js.map +1 -1
- package/dist/{durable-effects-B0-qGQDy.d.ts → durable-effects-7MTK_jnj.d.ts} +1 -1
- package/dist/effect/index.d.ts +151 -0
- package/dist/effect/index.js +98 -0
- package/dist/effect/index.js.map +1 -0
- package/dist/extension/index.d.ts +6 -7
- package/dist/extension/index.js +3 -2
- package/dist/extension/index.js.map +1 -1
- package/dist/flow/index.d.ts +68 -70
- package/dist/flow/index.js +32 -27
- package/dist/flow/index.js.map +1 -1
- package/dist/idempotency/index.d.ts +6 -7
- package/dist/idempotency/index.js +4 -2
- package/dist/{index-DJWIER_3.d.ts → index-7vV8xmsD.d.ts} +1 -1
- package/dist/{index-NjrT3fL2.d.ts → index-Bz5Rh9Ga.d.ts} +19 -96
- package/dist/index-CjFDUBeq.d.ts +207 -0
- package/dist/{index-6A7dB8f6.d.ts → index-CkFYA9Y6.d.ts} +14 -16
- package/dist/{index-B5BZ-_cT.d.ts → index-Cou86-GX.d.ts} +189 -108
- package/dist/{index-TvRb1KVf.d.ts → index-DU6JpQ8G.d.ts} +1 -1
- package/dist/{index-q0hXFeZ0.d.ts → index-Xufl55W8.d.ts} +286 -215
- package/dist/{index-4vZzljCD.d.ts → index-_dOR3xqA.d.ts} +57 -45
- package/dist/index.d.ts +181 -385
- package/dist/index.js +2 -1
- package/dist/jev/index.d.ts +147 -99
- package/dist/jev/index.js +329 -233
- package/dist/jev/index.js.map +1 -1
- package/dist/{machine-CdIcN2Xm.d.ts → machine-BQzTV8On.d.ts} +171 -280
- package/dist/machine-viz/index.d.ts +5 -5
- package/dist/machine-viz/index.js +8 -4
- package/dist/machine-viz/index.js.map +1 -1
- package/dist/mem/index.d.ts +3 -4
- package/dist/mem/index.js +2 -1
- package/dist/mem/index.js.map +1 -1
- package/dist/node/index.d.ts +119 -78
- package/dist/node/index.js +52 -38
- package/dist/node/index.js.map +1 -1
- package/dist/paginate/index.d.ts +27 -33
- package/dist/paginate/index.js +6 -4
- package/dist/parity/index.d.ts +4 -5
- package/dist/parity/index.js +3 -2
- package/dist/parity/index.js.map +1 -1
- package/dist/pbt/index.d.ts +2 -2
- package/dist/pbt/index.js +2 -1
- package/dist/pbt/index.js.map +1 -1
- package/dist/persistence/index.d.ts +6 -7
- package/dist/persistence/index.js +5 -3
- package/dist/promise/index.d.ts +186 -0
- package/dist/promise/index.js +5 -0
- package/dist/promise/index.js.map +1 -0
- package/dist/protocol-ClRZBhHQ.d.ts +53 -0
- package/dist/react/index.d.ts +36 -17
- package/dist/react/index.js +27 -4
- package/dist/react/index.js.map +1 -1
- package/dist/resilience/index.d.ts +63 -455
- package/dist/resilience/index.js +37 -483
- package/dist/resilience/index.js.map +1 -1
- package/dist/{runtime-types-LWDoi943.d.ts → runtime-types-CiVpC0HN.d.ts} +131 -388
- package/dist/testing/index.d.ts +165 -15
- package/dist/testing/index.js +104 -3
- package/dist/testing/index.js.map +1 -1
- package/dist/timing/index.d.ts +25 -22
- package/dist/timing/index.js +4 -3
- package/dist/timing/index.js.map +1 -1
- package/dist/{types-DP3f5NIl.d.ts → types-LxZxGYda.d.ts} +2 -2
- package/dist/work-queue/index.d.ts +5 -6
- package/package.json +18 -5
- package/dist/chunk-AQ6ZIMEA.js.map +0 -1
- package/dist/chunk-F2US5BW6.js +0 -72
- package/dist/chunk-F2US5BW6.js.map +0 -1
- package/dist/chunk-F7FMAWLA.js.map +0 -1
- package/dist/chunk-HUKZQX6U.js +0 -97
- package/dist/chunk-HUKZQX6U.js.map +0 -1
- package/dist/chunk-HYDWG35Q.js.map +0 -1
- package/dist/chunk-I5MQMJDW.js.map +0 -1
- package/dist/chunk-LYLTKJWM.js.map +0 -1
- package/dist/chunk-MYXFNWAJ.js.map +0 -1
- package/dist/chunk-RQULHGYH.js +0 -1804
- package/dist/chunk-RQULHGYH.js.map +0 -1
- package/dist/chunk-Z2L3LSNV.js.map +0 -1
- package/dist/index-DMCeMPMJ.d.ts +0 -272
|
@@ -1,161 +1,21 @@
|
|
|
1
|
-
import { S as Sub,
|
|
2
|
-
import { S as SubscribeHandler } from './types-
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* internal/resilience/cache — a TTL cache as pure state, PLUS a periodic eviction Sub.
|
|
6
|
-
*
|
|
7
|
-
* Same pure-state-ops shape as `../../idempotency/` and
|
|
8
|
-
* `../../work-queue/ops.ts`: a state type plus pure transition functions. Host-
|
|
9
|
-
* agnostic by construction — no `Date.now()`, no timers, no I/O inside the ops.
|
|
10
|
-
* The caller injects the clock (`nowMs`) and folds the result through its own
|
|
11
|
-
* reducer, keeping the store inside TEA's invariant 2 (transitions are pure:
|
|
12
|
-
* same inputs → same outputs) so a consumer can exercise it with `replay`
|
|
13
|
-
* without a fake clock.
|
|
14
|
-
*
|
|
15
|
-
* Every op returns a NEW cache value and never mutates its input (invariant 1 —
|
|
16
|
-
* state is a value, not a place). `get` / `has` are read-shaped; `set` /
|
|
17
|
-
* `remove` / `evictExpired` return a new cache.
|
|
18
|
-
*
|
|
19
|
-
* **Distinct from `/idempotency`.** Both are TTL-bounded key maps, but they
|
|
20
|
-
* answer different questions and so carry different shapes:
|
|
21
|
-
*
|
|
22
|
-
* - `/idempotency` is DEDUPE-focused — "have I already processed this key?"
|
|
23
|
-
* TTL is a single store-level policy (`ttlMs`), each entry stores the
|
|
24
|
-
* `atMs` it was remembered at, and expiry is derived (`now - atMs >=
|
|
25
|
-
* ttlMs`). It carries an `order` ledger + `capacity` because the use case
|
|
26
|
-
* (webhook receivers, retried messages) needs bounded LRU eviction.
|
|
27
|
-
* - `/cache` is RETRIEVAL-focused — "what is the value for this key right
|
|
28
|
-
* now?" TTL is set PER ENTRY at write time: `set(key, value, nowMs,
|
|
29
|
-
* ttlMs)` bakes the absolute `expiresAtMs = nowMs + ttlMs` into the entry.
|
|
30
|
-
* Different keys can carry different lifetimes (a hot key cached for 1s, a
|
|
31
|
-
* cold one for 1h) — the dimension idempotency's single store-level
|
|
32
|
-
* `ttlMs` cannot express. No `order`, no `capacity`: a cache bounds itself
|
|
33
|
-
* by TTL + periodic eviction, not by LRU count.
|
|
34
|
-
*
|
|
35
|
-
* The eviction Sub is the "eviction is a Msg" half: a `setInterval`-shaped Sub
|
|
36
|
-
* (composed directly on `fromInterval`, from `src/subs/`) that periodically
|
|
37
|
-
* dispatches a `cache_evict` Msg the consumer's reducer handles by calling
|
|
38
|
-
* `evictExpired`. Time still never enters the reducer — the Sub fires the Msg,
|
|
39
|
-
* the reducer reads `nowMs` off the Msg (the consumer stamps it at the Sub
|
|
40
|
-
* boundary), and `evictExpired` does the pure drop.
|
|
41
|
-
*
|
|
42
|
-
* Internal to the package (`src/internal/resilience/`), not a published
|
|
43
|
-
* subpath (#46); same one-shape-per-module rule as `work-queue`, `idempotency`,
|
|
44
|
-
* and `rate-limit`.
|
|
45
|
-
*/
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* A cached entry: the `value` and the absolute clock reading `expiresAtMs` it
|
|
49
|
-
* expires at. Unlike `/idempotency`'s entry (which stores `atMs` and derives
|
|
50
|
-
* expiry from a store-level `ttlMs`), the expiry instant is baked in at write
|
|
51
|
-
* time — so per-entry TTLs are a first-class capability. Both fields are plain
|
|
52
|
-
* data so the whole cache is trivially JSON-serializable into a consumer's
|
|
53
|
-
* `Store<S>`.
|
|
54
|
-
*/
|
|
55
|
-
interface CacheEntry<V> {
|
|
56
|
-
readonly value: V;
|
|
57
|
-
readonly expiresAtMs: number;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* The cache: a map of `key → entry`. No `order` ledger and no `capacity` (cf.
|
|
61
|
-
* `/idempotency`) — a TTL cache bounds itself by expiry + periodic eviction,
|
|
62
|
-
* not by LRU count. Each entry carries its own `expiresAtMs`, so the cache
|
|
63
|
-
* needs no store-level TTL policy field.
|
|
64
|
-
*/
|
|
65
|
-
interface TtlCache<V> {
|
|
66
|
-
readonly entries: Readonly<Record<string, CacheEntry<V>>>;
|
|
67
|
-
}
|
|
68
|
-
/** Create an empty cache. */
|
|
69
|
-
declare function initCache<V>(): TtlCache<V>;
|
|
70
|
-
/**
|
|
71
|
-
* The cached `value` for `key` iff present AND unexpired at `nowMs`, else
|
|
72
|
-
* `undefined`. An expired entry reads as absent even though it is physically
|
|
73
|
-
* still in `entries` until the next `evictExpired` (or an overwriting `set`)
|
|
74
|
-
* drops it. PURE — reads only, never mutates.
|
|
75
|
-
*/
|
|
76
|
-
declare function get<V>(cache: TtlCache<V>, key: string, nowMs: number): V | undefined;
|
|
77
|
-
/**
|
|
78
|
-
* True iff `key` is present AND unexpired at `nowMs`. The boolean sibling of
|
|
79
|
-
* `get` for callers that only need presence, not the value. PURE.
|
|
80
|
-
*/
|
|
81
|
-
declare function has<V>(cache: TtlCache<V>, key: string, nowMs: number): boolean;
|
|
82
|
-
/**
|
|
83
|
-
* Write `key → value` with an absolute expiry of `nowMs + ttlMs`. Re-`set`ting
|
|
84
|
-
* an existing key overwrites both the value and the expiry — the TTL clock
|
|
85
|
-
* restarts from the new `nowMs`. PURE — returns a new cache; the input is
|
|
86
|
-
* untouched (a fresh shallow copy of `entries`).
|
|
87
|
-
*/
|
|
88
|
-
declare function set<V>(cache: TtlCache<V>, key: string, value: V, nowMs: number, ttlMs: number): TtlCache<V>;
|
|
89
|
-
/**
|
|
90
|
-
* Drop `key` regardless of expiry. PURE — returns a new cache; the input is
|
|
91
|
-
* untouched. Removing an absent key returns an equivalent cache (the shallow
|
|
92
|
-
* copy still happens, but no entry is dropped).
|
|
93
|
-
*/
|
|
94
|
-
declare function remove<V>(cache: TtlCache<V>, key: string): TtlCache<V>;
|
|
95
|
-
/**
|
|
96
|
-
* Physically drop every entry expired at `nowMs` (`nowMs >= expiresAtMs`).
|
|
97
|
-
* Returns a NEW cache with the survivors. This is the pure work the eviction
|
|
98
|
-
* Sub's `cache_evict` Msg triggers — `get` / `has` already treat expired
|
|
99
|
-
* entries as absent, so eviction is purely a memory-reclaim pass, not a
|
|
100
|
-
* correctness one. PURE — the input cache is never mutated.
|
|
101
|
-
*/
|
|
102
|
-
declare function evictExpired<V>(cache: TtlCache<V>, nowMs: number): TtlCache<V>;
|
|
103
|
-
/**
|
|
104
|
-
* The Msg the eviction Sub dispatches each tick. `id` is the Sub's own id so a
|
|
105
|
-
* consumer with multiple caches can route the tick to the matching cache. The
|
|
106
|
-
* reducer reads `Date.now()`-free time by stamping `nowMs` itself at the Sub
|
|
107
|
-
* boundary if needed; the canonical reducer cell is
|
|
108
|
-
* `cache_evict: (s) => [{ ...s, cache: evictExpired(s.cache, nowMs) }, []]`.
|
|
109
|
-
*/
|
|
110
|
-
interface CacheEvictMsg {
|
|
111
|
-
readonly type: "cache_evict";
|
|
112
|
-
readonly id: string;
|
|
113
|
-
}
|
|
114
|
-
/** Construct a `cache_evict` Msg for the cache identified by `id`. */
|
|
115
|
-
declare function cacheEvictMsg(id: string): CacheEvictMsg;
|
|
116
|
-
/**
|
|
117
|
-
* The Sub shape the eviction factory installs: a `setInterval`-shaped Sub
|
|
118
|
-
* carrying its tick period (`intervalMs`) per the substrate's
|
|
119
|
-
* reconcile-by-id-with-data-on-the-Sub contract. `type: "cache"` is the
|
|
120
|
-
* lowercase source-noun Sub convention (canon §2.5 / naming-style).
|
|
121
|
-
*/
|
|
122
|
-
type CacheEvictionSub = Sub<"cache"> & {
|
|
123
|
-
readonly intervalMs: number;
|
|
124
|
-
};
|
|
125
|
-
/**
|
|
126
|
-
* Declare an eviction Sub for the cache identified by `id`, ticking every
|
|
127
|
-
* `everyMs`. Drop the result into `subscriptions(state)`:
|
|
128
|
-
*
|
|
129
|
-
* subscriptions: (s) => [cacheEvictionSub("session-cache", 30_000)]
|
|
130
|
-
*
|
|
131
|
-
* Same id ⇒ same running interval across transitions (no reconcile churn);
|
|
132
|
-
* changing `everyMs` alone will NOT re-arm the timer (the substrate leaves a
|
|
133
|
-
* same-id Sub running) — change the `id` too if the period must change. This
|
|
134
|
-
* mirrors `fromInterval`'s data-on-the-Sub tradeoff exactly.
|
|
135
|
-
*/
|
|
136
|
-
declare function cacheEvictionSub(id: string, everyMs: number): CacheEvictionSub;
|
|
137
|
-
/**
|
|
138
|
-
* The `subscribe` cell for the eviction Sub. Assign it to
|
|
139
|
-
* `machine.subscribe.cache`:
|
|
140
|
-
*
|
|
141
|
-
* subscribe: { cache: cacheEvictionSubscribe }
|
|
142
|
-
*
|
|
143
|
-
* Composed directly on `fromInterval` — it owns the `setInterval` /
|
|
144
|
-
* `clearInterval` lifecycle; this cell only pins the Msg the tick dispatches
|
|
145
|
-
* (`cacheEvictMsg(sub.id)`). The cleanup `fromInterval` returns stops the
|
|
146
|
-
* interval when the Sub is reconciled out (the cache's state exits the phase
|
|
147
|
-
* that declared it).
|
|
148
|
-
*/
|
|
149
|
-
declare const cacheEvictionSubscribe: SubscribeHandler<CacheEvictionSub, CacheEvictMsg, unknown>;
|
|
1
|
+
import { n as SubId, S as Sub, o as DepKeyedSub, h as Dispose, T as TimerDeps } from './core-BbgtiXdg.js';
|
|
2
|
+
import { S as SubscribeHandler } from './types-LxZxGYda.js';
|
|
150
3
|
|
|
151
4
|
/**
|
|
152
5
|
* internal/resilience/deadline — a one-shot Sub that fires when an ABSOLUTE wall-clock
|
|
153
6
|
* deadline is reached.
|
|
154
7
|
*
|
|
155
8
|
* Generalizes the recurring "auto-fail a machine at T+N" / "15-minute stale
|
|
156
|
-
* guard" pattern: a
|
|
157
|
-
*
|
|
158
|
-
*
|
|
9
|
+
* guard" pattern: a deadline a battery lists only while it should be armed,
|
|
10
|
+
* which dispatches a single `DeadlineExceeded` Msg when the wall clock crosses
|
|
11
|
+
* `atMs`.
|
|
12
|
+
*
|
|
13
|
+
* A battery's `subs(slice)` returns its deadlines as a LIST (`DeadlineSub[]`,
|
|
14
|
+
* one per retry timer, say). A machine declares that list as ONE Sub of type
|
|
15
|
+
* `"deadline"` whose `deps` is the list (`deadlinesSub(select)`), and the
|
|
16
|
+
* `deadline` runner arms every deadline in it. A change to the list restarts
|
|
17
|
+
* the runner, which re-arms each deadline for its REMAINING time — `atMs` is
|
|
18
|
+
* absolute, so a restart never moves a deadline.
|
|
159
19
|
*
|
|
160
20
|
* Difference from `fromTimeout` (relative — "fire after N ms"): a deadline is
|
|
161
21
|
* an ABSOLUTE target. The delay is computed at subscribe time as
|
|
@@ -168,10 +28,9 @@ declare const cacheEvictionSubscribe: SubscribeHandler<CacheEvictionSub, CacheEv
|
|
|
168
28
|
* reconcile pass — the substrate must finish wiring all subs before any Msg
|
|
169
29
|
* lands).
|
|
170
30
|
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
* `
|
|
174
|
-
* and delegates the `setTimeout` / `clearTimeout` to `fromTimeout`. The clock
|
|
31
|
+
* `deadlineSub` produces one deadline literal carrying `atMs`; the runner
|
|
32
|
+
* (`subscribeDeadline`) translates each `atMs` → a relative delay and arms a
|
|
33
|
+
* `setTimeout` / `clearTimeout` pair per deadline. The clock
|
|
175
34
|
* read (`Date.now()`) lives only in the subscribe handler — never in a reducer
|
|
176
35
|
* (invariant 2) — and is exercised in tests via vitest fake timers, matching
|
|
177
36
|
* the no-injection convention of the `fromTimeout` / `fromInterval` exemplars.
|
|
@@ -198,19 +57,38 @@ declare const cacheEvictionSubscribe: SubscribeHandler<CacheEvictionSub, CacheEv
|
|
|
198
57
|
*/
|
|
199
58
|
|
|
200
59
|
/**
|
|
201
|
-
*
|
|
60
|
+
* One deadline, as a battery lists it. `atMs` is the absolute target — epoch
|
|
202
61
|
* milliseconds (the `Date.now()` / `Date.parse(...)` scale), NOT a relative
|
|
203
|
-
* delay.
|
|
204
|
-
*
|
|
205
|
-
* a
|
|
62
|
+
* delay. `id` names the deadline inside its list: it rides on the dispatched
|
|
63
|
+
* Msg so a reducer can tell WHICH deadline fired, and a registry-backed timer
|
|
64
|
+
* (a DO alarm slot) keys its entry on it.
|
|
206
65
|
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
* `TimeoutSubData` the `fromTimeout` factory consumes.
|
|
66
|
+
* It is plain data — it becomes part of the `deadline` Sub's `deps`, which the
|
|
67
|
+
* engine hashes — and is not itself a running Sub.
|
|
210
68
|
*/
|
|
211
|
-
type DeadlineSub =
|
|
69
|
+
type DeadlineSub<N extends string | undefined = undefined> = {
|
|
70
|
+
readonly id: SubId;
|
|
71
|
+
readonly type: "deadline";
|
|
212
72
|
readonly atMs: number;
|
|
213
|
-
} & DeadlineOpts
|
|
73
|
+
} & DeadlineOpts<N>;
|
|
74
|
+
/**
|
|
75
|
+
* The running `"deadline"` Sub: its `deps` is the non-empty list of deadlines
|
|
76
|
+
* to arm. Name it in `types.sub` for a machine that arms deadlines.
|
|
77
|
+
*/
|
|
78
|
+
type DeadlinesSub<N extends string | undefined = undefined> = Sub<"deadline", readonly DeadlineSub<N>[]>;
|
|
79
|
+
/**
|
|
80
|
+
* The `deps` of a `deadline` Sub: the list, or `null` when it is empty — an
|
|
81
|
+
* empty list is a Sub with nothing to arm, and an off Sub is not a live one
|
|
82
|
+
* (`driveToDone` reads live Subs to tell a waiting machine from a stalled one).
|
|
83
|
+
*/
|
|
84
|
+
declare function deadlines<N extends string | undefined = undefined>(list: readonly DeadlineSub<N>[]): readonly DeadlineSub<N>[] | null;
|
|
85
|
+
/**
|
|
86
|
+
* The `subs` entry that arms whatever deadlines `select` lists at a state:
|
|
87
|
+
*
|
|
88
|
+
* subs: [deadlinesSub((s: State) => rc.subs(s.resilience))],
|
|
89
|
+
* // run(machine, { subscribe: { deadline: subscribeDeadline } })
|
|
90
|
+
*/
|
|
91
|
+
declare function deadlinesSub<S, N extends string | undefined = undefined>(select: (state: S) => readonly DeadlineSub<N>[]): DepKeyedSub<S, DeadlinesSub<N>>;
|
|
214
92
|
/**
|
|
215
93
|
* Additive options the `deadlineSub` factory folds onto the Sub literal. This
|
|
216
94
|
* is the extension seam (invariant 5 — composition by reduction): future fields
|
|
@@ -221,10 +99,24 @@ type DeadlineSub = Sub<"deadline"> & {
|
|
|
221
99
|
* they ask for what they need by name, never positionally, so a shape extension
|
|
222
100
|
* never rewrites the ~13 `deadlineSub(id, atMs)` call sites.
|
|
223
101
|
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
102
|
+
* The first field to land here is {@link DeadlineOpts.name} — the knob name the
|
|
103
|
+
* dispatched Msg tag is derived from — and it landed exactly that way: one
|
|
104
|
+
* optional member, zero rewritten call sites.
|
|
226
105
|
*/
|
|
227
|
-
type DeadlineOpts = {
|
|
106
|
+
type DeadlineOpts<N extends string | undefined = undefined> = {
|
|
107
|
+
/**
|
|
108
|
+
* The knob this deadline belongs to. Present → the dispatched Msg's tag is
|
|
109
|
+
* `` `${name}_deadline` ``; absent → the tag stays the bare
|
|
110
|
+
* `"deadline_exceeded"` every machine wired before this field is already
|
|
111
|
+
* handling. `N` is the name as a TYPE as well as a value, so a consumer's
|
|
112
|
+
* cell key and the Msg type are derived from one place and cannot drift.
|
|
113
|
+
*
|
|
114
|
+
* Name a deadline when a machine arms more than one family of them. The Sub
|
|
115
|
+
* *id* was already scoped (`` `${name}:deadline:${key}` ``); this scopes the
|
|
116
|
+
* Msg the Sub dispatches, which is what a reducer cell keys off.
|
|
117
|
+
*/
|
|
118
|
+
readonly name?: N;
|
|
119
|
+
};
|
|
228
120
|
/**
|
|
229
121
|
* The Msg the deadline dispatches when the wall clock crosses `atMs`. Exported
|
|
230
122
|
* as a tagged shape so consumers can union it into their machine's Msg type and
|
|
@@ -238,25 +130,31 @@ type DeadlineOpts = {};
|
|
|
238
130
|
* they prefer — `deadlineExceeded(...)` only fixes the wire shape, not the
|
|
239
131
|
* consumer's local dialect.
|
|
240
132
|
*/
|
|
241
|
-
type DeadlineExceeded = {
|
|
242
|
-
readonly type:
|
|
133
|
+
type DeadlineExceeded<N extends string | undefined = undefined> = {
|
|
134
|
+
readonly type: DeadlineMsgType<N>;
|
|
243
135
|
readonly id: string;
|
|
244
136
|
readonly atMs: number;
|
|
245
137
|
};
|
|
246
138
|
/**
|
|
247
|
-
*
|
|
248
|
-
*
|
|
139
|
+
* The dispatched Msg's tag, derived from the deadline's optional name. A named
|
|
140
|
+
* deadline speaks `` `${N}_deadline` ``; an unnamed one speaks the bare
|
|
141
|
+
* `"deadline_exceeded"` literal, which is what `DeadlineExceeded` with no type
|
|
142
|
+
* argument still means — so every machine wired before the name existed keeps
|
|
143
|
+
* the exact type it had.
|
|
144
|
+
*/
|
|
145
|
+
type DeadlineMsgType<N extends string | undefined> = N extends string ? `${N}_deadline` : "deadline_exceeded";
|
|
146
|
+
/**
|
|
147
|
+
* Build a deadline literal. Pure data — no clock read, no timer; the timer
|
|
148
|
+
* is armed later by the `deadline` runner. `id` is branded via `subId(...)` so
|
|
249
149
|
* accidental raw-string drift fails at the type level (invariant 7).
|
|
250
150
|
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
* the
|
|
254
|
-
*
|
|
255
|
-
* `fromTimeout`'s.
|
|
151
|
+
* List it from a battery's `subs(slice)` while the deadline should be armed;
|
|
152
|
+
* drop it to disarm — the engine restarts the `deadline` Sub, whose cleanup
|
|
153
|
+
* clears the pending timer before it fires. That is the "cancel on state exit"
|
|
154
|
+
* lifecycle.
|
|
256
155
|
*
|
|
257
|
-
* @param id
|
|
258
|
-
*
|
|
259
|
-
* deadlines on the same machine.
|
|
156
|
+
* @param id Identity for this deadline, echoed on the Msg it fires. Use
|
|
157
|
+
* distinct ids for distinct deadlines on the same machine.
|
|
260
158
|
* @param atMs Absolute target in epoch milliseconds (e.g. `Date.now() + 900_000`
|
|
261
159
|
* for a 15-minute guard, or a persisted `expiresAt`).
|
|
262
160
|
* @param opts Optional additive fields folded onto the Sub literal (see
|
|
@@ -265,17 +163,16 @@ type DeadlineExceeded = {
|
|
|
265
163
|
* optionality is the future-proofing seam — extending the shape
|
|
266
164
|
* never breaks an existing `deadlineSub(id, atMs)` call site.
|
|
267
165
|
*/
|
|
268
|
-
declare function deadlineSub(id: string, atMs: number, opts?: DeadlineOpts): DeadlineSub
|
|
166
|
+
declare function deadlineSub<N extends string | undefined = undefined>(id: string, atMs: number, opts?: DeadlineOpts<N>): DeadlineSub<N>;
|
|
269
167
|
/**
|
|
270
168
|
* The host-plugged timer backing. Given the deadline's stable `id`, its
|
|
271
169
|
* absolute `atMs`, and the Msg to fire, arm the platform-appropriate timer
|
|
272
170
|
* (DO alarm registry, `setTimeout`, fake timer) and return a cleanup that
|
|
273
171
|
* cancels the pending fire.
|
|
274
172
|
*
|
|
275
|
-
* `id` is the
|
|
276
|
-
* keys its entry on `id` so the cleanup deletes the EXACT entry
|
|
277
|
-
*
|
|
278
|
-
* handle).
|
|
173
|
+
* `id` is the deadline's own id — a registry-backed host (the DO alarm slot)
|
|
174
|
+
* keys its entry on `id` so the cleanup deletes the EXACT entry it armed. A
|
|
175
|
+
* `setTimeout`-backed host ignores `id` (the closure holds the handle).
|
|
279
176
|
*
|
|
280
177
|
* The host computes the gap itself: `atMs - Date.now()` is the REMAINING time.
|
|
281
178
|
* On a host that just rehydrated after hibernation that gap is the shrunken
|
|
@@ -285,61 +182,235 @@ declare function deadlineSub(id: string, atMs: number, opts?: DeadlineOpts): Dea
|
|
|
285
182
|
*/
|
|
286
183
|
type ArmTimer<M> = (id: SubId, atMs: number, msg: M, dispatch: (msg: M) => void) => () => void;
|
|
287
184
|
/**
|
|
288
|
-
* Build the `
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
* `deadlineExceeded(...)` Msg
|
|
185
|
+
* Build the `deadline` runner from a host-plugged `armTimer`. This module owns
|
|
186
|
+
* the deadline shape, the anchor, and the Msg; the HOST owns what backs the
|
|
187
|
+
* timer. The runner arms every deadline in the Sub's `deps` list — reading
|
|
188
|
+
* `id` + `atMs`, building the `deadlineExceeded(...)` Msg and handing all three
|
|
189
|
+
* to `armTimer` — and its cleanup cancels them all.
|
|
292
190
|
*
|
|
293
191
|
* Use it when `setTimeout` is the wrong backing — most concretely a Durable
|
|
294
192
|
* Object, which hibernates and must register a `do_alarm` instead:
|
|
295
193
|
*
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
194
|
+
* run(machine, {
|
|
195
|
+
* subscribe: {
|
|
196
|
+
* deadline: subscribeWith((id, atMs, msg, dispatch) =>
|
|
197
|
+
* alarms.register(id, atMs, () => dispatch(msg)),
|
|
198
|
+
* ),
|
|
199
|
+
* },
|
|
200
|
+
* })
|
|
301
201
|
*
|
|
302
202
|
* For the `setTimeout` default, use {@link subscribeDeadline} — it is exactly
|
|
303
203
|
* `subscribeWith(setTimeoutArmTimer())`, so there is one deadline surface and
|
|
304
204
|
* one anchor, not a second one per host.
|
|
305
205
|
*/
|
|
306
|
-
declare function subscribeWith(armTimer: ArmTimer<DeadlineExceeded
|
|
206
|
+
declare function subscribeWith<N extends string | undefined = undefined>(armTimer: ArmTimer<DeadlineExceeded<N>>): (sub: DeadlinesSub<N>, ctx: unknown, dispatch: (msg: DeadlineExceeded<N>) => void) => Dispose;
|
|
307
207
|
/**
|
|
308
208
|
* The `setTimeout` timer backing — for node / browser / any host whose timer is
|
|
309
209
|
* a plain `setTimeout`. Arms for the REMAINING time (`max(0, atMs - Date.now())`,
|
|
310
210
|
* floored at 0 so an already-past deadline fires on the NEXT tick rather than
|
|
311
211
|
* synchronously inside the reconcile pass), so a deadline re-derived after a
|
|
312
|
-
* rehydrate fires at the original instant, not a
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
* `clearTimeout`: this package has ONE timer lifecycle, and the arm-timer seam
|
|
316
|
-
* plugs into it instead of forking it. A hibernating host does NOT use this —
|
|
317
|
-
* it plugs its own `armTimer` (a `do_alarm` registration) into
|
|
318
|
-
* {@link subscribeWith}.
|
|
212
|
+
* rehydrate — or re-armed by a restart — fires at the original instant, not a
|
|
213
|
+
* fresh full window. A hibernating host does NOT use this — it plugs its own
|
|
214
|
+
* `armTimer` (a `do_alarm` registration) into {@link subscribeWith}.
|
|
319
215
|
*/
|
|
320
|
-
declare function setTimeoutArmTimer(): ArmTimer<DeadlineExceeded
|
|
216
|
+
declare function setTimeoutArmTimer<N extends string | undefined = undefined>(): ArmTimer<DeadlineExceeded<N>>;
|
|
321
217
|
/**
|
|
322
|
-
* The `
|
|
323
|
-
*
|
|
324
|
-
* dispatches `deadlineExceeded(...)` when
|
|
325
|
-
*
|
|
218
|
+
* The `deadline` runner for the DEFAULT `setTimeout` backing. Arms a one-shot
|
|
219
|
+
* timer per listed deadline for its remaining delay `max(0, atMs - Date.now())`
|
|
220
|
+
* and dispatches `deadlineExceeded(...)` when each fires; the cleanup clears
|
|
221
|
+
* every pending timer.
|
|
326
222
|
*
|
|
327
223
|
* It is `subscribeWith(setTimeoutArmTimer())` — the default backing named, not
|
|
328
224
|
* a separate implementation, so the host-plugged path and the default path can
|
|
329
225
|
* never disagree about the anchor.
|
|
330
226
|
*
|
|
331
|
-
*
|
|
227
|
+
* run(machine, { subscribe: { deadline: subscribeDeadline } })
|
|
332
228
|
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
229
|
+
* Generic in the deadline's name so ONE runner serves every knob in a machine:
|
|
230
|
+
* the tag it dispatches is read off each deadline's own `name`, so a machine
|
|
231
|
+
* listing a named knob's deadlines beside an unnamed one's wires this once.
|
|
336
232
|
*/
|
|
337
|
-
declare const subscribeDeadline:
|
|
233
|
+
declare const subscribeDeadline: <N extends string | undefined = undefined>(sub: DeadlinesSub<N>, ctx: unknown, dispatch: (msg: DeadlineExceeded<N>) => void) => Dispose;
|
|
338
234
|
/**
|
|
339
235
|
* Construct the Msg the deadline dispatches. Exported so consumers can build /
|
|
340
|
-
* assert the same shape (and so the
|
|
236
|
+
* assert the same shape (and so the runner and tests share one
|
|
341
237
|
* constructor rather than two literals that can drift).
|
|
342
238
|
*/
|
|
343
|
-
declare function deadlineExceeded(id: string, atMs: number): DeadlineExceeded
|
|
239
|
+
declare function deadlineExceeded<N extends string | undefined = undefined>(id: string, atMs: number, name?: N): DeadlineExceeded<N>;
|
|
240
|
+
/**
|
|
241
|
+
* The one place the named tag is spelled. `undefined` → the bare
|
|
242
|
+
* `"deadline_exceeded"` literal; a name → `` `${name}_deadline` ``. Exported so
|
|
243
|
+
* a consumer keying a reducer cell off a named deadline derives the key from
|
|
244
|
+
* the same function the Sub dispatches through, instead of re-spelling the
|
|
245
|
+
* template and drifting.
|
|
246
|
+
*/
|
|
247
|
+
declare function deadlineMsgType<N extends string | undefined>(name?: N): DeadlineMsgType<N>;
|
|
248
|
+
/**
|
|
249
|
+
* The built-in `timer` Sub's deps for the SOONEST deadline in `list`, counted
|
|
250
|
+
* from `nowMs`; `null` when the list is empty (no timer armed).
|
|
251
|
+
*
|
|
252
|
+
* This is how a battery arms a whole deadline list through the engine's
|
|
253
|
+
* built-in `timer` instead of a `deadline` runner: a machine declares ONE
|
|
254
|
+
* `{ type: "timer", deps: (s) => nextTimer(list(s), clock(s)) }` entry. The
|
|
255
|
+
* timer fires the soonest deadline's `DeadlineExceeded` Msg; the transition it
|
|
256
|
+
* causes drops that deadline from the list, and the next soonest is armed.
|
|
257
|
+
*
|
|
258
|
+
* `nowMs` is the instant the list was last computed against — the battery's
|
|
259
|
+
* own record of its latest transition, never a clock read (invariant 2). The
|
|
260
|
+
* countdown is `atMs - nowMs`, clamped at `0`, and because `ms` is part of the
|
|
261
|
+
* deps, every new `nowMs` restarts the countdown from that instant. PURE.
|
|
262
|
+
*/
|
|
263
|
+
declare function nextTimer<N extends string | undefined = undefined>(list: readonly DeadlineSub<N>[], nowMs: number): TimerDeps<DeadlineExceeded<N>> | null;
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* internal/resilience/cache — a TTL cache as pure state, PLUS a periodic eviction Sub.
|
|
267
|
+
*
|
|
268
|
+
* Same pure-state-ops shape as `../../idempotency/` and
|
|
269
|
+
* `../../work-queue/ops.ts`: a state type plus pure transition functions. Host-
|
|
270
|
+
* agnostic by construction — no `Date.now()`, no timers, no I/O inside the ops.
|
|
271
|
+
* The caller injects the clock (`nowMs`) and folds the result through its own
|
|
272
|
+
* reducer, keeping the store inside TEA's invariant 2 (transitions are pure:
|
|
273
|
+
* same inputs → same outputs) so a consumer can exercise it with `replay`
|
|
274
|
+
* without a fake clock.
|
|
275
|
+
*
|
|
276
|
+
* Every op returns a NEW cache value and never mutates its input (invariant 1 —
|
|
277
|
+
* state is a value, not a place). `get` / `has` are read-shaped; `set` /
|
|
278
|
+
* `remove` / `evictExpired` return a new cache.
|
|
279
|
+
*
|
|
280
|
+
* **Distinct from `/idempotency`.** Both are TTL-bounded key maps, but they
|
|
281
|
+
* answer different questions and so carry different shapes:
|
|
282
|
+
*
|
|
283
|
+
* - `/idempotency` is DEDUPE-focused — "have I already processed this key?"
|
|
284
|
+
* TTL is a single store-level policy (`ttlMs`), each entry stores the
|
|
285
|
+
* `atMs` it was remembered at, and expiry is derived (`now - atMs >=
|
|
286
|
+
* ttlMs`). It carries an `order` ledger + `capacity` because the use case
|
|
287
|
+
* (webhook receivers, retried messages) needs bounded LRU eviction.
|
|
288
|
+
* - `/cache` is RETRIEVAL-focused — "what is the value for this key right
|
|
289
|
+
* now?" TTL is set PER ENTRY at write time: `set(key, value, nowMs,
|
|
290
|
+
* ttlMs)` bakes the absolute `expiresAtMs = nowMs + ttlMs` into the entry.
|
|
291
|
+
* Different keys can carry different lifetimes (a hot key cached for 1s, a
|
|
292
|
+
* cold one for 1h) — the dimension idempotency's single store-level
|
|
293
|
+
* `ttlMs` cannot express. No `order`, no `capacity`: a cache bounds itself
|
|
294
|
+
* by TTL + periodic eviction, not by LRU count.
|
|
295
|
+
*
|
|
296
|
+
* The eviction Sub is the "eviction is a Msg" half: a `setInterval`-shaped Sub
|
|
297
|
+
* (composed directly on `fromInterval`, from `src/subs/`) that periodically
|
|
298
|
+
* dispatches a `cache_evict` Msg the consumer's reducer handles by calling
|
|
299
|
+
* `evictExpired`. Time still never enters the reducer — the Sub fires the Msg,
|
|
300
|
+
* the reducer reads `nowMs` off the Msg (the consumer stamps it at the Sub
|
|
301
|
+
* boundary), and `evictExpired` does the pure drop.
|
|
302
|
+
*
|
|
303
|
+
* Internal to the package (`src/internal/resilience/`), not a published
|
|
304
|
+
* subpath (#46); same one-shape-per-module rule as `work-queue`, `idempotency`,
|
|
305
|
+
* and `rate-limit`.
|
|
306
|
+
*/
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* A cached entry: the `value` and the absolute clock reading `expiresAtMs` it
|
|
310
|
+
* expires at. Unlike `/idempotency`'s entry (which stores `atMs` and derives
|
|
311
|
+
* expiry from a store-level `ttlMs`), the expiry instant is baked in at write
|
|
312
|
+
* time — so per-entry TTLs are a first-class capability. Both fields are plain
|
|
313
|
+
* data so the whole cache is trivially JSON-serializable into a consumer's
|
|
314
|
+
* `Store<S>`.
|
|
315
|
+
*/
|
|
316
|
+
interface CacheEntry<V> {
|
|
317
|
+
readonly value: V;
|
|
318
|
+
readonly expiresAtMs: number;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* The cache: a map of `key → entry`. No `order` ledger and no `capacity` (cf.
|
|
322
|
+
* `/idempotency`) — a TTL cache bounds itself by expiry + periodic eviction,
|
|
323
|
+
* not by LRU count. Each entry carries its own `expiresAtMs`, so the cache
|
|
324
|
+
* needs no store-level TTL policy field.
|
|
325
|
+
*/
|
|
326
|
+
interface TtlCache<V> {
|
|
327
|
+
readonly entries: Readonly<Record<string, CacheEntry<V>>>;
|
|
328
|
+
}
|
|
329
|
+
/** Create an empty cache. */
|
|
330
|
+
declare function initCache<V>(): TtlCache<V>;
|
|
331
|
+
/**
|
|
332
|
+
* The cached `value` for `key` iff present AND unexpired at `nowMs`, else
|
|
333
|
+
* `undefined`. An expired entry reads as absent even though it is physically
|
|
334
|
+
* still in `entries` until the next `evictExpired` (or an overwriting `set`)
|
|
335
|
+
* drops it. PURE — reads only, never mutates.
|
|
336
|
+
*/
|
|
337
|
+
declare function get<V>(cache: TtlCache<V>, key: string, nowMs: number): V | undefined;
|
|
338
|
+
/**
|
|
339
|
+
* True iff `key` is present AND unexpired at `nowMs`. The boolean sibling of
|
|
340
|
+
* `get` for callers that only need presence, not the value. PURE.
|
|
341
|
+
*/
|
|
342
|
+
declare function has<V>(cache: TtlCache<V>, key: string, nowMs: number): boolean;
|
|
343
|
+
/**
|
|
344
|
+
* Write `key → value` with an absolute expiry of `nowMs + ttlMs`. Re-`set`ting
|
|
345
|
+
* an existing key overwrites both the value and the expiry — the TTL clock
|
|
346
|
+
* restarts from the new `nowMs`. PURE — returns a new cache; the input is
|
|
347
|
+
* untouched (a fresh shallow copy of `entries`).
|
|
348
|
+
*/
|
|
349
|
+
declare function set<V>(cache: TtlCache<V>, key: string, value: V, nowMs: number, ttlMs: number): TtlCache<V>;
|
|
350
|
+
/**
|
|
351
|
+
* Drop `key` regardless of expiry. PURE — returns a new cache; the input is
|
|
352
|
+
* untouched. Removing an absent key returns an equivalent cache (the shallow
|
|
353
|
+
* copy still happens, but no entry is dropped).
|
|
354
|
+
*/
|
|
355
|
+
declare function remove<V>(cache: TtlCache<V>, key: string): TtlCache<V>;
|
|
356
|
+
/**
|
|
357
|
+
* Physically drop every entry expired at `nowMs` (`nowMs >= expiresAtMs`).
|
|
358
|
+
* Returns a NEW cache with the survivors. This is the pure work the eviction
|
|
359
|
+
* Sub's `cache_evict` Msg triggers — `get` / `has` already treat expired
|
|
360
|
+
* entries as absent, so eviction is purely a memory-reclaim pass, not a
|
|
361
|
+
* correctness one. PURE — the input cache is never mutated.
|
|
362
|
+
*/
|
|
363
|
+
declare function evictExpired<V>(cache: TtlCache<V>, nowMs: number): TtlCache<V>;
|
|
364
|
+
/**
|
|
365
|
+
* The Msg the eviction Sub dispatches each tick. `id` is the cache's name so a
|
|
366
|
+
* consumer with multiple caches can route the tick to the matching cache. The
|
|
367
|
+
* reducer reads `Date.now()`-free time by stamping `nowMs` itself at the Sub
|
|
368
|
+
* boundary if needed; the canonical reducer cell is
|
|
369
|
+
* `cache_evict: (s) => [{ ...s, cache: evictExpired(s.cache, nowMs) }, []]`.
|
|
370
|
+
*/
|
|
371
|
+
interface CacheEvictMsg {
|
|
372
|
+
readonly type: "cache_evict";
|
|
373
|
+
readonly id: string;
|
|
374
|
+
}
|
|
375
|
+
/** Construct a `cache_evict` Msg for the cache identified by `id`. */
|
|
376
|
+
declare function cacheEvictMsg(id: string): CacheEvictMsg;
|
|
377
|
+
/**
|
|
378
|
+
* The `deps` of an eviction Sub: which cache it ticks for (`name`, echoed as
|
|
379
|
+
* the Msg's `id`) and the tick period (`intervalMs`, which `fromInterval`
|
|
380
|
+
* reads).
|
|
381
|
+
*/
|
|
382
|
+
type CacheEvictionDeps = {
|
|
383
|
+
readonly name: string;
|
|
384
|
+
readonly intervalMs: number;
|
|
385
|
+
};
|
|
386
|
+
/**
|
|
387
|
+
* The running eviction Sub: a `setInterval`-shaped Sub whose `deps` carry the
|
|
388
|
+
* cache's name and tick period. `type: "cache"` is the lowercase source-noun
|
|
389
|
+
* Sub convention (canon §2.5 / naming-style).
|
|
390
|
+
*/
|
|
391
|
+
type CacheEvictionSub = Sub<"cache", CacheEvictionDeps>;
|
|
392
|
+
/**
|
|
393
|
+
* The `subs` entry for an eviction tick on the cache named `name`, every
|
|
394
|
+
* `everyMs`. It is on for the machine's life:
|
|
395
|
+
*
|
|
396
|
+
* subs: [cacheEvictionSub("session-cache", 30_000)],
|
|
397
|
+
* // run(machine, { subscribe: { cache: cacheEvictionSubscribe } })
|
|
398
|
+
*
|
|
399
|
+
* The engine keys the Sub on its `deps`, so a machine that declares the same
|
|
400
|
+
* name and period keeps one running interval, and a changed period restarts
|
|
401
|
+
* it at the new one.
|
|
402
|
+
*/
|
|
403
|
+
declare function cacheEvictionSub(name: string, everyMs: number): DepKeyedSub<unknown, CacheEvictionSub>;
|
|
404
|
+
/**
|
|
405
|
+
* The `cache` runner for the eviction Sub. Hand it to `run`:
|
|
406
|
+
*
|
|
407
|
+
* run(machine, { subscribe: { cache: cacheEvictionSubscribe } })
|
|
408
|
+
*
|
|
409
|
+
* Composed directly on `fromInterval` — it owns the `setInterval` /
|
|
410
|
+
* `clearInterval` lifecycle; this runner only pins the Msg the tick dispatches
|
|
411
|
+
* (`cacheEvictMsg(sub.deps.name)`). The cleanup `fromInterval` returns stops
|
|
412
|
+
* the interval when the Sub stops.
|
|
413
|
+
*/
|
|
414
|
+
declare const cacheEvictionSubscribe: SubscribeHandler<CacheEvictionSub, CacheEvictMsg, unknown>;
|
|
344
415
|
|
|
345
|
-
export { type ArmTimer as A, type CacheEvictionSub as C, type DeadlineExceeded as D, type TtlCache as T, type DeadlineSub as a,
|
|
416
|
+
export { type ArmTimer as A, type CacheEvictionSub as C, type DeadlineExceeded as D, type TtlCache as T, type DeadlineSub as a, type DeadlinesSub as b, deadlineSub as c, deadlineExceeded as d, deadlinesSub as e, type CacheEvictMsg as f, type CacheEntry as g, type CacheEvictionDeps as h, type DeadlineMsgType as i, type DeadlineOpts as j, cacheEvictMsg as k, cacheEvictionSub as l, cacheEvictionSubscribe as m, deadlineMsgType as n, deadlines as o, evictExpired as p, get as q, has as r, subscribeDeadline as s, initCache as t, nextTimer as u, remove as v, set as w, setTimeoutArmTimer as x, subscribeWith as y };
|