@temporalio/workflow 1.17.2 → 1.17.3
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/package.json +3 -3
- package/src/current-random.ts +5 -0
- package/src/flags.ts +1 -1
- package/src/global-overrides.ts +3 -2
- package/src/index.ts +4 -2
- package/src/interceptor-composition.ts +31 -0
- package/src/interfaces.ts +5 -4
- package/src/internals.ts +213 -55
- package/src/logs.ts +1 -1
- package/src/metrics.ts +1 -1
- package/src/nexus.ts +8 -3
- package/src/random-helpers.ts +21 -0
- package/src/random-stream-seed.ts +18 -0
- package/src/random-streams.ts +132 -0
- package/src/worker-interface.ts +3 -1
- package/src/workflow.ts +99 -54
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { assertInWorkflowContext } from './global-attributes';
|
|
2
|
+
import type { Activator } from './internals';
|
|
3
|
+
import { fillWithRandom, uuid4FromRandom } from './random-helpers';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A deterministic PRNG stream scoped to the current Workflow execution.
|
|
7
|
+
*
|
|
8
|
+
* Workflow code already gets a deterministic default stream through `Math.random()`.
|
|
9
|
+
* This interface exposes additional named streams that are derived from the workflow
|
|
10
|
+
* seed without consuming that default stream. Repeated calls to
|
|
11
|
+
* {@link getRandomStream} with the same name refer to the same logical stream state
|
|
12
|
+
* for the current workflow execution, and that state is preserved across activations.
|
|
13
|
+
*
|
|
14
|
+
* The primary use case is workflow plugins and interceptors that need private,
|
|
15
|
+
* replay-stable entropy without perturbing user workflow randomness.
|
|
16
|
+
*
|
|
17
|
+
* @experimental This API may be removed or changed in the future.
|
|
18
|
+
*/
|
|
19
|
+
export interface WorkflowRandomStream {
|
|
20
|
+
/**
|
|
21
|
+
* Draw the next deterministic pseudo-random number from this stream.
|
|
22
|
+
*
|
|
23
|
+
* This is equivalent to `Math.random()`, but isolated from the workflow's main
|
|
24
|
+
* random stream and from other named streams.
|
|
25
|
+
*/
|
|
26
|
+
random(): number;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Generate a deterministic UUIDv4 backed by this stream.
|
|
30
|
+
*/
|
|
31
|
+
uuid4(): string;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Fill a byte array deterministically from this stream.
|
|
35
|
+
*/
|
|
36
|
+
fill(bytes: Uint8Array): Uint8Array;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Run `fn` with scoped workflow-random helpers such as `Math.random()` and `uuid4()`
|
|
40
|
+
* routed through this stream.
|
|
41
|
+
*
|
|
42
|
+
* This is the scoped override API for workflow random streams. It is intended for
|
|
43
|
+
* bounded plugin/interceptor code that needs existing calls to `Math.random()` or
|
|
44
|
+
* `uuid4()` to use this stream without perturbing the workflow's default random
|
|
45
|
+
* sequence or any other named stream.
|
|
46
|
+
*
|
|
47
|
+
* The override follows async continuations started by `fn`. Workflow interceptor
|
|
48
|
+
* `next(...)` continuations restore the downstream workflow random scope before
|
|
49
|
+
* entering the rest of the interceptor chain or workflow code, so a temporary
|
|
50
|
+
* plugin scope does not leak downstream unless that downstream code explicitly
|
|
51
|
+
* establishes its own scope.
|
|
52
|
+
*
|
|
53
|
+
* Prefer explicit `stream.random()` / `stream.uuid4()` calls when that is practical.
|
|
54
|
+
* Use `stream.with(...)` when a temporary scoped override is the better fit, or
|
|
55
|
+
* when you want to keep the stream instance in module scope and reuse it directly.
|
|
56
|
+
*/
|
|
57
|
+
with<T>(fn: () => T): T;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
class ActivatorRandomStream implements WorkflowRandomStream {
|
|
61
|
+
constructor(
|
|
62
|
+
protected readonly activator: Activator,
|
|
63
|
+
protected readonly name: string
|
|
64
|
+
) {}
|
|
65
|
+
|
|
66
|
+
random(): number {
|
|
67
|
+
return this.activator.getNamedRandom(this.name)();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
uuid4(): string {
|
|
71
|
+
return uuid4FromRandom(() => this.random());
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
fill(bytes: Uint8Array): Uint8Array {
|
|
75
|
+
return fillWithRandom(() => this.random(), bytes);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
with<T>(fn: () => T): T {
|
|
79
|
+
return this.activator.withCurrentRandom(this, fn);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
class DefaultWorkflowRandomStream implements WorkflowRandomStream {
|
|
84
|
+
random(): number {
|
|
85
|
+
return assertInWorkflowContext('Workflow.workflowRandom may only be used from workflow context.').random();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
uuid4(): string {
|
|
89
|
+
return uuid4FromRandom(() => this.random());
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
fill(bytes: Uint8Array): Uint8Array {
|
|
93
|
+
return fillWithRandom(() => this.random(), bytes);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
with<T>(fn: () => T): T {
|
|
97
|
+
const activator = assertInWorkflowContext('Workflow.workflowRandom may only be used from workflow context.');
|
|
98
|
+
return activator.withCurrentRandom(this, fn);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The default deterministic random stream for the current workflow execution.
|
|
104
|
+
*
|
|
105
|
+
* This exposes the same underlying sequence used by workflow-level `Math.random()`
|
|
106
|
+
* when no named override is active. It can be useful for plugin/interceptor code
|
|
107
|
+
* that wants an explicit handle to the main workflow random stream, including from
|
|
108
|
+
* inside a temporary named scope established by another `WorkflowRandomStream`.
|
|
109
|
+
*
|
|
110
|
+
* @experimental This API may be removed or changed in the future.
|
|
111
|
+
*/
|
|
112
|
+
export const workflowRandom: WorkflowRandomStream = new DefaultWorkflowRandomStream();
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get a named deterministic random stream for the current workflow execution.
|
|
116
|
+
*
|
|
117
|
+
* Named streams are derived from the workflow seed and a stable stream name,
|
|
118
|
+
* without consuming the workflow's default `Math.random()` stream. Repeated
|
|
119
|
+
* calls with the same `name` within a workflow execution refer to the same
|
|
120
|
+
* logical stream state, including across activations.
|
|
121
|
+
*
|
|
122
|
+
* This is the preferred entry point for workflow plugins and interceptors that
|
|
123
|
+
* need their own deterministic entropy. Use stable package- or module-style
|
|
124
|
+
* names so the stream identity remains replay-safe, then keep the returned
|
|
125
|
+
* `WorkflowRandomStream` around and call its methods directly.
|
|
126
|
+
*
|
|
127
|
+
* @experimental This API may be removed or changed in the future.
|
|
128
|
+
*/
|
|
129
|
+
export function getRandomStream(name: string): WorkflowRandomStream {
|
|
130
|
+
const activator = assertInWorkflowContext('Workflow.getRandomStream(...) may only be used from workflow context.');
|
|
131
|
+
return new ActivatorRandomStream(activator, name);
|
|
132
|
+
}
|
package/src/worker-interface.ts
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { WorkflowFunctionWithOptions } from '@temporalio/common';
|
|
7
7
|
import { encodeVersioningBehavior, IllegalStateError } from '@temporalio/common';
|
|
8
|
-
import { composeInterceptors } from '@temporalio/common/lib/interceptors';
|
|
9
8
|
import type { coresdk } from '@temporalio/proto';
|
|
10
9
|
import type { WorkflowInterceptorsFactory } from './interceptors';
|
|
11
10
|
import type { WorkflowCreateOptionsInternal } from './interfaces';
|
|
12
11
|
import { Activator } from './internals';
|
|
12
|
+
import { composeInterceptors } from './interceptor-composition';
|
|
13
13
|
import { setActivator, getActivator, maybeGetActivator } from './global-attributes';
|
|
14
14
|
|
|
15
15
|
// Export the type for use on the "worker" side
|
|
@@ -91,6 +91,8 @@ export function initRuntime(options: WorkflowCreateOptionsInternal): void {
|
|
|
91
91
|
if (isWorkflowFunctionWithOptions(activator.workflow)) {
|
|
92
92
|
if (typeof activator.workflow.workflowDefinitionOptions === 'object') {
|
|
93
93
|
activator.versioningBehavior = activator.workflow.workflowDefinitionOptions.versioningBehavior;
|
|
94
|
+
activator.workflowDefinitionFailureExceptionTypes =
|
|
95
|
+
activator.workflow.workflowDefinitionOptions.failureExceptionTypes;
|
|
94
96
|
} else {
|
|
95
97
|
activator.workflowDefinitionOptionsGetter = activator.workflow.workflowDefinitionOptions;
|
|
96
98
|
}
|
package/src/workflow.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ActivityFunction,
|
|
3
|
+
ActivitySerializationContext,
|
|
3
4
|
ActivityOptions,
|
|
4
5
|
LocalActivityOptions,
|
|
5
6
|
QueryDefinition,
|
|
@@ -10,6 +11,7 @@ import type {
|
|
|
10
11
|
UpdateDefinition,
|
|
11
12
|
WithWorkflowArgs,
|
|
12
13
|
Workflow,
|
|
14
|
+
WorkflowSerializationContext,
|
|
13
15
|
WorkflowResultType,
|
|
14
16
|
WorkflowReturnType,
|
|
15
17
|
WorkflowUpdateValidatorType,
|
|
@@ -24,9 +26,9 @@ import {
|
|
|
24
26
|
extractWorkflowType,
|
|
25
27
|
HandlerUnfinishedPolicy,
|
|
26
28
|
mapToPayloads,
|
|
27
|
-
toPayloads,
|
|
28
|
-
TypedSearchAttributes,
|
|
29
29
|
encodeInitialVersioningBehavior,
|
|
30
|
+
toPayloadsWithContext,
|
|
31
|
+
TypedSearchAttributes,
|
|
30
32
|
} from '@temporalio/common';
|
|
31
33
|
import { userMetadataToPayload } from '@temporalio/common/lib/user-metadata';
|
|
32
34
|
import {
|
|
@@ -36,11 +38,11 @@ import {
|
|
|
36
38
|
import { versioningIntentToProto } from '@temporalio/common/lib/versioning-intent-enum';
|
|
37
39
|
import type { Duration } from '@temporalio/common/lib/time';
|
|
38
40
|
import { msOptionalToTs, msToNumber, msToTs, requiredTsToMs } from '@temporalio/common/lib/time';
|
|
39
|
-
import { composeInterceptors } from '@temporalio/common/lib/interceptors';
|
|
40
41
|
import type { temporal } from '@temporalio/proto';
|
|
41
42
|
import { deepMerge } from '@temporalio/common/lib/internal-workflow';
|
|
42
43
|
import { throwIfReservedName } from '@temporalio/common/lib/reserved';
|
|
43
44
|
import { CancellationScope, registerSleepImplementation } from './cancellation-scope';
|
|
45
|
+
import { composeInterceptors } from './interceptor-composition';
|
|
44
46
|
import { UpdateScope } from './update-scope';
|
|
45
47
|
import type {
|
|
46
48
|
ActivityInput,
|
|
@@ -73,12 +75,43 @@ import {
|
|
|
73
75
|
} from './interfaces';
|
|
74
76
|
import { LocalActivityDoBackoff } from './errors';
|
|
75
77
|
import { assertInWorkflowContext, getActivator, maybeGetActivator } from './global-attributes';
|
|
78
|
+
import { uuid4FromRandom } from './random-helpers';
|
|
76
79
|
import { untrackPromise } from './stack-helpers';
|
|
77
80
|
import type { ChildWorkflowHandle, ExternalWorkflowHandle } from './workflow-handle';
|
|
78
81
|
|
|
79
82
|
// Avoid a circular dependency
|
|
80
83
|
registerSleepImplementation(sleep);
|
|
81
84
|
|
|
85
|
+
function currentWorkflowSerializationContext(info: WorkflowInfo): WorkflowSerializationContext {
|
|
86
|
+
return {
|
|
87
|
+
type: 'workflow',
|
|
88
|
+
namespace: info.namespace,
|
|
89
|
+
workflowId: info.workflowId,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function targetWorkflowSerializationContext(info: WorkflowInfo, workflowId: string): WorkflowSerializationContext {
|
|
94
|
+
return {
|
|
95
|
+
type: 'workflow',
|
|
96
|
+
namespace: info.namespace,
|
|
97
|
+
workflowId,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function activitySerializationContext(
|
|
102
|
+
info: WorkflowInfo,
|
|
103
|
+
activityId: string | undefined,
|
|
104
|
+
isLocal: boolean
|
|
105
|
+
): ActivitySerializationContext {
|
|
106
|
+
return {
|
|
107
|
+
type: 'activity',
|
|
108
|
+
namespace: info.namespace,
|
|
109
|
+
activityId,
|
|
110
|
+
workflowId: info.workflowId,
|
|
111
|
+
isLocal,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
82
115
|
/**
|
|
83
116
|
* Adds default values of `workflowId` and `cancellationType` to given workflow options.
|
|
84
117
|
*/
|
|
@@ -99,6 +132,7 @@ export function addDefaultWorkflowOptions<T extends Workflow>(
|
|
|
99
132
|
*/
|
|
100
133
|
function timerNextHandler({ seq, durationMs, options }: TimerInput) {
|
|
101
134
|
const activator = getActivator();
|
|
135
|
+
const context = currentWorkflowSerializationContext(activator.info);
|
|
102
136
|
return new Promise<void>((resolve, reject) => {
|
|
103
137
|
const scope = CancellationScope.current();
|
|
104
138
|
if (scope.consideredCancelled) {
|
|
@@ -125,7 +159,7 @@ function timerNextHandler({ seq, durationMs, options }: TimerInput) {
|
|
|
125
159
|
seq,
|
|
126
160
|
startToFireTimeout: msToTs(durationMs),
|
|
127
161
|
},
|
|
128
|
-
userMetadata: userMetadataToPayload(activator.payloadConverter, options?.summary, undefined),
|
|
162
|
+
userMetadata: userMetadataToPayload(activator.payloadConverter, options?.summary, undefined, context),
|
|
129
163
|
});
|
|
130
164
|
activator.completions.timer.set(seq, {
|
|
131
165
|
resolve,
|
|
@@ -173,6 +207,8 @@ const validateLocalActivityOptions = validateActivityOptions;
|
|
|
173
207
|
function scheduleActivityNextHandler({ options, args, headers, seq, activityType }: ActivityInput): Promise<unknown> {
|
|
174
208
|
const activator = getActivator();
|
|
175
209
|
validateActivityOptions(options);
|
|
210
|
+
const activityId = options.activityId ?? `${seq}`;
|
|
211
|
+
const context = activitySerializationContext(activator.info, activityId, false);
|
|
176
212
|
return new Promise((resolve, reject) => {
|
|
177
213
|
const scope = CancellationScope.current();
|
|
178
214
|
if (scope.consideredCancelled) {
|
|
@@ -196,9 +232,9 @@ function scheduleActivityNextHandler({ options, args, headers, seq, activityType
|
|
|
196
232
|
activator.pushCommand({
|
|
197
233
|
scheduleActivity: {
|
|
198
234
|
seq,
|
|
199
|
-
activityId
|
|
235
|
+
activityId,
|
|
200
236
|
activityType,
|
|
201
|
-
arguments:
|
|
237
|
+
arguments: toPayloadsWithContext(activator.payloadConverter, context, args),
|
|
202
238
|
retryPolicy: options.retry ? compileRetryPolicy(options.retry) : undefined,
|
|
203
239
|
taskQueue: options.taskQueue || activator.info.taskQueue,
|
|
204
240
|
heartbeatTimeout: msOptionalToTs(options.heartbeatTimeout),
|
|
@@ -208,14 +244,15 @@ function scheduleActivityNextHandler({ options, args, headers, seq, activityType
|
|
|
208
244
|
headers,
|
|
209
245
|
cancellationType: encodeActivityCancellationType(options.cancellationType),
|
|
210
246
|
doNotEagerlyExecute: !(options.allowEagerDispatch ?? true),
|
|
211
|
-
versioningIntent: versioningIntentToProto(options.versioningIntent),
|
|
247
|
+
versioningIntent: versioningIntentToProto(options.versioningIntent),
|
|
212
248
|
priority: options.priority ? compilePriority(options.priority) : undefined,
|
|
213
249
|
},
|
|
214
|
-
userMetadata: userMetadataToPayload(activator.payloadConverter, options.summary, undefined),
|
|
250
|
+
userMetadata: userMetadataToPayload(activator.payloadConverter, options.summary, undefined, context),
|
|
215
251
|
});
|
|
216
252
|
activator.completions.activity.set(seq, {
|
|
217
253
|
resolve,
|
|
218
254
|
reject,
|
|
255
|
+
context,
|
|
219
256
|
});
|
|
220
257
|
});
|
|
221
258
|
}
|
|
@@ -233,6 +270,8 @@ async function scheduleLocalActivityNextHandler({
|
|
|
233
270
|
originalScheduleTime,
|
|
234
271
|
}: LocalActivityInput): Promise<unknown> {
|
|
235
272
|
const activator = getActivator();
|
|
273
|
+
const activityId = `${seq}`;
|
|
274
|
+
const context = activitySerializationContext(activator.info, activityId, true);
|
|
236
275
|
// Eagerly fail the local activity (which will in turn fail the workflow task.
|
|
237
276
|
// Do not fail on replay where the local activities may not be registered on the replay worker.
|
|
238
277
|
if (!activator.info.unsafe.isReplaying && !activator.registeredActivityNames.has(activityType)) {
|
|
@@ -265,10 +304,9 @@ async function scheduleLocalActivityNextHandler({
|
|
|
265
304
|
seq,
|
|
266
305
|
attempt,
|
|
267
306
|
originalScheduleTime,
|
|
268
|
-
|
|
269
|
-
activityId: `${seq}`,
|
|
307
|
+
activityId,
|
|
270
308
|
activityType,
|
|
271
|
-
arguments:
|
|
309
|
+
arguments: toPayloadsWithContext(activator.payloadConverter, context, args),
|
|
272
310
|
retryPolicy: options.retry ? compileRetryPolicy(options.retry) : undefined,
|
|
273
311
|
scheduleToCloseTimeout: msOptionalToTs(options.scheduleToCloseTimeout),
|
|
274
312
|
startToCloseTimeout: msOptionalToTs(options.startToCloseTimeout),
|
|
@@ -277,11 +315,12 @@ async function scheduleLocalActivityNextHandler({
|
|
|
277
315
|
headers,
|
|
278
316
|
cancellationType: encodeActivityCancellationType(options.cancellationType),
|
|
279
317
|
},
|
|
280
|
-
userMetadata: userMetadataToPayload(activator.payloadConverter, options.summary, undefined),
|
|
318
|
+
userMetadata: userMetadataToPayload(activator.payloadConverter, options.summary, undefined, context),
|
|
281
319
|
});
|
|
282
320
|
activator.completions.activity.set(seq, {
|
|
283
321
|
resolve,
|
|
284
322
|
reject,
|
|
323
|
+
context,
|
|
285
324
|
});
|
|
286
325
|
});
|
|
287
326
|
}
|
|
@@ -369,6 +408,7 @@ function startChildWorkflowExecutionNextHandler({
|
|
|
369
408
|
}: StartChildWorkflowExecutionInput): Promise<[Promise<string>, Promise<unknown>]> {
|
|
370
409
|
const activator = getActivator();
|
|
371
410
|
const workflowId = options.workflowId ?? uuid4();
|
|
411
|
+
const context = targetWorkflowSerializationContext(activator.info, workflowId);
|
|
372
412
|
const startPromise = new Promise<string>((resolve, reject) => {
|
|
373
413
|
const scope = CancellationScope.current();
|
|
374
414
|
if (scope.consideredCancelled) {
|
|
@@ -394,7 +434,7 @@ function startChildWorkflowExecutionNextHandler({
|
|
|
394
434
|
seq,
|
|
395
435
|
workflowId,
|
|
396
436
|
workflowType,
|
|
397
|
-
input:
|
|
437
|
+
input: toPayloadsWithContext(activator.payloadConverter, context, options.args),
|
|
398
438
|
retryPolicy: options.retry ? compileRetryPolicy(options.retry) : undefined,
|
|
399
439
|
taskQueue: options.taskQueue || activator.info.taskQueue,
|
|
400
440
|
workflowExecutionTimeout: msOptionalToTs(options.workflowExecutionTimeout),
|
|
@@ -407,18 +447,24 @@ function startChildWorkflowExecutionNextHandler({
|
|
|
407
447
|
parentClosePolicy: encodeParentClosePolicy(options.parentClosePolicy),
|
|
408
448
|
cronSchedule: options.cronSchedule,
|
|
409
449
|
searchAttributes:
|
|
410
|
-
options.searchAttributes || options.typedSearchAttributes
|
|
411
|
-
? { indexedFields: encodeUnifiedSearchAttributes(options.searchAttributes, options.typedSearchAttributes) }
|
|
450
|
+
options.searchAttributes || options.typedSearchAttributes
|
|
451
|
+
? { indexedFields: encodeUnifiedSearchAttributes(options.searchAttributes, options.typedSearchAttributes) }
|
|
412
452
|
: undefined,
|
|
413
|
-
memo: options.memo && mapToPayloads(activator.payloadConverter, options.memo),
|
|
414
|
-
versioningIntent: versioningIntentToProto(options.versioningIntent),
|
|
453
|
+
memo: options.memo && mapToPayloads(activator.payloadConverter, options.memo, context),
|
|
454
|
+
versioningIntent: versioningIntentToProto(options.versioningIntent),
|
|
415
455
|
priority: options.priority ? compilePriority(options.priority) : undefined,
|
|
416
456
|
},
|
|
417
|
-
userMetadata: userMetadataToPayload(
|
|
457
|
+
userMetadata: userMetadataToPayload(
|
|
458
|
+
activator.payloadConverter,
|
|
459
|
+
options?.staticSummary,
|
|
460
|
+
options?.staticDetails,
|
|
461
|
+
context
|
|
462
|
+
),
|
|
418
463
|
});
|
|
419
464
|
activator.completions.childWorkflowStart.set(seq, {
|
|
420
465
|
resolve,
|
|
421
466
|
reject,
|
|
467
|
+
context,
|
|
422
468
|
});
|
|
423
469
|
});
|
|
424
470
|
|
|
@@ -430,6 +476,7 @@ function startChildWorkflowExecutionNextHandler({
|
|
|
430
476
|
activator.completions.childWorkflowComplete.set(seq, {
|
|
431
477
|
resolve,
|
|
432
478
|
reject,
|
|
479
|
+
context,
|
|
433
480
|
});
|
|
434
481
|
});
|
|
435
482
|
untrackPromise(startPromise);
|
|
@@ -443,6 +490,8 @@ function startChildWorkflowExecutionNextHandler({
|
|
|
443
490
|
|
|
444
491
|
function signalWorkflowNextHandler({ seq, signalName, args, target, headers }: SignalWorkflowInput) {
|
|
445
492
|
const activator = getActivator();
|
|
493
|
+
const targetWorkflowId = target.type === 'external' ? target.workflowExecution.workflowId : target.childWorkflowId;
|
|
494
|
+
const context = targetWorkflowSerializationContext(activator.info, targetWorkflowId!);
|
|
446
495
|
return new Promise<any>((resolve, reject) => {
|
|
447
496
|
const scope = CancellationScope.current();
|
|
448
497
|
if (scope.consideredCancelled) {
|
|
@@ -463,7 +512,7 @@ function signalWorkflowNextHandler({ seq, signalName, args, target, headers }: S
|
|
|
463
512
|
activator.pushCommand({
|
|
464
513
|
signalExternalWorkflowExecution: {
|
|
465
514
|
seq,
|
|
466
|
-
args:
|
|
515
|
+
args: toPayloadsWithContext(activator.payloadConverter, context, args),
|
|
467
516
|
headers,
|
|
468
517
|
signalName,
|
|
469
518
|
...(target.type === 'external'
|
|
@@ -479,7 +528,7 @@ function signalWorkflowNextHandler({ seq, signalName, args, target, headers }: S
|
|
|
479
528
|
},
|
|
480
529
|
});
|
|
481
530
|
|
|
482
|
-
activator.completions.signalWorkflow.set(seq, { resolve, reject });
|
|
531
|
+
activator.completions.signalWorkflow.set(seq, { resolve, reject, context });
|
|
483
532
|
});
|
|
484
533
|
}
|
|
485
534
|
|
|
@@ -526,7 +575,7 @@ export type ActivityFunctionWithOptions<T extends ActivityFunction> = T & {
|
|
|
526
575
|
* provided options.
|
|
527
576
|
*
|
|
528
577
|
* @param options ActivityOptions
|
|
529
|
-
* @param args
|
|
578
|
+
* @param args list of arguments
|
|
530
579
|
* @returns return value of the activity
|
|
531
580
|
*
|
|
532
581
|
* @experimental executeWithOptions is a new method to provide call-site options and is subject to change
|
|
@@ -547,7 +596,7 @@ export type LocalActivityFunctionWithOptions<T extends ActivityFunction> = T & {
|
|
|
547
596
|
* provided options.
|
|
548
597
|
*
|
|
549
598
|
* @param options LocalActivityOptions
|
|
550
|
-
* @param args
|
|
599
|
+
* @param args list of arguments
|
|
551
600
|
* @returns return value of the activity
|
|
552
601
|
*
|
|
553
602
|
* @experimental executeWithOptions is a new method to provide call-site options and is subject to change
|
|
@@ -735,7 +784,11 @@ export function getExternalWorkflowHandle(workflowId: string, runId?: string): E
|
|
|
735
784
|
},
|
|
736
785
|
},
|
|
737
786
|
});
|
|
738
|
-
activator.completions.cancelWorkflow.set(seq, {
|
|
787
|
+
activator.completions.cancelWorkflow.set(seq, {
|
|
788
|
+
resolve,
|
|
789
|
+
reject,
|
|
790
|
+
context: targetWorkflowSerializationContext(activator.info, workflowId),
|
|
791
|
+
});
|
|
739
792
|
});
|
|
740
793
|
},
|
|
741
794
|
signal<Args extends any[]>(def: SignalDefinition<Args> | string, ...args: Args): Promise<void> {
|
|
@@ -1015,21 +1068,22 @@ export function makeContinueAsNewFunc<F extends Workflow>(
|
|
|
1015
1068
|
};
|
|
1016
1069
|
|
|
1017
1070
|
return (...args: Parameters<F>): Promise<never> => {
|
|
1071
|
+
const context = currentWorkflowSerializationContext(info);
|
|
1018
1072
|
const fn = composeInterceptors(activator.interceptors.outbound, 'continueAsNew', async (input) => {
|
|
1019
1073
|
const { headers, args, options } = input;
|
|
1020
1074
|
throw new ContinueAsNew({
|
|
1021
1075
|
workflowType: options.workflowType,
|
|
1022
|
-
arguments:
|
|
1076
|
+
arguments: toPayloadsWithContext(activator.payloadConverter, context, args),
|
|
1023
1077
|
headers,
|
|
1024
1078
|
taskQueue: options.taskQueue,
|
|
1025
|
-
memo: options.memo && mapToPayloads(activator.payloadConverter, options.memo),
|
|
1079
|
+
memo: options.memo && mapToPayloads(activator.payloadConverter, options.memo, context),
|
|
1026
1080
|
searchAttributes:
|
|
1027
|
-
options.searchAttributes || options.typedSearchAttributes
|
|
1028
|
-
? { indexedFields: encodeUnifiedSearchAttributes(options.searchAttributes, options.typedSearchAttributes) }
|
|
1081
|
+
options.searchAttributes || options.typedSearchAttributes
|
|
1082
|
+
? { indexedFields: encodeUnifiedSearchAttributes(options.searchAttributes, options.typedSearchAttributes) }
|
|
1029
1083
|
: undefined,
|
|
1030
1084
|
workflowRunTimeout: msOptionalToTs(options.workflowRunTimeout),
|
|
1031
1085
|
workflowTaskTimeout: msOptionalToTs(options.workflowTaskTimeout),
|
|
1032
|
-
versioningIntent: versioningIntentToProto(options.versioningIntent),
|
|
1086
|
+
versioningIntent: versioningIntentToProto(options.versioningIntent),
|
|
1033
1087
|
initialVersioningBehavior: encodeInitialVersioningBehavior(options.initialVersioningBehavior),
|
|
1034
1088
|
});
|
|
1035
1089
|
});
|
|
@@ -1042,7 +1096,7 @@ export function makeContinueAsNewFunc<F extends Workflow>(
|
|
|
1042
1096
|
}
|
|
1043
1097
|
|
|
1044
1098
|
/**
|
|
1045
|
-
* {@link https://docs.temporal.io/
|
|
1099
|
+
* {@link https://docs.temporal.io/workflow-execution/continue-as-new#continue-as-new | Continues-As-New} the current Workflow Execution
|
|
1046
1100
|
* with default options.
|
|
1047
1101
|
*
|
|
1048
1102
|
* Shorthand for `makeContinueAsNewFunc<F>()(...args)`. (See: {@link makeContinueAsNewFunc}.)
|
|
@@ -1070,24 +1124,8 @@ export function continueAsNew<F extends Workflow>(...args: Parameters<F>): Promi
|
|
|
1070
1124
|
* See the {@link https://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid | stackoverflow discussion}.
|
|
1071
1125
|
*/
|
|
1072
1126
|
export function uuid4(): string {
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
// Create a view backed by a 16-byte buffer
|
|
1076
|
-
const view = new DataView(new ArrayBuffer(16));
|
|
1077
|
-
// Fill buffer with random values
|
|
1078
|
-
view.setUint32(0, (Math.random() * 0x100000000) >>> 0);
|
|
1079
|
-
view.setUint32(4, (Math.random() * 0x100000000) >>> 0);
|
|
1080
|
-
view.setUint32(8, (Math.random() * 0x100000000) >>> 0);
|
|
1081
|
-
view.setUint32(12, (Math.random() * 0x100000000) >>> 0);
|
|
1082
|
-
// Patch the 6th byte to reflect a version 4 UUID
|
|
1083
|
-
view.setUint8(6, (view.getUint8(6) & 0xf) | 0x40);
|
|
1084
|
-
// Patch the 8th byte to reflect a variant 1 UUID (version 4 UUIDs are)
|
|
1085
|
-
view.setUint8(8, (view.getUint8(8) & 0x3f) | 0x80);
|
|
1086
|
-
// Compile the canonical textual form from the array data
|
|
1087
|
-
return `${ho(view.getUint32(0), 8)}-${ho(view.getUint16(4), 4)}-${ho(view.getUint16(6), 4)}-${ho(
|
|
1088
|
-
view.getUint16(8),
|
|
1089
|
-
4
|
|
1090
|
-
)}-${ho(view.getUint32(10), 8)}${ho(view.getUint16(14), 4)}`;
|
|
1127
|
+
const activator = maybeGetActivator();
|
|
1128
|
+
return uuid4FromRandom(activator ? () => activator.currentRandom() : Math.random);
|
|
1091
1129
|
}
|
|
1092
1130
|
|
|
1093
1131
|
/**
|
|
@@ -1516,7 +1554,6 @@ export function setDefaultQueryHandler(handler: DefaultQueryHandler | undefined)
|
|
|
1516
1554
|
* If using SearchAttributeUpdatePair[] (preferred), set a value to null to remove the search attribute.
|
|
1517
1555
|
* If using SearchAttributes (deprecated), set a value to undefined or an empty list to remove the search attribute.
|
|
1518
1556
|
*/
|
|
1519
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1520
1557
|
export function upsertSearchAttributes(searchAttributes: SearchAttributes | SearchAttributeUpdatePair[]): void {
|
|
1521
1558
|
const activator = assertInWorkflowContext(
|
|
1522
1559
|
'Workflow.upsertSearchAttributes(...) may only be used from a Workflow Execution.'
|
|
@@ -1538,7 +1575,7 @@ export function upsertSearchAttributes(searchAttributes: SearchAttributes | Sear
|
|
|
1538
1575
|
|
|
1539
1576
|
activator.mutateWorkflowInfo((info: WorkflowInfo): WorkflowInfo => {
|
|
1540
1577
|
// Create a copy of the current state.
|
|
1541
|
-
const newSearchAttributes: SearchAttributes = { ...info.searchAttributes };
|
|
1578
|
+
const newSearchAttributes: SearchAttributes = { ...info.searchAttributes };
|
|
1542
1579
|
for (const pair of searchAttributes) {
|
|
1543
1580
|
if (pair.value == null) {
|
|
1544
1581
|
// If the value is null, remove the search attribute.
|
|
@@ -1547,7 +1584,7 @@ export function upsertSearchAttributes(searchAttributes: SearchAttributes | Sear
|
|
|
1547
1584
|
} else {
|
|
1548
1585
|
newSearchAttributes[pair.key.name] = Array.isArray(pair.value)
|
|
1549
1586
|
? pair.value
|
|
1550
|
-
: ([pair.value] as SearchAttributeValue);
|
|
1587
|
+
: ([pair.value] as SearchAttributeValue);
|
|
1551
1588
|
}
|
|
1552
1589
|
}
|
|
1553
1590
|
return {
|
|
@@ -1570,7 +1607,7 @@ export function upsertSearchAttributes(searchAttributes: SearchAttributes | Sear
|
|
|
1570
1607
|
activator.mutateWorkflowInfo((info: WorkflowInfo): WorkflowInfo => {
|
|
1571
1608
|
// Create a new copy of the current state.
|
|
1572
1609
|
let typedSearchAttributes = info.typedSearchAttributes.updateCopy([]);
|
|
1573
|
-
const newSearchAttributes: SearchAttributes = { ...info.searchAttributes };
|
|
1610
|
+
const newSearchAttributes: SearchAttributes = { ...info.searchAttributes };
|
|
1574
1611
|
|
|
1575
1612
|
// Upsert legacy search attributes into typedSearchAttributes.
|
|
1576
1613
|
for (const [k, v] of Object.entries(searchAttributes)) {
|
|
@@ -1675,6 +1712,7 @@ export function upsertSearchAttributes(searchAttributes: SearchAttributes | Sear
|
|
|
1675
1712
|
*/
|
|
1676
1713
|
export function upsertMemo(memo: Record<string, unknown>): void {
|
|
1677
1714
|
const activator = assertInWorkflowContext('Workflow.upsertMemo(...) may only be used from a Workflow Execution.');
|
|
1715
|
+
const context = currentWorkflowSerializationContext(activator.info);
|
|
1678
1716
|
|
|
1679
1717
|
if (memo == null) {
|
|
1680
1718
|
throw new Error('memo must be a non-null Record');
|
|
@@ -1686,7 +1724,8 @@ export function upsertMemo(memo: Record<string, unknown>): void {
|
|
|
1686
1724
|
fields: mapToPayloads(
|
|
1687
1725
|
activator.payloadConverter,
|
|
1688
1726
|
// Convert null to undefined
|
|
1689
|
-
Object.fromEntries(Object.entries(memo).map(([k, v]) => [k, v ?? undefined]))
|
|
1727
|
+
Object.fromEntries(Object.entries(memo).map(([k, v]) => [k, v ?? undefined])),
|
|
1728
|
+
context
|
|
1690
1729
|
),
|
|
1691
1730
|
},
|
|
1692
1731
|
},
|
|
@@ -1728,7 +1767,10 @@ export function allHandlersFinished(): boolean {
|
|
|
1728
1767
|
* @example
|
|
1729
1768
|
* For example:
|
|
1730
1769
|
* ```ts
|
|
1731
|
-
* setWorkflowOptions({
|
|
1770
|
+
* setWorkflowOptions({
|
|
1771
|
+
* versioningBehavior: 'PINNED',
|
|
1772
|
+
* failureExceptionTypes: [CustomWorkflowError]
|
|
1773
|
+
* }, myWorkflow);
|
|
1732
1774
|
* export async function myWorkflow(): Promise<string> {
|
|
1733
1775
|
* // Workflow code here
|
|
1734
1776
|
* return "hi";
|
|
@@ -1742,7 +1784,10 @@ export function allHandlersFinished(): boolean {
|
|
|
1742
1784
|
* // Workflow code here
|
|
1743
1785
|
* return "hi";
|
|
1744
1786
|
* }
|
|
1745
|
-
* setWorkflowOptions({
|
|
1787
|
+
* setWorkflowOptions({
|
|
1788
|
+
* versioningBehavior: 'PINNED',
|
|
1789
|
+
* failureExceptionTypes: [CustomWorkflowError]
|
|
1790
|
+
* }, module.exports.default);
|
|
1746
1791
|
* ```
|
|
1747
1792
|
*
|
|
1748
1793
|
* @param options Options for the workflow defintion, or a function that returns options. If a
|