@swirls/looms 0.0.2 → 0.1.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/CHANGELOG.md +9 -2
- package/dist/actor/cell.d.ts +9 -4
- package/dist/actor/cell.js +7 -11
- package/dist/actor/host.d.ts +7 -3
- package/dist/actor/host.js +55 -32
- package/dist/actor/index.d.ts +1 -1
- package/dist/actor/index.js +1 -1
- package/dist/actor/routing.d.ts +12 -1
- package/dist/actor/routing.js +57 -51
- package/dist/agent/agent-session.d.ts +5 -0
- package/dist/agent/agent-session.js +270 -0
- package/dist/agent/definitions-store.d.ts +2 -3
- package/dist/agent/definitions-store.js +2 -4
- package/dist/agent/definitions.d.ts +46 -20
- package/dist/agent/definitions.js +36 -8
- package/dist/agent/effects.d.ts +359 -2
- package/dist/agent/effects.js +60 -34
- package/dist/agent/events.d.ts +175 -19
- package/dist/agent/events.js +45 -10
- package/dist/agent/index.d.ts +1 -0
- package/dist/agent/index.js +1 -0
- package/dist/agent/llm.d.ts +11 -2
- package/dist/agent/llm.js +13 -3
- package/dist/agent/module.d.ts +812 -78
- package/dist/agent/module.js +16 -4
- package/dist/agent/scope.d.ts +174 -18
- package/dist/agent/signals.d.ts +24 -1
- package/dist/agent/signals.js +77 -0
- package/dist/agent/threads.d.ts +3 -33
- package/dist/agent/threads.js +20 -25
- package/dist/agent/tool-schema.js +0 -1
- package/dist/agent/types.d.ts +49 -4
- package/dist/agent/types.js +5 -5
- package/dist/ai-vercel/index.d.ts +1 -0
- package/dist/ai-vercel/index.js +1 -0
- package/dist/ai-vercel/messages.js +22 -7
- package/dist/ai-vercel/vercel-evaluator.d.ts +23 -0
- package/dist/ai-vercel/vercel-evaluator.js +68 -0
- package/dist/ai-vercel/vercel-llm.js +64 -44
- package/dist/approval/effects.d.ts +89 -0
- package/dist/approval/module.d.ts +179 -29
- package/dist/approval/projections.d.ts +0 -14
- package/dist/approval/projections.js +20 -3
- package/dist/approval/signals.d.ts +2 -0
- package/dist/approval/signals.js +18 -2
- package/dist/cli/cli.js +2 -0
- package/dist/cli/commands.js +12 -16
- package/dist/client/client.d.ts +1195 -18
- package/dist/client/client.js +118 -120
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/sse.d.ts +13 -0
- package/dist/client/sse.js +38 -44
- package/dist/cloudflare/alarm-scheduler.d.ts +12 -2
- package/dist/cloudflare/alarm-scheduler.js +34 -7
- package/dist/cloudflare/durable-object.d.ts +10 -2
- package/dist/cloudflare/durable-object.js +59 -28
- package/dist/cloudflare/event-store.d.ts +7 -2
- package/dist/cloudflare/event-store.js +8 -7
- package/dist/cloudflare/index.d.ts +2 -2
- package/dist/cloudflare/index.js +2 -2
- package/dist/cloudflare/router.js +13 -14
- package/dist/core/catalog.d.ts +3 -3
- package/dist/core/catalog.js +2 -5
- package/dist/core/coalesce.js +5 -3
- package/dist/core/compose.d.ts +2 -1
- package/dist/core/compose.js +18 -2
- package/dist/core/definition-store.d.ts +11 -0
- package/dist/core/definition-store.js +6 -0
- package/dist/core/effects.d.ts +127 -15
- package/dist/core/effects.js +89 -17
- package/dist/core/envelope.d.ts +27 -7
- package/dist/core/envelope.js +20 -6
- package/dist/core/event-codec.d.ts +4 -2
- package/dist/core/event-codec.js +10 -5
- package/dist/core/event-index.js +1 -3
- package/dist/core/fold.d.ts +3 -0
- package/dist/core/fold.js +171 -24
- package/dist/core/ids.d.ts +5 -0
- package/dist/core/ids.js +35 -2
- package/dist/core/index.d.ts +5 -1
- package/dist/core/index.js +5 -1
- package/dist/core/kind.d.ts +22 -0
- package/dist/core/kind.js +17 -0
- package/dist/core/limits.d.ts +34 -0
- package/dist/core/limits.js +46 -0
- package/dist/core/match.d.ts +1 -0
- package/dist/core/match.js +10 -1
- package/dist/core/module-scope.d.ts +22 -20
- package/dist/core/module-scope.js +38 -28
- package/dist/core/module.d.ts +16 -5
- package/dist/core/module.js +4 -0
- package/dist/core/projection.d.ts +13 -13
- package/dist/core/projection.js +9 -5
- package/dist/core/protocol.d.ts +68 -11
- package/dist/core/protocol.js +82 -11
- package/dist/core/replay.d.ts +312 -3
- package/dist/core/replay.js +11 -1
- package/dist/core/schema.d.ts +8 -19
- package/dist/core/schema.js +25 -80
- package/dist/core/serialize.js +2 -4
- package/dist/core/snapshot-store.d.ts +2 -1
- package/dist/core/snapshot-store.js +8 -2
- package/dist/core/snapshots.d.ts +5 -1
- package/dist/core/snapshots.js +10 -15
- package/dist/core/sqlite-store.js +56 -11
- package/dist/core/state.d.ts +142 -4
- package/dist/core/state.js +78 -0
- package/dist/core/store.d.ts +38 -3
- package/dist/core/store.js +77 -22
- package/dist/core/thread.d.ts +10 -9
- package/dist/core/types.d.ts +5 -8
- package/dist/core/types.js +28 -24
- package/dist/core/validate-event.js +14 -4
- package/dist/core/worker-callback.d.ts +17 -0
- package/dist/core/worker-callback.js +1 -0
- package/dist/debugger/components/event-stream.js +9 -10
- package/dist/debugger/events/protocol-summarize.js +26 -0
- package/dist/debugger/lib/event-meta.js +1 -2
- package/dist/evaluator/definitions-store.d.ts +11 -0
- package/dist/evaluator/definitions-store.js +8 -0
- package/dist/evaluator/definitions.d.ts +18 -0
- package/dist/evaluator/definitions.js +4 -0
- package/dist/evaluator/effects.d.ts +226 -0
- package/dist/evaluator/effects.js +132 -0
- package/dist/evaluator/evaluator.d.ts +24 -0
- package/dist/evaluator/evaluator.js +67 -0
- package/dist/evaluator/events.d.ts +59 -0
- package/dist/evaluator/events.js +21 -0
- package/dist/evaluator/index.d.ts +11 -0
- package/dist/evaluator/index.js +11 -0
- package/dist/evaluator/module.d.ts +606 -0
- package/dist/evaluator/module.js +25 -0
- package/dist/evaluator/projections.d.ts +50 -0
- package/dist/evaluator/projections.js +20 -0
- package/dist/evaluator/scope.d.ts +59 -0
- package/dist/evaluator/scope.js +7 -0
- package/dist/evaluator/signals.d.ts +3 -0
- package/dist/evaluator/signals.js +17 -0
- package/dist/evaluator/threads.d.ts +62 -0
- package/dist/evaluator/threads.js +117 -0
- package/dist/evaluator/types.d.ts +125 -0
- package/dist/evaluator/types.js +51 -0
- package/dist/projectors/delivery.d.ts +83 -0
- package/dist/projectors/delivery.js +182 -0
- package/dist/projectors/index-model.d.ts +1 -1
- package/dist/projectors/index-model.js +4 -6
- package/dist/projectors/index.d.ts +2 -0
- package/dist/projectors/index.js +2 -0
- package/dist/projectors/memory.d.ts +1 -1
- package/dist/projectors/memory.js +10 -8
- package/dist/projectors/postgres.js +44 -58
- package/dist/projectors/projector.d.ts +17 -0
- package/dist/projectors/projector.js +26 -1
- package/dist/projectors/sqlite-delivery.d.ts +4 -0
- package/dist/projectors/sqlite-delivery.js +147 -0
- package/dist/projectors/sqlite.js +23 -23
- package/dist/projectors/with-projectors.d.ts +7 -2
- package/dist/projectors/with-projectors.js +27 -28
- package/dist/react/notify.js +11 -8
- package/dist/react/react.d.ts +2 -3
- package/dist/react/react.js +14 -30
- package/dist/react/sse.d.ts +1 -1
- package/dist/react/sse.js +1 -1
- package/dist/react/store.d.ts +2 -2
- package/dist/react/store.js +109 -119
- package/dist/runtime/cancellation.d.ts +16 -0
- package/dist/runtime/cancellation.js +129 -0
- package/dist/runtime/deadline-transition-planner.d.ts +31 -0
- package/dist/runtime/deadline-transition-planner.js +139 -0
- package/dist/runtime/dispatch-effect.d.ts +3 -2
- package/dist/runtime/dispatch-effect.js +7 -13
- package/dist/runtime/errors.d.ts +51 -0
- package/dist/runtime/errors.js +39 -0
- package/dist/runtime/{runtime-helpers.d.ts → helpers.d.ts} +24 -4
- package/dist/runtime/{runtime-helpers.js → helpers.js} +56 -19
- package/dist/runtime/index.d.ts +3 -3
- package/dist/runtime/index.js +3 -3
- package/dist/runtime/ingress.d.ts +15 -0
- package/dist/runtime/ingress.js +42 -0
- package/dist/runtime/local-effect-executor.d.ts +21 -0
- package/dist/runtime/local-effect-executor.js +137 -0
- package/dist/runtime/looms.d.ts +22 -4
- package/dist/runtime/looms.js +54 -36
- package/dist/runtime/observer.d.ts +45 -0
- package/dist/runtime/observer.js +11 -0
- package/dist/runtime/repository.d.ts +55 -0
- package/dist/runtime/repository.js +278 -0
- package/dist/runtime/retry-policy.d.ts +16 -0
- package/dist/runtime/retry-policy.js +29 -0
- package/dist/runtime/run-execution-context.d.ts +30 -0
- package/dist/runtime/run-execution-context.js +56 -0
- package/dist/runtime/run-start.d.ts +4 -0
- package/dist/runtime/run-start.js +44 -0
- package/dist/runtime/runtime.d.ts +6 -87
- package/dist/runtime/runtime.js +233 -470
- package/dist/runtime/server/auth.d.ts +40 -0
- package/dist/runtime/server/auth.js +40 -0
- package/dist/runtime/server/fetch-handler.d.ts +14 -0
- package/dist/runtime/server/fetch-handler.js +51 -0
- package/dist/runtime/server/http.d.ts +37 -0
- package/dist/runtime/server/http.js +62 -0
- package/dist/runtime/server/route-table.d.ts +55 -0
- package/dist/runtime/server/route-table.js +74 -0
- package/dist/runtime/server/routes/effects.d.ts +3 -0
- package/dist/runtime/server/routes/effects.js +59 -0
- package/dist/runtime/server/routes/operations.d.ts +3 -0
- package/dist/runtime/server/routes/operations.js +14 -0
- package/dist/runtime/server/routes/runs.d.ts +3 -0
- package/dist/runtime/server/routes/runs.js +192 -0
- package/dist/runtime/server.d.ts +2 -8
- package/dist/runtime/server.js +4 -211
- package/dist/runtime/sse.d.ts +2 -1
- package/dist/runtime/sse.js +24 -17
- package/dist/runtime/types.d.ts +115 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtime/wake-engine.d.ts +20 -0
- package/dist/runtime/wake-engine.js +311 -0
- package/dist/runtime/wake-scheduler.d.ts +3 -4
- package/dist/runtime/wake-scheduler.js +25 -31
- package/dist/runtime/worker-callbacks.d.ts +16 -0
- package/dist/runtime/worker-callbacks.js +167 -0
- package/dist/runtime/worker-effect-dispatch.d.ts +36 -0
- package/dist/runtime/worker-effect-dispatch.js +126 -0
- package/dist/s2/config.d.ts +1 -1
- package/dist/s2/config.js +1 -1
- package/dist/s2/lite.js +70 -82
- package/dist/s2/pagination.d.ts +15 -0
- package/dist/s2/pagination.js +36 -0
- package/dist/s2/projector.js +6 -5
- package/dist/s2/snapshot-store.js +148 -163
- package/dist/s2/store.d.ts +3 -21
- package/dist/s2/store.js +71 -165
- package/dist/testing/index.d.ts +15 -0
- package/dist/testing/index.js +53 -16
- package/dist/workflow/definitions-store.d.ts +2 -3
- package/dist/workflow/definitions-store.js +2 -2
- package/dist/workflow/definitions.d.ts +78 -22
- package/dist/workflow/definitions.js +80 -14
- package/dist/workflow/effects.d.ts +453 -107
- package/dist/workflow/effects.js +214 -115
- package/dist/workflow/events.d.ts +149 -5
- package/dist/workflow/events.js +27 -5
- package/dist/workflow/graph.d.ts +56 -0
- package/dist/workflow/graph.js +207 -0
- package/dist/workflow/index.d.ts +1 -0
- package/dist/workflow/index.js +1 -0
- package/dist/workflow/module.d.ts +1047 -266
- package/dist/workflow/projections.d.ts +14 -12
- package/dist/workflow/projections.js +30 -8
- package/dist/workflow/scope.d.ts +146 -4
- package/dist/workflow/threads.d.ts +383 -155
- package/dist/workflow/threads.js +308 -110
- package/package.json +7 -2
- package/dist/runtime/events-api.d.ts +0 -4
- package/dist/runtime/events-api.js +0 -84
package/dist/s2/store.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { AppendInput, AppendRecord, S2, S2Endpoints, S2Error } from '@s2-dev/streamstore';
|
|
2
|
-
import { Effect, Layer,
|
|
3
|
-
import { createKeyedSerializer, EventStoreConflictError, EventStoreError, EventStoreFencedError, EventStoreTag, EventEnvelopeSchema, fromWireEvent, withSnapshotStore, } from '@swirls/looms/core';
|
|
2
|
+
import { DateTime, Effect, Layer, Predicate, Schema, Stream } from 'effect';
|
|
3
|
+
import { createKeyedSerializer, assertEventByteLimits, EventStoreConflictError, EventStoreError, EventStoreFencedError, EventPayloadTooLargeError, EventStoreTag, EventEnvelopeSchema, fromWireEvent, withSnapshotStore, } from '@swirls/looms/core';
|
|
4
4
|
import { S2ConfigSchema, streamNameForRun } from './config.js';
|
|
5
|
+
import { paginateS2Stream } from './pagination.js';
|
|
5
6
|
import { s2SnapshotStore } from './snapshot-store.js';
|
|
6
7
|
function toStoreError(cause, message) {
|
|
7
8
|
if (cause instanceof EventStoreError) {
|
|
@@ -20,50 +21,8 @@ function isUnsatisfiableCause(cause) {
|
|
|
20
21
|
const text = cause instanceof Error ? cause.message : String(cause);
|
|
21
22
|
return text.includes('out of range') || text.includes('Range not satisfiable');
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
*
|
|
26
|
-
* Pulls pages on demand until the stream tail is reached, an empty page is returned,
|
|
27
|
-
* or the caller's optional limit is satisfied.
|
|
28
|
-
*/
|
|
29
|
-
export function paginateS2Stream(fetchPage, options) {
|
|
30
|
-
const fromSeq = options?.fromSeq ?? 0;
|
|
31
|
-
const userLimit = options?.limit;
|
|
32
|
-
const maxPageSize = options?.maxPageSize ?? 1000;
|
|
33
|
-
if (userLimit !== undefined && userLimit <= 0) {
|
|
34
|
-
return Stream.empty;
|
|
35
|
-
}
|
|
36
|
-
const baseStream = Stream.paginate({ cursor: fromSeq, remaining: userLimit }, (state) => Effect.gen(function* () {
|
|
37
|
-
if (state.remaining !== undefined && state.remaining <= 0) {
|
|
38
|
-
return [[], Option.none()];
|
|
39
|
-
}
|
|
40
|
-
const count = state.remaining !== undefined ? Math.min(maxPageSize, state.remaining) : maxPageSize;
|
|
41
|
-
const page = yield* fetchPage(state.cursor, count);
|
|
42
|
-
if (!page || page.records.length === 0) {
|
|
43
|
-
return [[], Option.none()];
|
|
44
|
-
}
|
|
45
|
-
const records = page.records.filter((r) => r.seqNum >= fromSeq);
|
|
46
|
-
const nextRemaining = state.remaining !== undefined ? state.remaining - records.length : undefined;
|
|
47
|
-
const lastRecord = page.records[page.records.length - 1];
|
|
48
|
-
if (!lastRecord || (nextRemaining !== undefined && nextRemaining <= 0)) {
|
|
49
|
-
return [records, Option.none()];
|
|
50
|
-
}
|
|
51
|
-
const nextCursor = Math.max(state.cursor + 1, lastRecord.seqNum + 1);
|
|
52
|
-
if (nextCursor <= state.cursor) {
|
|
53
|
-
return [records, Option.none()];
|
|
54
|
-
}
|
|
55
|
-
// A byte-capped page is shorter than `count`. Do not trust `page.tail`
|
|
56
|
-
// there — S2 may report the batch end, not the stream tail.
|
|
57
|
-
const shortPage = page.records.length < count;
|
|
58
|
-
const reachedTail = !shortPage && page.tail?.seqNum !== undefined && nextCursor >= page.tail.seqNum;
|
|
59
|
-
if (reachedTail) {
|
|
60
|
-
return [records, Option.none()];
|
|
61
|
-
}
|
|
62
|
-
return [records, Option.some({ cursor: nextCursor, remaining: nextRemaining })];
|
|
63
|
-
}));
|
|
64
|
-
return userLimit !== undefined ? baseStream.pipe(Stream.take(userLimit)) : baseStream;
|
|
65
|
-
}
|
|
66
|
-
export async function readAllPages(fetchPage, options) {
|
|
24
|
+
export { paginateS2Stream };
|
|
25
|
+
export function readAllPages(fetchPage, options) {
|
|
67
26
|
const fetchPageEffect = (cursor, count) => Effect.tryPromise({
|
|
68
27
|
try: () => fetchPage(cursor, count),
|
|
69
28
|
catch: (cause) => toStoreError(cause, 'S2 page fetch failed'),
|
|
@@ -101,61 +60,49 @@ function createClient(config) {
|
|
|
101
60
|
* Expected tail mismatches during `append` map to `EventStoreConflictError`.
|
|
102
61
|
*/
|
|
103
62
|
export function s2(config) {
|
|
104
|
-
const parsed = Schema.
|
|
63
|
+
const parsed = Schema.decodeSync(S2ConfigSchema)(config);
|
|
105
64
|
const client = createClient(parsed);
|
|
106
65
|
const basin = client.basin(parsed.basin);
|
|
107
66
|
const ensured = new Set();
|
|
108
67
|
const appends = createKeyedSerializer();
|
|
109
68
|
let basinEnsured = false;
|
|
110
|
-
const ensureBasin = Effect.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
try {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
catch (err) {
|
|
119
|
-
// 409 = already exists
|
|
120
|
-
if (!(err instanceof S2Error && err.status === 409)) {
|
|
121
|
-
throw err;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
69
|
+
const ensureBasin = Effect.suspend(() => {
|
|
70
|
+
if (basinEnsured) {
|
|
71
|
+
return Effect.void;
|
|
72
|
+
}
|
|
73
|
+
return Effect.tryPromise({
|
|
74
|
+
try: () => client.basins.create({ basin: parsed.basin }),
|
|
75
|
+
catch: (cause) => toStoreError(cause, `Failed to ensure basin ${parsed.basin}`),
|
|
76
|
+
}).pipe(Effect.catchIf((error) => error.cause instanceof S2Error && error.cause.status === 409, () => Effect.void), Effect.tap(() => Effect.sync(() => {
|
|
124
77
|
basinEnsured = true;
|
|
125
|
-
},
|
|
126
|
-
catch: (cause) => toStoreError(cause, `Failed to ensure basin ${parsed.basin}`),
|
|
78
|
+
})), Effect.asVoid);
|
|
127
79
|
});
|
|
128
80
|
const ensureStream = (runId) => Effect.gen(function* () {
|
|
129
81
|
yield* ensureBasin;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
// Some local servers may not support create; try using the stream anyway.
|
|
146
|
-
if (!(err instanceof S2Error && (err.status === 404 || err.status === 405))) {
|
|
147
|
-
throw err;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
ensured.add(name);
|
|
152
|
-
return basin.stream(name);
|
|
153
|
-
},
|
|
154
|
-
catch: (cause) => toStoreError(cause, `Failed to open stream for ${runId}`),
|
|
155
|
-
});
|
|
82
|
+
const name = streamNameForRun(runId);
|
|
83
|
+
if (!ensured.has(name)) {
|
|
84
|
+
yield* Effect.tryPromise({
|
|
85
|
+
try: () => basin.streams.create({
|
|
86
|
+
stream: name,
|
|
87
|
+
config: parsed.streamConfig,
|
|
88
|
+
}),
|
|
89
|
+
catch: (cause) => toStoreError(cause, `Failed to open stream for ${runId}`),
|
|
90
|
+
}).pipe(Effect.catchIf((error) => error.cause instanceof S2Error &&
|
|
91
|
+
(error.cause.status === 409 ||
|
|
92
|
+
error.cause.status === 404 ||
|
|
93
|
+
error.cause.status === 405), () => Effect.void));
|
|
94
|
+
ensured.add(name);
|
|
95
|
+
}
|
|
96
|
+
return basin.stream(name);
|
|
156
97
|
});
|
|
157
98
|
const service = {
|
|
158
99
|
append: (runId, events, options) => Effect.gen(function* () {
|
|
100
|
+
yield* Effect.try({
|
|
101
|
+
try: () => assertEventByteLimits(runId, events),
|
|
102
|
+
catch: (cause) => cause instanceof EventPayloadTooLargeError
|
|
103
|
+
? cause
|
|
104
|
+
: new EventStoreError('event size validation failed', cause),
|
|
105
|
+
});
|
|
159
106
|
if (events.length === 0 && options?.fence === undefined) {
|
|
160
107
|
const tail = yield* service.tail(runId);
|
|
161
108
|
return { sequences: [], tail };
|
|
@@ -186,7 +133,7 @@ export function s2(config) {
|
|
|
186
133
|
cause.status === 412 &&
|
|
187
134
|
(message.includes('fenc') || message.includes('token'));
|
|
188
135
|
if (isFence) {
|
|
189
|
-
return yield*
|
|
136
|
+
return yield* new EventStoreFencedError(runId, options?.fencingToken ?? options?.fence ?? '');
|
|
190
137
|
}
|
|
191
138
|
if (cause instanceof S2Error &&
|
|
192
139
|
(cause.status === 409 ||
|
|
@@ -195,9 +142,9 @@ export function s2(config) {
|
|
|
195
142
|
message.includes('match'))) {
|
|
196
143
|
const tailCheck = yield* Effect.promise(() => stream.checkTail().catch(() => null));
|
|
197
144
|
const actualTail = tailCheck?.tail?.seqNum ?? 0;
|
|
198
|
-
return yield*
|
|
145
|
+
return yield* new EventStoreConflictError(runId, options?.expectedTail ?? 0, actualTail);
|
|
199
146
|
}
|
|
200
|
-
return yield*
|
|
147
|
+
return yield* toStoreError(cause, `Append failed for ${runId}`);
|
|
201
148
|
}
|
|
202
149
|
const ack = appendResult.ack;
|
|
203
150
|
const sequences = [];
|
|
@@ -213,23 +160,13 @@ export function s2(config) {
|
|
|
213
160
|
return Stream.unwrap(Effect.gen(function* () {
|
|
214
161
|
const stream = yield* ensureStream(runId);
|
|
215
162
|
const fetchPage = (cursor, count) => Effect.tryPromise({
|
|
216
|
-
try:
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
ignoreCommandRecords: true,
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
catch (cause) {
|
|
225
|
-
if (isUnsatisfiableCause(cause)) {
|
|
226
|
-
return null;
|
|
227
|
-
}
|
|
228
|
-
throw cause;
|
|
229
|
-
}
|
|
230
|
-
},
|
|
163
|
+
try: () => stream.read({
|
|
164
|
+
start: { from: { seqNum: cursor }, clamp: true },
|
|
165
|
+
stop: { limits: { count } },
|
|
166
|
+
ignoreCommandRecords: true,
|
|
167
|
+
}),
|
|
231
168
|
catch: (cause) => toStoreError(cause, `Read failed for ${runId}`),
|
|
232
|
-
});
|
|
169
|
+
}).pipe(Effect.catchIf((error) => isUnsatisfiableCause(error.cause), () => Effect.succeed(null)));
|
|
233
170
|
const recordStream = paginateS2Stream(fetchPage, {
|
|
234
171
|
fromSeq: s2From,
|
|
235
172
|
limit: options?.limit,
|
|
@@ -246,31 +183,21 @@ export function s2(config) {
|
|
|
246
183
|
const res = yield* Effect.tryPromise({
|
|
247
184
|
try: () => stream.checkTail(),
|
|
248
185
|
catch: (cause) => toStoreError(cause, `Tail failed for ${runId}`),
|
|
249
|
-
}).pipe(Effect.
|
|
250
|
-
|
|
251
|
-
|
|
186
|
+
}).pipe(Effect.catchIf((err) => err.cause instanceof S2Error && err.cause.status === 404, () => Effect.succeed({
|
|
187
|
+
tail: { seqNum: 0, timestamp: DateTime.toDateUtc(DateTime.makeUnsafe(0)) },
|
|
188
|
+
})));
|
|
252
189
|
return res.tail.seqNum;
|
|
253
190
|
}),
|
|
254
191
|
bounds: (runId) => Effect.gen(function* () {
|
|
255
192
|
const stream = yield* ensureStream(runId);
|
|
256
193
|
const page = yield* Effect.tryPromise({
|
|
257
|
-
try:
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
ignoreCommandRecords: false,
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
catch (cause) {
|
|
266
|
-
if (isUnsatisfiableCause(cause)) {
|
|
267
|
-
return null;
|
|
268
|
-
}
|
|
269
|
-
throw cause;
|
|
270
|
-
}
|
|
271
|
-
},
|
|
194
|
+
try: () => stream.read({
|
|
195
|
+
start: { from: { seqNum: 0 }, clamp: true },
|
|
196
|
+
stop: { limits: { count: 1 } },
|
|
197
|
+
ignoreCommandRecords: false,
|
|
198
|
+
}),
|
|
272
199
|
catch: (cause) => toStoreError(cause, `Bounds failed for ${runId}`),
|
|
273
|
-
});
|
|
200
|
+
}).pipe(Effect.catchIf((error) => isUnsatisfiableCause(error.cause), () => Effect.succeed(null)));
|
|
274
201
|
const tail = page?.tail?.seqNum ?? 0;
|
|
275
202
|
const first = page?.records[0];
|
|
276
203
|
const head = first ? first.seqNum + 1 : tail + 1;
|
|
@@ -284,49 +211,28 @@ export function s2(config) {
|
|
|
284
211
|
catch: (cause) => toStoreError(cause, `Trim failed for ${runId}`),
|
|
285
212
|
});
|
|
286
213
|
}),
|
|
287
|
-
subscribe: (runId, options) =>
|
|
214
|
+
subscribe: (runId, options) => {
|
|
288
215
|
const fromSeq = options?.fromSeq ?? 1;
|
|
289
216
|
const s2From = Math.max(0, fromSeq - 1);
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const session = await stream.readSession({
|
|
217
|
+
return Stream.unwrap(Effect.gen(function* () {
|
|
218
|
+
const stream = yield* ensureStream(runId);
|
|
219
|
+
const session = yield* Effect.tryPromise({
|
|
220
|
+
try: () => stream.readSession({
|
|
295
221
|
start: { from: { seqNum: s2From }, clamp: true },
|
|
296
222
|
ignoreCommandRecords: true,
|
|
297
|
-
})
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
Queue.offerUnsafe(queue, parseEvent(record.body, record.seqNum));
|
|
306
|
-
}
|
|
307
|
-
Queue.endUnsafe(queue);
|
|
308
|
-
resume(Effect.void);
|
|
309
|
-
}
|
|
310
|
-
catch (cause) {
|
|
311
|
-
await Effect.runPromise(Queue.fail(queue, toStoreError(cause, `Subscribe failed for ${runId}`)));
|
|
312
|
-
resume(Effect.void);
|
|
313
|
-
}
|
|
314
|
-
};
|
|
315
|
-
void run();
|
|
316
|
-
return Effect.sync(() => {
|
|
317
|
-
stopped = true;
|
|
318
|
-
});
|
|
319
|
-
})),
|
|
320
|
-
listRuns: () => Effect.tryPromise({
|
|
321
|
-
try: async () => {
|
|
322
|
-
const listed = await basin.streams.list({ prefix: 'runs/' });
|
|
323
|
-
return listed.streams
|
|
324
|
-
.map((s) => s.name)
|
|
325
|
-
.filter((name) => name.startsWith('runs/'))
|
|
326
|
-
.map((name) => name.slice('runs/'.length));
|
|
327
|
-
},
|
|
223
|
+
}),
|
|
224
|
+
catch: (cause) => toStoreError(cause, `Subscribe failed for ${runId}`),
|
|
225
|
+
});
|
|
226
|
+
return Stream.fromAsyncIterable(session, (cause) => toStoreError(cause, `Subscribe failed for ${runId}`)).pipe(Stream.filter((record) => record.seqNum >= s2From), Stream.map((record) => parseEvent(record.body, record.seqNum)));
|
|
227
|
+
}));
|
|
228
|
+
},
|
|
229
|
+
listRuns: Effect.tryPromise({
|
|
230
|
+
try: () => basin.streams.list({ prefix: 'runs/' }),
|
|
328
231
|
catch: (cause) => toStoreError(cause, 'listRuns failed'),
|
|
329
|
-
})
|
|
232
|
+
}).pipe(Effect.map((listed) => listed.streams
|
|
233
|
+
.map((stream) => stream.name)
|
|
234
|
+
.filter((name) => name.startsWith('runs/'))
|
|
235
|
+
.map((name) => name.slice('runs/'.length)))),
|
|
330
236
|
};
|
|
331
237
|
return withSnapshotStore(service, s2SnapshotStore(parsed));
|
|
332
238
|
}
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -9,3 +9,18 @@ export interface TestRuntime {
|
|
|
9
9
|
export declare function createTestRuntime(modules: readonly AnyRuntimeModule[]): Promise<TestRuntime>;
|
|
10
10
|
export declare function assertReplayDeterministic(test: TestRuntime, runId: string): Promise<void>;
|
|
11
11
|
export declare function moduleConformance(module: AnyRuntimeModule): string[];
|
|
12
|
+
export type EventStoreFaultOperation = 'append' | 'read' | 'tail' | 'bounds' | 'trim' | 'listRuns';
|
|
13
|
+
export interface EventStoreFaultContext {
|
|
14
|
+
readonly operation: EventStoreFaultOperation;
|
|
15
|
+
readonly runId: string | null;
|
|
16
|
+
readonly call: number;
|
|
17
|
+
}
|
|
18
|
+
export interface FaultInjectingEventStore {
|
|
19
|
+
readonly store: EventStore;
|
|
20
|
+
readonly calls: ReadonlyMap<EventStoreFaultOperation, number>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Wraps an EventStore with deterministic delay/failure injection. Throw from
|
|
24
|
+
* `before` to fail a call, or await inside it to pause a race at an exact store boundary.
|
|
25
|
+
*/
|
|
26
|
+
export declare function createFaultInjectingEventStore(source: EventStore, before: (context: EventStoreFaultContext) => void | Promise<void>): FaultInjectingEventStore;
|
package/dist/testing/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Effect, Predicate } from 'effect';
|
|
2
|
-
import { composeModules, EventStoreTag, foldRun, makeMemoryEventStore, ModuleCompositionError, } from '@swirls/looms/core';
|
|
2
|
+
import { composeModules, EventStoreTag, foldRun, makeMemoryEventStore, ModuleCompositionError, EventStoreError, snapshotStoreOf, stringifyJson, withSnapshotStore, } from '@swirls/looms/core';
|
|
3
3
|
import { createRuntime } from '@swirls/looms/runtime';
|
|
4
4
|
function namedEffects(effects) {
|
|
5
5
|
const listed = [];
|
|
@@ -15,22 +15,24 @@ function namedEffects(effects) {
|
|
|
15
15
|
}
|
|
16
16
|
return listed;
|
|
17
17
|
}
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
export function createTestRuntime(modules) {
|
|
19
|
+
return Effect.runPromise(makeMemoryEventStore.pipe(Effect.map((store) => {
|
|
20
|
+
const runtime = createRuntime({ modules, store });
|
|
21
|
+
return {
|
|
22
|
+
runtime,
|
|
23
|
+
store,
|
|
24
|
+
run: (effect) => Effect.runPromise(Effect.provideService(effect, EventStoreTag, store)),
|
|
25
|
+
};
|
|
26
|
+
})));
|
|
26
27
|
}
|
|
27
|
-
export
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
export function assertReplayDeterministic(test, runId) {
|
|
29
|
+
return test.run(test.runtime.getEvents(runId)).then((events) => {
|
|
30
|
+
const first = foldRun(events, test.runtime.registry, { runId });
|
|
31
|
+
const second = foldRun(events, test.runtime.registry, { runId });
|
|
32
|
+
if (stringifyJson(first) !== stringifyJson(second)) {
|
|
33
|
+
throw new Error(`Replay is not deterministic for run ${runId}`);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
34
36
|
}
|
|
35
37
|
export function moduleConformance(module) {
|
|
36
38
|
const errors = [];
|
|
@@ -61,3 +63,38 @@ export function moduleConformance(module) {
|
|
|
61
63
|
}
|
|
62
64
|
return errors;
|
|
63
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Wraps an EventStore with deterministic delay/failure injection. Throw from
|
|
68
|
+
* `before` to fail a call, or await inside it to pause a race at an exact store boundary.
|
|
69
|
+
*/
|
|
70
|
+
export function createFaultInjectingEventStore(source, before) {
|
|
71
|
+
const calls = new Map();
|
|
72
|
+
const inject = (operation, runId) => Effect.tryPromise({
|
|
73
|
+
try: () => {
|
|
74
|
+
const call = (calls.get(operation) ?? 0) + 1;
|
|
75
|
+
calls.set(operation, call);
|
|
76
|
+
return Promise.resolve(before({ operation, runId, call }));
|
|
77
|
+
},
|
|
78
|
+
catch: (cause) => cause instanceof EventStoreError
|
|
79
|
+
? cause
|
|
80
|
+
: new EventStoreError(`Injected ${operation} fault`, cause),
|
|
81
|
+
});
|
|
82
|
+
const store = {
|
|
83
|
+
append: (runId, events, options) => inject('append', runId).pipe(Effect.andThen(source.append(runId, events, options))),
|
|
84
|
+
read: (runId, options) => inject('read', runId).pipe(Effect.andThen(source.read(runId, options))),
|
|
85
|
+
tail: (runId) => inject('tail', runId).pipe(Effect.andThen(source.tail(runId))),
|
|
86
|
+
subscribe: source.subscribe,
|
|
87
|
+
listRuns: inject('listRuns', null).pipe(Effect.andThen(source.listRuns)),
|
|
88
|
+
};
|
|
89
|
+
if (source.bounds) {
|
|
90
|
+
store.bounds = (runId) => inject('bounds', runId).pipe(Effect.andThen(source.bounds(runId)));
|
|
91
|
+
}
|
|
92
|
+
if (source.trim) {
|
|
93
|
+
store.trim = (runId, beforeSeq) => inject('trim', runId).pipe(Effect.andThen(source.trim(runId, beforeSeq)));
|
|
94
|
+
}
|
|
95
|
+
const snapshots = snapshotStoreOf(source);
|
|
96
|
+
if (snapshots) {
|
|
97
|
+
withSnapshotStore(store, snapshots);
|
|
98
|
+
}
|
|
99
|
+
return { store, calls };
|
|
100
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Context, Layer } from 'effect';
|
|
2
|
+
import { type DefinitionStore } from '@swirls/looms/core';
|
|
2
3
|
import type { WorkflowDefinition } from './definitions';
|
|
3
|
-
export
|
|
4
|
-
get(name: string): WorkflowDefinition | undefined;
|
|
5
|
-
}
|
|
4
|
+
export type WorkflowDefinitionStore = DefinitionStore<WorkflowDefinition>;
|
|
6
5
|
declare const WorkflowDefinitionsTag_base: Context.ServiceClass<WorkflowDefinitionsTag, "looms/WorkflowDefinitions", WorkflowDefinitionStore>;
|
|
7
6
|
export declare class WorkflowDefinitionsTag extends WorkflowDefinitionsTag_base {
|
|
8
7
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Context, Layer } from 'effect';
|
|
2
|
+
import { makeDefinitionStore } from '@swirls/looms/core';
|
|
2
3
|
export class WorkflowDefinitionsTag extends Context.Service()('looms/WorkflowDefinitions') {
|
|
3
4
|
}
|
|
4
5
|
export function makeWorkflowDefinitionStore(definitions) {
|
|
5
|
-
|
|
6
|
-
return { get: (name) => map.get(name) };
|
|
6
|
+
return makeDefinitionStore('workflow', definitions);
|
|
7
7
|
}
|
|
8
8
|
export const WorkflowDefinitionsLive = (definitions) => Layer.succeed(WorkflowDefinitionsTag, makeWorkflowDefinitionStore(definitions));
|
|
@@ -1,14 +1,34 @@
|
|
|
1
1
|
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
2
|
import { Schema } from 'effect';
|
|
3
|
-
import
|
|
3
|
+
import { type JsonValue, type RuntimeEffect } from '@swirls/looms/core';
|
|
4
|
+
import { type WorkflowEdge } from './graph';
|
|
4
5
|
export declare const NodeStatusSchema: Schema.Union<readonly [Schema.Literal<"pending">, Schema.Literal<"running">, Schema.Literal<"completed">, Schema.Literal<"failed">, Schema.Literal<"skipped">]>;
|
|
5
6
|
export type NodeStatus = Schema.Schema.Type<typeof NodeStatusSchema>;
|
|
6
7
|
export declare const NodeStateSchema: Schema.Struct<{
|
|
7
8
|
readonly status: Schema.Union<readonly [Schema.Literal<"pending">, Schema.Literal<"running">, Schema.Literal<"completed">, Schema.Literal<"failed">, Schema.Literal<"skipped">]>;
|
|
8
9
|
readonly result: Schema.NullOr<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
|
|
9
10
|
readonly error: Schema.NullOr<Schema.String>;
|
|
11
|
+
readonly attempts: Schema.optional<Schema.Finite>;
|
|
12
|
+
readonly branch: Schema.optional<Schema.String>;
|
|
13
|
+
/** Policy skips (skip/fallback) still satisfy dependents; inactive skips propagate. */
|
|
14
|
+
readonly skipCause: Schema.optional<Schema.Union<readonly [Schema.Literal<"inactive">, Schema.Literal<"policy">]>>;
|
|
15
|
+
readonly iteration: Schema.optional<Schema.Struct<{
|
|
16
|
+
readonly index: Schema.Finite;
|
|
17
|
+
readonly previous: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
18
|
+
}>>;
|
|
10
19
|
}>;
|
|
11
20
|
export type NodeState = Schema.Schema.Type<typeof NodeStateSchema>;
|
|
21
|
+
export interface WorkflowChild {
|
|
22
|
+
readonly kind: string;
|
|
23
|
+
readonly name: string;
|
|
24
|
+
readonly version?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface WorkflowIteration {
|
|
27
|
+
/** Zero-based index of the loop iteration about to run. */
|
|
28
|
+
readonly index: number;
|
|
29
|
+
/** Output of the previous iteration's child. */
|
|
30
|
+
readonly previous: JsonValue;
|
|
31
|
+
}
|
|
12
32
|
export interface WorkflowNodeContext<TInput = JsonValue> {
|
|
13
33
|
readonly threadId: string;
|
|
14
34
|
readonly nodeId: string;
|
|
@@ -16,12 +36,22 @@ export interface WorkflowNodeContext<TInput = JsonValue> {
|
|
|
16
36
|
readonly results: {
|
|
17
37
|
[nodeId: string]: JsonValue | null;
|
|
18
38
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}, input: JsonValue): NodeResult;
|
|
39
|
+
/** Set when a `loop` node is re-run after one of its iterations completed. */
|
|
40
|
+
readonly iteration: WorkflowIteration | undefined;
|
|
41
|
+
spawn(child: WorkflowChild, input: JsonValue): NodeResult;
|
|
23
42
|
sleep(ms: number): NodeResult;
|
|
24
43
|
effects(effects: RuntimeEffect[]): NodeResult;
|
|
44
|
+
/** Run one child per item, one at a time, and finish with the ordered outputs. */
|
|
45
|
+
map(child: WorkflowChild, items: readonly JsonValue[]): NodeResult;
|
|
46
|
+
/** Run one child per item with bounded concurrency and finish with the ordered outputs. */
|
|
47
|
+
fanout(child: WorkflowChild, items: readonly JsonValue[], concurrency?: number): NodeResult;
|
|
48
|
+
/**
|
|
49
|
+
* Run one child iteration, then re-run this node with `ctx.iteration`. Returning a value
|
|
50
|
+
* instead ends the loop; reaching `maxIterations` ends it with the last iteration's output.
|
|
51
|
+
*/
|
|
52
|
+
loop(child: WorkflowChild, input: JsonValue, options: {
|
|
53
|
+
maxIterations: number;
|
|
54
|
+
}): NodeResult;
|
|
25
55
|
}
|
|
26
56
|
export type NodeResult = {
|
|
27
57
|
type: 'value';
|
|
@@ -30,26 +60,64 @@ export type NodeResult = {
|
|
|
30
60
|
type: 'spawn';
|
|
31
61
|
kind: string;
|
|
32
62
|
name: string;
|
|
63
|
+
version: string;
|
|
33
64
|
input: JsonValue;
|
|
65
|
+
childThreadId?: string;
|
|
34
66
|
} | {
|
|
35
67
|
type: 'sleep';
|
|
36
68
|
ms: number;
|
|
37
69
|
} | {
|
|
38
70
|
type: 'effects';
|
|
39
71
|
effects: RuntimeEffect[];
|
|
72
|
+
} | {
|
|
73
|
+
type: 'switch';
|
|
74
|
+
branch: string;
|
|
75
|
+
value?: JsonValue;
|
|
76
|
+
} | {
|
|
77
|
+
type: 'map';
|
|
78
|
+
kind: string;
|
|
79
|
+
name: string;
|
|
80
|
+
version: string;
|
|
81
|
+
items: readonly JsonValue[];
|
|
82
|
+
concurrency: number;
|
|
83
|
+
} | {
|
|
84
|
+
type: 'loop';
|
|
85
|
+
kind: string;
|
|
86
|
+
name: string;
|
|
87
|
+
version: string;
|
|
88
|
+
input: JsonValue;
|
|
89
|
+
maxIterations: number;
|
|
90
|
+
};
|
|
91
|
+
export type WorkflowFailurePolicy = {
|
|
92
|
+
readonly type: 'fail';
|
|
93
|
+
} | {
|
|
94
|
+
readonly type: 'skip';
|
|
95
|
+
} | {
|
|
96
|
+
readonly type: 'retry';
|
|
97
|
+
readonly maxAttempts: number;
|
|
98
|
+
} | {
|
|
99
|
+
readonly type: 'fallback';
|
|
100
|
+
readonly nodeId: string;
|
|
40
101
|
};
|
|
41
102
|
export interface WorkflowNodeDefinition<TInput = JsonValue> {
|
|
42
103
|
id: string;
|
|
43
104
|
deps?: string[];
|
|
105
|
+
type?: 'task' | 'switch' | 'workflow';
|
|
106
|
+
failure?: WorkflowFailurePolicy;
|
|
44
107
|
run(ctx: WorkflowNodeContext<TInput>): Promise<JsonValue | NodeResult> | JsonValue | NodeResult;
|
|
45
108
|
}
|
|
46
109
|
export interface WorkflowDefinition<TName extends string = string, TInput = JsonValue, TOutput extends JsonValue = JsonValue> {
|
|
47
110
|
readonly kind: 'workflow';
|
|
48
111
|
readonly name: TName;
|
|
112
|
+
readonly version: string;
|
|
49
113
|
readonly description?: string;
|
|
50
|
-
readonly input?: StandardSchemaV1<any, TInput> | Schema.
|
|
114
|
+
readonly input?: StandardSchemaV1<any, TInput> | Schema.ConstraintDecoder<TInput>;
|
|
51
115
|
readonly nodes: WorkflowNodeDefinition<TInput>[];
|
|
116
|
+
readonly edges?: readonly WorkflowEdge[];
|
|
52
117
|
readonly concurrency?: number;
|
|
118
|
+
readonly inputLimitBytes?: number;
|
|
119
|
+
readonly outputLimitBytes?: number;
|
|
120
|
+
readonly checkpointLimitBytes?: number;
|
|
53
121
|
output?(ctx: {
|
|
54
122
|
input: TInput;
|
|
55
123
|
results: {
|
|
@@ -58,25 +126,13 @@ export interface WorkflowDefinition<TName extends string = string, TInput = Json
|
|
|
58
126
|
}): TOutput;
|
|
59
127
|
}
|
|
60
128
|
export type AnyWorkflowDefinition = WorkflowDefinition;
|
|
61
|
-
export declare function defineWorkflow<TName extends string,
|
|
62
|
-
|
|
63
|
-
description?: string;
|
|
64
|
-
input?: TSchema;
|
|
65
|
-
nodes: Array<{
|
|
66
|
-
id: string;
|
|
67
|
-
deps?: string[];
|
|
68
|
-
run: (ctx: WorkflowNodeContext<TInput>) => Promise<JsonValue | NodeResult> | JsonValue | NodeResult;
|
|
69
|
-
}>;
|
|
70
|
-
concurrency?: number;
|
|
71
|
-
output?: (ctx: {
|
|
72
|
-
input: TInput;
|
|
73
|
-
results: {
|
|
74
|
-
[nodeId: string]: JsonValue | null;
|
|
75
|
-
};
|
|
76
|
-
}) => TOutput;
|
|
129
|
+
export declare function defineWorkflow<TName extends string, TInput = JsonValue, TOutput extends JsonValue = JsonValue>(def: Omit<WorkflowDefinition<TName, TInput, TOutput>, 'kind' | 'version'> & {
|
|
130
|
+
readonly version?: string;
|
|
77
131
|
}): WorkflowDefinition<TName, TInput, TOutput>;
|
|
78
132
|
export declare function readyNodes(workflow: WorkflowDefinition, nodeStates: {
|
|
79
133
|
[nodeId: string]: {
|
|
80
134
|
status: string;
|
|
81
135
|
};
|
|
82
136
|
}): string[];
|
|
137
|
+
/** Builds the helpers a node's `run` uses to describe durable work. */
|
|
138
|
+
export declare function nodeResultBuilders(): Pick<WorkflowNodeContext, 'spawn' | 'sleep' | 'effects' | 'map' | 'fanout' | 'loop'>;
|