@demlik/tea 0.6.0 → 0.8.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 (158) 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 +217 -0
  12. package/dist/chart/index.js +16 -0
  13. package/dist/chart/index.js.map +1 -0
  14. package/dist/chart/inspect/index.d.ts +85 -0
  15. package/dist/chart/inspect/index.js +5 -0
  16. package/dist/chart/inspect/index.js.map +1 -0
  17. package/dist/chart/inspect/react.d.ts +112 -0
  18. package/dist/chart/inspect/react.js +11 -0
  19. package/dist/chart/inspect/react.js.map +1 -0
  20. package/dist/chart/inspect/styles.css +286 -0
  21. package/dist/chart/lane/index.d.ts +361 -0
  22. package/dist/chart/lane/index.js +9 -0
  23. package/dist/chart/lane/index.js.map +1 -0
  24. package/dist/chart/lane/react.d.ts +83 -0
  25. package/dist/chart/lane/react.js +559 -0
  26. package/dist/chart/lane/react.js.map +1 -0
  27. package/dist/chart/lane/styles.css +498 -0
  28. package/dist/chart/report/index.d.ts +116 -0
  29. package/dist/chart/report/index.js +32 -0
  30. package/dist/chart/report/index.js.map +1 -0
  31. package/dist/{chunk-2UGTDYOK.js → chunk-3JU6657S.js} +3 -3
  32. package/dist/{chunk-2UGTDYOK.js.map → chunk-3JU6657S.js.map} +1 -1
  33. package/dist/{chunk-AZ3NHUPD.js → chunk-4KTWLPL5.js} +3 -3
  34. package/dist/{chunk-AZ3NHUPD.js.map → chunk-4KTWLPL5.js.map} +1 -1
  35. package/dist/{chunk-ZH454CR5.js → chunk-5EHLEUTL.js} +24 -4
  36. package/dist/chunk-5EHLEUTL.js.map +1 -0
  37. package/dist/chunk-5KJKJOFC.js +758 -0
  38. package/dist/chunk-5KJKJOFC.js.map +1 -0
  39. package/dist/{chunk-WF46FABO.js → chunk-7SVIRMC6.js} +3 -3
  40. package/dist/{chunk-WF46FABO.js.map → chunk-7SVIRMC6.js.map} +1 -1
  41. package/dist/{chunk-XMHZ44QC.js → chunk-7V4EVLZA.js} +4 -4
  42. package/dist/{chunk-XMHZ44QC.js.map → chunk-7V4EVLZA.js.map} +1 -1
  43. package/dist/{chunk-IVHQCVFX.js → chunk-A56OR5RF.js} +3 -3
  44. package/dist/{chunk-IVHQCVFX.js.map → chunk-A56OR5RF.js.map} +1 -1
  45. package/dist/{chunk-O7ED34JH.js → chunk-B5VXOFMZ.js} +47 -15
  46. package/dist/chunk-B5VXOFMZ.js.map +1 -0
  47. package/dist/chunk-E6GEDSF4.js +307 -0
  48. package/dist/chunk-E6GEDSF4.js.map +1 -0
  49. package/dist/{chunk-EC6FO42Q.js → chunk-EUF7XOEE.js} +6 -6
  50. package/dist/{chunk-EC6FO42Q.js.map → chunk-EUF7XOEE.js.map} +1 -1
  51. package/dist/{chunk-XOAP3LOT.js → chunk-I5OV65MS.js} +3 -3
  52. package/dist/{chunk-XOAP3LOT.js.map → chunk-I5OV65MS.js.map} +1 -1
  53. package/dist/chunk-JER5O7PQ.js +416 -0
  54. package/dist/chunk-JER5O7PQ.js.map +1 -0
  55. package/dist/{chunk-ASX7PL2K.js → chunk-JGVL3WPL.js} +3 -3
  56. package/dist/{chunk-ASX7PL2K.js.map → chunk-JGVL3WPL.js.map} +1 -1
  57. package/dist/{chunk-S6Y3RNX4.js → chunk-JP5D6GSL.js} +4 -4
  58. package/dist/{chunk-S6Y3RNX4.js.map → chunk-JP5D6GSL.js.map} +1 -1
  59. package/dist/{chunk-ERYY5P6W.js → chunk-ND3DH6KM.js} +3 -3
  60. package/dist/{chunk-ERYY5P6W.js.map → chunk-ND3DH6KM.js.map} +1 -1
  61. package/dist/{chunk-A7USKARI.js → chunk-SUST46KO.js} +3 -3
  62. package/dist/{chunk-A7USKARI.js.map → chunk-SUST46KO.js.map} +1 -1
  63. package/dist/{chunk-2KBEIGYP.js → chunk-TP5UPGLY.js} +3 -3
  64. package/dist/{chunk-2KBEIGYP.js.map → chunk-TP5UPGLY.js.map} +1 -1
  65. package/dist/chunk-UOC2PL46.js +13 -0
  66. package/dist/chunk-UOC2PL46.js.map +1 -0
  67. package/dist/{chunk-66S2KEQQ.js → chunk-WLGO64WC.js} +4 -4
  68. package/dist/{chunk-66S2KEQQ.js.map → chunk-WLGO64WC.js.map} +1 -1
  69. package/dist/chunk-WRAA7GVZ.js +209 -0
  70. package/dist/chunk-WRAA7GVZ.js.map +1 -0
  71. package/dist/chunk-ZCL7SCBK.js +685 -0
  72. package/dist/chunk-ZCL7SCBK.js.map +1 -0
  73. package/dist/chunk-ZD6PNZBK.js +457 -0
  74. package/dist/chunk-ZD6PNZBK.js.map +1 -0
  75. package/dist/{chunk-UV7VCTNC.js → chunk-ZF6WDK2D.js} +7 -7
  76. package/dist/{chunk-UV7VCTNC.js.map → chunk-ZF6WDK2D.js.map} +1 -1
  77. package/dist/{core-CUN5HDKL.d.ts → core-Bjc0bS8j.d.ts} +22 -1
  78. package/dist/deadline/index.d.ts +2 -2
  79. package/dist/deadline/index.js +3 -3
  80. package/dist/devtools/index.js +2 -207
  81. package/dist/devtools/index.js.map +1 -1
  82. package/dist/do/index.d.ts +7 -6
  83. package/dist/do/index.js +10 -10
  84. package/dist/{durable-effects-CXMOi-sp.d.ts → durable-effects-D8SqU_if.d.ts} +1 -1
  85. package/dist/extension/index.d.ts +3 -2
  86. package/dist/extension/index.js +3 -3
  87. package/dist/extension/react.js +3 -3
  88. package/dist/extension/subs/index.d.ts +2 -2
  89. package/dist/fan-out/index.d.ts +3 -2
  90. package/dist/fold-Blbhru48.d.ts +435 -0
  91. package/dist/graph-CcjWZVrd.d.ts +923 -0
  92. package/dist/idempotent-intake/index.d.ts +3 -2
  93. package/dist/index.d.ts +5 -4
  94. package/dist/index.js +2 -2
  95. package/dist/llm-call/index.d.ts +2 -2
  96. package/dist/llm-call/index.js +7 -7
  97. package/dist/{machine-CoWpvp8x.d.ts → machine-0OmPvsy_.d.ts} +1 -1
  98. package/dist/machine-viz/index.d.ts +19 -2
  99. package/dist/machine-viz/index.js +4 -10
  100. package/dist/machine-viz/index.js.map +1 -1
  101. package/dist/mem/index.d.ts +3 -2
  102. package/dist/monitored-run/index.d.ts +4 -3
  103. package/dist/monitored-run/index.js +5 -5
  104. package/dist/node/index.d.ts +3 -2
  105. package/dist/paginated-walk/index.d.ts +2 -2
  106. package/dist/paginated-walk/index.js +7 -7
  107. package/dist/parity/index.d.ts +3 -2
  108. package/dist/parity/index.js +3 -3
  109. package/dist/pbt/arbitraries/index.d.ts +1 -1
  110. package/dist/pbt/index.d.ts +1 -1
  111. package/dist/pbt/index.js +3 -3
  112. package/dist/pbt/runners/index.d.ts +1 -1
  113. package/dist/pbt/runners/index.js +3 -3
  114. package/dist/poller/index.d.ts +28 -7
  115. package/dist/poller/index.js +3 -3
  116. package/dist/poller/index.js.map +1 -1
  117. package/dist/prediction/index.d.ts +1 -1
  118. package/dist/prediction/index.js +2 -2
  119. package/dist/pure/index.d.ts +1 -1
  120. package/dist/pure/index.js +2 -2
  121. package/dist/react/index.d.ts +3 -2
  122. package/dist/react/index.js +2 -2
  123. package/dist/reconciler/index.d.ts +2 -2
  124. package/dist/reconciler/index.js +7 -7
  125. package/dist/recorder/index.d.ts +3 -2
  126. package/dist/reducer-BqyoVyBG.d.ts +447 -0
  127. package/dist/resilient-call/index.d.ts +2 -2
  128. package/dist/resilient-call/index.js +6 -6
  129. package/dist/run-BVeqJDBF.d.ts +713 -0
  130. package/dist/{runtime-types-BD3ZFfdw.d.ts → runtime-types-Bxc637bp.d.ts} +53 -2
  131. package/dist/saga/index.d.ts +3 -2
  132. package/dist/samples-DCt6nF8H.d.ts +59 -0
  133. package/dist/snapshot/index.d.ts +1 -1
  134. package/dist/snapshot/index.js +3 -3
  135. package/dist/subs/index.d.ts +4 -3
  136. package/dist/subs/index.js +3 -3
  137. package/dist/testing/index.d.ts +3 -2
  138. package/dist/testing/index.js +2 -2
  139. package/dist/throttled-input/index.d.ts +2 -2
  140. package/dist/throttled-input/index.js +6 -6
  141. package/dist/token-refresh/index.d.ts +1 -1
  142. package/dist/token-refresh/index.js +3 -3
  143. package/dist/trace-replay/index.d.ts +3 -2
  144. package/dist/trace-replay/index.js +3 -3
  145. package/dist/{types-CjAkI83U.d.ts → types-D7u9DkH7.d.ts} +1 -1
  146. package/dist/with-deadline/index.d.ts +1 -1
  147. package/dist/with-deadline/index.js +2 -2
  148. package/dist/with-resilience/index.d.ts +2 -2
  149. package/dist/with-resilience/index.js +6 -6
  150. package/dist/with-telemetry/index.d.ts +1 -1
  151. package/dist/with-telemetry/index.js +2 -2
  152. package/dist/work-queue/adapter.d.ts +3 -2
  153. package/dist/work-queue/index.d.ts +3 -2
  154. package/dist/work-queue/ops.d.ts +3 -2
  155. package/dist/workflow/index.d.ts +3 -3
  156. package/package.json +31 -1
  157. package/dist/chunk-O7ED34JH.js.map +0 -1
  158. package/dist/chunk-ZH454CR5.js.map +0 -1
