UNPKG

@browserbasehq/stagehand

Version:

An AI web browsing framework focused on simplicity and extensibility.

1,348 lines (1,345 loc) • 1.46 MB
var __create = Object.create; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __async = (__this, __arguments, generator) => { return new Promise((resolve3, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve3(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // lib/v3/logger.ts function bindInstanceLogger(instanceId, logger) { instanceLoggers.set(instanceId, logger); } function unbindInstanceLogger(instanceId) { instanceLoggers.delete(instanceId); } function withInstanceLogContext(instanceId, fn) { return logContext.run(instanceId, fn); } function v3Logger(line) { var _a, _b, _c; const id = logContext.getStore(); if (id) { const fn = instanceLoggers.get(id); if (fn) { const enriched = __spreadProps(__spreadValues({}, line), { auxiliary: __spreadValues({}, line.auxiliary || {}) }); try { fn(enriched); return; } catch (e) { } } } const ts = (_a = line.timestamp) != null ? _a : (/* @__PURE__ */ new Date()).toISOString(); const lvl = (_b = line.level) != null ? _b : 1; const levelStr = lvl === 0 ? "ERROR" : lvl === 2 ? "DEBUG" : "INFO"; let output = `[${ts}] ${levelStr}: ${line.message}`; if (line.auxiliary) { for (const [key, { value, type }] of Object.entries(line.auxiliary)) { let formattedValue = value; if (type === "object") { try { formattedValue = JSON.stringify(JSON.parse(value), null, 2).split("\n").map((line2, i) => i === 0 ? line2 : ` ${line2}`).join("\n"); } catch (e) { formattedValue = value; } } output += ` ${key}: ${formattedValue}`; } } if (lvl === 0) { console.error(output); } else if (lvl === 2) { ((_c = console.debug) != null ? _c : console.log)(output); } else { console.log(output); } } var import_node_async_hooks, logContext, instanceLoggers; var init_logger = __esm({ "lib/v3/logger.ts"() { import_node_async_hooks = require("async_hooks"); logContext = new import_node_async_hooks.AsyncLocalStorage(); instanceLoggers = /* @__PURE__ */ new Map(); } }); // lib/v3/understudy/executionContextRegistry.ts var ExecutionContextRegistry, executionContexts; var init_executionContextRegistry = __esm({ "lib/v3/understudy/executionContextRegistry.ts"() { ExecutionContextRegistry = class { constructor() { this.byFrame = /* @__PURE__ */ new WeakMap(); this.byExec = /* @__PURE__ */ new WeakMap(); } /** Wire listeners for this session. Call BEFORE Runtime.enable. */ attachSession(session) { const onCreated = (evt) => { var _a; const aux = (_a = evt.context.auxData) != null ? _a : {}; if (aux.isDefault === true && typeof aux.frameId === "string") { this.register(session, aux.frameId, evt.context.id); } }; const onDestroyed = (evt) => { const rev = this.byExec.get(session); const fwd = this.byFrame.get(session); if (!rev || !fwd) return; const frameId = rev.get(evt.executionContextId); if (!frameId) return; rev.delete(evt.executionContextId); if (fwd.get(frameId) === evt.executionContextId) fwd.delete(frameId); }; const onCleared = () => { this.byFrame.delete(session); this.byExec.delete(session); }; session.on("Runtime.executionContextCreated", onCreated); session.on("Runtime.executionContextDestroyed", onDestroyed); session.on("Runtime.executionContextsCleared", onCleared); } getMainWorld(session, frameId) { var _a, _b; return (_b = (_a = this.byFrame.get(session)) == null ? void 0 : _a.get(frameId)) != null ? _b : null; } waitForMainWorld(session, frameId, timeoutMs = 800) { return __async(this, null, function* () { const cached = this.getMainWorld(session, frameId); if (cached) return cached; yield session.send("Runtime.enable").catch(() => { }); const after = this.getMainWorld(session, frameId); if (after) return after; return yield new Promise((resolve3, reject) => { let done = false; const onCreated = (evt) => { var _a; const aux = (_a = evt.context.auxData) != null ? _a : {}; if (aux.isDefault === true && aux.frameId === frameId) { this.register(session, frameId, evt.context.id); if (!done) { done = true; clearTimeout(timer); session.off("Runtime.executionContextCreated", onCreated); resolve3(evt.context.id); } } }; const timer = setTimeout(() => { if (!done) { done = true; session.off("Runtime.executionContextCreated", onCreated); reject(new Error(`main world not ready for frame ${frameId}`)); } }, timeoutMs); session.on("Runtime.executionContextCreated", onCreated); }); }); } register(session, frameId, ctxId) { let fwd = this.byFrame.get(session); if (!fwd) { fwd = /* @__PURE__ */ new Map(); this.byFrame.set(session, fwd); } let rev = this.byExec.get(session); if (!rev) { rev = /* @__PURE__ */ new Map(); this.byExec.set(session, rev); } fwd.set(frameId, ctxId); rev.set(ctxId, frameId); } }; executionContexts = new ExecutionContextRegistry(); } }); // lib/v3/understudy/a11y/snapshot.ts function resolveXpathForLocation(page, x, y) { return __async(this, null, function* () { var _a, _b, _c, _d, _e, _f, _g, _h; const tree = page.getFullFrameTree(); const parentByFrame = /* @__PURE__ */ new Map(); (function index(n, parent) { var _a2; parentByFrame.set(n.frame.id, parent); for (const c of (_a2 = n.childFrames) != null ? _a2 : []) index(c, n.frame.id); })(tree, null); const iframeChain = []; let curFrameId = page.mainFrameId(); let curSession = page.getSessionForFrame(curFrameId); let curX = x; let curY = y; for (let depth = 0; depth < 8; depth++) { try { yield curSession.send("DOM.enable").catch(() => { }); let sx = 0; let sy = 0; try { yield curSession.send("Runtime.enable").catch(() => { }); const ctxId = yield executionContexts.waitForMainWorld(curSession, curFrameId).catch(() => { }); const evalParams = ctxId ? { contextId: ctxId, expression: scrollOffsetsExpr(), returnByValue: true } : { expression: scrollOffsetsExpr(), returnByValue: true }; const { result } = yield curSession.send("Runtime.evaluate", evalParams); sx = Number((_b = (_a = result == null ? void 0 : result.value) == null ? void 0 : _a.sx) != null ? _b : 0); sy = Number((_d = (_c = result == null ? void 0 : result.value) == null ? void 0 : _c.sy) != null ? _d : 0); } catch (e) { } const xi = Math.max(0, Math.floor(curX + sx)); const yi = Math.max(0, Math.floor(curY + sy)); let res; try { res = yield curSession.send("DOM.getNodeForLocation", { x: xi, y: yi, includeUserAgentShadowDOM: false, ignorePointerEventsNone: false }); } catch (e) { return null; } const be = res == null ? void 0 : res.backendNodeId; const reportedFrameId = res == null ? void 0 : res.frameId; if (typeof be === "number" && reportedFrameId && reportedFrameId !== curFrameId) { const abs = yield buildAbsoluteXPathFromChain( iframeChain, curSession, be ); return abs ? { frameId: reportedFrameId, backendNodeId: be, absoluteXPath: abs } : null; } if (typeof be !== "number") return null; let matchedChild; for (const fid of listChildrenOf(parentByFrame, curFrameId)) { try { const { backendNodeId } = yield curSession.send("DOM.getFrameOwner", { frameId: fid }); if (backendNodeId === be) { matchedChild = fid; break; } } catch (e) { } } if (!matchedChild) { const abs = yield buildAbsoluteXPathFromChain( iframeChain, curSession, be ); return abs ? { frameId: curFrameId, backendNodeId: be, absoluteXPath: abs } : null; } iframeChain.push({ parentFrameId: curFrameId, parentSession: curSession, iframeBackendNodeId: be }); let left = 0; let top = 0; try { const { object } = yield curSession.send("DOM.resolveNode", { backendNodeId: be }); const objectId = object == null ? void 0 : object.objectId; if (objectId) { const { result } = yield curSession.send("Runtime.callFunctionOn", { objectId, functionDeclaration: "function(){ const r=this.getBoundingClientRect(); return {left:r.left, top:r.top}; }", returnByValue: true }); left = Number((_f = (_e = result == null ? void 0 : result.value) == null ? void 0 : _e.left) != null ? _f : 0); top = Number((_h = (_g = result == null ? void 0 : result.value) == null ? void 0 : _g.top) != null ? _h : 0); yield curSession.send("Runtime.releaseObject", { objectId }).catch(() => { }); } } catch (e) { } curX = Math.max(0, curX - left); curY = Math.max(0, curY - top); curFrameId = matchedChild; curSession = page.getSessionForFrame(curFrameId); } catch (e) { return null; } } return null; }); } function computeActiveElementXpath(page) { return __async(this, null, function* () { var _a; const tree = page.getFullFrameTree(); const parentByFrame = /* @__PURE__ */ new Map(); (function index(n, parent) { var _a2; parentByFrame.set(n.frame.id, parent); for (const c of (_a2 = n.childFrames) != null ? _a2 : []) index(c, n.frame.id); })(tree, null); const frames = page.listAllFrameIds(); let focusedFrameId = null; for (const fid of frames) { const sess = page.getSessionForFrame(fid); try { yield sess.send("Runtime.enable").catch(() => { }); const ctxId = yield executionContexts.waitForMainWorld(sess, fid, 1e3).catch(() => { }); const evalParams = ctxId ? { contextId: ctxId, expression: "document.hasFocus()===true", returnByValue: true } : { expression: "document.hasFocus()===true", returnByValue: true }; const { result } = yield sess.send( "Runtime.evaluate", evalParams ); if ((result == null ? void 0 : result.value) === true) { focusedFrameId = fid; break; } } catch (e) { } } if (!focusedFrameId) focusedFrameId = page.mainFrameId(); const focusedSession = page.getSessionForFrame(focusedFrameId); let objectId; try { yield focusedSession.send("Runtime.enable").catch(() => { }); const ctxId = yield executionContexts.waitForMainWorld(focusedSession, focusedFrameId, 1e3).catch(() => { }); const expr = `(() => { try { function deepActive(doc) { let el = doc.activeElement || null; while (el && el.shadowRoot && el.shadowRoot.activeElement) { el = el.shadowRoot.activeElement; } return el || null; } return deepActive(document); } catch { return null; } })()`; const evalParams = ctxId ? { contextId: ctxId, expression: expr, returnByValue: false } : { expression: expr, returnByValue: false }; const { result } = yield focusedSession.send( "Runtime.evaluate", evalParams ); objectId = result == null ? void 0 : result.objectId; } catch (e) { objectId = void 0; } if (!objectId) return null; const leafXPath = yield (() => __async(null, null, function* () { try { const { result } = yield focusedSession.send("Runtime.callFunctionOn", { objectId, functionDeclaration: `function() { try { const node = this; function sibIndex(n) { let i = 1; const t = n.nodeType+':'+(n.nodeName||'').toLowerCase(); for (let p = n.previousSibling; p; p = p.previousSibling) { const key = p.nodeType+':'+(p.nodeName||'').toLowerCase(); if (key === t) i++; } return i; } function step(n) { if (!n) return ''; if (n.nodeType === Node.DOCUMENT_NODE) return ''; if (n.nodeType === Node.DOCUMENT_FRAGMENT_NODE) return '//'; if (n.nodeType === Node.TEXT_NODE) return 'text()[' + sibIndex(n) + ']'; if (n.nodeType === Node.COMMENT_NODE) return 'comment()[' + sibIndex(n) + ']'; const tag = (n.nodeName||'').toLowerCase(); const name = tag.includes(':') ? "*[name()='"+tag+"']" : tag; return name + '[' + sibIndex(n) + ']'; } const parts = []; let cur = node; while (cur) { if (cur.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { parts.push('//'); cur = (cur && cur.host) ? cur.host : null; continue; } const s = step(cur); if (s) parts.push(s); cur = cur.parentNode; } parts.reverse(); let out = ''; for (const s of parts) { if (s === '//') out = out ? (out.endsWith('/') ? out + '/' : out + '//') : '//'; else out = out ? (out.endsWith('/') ? out + s : out + '/' + s) : '/' + s; } return out || '/'; } catch { return ''; } }`, returnByValue: true }); try { yield focusedSession.send("Runtime.releaseObject", { objectId }); } catch (e) { } const xp = (result == null ? void 0 : result.value) || ""; return typeof xp === "string" && xp ? xp : null; } catch (e) { try { yield focusedSession.send("Runtime.releaseObject", { objectId }); } catch (e2) { } return null; } }))(); if (!leafXPath) return null; let prefix = ""; let cur = focusedFrameId; while (cur) { const parent = (_a = parentByFrame.get(cur)) != null ? _a : null; if (!parent) break; const parentSess = page.getSessionForFrame(parent); try { const { backendNodeId } = yield parentSess.send("DOM.getFrameOwner", { frameId: cur }); if (typeof backendNodeId === "number") { const xp = yield absoluteXPathForBackendNode(parentSess, backendNodeId); if (xp) prefix = prefix ? prefixXPath(prefix, xp) : normalizeXPath(xp); } } catch (e) { } cur = parent; } return prefix ? prefixXPath(prefix, leafXPath) : normalizeXPath(leafXPath); }); } function scrollOffsetsExpr() { return "({sx:(window.scrollX||window.pageXOffset||0),sy:(window.scrollY||window.pageYOffset||0)})"; } function buildAbsoluteXPathFromChain(chain, leafSession, leafBackendNodeId) { return __async(this, null, function* () { let prefix = ""; for (const step of chain) { const xp = yield absoluteXPathForBackendNode( step.parentSession, step.iframeBackendNodeId ); if (!xp) continue; prefix = prefix ? prefixXPath(prefix, xp) : normalizeXPath(xp); } const leaf = yield absoluteXPathForBackendNode( leafSession, leafBackendNodeId ); if (!leaf) return prefix || "/"; return prefix ? prefixXPath(prefix, leaf) : normalizeXPath(leaf); }); } function absoluteXPathForBackendNode(session, backendNodeId) { return __async(this, null, function* () { try { const { object } = yield session.send( "DOM.resolveNode", { backendNodeId } ); const objectId = object == null ? void 0 : object.objectId; if (!objectId) return null; const { result } = yield session.send( "Runtime.callFunctionOn", { objectId, functionDeclaration: `function() { try { const node = this; function sibIndex(n) { let i = 1; const t = n.nodeType+':'+(n.nodeName||'').toLowerCase(); for (let p = n.previousSibling; p; p = p.previousSibling) { const key = p.nodeType+':'+(p.nodeName||'').toLowerCase(); if (key === t) i++; } return i; } function step(n) { if (!n) return ''; if (n.nodeType === Node.DOCUMENT_NODE) return ''; if (n.nodeType === Node.DOCUMENT_FRAGMENT_NODE) return '//'; // ShadowRoot hop if (n.nodeType === Node.TEXT_NODE) return 'text()[' + sibIndex(n) + ']'; if (n.nodeType === Node.COMMENT_NODE) return 'comment()[' + sibIndex(n) + ']'; const tag = (n.nodeName||'').toLowerCase(); const name = tag.includes(':') ? "*[name()='"+tag+"']" : tag; return name + '[' + sibIndex(n) + ']'; } const parts = []; let cur = node; while (cur) { // Insert a marker before stepping out of a ShadowRoot if (cur.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { parts.push('//'); cur = (cur && cur.host) ? cur.host : null; continue; } const s = step(cur); if (s) parts.push(s); cur = cur.parentNode; } parts.reverse(); let out = ''; for (const s of parts) { if (s === '//') out = out ? (out.endsWith('/') ? out + '/' : out + '//') : '//'; else out = out ? (out.endsWith('/') ? out + s : out + '/' + s) : '/' + s; } return out || '/'; } catch { return ''; } }`, returnByValue: true } ); yield session.send("Runtime.releaseObject", { objectId }).catch(() => { }); return typeof (result == null ? void 0 : result.value) === "string" && result.value ? result.value : null; } catch (e) { return null; } }); } function captureHybridSnapshot(page, options) { return __async(this, null, function* () { var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l; const pierce = (_a = options == null ? void 0 : options.pierceShadow) != null ? _a : true; const rootId = page.mainFrameId(); const frameTree = page.asProtocolFrameTree(rootId); const parentByFrame = /* @__PURE__ */ new Map(); (function index(n, parent) { var _a2; parentByFrame.set(n.frame.id, parent); for (const c of (_a2 = n.childFrames) != null ? _a2 : []) index(c, n.frame.id); })(frameTree, null); const frames = page.listAllFrameIds(); const combinedXpathMap = {}; const combinedUrlMap = {}; const perFrameOutlines = []; const perFrameMaps = /* @__PURE__ */ new Map(); const requestedFocus = (_b = options == null ? void 0 : options.focusSelector) == null ? void 0 : _b.trim(); if (requestedFocus) { const logScopeFallback = () => { var _a2; v3Logger({ message: `Unable to narrow scope with selector. Falling back to using full DOM`, level: 1, auxiliary: { arguments: { value: `selector: ${(_a2 = options == null ? void 0 : options.focusSelector) == null ? void 0 : _a2.trim()}`, type: "string" } } }); }; try { let targetFrameId; let tailSelector; let absPrefix2; const looksLikeXPath = /^xpath=/i.test(requestedFocus) || requestedFocus.startsWith("/"); if (looksLikeXPath) { const focus = normalizeXPath(requestedFocus); const hit = yield resolveFocusFrameAndTail( page, focus, parentByFrame, rootId ); targetFrameId = hit.targetFrameId; tailSelector = hit.tailXPath || void 0; absPrefix2 = hit.absPrefix; } else { const cssHit = yield resolveCssFocusFrameAndTail( page, requestedFocus, parentByFrame, rootId ); targetFrameId = cssHit.targetFrameId; tailSelector = cssHit.tailSelector || void 0; absPrefix2 = cssHit.absPrefix; } const owningSess = ownerSession(page, targetFrameId); const parentId = parentByFrame.get(targetFrameId); const sameSessionAsParent = !!parentId && ownerSession(page, parentId) === ownerSession(page, targetFrameId); const { tagNameMap, xpathMap, scrollableMap } = yield domMapsForSession( owningSess, targetFrameId, pierce, (fid, be) => `${page.getOrdinal(fid)}-${be}`, /*attemptOwnerLookup=*/ sameSessionAsParent ); const { outline, urlMap, scopeApplied } = yield a11yForFrame( owningSess, targetFrameId, { focusSelector: tailSelector || void 0, tagNameMap, experimental: (_c = options == null ? void 0 : options.experimental) != null ? _c : false, scrollableMap, encode: (backendNodeId) => `${page.getOrdinal(targetFrameId)}-${backendNodeId}` } ); const combinedXpathMap2 = {}; const abs = absPrefix2 != null ? absPrefix2 : ""; const isRoot = !abs || abs === "/"; if (isRoot) { Object.assign(combinedXpathMap2, xpathMap); } else { for (const [encId, xp] of Object.entries(xpathMap)) { combinedXpathMap2[encId] = prefixXPath(abs, xp); } } const combinedUrlMap2 = __spreadValues({}, urlMap); const snapshot = { combinedTree: outline, combinedXpathMap: combinedXpathMap2, combinedUrlMap: combinedUrlMap2, perFrame: [ { frameId: targetFrameId, outline, xpathMap, urlMap } ] }; if (scopeApplied) { return snapshot; } logScopeFallback(); } catch (e) { logScopeFallback(); } } function buildSessionDomIndex(session, pierce2) { return __async(this, null, function* () { var _a2, _b2; yield session.send("DOM.enable").catch(() => { }); const { root } = yield session.send( "DOM.getDocument", { depth: -1, pierce: pierce2 } ); const absByBe = /* @__PURE__ */ new Map(); const tagByBe = /* @__PURE__ */ new Map(); const scrollByBe = /* @__PURE__ */ new Map(); const docRootOf = /* @__PURE__ */ new Map(); const contentDocRootByIframe = /* @__PURE__ */ new Map(); const rootBe = root.backendNodeId; const stack = [{ node: root, xp: "/", docRootBe: rootBe }]; while (stack.length) { const { node, xp, docRootBe } = stack.pop(); if (node.backendNodeId) { absByBe.set(node.backendNodeId, xp || "/"); tagByBe.set(node.backendNodeId, String(node.nodeName).toLowerCase()); if ((node == null ? void 0 : node.isScrollable) === true) scrollByBe.set(node.backendNodeId, true); docRootOf.set(node.backendNodeId, docRootBe); } const kids = (_a2 = node.children) != null ? _a2 : []; if (kids.length) { const segs = buildChildXPathSegments(kids); for (let i = kids.length - 1; i >= 0; i--) { const child = kids[i]; const step = segs[i]; stack.push({ node: child, xp: joinXPath(xp, step), docRootBe }); } } for (const sr of (_b2 = node.shadowRoots) != null ? _b2 : []) { stack.push({ node: sr, xp: joinXPath(xp, "//"), docRootBe }); } const cd = node.contentDocument; if (cd && typeof cd.backendNodeId === "number") { contentDocRootByIframe.set(node.backendNodeId, cd.backendNodeId); stack.push({ node: cd, xp, docRootBe: cd.backendNodeId }); } } return { rootBackend: rootBe, absByBe, tagByBe, scrollByBe, docRootOf, contentDocRootByIframe }; }); } function relativizeXPath(baseAbs, nodeAbs) { const base = normalizeXPath(baseAbs); const abs = normalizeXPath(nodeAbs); if (abs === base) return "/"; if (abs.startsWith(base)) { const tail = abs.slice(base.length); if (!tail) return "/"; return tail.startsWith("/") || tail.startsWith("//") ? tail : `/${tail}`; } if (base === "/") return abs; return abs; } const sessionToIndex = /* @__PURE__ */ new Map(); const sessionById = /* @__PURE__ */ new Map(); for (const frameId of frames) { const sess = ownerSession(page, frameId); const sid = (_d = sess.id) != null ? _d : "root"; if (!sessionById.has(sid)) sessionById.set(sid, sess); } for (const [sid, sess] of sessionById.entries()) { const idx = yield buildSessionDomIndex(sess, pierce); sessionToIndex.set(sid, idx); } for (const frameId of frames) { const sess = ownerSession(page, frameId); const sid = (_e = sess.id) != null ? _e : "root"; let idx = sessionToIndex.get(sid); if (!idx) { idx = yield buildSessionDomIndex(sess, pierce); sessionToIndex.set(sid, idx); } const parentId = parentByFrame.get(frameId); const sameSessionAsParent = !!parentId && ownerSession(page, parentId) === sess; let docRootBe = idx.rootBackend; if (sameSessionAsParent) { try { const { backendNodeId } = yield sess.send( "DOM.getFrameOwner", { frameId } ); if (typeof backendNodeId === "number") { const cdBe = idx.contentDocRootByIframe.get(backendNodeId); if (typeof cdBe === "number") docRootBe = cdBe; } } catch (e) { } } const tagNameMap = {}; const xpathMap = {}; const scrollableMap = {}; const enc = (be) => `${page.getOrdinal(frameId)}-${be}`; const baseAbs = (_f = idx.absByBe.get(docRootBe)) != null ? _f : "/"; for (const [be, nodeAbs] of idx.absByBe.entries()) { const nodeDocRoot = idx.docRootOf.get(be); if (nodeDocRoot !== docRootBe) continue; const rel = relativizeXPath(baseAbs, nodeAbs); const key = enc(be); xpathMap[key] = rel; const tag = idx.tagByBe.get(be); if (tag) tagNameMap[key] = tag; if (idx.scrollByBe.get(be)) scrollableMap[key] = true; } const { outline, urlMap } = yield a11yForFrame(sess, frameId, { experimental: (_g = options == null ? void 0 : options.experimental) != null ? _g : false, tagNameMap, scrollableMap, encode: (backendNodeId) => `${page.getOrdinal(frameId)}-${backendNodeId}` }); perFrameOutlines.push({ frameId, outline }); perFrameMaps.set(frameId, { tagNameMap, xpathMap, scrollableMap, urlMap }); } const absPrefix = /* @__PURE__ */ new Map(); const iframeHostEncByChild = /* @__PURE__ */ new Map(); absPrefix.set(rootId, ""); const queue = [rootId]; while (queue.length) { const parent = queue.shift(); const parentAbs = absPrefix.get(parent); for (const child of frames) { if (parentByFrame.get(child) !== parent) continue; queue.push(child); const parentSess = parentSession(page, parentByFrame, child); const ownerBackendNodeId = yield (() => __async(null, null, function* () { try { const { backendNodeId } = yield parentSess.send("DOM.getFrameOwner", { frameId: child }); return backendNodeId; } catch (e) { return void 0; } }))(); if (!ownerBackendNodeId) { absPrefix.set(child, parentAbs); continue; } const parentDom = perFrameMaps.get(parent); const iframeEnc = `${page.getOrdinal(parent)}-${ownerBackendNodeId}`; const iframeXPath = parentDom == null ? void 0 : parentDom.xpathMap[iframeEnc]; const childAbs = iframeXPath ? prefixXPath(parentAbs || "/", iframeXPath) : parentAbs; absPrefix.set(child, childAbs); iframeHostEncByChild.set(child, iframeEnc); } } for (const frameId of frames) { const maps = perFrameMaps.get(frameId); if (!maps) continue; const abs = (_h = absPrefix.get(frameId)) != null ? _h : ""; const isRoot = abs === "" || abs === "/"; if (isRoot) { Object.assign(combinedXpathMap, maps.xpathMap); Object.assign(combinedUrlMap, maps.urlMap); continue; } for (const [encId, xp] of Object.entries(maps.xpathMap)) { combinedXpathMap[encId] = prefixXPath(abs, xp); } Object.assign(combinedUrlMap, maps.urlMap); } const idToTree = /* @__PURE__ */ new Map(); for (const { frameId, outline } of perFrameOutlines) { const parentEnc = iframeHostEncByChild.get(frameId); if (parentEnc) idToTree.set(parentEnc, outline); } const rootOutline = (_l = (_k = (_i = perFrameOutlines.find((o) => o.frameId === rootId)) == null ? void 0 : _i.outline) != null ? _k : (_j = perFrameOutlines[0]) == null ? void 0 : _j.outline) != null ? _l : ""; const combinedTree = injectSubtrees(rootOutline, idToTree); return { combinedTree, combinedXpathMap, combinedUrlMap, perFrame: perFrameOutlines.map(({ frameId, outline }) => { var _a2, _b2; const maps = perFrameMaps.get(frameId); return { frameId, outline, xpathMap: (_a2 = maps == null ? void 0 : maps.xpathMap) != null ? _a2 : {}, urlMap: (_b2 = maps == null ? void 0 : maps.urlMap) != null ? _b2 : {} }; }) }; }); } function prefixXPath(parentAbs, child) { const p = parentAbs === "/" ? "" : parentAbs.replace(/\/$/, ""); if (!child || child === "/") return p || "/"; if (child.startsWith("//")) return p ? `${p}//${child.slice(2)}` : `//${child.slice(2)}`; const c = child.replace(/^\//, ""); return p ? `${p}/${c}` : `/${c}`; } function normalizeXPath(x) { if (!x) return ""; let s = x.trim().replace(/^xpath=/i, ""); if (!s.startsWith("/")) s = "/" + s; if (s.length > 1 && s.endsWith("/")) s = s.slice(0, -1); return s; } function parseXPathToSteps(path6) { const s = path6.trim(); let i = 0; const steps = []; while (i < s.length) { let axis = "child"; if (s.startsWith("//", i)) { axis = "desc"; i += 2; } else if (s[i] === "/") { axis = "child"; i += 1; } const start = i; while (i < s.length && s[i] !== "/") i++; const raw = s.slice(start, i).trim(); if (!raw) continue; const name = raw.replace(/\[\d+\]\s*$/u, "").toLowerCase(); steps.push({ axis, raw, name }); } return steps; } function buildXPathFromSteps(steps) { let out = ""; for (const st of steps) { out += st.axis === "desc" ? "//" : "/"; out += st.raw; } return out || "/"; } function resolveFocusFrameAndTail(page, absoluteXPath, parentByFrame, rootId) { return __async(this, null, function* () { const steps = parseXPathToSteps(absoluteXPath); let ctxFrameId = rootId; let buf = []; let absPrefix = ""; const flushIntoChild = () => __async(null, null, function* () { if (!buf.length) return; const selectorForIframe = buildXPathFromSteps(buf); const parentSess = page.getSessionForFrame(ctxFrameId); const objectId = yield resolveObjectIdForXPath( parentSess, selectorForIframe, ctxFrameId ); if (!objectId) throw new Error("Failed to resolve iframe element by XPath"); try { yield parentSess.send("DOM.enable").catch(() => { }); const desc = yield parentSess.send( "DOM.describeNode", { objectId } ); const iframeBackendNodeId = desc.node.backendNodeId; let childFrameId; for (const fid of listChildrenOf(parentByFrame, ctxFrameId)) { try { const { backendNodeId } = yield parentSess.send("DOM.getFrameOwner", { frameId: fid }); if (backendNodeId === iframeBackendNodeId) { childFrameId = fid; break; } } catch (e) { } } if (!childFrameId) throw new Error("Could not map iframe to child frameId"); absPrefix = prefixXPath(absPrefix || "/", selectorForIframe); ctxFrameId = childFrameId; } finally { yield parentSess.send("Runtime.releaseObject", { objectId }).catch(() => { }); } buf = []; }); for (const st of steps) { buf.push(st); if (IFRAME_STEP_RE.test(st.name)) { yield flushIntoChild(); } } const tailXPath = buildXPathFromSteps(buf); return { targetFrameId: ctxFrameId, tailXPath, absPrefix }; }); } function resolveCssFocusFrameAndTail(page, rawSelector, parentByFrame, rootId) { return __async(this, null, function* () { var _a; const parts = rawSelector.split(">>").map((s) => s.trim()).filter(Boolean); let ctxFrameId = rootId; const absPrefix = ""; for (let i = 0; i < Math.max(0, parts.length - 1); i++) { const parentSess = page.getSessionForFrame(ctxFrameId); const objectId = yield resolveObjectIdForCss( parentSess, parts[i], ctxFrameId ); if (!objectId) throw new Error("Failed to resolve iframe via CSS hop"); try { yield parentSess.send("DOM.enable").catch(() => { }); const desc = yield parentSess.send( "DOM.describeNode", { objectId } ); const iframeBackendNodeId = desc.node.backendNodeId; let childFrameId; for (const fid of listChildrenOf(parentByFrame, ctxFrameId)) { try { const { backendNodeId } = yield parentSess.send("DOM.getFrameOwner", { frameId: fid }); if (backendNodeId === iframeBackendNodeId) { childFrameId = fid; break; } } catch (e) { } } if (!childFrameId) throw new Error("Could not map CSS iframe hop to child frameId"); ctxFrameId = childFrameId; } finally { yield parentSess.send("Runtime.releaseObject", { objectId }).catch(() => { }); } } const tailSelector = (_a = parts[parts.length - 1]) != null ? _a : "*"; return { targetFrameId: ctxFrameId, tailSelector, absPrefix }; }); } function listChildrenOf(parentByFrame, parentId) { const out = []; for (const [fid, p] of parentByFrame.entries()) { if (p === parentId) out.push(fid); } return out; } function domMapsForSession(session, frameId, pierce, encode = (fid, be) => `${fid}-${be}`, attemptOwnerLookup = true) { return __async(this, null, function* () { var _a, _b; yield session.send("DOM.enable").catch(() => { }); const { root } = yield session.send( "DOM.getDocument", { depth: -1, pierce } ); let startNode = root; if (attemptOwnerLookup) { try { const owner = yield session.send( "DOM.getFrameOwner", { frameId } ); const ownerBackendId = owner.backendNodeId; if (typeof ownerBackendId === "number") { const ownerEl = findNodeByBackendId(root, ownerBackendId); if (ownerEl == null ? void 0 : ownerEl.contentDocument) { startNode = ownerEl.contentDocument; } } } catch (e) { } } const tagNameMap = {}; const xpathMap = {}; const scrollableMap = {}; const stack = [{ node: startNode, xpath: "" }]; while (stack.length) { const { node, xpath } = stack.pop(); if (node.backendNodeId) { const encId = encode(frameId, node.backendNodeId); tagNameMap[encId] = String(node.nodeName).toLowerCase(); xpathMap[encId] = xpath || "/"; const isScrollable = (node == null ? void 0 : node.isScrollable) === true; if (isScrollable) scrollableMap[encId] = true; } const kids = (_a = node.children) != null ? _a : []; if (kids.length) { const segs = buildChildXPathSegments(kids); for (let i = kids.length - 1; i >= 0; i--) { const child = kids[i]; const step = segs[i]; stack.push({ node: child, xpath: joinXPath(xpath, step) }); } } for (const sr of (_b = node.shadowRoots) != null ? _b : []) { stack.push({ node: sr, xpath: joinXPath(xpath, "//") }); } } return { tagNameMap, xpathMap, scrollableMap }; }); } function buildChildXPathSegments(kids) { var _a; const segs = []; const ctr = {}; for (const child of kids) { const tag = String(child.nodeName).toLowerCase(); const key = `${child.nodeType}:${tag}`; const idx = ctr[key] = ((_a = ctr[key]) != null ? _a : 0) + 1; if (child.nodeType === 3) { segs.push(`text()[${idx}]`); } else if (child.nodeType === 8) { segs.push(`comment()[${idx}]`); } else { segs.push( tag.includes(":") ? `*[name()='${tag}'][${idx}]` : `${tag}[${idx}]` ); } } return segs; } function joinXPath(base, step) { if (step === "//") { if (!base || base === "/") return "//"; return base.endsWith("/") ? `${base}/` : `${base}//`; } if (!base || base === "/") return step ? `/${step}` : "/"; if (base.endsWith("//")) return `${base}${step}`; if (!step) return base; return `${base}/${step}`; } function a11yForFrame(session, frameId, opts) { return __async(this, null, function* () { var _a, _b; yield session.send("Accessibility.enable").catch(() => { }); yield session.send("Runtime.enable").catch(() => { }); yield session.send("DOM.enable").catch(() => { }); let nodes = []; try { const params = frameId ? { frameId } : {}; ({ nodes } = yield session.send("Accessibility.getFullAXTree", params)); } catch (e) { const msg = String((_b = (_a = e == null ? void 0 : e.message) != null ? _a : e) != null ? _b : ""); const isFrameScopeError = msg.includes("Frame with the given") || msg.includes("does not belong to the target") || msg.includes("is not found"); if (!isFrameScopeError || !frameId) throw e; ({ nodes } = yield session.send("Accessibility.getFullAXTree")); } const urlMap = {}; for (const n of nodes) { const be = n.backendDOMNodeId; if (typeof be !== "number") continue; const url = extractUrlFromAXNode(n); if (!url) continue; const enc = opts.encode(be); urlMap[enc] = url; } let scopeApplied = false; const nodesForOutline = yield (() => __async(null, null, function* () { var _a2, _b2, _c; const sel = (_a2 = opts.focusSelector) == null ? void 0 : _a2.trim(); if (!sel) return nodes; try { const looksLikeXPath = /^xpath=/i.test(sel) || sel.startsWith("/"); const objectId = looksLikeXPath ? yield resolveObjectIdForXPath(session, sel, frameId) : yield resolveObjectIdForCss(session, sel, frameId); if (!objectId) return nodes; const desc = yield session.send( "DOM.describeNode", { objectId } ); const be = (_b2 = desc.node) == null ? void 0 : _b2.backendNodeId; if (typeof be !== "number") return nodes; const target = nodes.find((n) => n.backendDOMNodeId === be); if (!target) return nodes; scopeApplied = true; const keep = /* @__PURE__ */ new Set([target.nodeId]); const queue = [target]; while (queue.length) { const cur = queue.shift(); for (const id of (_c = cur.childIds) != null ? _c : []) { if (keep.has(id)) continue; keep.add(id); const child = nodes.find((n) => n.nodeId === id); if (child) queue.push(child); } } return nodes.filter((n) => keep.has(n.nodeId)).map( (n) => n.nodeId === target.nodeId ? __spreadProps(__spreadValues({}, n), { parentId: void 0 }) : n ); } catch (e) { return nodes; } }))(); const decorated = decorateRoles(nodesForOutline, opts); const { tree } = yield buildHierarchicalTree(decorated, opts); const simplified = tree.map((n) => formatTreeLine(n)).join("\n"); return { outline: simplified.trimEnd(), urlMap, scopeApplied }; }); } function resolveObjectIdForXPath(session, xpath, frameId) { return __async(this, null, function* () { var _a; let contextId; try { if (frameId) { contextId = yield executionContexts.waitForMainWorld(session, frameId, 800).catch( () => { var _a2; return (_a2 = executionContexts.getMainWorld(session, frameId)) != null ? _a2 : void 0; } ); } } catch (e) { contextId = void 0; } const expr = `(() => { const xp = ${JSON.stringify(xpath)}; try { if (window.__stagehandV3__ && typeof window.__stagehandV3__.resolveSimpleXPath === 'function') { return window.__stagehandV3__.resolveSimpleXPath(xp); } } catch {} try { const res = document.evaluate(xp, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null); return res.singleNodeValue; } catch { return null; } })()`; const { result, exceptionDetails } = yield session.send("Runtime.evaluate", { expression: expr, returnByValue: false, contextId, awaitPromise: true }); if (exceptionDetails) return null; return (_a = result == null ? void 0 : result.objectId) != null ? _a : null; }); } function resolveObjectIdForCss(session, selector, frameId) { return __async(this, null, function* () { var _a; let contextId; try { if (frameId) { contextId = yield executionContexts.waitForMainWorld(session, frameId, 800).catch( () => { var _a2; return (_a2 = executionContexts.getMainWorld(session, frameId)) != null ? _a2 : void 0; } ); } } catch (e) { contextId = void 0; } const expr = `(() => { const selector = ${JSON.stringify(selector)}; function queryOpenDeep(root) { try { const hit = root.querySelector(selector); if (hit) return hit; } catch {} const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT); let n; while ((n = walker.nextNode())) { if (n.shadowRoot) { const found = queryOpenDeep(n.shadowRoot); if (found) return found; } } return null; } const backdoor = window.__stagehandV3__; if (backdoor && typeof backdoor.getClosedRoot === 'function') { function* roots() { yield document; const queue = []; try { const w = document.createTreeWalker(document, NodeFilter.SHOW_ELEMENT); let e; while ((e = w.nextNode())) { if (e.shadowRoot) queue.push(e.shadowRoot); try { const closed = backdoor.getClosedRoot(e); if (closed) queue.push(closed); } catch {} } } catch {} while (queue.length) { const r = queue.shift(); yield r; try { const w2 = document.createTreeWalker(r, NodeFilter.SHOW_ELEMENT); let e2; while ((e2 = w2.nextNode())) { if (e2.shadowRoot) queue.push(e2.shadowRoot); try { const closed2 = backdoor.getClosedRoot(e2); if (closed2) queue.push(closed2); } catch {} } } catch {} } } for (const r of roots()) { try { const hit = r.querySelector(selector); if (hit) return hit; } catch {} } return null; } return queryOpenDeep(document); })()`; const { result, exceptionDetails } = yield session.send("Runtime.evaluate", { expression: expr, returnByValue: false, contextId, awaitPromise: true }); if (exceptionDetails) return null; return (_a = result == null ? void 0 : result.objectId) != null ? _a : null; }); } function decorateRoles(nodes, opts) { const asRole = (n) => { var _a, _b; return String((_b = (_a = n.role) == null ? void 0 : _a.value) != null ? _b : ""); }; return nodes.map((n) => { var _a, _b, _c; let encodedId; if (typeof n.backendDOMNodeId === "number") { try { encodedId = opts.encode(n.backendDOMNodeId); } catch (e) { } } let role = asRole(n); const domIsScrollable = encodedId ? opts.scrollableMap[encodedId] === true : false; const tag = encodedId ? opts.tagNameMap[encodedId] : void 0; const isHtmlElement = tag === "html"; if ((domIsScrollable || isHtmlElement) && tag !== "#document") { const tagLabel = tag && tag.startsWith("#") ? tag.slice(1) : tag; role = tagLabel ? `scrollable, ${tagLabel}` : `scrollable${role ? `, ${role}` : ""}`; } return { role, name: (_a = n.name) == null ? void 0 : _a.value, description: (_b = n.description) == null ? void 0 : _b.value, value: (_c = n.value) == null ? void 0 : _c.value, nodeId: n.nodeId, backendDOMNodeId: n.backendDOMNodeId, parentId: n.paren