@temporalio/workflow 1.17.4 → 1.18.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.
Files changed (53) hide show
  1. package/lib/current-random.d.ts +1 -0
  2. package/lib/current-random.js +8 -0
  3. package/lib/current-random.js.map +1 -0
  4. package/lib/flags.js +1 -1
  5. package/lib/flags.js.map +1 -1
  6. package/lib/global-overrides.js +3 -2
  7. package/lib/global-overrides.js.map +1 -1
  8. package/lib/index.d.ts +3 -3
  9. package/lib/index.js +4 -1
  10. package/lib/index.js.map +1 -1
  11. package/lib/interceptor-composition.d.ts +23 -0
  12. package/lib/interceptor-composition.js +31 -0
  13. package/lib/interceptor-composition.js.map +1 -0
  14. package/lib/interfaces.d.ts +1 -0
  15. package/lib/internals.d.ts +55 -12
  16. package/lib/internals.js +178 -52
  17. package/lib/internals.js.map +1 -1
  18. package/lib/logs.js +2 -2
  19. package/lib/logs.js.map +1 -1
  20. package/lib/metrics.js +2 -2
  21. package/lib/metrics.js.map +1 -1
  22. package/lib/nexus.js +9 -4
  23. package/lib/nexus.js.map +1 -1
  24. package/lib/random-helpers.d.ts +2 -0
  25. package/lib/random-helpers.js +22 -0
  26. package/lib/random-helpers.js.map +1 -0
  27. package/lib/random-stream-seed.d.ts +1 -0
  28. package/lib/random-stream-seed.js +19 -0
  29. package/lib/random-stream-seed.js.map +1 -0
  30. package/lib/random-streams.d.ts +78 -0
  31. package/lib/random-streams.js +72 -0
  32. package/lib/random-streams.js.map +1 -0
  33. package/lib/worker-interface.js +6 -4
  34. package/lib/worker-interface.js.map +1 -1
  35. package/lib/workflow.d.ts +11 -5
  36. package/lib/workflow.js +88 -55
  37. package/lib/workflow.js.map +1 -1
  38. package/package.json +3 -3
  39. package/src/current-random.ts +5 -0
  40. package/src/flags.ts +1 -1
  41. package/src/global-overrides.ts +3 -2
  42. package/src/index.ts +4 -2
  43. package/src/interceptor-composition.ts +31 -0
  44. package/src/interfaces.ts +5 -4
  45. package/src/internals.ts +213 -55
  46. package/src/logs.ts +1 -1
  47. package/src/metrics.ts +1 -1
  48. package/src/nexus.ts +8 -3
  49. package/src/random-helpers.ts +21 -0
  50. package/src/random-stream-seed.ts +18 -0
  51. package/src/random-streams.ts +132 -0
  52. package/src/worker-interface.ts +3 -1
  53. package/src/workflow.ts +99 -54
package/src/nexus.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import type * as nexus from 'nexus-rpc';
2
2
  import { msOptionalToTs } from '@temporalio/common/lib/time';
3
3
  import { userMetadataToPayload } from '@temporalio/common/lib/user-metadata';
4
- import { composeInterceptors } from '@temporalio/common/lib/interceptors';
5
4
  import { makeProtoEnumConverters } from '@temporalio/common/lib/internal-workflow/enums-helpers';
6
5
  import type { coresdk } from '@temporalio/proto';
7
6
  import { CancellationScope } from './cancellation-scope';
8
7
  import { getActivator } from './global-attributes';
8
+ import { composeInterceptors } from './interceptor-composition';
9
9
  import { untrackPromise } from './stack-helpers';
10
10
  import type { StartNexusOperationInput, StartNexusOperationOutput, StartNexusOperationOptions } from './interceptors';
11
11
 
