@temporalio/workflow 1.4.4 → 1.5.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/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/src/stack-helpers.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { PromiseStackStore } from './internals';
|
|
|
4
4
|
* Helper function to remove a promise from being tracked for stack trace query purposes
|
|
5
5
|
*/
|
|
6
6
|
export function untrackPromise(promise: Promise<unknown>): void {
|
|
7
|
-
const store = (globalThis as any).__TEMPORAL__?.promiseStackStore as PromiseStackStore | undefined;
|
|
7
|
+
const store = (globalThis as any).__TEMPORAL__?.activator?.promiseStackStore as PromiseStackStore | undefined;
|
|
8
8
|
if (!store) return;
|
|
9
9
|
store.childToParent.delete(promise);
|
|
10
10
|
store.promiseToStack.delete(promise);
|
package/src/worker-interface.ts
CHANGED
|
@@ -3,43 +3,20 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import { IllegalStateError
|
|
6
|
+
import { IllegalStateError } from '@temporalio/common';
|
|
7
7
|
import { msToTs, tsToMs } from '@temporalio/common/lib/time';
|
|
8
8
|
import { composeInterceptors } from '@temporalio/common/lib/interceptors';
|
|
9
9
|
import type { coresdk } from '@temporalio/proto';
|
|
10
|
-
import { alea } from './alea';
|
|
11
10
|
import { storage } from './cancellation-scope';
|
|
12
11
|
import { DeterminismViolationError } from './errors';
|
|
13
12
|
import { WorkflowInterceptorsFactory } from './interceptors';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
13
|
+
import { WorkflowCreateOptionsWithSourceMap } from './interfaces';
|
|
14
|
+
import { Activator, getActivator } from './internals';
|
|
16
15
|
import { SinkCall } from './sinks';
|
|
17
|
-
import type { RawSourceMap } from 'source-map';
|
|
18
16
|
|
|
19
17
|
// Export the type for use on the "worker" side
|
|
20
18
|
export { PromiseStackStore } from './internals';
|
|
21
19
|
|
|
22
|
-
export interface WorkflowCreateOptions {
|
|
23
|
-
info: WorkflowInfo;
|
|
24
|
-
randomnessSeed: number[];
|
|
25
|
-
now: number;
|
|
26
|
-
patches: string[];
|
|
27
|
-
showStackTraceSources: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface WorkflowCreateOptionsWithSourceMap extends WorkflowCreateOptions {
|
|
31
|
-
sourceMap: RawSourceMap;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface ImportFunctions {
|
|
35
|
-
importWorkflows: WorkflowsImportFunc;
|
|
36
|
-
importInterceptors: InterceptorsImportFunc;
|
|
37
|
-
}
|
|
38
|
-
export function setImportFuncs({ importWorkflows, importInterceptors }: ImportFunctions): void {
|
|
39
|
-
state.importWorkflows = importWorkflows;
|
|
40
|
-
state.importInterceptors = importInterceptors;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
20
|
const global = globalThis as any;
|
|
44
21
|
const OriginalDate = globalThis.Date;
|
|
45
22
|
|
|
@@ -60,11 +37,11 @@ export function overrideGlobals(): void {
|
|
|
60
37
|
if (args.length > 0) {
|
|
61
38
|
return new (OriginalDate as any)(...args);
|
|
62
39
|
}
|
|
63
|
-
return new OriginalDate(
|
|
40
|
+
return new OriginalDate(getActivator().now);
|
|
64
41
|
};
|
|
65
42
|
|
|
66
43
|
global.Date.now = function () {
|
|
67
|
-
return
|
|
44
|
+
return getActivator().now;
|
|
68
45
|
};
|
|
69
46
|
|
|
70
47
|
global.Date.parse = OriginalDate.parse.bind(OriginalDate);
|
|
@@ -76,12 +53,13 @@ export function overrideGlobals(): void {
|
|
|
76
53
|
* @param ms sleep duration - number of milliseconds. If given a negative number, value will be set to 1.
|
|
77
54
|
*/
|
|
78
55
|
global.setTimeout = function (cb: (...args: any[]) => any, ms: number, ...args: any[]): number {
|
|
56
|
+
const activator = getActivator();
|
|
79
57
|
ms = Math.max(1, ms);
|
|
80
|
-
const seq =
|
|
58
|
+
const seq = activator.nextSeqs.timer++;
|
|
81
59
|
// Create a Promise for AsyncLocalStorage to be able to track this completion using promise hooks.
|
|
82
60
|
new Promise((resolve, reject) => {
|
|
83
|
-
|
|
84
|
-
|
|
61
|
+
activator.completions.timer.set(seq, { resolve, reject });
|
|
62
|
+
activator.pushCommand({
|
|
85
63
|
startTimer: {
|
|
86
64
|
seq,
|
|
87
65
|
startToFireTimeout: msToTs(ms),
|
|
@@ -95,17 +73,18 @@ export function overrideGlobals(): void {
|
|
|
95
73
|
};
|
|
96
74
|
|
|
97
75
|
global.clearTimeout = function (handle: number): void {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
76
|
+
const activator = getActivator();
|
|
77
|
+
activator.nextSeqs.timer++;
|
|
78
|
+
activator.completions.timer.delete(handle);
|
|
79
|
+
activator.pushCommand({
|
|
101
80
|
cancelTimer: {
|
|
102
81
|
seq: handle,
|
|
103
82
|
},
|
|
104
83
|
});
|
|
105
84
|
};
|
|
106
85
|
|
|
107
|
-
//
|
|
108
|
-
Math.random = () =>
|
|
86
|
+
// activator.random is mutable, don't hardcode its reference
|
|
87
|
+
Math.random = () => getActivator().random();
|
|
109
88
|
}
|
|
110
89
|
|
|
111
90
|
/**
|
|
@@ -113,55 +92,36 @@ export function overrideGlobals(): void {
|
|
|
113
92
|
*
|
|
114
93
|
* Sets required internal state and instantiates the workflow and interceptors.
|
|
115
94
|
*/
|
|
116
|
-
export
|
|
117
|
-
info
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
// Set the runId globally on the context so it can be retrieved in the case
|
|
125
|
-
// of an unhandled promise rejection.
|
|
126
|
-
global.__TEMPORAL__.runId = info.runId;
|
|
127
|
-
// Set the promiseStackStore so promises can be tracked
|
|
128
|
-
global.__TEMPORAL__.promiseStackStore = {
|
|
129
|
-
promiseToStack: new Map(),
|
|
130
|
-
childToParent: new Map(),
|
|
131
|
-
};
|
|
95
|
+
export function initRuntime(options: WorkflowCreateOptionsWithSourceMap): void {
|
|
96
|
+
const { info } = options;
|
|
97
|
+
info.unsafe.now = OriginalDate.now;
|
|
98
|
+
const activator = new Activator(options);
|
|
99
|
+
// There's on activator per workflow instance, set it globally on the context.
|
|
100
|
+
// We do this before importing any user code so user code can statically reference @temporalio/workflow functions
|
|
101
|
+
// as well as Date and Math.random.
|
|
102
|
+
global.__TEMPORAL__.activator = activator;
|
|
132
103
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
state.random = alea(randomnessSeed);
|
|
137
|
-
state.showStackTraceSources = showStackTraceSources;
|
|
138
|
-
state.sourceMap = sourceMap;
|
|
139
|
-
|
|
140
|
-
if (info.unsafe.isReplaying) {
|
|
141
|
-
for (const patch of patches) {
|
|
142
|
-
state.knownPresentPatches.add(patch);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// @ts-expect-error this is a webpack alias to payloadConverterPath
|
|
147
|
-
const customPayloadConverter = (await import('__temporal_custom_payload_converter')).payloadConverter;
|
|
104
|
+
// webpack alias to payloadConverterPath
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
106
|
+
const customPayloadConverter = require('__temporal_custom_payload_converter').payloadConverter;
|
|
148
107
|
// The `payloadConverter` export is validated in the Worker
|
|
149
|
-
if (customPayloadConverter
|
|
150
|
-
|
|
108
|
+
if (customPayloadConverter != null) {
|
|
109
|
+
activator.payloadConverter = customPayloadConverter;
|
|
151
110
|
}
|
|
152
|
-
//
|
|
153
|
-
|
|
111
|
+
// webpack alias to failureConverterPath
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
113
|
+
const customFailureConverter = require('__temporal_custom_failure_converter').failureConverter;
|
|
154
114
|
// The `failureConverter` export is validated in the Worker
|
|
155
|
-
if (customFailureConverter
|
|
156
|
-
|
|
115
|
+
if (customFailureConverter != null) {
|
|
116
|
+
activator.failureConverter = customFailureConverter;
|
|
157
117
|
}
|
|
158
118
|
|
|
159
|
-
const { importWorkflows, importInterceptors } =
|
|
119
|
+
const { importWorkflows, importInterceptors } = global.__TEMPORAL__;
|
|
160
120
|
if (importWorkflows === undefined || importInterceptors === undefined) {
|
|
161
|
-
throw new IllegalStateError('Workflow
|
|
121
|
+
throw new IllegalStateError('Workflow bundle did not register import hooks');
|
|
162
122
|
}
|
|
163
123
|
|
|
164
|
-
const interceptors =
|
|
124
|
+
const interceptors = importInterceptors();
|
|
165
125
|
for (const mod of interceptors) {
|
|
166
126
|
const factory: WorkflowInterceptorsFactory = mod.interceptors;
|
|
167
127
|
if (factory !== undefined) {
|
|
@@ -169,18 +129,18 @@ export async function initRuntime({
|
|
|
169
129
|
throw new TypeError(`interceptors must be a function, got: ${factory}`);
|
|
170
130
|
}
|
|
171
131
|
const interceptors = factory();
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
132
|
+
activator.interceptors.inbound.push(...(interceptors.inbound ?? []));
|
|
133
|
+
activator.interceptors.outbound.push(...(interceptors.outbound ?? []));
|
|
134
|
+
activator.interceptors.internals.push(...(interceptors.internals ?? []));
|
|
175
135
|
}
|
|
176
136
|
}
|
|
177
137
|
|
|
178
|
-
const mod =
|
|
138
|
+
const mod = importWorkflows();
|
|
179
139
|
const workflow = mod[info.workflowType];
|
|
180
140
|
if (typeof workflow !== 'function') {
|
|
181
141
|
throw new TypeError(`'${info.workflowType}' is not a function`);
|
|
182
142
|
}
|
|
183
|
-
|
|
143
|
+
activator.workflow = workflow;
|
|
184
144
|
}
|
|
185
145
|
|
|
186
146
|
/**
|
|
@@ -188,23 +148,21 @@ export async function initRuntime({
|
|
|
188
148
|
* @returns a boolean indicating whether job was processed or ignored
|
|
189
149
|
*/
|
|
190
150
|
export function activate(activation: coresdk.workflow_activation.WorkflowActivation, batchIndex: number): void {
|
|
191
|
-
const
|
|
151
|
+
const activator = getActivator();
|
|
152
|
+
const intercept = composeInterceptors(activator.interceptors.internals, 'activate', ({ activation, batchIndex }) => {
|
|
192
153
|
if (batchIndex === 0) {
|
|
193
|
-
if (state.info === undefined) {
|
|
194
|
-
throw new IllegalStateError('Workflow has not been initialized');
|
|
195
|
-
}
|
|
196
154
|
if (!activation.jobs) {
|
|
197
155
|
throw new TypeError('Got activation with no jobs');
|
|
198
156
|
}
|
|
199
157
|
if (activation.timestamp != null) {
|
|
200
158
|
// timestamp will not be updated for activation that contain only queries
|
|
201
|
-
|
|
159
|
+
activator.now = tsToMs(activation.timestamp);
|
|
202
160
|
}
|
|
203
161
|
if (activation.historyLength == null) {
|
|
204
162
|
throw new TypeError('Got activation with no historyLength');
|
|
205
163
|
}
|
|
206
|
-
|
|
207
|
-
|
|
164
|
+
activator.info.unsafe.isReplaying = activation.isReplaying ?? false;
|
|
165
|
+
activator.info.historyLength = activation.historyLength;
|
|
208
166
|
}
|
|
209
167
|
|
|
210
168
|
// Cast from the interface to the class which has the `variant` attribute.
|
|
@@ -223,10 +181,10 @@ export function activate(activation: coresdk.workflow_activation.WorkflowActivat
|
|
|
223
181
|
// The only job that can be executed on a completed workflow is a query.
|
|
224
182
|
// We might get other jobs after completion for instance when a single
|
|
225
183
|
// activation contains multiple jobs and the first one completes the workflow.
|
|
226
|
-
if (
|
|
184
|
+
if (activator.completed && job.variant !== 'queryWorkflow') {
|
|
227
185
|
return;
|
|
228
186
|
}
|
|
229
|
-
|
|
187
|
+
activator[job.variant](variant as any /* TS can't infer this type */);
|
|
230
188
|
if (showUnblockConditions(job)) {
|
|
231
189
|
tryUnblockConditions();
|
|
232
190
|
}
|
|
@@ -245,18 +203,18 @@ export function activate(activation: coresdk.workflow_activation.WorkflowActivat
|
|
|
245
203
|
* Activation failures are handled in the main Node.js isolate.
|
|
246
204
|
*/
|
|
247
205
|
export function concludeActivation(): coresdk.workflow_completion.IWorkflowActivationCompletion {
|
|
248
|
-
const
|
|
249
|
-
const
|
|
250
|
-
const {
|
|
251
|
-
|
|
206
|
+
const activator = getActivator();
|
|
207
|
+
const intercept = composeInterceptors(activator.interceptors.internals, 'concludeActivation', (input) => input);
|
|
208
|
+
const { info } = activator;
|
|
209
|
+
const { commands } = intercept({ commands: activator.getAndResetCommands() });
|
|
252
210
|
return {
|
|
253
|
-
runId: info
|
|
211
|
+
runId: info.runId,
|
|
254
212
|
successful: { commands },
|
|
255
213
|
};
|
|
256
214
|
}
|
|
257
215
|
|
|
258
216
|
export function getAndResetSinkCalls(): SinkCall[] {
|
|
259
|
-
return
|
|
217
|
+
return getActivator().getAndResetSinkCalls();
|
|
260
218
|
}
|
|
261
219
|
|
|
262
220
|
/**
|
|
@@ -268,12 +226,12 @@ export function tryUnblockConditions(): number {
|
|
|
268
226
|
let numUnblocked = 0;
|
|
269
227
|
for (;;) {
|
|
270
228
|
const prevUnblocked = numUnblocked;
|
|
271
|
-
for (const [seq, cond] of
|
|
229
|
+
for (const [seq, cond] of getActivator().blockedConditions.entries()) {
|
|
272
230
|
if (cond.fn()) {
|
|
273
231
|
cond.resolve();
|
|
274
232
|
numUnblocked++;
|
|
275
233
|
// It is safe to delete elements during map iteration
|
|
276
|
-
|
|
234
|
+
getActivator().blockedConditions.delete(seq);
|
|
277
235
|
}
|
|
278
236
|
}
|
|
279
237
|
if (prevUnblocked === numUnblocked) {
|
|
@@ -290,13 +248,9 @@ export function showUnblockConditions(job: coresdk.workflow_activation.IWorkflow
|
|
|
290
248
|
return !job.queryWorkflow && !job.notifyHasPatch;
|
|
291
249
|
}
|
|
292
250
|
|
|
293
|
-
export
|
|
294
|
-
const dispose = composeInterceptors(
|
|
251
|
+
export function dispose(): void {
|
|
252
|
+
const dispose = composeInterceptors(getActivator().interceptors.internals, 'dispose', async () => {
|
|
295
253
|
storage.disable();
|
|
296
254
|
});
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
export function errorToFailure(err: unknown): ProtoFailure {
|
|
301
|
-
return state.failureConverter.errorToFailure(err);
|
|
255
|
+
dispose({});
|
|
302
256
|
}
|