@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.
Files changed (116) hide show
  1. package/README.md +30 -7
  2. package/dist/agent/index.d.ts +49 -36
  3. package/dist/agent/index.js +12 -8
  4. package/dist/chunk-4LPY67E4.js +18 -0
  5. package/dist/chunk-4LPY67E4.js.map +1 -0
  6. package/dist/chunk-5XU6ZJO2.js +533 -0
  7. package/dist/chunk-5XU6ZJO2.js.map +1 -0
  8. package/dist/{chunk-AQ6ZIMEA.js → chunk-6NG4V5SB.js} +89 -115
  9. package/dist/chunk-6NG4V5SB.js.map +1 -0
  10. package/dist/{chunk-HYDWG35Q.js → chunk-7MGXQZPW.js} +11 -9
  11. package/dist/chunk-7MGXQZPW.js.map +1 -0
  12. package/dist/{chunk-BLJ2JQSH.js → chunk-BCTEOVOU.js} +3 -3
  13. package/dist/{chunk-BLJ2JQSH.js.map → chunk-BCTEOVOU.js.map} +1 -1
  14. package/dist/chunk-CH5KTOEZ.js +710 -0
  15. package/dist/chunk-CH5KTOEZ.js.map +1 -0
  16. package/dist/chunk-CIHSHLQX.js +64 -0
  17. package/dist/chunk-CIHSHLQX.js.map +1 -0
  18. package/dist/{chunk-Z2L3LSNV.js → chunk-EHUDXAKC.js} +246 -210
  19. package/dist/chunk-EHUDXAKC.js.map +1 -0
  20. package/dist/chunk-GOX2CYIM.js +380 -0
  21. package/dist/chunk-GOX2CYIM.js.map +1 -0
  22. package/dist/chunk-IVJP3GKJ.js +104 -0
  23. package/dist/chunk-IVJP3GKJ.js.map +1 -0
  24. package/dist/{chunk-F7FMAWLA.js → chunk-MVZS5FDM.js} +13 -6
  25. package/dist/chunk-MVZS5FDM.js.map +1 -0
  26. package/dist/{chunk-LYLTKJWM.js → chunk-MYNLY3HP.js} +9 -8
  27. package/dist/chunk-MYNLY3HP.js.map +1 -0
  28. package/dist/{chunk-I5MQMJDW.js → chunk-NSIAORRY.js} +20 -30
  29. package/dist/chunk-NSIAORRY.js.map +1 -0
  30. package/dist/{chunk-MYXFNWAJ.js → chunk-QNGYQMZB.js} +4 -4
  31. package/dist/chunk-QNGYQMZB.js.map +1 -0
  32. package/dist/chunk-WT5PBPLB.js +98 -0
  33. package/dist/chunk-WT5PBPLB.js.map +1 -0
  34. package/dist/{core-CTzMJ82M.d.ts → core-BbgtiXdg.d.ts} +231 -139
  35. package/dist/do/index.d.ts +37 -32
  36. package/dist/do/index.js +21 -14
  37. package/dist/do/index.js.map +1 -1
  38. package/dist/{durable-effects-B0-qGQDy.d.ts → durable-effects-7MTK_jnj.d.ts} +1 -1
  39. package/dist/effect/index.d.ts +151 -0
  40. package/dist/effect/index.js +98 -0
  41. package/dist/effect/index.js.map +1 -0
  42. package/dist/extension/index.d.ts +6 -7
  43. package/dist/extension/index.js +3 -2
  44. package/dist/extension/index.js.map +1 -1
  45. package/dist/flow/index.d.ts +68 -70
  46. package/dist/flow/index.js +32 -27
  47. package/dist/flow/index.js.map +1 -1
  48. package/dist/idempotency/index.d.ts +6 -7
  49. package/dist/idempotency/index.js +4 -2
  50. package/dist/{index-DJWIER_3.d.ts → index-7vV8xmsD.d.ts} +1 -1
  51. package/dist/{index-NjrT3fL2.d.ts → index-Bz5Rh9Ga.d.ts} +19 -96
  52. package/dist/index-CjFDUBeq.d.ts +207 -0
  53. package/dist/{index-6A7dB8f6.d.ts → index-CkFYA9Y6.d.ts} +14 -16
  54. package/dist/{index-B5BZ-_cT.d.ts → index-Cou86-GX.d.ts} +189 -108
  55. package/dist/{index-TvRb1KVf.d.ts → index-DU6JpQ8G.d.ts} +1 -1
  56. package/dist/{index-q0hXFeZ0.d.ts → index-Xufl55W8.d.ts} +286 -215
  57. package/dist/{index-4vZzljCD.d.ts → index-_dOR3xqA.d.ts} +57 -45
  58. package/dist/index.d.ts +181 -385
  59. package/dist/index.js +2 -1
  60. package/dist/jev/index.d.ts +147 -99
  61. package/dist/jev/index.js +329 -233
  62. package/dist/jev/index.js.map +1 -1
  63. package/dist/{machine-CdIcN2Xm.d.ts → machine-BQzTV8On.d.ts} +171 -280
  64. package/dist/machine-viz/index.d.ts +5 -5
  65. package/dist/machine-viz/index.js +8 -4
  66. package/dist/machine-viz/index.js.map +1 -1
  67. package/dist/mem/index.d.ts +3 -4
  68. package/dist/mem/index.js +2 -1
  69. package/dist/mem/index.js.map +1 -1
  70. package/dist/node/index.d.ts +119 -78
  71. package/dist/node/index.js +52 -38
  72. package/dist/node/index.js.map +1 -1
  73. package/dist/paginate/index.d.ts +27 -33
  74. package/dist/paginate/index.js +6 -4
  75. package/dist/parity/index.d.ts +4 -5
  76. package/dist/parity/index.js +3 -2
  77. package/dist/parity/index.js.map +1 -1
  78. package/dist/pbt/index.d.ts +2 -2
  79. package/dist/pbt/index.js +2 -1
  80. package/dist/pbt/index.js.map +1 -1
  81. package/dist/persistence/index.d.ts +6 -7
  82. package/dist/persistence/index.js +5 -3
  83. package/dist/promise/index.d.ts +186 -0
  84. package/dist/promise/index.js +5 -0
  85. package/dist/promise/index.js.map +1 -0
  86. package/dist/protocol-ClRZBhHQ.d.ts +53 -0
  87. package/dist/react/index.d.ts +36 -17
  88. package/dist/react/index.js +27 -4
  89. package/dist/react/index.js.map +1 -1
  90. package/dist/resilience/index.d.ts +63 -455
  91. package/dist/resilience/index.js +37 -483
  92. package/dist/resilience/index.js.map +1 -1
  93. package/dist/{runtime-types-LWDoi943.d.ts → runtime-types-CiVpC0HN.d.ts} +131 -388
  94. package/dist/testing/index.d.ts +165 -15
  95. package/dist/testing/index.js +104 -3
  96. package/dist/testing/index.js.map +1 -1
  97. package/dist/timing/index.d.ts +25 -22
  98. package/dist/timing/index.js +4 -3
  99. package/dist/timing/index.js.map +1 -1
  100. package/dist/{types-DP3f5NIl.d.ts → types-LxZxGYda.d.ts} +2 -2
  101. package/dist/work-queue/index.d.ts +5 -6
  102. package/package.json +18 -5
  103. package/dist/chunk-AQ6ZIMEA.js.map +0 -1
  104. package/dist/chunk-F2US5BW6.js +0 -72
  105. package/dist/chunk-F2US5BW6.js.map +0 -1
  106. package/dist/chunk-F7FMAWLA.js.map +0 -1
  107. package/dist/chunk-HUKZQX6U.js +0 -97
  108. package/dist/chunk-HUKZQX6U.js.map +0 -1
  109. package/dist/chunk-HYDWG35Q.js.map +0 -1
  110. package/dist/chunk-I5MQMJDW.js.map +0 -1
  111. package/dist/chunk-LYLTKJWM.js.map +0 -1
  112. package/dist/chunk-MYXFNWAJ.js.map +0 -1
  113. package/dist/chunk-RQULHGYH.js +0 -1804
  114. package/dist/chunk-RQULHGYH.js.map +0 -1
  115. package/dist/chunk-Z2L3LSNV.js.map +0 -1
  116. package/dist/index-DMCeMPMJ.d.ts +0 -272
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
- export { Cmd, DispatchDiscardedError, DisposeTimeoutNotice, DriveFailedError, DriveStalledError, IdentityDropNotice, NO_ACK, NoCellError, PortNameCollisionError, ProvideFailedError, ProviderCycleError, QuiescenceTimeoutError, RuntimeDiscardNotice, RuntimeDiscardedError, StoreConflictError, SubIdCollisionError, UnknownProviderError, __resetPortRegistry, absurd, acceptedTypes, acceptsOf, ack, applyCell, applyCellChecked, asReducer, combineManagedResources, defineListener, defineMachine, defineManagedResource, definePort, dep, describeMachine, detectUpdateForm, driveToDone, foldMsgs, foldUpdates, formOf, fromBroadcastChannel, fromEventSource, fromEventTarget, fromInterval, fromPort, fromReconnectingWebSocket, fromTimeout, fromTransport, fromWebSocket, historyTracker, initAck, isFencedStore, isProvided, layer, msgKeysOf, nextSeq, noop, partitionByAck, provide, reconcile, replay, run, schemaMigrate, settle, structuralHash, subId, tagSeq, tryApplyCell, tryFoldMsgs, tryInterpret, value, wrapDetached } from './chunk-RQULHGYH.js';
1
+ export { NO_ACK, ack, defineListener, defineManagedResource, fromBroadcastChannel, fromEventSource, fromEventTarget, fromInterval, fromPort, fromReconnectingWebSocket, fromTimeout, fromTransport, fromWebSocket, historyTracker, initAck, liftSlice, nextSeq, partitionByAck, readInOrder, reconcile, tagSeq } from './chunk-GOX2CYIM.js';
2
+ export { AsyncSchemaError, Cmd, DispatchDiscardedError, DisposeTimeoutNotice, DriveFailedError, DriveStalledError, IdentityDropNotice, NoCellError, Outcome, OutcomeContractError, PortNameCollisionError, QuiescenceTimeoutError, RuntimeDiscardNotice, RuntimeDiscardedError, StoreConflictError, UndeclaredFailureError, __resetPortRegistry, absurd, acceptedTypes, acceptsOf, applyCell, applyCellChecked, asReducer, defineMachine, definePort, describeMachine, detectUpdateForm, foldMsgs, foldUpdates, formOf, isFencedStore, msgKeysOf, noop, replay, schemaMigrate, structuralHash, subId, subIdOf, tryApplyCell, tryFoldMsgs, tryInterpret, wrapDetached } from './chunk-CH5KTOEZ.js';
2
3
  //# sourceMappingURL=index.js.map
