@temporalio/workflow 1.11.8 → 1.12.0
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/lib/alea.js +5 -3
- package/lib/alea.js.map +1 -1
- package/lib/cancellation-scope.js +36 -18
- package/lib/cancellation-scope.js.map +1 -1
- package/lib/errors.js +1 -0
- package/lib/errors.js.map +1 -1
- package/lib/flags.js +1 -1
- package/lib/flags.js.map +1 -1
- package/lib/index.d.ts +6 -3
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/interceptors.d.ts +11 -1
- package/lib/interfaces.d.ts +70 -5
- package/lib/interfaces.js +1 -0
- package/lib/interfaces.js.map +1 -1
- package/lib/internals.d.ts +55 -7
- package/lib/internals.js +323 -201
- package/lib/internals.js.map +1 -1
- package/lib/logs.js +11 -1
- package/lib/logs.js.map +1 -1
- package/lib/metrics.d.ts +32 -0
- package/lib/metrics.js +118 -0
- package/lib/metrics.js.map +1 -0
- package/lib/trigger.js +8 -0
- package/lib/trigger.js.map +1 -1
- package/lib/update-scope.js +8 -0
- package/lib/update-scope.js.map +1 -1
- package/lib/worker-interface.d.ts +6 -1
- package/lib/worker-interface.js +165 -103
- package/lib/worker-interface.js.map +1 -1
- package/lib/workflow.d.ts +75 -17
- package/lib/workflow.js +207 -34
- package/lib/workflow.js.map +1 -1
- package/package.json +7 -4
- package/src/flags.ts +1 -1
- package/src/index.ts +4 -2
- package/src/interceptors.ts +21 -1
- package/src/interfaces.ts +85 -9
- package/src/internals.ts +133 -33
- package/src/logs.ts +11 -1
- package/src/metrics.ts +191 -0
- package/src/worker-interface.ts +164 -107
- package/src/workflow.ts +232 -38
package/lib/internals.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.Activator = void 0;
|
|
7
7
|
const common_1 = require("@temporalio/common");
|
|
8
|
+
const payload_search_attributes_1 = require("@temporalio/common/lib/converter/payload-search-attributes");
|
|
8
9
|
const interceptors_1 = require("@temporalio/common/lib/interceptors");
|
|
9
10
|
const internal_workflow_1 = require("@temporalio/common/lib/internal-workflow");
|
|
10
11
|
const alea_1 = require("./alea");
|
|
@@ -42,186 +43,263 @@ const [_encodeStartChildWorkflowExecutionFailedCause, decodeStartChildWorkflowEx
|
|
|
42
43
|
* - Call user-defined functions, including any form of interceptor.
|
|
43
44
|
*/
|
|
44
45
|
class Activator {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Cache for modules - referenced in reusable-vm.ts
|
|
48
|
+
*/
|
|
49
|
+
moduleCache = new Map();
|
|
50
|
+
/**
|
|
51
|
+
* Map of task sequence to a Completion
|
|
52
|
+
*/
|
|
53
|
+
completions = {
|
|
54
|
+
timer: new Map(),
|
|
55
|
+
activity: new Map(),
|
|
56
|
+
childWorkflowStart: new Map(),
|
|
57
|
+
childWorkflowComplete: new Map(),
|
|
58
|
+
signalWorkflow: new Map(),
|
|
59
|
+
cancelWorkflow: new Map(),
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Holds buffered Update calls until a handler is registered
|
|
63
|
+
*/
|
|
64
|
+
bufferedUpdates = Array();
|
|
65
|
+
/**
|
|
66
|
+
* Holds buffered signal calls until a handler is registered
|
|
67
|
+
*/
|
|
68
|
+
bufferedSignals = Array();
|
|
69
|
+
/**
|
|
70
|
+
* Mapping of update name to handler and validator
|
|
71
|
+
*/
|
|
72
|
+
updateHandlers = new Map();
|
|
73
|
+
/**
|
|
74
|
+
* Mapping of signal name to handler
|
|
75
|
+
*/
|
|
76
|
+
signalHandlers = new Map();
|
|
77
|
+
/**
|
|
78
|
+
* Mapping of in-progress updates to handler execution information.
|
|
79
|
+
*/
|
|
80
|
+
inProgressUpdates = new Map();
|
|
81
|
+
/**
|
|
82
|
+
* Mapping of in-progress signals to handler execution information.
|
|
83
|
+
*/
|
|
84
|
+
inProgressSignals = new Map();
|
|
85
|
+
/**
|
|
86
|
+
* A sequence number providing unique identifiers for signal handler executions.
|
|
87
|
+
*/
|
|
88
|
+
signalHandlerExecutionSeq = 0;
|
|
89
|
+
/**
|
|
90
|
+
* A signal handler that catches calls for non-registered signal names.
|
|
91
|
+
*/
|
|
92
|
+
defaultSignalHandler;
|
|
93
|
+
/**
|
|
94
|
+
* A update handler that catches calls for non-registered update names.
|
|
95
|
+
*/
|
|
96
|
+
defaultUpdateHandler;
|
|
97
|
+
/**
|
|
98
|
+
* A query handler that catches calls for non-registered query names.
|
|
99
|
+
*/
|
|
100
|
+
defaultQueryHandler;
|
|
101
|
+
/**
|
|
102
|
+
* Source map file for looking up the source files in response to __enhanced_stack_trace
|
|
103
|
+
*/
|
|
104
|
+
sourceMap;
|
|
105
|
+
/**
|
|
106
|
+
* Whether or not to send the sources in enhanced stack trace query responses
|
|
107
|
+
*/
|
|
108
|
+
showStackTraceSources;
|
|
109
|
+
promiseStackStore = {
|
|
110
|
+
promiseToStack: new Map(),
|
|
111
|
+
childToParent: new Map(),
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* The error that caused the current Workflow Task to fail. Sets if a non-`TemporalFailure`
|
|
115
|
+
* error bubbles up out of the Workflow function, or out of a Signal or Update handler. We
|
|
116
|
+
* capture errors this way because those functions are not technically awaited when started,
|
|
117
|
+
* but left to run asynchronously. There is therefore no real "parent" function that can
|
|
118
|
+
* directly handle those errors, and not capturing it would result in an Unhandled Promise
|
|
119
|
+
* Rejection. So instead, we buffer the error here, to then be processed in the context
|
|
120
|
+
* of our own synchronous Activation handling event loop.
|
|
121
|
+
*
|
|
122
|
+
* Our code does a best effort to stop processing the current activation as soon as possible
|
|
123
|
+
* after this field is set:
|
|
124
|
+
* - If an error is thrown while executing code synchronously (e.g. anything before the
|
|
125
|
+
* first `await` statement in a Workflow function or a signal/update handler), the error
|
|
126
|
+
* will be _immediately_ rethrown, which will prevent execution of further jobs in the
|
|
127
|
+
* current activation. We know we're currently running code synchronously thanks to the
|
|
128
|
+
* `rethrowSynchronously` flag below.
|
|
129
|
+
* - It an error is thrown while executing microtasks, then the error will be rethrown on
|
|
130
|
+
* the next call to `tryUnblockConditions()`.
|
|
131
|
+
*
|
|
132
|
+
* Unfortunately, there's no way for us to prevent further execution of microtasks that have
|
|
133
|
+
* already been scheduled, nor those that will be recursively scheduled from those microtasks.
|
|
134
|
+
* Should more errors get thrown while settling microtasks, those will be ignored (i.e. only
|
|
135
|
+
* the first captured error is preserved).
|
|
136
|
+
*/
|
|
137
|
+
workflowTaskError;
|
|
138
|
+
/**
|
|
139
|
+
* Set to true when running synchronous code (e.g. while processing activation jobs and when calling
|
|
140
|
+
* `tryUnblockConditions()`). While this flag is set, it is safe to let errors bubble up.
|
|
141
|
+
*/
|
|
142
|
+
rethrowSynchronously = false;
|
|
143
|
+
rootScope = new cancellation_scope_1.RootCancellationScope();
|
|
144
|
+
/**
|
|
145
|
+
* Mapping of query name to handler
|
|
146
|
+
*/
|
|
147
|
+
queryHandlers = new Map([
|
|
148
|
+
[
|
|
149
|
+
'__stack_trace',
|
|
150
|
+
{
|
|
151
|
+
handler: () => {
|
|
152
|
+
return this.getStackTraces()
|
|
153
|
+
.map((s) => s.formatted)
|
|
154
|
+
.join('\n\n');
|
|
107
155
|
},
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
156
|
+
description: 'Returns a sensible stack trace.',
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
[
|
|
160
|
+
'__enhanced_stack_trace',
|
|
161
|
+
{
|
|
162
|
+
handler: () => {
|
|
163
|
+
const { sourceMap } = this;
|
|
164
|
+
const sdk = { name: 'typescript', version: pkg_1.default.version };
|
|
165
|
+
const stacks = this.getStackTraces().map(({ structured: locations }) => ({ locations }));
|
|
166
|
+
const sources = {};
|
|
167
|
+
if (this.showStackTraceSources) {
|
|
168
|
+
for (const { locations } of stacks) {
|
|
169
|
+
for (const { file_path } of locations) {
|
|
170
|
+
if (!file_path)
|
|
171
|
+
continue;
|
|
172
|
+
const content = sourceMap?.sourcesContent?.[sourceMap?.sources.indexOf(file_path)];
|
|
173
|
+
if (!content)
|
|
174
|
+
continue;
|
|
175
|
+
sources[file_path] = [
|
|
176
|
+
{
|
|
177
|
+
line_offset: 0,
|
|
178
|
+
content,
|
|
179
|
+
},
|
|
180
|
+
];
|
|
132
181
|
}
|
|
133
182
|
}
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
description: 'Returns a stack trace annotated with source information.',
|
|
183
|
+
}
|
|
184
|
+
return { sdk, stacks, sources };
|
|
137
185
|
},
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
186
|
+
description: 'Returns a stack trace annotated with source information.',
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
[
|
|
190
|
+
'__temporal_workflow_metadata',
|
|
191
|
+
{
|
|
192
|
+
handler: () => {
|
|
193
|
+
const workflowType = this.info.workflowType;
|
|
194
|
+
const queryDefinitions = Array.from(this.queryHandlers.entries()).map(([name, value]) => ({
|
|
195
|
+
name,
|
|
196
|
+
description: value.description,
|
|
197
|
+
}));
|
|
198
|
+
const signalDefinitions = Array.from(this.signalHandlers.entries()).map(([name, value]) => ({
|
|
199
|
+
name,
|
|
200
|
+
description: value.description,
|
|
201
|
+
}));
|
|
202
|
+
const updateDefinitions = Array.from(this.updateHandlers.entries()).map(([name, value]) => ({
|
|
203
|
+
name,
|
|
204
|
+
description: value.description,
|
|
205
|
+
}));
|
|
206
|
+
return {
|
|
207
|
+
definition: {
|
|
208
|
+
type: workflowType,
|
|
209
|
+
queryDefinitions,
|
|
210
|
+
signalDefinitions,
|
|
211
|
+
updateDefinitions,
|
|
212
|
+
},
|
|
213
|
+
};
|
|
166
214
|
},
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
215
|
+
description: 'Returns metadata associated with this workflow.',
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
]);
|
|
219
|
+
/**
|
|
220
|
+
* Loaded in {@link initRuntime}
|
|
221
|
+
*/
|
|
222
|
+
interceptors = {
|
|
223
|
+
inbound: [],
|
|
224
|
+
outbound: [],
|
|
225
|
+
internals: [],
|
|
226
|
+
};
|
|
227
|
+
/**
|
|
228
|
+
* Buffer that stores all generated commands, reset after each activation
|
|
229
|
+
*/
|
|
230
|
+
commands = [];
|
|
231
|
+
/**
|
|
232
|
+
* Stores all {@link condition}s that haven't been unblocked yet
|
|
233
|
+
*/
|
|
234
|
+
blockedConditions = new Map();
|
|
235
|
+
/**
|
|
236
|
+
* Is this Workflow completed?
|
|
237
|
+
*
|
|
238
|
+
* A Workflow will be considered completed if it generates a command that the
|
|
239
|
+
* system considers as a final Workflow command (e.g.
|
|
240
|
+
* completeWorkflowExecution or failWorkflowExecution).
|
|
241
|
+
*/
|
|
242
|
+
completed = false;
|
|
243
|
+
/**
|
|
244
|
+
* Was this Workflow cancelled?
|
|
245
|
+
*/
|
|
246
|
+
cancelled = false;
|
|
247
|
+
/**
|
|
248
|
+
* The next (incremental) sequence to assign when generating completable commands
|
|
249
|
+
*/
|
|
250
|
+
nextSeqs = {
|
|
251
|
+
timer: 1,
|
|
252
|
+
activity: 1,
|
|
253
|
+
childWorkflow: 1,
|
|
254
|
+
signalWorkflow: 1,
|
|
255
|
+
cancelWorkflow: 1,
|
|
256
|
+
condition: 1,
|
|
257
|
+
// Used internally to keep track of active stack traces
|
|
258
|
+
stack: 1,
|
|
259
|
+
};
|
|
260
|
+
/**
|
|
261
|
+
* This is set every time the workflow executes an activation
|
|
262
|
+
* May be accessed and modified from outside the VM.
|
|
263
|
+
*/
|
|
264
|
+
now;
|
|
265
|
+
/**
|
|
266
|
+
* Reference to the current Workflow, initialized when a Workflow is started
|
|
267
|
+
*/
|
|
268
|
+
workflow;
|
|
269
|
+
/**
|
|
270
|
+
* Information about the current Workflow
|
|
271
|
+
* May be accessed from outside the VM.
|
|
272
|
+
*/
|
|
273
|
+
info;
|
|
274
|
+
/**
|
|
275
|
+
* A deterministic RNG, used by the isolate's overridden Math.random
|
|
276
|
+
*/
|
|
277
|
+
random;
|
|
278
|
+
payloadConverter = common_1.defaultPayloadConverter;
|
|
279
|
+
failureConverter = common_1.defaultFailureConverter;
|
|
280
|
+
/**
|
|
281
|
+
* Patches we know the status of for this workflow, as in {@link patched}
|
|
282
|
+
*/
|
|
283
|
+
knownPresentPatches = new Set();
|
|
284
|
+
/**
|
|
285
|
+
* Patches we sent to core {@link patched}
|
|
286
|
+
*/
|
|
287
|
+
sentPatches = new Set();
|
|
288
|
+
knownFlags = new Set();
|
|
289
|
+
/**
|
|
290
|
+
* Buffered sink calls per activation
|
|
291
|
+
*/
|
|
292
|
+
sinkCalls = Array();
|
|
293
|
+
/**
|
|
294
|
+
* A nanosecond resolution time function, externally injected. This is used to
|
|
295
|
+
* precisely sort logs entries emitted from the Workflow Context vs those emitted
|
|
296
|
+
* from other sources (e.g. main thread, Core, etc).
|
|
297
|
+
*/
|
|
298
|
+
getTimeOfDay;
|
|
299
|
+
registeredActivityNames;
|
|
300
|
+
versioningBehavior;
|
|
301
|
+
workflowDefinitionOptionsGetter;
|
|
302
|
+
constructor({ info, now, showStackTraceSources, sourceMap, getTimeOfDay, randomnessSeed, registeredActivityNames, }) {
|
|
225
303
|
this.getTimeOfDay = getTimeOfDay;
|
|
226
304
|
this.info = info;
|
|
227
305
|
this.now = now;
|
|
@@ -281,6 +359,7 @@ class Activator {
|
|
|
281
359
|
return {
|
|
282
360
|
commands: this.commands.splice(0),
|
|
283
361
|
usedInternalFlags: [...this.knownFlags],
|
|
362
|
+
versioningBehavior: this.versioningBehavior,
|
|
284
363
|
};
|
|
285
364
|
}
|
|
286
365
|
async startWorkflowNextHandler({ args }) {
|
|
@@ -302,13 +381,17 @@ class Activator {
|
|
|
302
381
|
// Most things related to initialization have already been handled in the constructor
|
|
303
382
|
this.mutateWorkflowInfo((info) => ({
|
|
304
383
|
...info,
|
|
305
|
-
searchAttributes: (0,
|
|
384
|
+
searchAttributes: (0, payload_search_attributes_1.decodeSearchAttributes)(searchAttributes?.indexedFields),
|
|
385
|
+
typedSearchAttributes: (0, payload_search_attributes_1.decodeTypedSearchAttributes)(searchAttributes?.indexedFields),
|
|
306
386
|
memo: (0, common_1.mapFromPayloads)(this.payloadConverter, memo?.fields),
|
|
307
387
|
lastResult: (0, common_1.fromPayloadsAtIndex)(this.payloadConverter, 0, lastCompletionResult?.payloads),
|
|
308
388
|
lastFailure: continuedFailure != null
|
|
309
389
|
? this.failureConverter.failureToError(continuedFailure, this.payloadConverter)
|
|
310
390
|
: undefined,
|
|
311
391
|
}));
|
|
392
|
+
if (this.workflowDefinitionOptionsGetter) {
|
|
393
|
+
this.versioningBehavior = this.workflowDefinitionOptionsGetter().versioningBehavior;
|
|
394
|
+
}
|
|
312
395
|
}
|
|
313
396
|
cancelWorkflow(_activation) {
|
|
314
397
|
this.cancelled = true;
|
|
@@ -344,14 +427,6 @@ class Activator {
|
|
|
344
427
|
reject(new errors_1.LocalActivityDoBackoff(activation.result.backoff));
|
|
345
428
|
}
|
|
346
429
|
}
|
|
347
|
-
resolveNexusOperationStart(_activation) {
|
|
348
|
-
// This will never be called as we can't produce Nexus-related commands anyway
|
|
349
|
-
throw new Error('Nexus is not yet implemented');
|
|
350
|
-
}
|
|
351
|
-
resolveNexusOperation(_activation) {
|
|
352
|
-
// This will never be called as we can't produce Nexus-related commands anyway
|
|
353
|
-
throw new Error('Nexus is not yet implemented');
|
|
354
|
-
}
|
|
355
430
|
resolveChildWorkflowExecutionStart(activation) {
|
|
356
431
|
const { resolve, reject } = this.consumeCompletion('childWorkflowStart', getSeq(activation));
|
|
357
432
|
if (activation.succeeded) {
|
|
@@ -401,14 +476,25 @@ class Activator {
|
|
|
401
476
|
reject(this.failureToError(failure));
|
|
402
477
|
}
|
|
403
478
|
}
|
|
479
|
+
resolveNexusOperationStart(_) {
|
|
480
|
+
throw new Error('TODO');
|
|
481
|
+
}
|
|
482
|
+
resolveNexusOperation(_) {
|
|
483
|
+
throw new Error('TODO');
|
|
484
|
+
}
|
|
404
485
|
// Intentionally non-async function so this handler doesn't show up in the stack trace
|
|
405
486
|
queryWorkflowNextHandler({ queryName, args }) {
|
|
406
|
-
|
|
487
|
+
let fn = this.queryHandlers.get(queryName)?.handler;
|
|
488
|
+
if (fn === undefined && this.defaultQueryHandler !== undefined) {
|
|
489
|
+
fn = this.defaultQueryHandler.bind(undefined, queryName);
|
|
490
|
+
}
|
|
491
|
+
// No handler or default registered, fail.
|
|
407
492
|
if (fn === undefined) {
|
|
408
493
|
const knownQueryTypes = [...this.queryHandlers.keys()].join(' ');
|
|
409
494
|
// Fail the query
|
|
410
495
|
return Promise.reject(new ReferenceError(`Workflow did not register a handler for ${queryName}. Registered queries: [${knownQueryTypes}]`));
|
|
411
496
|
}
|
|
497
|
+
// Execute handler.
|
|
412
498
|
try {
|
|
413
499
|
const ret = fn(...args);
|
|
414
500
|
if (ret instanceof Promise) {
|
|
@@ -444,8 +530,17 @@ class Activator {
|
|
|
444
530
|
if (!protocolInstanceId) {
|
|
445
531
|
throw new TypeError('Missing activation update protocolInstanceId');
|
|
446
532
|
}
|
|
447
|
-
const entry = this.updateHandlers.get(name)
|
|
448
|
-
|
|
533
|
+
const entry = this.updateHandlers.get(name) ??
|
|
534
|
+
(this.defaultUpdateHandler
|
|
535
|
+
? {
|
|
536
|
+
handler: this.defaultUpdateHandler.bind(undefined, name),
|
|
537
|
+
validator: undefined,
|
|
538
|
+
// Default to a warning policy.
|
|
539
|
+
unfinishedPolicy: common_1.HandlerUnfinishedPolicy.WARN_AND_ABANDON,
|
|
540
|
+
}
|
|
541
|
+
: null);
|
|
542
|
+
// If we don't have an entry from either source, buffer and return
|
|
543
|
+
if (entry === null) {
|
|
449
544
|
this.bufferedUpdates.push(activation);
|
|
450
545
|
return;
|
|
451
546
|
}
|
|
@@ -506,7 +601,7 @@ class Activator {
|
|
|
506
601
|
this.rejectUpdate(protocolInstanceId, error);
|
|
507
602
|
}
|
|
508
603
|
else {
|
|
509
|
-
|
|
604
|
+
this.handleWorkflowFailure(error);
|
|
510
605
|
}
|
|
511
606
|
})
|
|
512
607
|
.finally(() => this.inProgressUpdates.delete(updateId));
|
|
@@ -526,13 +621,22 @@ class Activator {
|
|
|
526
621
|
dispatchBufferedUpdates() {
|
|
527
622
|
const bufferedUpdates = this.bufferedUpdates;
|
|
528
623
|
while (bufferedUpdates.length) {
|
|
529
|
-
|
|
530
|
-
if (
|
|
531
|
-
|
|
532
|
-
|
|
624
|
+
// We have a default update handler, so all updates are dispatchable.
|
|
625
|
+
if (this.defaultUpdateHandler) {
|
|
626
|
+
const update = bufferedUpdates.shift();
|
|
627
|
+
// Logically, this must be defined as we're in the loop.
|
|
628
|
+
// But Typescript doesn't know that so we use a non-null assertion (!).
|
|
629
|
+
this.doUpdate(update);
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
const foundIndex = bufferedUpdates.findIndex((update) => this.updateHandlers.has(update.name));
|
|
633
|
+
if (foundIndex === -1) {
|
|
634
|
+
// No buffered Updates have a handler yet.
|
|
635
|
+
break;
|
|
636
|
+
}
|
|
637
|
+
const [update] = bufferedUpdates.splice(foundIndex, 1);
|
|
638
|
+
this.doUpdate(update);
|
|
533
639
|
}
|
|
534
|
-
const [update] = bufferedUpdates.splice(foundIndex, 1);
|
|
535
|
-
this.doUpdate(update);
|
|
536
640
|
}
|
|
537
641
|
}
|
|
538
642
|
rejectBufferedUpdates() {
|
|
@@ -617,6 +721,8 @@ class Activator {
|
|
|
617
721
|
}
|
|
618
722
|
}
|
|
619
723
|
warnIfUnfinishedHandlers() {
|
|
724
|
+
if (this.workflowTaskError)
|
|
725
|
+
return;
|
|
620
726
|
const getWarnable = (handlerExecutions) => {
|
|
621
727
|
return Array.from(handlerExecutions).filter((ex) => ex.unfinishedPolicy === common_1.HandlerUnfinishedPolicy.WARN_AND_ABANDON);
|
|
622
728
|
};
|
|
@@ -717,19 +823,14 @@ class Activator {
|
|
|
717
823
|
* Transforms failures into a command to be sent to the server.
|
|
718
824
|
* Used to handle any failure emitted by the Workflow.
|
|
719
825
|
*/
|
|
720
|
-
|
|
826
|
+
handleWorkflowFailure(error) {
|
|
721
827
|
if (this.cancelled && (0, errors_1.isCancellation)(error)) {
|
|
722
828
|
this.pushCommand({ cancelWorkflowExecution: {} }, true);
|
|
723
829
|
}
|
|
724
830
|
else if (error instanceof interfaces_1.ContinueAsNew) {
|
|
725
831
|
this.pushCommand({ continueAsNewWorkflowExecution: error.command }, true);
|
|
726
832
|
}
|
|
727
|
-
else {
|
|
728
|
-
if (!(error instanceof common_1.TemporalFailure)) {
|
|
729
|
-
// This results in an unhandled rejection which will fail the activation
|
|
730
|
-
// preventing it from completing.
|
|
731
|
-
throw error;
|
|
732
|
-
}
|
|
833
|
+
else if (error instanceof common_1.TemporalFailure) {
|
|
733
834
|
// Fail the workflow. We do not want to issue unfinishedHandlers warnings. To achieve that, we
|
|
734
835
|
// mark all handlers as completed now.
|
|
735
836
|
this.inProgressSignals.clear();
|
|
@@ -740,6 +841,27 @@ class Activator {
|
|
|
740
841
|
},
|
|
741
842
|
}, true);
|
|
742
843
|
}
|
|
844
|
+
else {
|
|
845
|
+
this.recordWorkflowTaskError(error);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
recordWorkflowTaskError(error) {
|
|
849
|
+
// Only keep the first error that bubbles up; subsequent errors will be ignored.
|
|
850
|
+
if (this.workflowTaskError === undefined)
|
|
851
|
+
this.workflowTaskError = error;
|
|
852
|
+
// Immediately rethrow the error if we know it is safe to do so (i.e. we are not running async
|
|
853
|
+
// microtasks). Otherwise, the error will be rethrown whenever we get an opportunity to do so,
|
|
854
|
+
// e.g. the next time `tryUnblockConditions()` is called.
|
|
855
|
+
if (this.rethrowSynchronously)
|
|
856
|
+
this.maybeRethrowWorkflowTaskError();
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* If a Workflow Task error was captured, and we are running in synchronous mode,
|
|
860
|
+
* then bubble it up now. This is safe to call even if there is no error to rethrow.
|
|
861
|
+
*/
|
|
862
|
+
maybeRethrowWorkflowTaskError() {
|
|
863
|
+
if (this.workflowTaskError)
|
|
864
|
+
throw this.workflowTaskError;
|
|
743
865
|
}
|
|
744
866
|
completeQuery(queryId, result) {
|
|
745
867
|
this.pushCommand({
|