@temporalio/client 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.
@@ -18,23 +18,24 @@ import {
18
18
  decodeOptionalFailureToOptionalError,
19
19
  encodeMapToPayloads,
20
20
  encodeToPayloads,
21
+ filterNullAndUndefined,
22
+ isLoadedDataConverter,
21
23
  loadDataConverter,
22
- } from '@temporalio/internal-non-workflow-common';
24
+ } from '@temporalio/common/lib/internal-non-workflow';
23
25
  import {
24
26
  BaseWorkflowHandle,
25
27
  compileRetryPolicy,
26
- composeInterceptors,
27
- optionalTsToDate,
28
28
  QueryDefinition,
29
- Replace,
30
29
  SearchAttributes,
31
30
  SignalDefinition,
32
- tsToDate,
33
31
  WithWorkflowArgs,
34
32
  Workflow,
35
33
  WorkflowNotFoundError,
36
34
  WorkflowResultType,
37
- } from '@temporalio/internal-workflow-common';
35
+ } from '@temporalio/common';
36
+ import { optionalTsToDate, tsToDate } from '@temporalio/common/lib/time';
37
+ import { composeInterceptors } from '@temporalio/common/lib/interceptors';
38
+ import { Replace } from '@temporalio/common/lib/type-helpers';
38
39
  import { temporal } from '@temporalio/proto';
39
40
  import os from 'os';
40
41
  import { v4 as uuid4 } from 'uuid';
@@ -118,7 +119,17 @@ export interface WorkflowHandle<T extends Workflow = Workflow> extends BaseWorkf
118
119
  terminate(reason?: string): Promise<TerminateWorkflowExecutionResponse>;
119
120
 
120
121
  /**
121
- * Cancel a running Workflow
122
+ * Cancel a running Workflow.
123
+ *
124
+ * When a Workflow is cancelled, the root scope throws {@link CancelledFailure} with `message: 'Workflow canceled'`.
125
+ * That means that all cancellable scopes will throw `CancelledFailure`.
126
+ *
127
+ * Cancellation may be propagated to Activities depending on {@link ActivityOptions#cancellationType}, after which
128
+ * Activity calls may throw an {@link ActivityFailure}, and `isCancellation(error)` will be true (see {@link isCancellation}).
129
+ *
130
+ * Cancellation may be propagated to Child Workflows depending on {@link ChildWorkflowOptions#cancellationType}, after
131
+ * which calls to {@link executeChild} and {@link ChildWorkflowHandle#result} will throw, and `isCancellation(error)`
132
+ * will be true (see {@link isCancellation}).
122
133
  */
123
134
  cancel(): Promise<RequestCancelWorkflowExecutionResponse>;
124
135
 
@@ -160,9 +171,9 @@ export interface WorkflowHandleWithSignaledRunId<T extends Workflow = Workflow>
160
171
 
