@temporalio/workflow 1.4.3 → 1.5.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/cancellation-scope.d.ts +1 -2
- package/lib/cancellation-scope.js +6 -4
- package/lib/cancellation-scope.js.map +1 -1
- package/lib/errors.d.ts +0 -1
- package/lib/errors.js +1 -3
- package/lib/errors.js.map +1 -1
- package/lib/index.d.ts +4 -3
- package/lib/index.js +1 -3
- package/lib/index.js.map +1 -1
- package/lib/interceptors.d.ts +1 -1
- package/lib/interfaces.d.ts +13 -3
- package/lib/internals.d.ts +81 -72
- package/lib/internals.js +328 -305
- package/lib/internals.js.map +1 -1
- package/lib/stack-helpers.js +1 -1
- package/lib/stack-helpers.js.map +1 -1
- package/lib/worker-interface.d.ts +3 -27
- package/lib/worker-interface.js +57 -100
- package/lib/worker-interface.js.map +1 -1
- package/lib/workflow.js +92 -86
- package/lib/workflow.js.map +1 -1
- package/package.json +4 -4
- package/src/cancellation-scope.ts +7 -5
- package/src/errors.ts +0 -2
- package/src/index.ts +16 -5
- package/src/interceptors.ts +1 -1
- package/src/interfaces.ts +15 -3
- package/src/internals.ts +403 -347
- package/src/stack-helpers.ts +1 -1
- package/src/worker-interface.ts +59 -105
- package/src/workflow.ts +93 -88
package/lib/internals.js
CHANGED
|
@@ -1,23 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
-
};
|
|
7
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
8
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
9
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
10
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
11
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
12
|
-
};
|
|
13
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
4
|
};
|
|
16
|
-
var _State_now;
|
|
17
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
-
exports.
|
|
6
|
+
exports.getActivator = exports.Activator = exports.LATEST_INTERNAL_PATCH_NUMBER = exports.LocalActivityDoBackoff = void 0;
|
|
19
7
|
const common_1 = require("@temporalio/common");
|
|
20
|
-
const common_2 = require("@temporalio/common");
|
|
21
8
|
const interceptors_1 = require("@temporalio/common/lib/interceptors");
|
|
22
9
|
const type_helpers_1 = require("@temporalio/common/lib/type-helpers");
|
|
23
10
|
const alea_1 = require("./alea");
|
|
@@ -43,14 +30,214 @@ class LocalActivityDoBackoff {
|
|
|
43
30
|
}
|
|
44
31
|
}
|
|
45
32
|
exports.LocalActivityDoBackoff = LocalActivityDoBackoff;
|
|
33
|
+
/**
|
|
34
|
+
* SDK Internal Patches are created by the SDK to avoid breaking history when behaviour
|
|
35
|
+
* of existing API need to be modified. This is the patch number supported by the current
|
|
36
|
+
* version of the SDK.
|
|
37
|
+
*
|
|
38
|
+
* History:
|
|
39
|
+
* 1: Fix `condition(..., 0)` is not the same as `condition(..., undefined)`
|
|
40
|
+
*/
|
|
41
|
+
exports.LATEST_INTERNAL_PATCH_NUMBER = 1;
|
|
42
|
+
/**
|
|
43
|
+
* Keeps all of the Workflow runtime state like pending completions for activities and timers.
|
|
44
|
+
*
|
|
45
|
+
* Implements handlers for all workflow activation jobs.
|
|
46
|
+
*/
|
|
46
47
|
class Activator {
|
|
47
|
-
constructor() {
|
|
48
|
+
constructor({ info, now, showStackTraceSources, sourceMap, randomnessSeed, patches, }) {
|
|
49
|
+
/**
|
|
50
|
+
* Map of task sequence to a Completion
|
|
51
|
+
*/
|
|
52
|
+
this.completions = {
|
|
53
|
+
timer: new Map(),
|
|
54
|
+
activity: new Map(),
|
|
55
|
+
childWorkflowStart: new Map(),
|
|
56
|
+
childWorkflowComplete: new Map(),
|
|
57
|
+
signalWorkflow: new Map(),
|
|
58
|
+
cancelWorkflow: new Map(),
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Holds buffered signal calls until a handler is registered
|
|
62
|
+
*/
|
|
63
|
+
this.bufferedSignals = new Map();
|
|
64
|
+
/**
|
|
65
|
+
* Holds buffered query calls until a handler is registered.
|
|
66
|
+
*
|
|
67
|
+
* **IMPORTANT** queries are only buffered until workflow is started.
|
|
68
|
+
* This is required because async interceptors might block workflow function invocation
|
|
69
|
+
* which delays query handler registration.
|
|
70
|
+
*/
|
|
71
|
+
this.bufferedQueries = Array();
|
|
72
|
+
/**
|
|
73
|
+
* Mapping of signal name to handler
|
|
74
|
+
*/
|
|
75
|
+
this.signalHandlers = new Map();
|
|
76
|
+
this.promiseStackStore = {
|
|
77
|
+
promiseToStack: new Map(),
|
|
78
|
+
childToParent: new Map(),
|
|
79
|
+
};
|
|
80
|
+
this.rootScope = new cancellation_scope_1.RootCancellationScope();
|
|
81
|
+
/**
|
|
82
|
+
* Mapping of query name to handler
|
|
83
|
+
*/
|
|
84
|
+
this.queryHandlers = new Map([
|
|
85
|
+
[
|
|
86
|
+
'__stack_trace',
|
|
87
|
+
() => {
|
|
88
|
+
return this.getStackTraces()
|
|
89
|
+
.map((s) => s.formatted)
|
|
90
|
+
.join('\n\n');
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
[
|
|
94
|
+
'__enhanced_stack_trace',
|
|
95
|
+
() => {
|
|
96
|
+
const { sourceMap } = this;
|
|
97
|
+
const sdk = { name: 'typescript', version: pkg_1.default.version };
|
|
98
|
+
const stacks = this.getStackTraces().map(({ structured: locations }) => ({ locations }));
|
|
99
|
+
const sources = {};
|
|
100
|
+
if (this.showStackTraceSources) {
|
|
101
|
+
for (const { locations } of stacks) {
|
|
102
|
+
for (const { filePath } of locations) {
|
|
103
|
+
if (!filePath)
|
|
104
|
+
continue;
|
|
105
|
+
const content = sourceMap?.sourcesContent?.[sourceMap?.sources.indexOf(filePath)];
|
|
106
|
+
if (!content)
|
|
107
|
+
continue;
|
|
108
|
+
sources[filePath] = [
|
|
109
|
+
{
|
|
110
|
+
content,
|
|
111
|
+
lineOffset: 0,
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return { sdk, stacks, sources };
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
]);
|
|
121
|
+
/**
|
|
122
|
+
* Loaded in {@link initRuntime}
|
|
123
|
+
*/
|
|
124
|
+
this.interceptors = { inbound: [], outbound: [], internals: [] };
|
|
125
|
+
/**
|
|
126
|
+
* Buffer that stores all generated commands, reset after each activation
|
|
127
|
+
*/
|
|
128
|
+
this.commands = [];
|
|
129
|
+
/**
|
|
130
|
+
* Stores all {@link condition}s that haven't been unblocked yet
|
|
131
|
+
*/
|
|
132
|
+
this.blockedConditions = new Map();
|
|
133
|
+
/**
|
|
134
|
+
* Is this Workflow completed?
|
|
135
|
+
*
|
|
136
|
+
* A Workflow will be considered completed if it generates a command that the
|
|
137
|
+
* system considers as a final Workflow command (e.g.
|
|
138
|
+
* completeWorkflowExecution or failWorkflowExecution).
|
|
139
|
+
*/
|
|
140
|
+
this.completed = false;
|
|
141
|
+
/**
|
|
142
|
+
* Was this Workflow cancelled?
|
|
143
|
+
*/
|
|
144
|
+
this.cancelled = false;
|
|
145
|
+
/**
|
|
146
|
+
* This is tracked to allow buffering queries until a workflow function is called.
|
|
147
|
+
* TODO(bergundy): I don't think this makes sense since queries run last in an activation and must be responded to in
|
|
148
|
+
* the same activation.
|
|
149
|
+
*/
|
|
48
150
|
this.workflowFunctionWasCalled = false;
|
|
151
|
+
/**
|
|
152
|
+
* The next (incremental) sequence to assign when generating completable commands
|
|
153
|
+
*/
|
|
154
|
+
this.nextSeqs = {
|
|
155
|
+
timer: 1,
|
|
156
|
+
activity: 1,
|
|
157
|
+
childWorkflow: 1,
|
|
158
|
+
signalWorkflow: 1,
|
|
159
|
+
cancelWorkflow: 1,
|
|
160
|
+
condition: 1,
|
|
161
|
+
// Used internally to keep track of active stack traces
|
|
162
|
+
stack: 1,
|
|
163
|
+
};
|
|
164
|
+
this.payloadConverter = common_1.defaultPayloadConverter;
|
|
165
|
+
this.failureConverter = common_1.defaultFailureConverter;
|
|
166
|
+
/**
|
|
167
|
+
* Patches we know the status of for this workflow, as in {@link patched}
|
|
168
|
+
*/
|
|
169
|
+
this.knownPresentPatches = new Set();
|
|
170
|
+
/**
|
|
171
|
+
* Patches we sent to core {@link patched}
|
|
172
|
+
*/
|
|
173
|
+
this.sentPatches = new Set();
|
|
174
|
+
/**
|
|
175
|
+
* SDK Internal Patches are created by the SDK to avoid breaking history when behaviour
|
|
176
|
+
* of existing API need to be modified.
|
|
177
|
+
*/
|
|
178
|
+
this.internalPatchNumber = 0;
|
|
179
|
+
this.sinkCalls = Array();
|
|
180
|
+
this.info = info;
|
|
181
|
+
this.now = now;
|
|
182
|
+
this.showStackTraceSources = showStackTraceSources;
|
|
183
|
+
this.sourceMap = sourceMap;
|
|
184
|
+
this.random = (0, alea_1.alea)(randomnessSeed);
|
|
185
|
+
if (info.unsafe.isReplaying) {
|
|
186
|
+
for (const patch of patches) {
|
|
187
|
+
this.knownPresentPatches.add(patch);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
getStackTraces() {
|
|
192
|
+
const { childToParent, promiseToStack } = this.promiseStackStore;
|
|
193
|
+
const internalNodes = [...childToParent.values()].reduce((acc, curr) => {
|
|
194
|
+
for (const p of curr) {
|
|
195
|
+
acc.add(p);
|
|
196
|
+
}
|
|
197
|
+
return acc;
|
|
198
|
+
}, new Set());
|
|
199
|
+
const stacks = new Map();
|
|
200
|
+
for (const child of childToParent.keys()) {
|
|
201
|
+
if (!internalNodes.has(child)) {
|
|
202
|
+
const stack = promiseToStack.get(child);
|
|
203
|
+
if (!stack || !stack.formatted)
|
|
204
|
+
continue;
|
|
205
|
+
stacks.set(stack.formatted, stack);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Not 100% sure where this comes from, just filter it out
|
|
209
|
+
stacks.delete(' at Promise.then (<anonymous>)');
|
|
210
|
+
stacks.delete(' at Promise.then (<anonymous>)\n');
|
|
211
|
+
return [...stacks].map(([_, stack]) => stack);
|
|
212
|
+
}
|
|
213
|
+
getAndResetSinkCalls() {
|
|
214
|
+
const { sinkCalls } = this;
|
|
215
|
+
this.sinkCalls = [];
|
|
216
|
+
return sinkCalls;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Buffer a Workflow command to be collected at the end of the current activation.
|
|
220
|
+
*
|
|
221
|
+
* Prevents commands from being added after Workflow completion.
|
|
222
|
+
*/
|
|
223
|
+
pushCommand(cmd, complete = false) {
|
|
224
|
+
// Only query responses may be sent after completion
|
|
225
|
+
if (this.completed && !cmd.respondToQuery)
|
|
226
|
+
return;
|
|
227
|
+
this.commands.push(cmd);
|
|
228
|
+
if (complete) {
|
|
229
|
+
this.completed = true;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
getAndResetCommands() {
|
|
233
|
+
const commands = this.commands;
|
|
234
|
+
this.commands = [];
|
|
235
|
+
return commands;
|
|
49
236
|
}
|
|
50
237
|
async startWorkflowNextHandler({ args }) {
|
|
51
|
-
const { workflow } =
|
|
238
|
+
const { workflow } = this;
|
|
52
239
|
if (workflow === undefined) {
|
|
53
|
-
throw new
|
|
240
|
+
throw new common_1.IllegalStateError('Workflow uninitialized');
|
|
54
241
|
}
|
|
55
242
|
let promise;
|
|
56
243
|
try {
|
|
@@ -61,7 +248,7 @@ class Activator {
|
|
|
61
248
|
// Otherwise this Workflow will now be queryable.
|
|
62
249
|
this.workflowFunctionWasCalled = true;
|
|
63
250
|
// Empty the buffer
|
|
64
|
-
const buffer =
|
|
251
|
+
const buffer = this.bufferedQueries.splice(0);
|
|
65
252
|
for (const activation of buffer) {
|
|
66
253
|
this.queryWorkflow(activation);
|
|
67
254
|
}
|
|
@@ -69,44 +256,40 @@ class Activator {
|
|
|
69
256
|
return await promise;
|
|
70
257
|
}
|
|
71
258
|
startWorkflow(activation) {
|
|
72
|
-
const
|
|
73
|
-
if (info === undefined) {
|
|
74
|
-
throw new common_2.IllegalStateError('Workflow has not been initialized');
|
|
75
|
-
}
|
|
76
|
-
const execute = (0, interceptors_1.composeInterceptors)(exports.state.interceptors.inbound, 'execute', this.startWorkflowNextHandler.bind(this));
|
|
259
|
+
const execute = (0, interceptors_1.composeInterceptors)(this.interceptors.inbound, 'execute', this.startWorkflowNextHandler.bind(this));
|
|
77
260
|
(0, stack_helpers_1.untrackPromise)(execute({
|
|
78
261
|
headers: activation.headers ?? {},
|
|
79
|
-
args: (0,
|
|
80
|
-
}).then(completeWorkflow, handleWorkflowFailure));
|
|
262
|
+
args: (0, common_1.arrayFromPayloads)(this.payloadConverter, activation.arguments),
|
|
263
|
+
}).then(this.completeWorkflow.bind(this), this.handleWorkflowFailure.bind(this)));
|
|
81
264
|
}
|
|
82
265
|
cancelWorkflow(_activation) {
|
|
83
|
-
|
|
84
|
-
|
|
266
|
+
this.cancelled = true;
|
|
267
|
+
this.rootScope.cancel();
|
|
85
268
|
}
|
|
86
269
|
fireTimer(activation) {
|
|
87
270
|
// Timers are a special case where their completion might not be in Workflow state,
|
|
88
271
|
// this is due to immediate timer cancellation that doesn't go wait for Core.
|
|
89
|
-
const completion = maybeConsumeCompletion('timer', getSeq(activation));
|
|
272
|
+
const completion = this.maybeConsumeCompletion('timer', getSeq(activation));
|
|
90
273
|
completion?.resolve(undefined);
|
|
91
274
|
}
|
|
92
275
|
resolveActivity(activation) {
|
|
93
276
|
if (!activation.result) {
|
|
94
277
|
throw new TypeError('Got ResolveActivity activation with no result');
|
|
95
278
|
}
|
|
96
|
-
const { resolve, reject } = consumeCompletion('activity', getSeq(activation));
|
|
279
|
+
const { resolve, reject } = this.consumeCompletion('activity', getSeq(activation));
|
|
97
280
|
if (activation.result.completed) {
|
|
98
281
|
const completed = activation.result.completed;
|
|
99
|
-
const result = completed.result ?
|
|
282
|
+
const result = completed.result ? this.payloadConverter.fromPayload(completed.result) : undefined;
|
|
100
283
|
resolve(result);
|
|
101
284
|
}
|
|
102
285
|
else if (activation.result.failed) {
|
|
103
286
|
const { failure } = activation.result.failed;
|
|
104
|
-
const err = failure ?
|
|
287
|
+
const err = failure ? this.failureToError(failure) : undefined;
|
|
105
288
|
reject(err);
|
|
106
289
|
}
|
|
107
290
|
else if (activation.result.cancelled) {
|
|
108
291
|
const { failure } = activation.result.cancelled;
|
|
109
|
-
const err = failure ?
|
|
292
|
+
const err = failure ? this.failureToError(failure) : undefined;
|
|
110
293
|
reject(err);
|
|
111
294
|
}
|
|
112
295
|
else if (activation.result.backoff) {
|
|
@@ -114,25 +297,25 @@ class Activator {
|
|
|
114
297
|
}
|
|
115
298
|
}
|
|
116
299
|
resolveChildWorkflowExecutionStart(activation) {
|
|
117
|
-
const { resolve, reject } = consumeCompletion('childWorkflowStart', getSeq(activation));
|
|
300
|
+
const { resolve, reject } = this.consumeCompletion('childWorkflowStart', getSeq(activation));
|
|
118
301
|
if (activation.succeeded) {
|
|
119
302
|
resolve(activation.succeeded.runId);
|
|
120
303
|
}
|
|
121
304
|
else if (activation.failed) {
|
|
122
305
|
if (activation.failed.cause !==
|
|
123
306
|
StartChildWorkflowExecutionFailedCause.START_CHILD_WORKFLOW_EXECUTION_FAILED_CAUSE_WORKFLOW_ALREADY_EXISTS) {
|
|
124
|
-
throw new
|
|
307
|
+
throw new common_1.IllegalStateError('Got unknown StartChildWorkflowExecutionFailedCause');
|
|
125
308
|
}
|
|
126
309
|
if (!(activation.seq && activation.failed.workflowId && activation.failed.workflowType)) {
|
|
127
310
|
throw new TypeError('Missing attributes in activation job');
|
|
128
311
|
}
|
|
129
|
-
reject(new
|
|
312
|
+
reject(new common_1.WorkflowExecutionAlreadyStartedError('Workflow execution already started', activation.failed.workflowId, activation.failed.workflowType));
|
|
130
313
|
}
|
|
131
314
|
else if (activation.cancelled) {
|
|
132
315
|
if (!activation.cancelled.failure) {
|
|
133
316
|
throw new TypeError('Got no failure in cancelled variant');
|
|
134
317
|
}
|
|
135
|
-
reject(
|
|
318
|
+
reject(this.failureToError(activation.cancelled.failure));
|
|
136
319
|
}
|
|
137
320
|
else {
|
|
138
321
|
throw new TypeError('Got ResolveChildWorkflowExecutionStart with no status');
|
|
@@ -142,10 +325,10 @@ class Activator {
|
|
|
142
325
|
if (!activation.result) {
|
|
143
326
|
throw new TypeError('Got ResolveChildWorkflowExecution activation with no result');
|
|
144
327
|
}
|
|
145
|
-
const { resolve, reject } = consumeCompletion('childWorkflowComplete', getSeq(activation));
|
|
328
|
+
const { resolve, reject } = this.consumeCompletion('childWorkflowComplete', getSeq(activation));
|
|
146
329
|
if (activation.result.completed) {
|
|
147
330
|
const completed = activation.result.completed;
|
|
148
|
-
const result = completed.result ?
|
|
331
|
+
const result = completed.result ? this.payloadConverter.fromPayload(completed.result) : undefined;
|
|
149
332
|
resolve(result);
|
|
150
333
|
}
|
|
151
334
|
else if (activation.result.failed) {
|
|
@@ -153,23 +336,23 @@ class Activator {
|
|
|
153
336
|
if (failure === undefined || failure === null) {
|
|
154
337
|
throw new TypeError('Got failed result with no failure attribute');
|
|
155
338
|
}
|
|
156
|
-
reject(
|
|
339
|
+
reject(this.failureToError(failure));
|
|
157
340
|
}
|
|
158
341
|
else if (activation.result.cancelled) {
|
|
159
342
|
const { failure } = activation.result.cancelled;
|
|
160
343
|
if (failure === undefined || failure === null) {
|
|
161
344
|
throw new TypeError('Got cancelled result with no failure attribute');
|
|
162
345
|
}
|
|
163
|
-
reject(
|
|
346
|
+
reject(this.failureToError(failure));
|
|
164
347
|
}
|
|
165
348
|
}
|
|
166
349
|
// Intentionally not made function async so this handler doesn't show up in the stack trace
|
|
167
350
|
queryWorkflowNextHandler({ queryName, args }) {
|
|
168
|
-
const fn =
|
|
351
|
+
const fn = this.queryHandlers.get(queryName);
|
|
169
352
|
if (fn === undefined) {
|
|
170
|
-
const knownQueryTypes = [...
|
|
353
|
+
const knownQueryTypes = [...this.queryHandlers.keys()].join(' ');
|
|
171
354
|
// Fail the query
|
|
172
|
-
|
|
355
|
+
return Promise.reject(new ReferenceError(`Workflow did not register a handler for ${queryName}. Registered queries: [${knownQueryTypes}]`));
|
|
173
356
|
}
|
|
174
357
|
try {
|
|
175
358
|
const ret = fn(...args);
|
|
@@ -184,25 +367,25 @@ class Activator {
|
|
|
184
367
|
}
|
|
185
368
|
queryWorkflow(activation) {
|
|
186
369
|
if (!this.workflowFunctionWasCalled) {
|
|
187
|
-
|
|
370
|
+
this.bufferedQueries.push(activation);
|
|
188
371
|
return;
|
|
189
372
|
}
|
|
190
373
|
const { queryType, queryId, headers } = activation;
|
|
191
374
|
if (!(queryType && queryId)) {
|
|
192
375
|
throw new TypeError('Missing query activation attributes');
|
|
193
376
|
}
|
|
194
|
-
const execute = (0, interceptors_1.composeInterceptors)(
|
|
377
|
+
const execute = (0, interceptors_1.composeInterceptors)(this.interceptors.inbound, 'handleQuery', this.queryWorkflowNextHandler.bind(this));
|
|
195
378
|
execute({
|
|
196
379
|
queryName: queryType,
|
|
197
|
-
args: (0,
|
|
380
|
+
args: (0, common_1.arrayFromPayloads)(this.payloadConverter, activation.arguments),
|
|
198
381
|
queryId,
|
|
199
382
|
headers: headers ?? {},
|
|
200
|
-
}).then((result) => completeQuery(queryId, result), (reason) => failQuery(queryId, reason));
|
|
383
|
+
}).then((result) => this.completeQuery(queryId, result), (reason) => this.failQuery(queryId, reason));
|
|
201
384
|
}
|
|
202
385
|
async signalWorkflowNextHandler({ signalName, args }) {
|
|
203
|
-
const fn =
|
|
386
|
+
const fn = this.signalHandlers.get(signalName);
|
|
204
387
|
if (fn === undefined) {
|
|
205
|
-
throw new
|
|
388
|
+
throw new common_1.IllegalStateError(`No registered signal handler for signal ${signalName}`);
|
|
206
389
|
}
|
|
207
390
|
return await fn(...args);
|
|
208
391
|
}
|
|
@@ -211,36 +394,36 @@ class Activator {
|
|
|
211
394
|
if (!signalName) {
|
|
212
395
|
throw new TypeError('Missing activation signalName');
|
|
213
396
|
}
|
|
214
|
-
const fn =
|
|
397
|
+
const fn = this.signalHandlers.get(signalName);
|
|
215
398
|
if (fn === undefined) {
|
|
216
|
-
let buffer =
|
|
399
|
+
let buffer = this.bufferedSignals.get(signalName);
|
|
217
400
|
if (buffer === undefined) {
|
|
218
401
|
buffer = [];
|
|
219
|
-
|
|
402
|
+
this.bufferedSignals.set(signalName, buffer);
|
|
220
403
|
}
|
|
221
404
|
buffer.push(activation);
|
|
222
405
|
return;
|
|
223
406
|
}
|
|
224
|
-
const execute = (0, interceptors_1.composeInterceptors)(
|
|
407
|
+
const execute = (0, interceptors_1.composeInterceptors)(this.interceptors.inbound, 'handleSignal', this.signalWorkflowNextHandler.bind(this));
|
|
225
408
|
execute({
|
|
226
|
-
args: (0,
|
|
409
|
+
args: (0, common_1.arrayFromPayloads)(this.payloadConverter, activation.input),
|
|
227
410
|
signalName,
|
|
228
411
|
headers: headers ?? {},
|
|
229
|
-
}).catch(handleWorkflowFailure);
|
|
412
|
+
}).catch(this.handleWorkflowFailure.bind(this));
|
|
230
413
|
}
|
|
231
414
|
resolveSignalExternalWorkflow(activation) {
|
|
232
|
-
const { resolve, reject } = consumeCompletion('signalWorkflow', getSeq(activation));
|
|
415
|
+
const { resolve, reject } = this.consumeCompletion('signalWorkflow', getSeq(activation));
|
|
233
416
|
if (activation.failure) {
|
|
234
|
-
reject(
|
|
417
|
+
reject(this.failureToError(activation.failure));
|
|
235
418
|
}
|
|
236
419
|
else {
|
|
237
420
|
resolve(undefined);
|
|
238
421
|
}
|
|
239
422
|
}
|
|
240
423
|
resolveRequestCancelExternalWorkflow(activation) {
|
|
241
|
-
const { resolve, reject } = consumeCompletion('cancelWorkflow', getSeq(activation));
|
|
424
|
+
const { resolve, reject } = this.consumeCompletion('cancelWorkflow', getSeq(activation));
|
|
242
425
|
if (activation.failure) {
|
|
243
|
-
reject(
|
|
426
|
+
reject(this.failureToError(activation.failure));
|
|
244
427
|
}
|
|
245
428
|
else {
|
|
246
429
|
resolve(undefined);
|
|
@@ -250,274 +433,114 @@ class Activator {
|
|
|
250
433
|
if (!activation.randomnessSeed) {
|
|
251
434
|
throw new TypeError('Expected activation with randomnessSeed attribute');
|
|
252
435
|
}
|
|
253
|
-
|
|
436
|
+
this.random = (0, alea_1.alea)(activation.randomnessSeed.toBytes());
|
|
254
437
|
}
|
|
255
438
|
notifyHasPatch(activation) {
|
|
256
439
|
if (!activation.patchId) {
|
|
257
440
|
throw new TypeError('Notify has patch missing patch name');
|
|
258
441
|
}
|
|
259
|
-
|
|
442
|
+
if (activation.patchId.startsWith('__sdk_internal_patch_number:')) {
|
|
443
|
+
const internalPatchNumber = parseInt(activation.patchId.substring('__sdk_internal_patch_number:'.length));
|
|
444
|
+
if (internalPatchNumber > exports.LATEST_INTERNAL_PATCH_NUMBER)
|
|
445
|
+
throw new common_1.IllegalStateError(`Unsupported internal patch number: ${internalPatchNumber} > ${exports.LATEST_INTERNAL_PATCH_NUMBER}`);
|
|
446
|
+
if (this.internalPatchNumber < internalPatchNumber)
|
|
447
|
+
this.internalPatchNumber = internalPatchNumber;
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
this.knownPresentPatches.add(activation.patchId);
|
|
451
|
+
}
|
|
260
452
|
}
|
|
261
|
-
|
|
262
|
-
|
|
453
|
+
checkInternalPatchAtLeast(minimumPatchNumber) {
|
|
454
|
+
if (this.internalPatchNumber >= minimumPatchNumber)
|
|
455
|
+
return true;
|
|
456
|
+
if (!this.info.unsafe.isReplaying) {
|
|
457
|
+
this.internalPatchNumber = minimumPatchNumber;
|
|
458
|
+
this.pushCommand({
|
|
459
|
+
setPatchMarker: { patchId: `__sdk_internal_patch_number:${exports.LATEST_INTERNAL_PATCH_NUMBER}`, deprecated: false },
|
|
460
|
+
});
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
return false;
|
|
263
464
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Keeps all of the Workflow runtime state like pending completions for activities and timers and the scope stack.
|
|
268
|
-
*
|
|
269
|
-
* State mutates each time the Workflow is activated.
|
|
270
|
-
*/
|
|
271
|
-
class State {
|
|
272
|
-
constructor() {
|
|
273
|
-
/**
|
|
274
|
-
* Activator executes activation jobs
|
|
275
|
-
*/
|
|
276
|
-
this.activator = new Activator();
|
|
277
|
-
/**
|
|
278
|
-
* Map of task sequence to a Completion
|
|
279
|
-
*/
|
|
280
|
-
this.completions = {
|
|
281
|
-
timer: new Map(),
|
|
282
|
-
activity: new Map(),
|
|
283
|
-
childWorkflowStart: new Map(),
|
|
284
|
-
childWorkflowComplete: new Map(),
|
|
285
|
-
signalWorkflow: new Map(),
|
|
286
|
-
cancelWorkflow: new Map(),
|
|
287
|
-
};
|
|
288
|
-
/**
|
|
289
|
-
* Holds buffered signal calls until a handler is registered
|
|
290
|
-
*/
|
|
291
|
-
this.bufferedSignals = new Map();
|
|
292
|
-
/**
|
|
293
|
-
* Holds buffered query calls until a handler is registered.
|
|
294
|
-
*
|
|
295
|
-
* **IMPORTANT** queries are only buffered until workflow is started.
|
|
296
|
-
* This is required because async interceptors might block workflow function invocation
|
|
297
|
-
* which delays query handler registration.
|
|
298
|
-
*/
|
|
299
|
-
this.bufferedQueries = Array();
|
|
300
|
-
/**
|
|
301
|
-
* Mapping of signal name to handler
|
|
302
|
-
*/
|
|
303
|
-
this.signalHandlers = new Map();
|
|
304
|
-
/**
|
|
305
|
-
* Whether or not to send the sources in enhanced stack trace query responses
|
|
306
|
-
*/
|
|
307
|
-
this.showStackTraceSources = false;
|
|
308
|
-
/**
|
|
309
|
-
* Mapping of query name to handler
|
|
310
|
-
*/
|
|
311
|
-
this.queryHandlers = new Map([
|
|
312
|
-
[
|
|
313
|
-
'__stack_trace',
|
|
314
|
-
() => {
|
|
315
|
-
return this.getStackTraces()
|
|
316
|
-
.map((s) => s.formatted)
|
|
317
|
-
.join('\n\n');
|
|
318
|
-
},
|
|
319
|
-
],
|
|
320
|
-
[
|
|
321
|
-
'__enhanced_stack_trace',
|
|
322
|
-
() => {
|
|
323
|
-
const { sourceMap } = this;
|
|
324
|
-
const sdk = { name: 'typescript', version: pkg_1.default.version };
|
|
325
|
-
const stacks = this.getStackTraces().map(({ structured: locations }) => ({ locations }));
|
|
326
|
-
const sources = {};
|
|
327
|
-
if (this.showStackTraceSources) {
|
|
328
|
-
for (const { locations } of stacks) {
|
|
329
|
-
for (const { filePath } of locations) {
|
|
330
|
-
if (!filePath)
|
|
331
|
-
continue;
|
|
332
|
-
const content = sourceMap?.sourcesContent?.[sourceMap?.sources.indexOf(filePath)];
|
|
333
|
-
if (!content)
|
|
334
|
-
continue;
|
|
335
|
-
sources[filePath] = [
|
|
336
|
-
{
|
|
337
|
-
content,
|
|
338
|
-
lineOffset: 0,
|
|
339
|
-
},
|
|
340
|
-
];
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
return { sdk, stacks, sources };
|
|
345
|
-
},
|
|
346
|
-
],
|
|
347
|
-
]);
|
|
348
|
-
/**
|
|
349
|
-
* Loaded in {@link initRuntime}
|
|
350
|
-
*/
|
|
351
|
-
this.interceptors = { inbound: [], outbound: [], internals: [] };
|
|
352
|
-
/**
|
|
353
|
-
* Buffer that stores all generated commands, reset after each activation
|
|
354
|
-
*/
|
|
355
|
-
this.commands = [];
|
|
356
|
-
/**
|
|
357
|
-
* Stores all {@link condition}s that haven't been unblocked yet
|
|
358
|
-
*/
|
|
359
|
-
this.blockedConditions = new Map();
|
|
360
|
-
/**
|
|
361
|
-
* Is this Workflow completed?
|
|
362
|
-
*
|
|
363
|
-
* A Workflow will be considered completed if it generates a command that the
|
|
364
|
-
* system considers as a final Workflow command (e.g.
|
|
365
|
-
* completeWorkflowExecution or failWorkflowExecution).
|
|
366
|
-
*/
|
|
367
|
-
this.completed = false;
|
|
368
|
-
/**
|
|
369
|
-
* Was this Workflow cancelled?
|
|
370
|
-
*/
|
|
371
|
-
this.cancelled = false;
|
|
372
|
-
/**
|
|
373
|
-
* The next (incremental) sequence to assign when generating completable commands
|
|
374
|
-
*/
|
|
375
|
-
this.nextSeqs = {
|
|
376
|
-
timer: 1,
|
|
377
|
-
activity: 1,
|
|
378
|
-
childWorkflow: 1,
|
|
379
|
-
signalWorkflow: 1,
|
|
380
|
-
cancelWorkflow: 1,
|
|
381
|
-
condition: 1,
|
|
382
|
-
// Used internally to keep track of active stack traces
|
|
383
|
-
stack: 1,
|
|
384
|
-
};
|
|
385
|
-
/**
|
|
386
|
-
* This is set every time the workflow executes an activation
|
|
387
|
-
*/
|
|
388
|
-
_State_now.set(this, void 0);
|
|
389
|
-
/**
|
|
390
|
-
* A deterministic RNG, used by the isolate's overridden Math.random
|
|
391
|
-
*/
|
|
392
|
-
this.random = function () {
|
|
393
|
-
throw new common_2.IllegalStateError('Tried to use Math.random before Workflow has been initialized');
|
|
394
|
-
};
|
|
395
|
-
this.payloadConverter = common_2.defaultPayloadConverter;
|
|
396
|
-
this.failureConverter = common_1.defaultFailureConverter;
|
|
397
|
-
/**
|
|
398
|
-
* Patches we know the status of for this workflow, as in {@link patched}
|
|
399
|
-
*/
|
|
400
|
-
this.knownPresentPatches = new Set();
|
|
401
|
-
/**
|
|
402
|
-
* Patches we sent to core {@link patched}
|
|
403
|
-
*/
|
|
404
|
-
this.sentPatches = new Set();
|
|
405
|
-
this.sinkCalls = Array();
|
|
465
|
+
removeFromCache() {
|
|
466
|
+
throw new common_1.IllegalStateError('removeFromCache activation job should not reach workflow');
|
|
406
467
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
468
|
+
/**
|
|
469
|
+
* Transforms failures into a command to be sent to the server.
|
|
470
|
+
* Used to handle any failure emitted by the Workflow.
|
|
471
|
+
*/
|
|
472
|
+
async handleWorkflowFailure(error) {
|
|
473
|
+
if (this.cancelled && (0, errors_1.isCancellation)(error)) {
|
|
474
|
+
this.pushCommand({ cancelWorkflowExecution: {} }, true);
|
|
475
|
+
}
|
|
476
|
+
else if (error instanceof interfaces_1.ContinueAsNew) {
|
|
477
|
+
this.pushCommand({ continueAsNewWorkflowExecution: error.command }, true);
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
if (!(error instanceof common_1.TemporalFailure)) {
|
|
481
|
+
// This results in an unhandled rejection which will fail the activation
|
|
482
|
+
// preventing it from completing.
|
|
483
|
+
throw error;
|
|
422
484
|
}
|
|
485
|
+
this.pushCommand({
|
|
486
|
+
failWorkflowExecution: {
|
|
487
|
+
failure: this.errorToFailure(error),
|
|
488
|
+
},
|
|
489
|
+
}, true);
|
|
423
490
|
}
|
|
424
|
-
// Not 100% sure where this comes from, just filter it out
|
|
425
|
-
stacks.delete(' at Promise.then (<anonymous>)');
|
|
426
|
-
stacks.delete(' at Promise.then (<anonymous>)\n');
|
|
427
|
-
return [...stacks].map(([_, stack]) => stack);
|
|
428
491
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
}
|
|
433
|
-
return __classPrivateFieldGet(this, _State_now, "f");
|
|
492
|
+
completeQuery(queryId, result) {
|
|
493
|
+
this.pushCommand({
|
|
494
|
+
respondToQuery: { queryId, succeeded: { response: this.payloadConverter.toPayload(result) } },
|
|
495
|
+
});
|
|
434
496
|
}
|
|
435
|
-
|
|
436
|
-
|
|
497
|
+
failQuery(queryId, error) {
|
|
498
|
+
this.pushCommand({
|
|
499
|
+
respondToQuery: {
|
|
500
|
+
queryId,
|
|
501
|
+
failed: this.errorToFailure((0, common_1.ensureTemporalFailure)(error)),
|
|
502
|
+
},
|
|
503
|
+
});
|
|
437
504
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
505
|
+
/** Consume a completion if it exists in Workflow state */
|
|
506
|
+
maybeConsumeCompletion(type, taskSeq) {
|
|
507
|
+
const completion = this.completions[type].get(taskSeq);
|
|
508
|
+
if (completion !== undefined) {
|
|
509
|
+
this.completions[type].delete(taskSeq);
|
|
510
|
+
}
|
|
511
|
+
return completion;
|
|
442
512
|
}
|
|
443
|
-
/**
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
pushCommand(cmd, complete = false) {
|
|
449
|
-
// Only query responses may be sent after completion
|
|
450
|
-
if (this.completed && !cmd.respondToQuery)
|
|
451
|
-
return;
|
|
452
|
-
this.commands.push(cmd);
|
|
453
|
-
if (complete) {
|
|
454
|
-
this.completed = true;
|
|
513
|
+
/** Consume a completion if it exists in Workflow state, throws if it doesn't */
|
|
514
|
+
consumeCompletion(type, taskSeq) {
|
|
515
|
+
const completion = this.maybeConsumeCompletion(type, taskSeq);
|
|
516
|
+
if (completion === undefined) {
|
|
517
|
+
throw new common_1.IllegalStateError(`No completion for taskSeq ${taskSeq}`);
|
|
455
518
|
}
|
|
519
|
+
return completion;
|
|
456
520
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
function completeWorkflow(result) {
|
|
462
|
-
exports.state.pushCommand({
|
|
463
|
-
completeWorkflowExecution: {
|
|
464
|
-
result: exports.state.payloadConverter.toPayload(result),
|
|
465
|
-
},
|
|
466
|
-
}, true);
|
|
467
|
-
}
|
|
468
|
-
/**
|
|
469
|
-
* Transforms failures into a command to be sent to the server.
|
|
470
|
-
* Used to handle any failure emitted by the Workflow.
|
|
471
|
-
*/
|
|
472
|
-
async function handleWorkflowFailure(error) {
|
|
473
|
-
if (exports.state.cancelled && (0, errors_1.isCancellation)(error)) {
|
|
474
|
-
exports.state.pushCommand({ cancelWorkflowExecution: {} }, true);
|
|
475
|
-
}
|
|
476
|
-
else if (error instanceof interfaces_1.ContinueAsNew) {
|
|
477
|
-
exports.state.pushCommand({ continueAsNewWorkflowExecution: error.command }, true);
|
|
478
|
-
}
|
|
479
|
-
else {
|
|
480
|
-
if (!(error instanceof common_2.TemporalFailure)) {
|
|
481
|
-
// This results in an unhandled rejection which will fail the activation
|
|
482
|
-
// preventing it from completing.
|
|
483
|
-
throw error;
|
|
484
|
-
}
|
|
485
|
-
exports.state.pushCommand({
|
|
486
|
-
failWorkflowExecution: {
|
|
487
|
-
failure: exports.state.failureConverter.errorToFailure(error),
|
|
521
|
+
completeWorkflow(result) {
|
|
522
|
+
this.pushCommand({
|
|
523
|
+
completeWorkflowExecution: {
|
|
524
|
+
result: this.payloadConverter.toPayload(result),
|
|
488
525
|
},
|
|
489
526
|
}, true);
|
|
490
527
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
async function failQuery(queryId, error) {
|
|
499
|
-
exports.state.pushCommand({
|
|
500
|
-
respondToQuery: { queryId, failed: exports.state.failureConverter.errorToFailure((0, common_2.ensureTemporalFailure)(error)) },
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
/** Consume a completion if it exists in Workflow state */
|
|
504
|
-
function maybeConsumeCompletion(type, taskSeq) {
|
|
505
|
-
const completion = exports.state.completions[type].get(taskSeq);
|
|
506
|
-
if (completion !== undefined) {
|
|
507
|
-
exports.state.completions[type].delete(taskSeq);
|
|
528
|
+
errorToFailure(err) {
|
|
529
|
+
return this.failureConverter.errorToFailure(err, this.payloadConverter);
|
|
530
|
+
}
|
|
531
|
+
failureToError(failure) {
|
|
532
|
+
return this.failureConverter.failureToError(failure, this.payloadConverter);
|
|
508
533
|
}
|
|
509
|
-
return completion;
|
|
510
534
|
}
|
|
511
|
-
exports.
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
return completion;
|
|
535
|
+
exports.Activator = Activator;
|
|
536
|
+
function getActivator() {
|
|
537
|
+
const activator = globalThis.__TEMPORAL__?.activator;
|
|
538
|
+
if (activator === undefined) {
|
|
539
|
+
throw new common_1.IllegalStateError('Workflow uninitialized');
|
|
540
|
+
}
|
|
541
|
+
return activator;
|
|
519
542
|
}
|
|
520
|
-
exports.
|
|
543
|
+
exports.getActivator = getActivator;
|
|
521
544
|
function getSeq(activation) {
|
|
522
545
|
const seq = activation.seq;
|
|
523
546
|
if (seq === undefined || seq === null) {
|