3
4
  //# sourceMappingURL=index.js.map
@@ -1,15 +1,13 @@
1
- import { b as CmdDef, C as Cmd } from '../core-CTzMJ82M.js';
1
+ import { R as RunCmd, F as FailMsg, S as SucceedMsg, a as ResilientState, b as ResilientTimerMsg } from '../index-Cou86-GX.js';
2
+ import { l as Outcome, b as CmdDef, T as TimerDeps, C as Cmd, o as DepKeyedSub, q as TimerSub } from '../core-BbgtiXdg.js';
2
3
  import { RetryPolicy } from '../retry-backoff/index.js';
3
- import { R as RunCmd, F as FailMsg, S as SucceedMsg, a as ResilientState } from '../index-B5BZ-_cT.js';
4
- import { a as DeadlineSub, D as DeadlineExceeded, T as TtlCache, C as CacheEvictionSub, c as CacheEvictMsg } from '../index-q0hXFeZ0.js';
5
- export { b as deadlineSub, s as subscribeDeadline } from '../index-q0hXFeZ0.js';
6
- import { S as SubscribeHandler } from '../types-DP3f5NIl.js';
7
- import { B as BatchWindow, a as BatchWindowSub } from '../index-4vZzljCD.js';
8
- import { F as FanOutState } from '../index-6A7dB8f6.js';
9
- import 'zod';
10
- import '../index-DJWIER_3.js';
11
- import '../runtime-types-LWDoi943.js';
12
- import 'better-result';
4
+ import { D as DeadlineExceeded, a as DeadlineSub, T as TtlCache, C as CacheEvictionSub, f as CacheEvictMsg } from '../index-Xufl55W8.js';
5
+ import { S as SubscribeHandler } from '../types-LxZxGYda.js';
6
+ import { B as BatchWindow, a as BatchWindowSub, b as BatchWindowExpired } from '../index-_dOR3xqA.js';
7
+ import { F as FanOutState } from '../index-CkFYA9Y6.js';
8
+ import '@standard-schema/spec';
9
+ import '../index-7vV8xmsD.js';
10
+ import '../runtime-types-CiVpC0HN.js';
13
11
 