@@ -0,0 +1,758 @@
1
+ import { chartMermaid, edgeKey } from './chunk-E6GEDSF4.js';
2
+
3
+ // src/chart/report/draw.ts
4
+ function drawTask(chart, opts = {}) {
5
+ return chartMermaid(chart, {
6
+ polarity: true,
7
+ ...opts.current === void 0 ? {} : { highlight: opts.current },
8
+ ...opts.walked === void 0 ? {} : { walked: opts.walked }
9
+ });
10
+ }
11
+ function walkedEdges(steps) {
12
+ const counts = /* @__PURE__ */ new Map();
13
+ for (const step of steps) {
14
+ counts.set(step.edge, (counts.get(step.edge) ?? 0) + 1);
15
+ }
16
+ return counts;
17
+ }
18
+
19
+ // src/chart/report/workflow.ts
20
+ var bareEvent = (event) => {
21
+ const dot = event.indexOf(".");
22
+ return dot === -1 ? event : event.slice(dot + 1);
23
+ };
24
+ var WorkflowImportError = class extends Error {
25
+ constructor(defects) {
26
+ super(
27
+ `@demlik/tea: this workflow.json does not compile to charts \u2014
28
+ ` + defects.map((d) => ` \u2022 ${d}`).join("\n")
29
+ );
30
+ this.defects = defects;
31
+ }
32
+ defects;
33
+ name = "WorkflowImportError";
34
+ _tag = "WorkflowImportError";
35
+ };
36
+ var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
37
+ var nodeType = (node) => isRecord(node) && typeof node.type === "string" ? node.type : void 0;
38
+ var RETRY_BUDGET = 2;
39
+ var DEFAULT_GUARD = "retries remain";
40
+ function importRegion(taskId, region) {
41
+ const defects = [];
42
+ if (!isRecord(region) || !isRecord(region.states) || typeof region.initial !== "string") {
43
+ return {
44
+ defects: [
45
+ `task "${taskId}": region must carry string \`initial\` and \`states\``
46
+ ]
47
+ };
48
+ }
49
+ const states = region.states;
50
+ const initialState = region.initial;
51
+ if (states[initialState] === void 0) {
52
+ defects.push(
53
+ `task "${taskId}": initial state "${initialState}" is not in \`states\``
54
+ );
55
+ }
56
+ const finals = /* @__PURE__ */ new Set();
57
+ for (const [name, node] of Object.entries(states)) {
58
+ if (nodeType(node) === "final") finals.add(name);
59
+ }
60
+ const errorFinals = /* @__PURE__ */ new Set();
61
+ const on = {};
62
+ for (const [stateName, node] of Object.entries(states)) {
63
+ if (nodeType(node) === "history") continue;
64
+ const edges = {};
65
+ on[stateName] = edges;
66
+ if (!isRecord(node)) {
67
+ defects.push(`task "${taskId}": state "${stateName}" is not an object`);
68
+ continue;
69
+ }
70
+ const raw = node.on ?? {};
71
+ if (!isRecord(raw)) {
72
+ defects.push(
73
+ `task "${taskId}": state "${stateName}" carries a non-object \`on\``
74
+ );
75
+ continue;
76
+ }
77
+ const spelledAs = /* @__PURE__ */ new Map();
78
+ for (const [eventName, transition] of Object.entries(raw)) {
79
+ const event = bareEvent(eventName);
80
+ if (event === "") {
81
+ defects.push(
82
+ `task "${taskId}": state "${stateName}" listens for "${eventName}", which names no event once its namespace is stripped`
83
+ );
84
+ continue;
85
+ }
86
+ const already = spelledAs.get(event);
87
+ if (already !== void 0) {
88
+ defects.push(
89
+ `task "${taskId}": state "${stateName}" spells the same event twice \u2014 "${already}" and "${eventName}" are both \`${event}\`, and a state routes each event once`
90
+ );
91
+ continue;
92
+ }
93
+ spelledAs.set(event, eventName);
94
+ if (Array.isArray(transition)) {
95
+ const arms = transition.map(
96
+ (arm) => isRecord(arm) && typeof arm.target === "string" ? arm.target : void 0
97
+ );
98
+ const [retry, fallthrough] = arms;
99
+ if (transition.length !== 2 || retry === void 0 || fallthrough === void 0) {
100
+ defects.push(
101
+ `task "${taskId}": guarded "${eventName}" must be a two-arm array of \`{target}\` \u2014 [retry-while-retries-remain, else-fallthrough]`
102
+ );
103
+ continue;
104
+ }
105
+ for (const target of [retry, fallthrough]) {
106
+ if (states[target] === void 0) {
107
+ defects.push(
108
+ `task "${taskId}": "${eventName}" targets unknown state "${target}"`
109
+ );
110
+ }
111
+ }
112
+ if (finals.has(fallthrough)) errorFinals.add(fallthrough);
113
+ const head = transition[0];
114
+ const when = isRecord(head) && typeof head.guard === "string" ? head.guard : DEFAULT_GUARD;
115
+ edges[event] = { target: retry, when, otherwise: fallthrough };
116
+ continue;
117
+ }
118
+ if (typeof transition !== "string") {
119
+ defects.push(
120
+ `task "${taskId}": "${eventName}" is neither a target nor a guarded array`
121
+ );
122
+ continue;
123
+ }
124
+ if (states[transition] === void 0) {
125
+ defects.push(
126
+ `task "${taskId}": "${eventName}" targets unknown state "${transition}"`
127
+ );
128
+ continue;
129
+ }
130
+ edges[event] = nodeType(states[transition]) === "history" ? { resume: { fallback: initialState } } : { target: transition };
131
+ }
132
+ }
133
+ if (defects.length > 0) return { defects };
134
+ const out = {};
135
+ for (const [name, node] of Object.entries(states)) {
136
+ if (nodeType(node) === "history") continue;
137
+ const edges = on[name] ?? {};
138
+ const isFinal = finals.has(name);
139
+ out[name] = {
140
+ ...name === initialState ? { initial: true } : {},
141
+ ...Object.keys(edges).length > 0 ? { on: edges } : {},
142
+ ...isFinal ? { end: errorFinals.has(name) ? "error" : true } : {}
143
+ };
144
+ }
145
+ return { node: out, defects: [] };
146
+ }
147
+ function onDoneTargets(phaseName, onDone) {
148
+ const defect = `phase "${phaseName}": \`onDone\` must be a two-arm array of \`{target}\` \u2014 [{target, guard}, {target}]`;
149
+ if (!Array.isArray(onDone) || onDone.length !== 2) return { defect };
150
+ const [success, trip] = onDone.map(
151
+ (arm) => isRecord(arm) && typeof arm.target === "string" ? arm.target : void 0
152
+ );
153
+ return success === void 0 || trip === void 0 ? { defect } : { targets: [success, trip] };
154
+ }
155
+ function chartFromWorkflow(document, options = {}) {
156
+ const defects = [];
157
+ const machineDef = isRecord(document) ? document.machine : void 0;
158
+ if (!isRecord(machineDef) || !isRecord(machineDef.states)) {
159
+ throw new WorkflowImportError([
160
+ "document must carry a `machine.states` object"
161
+ ]);
162
+ }
163
+ const documentContext = isRecord(machineDef.context) ? machineDef.context : {};
164
+ const declaredTrigger = isRecord(document) ? document.trigger : void 0;
165
+ if (declaredTrigger !== void 0 && typeof declaredTrigger !== "string") {
166
+ defects.push(
167
+ "document `trigger` must be a string naming what fires this lane"
168
+ );
169
+ }
170
+ const machineStates = machineDef.states;
171
+ const charts = {};
172
+ const context = {};
173
+ const phases = [];
174
+ const gateTargets = /* @__PURE__ */ new Set();
175
+ let terminals;
176
+ for (const [phaseName, node] of Object.entries(machineStates)) {
177
+ if (nodeType(node) !== "parallel" || !isRecord(node)) continue;
178
+ const regions = node.states;
179
+ if (!isRecord(regions) || Object.keys(regions).length === 0) {
180
+ defects.push(
181
+ `phase "${phaseName}": a parallel phase must carry task regions in \`states\``
182
+ );
183
+ continue;
184
+ }
185
+ const phaseTasks = [];
186
+ for (const [taskId, region] of Object.entries(regions)) {
187
+ const imported = importRegion(taskId, region);
188
+ defects.push(...imported.defects);
189
+ if (imported.node !== void 0) {
190
+ const events = {};
191
+ for (const edges of Object.values(imported.node)) {
192
+ for (const event of Object.keys(edges.on ?? {})) {
193
+ const origin = options.from?.[event];
194
+ events[event] = origin === void 0 ? { scope: "edges" } : { scope: "edges", from: origin };
195
+ }
196
+ }
197
+ charts[taskId] = { events, states: { [phaseName]: imported.node } };
198
+ }
199
+ const ctx = documentContext[taskId];
200
+ const bag = isRecord(ctx) ? ctx : {};
201
+ const { maxRetries: rawMax, retries: _retries, ...extras } = bag;
202
+ context[taskId] = {
203
+ maxRetries: typeof rawMax === "number" ? rawMax : RETRY_BUDGET,
204
+ extras
205
+ };
206
+ phaseTasks.push(taskId);
207
+ }
208
+ phases.push({ name: phaseName, tasks: phaseTasks });
209
+ const gate = onDoneTargets(phaseName, node.onDone);
210
+ if (gate.defect !== void 0) defects.push(gate.defect);
211
+ if (gate.targets !== void 0) {
212
+ for (const target of gate.targets) {
213
+ gateTargets.add(target);
214
+ if (machineStates[target] === void 0) {
215
+ defects.push(
216
+ `phase "${phaseName}": \`onDone\` targets unknown machine-level state "${target}"`
217
+ );
218
+ }
219
+ }
220
+ terminals = { complete: gate.targets[0], tripped: gate.targets[1] };
221
+ }
222
+ }
223
+ for (const [name, node] of Object.entries(machineStates)) {
224
+ if (nodeType(node) === "parallel" && isRecord(node)) continue;
225
+ if (nodeType(node) !== "final") {
226
+ defects.push(
227
+ `machine-level state "${name}" is neither a \`parallel\` phase nor a \`final\` terminal`
228
+ );
229
+ } else if (!gateTargets.has(name)) {
230
+ defects.push(
231
+ `machine-level final "${name}" is targeted by no phase's \`onDone\` pair`
232
+ );
233
+ }
234
+ }
235
+ if (phases.length === 0)
236
+ defects.push("machine holds no `parallel` phase state");
237
+ if (defects.length > 0) throw new WorkflowImportError(defects);
238
+ if (terminals === void 0) {
239
+ throw new WorkflowImportError([
240
+ "no phase carried a readable `onDone` pair"
241
+ ]);
242
+ }
243
+ const id = isRecord(document) && typeof document.id === "string" ? document.id : void 0;
244
+ const lane = {
245
+ ...id === void 0 ? {} : { id },
246
+ ...typeof declaredTrigger === "string" ? { trigger: declaredTrigger } : {},
247
+ phases,
248
+ terminals,
249
+ charts,
250
+ context
251
+ };
252
+ if (options.strictFrom === true) {
253
+ const declared = new Set(eventAlphabet(lane));
254
+ const strays = Object.keys(options.from ?? {}).filter(
255
+ (event) => !declared.has(event)
256
+ );
257
+ if (strays.length > 0) {
258
+ throw new WorkflowImportError(
259
+ strays.map(
260
+ (event) => `\`from\` names "${event}", which no state in this document routes \u2014 the map is keyed by the BARE event name`
261
+ )
262
+ );
263
+ }
264
+ }
265
+ return lane;
266
+ }
267
+ function chartFromWorkflowText(text, options = {}) {
268
+ let parsed;
269
+ try {
270
+ parsed = JSON.parse(text);
271
+ } catch {
272
+ throw new WorkflowImportError(["the document is not JSON"]);
273
+ }
274
+ return chartFromWorkflow(parsed, options);
275
+ }
276
+ function statesOf(chart) {
277
+ const out = /* @__PURE__ */ new Map();
278
+ for (const group of Object.values(chart.states)) {
279
+ for (const [name, node] of Object.entries(group)) out.set(name, node);
280
+ }
281
+ return out;
282
+ }
283
+ function initialOf(chart) {
284
+ for (const [name, node] of statesOf(chart)) {
285
+ if (node.initial === true) return name;
286
+ }
287
+ throw new Error("@demlik/tea: imported chart marks no initial state");
288
+ }
289
+ function eventAlphabet(lane) {
290
+ const out = /* @__PURE__ */ new Set();
291
+ for (const chart of Object.values(lane.charts)) {
292
+ for (const event of Object.keys(chart.events)) out.add(event);
293
+ }
294
+ return [...out];
295
+ }
296
+ function originOf(chart, event) {
297
+ return chart.events[event]?.from;
298
+ }
299
+ function endPolarityOf(node) {
300
+ if (node?.end === void 0) return false;
301
+ return node.end;
302
+ }
303
+
304
+ // src/chart/report/fold.ts
305
+ function walkedEdgeKey(chart, from, event, to) {
306
+ const declared = statesOf(chart).get(from)?.on?.[event];
307
+ const drawn = declared !== void 0 && "resume" in declared ? declared.resume.fallback : to;
308
+ return edgeKey(from, event, drawn);
309
+ }
310
+ var UnreplayableLogError = class extends Error {
311
+ constructor(defects) {
312
+ super(
313
+ `@demlik/tea: this events log does not replay against this workflow \u2014
314
+ ` + defects.map((d) => ` \u2022 ${d}`).join("\n")
315
+ );
316
+ this.defects = defects;
317
+ }
318
+ defects;
319
+ name = "UnreplayableLogError";
320
+ _tag = "UnreplayableLogError";
321
+ };
322
+ function parseEventsJsonl(text) {
323
+ const defects = [];
324
+ const entries = [];
325
+ for (const [index, line] of text.split("\n").entries()) {
326
+ if (line.trim() === "") continue;
327
+ let parsed;
328
+ try {
329
+ parsed = JSON.parse(line);
330
+ } catch {
331
+ defects.push(`line ${index + 1} is not JSON`);
332
+ continue;
333
+ }
334
+ const entry = asLogEntry(parsed);
335
+ if (entry === void 0) {
336
+ defects.push(
337
+ `line ${index + 1} does not carry string \`task\`/\`event\`/\`at\``
338
+ );
339
+ continue;
340
+ }
341
+ entries.push(entry);
342
+ }
343
+ if (defects.length > 0) throw new UnreplayableLogError(defects);
344
+ return entries;
345
+ }
346
+ function asLogEntry(value) {
347
+ if (typeof value !== "object" || value === null) return void 0;
348
+ const record = value;
349
+ if (typeof record.task !== "string" || typeof record.event !== "string" || typeof record.at !== "string") {
350
+ return void 0;
351
+ }
352
+ return { task: record.task, event: record.event, at: record.at };
353
+ }
354
+ function parseHistoryJson(text) {
355
+ let parsed;
356
+ try {
357
+ parsed = JSON.parse(text);
358
+ } catch {
359
+ throw new UnreplayableLogError(["`lane history` output is not JSON"]);
360
+ }
361
+ if (!Array.isArray(parsed)) {
362
+ throw new UnreplayableLogError([
363
+ "`lane history` output is not a JSON array of entries"
364
+ ]);
365
+ }
366
+ const entries = [];
367
+ for (const [index, raw] of parsed.entries()) {
368
+ const entry = asLogEntry(raw);
369
+ if (entry === void 0) {
370
+ throw new UnreplayableLogError([
371
+ `history entry ${index} does not carry string \`task\`/\`event\`/\`at\``
372
+ ]);
373
+ }
374
+ entries.push(entry);
375
+ }
376
+ return entries;
377
+ }
378
+ var unknownTaskDefects = (lane, entries) => {
379
+ const seen = /* @__PURE__ */ new Set();
380
+ const defects = [];
381
+ for (const entry of entries) {
382
+ if (lane.charts[entry.task] !== void 0 || seen.has(entry.task)) continue;
383
+ seen.add(entry.task);
384
+ defects.push(
385
+ `log names task "${entry.task}", which is not in this lane's machine`
386
+ );
387
+ }
388
+ return defects;
389
+ };
390
+ var chartOf = (lane, taskId) => {
391
+ const chart = lane.charts[taskId];
392
+ if (chart === void 0) {
393
+ throw new UnreplayableLogError([
394
+ `log names task "${taskId}", which is not in this lane's machine`
395
+ ]);
396
+ }
397
+ return chart;
398
+ };
399
+ function initialStates(lane) {
400
+ const out = {};
401
+ for (const [taskId, chart] of Object.entries(lane.charts)) {
402
+ out[taskId] = {
403
+ type: initialOf(chart),
404
+ retries: 0,
405
+ maxRetries: lane.context[taskId]?.maxRetries ?? RETRY_BUDGET
406
+ };
407
+ }
408
+ return out;
409
+ }
410
+ function stepTask(chart, state, event, taskId) {
411
+ const node = statesOf(chart).get(state.type);
412
+ const edge = node?.on?.[event];
413
+ if (edge === void 0) {
414
+ if (chart.refusals?.[state.type]?.includes(event) === true) return state;
415
+ throw new UnreplayableLogError([
416
+ `${taskId === void 0 ? "" : `task "${taskId}": `}state "${state.type}" holds no cell for "${event}"`
417
+ ]);
418
+ }
419
+ if ("resume" in edge) {
420
+ return { ...state, type: state.was ?? edge.resume.fallback };
421
+ }
422
+ if ("when" in edge) {
423
+ return state.retries < state.maxRetries ? {
424
+ ...state,
425
+ type: edge.target,
426
+ retries: state.retries + 1,
427
+ was: state.type
428
+ } : { ...state, type: edge.otherwise, was: state.type };
429
+ }
430
+ return { ...state, type: edge.target, was: state.type };
431
+ }
432
+ function foldLane(lane, entries) {
433
+ const unknown = unknownTaskDefects(lane, entries);
434
+ if (unknown.length > 0) throw new UnreplayableLogError(unknown);
435
+ const states = { ...initialStates(lane) };
436
+ for (const entry of entries) {
437
+ const chart = chartOf(lane, entry.task);
438
+ const current = states[entry.task];
439
+ if (current === void 0) {
440
+ throw new UnreplayableLogError([
441
+ `log names task "${entry.task}", which is not in this lane's machine`
442
+ ]);
443
+ }
444
+ states[entry.task] = stepTask(
445
+ chart,
446
+ current,
447
+ bareEvent(entry.event),
448
+ entry.task
449
+ );
450
+ }
451
+ return states;
452
+ }
453
+ function timeline(lane, entries) {
454
+ const unknown = unknownTaskDefects(lane, entries);
455
+ if (unknown.length > 0) throw new UnreplayableLogError(unknown);
456
+ const states = { ...initialStates(lane) };
457
+ const steps = [];
458
+ for (const [index, entry] of entries.entries()) {
459
+ const chart = chartOf(lane, entry.task);
460
+ const before = states[entry.task];
461
+ if (before === void 0) {
462
+ throw new UnreplayableLogError([
463
+ `log names task "${entry.task}", which is not in this lane's machine`
464
+ ]);
465
+ }
466
+ const event = bareEvent(entry.event);
467
+ const after = stepTask(chart, before, event, entry.task);
468
+ states[entry.task] = after;
469
+ steps.push({
470
+ index,
471
+ at: entry.at,
472
+ task: entry.task,
473
+ event: entry.event,
474
+ from: before.type,
475
+ to: after.type,
476
+ edge: walkedEdgeKey(chart, before.type, event, after.type)
477
+ });
478
+ }
479
+ return steps;
480
+ }
481
+ function trippedTasks(lane, states) {
482
+ return Object.keys(states).filter(
483
+ (taskId) => endPolarityOf(nodeOf(lane, taskId, states)) === "error"
484
+ );
485
+ }
486
+ function phaseStandings(lane, states) {
487
+ const isFinal = (taskId) => endPolarityOf(nodeOf(lane, taskId, states)) !== false;
488
+ const errors = trippedTasks(lane, states);
489
+ let stopped = false;
490
+ return lane.phases.map((phase, index) => {
491
+ const tripped = phase.tasks.filter((taskId) => errors.includes(taskId));
492
+ const base = { name: phase.name, index, tasks: phase.tasks, tripped };
493
+ if (stopped) return { ...base, standing: "waiting" };
494
+ if (!phase.tasks.every(isFinal)) {
495
+ stopped = true;
496
+ return { ...base, standing: "active" };
497
+ }
498
+ if (tripped.length > 0) {
499
+ stopped = true;
500
+ return { ...base, standing: "tripped" };
501
+ }
502
+ return { ...base, standing: "complete" };
503
+ });
504
+ }
505
+ function laneTerminalReached(stands, terminals) {
506
+ if (stands.some((stand) => stand.standing === "tripped"))
507
+ return terminals.tripped;
508
+ if (stands.some((stand) => stand.standing === "active")) return void 0;
509
+ return terminals.complete;
510
+ }
511
+ function deriveLaneStatus(lane, states) {
512
+ const errors = trippedTasks(lane, states);
513
+ const context = {};
514
+ for (const [taskId, state] of Object.entries(states)) {
515
+ context[taskId] = {
516
+ retries: state.retries,
517
+ maxRetries: state.maxRetries,
518
+ ...lane.context[taskId]?.extras ?? {}
519
+ };
520
+ }
521
+ context.errors = errors;
522
+ const stands = phaseStandings(lane, states);
523
+ const terminal = laneTerminalReached(stands, lane.terminals);
524
+ if (terminal !== void 0) {
525
+ return { stateValue: terminal, status: "done", context };
526
+ }
527
+ const active = stands.find((stand) => stand.standing === "active");
528
+ if (active === void 0) {
529
+ return { stateValue: lane.terminals.complete, status: "done", context };
530
+ }
531
+ const stateValue = {
532
+ [active.name]: Object.fromEntries(
533
+ active.tasks.map((taskId) => [taskId, states[taskId]?.type ?? "?"])
534
+ )
535
+ };
536
+ for (const stand of stands.slice(active.index + 1)) {
537
+ stateValue[stand.name] = "waiting";
538
+ }
539
+ return { stateValue, status: "active", context };
540
+ }
541
+ function nodeOf(lane, taskId, states) {
542
+ const chart = lane.charts[taskId];
543
+ const state = states[taskId];
544
+ if (chart === void 0 || state === void 0) return void 0;
545
+ return statesOf(chart).get(state.type);
546
+ }
547
+ function parseStatusJson(text) {
548
+ let parsed;
549
+ try {
550
+ parsed = JSON.parse(text);
551
+ } catch {
552
+ throw new UnreplayableLogError(["`lane status` output is not JSON"]);
553
+ }
554
+ if (typeof parsed !== "object" || parsed === null) {
555
+ throw new UnreplayableLogError(["`lane status` output is not an object"]);
556
+ }
557
+ const record = parsed;
558
+ if (record.status !== "active" && record.status !== "done") {
559
+ throw new UnreplayableLogError([
560
+ '`lane status` output carries no `status` of "active" | "done"'
561
+ ]);
562
+ }
563
+ if (record.stateValue === void 0) {
564
+ throw new UnreplayableLogError([
565
+ "`lane status` output carries no `stateValue`"
566
+ ]);
567
+ }
568
+ return {
569
+ stateValue: record.stateValue,
570
+ status: record.status,
571
+ context: typeof record.context === "object" && record.context !== null ? record.context : {}
572
+ };
573
+ }
574
+
575
+ // src/chart/report/report.ts
576
+ var isImportedLane = (v) => typeof v === "object" && v !== null && Array.isArray(v.phases) && typeof v.charts === "object";
577
+ function activePhaseName(status) {
578
+ if (typeof status.stateValue === "string") return void 0;
579
+ for (const [name, value] of Object.entries(status.stateValue)) {
580
+ if (typeof value === "object") return name;
581
+ }
582
+ return void 0;
583
+ }
584
+ var listed = (events) => events.map((e) => `\`${e}\``).join(", ");
585
+ function phrase(origin, state, events) {
586
+ if (origin === "cmd") {
587
+ return `the work \`${state}\` dispatched \u2014 ${listed(events)}`;
588
+ }
589
+ if (origin === "sub") {
590
+ return `the source \`${state}\` is subscribed to \u2014 ${listed(events)}`;
591
+ }
592
+ return `${origin.world}'s ${listed(events)}`;
593
+ }
594
+ var originKey = (origin) => typeof origin === "string" ? origin : `world:${origin.world}`;
595
+ function waitingOn(chart, state) {
596
+ const node = statesOf(chart).get(state);
597
+ if (node?.end !== void 0) return null;
598
+ const events = Object.keys(node?.on ?? {});
599
+ if (events.length === 0) {
600
+ return `\`${state}\` accepts no events, and it is not a final`;
601
+ }
602
+ const groups = /* @__PURE__ */ new Map();
603
+ const undeclared = [];
604
+ for (const event of events) {
605
+ const origin = originOf(chart, event);
606
+ if (origin === void 0) {
607
+ undeclared.push(event);
608
+ continue;
609
+ }
610
+ const key = originKey(origin);
611
+ const group = groups.get(key);
612
+ if (group === void 0) groups.set(key, { origin, events: [event] });
613
+ else group.events.push(event);
614
+ }
615
+ if (groups.size === 0) return `\`${state}\` accepts ${listed(events)}`;
616
+ const parts = [...groups.values()].map(
617
+ (g) => phrase(g.origin, state, g.events)
618
+ );
619
+ if (undeclared.length > 0) {
620
+ parts.push(`${listed(undeclared)}, from somewhere the chart does not say`);
621
+ }
622
+ return parts.join(" \xB7 ");
623
+ }
624
+ function retryLine(chart, state) {
625
+ if (state.retries <= 0) return null;
626
+ const budget = `${state.retries}/${state.maxRetries}`;
627
+ if (state.retries < state.maxRetries) return `**retries:** ${budget}`;
628
+ const node = statesOf(chart).get(state.type);
629
+ for (const [event, edge] of Object.entries(node?.on ?? {})) {
630
+ if ("when" in edge) {
631
+ return `**retries:** ${budget} \u2014 spent; one \`${event}\` from \`${edge.otherwise}\``;
632
+ }
633
+ }
634
+ return `**retries:** ${budget} \u2014 spent`;
635
+ }
636
+ function phaseLine(stand, states) {
637
+ const count = `${stand.tasks.length} task${stand.tasks.length === 1 ? "" : "s"}`;
638
+ if (stand.standing === "waiting") {
639
+ return `**${stand.name}:** waiting \u2014 ${count}, not started.`;
640
+ }
641
+ const leaves = stand.tasks.map(
642
+ (taskId) => `\`${taskId}\` = \`${states[taskId]?.type ?? "?"}\`${stand.tripped.includes(taskId) ? " **(tripped)**" : ""}`
643
+ ).join(", ");
644
+ return `**${stand.name}:** ${stand.standing} \u2014 ${count}: ${leaves}`;
645
+ }
646
+ function timelineTable(steps) {
647
+ if (steps.length === 0)
648
+ return "_no events yet \u2014 the lane is at its initial state._";
649
+ const rows = steps.map(
650
+ (s) => `| ${s.index + 1} | ${s.at} | \`${s.task}\` | \`${s.event}\` | \`${s.from}\` \u2192 \`${s.to}\` |`
651
+ );
652
+ return [
653
+ "| # | at | task | event | from \u2192 to |",
654
+ "|---|---|---|---|---|",
655
+ ...rows
656
+ ].join("\n");
657
+ }
658
+ function laneReport(input) {
659
+ const lane = isImportedLane(input.workflow) ? input.workflow : chartFromWorkflow(input.workflow);
660
+ const entries = input.entries;
661
+ const states = foldLane(lane, entries);
662
+ const status = input.status ?? deriveLaneStatus(lane, states);
663
+ const steps = timeline(lane, entries);
664
+ const title = input.title ?? lane.id ?? "lane";
665
+ const out = [`## ${title} \u2014 ${status.status}`, ""];
666
+ out.push(`**where it is:** ${describeStateValue(status.stateValue)}`);
667
+ if (lane.trigger !== void 0) out.push(`**fired by:** \`${lane.trigger}\``);
668
+ const errors = status.context.errors;
669
+ if (Array.isArray(errors) && errors.length > 0) {
670
+ out.push(
671
+ `**tripped:** ${errors.map((t) => `\`${String(t)}\``).join(", ")} \u2014 the lane lands on \`${lane.terminals.tripped}\``
672
+ );
673
+ }
674
+ out.push("");
675
+ const stands = phaseStandings(lane, states);
676
+ const named = activePhaseName(status);
677
+ const active = stands.find((s) => s.name === named) ?? stands.find((s) => s.standing === "active");
678
+ for (const stand of stands) {
679
+ if (stand === active) {
680
+ const untouched = [];
681
+ for (const taskId of stand.tasks) {
682
+ const chart = lane.charts[taskId];
683
+ const state = states[taskId];
684
+ if (chart === void 0 || state === void 0) continue;
685
+ if (state.type === initialOf(chart)) untouched.push(taskId);
686
+ }
687
+ if (untouched.length === stand.tasks.length) untouched.shift();
688
+ for (const taskId of stand.tasks) {
689
+ const chart = lane.charts[taskId];
690
+ const state = states[taskId];
691
+ if (chart === void 0 || state === void 0) continue;
692
+ if (untouched.includes(taskId)) continue;
693
+ const polarity = endPolarityOf(statesOf(chart).get(state.type));
694
+ out.push(
695
+ `### ${taskId} \u2014 \`${state.type}\`${polarity === "error" ? " \u2014 TRIPPED" : ""}`
696
+ );
697
+ const waiting = waitingOn(chart, state.type);
698
+ out.push(
699
+ waiting !== null ? `**waiting on:** ${waiting}` : polarity === "error" ? (
700
+ // The distinction `end: "error"` bought, spent where it is worth
701
+ // something: this task is as finished as a shipped one, and the
702
+ // phase it is in is going to trip the lane when its siblings
703
+ // finish. A report that said "final" for both would bury that.
704
+ `**waiting on:** nothing \u2014 this task landed on the error final \`${state.type}\`, and \`${stand.name}\` will trip the lane when its siblings finish.`
705
+ ) : "**waiting on:** nothing \u2014 this task is final."
706
+ );
707
+ const retries = retryLine(chart, state);
708
+ if (retries !== null) out.push(retries);
709
+ out.push("");
710
+ out.push("```mermaid");
711
+ out.push(
712
+ drawTask(chart, {
713
+ current: state.type,
714
+ walked: walkedEdges(steps.filter((s) => s.task === taskId))
715
+ })
716
+ );
717
+ out.push("```");
718
+ out.push("");
719
+ }
720
+ if (untouched.length > 0) {
721
+ const chart = lane.charts[untouched[0]];
722
+ const at = chart === void 0 ? "their entry state" : `\`${initialOf(chart)}\``;
723
+ out.push(
724
+ `**not started yet:** ${untouched.map((t) => `\`${t}\``).join(", ")} \u2014 still at ${at}.`
725
+ );
726
+ out.push("");
727
+ }
728
+ continue;
729
+ }
730
+ out.push(phaseLine(stand, states));
731
+ out.push("");
732
+ }
733
+ out.push("### timeline");
734
+ out.push("");
735
+ out.push(timelineTable(steps));
736
+ out.push("");
737
+ return {
738
+ markdown: `${out.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd()}
739
+ `
740
+ };
741
+ }
742
+ function describeStateValue(value) {
743
+ if (typeof value === "string") return `\`${value}\` \u2014 the workflow is done.`;
744
+ const parts = [];
745
+ for (const [phase, leaf] of Object.entries(value)) {
746
+ if (typeof leaf === "string") {
747
+ parts.push(`\`${phase}\`: ${leaf}`);
748
+ continue;
749
+ }
750
+ const inner = Object.entries(leaf).map(([task, state]) => `\`${task}\` = \`${state}\``).join(", ");
751
+ parts.push(`\`${phase}\` \u2192 ${inner}`);
752
+ }
753
+ return parts.join(" \xB7 ");
754
+ }
755
+
756
+ export { RETRY_BUDGET, UnreplayableLogError, WorkflowImportError, bareEvent, chartFromWorkflow, chartFromWorkflowText, deriveLaneStatus, drawTask, endPolarityOf, eventAlphabet, foldLane, initialOf, initialStates, laneReport, laneTerminalReached, originOf, parseEventsJsonl, parseHistoryJson, parseStatusJson, phaseStandings, statesOf, stepTask, timeline, trippedTasks, waitingOn, walkedEdgeKey, walkedEdges };
757
+ //# sourceMappingURL=chunk-5KJKJOFC.js.map
758
+ //# sourceMappingURL=chunk-5KJKJOFC.js.map