@demlik/tea 0.5.1 → 0.7.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.
Files changed (122) hide show
  1. package/dist/agent/index.d.ts +6 -5
  2. package/dist/agent/index.js +10 -10
  3. package/dist/authed-call/index.d.ts +2 -2
  4. package/dist/authed-call/index.js +7 -7
  5. package/dist/await-terminal/index.d.ts +3 -2
  6. package/dist/await-terminal/index.js +2 -2
  7. package/dist/batch-window/index.d.ts +4 -3
  8. package/dist/batch-window/index.js +3 -3
  9. package/dist/cache/index.d.ts +2 -2
  10. package/dist/cache/index.js +4 -4
  11. package/dist/chart/index.d.ts +934 -0
  12. package/dist/chart/index.js +244 -0
  13. package/dist/chart/index.js.map +1 -0
  14. package/dist/{chunk-2UGTDYOK.js → chunk-3JU6657S.js} +3 -3
  15. package/dist/{chunk-2UGTDYOK.js.map → chunk-3JU6657S.js.map} +1 -1
  16. package/dist/{chunk-AZ3NHUPD.js → chunk-4KTWLPL5.js} +3 -3
  17. package/dist/{chunk-AZ3NHUPD.js.map → chunk-4KTWLPL5.js.map} +1 -1
  18. package/dist/{chunk-ZH454CR5.js → chunk-5EHLEUTL.js} +24 -4
  19. package/dist/chunk-5EHLEUTL.js.map +1 -0
  20. package/dist/{chunk-WF46FABO.js → chunk-7SVIRMC6.js} +3 -3
  21. package/dist/{chunk-WF46FABO.js.map → chunk-7SVIRMC6.js.map} +1 -1
  22. package/dist/{chunk-XMHZ44QC.js → chunk-7V4EVLZA.js} +4 -4
  23. package/dist/{chunk-XMHZ44QC.js.map → chunk-7V4EVLZA.js.map} +1 -1
  24. package/dist/{chunk-IVHQCVFX.js → chunk-A56OR5RF.js} +3 -3
  25. package/dist/{chunk-IVHQCVFX.js.map → chunk-A56OR5RF.js.map} +1 -1
  26. package/dist/{chunk-O7ED34JH.js → chunk-B5VXOFMZ.js} +47 -15
  27. package/dist/chunk-B5VXOFMZ.js.map +1 -0
  28. package/dist/{chunk-EC6FO42Q.js → chunk-EUF7XOEE.js} +6 -6
  29. package/dist/{chunk-EC6FO42Q.js.map → chunk-EUF7XOEE.js.map} +1 -1
  30. package/dist/{chunk-XOAP3LOT.js → chunk-I5OV65MS.js} +3 -3
  31. package/dist/{chunk-XOAP3LOT.js.map → chunk-I5OV65MS.js.map} +1 -1
  32. package/dist/{chunk-ASX7PL2K.js → chunk-JGVL3WPL.js} +3 -3
  33. package/dist/{chunk-ASX7PL2K.js.map → chunk-JGVL3WPL.js.map} +1 -1
  34. package/dist/{chunk-S6Y3RNX4.js → chunk-JP5D6GSL.js} +4 -4
  35. package/dist/{chunk-S6Y3RNX4.js.map → chunk-JP5D6GSL.js.map} +1 -1
  36. package/dist/{chunk-ERYY5P6W.js → chunk-ND3DH6KM.js} +3 -3
  37. package/dist/{chunk-ERYY5P6W.js.map → chunk-ND3DH6KM.js.map} +1 -1
  38. package/dist/{chunk-A7USKARI.js → chunk-SUST46KO.js} +3 -3
  39. package/dist/{chunk-A7USKARI.js.map → chunk-SUST46KO.js.map} +1 -1
  40. package/dist/{chunk-2KBEIGYP.js → chunk-TP5UPGLY.js} +3 -3
  41. package/dist/{chunk-2KBEIGYP.js.map → chunk-TP5UPGLY.js.map} +1 -1
  42. package/dist/{chunk-66S2KEQQ.js → chunk-WLGO64WC.js} +4 -4
  43. package/dist/{chunk-66S2KEQQ.js.map → chunk-WLGO64WC.js.map} +1 -1
  44. package/dist/{chunk-UV7VCTNC.js → chunk-ZF6WDK2D.js} +7 -7
  45. package/dist/{chunk-UV7VCTNC.js.map → chunk-ZF6WDK2D.js.map} +1 -1
  46. package/dist/{core-JPHsMZVB.d.ts → core-Bjc0bS8j.d.ts} +37 -1
  47. package/dist/deadline/index.d.ts +2 -2
  48. package/dist/deadline/index.js +3 -3
  49. package/dist/do/index.d.ts +7 -6
  50. package/dist/do/index.js +10 -10
  51. package/dist/{durable-effects-MRnCBCxV.d.ts → durable-effects-D8SqU_if.d.ts} +1 -1
  52. package/dist/extension/index.d.ts +3 -2
  53. package/dist/extension/index.js +3 -3
  54. package/dist/extension/react.js +3 -3
  55. package/dist/extension/subs/index.d.ts +2 -2
  56. package/dist/fan-out/index.d.ts +3 -2
  57. package/dist/idempotent-intake/index.d.ts +3 -2
  58. package/dist/index.d.ts +5 -4
  59. package/dist/index.js +2 -2
  60. package/dist/llm-call/index.d.ts +2 -2
  61. package/dist/llm-call/index.js +7 -7
  62. package/dist/{machine-nGXCMyHn.d.ts → machine-0OmPvsy_.d.ts} +1 -1
  63. package/dist/machine-viz/index.d.ts +1 -1
  64. package/dist/machine-viz/index.js +2 -2
  65. package/dist/mem/index.d.ts +3 -2
  66. package/dist/monitored-run/index.d.ts +4 -3
  67. package/dist/monitored-run/index.js +5 -5
  68. package/dist/node/index.d.ts +3 -2
  69. package/dist/paginated-walk/index.d.ts +2 -2
  70. package/dist/paginated-walk/index.js +7 -7
  71. package/dist/parity/index.d.ts +31 -2
  72. package/dist/parity/index.js +3 -3
  73. package/dist/parity/index.js.map +1 -1
  74. package/dist/pbt/arbitraries/index.d.ts +1 -1
  75. package/dist/pbt/index.d.ts +1 -1
  76. package/dist/pbt/index.js +3 -3
  77. package/dist/pbt/runners/index.d.ts +1 -1
  78. package/dist/pbt/runners/index.js +3 -3
  79. package/dist/poller/index.d.ts +28 -7
  80. package/dist/poller/index.js +3 -3
  81. package/dist/poller/index.js.map +1 -1
  82. package/dist/prediction/index.d.ts +1 -1
  83. package/dist/prediction/index.js +2 -2
  84. package/dist/pure/index.d.ts +1 -1
  85. package/dist/pure/index.js +2 -2
  86. package/dist/react/index.d.ts +3 -2
  87. package/dist/react/index.js +2 -2
  88. package/dist/reconciler/index.d.ts +2 -2
  89. package/dist/reconciler/index.js +7 -7
  90. package/dist/recorder/index.d.ts +3 -2
  91. package/dist/resilient-call/index.d.ts +2 -2
  92. package/dist/resilient-call/index.js +6 -6
  93. package/dist/{runtime-types-BbBq4LWo.d.ts → runtime-types-Bxc637bp.d.ts} +53 -2
  94. package/dist/saga/index.d.ts +3 -2
  95. package/dist/snapshot/index.d.ts +1 -1
  96. package/dist/snapshot/index.js +3 -3
  97. package/dist/subs/index.d.ts +4 -3
  98. package/dist/subs/index.js +3 -3
  99. package/dist/testing/index.d.ts +3 -2
  100. package/dist/testing/index.js +2 -2
  101. package/dist/throttled-input/index.d.ts +2 -2
  102. package/dist/throttled-input/index.js +6 -6
  103. package/dist/token-refresh/index.d.ts +1 -1
  104. package/dist/token-refresh/index.js +3 -3
  105. package/dist/trace-replay/index.d.ts +3 -2
  106. package/dist/trace-replay/index.js +3 -3
  107. package/dist/{types-BMZ7IMrd.d.ts → types-D7u9DkH7.d.ts} +1 -1
  108. package/dist/with-deadline/index.d.ts +1 -1
  109. package/dist/with-deadline/index.js +2 -2
  110. package/dist/with-resilience/index.d.ts +2 -2
  111. package/dist/with-resilience/index.js +6 -6
  112. package/dist/with-telemetry/index.d.ts +1 -1
  113. package/dist/with-telemetry/index.js +2 -2
  114. package/dist/work-queue/adapter.d.ts +3 -2
  115. package/dist/work-queue/index.d.ts +3 -2
  116. package/dist/work-queue/ops.d.ts +3 -2
  117. package/dist/workflow/index.d.ts +6 -4
  118. package/dist/workflow/index.js +1 -1
  119. package/dist/workflow/index.js.map +1 -1
  120. package/package.json +8 -1
  121. package/dist/chunk-O7ED34JH.js.map +0 -1
  122. package/dist/chunk-ZH454CR5.js.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import { DeadlineSub } from '../deadline/index.js';
2
2
  import { IdempotencyStore } from '../idempotency/index.js';
3
- import { C as Cmd, S as Sub } from '../core-JPHsMZVB.js';
3
+ import { C as Cmd, S as Sub } from '../core-Bjc0bS8j.js';
4
4
  import { RetryState, AnyRetryPolicy } from '../retry-backoff/index.js';
5
- import '../types-BMZ7IMrd.js';
5
+ import '../types-D7u9DkH7.js';
6
6
 
7
7
  /**
8
8
  * @demlik/tea/poller — "poll a source every N ms until a predicate holds, with
@@ -203,6 +203,27 @@ type PollerState<R> = (PollerCore<R> & {
203
203
  }) | (PollerCore<R> & {
204
204
  readonly phase: "gave_up";
205
205
  });
206
+ /**
207
+ * The three arms, named — so a verb can DECLARE the phases it can actually
208
+ * reach instead of the whole union.
209
+ *
210
+ * A verb whose return type is the full `PollerState<R>` tells a caller nothing
211
+ * about where the poller went, and a caller that needs to know (a chart
212
+ * declaring an edge's fan-out; an exhaustive `switch`) has to re-derive it by
213
+ * reading the verb's body. Every one of these narrowings is already true of the
214
+ * implementation — `start` only ever builds a `polling` arm, `tickResult` never
215
+ * builds `gave_up`, `tickErr` never builds `done` — so naming them costs
216
+ * nothing at runtime and moves a fact out of the source and into the signature.
217
+ */
218
+ type PollerPolling<R> = Extract<PollerState<R>, {
219
+ phase: "polling";
220
+ }>;
221
+ type PollerDone<R> = Extract<PollerState<R>, {
222
+ phase: "done";
223
+ }>;
224
+ type PollerGaveUp<R> = Extract<PollerState<R>, {
225
+ phase: "gave_up";
226
+ }>;
206
227
  /**
207
228
  * The poller Sub type — a `../deadline` Sub under a fixed id family. One id per
208
229
  * knob instance keeps the reconcile pass from churning the timer: the same id
@@ -223,7 +244,7 @@ type PollerSub = DeadlineSub;
223
244
  */
