ts2workflows 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"expressions.d.ts","sourceRoot":"","sources":["../../src/transpiler/expressions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAkB,MAAM,sCAAsC,CAAA;AAC/E,OAAO,EAGL,UAAU,EAGV,SAAS,EAMV,MAAM,uBAAuB,CAAA;AAI9B,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,GAAG,UAAU,CAO3E;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,QAAQ,CAAC,gBAAgB,GAC9B,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,UAAU,CAAC,CAgCxC;AAED,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,QAAQ,CAAC,gBAAgB,GAC9B,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAG5B;AAsMD,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,QAAQ,CAAC,gBAAgB,GAC9B,UAAU,CAcZ;AA0JD,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAG3D;AAED,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAGvE;AA8CD,wBAAgB,aAAa,CAC3B,CAAC,SACG,QAAQ,CAAC,UAAU,GACnB,QAAQ,CAAC,QAAQ,GACjB,QAAQ,CAAC,aAAa,GACtB,IAAI,EACR,KAAK,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,QAAQ,CAAC,aAAa,CAAC,EAAE,CAgBlD;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAElE"}
1
+ {"version":3,"file":"expressions.d.ts","sourceRoot":"","sources":["../../src/transpiler/expressions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAkB,MAAM,sCAAsC,CAAA;AAC/E,OAAO,EAGL,UAAU,EAGV,SAAS,EAMV,MAAM,uBAAuB,CAAA;AAI9B,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,GAAG,UAAU,CAO3E;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,QAAQ,CAAC,gBAAgB,GAC9B,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,UAAU,CAAC,CAgCxC;AAED,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,QAAQ,CAAC,gBAAgB,GAC9B,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAG5B;AAgOD,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,QAAQ,CAAC,gBAAgB,GAC9B,UAAU,CAcZ;AA0JD,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAG3D;AAED,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAGvE;AAoDD,wBAAgB,aAAa,CAC3B,CAAC,SACG,QAAQ,CAAC,UAAU,GACnB,QAAQ,CAAC,QAAQ,GACjB,QAAQ,CAAC,aAAa,GACtB,IAAI,EACR,KAAK,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,QAAQ,CAAC,aAAa,CAAC,EAAE,CAgBlD;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAElE"}
@@ -149,6 +149,7 @@ function convertUnaryExpression(instance) {
149
149
  throw new WorkflowSyntaxError('only prefix unary operators are supported', instance.loc);
150
150
  }
151
151
  let op;
152
+ let istypeof = false;
152
153
  switch (instance.operator) {
153
154
  case '+':
154
155
  op = '+';
@@ -164,6 +165,10 @@ function convertUnaryExpression(instance) {
164
165
  // This is wrong: the return value should be ignored.
165
166
  op = undefined;
166
167
  break;
168
+ case 'typeof':
169
+ op = undefined;
170
+ istypeof = true;
171
+ break;
167
172
  case undefined:
168
173
  op = undefined;
169
174
  break;
@@ -171,7 +176,27 @@ function convertUnaryExpression(instance) {
171
176
  throw new WorkflowSyntaxError(`Unsupported unary operator: ${instance.operator}`, instance.loc);
172
177
  }
173
178
  const ex = convertExpression(instance.argument);
174
- return op ? new UnaryExpression(op, ex) : ex;
179
+ if (istypeof) {
180
+ return convertTypeOfExpression(ex);
181
+ }
182
+ else if (op) {
183
+ return new UnaryExpression(op, ex);
184
+ }
185
+ else {
186
+ return ex;
187
+ }
188
+ }
189
+ function convertTypeOfExpression(value) {
190
+ // Note for future rectoring: evalute value only once (in case it has side effects)
191
+ return new FunctionInvocationExpression('text.replace_all_regex', [
192
+ new FunctionInvocationExpression('text.replace_all_regex', [
193
+ new FunctionInvocationExpression('get_type', [value]),
194
+ new PrimitiveExpression('^(bytes|list|map|null)$'),
195
+ new PrimitiveExpression('object'),
196
+ ]),
197
+ new PrimitiveExpression('^(double|integer)$'),
198
+ new PrimitiveExpression('number'),
199
+ ]);
175
200
  }
176
201
  export function convertMemberExpression(node) {
177
202
  if (node.property.type === AST_NODE_TYPES.PrivateIdentifier) {
@@ -307,7 +332,12 @@ function convertTemplateLiteralToExpression(node) {
307
332
  const stringTerms = node.quasis
308
333
  .map((x) => x.value.cooked)
309
334
  .map((x) => new PrimitiveExpression(x));
310
- const templateTerms = node.expressions.map(convertExpression);
335
+ const templateTerms = node.expressions
336
+ .map(convertExpression)
337
+ .map((ex) => new FunctionInvocationExpression('default', [
338
+ ex,
339
+ new PrimitiveExpression('null'),
340
+ ]));
311
341
  // interleave string parts and the expression parts starting with strings
312
342
  const interleavedTerms = stringTerms
313
343
  .slice(0, stringTerms.length - 1)
@@ -324,13 +354,8 @@ function convertTemplateLiteralToExpression(node) {
324
354
  if (interleavedTerms.length === 0) {
325
355
  return new PrimitiveExpression('');
326
356
  }
327
- else if (interleavedTerms.length === 1) {
328
- return interleavedTerms[0];
329
- }
330
357
  else {
331
- return interleavedTerms.reduce((previous, current) => {
332
- return new BinaryExpression(previous, '+', current);
333
- });
358
+ return interleavedTerms.reduce((previous, current) => new BinaryExpression(previous, '+', current));
334
359
  }
335
360
  }
336
361
  export function throwIfSpread(nodes) {
@@ -49,24 +49,14 @@ function parseSubworkflows(node) {
49
49
  const workflowParams = nodeParams.map((param) => {
50
50
  switch (param.type) {
51
51
  case AST_NODE_TYPES.Identifier:
52
- return { name: param.name };
53
- case AST_NODE_TYPES.AssignmentPattern:
54
- if (param.left.type !== AST_NODE_TYPES.Identifier) {
55
- throw new WorkflowSyntaxError('The default value must be an identifier', param.left.loc);
56
- }
57
- if (param.right.type !== AST_NODE_TYPES.Literal) {
58
- throw new WorkflowSyntaxError('The default value must be a literal', param.right.loc);
52
+ if (param.optional) {
53
+ return { name: param.name, default: null };
59
54
  }
60
- if (!(typeof param.right.value === 'string' ||
61
- typeof param.right.value === 'number' ||
62
- typeof param.right.value === 'boolean' ||
63
- param.right.value === null)) {
64
- throw new WorkflowSyntaxError('The default value must be a string, number, boolean or null', param.left.loc);
55
+ else {
56
+ return { name: param.name };
65
57
  }
66
- return {
67
- name: param.left.name,
68
- default: param.right.value,
69
- };
58
+ case AST_NODE_TYPES.AssignmentPattern:
59
+ return parseSubworkflowDefaultArgument(param);
70
60
  default:
71
61
  throw new WorkflowSyntaxError('Function parameter must be an identifier or an assignment', param.loc);
72
62
  }
@@ -74,3 +64,31 @@ function parseSubworkflows(node) {
74
64
  const steps = parseStatement(node.body, {});
75
65
  return new SubworkflowAST(node.id.name, steps, workflowParams);
76
66
  }
67
+ function parseSubworkflowDefaultArgument(param) {
68
+ if (param.left.type !== AST_NODE_TYPES.Identifier) {
69
+ throw new WorkflowSyntaxError('The default value must be an identifier', param.left.loc);
70
+ }
71
+ if (param.left.optional) {
72
+ throw new WorkflowSyntaxError("Parameter can't have default value and initializer", param.left.loc);
73
+ }
74
+ const name = param.left.name;
75
+ let defaultValue;
76
+ if (param.right.type === AST_NODE_TYPES.Identifier &&
77
+ param.right.name === 'undefined') {
78
+ defaultValue = null;
79
+ }
80
+ else if (param.right.type === AST_NODE_TYPES.Literal &&
81
+ (typeof param.right.value === 'string' ||
82
+ typeof param.right.value === 'number' ||
83
+ typeof param.right.value === 'boolean' ||
84
+ param.right.value === null)) {
85
+ defaultValue = param.right.value;
86
+ }
87
+ else {
88
+ throw new WorkflowSyntaxError('The default value must be a literal number, string, boolean, null, or undefined', param.right.loc);
89
+ }
90
+ return {
91
+ name,
92
+ default: defaultValue,
93
+ };
94
+ }
@@ -34,11 +34,13 @@ Map keys can be identifiers or strings: `{temperature: -12}` or `{"temperature":
34
34
 
35
35
  ### Bytes type
36
36
 
37
- It is not possible to construct a bytes object expect by calling a function that returns bytes (e.g. base64.decode). It is not possible to do anything else with a bytes object than to assign it to a variable and to pass it one of the functions that take bytes type as input variable (e.g. base64.encode).
37
+ A `bytes` object can only be constructed by calling a function that returns bytes (e.g. `base64.decode`). The only things that can be done with a `bytes` object is to assign it to variable and to pass the `bytes` object to one of the functions that take `bytes` type as input variable (e.g. `base64.encode`).
38
38
 
39
39
  ### null type
40
40
 
41
- In addition to the literal `null`, the Typescript `undefined` value is also treated as `null` in Workflows YAML.
41
+ In addition to the literal `null`, the Typescript `undefined` value is also translated to `null` in Workflows YAML.
42
+
43
+ Note that on Typescript-level typechecking `null` and `undefined` are considered distinct types.
42
44
 
43
45
  ### Implicit type conversions
44
46
 
@@ -79,6 +81,7 @@ sys.get_env('GOOGLE_CLOUD_PROJECT_ID')
79
81
  | ?? | nullish coalescing |
80
82
  | ?. | optional chaining |
81
83
  | ? : | conditional operator |
84
+ | typeof | return the type of the operand as a string |
82
85
 
83
86
  The [precendence order of operators](https://cloud.google.com/workflows/docs/reference/syntax/datatypes#order-operations) is the same as in GCP Workflows.
84
87
 
@@ -98,7 +101,7 @@ is converted to an [if() expression](https://cloud.google.com/workflows/docs/ref
98
101
  ${if(x > 0, "positive", "not positive")}
99
102
  ```
100
103
 
101
- ⚠️ Note that Workflows always evaluates both expression branches unlike Typescript which evaluates only the branch that gets executed.
104
+ ⚠️ Workflows always evaluates both expression branches unlike Typescript which evaluates only the branch that gets executed.
102
105
 
103
106
  ### Nullish coalescing operator
104
107
 
@@ -114,7 +117,7 @@ is converted to a [default() expression](https://cloud.google.com/workflows/docs
114
117
  ${default(x, "default value")}
115
118
  ```
116
119
 
117
- ⚠️ Note that Workflows always evaluates the right-hand side expression unlike Typescript which evaluates the right-hand side only if the left-hand side is `null` or `undefined`.
120
+ ⚠️ Workflows always evaluates the right-hand side expression unlike Typescript which evaluates the right-hand side only if the left-hand side is `null` or `undefined`.
118
121
 
119
122
  ### Optional chaining
120
123
 
@@ -130,11 +133,28 @@ is converted to a [map.get() expression](https://cloud.google.com/workflows/docs
130
133
  ${map.get(data, ["user", "name"])}
131
134
  ```
132
135
 
136
+ ### typeof operator
137
+
138
+ Returns the type of the operand as a string. The return values are the same ones that Javascript typeof operation returns. The following table shows the possible values for different operand types.
139
+
140
+ | Operand | Result |
141
+ | ------- | --------- |
142
+ | boolean | "boolean" |
143
+ | bytes | "object" |
144
+ | double | "number" |
145
+ | integer | "number" |
146
+ | list | "object" |
147
+ | map | "object" |
148
+ | string | "string" |
149
+ | null | "object" |
150
+
151
+ The typeof operator is useful as a type guard in Typescript (e.g. `typeof x === "string"`). For other use cases, consider the [get_type function](https://cloud.google.com/workflows/docs/reference/stdlib/expression-helpers#type_functions) from the Workflows standard library. It makes finer distinctions between types. It, for example, returns distinct values for lists and maps.
152
+
133
153
  ## Template literals
134
154
 
135
155
  Template literals are strings that support string interpolation. For example, `Hello ${name}`.
136
156
 
137
- ⚠️ Interpolated values can (only) be numbers, strings or booleans. Other types will throw a TypeError at runtime.
157
+ ⚠️ Interpolated values can (only) be numbers, strings, booleans or nulls. Other types will throw a TypeError at runtime.
138
158
 
139
159
  ## Subworkflow definitions
140
160
 
@@ -155,7 +175,7 @@ function anotherWorkflow(): number {
155
175
  }
156
176
  ```
157
177
 
158
- Workflows can have parameters:
178
+ Subworkflows can have parameters:
159
179
 
160
180
  ```typescript
161
181
  function multiply(firstFactor: number, secondFactor: number): number {
@@ -163,10 +183,18 @@ function multiply(firstFactor: number, secondFactor: number): number {
163
183
  }
164
184
  ```
165
185
 
166
- Parameters can be optional and have a default value that is used if a value is not provided in a subworkflow call:
186
+ Optional parameters can be specified with a question mark. If a value is not provided on the call site, the value is set to `null` during the subworkflow execution. The following subworkflow can be called as `greet()` or `greet("Tiabeanie")`.
187
+
188
+ ```typescript
189
+ function greet(name?: string): string {
190
+ return 'Hello, ${name ?? "world"}'
191
+ }
192
+ ```
193
+
194
+ Parameters can have default values. The default value is used if a value is not provided in a subworkflow call. The following subworkflow can be called with one parameter (`log(3)`) or with two parameters (`log(3, 2)`).
167
195
 
168
196
  ```typescript
169
- function log(x, base = 10) {
197
+ function log(x: number, base: number = 10) {
170
198
  return 'Should compute the logarithm of x'
171
199
  }
172
200
  ```
@@ -201,7 +229,7 @@ is converted to an [assign step](https://cloud.google.com/workflows/docs/referen
201
229
 
202
230
  This syntax can be used to call [standard library functions](https://cloud.google.com/workflows/docs/reference/stdlib/overview), subworkflows or connectors. Note that Javascript runtime functions (such as `fetch()`, `console.error()` or `new XMLHttpRequest()`) are not available on Workflows.
203
231
 
204
- GCP Workflows language has two ways of calling functions and subworkflows: as expression in an [assign step](https://cloud.google.com/workflows/docs/reference/syntax/variables#assign-step) or as [call step](https://cloud.google.com/workflows/docs/reference/syntax/calls). They can mostly be used interchangeably. However, [blocking calls](https://cloud.google.com/workflows/docs/reference/syntax/expressions#blocking-calls) must be made as call steps. The transpiler tries to automatically output a call step when necessary.
232
+ GCP Workflows language has two ways of calling functions and subworkflows: as expression in an [assign step](https://cloud.google.com/workflows/docs/reference/syntax/variables#assign-step) or as [call step](https://cloud.google.com/workflows/docs/reference/syntax/calls). They can mostly be used interchangeably. However, [blocking calls](https://cloud.google.com/workflows/docs/reference/syntax/expressions#blocking-calls) must be made as call steps. The ts2workflows transpiler tries to automatically output a call step when necessary.
205
233
 
206
234
  It is also possible to force a function to be called as call step. This might be useful, if the transpiler fails to output call step when it should, or if you want to use named parameters. For example, the following Typescript program
207
235
 
@@ -738,7 +766,7 @@ is converted to a step with the label `setName`:
738
766
 
739
767
  ## Type annotations for standard library functions
740
768
 
741
- Type annotations for GCP Workflows standard library functions and expression helpers are provided by importing "ts2workflows/types/workflowslib".
769
+ Type annotations for [GCP Workflows standard library functions and expression helpers](https://cloud.google.com/workflows/docs/reference/stdlib/overview) are provided by importing "ts2workflows/types/workflowslib".
742
770
 
743
771
  ```typescript
744
772
  import { sys } from 'ts2workflows/types/workflowslib'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts2workflows",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Transpile Typescript code to GCP Workflows programs",
5
5
  "homepage": "https://github.com/aajanki/ts2workflows",
6
6
  "repository": {
@@ -66,7 +66,7 @@
66
66
  "eslint": "^9.10.0",
67
67
  "husky": "^9.1.6",
68
68
  "lint-staged": "^15.2.10",
69
- "mocha": "^10.4.0",
69
+ "mocha": "^11.1.0",
70
70
  "prettier": "^3.2.5",
71
71
  "rimraf": "^5.0.10",
72
72
  "tsx": "^4.10.2",
@@ -78,4 +78,4 @@
78
78
  "typescript": "^5.0.0",
79
79
  "yaml": "^2.4.2"
80
80
  }
81
- }
81
+ }
@@ -1,11 +1,13 @@
1
+ export {}
2
+
1
3
  // Type annotations for GCP Workflows expression helpers, standard library
2
4
  // functions and (some) connectors
3
5
 
4
6
  // An opaque bytes type.
5
7
  // This should really be defined in the transpiler, not here.
6
- declare const tag: unique symbol
8
+ declare const __bytes_tag: unique symbol
7
9
  export interface bytes {
8
- readonly [tag]: 'bytes'
10
+ readonly [__bytes_tag]: 'bytes'
9
11
  }
10
12
 
11
13
  // GCP Workflows expression helpers
@@ -37,17 +39,36 @@ export declare namespace base64 {
37
39
  }
38
40
 
39
41
  export declare namespace events {
40
- function await_callback(
42
+ function await_callback<ResponseType = unknown>(
41
43
  callback: {
42
44
  url: string
43
45
  },
44
46
  timeout?: number,
45
- ): object
47
+ ): {
48
+ http_request: {
49
+ body: ResponseType | null
50
+ headers: Record<string, string>
51
+ method: string
52
+ query: Record<string, string>
53
+ url: string
54
+ }
55
+ received_time: string
56
+ type: string
57
+ }
46
58
  function create_callback_endpoint(http_callback_method: string): {
47
59
  url: string
48
60
  }
49
61
  }
50
62
 
63
+ export declare namespace hash {
64
+ export function compute_checksum(data: bytes, algorithm: string): bytes
65
+ export function compute_hmac(
66
+ key: bytes,
67
+ data: bytes,
68
+ algorithm: string,
69
+ ): bytes
70
+ }
71
+
51
72
  export declare namespace http {
52
73
  export function default_retry(errormap: Record<string, any>): void
53
74
  export function default_retry_non_idempotent(
@@ -59,7 +80,7 @@ export declare namespace http {
59
80
  export function default_retry_predicate_non_idempotent(
60
81
  errormap: Record<string, any>,
61
82
  ): boolean
62
- function _delete(
83
+ function _delete<ResponseType = unknown>(
63
84
  url: string,
64
85
  timeout?: number,
65
86
  body?: unknown,
@@ -72,11 +93,11 @@ export declare namespace http {
72
93
  private_service_name?: string,
73
94
  ca_certificate?: string,
74
95
  ): {
75
- body: unknown
96
+ body: ResponseType
76
97
  code: number
77
98
  headers: Record<string, string>
78
99
  }
79
- export function get(
100
+ export function get<ResponseType = unknown>(
80
101
  url: string,
81
102
  timeout?: number,
82
103
  headers?: Record<string, string>,
@@ -88,11 +109,11 @@ export declare namespace http {
88
109
  private_service_name?: string,
89
110
  ca_certificate?: string,
90
111
  ): {
91
- body: unknown
112
+ body: ResponseType
92
113
  code: number
93
114
  headers: Record<string, string>
94
115
  }
95
- export function patch(
116
+ export function patch<ResponseType = unknown>(
96
117
  url: string,
97
118
  timeout?: number,
98
119
  body?: unknown,
@@ -105,11 +126,11 @@ export declare namespace http {
105
126
  private_service_name?: string,
106
127
  ca_certificate?: string,
107
128
  ): {
108
- body: unknown
129
+ body: ResponseType
109
130
  code: number
110
131
  headers: Record<string, string>
111
132
  }
112
- export function post(
133
+ export function post<ResponseType = unknown>(
113
134
  url: string,
114
135
  timeout?: number,
115
136
  body?: unknown,
@@ -122,11 +143,11 @@ export declare namespace http {
122
143
  private_service_name?: string,
123
144
  ca_certificate?: string,
124
145
  ): {
125
- body: unknown
146
+ body: ResponseType
126
147
  code: number
127
148
  headers: Record<string, string>
128
149
  }
129
- export function put(
150
+ export function put<ResponseType = unknown>(
130
151
  url: string,
131
152
  timeout?: number,
132
153
  body?: unknown,
@@ -139,11 +160,11 @@ export declare namespace http {
139
160
  private_service_name?: string,
140
161
  ca_certificate?: string,
141
162
  ): {
142
- body: unknown
163
+ body: ResponseType
143
164
  code: number
144
165
  headers: Record<string, string>
145
166
  }
146
- export function request(
167
+ export function request<ResponseType = unknown>(
147
168
  method: string,
148
169
  url: string,
149
170
  timeout?: number,
@@ -157,7 +178,7 @@ export declare namespace http {
157
178
  private_service_name?: string,
158
179
  ca_certificate?: string,
159
180
  ): {
160
- body: unknown
181
+ body: ResponseType
161
182
  code: number
162
183
  headers: Record<string, string>
163
184
  }
@@ -173,7 +194,8 @@ export declare namespace json {
173
194
  | boolean
174
195
  | unknown[]
175
196
  | Record<string, unknown>
176
- | null,
197
+ | null
198
+ | undefined,
177
199
  indent?:
178
200
  | boolean
179
201
  | {
@@ -188,7 +210,8 @@ export declare namespace json {
188
210
  | boolean
189
211
  | unknown[]
190
212
  | Record<string, unknown>
191
- | null,
213
+ | null
214
+ | undefined,
192
215
  indent?:
193
216
  | boolean
194
217
  | {
@@ -237,7 +260,8 @@ export declare namespace retry {
237
260
  }
238
261
 
239
262
  export declare namespace sys {
240
- function get_env(name: string, default_value?: string): string | undefined
263
+ function get_env(name: string): string | null
264
+ function get_env(name: string, default_value: string): string
241
265
  function log(
242
266
  data?: number | boolean | string | unknown[] | object,
243
267
  severity?: string,
@@ -716,7 +740,7 @@ export declare function retry_policy(
716
740
  | ((errormap: Record<string, any>) => void)
717
741
  | {
718
742
  predicate?: (errormap: Record<string, any>) => boolean
719
- max_retries: number | string | null
743
+ max_retries?: number | string | null
720
744
  backoff: {
721
745
  initial_delay?: number | string | null
722
746
  max_delay?: number | string | null