ts2workflows 0.9.0 → 0.11.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.
@@ -20,6 +20,14 @@ Semicolon can be used as optional statement delimitter.
20
20
  - Array: `[1, 2, 3]`
21
21
  - Map: `{temperature: -12, unit: "Celsius"}`
22
22
 
23
+ The Typescript type alias `WorkflowsValue` (provided by `ts2workflows/types/workflowslib`) represents any valid Workflows value. Sample usage:
24
+
25
+ ```typescript
26
+ import { WorkflowsValue } from 'ts2workflows/types/workflowslib'
27
+
28
+ const x: WorkflowsValue = [1, 2]
29
+ ```
30
+
23
31
  ### Array type
24
32
 
25
33
  ⚠️ Arrays are not objects in GCP Workflows. In particular, methods like `[].map()` and `[].concat()` are not available.
@@ -46,7 +54,7 @@ Note that `null` and `undefined` still are distinct types on the type checking s
46
54
 
47
55
  Expressions that combine variables with operators such as `+`, `>`, `==` perform implict type conversions according to the [rules listed on GCP Workflows documentation](https://cloud.google.com/workflows/docs/reference/syntax/datatypes#implicit-conversions). For example, applying `+` to a string and a number concatenates the values into a string.
48
56
 
49
- ⚠️ Checking if a variable is null or not must be done by an explicit comparison: `if (var != null) {...}`. Relying on an implicit conversion (`if (var) {...}` where `var` is not a boolean) results in a TypeError at runtime.
57
+ ⚠️ Checking if a variable is null or not must be done by an explicit comparison: `if (myVar != null) {...}`. Relying on an implicit conversion (`if (myVvar) {...}` where `myVar` is not a boolean) results in a TypeError at runtime.
50
58
 
51
59
  ## Expressions
52
60
 
@@ -156,6 +164,25 @@ String literals can include interpolated variables. The syntax is same as in Typ
156
164
 
157
165
  ⚠️ Interpolated values can (only) be numbers, strings, booleans or nulls. Other types will throw a TypeError at runtime.
158
166
 
167
+ ## Variable scopes
168
+
169
+ Variable scopes are determined by the [GCP Workflows scoping rules](https://cloud.google.com/workflows/docs/reference/syntax/variables#variable-scope).
170
+
171
+ Variables have function scope, that is they are available in the function where they are defined. Variables defined in `for` and `except` blocks are exceptions; they belong to a local scope of the block in which they are declared.
172
+
173
+ ⚠️ TypeScript has more strict scoping rules. In Typescript, variables declared with `let` or `const` are accessible only on the block in which they are declared (i.e. variable declared inside an if branch cannot be accessed after the if block ends). Programs written according to TypeScript scoping rules always produce valid GCP Workflows programs, too.
174
+
175
+ Trying to read a variable before it is assigned causes a runtime error.
176
+
177
+ ⚠️ If variable is not initialized at declaration, its value is implicitly set to `null`. For example, the following program will return `null`. (Note that the sample is not valid as a TypeScript program. TypeScript compiler consideres variable `x` to be used before it is assigned.)
178
+
179
+ ```typescript
180
+ function main(): void {
181
+ let x: number | null
182
+ return x
183
+ }
184
+ ```
185
+
159
186
  ## Subworkflow definitions
160
187
 
161
188
  ts2workflows converts Typescript `function`s to subworkflow definitions.
@@ -694,24 +721,25 @@ If an exception gets thrown inside a try block, the stack trace in Workflows log
694
721
 
695
722
  ## Retrying on errors
696
723
 
697
- It is possible to set a retry policy for a try-catch statement. Because Typescript does not have `retry` keyword, the retry is implemented by a special `retry_policy` intrinsic function. It must be called immediately after a try-catch block. `retry_policy` is ignored elsewhere.
724
+ It is possible to set a retry policy for a try-catch statement. Because Typescript does not have `retry` keyword, the retry is implemented by a `retry_policy` intrinsic function. It must be called inside a try block. `retry_policy` is ignored elsewhere. Only the first `retry_policy` in a try block has any effect.
698
725
 
699
726
  The arguments of `retry_policy` specify which errors are retried and how many times. The arguments can be either a policy provided by GCP Workflows or a custom retry policy as explained in the next sections.
700
727
 
701
- If an exception gets thrown in a try block and the retry policy covers the exception, the try block is executed again. Finally and catch blocks are run after possible retry attempts. The following sample retries `http.get()` if it throws an HTTP error and executes `sys.log('Error!')` and `closeConnection()` after retry attempts.
728
+ If an exception gets thrown in a try block and the retry policy covers the exception, the try block is executed again. Finally and catch blocks are run if the try block keeps failing after all retry attempts have been used up. The following sample retries `http.get()` if it throws an HTTP error covered by `http.default_retry`. It logs `sys.log('Error!')` if the number of retry attempts exceed retry policy's maximum retry attempt count. `closeConnection()` is run always regardless of whether the HTTP request succeeded or failed.
702
729
 
703
730
  ```javascript
704
731
  import { http, retry_policy, sys } from 'ts2workflows/types/workflowslib'
705
732
 
706
733
  function main() {
707
734
  try {
735
+ retry_policy(http.default_retry)
736
+
708
737
  http.get('https://visit.dreamland.test/')
709
738
  } catch (err) {
710
739
  sys.log('Error!')
711
740
  } finally {
712
741
  closeConnection()
713
742
  }
714
- retry_policy(http.default_retry)
715
743
  }
716
744
  ```
717
745
 
@@ -724,11 +752,12 @@ import { http, retry_policy } from 'ts2workflows/types/workflowslib'
724
752
 
725
753
  function main() {
726
754
  try {
755
+ retry_policy(http.default_retry)
756
+
727
757
  http.get('https://visit.dreamland.test/')
728
758
  } catch (err) {
729
759
  return 'Error!'
730
760
  }
731
- retry_policy(http.default_retry)
732
761
  }
733
762
  ```
734
763
 
@@ -743,19 +772,20 @@ import { http, retry_policy } from 'ts2workflows/types/workflowslib'
743
772
 
744
773
  function main() {
745
774
  try {
775
+ retry_policy({
776
+ predicate: http.default_retry_predicate,
777
+ max_retries: 3,
778
+ backoff: {
779
+ initial_delay: 0.5,
780
+ max_delay: 60,
781
+ multiplier: 2,
782
+ },
783
+ })
784
+
746
785
  http.get('https://visit.dreamland.test/')
747
786
  } catch (err) {
748
787
  return 'Error!'
749
788
  }
750
- retry_policy({
751
- predicate: http.default_retry_predicate,
752
- max_retries: 3,
753
- backoff: {
754
- initial_delay: 0.5,
755
- max_delay: 60,
756
- multiplier: 2,
757
- },
758
- })
759
789
  }
760
790
  ```
761
791
 
@@ -904,7 +934,7 @@ function retry_policy(
904
934
  ): void
905
935
  ```
906
936
 
907
- A retry policy can be attached to a `try`-`catch` block be calling `retry_policy` as the next statement after the `try`-`catch`. ts2workflows ignores `retry_policy` everywhere else except after a `try`-`catch`.
937
+ A retry policy can be attached to a `try`-`catch` block by calling `retry_policy` inside the `try` block. ts2workflows ignores `retry_policy` everywhere else.
908
938
 
909
939
  See the section on [retrying errors](#retrying-on-errors).
910
940
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts2workflows",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Transpile Typescript code to GCP Workflows programs",
5
5
  "homepage": "https://github.com/aajanki/ts2workflows",
6
6
  "repository": {
@@ -12,7 +12,7 @@
12
12
  "types": "dist/index.d.ts",
13
13
  "type": "module",
14
14
  "engines": {
15
- "node": ">=18"
15
+ "node": ">=20"
16
16
  },
17
17
  "scripts": {
18
18
  "build": "rimraf dist && npm run build:functionmetadata && tsc",
@@ -20,6 +20,7 @@
20
20
  "lint": "eslint src test scripts",
21
21
  "format": "prettier . --write",
22
22
  "test": "mocha",
23
+ "test-coverage": "nyc mocha",
23
24
  "prepare": "husky && npm run build"
24
25
  },
25
26
  "lint-staged": {
@@ -61,25 +62,26 @@
61
62
  "@eslint/js": "^9.10.0",
62
63
  "@types/chai": "^5.0.1",
63
64
  "@types/mocha": "^10.0.6",
64
- "@types/node": "^18",
65
+ "@types/node": "^20",
65
66
  "@types/ramda": "^0.30.2",
66
67
  "@typescript-eslint/eslint-plugin": "^8.0.0",
67
68
  "@typescript-eslint/parser": "^8.0.0",
68
69
  "chai": "^5.1.1",
69
70
  "eslint": "^9.10.0",
70
71
  "husky": "^9.1.6",
71
- "lint-staged": "^15.2.10",
72
+ "lint-staged": "^16.1.2",
72
73
  "mocha": "^11.1.0",
74
+ "nyc": "^17.1.0",
73
75
  "prettier": "^3.2.5",
74
- "rimraf": "^5.0.10",
75
- "tsx": "^4.10.2",
76
+ "rimraf": "^6.0.1",
77
+ "tsx": "~4.19.4",
76
78
  "typescript-eslint": "^8.0.0"
77
79
  },
78
80
  "dependencies": {
79
81
  "@typescript-eslint/typescript-estree": "^8.0.0",
80
- "commander": "^13.1.0",
81
- "ramda": "^0.30.1",
82
- "typescript": "^5.0.0",
82
+ "commander": "^14.0.0",
83
+ "ramda": "^0.31.3",
84
+ "typescript": "^5.4.0",
83
85
  "yaml": "^2.4.2"
84
86
  }
85
87
  }
@@ -10,6 +10,28 @@ export interface bytes {
10
10
  readonly [__bytes_tag]: 'bytes'
11
11
  }
12
12
 
13
+ // GCP Workflows data types
14
+ export type WorkflowsValue =
15
+ | boolean
16
+ | number
17
+ | string
18
+ | bytes
19
+ | WorkflowsValue[]
20
+ | { [key: string]: WorkflowsValue }
21
+ | null
22
+
23
+ type BooleanNumberStringListOrDict =
24
+ | boolean
25
+ | number
26
+ | string
27
+ | BooleanNumberStringListOrDict[]
28
+ | { [key: string]: BooleanNumberStringListOrDict }
29
+
30
+ type HTTPQuery = Record<
31
+ string,
32
+ string | number | boolean | (string | number | boolean)[]
33
+ >
34
+
13
35
  // GCP Workflows expression helpers
14
36
 
15
37
  export declare function double(x: string | number): number
@@ -19,19 +41,10 @@ export declare function keys(map: Record<string, unknown>): string[]
19
41
  export declare function len(
20
42
  value: unknown[] | Record<string, unknown> | string,
21
43
  ): number
22
- export declare function get_type(
23
- value:
24
- | boolean
25
- | number
26
- | string
27
- | unknown[]
28
- | Record<string, unknown>
29
- | null
30
- | bytes
31
- | undefined,
32
- ): string
44
+ export declare function get_type(value: unknown): string
33
45
 
34
46
  // GCP Workflows standard library functions
47
+ // https://cloud.google.com/workflows/docs/reference/stdlib/overview
35
48
 
36
49
  export declare namespace base64 {
37
50
  function decode(data: bytes, padding?: boolean): string
@@ -39,7 +52,7 @@ export declare namespace base64 {
39
52
  }
40
53
 
41
54
  export declare namespace events {
42
- function await_callback<ResponseType = unknown>(
55
+ function await_callback<ResponseType = WorkflowsValue>(
43
56
  callback: {
44
57
  url: string
45
58
  },
@@ -80,15 +93,12 @@ export declare namespace http {
80
93
  export function default_retry_predicate_non_idempotent(
81
94
  errormap: Record<string, any>,
82
95
  ): boolean
83
- function _delete<ResponseType = unknown>(
96
+ function _delete<ResponseType = WorkflowsValue>(
84
97
  url: string,
85
98
  timeout?: number,
86
- body?: unknown,
99
+ body?: any,
87
100
  headers?: Record<string, string>,
88
- query?: Record<
89
- string,
90
- string | number | boolean | (string | number | boolean)[]
91
- >,
101
+ query?: HTTPQuery,
92
102
  auth?: Record<string, string>,
93
103
  private_service_name?: string,
94
104
  ca_certificate?: string,
@@ -97,14 +107,11 @@ export declare namespace http {
97
107
  code: number
98
108
  headers: Record<string, string>
99
109
  }
100
- export function get<ResponseType = unknown>(
110
+ export function get<ResponseType = WorkflowsValue>(
101
111
  url: string,
102
112
  timeout?: number,
103
113
  headers?: Record<string, string>,
104
- query?: Record<
105
- string,
106
- string | number | boolean | (string | number | boolean)[]
107
- >,
114
+ query?: HTTPQuery,
108
115
  auth?: Record<string, string>,
109
116
  private_service_name?: string,
110
117
  ca_certificate?: string,
@@ -113,10 +120,10 @@ export declare namespace http {
113
120
  code: number
114
121
  headers: Record<string, string>
115
122
  }
116
- export function patch<ResponseType = unknown>(
123
+ export function patch<ResponseType = WorkflowsValue>(
117
124
  url: string,
118
125
  timeout?: number,
119
- body?: unknown,
126
+ body?: any,
120
127
  headers?: Record<string, string>,
121
128
  query?: Record<
122
129
  string,
@@ -130,15 +137,12 @@ export declare namespace http {
130
137
  code: number
131
138
  headers: Record<string, string>
132
139
  }
133
- export function post<ResponseType = unknown>(
140
+ export function post<ResponseType = WorkflowsValue>(
134
141
  url: string,
135
142
  timeout?: number,
136
- body?: unknown,
143
+ body?: any,
137
144
  headers?: Record<string, string>,
138
- query?: Record<
139
- string,
140
- string | number | boolean | (string | number | boolean)[]
141
- >,
145
+ query?: HTTPQuery,
142
146
  auth?: Record<string, string>,
143
147
  private_service_name?: string,
144
148
  ca_certificate?: string,
@@ -147,15 +151,12 @@ export declare namespace http {
147
151
  code: number
148
152
  headers: Record<string, string>
149
153
  }
150
- export function put<ResponseType = unknown>(
154
+ export function put<ResponseType = WorkflowsValue>(
151
155
  url: string,
152
156
  timeout?: number,
153
- body?: unknown,
157
+ body?: any,
154
158
  headers?: Record<string, string>,
155
- query?: Record<
156
- string,
157
- string | number | boolean | (string | number | boolean)[]
158
- >,
159
+ query?: HTTPQuery,
159
160
  auth?: Record<string, string>,
160
161
  private_service_name?: string,
161
162
  ca_certificate?: string,
@@ -164,16 +165,13 @@ export declare namespace http {
164
165
  code: number
165
166
  headers: Record<string, string>
166
167
  }
167
- export function request<ResponseType = unknown>(
168
+ export function request<ResponseType = WorkflowsValue>(
168
169
  method: string,
169
170
  url: string,
170
171
  timeout?: number,
171
- body?: unknown,
172
+ body?: any,
172
173
  headers?: Record<string, string>,
173
- query?: Record<
174
- string,
175
- string | number | boolean | (string | number | boolean)[]
176
- >,
174
+ query?: HTTPQuery,
177
175
  auth?: Record<string, string>,
178
176
  private_service_name?: string,
179
177
  ca_certificate?: string,
@@ -186,16 +184,9 @@ export declare namespace http {
186
184
  }
187
185
 
188
186
  export declare namespace json {
189
- function decode(data: bytes | string): unknown
187
+ function decode(data: bytes | string): WorkflowsValue
190
188
  function encode(
191
- data:
192
- | string
193
- | number
194
- | boolean
195
- | unknown[]
196
- | Record<string, unknown>
197
- | null
198
- | undefined,
189
+ data: unknown,
199
190
  indent?:
200
191
  | boolean
201
192
  | {
@@ -204,14 +195,7 @@ export declare namespace json {
204
195
  },
205
196
  ): bytes
206
197
  function encode_to_string(
207
- data:
208
- | string
209
- | number
210
- | boolean
211
- | unknown[]
212
- | Record<string, unknown>
213
- | null
214
- | undefined,
198
+ data: unknown,
215
199
  indent?:
216
200
  | boolean
217
201
  | {
@@ -228,10 +212,10 @@ export declare namespace list {
228
212
 
229
213
  export declare namespace map {
230
214
  function _delete<T>(map: Record<string, T>, key: string): Record<string, T>
231
- export function get<T, K extends string | string[]>(
232
- map: Record<string, T>,
233
- keys: K,
234
- ): K extends string ? T | null : unknown
215
+ // map.get() with a string key, returns a property value or null
216
+ export function get<T>(map: Record<string, T>, keys: string): T | null
217
+ // map.get() with string[] key or non-object lookup, the return type is unknown
218
+ export function get(map: any, keys: string | string[]): WorkflowsValue
235
219
  export function merge<T, U>(
236
220
  first: Record<string, T>,
237
221
  second: Record<string, U>,
@@ -250,23 +234,23 @@ export declare namespace math {
250
234
  }
251
235
 
252
236
  export declare namespace retry {
253
- function always(exception: unknown): void
237
+ function always(exception: any): void
254
238
  const default_backoff: {
255
239
  initial_delay: number
256
240
  max_delay: number
257
241
  multiplier: number
258
242
  }
259
- function never(exception: unknown): void
243
+ function never(exception: any): void
260
244
  }
261
245
 
262
246
  export declare namespace sys {
263
247
  function get_env(name: string): string | null
264
248
  function get_env(name: string, default_value: string): string
265
249
  function log(
266
- data?: number | boolean | string | unknown[] | object,
250
+ data?: BooleanNumberStringListOrDict,
267
251
  severity?: string,
268
- text?: number | boolean | string | unknown[] | object,
269
- json?: object,
252
+ text?: BooleanNumberStringListOrDict,
253
+ json?: Record<string, any>,
270
254
  timeout?: number,
271
255
  ): void
272
256
  function now(): number
@@ -456,9 +440,9 @@ export declare namespace googleapis {
456
440
  interface GoogleLongrunningOperation {
457
441
  done: boolean
458
442
  error?: Status
459
- metadata?: Record<string, unknown>
443
+ metadata?: Record<string, any>
460
444
  name: string
461
- response: Record<string, unknown>
445
+ response: Record<string, any>
462
446
  }
463
447
  interface GoogleLongrunningListOperationsResponse {
464
448
  nextPageToken?: string
@@ -476,7 +460,7 @@ export declare namespace googleapis {
476
460
  displayName: string
477
461
  labels: Record<string, string>
478
462
  locationId: string
479
- metadata?: Record<string, unknown>
463
+ metadata?: Record<string, any>
480
464
  name: string
481
465
  }
482
466
  interface ListCollectionIdsRequest {
@@ -538,7 +522,7 @@ export declare namespace googleapis {
538
522
  }
539
523
  interface Status {
540
524
  code: number
541
- details: Record<string, unknown>[]
525
+ details: Record<string, any>[]
542
526
  message: string
543
527
  }
544
528
  interface StructuredQuery {
@@ -751,5 +735,5 @@ export declare function retry_policy(
751
735
 
752
736
  export declare function call_step<T, A extends any[]>(
753
737
  func: (...args: A) => T,
754
- arguments: Record<string, unknown>,
738
+ arguments: Record<string, any>,
755
739
  ): T
@@ -1,20 +0,0 @@
1
- import { WorkflowApp } from './workflows.js';
2
- export declare class WorkflowValidationError extends Error {
3
- issues: WorkflowIssue[];
4
- constructor(issues: WorkflowIssue[]);
5
- }
6
- export interface WorkflowIssue {
7
- type: string;
8
- message: string;
9
- }
10
- /**
11
- * Execute all syntax validators on a WorkflowApp app.
12
- *
13
- * Throws a WorkflowValidationError if there are errors.
14
- */
15
- export declare function validate(app: WorkflowApp, disabled?: string[]): void;
16
- /**
17
- * Returns all validator names.
18
- */
19
- export declare function validatorNames(): string[];
20
- //# sourceMappingURL=validation.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/ast/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEzD,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,MAAM,EAAE,aAAa,EAAE,CAAA;gBAEX,MAAM,EAAE,aAAa,EAAE;CAMpC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAWD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,WAAW,EAAE,QAAQ,GAAE,MAAM,EAAO,GAAG,IAAI,CAexE;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAEzC"}