ts2workflows 0.5.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.
- package/dist/ast/stepnames.js +4 -2
- package/dist/ast/steps.d.ts +9 -9
- package/dist/ast/steps.d.ts.map +1 -1
- package/dist/ast/steps.js +20 -8
- package/dist/ast/workflows.js +1 -1
- package/dist/transpiler/expressions.d.ts +1 -0
- package/dist/transpiler/expressions.d.ts.map +1 -1
- package/dist/transpiler/expressions.js +37 -9
- package/dist/transpiler/index.js +34 -16
- package/dist/transpiler/statements.d.ts +1 -0
- package/dist/transpiler/statements.d.ts.map +1 -1
- package/dist/transpiler/statements.js +227 -42
- package/dist/transpiler/transformations.d.ts.map +1 -1
- package/dist/transpiler/transformations.js +3 -92
- package/dist/utils.d.ts +7 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +17 -0
- package/language_reference.md +85 -20
- package/package.json +3 -3
- package/types/workflowslib.d.ts +61 -35
|
@@ -2,18 +2,18 @@ import { AST_NODE_TYPES } from '@typescript-eslint/typescript-estree';
|
|
|
2
2
|
import { AssignStepAST, CallStepAST, ForStepAST, JumpTargetAST, NextStepAST, ParallelStepAST, RaiseStepAST, ReturnStepAST, StepsStepAST, SwitchStepAST, TryStepAST, } from '../ast/steps.js';
|
|
3
3
|
import { BinaryExpression, FunctionInvocationExpression, PrimitiveExpression, VariableReferenceExpression, isExpression, isFullyQualifiedName, isLiteral, } from '../ast/expressions.js';
|
|
4
4
|
import { InternalTranspilingError, WorkflowSyntaxError } from '../errors.js';
|
|
5
|
-
import { isRecord } from '../utils.js';
|
|
5
|
+
import { flatMapPair, isRecord, mapRecordValues } from '../utils.js';
|
|
6
6
|
import { transformAST } from './transformations.js';
|
|
7
|
-
import { convertExpression, convertMemberExpression, convertObjectExpression, convertObjectAsExpressionValues, isMagicFunction, throwIfSpread, isMagicFunctionStatmentOnly, } from './expressions.js';
|
|
7
|
+
import { convertExpression, convertMemberExpression, convertObjectExpression, convertObjectAsExpressionValues, isMagicFunction, throwIfSpread, isMagicFunctionStatmentOnly, asExpression, } from './expressions.js';
|
|
8
8
|
import { blockingFunctions } from './generated/functionMetadata.js';
|
|
9
9
|
export function parseStatement(node, ctx, postSteps) {
|
|
10
|
-
const steps = parseStatementRecursively(node, ctx);
|
|
10
|
+
const steps = parseStatementRecursively(node, undefined, ctx);
|
|
11
11
|
return transformAST(steps.concat(postSteps ?? []));
|
|
12
12
|
}
|
|
13
|
-
function parseStatementRecursively(node, ctx) {
|
|
13
|
+
function parseStatementRecursively(node, nextNode, ctx) {
|
|
14
14
|
switch (node.type) {
|
|
15
15
|
case AST_NODE_TYPES.BlockStatement:
|
|
16
|
-
return node.body
|
|
16
|
+
return flatMapPair(node.body, (statement, nextStatement) => parseStatementRecursively(statement, nextStatement, ctx));
|
|
17
17
|
case AST_NODE_TYPES.VariableDeclaration:
|
|
18
18
|
return convertVariableDeclarations(node.declarations, ctx);
|
|
19
19
|
case AST_NODE_TYPES.ExpressionStatement:
|
|
@@ -21,13 +21,13 @@ function parseStatementRecursively(node, ctx) {
|
|
|
21
21
|
return assignmentExpressionToSteps(node.expression, ctx);
|
|
22
22
|
}
|
|
23
23
|
else if (node.expression.type === AST_NODE_TYPES.CallExpression) {
|
|
24
|
-
return
|
|
24
|
+
return callExpressionToStep(node.expression, undefined, ctx);
|
|
25
25
|
}
|
|
26
26
|
else {
|
|
27
27
|
return [generalExpressionToAssignStep(node.expression)];
|
|
28
28
|
}
|
|
29
29
|
case AST_NODE_TYPES.ReturnStatement:
|
|
30
|
-
return [returnStatementToReturnStep(node)];
|
|
30
|
+
return [returnStatementToReturnStep(node, ctx)];
|
|
31
31
|
case AST_NODE_TYPES.ThrowStatement:
|
|
32
32
|
return [throwStatementToRaiseStep(node)];
|
|
33
33
|
case AST_NODE_TYPES.IfStatement:
|
|
@@ -46,8 +46,14 @@ function parseStatementRecursively(node, ctx) {
|
|
|
46
46
|
return [breakStatementToNextStep(node, ctx)];
|
|
47
47
|
case AST_NODE_TYPES.ContinueStatement:
|
|
48
48
|
return [continueStatementToNextStep(node, ctx)];
|
|
49
|
-
case AST_NODE_TYPES.TryStatement:
|
|
50
|
-
|
|
49
|
+
case AST_NODE_TYPES.TryStatement: {
|
|
50
|
+
let retryPolicy = undefined;
|
|
51
|
+
if (nextNode?.type === AST_NODE_TYPES.ExpressionStatement &&
|
|
52
|
+
nextNode.expression.type === AST_NODE_TYPES.CallExpression) {
|
|
53
|
+
retryPolicy = parseRetryPolicy(nextNode.expression);
|
|
54
|
+
}
|
|
55
|
+
return tryStatementToTrySteps(node, retryPolicy, ctx);
|
|
56
|
+
}
|
|
51
57
|
case AST_NODE_TYPES.LabeledStatement:
|
|
52
58
|
return labeledStep(node, ctx);
|
|
53
59
|
case AST_NODE_TYPES.EmptyStatement:
|
|
@@ -64,7 +70,7 @@ function parseStatementRecursively(node, ctx) {
|
|
|
64
70
|
}
|
|
65
71
|
}
|
|
66
72
|
function convertVariableDeclarations(declarations, ctx) {
|
|
67
|
-
return declarations.
|
|
73
|
+
return declarations.flatMap((decl) => {
|
|
68
74
|
if (decl.id.type !== AST_NODE_TYPES.Identifier) {
|
|
69
75
|
throw new WorkflowSyntaxError('Expected Identifier', decl.loc);
|
|
70
76
|
}
|
|
@@ -82,7 +88,7 @@ function convertVariableDeclarations(declarations, ctx) {
|
|
|
82
88
|
const value = decl.init == null
|
|
83
89
|
? new PrimitiveExpression(null)
|
|
84
90
|
: convertExpression(decl.init);
|
|
85
|
-
return new AssignStepAST([[targetName, value]]);
|
|
91
|
+
return [new AssignStepAST([[targetName, value]])];
|
|
86
92
|
}
|
|
87
93
|
});
|
|
88
94
|
}
|
|
@@ -133,7 +139,7 @@ function assignmentExpressionToSteps(node, ctx) {
|
|
|
133
139
|
const needsTempVariable = compoundOperator === undefined ||
|
|
134
140
|
node.left.type !== AST_NODE_TYPES.Identifier;
|
|
135
141
|
const resultVariable = needsTempVariable ? '__temp' : targetName;
|
|
136
|
-
steps.push(callExpressionToStep(node.right, resultVariable, ctx));
|
|
142
|
+
steps.push(...callExpressionToStep(node.right, resultVariable, ctx));
|
|
137
143
|
if (!needsTempVariable) {
|
|
138
144
|
return steps;
|
|
139
145
|
}
|
|
@@ -154,20 +160,25 @@ function callExpressionToStep(node, resultVariable, ctx) {
|
|
|
154
160
|
const calleeName = calleeExpression.toString();
|
|
155
161
|
if (calleeName === 'parallel') {
|
|
156
162
|
// A custom implementation for "parallel"
|
|
157
|
-
return callExpressionToParallelStep(node, ctx);
|
|
163
|
+
return [callExpressionToParallelStep(node, ctx)];
|
|
158
164
|
}
|
|
159
165
|
else if (calleeName === 'retry_policy') {
|
|
160
|
-
|
|
166
|
+
// retry_policy() is handled by AST_NODE_TYPES.TryStatement and therefore ignored here
|
|
167
|
+
return [];
|
|
161
168
|
}
|
|
162
169
|
else if (calleeName === 'call_step') {
|
|
163
|
-
return createCallStep(node.arguments, resultVariable);
|
|
170
|
+
return [createCallStep(node, node.arguments, resultVariable)];
|
|
164
171
|
}
|
|
165
172
|
else if (blockingFunctions.has(calleeName)) {
|
|
166
173
|
const argumentNames = blockingFunctions.get(calleeName) ?? [];
|
|
167
|
-
return
|
|
174
|
+
return [
|
|
175
|
+
blockingFunctionCallStep(calleeName, argumentNames, node.arguments, resultVariable),
|
|
176
|
+
];
|
|
168
177
|
}
|
|
169
178
|
else {
|
|
170
|
-
return
|
|
179
|
+
return [
|
|
180
|
+
callExpressionAssignStep(calleeName, node.arguments, resultVariable),
|
|
181
|
+
];
|
|
171
182
|
}
|
|
172
183
|
}
|
|
173
184
|
else {
|
|
@@ -183,17 +194,9 @@ function callExpressionAssignStep(functionName, argumentsNode, resultVariable) {
|
|
|
183
194
|
],
|
|
184
195
|
]);
|
|
185
196
|
}
|
|
186
|
-
function
|
|
187
|
-
if (argumentsNode.length < 1 ||
|
|
188
|
-
argumentsNode[0].type !== AST_NODE_TYPES.ObjectExpression) {
|
|
189
|
-
throw new WorkflowSyntaxError('Expected one object parameter', argumentsNode[0].loc);
|
|
190
|
-
}
|
|
191
|
-
const workflowArguments = convertObjectAsExpressionValues(argumentsNode[0]);
|
|
192
|
-
return new CallStepAST(functionName, workflowArguments, resultVariable);
|
|
193
|
-
}
|
|
194
|
-
function createCallStep(argumentsNode, resultVariable) {
|
|
197
|
+
function createCallStep(node, argumentsNode, resultVariable) {
|
|
195
198
|
if (argumentsNode.length < 1) {
|
|
196
|
-
throw new WorkflowSyntaxError('The first argument must be a Function',
|
|
199
|
+
throw new WorkflowSyntaxError('The first argument must be a Function', node.loc);
|
|
197
200
|
}
|
|
198
201
|
let functionName;
|
|
199
202
|
if (argumentsNode[0].type === AST_NODE_TYPES.Identifier) {
|
|
@@ -327,8 +330,13 @@ function parseParallelOptions(node) {
|
|
|
327
330
|
function generalExpressionToAssignStep(node) {
|
|
328
331
|
return new AssignStepAST([['__temp', convertExpression(node)]]);
|
|
329
332
|
}
|
|
330
|
-
function returnStatementToReturnStep(node) {
|
|
333
|
+
function returnStatementToReturnStep(node, ctx) {
|
|
331
334
|
const value = node.argument ? convertExpression(node.argument) : undefined;
|
|
335
|
+
if (ctx.finalizerTargets && ctx.finalizerTargets.length > 0) {
|
|
336
|
+
// If we are in try statement with a finally block, return statements are
|
|
337
|
+
// replaced by a jump to finally back with a captured return value.
|
|
338
|
+
return delayedReturnAndJumpToFinalizer(value, ctx);
|
|
339
|
+
}
|
|
332
340
|
return new ReturnStepAST(value);
|
|
333
341
|
}
|
|
334
342
|
function throwStatementToRaiseStep(node) {
|
|
@@ -464,6 +472,11 @@ function doWhileStatementSteps(node, ctx) {
|
|
|
464
472
|
return steps;
|
|
465
473
|
}
|
|
466
474
|
function breakStatementToNextStep(node, ctx) {
|
|
475
|
+
if (ctx.finalizerTargets) {
|
|
476
|
+
// TODO: would need to detect if this breaks out of the try or catch block,
|
|
477
|
+
// execute the finally block first and then do the break.
|
|
478
|
+
throw new WorkflowSyntaxError('break is not supported inside a try-finally block', node.loc);
|
|
479
|
+
}
|
|
467
480
|
let target;
|
|
468
481
|
if (node.label) {
|
|
469
482
|
target = node.label.name;
|
|
@@ -477,6 +490,11 @@ function breakStatementToNextStep(node, ctx) {
|
|
|
477
490
|
return new NextStepAST(target);
|
|
478
491
|
}
|
|
479
492
|
function continueStatementToNextStep(node, ctx) {
|
|
493
|
+
if (ctx.finalizerTargets) {
|
|
494
|
+
// TODO: would need to detect if continue breaks out of the try or catch block,
|
|
495
|
+
// execute the finally block first and then do the continue.
|
|
496
|
+
throw new WorkflowSyntaxError('continue is not supported inside a try-finally block', node.loc);
|
|
497
|
+
}
|
|
480
498
|
let target;
|
|
481
499
|
if (node.label) {
|
|
482
500
|
target = node.label.name;
|
|
@@ -489,25 +507,129 @@ function continueStatementToNextStep(node, ctx) {
|
|
|
489
507
|
}
|
|
490
508
|
return new NextStepAST(target);
|
|
491
509
|
}
|
|
492
|
-
function
|
|
493
|
-
|
|
494
|
-
|
|
510
|
+
function tryStatementToTrySteps(node, retryPolicy, ctx) {
|
|
511
|
+
if (!node.finalizer) {
|
|
512
|
+
// Basic try-catch without a finally block
|
|
513
|
+
const baseTryStep = parseTryCatchRetry(node, ctx, retryPolicy);
|
|
514
|
+
return [baseTryStep];
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
// Try-finally is translated to two nested try blocks. The innermost try is
|
|
518
|
+
// the actual try body with control flow statements (return, in the future
|
|
519
|
+
// also break/continue) replaced by jumps to the finally block.
|
|
520
|
+
//
|
|
521
|
+
// The outer try's catch block saved the exception and continues to the
|
|
522
|
+
// finally block.
|
|
523
|
+
//
|
|
524
|
+
// The nested try blocks are followed by the finally block and a swith for
|
|
525
|
+
// checking if we need to perform a delayed return/raise.
|
|
526
|
+
const startOfFinalizer = new JumpTargetAST();
|
|
527
|
+
const targets = ctx.finalizerTargets ?? [];
|
|
528
|
+
targets.push(startOfFinalizer.label);
|
|
529
|
+
ctx = Object.assign({}, ctx, { finalizerTargets: targets });
|
|
530
|
+
const [conditionVariable, valueVariable] = finalizerVariables(ctx);
|
|
531
|
+
const innerTry = parseTryCatchRetry(node, ctx, retryPolicy);
|
|
532
|
+
const outerTry = new TryStepAST([innerTry], finalizerDelayedException('__fin_exc', conditionVariable, valueVariable), undefined, '__fin_exc');
|
|
533
|
+
// Reset ctx before parsing the finally block because we don't want to
|
|
534
|
+
// transform returns in finally block in to delayed returns
|
|
535
|
+
if (ctx.finalizerTargets && ctx.finalizerTargets.length <= 1) {
|
|
536
|
+
delete ctx.finalizerTargets;
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
ctx.finalizerTargets?.pop();
|
|
540
|
+
}
|
|
541
|
+
const finallyBlock = parseStatement(node.finalizer, ctx);
|
|
542
|
+
return [
|
|
543
|
+
finalizerInitializer(conditionVariable, valueVariable),
|
|
544
|
+
outerTry,
|
|
545
|
+
startOfFinalizer,
|
|
546
|
+
...finallyBlock,
|
|
547
|
+
finalizerFooter(conditionVariable, valueVariable),
|
|
548
|
+
];
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
function finalizerVariables(ctx) {
|
|
552
|
+
const targets = ctx.finalizerTargets ?? [];
|
|
553
|
+
const nestingLevel = targets.length > 0 ? `${targets.length}` : '';
|
|
554
|
+
const conditionVariable = `__t2w_finally_condition${nestingLevel}`;
|
|
555
|
+
const valueVariable = `__t2w_finally_value${nestingLevel}`;
|
|
556
|
+
return [conditionVariable, valueVariable];
|
|
557
|
+
}
|
|
558
|
+
function parseTryCatchRetry(node, ctx, retryPolicy) {
|
|
559
|
+
const trySteps = parseStatement(node.block, ctx);
|
|
560
|
+
let exceptSteps = undefined;
|
|
495
561
|
let errorVariable = undefined;
|
|
496
562
|
if (node.handler) {
|
|
497
563
|
exceptSteps = parseStatement(node.handler.body, ctx);
|
|
498
|
-
|
|
499
|
-
if (handlerParam) {
|
|
500
|
-
if (handlerParam.type !== AST_NODE_TYPES.Identifier) {
|
|
501
|
-
throw new WorkflowSyntaxError('The error variable must be an identifier', handlerParam.loc);
|
|
502
|
-
}
|
|
503
|
-
errorVariable = handlerParam.name;
|
|
504
|
-
}
|
|
564
|
+
errorVariable = extractErrorVariableName(node.handler.param);
|
|
505
565
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
566
|
+
const baseTryStep = new TryStepAST(trySteps, exceptSteps, retryPolicy, errorVariable);
|
|
567
|
+
return baseTryStep;
|
|
568
|
+
}
|
|
569
|
+
function extractErrorVariableName(param) {
|
|
570
|
+
if (!param) {
|
|
571
|
+
return undefined;
|
|
572
|
+
}
|
|
573
|
+
if (param.type !== AST_NODE_TYPES.Identifier) {
|
|
574
|
+
throw new WorkflowSyntaxError('The error variable must be an identifier', param.loc);
|
|
509
575
|
}
|
|
510
|
-
return
|
|
576
|
+
return param.name;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* The shared header for try-finally for initializing the temp variables
|
|
580
|
+
*/
|
|
581
|
+
function finalizerInitializer(conditionVariable, valueVariable) {
|
|
582
|
+
return new AssignStepAST([
|
|
583
|
+
[conditionVariable, new PrimitiveExpression(null)],
|
|
584
|
+
[valueVariable, new PrimitiveExpression(null)],
|
|
585
|
+
]);
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* The shared footer of a finally block that re-throws the exception or
|
|
589
|
+
* returns the value returned by the try body.
|
|
590
|
+
*
|
|
591
|
+
* The footer code in TypeScript:
|
|
592
|
+
*
|
|
593
|
+
* if (__t2w_finally_condition == "return") {
|
|
594
|
+
* return __t2w_finally_value
|
|
595
|
+
* } elseif (__t2w_finally_condition == "raise") {
|
|
596
|
+
* throw __t2w_finally_value
|
|
597
|
+
* }
|
|
598
|
+
*/
|
|
599
|
+
function finalizerFooter(conditionVariable, valueVariable) {
|
|
600
|
+
return new SwitchStepAST([
|
|
601
|
+
{
|
|
602
|
+
condition: new BinaryExpression(new VariableReferenceExpression(conditionVariable), '==', new PrimitiveExpression('return')),
|
|
603
|
+
steps: [
|
|
604
|
+
new ReturnStepAST(new VariableReferenceExpression(valueVariable)),
|
|
605
|
+
],
|
|
606
|
+
},
|
|
607
|
+
{
|
|
608
|
+
condition: new BinaryExpression(new VariableReferenceExpression(conditionVariable), '==', new PrimitiveExpression('raise')),
|
|
609
|
+
steps: [new RaiseStepAST(new VariableReferenceExpression(valueVariable))],
|
|
610
|
+
},
|
|
611
|
+
]);
|
|
612
|
+
}
|
|
613
|
+
function finalizerDelayedException(exceptionVariableName, conditionVariableName, valueVariableName) {
|
|
614
|
+
return [
|
|
615
|
+
new AssignStepAST([
|
|
616
|
+
[conditionVariableName, new PrimitiveExpression('raise')],
|
|
617
|
+
[
|
|
618
|
+
valueVariableName,
|
|
619
|
+
new VariableReferenceExpression(exceptionVariableName),
|
|
620
|
+
],
|
|
621
|
+
]),
|
|
622
|
+
];
|
|
623
|
+
}
|
|
624
|
+
function delayedReturnAndJumpToFinalizer(value, ctx) {
|
|
625
|
+
const finalizerTarget = ctx.finalizerTargets && ctx.finalizerTargets.length > 0
|
|
626
|
+
? ctx.finalizerTargets[ctx.finalizerTargets.length - 1]
|
|
627
|
+
: undefined;
|
|
628
|
+
const [conditionVariable, valueVariable] = finalizerVariables(ctx);
|
|
629
|
+
return new AssignStepAST([
|
|
630
|
+
[conditionVariable, new PrimitiveExpression('return')],
|
|
631
|
+
[valueVariable, value ?? new PrimitiveExpression(null)],
|
|
632
|
+
], finalizerTarget);
|
|
511
633
|
}
|
|
512
634
|
function labeledStep(node, ctx) {
|
|
513
635
|
const steps = parseStatement(node.body, ctx);
|
|
@@ -516,3 +638,66 @@ function labeledStep(node, ctx) {
|
|
|
516
638
|
}
|
|
517
639
|
return steps;
|
|
518
640
|
}
|
|
641
|
+
function parseRetryPolicy(node) {
|
|
642
|
+
const callee = node.callee;
|
|
643
|
+
if (callee.type !== AST_NODE_TYPES.Identifier ||
|
|
644
|
+
callee.name !== 'retry_policy') {
|
|
645
|
+
// Ignore everything else besides retry_policy()
|
|
646
|
+
return undefined;
|
|
647
|
+
}
|
|
648
|
+
if (node.arguments.length < 1) {
|
|
649
|
+
throw new WorkflowSyntaxError('Required argument missing', node.loc);
|
|
650
|
+
}
|
|
651
|
+
const arg0 = throwIfSpread(node.arguments).map(convertExpression)[0];
|
|
652
|
+
const argsLoc = node.arguments[0].loc;
|
|
653
|
+
if (isFullyQualifiedName(arg0)) {
|
|
654
|
+
return arg0.toString();
|
|
655
|
+
}
|
|
656
|
+
else if (arg0.expressionType === 'primitive' && isRecord(arg0.value)) {
|
|
657
|
+
return retryPolicyFromParams(arg0.value, argsLoc);
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
throw new WorkflowSyntaxError('Unexpected type', argsLoc);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
function retryPolicyFromParams(paramsObject, argsLoc) {
|
|
664
|
+
const params = mapRecordValues(paramsObject, asExpression);
|
|
665
|
+
if ('backoff' in params) {
|
|
666
|
+
let predicate = '';
|
|
667
|
+
const predicateEx = params.predicate;
|
|
668
|
+
if (predicateEx === undefined) {
|
|
669
|
+
predicate = undefined;
|
|
670
|
+
}
|
|
671
|
+
else if (isFullyQualifiedName(predicateEx)) {
|
|
672
|
+
predicate = predicateEx.toString();
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
throw new WorkflowSyntaxError('"predicate" must be a function name', argsLoc);
|
|
676
|
+
}
|
|
677
|
+
const backoffEx = params.backoff;
|
|
678
|
+
if (backoffEx.expressionType === 'primitive' && isRecord(backoffEx.value)) {
|
|
679
|
+
const backoffLit = backoffEx.value;
|
|
680
|
+
return {
|
|
681
|
+
predicate,
|
|
682
|
+
maxRetries: params.max_retries,
|
|
683
|
+
backoff: {
|
|
684
|
+
initialDelay: backoffLit.initial_delay
|
|
685
|
+
? asExpression(backoffLit.initial_delay)
|
|
686
|
+
: undefined,
|
|
687
|
+
maxDelay: backoffLit.max_delay
|
|
688
|
+
? asExpression(backoffLit.max_delay)
|
|
689
|
+
: undefined,
|
|
690
|
+
multiplier: backoffLit.multiplier
|
|
691
|
+
? asExpression(backoffLit.multiplier)
|
|
692
|
+
: undefined,
|
|
693
|
+
},
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
else {
|
|
697
|
+
throw new WorkflowSyntaxError('Expected an object literal', argsLoc);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
throw new WorkflowSyntaxError('Some required retry policy parameters are missing', argsLoc);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transformations.d.ts","sourceRoot":"","sources":["../../src/transpiler/transformations.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"transformations.d.ts","sourceRoot":"","sources":["../../src/transpiler/transformations.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,eAAe,EAChB,MAAM,iBAAiB,CAAA;AAqBxB;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,eAAe,EAAE,CAQxE;AAiCD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,eAAe,EAAE,GACvB,eAAe,EAAE,CAqBnB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { AssignStepAST, CallStepAST, ForStepAST, RaiseStepAST, ReturnStepAST, SwitchStepAST,
|
|
1
|
+
import { AssignStepAST, CallStepAST, ForStepAST, RaiseStepAST, ReturnStepAST, SwitchStepAST, } from '../ast/steps.js';
|
|
2
2
|
import { InternalTranspilingError } from '../errors.js';
|
|
3
3
|
import { isRecord, mapRecordValues } from '../utils.js';
|
|
4
|
-
import { BinaryExpression, FunctionInvocationExpression, MemberExpression, PrimitiveExpression, UnaryExpression, VariableReferenceExpression,
|
|
4
|
+
import { BinaryExpression, FunctionInvocationExpression, MemberExpression, PrimitiveExpression, UnaryExpression, VariableReferenceExpression, isExpression, isLiteral, } from '../ast/expressions.js';
|
|
5
5
|
import { blockingFunctions } from './generated/functionMetadata.js';
|
|
6
6
|
const Unmodified = Symbol();
|
|
7
7
|
/**
|
|
@@ -11,7 +11,7 @@ const Unmodified = Symbol();
|
|
|
11
11
|
* called on each nesting level separately.
|
|
12
12
|
*/
|
|
13
13
|
export function transformAST(steps) {
|
|
14
|
-
return blockingCallsAsCallSteps(runtimeFunctionImplementation(flattenPlainNextConditions(
|
|
14
|
+
return blockingCallsAsCallSteps(runtimeFunctionImplementation(flattenPlainNextConditions(mergeAssignSteps(mapLiteralsAsAssignSteps(steps)))));
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* Merge consecutive assign steps into one assign step
|
|
@@ -35,95 +35,6 @@ function mergeAssignSteps(steps) {
|
|
|
35
35
|
return acc;
|
|
36
36
|
}, []);
|
|
37
37
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Transform a retry_policy call step to a retry block in a preceeding try step
|
|
40
|
-
*/
|
|
41
|
-
function combineRetryBlocksToTry(steps) {
|
|
42
|
-
return steps.reduce((acc, current) => {
|
|
43
|
-
const prev = acc.length > 0 ? acc[acc.length - 1] : null;
|
|
44
|
-
if (current.tag === 'call' && current.call === 'retry_policy') {
|
|
45
|
-
if (prev?.tag === 'try') {
|
|
46
|
-
if (prev.retryPolicy) {
|
|
47
|
-
throw new InternalTranspilingError('Retry policy already assigned!');
|
|
48
|
-
}
|
|
49
|
-
let retryPolicy = undefined;
|
|
50
|
-
const retryParameters = current.args;
|
|
51
|
-
if (retryParameters) {
|
|
52
|
-
const retryPolicyEx = retryParameters.policy;
|
|
53
|
-
if (retryPolicyEx && isFullyQualifiedName(retryPolicyEx)) {
|
|
54
|
-
retryPolicy = retryPolicyEx.toString();
|
|
55
|
-
}
|
|
56
|
-
if (!retryPolicy) {
|
|
57
|
-
let predicate = '';
|
|
58
|
-
const predicateEx = retryParameters.predicate;
|
|
59
|
-
if (predicateEx) {
|
|
60
|
-
if (isFullyQualifiedName(predicateEx)) {
|
|
61
|
-
predicate = predicateEx.toString();
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
throw new InternalTranspilingError('"predicate" must be a function name');
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
const maxRetries = parseRetryPolicyNumber(retryParameters, 'max_retries');
|
|
68
|
-
let initialDelay = 1;
|
|
69
|
-
let maxDelay = 1;
|
|
70
|
-
let multiplier = 1;
|
|
71
|
-
const backoffEx = retryParameters.backoff;
|
|
72
|
-
if (backoffEx &&
|
|
73
|
-
isLiteral(backoffEx) &&
|
|
74
|
-
backoffEx.expressionType === 'primitive') {
|
|
75
|
-
const backoffLit = backoffEx.value;
|
|
76
|
-
if (isRecord(backoffLit)) {
|
|
77
|
-
initialDelay = parseRetryPolicyNumber(backoffLit, 'initial_delay');
|
|
78
|
-
maxDelay = parseRetryPolicyNumber(backoffLit, 'max_delay');
|
|
79
|
-
multiplier = parseRetryPolicyNumber(backoffLit, 'multiplier');
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
retryPolicy = {
|
|
83
|
-
predicate,
|
|
84
|
-
maxRetries,
|
|
85
|
-
backoff: {
|
|
86
|
-
initialDelay,
|
|
87
|
-
maxDelay,
|
|
88
|
-
multiplier,
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
const tryWithRetry = new TryStepAST(prev.trySteps, prev.exceptSteps, retryPolicy, prev.errorMap);
|
|
94
|
-
acc.pop();
|
|
95
|
-
acc.push(tryWithRetry);
|
|
96
|
-
}
|
|
97
|
-
// If prev is not a try step, "retry_policy" is ignored. Should print a warning.
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
acc.push(current);
|
|
101
|
-
}
|
|
102
|
-
return acc;
|
|
103
|
-
}, []);
|
|
104
|
-
}
|
|
105
|
-
function parseRetryPolicyNumber(record, keyName) {
|
|
106
|
-
let primitiveValue;
|
|
107
|
-
const primitiveOrExpression = record[keyName];
|
|
108
|
-
if (primitiveOrExpression && isExpression(primitiveOrExpression)) {
|
|
109
|
-
if (isLiteral(primitiveOrExpression)) {
|
|
110
|
-
primitiveValue = expressionToLiteralValueOrLiteralExpression(primitiveOrExpression);
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
throw new InternalTranspilingError(`Support for non-literal "${keyName}" values not yet implemented`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
else if (primitiveOrExpression) {
|
|
117
|
-
primitiveValue = primitiveOrExpression;
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
throw new InternalTranspilingError(`"${keyName}" expected`);
|
|
121
|
-
}
|
|
122
|
-
if (typeof primitiveValue !== 'number') {
|
|
123
|
-
throw new InternalTranspilingError(`"${keyName}" must be a number`);
|
|
124
|
-
}
|
|
125
|
-
return primitiveValue;
|
|
126
|
-
}
|
|
127
38
|
/**
|
|
128
39
|
* Merge a next step to the previous step.
|
|
129
40
|
*
|
package/dist/utils.d.ts
CHANGED
|
@@ -3,4 +3,11 @@ export declare function isRecord(object: unknown): object is Record<keyof never,
|
|
|
3
3
|
* Apply f to values of obj and return the result
|
|
4
4
|
*/
|
|
5
5
|
export declare function mapRecordValues<T, U>(obj: Record<string, T>, f: (t: T) => U): Record<string, U>;
|
|
6
|
+
/**
|
|
7
|
+
* Like arr.flatMap() but the callback takes two consecutive array elements.
|
|
8
|
+
*
|
|
9
|
+
* During the last execution of the callback, the second argument (which would
|
|
10
|
+
* be element after the last array element) will be undefined.
|
|
11
|
+
*/
|
|
12
|
+
export declare function flatMapPair<T, U>(arr: T[], callback: (val: T, next: T | undefined) => U[]): U[];
|
|
6
13
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,QAAQ,CACtB,MAAM,EAAE,OAAO,GACd,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,OAAO,CAAC,CAExC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAClC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EACtB,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GACb,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAEnB"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,QAAQ,CACtB,MAAM,EAAE,OAAO,GACd,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,OAAO,CAAC,CAExC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAClC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EACtB,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GACb,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAEnB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAC9B,GAAG,EAAE,CAAC,EAAE,EACR,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,SAAS,KAAK,CAAC,EAAE,GAC7C,CAAC,EAAE,CAaL"}
|
package/dist/utils.js
CHANGED
|
@@ -7,3 +7,20 @@ export function isRecord(object) {
|
|
|
7
7
|
export function mapRecordValues(obj, f) {
|
|
8
8
|
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, f(v)]));
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Like arr.flatMap() but the callback takes two consecutive array elements.
|
|
12
|
+
*
|
|
13
|
+
* During the last execution of the callback, the second argument (which would
|
|
14
|
+
* be element after the last array element) will be undefined.
|
|
15
|
+
*/
|
|
16
|
+
export function flatMapPair(arr, callback) {
|
|
17
|
+
if (arr.length <= 0) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
const mapped = [];
|
|
21
|
+
for (let i = 0; i < arr.length - 1; i++) {
|
|
22
|
+
mapped.push(...callback(arr[i], arr[i + 1]));
|
|
23
|
+
}
|
|
24
|
+
mapped.push(...callback(arr[arr.length - 1], undefined));
|
|
25
|
+
return mapped;
|
|
26
|
+
}
|