@temporalio/client 1.7.4 → 1.8.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 (45) hide show
  1. package/lib/async-completion-client.js +25 -22
  2. package/lib/async-completion-client.js.map +1 -1
  3. package/lib/build-id-types.d.ts +95 -0
  4. package/lib/build-id-types.js +28 -0
  5. package/lib/build-id-types.js.map +1 -0
  6. package/lib/client.d.ts +7 -0
  7. package/lib/client.js +6 -0
  8. package/lib/client.js.map +1 -1
  9. package/lib/connection.d.ts +2 -1
  10. package/lib/connection.js +1 -1
  11. package/lib/connection.js.map +1 -1
  12. package/lib/errors.d.ts +4 -4
  13. package/lib/errors.js +7 -7
  14. package/lib/errors.js.map +1 -1
  15. package/lib/helpers.d.ts +10 -0
  16. package/lib/helpers.js +31 -1
  17. package/lib/helpers.js.map +1 -1
  18. package/lib/index.d.ts +2 -0
  19. package/lib/index.js +1 -0
  20. package/lib/index.js.map +1 -1
  21. package/lib/schedule-client.d.ts +1 -1
  22. package/lib/schedule-client.js +36 -27
  23. package/lib/schedule-client.js.map +1 -1
  24. package/lib/schedule-helpers.js +7 -7
  25. package/lib/schedule-helpers.js.map +1 -1
  26. package/lib/schedule-types.d.ts +5 -5
  27. package/lib/task-queue-client.d.ts +122 -0
  28. package/lib/task-queue-client.js +229 -0
  29. package/lib/task-queue-client.js.map +1 -0
  30. package/lib/workflow-client.d.ts +1 -1
  31. package/lib/workflow-client.js +93 -81
  32. package/lib/workflow-client.js.map +1 -1
  33. package/package.json +4 -4
  34. package/src/async-completion-client.ts +29 -23
  35. package/src/build-id-types.ts +146 -0
  36. package/src/client.ts +13 -0
  37. package/src/connection.ts +4 -4
  38. package/src/errors.ts +7 -6
  39. package/src/helpers.ts +36 -1
  40. package/src/index.ts +11 -0
  41. package/src/schedule-client.ts +35 -27
  42. package/src/schedule-helpers.ts +8 -7
  43. package/src/schedule-types.ts +5 -5
  44. package/src/task-queue-client.ts +297 -0
  45. package/src/workflow-client.ts +98 -84
@@ -1,5 +1,5 @@
1
1
  import { checkExtends, Replace } from '@temporalio/common/lib/type-helpers';
2
- import { SearchAttributes, Workflow } from '@temporalio/common';
2
+ import { Duration, SearchAttributes, Workflow } from '@temporalio/common';
3
3
  import type { temporal } from '@temporalio/proto';
4
4
  import { WorkflowStartOptions } from './workflow-options';
5
5
 
@@ -46,7 +46,7 @@ export interface ScheduleOptions<A extends ScheduleOptionsAction = ScheduleOptio
46
46
  * @default 1 minute
47
47
  * @format number of milliseconds or {@link https://www.npmjs.com/package/ms | ms-formatted string}
48
48
  */
49
- catchupWindow?: number | string;
49
+ catchupWindow?: Duration;
50
50
 
51
51
  /**
52
52
  * When an Action times out or reaches the end of its Retry Policy, {@link pause}.
@@ -465,7 +465,7 @@ export interface ScheduleSpec {
465
465
  * @default 0
466
466
  * @format number of milliseconds or {@link https://www.npmjs.com/package/ms | ms-formatted string}
467
467
  */
468
- jitter?: number | string;
468
+ jitter?: Duration;
469
469
 
470
470
  /**
471
471
  * IANA timezone name, for example `US/Pacific`.
@@ -659,7 +659,7 @@ export interface IntervalSpec {
659
659
  *
660
660
  * @format number of milliseconds or {@link https://www.npmjs.com/package/ms | ms-formatted string}
661
661
  */
662
- every: number | string;
662
+ every: Duration;
663
663
 
664
664
  /**
665
665
  * Value is rounded to the nearest second.
@@ -667,7 +667,7 @@ export interface IntervalSpec {
667
667
  * @default 0
668
668
  * @format number of milliseconds or {@link https://www.npmjs.com/package/ms | ms-formatted string}
669
669
  */
670
- offset?: number | string;
670
+ offset?: Duration;
671
671
  }