14
12
  /**
15
13
  * @packageDocumentation
@@ -59,12 +57,19 @@ import 'better-result';
59
57
  * network actually produced. Every `unknown` reaches a `JevErr`; nothing
60
58
  * reaches a `throw`.
61
59
  *
62
- * Both halves of a choice question's claim are checked, because both are cast
63
- * away at the tail of `parseAnswers`: `choice` must be one of the criteria
64
- * keys, and `probabilities` must be keyed by EXACTLY those keys — none
65
- * missing, none extra. A partial distribution typed `Record<K, number>` is the
66
- * representable-invalid state this module exists to refuse, and it arrives
67
- * through this gate or not at all.
60
+ * The check itself is a walk DERIVED from the questions map, one step per
61
+ * field, and the typed value is the fresh object it builds. Both halves of a
62
+ * choice question's claim are structural in that walk: `choice` must be one of
63
+ * the criteria keys, and `probabilities` must carry exactly those keys — so a
64
+ * distribution missing one or carrying an extra fails the check rather than a
65
+ * separate arm a later reader has to remember. A partial distribution typed `Record<K, number>` is
66
+ * the representable-invalid state this module exists to refuse, and it arrives
67
+ * through this gate or not at all. The envelope is checked by the same schema:
68
+ * `model` is a string and `usage` carries two finite numbers.
69
+ *
70
+ * The walk stops at the first field that fails, and that field's path is
71
+ * mapped onto the `JevErr` arm that names it. It is hand-written rather than a
72
+ * schema library's so the published package depends on none (ADR 0021 §5).
68
73
  *
69
74
  * ## Why status classification lives here and not in `ask`
70
75
  *
@@ -280,12 +285,11 @@ type JevParse<Q extends JevQuestionMap> = {
280
285
  * Turn an `unknown` response body into the typed answers for `questions`, or
281
286
  * into one `JevErr`. Pure, total, and never throwing on any input.
282
287
  *
283
- * The walk is question-driven, not body-driven: every id the request asked
284
- * about must be answered, and each answer is checked against ITS question's
285
- * type and — for a choice — against that question's own criteria keys, both as
286
- * the `choice` picked and as the exact key set of `probabilities`. An
287
- * answer under an id nobody asked about is ignored rather than refused; the
288
- * questions are the contract, and a wider response still satisfies it.
288
+ * The schema is question-driven: every id the request asked about must be
289
+ * answered, each answer is checked against ITS question's type and — for a
290
+ * choice — against that question's own criteria keys, both as the `choice`
291
+ * picked and as the exact key set of `probabilities`. An answer under an id
292
+ * nobody asked about is dropped rather than refused.
289
293
  */
