@enricai/barnacle 1.6.7 → 1.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/plugins/config-plugin.d.ts +1 -0
- package/dist/plugins/config-plugin.d.ts.map +1 -1
- package/dist/plugins/config-plugin.js +2 -0
- package/dist/plugins/config-plugin.js.map +1 -1
- package/dist/scraper/behavioral-signals.d.ts +10 -3
- package/dist/scraper/behavioral-signals.d.ts.map +1 -1
- package/dist/scraper/behavioral-signals.js +17 -6
- package/dist/scraper/behavioral-signals.js.map +1 -1
- package/dist/scraper/deep-query.d.ts +8 -0
- package/dist/scraper/deep-query.d.ts.map +1 -1
- package/dist/scraper/deep-query.js +9 -1
- package/dist/scraper/deep-query.js.map +1 -1
- package/dist/scraper/flow-runner.d.ts +156 -24
- package/dist/scraper/flow-runner.d.ts.map +1 -1
- package/dist/scraper/flow-runner.js +246 -134
- package/dist/scraper/flow-runner.js.map +1 -1
- package/dist/scraper/frame-target.d.ts +98 -0
- package/dist/scraper/frame-target.d.ts.map +1 -0
- package/dist/scraper/frame-target.js +212 -0
- package/dist/scraper/frame-target.js.map +1 -0
- package/dist/scraper/stagehand-guard.d.ts +31 -3
- package/dist/scraper/stagehand-guard.d.ts.map +1 -1
- package/dist/scraper/stagehand-guard.js +53 -7
- package/dist/scraper/stagehand-guard.js.map +1 -1
- package/dist/scraper/submit-control.d.ts +15 -2
- package/dist/scraper/submit-control.d.ts.map +1 -1
- package/dist/scraper/submit-control.js +17 -4
- package/dist/scraper/submit-control.js.map +1 -1
- package/dist/scripts/recon-browser.d.ts +95 -1
- package/dist/scripts/recon-browser.d.ts.map +1 -1
- package/dist/scripts/recon-browser.js +54 -10
- package/dist/scripts/recon-browser.js.map +1 -1
- package/dist/scripts/recon-generate.d.ts +6 -0
- package/dist/scripts/recon-generate.d.ts.map +1 -1
- package/dist/scripts/recon-generate.js +9 -7
- package/dist/scripts/recon-generate.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-origin frame resolver: the single seam that lets every downstream
|
|
3
|
+
* helper (flow-runner primitives, guarded Stagehand calls) act on either the
|
|
4
|
+
* main frame or a resolved cross-origin OOPIF without knowing which. Some
|
|
5
|
+
* ATS integrations (e.g. UCHealth's Talemetry wizard) embed their entire
|
|
6
|
+
* application form inside a cross-origin `<iframe>` rather than navigating
|
|
7
|
+
* the top window to it, and `document`-rooted helpers (`page.evaluate`,
|
|
8
|
+
* `page.locator`) cannot reach across that boundary — `contentDocument` on a
|
|
9
|
+
* cross-origin iframe element is `null` from page script's perspective.
|
|
10
|
+
* `resolveFrameTarget` finds the child `Frame` Stagehand's CDP layer already
|
|
11
|
+
* attached to and wraps it in a uniform `FrameTarget` surface.
|
|
12
|
+
*/
|
|
13
|
+
import type { Page } from "@browserbasehq/stagehand";
|
|
14
|
+
/** The frame handle type `Page.frames()` returns, without a deep import into Stagehand's understudy internals. */
|
|
15
|
+
type StagehandFrame = ReturnType<Page["frames"]>[number];
|
|
16
|
+
/**
|
|
17
|
+
* Uniform evaluate/locator/url/title surface bound to either the main frame
|
|
18
|
+
* (`frame: null`) or a resolved cross-origin child frame. `frameSelector` is
|
|
19
|
+
* the CSS selector of the scoped frame (or `null` for main), carried so
|
|
20
|
+
* callers can pass it straight through to `ObserveOptions.selector` /
|
|
21
|
+
* `ExtractOptions.selector` and scope Stagehand's own observe/extract calls
|
|
22
|
+
* to the same frame this target evaluates and locates against.
|
|
23
|
+
*/
|
|
24
|
+
export interface FrameTarget {
|
|
25
|
+
/** The resolved child frame, or `null` when this target is bound to the main frame. */
|
|
26
|
+
readonly frame: StagehandFrame | null;
|
|
27
|
+
/** CSS selector for the Stagehand scope hint (`ObserveOptions.selector` / `ExtractOptions.selector`), or `null` for the main frame. */
|
|
28
|
+
readonly frameSelector: string | null;
|
|
29
|
+
/** Evaluate a function or expression against the resolved frame (or the main frame when unresolved). */
|
|
30
|
+
evaluate<R = unknown, Arg = unknown>(pageFunctionOrExpression: string | ((arg: Arg) => R | Promise<R>), arg?: Arg): Promise<R>;
|
|
31
|
+
/** Build a Locator scoped to the resolved frame (or the main frame when unresolved). */
|
|
32
|
+
locator(selector: string): ReturnType<Page["locator"]>;
|
|
33
|
+
/** Current URL of the resolved frame (or the main frame when unresolved). */
|
|
34
|
+
url(): Promise<string>;
|
|
35
|
+
/** Current document title. Cross-origin child frames have no accessible `document.title` distinct from the top document via CDP `Page.title`, so this always reads the top document's title. */
|
|
36
|
+
title(): Promise<string>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Builds the main-frame `FrameTarget`: every method delegates straight to
|
|
40
|
+
* `Page`, matching today's behavior for every site whose ATS form never
|
|
41
|
+
* leaves the top window. Exported so call sites that have not yet resolved
|
|
42
|
+
* a `FrameTarget` from `deps.frameSelector` can still pass a target-shaped
|
|
43
|
+
* value to helpers that now require one.
|
|
44
|
+
*/
|
|
45
|
+
export declare function mainFrameTarget(page: Page): FrameTarget;
|
|
46
|
+
/**
|
|
47
|
+
* Resolves the `FrameTarget` for `frameSelector` against `page`, polling for
|
|
48
|
+
* up to `FRAME_READY_TIMEOUT_MS` (at `FRAME_READY_POLL_MS` intervals) before
|
|
49
|
+
* falling back to the main-frame target rather than throwing:
|
|
50
|
+
*
|
|
51
|
+
* 1. `frameSelector` is `null`/`undefined` → main-frame target (today's
|
|
52
|
+
* behavior, unchanged) — zero polling, zero delay.
|
|
53
|
+
* 2. Each poll: no element in the main document matches `frameSelector`, or
|
|
54
|
+
* it isn't an `<iframe>`, or its `src` can't be read and more than one
|
|
55
|
+
* `page.frames()` candidate exists (identity match is ambiguous), or no
|
|
56
|
+
* `page.frames()` entry has a matching origin yet → try again after
|
|
57
|
+
* `FRAME_READY_POLL_MS`.
|
|
58
|
+
* 3. A poll finds a matching frame → a child-frame target bound to it,
|
|
59
|
+
* however many polls it took (an iframe created mid-flow by an earlier
|
|
60
|
+
* step, e.g. after a click reveals it, resolves as soon as Stagehand's
|
|
61
|
+
* CDP layer attaches to it instead of only when present at the first
|
|
62
|
+
* poll).
|
|
63
|
+
* 4. Still unresolved once the deadline passes → main-frame target, with a
|
|
64
|
+
* `warn` naming the selector so a silent revert-to-main-frame is
|
|
65
|
+
* diagnosable from the log instead of invisible.
|
|
66
|
+
*
|
|
67
|
+
* `opts` overrides the poll timing for tests; production call sites rely on
|
|
68
|
+
* the `FRAME_READY_TIMEOUT_MS`/`FRAME_READY_POLL_MS` defaults.
|
|
69
|
+
*/
|
|
70
|
+
export declare function resolveFrameTarget(page: Page, frameSelector?: string | null, opts?: {
|
|
71
|
+
timeoutMs?: number;
|
|
72
|
+
pollMs?: number;
|
|
73
|
+
}): Promise<FrameTarget>;
|
|
74
|
+
/**
|
|
75
|
+
* Composes a Stagehand hop-notation scope string from a frame selector and an
|
|
76
|
+
* inner selector, for callers building `ObserveOptions.selector` /
|
|
77
|
+
* `ExtractOptions.selector` values — kept separate from `FrameTarget` itself
|
|
78
|
+
* so `resolveFrameTarget` keeps receiving only the bare iframe-id hop (the
|
|
79
|
+
* contract `frame-resolve.test.ts` pins) rather than a pre-composed string.
|
|
80
|
+
*/
|
|
81
|
+
export declare function buildHopSelector(frameSelector: string | null | undefined, innerSelector: string): string;
|
|
82
|
+
/** Shared delay helper — `FrameTarget` has no `waitForTimeout` since it isn't frame-scoped. */
|
|
83
|
+
export declare function sleep(ms: number): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Blocks until a resolved child frame has a live document (`document.readyState`
|
|
86
|
+
* is `"interactive"` or `"complete"`), so callers do not observe/act against a
|
|
87
|
+
* frame that CDP has attached to but that has not yet navigated past `about:blank`
|
|
88
|
+
* — the state right after `Target.setAutoAttach` fires and before the OOPIF's own
|
|
89
|
+
* navigation lands. Best-effort like `waitForSpaReady`: never throws, just resolves
|
|
90
|
+
* once ready or once `timeoutMs` elapses, so a frame that never becomes ready
|
|
91
|
+
* degrades to "proceed anyway" rather than hanging the flow.
|
|
92
|
+
*/
|
|
93
|
+
export declare function waitForChildFrameReady(target: FrameTarget, opts?: {
|
|
94
|
+
timeoutMs?: number;
|
|
95
|
+
pollMs?: number;
|
|
96
|
+
}): Promise<void>;
|
|
97
|
+
export {};
|
|
98
|
+
//# sourceMappingURL=frame-target.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-target.d.ts","sourceRoot":"","sources":["../../src/scraper/frame-target.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAMrD,kHAAkH;AAClH,KAAK,cAAc,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,uFAAuF;IACvF,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,uIAAuI;IACvI,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,wGAAwG;IACxG,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,GAAG,OAAO,EACjC,wBAAwB,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,EACjE,GAAG,CAAC,EAAE,GAAG,GACR,OAAO,CAAC,CAAC,CAAC,CAAC;IACd,wFAAwF;IACxF,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IACvD,6EAA6E;IAC7E,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACvB,gMAAgM;IAChM,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,WAAW,CASvD;AA0FD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,IAAI,EACV,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,EAC7B,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GACjD,OAAO,CAAC,WAAW,CAAC,CAoBtB;AAID;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,aAAa,EAAE,MAAM,GACpB,MAAM,CAOR;AAMD,+FAA+F;AAC/F,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,WAAW,EACnB,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GACjD,OAAO,CAAC,IAAI,CAAC,CAqBf"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cross-origin frame resolver: the single seam that lets every downstream
|
|
4
|
+
* helper (flow-runner primitives, guarded Stagehand calls) act on either the
|
|
5
|
+
* main frame or a resolved cross-origin OOPIF without knowing which. Some
|
|
6
|
+
* ATS integrations (e.g. UCHealth's Talemetry wizard) embed their entire
|
|
7
|
+
* application form inside a cross-origin `<iframe>` rather than navigating
|
|
8
|
+
* the top window to it, and `document`-rooted helpers (`page.evaluate`,
|
|
9
|
+
* `page.locator`) cannot reach across that boundary — `contentDocument` on a
|
|
10
|
+
* cross-origin iframe element is `null` from page script's perspective.
|
|
11
|
+
* `resolveFrameTarget` finds the child `Frame` Stagehand's CDP layer already
|
|
12
|
+
* attached to and wraps it in a uniform `FrameTarget` surface.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.mainFrameTarget = mainFrameTarget;
|
|
16
|
+
exports.resolveFrameTarget = resolveFrameTarget;
|
|
17
|
+
exports.buildHopSelector = buildHopSelector;
|
|
18
|
+
exports.sleep = sleep;
|
|
19
|
+
exports.waitForChildFrameReady = waitForChildFrameReady;
|
|
20
|
+
const logging_1 = require("../lib/logging");
|
|
21
|
+
const logger = (0, logging_1.getLogger)({ name: "scraper/frame-target" });
|
|
22
|
+
/**
|
|
23
|
+
* Builds the main-frame `FrameTarget`: every method delegates straight to
|
|
24
|
+
* `Page`, matching today's behavior for every site whose ATS form never
|
|
25
|
+
* leaves the top window. Exported so call sites that have not yet resolved
|
|
26
|
+
* a `FrameTarget` from `deps.frameSelector` can still pass a target-shaped
|
|
27
|
+
* value to helpers that now require one.
|
|
28
|
+
*/
|
|
29
|
+
function mainFrameTarget(page) {
|
|
30
|
+
return {
|
|
31
|
+
frame: null,
|
|
32
|
+
frameSelector: null,
|
|
33
|
+
evaluate: (pageFunctionOrExpression, arg) => page.evaluate(pageFunctionOrExpression, arg),
|
|
34
|
+
locator: (selector) => page.locator(selector),
|
|
35
|
+
url: () => Promise.resolve(page.url()),
|
|
36
|
+
title: () => page.title(),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Builds a child-frame `FrameTarget`: `evaluate`/`locator` delegate to the
|
|
41
|
+
* resolved `Frame` (reaching across the cross-origin boundary via its own
|
|
42
|
+
* CDP session), while `url`/`title` fall back to the frame's own
|
|
43
|
+
* `location.href` and the top document's title respectively — `Frame` has
|
|
44
|
+
* no `title()` of its own.
|
|
45
|
+
*/
|
|
46
|
+
function childFrameTarget(page, frame, frameSelector) {
|
|
47
|
+
return {
|
|
48
|
+
frame,
|
|
49
|
+
frameSelector,
|
|
50
|
+
evaluate: (pageFunctionOrExpression, arg) => frame.evaluate(pageFunctionOrExpression, arg),
|
|
51
|
+
locator: (selector) => frame.locator(selector),
|
|
52
|
+
url: () => frame.evaluate("location.href"),
|
|
53
|
+
title: () => page.title(),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Reads the origin (scheme + host) off a URL string, or `null` if it isn't
|
|
58
|
+
* parseable — used to match a candidate `page.frames()` entry against the
|
|
59
|
+
* `<iframe>` element's `src` attribute without requiring an exact URL match
|
|
60
|
+
* (the iframe `src` and the frame's live `location.href` commonly differ by
|
|
61
|
+
* path/query after the child navigates, e.g. an application UUID appended
|
|
62
|
+
* post-load).
|
|
63
|
+
*/
|
|
64
|
+
function originOf(url) {
|
|
65
|
+
try {
|
|
66
|
+
return new URL(url).origin;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Attempts one resolution pass: reads the `<iframe>` element's `src` (not
|
|
74
|
+
* `contentDocument`, which stays readable across the cross-origin boundary —
|
|
75
|
+
* only same-origin script access to the child's document is blocked), then
|
|
76
|
+
* looks for a `page.frames()` entry whose origin matches it. Returns `null`
|
|
77
|
+
* rather than a target so the caller can distinguish "not yet attached, keep
|
|
78
|
+
* polling" from a resolved target.
|
|
79
|
+
*
|
|
80
|
+
* The `src` attribute read can lose a same-tick race against the widget
|
|
81
|
+
* script that constructs the `<iframe>`: some ATS integrations (e.g.
|
|
82
|
+
* UCHealth's Talemetry wizard) assign `src` as a JS property immediately
|
|
83
|
+
* before `appendChild`, so a poll can observe the element already in the
|
|
84
|
+
* DOM with `src` still empty or not yet reflected to the attribute. Giving
|
|
85
|
+
* up in that case would depend on same-tick attribute reflection that isn't
|
|
86
|
+
* guaranteed. Instead, when the element is confirmed to be the matching
|
|
87
|
+
* `<iframe>` but its `src` can't be read, fall back to matching by element
|
|
88
|
+
* identity: if `page.frames()` has resolved exactly one candidate frame
|
|
89
|
+
* beyond the main frame, that frame must be the one CDP attached to for
|
|
90
|
+
* this iframe, so bind to it directly rather than degrading to the main
|
|
91
|
+
* frame.
|
|
92
|
+
*/
|
|
93
|
+
async function tryResolveChildFrame(page, frameSelector) {
|
|
94
|
+
const iframeSrcExpr = `(() => {
|
|
95
|
+
const el = document.querySelector(${JSON.stringify(frameSelector)});
|
|
96
|
+
if (!el || el.tagName !== "IFRAME") return { matched: false, src: null };
|
|
97
|
+
return { matched: true, src: el.getAttribute("src") };
|
|
98
|
+
})()`;
|
|
99
|
+
const { matched, src: iframeSrc } = await page.evaluate(iframeSrcExpr);
|
|
100
|
+
if (!matched)
|
|
101
|
+
return null;
|
|
102
|
+
const candidates = page.frames();
|
|
103
|
+
const targetOrigin = iframeSrc ? originOf(iframeSrc) : null;
|
|
104
|
+
if (!targetOrigin) {
|
|
105
|
+
const [onlyCandidate] = candidates;
|
|
106
|
+
return candidates.length === 1 && onlyCandidate
|
|
107
|
+
? childFrameTarget(page, onlyCandidate, frameSelector)
|
|
108
|
+
: null;
|
|
109
|
+
}
|
|
110
|
+
for (const candidate of candidates) {
|
|
111
|
+
const candidateUrl = await candidate.evaluate("location.href").catch(() => null);
|
|
112
|
+
if (candidateUrl && originOf(candidateUrl) === targetOrigin) {
|
|
113
|
+
return childFrameTarget(page, candidate, frameSelector);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Resolves the `FrameTarget` for `frameSelector` against `page`, polling for
|
|
120
|
+
* up to `FRAME_READY_TIMEOUT_MS` (at `FRAME_READY_POLL_MS` intervals) before
|
|
121
|
+
* falling back to the main-frame target rather than throwing:
|
|
122
|
+
*
|
|
123
|
+
* 1. `frameSelector` is `null`/`undefined` → main-frame target (today's
|
|
124
|
+
* behavior, unchanged) — zero polling, zero delay.
|
|
125
|
+
* 2. Each poll: no element in the main document matches `frameSelector`, or
|
|
126
|
+
* it isn't an `<iframe>`, or its `src` can't be read and more than one
|
|
127
|
+
* `page.frames()` candidate exists (identity match is ambiguous), or no
|
|
128
|
+
* `page.frames()` entry has a matching origin yet → try again after
|
|
129
|
+
* `FRAME_READY_POLL_MS`.
|
|
130
|
+
* 3. A poll finds a matching frame → a child-frame target bound to it,
|
|
131
|
+
* however many polls it took (an iframe created mid-flow by an earlier
|
|
132
|
+
* step, e.g. after a click reveals it, resolves as soon as Stagehand's
|
|
133
|
+
* CDP layer attaches to it instead of only when present at the first
|
|
134
|
+
* poll).
|
|
135
|
+
* 4. Still unresolved once the deadline passes → main-frame target, with a
|
|
136
|
+
* `warn` naming the selector so a silent revert-to-main-frame is
|
|
137
|
+
* diagnosable from the log instead of invisible.
|
|
138
|
+
*
|
|
139
|
+
* `opts` overrides the poll timing for tests; production call sites rely on
|
|
140
|
+
* the `FRAME_READY_TIMEOUT_MS`/`FRAME_READY_POLL_MS` defaults.
|
|
141
|
+
*/
|
|
142
|
+
async function resolveFrameTarget(page, frameSelector, opts = {}) {
|
|
143
|
+
if (!frameSelector)
|
|
144
|
+
return mainFrameTarget(page);
|
|
145
|
+
const resolved = await tryResolveChildFrame(page, frameSelector);
|
|
146
|
+
if (resolved)
|
|
147
|
+
return resolved;
|
|
148
|
+
const timeoutMs = opts.timeoutMs ?? FRAME_READY_TIMEOUT_MS;
|
|
149
|
+
const pollMs = opts.pollMs ?? FRAME_READY_POLL_MS;
|
|
150
|
+
const deadline = Date.now() + timeoutMs;
|
|
151
|
+
while (Date.now() < deadline) {
|
|
152
|
+
await sleep(pollMs);
|
|
153
|
+
const polled = await tryResolveChildFrame(page, frameSelector);
|
|
154
|
+
if (polled)
|
|
155
|
+
return polled;
|
|
156
|
+
}
|
|
157
|
+
logger.warn(`frame ${frameSelector} did not attach within ${timeoutMs}ms — falling back to main frame`);
|
|
158
|
+
return mainFrameTarget(page);
|
|
159
|
+
}
|
|
160
|
+
const HOP_SEPARATOR = " >> ";
|
|
161
|
+
/**
|
|
162
|
+
* Composes a Stagehand hop-notation scope string from a frame selector and an
|
|
163
|
+
* inner selector, for callers building `ObserveOptions.selector` /
|
|
164
|
+
* `ExtractOptions.selector` values — kept separate from `FrameTarget` itself
|
|
165
|
+
* so `resolveFrameTarget` keeps receiving only the bare iframe-id hop (the
|
|
166
|
+
* contract `frame-resolve.test.ts` pins) rather than a pre-composed string.
|
|
167
|
+
*/
|
|
168
|
+
function buildHopSelector(frameSelector, innerSelector) {
|
|
169
|
+
if (!frameSelector)
|
|
170
|
+
return innerSelector;
|
|
171
|
+
const trimmedFrameSelector = frameSelector.trimEnd();
|
|
172
|
+
if (trimmedFrameSelector.endsWith(">>")) {
|
|
173
|
+
return `${trimmedFrameSelector} ${innerSelector.trimStart()}`;
|
|
174
|
+
}
|
|
175
|
+
return `${trimmedFrameSelector}${HOP_SEPARATOR}${innerSelector.trimStart()}`;
|
|
176
|
+
}
|
|
177
|
+
/** Readiness-wait defaults — cheap poll, short timeout: an attached-but-not-yet-navigated child frame should settle in well under a second. */
|
|
178
|
+
const FRAME_READY_TIMEOUT_MS = 5_000;
|
|
179
|
+
const FRAME_READY_POLL_MS = 100;
|
|
180
|
+
/** Shared delay helper — `FrameTarget` has no `waitForTimeout` since it isn't frame-scoped. */
|
|
181
|
+
function sleep(ms) {
|
|
182
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Blocks until a resolved child frame has a live document (`document.readyState`
|
|
186
|
+
* is `"interactive"` or `"complete"`), so callers do not observe/act against a
|
|
187
|
+
* frame that CDP has attached to but that has not yet navigated past `about:blank`
|
|
188
|
+
* — the state right after `Target.setAutoAttach` fires and before the OOPIF's own
|
|
189
|
+
* navigation lands. Best-effort like `waitForSpaReady`: never throws, just resolves
|
|
190
|
+
* once ready or once `timeoutMs` elapses, so a frame that never becomes ready
|
|
191
|
+
* degrades to "proceed anyway" rather than hanging the flow.
|
|
192
|
+
*/
|
|
193
|
+
async function waitForChildFrameReady(target, opts = {}) {
|
|
194
|
+
if (!target.frame)
|
|
195
|
+
return;
|
|
196
|
+
const timeoutMs = opts.timeoutMs ?? FRAME_READY_TIMEOUT_MS;
|
|
197
|
+
const pollMs = opts.pollMs ?? FRAME_READY_POLL_MS;
|
|
198
|
+
const isReady = async () => {
|
|
199
|
+
const readyState = await target.evaluate("document.readyState").catch(() => null);
|
|
200
|
+
return readyState === "interactive" || readyState === "complete";
|
|
201
|
+
};
|
|
202
|
+
if (await isReady())
|
|
203
|
+
return;
|
|
204
|
+
const deadline = Date.now() + timeoutMs;
|
|
205
|
+
while (Date.now() < deadline) {
|
|
206
|
+
await sleep(pollMs);
|
|
207
|
+
if (await isReady())
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
logger.warn(`child frame ${target.frameSelector ?? "(unresolved)"} still not ready after ${timeoutMs}ms — proceeding anyway`);
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=frame-target.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-target.js","sourceRoot":"","sources":["../../src/scraper/frame-target.ts"],"names":[],"mappings":"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"}
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
import type { Action, ActOptions, ActResult, ExtractOptions, ObserveOptions, Stagehand } from "@browserbasehq/stagehand";
|
|
40
40
|
import { z } from "zod/v4";
|
|
41
41
|
import { type LlmCallInput } from "../lib/telemetry/call-capture";
|
|
42
|
+
import { type FrameTarget } from "../scraper/frame-target";
|
|
42
43
|
/**
|
|
43
44
|
* Injectable capture function — matches `captureLlmCall`'s signature. Same
|
|
44
45
|
* shape as `JudgeCaptureFn` in `@/lib/llm/judge`. Lets each guard call route
|
|
@@ -109,20 +110,47 @@ export declare class StagehandSchemaError extends Error {
|
|
|
109
110
|
* `Action` (from a prior `observe`). On the happy path, returns Stagehand's
|
|
110
111
|
* `ActResult` verbatim. On envelope drift, throws `StagehandSchemaError`
|
|
111
112
|
* and logs `failureKind: "schema-validation-failed"`.
|
|
113
|
+
*
|
|
114
|
+
* Accepts the same trailing `frameTarget` param as `guardedObserve`/
|
|
115
|
+
* `guardedExtract` for signature symmetry, but does not forward it into
|
|
116
|
+
* `ActOptions` — `ActOptions` has no `selector` field, and its `page`
|
|
117
|
+
* override only accepts a full Playwright/Puppeteer/Patchright/understudy
|
|
118
|
+
* `Page`, not the `Frame` handle `FrameTarget.frame` exposes (the
|
|
119
|
+
* understudy `Page` constructor is private, so there is no way to
|
|
120
|
+
* synthesize a frame-scoped `Page`). Scope `act` at the call site instead:
|
|
121
|
+
* pair a frame-scoped `guardedObserve` (whose returned `Action.selector`
|
|
122
|
+
* already targets the resolved frame's DOM) with `guardedAct` called on
|
|
123
|
+
* that `Action` — the `observe(...)[0] -> act(target)` pattern already used
|
|
124
|
+
* throughout `flow-runner.ts`.
|
|
112
125
|
*/
|
|
113
|
-
export declare function guardedAct(stagehand: Stagehand, input: string | Action, options?: ActOptions, captureFn?: StagehandCaptureFn): Promise<ActResult>;
|
|
126
|
+
export declare function guardedAct(stagehand: Stagehand, input: string | Action, options?: ActOptions, captureFn?: StagehandCaptureFn, _frameTarget?: FrameTarget): Promise<ActResult>;
|
|
114
127
|
/**
|
|
115
128
|
* Schema-guarded wrapper around Stagehand's `observe`. Mirrors the
|
|
116
129
|
* Stagehand overloads: no args, options-only, instruction-only,
|
|
117
130
|
* instruction + options. On envelope drift (the `Action[]` shape changes),
|
|
118
131
|
* throws `StagehandSchemaError`.
|
|
132
|
+
*
|
|
133
|
+
* When `frameTarget` resolves to a cross-origin child frame, its
|
|
134
|
+
* `frameSelector` is merged into `ObserveOptions.selector` so Stagehand's
|
|
135
|
+
* snapshot/candidate search is scoped to that frame's DOM instead of only
|
|
136
|
+
* the top frame. A caller-supplied `options.selector` wins over
|
|
137
|
+
* `frameTarget.frameSelector` — the caller made an explicit choice. The
|
|
138
|
+
* main-frame target (`frameTarget` omitted, or its `frameSelector` is
|
|
139
|
+
* `null`) leaves `options` untouched, so every existing call site is
|
|
140
|
+
* byte-identical to today.
|
|
119
141
|
*/
|
|
120
|
-
export declare function guardedObserve(stagehand: Stagehand, instruction?: string, options?: ObserveOptions, captureFn?: StagehandCaptureFn): Promise<Action[]>;
|
|
142
|
+
export declare function guardedObserve(stagehand: Stagehand, instruction?: string, options?: ObserveOptions, captureFn?: StagehandCaptureFn, frameTarget?: FrameTarget): Promise<Action[]>;
|
|
121
143
|
/**
|
|
122
144
|
* Schema-guarded wrapper around Stagehand's `extract` 3-arg overload. The
|
|
123
145
|
* caller's Zod schema is what Stagehand asks the LLM to satisfy AND what we
|
|
124
146
|
* `safeParse` against caller-side. Refuse the 1-arg / 2-arg defaults; every
|
|
125
147
|
* extract call in the codebase must enforce a schema.
|
|
148
|
+
*
|
|
149
|
+
* When `frameTarget` resolves to a cross-origin child frame, its
|
|
150
|
+
* `frameSelector` is merged into `ExtractOptions.selector` so Stagehand
|
|
151
|
+
* extracts from that frame's DOM instead of only the top frame. A
|
|
152
|
+
* caller-supplied `options.selector` wins over `frameTarget.frameSelector`.
|
|
153
|
+
* The main-frame target leaves `options` untouched.
|
|
126
154
|
*/
|
|
127
|
-
export declare function guardedExtract<T extends z.ZodTypeAny>(stagehand: Stagehand, instruction: string, schema: T, options?: ExtractOptions, captureFn?: StagehandCaptureFn): Promise<z.infer<T>>;
|
|
155
|
+
export declare function guardedExtract<T extends z.ZodTypeAny>(stagehand: Stagehand, instruction: string, schema: T, options?: ExtractOptions, captureFn?: StagehandCaptureFn, frameTarget?: FrameTarget): Promise<z.infer<T>>;
|
|
128
156
|
//# sourceMappingURL=stagehand-guard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stagehand-guard.d.ts","sourceRoot":"","sources":["../../src/scraper/stagehand-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAIH,OAAO,KAAK,EACV,MAAM,EACN,UAAU,EACV,SAAS,EACT,cAAc,EACd,cAAc,EACd,SAAS,EACV,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"stagehand-guard.d.ts","sourceRoot":"","sources":["../../src/scraper/stagehand-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAIH,OAAO,KAAK,EACV,MAAM,EACN,UAAU,EACV,SAAS,EACT,cAAc,EACd,cAAc,EACd,SAAS,EACV,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,8BAA8B,CAAC;AAMtC,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE5E;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAExE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,aAAa;;;;;iBAKxB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;iBAM5B,CAAC;AAEH;;;;;;GAMG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,EAAG,0BAA0B,CAAU;IACpD,QAAQ,CAAC,SAAS,EAAE,KAAK,GAAG,SAAS,GAAG,SAAS,CAAC;IAClD,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAE9B,YACE,SAAS,EAAE,KAAK,GAAG,SAAS,GAAG,SAAS,EACxC,QAAQ,EAAE,CAAC,CAAC,QAAQ,EACpB,WAAW,EAAE,OAAO,EAOrB;CACF;AAwCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,UAAU,CAC9B,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,OAAO,CAAC,EAAE,UAAU,EACpB,SAAS,CAAC,EAAE,kBAAkB,EAC9B,YAAY,CAAC,EAAE,WAAW,GACzB,OAAO,CAAC,SAAS,CAAC,CAqEpB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,SAAS,EACpB,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,cAAc,EACxB,SAAS,CAAC,EAAE,kBAAkB,EAC9B,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,MAAM,EAAE,CAAC,CAmEnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EACzD,SAAS,EAAE,SAAS,EACpB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,CAAC,EACT,OAAO,CAAC,EAAE,cAAc,EACxB,SAAS,CAAC,EAAE,kBAAkB,EAC9B,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAsErB"}
|
|
@@ -46,6 +46,7 @@ const node_crypto_1 = require("node:crypto");
|
|
|
46
46
|
const v4_1 = require("zod/v4");
|
|
47
47
|
const call_capture_1 = require("../lib/telemetry/call-capture");
|
|
48
48
|
const call_types_1 = require("../lib/telemetry/call-types");
|
|
49
|
+
const frame_target_1 = require("../scraper/frame-target");
|
|
49
50
|
/**
|
|
50
51
|
* Zod mirror of Stagehand's public `Action` shape (from
|
|
51
52
|
* `@browserbasehq/stagehand/.../public/methods.d.ts`):
|
|
@@ -118,14 +119,43 @@ function safeStringify(value, cap = 4000) {
|
|
|
118
119
|
function actInstructionOf(input) {
|
|
119
120
|
return typeof input === "string" ? input : input.description;
|
|
120
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Merges `frameTarget.frameSelector` into `options.selector` for
|
|
124
|
+
* `ObserveOptions`/`ExtractOptions`, composed as Stagehand `">>"` hop
|
|
125
|
+
* notation (`buildHopSelector`) rather than the bare iframe selector —
|
|
126
|
+
* `resolveCssFocusFrameAndTail` only crosses into the child frame's own CDP
|
|
127
|
+
* session when the selector has a hop segment before `>>`; a bare selector
|
|
128
|
+
* performs zero hops and scopes to the (empty) `<iframe>` element itself.
|
|
129
|
+
* The inner segment defaults to `*` (whole-frame scope) since callers don't
|
|
130
|
+
* supply one here. Returns `options` untouched when `frameTarget` is
|
|
131
|
+
* unresolved (main frame) or a caller-supplied `selector` is already
|
|
132
|
+
* present — both cases must leave today's call sites byte-identical.
|
|
133
|
+
*/
|
|
134
|
+
function frameScopedOptions(options, frameTarget) {
|
|
135
|
+
if (!frameTarget?.frameSelector || options?.selector !== undefined)
|
|
136
|
+
return options;
|
|
137
|
+
return { ...options, selector: (0, frame_target_1.buildHopSelector)(frameTarget.frameSelector, "*") };
|
|
138
|
+
}
|
|
121
139
|
/**
|
|
122
140
|
* Schema-guarded wrapper around Stagehand's `act`. Same signature as the
|
|
123
141
|
* underlying call: accepts either an instruction string or a structured
|
|
124
142
|
* `Action` (from a prior `observe`). On the happy path, returns Stagehand's
|
|
125
143
|
* `ActResult` verbatim. On envelope drift, throws `StagehandSchemaError`
|
|
126
144
|
* and logs `failureKind: "schema-validation-failed"`.
|
|
145
|
+
*
|
|
146
|
+
* Accepts the same trailing `frameTarget` param as `guardedObserve`/
|
|
147
|
+
* `guardedExtract` for signature symmetry, but does not forward it into
|
|
148
|
+
* `ActOptions` — `ActOptions` has no `selector` field, and its `page`
|
|
149
|
+
* override only accepts a full Playwright/Puppeteer/Patchright/understudy
|
|
150
|
+
* `Page`, not the `Frame` handle `FrameTarget.frame` exposes (the
|
|
151
|
+
* understudy `Page` constructor is private, so there is no way to
|
|
152
|
+
* synthesize a frame-scoped `Page`). Scope `act` at the call site instead:
|
|
153
|
+
* pair a frame-scoped `guardedObserve` (whose returned `Action.selector`
|
|
154
|
+
* already targets the resolved frame's DOM) with `guardedAct` called on
|
|
155
|
+
* that `Action` — the `observe(...)[0] -> act(target)` pattern already used
|
|
156
|
+
* throughout `flow-runner.ts`.
|
|
127
157
|
*/
|
|
128
|
-
async function guardedAct(stagehand, input, options, captureFn) {
|
|
158
|
+
async function guardedAct(stagehand, input, options, captureFn, _frameTarget) {
|
|
129
159
|
const callId = (0, node_crypto_1.randomUUID)();
|
|
130
160
|
const userContent = actInstructionOf(input);
|
|
131
161
|
const t0 = performance.now();
|
|
@@ -192,19 +222,29 @@ async function guardedAct(stagehand, input, options, captureFn) {
|
|
|
192
222
|
* Stagehand overloads: no args, options-only, instruction-only,
|
|
193
223
|
* instruction + options. On envelope drift (the `Action[]` shape changes),
|
|
194
224
|
* throws `StagehandSchemaError`.
|
|
225
|
+
*
|
|
226
|
+
* When `frameTarget` resolves to a cross-origin child frame, its
|
|
227
|
+
* `frameSelector` is merged into `ObserveOptions.selector` so Stagehand's
|
|
228
|
+
* snapshot/candidate search is scoped to that frame's DOM instead of only
|
|
229
|
+
* the top frame. A caller-supplied `options.selector` wins over
|
|
230
|
+
* `frameTarget.frameSelector` — the caller made an explicit choice. The
|
|
231
|
+
* main-frame target (`frameTarget` omitted, or its `frameSelector` is
|
|
232
|
+
* `null`) leaves `options` untouched, so every existing call site is
|
|
233
|
+
* byte-identical to today.
|
|
195
234
|
*/
|
|
196
|
-
async function guardedObserve(stagehand, instruction, options, captureFn) {
|
|
235
|
+
async function guardedObserve(stagehand, instruction, options, captureFn, frameTarget) {
|
|
197
236
|
const callId = (0, node_crypto_1.randomUUID)();
|
|
198
237
|
const userContent = instruction ?? "";
|
|
199
238
|
const t0 = performance.now();
|
|
239
|
+
const scopedOptions = frameScopedOptions(options, frameTarget);
|
|
200
240
|
try {
|
|
201
241
|
// Match Stagehand's overloads: pass instruction only when defined, so
|
|
202
242
|
// the SDK falls through to its no-arg/options-only path otherwise.
|
|
203
243
|
const raw = instruction === undefined
|
|
204
|
-
?
|
|
244
|
+
? scopedOptions === undefined
|
|
205
245
|
? await stagehand.observe()
|
|
206
|
-
: await stagehand.observe(
|
|
207
|
-
: await stagehand.observe(instruction,
|
|
246
|
+
: await stagehand.observe(scopedOptions)
|
|
247
|
+
: await stagehand.observe(instruction, scopedOptions);
|
|
208
248
|
const latencyMs = performance.now() - t0;
|
|
209
249
|
const parsed = v4_1.z.array(exports.ACTION_SCHEMA).safeParse(raw);
|
|
210
250
|
if (!parsed.success) {
|
|
@@ -257,8 +297,14 @@ async function guardedObserve(stagehand, instruction, options, captureFn) {
|
|
|
257
297
|
* caller's Zod schema is what Stagehand asks the LLM to satisfy AND what we
|
|
258
298
|
* `safeParse` against caller-side. Refuse the 1-arg / 2-arg defaults; every
|
|
259
299
|
* extract call in the codebase must enforce a schema.
|
|
300
|
+
*
|
|
301
|
+
* When `frameTarget` resolves to a cross-origin child frame, its
|
|
302
|
+
* `frameSelector` is merged into `ExtractOptions.selector` so Stagehand
|
|
303
|
+
* extracts from that frame's DOM instead of only the top frame. A
|
|
304
|
+
* caller-supplied `options.selector` wins over `frameTarget.frameSelector`.
|
|
305
|
+
* The main-frame target leaves `options` untouched.
|
|
260
306
|
*/
|
|
261
|
-
async function guardedExtract(stagehand, instruction, schema, options, captureFn) {
|
|
307
|
+
async function guardedExtract(stagehand, instruction, schema, options, captureFn, frameTarget) {
|
|
262
308
|
const callId = (0, node_crypto_1.randomUUID)();
|
|
263
309
|
const t0 = performance.now();
|
|
264
310
|
try {
|
|
@@ -270,7 +316,7 @@ async function guardedExtract(stagehand, instruction, schema, options, captureFn
|
|
|
270
316
|
// the cleanest public-API way to express "whatever overload-2 expects."
|
|
271
317
|
// The `as unknown` step is needed because TS won't accept a single
|
|
272
318
|
// direct cast across the entire overload set.
|
|
273
|
-
const raw = await stagehand.extract(instruction, schema, options);
|
|
319
|
+
const raw = await stagehand.extract(instruction, schema, frameScopedOptions(options, frameTarget));
|
|
274
320
|
const latencyMs = performance.now() - t0;
|
|
275
321
|
const parsed = schema.safeParse(raw);
|
|
276
322
|
if (!parsed.success) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stagehand-guard.js","sourceRoot":"","sources":["../../src/scraper/stagehand-guard.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;;;;;;AAEH,6CAAyC;AAUzC,+BAA2B;AAE3B,+DAIsC;AACtC,2DAIoC;
|
|
1
|
+
{"version":3,"file":"stagehand-guard.js","sourceRoot":"","sources":["../../src/scraper/stagehand-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,8 +22,16 @@
|
|
|
22
22
|
* re-runs the identical traversal and clicks the element at that index, so
|
|
23
23
|
* the caller can locate once, decide which candidate to try, then click by
|
|
24
24
|
* index without holding a live element handle across the two round trips.
|
|
25
|
+
*
|
|
26
|
+
* `root` overrides the traversal root expression (default `"document"`),
|
|
27
|
+
* interpolated verbatim into the generated code so a caller evaluating
|
|
28
|
+
* this expression via `Frame.evaluate` can pass `"document"` and still
|
|
29
|
+
* resolve that frame's own document — the expression never captures an
|
|
30
|
+
* outer `document` reference. {@link buildClickByDeepIndexExpr} must be
|
|
31
|
+
* given the same `root` so its re-run traversal produces the same
|
|
32
|
+
* `deepIndex` ordering as the one this call returned.
|
|
25
33
|
*/
|
|
26
|
-
export declare function buildRankSubmitCandidatesExpr(): string;
|
|
34
|
+
export declare function buildRankSubmitCandidatesExpr(root?: string): string;
|
|
27
35
|
/**
|
|
28
36
|
* Builds a self-contained `page.evaluate` expression string that re-runs
|
|
29
37
|
* the same deterministic deep traversal as {@link buildRankSubmitCandidatesExpr}
|
|
@@ -32,8 +40,13 @@ export declare function buildRankSubmitCandidatesExpr(): string;
|
|
|
32
40
|
* convention). Returns `{ clicked: false }` without throwing if the index is
|
|
33
41
|
* out of range for the current DOM (e.g. the page changed between the
|
|
34
42
|
* locate and click calls).
|
|
43
|
+
*
|
|
44
|
+
* `root` overrides the traversal root expression (default `"document"`)
|
|
45
|
+
* and must match the `root` passed to the {@link buildRankSubmitCandidatesExpr}
|
|
46
|
+
* call that produced `deepIndex`, or the re-run traversal order will not
|
|
47
|
+
* line up with the original ranking.
|
|
35
48
|
*/
|
|
36
|
-
export declare function buildClickByDeepIndexExpr(deepIndex: number): string;
|
|
49
|
+
export declare function buildClickByDeepIndexExpr(deepIndex: number, root?: string): string;
|
|
37
50
|
/** Confidence tier for a ranked submit candidate — higher is more confident. See {@link buildRankSubmitCandidatesExpr}. */
|
|
38
51
|
export type SubmitCandidateTier = 1 | 2 | 3;
|
|
39
52
|
/** One ranked candidate returned by {@link buildRankSubmitCandidatesExpr}'s `page.evaluate` call. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"submit-control.d.ts","sourceRoot":"","sources":["../../src/scraper/submit-control.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAyEH
|
|
1
|
+
{"version":3,"file":"submit-control.d.ts","sourceRoot":"","sources":["../../src/scraper/submit-control.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAyEH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,SAAa,GAAG,MAAM,CAsBvE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,SAAa,GAAG,MAAM,CAYtF;AAED,2HAA2H;AAC3H,MAAM,MAAM,mBAAmB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAE5C,qGAAqG;AACrG,MAAM,WAAW,eAAe;IAC9B,mIAAmI;IACnI,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,mBAAmB,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,0EAA0E;AAC1E,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;CAClB"}
|
|
@@ -93,13 +93,21 @@ const RANK_TIERS_EXPR = `((el, name) => {
|
|
|
93
93
|
* re-runs the identical traversal and clicks the element at that index, so
|
|
94
94
|
* the caller can locate once, decide which candidate to try, then click by
|
|
95
95
|
* index without holding a live element handle across the two round trips.
|
|
96
|
+
*
|
|
97
|
+
* `root` overrides the traversal root expression (default `"document"`),
|
|
98
|
+
* interpolated verbatim into the generated code so a caller evaluating
|
|
99
|
+
* this expression via `Frame.evaluate` can pass `"document"` and still
|
|
100
|
+
* resolve that frame's own document — the expression never captures an
|
|
101
|
+
* outer `document` reference. {@link buildClickByDeepIndexExpr} must be
|
|
102
|
+
* given the same `root` so its re-run traversal produces the same
|
|
103
|
+
* `deepIndex` ordering as the one this call returned.
|
|
96
104
|
*/
|
|
97
|
-
function buildRankSubmitCandidatesExpr() {
|
|
105
|
+
function buildRankSubmitCandidatesExpr(root = "document") {
|
|
98
106
|
return `(() => {
|
|
99
107
|
const accessibleName = ${ACCESSIBLE_NAME_EXPR};
|
|
100
108
|
const rankTier = ${RANK_TIERS_EXPR};
|
|
101
109
|
const deepElements = ${DEEP_ELEMENTS_EXPR};
|
|
102
|
-
const all = deepElements(
|
|
110
|
+
const all = deepElements(${root});
|
|
103
111
|
const ranked = [];
|
|
104
112
|
for (let i = 0; i < all.length; i++) {
|
|
105
113
|
const el = all[i];
|
|
@@ -125,11 +133,16 @@ function buildRankSubmitCandidatesExpr() {
|
|
|
125
133
|
* convention). Returns `{ clicked: false }` without throwing if the index is
|
|
126
134
|
* out of range for the current DOM (e.g. the page changed between the
|
|
127
135
|
* locate and click calls).
|
|
136
|
+
*
|
|
137
|
+
* `root` overrides the traversal root expression (default `"document"`)
|
|
138
|
+
* and must match the `root` passed to the {@link buildRankSubmitCandidatesExpr}
|
|
139
|
+
* call that produced `deepIndex`, or the re-run traversal order will not
|
|
140
|
+
* line up with the original ranking.
|
|
128
141
|
*/
|
|
129
|
-
function buildClickByDeepIndexExpr(deepIndex) {
|
|
142
|
+
function buildClickByDeepIndexExpr(deepIndex, root = "document") {
|
|
130
143
|
return `(() => {
|
|
131
144
|
const deepElements = ${DEEP_ELEMENTS_EXPR};
|
|
132
|
-
const all = deepElements(
|
|
145
|
+
const all = deepElements(${root});
|
|
133
146
|
const el = all[${JSON.stringify(deepIndex)}];
|
|
134
147
|
if (!el) return { clicked: false };
|
|
135
148
|
if (typeof el.focus === "function") { try { el.focus(); } catch (e) {} }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"submit-control.js","sourceRoot":"","sources":["../../src/scraper/submit-control.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;;AAEH;;;;;;GAMG;AACH,MAAM,kBAAkB,GAAG;;;GAGxB,CAAC;AAEJ;;;;;GAKG;AACH,MAAM,oBAAoB,GAAG;;;GAG1B,CAAC;AAEJ;;;;;GAKG;AACH,MAAM,kBAAkB,GAAG;;;;;;;;;;;GAWxB,CAAC;AAEJ;;;;;;;;;;;;;;GAcG;AACH,MAAM,eAAe,GAAG;uBACD,kBAAkB;;;;;;;;;;;GAWtC,CAAC;AAEJ
|
|
1
|
+
{"version":3,"file":"submit-control.js","sourceRoot":"","sources":["../../src/scraper/submit-control.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;;AAEH;;;;;;GAMG;AACH,MAAM,kBAAkB,GAAG;;;GAGxB,CAAC;AAEJ;;;;;GAKG;AACH,MAAM,oBAAoB,GAAG;;;GAG1B,CAAC;AAEJ;;;;;GAKG;AACH,MAAM,kBAAkB,GAAG;;;;;;;;;;;GAWxB,CAAC;AAEJ;;;;;;;;;;;;;;GAcG;AACH,MAAM,eAAe,GAAG;uBACD,kBAAkB;;;;;;;;;;;GAWtC,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,uCAA8C,IAAI,GAAG,UAAU;IAC7D,OAAO;6BACoB,oBAAoB;uBAC1B,eAAe;2BACX,kBAAkB;+BACd,IAAI;;;;;;;;;;;;;;;;OAgB5B,CAAC;AACR,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,mCAA0C,SAAiB,EAAE,IAAI,GAAG,UAAU;IAC5E,OAAO;2BACkB,kBAAkB;+BACd,IAAI;qBACd,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;;;;;;;OAOvC,CAAC;AACR,CAAC"}
|