Files
hermes-skills/skills/devops/hermes-browser-setup/references/js-rendered-api-docs.md
T

4.0 KiB

JS-Rendered API Documentation Pages

Pattern observed when browsing OpenAPI/Redoc-styled docs (like RingLogix, Swagger, Stoplight):

What the Accessibility Tree Shows

  • Sidebar/nav tree — fully accessible via browser_snapshot(full=true). Category names, endpoint paths, and hierarchical structure appear as text nodes.
  • Main content pane — the actual API spec (endpoint details, parameters, schemas, sample requests) renders in a JavaScript-driven viewer (Redoc, Swagger UI, Scalar). The accessibility tree may show these as opaque elements with role="region" or similar, with no rich text content.
  • Search/filter box at the top — accessible for navigation.
  1. browser_navigate(url) — loads the page
  2. browser_snapshot(full=true) — get the full sidebar nav structure
  3. If sidebar content is present but main pane is empty:
    • browser_click(element) on a sidebar link to navigate to a specific endpoint
    • browser_snapshot(full=true) again after navigation (JS may then render that section)
    • Or use browser_vision to screenshot and analyze visually

CDP Bulk Extraction (for large Redoc/Swagger pages)

When the SPA loads a huge page (e.g. 238 endpoints, 186K chars of docs), browser_snapshot truncates at ~18K lines and browser_console JS selectors may find no <nav> element (Redoc renders into custom elements or shadow DOM). Use this CDP-based probe sequence instead:

Step 1: Navigate and identify the correct target

browser_navigate(url)
// Wait ~2s for SPA bootstrap
browser_snapshot(full=true)  // Sidebar should be visible

Step 2: List all browser targets via CDP

browser_cdp(method="Target.getTargets", params={})

Look for the page with the expected title (NOT chrome://newtab). Returns targetInfos array — the target you navigated to has the real URL and title field.

Step 3: Extract text via CDP on the correct target

browser_cdp(
  target_id="<target ID from Step 2>",
  method="Runtime.evaluate",
  params={
    expression: "document.body.innerText.substring(0, 8000)",
    returnByValue: true
  }
)

Page through sections by changing the substring range. For large docs (e.g. RingLogix: 186K chars), use intervals of 3000-4000 at a time.

Step 4: Get full sidebar nav list via CDP

browser_cdp(
  target_id="<target ID>",
  method="Runtime.evaluate",
  params={
    expression: "Array.from(document.querySelectorAll('li a')).map(a => a.textContent.trim()).filter(t => t)",
    returnByValue: true
  }
)

Returns the complete sidebar as an ordered array of link text.

Step 5: Confirm page identity

browser_cdp(
  target_id="<target ID>",
  method="Runtime.evaluate",
  params={
    expression: "document.title",
    returnByValue: true
  }
)

Key differences from browser_console

Aspect browser_console browser_cdp + Runtime.evaluate
Cap Expression length None (full function bodies ok)
Target Current browser tab Any target by ID
Shadow DOM Limited Limited (same as console)
Return size Moderate Large (substring approach)
Syntax errors Reported inline Fail silently on malformed arrow functions — use (function(){...})() not ()=>{...}

Confirmed Case: RingLogix API Docs

URL: https://apidocs.ringlogix.com/ Status: Public, no login required Content: ~186K characters of API documentation on page load Structure: Sidebar with OAuth2, Agents, Agent Log, Answer Rule, Audio, CDR2, CDR Export, CDR Schedule, Call, Call Center Stats, Call Queue, Call Queue Stats, Subscriber, Device, Message, Emergency Address ~7,800+ elements in the DOM

Confirmed Case: browser_console caveats

Two pitfalls observed:

  1. querySelector('nav') returns null on Redoc pages — the sidebar renders as custom elements, not <nav>.
  2. Arrow functions (() => {...}) with template literals can trigger SyntaxError: Unexpected end of input via browser_console. Use (function(){...})() syntax instead for complex expressions.