@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
|
@@ -1,13 +1,22 @@
|
|
|
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
|
+
import { captureStack, parseStack } from './chunk-M272AHEX.js';
|
|
2
3
|
import { createRandom, seedOf, takeShimControl, uuidFrom } from './chunk-4LYSEANA.js';
|
|
3
|
-
import { isDurableWorkflow, SDK_VERSION } from './chunk-
|
|
4
|
-
import { utf8Bytes, splitActivations,
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
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';
|
|
7
8
|
|
|
8
9
|
// src/runtime/ids.ts
|
|
9
10
|
var CommandIds = class {
|
|
10
|
-
counters = {
|
|
11
|
+
counters = {
|
|
12
|
+
a: 0,
|
|
13
|
+
t: 0,
|
|
14
|
+
c: 0,
|
|
15
|
+
s: 0,
|
|
16
|
+
x: 0,
|
|
17
|
+
m: 0,
|
|
18
|
+
l: 0
|
|
19
|
+
};
|
|
11
20
|
/** The next id of a kind: `a1`, `a2`, ... */
|
|
12
21
|
next(kind) {
|
|
13
22
|
this.counters[kind] += 1;
|
|
@@ -28,7 +37,7 @@ function field(event, key) {
|
|
|
28
37
|
function describeCommand(command) {
|
|
29
38
|
switch (command.type) {
|
|
30
39
|
case "SCHEDULE_ACTIVITY":
|
|
31
|
-
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})`;
|
|
32
41
|
case "REQUEST_CANCEL_ACTIVITY":
|
|
33
42
|
return `REQUEST_CANCEL_ACTIVITY ${command.activityId}`;
|
|
34
43
|
case "START_TIMER":
|
|
@@ -45,13 +54,17 @@ function describeCommand(command) {
|
|
|
45
54
|
case "RECORD_MARKER":
|
|
46
55
|
return `RECORD_MARKER ${command.markerId}`;
|
|
47
56
|
case "RECORD_LOCAL_ACTIVITY":
|
|
48
|
-
return `RECORD_LOCAL_ACTIVITY ${command.localActivityId}`;
|
|
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}`;
|
|
49
62
|
default:
|
|
50
63
|
return command.type;
|
|
51
64
|
}
|
|
52
65
|
}
|
|
53
66
|
function describeEvent(event) {
|
|
54
|
-
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");
|
|
55
68
|
return `${event.type}${typeof id === "string" ? ` ${id}` : ""} (seq ${event.seq})`;
|
|
56
69
|
}
|
|
57
70
|
function mismatch(command, event) {
|
|
@@ -60,8 +73,13 @@ function mismatch(command, event) {
|
|
|
60
73
|
const differ = () => `the code issued ${describeCommand(command)} where the history has ${describeEvent(event)}`;
|
|
61
74
|
if (!typeOk) return differ();
|
|
62
75
|
switch (command.type) {
|
|
63
|
-
case "SCHEDULE_ACTIVITY":
|
|
64
|
-
|
|
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
|
+
}
|
|
65
83
|
case "REQUEST_CANCEL_ACTIVITY":
|
|
66
84
|
return field(event, "activityId") === command.activityId ? void 0 : differ();
|
|
67
85
|
case "START_TIMER":
|
|
@@ -77,8 +95,13 @@ function mismatch(command, event) {
|
|
|
77
95
|
return field(event, "sideEffectId") === command.sideEffectId ? void 0 : differ();
|
|
78
96
|
case "RECORD_MARKER":
|
|
79
97
|
return field(event, "markerId") === command.markerId ? void 0 : differ();
|
|
80
|
-
case "
|
|
81
|
-
|
|
98
|
+
case "ACCEPT_UPDATE":
|
|
99
|
+
case "COMPLETE_UPDATE":
|
|
100
|
+
return field(event, "updateId") === command.updateId ? void 0 : differ();
|
|
101
|
+
case "RECORD_LOCAL_ACTIVITY": {
|
|
102
|
+
const sameOperation = field(event, "nodeType") === void 0 || field(event, "nodeType") === command.nodeType && field(event, "operation") === command.operation;
|
|
103
|
+
return field(event, "localActivityId") === command.localActivityId && sameOperation ? void 0 : differ();
|
|
104
|
+
}
|
|
82
105
|
default:
|
|
83
106
|
return void 0;
|
|
84
107
|
}
|
|
@@ -90,6 +113,7 @@ function isSkippablePatch(event) {
|
|
|
90
113
|
function matchActivation(commands, recorded) {
|
|
91
114
|
let position = 0;
|
|
92
115
|
for (const command of commands) {
|
|
116
|
+
if (COMMAND_EVENT_TYPES[command.type] === null) continue;
|
|
93
117
|
let event = recorded[position];
|
|
94
118
|
while (event !== void 0 && isSkippablePatch(event) && !(command.type === "RECORD_MARKER" && command.markerId === field(event, "markerId"))) {
|
|
95
119
|
position += 1;
|
|
@@ -114,40 +138,36 @@ function matchActivation(commands, recorded) {
|
|
|
114
138
|
return void 0;
|
|
115
139
|
}
|
|
116
140
|
|
|
117
|
-
// src/runtime/stack.ts
|
|
118
|
-
var RUNTIME_FRAME = /fluidgrids-runtime|\/runtime\/(scheduler|index)\.|WorkflowExecution|RuntimeContext/;
|
|
119
|
-
function captureStack() {
|
|
120
|
-
const stack = new Error("stack").stack ?? "";
|
|
121
|
-
return stack.split("\n").slice(1).filter((line) => line.trim() !== "" && !RUNTIME_FRAME.test(line)).join("\n");
|
|
122
|
-
}
|
|
123
|
-
var V8_FRAME = /^\s*at (?:(.*?) \()?(.*?):(\d+):(\d+)\)?$/;
|
|
124
|
-
var QUICKJS_FRAME = /^\s*at (?:(.*?) )?\(?(.*?)(?::(\d+))?(?::(\d+))?\)?$/;
|
|
125
|
-
function parseStack(stack) {
|
|
126
|
-
const locations = [];
|
|
127
|
-
for (const line of stack.split("\n")) {
|
|
128
|
-
if (line.trim() === "") continue;
|
|
129
|
-
const match = V8_FRAME.exec(line) ?? QUICKJS_FRAME.exec(line);
|
|
130
|
-
if (!match) continue;
|
|
131
|
-
const location = {};
|
|
132
|
-
if (match[1]) location.functionName = match[1];
|
|
133
|
-
if (match[2]) location.filePath = match[2];
|
|
134
|
-
if (match[3]) location.line = Number(match[3]);
|
|
135
|
-
if (match[4]) location.column = Number(match[4]);
|
|
136
|
-
locations.push(location);
|
|
137
|
-
}
|
|
138
|
-
return locations;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
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
|
+
}
|
|
142
154
|
var SDK_NAME = "@fluidgrids/workflows";
|
|
143
155
|
var ContinueAsNewRequest = class {
|
|
144
|
-
constructor(input, options) {
|
|
156
|
+
constructor(input, options, headers = {}) {
|
|
145
157
|
this.input = input;
|
|
146
158
|
this.options = options;
|
|
159
|
+
this.headers = headers;
|
|
147
160
|
}
|
|
148
161
|
input;
|
|
149
162
|
options;
|
|
163
|
+
headers;
|
|
150
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
|
+
}
|
|
151
171
|
var TaskFailure = class {
|
|
152
172
|
constructor(cause, message, stackTrace, seq, errorType) {
|
|
153
173
|
this.cause = cause;
|
|
@@ -222,6 +242,32 @@ var Scope = class {
|
|
|
222
242
|
this.parent?.children.delete(this);
|
|
223
243
|
}
|
|
224
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
|
+
}
|
|
225
271
|
function toJson(value, what) {
|
|
226
272
|
if (value === void 0) return null;
|
|
227
273
|
if (typeof value === "function" || typeof value === "symbol")
|
|
@@ -251,6 +297,54 @@ function messageOf(error) {
|
|
|
251
297
|
function stackTextOf(error) {
|
|
252
298
|
return error instanceof Error && typeof error.stack === "string" ? error.stack : void 0;
|
|
253
299
|
}
|
|
300
|
+
function isJsonObject(value) {
|
|
301
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
302
|
+
}
|
|
303
|
+
function asFailure(value) {
|
|
304
|
+
return isJsonObject(value) ? value : void 0;
|
|
305
|
+
}
|
|
306
|
+
function infoFromStarted(info, started) {
|
|
307
|
+
if (started?.type !== "WORKFLOW_STARTED") return { ...info };
|
|
308
|
+
const p = started.payload;
|
|
309
|
+
const out = { ...info };
|
|
310
|
+
if (Object.hasOwn(p, "lastCompletionResult")) out.lastResult = p["lastCompletionResult"] ?? null;
|
|
311
|
+
if (Object.hasOwn(p, "lastFailure")) out.lastFailure = asFailure(p["lastFailure"]) ?? null;
|
|
312
|
+
const businessId = typeof p["businessId"] === "string" ? p["businessId"] : void 0;
|
|
313
|
+
const workflowId = typeof p["workflowId"] === "string" ? p["workflowId"] : businessId;
|
|
314
|
+
if (businessId !== void 0) out.businessId = businessId;
|
|
315
|
+
if (workflowId !== void 0) out.workflowId = workflowId;
|
|
316
|
+
if (typeof p["runAttempt"] === "number") {
|
|
317
|
+
out.runAttempt = p["runAttempt"];
|
|
318
|
+
out.attempt = p["runAttempt"];
|
|
319
|
+
}
|
|
320
|
+
if (typeof p["summary"] === "string") out.summary = p["summary"];
|
|
321
|
+
if (typeof p["details"] === "string") out.details = p["details"];
|
|
322
|
+
if (isJsonObject(p["context"])) {
|
|
323
|
+
out.context = { ...p["context"] };
|
|
324
|
+
out.memo = { ...p["context"] };
|
|
325
|
+
}
|
|
326
|
+
const timeouts = {};
|
|
327
|
+
const nested = isJsonObject(p["timeouts"]) ? p["timeouts"] : {};
|
|
328
|
+
for (const key of ["executionTimeout", "runTimeout", "workflowTaskTimeout"]) {
|
|
329
|
+
const value = nested[key] ?? p[key];
|
|
330
|
+
if (typeof value === "number") timeouts[key] = value;
|
|
331
|
+
}
|
|
332
|
+
if (Object.keys(timeouts).length > 0) out.timeouts = timeouts;
|
|
333
|
+
if (p["versioningBehavior"] === "PINNED" || p["versioningBehavior"] === "AUTO_UPGRADE")
|
|
334
|
+
out.versioningBehavior = p["versioningBehavior"];
|
|
335
|
+
if (isJsonObject(p["retry"])) out.retry = p["retry"];
|
|
336
|
+
if (typeof p["priority"] === "number") out.priority = p["priority"];
|
|
337
|
+
if (typeof p["firstRunId"] === "string") out.firstRunId = p["firstRunId"];
|
|
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"];
|
|
344
|
+
if (out.workflowId === void 0 && out.businessId !== void 0) out.workflowId = out.businessId;
|
|
345
|
+
if (out.runAttempt === void 0 && out.attempt !== void 0) out.runAttempt = out.attempt;
|
|
346
|
+
return out;
|
|
347
|
+
}
|
|
254
348
|
var WorkflowExecution = class {
|
|
255
349
|
request;
|
|
256
350
|
definition;
|
|
@@ -261,7 +355,7 @@ var WorkflowExecution = class {
|
|
|
261
355
|
activationOpen = false;
|
|
262
356
|
replaying = false;
|
|
263
357
|
clock = 0;
|
|
264
|
-
|
|
358
|
+
randomSource;
|
|
265
359
|
ids = new CommandIds();
|
|
266
360
|
commands = [];
|
|
267
361
|
newCommands = [];
|
|
@@ -300,7 +394,34 @@ var WorkflowExecution = class {
|
|
|
300
394
|
signalHandlers = /* @__PURE__ */ new Map();
|
|
301
395
|
signalWaiters = /* @__PURE__ */ new Map();
|
|
302
396
|
defaultSignalHandler;
|
|
397
|
+
defaultSignalPolicy = "WARN_AND_ABANDON";
|
|
398
|
+
signalPolicies = /* @__PURE__ */ new Map();
|
|
303
399
|
runningHandlers = 0;
|
|
400
|
+
/** Running asynchronous handlers by name, for the drain warning (11 §2.18). */
|
|
401
|
+
running = /* @__PURE__ */ new Map();
|
|
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;
|
|
421
|
+
recordedLocalActivities = /* @__PURE__ */ new Map();
|
|
422
|
+
localActivityResults = /* @__PURE__ */ new Map();
|
|
423
|
+
localActivities = /* @__PURE__ */ new Map();
|
|
424
|
+
memo;
|
|
304
425
|
conditions = [];
|
|
305
426
|
currentDetails;
|
|
306
427
|
context;
|
|
@@ -323,7 +444,7 @@ var WorkflowExecution = class {
|
|
|
323
444
|
this.limitEvents = limit.events;
|
|
324
445
|
this.limitBytes = limit.bytes;
|
|
325
446
|
this.captureStacks = input.request.kind === "query";
|
|
326
|
-
this.
|
|
447
|
+
this.randomSource = createRandom(seedOf(randomnessSeedOf(input.request)));
|
|
327
448
|
for (const event of history) {
|
|
328
449
|
if (event.type === "SIDE_EFFECT_RECORDED") {
|
|
329
450
|
const id = event.payload["sideEffectId"];
|
|
@@ -332,17 +453,67 @@ var WorkflowExecution = class {
|
|
|
332
453
|
} else if (event.type === "MARKER_RECORDED") {
|
|
333
454
|
const id = event.payload["markerId"];
|
|
334
455
|
if (typeof id === "string") this.recordedMarkers.add(id);
|
|
456
|
+
} else if (event.type === "LOCAL_ACTIVITY_COMPLETED" || event.type === "LOCAL_ACTIVITY_FAILED") {
|
|
457
|
+
const id = event.payload["localActivityId"];
|
|
458
|
+
const failure = asFailure(event.payload["failure"]);
|
|
459
|
+
const attempts = event.payload["attempts"];
|
|
460
|
+
if (typeof id === "string")
|
|
461
|
+
this.recordedLocalActivities.set(id, {
|
|
462
|
+
...event.type === "LOCAL_ACTIVITY_FAILED" && failure ? { failure } : { result: event.payload["result"] ?? null },
|
|
463
|
+
attempts: typeof attempts === "number" ? attempts : 1
|
|
464
|
+
});
|
|
335
465
|
}
|
|
336
466
|
}
|
|
467
|
+
if (input.request.kind === "task")
|
|
468
|
+
for (const entry of input.request.localActivityResults ?? [])
|
|
469
|
+
this.localActivityResults.set(entry.localActivityId, {
|
|
470
|
+
...entry.failure !== void 0 ? { failure: entry.failure } : { result: entry.result ?? null },
|
|
471
|
+
attempts: entry.attempts
|
|
472
|
+
});
|
|
337
473
|
this.activations = splitActivations(
|
|
338
474
|
history,
|
|
339
475
|
input.request.nowMs,
|
|
340
476
|
input.request.kind !== "query"
|
|
341
477
|
);
|
|
342
478
|
const execution = this;
|
|
479
|
+
const snapshot = infoFromStarted(input.request.info, history[0]);
|
|
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;
|
|
343
505
|
this.info = {
|
|
344
|
-
...
|
|
506
|
+
...snapshot,
|
|
345
507
|
workflowFn: input.request.workflowFn,
|
|
508
|
+
get context() {
|
|
509
|
+
return { ...execution.memo };
|
|
510
|
+
},
|
|
511
|
+
get memo() {
|
|
512
|
+
return { ...execution.memo };
|
|
513
|
+
},
|
|
514
|
+
get searchAttributes() {
|
|
515
|
+
return { ...execution.searchAttributes };
|
|
516
|
+
},
|
|
346
517
|
get historyLength() {
|
|
347
518
|
return execution.historyLength;
|
|
348
519
|
},
|
|
@@ -365,9 +536,33 @@ var WorkflowExecution = class {
|
|
|
365
536
|
now() {
|
|
366
537
|
return this.clock;
|
|
367
538
|
}
|
|
368
|
-
/** The next deterministic random number. */
|
|
539
|
+
/** The next deterministic random number (`Math.random()` and `crypto.getRandomValues()`). */
|
|
369
540
|
nextRandom() {
|
|
370
|
-
|
|
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);
|
|
371
566
|
}
|
|
372
567
|
/** Begin or finish an activation; `done` when there is nothing left to do. */
|
|
373
568
|
step() {
|
|
@@ -389,12 +584,26 @@ var WorkflowExecution = class {
|
|
|
389
584
|
this.checkConditions();
|
|
390
585
|
this.finishActivation();
|
|
391
586
|
}
|
|
587
|
+
const requests = this.pendingLocalActivityRequests();
|
|
588
|
+
if (requests.length > 0) {
|
|
589
|
+
const requested = {
|
|
590
|
+
protocol: HISTORY_PROTOCOL,
|
|
591
|
+
outcome: "localActivityRequested",
|
|
592
|
+
commands: [],
|
|
593
|
+
localActivityRequests: requests,
|
|
594
|
+
logs: []
|
|
595
|
+
};
|
|
596
|
+
return requested;
|
|
597
|
+
}
|
|
392
598
|
const result = {
|
|
393
599
|
protocol: HISTORY_PROTOCOL,
|
|
394
600
|
outcome: this.failure ? "failed" : "commands",
|
|
395
601
|
commands: this.failure ? [] : this.newCommands,
|
|
396
602
|
logs: this.logs
|
|
397
603
|
};
|
|
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;
|
|
398
607
|
if (this.failure) {
|
|
399
608
|
result.failure = { cause: this.failure.cause, message: this.failure.message };
|
|
400
609
|
if (this.failure.stackTrace !== void 0)
|
|
@@ -402,9 +611,19 @@ var WorkflowExecution = class {
|
|
|
402
611
|
if (this.failure.seq !== void 0) result.failure.seq = this.failure.seq;
|
|
403
612
|
if (this.failure.errorType !== void 0) result.failure.errorType = this.failure.errorType;
|
|
404
613
|
}
|
|
405
|
-
if (this.request.kind === "query"
|
|
614
|
+
if (this.request.kind === "query" || (this.request.queries ?? []).length > 0)
|
|
615
|
+
result.queryResults = this.answerQueries();
|
|
406
616
|
return result;
|
|
407
617
|
}
|
|
618
|
+
/**
|
|
619
|
+
* The local activities the open activation waits for, when the task is a
|
|
620
|
+
* workflow task that has not failed or closed the run (a run that ends
|
|
621
|
+
* abandons a local activity it did not await).
|
|
622
|
+
*/
|
|
623
|
+
pendingLocalActivityRequests() {
|
|
624
|
+
if (this.failure || this.closed || this.request.kind !== "task") return [];
|
|
625
|
+
return [...this.localActivities.values()].filter((entry) => !entry.replayed).map((entry) => entry.request);
|
|
626
|
+
}
|
|
408
627
|
/** Fail the task (the runtime's own failures and code errors). */
|
|
409
628
|
failTask(cause, message, stackTrace, seq, errorType) {
|
|
410
629
|
if (!this.failure) this.failure = new TaskFailure(cause, message, stackTrace, seq, errorType);
|
|
@@ -420,11 +639,54 @@ var WorkflowExecution = class {
|
|
|
420
639
|
if (this.failure) return;
|
|
421
640
|
this.deliver(event);
|
|
422
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;
|
|
423
684
|
}
|
|
424
685
|
finishActivation() {
|
|
425
686
|
this.activationOpen = false;
|
|
426
687
|
const activation = this.activations[this.index];
|
|
427
688
|
if (!activation) return;
|
|
689
|
+
this.rejectUnhandledUpdates();
|
|
428
690
|
if (activation.recorded) {
|
|
429
691
|
const found = matchActivation(this.commands, activation.recorded);
|
|
430
692
|
if (found)
|
|
@@ -434,11 +696,30 @@ var WorkflowExecution = class {
|
|
|
434
696
|
void 0,
|
|
435
697
|
found.seq
|
|
436
698
|
);
|
|
699
|
+
else this.checkReplayedLocalActivities();
|
|
437
700
|
} else {
|
|
438
701
|
this.newCommands = this.commands;
|
|
439
702
|
}
|
|
440
703
|
this.commands = [];
|
|
441
704
|
}
|
|
705
|
+
/**
|
|
706
|
+
* A local activity issued on a replayed activation without a recorded
|
|
707
|
+
* outcome is fine only when the first execution dropped it in that same
|
|
708
|
+
* activation: its scope was cancelled (the entry is gone) or the run closed.
|
|
709
|
+
* One still waiting means the first execution would have requested and
|
|
710
|
+
* recorded it, so the code diverged from the history.
|
|
711
|
+
*/
|
|
712
|
+
checkReplayedLocalActivities() {
|
|
713
|
+
if (this.closed) return;
|
|
714
|
+
for (const entry of this.localActivities.values()) {
|
|
715
|
+
if (!entry.replayed) continue;
|
|
716
|
+
this.failTask(
|
|
717
|
+
"NON_DETERMINISM",
|
|
718
|
+
`Replay of ${entry.description ?? "a local activity"} found no recorded outcome: the code issued a local activity the history does not have`
|
|
719
|
+
);
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
442
723
|
// ---- inputs ---------------------------------------------------------------
|
|
443
724
|
deliver(event) {
|
|
444
725
|
const p = event.payload;
|
|
@@ -465,8 +746,20 @@ var WorkflowExecution = class {
|
|
|
465
746
|
return;
|
|
466
747
|
}
|
|
467
748
|
case "SIGNAL_RECEIVED":
|
|
468
|
-
this.dispatchSignal(id("name"), p["payload"] ?? null);
|
|
749
|
+
this.dispatchSignal(id("name"), p["payload"] ?? null, headersOf(p["headers"]));
|
|
469
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
|
+
}
|
|
470
763
|
case "CHILD_RUN_STARTED":
|
|
471
764
|
case "CHILD_RUN_COMPLETED":
|
|
472
765
|
case "CHILD_RUN_FAILED":
|
|
@@ -516,12 +809,14 @@ var WorkflowExecution = class {
|
|
|
516
809
|
pending.release?.();
|
|
517
810
|
const p = event.payload;
|
|
518
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}`;
|
|
519
813
|
const wrap = (cause) => new ActivityFailure(
|
|
520
|
-
`Activity ${activityId} (${
|
|
814
|
+
`Activity ${activityId} (${label}) failed: ${cause.message}`,
|
|
521
815
|
activityId,
|
|
522
816
|
pending.nodeType,
|
|
523
817
|
pending.operation,
|
|
524
|
-
cause
|
|
818
|
+
cause,
|
|
819
|
+
pending.activityName
|
|
525
820
|
);
|
|
526
821
|
switch (event.type) {
|
|
527
822
|
case "ACTIVITY_COMPLETED":
|
|
@@ -613,10 +908,24 @@ var WorkflowExecution = class {
|
|
|
613
908
|
else result.reject(outcome.error);
|
|
614
909
|
}
|
|
615
910
|
}
|
|
616
|
-
|
|
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 = {}) {
|
|
617
922
|
const handler = this.signalHandlers.get(name);
|
|
618
923
|
if (handler) {
|
|
619
|
-
this.runHandler(
|
|
924
|
+
this.runHandler(
|
|
925
|
+
this.signalCall(name, payload, headers, handler),
|
|
926
|
+
name,
|
|
927
|
+
this.signalPolicies.get(name)
|
|
928
|
+
);
|
|
620
929
|
return;
|
|
621
930
|
}
|
|
622
931
|
const waiters = this.signalWaiters.get(name);
|
|
@@ -628,7 +937,11 @@ var WorkflowExecution = class {
|
|
|
628
937
|
}
|
|
629
938
|
if (this.defaultSignalHandler) {
|
|
630
939
|
const fallback = this.defaultSignalHandler;
|
|
631
|
-
this.runHandler(
|
|
940
|
+
this.runHandler(
|
|
941
|
+
this.signalCall(name, payload, headers, (value) => fallback(name, value)),
|
|
942
|
+
name,
|
|
943
|
+
this.defaultSignalPolicy
|
|
944
|
+
);
|
|
632
945
|
return;
|
|
633
946
|
}
|
|
634
947
|
const buffer = this.signalBuffers.get(name) ?? [];
|
|
@@ -644,7 +957,7 @@ var WorkflowExecution = class {
|
|
|
644
957
|
if (position >= 0) this.signalOrder.splice(position, 1);
|
|
645
958
|
return payload;
|
|
646
959
|
}
|
|
647
|
-
runHandler(call) {
|
|
960
|
+
runHandler(call, name, policy = "WARN_AND_ABANDON") {
|
|
648
961
|
let result;
|
|
649
962
|
try {
|
|
650
963
|
result = call();
|
|
@@ -653,23 +966,265 @@ var WorkflowExecution = class {
|
|
|
653
966
|
return;
|
|
654
967
|
}
|
|
655
968
|
if (result instanceof Promise) {
|
|
656
|
-
this.
|
|
969
|
+
const done = this.trackHandler("signal", name, policy);
|
|
970
|
+
result.then(done, (error) => {
|
|
971
|
+
done();
|
|
972
|
+
this.onWorkflowError(error);
|
|
973
|
+
});
|
|
974
|
+
}
|
|
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);
|
|
657
1090
|
result.then(
|
|
658
|
-
() => {
|
|
659
|
-
|
|
1091
|
+
(value) => {
|
|
1092
|
+
done();
|
|
1093
|
+
this.completeUpdate(update, { ok: true, value });
|
|
660
1094
|
},
|
|
661
1095
|
(error) => {
|
|
662
|
-
|
|
663
|
-
this.
|
|
1096
|
+
done();
|
|
1097
|
+
this.completeUpdate(update, { ok: false, error });
|
|
664
1098
|
}
|
|
665
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;
|
|
666
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
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* The drain check of 11 §2.18 when the workflow finishes: handlers still
|
|
1193
|
+
* running with policy `WARN_AND_ABANDON` give one warning line and
|
|
1194
|
+
* `unfinishedHandlers` in the result. A replay never reports them.
|
|
1195
|
+
*/
|
|
1196
|
+
checkUnfinishedHandlers(command) {
|
|
1197
|
+
if (this.replaying || command.type === "CANCEL_WORKFLOW") return;
|
|
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
|
+
);
|
|
1203
|
+
if (unfinished.length === 0) return;
|
|
1204
|
+
this.unfinished = unfinished;
|
|
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";
|
|
1207
|
+
this.logs.push({
|
|
1208
|
+
level: "warn",
|
|
1209
|
+
message: `The workflow finished while ${total} ${what}${total === 1 ? " was" : "s were"} still running (${unfinished.map((entry) => `${entry.name} x${entry.count}`).join(
|
|
1210
|
+
", "
|
|
1211
|
+
)}); await ctx.allHandlersFinished() before returning, or set unfinishedPolicy: 'ABANDON' on the handler`,
|
|
1212
|
+
data: unfinished.map((entry) => ({ kind: entry.kind, name: entry.name, count: entry.count }))
|
|
1213
|
+
});
|
|
667
1214
|
}
|
|
668
1215
|
// ---- the workflow function ------------------------------------------------
|
|
669
1216
|
startFunction(input) {
|
|
670
1217
|
let promise;
|
|
671
1218
|
try {
|
|
672
|
-
|
|
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 } });
|
|
673
1228
|
} catch (error) {
|
|
674
1229
|
promise = Promise.reject(error);
|
|
675
1230
|
}
|
|
@@ -699,7 +1254,13 @@ var WorkflowExecution = class {
|
|
|
699
1254
|
onWorkflowError(error) {
|
|
700
1255
|
if (this.closed || this.failure) return;
|
|
701
1256
|
if (error instanceof ContinueAsNewRequest) {
|
|
702
|
-
|
|
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);
|
|
703
1264
|
return;
|
|
704
1265
|
}
|
|
705
1266
|
const name = errorName(error);
|
|
@@ -719,6 +1280,10 @@ var WorkflowExecution = class {
|
|
|
719
1280
|
this.failTask("UNSUPPORTED_COMMAND", messageOf(error), stackTextOf(error));
|
|
720
1281
|
return;
|
|
721
1282
|
}
|
|
1283
|
+
if (error instanceof QueryHandlerSideEffect) {
|
|
1284
|
+
this.failTask("WORKFLOW_CODE_ERROR", messageOf(error), stackTextOf(error), void 0, name);
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
722
1287
|
if (this.cancelRequested && isCancellation(error)) {
|
|
723
1288
|
this.pushTerminal({ type: "CANCEL_WORKFLOW" });
|
|
724
1289
|
return;
|
|
@@ -753,6 +1318,7 @@ var WorkflowExecution = class {
|
|
|
753
1318
|
}
|
|
754
1319
|
/** Issue a command; false when the run is already closed (the command is dropped). */
|
|
755
1320
|
push(command) {
|
|
1321
|
+
this.assertWritable(command.type);
|
|
756
1322
|
if (this.closed || this.failure) return false;
|
|
757
1323
|
this.checkLimit();
|
|
758
1324
|
this.commands.push(command);
|
|
@@ -762,6 +1328,7 @@ var WorkflowExecution = class {
|
|
|
762
1328
|
if (this.closed || this.failure) return;
|
|
763
1329
|
this.commands.push(command);
|
|
764
1330
|
this.closed = true;
|
|
1331
|
+
this.checkUnfinishedHandlers(command);
|
|
765
1332
|
}
|
|
766
1333
|
stack() {
|
|
767
1334
|
return this.captureStacks ? captureStack() : void 0;
|
|
@@ -777,7 +1344,7 @@ var WorkflowExecution = class {
|
|
|
777
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`,
|
|
778
1345
|
{ type: "durable.activityTypeNotAllowed", errorType: "VALIDATION", nonRetryable: true }
|
|
779
1346
|
);
|
|
780
|
-
const activityId = this.
|
|
1347
|
+
const activityId = this.nextId("a");
|
|
781
1348
|
const operationName = options.operation ?? operation.operation ?? "execute";
|
|
782
1349
|
const nodeVersion = options.nodeVersion ?? operation.version;
|
|
783
1350
|
const cancellationType = options.cancellationType ?? "WAIT_CANCELLATION_COMPLETED";
|
|
@@ -804,20 +1371,119 @@ var WorkflowExecution = class {
|
|
|
804
1371
|
if (options.priority !== void 0) command.priority = options.priority;
|
|
805
1372
|
if (options.summary !== void 0) command.summary = options.summary;
|
|
806
1373
|
if (options.runOn !== void 0) command.runOn = options.runOn;
|
|
807
|
-
|
|
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;
|
|
808
1481
|
if (scope.consideredCancelled)
|
|
809
1482
|
return Promise.reject(new CancelledFailure("Scope is cancelled"));
|
|
810
1483
|
if (!this.push(command)) return this.never();
|
|
811
1484
|
this.issued.add(`activity:${activityId}`);
|
|
812
1485
|
return new Promise((resolve, reject) => {
|
|
813
|
-
const entry = {
|
|
814
|
-
resolve,
|
|
815
|
-
reject,
|
|
816
|
-
description: `activity ${activityId} (${operation.nodeType} ${operationName})`,
|
|
817
|
-
nodeType: operation.nodeType,
|
|
818
|
-
operation: operationName,
|
|
819
|
-
cancellationType
|
|
820
|
-
};
|
|
1486
|
+
const entry = { ...pending, resolve, reject };
|
|
821
1487
|
const stack = this.stack();
|
|
822
1488
|
if (stack !== void 0) entry.stack = stack;
|
|
823
1489
|
entry.release = scope.onCancel(() => {
|
|
@@ -827,7 +1493,8 @@ var WorkflowExecution = class {
|
|
|
827
1493
|
activityId,
|
|
828
1494
|
entry.nodeType,
|
|
829
1495
|
entry.operation,
|
|
830
|
-
new CancelledFailure("Activity cancelled")
|
|
1496
|
+
new CancelledFailure("Activity cancelled"),
|
|
1497
|
+
entry.activityName
|
|
831
1498
|
);
|
|
832
1499
|
if (cancellationType !== "ABANDON")
|
|
833
1500
|
this.push({ type: "REQUEST_CANCEL_ACTIVITY", activityId });
|
|
@@ -839,6 +1506,124 @@ var WorkflowExecution = class {
|
|
|
839
1506
|
this.activities.set(activityId, entry);
|
|
840
1507
|
});
|
|
841
1508
|
}
|
|
1509
|
+
/**
|
|
1510
|
+
* `ctx.localActivity` (11 §2.12). A replayed activation returns the outcome
|
|
1511
|
+
* its history recorded; the open activation takes the outcome the worker
|
|
1512
|
+
* passed in `localActivityResults`, or asks for the local activity
|
|
1513
|
+
* (`localActivityRequested`) and waits for the same task to run again.
|
|
1514
|
+
* Either way the outcome is recorded with `RECORD_LOCAL_ACTIVITY` at the
|
|
1515
|
+
* call, so a replay issues the same command at the same position.
|
|
1516
|
+
*/
|
|
1517
|
+
localActivity(scope, operation, options) {
|
|
1518
|
+
if (typeof operation === "function")
|
|
1519
|
+
throw new PhaseCFeatureError("ctx.localActivity with a function (user local activities)");
|
|
1520
|
+
if (typeof operation !== "object" || operation === null || typeof operation.nodeType !== "string")
|
|
1521
|
+
throw new TypeError(
|
|
1522
|
+
"ctx.localActivity needs a node operation (from nodes.* or the generated clients)"
|
|
1523
|
+
);
|
|
1524
|
+
const descriptor = operation;
|
|
1525
|
+
if (descriptor.role === "trigger" || !isActivityNodeTypeAllowed(descriptor.nodeType))
|
|
1526
|
+
throw new ApplicationError(
|
|
1527
|
+
`${descriptor.nodeType} cannot run as a durable activity: only store and registry nodes and internal:http, internal:datatable, internal:set and internal:code can`,
|
|
1528
|
+
{ type: "durable.activityTypeNotAllowed", errorType: "VALIDATION", nonRetryable: true }
|
|
1529
|
+
);
|
|
1530
|
+
if (typeof options !== "object" || options === null)
|
|
1531
|
+
throw new TypeError("ctx.localActivity needs options with args");
|
|
1532
|
+
const timeout = options.startToCloseTimeout === void 0 ? LOCAL_ACTIVITY_MAX_TIMEOUT_MS : toMs(options.startToCloseTimeout, "startToCloseTimeout");
|
|
1533
|
+
if (timeout < 1 || timeout > LOCAL_ACTIVITY_MAX_TIMEOUT_MS)
|
|
1534
|
+
throw new TypeError(
|
|
1535
|
+
`A local activity's startToCloseTimeout must be between 1 ms and 30 s (got ${timeout} ms); use ctx.run for longer calls`
|
|
1536
|
+
);
|
|
1537
|
+
const localActivityId = this.nextId("l");
|
|
1538
|
+
const operationName = options.operation ?? descriptor.operation ?? "execute";
|
|
1539
|
+
const nodeVersion = options.nodeVersion ?? descriptor.version;
|
|
1540
|
+
const retry = explicitRetryPolicy(options.retry);
|
|
1541
|
+
const args = toJson(options.args, `The args of ${descriptor.nodeType}`);
|
|
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}`,
|
|
1548
|
+
localActivityId,
|
|
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",
|
|
1586
|
+
nodeType: descriptor.nodeType,
|
|
1587
|
+
operation: operationName,
|
|
1588
|
+
args: args2,
|
|
1589
|
+
retry,
|
|
1590
|
+
startToCloseTimeout: timeout,
|
|
1591
|
+
attempt,
|
|
1592
|
+
backoffMs
|
|
1593
|
+
};
|
|
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
|
+
});
|
|
1613
|
+
};
|
|
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({
|
|
1621
|
+
localActivityId,
|
|
1622
|
+
nodeType: descriptor.nodeType,
|
|
1623
|
+
operation: operationName,
|
|
1624
|
+
args
|
|
1625
|
+
});
|
|
1626
|
+
}
|
|
842
1627
|
startTimer(scope, durationMs, summary) {
|
|
843
1628
|
return this.timer(scope, durationMs, summary).promise;
|
|
844
1629
|
}
|
|
@@ -847,7 +1632,7 @@ var WorkflowExecution = class {
|
|
|
847
1632
|
if (durationMs <= 0) return { promise: Promise.resolve() };
|
|
848
1633
|
if (scope.consideredCancelled)
|
|
849
1634
|
return { promise: Promise.reject(new CancelledFailure("Scope is cancelled")) };
|
|
850
|
-
const timerId = this.
|
|
1635
|
+
const timerId = this.nextId("t");
|
|
851
1636
|
const command = {
|
|
852
1637
|
type: "START_TIMER",
|
|
853
1638
|
timerId,
|
|
@@ -960,13 +1745,16 @@ var WorkflowExecution = class {
|
|
|
960
1745
|
});
|
|
961
1746
|
});
|
|
962
1747
|
}
|
|
963
|
-
onSignal(signal, handler) {
|
|
1748
|
+
onSignal(signal, handler, options = {}) {
|
|
1749
|
+
if (typeof handler !== "function") throw new TypeError("ctx.onSignal needs a handler function");
|
|
1750
|
+
const policy = checkPolicy(options.unfinishedPolicy);
|
|
964
1751
|
const name = signalName(signal);
|
|
965
1752
|
this.signalHandlers.set(name, handler);
|
|
1753
|
+
this.signalPolicies.set(name, policy);
|
|
966
1754
|
let payload = this.takeBuffered(name);
|
|
967
1755
|
while (payload !== void 0) {
|
|
968
1756
|
const value = payload;
|
|
969
|
-
this.runHandler(() => handler(value));
|
|
1757
|
+
this.runHandler(() => handler(value), name, policy);
|
|
970
1758
|
payload = this.takeBuffered(name);
|
|
971
1759
|
}
|
|
972
1760
|
}
|
|
@@ -976,8 +1764,36 @@ var WorkflowExecution = class {
|
|
|
976
1764
|
for (const entry of buffered) {
|
|
977
1765
|
if (this.signalHandlers.has(entry.name)) continue;
|
|
978
1766
|
const payload = this.takeBuffered(entry.name);
|
|
979
|
-
if (payload !== void 0)
|
|
1767
|
+
if (payload !== void 0)
|
|
1768
|
+
this.runHandler(() => handler(entry.name, payload), entry.name, this.defaultSignalPolicy);
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
/** The command target of a run handle (11 §2.1, §2.24). */
|
|
1772
|
+
externalTarget(target) {
|
|
1773
|
+
if (typeof target === "string") {
|
|
1774
|
+
if (target === "") throw new TypeError("A run handle needs a run id");
|
|
1775
|
+
return { runId: target };
|
|
1776
|
+
}
|
|
1777
|
+
if (typeof target === "object" && target !== null) {
|
|
1778
|
+
if ("runId" in target && typeof target.runId === "string" && target.runId !== "")
|
|
1779
|
+
return { runId: target.runId };
|
|
1780
|
+
if ("workflowKey" in target && typeof target.workflowKey === "string" && target.workflowKey !== "" && typeof target.workflowId === "string" && target.workflowId !== "") {
|
|
1781
|
+
const out = { workflowKey: target.workflowKey, workflowId: target.workflowId };
|
|
1782
|
+
if (typeof target.firstRunId === "string" && target.firstRunId !== "")
|
|
1783
|
+
out.firstRunId = target.firstRunId;
|
|
1784
|
+
return out;
|
|
1785
|
+
}
|
|
980
1786
|
}
|
|
1787
|
+
throw new TypeError("A run handle needs a run id or { workflowKey, workflowId }");
|
|
1788
|
+
}
|
|
1789
|
+
runHandle(target) {
|
|
1790
|
+
const resolved = this.externalTarget(target);
|
|
1791
|
+
return {
|
|
1792
|
+
runId: "runId" in resolved ? resolved.runId : "",
|
|
1793
|
+
..."workflowId" in resolved ? { workflowKey: resolved.workflowKey, workflowId: resolved.workflowId } : {},
|
|
1794
|
+
signal: (signal, payload) => this.signalExternal(resolved, signalName(signal), payload),
|
|
1795
|
+
cancel: () => this.cancelExternal(resolved)
|
|
1796
|
+
};
|
|
981
1797
|
}
|
|
982
1798
|
childTarget(target) {
|
|
983
1799
|
if (typeof target === "string") {
|
|
@@ -1004,6 +1820,12 @@ var WorkflowExecution = class {
|
|
|
1004
1820
|
if (options.details !== void 0) opts["details"] = options.details;
|
|
1005
1821
|
if (options.context !== void 0)
|
|
1006
1822
|
opts["context"] = toJson(options.context, "The child context");
|
|
1823
|
+
if (options.retry !== void 0)
|
|
1824
|
+
opts["retry"] = toJson(runRetryPolicyOf(options.retry), "The child retry policy");
|
|
1825
|
+
if (options.versioningBehavior !== void 0)
|
|
1826
|
+
opts["versioningBehavior"] = options.versioningBehavior;
|
|
1827
|
+
if (options.idReusePolicy !== void 0) opts["idReusePolicy"] = options.idReusePolicy;
|
|
1828
|
+
if (options.idConflictPolicy !== void 0) opts["idConflictPolicy"] = options.idConflictPolicy;
|
|
1007
1829
|
const durations = [
|
|
1008
1830
|
["startDelay", "startDelay"],
|
|
1009
1831
|
["executionTimeout", "executionTimeout"],
|
|
@@ -1025,13 +1847,39 @@ var WorkflowExecution = class {
|
|
|
1025
1847
|
...opts
|
|
1026
1848
|
}
|
|
1027
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;
|
|
1028
1854
|
if (options.version !== void 0) command.version = options.version;
|
|
1029
1855
|
if (options.headers !== void 0) command.headers = { ...options.headers };
|
|
1030
1856
|
return command;
|
|
1031
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
|
+
}
|
|
1032
1880
|
startChildRun(scope, target, options, defaultPolicy) {
|
|
1033
1881
|
if (scope.consideredCancelled) return void 0;
|
|
1034
|
-
const childId = this.
|
|
1882
|
+
const childId = this.nextId("c");
|
|
1035
1883
|
const command = this.childCommand(childId, target, options, defaultPolicy);
|
|
1036
1884
|
if (!this.push(command)) return void 0;
|
|
1037
1885
|
this.issued.add(`child:${childId}`);
|
|
@@ -1056,7 +1904,7 @@ var WorkflowExecution = class {
|
|
|
1056
1904
|
entry.reject(error);
|
|
1057
1905
|
return;
|
|
1058
1906
|
}
|
|
1059
|
-
const externalId = this.
|
|
1907
|
+
const externalId = this.nextId("x");
|
|
1060
1908
|
if (this.push({
|
|
1061
1909
|
type: "REQUEST_CANCEL_EXTERNAL_RUN",
|
|
1062
1910
|
externalId,
|
|
@@ -1090,15 +1938,17 @@ var WorkflowExecution = class {
|
|
|
1090
1938
|
};
|
|
1091
1939
|
}
|
|
1092
1940
|
child(scope, target, options) {
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
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
|
+
});
|
|
1102
1952
|
}
|
|
1103
1953
|
childResult(child) {
|
|
1104
1954
|
if (child.settled && child.outcome) {
|
|
@@ -1118,6 +1968,13 @@ var WorkflowExecution = class {
|
|
|
1118
1968
|
});
|
|
1119
1969
|
}
|
|
1120
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) {
|
|
1121
1978
|
const child = this.startChildRun(
|
|
1122
1979
|
scope,
|
|
1123
1980
|
target,
|
|
@@ -1147,7 +2004,21 @@ var WorkflowExecution = class {
|
|
|
1147
2004
|
});
|
|
1148
2005
|
}
|
|
1149
2006
|
signalExternal(target, name, payload) {
|
|
1150
|
-
|
|
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");
|
|
1151
2022
|
const command = {
|
|
1152
2023
|
type: "SIGNAL_RUN",
|
|
1153
2024
|
externalId,
|
|
@@ -1155,6 +2026,7 @@ var WorkflowExecution = class {
|
|
|
1155
2026
|
name,
|
|
1156
2027
|
payload: toJson(payload, `The payload of signal ${name}`)
|
|
1157
2028
|
};
|
|
2029
|
+
if (headers !== void 0 && Object.keys(headers).length > 0) command.headers = { ...headers };
|
|
1158
2030
|
if (!this.push(command)) return this.never();
|
|
1159
2031
|
this.issued.add(`external:${externalId}`);
|
|
1160
2032
|
return new Promise((resolve, reject) => {
|
|
@@ -1169,7 +2041,15 @@ var WorkflowExecution = class {
|
|
|
1169
2041
|
});
|
|
1170
2042
|
}
|
|
1171
2043
|
cancelExternal(target) {
|
|
1172
|
-
|
|
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");
|
|
1173
2053
|
if (!this.push({ type: "REQUEST_CANCEL_EXTERNAL_RUN", externalId, target }))
|
|
1174
2054
|
return this.never();
|
|
1175
2055
|
this.issued.add(`external:${externalId}`);
|
|
@@ -1185,7 +2065,15 @@ var WorkflowExecution = class {
|
|
|
1185
2065
|
});
|
|
1186
2066
|
}
|
|
1187
2067
|
sideEffect(fn) {
|
|
1188
|
-
|
|
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");
|
|
1189
2077
|
let value;
|
|
1190
2078
|
if (this.replaying && this.recordedSideEffects.has(sideEffectId)) {
|
|
1191
2079
|
value = this.recordedSideEffects.get(sideEffectId) ?? null;
|
|
@@ -1199,6 +2087,7 @@ var WorkflowExecution = class {
|
|
|
1199
2087
|
if (typeof patchId !== "string" || patchId === "") throw new TypeError("A patch needs an id");
|
|
1200
2088
|
const known = this.patches.get(patchId);
|
|
1201
2089
|
if (known !== void 0) return known;
|
|
2090
|
+
this.assertWritable(deprecated ? "ctx.deprecatePatch()" : "ctx.patched()");
|
|
1202
2091
|
const markerId = patchMarkerId(patchId);
|
|
1203
2092
|
let result;
|
|
1204
2093
|
if (this.replaying) {
|
|
@@ -1225,25 +2114,120 @@ var WorkflowExecution = class {
|
|
|
1225
2114
|
}
|
|
1226
2115
|
setCurrentDetails(markdown) {
|
|
1227
2116
|
if (typeof markdown !== "string") throw new TypeError("setCurrentDetails takes Markdown text");
|
|
2117
|
+
this.assertWritable("ctx.setCurrentDetails()");
|
|
1228
2118
|
this.currentDetails = markdown;
|
|
1229
2119
|
this.push({
|
|
1230
2120
|
type: "RECORD_MARKER",
|
|
1231
|
-
markerId: this.
|
|
2121
|
+
markerId: this.nextId("m"),
|
|
1232
2122
|
data: { currentDetails: markdown }
|
|
1233
2123
|
});
|
|
1234
2124
|
}
|
|
1235
2125
|
upsertContext(entries) {
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
2126
|
+
const json = toJson(entries, "The context entries");
|
|
2127
|
+
if (!isJsonObject(json)) throw new TypeError("ctx.upsertContext takes an object");
|
|
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}`))
|
|
1239
2181
|
});
|
|
1240
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: {} });
|
|
2224
|
+
}
|
|
1241
2225
|
/** The request a `ctx.continueAsNew` call throws; the run ends with `CONTINUE_AS_NEW`. */
|
|
1242
2226
|
continueAsNew(input, options) {
|
|
1243
2227
|
const opts = {};
|
|
1244
2228
|
if (options.taskQueue !== void 0) opts["taskQueue"] = options.taskQueue;
|
|
1245
2229
|
if (options.retry !== void 0)
|
|
1246
|
-
opts["retry"] = toJson(
|
|
2230
|
+
opts["retry"] = toJson(continueAsNewRetryOf(options.retry), "retry");
|
|
1247
2231
|
if (options.workflowTaskTimeout !== void 0)
|
|
1248
2232
|
opts["workflowTaskTimeout"] = toMs(options.workflowTaskTimeout, "workflowTaskTimeout");
|
|
1249
2233
|
if (options.context !== void 0) opts["context"] = toJson(options.context, "context");
|
|
@@ -1292,6 +2276,7 @@ var WorkflowExecution = class {
|
|
|
1292
2276
|
out.push({ description: entry.description ?? fallback, stack: entry.stack ?? "" });
|
|
1293
2277
|
};
|
|
1294
2278
|
for (const entry of this.activities.values()) add(entry, "activity");
|
|
2279
|
+
for (const entry of this.localActivities.values()) add(entry, "local activity");
|
|
1295
2280
|
for (const entry of this.timers.values()) add(entry, "timer");
|
|
1296
2281
|
for (const child of this.children.values()) {
|
|
1297
2282
|
add(child.started, "child start");
|
|
@@ -1309,13 +2294,49 @@ var WorkflowExecution = class {
|
|
|
1309
2294
|
);
|
|
1310
2295
|
return out;
|
|
1311
2296
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
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")
|
|
1315
2325
|
return {
|
|
1316
2326
|
queryId: query.queryId,
|
|
1317
|
-
error: `
|
|
2327
|
+
error: `The handler of query "${query.name}" returned a promise; query handlers must return a value`
|
|
1318
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);
|
|
1319
2340
|
if (this.failure)
|
|
1320
2341
|
return { queryId: query.queryId, error: `The replay failed: ${this.failure.message}` };
|
|
1321
2342
|
if (query.name === "__stack_trace")
|
|
@@ -1363,9 +2384,20 @@ ${entry.stack}`).join("\n\n")
|
|
|
1363
2384
|
contextFor(bound) {
|
|
1364
2385
|
const scope = () => bound ?? this.currentScope();
|
|
1365
2386
|
const log = (level) => (message, data) => {
|
|
1366
|
-
if (this.replaying) return;
|
|
2387
|
+
if (this.replaying || this.readOnly !== void 0) return;
|
|
1367
2388
|
const line = { level, message: String(message) };
|
|
1368
|
-
|
|
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
|
+
}
|
|
1369
2401
|
this.logs.push(line);
|
|
1370
2402
|
};
|
|
1371
2403
|
const logger = {
|
|
@@ -1376,31 +2408,42 @@ ${entry.stack}`).join("\n\n")
|
|
|
1376
2408
|
};
|
|
1377
2409
|
const scopeFn = ((fn, options = {}) => this.cancellationScope(scope(), fn, options));
|
|
1378
2410
|
scopeFn.current = () => scope();
|
|
1379
|
-
const phaseC = (feature) => () => {
|
|
1380
|
-
throw new PhaseCFeatureError(feature);
|
|
1381
|
-
};
|
|
1382
2411
|
const context = {
|
|
1383
2412
|
info: this.info,
|
|
1384
2413
|
log: logger,
|
|
1385
2414
|
run: (operation, options) => this.scheduleActivity(scope(), operation, options),
|
|
1386
|
-
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
|
+
},
|
|
1387
2426
|
condition: (predicate, timeout) => this.condition(scope(), predicate, timeout),
|
|
1388
2427
|
signalReceived: (signal) => this.signalReceived(scope(), signal),
|
|
1389
|
-
onSignal: (signal,
|
|
2428
|
+
onSignal: ((signal, optionsOrHandler, maybeHandler) => {
|
|
2429
|
+
if (typeof optionsOrHandler === "function") this.onSignal(signal, optionsOrHandler);
|
|
2430
|
+
else if (maybeHandler !== void 0)
|
|
2431
|
+
this.onSignal(signal, maybeHandler, optionsOrHandler ?? {});
|
|
2432
|
+
else throw new TypeError("ctx.onSignal needs a handler function");
|
|
2433
|
+
}),
|
|
1390
2434
|
setDefaultSignalHandler: (handler) => this.setDefaultSignalHandler(handler),
|
|
1391
2435
|
child: (target, options = {}) => this.child(scope(), target, options),
|
|
1392
2436
|
startChild: (target, options = {}) => this.startChild(scope(), target, options),
|
|
1393
|
-
signalRun: (
|
|
2437
|
+
signalRun: (target, signal, payload) => this.signalExternal(this.externalTarget(target), signalName(signal), payload),
|
|
1394
2438
|
signalParent: (signal, payload) => this.signalExternal({ parent: true }, signalName(signal), payload),
|
|
1395
|
-
getRunHandle: (
|
|
1396
|
-
|
|
1397
|
-
signal: (signal, payload) => this.signalExternal({ runId }, signalName(signal), payload),
|
|
1398
|
-
cancel: () => this.cancelExternal({ runId })
|
|
1399
|
-
}),
|
|
1400
|
-
cancelRun: (runId) => this.cancelExternal({ runId }),
|
|
2439
|
+
getRunHandle: (target) => this.runHandle(target),
|
|
2440
|
+
cancelRun: (target) => this.cancelExternal(this.externalTarget(target)),
|
|
1401
2441
|
now: () => this.clock,
|
|
1402
2442
|
random: () => this.random(),
|
|
1403
|
-
uuid: () =>
|
|
2443
|
+
uuid: () => {
|
|
2444
|
+
this.assertWritable("ctx.uuid()");
|
|
2445
|
+
return uuidFrom(this.randomSource);
|
|
2446
|
+
},
|
|
1404
2447
|
sideEffect: (fn) => this.sideEffect(fn),
|
|
1405
2448
|
patched: (patchId) => this.patched(patchId, false),
|
|
1406
2449
|
deprecatePatch: (patchId) => {
|
|
@@ -1408,13 +2451,7 @@ ${entry.stack}`).join("\n\n")
|
|
|
1408
2451
|
},
|
|
1409
2452
|
setCurrentDetails: (markdown) => this.setCurrentDetails(markdown),
|
|
1410
2453
|
upsertContext: (entries) => this.upsertContext(entries),
|
|
1411
|
-
continueAsNew: (input, options = {}) =>
|
|
1412
|
-
try {
|
|
1413
|
-
return Promise.reject(this.continueAsNew(input, options));
|
|
1414
|
-
} catch (error) {
|
|
1415
|
-
return Promise.reject(error);
|
|
1416
|
-
}
|
|
1417
|
-
},
|
|
2454
|
+
continueAsNew: (input, options = {}) => this.continueAsNewCall(input, options),
|
|
1418
2455
|
cancellationScope: scopeFn,
|
|
1419
2456
|
nonCancellable: (fn) => this.cancellationScope(scope(), fn, { nonCancellable: true }),
|
|
1420
2457
|
onCancel: (fn) => {
|
|
@@ -1450,13 +2487,14 @@ ${entry.stack}`).join("\n\n")
|
|
|
1450
2487
|
};
|
|
1451
2488
|
},
|
|
1452
2489
|
allHandlersFinished: () => this.allHandlersFinished(),
|
|
1453
|
-
activity:
|
|
1454
|
-
localActivity:
|
|
1455
|
-
|
|
1456
|
-
),
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
upsertSearchAttributes:
|
|
2490
|
+
activity: (activity, options) => this.userActivity(scope(), activity, options),
|
|
2491
|
+
localActivity: ((operation, options) => this.localActivity(scope(), operation, options)),
|
|
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()
|
|
1460
2498
|
};
|
|
1461
2499
|
return Object.freeze(context);
|
|
1462
2500
|
}
|
|
@@ -1468,17 +2506,36 @@ function buildWorkflowRegistry(modules) {
|
|
|
1468
2506
|
const byName = /* @__PURE__ */ new Map();
|
|
1469
2507
|
const exportNames = /* @__PURE__ */ new Map();
|
|
1470
2508
|
const ambiguous = /* @__PURE__ */ new Set();
|
|
2509
|
+
const interceptors = [];
|
|
2510
|
+
let dynamic;
|
|
2511
|
+
let dynamicAmbiguous = false;
|
|
1471
2512
|
for (const [, namespace] of modules) {
|
|
1472
2513
|
for (const exportName of Object.keys(namespace).sort()) {
|
|
1473
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
|
+
}
|
|
1474
2520
|
if (!isDurableWorkflow(value)) continue;
|
|
1475
2521
|
const existing = byName.get(exportName);
|
|
1476
2522
|
if (existing && existing !== value) ambiguous.add(exportName);
|
|
1477
2523
|
byName.set(exportName, value);
|
|
1478
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
|
+
}
|
|
1479
2529
|
}
|
|
1480
2530
|
}
|
|
1481
|
-
return {
|
|
2531
|
+
return {
|
|
2532
|
+
byName,
|
|
2533
|
+
exportNames,
|
|
2534
|
+
ambiguous,
|
|
2535
|
+
...dynamic !== void 0 ? { dynamic } : {},
|
|
2536
|
+
...dynamicAmbiguous ? { dynamicAmbiguous } : {},
|
|
2537
|
+
...interceptors.length > 0 ? { interceptors } : {}
|
|
2538
|
+
};
|
|
1482
2539
|
}
|
|
1483
2540
|
function failedResult(cause, message) {
|
|
1484
2541
|
return {
|
|
@@ -1507,6 +2564,23 @@ function checkRequest(value) {
|
|
|
1507
2564
|
}
|
|
1508
2565
|
if (typeof value["nowMs"] !== "number" || !Number.isFinite(value["nowMs"]))
|
|
1509
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";
|
|
1510
2584
|
return value;
|
|
1511
2585
|
}
|
|
1512
2586
|
function createWorkflowRuntime(registry, shims = void 0) {
|
|
@@ -1523,6 +2597,7 @@ function createWorkflowRuntime(registry, shims = void 0) {
|
|
|
1523
2597
|
return Object.freeze({
|
|
1524
2598
|
protocol: HISTORY_PROTOCOL,
|
|
1525
2599
|
sdkVersion: SDK_VERSION,
|
|
2600
|
+
capabilities: Object.freeze([...RUNTIME_CAPABILITIES]),
|
|
1526
2601
|
start(requestJson) {
|
|
1527
2602
|
shims?.detach();
|
|
1528
2603
|
execution = void 0;
|
|
@@ -1556,7 +2631,15 @@ function createWorkflowRuntime(registry, shims = void 0) {
|
|
|
1556
2631
|
);
|
|
1557
2632
|
return;
|
|
1558
2633
|
}
|
|
1559
|
-
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;
|
|
1560
2643
|
if (!definition) {
|
|
1561
2644
|
early = failedResult(
|
|
1562
2645
|
"INVALID_REQUEST",
|
|
@@ -1569,7 +2652,8 @@ function createWorkflowRuntime(registry, shims = void 0) {
|
|
|
1569
2652
|
definition,
|
|
1570
2653
|
exportNames: registry.exportNames,
|
|
1571
2654
|
request,
|
|
1572
|
-
sdkVersion: SDK_VERSION
|
|
2655
|
+
sdkVersion: SDK_VERSION,
|
|
2656
|
+
...registry.interceptors !== void 0 ? { interceptors: registry.interceptors } : {}
|
|
1573
2657
|
});
|
|
1574
2658
|
execution = created;
|
|
1575
2659
|
shims?.attach({ now: () => created.now(), random: () => created.nextRandom() });
|