@temporalio/workflow 1.2.0 → 1.4.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/src/index.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * The recommended way of scheduling timers is by using the {@link sleep} function. We've replaced `setTimeout` and
10
10
  * `clearTimeout` with deterministic versions so these are also usable but have a limitation that they don't play well
11
- * with {@link https://docs.temporal.io/typescript/workflow-scopes-and-cancellation | cancellation scopes}.
11
+ * with {@link https://docs.temporal.io/typescript/cancellation-scopes | cancellation scopes}.
12
12
  *
13
13
  * <!--SNIPSTART typescript-sleep-workflow-->
14
14
  * <!--SNIPEND-->
@@ -34,45 +34,41 @@
34
34
  * <!--SNIPSTART typescript-workflow-signal-implementation-->
35
35
  * <!--SNIPEND-->
36
36
  *
37
- * ### Deterministic built-ins
38
- * It is safe to call `Math.random()` and `Date()` in workflow code as they are replaced with deterministic versions. We
39
- * also provide a deterministic {@link uuid4} function for convenience.
37
+ * ### More
40
38
  *
41
- * ### [Cancellation and scopes](https://docs.temporal.io/typescript/workflow-scopes-and-cancellation)
42
- * - {@link CancellationScope}
43
- * - {@link Trigger}
44
- *
45
- * ### [Sinks](https://docs.temporal.io/typescript/sinks)
46
- * - {@link Sinks}
39
+ * - [Deterministic built-ins](https://docs.temporal.io/typescript/determinism#sources-of-non-determinism)
40
+ * - [Cancellation and scopes](https://docs.temporal.io/typescript/cancellation-scopes)
41
+ * - {@link CancellationScope}
42
+ * - {@link Trigger}
43
+ * - [Sinks](https://docs.temporal.io/application-development/observability/?lang=ts#logging)
44
+ * - {@link Sinks}
47
45
  *
48
46
  * @module
49
47
  */
50
48
 
51
49
  export {
50
+ ActivityCancellationType,
52
51
  ActivityFailure,
52
+ ActivityFunction,
53
+ ActivityInterface,
54
+ ActivityOptions,
53
55
  ApplicationFailure,
54
56
  CancelledFailure,
55
57
  ChildWorkflowFailure,
56
58
  defaultPayloadConverter,
57
59
  PayloadConverter,
60
+ RetryPolicy,
58
61
  rootCause,
59
62
  ServerFailure,
60
63
  TemporalFailure,
61
64
  TerminatedFailure,
62
65
  TimeoutFailure,
63
- } from '@temporalio/common';
64
- export {
65
- ActivityCancellationType,
66
- ActivityFunction,
67
- ActivityInterface, // eslint-disable-line deprecation/deprecation
68
- ActivityOptions,
69
- RetryPolicy,
70
66
  UntypedActivities,
71
- } from '@temporalio/internal-workflow-common';
72
- export * from '@temporalio/internal-workflow-common/lib/errors';
73
- export * from '@temporalio/internal-workflow-common/lib/interfaces';
74
- export * from '@temporalio/internal-workflow-common/lib/workflow-handle';
75
- export * from '@temporalio/internal-workflow-common/lib/workflow-options';
67
+ } from '@temporalio/common';
68
+ export * from '@temporalio/common/lib/errors';
69
+ export * from '@temporalio/common/lib/interfaces';
70
+ export * from '@temporalio/common/lib/workflow-handle';
71
+ export * from '@temporalio/common/lib/workflow-options';
76
72
  export { AsyncLocalStorage, CancellationScope, CancellationScopeOptions, ROOT_SCOPE } from './cancellation-scope';
77
73
  export * from './errors';
78
74
  export * from './interceptors';
@@ -81,16 +77,17 @@ export {
81
77
  ChildWorkflowOptions,
82
78
  ContinueAsNew,
83
79
  ContinueAsNewOptions,
80
+ EnhancedStackTrace,
81
+ FileLocation,
82
+ FileSlice,
84
83
  ParentClosePolicy,
85
84
  ParentWorkflowInfo,
86
- WorkflowInfo,
87
- FileSlice,
88
- FileLocation,
89
- StackTrace,
90
- EnhancedStackTrace,
91
85
  SDKInfo,
86
+ StackTrace,
87
+ UnsafeWorkflowInfo,
88
+ WorkflowInfo,
92
89
  } from './interfaces';
93
- export { Sink, SinkCall, SinkFunction, Sinks } from './sinks';
90
+ export { LoggerSinks, Sink, SinkCall, SinkFunction, Sinks } from './sinks';
94
91
  export { Trigger } from './trigger';
95
92
  export * from './workflow';
96
93
  export { ChildWorkflowHandle, ExternalWorkflowHandle } from './workflow-handle';
@@ -6,8 +6,7 @@
6
6
  * @module
7
7
  */
8
8
 
9
- import { WorkflowExecution } from '@temporalio/common';
10
- import { ActivityOptions, Headers, LocalActivityOptions, Next, Timestamp } from '@temporalio/internal-workflow-common';
9
+ import { ActivityOptions, Headers, LocalActivityOptions, Next, Timestamp, WorkflowExecution } from '@temporalio/common';
11
10
  import type { coresdk } from '@temporalio/proto';
12
11
  import { ChildWorkflowOptionsWithDefaults, ContinueAsNewOptions } from './interfaces';
13
12
 
package/src/interfaces.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { RetryPolicy, TemporalFailure } from '@temporalio/common';
2
- import { checkExtends, CommonWorkflowOptions, SearchAttributes } from '@temporalio/internal-workflow-common';
2
+ import { CommonWorkflowOptions, SearchAttributes } from '@temporalio/common';
3
+ import { checkExtends } from '@temporalio/common/lib/type-helpers';
3
4
  import type { coresdk } from '@temporalio/proto';
4
5
 
5
6
  /**
@@ -135,6 +136,14 @@ export interface WorkflowInfo {
135
136
  * Never rely on this information in Workflow logic as it will cause non-deterministic behavior.
136
137
  */
137
138
  export interface UnsafeWorkflowInfo {
139
+ /**
140
+ * Current system time in milliseconds
141
+ *
142
+ * The safe version of time is `new Date()` and `Date.now()`, which are set on the first invocation of a Workflow
143
+ * Task and stay constant for the duration of the Task and during replay.
144
+ */
145
+ now(): number;
146
+
138
147
  isReplaying: boolean;
139
148
  }
140
149
 
@@ -169,12 +178,12 @@ export interface ContinueAsNewOptions {
169
178
  taskQueue?: string;
170
179
  /**
171
180
  * Timeout for the entire Workflow run
172
- * @format {@link https://www.npmjs.com/package/ms | ms} formatted string
181
+ * @format {@link https://www.npmjs.com/package/ms | ms-formatted string}
173
182
  */
174
183
  workflowRunTimeout?: string;
175
184
  /**
176
185
  * Timeout for a single Workflow task
177
- * @format {@link https://www.npmjs.com/package/ms | ms} formatted string
186
+ * @format {@link https://www.npmjs.com/package/ms | ms-formatted string}
178
187
  */
179
188
  workflowTaskTimeout?: string;
180
189
  /**
@@ -223,6 +232,7 @@ export enum ChildWorkflowCancellationType {
223
232
  }
224
233
 
225
234
  checkExtends<coresdk.child_workflow.ChildWorkflowCancellationType, ChildWorkflowCancellationType>();
235
+ checkExtends<ChildWorkflowCancellationType, coresdk.child_workflow.ChildWorkflowCancellationType>();
226
236
 
227
237
  /**
228
238
  * How a Child Workflow reacts to the Parent Workflow reaching a Closed state.
@@ -254,6 +264,7 @@ export enum ParentClosePolicy {
254
264
  }
255
265
 
256
266
  checkExtends<coresdk.child_workflow.ParentClosePolicy, ParentClosePolicy>();
267
+ checkExtends<ParentClosePolicy, coresdk.child_workflow.ParentClosePolicy>();
257
268
 
258
269
  export interface ChildWorkflowOptions extends CommonWorkflowOptions {
259
270
  /**
package/src/internals.ts CHANGED
@@ -1,22 +1,17 @@
1
- import { PayloadConverter } from '@temporalio/common';
1
+ import { defaultFailureConverter, FailureConverter, PayloadConverter } from '@temporalio/common';
2
2
  import type { RawSourceMap } from 'source-map';
3
3
  import {
4
4
  arrayFromPayloads,
5
5
  defaultPayloadConverter,
6
6
  ensureTemporalFailure,
7
- errorToFailure,
8
- failureToError,
9
- optionalFailureToOptionalError,
10
- TemporalFailure,
11
- } from '@temporalio/common';
12
- import {
13
- checkExtends,
14
- composeInterceptors,
15
7
  IllegalStateError,
8
+ TemporalFailure,
16
9
  Workflow,
17
10
  WorkflowQueryType,
18
11
  WorkflowSignalType,
19
- } from '@temporalio/internal-workflow-common';
12
+ } from '@temporalio/common';
13
+ import { composeInterceptors } from '@temporalio/common/lib/interceptors';
14
+ import { checkExtends } from '@temporalio/common/lib/type-helpers';
20
15
  import type { coresdk } from '@temporalio/proto';
21
16
  import { alea, RNG } from './alea';
22
17
  import { ROOT_SCOPE } from './cancellation-scope';
@@ -39,6 +34,7 @@ enum StartChildWorkflowExecutionFailedCause {
39
34
  }
40
35
 
41
36
  checkExtends<coresdk.child_workflow.StartChildWorkflowExecutionFailedCause, StartChildWorkflowExecutionFailedCause>();
37
+ checkExtends<StartChildWorkflowExecutionFailedCause, coresdk.child_workflow.StartChildWorkflowExecutionFailedCause>();
42
38
 
43
39
  export interface Stack {
44
40
  formatted: string;
@@ -147,11 +143,11 @@ export class Activator implements ActivationHandler {
147
143
  resolve(result);
148
144
  } else if (activation.result.failed) {
149
145
  const { failure } = activation.result.failed;
150
- const err = optionalFailureToOptionalError(failure, state.payloadConverter);
146
+ const err = failure ? state.failureConverter.failureToError(failure) : undefined;
151
147
  reject(err);
152
148
  } else if (activation.result.cancelled) {
153
149
  const { failure } = activation.result.cancelled;
154
- const err = optionalFailureToOptionalError(failure, state.payloadConverter);
150
+ const err = failure ? state.failureConverter.failureToError(failure) : undefined;
155
151
  reject(err);
156
152
  } else if (activation.result.backoff) {
157
153
  reject(new LocalActivityDoBackoff(activation.result.backoff));
@@ -185,7 +181,7 @@ export class Activator implements ActivationHandler {
185
181
  if (!activation.cancelled.failure) {
186
182
  throw new TypeError('Got no failure in cancelled variant');
187
183
  }
188
- reject(failureToError(activation.cancelled.failure, state.payloadConverter));
184
+ reject(state.failureConverter.failureToError(activation.cancelled.failure));
189
185
  } else {
190
186
  throw new TypeError('Got ResolveChildWorkflowExecutionStart with no status');
191
187
  }
@@ -205,13 +201,13 @@ export class Activator implements ActivationHandler {
205
201
  if (failure === undefined || failure === null) {
206
202
  throw new TypeError('Got failed result with no failure attribute');
207
203
  }
208
- reject(failureToError(failure, state.payloadConverter));
204
+ reject(state.failureConverter.failureToError(failure));
209
205
  } else if (activation.result.cancelled) {
210
206
  const { failure } = activation.result.cancelled;
211
207
  if (failure === undefined || failure === null) {
212
208
  throw new TypeError('Got cancelled result with no failure attribute');
213
209
  }
214
- reject(failureToError(failure, state.payloadConverter));
210
+ reject(state.failureConverter.failureToError(failure));
215
211
  }
216
212
  }
217
213
 
@@ -303,7 +299,7 @@ export class Activator implements ActivationHandler {
303
299
  public resolveSignalExternalWorkflow(activation: coresdk.workflow_activation.IResolveSignalExternalWorkflow): void {
304
300
  const { resolve, reject } = consumeCompletion('signalWorkflow', getSeq(activation));
305
301
  if (activation.failure) {
306
- reject(failureToError(activation.failure, state.payloadConverter));
302
+ reject(state.failureConverter.failureToError(activation.failure));
307
303
  } else {
308
304
  resolve(undefined);
309
305
  }
@@ -314,7 +310,7 @@ export class Activator implements ActivationHandler {
314
310
  ): void {
315
311
  const { resolve, reject } = consumeCompletion('cancelWorkflow', getSeq(activation));
316
312
  if (activation.failure) {
317
- reject(failureToError(activation.failure, state.payloadConverter));
313
+ reject(state.failureConverter.failureToError(activation.failure));
318
314
  } else {
319
315
  resolve(undefined);
320
316
  }
@@ -546,6 +542,7 @@ export class State {
546
542
  public importInterceptors?: InterceptorsImportFunc;
547
543
 
548
544
  public payloadConverter: PayloadConverter = defaultPayloadConverter;
545
+ public failureConverter: FailureConverter = defaultFailureConverter;
549
546
 
550
547
  /**
551
548
  * Patches we know the status of for this workflow, as in {@link patched}
@@ -612,7 +609,7 @@ export async function handleWorkflowFailure(error: unknown): Promise<void> {
612
609
  state.pushCommand(
613
610
  {
614
611
  failWorkflowExecution: {
615
- failure: errorToFailure(error, state.payloadConverter),
612
+ failure: state.failureConverter.errorToFailure(error),
616
613
  },
617
614
  },
618
615
  true
@@ -628,7 +625,7 @@ function completeQuery(queryId: string, result: unknown) {
628
625
 
629
626
  async function failQuery(queryId: string, error: any) {
630
627
  state.pushCommand({
631
- respondToQuery: { queryId, failed: errorToFailure(ensureTemporalFailure(error), state.payloadConverter) },
628
+ respondToQuery: { queryId, failed: state.failureConverter.errorToFailure(ensureTemporalFailure(error)) },
632
629
  });
633
630
  }
634
631
 
package/src/sinks.ts CHANGED
@@ -39,3 +39,16 @@ export interface SinkCall {
39
39
  fnName: string;
40
40
  args: any[];
41
41
  }
42
+
43
+ /**
44
+ * Sink interface for forwarding logs from the Workflow sandbox to the Worker
45
+ */
46
+ export interface LoggerSinks extends Sinks {
47
+ defaultWorkerLogger: {
48
+ trace(message: string, attrs: Record<string, unknown>): void;
49
+ debug(message: string, attrs: Record<string, unknown>): void;
50
+ info(message: string, attrs: Record<string, unknown>): void;
51
+ warn(message: string, attrs: Record<string, unknown>): void;
52
+ error(message: string, attrs: Record<string, unknown>): void;
53
+ };
54
+ }
@@ -3,8 +3,9 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import { errorToFailure as _errorToFailure, ProtoFailure } from '@temporalio/common';
7
- import { composeInterceptors, IllegalStateError, msToTs, tsToMs } from '@temporalio/internal-workflow-common';
6
+ import { IllegalStateError, ProtoFailure } from '@temporalio/common';
7
+ import { msToTs, tsToMs } from '@temporalio/common/lib/time';
8
+ import { composeInterceptors } from '@temporalio/common/lib/interceptors';
8
9
  import type { coresdk } from '@temporalio/proto';
9
10
  import { alea } from './alea';
10
11
  import { storage } from './cancellation-scope';
@@ -39,8 +40,10 @@ export function setImportFuncs({ importWorkflows, importInterceptors }: ImportFu
39
40
  state.importInterceptors = importInterceptors;
40
41
  }
41
42
 
43
+ const global = globalThis as any;
44
+ const OriginalDate = globalThis.Date;
45
+
42
46
  export function overrideGlobals(): void {
43
- const global = globalThis as any;
44
47
  // Mock any weak reference because GC is non-deterministic and the effect is observable from the Workflow.
45
48
  // WeakRef is implemented in V8 8.4 which is embedded in node >=14.6.0.
46
49
  // Workflow developer will get a meaningful exception if they try to use these.
@@ -53,8 +56,6 @@ export function overrideGlobals(): void {
53
56
  );
54
57
  };
55
58
 
56
- const OriginalDate = globalThis.Date;
57
-
58
59
  global.Date = function (...args: unknown[]) {
59
60
  if (args.length > 0) {
60
61
  return new (OriginalDate as any)(...args);
@@ -120,7 +121,6 @@ export async function initRuntime({
120
121
  sourceMap,
121
122
  showStackTraceSources,
122
123
  }: WorkflowCreateOptionsWithSourceMap): Promise<void> {
123
- const global = globalThis as any;
124
124
  // Set the runId globally on the context so it can be retrieved in the case
125
125
  // of an unhandled promise rejection.
126
126
  global.__TEMPORAL__.runId = info.runId;
@@ -131,6 +131,7 @@ export async function initRuntime({
131
131
  };
132
132
 
133
133
  state.info = info;
134
+ state.info.unsafe.now = OriginalDate.now;
134
135
  state.now = now;
135
136
  state.random = alea(randomnessSeed);
136
137
  state.showStackTraceSources = showStackTraceSources;
@@ -142,14 +143,18 @@ export async function initRuntime({
142
143
  }
143
144
  }
144
145
 
145
- // webpack doesn't know what to bundle given a dynamic import expression, so we can't do:
146
- // state.payloadConverter = (await import(payloadConverterPath)).payloadConverter;
147
146
  // @ts-expect-error this is a webpack alias to payloadConverterPath
148
147
  const customPayloadConverter = (await import('__temporal_custom_payload_converter')).payloadConverter;
149
148
  // The `payloadConverter` export is validated in the Worker
150
149
  if (customPayloadConverter !== undefined) {
151
150
  state.payloadConverter = customPayloadConverter;
152
151
  }
152
+ // @ts-expect-error this is a webpack alias to failureConverterPath
153
+ const customFailureConverter = (await import('__temporal_custom_failure_converter')).failureConverter;
154
+ // The `failureConverter` export is validated in the Worker
155
+ if (customFailureConverter !== undefined) {
156
+ state.failureConverter = customFailureConverter;
157
+ }
153
158
 
154
159
  const { importWorkflows, importInterceptors } = state;
155
160
  if (importWorkflows === undefined || importInterceptors === undefined) {
@@ -293,5 +298,5 @@ export async function dispose(): Promise<void> {
293
298
  }
294
299
 
295
300
  export function errorToFailure(err: unknown): ProtoFailure {
296
- return _errorToFailure(err, state.payloadConverter);
301
+ return state.failureConverter.errorToFailure(err);
297
302
  }
@@ -1,4 +1,4 @@
1
- import { BaseWorkflowHandle, SignalDefinition, Workflow } from '@temporalio/internal-workflow-common';
1
+ import { BaseWorkflowHandle, SignalDefinition, Workflow } from '@temporalio/common';
2
2
 
3
3
  /**
4
4
  * Handle representing an external Workflow Execution.
package/src/workflow.ts CHANGED
@@ -1,24 +1,23 @@
1
- import { mapToPayloads, searchAttributePayloadConverter, toPayloads } from '@temporalio/common';
2
1
  import {
3
2
  ActivityFunction,
4
3
  ActivityOptions,
5
4
  compileRetryPolicy,
6
- composeInterceptors,
7
5
  IllegalStateError,
8
6
  LocalActivityOptions,
9
- msOptionalToTs,
10
- msToNumber,
11
- msToTs,
7
+ mapToPayloads,
12
8
  QueryDefinition,
9
+ searchAttributePayloadConverter,
13
10
  SearchAttributes,
14
11
  SignalDefinition,
15
- tsToMs,
12
+ toPayloads,
16
13
  UntypedActivities,
17
14
  WithWorkflowArgs,
18
15
  Workflow,
19
16
  WorkflowResultType,
20
17
  WorkflowReturnType,
21
- } from '@temporalio/internal-workflow-common';
18
+ } from '@temporalio/common';
19
+ import { msOptionalToTs, msToNumber, msToTs, tsToMs } from '@temporalio/common/lib/time';
20
+ import { composeInterceptors } from '@temporalio/common/lib/interceptors';
22
21
  import { CancellationScope, registerSleepImplementation } from './cancellation-scope';
23
22
  import {
24
23
  ActivityInput,
@@ -102,7 +101,7 @@ function timerNextHandler(input: TimerInput) {
102
101
  *
103
102
  * Schedules a timer on the Temporal service.
104
103
  *
105
- * @param ms sleep duration - {@link https://www.npmjs.com/package/ms | ms} formatted string or number of milliseconds.
104
+ * @param ms sleep duration - number of milliseconds or {@link https://www.npmjs.com/package/ms | ms-formatted string}.
106
105
  * If given a negative number or 0, value will be set to 1.
107
106
  */
108
107
  export function sleep(ms: number | string): Promise<void> {
@@ -171,6 +170,7 @@ function scheduleActivityNextHandler({ options, args, headers, seq, activityType
171
170
  scheduleToStartTimeout: msOptionalToTs(options.scheduleToStartTimeout),
172
171
  headers,
173
172
  cancellationType: options.cancellationType,
173
+ doNotEagerlyExecute: !(options.allowEagerDispatch ?? true),
174
174
  },
175
175
  });
176
176
  state.completions.activity.set(seq, {
@@ -325,21 +325,10 @@ function startChildWorkflowExecutionNextHandler({
325
325
  untrackPromise(
326
326
  scope.cancelRequested.catch(() => {
327
327
  const complete = !state.completions.childWorkflowComplete.has(seq);
328
- const started = !state.completions.childWorkflowStart.has(seq);
329
328
 
330
- if (started && !complete) {
331
- const cancelSeq = state.nextSeqs.cancelWorkflow++;
329
+ if (!complete) {
332
330
  state.pushCommand({
333
- requestCancelExternalWorkflowExecution: {
334
- seq: cancelSeq,
335
- childWorkflowId: workflowId,
336
- },
337
- });
338
- // Not interested in this completion
339
- state.completions.cancelWorkflow.set(cancelSeq, { resolve: () => undefined, reject: () => undefined });
340
- } else if (!started) {
341
- state.pushCommand({
342
- cancelUnstartedChildWorkflowExecution: { childWorkflowSeq: seq },
331
+ cancelChildWorkflowExecution: { childWorkflowSeq: seq },
343
332
  });
344
333
  }
345
334
  // Nothing to cancel otherwise
@@ -1078,7 +1067,7 @@ function patchInternal(patchId: string, deprecated: boolean): boolean {
1078
1067
  /**
1079
1068
  * Returns a Promise that resolves when `fn` evaluates to `true` or `timeout` expires.
1080
1069
  *
1081
- * @param timeout {@link https://www.npmjs.com/package/ms | ms} formatted string or number of milliseconds
1070
+ * @param timeout number of milliseconds or {@link https://www.npmjs.com/package/ms | ms-formatted string}
1082
1071
  *
1083
1072
  * @returns a boolean indicating whether the condition was true before the timeout expires
1084
1073
  */