@fluidgrids/workflows 2026.1029.1 → 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 +112 -4
- package/dist/{activation-CESehfYi.d.cts → activation-DqUfpHzk.d.cts} +1 -1
- package/dist/{activation-BnVQMv9y.d.ts → activation-xpx1oWuo.d.ts} +1 -1
- package/dist/{chunk-MJ5TGYVG.js → chunk-2D3MYIIF.js} +969 -153
- package/dist/{chunk-2B37XAFQ.cjs → chunk-3U2NU7YC.cjs} +24 -11
- package/dist/{chunk-NEIUQASE.cjs → chunk-4QKB4ARC.cjs} +3 -3
- package/dist/{chunk-GBQ33SIZ.cjs → chunk-54FLQNUM.cjs} +12 -1
- package/dist/{chunk-2DDULZFM.js → chunk-5HIVIV53.js} +86 -75
- package/dist/chunk-5MFVJM76.js +1222 -0
- package/dist/{chunk-EWYADREB.cjs → chunk-672KMMBK.cjs} +2 -2
- package/dist/{chunk-KXJDRQ6G.js → chunk-6A2FGSUS.js} +22 -2
- package/dist/{chunk-OIX4O6UR.js → chunk-6OLN6BMB.js} +16 -3
- package/dist/{chunk-6FSHT5IL.js → chunk-7EZIANC7.js} +1 -1
- package/dist/chunk-A5SXCA62.cjs +93 -0
- package/dist/chunk-AC6ZEXVR.js +85 -0
- package/dist/{chunk-FWBRQWRX.js → chunk-AE5Q6ZAJ.js} +167 -10
- package/dist/{chunk-7ILGCUI6.js → chunk-C4F5KJTI.js} +142 -3
- package/dist/{chunk-WXXNCHUY.js → chunk-G5H32GIG.js} +1 -1
- package/dist/{chunk-OMY3CKUZ.cjs → chunk-GEQDGUGB.cjs} +1042 -226
- package/dist/{chunk-25BKWR2O.js → chunk-HCM7ZC7Q.js} +1 -1
- package/dist/{chunk-I47EENR4.cjs → chunk-HMZUGSMU.cjs} +673 -156
- package/dist/{chunk-NXFBVDOI.cjs → chunk-IQPXHQQQ.cjs} +8 -4
- package/dist/{chunk-RUQIMTN5.cjs → chunk-ISOIA7NV.cjs} +178 -15
- package/dist/chunk-JSC75O5C.cjs +167 -0
- package/dist/{chunk-BD7ZPG53.js → chunk-NWQ5WRLE.js} +10 -2
- package/dist/chunk-ODZ2IAR2.cjs +1247 -0
- package/dist/{chunk-FQSJI5NV.js → chunk-SR7TR5KX.js} +6 -2
- package/dist/{chunk-OKX3FYIM.cjs → chunk-TCTE7DX5.cjs} +3 -3
- package/dist/{chunk-3FIDHONL.cjs → chunk-TYF56RGB.cjs} +99 -80
- package/dist/{chunk-J3PYFGKA.cjs → chunk-VA4OFR72.cjs} +162 -4
- package/dist/chunk-VU5S5A7V.js +150 -0
- package/dist/{chunk-A36CCOWS.js → chunk-YEHR4NNE.js} +633 -128
- package/dist/{chunk-ELL2RBML.cjs → chunk-ZCGKGDCL.cjs} +23 -1
- package/dist/codegen/index.d.cts +2 -2
- package/dist/codegen/index.d.ts +2 -2
- package/dist/{collect-BTHjQaSp.d.ts → collect-6NIEufHd.d.ts} +20 -3
- package/dist/{collect-CSyOg5XC.d.cts → collect-CcTCKqx6.d.cts} +20 -3
- package/dist/{file-cgL9l9l8.d.cts → file-BxYpwjRQ.d.cts} +250 -15
- package/dist/{file-olQudcx5.d.ts → file-CFdPQJI9.d.ts} +250 -15
- package/dist/history/index.cjs +283 -106
- package/dist/history/index.d.cts +549 -10
- package/dist/history/index.d.ts +549 -10
- package/dist/history/index.js +5 -4
- package/dist/index.cjs +146 -53
- package/dist/index.d.cts +38 -14
- package/dist/index.d.ts +38 -14
- package/dist/index.js +6 -5
- package/dist/interceptors-CNcr2dlk.d.ts +159 -0
- package/dist/interceptors-CqkNZVfI.d.cts +159 -0
- package/dist/ir/index.cjs +339 -36
- package/dist/ir/index.d.cts +244 -8
- package/dist/ir/index.d.ts +244 -8
- package/dist/ir/index.js +306 -21
- package/dist/{lower-3S6PsqjJ.d.cts → lower-BtRfIW2A.d.cts} +23 -9
- package/dist/{lower-DmZpQ8hJ.d.ts → lower-CpsMFOFX.d.ts} +23 -9
- package/dist/{manifest-BJjrQt77.d.ts → manifest-D-I_twys.d.ts} +1 -1
- package/dist/{manifest-gRI2Q5Fj.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 +27 -27
- package/dist/quickjs/index.d.cts +6 -6
- package/dist/quickjs/index.d.ts +6 -6
- package/dist/quickjs/index.js +9 -9
- package/dist/roundtrip/index.cjs +72 -66
- package/dist/roundtrip/index.d.cts +25 -11
- package/dist/roundtrip/index.d.ts +25 -11
- package/dist/roundtrip/index.js +13 -11
- package/dist/runtime/index.cjs +19 -19
- package/dist/runtime/index.d.cts +139 -12
- package/dist/runtime/index.d.ts +139 -12
- package/dist/runtime/index.js +6 -6
- package/dist/testing/index.cjs +688 -121
- package/dist/testing/index.d.cts +174 -10
- package/dist/testing/index.d.ts +174 -10
- package/dist/testing/index.js +646 -82
- package/dist/{types-CnIOinoz.d.ts → types-Bv2i4cBX.d.cts} +126 -23
- package/dist/{types-CQKOhPTa.d.cts → types-DGx-b9eq.d.ts} +126 -23
- package/dist/{types-Bb2wRnJ5.d.cts → types-V7jEEUaL.d.cts} +1 -1
- package/dist/{types-Bb2wRnJ5.d.ts → types-V7jEEUaL.d.ts} +1 -1
- package/dist/{workflow-8uRU9rhL.d.ts → workflow-B18OeF7g.d.ts} +2 -2
- package/dist/{workflow-DWTPHFbt.d.ts → workflow-BGnv8SS6.d.ts} +261 -14
- package/dist/{workflow-CZOXUlQ5.d.cts → workflow-BJPFv3cp.d.cts} +2 -2
- package/dist/{workflow-O6e64ZcG.d.cts → workflow-DS32Gtws.d.cts} +261 -14
- package/fixtures/histories/activity-options-updated.history.json +159 -0
- package/fixtures/histories/activity-paused-unpaused.history.json +160 -0
- package/fixtures/histories/codec-opaque.history.json +229 -0
- package/fixtures/histories/dynamic-workflow.history.json +150 -0
- package/fixtures/histories/interceptor-headers.history.json +339 -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/search-attributes-upserted.history.json +177 -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 +255 -81
- package/schema/ir-v2.schema.json +246 -1
- package/schema/payload-fields.json +162 -0
- package/schema/run-event.schema.json +239 -80
- package/schema/workflow-command.schema.json +210 -84
- package/schema/workflow-task.schema.json +532 -167
- package/dist/chunk-6QPOJZT6.cjs +0 -1180
- package/dist/chunk-A2OHX3UQ.js +0 -1156
- package/dist/chunk-PWIUEWC3.js +0 -81
- package/dist/chunk-YHBVMOMK.cjs +0 -87
- package/dist/node-evaluator-SOADN23V.js +0 -14
- package/dist/node-evaluator-YGDEXK4U.cjs +0 -24
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { ServerFailure, failureToError, CancelledFailure, TimeoutFailure, ApplicationError, TerminatedFailure, DeterminismViolation, HistoryLimitExceeded, PhaseCFeatureError, isCancellation,
|
|
1
|
+
import { QueryHandlerSideEffect, ServerFailure, failureToError, CancelledFailure, TimeoutFailure, ApplicationError, TerminatedFailure, errorToFailure, isRunFailure, DeterminismViolation, HistoryLimitExceeded, PhaseCFeatureError, isCancellation, ActivityFailure, signalName, ChildRunFailure } from './chunk-6A2FGSUS.js';
|
|
2
2
|
import { captureStack, parseStack } from './chunk-M272AHEX.js';
|
|
3
3
|
import { createRandom, seedOf, takeShimControl, uuidFrom } from './chunk-4LYSEANA.js';
|
|
4
|
-
import { optionalMs, toMs, isDurableWorkflow, runRetryPolicyOf, continueAsNewRetryOf, SDK_VERSION } from './chunk-
|
|
5
|
-
import { utf8Bytes, splitActivations, CHILD_DEFAULT_CANCELLATION_TYPE, CHILD_DEFAULT_PARENT_CLOSE_POLICY } from './chunk-
|
|
6
|
-
import {
|
|
7
|
-
import { explicitRetryPolicy, computeRetryDelay } from './chunk-
|
|
4
|
+
import { resolveWorkflowInterceptors, compose, optionalMs, isActivityDefinition, checkActivityName, checkTaskQueueName, toMs, isDurableWorkflow, runRetryPolicyOf, continueAsNewRetryOf, interceptorsFactoryOf, SDK_VERSION } from './chunk-C4F5KJTI.js';
|
|
5
|
+
import { utf8Bytes, splitActivations, CHILD_DEFAULT_CANCELLATION_TYPE, CHILD_DEFAULT_PARENT_CLOSE_POLICY, mergeSearchAttributes } from './chunk-AE5Q6ZAJ.js';
|
|
6
|
+
import { isActivityNodeTypeAllowed } from './chunk-AC6ZEXVR.js';
|
|
7
|
+
import { COMMAND_EVENT_TYPES, DEFAULT_HISTORY_LIMIT, CONTINUE_AS_NEW_SUGGESTED_RATIO, HISTORY_PROTOCOL, HISTORY_LIMIT_EXCEEDED_RATIO, explicitRetryPolicy, MAX_ACTIVITY_CONNECTIONS, LOCAL_ACTIVITY_MAX_TIMEOUT_MS, MAX_SINK_CALLS, RUNTIME_CAPABILITIES, computeRetryDelay, FAIRNESS_KEY_MAX_LENGTH, FAIRNESS_WEIGHT_MIN, FAIRNESS_WEIGHT_MAX, BUILT_IN_QUERIES } from './chunk-5HIVIV53.js';
|
|
8
8
|
|
|
9
9
|
// src/runtime/ids.ts
|
|
10
10
|
var CommandIds = class {
|
|
@@ -37,7 +37,7 @@ function field(event, key) {
|
|
|
37
37
|
function describeCommand(command) {
|
|
38
38
|
switch (command.type) {
|
|
39
39
|
case "SCHEDULE_ACTIVITY":
|
|
40
|
-
return `SCHEDULE_ACTIVITY ${command.activityId} (${command.nodeType} ${command.operation})`;
|
|
40
|
+
return command.kind === "user" ? `SCHEDULE_ACTIVITY ${command.activityId} (user ${command.activityName})` : `SCHEDULE_ACTIVITY ${command.activityId} (${command.nodeType} ${command.operation})`;
|
|
41
41
|
case "REQUEST_CANCEL_ACTIVITY":
|
|
42
42
|
return `REQUEST_CANCEL_ACTIVITY ${command.activityId}`;
|
|
43
43
|
case "START_TIMER":
|
|
@@ -55,12 +55,16 @@ function describeCommand(command) {
|
|
|
55
55
|
return `RECORD_MARKER ${command.markerId}`;
|
|
56
56
|
case "RECORD_LOCAL_ACTIVITY":
|
|
57
57
|
return `RECORD_LOCAL_ACTIVITY ${command.localActivityId}${command.nodeType !== void 0 ? ` (${command.nodeType} ${command.operation ?? ""})` : ""}`;
|
|
58
|
+
case "ACCEPT_UPDATE":
|
|
59
|
+
case "REJECT_UPDATE":
|
|
60
|
+
case "COMPLETE_UPDATE":
|
|
61
|
+
return `${command.type} ${command.updateId}`;
|
|
58
62
|
default:
|
|
59
63
|
return command.type;
|
|
60
64
|
}
|
|
61
65
|
}
|
|
62
66
|
function describeEvent(event) {
|
|
63
|
-
const id = field(event, "activityId") ?? field(event, "timerId") ?? field(event, "childId") ?? field(event, "externalId") ?? field(event, "sideEffectId") ?? field(event, "markerId") ?? field(event, "localActivityId");
|
|
67
|
+
const id = field(event, "activityId") ?? field(event, "timerId") ?? field(event, "childId") ?? field(event, "externalId") ?? field(event, "sideEffectId") ?? field(event, "markerId") ?? field(event, "localActivityId") ?? field(event, "updateId");
|
|
64
68
|
return `${event.type}${typeof id === "string" ? ` ${id}` : ""} (seq ${event.seq})`;
|
|
65
69
|
}
|
|
66
70
|
function mismatch(command, event) {
|
|
@@ -69,8 +73,13 @@ function mismatch(command, event) {
|
|
|
69
73
|
const differ = () => `the code issued ${describeCommand(command)} where the history has ${describeEvent(event)}`;
|
|
70
74
|
if (!typeOk) return differ();
|
|
71
75
|
switch (command.type) {
|
|
72
|
-
case "SCHEDULE_ACTIVITY":
|
|
73
|
-
|
|
76
|
+
case "SCHEDULE_ACTIVITY": {
|
|
77
|
+
if (field(event, "activityId") !== command.activityId) return differ();
|
|
78
|
+
const kind = field(event, "kind") ?? "node";
|
|
79
|
+
if (command.kind === "user")
|
|
80
|
+
return kind === "user" && field(event, "activityName") === command.activityName ? void 0 : differ();
|
|
81
|
+
return kind === "node" && field(event, "nodeType") === command.nodeType && field(event, "operation") === command.operation ? void 0 : differ();
|
|
82
|
+
}
|
|
74
83
|
case "REQUEST_CANCEL_ACTIVITY":
|
|
75
84
|
return field(event, "activityId") === command.activityId ? void 0 : differ();
|
|
76
85
|
case "START_TIMER":
|
|
@@ -86,6 +95,9 @@ function mismatch(command, event) {
|
|
|
86
95
|
return field(event, "sideEffectId") === command.sideEffectId ? void 0 : differ();
|
|
87
96
|
case "RECORD_MARKER":
|
|
88
97
|
return field(event, "markerId") === command.markerId ? void 0 : differ();
|
|
98
|
+
case "ACCEPT_UPDATE":
|
|
99
|
+
case "COMPLETE_UPDATE":
|
|
100
|
+
return field(event, "updateId") === command.updateId ? void 0 : differ();
|
|
89
101
|
case "RECORD_LOCAL_ACTIVITY": {
|
|
90
102
|
const sameOperation = field(event, "nodeType") === void 0 || field(event, "nodeType") === command.nodeType && field(event, "operation") === command.operation;
|
|
91
103
|
return field(event, "localActivityId") === command.localActivityId && sameOperation ? void 0 : differ();
|
|
@@ -101,6 +113,7 @@ function isSkippablePatch(event) {
|
|
|
101
113
|
function matchActivation(commands, recorded) {
|
|
102
114
|
let position = 0;
|
|
103
115
|
for (const command of commands) {
|
|
116
|
+
if (COMMAND_EVENT_TYPES[command.type] === null) continue;
|
|
104
117
|
let event = recorded[position];
|
|
105
118
|
while (event !== void 0 && isSkippablePatch(event) && !(command.type === "RECORD_MARKER" && command.markerId === field(event, "markerId"))) {
|
|
106
119
|
position += 1;
|
|
@@ -126,15 +139,35 @@ function matchActivation(commands, recorded) {
|
|
|
126
139
|
}
|
|
127
140
|
|
|
128
141
|
// src/runtime/scheduler.ts
|
|
142
|
+
var COMMAND_ID_CALLS = {
|
|
143
|
+
a: "Scheduling an activity",
|
|
144
|
+
t: "Starting a timer",
|
|
145
|
+
c: "Starting a child run",
|
|
146
|
+
s: "ctx.sideEffect()",
|
|
147
|
+
x: "Signalling or cancelling another run",
|
|
148
|
+
m: "ctx.setCurrentDetails()",
|
|
149
|
+
l: "Running a local activity"
|
|
150
|
+
};
|
|
151
|
+
function isBuiltInQuery(name) {
|
|
152
|
+
return BUILT_IN_QUERIES.includes(name);
|
|
153
|
+
}
|
|
129
154
|
var SDK_NAME = "@fluidgrids/workflows";
|
|
130
155
|
var ContinueAsNewRequest = class {
|
|
131
|
-
constructor(input, options) {
|
|
156
|
+
constructor(input, options, headers = {}) {
|
|
132
157
|
this.input = input;
|
|
133
158
|
this.options = options;
|
|
159
|
+
this.headers = headers;
|
|
134
160
|
}
|
|
135
161
|
input;
|
|
136
162
|
options;
|
|
163
|
+
headers;
|
|
137
164
|
};
|
|
165
|
+
function checkPolicy(policy) {
|
|
166
|
+
const value = policy ?? "WARN_AND_ABANDON";
|
|
167
|
+
if (value !== "WARN_AND_ABANDON" && value !== "ABANDON")
|
|
168
|
+
throw new TypeError("unfinishedPolicy must be WARN_AND_ABANDON or ABANDON");
|
|
169
|
+
return value;
|
|
170
|
+
}
|
|
138
171
|
var TaskFailure = class {
|
|
139
172
|
constructor(cause, message, stackTrace, seq, errorType) {
|
|
140
173
|
this.cause = cause;
|
|
@@ -209,6 +242,32 @@ var Scope = class {
|
|
|
209
242
|
this.parent?.children.delete(this);
|
|
210
243
|
}
|
|
211
244
|
};
|
|
245
|
+
function checkHandlerName(name, what) {
|
|
246
|
+
if (typeof name !== "string" || name === "" || name.startsWith("__"))
|
|
247
|
+
throw new TypeError(`${what} needs a name that does not start with "__"`);
|
|
248
|
+
return name;
|
|
249
|
+
}
|
|
250
|
+
function definitionName(value, what) {
|
|
251
|
+
return checkHandlerName(typeof value === "string" ? value : value?.name, what);
|
|
252
|
+
}
|
|
253
|
+
function isHeaders(value) {
|
|
254
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((entry) => typeof entry === "string");
|
|
255
|
+
}
|
|
256
|
+
function headersOf(value) {
|
|
257
|
+
return isHeaders(value) ? { ...value } : {};
|
|
258
|
+
}
|
|
259
|
+
function checkFairness(options) {
|
|
260
|
+
const key = options.fairnessKey;
|
|
261
|
+
if (key !== void 0 && (typeof key !== "string" || key === "" || key.length > FAIRNESS_KEY_MAX_LENGTH))
|
|
262
|
+
throw new TypeError(
|
|
263
|
+
`fairnessKey must be a string of 1 to ${FAIRNESS_KEY_MAX_LENGTH} characters`
|
|
264
|
+
);
|
|
265
|
+
const weight = options.fairnessWeight;
|
|
266
|
+
if (weight !== void 0 && (typeof weight !== "number" || !Number.isFinite(weight) || weight < FAIRNESS_WEIGHT_MIN || weight > FAIRNESS_WEIGHT_MAX))
|
|
267
|
+
throw new TypeError(
|
|
268
|
+
`fairnessWeight must be a number from ${FAIRNESS_WEIGHT_MIN} to ${FAIRNESS_WEIGHT_MAX}`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
212
271
|
function toJson(value, what) {
|
|
213
272
|
if (value === void 0) return null;
|
|
214
273
|
if (typeof value === "function" || typeof value === "symbol")
|
|
@@ -277,6 +336,11 @@ function infoFromStarted(info, started) {
|
|
|
277
336
|
if (typeof p["priority"] === "number") out.priority = p["priority"];
|
|
278
337
|
if (typeof p["firstRunId"] === "string") out.firstRunId = p["firstRunId"];
|
|
279
338
|
if (typeof p["continuedFromRunId"] === "string") out.continuedFromRunId = p["continuedFromRunId"];
|
|
339
|
+
if (isHeaders(p["headers"])) out.headers = { ...p["headers"] };
|
|
340
|
+
if (isJsonObject(p["searchAttributes"]))
|
|
341
|
+
out.searchAttributes = mergeSearchAttributes({}, p["searchAttributes"]);
|
|
342
|
+
if (typeof p["fairnessKey"] === "string") out.fairnessKey = p["fairnessKey"];
|
|
343
|
+
if (typeof p["fairnessWeight"] === "number") out.fairnessWeight = p["fairnessWeight"];
|
|
280
344
|
if (out.workflowId === void 0 && out.businessId !== void 0) out.workflowId = out.businessId;
|
|
281
345
|
if (out.runAttempt === void 0 && out.attempt !== void 0) out.runAttempt = out.attempt;
|
|
282
346
|
return out;
|
|
@@ -291,7 +355,7 @@ var WorkflowExecution = class {
|
|
|
291
355
|
activationOpen = false;
|
|
292
356
|
replaying = false;
|
|
293
357
|
clock = 0;
|
|
294
|
-
|
|
358
|
+
randomSource;
|
|
295
359
|
ids = new CommandIds();
|
|
296
360
|
commands = [];
|
|
297
361
|
newCommands = [];
|
|
@@ -336,6 +400,24 @@ var WorkflowExecution = class {
|
|
|
336
400
|
/** Running asynchronous handlers by name, for the drain warning (11 §2.18). */
|
|
337
401
|
running = /* @__PURE__ */ new Map();
|
|
338
402
|
unfinished;
|
|
403
|
+
/** Updates delivered so far, by id (an update is delivered at most once per task). */
|
|
404
|
+
deliveredUpdates = /* @__PURE__ */ new Set();
|
|
405
|
+
/** Updates delivered in the current activation without a handler yet. */
|
|
406
|
+
bufferedUpdates = [];
|
|
407
|
+
updateHandlers = /* @__PURE__ */ new Map();
|
|
408
|
+
defaultUpdateHandler;
|
|
409
|
+
queryHandlers = /* @__PURE__ */ new Map();
|
|
410
|
+
defaultQueryHandler;
|
|
411
|
+
/** Set while a query handler or an update validator runs: commands and sinks throw. */
|
|
412
|
+
readOnly;
|
|
413
|
+
/** The first state-changing call a read-only handler made, even when it caught the error. */
|
|
414
|
+
readOnlyViolation;
|
|
415
|
+
searchAttributes;
|
|
416
|
+
startHeaders;
|
|
417
|
+
inbound;
|
|
418
|
+
outbound;
|
|
419
|
+
sinkCalls = [];
|
|
420
|
+
sinkCallsDropped = false;
|
|
339
421
|
recordedLocalActivities = /* @__PURE__ */ new Map();
|
|
340
422
|
localActivityResults = /* @__PURE__ */ new Map();
|
|
341
423
|
localActivities = /* @__PURE__ */ new Map();
|
|
@@ -362,7 +444,7 @@ var WorkflowExecution = class {
|
|
|
362
444
|
this.limitEvents = limit.events;
|
|
363
445
|
this.limitBytes = limit.bytes;
|
|
364
446
|
this.captureStacks = input.request.kind === "query";
|
|
365
|
-
this.
|
|
447
|
+
this.randomSource = createRandom(seedOf(randomnessSeedOf(input.request)));
|
|
366
448
|
for (const event of history) {
|
|
367
449
|
if (event.type === "SIDE_EFFECT_RECORDED") {
|
|
368
450
|
const id = event.payload["sideEffectId"];
|
|
@@ -396,6 +478,30 @@ var WorkflowExecution = class {
|
|
|
396
478
|
const execution = this;
|
|
397
479
|
const snapshot = infoFromStarted(input.request.info, history[0]);
|
|
398
480
|
this.memo = { ...snapshot.memo ?? snapshot.context ?? {} };
|
|
481
|
+
this.searchAttributes = { ...snapshot.searchAttributes ?? {} };
|
|
482
|
+
this.startHeaders = { ...snapshot.headers ?? {} };
|
|
483
|
+
let inbound = [];
|
|
484
|
+
let outbound = [];
|
|
485
|
+
const factories = input.interceptors ?? [];
|
|
486
|
+
if (factories.length > 0 && input.request.kind === "task" && !(input.request.features ?? []).includes("interceptors"))
|
|
487
|
+
this.failTask(
|
|
488
|
+
"UNSUPPORTED_COMMAND",
|
|
489
|
+
"The bundle exports interceptors, which this workspace does not enable (needs the fluidgrids.run-control flag)"
|
|
490
|
+
);
|
|
491
|
+
else
|
|
492
|
+
try {
|
|
493
|
+
const resolved = resolveWorkflowInterceptors(factories);
|
|
494
|
+
inbound = resolved.inbound;
|
|
495
|
+
outbound = resolved.outbound;
|
|
496
|
+
} catch (error) {
|
|
497
|
+
this.failTask(
|
|
498
|
+
"WORKFLOW_CODE_ERROR",
|
|
499
|
+
`The interceptors factory failed: ${messageOf(error)}`,
|
|
500
|
+
stackTextOf(error)
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
this.inbound = inbound;
|
|
504
|
+
this.outbound = outbound;
|
|
399
505
|
this.info = {
|
|
400
506
|
...snapshot,
|
|
401
507
|
workflowFn: input.request.workflowFn,
|
|
@@ -405,6 +511,9 @@ var WorkflowExecution = class {
|
|
|
405
511
|
get memo() {
|
|
406
512
|
return { ...execution.memo };
|
|
407
513
|
},
|
|
514
|
+
get searchAttributes() {
|
|
515
|
+
return { ...execution.searchAttributes };
|
|
516
|
+
},
|
|
408
517
|
get historyLength() {
|
|
409
518
|
return execution.historyLength;
|
|
410
519
|
},
|
|
@@ -427,9 +536,33 @@ var WorkflowExecution = class {
|
|
|
427
536
|
now() {
|
|
428
537
|
return this.clock;
|
|
429
538
|
}
|
|
430
|
-
/** The next deterministic random number. */
|
|
539
|
+
/** The next deterministic random number (`Math.random()` and `crypto.getRandomValues()`). */
|
|
431
540
|
nextRandom() {
|
|
432
|
-
|
|
541
|
+
this.assertWritable("Math.random() or crypto.getRandomValues()");
|
|
542
|
+
return this.randomSource();
|
|
543
|
+
}
|
|
544
|
+
/** `ctx.random()`: advances the run's random source, so read-only handlers may not call it. */
|
|
545
|
+
random() {
|
|
546
|
+
this.assertWritable("ctx.random()");
|
|
547
|
+
return this.randomSource();
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Throw `QueryHandlerSideEffect` when a query handler or an update validator
|
|
551
|
+
* calls something that changes the run's deterministic state (the random
|
|
552
|
+
* source, the command id counters, patches, details). Validators are not run
|
|
553
|
+
* again on replay, and a worker that keeps a run's state between tasks keeps
|
|
554
|
+
* whatever a query handler changed, so either would make the next replay
|
|
555
|
+
* diverge. The violation is remembered, so catching the error does not hide it.
|
|
556
|
+
*/
|
|
557
|
+
assertWritable(what) {
|
|
558
|
+
if (this.readOnly === void 0) return;
|
|
559
|
+
this.readOnlyViolation ??= what;
|
|
560
|
+
throw new QueryHandlerSideEffect(`${what} (in ${this.readOnly})`);
|
|
561
|
+
}
|
|
562
|
+
/** The next command id of a kind; refused in read-only handlers (it advances a counter). */
|
|
563
|
+
nextId(kind) {
|
|
564
|
+
this.assertWritable(COMMAND_ID_CALLS[kind]);
|
|
565
|
+
return this.ids.next(kind);
|
|
433
566
|
}
|
|
434
567
|
/** Begin or finish an activation; `done` when there is nothing left to do. */
|
|
435
568
|
step() {
|
|
@@ -469,6 +602,8 @@ var WorkflowExecution = class {
|
|
|
469
602
|
logs: this.logs
|
|
470
603
|
};
|
|
471
604
|
if (!this.failure && this.unfinished !== void 0) result.unfinishedHandlers = this.unfinished;
|
|
605
|
+
if (!this.failure && this.request.kind === "task" && this.sinkCalls.length > 0)
|
|
606
|
+
result.sinkCalls = this.sinkCalls;
|
|
472
607
|
if (this.failure) {
|
|
473
608
|
result.failure = { cause: this.failure.cause, message: this.failure.message };
|
|
474
609
|
if (this.failure.stackTrace !== void 0)
|
|
@@ -476,7 +611,8 @@ var WorkflowExecution = class {
|
|
|
476
611
|
if (this.failure.seq !== void 0) result.failure.seq = this.failure.seq;
|
|
477
612
|
if (this.failure.errorType !== void 0) result.failure.errorType = this.failure.errorType;
|
|
478
613
|
}
|
|
479
|
-
if (this.request.kind === "query"
|
|
614
|
+
if (this.request.kind === "query" || (this.request.queries ?? []).length > 0)
|
|
615
|
+
result.queryResults = this.answerQueries();
|
|
480
616
|
return result;
|
|
481
617
|
}
|
|
482
618
|
/**
|
|
@@ -503,11 +639,54 @@ var WorkflowExecution = class {
|
|
|
503
639
|
if (this.failure) return;
|
|
504
640
|
this.deliver(event);
|
|
505
641
|
}
|
|
642
|
+
for (const update of this.updatesOf(activation)) {
|
|
643
|
+
if (this.failure) return;
|
|
644
|
+
this.deliverUpdate(update);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* The updates an activation delivers after its inputs (C-ABI-H): a replayed
|
|
649
|
+
* activation re-delivers the updates its task accepted (validators
|
|
650
|
+
* skipped); the open activation of a workflow task delivers the lease's
|
|
651
|
+
* pending updates. Updates history admitted (`UPDATE_ADMITTED`) arrive as
|
|
652
|
+
* inputs instead.
|
|
653
|
+
*/
|
|
654
|
+
updatesOf(activation) {
|
|
655
|
+
const out = [];
|
|
656
|
+
if (activation.recorded) {
|
|
657
|
+
for (const event of activation.recorded) {
|
|
658
|
+
if (event.type !== "UPDATE_ACCEPTED") continue;
|
|
659
|
+
const p = event.payload;
|
|
660
|
+
const updateId = typeof p["updateId"] === "string" ? p["updateId"] : "";
|
|
661
|
+
if (this.deliveredUpdates.has(updateId)) continue;
|
|
662
|
+
out.push({
|
|
663
|
+
updateId,
|
|
664
|
+
name: typeof p["name"] === "string" ? p["name"] : "",
|
|
665
|
+
args: p["args"] ?? null,
|
|
666
|
+
headers: headersOf(p["headers"]),
|
|
667
|
+
validate: false
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
return out;
|
|
671
|
+
}
|
|
672
|
+
if (this.request.kind === "query") return out;
|
|
673
|
+
for (const update of this.request.updates ?? []) {
|
|
674
|
+
if (update.admitted === true || this.deliveredUpdates.has(update.updateId)) continue;
|
|
675
|
+
out.push({
|
|
676
|
+
updateId: update.updateId,
|
|
677
|
+
name: update.name,
|
|
678
|
+
args: update.args ?? null,
|
|
679
|
+
headers: headersOf(update.headers),
|
|
680
|
+
validate: true
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
return out;
|
|
506
684
|
}
|
|
507
685
|
finishActivation() {
|
|
508
686
|
this.activationOpen = false;
|
|
509
687
|
const activation = this.activations[this.index];
|
|
510
688
|
if (!activation) return;
|
|
689
|
+
this.rejectUnhandledUpdates();
|
|
511
690
|
if (activation.recorded) {
|
|
512
691
|
const found = matchActivation(this.commands, activation.recorded);
|
|
513
692
|
if (found)
|
|
@@ -567,8 +746,20 @@ var WorkflowExecution = class {
|
|
|
567
746
|
return;
|
|
568
747
|
}
|
|
569
748
|
case "SIGNAL_RECEIVED":
|
|
570
|
-
this.dispatchSignal(id("name"), p["payload"] ?? null);
|
|
749
|
+
this.dispatchSignal(id("name"), p["payload"] ?? null, headersOf(p["headers"]));
|
|
571
750
|
return;
|
|
751
|
+
case "UPDATE_ADMITTED": {
|
|
752
|
+
const updateId = id("updateId");
|
|
753
|
+
if (this.deliveredUpdates.has(updateId)) return;
|
|
754
|
+
this.deliverUpdate({
|
|
755
|
+
updateId,
|
|
756
|
+
name: id("name"),
|
|
757
|
+
args: p["args"] ?? null,
|
|
758
|
+
headers: headersOf(p["headers"]),
|
|
759
|
+
validate: true
|
|
760
|
+
});
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
572
763
|
case "CHILD_RUN_STARTED":
|
|
573
764
|
case "CHILD_RUN_COMPLETED":
|
|
574
765
|
case "CHILD_RUN_FAILED":
|
|
@@ -618,12 +809,14 @@ var WorkflowExecution = class {
|
|
|
618
809
|
pending.release?.();
|
|
619
810
|
const p = event.payload;
|
|
620
811
|
const failure = typeof p["failure"] === "object" && p["failure"] !== null && !Array.isArray(p["failure"]) ? p["failure"] : void 0;
|
|
812
|
+
const label = pending.activityName !== void 0 ? `user ${pending.activityName}` : `${pending.nodeType} ${pending.operation}`;
|
|
621
813
|
const wrap = (cause) => new ActivityFailure(
|
|
622
|
-
`Activity ${activityId} (${
|
|
814
|
+
`Activity ${activityId} (${label}) failed: ${cause.message}`,
|
|
623
815
|
activityId,
|
|
624
816
|
pending.nodeType,
|
|
625
817
|
pending.operation,
|
|
626
|
-
cause
|
|
818
|
+
cause,
|
|
819
|
+
pending.activityName
|
|
627
820
|
);
|
|
628
821
|
switch (event.type) {
|
|
629
822
|
case "ACTIVITY_COMPLETED":
|
|
@@ -715,10 +908,24 @@ var WorkflowExecution = class {
|
|
|
715
908
|
else result.reject(outcome.error);
|
|
716
909
|
}
|
|
717
910
|
}
|
|
718
|
-
|
|
911
|
+
/** Run a signal handler through the inbound `handleSignal` interceptors. */
|
|
912
|
+
signalCall(name, payload, headers, handler) {
|
|
913
|
+
if (this.inbound.length === 0) return () => handler(payload);
|
|
914
|
+
const chain = compose(
|
|
915
|
+
this.inbound,
|
|
916
|
+
"handleSignal",
|
|
917
|
+
(input) => handler(input.payload)
|
|
918
|
+
);
|
|
919
|
+
return () => chain({ name, payload, headers });
|
|
920
|
+
}
|
|
921
|
+
dispatchSignal(name, payload, headers = {}) {
|
|
719
922
|
const handler = this.signalHandlers.get(name);
|
|
720
923
|
if (handler) {
|
|
721
|
-
this.runHandler(
|
|
924
|
+
this.runHandler(
|
|
925
|
+
this.signalCall(name, payload, headers, handler),
|
|
926
|
+
name,
|
|
927
|
+
this.signalPolicies.get(name)
|
|
928
|
+
);
|
|
722
929
|
return;
|
|
723
930
|
}
|
|
724
931
|
const waiters = this.signalWaiters.get(name);
|
|
@@ -730,7 +937,11 @@ var WorkflowExecution = class {
|
|
|
730
937
|
}
|
|
731
938
|
if (this.defaultSignalHandler) {
|
|
732
939
|
const fallback = this.defaultSignalHandler;
|
|
733
|
-
this.runHandler(
|
|
940
|
+
this.runHandler(
|
|
941
|
+
this.signalCall(name, payload, headers, (value) => fallback(name, value)),
|
|
942
|
+
name,
|
|
943
|
+
this.defaultSignalPolicy
|
|
944
|
+
);
|
|
734
945
|
return;
|
|
735
946
|
}
|
|
736
947
|
const buffer = this.signalBuffers.get(name) ?? [];
|
|
@@ -755,22 +966,228 @@ var WorkflowExecution = class {
|
|
|
755
966
|
return;
|
|
756
967
|
}
|
|
757
968
|
if (result instanceof Promise) {
|
|
758
|
-
this.
|
|
759
|
-
const entry = this.running.get(name) ?? { count: 0, policy };
|
|
760
|
-
entry.count += 1;
|
|
761
|
-
entry.policy = policy;
|
|
762
|
-
this.running.set(name, entry);
|
|
763
|
-
const done = () => {
|
|
764
|
-
this.runningHandlers -= 1;
|
|
765
|
-
entry.count -= 1;
|
|
766
|
-
if (entry.count === 0) this.running.delete(name);
|
|
767
|
-
};
|
|
969
|
+
const done = this.trackHandler("signal", name, policy);
|
|
768
970
|
result.then(done, (error) => {
|
|
769
971
|
done();
|
|
770
972
|
this.onWorkflowError(error);
|
|
771
973
|
});
|
|
772
974
|
}
|
|
773
975
|
}
|
|
976
|
+
/** Count a running asynchronous handler (drain check, `allHandlersFinished`); returns its end. */
|
|
977
|
+
trackHandler(kind, name, policy) {
|
|
978
|
+
this.runningHandlers += 1;
|
|
979
|
+
const key = `${kind}:${name}`;
|
|
980
|
+
const entry = this.running.get(key) ?? { kind, name, count: 0, policy };
|
|
981
|
+
entry.count += 1;
|
|
982
|
+
entry.policy = policy;
|
|
983
|
+
this.running.set(key, entry);
|
|
984
|
+
return () => {
|
|
985
|
+
this.runningHandlers -= 1;
|
|
986
|
+
entry.count -= 1;
|
|
987
|
+
if (entry.count === 0) this.running.delete(key);
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
// ---- updates (Phase C) ----------------------------------------------------
|
|
991
|
+
deliverUpdate(update) {
|
|
992
|
+
this.deliveredUpdates.add(update.updateId);
|
|
993
|
+
const entry = this.updateHandlerFor(update.name);
|
|
994
|
+
if (!entry) {
|
|
995
|
+
this.bufferedUpdates.push(update);
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
this.runUpdate(update, entry);
|
|
999
|
+
}
|
|
1000
|
+
updateHandlerFor(name) {
|
|
1001
|
+
const own = this.updateHandlers.get(name);
|
|
1002
|
+
if (own) return own;
|
|
1003
|
+
const fallback = this.defaultUpdateHandler;
|
|
1004
|
+
if (!fallback) return void 0;
|
|
1005
|
+
const validate = fallback.validate;
|
|
1006
|
+
return {
|
|
1007
|
+
handler: (args) => fallback.handler(name, args),
|
|
1008
|
+
...validate !== void 0 ? { validate: (args) => validate(name, args) } : {},
|
|
1009
|
+
policy: fallback.policy
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Run `fn` with commands, sinks and every other change to the run's
|
|
1014
|
+
* deterministic state refused (query handlers, update validators). A
|
|
1015
|
+
* refused call fails the whole call with `QueryHandlerSideEffect`, even
|
|
1016
|
+
* when the code caught the error it threw.
|
|
1017
|
+
*/
|
|
1018
|
+
readOnlyCall(what, fn) {
|
|
1019
|
+
const previous = this.readOnly;
|
|
1020
|
+
const previousViolation = this.readOnlyViolation;
|
|
1021
|
+
this.readOnly = what;
|
|
1022
|
+
this.readOnlyViolation = void 0;
|
|
1023
|
+
try {
|
|
1024
|
+
let value;
|
|
1025
|
+
try {
|
|
1026
|
+
value = fn();
|
|
1027
|
+
} catch (error) {
|
|
1028
|
+
const violation2 = this.readOnlyViolation;
|
|
1029
|
+
if (violation2 !== void 0 && !(error instanceof QueryHandlerSideEffect))
|
|
1030
|
+
throw new QueryHandlerSideEffect(`${violation2} (in ${what})`);
|
|
1031
|
+
throw error;
|
|
1032
|
+
}
|
|
1033
|
+
const violation = this.readOnlyViolation;
|
|
1034
|
+
if (violation !== void 0) throw new QueryHandlerSideEffect(`${violation} (in ${what})`);
|
|
1035
|
+
return value;
|
|
1036
|
+
} finally {
|
|
1037
|
+
this.readOnly = previous;
|
|
1038
|
+
this.readOnlyViolation = previousViolation;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Validate, accept and run one update. A validator error rejects it
|
|
1043
|
+
* (`REJECT_UPDATE`, no history); otherwise `ACCEPT_UPDATE` is issued and
|
|
1044
|
+
* the handler's result or failure completes it (`COMPLETE_UPDATE`). A
|
|
1045
|
+
* handler that throws something other than a failure class fails the task,
|
|
1046
|
+
* as a signal handler does.
|
|
1047
|
+
*/
|
|
1048
|
+
runUpdate(update, entry) {
|
|
1049
|
+
if (this.closed || this.failure) return;
|
|
1050
|
+
const input = {
|
|
1051
|
+
updateId: update.updateId,
|
|
1052
|
+
name: update.name,
|
|
1053
|
+
args: update.args,
|
|
1054
|
+
headers: update.headers
|
|
1055
|
+
};
|
|
1056
|
+
if (update.validate && (entry.validate !== void 0 || this.inbound.length > 0)) {
|
|
1057
|
+
const validate = compose(
|
|
1058
|
+
this.inbound,
|
|
1059
|
+
"validateUpdate",
|
|
1060
|
+
(checked) => entry.validate?.(checked.args)
|
|
1061
|
+
);
|
|
1062
|
+
try {
|
|
1063
|
+
this.readOnlyCall(`The validator of update ${update.name}`, () => validate(input));
|
|
1064
|
+
} catch (error) {
|
|
1065
|
+
if (error instanceof QueryHandlerSideEffect) {
|
|
1066
|
+
this.failTask("WORKFLOW_CODE_ERROR", messageOf(error), stackTextOf(error));
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
const failure = errorToFailure(error);
|
|
1070
|
+
delete failure.stackTrace;
|
|
1071
|
+
this.commands.push({ type: "REJECT_UPDATE", updateId: update.updateId, failure });
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
if (!this.push({ type: "ACCEPT_UPDATE", updateId: update.updateId })) return;
|
|
1076
|
+
const handle = compose(
|
|
1077
|
+
this.inbound,
|
|
1078
|
+
"handleUpdate",
|
|
1079
|
+
(accepted) => entry.handler(accepted.args)
|
|
1080
|
+
);
|
|
1081
|
+
let result;
|
|
1082
|
+
try {
|
|
1083
|
+
result = handle(input);
|
|
1084
|
+
} catch (error) {
|
|
1085
|
+
this.completeUpdate(update, { ok: false, error });
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
if (result instanceof Promise) {
|
|
1089
|
+
const done = this.trackHandler("update", update.name, entry.policy);
|
|
1090
|
+
result.then(
|
|
1091
|
+
(value) => {
|
|
1092
|
+
done();
|
|
1093
|
+
this.completeUpdate(update, { ok: true, value });
|
|
1094
|
+
},
|
|
1095
|
+
(error) => {
|
|
1096
|
+
done();
|
|
1097
|
+
this.completeUpdate(update, { ok: false, error });
|
|
1098
|
+
}
|
|
1099
|
+
);
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
this.completeUpdate(update, { ok: true, value: result });
|
|
1103
|
+
}
|
|
1104
|
+
completeUpdate(update, outcome) {
|
|
1105
|
+
if (this.closed || this.failure) return;
|
|
1106
|
+
if (outcome.ok) {
|
|
1107
|
+
let value;
|
|
1108
|
+
try {
|
|
1109
|
+
value = toJson(outcome.value, `The result of update ${update.name}`);
|
|
1110
|
+
} catch (error2) {
|
|
1111
|
+
this.onWorkflowError(error2);
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
this.push({ type: "COMPLETE_UPDATE", updateId: update.updateId, result: value });
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
const error = outcome.error;
|
|
1118
|
+
if (error instanceof ContinueAsNewRequest || !(isRunFailure(error) || this.isFailureType(error))) {
|
|
1119
|
+
this.onWorkflowError(error);
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
const failure = errorToFailure(error);
|
|
1123
|
+
delete failure.stackTrace;
|
|
1124
|
+
this.push({ type: "COMPLETE_UPDATE", updateId: update.updateId, failure });
|
|
1125
|
+
}
|
|
1126
|
+
/** Reject the updates of this activation that no handler took (`UpdateNotHandled`). */
|
|
1127
|
+
rejectUnhandledUpdates() {
|
|
1128
|
+
const pending = this.bufferedUpdates;
|
|
1129
|
+
this.bufferedUpdates = [];
|
|
1130
|
+
if (this.closed || this.failure) return;
|
|
1131
|
+
for (const update of pending)
|
|
1132
|
+
this.commands.push({
|
|
1133
|
+
type: "REJECT_UPDATE",
|
|
1134
|
+
updateId: update.updateId,
|
|
1135
|
+
failure: {
|
|
1136
|
+
errorType: "VALIDATION",
|
|
1137
|
+
type: "UpdateNotHandled",
|
|
1138
|
+
message: `The workflow has no handler for update "${update.name}"`,
|
|
1139
|
+
source: "sdk-ts"
|
|
1140
|
+
}
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
handleUpdate(update, handler, options = {}) {
|
|
1144
|
+
const name = definitionName(update, "ctx.handleUpdate");
|
|
1145
|
+
if (typeof handler !== "function")
|
|
1146
|
+
throw new TypeError("ctx.handleUpdate needs a handler function");
|
|
1147
|
+
if (options.validate !== void 0 && typeof options.validate !== "function")
|
|
1148
|
+
throw new TypeError("validate must be a function");
|
|
1149
|
+
const policy = checkPolicy(options.unfinishedPolicy);
|
|
1150
|
+
this.updateHandlers.set(name, {
|
|
1151
|
+
handler,
|
|
1152
|
+
...options.validate !== void 0 ? { validate: options.validate } : {},
|
|
1153
|
+
policy
|
|
1154
|
+
});
|
|
1155
|
+
this.dispatchBufferedUpdates();
|
|
1156
|
+
}
|
|
1157
|
+
setDefaultUpdateHandler(first, second) {
|
|
1158
|
+
const handler = typeof first === "function" ? first : second;
|
|
1159
|
+
const options = (typeof first === "function" ? second : first) ?? {};
|
|
1160
|
+
if (typeof handler !== "function" || typeof options !== "object")
|
|
1161
|
+
throw new TypeError("ctx.setDefaultUpdateHandler needs a handler function");
|
|
1162
|
+
if (options.validate !== void 0 && typeof options.validate !== "function")
|
|
1163
|
+
throw new TypeError("validate must be a function");
|
|
1164
|
+
this.defaultUpdateHandler = {
|
|
1165
|
+
handler,
|
|
1166
|
+
...options.validate !== void 0 ? { validate: options.validate } : {},
|
|
1167
|
+
policy: checkPolicy(options.unfinishedPolicy)
|
|
1168
|
+
};
|
|
1169
|
+
this.dispatchBufferedUpdates();
|
|
1170
|
+
}
|
|
1171
|
+
dispatchBufferedUpdates() {
|
|
1172
|
+
const pending = this.bufferedUpdates;
|
|
1173
|
+
this.bufferedUpdates = [];
|
|
1174
|
+
for (const update of pending) {
|
|
1175
|
+
const entry = this.updateHandlerFor(update.name);
|
|
1176
|
+
if (entry) this.runUpdate(update, entry);
|
|
1177
|
+
else this.bufferedUpdates.push(update);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
handleQuery(query, handler) {
|
|
1181
|
+
const name = definitionName(query, "ctx.handleQuery");
|
|
1182
|
+
if (typeof handler !== "function")
|
|
1183
|
+
throw new TypeError("ctx.handleQuery needs a handler function");
|
|
1184
|
+
this.queryHandlers.set(name, handler);
|
|
1185
|
+
}
|
|
1186
|
+
setDefaultQueryHandler(handler) {
|
|
1187
|
+
if (typeof handler !== "function")
|
|
1188
|
+
throw new TypeError("ctx.setDefaultQueryHandler needs a handler function");
|
|
1189
|
+
this.defaultQueryHandler = handler;
|
|
1190
|
+
}
|
|
774
1191
|
/**
|
|
775
1192
|
* The drain check of 11 §2.18 when the workflow finishes: handlers still
|
|
776
1193
|
* running with policy `WARN_AND_ABANDON` give one warning line and
|
|
@@ -778,13 +1195,18 @@ var WorkflowExecution = class {
|
|
|
778
1195
|
*/
|
|
779
1196
|
checkUnfinishedHandlers(command) {
|
|
780
1197
|
if (this.replaying || command.type === "CANCEL_WORKFLOW") return;
|
|
781
|
-
const unfinished = [...this.running.
|
|
1198
|
+
const unfinished = [...this.running.values()].filter((entry) => entry.count > 0 && entry.policy === "WARN_AND_ABANDON").map(
|
|
1199
|
+
(entry) => ({ kind: entry.kind, name: entry.name, count: entry.count })
|
|
1200
|
+
).sort(
|
|
1201
|
+
(a, b) => a.kind !== b.kind ? a.kind < b.kind ? -1 : 1 : a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
|
1202
|
+
);
|
|
782
1203
|
if (unfinished.length === 0) return;
|
|
783
1204
|
this.unfinished = unfinished;
|
|
784
1205
|
const total = unfinished.reduce((sum, entry) => sum + entry.count, 0);
|
|
1206
|
+
const what = unfinished.every((entry) => entry.kind === "signal") ? "signal handler" : unfinished.every((entry) => entry.kind === "update") ? "update handler" : "signal or update handler";
|
|
785
1207
|
this.logs.push({
|
|
786
1208
|
level: "warn",
|
|
787
|
-
message: `The workflow finished while ${total}
|
|
1209
|
+
message: `The workflow finished while ${total} ${what}${total === 1 ? " was" : "s were"} still running (${unfinished.map((entry) => `${entry.name} x${entry.count}`).join(
|
|
788
1210
|
", "
|
|
789
1211
|
)}); await ctx.allHandlersFinished() before returning, or set unfinishedPolicy: 'ABANDON' on the handler`,
|
|
790
1212
|
data: unfinished.map((entry) => ({ kind: entry.kind, name: entry.name, count: entry.count }))
|
|
@@ -794,7 +1216,15 @@ var WorkflowExecution = class {
|
|
|
794
1216
|
startFunction(input) {
|
|
795
1217
|
let promise;
|
|
796
1218
|
try {
|
|
797
|
-
|
|
1219
|
+
const run = (value) => (
|
|
1220
|
+
// The input crossed the history as JSON; the function declared its type.
|
|
1221
|
+
Promise.resolve(this.definition.fn(this.context, value))
|
|
1222
|
+
);
|
|
1223
|
+
promise = this.inbound.length === 0 ? run(input) : compose(
|
|
1224
|
+
this.inbound,
|
|
1225
|
+
"execute",
|
|
1226
|
+
(execute) => run(execute.input)
|
|
1227
|
+
)({ workflowFn: this.request.workflowFn, input, headers: { ...this.startHeaders } });
|
|
798
1228
|
} catch (error) {
|
|
799
1229
|
promise = Promise.reject(error);
|
|
800
1230
|
}
|
|
@@ -824,7 +1254,13 @@ var WorkflowExecution = class {
|
|
|
824
1254
|
onWorkflowError(error) {
|
|
825
1255
|
if (this.closed || this.failure) return;
|
|
826
1256
|
if (error instanceof ContinueAsNewRequest) {
|
|
827
|
-
|
|
1257
|
+
const command = {
|
|
1258
|
+
type: "CONTINUE_AS_NEW",
|
|
1259
|
+
input: error.input,
|
|
1260
|
+
options: error.options
|
|
1261
|
+
};
|
|
1262
|
+
if (Object.keys(error.headers).length > 0) command.headers = { ...error.headers };
|
|
1263
|
+
this.pushTerminal(command);
|
|
828
1264
|
return;
|
|
829
1265
|
}
|
|
830
1266
|
const name = errorName(error);
|
|
@@ -844,6 +1280,10 @@ var WorkflowExecution = class {
|
|
|
844
1280
|
this.failTask("UNSUPPORTED_COMMAND", messageOf(error), stackTextOf(error));
|
|
845
1281
|
return;
|
|
846
1282
|
}
|
|
1283
|
+
if (error instanceof QueryHandlerSideEffect) {
|
|
1284
|
+
this.failTask("WORKFLOW_CODE_ERROR", messageOf(error), stackTextOf(error), void 0, name);
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
847
1287
|
if (this.cancelRequested && isCancellation(error)) {
|
|
848
1288
|
this.pushTerminal({ type: "CANCEL_WORKFLOW" });
|
|
849
1289
|
return;
|
|
@@ -878,6 +1318,7 @@ var WorkflowExecution = class {
|
|
|
878
1318
|
}
|
|
879
1319
|
/** Issue a command; false when the run is already closed (the command is dropped). */
|
|
880
1320
|
push(command) {
|
|
1321
|
+
this.assertWritable(command.type);
|
|
881
1322
|
if (this.closed || this.failure) return false;
|
|
882
1323
|
this.checkLimit();
|
|
883
1324
|
this.commands.push(command);
|
|
@@ -903,7 +1344,7 @@ var WorkflowExecution = class {
|
|
|
903
1344
|
`${operation.nodeType} cannot run as a durable activity: only store and registry nodes and internal:http, internal:datatable, internal:set and internal:code can`,
|
|
904
1345
|
{ type: "durable.activityTypeNotAllowed", errorType: "VALIDATION", nonRetryable: true }
|
|
905
1346
|
);
|
|
906
|
-
const activityId = this.
|
|
1347
|
+
const activityId = this.nextId("a");
|
|
907
1348
|
const operationName = options.operation ?? operation.operation ?? "execute";
|
|
908
1349
|
const nodeVersion = options.nodeVersion ?? operation.version;
|
|
909
1350
|
const cancellationType = options.cancellationType ?? "WAIT_CANCELLATION_COMPLETED";
|
|
@@ -930,20 +1371,119 @@ var WorkflowExecution = class {
|
|
|
930
1371
|
if (options.priority !== void 0) command.priority = options.priority;
|
|
931
1372
|
if (options.summary !== void 0) command.summary = options.summary;
|
|
932
1373
|
if (options.runOn !== void 0) command.runOn = options.runOn;
|
|
933
|
-
|
|
1374
|
+
checkFairness(options);
|
|
1375
|
+
if (options.fairnessKey !== void 0) command.fairnessKey = options.fairnessKey;
|
|
1376
|
+
if (options.fairnessWeight !== void 0) command.fairnessWeight = options.fairnessWeight;
|
|
1377
|
+
return this.issueActivity(
|
|
1378
|
+
scope,
|
|
1379
|
+
command,
|
|
1380
|
+
options.headers,
|
|
1381
|
+
{
|
|
1382
|
+
description: `activity ${activityId} (${operation.nodeType} ${operationName})`,
|
|
1383
|
+
nodeType: operation.nodeType,
|
|
1384
|
+
operation: operationName,
|
|
1385
|
+
cancellationType
|
|
1386
|
+
},
|
|
1387
|
+
{ kind: "node", name: operation.nodeType, operation: operationName }
|
|
1388
|
+
);
|
|
1389
|
+
}
|
|
1390
|
+
/**
|
|
1391
|
+
* `ctx.activity` (Phase C): a user activity on a self-hosted task queue
|
|
1392
|
+
* (`SCHEDULE_ACTIVITY` of kind `user`). The queue is the option, else the
|
|
1393
|
+
* declaration's, else the run's own.
|
|
1394
|
+
*/
|
|
1395
|
+
userActivity(scope, activity, options) {
|
|
1396
|
+
if (typeof options !== "object" || options === null)
|
|
1397
|
+
throw new TypeError("ctx.activity needs options with args");
|
|
1398
|
+
let name;
|
|
1399
|
+
let declaredQueue;
|
|
1400
|
+
if (typeof activity === "string") name = activity;
|
|
1401
|
+
else if (isActivityDefinition(activity)) {
|
|
1402
|
+
name = activity.name;
|
|
1403
|
+
declaredQueue = activity.options.taskQueue;
|
|
1404
|
+
} else throw new TypeError("ctx.activity needs an activity name or a declareActivity() value");
|
|
1405
|
+
checkActivityName(name);
|
|
1406
|
+
const taskQueue = options.taskQueue ?? declaredQueue ?? this.info.taskQueue ?? "managed";
|
|
1407
|
+
checkTaskQueueName(taskQueue);
|
|
1408
|
+
const connections = options.connections;
|
|
1409
|
+
if (connections !== void 0 && (!Array.isArray(connections) || connections.length > MAX_ACTIVITY_CONNECTIONS || new Set(connections).size !== connections.length || !connections.every((entry) => typeof entry === "string" && entry !== "")))
|
|
1410
|
+
throw new TypeError(
|
|
1411
|
+
`connections must be at most ${MAX_ACTIVITY_CONNECTIONS} distinct connection names`
|
|
1412
|
+
);
|
|
1413
|
+
checkFairness(options);
|
|
1414
|
+
const activityId = this.nextId("a");
|
|
1415
|
+
const cancellationType = options.cancellationType ?? "WAIT_CANCELLATION_COMPLETED";
|
|
1416
|
+
const command = {
|
|
1417
|
+
type: "SCHEDULE_ACTIVITY",
|
|
1418
|
+
activityId,
|
|
1419
|
+
kind: "user",
|
|
1420
|
+
activityName: name,
|
|
1421
|
+
taskQueue,
|
|
1422
|
+
args: toJson(options.args, `The args of activity ${name}`),
|
|
1423
|
+
retry: explicitRetryPolicy(options.retry)
|
|
1424
|
+
};
|
|
1425
|
+
const startToClose = optionalMs(options.startToCloseTimeout, "startToCloseTimeout");
|
|
1426
|
+
const scheduleToClose = optionalMs(options.scheduleToCloseTimeout, "scheduleToCloseTimeout");
|
|
1427
|
+
const scheduleToStart = optionalMs(options.scheduleToStartTimeout, "scheduleToStartTimeout");
|
|
1428
|
+
const heartbeat = optionalMs(options.heartbeatTimeout, "heartbeatTimeout");
|
|
1429
|
+
if (startToClose !== void 0) command.startToCloseTimeout = startToClose;
|
|
1430
|
+
if (scheduleToClose !== void 0) command.scheduleToCloseTimeout = scheduleToClose;
|
|
1431
|
+
if (scheduleToStart !== void 0) command.scheduleToStartTimeout = scheduleToStart;
|
|
1432
|
+
if (heartbeat !== void 0) command.heartbeatTimeout = heartbeat;
|
|
1433
|
+
if (options.cancellationType !== void 0) command.cancellationType = options.cancellationType;
|
|
1434
|
+
if (options.priority !== void 0) command.priority = options.priority;
|
|
1435
|
+
if (options.summary !== void 0) command.summary = options.summary;
|
|
1436
|
+
if (options.fairnessKey !== void 0) command.fairnessKey = options.fairnessKey;
|
|
1437
|
+
if (options.fairnessWeight !== void 0) command.fairnessWeight = options.fairnessWeight;
|
|
1438
|
+
if (connections !== void 0) command.connections = [...connections];
|
|
1439
|
+
return this.issueActivity(
|
|
1440
|
+
scope,
|
|
1441
|
+
command,
|
|
1442
|
+
options.headers,
|
|
1443
|
+
{
|
|
1444
|
+
description: `activity ${activityId} (user ${name})`,
|
|
1445
|
+
nodeType: "",
|
|
1446
|
+
operation: name,
|
|
1447
|
+
activityName: name,
|
|
1448
|
+
cancellationType
|
|
1449
|
+
},
|
|
1450
|
+
{ kind: "user", name }
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1453
|
+
/** Run the outbound `scheduleActivity` interceptors, then issue the command. */
|
|
1454
|
+
issueActivity(scope, command, headers, pending, what) {
|
|
1455
|
+
if (this.outbound.length === 0) {
|
|
1456
|
+
if (headers !== void 0) command.headers = { ...headers };
|
|
1457
|
+
return this.pushActivity(scope, command, pending);
|
|
1458
|
+
}
|
|
1459
|
+
const chain = compose(
|
|
1460
|
+
this.outbound,
|
|
1461
|
+
"scheduleActivity",
|
|
1462
|
+
(input) => {
|
|
1463
|
+
command.args = toJson(input.args, `The args of ${what.name}`);
|
|
1464
|
+
if (headers !== void 0 || Object.keys(input.headers).length > 0)
|
|
1465
|
+
command.headers = { ...input.headers };
|
|
1466
|
+
return this.pushActivity(scope, command, pending);
|
|
1467
|
+
}
|
|
1468
|
+
);
|
|
1469
|
+
return chain({
|
|
1470
|
+
activityId: command.activityId,
|
|
1471
|
+
kind: what.kind,
|
|
1472
|
+
name: what.name,
|
|
1473
|
+
...what.operation !== void 0 ? { operation: what.operation } : {},
|
|
1474
|
+
args: command.args,
|
|
1475
|
+
headers: { ...headers ?? {} }
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
pushActivity(scope, command, pending) {
|
|
1479
|
+
const activityId = command.activityId;
|
|
1480
|
+
const cancellationType = pending.cancellationType;
|
|
934
1481
|
if (scope.consideredCancelled)
|
|
935
1482
|
return Promise.reject(new CancelledFailure("Scope is cancelled"));
|
|
936
1483
|
if (!this.push(command)) return this.never();
|
|
937
1484
|
this.issued.add(`activity:${activityId}`);
|
|
938
1485
|
return new Promise((resolve, reject) => {
|
|
939
|
-
const entry = {
|
|
940
|
-
resolve,
|
|
941
|
-
reject,
|
|
942
|
-
description: `activity ${activityId} (${operation.nodeType} ${operationName})`,
|
|
943
|
-
nodeType: operation.nodeType,
|
|
944
|
-
operation: operationName,
|
|
945
|
-
cancellationType
|
|
946
|
-
};
|
|
1486
|
+
const entry = { ...pending, resolve, reject };
|
|
947
1487
|
const stack = this.stack();
|
|
948
1488
|
if (stack !== void 0) entry.stack = stack;
|
|
949
1489
|
entry.release = scope.onCancel(() => {
|
|
@@ -953,7 +1493,8 @@ var WorkflowExecution = class {
|
|
|
953
1493
|
activityId,
|
|
954
1494
|
entry.nodeType,
|
|
955
1495
|
entry.operation,
|
|
956
|
-
new CancelledFailure("Activity cancelled")
|
|
1496
|
+
new CancelledFailure("Activity cancelled"),
|
|
1497
|
+
entry.activityName
|
|
957
1498
|
);
|
|
958
1499
|
if (cancellationType !== "ABANDON")
|
|
959
1500
|
this.push({ type: "REQUEST_CANCEL_ACTIVITY", activityId });
|
|
@@ -993,80 +1534,94 @@ var WorkflowExecution = class {
|
|
|
993
1534
|
throw new TypeError(
|
|
994
1535
|
`A local activity's startToCloseTimeout must be between 1 ms and 30 s (got ${timeout} ms); use ctx.run for longer calls`
|
|
995
1536
|
);
|
|
996
|
-
const localActivityId = this.
|
|
1537
|
+
const localActivityId = this.nextId("l");
|
|
997
1538
|
const operationName = options.operation ?? descriptor.operation ?? "execute";
|
|
998
1539
|
const nodeVersion = options.nodeVersion ?? descriptor.version;
|
|
999
1540
|
const retry = explicitRetryPolicy(options.retry);
|
|
1000
1541
|
const args = toJson(options.args, `The args of ${descriptor.nodeType}`);
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
descriptor.nodeType,
|
|
1008
|
-
operationName,
|
|
1009
|
-
cause
|
|
1010
|
-
);
|
|
1011
|
-
const record = (outcome) => {
|
|
1012
|
-
const command = {
|
|
1013
|
-
type: "RECORD_LOCAL_ACTIVITY",
|
|
1542
|
+
const proceed = (args2) => {
|
|
1543
|
+
if (scope.consideredCancelled)
|
|
1544
|
+
return Promise.reject(new CancelledFailure("Scope is cancelled"));
|
|
1545
|
+
const label = `local activity ${localActivityId} (${descriptor.nodeType} ${operationName})`;
|
|
1546
|
+
const wrap = (cause) => new ActivityFailure(
|
|
1547
|
+
`Local activity ${localActivityId} (${descriptor.nodeType} ${operationName}) failed: ${cause.message}`,
|
|
1014
1548
|
localActivityId,
|
|
1015
|
-
|
|
1549
|
+
descriptor.nodeType,
|
|
1550
|
+
operationName,
|
|
1551
|
+
cause
|
|
1552
|
+
);
|
|
1553
|
+
const record = (outcome) => {
|
|
1554
|
+
const command = {
|
|
1555
|
+
type: "RECORD_LOCAL_ACTIVITY",
|
|
1556
|
+
localActivityId,
|
|
1557
|
+
attempts: Math.max(1, Math.round(outcome.attempts)),
|
|
1558
|
+
nodeType: descriptor.nodeType,
|
|
1559
|
+
operation: operationName,
|
|
1560
|
+
...outcome.failure !== void 0 ? { failure: outcome.failure } : { result: outcome.result ?? null }
|
|
1561
|
+
};
|
|
1562
|
+
if (options.summary !== void 0) command.summary = options.summary;
|
|
1563
|
+
if (!this.push(command)) return this.never();
|
|
1564
|
+
if (outcome.failure !== void 0)
|
|
1565
|
+
return Promise.reject(wrap(failureToError(outcome.failure)));
|
|
1566
|
+
return Promise.resolve(outcome.result ?? null);
|
|
1567
|
+
};
|
|
1568
|
+
const replayed = this.replaying;
|
|
1569
|
+
if (replayed) {
|
|
1570
|
+
const recorded = this.recordedLocalActivities.get(localActivityId);
|
|
1571
|
+
if (recorded !== void 0) return record(recorded);
|
|
1572
|
+
}
|
|
1573
|
+
const given = replayed ? void 0 : this.localActivityResults.get(localActivityId);
|
|
1574
|
+
let attempt = 1;
|
|
1575
|
+
let backoffMs = 0;
|
|
1576
|
+
if (given !== void 0) {
|
|
1577
|
+
if (given.failure === void 0) return record(given);
|
|
1578
|
+
const decision = computeRetryDelay(retry, given.attempts, given.failure);
|
|
1579
|
+
if (!decision.retry) return record(given);
|
|
1580
|
+
attempt = given.attempts + 1;
|
|
1581
|
+
backoffMs = decision.delayMs;
|
|
1582
|
+
}
|
|
1583
|
+
const request = {
|
|
1584
|
+
localActivityId,
|
|
1585
|
+
kind: "node",
|
|
1016
1586
|
nodeType: descriptor.nodeType,
|
|
1017
1587
|
operation: operationName,
|
|
1018
|
-
|
|
1588
|
+
args: args2,
|
|
1589
|
+
retry,
|
|
1590
|
+
startToCloseTimeout: timeout,
|
|
1591
|
+
attempt,
|
|
1592
|
+
backoffMs
|
|
1019
1593
|
};
|
|
1020
|
-
if (
|
|
1021
|
-
if (
|
|
1022
|
-
if (
|
|
1023
|
-
|
|
1024
|
-
|
|
1594
|
+
if (nodeVersion !== void 0) request.nodeVersion = nodeVersion;
|
|
1595
|
+
if (options.connection !== void 0) request.connection = options.connection;
|
|
1596
|
+
if (options.summary !== void 0) request.summary = options.summary;
|
|
1597
|
+
return new Promise((resolve, reject) => {
|
|
1598
|
+
const entry = {
|
|
1599
|
+
resolve,
|
|
1600
|
+
reject,
|
|
1601
|
+
description: label,
|
|
1602
|
+
request,
|
|
1603
|
+
replayed
|
|
1604
|
+
};
|
|
1605
|
+
const stack = this.stack();
|
|
1606
|
+
if (stack !== void 0) entry.stack = stack;
|
|
1607
|
+
entry.release = scope.onCancel(() => {
|
|
1608
|
+
if (!this.localActivities.delete(localActivityId)) return;
|
|
1609
|
+
reject(wrap(new CancelledFailure("Local activity cancelled")));
|
|
1610
|
+
});
|
|
1611
|
+
this.localActivities.set(localActivityId, entry);
|
|
1612
|
+
});
|
|
1025
1613
|
};
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
let backoffMs = 0;
|
|
1034
|
-
if (given !== void 0) {
|
|
1035
|
-
if (given.failure === void 0) return record(given);
|
|
1036
|
-
const decision = computeRetryDelay(retry, given.attempts, given.failure);
|
|
1037
|
-
if (!decision.retry) return record(given);
|
|
1038
|
-
attempt = given.attempts + 1;
|
|
1039
|
-
backoffMs = decision.delayMs;
|
|
1040
|
-
}
|
|
1041
|
-
const request = {
|
|
1614
|
+
if (this.outbound.length === 0) return proceed(args);
|
|
1615
|
+
const chain = compose(
|
|
1616
|
+
this.outbound,
|
|
1617
|
+
"scheduleLocalActivity",
|
|
1618
|
+
(input) => proceed(toJson(input.args, `The args of ${descriptor.nodeType}`))
|
|
1619
|
+
);
|
|
1620
|
+
return chain({
|
|
1042
1621
|
localActivityId,
|
|
1043
|
-
kind: "node",
|
|
1044
1622
|
nodeType: descriptor.nodeType,
|
|
1045
1623
|
operation: operationName,
|
|
1046
|
-
args
|
|
1047
|
-
retry,
|
|
1048
|
-
startToCloseTimeout: timeout,
|
|
1049
|
-
attempt,
|
|
1050
|
-
backoffMs
|
|
1051
|
-
};
|
|
1052
|
-
if (nodeVersion !== void 0) request.nodeVersion = nodeVersion;
|
|
1053
|
-
if (options.connection !== void 0) request.connection = options.connection;
|
|
1054
|
-
if (options.summary !== void 0) request.summary = options.summary;
|
|
1055
|
-
return new Promise((resolve, reject) => {
|
|
1056
|
-
const entry = {
|
|
1057
|
-
resolve,
|
|
1058
|
-
reject,
|
|
1059
|
-
description: label,
|
|
1060
|
-
request,
|
|
1061
|
-
replayed
|
|
1062
|
-
};
|
|
1063
|
-
const stack = this.stack();
|
|
1064
|
-
if (stack !== void 0) entry.stack = stack;
|
|
1065
|
-
entry.release = scope.onCancel(() => {
|
|
1066
|
-
if (!this.localActivities.delete(localActivityId)) return;
|
|
1067
|
-
reject(wrap(new CancelledFailure("Local activity cancelled")));
|
|
1068
|
-
});
|
|
1069
|
-
this.localActivities.set(localActivityId, entry);
|
|
1624
|
+
args
|
|
1070
1625
|
});
|
|
1071
1626
|
}
|
|
1072
1627
|
startTimer(scope, durationMs, summary) {
|
|
@@ -1077,7 +1632,7 @@ var WorkflowExecution = class {
|
|
|
1077
1632
|
if (durationMs <= 0) return { promise: Promise.resolve() };
|
|
1078
1633
|
if (scope.consideredCancelled)
|
|
1079
1634
|
return { promise: Promise.reject(new CancelledFailure("Scope is cancelled")) };
|
|
1080
|
-
const timerId = this.
|
|
1635
|
+
const timerId = this.nextId("t");
|
|
1081
1636
|
const command = {
|
|
1082
1637
|
type: "START_TIMER",
|
|
1083
1638
|
timerId,
|
|
@@ -1192,9 +1747,7 @@ var WorkflowExecution = class {
|
|
|
1192
1747
|
}
|
|
1193
1748
|
onSignal(signal, handler, options = {}) {
|
|
1194
1749
|
if (typeof handler !== "function") throw new TypeError("ctx.onSignal needs a handler function");
|
|
1195
|
-
const policy = options.unfinishedPolicy
|
|
1196
|
-
if (policy !== "WARN_AND_ABANDON" && policy !== "ABANDON")
|
|
1197
|
-
throw new TypeError("unfinishedPolicy must be WARN_AND_ABANDON or ABANDON");
|
|
1750
|
+
const policy = checkPolicy(options.unfinishedPolicy);
|
|
1198
1751
|
const name = signalName(signal);
|
|
1199
1752
|
this.signalHandlers.set(name, handler);
|
|
1200
1753
|
this.signalPolicies.set(name, policy);
|
|
@@ -1294,13 +1847,39 @@ var WorkflowExecution = class {
|
|
|
1294
1847
|
...opts
|
|
1295
1848
|
}
|
|
1296
1849
|
};
|
|
1850
|
+
checkFairness(options);
|
|
1851
|
+
if (options.fairnessKey !== void 0) command.options.fairnessKey = options.fairnessKey;
|
|
1852
|
+
if (options.fairnessWeight !== void 0)
|
|
1853
|
+
command.options.fairnessWeight = options.fairnessWeight;
|
|
1297
1854
|
if (options.version !== void 0) command.version = options.version;
|
|
1298
1855
|
if (options.headers !== void 0) command.headers = { ...options.headers };
|
|
1299
1856
|
return command;
|
|
1300
1857
|
}
|
|
1858
|
+
/**
|
|
1859
|
+
* Run the outbound `startChild` interceptors around a child start. `go`
|
|
1860
|
+
* starts the child with the options the interceptors leave (input and
|
|
1861
|
+
* headers).
|
|
1862
|
+
*/
|
|
1863
|
+
interceptChild(target, options, go) {
|
|
1864
|
+
if (this.outbound.length === 0) return go(options);
|
|
1865
|
+
const chain = compose(
|
|
1866
|
+
this.outbound,
|
|
1867
|
+
"startChild",
|
|
1868
|
+
(input) => go({
|
|
1869
|
+
...options,
|
|
1870
|
+
input: input.input,
|
|
1871
|
+
...options.headers !== void 0 || Object.keys(input.headers).length > 0 ? { headers: { ...input.headers } } : {}
|
|
1872
|
+
})
|
|
1873
|
+
);
|
|
1874
|
+
return chain({
|
|
1875
|
+
...this.childTarget(target),
|
|
1876
|
+
input: toJson(options.input, "The child input"),
|
|
1877
|
+
headers: { ...options.headers ?? {} }
|
|
1878
|
+
});
|
|
1879
|
+
}
|
|
1301
1880
|
startChildRun(scope, target, options, defaultPolicy) {
|
|
1302
1881
|
if (scope.consideredCancelled) return void 0;
|
|
1303
|
-
const childId = this.
|
|
1882
|
+
const childId = this.nextId("c");
|
|
1304
1883
|
const command = this.childCommand(childId, target, options, defaultPolicy);
|
|
1305
1884
|
if (!this.push(command)) return void 0;
|
|
1306
1885
|
this.issued.add(`child:${childId}`);
|
|
@@ -1325,7 +1904,7 @@ var WorkflowExecution = class {
|
|
|
1325
1904
|
entry.reject(error);
|
|
1326
1905
|
return;
|
|
1327
1906
|
}
|
|
1328
|
-
const externalId = this.
|
|
1907
|
+
const externalId = this.nextId("x");
|
|
1329
1908
|
if (this.push({
|
|
1330
1909
|
type: "REQUEST_CANCEL_EXTERNAL_RUN",
|
|
1331
1910
|
externalId,
|
|
@@ -1359,15 +1938,17 @@ var WorkflowExecution = class {
|
|
|
1359
1938
|
};
|
|
1360
1939
|
}
|
|
1361
1940
|
child(scope, target, options) {
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1941
|
+
return this.interceptChild(target, options, (opts) => {
|
|
1942
|
+
const child = this.startChildRun(
|
|
1943
|
+
scope,
|
|
1944
|
+
target,
|
|
1945
|
+
opts,
|
|
1946
|
+
CHILD_DEFAULT_PARENT_CLOSE_POLICY.child
|
|
1947
|
+
);
|
|
1948
|
+
if (!child)
|
|
1949
|
+
return scope.consideredCancelled ? Promise.reject(new CancelledFailure("Scope is cancelled")) : this.never();
|
|
1950
|
+
return this.childResult(child);
|
|
1951
|
+
});
|
|
1371
1952
|
}
|
|
1372
1953
|
childResult(child) {
|
|
1373
1954
|
if (child.settled && child.outcome) {
|
|
@@ -1387,6 +1968,13 @@ var WorkflowExecution = class {
|
|
|
1387
1968
|
});
|
|
1388
1969
|
}
|
|
1389
1970
|
startChild(scope, target, options) {
|
|
1971
|
+
return this.interceptChild(
|
|
1972
|
+
target,
|
|
1973
|
+
options,
|
|
1974
|
+
(opts) => this.startChildHandle(scope, target, opts)
|
|
1975
|
+
);
|
|
1976
|
+
}
|
|
1977
|
+
startChildHandle(scope, target, options) {
|
|
1390
1978
|
const child = this.startChildRun(
|
|
1391
1979
|
scope,
|
|
1392
1980
|
target,
|
|
@@ -1416,7 +2004,21 @@ var WorkflowExecution = class {
|
|
|
1416
2004
|
});
|
|
1417
2005
|
}
|
|
1418
2006
|
signalExternal(target, name, payload) {
|
|
1419
|
-
|
|
2007
|
+
if (this.outbound.length === 0) return this.pushSignal(target, name, payload, void 0);
|
|
2008
|
+
const chain = compose(
|
|
2009
|
+
this.outbound,
|
|
2010
|
+
"signalRun",
|
|
2011
|
+
(input) => this.pushSignal(input.target, input.name, input.payload, input.headers)
|
|
2012
|
+
);
|
|
2013
|
+
return chain({
|
|
2014
|
+
target,
|
|
2015
|
+
name,
|
|
2016
|
+
payload: toJson(payload, `The payload of signal ${name}`),
|
|
2017
|
+
headers: {}
|
|
2018
|
+
});
|
|
2019
|
+
}
|
|
2020
|
+
pushSignal(target, name, payload, headers) {
|
|
2021
|
+
const externalId = this.nextId("x");
|
|
1420
2022
|
const command = {
|
|
1421
2023
|
type: "SIGNAL_RUN",
|
|
1422
2024
|
externalId,
|
|
@@ -1424,6 +2026,7 @@ var WorkflowExecution = class {
|
|
|
1424
2026
|
name,
|
|
1425
2027
|
payload: toJson(payload, `The payload of signal ${name}`)
|
|
1426
2028
|
};
|
|
2029
|
+
if (headers !== void 0 && Object.keys(headers).length > 0) command.headers = { ...headers };
|
|
1427
2030
|
if (!this.push(command)) return this.never();
|
|
1428
2031
|
this.issued.add(`external:${externalId}`);
|
|
1429
2032
|
return new Promise((resolve, reject) => {
|
|
@@ -1438,7 +2041,15 @@ var WorkflowExecution = class {
|
|
|
1438
2041
|
});
|
|
1439
2042
|
}
|
|
1440
2043
|
cancelExternal(target) {
|
|
1441
|
-
|
|
2044
|
+
if (this.outbound.length === 0) return this.pushCancel(target);
|
|
2045
|
+
return compose(
|
|
2046
|
+
this.outbound,
|
|
2047
|
+
"cancelRun",
|
|
2048
|
+
(input) => this.pushCancel(input.target)
|
|
2049
|
+
)({ target });
|
|
2050
|
+
}
|
|
2051
|
+
pushCancel(target) {
|
|
2052
|
+
const externalId = this.nextId("x");
|
|
1442
2053
|
if (!this.push({ type: "REQUEST_CANCEL_EXTERNAL_RUN", externalId, target }))
|
|
1443
2054
|
return this.never();
|
|
1444
2055
|
this.issued.add(`external:${externalId}`);
|
|
@@ -1454,7 +2065,15 @@ var WorkflowExecution = class {
|
|
|
1454
2065
|
});
|
|
1455
2066
|
}
|
|
1456
2067
|
sideEffect(fn) {
|
|
1457
|
-
|
|
2068
|
+
if (this.outbound.length === 0) return this.recordSideEffect(fn);
|
|
2069
|
+
return compose(
|
|
2070
|
+
this.outbound,
|
|
2071
|
+
"sideEffect",
|
|
2072
|
+
() => this.recordSideEffect(fn)
|
|
2073
|
+
)({});
|
|
2074
|
+
}
|
|
2075
|
+
recordSideEffect(fn) {
|
|
2076
|
+
const sideEffectId = this.nextId("s");
|
|
1458
2077
|
let value;
|
|
1459
2078
|
if (this.replaying && this.recordedSideEffects.has(sideEffectId)) {
|
|
1460
2079
|
value = this.recordedSideEffects.get(sideEffectId) ?? null;
|
|
@@ -1468,6 +2087,7 @@ var WorkflowExecution = class {
|
|
|
1468
2087
|
if (typeof patchId !== "string" || patchId === "") throw new TypeError("A patch needs an id");
|
|
1469
2088
|
const known = this.patches.get(patchId);
|
|
1470
2089
|
if (known !== void 0) return known;
|
|
2090
|
+
this.assertWritable(deprecated ? "ctx.deprecatePatch()" : "ctx.patched()");
|
|
1471
2091
|
const markerId = patchMarkerId(patchId);
|
|
1472
2092
|
let result;
|
|
1473
2093
|
if (this.replaying) {
|
|
@@ -1494,17 +2114,113 @@ var WorkflowExecution = class {
|
|
|
1494
2114
|
}
|
|
1495
2115
|
setCurrentDetails(markdown) {
|
|
1496
2116
|
if (typeof markdown !== "string") throw new TypeError("setCurrentDetails takes Markdown text");
|
|
2117
|
+
this.assertWritable("ctx.setCurrentDetails()");
|
|
1497
2118
|
this.currentDetails = markdown;
|
|
1498
2119
|
this.push({
|
|
1499
2120
|
type: "RECORD_MARKER",
|
|
1500
|
-
markerId: this.
|
|
2121
|
+
markerId: this.nextId("m"),
|
|
1501
2122
|
data: { currentDetails: markdown }
|
|
1502
2123
|
});
|
|
1503
2124
|
}
|
|
1504
2125
|
upsertContext(entries) {
|
|
1505
2126
|
const json = toJson(entries, "The context entries");
|
|
1506
2127
|
if (!isJsonObject(json)) throw new TypeError("ctx.upsertContext takes an object");
|
|
1507
|
-
|
|
2128
|
+
const apply = (input) => {
|
|
2129
|
+
const final = toJson(input.entries, "The context entries");
|
|
2130
|
+
if (!isJsonObject(final)) throw new TypeError("ctx.upsertContext takes an object");
|
|
2131
|
+
if (this.push({ type: "UPSERT_CONTEXT", entries: final }))
|
|
2132
|
+
this.memo = { ...this.memo, ...final };
|
|
2133
|
+
};
|
|
2134
|
+
if (this.outbound.length === 0) apply({ entries: json });
|
|
2135
|
+
else
|
|
2136
|
+
compose(
|
|
2137
|
+
this.outbound,
|
|
2138
|
+
"upsertContext",
|
|
2139
|
+
apply
|
|
2140
|
+
)({ entries: json });
|
|
2141
|
+
}
|
|
2142
|
+
/** `ctx.upsertSearchAttributes` (Phase C): a null value removes an attribute. */
|
|
2143
|
+
upsertSearchAttributes(attributes) {
|
|
2144
|
+
const json = toJson(attributes, "The search attributes");
|
|
2145
|
+
if (!isJsonObject(json) || Object.keys(json).length === 0)
|
|
2146
|
+
throw new TypeError("ctx.upsertSearchAttributes takes an object with at least one attribute");
|
|
2147
|
+
for (const key of Object.keys(json))
|
|
2148
|
+
if (key.trim() === "") throw new TypeError("A search attribute needs a name");
|
|
2149
|
+
const apply = (input) => {
|
|
2150
|
+
const final = toJson(input.attributes, "The search attributes");
|
|
2151
|
+
if (!isJsonObject(final)) throw new TypeError("ctx.upsertSearchAttributes takes an object");
|
|
2152
|
+
if (this.push({ type: "UPSERT_SEARCH_ATTRIBUTES", attributes: final }))
|
|
2153
|
+
this.searchAttributes = mergeSearchAttributes(this.searchAttributes, final);
|
|
2154
|
+
};
|
|
2155
|
+
if (this.outbound.length === 0) apply({ attributes: json });
|
|
2156
|
+
else
|
|
2157
|
+
compose(
|
|
2158
|
+
this.outbound,
|
|
2159
|
+
"upsertSearchAttributes",
|
|
2160
|
+
apply
|
|
2161
|
+
)({ attributes: json });
|
|
2162
|
+
}
|
|
2163
|
+
/** A `ctx.sinks.<sink>.<fn>(...args)` call: recorded once, never on replay (11 §2.31). */
|
|
2164
|
+
callSink(sink, fn, args) {
|
|
2165
|
+
this.assertWritable(`The sink call ${sink}.${fn}`);
|
|
2166
|
+
if (this.replaying || this.closed || this.failure || this.request.kind !== "task") return;
|
|
2167
|
+
if (this.sinkCalls.length >= MAX_SINK_CALLS) {
|
|
2168
|
+
if (!this.sinkCallsDropped) {
|
|
2169
|
+
this.sinkCallsDropped = true;
|
|
2170
|
+
this.logs.push({
|
|
2171
|
+
level: "warn",
|
|
2172
|
+
message: `More than ${MAX_SINK_CALLS} sink calls in one workflow task; the rest were dropped`
|
|
2173
|
+
});
|
|
2174
|
+
}
|
|
2175
|
+
return;
|
|
2176
|
+
}
|
|
2177
|
+
this.sinkCalls.push({
|
|
2178
|
+
sink,
|
|
2179
|
+
fn,
|
|
2180
|
+
args: args.map((arg, index) => toJson(arg, `Argument ${index + 1} of ${sink}.${fn}`))
|
|
2181
|
+
});
|
|
2182
|
+
}
|
|
2183
|
+
sinksProxy() {
|
|
2184
|
+
const execution = this;
|
|
2185
|
+
const sinkOf = (sink) => new Proxy(
|
|
2186
|
+
{},
|
|
2187
|
+
{
|
|
2188
|
+
get(_target, fn) {
|
|
2189
|
+
if (typeof fn !== "string" || fn === "then") return void 0;
|
|
2190
|
+
return (...args) => execution.callSink(sink, fn, args);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
);
|
|
2194
|
+
return new Proxy(
|
|
2195
|
+
{},
|
|
2196
|
+
{
|
|
2197
|
+
get(_target, sink) {
|
|
2198
|
+
if (typeof sink !== "string" || sink === "then") return void 0;
|
|
2199
|
+
return sinkOf(sink);
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
);
|
|
2203
|
+
}
|
|
2204
|
+
/** `ctx.continueAsNew`: a rejection with the request, after the outbound interceptors. */
|
|
2205
|
+
continueAsNewCall(input, options) {
|
|
2206
|
+
let request;
|
|
2207
|
+
try {
|
|
2208
|
+
request = this.continueAsNew(input, options);
|
|
2209
|
+
} catch (error) {
|
|
2210
|
+
return Promise.reject(error);
|
|
2211
|
+
}
|
|
2212
|
+
if (this.outbound.length === 0) return Promise.reject(request);
|
|
2213
|
+
return compose(
|
|
2214
|
+
this.outbound,
|
|
2215
|
+
"continueAsNew",
|
|
2216
|
+
(changed) => Promise.reject(
|
|
2217
|
+
new ContinueAsNewRequest(
|
|
2218
|
+
toJson(changed.input, "The continue-as-new input"),
|
|
2219
|
+
changed.options,
|
|
2220
|
+
headersOf(changed.headers)
|
|
2221
|
+
)
|
|
2222
|
+
)
|
|
2223
|
+
)({ input: request.input, options: { ...request.options }, headers: {} });
|
|
1508
2224
|
}
|
|
1509
2225
|
/** The request a `ctx.continueAsNew` call throws; the run ends with `CONTINUE_AS_NEW`. */
|
|
1510
2226
|
continueAsNew(input, options) {
|
|
@@ -1578,13 +2294,49 @@ var WorkflowExecution = class {
|
|
|
1578
2294
|
);
|
|
1579
2295
|
return out;
|
|
1580
2296
|
}
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
2297
|
+
/**
|
|
2298
|
+
* Answer a custom query (Phase C): the handler registered for the name, else
|
|
2299
|
+
* the default handler, run read-only through the inbound `handleQuery`
|
|
2300
|
+
* interceptors. No handler gives `rejected`.
|
|
2301
|
+
*/
|
|
2302
|
+
answerCustomQuery(query) {
|
|
2303
|
+
if (this.failure)
|
|
2304
|
+
return { queryId: query.queryId, error: `The replay failed: ${this.failure.message}` };
|
|
2305
|
+
const own = this.queryHandlers.get(query.name);
|
|
2306
|
+
const fallback = this.defaultQueryHandler;
|
|
2307
|
+
const handler = own ?? (fallback !== void 0 ? (args) => fallback(query.name, args) : void 0);
|
|
2308
|
+
if (handler === void 0)
|
|
2309
|
+
return {
|
|
2310
|
+
queryId: query.queryId,
|
|
2311
|
+
rejected: true,
|
|
2312
|
+
error: `The workflow has no handler for query "${query.name}"`
|
|
2313
|
+
};
|
|
2314
|
+
const chain = compose(
|
|
2315
|
+
this.inbound,
|
|
2316
|
+
"handleQuery",
|
|
2317
|
+
(input) => handler(input.args)
|
|
2318
|
+
);
|
|
2319
|
+
try {
|
|
2320
|
+
const value = this.readOnlyCall(
|
|
2321
|
+
`the handler of query ${query.name}`,
|
|
2322
|
+
() => chain({ queryId: query.queryId, name: query.name, args: query.args ?? null })
|
|
2323
|
+
);
|
|
2324
|
+
if (typeof value === "object" && value !== null && typeof value.then === "function")
|
|
1584
2325
|
return {
|
|
1585
2326
|
queryId: query.queryId,
|
|
1586
|
-
error: `
|
|
2327
|
+
error: `The handler of query "${query.name}" returned a promise; query handlers must return a value`
|
|
1587
2328
|
};
|
|
2329
|
+
return { queryId: query.queryId, value: toJson(value, `The result of query ${query.name}`) };
|
|
2330
|
+
} catch (error) {
|
|
2331
|
+
return {
|
|
2332
|
+
queryId: query.queryId,
|
|
2333
|
+
error: `${errorName(error) || "Error"}: ${messageOf(error)}`
|
|
2334
|
+
};
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
answerQueries() {
|
|
2338
|
+
return (this.request.queries ?? []).map((query) => {
|
|
2339
|
+
if (!isBuiltInQuery(query.name)) return this.answerCustomQuery(query);
|
|
1588
2340
|
if (this.failure)
|
|
1589
2341
|
return { queryId: query.queryId, error: `The replay failed: ${this.failure.message}` };
|
|
1590
2342
|
if (query.name === "__stack_trace")
|
|
@@ -1632,9 +2384,20 @@ ${entry.stack}`).join("\n\n")
|
|
|
1632
2384
|
contextFor(bound) {
|
|
1633
2385
|
const scope = () => bound ?? this.currentScope();
|
|
1634
2386
|
const log = (level) => (message, data) => {
|
|
1635
|
-
if (this.replaying) return;
|
|
2387
|
+
if (this.replaying || this.readOnly !== void 0) return;
|
|
1636
2388
|
const line = { level, message: String(message) };
|
|
1637
|
-
|
|
2389
|
+
const payload = data !== void 0 ? toJson(data, "The log data") : void 0;
|
|
2390
|
+
const attributes = this.outbound.length === 0 ? {} : compose(
|
|
2391
|
+
this.outbound,
|
|
2392
|
+
"getLogAttributes",
|
|
2393
|
+
(input) => input
|
|
2394
|
+
)({});
|
|
2395
|
+
if (Object.keys(attributes).length === 0) {
|
|
2396
|
+
if (payload !== void 0) line.data = payload;
|
|
2397
|
+
} else {
|
|
2398
|
+
const extra = toJson(attributes, "The log attributes");
|
|
2399
|
+
line.data = payload === void 0 ? extra : isJsonObject(payload) && isJsonObject(extra) ? { ...extra, ...payload } : { ...isJsonObject(extra) ? extra : {}, data: payload };
|
|
2400
|
+
}
|
|
1638
2401
|
this.logs.push(line);
|
|
1639
2402
|
};
|
|
1640
2403
|
const logger = {
|
|
@@ -1645,14 +2408,21 @@ ${entry.stack}`).join("\n\n")
|
|
|
1645
2408
|
};
|
|
1646
2409
|
const scopeFn = ((fn, options = {}) => this.cancellationScope(scope(), fn, options));
|
|
1647
2410
|
scopeFn.current = () => scope();
|
|
1648
|
-
const phaseC = (feature) => () => {
|
|
1649
|
-
throw new PhaseCFeatureError(feature);
|
|
1650
|
-
};
|
|
1651
2411
|
const context = {
|
|
1652
2412
|
info: this.info,
|
|
1653
2413
|
log: logger,
|
|
1654
2414
|
run: (operation, options) => this.scheduleActivity(scope(), operation, options),
|
|
1655
|
-
sleep: (duration, options = {}) =>
|
|
2415
|
+
sleep: (duration, options = {}) => {
|
|
2416
|
+
const durationMs = toMs(duration, "sleep duration");
|
|
2417
|
+
if (this.outbound.length === 0)
|
|
2418
|
+
return this.startTimer(scope(), durationMs, options.summary);
|
|
2419
|
+
const current = scope();
|
|
2420
|
+
return compose(
|
|
2421
|
+
this.outbound,
|
|
2422
|
+
"startTimer",
|
|
2423
|
+
(input) => this.startTimer(current, toMs(input.durationMs, "sleep duration"), input.summary)
|
|
2424
|
+
)({ durationMs, ...options.summary !== void 0 ? { summary: options.summary } : {} });
|
|
2425
|
+
},
|
|
1656
2426
|
condition: (predicate, timeout) => this.condition(scope(), predicate, timeout),
|
|
1657
2427
|
signalReceived: (signal) => this.signalReceived(scope(), signal),
|
|
1658
2428
|
onSignal: ((signal, optionsOrHandler, maybeHandler) => {
|
|
@@ -1670,7 +2440,10 @@ ${entry.stack}`).join("\n\n")
|
|
|
1670
2440
|
cancelRun: (target) => this.cancelExternal(this.externalTarget(target)),
|
|
1671
2441
|
now: () => this.clock,
|
|
1672
2442
|
random: () => this.random(),
|
|
1673
|
-
uuid: () =>
|
|
2443
|
+
uuid: () => {
|
|
2444
|
+
this.assertWritable("ctx.uuid()");
|
|
2445
|
+
return uuidFrom(this.randomSource);
|
|
2446
|
+
},
|
|
1674
2447
|
sideEffect: (fn) => this.sideEffect(fn),
|
|
1675
2448
|
patched: (patchId) => this.patched(patchId, false),
|
|
1676
2449
|
deprecatePatch: (patchId) => {
|
|
@@ -1678,13 +2451,7 @@ ${entry.stack}`).join("\n\n")
|
|
|
1678
2451
|
},
|
|
1679
2452
|
setCurrentDetails: (markdown) => this.setCurrentDetails(markdown),
|
|
1680
2453
|
upsertContext: (entries) => this.upsertContext(entries),
|
|
1681
|
-
continueAsNew: (input, options = {}) =>
|
|
1682
|
-
try {
|
|
1683
|
-
return Promise.reject(this.continueAsNew(input, options));
|
|
1684
|
-
} catch (error) {
|
|
1685
|
-
return Promise.reject(error);
|
|
1686
|
-
}
|
|
1687
|
-
},
|
|
2454
|
+
continueAsNew: (input, options = {}) => this.continueAsNewCall(input, options),
|
|
1688
2455
|
cancellationScope: scopeFn,
|
|
1689
2456
|
nonCancellable: (fn) => this.cancellationScope(scope(), fn, { nonCancellable: true }),
|
|
1690
2457
|
onCancel: (fn) => {
|
|
@@ -1720,11 +2487,14 @@ ${entry.stack}`).join("\n\n")
|
|
|
1720
2487
|
};
|
|
1721
2488
|
},
|
|
1722
2489
|
allHandlersFinished: () => this.allHandlersFinished(),
|
|
1723
|
-
activity:
|
|
2490
|
+
activity: (activity, options) => this.userActivity(scope(), activity, options),
|
|
1724
2491
|
localActivity: ((operation, options) => this.localActivity(scope(), operation, options)),
|
|
1725
|
-
handleQuery:
|
|
1726
|
-
|
|
1727
|
-
|
|
2492
|
+
handleQuery: ((query, handler) => this.handleQuery(query, handler)),
|
|
2493
|
+
setDefaultQueryHandler: (handler) => this.setDefaultQueryHandler(handler),
|
|
2494
|
+
handleUpdate: ((update, handler, options) => this.handleUpdate(update, handler, options)),
|
|
2495
|
+
setDefaultUpdateHandler: ((first, second) => this.setDefaultUpdateHandler(first, second)),
|
|
2496
|
+
upsertSearchAttributes: (attributes) => this.upsertSearchAttributes(attributes),
|
|
2497
|
+
sinks: this.sinksProxy()
|
|
1728
2498
|
};
|
|
1729
2499
|
return Object.freeze(context);
|
|
1730
2500
|
}
|
|
@@ -1736,17 +2506,36 @@ function buildWorkflowRegistry(modules) {
|
|
|
1736
2506
|
const byName = /* @__PURE__ */ new Map();
|
|
1737
2507
|
const exportNames = /* @__PURE__ */ new Map();
|
|
1738
2508
|
const ambiguous = /* @__PURE__ */ new Set();
|
|
2509
|
+
const interceptors = [];
|
|
2510
|
+
let dynamic;
|
|
2511
|
+
let dynamicAmbiguous = false;
|
|
1739
2512
|
for (const [, namespace] of modules) {
|
|
1740
2513
|
for (const exportName of Object.keys(namespace).sort()) {
|
|
1741
2514
|
const value = namespace[exportName];
|
|
2515
|
+
if (exportName === "interceptors") {
|
|
2516
|
+
const factory = interceptorsFactoryOf(value);
|
|
2517
|
+
if (factory !== void 0 && !interceptors.includes(factory)) interceptors.push(factory);
|
|
2518
|
+
continue;
|
|
2519
|
+
}
|
|
1742
2520
|
if (!isDurableWorkflow(value)) continue;
|
|
1743
2521
|
const existing = byName.get(exportName);
|
|
1744
2522
|
if (existing && existing !== value) ambiguous.add(exportName);
|
|
1745
2523
|
byName.set(exportName, value);
|
|
1746
2524
|
if (!exportNames.has(value)) exportNames.set(value, exportName);
|
|
2525
|
+
if (value.dynamic === true) {
|
|
2526
|
+
if (dynamic !== void 0 && dynamic !== value) dynamicAmbiguous = true;
|
|
2527
|
+
dynamic ??= value;
|
|
2528
|
+
}
|
|
1747
2529
|
}
|
|
1748
2530
|
}
|
|
1749
|
-
return {
|
|
2531
|
+
return {
|
|
2532
|
+
byName,
|
|
2533
|
+
exportNames,
|
|
2534
|
+
ambiguous,
|
|
2535
|
+
...dynamic !== void 0 ? { dynamic } : {},
|
|
2536
|
+
...dynamicAmbiguous ? { dynamicAmbiguous } : {},
|
|
2537
|
+
...interceptors.length > 0 ? { interceptors } : {}
|
|
2538
|
+
};
|
|
1750
2539
|
}
|
|
1751
2540
|
function failedResult(cause, message) {
|
|
1752
2541
|
return {
|
|
@@ -1775,6 +2564,23 @@ function checkRequest(value) {
|
|
|
1775
2564
|
}
|
|
1776
2565
|
if (typeof value["nowMs"] !== "number" || !Number.isFinite(value["nowMs"]))
|
|
1777
2566
|
return "nowMs must be a number";
|
|
2567
|
+
const updates = value["updates"];
|
|
2568
|
+
if (updates !== void 0) {
|
|
2569
|
+
if (!Array.isArray(updates)) return "updates must be an array";
|
|
2570
|
+
for (const [index, update] of updates.entries())
|
|
2571
|
+
if (!isObject(update) || typeof update["updateId"] !== "string" || update["updateId"] === "" || typeof update["name"] !== "string")
|
|
2572
|
+
return `updates[${index}] needs an updateId and a name`;
|
|
2573
|
+
}
|
|
2574
|
+
const queries = value["queries"];
|
|
2575
|
+
if (queries !== void 0) {
|
|
2576
|
+
if (!Array.isArray(queries)) return "queries must be an array";
|
|
2577
|
+
for (const [index, query] of queries.entries())
|
|
2578
|
+
if (!isObject(query) || typeof query["queryId"] !== "string" || typeof query["name"] !== "string")
|
|
2579
|
+
return `queries[${index}] needs a queryId and a name`;
|
|
2580
|
+
}
|
|
2581
|
+
const features = value["features"];
|
|
2582
|
+
if (features !== void 0 && (!Array.isArray(features) || !features.every((feature) => typeof feature === "string")))
|
|
2583
|
+
return "features must be an array of strings";
|
|
1778
2584
|
return value;
|
|
1779
2585
|
}
|
|
1780
2586
|
function createWorkflowRuntime(registry, shims = void 0) {
|
|
@@ -1791,6 +2597,7 @@ function createWorkflowRuntime(registry, shims = void 0) {
|
|
|
1791
2597
|
return Object.freeze({
|
|
1792
2598
|
protocol: HISTORY_PROTOCOL,
|
|
1793
2599
|
sdkVersion: SDK_VERSION,
|
|
2600
|
+
capabilities: Object.freeze([...RUNTIME_CAPABILITIES]),
|
|
1794
2601
|
start(requestJson) {
|
|
1795
2602
|
shims?.detach();
|
|
1796
2603
|
execution = void 0;
|
|
@@ -1824,7 +2631,15 @@ function createWorkflowRuntime(registry, shims = void 0) {
|
|
|
1824
2631
|
);
|
|
1825
2632
|
return;
|
|
1826
2633
|
}
|
|
1827
|
-
const
|
|
2634
|
+
const exported = registry.byName.get(request.workflowFn);
|
|
2635
|
+
if (!exported && registry.dynamic !== void 0 && registry.dynamicAmbiguous === true) {
|
|
2636
|
+
early = failedResult(
|
|
2637
|
+
"INVALID_REQUEST",
|
|
2638
|
+
`More than one module exports a dynamic workflow; "${request.workflowFn}" cannot be served`
|
|
2639
|
+
);
|
|
2640
|
+
return;
|
|
2641
|
+
}
|
|
2642
|
+
const definition = exported ?? registry.dynamic;
|
|
1828
2643
|
if (!definition) {
|
|
1829
2644
|
early = failedResult(
|
|
1830
2645
|
"INVALID_REQUEST",
|
|
@@ -1837,7 +2652,8 @@ function createWorkflowRuntime(registry, shims = void 0) {
|
|
|
1837
2652
|
definition,
|
|
1838
2653
|
exportNames: registry.exportNames,
|
|
1839
2654
|
request,
|
|
1840
|
-
sdkVersion: SDK_VERSION
|
|
2655
|
+
sdkVersion: SDK_VERSION,
|
|
2656
|
+
...registry.interceptors !== void 0 ? { interceptors: registry.interceptors } : {}
|
|
1841
2657
|
});
|
|
1842
2658
|
execution = created;
|
|
1843
2659
|
shims?.attach({ now: () => created.now(), random: () => created.nextRandom() });
|