@rivetkit/workflow-engine 2.1.0-rc.1
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/LICENSE +203 -0
- package/dist/schemas/v1.ts +781 -0
- package/dist/tsup/chunk-GJ66YE5W.cjs +3441 -0
- package/dist/tsup/chunk-GJ66YE5W.cjs.map +1 -0
- package/dist/tsup/chunk-JWHWQBZP.js +3441 -0
- package/dist/tsup/chunk-JWHWQBZP.js.map +1 -0
- package/dist/tsup/index.cjs +93 -0
- package/dist/tsup/index.cjs.map +1 -0
- package/dist/tsup/index.d.cts +884 -0
- package/dist/tsup/index.d.ts +884 -0
- package/dist/tsup/index.js +93 -0
- package/dist/tsup/index.js.map +1 -0
- package/dist/tsup/testing.cjs +316 -0
- package/dist/tsup/testing.cjs.map +1 -0
- package/dist/tsup/testing.d.cts +52 -0
- package/dist/tsup/testing.d.ts +52 -0
- package/dist/tsup/testing.js +316 -0
- package/dist/tsup/testing.js.map +1 -0
- package/package.json +70 -0
- package/schemas/serde.ts +609 -0
- package/schemas/v1.bare +203 -0
- package/schemas/versioned.ts +107 -0
- package/src/context.ts +1845 -0
- package/src/driver.ts +103 -0
- package/src/errors.ts +170 -0
- package/src/index.ts +907 -0
- package/src/keys.ts +277 -0
- package/src/location.ts +168 -0
- package/src/storage.ts +364 -0
- package/src/testing.ts +292 -0
- package/src/types.ts +508 -0
- package/src/utils.ts +48 -0
|
@@ -0,0 +1,884 @@
|
|
|
1
|
+
import { Logger } from 'pino';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Index into the entry name registry.
|
|
5
|
+
* Names are stored once and referenced by this index to avoid repetition.
|
|
6
|
+
*/
|
|
7
|
+
type NameIndex = number;
|
|
8
|
+
/**
|
|
9
|
+
* A segment in a location path.
|
|
10
|
+
* Either a name index (for named entries) or a loop iteration marker.
|
|
11
|
+
*/
|
|
12
|
+
type PathSegment = NameIndex | LoopIterationMarker;
|
|
13
|
+
/**
|
|
14
|
+
* Marker for a loop iteration in a location path.
|
|
15
|
+
*/
|
|
16
|
+
interface LoopIterationMarker {
|
|
17
|
+
loop: NameIndex;
|
|
18
|
+
iteration: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Location identifies where an entry exists in the workflow execution tree.
|
|
22
|
+
* It forms a path from the root through loops, joins, and branches.
|
|
23
|
+
*/
|
|
24
|
+
type Location = PathSegment[];
|
|
25
|
+
/**
|
|
26
|
+
* Current state of a sleep entry.
|
|
27
|
+
*/
|
|
28
|
+
type SleepState = "pending" | "completed" | "interrupted";
|
|
29
|
+
/**
|
|
30
|
+
* Status of an entry in the workflow.
|
|
31
|
+
*/
|
|
32
|
+
type EntryStatus = "pending" | "running" | "completed" | "failed" | "exhausted";
|
|
33
|
+
/**
|
|
34
|
+
* Status of a branch in join/race.
|
|
35
|
+
*/
|
|
36
|
+
type BranchStatusType = "pending" | "running" | "completed" | "failed" | "cancelled";
|
|
37
|
+
/**
|
|
38
|
+
* Current state of the workflow.
|
|
39
|
+
*/
|
|
40
|
+
type WorkflowState = "pending" | "running" | "sleeping" | "failed" | "completed" | "cancelled" | "rolling_back";
|
|
41
|
+
/**
|
|
42
|
+
* Step entry data.
|
|
43
|
+
*/
|
|
44
|
+
interface StepEntry {
|
|
45
|
+
output?: unknown;
|
|
46
|
+
error?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Loop entry data.
|
|
50
|
+
*/
|
|
51
|
+
interface LoopEntry {
|
|
52
|
+
state: unknown;
|
|
53
|
+
iteration: number;
|
|
54
|
+
output?: unknown;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Sleep entry data.
|
|
58
|
+
*/
|
|
59
|
+
interface SleepEntry {
|
|
60
|
+
deadline: number;
|
|
61
|
+
state: SleepState;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Message entry data.
|
|
65
|
+
*/
|
|
66
|
+
interface MessageEntry {
|
|
67
|
+
name: string;
|
|
68
|
+
data: unknown;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Rollback checkpoint entry data.
|
|
72
|
+
*/
|
|
73
|
+
interface RollbackCheckpointEntry {
|
|
74
|
+
name: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Branch status for join/race entries.
|
|
78
|
+
*/
|
|
79
|
+
interface BranchStatus {
|
|
80
|
+
status: BranchStatusType;
|
|
81
|
+
output?: unknown;
|
|
82
|
+
error?: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Join entry data.
|
|
86
|
+
*/
|
|
87
|
+
interface JoinEntry {
|
|
88
|
+
branches: Record<string, BranchStatus>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Race entry data.
|
|
92
|
+
*/
|
|
93
|
+
interface RaceEntry {
|
|
94
|
+
winner: string | null;
|
|
95
|
+
branches: Record<string, BranchStatus>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Removed entry data - placeholder for removed steps in workflow migrations.
|
|
99
|
+
*/
|
|
100
|
+
interface RemovedEntry {
|
|
101
|
+
originalType: EntryKindType;
|
|
102
|
+
originalName?: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* All possible entry kind types.
|
|
106
|
+
*/
|
|
107
|
+
type EntryKindType = "step" | "loop" | "sleep" | "message" | "rollback_checkpoint" | "join" | "race" | "removed";
|
|
108
|
+
/**
|
|
109
|
+
* Type-specific entry data.
|
|
110
|
+
*/
|
|
111
|
+
type EntryKind = {
|
|
112
|
+
type: "step";
|
|
113
|
+
data: StepEntry;
|
|
114
|
+
} | {
|
|
115
|
+
type: "loop";
|
|
116
|
+
data: LoopEntry;
|
|
117
|
+
} | {
|
|
118
|
+
type: "sleep";
|
|
119
|
+
data: SleepEntry;
|
|
120
|
+
} | {
|
|
121
|
+
type: "message";
|
|
122
|
+
data: MessageEntry;
|
|
123
|
+
} | {
|
|
124
|
+
type: "rollback_checkpoint";
|
|
125
|
+
data: RollbackCheckpointEntry;
|
|
126
|
+
} | {
|
|
127
|
+
type: "join";
|
|
128
|
+
data: JoinEntry;
|
|
129
|
+
} | {
|
|
130
|
+
type: "race";
|
|
131
|
+
data: RaceEntry;
|
|
132
|
+
} | {
|
|
133
|
+
type: "removed";
|
|
134
|
+
data: RemovedEntry;
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* An entry in the workflow history.
|
|
138
|
+
*/
|
|
139
|
+
interface Entry {
|
|
140
|
+
id: string;
|
|
141
|
+
location: Location;
|
|
142
|
+
kind: EntryKind;
|
|
143
|
+
dirty: boolean;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Metadata for an entry (stored separately, lazily loaded).
|
|
147
|
+
*/
|
|
148
|
+
interface EntryMetadata {
|
|
149
|
+
status: EntryStatus;
|
|
150
|
+
error?: string;
|
|
151
|
+
attempts: number;
|
|
152
|
+
lastAttemptAt: number;
|
|
153
|
+
createdAt: number;
|
|
154
|
+
completedAt?: number;
|
|
155
|
+
rollbackCompletedAt?: number;
|
|
156
|
+
rollbackError?: string;
|
|
157
|
+
dirty: boolean;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* A message in the queue.
|
|
161
|
+
*/
|
|
162
|
+
interface Message {
|
|
163
|
+
/** Unique message ID. */
|
|
164
|
+
id: string;
|
|
165
|
+
name: string;
|
|
166
|
+
data: unknown;
|
|
167
|
+
sentAt: number;
|
|
168
|
+
/**
|
|
169
|
+
* Optional completion callback for queue-backed drivers.
|
|
170
|
+
*
|
|
171
|
+
* This is runtime-only and is not persisted.
|
|
172
|
+
*/
|
|
173
|
+
complete?: (response?: unknown) => Promise<void>;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Options for receiving queue messages in workflows.
|
|
177
|
+
*/
|
|
178
|
+
interface WorkflowQueueNextOptions {
|
|
179
|
+
/**
|
|
180
|
+
* Queue names to receive from.
|
|
181
|
+
* If omitted, receives from all queue names.
|
|
182
|
+
*/
|
|
183
|
+
names?: readonly string[];
|
|
184
|
+
/** Maximum number of messages to receive. Defaults to 1. */
|
|
185
|
+
count?: number;
|
|
186
|
+
/**
|
|
187
|
+
* Timeout in milliseconds.
|
|
188
|
+
* Omit to wait indefinitely.
|
|
189
|
+
*/
|
|
190
|
+
timeout?: number;
|
|
191
|
+
/** Whether returned messages must be manually completed. */
|
|
192
|
+
completable?: boolean;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Message returned by workflow queue operations.
|
|
196
|
+
*/
|
|
197
|
+
interface WorkflowQueueMessage<TBody = unknown> {
|
|
198
|
+
id: string | bigint;
|
|
199
|
+
name: string;
|
|
200
|
+
body: TBody;
|
|
201
|
+
createdAt: number;
|
|
202
|
+
complete?(response?: unknown): Promise<void>;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Workflow queue interface.
|
|
206
|
+
*/
|
|
207
|
+
interface WorkflowQueue {
|
|
208
|
+
next<TBody = unknown>(name: string, opts?: WorkflowQueueNextOptions): Promise<Array<WorkflowQueueMessage<TBody>>>;
|
|
209
|
+
send(name: string, body: unknown): Promise<void>;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Workflow history - maps location keys to entries.
|
|
213
|
+
*/
|
|
214
|
+
interface History {
|
|
215
|
+
entries: Map<string, Entry>;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* History entry snapshot without internal dirty flags.
|
|
219
|
+
*/
|
|
220
|
+
type WorkflowHistoryEntry = Omit<Entry, "dirty">;
|
|
221
|
+
/**
|
|
222
|
+
* Entry metadata snapshot without internal dirty flags.
|
|
223
|
+
*/
|
|
224
|
+
type WorkflowEntryMetadataSnapshot = Omit<EntryMetadata, "dirty">;
|
|
225
|
+
/**
|
|
226
|
+
* Snapshot of workflow history for observers.
|
|
227
|
+
*/
|
|
228
|
+
interface WorkflowHistorySnapshot {
|
|
229
|
+
nameRegistry: string[];
|
|
230
|
+
entries: WorkflowHistoryEntry[];
|
|
231
|
+
entryMetadata: ReadonlyMap<string, WorkflowEntryMetadataSnapshot>;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Structured error information for workflow failures.
|
|
235
|
+
*/
|
|
236
|
+
interface WorkflowError {
|
|
237
|
+
/** Error name/type (e.g., "TypeError", "CriticalError") */
|
|
238
|
+
name: string;
|
|
239
|
+
/** Error message */
|
|
240
|
+
message: string;
|
|
241
|
+
/** Stack trace if available */
|
|
242
|
+
stack?: string;
|
|
243
|
+
/** Custom error properties (for structured errors) */
|
|
244
|
+
metadata?: Record<string, unknown>;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Complete storage state for a workflow.
|
|
248
|
+
*/
|
|
249
|
+
interface Storage {
|
|
250
|
+
nameRegistry: string[];
|
|
251
|
+
flushedNameCount: number;
|
|
252
|
+
history: History;
|
|
253
|
+
entryMetadata: Map<string, EntryMetadata>;
|
|
254
|
+
output?: unknown;
|
|
255
|
+
state: WorkflowState;
|
|
256
|
+
flushedState?: WorkflowState;
|
|
257
|
+
error?: WorkflowError;
|
|
258
|
+
flushedError?: WorkflowError;
|
|
259
|
+
flushedOutput?: unknown;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Driver interface for workflow message persistence.
|
|
263
|
+
*/
|
|
264
|
+
interface WorkflowMessageDriver {
|
|
265
|
+
addMessage(message: Message): Promise<void>;
|
|
266
|
+
/**
|
|
267
|
+
* Receive messages directly from the host queue implementation.
|
|
268
|
+
* The operation must be non-blocking and return immediately.
|
|
269
|
+
*/
|
|
270
|
+
receiveMessages(opts: {
|
|
271
|
+
names?: readonly string[];
|
|
272
|
+
count: number;
|
|
273
|
+
completable: boolean;
|
|
274
|
+
}): Promise<Message[]>;
|
|
275
|
+
/**
|
|
276
|
+
* Complete a previously consumed message with an optional response payload.
|
|
277
|
+
*/
|
|
278
|
+
completeMessage(messageId: string, response?: unknown): Promise<void>;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Context available to rollback handlers.
|
|
282
|
+
*/
|
|
283
|
+
interface RollbackContextInterface {
|
|
284
|
+
readonly workflowId: string;
|
|
285
|
+
readonly abortSignal: AbortSignal;
|
|
286
|
+
isEvicted(): boolean;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Configuration for a step.
|
|
290
|
+
*/
|
|
291
|
+
interface StepConfig<T> {
|
|
292
|
+
name: string;
|
|
293
|
+
run: () => Promise<T>;
|
|
294
|
+
rollback?: (ctx: RollbackContextInterface, output: T) => Promise<void>;
|
|
295
|
+
/** If true, step result is not persisted (use for idempotent operations). */
|
|
296
|
+
ephemeral?: boolean;
|
|
297
|
+
/** Maximum number of retry attempts (default: 3). */
|
|
298
|
+
maxRetries?: number;
|
|
299
|
+
/** Base delay in ms for exponential backoff (default: 100). */
|
|
300
|
+
retryBackoffBase?: number;
|
|
301
|
+
/** Maximum delay in ms for exponential backoff (default: 30000). */
|
|
302
|
+
retryBackoffMax?: number;
|
|
303
|
+
/** Timeout in ms for step execution (default: 30000). Set to 0 to disable. */
|
|
304
|
+
timeout?: number;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Result from a loop iteration.
|
|
308
|
+
*/
|
|
309
|
+
type LoopResult<S, T> = {
|
|
310
|
+
continue: true;
|
|
311
|
+
state: S;
|
|
312
|
+
} | {
|
|
313
|
+
break: true;
|
|
314
|
+
value: T;
|
|
315
|
+
};
|
|
316
|
+
/**
|
|
317
|
+
* Configuration for a loop.
|
|
318
|
+
*/
|
|
319
|
+
interface LoopConfig<S, T> {
|
|
320
|
+
name: string;
|
|
321
|
+
state?: S;
|
|
322
|
+
run: (ctx: WorkflowContextInterface, state: S) => Promise<LoopResult<S, T>>;
|
|
323
|
+
commitInterval?: number;
|
|
324
|
+
/** Trim loop history every N iterations. Defaults to commitInterval or 20. */
|
|
325
|
+
historyEvery?: number;
|
|
326
|
+
/** Retain the last N iterations of history. Defaults to commitInterval or 20. */
|
|
327
|
+
historyKeep?: number;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Configuration for a branch in join/race.
|
|
331
|
+
*/
|
|
332
|
+
interface BranchConfig<T> {
|
|
333
|
+
run: (ctx: WorkflowContextInterface) => Promise<T>;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Extract the output type from a BranchConfig.
|
|
337
|
+
*/
|
|
338
|
+
type BranchOutput<T> = T extends BranchConfig<infer O> ? O : never;
|
|
339
|
+
/**
|
|
340
|
+
* The workflow context interface exposed to workflow functions.
|
|
341
|
+
*/
|
|
342
|
+
interface WorkflowContextInterface {
|
|
343
|
+
readonly workflowId: string;
|
|
344
|
+
readonly abortSignal: AbortSignal;
|
|
345
|
+
readonly queue: WorkflowQueue;
|
|
346
|
+
step<T>(name: string, run: () => Promise<T>): Promise<T>;
|
|
347
|
+
step<T>(config: StepConfig<T>): Promise<T>;
|
|
348
|
+
loop<T>(name: string, run: (ctx: WorkflowContextInterface) => Promise<LoopResult<undefined, T>>): Promise<T>;
|
|
349
|
+
loop<S, T>(config: LoopConfig<S, T>): Promise<T>;
|
|
350
|
+
sleep(name: string, durationMs: number): Promise<void>;
|
|
351
|
+
sleepUntil(name: string, timestampMs: number): Promise<void>;
|
|
352
|
+
rollbackCheckpoint(name: string): Promise<void>;
|
|
353
|
+
join<T extends Record<string, BranchConfig<unknown>>>(name: string, branches: T): Promise<{
|
|
354
|
+
[K in keyof T]: BranchOutput<T[K]>;
|
|
355
|
+
}>;
|
|
356
|
+
race<T>(name: string, branches: Array<{
|
|
357
|
+
name: string;
|
|
358
|
+
run: (ctx: WorkflowContextInterface) => Promise<T>;
|
|
359
|
+
}>): Promise<{
|
|
360
|
+
winner: string;
|
|
361
|
+
value: T;
|
|
362
|
+
}>;
|
|
363
|
+
removed(name: string, originalType: EntryKindType): Promise<void>;
|
|
364
|
+
isEvicted(): boolean;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Workflow function type.
|
|
368
|
+
*/
|
|
369
|
+
type WorkflowRunMode = "yield" | "live";
|
|
370
|
+
interface RunWorkflowOptions {
|
|
371
|
+
mode?: WorkflowRunMode;
|
|
372
|
+
logger?: Logger;
|
|
373
|
+
onHistoryUpdated?: (history: WorkflowHistorySnapshot) => void;
|
|
374
|
+
}
|
|
375
|
+
type WorkflowFunction<TInput = unknown, TOutput = unknown> = (ctx: WorkflowContextInterface, input: TInput) => Promise<TOutput>;
|
|
376
|
+
/**
|
|
377
|
+
* Result returned when a workflow run completes or yields.
|
|
378
|
+
*/
|
|
379
|
+
interface WorkflowResult<TOutput = unknown> {
|
|
380
|
+
state: WorkflowState;
|
|
381
|
+
output?: TOutput;
|
|
382
|
+
sleepUntil?: number;
|
|
383
|
+
waitingForMessages?: string[];
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Handle for managing a running workflow.
|
|
387
|
+
*
|
|
388
|
+
* Returned by `runWorkflow()`. The workflow starts executing immediately.
|
|
389
|
+
* Use `.result` to await completion, and other methods to interact with
|
|
390
|
+
* the running workflow.
|
|
391
|
+
*/
|
|
392
|
+
interface WorkflowHandle<TOutput = unknown> {
|
|
393
|
+
readonly workflowId: string;
|
|
394
|
+
/**
|
|
395
|
+
* Promise that resolves when the workflow completes or yields.
|
|
396
|
+
*/
|
|
397
|
+
readonly result: Promise<WorkflowResult<TOutput>>;
|
|
398
|
+
/**
|
|
399
|
+
* Send a message to the workflow.
|
|
400
|
+
* The message is delegated to the runtime message driver.
|
|
401
|
+
* In live mode, this wakes workflows waiting on queue messages.
|
|
402
|
+
*/
|
|
403
|
+
message(name: string, data: unknown): Promise<void>;
|
|
404
|
+
/**
|
|
405
|
+
* Wake the workflow immediately by setting an alarm for now.
|
|
406
|
+
*/
|
|
407
|
+
wake(): Promise<void>;
|
|
408
|
+
/**
|
|
409
|
+
* Reset exhausted retries and schedule the workflow to run again.
|
|
410
|
+
*/
|
|
411
|
+
recover(): Promise<void>;
|
|
412
|
+
/**
|
|
413
|
+
* Request the workflow to stop gracefully.
|
|
414
|
+
* The workflow will throw EvictedError at its next yield point,
|
|
415
|
+
* flush its state, and resolve the result promise.
|
|
416
|
+
*/
|
|
417
|
+
evict(): void;
|
|
418
|
+
/**
|
|
419
|
+
* Cancel the workflow permanently.
|
|
420
|
+
* Sets the workflow state to "cancelled" and clears any pending alarms.
|
|
421
|
+
* Unlike evict(), this marks the workflow as permanently stopped.
|
|
422
|
+
*/
|
|
423
|
+
cancel(): Promise<void>;
|
|
424
|
+
/**
|
|
425
|
+
* Get the workflow output if completed.
|
|
426
|
+
*/
|
|
427
|
+
getOutput(): Promise<TOutput | undefined>;
|
|
428
|
+
/**
|
|
429
|
+
* Get the current workflow state.
|
|
430
|
+
*/
|
|
431
|
+
getState(): Promise<WorkflowState>;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* A key-value entry returned from list operations.
|
|
436
|
+
*/
|
|
437
|
+
interface KVEntry {
|
|
438
|
+
key: Uint8Array;
|
|
439
|
+
value: Uint8Array;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* A write operation for batch writes.
|
|
443
|
+
*/
|
|
444
|
+
interface KVWrite {
|
|
445
|
+
key: Uint8Array;
|
|
446
|
+
value: Uint8Array;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* The engine driver provides the KV and scheduling interface.
|
|
450
|
+
* Implementations must provide these methods to integrate with different backends.
|
|
451
|
+
*
|
|
452
|
+
* IMPORTANT: Each workflow instance must have its own isolated driver/KV namespace.
|
|
453
|
+
* The workflow engine is the sole reader/writer of its KV during execution.
|
|
454
|
+
* KV operations do not include workflow IDs because isolation is provided externally
|
|
455
|
+
* by the host system (e.g., Cloudflare Durable Objects, dedicated actor processes).
|
|
456
|
+
*
|
|
457
|
+
* External systems may only enqueue messages through the configured message driver
|
|
458
|
+
* (via WorkflowHandle.message()).
|
|
459
|
+
* See architecture.md "Isolation Model" for details.
|
|
460
|
+
*/
|
|
461
|
+
interface EngineDriver {
|
|
462
|
+
/**
|
|
463
|
+
* Get a value by key.
|
|
464
|
+
* Returns null if the key doesn't exist.
|
|
465
|
+
*/
|
|
466
|
+
get(key: Uint8Array): Promise<Uint8Array | null>;
|
|
467
|
+
/**
|
|
468
|
+
* Set a value by key.
|
|
469
|
+
*/
|
|
470
|
+
set(key: Uint8Array, value: Uint8Array): Promise<void>;
|
|
471
|
+
/**
|
|
472
|
+
* Delete a key.
|
|
473
|
+
*/
|
|
474
|
+
delete(key: Uint8Array): Promise<void>;
|
|
475
|
+
/**
|
|
476
|
+
* Delete all keys with a given prefix.
|
|
477
|
+
*/
|
|
478
|
+
deletePrefix(prefix: Uint8Array): Promise<void>;
|
|
479
|
+
/**
|
|
480
|
+
* List all key-value pairs with a given prefix.
|
|
481
|
+
*
|
|
482
|
+
* IMPORTANT: Results MUST be sorted by key in lexicographic byte order.
|
|
483
|
+
* The workflow engine relies on this ordering for deterministic history
|
|
484
|
+
* replay and name registry reconstruction. Failing to sort will cause
|
|
485
|
+
* non-deterministic replay behavior.
|
|
486
|
+
*/
|
|
487
|
+
list(prefix: Uint8Array): Promise<KVEntry[]>;
|
|
488
|
+
/**
|
|
489
|
+
* Batch write multiple key-value pairs.
|
|
490
|
+
* Should be atomic if possible.
|
|
491
|
+
*/
|
|
492
|
+
batch(writes: KVWrite[]): Promise<void>;
|
|
493
|
+
/**
|
|
494
|
+
* Set an alarm to wake the workflow at a specific time.
|
|
495
|
+
* @param workflowId The workflow to wake
|
|
496
|
+
* @param wakeAt Timestamp in milliseconds when to wake
|
|
497
|
+
*/
|
|
498
|
+
setAlarm(workflowId: string, wakeAt: number): Promise<void>;
|
|
499
|
+
/**
|
|
500
|
+
* Clear any pending alarm for a workflow.
|
|
501
|
+
*/
|
|
502
|
+
clearAlarm(workflowId: string): Promise<void>;
|
|
503
|
+
/**
|
|
504
|
+
* How often the worker polls for work (in milliseconds).
|
|
505
|
+
* Affects the threshold for in-memory vs scheduled sleeps.
|
|
506
|
+
*/
|
|
507
|
+
readonly workerPollInterval: number;
|
|
508
|
+
/** Queue-backed message driver used for workflow messaging. */
|
|
509
|
+
readonly messageDriver: WorkflowMessageDriver;
|
|
510
|
+
/**
|
|
511
|
+
* Wait for incoming messages when running in live mode.
|
|
512
|
+
* Implementations should resolve when any of the specified message names are available.
|
|
513
|
+
*/
|
|
514
|
+
waitForMessages(messageNames: string[], abortSignal: AbortSignal): Promise<void>;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Default values for step configuration.
|
|
519
|
+
* These are exported so users can reference them when overriding.
|
|
520
|
+
*/
|
|
521
|
+
declare const DEFAULT_MAX_RETRIES = 3;
|
|
522
|
+
declare const DEFAULT_RETRY_BACKOFF_BASE = 100;
|
|
523
|
+
declare const DEFAULT_RETRY_BACKOFF_MAX = 30000;
|
|
524
|
+
declare const DEFAULT_LOOP_COMMIT_INTERVAL = 20;
|
|
525
|
+
declare const DEFAULT_LOOP_HISTORY_EVERY = 20;
|
|
526
|
+
declare const DEFAULT_LOOP_HISTORY_KEEP = 20;
|
|
527
|
+
declare const DEFAULT_STEP_TIMEOUT = 30000;
|
|
528
|
+
/**
|
|
529
|
+
* Internal representation of a rollback handler.
|
|
530
|
+
*/
|
|
531
|
+
interface RollbackAction<T = unknown> {
|
|
532
|
+
entryId: string;
|
|
533
|
+
name: string;
|
|
534
|
+
output: T;
|
|
535
|
+
rollback: (ctx: RollbackContextInterface, output: T) => Promise<void>;
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Internal implementation of WorkflowContext.
|
|
539
|
+
*/
|
|
540
|
+
declare class WorkflowContextImpl implements WorkflowContextInterface {
|
|
541
|
+
readonly workflowId: string;
|
|
542
|
+
private storage;
|
|
543
|
+
private driver;
|
|
544
|
+
private messageDriver;
|
|
545
|
+
private entryInProgress;
|
|
546
|
+
private abortController;
|
|
547
|
+
private currentLocation;
|
|
548
|
+
private visitedKeys;
|
|
549
|
+
private mode;
|
|
550
|
+
private rollbackActions?;
|
|
551
|
+
private rollbackCheckpointSet;
|
|
552
|
+
/** Track names used in current execution to detect duplicates */
|
|
553
|
+
private usedNamesInExecution;
|
|
554
|
+
private pendingCompletableMessageIds;
|
|
555
|
+
private historyNotifier?;
|
|
556
|
+
private logger?;
|
|
557
|
+
constructor(workflowId: string, storage: Storage, driver: EngineDriver, messageDriver: WorkflowMessageDriver, location?: Location, abortController?: AbortController, mode?: "forward" | "rollback", rollbackActions?: RollbackAction[], rollbackCheckpointSet?: boolean, historyNotifier?: () => void, logger?: Logger);
|
|
558
|
+
get abortSignal(): AbortSignal;
|
|
559
|
+
get queue(): WorkflowQueue;
|
|
560
|
+
isEvicted(): boolean;
|
|
561
|
+
private assertNotInProgress;
|
|
562
|
+
private checkEvicted;
|
|
563
|
+
private flushStorage;
|
|
564
|
+
/**
|
|
565
|
+
* Create a new branch context for parallel/nested execution.
|
|
566
|
+
*/
|
|
567
|
+
createBranch(location: Location, abortController?: AbortController): WorkflowContextImpl;
|
|
568
|
+
/**
|
|
569
|
+
* Log a debug message using the configured logger.
|
|
570
|
+
*/
|
|
571
|
+
private log;
|
|
572
|
+
/**
|
|
573
|
+
* Mark a key as visited.
|
|
574
|
+
*/
|
|
575
|
+
private markVisited;
|
|
576
|
+
/**
|
|
577
|
+
* Check if a name has already been used at the current location in this execution.
|
|
578
|
+
* Throws HistoryDivergedError if duplicate detected.
|
|
579
|
+
*/
|
|
580
|
+
private checkDuplicateName;
|
|
581
|
+
private stopRollback;
|
|
582
|
+
private stopRollbackIfMissing;
|
|
583
|
+
private stopRollbackIfIncomplete;
|
|
584
|
+
private registerRollbackAction;
|
|
585
|
+
/**
|
|
586
|
+
* Ensure a rollback checkpoint exists before registering rollback handlers.
|
|
587
|
+
*/
|
|
588
|
+
private ensureRollbackCheckpoint;
|
|
589
|
+
/**
|
|
590
|
+
* Validate that all expected entries in the branch were visited.
|
|
591
|
+
* Throws HistoryDivergedError if there are unvisited entries.
|
|
592
|
+
*/
|
|
593
|
+
validateComplete(): void;
|
|
594
|
+
/**
|
|
595
|
+
* Evict the workflow.
|
|
596
|
+
*/
|
|
597
|
+
evict(): void;
|
|
598
|
+
/**
|
|
599
|
+
* Wait for eviction message.
|
|
600
|
+
*
|
|
601
|
+
* The event listener uses { once: true } to auto-remove after firing,
|
|
602
|
+
* preventing memory leaks if this method is called multiple times.
|
|
603
|
+
*/
|
|
604
|
+
waitForEviction(): Promise<never>;
|
|
605
|
+
step<T>(nameOrConfig: string | StepConfig<T>, run?: () => Promise<T>): Promise<T>;
|
|
606
|
+
private executeStep;
|
|
607
|
+
/**
|
|
608
|
+
* Execute a promise with timeout.
|
|
609
|
+
*
|
|
610
|
+
* Note: This does NOT cancel the underlying operation. JavaScript Promises
|
|
611
|
+
* cannot be cancelled once started. When a timeout occurs:
|
|
612
|
+
* - The step is marked as failed with StepTimeoutError
|
|
613
|
+
* - The underlying async operation continues running in the background
|
|
614
|
+
* - Any side effects from the operation may still occur
|
|
615
|
+
*
|
|
616
|
+
* For cancellable operations, pass ctx.abortSignal to APIs that support AbortSignal:
|
|
617
|
+
*
|
|
618
|
+
* return fetch(url, { signal: ctx.abortSignal });
|
|
619
|
+
|
|
620
|
+
* });
|
|
621
|
+
*
|
|
622
|
+
* Or check ctx.isEvicted() periodically in long-running loops.
|
|
623
|
+
*/
|
|
624
|
+
private executeStepRollback;
|
|
625
|
+
private executeWithTimeout;
|
|
626
|
+
loop<S, T>(nameOrConfig: string | LoopConfig<S, T>, run?: (ctx: WorkflowContextInterface) => Promise<LoopResult<undefined, T>>): Promise<T>;
|
|
627
|
+
private executeLoop;
|
|
628
|
+
/**
|
|
629
|
+
* Delete old loop iteration entries to save storage space.
|
|
630
|
+
*
|
|
631
|
+
* Loop locations always end with a NameIndex (number) because loops are
|
|
632
|
+
* created via appendName(). Even for nested loops, the innermost loop's
|
|
633
|
+
* location ends with its name index:
|
|
634
|
+
*
|
|
635
|
+
* ctx.loop("outer") → location: [outerIndex]
|
|
636
|
+
* iteration 0 → location: [{ loop: outerIndex, iteration: 0 }]
|
|
637
|
+
* ctx.loop("inner") → location: [{ loop: outerIndex, iteration: 0 }, innerIndex]
|
|
638
|
+
*
|
|
639
|
+
* This function removes iterations older than (currentIteration - historyKeep)
|
|
640
|
+
* every historyEvery iterations.
|
|
641
|
+
*/
|
|
642
|
+
private forgetOldIterations;
|
|
643
|
+
sleep(name: string, durationMs: number): Promise<void>;
|
|
644
|
+
sleepUntil(name: string, timestampMs: number): Promise<void>;
|
|
645
|
+
private executeSleep;
|
|
646
|
+
rollbackCheckpoint(name: string): Promise<void>;
|
|
647
|
+
private executeRollbackCheckpoint;
|
|
648
|
+
private queueSend;
|
|
649
|
+
private queueNext;
|
|
650
|
+
private executeQueueNext;
|
|
651
|
+
private normalizeQueueNames;
|
|
652
|
+
private messageNamesLabel;
|
|
653
|
+
private receiveMessagesNow;
|
|
654
|
+
private recordQueueMessages;
|
|
655
|
+
private recordQueueCountEntry;
|
|
656
|
+
private readReplayQueueMessages;
|
|
657
|
+
private toWorkflowQueueMessage;
|
|
658
|
+
private createQueueMessage;
|
|
659
|
+
private markQueueMessageCompleted;
|
|
660
|
+
private completeMessage;
|
|
661
|
+
private toHistoryQueueMessage;
|
|
662
|
+
private fromHistoryQueueMessage;
|
|
663
|
+
join<T extends Record<string, BranchConfig<unknown>>>(name: string, branches: T): Promise<{
|
|
664
|
+
[K in keyof T]: BranchOutput<T[K]>;
|
|
665
|
+
}>;
|
|
666
|
+
private executeJoin;
|
|
667
|
+
race<T>(name: string, branches: Array<{
|
|
668
|
+
name: string;
|
|
669
|
+
run: (ctx: WorkflowContextInterface) => Promise<T>;
|
|
670
|
+
}>): Promise<{
|
|
671
|
+
winner: string;
|
|
672
|
+
value: T;
|
|
673
|
+
}>;
|
|
674
|
+
private executeRace;
|
|
675
|
+
removed(name: string, originalType: EntryKindType): Promise<void>;
|
|
676
|
+
private executeRemoved;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Thrown from steps to prevent retry.
|
|
681
|
+
* Use this when an error is unrecoverable and retrying would be pointless.
|
|
682
|
+
*/
|
|
683
|
+
declare class CriticalError extends Error {
|
|
684
|
+
constructor(message: string);
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Thrown from steps to force rollback without retry.
|
|
688
|
+
*/
|
|
689
|
+
declare class RollbackError extends Error {
|
|
690
|
+
constructor(message: string);
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Thrown when rollback is used without a checkpoint.
|
|
694
|
+
*/
|
|
695
|
+
declare class RollbackCheckpointError extends Error {
|
|
696
|
+
constructor();
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Internal: Workflow should sleep until deadline.
|
|
700
|
+
* This is thrown to yield control back to the scheduler.
|
|
701
|
+
* Optionally, the workflow can also wake early if certain messages arrive.
|
|
702
|
+
*/
|
|
703
|
+
declare class SleepError extends Error {
|
|
704
|
+
readonly deadline: number;
|
|
705
|
+
readonly messageNames?: string[] | undefined;
|
|
706
|
+
constructor(deadline: number, messageNames?: string[] | undefined);
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Internal: Workflow is waiting for messages.
|
|
710
|
+
* This is thrown to yield control back to the scheduler.
|
|
711
|
+
*/
|
|
712
|
+
declare class MessageWaitError extends Error {
|
|
713
|
+
readonly messageNames: string[];
|
|
714
|
+
constructor(messageNames: string[]);
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Internal: Workflow was evicted.
|
|
718
|
+
* This is thrown when the workflow is being gracefully stopped.
|
|
719
|
+
*/
|
|
720
|
+
declare class EvictedError extends Error {
|
|
721
|
+
constructor();
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Workflow code changed incompatibly.
|
|
725
|
+
* Thrown when history doesn't match the current workflow code.
|
|
726
|
+
*/
|
|
727
|
+
declare class HistoryDivergedError extends Error {
|
|
728
|
+
constructor(message: string);
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Step exhausted all retries.
|
|
732
|
+
*/
|
|
733
|
+
declare class StepExhaustedError extends Error {
|
|
734
|
+
readonly stepName: string;
|
|
735
|
+
readonly lastError?: string | undefined;
|
|
736
|
+
constructor(stepName: string, lastError?: string | undefined);
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Step failed (will be retried).
|
|
740
|
+
* Internal error used to trigger retry logic.
|
|
741
|
+
*/
|
|
742
|
+
declare class StepFailedError extends Error {
|
|
743
|
+
readonly stepName: string;
|
|
744
|
+
readonly originalError: unknown;
|
|
745
|
+
readonly attempts: number;
|
|
746
|
+
constructor(stepName: string, originalError: unknown, attempts: number);
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Join had branch failures.
|
|
750
|
+
*/
|
|
751
|
+
declare class JoinError extends Error {
|
|
752
|
+
readonly errors: Record<string, Error>;
|
|
753
|
+
constructor(errors: Record<string, Error>);
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Race had all branches fail.
|
|
757
|
+
*/
|
|
758
|
+
declare class RaceError extends Error {
|
|
759
|
+
readonly errors: Array<{
|
|
760
|
+
name: string;
|
|
761
|
+
error: string;
|
|
762
|
+
}>;
|
|
763
|
+
constructor(message: string, errors: Array<{
|
|
764
|
+
name: string;
|
|
765
|
+
error: string;
|
|
766
|
+
}>);
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Branch was cancelled (used by race).
|
|
770
|
+
*/
|
|
771
|
+
declare class CancelledError extends Error {
|
|
772
|
+
constructor();
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Entry is currently being processed.
|
|
776
|
+
* Thrown when user forgets to await a step.
|
|
777
|
+
*/
|
|
778
|
+
declare class EntryInProgressError extends Error {
|
|
779
|
+
constructor();
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Check if a path segment is a loop iteration marker.
|
|
784
|
+
*/
|
|
785
|
+
declare function isLoopIterationMarker(segment: PathSegment): segment is LoopIterationMarker;
|
|
786
|
+
/**
|
|
787
|
+
* Register a name in the registry and return its index.
|
|
788
|
+
* If the name already exists, returns the existing index.
|
|
789
|
+
*/
|
|
790
|
+
declare function registerName(storage: Storage, name: string): NameIndex;
|
|
791
|
+
/**
|
|
792
|
+
* Resolve a name index to its string value.
|
|
793
|
+
*/
|
|
794
|
+
declare function resolveName(storage: Storage, index: NameIndex): string;
|
|
795
|
+
/**
|
|
796
|
+
* Convert a location to a KV key string.
|
|
797
|
+
* Named entries use their string name, loop iterations use ~N format.
|
|
798
|
+
*/
|
|
799
|
+
declare function locationToKey(storage: Storage, location: Location): string;
|
|
800
|
+
/**
|
|
801
|
+
* Append a named segment to a location.
|
|
802
|
+
*/
|
|
803
|
+
declare function appendName(storage: Storage, location: Location, name: string): Location;
|
|
804
|
+
/**
|
|
805
|
+
* Append a loop iteration segment to a location.
|
|
806
|
+
*/
|
|
807
|
+
declare function appendLoopIteration(storage: Storage, location: Location, loopName: string, iteration: number): Location;
|
|
808
|
+
/**
|
|
809
|
+
* Create an empty location (root).
|
|
810
|
+
*/
|
|
811
|
+
declare function emptyLocation(): Location;
|
|
812
|
+
/**
|
|
813
|
+
* Get the parent location (all segments except the last).
|
|
814
|
+
*/
|
|
815
|
+
declare function parentLocation(location: Location): Location;
|
|
816
|
+
/**
|
|
817
|
+
* Check if one location is a prefix of another.
|
|
818
|
+
*/
|
|
819
|
+
declare function isLocationPrefix(prefix: Location, location: Location): boolean;
|
|
820
|
+
/**
|
|
821
|
+
* Compare two locations for equality.
|
|
822
|
+
*/
|
|
823
|
+
declare function locationsEqual(a: Location, b: Location): boolean;
|
|
824
|
+
|
|
825
|
+
/**
|
|
826
|
+
* Create an empty storage instance.
|
|
827
|
+
*/
|
|
828
|
+
declare function createStorage(): Storage;
|
|
829
|
+
/**
|
|
830
|
+
* Create a snapshot of workflow history for observers.
|
|
831
|
+
*/
|
|
832
|
+
declare function createHistorySnapshot(storage: Storage): WorkflowHistorySnapshot;
|
|
833
|
+
/**
|
|
834
|
+
* Generate a UUID v4.
|
|
835
|
+
*/
|
|
836
|
+
declare function generateId(): string;
|
|
837
|
+
/**
|
|
838
|
+
* Create a new entry.
|
|
839
|
+
*/
|
|
840
|
+
declare function createEntry(location: Location, kind: EntryKind): Entry;
|
|
841
|
+
/**
|
|
842
|
+
* Create or get metadata for an entry.
|
|
843
|
+
*/
|
|
844
|
+
declare function getOrCreateMetadata(storage: Storage, entryId: string): EntryMetadata;
|
|
845
|
+
/**
|
|
846
|
+
* Load storage from the driver.
|
|
847
|
+
*/
|
|
848
|
+
declare function loadStorage(driver: EngineDriver): Promise<Storage>;
|
|
849
|
+
/**
|
|
850
|
+
* Load metadata for an entry (lazy loading).
|
|
851
|
+
*/
|
|
852
|
+
declare function loadMetadata(storage: Storage, driver: EngineDriver, entryId: string): Promise<EntryMetadata>;
|
|
853
|
+
/**
|
|
854
|
+
* Flush all dirty data to the driver.
|
|
855
|
+
*/
|
|
856
|
+
declare function flush(storage: Storage, driver: EngineDriver, onHistoryUpdated?: () => void): Promise<void>;
|
|
857
|
+
/**
|
|
858
|
+
* Delete entries with a given location prefix (used for loop forgetting).
|
|
859
|
+
* Also cleans up associated metadata from both memory and driver.
|
|
860
|
+
*/
|
|
861
|
+
declare function deleteEntriesWithPrefix(storage: Storage, driver: EngineDriver, prefixLocation: Location, onHistoryUpdated?: () => void): Promise<void>;
|
|
862
|
+
/**
|
|
863
|
+
* Get an entry by location.
|
|
864
|
+
*/
|
|
865
|
+
declare function getEntry(storage: Storage, location: Location): Entry | undefined;
|
|
866
|
+
/**
|
|
867
|
+
* Set an entry by location.
|
|
868
|
+
*/
|
|
869
|
+
declare function setEntry(storage: Storage, location: Location, entry: Entry): void;
|
|
870
|
+
|
|
871
|
+
declare const Loop: {
|
|
872
|
+
continue: <S>(state: S) => {
|
|
873
|
+
continue: true;
|
|
874
|
+
state: S;
|
|
875
|
+
};
|
|
876
|
+
break: <T>(value: T) => {
|
|
877
|
+
break: true;
|
|
878
|
+
value: T;
|
|
879
|
+
};
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
declare function runWorkflow<TInput, TOutput>(workflowId: string, workflowFn: WorkflowFunction<TInput, TOutput>, input: TInput, driver: EngineDriver, options?: RunWorkflowOptions): WorkflowHandle<TOutput>;
|
|
883
|
+
|
|
884
|
+
export { type BranchConfig, type BranchOutput, type BranchStatus, type BranchStatusType, CancelledError, CriticalError, DEFAULT_LOOP_COMMIT_INTERVAL, DEFAULT_LOOP_HISTORY_EVERY, DEFAULT_LOOP_HISTORY_KEEP, DEFAULT_MAX_RETRIES, DEFAULT_RETRY_BACKOFF_BASE, DEFAULT_RETRY_BACKOFF_MAX, DEFAULT_STEP_TIMEOUT, type EngineDriver, type Entry, EntryInProgressError, type EntryKind, type EntryKindType, type EntryMetadata, type EntryStatus, EvictedError, type History, HistoryDivergedError, type JoinEntry, JoinError, type KVEntry, type KVWrite, type Location, Loop, type LoopConfig, type LoopEntry, type LoopIterationMarker, type LoopResult, type Message, type MessageEntry, MessageWaitError, type NameIndex, type PathSegment, type RaceEntry, RaceError, type RemovedEntry, type RollbackCheckpointEntry, RollbackCheckpointError, type RollbackContextInterface, RollbackError, type RunWorkflowOptions, type SleepEntry, SleepError, type SleepState, type StepConfig, type StepEntry, StepExhaustedError, StepFailedError, type Storage, WorkflowContextImpl, type WorkflowContextInterface, type WorkflowEntryMetadataSnapshot, type WorkflowFunction, type WorkflowHandle, type WorkflowHistoryEntry, type WorkflowHistorySnapshot, type WorkflowMessageDriver, type WorkflowQueue, type WorkflowQueueMessage, type WorkflowQueueNextOptions, type WorkflowResult, type WorkflowRunMode, type WorkflowState, appendLoopIteration, appendName, createEntry, createHistorySnapshot, createStorage, deleteEntriesWithPrefix, emptyLocation, flush, generateId, getEntry, getOrCreateMetadata, isLocationPrefix, isLoopIterationMarker, loadMetadata, loadStorage, locationToKey, locationsEqual, parentLocation, registerName, resolveName, runWorkflow, setEntry };
|