runLoop function

Last updated: 2026-03-05T10:53:28.864Z

Metrics

LOC: 123 Complexity: 32 Params: 1

Signature

runLoop(signal: AbortSignal): : Promise<void>

Architecture violations

View all

  • [warning] max-cyclomatic-complexity: 'runLoop' has cyclomatic complexity 32 (max 10)
  • [warning] max-lines: 'runLoop' has 123 lines (max 80)

Source Code

  async function runLoop(signal: AbortSignal): Promise<void> {
    if (!flow || !rng) return;

    // Navigate to baseUrl before executing steps
    if (flow.metadata.baseUrl) {
      log.info(`Navigating to baseUrl: ${flow.metadata.baseUrl}`);
      const loaded = await navigateAndWait(tabId, flow.metadata.baseUrl);
      if (!loaded) {
        log.warn(`Failed to navigate to baseUrl: ${flow.metadata.baseUrl}`);
      }
      await injectContentScript(tabId);
    }

    for (; stepIndex < flow.steps.length; stepIndex++) {
      if (signal.aborted) return;
      await waitIfPaused();
      if (signal.aborted) return;

      const step = flow.steps[stepIndex]!;
      emitProgress(step);

      // Apply delay before step
      const delay =
        config.useRecordedTimings && step.delayBefore
          ? step.delayBefore
          : config.stepDelay;

      if (delay > 0 && stepIndex > 0) {
        try {
          await sleep(delay, signal);
        } catch {
          return; // aborted during delay
        }
      }

      // Resolve value for fill steps
      let resolvedValue: string | undefined;
      if (step.action === "fill" && step.valueSource) {
        resolvedValue = resolveValueSource(step.valueSource, generate);
      }

      // Execute
      let result: StepResult;
      if (step.action === "navigate") {
        result = await executeNavigateStep(step);
      } else {
        // Before interaction, move cursor (sent as separate message)
        if (
          step.selector &&
          config.highlightDuration > 0 &&
          config.showCursor !== false
        ) {
          try {
            await chrome.tabs.sendMessage(tabId, {
              type: "DEMO_CURSOR_MOVE",
              payload: { selector: step.selector, durationMs: 400 },
            });
            await sleep(config.highlightDuration, signal);
          } catch {
            // cursor overlay is optional — ignore
          }
        }

        // Click effect
        if (step.action === "click" && config.showCursor !== false) {
          try {
            await chrome.tabs.sendMessage(tabId, {
              type: "DEMO_CURSOR_CLICK",
            });
          } catch {
            // ignore
          }
        }

        // Highlight element
        if (config.highlightDuration > 0) {
          try {
            await chrome.tabs.sendMessage(tabId, {
              type: "DEMO_HIGHLIGHT_ELEMENT",
              payload: { step, durationMs: config.highlightDuration },
            });
          } catch {
            // ignore
          }
        }

        result = await sendStepToContentScript(step, resolvedValue);
      }

      stepResults.push({ stepId: step.id, result });

      // Handle failure
      if (result.status === "failed") {
        if (step.optional) {
          log.info(
            `Optional step ${step.id} failed, continuing:`,
            result.error,
          );
        } else {
          log.warn(`Step ${step.id} failed:`, result.error);
          setStatus("failed");
          callbacks.onComplete?.(buildResult());
          return;
        }
      }

      // Apply delay after step
      if (step.delayAfter && step.delayAfter > 0) {
        try {
          await sleep(step.delayAfter, signal);
        } catch {
          return;
        }
      }
    }

    // All steps complete
    setStatus("completed");
    chrome.tabs
      .sendMessage(tabId, { type: "DEMO_CURSOR_DESTROY" })
      .catch(() => {});
    callbacks.onComplete?.(buildResult());
  }

Dependencies (Outgoing)

graph LR runLoop["runLoop"] navigateAndWait["navigateAndWait"] injectContentScript["injectContentScript"] waitIfPaused["waitIfPaused"] emitProgress["emitProgress"] sleep["sleep"] resolveValueSource["resolveValueSource"] executeNavigateStep["executeNavigateStep"] sendStepToContentScript["sendStepToContentScript"] setStatus["setStatus"] buildResult["buildResult"] runLoop -->|calls| navigateAndWait runLoop -->|calls| injectContentScript runLoop -->|calls| waitIfPaused runLoop -->|calls| emitProgress runLoop -->|calls| sleep runLoop -->|calls| resolveValueSource runLoop -->|calls| executeNavigateStep runLoop -->|calls| sendStepToContentScript runLoop -->|calls| setStatus runLoop -->|calls| buildResult style runLoop fill:#dbeafe,stroke:#2563eb,stroke-width:2px click runLoop "c94a70bb97b4ccad.html" click navigateAndWait "915565f33bfb5777.html" click injectContentScript "8371aca98c2d1d41.html" click waitIfPaused "96b61f4e18798e3c.html" click emitProgress "fc32d58a10988ff8.html" click sleep "25404f77365ed229.html" click resolveValueSource "038d75ba8fb4a8c2.html" click executeNavigateStep "b662d9614e361217.html" click sendStepToContentScript "98a94012c260f4a2.html" click setStatus "811d4f691e446460.html" click buildResult "647e3b02375c2aa7.html"

Impact (Incoming)

graph LR runLoop["runLoop"] start["start"] start -->|calls| runLoop style runLoop fill:#dbeafe,stroke:#2563eb,stroke-width:2px click runLoop "c94a70bb97b4ccad.html" click start "56469c2f243a2128.html"
SourceType
start calls