@voltro/workflow 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 +52 -0
- package/LICENSE +57 -0
- package/README.md +26 -0
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +409 -0
- package/dist/cluster-Cl6mB2rW.js +31 -0
- package/dist/cluster.d.ts +112 -0
- package/dist/cluster.js +2 -0
- package/dist/clusterTestSuite.d.ts +84 -0
- package/dist/clusterTestSuite.js +180 -0
- package/dist/index.d.ts +865 -0
- package/dist/index.js +3 -0
- package/dist/primitives-CWy1iu5w.js +111 -0
- package/dist/primitives.d.ts +229 -0
- package/dist/primitives.js +2 -0
- package/dist/src-CNeLb-4L.js +817 -0
- package/package.json +68 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,865 @@
|
|
|
1
|
+
import { Cause } from 'effect';
|
|
2
|
+
import { Context } from 'effect';
|
|
3
|
+
import { DurableClock as durableClock } from '@effect/workflow';
|
|
4
|
+
import { DurableQueue as durableQueueModule } from '@effect/workflow';
|
|
5
|
+
import { DurableRateLimiter as durableRateLimiterModule } from '@effect/workflow';
|
|
6
|
+
import { Effect } from 'effect';
|
|
7
|
+
import { FiberRef } from 'effect';
|
|
8
|
+
import { Layer } from 'effect';
|
|
9
|
+
import { ParseResult } from 'effect';
|
|
10
|
+
import { QueryDescriptor } from '@voltro/database';
|
|
11
|
+
import { Row } from '@voltro/database';
|
|
12
|
+
import { Schema } from 'effect';
|
|
13
|
+
import { SchemaTable } from '@voltro/database';
|
|
14
|
+
import { Activity as stepModule } from '@effect/workflow';
|
|
15
|
+
import { Subject } from '@voltro/protocol';
|
|
16
|
+
import { TableLike } from '@voltro/database';
|
|
17
|
+
import { WorkflowEngine } from '@effect/workflow';
|
|
18
|
+
import { Workflow as workflowModule } from '@effect/workflow';
|
|
19
|
+
import { WorkflowParentClosePolicy } from '@voltro/protocol';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Wait inside a workflow body for an externally-injected signal.
|
|
23
|
+
*
|
|
24
|
+
* Usage:
|
|
25
|
+
* ```ts
|
|
26
|
+
* const buildExecute = (ctx: AppContext) =>
|
|
27
|
+
* (input: { ... }) =>
|
|
28
|
+
* Effect.gen(function* () {
|
|
29
|
+
* // ... earlier steps ...
|
|
30
|
+
* const decision = yield* awaitSignal(ctx, {
|
|
31
|
+
* name: 'approval',
|
|
32
|
+
* schema: Schema.Struct({ approved: Schema.Boolean }),
|
|
33
|
+
* })
|
|
34
|
+
* // `decision: { approved: boolean }` — typed by the schema.
|
|
35
|
+
* if (!decision.approved) return { status: 'rejected' as const }
|
|
36
|
+
* // ... continue ...
|
|
37
|
+
* })
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* From the dashboard, click "Send signal" on the run row and pick
|
|
41
|
+
* `approval` with a JSON body matching the schema.
|
|
42
|
+
*/
|
|
43
|
+
export declare const awaitSignal: <A>(ctx: AwaitSignalCtx, options: AwaitSignalOptions<A>) => Effect.Effect<A, Cause.UnknownException | ParseResult.ParseError>;
|
|
44
|
+
|
|
45
|
+
/** The slice of `ctx.store` awaitSignal needs — just `query`, typed against
|
|
46
|
+
* the real `QueryDescriptor` so a full `AppContext` store (`DataStore`) is
|
|
47
|
+
* assignable. This is what makes the documented `awaitSignal(ctx, …)` call
|
|
48
|
+
* inside a workflow executor typecheck. (`@voltro/workflow` already depends on
|
|
49
|
+
* `@voltro/database` — see the `and`/`eq` import above — so this adds no new
|
|
50
|
+
* package edge.) */
|
|
51
|
+
export declare interface AwaitSignalCtx {
|
|
52
|
+
readonly store: {
|
|
53
|
+
readonly query: (descriptor: QueryDescriptor) => Promise<ReadonlyArray<unknown>>;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export declare interface AwaitSignalOptions<A> {
|
|
58
|
+
/** Caller-chosen signal name. Mirror this in the dashboard's "Send
|
|
59
|
+
* signal" modal — the inspect bus matches on this exact string. */
|
|
60
|
+
readonly name: string;
|
|
61
|
+
/** Schema the incoming payload is parsed against. Parse failures
|
|
62
|
+
* fail the activity with a typed error (the schema's ParseError);
|
|
63
|
+
* fix the sender's payload shape and re-send. */
|
|
64
|
+
readonly schema: Schema.Schema<A, any>;
|
|
65
|
+
/** Initial poll interval in milliseconds. Default 200 ms — gives
|
|
66
|
+
* sub-second wake for fresh signals. Subsequent polls back off
|
|
67
|
+
* exponentially up to `maxPollIntervalMs`. */
|
|
68
|
+
readonly pollIntervalMs?: number;
|
|
69
|
+
/** Upper bound on the exponential backoff. Default 5000 ms. The
|
|
70
|
+
* poll loop never sleeps longer than this between checks. */
|
|
71
|
+
readonly maxPollIntervalMs?: number;
|
|
72
|
+
/** Max time to wait before failing the activity. Default 24 h.
|
|
73
|
+
* Pass `Number.POSITIVE_INFINITY` for an unbounded wait. */
|
|
74
|
+
readonly timeoutMs?: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Wait inside a workflow body for an externally-injected signal, SUSPENDING
|
|
79
|
+
* the run (freeing its worker) until the signal arrives or the timeout fires.
|
|
80
|
+
*
|
|
81
|
+
* Drop-in shape-compatible with {@link awaitSignal}; prefer this variant for
|
|
82
|
+
* waits that may last minutes/hours/days (human-in-the-loop, AI-Flows) so a
|
|
83
|
+
* parked run does not pin a fiber.
|
|
84
|
+
*
|
|
85
|
+
* ```ts
|
|
86
|
+
* const decision = yield* awaitSignalSuspending(ctx, {
|
|
87
|
+
* name: 'approval',
|
|
88
|
+
* schema: Schema.Struct({ approved: Schema.Boolean }),
|
|
89
|
+
* })
|
|
90
|
+
* // `decision: { approved: boolean }` — the run was Suspended until an
|
|
91
|
+
* // external `ctx.workflows.signal(target, 'approval', { approved })` resumed it.
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* `ctx` is accepted for signature parity with `awaitSignal` (so the two are
|
|
95
|
+
* interchangeable in a body); the suspending variant reads the durable
|
|
96
|
+
* deferred, not `ctx.store`.
|
|
97
|
+
*/
|
|
98
|
+
export declare const awaitSignalSuspending: <A>(ctx: AwaitSignalCtx, options: AwaitSignalSuspendingOptions<A>) => Effect.Effect<A, ParseResult.ParseError>;
|
|
99
|
+
|
|
100
|
+
export declare interface AwaitSignalSuspendingOptions<A> {
|
|
101
|
+
/** Caller-chosen signal name. The external completer targets this exact
|
|
102
|
+
* string — mirror it in the dashboard's "Send signal" modal. */
|
|
103
|
+
readonly name: string;
|
|
104
|
+
/** Schema the incoming payload is decoded against. A decode failure fails
|
|
105
|
+
* the workflow with the schema's `ParseError`. */
|
|
106
|
+
readonly schema: Schema.Schema<A, any>;
|
|
107
|
+
/** Max time to wait before failing the workflow. Default 24 h. The timeout
|
|
108
|
+
* is itself durable (a raced `DurableClock`), so a long wait frees the
|
|
109
|
+
* runner too. Pass `Number.POSITIVE_INFINITY` for an unbounded wait. */
|
|
110
|
+
readonly timeoutMs?: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export declare const awaitUpdate: <A, B = A>(ctx: AwaitUpdateCtx, options: AwaitUpdateOptions<A, B>) => Effect.Effect<B, unknown, never>;
|
|
114
|
+
|
|
115
|
+
/** The `ctx.store` slice awaitUpdate needs, typed against the real
|
|
116
|
+
* `QueryDescriptor` so a full `AppContext` store (`DataStore`) is assignable —
|
|
117
|
+
* i.e. so the documented `awaitUpdate(ctx, …)` call inside an executor
|
|
118
|
+
* typechecks. Mirrors `AwaitSignalCtx`. */
|
|
119
|
+
export declare interface AwaitUpdateCtx {
|
|
120
|
+
readonly store: {
|
|
121
|
+
readonly query: (descriptor: QueryDescriptor) => Promise<ReadonlyArray<unknown>>;
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export declare interface AwaitUpdateOptions<A, B = A> {
|
|
126
|
+
readonly name: string;
|
|
127
|
+
readonly schema: Schema.Schema<A, any>;
|
|
128
|
+
readonly success?: Schema.Schema<B, any>;
|
|
129
|
+
readonly handle?: (payload: A) => Effect.Effect<B, unknown, never>;
|
|
130
|
+
readonly pollIntervalMs?: number;
|
|
131
|
+
readonly maxPollIntervalMs?: number;
|
|
132
|
+
readonly timeoutMs?: number;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export declare const closeWorkflowChildrenForParent: (options: CloseWorkflowChildrenForParentOptions) => Promise<ReadonlyArray<WorkflowParentCloseDecision>>;
|
|
136
|
+
|
|
137
|
+
export declare interface CloseWorkflowChildrenForParentOptions {
|
|
138
|
+
readonly store: WorkflowParentCloseStore;
|
|
139
|
+
readonly parentExecutionId: string;
|
|
140
|
+
readonly parentStatus: WorkflowParentCloseStatus;
|
|
141
|
+
readonly recorder?: WorkflowRunRecorderService;
|
|
142
|
+
readonly emit?: WorkflowParentCloseEmitter['emit'];
|
|
143
|
+
readonly log?: WorkflowParentCloseLogger;
|
|
144
|
+
readonly limit?: number;
|
|
145
|
+
readonly interruptChild: (workflowName: string, executionId: string, policy: Exclude<WorkflowParentClosePolicy, 'abandon'>) => Promise<void>;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Externally complete a suspending signal wait, resuming the parked run with
|
|
150
|
+
* `payload`. The counterpart to {@link awaitSignalSuspending} and the
|
|
151
|
+
* suspending analogue of `sendWorkflowSignal`.
|
|
152
|
+
*
|
|
153
|
+
* Returns an `Effect` that requires the `WorkflowEngine` — run it through the
|
|
154
|
+
* same runner that executes workflows (the CLI's `workflowRuntime.runPromise`,
|
|
155
|
+
* i.e. the facade's `runEffect`), so `DurableDeferred.done` reaches the engine.
|
|
156
|
+
*
|
|
157
|
+
* Completing a deferred that no run is awaiting is a no-op, so it is safe to
|
|
158
|
+
* call this for every signal alongside `sendWorkflowSignal` — the polling
|
|
159
|
+
* `awaitSignal` reads the row, the suspending variant reads the deferred, and
|
|
160
|
+
* whichever the target workflow uses resumes.
|
|
161
|
+
*/
|
|
162
|
+
export declare const completeSuspendingSignal: (input: CompleteSuspendingSignalInput) => Effect.Effect<{
|
|
163
|
+
readonly completed: boolean;
|
|
164
|
+
}, Cause.UnknownException, WorkflowEngine.WorkflowEngine>;
|
|
165
|
+
|
|
166
|
+
/** Input to {@link completeSuspendingSignal}. */
|
|
167
|
+
export declare interface CompleteSuspendingSignalInput {
|
|
168
|
+
/** Which run to resume. If it carries BOTH `executionId` and `workflowName`
|
|
169
|
+
* the store lookup is skipped; otherwise `store` is used to resolve them. */
|
|
170
|
+
readonly target: WorkflowMessageTarget;
|
|
171
|
+
/** Signal name — must match the workflow's `awaitSignalSuspending({ name })`. */
|
|
172
|
+
readonly signalName: string;
|
|
173
|
+
/** Payload delivered to the workflow. Stored raw and decoded on the awaiting
|
|
174
|
+
* side through that call's `schema`. */
|
|
175
|
+
readonly payload?: unknown;
|
|
176
|
+
/** Store used to resolve the run's `executionId` + workflow name when the
|
|
177
|
+
* target doesn't already provide both. */
|
|
178
|
+
readonly store?: WorkflowMessageStore;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** FiberRef carrying the active workflow's executionId — set by
|
|
182
|
+
* `wrapWithRunRecording` on every workflow body entry, read by a
|
|
183
|
+
* child workflow's recorder to populate
|
|
184
|
+
* `_voltro_workflow_runs.parentExecutionId`. Distinct
|
|
185
|
+
* from `CurrentWorkflowRunId` (which is the runs-row primary key)
|
|
186
|
+
* because the natural durable id used by other workflows is the
|
|
187
|
+
* engine-assigned executionId, not our row id. */
|
|
188
|
+
export declare const CurrentWorkflowExecutionId: FiberRef.FiberRef<string | undefined>;
|
|
189
|
+
|
|
190
|
+
/** FiberRef carrying the active workflow run's id. The outer
|
|
191
|
+
* `wrapWithRunRecording` in dev.ts sets it via
|
|
192
|
+
* `Effect.locally(CurrentWorkflowRunId, runId)`. */
|
|
193
|
+
export declare const CurrentWorkflowRunId: FiberRef.FiberRef<string | undefined>;
|
|
194
|
+
|
|
195
|
+
export { durableClock }
|
|
196
|
+
|
|
197
|
+
/** Define a durable queue for concurrency-controlled side work. */
|
|
198
|
+
export declare const durableQueue: typeof durableQueueModule.make;
|
|
199
|
+
|
|
200
|
+
export { durableQueueModule }
|
|
201
|
+
|
|
202
|
+
export { durableRateLimiterModule }
|
|
203
|
+
|
|
204
|
+
/** Try to read the current workflow execution id. Returns undefined
|
|
205
|
+
* when called outside a recorded workflow body. */
|
|
206
|
+
export declare const getCurrentWorkflowExecutionId: () => Effect.Effect<string | undefined>;
|
|
207
|
+
|
|
208
|
+
/** Try to read the current run id. Returns undefined when not in a
|
|
209
|
+
* recorded workflow context (= step is being run outside the
|
|
210
|
+
* framework's wrapper, e.g. in a unit test). */
|
|
211
|
+
export declare const getCurrentWorkflowRunId: () => Effect.Effect<string | undefined>;
|
|
212
|
+
|
|
213
|
+
export declare const getWorkflowVersionMetadata: (value: unknown) => WorkflowVersionMetadata;
|
|
214
|
+
|
|
215
|
+
export declare interface InMemoryRecorder {
|
|
216
|
+
readonly layer: Layer.Layer<WorkflowRunRecorder>;
|
|
217
|
+
readonly readSteps: () => ReadonlyArray<RecordedStep>;
|
|
218
|
+
readonly readEvents: () => ReadonlyArray<RecordedEvent>;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export declare const inMemoryWorkflowEngineLayer: Layer.Layer<WorkflowEngine.WorkflowEngine>;
|
|
222
|
+
|
|
223
|
+
export declare interface InspectedStep {
|
|
224
|
+
readonly name: string;
|
|
225
|
+
/** The attempt number of THIS row (1 on first try, 2+ on retry). When
|
|
226
|
+
* a step has multiple attempts, the assembled step reflects the
|
|
227
|
+
* latest attempt's status/output/error. */
|
|
228
|
+
readonly attempt: number;
|
|
229
|
+
/** Total attempts recorded for this stepName (row count). */
|
|
230
|
+
readonly attempts: number;
|
|
231
|
+
readonly status: 'running' | 'succeeded' | 'failed';
|
|
232
|
+
readonly input: unknown;
|
|
233
|
+
readonly output: unknown;
|
|
234
|
+
readonly error: {
|
|
235
|
+
readonly tag: string | null;
|
|
236
|
+
readonly message: string;
|
|
237
|
+
} | null;
|
|
238
|
+
readonly durationMs: number | null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export declare interface InspectedWorkflow {
|
|
242
|
+
readonly id: string;
|
|
243
|
+
readonly executionId: string;
|
|
244
|
+
readonly name: string;
|
|
245
|
+
readonly status: 'running' | 'succeeded' | 'failed' | 'cancelled' | 'suspended';
|
|
246
|
+
readonly input: unknown;
|
|
247
|
+
readonly output: unknown;
|
|
248
|
+
readonly subject: Subject | null;
|
|
249
|
+
readonly source: string | null;
|
|
250
|
+
readonly steps: ReadonlyArray<InspectedStep>;
|
|
251
|
+
readonly startedAt: Date;
|
|
252
|
+
readonly finishedAt: Date | null;
|
|
253
|
+
readonly traceId: string | null;
|
|
254
|
+
readonly parentExecutionId: string | null;
|
|
255
|
+
readonly parentClosePolicy: WorkflowParentClosePolicy | null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** Minimal store surface `inspectWorkflow` needs: descriptor-in /
|
|
259
|
+
* rows-out. Satisfied by `FluentStore`, `DataStore`, and
|
|
260
|
+
* `InMemoryDataStore`. */
|
|
261
|
+
export declare interface InspectStore {
|
|
262
|
+
query(descriptor: QueryDescriptor): Promise<ReadonlyArray<Row>>;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Resolve a workflow run by its `id` OR its `executionId`, assemble the
|
|
267
|
+
* full inspected shape (run header + per-step attempts), and return it.
|
|
268
|
+
* Returns `null` when no run matches.
|
|
269
|
+
*
|
|
270
|
+
* @param idOrExecutionId `_voltro_workflow_runs.id` or `.executionId`.
|
|
271
|
+
* @param store any `query`-capable store (fluent `ctx.store`,
|
|
272
|
+
* `InMemoryDataStore`, or a live SQL `DataStore`).
|
|
273
|
+
*/
|
|
274
|
+
export declare const inspectWorkflow: (idOrExecutionId: string, store: InspectStore) => Promise<InspectedWorkflow | null>;
|
|
275
|
+
|
|
276
|
+
export declare const isWorkflowWorkerLayer: (value: unknown) => value is WorkflowWorkerLayerBrand;
|
|
277
|
+
|
|
278
|
+
export declare const makeInMemoryRecorder: () => InMemoryRecorder;
|
|
279
|
+
|
|
280
|
+
export declare const makeWorkflowRunRecorder: (options: WorkflowRunRecorderOptions) => WorkflowRunRecorderService;
|
|
281
|
+
|
|
282
|
+
export declare const makeWorkflowUpdateId: () => string;
|
|
283
|
+
|
|
284
|
+
declare type NormaliseWorkflowMessages<M extends WorkflowMessageSchemas | undefined> = {
|
|
285
|
+
readonly signals: M extends {
|
|
286
|
+
readonly signals: infer Signals;
|
|
287
|
+
} ? NonNullable<Signals> & Readonly<Record<string, Schema.Schema.Any>> : {};
|
|
288
|
+
readonly updates: M extends {
|
|
289
|
+
readonly updates: infer Updates;
|
|
290
|
+
} ? NonNullable<Updates> & Readonly<Record<string, WorkflowMessagePairSchemas>> : {};
|
|
291
|
+
readonly queries: M extends {
|
|
292
|
+
readonly queries: infer Queries;
|
|
293
|
+
} ? NonNullable<Queries> & Readonly<Record<string, WorkflowMessagePairSchemas>> : {};
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/** Enqueue work and await the durable queue worker's result. */
|
|
297
|
+
export declare const processQueue: typeof durableQueueModule.process;
|
|
298
|
+
|
|
299
|
+
/** Start a durable queue worker layer with bounded concurrency.
|
|
300
|
+
*
|
|
301
|
+
* `DurableQueue.worker` is itself generic (`<Payload, Success, Error, R>`), so
|
|
302
|
+
* a hand-written wrapper can't re-state that generic signature — the body runs
|
|
303
|
+
* against erased args and the const's explicit `: typeof DurableQueue.worker`
|
|
304
|
+
* annotation restores the full generic public type. That annotation (not an
|
|
305
|
+
* inferred type) is what emits in the `.d.ts`, so it stays portable. */
|
|
306
|
+
export declare const queueWorker: typeof durableQueueModule.worker;
|
|
307
|
+
|
|
308
|
+
/** Durable rate limiter activity. Delays through the workflow clock. */
|
|
309
|
+
export declare const rateLimit: typeof durableRateLimiterModule.rateLimit;
|
|
310
|
+
|
|
311
|
+
/** One recorded run-event — mirrors a `_voltro_workflow_run_events` row. */
|
|
312
|
+
export declare interface RecordedEvent {
|
|
313
|
+
readonly id: string;
|
|
314
|
+
readonly runId: string;
|
|
315
|
+
readonly eventType: WorkflowRunEventType;
|
|
316
|
+
readonly payload: Record<string, unknown> | null;
|
|
317
|
+
readonly stepName: string | null;
|
|
318
|
+
readonly attempt: number | null;
|
|
319
|
+
readonly occurredAt: Date;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** One recorded step attempt — mirrors a `_voltro_workflow_run_steps`
|
|
323
|
+
* row closely enough for inspection. One row per attempt. */
|
|
324
|
+
export declare interface RecordedStep {
|
|
325
|
+
readonly id: string;
|
|
326
|
+
readonly runId: string;
|
|
327
|
+
readonly stepName: string;
|
|
328
|
+
readonly attempt: number;
|
|
329
|
+
status: 'running' | 'succeeded' | 'failed';
|
|
330
|
+
readonly input: unknown;
|
|
331
|
+
readonly retryPolicy: StepRetryPolicy | null;
|
|
332
|
+
output: unknown;
|
|
333
|
+
errorTag: string | null;
|
|
334
|
+
errorMessage: string | null;
|
|
335
|
+
errorCause: {
|
|
336
|
+
readonly pretty: string;
|
|
337
|
+
readonly failures: ReadonlyArray<unknown>;
|
|
338
|
+
readonly defects: ReadonlyArray<unknown>;
|
|
339
|
+
} | null;
|
|
340
|
+
readonly startedAt: Date;
|
|
341
|
+
completedAt: Date | null;
|
|
342
|
+
durationMs: number | null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export declare const resolveWorkflowMessageRun: (store: WorkflowMessageStore, target: WorkflowMessageTarget) => Promise<WorkflowResolvedRun>;
|
|
346
|
+
|
|
347
|
+
export declare const sendWorkflowSignal: (input: {
|
|
348
|
+
readonly store: WorkflowMessageStore;
|
|
349
|
+
readonly recorder: WorkflowRunRecorderService;
|
|
350
|
+
readonly target: WorkflowMessageTarget;
|
|
351
|
+
readonly signalName: string;
|
|
352
|
+
readonly payload?: unknown;
|
|
353
|
+
}) => Promise<{
|
|
354
|
+
readonly eventId: string;
|
|
355
|
+
}>;
|
|
356
|
+
|
|
357
|
+
export declare const sendWorkflowUpdate: (input: {
|
|
358
|
+
readonly store: WorkflowMessageStore;
|
|
359
|
+
readonly recorder: WorkflowRunRecorderService;
|
|
360
|
+
readonly target: WorkflowMessageTarget;
|
|
361
|
+
readonly updateName: string;
|
|
362
|
+
readonly payload?: unknown;
|
|
363
|
+
readonly options?: WorkflowUpdateSendOptions;
|
|
364
|
+
}) => Promise<WorkflowUpdateResult>;
|
|
365
|
+
|
|
366
|
+
export declare const serialiseWorkflowRowForWire: (row: Record<string, unknown>) => Record<string, unknown>;
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Durable sleep — wake time journaled in the cluster. Wrapper around
|
|
370
|
+
* `DurableClock.sleep` that emits `timer-set` / `timer-fired` events
|
|
371
|
+
* to the recorder so the dashboard's Gantt timeline renders the wait
|
|
372
|
+
* as a visible bar — a workflow waiting on a cron-style delay should
|
|
373
|
+
* not look like a black hole.
|
|
374
|
+
*
|
|
375
|
+
* The wrap is transparent: signature matches `DurableClock.sleep`,
|
|
376
|
+
* no observable behavior change beyond the event emissions. When no
|
|
377
|
+
* recorder is in scope (unit tests, no `voltro dev`/`voltro start`
|
|
378
|
+
* wrapper around the workflow) it's a zero-overhead passthrough.
|
|
379
|
+
*/
|
|
380
|
+
export declare const sleep: typeof durableClock.sleep;
|
|
381
|
+
|
|
382
|
+
export declare const step: <R, Success extends Schema.Schema.Any = typeof Schema.Void, Error extends Schema.Schema.All = typeof Schema.Never>(options: StepOptions<R, Success, Error>) => ReturnType<typeof stepModule.make<R, Success, Error>>;
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Derive a deterministic idempotency key for the current workflow run,
|
|
386
|
+
* scoped to `name`. Call it inside a `step()` so a retried external
|
|
387
|
+
* side-effect converges on a single key instead of double-creating —
|
|
388
|
+
* pair it with a read-before-write provider-operations ledger. Pass
|
|
389
|
+
* `{ includeAttempt: true }` to vary the key per attempt.
|
|
390
|
+
*
|
|
391
|
+
* Named for the framework's `step` vocabulary (it is upstream's
|
|
392
|
+
* `Activity.idempotencyKey`).
|
|
393
|
+
*/
|
|
394
|
+
export declare const stepIdempotencyKey: typeof stepModule.idempotencyKey;
|
|
395
|
+
|
|
396
|
+
export { stepModule }
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Define a checkpointed step (activity) inside a workflow body.
|
|
400
|
+
*
|
|
401
|
+
* Wraps `@effect/workflow`'s `Activity.make` to record every
|
|
402
|
+
* invocation in `_voltro_workflow_run_steps`. The wrapping is
|
|
403
|
+
* transparent — accepts everything `Activity.make` accepts, plus an
|
|
404
|
+
* optional `input` field whose value is persisted to the step row
|
|
405
|
+
* (truncated to 8 KB by the recorder). The dashboard surfaces it as
|
|
406
|
+
* "Input" alongside Output, so operators can answer "what arguments
|
|
407
|
+
* did this step receive?" without grep-ing logs.
|
|
408
|
+
*
|
|
409
|
+
* ```ts
|
|
410
|
+
* step({
|
|
411
|
+
* name: 'fetch-todos',
|
|
412
|
+
* input: { tenantId, since: lastFetch }, // ← optional, recorded
|
|
413
|
+
* execute: Effect.tryPromise({ try: () => ctx.store.query(...) }),
|
|
414
|
+
* })
|
|
415
|
+
* ```
|
|
416
|
+
*
|
|
417
|
+
* Without the recorder service in scope (unit tests, etc.) the input
|
|
418
|
+
* is dropped and the wrap is a no-op.
|
|
419
|
+
*/
|
|
420
|
+
declare type StepOptions<R, Success extends Schema.Schema.Any, Error extends Schema.Schema.All> = {
|
|
421
|
+
readonly name: string;
|
|
422
|
+
readonly success?: Success | undefined;
|
|
423
|
+
readonly error?: Error | undefined;
|
|
424
|
+
readonly execute: Effect.Effect<Success['Type'], Error['Type'], R>;
|
|
425
|
+
readonly interruptRetryPolicy?: Parameters<typeof stepModule.make<R, Success, Error>>[0]['interruptRetryPolicy'];
|
|
426
|
+
/** Structured value recorded into `_voltro_workflow_run_steps.input`.
|
|
427
|
+
* Pass whatever makes the step debuggable in isolation. */
|
|
428
|
+
readonly input?: unknown;
|
|
429
|
+
/** Declarative retry-policy summary recorded into
|
|
430
|
+
* `_voltro_workflow_run_steps.retryPolicy`. Pure metadata — does
|
|
431
|
+
* NOT change retry behavior on its own. Wire actual retries via
|
|
432
|
+
* `Effect.retry` inside `execute:` or via `interruptRetryPolicy`
|
|
433
|
+
* above; this field tells the dashboard what the intended policy
|
|
434
|
+
* looks like so the per-attempt panel can show "attempt 2 of 5,
|
|
435
|
+
* exponential 1s base" instead of just "attempt 2". */
|
|
436
|
+
readonly retry?: StepRetryPolicy;
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Declarative retry-policy summary. Pure metadata for the dashboard;
|
|
441
|
+
* the framework does NOT apply this. Users wire actual retry behavior
|
|
442
|
+
* via `Effect.retry(Schedule.*)` inside `execute:` or
|
|
443
|
+
* `Activity.make`'s `interruptRetryPolicy`.
|
|
444
|
+
*
|
|
445
|
+
* Shape is intentionally close to Effect's Schedule combinators so a
|
|
446
|
+
* future framework version can auto-translate declarative policies
|
|
447
|
+
* into a Schedule + apply it. For now: record + display.
|
|
448
|
+
*
|
|
449
|
+
* { strategy: 'exponential', maxAttempts: 5, baseDelay: '1 second', maxDelay: '30 seconds' }
|
|
450
|
+
* { strategy: 'fixed', maxAttempts: 3, baseDelay: '500 millis' }
|
|
451
|
+
* { strategy: 'linear', maxAttempts: 4, baseDelay: '1 second', step: '2 seconds' }
|
|
452
|
+
*/
|
|
453
|
+
export declare interface StepRetryPolicy {
|
|
454
|
+
readonly strategy: 'exponential' | 'fixed' | 'linear';
|
|
455
|
+
readonly maxAttempts: number;
|
|
456
|
+
/** Effect Duration string, e.g. `'1 second'`, `'500 millis'`. */
|
|
457
|
+
readonly baseDelay?: string;
|
|
458
|
+
/** Cap on exponential / linear growth, e.g. `'30 seconds'`. */
|
|
459
|
+
readonly maxDelay?: string;
|
|
460
|
+
/** Increment for `linear` strategy. */
|
|
461
|
+
readonly step?: string;
|
|
462
|
+
/** Free-form note rendered as a chip in the dashboard. Useful when
|
|
463
|
+
* the retry behavior is partially implemented in the user's
|
|
464
|
+
* Effect chain and the summary is just documentation. */
|
|
465
|
+
readonly note?: string;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* The stable `DurableDeferred` name for a suspending signal wait. It is a pure
|
|
470
|
+
* function of the workflow name + the signal name, so the awaiting body and an
|
|
471
|
+
* external completer derive the SAME name without any shared state — the
|
|
472
|
+
* engine keys the deferred result by `<executionId>/<this name>`.
|
|
473
|
+
*/
|
|
474
|
+
export declare const suspendingSignalDeferredName: (workflowName: string, signalName: string) => string;
|
|
475
|
+
|
|
476
|
+
export declare const truncateWorkflowValue: (value: unknown, bytes?: number) => unknown;
|
|
477
|
+
|
|
478
|
+
export declare const _voltroWorkflowRunEventsTable: SchemaTable;
|
|
479
|
+
|
|
480
|
+
export declare const _voltroWorkflowRunsTable: SchemaTable;
|
|
481
|
+
|
|
482
|
+
export declare const _voltroWorkflowRunStepsTable: SchemaTable;
|
|
483
|
+
|
|
484
|
+
export declare const _voltroWorkflowStartContextsTable: TableLike;
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Add compensating (rollback) logic to a top-level effect in a workflow
|
|
488
|
+
* body. The finalizer runs if the WHOLE workflow later fails — use it for
|
|
489
|
+
* saga-style undo: release a half-acquired resource, revert a partial
|
|
490
|
+
* promotion. Upstream constraint: compensation is registered only for
|
|
491
|
+
* top-level effects in the workflow, NOT for nested steps/activities.
|
|
492
|
+
*
|
|
493
|
+
* Pinned with `typeof` to keep the emitted .d.ts portable (see the TS2742
|
|
494
|
+
* note above the type-explicit re-exports).
|
|
495
|
+
*
|
|
496
|
+
* ```ts
|
|
497
|
+
* yield* withCompensation(
|
|
498
|
+
* step({ name: 'provision-db', execute: ... }),
|
|
499
|
+
* (resource) => releaseResource(resource), // runs only if the workflow fails
|
|
500
|
+
* )
|
|
501
|
+
* ```
|
|
502
|
+
*/
|
|
503
|
+
export declare const withCompensation: typeof workflowModule.withCompensation;
|
|
504
|
+
|
|
505
|
+
/** Define a durable workflow. Adds Voltro version metadata on top of `Workflow.make`. */
|
|
506
|
+
export declare const workflow: WorkflowFn;
|
|
507
|
+
|
|
508
|
+
export declare interface WorkflowExecuteRecordingOptions extends WorkflowRunRecorderOptions {
|
|
509
|
+
readonly name: string;
|
|
510
|
+
readonly subject?: unknown;
|
|
511
|
+
readonly source?: string | null;
|
|
512
|
+
readonly traceId?: string | null;
|
|
513
|
+
readonly workflowVersion?: string | null;
|
|
514
|
+
readonly workflowCompatibleVersions?: ReadonlyArray<string> | null;
|
|
515
|
+
readonly workflowPatches?: ReadonlyArray<string> | null;
|
|
516
|
+
readonly parentExecutionId?: string | null;
|
|
517
|
+
readonly parentClosePolicy?: WorkflowParentClosePolicy | null;
|
|
518
|
+
readonly onParentClose?: (input: {
|
|
519
|
+
readonly workflowName: string;
|
|
520
|
+
readonly executionId: string;
|
|
521
|
+
readonly runId?: string;
|
|
522
|
+
readonly status: WorkflowParentCloseStatus;
|
|
523
|
+
}) => Promise<void>;
|
|
524
|
+
readonly recorder?: WorkflowRunRecorderService;
|
|
525
|
+
readonly pluginWorkflowStepLayer?: Layer.Layer<any, never, never>;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/** Callable shape of `workflow`: the raw `Workflow.make` surface PLUS the
|
|
529
|
+
* Voltro-typed overload that threads the message-schema metadata onto the
|
|
530
|
+
* returned definition. Explicit alias so the `.d.ts` names this type by its
|
|
531
|
+
* own alias — the inferred `Workflow.make` type references internal `@effect`
|
|
532
|
+
* modules via `.pnpm` paths and is not portable (TS2742) when re-emitted. */
|
|
533
|
+
declare type WorkflowFn = WorkflowMake & (<const Options extends WorkflowOptions>(options: Options) => ReturnType<WorkflowMake> & WorkflowMessagesCarrier<NormaliseWorkflowMessages<Options['messages']>>);
|
|
534
|
+
|
|
535
|
+
declare type WorkflowMake = typeof workflowModule.make;
|
|
536
|
+
|
|
537
|
+
export declare interface WorkflowMessagePairSchemas {
|
|
538
|
+
readonly payload: Schema.Schema.Any;
|
|
539
|
+
readonly success: Schema.Schema.Any;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
export declare interface WorkflowMessagesCarrier<M extends WorkflowMessagesMetadata = WorkflowMessagesMetadata> {
|
|
543
|
+
readonly [WorkflowMessagesProperty]: M;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export declare interface WorkflowMessageSchemas {
|
|
547
|
+
readonly signals?: Readonly<Record<string, Schema.Schema.Any>>;
|
|
548
|
+
readonly updates?: Readonly<Record<string, WorkflowMessagePairSchemas>>;
|
|
549
|
+
readonly queries?: Readonly<Record<string, WorkflowMessagePairSchemas>>;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
export declare interface WorkflowMessagesMetadata {
|
|
553
|
+
readonly signals: Readonly<Record<string, Schema.Schema.Any>>;
|
|
554
|
+
readonly updates: Readonly<Record<string, WorkflowMessagePairSchemas>>;
|
|
555
|
+
readonly queries: Readonly<Record<string, WorkflowMessagePairSchemas>>;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
export declare const WorkflowMessagesProperty = "__voltroWorkflowMessages";
|
|
559
|
+
|
|
560
|
+
export declare interface WorkflowMessageStore {
|
|
561
|
+
readonly query: (input: QueryDescriptor) => Promise<ReadonlyArray<unknown>>;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
export declare interface WorkflowMessageTarget {
|
|
565
|
+
readonly id?: string;
|
|
566
|
+
readonly executionId?: string;
|
|
567
|
+
/** Workflow name (the `_voltro_workflow_runs.tag` column). Lets a caller
|
|
568
|
+
* that already holds it (e.g. from a `WorkflowRunHandle`) skip the store
|
|
569
|
+
* lookup when completing a suspending signal — see
|
|
570
|
+
* `completeSuspendingSignal`. */
|
|
571
|
+
readonly workflowName?: string;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
export { workflowModule }
|
|
575
|
+
|
|
576
|
+
declare type WorkflowOptions = Parameters<WorkflowMake>[0] & WorkflowVersionOptions & {
|
|
577
|
+
readonly messages?: WorkflowMessageSchemas;
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
export declare interface WorkflowParentCloseDecision {
|
|
581
|
+
readonly runId: string;
|
|
582
|
+
readonly workflowName: string;
|
|
583
|
+
readonly executionId: string;
|
|
584
|
+
readonly policy: WorkflowParentClosePolicy | null;
|
|
585
|
+
readonly action: 'cancelled' | 'abandoned' | 'skipped' | 'failed';
|
|
586
|
+
readonly reason?: string;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
export declare interface WorkflowParentCloseEmitter {
|
|
590
|
+
emit(channel: 'workflowRuns' | 'workflowSteps' | 'workflowEvents', event: {
|
|
591
|
+
readonly op: 'insert' | 'update';
|
|
592
|
+
readonly row: Record<string, unknown>;
|
|
593
|
+
}): void;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
export declare interface WorkflowParentCloseLogger {
|
|
597
|
+
warn(message: string, fields?: Record<string, unknown>): void;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
export declare type WorkflowParentCloseStatus = 'succeeded' | 'failed' | 'cancelled';
|
|
601
|
+
|
|
602
|
+
export declare interface WorkflowParentCloseStore {
|
|
603
|
+
query(descriptor: QueryDescriptor): Promise<ReadonlyArray<unknown>>;
|
|
604
|
+
update(table: string, id: string, patch: unknown): Promise<unknown | null>;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
export declare interface WorkflowRecordingEmitter {
|
|
608
|
+
emit(channel: 'workflowRuns' | 'workflowSteps' | 'workflowEvents', event: {
|
|
609
|
+
readonly op: 'insert' | 'update';
|
|
610
|
+
readonly row: Record<string, unknown>;
|
|
611
|
+
}): void;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
export declare interface WorkflowRecordingLogger {
|
|
615
|
+
warn(message: string, fields?: Record<string, unknown>): void;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
export declare interface WorkflowRecordingStore {
|
|
619
|
+
insert(table: string, row: unknown): Promise<unknown>;
|
|
620
|
+
update(table: string, id: string, patch: unknown): Promise<unknown | null>;
|
|
621
|
+
query?(descriptor: unknown): Promise<ReadonlyArray<unknown>>;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
export declare interface WorkflowResolvedRun {
|
|
625
|
+
readonly id: string;
|
|
626
|
+
readonly executionId?: string;
|
|
627
|
+
/** Denormalised workflow name (`_voltro_workflow_runs.tag`). Present because
|
|
628
|
+
* the resolve query projects all columns; used to key a suspending signal's
|
|
629
|
+
* `DurableDeferred` by workflow name + signal name. */
|
|
630
|
+
readonly tag?: string;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Closed enumeration of event types the framework knows how to emit.
|
|
635
|
+
* Kept here (not in `runEventsTable.ts`) so callers in the recorder
|
|
636
|
+
* + dev.ts share a single source of truth. The TABLE column is
|
|
637
|
+
* open-text on purpose (future phases add new types without a column
|
|
638
|
+
* migration) — but at emit time we still want type-safety.
|
|
639
|
+
*/
|
|
640
|
+
export declare type WorkflowRunEventType = 'run-started' | 'run-suspended' | 'run-resumed' | 'run-cancelled' | 'run-succeeded' | 'run-failed'
|
|
641
|
+
/** Emitted when `sleep({ name, duration })` enters the wait — `payload`
|
|
642
|
+
* carries `{ name, durationMs, scheduledWakeAt }`. The dashboard
|
|
643
|
+
* Gantt renders these as a hatched bar in the timeline so a
|
|
644
|
+
* workflow waiting on a cron-style delay isn't shown as silently
|
|
645
|
+
* hung. */
|
|
646
|
+
| 'timer-set'
|
|
647
|
+
/** Emitted when the sleep wakes — `payload` carries `{ name,
|
|
648
|
+
* actualDurationMs }`. Pairs with the matching `timer-set` on the
|
|
649
|
+
* same `name`. */
|
|
650
|
+
| 'timer-fired'
|
|
651
|
+
/** Emitted by `awaitSignal({ name, schema })` when the
|
|
652
|
+
* workflow enters the wait. `payload` carries `{ signalName }`. The
|
|
653
|
+
* dashboard surfaces the awaited signal in the run header + adds a
|
|
654
|
+
* "Send signal" CTA targeting this name. */
|
|
655
|
+
| 'signal-awaited'
|
|
656
|
+
/** Emitted by the inspect bus when an external caller
|
|
657
|
+
* sends a signal via `POST /_voltro/inspect/workflows/runs/:id/signal`.
|
|
658
|
+
* `payload` carries `{ signalName, value }`. The polling
|
|
659
|
+
* `awaitSignal` activity picks the event up on its next tick. */
|
|
660
|
+
| 'signal-sent'
|
|
661
|
+
/** Emitted by `awaitSignal` after a matching
|
|
662
|
+
* `signal-sent` is observed and parsed. `payload` carries
|
|
663
|
+
* `{ signalName, value }`. Pairs visually with `signal-awaited` for
|
|
664
|
+
* the same signalName so timelines render a complete handshake. */
|
|
665
|
+
| 'signal-received'
|
|
666
|
+
/** Emitted when an external caller sends a synchronous workflow
|
|
667
|
+
* update. `payload` carries `{ updateId, updateName, value }`.
|
|
668
|
+
* The matching `awaitUpdate(...)` activity picks it up, validates
|
|
669
|
+
* it, and records a terminal update event. */
|
|
670
|
+
| 'update-requested'
|
|
671
|
+
/** Emitted by `awaitUpdate(...)` once the workflow has claimed an
|
|
672
|
+
* update request. Useful in dashboards to distinguish "queued for
|
|
673
|
+
* workflow" from "currently being handled". */
|
|
674
|
+
| 'update-received'
|
|
675
|
+
/** Emitted by `awaitUpdate(...)` after the handler returns.
|
|
676
|
+
* `payload` carries `{ updateId, updateName, result }`; the
|
|
677
|
+
* caller's `ctx.workflows.update(...)` waits for this event. */
|
|
678
|
+
| 'update-completed'
|
|
679
|
+
/** Emitted by `awaitUpdate(...)` when schema validation or the
|
|
680
|
+
* update handler fails. `payload` carries `{ updateId, updateName,
|
|
681
|
+
* errorTag, errorMessage }`; the caller's update promise rejects. */
|
|
682
|
+
| 'update-failed';
|
|
683
|
+
|
|
684
|
+
export declare class WorkflowRunRecorder extends WorkflowRunRecorder_base {
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
declare const WorkflowRunRecorder_base: Context.TagClass<WorkflowRunRecorder, "@voltro/WorkflowRunRecorder", WorkflowRunRecorderService>;
|
|
688
|
+
|
|
689
|
+
export declare interface WorkflowRunRecorderOptions {
|
|
690
|
+
readonly store: WorkflowRecordingStore;
|
|
691
|
+
readonly emit?: WorkflowRecordingEmitter['emit'];
|
|
692
|
+
readonly log?: WorkflowRecordingLogger;
|
|
693
|
+
readonly truncateBytes?: number;
|
|
694
|
+
readonly makeId?: (prefix: 'wr' | 'ws' | 'wfev') => string;
|
|
695
|
+
/** Dormancy wakeup producer — present only in sleep mode. Absent →
|
|
696
|
+
* zero overhead (always-on default). */
|
|
697
|
+
readonly wakeups?: WorkflowWakeupHook;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/** Per-step lifecycle hooks. Implementations should not throw —
|
|
701
|
+
* recorder failures must NOT break workflow execution.
|
|
702
|
+
*
|
|
703
|
+
* Two responsibilities: write rows to the steps table AND emit
|
|
704
|
+
* inspect-stream events so cross-process consumers (cloud-api's
|
|
705
|
+
* per-app SSE bridge → cache mirror → dashboard subscriptions) see
|
|
706
|
+
* state transitions in real time. */
|
|
707
|
+
export declare interface WorkflowRunRecorderService {
|
|
708
|
+
/** Called before a step body runs. Returns an id the caller passes
|
|
709
|
+
* back to `endStep`. Implementations may return undefined to
|
|
710
|
+
* signal "not recording this step" (e.g. no current run id, or
|
|
711
|
+
* the runs table isn't available yet); callers should treat that
|
|
712
|
+
* as "no recording happened" and not call `endStep`. */
|
|
713
|
+
readonly startStep: (input: {
|
|
714
|
+
readonly runId: string;
|
|
715
|
+
readonly stepName: string;
|
|
716
|
+
readonly attempt: number;
|
|
717
|
+
/** User-declared structured input recorded into
|
|
718
|
+
* `_voltro_workflow_run_steps.input`. The recorder truncates large
|
|
719
|
+
* payloads — pass whatever the step needs to make sense in
|
|
720
|
+
* isolation, not the full universe. */
|
|
721
|
+
readonly stepInput?: unknown;
|
|
722
|
+
/** Declarative retry-policy summary recorded into
|
|
723
|
+
* `_voltro_workflow_run_steps.retryPolicy`. Pure metadata — the
|
|
724
|
+
* framework doesn't apply this; the user is still responsible for
|
|
725
|
+
* using `Effect.retry` or `Activity.interruptRetryPolicy` to
|
|
726
|
+
* implement the actual retry behavior. The dashboard renders the
|
|
727
|
+
* policy alongside the attempt counter so operators can see
|
|
728
|
+
* "attempt 2 of 5, exponential 1s base" at a glance. */
|
|
729
|
+
readonly retryPolicy?: StepRetryPolicy;
|
|
730
|
+
}) => Promise<string | undefined>;
|
|
731
|
+
readonly endStepSuccess: (input: {
|
|
732
|
+
readonly stepRecordId: string;
|
|
733
|
+
readonly output: unknown;
|
|
734
|
+
readonly durationMs: number;
|
|
735
|
+
}) => Promise<void>;
|
|
736
|
+
readonly endStepFailure: (input: {
|
|
737
|
+
readonly stepRecordId: string;
|
|
738
|
+
readonly errorTag: string | null;
|
|
739
|
+
readonly errorMessage: string;
|
|
740
|
+
/** Structured Cause record — pretty form +
|
|
741
|
+
* typed failures + defects. The dashboard renders the pretty
|
|
742
|
+
* string as a stack-trace panel in the expanded step view, so
|
|
743
|
+
* ops can see "where in user code" the throw originated. */
|
|
744
|
+
readonly errorCause?: {
|
|
745
|
+
readonly pretty: string;
|
|
746
|
+
readonly failures: ReadonlyArray<unknown>;
|
|
747
|
+
readonly defects: ReadonlyArray<unknown>;
|
|
748
|
+
};
|
|
749
|
+
readonly durationMs: number;
|
|
750
|
+
}) => Promise<void>;
|
|
751
|
+
/**
|
|
752
|
+
* Record a run-scoped lifecycle event into
|
|
753
|
+
* `_voltro_workflow_run_events`. Used for transitions that don't
|
|
754
|
+
* have a row representation in the runs or steps tables:
|
|
755
|
+
* suspend / resume / cancel, and (in later phases) timer fires +
|
|
756
|
+
* signal receipts + child spawns. Implementations should NOT throw —
|
|
757
|
+
* recorder failures must NOT break workflow execution.
|
|
758
|
+
*/
|
|
759
|
+
readonly recordEvent: (input: {
|
|
760
|
+
readonly runId: string;
|
|
761
|
+
readonly eventType: WorkflowRunEventType;
|
|
762
|
+
readonly payload?: Record<string, unknown> | null;
|
|
763
|
+
readonly stepName?: string | null;
|
|
764
|
+
readonly attempt?: number | null;
|
|
765
|
+
}) => Promise<{
|
|
766
|
+
readonly id: string;
|
|
767
|
+
}>;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Per-step plugin interceptor. Plugins can wrap every `step()`
|
|
772
|
+
* (== `Activity.make`) invocation with observability, suppress /
|
|
773
|
+
* substitute behaviour, or retry-policy overrides. Composed across
|
|
774
|
+
* plugins by the CLI (`composePluginWorkflowStep`).
|
|
775
|
+
*
|
|
776
|
+
* Receives the user's `execute` Effect + a `WorkflowStepContext`
|
|
777
|
+
* carrying `{ runId, stepName, attempt }`. Returns a wrapped Effect.
|
|
778
|
+
* A FAIL inside the interceptor abort the step the same way a fail
|
|
779
|
+
* in the user effect would — including triggering @effect/workflow's
|
|
780
|
+
* retry policy.
|
|
781
|
+
*/
|
|
782
|
+
export declare interface WorkflowStepContext {
|
|
783
|
+
/** Run id from `_voltro_workflow_runs` (or 'unrecorded'). */
|
|
784
|
+
readonly runId: string;
|
|
785
|
+
/** Step name (== `Activity.make({ name })`). */
|
|
786
|
+
readonly stepName: string;
|
|
787
|
+
/** Attempt number (1 on first try, 2+ on retry). */
|
|
788
|
+
readonly attempt: number;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
export declare type WorkflowStepInterceptor = <Success, ErrorE, R>(next: Effect.Effect<Success, ErrorE, R>, ctx: WorkflowStepContext) => Effect.Effect<Success, ErrorE, R>;
|
|
792
|
+
|
|
793
|
+
export declare interface WorkflowStepInterceptorService {
|
|
794
|
+
readonly interceptor: WorkflowStepInterceptor;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Optional service Tag carrying the composed per-step interceptor
|
|
799
|
+
* chain. When absent (no plugin contributes `onWorkflowStep`) the
|
|
800
|
+
* step wrapper takes the zero-overhead unchanged path.
|
|
801
|
+
*/
|
|
802
|
+
export declare class WorkflowStepInterceptorTag extends WorkflowStepInterceptorTag_base {
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
declare const WorkflowStepInterceptorTag_base: Context.TagClass<WorkflowStepInterceptorTag, "@voltro/WorkflowStepInterceptor", WorkflowStepInterceptorService>;
|
|
806
|
+
|
|
807
|
+
export declare interface WorkflowUpdateResult {
|
|
808
|
+
readonly eventId: string;
|
|
809
|
+
readonly updateId: string;
|
|
810
|
+
readonly completedEventId: string;
|
|
811
|
+
readonly result: unknown;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
export declare interface WorkflowUpdateSendOptions {
|
|
815
|
+
readonly timeoutMs?: number;
|
|
816
|
+
readonly pollIntervalMs?: number;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
export declare interface WorkflowVersionMetadata {
|
|
820
|
+
readonly version: string;
|
|
821
|
+
readonly compatibleWith: ReadonlyArray<string>;
|
|
822
|
+
readonly patches: ReadonlyArray<string>;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
export declare interface WorkflowVersionOptions {
|
|
826
|
+
readonly version?: string | number;
|
|
827
|
+
readonly compatibleWith?: ReadonlyArray<string | number>;
|
|
828
|
+
readonly patches?: ReadonlyArray<string>;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
export declare const WorkflowVersionTypeId: unique symbol;
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Dormancy producer (plan 50 — scale-to-zero). Injected by the CLI ONLY
|
|
835
|
+
* in `dormancy: 'sleep'` mode so the workflow package stays free of a
|
|
836
|
+
* `@voltro/runtime` dependency (it has none). The recorder mirrors a
|
|
837
|
+
* durable-clock wait into the wakeup registry: `register` on `timer-set`,
|
|
838
|
+
* `cancel` when the wait ends or the run reaches a terminal state. The
|
|
839
|
+
* external waker reads those rows to resume a scaled-to-zero runner.
|
|
840
|
+
*
|
|
841
|
+
* This NEVER touches `DurableClock.sleep` or the cluster journal — it is
|
|
842
|
+
* additive bookkeeping next to the existing best-effort event write, so
|
|
843
|
+
* the durable-resume path is unaffected. Implementations must not throw.
|
|
844
|
+
*/
|
|
845
|
+
export declare interface WorkflowWakeupHook {
|
|
846
|
+
/** `tenantId` is the wake-routing target carried from the workflow's
|
|
847
|
+
* caller subject (plan 50). Absent → the CLI hook falls back to the
|
|
848
|
+
* deployment's configured tenant (correct for single-tenant). */
|
|
849
|
+
readonly register: (runId: string, wakeAt: Date, tenantId?: string) => Promise<void>;
|
|
850
|
+
/** `tenantId` is carried on the `timer-fired` event (the normal cancel
|
|
851
|
+
* path) so the cancel targets the same row `register` created. Absent
|
|
852
|
+
* on terminal-event cancels → the CLI hook falls back to the
|
|
853
|
+
* deployment tenant (consistent with register for single-tenant). */
|
|
854
|
+
readonly cancel: (runId: string, tenantId?: string) => Promise<void>;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
export declare interface WorkflowWorkerLayerBrand {
|
|
858
|
+
readonly [WorkflowWorkerLayerTypeId]: true;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
export declare const WorkflowWorkerLayerTypeId: unique symbol;
|
|
862
|
+
|
|
863
|
+
export declare const wrapWorkflowExecuteWithRunRecording: (options: WorkflowExecuteRecordingOptions, userExecute: (payload: unknown, executionId: string) => Effect.Effect<unknown, unknown, never>) => (payload: unknown, executionId: string) => Effect.Effect<unknown, unknown, never>;
|
|
864
|
+
|
|
865
|
+
export { }
|