feat: support await in $B js and eval commands

Auto-wrap await expressions in async IIFE context so
$B js "await fetch(...)" works without SyntaxError.

- hasAwait() strips comments before detection
- js: expression wrapping (async()=>(expr))()
- eval: smart wrapping — single-line=expression, multi-line=block
- 6 new unit tests covering async, false-positive, and return semantics
This commit is contained in:
Garry Tan
2026-03-16 09:43:41 -05:00
parent 2357f134ce
commit a05a670189
2 changed files with 69 additions and 1 deletions

View File

@@ -11,6 +11,12 @@ import type { Page } from 'playwright';
import * as fs from 'fs';
import * as path from 'path';
/** Detect await keyword, ignoring comments. Accepted risk: await in string literals triggers wrapping (harmless). */
function hasAwait(code: string): boolean {
const stripped = code.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
return /\bawait\b/.test(stripped);
}
// Security: Path validation to prevent path traversal attacks
const SAFE_DIRECTORIES = ['/tmp', process.cwd()];
@@ -118,7 +124,8 @@ export async function handleReadCommand(
case 'js': {
const expr = args[0];
if (!expr) throw new Error('Usage: browse js <expression>');
const result = await page.evaluate(expr);
const wrapped = hasAwait(expr) ? `(async()=>(${expr}))()` : expr;
const result = await page.evaluate(wrapped);
return typeof result === 'object' ? JSON.stringify(result, null, 2) : String(result ?? '');
}
@@ -128,6 +135,13 @@ export async function handleReadCommand(
validateReadPath(filePath);
if (!fs.existsSync(filePath)) throw new Error(`File not found: ${filePath}`);
const code = fs.readFileSync(filePath, 'utf-8');
if (hasAwait(code)) {
const trimmed = code.trim();
const isSingleExpr = trimmed.split('\n').length === 1;
const wrapped = isSingleExpr ? `(async()=>(${trimmed}))()` : `(async()=>{\n${code}\n})()`;
const result = await page.evaluate(wrapped);
return typeof result === 'object' ? JSON.stringify(result, null, 2) : String(result ?? '');
}
const result = await page.evaluate(code);
return typeof result === 'object' ? JSON.stringify(result, null, 2) : String(result ?? '');
}