@temporalio/common 0.15.0 → 0.17.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.
- package/lib/activity-options.d.ts +9 -5
- package/lib/activity-options.js +10 -2
- package/lib/activity-options.js.map +1 -1
- package/lib/converter/data-converter.d.ts +9 -2
- package/lib/converter/data-converter.js +14 -2
- package/lib/converter/data-converter.js.map +1 -1
- package/lib/converter/helpers.d.ts +1 -0
- package/lib/converter/helpers.js +33 -0
- package/lib/converter/helpers.js.map +1 -0
- package/lib/converter/payload-converter.d.ts +34 -2
- package/lib/converter/payload-converter.js +137 -1
- package/lib/converter/payload-converter.js.map +1 -1
- package/lib/converter/types.d.ts +2 -1
- package/lib/converter/types.js +2 -1
- package/lib/converter/types.js.map +1 -1
- package/lib/errors.d.ts +4 -0
- package/lib/errors.js +13 -1
- package/lib/errors.js.map +1 -1
- package/lib/failure.d.ts +20 -7
- package/lib/failure.js +32 -9
- package/lib/failure.js.map +1 -1
- package/lib/index.d.ts +3 -0
- package/lib/index.js +3 -0
- package/lib/index.js.map +1 -1
- package/lib/interceptors.d.ts +1 -1
- package/lib/interfaces.d.ts +4 -38
- package/lib/retry-policy.d.ts +43 -0
- package/lib/retry-policy.js +36 -0
- package/lib/retry-policy.js.map +1 -0
- package/lib/time.d.ts +2 -1
- package/lib/time.js +7 -1
- package/lib/time.js.map +1 -1
- package/lib/type-helpers.d.ts +5 -0
- package/lib/type-helpers.js +18 -0
- package/lib/type-helpers.js.map +1 -1
- package/lib/utils.d.ts +4 -0
- package/lib/utils.js +11 -0
- package/lib/utils.js.map +1 -0
- package/lib/workflow-options.d.ts +16 -21
- package/lib/workflow-options.js +13 -3
- package/lib/workflow-options.js.map +1 -1
- package/package.json +8 -4
- package/src/activity-options.ts +14 -5
- package/src/converter/data-converter.ts +29 -6
- package/src/converter/helpers.ts +38 -0
- package/src/converter/payload-converter.ts +155 -4
- package/src/converter/types.ts +3 -1
- package/src/errors.ts +18 -0
- package/src/failure.ts +34 -10
- package/src/index.ts +3 -0
- package/src/interceptors.ts +1 -1
- package/src/interfaces.ts +4 -40
- package/src/retry-policy.ts +73 -0
- package/src/time.ts +6 -1
- package/src/type-helpers.ts +23 -0
- package/src/utils.ts +6 -0
- package/src/workflow-options.ts +29 -34
- package/tsconfig.tsbuildinfo +1 -1
- package/CHANGELOG.md +0 -8
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
import { ValueError } from '../errors';
|
|
2
|
-
import {
|
|
1
|
+
import { ValueError, DataConverterError } from '../errors';
|
|
2
|
+
import {
|
|
3
|
+
u8,
|
|
4
|
+
str,
|
|
5
|
+
Payload,
|
|
6
|
+
encodingTypes,
|
|
7
|
+
encodingKeys,
|
|
8
|
+
METADATA_ENCODING_KEY,
|
|
9
|
+
METADATA_MESSAGE_TYPE_KEY,
|
|
10
|
+
} from './types';
|
|
11
|
+
import { isRecord, hasOwnProperty, hasOwnProperties } from '../type-helpers';
|
|
12
|
+
import { errorMessage } from '../errors';
|
|
13
|
+
import * as protoJsonSerializer from 'proto3-json-serializer';
|
|
14
|
+
import type { Root, Type, Namespace, Message } from 'protobufjs';
|
|
3
15
|
|
|
4
16
|
/**
|
|
5
17
|
* Used by the framework to serialize/deserialize method parameters that need to be sent over the
|
|
@@ -15,7 +27,7 @@ export interface PayloadConverter {
|
|
|
15
27
|
* Implements conversion of value to payload
|
|
16
28
|
*
|
|
17
29
|
* @param value JS value to convert.
|
|
18
|
-
* @return converted value
|
|
30
|
+
* @return converted value or `undefined` if unable to convert.
|
|
19
31
|
* @throws DataConverterException if conversion of the value passed as parameter failed for any
|
|
20
32
|
* reason.
|
|
21
33
|
*/
|
|
@@ -38,7 +50,7 @@ export interface PayloadConverter {
|
|
|
38
50
|
* Implements conversion of value to payload
|
|
39
51
|
*
|
|
40
52
|
* @param value JS value to convert.
|
|
41
|
-
* @return converted value
|
|
53
|
+
* @return converted value or `undefined` if unable to convert.
|
|
42
54
|
* @throws DataConverterException if conversion of the value passed as parameter failed for any
|
|
43
55
|
* reason.
|
|
44
56
|
*/
|
|
@@ -140,3 +152,142 @@ export class BinaryPayloadConverter extends AsyncFacadePayloadConverter {
|
|
|
140
152
|
return content.data as any;
|
|
141
153
|
}
|
|
142
154
|
}
|
|
155
|
+
|
|
156
|
+
abstract class ProtobufPayloadConverter extends AsyncFacadePayloadConverter {
|
|
157
|
+
protected readonly root: Root | undefined;
|
|
158
|
+
|
|
159
|
+
// Don't use type Root here because root.d.ts doesn't export Root, so users would have to type assert
|
|
160
|
+
constructor(root?: unknown) {
|
|
161
|
+
super();
|
|
162
|
+
|
|
163
|
+
if (root) {
|
|
164
|
+
if (!isRoot(root)) {
|
|
165
|
+
throw new TypeError('root must be an instance of a protobufjs Root');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this.root = root;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
protected validatePayload(content: Payload): { messageType: Type; data: Uint8Array } {
|
|
173
|
+
if (content.data === undefined || content.data === null) {
|
|
174
|
+
throw new ValueError('Got payload with no data');
|
|
175
|
+
}
|
|
176
|
+
if (!content.metadata || !(METADATA_MESSAGE_TYPE_KEY in content.metadata)) {
|
|
177
|
+
throw new ValueError(`Got protobuf payload without metadata.${METADATA_MESSAGE_TYPE_KEY}`);
|
|
178
|
+
}
|
|
179
|
+
if (!this.root) {
|
|
180
|
+
throw new DataConverterError('Unable to deserialize protobuf message without `root` being provided');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const messageTypeName = str(content.metadata[METADATA_MESSAGE_TYPE_KEY]);
|
|
184
|
+
let messageType;
|
|
185
|
+
try {
|
|
186
|
+
messageType = this.root.lookupType(messageTypeName);
|
|
187
|
+
} catch (e) {
|
|
188
|
+
if (errorMessage(e)?.includes('no such type')) {
|
|
189
|
+
throw new DataConverterError(
|
|
190
|
+
`Got a \`${messageTypeName}\` protobuf message but cannot find corresponding message class in \`root\``
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
throw e;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return { messageType, data: content.data };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
protected constructPayload({ messageTypeName, message }: { messageTypeName: string; message: Uint8Array }): Payload {
|
|
201
|
+
return {
|
|
202
|
+
metadata: {
|
|
203
|
+
[METADATA_ENCODING_KEY]: u8(this.encodingType),
|
|
204
|
+
[METADATA_MESSAGE_TYPE_KEY]: u8(messageTypeName),
|
|
205
|
+
},
|
|
206
|
+
data: message,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Converts between protobufjs Message instances and serialized Protobuf Payload
|
|
213
|
+
*/
|
|
214
|
+
export class ProtobufBinaryPayloadConverter extends ProtobufPayloadConverter {
|
|
215
|
+
public encodingType = encodingTypes.METADATA_ENCODING_PROTOBUF;
|
|
216
|
+
|
|
217
|
+
constructor(root?: unknown) {
|
|
218
|
+
super(root);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
public toDataSync(value: unknown): Payload | undefined {
|
|
222
|
+
if (!isProtobufMessage(value)) {
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return this.constructPayload({
|
|
227
|
+
messageTypeName: getNamespacedTypeName(value.$type),
|
|
228
|
+
message: value.$type.encode(value).finish(),
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
public fromDataSync<T>(content: Payload): T {
|
|
233
|
+
const { messageType, data } = this.validatePayload(content);
|
|
234
|
+
return messageType.decode(data) as unknown as T;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Converts between protobufjs Message instances and serialized JSON Payload
|
|
240
|
+
*/
|
|
241
|
+
export class ProtobufJsonPayloadConverter extends ProtobufPayloadConverter {
|
|
242
|
+
public encodingType = encodingTypes.METADATA_ENCODING_PROTOBUF_JSON;
|
|
243
|
+
|
|
244
|
+
constructor(root?: unknown) {
|
|
245
|
+
super(root);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
public toDataSync(value: unknown): Payload | undefined {
|
|
249
|
+
if (!isProtobufMessage(value)) {
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const jsonValue = protoJsonSerializer.toProto3JSON(value);
|
|
254
|
+
|
|
255
|
+
return this.constructPayload({
|
|
256
|
+
messageTypeName: getNamespacedTypeName(value.$type),
|
|
257
|
+
message: u8(JSON.stringify(jsonValue)),
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
public fromDataSync<T>(content: Payload): T {
|
|
262
|
+
const { messageType, data } = this.validatePayload(content);
|
|
263
|
+
return protoJsonSerializer.fromProto3JSON(messageType, JSON.parse(str(data))) as unknown as T;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function isProtobufType(type: unknown): type is Type {
|
|
268
|
+
return (
|
|
269
|
+
isRecord(type) &&
|
|
270
|
+
type.constructor.name === 'Type' &&
|
|
271
|
+
hasOwnProperties(type, ['parent', 'name', 'create', 'encode', 'decode']) &&
|
|
272
|
+
typeof type.name === 'string' &&
|
|
273
|
+
typeof type.create === 'function' &&
|
|
274
|
+
typeof type.encode === 'function' &&
|
|
275
|
+
typeof type.decode === 'function'
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function isProtobufMessage(value: unknown): value is Message {
|
|
280
|
+
return isRecord(value) && hasOwnProperty(value, '$type') && isProtobufType(value.$type);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function getNamespacedTypeName(node: Type | Namespace): string {
|
|
284
|
+
if (node.parent && !isRoot(node.parent)) {
|
|
285
|
+
return getNamespacedTypeName(node.parent) + '.' + node.name;
|
|
286
|
+
} else {
|
|
287
|
+
return node.name;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function isRoot(root: unknown): root is Root {
|
|
292
|
+
return isRecord(root) && root.constructor.name === 'Root';
|
|
293
|
+
}
|
package/src/converter/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as iface from '@temporalio/proto/lib/coresdk';
|
|
1
|
+
import type * as iface from '@temporalio/proto/lib/coresdk';
|
|
2
2
|
import { TextEncoder, TextDecoder } from '../encoding';
|
|
3
3
|
|
|
4
4
|
export type Payload = iface.coresdk.common.IPayload;
|
|
@@ -30,3 +30,5 @@ export const encodingKeys = {
|
|
|
30
30
|
METADATA_ENCODING_PROTOBUF_JSON: u8(encodingTypes.METADATA_ENCODING_PROTOBUF_JSON),
|
|
31
31
|
METADATA_ENCODING_PROTOBUF: u8(encodingTypes.METADATA_ENCODING_PROTOBUF),
|
|
32
32
|
} as const;
|
|
33
|
+
|
|
34
|
+
export const METADATA_MESSAGE_TYPE_KEY = 'messageType';
|
package/src/errors.ts
CHANGED
|
@@ -25,3 +25,21 @@ export function errorMessage(err: unknown): string | undefined {
|
|
|
25
25
|
}
|
|
26
26
|
return undefined;
|
|
27
27
|
}
|
|
28
|
+
|
|
29
|
+
interface ErrorWithCode {
|
|
30
|
+
code: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get error code from an Error or return undefined
|
|
34
|
+
*/
|
|
35
|
+
export function errorCode(error: unknown): string | undefined {
|
|
36
|
+
if (
|
|
37
|
+
typeof error === 'object' &&
|
|
38
|
+
(error as ErrorWithCode).code !== undefined &&
|
|
39
|
+
typeof (error as ErrorWithCode).code === 'string'
|
|
40
|
+
) {
|
|
41
|
+
return (error as ErrorWithCode).code;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
package/src/failure.ts
CHANGED
|
@@ -1,12 +1,36 @@
|
|
|
1
|
-
import { temporal } from '@temporalio/proto/lib/coresdk';
|
|
1
|
+
import type { temporal } from '@temporalio/proto/lib/coresdk';
|
|
2
2
|
import { DataConverter, arrayFromPayloads } from './converter/data-converter';
|
|
3
|
+
import { checkExtends } from './type-helpers';
|
|
3
4
|
|
|
4
5
|
export const FAILURE_SOURCE = 'TypeScriptSDK';
|
|
5
6
|
export type ProtoFailure = temporal.api.failure.v1.IFailure;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
-
|
|
7
|
+
// Avoid importing the proto implementation to reduce workflow bundle size
|
|
8
|
+
// Copied from temporal.api.enums.v1.TimeoutType
|
|
9
|
+
export enum TimeoutType {
|
|
10
|
+
TIMEOUT_TYPE_UNSPECIFIED = 0,
|
|
11
|
+
TIMEOUT_TYPE_START_TO_CLOSE = 1,
|
|
12
|
+
TIMEOUT_TYPE_SCHEDULE_TO_START = 2,
|
|
13
|
+
TIMEOUT_TYPE_SCHEDULE_TO_CLOSE = 3,
|
|
14
|
+
TIMEOUT_TYPE_HEARTBEAT = 4,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
checkExtends<temporal.api.enums.v1.TimeoutType, TimeoutType>();
|
|
18
|
+
|
|
19
|
+
// Avoid importing the proto implementation to reduce workflow bundle size
|
|
20
|
+
// Copied from temporal.api.enums.v1.RetryState
|
|
21
|
+
export enum RetryState {
|
|
22
|
+
RETRY_STATE_UNSPECIFIED = 0,
|
|
23
|
+
RETRY_STATE_IN_PROGRESS = 1,
|
|
24
|
+
RETRY_STATE_NON_RETRYABLE_FAILURE = 2,
|
|
25
|
+
RETRY_STATE_TIMEOUT = 3,
|
|
26
|
+
RETRY_STATE_MAXIMUM_ATTEMPTS_REACHED = 4,
|
|
27
|
+
RETRY_STATE_RETRY_POLICY_NOT_SET = 5,
|
|
28
|
+
RETRY_STATE_INTERNAL_SERVER_ERROR = 6,
|
|
29
|
+
RETRY_STATE_CANCEL_REQUESTED = 7,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
checkExtends<temporal.api.enums.v1.RetryState, RetryState>();
|
|
33
|
+
|
|
10
34
|
export type WorkflowExecution = temporal.api.common.v1.IWorkflowExecution;
|
|
11
35
|
|
|
12
36
|
/**
|
|
@@ -90,8 +114,8 @@ export class ApplicationFailure extends TemporalFailure {
|
|
|
90
114
|
* @param details optional details about the failure. They are serialized using the same approach
|
|
91
115
|
* as arguments and results.
|
|
92
116
|
*/
|
|
93
|
-
public static retryable(message: string | undefined, type
|
|
94
|
-
return new this(message, type, false, details);
|
|
117
|
+
public static retryable(message: string | undefined, type?: string, ...details: unknown[]): ApplicationFailure {
|
|
118
|
+
return new this(message, type ?? 'Error', false, details);
|
|
95
119
|
}
|
|
96
120
|
|
|
97
121
|
/**
|
|
@@ -105,8 +129,8 @@ export class ApplicationFailure extends TemporalFailure {
|
|
|
105
129
|
* @param details optional details about the failure. They are serialized using the same approach
|
|
106
130
|
* as arguments and results.
|
|
107
131
|
*/
|
|
108
|
-
public static nonRetryable(message: string | undefined, type
|
|
109
|
-
return new this(message, type, true, details);
|
|
132
|
+
public static nonRetryable(message: string | undefined, type?: string, ...details: unknown[]): ApplicationFailure {
|
|
133
|
+
return new this(message, type ?? 'Error', true, details);
|
|
110
134
|
}
|
|
111
135
|
}
|
|
112
136
|
|
|
@@ -198,7 +222,7 @@ export async function optionalErrorToOptionalFailure(
|
|
|
198
222
|
*/
|
|
199
223
|
const CUTTOFF_STACK_PATTERNS = [
|
|
200
224
|
/** Activity execution */
|
|
201
|
-
/\s+at Activity\.execute \(
|
|
225
|
+
/\s+at Activity\.execute \(.*[\\/]worker[\\/](?:src|lib)[\\/]activity\.[jt]s:\d+:\d+\)/,
|
|
202
226
|
/** Workflow activation */
|
|
203
227
|
/\s+at Activator\.\S+NextHandler \(webpack-internal:\/\/\/.*\/internals\.[jt]s:\d+:\d+\)/,
|
|
204
228
|
];
|
package/src/index.ts
CHANGED
|
@@ -5,11 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export * from './activity-options';
|
|
7
7
|
export * from './converter/data-converter';
|
|
8
|
+
export * from './converter/helpers';
|
|
8
9
|
export * from './errors';
|
|
9
10
|
export * from './failure';
|
|
10
11
|
export * from './interceptors';
|
|
11
12
|
export * from './interfaces';
|
|
13
|
+
export * from './retry-policy';
|
|
12
14
|
export * from './time';
|
|
13
15
|
export * from './tls-config';
|
|
14
16
|
export * from './workflow-handle';
|
|
15
17
|
export * from './workflow-options';
|
|
18
|
+
export * from './utils';
|
package/src/interceptors.ts
CHANGED
package/src/interfaces.ts
CHANGED
|
@@ -13,6 +13,8 @@ export type Workflow = (...args: any[]) => WorkflowReturnType;
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* An interface representing a Workflow signal definition, as returned from {@link defineSignal}
|
|
16
|
+
*
|
|
17
|
+
* @remarks `_Args` can be used for parameter type inference in handler functions and *WorkflowHandle methods.
|
|
16
18
|
*/
|
|
17
19
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
18
20
|
export interface SignalDefinition<_Args extends any[] = []> {
|
|
@@ -22,6 +24,8 @@ export interface SignalDefinition<_Args extends any[] = []> {
|
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* An interface representing a Workflow query definition as returned from {@link defineQuery}
|
|
27
|
+
*
|
|
28
|
+
* @remarks `_Args` and `_Ret` can be used for parameter type inference in handler functions and *WorkflowHandle methods.
|
|
25
29
|
*/
|
|
26
30
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
27
31
|
export interface QueryDefinition<_Ret, _Args extends any[] = []> {
|
|
@@ -31,43 +35,3 @@ export interface QueryDefinition<_Ret, _Args extends any[] = []> {
|
|
|
31
35
|
|
|
32
36
|
/** Get the "unwrapped" return type (without Promise) of the execute handler from Workflow type `W` */
|
|
33
37
|
export type WorkflowResultType<W extends Workflow> = ReturnType<W> extends Promise<infer R> ? R : never;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Defines options for activity retries
|
|
37
|
-
* @see {@link https://www.javadoc.io/doc/io.temporal/temporal-sdk/latest/io/temporal/common/RetryOptions.Builder.html | Java SDK definition}
|
|
38
|
-
*/
|
|
39
|
-
export interface RetryOptions {
|
|
40
|
-
/**
|
|
41
|
-
* Coefficient used to calculate the next retry interval.
|
|
42
|
-
* The next retry interval is previous interval multiplied by this coefficient.
|
|
43
|
-
* @minimum 1
|
|
44
|
-
* @default 2
|
|
45
|
-
*/
|
|
46
|
-
backoffCoefficient?: number;
|
|
47
|
-
/**
|
|
48
|
-
* Interval of the first retry.
|
|
49
|
-
* If coefficient is 1 then it is used for all retries
|
|
50
|
-
* @format {@link https://www.npmjs.com/package/ms | ms} formatted string or number of milliseconds
|
|
51
|
-
*/
|
|
52
|
-
initialInterval: string | number;
|
|
53
|
-
/**
|
|
54
|
-
* Maximum number of attempts. When exceeded the retries stop even if not expired yet.
|
|
55
|
-
* @minimum 1
|
|
56
|
-
* @default Infinity
|
|
57
|
-
*/
|
|
58
|
-
maximumAttempts?: number;
|
|
59
|
-
/**
|
|
60
|
-
* Maximum interval between retries.
|
|
61
|
-
* Exponential backoff leads to interval increase.
|
|
62
|
-
* This value is the cap of the increase.
|
|
63
|
-
*
|
|
64
|
-
* @default 100x of {@link initialInterval}
|
|
65
|
-
* @format {@link https://www.npmjs.com/package/ms | ms} formatted string or number of milliseconds
|
|
66
|
-
*/
|
|
67
|
-
maximumInterval?: string | number;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* List of application failures types to not retry.
|
|
71
|
-
*/
|
|
72
|
-
nonRetryableErrorTypes?: string[];
|
|
73
|
-
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { temporal } from '@temporalio/proto';
|
|
2
|
+
import { ValueError } from '.';
|
|
3
|
+
import { msOptionalToNumber, msOptionalToTs, msToNumber, msToTs } from './time';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options for retrying Workflows and Activities
|
|
7
|
+
*/
|
|
8
|
+
export interface RetryPolicy {
|
|
9
|
+
/**
|
|
10
|
+
* Coefficient used to calculate the next retry interval.
|
|
11
|
+
* The next retry interval is previous interval multiplied by this coefficient.
|
|
12
|
+
* @minimum 1
|
|
13
|
+
* @default 2
|
|
14
|
+
*/
|
|
15
|
+
backoffCoefficient?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Interval of the first retry.
|
|
18
|
+
* If coefficient is 1 then it is used for all retries
|
|
19
|
+
* @format {@link https://www.npmjs.com/package/ms | ms} formatted string or number of milliseconds
|
|
20
|
+
* @default 1 second
|
|
21
|
+
*/
|
|
22
|
+
initialInterval?: string | number;
|
|
23
|
+
/**
|
|
24
|
+
* Maximum number of attempts. When exceeded the retries stop even if not expired yet.
|
|
25
|
+
* @minimum 1
|
|
26
|
+
* @default Infinity
|
|
27
|
+
*/
|
|
28
|
+
maximumAttempts?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Maximum interval between retries.
|
|
31
|
+
* Exponential backoff leads to interval increase.
|
|
32
|
+
* This value is the cap of the increase.
|
|
33
|
+
*
|
|
34
|
+
* @default 100x of {@link initialInterval}
|
|
35
|
+
* @format {@link https://www.npmjs.com/package/ms | ms} formatted string or number of milliseconds
|
|
36
|
+
*/
|
|
37
|
+
maximumInterval?: string | number;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* List of application failures types to not retry.
|
|
41
|
+
*/
|
|
42
|
+
nonRetryableErrorTypes?: string[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Turns a TS RetryPolicy into a proto compatible RetryPolicy
|
|
47
|
+
*/
|
|
48
|
+
export function compileRetryPolicy(retryPolicy: RetryPolicy): temporal.api.common.v1.IRetryPolicy {
|
|
49
|
+
if (retryPolicy.backoffCoefficient != null && retryPolicy.backoffCoefficient <= 0) {
|
|
50
|
+
throw new ValueError('RetryPolicy.backoffCoefficient must be greater than 0');
|
|
51
|
+
}
|
|
52
|
+
if (retryPolicy.maximumAttempts != null && retryPolicy.maximumAttempts <= 0) {
|
|
53
|
+
throw new ValueError('RetryPolicy.maximumAttempts must be greater than 0');
|
|
54
|
+
}
|
|
55
|
+
const maximumInterval = msOptionalToNumber(retryPolicy.maximumInterval);
|
|
56
|
+
const initialInterval = msToNumber(retryPolicy.initialInterval ?? 1000);
|
|
57
|
+
if (maximumInterval === 0) {
|
|
58
|
+
throw new ValueError('RetryPolicy.maximumInterval cannot be 0');
|
|
59
|
+
}
|
|
60
|
+
if (initialInterval === 0) {
|
|
61
|
+
throw new ValueError('RetryPolicy.initialInterval cannot be 0');
|
|
62
|
+
}
|
|
63
|
+
if (maximumInterval != null && maximumInterval < initialInterval) {
|
|
64
|
+
throw new ValueError('RetryPolicy.maximumInterval cannot be less than its initialInterval');
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
maximumAttempts: retryPolicy.maximumAttempts,
|
|
68
|
+
initialInterval: msToTs(initialInterval),
|
|
69
|
+
maximumInterval: msOptionalToTs(maximumInterval),
|
|
70
|
+
backoffCoefficient: retryPolicy.backoffCoefficient,
|
|
71
|
+
nonRetryableErrorTypes: retryPolicy.nonRetryableErrorTypes,
|
|
72
|
+
};
|
|
73
|
+
}
|
package/src/time.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Long from 'long';
|
|
2
2
|
import ms from 'ms';
|
|
3
|
-
import * as iface from '@temporalio/proto/lib/coresdk';
|
|
3
|
+
import type * as iface from '@temporalio/proto/lib/coresdk';
|
|
4
4
|
import { ValueError } from './errors';
|
|
5
5
|
|
|
6
6
|
// NOTE: these are the same interface in JS
|
|
@@ -59,6 +59,11 @@ export function msOptionalToTs(str: string | number | undefined): Timestamp | un
|
|
|
59
59
|
return msNumberToTs(ms(str));
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
export function msOptionalToNumber(val: string | number | undefined): number | undefined {
|
|
63
|
+
if (val === undefined) return undefined;
|
|
64
|
+
return msToNumber(val);
|
|
65
|
+
}
|
|
66
|
+
|
|
62
67
|
export function msToNumber(val: string | number): number {
|
|
63
68
|
if (typeof val === 'number') {
|
|
64
69
|
return val;
|
package/src/type-helpers.ts
CHANGED
|
@@ -4,3 +4,26 @@ export type AnyFunc = (...args: any[]) => any;
|
|
|
4
4
|
export type OmitLast<T> = T extends [...infer REST, any] ? REST : never;
|
|
5
5
|
/** F with all arguments but the last */
|
|
6
6
|
export type OmitLastParam<F extends AnyFunc> = (...args: OmitLast<Parameters<F>>) => ReturnType<F>;
|
|
7
|
+
|
|
8
|
+
/** Verify that an type _Copy extends _Orig */
|
|
9
|
+
export function checkExtends<_Orig, _Copy extends _Orig>(): void {
|
|
10
|
+
// noop, just type check
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
14
|
+
return typeof value === 'object' && value !== null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function hasOwnProperty<X extends Record<string, unknown>, Y extends PropertyKey>(
|
|
18
|
+
record: X,
|
|
19
|
+
prop: Y
|
|
20
|
+
): record is X & Record<Y, unknown> {
|
|
21
|
+
return prop in record;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function hasOwnProperties<X extends Record<string, unknown>, Y extends PropertyKey>(
|
|
25
|
+
record: X,
|
|
26
|
+
props: Y[]
|
|
27
|
+
): record is X & Record<Y, unknown> {
|
|
28
|
+
return props.every((prop) => prop in record);
|
|
29
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper to prevent undefined and null values overriding defaults when merging maps
|
|
3
|
+
*/
|
|
4
|
+
export function filterNullAndUndefined<T extends Record<string, any>>(obj: T): T {
|
|
5
|
+
return Object.fromEntries(Object.entries(obj).filter(([_k, v]) => v != null)) as any;
|
|
6
|
+
}
|
package/src/workflow-options.ts
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import { coresdk, google } from '@temporalio/proto/lib/coresdk';
|
|
1
|
+
import type { coresdk, google } from '@temporalio/proto/lib/coresdk';
|
|
2
2
|
import { Workflow } from './interfaces';
|
|
3
|
+
import { RetryPolicy } from './retry-policy';
|
|
3
4
|
import { msToTs } from './time';
|
|
5
|
+
import { checkExtends } from './type-helpers';
|
|
6
|
+
|
|
7
|
+
// Avoid importing the proto implementation to reduce workflow bundle size
|
|
8
|
+
// Copied from coresdk.common.WorkflowIdReusePolicy
|
|
9
|
+
export enum WorkflowIdReusePolicy {
|
|
10
|
+
WORKFLOW_ID_REUSE_POLICY_UNSPECIFIED = 0,
|
|
11
|
+
WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE = 1,
|
|
12
|
+
WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY = 2,
|
|
13
|
+
WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE = 3,
|
|
14
|
+
}
|
|
4
15
|
|
|
5
|
-
|
|
6
|
-
export const WorkflowIdReusePolicy = coresdk.common.WorkflowIdReusePolicy;
|
|
7
|
-
export type RetryPolicy = coresdk.common.IRetryPolicy;
|
|
16
|
+
checkExtends<coresdk.common.WorkflowIdReusePolicy, WorkflowIdReusePolicy>();
|
|
8
17
|
|
|
9
18
|
export interface BaseWorkflowOptions {
|
|
10
|
-
/**
|
|
11
|
-
* Workflow id to use when starting. If not specified a UUID is generated. Note that it is
|
|
12
|
-
* dangerous as in case of client side retries no deduplication will happen based on the
|
|
13
|
-
* generated id. So prefer assigning business meaningful ids if possible.
|
|
14
|
-
*/
|
|
15
|
-
workflowId?: string;
|
|
16
|
-
|
|
17
19
|
/**
|
|
18
20
|
* Specifies server behavior if a completed workflow with the same id exists. Note that under no
|
|
19
21
|
* conditions Temporal allows two workflows with the same namespace and workflow id run
|
|
@@ -26,12 +28,12 @@ export interface BaseWorkflowOptions {
|
|
|
26
28
|
workflowIdReusePolicy?: WorkflowIdReusePolicy;
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
+
* Controls how a Workflow is retried.
|
|
32
|
+
*
|
|
33
|
+
* Workflows should typically use the system default, do not set this unless
|
|
34
|
+
* you know what you're doing.
|
|
31
35
|
*/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
retryPolicy?: coresdk.common.IRetryPolicy;
|
|
36
|
+
retry?: RetryPolicy;
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
39
|
* Optional cron schedule for Workflow. If a cron schedule is specified, the Workflow will run
|
|
@@ -100,29 +102,22 @@ export interface WorkflowDurationOptions {
|
|
|
100
102
|
workflowTaskTimeout?: string | number;
|
|
101
103
|
}
|
|
102
104
|
|
|
103
|
-
export type
|
|
105
|
+
export type CommonWorkflowOptions = BaseWorkflowOptions & WorkflowDurationOptions;
|
|
104
106
|
|
|
105
|
-
export type
|
|
106
|
-
|
|
107
|
+
export type WithCompiledWorkflowDurationOptions<T extends WorkflowDurationOptions> = Omit<
|
|
108
|
+
T,
|
|
109
|
+
'workflowExecutionTimeout' | 'workflowRunTimeout' | 'workflowTaskTimeout'
|
|
107
110
|
> & {
|
|
108
|
-
|
|
111
|
+
workflowExecutionTimeout?: google.protobuf.IDuration;
|
|
112
|
+
workflowRunTimeout?: google.protobuf.IDuration;
|
|
113
|
+
workflowTaskTimeout?: google.protobuf.IDuration;
|
|
109
114
|
};
|
|
110
115
|
|
|
111
|
-
export
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
workflowExecutionTimeout?: google.protobuf.IDuration;
|
|
116
|
-
workflowRunTimeout?: google.protobuf.IDuration;
|
|
117
|
-
workflowTaskTimeout?: google.protobuf.IDuration;
|
|
118
|
-
};
|
|
116
|
+
export function compileWorkflowOptions<T extends WorkflowDurationOptions>(
|
|
117
|
+
options: T
|
|
118
|
+
): WithCompiledWorkflowDurationOptions<T> {
|
|
119
|
+
const { workflowExecutionTimeout, workflowRunTimeout, workflowTaskTimeout, ...rest } = options;
|
|
119
120
|
|
|
120
|
-
export function compileWorkflowOptions<T extends Workflow>({
|
|
121
|
-
workflowExecutionTimeout,
|
|
122
|
-
workflowRunTimeout,
|
|
123
|
-
workflowTaskTimeout,
|
|
124
|
-
...rest
|
|
125
|
-
}: WorkflowOptionsWithDefaults<T>): CompiledWorkflowOptions<T> {
|
|
126
121
|
return {
|
|
127
122
|
...rest,
|
|
128
123
|
workflowExecutionTimeout: workflowExecutionTimeout ? msToTs(workflowExecutionTimeout) : undefined,
|