290
294
  declare const parseAnswers: <Q extends JevQuestionMap>(questions: Q, body: unknown) => JevParse<Q>;
291
295
  /**
@@ -309,39 +313,33 @@ type JevHttpStatus = "ok" | "retry" | "terminal";
309
313
  declare const classifyStatus: (status: number) => JevHttpStatus;
310
314
 
311
315
  /**
312
- * The injected HTTP caller — the one seam that touches the network.
316
+ * What an HTTP call to Jev came back with: the status and the undecoded body.
313
317
  *
314
- * It returns the status and the undecoded body rather than throwing on a
318
+ * Your handler returns this to {@link decodeJevReply} rather than throwing on a
315
319
  * non-2xx, because WHICH non-2xx it is decides whether the call backs off, and
316
- * a thrown adapter error has already lost that. `signal` is optional so a
317
- * `fetch` adapter can be cancelled; resilient-call issues no signal today, and
318
- * the slot exists so an adapter's own shape does not have to change when it
319
- * does.
320
- *
321
- * A `fetch` adapter and a scripted test fake satisfy this identically, which is
322
- * the whole reason the key lives in the adapter's closure and never in config.
320
+ * a thrown error has already lost that.
323
321
  */
324
- type JevPort<Q extends JevQuestionMap = JevQuestionMap> = (request: JevRequest<Q>, signal?: AbortSignal) => Promise<{
322
+ interface JevHttpReply {
325
323
  readonly status: number;
326
324
  readonly body: unknown;
327
- }>;
325
+ }
328
326
  /**
329
327
  * The pure decider that answers when the network cannot.
330
328
  *
331
- * Called on exactly two paths: `port` is absent (the no-key case), and the
332
- * resilient retry budget is spent. It must be a pure function of the request —
333
- * the exhaustion path runs it inside the `fail` VERB, which replay re-runs, so
334
- * a clock read or an I/O call here would make the machine unreplayable.
335
- * Returning a {@link JevErr} is how a fallback says "I have no answer for
336
- * this one"; the call then settles as that error.
329
+ * Called on exactly two paths: a handler with no key returns
330
+ * {@link offlineJevAnswer}, and the resilient retry budget is spent. It must
331
+ * be a pure function of the request — the exhaustion path runs it inside the
332
+ * `fail` VERB, which replay re-runs, so a clock read or an I/O call here would
333
+ * make the machine unreplayable. Returning a {@link JevErr} is how a fallback
334
+ * says "I have no answer for this one"; the call then settles as that error.
337
335
  */
338
336
  type JevFallback<Q extends JevQuestionMap> = (request: JevRequest<Q>) => JevAnswers<Q> | JevErr;
339
337
  /**
340
338
  * Every way an ask can fail, as DATA.
341
339
  *
342
340
  * A superset of the protocol child's {@link JevErr}: a parse failure is
343
- * reported with the protocol's own tag untouched, and the three tags below are
344
- * the ones only a caller can observe. `{_tag}`-discriminated and
341
+ * reported with the protocol's own tag untouched, and the tags below are the
342
+ * ones only a caller can observe. `{_tag}`-discriminated and
345
343
  * JSON-round-trippable like the rest (ADR 0011), because it is carried on the
346
344
  * settle Msg and stored in the slice's `failed` phase.
347
345
  *
@@ -359,12 +357,12 @@ type JevAskErr = JevErr
359
357
  readonly _tag: "http_terminal";
360
358
  readonly status: number;
361
359
  }
362
- /** The port itself rejected (a socket error, a DNS failure) — transient. */
360
+ /** The call itself threw (a socket error, a DNS failure) — transient. */
363
361
  | {
364
362
  readonly _tag: "port_threw";
365
363
  readonly reason: string;
366
364
  }
367
- /** No `port` and no `fallback`: the knob was configured with no way to answer. */
365
+ /** No key and no `fallback`: the knob was configured with no way to answer. */
368
366
  | {
369
367
  readonly _tag: "no_answer_path";
370
368
  };
@@ -378,19 +376,23 @@ type JevAskErr = JevErr
378
376
  */
379
377
  declare function isTransientJevAskErr(error: JevAskErr): boolean;
380
378
  /**
381
- * The carrier that gets a {@link JevAskErr} out of the port and through
382
- * resilient-call's throw seam intact.
383
- *
384
- * resilient-call routes a rejected port to a failure Msg carrying the raw
385
- * thrown value, so throwing is the only way the handler can say "this attempt
386
- * failed" — and a bare `Error` would arrive with its classification already
387
- * lost. The typed payload rides on `jev`; the slice never stores this object
388
- * (the verbs store `jev` itself), so nothing un-serializable reaches Model.
379
+ * How a {@link JevAskErr} crosses the handler: the run Cmd's declared
380
+ * `port_rejected` tag, with the typed error on `jev`. The engine mints it into
381
+ * `resilient_run_err` untouched, and {@link jevAskErrOf} reads it back.
389
382
  */
