testblocks 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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +333 -0
  3. package/dist/cli/executor.d.ts +32 -0
  4. package/dist/cli/executor.js +517 -0
  5. package/dist/cli/index.d.ts +2 -0
  6. package/dist/cli/index.js +411 -0
  7. package/dist/cli/reporters.d.ts +62 -0
  8. package/dist/cli/reporters.js +451 -0
  9. package/dist/client/assets/index-4hbFPUhP.js +2087 -0
  10. package/dist/client/assets/index-4hbFPUhP.js.map +1 -0
  11. package/dist/client/assets/index-Dnk1ti7l.css +1 -0
  12. package/dist/client/index.html +25 -0
  13. package/dist/core/blocks/api.d.ts +2 -0
  14. package/dist/core/blocks/api.js +610 -0
  15. package/dist/core/blocks/data-driven.d.ts +2 -0
  16. package/dist/core/blocks/data-driven.js +245 -0
  17. package/dist/core/blocks/index.d.ts +15 -0
  18. package/dist/core/blocks/index.js +71 -0
  19. package/dist/core/blocks/lifecycle.d.ts +2 -0
  20. package/dist/core/blocks/lifecycle.js +199 -0
  21. package/dist/core/blocks/logic.d.ts +2 -0
  22. package/dist/core/blocks/logic.js +357 -0
  23. package/dist/core/blocks/playwright.d.ts +2 -0
  24. package/dist/core/blocks/playwright.js +764 -0
  25. package/dist/core/blocks/procedures.d.ts +5 -0
  26. package/dist/core/blocks/procedures.js +321 -0
  27. package/dist/core/index.d.ts +5 -0
  28. package/dist/core/index.js +44 -0
  29. package/dist/core/plugins.d.ts +66 -0
  30. package/dist/core/plugins.js +118 -0
  31. package/dist/core/types.d.ts +153 -0
  32. package/dist/core/types.js +2 -0
  33. package/dist/server/codegenManager.d.ts +54 -0
  34. package/dist/server/codegenManager.js +259 -0
  35. package/dist/server/codegenParser.d.ts +17 -0
  36. package/dist/server/codegenParser.js +598 -0
  37. package/dist/server/executor.d.ts +37 -0
  38. package/dist/server/executor.js +672 -0
  39. package/dist/server/globals.d.ts +85 -0
  40. package/dist/server/globals.js +273 -0
  41. package/dist/server/index.d.ts +2 -0
  42. package/dist/server/index.js +361 -0
  43. package/dist/server/plugins.d.ts +55 -0
  44. package/dist/server/plugins.js +206 -0
  45. package/package.json +103 -0