@@ -191,6 +191,11 @@ function startNexusOperationNextHandler({
191
191
  headers,
192
192
  }: StartNexusOperationInput): Promise<StartNexusOperationOutput> {
193
193
  const activator = getActivator();
194
+ const context = {
195
+ type: 'workflow' as const,
196
+ namespace: activator.info.namespace,
197
+ workflowId: activator.info.workflowId,
198
+ };
194
199
 
195
200
  return new Promise<StartNexusOperationOutput>((resolve, reject) => {
196
201
  const scope = CancellationScope.current();
@@ -223,13 +228,13 @@ function startNexusOperationNextHandler({
223
228
  service,
224
229
  operation,
225
230
  nexusHeader: headers,
226
- input: activator.payloadConverter.toPayload(input),
231
+ input: activator.payloadConverter.toPayload(input, context),
227
232
  scheduleToCloseTimeout: msOptionalToTs(options?.scheduleToCloseTimeout),
228
233
  scheduleToStartTimeout: msOptionalToTs(options?.scheduleToStartTimeout),
229
234
  startToCloseTimeout: msOptionalToTs(options?.startToCloseTimeout),
230
235
  cancellationType: encodeNexusOperationCancellationType(options?.cancellationType),
231
236
  },
232
- userMetadata: userMetadataToPayload(activator.payloadConverter, options?.summary, undefined),
237
+ userMetadata: userMetadataToPayload(activator.payloadConverter, options?.summary, undefined, context),
233
238
  });
234
239
 
235
240
  activator.completions.nexusOperationStart.set(seq, {
@@ -0,0 +1,21 @@
1
+ export function fillWithRandom(random: () => number, bytes: Uint8Array): Uint8Array {
2
+ for (let i = 0; i < bytes.length; ++i) {
3
+ bytes[i] = Math.floor(random() * 0x100);
4
+ }
5
+ return bytes;
6
+ }
7
+
8
+ export function uuid4FromRandom(random: () => number): string {
9
+ const ho = (n: number, p: number) => n.toString(16).padStart(p, '0');
10
+ const view = new DataView(new ArrayBuffer(16));
11
+ view.setUint32(0, (random() * 0x100000000) >>> 0);
12
+ view.setUint32(4, (random() * 0x100000000) >>> 0);
13
+ view.setUint32(8, (random() * 0x100000000) >>> 0);
14
+ view.setUint32(12, (random() * 0x100000000) >>> 0);
15
+ view.setUint8(6, (view.getUint8(6) & 0xf) | 0x40);
16
+ view.setUint8(8, (view.getUint8(8) & 0x3f) | 0x80);
17
+ return `${ho(view.getUint32(0), 8)}-${ho(view.getUint16(4), 4)}-${ho(view.getUint16(6), 4)}-${ho(
18
+ view.getUint16(8),
19
+ 4
20
+ )}-${ho(view.getUint32(10), 8)}${ho(view.getUint16(14), 4)}`;
21
+ }
@@ -0,0 +1,18 @@
1
+ import { encode } from '@temporalio/common/lib/encoding';
2
+
3
+ const RANDOM_STREAM_SEED_PREFIX = Array.from(encode('temporal-workflow-random-stream-v1'));
4
+
5
+ function encodeU32(value: number): number[] {
6
+ return [(value >>> 24) & 0xff, (value >>> 16) & 0xff, (value >>> 8) & 0xff, value & 0xff];
7
+ }
8
+
9
+ export function deriveAleaSeed(seed: number[], namespace: string): number[] {
10
+ const namespaceBytes = Array.from(encode(namespace));
11
+ return [
12
+ ...RANDOM_STREAM_SEED_PREFIX,
13
+ ...encodeU32(seed.length),
14
+ ...seed,
15
+ ...encodeU32(namespaceBytes.length),
16
+ ...namespaceBytes,
17
+ ];
18
+ }
@@ -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
+ }
@@ -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: options.activityId ?? `${seq}`,
235
+ activityId,
200
236
  activityType,
201
- arguments: toPayloads(activator.payloadConverter, ...args),
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), // eslint-disable-line @typescript-eslint/no-deprecated
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
- // Intentionally not exposing activityId as an option
269
- activityId: `${seq}`,
307
+ activityId,
270
308
  activityType,
271
- arguments: toPayloads(activator.payloadConverter, ...args),
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: toPayloads(activator.payloadConverter, ...options.args),
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 // eslint-disable-line @typescript-eslint/no-deprecated
411
- ? { indexedFields: encodeUnifiedSearchAttributes(options.searchAttributes, options.typedSearchAttributes) } // eslint-disable-line @typescript-eslint/no-deprecated
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), // eslint-disable-line @typescript-eslint/no-deprecated
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(activator.payloadConverter, options?.staticSummary, options?.staticDetails),
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: toPayloads(activator.payloadConverter, ...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: list of arguments
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: list of arguments
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, { resolve, reject });
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: toPayloads(activator.payloadConverter, ...args),
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 // eslint-disable-line @typescript-eslint/no-deprecated
1028
- ? { indexedFields: encodeUnifiedSearchAttributes(options.searchAttributes, options.typedSearchAttributes) } // eslint-disable-line @typescript-eslint/no-deprecated
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), // eslint-disable-line @typescript-eslint/no-deprecated
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/concepts/what-is-continue-as-new/ | Continues-As-New} the current Workflow Execution
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
- // Return the hexadecimal text representation of number `n`, padded with zeroes to be of length `p`
1074
- const ho = (n: number, p: number) => n.toString(16).padStart(p, '0');
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 }; // eslint-disable-line @typescript-eslint/no-deprecated
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); // eslint-disable-line @typescript-eslint/no-deprecated
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 }; // eslint-disable-line @typescript-eslint/no-deprecated
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({ versioningBehavior: 'PINNED' }, myWorkflow);
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({ versioningBehavior: 'PINNED' }, module.exports.default);
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