@demlik/tea 0.1.2 → 0.3.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.
- package/README.md +49 -0
- package/dist/agent/index.d.ts +16 -770
- package/dist/agent/index.js +18 -16
- package/dist/authed-call/index.d.ts +3 -3
- package/dist/authed-call/index.js +22 -36
- package/dist/authed-call/index.js.map +1 -1
- package/dist/await-terminal/index.d.ts +2 -2
- package/dist/await-terminal/index.js +2 -2
- package/dist/batch-window/index.d.ts +3 -3
- package/dist/batch-window/index.js +3 -3
- package/dist/cache/index.d.ts +2 -2
- package/dist/cache/index.js +5 -2
- package/dist/{chunk-3545BOYL.js → chunk-25LBDCBC.js} +6 -5
- package/dist/chunk-25LBDCBC.js.map +1 -0
- package/dist/{chunk-IN33Q4VJ.js → chunk-2KNLKQVN.js} +3 -3
- package/dist/{chunk-IN33Q4VJ.js.map → chunk-2KNLKQVN.js.map} +1 -1
- package/dist/{chunk-MMH7CW5K.js → chunk-BP2R7TPP.js} +216 -157
- package/dist/chunk-BP2R7TPP.js.map +1 -0
- package/dist/chunk-C6QC273X.js +71 -0
- package/dist/chunk-C6QC273X.js.map +1 -0
- package/dist/{chunk-7W4LMPUW.js → chunk-CAXMQJJ3.js} +17 -24
- package/dist/chunk-CAXMQJJ3.js.map +1 -0
- package/dist/{chunk-UV6UWGEO.js → chunk-D736QUGF.js} +6 -5
- package/dist/chunk-D736QUGF.js.map +1 -0
- package/dist/chunk-EWJP3YYP.js +21 -0
- package/dist/chunk-EWJP3YYP.js.map +1 -0
- package/dist/{chunk-TGG26XMK.js → chunk-FV3Z5MBE.js} +10 -5
- package/dist/chunk-FV3Z5MBE.js.map +1 -0
- package/dist/chunk-G5E74IJB.js +8 -0
- package/dist/chunk-G5E74IJB.js.map +1 -0
- package/dist/{chunk-27N2SPTN.js → chunk-IXBKFQH5.js} +12 -11
- package/dist/chunk-IXBKFQH5.js.map +1 -0
- package/dist/{chunk-SQTCS44W.js → chunk-JFFVBEDI.js} +3 -3
- package/dist/{chunk-SQTCS44W.js.map → chunk-JFFVBEDI.js.map} +1 -1
- package/dist/chunk-LYLDCIPH.js +365 -0
- package/dist/chunk-LYLDCIPH.js.map +1 -0
- package/dist/{chunk-HJO6VNQI.js → chunk-MU6MCIAC.js} +3 -3
- package/dist/{chunk-HJO6VNQI.js.map → chunk-MU6MCIAC.js.map} +1 -1
- package/dist/{chunk-UYWUVEP6.js → chunk-MUUROGIH.js} +2 -2
- package/dist/chunk-MUUROGIH.js.map +1 -0
- package/dist/{chunk-EHIX7NYZ.js → chunk-MVY5DAMF.js} +4 -4
- package/dist/{chunk-EHIX7NYZ.js.map → chunk-MVY5DAMF.js.map} +1 -1
- package/dist/{chunk-LP7VQBRG.js → chunk-NPERUUUE.js} +76 -95
- package/dist/chunk-NPERUUUE.js.map +1 -0
- package/dist/{chunk-QLA76GIT.js → chunk-PBDYW2TU.js} +68 -9
- package/dist/chunk-PBDYW2TU.js.map +1 -0
- package/dist/{chunk-5VNWZADP.js → chunk-Q2UM7BXU.js} +18 -7
- package/dist/chunk-Q2UM7BXU.js.map +1 -0
- package/dist/chunk-RMX4EOQK.js +26 -0
- package/dist/chunk-RMX4EOQK.js.map +1 -0
- package/dist/{chunk-2YOE5PIS.js → chunk-S2EGRPCM.js} +8 -6
- package/dist/chunk-S2EGRPCM.js.map +1 -0
- package/dist/{chunk-ON24RKSE.js → chunk-S4O26D7B.js} +7 -14
- package/dist/chunk-S4O26D7B.js.map +1 -0
- package/dist/{chunk-3XJWC7VW.js → chunk-SK5JGUY7.js} +3 -3
- package/dist/{chunk-3XJWC7VW.js.map → chunk-SK5JGUY7.js.map} +1 -1
- package/dist/{chunk-5DEQ5I3U.js → chunk-TPGASTFF.js} +8 -10
- package/dist/chunk-TPGASTFF.js.map +1 -0
- package/dist/{chunk-SC3FAFZN.js → chunk-UF4MZSLT.js} +2 -2
- package/dist/chunk-UF4MZSLT.js.map +1 -0
- package/dist/{chunk-PS23H6KQ.js → chunk-VBUCHVCV.js} +6 -5
- package/dist/chunk-VBUCHVCV.js.map +1 -0
- package/dist/{chunk-S3FQN4SL.js → chunk-WGGAMW7Y.js} +191 -178
- package/dist/chunk-WGGAMW7Y.js.map +1 -0
- package/dist/chunk-ZJ5AOYBH.js +8 -0
- package/dist/chunk-ZJ5AOYBH.js.map +1 -0
- package/dist/{chunk-LEC4U6CX.js → chunk-ZVJZ72HY.js} +3 -3
- package/dist/{chunk-LEC4U6CX.js.map → chunk-ZVJZ72HY.js.map} +1 -1
- package/dist/circuit-breaker/index.d.ts +1 -0
- package/dist/circuit-breaker/index.js +1 -1
- package/dist/{core-BoB6t7dB.d.ts → core-42kapW0n.d.ts} +105 -6
- package/dist/deadline/index.d.ts +77 -11
- package/dist/deadline/index.js +3 -3
- package/dist/devtools/index.js +8 -28
- package/dist/devtools/index.js.map +1 -1
- package/dist/do/index.d.ts +483 -339
- package/dist/do/index.js +244 -214
- package/dist/do/index.js.map +1 -1
- package/dist/{durable-effects-CO9Uh971.d.ts → durable-effects-CFRngTNA.d.ts} +3 -3
- package/dist/extension/index.d.ts +25 -7
- package/dist/extension/index.js +4 -3
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/react.js +4 -3
- package/dist/extension/react.js.map +1 -1
- package/dist/extension/subs/index.d.ts +2 -2
- package/dist/extension/subs/index.js +7 -10
- package/dist/extension/subs/index.js.map +1 -1
- package/dist/fan-out/index.d.ts +10 -10
- package/dist/fan-out/index.js +3 -3
- package/dist/idempotent-intake/index.d.ts +2 -2
- package/dist/idempotent-intake/index.js +3 -3
- package/dist/index.d.ts +50 -472
- package/dist/index.js +2 -2
- package/dist/llm-call/index.d.ts +3 -2
- package/dist/llm-call/index.js +12 -10
- package/dist/machine-B_BF4jij.d.ts +804 -0
- package/dist/machine-viz/index.d.ts +1 -1
- package/dist/machine-viz/index.js +2 -2
- package/dist/mem/index.d.ts +3 -2
- package/dist/mem/index.js.map +1 -1
- package/dist/monitored-run/index.d.ts +54 -30
- package/dist/monitored-run/index.js +7 -7
- package/dist/node/index.d.ts +3 -2
- package/dist/node/index.js +2 -3
- package/dist/node/index.js.map +1 -1
- package/dist/paginated-walk/index.d.ts +2 -2
- package/dist/paginated-walk/index.js +11 -10
- package/dist/parity/index.d.ts +6 -5
- package/dist/parity/index.js +5 -4
- package/dist/parity/index.js.map +1 -1
- package/dist/pbt/arbitraries/index.d.ts +1 -1
- package/dist/pbt/index.d.ts +1 -1
- package/dist/pbt/index.js +3 -3
- package/dist/pbt/index.js.map +1 -1
- package/dist/pbt/runners/index.d.ts +1 -1
- package/dist/pbt/runners/index.js +3 -3
- package/dist/poller/index.d.ts +59 -17
- package/dist/poller/index.js +56 -20
- package/dist/poller/index.js.map +1 -1
- package/dist/prediction/index.d.ts +1 -1
- package/dist/prediction/index.js +2 -2
- package/dist/pure/index.d.ts +1 -1
- package/dist/pure/index.js +2 -2
- package/dist/rate-limit/index.d.ts +2 -2
- package/dist/rate-limit/index.js +1 -1
- package/dist/react/index.d.ts +3 -2
- package/dist/react/index.js +2 -2
- package/dist/react/index.js.map +1 -1
- package/dist/reconciler/index.d.ts +2 -2
- package/dist/reconciler/index.js +13 -15
- package/dist/reconciler/index.js.map +1 -1
- package/dist/recorder/index.d.ts +20 -20
- package/dist/recorder/index.js +1 -1
- package/dist/resilient-call/index.d.ts +31 -6
- package/dist/resilient-call/index.js +10 -9
- package/dist/retry-backoff/index.d.ts +191 -14
- package/dist/retry-backoff/index.js +1 -1
- package/dist/retry-to-success/index.js +1 -1
- package/dist/runtime-types-gsog27b6.d.ts +337 -0
- package/dist/saga/index.d.ts +3 -2
- package/dist/saga/index.js +2 -2
- package/dist/saga/index.js.map +1 -1
- package/dist/snapshot/index.d.ts +1 -1
- package/dist/snapshot/index.js +3 -3
- package/dist/subs/index.d.ts +372 -4
- package/dist/subs/index.js +4 -1
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +2 -2
- package/dist/throttled-input/index.d.ts +2 -2
- package/dist/throttled-input/index.js +14 -13
- package/dist/throttled-input/index.js.map +1 -1
- package/dist/token-refresh/index.d.ts +1 -1
- package/dist/token-refresh/index.js +3 -3
- package/dist/trace-replay/index.d.ts +12 -7
- package/dist/trace-replay/index.js +4 -3
- package/dist/{types-B-8bcAp8.d.ts → types-KbL3W1mj.d.ts} +1 -1
- package/dist/with-deadline/index.d.ts +1 -1
- package/dist/with-deadline/index.js +2 -2
- package/dist/with-resilience/index.d.ts +21 -2
- package/dist/with-resilience/index.js +12 -23
- package/dist/with-resilience/index.js.map +1 -1
- package/dist/with-telemetry/index.d.ts +1 -1
- package/dist/with-telemetry/index.js +2 -2
- package/dist/work-queue/adapter.d.ts +2 -2
- package/dist/work-queue/adapter.js +2 -2
- package/dist/work-queue/index.d.ts +3 -2
- package/dist/work-queue/index.js +1 -1
- package/dist/work-queue/index.js.map +1 -1
- package/dist/work-queue/ops.d.ts +2 -2
- package/dist/work-queue/ops.js +1 -1
- package/dist/workflow/index.d.ts +51 -19
- package/dist/workflow/index.js +71 -52
- package/dist/workflow/index.js.map +1 -1
- package/package.json +4 -19
- package/dist/chunk-27N2SPTN.js.map +0 -1
- package/dist/chunk-2YOE5PIS.js.map +0 -1
- package/dist/chunk-3545BOYL.js.map +0 -1
- package/dist/chunk-5DEQ5I3U.js.map +0 -1
- package/dist/chunk-5VNWZADP.js.map +0 -1
- package/dist/chunk-6RGBKODV.js +0 -268
- package/dist/chunk-6RGBKODV.js.map +0 -1
- package/dist/chunk-7W4LMPUW.js.map +0 -1
- package/dist/chunk-DILMKOZ7.js +0 -91
- package/dist/chunk-DILMKOZ7.js.map +0 -1
- package/dist/chunk-K7Q23EQ4.js +0 -20
- package/dist/chunk-K7Q23EQ4.js.map +0 -1
- package/dist/chunk-LP7VQBRG.js.map +0 -1
- package/dist/chunk-MMH7CW5K.js.map +0 -1
- package/dist/chunk-NSDFA6M6.js +0 -198
- package/dist/chunk-NSDFA6M6.js.map +0 -1
- package/dist/chunk-ON24RKSE.js.map +0 -1
- package/dist/chunk-PS23H6KQ.js.map +0 -1
- package/dist/chunk-QLA76GIT.js.map +0 -1
- package/dist/chunk-S3FQN4SL.js.map +0 -1
- package/dist/chunk-SC3FAFZN.js.map +0 -1
- package/dist/chunk-TGG26XMK.js.map +0 -1
- package/dist/chunk-UV6UWGEO.js.map +0 -1
- package/dist/chunk-UYWUVEP6.js.map +0 -1
- package/dist/chunk-VLGARN6H.js +0 -202
- package/dist/chunk-VLGARN6H.js.map +0 -1
- package/dist/raft/demo.d.ts +0 -105
- package/dist/raft/demo.js +0 -172
- package/dist/raft/demo.js.map +0 -1
- package/dist/raft/index.d.ts +0 -548
- package/dist/raft/index.js +0 -7
- package/dist/raft/index.js.map +0 -1
- package/dist/raft/sim.d.ts +0 -222
- package/dist/raft/sim.js +0 -8
- package/dist/raft/sim.js.map +0 -1
- package/dist/raft/timeline.d.ts +0 -46
- package/dist/raft/timeline.js +0 -62
- package/dist/raft/timeline.js.map +0 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Store } from '../
|
|
2
|
-
import '../core-
|
|
1
|
+
import { S as Store } from '../runtime-types-gsog27b6.js';
|
|
2
|
+
import '../core-42kapW0n.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
+
* @packageDocumentation
|
|
5
6
|
* @demlik/tea/work-queue — substrate-agnostic work-queue lifecycle on `Store<S>`.
|
|
6
7
|
*
|
|
7
8
|
* Two public surfaces, one shape:
|
package/dist/work-queue/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { enqueueOp, claimNextOp, markDoneOp, patchItemOp, resetRunningOp, removeOp } from '../chunk-
|
|
1
|
+
import { enqueueOp, claimNextOp, markDoneOp, patchItemOp, resetRunningOp, removeOp } from '../chunk-FV3Z5MBE.js';
|
|
2
2
|
|
|
3
3
|
// src/work-queue/index.ts
|
|
4
4
|
function genId() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/work-queue/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @demlik/tea/work-queue — substrate-agnostic work-queue lifecycle on `Store<S>`.\n *\n * Two public surfaces, one shape:\n * - `createQueue(store)` (this file) is the in-process adapter — it binds the\n * pure ops to an injected `Store` and runs `load → mutate → save`.\n * - `@demlik/tea/work-queue/ops` (`./ops`) is the BLESSED, importable surface\n * of those same pure ops. L2 compositions that own their own Model slice\n * (`monitored-run`, `idempotent-intake`, …) import the ops directly and\n * delegate status flips to them instead of re-rolling the transitions.\n * Both routes call the identical ops, so the lifecycle rules live in exactly\n * one place. This mirrors `@demlik/tea/do`'s one-shape-per-package rule.\n *\n * The package is generic in the input payload `I` (audit-specific fields go\n * here in apps/extension). `output` is a free-form side-channel correlation\n * token (a backend id, URL, trace id) — typed as `string` because that's its\n * only honest type. Consumers that need structured per-item data persist it in\n * their own store and put the foreign-key string in `output`.\n */\n\nimport type { Store } from \"../index\";\nimport {\n claimNextOp,\n enqueueOp,\n markDoneOp,\n patchItemOp,\n removeOp,\n resetRunningOp,\n} from \"./ops\";\n\n/**\n * Generate a queue-item id at the adapter boundary. Reads `Date.now()` and\n * `Math.random()` — impure helpers belong in the adapter (`index.ts`),\n * never in `ops.ts` (which is purely-functional by contract). Uses native\n * UUID when available, falls back to timestamp + random for environments\n * without `crypto.randomUUID`.\n */\nfunction genId(): string {\n return (\n globalThis.crypto?.randomUUID?.() ??\n `q_${Date.now()}_${Math.random().toString(36).slice(2)}`\n );\n}\n\nexport type QueueItemStatus =\n | \"pending\"\n | \"running\"\n | \"done\"\n | \"failed\"\n | \"cancelled\";\n\n/**\n * A single queue entry. Every field is `readonly`: a persisted slice is\n * immutable, so a transition returns a NEW item (the ops always spread —\n * `{ ...item, status: … }`) rather than mutating in place. This type-enforces\n * the durability invariant the consumers already honor by convention, and\n * keeps a slice round-trippable through JSON unchanged across a reload.\n */\nexport interface QueueItem<I> {\n readonly id: string;\n readonly status: QueueItemStatus;\n readonly input: I;\n readonly enqueuedAt: number;\n readonly startedAt?: number;\n readonly finishedAt?: number;\n readonly output?: string;\n readonly error?: string;\n}\n\n/**\n * Caller-facing enqueue payload. Just `I` today — kept named so future\n * variants (priority, deferUntil, …) can grow without breaking callers.\n */\nexport type EnqueueInput<I> = I;\n\n/**\n * Adapter that binds the pure ops to an injected `Store<QueueItem<I>[]>`.\n *\n * `load → mutate → save` is the only durability discipline this package\n * imposes. The store decides what \"save\" means (chrome.storage, KV, DO\n * storage, in-memory) — that decision belongs at the caller.\n *\n * Ops that don't change the queue (e.g. `markDone` on an unknown id) skip the\n * save call so the underlying store doesn't observe a write churn.\n */\nexport function createQueue<I>(store: Store<QueueItem<I>[]>) {\n // `Store<S>.load()` returns `unknown` (invariant 8 boundary). Route\n // through `store.migrate(raw)` to get the typed value before any\n // queue operation reads it — same wiring the substrate's `run()` uses at\n // boot. The work queue is a substrate-agnostic primitive on top of\n // `Store<S>`, so it honors the same contract.\n async function loadParsed(): Promise<QueueItem<I>[] | null> {\n const raw = await store.load();\n return store.migrate(raw);\n }\n\n async function list(): Promise<QueueItem<I>[]> {\n return (await loadParsed()) ?? [];\n }\n\n async function enqueue(input: EnqueueInput<I>): Promise<QueueItem<I>> {\n const queue = await list();\n const { next, item } = enqueueOp<I>(queue, input, Date.now(), genId());\n await store.save(next);\n return item;\n }\n\n async function claimNext(): Promise<QueueItem<I> | null> {\n const queue = await list();\n const result = claimNextOp<I>(queue, Date.now());\n if (!result) return null;\n await store.save(result.next);\n return result.claimed;\n }\n\n // `markDone` ignores the optional second arg by design: completed runs leave\n // the queue, and the consumer is expected to persist any output (auditId,\n // etc.) wherever those completed records belong. Kept in the signature for\n // call-site readability and forward-compat with `bindOutput`.\n async function markDone(id: string): Promise<void> {\n const queue = await list();\n const { next, changed } = markDoneOp<I>(queue, id);\n if (changed) await store.save(next);\n }\n\n async function markFailed(id: string, error: string): Promise<void> {\n const queue = await list();\n const { next, changed } = patchItemOp<I>(queue, id, (item) => ({\n ...item,\n status: \"failed\",\n finishedAt: Date.now(),\n error,\n }));\n if (changed) await store.save(next);\n }\n\n async function markCancelled(id: string): Promise<void> {\n const queue = await list();\n const { next, changed } = patchItemOp<I>(queue, id, (item) => ({\n ...item,\n status: \"cancelled\",\n finishedAt: Date.now(),\n }));\n if (changed) await store.save(next);\n }\n\n async function resetRunningToPending(): Promise<void> {\n const queue = await list();\n const { next, changed } = resetRunningOp<I>(queue);\n if (changed) await store.save(next);\n }\n\n async function removeItem(id: string): Promise<void> {\n const queue = await list();\n const { next, changed } = removeOp<I>(queue, id);\n if (changed) await store.save(next);\n }\n\n async function retryItem(id: string): Promise<void> {\n const queue = await list();\n const { next, changed } = patchItemOp<I>(queue, id, (item) => ({\n ...item,\n status: \"pending\",\n startedAt: undefined,\n finishedAt: undefined,\n error: undefined,\n output: undefined,\n }));\n if (changed) await store.save(next);\n }\n\n // `bindOutput` attaches a side-channel correlation token (string) to a\n // running item without changing its status. The queue can't reach into\n // `I` to mutate it (the substrate is domain-agnostic), so callers that\n // need to thread a backend id / URL / trace id through the lifecycle\n // route it here. Consumers needing structured payloads should persist\n // them in their own store and put the foreign-key string in `output`.\n async function bindOutput(id: string, value: string): Promise<void> {\n const queue = await list();\n const { next, changed } = patchItemOp<I>(queue, id, (item) => ({\n ...item,\n output: value,\n }));\n if (changed) await store.save(next);\n }\n\n return {\n enqueue,\n claimNext,\n markDone,\n markFailed,\n markCancelled,\n resetRunningToPending,\n removeItem,\n retryItem,\n bindOutput,\n list,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/work-queue/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @packageDocumentation\n * @demlik/tea/work-queue — substrate-agnostic work-queue lifecycle on `Store<S>`.\n *\n * Two public surfaces, one shape:\n * - `createQueue(store)` (this file) is the in-process adapter — it binds the\n * pure ops to an injected `Store` and runs `load → mutate → save`.\n * - `@demlik/tea/work-queue/ops` (`./ops`) is the BLESSED, importable surface\n * of those same pure ops. L2 compositions that own their own Model slice\n * (`monitored-run`, `idempotent-intake`, …) import the ops directly and\n * delegate status flips to them instead of re-rolling the transitions.\n * Both routes call the identical ops, so the lifecycle rules live in exactly\n * one place. This mirrors `@demlik/tea/do`'s one-shape-per-package rule.\n *\n * The package is generic in the input payload `I` (audit-specific fields go\n * here in apps/extension). `output` is a free-form side-channel correlation\n * token (a backend id, URL, trace id) — typed as `string` because that's its\n * only honest type. Consumers that need structured per-item data persist it in\n * their own store and put the foreign-key string in `output`.\n */\n\nimport type { Store } from \"../index\";\nimport {\n claimNextOp,\n enqueueOp,\n markDoneOp,\n patchItemOp,\n removeOp,\n resetRunningOp,\n} from \"./ops\";\n\n/**\n * Generate a queue-item id at the adapter boundary. Reads `Date.now()` and\n * `Math.random()` — impure helpers belong in the adapter (`index.ts`),\n * never in `ops.ts` (which is purely-functional by contract). Uses native\n * UUID when available, falls back to timestamp + random for environments\n * without `crypto.randomUUID`.\n */\nfunction genId(): string {\n return (\n globalThis.crypto?.randomUUID?.() ??\n `q_${Date.now()}_${Math.random().toString(36).slice(2)}`\n );\n}\n\nexport type QueueItemStatus =\n | \"pending\"\n | \"running\"\n | \"done\"\n | \"failed\"\n | \"cancelled\";\n\n/**\n * A single queue entry. Every field is `readonly`: a persisted slice is\n * immutable, so a transition returns a NEW item (the ops always spread —\n * `{ ...item, status: … }`) rather than mutating in place. This type-enforces\n * the durability invariant the consumers already honor by convention, and\n * keeps a slice round-trippable through JSON unchanged across a reload.\n */\nexport interface QueueItem<I> {\n readonly id: string;\n readonly status: QueueItemStatus;\n readonly input: I;\n readonly enqueuedAt: number;\n readonly startedAt?: number;\n readonly finishedAt?: number;\n readonly output?: string;\n readonly error?: string;\n}\n\n/**\n * Caller-facing enqueue payload. Just `I` today — kept named so future\n * variants (priority, deferUntil, …) can grow without breaking callers.\n */\nexport type EnqueueInput<I> = I;\n\n/**\n * Adapter that binds the pure ops to an injected `Store<QueueItem<I>[]>`.\n *\n * `load → mutate → save` is the only durability discipline this package\n * imposes. The store decides what \"save\" means (chrome.storage, KV, DO\n * storage, in-memory) — that decision belongs at the caller.\n *\n * Ops that don't change the queue (e.g. `markDone` on an unknown id) skip the\n * save call so the underlying store doesn't observe a write churn.\n */\nexport function createQueue<I>(store: Store<QueueItem<I>[]>) {\n // `Store<S>.load()` returns `unknown` (invariant 8 boundary). Route\n // through `store.migrate(raw)` to get the typed value before any\n // queue operation reads it — same wiring the substrate's `run()` uses at\n // boot. The work queue is a substrate-agnostic primitive on top of\n // `Store<S>`, so it honors the same contract.\n async function loadParsed(): Promise<QueueItem<I>[] | null> {\n const raw = await store.load();\n return store.migrate(raw);\n }\n\n async function list(): Promise<QueueItem<I>[]> {\n return (await loadParsed()) ?? [];\n }\n\n async function enqueue(input: EnqueueInput<I>): Promise<QueueItem<I>> {\n const queue = await list();\n const { next, item } = enqueueOp<I>(queue, input, Date.now(), genId());\n await store.save(next);\n return item;\n }\n\n async function claimNext(): Promise<QueueItem<I> | null> {\n const queue = await list();\n const result = claimNextOp<I>(queue, Date.now());\n if (!result) return null;\n await store.save(result.next);\n return result.claimed;\n }\n\n // `markDone` ignores the optional second arg by design: completed runs leave\n // the queue, and the consumer is expected to persist any output (auditId,\n // etc.) wherever those completed records belong. Kept in the signature for\n // call-site readability and forward-compat with `bindOutput`.\n async function markDone(id: string): Promise<void> {\n const queue = await list();\n const { next, changed } = markDoneOp<I>(queue, id);\n if (changed) await store.save(next);\n }\n\n async function markFailed(id: string, error: string): Promise<void> {\n const queue = await list();\n const { next, changed } = patchItemOp<I>(queue, id, (item) => ({\n ...item,\n status: \"failed\",\n finishedAt: Date.now(),\n error,\n }));\n if (changed) await store.save(next);\n }\n\n async function markCancelled(id: string): Promise<void> {\n const queue = await list();\n const { next, changed } = patchItemOp<I>(queue, id, (item) => ({\n ...item,\n status: \"cancelled\",\n finishedAt: Date.now(),\n }));\n if (changed) await store.save(next);\n }\n\n async function resetRunningToPending(): Promise<void> {\n const queue = await list();\n const { next, changed } = resetRunningOp<I>(queue);\n if (changed) await store.save(next);\n }\n\n async function removeItem(id: string): Promise<void> {\n const queue = await list();\n const { next, changed } = removeOp<I>(queue, id);\n if (changed) await store.save(next);\n }\n\n async function retryItem(id: string): Promise<void> {\n const queue = await list();\n const { next, changed } = patchItemOp<I>(queue, id, (item) => ({\n ...item,\n status: \"pending\",\n startedAt: undefined,\n finishedAt: undefined,\n error: undefined,\n output: undefined,\n }));\n if (changed) await store.save(next);\n }\n\n // `bindOutput` attaches a side-channel correlation token (string) to a\n // running item without changing its status. The queue can't reach into\n // `I` to mutate it (the substrate is domain-agnostic), so callers that\n // need to thread a backend id / URL / trace id through the lifecycle\n // route it here. Consumers needing structured payloads should persist\n // them in their own store and put the foreign-key string in `output`.\n async function bindOutput(id: string, value: string): Promise<void> {\n const queue = await list();\n const { next, changed } = patchItemOp<I>(queue, id, (item) => ({\n ...item,\n output: value,\n }));\n if (changed) await store.save(next);\n }\n\n return {\n enqueue,\n claimNext,\n markDone,\n markFailed,\n markCancelled,\n resetRunningToPending,\n removeItem,\n retryItem,\n bindOutput,\n list,\n };\n}\n"]}
|
package/dist/work-queue/ops.d.ts
CHANGED
package/dist/work-queue/ops.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { claimNextOp, enqueueOp, markDoneOp, patchItemOp, removeOp, resetRunningOp } from '../chunk-
|
|
1
|
+
export { claimNextOp, enqueueOp, markDoneOp, patchItemOp, removeOp, resetRunningOp } from '../chunk-FV3Z5MBE.js';
|
|
2
2
|
//# sourceMappingURL=ops.js.map
|
|
3
3
|
//# sourceMappingURL=ops.js.map
|
package/dist/workflow/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { D as DeliveryId, E as EffectLedgerEvent } from '../durable-effects-
|
|
2
|
-
export { a as EffectConfirmed, b as EffectOwed, e as emptyLedger } from '../durable-effects-
|
|
3
|
-
import { C as Cmd } from '../core-
|
|
1
|
+
import { D as DeliveryId, E as EffectLedgerEvent } from '../durable-effects-CFRngTNA.js';
|
|
2
|
+
export { a as EffectConfirmed, b as EffectOwed, e as emptyLedger } from '../durable-effects-CFRngTNA.js';
|
|
3
|
+
import { C as Cmd } from '../core-42kapW0n.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
+
* @packageDocumentation
|
|
6
7
|
* @demlik/tea/workflow — the durable-workflow runtime core (#124, the first
|
|
7
8
|
* Phase-1 slice of the Temporal-style durable-workflow engine, epic #118).
|
|
8
9
|
*
|
|
@@ -99,6 +100,16 @@ interface WorkflowStep<A> {
|
|
|
99
100
|
*/
|
|
100
101
|
readonly compensation?: A;
|
|
101
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* A workflow's step sequence at construction — a NON-EMPTY tuple. A workflow
|
|
105
|
+
* with nothing to do reaches `completed` only by carrying a final result, and an
|
|
106
|
+
* empty sequence produces none, so "≥ 1 step" is a precondition. Encoding it as
|
|
107
|
+
* `readonly [WorkflowStep<A>, ...WorkflowStep<A>[]]` makes the empty case a
|
|
108
|
+
* COMPILE error at every construction boundary (`init`, `foldWorkflow`,
|
|
109
|
+
* `workflowGrain`) — parse-don't-validate, so the reducer never needs a runtime
|
|
110
|
+
* throw to reject a resultless workflow.
|
|
111
|
+
*/
|
|
112
|
+
type WorkflowSteps<A> = readonly [WorkflowStep<A>, ...WorkflowStep<A>[]];
|
|
102
113
|
/**
|
|
103
114
|
* A completed step: the step that ran plus the result its activity produced.
|
|
104
115
|
* The `running` state accumulates these in execution order, so the full
|
|
@@ -262,6 +273,17 @@ interface CompensationFailedWorkflow<A, R, F> {
|
|
|
262
273
|
* only while the workflow is actively driving one" unrepresentable to violate.
|
|
263
274
|
*/
|
|
264
275
|
type WorkflowState<A, R, F> = RunningWorkflow<A, R> | CompletedWorkflow<A, R> | FailedWorkflow<A, R, F> | CompensatingWorkflow<A, R, F> | FailedCompensatedWorkflow<A, R, F> | CompensationFailedWorkflow<A, R, F>;
|
|
276
|
+
/** Every `status` discriminant of the {@link WorkflowState} union. */
|
|
277
|
+
type WorkflowStatus = WorkflowState<unknown, unknown, unknown>["status"];
|
|
278
|
+
/**
|
|
279
|
+
* The runtime accept-set of every {@link WorkflowStatus} — the single source of
|
|
280
|
+
* truth the boundary snapshot parse keys off (see `do.ts`). Built from an
|
|
281
|
+
* exhaustive `Record<WorkflowStatus, true>`, so adding a variant to the union
|
|
282
|
+
* above without listing it here is a COMPILE error: the set can never silently
|
|
283
|
+
* drift from the union again (the #312 regression, where the #125 compensation
|
|
284
|
+
* statuses were missing and evicted-mid-rollback grains re-seeded fresh).
|
|
285
|
+
*/
|
|
286
|
+
declare const WORKFLOW_STATUSES: ReadonlySet<string>;
|
|
265
287
|
/**
|
|
266
288
|
* Dispatch the in-flight activity. Routed through the consumer's `interpret`
|
|
267
289
|
* over an injected activity port. The reducer emits this literal; it performs
|
|
@@ -295,7 +317,7 @@ interface CompensationCmd<A> extends Cmd<"workflow_compensation"> {
|
|
|
295
317
|
/** The Cmd union this module emits: forward activity dispatches AND (#125)
|
|
296
318
|
* reverse compensation dispatches. The owed/confirmed ledger events are not
|
|
297
319
|
* `WorkflowCmd`s — they are Msg variants persisted into the event log (see
|
|
298
|
-
* {@link EffectLedgerEvent}); the {@link
|
|
320
|
+
* {@link EffectLedgerEvent}); the {@link WorkflowReducerStep} tuple carries them out
|
|
299
321
|
* alongside the dispatch Cmd so the host persists owed-before-dispatch. Both
|
|
300
322
|
* Cmd kinds are owed effects on the same ledger, so `survivingActivities`
|
|
301
323
|
* re-emits either kind on cold wake. */
|
|
@@ -339,9 +361,20 @@ interface CompensationErr<F> {
|
|
|
339
361
|
/** The Msg union the reducer folds: forward activity results AND (#125) reverse
|
|
340
362
|
* compensation results. */
|
|
341
363
|
type WorkflowMsg<R, F> = ActivityOk<R> | ActivityErr<F> | CompensationOk | CompensationErr<F>;
|
|
364
|
+
/** Every `type` discriminant tag of the {@link WorkflowMsg} union. */
|
|
365
|
+
type WorkflowMsgType = WorkflowMsg<unknown, unknown>["type"];
|
|
366
|
+
/**
|
|
367
|
+
* The runtime accept-set of every {@link WorkflowMsgType} — the single source of
|
|
368
|
+
* truth the boundary replay parse keys off (see `do.ts`). Built from an
|
|
369
|
+
* exhaustive `Record<WorkflowMsgType, true>`, so adding a Msg variant to the
|
|
370
|
+
* union above without listing it here is a COMPILE error: the set can never
|
|
371
|
+
* silently drift from the union again (the #312 regression, where the #125
|
|
372
|
+
* compensation result tags were missing and dropped from replay).
|
|
373
|
+
*/
|
|
374
|
+
declare const WORKFLOW_MSG_TYPES: ReadonlySet<string>;
|
|
342
375
|
/** A workflow reducer step: the next state, the ledger events to persist
|
|
343
376
|
* (owed-before-dispatch), and the activity Cmds to dispatch. */
|
|
344
|
-
interface
|
|
377
|
+
interface WorkflowReducerStep<A, R, F> {
|
|
345
378
|
readonly state: WorkflowState<A, R, F>;
|
|
346
379
|
/** Ledger events to persist into the event log, in order, BEFORE `cmds`. */
|
|
347
380
|
readonly ledger: readonly EffectLedgerEvent<WorkflowCmd<A>>[];
|
|
@@ -354,15 +387,14 @@ interface WorkflowStep0<A, R, F> {
|
|
|
354
387
|
* owed-but-unconfirmed dispatch on cold wake (idempotent by delivery id). */
|
|
355
388
|
interface Workflow<A, R, F> {
|
|
356
389
|
/**
|
|
357
|
-
* Seed a fresh workflow over `steps`
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
* - Otherwise → `running` with `current` = step 0, owed on the ledger.
|
|
390
|
+
* Seed a fresh workflow over `steps` (a NON-EMPTY {@link WorkflowSteps}) and
|
|
391
|
+
* dispatch its first activity → `running` with `current` = step 0, owed on the
|
|
392
|
+
* ledger. The "≥ 1 step" precondition is carried by the tuple type: an empty
|
|
393
|
+
* sequence is a compile error at the call site, not a runtime throw here — a
|
|
394
|
+
* workflow with nothing to do could never reach `completed` (no final result
|
|
395
|
+
* to carry), so it is unrepresentable by construction.
|
|
364
396
|
*/
|
|
365
|
-
init(steps:
|
|
397
|
+
init(steps: WorkflowSteps<A>): WorkflowReducerStep<A, R, F>;
|
|
366
398
|
/**
|
|
367
399
|
* Fold an activity success. If `msg.id` does not match the in-flight
|
|
368
400
|
* activity's id (a stale/duplicate result from re-emit-on-wake, or a result
|
|
@@ -375,7 +407,7 @@ interface Workflow<A, R, F> {
|
|
|
375
407
|
* Only meaningful on `running`; a result arriving for a terminal workflow is
|
|
376
408
|
* a no-op (the workflow is done — at-least-once tolerates the late echo).
|
|
377
409
|
*/
|
|
378
|
-
onActivityOk(state: WorkflowState<A, R, F>, msg: ActivityOk<R>):
|
|
410
|
+
onActivityOk(state: WorkflowState<A, R, F>, msg: ActivityOk<R>): WorkflowReducerStep<A, R, F>;
|
|
379
411
|
/**
|
|
380
412
|
* Fold an activity failure (#125). Same id-match idempotency guard as
|
|
381
413
|
* {@link onActivityOk}. On a match: confirm the owed activity on the ledger,
|
|
@@ -386,7 +418,7 @@ interface Workflow<A, R, F> {
|
|
|
386
418
|
* every completed step is irreversible), settle `failed` directly with
|
|
387
419
|
* nothing to unwind.
|
|
388
420
|
*/
|
|
389
|
-
onActivityErr(state: WorkflowState<A, R, F>, msg: ActivityErr<F>):
|
|
421
|
+
onActivityErr(state: WorkflowState<A, R, F>, msg: ActivityErr<F>): WorkflowReducerStep<A, R, F>;
|
|
390
422
|
/**
|
|
391
423
|
* Fold a compensation success (#125). Same id-match idempotency guard against
|
|
392
424
|
* the in-flight compensation. On a match: confirm the owed compensation,
|
|
@@ -395,7 +427,7 @@ interface Workflow<A, R, F> {
|
|
|
395
427
|
* `failed_compensated` when the unwind is complete. Only meaningful on
|
|
396
428
|
* `compensating`; a result for a terminal/forward workflow is a no-op.
|
|
397
429
|
*/
|
|
398
|
-
onCompensationOk(state: WorkflowState<A, R, F>, msg: CompensationOk):
|
|
430
|
+
onCompensationOk(state: WorkflowState<A, R, F>, msg: CompensationOk): WorkflowReducerStep<A, R, F>;
|
|
399
431
|
/**
|
|
400
432
|
* Fold a compensation failure (#125). Same id-match idempotency guard. On a
|
|
401
433
|
* match: confirm the owed compensation and HALT the rollback at the terminal
|
|
@@ -403,7 +435,7 @@ interface Workflow<A, R, F> {
|
|
|
403
435
|
* state to reconcile by hand, never a workflow wedged in `compensating`
|
|
404
436
|
* forever. Mirrors `@demlik/tea/saga`'s `undoErr`.
|
|
405
437
|
*/
|
|
406
|
-
onCompensationErr(state: WorkflowState<A, R, F>, msg: CompensationErr<F>):
|
|
438
|
+
onCompensationErr(state: WorkflowState<A, R, F>, msg: CompensationErr<F>): WorkflowReducerStep<A, R, F>;
|
|
407
439
|
/**
|
|
408
440
|
* The owed-but-unconfirmed dispatch(es) to re-emit on activation — a forward
|
|
409
441
|
* activity OR (#125) a compensation, rebuilt by folding the persisted ledger
|
|
@@ -445,6 +477,6 @@ declare function createWorkflow<A, R, F>(restore?: {
|
|
|
445
477
|
* fresh recorder, not part of the replayed Msg log — exactly as the activity
|
|
446
478
|
* results are the only thing the consumer records and replays.
|
|
447
479
|
*/
|
|
448
|
-
declare function foldWorkflow<A, R, F>(steps:
|
|
480
|
+
declare function foldWorkflow<A, R, F>(steps: WorkflowSteps<A>, msgs: Iterable<WorkflowMsg<R, F>>): WorkflowState<A, R, F>;
|
|
449
481
|
|
|
450
|
-
export { type ActivityCmd, type ActivityErr, type ActivityOk, type CompensatingWorkflow, type CompensationCmd, type CompensationErr, type CompensationFailedWorkflow, type CompensationOk, type CompletedStep, type CompletedWorkflow, DeliveryId, EffectLedgerEvent, type FailedCompensatedWorkflow, type FailedWorkflow, type InFlightActivity, type InFlightCompensation, type RunningWorkflow, type Workflow, type WorkflowCmd, type WorkflowMsg, type WorkflowState, type WorkflowStep, type
|
|
482
|
+
export { type ActivityCmd, type ActivityErr, type ActivityOk, type CompensatingWorkflow, type CompensationCmd, type CompensationErr, type CompensationFailedWorkflow, type CompensationOk, type CompletedStep, type CompletedWorkflow, DeliveryId, EffectLedgerEvent, type FailedCompensatedWorkflow, type FailedWorkflow, type InFlightActivity, type InFlightCompensation, type RunningWorkflow, WORKFLOW_MSG_TYPES, WORKFLOW_STATUSES, type Workflow, type WorkflowCmd, type WorkflowMsg, type WorkflowMsgType, type WorkflowReducerStep, type WorkflowState, type WorkflowStatus, type WorkflowStep, type WorkflowSteps, createWorkflow, foldWorkflow };
|
package/dist/workflow/index.js
CHANGED
|
@@ -1,7 +1,39 @@
|
|
|
1
|
-
import { pendingEffectsLedger, foldLedger, survivingEffects } from '../chunk-
|
|
2
|
-
export { emptyLedger } from '../chunk-
|
|
1
|
+
import { pendingEffectsLedger, foldLedger, survivingEffects } from '../chunk-25LBDCBC.js';
|
|
2
|
+
export { emptyLedger } from '../chunk-25LBDCBC.js';
|
|
3
|
+
|
|
4
|
+
// src/workflow/route.ts
|
|
5
|
+
function routeWorkflowMsg(wf, state, msg) {
|
|
6
|
+
switch (msg.type) {
|
|
7
|
+
case "activity_ok":
|
|
8
|
+
return wf.onActivityOk(state, msg);
|
|
9
|
+
case "activity_err":
|
|
10
|
+
return wf.onActivityErr(state, msg);
|
|
11
|
+
case "compensation_ok":
|
|
12
|
+
return wf.onCompensationOk(state, msg);
|
|
13
|
+
case "compensation_err":
|
|
14
|
+
return wf.onCompensationErr(state, msg);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
3
17
|
|
|
4
18
|
// src/workflow/index.ts
|
|
19
|
+
var WORKFLOW_STATUSES = new Set(
|
|
20
|
+
Object.keys({
|
|
21
|
+
running: true,
|
|
22
|
+
completed: true,
|
|
23
|
+
failed: true,
|
|
24
|
+
compensating: true,
|
|
25
|
+
failed_compensated: true,
|
|
26
|
+
compensation_failed: true
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
var WORKFLOW_MSG_TYPES = new Set(
|
|
30
|
+
Object.keys({
|
|
31
|
+
activity_ok: true,
|
|
32
|
+
activity_err: true,
|
|
33
|
+
compensation_ok: true,
|
|
34
|
+
compensation_err: true
|
|
35
|
+
})
|
|
36
|
+
);
|
|
5
37
|
function createWorkflow(restore) {
|
|
6
38
|
const recorder = pendingEffectsLedger(restore);
|
|
7
39
|
function stepAt(steps, index) {
|
|
@@ -53,13 +85,19 @@ function createWorkflow(restore) {
|
|
|
53
85
|
}
|
|
54
86
|
return null;
|
|
55
87
|
}
|
|
88
|
+
function continueCompensating(base, next, confirmed) {
|
|
89
|
+
const compensating = {
|
|
90
|
+
...base,
|
|
91
|
+
current: next.current
|
|
92
|
+
};
|
|
93
|
+
return {
|
|
94
|
+
state: compensating,
|
|
95
|
+
ledger: [confirmed, next.owed],
|
|
96
|
+
cmds: [next.cmd]
|
|
97
|
+
};
|
|
98
|
+
}
|
|
56
99
|
return {
|
|
57
100
|
init(steps) {
|
|
58
|
-
if (steps.length === 0) {
|
|
59
|
-
throw new Error(
|
|
60
|
-
"createWorkflow.init: a workflow must have at least one step. An empty step sequence has no final result to carry into `completed`, so it is unrepresentable as a completed workflow \u2014 reject it at the boundary rather than admit a resultless terminal state."
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
101
|
const { current, owed, cmd } = oweActivity(stepAt(steps, 0), 0);
|
|
64
102
|
const state = {
|
|
65
103
|
status: "running",
|
|
@@ -117,20 +155,18 @@ function createWorkflow(restore) {
|
|
|
117
155
|
};
|
|
118
156
|
return { state: failed, ledger: [confirmed], cmds: [] };
|
|
119
157
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
cmds: [next.cmd]
|
|
133
|
-
};
|
|
158
|
+
return continueCompensating(
|
|
159
|
+
{
|
|
160
|
+
status: "compensating",
|
|
161
|
+
steps: state.steps,
|
|
162
|
+
completed: state.completed,
|
|
163
|
+
failedStep: state.current.step,
|
|
164
|
+
failure: msg.failure,
|
|
165
|
+
compensated: []
|
|
166
|
+
},
|
|
167
|
+
next,
|
|
168
|
+
confirmed
|
|
169
|
+
);
|
|
134
170
|
},
|
|
135
171
|
onCompensationOk(state, msg) {
|
|
136
172
|
if (state.status !== "compensating" || msg.id !== state.current.id) {
|
|
@@ -155,20 +191,18 @@ function createWorkflow(restore) {
|
|
|
155
191
|
};
|
|
156
192
|
return { state: settled, ledger: [confirmed], cmds: [] };
|
|
157
193
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
cmds: [next.cmd]
|
|
171
|
-
};
|
|
194
|
+
return continueCompensating(
|
|
195
|
+
{
|
|
196
|
+
status: "compensating",
|
|
197
|
+
steps: state.steps,
|
|
198
|
+
completed: state.completed,
|
|
199
|
+
failedStep: state.failedStep,
|
|
200
|
+
failure: state.failure,
|
|
201
|
+
compensated
|
|
202
|
+
},
|
|
203
|
+
next,
|
|
204
|
+
confirmed
|
|
205
|
+
);
|
|
172
206
|
},
|
|
173
207
|
onCompensationErr(state, msg) {
|
|
174
208
|
if (state.status !== "compensating" || msg.id !== state.current.id) {
|
|
@@ -200,26 +234,11 @@ function foldWorkflow(steps, msgs) {
|
|
|
200
234
|
const wf = createWorkflow();
|
|
201
235
|
let { state } = wf.init(steps);
|
|
202
236
|
for (const msg of msgs) {
|
|
203
|
-
|
|
204
|
-
switch (msg.type) {
|
|
205
|
-
case "activity_ok":
|
|
206
|
-
step = wf.onActivityOk(state, msg);
|
|
207
|
-
break;
|
|
208
|
-
case "activity_err":
|
|
209
|
-
step = wf.onActivityErr(state, msg);
|
|
210
|
-
break;
|
|
211
|
-
case "compensation_ok":
|
|
212
|
-
step = wf.onCompensationOk(state, msg);
|
|
213
|
-
break;
|
|
214
|
-
case "compensation_err":
|
|
215
|
-
step = wf.onCompensationErr(state, msg);
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
state = step.state;
|
|
237
|
+
state = routeWorkflowMsg(wf, state, msg).state;
|
|
219
238
|
}
|
|
220
239
|
return state;
|
|
221
240
|
}
|
|
222
241
|
|
|
223
|
-
export { createWorkflow, foldWorkflow };
|
|
242
|
+
export { WORKFLOW_MSG_TYPES, WORKFLOW_STATUSES, createWorkflow, foldWorkflow };
|
|
224
243
|
//# sourceMappingURL=index.js.map
|
|
225
244
|
//# sourceMappingURL=index.js.map
|