@glubean/runner 0.4.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,267 @@
1
+ /**
2
+ * @module workflow/execute
3
+ *
4
+ * vNext `workflow` EXECUTOR — the per-node runtime harness.
5
+ *
6
+ * GOVERNING PRINCIPLE (2026-06-09): no backward compatibility. Where `runFlow`
7
+ * (`contract-core.ts`) and the `test()` harness (`packages/runner`) diverge, this
8
+ * executor picks the ONE rule decided in the proposal §17 self-consistency corpus
9
+ * and every node obeys it. See `internal/40-discovery/proposals/contract-workflow-vnext.md`.
10
+ *
11
+ * This slice (S2.0) builds the foundation every later node type stands on:
12
+ * - a per-node derived child ctx (`makeNodeScope`) that attributes evidence to
13
+ * the node, tracks its pass/fail + structured-evidence emission, and — after
14
+ * the node settles — QUARANTINES any late evidence the body still emits (§17 #12);
15
+ * - `runNode`: brackets the node with `workflow:node_start` / `:node_end`
16
+ * events, runs the body under a per-node `AbortController`, and enforces a
17
+ * TERMINAL per-node timeout (no retry; §17 #4) that aborts the node (§17 #12).
18
+ *
19
+ * On top of it: `runWorkflow` walks setup → nodes → teardown (S2.1); `runBranch`
20
+ * runs only the taken side (S2.4a, §17 #6); `runPollNode` is the bounded
21
+ * poll-until with per-attempt quarantine (S2.4b, §17 #3). Control-flow nodes
22
+ * (branch/poll) own their bracket/bounds and are dispatched by `runNodeList`,
23
+ * not `runNode`; remaining reserved kinds throw "not implemented yet".
24
+ */
25
+ import type { TestContext, ProjectionGrade, StaticGrade, Workflow, WorkflowContext, WorkflowNode, ExtractedPredicate } from "@glubean/sdk";
26
+ /**
27
+ * Human-readable label for a declarative expect item (phase4 §7.2) — derived
28
+ * from the extracted predicate, never authored, so it cannot drift from what
29
+ * actually evaluates. Used for assertion-event messages.
30
+ */
31
+ export declare function predicateLabel(p: ExtractedPredicate): string;
32
+ /**
33
+ * Host integration point (used by @glubean/runner's http hooks): the ctx that
34
+ * observability emitted DURING a workflow node body should be attributed to,
35
+ * or undefined outside any workflow node.
36
+ */
37
+ export declare function __activeWorkflowNodeCtx(): TestContext | undefined;
38
+ /**
39
+ * Per-node boundary events ride the generic `ctx.event` channel for this slice
40
+ * (zero runner-package change — they reach the timeline as namespaced
41
+ * `GlubeanEvent`s). S2.5 decides whether to promote them to first-class
42
+ * `node_start` / `node_end` event kinds for richer Cloud rendering.
43
+ */
44
+ export declare const NODE_START_EVENT = "workflow:node_start";
45
+ export declare const NODE_END_EVENT = "workflow:node_end";
46
+ export type NodeStatus = "passed" | "failed" | "skipped";
47
+ export interface NodeStartEventData {
48
+ nodeId: string;
49
+ kind: WorkflowNode["kind"];
50
+ name: string;
51
+ /** Set on a retrying node (§17 #7): which attempt this bracket is (1-based). */
52
+ attempt?: number;
53
+ /** Set on a retrying node (§17 #7): the configured total attempts. */
54
+ attempts?: number;
55
+ [k: string]: unknown;
56
+ }
57
+ export interface NodeEndEventData {
58
+ nodeId: string;
59
+ kind: WorkflowNode["kind"];
60
+ name: string;
61
+ status: NodeStatus;
62
+ /** Runtime grade after evidence promotion (§17 #10). */
63
+ grade: ProjectionGrade;
64
+ durationMs: number;
65
+ error?: string;
66
+ /** Set on a retrying node (§17 #7): which attempt this bracket is (1-based). */
67
+ attempt?: number;
68
+ /** Set on a retrying node (§17 #7): the configured total attempts. */
69
+ attempts?: number;
70
+ [k: string]: unknown;
71
+ }
72
+ /** A node exceeded its `timeout`. TERMINAL — the node is not retried (§17 #4). */
73
+ export declare class NodeTimeoutError extends Error {
74
+ readonly nodeId: string;
75
+ readonly timeoutMs: number;
76
+ constructor(nodeId: string, timeoutMs: number);
77
+ }
78
+ /**
79
+ * A workflow phase (setup, or a node) FAILED via soft assertions/validation —
80
+ * nothing was thrown, so `runNode` has no `error` to report. The executor
81
+ * synthesizes this as the run's `cause` so teardown + the result carry a
82
+ * non-empty failure even for the pure soft-failure path (codex S2.1 P2).
83
+ */
84
+ export declare class WorkflowPhaseFailedError extends Error {
85
+ readonly workflowId: string;
86
+ readonly phase: string;
87
+ constructor(workflowId: string, phase: string);
88
+ }
89
+ /**
90
+ * A `compute` node returned a thenable — an async fn, or (the case the builder
91
+ * can't catch) a sync fn that returns a Promise. compute MUST be synchronous and
92
+ * I/O-free; the executor FAILS the node rather than committing the Promise as
93
+ * state, so async/I/O can't masquerade as a `full` pure transform (§17 #11).
94
+ */
95
+ export declare class ComputeAsyncError extends Error {
96
+ readonly nodeId: string;
97
+ constructor(nodeId: string);
98
+ }
99
+ /**
100
+ * A per-node child `TestContext` (+ `signal`). Sibling to `quarantinedCtx`
101
+ * (`contract-flow-poll.ts`): same "derive with `Object.create`, override the
102
+ * pass/fail APIs, pass observability through" technique, different policy —
103
+ * evidence emits IMMEDIATELY while the node is live, then is DROPPED once the
104
+ * node settles (`seal()`), so a body that ignores `signal` and keeps running
105
+ * after a timeout/failure cannot leak late evidence into the run (§17 #12).
106
+ *
107
+ * It also records, for the executor: whether any assertion/validate/fail FAILED
108
+ * (the node's pass/fail decision, §17 #5) and whether the node emitted ≥1 piece
109
+ * of STRUCTURED evidence — protocol `trace`, `assertion`, `schema_validation`,
110
+ * `metric` — which promotes a statically-`opaque` node to `trace` (§17 #10).
111
+ * Plain `log` / `warn` / `action` / `event` do NOT count as structured evidence.
112
+ */
113
+ export interface NodeScope {
114
+ readonly ctx: WorkflowContext;
115
+ /** True iff some assertion / error-severity validation / `fail()` recorded a failure (§17 #5). */
116
+ hasFailure(): boolean;
117
+ /** True iff the node emitted ≥1 structured evidence (drives opaque→trace, §17 #10). */
118
+ emittedStructuredEvidence(): boolean;
119
+ /** Settle the node: subsequent emissions are quarantined (dropped) (§17 #12). */
120
+ seal(): void;
121
+ }
122
+ export declare function makeNodeScope(base: TestContext, signal: AbortSignal): NodeScope;
123
+ /**
124
+ * Runtime grade = the static floor, promoted from `opaque` → `trace` iff the node
125
+ * emitted structured evidence at run time (§17 #10). `full` / `partial` are never
126
+ * promoted (they are already as good as or better than `trace`).
127
+ */
128
+ export declare function promoteGrade(staticGrade: StaticGrade, scope: NodeScope): ProjectionGrade;
129
+ export interface NodeRunResult {
130
+ status: NodeStatus;
131
+ /** Committed state. On failure this is the PRIOR state — a failed node does not
132
+ * commit its return (§17 #13). On success: the body's return, or the prior state
133
+ * when the body returned void/undefined (§17 #2).
134
+ *
135
+ * §17 #14: state is treated as IMMUTABLE, but the executor does NOT clone it
136
+ * (perf). A body that mutates the LIVE state object in place takes effect
137
+ * IMMEDIATELY and is NOT isolated or rolled back on failure/timeout — commit-on-
138
+ * success governs the RETURN VALUE, not in-place writes. A node needing rollback
139
+ * safety MUST return a new object instead of mutating in place. */
140
+ state: unknown;
141
+ /** Runtime grade after evidence promotion (§17 #10). */
142
+ grade: ProjectionGrade;
143
+ /** The error that failed the node (throw / timeout / fatal), if any. */
144
+ error?: unknown;
145
+ }
146
+ /** Attempt label for a retrying node's events (§17 #7): `n` of `of`. */
147
+ export interface AttemptInfo {
148
+ n: number;
149
+ of: number;
150
+ }
151
+ export interface RunNodeOptions {
152
+ /** Static floor grade for this node (from the projector); promoted per §17 #10. */
153
+ staticGrade: StaticGrade;
154
+ /** Per-node terminal timeout (ms). Omit / non-finite = unbounded (§17 #4). */
155
+ timeoutMs?: number;
156
+ /** Stamp this run's node_start/node_end events as attempt n of of (§17 #7). */
157
+ attempt?: AttemptInfo;
158
+ /**
159
+ * Called with the settled verdict JUST BEFORE this run's node_end event is
160
+ * emitted — the retry wrapper uses it to flush the terminal attempt's buffered
161
+ * assert/validate evidence INSIDE the attempt bracket, so timeline grouping
162
+ * holds (codex S2.4c R2 P2). Runs after the scope is sealed; anything it emits
163
+ * goes straight to the base ctx.
164
+ */
165
+ beforeNodeEnd?: (status: NodeStatus, error?: unknown) => void;
166
+ /**
167
+ * Adjust the runtime grade before it is reported — applied to BOTH the
168
+ * node_end event and the returned result, so they always agree. The retry
169
+ * wrapper uses it to tie opaque→trace promotion to HOST-VISIBLE evidence
170
+ * across attempts: the scope's structured flag cannot distinguish
171
+ * pass-through traces from buffered (possibly-discarded) assert counts, and
172
+ * an earlier attempt's promotion must survive into the terminal bracket
173
+ * (§17 #10; codex S2.5 R5 P2).
174
+ */
175
+ adjustGrade?: (computed: ProjectionGrade, info: {
176
+ structured: boolean;
177
+ status: NodeStatus;
178
+ error?: unknown;
179
+ }) => ProjectionGrade;
180
+ }
181
+ /**
182
+ * Run ONE node: bracket it with node_start/node_end, execute its body under a
183
+ * per-node child ctx + `AbortController`, enforce a TERMINAL timeout (§17 #4),
184
+ * and quarantine any late evidence after the node settles (§17 #12). Returns the
185
+ * verdict + the state to carry forward (commit-on-success, §17 #13).
186
+ */
187
+ export declare function runNode(base: TestContext, node: WorkflowNode, state: unknown, opts: RunNodeOptions): Promise<NodeRunResult>;
188
+ export interface WorkflowNodeOutcome {
189
+ id: string;
190
+ status: NodeStatus;
191
+ /** Runtime grade after evidence promotion (§17 #10). */
192
+ grade: ProjectionGrade;
193
+ }
194
+ export interface WorkflowRunResult {
195
+ /** `skipped` if the workflow opts out (meta.skip) or a node called `ctx.skip()`;
196
+ * `failed` if setup threw / soft-failed or a node failed; else `passed`. */
197
+ status: "passed" | "failed" | "skipped";
198
+ /** Last COMMITTED state — passed nodes commit, failed/skipped do not (§17 #13). */
199
+ state: unknown;
200
+ /** Per-node outcomes, authored order. */
201
+ nodes: WorkflowNodeOutcome[];
202
+ /** The primary cause that failed the run (the one teardown sees), if any. */
203
+ error?: unknown;
204
+ /** Why the run skipped: the user-authored ctx.skip(reason) (setup/node/
205
+ * branch-predicate/poll), or `meta.skip`. Only set when status === "skipped"
206
+ * (codex S2.5 R6 — the host's skipped status must keep the authored reason). */
207
+ skipReason?: string;
208
+ }
209
+ /** Branch-family decision event (§17 #9 "branch decision") — rides the generic
210
+ * ctx.event channel like node_start/node_end; the runner unwraps it into a
211
+ * first-class `branch_decision` timeline event. */
212
+ export declare const BRANCH_DECISION_EVENT = "workflow:branch_decision";
213
+ export interface BranchDecisionEventData {
214
+ nodeId: string;
215
+ mode: "predicate" | "value";
216
+ takenIndex: number | "default";
217
+ takenLabel?: string;
218
+ [k: string]: unknown;
219
+ }
220
+ /** Per-attempt timeline event (§17 #9 "poll attempt timeline"). Rides the generic
221
+ * `ctx.event` channel like node_start/node_end; emitted through the NODE scope, so
222
+ * it is observability (never promotes the grade) and is quarantined after seal. */
223
+ export declare const POLL_ATTEMPT_EVENT = "workflow:poll_attempt";
224
+ export interface PollAttemptEventData {
225
+ nodeId: string;
226
+ attempt: number;
227
+ /** `satisfied` — until held; `probe` — until not yet; `failed` — the attempt
228
+ * threw / exceeded its budget (the poll node fails). */
229
+ outcome: "satisfied" | "probe" | "failed";
230
+ durationMs: number;
231
+ /**
232
+ * Inbound polls only (design §9.4): the matcher's classification for this
233
+ * attempt — `matched`, the probe attributions (`no-delivery` /
234
+ * `type-mismatch` / `correlation-mismatch` / `schema-mismatch`), or the
235
+ * fail class that ended the poll. Diagnosis, never verdict.
236
+ */
237
+ classification?: string;
238
+ /**
239
+ * Inbound polls, satisfied attempts only: `receivedAt − pollStart` of the
240
+ * matched delivery (ms) — the measured side of `expect.within` (§9.4a #5).
241
+ * NEGATIVE when the delivery arrived before the poll started (allowed
242
+ * evidence, §9.4a #3).
243
+ */
244
+ withinDeltaMs?: number;
245
+ [k: string]: unknown;
246
+ }
247
+ /**
248
+ * A fail-class inbound classification (inbound-contract-design §9.4 rows 1–3):
249
+ * a property of the delivery CHANNEL (forgery / replay / non-JSON), never
250
+ * noise — it fails the poll node even when a later delivery in the same
251
+ * snapshot would have matched (§9.4a #4, first terminal wins).
252
+ */
253
+ export declare class InboundDeliveryFailError extends Error {
254
+ readonly classification: "signature-invalid" | "stale" | "unparseable";
255
+ constructor(classification: "signature-invalid" | "stale" | "unparseable", nodeId: string, detail?: string);
256
+ }
257
+ /**
258
+ * Execute a built `Workflow` against a host `TestContext`. setup runs INSIDE the
259
+ * protected region so teardown ALWAYS runs — even when setup throws (§17 #1, the
260
+ * opposite of runFlow). Nodes run in authored order with fail-stop: a non-passed
261
+ * node stops the rest (emitted `skipped`). A node `ctx.skip()` skips the whole
262
+ * workflow (mirrors the runner's whole-test skip); `meta.skip` opts the workflow
263
+ * out entirely (discoverable, never executed). teardown sees the last committed
264
+ * state + the failing cause; a teardown that throws is logged and never masks it.
265
+ */
266
+ export declare function runWorkflow<State = unknown>(wf: Workflow<State>, baseCtx: TestContext): Promise<WorkflowRunResult>;
267
+ //# sourceMappingURL=execute.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../src/workflow/execute.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AASH,OAAO,KAAK,EAMV,WAAW,EAaX,eAAe,EAEf,WAAW,EACX,QAAQ,EACR,eAAe,EACf,YAAY,EAEZ,kBAAkB,EAEnB,MAAM,cAAc,CAAC;AAoBtB;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAuB5D;AAoBD;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,WAAW,GAAG,SAAS,CAEjE;AAWD;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,wBAAwB,CAAC;AACtD,eAAO,MAAM,cAAc,sBAAsB,CAAC;AAElD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEzD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;IACnB,wDAAwD;IACxD,KAAK,EAAE,eAAe,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAMD,kFAAkF;AAClF,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBACf,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CAM9C;AAED;;;;;GAKG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBACX,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAM9C;AAED;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBACZ,MAAM,EAAE,MAAM;CAQ3B;AAMD;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAC;IAC9B,kGAAkG;IAClG,UAAU,IAAI,OAAO,CAAC;IACtB,uFAAuF;IACvF,yBAAyB,IAAI,OAAO,CAAC;IACrC,iFAAiF;IACjF,IAAI,IAAI,IAAI,CAAC;CACd;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,SAAS,CAkJ/E;AAMD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,GAAG,eAAe,CAIxF;AAMD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,UAAU,CAAC;IACnB;;;;;;;;uEAQmE;IACnE,KAAK,EAAE,OAAO,CAAC;IACf,wDAAwD;IACxD,KAAK,EAAE,eAAe,CAAC;IACvB,wEAAwE;IACxE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wEAAwE;AACxE,MAAM,WAAW,WAAW;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,cAAc;IAC7B,mFAAmF;IACnF,WAAW,EAAE,WAAW,CAAC;IACzB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9D;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,CACZ,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,UAAU,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,KAC/D,eAAe,CAAC;CACtB;AAmOD;;;;;GAKG;AACH,wBAAsB,OAAO,CAC3B,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,aAAa,CAAC,CA8FxB;AAMD,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,UAAU,CAAC;IACnB,wDAAwD;IACxD,KAAK,EAAE,eAAe,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC;gFAC4E;IAC5E,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IACxC,mFAAmF;IACnF,KAAK,EAAE,OAAO,CAAC;IACf,yCAAyC;IACzC,KAAK,EAAE,mBAAmB,EAAE,CAAC;IAC7B,6EAA6E;IAC7E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;oFAEgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAkSD;;mDAEmD;AACnD,eAAO,MAAM,qBAAqB,6BAA6B,CAAC;AAEhE,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AA8HD;;mFAEmF;AACnF,eAAO,MAAM,kBAAkB,0BAA0B,CAAC;AAE1D,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB;4DACwD;IACxD,OAAO,EAAE,WAAW,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAsBD;;;;;GAKG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IAE/C,QAAQ,CAAC,cAAc,EAAE,mBAAmB,GAAG,OAAO,GAAG,aAAa;gBAA7D,cAAc,EAAE,mBAAmB,GAAG,OAAO,GAAG,aAAa,EACtE,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM;CASlB;AAmeD;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,KAAK,GAAG,OAAO,EAC/C,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,EACnB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,iBAAiB,CAAC,CAmI5B"}