@enricai/barnacle 1.6.11 → 1.6.13
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/README.md +113 -9
- package/dist/api/schemas/submissions.d.ts +14 -0
- package/dist/api/schemas/submissions.d.ts.map +1 -1
- package/dist/api/schemas/submissions.js +1 -0
- package/dist/api/schemas/submissions.js.map +1 -1
- package/dist/config.d.ts +33 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +4 -0
- package/dist/config.js.map +1 -1
- package/dist/lib/telemetry/beacon-capture.d.ts +45 -2
- package/dist/lib/telemetry/beacon-capture.d.ts.map +1 -1
- package/dist/lib/telemetry/beacon-capture.js +36 -0
- package/dist/lib/telemetry/beacon-capture.js.map +1 -1
- package/dist/lib/telemetry/reconciliation-record.d.ts +50 -2
- package/dist/lib/telemetry/reconciliation-record.d.ts.map +1 -1
- package/dist/lib/telemetry/reconciliation-record.js +60 -7
- package/dist/lib/telemetry/reconciliation-record.js.map +1 -1
- package/dist/lib/telemetry/reconciliation-source.d.ts +7 -1
- package/dist/lib/telemetry/reconciliation-source.d.ts.map +1 -1
- package/dist/lib/telemetry/reconciliation-source.js +21 -5
- package/dist/lib/telemetry/reconciliation-source.js.map +1 -1
- package/dist/lib/telemetry/run-telemetry.d.ts +67 -0
- package/dist/lib/telemetry/run-telemetry.d.ts.map +1 -0
- package/dist/lib/telemetry/run-telemetry.js +55 -0
- package/dist/lib/telemetry/run-telemetry.js.map +1 -0
- package/dist/lib/telemetry/submission-capture.d.ts +11 -4
- package/dist/lib/telemetry/submission-capture.d.ts.map +1 -1
- package/dist/lib/telemetry/submission-capture.js +1 -0
- package/dist/lib/telemetry/submission-capture.js.map +1 -1
- package/dist/lib/telemetry/submission-reader.d.ts +11 -3
- package/dist/lib/telemetry/submission-reader.d.ts.map +1 -1
- package/dist/lib/telemetry/submission-reader.js +32 -8
- package/dist/lib/telemetry/submission-reader.js.map +1 -1
- package/dist/lib/tracking-click.d.ts.map +1 -1
- package/dist/lib/tracking-click.js +19 -0
- package/dist/lib/tracking-click.js.map +1 -1
- package/dist/plugins/loader.d.ts +20 -0
- package/dist/plugins/loader.d.ts.map +1 -1
- package/dist/plugins/loader.js +94 -17
- package/dist/plugins/loader.js.map +1 -1
- package/dist/scraper/deep-locator-actuate.d.ts +94 -0
- package/dist/scraper/deep-locator-actuate.d.ts.map +1 -0
- package/dist/scraper/deep-locator-actuate.js +216 -0
- package/dist/scraper/deep-locator-actuate.js.map +1 -0
- package/dist/scraper/deep-locator-candidates.d.ts +105 -18
- package/dist/scraper/deep-locator-candidates.d.ts.map +1 -1
- package/dist/scraper/deep-locator-candidates.js +238 -46
- package/dist/scraper/deep-locator-candidates.js.map +1 -1
- package/dist/scraper/deep-locator-click.d.ts +71 -0
- package/dist/scraper/deep-locator-click.d.ts.map +1 -0
- package/dist/scraper/deep-locator-click.js +71 -0
- package/dist/scraper/deep-locator-click.js.map +1 -0
- package/dist/scraper/deep-locator-fake.d.ts +344 -14
- package/dist/scraper/deep-locator-fake.d.ts.map +1 -1
- package/dist/scraper/deep-locator-fake.js +509 -7
- package/dist/scraper/deep-locator-fake.js.map +1 -1
- package/dist/scraper/deep-locator-scan.d.ts +220 -0
- package/dist/scraper/deep-locator-scan.d.ts.map +1 -0
- package/dist/scraper/deep-locator-scan.js +386 -0
- package/dist/scraper/deep-locator-scan.js.map +1 -0
- package/dist/scraper/fake-timer-advance.d.ts +47 -0
- package/dist/scraper/fake-timer-advance.d.ts.map +1 -0
- package/dist/scraper/fake-timer-advance.js +44 -0
- package/dist/scraper/fake-timer-advance.js.map +1 -0
- package/dist/scraper/flow-runner.d.ts +36 -1
- package/dist/scraper/flow-runner.d.ts.map +1 -1
- package/dist/scraper/flow-runner.js +354 -56
- package/dist/scraper/flow-runner.js.map +1 -1
- package/dist/scraper/frame-target.d.ts +28 -0
- package/dist/scraper/frame-target.d.ts.map +1 -1
- package/dist/scraper/frame-target.js +30 -0
- package/dist/scraper/frame-target.js.map +1 -1
- package/dist/scraper/session-browserbase.d.ts +13 -0
- package/dist/scraper/session-browserbase.d.ts.map +1 -1
- package/dist/scraper/session-browserbase.js +29 -0
- package/dist/scraper/session-browserbase.js.map +1 -1
- package/dist/scraper/session-ip.d.ts +69 -0
- package/dist/scraper/session-ip.d.ts.map +1 -0
- package/dist/scraper/session-ip.js +88 -0
- package/dist/scraper/session-ip.js.map +1 -0
- package/dist/scraper/session-shared.d.ts +9 -0
- package/dist/scraper/session-shared.d.ts.map +1 -1
- package/dist/scraper/session-shared.js.map +1 -1
- package/dist/scraper/submit-control.d.ts +14 -2
- package/dist/scraper/submit-control.d.ts.map +1 -1
- package/dist/scraper/submit-control.js +33 -2
- package/dist/scraper/submit-control.js.map +1 -1
- package/dist/scripts/recon-browser.d.ts.map +1 -1
- package/dist/scripts/recon-browser.js +32 -0
- package/dist/scripts/recon-browser.js.map +1 -1
- package/dist/site-plugin.d.ts +20 -0
- package/dist/site-plugin.d.ts.map +1 -1
- package/dist/testing/integration-runner.d.ts.map +1 -1
- package/dist/testing/integration-runner.js +6 -7
- package/dist/testing/integration-runner.js.map +1 -1
- package/package.json +1 -1
|
@@ -9,12 +9,31 @@
|
|
|
9
9
|
* when the step is scoped to a child frame.
|
|
10
10
|
*/
|
|
11
11
|
import type { Page } from "@browserbasehq/stagehand";
|
|
12
|
+
import { type FrameTarget } from "../scraper/frame-target";
|
|
12
13
|
/** Overrides for the watchdog timeouts this module applies to every `deepLocator()` await; tests pass small values so cases don't burn wall-clock. */
|
|
13
14
|
export interface DeepLocatorTimeoutOptions {
|
|
14
15
|
/** Per-call watchdog timeout for `count()`/`textContent()`/`click()`. Defaults to {@link DEFAULT_DEEP_LOCATOR_CALL_TIMEOUT_MS}. */
|
|
15
16
|
callTimeoutMs?: number;
|
|
16
|
-
/** Total budget for {@link resolveDeepLocatorCandidates}'s enumeration loop. Defaults to {@link DEFAULT_DEEP_LOCATOR_ENUMERATION_BUDGET_MS}. */
|
|
17
|
+
/** Total budget for {@link resolveDeepLocatorCandidates}'s legacy enumeration loop. Defaults to {@link DEFAULT_DEEP_LOCATOR_ENUMERATION_BUDGET_MS}. */
|
|
17
18
|
enumerationBudgetMs?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Pre-resolved `FrameTarget` to scan via one batched
|
|
21
|
+
* `evaluate(buildScanFrameCandidatesExpr(innerSelector))` round-trip
|
|
22
|
+
* instead of the legacy `count()` + per-candidate `textContent()` loop.
|
|
23
|
+
* When omitted, a single non-polling `probeAttachedFrameTarget(page,
|
|
24
|
+
* frameSelector)` pass (`frame-target.ts`) is attempted internally so
|
|
25
|
+
* existing call sites get the batched fast path for free. Unlike a bare
|
|
26
|
+
* `resolveFrameTarget(page, frameSelector, { timeoutMs: 0 })` pass — which
|
|
27
|
+
* always loses the race against genuine CDP latency — this probe carries a
|
|
28
|
+
* real per-probe budget (`config.scraper.framePresenceProbeFloorMs`), so it
|
|
29
|
+
* can actually land against an already-attached frame under live latency.
|
|
30
|
+
* If the probe doesn't land a resolved child frame (not yet attached, or
|
|
31
|
+
* `frameSelector` is null/undefined), the legacy loop runs unchanged. A
|
|
32
|
+
* caller that already resolved a `FrameTarget` (e.g. `flow-runner.ts`'s
|
|
33
|
+
* per-step resolution) should pass it here to skip the redundant internal
|
|
34
|
+
* probe.
|
|
35
|
+
*/
|
|
36
|
+
frameTarget?: FrameTarget;
|
|
18
37
|
}
|
|
19
38
|
/**
|
|
20
39
|
* One candidate element `page.deepLocator()` resolved inside a scoped frame.
|
|
@@ -33,9 +52,48 @@ export interface DeepLocatorCandidate {
|
|
|
33
52
|
index: number;
|
|
34
53
|
/** `deeplocator=`-prefixed selector identifying this candidate for downstream `resolvedAction` synthesis; deliberately not `xpath=` (see {@link DeepLocatorCandidate} docs). */
|
|
35
54
|
selector: string;
|
|
36
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Accessible text for this candidate: the derived accessible name (see
|
|
57
|
+
* {@link buildScanFrameCandidatesExpr}) on the batched-scan fast path,
|
|
58
|
+
* or the delegate's raw `textContent()` on the legacy per-candidate
|
|
59
|
+
* enumeration fallback.
|
|
60
|
+
*/
|
|
37
61
|
accessibleText: string;
|
|
38
62
|
}
|
|
63
|
+
/** One quoted phrase from an instruction, tagged with whether a negation marker (`NEGATION_MARKERS`) governs it. See {@link extractTaggedPhrases}. */
|
|
64
|
+
export interface TaggedPhrase {
|
|
65
|
+
text: string;
|
|
66
|
+
negated: boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Extracts every single-quoted phrase from `instruction`, tagging each as
|
|
70
|
+
* negated when a negation marker (`NEGATION_MARKERS`) precedes it within the
|
|
71
|
+
* same sentence — flow instructions keep each "Do NOT click 'X', 'Y', ..."
|
|
72
|
+
* clause as one sentence listing every negated phrase, so a sentence
|
|
73
|
+
* boundary (`.`/`;`) between the marker and the phrase ends the negation's
|
|
74
|
+
* reach. Mirrors the quoted-phrase extraction convention in
|
|
75
|
+
* `parseSelectStep`/`parseRadioStep` (`flow-runner.ts`) — same
|
|
76
|
+
* `/'([^']+)'/g` shape — but here we need ALL quoted phrases plus their
|
|
77
|
+
* polarity, not just one option/label pair. Exported so `flow-runner.ts` can
|
|
78
|
+
* re-derive the exact same ranking {@link scoreCandidate} used to sort
|
|
79
|
+
* `deepLocatorCandidates`, to detect a tie for the top rank rather than
|
|
80
|
+
* duplicating this logic.
|
|
81
|
+
*/
|
|
82
|
+
export declare function extractTaggedPhrases(instruction: string): TaggedPhrase[];
|
|
83
|
+
/**
|
|
84
|
+
* Scores one candidate's relevance to `instruction`'s tagged phrases, higher
|
|
85
|
+
* is more relevant. Mirrors `submit-control.ts`'s tiered-ranking shape:
|
|
86
|
+
* negative signal is checked first and wins outright (a candidate matching
|
|
87
|
+
* ANY negated phrase is actively demoted below "no match" — a decoy sibling
|
|
88
|
+
* button is worse than an unrelated structural node), then positive
|
|
89
|
+
* exact/substring match tiers, falling through to 0 for empty or
|
|
90
|
+
* unrelated text so a structural container with no accessible text can
|
|
91
|
+
* never outrank a candidate whose text actually matches the instruction.
|
|
92
|
+
* Exported so `flow-runner.ts` can detect a tie for the top rank among
|
|
93
|
+
* `resolveDeepLocatorCandidates`'s already-sorted output (see
|
|
94
|
+
* {@link extractTaggedPhrases}'s docblock).
|
|
95
|
+
*/
|
|
96
|
+
export declare function scoreCandidate(accessibleText: string, phrases: TaggedPhrase[]): number;
|
|
39
97
|
/**
|
|
40
98
|
* Enumerates every element `page.deepLocator()` matches inside the frame
|
|
41
99
|
* scoped by `frameSelector`, ranked by relevance to `instruction` (highest
|
|
@@ -50,20 +108,25 @@ export interface DeepLocatorCandidate {
|
|
|
50
108
|
* cascading through candidate sources can move on to the next technique
|
|
51
109
|
* instead of hanging the step forever.
|
|
52
110
|
*
|
|
111
|
+
* Prefers a single batched `evaluate` round-trip over the frame
|
|
112
|
+
* (see {@link scanFrameCandidatesBatched}) — collapsing the O(n) per-candidate
|
|
113
|
+
* `textContent()` round-trips that made a dense OOPIF form (371 candidates
|
|
114
|
+
* measured live) unresolvable inside any reasonable budget — and falls back
|
|
115
|
+
* to the legacy `count()` + per-candidate loop (see
|
|
116
|
+
* {@link enumerateCandidatesViaLegacyLoop}) when no frame seam is available,
|
|
117
|
+
* the batched scan fails, or the batched scan resolves to zero candidates —
|
|
118
|
+
* the batched expression's light-DOM `querySelectorAll` can't see into a
|
|
119
|
+
* shadow root the way `page.deepLocator()` can, so a zero-length scan is
|
|
120
|
+
* treated as inconclusive rather than authoritative. Pass an already-resolved
|
|
121
|
+
* {@link DeepLocatorTimeoutOptions.frameTarget} to use the fast path without
|
|
122
|
+
* paying for an internal re-resolution.
|
|
123
|
+
*
|
|
53
124
|
* Ranking exists because a hop like `"*"` matches every element inside a
|
|
54
125
|
* wizard iframe (html, body, every div, ...) — DOM order alone almost always
|
|
55
126
|
* puts a structural container first, not the control the step instruction
|
|
56
127
|
* actually names. Pass `instruction` as `null`/`undefined` (or omit it) to
|
|
57
128
|
* skip ranking and get delegate order — e.g. for pre-cascade reachability
|
|
58
129
|
* probes that only care whether the frame has ANY candidates.
|
|
59
|
-
*
|
|
60
|
-
* `count()` and each per-candidate `textContent()` are individually bounded
|
|
61
|
-
* by `timeoutOptions.callTimeoutMs` via {@link withWatchdog}; the enumeration
|
|
62
|
-
* loop as a whole is additionally bounded by `timeoutOptions.enumerationBudgetMs`
|
|
63
|
-
* so a hop with dozens of slow-but-settling elements (e.g. 65 candidates
|
|
64
|
-
* matched against a live OOPIF) can't rack up an unbounded total cost even
|
|
65
|
-
* when no single call hangs — the loop aborts early and returns whatever
|
|
66
|
-
* candidates it already resolved.
|
|
67
130
|
*/
|
|
68
131
|
export declare function resolveDeepLocatorCandidates(page: Page, frameSelector: string | null | undefined, innerSelector: string, instruction?: string | null, timeoutOptions?: DeepLocatorTimeoutOptions): Promise<DeepLocatorCandidate[]>;
|
|
69
132
|
/**
|
|
@@ -71,14 +134,38 @@ export declare function resolveDeepLocatorCandidates(page: Page, frameSelector:
|
|
|
71
134
|
* re-deriving the same hop selector `resolveDeepLocatorCandidates` used
|
|
72
135
|
* rather than trusting a caller-supplied `xpath=` string, so the two stay in
|
|
73
136
|
* lockstep even if a candidate's `selector` field is only ever used for
|
|
74
|
-
* display/logging.
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* the same
|
|
81
|
-
*
|
|
137
|
+
* display/logging. Resolves on success and rejects on failure — the caller
|
|
138
|
+
* must infer the outcome from whether this call throws plus their own
|
|
139
|
+
* downstream DOM verification, not from a returned boolean.
|
|
140
|
+
*
|
|
141
|
+
* Prefers the one-round-trip {@link clickCandidateBatched} fast path when a
|
|
142
|
+
* frame seam is available (`timeoutOptions.frameTarget`, or one resolved
|
|
143
|
+
* internally the same way {@link scanFrameCandidatesBatched} does), falling
|
|
144
|
+
* back to the legacy `DeepLocatorDelegate.click()` — which never reports
|
|
145
|
+
* success via a return value, only via not rejecting — when no seam is
|
|
146
|
+
* available, the batched call fails/degrades, or it reports the index
|
|
147
|
+
* stale (`reason: "out-of-range"`). A batched `reason: "not-actionable"`
|
|
148
|
+
* result throws an error {@link isNodeNotActionableError} classifies, the
|
|
149
|
+
* same contract a real click against an unrendered node rejects with via the
|
|
150
|
+
* CDP `-32000 Node does not have a layout object` error, so a caller
|
|
151
|
+
* cascading through candidates treats both paths identically.
|
|
152
|
+
*
|
|
153
|
+
* The legacy fallback's watchdog scales with `index` (see
|
|
154
|
+
* {@link DEEP_LOCATOR_CLICK_INDEX_ROUND_TRIP_MS}) so a legitimately-reachable
|
|
155
|
+
* candidate isn't killed by a budget sized for a single round-trip. A
|
|
156
|
+
* `click()` that still exceeds that scaled budget (a wedged CDP round-trip
|
|
157
|
+
* against a racy OOPIF frame) rejects with a `WatchdogTimeoutError` the same
|
|
158
|
+
* as any other failure, preserving the "rejects on failure" contract instead
|
|
159
|
+
* of hanging the caller forever.
|
|
82
160
|
*/
|
|
83
161
|
export declare function clickDeepLocatorCandidate(page: Page, frameSelector: string | null | undefined, innerSelector: string, index: number, timeoutOptions?: DeepLocatorTimeoutOptions): Promise<void>;
|
|
162
|
+
/**
|
|
163
|
+
* `fillDeepLocatorCandidate`/`selectDeepLocatorCandidateOption` live in
|
|
164
|
+
* `deep-locator-actuate.ts`, not here — that module's `writeAndVerify` wraps
|
|
165
|
+
* the same `buildHopSelector`/`withWatchdog` contract this file's
|
|
166
|
+
* `clickDeepLocatorCandidate` uses, but additionally reads the write back
|
|
167
|
+
* through `inputValue()` before reporting success (`verifyDomEffect` can't
|
|
168
|
+
* resolve a `deeplocator=` selector, so the read-back is the only
|
|
169
|
+
* confirmation a fill/select actually landed).
|
|
170
|
+
*/
|
|
84
171
|
//# sourceMappingURL=deep-locator-candidates.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deep-locator-candidates.d.ts","sourceRoot":"","sources":["../../src/scraper/deep-locator-candidates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"deep-locator-candidates.d.ts","sourceRoot":"","sources":["../../src/scraper/deep-locator-candidates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAWrD,OAAO,EAEL,KAAK,WAAW,EAEjB,MAAM,wBAAwB,CAAC;AA2BhC,sJAAsJ;AACtJ,MAAM,WAAW,yBAAyB;IACxC,mIAAmI;IACnI,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uJAAuJ;IACvJ,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;;;;;;;;;;;OAgBG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAKD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,oBAAoB;IACnC,kJAAkJ;IAClJ,KAAK,EAAE,MAAM,CAAC;IACd,gLAAgL;IAChL,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC;CACxB;AAsBD,sJAAsJ;AACtJ,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,EAAE,CAaxE;AAOD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAStF;AA6JD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,aAAa,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,EAC3B,cAAc,GAAE,yBAA8B,GAC7C,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAuCjC;AAqDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM,EACb,cAAc,GAAE,yBAA8B,GAC7C,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED;;;;;;;;GAQG"}
|
|
@@ -10,10 +10,13 @@
|
|
|
10
10
|
* when the step is scoped to a child frame.
|
|
11
11
|
*/
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.extractTaggedPhrases = extractTaggedPhrases;
|
|
14
|
+
exports.scoreCandidate = scoreCandidate;
|
|
13
15
|
exports.resolveDeepLocatorCandidates = resolveDeepLocatorCandidates;
|
|
14
16
|
exports.clickDeepLocatorCandidate = clickDeepLocatorCandidate;
|
|
15
17
|
const errors_1 = require("../lib/errors");
|
|
16
18
|
const logging_1 = require("../lib/logging");
|
|
19
|
+
const deep_locator_scan_1 = require("../scraper/deep-locator-scan");
|
|
17
20
|
const frame_target_1 = require("../scraper/frame-target");
|
|
18
21
|
const watchdog_1 = require("../scraper/watchdog");
|
|
19
22
|
const logger = (0, logging_1.getLogger)({ name: "scraper/deep-locator-candidates" });
|
|
@@ -25,12 +28,16 @@ const logger = (0, logging_1.getLogger)({ name: "scraper/deep-locator-candidates
|
|
|
25
28
|
*/
|
|
26
29
|
const DEFAULT_DEEP_LOCATOR_CALL_TIMEOUT_MS = 10_000;
|
|
27
30
|
/**
|
|
28
|
-
* Total wall-clock budget for {@link resolveDeepLocatorCandidates}'s
|
|
29
|
-
* per-candidate `textContent()` enumeration loop
|
|
31
|
+
* Total wall-clock budget for {@link resolveDeepLocatorCandidates}'s legacy
|
|
32
|
+
* per-candidate `textContent()` enumeration loop — the fallback path taken
|
|
33
|
+
* when no batched frame-scoped scan is available (see
|
|
34
|
+
* {@link DeepLocatorTimeoutOptions.frameTarget}). A hop like `"*"` can match
|
|
30
35
|
* dozens of elements (65 observed against a live OOPIF) — each individually
|
|
31
36
|
* fast but settling, not hanging — so a per-call timeout alone doesn't bound
|
|
32
37
|
* the loop's total cost. Roughly half of `flow-runner.ts`'s
|
|
33
38
|
* `STEP_WATCHDOG_MS` (120s), leaving headroom for the rest of the attempt.
|
|
39
|
+
* The batched scan path completes in one round-trip and is never gated by
|
|
40
|
+
* this budget.
|
|
34
41
|
*/
|
|
35
42
|
const DEFAULT_DEEP_LOCATOR_ENUMERATION_BUDGET_MS = 60_000;
|
|
36
43
|
/**
|
|
@@ -60,7 +67,10 @@ const NEGATION_MARKERS = /\b(?:do\s+not|don't|never|avoid)\b/gi;
|
|
|
60
67
|
* reach. Mirrors the quoted-phrase extraction convention in
|
|
61
68
|
* `parseSelectStep`/`parseRadioStep` (`flow-runner.ts`) — same
|
|
62
69
|
* `/'([^']+)'/g` shape — but here we need ALL quoted phrases plus their
|
|
63
|
-
* polarity, not just one option/label pair.
|
|
70
|
+
* polarity, not just one option/label pair. Exported so `flow-runner.ts` can
|
|
71
|
+
* re-derive the exact same ranking {@link scoreCandidate} used to sort
|
|
72
|
+
* `deepLocatorCandidates`, to detect a tie for the top rank rather than
|
|
73
|
+
* duplicating this logic.
|
|
64
74
|
*/
|
|
65
75
|
function extractTaggedPhrases(instruction) {
|
|
66
76
|
const negationStarts = [...instruction.matchAll(NEGATION_MARKERS)].map((m) => m.index);
|
|
@@ -88,6 +98,9 @@ function normalize(text) {
|
|
|
88
98
|
* exact/substring match tiers, falling through to 0 for empty or
|
|
89
99
|
* unrelated text so a structural container with no accessible text can
|
|
90
100
|
* never outrank a candidate whose text actually matches the instruction.
|
|
101
|
+
* Exported so `flow-runner.ts` can detect a tie for the top rank among
|
|
102
|
+
* `resolveDeepLocatorCandidates`'s already-sorted output (see
|
|
103
|
+
* {@link extractTaggedPhrases}'s docblock).
|
|
91
104
|
*/
|
|
92
105
|
function scoreCandidate(accessibleText, phrases) {
|
|
93
106
|
const normalizedText = normalize(accessibleText);
|
|
@@ -103,6 +116,132 @@ function scoreCandidate(accessibleText, phrases) {
|
|
|
103
116
|
return 2;
|
|
104
117
|
return 0;
|
|
105
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Resolves the `FrameTarget` the batched scan should evaluate against:
|
|
121
|
+
* `timeoutOptions.frameTarget` when the caller already resolved one, else a
|
|
122
|
+
* single non-polling `probeAttachedFrameTarget(page, frameSelector)` pass
|
|
123
|
+
* (`frame-target.ts`) so existing call sites — which pass only a
|
|
124
|
+
* `frameSelector` string, not a `FrameTarget` — still get the batched fast
|
|
125
|
+
* path without themselves changing. Unlike a bare `resolveFrameTarget(page,
|
|
126
|
+
* frameSelector, { timeoutMs: 0 })` pass, the probe carries a real per-probe
|
|
127
|
+
* budget, so it can land against an already-attached frame under genuine CDP
|
|
128
|
+
* latency instead of always losing a zero-budget race. Returns `null` (never
|
|
129
|
+
* throws) when `frameSelector` is unset, the probe rejects (e.g. a fake
|
|
130
|
+
* `Page` in a legacy-path test lacking `evaluate`/`frames`), or nothing
|
|
131
|
+
* attaches within the probe's budget — each of those means "no frame seam
|
|
132
|
+
* available", and the caller degrades to the legacy per-candidate loop.
|
|
133
|
+
*/
|
|
134
|
+
async function resolveScanFrameTarget(page, frameSelector, timeoutOptions) {
|
|
135
|
+
if (timeoutOptions.frameTarget)
|
|
136
|
+
return timeoutOptions.frameTarget;
|
|
137
|
+
if (!frameSelector)
|
|
138
|
+
return null;
|
|
139
|
+
try {
|
|
140
|
+
return await (0, frame_target_1.probeAttachedFrameTarget)(page, frameSelector);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** Narrows a batched-scan evaluate result to {@link FrameCandidateScanResult}'s shape, guarding against a non-conforming payload (Issue #2's degrade-to-legacy-loop contract). */
|
|
147
|
+
function isFrameCandidateScanResult(entry) {
|
|
148
|
+
if (typeof entry !== "object" || entry === null)
|
|
149
|
+
return false;
|
|
150
|
+
const candidate = entry;
|
|
151
|
+
return (typeof candidate.index === "number" &&
|
|
152
|
+
typeof candidate.text === "string" &&
|
|
153
|
+
typeof candidate.visible === "boolean");
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Batched-scan fast path: one `frameTarget.evaluate(buildScanFrameCandidatesExpr(innerSelector))`
|
|
157
|
+
* round-trip replaces the legacy `count()` + N × `nth(i).textContent()` loop.
|
|
158
|
+
* Returns `null` (never throws) when no frame seam is available, the
|
|
159
|
+
* evaluate call rejects (missing seam, thrown error, or a watchdog timeout —
|
|
160
|
+
* `frameTarget.evaluate` already carries its own watchdog), the resolved
|
|
161
|
+
* payload doesn't conform to {@link FrameCandidateScanResult}[], or the raw
|
|
162
|
+
* scan matched zero elements — every one of those degrades the caller to the
|
|
163
|
+
* legacy loop instead of losing candidates. A raw zero-match scan is treated
|
|
164
|
+
* as inconclusive rather than authoritative because the scan expression's
|
|
165
|
+
* light-DOM `querySelectorAll` can't see into a shadow root the way
|
|
166
|
+
* `page.deepLocator()` (used by the legacy loop) can, so it must not be
|
|
167
|
+
* confused with the *visibility* filter below, which legitimately produces
|
|
168
|
+
* an empty result when the scan *did* match elements but every one of them
|
|
169
|
+
* is unrendered. Filters out `visible:false` entries (Issue #2: an
|
|
170
|
+
* unrendered node can never be the target of a real click) before the
|
|
171
|
+
* caller ranks what's left, logging at `warn` when candidates are dropped
|
|
172
|
+
* so a frame whose every node reports unrendered doesn't silently look like
|
|
173
|
+
* an empty frame.
|
|
174
|
+
*/
|
|
175
|
+
async function scanFrameCandidatesBatched(page, frameSelector, innerSelector, hopSelector, timeoutOptions) {
|
|
176
|
+
const frameTarget = await resolveScanFrameTarget(page, frameSelector, timeoutOptions);
|
|
177
|
+
if (!frameTarget)
|
|
178
|
+
return null;
|
|
179
|
+
let scanResults;
|
|
180
|
+
try {
|
|
181
|
+
scanResults = await frameTarget.evaluate((0, deep_locator_scan_1.buildScanFrameCandidatesExpr)(innerSelector));
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
logger.warn(`deepLocator batched scan for ${hopSelector} failed, degrading to per-candidate enumeration: ${(0, errors_1.toErrorMessage)(err)}`);
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
if (!Array.isArray(scanResults) || !scanResults.every(isFrameCandidateScanResult)) {
|
|
188
|
+
logger.warn(`deepLocator batched scan for ${hopSelector} returned a non-conforming payload, degrading to per-candidate enumeration`);
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
if (scanResults.length === 0) {
|
|
192
|
+
logger.warn(`deepLocator batched scan for ${hopSelector} matched zero elements, degrading to per-candidate enumeration in case a shadow-DOM control was missed`);
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
const visibleResults = scanResults.filter((entry) => entry.visible);
|
|
196
|
+
if (visibleResults.length < scanResults.length) {
|
|
197
|
+
logger.warn(`deepLocator batched scan for ${hopSelector} dropped ${scanResults.length - visibleResults.length} unrendered candidate(s)`);
|
|
198
|
+
}
|
|
199
|
+
return visibleResults.map((entry) => ({
|
|
200
|
+
index: entry.index,
|
|
201
|
+
selector: candidateSelector(hopSelector, entry.index),
|
|
202
|
+
accessibleText: entry.text.trim(),
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Legacy fallback: `count()` then one `nth(index).textContent()` round-trip
|
|
207
|
+
* per candidate, individually bounded by `callTimeoutMs` via
|
|
208
|
+
* {@link withWatchdog} and collectively bounded by `enumerationBudgetMs` so a
|
|
209
|
+
* hop with dozens of slow-but-settling elements (e.g. 65 candidates matched
|
|
210
|
+
* against a live OOPIF) can't rack up an unbounded total cost even when no
|
|
211
|
+
* single call hangs — the loop aborts early and returns whatever candidates
|
|
212
|
+
* it already resolved. Never throws: a `count()`/`textContent()` failure
|
|
213
|
+
* (detached frame, navigated-away element) or either call exceeding its
|
|
214
|
+
* watchdog budget degrades to `[]`/an empty `accessibleText`.
|
|
215
|
+
*/
|
|
216
|
+
async function enumerateCandidatesViaLegacyLoop(delegate, hopSelector, callTimeoutMs, enumerationBudgetMs) {
|
|
217
|
+
const count = await (0, watchdog_1.withWatchdog)(() => delegate.count(), {
|
|
218
|
+
timeoutMs: callTimeoutMs,
|
|
219
|
+
label: `deepLocator count() for ${hopSelector}`,
|
|
220
|
+
}).catch((err) => {
|
|
221
|
+
logger.warn(`deepLocator count() threw for ${hopSelector}: ${(0, errors_1.toErrorMessage)(err)}`);
|
|
222
|
+
return 0;
|
|
223
|
+
});
|
|
224
|
+
if (count === 0)
|
|
225
|
+
return [];
|
|
226
|
+
const candidates = [];
|
|
227
|
+
const enumerationDeadline = Date.now() + enumerationBudgetMs;
|
|
228
|
+
for (let index = 0; index < count; index++) {
|
|
229
|
+
if (Date.now() >= enumerationDeadline) {
|
|
230
|
+
logger.warn(`deepLocator enumeration for ${hopSelector} aborted after exceeding ${enumerationBudgetMs}ms budget at candidate ${index}/${count}`);
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
const accessibleText = await (0, watchdog_1.withWatchdog)(() => delegate.nth(index).textContent(), {
|
|
234
|
+
timeoutMs: callTimeoutMs,
|
|
235
|
+
label: `deepLocator textContent() for ${hopSelector} nth=${index}`,
|
|
236
|
+
}).catch(() => "");
|
|
237
|
+
candidates.push({
|
|
238
|
+
index,
|
|
239
|
+
selector: candidateSelector(hopSelector, index),
|
|
240
|
+
accessibleText: accessibleText.trim(),
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
return candidates;
|
|
244
|
+
}
|
|
106
245
|
/**
|
|
107
246
|
* Enumerates every element `page.deepLocator()` matches inside the frame
|
|
108
247
|
* scoped by `frameSelector`, ranked by relevance to `instruction` (highest
|
|
@@ -117,20 +256,25 @@ function scoreCandidate(accessibleText, phrases) {
|
|
|
117
256
|
* cascading through candidate sources can move on to the next technique
|
|
118
257
|
* instead of hanging the step forever.
|
|
119
258
|
*
|
|
259
|
+
* Prefers a single batched `evaluate` round-trip over the frame
|
|
260
|
+
* (see {@link scanFrameCandidatesBatched}) — collapsing the O(n) per-candidate
|
|
261
|
+
* `textContent()` round-trips that made a dense OOPIF form (371 candidates
|
|
262
|
+
* measured live) unresolvable inside any reasonable budget — and falls back
|
|
263
|
+
* to the legacy `count()` + per-candidate loop (see
|
|
264
|
+
* {@link enumerateCandidatesViaLegacyLoop}) when no frame seam is available,
|
|
265
|
+
* the batched scan fails, or the batched scan resolves to zero candidates —
|
|
266
|
+
* the batched expression's light-DOM `querySelectorAll` can't see into a
|
|
267
|
+
* shadow root the way `page.deepLocator()` can, so a zero-length scan is
|
|
268
|
+
* treated as inconclusive rather than authoritative. Pass an already-resolved
|
|
269
|
+
* {@link DeepLocatorTimeoutOptions.frameTarget} to use the fast path without
|
|
270
|
+
* paying for an internal re-resolution.
|
|
271
|
+
*
|
|
120
272
|
* Ranking exists because a hop like `"*"` matches every element inside a
|
|
121
273
|
* wizard iframe (html, body, every div, ...) — DOM order alone almost always
|
|
122
274
|
* puts a structural container first, not the control the step instruction
|
|
123
275
|
* actually names. Pass `instruction` as `null`/`undefined` (or omit it) to
|
|
124
276
|
* skip ranking and get delegate order — e.g. for pre-cascade reachability
|
|
125
277
|
* probes that only care whether the frame has ANY candidates.
|
|
126
|
-
*
|
|
127
|
-
* `count()` and each per-candidate `textContent()` are individually bounded
|
|
128
|
-
* by `timeoutOptions.callTimeoutMs` via {@link withWatchdog}; the enumeration
|
|
129
|
-
* loop as a whole is additionally bounded by `timeoutOptions.enumerationBudgetMs`
|
|
130
|
-
* so a hop with dozens of slow-but-settling elements (e.g. 65 candidates
|
|
131
|
-
* matched against a live OOPIF) can't rack up an unbounded total cost even
|
|
132
|
-
* when no single call hangs — the loop aborts early and returns whatever
|
|
133
|
-
* candidates it already resolved.
|
|
134
278
|
*/
|
|
135
279
|
async function resolveDeepLocatorCandidates(page, frameSelector, innerSelector, instruction, timeoutOptions = {}) {
|
|
136
280
|
const callTimeoutMs = timeoutOptions.callTimeoutMs ?? DEFAULT_DEEP_LOCATOR_CALL_TIMEOUT_MS;
|
|
@@ -141,32 +285,9 @@ async function resolveDeepLocatorCandidates(page, frameSelector, innerSelector,
|
|
|
141
285
|
logger.warn(`deepLocator() is unavailable on this page for ${hopSelector}`);
|
|
142
286
|
return [];
|
|
143
287
|
}
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}).catch((err) => {
|
|
148
|
-
logger.warn(`deepLocator count() threw for ${hopSelector}: ${(0, errors_1.toErrorMessage)(err)}`);
|
|
149
|
-
return 0;
|
|
150
|
-
});
|
|
151
|
-
if (count === 0)
|
|
152
|
-
return [];
|
|
153
|
-
const candidates = [];
|
|
154
|
-
const enumerationDeadline = Date.now() + enumerationBudgetMs;
|
|
155
|
-
for (let index = 0; index < count; index++) {
|
|
156
|
-
if (Date.now() >= enumerationDeadline) {
|
|
157
|
-
logger.warn(`deepLocator enumeration for ${hopSelector} aborted after exceeding ${enumerationBudgetMs}ms budget at candidate ${index}/${count}`);
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
const accessibleText = await (0, watchdog_1.withWatchdog)(() => delegate.nth(index).textContent(), {
|
|
161
|
-
timeoutMs: callTimeoutMs,
|
|
162
|
-
label: `deepLocator textContent() for ${hopSelector} nth=${index}`,
|
|
163
|
-
}).catch(() => "");
|
|
164
|
-
candidates.push({
|
|
165
|
-
index,
|
|
166
|
-
selector: candidateSelector(hopSelector, index),
|
|
167
|
-
accessibleText: accessibleText.trim(),
|
|
168
|
-
});
|
|
169
|
-
}
|
|
288
|
+
const batchedCandidates = await scanFrameCandidatesBatched(page, frameSelector, innerSelector, hopSelector, timeoutOptions);
|
|
289
|
+
const candidates = batchedCandidates ??
|
|
290
|
+
(await enumerateCandidatesViaLegacyLoop(delegate, hopSelector, callTimeoutMs, enumerationBudgetMs));
|
|
170
291
|
if (!instruction)
|
|
171
292
|
return candidates;
|
|
172
293
|
const phrases = extractTaggedPhrases(instruction);
|
|
@@ -181,26 +302,97 @@ async function resolveDeepLocatorCandidates(page, frameSelector, innerSelector,
|
|
|
181
302
|
.sort((a, b) => b.score - a.score || a.originalOrder - b.originalOrder)
|
|
182
303
|
.map(({ candidate }) => candidate);
|
|
183
304
|
}
|
|
305
|
+
/** Narrows a batched-click evaluate result to {@link FrameCandidateClickResult}'s shape, guarding against a non-conforming payload (the same degrade-to-legacy-loop contract {@link isFrameCandidateScanResult} enforces for the scan side). */
|
|
306
|
+
function isFrameCandidateClickResult(entry) {
|
|
307
|
+
if (typeof entry !== "object" || entry === null)
|
|
308
|
+
return false;
|
|
309
|
+
const result = entry;
|
|
310
|
+
if (typeof result.clicked !== "boolean")
|
|
311
|
+
return false;
|
|
312
|
+
if (result.clicked)
|
|
313
|
+
return true;
|
|
314
|
+
return result.reason === "out-of-range" || result.reason === "not-actionable";
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Batched-click fast path: one `frameTarget.evaluate(buildClickFrameCandidateExpr(innerSelector, index))`
|
|
318
|
+
* round-trip replaces the legacy `nth(index).click()`, which (per
|
|
319
|
+
* {@link DEEP_LOCATOR_CLICK_INDEX_ROUND_TRIP_MS}'s docs) pays `index + 1`
|
|
320
|
+
* serial CDP round-trips through `resolveAtIndex`. Returns `null` (never
|
|
321
|
+
* throws) when no frame seam is available, the evaluate call rejects, or the
|
|
322
|
+
* resolved payload doesn't conform to {@link FrameCandidateClickResult} —
|
|
323
|
+
* every one of those degrades the caller to the legacy `nth(index).click()`
|
|
324
|
+
* path instead of losing the click, mirroring {@link scanFrameCandidatesBatched}'s
|
|
325
|
+
* degrade contract on the enumeration side.
|
|
326
|
+
*/
|
|
327
|
+
async function clickCandidateBatched(page, frameSelector, innerSelector, index, hopSelector, timeoutOptions) {
|
|
328
|
+
const frameTarget = await resolveScanFrameTarget(page, frameSelector, timeoutOptions);
|
|
329
|
+
if (!frameTarget)
|
|
330
|
+
return null;
|
|
331
|
+
let clickResult;
|
|
332
|
+
try {
|
|
333
|
+
clickResult = await frameTarget.evaluate((0, deep_locator_scan_1.buildClickFrameCandidateExpr)(innerSelector, index));
|
|
334
|
+
}
|
|
335
|
+
catch (err) {
|
|
336
|
+
logger.warn(`deepLocator batched click for ${hopSelector} nth=${index} failed, degrading to delegate click: ${(0, errors_1.toErrorMessage)(err)}`);
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
if (!isFrameCandidateClickResult(clickResult)) {
|
|
340
|
+
logger.warn(`deepLocator batched click for ${hopSelector} nth=${index} returned a non-conforming payload, degrading to delegate click`);
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
return clickResult;
|
|
344
|
+
}
|
|
184
345
|
/**
|
|
185
346
|
* Clicks the candidate at `index` inside the frame scoped by `frameSelector`,
|
|
186
347
|
* re-deriving the same hop selector `resolveDeepLocatorCandidates` used
|
|
187
348
|
* rather than trusting a caller-supplied `xpath=` string, so the two stay in
|
|
188
349
|
* lockstep even if a candidate's `selector` field is only ever used for
|
|
189
|
-
* display/logging.
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
* the same
|
|
196
|
-
*
|
|
350
|
+
* display/logging. Resolves on success and rejects on failure — the caller
|
|
351
|
+
* must infer the outcome from whether this call throws plus their own
|
|
352
|
+
* downstream DOM verification, not from a returned boolean.
|
|
353
|
+
*
|
|
354
|
+
* Prefers the one-round-trip {@link clickCandidateBatched} fast path when a
|
|
355
|
+
* frame seam is available (`timeoutOptions.frameTarget`, or one resolved
|
|
356
|
+
* internally the same way {@link scanFrameCandidatesBatched} does), falling
|
|
357
|
+
* back to the legacy `DeepLocatorDelegate.click()` — which never reports
|
|
358
|
+
* success via a return value, only via not rejecting — when no seam is
|
|
359
|
+
* available, the batched call fails/degrades, or it reports the index
|
|
360
|
+
* stale (`reason: "out-of-range"`). A batched `reason: "not-actionable"`
|
|
361
|
+
* result throws an error {@link isNodeNotActionableError} classifies, the
|
|
362
|
+
* same contract a real click against an unrendered node rejects with via the
|
|
363
|
+
* CDP `-32000 Node does not have a layout object` error, so a caller
|
|
364
|
+
* cascading through candidates treats both paths identically.
|
|
365
|
+
*
|
|
366
|
+
* The legacy fallback's watchdog scales with `index` (see
|
|
367
|
+
* {@link DEEP_LOCATOR_CLICK_INDEX_ROUND_TRIP_MS}) so a legitimately-reachable
|
|
368
|
+
* candidate isn't killed by a budget sized for a single round-trip. A
|
|
369
|
+
* `click()` that still exceeds that scaled budget (a wedged CDP round-trip
|
|
370
|
+
* against a racy OOPIF frame) rejects with a `WatchdogTimeoutError` the same
|
|
371
|
+
* as any other failure, preserving the "rejects on failure" contract instead
|
|
372
|
+
* of hanging the caller forever.
|
|
197
373
|
*/
|
|
198
374
|
async function clickDeepLocatorCandidate(page, frameSelector, innerSelector, index, timeoutOptions = {}) {
|
|
199
375
|
const callTimeoutMs = timeoutOptions.callTimeoutMs ?? DEFAULT_DEEP_LOCATOR_CALL_TIMEOUT_MS;
|
|
200
376
|
const hopSelector = (0, frame_target_1.buildHopSelector)(frameSelector, innerSelector);
|
|
377
|
+
const batchedResult = await clickCandidateBatched(page, frameSelector, innerSelector, index, hopSelector, timeoutOptions);
|
|
378
|
+
if (batchedResult?.clicked)
|
|
379
|
+
return;
|
|
380
|
+
if (batchedResult?.reason === "not-actionable") {
|
|
381
|
+
throw new Error(`deepLocator batched click for ${hopSelector} nth=${index}: node does not have a layout object`);
|
|
382
|
+
}
|
|
383
|
+
const scaledCallTimeoutMs = callTimeoutMs + index * deep_locator_scan_1.DEEP_LOCATOR_CLICK_INDEX_ROUND_TRIP_MS;
|
|
201
384
|
await (0, watchdog_1.withWatchdog)(() => page.deepLocator(hopSelector).nth(index).click(), {
|
|
202
|
-
timeoutMs:
|
|
385
|
+
timeoutMs: scaledCallTimeoutMs,
|
|
203
386
|
label: `deepLocator click() for ${hopSelector} nth=${index}`,
|
|
204
387
|
});
|
|
205
388
|
}
|
|
389
|
+
/**
|
|
390
|
+
* `fillDeepLocatorCandidate`/`selectDeepLocatorCandidateOption` live in
|
|
391
|
+
* `deep-locator-actuate.ts`, not here — that module's `writeAndVerify` wraps
|
|
392
|
+
* the same `buildHopSelector`/`withWatchdog` contract this file's
|
|
393
|
+
* `clickDeepLocatorCandidate` uses, but additionally reads the write back
|
|
394
|
+
* through `inputValue()` before reporting success (`verifyDomEffect` can't
|
|
395
|
+
* resolve a `deeplocator=` selector, so the read-back is the only
|
|
396
|
+
* confirmation a fill/select actually landed).
|
|
397
|
+
*/
|
|
206
398
|
//# sourceMappingURL=deep-locator-candidates.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deep-locator-candidates.js","sourceRoot":"","sources":["../../src/scraper/deep-locator-candidates.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG
|
|
1
|
+
{"version":3,"file":"deep-locator-candidates.js","sourceRoot":"","sources":["../../src/scraper/deep-locator-candidates.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Candidate-click cascade for `deep-locator-candidates.ts`'s ranked
|
|
3
|
+
* `DeepLocatorCandidate[]`: `flow-runner.ts` used to click only
|
|
4
|
+
* `deepLocatorCandidates[0]` (see the bug report's Issue #2), so a top pick
|
|
5
|
+
* that turned out to be unrendered — a click rejecting with the CDP `-32000
|
|
6
|
+
* Node does not have a layout object` error `isNodeNotActionableError`
|
|
7
|
+
* classifies — scored the whole attempt as a failed click instead of trying
|
|
8
|
+
* the next-ranked candidate. {@link clickFirstActionableCandidate} walks the
|
|
9
|
+
* list instead, so a not-actionable rejection or a caller-supplied deny hit
|
|
10
|
+
* costs only that one candidate, not the attempt.
|
|
11
|
+
*
|
|
12
|
+
* Extracted out of `flow-runner.ts` so the walk is unit-testable against a
|
|
13
|
+
* stub click function — no fake `Page`, no Stagehand — and so
|
|
14
|
+
* `flow-runner.ts`'s own edit stays a call swap (see `bugfix-005`).
|
|
15
|
+
*
|
|
16
|
+
* NOT IMPLEMENTED: escalating to Stagehand's `Locator.sendClickEvent()` (a
|
|
17
|
+
* layout-free actuation) as a rescue for a candidate that's genuinely never
|
|
18
|
+
* going to render. The report asks for skip-and-continue, not a rescue path.
|
|
19
|
+
*/
|
|
20
|
+
import type { DeepLocatorCandidate } from "../scraper/deep-locator-candidates";
|
|
21
|
+
/**
|
|
22
|
+
* Upper bound on how many candidates a single {@link clickFirstActionableCandidate}
|
|
23
|
+
* call will actually click. Denied candidates are skipped without a click and
|
|
24
|
+
* don't count against this cap — only a real `click()` invocation does — so a
|
|
25
|
+
* dense post-filter list (a handful of controls, per bugfix-002's visibility
|
|
26
|
+
* filter and bugfix-005's interactive scoping) still resolves in one or two
|
|
27
|
+
* attempts, while a pathological list (371 candidates, the uchealth-7
|
|
28
|
+
* measurement) can't spend the whole step budget clicking its way through
|
|
29
|
+
* every entry.
|
|
30
|
+
*/
|
|
31
|
+
export declare const DEFAULT_CLICK_CANDIDATE_ATTEMPT_CAP = 5;
|
|
32
|
+
/**
|
|
33
|
+
* Attempts to click one candidate. Resolves on a successful click; rejects on
|
|
34
|
+
* failure — the same "no boolean return, infer from throw" contract
|
|
35
|
+
* `clickDeepLocatorCandidate` (`deep-locator-candidates.ts`) already uses, so
|
|
36
|
+
* a caller can inject that function directly (partially applied over
|
|
37
|
+
* `page`/`frameSelector`/`innerSelector`) without an adapter.
|
|
38
|
+
*/
|
|
39
|
+
export type ClickCandidateFn = (candidate: DeepLocatorCandidate) => Promise<void>;
|
|
40
|
+
/** Refuses a candidate before it's clicked — e.g. `flow-runner.ts`'s `isWizardExitAction` deny-list, injected so this module stays site-agnostic. */
|
|
41
|
+
export type DenyCandidatePredicate = (candidate: DeepLocatorCandidate) => boolean;
|
|
42
|
+
export interface ClickCandidateCascadeOptions {
|
|
43
|
+
/** Skips a candidate without clicking it when this returns `true`. Optional — omit to click every non-not-actionable candidate in rank order. */
|
|
44
|
+
denyCandidate?: DenyCandidatePredicate;
|
|
45
|
+
/** Overrides {@link DEFAULT_CLICK_CANDIDATE_ATTEMPT_CAP}. */
|
|
46
|
+
attemptCap?: number;
|
|
47
|
+
}
|
|
48
|
+
/** Outcome of a {@link clickFirstActionableCandidate} walk. */
|
|
49
|
+
export interface ClickCandidateCascadeOutcome {
|
|
50
|
+
/** `true` only when a candidate's click resolved (didn't reject). */
|
|
51
|
+
clicked: boolean;
|
|
52
|
+
/** The candidate that was clicked, or `null` when the walk exhausted the list/cap without a successful click. */
|
|
53
|
+
candidate: DeepLocatorCandidate | null;
|
|
54
|
+
/** Selectors of every candidate this walk actually clicked (attempted), in attempt order — denied candidates are excluded since they were never clicked. */
|
|
55
|
+
triedSelectors: string[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Walks `candidates` in rank order, clicking each via `click` until one
|
|
59
|
+
* succeeds. A candidate refused by `denyCandidate` is skipped without being
|
|
60
|
+
* clicked and doesn't count against `attemptCap`. A click rejection that
|
|
61
|
+
* {@link isNodeNotActionableError} classifies as "not actionable" (unrendered
|
|
62
|
+
* node) is treated as "skip this candidate, try the next"; any other
|
|
63
|
+
* rejection stops the walk immediately and rethrows, so a caller doesn't
|
|
64
|
+
* silently burn the remaining candidates on an error that had nothing to do
|
|
65
|
+
* with actionability (a detached frame, a wedged click's
|
|
66
|
+
* `WatchdogTimeoutError`). Never throws on exhaustion — an exhausted list
|
|
67
|
+
* (every candidate denied or not-actionable, or the cap reached) resolves to
|
|
68
|
+
* `{ clicked: false, candidate: null, triedSelectors }` instead.
|
|
69
|
+
*/
|
|
70
|
+
export declare function clickFirstActionableCandidate(candidates: readonly DeepLocatorCandidate[], click: ClickCandidateFn, options?: ClickCandidateCascadeOptions): Promise<ClickCandidateCascadeOutcome>;
|
|
71
|
+
//# sourceMappingURL=deep-locator-click.d.ts.map
|