672
672
 
673
673
  /**
@@ -0,0 +1,297 @@
1
+ import { status } from '@grpc/grpc-js';
2
+ import { filterNullAndUndefined } from '@temporalio/common/lib/internal-non-workflow';
3
+ import { assertNever, RequireAtLeastOne } from '@temporalio/common/lib/type-helpers';
4
+ import { temporal } from '@temporalio/proto';
5
+ import { BaseClient, BaseClientOptions, defaultBaseClientOptions, LoadedWithDefaults } from './base-client';
6
+ import { WorkflowService } from './types';
7
+ import { BuildIdOperation, versionSetsFromProto, WorkerBuildIdVersionSets } from './build-id-types';
8
+ import { isGrpcServiceError, ServiceError } from './errors';
9
+ import { rethrowKnownErrorTypes } from './helpers';
10
+
11
+ type IUpdateWorkerBuildIdCompatibilityRequest =
12
+ temporal.api.workflowservice.v1.IUpdateWorkerBuildIdCompatibilityRequest;
13
+ type GetWorkerTaskReachabilityResponse = temporal.api.workflowservice.v1.GetWorkerTaskReachabilityResponse;
14
+
15
+ /**
16
+ * @experimental
17
+ */
18
+ export type TaskQueueClientOptions = BaseClientOptions;
19
+
20
+ /**
21
+ * @experimental
22
+ */
23
+ export type LoadedTaskQueueClientOptions = LoadedWithDefaults<TaskQueueClientOptions>;
24
+
25
+ /**
26
+ * A stand-in for a Build Id for unversioned Workers
27
+ * @experimental
28
+ */
29
+ export const UnversionedBuildId = Symbol.for('__temporal_unversionedBuildId');
30
+ export type UnversionedBuildIdType = typeof UnversionedBuildId;
31
+
32
+ /**
33
+ * Client for starting Workflow executions and creating Workflow handles
34
+ *
35
+ * @experimental
36
+ */
37
+ export class TaskQueueClient extends BaseClient {
38
+ public readonly options: LoadedTaskQueueClientOptions;
39
+
40
+ constructor(options?: TaskQueueClientOptions) {
41
+ super(options);
42
+ this.options = {
43
+ ...defaultBaseClientOptions(),
44
+ ...filterNullAndUndefined(options ?? {}),
45
+ loadedDataConverter: this.dataConverter,
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Raw gRPC access to the Temporal service.
51
+ *
52
+ * **NOTE**: The namespace provided in {@link options} is **not** automatically set on requests
53
+ * using this service attribute.
54
+ */
55
+ get workflowService(): WorkflowService {
56
+ return this.connection.workflowService;
57
+ }
58
+
59
+ /**
60
+ * Used to add new Build Ids or otherwise update the relative compatibility of Build Ids as
61
+ * defined on a specific task queue for the Worker Versioning feature. For more on this feature,
62
+ * see https://docs.temporal.io/workers#worker-versioning
63
+ *
64
+ * @param taskQueue The task queue to make changes to.
65
+ * @param operation The operation to be performed.
66
+ */
67
+ public async updateBuildIdCompatibility(taskQueue: string, operation: BuildIdOperation): Promise<void> {
68
+ const request: IUpdateWorkerBuildIdCompatibilityRequest = {
69
+ namespace: this.options.namespace,
70
+ taskQueue,
71
+ };
72
+ switch (operation.operation) {
73
+ case 'addNewIdInNewDefaultSet':
74
+ request.addNewBuildIdInNewDefaultSet = operation.buildId;
75
+ break;
76
+ case 'addNewCompatibleVersion':
77
+ request.addNewCompatibleBuildId = {
78
+ newBuildId: operation.buildId,
79
+ existingCompatibleBuildId: operation.existingCompatibleBuildId,
80
+ };
81
+ break;
82
+ case 'promoteSetByBuildId':
83
+ request.promoteSetByBuildId = operation.buildId;
84
+ break;
85
+ case 'promoteBuildIdWithinSet':
86
+ request.promoteBuildIdWithinSet = operation.buildId;
87
+ break;
88
+ case 'mergeSets':
89
+ request.mergeSets = {
90
+ primarySetBuildId: operation.primaryBuildId,
91
+ secondarySetBuildId: operation.secondaryBuildId,
92
+ };
93
+ break;
94
+ default:
95
+ assertNever('Unknown build id update operation', operation);
96
+ }
97
+ try {
98
+ await this.workflowService.updateWorkerBuildIdCompatibility(request);
99
+ } catch (e) {
100
+ this.rethrowGrpcError(e, 'Unexpected error updating Build Id compatibility');
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Fetch the sets of compatible Build Ids for a given task queue.
106
+ *
107
+ * @param taskQueue The task queue to fetch the compatibility information for.
108
+ * @returns The sets of compatible Build Ids for the given task queue, or undefined if the queue
109
+ * has no Build Ids defined on it.
110
+ */
111
+ public async getBuildIdCompatability(taskQueue: string): Promise<WorkerBuildIdVersionSets | undefined> {
112
+ let resp;
113
+ try {
114
+ resp = await this.workflowService.getWorkerBuildIdCompatibility({
115
+ taskQueue,
116
+ namespace: this.options.namespace,
117
+ });
118
+ } catch (e) {
119
+ this.rethrowGrpcError(e, 'Unexpected error fetching Build Id compatibility');
120
+ }
121
+ if (resp.majorVersionSets == null || resp.majorVersionSets.length === 0) {
122
+ return undefined;
123
+ }
124
+ return versionSetsFromProto(resp);
125
+ }
126
+
127
+ /**
128
+ * Fetches task reachability to determine whether a worker may be retired. The request may specify
129
+ * task queues to query for or let the server fetch all task queues mapped to the given build IDs.
130
+ *
131
+ * When requesting a large number of task queues or all task queues associated with the given
132
+ * build ids in a namespace, all task queues will be listed in the response but some of them may
133
+ * not contain reachability information due to a server enforced limit. When reaching the limit,
134
+ * task queues that reachability information could not be retrieved for will be marked with a
135
+ * `NotFetched` entry in {@link BuildIdReachability.taskQueueReachability}. The caller may issue
136
+ * another call to get the reachability for those task queues.
137
+ */
138
+ public async getReachability(options: ReachabilityOptions): Promise<ReachabilityResponse> {
139
+ let resp;
140
+ const buildIds = options.buildIds?.map((bid) => {
141
+ if (bid === UnversionedBuildId) {
142
+ return '';
143
+ }
144
+ return bid;
145
+ });
146
+ try {
147
+ resp = await this.workflowService.getWorkerTaskReachability({
148
+ namespace: this.options.namespace,
149
+ taskQueues: options.taskQueues,
150
+ buildIds,
151
+ reachability: reachabilityTypeToProto(options.reachability),
152
+ });
153
+ } catch (e) {
154
+ this.rethrowGrpcError(e, 'Unexpected error fetching Build Id reachability');
155
+ }
156
+ return reachabilityResponseFromProto(resp);
157
+ }
158
+
159
+ protected rethrowGrpcError(err: unknown, fallbackMessage: string): never {
160
+ if (isGrpcServiceError(err)) {
161
+ rethrowKnownErrorTypes(err);
162
+ if (err.code === status.NOT_FOUND) {
163
+ throw new BuildIdNotFoundError(err.details ?? 'Build Id not found');
164
+ }
165
+ throw new ServiceError(fallbackMessage, { cause: err });
166
+ }
167
+ throw new ServiceError('Unexpected error while making gRPC request');
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Options for {@link TaskQueueClient.getReachability}
173
+ */
174
+ export type ReachabilityOptions = RequireAtLeastOne<BaseReachabilityOptions, 'buildIds' | 'taskQueues'>;
175
+
176
+ /**
177
+ * There are different types of reachability:
178
+ * - `NEW_WORKFLOWS`: The Build Id might be used by new workflows
179
+ * - `EXISTING_WORKFLOWS` The Build Id might be used by open workflows and/or closed workflows.
180
+ * - `OPEN_WORKFLOWS` The Build Id might be used by open workflows
181
+ * - `CLOSED_WORKFLOWS` The Build Id might be used by closed workflows
182
+ */
183
+ export type ReachabilityType = 'NEW_WORKFLOWS' | 'EXISTING_WORKFLOWS' | 'OPEN_WORKFLOWS' | 'CLOSED_WORKFLOWS';
184
+
185
+ /**
186
+ * See {@link ReachabilityOptions}
187
+ */
188
+ export interface BaseReachabilityOptions {
189
+ /**
190
+ * A list of build ids to query the reachability of. Currently, at least one Build Id must be
191
+ * specified, but this restriction may be lifted in the future.
192
+ */
193
+ buildIds: (string | UnversionedBuildIdType)[];
194
+ /**
195
+ * A list of task queues with Build Ids defined on them that the request is
196
+ * concerned with.
197
+ */
198
+ taskQueues?: string[];
199
+ /** The kind of reachability this request is concerned with. */
200
+ reachability?: ReachabilityType;
201
+ }
202
+
203
+ export interface ReachabilityResponse {
204
+ /** Maps Build Ids to their reachability information. */
205
+ buildIdReachability: Record<string | UnversionedBuildIdType, BuildIdReachability>;
206
+ }
207
+
208
+ export type ReachabilityTypeResponse = ReachabilityType | 'NOT_FETCHED';
209
+
210
+ export interface BuildIdReachability {
211
+ /**
212
+ * Maps Task Queue names to how the Build Id may be reachable from them. If they are not
213
+ * reachable, the map value will be an empty array.
214
+ */
215
+ taskQueueReachability: Record<string, ReachabilityTypeResponse[]>;
216
+ }
217
+
218
+ function reachabilityTypeToProto(type: ReachabilityType | undefined | null): temporal.api.enums.v1.TaskReachability {
219
+ switch (type) {
220
+ case null:
221
+ case undefined:
222
+ return temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_UNSPECIFIED;
223
+ case 'NEW_WORKFLOWS':
224
+ return temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_NEW_WORKFLOWS;
225
+ case 'EXISTING_WORKFLOWS':
226
+ return temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_EXISTING_WORKFLOWS;
227
+ case 'OPEN_WORKFLOWS':
228
+ return temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_OPEN_WORKFLOWS;
229
+ case 'CLOSED_WORKFLOWS':
230
+ return temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_CLOSED_WORKFLOWS;
231
+ default:
232
+ assertNever('Unknown Build Id reachability operation', type);
233
+ }
234
+ }
235
+
236
+ export function reachabilityResponseFromProto(resp: GetWorkerTaskReachabilityResponse): ReachabilityResponse {
237
+ return {
238
+ buildIdReachability: Object.fromEntries(
239
+ resp.buildIdReachability.map((bir) => {
240
+ const taskQueueReachability: Record<string, ReachabilityTypeResponse[]> = {};
241
+ if (bir.taskQueueReachability != null) {
242
+ for (const tqr of bir.taskQueueReachability) {
243
+ if (tqr.taskQueue == null) {
244
+ continue;
245
+ }
246
+ if (tqr.reachability == null) {
247
+ taskQueueReachability[tqr.taskQueue] = [];
248
+ continue;
249
+ }
250
+ taskQueueReachability[tqr.taskQueue] = tqr.reachability.map(reachabilityTypeFromProto);
251
+ }
252
+ }
253
+ let bid: string | UnversionedBuildIdType;
254
+ if (bir.buildId) {
255
+ bid = bir.buildId;
256
+ } else {
257
+ bid = UnversionedBuildId;
258
+ }
259
+ return [bid, { taskQueueReachability }];
260
+ })
261
+ ) as Record<string | UnversionedBuildIdType, BuildIdReachability>,
262
+ };
263
+ }
264
+
265
+ function reachabilityTypeFromProto(rtype: temporal.api.enums.v1.TaskReachability): ReachabilityTypeResponse {
266
+ switch (rtype) {
267
+ case temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_UNSPECIFIED:
268
+ return 'NOT_FETCHED';
269
+ case temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_NEW_WORKFLOWS:
270
+ return 'NEW_WORKFLOWS';
271
+ case temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_EXISTING_WORKFLOWS:
272
+ return 'EXISTING_WORKFLOWS';
273
+ case temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_OPEN_WORKFLOWS:
274
+ return 'OPEN_WORKFLOWS';
275
+ case temporal.api.enums.v1.TaskReachability.TASK_REACHABILITY_CLOSED_WORKFLOWS:
276
+ return 'CLOSED_WORKFLOWS';
277
+ default:
278
+ return assertNever('Unknown Build Id reachability operation', rtype);
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Thrown when one or more Build Ids are not found while using the {@link TaskQueueClient}.
284
+ *
285
+ * It could be because:
286
+ * - Id passed is incorrect
287
+ * - Build Id has been scavenged by the server.
288
+ *
289
+ * @experimental
290
+ */
291
+ export class BuildIdNotFoundError extends Error {
292
+ public readonly name: string = 'BuildIdNotFoundError';
293
+
294
+ constructor(message: string) {
295
+ super(message);
296
+ }
297
+ }
@@ -18,6 +18,7 @@ import {
18
18
  WorkflowExecutionAlreadyStartedError,
19
19
  WorkflowNotFoundError,
20
20
  WorkflowResultType,
21
+ extractWorkflowType,
21
22
  } from '@temporalio/common';
22
23
  import { composeInterceptors } from '@temporalio/common/lib/interceptors';
23
24
  import { History } from '@temporalio/common/lib/proto-utils';
@@ -30,7 +31,7 @@ import {
30
31
  filterNullAndUndefined,
31
32
  } from '@temporalio/common/lib/internal-non-workflow';
32
33
  import { temporal } from '@temporalio/proto';
33
- import { isServerErrorResponse, ServiceError, WorkflowContinuedAsNewError, WorkflowFailedError } from './errors';
34
+ import { ServiceError, WorkflowContinuedAsNewError, WorkflowFailedError, isGrpcServiceError } from './errors';
34
35
  import {
35
36
  WorkflowCancelInput,
36
37
  WorkflowClientInterceptor,
@@ -59,7 +60,7 @@ import {
59
60
  WorkflowSignalWithStartOptions,
60
61
  WorkflowStartOptions,
61
62
  } from './workflow-options';
62
- import { executionInfoFromRaw } from './helpers';
63
+ import { executionInfoFromRaw, rethrowKnownErrorTypes } from './helpers';
63
64
  import {
64
65
  BaseClient,
65
66
  BaseClientOptions,
@@ -345,7 +346,7 @@ export class WorkflowClient extends BaseClient {
345
346
  options: WithWorkflowArgs<T, WorkflowOptions>,
346
347
  interceptors: WorkflowClientInterceptor[]
347
348
  ): Promise<string> {
348
- const workflowType = typeof workflowTypeOrFunc === 'string' ? workflowTypeOrFunc : workflowTypeOrFunc.name;
349
+ const workflowType = extractWorkflowType(workflowTypeOrFunc);
349
350
  assertRequiredWorkflowOptions(options);
350
351
  const compiledOptions = compileWorkflowOptions(ensureArgs(options));
351
352
 
@@ -369,7 +370,7 @@ export class WorkflowClient extends BaseClient {
369
370
  options: WithWorkflowArgs<T, WorkflowSignalWithStartOptions<SA>>,
370
371
  interceptors: WorkflowClientInterceptor[]
371
372
  ): Promise<string> {
372
- const workflowType = typeof workflowTypeOrFunc === 'string' ? workflowTypeOrFunc : workflowTypeOrFunc.name;
373
+ const workflowType = extractWorkflowType(workflowTypeOrFunc);
373
374
  const { signal, signalArgs, ...rest } = options;
374
375
  assertRequiredWorkflowOptions(rest);
375
376
  const compiledOptions = compileWorkflowOptions(ensureArgs(rest));
@@ -486,7 +487,7 @@ export class WorkflowClient extends BaseClient {
486
487
  try {
487
488
  res = await this.workflowService.getWorkflowExecutionHistory(req);
488
489
  } catch (err) {
489
- this.rethrowGrpcError(err, { workflowId, runId }, 'Failed to get Workflow execution history');
490
+ this.rethrowGrpcError(err, 'Failed to get Workflow execution history', { workflowId, runId });
490
491
  }
491
492
  const events = res.history?.events;
492
493
 
@@ -580,18 +581,21 @@ export class WorkflowClient extends BaseClient {
580
581
  }
581
582
  }
582
583
 
583
- protected rethrowGrpcError(err: unknown, workflowExecution: WorkflowExecution, fallbackMessage: string): never {
584
- if (isServerErrorResponse(err)) {
584
+ protected rethrowGrpcError(err: unknown, fallbackMessage: string, workflowExecution?: WorkflowExecution): never {
585
+ if (isGrpcServiceError(err)) {
586
+ rethrowKnownErrorTypes(err);
587
+
585
588
  if (err.code === grpcStatus.NOT_FOUND) {
586
589
  throw new WorkflowNotFoundError(
587
590
  err.details ?? 'Workflow not found',
588
- workflowExecution.workflowId,
589
- workflowExecution.runId
591
+ workflowExecution?.workflowId ?? '',
592
+ workflowExecution?.runId
590
593
  );
591
594
  }
595
+
592
596
  throw new ServiceError(fallbackMessage, { cause: err });
593
597
  }
594
- throw new ServiceError('Unexpected error while making gRPC request');
598
+ throw new ServiceError('Unexpected error while making gRPC request', { cause: err as Error });
595
599
  }
596
600
 
597
601
  /**
@@ -600,23 +604,27 @@ export class WorkflowClient extends BaseClient {
600
604
  * Used as the final function of the query interceptor chain
601
605
  */
602
606
  protected async _queryWorkflowHandler(input: WorkflowQueryInput): Promise<unknown> {
607
+ const req: temporal.api.workflowservice.v1.IQueryWorkflowRequest = {
608
+ queryRejectCondition: input.queryRejectCondition,
609
+ namespace: this.options.namespace,
610
+ execution: input.workflowExecution,
611
+ query: {
612
+ queryType: input.queryType,
613
+ queryArgs: { payloads: await encodeToPayloads(this.dataConverter, ...input.args) },
614
+ header: { fields: input.headers },
615
+ },
616
+ };
603
617
  let response: temporal.api.workflowservice.v1.QueryWorkflowResponse;
604
618
  try {
605
- response = await this.workflowService.queryWorkflow({
606
- queryRejectCondition: input.queryRejectCondition,
607
- namespace: this.options.namespace,
608
- execution: input.workflowExecution,
609
- query: {
610
- queryType: input.queryType,
611
- queryArgs: { payloads: await encodeToPayloads(this.dataConverter, ...input.args) },
612
- header: { fields: input.headers },
613
- },
614
- });
619
+ response = await this.workflowService.queryWorkflow(req);
615
620
  } catch (err) {
616
- if (isServerErrorResponse(err) && err.code === grpcStatus.INVALID_ARGUMENT) {
617
- throw new QueryNotRegisteredError(err.message.replace(/^3 INVALID_ARGUMENT: /, ''), err.code);
621
+ if (isGrpcServiceError(err)) {
622
+ rethrowKnownErrorTypes(err);
623
+ if (err.code === grpcStatus.INVALID_ARGUMENT) {
624
+ throw new QueryNotRegisteredError(err.message.replace(/^3 INVALID_ARGUMENT: /, ''), err.code);
625
+ }
618
626
  }
619
- this.rethrowGrpcError(err, input.workflowExecution, 'Failed to query Workflow');
627
+ this.rethrowGrpcError(err, 'Failed to query Workflow', input.workflowExecution);
620
628
  }
621
629
  if (response.queryRejected) {
622
630
  if (response.queryRejected.status === undefined || response.queryRejected.status === null) {
@@ -637,19 +645,20 @@ export class WorkflowClient extends BaseClient {
637
645
  * Used as the final function of the signal interceptor chain
638
646
  */
639
647
  protected async _signalWorkflowHandler(input: WorkflowSignalInput): Promise<void> {
648
+ const req: temporal.api.workflowservice.v1.ISignalWorkflowExecutionRequest = {
649
+ identity: this.options.identity,
650
+ namespace: this.options.namespace,
651
+ workflowExecution: input.workflowExecution,
652
+ requestId: uuid4(),
653
+ // control is unused,
654
+ signalName: input.signalName,
655
+ header: { fields: input.headers },
656
+ input: { payloads: await encodeToPayloads(this.dataConverter, ...input.args) },
657
+ };
640
658
  try {
641
- await this.workflowService.signalWorkflowExecution({
642
- identity: this.options.identity,
643
- namespace: this.options.namespace,
644
- workflowExecution: input.workflowExecution,
645
- requestId: uuid4(),
646
- // control is unused,
647
- signalName: input.signalName,
648
- header: { fields: input.headers },
649
- input: { payloads: await encodeToPayloads(this.dataConverter, ...input.args) },
650
- });
659
+ await this.workflowService.signalWorkflowExecution(req);
651
660
  } catch (err) {
652
- this.rethrowGrpcError(err, input.workflowExecution, 'Failed to signal Workflow');
661
+ this.rethrowGrpcError(err, 'Failed to signal Workflow', input.workflowExecution);
653
662
  }
654
663
  }
655
664
 
@@ -661,37 +670,37 @@ export class WorkflowClient extends BaseClient {
661
670
  protected async _signalWithStartWorkflowHandler(input: WorkflowSignalWithStartInput): Promise<string> {
662
671
  const { identity } = this.options;
663
672
  const { options, workflowType, signalName, signalArgs, headers } = input;
673
+ const req: temporal.api.workflowservice.v1.ISignalWithStartWorkflowExecutionRequest = {
674
+ namespace: this.options.namespace,
675
+ identity,
676
+ requestId: uuid4(),
677
+ workflowId: options.workflowId,
678
+ workflowIdReusePolicy: options.workflowIdReusePolicy,
679
+ workflowType: { name: workflowType },
680
+ input: { payloads: await encodeToPayloads(this.dataConverter, ...options.args) },
681
+ signalName,
682
+ signalInput: { payloads: await encodeToPayloads(this.dataConverter, ...signalArgs) },
683
+ taskQueue: {
684
+ kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED,
685
+ name: options.taskQueue,
686
+ },
687
+ workflowExecutionTimeout: options.workflowExecutionTimeout,
688
+ workflowRunTimeout: options.workflowRunTimeout,
689
+ workflowTaskTimeout: options.workflowTaskTimeout,
690
+ retryPolicy: options.retry ? compileRetryPolicy(options.retry) : undefined,
691
+ memo: options.memo ? { fields: await encodeMapToPayloads(this.dataConverter, options.memo) } : undefined,
692
+ searchAttributes: options.searchAttributes
693
+ ? {
694
+ indexedFields: mapToPayloads(searchAttributePayloadConverter, options.searchAttributes),
695
+ }
696
+ : undefined,
697
+ cronSchedule: options.cronSchedule,
698
+ header: { fields: headers },
699
+ };
664
700
  try {
665
- const { runId } = await this.workflowService.signalWithStartWorkflowExecution({
666
- namespace: this.options.namespace,
667
- identity,
668
- requestId: uuid4(),
669
- workflowId: options.workflowId,
670
- workflowIdReusePolicy: options.workflowIdReusePolicy,
671
- workflowType: { name: workflowType },
672
- input: { payloads: await encodeToPayloads(this.dataConverter, ...options.args) },
673
- signalName,
674
- signalInput: { payloads: await encodeToPayloads(this.dataConverter, ...signalArgs) },
675
- taskQueue: {
676
- kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED,
677
- name: options.taskQueue,
678
- },
679
- workflowExecutionTimeout: options.workflowExecutionTimeout,
680
- workflowRunTimeout: options.workflowRunTimeout,
681
- workflowTaskTimeout: options.workflowTaskTimeout,
682
- retryPolicy: options.retry ? compileRetryPolicy(options.retry) : undefined,
683
- memo: options.memo ? { fields: await encodeMapToPayloads(this.dataConverter, options.memo) } : undefined,
684
- searchAttributes: options.searchAttributes
685
- ? {
686
- indexedFields: mapToPayloads(searchAttributePayloadConverter, options.searchAttributes),
687
- }
688
- : undefined,
689
- cronSchedule: options.cronSchedule,
690
- header: { fields: headers },
691
- });
692
- return runId;
701
+ return (await this.workflowService.signalWithStartWorkflowExecution(req)).runId;
693
702
  } catch (err) {
694
- this.rethrowGrpcError(err, { workflowId: options.workflowId }, 'Failed to signalWithStart Workflow');
703
+ this.rethrowGrpcError(err, 'Failed to signalWithStart Workflow', { workflowId: options.workflowId });
695
704
  }
696
705
  }
697
706
 
@@ -729,8 +738,7 @@ export class WorkflowClient extends BaseClient {
729
738
  header: { fields: headers },
730
739
  };
731
740
  try {
732
- const res = await this.workflowService.startWorkflowExecution(req);
733
- return res.runId;
741
+ return (await this.workflowService.startWorkflowExecution(req)).runId;
734
742
  } catch (err: any) {
735
743
  if (err.code === grpcStatus.ALREADY_EXISTS) {
736
744
  throw new WorkflowExecutionAlreadyStartedError(
@@ -739,7 +747,7 @@ export class WorkflowClient extends BaseClient {
739
747
  workflowType
740
748
  );
741
749
  }
742
- this.rethrowGrpcError(err, { workflowId: opts.workflowId }, 'Failed to start Workflow');
750
+ this.rethrowGrpcError(err, 'Failed to start Workflow', { workflowId: opts.workflowId });
743
751
  }
744
752
  }
745
753
 
@@ -751,18 +759,19 @@ export class WorkflowClient extends BaseClient {
751
759
  protected async _terminateWorkflowHandler(
752
760
  input: WorkflowTerminateInput
753
761
  ): Promise<TerminateWorkflowExecutionResponse> {
762
+ const req: temporal.api.workflowservice.v1.ITerminateWorkflowExecutionRequest = {
763
+ namespace: this.options.namespace,
764
+ identity: this.options.identity,
765
+ ...input,
766
+ details: {
767
+ payloads: input.details ? await encodeToPayloads(this.dataConverter, ...input.details) : undefined,
768
+ },
769
+ firstExecutionRunId: input.firstExecutionRunId,
770
+ };
754
771
  try {
755
- return await this.workflowService.terminateWorkflowExecution({
756
- namespace: this.options.namespace,
757
- identity: this.options.identity,
758
- ...input,
759
- details: {
760
- payloads: input.details ? await encodeToPayloads(this.dataConverter, ...input.details) : undefined,
761
- },
762
- firstExecutionRunId: input.firstExecutionRunId,
763
- });
772
+ return await this.workflowService.terminateWorkflowExecution(req);
764
773
  } catch (err) {
765
- this.rethrowGrpcError(err, input.workflowExecution, 'Failed to terminate Workflow');
774
+ this.rethrowGrpcError(err, 'Failed to terminate Workflow', input.workflowExecution);
766
775
  }
767
776
  }
768
777
 
@@ -781,7 +790,7 @@ export class WorkflowClient extends BaseClient {
781
790
  firstExecutionRunId: input.firstExecutionRunId,
782
791
  });
783
792
  } catch (err) {
784
- this.rethrowGrpcError(err, input.workflowExecution, 'Failed to cancel workflow');
793
+ this.rethrowGrpcError(err, 'Failed to cancel workflow', input.workflowExecution);
785
794
  }
786
795
  }
787
796
 
@@ -797,7 +806,7 @@ export class WorkflowClient extends BaseClient {
797
806
  execution: input.workflowExecution,
798
807
  });
799
808
  } catch (err) {
800
- this.rethrowGrpcError(err, input.workflowExecution, 'Failed to describe workflow');
809
+ this.rethrowGrpcError(err, 'Failed to describe workflow', input.workflowExecution);
801
810
  }
802
811
  }
803
812
 
@@ -925,12 +934,17 @@ export class WorkflowClient extends BaseClient {
925
934
  protected async *_list(options?: ListOptions): AsyncIterable<WorkflowExecutionInfo> {
926
935
  let nextPageToken: Uint8Array = Buffer.alloc(0);
927
936
  for (;;) {
928
- const response = await this.workflowService.listWorkflowExecutions({
929
- namespace: this.options.namespace,
930
- query: options?.query,
931
- nextPageToken,
932
- pageSize: options?.pageSize,
933
- });
937
+ let response: temporal.api.workflowservice.v1.ListWorkflowExecutionsResponse;
938
+ try {
939
+ response = await this.workflowService.listWorkflowExecutions({
940
+ namespace: this.options.namespace,
941
+ query: options?.query,
942
+ nextPageToken,
943
+ pageSize: options?.pageSize,
944
+ });
945
+ } catch (e) {
946
+ this.rethrowGrpcError(e, 'Failed to list workflows', undefined);
947
+ }
934
948
  // Not decoding memo payloads concurrently even though we could have to keep the lazy nature of this iterator.
935
949
  // Decoding is done for `memo` fields which tend to be small.
936
950
  // We might decide to change that based on user feedback.