@fluidgrids/workflows 2026.1028.1 → 2026.1029.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/README.md +72 -10
- package/dist/{activation-DnhG7SmF.d.ts → activation-BnVQMv9y.d.ts} +1 -1
- package/dist/{activation-BBh1p82z.d.cts → activation-CESehfYi.d.cts} +1 -1
- package/dist/{chunk-XB53G2DK.js → chunk-25BKWR2O.js} +1 -1
- package/dist/{chunk-CBL5JSCC.cjs → chunk-2B37XAFQ.cjs} +44 -8
- package/dist/{chunk-DJYMDLV5.js → chunk-2DDULZFM.js} +93 -1
- package/dist/{chunk-623FVQSN.cjs → chunk-3FIDHONL.cjs} +104 -0
- package/dist/chunk-6QPOJZT6.cjs +1180 -0
- package/dist/chunk-7ILGCUI6.js +154 -0
- package/dist/chunk-A2OHX3UQ.js +1156 -0
- package/dist/{chunk-VOKMAAJP.js → chunk-A36CCOWS.js} +188 -19
- package/dist/{chunk-FMSW2HDF.cjs → chunk-ELL2RBML.cjs} +0 -14
- package/dist/{chunk-JD2VQIDZ.cjs → chunk-EWYADREB.cjs} +2 -2
- package/dist/{chunk-5HDSBTBX.js → chunk-FQSJI5NV.js} +1 -1
- package/dist/{chunk-ABFZWYHT.js → chunk-FWBRQWRX.js} +156 -48
- package/dist/{chunk-PZCVF76Q.cjs → chunk-I47EENR4.cjs} +209 -40
- package/dist/chunk-J3PYFGKA.cjs +166 -0
- package/dist/{chunk-C5OLWZ42.js → chunk-KXJDRQ6G.js} +1 -13
- package/dist/chunk-M272AHEX.js +25 -0
- package/dist/{chunk-3FOTFQ3J.js → chunk-MJ5TGYVG.js} +333 -65
- package/dist/{chunk-57MSZ24T.cjs → chunk-NXFBVDOI.cjs} +3 -3
- package/dist/{chunk-PDHLTDZB.js → chunk-OIX4O6UR.js} +38 -2
- package/dist/{chunk-HR3BN4C4.cjs → chunk-OKX3FYIM.cjs} +3 -3
- package/dist/{chunk-CSNWNFKU.cjs → chunk-OMY3CKUZ.cjs} +404 -136
- package/dist/chunk-PWIUEWC3.js +81 -0
- package/dist/chunk-Q4MWQNJK.cjs +28 -0
- package/dist/{chunk-P6TA5EZT.cjs → chunk-RUQIMTN5.cjs} +171 -57
- package/dist/{chunk-IISBRUWJ.js → chunk-WXXNCHUY.js} +1 -1
- package/dist/chunk-YHBVMOMK.cjs +87 -0
- package/dist/codegen/index.d.cts +2 -2
- package/dist/codegen/index.d.ts +2 -2
- package/dist/collect-BTHjQaSp.d.ts +86 -0
- package/dist/collect-CSyOg5XC.d.cts +86 -0
- package/dist/{file-yuPCFU5b.d.ts → file-cgL9l9l8.d.cts} +765 -14
- package/dist/{file-BOoHZgoQ.d.cts → file-olQudcx5.d.ts} +765 -14
- package/dist/history/index.cjs +198 -86
- package/dist/history/index.d.cts +49 -10
- package/dist/history/index.d.ts +49 -10
- package/dist/history/index.js +49 -4
- package/dist/index.cjs +64 -51
- package/dist/index.d.cts +9 -53
- package/dist/index.d.ts +9 -53
- package/dist/index.js +5 -4
- package/dist/ir/index.cjs +24 -3
- package/dist/ir/index.d.cts +43 -4
- package/dist/ir/index.d.ts +43 -4
- package/dist/ir/index.js +24 -3
- package/dist/{lower-BuSvAFqi.d.cts → lower-3S6PsqjJ.d.cts} +41 -7
- package/dist/{lower-C_Qv_34S.d.ts → lower-DmZpQ8hJ.d.ts} +41 -7
- package/dist/lower-types-DQNBfWQn.d.cts +37 -0
- package/dist/lower-types-DQNBfWQn.d.ts +37 -0
- package/dist/{manifest-C8N6HDYl.d.ts → manifest-BJjrQt77.d.ts} +1 -1
- package/dist/{manifest-0OLw0i7k.d.cts → manifest-gRI2Q5Fj.d.cts} +1 -1
- package/dist/node-evaluator-SOADN23V.js +14 -0
- package/dist/node-evaluator-YGDEXK4U.cjs +24 -0
- package/dist/quickjs/index.cjs +25 -24
- package/dist/quickjs/index.d.cts +7 -7
- package/dist/quickjs/index.d.ts +7 -7
- package/dist/quickjs/index.js +8 -7
- package/dist/roundtrip/index.cjs +245 -43
- package/dist/roundtrip/index.d.cts +88 -11
- package/dist/roundtrip/index.d.ts +88 -11
- package/dist/roundtrip/index.js +172 -8
- package/dist/runtime/index.cjs +21 -20
- package/dist/runtime/index.d.cts +49 -7
- package/dist/runtime/index.d.ts +49 -7
- package/dist/runtime/index.js +8 -7
- package/dist/testing/index.cjs +514 -74
- package/dist/testing/index.d.cts +128 -11
- package/dist/testing/index.d.ts +128 -11
- package/dist/testing/index.js +480 -42
- package/dist/{types-BGoPzgcl.d.ts → types-Bb2wRnJ5.d.cts} +20 -1
- package/dist/{types-BGoPzgcl.d.cts → types-Bb2wRnJ5.d.ts} +20 -1
- package/dist/{types-DV-HX_Gt.d.ts → types-CQKOhPTa.d.cts} +122 -14
- package/dist/{types-CpBBjaGX.d.cts → types-CnIOinoz.d.ts} +122 -14
- package/dist/{workflow-Bpr3vnA_.d.ts → workflow-8uRU9rhL.d.ts} +2 -2
- package/dist/{workflow-FXUkpIBc.d.cts → workflow-CZOXUlQ5.d.cts} +2 -2
- package/dist/{workflow-J-yYixny.d.cts → workflow-DWTPHFbt.d.ts} +124 -12
- package/dist/{workflow-C2a05WUJ.d.ts → workflow-O6e64ZcG.d.cts} +124 -12
- package/fixtures/histories/auto-upgrade-patched.history.json +276 -0
- package/fixtures/histories/benign-failure.history.json +146 -0
- package/fixtures/histories/cancel-activity-try-cancel.history.json +150 -0
- package/fixtures/histories/child-cancel-wait-requested.history.json +217 -0
- package/fixtures/histories/child-options-roundtrip.history.json +192 -0
- package/fixtures/histories/continue-as-new-last-result.history.json +86 -0
- package/fixtures/histories/continue-as-new-options.history.json +99 -0
- package/fixtures/histories/continue-as-new-r1-long-task-queue.history.json +78 -0
- package/fixtures/histories/continue-as-new-r1-retry-options.history.json +83 -0
- package/fixtures/histories/execution-timeout-chain.history.json +98 -0
- package/fixtures/histories/external-cancel-resolved.history.json +123 -0
- package/fixtures/histories/external-cancel-unknown-target.history.json +127 -0
- package/fixtures/histories/failure-error-types-fails-run.history.json +144 -0
- package/fixtures/histories/handler-drain-warn.history.json +222 -0
- package/fixtures/histories/heartbeat-details-on-retry.history.json +145 -0
- package/fixtures/histories/local-activity-abandoned-at-completion.history.json +73 -0
- package/fixtures/histories/local-activity-cancelled-in-scope.history.json +118 -0
- package/fixtures/histories/local-activity-completed.history.json +157 -0
- package/fixtures/histories/local-activity-failed.history.json +93 -0
- package/fixtures/histories/local-activity-retry-in-task.history.json +88 -0
- package/fixtures/histories/run-retry-last-failure.history.json +90 -0
- package/fixtures/histories/run-timeout.history.json +159 -0
- package/fixtures/histories/schedule-to-start-timeout.history.json +132 -0
- package/fixtures/histories/start-delay-signal-ends-delay.history.json +155 -0
- package/fixtures/histories/typeerror-task-retry-100.history.json +137 -0
- package/package.json +1 -1
- package/schema/history-file.schema.json +283 -2
- package/schema/ir-v2.schema.json +45 -0
- package/schema/run-event.schema.json +264 -2
- package/schema/workflow-command.schema.json +178 -1
- package/schema/workflow-task.schema.json +392 -4
- package/dist/assemble-BLqQ8lSu.d.cts +0 -39
- package/dist/assemble-DWgaXHd-.d.ts +0 -39
- package/dist/chunk-2DP2Y6EU.cjs +0 -832
- package/dist/chunk-2J6TF7YI.cjs +0 -86
- package/dist/chunk-7NQ37GZU.js +0 -73
- package/dist/chunk-BO6OVILK.js +0 -79
- package/dist/chunk-FQIHX7YI.cjs +0 -80
- package/dist/chunk-H7KEVW2M.js +0 -816
- package/dist/lower-types-nofRXw9c.d.cts +0 -15
- package/dist/lower-types-nofRXw9c.d.ts +0 -15
- package/dist/node-evaluator-CJNP4GHX.js +0 -12
- package/dist/node-evaluator-DMKPKUWA.cjs +0 -22
package/dist/testing/index.js
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import { failedTaskResult } from '../chunk-
|
|
2
|
-
import { isTier1Workflow, toIR } from '../chunk-
|
|
1
|
+
import { failedTaskResult } from '../chunk-WXXNCHUY.js';
|
|
2
|
+
import { isTier1Workflow, toIR } from '../chunk-FQSJI5NV.js';
|
|
3
3
|
import { irHash, CODE_WORKFLOW_NODE_TYPE, normalizeDefinition } from '../chunk-BD7ZPG53.js';
|
|
4
4
|
import '../chunk-5LUIDMF4.js';
|
|
5
5
|
import '../chunk-JZ4TOOLN.js';
|
|
6
6
|
import '../chunk-E2VK7J2C.js';
|
|
7
7
|
import '../chunk-7AAZGVKD.js';
|
|
8
|
-
import { createHistoryFile, validateCommands, parseHistoryFile } from '../chunk-
|
|
8
|
+
import { createHistoryFile, validateCommands, parseHistoryFile } from '../chunk-A36CCOWS.js';
|
|
9
9
|
import { isRecord } from '../chunk-PMCM2OH4.js';
|
|
10
|
-
import { buildWorkflowRegistry, createWorkflowRuntime } from '../chunk-
|
|
11
|
-
import { failureToError, signalName,
|
|
12
|
-
import '../chunk-
|
|
10
|
+
import { buildWorkflowRegistry, createWorkflowRuntime } from '../chunk-MJ5TGYVG.js';
|
|
11
|
+
import { failureToError, signalName, ApplicationError, errorToFailure, CancelledFailure, DeterminismViolation } from '../chunk-KXJDRQ6G.js';
|
|
12
|
+
import '../chunk-M272AHEX.js';
|
|
13
13
|
import { TASK_PRELUDE } from '../chunk-4LYSEANA.js';
|
|
14
|
-
import { isDurableWorkflow } from '../chunk-
|
|
15
|
-
import
|
|
16
|
-
import {
|
|
17
|
-
import '../chunk-
|
|
14
|
+
import { runRetryPolicyOf, toMs, isDurableWorkflow } from '../chunk-7ILGCUI6.js';
|
|
15
|
+
import '../chunk-LNUQE6D6.js';
|
|
16
|
+
import { initialRunState, applyEvent, isActivityOpen, eventsForCommands, continueAsNewRetryShape, isChildOpen } from '../chunk-FWBRQWRX.js';
|
|
17
|
+
import { RUNTIME_RELEASE, isInputEvent, HISTORY_PROTOCOL, TERMINAL_COMMANDS } from '../chunk-2DDULZFM.js';
|
|
18
|
+
import { computeRetryDelay, computeRunRetryDelay } from '../chunk-PWIUEWC3.js';
|
|
18
19
|
import { createContext, Script } from 'vm';
|
|
19
20
|
|
|
20
21
|
// src/testing/host.ts
|
|
@@ -38,6 +39,32 @@ async function runWorkflowTaskInProcess(registry, request) {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
// src/testing/environment.ts
|
|
42
|
+
var WorkflowAlreadyStartedError = class extends Error {
|
|
43
|
+
constructor(workflowKey, workflowId, existingRunId) {
|
|
44
|
+
super(
|
|
45
|
+
`Workflow ${workflowKey} already has a run with workflow id "${workflowId}" (${existingRunId})`
|
|
46
|
+
);
|
|
47
|
+
this.workflowKey = workflowKey;
|
|
48
|
+
this.workflowId = workflowId;
|
|
49
|
+
this.existingRunId = existingRunId;
|
|
50
|
+
this.name = "WorkflowAlreadyStartedError";
|
|
51
|
+
}
|
|
52
|
+
workflowKey;
|
|
53
|
+
workflowId;
|
|
54
|
+
existingRunId;
|
|
55
|
+
};
|
|
56
|
+
var UnfinishedHandlersError = class extends Error {
|
|
57
|
+
constructor(runId, handlers) {
|
|
58
|
+
super(
|
|
59
|
+
`Run ${runId} finished while handlers were still running: ${handlers.map((handler) => `${handler.kind} ${handler.name} x${handler.count}`).join(", ")} (await ctx.allHandlersFinished(), or set unfinishedPolicy: 'ABANDON')`
|
|
60
|
+
);
|
|
61
|
+
this.runId = runId;
|
|
62
|
+
this.handlers = handlers;
|
|
63
|
+
this.name = "UnfinishedHandlersError";
|
|
64
|
+
}
|
|
65
|
+
runId;
|
|
66
|
+
handlers;
|
|
67
|
+
};
|
|
41
68
|
var WorkflowFailedError = class extends Error {
|
|
42
69
|
constructor(runId, failure) {
|
|
43
70
|
super(`Run ${runId} failed: ${failure.message}`);
|
|
@@ -113,6 +140,15 @@ function toDate(value) {
|
|
|
113
140
|
if (value instanceof Date) return value.getTime();
|
|
114
141
|
return typeof value === "number" ? value : Date.parse(value);
|
|
115
142
|
}
|
|
143
|
+
function optionalDuration(value, what) {
|
|
144
|
+
return value === void 0 ? void 0 : toMs(value, what);
|
|
145
|
+
}
|
|
146
|
+
var FAILED_STATUSES = /* @__PURE__ */ new Set([
|
|
147
|
+
"FAILED",
|
|
148
|
+
"CANCELLED",
|
|
149
|
+
"TERMINATED",
|
|
150
|
+
"TIMED_OUT"
|
|
151
|
+
]);
|
|
116
152
|
function toJsonValue(value) {
|
|
117
153
|
if (value === void 0) return null;
|
|
118
154
|
const text = JSON.stringify(value);
|
|
@@ -122,6 +158,10 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
122
158
|
clock;
|
|
123
159
|
timeSkipping;
|
|
124
160
|
enabled;
|
|
161
|
+
failOnUnfinishedHandlers;
|
|
162
|
+
historyRelease;
|
|
163
|
+
dues = [];
|
|
164
|
+
held = /* @__PURE__ */ new Set();
|
|
125
165
|
workspaceId;
|
|
126
166
|
runs = /* @__PURE__ */ new Map();
|
|
127
167
|
runOrder = [];
|
|
@@ -139,6 +179,8 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
139
179
|
this.clock = toDate(options.startTime);
|
|
140
180
|
this.timeSkipping = options.timeSkipping !== false;
|
|
141
181
|
this.enabled = options.enabled ?? "parity";
|
|
182
|
+
this.failOnUnfinishedHandlers = options.failOnUnfinishedHandlers !== false;
|
|
183
|
+
this.historyRelease = options.historyRelease ?? RUNTIME_RELEASE;
|
|
142
184
|
this.workspaceId = options.workspaceId ?? "test-workspace";
|
|
143
185
|
this.workflows = { ...options.workflows ?? {} };
|
|
144
186
|
this.registry = registryOf(this.workflows);
|
|
@@ -162,6 +204,22 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
162
204
|
impl
|
|
163
205
|
);
|
|
164
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Hold the activity queue of a node type (every node type when omitted):
|
|
209
|
+
* attempts wait instead of starting, and a `scheduleToStartTimeout` expires
|
|
210
|
+
* (`ACTIVITY_TIMED_OUT { timeoutType: SCHEDULE_TO_START }`, not retried).
|
|
211
|
+
*/
|
|
212
|
+
holdActivities(nodeType = "*") {
|
|
213
|
+
this.held.add(nodeType);
|
|
214
|
+
}
|
|
215
|
+
/** Release held activity queues (all when no node type is given). */
|
|
216
|
+
releaseActivities(nodeType) {
|
|
217
|
+
if (nodeType === void 0) this.held.clear();
|
|
218
|
+
else this.held.delete(nodeType);
|
|
219
|
+
}
|
|
220
|
+
isHeld(nodeType) {
|
|
221
|
+
return this.held.has("*") || this.held.has(nodeType);
|
|
222
|
+
}
|
|
165
223
|
/** Mock a child workflow started by key (graph or durable). */
|
|
166
224
|
mockChild(workflowKey, impl) {
|
|
167
225
|
this.childMocks.set(workflowKey, impl);
|
|
@@ -172,17 +230,85 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
172
230
|
// A declared workflow is stored by its declaration; its input type is the caller's.
|
|
173
231
|
this.ensureRegistered(definition)
|
|
174
232
|
);
|
|
233
|
+
const declared = this.workflows[workflowFn]?.options ?? {};
|
|
234
|
+
const workflowKey = options.workflowKey ?? (typeof definition === "string" ? this.workflows[workflowFn]?.name ?? workflowFn : definition.name);
|
|
235
|
+
if (options.workflowId !== void 0) {
|
|
236
|
+
const existing = this.useExisting(workflowKey, options);
|
|
237
|
+
if (existing !== void 0) {
|
|
238
|
+
for (const signal of options.signals ?? [])
|
|
239
|
+
this.deliverSignal(existing, signal.name, signal.payload);
|
|
240
|
+
await this.runUntilIdle(false);
|
|
241
|
+
return new TestRunHandle(this, existing);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const executionTimeoutMs = optionalDuration(
|
|
245
|
+
options.executionTimeout ?? declared.executionTimeout,
|
|
246
|
+
"executionTimeout"
|
|
247
|
+
);
|
|
248
|
+
const retry = options.retry ?? declared.retry;
|
|
249
|
+
const config = {
|
|
250
|
+
workflowKey,
|
|
251
|
+
runAttempt: 1,
|
|
252
|
+
...options.workflowId !== void 0 ? { businessId: options.workflowId } : {},
|
|
253
|
+
...executionTimeoutMs !== void 0 ? { executionTimeoutMs, executionDeadline: this.clock + executionTimeoutMs } : {},
|
|
254
|
+
...this.durationConfig(options.runTimeout ?? declared.runTimeout, "runTimeoutMs"),
|
|
255
|
+
...this.durationConfig(
|
|
256
|
+
options.workflowTaskTimeout ?? declared.workflowTaskTimeout,
|
|
257
|
+
"workflowTaskTimeoutMs"
|
|
258
|
+
),
|
|
259
|
+
...this.durationConfig(options.startDelay, "startDelayMs"),
|
|
260
|
+
...retry !== void 0 ? { retry: runRetryPolicyOf(retry) } : {},
|
|
261
|
+
...options.summary !== void 0 ? { summary: options.summary } : {},
|
|
262
|
+
...options.details !== void 0 ? { details: options.details } : {},
|
|
263
|
+
...options.memo !== void 0 ? { memo: toJsonValue(options.memo) } : {},
|
|
264
|
+
...(options.versioningBehavior ?? declared.versioningBehavior) !== void 0 ? { versioningBehavior: options.versioningBehavior ?? declared.versioningBehavior } : {}
|
|
265
|
+
};
|
|
175
266
|
const runId = this.createRun(
|
|
176
267
|
workflowFn,
|
|
177
268
|
toJsonValue(options.input),
|
|
178
269
|
{ ...options.info, ...options.runId !== void 0 ? { runId: options.runId } : {} },
|
|
179
|
-
void 0
|
|
270
|
+
void 0,
|
|
271
|
+
{},
|
|
272
|
+
config
|
|
180
273
|
);
|
|
181
274
|
for (const signal of options.signals ?? [])
|
|
182
275
|
this.deliverSignal(runId, signal.name, signal.payload);
|
|
183
276
|
await this.runUntilIdle(false);
|
|
184
277
|
return new TestRunHandle(this, runId);
|
|
185
278
|
}
|
|
279
|
+
durationConfig(value, key) {
|
|
280
|
+
const ms = optionalDuration(value, key);
|
|
281
|
+
if (ms === void 0) return {};
|
|
282
|
+
const out = {};
|
|
283
|
+
out[key] = ms;
|
|
284
|
+
return out;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* The workflow-id policies of a start (11 §2.1): the open run to use, or
|
|
288
|
+
* undefined to start a new run (after terminating the open one for
|
|
289
|
+
* `TERMINATE_EXISTING`). Throws {@link WorkflowAlreadyStartedError}.
|
|
290
|
+
*/
|
|
291
|
+
useExisting(workflowKey, options) {
|
|
292
|
+
const workflowId = options.workflowId ?? "";
|
|
293
|
+
const runs = this.runOrder.map((id) => this.run(id)).filter(
|
|
294
|
+
(run) => run.config.workflowKey === workflowKey && run.config.businessId === workflowId
|
|
295
|
+
);
|
|
296
|
+
const open = runs.find((run) => run.state.status === "RUNNING");
|
|
297
|
+
if (open) {
|
|
298
|
+
const conflict = options.idConflictPolicy ?? "FAIL";
|
|
299
|
+
if (conflict === "USE_EXISTING") return open.runId;
|
|
300
|
+
if (conflict === "FAIL")
|
|
301
|
+
throw new WorkflowAlreadyStartedError(workflowKey, workflowId, open.runId);
|
|
302
|
+
this.terminate(open.runId, "ID_CONFLICT");
|
|
303
|
+
return void 0;
|
|
304
|
+
}
|
|
305
|
+
const last = runs[runs.length - 1];
|
|
306
|
+
if (!last) return void 0;
|
|
307
|
+
const reuse = options.idReusePolicy ?? "ALLOW_DUPLICATE";
|
|
308
|
+
if (reuse === "REJECT_DUPLICATE" || reuse === "ALLOW_DUPLICATE_FAILED_ONLY" && !FAILED_STATUSES.has(last.state.status))
|
|
309
|
+
throw new WorkflowAlreadyStartedError(workflowKey, workflowId, last.runId);
|
|
310
|
+
return void 0;
|
|
311
|
+
}
|
|
186
312
|
/** Advance the clock by `duration`, firing timers and running work as it comes due. */
|
|
187
313
|
async sleep(duration) {
|
|
188
314
|
const until = this.clock + toMs(duration, "sleep duration");
|
|
@@ -233,7 +359,7 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
233
359
|
if (!run) throw new Error(`Unknown run ${runId}`);
|
|
234
360
|
return run;
|
|
235
361
|
}
|
|
236
|
-
createRun(workflowFn, input, info, parent, extraStarted = {}) {
|
|
362
|
+
createRun(workflowFn, input, info, parent, extraStarted = {}, config = { workflowKey: workflowFn, runAttempt: 1 }) {
|
|
237
363
|
this.runCounter += 1;
|
|
238
364
|
const runId = info.runId ?? `run-${this.runCounter}`;
|
|
239
365
|
const snapshot = {
|
|
@@ -254,18 +380,62 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
254
380
|
taskCounter: 0,
|
|
255
381
|
info: snapshot,
|
|
256
382
|
children: /* @__PURE__ */ new Map(),
|
|
383
|
+
config,
|
|
257
384
|
...parent ? { parent } : {}
|
|
258
385
|
};
|
|
386
|
+
const firstTaskDelay = config.startDelayMs ?? config.firstTaskDelayMs;
|
|
387
|
+
if (firstTaskDelay !== void 0 && firstTaskDelay > 0) {
|
|
388
|
+
run.delayedUntil = this.clock + firstTaskDelay;
|
|
389
|
+
this.dues.push({ runId, kind: "start", at: run.delayedUntil });
|
|
390
|
+
}
|
|
391
|
+
if (config.runTimeoutMs !== void 0)
|
|
392
|
+
this.dues.push({ runId, kind: "runTimeout", at: this.clock + config.runTimeoutMs });
|
|
393
|
+
if (config.executionDeadline !== void 0)
|
|
394
|
+
this.dues.push({ runId, kind: "executionTimeout", at: config.executionDeadline });
|
|
259
395
|
this.runs.set(runId, run);
|
|
260
396
|
this.runOrder.push(runId);
|
|
261
397
|
this.append(run, {
|
|
262
398
|
type: "WORKFLOW_STARTED",
|
|
263
399
|
// The service writes the seed of ctx.random()/ctx.uuid() into the start
|
|
264
400
|
// event so the history replays on its own; it uses the run id.
|
|
265
|
-
payload: {
|
|
401
|
+
payload: {
|
|
402
|
+
workflowFn,
|
|
403
|
+
input,
|
|
404
|
+
taskQueue: config.taskQueue ?? "managed",
|
|
405
|
+
randomnessSeed: runId,
|
|
406
|
+
...extraStarted,
|
|
407
|
+
...this.startedFields(config)
|
|
408
|
+
}
|
|
266
409
|
});
|
|
267
410
|
return runId;
|
|
268
411
|
}
|
|
412
|
+
/** The R2 fields of `WORKFLOW_STARTED`, each written only when it is set. */
|
|
413
|
+
startedFields(config) {
|
|
414
|
+
const out = {};
|
|
415
|
+
if (config.businessId !== void 0) {
|
|
416
|
+
out["businessId"] = config.businessId;
|
|
417
|
+
out["workflowId"] = config.businessId;
|
|
418
|
+
}
|
|
419
|
+
if (config.startDelayMs !== void 0) out["startDelayMs"] = config.startDelayMs;
|
|
420
|
+
if (config.retry !== void 0 || config.runAttempt > 1) out["runAttempt"] = config.runAttempt;
|
|
421
|
+
if (config.summary !== void 0) out["summary"] = config.summary;
|
|
422
|
+
if (config.details !== void 0) out["details"] = config.details;
|
|
423
|
+
if (config.memo !== void 0) out["context"] = config.memo;
|
|
424
|
+
const timeouts = {};
|
|
425
|
+
if (config.executionTimeoutMs !== void 0)
|
|
426
|
+
timeouts["executionTimeout"] = config.executionTimeoutMs;
|
|
427
|
+
if (config.runTimeoutMs !== void 0) timeouts["runTimeout"] = config.runTimeoutMs;
|
|
428
|
+
if (config.workflowTaskTimeoutMs !== void 0)
|
|
429
|
+
timeouts["workflowTaskTimeout"] = config.workflowTaskTimeoutMs;
|
|
430
|
+
if (Object.keys(timeouts).length > 0) out["timeouts"] = timeouts;
|
|
431
|
+
if (config.retry !== void 0) out["retry"] = toJsonValue(config.retry);
|
|
432
|
+
if (config.versioningBehavior !== void 0)
|
|
433
|
+
out["versioningBehavior"] = config.versioningBehavior;
|
|
434
|
+
if (config.lastCompletionResult !== void 0 && this.historyRelease >= 2)
|
|
435
|
+
out["lastCompletionResult"] = config.lastCompletionResult;
|
|
436
|
+
if (config.lastFailure !== void 0) out["lastFailure"] = toJsonValue(config.lastFailure);
|
|
437
|
+
return out;
|
|
438
|
+
}
|
|
269
439
|
append(run, draft) {
|
|
270
440
|
const event = {
|
|
271
441
|
seq: run.history.length + 1,
|
|
@@ -276,7 +446,8 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
276
446
|
};
|
|
277
447
|
applyEvent(run.state, event);
|
|
278
448
|
run.history.push(event);
|
|
279
|
-
if (
|
|
449
|
+
if (run.delayedUntil !== void 0 && event.type === "SIGNAL_RECEIVED") this.endDelay(run);
|
|
450
|
+
if (run.delayedUntil === void 0 && isInputEvent(event) && run.state.status === "RUNNING" && run.state.task.state === "NONE") {
|
|
280
451
|
run.taskCounter += 1;
|
|
281
452
|
const taskId = `${run.runId}-task-${run.taskCounter}`;
|
|
282
453
|
const scheduled = {
|
|
@@ -290,6 +461,22 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
290
461
|
}
|
|
291
462
|
return event;
|
|
292
463
|
}
|
|
464
|
+
/** End a start delay: the first workflow task is scheduled now. */
|
|
465
|
+
endDelay(run) {
|
|
466
|
+
run.delayedUntil = void 0;
|
|
467
|
+
this.dues = this.dues.filter((due) => !(due.runId === run.runId && due.kind === "start"));
|
|
468
|
+
if (run.state.status !== "RUNNING" || run.state.task.state !== "NONE") return;
|
|
469
|
+
run.taskCounter += 1;
|
|
470
|
+
const taskId = `${run.runId}-task-${run.taskCounter}`;
|
|
471
|
+
const scheduled = {
|
|
472
|
+
seq: run.history.length + 1,
|
|
473
|
+
type: "WORKFLOW_TASK_SCHEDULED",
|
|
474
|
+
payload: { taskId },
|
|
475
|
+
createdAt: new Date(this.clock).toISOString()
|
|
476
|
+
};
|
|
477
|
+
applyEvent(run.state, scheduled);
|
|
478
|
+
run.history.push(scheduled);
|
|
479
|
+
}
|
|
293
480
|
deliverSignal(runId, name, payload) {
|
|
294
481
|
const run = this.runs.get(runId);
|
|
295
482
|
if (!run || run.state.status !== "RUNNING") return false;
|
|
@@ -311,6 +498,11 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
311
498
|
type: "WORKFLOW_CANCEL_REQUESTED",
|
|
312
499
|
payload: reason !== void 0 ? { reason } : {}
|
|
313
500
|
});
|
|
501
|
+
if (run.delayedUntil !== void 0) {
|
|
502
|
+
run.delayedUntil = void 0;
|
|
503
|
+
this.append(run, { type: "WORKFLOW_CANCELLED", payload: {} });
|
|
504
|
+
this.onClosed(run);
|
|
505
|
+
}
|
|
314
506
|
return true;
|
|
315
507
|
}
|
|
316
508
|
terminate(runId, reason) {
|
|
@@ -330,7 +522,9 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
330
522
|
await this.runTask(task);
|
|
331
523
|
continue;
|
|
332
524
|
}
|
|
333
|
-
const attempt = this.attempts.find(
|
|
525
|
+
const attempt = this.attempts.find(
|
|
526
|
+
(entry) => entry.dueAt <= this.clock && !this.isHeld(entry.nodeType)
|
|
527
|
+
);
|
|
334
528
|
if (attempt) {
|
|
335
529
|
this.attempts = this.attempts.filter((entry) => entry !== attempt);
|
|
336
530
|
await this.runAttempt(attempt);
|
|
@@ -349,16 +543,78 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
349
543
|
}
|
|
350
544
|
throw new Error("The test environment did not settle after 100000 steps");
|
|
351
545
|
}
|
|
546
|
+
/** When a held attempt's schedule-to-start timeout expires, if it has one. */
|
|
547
|
+
heldDeadline(entry) {
|
|
548
|
+
return this.isHeld(entry.nodeType) && entry.scheduleToStart !== void 0 ? entry.dueAt + entry.scheduleToStart : void 0;
|
|
549
|
+
}
|
|
352
550
|
nextDue() {
|
|
353
|
-
const due = [
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
551
|
+
const due = [...this.timers.map((timer) => timer.fireAt)];
|
|
552
|
+
for (const entry of this.attempts) {
|
|
553
|
+
if (!this.isHeld(entry.nodeType)) due.push(entry.dueAt);
|
|
554
|
+
const deadline = this.heldDeadline(entry);
|
|
555
|
+
if (deadline !== void 0) due.push(deadline);
|
|
556
|
+
}
|
|
557
|
+
for (const entry of this.dues) due.push(entry.at);
|
|
357
558
|
return due.length === 0 ? void 0 : Math.min(...due);
|
|
358
559
|
}
|
|
560
|
+
/** Expire held attempts whose schedule-to-start timeout passed (not retried, 11 §2.3). */
|
|
561
|
+
expireHeld() {
|
|
562
|
+
const expired = this.attempts.filter((entry) => {
|
|
563
|
+
const deadline = this.heldDeadline(entry);
|
|
564
|
+
return deadline !== void 0 && deadline <= this.clock;
|
|
565
|
+
});
|
|
566
|
+
if (expired.length === 0) return false;
|
|
567
|
+
this.attempts = this.attempts.filter((entry) => !expired.includes(entry));
|
|
568
|
+
for (const entry of expired) {
|
|
569
|
+
const run = this.runs.get(entry.runId);
|
|
570
|
+
const activity = run?.state.activities[entry.activityId];
|
|
571
|
+
if (!run || run.state.status !== "RUNNING" || !activity || !isActivityOpen(activity))
|
|
572
|
+
continue;
|
|
573
|
+
this.append(run, {
|
|
574
|
+
type: "ACTIVITY_TIMED_OUT",
|
|
575
|
+
payload: {
|
|
576
|
+
activityId: entry.activityId,
|
|
577
|
+
attempt: entry.attempt,
|
|
578
|
+
timeoutType: "SCHEDULE_TO_START"
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
return true;
|
|
583
|
+
}
|
|
584
|
+
/** Fire run-level deadlines: the end of a start delay, run and execution timeouts. */
|
|
585
|
+
fireRunDues() {
|
|
586
|
+
const due = this.dues.filter((entry) => entry.at <= this.clock).sort((a, b) => a.at - b.at);
|
|
587
|
+
if (due.length === 0) return false;
|
|
588
|
+
this.dues = this.dues.filter((entry) => !due.includes(entry));
|
|
589
|
+
for (const entry of due) {
|
|
590
|
+
const run = this.runs.get(entry.runId);
|
|
591
|
+
if (!run || run.state.status !== "RUNNING") continue;
|
|
592
|
+
if (entry.kind === "start") {
|
|
593
|
+
if (run.delayedUntil !== void 0) this.endDelay(run);
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
const timeoutType = entry.kind === "runTimeout" ? "WORKFLOW_RUN" : "WORKFLOW_EXECUTION";
|
|
597
|
+
const failure = {
|
|
598
|
+
errorType: "TIMEOUT",
|
|
599
|
+
message: timeoutType === "WORKFLOW_RUN" ? "The run timeout expired" : "The execution timeout of the chain expired",
|
|
600
|
+
type: "TimeoutFailure",
|
|
601
|
+
timeoutType,
|
|
602
|
+
source: "engine"
|
|
603
|
+
};
|
|
604
|
+
run.delayedUntil = void 0;
|
|
605
|
+
this.append(run, {
|
|
606
|
+
type: "WORKFLOW_TIMED_OUT",
|
|
607
|
+
payload: { timeoutType, failure: toJsonValue(failure) }
|
|
608
|
+
});
|
|
609
|
+
this.onClosed(run);
|
|
610
|
+
}
|
|
611
|
+
return true;
|
|
612
|
+
}
|
|
359
613
|
fireDue() {
|
|
614
|
+
const expired = this.expireHeld();
|
|
615
|
+
const fired = this.fireRunDues();
|
|
360
616
|
const due = this.timers.filter((timer) => timer.fireAt <= this.clock).sort((a, b) => a.fireAt - b.fireAt);
|
|
361
|
-
if (due.length === 0) return
|
|
617
|
+
if (due.length === 0) return expired || fired;
|
|
362
618
|
this.timers = this.timers.filter((timer) => !due.includes(timer));
|
|
363
619
|
for (const timer of due) {
|
|
364
620
|
const run = this.runs.get(timer.runId);
|
|
@@ -379,7 +635,7 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
379
635
|
history: run.history,
|
|
380
636
|
nowMs: this.clock
|
|
381
637
|
};
|
|
382
|
-
const result =
|
|
638
|
+
const result = await this.executeWithLocalActivities(run, request);
|
|
383
639
|
const fail = (cause, message) => {
|
|
384
640
|
this.append(run, {
|
|
385
641
|
type: "WORKFLOW_TASK_FAILED",
|
|
@@ -406,12 +662,69 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
406
662
|
fail(drafts.cause, drafts.issues.map((issue) => issue.message).join("; "));
|
|
407
663
|
return;
|
|
408
664
|
}
|
|
665
|
+
if (result.unfinishedHandlers !== void 0 && result.unfinishedHandlers.length > 0)
|
|
666
|
+
run.unfinishedHandlers = result.unfinishedHandlers;
|
|
409
667
|
this.append(run, { type: "WORKFLOW_TASK_COMPLETED", taskId, payload: { taskId } });
|
|
410
668
|
for (const draft of drafts.events) this.append(run, draft);
|
|
411
669
|
for (const command of checked.commands)
|
|
412
670
|
if (run.state.status === "RUNNING" || TERMINAL_COMMANDS.has(command.type))
|
|
413
671
|
this.afterCommand(run, command);
|
|
414
672
|
}
|
|
673
|
+
execute(request) {
|
|
674
|
+
return this.executor ? this.executor(request) : runWorkflowTaskInProcess(this.registry, request);
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Run a workflow task as the worker does (11 §2.12): while it asks for
|
|
678
|
+
* local activities, run each through its mock and run the same task again
|
|
679
|
+
* with the outcomes, up to 1,000 rounds.
|
|
680
|
+
*/
|
|
681
|
+
async executeWithLocalActivities(run, request) {
|
|
682
|
+
const outcomes = /* @__PURE__ */ new Map();
|
|
683
|
+
let result = await this.execute(request);
|
|
684
|
+
for (let round = 0; result.outcome === "localActivityRequested"; round++) {
|
|
685
|
+
if (round >= 1e3)
|
|
686
|
+
return {
|
|
687
|
+
...result,
|
|
688
|
+
outcome: "failed",
|
|
689
|
+
commands: [],
|
|
690
|
+
failure: { cause: "INTERNAL", message: "The task asked for local activities 1000 times" }
|
|
691
|
+
};
|
|
692
|
+
for (const wanted of result.localActivityRequests ?? [])
|
|
693
|
+
outcomes.set(wanted.localActivityId, await this.runLocalActivity(run, wanted));
|
|
694
|
+
result = await this.execute({ ...request, localActivityResults: [...outcomes.values()] });
|
|
695
|
+
}
|
|
696
|
+
return result;
|
|
697
|
+
}
|
|
698
|
+
async runLocalActivity(run, wanted) {
|
|
699
|
+
const mock = this.mocks.get(`${wanted.nodeType}#${wanted.operation}`) ?? this.mocks.get(wanted.nodeType);
|
|
700
|
+
try {
|
|
701
|
+
if (!mock)
|
|
702
|
+
throw new ApplicationError(
|
|
703
|
+
`No mock for ${wanted.nodeType} ${wanted.operation} (use env.mock)`,
|
|
704
|
+
{ errorType: "VALIDATION", nonRetryable: true, type: "UnmockedActivity" }
|
|
705
|
+
);
|
|
706
|
+
const result = toJsonValue(
|
|
707
|
+
await mock(wanted.args, {
|
|
708
|
+
runId: run.runId,
|
|
709
|
+
activityId: wanted.localActivityId,
|
|
710
|
+
attempt: wanted.attempt,
|
|
711
|
+
nodeType: wanted.nodeType,
|
|
712
|
+
operation: wanted.operation,
|
|
713
|
+
local: true,
|
|
714
|
+
...wanted.connection !== void 0 ? { connection: wanted.connection } : {}
|
|
715
|
+
})
|
|
716
|
+
);
|
|
717
|
+
return { localActivityId: wanted.localActivityId, result, attempts: wanted.attempt };
|
|
718
|
+
} catch (error) {
|
|
719
|
+
const failure = errorToFailure(error, `node:${wanted.nodeType}`);
|
|
720
|
+
delete failure.stackTrace;
|
|
721
|
+
return {
|
|
722
|
+
localActivityId: wanted.localActivityId,
|
|
723
|
+
failure: toJsonValue(failure),
|
|
724
|
+
attempts: wanted.attempt
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
}
|
|
415
728
|
afterCommand(run, command) {
|
|
416
729
|
switch (command.type) {
|
|
417
730
|
case "SCHEDULE_ACTIVITY": {
|
|
@@ -427,7 +740,8 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
427
740
|
retry: command.retry,
|
|
428
741
|
cancelRequested: false,
|
|
429
742
|
...command.connection !== void 0 ? { connection: command.connection } : {},
|
|
430
|
-
...command.scheduleToCloseTimeout !== void 0 ? { scheduleToClose: command.scheduleToCloseTimeout } : {}
|
|
743
|
+
...command.scheduleToCloseTimeout !== void 0 ? { scheduleToClose: command.scheduleToCloseTimeout } : {},
|
|
744
|
+
...command.scheduleToStartTimeout !== void 0 ? { scheduleToStart: command.scheduleToStartTimeout } : {}
|
|
431
745
|
};
|
|
432
746
|
this.attempts.push(attempt);
|
|
433
747
|
return;
|
|
@@ -502,18 +816,25 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
502
816
|
return;
|
|
503
817
|
}
|
|
504
818
|
case "CONTINUE_AS_NEW": {
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
)
|
|
516
|
-
|
|
819
|
+
const options = command.options;
|
|
820
|
+
const config = {
|
|
821
|
+
...this.chainConfig(run.config),
|
|
822
|
+
// The run timeout restarts and the attempt count resets; the
|
|
823
|
+
// execution timeout of the chain carries over (11 §2.20).
|
|
824
|
+
lastCompletionResult: command.input
|
|
825
|
+
};
|
|
826
|
+
if (typeof options["taskQueue"] === "string") config.taskQueue = options["taskQueue"];
|
|
827
|
+
if (typeof options["workflowTaskTimeout"] === "number")
|
|
828
|
+
config.workflowTaskTimeoutMs = options["workflowTaskTimeout"];
|
|
829
|
+
if (options["versioningBehavior"] === "PINNED" || options["versioningBehavior"] === "AUTO_UPGRADE")
|
|
830
|
+
config.versioningBehavior = options["versioningBehavior"];
|
|
831
|
+
const memo = options["context"];
|
|
832
|
+
if (typeof memo === "object" && memo !== null && !Array.isArray(memo)) config.memo = memo;
|
|
833
|
+
else if (Object.keys(run.state.context).length > 0) config.memo = { ...run.state.context };
|
|
834
|
+
const retry = options["retry"];
|
|
835
|
+
if (continueAsNewRetryShape(options) === "run" && typeof retry === "object" && retry !== null && !Array.isArray(retry))
|
|
836
|
+
config.retry = retry;
|
|
837
|
+
this.startSuccessor(run, command.input, config);
|
|
517
838
|
this.onClosed(run);
|
|
518
839
|
return;
|
|
519
840
|
}
|
|
@@ -526,11 +847,62 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
526
847
|
return;
|
|
527
848
|
}
|
|
528
849
|
}
|
|
850
|
+
/** What a successor of a run inherits (continue-as-new, run retry). */
|
|
851
|
+
chainConfig(config) {
|
|
852
|
+
const out = { workflowKey: config.workflowKey, runAttempt: 1 };
|
|
853
|
+
for (const key of [
|
|
854
|
+
"businessId",
|
|
855
|
+
"executionDeadline",
|
|
856
|
+
"executionTimeoutMs",
|
|
857
|
+
"runTimeoutMs",
|
|
858
|
+
"workflowTaskTimeoutMs",
|
|
859
|
+
"retry",
|
|
860
|
+
"versioningBehavior",
|
|
861
|
+
"taskQueue",
|
|
862
|
+
"memo"
|
|
863
|
+
])
|
|
864
|
+
if (config[key] !== void 0) Object.assign(out, { [key]: config[key] });
|
|
865
|
+
return out;
|
|
866
|
+
}
|
|
867
|
+
/** Start the next run of a chain; the closed run's parent and handles follow it. */
|
|
868
|
+
startSuccessor(run, input, config) {
|
|
869
|
+
const next = this.createRun(
|
|
870
|
+
run.workflowFn,
|
|
871
|
+
input,
|
|
872
|
+
{
|
|
873
|
+
...run.info,
|
|
874
|
+
runId: void 0,
|
|
875
|
+
continuedFromRunId: run.runId,
|
|
876
|
+
firstRunId: run.info.firstRunId ?? run.runId
|
|
877
|
+
},
|
|
878
|
+
run.parent,
|
|
879
|
+
{},
|
|
880
|
+
config
|
|
881
|
+
);
|
|
882
|
+
run.continuedTo = next;
|
|
883
|
+
return next;
|
|
884
|
+
}
|
|
529
885
|
resolveTarget(run, target) {
|
|
530
886
|
if ("runId" in target) return target.runId;
|
|
531
887
|
if ("parent" in target) return run.parent?.runId ?? run.info.parentRunId ?? void 0;
|
|
532
|
-
if ("childId" in target)
|
|
533
|
-
|
|
888
|
+
if ("childId" in target) {
|
|
889
|
+
let childRunId = run.children.get(target.childId);
|
|
890
|
+
while (childRunId !== void 0) {
|
|
891
|
+
const next = this.runs.get(childRunId)?.continuedTo;
|
|
892
|
+
if (next === void 0) break;
|
|
893
|
+
childRunId = next;
|
|
894
|
+
}
|
|
895
|
+
return childRunId;
|
|
896
|
+
}
|
|
897
|
+
return this.resolveWorkflowId(target.workflowKey, target.workflowId, target.firstRunId);
|
|
898
|
+
}
|
|
899
|
+
/** The open run of a workflow id, else its latest run (11 §2.1). */
|
|
900
|
+
resolveWorkflowId(workflowKey, workflowId, firstRunId) {
|
|
901
|
+
const matching = this.runOrder.map((id) => this.run(id)).filter(
|
|
902
|
+
(candidate) => candidate.config.workflowKey === workflowKey && candidate.config.businessId === workflowId && (firstRunId === void 0 || (candidate.info.firstRunId ?? candidate.runId) === firstRunId)
|
|
903
|
+
);
|
|
904
|
+
const open = matching.find((candidate) => candidate.state.status === "RUNNING");
|
|
905
|
+
return (open ?? matching[matching.length - 1])?.runId;
|
|
534
906
|
}
|
|
535
907
|
startChild(run, command) {
|
|
536
908
|
const byFn = command.workflowFn !== void 0 && (this.executor !== void 0 || this.registry.byName.has(command.workflowFn));
|
|
@@ -563,6 +935,28 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
563
935
|
});
|
|
564
936
|
return;
|
|
565
937
|
}
|
|
938
|
+
const options = command.options;
|
|
939
|
+
const ms = (key) => typeof options[key] === "number" ? options[key] : void 0;
|
|
940
|
+
const executionTimeoutMs = ms("executionTimeout");
|
|
941
|
+
const childKey = command.workflowKey ?? this.registry.byName.get(workflowFn)?.name ?? workflowFn;
|
|
942
|
+
const config = { workflowKey: childKey, runAttempt: 1 };
|
|
943
|
+
if (typeof options.workflowId === "string") config.businessId = options.workflowId;
|
|
944
|
+
if (executionTimeoutMs !== void 0) {
|
|
945
|
+
config.executionTimeoutMs = executionTimeoutMs;
|
|
946
|
+
config.executionDeadline = this.clock + executionTimeoutMs;
|
|
947
|
+
}
|
|
948
|
+
const runTimeoutMs = ms("runTimeout");
|
|
949
|
+
if (runTimeoutMs !== void 0) config.runTimeoutMs = runTimeoutMs;
|
|
950
|
+
const taskTimeoutMs = ms("workflowTaskTimeout");
|
|
951
|
+
if (taskTimeoutMs !== void 0) config.workflowTaskTimeoutMs = taskTimeoutMs;
|
|
952
|
+
const startDelayMs = ms("startDelay");
|
|
953
|
+
if (startDelayMs !== void 0) config.startDelayMs = startDelayMs;
|
|
954
|
+
if (typeof options.summary === "string") config.summary = options.summary;
|
|
955
|
+
if (typeof options.details === "string") config.details = options.details;
|
|
956
|
+
if (options.context !== void 0) config.memo = options.context;
|
|
957
|
+
if (options.versioningBehavior !== void 0)
|
|
958
|
+
config.versioningBehavior = options.versioningBehavior;
|
|
959
|
+
if (options.retry !== void 0) config.retry = options.retry;
|
|
566
960
|
const childRunId = this.createRun(
|
|
567
961
|
workflowFn,
|
|
568
962
|
command.input,
|
|
@@ -571,7 +965,8 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
571
965
|
{
|
|
572
966
|
parentRunId: run.runId,
|
|
573
967
|
parentClosePolicy: command.options.parentClosePolicy
|
|
574
|
-
}
|
|
968
|
+
},
|
|
969
|
+
config
|
|
575
970
|
);
|
|
576
971
|
run.children.set(command.childId, childRunId);
|
|
577
972
|
this.append(run, {
|
|
@@ -628,6 +1023,7 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
628
1023
|
const mock = this.mocks.get(`${attempt.nodeType}#${attempt.operation}`) ?? this.mocks.get(attempt.nodeType);
|
|
629
1024
|
let failure;
|
|
630
1025
|
let result = null;
|
|
1026
|
+
let heartbeatDetails = attempt.heartbeatDetails;
|
|
631
1027
|
try {
|
|
632
1028
|
if (!mock)
|
|
633
1029
|
throw new ApplicationError(
|
|
@@ -645,7 +1041,11 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
645
1041
|
attempt: attempt.attempt,
|
|
646
1042
|
nodeType: attempt.nodeType,
|
|
647
1043
|
operation: attempt.operation,
|
|
648
|
-
...attempt.connection !== void 0 ? { connection: attempt.connection } : {}
|
|
1044
|
+
...attempt.connection !== void 0 ? { connection: attempt.connection } : {},
|
|
1045
|
+
...attempt.heartbeatDetails !== void 0 ? { heartbeatDetails: attempt.heartbeatDetails } : {},
|
|
1046
|
+
heartbeat: (details) => {
|
|
1047
|
+
heartbeatDetails = toJsonValue(details);
|
|
1048
|
+
}
|
|
649
1049
|
})
|
|
650
1050
|
);
|
|
651
1051
|
} catch (error) {
|
|
@@ -686,7 +1086,8 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
686
1086
|
this.attempts.push({
|
|
687
1087
|
...attempt,
|
|
688
1088
|
attempt: attempt.attempt + 1,
|
|
689
|
-
dueAt: this.clock + decision2.delayMs
|
|
1089
|
+
dueAt: this.clock + decision2.delayMs,
|
|
1090
|
+
...heartbeatDetails !== void 0 ? { heartbeatDetails } : {}
|
|
690
1091
|
});
|
|
691
1092
|
return;
|
|
692
1093
|
}
|
|
@@ -695,7 +1096,8 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
695
1096
|
this.attempts.push({
|
|
696
1097
|
...attempt,
|
|
697
1098
|
attempt: attempt.attempt + 1,
|
|
698
|
-
dueAt: this.clock + decision.delayMs
|
|
1099
|
+
dueAt: this.clock + decision.delayMs,
|
|
1100
|
+
...heartbeatDetails !== void 0 ? { heartbeatDetails } : {}
|
|
699
1101
|
});
|
|
700
1102
|
return;
|
|
701
1103
|
}
|
|
@@ -709,9 +1111,31 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
709
1111
|
}
|
|
710
1112
|
});
|
|
711
1113
|
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Run-level retry (11 §2.17): a failed or run-timed-out run whose policy
|
|
1116
|
+
* allows another attempt is followed by a new run of the chain after the
|
|
1117
|
+
* backoff, with `runAttempt + 1` and the failure as `lastFailure`.
|
|
1118
|
+
*/
|
|
1119
|
+
retryRun(run) {
|
|
1120
|
+
if (run.continuedTo !== void 0) return;
|
|
1121
|
+
const last = run.history[run.history.length - 1];
|
|
1122
|
+
if (!last || last.type !== "WORKFLOW_FAILED" && last.type !== "WORKFLOW_TIMED_OUT") return;
|
|
1123
|
+
const failure = last.payload["failure"] ?? { errorType: "TIMEOUT", message: "timed out", timeoutType: "WORKFLOW_RUN" };
|
|
1124
|
+
const decision = computeRunRetryDelay(run.config.retry, run.config.runAttempt, failure);
|
|
1125
|
+
if (!decision.retry) return;
|
|
1126
|
+
const config = {
|
|
1127
|
+
...this.chainConfig(run.config),
|
|
1128
|
+
runAttempt: run.config.runAttempt + 1,
|
|
1129
|
+
lastFailure: failure
|
|
1130
|
+
};
|
|
1131
|
+
if (decision.delayMs > 0) config.firstTaskDelayMs = decision.delayMs;
|
|
1132
|
+
this.startSuccessor(run, run.state.input ?? null, config);
|
|
1133
|
+
}
|
|
712
1134
|
onClosed(run) {
|
|
713
1135
|
this.timers = this.timers.filter((timer) => timer.runId !== run.runId);
|
|
714
1136
|
this.attempts = this.attempts.filter((entry) => entry.runId !== run.runId);
|
|
1137
|
+
this.dues = this.dues.filter((entry) => entry.runId !== run.runId);
|
|
1138
|
+
this.retryRun(run);
|
|
715
1139
|
for (const [childId2, childRunId] of run.children) {
|
|
716
1140
|
const child = run.state.children[childId2];
|
|
717
1141
|
if (!child || !isChildOpen(child) || childRunId === void 0) continue;
|
|
@@ -766,7 +1190,10 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
766
1190
|
default:
|
|
767
1191
|
this.append(parent, {
|
|
768
1192
|
type: "CHILD_RUN_TIMED_OUT",
|
|
769
|
-
payload: {
|
|
1193
|
+
payload: {
|
|
1194
|
+
...base,
|
|
1195
|
+
timeoutType: last.payload["timeoutType"] === "WORKFLOW_EXECUTION" ? "WORKFLOW_EXECUTION" : "WORKFLOW_RUN"
|
|
1196
|
+
}
|
|
770
1197
|
});
|
|
771
1198
|
}
|
|
772
1199
|
}
|
|
@@ -775,10 +1202,21 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
775
1202
|
let run = this.run(runId);
|
|
776
1203
|
while (run.continuedTo !== void 0) run = this.run(run.continuedTo);
|
|
777
1204
|
if (run.taskFailure) throw new WorkflowTaskFailedError(run.runId, run.taskFailure);
|
|
1205
|
+
if (this.failOnUnfinishedHandlers && run.unfinishedHandlers !== void 0)
|
|
1206
|
+
throw new UnfinishedHandlersError(run.runId, run.unfinishedHandlers);
|
|
778
1207
|
const last = run.history[run.history.length - 1];
|
|
779
1208
|
switch (run.state.status) {
|
|
780
1209
|
case "COMPLETED":
|
|
781
1210
|
return run.state.output ?? null;
|
|
1211
|
+
case "TIMED_OUT":
|
|
1212
|
+
throw new WorkflowFailedError(
|
|
1213
|
+
run.runId,
|
|
1214
|
+
last?.payload["failure"] ?? {
|
|
1215
|
+
errorType: "TIMEOUT",
|
|
1216
|
+
message: "timed out",
|
|
1217
|
+
type: "TimeoutFailure"
|
|
1218
|
+
}
|
|
1219
|
+
);
|
|
782
1220
|
case "FAILED":
|
|
783
1221
|
throw new WorkflowFailedError(
|
|
784
1222
|
run.runId,
|
|
@@ -1034,4 +1472,4 @@ async function runWorkflowTaskInVm(script, request, options = {}) {
|
|
|
1034
1472
|
}
|
|
1035
1473
|
}
|
|
1036
1474
|
|
|
1037
|
-
export { HistoryAssertionError, ReplayError, RunBlockedError, TestRunHandle, TestWorkflowEnvironment, WorkflowFailedError, WorkflowTaskFailedError, assertHistory, bundleToScript, drainJobs, expectIR, jsonCopy, registryOf, replay, replayRequest, runWorkflowTaskInProcess, runWorkflowTaskInVm, simulate };
|
|
1475
|
+
export { HistoryAssertionError, ReplayError, RunBlockedError, TestRunHandle, TestWorkflowEnvironment, UnfinishedHandlersError, WorkflowAlreadyStartedError, WorkflowFailedError, WorkflowTaskFailedError, assertHistory, bundleToScript, drainJobs, expectIR, jsonCopy, registryOf, replay, replayRequest, runWorkflowTaskInProcess, runWorkflowTaskInVm, simulate };
|