@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/jev/index.js CHANGED
@@ -1,13 +1,14 @@
1
- import { createBatchWindow, subscribeBatchWindow } from '../chunk-F7FMAWLA.js';
1
+ import { createBatchWindow } from '../chunk-MVZS5FDM.js';
2
2
  import '../chunk-LFZUK6J2.js';
3
- import { createFanOut } from '../chunk-MYXFNWAJ.js';
3
+ import { describeError } from '../chunk-G5E74IJB.js';
4
+ import { createFanOut } from '../chunk-QNGYQMZB.js';
4
5
  import '../chunk-TXN2KFHF.js';
5
- import { runCmdDef, createResilientCall, liftResilience, MsgType } from '../chunk-AQ6ZIMEA.js';
6
+ import { runCmdDef, createResilientCall } from '../chunk-6NG4V5SB.js';
6
7
  import '../chunk-VBDU4JL6.js';
7
- import { initCache, has, set, evictExpired, get, cacheEvictionSub, cacheEvictionSubscribe } from '../chunk-F2US5BW6.js';
8
- export { deadlineSub, subscribeDeadline } from '../chunk-F2US5BW6.js';
9
- import { describeError } from '../chunk-G5E74IJB.js';
10
- import '../chunk-RQULHGYH.js';
8
+ import '../chunk-4LPY67E4.js';
9
+ import { initCache, has, set, evictExpired, cacheEvictionSub, cacheEvictionSubscribe, get } from '../chunk-IVJP3GKJ.js';
10
+ import { liftSlice, readInOrder } from '../chunk-GOX2CYIM.js';
11
+ import { Outcome } from '../chunk-CH5KTOEZ.js';
11
12
 
12
13
  // src/internal/jev/protocol/index.ts
13
14
  var jevQuestions = (questions) => questions;
@@ -30,114 +31,176 @@ function isJevErr(value) {
30
31
  return typeof tag === "string" && tags.has(tag);
31
32
  }
32
33
  var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
33
- var isNum = (v) => typeof v === "number" && Number.isFinite(v);
34
- var isNumMap = (v) => isObj(v) && Object.values(v).every(isNum);
35
- var isStrMap = (v) => isObj(v) && Object.values(v).every((x) => typeof x === "string");
34
+ var at = (value, key) => isObj(value) ? value[key] : void 0;
36
35
  var err = (error) => ({
37
36
  ok: false,
38
37
  error
39
38
  });
