norn-cli 1.5.4 → 1.6.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/cli.js CHANGED
@@ -28203,7 +28203,7 @@ function getValueByPath(response, path10) {
28203
28203
  return void 0;
28204
28204
  }
28205
28205
  }
28206
- async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, workingDir, fullDocumentText, onProgress, callStack, sequenceArgs, apiDefinitions, tagFilterOptions, sequenceSources, executionContext) {
28206
+ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, workingDir, fullDocumentText, onProgress, callStack, sequenceArgs, apiDefinitions, tagFilterOptions, sequenceSources, executionContext, debugHooks) {
28207
28207
  const startTime = Date.now();
28208
28208
  const responses = [];
28209
28209
  const scriptResults = [];
@@ -28246,566 +28246,852 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
28246
28246
  let requestIndex = 0;
28247
28247
  const ifStack = [];
28248
28248
  const shouldSkip = () => ifStack.length > 0 && ifStack.some((v) => !v);
28249
- for (let stepIdx = 0; stepIdx < steps.length; stepIdx++) {
28250
- const step = steps[stepIdx];
28251
- if (step.type === "if") {
28252
- if (shouldSkip()) {
28253
- ifStack.push(false);
28254
- } else {
28255
- const conditionMet = evaluateIfCondition(step.content, responses, runtimeVariables, getValueByPath);
28256
- ifStack.push(conditionMet);
28257
- const stepResult = {
28258
- type: "print",
28259
- stepIndex: stepIdx,
28260
- print: {
28261
- title: `if ${step.content}: ${conditionMet ? "true" : "false"}`,
28262
- timestamp: Date.now() - startTime
28263
- }
28264
- };
28265
- orderedSteps.push(stepResult);
28266
- reportProgress(stepIdx, "if", `if ${step.content} \u2192 ${conditionMet}`, stepResult);
28267
- }
28268
- continue;
28269
- }
28270
- if (step.type === "endif") {
28271
- if (ifStack.length > 0) {
28272
- ifStack.pop();
28273
- }
28274
- continue;
28249
+ const resolveDebugStepLocation = (step) => {
28250
+ const sequenceName = executionContext?.sequenceName;
28251
+ const indexed = sequenceName ? executionContext?.sequenceLocationIndex?.get(sequenceName.toLowerCase()) : void 0;
28252
+ const startLine = executionContext?.sequenceStartLine ?? indexed?.startLine;
28253
+ return {
28254
+ filePath: executionContext?.filePath ?? indexed?.filePath,
28255
+ sequenceName,
28256
+ declarationLine: indexed?.declarationLine ?? executionContext?.sequenceStartLine,
28257
+ absoluteLine: startLine !== void 0 ? startLine + 1 + step.lineNumber : void 0,
28258
+ relativeLine: step.lineNumber,
28259
+ nestingDepth
28260
+ };
28261
+ };
28262
+ const emitBeforeStep = async (step, stepIdx) => {
28263
+ if (!debugHooks?.beforeStep) {
28264
+ return;
28275
28265
  }
28276
- if (shouldSkip()) {
28277
- continue;
28266
+ await debugHooks.beforeStep({
28267
+ stepType: step.type,
28268
+ stepContent: step.content,
28269
+ stepIndex: stepIdx,
28270
+ totalSteps,
28271
+ location: resolveDebugStepLocation(step),
28272
+ runtimeVariables,
28273
+ responses
28274
+ });
28275
+ };
28276
+ const emitAfterStep = async (step, stepIdx) => {
28277
+ if (!debugHooks?.afterStep) {
28278
+ return;
28278
28279
  }
28279
- if (step.type === "assertion") {
28280
- const parsed = parseAssertCommand(substituteVariables(step.content, runtimeVariables));
28281
- if (!parsed) {
28282
- errors.push(`Step ${stepIdx + 1}: Invalid assert command: ${step.content}`);
28283
- return {
28284
- name: "",
28285
- success: false,
28286
- responses,
28287
- scriptResults,
28288
- assertionResults,
28289
- steps: orderedSteps,
28290
- errors,
28291
- duration: Date.now() - startTime
28292
- };
28293
- }
28294
- const result = evaluateAssertion(parsed, responses, runtimeVariables, getValueByPath, responseIndexToVariable, workingDir);
28295
- assertionResults.push(result);
28296
- const stepResult = {
28297
- type: "assertion",
28298
- stepIndex: stepIdx,
28299
- assertion: result,
28300
- lineNumber: step.lineNumber
28301
- };
28302
- orderedSteps.push(stepResult);
28303
- const statusIcon = result.passed ? "\u2713" : "\u2717";
28304
- reportProgress(stepIdx, "assertion", `${statusIcon} assert ${result.expression}`, stepResult);
28305
- if (!result.passed) {
28306
- const failMessage = result.message ? `Assertion failed: ${result.message}` : `Assertion failed: ${result.expression}`;
28307
- errors.push(failMessage);
28308
- if (result.error) {
28309
- errors.push(` Error: ${result.error}`);
28310
- } else {
28311
- errors.push(` Expected: ${result.rightExpression ?? result.operator}`);
28312
- errors.push(` Actual: ${JSON.stringify(result.leftValue)}`);
28313
- }
28314
- return {
28315
- name: "",
28316
- success: false,
28317
- responses,
28318
- scriptResults,
28319
- assertionResults,
28320
- steps: orderedSteps,
28321
- errors,
28322
- duration: Date.now() - startTime
28323
- };
28324
- }
28325
- continue;
28280
+ await debugHooks.afterStep({
28281
+ stepType: step.type,
28282
+ stepContent: step.content,
28283
+ stepIndex: stepIdx,
28284
+ totalSteps,
28285
+ location: resolveDebugStepLocation(step),
28286
+ runtimeVariables,
28287
+ responses
28288
+ });
28289
+ };
28290
+ const emitFailure = async (message, step, stepIdx) => {
28291
+ if (!debugHooks?.onFailure) {
28292
+ return;
28326
28293
  }
28327
- if (step.type === "print") {
28328
- const parsed = parsePrintCommand(step.content);
28329
- const titleResolved = resolveBareVariables(parsed.title, runtimeVariables);
28330
- const title = substituteVariables(titleResolved, runtimeVariables);
28331
- const bodyResolved = parsed.body ? resolveBareVariables(parsed.body, runtimeVariables) : void 0;
28332
- const body = bodyResolved ? substituteVariables(bodyResolved, runtimeVariables) : void 0;
28333
- const stepResult = {
28334
- type: "print",
28335
- stepIndex: stepIdx,
28336
- print: {
28337
- title,
28338
- body,
28339
- timestamp: Date.now() - startTime
28294
+ await debugHooks.onFailure({
28295
+ message,
28296
+ stepIndex: stepIdx,
28297
+ location: step ? resolveDebugStepLocation(step) : void 0,
28298
+ runtimeVariables,
28299
+ responses
28300
+ });
28301
+ };
28302
+ if (debugHooks?.onSequenceEnter) {
28303
+ await debugHooks.onSequenceEnter({
28304
+ sequenceName: executionContext?.sequenceName,
28305
+ filePath: executionContext?.filePath,
28306
+ declarationLine: executionContext?.sequenceStartLine,
28307
+ nestingDepth,
28308
+ runtimeVariables,
28309
+ responses
28310
+ });
28311
+ }
28312
+ try {
28313
+ for (let stepIdx = 0; stepIdx < steps.length; stepIdx++) {
28314
+ const step = steps[stepIdx];
28315
+ await emitBeforeStep(step, stepIdx);
28316
+ try {
28317
+ if (step.type === "if") {
28318
+ if (shouldSkip()) {
28319
+ ifStack.push(false);
28320
+ } else {
28321
+ const conditionMet = evaluateIfCondition(step.content, responses, runtimeVariables, getValueByPath);
28322
+ ifStack.push(conditionMet);
28323
+ const stepResult = {
28324
+ type: "print",
28325
+ stepIndex: stepIdx,
28326
+ print: {
28327
+ title: `if ${step.content}: ${conditionMet ? "true" : "false"}`,
28328
+ timestamp: Date.now() - startTime
28329
+ }
28330
+ };
28331
+ orderedSteps.push(stepResult);
28332
+ reportProgress(stepIdx, "if", `if ${step.content} \u2192 ${conditionMet}`, stepResult);
28333
+ }
28334
+ continue;
28340
28335
  }
28341
- };
28342
- orderedSteps.push(stepResult);
28343
- reportProgress(stepIdx, "print", `print: ${title}`, stepResult);
28344
- continue;
28345
- }
28346
- if (step.type === "wait") {
28347
- const durationMs = parseWaitCommand(step.content);
28348
- reportProgress(stepIdx, "wait", `wait: ${durationMs}ms`);
28349
- await sleep2(durationMs);
28350
- const stepResult = {
28351
- type: "print",
28352
- stepIndex: stepIdx,
28353
- print: {
28354
- title: `Waited ${durationMs >= 1e3 ? durationMs / 1e3 + "s" : durationMs + "ms"}`,
28355
- timestamp: Date.now() - startTime
28336
+ if (step.type === "endif") {
28337
+ if (ifStack.length > 0) {
28338
+ ifStack.pop();
28339
+ }
28340
+ continue;
28356
28341
  }
28357
- };
28358
- orderedSteps.push(stepResult);
28359
- continue;
28360
- }
28361
- if (step.type === "json") {
28362
- const bareResolved = resolveBareVariables(step.content, runtimeVariables);
28363
- const parsed = parseJsonCommand(substituteVariables(bareResolved, runtimeVariables));
28364
- if (!parsed) {
28365
- errors.push(`Step ${stepIdx + 1}: Invalid json command: ${step.content}`);
28366
- return {
28367
- name: "",
28368
- success: false,
28369
- responses,
28370
- scriptResults,
28371
- assertionResults,
28372
- steps: orderedSteps,
28373
- errors,
28374
- duration: Date.now() - startTime
28375
- };
28376
- }
28377
- const result = readJsonFile(parsed.filePath, workingDir);
28378
- const stepResult = {
28379
- type: "json",
28380
- stepIndex: stepIdx,
28381
- json: {
28382
- varName: parsed.varName,
28383
- filePath: result.filePath,
28384
- success: result.success,
28385
- error: result.error,
28386
- timestamp: Date.now() - startTime
28342
+ if (shouldSkip()) {
28343
+ continue;
28387
28344
  }
28388
- };
28389
- orderedSteps.push(stepResult);
28390
- if (!result.success) {
28391
- errors.push(`Failed to load JSON file: ${result.error}`);
28392
- reportProgress(stepIdx, "json", `json: ${parsed.varName} \u2717 ${result.error}`, stepResult);
28393
- return {
28394
- name: "",
28395
- success: false,
28396
- responses,
28397
- scriptResults,
28398
- assertionResults,
28399
- steps: orderedSteps,
28400
- errors,
28401
- duration: Date.now() - startTime
28402
- };
28403
- }
28404
- runtimeVariables[parsed.varName] = JSON.stringify(result.data);
28405
- reportProgress(stepIdx, "json", `json: ${parsed.varName} = ${parsed.filePath}`, stepResult);
28406
- continue;
28407
- }
28408
- if (step.type === "propAssign") {
28409
- const parsed = parsePropertyAssignment(step.content);
28410
- if (!parsed) {
28411
- errors.push(`Step ${stepIdx + 1}: Invalid property assignment: ${step.content}`);
28412
- return {
28413
- name: "",
28414
- success: false,
28415
- responses,
28416
- scriptResults,
28417
- assertionResults,
28418
- steps: orderedSteps,
28419
- errors,
28420
- duration: Date.now() - startTime
28421
- };
28422
- }
28423
- if (!(parsed.varName in runtimeVariables)) {
28424
- errors.push(`Step ${stepIdx + 1}: Variable '${parsed.varName}' is not defined`);
28425
- return {
28426
- name: "",
28427
- success: false,
28428
- responses,
28429
- scriptResults,
28430
- assertionResults,
28431
- steps: orderedSteps,
28432
- errors,
28433
- duration: Date.now() - startTime
28434
- };
28435
- }
28436
- let jsonObj;
28437
- try {
28438
- jsonObj = JSON.parse(runtimeVariables[parsed.varName]);
28439
- } catch {
28440
- errors.push(`Step ${stepIdx + 1}: Variable '${parsed.varName}' is not a valid JSON object`);
28441
- return {
28442
- name: "",
28443
- success: false,
28444
- responses,
28445
- scriptResults,
28446
- assertionResults,
28447
- steps: orderedSteps,
28448
- errors,
28449
- duration: Date.now() - startTime
28450
- };
28451
- }
28452
- const valueTrimmed = parsed.value.trim();
28453
- const bareVarMatch = valueTrimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)((?:\.[a-zA-Z_][a-zA-Z0-9_]*|\[\d+\])*)$/);
28454
- let newValue;
28455
- if (bareVarMatch && bareVarMatch[1] in runtimeVariables) {
28456
- const varName = bareVarMatch[1];
28457
- const pathPart = bareVarMatch[2] || "";
28458
- let value = runtimeVariables[varName];
28459
- if (typeof value === "string") {
28460
- try {
28461
- const parsed2 = JSON.parse(value);
28462
- if (typeof parsed2 === "object" && parsed2 !== null) {
28463
- value = parsed2;
28345
+ if (step.type === "assertion") {
28346
+ const parsed = parseAssertCommand(substituteVariables(step.content, runtimeVariables));
28347
+ if (!parsed) {
28348
+ const invalidAssertMessage = `Step ${stepIdx + 1}: Invalid assert command: ${step.content}`;
28349
+ errors.push(invalidAssertMessage);
28350
+ await emitFailure(invalidAssertMessage, step, stepIdx);
28351
+ return {
28352
+ name: "",
28353
+ success: false,
28354
+ responses,
28355
+ scriptResults,
28356
+ assertionResults,
28357
+ steps: orderedSteps,
28358
+ errors,
28359
+ duration: Date.now() - startTime
28360
+ };
28361
+ }
28362
+ const result = evaluateAssertion(parsed, responses, runtimeVariables, getValueByPath, responseIndexToVariable, workingDir);
28363
+ assertionResults.push(result);
28364
+ const stepResult = {
28365
+ type: "assertion",
28366
+ stepIndex: stepIdx,
28367
+ assertion: result,
28368
+ lineNumber: step.lineNumber
28369
+ };
28370
+ orderedSteps.push(stepResult);
28371
+ const statusIcon = result.passed ? "\u2713" : "\u2717";
28372
+ reportProgress(stepIdx, "assertion", `${statusIcon} assert ${result.expression}`, stepResult);
28373
+ if (!result.passed) {
28374
+ const failMessage = result.message ? `Assertion failed: ${result.message}` : `Assertion failed: ${result.expression}`;
28375
+ errors.push(failMessage);
28376
+ if (result.error) {
28377
+ errors.push(` Error: ${result.error}`);
28378
+ } else {
28379
+ errors.push(` Expected: ${result.rightExpression ?? result.operator}`);
28380
+ errors.push(` Actual: ${JSON.stringify(result.leftValue)}`);
28464
28381
  }
28465
- } catch {
28382
+ await emitFailure(failMessage, step, stepIdx);
28383
+ return {
28384
+ name: "",
28385
+ success: false,
28386
+ responses,
28387
+ scriptResults,
28388
+ assertionResults,
28389
+ steps: orderedSteps,
28390
+ errors,
28391
+ duration: Date.now() - startTime
28392
+ };
28466
28393
  }
28394
+ continue;
28467
28395
  }
28468
- if (pathPart) {
28469
- const path10 = pathPart.replace(/^\./, "");
28470
- newValue = getNestedValueFromObject(value, path10);
28471
- } else {
28472
- newValue = value;
28473
- }
28474
- } else {
28475
- const bareResolvedValue = resolveBareVariables(parsed.value, runtimeVariables);
28476
- const resolvedValue = substituteVariables(bareResolvedValue, runtimeVariables);
28477
- newValue = resolvedValue;
28478
- try {
28479
- newValue = JSON.parse(resolvedValue);
28480
- } catch {
28396
+ if (step.type === "print") {
28397
+ const parsed = parsePrintCommand(step.content);
28398
+ const titleResolved = resolveBareVariables(parsed.title, runtimeVariables);
28399
+ const title = substituteVariables(titleResolved, runtimeVariables);
28400
+ const bodyResolved = parsed.body ? resolveBareVariables(parsed.body, runtimeVariables) : void 0;
28401
+ const body = bodyResolved ? substituteVariables(bodyResolved, runtimeVariables) : void 0;
28402
+ const stepResult = {
28403
+ type: "print",
28404
+ stepIndex: stepIdx,
28405
+ print: {
28406
+ title,
28407
+ body,
28408
+ timestamp: Date.now() - startTime
28409
+ }
28410
+ };
28411
+ orderedSteps.push(stepResult);
28412
+ reportProgress(stepIdx, "print", `print: ${title}`, stepResult);
28413
+ continue;
28481
28414
  }
28482
- }
28483
- const success = setNestedValue(jsonObj, parsed.propertyPath, newValue);
28484
- if (!success) {
28485
- errors.push(`Step ${stepIdx + 1}: Failed to set property '${parsed.propertyPath}' on '${parsed.varName}'`);
28486
- return {
28487
- name: "",
28488
- success: false,
28489
- responses,
28490
- scriptResults,
28491
- assertionResults,
28492
- steps: orderedSteps,
28493
- errors,
28494
- duration: Date.now() - startTime
28495
- };
28496
- }
28497
- runtimeVariables[parsed.varName] = JSON.stringify(jsonObj);
28498
- reportProgress(stepIdx, "propAssign", `${parsed.varName}.${parsed.propertyPath} = ${parsed.value}`, void 0);
28499
- continue;
28500
- }
28501
- if (step.type === "varAssign") {
28502
- const parsed = parseVarAssignCommand(step.content);
28503
- if (!parsed) {
28504
- errors.push(`Step ${stepIdx + 1}: Invalid variable assignment: ${step.content}`);
28505
- return {
28506
- name: "",
28507
- success: false,
28508
- responses,
28509
- scriptResults,
28510
- assertionResults,
28511
- steps: orderedSteps,
28512
- errors,
28513
- duration: Date.now() - startTime
28514
- };
28515
- }
28516
- const result = evaluateValueExpression(parsed.valueExpr, runtimeVariables);
28517
- if (result.error) {
28518
- errors.push(`Step ${stepIdx + 1}: ${result.error}`);
28519
- return {
28520
- name: "",
28521
- success: false,
28522
- responses,
28523
- scriptResults,
28524
- assertionResults,
28525
- steps: orderedSteps,
28526
- errors,
28527
- duration: Date.now() - startTime
28528
- };
28529
- }
28530
- runtimeVariables[parsed.varName] = result.value;
28531
- reportProgress(stepIdx, "propAssign", `var ${parsed.varName} = ${result.value}`, void 0);
28532
- continue;
28533
- }
28534
- if (step.type === "varRequest") {
28535
- const commandLine = step.content.split("\n")[0];
28536
- const parsed = parseVarRequestCommand(commandLine);
28537
- if (!parsed) {
28538
- errors.push(`Step ${stepIdx + 1}: Invalid variable request: ${commandLine}`);
28539
- return {
28540
- name: "",
28541
- success: false,
28542
- responses,
28543
- scriptResults,
28544
- assertionResults,
28545
- steps: orderedSteps,
28546
- errors,
28547
- duration: Date.now() - startTime
28548
- };
28549
- }
28550
- requestIndex++;
28551
- let resolvedUrl = "";
28552
- let resolvedHeaders = {};
28553
- let requestDescription = `${parsed.method} ${parsed.url}`;
28554
- try {
28555
- const endpointMatch = parsed.url.match(/^([a-zA-Z_][a-zA-Z0-9_]*)(?:\(([^)]*)\))?(\s+.*)?$/);
28556
- const potentialEndpointName = endpointMatch ? endpointMatch[1] : null;
28557
- const endpoint = apiDefinitions && potentialEndpointName ? getEndpoint({ headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints }, potentialEndpointName) : void 0;
28558
- if (endpoint && apiDefinitions) {
28559
- const paramsStr = endpointMatch[2] || "";
28560
- const headerGroupsStr = endpointMatch[3]?.trim() || "";
28561
- const paramTokens = paramsStr ? paramsStr.split(",").map((p) => p.trim()) : [];
28562
- const params = {};
28563
- const isNamedSyntax = paramTokens.length > 0 && paramTokens[0].includes(":");
28564
- if (isNamedSyntax) {
28565
- for (const token of paramTokens) {
28566
- const kvMatch = token.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.+)$/);
28567
- if (kvMatch) {
28568
- const paramName = kvMatch[1];
28569
- let paramValue = kvMatch[2].trim().replace(/^["']|["']$/g, "");
28570
- const bareResolved = resolveBareVariables(paramValue, runtimeVariables);
28571
- params[paramName] = substituteVariables(bareResolved, runtimeVariables);
28572
- }
28415
+ if (step.type === "wait") {
28416
+ const durationMs = parseWaitCommand(step.content);
28417
+ reportProgress(stepIdx, "wait", `wait: ${durationMs}ms`);
28418
+ await sleep2(durationMs);
28419
+ const stepResult = {
28420
+ type: "print",
28421
+ stepIndex: stepIdx,
28422
+ print: {
28423
+ title: `Waited ${durationMs >= 1e3 ? durationMs / 1e3 + "s" : durationMs + "ms"}`,
28424
+ timestamp: Date.now() - startTime
28573
28425
  }
28574
- } else {
28575
- endpoint.parameters.forEach((paramName, idx) => {
28576
- if (paramTokens[idx] !== void 0) {
28577
- let paramValue = paramTokens[idx].replace(/^["']|["']$/g, "");
28578
- const bareResolved = resolveBareVariables(paramValue, runtimeVariables);
28579
- params[paramName] = substituteVariables(bareResolved, runtimeVariables);
28580
- }
28581
- });
28426
+ };
28427
+ orderedSteps.push(stepResult);
28428
+ continue;
28429
+ }
28430
+ if (step.type === "json") {
28431
+ const bareResolved = resolveBareVariables(step.content, runtimeVariables);
28432
+ const parsed = parseJsonCommand(substituteVariables(bareResolved, runtimeVariables));
28433
+ if (!parsed) {
28434
+ errors.push(`Step ${stepIdx + 1}: Invalid json command: ${step.content}`);
28435
+ return {
28436
+ name: "",
28437
+ success: false,
28438
+ responses,
28439
+ scriptResults,
28440
+ assertionResults,
28441
+ steps: orderedSteps,
28442
+ errors,
28443
+ duration: Date.now() - startTime
28444
+ };
28582
28445
  }
28583
- let pathWithVars = substituteVariables(endpoint.path, runtimeVariables);
28584
- for (const paramName of endpoint.parameters) {
28585
- const value = params[paramName];
28586
- if (value !== void 0) {
28587
- pathWithVars = pathWithVars.replace(`{${paramName}}`, value);
28446
+ const result = readJsonFile(parsed.filePath, workingDir);
28447
+ const stepResult = {
28448
+ type: "json",
28449
+ stepIndex: stepIdx,
28450
+ json: {
28451
+ varName: parsed.varName,
28452
+ filePath: result.filePath,
28453
+ success: result.success,
28454
+ error: result.error,
28455
+ timestamp: Date.now() - startTime
28588
28456
  }
28457
+ };
28458
+ orderedSteps.push(stepResult);
28459
+ if (!result.success) {
28460
+ errors.push(`Failed to load JSON file: ${result.error}`);
28461
+ reportProgress(stepIdx, "json", `json: ${parsed.varName} \u2717 ${result.error}`, stepResult);
28462
+ return {
28463
+ name: "",
28464
+ success: false,
28465
+ responses,
28466
+ scriptResults,
28467
+ assertionResults,
28468
+ steps: orderedSteps,
28469
+ errors,
28470
+ duration: Date.now() - startTime
28471
+ };
28589
28472
  }
28590
- resolvedUrl = pathWithVars;
28591
- if (headerGroupsStr) {
28592
- const headerGroupNames = headerGroupsStr.split(/\s+/).filter((n) => n);
28593
- for (const groupName of headerGroupNames) {
28594
- const group = getHeaderGroup(
28595
- { headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints },
28596
- groupName
28597
- );
28598
- if (group) {
28599
- const groupHeaders = resolveHeaderValues(group, runtimeVariables);
28600
- Object.assign(resolvedHeaders, groupHeaders);
28473
+ runtimeVariables[parsed.varName] = JSON.stringify(result.data);
28474
+ reportProgress(stepIdx, "json", `json: ${parsed.varName} = ${parsed.filePath}`, stepResult);
28475
+ continue;
28476
+ }
28477
+ if (step.type === "propAssign") {
28478
+ const parsed = parsePropertyAssignment(step.content);
28479
+ if (!parsed) {
28480
+ errors.push(`Step ${stepIdx + 1}: Invalid property assignment: ${step.content}`);
28481
+ return {
28482
+ name: "",
28483
+ success: false,
28484
+ responses,
28485
+ scriptResults,
28486
+ assertionResults,
28487
+ steps: orderedSteps,
28488
+ errors,
28489
+ duration: Date.now() - startTime
28490
+ };
28491
+ }
28492
+ if (!(parsed.varName in runtimeVariables)) {
28493
+ errors.push(`Step ${stepIdx + 1}: Variable '${parsed.varName}' is not defined`);
28494
+ return {
28495
+ name: "",
28496
+ success: false,
28497
+ responses,
28498
+ scriptResults,
28499
+ assertionResults,
28500
+ steps: orderedSteps,
28501
+ errors,
28502
+ duration: Date.now() - startTime
28503
+ };
28504
+ }
28505
+ let jsonObj;
28506
+ try {
28507
+ jsonObj = JSON.parse(runtimeVariables[parsed.varName]);
28508
+ } catch {
28509
+ errors.push(`Step ${stepIdx + 1}: Variable '${parsed.varName}' is not a valid JSON object`);
28510
+ return {
28511
+ name: "",
28512
+ success: false,
28513
+ responses,
28514
+ scriptResults,
28515
+ assertionResults,
28516
+ steps: orderedSteps,
28517
+ errors,
28518
+ duration: Date.now() - startTime
28519
+ };
28520
+ }
28521
+ const valueTrimmed = parsed.value.trim();
28522
+ const bareVarMatch = valueTrimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)((?:\.[a-zA-Z_][a-zA-Z0-9_]*|\[\d+\])*)$/);
28523
+ let newValue;
28524
+ if (bareVarMatch && bareVarMatch[1] in runtimeVariables) {
28525
+ const varName = bareVarMatch[1];
28526
+ const pathPart = bareVarMatch[2] || "";
28527
+ let value = runtimeVariables[varName];
28528
+ if (typeof value === "string") {
28529
+ try {
28530
+ const parsed2 = JSON.parse(value);
28531
+ if (typeof parsed2 === "object" && parsed2 !== null) {
28532
+ value = parsed2;
28533
+ }
28534
+ } catch {
28601
28535
  }
28602
28536
  }
28603
- }
28604
- requestDescription = `${potentialEndpointName}(${paramTokens.join(", ")}) \u2192 ${parsed.method} ${resolvedUrl}`;
28605
- } else {
28606
- resolvedUrl = substituteVariables(parsed.url, runtimeVariables);
28607
- requestDescription = `${parsed.method} ${resolvedUrl}`;
28608
- }
28609
- let requestText = `${parsed.method} ${resolvedUrl}`;
28610
- if (Object.keys(resolvedHeaders).length > 0) {
28611
- const headerLines = Object.entries(resolvedHeaders).map(([k, v]) => `${k}: ${v}`);
28612
- requestText += "\n" + headerLines.join("\n");
28613
- }
28614
- const contentLines = step.content.split("\n");
28615
- if (contentLines.length > 1) {
28616
- const extraLines = contentLines.slice(1).map((line2) => substituteVariables(line2, runtimeVariables));
28617
- const hasExplicitBlank = extraLines.some((line2) => line2.trim() === "");
28618
- const firstNonEmpty = extraLines.find((line2) => line2.trim() !== "");
28619
- const startsWithHeader = firstNonEmpty ? /^[A-Za-z0-9\-_]+\s*:/.test(firstNonEmpty.trim()) : false;
28620
- if (hasExplicitBlank || startsWithHeader) {
28621
- requestText += "\n" + extraLines.join("\n");
28537
+ if (pathPart) {
28538
+ const path10 = pathPart.replace(/^\./, "");
28539
+ newValue = getNestedValueFromObject(value, path10);
28540
+ } else {
28541
+ newValue = value;
28542
+ }
28622
28543
  } else {
28623
- requestText += "\n\n" + extraLines.join("\n");
28544
+ const bareResolvedValue = resolveBareVariables(parsed.value, runtimeVariables);
28545
+ const resolvedValue = substituteVariables(bareResolvedValue, runtimeVariables);
28546
+ newValue = resolvedValue;
28547
+ try {
28548
+ newValue = JSON.parse(resolvedValue);
28549
+ } catch {
28550
+ }
28551
+ }
28552
+ const success = setNestedValue(jsonObj, parsed.propertyPath, newValue);
28553
+ if (!success) {
28554
+ errors.push(`Step ${stepIdx + 1}: Failed to set property '${parsed.propertyPath}' on '${parsed.varName}'`);
28555
+ return {
28556
+ name: "",
28557
+ success: false,
28558
+ responses,
28559
+ scriptResults,
28560
+ assertionResults,
28561
+ steps: orderedSteps,
28562
+ errors,
28563
+ duration: Date.now() - startTime
28564
+ };
28624
28565
  }
28566
+ runtimeVariables[parsed.varName] = JSON.stringify(jsonObj);
28567
+ reportProgress(stepIdx, "propAssign", `${parsed.varName}.${parsed.propertyPath} = ${parsed.value}`, void 0);
28568
+ continue;
28625
28569
  }
28626
- const requestParsed = parserHttpRequest(requestText, runtimeVariables);
28627
- validatePreparedRequest(
28628
- requestParsed,
28629
- buildRequestValidationContext(stepIdx + 1, requestParsed.method, requestParsed.url, `var ${parsed.varName}`)
28630
- );
28631
- const retryOpts = parsed.retryCount ? {
28632
- retryCount: parsed.retryCount,
28633
- backoffMs: parsed.backoffMs || 1e3,
28634
- onRetry: (attempt, total, error, waitMs) => {
28635
- reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] ${requestDescription} - waiting ${waitMs}ms`, void 0);
28570
+ if (step.type === "varAssign") {
28571
+ const parsed = parseVarAssignCommand(step.content);
28572
+ if (!parsed) {
28573
+ errors.push(`Step ${stepIdx + 1}: Invalid variable assignment: ${step.content}`);
28574
+ return {
28575
+ name: "",
28576
+ success: false,
28577
+ responses,
28578
+ scriptResults,
28579
+ assertionResults,
28580
+ steps: orderedSteps,
28581
+ errors,
28582
+ duration: Date.now() - startTime
28583
+ };
28636
28584
  }
28637
- } : void 0;
28638
- const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts) : await sendRequest(requestParsed, retryOpts);
28639
- addResponse(response);
28640
- responseIndexToVariable.set(responses.length, parsed.varName);
28641
- runtimeVariables[parsed.varName] = response;
28642
- const stepResult = {
28643
- type: "request",
28644
- stepIndex: stepIdx,
28645
- response,
28646
- requestMethod: parsed.method,
28647
- requestUrl: resolvedUrl,
28648
- variableName: parsed.varName
28649
- };
28650
- orderedSteps.push(stepResult);
28651
- reportProgress(stepIdx, "request", `var ${parsed.varName} = ${requestDescription}`, stepResult);
28652
- const relevantCaptures = captures.filter((c) => c.afterRequest === requestIndex);
28653
- for (const capture of relevantCaptures) {
28654
- const value = getValueByPath(response, capture.path);
28655
- if (value !== void 0) {
28656
- runtimeVariables[capture.varName] = value;
28657
- } else {
28658
- errors.push(`Warning: Could not capture ${capture.varName} from $${requestIndex}.${capture.path}`);
28585
+ const result = evaluateValueExpression(parsed.valueExpr, runtimeVariables);
28586
+ if (result.error) {
28587
+ errors.push(`Step ${stepIdx + 1}: ${result.error}`);
28588
+ return {
28589
+ name: "",
28590
+ success: false,
28591
+ responses,
28592
+ scriptResults,
28593
+ assertionResults,
28594
+ steps: orderedSteps,
28595
+ errors,
28596
+ duration: Date.now() - startTime
28597
+ };
28659
28598
  }
28599
+ runtimeVariables[parsed.varName] = result.value;
28600
+ reportProgress(stepIdx, "propAssign", `var ${parsed.varName} = ${result.value}`, void 0);
28601
+ continue;
28660
28602
  }
28661
- } catch (error) {
28662
- const userMessage = formatUserFacingError(
28663
- error,
28664
- buildRequestValidationContext(stepIdx + 1, void 0, void 0, `var ${parsed.varName}`)
28665
- );
28666
- let errorDetails = `Request failed for var ${parsed.varName}:
28603
+ if (step.type === "varRequest") {
28604
+ const commandLine = step.content.split("\n")[0];
28605
+ const parsed = parseVarRequestCommand(commandLine);
28606
+ if (!parsed) {
28607
+ errors.push(`Step ${stepIdx + 1}: Invalid variable request: ${commandLine}`);
28608
+ return {
28609
+ name: "",
28610
+ success: false,
28611
+ responses,
28612
+ scriptResults,
28613
+ assertionResults,
28614
+ steps: orderedSteps,
28615
+ errors,
28616
+ duration: Date.now() - startTime
28617
+ };
28618
+ }
28619
+ requestIndex++;
28620
+ let resolvedUrl = "";
28621
+ let resolvedHeaders = {};
28622
+ let requestDescription = `${parsed.method} ${parsed.url}`;
28623
+ try {
28624
+ const endpointMatch = parsed.url.match(/^([a-zA-Z_][a-zA-Z0-9_]*)(?:\(([^)]*)\))?(\s+.*)?$/);
28625
+ const potentialEndpointName = endpointMatch ? endpointMatch[1] : null;
28626
+ const endpoint = apiDefinitions && potentialEndpointName ? getEndpoint({ headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints }, potentialEndpointName) : void 0;
28627
+ if (endpoint && apiDefinitions) {
28628
+ const paramsStr = endpointMatch[2] || "";
28629
+ const headerGroupsStr = endpointMatch[3]?.trim() || "";
28630
+ const paramTokens = paramsStr ? paramsStr.split(",").map((p) => p.trim()) : [];
28631
+ const params = {};
28632
+ const isNamedSyntax = paramTokens.length > 0 && paramTokens[0].includes(":");
28633
+ if (isNamedSyntax) {
28634
+ for (const token of paramTokens) {
28635
+ const kvMatch = token.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.+)$/);
28636
+ if (kvMatch) {
28637
+ const paramName = kvMatch[1];
28638
+ let paramValue = kvMatch[2].trim().replace(/^["']|["']$/g, "");
28639
+ const bareResolved = resolveBareVariables(paramValue, runtimeVariables);
28640
+ params[paramName] = substituteVariables(bareResolved, runtimeVariables);
28641
+ }
28642
+ }
28643
+ } else {
28644
+ endpoint.parameters.forEach((paramName, idx) => {
28645
+ if (paramTokens[idx] !== void 0) {
28646
+ let paramValue = paramTokens[idx].replace(/^["']|["']$/g, "");
28647
+ const bareResolved = resolveBareVariables(paramValue, runtimeVariables);
28648
+ params[paramName] = substituteVariables(bareResolved, runtimeVariables);
28649
+ }
28650
+ });
28651
+ }
28652
+ let pathWithVars = substituteVariables(endpoint.path, runtimeVariables);
28653
+ for (const paramName of endpoint.parameters) {
28654
+ const value = params[paramName];
28655
+ if (value !== void 0) {
28656
+ pathWithVars = pathWithVars.replace(`{${paramName}}`, value);
28657
+ }
28658
+ }
28659
+ resolvedUrl = pathWithVars;
28660
+ if (headerGroupsStr) {
28661
+ const headerGroupNames = headerGroupsStr.split(/\s+/).filter((n) => n);
28662
+ for (const groupName of headerGroupNames) {
28663
+ const group = getHeaderGroup(
28664
+ { headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints },
28665
+ groupName
28666
+ );
28667
+ if (group) {
28668
+ const groupHeaders = resolveHeaderValues(group, runtimeVariables);
28669
+ Object.assign(resolvedHeaders, groupHeaders);
28670
+ }
28671
+ }
28672
+ }
28673
+ requestDescription = `${potentialEndpointName}(${paramTokens.join(", ")}) \u2192 ${parsed.method} ${resolvedUrl}`;
28674
+ } else {
28675
+ resolvedUrl = substituteVariables(parsed.url, runtimeVariables);
28676
+ requestDescription = `${parsed.method} ${resolvedUrl}`;
28677
+ }
28678
+ let requestText = `${parsed.method} ${resolvedUrl}`;
28679
+ if (Object.keys(resolvedHeaders).length > 0) {
28680
+ const headerLines = Object.entries(resolvedHeaders).map(([k, v]) => `${k}: ${v}`);
28681
+ requestText += "\n" + headerLines.join("\n");
28682
+ }
28683
+ const contentLines = step.content.split("\n");
28684
+ if (contentLines.length > 1) {
28685
+ const extraLines = contentLines.slice(1).map((line2) => substituteVariables(line2, runtimeVariables));
28686
+ const hasExplicitBlank = extraLines.some((line2) => line2.trim() === "");
28687
+ const firstNonEmpty = extraLines.find((line2) => line2.trim() !== "");
28688
+ const startsWithHeader = firstNonEmpty ? /^[A-Za-z0-9\-_]+\s*:/.test(firstNonEmpty.trim()) : false;
28689
+ if (hasExplicitBlank || startsWithHeader) {
28690
+ requestText += "\n" + extraLines.join("\n");
28691
+ } else {
28692
+ requestText += "\n\n" + extraLines.join("\n");
28693
+ }
28694
+ }
28695
+ const requestParsed = parserHttpRequest(requestText, runtimeVariables);
28696
+ validatePreparedRequest(
28697
+ requestParsed,
28698
+ buildRequestValidationContext(stepIdx + 1, requestParsed.method, requestParsed.url, `var ${parsed.varName}`)
28699
+ );
28700
+ const retryOpts = parsed.retryCount ? {
28701
+ retryCount: parsed.retryCount,
28702
+ backoffMs: parsed.backoffMs || 1e3,
28703
+ onRetry: (attempt, total, error, waitMs) => {
28704
+ reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] ${requestDescription} - waiting ${waitMs}ms`, void 0);
28705
+ }
28706
+ } : void 0;
28707
+ const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts) : await sendRequest(requestParsed, retryOpts);
28708
+ addResponse(response);
28709
+ responseIndexToVariable.set(responses.length, parsed.varName);
28710
+ runtimeVariables[parsed.varName] = response;
28711
+ const stepResult = {
28712
+ type: "request",
28713
+ stepIndex: stepIdx,
28714
+ response,
28715
+ requestMethod: parsed.method,
28716
+ requestUrl: resolvedUrl,
28717
+ variableName: parsed.varName
28718
+ };
28719
+ orderedSteps.push(stepResult);
28720
+ reportProgress(stepIdx, "request", `var ${parsed.varName} = ${requestDescription}`, stepResult);
28721
+ const relevantCaptures = captures.filter((c) => c.afterRequest === requestIndex);
28722
+ for (const capture of relevantCaptures) {
28723
+ const value = getValueByPath(response, capture.path);
28724
+ if (value !== void 0) {
28725
+ runtimeVariables[capture.varName] = value;
28726
+ } else {
28727
+ errors.push(`Warning: Could not capture ${capture.varName} from $${requestIndex}.${capture.path}`);
28728
+ }
28729
+ }
28730
+ } catch (error) {
28731
+ const userMessage = formatUserFacingError(
28732
+ error,
28733
+ buildRequestValidationContext(stepIdx + 1, void 0, void 0, `var ${parsed.varName}`)
28734
+ );
28735
+ let errorDetails = `Request failed for var ${parsed.varName}:
28667
28736
  ${indentMultiline(userMessage)}`;
28668
- if (requestDescription && !/\nRequest:\s/.test(userMessage)) {
28669
- errorDetails += `
28737
+ if (requestDescription && !/\nRequest:\s/.test(userMessage)) {
28738
+ errorDetails += `
28670
28739
  Request: ${requestDescription}`;
28740
+ }
28741
+ errors.push(errorDetails);
28742
+ await emitFailure(errorDetails, step, stepIdx);
28743
+ return {
28744
+ name: "",
28745
+ success: false,
28746
+ responses,
28747
+ scriptResults,
28748
+ assertionResults,
28749
+ steps: orderedSteps,
28750
+ errors,
28751
+ duration: Date.now() - startTime
28752
+ };
28753
+ }
28754
+ continue;
28671
28755
  }
28672
- errors.push(errorDetails);
28673
- return {
28674
- name: "",
28675
- success: false,
28676
- responses,
28677
- scriptResults,
28678
- assertionResults,
28679
- steps: orderedSteps,
28680
- errors,
28681
- duration: Date.now() - startTime
28682
- };
28683
- }
28684
- continue;
28685
- }
28686
- if (step.type === "script") {
28687
- const parsed = parseRunCommand(substituteVariables(step.content, runtimeVariables));
28688
- if (!parsed) {
28689
- errors.push(`Step ${stepIdx + 1}: Invalid run command: ${step.content}`);
28690
- return {
28691
- name: "",
28692
- success: false,
28693
- responses,
28694
- scriptResults,
28695
- assertionResults,
28696
- steps: orderedSteps,
28697
- errors,
28698
- duration: Date.now() - startTime
28699
- };
28700
- }
28701
- const substitutedArgs = parsed.args.map((arg) => {
28702
- const bareResolved = resolveBareVariables(arg, runtimeVariables);
28703
- return substituteVariables(bareResolved, runtimeVariables);
28704
- });
28705
- const substitutedPath = substituteVariables(parsed.scriptPath, runtimeVariables);
28706
- try {
28707
- const result = await runScript(
28708
- parsed.type,
28709
- substitutedPath,
28710
- substitutedArgs,
28711
- workingDir || process.cwd(),
28712
- runtimeVariables,
28713
- parsed.captureAs
28714
- );
28715
- scriptResults.push(result);
28716
- const stepResult = {
28717
- type: "script",
28718
- stepIndex: stepIdx,
28719
- script: result
28720
- };
28721
- orderedSteps.push(stepResult);
28722
- reportProgress(stepIdx, "script", `run ${parsed.type} ${substitutedPath}`, stepResult);
28723
- if (!result.success) {
28724
- errors.push(`Script failed (exit ${result.exitCode}): ${result.error || result.output}`);
28725
- return {
28726
- name: "",
28727
- success: false,
28728
- responses,
28729
- scriptResults,
28730
- assertionResults,
28731
- steps: orderedSteps,
28732
- errors,
28733
- duration: Date.now() - startTime
28734
- };
28735
- }
28736
- if (parsed.captureAs) {
28737
- let outputValue = result.output;
28756
+ if (step.type === "script") {
28757
+ const parsed = parseRunCommand(substituteVariables(step.content, runtimeVariables));
28758
+ if (!parsed) {
28759
+ errors.push(`Step ${stepIdx + 1}: Invalid run command: ${step.content}`);
28760
+ return {
28761
+ name: "",
28762
+ success: false,
28763
+ responses,
28764
+ scriptResults,
28765
+ assertionResults,
28766
+ steps: orderedSteps,
28767
+ errors,
28768
+ duration: Date.now() - startTime
28769
+ };
28770
+ }
28771
+ const substitutedArgs = parsed.args.map((arg) => {
28772
+ const bareResolved = resolveBareVariables(arg, runtimeVariables);
28773
+ return substituteVariables(bareResolved, runtimeVariables);
28774
+ });
28775
+ const substitutedPath = substituteVariables(parsed.scriptPath, runtimeVariables);
28738
28776
  try {
28739
- const jsonParsed = JSON.parse(result.output);
28740
- outputValue = jsonParsed;
28741
- } catch {
28777
+ const result = await runScript(
28778
+ parsed.type,
28779
+ substitutedPath,
28780
+ substitutedArgs,
28781
+ workingDir || process.cwd(),
28782
+ runtimeVariables,
28783
+ parsed.captureAs
28784
+ );
28785
+ scriptResults.push(result);
28786
+ const stepResult = {
28787
+ type: "script",
28788
+ stepIndex: stepIdx,
28789
+ script: result
28790
+ };
28791
+ orderedSteps.push(stepResult);
28792
+ reportProgress(stepIdx, "script", `run ${parsed.type} ${substitutedPath}`, stepResult);
28793
+ if (!result.success) {
28794
+ errors.push(`Script failed (exit ${result.exitCode}): ${result.error || result.output}`);
28795
+ return {
28796
+ name: "",
28797
+ success: false,
28798
+ responses,
28799
+ scriptResults,
28800
+ assertionResults,
28801
+ steps: orderedSteps,
28802
+ errors,
28803
+ duration: Date.now() - startTime
28804
+ };
28805
+ }
28806
+ if (parsed.captureAs) {
28807
+ let outputValue = result.output;
28808
+ try {
28809
+ const jsonParsed = JSON.parse(result.output);
28810
+ outputValue = jsonParsed;
28811
+ } catch {
28812
+ }
28813
+ runtimeVariables[parsed.captureAs] = outputValue;
28814
+ }
28815
+ } catch (error) {
28816
+ errors.push(`Script execution error: ${error.message}`);
28817
+ return {
28818
+ name: "",
28819
+ success: false,
28820
+ responses,
28821
+ scriptResults,
28822
+ assertionResults,
28823
+ steps: orderedSteps,
28824
+ errors,
28825
+ duration: Date.now() - startTime
28826
+ };
28742
28827
  }
28743
- runtimeVariables[parsed.captureAs] = outputValue;
28744
- }
28745
- } catch (error) {
28746
- errors.push(`Script execution error: ${error.message}`);
28747
- return {
28748
- name: "",
28749
- success: false,
28750
- responses,
28751
- scriptResults,
28752
- assertionResults,
28753
- steps: orderedSteps,
28754
- errors,
28755
- duration: Date.now() - startTime
28756
- };
28757
- }
28758
- } else if (step.type === "namedRequest") {
28759
- const parsed = parseRunNamedRequestCommand(step.content);
28760
- if (!parsed) {
28761
- errors.push(`Invalid run command: ${step.content}`);
28762
- return {
28763
- name: "",
28764
- success: false,
28765
- responses,
28766
- scriptResults,
28767
- assertionResults,
28768
- steps: orderedSteps,
28769
- errors,
28770
- duration: Date.now() - startTime
28771
- };
28772
- }
28773
- const { name: targetName, args: runArgs } = parsed;
28774
- if (!fullDocumentText) {
28775
- errors.push(`Cannot run "${targetName}": full document text not provided`);
28776
- return {
28777
- name: "",
28778
- success: false,
28779
- responses,
28780
- scriptResults,
28781
- assertionResults,
28782
- steps: orderedSteps,
28783
- errors,
28784
- duration: Date.now() - startTime
28785
- };
28786
- }
28787
- const allSequences = extractSequences(fullDocumentText);
28788
- const targetSequence = allSequences.find((s) => s.name === targetName);
28789
- if (targetSequence) {
28790
- const currentCallStack = callStack || [];
28791
- if (currentCallStack.includes(targetName)) {
28792
- errors.push(`Circular reference detected: ${[...currentCallStack, targetName].join(" \u2192 ")}`);
28793
- return {
28794
- name: "",
28795
- success: false,
28796
- responses,
28797
- scriptResults,
28798
- assertionResults,
28799
- steps: orderedSteps,
28800
- errors,
28801
- duration: Date.now() - startTime
28802
- };
28803
- }
28804
- let sequenceArgs2 = {};
28805
- if (targetSequence.parameters.length > 0 || runArgs.length > 0) {
28806
- const bindResult = bindSequenceArguments(targetSequence.parameters, runArgs, runtimeVariables);
28807
- if ("error" in bindResult) {
28808
- errors.push(`Error calling sequence "${targetName}": ${bindResult.error}`);
28828
+ } else if (step.type === "namedRequest") {
28829
+ const parsed = parseRunNamedRequestCommand(step.content);
28830
+ if (!parsed) {
28831
+ errors.push(`Invalid run command: ${step.content}`);
28832
+ return {
28833
+ name: "",
28834
+ success: false,
28835
+ responses,
28836
+ scriptResults,
28837
+ assertionResults,
28838
+ steps: orderedSteps,
28839
+ errors,
28840
+ duration: Date.now() - startTime
28841
+ };
28842
+ }
28843
+ const { name: targetName, args: runArgs } = parsed;
28844
+ if (!fullDocumentText) {
28845
+ errors.push(`Cannot run "${targetName}": full document text not provided`);
28846
+ return {
28847
+ name: "",
28848
+ success: false,
28849
+ responses,
28850
+ scriptResults,
28851
+ assertionResults,
28852
+ steps: orderedSteps,
28853
+ errors,
28854
+ duration: Date.now() - startTime
28855
+ };
28856
+ }
28857
+ const allSequences = extractSequences(fullDocumentText);
28858
+ const targetSequence = allSequences.find((s) => s.name === targetName);
28859
+ if (targetSequence) {
28860
+ const currentCallStack = callStack || [];
28861
+ if (currentCallStack.includes(targetName)) {
28862
+ errors.push(`Circular reference detected: ${[...currentCallStack, targetName].join(" \u2192 ")}`);
28863
+ return {
28864
+ name: "",
28865
+ success: false,
28866
+ responses,
28867
+ scriptResults,
28868
+ assertionResults,
28869
+ steps: orderedSteps,
28870
+ errors,
28871
+ duration: Date.now() - startTime
28872
+ };
28873
+ }
28874
+ let sequenceArgs2 = {};
28875
+ if (targetSequence.parameters.length > 0 || runArgs.length > 0) {
28876
+ const bindResult = bindSequenceArguments(targetSequence.parameters, runArgs, runtimeVariables);
28877
+ if ("error" in bindResult) {
28878
+ errors.push(`Error calling sequence "${targetName}": ${bindResult.error}`);
28879
+ return {
28880
+ name: "",
28881
+ success: false,
28882
+ responses,
28883
+ scriptResults,
28884
+ assertionResults,
28885
+ steps: orderedSteps,
28886
+ errors,
28887
+ duration: Date.now() - startTime
28888
+ };
28889
+ }
28890
+ sequenceArgs2 = bindResult.variables;
28891
+ }
28892
+ if (tagFilterOptions && tagFilterOptions.filters.length > 0) {
28893
+ if (!sequenceMatchesTags(targetSequence, tagFilterOptions)) {
28894
+ continue;
28895
+ }
28896
+ }
28897
+ reportProgress(stepIdx, "sequenceStart", `Starting sequence ${targetName}`, void 0, targetName);
28898
+ let subWorkingDir = workingDir;
28899
+ if (sequenceSources) {
28900
+ const sourceFile = sequenceSources.get(targetName.toLowerCase());
28901
+ if (sourceFile) {
28902
+ const path10 = await import("path");
28903
+ subWorkingDir = path10.dirname(sourceFile);
28904
+ }
28905
+ }
28906
+ const targetLocation = executionContext?.sequenceLocationIndex?.get(targetName.toLowerCase());
28907
+ const childExecutionContext = {
28908
+ filePath: targetLocation?.filePath ?? executionContext?.filePath,
28909
+ sequenceName: targetName,
28910
+ environment: executionContext?.environment,
28911
+ sequenceStartLine: targetLocation?.startLine ?? targetSequence.startLine,
28912
+ sequenceLocationIndex: executionContext?.sequenceLocationIndex
28913
+ };
28914
+ const subResult = await runSequenceWithJar(
28915
+ targetSequence.content,
28916
+ runtimeVariables,
28917
+ // Pass current runtime variables (includes file vars + any captured)
28918
+ cookieJar,
28919
+ subWorkingDir,
28920
+ fullDocumentText,
28921
+ onProgress,
28922
+ // Pass through progress callback
28923
+ [...currentCallStack, targetName],
28924
+ // Add to call stack for circular detection
28925
+ sequenceArgs2,
28926
+ // Pass bound arguments as initial scope variables
28927
+ apiDefinitions,
28928
+ // Pass API definitions for endpoint resolution
28929
+ tagFilterOptions,
28930
+ // Pass tag filter options for nested sequence filtering
28931
+ sequenceSources,
28932
+ // Pass sequence sources for nested sequences
28933
+ childExecutionContext,
28934
+ debugHooks
28935
+ );
28936
+ reportProgress(stepIdx, "sequenceEnd", `Finished sequence ${targetName}`, void 0, targetName);
28937
+ for (const subResponse of subResult.responses) {
28938
+ addResponse(subResponse);
28939
+ }
28940
+ scriptResults.push(...subResult.scriptResults);
28941
+ assertionResults.push(...subResult.assertionResults);
28942
+ for (const subStep of subResult.steps) {
28943
+ orderedSteps.push({
28944
+ ...subStep,
28945
+ stepIndex: orderedSteps.length
28946
+ });
28947
+ }
28948
+ requestIndex += subResult.responses.length;
28949
+ if (!subResult.success) {
28950
+ errors.push(`Sequence "${targetName}" failed`);
28951
+ errors.push(...subResult.errors);
28952
+ return {
28953
+ name: "",
28954
+ success: false,
28955
+ responses,
28956
+ scriptResults,
28957
+ assertionResults,
28958
+ steps: orderedSteps,
28959
+ errors,
28960
+ duration: Date.now() - startTime,
28961
+ variables: runtimeVariables
28962
+ };
28963
+ }
28964
+ continue;
28965
+ }
28966
+ const namedRequest = getNamedRequest(fullDocumentText, targetName);
28967
+ if (!namedRequest) {
28968
+ errors.push(`"${targetName}" is not a named request or sequence`);
28969
+ return {
28970
+ name: "",
28971
+ success: false,
28972
+ responses,
28973
+ scriptResults,
28974
+ assertionResults,
28975
+ steps: orderedSteps,
28976
+ errors,
28977
+ duration: Date.now() - startTime
28978
+ };
28979
+ }
28980
+ requestIndex++;
28981
+ let requestUrl = "";
28982
+ let requestMethod = "";
28983
+ try {
28984
+ let requestParsed;
28985
+ if (apiDefinitions && apiDefinitions.endpoints.length > 0 && isApiRequestLine(namedRequest.content, apiDefinitions.endpoints)) {
28986
+ const apiRequest = parseApiRequest(
28987
+ namedRequest.content,
28988
+ apiDefinitions.endpoints,
28989
+ apiDefinitions.headerGroups
28990
+ );
28991
+ if (apiRequest) {
28992
+ const endpoint = getEndpoint(
28993
+ { headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints },
28994
+ apiRequest.endpointName
28995
+ );
28996
+ if (endpoint) {
28997
+ let resolvedPath = endpoint.path;
28998
+ resolvedPath = resolvedPath.replace(/\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g, (_, varName) => {
28999
+ return runtimeVariables[varName] !== void 0 ? String(runtimeVariables[varName]) : `{{${varName}}}`;
29000
+ });
29001
+ for (const [paramName, paramValue] of Object.entries(apiRequest.params)) {
29002
+ let resolvedValue = paramValue;
29003
+ if (runtimeVariables[paramValue] !== void 0) {
29004
+ resolvedValue = String(runtimeVariables[paramValue]);
29005
+ } else if (paramValue.startsWith("{{") && paramValue.endsWith("}}")) {
29006
+ const varName = paramValue.slice(2, -2);
29007
+ if (runtimeVariables[varName] !== void 0) {
29008
+ resolvedValue = String(runtimeVariables[varName]);
29009
+ }
29010
+ }
29011
+ resolvedPath = resolvedPath.replace(`{${paramName}}`, resolvedValue);
29012
+ }
29013
+ const combinedHeaders = {};
29014
+ for (const groupName of apiRequest.headerGroupNames) {
29015
+ const group = getHeaderGroup(apiDefinitions, groupName);
29016
+ if (group) {
29017
+ const resolvedHeaders = resolveHeaderValues(group, runtimeVariables);
29018
+ Object.assign(combinedHeaders, resolvedHeaders);
29019
+ }
29020
+ }
29021
+ for (const [headerName, headerValue] of Object.entries(apiRequest.inlineHeaders)) {
29022
+ let resolved = headerValue;
29023
+ resolved = resolved.replace(/\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g, (_, varName) => {
29024
+ return runtimeVariables[varName] !== void 0 ? String(runtimeVariables[varName]) : `{{${varName}}}`;
29025
+ });
29026
+ combinedHeaders[headerName] = resolved;
29027
+ }
29028
+ const bodyParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
29029
+ requestParsed = {
29030
+ method: apiRequest.method,
29031
+ url: resolvedPath,
29032
+ headers: combinedHeaders,
29033
+ body: bodyParsed.body
29034
+ };
29035
+ } else {
29036
+ throw new Error(`Unknown endpoint: ${apiRequest.endpointName}`);
29037
+ }
29038
+ } else {
29039
+ requestParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
29040
+ }
29041
+ } else {
29042
+ requestParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
29043
+ if (apiDefinitions && apiDefinitions.headerGroups.length > 0) {
29044
+ requestParsed = applyHeaderGroupsToRequest(requestParsed, namedRequest.content, apiDefinitions.headerGroups, runtimeVariables);
29045
+ }
29046
+ }
29047
+ requestUrl = requestParsed.url;
29048
+ requestMethod = requestParsed.method;
29049
+ validatePreparedRequest(
29050
+ requestParsed,
29051
+ buildRequestValidationContext(stepIdx + 1, requestMethod, requestUrl, targetName)
29052
+ );
29053
+ const effectiveRetryCount = parsed.retryCount ?? requestParsed.retryCount;
29054
+ const effectiveBackoffMs = parsed.backoffMs ?? requestParsed.backoffMs ?? 1e3;
29055
+ const retryOpts = effectiveRetryCount ? {
29056
+ retryCount: effectiveRetryCount,
29057
+ backoffMs: effectiveBackoffMs,
29058
+ onRetry: (attempt, total, error, waitMs) => {
29059
+ reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] run ${targetName} - waiting ${waitMs}ms`, void 0);
29060
+ }
29061
+ } : void 0;
29062
+ const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts) : await sendRequest(requestParsed, retryOpts);
29063
+ addResponse(response);
29064
+ const stepResult = {
29065
+ type: "request",
29066
+ stepIndex: stepIdx,
29067
+ response,
29068
+ requestMethod: requestParsed.method,
29069
+ requestUrl: requestParsed.url
29070
+ };
29071
+ orderedSteps.push(stepResult);
29072
+ reportProgress(stepIdx, "namedRequest", `run ${targetName} \u2192 ${requestParsed.method} ${requestParsed.url}`, stepResult);
29073
+ const relevantCaptures = captures.filter((c) => c.afterRequest === requestIndex);
29074
+ for (const capture of relevantCaptures) {
29075
+ const value = getValueByPath(response, capture.path);
29076
+ if (value !== void 0) {
29077
+ runtimeVariables[capture.varName] = value;
29078
+ } else {
29079
+ errors.push(`Warning: Could not capture ${capture.varName} from $${requestIndex}.${capture.path}`);
29080
+ }
29081
+ }
29082
+ } catch (error) {
29083
+ const userMessage = formatUserFacingError(
29084
+ error,
29085
+ buildRequestValidationContext(stepIdx + 1, requestMethod || void 0, requestUrl || void 0, targetName)
29086
+ );
29087
+ let errorDetails = `Named request "${targetName}" failed:
29088
+ ${indentMultiline(userMessage)}`;
29089
+ if (requestUrl && !/\nRequest:\s/.test(userMessage)) {
29090
+ errorDetails += `
29091
+ Request: ${requestMethod} ${requestUrl}`;
29092
+ }
29093
+ errors.push(errorDetails);
29094
+ await emitFailure(errorDetails, step, stepIdx);
28809
29095
  return {
28810
29096
  name: "",
28811
29097
  success: false,
@@ -28817,600 +29103,420 @@ ${indentMultiline(userMessage)}`;
28817
29103
  duration: Date.now() - startTime
28818
29104
  };
28819
29105
  }
28820
- sequenceArgs2 = bindResult.variables;
28821
- }
28822
- if (tagFilterOptions && tagFilterOptions.filters.length > 0) {
28823
- if (!sequenceMatchesTags(targetSequence, tagFilterOptions)) {
28824
- continue;
29106
+ } else if (step.type === "varRunSequence") {
29107
+ const parsed = parseVarRunSequenceCommand(step.content);
29108
+ if (!parsed) {
29109
+ errors.push(`Invalid var run sequence command: ${step.content}`);
29110
+ return {
29111
+ name: "",
29112
+ success: false,
29113
+ responses,
29114
+ scriptResults,
29115
+ assertionResults,
29116
+ steps: orderedSteps,
29117
+ errors,
29118
+ duration: Date.now() - startTime
29119
+ };
28825
29120
  }
28826
- }
28827
- reportProgress(stepIdx, "sequenceStart", `Starting sequence ${targetName}`, void 0, targetName);
28828
- let subWorkingDir = workingDir;
28829
- if (sequenceSources) {
28830
- const sourceFile = sequenceSources.get(targetName.toLowerCase());
28831
- if (sourceFile) {
28832
- const path10 = await import("path");
28833
- subWorkingDir = path10.dirname(sourceFile);
29121
+ const { varName, sequenceName, args: runArgs } = parsed;
29122
+ if (!fullDocumentText) {
29123
+ errors.push(`Cannot run sequence "${sequenceName}": full document text not provided`);
29124
+ return {
29125
+ name: "",
29126
+ success: false,
29127
+ responses,
29128
+ scriptResults,
29129
+ assertionResults,
29130
+ steps: orderedSteps,
29131
+ errors,
29132
+ duration: Date.now() - startTime
29133
+ };
28834
29134
  }
28835
- }
28836
- const subResult = await runSequenceWithJar(
28837
- targetSequence.content,
28838
- runtimeVariables,
28839
- // Pass current runtime variables (includes file vars + any captured)
28840
- cookieJar,
28841
- subWorkingDir,
28842
- fullDocumentText,
28843
- onProgress,
28844
- // Pass through progress callback
28845
- [...currentCallStack, targetName],
28846
- // Add to call stack for circular detection
28847
- sequenceArgs2,
28848
- // Pass bound arguments as initial scope variables
28849
- apiDefinitions,
28850
- // Pass API definitions for endpoint resolution
28851
- tagFilterOptions,
28852
- // Pass tag filter options for nested sequence filtering
28853
- sequenceSources,
28854
- // Pass sequence sources for nested sequences
28855
- executionContext
28856
- );
28857
- reportProgress(stepIdx, "sequenceEnd", `Finished sequence ${targetName}`, void 0, targetName);
28858
- for (const subResponse of subResult.responses) {
28859
- addResponse(subResponse);
28860
- }
28861
- scriptResults.push(...subResult.scriptResults);
28862
- assertionResults.push(...subResult.assertionResults);
28863
- for (const subStep of subResult.steps) {
28864
- orderedSteps.push({
28865
- ...subStep,
28866
- stepIndex: orderedSteps.length
28867
- });
28868
- }
28869
- requestIndex += subResult.responses.length;
28870
- if (!subResult.success) {
28871
- errors.push(`Sequence "${targetName}" failed`);
28872
- errors.push(...subResult.errors);
28873
- return {
28874
- name: "",
28875
- success: false,
28876
- responses,
28877
- scriptResults,
28878
- assertionResults,
28879
- steps: orderedSteps,
28880
- errors,
28881
- duration: Date.now() - startTime,
28882
- variables: runtimeVariables
28883
- };
28884
- }
28885
- continue;
28886
- }
28887
- const namedRequest = getNamedRequest(fullDocumentText, targetName);
28888
- if (!namedRequest) {
28889
- errors.push(`"${targetName}" is not a named request or sequence`);
28890
- return {
28891
- name: "",
28892
- success: false,
28893
- responses,
28894
- scriptResults,
28895
- assertionResults,
28896
- steps: orderedSteps,
28897
- errors,
28898
- duration: Date.now() - startTime
28899
- };
28900
- }
28901
- requestIndex++;
28902
- let requestUrl = "";
28903
- let requestMethod = "";
28904
- try {
28905
- let requestParsed;
28906
- if (apiDefinitions && apiDefinitions.endpoints.length > 0 && isApiRequestLine(namedRequest.content, apiDefinitions.endpoints)) {
28907
- const apiRequest = parseApiRequest(
28908
- namedRequest.content,
28909
- apiDefinitions.endpoints,
28910
- apiDefinitions.headerGroups
28911
- );
28912
- if (apiRequest) {
28913
- const endpoint = getEndpoint(
28914
- { headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints },
28915
- apiRequest.endpointName
28916
- );
28917
- if (endpoint) {
28918
- let resolvedPath = endpoint.path;
28919
- resolvedPath = resolvedPath.replace(/\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g, (_, varName) => {
28920
- return runtimeVariables[varName] !== void 0 ? String(runtimeVariables[varName]) : `{{${varName}}}`;
28921
- });
28922
- for (const [paramName, paramValue] of Object.entries(apiRequest.params)) {
28923
- let resolvedValue = paramValue;
28924
- if (runtimeVariables[paramValue] !== void 0) {
28925
- resolvedValue = String(runtimeVariables[paramValue]);
28926
- } else if (paramValue.startsWith("{{") && paramValue.endsWith("}}")) {
28927
- const varName = paramValue.slice(2, -2);
28928
- if (runtimeVariables[varName] !== void 0) {
28929
- resolvedValue = String(runtimeVariables[varName]);
28930
- }
29135
+ const allSequences = extractSequences(fullDocumentText);
29136
+ const targetSequence = allSequences.find((s) => s.name === sequenceName);
29137
+ if (!targetSequence) {
29138
+ const namedRequest = getNamedRequest(fullDocumentText, sequenceName);
29139
+ if (!namedRequest) {
29140
+ errors.push(`"${sequenceName}" is not a named request or sequence`);
29141
+ return {
29142
+ name: "",
29143
+ success: false,
29144
+ responses,
29145
+ scriptResults,
29146
+ assertionResults,
29147
+ steps: orderedSteps,
29148
+ errors,
29149
+ duration: Date.now() - startTime
29150
+ };
29151
+ }
29152
+ requestIndex++;
29153
+ let requestUrl = "";
29154
+ let requestMethod = "";
29155
+ try {
29156
+ const requestParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
29157
+ requestUrl = requestParsed.url;
29158
+ requestMethod = requestParsed.method;
29159
+ validatePreparedRequest(
29160
+ requestParsed,
29161
+ buildRequestValidationContext(stepIdx + 1, requestMethod, requestUrl, `${varName} = run ${sequenceName}`)
29162
+ );
29163
+ const effectiveRetryCount = parsed.retryCount ?? requestParsed.retryCount;
29164
+ const effectiveBackoffMs = parsed.backoffMs ?? requestParsed.backoffMs ?? 1e3;
29165
+ const retryOpts = effectiveRetryCount ? {
29166
+ retryCount: effectiveRetryCount,
29167
+ backoffMs: effectiveBackoffMs,
29168
+ onRetry: (attempt, total, error, waitMs) => {
29169
+ reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] var ${varName} = run ${sequenceName} - waiting ${waitMs}ms`, void 0);
28931
29170
  }
28932
- resolvedPath = resolvedPath.replace(`{${paramName}}`, resolvedValue);
28933
- }
28934
- const combinedHeaders = {};
28935
- for (const groupName of apiRequest.headerGroupNames) {
28936
- const group = getHeaderGroup(apiDefinitions, groupName);
28937
- if (group) {
28938
- const resolvedHeaders = resolveHeaderValues(group, runtimeVariables);
28939
- Object.assign(combinedHeaders, resolvedHeaders);
29171
+ } : void 0;
29172
+ const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts) : await sendRequest(requestParsed, retryOpts);
29173
+ addResponse(response);
29174
+ responseIndexToVariable.set(responses.length, varName);
29175
+ const stepResult = {
29176
+ type: "request",
29177
+ stepIndex: stepIdx,
29178
+ response,
29179
+ requestMethod: requestParsed.method,
29180
+ requestUrl: requestParsed.url,
29181
+ variableName: varName
29182
+ };
29183
+ orderedSteps.push(stepResult);
29184
+ reportProgress(stepIdx, "namedRequest", `var ${varName} = run ${sequenceName} \u2192 ${requestParsed.method} ${requestParsed.url}`, stepResult);
29185
+ const capturedResponse = {
29186
+ status: response.status,
29187
+ statusText: response.statusText,
29188
+ body: response.body,
29189
+ headers: response.headers
29190
+ };
29191
+ runtimeVariables[varName] = JSON.stringify(capturedResponse);
29192
+ const relevantCaptures = captures.filter((c) => c.afterRequest === requestIndex);
29193
+ for (const capture of relevantCaptures) {
29194
+ const value = getValueByPath(response, capture.path);
29195
+ if (value !== void 0) {
29196
+ runtimeVariables[capture.varName] = value;
29197
+ } else {
29198
+ errors.push(`Warning: Could not capture ${capture.varName} from $${requestIndex}.${capture.path}`);
28940
29199
  }
28941
29200
  }
28942
- for (const [headerName, headerValue] of Object.entries(apiRequest.inlineHeaders)) {
28943
- let resolved = headerValue;
28944
- resolved = resolved.replace(/\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g, (_, varName) => {
28945
- return runtimeVariables[varName] !== void 0 ? String(runtimeVariables[varName]) : `{{${varName}}}`;
28946
- });
28947
- combinedHeaders[headerName] = resolved;
29201
+ } catch (error) {
29202
+ const userMessage = formatUserFacingError(
29203
+ error,
29204
+ buildRequestValidationContext(stepIdx + 1, requestMethod || void 0, requestUrl || void 0, `${varName} = run ${sequenceName}`)
29205
+ );
29206
+ let errorDetails = `Named request "${sequenceName}" failed:
29207
+ ${indentMultiline(userMessage)}`;
29208
+ if (requestUrl && !/\nRequest:\s/.test(userMessage)) {
29209
+ errorDetails += `
29210
+ Request: ${requestMethod} ${requestUrl}`;
28948
29211
  }
28949
- const bodyParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
28950
- requestParsed = {
28951
- method: apiRequest.method,
28952
- url: resolvedPath,
28953
- headers: combinedHeaders,
28954
- body: bodyParsed.body
29212
+ errors.push(errorDetails);
29213
+ await emitFailure(errorDetails, step, stepIdx);
29214
+ return {
29215
+ name: "",
29216
+ success: false,
29217
+ responses,
29218
+ scriptResults,
29219
+ assertionResults,
29220
+ steps: orderedSteps,
29221
+ errors,
29222
+ duration: Date.now() - startTime
28955
29223
  };
28956
- } else {
28957
- throw new Error(`Unknown endpoint: ${apiRequest.endpointName}`);
28958
29224
  }
28959
- } else {
28960
- requestParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
28961
- }
28962
- } else {
28963
- requestParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
28964
- if (apiDefinitions && apiDefinitions.headerGroups.length > 0) {
28965
- requestParsed = applyHeaderGroupsToRequest(requestParsed, namedRequest.content, apiDefinitions.headerGroups, runtimeVariables);
29225
+ continue;
28966
29226
  }
28967
- }
28968
- requestUrl = requestParsed.url;
28969
- requestMethod = requestParsed.method;
28970
- validatePreparedRequest(
28971
- requestParsed,
28972
- buildRequestValidationContext(stepIdx + 1, requestMethod, requestUrl, targetName)
28973
- );
28974
- const effectiveRetryCount = parsed.retryCount ?? requestParsed.retryCount;
28975
- const effectiveBackoffMs = parsed.backoffMs ?? requestParsed.backoffMs ?? 1e3;
28976
- const retryOpts = effectiveRetryCount ? {
28977
- retryCount: effectiveRetryCount,
28978
- backoffMs: effectiveBackoffMs,
28979
- onRetry: (attempt, total, error, waitMs) => {
28980
- reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] run ${targetName} - waiting ${waitMs}ms`, void 0);
29227
+ const currentCallStack = callStack || [];
29228
+ if (currentCallStack.includes(sequenceName)) {
29229
+ errors.push(`Circular reference detected: ${[...currentCallStack, sequenceName].join(" \u2192 ")}`);
29230
+ return {
29231
+ name: "",
29232
+ success: false,
29233
+ responses,
29234
+ scriptResults,
29235
+ assertionResults,
29236
+ steps: orderedSteps,
29237
+ errors,
29238
+ duration: Date.now() - startTime
29239
+ };
28981
29240
  }
28982
- } : void 0;
28983
- const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts) : await sendRequest(requestParsed, retryOpts);
28984
- addResponse(response);
28985
- const stepResult = {
28986
- type: "request",
28987
- stepIndex: stepIdx,
28988
- response,
28989
- requestMethod: requestParsed.method,
28990
- requestUrl: requestParsed.url
28991
- };
28992
- orderedSteps.push(stepResult);
28993
- reportProgress(stepIdx, "namedRequest", `run ${targetName} \u2192 ${requestParsed.method} ${requestParsed.url}`, stepResult);
28994
- const relevantCaptures = captures.filter((c) => c.afterRequest === requestIndex);
28995
- for (const capture of relevantCaptures) {
28996
- const value = getValueByPath(response, capture.path);
28997
- if (value !== void 0) {
28998
- runtimeVariables[capture.varName] = value;
28999
- } else {
29000
- errors.push(`Warning: Could not capture ${capture.varName} from $${requestIndex}.${capture.path}`);
29241
+ let sequenceArgs2 = {};
29242
+ if (targetSequence.parameters.length > 0 || runArgs.length > 0) {
29243
+ const bindResult = bindSequenceArguments(targetSequence.parameters, runArgs, runtimeVariables);
29244
+ if ("error" in bindResult) {
29245
+ errors.push(`Error calling sequence "${sequenceName}": ${bindResult.error}`);
29246
+ return {
29247
+ name: "",
29248
+ success: false,
29249
+ responses,
29250
+ scriptResults,
29251
+ assertionResults,
29252
+ steps: orderedSteps,
29253
+ errors,
29254
+ duration: Date.now() - startTime
29255
+ };
29256
+ }
29257
+ sequenceArgs2 = bindResult.variables;
29001
29258
  }
29002
- }
29003
- } catch (error) {
29004
- const userMessage = formatUserFacingError(
29005
- error,
29006
- buildRequestValidationContext(stepIdx + 1, requestMethod || void 0, requestUrl || void 0, targetName)
29007
- );
29008
- let errorDetails = `Named request "${targetName}" failed:
29009
- ${indentMultiline(userMessage)}`;
29010
- if (requestUrl && !/\nRequest:\s/.test(userMessage)) {
29011
- errorDetails += `
29012
- Request: ${requestMethod} ${requestUrl}`;
29013
- }
29014
- errors.push(errorDetails);
29015
- return {
29016
- name: "",
29017
- success: false,
29018
- responses,
29019
- scriptResults,
29020
- assertionResults,
29021
- steps: orderedSteps,
29022
- errors,
29023
- duration: Date.now() - startTime
29024
- };
29025
- }
29026
- } else if (step.type === "varRunSequence") {
29027
- const parsed = parseVarRunSequenceCommand(step.content);
29028
- if (!parsed) {
29029
- errors.push(`Invalid var run sequence command: ${step.content}`);
29030
- return {
29031
- name: "",
29032
- success: false,
29033
- responses,
29034
- scriptResults,
29035
- assertionResults,
29036
- steps: orderedSteps,
29037
- errors,
29038
- duration: Date.now() - startTime
29039
- };
29040
- }
29041
- const { varName, sequenceName, args: runArgs } = parsed;
29042
- if (!fullDocumentText) {
29043
- errors.push(`Cannot run sequence "${sequenceName}": full document text not provided`);
29044
- return {
29045
- name: "",
29046
- success: false,
29047
- responses,
29048
- scriptResults,
29049
- assertionResults,
29050
- steps: orderedSteps,
29051
- errors,
29052
- duration: Date.now() - startTime
29053
- };
29054
- }
29055
- const allSequences = extractSequences(fullDocumentText);
29056
- const targetSequence = allSequences.find((s) => s.name === sequenceName);
29057
- if (!targetSequence) {
29058
- const namedRequest = getNamedRequest(fullDocumentText, sequenceName);
29059
- if (!namedRequest) {
29060
- errors.push(`"${sequenceName}" is not a named request or sequence`);
29061
- return {
29062
- name: "",
29063
- success: false,
29064
- responses,
29065
- scriptResults,
29066
- assertionResults,
29067
- steps: orderedSteps,
29068
- errors,
29069
- duration: Date.now() - startTime
29070
- };
29071
- }
29072
- requestIndex++;
29073
- let requestUrl = "";
29074
- let requestMethod = "";
29075
- try {
29076
- const requestParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
29077
- requestUrl = requestParsed.url;
29078
- requestMethod = requestParsed.method;
29079
- validatePreparedRequest(
29080
- requestParsed,
29081
- buildRequestValidationContext(stepIdx + 1, requestMethod, requestUrl, `${varName} = run ${sequenceName}`)
29082
- );
29083
- const effectiveRetryCount = parsed.retryCount ?? requestParsed.retryCount;
29084
- const effectiveBackoffMs = parsed.backoffMs ?? requestParsed.backoffMs ?? 1e3;
29085
- const retryOpts = effectiveRetryCount ? {
29086
- retryCount: effectiveRetryCount,
29087
- backoffMs: effectiveBackoffMs,
29088
- onRetry: (attempt, total, error, waitMs) => {
29089
- reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] var ${varName} = run ${sequenceName} - waiting ${waitMs}ms`, void 0);
29259
+ if (tagFilterOptions && tagFilterOptions.filters.length > 0) {
29260
+ if (!sequenceMatchesTags(targetSequence, tagFilterOptions)) {
29261
+ continue;
29090
29262
  }
29091
- } : void 0;
29092
- const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts) : await sendRequest(requestParsed, retryOpts);
29093
- addResponse(response);
29094
- responseIndexToVariable.set(responses.length, varName);
29095
- const stepResult = {
29096
- type: "request",
29097
- stepIndex: stepIdx,
29098
- response,
29099
- requestMethod: requestParsed.method,
29100
- requestUrl: requestParsed.url,
29101
- variableName: varName
29102
- };
29103
- orderedSteps.push(stepResult);
29104
- reportProgress(stepIdx, "namedRequest", `var ${varName} = run ${sequenceName} \u2192 ${requestParsed.method} ${requestParsed.url}`, stepResult);
29105
- const capturedResponse = {
29106
- status: response.status,
29107
- statusText: response.statusText,
29108
- body: response.body,
29109
- headers: response.headers
29110
- };
29111
- runtimeVariables[varName] = JSON.stringify(capturedResponse);
29112
- const relevantCaptures = captures.filter((c) => c.afterRequest === requestIndex);
29113
- for (const capture of relevantCaptures) {
29114
- const value = getValueByPath(response, capture.path);
29115
- if (value !== void 0) {
29116
- runtimeVariables[capture.varName] = value;
29117
- } else {
29118
- errors.push(`Warning: Could not capture ${capture.varName} from $${requestIndex}.${capture.path}`);
29263
+ }
29264
+ reportProgress(stepIdx, "sequenceStart", `Starting sequence ${sequenceName}`, void 0, sequenceName);
29265
+ let subWorkingDir = workingDir;
29266
+ if (sequenceSources) {
29267
+ const sourceFile = sequenceSources.get(sequenceName.toLowerCase());
29268
+ if (sourceFile) {
29269
+ const path10 = await import("path");
29270
+ subWorkingDir = path10.dirname(sourceFile);
29119
29271
  }
29120
29272
  }
29121
- } catch (error) {
29122
- const userMessage = formatUserFacingError(
29123
- error,
29124
- buildRequestValidationContext(stepIdx + 1, requestMethod || void 0, requestUrl || void 0, `${varName} = run ${sequenceName}`)
29273
+ const targetLocation = executionContext?.sequenceLocationIndex?.get(sequenceName.toLowerCase());
29274
+ const childExecutionContext = {
29275
+ filePath: targetLocation?.filePath ?? executionContext?.filePath,
29276
+ sequenceName,
29277
+ environment: executionContext?.environment,
29278
+ sequenceStartLine: targetLocation?.startLine ?? targetSequence.startLine,
29279
+ sequenceLocationIndex: executionContext?.sequenceLocationIndex
29280
+ };
29281
+ const subResult = await runSequenceWithJar(
29282
+ targetSequence.content,
29283
+ runtimeVariables,
29284
+ cookieJar,
29285
+ subWorkingDir,
29286
+ fullDocumentText,
29287
+ onProgress,
29288
+ [...currentCallStack, sequenceName],
29289
+ sequenceArgs2,
29290
+ // Pass bound arguments
29291
+ apiDefinitions,
29292
+ // Pass API definitions for endpoint resolution
29293
+ tagFilterOptions,
29294
+ // Pass tag filter options for nested sequence filtering
29295
+ sequenceSources,
29296
+ // Pass sequence sources for nested sequences
29297
+ childExecutionContext,
29298
+ debugHooks
29125
29299
  );
29126
- let errorDetails = `Named request "${sequenceName}" failed:
29127
- ${indentMultiline(userMessage)}`;
29128
- if (requestUrl && !/\nRequest:\s/.test(userMessage)) {
29129
- errorDetails += `
29130
- Request: ${requestMethod} ${requestUrl}`;
29300
+ reportProgress(stepIdx, "sequenceEnd", `Finished sequence ${sequenceName}`, void 0, sequenceName);
29301
+ for (const subResponse of subResult.responses) {
29302
+ addResponse(subResponse);
29131
29303
  }
29132
- errors.push(errorDetails);
29133
- return {
29134
- name: "",
29135
- success: false,
29136
- responses,
29137
- scriptResults,
29138
- assertionResults,
29139
- steps: orderedSteps,
29140
- errors,
29141
- duration: Date.now() - startTime
29142
- };
29143
- }
29144
- continue;
29145
- }
29146
- const currentCallStack = callStack || [];
29147
- if (currentCallStack.includes(sequenceName)) {
29148
- errors.push(`Circular reference detected: ${[...currentCallStack, sequenceName].join(" \u2192 ")}`);
29149
- return {
29150
- name: "",
29151
- success: false,
29152
- responses,
29153
- scriptResults,
29154
- assertionResults,
29155
- steps: orderedSteps,
29156
- errors,
29157
- duration: Date.now() - startTime
29158
- };
29159
- }
29160
- let sequenceArgs2 = {};
29161
- if (targetSequence.parameters.length > 0 || runArgs.length > 0) {
29162
- const bindResult = bindSequenceArguments(targetSequence.parameters, runArgs, runtimeVariables);
29163
- if ("error" in bindResult) {
29164
- errors.push(`Error calling sequence "${sequenceName}": ${bindResult.error}`);
29165
- return {
29166
- name: "",
29167
- success: false,
29168
- responses,
29169
- scriptResults,
29170
- assertionResults,
29171
- steps: orderedSteps,
29172
- errors,
29173
- duration: Date.now() - startTime
29174
- };
29175
- }
29176
- sequenceArgs2 = bindResult.variables;
29177
- }
29178
- if (tagFilterOptions && tagFilterOptions.filters.length > 0) {
29179
- if (!sequenceMatchesTags(targetSequence, tagFilterOptions)) {
29180
- continue;
29181
- }
29182
- }
29183
- reportProgress(stepIdx, "sequenceStart", `Starting sequence ${sequenceName}`, void 0, sequenceName);
29184
- let subWorkingDir = workingDir;
29185
- if (sequenceSources) {
29186
- const sourceFile = sequenceSources.get(sequenceName.toLowerCase());
29187
- if (sourceFile) {
29188
- const path10 = await import("path");
29189
- subWorkingDir = path10.dirname(sourceFile);
29190
- }
29191
- }
29192
- const subResult = await runSequenceWithJar(
29193
- targetSequence.content,
29194
- runtimeVariables,
29195
- cookieJar,
29196
- subWorkingDir,
29197
- fullDocumentText,
29198
- onProgress,
29199
- [...currentCallStack, sequenceName],
29200
- sequenceArgs2,
29201
- // Pass bound arguments
29202
- apiDefinitions,
29203
- // Pass API definitions for endpoint resolution
29204
- tagFilterOptions,
29205
- // Pass tag filter options for nested sequence filtering
29206
- sequenceSources,
29207
- // Pass sequence sources for nested sequences
29208
- executionContext
29209
- );
29210
- reportProgress(stepIdx, "sequenceEnd", `Finished sequence ${sequenceName}`, void 0, sequenceName);
29211
- for (const subResponse of subResult.responses) {
29212
- addResponse(subResponse);
29213
- }
29214
- scriptResults.push(...subResult.scriptResults);
29215
- assertionResults.push(...subResult.assertionResults);
29216
- for (const subStep of subResult.steps) {
29217
- orderedSteps.push({
29218
- ...subStep,
29219
- stepIndex: orderedSteps.length
29220
- });
29221
- }
29222
- const returnExpressions = extractReturnVariables(targetSequence.content);
29223
- if (returnExpressions && returnExpressions.length > 0 && subResult.variables) {
29224
- if (returnExpressions.length === 1) {
29225
- const resolved = resolveReturnExpression(returnExpressions[0], subResult.variables);
29226
- if (resolved) {
29227
- runtimeVariables[varName] = resolved.value;
29304
+ scriptResults.push(...subResult.scriptResults);
29305
+ assertionResults.push(...subResult.assertionResults);
29306
+ for (const subStep of subResult.steps) {
29307
+ orderedSteps.push({
29308
+ ...subStep,
29309
+ stepIndex: orderedSteps.length
29310
+ });
29311
+ }
29312
+ const returnExpressions = extractReturnVariables(targetSequence.content);
29313
+ if (returnExpressions && returnExpressions.length > 0 && subResult.variables) {
29314
+ if (returnExpressions.length === 1) {
29315
+ const resolved = resolveReturnExpression(returnExpressions[0], subResult.variables);
29316
+ if (resolved) {
29317
+ runtimeVariables[varName] = resolved.value;
29318
+ } else {
29319
+ runtimeVariables[varName] = "";
29320
+ }
29321
+ } else {
29322
+ const returnedObj = {};
29323
+ for (const expr of returnExpressions) {
29324
+ const resolved = resolveReturnExpression(expr, subResult.variables);
29325
+ if (resolved) {
29326
+ returnedObj[resolved.key] = resolved.value;
29327
+ }
29328
+ }
29329
+ runtimeVariables[varName] = JSON.stringify(returnedObj);
29330
+ }
29228
29331
  } else {
29229
29332
  runtimeVariables[varName] = "";
29230
29333
  }
29231
- } else {
29232
- const returnedObj = {};
29233
- for (const expr of returnExpressions) {
29234
- const resolved = resolveReturnExpression(expr, subResult.variables);
29235
- if (resolved) {
29236
- returnedObj[resolved.key] = resolved.value;
29237
- }
29334
+ requestIndex += subResult.responses.length;
29335
+ if (!subResult.success) {
29336
+ errors.push(`Sequence "${sequenceName}" failed`);
29337
+ errors.push(...subResult.errors);
29338
+ return {
29339
+ name: "",
29340
+ success: false,
29341
+ responses,
29342
+ scriptResults,
29343
+ assertionResults,
29344
+ steps: orderedSteps,
29345
+ errors,
29346
+ duration: Date.now() - startTime,
29347
+ variables: runtimeVariables
29348
+ };
29238
29349
  }
29239
- runtimeVariables[varName] = JSON.stringify(returnedObj);
29240
- }
29241
- } else {
29242
- runtimeVariables[varName] = "";
29243
- }
29244
- requestIndex += subResult.responses.length;
29245
- if (!subResult.success) {
29246
- errors.push(`Sequence "${sequenceName}" failed`);
29247
- errors.push(...subResult.errors);
29248
- return {
29249
- name: "",
29250
- success: false,
29251
- responses,
29252
- scriptResults,
29253
- assertionResults,
29254
- steps: orderedSteps,
29255
- errors,
29256
- duration: Date.now() - startTime,
29257
- variables: runtimeVariables
29258
- };
29259
- }
29260
- continue;
29261
- } else {
29262
- requestIndex++;
29263
- let parsed;
29264
- let requestDescription = "";
29265
- try {
29266
- if (apiDefinitions && apiDefinitions.endpoints.length > 0 && isApiRequestLine(step.content, apiDefinitions.endpoints)) {
29267
- const apiRequest = parseApiRequest(
29268
- step.content,
29269
- apiDefinitions.endpoints,
29270
- apiDefinitions.headerGroups
29271
- );
29272
- if (apiRequest) {
29273
- const endpoint = getEndpoint(
29274
- { headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints },
29275
- apiRequest.endpointName
29276
- );
29277
- if (!endpoint) {
29278
- throw new Error(`Unknown endpoint: ${apiRequest.endpointName}`);
29279
- }
29280
- const pathWithVars = substituteVariables(endpoint.path, runtimeVariables);
29281
- const resolvedParams = {};
29282
- for (const [key, value] of Object.entries(apiRequest.params)) {
29283
- const bareResolved = resolveBareVariables(value, runtimeVariables);
29284
- resolvedParams[key] = substituteVariables(bareResolved, runtimeVariables);
29285
- }
29286
- let resolvedPath = pathWithVars;
29287
- for (const paramName of endpoint.parameters) {
29288
- const value = resolvedParams[paramName];
29289
- if (value !== void 0) {
29290
- resolvedPath = resolvedPath.replace(`{${paramName}}`, value);
29291
- }
29292
- }
29293
- const combinedHeaders = {};
29294
- for (const groupName of apiRequest.headerGroupNames) {
29295
- const group = getHeaderGroup(
29296
- { headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints },
29297
- groupName
29350
+ continue;
29351
+ } else {
29352
+ requestIndex++;
29353
+ let parsed;
29354
+ let requestDescription = "";
29355
+ try {
29356
+ if (apiDefinitions && apiDefinitions.endpoints.length > 0 && isApiRequestLine(step.content, apiDefinitions.endpoints)) {
29357
+ const apiRequest = parseApiRequest(
29358
+ step.content,
29359
+ apiDefinitions.endpoints,
29360
+ apiDefinitions.headerGroups
29298
29361
  );
29299
- if (group) {
29300
- const resolvedHeaders = resolveHeaderValues(group, runtimeVariables);
29301
- Object.assign(combinedHeaders, resolvedHeaders);
29362
+ if (apiRequest) {
29363
+ const endpoint = getEndpoint(
29364
+ { headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints },
29365
+ apiRequest.endpointName
29366
+ );
29367
+ if (!endpoint) {
29368
+ throw new Error(`Unknown endpoint: ${apiRequest.endpointName}`);
29369
+ }
29370
+ const pathWithVars = substituteVariables(endpoint.path, runtimeVariables);
29371
+ const resolvedParams = {};
29372
+ for (const [key, value] of Object.entries(apiRequest.params)) {
29373
+ const bareResolved = resolveBareVariables(value, runtimeVariables);
29374
+ resolvedParams[key] = substituteVariables(bareResolved, runtimeVariables);
29375
+ }
29376
+ let resolvedPath = pathWithVars;
29377
+ for (const paramName of endpoint.parameters) {
29378
+ const value = resolvedParams[paramName];
29379
+ if (value !== void 0) {
29380
+ resolvedPath = resolvedPath.replace(`{${paramName}}`, value);
29381
+ }
29382
+ }
29383
+ const combinedHeaders = {};
29384
+ for (const groupName of apiRequest.headerGroupNames) {
29385
+ const group = getHeaderGroup(
29386
+ { headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints },
29387
+ groupName
29388
+ );
29389
+ if (group) {
29390
+ const resolvedHeaders = resolveHeaderValues(group, runtimeVariables);
29391
+ Object.assign(combinedHeaders, resolvedHeaders);
29392
+ }
29393
+ }
29394
+ for (const [headerName, headerValue] of Object.entries(apiRequest.inlineHeaders)) {
29395
+ combinedHeaders[headerName] = substituteVariables(headerValue, runtimeVariables);
29396
+ }
29397
+ let resolvedBody;
29398
+ if (apiRequest.body) {
29399
+ resolvedBody = substituteVariables(apiRequest.body, runtimeVariables);
29400
+ }
29401
+ parsed = {
29402
+ method: apiRequest.method,
29403
+ url: resolvedPath,
29404
+ headers: combinedHeaders,
29405
+ body: resolvedBody
29406
+ };
29407
+ requestDescription = `${apiRequest.endpointName}(${Object.values(resolvedParams).join(", ")}) \u2192 ${parsed.method} ${parsed.url}`;
29408
+ } else {
29409
+ parsed = parserHttpRequest(step.content, runtimeVariables);
29410
+ requestDescription = `${parsed.method} ${parsed.url}`;
29411
+ if (apiDefinitions && apiDefinitions.headerGroups.length > 0) {
29412
+ parsed = applyHeaderGroupsToRequest(parsed, step.content, apiDefinitions.headerGroups, runtimeVariables);
29413
+ }
29414
+ }
29415
+ } else {
29416
+ parsed = parserHttpRequest(step.content, runtimeVariables);
29417
+ requestDescription = `${parsed.method} ${parsed.url}`;
29418
+ if (apiDefinitions && apiDefinitions.headerGroups.length > 0) {
29419
+ parsed = applyHeaderGroupsToRequest(parsed, step.content, apiDefinitions.headerGroups, runtimeVariables);
29420
+ }
29421
+ if (parsed.body) {
29422
+ const bodyTrimmed = parsed.body.trim();
29423
+ const bareVarMatch = bodyTrimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)$/);
29424
+ if (bareVarMatch && bareVarMatch[1] in runtimeVariables) {
29425
+ const varValue = runtimeVariables[bareVarMatch[1]];
29426
+ if (typeof varValue === "object" && varValue !== null) {
29427
+ parsed.body = JSON.stringify(varValue);
29428
+ } else if (typeof varValue === "string") {
29429
+ parsed.body = varValue;
29430
+ } else {
29431
+ parsed.body = String(varValue);
29432
+ }
29433
+ }
29302
29434
  }
29303
29435
  }
29304
- for (const [headerName, headerValue] of Object.entries(apiRequest.inlineHeaders)) {
29305
- combinedHeaders[headerName] = substituteVariables(headerValue, runtimeVariables);
29306
- }
29307
- let resolvedBody;
29308
- if (apiRequest.body) {
29309
- resolvedBody = substituteVariables(apiRequest.body, runtimeVariables);
29310
- }
29311
- parsed = {
29312
- method: apiRequest.method,
29313
- url: resolvedPath,
29314
- headers: combinedHeaders,
29315
- body: resolvedBody
29436
+ const { retryCount, backoffMs } = extractRetryOptions(step.content);
29437
+ validatePreparedRequest(
29438
+ parsed,
29439
+ buildRequestValidationContext(stepIdx + 1, parsed.method, parsed.url)
29440
+ );
29441
+ const retryOpts = retryCount ?? parsed.retryCount ? {
29442
+ retryCount: retryCount ?? parsed.retryCount ?? 0,
29443
+ backoffMs: backoffMs ?? parsed.backoffMs ?? 1e3,
29444
+ onRetry: (attempt, total, error, waitMs) => {
29445
+ reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] ${requestDescription} - waiting ${waitMs}ms`, void 0);
29446
+ }
29447
+ } : void 0;
29448
+ const response = cookieJar ? await sendRequestWithJar(parsed, cookieJar, retryOpts) : await sendRequest(parsed, retryOpts);
29449
+ addResponse(response);
29450
+ const stepResult = {
29451
+ type: "request",
29452
+ stepIndex: stepIdx,
29453
+ response,
29454
+ requestMethod: parsed.method,
29455
+ requestUrl: parsed.url
29316
29456
  };
29317
- requestDescription = `${apiRequest.endpointName}(${Object.values(resolvedParams).join(", ")}) \u2192 ${parsed.method} ${parsed.url}`;
29318
- } else {
29319
- parsed = parserHttpRequest(step.content, runtimeVariables);
29320
- requestDescription = `${parsed.method} ${parsed.url}`;
29321
- if (apiDefinitions && apiDefinitions.headerGroups.length > 0) {
29322
- parsed = applyHeaderGroupsToRequest(parsed, step.content, apiDefinitions.headerGroups, runtimeVariables);
29323
- }
29324
- }
29325
- } else {
29326
- parsed = parserHttpRequest(step.content, runtimeVariables);
29327
- requestDescription = `${parsed.method} ${parsed.url}`;
29328
- if (apiDefinitions && apiDefinitions.headerGroups.length > 0) {
29329
- parsed = applyHeaderGroupsToRequest(parsed, step.content, apiDefinitions.headerGroups, runtimeVariables);
29330
- }
29331
- if (parsed.body) {
29332
- const bodyTrimmed = parsed.body.trim();
29333
- const bareVarMatch = bodyTrimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)$/);
29334
- if (bareVarMatch && bareVarMatch[1] in runtimeVariables) {
29335
- const varValue = runtimeVariables[bareVarMatch[1]];
29336
- if (typeof varValue === "object" && varValue !== null) {
29337
- parsed.body = JSON.stringify(varValue);
29338
- } else if (typeof varValue === "string") {
29339
- parsed.body = varValue;
29457
+ orderedSteps.push(stepResult);
29458
+ reportProgress(stepIdx, "request", requestDescription, stepResult);
29459
+ const relevantCaptures = captures.filter((c) => c.afterRequest === requestIndex);
29460
+ for (const capture of relevantCaptures) {
29461
+ const value = getValueByPath(response, capture.path);
29462
+ if (value !== void 0) {
29463
+ runtimeVariables[capture.varName] = value;
29340
29464
  } else {
29341
- parsed.body = String(varValue);
29465
+ errors.push(`Warning: Could not capture ${capture.varName} from $${requestIndex}.${capture.path}`);
29342
29466
  }
29343
29467
  }
29344
- }
29345
- }
29346
- const { retryCount, backoffMs } = extractRetryOptions(step.content);
29347
- validatePreparedRequest(
29348
- parsed,
29349
- buildRequestValidationContext(stepIdx + 1, parsed.method, parsed.url)
29350
- );
29351
- const retryOpts = retryCount ?? parsed.retryCount ? {
29352
- retryCount: retryCount ?? parsed.retryCount ?? 0,
29353
- backoffMs: backoffMs ?? parsed.backoffMs ?? 1e3,
29354
- onRetry: (attempt, total, error, waitMs) => {
29355
- reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] ${requestDescription} - waiting ${waitMs}ms`, void 0);
29356
- }
29357
- } : void 0;
29358
- const response = cookieJar ? await sendRequestWithJar(parsed, cookieJar, retryOpts) : await sendRequest(parsed, retryOpts);
29359
- addResponse(response);
29360
- const stepResult = {
29361
- type: "request",
29362
- stepIndex: stepIdx,
29363
- response,
29364
- requestMethod: parsed.method,
29365
- requestUrl: parsed.url
29366
- };
29367
- orderedSteps.push(stepResult);
29368
- reportProgress(stepIdx, "request", requestDescription, stepResult);
29369
- const relevantCaptures = captures.filter((c) => c.afterRequest === requestIndex);
29370
- for (const capture of relevantCaptures) {
29371
- const value = getValueByPath(response, capture.path);
29372
- if (value !== void 0) {
29373
- runtimeVariables[capture.varName] = value;
29374
- } else {
29375
- errors.push(`Warning: Could not capture ${capture.varName} from $${requestIndex}.${capture.path}`);
29376
- }
29377
- }
29378
- } catch (error) {
29379
- const userMessage = formatUserFacingError(
29380
- error,
29381
- buildRequestValidationContext(stepIdx + 1)
29382
- );
29383
- let errorDetails = `Request ${requestIndex} failed:
29468
+ } catch (error) {
29469
+ const userMessage = formatUserFacingError(
29470
+ error,
29471
+ buildRequestValidationContext(stepIdx + 1)
29472
+ );
29473
+ let errorDetails = `Request ${requestIndex} failed:
29384
29474
  ${indentMultiline(userMessage)}`;
29385
- if (requestDescription && !/\nRequest:\s/.test(userMessage)) {
29386
- errorDetails += `
29475
+ if (requestDescription && !/\nRequest:\s/.test(userMessage)) {
29476
+ errorDetails += `
29387
29477
  Request: ${requestDescription}`;
29478
+ }
29479
+ errors.push(errorDetails);
29480
+ await emitFailure(errorDetails, step, stepIdx);
29481
+ return {
29482
+ name: "",
29483
+ success: false,
29484
+ responses,
29485
+ scriptResults,
29486
+ assertionResults,
29487
+ steps: orderedSteps,
29488
+ errors,
29489
+ duration: Date.now() - startTime
29490
+ };
29491
+ }
29388
29492
  }
29389
- errors.push(errorDetails);
29390
- return {
29391
- name: "",
29392
- success: false,
29393
- responses,
29394
- scriptResults,
29395
- assertionResults,
29396
- steps: orderedSteps,
29397
- errors,
29398
- duration: Date.now() - startTime
29399
- };
29493
+ } finally {
29494
+ await emitAfterStep(step, stepIdx);
29400
29495
  }
29401
29496
  }
29497
+ return {
29498
+ name: "",
29499
+ success: errors.filter((e) => !e.startsWith("Warning")).length === 0,
29500
+ responses,
29501
+ scriptResults,
29502
+ assertionResults,
29503
+ steps: orderedSteps,
29504
+ errors,
29505
+ duration: Date.now() - startTime,
29506
+ variables: runtimeVariables
29507
+ };
29508
+ } finally {
29509
+ if (debugHooks?.onSequenceExit) {
29510
+ await debugHooks.onSequenceExit({
29511
+ sequenceName: executionContext?.sequenceName,
29512
+ filePath: executionContext?.filePath,
29513
+ declarationLine: executionContext?.sequenceStartLine,
29514
+ nestingDepth,
29515
+ runtimeVariables,
29516
+ responses
29517
+ });
29518
+ }
29402
29519
  }
29403
- return {
29404
- name: "",
29405
- success: errors.filter((e) => !e.startsWith("Warning")).length === 0,
29406
- responses,
29407
- scriptResults,
29408
- assertionResults,
29409
- steps: orderedSteps,
29410
- errors,
29411
- duration: Date.now() - startTime,
29412
- variables: runtimeVariables
29413
- };
29414
29520
  }
29415
29521
 
29416
29522
  // src/cli/colors.ts