@@ -0,0 +1,245 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dataDrivenBlocks = void 0;
4
+ // Data-Driven Testing Blocks
5
+ exports.dataDrivenBlocks = [
6
+ // Define test data inline
7
+ {
8
+ type: 'data_define',
9
+ category: 'Data',
10
+ color: '#00897B',
11
+ tooltip: 'Define test data sets for data-driven testing',
12
+ inputs: [
13
+ { name: 'DATA_JSON', type: 'field', fieldType: 'text', default: '[{"name": "test1", "value": 1}]' },
14
+ ],
15
+ output: { type: 'Array' },
16
+ execute: async (params, context) => {
17
+ const json = resolveVariables(params.DATA_JSON, context);
18
+ return JSON.parse(json);
19
+ },
20
+ },
21
+ // Load data from variable
22
+ {
23
+ type: 'data_from_variable',
24
+ category: 'Data',
25
+ color: '#00897B',
26
+ tooltip: 'Get test data from a variable',
27
+ inputs: [
28
+ { name: 'NAME', type: 'field', fieldType: 'text', required: true },
29
+ ],
30
+ output: { type: 'Array' },
31
+ execute: async (params, context) => {
32
+ const name = params.NAME;
33
+ return context.variables.get(name) || [];
34
+ },
35
+ },
36
+ // Get current data value
37
+ {
38
+ type: 'data_get_current',
39
+ category: 'Data',
40
+ color: '#00897B',
41
+ tooltip: 'Get a value from the current data set',
42
+ inputs: [
43
+ { name: 'KEY', type: 'field', fieldType: 'text', required: true },
44
+ ],
45
+ output: { type: ['String', 'Number', 'Boolean', 'Object', 'Array'] },
46
+ execute: async (params, context) => {
47
+ const key = params.KEY;
48
+ if (!context.currentData) {
49
+ throw new Error('No data set available. This block must be used inside a data-driven test.');
50
+ }
51
+ const value = context.currentData.values[key];
52
+ if (value === undefined) {
53
+ context.logger.warn(`Data key "${key}" not found in current data set`);
54
+ }
55
+ return value;
56
+ },
57
+ },
58
+ // Get current iteration index
59
+ {
60
+ type: 'data_get_index',
61
+ category: 'Data',
62
+ color: '#00897B',
63
+ tooltip: 'Get the current data iteration index (0-based)',
64
+ inputs: [],
65
+ output: { type: 'Number' },
66
+ execute: async (params, context) => {
67
+ return context.dataIndex ?? 0;
68
+ },
69
+ },
70
+ // Get current data set name
71
+ {
72
+ type: 'data_get_name',
73
+ category: 'Data',
74
+ color: '#00897B',
75
+ tooltip: 'Get the name of the current data set',
76
+ inputs: [],
77
+ output: { type: 'String' },
78
+ execute: async (params, context) => {
79
+ return context.currentData?.name ?? `Iteration ${(context.dataIndex ?? 0) + 1}`;
80
+ },
81
+ },
82
+ // For each data - iterate over data array
83
+ {
84
+ type: 'data_foreach',
85
+ category: 'Data',
86
+ color: '#00897B',
87
+ tooltip: 'Run steps for each item in a data set',
88
+ inputs: [
89
+ { name: 'DATA', type: 'value', check: 'Array', required: true },
90
+ { name: 'ITEM_VAR', type: 'field', fieldType: 'text', default: 'item' },
91
+ { name: 'INDEX_VAR', type: 'field', fieldType: 'text', default: 'index' },
92
+ { name: 'DO', type: 'statement' },
93
+ ],
94
+ previousStatement: true,
95
+ nextStatement: true,
96
+ execute: async (params, _context) => {
97
+ const data = params.DATA;
98
+ const itemVar = params.ITEM_VAR;
99
+ const indexVar = params.INDEX_VAR;
100
+ return {
101
+ dataLoop: true,
102
+ data,
103
+ itemVar,
104
+ indexVar,
105
+ statement: 'DO',
106
+ };
107
+ },
108
+ },
109
+ // Create data row
110
+ {
111
+ type: 'data_row',
112
+ category: 'Data',
113
+ color: '#00897B',
114
+ tooltip: 'Create a single data row with key-value pairs',
115
+ inputs: [
116
+ { name: 'NAME', type: 'field', fieldType: 'text', default: '' },
117
+ { name: 'JSON', type: 'field', fieldType: 'text', default: '{}' },
118
+ ],
119
+ output: { type: 'Object' },
120
+ execute: async (params, context) => {
121
+ const name = params.NAME;
122
+ const json = resolveVariables(params.JSON, context);
123
+ return {
124
+ name: name || undefined,
125
+ values: JSON.parse(json),
126
+ };
127
+ },
128
+ },
129
+ // Data table - create multiple rows at once
130
+ {
131
+ type: 'data_table',
132
+ category: 'Data',
133
+ color: '#00897B',
134
+ tooltip: 'Create a data table with headers and rows',
135
+ inputs: [
136
+ { name: 'HEADERS', type: 'field', fieldType: 'text', default: 'username, password, expected' },
137
+ { name: 'ROWS', type: 'field', fieldType: 'text', default: 'user1, pass1, true\nuser2, pass2, false' },
138
+ ],
139
+ output: { type: 'Array' },
140
+ execute: async (params, context) => {
141
+ const headersStr = params.HEADERS;
142
+ const rowsStr = resolveVariables(params.ROWS, context);
143
+ const headers = headersStr.split(',').map(h => h.trim());
144
+ const rows = rowsStr.split('\n').filter(r => r.trim());
145
+ return rows.map((row, index) => {
146
+ const values = row.split(',').map(v => {
147
+ const trimmed = v.trim();
148
+ // Try to parse as JSON for numbers, booleans, etc.
149
+ try {
150
+ return JSON.parse(trimmed);
151
+ }
152
+ catch {
153
+ return trimmed;
154
+ }
155
+ });
156
+ const obj = {};
157
+ headers.forEach((header, i) => {
158
+ obj[header] = values[i];
159
+ });
160
+ return {
161
+ name: `Row ${index + 1}`,
162
+ values: obj,
163
+ };
164
+ });
165
+ },
166
+ },
167
+ // CSV-style data
168
+ {
169
+ type: 'data_csv',
170
+ category: 'Data',
171
+ color: '#00897B',
172
+ tooltip: 'Parse CSV-style data (first row as headers)',
173
+ inputs: [
174
+ { name: 'CSV', type: 'field', fieldType: 'text', default: 'name,value\ntest1,100\ntest2,200' },
175
+ ],
176
+ output: { type: 'Array' },
177
+ execute: async (params, context) => {
178
+ const csv = resolveVariables(params.CSV, context);
179
+ const lines = csv.split('\n').filter(l => l.trim());
180
+ if (lines.length < 2) {
181
+ return [];
182
+ }
183
+ const headers = lines[0].split(',').map(h => h.trim());
184
+ return lines.slice(1).map((line, index) => {
185
+ const values = line.split(',').map(v => {
186
+ const trimmed = v.trim();
187
+ try {
188
+ return JSON.parse(trimmed);
189
+ }
190
+ catch {
191
+ return trimmed;
192
+ }
193
+ });
194
+ const obj = {};
195
+ headers.forEach((header, i) => {
196
+ obj[header] = values[i];
197
+ });
198
+ return {
199
+ name: `Row ${index + 1}`,
200
+ values: obj,
201
+ };
202
+ });
203
+ },
204
+ },
205
+ // Generate range of numbers
206
+ {
207
+ type: 'data_range',
208
+ category: 'Data',
209
+ color: '#00897B',
210
+ tooltip: 'Generate a range of numbers as data',
211
+ inputs: [
212
+ { name: 'START', type: 'field', fieldType: 'number', default: 1 },
213
+ { name: 'END', type: 'field', fieldType: 'number', default: 10 },
214
+ { name: 'STEP', type: 'field', fieldType: 'number', default: 1 },
215
+ { name: 'VAR_NAME', type: 'field', fieldType: 'text', default: 'n' },
216
+ ],
217
+ output: { type: 'Array' },
218
+ execute: async (params, _context) => {
219
+ const start = params.START;
220
+ const end = params.END;
221
+ const step = params.STEP;
222
+ const varName = params.VAR_NAME;
223
+ const result = [];
224
+ for (let i = start; i <= end; i += step) {
225
+ result.push({
226
+ name: `${varName}=${i}`,
227
+ values: { [varName]: i },
228
+ });
229
+ }
230
+ return result;
231
+ },
232
+ },
233
+ ];
234
+ // Helper function
235
+ function resolveVariables(text, context) {
236
+ return text.replace(/\$\{(\w+)\}/g, (_, varName) => {
237
+ // First check current data
238
+ if (context.currentData?.values[varName] !== undefined) {
239
+ return String(context.currentData.values[varName]);
240
+ }
241
+ // Then check variables
242
+ const value = context.variables.get(varName);
243
+ return value !== undefined ? String(value) : '';
244
+ });
245
+ }
@@ -0,0 +1,15 @@
1
+ export { apiBlocks } from './api';
2
+ export { playwrightBlocks } from './playwright';
3
+ export { logicBlocks } from './logic';
4
+ export { lifecycleBlocks } from './lifecycle';
5
+ export { dataDrivenBlocks } from './data-driven';
6
+ export { procedureBlocks, registerProcedure, getProcedure, clearProcedures } from './procedures';
7
+ import { BlockDefinition } from '../types';
8
+ export declare const builtInBlocks: BlockDefinition[];
9
+ export declare const blockRegistry: Map<string, BlockDefinition>;
10
+ export declare function registerBlock(block: BlockDefinition): void;
11
+ export declare function registerBlocks(blocks: BlockDefinition[]): void;
12
+ export declare function getBlock(type: string): BlockDefinition | undefined;
13
+ export declare function getAllBlocks(): BlockDefinition[];
14
+ export declare function getBlocksByCategory(category: string): BlockDefinition[];
15
+ export declare function getCategories(): string[];
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.blockRegistry = exports.builtInBlocks = exports.clearProcedures = exports.getProcedure = exports.registerProcedure = exports.procedureBlocks = exports.dataDrivenBlocks = exports.lifecycleBlocks = exports.logicBlocks = exports.playwrightBlocks = exports.apiBlocks = void 0;
4
+ exports.registerBlock = registerBlock;
5
+ exports.registerBlocks = registerBlocks;
6
+ exports.getBlock = getBlock;
7
+ exports.getAllBlocks = getAllBlocks;
8
+ exports.getBlocksByCategory = getBlocksByCategory;
9
+ exports.getCategories = getCategories;
10
+ var api_1 = require("./api");
11
+ Object.defineProperty(exports, "apiBlocks", { enumerable: true, get: function () { return api_1.apiBlocks; } });
12
+ var playwright_1 = require("./playwright");
13
+ Object.defineProperty(exports, "playwrightBlocks", { enumerable: true, get: function () { return playwright_1.playwrightBlocks; } });
14
+ var logic_1 = require("./logic");
15
+ Object.defineProperty(exports, "logicBlocks", { enumerable: true, get: function () { return logic_1.logicBlocks; } });
16
+ var lifecycle_1 = require("./lifecycle");
17
+ Object.defineProperty(exports, "lifecycleBlocks", { enumerable: true, get: function () { return lifecycle_1.lifecycleBlocks; } });
18
+ var data_driven_1 = require("./data-driven");
19
+ Object.defineProperty(exports, "dataDrivenBlocks", { enumerable: true, get: function () { return data_driven_1.dataDrivenBlocks; } });
20
+ var procedures_1 = require("./procedures");
21
+ Object.defineProperty(exports, "procedureBlocks", { enumerable: true, get: function () { return procedures_1.procedureBlocks; } });
22
+ Object.defineProperty(exports, "registerProcedure", { enumerable: true, get: function () { return procedures_1.registerProcedure; } });
23
+ Object.defineProperty(exports, "getProcedure", { enumerable: true, get: function () { return procedures_1.getProcedure; } });
24
+ Object.defineProperty(exports, "clearProcedures", { enumerable: true, get: function () { return procedures_1.clearProcedures; } });
25
+ const api_2 = require("./api");
26
+ const playwright_2 = require("./playwright");
27
+ const logic_2 = require("./logic");
28
+ const lifecycle_2 = require("./lifecycle");
29
+ const data_driven_2 = require("./data-driven");
30
+ const procedures_2 = require("./procedures");
31
+ // All built-in blocks
32
+ exports.builtInBlocks = [
33
+ ...api_2.apiBlocks,
34
+ ...playwright_2.playwrightBlocks,
35
+ ...logic_2.logicBlocks,
36
+ ...lifecycle_2.lifecycleBlocks,
37
+ ...data_driven_2.dataDrivenBlocks,
38
+ ...procedures_2.procedureBlocks,
39
+ ];
40
+ // Block registry for runtime lookup
41
+ exports.blockRegistry = new Map();
42
+ // Initialize registry with built-in blocks
43
+ exports.builtInBlocks.forEach(block => {
44
+ exports.blockRegistry.set(block.type, block);
45
+ });
46
+ // Function to register custom blocks (for plugins)
47
+ function registerBlock(block) {
48
+ exports.blockRegistry.set(block.type, block);
49
+ }
50
+ // Function to register multiple blocks
51
+ function registerBlocks(blocks) {
52
+ blocks.forEach(block => registerBlock(block));
53
+ }
54
+ // Get a block definition by type
55
+ function getBlock(type) {
56
+ return exports.blockRegistry.get(type);
57
+ }
58
+ // Get all registered blocks
59
+ function getAllBlocks() {
60
+ return Array.from(exports.blockRegistry.values());
61
+ }
62
+ // Get blocks by category
63
+ function getBlocksByCategory(category) {
64
+ return getAllBlocks().filter(block => block.category === category);
65
+ }
66
+ // Get all categories
67
+ function getCategories() {
68
+ const categories = new Set();
69
+ getAllBlocks().forEach(block => categories.add(block.category));
70
+ return Array.from(categories);
71
+ }
@@ -0,0 +1,2 @@
1
+ import { BlockDefinition } from '../types';
2
+ export declare const lifecycleBlocks: BlockDefinition[];
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.lifecycleBlocks = void 0;
4
+ // Lifecycle and Hook Blocks
5
+ exports.lifecycleBlocks = [
6
+ // Test Case - container for test steps
7
+ {
8
+ type: 'test_case',
9
+ category: 'Tests',
10
+ color: '#1E88E5',
11
+ tooltip: 'A test case containing steps to execute',
12
+ inputs: [
13
+ { name: 'NAME', type: 'field', fieldType: 'multiline', default: 'Test Case' },
14
+ { name: 'STEPS', type: 'statement' },
15
+ ],
16
+ execute: async (params, _context) => {
17
+ // Marker block - steps are executed by the executor
18
+ return { testCase: true, name: params.NAME, statement: 'STEPS' };
19
+ },
20
+ },
21
+ // Data-Driven Test Case - runs multiple times with different data
22
+ {
23
+ type: 'test_case_data_driven',
24
+ category: 'Tests',
25
+ color: '#1565C0',
26
+ tooltip: 'A data-driven test case that runs for each row of data (CSV format: header row, then data rows)',
27
+ inputs: [
28
+ { name: 'NAME', type: 'field', fieldType: 'text', default: 'Data-Driven Test' },
29
+ { name: 'DATA', type: 'field', fieldType: 'multiline', default: 'username,password,expected\nuser1,pass1,true\nuser2,pass2,false' },
30
+ { name: 'STEPS', type: 'statement' },
31
+ ],
32
+ execute: async (params, _context) => {
33
+ // Marker block - actual data parsing and execution handled by executor
34
+ const csvData = params.DATA;
35
+ const rows = csvData.trim().split('\n').map(row => row.split(',').map(cell => cell.trim()));
36
+ if (rows.length < 2) {
37
+ return { testCase: true, name: params.NAME, dataDriven: true, data: [], statement: 'STEPS' };
38
+ }
39
+ const headers = rows[0];
40
+ const data = rows.slice(1).map((row, index) => {
41
+ const values = {};
42
+ headers.forEach((header, i) => {
43
+ let value = row[i] || '';
44
+ // Try to parse as boolean or number
45
+ if (value === 'true')
46
+ value = true;
47
+ else if (value === 'false')
48
+ value = false;
49
+ else if (!isNaN(Number(value)) && value !== '')
50
+ value = Number(value);
51
+ values[header] = value;
52
+ });
53
+ return { name: `Row ${index + 1}`, values };
54
+ });
55
+ return { testCase: true, name: params.NAME, dataDriven: true, data, statement: 'STEPS' };
56
+ },
57
+ },
58
+ // Before All - runs once before all tests
59
+ {
60
+ type: 'lifecycle_before_all',
61
+ category: 'Lifecycle',
62
+ color: '#8E24AA',
63
+ tooltip: 'Steps to run once before all tests in the suite',
64
+ inputs: [
65
+ { name: 'DO', type: 'statement' },
66
+ ],
67
+ execute: async (_params, _context) => {
68
+ // Marker block - actual execution handled by executor
69
+ return { lifecycle: 'beforeAll', statement: 'DO' };
70
+ },
71
+ },
72
+ // After All - runs once after all tests
73
+ {
74
+ type: 'lifecycle_after_all',
75
+ category: 'Lifecycle',
76
+ color: '#8E24AA',
77
+ tooltip: 'Steps to run once after all tests in the suite',
78
+ inputs: [
79
+ { name: 'DO', type: 'statement' },
80
+ ],
81
+ execute: async (_params, _context) => {
82
+ return { lifecycle: 'afterAll', statement: 'DO' };
83
+ },
84
+ },
85
+ // Before Each - runs before each test
86
+ {
87
+ type: 'lifecycle_before_each',
88
+ category: 'Lifecycle',
89
+ color: '#8E24AA',
90
+ tooltip: 'Steps to run before each test',
91
+ inputs: [
92
+ { name: 'DO', type: 'statement' },
93
+ ],
94
+ execute: async (_params, _context) => {
95
+ return { lifecycle: 'beforeEach', statement: 'DO' };
96
+ },
97
+ },
98
+ // After Each - runs after each test
99
+ {
100
+ type: 'lifecycle_after_each',
101
+ category: 'Lifecycle',
102
+ color: '#8E24AA',
103
+ tooltip: 'Steps to run after each test',
104
+ inputs: [
105
+ { name: 'DO', type: 'statement' },
106
+ ],
107
+ execute: async (_params, _context) => {
108
+ return { lifecycle: 'afterEach', statement: 'DO' };
109
+ },
110
+ },
111
+ // Setup - alias for beforeEach with clearer naming
112
+ {
113
+ type: 'lifecycle_setup',
114
+ category: 'Lifecycle',
115
+ color: '#8E24AA',
116
+ tooltip: 'Setup steps to run before the test',
117
+ inputs: [
118
+ { name: 'DESCRIPTION', type: 'field', fieldType: 'text', default: 'Setup' },
119
+ { name: 'DO', type: 'statement' },
120
+ ],
121
+ previousStatement: true,
122
+ nextStatement: true,
123
+ execute: async (params, context) => {
124
+ context.logger.info(`Setup: ${params.DESCRIPTION}`);
125
+ return { lifecycle: 'setup', statement: 'DO' };
126
+ },
127
+ },
128
+ // Teardown - alias for afterEach with clearer naming
129
+ {
130
+ type: 'lifecycle_teardown',
131
+ category: 'Lifecycle',
132
+ color: '#8E24AA',
133
+ tooltip: 'Teardown steps to run after the test',
134
+ inputs: [
135
+ { name: 'DESCRIPTION', type: 'field', fieldType: 'text', default: 'Teardown' },
136
+ { name: 'DO', type: 'statement' },
137
+ ],
138
+ previousStatement: true,
139
+ nextStatement: true,
140
+ execute: async (params, context) => {
141
+ context.logger.info(`Teardown: ${params.DESCRIPTION}`);
142
+ return { lifecycle: 'teardown', statement: 'DO' };
143
+ },
144
+ },
145
+ // Cleanup on failure - only runs if test fails
146
+ {
147
+ type: 'lifecycle_on_failure',
148
+ category: 'Lifecycle',
149
+ color: '#C62828',
150
+ tooltip: 'Steps to run only if the test fails',
151
+ inputs: [
152
+ { name: 'DO', type: 'statement' },
153
+ ],
154
+ previousStatement: true,
155
+ nextStatement: true,
156
+ execute: async (_params, _context) => {
157
+ return { lifecycle: 'onFailure', statement: 'DO' };
158
+ },
159
+ },
160
+ // Skip test conditionally
161
+ {
162
+ type: 'lifecycle_skip_if',
163
+ category: 'Lifecycle',
164
+ color: '#757575',
165
+ tooltip: 'Skip the rest of the test if condition is true',
166
+ inputs: [
167
+ { name: 'CONDITION', type: 'value', check: 'Boolean', required: true },
168
+ { name: 'REASON', type: 'field', fieldType: 'text', default: 'Condition not met' },
169
+ ],
170
+ previousStatement: true,
171
+ nextStatement: true,
172
+ execute: async (params, _context) => {
173
+ const condition = params.CONDITION;
174
+ const reason = params.REASON;
175
+ if (condition) {
176
+ throw { skip: true, reason };
177
+ }
178
+ },
179
+ },
180
+ // Retry on failure
181
+ {
182
+ type: 'lifecycle_retry',
183
+ category: 'Lifecycle',
184
+ color: '#FF6F00',
185
+ tooltip: 'Retry steps on failure',
186
+ inputs: [
187
+ { name: 'TIMES', type: 'field', fieldType: 'number', default: 3 },
188
+ { name: 'DELAY', type: 'field', fieldType: 'number', default: 1000 },
189
+ { name: 'DO', type: 'statement' },
190
+ ],
191
+ previousStatement: true,
192
+ nextStatement: true,
193
+ execute: async (params, _context) => {
194
+ const times = params.TIMES;
195
+ const delay = params.DELAY;
196
+ return { retry: true, times, delay, statement: 'DO' };
197
+ },
198
+ },
199
+ ];
@@ -0,0 +1,2 @@
1
+ import { BlockDefinition } from '../types';
2
+ export declare const logicBlocks: BlockDefinition[];