@temporalio/common 1.11.3 → 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.
Files changed (65) hide show
  1. package/lib/activity-options.d.ts +8 -6
  2. package/lib/activity-options.js +13 -12
  3. package/lib/activity-options.js.map +1 -1
  4. package/lib/converter/failure-converter.js +8 -6
  5. package/lib/converter/failure-converter.js.map +1 -1
  6. package/lib/converter/payload-converter.js +7 -7
  7. package/lib/converter/payload-converter.js.map +1 -1
  8. package/lib/deprecated-time.js +9 -10
  9. package/lib/deprecated-time.js.map +1 -1
  10. package/lib/encoding.js +3 -3
  11. package/lib/encoding.js.map +1 -1
  12. package/lib/failure.d.ts +51 -23
  13. package/lib/failure.js +65 -32
  14. package/lib/failure.js.map +1 -1
  15. package/lib/index.js +4 -5
  16. package/lib/index.js.map +1 -1
  17. package/lib/interceptors.js +1 -2
  18. package/lib/interceptors.js.map +1 -1
  19. package/lib/interfaces.d.ts +5 -4
  20. package/lib/interfaces.js +4 -5
  21. package/lib/interfaces.js.map +1 -1
  22. package/lib/internal-non-workflow/codec-helpers.js +22 -23
  23. package/lib/internal-non-workflow/codec-helpers.js.map +1 -1
  24. package/lib/internal-non-workflow/data-converter-helpers.js +3 -4
  25. package/lib/internal-non-workflow/data-converter-helpers.js.map +1 -1
  26. package/lib/internal-non-workflow/parse-host-uri.js +3 -4
  27. package/lib/internal-non-workflow/parse-host-uri.js.map +1 -1
  28. package/lib/internal-non-workflow/proxy-config.js +1 -2
  29. package/lib/internal-non-workflow/proxy-config.js.map +1 -1
  30. package/lib/internal-non-workflow/tls-config.d.ts +0 -1
  31. package/lib/internal-non-workflow/tls-config.js +1 -2
  32. package/lib/internal-non-workflow/tls-config.js.map +1 -1
  33. package/lib/internal-non-workflow/utils.js +1 -2
  34. package/lib/internal-non-workflow/utils.js.map +1 -1
  35. package/lib/internal-workflow/enums-helpers.d.ts +170 -0
  36. package/lib/internal-workflow/enums-helpers.js +172 -0
  37. package/lib/internal-workflow/enums-helpers.js.map +1 -0
  38. package/lib/internal-workflow/index.d.ts +1 -0
  39. package/lib/internal-workflow/index.js +18 -0
  40. package/lib/internal-workflow/index.js.map +1 -0
  41. package/lib/proto-utils.js +4 -5
  42. package/lib/proto-utils.js.map +1 -1
  43. package/lib/retry-policy.js +2 -3
  44. package/lib/retry-policy.js.map +1 -1
  45. package/lib/time.js +12 -13
  46. package/lib/time.js.map +1 -1
  47. package/lib/type-helpers.d.ts +18 -0
  48. package/lib/type-helpers.js +12 -13
  49. package/lib/type-helpers.js.map +1 -1
  50. package/lib/versioning-intent-enum.js +2 -2
  51. package/lib/versioning-intent-enum.js.map +1 -1
  52. package/lib/workflow-options.d.ts +69 -17
  53. package/lib/workflow-options.js +64 -23
  54. package/lib/workflow-options.js.map +1 -1
  55. package/package.json +3 -3
  56. package/src/activity-options.ts +24 -13
  57. package/src/converter/failure-converter.ts +10 -6
  58. package/src/converter/payload-converter.ts +1 -1
  59. package/src/failure.ts +95 -28
  60. package/src/interfaces.ts +5 -4
  61. package/src/internal-non-workflow/data-converter-helpers.ts +1 -1
  62. package/src/internal-workflow/enums-helpers.ts +301 -0
  63. package/src/internal-workflow/index.ts +1 -0
  64. package/src/type-helpers.ts +79 -1
  65. package/src/workflow-options.ts +110 -23
package/src/failure.ts CHANGED
@@ -1,38 +1,105 @@
1
1
  import type { temporal } from '@temporalio/proto';
