@temporalio/common 1.11.2 → 1.11.4
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 +8 -6
- package/lib/activity-options.js +13 -12
- package/lib/activity-options.js.map +1 -1
- package/lib/converter/failure-converter.js +8 -6
- package/lib/converter/failure-converter.js.map +1 -1
- package/lib/converter/payload-converter.js +7 -7
- package/lib/converter/payload-converter.js.map +1 -1
- package/lib/deprecated-time.js +9 -10
- package/lib/deprecated-time.js.map +1 -1
- package/lib/encoding.js +3 -3
- package/lib/encoding.js.map +1 -1
- package/lib/failure.d.ts +51 -23
- package/lib/failure.js +65 -32
- package/lib/failure.js.map +1 -1
- package/lib/index.js +4 -5
- package/lib/index.js.map +1 -1
- package/lib/interceptors.js +1 -2
- package/lib/interceptors.js.map +1 -1
- package/lib/interfaces.d.ts +5 -4
- package/lib/interfaces.js +4 -5
- package/lib/interfaces.js.map +1 -1
- package/lib/internal-non-workflow/codec-helpers.js +22 -23
- package/lib/internal-non-workflow/codec-helpers.js.map +1 -1
- package/lib/internal-non-workflow/data-converter-helpers.js +3 -4
- package/lib/internal-non-workflow/data-converter-helpers.js.map +1 -1
- package/lib/internal-non-workflow/parse-host-uri.d.ts +5 -4
- package/lib/internal-non-workflow/parse-host-uri.js +9 -9
- package/lib/internal-non-workflow/parse-host-uri.js.map +1 -1
- package/lib/internal-non-workflow/proxy-config.js +1 -2
- package/lib/internal-non-workflow/proxy-config.js.map +1 -1
- package/lib/internal-non-workflow/tls-config.d.ts +2 -3
- package/lib/internal-non-workflow/tls-config.js +1 -2
- package/lib/internal-non-workflow/tls-config.js.map +1 -1
- package/lib/internal-non-workflow/utils.js +1 -2
- package/lib/internal-non-workflow/utils.js.map +1 -1
- package/lib/internal-workflow/enums-helpers.d.ts +170 -0
- package/lib/internal-workflow/enums-helpers.js +172 -0
- package/lib/internal-workflow/enums-helpers.js.map +1 -0
- package/lib/internal-workflow/index.d.ts +1 -0
- package/lib/internal-workflow/index.js +18 -0
- package/lib/internal-workflow/index.js.map +1 -0
- package/lib/proto-utils.js +4 -5
- package/lib/proto-utils.js.map +1 -1
- package/lib/retry-policy.js +2 -3
- package/lib/retry-policy.js.map +1 -1
- package/lib/time.js +12 -13
- package/lib/time.js.map +1 -1
- package/lib/type-helpers.d.ts +18 -0
- package/lib/type-helpers.js +12 -13
- package/lib/type-helpers.js.map +1 -1
- package/lib/versioning-intent-enum.js +2 -2
- package/lib/versioning-intent-enum.js.map +1 -1
- package/lib/workflow-options.d.ts +69 -17
- package/lib/workflow-options.js +64 -23
- package/lib/workflow-options.js.map +1 -1
- package/package.json +3 -3
- package/src/activity-options.ts +24 -13
- package/src/converter/failure-converter.ts +10 -6
- package/src/converter/payload-converter.ts +1 -1
- package/src/failure.ts +95 -28
- package/src/interfaces.ts +5 -4
- package/src/internal-non-workflow/data-converter-helpers.ts +1 -1
- package/src/internal-non-workflow/parse-host-uri.ts +6 -5
- package/src/internal-non-workflow/tls-config.ts +2 -2
- package/src/internal-workflow/enums-helpers.ts +301 -0
- package/src/internal-workflow/index.ts +1 -0
- package/src/type-helpers.ts +79 -1
- package/src/workflow-options.ts +110 -23
|
@@ -3,14 +3,16 @@ import {
|
|
|
3
3
|
ApplicationFailure,
|
|
4
4
|
CancelledFailure,
|
|
5
5
|
ChildWorkflowFailure,
|
|
6
|
+
decodeRetryState,
|
|
7
|
+
decodeTimeoutType,
|
|
8
|
+
encodeRetryState,
|
|
9
|
+
encodeTimeoutType,
|
|
6
10
|
FAILURE_SOURCE,
|
|
7
11
|
ProtoFailure,
|
|
8
|
-
RetryState,
|
|
9
12
|
ServerFailure,
|
|
10
13
|
TemporalFailure,
|
|
11
14
|
TerminatedFailure,
|
|
12
15
|
TimeoutFailure,
|
|
13
|
-
TimeoutType,
|
|
14
16
|
} from '../failure';
|
|
15
17
|
import { isError } from '../type-helpers';
|
|
16
18
|
import { msOptionalToTs } from '../time';
|
|
@@ -139,7 +141,7 @@ export class DefaultFailureConverter implements FailureConverter {
|
|
|
139
141
|
return new TimeoutFailure(
|
|
140
142
|
failure.message ?? undefined,
|
|
141
143
|
fromPayloadsAtIndex(payloadConverter, 0, failure.timeoutFailureInfo.lastHeartbeatDetails?.payloads),
|
|
142
|
-
failure.timeoutFailureInfo.timeoutType
|
|
144
|
+
decodeTimeoutType(failure.timeoutFailureInfo.timeoutType)
|
|
143
145
|
);
|
|
144
146
|
}
|
|
145
147
|
if (failure.terminatedFailureInfo) {
|
|
@@ -173,7 +175,7 @@ export class DefaultFailureConverter implements FailureConverter {
|
|
|
173
175
|
namespace ?? undefined,
|
|
174
176
|
workflowExecution,
|
|
175
177
|
workflowType.name,
|
|
176
|
-
retryState
|
|
178
|
+
decodeRetryState(retryState),
|
|
177
179
|
this.optionalFailureToOptionalError(failure.cause, payloadConverter)
|
|
178
180
|
);
|
|
179
181
|
}
|
|
@@ -185,7 +187,7 @@ export class DefaultFailureConverter implements FailureConverter {
|
|
|
185
187
|
failure.message ?? undefined,
|
|
186
188
|
failure.activityFailureInfo.activityType.name,
|
|
187
189
|
failure.activityFailureInfo.activityId ?? undefined,
|
|
188
|
-
failure.activityFailureInfo.retryState
|
|
190
|
+
decodeRetryState(failure.activityFailureInfo.retryState),
|
|
189
191
|
failure.activityFailureInfo.identity ?? undefined,
|
|
190
192
|
this.optionalFailureToOptionalError(failure.cause, payloadConverter)
|
|
191
193
|
);
|
|
@@ -244,6 +246,7 @@ export class DefaultFailureConverter implements FailureConverter {
|
|
|
244
246
|
...base,
|
|
245
247
|
activityFailureInfo: {
|
|
246
248
|
...err,
|
|
249
|
+
retryState: encodeRetryState(err.retryState),
|
|
247
250
|
activityType: { name: err.activityType },
|
|
248
251
|
},
|
|
249
252
|
};
|
|
@@ -253,6 +256,7 @@ export class DefaultFailureConverter implements FailureConverter {
|
|
|
253
256
|
...base,
|
|
254
257
|
childWorkflowExecutionFailureInfo: {
|
|
255
258
|
...err,
|
|
259
|
+
retryState: encodeRetryState(err.retryState),
|
|
256
260
|
workflowExecution: err.execution,
|
|
257
261
|
workflowType: { name: err.workflowType },
|
|
258
262
|
},
|
|
@@ -287,7 +291,7 @@ export class DefaultFailureConverter implements FailureConverter {
|
|
|
287
291
|
return {
|
|
288
292
|
...base,
|
|
289
293
|
timeoutFailureInfo: {
|
|
290
|
-
timeoutType: err.timeoutType,
|
|
294
|
+
timeoutType: encodeTimeoutType(err.timeoutType),
|
|
291
295
|
lastHeartbeatDetails: err.lastHeartbeatDetails
|
|
292
296
|
? { payloads: toPayloads(payloadConverter, err.lastHeartbeatDetails) }
|
|
293
297
|
: undefined,
|
package/src/failure.ts
CHANGED
|
@@ -1,38 +1,105 @@
|
|
|
1
1
|
import type { temporal } from '@temporalio/proto';
|
|
2
|
-
import {
|
|
2
|
+
import { errorMessage, isRecord, SymbolBasedInstanceOfError } from './type-helpers';
|
|
3
3
|
import { Duration } from './time';
|
|
4
|
+
import { makeProtoEnumConverters } from './internal-workflow';
|
|
4
5
|
|
|
5
6
|
export const FAILURE_SOURCE = 'TypeScriptSDK';
|
|
6
7
|
export type ProtoFailure = temporal.api.failure.v1.IFailure;
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
TIMEOUT_TYPE_SCHEDULE_TO_START = 2,
|
|
14
|
-
TIMEOUT_TYPE_SCHEDULE_TO_CLOSE = 3,
|
|
15
|
-
TIMEOUT_TYPE_HEARTBEAT = 4,
|
|
16
|
-
}
|
|
9
|
+
export const TimeoutType = {
|
|
10
|
+
START_TO_CLOSE: 'START_TO_CLOSE',
|
|
11
|
+
SCHEDULE_TO_START: 'SCHEDULE_TO_START',
|
|
12
|
+
SCHEDULE_TO_CLOSE: 'SCHEDULE_TO_CLOSE',
|
|
13
|
+
HEARTBEAT: 'HEARTBEAT',
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
15
|
+
/** @deprecated Use {@link START_TO_CLOSE} instead. */
|
|
16
|
+
TIMEOUT_TYPE_START_TO_CLOSE: 'START_TO_CLOSE', // eslint-disable-line deprecation/deprecation
|
|
17
|
+
|
|
18
|
+
/** @deprecated Use {@link SCHEDULE_TO_START} instead. */
|
|
19
|
+
TIMEOUT_TYPE_SCHEDULE_TO_START: 'SCHEDULE_TO_START', // eslint-disable-line deprecation/deprecation
|
|
20
|
+
|
|
21
|
+
/** @deprecated Use {@link SCHEDULE_TO_CLOSE} instead. */
|
|
22
|
+
TIMEOUT_TYPE_SCHEDULE_TO_CLOSE: 'SCHEDULE_TO_CLOSE', // eslint-disable-line deprecation/deprecation
|
|
23
|
+
|
|
24
|
+
/** @deprecated Use {@link HEARTBEAT} instead. */
|
|
25
|
+
TIMEOUT_TYPE_HEARTBEAT: 'HEARTBEAT', // eslint-disable-line deprecation/deprecation
|
|
26
|
+
|
|
27
|
+
/** @deprecated Use `undefined` instead. */
|
|
28
|
+
TIMEOUT_TYPE_UNSPECIFIED: undefined, // eslint-disable-line deprecation/deprecation
|
|
29
|
+
} as const;
|
|
30
|
+
export type TimeoutType = (typeof TimeoutType)[keyof typeof TimeoutType];
|
|
31
|
+
|
|
32
|
+
export const [encodeTimeoutType, decodeTimeoutType] = makeProtoEnumConverters<
|
|
33
|
+
temporal.api.enums.v1.TimeoutType,
|
|
34
|
+
typeof temporal.api.enums.v1.TimeoutType,
|
|
35
|
+
keyof typeof temporal.api.enums.v1.TimeoutType,
|
|
36
|
+
typeof TimeoutType,
|
|
37
|
+
'TIMEOUT_TYPE_'
|
|
38
|
+
>(
|
|
39
|
+
{
|
|
40
|
+
[TimeoutType.START_TO_CLOSE]: 1,
|
|
41
|
+
[TimeoutType.SCHEDULE_TO_START]: 2,
|
|
42
|
+
[TimeoutType.SCHEDULE_TO_CLOSE]: 3,
|
|
43
|
+
[TimeoutType.HEARTBEAT]: 4,
|
|
44
|
+
UNSPECIFIED: 0,
|
|
45
|
+
} as const,
|
|
46
|
+
'TIMEOUT_TYPE_'
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
export const RetryState = {
|
|
50
|
+
IN_PROGRESS: 'IN_PROGRESS',
|
|
51
|
+
NON_RETRYABLE_FAILURE: 'NON_RETRYABLE_FAILURE',
|
|
52
|
+
TIMEOUT: 'TIMEOUT',
|
|
53
|
+
MAXIMUM_ATTEMPTS_REACHED: 'MAXIMUM_ATTEMPTS_REACHED',
|
|
54
|
+
RETRY_POLICY_NOT_SET: 'RETRY_POLICY_NOT_SET',
|
|
55
|
+
INTERNAL_SERVER_ERROR: 'INTERNAL_SERVER_ERROR',
|
|
56
|
+
CANCEL_REQUESTED: 'CANCEL_REQUESTED',
|
|
57
|
+
|
|
58
|
+
/** @deprecated Use {@link IN_PROGRESS} instead. */
|
|
59
|
+
RETRY_STATE_IN_PROGRESS: 'IN_PROGRESS', // eslint-disable-line deprecation/deprecation
|
|
60
|
+
|
|
61
|
+
/** @deprecated Use {@link NON_RETRYABLE_FAILURE} instead. */
|
|
62
|
+
RETRY_STATE_NON_RETRYABLE_FAILURE: 'NON_RETRYABLE_FAILURE', // eslint-disable-line deprecation/deprecation
|
|
63
|
+
|
|
64
|
+
/** @deprecated Use {@link TIMEOUT} instead. */
|
|
65
|
+
RETRY_STATE_TIMEOUT: 'TIMEOUT', // eslint-disable-line deprecation/deprecation
|
|
66
|
+
|
|
67
|
+
/** @deprecated Use {@link MAXIMUM_ATTEMPTS_REACHED} instead. */
|
|
68
|
+
RETRY_STATE_MAXIMUM_ATTEMPTS_REACHED: 'MAXIMUM_ATTEMPTS_REACHED', // eslint-disable-line deprecation/deprecation
|
|
69
|
+
|
|
70
|
+
/** @deprecated Use {@link RETRY_POLICY_NOT_SET} instead. */
|
|
71
|
+
RETRY_STATE_RETRY_POLICY_NOT_SET: 'RETRY_POLICY_NOT_SET', // eslint-disable-line deprecation/deprecation
|
|
72
|
+
|
|
73
|
+
/** @deprecated Use {@link INTERNAL_SERVER_ERROR} instead. */
|
|
74
|
+
RETRY_STATE_INTERNAL_SERVER_ERROR: 'INTERNAL_SERVER_ERROR', // eslint-disable-line deprecation/deprecation
|
|
75
|
+
|
|
76
|
+
/** @deprecated Use {@link CANCEL_REQUESTED} instead. */
|
|
77
|
+
RETRY_STATE_CANCEL_REQUESTED: 'CANCEL_REQUESTED', // eslint-disable-line deprecation/deprecation
|
|
78
|
+
|
|
79
|
+
/** @deprecated Use `undefined` instead. */
|
|
80
|
+
RETRY_STATE_UNSPECIFIED: undefined, // eslint-disable-line deprecation/deprecation
|
|
81
|
+
} as const;
|
|
82
|
+
export type RetryState = (typeof RetryState)[keyof typeof RetryState];
|
|
33
83
|
|
|
34
|
-
|
|
35
|
-
|
|
84
|
+
export const [encodeRetryState, decodeRetryState] = makeProtoEnumConverters<
|
|
85
|
+
temporal.api.enums.v1.RetryState,
|
|
86
|
+
typeof temporal.api.enums.v1.RetryState,
|
|
87
|
+
keyof typeof temporal.api.enums.v1.RetryState,
|
|
88
|
+
typeof RetryState,
|
|
89
|
+
'RETRY_STATE_'
|
|
90
|
+
>(
|
|
91
|
+
{
|
|
92
|
+
[RetryState.IN_PROGRESS]: 1,
|
|
93
|
+
[RetryState.NON_RETRYABLE_FAILURE]: 2,
|
|
94
|
+
[RetryState.TIMEOUT]: 3,
|
|
95
|
+
[RetryState.MAXIMUM_ATTEMPTS_REACHED]: 4,
|
|
96
|
+
[RetryState.RETRY_POLICY_NOT_SET]: 5,
|
|
97
|
+
[RetryState.INTERNAL_SERVER_ERROR]: 6,
|
|
98
|
+
[RetryState.CANCEL_REQUESTED]: 7,
|
|
99
|
+
UNSPECIFIED: 0,
|
|
100
|
+
} as const,
|
|
101
|
+
'RETRY_STATE_'
|
|
102
|
+
);
|
|
36
103
|
|
|
37
104
|
export type WorkflowExecution = temporal.api.common.v1.IWorkflowExecution;
|
|
38
105
|
|
|
@@ -279,7 +346,7 @@ export class ChildWorkflowFailure extends TemporalFailure {
|
|
|
279
346
|
|
|
280
347
|
/**
|
|
281
348
|
* This exception is thrown in the following cases:
|
|
282
|
-
* - Workflow with the same Workflow
|
|
349
|
+
* - Workflow with the same Workflow ID is currently running and the {@link WorkflowOptions.workflowIdConflictPolicy} is `WORKFLOW_ID_CONFLICT_POLICY_FAIL`
|
|
283
350
|
* - There is a closed Workflow with the same Workflow Id and the {@link WorkflowOptions.workflowIdReusePolicy}
|
|
284
351
|
* is `WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE`
|
|
285
352
|
* - There is closed Workflow in the `Completed` state with the same Workflow Id and the {@link WorkflowOptions.workflowIdReusePolicy}
|
package/src/interfaces.ts
CHANGED
|
@@ -128,11 +128,11 @@ export interface HistoryAndWorkflowId {
|
|
|
128
128
|
* Policy defining actions taken when a workflow exits while update or signal handlers are running.
|
|
129
129
|
* The workflow exit may be due to successful return, failure, cancellation, or continue-as-new.
|
|
130
130
|
*/
|
|
131
|
-
export
|
|
131
|
+
export const HandlerUnfinishedPolicy = {
|
|
132
132
|
/**
|
|
133
133
|
* Issue a warning in addition to abandoning the handler execution. The warning will not be issued if the workflow fails.
|
|
134
134
|
*/
|
|
135
|
-
WARN_AND_ABANDON
|
|
135
|
+
WARN_AND_ABANDON: 'WARN_AND_ABANDON',
|
|
136
136
|
|
|
137
137
|
/**
|
|
138
138
|
* Abandon the handler execution.
|
|
@@ -140,5 +140,6 @@ export enum HandlerUnfinishedPolicy {
|
|
|
140
140
|
* In the case of an update handler this means that the client will receive an error rather than
|
|
141
141
|
* the update result.
|
|
142
142
|
*/
|
|
143
|
-
ABANDON
|
|
144
|
-
}
|
|
143
|
+
ABANDON: 'ABANDON',
|
|
144
|
+
} as const;
|
|
145
|
+
export type HandlerUnfinishedPolicy = (typeof HandlerUnfinishedPolicy)[keyof typeof HandlerUnfinishedPolicy];
|
|
@@ -37,7 +37,7 @@ function requireConverter<T>(
|
|
|
37
37
|
): T {
|
|
38
38
|
let module;
|
|
39
39
|
try {
|
|
40
|
-
module = require(path); // eslint-disable-line @typescript-eslint/no-
|
|
40
|
+
module = require(path); // eslint-disable-line @typescript-eslint/no-require-imports
|
|
41
41
|
} catch (error) {
|
|
42
42
|
if (errorCode(error) === 'MODULE_NOT_FOUND') {
|
|
43
43
|
throw new ValueError(`Could not find a file at the specified ${type}Path: '${path}'.`);
|
|
@@ -80,23 +80,24 @@ export function joinProtoHostPort(components: ProtoHostPort): string {
|
|
|
80
80
|
* Parse the address for the gRPC endpoint of a Temporal server.
|
|
81
81
|
*
|
|
82
82
|
* - The URI may only contain a hostname and a port.
|
|
83
|
-
* - Port is optional; it
|
|
83
|
+
* - Port is optional; if not specified, set it to `defaultPort`.
|
|
84
84
|
*
|
|
85
|
-
* Examples of valid URIs:
|
|
85
|
+
* Examples of valid URIs (assuming `defaultPort` is 7233):
|
|
86
86
|
*
|
|
87
87
|
* ```
|
|
88
|
-
* 127.0.0.1
|
|
88
|
+
* 127.0.0.1 => { host: '127.0.0.1', port: 7233 }
|
|
89
|
+
* 192.168.0.1:7233 => { host: '192.168.0.1', port: 7233 }
|
|
89
90
|
* my.temporal.service.com:7233 => { host: 'my.temporal.service.com', port: 7233 }
|
|
90
91
|
* [::ffff:192.0.2.128]:8080 => { host: '[::ffff:192.0.2.128]', port: 8080 }
|
|
91
92
|
* ```
|
|
92
93
|
*/
|
|
93
|
-
export function
|
|
94
|
+
export function normalizeGrpcEndpointAddress(uri: string, defaultPort: number): string {
|
|
94
95
|
const splitted = splitProtoHostPort(uri);
|
|
95
96
|
if (!splitted || splitted.scheme !== undefined) {
|
|
96
97
|
throw new TypeError(
|
|
97
98
|
`Invalid address for Temporal gRPC endpoint: expected URI of the form 'hostname' or 'hostname:port'; got '${uri}'`
|
|
98
99
|
);
|
|
99
100
|
}
|
|
100
|
-
splitted.port ??=
|
|
101
|
+
splitted.port ??= defaultPort;
|
|
101
102
|
return joinProtoHostPort(splitted);
|
|
102
103
|
}
|
|
@@ -27,11 +27,11 @@ export interface TLSConfig {
|
|
|
27
27
|
* Pass a falsy value to use a non-encrypted connection or `true` or `{}` to
|
|
28
28
|
* connect with TLS without any customization.
|
|
29
29
|
*/
|
|
30
|
-
export type TLSConfigOption = TLSConfig | boolean | null;
|
|
30
|
+
export type TLSConfigOption = TLSConfig | boolean | undefined | null;
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Normalize {@link TLSConfigOption} by turning false and null to undefined and true to and empty object
|
|
34
34
|
*/
|
|
35
|
-
export function normalizeTlsConfig(tls
|
|
35
|
+
export function normalizeTlsConfig(tls: TLSConfigOption): TLSConfig | undefined {
|
|
36
36
|
return typeof tls === 'object' ? (tls === null ? undefined : tls) : tls ? {} : undefined;
|
|
37
37
|
}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { ValueError } from '../errors';
|
|
2
|
+
import { Exact, RemovePrefix, UnionToIntersection } from '../type-helpers';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create encoding and decoding functions to convert between the numeric `enum` types produced by our
|
|
6
|
+
* Protobuf compiler and "const object of strings" enum values that we expose in our public APIs.
|
|
7
|
+
*
|
|
8
|
+
* ### Usage
|
|
9
|
+
*
|
|
10
|
+
* Newly introduced enums should follow the following pattern:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* type ParentClosePolicy = (typeof ParentClosePolicy)[keyof typeof ParentClosePolicy];
|
|
14
|
+
* const ParentClosePolicy = {
|
|
15
|
+
* TERMINATE: 'TERMINATE',
|
|
16
|
+
* ABANDON: 'ABANDON',
|
|
17
|
+
* REQUEST_CANCEL: 'REQUEST_CANCEL',
|
|
18
|
+
* } as const;
|
|
19
|
+
*
|
|
20
|
+
* const [encodeParentClosePolicy, decodeParentClosePolicy] = //
|
|
21
|
+
* makeProtoEnumConverters<
|
|
22
|
+
* coresdk.child_workflow.ParentClosePolicy,
|
|
23
|
+
* typeof coresdk.child_workflow.ParentClosePolicy,
|
|
24
|
+
* keyof typeof coresdk.child_workflow.ParentClosePolicy,
|
|
25
|
+
* typeof ParentClosePolicy,
|
|
26
|
+
* 'PARENT_CLOSE_POLICY_' // This may be an empty string if the proto enum doesn't add a repeated prefix on values
|
|
27
|
+
* >(
|
|
28
|
+
* {
|
|
29
|
+
* [ParentClosePolicy.TERMINATE]: 1, // These numbers must match the ones in the proto enum
|
|
30
|
+
* [ParentClosePolicy.ABANDON]: 2,
|
|
31
|
+
* [ParentClosePolicy.REQUEST_CANCEL]: 3,
|
|
32
|
+
*
|
|
33
|
+
* UNSPECIFIED: 0,
|
|
34
|
+
* } as const,
|
|
35
|
+
* 'PARENT_CLOSE_POLICY_'
|
|
36
|
+
* );
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* `makeProtoEnumConverters` supports other usage patterns, but they are only meant for
|
|
40
|
+
* backward compatibility with former enum definitions and should not be used for new enums.
|
|
41
|
+
*
|
|
42
|
+
* ### Context
|
|
43
|
+
*
|
|
44
|
+
* Temporal's Protobuf APIs define several `enum` types; our Protobuf compiler transforms these to
|
|
45
|
+
* traditional (i.e. non-const) [TypeScript numeric `enum`s](https://www.typescriptlang.org/docs/handbook/enums.html#numeric-enums).
|
|
46
|
+
*
|
|
47
|
+
* For various reasons, this is far from ideal:
|
|
48
|
+
*
|
|
49
|
+
* - Due to the dual nature of non-const TypeScript `enum`s (they are both a type and a value),
|
|
50
|
+
* it is not possible to refer to an enum value from code without a "real" import of the enum type
|
|
51
|
+
* (i.e. can't simply do `import type ...`). In Workflow code, such an import would result in
|
|
52
|
+
* loading our entire Protobuf definitions into the workflow sandbox, adding several megabytes to
|
|
53
|
+
* the per-workflow memory footprint, which is unacceptable; to avoid that, we need to maintain
|
|
54
|
+
* a mirror copy of each enum types used by in-workflow APIs, and export these from either
|
|
55
|
+
* `@temporalio/common` or `@temporalio/workflow`.
|
|
56
|
+
* - It is not desirable for users to need an explicit dependency on `@temporalio/proto` just to
|
|
57
|
+
* get access to these enum types; we therefore made it a common practice to reexport these enums
|
|
58
|
+
* from our public facing packages. However, experience demontrated that these reexports effectively
|
|
59
|
+
* resulted in poor and inconsistent documentation coverage compared to mirrored enums types.
|
|
60
|
+
* - Our Protobuf enum types tend to follow a verbose and redundant naming convention, which feels
|
|
61
|
+
* unatural and excessive according to most TypeScript style guides; e.g. instead of
|
|
62
|
+
* `workflowIdReusePolicy: WorkflowIdReusePolicy.WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE`,
|
|
63
|
+
* a TypeScript developer would generally expect to be able to write something similar to
|
|
64
|
+
* `workflowIdReusePolicy: 'REJECT_DUPLICATE'`.
|
|
65
|
+
* - Because of the way Protobuf works, many of our enum types contain an `UNSPECIFIED` value, which
|
|
66
|
+
* is used to explicitly identify a value that is unset. In TypeScript code, the `undefined` value
|
|
67
|
+
* already serves that purpose, and is definitely more idiomatic to TS developers, whereas these
|
|
68
|
+
* `UNSPECIFIED` values create noise and confusion in our APIs.
|
|
69
|
+
* - TypeScript editors generally do a very bad job at providing autocompletion that implies reaching
|
|
70
|
+
* for values of a TypeScript enum type, forcing developers to explicitly type in at least part
|
|
71
|
+
* of the name of the enum type before they can get autocompletion for its values. On the other
|
|
72
|
+
* hand, all TS editors immediately provide autocompletion for string union types.
|
|
73
|
+
* - The [TypeScript's official documentation](https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums)
|
|
74
|
+
* itself suggests that, in modern TypeScript, the use of `as const` objects may generally suffice
|
|
75
|
+
* and may be advantageous over the use of `enum` types.
|
|
76
|
+
*
|
|
77
|
+
* A const object of strings, combined with a union type of possible string values, provides a much
|
|
78
|
+
* more idiomatic syntax and a better DX for TypeScript developers. This however requires a way to
|
|
79
|
+
* convert back and forth between the `enum` values produced by the Protobuf compiler and the
|
|
80
|
+
* equivalent string values.
|
|
81
|
+
*
|
|
82
|
+
* This helper dynamically creates these conversion functions for a given Protobuf enum type,
|
|
83
|
+
* strongly building upon specific conventions that we have adopted in our Protobuf definitions.
|
|
84
|
+
*
|
|
85
|
+
* ### Validations
|
|
86
|
+
*
|
|
87
|
+
* The complex type signature of this helper is there to prevent most potential incoherencies
|
|
88
|
+
* that could result from having to manually synchronize the const object of strings enum and the
|
|
89
|
+
* conversion table with the proto enum, while not requiring a regular import on the Protobuf enum
|
|
90
|
+
* itself (so it can be used safely for enums meant to be used from workflow code).
|
|
91
|
+
*
|
|
92
|
+
* In particular, failing any of the following invariants will result in build time errors:
|
|
93
|
+
*
|
|
94
|
+
* - For every key of the form `PREFIX_KEY: number` in the proto enum, excluding the `UNSPECIFIED` key:
|
|
95
|
+
* - There MUST be a corresponding `KEY: 'KEY'` entry in the const object of strings enum;
|
|
96
|
+
* - There MAY be a corresponding `PREFIX_KEY: 'KEY'` in the const object of strings enum
|
|
97
|
+
* (this is meant to preserve backward compatibility with the former syntax; such aliases should
|
|
98
|
+
* not be added for new enums and enum entries introduced going forward);
|
|
99
|
+
* - There MUST be a corresponding `KEY: number` in the mapping table.
|
|
100
|
+
* - If the proto enum contains a `PREFIX_UNSPECIFIED` entry, then:
|
|
101
|
+
* - There MAY be a corresponding `PREFIX_UNSPECIFIED: undefined` and/or `UNSPECIFIED: undefined`
|
|
102
|
+
* entries in the const object of strings enum — this is meant to preserve backward compatibility
|
|
103
|
+
* with the former syntax; this alias should not be added for new enums introduced going forward;
|
|
104
|
+
* - There MUST be an `UNSPECIFIED: 0` in the mapping table.
|
|
105
|
+
* - The const object of strings enum MUST NOT contain any other keys than the ones mandated or
|
|
106
|
+
* optionally allowed be the preceeding rules.
|
|
107
|
+
* - The mapping table MUST NOT contain any other keys than the ones mandated above.
|
|
108
|
+
*
|
|
109
|
+
* These rules notably ensure that whenever a new value is added to an existing Proto enum, the code
|
|
110
|
+
* will fail to compile until the corresponding entry is added on the const object of strings enum
|
|
111
|
+
* and the mapping table.
|
|
112
|
+
*
|
|
113
|
+
* @internal
|
|
114
|
+
*/
|
|
115
|
+
export function makeProtoEnumConverters<
|
|
116
|
+
ProtoEnumValue extends number,
|
|
117
|
+
ProtoEnum extends { [k in ProtoEnumKey]: ProtoEnumValue },
|
|
118
|
+
ProtoEnumKey extends `${Prefix}${string}`,
|
|
119
|
+
StringEnumTypeActual extends Exact<StringEnumType, StringEnumTypeActual>,
|
|
120
|
+
Prefix extends string,
|
|
121
|
+
//
|
|
122
|
+
// Parameters after this point will be inferred; they're not meant not to be specified by developers
|
|
123
|
+
Unspecified = ProtoEnumKey extends `${Prefix}UNSPECIFIED` ? 'UNSPECIFIED' : never,
|
|
124
|
+
ShortStringEnumKey extends RemovePrefix<Prefix, ProtoEnumKey> = Exclude<
|
|
125
|
+
RemovePrefix<Prefix, ProtoEnumKey>,
|
|
126
|
+
Unspecified
|
|
127
|
+
>,
|
|
128
|
+
StringEnumType extends ProtoConstObjectOfStringsEnum<
|
|
129
|
+
ShortStringEnumKey,
|
|
130
|
+
Prefix,
|
|
131
|
+
Unspecified
|
|
132
|
+
> = ProtoConstObjectOfStringsEnum<ShortStringEnumKey, Prefix, Unspecified>,
|
|
133
|
+
MapTable extends ProtoEnumToConstObjectOfStringMapTable<
|
|
134
|
+
StringEnumType,
|
|
135
|
+
ProtoEnumValue,
|
|
136
|
+
ProtoEnum,
|
|
137
|
+
ProtoEnumKey,
|
|
138
|
+
Prefix,
|
|
139
|
+
Unspecified,
|
|
140
|
+
ShortStringEnumKey
|
|
141
|
+
> = ProtoEnumToConstObjectOfStringMapTable<
|
|
142
|
+
StringEnumType,
|
|
143
|
+
ProtoEnumValue,
|
|
144
|
+
ProtoEnum,
|
|
145
|
+
ProtoEnumKey,
|
|
146
|
+
Prefix,
|
|
147
|
+
Unspecified,
|
|
148
|
+
ShortStringEnumKey
|
|
149
|
+
>,
|
|
150
|
+
>(
|
|
151
|
+
mapTable: MapTable,
|
|
152
|
+
prefix: Prefix
|
|
153
|
+
): [
|
|
154
|
+
(
|
|
155
|
+
input: ShortStringEnumKey | `${Prefix}${ShortStringEnumKey}` | ProtoEnumValue | null | undefined
|
|
156
|
+
) => ProtoEnumValue | undefined, //
|
|
157
|
+
(input: ProtoEnumValue | null | undefined) => ShortStringEnumKey | undefined, //
|
|
158
|
+
] {
|
|
159
|
+
const reverseTable: Record<ProtoEnumValue, ShortStringEnumKey> = Object.fromEntries(
|
|
160
|
+
Object.entries(mapTable).map(([k, v]) => [v, k])
|
|
161
|
+
);
|
|
162
|
+
const hasUnspecified = (mapTable as any)['UNSPECIFIED'] === 0 || (mapTable as any)[`${prefix}UNSPECIFIED`] === 0;
|
|
163
|
+
|
|
164
|
+
function isShortStringEnumKeys(x: unknown): x is ShortStringEnumKey {
|
|
165
|
+
return typeof x === 'string' && x in mapTable;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function isNumericEnumValue(x: unknown): x is ProtoEnum[keyof ProtoEnum] {
|
|
169
|
+
return typeof x === 'number' && x in reverseTable;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function encode(
|
|
173
|
+
input: ShortStringEnumKey | `${Prefix}${ShortStringEnumKey}` | ProtoEnumValue | null | undefined
|
|
174
|
+
): ProtoEnumValue | undefined {
|
|
175
|
+
if (input == null) {
|
|
176
|
+
return undefined;
|
|
177
|
+
} else if (typeof input === 'string') {
|
|
178
|
+
let shorten: string = input;
|
|
179
|
+
if (shorten.startsWith(prefix)) {
|
|
180
|
+
shorten = shorten.slice(prefix.length);
|
|
181
|
+
}
|
|
182
|
+
if (isShortStringEnumKeys(shorten)) {
|
|
183
|
+
return mapTable[shorten];
|
|
184
|
+
}
|
|
185
|
+
throw new ValueError(`Invalid enum value: '${input}'`);
|
|
186
|
+
} else if (typeof input === 'number') {
|
|
187
|
+
return input;
|
|
188
|
+
} else {
|
|
189
|
+
throw new ValueError(`Invalid enum value: '${input}' of type ${typeof input}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function decode(input: ProtoEnumValue | null | undefined): ShortStringEnumKey | undefined {
|
|
194
|
+
if (input == null) {
|
|
195
|
+
return undefined;
|
|
196
|
+
} else if (typeof input === 'number') {
|
|
197
|
+
if (hasUnspecified && input === 0) {
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (isNumericEnumValue(input)) {
|
|
202
|
+
return reverseTable[input];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// We got a proto enum value that we don't yet know about (i.e. it didn't exist when this code
|
|
206
|
+
// was compiled). This is certainly a possibility, but given how our APIs evolve, this is is
|
|
207
|
+
// unlikely to be a terribly bad thing by itself (we avoid adding new enum values in places
|
|
208
|
+
// that would break backward compatibility with existing deployed code). Therefore, throwing
|
|
209
|
+
// on "unexpected" values is likely to end up causing more problems than it might avoid,
|
|
210
|
+
// especially given that the decoded value may actually never get read anwyay.
|
|
211
|
+
//
|
|
212
|
+
// Therefore, we instead cheat on type constraints and return a string of the form "unknown_23".
|
|
213
|
+
// That somewhat mirrors the behavior we'd get with the pure numerical approach.
|
|
214
|
+
return `unknown_${input}` as ShortStringEnumKey;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
throw new ValueError(`Invalid proto enum value: '${input}' of type ${typeof input}`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return [encode, decode] as const;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Given the exploded parameters of a proto enum (i.e. short keys, prefix, and short key of the
|
|
225
|
+
* unspecified value), make a type that _exactly_ corresponds to the const object of strings enum,
|
|
226
|
+
* e.g. the type that the developer is expected to write.
|
|
227
|
+
*
|
|
228
|
+
* For example, for coresdk.child_workflow.ParentClosePolicy, this evaluates to:
|
|
229
|
+
*
|
|
230
|
+
* {
|
|
231
|
+
* TERMINATE: "TERMINATE";
|
|
232
|
+
* ABANDON: "ABANDON";
|
|
233
|
+
* REQUEST_CANCEL: "REQUEST_CANCEL";
|
|
234
|
+
*
|
|
235
|
+
* PARENT_CLOSE_POLICY_TERMINATE?: "TERMINATE";
|
|
236
|
+
* PARENT_CLOSE_POLICY_ABANDON?: "ABANDON";
|
|
237
|
+
* PARENT_CLOSE_POLICY_REQUEST_CANCEL?: "REQUEST_CANCEL";
|
|
238
|
+
*
|
|
239
|
+
* PARENT_CLOSE_POLICY_UNSPECIFIED?: undefined;
|
|
240
|
+
* }
|
|
241
|
+
*/
|
|
242
|
+
type ProtoConstObjectOfStringsEnum<
|
|
243
|
+
ShortStringEnumKey extends string,
|
|
244
|
+
Prefix extends string,
|
|
245
|
+
Unspecified, // e.g. 'UNSPECIFIED'
|
|
246
|
+
> = UnionToIntersection<
|
|
247
|
+
| {
|
|
248
|
+
// e.g.: "TERMINATE": "TERMINATE"
|
|
249
|
+
readonly [k in ShortStringEnumKey]: k;
|
|
250
|
+
}
|
|
251
|
+
| {
|
|
252
|
+
[k in ShortStringEnumKey]: Prefix extends ''
|
|
253
|
+
? object
|
|
254
|
+
: {
|
|
255
|
+
// e.g.: "PARENT_CLOSE_POLICY_TERMINATE"?: "TERMINATE"
|
|
256
|
+
readonly [kk in `${Prefix}${k}`]?: k;
|
|
257
|
+
};
|
|
258
|
+
}[ShortStringEnumKey]
|
|
259
|
+
| (Unspecified extends string
|
|
260
|
+
? {
|
|
261
|
+
// e.g.: "PARENT_CLOSE_POLICY_UNSPECIFIED"?: undefined
|
|
262
|
+
[k in `${Prefix}${Unspecified}`]?: undefined;
|
|
263
|
+
}
|
|
264
|
+
: object)
|
|
265
|
+
| (Unspecified extends string
|
|
266
|
+
? {
|
|
267
|
+
// e.g.: "UNSPECIFIED"?: undefined
|
|
268
|
+
[k in `${Unspecified}`]?: undefined;
|
|
269
|
+
}
|
|
270
|
+
: object)
|
|
271
|
+
>;
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Given the exploded parameters of a proto enum (i.e. short keys, prefix, and short key of the
|
|
275
|
+
* unspecified value), make a type that _exactly_ corresponds to the mapping table that the user is
|
|
276
|
+
* expected to provide.
|
|
277
|
+
*
|
|
278
|
+
* For example, for coresdk.child_workflow.ParentClosePolicy, this evaluates to:
|
|
279
|
+
*
|
|
280
|
+
* {
|
|
281
|
+
* UNSPECIFIED: 0,
|
|
282
|
+
* TERMINATE: 1,
|
|
283
|
+
* ABANDON: 2,
|
|
284
|
+
* REQUEST_CANCEL: 3,
|
|
285
|
+
* }
|
|
286
|
+
*/
|
|
287
|
+
type ProtoEnumToConstObjectOfStringMapTable<
|
|
288
|
+
_StringEnum extends ProtoConstObjectOfStringsEnum<ShortStringEnumKey, Prefix, Unspecified>,
|
|
289
|
+
ProtoEnumValue extends number,
|
|
290
|
+
ProtoEnum extends { [k in ProtoEnumKey]: ProtoEnumValue },
|
|
291
|
+
ProtoEnumKey extends `${Prefix}${string}`,
|
|
292
|
+
Prefix extends string,
|
|
293
|
+
Unspecified,
|
|
294
|
+
ShortStringEnumKey extends RemovePrefix<Prefix, ProtoEnumKey>,
|
|
295
|
+
> = UnionToIntersection<
|
|
296
|
+
{
|
|
297
|
+
[k in ProtoEnumKey]: {
|
|
298
|
+
[kk in RemovePrefix<Prefix, k>]: ProtoEnum[k] extends number ? ProtoEnum[k] : never;
|
|
299
|
+
};
|
|
300
|
+
}[ProtoEnumKey]
|
|
301
|
+
>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './enums-helpers';
|