161
172
  export interface WorkflowClientOptions {
162
173
  /**
163
- * {@link DataConverter} to use for serializing and deserializing payloads
174
+ * {@link DataConverter} or {@link LoadedDataConverter} to use for serializing and deserializing payloads
164
175
  */
165
- dataConverter?: DataConverter;
176
+ dataConverter?: DataConverter | LoadedDataConverter;
166
177
 
167
178
  /**
168
179
  * Used to override and extend default Connection functionality
@@ -285,7 +296,10 @@ interface WorkflowHandleOptions extends GetWorkflowHandleOptions {
285
296
  export type WorkflowStartOptions<T extends Workflow = Workflow> = WithWorkflowArgs<T, WorkflowOptions>;
286
297
 
287
298
  /**
288
- * Client for starting Workflow executions and creating Workflow handles
299
+ * Client for starting Workflow executions and creating Workflow handles.
300
+ *
301
+ * Typically this client should not be instantiated directly, instead create the high level {@link Client} and use
302
+ * {@link Client.workflow} to interact with Workflows.
289
303
  */
290
304
  export class WorkflowClient {
291
305
  public readonly options: LoadedWorkflowClientOptions;
@@ -293,22 +307,29 @@ export class WorkflowClient {
293
307
 
294
308
  constructor(options?: WorkflowClientOptions) {
295
309
  this.connection = options?.connection ?? Connection.lazy();
310
+ const dataConverter = options?.dataConverter;
311
+ const loadedDataConverter = isLoadedDataConverter(dataConverter) ? dataConverter : loadDataConverter(dataConverter);
296
312
  this.options = {
297
313
  ...defaultWorkflowClientOptions(),
298
- ...options,
299
- loadedDataConverter: loadDataConverter(options?.dataConverter),
314
+ ...filterNullAndUndefined(options ?? {}),
315
+ loadedDataConverter,
300
316
  };
301
317
  }
302
318
 
303
319
  /**
304
320
  * Raw gRPC access to the Temporal service.
305
321
  *
306
- * **NOTE**: The namespace provided in {@link options} is **not** automatically set on requests made to the service.
322
+ * **NOTE**: The namespace provided in {@link options} is **not** automatically set on requests made via this service
323
+ * object.
307
324
  */
308
325
  get workflowService(): WorkflowService {
309
326
  return this.connection.workflowService;
310
327
  }
311
328
 
329
+ protected get dataConverter(): LoadedDataConverter {
330
+ return this.options.loadedDataConverter;
331
+ }
332
+
312
333
  /**
313
334
  * Set the deadline for any service requests executed in `fn`'s scope.
314
335
  */
@@ -377,7 +398,7 @@ export class WorkflowClient {
377
398
  headers: {},
378
399
  workflowType,
379
400
  signalName: typeof signal === 'string' ? signal : signal.name,
380
- signalArgs,
401
+ signalArgs: signalArgs ?? [],
381
402
  });
382
403
  }
383
404
 
@@ -409,15 +430,15 @@ export class WorkflowClient {
409
430
  }
410
431
 
411
432
  /**
412
- * Sends a signal to a running Workflow or starts a new one if not already running and immediately signals it.
413
- * Useful when you're unsure of the Workflows' run state.
433
+ * Sends a Signal to a running Workflow or starts a new one if not already running and immediately Signals it.
434
+ * Useful when you're unsure whether the Workflow has been started.
414
435
  *
415
- * @returns a WorkflowHandle to the started Workflow
436
+ * @returns a {@link WorkflowHandle} to the started Workflow
416
437
  */
417
- public async signalWithStart<T extends Workflow, SA extends any[] = []>(
418
- workflowTypeOrFunc: string | T,
419
- options: WithWorkflowArgs<T, WorkflowSignalWithStartOptions<SA>>
420
- ): Promise<WorkflowHandleWithSignaledRunId<T>> {
438
+ public async signalWithStart<WorkflowFn extends Workflow, SignalArgs extends any[] = []>(
439
+ workflowTypeOrFunc: string | WorkflowFn,
440
+ options: WithWorkflowArgs<WorkflowFn, WorkflowSignalWithStartOptions<SignalArgs>>
441
+ ): Promise<WorkflowHandleWithSignaledRunId<WorkflowFn>> {
421
442
  const { workflowId } = options;
422
443
  const interceptors = (this.options.interceptors.calls ?? []).map((ctor) => ctor({ workflowId }));
423
444
  const runId = await this._signalWithStart(workflowTypeOrFunc, options, interceptors);
@@ -430,7 +451,7 @@ export class WorkflowClient {
430
451
  runIdForResult: runId,
431
452
  interceptors,
432
453
  followRuns: options.followRuns ?? true,
433
- }) as WorkflowHandleWithSignaledRunId<T>; // Cast is safe because we know we add the signaledRunId below
454
+ }) as WorkflowHandleWithSignaledRunId<WorkflowFn>; // Cast is safe because we know we add the signaledRunId below
434
455
  (handle as any) /* readonly */.signaledRunId = runId;
