ts2workflows 0.1.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.
Files changed (50) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +82 -0
  3. package/dist/ast/expressions.d.ts +57 -0
  4. package/dist/ast/expressions.d.ts.map +1 -0
  5. package/dist/ast/expressions.js +300 -0
  6. package/dist/ast/stepnames.d.ts +9 -0
  7. package/dist/ast/stepnames.d.ts.map +1 -0
  8. package/dist/ast/stepnames.js +268 -0
  9. package/dist/ast/steps.d.ts +176 -0
  10. package/dist/ast/steps.d.ts.map +1 -0
  11. package/dist/ast/steps.js +534 -0
  12. package/dist/ast/validation.d.ts +20 -0
  13. package/dist/ast/validation.d.ts.map +1 -0
  14. package/dist/ast/validation.js +214 -0
  15. package/dist/ast/workflows.d.ts +28 -0
  16. package/dist/ast/workflows.d.ts.map +1 -0
  17. package/dist/ast/workflows.js +74 -0
  18. package/dist/cli.d.ts +3 -0
  19. package/dist/cli.d.ts.map +1 -0
  20. package/dist/cli.js +114 -0
  21. package/dist/errors.d.ts +18 -0
  22. package/dist/errors.d.ts.map +1 -0
  23. package/dist/errors.js +12 -0
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +2 -0
  27. package/dist/transpiler/asserts.d.ts +7 -0
  28. package/dist/transpiler/asserts.d.ts.map +1 -0
  29. package/dist/transpiler/asserts.js +11 -0
  30. package/dist/transpiler/expressions.d.ts +6 -0
  31. package/dist/transpiler/expressions.d.ts.map +1 -0
  32. package/dist/transpiler/expressions.js +223 -0
  33. package/dist/transpiler/generated/functionMetadata.d.ts +2 -0
  34. package/dist/transpiler/generated/functionMetadata.d.ts.map +1 -0
  35. package/dist/transpiler/generated/functionMetadata.js +324 -0
  36. package/dist/transpiler/index.d.ts +2 -0
  37. package/dist/transpiler/index.d.ts.map +1 -0
  38. package/dist/transpiler/index.js +74 -0
  39. package/dist/transpiler/statements.d.ts +7 -0
  40. package/dist/transpiler/statements.d.ts.map +1 -0
  41. package/dist/transpiler/statements.js +533 -0
  42. package/dist/transpiler/transformations.d.ts +28 -0
  43. package/dist/transpiler/transformations.d.ts.map +1 -0
  44. package/dist/transpiler/transformations.js +461 -0
  45. package/dist/utils.d.ts +2 -0
  46. package/dist/utils.d.ts.map +1 -0
  47. package/dist/utils.js +3 -0
  48. package/language_reference.md +771 -0
  49. package/package.json +62 -0
  50. package/types/workflowslib.d.ts +714 -0