224
245
  interface Poller<State, R> {
225
246
  /** Seed the Model slice. Idle until `start(...)` arms the first tick. */
226
- init(): PollerState<R>;
247
+ init(): PollerPolling<R>;
227
248
  /**
228
249
  * Arm the first tick. `at` is the current clock (carried in from the Msg /
229
250
  * runtime that kicks the poller off — never read inside the verb). Returns
@@ -234,7 +255,7 @@ interface Poller<State, R> {
234
255
  * fires when the deadline crosses `at + everyMs` and the consumer routes
235
256
  * `deadline_exceeded` → `tick`.
236
257
  */
237
- start(state: PollerState<R>, at: number): readonly [PollerState<R>, readonly Cmd[]];
258
+ start(state: PollerState<R>, at: number): readonly [PollerPolling<R>, readonly Cmd[]];
238
259
  /**
239
260
  * Perform one observation — the cadence verb. The consumer routes the
240
261
  * deadline Sub's `deadline_exceeded` Msg here; `tick` emits the single
@@ -274,7 +295,7 @@ interface Poller<State, R> {
274
295
  * passes the boolean. (`subs` re-checks `phase` to decide arming, so a
275
296
  * mis-passed `untilHeld` cannot leave a "done" poller arming timers.)
276
297
  */
277
- tickResult(state: PollerState<R>, result: R, at: number, untilHeld: boolean): readonly [PollerState<R>, readonly Cmd[]];
298
+ tickResult(state: PollerState<R>, result: R, at: number, untilHeld: boolean): readonly [PollerPolling<R> | PollerDone<R>, readonly Cmd[]];
278
299
  /**
279
300
  * Record a FAILED tick at clock `at`. Backs off instead of holding the
280
301
  * steady cadence:
@@ -300,7 +321,7 @@ interface Poller<State, R> {
300
321
  * body never names the global RNG, so a poller built with a fixed `rng`
301
322
  * replays a failure-and-backoff run bit-for-bit.
302
323
  */
303
- tickErr(state: PollerState<R>, error: unknown, at: number): readonly [PollerState<R>, readonly Cmd[]];
324
+ tickErr(state: PollerState<R>, error: unknown, at: number): readonly [PollerPolling<R> | PollerGaveUp<R>, readonly Cmd[]];
304
325
  /**
305
326
  * The pre-wired subscriptions cell. Returns the single tick-deadline Sub
306
327
  * while the poller is `"polling"` with an armed `nextAtMs`; returns `[]`
@@ -327,4 +348,4 @@ interface Poller<State, R> {
327
348
  */
328
349
  declare function createPoller<State, R>(config: PollerConfig<State, R>, rng?: () => number): Poller<State, R>;
329
350
 
330
- export { type Poller, type PollerConfig, type PollerState, type PollerSub, createPoller };
351
+ export { type Poller, type PollerConfig, type PollerDone, type PollerGaveUp, type PollerPolling, type PollerState, type PollerSub, createPoller };
@@ -1,10 +1,10 @@
1
- import { deadlineSub } from '../chunk-ASX7PL2K.js';
1
+ import { deadlineSub } from '../chunk-JGVL3WPL.js';
2
2
  import { asRng, defaultRetryPolicy, initRetry, recordFailure, shouldRetry, nextDelayMs } from '../chunk-Q2UM7BXU.js';
3
3
  import { idempotencyMemory } from '../chunk-BYQUGDD3.js';
4
4
  import '../chunk-K575PT4S.js';
5
5
  import '../chunk-2YFLOCWX.js';
6
- import '../chunk-ZH454CR5.js';
7
- import '../chunk-O7ED34JH.js';
6
+ import '../chunk-5EHLEUTL.js';
7
+ import '../chunk-B5VXOFMZ.js';
8
8
 
9
9
  // src/poller/index.ts
10
10
  function pollerSubId(atMs) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/poller/index.ts"],"names":["tick"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @demlik/tea/poller — \"poll a source every N ms until a predicate holds, with\n * backoff on failure\" as a TEA knob: one config object + a few pre-wired hooks\n * you spread into your machine.\n *\n * The recipe (which bricks it composes):\n *\n * - `../deadline` — the recurring tick is an ABSOLUTE-deadline Sub.\n * Each successful tick computes the NEXT absolute\n * fire time (`at + everyMs`) and the knob re-arms a\n * deadline against it. Absolute (not relative) so a\n * poller resumed after a DO eviction / page reload\n * fires at the correct wall-clock moment, not\n * `everyMs` after the reload (`../deadline`'s late-\n * subscribe property).\n * - `../retry-backoff` — a failed tick does NOT reschedule on the steady\n * cadence; it backs off. The next attempt is armed\n * `nextDelayMs(retry, policy)` from now, the retry\n * counter advances, and `shouldRetry` decides whether\n * the poller keeps trying or gives up. The bound may\n * be a COUNT or a wall-clock OUTAGE DURATION — see\n * `PollerConfig.retry`; `tickErr`'s `at` is the\n * streak clock, so both flow through unchanged.\n * - `../idempotency` — successive ticks against a flaky source can deliver\n * the SAME observation twice (a retried fetch, a\n * duplicate webhook the poll re-reads). `dedupeKey`\n * lets the consumer key each tick result; a duplicate\n * key is recorded but emits no fresh `onTick` follow-\n * up Cmd, so the side effect runs once per distinct\n * observation.\n *\n * Typical wiring (spread the knob's hooks into your machine):\n *\n * const poll = createPoller<MyState, MyResult>({\n * everyMs: 5_000,\n * until: (s) => s.poll.lastResult?.status === \"ready\",\n * onTick: () => ({ type: \"fetch_status\" }),\n * retry: { ...defaultRetryPolicy, maxAttempts: 4 },\n * });\n *\n * // Model slice:\n * init: (loaded) => [loaded ?? { poll: poll.init(), ... }, []],\n *\n * // The deadline Sub fires `deadline_exceeded` on the cadence/backoff target;\n * // route it to `tick`, the ONLY place the `onTick` fetch is emitted:\n * deadline_exceeded: (s) => {\n * const [slice, cmds] = poll.tick(s.poll);\n * return [{ ...s, poll: slice }, cmds];\n * },\n *\n * // update cells (thread `at` in from the firing Msg / the runtime clock):\n * poll_result: (s, m) => {\n * const untilHeld = config.until({ ...s, ...folded });\n * const [slice, cmds] = poll.tickResult(s.poll, m.result, m.at, untilHeld);\n * return [{ ...s, poll: slice }, cmds];\n * },\n * poll_error: (s, m) => {\n * const [slice, cmds] = poll.tickErr(s.poll, m.error, m.at);\n * return [{ ...s, poll: slice }, cmds];\n * },\n *\n * // Subs — the knob owns the interval/backoff deadline:\n * subscriptions: (s) => poll.subs(s.poll),\n *\n * The cadence has exactly ONE source: the `everyMs` deadline Sub. `start`,\n * `tickResult`, and `tickErr` only RE-ARM the deadline (they emit no cadence\n * Cmd); the fetch fires solely from `tick`, which the consumer routes the\n * deadline's `deadline_exceeded` Msg to. No verb performs an immediate\n * \"fire now\" observation, so the poller never polls faster than `everyMs`.\n *\n * The two non-negotiables every knob preserves hold here:\n * - **Durable** — `tick`, `lastResult`, `retry`, and the next-fire target all\n * live in the Model slice, so a poller survives eviction and resumes its\n * cadence from the persisted target.\n * - **Replayable** — every transition is a pure verb returning new state; the\n * clock is never read inside a verb (the firing Msg carries `at`), and the\n * retry RNG is injected ONCE at `createPoller` and threaded internally (no\n * verb body names the global RNG). `replay` reconstructs a poll run exactly.\n *\n * NOT a substrate primitive: it depends only on sibling subpaths\n * (`../deadline`, `../retry-backoff`, `../idempotency`) and the core `Cmd` /\n * `Sub` types. Consumers reach it via the `@demlik/tea/poller` subpath; the L1\n * escape hatch is those three sibling subpaths threaded by hand.\n */\n\nimport { type DeadlineSub, deadlineSub } from \"../deadline\";\nimport type { IdempotencyStore } from \"../idempotency\";\nimport { idempotencyMemory } from \"../idempotency/adapter\";\nimport type { Cmd, Sub } from \"../index\";\nimport {\n type AnyRetryPolicy,\n asRng,\n defaultRetryPolicy,\n initRetry,\n nextDelayMs,\n type RetryState,\n recordFailure,\n shouldRetry,\n} from \"../retry-backoff\";\n\n/**\n * The poller knob's config — the single object you hand `createPoller`.\n *\n * `State` is the consumer's WHOLE Model (so `until` can read any field), `R` is\n * the shape of one tick observation (`lastResult` carries it forward).\n */\nexport interface PollerConfig<State, R> {\n /**\n * The steady cadence, in milliseconds, between successful ticks. The next\n * tick is armed at the absolute target `at + everyMs` (computed from the\n * `at` the prior tick's Msg carried), NOT `everyMs` from when the Sub\n * happens to be reconciled — so the cadence does not drift across resumes.\n */\n readonly everyMs: number;\n /**\n * The stop predicate, read against the consumer's whole Model. When it\n * returns `true`, the poller is DONE: `subs` returns `[]` (the timer\n * disarms) and no further tick is scheduled. Pure — it must not read the\n * clock or mutate; the substrate calls it inside `subscriptions(state)`.\n */\n readonly until: (state: State) => boolean;\n /**\n * The effect to run on each tick — the actual \"go observe the source\" Cmd\n * (a fetch, a status read, a list call). The knob returns it from\n * `tickResult` when the run continues; the consumer's `interpret` performs\n * it and routes the observation back as the next tick-result Msg.\n *\n * Nullary by contract: a tick carries no per-call argument (the poller\n * re-reads the same source every cadence). Capture any target in the Cmd\n * literal the consumer's `onTick` returns.\n */\n readonly onTick: () => Cmd;\n /**\n * Backoff policy for FAILED ticks. Omit to use `defaultRetryPolicy`\n * (full-jitter, 5 attempts). A success resets the retry counter, so the\n * policy only governs consecutive failures.\n *\n * Any bound the `../retry-backoff` union admits: a count (`RetryPolicy`), a\n * wall-clock outage budget (`DurationRetryPolicy`), or explicit\n * `unbounded: true`. The duration bound needs no extra wiring here — every\n * failure already arrives through `tickErr(state, error, at)` carrying the\n * instant it was observed, so the streak clock is fed from the Msg's own\n * data and the poller never reads a clock of its own (invariant 2).\n */\n readonly retry?: AnyRetryPolicy;\n /**\n * Optional dedupe key derived from a tick result. When provided, a result\n * whose key was already `seen` records the observation but emits NO fresh\n * `onTick` follow-up — the side effect runs once per distinct observation.\n * Omit to treat every tick result as fresh (no dedupe). The dedupe store is\n * bounded by `dedupeTtlMs` if set; otherwise it grows with distinct keys.\n */\n readonly dedupeKey?: (result: R) => string;\n /**\n * TTL for the dedupe store, in milliseconds. Only meaningful when\n * `dedupeKey` is set. An observation older than `dedupeTtlMs` is forgotten,\n * so a key seen long ago is treated as fresh again. Omit for an unbounded\n * dedupe window (every distinct key remembered for the poller's lifetime).\n */\n readonly dedupeTtlMs?: number;\n}\n\n/**\n * The bookkeeping every poller phase carries in common, split out so each arm of\n * the `PollerState` discriminated union names only what it adds (`nextAtMs` on\n * `polling`) — the same discipline the sibling `PaginatorState` uses.\n *\n * - `tick` — count of ticks recorded so far (success or failure). Doubles\n * as an observability cursor and a stable per-tick discriminant.\n * - `lastResult` — the most recent successful observation, or `undefined`\n * before the first success. `until` typically reads it (via the\n * consumer's Model), but the knob never interprets it.\n * - `retry` — the `../retry-backoff` counter for consecutive failures.\n * Reset to `initRetry()` on every success. Once a failure has\n * been recorded it is a `TimedRetryState` (it carries the\n * streak's `firstFailureAtMs`, minted from `tickErr`'s `at`);\n * the field is typed at the `RetryState` supertype because a\n * poller that has never failed has no streak, and a slice\n * persisted before the origin existed rehydrates without one.\n * - `dedupe` — the `../idempotency` store backing `dedupeKey`. An empty,\n * unbounded store when `dedupeKey` is unset (never written).\n */\ninterface PollerCore<R> {\n readonly tick: number;\n readonly lastResult?: R;\n readonly retry: RetryState;\n readonly dedupe: IdempotencyStore<true>;\n}\n\n/**\n * The Model field the poller knob owns — its visible slice (the knob principle:\n * managed state lives in the Model, never a closure, so it is durable and\n * replayable). A discriminated union on `phase` so the armed-timer target lives\n * ONLY where it is meaningful and impossible combinations are unrepresentable\n * (pattern 11), the sibling `PaginatorState` idiom:\n *\n * - `polling` — ticks are scheduled. Carries `nextAtMs`, the ABSOLUTE target\n * the interval/backoff timer is armed against (persisted so a\n * resumed poller re-arms the same moment), or `null` before the\n * first `start` primes it.\n * - `done` — `until` held; the poll is finished. Carries NO `nextAtMs` — a\n * finished poll can never sit on an armed target.\n * - `gave_up` — backoff exhausted `maxAttempts`. Carries NO `nextAtMs` either.\n *\n * Because only `polling` carries `nextAtMs`, `subs` guards on `phase` alone to\n * decide whether a timer is desired — a `done`/`gave_up` state with a live target\n * (the old flat record's hazard) simply cannot be built.\n */\nexport type PollerState<R> =\n | (PollerCore<R> & {\n readonly phase: \"polling\";\n readonly nextAtMs: number | null;\n })\n | (PollerCore<R> & { readonly phase: \"done\" })\n | (PollerCore<R> & { readonly phase: \"gave_up\" });\n\n/**\n * The poller Sub type — a `../deadline` Sub under a fixed id family. One id per\n * knob instance keeps the reconcile pass from churning the timer: the same id\n * across transitions means \"same armed deadline\", even as `nextAtMs` advances.\n */\nexport type PollerSub = DeadlineSub;\n\n/**\n * The Sub id the poller arms its tick deadline under, keyed on the absolute\n * target. The id ROTATES per target because a one-shot deadline timer is\n * exhausted once it fires and the substrate reconciles subs by `id` alone: a\n * fixed id would leave the fired timer registered and never re-arm, so the\n * cadence would die after exactly one tick. Encoding `atMs` in the id makes each\n * cadence/backoff target a distinct identity — the reconcile pass retires the\n * spent timer and arms the next — while a resumed poller re-arms the SAME id\n * from its persisted `nextAtMs` (eviction is idempotent).\n *\n * A single poller machine arms exactly one tick timer at a time; a consumer\n * running several pollers on one machine namespaces them by composing the knob\n * under distinct slices (a future multi-poller config would thread a prefix).\n */\nfunction pollerSubId(atMs: number): string {\n return `poller:tick:${atMs}`;\n}\n\n/**\n * The knob handle returned by `createPoller`. Spread its hooks into your\n * machine: `init()` seeds the slice, `start(...)` arms the first tick,\n * `tickResult` / `tickErr` are the two update verbs, and `subs(...)` returns the\n * timer Sub while the poller is live.\n *\n * Pure-ops + subs module: there is no `handlers(ports)` cell. The poller's I/O\n * is the consumer's own `onTick` Cmd run by the consumer's `interpret` — the\n * knob never owns a port. (Compare `../deadline`, which ships a `subscribe`\n * cell; the poller reuses `../deadline`'s, so it has nothing of its own to\n * splice into `interpret`.)\n */\nexport interface Poller<\n // State is a phantom-style parameter — present so consumers can spell\n // `Poller<MyState, MyResult>` matching `createPoller<State, R>`. It is\n // consumed by the config's `until` predicate, not by any handle method, so\n // it does not appear in the interface body. Same discipline as\n // `BoundMachine`'s `Ctx`.\n // biome-ignore lint/correctness/noUnusedVariables: phantom type parameter (see above)\n State,\n R,\n> {\n /** Seed the Model slice. Idle until `start(...)` arms the first tick. */\n init(): PollerState<R>;\n /**\n * Arm the first tick. `at` is the current clock (carried in from the Msg /\n * runtime that kicks the poller off — never read inside the verb). Returns\n * the slice with the first deadline target set to `at + everyMs` and emits NO\n * Cmd. The cadence has ONE source — the deadline Sub — so `start` does not\n * perform the first observation itself (an immediate `onTick` here would race\n * the timer and poll as fast as the source responds). The first observation\n * fires when the deadline crosses `at + everyMs` and the consumer routes\n * `deadline_exceeded` → `tick`.\n */\n start(\n state: PollerState<R>,\n at: number,\n ): readonly [PollerState<R>, readonly Cmd[]];\n /**\n * Perform one observation — the cadence verb. The consumer routes the\n * deadline Sub's `deadline_exceeded` Msg here; `tick` emits the single\n * `config.onTick()` Cmd that goes and reads the source. This is the ONLY\n * next-tick mechanism: the timer fires, the consumer calls `tick`, the fetch\n * runs, and the observation comes back through `tickResult` / `tickErr`\n * (which re-arm the next deadline). A no-op on a finished poller — a stale\n * deadline fire racing a terminal transition the reconcile pass has not yet\n * retired emits nothing.\n *\n * Takes no `at`: `tick` does not touch the schedule (the just-fired deadline\n * is exhausted; the next target is set when the result lands), so it needs no\n * clock — keeping it trivially pure.\n */\n tick(state: PollerState<R>): readonly [PollerState<R>, readonly Cmd[]];\n /**\n * Record a successful tick observation at clock `at`.\n *\n * - Resets the retry counter (a success clears consecutive-failure state).\n * - Stores `result` as `lastResult` and bumps `tick`.\n * - Re-arms the next tick at the absolute target `at + everyMs` (the steady\n * cadence). It emits NO cadence `onTick` — the next observation fires from\n * the deadline Sub via `tick`, never immediately from a result.\n * - Dedupe (opt-in, orthogonal to cadence): when `dedupeKey` is set, a FRESH\n * observation emits one `onTick` FOLLOW-UP (the consumer's \"run the\n * downstream side effect once per distinct observation\" hook) and a\n * DUPLICATE key records the sighting but suppresses the follow-up. With no\n * `dedupeKey`, `tickResult` emits nothing — the cadence `tick` is the only\n * `onTick`, so an unconfigured poller never double-fetches.\n * - UNLESS `untilHeld` is `true` (the consumer evaluated `config.until`\n * against the post-result Model) — then the poller enters `\"done\"`,\n * disarms (`nextAtMs: null`), and emits no Cmd, dedupe or otherwise.\n *\n * `untilHeld` is passed in rather than computed here because `until` reads\n * the consumer's WHOLE Model, which this verb does not hold — the consumer\n * evaluates it at the call site after folding `result` into its Model and\n * passes the boolean. (`subs` re-checks `phase` to decide arming, so a\n * mis-passed `untilHeld` cannot leave a \"done\" poller arming timers.)\n */\n tickResult(\n state: PollerState<R>,\n result: R,\n at: number,\n untilHeld: boolean,\n ): readonly [PollerState<R>, readonly Cmd[]];\n /**\n * Record a FAILED tick at clock `at`. Backs off instead of holding the\n * steady cadence:\n *\n * - Advances the retry counter (`recordFailure`).\n * - If `shouldRetry` still permits another attempt, arms the next tick at\n * `at + nextDelayMs(retry, policy)` (the backoff curve) and emits NO Cmd —\n * the retry observation fires when that backoff deadline crosses and the\n * consumer routes `deadline_exceeded` → `tick`. The timer is the single\n * next-tick mechanism, on the backoff curve here instead of `everyMs`.\n * - If backoff is exhausted (`!shouldRetry`), enters `\"gave_up\"`, disarms\n * (`nextAtMs: null`), and emits no Cmd.\n *\n * `at` is both the backoff anchor AND the streak clock: it is passed to\n * `recordFailure` (starting / preserving `firstFailureAtMs`) and to\n * `shouldRetry`, so a `DurationRetryPolicy` on `config.retry` is honoured\n * with no extra wiring. Under a duration bound the poller keeps retrying\n * however many attempts the outage takes, and stops at the declared\n * wall-clock budget measured from the streak's FIRST failure.\n *\n * The backoff jitter uses the `rng` injected ONCE at `createPoller` (default\n * `Math.random` at the effect boundary, a fixed value in tests); the verb\n * body never names the global RNG, so a poller built with a fixed `rng`\n * replays a failure-and-backoff run bit-for-bit.\n */\n tickErr(\n state: PollerState<R>,\n error: unknown,\n at: number,\n ): readonly [PollerState<R>, readonly Cmd[]];\n /**\n * The pre-wired subscriptions cell. Returns the single tick-deadline Sub\n * while the poller is `\"polling\"` with an armed `nextAtMs`; returns `[]`\n * once `\"done\"` / `\"gave_up\"` (the timer disarms) or before `start`. Assign\n * directly: `subscriptions: (s) => poll.subs(s.poll)`.\n *\n * The `DeadlineSub` it returns is consumed by `../deadline`'s\n * `subscribeDeadline` cell, which the consumer wires into `subscribe` —\n * the poller does not redraw the timer lifecycle.\n */\n subs(state: PollerState<R>): readonly Sub[];\n}\n\n/**\n * Build a poller knob from `config`. Returns the hook bag to spread into a\n * machine (see the module doc-comment for the wiring). Pure factory — no clock\n * read, no timer; everything happens when the consumer calls the returned\n * verbs / `subs` with an injected `at`.\n *\n * `rng` is injected ONCE here (default `Math.random` at the effect boundary, a\n * fixed value in tests) and threaded internally into the backoff jitter. No\n * verb body ever names the global RNG, so a poller built with a fixed `rng`\n * replays a failure-and-backoff run bit-for-bit (invariant 2: the verbs are\n * pure given the injected `rng`).\n */\nexport function createPoller<State, R>(\n config: PollerConfig<State, R>,\n rng: () => number = Math.random,\n): Poller<State, R> {\n // Re-affirm the `[0, 1)` contract at the seam where the injected generator\n // enters the backoff math (brand from `../retry-backoff`). The factory's\n // public param stays a plain `() => number` so transitive callers are\n // unchanged; `asRng` is the single point where it's branded for `nextDelayMs`.\n const rngBranded = asRng(rng);\n const policy: AnyRetryPolicy = config.retry ?? defaultRetryPolicy;\n\n // The dedupe store seam, bound once. The poller delegates its dedupe-window\n // reads (`memory.isSeen`) and the per-sighting touch (`memory.remember`) to\n // the verbs, so the `IdempotencyStore` shape stays insulated.\n const memory = idempotencyMemory<true>();\n\n function init(): PollerState<R> {\n return {\n phase: \"polling\",\n tick: 0,\n retry: initRetry(),\n nextAtMs: null,\n // The dedupe store is created regardless; it is only WRITTEN when\n // `dedupeKey` is set, so an unconfigured poller carries an inert empty\n // store (no per-op cost, JSON-serializable, durable like the rest of\n // the slice).\n dedupe: memory.init({ ttlMs: config.dedupeTtlMs }),\n };\n }\n\n function start(\n state: PollerState<R>,\n at: number,\n ): readonly [PollerState<R>, readonly Cmd[]] {\n // Arm the first deadline at `at + everyMs` and emit NOTHING. The cadence has\n // ONE source — the `everyMs` deadline Sub. `start` does not perform the\n // first observation itself: that would be a second, immediate next-tick\n // mechanism racing the timer (it would fire as fast as the source responds,\n // never honoring `everyMs`). The first observation fires when the deadline\n // crosses `at + everyMs` and the consumer routes `deadline_exceeded` → the\n // `tick` verb (which emits `onTick`). No drift, no double-fire. Build the\n // `polling` arm explicitly so a (re)start from a terminal state adopts a live\n // target rather than spreading a terminal arm that never had one.\n return [\n {\n phase: \"polling\",\n tick: state.tick,\n lastResult: state.lastResult,\n retry: state.retry,\n nextAtMs: at + config.everyMs,\n dedupe: state.dedupe,\n },\n [],\n ];\n }\n\n function tick(\n state: PollerState<R>,\n ): readonly [PollerState<R>, readonly Cmd[]] {\n // The deadline fired. Perform the observation — this is the ONLY place the\n // `onTick` Cmd is emitted, so the timer is the single next-tick mechanism.\n // No schedule change here: the just-fired deadline is exhausted, and the\n // NEXT target is re-armed when the observation lands (`tickResult` /\n // `tickErr`). A no-op on a finished poller (a stale fire racing a terminal\n // transition the reconcile pass has not yet retired).\n if (state.phase !== \"polling\") return [state, []];\n return [state, [config.onTick()]];\n }\n\n function tickResult(\n state: PollerState<R>,\n result: R,\n at: number,\n untilHeld: boolean,\n ): readonly [PollerState<R>, readonly Cmd[]] {\n // A success always clears consecutive-failure state and records the datum.\n // The next arm is built explicitly (not spread from `state`) so `done`\n // carries no `nextAtMs` — a finished poll can never sit on an armed target.\n const tick = state.tick + 1;\n\n // Stop condition first: if the consumer's `until` held against the\n // post-result Model, we are DONE — disarm and emit nothing, regardless of\n // dedupe (the final observation is still recorded as `lastResult`).\n if (untilHeld) {\n return [\n {\n phase: \"done\",\n tick,\n lastResult: result,\n retry: initRetry(),\n dedupe: state.dedupe,\n },\n [],\n ];\n }\n\n // Re-arm the steady cadence: the next observation fires when THIS deadline\n // (`at + everyMs`) crosses and the consumer routes `deadline_exceeded` →\n // `tick`. `tickResult` itself never emits the cadence `onTick` — the timer\n // is the single next-tick mechanism (recording a result is not a reason to\n // immediately re-fetch; that would poll as fast as the source responds).\n const next: PollerState<R> = {\n phase: \"polling\",\n tick,\n lastResult: result,\n retry: initRetry(),\n nextAtMs: at + config.everyMs,\n dedupe: state.dedupe,\n };\n\n // Dedupe is an OPT-IN follow-up, orthogonal to the cadence above. When\n // `dedupeKey` is set, a FRESH observation emits one `onTick` follow-up (the\n // consumer's \"run the downstream side effect once per distinct observation\"\n // hook); a DUPLICATE key records the sighting but suppresses that follow-up.\n // With no `dedupeKey` there is no follow-up at all — the cadence `tick` is\n // the only `onTick`, so an unconfigured poller never double-fetches.\n if (!config.dedupeKey) return [next, []];\n\n const key = config.dedupeKey(result);\n const suppress = memory.isSeen(next.dedupe, key, at);\n // `remember` refreshes the key's TTL clock on every sighting (the\n // idempotency \"touch\" semantics), so a steadily-repeating observation never\n // ages out mid-poll. The stored value is the unit `true` — the poller only\n // needs presence, not a payload.\n const deduped: PollerState<R> = {\n ...next,\n dedupe: memory.remember(next.dedupe, key, true, at),\n };\n return [deduped, suppress ? [] : [config.onTick()]];\n }\n\n function tickErr(\n state: PollerState<R>,\n error: unknown,\n at: number,\n ): readonly [PollerState<R>, readonly Cmd[]] {\n // `at` is the failure's OBSERVATION instant, already carried in from the\n // Msg — so it doubles as the streak clock a duration bound is measured\n // against. The first failure of a streak sets `firstFailureAtMs`; every\n // later one preserves it, and a success resets to `initRetry()` (dropping\n // the origin with it). Nothing here reads a clock: `at` is Msg data,\n // exactly as `rng` is factory-injected data (invariant 2).\n const retry = recordFailure(state.retry, error, at);\n const tick = state.tick + 1;\n\n // Backoff exhausted → give up: emit nothing. `shouldRetry` reads the\n // just-advanced `retry`, so under a count bound the (maxAttempts)-th\n // failure is the one that stops the poller, and under a duration bound the\n // first failure observed at or past `firstFailureAtMs + maxElapsedMs` is.\n // The `gave_up` arm carries no `nextAtMs` — there is no next attempt to arm.\n if (!shouldRetry(retry, policy, at)) {\n return [\n {\n phase: \"gave_up\",\n tick,\n lastResult: state.lastResult,\n retry,\n dedupe: state.dedupe,\n },\n [],\n ];\n }\n\n // Back off: arm the next attempt at `at + backoff(retry)` rather than the\n // steady cadence, and emit NOTHING. The retry observation fires when this\n // backoff deadline crosses and the consumer routes `deadline_exceeded` →\n // `tick` — the timer is the single next-tick mechanism, on the backoff\n // curve here instead of `everyMs`. `rng` is the value injected once at\n // `createPoller`; the verb body never names the global RNG, so a fixed\n // `rng` makes the backoff target replay bit-for-bit.\n const delayMs = nextDelayMs(retry, policy, rngBranded);\n return [\n {\n phase: \"polling\",\n tick,\n lastResult: state.lastResult,\n retry,\n nextAtMs: at + delayMs,\n dedupe: state.dedupe,\n },\n [],\n ];\n }\n\n function subs(state: PollerState<R>): readonly Sub[] {\n // Arm the tick deadline only while live. `phase` is the authority (so a\n // mis-passed `untilHeld` into `tickResult` cannot resurrect a finished\n // poller); `nextAtMs` is the absolute target `../deadline` translates to a\n // remaining delay at subscribe time — a resumed poller re-arms the exact\n // persisted moment.\n if (state.phase !== \"polling\" || state.nextAtMs === null) return [];\n // The Sub id ROTATES with the target. A one-shot deadline timer is\n // exhausted once it fires, and the substrate's reconcile pass keys identity\n // by `id` alone — a fixed id would leave the fired timer in the registry and\n // NEVER re-arm for the next cadence (cadence dies after one tick). Folding\n // `nextAtMs` into the id makes each successive target a distinct identity:\n // the reconcile pass retires the spent timer (old id absent from the desired\n // set) and arms the next one (new id). A resumed poller re-arms the SAME id\n // from its persisted `nextAtMs`, so eviction is idempotent.\n return [deadlineSub(pollerSubId(state.nextAtMs), state.nextAtMs)];\n }\n\n return { init, start, tick, tickResult, tickErr, subs };\n}\n"]}