435
456
  return handle;
436
457
  }
@@ -506,7 +527,7 @@ export class WorkflowClient {
506
527
  // Note that we can only return one value from our workflow function in JS.
507
528
  // Ignore any other payloads in result
508
529
  const [result] = await decodeArrayFromPayloads(
509
- this.options.loadedDataConverter,
530
+ this.dataConverter,
510
531
  ev.workflowExecutionCompletedEventAttributes.result?.payloads
511
532
  );
512
533
  return result as any;
@@ -519,14 +540,14 @@ export class WorkflowClient {
519
540
  const { failure, retryState } = ev.workflowExecutionFailedEventAttributes;
520
541
  throw new WorkflowFailedError(
521
542
  'Workflow execution failed',
522
- await decodeOptionalFailureToOptionalError(this.options.loadedDataConverter, failure),
543
+ await decodeOptionalFailureToOptionalError(this.dataConverter, failure),
523
544
  retryState ?? RetryState.RETRY_STATE_UNSPECIFIED
524
545
  );
525
546
  } else if (ev.workflowExecutionCanceledEventAttributes) {
526
547
  const failure = new CancelledFailure(
527
548
  'Workflow canceled',
528
549
  await decodeArrayFromPayloads(
529
- this.options.loadedDataConverter,
550
+ this.dataConverter,
530
551
  ev.workflowExecutionCanceledEventAttributes.details?.payloads
531
552
  )
532
553
  );
@@ -606,7 +627,7 @@ export class WorkflowClient {
606
627
  execution: input.workflowExecution,
607
628
  query: {
608
629
  queryType: input.queryType,
609
- queryArgs: { payloads: await encodeToPayloads(this.options.loadedDataConverter, ...input.args) },
630
+ queryArgs: { payloads: await encodeToPayloads(this.dataConverter, ...input.args) },
610
631
  header: { fields: input.headers },
611
632
  },
612
633
  });
@@ -626,7 +647,7 @@ export class WorkflowClient {
626
647
  throw new TypeError('Invalid response from server');
627
648
  }
628
649
  // We ignore anything but the first result
629
- return await decodeFromPayloadsAtIndex(this.options.loadedDataConverter, 0, response.queryResult?.payloads);
650
+ return await decodeFromPayloadsAtIndex(this.dataConverter, 0, response.queryResult?.payloads);
630
651
  }
631
652
 
