@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.
Files changed (59) hide show
  1. package/lib/activity-options.d.ts +9 -5
  2. package/lib/activity-options.js +10 -2
  3. package/lib/activity-options.js.map +1 -1
  4. package/lib/converter/data-converter.d.ts +9 -2
  5. package/lib/converter/data-converter.js +14 -2
  6. package/lib/converter/data-converter.js.map +1 -1
  7. package/lib/converter/helpers.d.ts +1 -0
  8. package/lib/converter/helpers.js +33 -0
  9. package/lib/converter/helpers.js.map +1 -0
  10. package/lib/converter/payload-converter.d.ts +34 -2
  11. package/lib/converter/payload-converter.js +137 -1
  12. package/lib/converter/payload-converter.js.map +1 -1
  13. package/lib/converter/types.d.ts +2 -1
  14. package/lib/converter/types.js +2 -1
  15. package/lib/converter/types.js.map +1 -1
  16. package/lib/errors.d.ts +4 -0
  17. package/lib/errors.js +13 -1
  18. package/lib/errors.js.map +1 -1
  19. package/lib/failure.d.ts +20 -7
  20. package/lib/failure.js +32 -9
  21. package/lib/failure.js.map +1 -1
  22. package/lib/index.d.ts +3 -0
  23. package/lib/index.js +3 -0
  24. package/lib/index.js.map +1 -1
  25. package/lib/interceptors.d.ts +1 -1
  26. package/lib/interfaces.d.ts +4 -38
  27. package/lib/retry-policy.d.ts +43 -0
  28. package/lib/retry-policy.js +36 -0
  29. package/lib/retry-policy.js.map +1 -0
  30. package/lib/time.d.ts +2 -1
  31. package/lib/time.js +7 -1
  32. package/lib/time.js.map +1 -1
  33. package/lib/type-helpers.d.ts +5 -0
  34. package/lib/type-helpers.js +18 -0
  35. package/lib/type-helpers.js.map +1 -1
  36. package/lib/utils.d.ts +4 -0
  37. package/lib/utils.js +11 -0
  38. package/lib/utils.js.map +1 -0
  39. package/lib/workflow-options.d.ts +16 -21
  40. package/lib/workflow-options.js +13 -3
  41. package/lib/workflow-options.js.map +1 -1
  42. package/package.json +8 -4
  43. package/src/activity-options.ts +14 -5
  44. package/src/converter/data-converter.ts +29 -6
  45. package/src/converter/helpers.ts +38 -0
  46. package/src/converter/payload-converter.ts +155 -4
  47. package/src/converter/types.ts +3 -1
  48. package/src/errors.ts +18 -0
  49. package/src/failure.ts +34 -10
  50. package/src/index.ts +3 -0
  51. package/src/interceptors.ts +1 -1
  52. package/src/interfaces.ts +4 -40
  53. package/src/retry-policy.ts +73 -0
  54. package/src/time.ts +6 -1
  55. package/src/type-helpers.ts +23 -0
  56. package/src/utils.ts +6 -0
  57. package/src/workflow-options.ts +29 -34
  58. package/tsconfig.tsbuildinfo +1 -1
  59. package/CHANGELOG.md +0 -8
@@ -1,5 +1,17 @@
1
- import { ValueError } from '../errors';
2
- import { u8, str, Payload, encodingTypes, encodingKeys, METADATA_ENCODING_KEY } from './types';
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
+ }
@@ -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
- export type TimeoutType = temporal.api.enums.v1.TimeoutType;
7
- export const TimeoutType = temporal.api.enums.v1.TimeoutType;
8
- export type RetryState = temporal.api.enums.v1.RetryState;
9
- export const RetryState = temporal.api.enums.v1.RetryState;
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: string, ...details: unknown[]): ApplicationFailure {
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: string, ...details: unknown[]): ApplicationFailure {
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 \(.*\/worker\/(?:src|lib)\/activity\.[jt]s:\d+:\d+\)/,
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';
@@ -1,4 +1,4 @@
1
- import { coresdk } from '@temporalio/proto/lib/coresdk';
1
+ import type { coresdk } from '@temporalio/proto/lib/coresdk';
2
2
  import { AnyFunc, OmitLastParam } from './type-helpers';
3
3
 
4
4
  /**
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;
@@ -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
+ }
@@ -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
- export type WorkflowIdReusePolicy = coresdk.common.WorkflowIdReusePolicy;
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
- * Task queue to use for Workflow tasks. It should match a task queue specified when creating a
30
- * `Worker` that hosts the Workflow code.
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
- taskQueue?: string;
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 WorkflowOptions = BaseWorkflowOptions & WorkflowDurationOptions;
105
+ export type CommonWorkflowOptions = BaseWorkflowOptions & WorkflowDurationOptions;
104
106
 
105
- export type RequiredWorkflowOptions<T extends Workflow = Workflow> = Required<
106
- Pick<BaseWorkflowOptions, 'workflowId' | 'taskQueue'>
107
+ export type WithCompiledWorkflowDurationOptions<T extends WorkflowDurationOptions> = Omit<
108
+ T,
109
+ 'workflowExecutionTimeout' | 'workflowRunTimeout' | 'workflowTaskTimeout'
107
110
  > & {
108
- args: Parameters<T>[];
111
+ workflowExecutionTimeout?: google.protobuf.IDuration;
112
+ workflowRunTimeout?: google.protobuf.IDuration;
113
+ workflowTaskTimeout?: google.protobuf.IDuration;
109
114
  };
110
115
 
111
- export type WorkflowOptionsWithDefaults<T extends Workflow = Workflow> = WorkflowOptions & RequiredWorkflowOptions<T>;
112
-
113
- export type CompiledWorkflowOptions<T extends Workflow = Workflow> = BaseWorkflowOptions &
114
- RequiredWorkflowOptions<T> & {
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,