390
- declare class JevAskFailure extends Error {
383
+ type JevRejected = {
384
+ readonly _tag: "port_rejected";
391
385
  readonly jev: JevAskErr;
392
- constructor(jev: JevAskErr);
393
- }
386
+ };
387
+ /**
388
+ * Read a settled failure back as a {@link JevAskErr}. PURE.
389
+ *
390
+ * A {@link JevRejected} yields its `jev`. Anything else a handler returned —
391
+ * a bare `port_rejected`, a `deadline_exceeded` — is a failure this door did
392
+ * not classify, and it reads as the transient `port_threw`, exactly as a
393
+ * foreign throw always has.
394
+ */
395
+ declare function jevAskErrOf(error: unknown): JevAskErr;
394
396
  /** The model the door asks for when config names none. */
395
397
  declare const DEFAULT_JEV_MODEL = "jev-latest";
396
398
  /**
@@ -401,9 +403,7 @@ declare const DEFAULT_JEV_MODEL = "jev-latest";
401
403
  interface JevAskConfig<Q extends JevQuestionMap> {
402
404
  /** The question map every request carries; its keys type the answers. */
403
405
  readonly questions: Q;
404
- /** DI port — the HTTP caller. Absent = the no-key case; `fallback` answers instead. */
405
- readonly port?: JevPort<Q>;
406
- /** The pure decider for the no-port and budget-spent paths. Absent → those settle as an error. */
406
+ /** The pure decider for the no-key and budget-spent paths. Absent → those settle as an error. */
407
407
  readonly fallback?: JevFallback<Q>;
408
408
  /** Backoff policy, composed into `../../resilience/resilient-call`. Omit → no backoff. */
409
409
  readonly retry?: RetryPolicy;
@@ -411,7 +411,7 @@ interface JevAskConfig<Q extends JevQuestionMap> {
411
411
  readonly model?: string;
412
412
  }
413
413
  /**
414
- * The settled answer carried on `resilient_ok`.
414
+ * The settled answer carried on `resilient_run_ok`.
415
415
  *
416
416
  * `source` is not decoration: a fallback answer and a model answer are both
417
417
  * valid answers with identical shape, and a host that routes on confidence — or
@@ -428,6 +428,27 @@ interface JevOk<Q extends JevQuestionMap> {
428
428
  /** Which path produced this answer. */
429
429
  readonly source: "port" | "fallback";
430
430
  }
431
+ /**
432
+ * Turn an HTTP reply into the run Cmd's outcome. PURE: classify the status,
433
+ * then parse the body against the questions the request carried.
434
+ *
435
+ * `ok` → the typed {@link JevOk}; a 429 / 529 → the transient `http_retry`;
436
+ * any other non-2xx → the terminal `http_terminal`; a body that disagrees with
437
+ * the questions → the protocol's own {@link JevErr}. Every failure is a
438
+ * {@link JevRejected}.
439
+ */
440
+ declare function decodeJevReply<Q extends JevQuestionMap>(request: JevRequest<Q>, reply: JevHttpReply): Outcome<JevOk<Q>, JevRejected>;
441
+ /**
442
+ * The outcome for a call that threw before Jev answered — a socket error, a
443
+ * DNS failure. PURE. It is the transient `port_threw`, so it backs off.
444
+ */
445
+ declare function jevCallThrew(cause: unknown): Outcome<never, JevRejected>;
446
+ /**
447
+ * The outcome with no network at all: the fallback's answer, or its refusal,
448
+ * or `no_answer_path` when there is no fallback. PURE — what a handler with
449
+ * no key returns.
450
+ */
451
+ declare function offlineJevAnswer<Q extends JevQuestionMap>(request: JevRequest<Q>, fallback: JevFallback<Q> | undefined): Outcome<JevOk<Q>, JevRejected>;
431
452
  /**
432
453
  * The one effect this knob emits: run the ask for `key` with the plain-data
433
454
  * {@link JevRequest} (ADR 0014 — a Cmd is DECLARED, so its input, ok and err
@@ -445,15 +466,17 @@ declare function jevAskCmdDef<Q extends JevQuestionMap>(): CmdDef<"resilient_run
445
466
  } | {
446
467
  readonly [detail: string]: unknown;
447
468
  readonly _tag: "port_rejected";
448
- }, unknown>;
469
+ }>;
449
470
  /** The Cmd the verbs emit — `resilient_run` carrying the request. */
450
- type JevAskCmd<Q extends JevQuestionMap> = RunCmd<JevRequest<Q>>;
451
- /** The success settle Msg — resilient-call's, with `result` narrowed to {@link JevOk}. */
471
+ type JevAskCmd<Q extends JevQuestionMap> = RunCmd<JevRequest<Q>, "resilient", JevOk<Q>>;
472
+ /** The success settle Msg — resilient-call's, with `value` narrowed to {@link JevOk}. */
452
473
  type JevSucceedMsg<Q extends JevQuestionMap> = SucceedMsg<JevOk<Q>>;