632
653
  /**
@@ -644,7 +665,7 @@ export class WorkflowClient {
644
665
  // control is unused,
645
666
  signalName: input.signalName,
646
667
  header: { fields: input.headers },
647
- input: { payloads: await encodeToPayloads(this.options.loadedDataConverter, ...input.args) },
668
+ input: { payloads: await encodeToPayloads(this.dataConverter, ...input.args) },
648
669
  });
649
670
  } catch (err) {
650
671
  this.rethrowGrpcError(err, input.workflowExecution, 'Failed to signal Workflow');
@@ -667,9 +688,9 @@ export class WorkflowClient {
667
688
  workflowId: options.workflowId,
668
689
  workflowIdReusePolicy: options.workflowIdReusePolicy,
669
690
  workflowType: { name: workflowType },
670
- input: { payloads: await encodeToPayloads(this.options.loadedDataConverter, ...options.args) },
691
+ input: { payloads: await encodeToPayloads(this.dataConverter, ...options.args) },
671
692
  signalName,
672
- signalInput: { payloads: await encodeToPayloads(this.options.loadedDataConverter, ...signalArgs) },
693
+ signalInput: { payloads: await encodeToPayloads(this.dataConverter, ...signalArgs) },
673
694
  taskQueue: {
674
695
  kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED,
675
696
  name: options.taskQueue,
@@ -678,9 +699,7 @@ export class WorkflowClient {
678
699
  workflowRunTimeout: options.workflowRunTimeout,
679
700
  workflowTaskTimeout: options.workflowTaskTimeout,
680
701
  retryPolicy: options.retry ? compileRetryPolicy(options.retry) : undefined,
681
- memo: options.memo
682
- ? { fields: await encodeMapToPayloads(this.options.loadedDataConverter, options.memo) }
683
- : undefined,
702
+ memo: options.memo ? { fields: await encodeMapToPayloads(this.dataConverter, options.memo) } : undefined,
684
703
  searchAttributes: options.searchAttributes
685
704
  ? {
686
705
  indexedFields: mapToPayloads(searchAttributePayloadConverter, options.searchAttributes),
@@ -710,7 +729,7 @@ export class WorkflowClient {
710
729
  workflowId: opts.workflowId,
711
730
  workflowIdReusePolicy: opts.workflowIdReusePolicy,
712
731
  workflowType: { name: workflowType },
713
- input: { payloads: await encodeToPayloads(this.options.loadedDataConverter, ...opts.args) },
732
+ input: { payloads: await encodeToPayloads(this.dataConverter, ...opts.args) },
714
733
  taskQueue: {
715
734
  kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED,
716
735
  name: opts.taskQueue,
@@ -719,7 +738,7 @@ export class WorkflowClient {
719
738
  workflowRunTimeout: opts.workflowRunTimeout,
720
739
  workflowTaskTimeout: opts.workflowTaskTimeout,
721
740
  retryPolicy: opts.retry ? compileRetryPolicy(opts.retry) : undefined,
722
- memo: opts.memo ? { fields: await encodeMapToPayloads(this.options.loadedDataConverter, opts.memo) } : undefined,
741
+ memo: opts.memo ? { fields: await encodeMapToPayloads(this.dataConverter, opts.memo) } : undefined,
723
742
  searchAttributes: opts.searchAttributes
724
743
  ? {
725
744
  indexedFields: mapToPayloads(searchAttributePayloadConverter, opts.searchAttributes),
@@ -757,9 +776,7 @@ export class WorkflowClient {
757
776
  identity: this.options.identity,
758
777
  ...input,
759
778
  details: {
760
- payloads: input.details
761
- ? await encodeToPayloads(this.options.loadedDataConverter, ...input.details)
762
- : undefined,
779
+ payloads: input.details ? await encodeToPayloads(this.dataConverter, ...input.details) : undefined,
763
780
  },
764
781
  firstExecutionRunId: input.firstExecutionRunId,
765
782
  });
@@ -853,21 +870,20 @@ export class WorkflowClient {
853
870
  code: raw.workflowExecutionInfo!.status!,
854
871
  name: workflowStatusCodeToName(raw.workflowExecutionInfo!.status!),
855
872
  },
856
- // Technically safe to convert to number, unfortunately this was overlooked when this was originally
857
- // implemented.
858
- // Max history length is 50k, which is much less than Number.MAX_SAFE_INTEGER
859
- historyLength: raw.workflowExecutionInfo!.historyLength!,
873
+ // Safe to convert to number, max history length is 50k, which is much less than Number.MAX_SAFE_INTEGER
874
+ historyLength: raw.workflowExecutionInfo!.historyLength!.toNumber(),
860
875
  startTime: tsToDate(raw.workflowExecutionInfo!.startTime!),
861
876
  executionTime: optionalTsToDate(raw.workflowExecutionInfo!.executionTime),
862
877
  closeTime: optionalTsToDate(raw.workflowExecutionInfo!.closeTime),
863
- memo: await decodeMapFromPayloads(
864
- this.client.options.loadedDataConverter,
865
- raw.workflowExecutionInfo!.memo?.fields
878
+ memo: await decodeMapFromPayloads(this.client.dataConverter, raw.workflowExecutionInfo!.memo?.fields),
879
+ searchAttributes: Object.fromEntries(
880
+ Object.entries(
881
+ mapFromPayloads(
882
+ searchAttributePayloadConverter,
883
+ raw.workflowExecutionInfo!.searchAttributes?.indexedFields ?? {}
884
+ ) as SearchAttributes
885
+ ).filter(([_, v]) => v && v.length > 0) // Filter out empty arrays returned by pre 1.18 servers
866
886
  ),
867
- searchAttributes: mapFromPayloads(
868
- searchAttributePayloadConverter,
869
- raw.workflowExecutionInfo!.searchAttributes?.indexedFields ?? {}
870
- ) as SearchAttributes,
871
887
  parentExecution: raw.workflowExecutionInfo?.parentExecution
872
888
  ? {
873
889
  workflowId: raw.workflowExecutionInfo.parentExecution.workflowId!,
@@ -1,10 +1,6 @@
1
- import {
2
- CommonWorkflowOptions,
3
- SignalDefinition,
4
- WithCompiledWorkflowOptions,
5
- } from '@temporalio/internal-workflow-common';
1
+ import { CommonWorkflowOptions, SignalDefinition, WithCompiledWorkflowOptions } from '@temporalio/common';
6
2
 
7
- export * from '@temporalio/internal-workflow-common/lib/workflow-options';
3
+ export * from '@temporalio/common/lib/workflow-options';
8
4
 
9
5
  export interface CompiledWorkflowOptions extends WithCompiledWorkflowOptions<WorkflowOptions> {
10
6
  args: unknown[];
@@ -37,40 +33,30 @@ export interface WorkflowOptions extends CommonWorkflowOptions {
37
33
  followRuns?: boolean;
38
34
  }
39
35
 
40
- export interface WorkflowSignalWithStartOptions<SA extends any[] = []> extends WorkflowOptions {
36
+ export type WorkflowSignalWithStartOptions<SignalArgs extends any[] = []> = SignalArgs extends [any, ...any[]]
37
+ ? WorkflowSignalWithStartOptionsWithArgs<SignalArgs>
38
+ : WorkflowSignalWithStartOptionsWithoutArgs<SignalArgs>;
39
+
40
+ export interface WorkflowSignalWithStartOptionsWithoutArgs<SignalArgs extends any[]> extends WorkflowOptions {
41
41
  /**
42
42
  * SignalDefinition or name of signal
43
43
  */
