@temporalio/workflow 1.5.2 → 1.7.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 +3 -3
- package/lib/cancellation-scope.js +9 -5
- package/lib/cancellation-scope.js.map +1 -1
- package/lib/errors.d.ts +1 -1
- package/lib/errors.js +1 -1
- package/lib/global-attributes.d.ts +2 -0
- package/lib/global-attributes.js +12 -0
- package/lib/global-attributes.js.map +1 -0
- package/lib/interfaces.d.ts +15 -2
- package/lib/interfaces.js.map +1 -1
- package/lib/internals.d.ts +13 -3
- package/lib/internals.js +38 -13
- package/lib/internals.js.map +1 -1
- package/lib/stack-helpers.js +2 -1
- package/lib/stack-helpers.js.map +1 -1
- package/lib/worker-interface.d.ts +1 -1
- package/lib/worker-interface.js +43 -14
- package/lib/worker-interface.js.map +1 -1
- package/lib/workflow.d.ts +15 -9
- package/lib/workflow.js +43 -23
- package/lib/workflow.js.map +1 -1
- package/package.json +10 -5
- package/src/cancellation-scope.ts +8 -4
- package/src/errors.ts +1 -1
- package/src/global-attributes.ts +7 -0
- package/src/interfaces.ts +33 -4
- package/src/internals.ts +41 -13
- package/src/stack-helpers.ts +2 -1
- package/src/worker-interface.ts +42 -14
- package/src/workflow.ts +49 -40
package/src/worker-interface.ts
CHANGED
|
@@ -7,12 +7,13 @@ 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 {
|
|
10
|
+
import { disableStorage } from './cancellation-scope';
|
|
11
11
|
import { DeterminismViolationError } from './errors';
|
|
12
12
|
import { WorkflowInterceptorsFactory } from './interceptors';
|
|
13
|
-
import { WorkflowCreateOptionsWithSourceMap } from './interfaces';
|
|
13
|
+
import { WorkflowCreateOptionsWithSourceMap, WorkflowInfo } from './interfaces';
|
|
14
14
|
import { Activator, getActivator } from './internals';
|
|
15
15
|
import { SinkCall } from './sinks';
|
|
16
|
+
import { setActivatorUntyped } from './global-attributes';
|
|
16
17
|
|
|
17
18
|
// Export the type for use on the "worker" side
|
|
18
19
|
export { PromiseStackStore } from './internals';
|
|
@@ -22,7 +23,6 @@ const OriginalDate = globalThis.Date;
|
|
|
22
23
|
|
|
23
24
|
export function overrideGlobals(): void {
|
|
24
25
|
// Mock any weak reference because GC is non-deterministic and the effect is observable from the Workflow.
|
|
25
|
-
// WeakRef is implemented in V8 8.4 which is embedded in node >=14.6.0.
|
|
26
26
|
// Workflow developer will get a meaningful exception if they try to use these.
|
|
27
27
|
global.WeakRef = function () {
|
|
28
28
|
throw new DeterminismViolationError('WeakRef cannot be used in Workflows because v8 GC is non-deterministic');
|
|
@@ -93,13 +93,13 @@ export function overrideGlobals(): void {
|
|
|
93
93
|
* Sets required internal state and instantiates the workflow and interceptors.
|
|
94
94
|
*/
|
|
95
95
|
export function initRuntime(options: WorkflowCreateOptionsWithSourceMap): void {
|
|
96
|
-
const
|
|
96
|
+
const info: WorkflowInfo = fixPrototypes(options.info);
|
|
97
97
|
info.unsafe.now = OriginalDate.now;
|
|
98
|
-
const activator = new Activator(options);
|
|
98
|
+
const activator = new Activator({ ...options, info });
|
|
99
99
|
// There's on activator per workflow instance, set it globally on the context.
|
|
100
100
|
// We do this before importing any user code so user code can statically reference @temporalio/workflow functions
|
|
101
101
|
// as well as Date and Math.random.
|
|
102
|
-
|
|
102
|
+
setActivatorUntyped(activator);
|
|
103
103
|
|
|
104
104
|
// webpack alias to payloadConverterPath
|
|
105
105
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
@@ -126,7 +126,7 @@ export function initRuntime(options: WorkflowCreateOptionsWithSourceMap): void {
|
|
|
126
126
|
const factory: WorkflowInterceptorsFactory = mod.interceptors;
|
|
127
127
|
if (factory !== undefined) {
|
|
128
128
|
if (typeof factory !== 'function') {
|
|
129
|
-
throw new TypeError(`interceptors
|
|
129
|
+
throw new TypeError(`Failed to initialize workflows interceptors: expected a function, but got: '${factory}'`);
|
|
130
130
|
}
|
|
131
131
|
const interceptors = factory();
|
|
132
132
|
activator.interceptors.inbound.push(...(interceptors.inbound ?? []));
|
|
@@ -136,11 +136,39 @@ export function initRuntime(options: WorkflowCreateOptionsWithSourceMap): void {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
const mod = importWorkflows();
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
const workflowFn = mod[info.workflowType];
|
|
140
|
+
const defaultWorfklowFn = mod['default'];
|
|
141
|
+
|
|
142
|
+
if (typeof workflowFn === 'function') {
|
|
143
|
+
activator.workflow = workflowFn;
|
|
144
|
+
} else if (typeof defaultWorfklowFn === 'function') {
|
|
145
|
+
activator.workflow = defaultWorfklowFn;
|
|
146
|
+
} else {
|
|
147
|
+
const details =
|
|
148
|
+
workflowFn === undefined
|
|
149
|
+
? 'no such function is exported by the workflow bundle'
|
|
150
|
+
: `expected a function, but got: '${typeof workflowFn}'`;
|
|
151
|
+
throw new TypeError(`Failed to initialize workflow of type '${info.workflowType}': ${details}`);
|
|
142
152
|
}
|
|
143
|
-
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Objects transfered to the VM from outside have prototypes belonging to the
|
|
157
|
+
* outer context, which means that instanceof won't work inside the VM. This
|
|
158
|
+
* function recursively walks over the content of an object, and recreate some
|
|
159
|
+
* of these objects (notably Array, Date and Objects).
|
|
160
|
+
*/
|
|
161
|
+
function fixPrototypes<X>(obj: X): X {
|
|
162
|
+
if (obj != null && typeof obj === 'object') {
|
|
163
|
+
switch (Object.getPrototypeOf(obj)?.constructor?.name) {
|
|
164
|
+
case 'Array':
|
|
165
|
+
return Array.from((obj as Array<unknown>).map(fixPrototypes)) as X;
|
|
166
|
+
case 'Date':
|
|
167
|
+
return new Date(obj as unknown as Date) as X;
|
|
168
|
+
default:
|
|
169
|
+
return Object.fromEntries(Object.entries(obj).map(([k, v]): [string, any] => [k, fixPrototypes(v)])) as X;
|
|
170
|
+
}
|
|
171
|
+
} else return obj;
|
|
144
172
|
}
|
|
145
173
|
|
|
146
174
|
/**
|
|
@@ -185,7 +213,7 @@ export function activate(activation: coresdk.workflow_activation.WorkflowActivat
|
|
|
185
213
|
return;
|
|
186
214
|
}
|
|
187
215
|
activator[job.variant](variant as any /* TS can't infer this type */);
|
|
188
|
-
if (
|
|
216
|
+
if (shouldUnblockConditions(job)) {
|
|
189
217
|
tryUnblockConditions();
|
|
190
218
|
}
|
|
191
219
|
}
|
|
@@ -244,13 +272,13 @@ export function tryUnblockConditions(): number {
|
|
|
244
272
|
/**
|
|
245
273
|
* Predicate used to prevent triggering conditions for non-query and non-patch jobs.
|
|
246
274
|
*/
|
|
247
|
-
export function
|
|
275
|
+
export function shouldUnblockConditions(job: coresdk.workflow_activation.IWorkflowActivationJob): boolean {
|
|
248
276
|
return !job.queryWorkflow && !job.notifyHasPatch;
|
|
249
277
|
}
|
|
250
278
|
|
|
251
279
|
export function dispose(): void {
|
|
252
280
|
const dispose = composeInterceptors(getActivator().interceptors.internals, 'dispose', async () => {
|
|
253
|
-
|
|
281
|
+
disableStorage();
|
|
254
282
|
});
|
|
255
283
|
dispose({});
|
|
256
284
|
}
|
package/src/workflow.ts
CHANGED
|
@@ -32,10 +32,12 @@ import {
|
|
|
32
32
|
ChildWorkflowOptionsWithDefaults,
|
|
33
33
|
ContinueAsNew,
|
|
34
34
|
ContinueAsNewOptions,
|
|
35
|
+
DefaultSignalHandler,
|
|
35
36
|
EnhancedStackTrace,
|
|
37
|
+
Handler,
|
|
36
38
|
WorkflowInfo,
|
|
37
39
|
} from './interfaces';
|
|
38
|
-
import { LocalActivityDoBackoff, getActivator } from './internals';
|
|
40
|
+
import { LocalActivityDoBackoff, getActivator, maybeGetActivator } from './internals';
|
|
39
41
|
import { Sinks } from './sinks';
|
|
40
42
|
import { untrackPromise } from './stack-helpers';
|
|
41
43
|
import { ChildWorkflowHandle, ExternalWorkflowHandle } from './workflow-handle';
|
|
@@ -692,7 +694,7 @@ export async function startChild<T extends Workflow>(
|
|
|
692
694
|
options?: WithWorkflowArgs<T, ChildWorkflowOptions>
|
|
693
695
|
): Promise<ChildWorkflowHandle<T>> {
|
|
694
696
|
const activator = getActivator();
|
|
695
|
-
const optionsWithDefaults = addDefaultWorkflowOptions(options ?? {});
|
|
697
|
+
const optionsWithDefaults = addDefaultWorkflowOptions(options ?? ({} as any));
|
|
696
698
|
const workflowType = typeof workflowTypeOrFunc === 'string' ? workflowTypeOrFunc : workflowTypeOrFunc.name;
|
|
697
699
|
const execute = composeInterceptors(
|
|
698
700
|
activator.interceptors.outbound,
|
|
@@ -791,7 +793,7 @@ export async function executeChild<T extends Workflow>(
|
|
|
791
793
|
options?: WithWorkflowArgs<T, ChildWorkflowOptions>
|
|
792
794
|
): Promise<WorkflowResultType<T>> {
|
|
793
795
|
const activator = getActivator();
|
|
794
|
-
const optionsWithDefaults = addDefaultWorkflowOptions(options ?? {});
|
|
796
|
+
const optionsWithDefaults = addDefaultWorkflowOptions(options ?? ({} as any));
|
|
795
797
|
const workflowType = typeof workflowTypeOrFunc === 'string' ? workflowTypeOrFunc : workflowTypeOrFunc.name;
|
|
796
798
|
const execute = composeInterceptors(
|
|
797
799
|
activator.interceptors.outbound,
|
|
@@ -843,18 +845,7 @@ export function workflowInfo(): WorkflowInfo {
|
|
|
843
845
|
* Returns whether or not code is executing in workflow context
|
|
844
846
|
*/
|
|
845
847
|
export function inWorkflowContext(): boolean {
|
|
846
|
-
|
|
847
|
-
getActivator();
|
|
848
|
-
return true;
|
|
849
|
-
} catch (err: any) {
|
|
850
|
-
// Use string comparison in case multiple versions of @temporalio/common are
|
|
851
|
-
// installed in which case an instanceof check would fail.
|
|
852
|
-
if (err.name === 'IllegalStateError') {
|
|
853
|
-
return false;
|
|
854
|
-
} else {
|
|
855
|
-
throw err;
|
|
856
|
-
}
|
|
857
|
-
}
|
|
848
|
+
return maybeGetActivator() !== undefined;
|
|
858
849
|
}
|
|
859
850
|
|
|
860
851
|
/**
|
|
@@ -1131,11 +1122,13 @@ function conditionInner(fn: () => boolean): Promise<void> {
|
|
|
1131
1122
|
* Definitions are used to register handler in the Workflow via {@link setHandler} and to signal Workflows using a {@link WorkflowHandle}, {@link ChildWorkflowHandle} or {@link ExternalWorkflowHandle}.
|
|
1132
1123
|
* Definitions can be reused in multiple Workflows.
|
|
1133
1124
|
*/
|
|
1134
|
-
export function defineSignal<Args extends any[] = []
|
|
1125
|
+
export function defineSignal<Args extends any[] = [], Name extends string = string>(
|
|
1126
|
+
name: Name
|
|
1127
|
+
): SignalDefinition<Args, Name> {
|
|
1135
1128
|
return {
|
|
1136
1129
|
type: 'signal',
|
|
1137
1130
|
name,
|
|
1138
|
-
}
|
|
1131
|
+
} as SignalDefinition<Args, Name>;
|
|
1139
1132
|
}
|
|
1140
1133
|
|
|
1141
1134
|
/**
|
|
@@ -1144,26 +1137,15 @@ export function defineSignal<Args extends any[] = []>(name: string): SignalDefin
|
|
|
1144
1137
|
* Definitions are used to register handler in the Workflow via {@link setHandler} and to query Workflows using a {@link WorkflowHandle}.
|
|
1145
1138
|
* Definitions can be reused in multiple Workflows.
|
|
1146
1139
|
*/
|
|
1147
|
-
export function defineQuery<Ret, Args extends any[] = []
|
|
1140
|
+
export function defineQuery<Ret, Args extends any[] = [], Name extends string = string>(
|
|
1141
|
+
name: Name
|
|
1142
|
+
): QueryDefinition<Ret, Args, Name> {
|
|
1148
1143
|
return {
|
|
1149
1144
|
type: 'query',
|
|
1150
1145
|
name,
|
|
1151
|
-
}
|
|
1146
|
+
} as QueryDefinition<Ret, Args, Name>;
|
|
1152
1147
|
}
|
|
1153
1148
|
|
|
1154
|
-
/**
|
|
1155
|
-
* A handler function capable of accepting the arguments for a given SignalDefinition or QueryDefinition.
|
|
1156
|
-
*/
|
|
1157
|
-
export type Handler<
|
|
1158
|
-
Ret,
|
|
1159
|
-
Args extends any[],
|
|
1160
|
-
T extends SignalDefinition<Args> | QueryDefinition<Ret, Args>
|
|
1161
|
-
> = T extends SignalDefinition<infer A>
|
|
1162
|
-
? (...args: A) => void | Promise<void>
|
|
1163
|
-
: T extends QueryDefinition<infer R, infer A>
|
|
1164
|
-
? (...args: A) => R
|
|
1165
|
-
: never;
|
|
1166
|
-
|
|
1167
1149
|
/**
|
|
1168
1150
|
* Set a handler function for a Workflow query or signal.
|
|
1169
1151
|
*
|
|
@@ -1178,21 +1160,48 @@ export function setHandler<Ret, Args extends any[], T extends SignalDefinition<A
|
|
|
1178
1160
|
): void {
|
|
1179
1161
|
const activator = getActivator();
|
|
1180
1162
|
if (def.type === 'signal') {
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
}
|
|
1163
|
+
if (typeof handler === 'function') {
|
|
1164
|
+
activator.signalHandlers.set(def.name, handler as any);
|
|
1165
|
+
activator.dispatchBufferedSignals();
|
|
1166
|
+
} else if (handler == null) {
|
|
1167
|
+
activator.signalHandlers.delete(def.name);
|
|
1168
|
+
} else {
|
|
1169
|
+
throw new TypeError(`Expected handler to be either a function or 'undefined'. Got: '${typeof handler}'`);
|
|
1188
1170
|
}
|
|
1189
1171
|
} else if (def.type === 'query') {
|
|
1190
|
-
|
|
1172
|
+
if (typeof handler === 'function') {
|
|
1173
|
+
activator.queryHandlers.set(def.name, handler as any);
|
|
1174
|
+
} else if (handler == null) {
|
|
1175
|
+
activator.queryHandlers.delete(def.name);
|
|
1176
|
+
} else {
|
|
1177
|
+
throw new TypeError(`Expected handler to be either a function or 'undefined'. Got: '${typeof handler}'`);
|
|
1178
|
+
}
|
|
1191
1179
|
} else {
|
|
1192
1180
|
throw new TypeError(`Invalid definition type: ${(def as any).type}`);
|
|
1193
1181
|
}
|
|
1194
1182
|
}
|
|
1195
1183
|
|
|
1184
|
+
/**
|
|
1185
|
+
* Set a signal handler function that will handle signals calls for non-registered signal names.
|
|
1186
|
+
*
|
|
1187
|
+
* Signals are dispatched to the default signal handler in the order that they were accepted by the server.
|
|
1188
|
+
*
|
|
1189
|
+
* If this function is called multiple times for a given signal or query name the last handler will overwrite any previous calls.
|
|
1190
|
+
*
|
|
1191
|
+
* @param handler a function that will handle signals for non-registered signal names, or `undefined` to unset the handler.
|
|
1192
|
+
*/
|
|
1193
|
+
export function setDefaultSignalHandler(handler: DefaultSignalHandler | undefined): void {
|
|
1194
|
+
const activator = getActivator();
|
|
1195
|
+
if (typeof handler === 'function') {
|
|
1196
|
+
activator.defaultSignalHandler = handler;
|
|
1197
|
+
activator.dispatchBufferedSignals();
|
|
1198
|
+
} else if (handler == null) {
|
|
1199
|
+
activator.defaultSignalHandler = undefined;
|
|
1200
|
+
} else {
|
|
1201
|
+
throw new TypeError(`Expected handler to be either a function or 'undefined'. Got: '${typeof handler}'`);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1196
1205
|
/**
|
|
1197
1206
|
* Updates this Workflow's Search Attributes by merging the provided `searchAttributes` with the existing Search
|
|
1198
1207
|
* Attributes, `workflowInfo().searchAttributes`.
|