453
- /** The failure settle Msg — resilient-call's, with `error` narrowed to {@link JevAskErr}. */
454
- type JevFailMsg = Omit<FailMsg, "error"> & {
455
- readonly error: JevAskErr;
456
- };
474
+ /**
475
+ * The failure settle Msg the engine mints — resilient-call's. Its `error` is a
476
+ * {@link JevRejected} when your handler used this door's builders; `fail`
477
+ * reads it with {@link jevAskErrOf}.
478
+ */
479
+ type JevFailMsg = FailMsg;
457
480
  /** The retry / deadline timer Msg — `DeadlineExceeded`, inherited. */
458
481
  type JevTimerMsg = DeadlineExceeded;
459
482
  /**
@@ -461,44 +484,54 @@ type JevTimerMsg = DeadlineExceeded;
461
484
  * jitter (pass a fixed `() => 0` in tests to pin backoff; defaults to
462
485
  * `Math.random`, read only at resilient-call's verb boundary).
463
486
  *
464
- * The slice and five of the six verbs are resilient-call's, forwarded. `fail` is
465
- * the one composed verb — it reads the {@link JevAskErr} the handler typed and
466
- * routes on it (see its docblock); it reimplements no backoff. `handlers` is the
467
- * new piece: the port call, the status classification, the parse, and the
468
- * enriched settle Msg.
487
+ * The slice and most verbs are resilient-call's, forwarded. `fail` is the one
488
+ * composed verb — it reads the {@link JevAskErr} back off the settled Msg and
489
+ * routes on it (see its docblock); it reimplements no backoff. `decode`,
490
+ * `rejected` and `offline` are the pure outcome builders your handler returns.
469
491
  */
470
492
  declare function createJevAsk<Q extends JevQuestionMap>(config: JevAskConfig<Q>, rng?: () => number): {
493
+ name: "resilient";
494
+ /** The `Cmd.define`d run Cmd — list it in the machine's `cmds`. */
495
+ run: CmdDef<"resilient_run", {
496
+ readonly key: string;
497
+ readonly input: JevRequest<Q>;
498
+ }, JevOk<Q>, {
499
+ readonly [detail: string]: unknown;
500
+ readonly _tag: "deadline_exceeded";
501
+ } | {
502
+ readonly [detail: string]: unknown;
503
+ readonly _tag: "port_rejected";
504
+ }>;
471
505
  init: () => ResilientState<JevRequest<Q>, JevOk<Q>>;
472
506
  attempt: (s: ResilientState<JevRequest<Q>, JevOk<Q>>, key: string, content: JevState, at: number) => readonly [ResilientState<JevRequest<Q>, JevOk<Q>>, readonly JevAskCmd<Q>[]];
473
- succeed: (s: ResilientState<JevRequest<Q>, JevOk<Q>>, key: string, msg: JevSucceedMsg<Q>) => readonly [ResilientState<JevRequest<Q>, JevOk<Q>>, readonly JevAskCmd<Q>[]];
474
- fail: (s: ResilientState<JevRequest<Q>, JevOk<Q>>, key: string, msg: JevFailMsg) => readonly [ResilientState<JevRequest<Q>, JevOk<Q>>, readonly JevAskCmd<Q>[]];
507
+ succeed: (s: ResilientState<JevRequest<Q>, JevOk<Q>>, msg: JevSucceedMsg<Q>) => readonly [ResilientState<JevRequest<Q>, JevOk<Q>>, readonly JevAskCmd<Q>[]];
508
+ fail: (s: ResilientState<JevRequest<Q>, JevOk<Q>>, msg: JevFailMsg) => readonly [ResilientState<JevRequest<Q>, JevOk<Q>>, readonly JevAskCmd<Q>[]];
475
509
  onTimer: (s: ResilientState<JevRequest<Q>, JevOk<Q>>, msg: JevTimerMsg) => readonly [ResilientState<JevRequest<Q>, JevOk<Q>>, readonly JevAskCmd<Q>[]];
476
- subs: (s: ResilientState<JevRequest<Q>, JevOk<Q>>) => readonly DeadlineSub[];
477
- handlers: () => {
478
- resilient_run: (cmd: JevAskCmd<Q>) => Promise<JevSucceedMsg<Q> | JevFailMsg>;
479
- };
480
- ask: (request: JevRequest<Q>, signal?: AbortSignal) => Promise<JevOk<Q>>;
510
+ deadlines: (s: ResilientState<JevRequest<Q>, JevOk<Q>>) => readonly DeadlineSub[];
511
+ /** The built-in `timer` Sub's deps — resilient-call's `timer`. */
512
+ timer: (s: ResilientState<JevRequest<Q>, JevOk<Q>>) => TimerDeps<ResilientTimerMsg<"resilient">> | null;
513
+ /** Your handler's outcome for an HTTP reply — {@link decodeJevReply}. */
514
+ decode: (request: JevRequest<Q>, reply: JevHttpReply) => Outcome<JevOk<Q>, JevRejected>;
515
+ /** Your handler's outcome when the call threw — {@link jevCallThrew}. */
516
+ rejected: typeof jevCallThrew;
517
+ /** Your handler's outcome with no network — {@link offlineJevAnswer}. */
518
+ offline: (request: JevRequest<Q>) => Outcome<JevOk<Q>, JevRejected>;
481
519
  };
