@temporalio/client 1.4.3 → 1.5.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.
Files changed (57) hide show
  1. package/lib/async-completion-client.d.ts +5 -32
  2. package/lib/async-completion-client.js +6 -20
  3. package/lib/async-completion-client.js.map +1 -1
  4. package/lib/base-client.d.ts +53 -0
  5. package/lib/base-client.js +45 -0
  6. package/lib/base-client.js.map +1 -0
  7. package/lib/client.d.ts +12 -52
  8. package/lib/client.js +30 -49
  9. package/lib/client.js.map +1 -1
  10. package/lib/connection.d.ts +9 -9
  11. package/lib/connection.js +4 -3
  12. package/lib/connection.js.map +1 -1
  13. package/lib/errors.d.ts +0 -1
  14. package/lib/errors.js +1 -3
  15. package/lib/errors.js.map +1 -1
  16. package/lib/helpers.d.ts +3 -0
  17. package/lib/helpers.js +63 -0
  18. package/lib/helpers.js.map +1 -0
  19. package/lib/index.d.ts +2 -0
  20. package/lib/index.js +2 -0
  21. package/lib/index.js.map +1 -1
  22. package/lib/interceptors.d.ts +46 -10
  23. package/lib/iterators-utils.d.ts +31 -0
  24. package/lib/iterators-utils.js +80 -0
  25. package/lib/iterators-utils.js.map +1 -0
  26. package/lib/schedule-client.d.ts +175 -0
  27. package/lib/schedule-client.js +383 -0
  28. package/lib/schedule-client.js.map +1 -0
  29. package/lib/schedule-helpers.d.ts +20 -0
  30. package/lib/schedule-helpers.js +290 -0
  31. package/lib/schedule-helpers.js.map +1 -0
  32. package/lib/schedule-types.d.ts +691 -0
  33. package/lib/schedule-types.js +74 -0
  34. package/lib/schedule-types.js.map +1 -0
  35. package/lib/types.d.ts +8 -3
  36. package/lib/types.js.map +1 -1
  37. package/lib/workflow-client.d.ts +76 -59
  38. package/lib/workflow-client.js +87 -101
  39. package/lib/workflow-client.js.map +1 -1
  40. package/lib/workflow-options.d.ts +5 -1
  41. package/lib/workflow-options.js.map +1 -1
  42. package/package.json +7 -5
  43. package/src/async-completion-client.ts +16 -55
  44. package/src/base-client.ts +84 -0
  45. package/src/client.ts +41 -93
  46. package/src/connection.ts +12 -11
  47. package/src/errors.ts +0 -1
  48. package/src/helpers.ts +75 -0
  49. package/src/index.ts +2 -0
  50. package/src/interceptors.ts +54 -10
  51. package/src/iterators-utils.ts +116 -0
  52. package/src/schedule-client.ts +541 -0
  53. package/src/schedule-helpers.ts +414 -0
  54. package/src/schedule-types.ts +866 -0
  55. package/src/types.ts +12 -3
  56. package/src/workflow-client.ts +178 -180
  57. package/src/workflow-options.ts +12 -1
