@fluidgrids/workflows 2026.1028.2 → 2026.1030.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 +181 -11
- package/dist/{activation-BBh1p82z.d.cts → activation-DqUfpHzk.d.cts} +1 -1
- package/dist/{activation-DnhG7SmF.d.ts → activation-xpx1oWuo.d.ts} +1 -1
- package/dist/{chunk-VRHHBPT6.js → chunk-2D3MYIIF.js} +1212 -128
- package/dist/chunk-3U2NU7YC.cjs +124 -0
- package/dist/{chunk-NEIUQASE.cjs → chunk-4QKB4ARC.cjs} +3 -3
- package/dist/{chunk-GBQ33SIZ.cjs → chunk-54FLQNUM.cjs} +12 -1
- package/dist/chunk-5HIVIV53.js +330 -0
- package/dist/chunk-5MFVJM76.js +1222 -0
- package/dist/{chunk-JD2VQIDZ.cjs → chunk-672KMMBK.cjs} +2 -2
- package/dist/{chunk-C5OLWZ42.js → chunk-6A2FGSUS.js} +22 -14
- package/dist/{chunk-XKJA7KX3.js → chunk-6OLN6BMB.js} +52 -3
- package/dist/{chunk-6FSHT5IL.js → chunk-7EZIANC7.js} +1 -1
- package/dist/{chunk-2J6TF7YI.cjs → chunk-A5SXCA62.cjs} +10 -3
- package/dist/{chunk-BO6OVILK.js → chunk-AC6ZEXVR.js} +10 -4
- package/dist/{chunk-ABFZWYHT.js → chunk-AE5Q6ZAJ.js} +315 -50
- package/dist/chunk-C4F5KJTI.js +293 -0
- package/dist/{chunk-IISBRUWJ.js → chunk-G5H32GIG.js} +1 -1
- package/dist/chunk-GEQDGUGB.cjs +2719 -0
- package/dist/{chunk-XHXIDVEC.js → chunk-HCM7ZC7Q.js} +1 -1
- package/dist/chunk-HMZUGSMU.cjs +1554 -0
- package/dist/{chunk-SAOBPYYJ.cjs → chunk-IQPXHQQQ.cjs} +8 -4
- package/dist/{chunk-P6TA5EZT.cjs → chunk-ISOIA7NV.cjs} +335 -58
- package/dist/chunk-JSC75O5C.cjs +167 -0
- package/dist/chunk-M272AHEX.js +25 -0
- package/dist/{chunk-BD7ZPG53.js → chunk-NWQ5WRLE.js} +10 -2
- package/dist/chunk-ODZ2IAR2.cjs +1247 -0
- package/dist/chunk-Q4MWQNJK.cjs +28 -0
- package/dist/{chunk-HWJZVOG7.js → chunk-SR7TR5KX.js} +6 -2
- package/dist/{chunk-FEW3JBYA.cjs → chunk-TCTE7DX5.cjs} +3 -3
- package/dist/{chunk-623FVQSN.cjs → chunk-TYF56RGB.cjs} +123 -0
- package/dist/chunk-VA4OFR72.cjs +324 -0
- package/dist/chunk-VU5S5A7V.js +150 -0
- package/dist/chunk-YEHR4NNE.js +1522 -0
- package/dist/{chunk-FMSW2HDF.cjs → chunk-ZCGKGDCL.cjs} +23 -15
- package/dist/codegen/index.d.cts +2 -2
- package/dist/codegen/index.d.ts +2 -2
- package/dist/collect-6NIEufHd.d.ts +103 -0
- package/dist/collect-CcTCKqx6.d.cts +103 -0
- package/dist/{file-BOoHZgoQ.d.cts → file-BxYpwjRQ.d.cts} +1007 -21
- package/dist/{file-yuPCFU5b.d.ts → file-CFdPQJI9.d.ts} +1007 -21
- package/dist/history/index.cjs +375 -86
- package/dist/history/index.d.cts +590 -12
- package/dist/history/index.d.ts +590 -12
- package/dist/history/index.js +50 -4
- package/dist/index.cjs +156 -50
- package/dist/index.d.cts +38 -58
- package/dist/index.d.ts +38 -58
- package/dist/index.js +6 -4
- package/dist/interceptors-CNcr2dlk.d.ts +159 -0
- package/dist/interceptors-CqkNZVfI.d.cts +159 -0
- package/dist/ir/index.cjs +362 -38
- package/dist/ir/index.d.cts +282 -7
- package/dist/ir/index.d.ts +282 -7
- package/dist/ir/index.js +329 -23
- package/dist/{lower-BzmUz0sq.d.cts → lower-BtRfIW2A.d.cts} +56 -8
- package/dist/{lower-_-iT4HYs.d.ts → lower-CpsMFOFX.d.ts} +56 -8
- package/dist/{manifest-C8N6HDYl.d.ts → manifest-D-I_twys.d.ts} +1 -1
- package/dist/{manifest-0OLw0i7k.d.cts → manifest-DahBQVM8.d.cts} +1 -1
- package/dist/node-evaluator-DGQDPSPI.cjs +24 -0
- package/dist/node-evaluator-LEEVD7H7.js +14 -0
- package/dist/payload-fields-BVRQn2yH.d.cts +122 -0
- package/dist/payload-fields-oOBWxDiG.d.ts +122 -0
- package/dist/quickjs/index.cjs +28 -27
- package/dist/quickjs/index.d.cts +6 -6
- package/dist/quickjs/index.d.ts +6 -6
- package/dist/quickjs/index.js +10 -9
- package/dist/roundtrip/index.cjs +259 -51
- package/dist/roundtrip/index.d.cts +100 -9
- package/dist/roundtrip/index.d.ts +100 -9
- package/dist/roundtrip/index.js +177 -11
- package/dist/runtime/index.cjs +21 -20
- package/dist/runtime/index.d.cts +184 -15
- package/dist/runtime/index.d.ts +184 -15
- package/dist/runtime/index.js +8 -7
- package/dist/testing/index.cjs +1122 -115
- package/dist/testing/index.d.cts +294 -13
- package/dist/testing/index.d.ts +294 -13
- package/dist/testing/index.js +1086 -84
- package/dist/{types-DV-HX_Gt.d.ts → types-Bv2i4cBX.d.cts} +244 -33
- package/dist/{types-CpBBjaGX.d.cts → types-DGx-b9eq.d.ts} +244 -33
- package/dist/{types-BGoPzgcl.d.ts → types-V7jEEUaL.d.cts} +20 -1
- package/dist/{types-BGoPzgcl.d.cts → types-V7jEEUaL.d.ts} +20 -1
- package/dist/{workflow-Bpr3vnA_.d.ts → workflow-B18OeF7g.d.ts} +2 -2
- package/dist/workflow-BGnv8SS6.d.ts +803 -0
- package/dist/{workflow-FXUkpIBc.d.cts → workflow-BJPFv3cp.d.cts} +2 -2
- package/dist/workflow-DS32Gtws.d.cts +803 -0
- package/fixtures/histories/activity-options-updated.history.json +159 -0
- package/fixtures/histories/activity-paused-unpaused.history.json +160 -0
- 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/codec-opaque.history.json +229 -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/dynamic-workflow.history.json +150 -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/interceptor-headers.history.json +339 -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/query-custom.history.json +213 -0
- package/fixtures/histories/query-default-handler.history.json +120 -0
- package/fixtures/histories/reset-forked-reapplied-signals.history.json +239 -0
- package/fixtures/histories/reset-reapplied-update-admitted.history.json +228 -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/search-attributes-upserted.history.json +177 -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/fixtures/histories/update-accepted-completed.history.json +45 -30
- package/fixtures/histories/update-default-handler.history.json +246 -0
- package/fixtures/histories/update-handler-awaits-activity.history.json +196 -0
- package/fixtures/histories/update-not-handled.history.json +138 -0
- package/fixtures/histories/update-rejected-no-event.history.json +190 -0
- package/fixtures/histories/update-with-start-admitted.history.json +150 -0
- package/fixtures/histories/user-activity-cancel.history.json +149 -0
- package/fixtures/histories/user-activity-retry.history.json +143 -0
- package/fixtures/histories/user-activity.history.json +149 -0
- package/package.json +1 -1
- package/schema/bridge-v2.schema.json +1062 -0
- package/schema/history-file.schema.json +538 -83
- package/schema/ir-v2.schema.json +290 -0
- package/schema/payload-fields.json +162 -0
- package/schema/run-event.schema.json +503 -82
- package/schema/workflow-command.schema.json +372 -69
- package/schema/workflow-task.schema.json +924 -171
- package/dist/assemble-BLqQ8lSu.d.cts +0 -39
- package/dist/assemble-DWgaXHd-.d.ts +0 -39
- package/dist/chunk-DHTLALBL.cjs +0 -80
- package/dist/chunk-DJYMDLV5.js +0 -227
- package/dist/chunk-I5LR3D2K.js +0 -869
- package/dist/chunk-M4BBAERY.cjs +0 -1635
- package/dist/chunk-N23SKIG4.js +0 -73
- package/dist/chunk-PZCVF76Q.cjs +0 -868
- package/dist/chunk-QQ6ULXHI.cjs +0 -885
- package/dist/chunk-UK3WM7YZ.cjs +0 -75
- package/dist/chunk-VOKMAAJP.js +0 -848
- package/dist/node-evaluator-4MSDKWOG.cjs +0 -22
- package/dist/node-evaluator-SX4LEYQC.js +0 -12
- package/dist/workflow-C2a05WUJ.d.ts +0 -444
- package/dist/workflow-J-yYixny.d.cts +0 -444
package/dist/testing/index.cjs
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
3
|
+
var chunk672KMMBK_cjs = require('../chunk-672KMMBK.cjs');
|
|
4
|
+
var chunkIQPXHQQQ_cjs = require('../chunk-IQPXHQQQ.cjs');
|
|
5
|
+
var chunk54FLQNUM_cjs = require('../chunk-54FLQNUM.cjs');
|
|
6
6
|
require('../chunk-G4UEPUGO.cjs');
|
|
7
7
|
require('../chunk-GVTZMA5C.cjs');
|
|
8
8
|
require('../chunk-QKZL3ONM.cjs');
|
|
9
9
|
require('../chunk-TBDXTV22.cjs');
|
|
10
|
-
var
|
|
10
|
+
var chunkHMZUGSMU_cjs = require('../chunk-HMZUGSMU.cjs');
|
|
11
|
+
require('../chunk-JSC75O5C.cjs');
|
|
11
12
|
var chunkSAENRNBJ_cjs = require('../chunk-SAENRNBJ.cjs');
|
|
12
|
-
var
|
|
13
|
-
var
|
|
14
|
-
require('../chunk-
|
|
13
|
+
var chunkGEQDGUGB_cjs = require('../chunk-GEQDGUGB.cjs');
|
|
14
|
+
var chunkZCGKGDCL_cjs = require('../chunk-ZCGKGDCL.cjs');
|
|
15
|
+
require('../chunk-Q4MWQNJK.cjs');
|
|
15
16
|
var chunkL24YIZY7_cjs = require('../chunk-L24YIZY7.cjs');
|
|
16
|
-
var
|
|
17
|
-
|
|
18
|
-
var
|
|
19
|
-
require('../chunk-
|
|
17
|
+
var chunkVA4OFR72_cjs = require('../chunk-VA4OFR72.cjs');
|
|
18
|
+
require('../chunk-FR7KYWRO.cjs');
|
|
19
|
+
var chunkISOIA7NV_cjs = require('../chunk-ISOIA7NV.cjs');
|
|
20
|
+
require('../chunk-A5SXCA62.cjs');
|
|
21
|
+
var chunkTYF56RGB_cjs = require('../chunk-TYF56RGB.cjs');
|
|
20
22
|
require('../chunk-Z2Y65YOY.cjs');
|
|
21
23
|
var vm = require('vm');
|
|
22
24
|
|
|
@@ -24,8 +26,10 @@ var vm = require('vm');
|
|
|
24
26
|
function drainJobs() {
|
|
25
27
|
return new Promise((resolve) => setImmediate(resolve));
|
|
26
28
|
}
|
|
27
|
-
function registryOf(workflows) {
|
|
28
|
-
return
|
|
29
|
+
function registryOf(workflows, interceptors) {
|
|
30
|
+
return chunkGEQDGUGB_cjs.buildWorkflowRegistry([
|
|
31
|
+
["in-process", { ...workflows, ...interceptors !== void 0 ? { interceptors } : {} }]
|
|
32
|
+
]);
|
|
29
33
|
}
|
|
30
34
|
async function hostLoop(abi, requestJson) {
|
|
31
35
|
abi.start(requestJson);
|
|
@@ -35,19 +39,71 @@ async function hostLoop(abi, requestJson) {
|
|
|
35
39
|
return abi.complete();
|
|
36
40
|
}
|
|
37
41
|
async function runWorkflowTaskInProcess(registry, request) {
|
|
38
|
-
const abi =
|
|
42
|
+
const abi = chunkGEQDGUGB_cjs.createWorkflowRuntime(registry);
|
|
39
43
|
const json = await hostLoop(abi, JSON.stringify(request));
|
|
40
44
|
return JSON.parse(json);
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
// src/testing/environment.ts
|
|
48
|
+
var QueryRejectedError = class extends Error {
|
|
49
|
+
constructor(runId, queryName, message) {
|
|
50
|
+
super(message);
|
|
51
|
+
this.runId = runId;
|
|
52
|
+
this.queryName = queryName;
|
|
53
|
+
this.name = "QueryRejectedError";
|
|
54
|
+
}
|
|
55
|
+
runId;
|
|
56
|
+
queryName;
|
|
57
|
+
};
|
|
58
|
+
var QueryFailedError = class extends Error {
|
|
59
|
+
constructor(runId, queryName, message) {
|
|
60
|
+
super(message);
|
|
61
|
+
this.runId = runId;
|
|
62
|
+
this.queryName = queryName;
|
|
63
|
+
this.name = "QueryFailedError";
|
|
64
|
+
}
|
|
65
|
+
runId;
|
|
66
|
+
queryName;
|
|
67
|
+
};
|
|
68
|
+
var UpdateRefusedError = class extends Error {
|
|
69
|
+
constructor(message) {
|
|
70
|
+
super(message);
|
|
71
|
+
this.name = "UpdateRefusedError";
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var WorkflowAlreadyStartedError = class extends Error {
|
|
75
|
+
constructor(workflowKey, workflowId, existingRunId) {
|
|
76
|
+
super(
|
|
77
|
+
`Workflow ${workflowKey} already has a run with workflow id "${workflowId}" (${existingRunId})`
|
|
78
|
+
);
|
|
79
|
+
this.workflowKey = workflowKey;
|
|
80
|
+
this.workflowId = workflowId;
|
|
81
|
+
this.existingRunId = existingRunId;
|
|
82
|
+
this.name = "WorkflowAlreadyStartedError";
|
|
83
|
+
}
|
|
84
|
+
workflowKey;
|
|
85
|
+
workflowId;
|
|
86
|
+
existingRunId;
|
|
87
|
+
};
|
|
88
|
+
var UnfinishedHandlersError = class extends Error {
|
|
89
|
+
constructor(runId, handlers) {
|
|
90
|
+
super(
|
|
91
|
+
`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')`
|
|
92
|
+
);
|
|
93
|
+
this.runId = runId;
|
|
94
|
+
this.handlers = handlers;
|
|
95
|
+
this.name = "UnfinishedHandlersError";
|
|
96
|
+
}
|
|
97
|
+
runId;
|
|
98
|
+
handlers;
|
|
99
|
+
};
|
|
44
100
|
var WorkflowFailedError = class extends Error {
|
|
45
101
|
constructor(runId, failure) {
|
|
46
102
|
super(`Run ${runId} failed: ${failure.message}`);
|
|
47
103
|
this.runId = runId;
|
|
48
104
|
this.failure = failure;
|
|
49
105
|
this.name = "WorkflowFailedError";
|
|
50
|
-
Object.defineProperty(this, "cause", { value:
|
|
106
|
+
Object.defineProperty(this, "cause", { value: chunkZCGKGDCL_cjs.failureToError(failure), enumerable: false });
|
|
51
107
|
}
|
|
52
108
|
runId;
|
|
53
109
|
failure;
|
|
@@ -72,7 +128,7 @@ var RunBlockedError = class extends Error {
|
|
|
72
128
|
}
|
|
73
129
|
runId;
|
|
74
130
|
};
|
|
75
|
-
var TestRunHandle = class {
|
|
131
|
+
var TestRunHandle = class _TestRunHandle {
|
|
76
132
|
constructor(env, runId) {
|
|
77
133
|
this.env = env;
|
|
78
134
|
this.runId = runId;
|
|
@@ -89,7 +145,7 @@ var TestRunHandle = class {
|
|
|
89
145
|
}
|
|
90
146
|
/** Deliver a signal and run until idle (without skipping time). */
|
|
91
147
|
async signal(signal, payload) {
|
|
92
|
-
this.env.deliverSignal(this.runId,
|
|
148
|
+
this.env.deliverSignal(this.runId, chunkZCGKGDCL_cjs.signalName(signal), payload);
|
|
93
149
|
await this.env.runUntilIdle(false);
|
|
94
150
|
}
|
|
95
151
|
/** Request cancellation and run until idle (without skipping time). */
|
|
@@ -102,6 +158,22 @@ var TestRunHandle = class {
|
|
|
102
158
|
this.env.terminate(this.runId, reason);
|
|
103
159
|
await this.env.runUntilIdle(false);
|
|
104
160
|
}
|
|
161
|
+
/** Send an update and run until idle (Phase C). */
|
|
162
|
+
update(update, args, options = {}) {
|
|
163
|
+
return this.env.update(this.runId, update, args, options);
|
|
164
|
+
}
|
|
165
|
+
/** Answer a query from the run's history (Phase C custom queries and the built-ins). */
|
|
166
|
+
query(query, args) {
|
|
167
|
+
return this.env.query(this.runId, query, args);
|
|
168
|
+
}
|
|
169
|
+
/** Reset the run (11 §2.7): a new run forked at a completed task; this one is terminated. */
|
|
170
|
+
async reset(options = {}) {
|
|
171
|
+
return new _TestRunHandle(this.env, await this.env.reset(this.runId, options));
|
|
172
|
+
}
|
|
173
|
+
/** Sink calls the run's tasks made (Phase C). */
|
|
174
|
+
sinkCalls() {
|
|
175
|
+
return this.env.sinkCallsOf(this.runId);
|
|
176
|
+
}
|
|
105
177
|
/** Run until this run (or the last run of its continue-as-new chain) ends; its output. */
|
|
106
178
|
async result() {
|
|
107
179
|
return this.env.resultOf(this.runId);
|
|
@@ -116,6 +188,23 @@ function toDate(value) {
|
|
|
116
188
|
if (value instanceof Date) return value.getTime();
|
|
117
189
|
return typeof value === "number" ? value : Date.parse(value);
|
|
118
190
|
}
|
|
191
|
+
function optionalDuration(value, what) {
|
|
192
|
+
return value === void 0 ? void 0 : chunkVA4OFR72_cjs.toMs(value, what);
|
|
193
|
+
}
|
|
194
|
+
var FAILED_STATUSES = /* @__PURE__ */ new Set([
|
|
195
|
+
"FAILED",
|
|
196
|
+
"CANCELLED",
|
|
197
|
+
"TERMINATED",
|
|
198
|
+
"TIMED_OUT"
|
|
199
|
+
]);
|
|
200
|
+
function admittedPayload(update) {
|
|
201
|
+
return {
|
|
202
|
+
updateId: update.updateId,
|
|
203
|
+
name: update.name,
|
|
204
|
+
args: update.args,
|
|
205
|
+
...update.headers !== void 0 ? { headers: { ...update.headers } } : {}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
119
208
|
function toJsonValue(value) {
|
|
120
209
|
if (value === void 0) return null;
|
|
121
210
|
const text = JSON.stringify(value);
|
|
@@ -125,6 +214,12 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
125
214
|
clock;
|
|
126
215
|
timeSkipping;
|
|
127
216
|
enabled;
|
|
217
|
+
/** The bundle features the gate's flags enable, sent with every task (`request.features`). */
|
|
218
|
+
codeFeatures;
|
|
219
|
+
failOnUnfinishedHandlers;
|
|
220
|
+
historyRelease;
|
|
221
|
+
dues = [];
|
|
222
|
+
held = /* @__PURE__ */ new Set();
|
|
128
223
|
workspaceId;
|
|
129
224
|
runs = /* @__PURE__ */ new Map();
|
|
130
225
|
runOrder = [];
|
|
@@ -136,17 +231,65 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
136
231
|
timers = [];
|
|
137
232
|
mockedChildren = [];
|
|
138
233
|
runCounter = 0;
|
|
234
|
+
updateCounter = 0;
|
|
139
235
|
executor;
|
|
140
236
|
workflowKeys;
|
|
237
|
+
interceptors;
|
|
238
|
+
activityImpls;
|
|
239
|
+
activityInterceptors;
|
|
240
|
+
connections;
|
|
241
|
+
sinks;
|
|
242
|
+
/** Attempts that handed their completion to an outside system (`completeAsync`), by token. */
|
|
243
|
+
asyncAttempts = /* @__PURE__ */ new Map();
|
|
141
244
|
constructor(options) {
|
|
142
245
|
this.clock = toDate(options.startTime);
|
|
143
246
|
this.timeSkipping = options.timeSkipping !== false;
|
|
144
|
-
this.enabled = options.enabled ??
|
|
247
|
+
this.enabled = options.enabled ?? new Set(chunkISOIA7NV_cjs.COMMAND_FEATURES);
|
|
248
|
+
this.codeFeatures = chunkISOIA7NV_cjs.codeFeaturesEnabledBy(this.enabled);
|
|
249
|
+
this.failOnUnfinishedHandlers = options.failOnUnfinishedHandlers !== false;
|
|
250
|
+
this.historyRelease = options.historyRelease ?? chunkTYF56RGB_cjs.RUNTIME_RELEASE;
|
|
145
251
|
this.workspaceId = options.workspaceId ?? "test-workspace";
|
|
146
252
|
this.workflows = { ...options.workflows ?? {} };
|
|
147
|
-
this.
|
|
253
|
+
this.interceptors = options.interceptors;
|
|
254
|
+
this.registry = registryOf(this.workflows, this.interceptors);
|
|
148
255
|
this.executor = options.taskExecutor;
|
|
149
256
|
this.workflowKeys = { ...options.workflowKeys ?? {} };
|
|
257
|
+
this.activityImpls = [...options.activities ?? []];
|
|
258
|
+
for (const impl of this.activityImpls)
|
|
259
|
+
if (!chunkVA4OFR72_cjs.isActivityImplementation(impl))
|
|
260
|
+
throw new TypeError("activities must be implementActivity() or defineActivity() values");
|
|
261
|
+
this.activityInterceptors = [...options.activityInterceptors ?? []];
|
|
262
|
+
this.connections = { ...options.connections ?? {} };
|
|
263
|
+
this.sinks = { ...options.sinks ?? {} };
|
|
264
|
+
}
|
|
265
|
+
/** Register a user-activity implementation (Phase C). A later one replaces an earlier of the same name. */
|
|
266
|
+
registerActivity(implementation) {
|
|
267
|
+
if (!chunkVA4OFR72_cjs.isActivityImplementation(implementation))
|
|
268
|
+
throw new TypeError(
|
|
269
|
+
"registerActivity needs an implementActivity() or defineActivity() value"
|
|
270
|
+
);
|
|
271
|
+
const index = this.activityImpls.findIndex(
|
|
272
|
+
(impl) => impl.name === implementation.name && impl.dynamic === implementation.dynamic
|
|
273
|
+
);
|
|
274
|
+
if (index >= 0) this.activityImpls.splice(index, 1);
|
|
275
|
+
this.activityImpls.push(implementation);
|
|
276
|
+
}
|
|
277
|
+
/** Mock a user activity by name or declaration (Phase C). */
|
|
278
|
+
mockActivity(activity, fn) {
|
|
279
|
+
const name = typeof activity === "string" ? activity : activity.name;
|
|
280
|
+
if (typeof activity !== "string" && !chunkVA4OFR72_cjs.isActivityDefinition(activity))
|
|
281
|
+
throw new TypeError("mockActivity needs an activity name or a declareActivity() value");
|
|
282
|
+
this.registerActivity(
|
|
283
|
+
Object.freeze({
|
|
284
|
+
__fluidgrids: chunkVA4OFR72_cjs.ACTIVITY_MARKER,
|
|
285
|
+
name,
|
|
286
|
+
options: typeof activity === "string" ? {} : activity.options,
|
|
287
|
+
implementation: chunkVA4OFR72_cjs.ACTIVITY_IMPL_MARKER,
|
|
288
|
+
// The mock receives the activity's JSON args, typed by the declaration.
|
|
289
|
+
fn: (ctx, args) => fn(args, ctx),
|
|
290
|
+
dynamic: false
|
|
291
|
+
})
|
|
292
|
+
);
|
|
150
293
|
}
|
|
151
294
|
/** Create an environment. */
|
|
152
295
|
static async create(options = {}) {
|
|
@@ -165,6 +308,26 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
165
308
|
impl
|
|
166
309
|
);
|
|
167
310
|
}
|
|
311
|
+
/**
|
|
312
|
+
* Hold the activity queue of a node type (every node type when omitted):
|
|
313
|
+
* attempts wait instead of starting, and a `scheduleToStartTimeout` expires
|
|
314
|
+
* (`ACTIVITY_TIMED_OUT { timeoutType: SCHEDULE_TO_START }`, not retried).
|
|
315
|
+
*/
|
|
316
|
+
holdActivities(nodeType = "*") {
|
|
317
|
+
this.held.add(nodeType);
|
|
318
|
+
}
|
|
319
|
+
/** Release held activity queues (all when no node type is given). */
|
|
320
|
+
releaseActivities(nodeType) {
|
|
321
|
+
if (nodeType === void 0) this.held.clear();
|
|
322
|
+
else this.held.delete(nodeType);
|
|
323
|
+
}
|
|
324
|
+
/** The hold key of an attempt: its node type, or the task queue of a user activity. */
|
|
325
|
+
holdKeyOf(entry) {
|
|
326
|
+
return entry.kind === "user" ? entry.taskQueue ?? "" : entry.nodeType;
|
|
327
|
+
}
|
|
328
|
+
isHeld(nodeType) {
|
|
329
|
+
return this.held.has("*") || this.held.has(nodeType);
|
|
330
|
+
}
|
|
168
331
|
/** Mock a child workflow started by key (graph or durable). */
|
|
169
332
|
mockChild(workflowKey, impl) {
|
|
170
333
|
this.childMocks.set(workflowKey, impl);
|
|
@@ -175,20 +338,330 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
175
338
|
// A declared workflow is stored by its declaration; its input type is the caller's.
|
|
176
339
|
this.ensureRegistered(definition)
|
|
177
340
|
);
|
|
341
|
+
const declared = this.workflows[workflowFn]?.options ?? {};
|
|
342
|
+
const workflowKey = options.workflowKey ?? (typeof definition === "string" ? this.workflows[workflowFn]?.name ?? workflowFn : definition.name);
|
|
343
|
+
if (options.update !== void 0 && options.startDelay !== void 0)
|
|
344
|
+
throw new UpdateRefusedError(
|
|
345
|
+
"Update-with-start cannot be combined with a start delay (11 \xA72.6)"
|
|
346
|
+
);
|
|
347
|
+
if (options.workflowId !== void 0) {
|
|
348
|
+
const existing = this.useExisting(workflowKey, options);
|
|
349
|
+
if (existing !== void 0) {
|
|
350
|
+
for (const signal of options.signals ?? [])
|
|
351
|
+
this.deliverSignal(existing, signal.name, signal.payload);
|
|
352
|
+
if (options.update !== void 0)
|
|
353
|
+
this.addUpdate(this.run(existing), options.update, "PENDING");
|
|
354
|
+
await this.runUntilIdle(false);
|
|
355
|
+
return new TestRunHandle(this, existing);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const executionTimeoutMs = optionalDuration(
|
|
359
|
+
options.executionTimeout ?? declared.executionTimeout,
|
|
360
|
+
"executionTimeout"
|
|
361
|
+
);
|
|
362
|
+
const retry = options.retry ?? declared.retry;
|
|
363
|
+
const config = {
|
|
364
|
+
workflowKey,
|
|
365
|
+
runAttempt: 1,
|
|
366
|
+
...options.workflowId !== void 0 ? { businessId: options.workflowId } : {},
|
|
367
|
+
...executionTimeoutMs !== void 0 ? { executionTimeoutMs, executionDeadline: this.clock + executionTimeoutMs } : {},
|
|
368
|
+
...this.durationConfig(options.runTimeout ?? declared.runTimeout, "runTimeoutMs"),
|
|
369
|
+
...this.durationConfig(
|
|
370
|
+
options.workflowTaskTimeout ?? declared.workflowTaskTimeout,
|
|
371
|
+
"workflowTaskTimeoutMs"
|
|
372
|
+
),
|
|
373
|
+
...this.durationConfig(options.startDelay, "startDelayMs"),
|
|
374
|
+
...retry !== void 0 ? { retry: chunkVA4OFR72_cjs.runRetryPolicyOf(retry) } : {},
|
|
375
|
+
...options.summary !== void 0 ? { summary: options.summary } : {},
|
|
376
|
+
...options.details !== void 0 ? { details: options.details } : {},
|
|
377
|
+
...options.memo !== void 0 ? { memo: toJsonValue(options.memo) } : {},
|
|
378
|
+
...(options.versioningBehavior ?? declared.versioningBehavior) !== void 0 ? { versioningBehavior: options.versioningBehavior ?? declared.versioningBehavior } : {},
|
|
379
|
+
...options.headers !== void 0 ? { headers: { ...options.headers } } : {},
|
|
380
|
+
...options.searchAttributes !== void 0 ? { searchAttributes: toJsonValue(options.searchAttributes) } : {},
|
|
381
|
+
...options.fairnessKey !== void 0 ? { fairnessKey: options.fairnessKey } : {},
|
|
382
|
+
...options.fairnessWeight !== void 0 ? { fairnessWeight: options.fairnessWeight } : {}
|
|
383
|
+
};
|
|
178
384
|
const runId = this.createRun(
|
|
179
385
|
workflowFn,
|
|
180
386
|
toJsonValue(options.input),
|
|
181
387
|
{ ...options.info, ...options.runId !== void 0 ? { runId: options.runId } : {} },
|
|
182
|
-
void 0
|
|
388
|
+
void 0,
|
|
389
|
+
{},
|
|
390
|
+
config,
|
|
391
|
+
options.update !== void 0 ? [this.newUpdate(options.update, "ADMITTED")] : []
|
|
183
392
|
);
|
|
184
393
|
for (const signal of options.signals ?? [])
|
|
185
394
|
this.deliverSignal(runId, signal.name, signal.payload);
|
|
186
395
|
await this.runUntilIdle(false);
|
|
187
396
|
return new TestRunHandle(this, runId);
|
|
188
397
|
}
|
|
398
|
+
/**
|
|
399
|
+
* Update-with-start (11 §2.6): start the run (or, with
|
|
400
|
+
* `idConflictPolicy: USE_EXISTING`, reach the open one) and deliver the
|
|
401
|
+
* update before any other task runs.
|
|
402
|
+
*/
|
|
403
|
+
async startWithUpdate(definition, options) {
|
|
404
|
+
const updateId = options.update.updateId ?? this.nextUpdateId();
|
|
405
|
+
const handle = await this.start(definition, {
|
|
406
|
+
...options,
|
|
407
|
+
update: { ...options.update, updateId }
|
|
408
|
+
});
|
|
409
|
+
return { handle, update: this.updateResultOf(handle.runId, updateId) };
|
|
410
|
+
}
|
|
411
|
+
nextUpdateId() {
|
|
412
|
+
this.updateCounter += 1;
|
|
413
|
+
return `u${this.updateCounter}`;
|
|
414
|
+
}
|
|
415
|
+
/** A new update row (not attached to a run yet). */
|
|
416
|
+
newUpdate(update, status) {
|
|
417
|
+
return {
|
|
418
|
+
updateId: update.updateId ?? this.nextUpdateId(),
|
|
419
|
+
name: update.name,
|
|
420
|
+
args: toJsonValue(update.args),
|
|
421
|
+
...update.headers !== void 0 ? { headers: { ...update.headers } } : {},
|
|
422
|
+
status
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
/** Record an update on a run: pending (delivered by the next task) or admitted into history. */
|
|
426
|
+
addUpdate(run, update, status) {
|
|
427
|
+
const existing = update.updateId !== void 0 ? run.updates.get(update.updateId) : void 0;
|
|
428
|
+
if (existing) return existing;
|
|
429
|
+
const entry = this.newUpdate(update, status);
|
|
430
|
+
run.updates.set(entry.updateId, entry);
|
|
431
|
+
if (status === "ADMITTED")
|
|
432
|
+
this.append(run, { type: "UPDATE_ADMITTED", payload: admittedPayload(entry) });
|
|
433
|
+
else {
|
|
434
|
+
if (run.delayedUntil !== void 0) this.endDelay(run);
|
|
435
|
+
else this.scheduleTask(run);
|
|
436
|
+
}
|
|
437
|
+
return entry;
|
|
438
|
+
}
|
|
439
|
+
/** Schedule a workflow task for a run that has none (an update needs no input event). */
|
|
440
|
+
scheduleTask(run) {
|
|
441
|
+
if (run.state.status !== "RUNNING" || run.state.task.state !== "NONE") return;
|
|
442
|
+
if (run.delayedUntil !== void 0) return;
|
|
443
|
+
run.taskCounter += 1;
|
|
444
|
+
const taskId = `${run.runId}-task-${run.taskCounter}`;
|
|
445
|
+
const scheduled = {
|
|
446
|
+
seq: run.history.length + 1,
|
|
447
|
+
type: "WORKFLOW_TASK_SCHEDULED",
|
|
448
|
+
payload: { taskId },
|
|
449
|
+
createdAt: new Date(this.clock).toISOString()
|
|
450
|
+
};
|
|
451
|
+
chunkISOIA7NV_cjs.applyEvent(run.state, scheduled);
|
|
452
|
+
run.history.push(scheduled);
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Send an update to a run and run until idle (Phase C, 05 §6). The result
|
|
456
|
+
* is `REJECTED` (the validator refused it or no handler took it; nothing is
|
|
457
|
+
* written to history), `COMPLETED`, or `ACCEPTED` while its handler still
|
|
458
|
+
* waits (for a timer without time skipping, or with `waitFor: ACCEPTED`).
|
|
459
|
+
*/
|
|
460
|
+
async update(runId, update, args, options = {}) {
|
|
461
|
+
const run = this.run(runId);
|
|
462
|
+
const name = typeof update === "string" ? update : update.name;
|
|
463
|
+
if (options.updateId !== void 0 && run.updates.has(options.updateId))
|
|
464
|
+
return this.updateResultOf(runId, options.updateId);
|
|
465
|
+
if (run.state.status !== "RUNNING")
|
|
466
|
+
throw new UpdateRefusedError(`Run ${runId} is ${run.state.status}; it takes no updates`);
|
|
467
|
+
const entry = this.addUpdate(
|
|
468
|
+
run,
|
|
469
|
+
{
|
|
470
|
+
name,
|
|
471
|
+
args,
|
|
472
|
+
...options.updateId !== void 0 ? { updateId: options.updateId } : {},
|
|
473
|
+
...options.headers !== void 0 ? { headers: options.headers } : {}
|
|
474
|
+
},
|
|
475
|
+
"PENDING"
|
|
476
|
+
);
|
|
477
|
+
await this.runUntilIdle(false);
|
|
478
|
+
return this.updateResultOf(runId, entry.updateId);
|
|
479
|
+
}
|
|
480
|
+
/** The current result of an update. */
|
|
481
|
+
updateResultOf(runId, updateId) {
|
|
482
|
+
const entry = this.run(runId).updates.get(updateId);
|
|
483
|
+
if (!entry) throw new Error(`Run ${runId} has no update ${updateId}`);
|
|
484
|
+
return {
|
|
485
|
+
updateId,
|
|
486
|
+
status: entry.status === "ADMITTED" ? "PENDING" : entry.status,
|
|
487
|
+
...entry.result !== void 0 ? { result: entry.result } : {},
|
|
488
|
+
...entry.failure !== void 0 ? { failure: entry.failure } : {}
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
/** Answer a query from the run's history (a replay-only `query` task). */
|
|
492
|
+
async query(runId, query, args) {
|
|
493
|
+
const run = this.run(runId);
|
|
494
|
+
const name = typeof query === "string" ? query : query.name;
|
|
495
|
+
const request = {
|
|
496
|
+
protocol: chunkTYF56RGB_cjs.HISTORY_PROTOCOL,
|
|
497
|
+
kind: "query",
|
|
498
|
+
workflowFn: run.workflowFn,
|
|
499
|
+
info: run.info,
|
|
500
|
+
history: run.history,
|
|
501
|
+
nowMs: this.clock,
|
|
502
|
+
queries: [{ queryId: "q1", name, args: toJsonValue(args) }]
|
|
503
|
+
};
|
|
504
|
+
const result = await this.execute(request);
|
|
505
|
+
const answer = result.queryResults?.[0];
|
|
506
|
+
if (answer?.rejected === true)
|
|
507
|
+
throw new QueryRejectedError(runId, name, answer.error ?? `No handler for query ${name}`);
|
|
508
|
+
if (answer === void 0 || answer.error !== void 0)
|
|
509
|
+
throw new QueryFailedError(
|
|
510
|
+
runId,
|
|
511
|
+
name,
|
|
512
|
+
answer?.error ?? result.failure?.message ?? "The query was not answered"
|
|
513
|
+
);
|
|
514
|
+
return answer.value ?? null;
|
|
515
|
+
}
|
|
516
|
+
/** Sink calls the run's tasks made. */
|
|
517
|
+
sinkCallsOf(runId) {
|
|
518
|
+
return this.run(runId).sinkCalls.map((call) => ({ ...call, args: [...call.args] }));
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Reset a run (11 §2.7): a new run whose history is the original's up to
|
|
522
|
+
* and including the chosen completed workflow task (with its commands),
|
|
523
|
+
* then `WORKFLOW_RESET`, then the signals (and, as `UPDATE_ADMITTED`, the
|
|
524
|
+
* accepted updates) received after that point. The original is terminated
|
|
525
|
+
* with reason `RESET`; children it started before the point follow the new
|
|
526
|
+
* run, the others get their parent-close policy. Returns the new run id.
|
|
527
|
+
*/
|
|
528
|
+
async reset(runId, options = {}) {
|
|
529
|
+
const original = this.run(runId);
|
|
530
|
+
const history = original.history;
|
|
531
|
+
const points = [];
|
|
532
|
+
for (const event of history) {
|
|
533
|
+
if (event.type !== "WORKFLOW_TASK_COMPLETED") continue;
|
|
534
|
+
const taskId = typeof event.payload["taskId"] === "string" ? event.payload["taskId"] : "";
|
|
535
|
+
let end = event.seq;
|
|
536
|
+
let closes = false;
|
|
537
|
+
while (end < history.length) {
|
|
538
|
+
const next = history[end];
|
|
539
|
+
if (!next || !chunkTYF56RGB_cjs.isCommandEvent(next) || next.taskId !== taskId) break;
|
|
540
|
+
if (chunkTYF56RGB_cjs.TERMINAL_EVENTS.has(next.type)) closes = true;
|
|
541
|
+
end += 1;
|
|
542
|
+
}
|
|
543
|
+
if (!closes) points.push({ seq: event.seq, cut: end });
|
|
544
|
+
}
|
|
545
|
+
const point = options.toSeq !== void 0 ? [...points].reverse().find((entry) => entry.seq <= (options.toSeq ?? 0)) : options.to === "FIRST_TASK" ? points[0] : points[points.length - 1];
|
|
546
|
+
if (!point) throw new Error(`Run ${runId} has no completed workflow task to reset to`);
|
|
547
|
+
const cut = point.cut;
|
|
548
|
+
const copied = history.slice(0, cut).map((event) => ({ ...event, payload: { ...event.payload } }));
|
|
549
|
+
const later = history.slice(cut);
|
|
550
|
+
const reapplySignals = options.reapplySignals !== false;
|
|
551
|
+
const reapplyUpdates = options.reapplyUpdates !== false;
|
|
552
|
+
const keptChildren = new Set(
|
|
553
|
+
copied.filter((event) => event.type === "CHILD_RUN_INITIATED").map((event) => String(event.payload["childId"]))
|
|
554
|
+
);
|
|
555
|
+
this.runCounter += 1;
|
|
556
|
+
const newRunId = `run-${this.runCounter}`;
|
|
557
|
+
if (original.state.status === "RUNNING") {
|
|
558
|
+
original.resetTo = newRunId;
|
|
559
|
+
this.append(original, { type: "WORKFLOW_TERMINATED", payload: { reason: "RESET" } });
|
|
560
|
+
this.onClosed(original, keptChildren);
|
|
561
|
+
} else original.resetTo = newRunId;
|
|
562
|
+
const fork = {
|
|
563
|
+
runId: newRunId,
|
|
564
|
+
workflowFn: original.workflowFn,
|
|
565
|
+
history: copied,
|
|
566
|
+
state: chunkISOIA7NV_cjs.projectRunState(copied),
|
|
567
|
+
taskCounter: copied.filter((event) => event.type === "WORKFLOW_TASK_SCHEDULED").length,
|
|
568
|
+
info: { ...original.info, runId: newRunId },
|
|
569
|
+
children: new Map([...original.children].filter(([childId]) => keptChildren.has(childId))),
|
|
570
|
+
config: { ...original.config },
|
|
571
|
+
...original.parent ? { parent: original.parent } : {},
|
|
572
|
+
updates: /* @__PURE__ */ new Map(),
|
|
573
|
+
sinkCalls: []
|
|
574
|
+
};
|
|
575
|
+
this.runs.set(newRunId, fork);
|
|
576
|
+
this.runOrder.push(newRunId);
|
|
577
|
+
for (const [childId, childRunId] of fork.children) {
|
|
578
|
+
const child = childRunId !== void 0 ? this.runs.get(childRunId) : void 0;
|
|
579
|
+
if (child) child.parent = { runId: newRunId, childId };
|
|
580
|
+
}
|
|
581
|
+
this.append(fork, {
|
|
582
|
+
type: "WORKFLOW_RESET",
|
|
583
|
+
payload: {
|
|
584
|
+
fromRunId: runId,
|
|
585
|
+
toSeq: cut,
|
|
586
|
+
reapplySignals,
|
|
587
|
+
reapplyUpdates,
|
|
588
|
+
...options.reason !== void 0 ? { reason: options.reason } : {}
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
for (const [activityId, activity] of Object.entries(fork.state.activities)) {
|
|
592
|
+
if (!chunkISOIA7NV_cjs.isActivityOpen(activity)) continue;
|
|
593
|
+
const scheduled = copied[activity.scheduledSeq - 1];
|
|
594
|
+
if (scheduled?.type !== "ACTIVITY_SCHEDULED") continue;
|
|
595
|
+
this.afterCommand(fork, {
|
|
596
|
+
// The run-event schema checked the payload; it is the command that scheduled it.
|
|
597
|
+
...scheduled.payload,
|
|
598
|
+
type: "SCHEDULE_ACTIVITY",
|
|
599
|
+
activityId
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
for (const [timerId, timer] of Object.entries(fork.state.timers))
|
|
603
|
+
if (timer.status === "PENDING")
|
|
604
|
+
this.timers.push({ runId: newRunId, timerId, fireAt: Date.parse(timer.fireAt) });
|
|
605
|
+
for (const event of later) {
|
|
606
|
+
if (event.type === "SIGNAL_RECEIVED" && reapplySignals)
|
|
607
|
+
this.append(fork, { type: "SIGNAL_RECEIVED", payload: { ...event.payload } });
|
|
608
|
+
if (reapplyUpdates && (event.type === "UPDATE_ACCEPTED" || event.type === "UPDATE_ADMITTED") && !fork.updates.has(String(event.payload["updateId"])) && fork.state.updates[String(event.payload["updateId"])] === void 0) {
|
|
609
|
+
const headers = event.payload["headers"];
|
|
610
|
+
this.addUpdate(
|
|
611
|
+
fork,
|
|
612
|
+
{
|
|
613
|
+
updateId: String(event.payload["updateId"]),
|
|
614
|
+
name: String(event.payload["name"]),
|
|
615
|
+
args: event.payload["args"] ?? null,
|
|
616
|
+
...typeof headers === "object" && headers !== null && !Array.isArray(headers) ? (
|
|
617
|
+
// The run-event schema checked the headers map.
|
|
618
|
+
{ headers }
|
|
619
|
+
) : {}
|
|
620
|
+
},
|
|
621
|
+
"ADMITTED"
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (chunkISOIA7NV_cjs.hasUnprocessedInput(fork.history)) this.scheduleTask(fork);
|
|
626
|
+
await this.runUntilIdle(false);
|
|
627
|
+
return newRunId;
|
|
628
|
+
}
|
|
629
|
+
durationConfig(value, key) {
|
|
630
|
+
const ms = optionalDuration(value, key);
|
|
631
|
+
if (ms === void 0) return {};
|
|
632
|
+
const out = {};
|
|
633
|
+
out[key] = ms;
|
|
634
|
+
return out;
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* The workflow-id policies of a start (11 §2.1): the open run to use, or
|
|
638
|
+
* undefined to start a new run (after terminating the open one for
|
|
639
|
+
* `TERMINATE_EXISTING`). Throws {@link WorkflowAlreadyStartedError}.
|
|
640
|
+
*/
|
|
641
|
+
useExisting(workflowKey, options) {
|
|
642
|
+
const workflowId = options.workflowId ?? "";
|
|
643
|
+
const runs = this.runOrder.map((id) => this.run(id)).filter(
|
|
644
|
+
(run) => run.config.workflowKey === workflowKey && run.config.businessId === workflowId
|
|
645
|
+
);
|
|
646
|
+
const open = runs.find((run) => run.state.status === "RUNNING");
|
|
647
|
+
if (open) {
|
|
648
|
+
const conflict = options.idConflictPolicy ?? "FAIL";
|
|
649
|
+
if (conflict === "USE_EXISTING") return open.runId;
|
|
650
|
+
if (conflict === "FAIL")
|
|
651
|
+
throw new WorkflowAlreadyStartedError(workflowKey, workflowId, open.runId);
|
|
652
|
+
this.terminate(open.runId, "ID_CONFLICT");
|
|
653
|
+
return void 0;
|
|
654
|
+
}
|
|
655
|
+
const last = runs[runs.length - 1];
|
|
656
|
+
if (!last) return void 0;
|
|
657
|
+
const reuse = options.idReusePolicy ?? "ALLOW_DUPLICATE";
|
|
658
|
+
if (reuse === "REJECT_DUPLICATE" || reuse === "ALLOW_DUPLICATE_FAILED_ONLY" && !FAILED_STATUSES.has(last.state.status))
|
|
659
|
+
throw new WorkflowAlreadyStartedError(workflowKey, workflowId, last.runId);
|
|
660
|
+
return void 0;
|
|
661
|
+
}
|
|
189
662
|
/** Advance the clock by `duration`, firing timers and running work as it comes due. */
|
|
190
663
|
async sleep(duration) {
|
|
191
|
-
const until = this.clock +
|
|
664
|
+
const until = this.clock + chunkVA4OFR72_cjs.toMs(duration, "sleep duration");
|
|
192
665
|
await this.runUntilIdle(false);
|
|
193
666
|
for (; ; ) {
|
|
194
667
|
const next = this.nextDue();
|
|
@@ -210,7 +683,7 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
210
683
|
}
|
|
211
684
|
historyFileOf(runId, extra = {}) {
|
|
212
685
|
const run = this.run(runId);
|
|
213
|
-
return
|
|
686
|
+
return chunkHMZUGSMU_cjs.createHistoryFile(run.workflowFn, this.historyOf(runId), {
|
|
214
687
|
info: { ...run.info },
|
|
215
688
|
...extra
|
|
216
689
|
});
|
|
@@ -228,7 +701,7 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
228
701
|
if (value === definition) return name2;
|
|
229
702
|
const name = Object.hasOwn(this.workflows, definition.name) ? `${definition.name}#${this.runCounter}` : definition.name;
|
|
230
703
|
this.workflows[name] = definition;
|
|
231
|
-
this.registry = registryOf(this.workflows);
|
|
704
|
+
this.registry = registryOf(this.workflows, this.interceptors);
|
|
232
705
|
return name;
|
|
233
706
|
}
|
|
234
707
|
run(runId) {
|
|
@@ -236,7 +709,7 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
236
709
|
if (!run) throw new Error(`Unknown run ${runId}`);
|
|
237
710
|
return run;
|
|
238
711
|
}
|
|
239
|
-
createRun(workflowFn, input, info, parent, extraStarted = {}) {
|
|
712
|
+
createRun(workflowFn, input, info, parent, extraStarted = {}, config = { workflowKey: workflowFn, runAttempt: 1 }, admitted = []) {
|
|
240
713
|
this.runCounter += 1;
|
|
241
714
|
const runId = info.runId ?? `run-${this.runCounter}`;
|
|
242
715
|
const snapshot = {
|
|
@@ -253,23 +726,84 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
253
726
|
runId,
|
|
254
727
|
workflowFn,
|
|
255
728
|
history: [],
|
|
256
|
-
state:
|
|
729
|
+
state: chunkISOIA7NV_cjs.initialRunState(),
|
|
257
730
|
taskCounter: 0,
|
|
258
731
|
info: snapshot,
|
|
259
732
|
children: /* @__PURE__ */ new Map(),
|
|
260
|
-
|
|
733
|
+
config,
|
|
734
|
+
...parent ? { parent } : {},
|
|
735
|
+
updates: /* @__PURE__ */ new Map(),
|
|
736
|
+
sinkCalls: []
|
|
261
737
|
};
|
|
738
|
+
const firstTaskDelay = config.startDelayMs ?? config.firstTaskDelayMs;
|
|
739
|
+
if (firstTaskDelay !== void 0 && firstTaskDelay > 0) {
|
|
740
|
+
run.delayedUntil = this.clock + firstTaskDelay;
|
|
741
|
+
this.dues.push({ runId, kind: "start", at: run.delayedUntil });
|
|
742
|
+
}
|
|
743
|
+
if (config.runTimeoutMs !== void 0)
|
|
744
|
+
this.dues.push({ runId, kind: "runTimeout", at: this.clock + config.runTimeoutMs });
|
|
745
|
+
if (config.executionDeadline !== void 0)
|
|
746
|
+
this.dues.push({ runId, kind: "executionTimeout", at: config.executionDeadline });
|
|
262
747
|
this.runs.set(runId, run);
|
|
263
748
|
this.runOrder.push(runId);
|
|
264
|
-
this.append(
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
749
|
+
this.append(
|
|
750
|
+
run,
|
|
751
|
+
{
|
|
752
|
+
type: "WORKFLOW_STARTED",
|
|
753
|
+
// The service writes the seed of ctx.random()/ctx.uuid() into the start
|
|
754
|
+
// event so the history replays on its own; it uses the run id.
|
|
755
|
+
payload: {
|
|
756
|
+
workflowFn,
|
|
757
|
+
input,
|
|
758
|
+
taskQueue: config.taskQueue ?? "managed",
|
|
759
|
+
randomnessSeed: runId,
|
|
760
|
+
...extraStarted,
|
|
761
|
+
...this.startedFields(config)
|
|
762
|
+
}
|
|
763
|
+
},
|
|
764
|
+
admitted.length === 0
|
|
765
|
+
);
|
|
766
|
+
if (admitted.length > 0) {
|
|
767
|
+
for (const update of admitted) {
|
|
768
|
+
run.updates.set(update.updateId, update);
|
|
769
|
+
this.append(run, { type: "UPDATE_ADMITTED", payload: admittedPayload(update) }, false);
|
|
770
|
+
}
|
|
771
|
+
this.scheduleTask(run);
|
|
772
|
+
}
|
|
270
773
|
return runId;
|
|
271
774
|
}
|
|
272
|
-
|
|
775
|
+
/** The R2 fields of `WORKFLOW_STARTED`, each written only when it is set. */
|
|
776
|
+
startedFields(config) {
|
|
777
|
+
const out = {};
|
|
778
|
+
if (config.businessId !== void 0) {
|
|
779
|
+
out["businessId"] = config.businessId;
|
|
780
|
+
out["workflowId"] = config.businessId;
|
|
781
|
+
}
|
|
782
|
+
if (config.startDelayMs !== void 0) out["startDelayMs"] = config.startDelayMs;
|
|
783
|
+
if (config.retry !== void 0 || config.runAttempt > 1) out["runAttempt"] = config.runAttempt;
|
|
784
|
+
if (config.summary !== void 0) out["summary"] = config.summary;
|
|
785
|
+
if (config.details !== void 0) out["details"] = config.details;
|
|
786
|
+
if (config.memo !== void 0) out["context"] = config.memo;
|
|
787
|
+
const timeouts = {};
|
|
788
|
+
if (config.executionTimeoutMs !== void 0)
|
|
789
|
+
timeouts["executionTimeout"] = config.executionTimeoutMs;
|
|
790
|
+
if (config.runTimeoutMs !== void 0) timeouts["runTimeout"] = config.runTimeoutMs;
|
|
791
|
+
if (config.workflowTaskTimeoutMs !== void 0)
|
|
792
|
+
timeouts["workflowTaskTimeout"] = config.workflowTaskTimeoutMs;
|
|
793
|
+
if (Object.keys(timeouts).length > 0) out["timeouts"] = timeouts;
|
|
794
|
+
if (config.retry !== void 0) out["retry"] = toJsonValue(config.retry);
|
|
795
|
+
if (config.versioningBehavior !== void 0)
|
|
796
|
+
out["versioningBehavior"] = config.versioningBehavior;
|
|
797
|
+
if (config.lastCompletionResult !== void 0 && this.historyRelease >= 2)
|
|
798
|
+
out["lastCompletionResult"] = config.lastCompletionResult;
|
|
799
|
+
if (config.lastFailure !== void 0) out["lastFailure"] = toJsonValue(config.lastFailure);
|
|
800
|
+
if (config.headers !== void 0) out["headers"] = { ...config.headers };
|
|
801
|
+
if (config.searchAttributes !== void 0) out["searchAttributes"] = config.searchAttributes;
|
|
802
|
+
if (config.fairnessKey !== void 0) out["fairnessKey"] = config.fairnessKey;
|
|
803
|
+
if (config.fairnessWeight !== void 0) out["fairnessWeight"] = config.fairnessWeight;
|
|
804
|
+
return out;
|
|
805
|
+
}
|
|
806
|
+
append(run, draft, schedule = true) {
|
|
273
807
|
const event = {
|
|
274
808
|
seq: run.history.length + 1,
|
|
275
809
|
type: draft.type,
|
|
@@ -277,9 +811,10 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
277
811
|
createdAt: new Date(this.clock).toISOString(),
|
|
278
812
|
...draft.taskId !== void 0 ? { taskId: draft.taskId } : {}
|
|
279
813
|
};
|
|
280
|
-
|
|
814
|
+
chunkISOIA7NV_cjs.applyEvent(run.state, event);
|
|
281
815
|
run.history.push(event);
|
|
282
|
-
if (
|
|
816
|
+
if (run.delayedUntil !== void 0 && event.type === "SIGNAL_RECEIVED") this.endDelay(run);
|
|
817
|
+
if (schedule && run.delayedUntil === void 0 && chunkTYF56RGB_cjs.isInputEvent(event) && run.state.status === "RUNNING" && run.state.task.state === "NONE") {
|
|
283
818
|
run.taskCounter += 1;
|
|
284
819
|
const taskId = `${run.runId}-task-${run.taskCounter}`;
|
|
285
820
|
const scheduled = {
|
|
@@ -288,12 +823,18 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
288
823
|
payload: { taskId },
|
|
289
824
|
createdAt: event.createdAt
|
|
290
825
|
};
|
|
291
|
-
|
|
826
|
+
chunkISOIA7NV_cjs.applyEvent(run.state, scheduled);
|
|
292
827
|
run.history.push(scheduled);
|
|
293
828
|
}
|
|
294
829
|
return event;
|
|
295
830
|
}
|
|
296
|
-
|
|
831
|
+
/** End a start delay: the first workflow task is scheduled now. */
|
|
832
|
+
endDelay(run) {
|
|
833
|
+
run.delayedUntil = void 0;
|
|
834
|
+
this.dues = this.dues.filter((due) => !(due.runId === run.runId && due.kind === "start"));
|
|
835
|
+
this.scheduleTask(run);
|
|
836
|
+
}
|
|
837
|
+
deliverSignal(runId, name, payload, headers) {
|
|
297
838
|
const run = this.runs.get(runId);
|
|
298
839
|
if (!run || run.state.status !== "RUNNING") return false;
|
|
299
840
|
this.append(run, {
|
|
@@ -301,7 +842,8 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
301
842
|
payload: {
|
|
302
843
|
name,
|
|
303
844
|
payload: toJsonValue(payload),
|
|
304
|
-
receivedAt: new Date(this.clock).toISOString()
|
|
845
|
+
receivedAt: new Date(this.clock).toISOString(),
|
|
846
|
+
...headers !== void 0 ? { headers: { ...headers } } : {}
|
|
305
847
|
}
|
|
306
848
|
});
|
|
307
849
|
return true;
|
|
@@ -314,6 +856,11 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
314
856
|
type: "WORKFLOW_CANCEL_REQUESTED",
|
|
315
857
|
payload: reason !== void 0 ? { reason } : {}
|
|
316
858
|
});
|
|
859
|
+
if (run.delayedUntil !== void 0) {
|
|
860
|
+
run.delayedUntil = void 0;
|
|
861
|
+
this.append(run, { type: "WORKFLOW_CANCELLED", payload: {} });
|
|
862
|
+
this.onClosed(run);
|
|
863
|
+
}
|
|
317
864
|
return true;
|
|
318
865
|
}
|
|
319
866
|
terminate(runId, reason) {
|
|
@@ -333,7 +880,9 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
333
880
|
await this.runTask(task);
|
|
334
881
|
continue;
|
|
335
882
|
}
|
|
336
|
-
const attempt = this.attempts.find(
|
|
883
|
+
const attempt = this.attempts.find(
|
|
884
|
+
(entry) => entry.dueAt <= this.clock && !this.isHeld(this.holdKeyOf(entry))
|
|
885
|
+
);
|
|
337
886
|
if (attempt) {
|
|
338
887
|
this.attempts = this.attempts.filter((entry) => entry !== attempt);
|
|
339
888
|
await this.runAttempt(attempt);
|
|
@@ -352,16 +901,78 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
352
901
|
}
|
|
353
902
|
throw new Error("The test environment did not settle after 100000 steps");
|
|
354
903
|
}
|
|
904
|
+
/** When a held attempt's schedule-to-start timeout expires, if it has one. */
|
|
905
|
+
heldDeadline(entry) {
|
|
906
|
+
return this.isHeld(this.holdKeyOf(entry)) && entry.scheduleToStart !== void 0 ? entry.dueAt + entry.scheduleToStart : void 0;
|
|
907
|
+
}
|
|
355
908
|
nextDue() {
|
|
356
|
-
const due = [
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
909
|
+
const due = [...this.timers.map((timer) => timer.fireAt)];
|
|
910
|
+
for (const entry of this.attempts) {
|
|
911
|
+
if (!this.isHeld(this.holdKeyOf(entry))) due.push(entry.dueAt);
|
|
912
|
+
const deadline = this.heldDeadline(entry);
|
|
913
|
+
if (deadline !== void 0) due.push(deadline);
|
|
914
|
+
}
|
|
915
|
+
for (const entry of this.dues) due.push(entry.at);
|
|
360
916
|
return due.length === 0 ? void 0 : Math.min(...due);
|
|
361
917
|
}
|
|
918
|
+
/** Expire held attempts whose schedule-to-start timeout passed (not retried, 11 §2.3). */
|
|
919
|
+
expireHeld() {
|
|
920
|
+
const expired = this.attempts.filter((entry) => {
|
|
921
|
+
const deadline = this.heldDeadline(entry);
|
|
922
|
+
return deadline !== void 0 && deadline <= this.clock;
|
|
923
|
+
});
|
|
924
|
+
if (expired.length === 0) return false;
|
|
925
|
+
this.attempts = this.attempts.filter((entry) => !expired.includes(entry));
|
|
926
|
+
for (const entry of expired) {
|
|
927
|
+
const run = this.runs.get(entry.runId);
|
|
928
|
+
const activity = run?.state.activities[entry.activityId];
|
|
929
|
+
if (!run || run.state.status !== "RUNNING" || !activity || !chunkISOIA7NV_cjs.isActivityOpen(activity))
|
|
930
|
+
continue;
|
|
931
|
+
this.append(run, {
|
|
932
|
+
type: "ACTIVITY_TIMED_OUT",
|
|
933
|
+
payload: {
|
|
934
|
+
activityId: entry.activityId,
|
|
935
|
+
attempt: entry.attempt,
|
|
936
|
+
timeoutType: "SCHEDULE_TO_START"
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
return true;
|
|
941
|
+
}
|
|
942
|
+
/** Fire run-level deadlines: the end of a start delay, run and execution timeouts. */
|
|
943
|
+
fireRunDues() {
|
|
944
|
+
const due = this.dues.filter((entry) => entry.at <= this.clock).sort((a, b) => a.at - b.at);
|
|
945
|
+
if (due.length === 0) return false;
|
|
946
|
+
this.dues = this.dues.filter((entry) => !due.includes(entry));
|
|
947
|
+
for (const entry of due) {
|
|
948
|
+
const run = this.runs.get(entry.runId);
|
|
949
|
+
if (!run || run.state.status !== "RUNNING") continue;
|
|
950
|
+
if (entry.kind === "start") {
|
|
951
|
+
if (run.delayedUntil !== void 0) this.endDelay(run);
|
|
952
|
+
continue;
|
|
953
|
+
}
|
|
954
|
+
const timeoutType = entry.kind === "runTimeout" ? "WORKFLOW_RUN" : "WORKFLOW_EXECUTION";
|
|
955
|
+
const failure = {
|
|
956
|
+
errorType: "TIMEOUT",
|
|
957
|
+
message: timeoutType === "WORKFLOW_RUN" ? "The run timeout expired" : "The execution timeout of the chain expired",
|
|
958
|
+
type: "TimeoutFailure",
|
|
959
|
+
timeoutType,
|
|
960
|
+
source: "engine"
|
|
961
|
+
};
|
|
962
|
+
run.delayedUntil = void 0;
|
|
963
|
+
this.append(run, {
|
|
964
|
+
type: "WORKFLOW_TIMED_OUT",
|
|
965
|
+
payload: { timeoutType, failure: toJsonValue(failure) }
|
|
966
|
+
});
|
|
967
|
+
this.onClosed(run);
|
|
968
|
+
}
|
|
969
|
+
return true;
|
|
970
|
+
}
|
|
362
971
|
fireDue() {
|
|
972
|
+
const expired = this.expireHeld();
|
|
973
|
+
const fired = this.fireRunDues();
|
|
363
974
|
const due = this.timers.filter((timer) => timer.fireAt <= this.clock).sort((a, b) => a.fireAt - b.fireAt);
|
|
364
|
-
if (due.length === 0) return
|
|
975
|
+
if (due.length === 0) return expired || fired;
|
|
365
976
|
this.timers = this.timers.filter((timer) => !due.includes(timer));
|
|
366
977
|
for (const timer of due) {
|
|
367
978
|
const run = this.runs.get(timer.runId);
|
|
@@ -374,15 +985,27 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
374
985
|
async runTask(run) {
|
|
375
986
|
const taskId = run.state.task.taskId ?? `${run.runId}-task-${run.taskCounter}`;
|
|
376
987
|
this.append(run, { type: "WORKFLOW_TASK_STARTED", payload: { taskId, attempt: 1 } });
|
|
988
|
+
const delivered = [...run.updates.values()].filter(
|
|
989
|
+
(update) => update.status === "PENDING" || update.status === "ADMITTED"
|
|
990
|
+
);
|
|
991
|
+
const updates = delivered.map((update) => ({
|
|
992
|
+
updateId: update.updateId,
|
|
993
|
+
name: update.name,
|
|
994
|
+
args: update.args,
|
|
995
|
+
...update.headers !== void 0 ? { headers: { ...update.headers } } : {},
|
|
996
|
+
...update.status === "ADMITTED" ? { admitted: true } : {}
|
|
997
|
+
}));
|
|
377
998
|
const request = {
|
|
378
|
-
protocol:
|
|
999
|
+
protocol: chunkTYF56RGB_cjs.HISTORY_PROTOCOL,
|
|
379
1000
|
kind: "task",
|
|
380
1001
|
workflowFn: run.workflowFn,
|
|
381
1002
|
info: run.info,
|
|
382
1003
|
history: run.history,
|
|
383
|
-
nowMs: this.clock
|
|
1004
|
+
nowMs: this.clock,
|
|
1005
|
+
...updates.length > 0 ? { updates } : {},
|
|
1006
|
+
...this.codeFeatures.length > 0 ? { features: [...this.codeFeatures] } : {}
|
|
384
1007
|
};
|
|
385
|
-
const result =
|
|
1008
|
+
const result = await this.executeWithLocalActivities(run, request);
|
|
386
1009
|
const fail = (cause, message) => {
|
|
387
1010
|
this.append(run, {
|
|
388
1011
|
type: "WORKFLOW_TASK_FAILED",
|
|
@@ -395,29 +1018,145 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
395
1018
|
run.taskFailure = result;
|
|
396
1019
|
return;
|
|
397
1020
|
}
|
|
398
|
-
const
|
|
1021
|
+
const relief = {
|
|
1022
|
+
grantedLocalActivityIds: [],
|
|
1023
|
+
deliveredUpdateIds: delivered.map((update) => update.updateId)
|
|
1024
|
+
};
|
|
1025
|
+
const checked = chunkHMZUGSMU_cjs.validateCommands(result.commands, { enabled: this.enabled, relief });
|
|
399
1026
|
if (!checked.ok) {
|
|
400
1027
|
fail(checked.cause, checked.issues.map((issue) => issue.message).join("; "));
|
|
401
1028
|
return;
|
|
402
1029
|
}
|
|
403
|
-
const drafts =
|
|
1030
|
+
const drafts = chunkISOIA7NV_cjs.eventsForCommands(run.state, checked.commands, {
|
|
404
1031
|
taskId,
|
|
405
1032
|
nowMs: this.clock,
|
|
406
|
-
enabled: this.enabled
|
|
1033
|
+
enabled: this.enabled,
|
|
1034
|
+
relief,
|
|
1035
|
+
updates: Object.fromEntries(
|
|
1036
|
+
delivered.map((update) => [
|
|
1037
|
+
update.updateId,
|
|
1038
|
+
{
|
|
1039
|
+
name: update.name,
|
|
1040
|
+
args: update.args,
|
|
1041
|
+
...update.headers !== void 0 ? { headers: update.headers } : {}
|
|
1042
|
+
}
|
|
1043
|
+
])
|
|
1044
|
+
)
|
|
407
1045
|
});
|
|
408
1046
|
if (!drafts.ok) {
|
|
409
1047
|
fail(drafts.cause, drafts.issues.map((issue) => issue.message).join("; "));
|
|
410
1048
|
return;
|
|
411
1049
|
}
|
|
1050
|
+
if (result.unfinishedHandlers !== void 0 && result.unfinishedHandlers.length > 0)
|
|
1051
|
+
run.unfinishedHandlers = result.unfinishedHandlers;
|
|
1052
|
+
this.recordUpdateReplies(run, checked.commands, delivered);
|
|
1053
|
+
this.runSinks(run, result.sinkCalls ?? []);
|
|
412
1054
|
this.append(run, { type: "WORKFLOW_TASK_COMPLETED", taskId, payload: { taskId } });
|
|
413
1055
|
for (const draft of drafts.events) this.append(run, draft);
|
|
414
1056
|
for (const command of checked.commands)
|
|
415
|
-
if (run.state.status === "RUNNING" ||
|
|
1057
|
+
if (run.state.status === "RUNNING" || chunkTYF56RGB_cjs.TERMINAL_COMMANDS.has(command.type))
|
|
416
1058
|
this.afterCommand(run, command);
|
|
417
1059
|
}
|
|
1060
|
+
/** What a task's update replies mean for the run's update rows. */
|
|
1061
|
+
recordUpdateReplies(run, commands, delivered) {
|
|
1062
|
+
const answered = /* @__PURE__ */ new Set();
|
|
1063
|
+
for (const command of commands) {
|
|
1064
|
+
if (command.type !== "ACCEPT_UPDATE" && command.type !== "REJECT_UPDATE" && command.type !== "COMPLETE_UPDATE")
|
|
1065
|
+
continue;
|
|
1066
|
+
const entry = run.updates.get(command.updateId);
|
|
1067
|
+
if (!entry) continue;
|
|
1068
|
+
answered.add(command.updateId);
|
|
1069
|
+
if (command.type === "ACCEPT_UPDATE") entry.status = "ACCEPTED";
|
|
1070
|
+
else if (command.type === "REJECT_UPDATE") {
|
|
1071
|
+
entry.status = "REJECTED";
|
|
1072
|
+
entry.failure = command.failure;
|
|
1073
|
+
} else {
|
|
1074
|
+
entry.status = "COMPLETED";
|
|
1075
|
+
if (command.failure !== void 0) entry.failure = command.failure;
|
|
1076
|
+
else entry.result = command.result ?? null;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
for (const update of delivered)
|
|
1080
|
+
if (!answered.has(update.updateId) && update.status === "PENDING") {
|
|
1081
|
+
update.status = "REJECTED";
|
|
1082
|
+
update.failure = {
|
|
1083
|
+
errorType: "VALIDATION",
|
|
1084
|
+
type: "UpdateNotHandled",
|
|
1085
|
+
message: `The workflow task did not answer update "${update.name}"`
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
/** Keep a task's sink calls and call the configured sinks with them. */
|
|
1090
|
+
runSinks(run, calls) {
|
|
1091
|
+
for (const call of calls) {
|
|
1092
|
+
run.sinkCalls.push(call);
|
|
1093
|
+
const fn = this.sinks[call.sink]?.[call.fn];
|
|
1094
|
+
if (fn === void 0) continue;
|
|
1095
|
+
try {
|
|
1096
|
+
fn(...call.args);
|
|
1097
|
+
} catch {
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
execute(request) {
|
|
1102
|
+
return this.executor ? this.executor(request) : runWorkflowTaskInProcess(this.registry, request);
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Run a workflow task as the worker does (11 §2.12): while it asks for
|
|
1106
|
+
* local activities, run each through its mock and run the same task again
|
|
1107
|
+
* with the outcomes, up to 1,000 rounds.
|
|
1108
|
+
*/
|
|
1109
|
+
async executeWithLocalActivities(run, request) {
|
|
1110
|
+
const outcomes = /* @__PURE__ */ new Map();
|
|
1111
|
+
let result = await this.execute(request);
|
|
1112
|
+
for (let round = 0; result.outcome === "localActivityRequested"; round++) {
|
|
1113
|
+
if (round >= 1e3)
|
|
1114
|
+
return {
|
|
1115
|
+
...result,
|
|
1116
|
+
outcome: "failed",
|
|
1117
|
+
commands: [],
|
|
1118
|
+
failure: { cause: "INTERNAL", message: "The task asked for local activities 1000 times" }
|
|
1119
|
+
};
|
|
1120
|
+
for (const wanted of result.localActivityRequests ?? [])
|
|
1121
|
+
outcomes.set(wanted.localActivityId, await this.runLocalActivity(run, wanted));
|
|
1122
|
+
result = await this.execute({ ...request, localActivityResults: [...outcomes.values()] });
|
|
1123
|
+
}
|
|
1124
|
+
return result;
|
|
1125
|
+
}
|
|
1126
|
+
async runLocalActivity(run, wanted) {
|
|
1127
|
+
const mock = this.mocks.get(`${wanted.nodeType}#${wanted.operation}`) ?? this.mocks.get(wanted.nodeType);
|
|
1128
|
+
try {
|
|
1129
|
+
if (!mock)
|
|
1130
|
+
throw new chunkZCGKGDCL_cjs.ApplicationError(
|
|
1131
|
+
`No mock for ${wanted.nodeType} ${wanted.operation} (use env.mock)`,
|
|
1132
|
+
{ errorType: "VALIDATION", nonRetryable: true, type: "UnmockedActivity" }
|
|
1133
|
+
);
|
|
1134
|
+
const result = toJsonValue(
|
|
1135
|
+
await mock(wanted.args, {
|
|
1136
|
+
runId: run.runId,
|
|
1137
|
+
activityId: wanted.localActivityId,
|
|
1138
|
+
attempt: wanted.attempt,
|
|
1139
|
+
nodeType: wanted.nodeType,
|
|
1140
|
+
operation: wanted.operation,
|
|
1141
|
+
local: true,
|
|
1142
|
+
...wanted.connection !== void 0 ? { connection: wanted.connection } : {}
|
|
1143
|
+
})
|
|
1144
|
+
);
|
|
1145
|
+
return { localActivityId: wanted.localActivityId, result, attempts: wanted.attempt };
|
|
1146
|
+
} catch (error) {
|
|
1147
|
+
const failure = chunkZCGKGDCL_cjs.errorToFailure(error, `node:${wanted.nodeType}`);
|
|
1148
|
+
delete failure.stackTrace;
|
|
1149
|
+
return {
|
|
1150
|
+
localActivityId: wanted.localActivityId,
|
|
1151
|
+
failure: toJsonValue(failure),
|
|
1152
|
+
attempts: wanted.attempt
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
418
1156
|
afterCommand(run, command) {
|
|
419
1157
|
switch (command.type) {
|
|
420
1158
|
case "SCHEDULE_ACTIVITY": {
|
|
1159
|
+
const user = command.kind === "user";
|
|
421
1160
|
const attempt = {
|
|
422
1161
|
runId: run.runId,
|
|
423
1162
|
activityId: command.activityId,
|
|
@@ -425,12 +1164,16 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
425
1164
|
dueAt: this.clock,
|
|
426
1165
|
scheduledAt: this.clock,
|
|
427
1166
|
args: command.args,
|
|
428
|
-
|
|
429
|
-
|
|
1167
|
+
kind: command.kind,
|
|
1168
|
+
nodeType: user ? "" : command.nodeType,
|
|
1169
|
+
operation: user ? command.activityName : command.operation,
|
|
430
1170
|
retry: command.retry,
|
|
431
1171
|
cancelRequested: false,
|
|
432
|
-
|
|
433
|
-
...
|
|
1172
|
+
...!user && command.connection !== void 0 ? { connection: command.connection } : {},
|
|
1173
|
+
...user ? { taskQueue: command.taskQueue } : {},
|
|
1174
|
+
...command.headers !== void 0 ? { headers: { ...command.headers } } : {},
|
|
1175
|
+
...command.scheduleToCloseTimeout !== void 0 ? { scheduleToClose: command.scheduleToCloseTimeout } : {},
|
|
1176
|
+
...command.scheduleToStartTimeout !== void 0 ? { scheduleToStart: command.scheduleToStartTimeout } : {}
|
|
434
1177
|
};
|
|
435
1178
|
this.attempts.push(attempt);
|
|
436
1179
|
return;
|
|
@@ -505,18 +1248,25 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
505
1248
|
return;
|
|
506
1249
|
}
|
|
507
1250
|
case "CONTINUE_AS_NEW": {
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
)
|
|
519
|
-
|
|
1251
|
+
const options = command.options;
|
|
1252
|
+
const config = {
|
|
1253
|
+
...this.chainConfig(run.config),
|
|
1254
|
+
// The run timeout restarts and the attempt count resets; the
|
|
1255
|
+
// execution timeout of the chain carries over (11 §2.20).
|
|
1256
|
+
lastCompletionResult: command.input
|
|
1257
|
+
};
|
|
1258
|
+
if (typeof options["taskQueue"] === "string") config.taskQueue = options["taskQueue"];
|
|
1259
|
+
if (typeof options["workflowTaskTimeout"] === "number")
|
|
1260
|
+
config.workflowTaskTimeoutMs = options["workflowTaskTimeout"];
|
|
1261
|
+
if (options["versioningBehavior"] === "PINNED" || options["versioningBehavior"] === "AUTO_UPGRADE")
|
|
1262
|
+
config.versioningBehavior = options["versioningBehavior"];
|
|
1263
|
+
const memo = options["context"];
|
|
1264
|
+
if (typeof memo === "object" && memo !== null && !Array.isArray(memo)) config.memo = memo;
|
|
1265
|
+
else if (Object.keys(run.state.context).length > 0) config.memo = { ...run.state.context };
|
|
1266
|
+
const retry = options["retry"];
|
|
1267
|
+
if (chunkISOIA7NV_cjs.continueAsNewRetryShape(options) === "run" && typeof retry === "object" && retry !== null && !Array.isArray(retry))
|
|
1268
|
+
config.retry = retry;
|
|
1269
|
+
this.startSuccessor(run, command.input, config);
|
|
520
1270
|
this.onClosed(run);
|
|
521
1271
|
return;
|
|
522
1272
|
}
|
|
@@ -529,11 +1279,62 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
529
1279
|
return;
|
|
530
1280
|
}
|
|
531
1281
|
}
|
|
1282
|
+
/** What a successor of a run inherits (continue-as-new, run retry). */
|
|
1283
|
+
chainConfig(config) {
|
|
1284
|
+
const out = { workflowKey: config.workflowKey, runAttempt: 1 };
|
|
1285
|
+
for (const key of [
|
|
1286
|
+
"businessId",
|
|
1287
|
+
"executionDeadline",
|
|
1288
|
+
"executionTimeoutMs",
|
|
1289
|
+
"runTimeoutMs",
|
|
1290
|
+
"workflowTaskTimeoutMs",
|
|
1291
|
+
"retry",
|
|
1292
|
+
"versioningBehavior",
|
|
1293
|
+
"taskQueue",
|
|
1294
|
+
"memo"
|
|
1295
|
+
])
|
|
1296
|
+
if (config[key] !== void 0) Object.assign(out, { [key]: config[key] });
|
|
1297
|
+
return out;
|
|
1298
|
+
}
|
|
1299
|
+
/** Start the next run of a chain; the closed run's parent and handles follow it. */
|
|
1300
|
+
startSuccessor(run, input, config) {
|
|
1301
|
+
const next = this.createRun(
|
|
1302
|
+
run.workflowFn,
|
|
1303
|
+
input,
|
|
1304
|
+
{
|
|
1305
|
+
...run.info,
|
|
1306
|
+
runId: void 0,
|
|
1307
|
+
continuedFromRunId: run.runId,
|
|
1308
|
+
firstRunId: run.info.firstRunId ?? run.runId
|
|
1309
|
+
},
|
|
1310
|
+
run.parent,
|
|
1311
|
+
{},
|
|
1312
|
+
config
|
|
1313
|
+
);
|
|
1314
|
+
run.continuedTo = next;
|
|
1315
|
+
return next;
|
|
1316
|
+
}
|
|
532
1317
|
resolveTarget(run, target) {
|
|
533
1318
|
if ("runId" in target) return target.runId;
|
|
534
1319
|
if ("parent" in target) return run.parent?.runId ?? run.info.parentRunId ?? void 0;
|
|
535
|
-
if ("childId" in target)
|
|
536
|
-
|
|
1320
|
+
if ("childId" in target) {
|
|
1321
|
+
let childRunId = run.children.get(target.childId);
|
|
1322
|
+
while (childRunId !== void 0) {
|
|
1323
|
+
const next = this.runs.get(childRunId)?.continuedTo;
|
|
1324
|
+
if (next === void 0) break;
|
|
1325
|
+
childRunId = next;
|
|
1326
|
+
}
|
|
1327
|
+
return childRunId;
|
|
1328
|
+
}
|
|
1329
|
+
return this.resolveWorkflowId(target.workflowKey, target.workflowId, target.firstRunId);
|
|
1330
|
+
}
|
|
1331
|
+
/** The open run of a workflow id, else its latest run (11 §2.1). */
|
|
1332
|
+
resolveWorkflowId(workflowKey, workflowId, firstRunId) {
|
|
1333
|
+
const matching = this.runOrder.map((id) => this.run(id)).filter(
|
|
1334
|
+
(candidate) => candidate.config.workflowKey === workflowKey && candidate.config.businessId === workflowId && (firstRunId === void 0 || (candidate.info.firstRunId ?? candidate.runId) === firstRunId)
|
|
1335
|
+
);
|
|
1336
|
+
const open = matching.find((candidate) => candidate.state.status === "RUNNING");
|
|
1337
|
+
return (open ?? matching[matching.length - 1])?.runId;
|
|
537
1338
|
}
|
|
538
1339
|
startChild(run, command) {
|
|
539
1340
|
const byFn = command.workflowFn !== void 0 && (this.executor !== void 0 || this.registry.byName.has(command.workflowFn));
|
|
@@ -566,6 +1367,28 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
566
1367
|
});
|
|
567
1368
|
return;
|
|
568
1369
|
}
|
|
1370
|
+
const options = command.options;
|
|
1371
|
+
const ms = (key) => typeof options[key] === "number" ? options[key] : void 0;
|
|
1372
|
+
const executionTimeoutMs = ms("executionTimeout");
|
|
1373
|
+
const childKey = command.workflowKey ?? this.registry.byName.get(workflowFn)?.name ?? workflowFn;
|
|
1374
|
+
const config = { workflowKey: childKey, runAttempt: 1 };
|
|
1375
|
+
if (typeof options.workflowId === "string") config.businessId = options.workflowId;
|
|
1376
|
+
if (executionTimeoutMs !== void 0) {
|
|
1377
|
+
config.executionTimeoutMs = executionTimeoutMs;
|
|
1378
|
+
config.executionDeadline = this.clock + executionTimeoutMs;
|
|
1379
|
+
}
|
|
1380
|
+
const runTimeoutMs = ms("runTimeout");
|
|
1381
|
+
if (runTimeoutMs !== void 0) config.runTimeoutMs = runTimeoutMs;
|
|
1382
|
+
const taskTimeoutMs = ms("workflowTaskTimeout");
|
|
1383
|
+
if (taskTimeoutMs !== void 0) config.workflowTaskTimeoutMs = taskTimeoutMs;
|
|
1384
|
+
const startDelayMs = ms("startDelay");
|
|
1385
|
+
if (startDelayMs !== void 0) config.startDelayMs = startDelayMs;
|
|
1386
|
+
if (typeof options.summary === "string") config.summary = options.summary;
|
|
1387
|
+
if (typeof options.details === "string") config.details = options.details;
|
|
1388
|
+
if (options.context !== void 0) config.memo = options.context;
|
|
1389
|
+
if (options.versioningBehavior !== void 0)
|
|
1390
|
+
config.versioningBehavior = options.versioningBehavior;
|
|
1391
|
+
if (options.retry !== void 0) config.retry = options.retry;
|
|
569
1392
|
const childRunId = this.createRun(
|
|
570
1393
|
workflowFn,
|
|
571
1394
|
command.input,
|
|
@@ -574,7 +1397,8 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
574
1397
|
{
|
|
575
1398
|
parentRunId: run.runId,
|
|
576
1399
|
parentClosePolicy: command.options.parentClosePolicy
|
|
577
|
-
}
|
|
1400
|
+
},
|
|
1401
|
+
config
|
|
578
1402
|
);
|
|
579
1403
|
run.children.set(command.childId, childRunId);
|
|
580
1404
|
this.append(run, {
|
|
@@ -607,7 +1431,7 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
607
1431
|
payload: {
|
|
608
1432
|
childId: entry.childId,
|
|
609
1433
|
childRunId,
|
|
610
|
-
failure: toJsonValue(
|
|
1434
|
+
failure: toJsonValue(chunkZCGKGDCL_cjs.errorToFailure(error, "engine"))
|
|
611
1435
|
}
|
|
612
1436
|
});
|
|
613
1437
|
}
|
|
@@ -616,7 +1440,7 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
616
1440
|
const run = this.runs.get(attempt.runId);
|
|
617
1441
|
if (!run || run.state.status !== "RUNNING") return;
|
|
618
1442
|
const activity = run.state.activities[attempt.activityId];
|
|
619
|
-
if (!activity || !
|
|
1443
|
+
if (!activity || !chunkISOIA7NV_cjs.isActivityOpen(activity)) return;
|
|
620
1444
|
if (attempt.scheduleToClose !== void 0 && this.clock - attempt.scheduledAt > attempt.scheduleToClose) {
|
|
621
1445
|
this.append(run, {
|
|
622
1446
|
type: "ACTIVITY_TIMED_OUT",
|
|
@@ -628,35 +1452,151 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
628
1452
|
});
|
|
629
1453
|
return;
|
|
630
1454
|
}
|
|
631
|
-
const mock = this.mocks.get(`${attempt.nodeType}#${attempt.operation}`) ?? this.mocks.get(attempt.nodeType);
|
|
632
1455
|
let failure;
|
|
633
1456
|
let result = null;
|
|
1457
|
+
let heartbeatDetails = attempt.heartbeatDetails;
|
|
1458
|
+
let asyncToken;
|
|
634
1459
|
try {
|
|
635
|
-
if (
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
nonRetryable: true,
|
|
641
|
-
|
|
1460
|
+
if (attempt.kind === "user") {
|
|
1461
|
+
const impl = chunkVA4OFR72_cjs.findActivityImplementation(this.activityImpls, attempt.operation);
|
|
1462
|
+
if (!impl)
|
|
1463
|
+
throw new chunkZCGKGDCL_cjs.ApplicationError(
|
|
1464
|
+
`No implementation of activity ${attempt.operation} (use env.registerActivity or env.mockActivity)`,
|
|
1465
|
+
{ errorType: "VALIDATION", nonRetryable: true, type: "ActivityNotRegistered" }
|
|
1466
|
+
);
|
|
1467
|
+
const ctx = this.activityContext(
|
|
1468
|
+
attempt,
|
|
1469
|
+
(details) => {
|
|
1470
|
+
heartbeatDetails = details;
|
|
1471
|
+
},
|
|
1472
|
+
(token) => {
|
|
1473
|
+
asyncToken = token;
|
|
642
1474
|
}
|
|
643
1475
|
);
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
1476
|
+
result = toJsonValue(
|
|
1477
|
+
await chunkVA4OFR72_cjs.executeActivity(impl, ctx, attempt.args, this.activityInterceptors)
|
|
1478
|
+
);
|
|
1479
|
+
} else {
|
|
1480
|
+
const mock = this.mocks.get(`${attempt.nodeType}#${attempt.operation}`) ?? this.mocks.get(attempt.nodeType);
|
|
1481
|
+
if (!mock)
|
|
1482
|
+
throw new chunkZCGKGDCL_cjs.ApplicationError(
|
|
1483
|
+
`No mock for ${attempt.nodeType} ${attempt.operation} (use env.mock)`,
|
|
1484
|
+
{
|
|
1485
|
+
errorType: "VALIDATION",
|
|
1486
|
+
nonRetryable: true,
|
|
1487
|
+
type: "UnmockedActivity"
|
|
1488
|
+
}
|
|
1489
|
+
);
|
|
1490
|
+
result = toJsonValue(
|
|
1491
|
+
await mock(attempt.args, {
|
|
1492
|
+
runId: attempt.runId,
|
|
1493
|
+
activityId: attempt.activityId,
|
|
1494
|
+
attempt: attempt.attempt,
|
|
1495
|
+
nodeType: attempt.nodeType,
|
|
1496
|
+
operation: attempt.operation,
|
|
1497
|
+
...attempt.connection !== void 0 ? { connection: attempt.connection } : {},
|
|
1498
|
+
...attempt.heartbeatDetails !== void 0 ? { heartbeatDetails: attempt.heartbeatDetails } : {},
|
|
1499
|
+
heartbeat: (details) => {
|
|
1500
|
+
heartbeatDetails = toJsonValue(details);
|
|
1501
|
+
}
|
|
1502
|
+
})
|
|
1503
|
+
);
|
|
1504
|
+
}
|
|
654
1505
|
} catch (error) {
|
|
655
|
-
failure =
|
|
1506
|
+
failure = chunkZCGKGDCL_cjs.errorToFailure(
|
|
1507
|
+
error,
|
|
1508
|
+
attempt.kind === "user" ? "sdk-ts" : `node:${attempt.nodeType}`
|
|
1509
|
+
);
|
|
656
1510
|
delete failure.stackTrace;
|
|
657
1511
|
}
|
|
658
|
-
if (run.state.status !== "RUNNING" || !
|
|
1512
|
+
if (run.state.status !== "RUNNING" || !chunkISOIA7NV_cjs.isActivityOpen(run.state.activities[attempt.activityId] ?? activity))
|
|
659
1513
|
return;
|
|
1514
|
+
if (asyncToken !== void 0 && failure === void 0) {
|
|
1515
|
+
this.asyncAttempts.set(asyncToken, {
|
|
1516
|
+
...attempt,
|
|
1517
|
+
...heartbeatDetails !== void 0 ? { heartbeatDetails } : {}
|
|
1518
|
+
});
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
this.finishAttempt(run, attempt, failure, result, heartbeatDetails);
|
|
1522
|
+
}
|
|
1523
|
+
/** The `ActivityContext` of one user-activity attempt. */
|
|
1524
|
+
activityContext(attempt, onHeartbeat, onAsync) {
|
|
1525
|
+
const run = this.runs.get(attempt.runId);
|
|
1526
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1527
|
+
const signal = {
|
|
1528
|
+
get aborted() {
|
|
1529
|
+
return attempt.cancelRequested;
|
|
1530
|
+
},
|
|
1531
|
+
addEventListener: (_type, listener) => {
|
|
1532
|
+
listeners.add(listener);
|
|
1533
|
+
},
|
|
1534
|
+
removeEventListener: (_type, listener) => {
|
|
1535
|
+
listeners.delete(listener);
|
|
1536
|
+
}
|
|
1537
|
+
};
|
|
1538
|
+
return {
|
|
1539
|
+
info: {
|
|
1540
|
+
runId: attempt.runId,
|
|
1541
|
+
workspaceId: this.workspaceId,
|
|
1542
|
+
...run?.config.workflowKey !== void 0 ? { workflowKey: run.config.workflowKey } : {},
|
|
1543
|
+
activityId: attempt.activityId,
|
|
1544
|
+
activityName: attempt.operation,
|
|
1545
|
+
taskQueue: attempt.taskQueue ?? "managed",
|
|
1546
|
+
attempt: attempt.attempt,
|
|
1547
|
+
...attempt.heartbeatDetails !== void 0 ? { heartbeatDetails: attempt.heartbeatDetails } : {},
|
|
1548
|
+
headers: { ...attempt.headers ?? {} }
|
|
1549
|
+
},
|
|
1550
|
+
heartbeat: (details) => {
|
|
1551
|
+
onHeartbeat(toJsonValue(details));
|
|
1552
|
+
return Promise.resolve({ cancelRequested: attempt.cancelRequested, paused: false });
|
|
1553
|
+
},
|
|
1554
|
+
cancellationSignal: signal,
|
|
1555
|
+
completeAsync: () => {
|
|
1556
|
+
const token = `test-task-token:${attempt.runId}:${attempt.activityId}:${attempt.attempt}`;
|
|
1557
|
+
onAsync(token);
|
|
1558
|
+
return Promise.resolve(token);
|
|
1559
|
+
},
|
|
1560
|
+
connection: (name) => {
|
|
1561
|
+
const found = this.connections[name];
|
|
1562
|
+
if (found === void 0)
|
|
1563
|
+
return Promise.reject(
|
|
1564
|
+
new chunkZCGKGDCL_cjs.ApplicationError(`No connection "${name}" in the test environment`, {
|
|
1565
|
+
errorType: "AUTHORIZATION",
|
|
1566
|
+
nonRetryable: true,
|
|
1567
|
+
type: "ConnectionNotReleased"
|
|
1568
|
+
})
|
|
1569
|
+
);
|
|
1570
|
+
return Promise.resolve({ ...found });
|
|
1571
|
+
}
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
/** Complete an activity that called `completeAsync()` (`completeActivityAsync`). */
|
|
1575
|
+
async completeActivityAsync(token, result) {
|
|
1576
|
+
const attempt = this.takeAsync(token);
|
|
1577
|
+
const run = this.run(attempt.runId);
|
|
1578
|
+
this.finishAttempt(run, attempt, void 0, toJsonValue(result), attempt.heartbeatDetails);
|
|
1579
|
+
await this.runUntilIdle(false);
|
|
1580
|
+
}
|
|
1581
|
+
/** Fail an activity that called `completeAsync()` (`failActivityAsync`); the retry policy applies. */
|
|
1582
|
+
async failActivityAsync(token, error) {
|
|
1583
|
+
const attempt = this.takeAsync(token);
|
|
1584
|
+
const run = this.run(attempt.runId);
|
|
1585
|
+
const failure = chunkZCGKGDCL_cjs.errorToFailure(error, "sdk-ts");
|
|
1586
|
+
delete failure.stackTrace;
|
|
1587
|
+
this.finishAttempt(run, attempt, failure, null, attempt.heartbeatDetails);
|
|
1588
|
+
await this.runUntilIdle(false);
|
|
1589
|
+
}
|
|
1590
|
+
takeAsync(token) {
|
|
1591
|
+
const attempt = this.asyncAttempts.get(token);
|
|
1592
|
+
if (!attempt) throw new Error(`Unknown or used activity task token ${token}`);
|
|
1593
|
+
this.asyncAttempts.delete(token);
|
|
1594
|
+
return attempt;
|
|
1595
|
+
}
|
|
1596
|
+
/** Record an attempt's outcome: completion, a final failure or timeout, or the next attempt. */
|
|
1597
|
+
finishAttempt(run, attempt, failure, result, heartbeatDetails) {
|
|
1598
|
+
const open = run.state.activities[attempt.activityId];
|
|
1599
|
+
if (run.state.status !== "RUNNING" || !open || !chunkISOIA7NV_cjs.isActivityOpen(open)) return;
|
|
660
1600
|
const started = () => {
|
|
661
1601
|
this.append(run, {
|
|
662
1602
|
type: "ACTIVITY_STARTED",
|
|
@@ -672,7 +1612,7 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
672
1612
|
return;
|
|
673
1613
|
}
|
|
674
1614
|
if (failure.timeoutType !== void 0 || failure.type === "TimeoutFailure") {
|
|
675
|
-
const decision2 =
|
|
1615
|
+
const decision2 = chunkTYF56RGB_cjs.computeRetryDelay(attempt.retry, attempt.attempt, failure);
|
|
676
1616
|
if (!decision2.retry) {
|
|
677
1617
|
started();
|
|
678
1618
|
this.append(run, {
|
|
@@ -689,16 +1629,18 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
689
1629
|
this.attempts.push({
|
|
690
1630
|
...attempt,
|
|
691
1631
|
attempt: attempt.attempt + 1,
|
|
692
|
-
dueAt: this.clock + decision2.delayMs
|
|
1632
|
+
dueAt: this.clock + decision2.delayMs,
|
|
1633
|
+
...heartbeatDetails !== void 0 ? { heartbeatDetails } : {}
|
|
693
1634
|
});
|
|
694
1635
|
return;
|
|
695
1636
|
}
|
|
696
|
-
const decision =
|
|
1637
|
+
const decision = chunkTYF56RGB_cjs.computeRetryDelay(attempt.retry, attempt.attempt, failure);
|
|
697
1638
|
if (decision.retry) {
|
|
698
1639
|
this.attempts.push({
|
|
699
1640
|
...attempt,
|
|
700
1641
|
attempt: attempt.attempt + 1,
|
|
701
|
-
dueAt: this.clock + decision.delayMs
|
|
1642
|
+
dueAt: this.clock + decision.delayMs,
|
|
1643
|
+
...heartbeatDetails !== void 0 ? { heartbeatDetails } : {}
|
|
702
1644
|
});
|
|
703
1645
|
return;
|
|
704
1646
|
}
|
|
@@ -712,23 +1654,68 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
712
1654
|
}
|
|
713
1655
|
});
|
|
714
1656
|
}
|
|
715
|
-
|
|
1657
|
+
/**
|
|
1658
|
+
* Run-level retry (11 §2.17): a failed or run-timed-out run whose policy
|
|
1659
|
+
* allows another attempt is followed by a new run of the chain after the
|
|
1660
|
+
* backoff, with `runAttempt + 1` and the failure as `lastFailure`.
|
|
1661
|
+
*/
|
|
1662
|
+
retryRun(run) {
|
|
1663
|
+
if (run.continuedTo !== void 0) return;
|
|
1664
|
+
const last = run.history[run.history.length - 1];
|
|
1665
|
+
if (!last || last.type !== "WORKFLOW_FAILED" && last.type !== "WORKFLOW_TIMED_OUT") return;
|
|
1666
|
+
const failure = last.payload["failure"] ?? { errorType: "TIMEOUT", message: "timed out", timeoutType: "WORKFLOW_RUN" };
|
|
1667
|
+
const decision = chunkTYF56RGB_cjs.computeRunRetryDelay(run.config.retry, run.config.runAttempt, failure);
|
|
1668
|
+
if (!decision.retry) return;
|
|
1669
|
+
const config = {
|
|
1670
|
+
...this.chainConfig(run.config),
|
|
1671
|
+
runAttempt: run.config.runAttempt + 1,
|
|
1672
|
+
lastFailure: failure
|
|
1673
|
+
};
|
|
1674
|
+
if (decision.delayMs > 0) config.firstTaskDelayMs = decision.delayMs;
|
|
1675
|
+
this.startSuccessor(run, run.state.input ?? null, config);
|
|
1676
|
+
}
|
|
1677
|
+
/**
|
|
1678
|
+
* A run closed: drop its pending work, retry it, apply the parent-close
|
|
1679
|
+
* policy to its children (except `keptChildren`, which follow a reset
|
|
1680
|
+
* fork), settle its open updates and notify its parent.
|
|
1681
|
+
*/
|
|
1682
|
+
onClosed(run, keptChildren = /* @__PURE__ */ new Set()) {
|
|
716
1683
|
this.timers = this.timers.filter((timer) => timer.runId !== run.runId);
|
|
717
1684
|
this.attempts = this.attempts.filter((entry) => entry.runId !== run.runId);
|
|
1685
|
+
this.dues = this.dues.filter((entry) => entry.runId !== run.runId);
|
|
1686
|
+
for (const [token, entry] of this.asyncAttempts)
|
|
1687
|
+
if (entry.runId === run.runId) this.asyncAttempts.delete(token);
|
|
1688
|
+
this.retryRun(run);
|
|
718
1689
|
for (const [childId2, childRunId] of run.children) {
|
|
1690
|
+
if (keptChildren.has(childId2)) continue;
|
|
719
1691
|
const child = run.state.children[childId2];
|
|
720
|
-
if (!child || !
|
|
1692
|
+
if (!child || !chunkISOIA7NV_cjs.isChildOpen(child) || childRunId === void 0) continue;
|
|
721
1693
|
if (child.parentClosePolicy === "TERMINATE") this.terminate(childRunId, "parent closed");
|
|
722
1694
|
else if (child.parentClosePolicy === "REQUEST_CANCEL")
|
|
723
1695
|
this.requestCancel(childRunId, "parent closed");
|
|
724
1696
|
}
|
|
1697
|
+
for (const update of run.updates.values()) {
|
|
1698
|
+
if (update.status !== "PENDING" && update.status !== "ACCEPTED" && update.status !== "ADMITTED")
|
|
1699
|
+
continue;
|
|
1700
|
+
const accepted = update.status === "ACCEPTED";
|
|
1701
|
+
update.status = accepted ? "COMPLETED" : "REJECTED";
|
|
1702
|
+
update.failure = {
|
|
1703
|
+
errorType: accepted ? "PERMANENT" : "VALIDATION",
|
|
1704
|
+
type: "RunClosed",
|
|
1705
|
+
message: `The run closed before update "${update.name}" ${accepted ? "completed" : "was accepted"}`
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
725
1708
|
if (!run.parent) return;
|
|
726
1709
|
const parent = this.runs.get(run.parent.runId);
|
|
727
1710
|
if (!parent || parent.state.status !== "RUNNING") return;
|
|
728
1711
|
const childId = run.parent.childId;
|
|
729
|
-
if (!parent.state.children[childId] || !
|
|
1712
|
+
if (!parent.state.children[childId] || !chunkISOIA7NV_cjs.isChildOpen(parent.state.children[childId])) return;
|
|
730
1713
|
const last = run.history[run.history.length - 1];
|
|
731
1714
|
if (!last) return;
|
|
1715
|
+
if (run.resetTo !== void 0) {
|
|
1716
|
+
parent.children.set(childId, run.resetTo);
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
732
1719
|
if (run.continuedTo !== void 0) {
|
|
733
1720
|
const next = this.runs.get(run.continuedTo);
|
|
734
1721
|
if (next) {
|
|
@@ -769,26 +1756,41 @@ var TestWorkflowEnvironment = class _TestWorkflowEnvironment {
|
|
|
769
1756
|
default:
|
|
770
1757
|
this.append(parent, {
|
|
771
1758
|
type: "CHILD_RUN_TIMED_OUT",
|
|
772
|
-
payload: {
|
|
1759
|
+
payload: {
|
|
1760
|
+
...base,
|
|
1761
|
+
timeoutType: last.payload["timeoutType"] === "WORKFLOW_EXECUTION" ? "WORKFLOW_EXECUTION" : "WORKFLOW_RUN"
|
|
1762
|
+
}
|
|
773
1763
|
});
|
|
774
1764
|
}
|
|
775
1765
|
}
|
|
776
1766
|
async resultOf(runId) {
|
|
777
1767
|
await this.runUntilIdle(this.timeSkipping);
|
|
778
1768
|
let run = this.run(runId);
|
|
779
|
-
while (run.continuedTo !== void 0
|
|
1769
|
+
while (run.continuedTo !== void 0 || run.resetTo !== void 0)
|
|
1770
|
+
run = this.run(run.continuedTo ?? run.resetTo ?? run.runId);
|
|
780
1771
|
if (run.taskFailure) throw new WorkflowTaskFailedError(run.runId, run.taskFailure);
|
|
1772
|
+
if (this.failOnUnfinishedHandlers && run.unfinishedHandlers !== void 0)
|
|
1773
|
+
throw new UnfinishedHandlersError(run.runId, run.unfinishedHandlers);
|
|
781
1774
|
const last = run.history[run.history.length - 1];
|
|
782
1775
|
switch (run.state.status) {
|
|
783
1776
|
case "COMPLETED":
|
|
784
1777
|
return run.state.output ?? null;
|
|
1778
|
+
case "TIMED_OUT":
|
|
1779
|
+
throw new WorkflowFailedError(
|
|
1780
|
+
run.runId,
|
|
1781
|
+
last?.payload["failure"] ?? {
|
|
1782
|
+
errorType: "TIMEOUT",
|
|
1783
|
+
message: "timed out",
|
|
1784
|
+
type: "TimeoutFailure"
|
|
1785
|
+
}
|
|
1786
|
+
);
|
|
785
1787
|
case "FAILED":
|
|
786
1788
|
throw new WorkflowFailedError(
|
|
787
1789
|
run.runId,
|
|
788
1790
|
run.state.failure ?? { errorType: "UNKNOWN", message: "failed" }
|
|
789
1791
|
);
|
|
790
1792
|
case "CANCELLED":
|
|
791
|
-
throw new
|
|
1793
|
+
throw new chunkZCGKGDCL_cjs.CancelledFailure(`Run ${run.runId} was cancelled`);
|
|
792
1794
|
case "TERMINATED":
|
|
793
1795
|
throw new WorkflowFailedError(run.runId, {
|
|
794
1796
|
errorType: "PERMANENT",
|
|
@@ -819,7 +1821,7 @@ var ReplayError = class extends Error {
|
|
|
819
1821
|
function replayRequest(workflowFn, history, info = {}, nowMs) {
|
|
820
1822
|
const last = history[history.length - 1];
|
|
821
1823
|
return {
|
|
822
|
-
protocol:
|
|
1824
|
+
protocol: chunkTYF56RGB_cjs.HISTORY_PROTOCOL,
|
|
823
1825
|
kind: "verify",
|
|
824
1826
|
workflowFn,
|
|
825
1827
|
info: { runId: "replay", workspaceId: "replay", ...info },
|
|
@@ -829,14 +1831,14 @@ function replayRequest(workflowFn, history, info = {}, nowMs) {
|
|
|
829
1831
|
}
|
|
830
1832
|
function eventsOf(input) {
|
|
831
1833
|
if (Array.isArray(input)) return { events: input };
|
|
832
|
-
const file = typeof input === "string" ?
|
|
1834
|
+
const file = typeof input === "string" ? chunkHMZUGSMU_cjs.parseHistoryFile(input) : input;
|
|
833
1835
|
return { events: file.events, file };
|
|
834
1836
|
}
|
|
835
1837
|
async function replay(workflows, input, options = {}) {
|
|
836
1838
|
const { events, file } = eventsOf(input);
|
|
837
1839
|
const started = events[0]?.type === "WORKFLOW_STARTED" ? events[0].payload["workflowFn"] : void 0;
|
|
838
1840
|
const recordedFn = options.workflowFn ?? file?.workflowFn ?? (typeof started === "string" ? started : void 0);
|
|
839
|
-
const map =
|
|
1841
|
+
const map = chunkVA4OFR72_cjs.isDurableWorkflow(workflows) ? { [recordedFn ?? workflows.name]: workflows } : { ...workflows };
|
|
840
1842
|
const workflowFn = recordedFn ?? Object.keys(map)[0] ?? "";
|
|
841
1843
|
const result = await runWorkflowTaskInProcess(
|
|
842
1844
|
registryOf(map),
|
|
@@ -849,7 +1851,7 @@ async function replay(workflows, input, options = {}) {
|
|
|
849
1851
|
);
|
|
850
1852
|
if (result.outcome === "failed") {
|
|
851
1853
|
if (result.failure?.cause === "NON_DETERMINISM")
|
|
852
|
-
throw new
|
|
1854
|
+
throw new chunkZCGKGDCL_cjs.DeterminismViolation(result.failure.message);
|
|
853
1855
|
throw new ReplayError(result);
|
|
854
1856
|
}
|
|
855
1857
|
return result;
|
|
@@ -898,16 +1900,16 @@ function assertHistory(history, expected, options = {}) {
|
|
|
898
1900
|
);
|
|
899
1901
|
}
|
|
900
1902
|
function definitionOf(value) {
|
|
901
|
-
return
|
|
1903
|
+
return chunk54FLQNUM_cjs.normalizeDefinition(chunkIQPXHQQQ_cjs.isTier1Workflow(value) ? chunkIQPXHQQQ_cjs.toIR(value) : value);
|
|
902
1904
|
}
|
|
903
1905
|
function expectIR(value) {
|
|
904
1906
|
const definition = definitionOf(value);
|
|
905
|
-
const hash =
|
|
1907
|
+
const hash = chunk54FLQNUM_cjs.irHash(definition);
|
|
906
1908
|
return {
|
|
907
1909
|
definition,
|
|
908
1910
|
irHash: hash,
|
|
909
1911
|
toEqualIR(other) {
|
|
910
|
-
const otherHash =
|
|
1912
|
+
const otherHash = chunk54FLQNUM_cjs.irHash(definitionOf(other));
|
|
911
1913
|
if (otherHash !== hash)
|
|
912
1914
|
throw new HistoryAssertionError(`The IR differs: ${hash} versus ${otherHash}`);
|
|
913
1915
|
}
|
|
@@ -950,7 +1952,7 @@ async function simulate(definition, options = {}) {
|
|
|
950
1952
|
output = produced.output;
|
|
951
1953
|
handle = produced.handle;
|
|
952
1954
|
} else output = produced;
|
|
953
|
-
} else if (node.type ===
|
|
1955
|
+
} else if (node.type === chunk54FLQNUM_cjs.CODE_WORKFLOW_NODE_TYPE) {
|
|
954
1956
|
const state = node.args?.state;
|
|
955
1957
|
const workflowFn = chunkSAENRNBJ_cjs.isRecord(state) && typeof state["workflowFn"] === "string" ? state["workflowFn"] : "";
|
|
956
1958
|
const fn = options.workflows?.[workflowFn];
|
|
@@ -1018,30 +2020,35 @@ async function runWorkflowTaskInVm(script, request, options = {}) {
|
|
|
1018
2020
|
await settle();
|
|
1019
2021
|
if (step !== "more") break;
|
|
1020
2022
|
if (Date.now() > deadline)
|
|
1021
|
-
return
|
|
2023
|
+
return chunk672KMMBK_cjs.failedTaskResult(
|
|
1022
2024
|
"TASK_TIMEOUT",
|
|
1023
2025
|
`The workflow task ran past its ${timeout} ms budget`
|
|
1024
2026
|
);
|
|
1025
2027
|
}
|
|
1026
2028
|
const json = run("globalThis.__fluidgridsRuntime.complete()", "complete.js");
|
|
1027
2029
|
if (typeof json !== "string")
|
|
1028
|
-
return
|
|
2030
|
+
return chunk672KMMBK_cjs.failedTaskResult("INTERNAL", "The runtime returned no result");
|
|
1029
2031
|
return JSON.parse(json);
|
|
1030
2032
|
} catch (error) {
|
|
1031
2033
|
const fields = typeof error === "object" && error !== null ? error : {};
|
|
1032
2034
|
const message = typeof fields.message === "string" ? fields.message : String(error);
|
|
1033
2035
|
const name = typeof fields.name === "string" ? fields.name : "";
|
|
1034
|
-
if (/timed out/i.test(message)) return
|
|
1035
|
-
if (name === "DeterminismViolation") return
|
|
1036
|
-
return
|
|
2036
|
+
if (/timed out/i.test(message)) return chunk672KMMBK_cjs.failedTaskResult("TASK_TIMEOUT", message);
|
|
2037
|
+
if (name === "DeterminismViolation") return chunk672KMMBK_cjs.failedTaskResult("NON_DETERMINISM", message);
|
|
2038
|
+
return chunk672KMMBK_cjs.failedTaskResult("WORKFLOW_CODE_ERROR", `${name || "Error"}: ${message}`);
|
|
1037
2039
|
}
|
|
1038
2040
|
}
|
|
1039
2041
|
|
|
1040
2042
|
exports.HistoryAssertionError = HistoryAssertionError;
|
|
2043
|
+
exports.QueryFailedError = QueryFailedError;
|
|
2044
|
+
exports.QueryRejectedError = QueryRejectedError;
|
|
1041
2045
|
exports.ReplayError = ReplayError;
|
|
1042
2046
|
exports.RunBlockedError = RunBlockedError;
|
|
1043
2047
|
exports.TestRunHandle = TestRunHandle;
|
|
1044
2048
|
exports.TestWorkflowEnvironment = TestWorkflowEnvironment;
|
|
2049
|
+
exports.UnfinishedHandlersError = UnfinishedHandlersError;
|
|
2050
|
+
exports.UpdateRefusedError = UpdateRefusedError;
|
|
2051
|
+
exports.WorkflowAlreadyStartedError = WorkflowAlreadyStartedError;
|
|
1045
2052
|
exports.WorkflowFailedError = WorkflowFailedError;
|
|
1046
2053
|
exports.WorkflowTaskFailedError = WorkflowTaskFailedError;
|
|
1047
2054
|
exports.assertHistory = assertHistory;
|