@voyant-travel/workflows-orchestrator 0.107.10
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 +201 -0
- package/NOTICE +52 -0
- package/README.md +76 -0
- package/dist/abort-registry.d.ts +6 -0
- package/dist/abort-registry.d.ts.map +1 -0
- package/dist/abort-registry.js +37 -0
- package/dist/concurrency.d.ts +31 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +145 -0
- package/dist/drive.d.ts +67 -0
- package/dist/drive.d.ts.map +1 -0
- package/dist/drive.js +373 -0
- package/dist/driver-inmemory.d.ts +30 -0
- package/dist/driver-inmemory.d.ts.map +1 -0
- package/dist/driver-inmemory.js +394 -0
- package/dist/event-router.d.ts +51 -0
- package/dist/event-router.d.ts.map +1 -0
- package/dist/event-router.js +68 -0
- package/dist/http-step-handler.d.ts +25 -0
- package/dist/http-step-handler.d.ts.map +1 -0
- package/dist/http-step-handler.js +78 -0
- package/dist/in-memory-store.d.ts +5 -0
- package/dist/in-memory-store.d.ts.map +1 -0
- package/dist/in-memory-store.js +41 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/journal-helpers.d.ts +3 -0
- package/dist/journal-helpers.d.ts.map +1 -0
- package/dist/journal-helpers.js +9 -0
- package/dist/orchestrator.d.ts +116 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +411 -0
- package/dist/resume-run.d.ts +40 -0
- package/dist/resume-run.d.ts.map +1 -0
- package/dist/resume-run.js +119 -0
- package/dist/schedule.d.ts +51 -0
- package/dist/schedule.d.ts.map +1 -0
- package/dist/schedule.js +243 -0
- package/dist/testing/driver-compliance.d.ts +58 -0
- package/dist/testing/driver-compliance.d.ts.map +1 -0
- package/dist/testing/driver-compliance.js +667 -0
- package/dist/types.d.ts +182 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/package.json +51 -0
- package/src/__tests__/orchestrator-test-support.ts +18 -0
- package/src/abort-registry.ts +41 -0
- package/src/concurrency.ts +217 -0
- package/src/drive.ts +477 -0
- package/src/driver-inmemory.ts +511 -0
- package/src/event-router.ts +120 -0
- package/src/http-step-handler.ts +112 -0
- package/src/in-memory-store.ts +44 -0
- package/src/index.ts +73 -0
- package/src/journal-helpers.ts +11 -0
- package/src/orchestrator.ts +527 -0
- package/src/resume-run.ts +162 -0
- package/src/schedule.ts +310 -0
- package/src/testing/driver-compliance.ts +800 -0
- package/src/types.ts +201 -0
package/dist/drive.js
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
// The orchestrator's core loop.
|
|
2
|
+
//
|
|
3
|
+
// `driveUntilPaused` calls the tenant step handler repeatedly,
|
|
4
|
+
// merging each response into the run's journal, until the run is
|
|
5
|
+
// either terminal or parked on a waitpoint. It is deliberately free
|
|
6
|
+
// of persistence and transport — callers compose it with a
|
|
7
|
+
// `RunRecordStore` and a `StepHandler` (in-process or HTTP).
|
|
8
|
+
//
|
|
9
|
+
// See docs/runtime-protocol.md §2 + §5 for the wire semantics.
|
|
10
|
+
import { PROTOCOL_VERSION } from "@voyant-travel/workflows/protocol";
|
|
11
|
+
/**
|
|
12
|
+
* Drive a run forward. The passed-in record is mutated in place and
|
|
13
|
+
* also returned so callers can write it back to the store in one
|
|
14
|
+
* line: `await store.save(await driveUntilPaused(rec, opts))`.
|
|
15
|
+
*/
|
|
16
|
+
export async function driveUntilPaused(rec, opts) {
|
|
17
|
+
const maxInvocations = opts.maxInvocations ?? 128;
|
|
18
|
+
const now = opts.now ?? (() => Date.now());
|
|
19
|
+
while (rec.invocationCount < maxInvocations) {
|
|
20
|
+
if (isTerminal(rec.status))
|
|
21
|
+
break;
|
|
22
|
+
// Workflow-level timeout check. Compute-time-only: we compare
|
|
23
|
+
// cumulative invocation duration, not wall-clock, so parked runs
|
|
24
|
+
// don't starve their own budget while waiting on waitpoints.
|
|
25
|
+
if (rec.timeoutMs !== undefined && rec.timeoutMs > 0 && rec.computeTimeMs >= rec.timeoutMs) {
|
|
26
|
+
rec.status = "failed";
|
|
27
|
+
rec.error = {
|
|
28
|
+
category: "RUNTIME_ERROR",
|
|
29
|
+
code: "WORKFLOW_TIMEOUT",
|
|
30
|
+
message: `workflow exceeded its ${rec.timeoutMs}ms compute-time budget (${rec.computeTimeMs}ms used)`,
|
|
31
|
+
};
|
|
32
|
+
rec.completedAt = now();
|
|
33
|
+
rec.pendingWaitpoints = [];
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
if (opts.beforeInvocation) {
|
|
37
|
+
const go = await opts.beforeInvocation(rec);
|
|
38
|
+
if (!go)
|
|
39
|
+
break;
|
|
40
|
+
if (isTerminal(rec.status))
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
rec.invocationCount += 1;
|
|
44
|
+
const invocationStartedAt = now();
|
|
45
|
+
const req = buildStepRequest(rec);
|
|
46
|
+
const out = await opts.handler(req, {
|
|
47
|
+
signal: opts.signal,
|
|
48
|
+
onStreamChunk: opts.onStreamChunk,
|
|
49
|
+
});
|
|
50
|
+
rec.computeTimeMs += Math.max(0, now() - invocationStartedAt);
|
|
51
|
+
if (out.status !== 200) {
|
|
52
|
+
rec.status = "failed";
|
|
53
|
+
rec.error = {
|
|
54
|
+
category: "RUNTIME_ERROR",
|
|
55
|
+
code: "handler_error",
|
|
56
|
+
message: "message" in out.body ? out.body.message : `handler returned HTTP ${out.status}`,
|
|
57
|
+
};
|
|
58
|
+
rec.completedAt = now();
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
const response = out.body;
|
|
62
|
+
opts.onStepResponse?.({ runRecord: rec, response });
|
|
63
|
+
applyResponse(rec, response, now);
|
|
64
|
+
// Waiting with no pending waitpoints (all auto-resolved) is a
|
|
65
|
+
// protocol error; we still break rather than loop forever.
|
|
66
|
+
if (response.status === "waiting" && rec.pendingWaitpoints.length === 0) {
|
|
67
|
+
rec.status = "failed";
|
|
68
|
+
rec.error = {
|
|
69
|
+
category: "RUNTIME_ERROR",
|
|
70
|
+
code: "empty_waitpoint_list",
|
|
71
|
+
message: "tenant returned status=waiting without any registered waitpoints",
|
|
72
|
+
};
|
|
73
|
+
rec.completedAt = now();
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
// RUN waitpoints are resolvable inline via the triggerChild hook:
|
|
77
|
+
// run each child to completion, write the result back on the
|
|
78
|
+
// parent's journal, drop the RUN waitpoint, then loop.
|
|
79
|
+
if (response.status === "waiting") {
|
|
80
|
+
const runWaitpoints = rec.pendingWaitpoints.filter((w) => w.kind === "RUN");
|
|
81
|
+
if (runWaitpoints.length > 0) {
|
|
82
|
+
if (!opts.triggerChild) {
|
|
83
|
+
rec.status = "failed";
|
|
84
|
+
rec.error = {
|
|
85
|
+
category: "RUNTIME_ERROR",
|
|
86
|
+
code: "child_runs_unsupported",
|
|
87
|
+
message: "workflow used ctx.invoke but the driver has no triggerChild hook wired. " +
|
|
88
|
+
"Use orchestrator.trigger() from @voyant-travel/workflows-orchestrator, which wires children automatically.",
|
|
89
|
+
};
|
|
90
|
+
rec.completedAt = now();
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
const resolvedRunIds = new Set();
|
|
94
|
+
try {
|
|
95
|
+
for (const wp of runWaitpoints) {
|
|
96
|
+
const childResolution = await resolveChildRun(rec, wp, opts.triggerChild, now);
|
|
97
|
+
if (childResolution.kind === "resolved") {
|
|
98
|
+
rec.journal.waitpointsResolved[wp.clientWaitpointId] = childResolution.entry;
|
|
99
|
+
resolvedRunIds.add(wp.clientWaitpointId);
|
|
100
|
+
}
|
|
101
|
+
// deferred → leave the RUN waitpoint pending; the child
|
|
102
|
+
// will cascade-resume the parent on its terminal transition.
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
rec.status = "failed";
|
|
107
|
+
rec.error = {
|
|
108
|
+
category: "RUNTIME_ERROR",
|
|
109
|
+
code: "child_run_unresolvable",
|
|
110
|
+
message: err instanceof Error ? err.message : String(err),
|
|
111
|
+
};
|
|
112
|
+
rec.completedAt = now();
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
// Keep RUN waitpoints that are still deferred (child parked).
|
|
116
|
+
rec.pendingWaitpoints = rec.pendingWaitpoints.filter((w) => w.kind !== "RUN" || !resolvedRunIds.has(w.clientWaitpointId));
|
|
117
|
+
if (rec.pendingWaitpoints.length === 0) {
|
|
118
|
+
rec.status = "running";
|
|
119
|
+
// Loop continues → re-invoke with the resolved waitpoints in the journal.
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
// Still parked (non-RUN or deferred RUN); fall through to break.
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (rec.status !== "running")
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
if (rec.invocationCount >= maxInvocations && rec.status === "running") {
|
|
129
|
+
rec.status = "failed";
|
|
130
|
+
rec.error = {
|
|
131
|
+
category: "RUNTIME_ERROR",
|
|
132
|
+
code: "max_invocations_exceeded",
|
|
133
|
+
message: `orchestrator drove the run ${maxInvocations} times without reaching a terminal or waiting state`,
|
|
134
|
+
};
|
|
135
|
+
rec.completedAt = now();
|
|
136
|
+
}
|
|
137
|
+
return rec;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Accept a waitpoint injection for a parked run: match it against
|
|
141
|
+
* one of the pending waitpoints, write the resolution into the
|
|
142
|
+
* journal, flip the run to "running", and leave it ready to be
|
|
143
|
+
* re-driven by `driveUntilPaused`.
|
|
144
|
+
*/
|
|
145
|
+
export function applyWaitpointInjection(rec, injection, now = () => Date.now()) {
|
|
146
|
+
if (rec.status !== "waiting") {
|
|
147
|
+
return { ok: false, message: `run ${rec.id} is not parked (status: ${rec.status})` };
|
|
148
|
+
}
|
|
149
|
+
const matched = matchWaitpoint(rec.pendingWaitpoints, injection);
|
|
150
|
+
if (!matched) {
|
|
151
|
+
return {
|
|
152
|
+
ok: false,
|
|
153
|
+
message: `no pending waitpoint matches kind=${injection.kind}, key=${injectionKey(injection)}`,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
rec.journal.waitpointsResolved[matched.clientWaitpointId] = {
|
|
157
|
+
kind: matched.kind,
|
|
158
|
+
resolvedAt: now(),
|
|
159
|
+
payload: injection.payload,
|
|
160
|
+
source: "live",
|
|
161
|
+
matchedEventId: injection.kind === "EVENT" ? `evt_live_${injection.eventType}` : undefined,
|
|
162
|
+
};
|
|
163
|
+
rec.pendingWaitpoints = rec.pendingWaitpoints.filter((w) => w.clientWaitpointId !== matched.clientWaitpointId);
|
|
164
|
+
rec.status = "running";
|
|
165
|
+
return { ok: true };
|
|
166
|
+
}
|
|
167
|
+
// ---- Internals ----
|
|
168
|
+
function buildStepRequest(rec) {
|
|
169
|
+
return {
|
|
170
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
171
|
+
runId: rec.id,
|
|
172
|
+
workflowId: rec.workflowId,
|
|
173
|
+
workflowVersion: rec.workflowVersion,
|
|
174
|
+
invocationCount: rec.invocationCount,
|
|
175
|
+
input: rec.input,
|
|
176
|
+
journal: rec.journal,
|
|
177
|
+
environment: rec.environment,
|
|
178
|
+
// Deadlines aren't enforced yet in the reference orchestrator; the
|
|
179
|
+
// handler accepts the field for forward-compat.
|
|
180
|
+
deadline: Number.MAX_SAFE_INTEGER,
|
|
181
|
+
tenantMeta: rec.tenantMeta,
|
|
182
|
+
runMeta: {
|
|
183
|
+
number: rec.runMeta.number,
|
|
184
|
+
attempt: rec.runMeta.attempt,
|
|
185
|
+
triggeredBy: rec.triggeredBy,
|
|
186
|
+
tags: rec.tags,
|
|
187
|
+
startedAt: rec.startedAt,
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function applyResponse(rec, response, now) {
|
|
192
|
+
// Snapshot the metadata state from the prior invocation. The
|
|
193
|
+
// response journal is a clone of what we sent in, so its
|
|
194
|
+
// metadataState field won't reflect mutations the body just made —
|
|
195
|
+
// those come in `metadataUpdates`. We keep the prior state, apply
|
|
196
|
+
// the delta, then swap in the new journal shape.
|
|
197
|
+
const priorMetadata = rec.journal.metadataState;
|
|
198
|
+
// The handler returned the executor's journal post-invocation —
|
|
199
|
+
// trust it as the new source of truth for steps / waitpoints /
|
|
200
|
+
// compensations. We deep-clone to isolate from future executor
|
|
201
|
+
// mutations.
|
|
202
|
+
rec.journal = structuredClone(response.journal);
|
|
203
|
+
rec.journal.metadataState = { ...priorMetadata };
|
|
204
|
+
// Apply only the delta of metadata mutations. Each invocation's
|
|
205
|
+
// response re-emits every mutation the body made — including those
|
|
206
|
+
// from prior invocations, since the body replays from the start.
|
|
207
|
+
// The positional cursor on rec.metadataAppliedCount dedups them.
|
|
208
|
+
const newMutations = response.metadataUpdates.slice(rec.metadataAppliedCount);
|
|
209
|
+
applyMetadataUpdates(rec.journal.metadataState, newMutations);
|
|
210
|
+
rec.metadataAppliedCount = response.metadataUpdates.length;
|
|
211
|
+
// Accumulate stream chunks across invocations, grouped by streamId.
|
|
212
|
+
// Each response carries only chunks emitted in that invocation.
|
|
213
|
+
for (const chunk of response.streamChunks) {
|
|
214
|
+
const bucket = rec.streams[chunk.streamId] ?? [];
|
|
215
|
+
rec.streams[chunk.streamId] = bucket;
|
|
216
|
+
bucket.push({ ...chunk });
|
|
217
|
+
}
|
|
218
|
+
if (response.status === "completed") {
|
|
219
|
+
rec.status = "completed";
|
|
220
|
+
rec.output = response.output;
|
|
221
|
+
rec.completedAt = now();
|
|
222
|
+
rec.pendingWaitpoints = [];
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (response.status === "failed") {
|
|
226
|
+
rec.status = "failed";
|
|
227
|
+
rec.error = {
|
|
228
|
+
category: response.error.category,
|
|
229
|
+
code: response.error.code,
|
|
230
|
+
message: response.error.message,
|
|
231
|
+
};
|
|
232
|
+
rec.completedAt = now();
|
|
233
|
+
rec.pendingWaitpoints = [];
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (response.status === "cancelled") {
|
|
237
|
+
rec.status = "cancelled";
|
|
238
|
+
rec.completedAt = now();
|
|
239
|
+
rec.pendingWaitpoints = [];
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (response.status === "compensated" || response.status === "compensation_failed") {
|
|
243
|
+
rec.status = response.status;
|
|
244
|
+
if (response.error) {
|
|
245
|
+
rec.error = {
|
|
246
|
+
category: response.error.category,
|
|
247
|
+
code: response.error.code,
|
|
248
|
+
message: response.error.message,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
rec.completedAt = now();
|
|
252
|
+
rec.pendingWaitpoints = [];
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
// "waiting"
|
|
256
|
+
rec.status = "waiting";
|
|
257
|
+
const parkedAt = now();
|
|
258
|
+
rec.pendingWaitpoints = response.waitpoints.map((w) => {
|
|
259
|
+
const meta = { ...w.meta };
|
|
260
|
+
// Stamp wall-clock wake times on DATETIME waitpoints at park time,
|
|
261
|
+
// so alarm loops (local serve + CF DO) can fire at the right moment
|
|
262
|
+
// without re-deriving wall-clock from wherever the run is stored.
|
|
263
|
+
if (w.kind === "DATETIME" && typeof meta.wakeAt !== "number") {
|
|
264
|
+
const durationMs = w.timeoutMs ?? (typeof meta.durationMs === "number" ? meta.durationMs : 0);
|
|
265
|
+
meta.wakeAt = parkedAt + durationMs;
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
clientWaitpointId: w.clientWaitpointId,
|
|
269
|
+
kind: w.kind,
|
|
270
|
+
meta,
|
|
271
|
+
timeoutMs: w.timeoutMs,
|
|
272
|
+
};
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
function isTerminal(status) {
|
|
276
|
+
return (status === "completed" ||
|
|
277
|
+
status === "failed" ||
|
|
278
|
+
status === "cancelled" ||
|
|
279
|
+
status === "compensated" ||
|
|
280
|
+
status === "compensation_failed");
|
|
281
|
+
}
|
|
282
|
+
function matchWaitpoint(pending, inj) {
|
|
283
|
+
for (const wp of pending) {
|
|
284
|
+
if (wp.kind !== inj.kind)
|
|
285
|
+
continue;
|
|
286
|
+
if (inj.kind === "EVENT" && wp.meta.eventType === inj.eventType)
|
|
287
|
+
return wp;
|
|
288
|
+
if (inj.kind === "SIGNAL" && wp.meta.signalName === inj.name)
|
|
289
|
+
return wp;
|
|
290
|
+
if (inj.kind === "MANUAL" && wp.meta.tokenId === inj.tokenId)
|
|
291
|
+
return wp;
|
|
292
|
+
}
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
function injectionKey(inj) {
|
|
296
|
+
if (inj.kind === "EVENT")
|
|
297
|
+
return inj.eventType;
|
|
298
|
+
if (inj.kind === "SIGNAL")
|
|
299
|
+
return inj.name;
|
|
300
|
+
return inj.tokenId;
|
|
301
|
+
}
|
|
302
|
+
async function resolveChildRun(parent, wp, triggerChild, now) {
|
|
303
|
+
const childRecord = await triggerChild({ parent, waitpoint: wp });
|
|
304
|
+
const at = now();
|
|
305
|
+
if (wp.meta.detach === true) {
|
|
306
|
+
return {
|
|
307
|
+
kind: "resolved",
|
|
308
|
+
entry: {
|
|
309
|
+
kind: "RUN",
|
|
310
|
+
resolvedAt: at,
|
|
311
|
+
payload: undefined,
|
|
312
|
+
source: "replay",
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
if (childRecord.status === "completed") {
|
|
317
|
+
return {
|
|
318
|
+
kind: "resolved",
|
|
319
|
+
entry: {
|
|
320
|
+
kind: "RUN",
|
|
321
|
+
resolvedAt: at,
|
|
322
|
+
payload: childRecord.output,
|
|
323
|
+
source: "replay",
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
if (childRecord.status === "waiting") {
|
|
328
|
+
// Child parked on its own waitpoint(s). The parent parks too; the
|
|
329
|
+
// child's parent pointer (set by trigger's driveOptionsFor) will
|
|
330
|
+
// cascade-resume the parent when the child later reaches a
|
|
331
|
+
// terminal state via resume/cancel/alarm.
|
|
332
|
+
return { kind: "deferred" };
|
|
333
|
+
}
|
|
334
|
+
// Failed / cancelled / compensated / compensation_failed → surface as error.
|
|
335
|
+
const errMsg = childRecord.error?.message ?? `child run ended with status ${childRecord.status}`;
|
|
336
|
+
const errCode = childRecord.error?.code ?? "CHILD_RUN_ENDED";
|
|
337
|
+
return {
|
|
338
|
+
kind: "resolved",
|
|
339
|
+
entry: {
|
|
340
|
+
kind: "RUN",
|
|
341
|
+
resolvedAt: at,
|
|
342
|
+
source: "replay",
|
|
343
|
+
error: {
|
|
344
|
+
category: childRecord.error?.category ??
|
|
345
|
+
"USER_ERROR",
|
|
346
|
+
code: errCode,
|
|
347
|
+
message: errMsg,
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
function applyMetadataUpdates(state, updates) {
|
|
353
|
+
for (const u of updates) {
|
|
354
|
+
switch (u.op) {
|
|
355
|
+
case "set":
|
|
356
|
+
state[u.key] = u.value;
|
|
357
|
+
break;
|
|
358
|
+
case "increment": {
|
|
359
|
+
const cur = typeof state[u.key] === "number" ? state[u.key] : 0;
|
|
360
|
+
state[u.key] = cur + (u.value ?? 1);
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
case "append": {
|
|
364
|
+
const cur = Array.isArray(state[u.key]) ? state[u.key] : [];
|
|
365
|
+
state[u.key] = [...cur, u.value];
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
case "remove":
|
|
369
|
+
delete state[u.key];
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { EnvironmentName } from "@voyant-travel/workflows";
|
|
2
|
+
import { type DriverFactory } from "@voyant-travel/workflows/driver";
|
|
3
|
+
import type { RunRecord, StepHandler } from "./types.js";
|
|
4
|
+
export interface InMemoryDriverOptions {
|
|
5
|
+
/** Default environment for `trigger()` calls that don't specify one. */
|
|
6
|
+
defaultEnvironment?: EnvironmentName;
|
|
7
|
+
/** Tenant metadata stamped onto every triggered run. */
|
|
8
|
+
tenantMeta?: RunRecord["tenantMeta"];
|
|
9
|
+
/** Injectable clock; defaults to `Date.now`. */
|
|
10
|
+
now?: () => number;
|
|
11
|
+
/** Step handler override — defaults to in-process `handleStepRequest`. */
|
|
12
|
+
handler?: StepHandler;
|
|
13
|
+
/** Schedule runner tick interval. Defaults to 1_000 ms. */
|
|
14
|
+
schedulePollIntervalMs?: number;
|
|
15
|
+
/** Disable automatic firing for schedules registered through manifests. */
|
|
16
|
+
disableScheduleRunner?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Build an in-memory driver factory. The factory closes over its
|
|
20
|
+
* options and returns a fresh `WorkflowDriver` when `createApp()`
|
|
21
|
+
* (or a test) calls it with `DriverFactoryDeps`.
|
|
22
|
+
*
|
|
23
|
+
* Usage in tests:
|
|
24
|
+
*
|
|
25
|
+
* const driver = createInMemoryDriver()(testFactoryDeps())
|
|
26
|
+
* await driver.registerManifest({ environment: "production", manifest })
|
|
27
|
+
* await driver.trigger(myWorkflow, { … }, { idempotencyKey: "abc" })
|
|
28
|
+
*/
|
|
29
|
+
export declare function createInMemoryDriver(opts?: InMemoryDriverOptions): DriverFactory;
|
|
30
|
+
//# sourceMappingURL=driver-inmemory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"driver-inmemory.d.ts","sourceRoot":"","sources":["../src/driver-inmemory.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,eAAe,EAMhB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,KAAK,aAAa,EAQnB,MAAM,iCAAiC,CAAA;AAkBxC,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAIxD,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,kBAAkB,CAAC,EAAE,eAAe,CAAA;IACpC,wDAAwD;IACxD,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAA;IACpC,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,2DAA2D;IAC3D,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,2EAA2E;IAC3E,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC;AAQD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,qBAA0B,GAAG,aAAa,CA+TpF"}
|