ts2workflows 0.10.0 → 0.12.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/LICENSE +1 -1
- package/README.md +36 -9
- package/dist/ast/expressions.d.ts +37 -41
- package/dist/ast/expressions.d.ts.map +1 -1
- package/dist/ast/expressions.js +124 -255
- package/dist/ast/statements.d.ts +132 -0
- package/dist/ast/statements.d.ts.map +1 -0
- package/dist/ast/statements.js +197 -0
- package/dist/ast/steps.d.ts +82 -194
- package/dist/ast/steps.d.ts.map +1 -1
- package/dist/ast/steps.js +862 -521
- package/dist/ast/workflows.d.ts +14 -12
- package/dist/ast/workflows.d.ts.map +1 -1
- package/dist/ast/workflows.js +20 -35
- package/dist/cli.js +45 -32
- package/dist/errors.d.ts +4 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +7 -0
- package/dist/transpiler/index.d.ts +19 -1
- package/dist/transpiler/index.d.ts.map +1 -1
- package/dist/transpiler/index.js +158 -31
- package/dist/transpiler/linker.d.ts +3 -0
- package/dist/transpiler/linker.d.ts.map +1 -0
- package/dist/transpiler/linker.js +41 -0
- package/dist/transpiler/parseexpressions.d.ts +11 -0
- package/dist/transpiler/parseexpressions.d.ts.map +1 -0
- package/dist/transpiler/{expressions.js → parseexpressions.js} +90 -103
- package/dist/transpiler/parsestatement.d.ts +7 -0
- package/dist/transpiler/parsestatement.d.ts.map +1 -0
- package/dist/transpiler/parsestatement.js +862 -0
- package/dist/transpiler/stepnames.d.ts +3 -0
- package/dist/transpiler/stepnames.d.ts.map +1 -0
- package/dist/transpiler/stepnames.js +17 -0
- package/dist/transpiler/transformations.d.ts +3 -19
- package/dist/transpiler/transformations.d.ts.map +1 -1
- package/dist/transpiler/transformations.js +267 -322
- package/language_reference.md +53 -17
- package/package.json +15 -11
- package/types/workflowslib.d.ts +58 -74
- package/dist/ast/stepnames.d.ts +0 -9
- package/dist/ast/stepnames.d.ts.map +0 -1
- package/dist/ast/stepnames.js +0 -280
- package/dist/ast/validation.d.ts +0 -20
- package/dist/ast/validation.d.ts.map +0 -1
- package/dist/ast/validation.js +0 -214
- package/dist/transpiler/expressions.d.ts +0 -11
- package/dist/transpiler/expressions.d.ts.map +0 -1
- package/dist/transpiler/statements.d.ts +0 -10
- package/dist/transpiler/statements.d.ts.map +0 -1
- package/dist/transpiler/statements.js +0 -1100
- package/dist/utils.d.ts +0 -9
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -13
|
@@ -1,29 +1,39 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { BinaryExpression, FunctionInvocationExpression, MemberExpression, PrimitiveExpression, UnaryExpression, VariableReferenceExpression, asExpression, isFullyQualifiedName, nullEx, } from '../ast/expressions.js';
|
|
1
|
+
import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree';
|
|
2
|
+
import { binaryEx, booleanEx, expressionToString, functionInvocationEx, isQualifiedName, listEx, mapEx, memberEx, nullEx, numberEx, stringEx, unaryEx, variableReferenceEx, } from '../ast/expressions.js';
|
|
4
3
|
import { InternalTranspilingError, WorkflowSyntaxError } from '../errors.js';
|
|
5
4
|
export function convertExpression(instance) {
|
|
6
5
|
switch (instance.type) {
|
|
7
6
|
case AST_NODE_TYPES.ArrayExpression:
|
|
8
|
-
return
|
|
7
|
+
return convertArrayExpression(instance);
|
|
9
8
|
case AST_NODE_TYPES.ObjectExpression:
|
|
10
|
-
return
|
|
9
|
+
return convertObjectExpression(instance);
|
|
11
10
|
case AST_NODE_TYPES.Literal:
|
|
12
11
|
if (instance.value instanceof RegExp) {
|
|
13
12
|
throw new WorkflowSyntaxError('RegExp is not supported', instance.loc);
|
|
14
13
|
}
|
|
15
|
-
if (typeof instance.value === 'bigint') {
|
|
14
|
+
else if (typeof instance.value === 'bigint') {
|
|
16
15
|
throw new WorkflowSyntaxError('BigInt is not supported', instance.loc);
|
|
17
16
|
}
|
|
18
|
-
|
|
17
|
+
else if (typeof instance.value === 'string') {
|
|
18
|
+
return stringEx(instance.value);
|
|
19
|
+
}
|
|
20
|
+
else if (typeof instance.value === 'number') {
|
|
21
|
+
return numberEx(instance.value);
|
|
22
|
+
}
|
|
23
|
+
else if (typeof instance.value === 'boolean') {
|
|
24
|
+
return booleanEx(instance.value);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
return nullEx;
|
|
28
|
+
}
|
|
19
29
|
case AST_NODE_TYPES.TemplateLiteral:
|
|
20
30
|
return convertTemplateLiteralToExpression(instance);
|
|
21
31
|
case AST_NODE_TYPES.Identifier:
|
|
22
|
-
if (instance.name === '
|
|
32
|
+
if (instance.name === 'undefined') {
|
|
23
33
|
return nullEx;
|
|
24
34
|
}
|
|
25
35
|
else {
|
|
26
|
-
return
|
|
36
|
+
return variableReferenceEx(instance.name);
|
|
27
37
|
}
|
|
28
38
|
case AST_NODE_TYPES.UnaryExpression:
|
|
29
39
|
return convertUnaryExpression(instance);
|
|
@@ -39,56 +49,38 @@ export function convertExpression(instance) {
|
|
|
39
49
|
case AST_NODE_TYPES.ConditionalExpression:
|
|
40
50
|
return convertConditionalExpression(instance);
|
|
41
51
|
case AST_NODE_TYPES.TSAsExpression:
|
|
42
|
-
return convertExpression(instance.expression);
|
|
43
52
|
case AST_NODE_TYPES.TSNonNullExpression:
|
|
53
|
+
case AST_NODE_TYPES.TSInstantiationExpression:
|
|
54
|
+
case AST_NODE_TYPES.TSSatisfiesExpression:
|
|
44
55
|
return convertExpression(instance.expression);
|
|
45
56
|
case AST_NODE_TYPES.AwaitExpression:
|
|
46
57
|
return convertExpression(instance.argument);
|
|
47
|
-
case AST_NODE_TYPES.TSInstantiationExpression:
|
|
48
|
-
return convertExpression(instance.expression);
|
|
49
58
|
default:
|
|
50
59
|
throw new WorkflowSyntaxError(`Not implemented expression type: ${instance.type}`, instance.loc);
|
|
51
60
|
}
|
|
52
61
|
}
|
|
53
|
-
function convertExpressionOrPrimitive(instance) {
|
|
54
|
-
const ex = convertExpression(instance);
|
|
55
|
-
if (ex.expressionType === 'primitive') {
|
|
56
|
-
return ex.value;
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
return ex;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
62
|
export function convertObjectExpression(node) {
|
|
63
|
-
return Object.fromEntries(throwIfSpread(node.properties).map(({ key, value }) => {
|
|
63
|
+
return mapEx(Object.fromEntries(throwIfSpread(node.properties).map(({ key, value }) => {
|
|
64
64
|
let keyPrimitive;
|
|
65
65
|
if (key.type === AST_NODE_TYPES.Identifier) {
|
|
66
66
|
keyPrimitive = key.name;
|
|
67
67
|
}
|
|
68
|
-
else if (key.type === AST_NODE_TYPES.Literal
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
throw new WorkflowSyntaxError(`Map keys must be identifiers or strings, encountered: ${typeof key.value}`, key.loc);
|
|
74
|
-
}
|
|
68
|
+
else if (key.type === AST_NODE_TYPES.Literal &&
|
|
69
|
+
typeof key.value === 'string') {
|
|
70
|
+
keyPrimitive = key.value;
|
|
75
71
|
}
|
|
76
72
|
else {
|
|
77
|
-
throw new WorkflowSyntaxError(`
|
|
73
|
+
throw new WorkflowSyntaxError(`Map keys must be identifiers or strings, encountered: ${key.type}`, key.loc);
|
|
78
74
|
}
|
|
79
75
|
if (value.type === AST_NODE_TYPES.AssignmentPattern ||
|
|
80
76
|
value.type === AST_NODE_TYPES.TSEmptyBodyFunctionExpression) {
|
|
81
77
|
throw new WorkflowSyntaxError('Value not supported', value.loc);
|
|
82
78
|
}
|
|
83
|
-
return [keyPrimitive,
|
|
84
|
-
}));
|
|
85
|
-
}
|
|
86
|
-
export function convertObjectAsExpressionValues(node) {
|
|
87
|
-
// Convert Primitive values to PrimitiveExpressions
|
|
88
|
-
return R.map(asExpression, convertObjectExpression(node));
|
|
79
|
+
return [keyPrimitive, convertExpression(value)];
|
|
80
|
+
})));
|
|
89
81
|
}
|
|
90
82
|
function convertArrayExpression(instance) {
|
|
91
|
-
return throwIfSpread(instance.elements).map((e) => e === null ? nullEx :
|
|
83
|
+
return listEx(throwIfSpread(instance.elements).map((e) => e === null ? nullEx : convertExpression(e)));
|
|
92
84
|
}
|
|
93
85
|
function convertBinaryExpression(instance) {
|
|
94
86
|
// Special case for nullish coalescing because the result is a function call
|
|
@@ -127,19 +119,16 @@ function convertBinaryExpression(instance) {
|
|
|
127
119
|
default:
|
|
128
120
|
throw new WorkflowSyntaxError(`Unsupported binary operator: ${instance.operator}`, instance.loc);
|
|
129
121
|
}
|
|
130
|
-
|
|
131
|
-
throw new WorkflowSyntaxError('Private identifier not supported', instance.left.loc);
|
|
132
|
-
}
|
|
133
|
-
return new BinaryExpression(convertExpression(instance.left), op, convertExpression(instance.right));
|
|
122
|
+
return binaryEx(convertExpression(throwIfPrivateIdentifier(instance.left)), op, convertExpression(instance.right));
|
|
134
123
|
}
|
|
135
124
|
function nullishCoalescingExpression(left, right) {
|
|
136
|
-
return
|
|
125
|
+
return functionInvocationEx('default', [
|
|
137
126
|
convertExpression(left),
|
|
138
127
|
convertExpression(right),
|
|
139
128
|
]);
|
|
140
129
|
}
|
|
141
130
|
function convertUnaryExpression(instance) {
|
|
142
|
-
if (instance.prefix
|
|
131
|
+
if (!instance.prefix) {
|
|
143
132
|
throw new WorkflowSyntaxError('only prefix unary operators are supported', instance.loc);
|
|
144
133
|
}
|
|
145
134
|
let op;
|
|
@@ -163,9 +152,6 @@ function convertUnaryExpression(instance) {
|
|
|
163
152
|
op = undefined;
|
|
164
153
|
istypeof = true;
|
|
165
154
|
break;
|
|
166
|
-
case undefined:
|
|
167
|
-
op = undefined;
|
|
168
|
-
break;
|
|
169
155
|
default:
|
|
170
156
|
throw new WorkflowSyntaxError(`Unsupported unary operator: ${instance.operator}`, instance.loc);
|
|
171
157
|
}
|
|
@@ -174,7 +160,7 @@ function convertUnaryExpression(instance) {
|
|
|
174
160
|
return convertTypeOfExpression(ex);
|
|
175
161
|
}
|
|
176
162
|
else if (op) {
|
|
177
|
-
return
|
|
163
|
+
return unaryEx(op, ex);
|
|
178
164
|
}
|
|
179
165
|
else {
|
|
180
166
|
return ex;
|
|
@@ -182,55 +168,54 @@ function convertUnaryExpression(instance) {
|
|
|
182
168
|
}
|
|
183
169
|
function convertTypeOfExpression(value) {
|
|
184
170
|
// Note for future refactoring: evalute value only once (in case it has side effects)
|
|
185
|
-
return
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
171
|
+
return functionInvocationEx('text.replace_all_regex', [
|
|
172
|
+
functionInvocationEx('text.replace_all_regex', [
|
|
173
|
+
functionInvocationEx('get_type', [value]),
|
|
174
|
+
stringEx('^(bytes|list|map|null)$'),
|
|
175
|
+
stringEx('object'),
|
|
190
176
|
]),
|
|
191
|
-
|
|
192
|
-
|
|
177
|
+
stringEx('^(double|integer)$'),
|
|
178
|
+
stringEx('number'),
|
|
193
179
|
]);
|
|
194
180
|
}
|
|
195
181
|
export function convertMemberExpression(node) {
|
|
196
|
-
if (node.property.type === AST_NODE_TYPES.PrivateIdentifier) {
|
|
197
|
-
throw new WorkflowSyntaxError('Private identifier not supported', node.property.loc);
|
|
198
|
-
}
|
|
199
182
|
const object = convertExpression(node.object);
|
|
200
|
-
return
|
|
183
|
+
return memberEx(object, convertExpression(throwIfPrivateIdentifier(node.property)), node.computed);
|
|
201
184
|
}
|
|
202
185
|
function convertChainExpression(node) {
|
|
203
186
|
const properties = chainExpressionToFlatArray(node.expression);
|
|
204
187
|
const args = optionalChainToMapGetArguments(properties);
|
|
205
|
-
return
|
|
188
|
+
return functionInvocationEx('map.get', args);
|
|
206
189
|
}
|
|
207
190
|
function chainExpressionToFlatArray(node) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
191
|
+
switch (node.type) {
|
|
192
|
+
case TSESTree.AST_NODE_TYPES.Identifier:
|
|
193
|
+
case TSESTree.AST_NODE_TYPES.ChainExpression:
|
|
194
|
+
return [
|
|
195
|
+
{
|
|
196
|
+
property: node,
|
|
197
|
+
optional: false,
|
|
198
|
+
computed: false,
|
|
199
|
+
},
|
|
200
|
+
];
|
|
201
|
+
case TSESTree.AST_NODE_TYPES.MemberExpression: {
|
|
202
|
+
const data = {
|
|
203
|
+
property: throwIfPrivateIdentifier(node.property),
|
|
204
|
+
optional: node.optional,
|
|
205
|
+
computed: node.computed,
|
|
206
|
+
};
|
|
207
|
+
return chainExpressionToFlatArray(node.object).concat([data]);
|
|
211
208
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
else {
|
|
220
|
-
return [
|
|
221
|
-
{
|
|
222
|
-
property: node,
|
|
223
|
-
optional: false,
|
|
224
|
-
computed: false,
|
|
225
|
-
},
|
|
226
|
-
];
|
|
209
|
+
case AST_NODE_TYPES.TSAsExpression:
|
|
210
|
+
case AST_NODE_TYPES.TSNonNullExpression:
|
|
211
|
+
case AST_NODE_TYPES.TSInstantiationExpression:
|
|
212
|
+
case AST_NODE_TYPES.TSSatisfiesExpression:
|
|
213
|
+
return chainExpressionToFlatArray(node.expression);
|
|
214
|
+
default:
|
|
215
|
+
throw new WorkflowSyntaxError(`Type ${node.type} is not supported in optional chaining`, node.loc);
|
|
227
216
|
}
|
|
228
217
|
}
|
|
229
218
|
function optionalChainToMapGetArguments(properties) {
|
|
230
|
-
if (properties.length <= 0) {
|
|
231
|
-
// this shouldn't happen
|
|
232
|
-
return [];
|
|
233
|
-
}
|
|
234
219
|
let base;
|
|
235
220
|
let optionalSliceStart;
|
|
236
221
|
const firstOptional = properties.findIndex((p) => p.optional);
|
|
@@ -253,8 +238,8 @@ function optionalChainToMapGetArguments(properties) {
|
|
|
253
238
|
if (opt.computed) {
|
|
254
239
|
return propertyExp;
|
|
255
240
|
}
|
|
256
|
-
else if (
|
|
257
|
-
return
|
|
241
|
+
else if (isQualifiedName(propertyExp)) {
|
|
242
|
+
return stringEx(expressionToString(propertyExp));
|
|
258
243
|
}
|
|
259
244
|
else {
|
|
260
245
|
throw new WorkflowSyntaxError('Unexpected property in an optional chain', opt.property.loc);
|
|
@@ -262,7 +247,7 @@ function optionalChainToMapGetArguments(properties) {
|
|
|
262
247
|
});
|
|
263
248
|
const args = [base];
|
|
264
249
|
if (optionals.length > 1) {
|
|
265
|
-
args.push(
|
|
250
|
+
args.push(listEx(optionals));
|
|
266
251
|
}
|
|
267
252
|
else if (optionals.length === 1) {
|
|
268
253
|
args.push(optionals[0]);
|
|
@@ -271,10 +256,10 @@ function optionalChainToMapGetArguments(properties) {
|
|
|
271
256
|
}
|
|
272
257
|
function memberExpressionFromList(properties) {
|
|
273
258
|
if (properties.length >= 2) {
|
|
274
|
-
const base =
|
|
259
|
+
const base = memberEx(convertExpression(properties[0].property), convertExpression(properties[1].property), properties[1].computed);
|
|
275
260
|
return properties
|
|
276
261
|
.slice(2)
|
|
277
|
-
.reduce((exp, current) =>
|
|
262
|
+
.reduce((exp, current) => memberEx(exp, convertExpression(current.property), current.computed), base);
|
|
278
263
|
}
|
|
279
264
|
else if (properties.length === 1) {
|
|
280
265
|
return convertExpression(properties[0].property);
|
|
@@ -288,8 +273,8 @@ function convertCallExpression(node) {
|
|
|
288
273
|
throw new WorkflowSyntaxError('Optional call expressions are not supported', node.loc);
|
|
289
274
|
}
|
|
290
275
|
const calleeExpression = convertExpression(node.callee);
|
|
291
|
-
if (
|
|
292
|
-
const calleeName = calleeExpression
|
|
276
|
+
if (isQualifiedName(calleeExpression)) {
|
|
277
|
+
const calleeName = expressionToString(calleeExpression);
|
|
293
278
|
if (isIntrinsic(calleeName)) {
|
|
294
279
|
let msg;
|
|
295
280
|
if (calleeName === 'call_step') {
|
|
@@ -302,7 +287,7 @@ function convertCallExpression(node) {
|
|
|
302
287
|
throw new WorkflowSyntaxError(msg, node.callee.loc);
|
|
303
288
|
}
|
|
304
289
|
const argumentExpressions = throwIfSpread(node.arguments).map(convertExpression);
|
|
305
|
-
return
|
|
290
|
+
return functionInvocationEx(calleeName, argumentExpressions);
|
|
306
291
|
}
|
|
307
292
|
else {
|
|
308
293
|
throw new WorkflowSyntaxError('Callee should be a qualified name', node.loc);
|
|
@@ -312,7 +297,7 @@ export function isIntrinsic(calleeName) {
|
|
|
312
297
|
const intrinsics = ['parallel', 'retry_policy', 'call_step'];
|
|
313
298
|
return intrinsics.includes(calleeName);
|
|
314
299
|
}
|
|
315
|
-
export function
|
|
300
|
+
export function isIntrinsicStatement(calleeName) {
|
|
316
301
|
const statementNames = ['parallel', 'retry_policy'];
|
|
317
302
|
return statementNames.includes(calleeName);
|
|
318
303
|
}
|
|
@@ -320,18 +305,15 @@ function convertConditionalExpression(node) {
|
|
|
320
305
|
const test = convertExpression(node.test);
|
|
321
306
|
const consequent = convertExpression(node.consequent);
|
|
322
307
|
const alternate = convertExpression(node.alternate);
|
|
323
|
-
return
|
|
308
|
+
return functionInvocationEx('if', [test, consequent, alternate]);
|
|
324
309
|
}
|
|
325
310
|
function convertTemplateLiteralToExpression(node) {
|
|
326
311
|
const stringTerms = node.quasis
|
|
327
312
|
.map((x) => x.value.cooked)
|
|
328
|
-
.map((x) =>
|
|
313
|
+
.map((x) => stringEx(x));
|
|
329
314
|
const templateTerms = node.expressions
|
|
330
315
|
.map(convertExpression)
|
|
331
|
-
.map((ex) =>
|
|
332
|
-
ex,
|
|
333
|
-
new PrimitiveExpression('null'),
|
|
334
|
-
]));
|
|
316
|
+
.map((ex) => functionInvocationEx('default', [ex, stringEx('null')]));
|
|
335
317
|
// interleave string parts and the expression parts starting with strings
|
|
336
318
|
const interleavedTerms = stringTerms
|
|
337
319
|
.slice(0, stringTerms.length - 1)
|
|
@@ -346,10 +328,10 @@ function convertTemplateLiteralToExpression(node) {
|
|
|
346
328
|
interleavedTerms.push(stringTerms[stringTerms.length - 1]);
|
|
347
329
|
}
|
|
348
330
|
if (interleavedTerms.length === 0) {
|
|
349
|
-
return
|
|
331
|
+
return stringEx('');
|
|
350
332
|
}
|
|
351
333
|
else {
|
|
352
|
-
return interleavedTerms.reduce((previous, current) =>
|
|
334
|
+
return interleavedTerms.reduce((previous, current) => binaryEx(previous, '+', current));
|
|
353
335
|
}
|
|
354
336
|
}
|
|
355
337
|
export function throwIfSpread(nodes) {
|
|
@@ -360,11 +342,16 @@ export function throwIfSpread(nodes) {
|
|
|
360
342
|
const argumentExpressions = nodes.filter((x) => x?.type !== AST_NODE_TYPES.SpreadElement);
|
|
361
343
|
return argumentExpressions;
|
|
362
344
|
}
|
|
363
|
-
export function
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
345
|
+
export function throwIfPrivateIdentifier(prop) {
|
|
346
|
+
if (prop.type === AST_NODE_TYPES.PrivateIdentifier) {
|
|
347
|
+
throw new WorkflowSyntaxError('Private identifier not supported', prop.loc);
|
|
348
|
+
}
|
|
349
|
+
return prop;
|
|
350
|
+
}
|
|
351
|
+
export function convertAssignmentTarget(node) {
|
|
352
|
+
const ex = convertExpression(node);
|
|
353
|
+
if (ex.tag !== 'variableReference' && ex.tag !== 'member') {
|
|
354
|
+
throw new WorkflowSyntaxError('An assignment taget must be an identifer or a member expression', node.loc);
|
|
368
355
|
}
|
|
369
356
|
return ex;
|
|
370
357
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { TSESTree } from '@typescript-eslint/typescript-estree';
|
|
2
|
+
import { WorkflowStatement } from '../ast/statements.js';
|
|
3
|
+
export interface ParsingContext {
|
|
4
|
+
readonly parallelNestingLevel?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function parseStatement(node: TSESTree.Statement, ctx: ParsingContext): WorkflowStatement[];
|
|
7
|
+
//# sourceMappingURL=parsestatement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parsestatement.d.ts","sourceRoot":"","sources":["../../src/transpiler/parsestatement.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,QAAQ,EAAE,MAAM,sCAAsC,CAAA;AAoB/E,OAAO,EAiBL,iBAAiB,EAMlB,MAAM,sBAAsB,CAAA;AAa7B,MAAM,WAAW,cAAc;IAG7B,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAA;CACvC;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,QAAQ,CAAC,SAAS,EACxB,GAAG,EAAE,cAAc,GAClB,iBAAiB,EAAE,CAiFrB"}
|