Concepts

JSON output & exit codes

Using the global --json flag and exit codes to script browserlane reliably.

browserlane is built to be scripted. Two mechanisms make its output easy to consume from other programs: the global --json flag and meaningful exit codes.

The --json flag

--json is a global flag — append it to a command to get machine-readable output where the command produces data:

bl eval --json 'JSON.stringify({url: location.href, title: document.title})'

This pairs naturally with bl eval, which is the most common place you want a structured payload back. Combine it with --stdin for longer scripts:

bl eval --json --stdin <<'EOF'
const rows = [...document.querySelectorAll('table tbody tr')];
JSON.stringify(rows.map(r => {
  const cells = r.querySelectorAll('td');
  return { name: cells[0].textContent.trim(), price: cells[1].textContent.trim() };
}));
EOF

Make eval return the value

bl eval prints the expression's result. If your script doesn't end in an expression that evaluates to your data, you'll get null — so make the last line the value you want.

State checks return true / false

The bl is checks print a plain true or false, which is easy to branch on in a script:

bl is visible "h1"        # prints "true" or "false"
bl is enabled "button"
bl is checked "#agree"
bl is actionable ".cta"
if [ "$(bl is visible '.modal')" = "true" ]; then
  bl click ".modal .close"
fi

When the check is really an assertion — the flow should stop if the state is wrong — skip the string comparison and use bl expect, which folds the check into the exit code (next section).

Exit codes for scripting

Some commands signal their result through the exit code so you can use them directly in shell conditionals — no output parsing required. The clearest example is bl is-installed:

bl is-installed   # exit 0 = Chrome + chromedriver present, exit 1 = missing
bl is-installed || bl install   # install Chrome only if it isn't already there

bl expect makes the exit code the whole point: every assertion exits 0 on pass and 1 on failure, which turns a browser flow into something a script or CI job can gate on directly:

bl expect url contains "/dashboard"   # PASS expect … and exit 0 — or exit 1 with the actual URL
bl expect count ".error" 0            # assert no error elements on the page
bl expect js "window.appReady === true"

With --json, a passing assertion prints {"ok":true,"result":"PASS …"} and a failing one prints {"ok":false,"error":"expect … failed: actual …"} — same envelope, same exit codes.

More generally, bl follows the usual convention: a command exits 0 on success and non-zero on failure. Chains stop on the first failure, so a sequence like the following won't take the screenshot if navigation fails — and an expect in the middle of a chain acts as a checkpoint:

bl open https://example.com && bl screenshot -o page.png
bl open https://example.com && bl expect title contains "Example" && bl screenshot -o page.png

On this page