2
- import { checkExtends, errorMessage, isRecord, SymbolBasedInstanceOfError } from './type-helpers';
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
- // Avoid importing the proto implementation to reduce workflow bundle size
9
- // Copied from temporal.api.enums.v1.TimeoutType
10
- export enum TimeoutType {
11
- TIMEOUT_TYPE_UNSPECIFIED = 0,
12
- TIMEOUT_TYPE_START_TO_CLOSE = 1,
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
- checkExtends<temporal.api.enums.v1.TimeoutType, TimeoutType>();
19
- checkExtends<TimeoutType, temporal.api.enums.v1.TimeoutType>();
20
-
21
- // Avoid importing the proto implementation to reduce workflow bundle size
22
- // Copied from temporal.api.enums.v1.RetryState
23
- export enum RetryState {
24
- RETRY_STATE_UNSPECIFIED = 0,
25
- RETRY_STATE_IN_PROGRESS = 1,
26
- RETRY_STATE_NON_RETRYABLE_FAILURE = 2,
27
- RETRY_STATE_TIMEOUT = 3,
28
- RETRY_STATE_MAXIMUM_ATTEMPTS_REACHED = 4,
29
- RETRY_STATE_RETRY_POLICY_NOT_SET = 5,
30
- RETRY_STATE_INTERNAL_SERVER_ERROR = 6,
31
- RETRY_STATE_CANCEL_REQUESTED = 7,
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
- checkExtends<temporal.api.enums.v1.RetryState, RetryState>();
35
- checkExtends<RetryState, temporal.api.enums.v1.RetryState>();
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 Id is currently running
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 enum HandlerUnfinishedPolicy {
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 = 1,
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 = 2,
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-var-requires
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}'.`);
@@ -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';
@@ -17,6 +17,84 @@ export function checkExtends<_Orig, _Copy extends _Orig>(): void {
17
17
 
18
18
  export type Replace<Base, New> = Omit<Base, keyof New> & New;
19
19
 
20
+ // From https://github.com/sindresorhus/type-fest/blob/main/source/union-to-intersection.d.ts
21
+ // MIT or CC0-1.0 — It is meant to be copied into your codebase rather than being used as a dependency.
22
+ export type UnionToIntersection<Union> =
23
+ // `extends unknown` is always going to be the case and is used to convert the `Union` into a
24
+ // [distributive conditional type](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
25
+ (
26
+ Union extends unknown
27
+ ? // The union type is used as the only argument to a function since the union
28
+ // of function arguments is an intersection.
29
+ (distributedUnion: Union) => void
30
+ : // This won't happen.
31
+ never
32
+ ) extends // Infer the `Intersection` type since TypeScript represents the positional
33
+ // arguments of unions of functions as an intersection of the union.
34
+ (mergedIntersection: infer Intersection) => void
35
+ ? // The `& Union` is to allow indexing by the resulting type
36
+ Intersection & Union
37
+ : never;
38
+
39
+ type IsEqual<A, B> = (<G>() => G extends A ? 1 : 2) extends <G>() => G extends B ? 1 : 2 ? true : false;
40
+
41
+ type Primitive = null | undefined | string | number | boolean | symbol | bigint;
42
+
43
+ type IsNull<T> = [T] extends [null] ? true : false;
44
+
45
+ type IsUnknown<T> = unknown extends T // `T` can be `unknown` or `any`
46
+ ? IsNull<T> extends false // `any` can be `null`, but `unknown` can't be
47
+ ? true
48
+ : false
49
+ : false;
50
+
51
+ type ObjectValue<T, K> = K extends keyof T
52
+ ? T[K]
53
+ : ToString<K> extends keyof T
54
+ ? T[ToString<K>]
55
+ : K extends `${infer NumberK extends number}`
56
+ ? NumberK extends keyof T
57
+ ? T[NumberK]
58
+ : never
59
+ : never;
60
+
61
+ type ToString<T> = T extends string | number ? `${T}` : never;
62
+
63
+ type KeysOfUnion<ObjectType> = ObjectType extends unknown ? keyof ObjectType : never;
64
+
65
+ type ArrayElement<T> = T extends readonly unknown[] ? T[0] : never;
66
+
67
+ type ExactObject<ParameterType, InputType> = {
68
+ [Key in keyof ParameterType]: Exact<ParameterType[Key], ObjectValue<InputType, Key>>;
69
+ } & Record<Exclude<keyof InputType, KeysOfUnion<ParameterType>>, never>;
70
+
71
+ export type Exact<ParameterType, InputType> =
72
+ // Before distributing, check if the two types are equal and if so, return the parameter type immediately
73
+ IsEqual<ParameterType, InputType> extends true
74
+ ? ParameterType
75
+ : // If the parameter is a primitive, return it as is immediately to avoid it being converted to a complex type
76
+ ParameterType extends Primitive
77
+ ? ParameterType
78
+ : // If the parameter is an unknown, return it as is immediately to avoid it being converted to a complex type
79
+ IsUnknown<ParameterType> extends true
80
+ ? unknown
81
+ : // If the parameter is a Function, return it as is because this type is not capable of handling function, leave it to TypeScript
82
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
83
+ ParameterType extends Function
84
+ ? ParameterType
85
+ : // Convert union of array to array of union: A[] & B[] => (A & B)[]
86
+ ParameterType extends unknown[]
87
+ ? Array<Exact<ArrayElement<ParameterType>, ArrayElement<InputType>>>
88
+ : // In TypeScript, Array is a subtype of ReadonlyArray, so always test Array before ReadonlyArray.
89
+ ParameterType extends readonly unknown[]
90
+ ? ReadonlyArray<Exact<ArrayElement<ParameterType>, ArrayElement<InputType>>>
91
+ : ExactObject<ParameterType, InputType>;
92
+ // End of borrow from https://github.com/sindresorhus/type-fest/blob/main/source/union-to-intersection.d.ts
93
+
94
+ export type RemovePrefix<Prefix extends string, Keys extends string> = {
95
+ [k in Keys]: k extends `${Prefix}${infer Suffix}` ? Suffix : never;
96
+ }[Keys];
97
+
20
98
  export function isRecord(value: unknown): value is Record<string, unknown> {
21
99
  return typeof value === 'object' && value !== null;
22
100
  }
@@ -153,7 +231,7 @@ export function deepFreeze<T>(object: T): T {
153
231
  if (value && typeof value === 'object') {
154
232
  try {
155
233
  deepFreeze(value);
156
- } catch (err) {
234
+ } catch (_err) {
157
235
  // This is okay, there are some typed arrays that cannot be frozen (encodingKeys)
158
236
  }
159
237
  } else if (typeof value === 'function') {