482
520
  /**
483
521
  * The Cmd type a host machine declares in `types.cmd` when it splices a
484
522
  * {@link createJevAsk} knob in — {@link JevAskCmd} under the name a `types`
485
523
  * block reads well with. It is stated here so a host writes `cmd: {} as
486
- * JevCmd<Questions>` rather than deriving it from the knob's shape, which is
487
- * what `ReturnType<Ask["attempt"]>[1][number]` used to be doing at every call
488
- * site: a spelling of the same type that breaks the moment `attempt`'s tuple
489
- * changes, and that reads as machinery rather than as a name.
524
+ * JevCmd<Questions>` rather than deriving it from the knob's shape.
490
525
  */
491
526
  type JevCmd<Q extends JevQuestionMap> = JevAskCmd<Q>;
492
- /**
493
- * The Sub type a host machine declares in `types.sub` — the deadline Sub
494
- * `subs` emits, inherited from resilient-call. Named here for the same reason
495
- * as {@link JevCmd}: a host names the type, it does not re-derive it.
496
- */
497
- type JevSub = DeadlineSub;
498
527
  /**
499
528
  * Lift a knob result `[slice, cmds]` into a host `[State, cmds]` where the slice
500
529
  * lives at `state.resilience` — resilient-call's convenience, re-typed for this
501
530
  * door's slice so a consumer wires one import. Pure.
531
+ *
532
+ * The record rebuild is `liftSlice` from the root door (#231), the same helper
533
+ * `liftResilience` is expressed over — this stays as the named, pre-keyed
534
+ * convenience for this door, unchanged in name, signature and subpath.
502
535
  */
503
536
  declare function liftJevAsk<S extends {
504
537
  resilience: ResilientState<JevRequest<Q>, JevOk<Q>>;
@@ -565,9 +598,7 @@ interface ClassifyBatchConfig<I, C extends string> {
565
598
  readonly evictEveryMs?: number;
566
599
  /** The model asked for. Forwarded to `../ask`'s default when omitted. */
567
600
  readonly model?: string;
568
- /** DI port — the HTTP caller. Forwarded to `../ask`; absent is the no-key case. */
569
- readonly port?: JevPort<ItemQuestions<C>>;
570
- /** The pure decider for the no-port path. Forwarded to `../ask`. */
601
+ /** The pure decider `offline` answers with, for a handler with no key. */
571
602
  readonly fallback?: JevFallback<ItemQuestions<C>>;
572
603
  }
573
604
  /**
@@ -581,7 +612,7 @@ interface ClassifyBatchState<I, C extends string> {
581
612
  readonly fanOut: FanOutState<Batch<I>, JevOk<ItemQuestions<C>>>;
582
613
  /** The answers, keyed by `keyOf` — `../../resilience/cache`'s slice. */
583
614
  readonly cache: TtlCache<ItemAnswer<C>>;
584
- /** Keys whose batch settled `resilient_err`, with the error that settled it. */
615
+ /** Keys whose batch settled `resilient_run_err`, with the error that settled it. */
585
616
  readonly failed: Readonly<Record<string, JevAskErr>>;
586
617
  }
587
618
  /** What {@link ClassifyBatchKnob.answerFor} reports about one key. */
@@ -591,7 +622,7 @@ type KeyAnswer<C extends string> =
591
622
  readonly status: "answered";
592
623
  readonly answer: ItemAnswer<C>;
593
624
  }
