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,610 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.apiBlocks = void 0;
7
+ const jsonpath_plus_1 = require("jsonpath-plus");
8
+ const xpath_1 = __importDefault(require("xpath"));
9
+ const xmldom_1 = require("xmldom");
10
+ // API Testing Blocks
11
+ exports.apiBlocks = [
12
+ // ============================================
13
+ // HEADER MANAGEMENT BLOCKS
14
+ // ============================================
15
+ // Set a single header
16
+ {
17
+ type: 'api_set_header',
18
+ category: 'API',
19
+ color: '#7B1FA2',
20
+ tooltip: 'Set a request header (applies to subsequent requests)',
21
+ inputs: [
22
+ { name: 'NAME', type: 'field', fieldType: 'text', required: true, default: 'Authorization' },
23
+ { name: 'VALUE', type: 'field', fieldType: 'text', required: true, default: 'Bearer token' },
24
+ ],
25
+ previousStatement: true,
26
+ nextStatement: true,
27
+ execute: async (params, context) => {
28
+ const name = params.NAME;
29
+ const value = resolveVariables(params.VALUE, context);
30
+ // Get or create headers map
31
+ let headers = context.variables.get('__requestHeaders');
32
+ if (!headers) {
33
+ headers = {};
34
+ }
35
+ headers[name] = value;
36
+ context.variables.set('__requestHeaders', headers);
37
+ context.logger.info(`Set header: ${name}: ${value.substring(0, 30)}${value.length > 30 ? '...' : ''}`);
38
+ return {
39
+ _summary: `${name}: ${value.substring(0, 30)}${value.length > 30 ? '...' : ''}`,
40
+ name,
41
+ value,
42
+ };
43
+ },
44
+ },
45
+ // Set multiple headers at once
46
+ {
47
+ type: 'api_set_headers',
48
+ category: 'API',
49
+ color: '#7B1FA2',
50
+ tooltip: 'Set multiple request headers from JSON object',
51
+ inputs: [
52
+ { name: 'HEADERS', type: 'field', fieldType: 'text', required: true, default: '{"Content-Type": "application/json"}' },
53
+ ],
54
+ previousStatement: true,
55
+ nextStatement: true,
56
+ execute: async (params, context) => {
57
+ const headersStr = resolveVariables(params.HEADERS, context);
58
+ try {
59
+ const newHeaders = JSON.parse(headersStr);
60
+ // Get or create headers map
61
+ let headers = context.variables.get('__requestHeaders');
62
+ if (!headers) {
63
+ headers = {};
64
+ }
65
+ // Merge new headers
66
+ Object.assign(headers, newHeaders);
67
+ context.variables.set('__requestHeaders', headers);
68
+ const headerNames = Object.keys(newHeaders).join(', ');
69
+ context.logger.info(`Set headers: ${headerNames}`);
70
+ return {
71
+ _summary: `Set ${Object.keys(newHeaders).length} headers`,
72
+ headers: newHeaders,
73
+ };
74
+ }
75
+ catch {
76
+ throw new Error(`Invalid JSON for headers: ${headersStr}`);
77
+ }
78
+ },
79
+ },
80
+ // Clear all headers
81
+ {
82
+ type: 'api_clear_headers',
83
+ category: 'API',
84
+ color: '#7B1FA2',
85
+ tooltip: 'Clear all request headers',
86
+ inputs: [],
87
+ previousStatement: true,
88
+ nextStatement: true,
89
+ execute: async (params, context) => {
90
+ context.variables.delete('__requestHeaders');
91
+ context.logger.info('Cleared all request headers');
92
+ return { _summary: 'Headers cleared' };
93
+ },
94
+ },
95
+ // ============================================
96
+ // STATEMENT BLOCKS - Chain together visually
97
+ // ============================================
98
+ // HTTP GET Request (Statement - stores response)
99
+ {
100
+ type: 'api_get',
101
+ category: 'API',
102
+ color: '#4CAF50',
103
+ tooltip: 'Perform HTTP GET request and store response',
104
+ inputs: [
105
+ { name: 'URL', type: 'field', fieldType: 'text', required: true },
106
+ { name: 'HEADERS', type: 'field', fieldType: 'text', default: '' },
107
+ ],
108
+ previousStatement: true,
109
+ nextStatement: true,
110
+ execute: async (params, context) => {
111
+ const url = resolveVariables(params.URL, context);
112
+ const contextHeaders = context.variables.get('__requestHeaders') || {};
113
+ const inlineHeaders = parseHeaders(params.HEADERS, context);
114
+ const headers = { ...contextHeaders, ...inlineHeaders };
115
+ context.logger.info(`GET ${url}`);
116
+ const response = await fetch(url, {
117
+ headers,
118
+ signal: context.abortSignal,
119
+ });
120
+ const parsed = await parseResponse(response);
121
+ context.variables.set('__lastResponse', parsed);
122
+ return parsed;
123
+ },
124
+ },
125
+ // HTTP POST Request (Statement)
126
+ {
127
+ type: 'api_post',
128
+ category: 'API',
129
+ color: '#4CAF50',
130
+ tooltip: 'Perform HTTP POST request and store response',
131
+ inputs: [
132
+ { name: 'URL', type: 'field', fieldType: 'text', required: true },
133
+ { name: 'BODY', type: 'field', fieldType: 'text', default: '{}' },
134
+ { name: 'HEADERS', type: 'field', fieldType: 'text', default: '' },
135
+ ],
136
+ previousStatement: true,
137
+ nextStatement: true,
138
+ execute: async (params, context) => {
139
+ const url = resolveVariables(params.URL, context);
140
+ const bodyStr = resolveVariables(params.BODY || '{}', context);
141
+ const contextHeaders = context.variables.get('__requestHeaders') || {};
142
+ const inlineHeaders = parseHeaders(params.HEADERS, context);
143
+ let body;
144
+ try {
145
+ body = JSON.parse(bodyStr);
146
+ }
147
+ catch {
148
+ body = bodyStr;
149
+ }
150
+ context.logger.info(`POST ${url}`);
151
+ const response = await fetch(url, {
152
+ method: 'POST',
153
+ headers: { 'Content-Type': 'application/json', ...contextHeaders, ...inlineHeaders },
154
+ body: typeof body === 'string' ? body : JSON.stringify(body),
155
+ signal: context.abortSignal,
156
+ });
157
+ const parsed = await parseResponse(response);
158
+ context.variables.set('__lastResponse', parsed);
159
+ return parsed;
160
+ },
161
+ },
162
+ // HTTP PUT Request (Statement)
163
+ {
164
+ type: 'api_put',
165
+ category: 'API',
166
+ color: '#4CAF50',
167
+ tooltip: 'Perform HTTP PUT request and store response',
168
+ inputs: [
169
+ { name: 'URL', type: 'field', fieldType: 'text', required: true },
170
+ { name: 'BODY', type: 'field', fieldType: 'text', default: '{}' },
171
+ { name: 'HEADERS', type: 'field', fieldType: 'text', default: '' },
172
+ ],
173
+ previousStatement: true,
174
+ nextStatement: true,
175
+ execute: async (params, context) => {
176
+ const url = resolveVariables(params.URL, context);
177
+ const bodyStr = resolveVariables(params.BODY || '{}', context);
178
+ const contextHeaders = context.variables.get('__requestHeaders') || {};
179
+ const inlineHeaders = parseHeaders(params.HEADERS, context);
180
+ let body;
181
+ try {
182
+ body = JSON.parse(bodyStr);
183
+ }
184
+ catch {
185
+ body = bodyStr;
186
+ }
187
+ context.logger.info(`PUT ${url}`);
188
+ const response = await fetch(url, {
189
+ method: 'PUT',
190
+ headers: { 'Content-Type': 'application/json', ...contextHeaders, ...inlineHeaders },
191
+ body: typeof body === 'string' ? body : JSON.stringify(body),
192
+ signal: context.abortSignal,
193
+ });
194
+ const parsed = await parseResponse(response);
195
+ context.variables.set('__lastResponse', parsed);
196
+ return parsed;
197
+ },
198
+ },
199
+ // HTTP PATCH Request (Statement)
200
+ {
201
+ type: 'api_patch',
202
+ category: 'API',
203
+ color: '#4CAF50',
204
+ tooltip: 'Perform HTTP PATCH request and store response',
205
+ inputs: [
206
+ { name: 'URL', type: 'field', fieldType: 'text', required: true },
207
+ { name: 'BODY', type: 'field', fieldType: 'text', default: '{}' },
208
+ { name: 'HEADERS', type: 'field', fieldType: 'text', default: '' },
209
+ ],
210
+ previousStatement: true,
211
+ nextStatement: true,
212
+ execute: async (params, context) => {
213
+ const url = resolveVariables(params.URL, context);
214
+ const bodyStr = resolveVariables(params.BODY || '{}', context);
215
+ const contextHeaders = context.variables.get('__requestHeaders') || {};
216
+ const inlineHeaders = parseHeaders(params.HEADERS, context);
217
+ let body;
218
+ try {
219
+ body = JSON.parse(bodyStr);
220
+ }
221
+ catch {
222
+ body = bodyStr;
223
+ }
224
+ context.logger.info(`PATCH ${url}`);
225
+ const response = await fetch(url, {
226
+ method: 'PATCH',
227
+ headers: { 'Content-Type': 'application/json', ...contextHeaders, ...inlineHeaders },
228
+ body: typeof body === 'string' ? body : JSON.stringify(body),
229
+ signal: context.abortSignal,
230
+ });
231
+ const parsed = await parseResponse(response);
232
+ context.variables.set('__lastResponse', parsed);
233
+ return parsed;
234
+ },
235
+ },
236
+ // HTTP DELETE Request (Statement)
237
+ {
238
+ type: 'api_delete',
239
+ category: 'API',
240
+ color: '#4CAF50',
241
+ tooltip: 'Perform HTTP DELETE request and store response',
242
+ inputs: [
243
+ { name: 'URL', type: 'field', fieldType: 'text', required: true },
244
+ { name: 'HEADERS', type: 'field', fieldType: 'text', default: '' },
245
+ ],
246
+ previousStatement: true,
247
+ nextStatement: true,
248
+ execute: async (params, context) => {
249
+ const url = resolveVariables(params.URL, context);
250
+ const contextHeaders = context.variables.get('__requestHeaders') || {};
251
+ const inlineHeaders = parseHeaders(params.HEADERS, context);
252
+ const headers = { ...contextHeaders, ...inlineHeaders };
253
+ context.logger.info(`DELETE ${url}`);
254
+ const response = await fetch(url, {
255
+ method: 'DELETE',
256
+ headers,
257
+ signal: context.abortSignal,
258
+ });
259
+ const parsed = await parseResponse(response);
260
+ context.variables.set('__lastResponse', parsed);
261
+ return parsed;
262
+ },
263
+ },
264
+ // Assert Status Code (uses last response if not provided)
265
+ {
266
+ type: 'api_assert_status',
267
+ category: 'API',
268
+ color: '#FF9800',
269
+ tooltip: 'Assert that response has expected status code',
270
+ inputs: [
271
+ { name: 'STATUS', type: 'field', fieldType: 'number', default: 200, required: true },
272
+ ],
273
+ previousStatement: true,
274
+ nextStatement: true,
275
+ execute: async (params, context) => {
276
+ const response = context.variables.get('__lastResponse');
277
+ if (!response) {
278
+ throw new Error('No response available. Make sure to call an API request first.');
279
+ }
280
+ const expectedStatus = params.STATUS;
281
+ if (response.status !== expectedStatus) {
282
+ throw new Error(`Expected status ${expectedStatus} but got ${response.status}`);
283
+ }
284
+ context.logger.info(`✓ Status is ${expectedStatus}`);
285
+ return {
286
+ _summary: `✓ Status ${response.status} === ${expectedStatus}`,
287
+ expected: expectedStatus,
288
+ actual: response.status,
289
+ };
290
+ },
291
+ },
292
+ // Assert Response Body Contains
293
+ {
294
+ type: 'api_assert_body_contains',
295
+ category: 'API',
296
+ color: '#FF9800',
297
+ tooltip: 'Assert that response body contains expected value',
298
+ inputs: [
299
+ { name: 'PATH', type: 'field', fieldType: 'text', default: '' },
300
+ { name: 'VALUE', type: 'field', fieldType: 'text', required: true },
301
+ ],
302
+ previousStatement: true,
303
+ nextStatement: true,
304
+ execute: async (params, context) => {
305
+ const response = context.variables.get('__lastResponse');
306
+ if (!response) {
307
+ throw new Error('No response available. Make sure to call an API request first.');
308
+ }
309
+ const path = params.PATH;
310
+ const expectedValue = resolveVariables(params.VALUE, context);
311
+ const actualValue = path ? getValueByPath(response.body, path) : response.body;
312
+ const actualStr = typeof actualValue === 'string' ? actualValue : JSON.stringify(actualValue);
313
+ if (!actualStr.includes(expectedValue)) {
314
+ throw new Error(`Expected ${path || 'body'} to contain "${expectedValue}" but got "${actualStr}"`);
315
+ }
316
+ context.logger.info(`✓ ${path || 'body'} contains "${expectedValue}"`);
317
+ return {
318
+ _summary: `✓ ${path || 'body'} contains "${expectedValue}"`,
319
+ path: path || 'body',
320
+ expected: expectedValue,
321
+ actual: actualStr.substring(0, 100) + (actualStr.length > 100 ? '...' : ''),
322
+ };
323
+ },
324
+ },
325
+ // Extract Value using JSONPath
326
+ {
327
+ type: 'api_extract_jsonpath',
328
+ category: 'API',
329
+ color: '#2196F3',
330
+ tooltip: 'Extract a value from JSON response using JSONPath expression',
331
+ inputs: [
332
+ { name: 'JSONPATH', type: 'field', fieldType: 'text', required: true, default: '$.data.id' },
333
+ { name: 'VARIABLE', type: 'field', fieldType: 'text', required: true },
334
+ ],
335
+ previousStatement: true,
336
+ nextStatement: true,
337
+ execute: async (params, context) => {
338
+ const response = context.variables.get('__lastResponse');
339
+ if (!response) {
340
+ throw new Error('No response available. Make sure to call an API request first.');
341
+ }
342
+ const jsonPath = params.JSONPATH;
343
+ const varName = params.VARIABLE;
344
+ try {
345
+ const results = (0, jsonpath_plus_1.JSONPath)({ path: jsonPath, json: response.body });
346
+ // If single result, unwrap from array
347
+ const value = results.length === 1 ? results[0] : results;
348
+ context.variables.set(varName, value);
349
+ const valueStr = JSON.stringify(value);
350
+ context.logger.info(`Extracted (JSONPath) ${jsonPath} → ${varName} = ${valueStr}`);
351
+ return {
352
+ _summary: `${varName} = ${valueStr.substring(0, 50)}${valueStr.length > 50 ? '...' : ''}`,
353
+ variable: varName,
354
+ jsonPath,
355
+ value,
356
+ };
357
+ }
358
+ catch (e) {
359
+ throw new Error(`Invalid JSONPath expression: ${jsonPath}. Error: ${e.message}`);
360
+ }
361
+ },
362
+ },
363
+ // Extract Value using XPath (for XML/HTML responses)
364
+ {
365
+ type: 'api_extract_xpath',
366
+ category: 'API',
367
+ color: '#2196F3',
368
+ tooltip: 'Extract a value from XML/HTML response using XPath expression',
369
+ inputs: [
370
+ { name: 'XPATH', type: 'field', fieldType: 'text', required: true, default: '//title/text()' },
371
+ { name: 'VARIABLE', type: 'field', fieldType: 'text', required: true },
372
+ ],
373
+ previousStatement: true,
374
+ nextStatement: true,
375
+ execute: async (params, context) => {
376
+ const response = context.variables.get('__lastResponse');
377
+ if (!response) {
378
+ throw new Error('No response available. Make sure to call an API request first.');
379
+ }
380
+ const xpathExpr = params.XPATH;
381
+ const varName = params.VARIABLE;
382
+ try {
383
+ // Ensure body is a string for XML parsing
384
+ const xmlString = typeof response.body === 'string'
385
+ ? response.body
386
+ : JSON.stringify(response.body);
387
+ const doc = new xmldom_1.DOMParser().parseFromString(xmlString, 'text/xml');
388
+ const nodes = xpath_1.default.select(xpathExpr, doc);
389
+ // Convert nodes to values
390
+ let value;
391
+ if (Array.isArray(nodes)) {
392
+ if (nodes.length === 0) {
393
+ value = null;
394
+ }
395
+ else if (nodes.length === 1) {
396
+ value = getNodeValue(nodes[0]);
397
+ }
398
+ else {
399
+ value = nodes.map(getNodeValue);
400
+ }
401
+ }
402
+ else {
403
+ value = nodes;
404
+ }
405
+ context.variables.set(varName, value);
406
+ const valueStr = JSON.stringify(value);
407
+ context.logger.info(`Extracted (XPath) ${xpathExpr} → ${varName} = ${valueStr}`);
408
+ return {
409
+ _summary: `${varName} = ${valueStr.substring(0, 50)}${valueStr.length > 50 ? '...' : ''}`,
410
+ variable: varName,
411
+ xpath: xpathExpr,
412
+ value,
413
+ };
414
+ }
415
+ catch (e) {
416
+ throw new Error(`XPath extraction failed: ${xpathExpr}. Error: ${e.message}`);
417
+ }
418
+ },
419
+ },
420
+ // Legacy extract (simple dot notation) - kept for backwards compatibility
421
+ {
422
+ type: 'api_extract',
423
+ category: 'API',
424
+ color: '#2196F3',
425
+ tooltip: 'Extract a value from response using dot notation (e.g., data.user.name)',
426
+ inputs: [
427
+ { name: 'PATH', type: 'field', fieldType: 'text', required: true },
428
+ { name: 'VARIABLE', type: 'field', fieldType: 'text', required: true },
429
+ ],
430
+ previousStatement: true,
431
+ nextStatement: true,
432
+ execute: async (params, context) => {
433
+ const response = context.variables.get('__lastResponse');
434
+ if (!response) {
435
+ throw new Error('No response available. Make sure to call an API request first.');
436
+ }
437
+ const path = params.PATH;
438
+ const varName = params.VARIABLE;
439
+ const value = getValueByPath(response.body, path);
440
+ context.variables.set(varName, value);
441
+ const valueStr = JSON.stringify(value);
442
+ context.logger.info(`Extracted ${path} → ${varName} = ${valueStr}`);
443
+ return {
444
+ _summary: `${varName} = ${valueStr.substring(0, 50)}${valueStr.length > 50 ? '...' : ''}`,
445
+ variable: varName,
446
+ path,
447
+ value,
448
+ };
449
+ },
450
+ },
451
+ // ============================================
452
+ // VALUE BLOCKS - For advanced compositions
453
+ // ============================================
454
+ // Create Headers Object
455
+ {
456
+ type: 'api_headers',
457
+ category: 'API',
458
+ color: '#9C27B0',
459
+ tooltip: 'Create a headers object',
460
+ inputs: [
461
+ { name: 'AUTH_TYPE', type: 'field', fieldType: 'dropdown', options: [['None', 'none'], ['Bearer Token', 'bearer'], ['Basic Auth', 'basic'], ['API Key', 'apikey']] },
462
+ { name: 'AUTH_VALUE', type: 'field', fieldType: 'text' },
463
+ { name: 'CUSTOM', type: 'value', check: 'Object' },
464
+ ],
465
+ output: { type: 'Object' },
466
+ execute: async (params, context) => {
467
+ const authType = params.AUTH_TYPE;
468
+ const authValue = resolveVariables(params.AUTH_VALUE || '', context);
469
+ const custom = params.CUSTOM || {};
470
+ const headers = { ...custom };
471
+ switch (authType) {
472
+ case 'bearer':
473
+ headers['Authorization'] = `Bearer ${authValue}`;
474
+ break;
475
+ case 'basic':
476
+ headers['Authorization'] = `Basic ${Buffer.from(authValue).toString('base64')}`;
477
+ break;
478
+ case 'apikey':
479
+ headers['X-API-Key'] = authValue;
480
+ break;
481
+ }
482
+ return headers;
483
+ },
484
+ },
485
+ // Create JSON Body
486
+ {
487
+ type: 'api_json_body',
488
+ category: 'API',
489
+ color: '#9C27B0',
490
+ tooltip: 'Create a JSON body from key-value pairs or raw JSON',
491
+ inputs: [
492
+ { name: 'JSON', type: 'field', fieldType: 'text', default: '{}' },
493
+ ],
494
+ output: { type: 'Object' },
495
+ execute: async (params, context) => {
496
+ const json = resolveVariables(params.JSON, context);
497
+ return JSON.parse(json);
498
+ },
499
+ },
500
+ ];
501
+ // Helper functions
502
+ function resolveVariables(text, context) {
503
+ // Match ${varName} or ${varName.property.path}
504
+ return text.replace(/\$\{([\w.]+)\}/g, (match, path) => {
505
+ const parts = path.split('.');
506
+ const varName = parts[0];
507
+ let value = context.variables.get(varName);
508
+ // Navigate through object properties if path has dots
509
+ if (parts.length > 1 && value !== undefined && value !== null) {
510
+ for (let i = 1; i < parts.length; i++) {
511
+ if (value === undefined || value === null)
512
+ break;
513
+ value = value[parts[i]];
514
+ }
515
+ }
516
+ if (value === undefined || value === null) {
517
+ return match; // Keep original if not found
518
+ }
519
+ // Return stringified value
520
+ if (typeof value === 'object') {
521
+ return JSON.stringify(value);
522
+ }
523
+ return String(value);
524
+ });
525
+ }
526
+ function parseHeaders(headersStr, context) {
527
+ if (!headersStr || !headersStr.trim())
528
+ return {};
529
+ const resolved = resolveVariables(headersStr, context);
530
+ // Try JSON format first: {"Authorization": "Bearer token"}
531
+ try {
532
+ const parsed = JSON.parse(resolved);
533
+ if (typeof parsed === 'object' && parsed !== null) {
534
+ return parsed;
535
+ }
536
+ }
537
+ catch {
538
+ // Not JSON, try key:value format
539
+ }
540
+ // Try key:value format (one per line or semicolon-separated)
541
+ // Example: "Authorization: Bearer token; Content-Type: application/json"
542
+ const headers = {};
543
+ const pairs = resolved.split(/[;\n]/).map(s => s.trim()).filter(s => s);
544
+ for (const pair of pairs) {
545
+ const colonIndex = pair.indexOf(':');
546
+ if (colonIndex > 0) {
547
+ const key = pair.slice(0, colonIndex).trim();
548
+ const value = pair.slice(colonIndex + 1).trim();
549
+ headers[key] = value;
550
+ }
551
+ }
552
+ return headers;
553
+ }
554
+ async function parseResponse(response) {
555
+ const headers = {};
556
+ response.headers.forEach((value, key) => {
557
+ headers[key] = value;
558
+ });
559
+ let body;
560
+ const contentType = response.headers.get('content-type') || '';
561
+ if (contentType.includes('application/json')) {
562
+ body = await response.json();
563
+ }
564
+ else {
565
+ body = await response.text();
566
+ }
567
+ return { status: response.status, headers, body };
568
+ }
569
+ function getValueByPath(obj, path) {
570
+ if (!path)
571
+ return obj;
572
+ const parts = path.split('.');
573
+ let current = obj;
574
+ for (const part of parts) {
575
+ if (current === null || current === undefined)
576
+ return undefined;
577
+ // Handle array indexing like "items[0]"
578
+ const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
579
+ if (arrayMatch) {
580
+ const [, key, index] = arrayMatch;
581
+ current = current[key];
582
+ if (Array.isArray(current)) {
583
+ current = current[parseInt(index, 10)];
584
+ }
585
+ }
586
+ else {
587
+ current = current[part];
588
+ }
589
+ }
590
+ return current;
591
+ }
592
+ // Helper to extract value from XPath node
593
+ function getNodeValue(node) {
594
+ if (!node)
595
+ return null;
596
+ const n = node;
597
+ // Text node or attribute
598
+ if (n.nodeValue !== undefined) {
599
+ return n.nodeValue;
600
+ }
601
+ // Element node - get text content
602
+ if (n.textContent !== undefined) {
603
+ return n.textContent;
604
+ }
605
+ // Fallback to string representation
606
+ if (n.toString) {
607
+ return n.toString();
608
+ }
609
+ return null;
610
+ }
@@ -0,0 +1,2 @@
1
+ import { BlockDefinition } from '../types';
2
+ export declare const dataDrivenBlocks: BlockDefinition[];