@@ -0,0 +1,414 @@
1
+ import Long from 'long'; // eslint-disable-line import/no-named-as-default
2
+ import {
3
+ compileRetryPolicy,
4
+ decompileRetryPolicy,
5
+ LoadedDataConverter,
6
+ mapFromPayloads,
7
+ mapToPayloads,
8
+ searchAttributePayloadConverter,
9
+ SearchAttributes,
10
+ } from '@temporalio/common';
11
+ import { Headers } from '@temporalio/common/lib/interceptors';
12
+ import {
13
+ decodeArrayFromPayloads,
14
+ decodeMapFromPayloads,
15
+ encodeMapToPayloads,
16
+ encodeToPayloads,
17
+ } from '@temporalio/common/lib/internal-non-workflow';
18
+ import { temporal } from '@temporalio/proto';
19
+ import {
20
+ msOptionalToTs,
21
+ msToTs,
22
+ optionalDateToTs,
23
+ optionalTsToDate,
24
+ optionalTsToMs,
25
+ tsToDate,
26
+ } from '@temporalio/common/lib/time';
27
+ import { RequireAtLeastOne } from '@temporalio/common/src/type-helpers';
28
+ import {
29
+ CalendarSpec,
30
+ CalendarSpecDescription,
31
+ CompiledScheduleOptions,
32
+ CompiledScheduleUpdateOptions,
33
+ Range,
34
+ ScheduleOptions,
35
+ ScheduleOverlapPolicy,
36
+ ScheduleUpdateOptions,
37
+ DayOfWeek,
38
+ DAYS_OF_WEEK,
39
+ Month,
40
+ MONTHS,
41
+ LooseRange,
42
+ ScheduleSpec,
43
+ CompiledScheduleAction,
44
+ ScheduleSpecDescription,
45
+ IntervalSpecDescription,
46
+ ScheduleDescriptionAction,
47
+ ScheduleExecutionActionResult,
48
+ ScheduleExecutionResult,
49
+ ScheduleExecutionStartWorkflowActionResult,
50
+ } from './schedule-types';
51
+
52
+ const [encodeSecond, decodeSecond] = makeCalendarSpecFieldCoders(
53
+ 'second',
54
+ (x: number) => (typeof x === 'number' && x >= 0 && x <= 59 ? x : undefined),
55
+ (x: number) => x,
56
+ [{ start: 0, end: 0, step: 0 }], // default to 0
57
+ [{ start: 0, end: 59, step: 1 }]
58
+ );
59
+
60
+ const [encodeMinute, decodeMinue] = makeCalendarSpecFieldCoders(
61
+ 'minute',
62
+ (x: number) => (typeof x === 'number' && x >= 0 && x <= 59 ? x : undefined),
63
+ (x: number) => x,
64
+ [{ start: 0, end: 0, step: 0 }], // default to 0
65
+ [{ start: 0, end: 59, step: 1 }]
66
+ );
67
+
68
+ const [encodeHour, decodeHour] = makeCalendarSpecFieldCoders(
69
+ 'hour',
70
+ (x: number) => (typeof x === 'number' && x >= 0 && x <= 59 ? x : undefined),
71
+ (x: number) => x,
72
+ [{ start: 0, end: 0, step: 0 }], // default to 0
73
+ [{ start: 0, end: 23, step: 1 }]
74
+ );
75
+
76
+ const [encodeDayOfMonth, decodeDayOfMonth] = makeCalendarSpecFieldCoders(
77
+ 'dayOfMonth',
78
+ (x: number) => (typeof x === 'number' && x >= 0 && x <= 6 ? x : undefined),
79
+ (x: number) => x,
80
+ [{ start: 1, end: 31, step: 1 }], // default to *
81
+ [{ start: 1, end: 31, step: 1 }]
82
+ );
83
+
84
+ const [encodeMonth, decodeMonth] = makeCalendarSpecFieldCoders(
85
+ 'month',
86
+ function monthNameToNumber(month: Month): number | undefined {
87
+ const index = MONTHS.indexOf(month);
88
+ return index >= 0 ? index + 1 : undefined;
89
+ },
90
+ (month: number) => MONTHS[month - 1],
91
+ [{ start: 1, end: 12, step: 1 }], // default to *
92
+ [{ start: 1, end: 12, step: 1 }]
93
+ );
94
+
95
+ const [encodeYear, decodeYear] = makeCalendarSpecFieldCoders(
96
+ 'year',
97
+ (x: number) => (typeof x === 'number' ? x : undefined),
98
+ (x: number) => x,
99
+ [], // default to *
100
+ [] // special case: * for years is encoded as no range at all
101
+ );
102
+
103
+ const [encodeDayOfWeek, decodeDayOfWeek] = makeCalendarSpecFieldCoders(
104
+ 'dayOfWeek',
105
+ function dayOfWeekNameToNumber(day: DayOfWeek): number | undefined {
106
+ const index = DAYS_OF_WEEK.indexOf(day);
107
+ return index >= 0 ? index : undefined;
108
+ },
109
+ (day: number) => DAYS_OF_WEEK[day],
110
+ [{ start: 0, end: 6, step: 1 }], // default to *
111
+ [{ start: 0, end: 6, step: 1 }]
112
+ );
113
+
114
+ function makeCalendarSpecFieldCoders<Unit>(
115
+ fieldName: string,
116
+ encodeValueFn: (x: Unit) => number | undefined,
117
+ decodeValueFn: (x: number) => Unit,
118
+ defaultValue: temporal.api.schedule.v1.IRange[],
119
+ matchAllValue: temporal.api.schedule.v1.IRange[]
120
+ ) {
121
+ function encoder(
122
+ input: LooseRange<Unit> | LooseRange<Unit>[] | '*' | undefined
123
+ ): temporal.api.schedule.v1.IRange[] | undefined {
124
+ if (input === undefined) return defaultValue;
125
+ if (input === '*') return matchAllValue;
126
+
127
+ return (Array.isArray(input) ? input : [input]).map((item) => {
128
+ if (typeof item === 'object' && (item as Range<Unit>).start !== undefined) {
129
+ const range = item as Range<Unit>;
130
+ const start = encodeValueFn(range.start);
131
+ if (start !== undefined) {
132
+ return {
133
+ start,
134
+ end: range.end !== undefined ? encodeValueFn(range.end) ?? start : 1,
135
+ step: typeof range.step === 'number' && range.step > 0 ? range.step : 1,
136
+ };
137
+ }
138
+ }
139
+ if (item !== undefined) {
140
+ const value = encodeValueFn(item as Unit);
141
+ if (value !== undefined) return { start: value, end: value, step: 1 };
142
+ }
143
+ throw new Error(`Invalid CalendarSpec component for field ${fieldName}: '${item}' of type '${typeof item}'`);
144
+ });
145
+ }
146
+
147
+ function decoder(input: temporal.api.schedule.v1.IRange[] | undefined | null): Range<Unit>[] {
148
+ if (!input) return [];
149
+ return (input as temporal.api.schedule.v1.Range[]).map((pb): Range<Unit> => {
150
+ const start = decodeValueFn(pb.start);
151
+ const end = pb.end > pb.start ? decodeValueFn(pb.end) ?? start : start;
152
+ const step = pb.step > 0 ? pb.step : 1;
153
+ return { start, end, step };
154
+ });
155
+ }
156
+
157
+ return [encoder, decoder] as const;
158
+ }
159
+
160
+ export function encodeOptionalStructuredCalendarSpecs(
161
+ input: CalendarSpec[] | null | undefined
162
+ ): temporal.api.schedule.v1.IStructuredCalendarSpec[] | undefined {
163
+ if (!input) return undefined;
164
+ return input.map((spec) => ({
165
+ second: encodeSecond(spec.second),
166
+ minute: encodeMinute(spec.minute),
167
+ hour: encodeHour(spec.hour),
168
+ dayOfMonth: encodeDayOfMonth(spec.dayOfMonth),
169
+ month: encodeMonth(spec.month),
170
+ year: encodeYear(spec.year),
171
+ dayOfWeek: encodeDayOfWeek(spec.dayOfWeek),
172
+ comment: spec.comment,
173
+ }));
174
+ }
175
+
176
+ export function decodeOptionalStructuredCalendarSpecs(
177
+ input: temporal.api.schedule.v1.IStructuredCalendarSpec[] | null | undefined
178
+ ): CalendarSpecDescription[] {
179
+ if (!input) return [];
180
+
181
+ return (input as temporal.api.schedule.v1.StructuredCalendarSpec[]).map(
182
+ (pb): CalendarSpecDescription => ({
183
+ second: decodeSecond(pb.second),
184
+ minute: decodeMinue(pb.minute),
185
+ hour: decodeHour(pb.hour),
186
+ dayOfMonth: decodeDayOfMonth(pb.dayOfMonth),
187
+ month: decodeMonth(pb.month),
188
+ year: decodeYear(pb.year),
189
+ dayOfWeek: decodeDayOfWeek(pb.dayOfWeek),
190
+ comment: pb.comment,
191
+ })
192
+ );
193
+ }
194
+
195
+ export function encodeOverlapPolicy(input: ScheduleOverlapPolicy): temporal.api.enums.v1.ScheduleOverlapPolicy {
196
+ return temporal.api.enums.v1.ScheduleOverlapPolicy[
197
+ `SCHEDULE_OVERLAP_POLICY_${ScheduleOverlapPolicy[input] as keyof typeof ScheduleOverlapPolicy}`
198
+ ];
199
+ }
200
+
201
+ export function decodeOverlapPolicy(input?: temporal.api.enums.v1.ScheduleOverlapPolicy | null): ScheduleOverlapPolicy {
202
+ if (!input) return ScheduleOverlapPolicy.UNSPECIFIED;
203
+ const encodedPolicyName = temporal.api.enums.v1.ScheduleOverlapPolicy[input];
204
+ const decodedPolicyName = encodedPolicyName.substring(
205
+ 'SCHEDULE_OVERLAP_POLICY_'.length
206
+ ) as keyof typeof ScheduleOverlapPolicy;
207
+ return ScheduleOverlapPolicy[decodedPolicyName];
208
+ }
209
+
210
+ export function compileScheduleOptions(options: ScheduleOptions): CompiledScheduleOptions {
211
+ const workflowTypeOrFunc = options.action.workflowType;
212
+ const workflowType = typeof workflowTypeOrFunc === 'string' ? workflowTypeOrFunc : workflowTypeOrFunc.name;
213
+ return {
214
+ ...options,
215
+ action: {
216
+ ...options.action,
217
+ workflowId: options.action.workflowId ?? `${options.scheduleId}-workflow`,
218
+ workflowType,
219
+ args: options.action.args ?? [],
220
+ },
221
+ };
222
+ }
223
+
224
+ export function compileUpdatedScheduleOptions(options: ScheduleUpdateOptions): CompiledScheduleUpdateOptions {
225
+ const workflowTypeOrFunc = options.action.workflowType;
226
+ const workflowType = typeof workflowTypeOrFunc === 'string' ? workflowTypeOrFunc : workflowTypeOrFunc.name;
227
+ return {
228
+ ...options,
229
+ action: {
230
+ ...options.action,
231
+ workflowType,
232
+ args: options.action.args ?? [],
233
+ },
234
+ };
235
+ }
236
+
237
+ export function encodeScheduleSpec(spec: ScheduleSpec): temporal.api.schedule.v1.IScheduleSpec {
238
+ return {
239
+ structuredCalendar: encodeOptionalStructuredCalendarSpecs(spec.calendars),
240
+ interval: spec.intervals?.map((interval) => ({
241
+ interval: msToTs(interval.every),
242
+ phase: msOptionalToTs(interval.offset),
243
+ })),
244
+ cronString: spec.cronExpressions,
245
+ excludeStructuredCalendar: encodeOptionalStructuredCalendarSpecs(spec.skip),
246
+ startTime: optionalDateToTs(spec.startAt),
247
+ endTime: optionalDateToTs(spec.endAt),
248
+ jitter: msOptionalToTs(spec.jitter),
249
+ timezoneName: spec.timezone,
250
+ };
251
+ }
252
+
253
+ export async function encodeScheduleAction(
254
+ dataConverter: LoadedDataConverter,
255
+ action: CompiledScheduleAction,
256
+ headers: Headers
257
+ ): Promise<temporal.api.schedule.v1.IScheduleAction> {
258
+ return {
259
+ startWorkflow: {
260
+ workflowId: action.workflowId,
261
+ workflowType: {
262
+ name: action.workflowType,
263
+ },
264
+ input: { payloads: await encodeToPayloads(dataConverter, ...action.args) },
265
+ taskQueue: {
266
+ kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED,
267
+ name: action.taskQueue,
268
+ },
269
+ workflowExecutionTimeout: msOptionalToTs(action.workflowExecutionTimeout),
270
+ workflowRunTimeout: msOptionalToTs(action.workflowRunTimeout),
271
+ workflowTaskTimeout: msOptionalToTs(action.workflowTaskTimeout),
272
+ retryPolicy: action.retry ? compileRetryPolicy(action.retry) : undefined,
273
+ memo: action.memo ? { fields: await encodeMapToPayloads(dataConverter, action.memo) } : undefined,
274
+ searchAttributes: action.searchAttributes
275
+ ? {
276
+ indexedFields: mapToPayloads(searchAttributePayloadConverter, action.searchAttributes),
277
+ }
278
+ : undefined,
279
+ header: { fields: headers },
280
+ },
281
+ };
282
+ }
283
+
284
+ export function encodeSchedulePolicies(
285
+ policies?: ScheduleOptions['policies']
286
+ ): temporal.api.schedule.v1.ISchedulePolicies {
287
+ return {
288
+ catchupWindow: msOptionalToTs(policies?.catchupWindow),
289
+ overlapPolicy: policies?.overlap ? encodeOverlapPolicy(policies.overlap) : undefined,
290
+ pauseOnFailure: policies?.pauseOnFailure,
291
+ };
292
+ }
293
+
294
+ export function encodeScheduleState(state?: ScheduleOptions['state']): temporal.api.schedule.v1.IScheduleState {
295
+ return {
296
+ paused: state?.paused,
297
+ notes: state?.note,
298
+ limitedActions: state?.remainingActions !== undefined,
299
+ remainingActions: state?.remainingActions ? Long.fromNumber(state?.remainingActions) : undefined,
300
+ };
301
+ }
302
+
303
+ export function decodeScheduleSpec(
304
+ pb: temporal.api.schedule.v1.IScheduleSpec
305
+ ): RequireAtLeastOne<ScheduleSpecDescription, 'calendars' | 'intervals'> {
306
+ // Note: the server will have compiled calendar and cron_string fields into
307
+ // structured_calendar (and maybe interval and timezone_name), so at this
308
+ // point, we'll see only structured_calendar, interval, etc.
309
+ return {
310
+ calendars: decodeOptionalStructuredCalendarSpecs(pb.structuredCalendar),
311
+ intervals: (pb.interval ?? []).map(
312
+ (x) =>
313
+ <IntervalSpecDescription>{
314
+ every: optionalTsToMs(x.interval),
315
+ offset: optionalTsToMs(x.phase),
316
+ }
317
+ ),
318
+ skip: decodeOptionalStructuredCalendarSpecs(pb.excludeStructuredCalendar),
319
+ startAt: optionalTsToDate(pb.startTime),
320
+ endAt: optionalTsToDate(pb.endTime),
321
+ jitter: optionalTsToMs(pb.jitter),
322
+ timezone: pb.timezoneName ?? undefined,
323
+ };
324
+ }
325
+
326
+ export async function decodeScheduleAction(
327
+ dataConverter: LoadedDataConverter,
328
+ pb: temporal.api.schedule.v1.IScheduleAction
329
+ ): Promise<ScheduleDescriptionAction> {
330
+ if (pb.startWorkflow) {
331
+ return {
332
+ type: 'startWorkflow',
333
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
334
+ workflowId: pb.startWorkflow.workflowId!,
335
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
336
+ workflowType: pb.startWorkflow.workflowType!.name!,
337
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
338
+ taskQueue: pb.startWorkflow.taskQueue!.name!,
339
+ args: await decodeArrayFromPayloads(dataConverter, pb.startWorkflow.input?.payloads),
340
+ memo: await decodeMapFromPayloads(dataConverter, pb.startWorkflow.memo?.fields),
341
+ retry: decompileRetryPolicy(pb.startWorkflow.retryPolicy),
342
+ searchAttributes: Object.fromEntries(
343
+ Object.entries(
344
+ mapFromPayloads(
345
+ searchAttributePayloadConverter,
346
+ pb.startWorkflow.searchAttributes?.indexedFields ?? {}
347
+ ) as SearchAttributes
348
+ )
349
+ ),
350
+ workflowExecutionTimeout: optionalTsToMs(pb.startWorkflow.workflowExecutionTimeout),
351
+ workflowRunTimeout: optionalTsToMs(pb.startWorkflow.workflowRunTimeout),
352
+ workflowTaskTimeout: optionalTsToMs(pb.startWorkflow.workflowTaskTimeout),
353
+ };
354
+ }
355
+ throw new Error('Unsupported schedule action');
356
+ }
357
+
358
+ export function decodeSearchAttributes(
359
+ pb: temporal.api.common.v1.ISearchAttributes | undefined | null
360
+ ): SearchAttributes {
361
+ if (!pb?.indexedFields) return {};
362
+ return Object.fromEntries(
363
+ Object.entries(mapFromPayloads(searchAttributePayloadConverter, pb.indexedFields) as SearchAttributes).filter(
364
+ ([_, v]) => v && v.length > 0
365
+ ) // Filter out empty arrays returned by pre 1.18 servers
366
+ );
367
+ }
368
+
369
+ export function decodeScheduleRunningActions(
370
+ pb?: temporal.api.common.v1.IWorkflowExecution[] | null
371
+ ): ScheduleExecutionStartWorkflowActionResult[] {
372
+ if (!pb) return [];
373
+ return pb.map(
374
+ (x): ScheduleExecutionStartWorkflowActionResult => ({
375
+ type: 'startWorkflow',
376
+ workflow: {
377
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
378
+ workflowId: x.workflowId!,
379
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
380
+ firstExecutionRunId: x.runId!,
381
+ },
382
+ })
383
+ );
384
+ }
385
+
386
+ export function decodeScheduleRecentActions(
387
+ pb?: temporal.api.schedule.v1.IScheduleActionResult[] | null
388
+ ): ScheduleExecutionResult[] {
389
+ if (!pb) return [];
390
+ return (pb as Required<temporal.api.schedule.v1.IScheduleActionResult>[]).map(
391
+ (executionResult): ScheduleExecutionResult => {
392
+ let action: ScheduleExecutionActionResult | undefined;
393
+ if (executionResult.startWorkflowResult) {
394
+ action = {
395
+ type: 'startWorkflow',
396
+ workflow: {
397
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
398
+ workflowId: executionResult.startWorkflowResult!.workflowId!,
399
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
400
+ firstExecutionRunId: executionResult.startWorkflowResult!.runId!,
401
+ },
402
+ };
403
+ } else throw new Error('Unsupported schedule action');
404
+
405
+ return {
406
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
407
+ scheduledAt: tsToDate(executionResult.scheduleTime!),
408
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
409
+ takenAt: tsToDate(executionResult.actualTime!),
410
+ action,
411
+ };
412
+ }
413
+ );
414
+ }