594
- /** The batch carrying this key settled `resilient_err`. Nothing was cached. */
625
+ /** The batch carrying this key settled `resilient_run_err`. Nothing was cached. */
595
626
  | {
596
627
  readonly status: "failed";
597
628
  readonly error: JevAskErr;
@@ -612,6 +643,17 @@ type KeyAnswer<C extends string> =
612
643
  * that keeps one key out of two batches at once.
613
644
  */
614
645
  declare function createClassifyBatch<I, C extends string>(config: ClassifyBatchConfig<I, C>): {
646
+ /** The ask Cmd def — list it in the machine's `cmds`. */
647
+ ask: CmdDef<"resilient_run", {
648
+ readonly key: string;
649
+ readonly input: JevRequest<Readonly<Record<string, ItemQuestion<C>>>>;
650
+ }, JevOk<Readonly<Record<string, ItemQuestion<C>>>>, {
651
+ readonly [detail: string]: unknown;
652
+ readonly _tag: "deadline_exceeded";
653
+ } | {
654
+ readonly [detail: string]: unknown;
655
+ readonly _tag: "port_rejected";
656
+ }>;
615
657
  init: () => ClassifyBatchState<I, C>;
616
658
  add: (state: ClassifyBatchState<I, C>, item: I, at: number) => readonly [ClassifyBatchState<I, C>, readonly ClassifyBatchCmd<C>[]];
617
659
  onWindow: (state: ClassifyBatchState<I, C>, at: number) => readonly [ClassifyBatchState<I, C>, readonly ClassifyBatchCmd<C>[]];
@@ -619,16 +661,22 @@ declare function createClassifyBatch<I, C extends string>(config: ClassifyBatchC
619
661
  onBatchErr: (state: ClassifyBatchState<I, C>, msg: ClassifyBatchErrMsg) => readonly [ClassifyBatchState<I, C>, readonly ClassifyBatchCmd<C>[]];
620
662
  onEvict: (state: ClassifyBatchState<I, C>, at: number) => readonly [ClassifyBatchState<I, C>, readonly ClassifyBatchCmd<C>[]];
621
663
  answerFor: (state: ClassifyBatchState<I, C>, key: string, at: number) => KeyAnswer<C>;
622
- subs: (state: ClassifyBatchState<I, C>, id?: string) => readonly (BatchWindowSub | CacheEvictionSub)[];
664
+ subs: (state: ClassifyBatchState<I, C>, id?: string) => readonly BatchWindowSub[];
665
+ subEntries: <Model>(select: (model: Model) => ClassifyBatchState<I, C>, id?: string) => readonly DepKeyedSub<Model, TimerSub<BatchWindowExpired> | CacheEvictionSub>[];
623
666
  subscribers: () => {
624
- deadline: SubscribeHandler<DeadlineSub, DeadlineExceeded, unknown>;
625
667
  cache: SubscribeHandler<CacheEvictionSub, CacheEvictMsg, unknown>;
626
668
  };
627
- handlers: () => {
628
- resilient_run: (cmd: ClassifyBatchCmd<C>) => Promise<ClassifyBatchOkMsg<C> | ClassifyBatchErrMsg>;
629
- };
669
+ /**
670
+ * Your handler's outcome for Jev's reply to one batch — `../ask`'s
671
+ * `decodeJevReply`, parsing against the questions the batch carried.
672
+ */
673
+ decode: (request: JevRequest<ItemQuestions<C>>, reply: JevHttpReply) => Outcome<JevOk<Readonly<Record<string, ItemQuestion<C>>>>, JevRejected>;
674
+ /** Your handler's outcome when the call threw — `../ask`'s `jevCallThrew`. */
675
+ rejected: typeof jevCallThrew;
676
+ /** Your handler's outcome with no network — the configured `fallback`. */
677
+ offline: (request: JevRequest<ItemQuestions<C>>) => Outcome<JevOk<Readonly<Record<string, ItemQuestion<C>>>>, JevRejected>;
630
678
  };
631
679
  /** The bound knob {@link createClassifyBatch} returns. */
632
680
  type ClassifyBatchKnob<I, C extends string> = ReturnType<typeof createClassifyBatch<I, C>>;
633
681
 
634
- export { type Batch, type ClassifyBatchCmd, type ClassifyBatchConfig, type ClassifyBatchErrMsg, type ClassifyBatchKnob, type ClassifyBatchOkMsg, type ClassifyBatchState, type ClassifyCriteria, DEFAULT_CLASSIFY_MAX_ITEMS, DEFAULT_JEV_MODEL, DeadlineExceeded, DeadlineSub, type ItemAnswer, type ItemQuestion, type ItemQuestions, JEV_ENDPOINT, type JevAnswer, type JevAnswerFor, type JevAnswers, type JevAskCmd, type JevAskConfig, type JevAskErr, JevAskFailure, type JevChoiceAnswer, type JevChoiceCriteria, type JevChoiceQuestion, type JevCmd, type JevErr, type JevFailMsg, type JevFallback, type JevHttpStatus, type JevNoulAnswer, type JevNoulQuestion, type JevOk, type JevParse, type JevPort, type JevQuestion, type JevQuestionMap, type JevQuestionType, type JevRequest, type JevResponse, type JevScoreAnswer, type JevScoreCriteria, type JevScoreQuestion, type JevState, type JevSub, type JevSucceedMsg, type JevText, type JevTimerMsg, type JevUsage, type KeyAnswer, ResilientState, classifyStatus, createClassifyBatch, createJevAsk, isJevErr, isTransientJevAskErr, jevAskCmdDef, jevQuestions, liftJevAsk, parseAnswers };
682
+ export { type Batch, type ClassifyBatchCmd, type ClassifyBatchConfig, type ClassifyBatchErrMsg, type ClassifyBatchKnob, type ClassifyBatchOkMsg, type ClassifyBatchState, type ClassifyCriteria, DEFAULT_CLASSIFY_MAX_ITEMS, DEFAULT_JEV_MODEL, type ItemAnswer, type ItemQuestion, type ItemQuestions, JEV_ENDPOINT, type JevAnswer, type JevAnswerFor, type JevAnswers, type JevAskCmd, type JevAskConfig, type JevAskErr, type JevChoiceAnswer, type JevChoiceCriteria, type JevChoiceQuestion, type JevCmd, type JevErr, type JevFailMsg, type JevFallback, type JevHttpReply, type JevHttpStatus, type JevNoulAnswer, type JevNoulQuestion, type JevOk, type JevParse, type JevQuestion, type JevQuestionMap, type JevQuestionType, type JevRejected, type JevRequest, type JevResponse, type JevScoreAnswer, type JevScoreCriteria, type JevScoreQuestion, type JevState, type JevSucceedMsg, type JevText, type JevTimerMsg, type JevUsage, type KeyAnswer, ResilientState, classifyStatus, createClassifyBatch, createJevAsk, decodeJevReply, isJevErr, isTransientJevAskErr, jevAskCmdDef, jevAskErrOf, jevCallThrew, jevQuestions, liftJevAsk, offlineJevAnswer, parseAnswers };