44
- signal: SignalDefinition<SA> | string;
44
+ signal: SignalDefinition | string;
45
+
45
46
  /**
46
47
  * Arguments to invoke the signal handler with
47
48
  */
48
- signalArgs: SA;
49
+ signalArgs?: SignalArgs;
49
50
  }
50
51
 
51
- // export interface WorkflowOptionsWithDefaults<T extends Workflow> extends CommonWorkflowOptionsWithDefaults<T> {
52
- // /**
53
- // * If set to true, instructs the client to follow the chain of execution before returning a Workflow's result.
54
- // *
55
- // * Workflow execution is chained if the Workflow has a cron schedule or continues-as-new or configured to retry
56
- // * after failure or timeout.
57
- // *
58
- // * @default true
59
- // */
60
- // followRuns: boolean;
61
- // }
62
- //
63
- // /**
64
- // * Adds default values to `workflowId` and `workflowIdReusePolicy` to given workflow options.
65
- // */
66
- // export function addDefaults<T extends Workflow>(
67
- // opts: WithWorkflowArgs<T, WorkflowOptions>
68
- // ): WorkflowOptionsWithDefaults<T> {
69
- // const { workflowId, args, ...rest } = opts;
70
- // return {
71
- // followRuns: true,
72
- // args: args ?? [],
73
- // workflowId: workflowId ?? uuid4(),
74
- // ...rest,
75
- // };
76
- // }
52
+ export interface WorkflowSignalWithStartOptionsWithArgs<SignalArgs extends any[]> extends WorkflowOptions {
53
+ /**
54
+ * SignalDefinition or name of signal
55
+ */
56
+ signal: SignalDefinition<SignalArgs> | string;
57
+
58
+ /**
59
+ * Arguments to invoke the signal handler with
60
+ */
61
+ signalArgs: SignalArgs;
62
+ }