1
+ {"version":3,"sources":["../../src/poller/index.ts"],"names":["tick"],"mappings":";;;;;;;;;AA6PA,SAAS,YAAY,IAAA,EAAsB;AACzC,EAAA,OAAO,eAAe,IAAI,CAAA,CAAA;AAC5B;AA4IO,SAAS,YAAA,CACd,MAAA,EACA,GAAA,GAAoB,IAAA,CAAK,MAAA,EACP;AAKlB,EAAA,MAAM,UAAA,GAAa,MAAM,GAAG,CAAA;AAC5B,EAAA,MAAM,MAAA,GAAyB,OAAO,KAAA,IAAS,kBAAA;AAK/C,EAAA,MAAM,SAAS,iBAAA,EAAwB;AAEvC,EAAA,SAAS,IAAA,GAAyB;AAChC,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,SAAA;AAAA,MACP,IAAA,EAAM,CAAA;AAAA,MACN,OAAO,SAAA,EAAU;AAAA,MACjB,QAAA,EAAU,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAKV,QAAQ,MAAA,CAAO,IAAA,CAAK,EAAE,KAAA,EAAO,MAAA,CAAO,aAAa;AAAA,KACnD;AAAA,EACF;AAEA,EAAA,SAAS,KAAA,CACP,OACA,EAAA,EAC6C;AAU7C,IAAA,OAAO;AAAA,MACL;AAAA,QACE,KAAA,EAAO,SAAA;AAAA,QACP,MAAM,KAAA,CAAM,IAAA;AAAA,QACZ,YAAY,KAAA,CAAM,UAAA;AAAA,QAClB,OAAO,KAAA,CAAM,KAAA;AAAA,QACb,QAAA,EAAU,KAAK,MAAA,CAAO,OAAA;AAAA,QACtB,QAAQ,KAAA,CAAM;AAAA,OAChB;AAAA,MACA;AAAC,KACH;AAAA,EACF;AAEA,EAAA,SAAS,KACP,KAAA,EAC2C;AAO3C,IAAA,IAAI,MAAM,KAAA,KAAU,SAAA,SAAkB,CAAC,KAAA,EAAO,EAAE,CAAA;AAChD,IAAA,OAAO,CAAC,KAAA,EAAO,CAAC,MAAA,CAAO,MAAA,EAAQ,CAAC,CAAA;AAAA,EAClC;AAEA,EAAA,SAAS,UAAA,CACP,KAAA,EACA,MAAA,EACA,EAAA,EACA,SAAA,EAC6D;AAI7D,IAAA,MAAMA,KAAAA,GAAO,MAAM,IAAA,GAAO,CAAA;AAK1B,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,OAAO;AAAA,QACL;AAAA,UACE,KAAA,EAAO,MAAA;AAAA,UACP,IAAA,EAAAA,KAAAA;AAAA,UACA,UAAA,EAAY,MAAA;AAAA,UACZ,OAAO,SAAA,EAAU;AAAA,UACjB,QAAQ,KAAA,CAAM;AAAA,SAChB;AAAA,QACA;AAAC,OACH;AAAA,IACF;AAOA,IAAA,MAAM,IAAA,GAAyB;AAAA,MAC7B,KAAA,EAAO,SAAA;AAAA,MACP,IAAA,EAAAA,KAAAA;AAAA,MACA,UAAA,EAAY,MAAA;AAAA,MACZ,OAAO,SAAA,EAAU;AAAA,MACjB,QAAA,EAAU,KAAK,MAAA,CAAO,OAAA;AAAA,MACtB,QAAQ,KAAA,CAAM;AAAA,KAChB;AAQA,IAAA,IAAI,CAAC,MAAA,CAAO,SAAA,SAAkB,CAAC,IAAA,EAAM,EAAE,CAAA;AAEvC,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,SAAA,CAAU,MAAM,CAAA;AACnC,IAAA,MAAM,WAAW,MAAA,CAAO,MAAA,CAAO,IAAA,CAAK,MAAA,EAAQ,KAAK,EAAE,CAAA;AAKnD,IAAA,MAAM,OAAA,GAA4B;AAAA,MAChC,GAAG,IAAA;AAAA,MACH,QAAQ,MAAA,CAAO,QAAA,CAAS,KAAK,MAAA,EAAQ,GAAA,EAAK,MAAM,EAAE;AAAA,KACpD;AACA,IAAA,OAAO,CAAC,SAAS,QAAA,GAAW,KAAK,CAAC,MAAA,CAAO,MAAA,EAAQ,CAAC,CAAA;AAAA,EACpD;AAEA,EAAA,SAAS,OAAA,CACP,KAAA,EACA,KAAA,EACA,EAAA,EAC+D;AAO/D,IAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,KAAA,CAAM,KAAA,EAAO,OAAO,EAAE,CAAA;AAClD,IAAA,MAAMA,KAAAA,GAAO,MAAM,IAAA,GAAO,CAAA;AAO1B,IAAA,IAAI,CAAC,WAAA,CAAY,KAAA,EAAO,MAAA,EAAQ,EAAE,CAAA,EAAG;AACnC,MAAA,OAAO;AAAA,QACL;AAAA,UACE,KAAA,EAAO,SAAA;AAAA,UACP,IAAA,EAAAA,KAAAA;AAAA,UACA,YAAY,KAAA,CAAM,UAAA;AAAA,UAClB,KAAA;AAAA,UACA,QAAQ,KAAA,CAAM;AAAA,SAChB;AAAA,QACA;AAAC,OACH;AAAA,IACF;AASA,IAAA,MAAM,OAAA,GAAU,WAAA,CAAY,KAAA,EAAO,MAAA,EAAQ,UAAU,CAAA;AACrD,IAAA,OAAO;AAAA,MACL;AAAA,QACE,KAAA,EAAO,SAAA;AAAA,QACP,IAAA,EAAAA,KAAAA;AAAA,QACA,YAAY,KAAA,CAAM,UAAA;AAAA,QAClB,KAAA;AAAA,QACA,UAAU,EAAA,GAAK,OAAA;AAAA,QACf,QAAQ,KAAA,CAAM;AAAA,OAChB;AAAA,MACA;AAAC,KACH;AAAA,EACF;AAEA,EAAA,SAAS,KAAK,KAAA,EAAuC;AAMnD,IAAA,IAAI,MAAM,KAAA,KAAU,SAAA,IAAa,MAAM,QAAA,KAAa,IAAA,SAAa,EAAC;AASlE,IAAA,OAAO,CAAC,YAAY,WAAA,CAAY,KAAA,CAAM,QAAQ,CAAA,EAAG,KAAA,CAAM,QAAQ,CAAC,CAAA;AAAA,EAClE;AAEA,EAAA,OAAO,EAAE,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,UAAA,EAAY,SAAS,IAAA,EAAK;AACxD","file":"index.js","sourcesContent":["/**\n * @demlik/tea/poller — \"poll a source every N ms until a predicate holds, with\n * backoff on failure\" as a TEA knob: one config object + a few pre-wired hooks\n * you spread into your machine.\n *\n * The recipe (which bricks it composes):\n *\n * - `../deadline` — the recurring tick is an ABSOLUTE-deadline Sub.\n * Each successful tick computes the NEXT absolute\n * fire time (`at + everyMs`) and the knob re-arms a\n * deadline against it. Absolute (not relative) so a\n * poller resumed after a DO eviction / page reload\n * fires at the correct wall-clock moment, not\n * `everyMs` after the reload (`../deadline`'s late-\n * subscribe property).\n * - `../retry-backoff` — a failed tick does NOT reschedule on the steady\n * cadence; it backs off. The next attempt is armed\n * `nextDelayMs(retry, policy)` from now, the retry\n * counter advances, and `shouldRetry` decides whether\n * the poller keeps trying or gives up. The bound may\n * be a COUNT or a wall-clock OUTAGE DURATION — see\n * `PollerConfig.retry`; `tickErr`'s `at` is the\n * streak clock, so both flow through unchanged.\n * - `../idempotency` — successive ticks against a flaky source can deliver\n * the SAME observation twice (a retried fetch, a\n * duplicate webhook the poll re-reads). `dedupeKey`\n * lets the consumer key each tick result; a duplicate\n * key is recorded but emits no fresh `onTick` follow-\n * up Cmd, so the side effect runs once per distinct\n * observation.\n *\n * Typical wiring (spread the knob's hooks into your machine):\n *\n * const poll = createPoller<MyState, MyResult>({\n * everyMs: 5_000,\n * until: (s) => s.poll.lastResult?.status === \"ready\",\n * onTick: () => ({ type: \"fetch_status\" }),\n * retry: { ...defaultRetryPolicy, maxAttempts: 4 },\n * });\n *\n * // Model slice:\n * init: (loaded) => [loaded ?? { poll: poll.init(), ... }, []],\n *\n * // The deadline Sub fires `deadline_exceeded` on the cadence/backoff target;\n * // route it to `tick`, the ONLY place the `onTick` fetch is emitted:\n * deadline_exceeded: (s) => {\n * const [slice, cmds] = poll.tick(s.poll);\n * return [{ ...s, poll: slice }, cmds];\n * },\n *\n * // update cells (thread `at` in from the firing Msg / the runtime clock):\n * poll_result: (s, m) => {\n * const untilHeld = config.until({ ...s, ...folded });\n * const [slice, cmds] = poll.tickResult(s.poll, m.result, m.at, untilHeld);\n * return [{ ...s, poll: slice }, cmds];\n * },\n * poll_error: (s, m) => {\n * const [slice, cmds] = poll.tickErr(s.poll, m.error, m.at);\n * return [{ ...s, poll: slice }, cmds];\n * },\n *\n * // Subs — the knob owns the interval/backoff deadline:\n * subscriptions: (s) => poll.subs(s.poll),\n *\n * The cadence has exactly ONE source: the `everyMs` deadline Sub. `start`,\n * `tickResult`, and `tickErr` only RE-ARM the deadline (they emit no cadence\n * Cmd); the fetch fires solely from `tick`, which the consumer routes the\n * deadline's `deadline_exceeded` Msg to. No verb performs an immediate\n * \"fire now\" observation, so the poller never polls faster than `everyMs`.\n *\n * The two non-negotiables every knob preserves hold here:\n * - **Durable** — `tick`, `lastResult`, `retry`, and the next-fire target all\n * live in the Model slice, so a poller survives eviction and resumes its\n * cadence from the persisted target.\n * - **Replayable** — every transition is a pure verb returning new state; the\n * clock is never read inside a verb (the firing Msg carries `at`), and the\n * retry RNG is injected ONCE at `createPoller` and threaded internally (no\n * verb body names the global RNG). `replay` reconstructs a poll run exactly.\n *\n * NOT a substrate primitive: it depends only on sibling subpaths\n * (`../deadline`, `../retry-backoff`, `../idempotency`) and the core `Cmd` /\n * `Sub` types. Consumers reach it via the `@demlik/tea/poller` subpath; the L1\n * escape hatch is those three sibling subpaths threaded by hand.\n */\n\nimport { type DeadlineSub, deadlineSub } from \"../deadline\";\nimport type { IdempotencyStore } from \"../idempotency\";\nimport { idempotencyMemory } from \"../idempotency/adapter\";\nimport type { Cmd, Sub } from \"../index\";\nimport {\n type AnyRetryPolicy,\n asRng,\n defaultRetryPolicy,\n initRetry,\n nextDelayMs,\n type RetryState,\n recordFailure,\n shouldRetry,\n} from \"../retry-backoff\";\n\n/**\n * The poller knob's config — the single object you hand `createPoller`.\n *\n * `State` is the consumer's WHOLE Model (so `until` can read any field), `R` is\n * the shape of one tick observation (`lastResult` carries it forward).\n */\nexport interface PollerConfig<State, R> {\n /**\n * The steady cadence, in milliseconds, between successful ticks. The next\n * tick is armed at the absolute target `at + everyMs` (computed from the\n * `at` the prior tick's Msg carried), NOT `everyMs` from when the Sub\n * happens to be reconciled — so the cadence does not drift across resumes.\n */\n readonly everyMs: number;\n /**\n * The stop predicate, read against the consumer's whole Model. When it\n * returns `true`, the poller is DONE: `subs` returns `[]` (the timer\n * disarms) and no further tick is scheduled. Pure — it must not read the\n * clock or mutate; the substrate calls it inside `subscriptions(state)`.\n */\n readonly until: (state: State) => boolean;\n /**\n * The effect to run on each tick — the actual \"go observe the source\" Cmd\n * (a fetch, a status read, a list call). The knob returns it from\n * `tickResult` when the run continues; the consumer's `interpret` performs\n * it and routes the observation back as the next tick-result Msg.\n *\n * Nullary by contract: a tick carries no per-call argument (the poller\n * re-reads the same source every cadence). Capture any target in the Cmd\n * literal the consumer's `onTick` returns.\n */\n readonly onTick: () => Cmd;\n /**\n * Backoff policy for FAILED ticks. Omit to use `defaultRetryPolicy`\n * (full-jitter, 5 attempts). A success resets the retry counter, so the\n * policy only governs consecutive failures.\n *\n * Any bound the `../retry-backoff` union admits: a count (`RetryPolicy`), a\n * wall-clock outage budget (`DurationRetryPolicy`), or explicit\n * `unbounded: true`. The duration bound needs no extra wiring here — every\n * failure already arrives through `tickErr(state, error, at)` carrying the\n * instant it was observed, so the streak clock is fed from the Msg's own\n * data and the poller never reads a clock of its own (invariant 2).\n */\n readonly retry?: AnyRetryPolicy;\n /**\n * Optional dedupe key derived from a tick result. When provided, a result\n * whose key was already `seen` records the observation but emits NO fresh\n * `onTick` follow-up — the side effect runs once per distinct observation.\n * Omit to treat every tick result as fresh (no dedupe). The dedupe store is\n * bounded by `dedupeTtlMs` if set; otherwise it grows with distinct keys.\n */\n readonly dedupeKey?: (result: R) => string;\n /**\n * TTL for the dedupe store, in milliseconds. Only meaningful when\n * `dedupeKey` is set. An observation older than `dedupeTtlMs` is forgotten,\n * so a key seen long ago is treated as fresh again. Omit for an unbounded\n * dedupe window (every distinct key remembered for the poller's lifetime).\n */\n readonly dedupeTtlMs?: number;\n}\n\n/**\n * The bookkeeping every poller phase carries in common, split out so each arm of\n * the `PollerState` discriminated union names only what it adds (`nextAtMs` on\n * `polling`) — the same discipline the sibling `PaginatorState` uses.\n *\n * - `tick` — count of ticks recorded so far (success or failure). Doubles\n * as an observability cursor and a stable per-tick discriminant.\n * - `lastResult` — the most recent successful observation, or `undefined`\n * before the first success. `until` typically reads it (via the\n * consumer's Model), but the knob never interprets it.\n * - `retry` — the `../retry-backoff` counter for consecutive failures.\n * Reset to `initRetry()` on every success. Once a failure has\n * been recorded it is a `TimedRetryState` (it carries the\n * streak's `firstFailureAtMs`, minted from `tickErr`'s `at`);\n * the field is typed at the `RetryState` supertype because a\n * poller that has never failed has no streak, and a slice\n * persisted before the origin existed rehydrates without one.\n * - `dedupe` — the `../idempotency` store backing `dedupeKey`. An empty,\n * unbounded store when `dedupeKey` is unset (never written).\n */\ninterface PollerCore<R> {\n readonly tick: number;\n readonly lastResult?: R;\n readonly retry: RetryState;\n readonly dedupe: IdempotencyStore<true>;\n}\n\n/**\n * The Model field the poller knob owns — its visible slice (the knob principle:\n * managed state lives in the Model, never a closure, so it is durable and\n * replayable). A discriminated union on `phase` so the armed-timer target lives\n * ONLY where it is meaningful and impossible combinations are unrepresentable\n * (pattern 11), the sibling `PaginatorState` idiom:\n *\n * - `polling` — ticks are scheduled. Carries `nextAtMs`, the ABSOLUTE target\n * the interval/backoff timer is armed against (persisted so a\n * resumed poller re-arms the same moment), or `null` before the\n * first `start` primes it.\n * - `done` — `until` held; the poll is finished. Carries NO `nextAtMs` — a\n * finished poll can never sit on an armed target.\n * - `gave_up` — backoff exhausted `maxAttempts`. Carries NO `nextAtMs` either.\n *\n * Because only `polling` carries `nextAtMs`, `subs` guards on `phase` alone to\n * decide whether a timer is desired — a `done`/`gave_up` state with a live target\n * (the old flat record's hazard) simply cannot be built.\n */\nexport type PollerState<R> =\n | (PollerCore<R> & {\n readonly phase: \"polling\";\n readonly nextAtMs: number | null;\n })\n | (PollerCore<R> & { readonly phase: \"done\" })\n | (PollerCore<R> & { readonly phase: \"gave_up\" });\n\n/**\n * The three arms, named — so a verb can DECLARE the phases it can actually\n * reach instead of the whole union.\n *\n * A verb whose return type is the full `PollerState<R>` tells a caller nothing\n * about where the poller went, and a caller that needs to know (a chart\n * declaring an edge's fan-out; an exhaustive `switch`) has to re-derive it by\n * reading the verb's body. Every one of these narrowings is already true of the\n * implementation — `start` only ever builds a `polling` arm, `tickResult` never\n * builds `gave_up`, `tickErr` never builds `done` — so naming them costs\n * nothing at runtime and moves a fact out of the source and into the signature.\n */\nexport type PollerPolling<R> = Extract<PollerState<R>, { phase: \"polling\" }>;\nexport type PollerDone<R> = Extract<PollerState<R>, { phase: \"done\" }>;\nexport type PollerGaveUp<R> = Extract<PollerState<R>, { phase: \"gave_up\" }>;\n\n/**\n * The poller Sub type — a `../deadline` Sub under a fixed id family. One id per\n * knob instance keeps the reconcile pass from churning the timer: the same id\n * across transitions means \"same armed deadline\", even as `nextAtMs` advances.\n */\nexport type PollerSub = DeadlineSub;\n\n/**\n * The Sub id the poller arms its tick deadline under, keyed on the absolute\n * target. The id ROTATES per target because a one-shot deadline timer is\n * exhausted once it fires and the substrate reconciles subs by `id` alone: a\n * fixed id would leave the fired timer registered and never re-arm, so the\n * cadence would die after exactly one tick. Encoding `atMs` in the id makes each\n * cadence/backoff target a distinct identity — the reconcile pass retires the\n * spent timer and arms the next — while a resumed poller re-arms the SAME id\n * from its persisted `nextAtMs` (eviction is idempotent).\n *\n * A single poller machine arms exactly one tick timer at a time; a consumer\n * running several pollers on one machine namespaces them by composing the knob\n * under distinct slices (a future multi-poller config would thread a prefix).\n */\nfunction pollerSubId(atMs: number): string {\n return `poller:tick:${atMs}`;\n}\n\n/**\n * The knob handle returned by `createPoller`. Spread its hooks into your\n * machine: `init()` seeds the slice, `start(...)` arms the first tick,\n * `tickResult` / `tickErr` are the two update verbs, and `subs(...)` returns the\n * timer Sub while the poller is live.\n *\n * Pure-ops + subs module: there is no `handlers(ports)` cell. The poller's I/O\n * is the consumer's own `onTick` Cmd run by the consumer's `interpret` — the\n * knob never owns a port. (Compare `../deadline`, which ships a `subscribe`\n * cell; the poller reuses `../deadline`'s, so it has nothing of its own to\n * splice into `interpret`.)\n */\nexport interface Poller<\n // State is a phantom-style parameter — present so consumers can spell\n // `Poller<MyState, MyResult>` matching `createPoller<State, R>`. It is\n // consumed by the config's `until` predicate, not by any handle method, so\n // it does not appear in the interface body. Same discipline as\n // `BoundMachine`'s `Ctx`.\n // biome-ignore lint/correctness/noUnusedVariables: phantom type parameter (see above)\n State,\n R,\n> {\n /** Seed the Model slice. Idle until `start(...)` arms the first tick. */\n init(): PollerPolling<R>;\n /**\n * Arm the first tick. `at` is the current clock (carried in from the Msg /\n * runtime that kicks the poller off — never read inside the verb). Returns\n * the slice with the first deadline target set to `at + everyMs` and emits NO\n * Cmd. The cadence has ONE source — the deadline Sub — so `start` does not\n * perform the first observation itself (an immediate `onTick` here would race\n * the timer and poll as fast as the source responds). The first observation\n * fires when the deadline crosses `at + everyMs` and the consumer routes\n * `deadline_exceeded` → `tick`.\n */\n start(\n state: PollerState<R>,\n at: number,\n ): readonly [PollerPolling<R>, readonly Cmd[]];\n /**\n * Perform one observation — the cadence verb. The consumer routes the\n * deadline Sub's `deadline_exceeded` Msg here; `tick` emits the single\n * `config.onTick()` Cmd that goes and reads the source. This is the ONLY\n * next-tick mechanism: the timer fires, the consumer calls `tick`, the fetch\n * runs, and the observation comes back through `tickResult` / `tickErr`\n * (which re-arm the next deadline). A no-op on a finished poller — a stale\n * deadline fire racing a terminal transition the reconcile pass has not yet\n * retired emits nothing.\n *\n * Takes no `at`: `tick` does not touch the schedule (the just-fired deadline\n * is exhausted; the next target is set when the result lands), so it needs no\n * clock — keeping it trivially pure.\n */\n tick(state: PollerState<R>): readonly [PollerState<R>, readonly Cmd[]];\n /**\n * Record a successful tick observation at clock `at`.\n *\n * - Resets the retry counter (a success clears consecutive-failure state).\n * - Stores `result` as `lastResult` and bumps `tick`.\n * - Re-arms the next tick at the absolute target `at + everyMs` (the steady\n * cadence). It emits NO cadence `onTick` — the next observation fires from\n * the deadline Sub via `tick`, never immediately from a result.\n * - Dedupe (opt-in, orthogonal to cadence): when `dedupeKey` is set, a FRESH\n * observation emits one `onTick` FOLLOW-UP (the consumer's \"run the\n * downstream side effect once per distinct observation\" hook) and a\n * DUPLICATE key records the sighting but suppresses the follow-up. With no\n * `dedupeKey`, `tickResult` emits nothing — the cadence `tick` is the only\n * `onTick`, so an unconfigured poller never double-fetches.\n * - UNLESS `untilHeld` is `true` (the consumer evaluated `config.until`\n * against the post-result Model) — then the poller enters `\"done\"`,\n * disarms (`nextAtMs: null`), and emits no Cmd, dedupe or otherwise.\n *\n * `untilHeld` is passed in rather than computed here because `until` reads\n * the consumer's WHOLE Model, which this verb does not hold — the consumer\n * evaluates it at the call site after folding `result` into its Model and\n * passes the boolean. (`subs` re-checks `phase` to decide arming, so a\n * mis-passed `untilHeld` cannot leave a \"done\" poller arming timers.)\n */\n tickResult(\n state: PollerState<R>,\n result: R,\n at: number,\n untilHeld: boolean,\n ): readonly [PollerPolling<R> | PollerDone<R>, readonly Cmd[]];\n /**\n * Record a FAILED tick at clock `at`. Backs off instead of holding the\n * steady cadence:\n *\n * - Advances the retry counter (`recordFailure`).\n * - If `shouldRetry` still permits another attempt, arms the next tick at\n * `at + nextDelayMs(retry, policy)` (the backoff curve) and emits NO Cmd —\n * the retry observation fires when that backoff deadline crosses and the\n * consumer routes `deadline_exceeded` → `tick`. The timer is the single\n * next-tick mechanism, on the backoff curve here instead of `everyMs`.\n * - If backoff is exhausted (`!shouldRetry`), enters `\"gave_up\"`, disarms\n * (`nextAtMs: null`), and emits no Cmd.\n *\n * `at` is both the backoff anchor AND the streak clock: it is passed to\n * `recordFailure` (starting / preserving `firstFailureAtMs`) and to\n * `shouldRetry`, so a `DurationRetryPolicy` on `config.retry` is honoured\n * with no extra wiring. Under a duration bound the poller keeps retrying\n * however many attempts the outage takes, and stops at the declared\n * wall-clock budget measured from the streak's FIRST failure.\n *\n * The backoff jitter uses the `rng` injected ONCE at `createPoller` (default\n * `Math.random` at the effect boundary, a fixed value in tests); the verb\n * body never names the global RNG, so a poller built with a fixed `rng`\n * replays a failure-and-backoff run bit-for-bit.\n */\n tickErr(\n state: PollerState<R>,\n error: unknown,\n at: number,\n ): readonly [PollerPolling<R> | PollerGaveUp<R>, readonly Cmd[]];\n /**\n * The pre-wired subscriptions cell. Returns the single tick-deadline Sub\n * while the poller is `\"polling\"` with an armed `nextAtMs`; returns `[]`\n * once `\"done\"` / `\"gave_up\"` (the timer disarms) or before `start`. Assign\n * directly: `subscriptions: (s) => poll.subs(s.poll)`.\n *\n * The `DeadlineSub` it returns is consumed by `../deadline`'s\n * `subscribeDeadline` cell, which the consumer wires into `subscribe` —\n * the poller does not redraw the timer lifecycle.\n */\n subs(state: PollerState<R>): readonly Sub[];\n}\n\n/**\n * Build a poller knob from `config`. Returns the hook bag to spread into a\n * machine (see the module doc-comment for the wiring). Pure factory — no clock\n * read, no timer; everything happens when the consumer calls the returned\n * verbs / `subs` with an injected `at`.\n *\n * `rng` is injected ONCE here (default `Math.random` at the effect boundary, a\n * fixed value in tests) and threaded internally into the backoff jitter. No\n * verb body ever names the global RNG, so a poller built with a fixed `rng`\n * replays a failure-and-backoff run bit-for-bit (invariant 2: the verbs are\n * pure given the injected `rng`).\n */\nexport function createPoller<State, R>(\n config: PollerConfig<State, R>,\n rng: () => number = Math.random,\n): Poller<State, R> {\n // Re-affirm the `[0, 1)` contract at the seam where the injected generator\n // enters the backoff math (brand from `../retry-backoff`). The factory's\n // public param stays a plain `() => number` so transitive callers are\n // unchanged; `asRng` is the single point where it's branded for `nextDelayMs`.\n const rngBranded = asRng(rng);\n const policy: AnyRetryPolicy = config.retry ?? defaultRetryPolicy;\n\n // The dedupe store seam, bound once. The poller delegates its dedupe-window\n // reads (`memory.isSeen`) and the per-sighting touch (`memory.remember`) to\n // the verbs, so the `IdempotencyStore` shape stays insulated.\n const memory = idempotencyMemory<true>();\n\n function init(): PollerPolling<R> {\n return {\n phase: \"polling\",\n tick: 0,\n retry: initRetry(),\n nextAtMs: null,\n // The dedupe store is created regardless; it is only WRITTEN when\n // `dedupeKey` is set, so an unconfigured poller carries an inert empty\n // store (no per-op cost, JSON-serializable, durable like the rest of\n // the slice).\n dedupe: memory.init({ ttlMs: config.dedupeTtlMs }),\n };\n }\n\n function start(\n state: PollerState<R>,\n at: number,\n ): readonly [PollerPolling<R>, readonly Cmd[]] {\n // Arm the first deadline at `at + everyMs` and emit NOTHING. The cadence has\n // ONE source — the `everyMs` deadline Sub. `start` does not perform the\n // first observation itself: that would be a second, immediate next-tick\n // mechanism racing the timer (it would fire as fast as the source responds,\n // never honoring `everyMs`). The first observation fires when the deadline\n // crosses `at + everyMs` and the consumer routes `deadline_exceeded` → the\n // `tick` verb (which emits `onTick`). No drift, no double-fire. Build the\n // `polling` arm explicitly so a (re)start from a terminal state adopts a live\n // target rather than spreading a terminal arm that never had one.\n return [\n {\n phase: \"polling\",\n tick: state.tick,\n lastResult: state.lastResult,\n retry: state.retry,\n nextAtMs: at + config.everyMs,\n dedupe: state.dedupe,\n },\n [],\n ];\n }\n\n function tick(\n state: PollerState<R>,\n ): readonly [PollerState<R>, readonly Cmd[]] {\n // The deadline fired. Perform the observation — this is the ONLY place the\n // `onTick` Cmd is emitted, so the timer is the single next-tick mechanism.\n // No schedule change here: the just-fired deadline is exhausted, and the\n // NEXT target is re-armed when the observation lands (`tickResult` /\n // `tickErr`). A no-op on a finished poller (a stale fire racing a terminal\n // transition the reconcile pass has not yet retired).\n if (state.phase !== \"polling\") return [state, []];\n return [state, [config.onTick()]];\n }\n\n function tickResult(\n state: PollerState<R>,\n result: R,\n at: number,\n untilHeld: boolean,\n ): readonly [PollerPolling<R> | PollerDone<R>, readonly Cmd[]] {\n // A success always clears consecutive-failure state and records the datum.\n // The next arm is built explicitly (not spread from `state`) so `done`\n // carries no `nextAtMs` — a finished poll can never sit on an armed target.\n const tick = state.tick + 1;\n\n // Stop condition first: if the consumer's `until` held against the\n // post-result Model, we are DONE — disarm and emit nothing, regardless of\n // dedupe (the final observation is still recorded as `lastResult`).\n if (untilHeld) {\n return [\n {\n phase: \"done\",\n tick,\n lastResult: result,\n retry: initRetry(),\n dedupe: state.dedupe,\n },\n [],\n ];\n }\n\n // Re-arm the steady cadence: the next observation fires when THIS deadline\n // (`at + everyMs`) crosses and the consumer routes `deadline_exceeded` →\n // `tick`. `tickResult` itself never emits the cadence `onTick` — the timer\n // is the single next-tick mechanism (recording a result is not a reason to\n // immediately re-fetch; that would poll as fast as the source responds).\n const next: PollerPolling<R> = {\n phase: \"polling\",\n tick,\n lastResult: result,\n retry: initRetry(),\n nextAtMs: at + config.everyMs,\n dedupe: state.dedupe,\n };\n\n // Dedupe is an OPT-IN follow-up, orthogonal to the cadence above. When\n // `dedupeKey` is set, a FRESH observation emits one `onTick` follow-up (the\n // consumer's \"run the downstream side effect once per distinct observation\"\n // hook); a DUPLICATE key records the sighting but suppresses that follow-up.\n // With no `dedupeKey` there is no follow-up at all — the cadence `tick` is\n // the only `onTick`, so an unconfigured poller never double-fetches.\n if (!config.dedupeKey) return [next, []];\n\n const key = config.dedupeKey(result);\n const suppress = memory.isSeen(next.dedupe, key, at);\n // `remember` refreshes the key's TTL clock on every sighting (the\n // idempotency \"touch\" semantics), so a steadily-repeating observation never\n // ages out mid-poll. The stored value is the unit `true` — the poller only\n // needs presence, not a payload.\n const deduped: PollerPolling<R> = {\n ...next,\n dedupe: memory.remember(next.dedupe, key, true, at),\n };\n return [deduped, suppress ? [] : [config.onTick()]];\n }\n\n function tickErr(\n state: PollerState<R>,\n error: unknown,\n at: number,\n ): readonly [PollerPolling<R> | PollerGaveUp<R>, readonly Cmd[]] {\n // `at` is the failure's OBSERVATION instant, already carried in from the\n // Msg — so it doubles as the streak clock a duration bound is measured\n // against. The first failure of a streak sets `firstFailureAtMs`; every\n // later one preserves it, and a success resets to `initRetry()` (dropping\n // the origin with it). Nothing here reads a clock: `at` is Msg data,\n // exactly as `rng` is factory-injected data (invariant 2).\n const retry = recordFailure(state.retry, error, at);\n const tick = state.tick + 1;\n\n // Backoff exhausted → give up: emit nothing. `shouldRetry` reads the\n // just-advanced `retry`, so under a count bound the (maxAttempts)-th\n // failure is the one that stops the poller, and under a duration bound the\n // first failure observed at or past `firstFailureAtMs + maxElapsedMs` is.\n // The `gave_up` arm carries no `nextAtMs` — there is no next attempt to arm.\n if (!shouldRetry(retry, policy, at)) {\n return [\n {\n phase: \"gave_up\",\n tick,\n lastResult: state.lastResult,\n retry,\n dedupe: state.dedupe,\n },\n [],\n ];\n }\n\n // Back off: arm the next attempt at `at + backoff(retry)` rather than the\n // steady cadence, and emit NOTHING. The retry observation fires when this\n // backoff deadline crosses and the consumer routes `deadline_exceeded` →\n // `tick` — the timer is the single next-tick mechanism, on the backoff\n // curve here instead of `everyMs`. `rng` is the value injected once at\n // `createPoller`; the verb body never names the global RNG, so a fixed\n // `rng` makes the backoff target replay bit-for-bit.\n const delayMs = nextDelayMs(retry, policy, rngBranded);\n return [\n {\n phase: \"polling\",\n tick,\n lastResult: state.lastResult,\n retry,\n nextAtMs: at + delayMs,\n dedupe: state.dedupe,\n },\n [],\n ];\n }\n\n function subs(state: PollerState<R>): readonly Sub[] {\n // Arm the tick deadline only while live. `phase` is the authority (so a\n // mis-passed `untilHeld` into `tickResult` cannot resurrect a finished\n // poller); `nextAtMs` is the absolute target `../deadline` translates to a\n // remaining delay at subscribe time — a resumed poller re-arms the exact\n // persisted moment.\n if (state.phase !== \"polling\" || state.nextAtMs === null) return [];\n // The Sub id ROTATES with the target. A one-shot deadline timer is\n // exhausted once it fires, and the substrate's reconcile pass keys identity\n // by `id` alone — a fixed id would leave the fired timer in the registry and\n // NEVER re-arm for the next cadence (cadence dies after one tick). Folding\n // `nextAtMs` into the id makes each successive target a distinct identity:\n // the reconcile pass retires the spent timer (old id absent from the desired\n // set) and arms the next one (new id). A resumed poller re-arms the SAME id\n // from its persisted `nextAtMs`, so eviction is idempotent.\n return [deadlineSub(pollerSubId(state.nextAtMs), state.nextAtMs)];\n }\n\n return { init, start, tick, tickResult, tickErr, subs };\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { C as Cmd, S as Sub, M as Machine } from '../core-JPHsMZVB.js';
1
+ import { C as Cmd, S as Sub, M as Machine } from '../core-Bjc0bS8j.js';
2
2
 
3
3
  /**
4
4
  * @demlik/tea/prediction — the client-prediction ack primitive (epic #186, facet 2).
@@ -1,4 +1,4 @@
1
- export { NO_ACK, ack, initAck, nextSeq, partitionByAck, reconcile, tagSeq } from '../chunk-AZ3NHUPD.js';
2
- import '../chunk-O7ED34JH.js';
1
+ export { NO_ACK, ack, initAck, nextSeq, partitionByAck, reconcile, tagSeq } from '../chunk-4KTWLPL5.js';
2
+ import '../chunk-B5VXOFMZ.js';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map
@@ -1,2 +1,2 @@
1
1
  export { Ack, AckPartition, NO_ACK, Seq, SeqTagged, ack, initAck, nextSeq, partitionByAck, reconcile, tagSeq } from '../prediction/index.js';
2
- export { C as Cmd, d as ContextFree, D as DepKeyedSub, e as Dispose, f as Identity, I as Interpret, c as InterpretDetached, M as Machine, N as NoCellError, g as NoCtx, a as Port, P as PortEmitter, R as Reducer, S as Sub, b as SubId, h as Subscribe, i as SyncReturn, T as Transitions, U as UpdateForm, k as detectUpdateForm, l as foldMsgs, m as formOf, s as structuralHash, o as subId } from '../core-JPHsMZVB.js';
2
+ export { C as Cmd, d as ContextFree, D as DepKeyedSub, e as Dispose, f as Identity, I as Interpret, c as InterpretDetached, M as Machine, g as MachineShape, N as NoCellError, h as NoCtx, a as Port, P as PortEmitter, R as Reducer, S as Sub, b as SubId, i as Subscribe, j as SyncReturn, T as Transitions, U as UpdateForm, k as acceptsOf, n as describeMachine, o as detectUpdateForm, p as foldMsgs, r as formOf, t as structuralHash, u as subId } from '../core-Bjc0bS8j.js';
@@ -1,4 +1,4 @@
1
- export { NO_ACK, ack, initAck, nextSeq, partitionByAck, reconcile, tagSeq } from '../chunk-AZ3NHUPD.js';
2
- export { Cmd, NoCellError, detectUpdateForm, foldMsgs, formOf, structuralHash, subId } from '../chunk-O7ED34JH.js';
1
+ export { NO_ACK, ack, initAck, nextSeq, partitionByAck, reconcile, tagSeq } from '../chunk-4KTWLPL5.js';
2
+ export { Cmd, NoCellError, acceptsOf, describeMachine, detectUpdateForm, foldMsgs, formOf, structuralHash, subId } from '../chunk-B5VXOFMZ.js';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,6 @@
1
- import { S as Store, R as Runtime } from '../runtime-types-BbBq4LWo.js';
2
- import { C as Cmd, S as Sub, M as Machine } from '../core-JPHsMZVB.js';
1
+ import { S as Store, R as Runtime } from '../runtime-types-Bxc637bp.js';
2
+ import { C as Cmd, S as Sub, M as Machine } from '../core-Bjc0bS8j.js';
3
+ import 'better-result';
3
4
 
4
5
  /**
5
6
  * @packageDocumentation
@@ -1,5 +1,5 @@
1
- import { run } from '../chunk-ZH454CR5.js';
2
- import '../chunk-O7ED34JH.js';
1
+ import { run } from '../chunk-5EHLEUTL.js';
2
+ import '../chunk-B5VXOFMZ.js';
3
3
  import { useMemo, useRef, useReducer, useSyncExternalStore, useEffect } from 'react';
4
4
 
5
5
  function useMachine(machine, opts) {
@@ -1,6 +1,6 @@
1
1
  import { CircuitConfig, RateLimitConfig, DeadlineConfig, ResilientPorts, RunCmd, FailMsg, SucceedMsg } from '../resilient-call/index.js';
2
2
  import { TtlCache } from '../cache/index.js';
3
- import { C as Cmd } from '../core-JPHsMZVB.js';
3
+ import { C as Cmd } from '../core-Bjc0bS8j.js';
4
4
  import { PaginatedWalkState, PaginatedWalkTimerMsg, FetchPageCmd, PageErrMsg, PageOkMsg } from '../paginated-walk/index.js';
5
5
  import { RetryPolicy } from '../retry-backoff/index.js';
6
6
  import { DeadlineSub } from '../deadline/index.js';
@@ -8,7 +8,7 @@ export { deadlineSub, subscribeDeadline } from '../deadline/index.js';
8
8
  import '../circuit-breaker/index.js';
9
9
  import '../protocol-B5YmJr0d.js';
10
10
  import '../rate-limit/index.js';
11
- import '../types-BMZ7IMrd.js';
11
+ import '../types-D7u9DkH7.js';
12
12
  import '../paginator/index.js';
13
13
 
14
14
  /**
@@ -1,17 +1,17 @@
1
- import { createPaginatedWalk, PAGE_KEY } from '../chunk-XOAP3LOT.js';
1
+ import { createPaginatedWalk, PAGE_KEY } from '../chunk-I5OV65MS.js';
2
2
  import '../chunk-SINXE47Y.js';
3
- import '../chunk-EC6FO42Q.js';
3
+ import '../chunk-EUF7XOEE.js';
4
4
  import '../chunk-JELSZN25.js';
5
- export { deadlineSub, subscribeDeadline } from '../chunk-ASX7PL2K.js';
5
+ export { deadlineSub, subscribeDeadline } from '../chunk-JGVL3WPL.js';
6
6
  import '../chunk-Q2UM7BXU.js';
7
7
  import '../chunk-S2EGRPCM.js';
8
8
  import '../chunk-UF4MZSLT.js';
9
- import { initCache, get, set } from '../chunk-WF46FABO.js';
10
- import '../chunk-2UGTDYOK.js';
9
+ import { initCache, get, set } from '../chunk-7SVIRMC6.js';
10
+ import '../chunk-3JU6657S.js';
11
11
  import '../chunk-2YFLOCWX.js';
12
12
  import '../chunk-ZJ5AOYBH.js';
13
- import '../chunk-ZH454CR5.js';
14
- import '../chunk-O7ED34JH.js';
13
+ import '../chunk-5EHLEUTL.js';
14
+ import '../chunk-B5VXOFMZ.js';
15
15
 
16
16
  // src/reconciler/index.ts
17
17
  function createReconciler(config, rng = Math.random) {
@@ -1,5 +1,6 @@
1
- import { B as BootingRuntime } from '../runtime-types-BbBq4LWo.js';
2
- import '../core-JPHsMZVB.js';
1
+ import { B as BootingRuntime } from '../runtime-types-Bxc637bp.js';
2
+ import 'better-result';
3
+ import '../core-Bjc0bS8j.js';
3
4
 
4
5
  /**
5
6
  * A recorded run, sufficient to reproduce it via `../trace-replay`.
@@ -2,11 +2,11 @@ import { TtlCache } from '../cache/index.js';
2
2
  import { CircuitState } from '../circuit-breaker/index.js';
3
3
  import { DeadlineExceeded, DeadlineSub } from '../deadline/index.js';
4
4
  export { deadlineSub, subscribeDeadline } from '../deadline/index.js';
5
- import { C as Cmd } from '../core-JPHsMZVB.js';
5
+ import { C as Cmd } from '../core-Bjc0bS8j.js';
6
6
  import { M as MsgType } from '../protocol-B5YmJr0d.js';
7
7
  import { TokenBucket } from '../rate-limit/index.js';
8
8
  import { RetryState, AnyRetryPolicy } from '../retry-backoff/index.js';
9
- import '../types-BMZ7IMrd.js';
9
+ import '../types-D7u9DkH7.js';
10
10
 
11
11
  /**
12
12
  * @packageDocumentation
@@ -1,14 +1,14 @@
1
- export { createResilientCall, liftResilience } from '../chunk-EC6FO42Q.js';
1
+ export { createResilientCall, liftResilience } from '../chunk-EUF7XOEE.js';
2
2
  import '../chunk-JELSZN25.js';
3
- export { deadlineSub, subscribeDeadline } from '../chunk-ASX7PL2K.js';
3
+ export { deadlineSub, subscribeDeadline } from '../chunk-JGVL3WPL.js';
4
4
  import '../chunk-Q2UM7BXU.js';
5
5
  import '../chunk-S2EGRPCM.js';
6
6
  import '../chunk-UF4MZSLT.js';
7
- import '../chunk-WF46FABO.js';
8
- import '../chunk-2UGTDYOK.js';
7
+ import '../chunk-7SVIRMC6.js';
8
+ import '../chunk-3JU6657S.js';
9
9
  import '../chunk-2YFLOCWX.js';
10
10
  import '../chunk-ZJ5AOYBH.js';
11
- import '../chunk-ZH454CR5.js';
12
- import '../chunk-O7ED34JH.js';
11
+ import '../chunk-5EHLEUTL.js';
12
+ import '../chunk-B5VXOFMZ.js';
13
13
  //# sourceMappingURL=index.js.map
14
14
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,5 @@
1
- import { a as Port, C as Cmd, R as Reducer, S as Sub, M as Machine, T as Transitions, c as InterpretDetached, P as PortEmitter } from './core-JPHsMZVB.js';
1
+ import { Result } from 'better-result';
2
+ import { a as Port, N as NoCellError, C as Cmd, R as Reducer, S as Sub, M as Machine, T as Transitions, U as UpdateForm, c as InterpretDetached, P as PortEmitter } from './core-Bjc0bS8j.js';
2
3
 
3
4
  /**
4
5
  * @demlik/tea runtime interface surface + pure helpers — the public types,
@@ -446,6 +447,56 @@ declare function replay<S, M extends {
446
447
  declare function wrapDetached<C extends Cmd, M extends {
447
448
  type: string;
448
449
  }, Allowed extends M, Ctx>(handler: InterpretDetached<C, Allowed, Ctx>): (cmd: C, ctx: Ctx & PortEmitter, dispatch?: (msg: M) => void) => Promise<void>;
450
+ /**
451
+ * `applyCell` with the missing-cell case in the return type.
452
+ *
453
+ * `Ok` carries the cell's `[nextState, cmds]` verbatim; `Err` carries the same
454
+ * `NoCellError` (msg.type + state name) `applyCell` would have thrown. Dispatch
455
+ * inside `run` keeps using the throwing `applyCell` — this is for callers that
456
+ * step a machine themselves.
457
+ */
458
+ declare function tryApplyCell<S, M extends {
459
+ type: string;
460
+ }, C extends Cmd>(machine: {
461
+ update: object;
462
+ __form?: UpdateForm;
463
+ }, state: S, msg: M): Result<readonly [S, readonly C[]], NoCellError>;
464
+ /**
465
+ * The refusal `tryFoldMsgs` reports: WHICH msg in the log had no cell, where.
466
+ *
467
+ * A plain record, not a new `Error` subclass — it is a `Result` payload, never
468
+ * thrown, and the actual error is the existing `NoCellError` it carries. The
469
+ * package's thrown-error idiom (`class extends Error` + `_tag`, pinned by
470
+ * `error-idiom.test.ts`) applies to errors raised at the runtime edge; this one
471
+ * is never raised, so adding a class would state a promise it does not make.
472
+ */
473
+ interface FoldRefusal<M> {
474
+ /** Index into the `msgs` array the fold was given. */
475
+ readonly index: number;
476
+ /** The offending Msg itself — so the caller can print it, not just point. */
477
+ readonly msg: M;
478
+ /** The underlying cell-lookup failure, carrying msg.type + state name. */
479
+ readonly error: NoCellError;
480
+ }
481
+ /**
482
+ * `foldMsgs` with the missing-cell case in the return type, INCLUDING which
483
+ * message failed.
484
+ *
485
+ * The motivating use is "this persisted log does not replay — tell the user
486
+ * where". A bare `Result<S, NoCellError>` cannot: the error names the msg.type
487
+ * and the state, but a log usually contains that type many times, so the
488
+ * INDEX is the load-bearing fact. The fold stops at the first refusal (state
489
+ * past that point is not defined) and returns it.
490
+ *
491
+ * Shares `foldMsgs`' dev-mode discipline: `deepFreeze` the input state,
492
+ * `assertPureResult` the cell's return. Both compile out of production.
493
+ */
494
+ declare function tryFoldMsgs<S, M extends {
495
+ type: string;
496
+ }, C extends Cmd>(machine: {
497
+ update: object;
498
+ __form?: UpdateForm;
499
+ }, base: S, msgs: readonly M[]): Result<S, FoldRefusal<M>>;
449
500
  declare function tryInterpret<C extends Cmd, Ok, M, Ctx>(work: (cmd: C, ctx: Ctx) => Promise<Ok>, onOk: (value: Ok, cmd: C) => M, onErr: (error: unknown, cmd: C) => M): (cmd: C, ctx: Ctx) => Promise<M>;
450
501
 
451
- export { type BootingRuntime as B, type CtxArg as C, DispatchDiscardedError as D, IdentityDropNotice as I, type OnError as O, PortNameCollisionError as P, QuiescenceTimeoutError as Q, type Runtime as R, type Store as S, __resetPortRegistry as _, type Supervision as a, type Branded as b, type DispatchSettle as c, DisposeTimeoutNotice as d, RuntimeDiscardNotice as e, RuntimeDiscardedError as f, type RuntimeErrorContext as g, type RuntimeErrorPhase as h, type RuntimeRef as i, type Schema as j, SubIdCollisionError as k, type SupervisionStrategy as l, absurd as m, asReducer as n, defineMachine as o, definePort as p, noop as q, replay as r, schemaMigrate as s, tryInterpret as t, wrapDetached as w };
502
+ export { type BootingRuntime as B, type CtxArg as C, DispatchDiscardedError as D, type FoldRefusal as F, IdentityDropNotice as I, type OnError as O, PortNameCollisionError as P, QuiescenceTimeoutError as Q, type Runtime as R, type Store as S, __resetPortRegistry as _, type Supervision as a, type Branded as b, type DispatchSettle as c, DisposeTimeoutNotice as d, RuntimeDiscardNotice as e, RuntimeDiscardedError as f, type RuntimeErrorContext as g, type RuntimeErrorPhase as h, type RuntimeRef as i, type Schema as j, SubIdCollisionError as k, type SupervisionStrategy as l, absurd as m, asReducer as n, defineMachine as o, definePort as p, noop as q, replay as r, schemaMigrate as s, tryApplyCell as t, tryFoldMsgs as u, tryInterpret as v, wrapDetached as w };
@@ -1,6 +1,7 @@
1
- import { C as Cmd } from '../core-JPHsMZVB.js';
1
+ import { C as Cmd } from '../core-Bjc0bS8j.js';
2
2
  import { QueueItem } from '../work-queue/index.js';
3
- import '../runtime-types-BbBq4LWo.js';
3
+ import '../runtime-types-Bxc637bp.js';
4
+ import 'better-result';
4
5
 
5
6
  /**
6
7
  * @packageDocumentation
@@ -1,4 +1,4 @@
1
- import { C as Cmd, I as Interpret } from '../core-JPHsMZVB.js';
1
+ import { C as Cmd, I as Interpret } from '../core-Bjc0bS8j.js';
2
2
 
3
3
  /**
4
4
  * @demlik/tea/snapshot — periodic state checkpoint to a host store (R2/KV-shaped).
@@ -1,5 +1,5 @@
1
- export { createSnapshot, snapshotFailed, snapshotLoadFailed, snapshotLoaded, snapshotSaved } from '../chunk-XMHZ44QC.js';
2
- import '../chunk-ZH454CR5.js';
3
- import '../chunk-O7ED34JH.js';
1
+ export { createSnapshot, snapshotFailed, snapshotLoadFailed, snapshotLoaded, snapshotSaved } from '../chunk-7V4EVLZA.js';
2
+ import '../chunk-5EHLEUTL.js';
3
+ import '../chunk-B5VXOFMZ.js';
4
4
  //# sourceMappingURL=index.js.map
5
5
  //# sourceMappingURL=index.js.map
@@ -1,6 +1,7 @@
1
- import { S as Sub, a as Port, e as Dispose, D as DepKeyedSub, b as SubId } from '../core-JPHsMZVB.js';
2
- import { S as SubscribeHandler } from '../types-BMZ7IMrd.js';
3
- import { R as Runtime } from '../runtime-types-BbBq4LWo.js';
1
+ import { S as Sub, a as Port, e as Dispose, D as DepKeyedSub, b as SubId } from '../core-Bjc0bS8j.js';
2
+ import { S as SubscribeHandler } from '../types-D7u9DkH7.js';
3
+ import { R as Runtime } from '../runtime-types-Bxc637bp.js';
4
+ import 'better-result';
4
5
 
5
6
  /**
6
7
  * The imperative listener target, expressed as the `add`/`remove` pair the
@@ -1,7 +1,7 @@
1
- export { combineManagedResources, defineListener, defineManagedResource, fromBroadcastChannel, fromEventSource, fromEventTarget, fromInterval, fromPort, fromReconnectingWebSocket, fromTransport, fromWebSocket } from '../chunk-2UGTDYOK.js';
1
+ export { combineManagedResources, defineListener, defineManagedResource, fromBroadcastChannel, fromEventSource, fromEventTarget, fromInterval, fromPort, fromReconnectingWebSocket, fromTransport, fromWebSocket } from '../chunk-3JU6657S.js';
2
2
  export { fromTimeout } from '../chunk-2YFLOCWX.js';
3
3
  import '../chunk-ZJ5AOYBH.js';
4
- import '../chunk-ZH454CR5.js';
5
- import '../chunk-O7ED34JH.js';
4
+ import '../chunk-5EHLEUTL.js';
5
+ import '../chunk-B5VXOFMZ.js';
6
6
  //# sourceMappingURL=index.js.map
7
7
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,6 @@
1
- import { C as Cmd, S as Sub, M as Machine } from '../core-JPHsMZVB.js';
2
- import { R as Runtime } from '../runtime-types-BbBq4LWo.js';
1
+ import { C as Cmd, S as Sub, M as Machine } from '../core-Bjc0bS8j.js';
2
+ import { R as Runtime } from '../runtime-types-Bxc637bp.js';
3
+ import 'better-result';
3
4
 
4
5
  /**
5
6
  * The composed Model shape every `withX` wrapper produces: the base machine's
@@ -1,5 +1,5 @@
1
- import { replay } from '../chunk-ZH454CR5.js';
2
- import '../chunk-O7ED34JH.js';
1
+ import { replay } from '../chunk-5EHLEUTL.js';
2
+ import '../chunk-B5VXOFMZ.js';
3
3
  import { expect, vi } from 'vitest';
4
4
 
5
5
  function assertWrapperFaithful(base, makeWrapped, opts) {
@@ -1,10 +1,10 @@
1
1
  import { TtlCache } from '../cache/index.js';
2
2
  import { DeadlineSub } from '../deadline/index.js';
3
3
  export { DeadlineExceeded as ThrottledInputSettled, subscribeDeadline as subscribeThrottledInput, deadlineExceeded as throttledInputSettled } from '../deadline/index.js';
4
- import { C as Cmd, S as Sub } from '../core-JPHsMZVB.js';
4
+ import { C as Cmd, S as Sub } from '../core-Bjc0bS8j.js';
5
5
  export { Debounced, debounce } from '../debounce/index.js';
6
6
  export { Throttled, throttle } from '../throttle/index.js';
7
- import '../types-BMZ7IMrd.js';
7
+ import '../types-D7u9DkH7.js';
8
8
 
9
9
  /**
10
10
  * @demlik/tea/throttled-input — gate a high-frequency input stream into a
@@ -1,13 +1,13 @@
1
1
  export { debounce } from '../chunk-J6527VRQ.js';
2
2
  export { throttle } from '../chunk-HCEVHFJ7.js';
3
- import { deadlineSub } from '../chunk-ASX7PL2K.js';
4
- export { subscribeDeadline as subscribeThrottledInput, deadlineExceeded as throttledInputSettled } from '../chunk-ASX7PL2K.js';
5
- import { initCache, get, set } from '../chunk-WF46FABO.js';
6
- import '../chunk-2UGTDYOK.js';
3
+ import { deadlineSub } from '../chunk-JGVL3WPL.js';
4
+ export { subscribeDeadline as subscribeThrottledInput, deadlineExceeded as throttledInputSettled } from '../chunk-JGVL3WPL.js';
5
+ import { initCache, get, set } from '../chunk-7SVIRMC6.js';
6
+ import '../chunk-3JU6657S.js';
7
7
  import '../chunk-2YFLOCWX.js';
8
8
  import '../chunk-ZJ5AOYBH.js';
9
- import '../chunk-ZH454CR5.js';
10
- import '../chunk-O7ED34JH.js';
9
+ import '../chunk-5EHLEUTL.js';
10
+ import '../chunk-B5VXOFMZ.js';
11
11
 
12
12
  // src/throttled-input/index.ts
13
13
  var DEFAULT_SUB_ID = "throttled-input:settle";
@@ -1,4 +1,4 @@
1
- import { C as Cmd, I as Interpret } from '../core-JPHsMZVB.js';
1
+ import { C as Cmd, I as Interpret } from '../core-Bjc0bS8j.js';
2
2
  import { M as MsgType } from '../protocol-B5YmJr0d.js';
3
3
 
4
4
  /**
@@ -1,6 +1,6 @@
1
- export { createTokenRefresh, initTokenRefresh, refreshTokenCmd, tokenRefreshFailedMsg, tokenRefreshedMsg } from '../chunk-2KBEIGYP.js';
1
+ export { createTokenRefresh, initTokenRefresh, refreshTokenCmd, tokenRefreshFailedMsg, tokenRefreshedMsg } from '../chunk-TP5UPGLY.js';
2
2
  import '../chunk-JELSZN25.js';
3
- import '../chunk-ZH454CR5.js';
4
- import '../chunk-O7ED34JH.js';
3
+ import '../chunk-5EHLEUTL.js';
4
+ import '../chunk-B5VXOFMZ.js';
5
5
  //# sourceMappingURL=index.js.map
6
6
  //# sourceMappingURL=index.js.map
@@ -1,6 +1,7 @@
1
- import { C as Cmd, S as Sub, M as Machine } from '../core-JPHsMZVB.js';
1
+ import { C as Cmd, S as Sub, M as Machine } from '../core-Bjc0bS8j.js';
2
2
  import { Trace } from '../recorder/index.js';
3
- import '../runtime-types-BbBq4LWo.js';
3
+ import '../runtime-types-Bxc637bp.js';
4
+ import 'better-result';
4
5
 
5
6
  /**
6
7
  * The outcome of {@link replayTrace}.
@@ -1,6 +1,6 @@
1
- export { deepEqual, replayTrace } from '../chunk-A7USKARI.js';
1
+ export { deepEqual, replayTrace } from '../chunk-SUST46KO.js';
2
2
  import '../chunk-EWJP3YYP.js';
3
- import '../chunk-ZH454CR5.js';
4
- import '../chunk-O7ED34JH.js';
3
+ import '../chunk-5EHLEUTL.js';
4
+ import '../chunk-B5VXOFMZ.js';
5
5
  //# sourceMappingURL=index.js.map
6
6
  //# sourceMappingURL=index.js.map