@@ -0,0 +1,534 @@
1
+ import { isRecord } from '../utils.js';
2
+ import { expressionToLiteralValueOrLiteralExpression, } from './expressions.js';
3
+ import { Subworkflow } from './workflows.js';
4
+ export class SubworkflowAST {
5
+ name;
6
+ steps;
7
+ params;
8
+ constructor(name, steps, params) {
9
+ this.name = name;
10
+ this.steps = steps;
11
+ this.params = params;
12
+ }
13
+ withStepNames(generate) {
14
+ const steps = this.steps.map((step) => namedSteps(step, generate));
15
+ return new Subworkflow(this.name, steps, this.params);
16
+ }
17
+ }
18
+ // https://cloud.google.com/workflows/docs/reference/syntax/variables#assign-step
19
+ export class AssignStepAST {
20
+ tag = 'assign';
21
+ assignments;
22
+ label;
23
+ constructor(assignments, label) {
24
+ this.assignments = assignments;
25
+ this.label = label;
26
+ }
27
+ }
28
+ // https://cloud.google.com/workflows/docs/reference/syntax/calls
29
+ export class CallStepAST {
30
+ tag = 'call';
31
+ call;
32
+ args;
33
+ result;
34
+ label;
35
+ constructor(call, args, result, label) {
36
+ this.call = call;
37
+ this.args = args;
38
+ this.result = result;
39
+ this.label = label;
40
+ }
41
+ labelPrefix() {
42
+ return 'call_' + this.call.replaceAll(/[^a-zA-Z0-9]/g, '_') + '_';
43
+ }
44
+ }
45
+ // https://cloud.google.com/workflows/docs/reference/syntax/iteration
46
+ export class ForStepAST {
47
+ tag = 'for';
48
+ steps;
49
+ loopVariableName;
50
+ indexVariableName;
51
+ listExpression;
52
+ rangeStart;
53
+ rangeEnd;
54
+ label;
55
+ constructor(steps, loopVariableName, listExpression, indexVariable, rangeStart, rangeEnd, label) {
56
+ this.steps = steps;
57
+ this.loopVariableName = loopVariableName;
58
+ this.listExpression = listExpression;
59
+ this.indexVariableName = indexVariable;
60
+ this.rangeStart = rangeStart;
61
+ this.rangeEnd = rangeEnd;
62
+ this.label = label;
63
+ }
64
+ }
65
+ export class ForStepASTNamed {
66
+ tag = 'for';
67
+ steps;
68
+ loopVariableName;
69
+ indexVariableName;
70
+ listExpression;
71
+ rangeStart;
72
+ rangeEnd;
73
+ constructor(steps, loopVariableName, listExpression, indexVariable, rangeStart, rangeEnd) {
74
+ this.steps = steps;
75
+ this.loopVariableName = loopVariableName;
76
+ this.listExpression = listExpression;
77
+ this.indexVariableName = indexVariable;
78
+ this.rangeStart = rangeStart;
79
+ this.rangeEnd = rangeEnd;
80
+ }
81
+ }
82
+ export class NextStepAST {
83
+ tag = 'next';
84
+ target;
85
+ label;
86
+ constructor(target, label) {
87
+ this.target = target;
88
+ this.label = label;
89
+ }
90
+ }
91
+ // https://cloud.google.com/workflows/docs/reference/syntax/parallel-steps
92
+ export class ParallelStepAST {
93
+ tag = 'parallel';
94
+ steps;
95
+ shared;
96
+ concurrencyLimit;
97
+ exceptionPolicy;
98
+ label;
99
+ constructor(steps, shared, concurrencyLimit, exceptionPolicy, label) {
100
+ this.steps = steps;
101
+ this.shared = shared;
102
+ this.concurrencyLimit = concurrencyLimit;
103
+ this.exceptionPolicy = exceptionPolicy;
104
+ this.label = label;
105
+ }
106
+ }
107
+ export class ParallelStepASTNamed {
108
+ tag = 'parallel';
109
+ branches; // Either steps for each branch
110
+ forStep; // ... or a parallel for
111
+ shared;
112
+ concurrenceLimit;
113
+ exceptionPolicy;
114
+ constructor(steps, shared, concurrencyLimit, exceptionPolicy) {
115
+ this.shared = shared;
116
+ this.concurrenceLimit = concurrencyLimit;
117
+ this.exceptionPolicy = exceptionPolicy;
118
+ if (!isRecord(steps)) {
119
+ this.forStep = steps;
120
+ }
121
+ else {
122
+ this.branches = Object.entries(steps).map((x) => {
123
+ return { name: x[0], step: x[1] };
124
+ });
125
+ }
126
+ }
127
+ }
128
+ // https://cloud.google.com/workflows/docs/reference/syntax/raising-errors
129
+ export class RaiseStepAST {
130
+ tag = 'raise';
131
+ value;
132
+ label;
133
+ constructor(value, label) {
134
+ this.value = value;
135
+ this.label = label;
136
+ }
137
+ }
138
+ // https://cloud.google.com/workflows/docs/reference/syntax/completing
139
+ export class ReturnStepAST {
140
+ tag = 'return';
141
+ value;
142
+ label;
143
+ constructor(value, label) {
144
+ this.value = value;
145
+ this.label = label;
146
+ }
147
+ }
148
+ // https://cloud.google.com/workflows/docs/reference/syntax/steps#embedded-steps
149
+ export class StepsStepAST {
150
+ tag = 'steps';
151
+ steps;
152
+ label;
153
+ constructor(steps, label) {
154
+ this.steps = steps;
155
+ this.label = label;
156
+ }
157
+ }
158
+ export class StepsStepASTNamed {
159
+ tag = 'steps';
160
+ steps;
161
+ constructor(steps) {
162
+ this.steps = steps;
163
+ }
164
+ }
165
+ // https://cloud.google.com/workflows/docs/reference/syntax/conditions
166
+ export class SwitchStepAST {
167
+ tag = 'switch';
168
+ branches;
169
+ label;
170
+ constructor(branches, label) {
171
+ this.branches = branches;
172
+ this.label = label;
173
+ }
174
+ }
175
+ export class SwitchStepASTNamed {
176
+ tag = 'switch';
177
+ conditions;
178
+ next;
179
+ constructor(conditions, next) {
180
+ this.conditions = conditions;
181
+ this.next = next;
182
+ }
183
+ }
184
+ // https://cloud.google.com/workflows/docs/reference/syntax/catching-errors
185
+ export class TryStepAST {
186
+ tag = 'try';
187
+ trySteps;
188
+ exceptSteps;
189
+ retryPolicy;
190
+ errorMap;
191
+ label;
192
+ constructor(trySteps, exceptSteps, retryPolicy, errorMap, label) {
193
+ this.trySteps = trySteps;
194
+ this.exceptSteps = exceptSteps;
195
+ this.retryPolicy = retryPolicy;
196
+ this.errorMap = errorMap;
197
+ this.label = label;
198
+ }
199
+ }
200
+ export class TryStepASTNamed {
201
+ tag = 'try';
202
+ retryPolicy;
203
+ errorMap;
204
+ // Steps in the try block
205
+ trySteps;
206
+ // Steps in the except block
207
+ exceptSteps;
208
+ constructor(steps, exceptSteps, retryPolicy, errorMap) {
209
+ this.trySteps = steps;
210
+ this.retryPolicy = retryPolicy;
211
+ this.errorMap = errorMap;
212
+ this.exceptSteps = exceptSteps;
213
+ }
214
+ }
215
+ // Internal step that represents a potential jump target.
216
+ // This can be ued as a placeholder when the actual target step is not yet known.
217
+ // JumpTargetAST is removed before transpiling to workflows YAML.
218
+ export class JumpTargetAST {
219
+ tag = 'jumptarget';
220
+ label;
221
+ constructor() {
222
+ this.label = `jumptarget_${Math.floor(Math.random() * 2 ** 32).toString(16)}`;
223
+ }
224
+ }
225
+ /**
226
+ * Assign a name for this step and its child steps.
227
+ *
228
+ * The name is generated by calling generateName with a prefix identifying the step type.
229
+ */
230
+ export function namedSteps(step, generateName) {
231
+ switch (step.tag) {
232
+ case 'assign':
233
+ case 'next':
234
+ case 'raise':
235
+ case 'return':
236
+ return {
237
+ name: step.label ?? generateName(step.tag),
238
+ step,
239
+ };
240
+ case 'call':
241
+ return {
242
+ name: step.label ?? generateName(step.labelPrefix()),
243
+ step,
244
+ };
245
+ case 'for':
246
+ return namedStepsFor(step, generateName);
247
+ case 'parallel':
248
+ return namedStepsParallel(step, generateName);
249
+ case 'steps':
250
+ return namedStepsSteps(step, generateName);
251
+ case 'switch':
252
+ return namedStepsSwitch(step, generateName);
253
+ case 'try':
254
+ return namedStepsTry(step, generateName);
255
+ case 'jumptarget':
256
+ return {
257
+ name: step.label,
258
+ step: step,
259
+ };
260
+ }
261
+ }
262
+ function namedStepsFor(step, generateName) {
263
+ return {
264
+ name: step.label ?? generateName('for'),
265
+ step: new ForStepASTNamed(step.steps.map((nestedStep) => namedSteps(nestedStep, generateName)), step.loopVariableName, step.listExpression),
266
+ };
267
+ }
268
+ function namedStepsParallel(step, generateName) {
269
+ let steps;
270
+ if (!isRecord(step.steps)) {
271
+ const forStep = namedSteps(step.steps, generateName).step;
272
+ if (forStep.tag !== 'for') {
273
+ throw new Error(`Encountered a step of type ${forStep.tag} when a for step was expected`);
274
+ }
275
+ steps = forStep;
276
+ }
277
+ else {
278
+ steps = Object.fromEntries(Object.entries(step.steps).map(([name, nested]) => {
279
+ const named = nested.steps.map((x) => namedSteps(x, generateName));
280
+ return [name, new StepsStepASTNamed(named)];
281
+ }));
282
+ }
283
+ return {
284
+ name: step.label ?? generateName('parallel'),
285
+ step: new ParallelStepASTNamed(steps, step.shared, step.concurrencyLimit, step.exceptionPolicy),
286
+ };
287
+ }
288
+ function namedStepsSteps(step, generateName) {
289
+ return {
290
+ name: step.label ?? generateName('steps'),
291
+ step: new StepsStepASTNamed(step.steps.map((nested) => namedSteps(nested, generateName))),
292
+ };
293
+ }
294
+ function namedStepsSwitch(step, generateName) {
295
+ const namedBranches = step.branches.map((branch) => ({
296
+ condition: branch.condition,
297
+ steps: branch.steps.map((nested) => namedSteps(nested, generateName)),
298
+ next: branch.next,
299
+ }));
300
+ return {
301
+ name: step.label ?? generateName('switch'),
302
+ step: new SwitchStepASTNamed(namedBranches),
303
+ };
304
+ }
305
+ function namedStepsTry(step, generateName) {
306
+ const namedTrySteps = step.trySteps.map((nested) => namedSteps(nested, generateName));
307
+ const namedExceptSteps = step.exceptSteps.map((nested) => namedSteps(nested, generateName));
308
+ return {
309
+ name: step.label ?? generateName('try'),
310
+ step: new TryStepASTNamed(namedTrySteps, namedExceptSteps, step.retryPolicy, step.errorMap),
311
+ };
312
+ }
313
+ /**
314
+ * Returns the nested steps in contained in this step.
315
+ *
316
+ * Used in iterating the AST tree.
317
+ */
318
+ export function nestedSteps(step) {
319
+ switch (step.tag) {
320
+ case 'assign':
321
+ case 'call':
322
+ case 'next':
323
+ case 'raise':
324
+ case 'return':
325
+ case 'jumptarget':
326
+ return [];
327
+ case 'for':
328
+ case 'steps':
329
+ return [step.steps];
330
+ case 'parallel':
331
+ return nestedStepsParallel(step);
332
+ case 'switch':
333
+ return step.conditions.map((x) => x.steps);
334
+ case 'try':
335
+ return nestedStepsTry(step);
336
+ }
337
+ }
338
+ function nestedStepsParallel(step) {
339
+ const nested = [];
340
+ if (step.branches && step.branches.length > 0) {
341
+ // return each branch as a separate child
342
+ nested.push(...step.branches.map((x) => [x]));
343
+ }
344
+ if (step.forStep?.steps && step.forStep.steps.length > 0) {
345
+ nested.push(step.forStep.steps);
346
+ }
347
+ return nested;
348
+ }
349
+ function nestedStepsTry(step) {
350
+ const nested = [];
351
+ if (step.trySteps.length > 0) {
352
+ nested.push(step.trySteps);
353
+ }
354
+ if (step.exceptSteps.length > 0) {
355
+ nested.push(step.exceptSteps);
356
+ }
357
+ return nested;
358
+ }
359
+ /**
360
+ * Returns an GCP Workflows object representation of a step.
361
+ */
362
+ export function renderStep(step) {
363
+ switch (step.tag) {
364
+ case 'assign':
365
+ return {
366
+ assign: step.assignments.map(([key, val]) => {
367
+ return { [key]: expressionToLiteralValueOrLiteralExpression(val) };
368
+ }),
369
+ };
370
+ case 'call':
371
+ return renderCallStep(step);
372
+ case 'for':
373
+ return {
374
+ for: renderForBody(step),
375
+ };
376
+ case 'parallel':
377
+ return {
378
+ parallel: {
379
+ ...(step.shared && { shared: step.shared }),
380
+ ...(step.concurrenceLimit && {
381
+ concurrency_limit: step.concurrenceLimit,
382
+ }),
383
+ ...(step.exceptionPolicy && {
384
+ exception_policy: step.exceptionPolicy,
385
+ }),
386
+ ...(step.branches && { branches: renderSteps(step.branches) }),
387
+ ...(step.forStep && { for: renderForBody(step.forStep) }),
388
+ },
389
+ };
390
+ case 'next':
391
+ return {
392
+ next: step.target,
393
+ };
394
+ case 'raise':
395
+ return {
396
+ raise: expressionToLiteralValueOrLiteralExpression(step.value),
397
+ };
398
+ case 'return':
399
+ return renderReturnStep(step);
400
+ case 'steps':
401
+ return {
402
+ steps: renderSteps(step.steps),
403
+ };
404
+ case 'switch':
405
+ return {
406
+ switch: step.conditions.map(renderSwitchCondition),
407
+ ...(step.next && { next: step.next }),
408
+ };
409
+ case 'try':
410
+ return renderTryStep(step);
411
+ case 'jumptarget':
412
+ return {};
413
+ }
414
+ }
415
+ function renderSwitchCondition(cond) {
416
+ return {
417
+ condition: expressionToLiteralValueOrLiteralExpression(cond.condition),
418
+ ...(cond.steps.length > 0 && { steps: renderSteps(cond.steps) }),
419
+ ...(cond.next && { next: cond.next }),
420
+ };
421
+ }
422
+ function renderCallStep(step) {
423
+ let args = undefined;
424
+ if (step.args) {
425
+ args = Object.fromEntries(Object.entries(step.args).map(([k, v]) => {
426
+ return [k, expressionToLiteralValueOrLiteralExpression(v)];
427
+ }));
428
+ }
429
+ return {
430
+ call: step.call,
431
+ ...(args && { args }),
432
+ ...(step.result && { result: step.result }),
433
+ };
434
+ }
435
+ function renderForBody(step) {
436
+ let range;
437
+ let inValue;
438
+ if (typeof step.listExpression === 'undefined') {
439
+ range = [step.rangeStart, step.rangeEnd];
440
+ inValue = undefined;
441
+ }
442
+ else {
443
+ inValue = expressionToLiteralValueOrLiteralExpression(step.listExpression);
444
+ range = undefined;
445
+ }
446
+ return {
447
+ value: step.loopVariableName,
448
+ ...(step.indexVariableName && { index: step.indexVariableName }),
449
+ ...(inValue && { in: inValue }),
450
+ ...(range && { range }),
451
+ steps: renderSteps(step.steps),
452
+ };
453
+ }
454
+ function renderReturnStep(step) {
455
+ if (step.value) {
456
+ return {
457
+ return: expressionToLiteralValueOrLiteralExpression(step.value),
458
+ };
459
+ }
460
+ else {
461
+ return {
462
+ next: 'end',
463
+ };
464
+ }
465
+ }
466
+ function renderTryStep(step) {
467
+ let retry;
468
+ if (typeof step.retryPolicy === 'undefined') {
469
+ retry = undefined;
470
+ }
471
+ else if (typeof step.retryPolicy === 'string') {
472
+ retry = `\${${step.retryPolicy}}`;
473
+ }
474
+ else {
475
+ const predicateName = step.retryPolicy.predicate;
476
+ retry = {
477
+ predicate: `\${${predicateName}}`,
478
+ max_retries: step.retryPolicy.maxRetries,
479
+ backoff: {
480
+ initial_delay: step.retryPolicy.backoff.initialDelay,
481
+ max_delay: step.retryPolicy.backoff.maxDelay,
482
+ multiplier: step.retryPolicy.backoff.multiplier,
483
+ },
484
+ };
485
+ }
486
+ let except;
487
+ if (step.exceptSteps.length > 0) {
488
+ except = {
489
+ as: step.errorMap,
490
+ steps: renderSteps(step.exceptSteps),
491
+ };
492
+ }
493
+ else {
494
+ except = undefined;
495
+ }
496
+ return {
497
+ try: {
498
+ steps: renderSteps(step.trySteps),
499
+ },
500
+ ...(retry && { retry }),
501
+ ...(except && { except }),
502
+ };
503
+ }
504
+ function renderSteps(steps) {
505
+ return steps.map((x) => {
506
+ return { [x.name]: renderStep(x.step) };
507
+ });
508
+ }
509
+ export function stepWithLabel(step, label) {
510
+ switch (step.tag) {
511
+ case 'assign':
512
+ return new AssignStepAST(step.assignments, label);
513
+ case 'call':
514
+ return new CallStepAST(step.call, step.args, step.result, label);
515
+ case 'for':
516
+ return new ForStepAST(step.steps, step.loopVariableName, step.listExpression, step.indexVariableName, step.rangeStart, step.rangeEnd, label);
517
+ case 'next':
518
+ return new NextStepAST(step.target, label);
519
+ case 'parallel':
520
+ return new ParallelStepAST(step.steps, step.shared, step.concurrencyLimit, step.exceptionPolicy, label);
521
+ case 'raise':
522
+ return new RaiseStepAST(step.value, label);
523
+ case 'return':
524
+ return new ReturnStepAST(step.value, label);
525
+ case 'steps':
526
+ return new StepsStepAST(step.steps, label);
527
+ case 'switch':
528
+ return new SwitchStepAST(step.branches, label);
529
+ case 'try':
530
+ return new TryStepAST(step.trySteps, step.exceptSteps, step.retryPolicy, step.errorMap, label);
531
+ case 'jumptarget':
532
+ return step;
533
+ }
534
+ }
@@ -0,0 +1,20 @@
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
@@ -0,0 +1 @@
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"}