40
- var checkAnswer = (id, question, answer) => {
41
- if (!isObj(answer)) {
42
- return { _tag: "malformed_answer", id, reason: "answer is not an object" };
43
- }
44
- const received = answer["type"];
45
- if (typeof received !== "string") {
46
- return { _tag: "malformed_answer", id, reason: "answer has no `type`" };
47
- }
48
- if (received !== question.type) {
49
- return {
50
- _tag: "answer_type_mismatch",
51
- id,
52
- expected: question.type,
53
- received
54
- };
39
+ var pass = (value) => ({ ok: true, value });
40
+ var fail = (path) => ({
41
+ ok: false,
42
+ issue: { path }
43
+ });
44
+ var isFiniteNumber = (v) => typeof v === "number" && Number.isFinite(v);
45
+ var object = (value, path, fields) => {
46
+ if (!isObj(value)) return fail(path);
47
+ const out = {};
48
+ for (const [key, check] of fields) {
49
+ const step = check(value[key], [...path, key]);
50
+ if (!step.ok) return step;
51
+ out[key] = step.value;
52
+ }
53
+ return pass(out);
54
+ };
55
+ var record = (isValue) => (value, path) => {
56
+ if (!isObj(value)) return fail(path);
57
+ for (const [key, v] of Object.entries(value)) {
58
+ if (!isValue(v)) return fail([...path, key]);
55
59
  }
56
- if (question.type === "noul") {
57
- return isNum(answer["noul"]) ? void 0 : { _tag: "malformed_answer", id, reason: "`noul` is not a number" };
60
+ return pass({ ...value });
61
+ };
62
+ var literal = (expected) => (value, path) => value === expected ? pass(expected) : fail(path);
63
+ var finite = (value, path) => isFiniteNumber(value) ? pass(value) : fail(path);
64
+ var string = (value, path) => typeof value === "string" ? pass(value) : fail(path);
65
+ var exactProbabilities = (options) => (value, path) => {
66
+ const named = object(
67
+ value,
68
+ path,
69
+ options.map((k) => [k, finite])
70
+ );
71
+ if (!named.ok || !isObj(value)) return named;
72
+ return Object.keys(value).every((k) => options.includes(k)) ? named : fail(path);
73
+ };
74
+ var checkAnswer = (question, value, path) => {
75
+ switch (question.type) {
76
+ case "noul":
77
+ return object(value, path, [
78
+ ["type", literal("noul")],
79
+ ["noul", finite]
80
+ ]);
81
+ case "score":
82
+ return object(value, path, [
83
+ ["type", literal("score")],
84
+ ["confidence", finite],
85
+ ["score", finite],
86
+ ["legend", record((v) => typeof v === "string")],
87
+ ["probabilities", record(isFiniteNumber)]
88
+ ]);
89
+ case "choice": {
90
+ const options = Object.keys(question.criteria);
91
+ return object(value, path, [
92
+ ["type", literal("choice")],
93
+ [
94
+ "choice",
95
+ (v, p) => typeof v === "string" && options.includes(v) ? pass(v) : fail(p)
96
+ ],
97
+ ["confidence", finite],
98
+ ["probabilities", exactProbabilities(options)]
99
+ ]);
100
+ }
58
101
  }
59
- if (!isNum(answer["confidence"])) {
60
- return {
61
- _tag: "malformed_answer",
62
- id,
63
- reason: "`confidence` is not a number"
64
- };
102
+ };
103
+ var checkResponse = (questions, body) => {
104
+ const envelope = object(
105
+ body,
106
+ [],
107
+ [
108
+ ["model", string],
109
+ [
110
+ "usage",
111
+ (v, p) => object(v, p, [
112
+ ["input_tokens", finite],
113
+ ["output_tokens", finite]
114
+ ])
115
+ ],
116
+ [
117
+ "answers",
118
+ (v, p) => object(
119
+ v,
120
+ p,
121
+ Object.entries(questions).map(
122
+ ([id, question]) => [id, (a, ap) => checkAnswer(question, a, ap)]
123
+ )
124
+ )
125
+ ]
126
+ ]
127
+ );
128
+ if (!envelope.ok) return envelope;
129
+ const { model, usage, answers } = envelope.value;
130
+ return pass({ model, usage, answers });
131
+ };
132
+ var ANSWER_FIELD_REASON = {
133
+ confidence: "`confidence` is not a number",
134
+ score: "`score` is not a number",
135
+ legend: "`legend` is not a map of strings",
136
+ probabilities: "`probabilities` is not a map of numbers",
137
+ choice: "`choice` is not a string",
138
+ noul: "`noul` is not a number"
139
+ };
140
+ var toJevErr = (questions, body, issue) => {
141
+ const [head, id, field] = issue.path;
142
+ if (head === "model") {
143
+ return { _tag: "malformed_body", reason: "`model` is not a string" };
65
144
  }
66
- const probabilities = answer["probabilities"];
67
- if (!isNumMap(probabilities)) {
145
+ if (head === "usage") {
68
146
  return {
69
- _tag: "malformed_answer",
70
- id,
71
- reason: "`probabilities` is not a map of numbers"
72
- };
73
- }
74
- if (question.type === "score") {
75
- if (!isNum(answer["score"])) {
76
- return {
77
- _tag: "malformed_answer",
78
- id,
79
- reason: "`score` is not a number"
80
- };
81
- }
82
- return isStrMap(answer["legend"]) ? void 0 : {
83
- _tag: "malformed_answer",
84
- id,
85
- reason: "`legend` is not a map of strings"
147
+ _tag: "malformed_body",
148
+ reason: "`usage` is not `{ input_tokens, output_tokens }`"
86
149
  };
87
150
  }
88
- const choice = answer["choice"];
89
- if (typeof choice !== "string") {
90
- return { _tag: "malformed_answer", id, reason: "`choice` is not a string" };
151
+ if (head !== "answers") {
152
+ return { _tag: "malformed_body", reason: "body is not an object" };
91
153
  }
92
- const options = Object.keys(question.criteria);
93
- if (!options.includes(choice)) {
94
- return { _tag: "off_criteria_choice", id, choice, options };
154
+ const answers = at(body, "answers");
155
+ if (!isObj(answers) || typeof id !== "string") {
156
+ return { _tag: "malformed_body", reason: "`answers` is not an object" };
95
157
  }
96
- const missing = options.filter((k) => !Object.hasOwn(probabilities, k));
97
- const extra = Object.keys(probabilities).filter((k) => !options.includes(k));
98
- return missing.length === 0 && extra.length === 0 ? void 0 : { _tag: "off_criteria_probabilities", id, missing, extra, options };
99
- };
100
- var parseAnswers = (questions, body) => {
101
- if (!isObj(body))
102
- return err({ _tag: "malformed_body", reason: "body is not an object" });
103
- const model = body["model"];
104
- if (typeof model !== "string") {
105
- return err({ _tag: "malformed_body", reason: "`model` is not a string" });
106
- }
107
- const usage = body["usage"];
108
- if (!isObj(usage) || !isNum(usage["input_tokens"]) || !isNum(usage["output_tokens"])) {
109
- return err({
110
- _tag: "malformed_body",
111
- reason: "`usage` is not `{ input_tokens, output_tokens }`"
112
- });
158
+ if (!Object.hasOwn(answers, id)) return { _tag: "missing_answer", id };
159
+ const answer = answers[id];
160
+ const question = questions[id];
161
+ if (question === void 0 || field === void 0) {
162
+ return { _tag: "malformed_answer", id, reason: "answer is not an object" };
113
163
  }
114
- const answers = body["answers"];
115
- if (!isObj(answers)) {
116
- return err({
117
- _tag: "malformed_body",
118
- reason: "`answers` is not an object"
119
- });
164
+ if (field === "type") {
165
+ const received = at(answer, "type");
166
+ return typeof received === "string" ? { _tag: "answer_type_mismatch", id, expected: question.type, received } : { _tag: "malformed_answer", id, reason: "answer has no `type`" };
120
167
  }
121
- for (const [id, question] of Object.entries(questions)) {
122
- if (!Object.hasOwn(answers, id)) {
123
- return err({ _tag: "missing_answer", id });
168
+ if (question.type === "choice") {
169
+ const options = Object.keys(question.criteria);
170
+ if (field === "choice") {
171
+ const choice = at(answer, "choice");
172
+ if (typeof choice === "string") {
173
+ return { _tag: "off_criteria_choice", id, choice, options };
174
+ }
175
+ }
176
+ if (field === "probabilities") {
177
+ const probabilities = at(answer, "probabilities");
178
+ const key = issue.path[3];
179
+ const badValue = isObj(probabilities) && typeof key === "string" && Object.hasOwn(probabilities, key);
180
+ if (isObj(probabilities) && !badValue) {
181
+ return {
182
+ _tag: "off_criteria_probabilities",
183
+ id,
184
+ missing: options.filter((k) => !Object.hasOwn(probabilities, k)),
185
+ extra: Object.keys(probabilities).filter((k) => !options.includes(k)),
186
+ options
187
+ };
188
+ }
124
189
  }
125
- const bad = checkAnswer(id, question, answers[id]);
126
- if (bad !== void 0) return err(bad);
127
190
  }
191
+ const named = typeof field === "string" ? ANSWER_FIELD_REASON[field] : void 0;
128
192
  return {
129
- ok: true,
130
- // Every id of `questions` passed `checkAnswer`, which is exactly the set of
131
- // obligations `JevAnswers<Q>` states. `unknown` is the bridge because the
132
- // per-id narrowing happened in a loop, where the compiler cannot carry it.
133
- answers,
134
- model,
135
- usage: {
136
- input_tokens: usage["input_tokens"],
137
- output_tokens: usage["output_tokens"]
138
- }
193
+ _tag: "malformed_answer",
194
+ id,
195
+ reason: named ?? "answer is malformed"
139
196
  };
140
197
  };
198
+ var parseAnswers = (questions, body) => {
199
+ const parsed = checkResponse(questions, body);
200
+ if (!parsed.ok) return err(toJevErr(questions, body, parsed.issue));
201
+ const { answers, model, usage } = parsed.value;
202
+ return { ok: true, answers, model, usage };
203
+ };
141
204
  var classifyStatus = (status) => {
142
205
  if (status >= 200 && status < 300) return "ok";
143
206
  if (status === 429 || status === 529) return "retry";
@@ -148,20 +211,53 @@ var classifyStatus = (status) => {
148
211
  function isTransientJevAskErr(error) {
149
212
  return error._tag === "http_retry" || error._tag === "port_threw";
150
213
  }
151
- var JevAskFailure = class extends Error {
152
- constructor(jev) {
153
- super(`jev ask: ${jev._tag}`);
154
- this.jev = jev;
155
- this.name = "JevAskFailure";
214
+ function reject(jev) {
215
+ return Outcome.err({ _tag: "port_rejected", jev });
216
+ }
217
+ function jevAskErrOf(error) {
218
+ if (typeof error === "object" && error !== null) {
219
+ const jev = error.jev;
220
+ if (typeof jev === "object" && jev !== null && typeof jev._tag === "string") {
221
+ return jev;
222
+ }
156
223
  }
157
- jev;
158
- };
159
- function toAskErr(error) {
160
- if (error instanceof JevAskFailure) return error.jev;
161
224
  return { _tag: "port_threw", reason: describeError(error) };
162
225
  }
163
226
  var DEFAULT_JEV_MODEL = "jev-latest";
164
227
  var NO_USAGE = { input_tokens: 0, output_tokens: 0 };
228
+ function isErrDecision(decided) {
229
+ return isJevErr(decided);
230
+ }
231
+ function decodeJevReply(request, reply) {
232
+ const kind = classifyStatus(reply.status);
233
+ if (kind === "retry")
234
+ return reject({ _tag: "http_retry", status: reply.status });
235
+ if (kind === "terminal") {
236
+ return reject({ _tag: "http_terminal", status: reply.status });
237
+ }
238
+ const parsed = parseAnswers(request.questions, reply.body);
239
+ if (!parsed.ok) return reject(parsed.error);
240
+ return Outcome.ok({
241
+ answers: parsed.answers,
242
+ model: parsed.model,
243
+ usage: parsed.usage,
244
+ source: "port"
245
+ });
246
+ }
247
+ function jevCallThrew(cause) {
248
+ return reject({ _tag: "port_threw", reason: describeError(cause) });
249
+ }
250
+ function offlineJevAnswer(request, fallback) {
251
+ if (fallback === void 0) return reject({ _tag: "no_answer_path" });
252
+ const decided = fallback(request);
253
+ if (isErrDecision(decided)) return reject(decided);
254
+ return Outcome.ok({
255
+ answers: decided,
256
+ model: request.model,
257
+ usage: NO_USAGE,
258
+ source: "fallback"
259
+ });
260
+ }
165
261
  function jevAskCmdDef() {
166
262
  return runCmdDef();
167
263
  }
@@ -171,9 +267,6 @@ function createJevAsk(config, rng = Math.random) {
171
267
  };
172
268
  const rc = createResilientCall(resilientConfig, rng);
173
269
  const model = config.model ?? DEFAULT_JEV_MODEL;
174
- function isErrDecision(decided) {
175
- return isJevErr(decided);
176
- }
177
270
  function liveRequest(s, key) {
178
271
  const call = s.calls[key];
179
272
  if (call?.phase === "running" || call?.phase === "waiting_retry") {
@@ -184,100 +277,71 @@ function createJevAsk(config, rng = Math.random) {
184
277
  function init() {
185
278
  return rc.init();
186
279
  }
187
- function attempt(s, key, content, at) {
280
+ function attempt(s, key, content, at2) {
188
281
  const request = {
189
282
  state: content,
190
283
  model,
191
284
  questions: config.questions
192
285
  };
193
- return rc.attempt(s, key, request, at);
194
- }
195
- function succeed(s, key, msg) {
196
- return rc.succeed(s, key, msg);
197
- }
198
- function settleFromFallback(s, key, request, fallback, at) {
199
- const decided = fallback(request);
200
- if (isErrDecision(decided)) return rc.settleFailed(s, key, decided);
201
- return rc.succeed(s, key, {
202
- type: MsgType.ResilientOk,
203
- key,
204
- result: {
205
- answers: decided,
206
- model: request.model,
207
- usage: NO_USAGE,
208
- source: "fallback"
209
- },
210
- at
286
+ return rc.attempt(s, key, request, at2);
287
+ }
288
+ function succeed(s, msg) {
289
+ const { call, cmds } = rc.settle(s, msg);
290
+ return [call, cmds];
291
+ }
292
+ function settleFromFallback(s, key, request, fallback, at2) {
293
+ const decided = offlineJevAnswer(request, fallback);
294
+ if (decided._tag === "Err") {
295
+ return rc.settleFailed(s, key, decided.error.jev, at2);
296
+ }
297
+ const { call, cmds } = rc.settle(s, {
298
+ cmd: { key },
299
+ value: decided.value,
300
+ at: at2
211
301
  });
302
+ return [call, cmds];
212
303
  }
213
- function fail(s, key, msg) {
214
- if (!isTransientJevAskErr(msg.error)) {
215
- return rc.settleFailed(s, key, msg.error);
304
+ function fail2(s, msg) {
305
+ const { key } = msg.cmd;
306
+ const error = jevAskErrOf(msg.error);
307
+ if (!isTransientJevAskErr(error)) {
308
+ return rc.settleFailed(s, key, error, msg.at);
216
309
  }
217
310
  const request = liveRequest(s, key);
218
- const settled = rc.fail(s, key, msg);
219
- const backedOff = settled[0].calls[key]?.phase !== "failed";
220
- if (backedOff || config.fallback === void 0 || request === void 0) {
221
- return settled;
311
+ const { call, cmds, outcome } = rc.settle(s, { ...msg, error });
312
+ if (outcome.kind !== "failed" || config.fallback === void 0 || request === void 0) {
313
+ return [call, cmds];
222
314
  }
223
315
  return settleFromFallback(s, key, request, config.fallback, msg.at);
224
316
  }
225
317
  function onTimer(s, msg) {
226
318
  return rc.onTimer(s, msg);
227
319
  }
228
- function subs(s) {
229
- return rc.subs(s);
230
- }
231
- async function ask(request, signal) {
232
- if (config.port === void 0) {
233
- if (config.fallback === void 0) {
234
- throw new JevAskFailure({ _tag: "no_answer_path" });
235
- }
236
- const decided = config.fallback(request);
237
- if (isErrDecision(decided)) throw new JevAskFailure(decided);
238
- return {
239
- answers: decided,
240
- model: request.model,
241
- usage: NO_USAGE,
242
- source: "fallback"
243
- };
244
- }
245
- const { status, body } = await config.port(request, signal);
246
- const kind = classifyStatus(status);
247
- if (kind === "retry")
248
- throw new JevAskFailure({ _tag: "http_retry", status });
249
- if (kind === "terminal") {
250
- throw new JevAskFailure({ _tag: "http_terminal", status });
251
- }
252
- const parsed = parseAnswers(config.questions, body);
253
- if (!parsed.ok) throw new JevAskFailure(parsed.error);
254
- return {
255
- answers: parsed.answers,
256
- model: parsed.model,
257
- usage: parsed.usage,
258
- source: "port"
259
- };
260
- }
261
- const resilientRunHandler = rc.handlers({
262
- run: (request) => ask(request)
263
- }).resilient_run;
264
- async function settleOf(cmd) {
265
- const settle = await resilientRunHandler(cmd, {});
266
- if (settle.type === MsgType.ResilientOk) return settle;
267
- return {
268
- type: MsgType.ResilientErr,
269
- key: settle.key,
270
- error: toAskErr(settle.error),
271
- at: settle.at
272
- };
320
+ function deadlines(s) {
321
+ return rc.deadlines(s);
273
322
  }
274
- function handlers() {
275
- return { resilient_run: (cmd) => settleOf(cmd) };
276
- }
277
- return { init, attempt, succeed, fail, onTimer, subs, handlers, ask };
323
+ return {
324
+ name: rc.name,
325
+ /** The `Cmd.define`d run Cmd — list it in the machine's `cmds`. */
326
+ run: rc.run,
327
+ init,
328
+ attempt,
329
+ succeed,
330
+ fail: fail2,
331
+ onTimer,
332
+ deadlines,
333
+ /** The built-in `timer` Sub's deps — resilient-call's `timer`. */
334
+ timer: rc.timer,
335
+ /** Your handler's outcome for an HTTP reply — {@link decodeJevReply}. */
336
+ decode: (request, reply) => decodeJevReply(request, reply),
337
+ /** Your handler's outcome when the call threw — {@link jevCallThrew}. */
338
+ rejected: jevCallThrew,
339
+ /** Your handler's outcome with no network — {@link offlineJevAnswer}. */
340
+ offline: (request) => offlineJevAnswer(request, config.fallback)
341
+ };
278
342
  }
279
343
  function liftJevAsk(state, result) {
280
- return liftResilience(state, result);
344
+ return liftSlice("resilience", state, result);
281
345
  }
282
346
 
283
347
  // src/internal/jev/classify-batch/index.ts
@@ -346,75 +410,98 @@ function createClassifyBatch(config) {
346
410
  id: cmd.items.map(config.keyOf).join("\0"),
347
411
  items: cmd.items
348
412
  }));
349
- const [next, cmds] = fanOut.scatter(state.fanOut, batches);
350
- return [{ ...state, fanOut: next }, cmds];
413
+ return liftSlice("fanOut", state, fanOut.scatter(state.fanOut, batches));
351
414
  }
352
- function add(state, item, at) {
415
+ function add(state, item, at2) {
353
416
  const key = config.keyOf(item);
354
- if (has(state.cache, key, at)) return [state, []];
417
+ if (has(state.cache, key, at2)) return [state, []];
355
418
  if (keysInFlight(state).has(key)) return [state, []];
356
419
  const failed = { ...state.failed };
357
420
  delete failed[key];
358
- const [nextWindow, flushed] = window.add(state.window, item, at);
359
- return scatter({ ...state, failed, window: nextWindow }, flushed);
360
- }
361
- function onWindow(state, at) {
362
- const [nextWindow, flushed] = window.onWindow(state.window, at);
363
- return scatter({ ...state, window: nextWindow }, flushed);
421
+ const [buffered, flushed] = liftSlice(
422
+ "window",
423
+ { ...state, failed },
424
+ window.add(state.window, item, at2)
425
+ );
426
+ return scatter(buffered, flushed);
427
+ }
428
+ function onWindow(state, at2) {
429
+ const [closed, flushed] = liftSlice(
430
+ "window",
431
+ state,
432
+ window.onWindow(state.window, at2)
433
+ );
434
+ return scatter(closed, flushed);
364
435
  }
365
436
  function onBatchOk(state, msg) {
366
437
  let cache = state.cache;
367
- for (const [key, answer] of Object.entries(msg.result.answers)) {
438
+ for (const [key, answer] of Object.entries(msg.value.answers)) {
368
439
  cache = set(cache, key, answer, msg.at, config.ttlMs);
369
440
  }
370
441
  const failed = { ...state.failed };
371
- for (const key of Object.keys(msg.result.answers)) delete failed[key];
372
- const [nextFanOut, cmds] = fanOut.itemOk(state.fanOut, msg.key, msg.result);
373
- return [{ ...state, cache, failed, fanOut: nextFanOut }, cmds];
442
+ for (const key of Object.keys(msg.value.answers)) delete failed[key];
443
+ return liftSlice(
444
+ "fanOut",
445
+ { ...state, cache, failed },
446
+ fanOut.itemOk(state.fanOut, msg.cmd.key, msg.value)
447
+ );
374
448
  }
375
449
  function onBatchErr(state, msg) {
376
- const batch = runningBatch(state, msg.key);
450
+ const batch = runningBatch(state, msg.cmd.key);
451
+ const error = jevAskErrOf(msg.error);
377
452
  const failed = { ...state.failed };
378
453
  if (batch !== void 0) {
379
- for (const item of batch.items) failed[config.keyOf(item)] = msg.error;
454
+ for (const item of batch.items) failed[config.keyOf(item)] = error;
380
455
  }
381
- const [nextFanOut, cmds] = fanOut.itemErr(state.fanOut, msg.key, msg.error);
382
- return [{ ...state, failed, fanOut: nextFanOut }, cmds];
383
- }
384
- function onEvict(state, at) {
385
- return [{ ...state, cache: evictExpired(state.cache, at) }, []];
386
- }
387
- function answerFor(state, key, at) {
388
- const answer = get(state.cache, key, at);
389
- if (answer !== void 0) return { status: "answered", answer };
390
- const error = state.failed[key];
391
- if (error !== void 0) return { status: "failed", error };
392
- if (keysInFlight(state).has(key)) return { status: "pending" };
393
- return { status: "absent" };
456
+ return liftSlice(
457
+ "fanOut",
458
+ { ...state, failed },
459
+ fanOut.itemErr(state.fanOut, msg.cmd.key, error)
460
+ );
461
+ }
462
+ function onEvict(state, at2) {
463
+ return liftSlice("cache", state, [evictExpired(state.cache, at2), []]);
464
+ }
465
+ const ANSWER_ORDER = [
466
+ {
467
+ name: "cache",
468
+ read: ({ state, key, at: at2 }) => {
469
+ const answer = get(state.cache, key, at2);
470
+ return answer === void 0 ? void 0 : { status: "answered", answer };
471
+ }
472
+ },
473
+ {
474
+ name: "failed",
475
+ read: ({ state, key }) => {
476
+ const error = state.failed[key];
477
+ return error === void 0 ? void 0 : { status: "failed", error };
478
+ }
479
+ },
480
+ {
481
+ name: "inFlight",
482
+ read: ({ state, key }) => keysInFlight(state).has(key) ? { status: "pending" } : void 0
483
+ }
484
+ ];
485
+ function answerFor(state, key, at2) {
486
+ return readInOrder({ state, key, at: at2 }, ANSWER_ORDER, { status: "absent" });
394
487
  }
395
488
  function subs(state, id = "jev-classify-batch") {
396
- const windowSubs = window.subs(state.window, id);
397
- if (config.evictEveryMs === void 0) return windowSubs;
398
- return [...windowSubs, cacheEvictionSub(id, config.evictEveryMs)];
399
- }
400
- function subscribers() {
401
- return { deadline: subscribeBatchWindow, cache: cacheEvictionSubscribe };
489
+ return window.subs(state.window, id);
402
490
  }
403
- function handlers() {
404
- return {
405
- resilient_run: (cmd) => {
406
- const request = cmd.input;
407
- const ask = createJevAsk({
408
- questions: request.questions,
409
- ...config.port === void 0 ? {} : { port: config.port },
410
- ...config.fallback === void 0 ? {} : { fallback: config.fallback },
411
- ...config.model === void 0 ? {} : { model: config.model }
412
- });
413
- return ask.handlers().resilient_run(cmd);
414
- }
491
+ function subEntries(select, id = "jev-classify-batch") {
492
+ const flush = {
493
+ type: "timer",
494
+ deps: (model) => window.timer(select(model).window, id)
415
495
  };
496
+ if (config.evictEveryMs === void 0) return [flush];
497
+ return [flush, cacheEvictionSub(id, config.evictEveryMs)];
498
+ }
499
+ function subscribers() {
500
+ return { cache: cacheEvictionSubscribe };
416
501
  }
417
502
  return {
503
+ /** The ask Cmd def — list it in the machine's `cmds`. */
504
+ ask: askCmd,
418
505
  init,
419
506
  add,
420
507
  onWindow,
@@ -423,11 +510,20 @@ function createClassifyBatch(config) {
423
510
  onEvict,
424
511
  answerFor,
425
512
  subs,
513
+ subEntries,
426
514
  subscribers,
427
- handlers
515
+ /**
516
+ * Your handler's outcome for Jev's reply to one batch — `../ask`'s
517
+ * `decodeJevReply`, parsing against the questions the batch carried.
518
+ */
519
+ decode: (request, reply) => decodeJevReply(request, reply),
520
+ /** Your handler's outcome when the call threw — `../ask`'s `jevCallThrew`. */
521
+ rejected: jevCallThrew,
522
+ /** Your handler's outcome with no network — the configured `fallback`. */
523
+ offline: (request) => offlineJevAnswer(request, config.fallback)
428
524
  };
429
525
  }
430
526
 
431
- export { DEFAULT_CLASSIFY_MAX_ITEMS, DEFAULT_JEV_MODEL, JEV_ENDPOINT, JevAskFailure, classifyStatus, createClassifyBatch, createJevAsk, isJevErr, isTransientJevAskErr, jevAskCmdDef, jevQuestions, liftJevAsk, parseAnswers };
527
+ export { DEFAULT_CLASSIFY_MAX_ITEMS, DEFAULT_JEV_MODEL, JEV_ENDPOINT, classifyStatus, createClassifyBatch, createJevAsk, decodeJevReply, isJevErr, isTransientJevAskErr, jevAskCmdDef, jevAskErrOf, jevCallThrew, jevQuestions, liftJevAsk, offlineJevAnswer, parseAnswers };
432
528
  //# sourceMappingURL=index.js.map
433
529
  //# sourceMappingURL=index.js.map