@enricai/barnacle 1.9.11 → 1.10.0
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/scraper/browser-click-expr.d.ts +44 -0
- package/dist/scraper/browser-click-expr.d.ts.map +1 -0
- package/dist/scraper/browser-click-expr.js +69 -0
- package/dist/scraper/browser-click-expr.js.map +1 -0
- package/dist/scraper/deep-locator-candidates.d.ts +85 -23
- package/dist/scraper/deep-locator-candidates.d.ts.map +1 -1
- package/dist/scraper/deep-locator-candidates.js +120 -40
- package/dist/scraper/deep-locator-candidates.js.map +1 -1
- package/dist/scraper/deep-locator-click.d.ts +40 -2
- package/dist/scraper/deep-locator-click.d.ts.map +1 -1
- package/dist/scraper/deep-locator-click.js +29 -3
- package/dist/scraper/deep-locator-click.js.map +1 -1
- package/dist/scraper/deep-locator-fake.d.ts.map +1 -1
- package/dist/scraper/deep-locator-fake.js +1 -0
- package/dist/scraper/deep-locator-fake.js.map +1 -1
- package/dist/scraper/deep-locator-scan.d.ts +30 -16
- package/dist/scraper/deep-locator-scan.d.ts.map +1 -1
- package/dist/scraper/deep-locator-scan.js +65 -25
- package/dist/scraper/deep-locator-scan.js.map +1 -1
- package/dist/scraper/deep-query.d.ts +5 -5
- package/dist/scraper/deep-query.d.ts.map +1 -1
- package/dist/scraper/deep-query.js +7 -9
- package/dist/scraper/deep-query.js.map +1 -1
- package/dist/scraper/flow-runner.d.ts +7 -7
- package/dist/scraper/flow-runner.d.ts.map +1 -1
- package/dist/scraper/flow-runner.js +190 -96
- package/dist/scraper/flow-runner.js.map +1 -1
- package/dist/scraper/submit-control.d.ts +5 -3
- package/dist/scraper/submit-control.d.ts.map +1 -1
- package/dist/scraper/submit-control.js +7 -7
- package/dist/scraper/submit-control.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared browser-context click-activation snippet. Three primitives —
|
|
3
|
+
* `buildClickFrameCandidateExpr` (`deep-locator-scan.ts`),
|
|
4
|
+
* `buildDeepSubmitClickExpr` (`deep-query.ts`), and `buildClickByDeepIndexExpr`
|
|
5
|
+
* (`submit-control.ts`) — each need to activate a resolved element from inside
|
|
6
|
+
* a `page.evaluate`/`Frame.evaluate` string. They used to dispatch bare
|
|
7
|
+
* `new Event("mousedown"/"mouseup"/"click", { bubbles: true })`, which BUBBLES
|
|
8
|
+
* (so analytics/telemetry listeners fire) but is NOT a `MouseEvent`/`PointerEvent`
|
|
9
|
+
* instance — React's synthetic-event system and design-system widgets (Base Web,
|
|
10
|
+
* etc.) refuse a bare `Event` as a real user activation. The observed symptom on
|
|
11
|
+
* a markerless multi-select wizard: an option/toggle click reached the element
|
|
12
|
+
* and its analytics pixel recorded the click, yet the widget's "N selected"
|
|
13
|
+
* counter stayed flat because the selection never registered. This snippet
|
|
14
|
+
* dispatches a realistic `PointerEvent`/`MouseEvent` gesture
|
|
15
|
+
* (`pointerdown → mousedown → pointerup → mouseup`) and then triggers the
|
|
16
|
+
* activation with EXACTLY ONE click: native `el.click()` when the element has it
|
|
17
|
+
* (the trusted-path activation a `<button>` toggle listens for), else a single
|
|
18
|
+
* synthetic `MouseEvent("click")`. It must never do BOTH — a synthetic `click`
|
|
19
|
+
* dispatch plus a native `click()` fires a toggle handler twice
|
|
20
|
+
* (select → deselect = net zero), re-creating the very phantom this snippet
|
|
21
|
+
* exists to cure. Kept as a single interpolated string so all four primitives
|
|
22
|
+
* stay identical (DRY) rather than drifting copies.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Emits a browser-context statement block that activates the element bound to
|
|
26
|
+
* `elVar`: focus, then a `pointerdown → mousedown → pointerup → mouseup` gesture
|
|
27
|
+
* using real `MouseEvent`/`PointerEvent` constructors (feature-detecting
|
|
28
|
+
* `PointerEvent`, since a non-pointer environment still has `MouseEvent`),
|
|
29
|
+
* followed by EXACTLY ONE click activation.
|
|
30
|
+
*
|
|
31
|
+
* `elVar` is interpolated verbatim as an already-in-scope identifier — the
|
|
32
|
+
* caller resolves the element (e.g. `const el = matches[index]`) before
|
|
33
|
+
* interpolating this block. The single click is delivered via native
|
|
34
|
+
* `elVar.click()` when the element exposes one (every `HTMLElement` does; it is
|
|
35
|
+
* the trusted-path activation that drives React/Base Web toggle state), and via
|
|
36
|
+
* one synthetic `MouseEvent("click")` ONLY as the `else` for a non-`HTMLElement`
|
|
37
|
+
* that has no native `click()`. The two paths are mutually exclusive on purpose:
|
|
38
|
+
* dispatching a synthetic `click` AND calling native `click()` would fire the
|
|
39
|
+
* element's handler twice, and on a toggle that is select → deselect = net zero.
|
|
40
|
+
* The gesture events (down/up) are not click activations, so they never
|
|
41
|
+
* double-fire the handler.
|
|
42
|
+
*/
|
|
43
|
+
export declare function clickActivationExpr(elVar: string): string;
|
|
44
|
+
//# sourceMappingURL=browser-click-expr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-click-expr.d.ts","sourceRoot":"","sources":["../../src/scraper/browser-click-expr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAsBzD"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared browser-context click-activation snippet. Three primitives —
|
|
4
|
+
* `buildClickFrameCandidateExpr` (`deep-locator-scan.ts`),
|
|
5
|
+
* `buildDeepSubmitClickExpr` (`deep-query.ts`), and `buildClickByDeepIndexExpr`
|
|
6
|
+
* (`submit-control.ts`) — each need to activate a resolved element from inside
|
|
7
|
+
* a `page.evaluate`/`Frame.evaluate` string. They used to dispatch bare
|
|
8
|
+
* `new Event("mousedown"/"mouseup"/"click", { bubbles: true })`, which BUBBLES
|
|
9
|
+
* (so analytics/telemetry listeners fire) but is NOT a `MouseEvent`/`PointerEvent`
|
|
10
|
+
* instance — React's synthetic-event system and design-system widgets (Base Web,
|
|
11
|
+
* etc.) refuse a bare `Event` as a real user activation. The observed symptom on
|
|
12
|
+
* a markerless multi-select wizard: an option/toggle click reached the element
|
|
13
|
+
* and its analytics pixel recorded the click, yet the widget's "N selected"
|
|
14
|
+
* counter stayed flat because the selection never registered. This snippet
|
|
15
|
+
* dispatches a realistic `PointerEvent`/`MouseEvent` gesture
|
|
16
|
+
* (`pointerdown → mousedown → pointerup → mouseup`) and then triggers the
|
|
17
|
+
* activation with EXACTLY ONE click: native `el.click()` when the element has it
|
|
18
|
+
* (the trusted-path activation a `<button>` toggle listens for), else a single
|
|
19
|
+
* synthetic `MouseEvent("click")`. It must never do BOTH — a synthetic `click`
|
|
20
|
+
* dispatch plus a native `click()` fires a toggle handler twice
|
|
21
|
+
* (select → deselect = net zero), re-creating the very phantom this snippet
|
|
22
|
+
* exists to cure. Kept as a single interpolated string so all four primitives
|
|
23
|
+
* stay identical (DRY) rather than drifting copies.
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.clickActivationExpr = clickActivationExpr;
|
|
27
|
+
/**
|
|
28
|
+
* Emits a browser-context statement block that activates the element bound to
|
|
29
|
+
* `elVar`: focus, then a `pointerdown → mousedown → pointerup → mouseup` gesture
|
|
30
|
+
* using real `MouseEvent`/`PointerEvent` constructors (feature-detecting
|
|
31
|
+
* `PointerEvent`, since a non-pointer environment still has `MouseEvent`),
|
|
32
|
+
* followed by EXACTLY ONE click activation.
|
|
33
|
+
*
|
|
34
|
+
* `elVar` is interpolated verbatim as an already-in-scope identifier — the
|
|
35
|
+
* caller resolves the element (e.g. `const el = matches[index]`) before
|
|
36
|
+
* interpolating this block. The single click is delivered via native
|
|
37
|
+
* `elVar.click()` when the element exposes one (every `HTMLElement` does; it is
|
|
38
|
+
* the trusted-path activation that drives React/Base Web toggle state), and via
|
|
39
|
+
* one synthetic `MouseEvent("click")` ONLY as the `else` for a non-`HTMLElement`
|
|
40
|
+
* that has no native `click()`. The two paths are mutually exclusive on purpose:
|
|
41
|
+
* dispatching a synthetic `click` AND calling native `click()` would fire the
|
|
42
|
+
* element's handler twice, and on a toggle that is select → deselect = net zero.
|
|
43
|
+
* The gesture events (down/up) are not click activations, so they never
|
|
44
|
+
* double-fire the handler.
|
|
45
|
+
*/
|
|
46
|
+
function clickActivationExpr(elVar) {
|
|
47
|
+
return `{
|
|
48
|
+
if (typeof ${elVar}.focus === "function") { try { ${elVar}.focus(); } catch (e) {} }
|
|
49
|
+
const __ceOpts = { bubbles: true, cancelable: true, composed: true, view: (typeof window !== "undefined" ? window : undefined), button: 0 };
|
|
50
|
+
const __mouse = (type, buttons) => {
|
|
51
|
+
try { return new MouseEvent(type, Object.assign({}, __ceOpts, { buttons: buttons })); }
|
|
52
|
+
catch (e) { return new Event(type, { bubbles: true, cancelable: true }); }
|
|
53
|
+
};
|
|
54
|
+
const __pointer = (type, buttons) => {
|
|
55
|
+
if (typeof PointerEvent === "function") {
|
|
56
|
+
try { return new PointerEvent(type, Object.assign({}, __ceOpts, { buttons: buttons, pointerType: "mouse", isPrimary: true })); }
|
|
57
|
+
catch (e) {}
|
|
58
|
+
}
|
|
59
|
+
return __mouse(type, buttons);
|
|
60
|
+
};
|
|
61
|
+
${elVar}.dispatchEvent(__pointer("pointerdown", 1));
|
|
62
|
+
${elVar}.dispatchEvent(__mouse("mousedown", 1));
|
|
63
|
+
${elVar}.dispatchEvent(__pointer("pointerup", 0));
|
|
64
|
+
${elVar}.dispatchEvent(__mouse("mouseup", 0));
|
|
65
|
+
if (typeof ${elVar}.click === "function") { try { ${elVar}.click(); } catch (e) {} }
|
|
66
|
+
else { ${elVar}.dispatchEvent(__mouse("click", 0)); }
|
|
67
|
+
}`;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=browser-click-expr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-click-expr.js","sourceRoot":"","sources":["../../src/scraper/browser-click-expr.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;;;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,6BAAoC,KAAa;IAC/C,OAAO;iBACQ,KAAK,kCAAkC,KAAK;;;;;;;;;;;;;MAavD,KAAK;MACL,KAAK;MACL,KAAK;MACL,KAAK;iBACM,KAAK,kCAAkC,KAAK;aAChD,KAAK;IACd,CAAC;AACL,CAAC"}
|
|
@@ -34,6 +34,21 @@ export interface DeepLocatorTimeoutOptions {
|
|
|
34
34
|
* probe.
|
|
35
35
|
*/
|
|
36
36
|
frameTarget?: FrameTarget;
|
|
37
|
+
/**
|
|
38
|
+
* Forces {@link clickDeepLocatorCandidate} to skip the batched in-page
|
|
39
|
+
* synthetic click ({@link buildClickFrameCandidateExpr}, `isTrusted=false`)
|
|
40
|
+
* and activate ONLY via the trusted CDP `deepLocator().nth(index).click()`
|
|
41
|
+
* (`Input.dispatchMouseEvent`, `isTrusted=true`). The synthetic fast path is
|
|
42
|
+
* the default because it collapses the `index + 1` per-index resolve
|
|
43
|
+
* round-trips; a caller sets this ONLY on the phantom-driven retry, where the
|
|
44
|
+
* prior synthetic click already fired without effect and the per-index cost
|
|
45
|
+
* is worth paying to deliver a click a React/design-system handler accepts.
|
|
46
|
+
* Omitted/false everywhere else so throughput is unchanged for the common
|
|
47
|
+
* case. Does NOT suppress the not-actionable probe: an unrendered node still
|
|
48
|
+
* throws {@link isNodeNotActionableError} rather than paying a scaled-timeout
|
|
49
|
+
* CDP click.
|
|
50
|
+
*/
|
|
51
|
+
preferTrustedClick?: boolean;
|
|
37
52
|
}
|
|
38
53
|
/**
|
|
39
54
|
* One candidate element `page.deepLocator()` resolved inside a scoped frame.
|
|
@@ -59,6 +74,15 @@ export interface DeepLocatorCandidate {
|
|
|
59
74
|
* enumeration fallback.
|
|
60
75
|
*/
|
|
61
76
|
accessibleText: string;
|
|
77
|
+
/**
|
|
78
|
+
* `true` when the batched scan flagged this candidate as a navigation/advance
|
|
79
|
+
* control (BACK/Next/Cancel — see {@link IS_NAV_CANDIDATE_EXPR}). Feeds
|
|
80
|
+
* {@link scoreCandidate}'s nav-demotion tier so a "select '<option>'" step
|
|
81
|
+
* never resolves to the wizard's back/next button. Always `false` on the
|
|
82
|
+
* legacy per-candidate path (which can't read `data-testid`); there
|
|
83
|
+
* {@link scoreCandidate} re-derives the text-based portion of the nav check.
|
|
84
|
+
*/
|
|
85
|
+
isNav: boolean;
|
|
62
86
|
}
|
|
63
87
|
/** One quoted phrase from an instruction, tagged with whether a negation marker (`NEGATION_MARKERS`) governs it. See {@link extractTaggedPhrases}. */
|
|
64
88
|
export interface TaggedPhrase {
|
|
@@ -82,18 +106,41 @@ export interface TaggedPhrase {
|
|
|
82
106
|
export declare function extractTaggedPhrases(instruction: string): TaggedPhrase[];
|
|
83
107
|
/**
|
|
84
108
|
* Scores one candidate's relevance to `instruction`'s tagged phrases, higher
|
|
85
|
-
* is more relevant. Parallels `submit-control.ts`'s tiered-ranking shape
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
109
|
+
* is more relevant. Parallels `submit-control.ts`'s tiered-ranking shape but
|
|
110
|
+
* adds two tiers the markerless-widget bug report needs: a nav/back demotion
|
|
111
|
+
* (so a "select '<option>'" step never lands on the wizard's BACK/Next button,
|
|
112
|
+
* whose own text may exact-match nothing OR be empty) and an ends-with tier (so
|
|
113
|
+
* an option whose accessible name is polluted by a leading icon-glyph name —
|
|
114
|
+
* an icon-font/`<svg><title>` prefix rendering as `"<IconName><Label>"` — still
|
|
115
|
+
* beats a bare exact-text decoy by matching on the trailing label).
|
|
116
|
+
*
|
|
117
|
+
* Strict-priority tiers, first match wins:
|
|
118
|
+
* - negated-phrase match → `-2` (worst: a decoy sibling is worse than an
|
|
119
|
+
* unrelated structural node — the invariant the original tiering established),
|
|
120
|
+
* - exact positive → `4`,
|
|
121
|
+
* - ends-with a positive → `3` (the icon-glyph suffix case),
|
|
122
|
+
* - contains a positive (either direction) → `2`,
|
|
123
|
+
* - nav/back control with NO positive textual match → `-1`,
|
|
124
|
+
* - else → `0`.
|
|
125
|
+
*
|
|
126
|
+
* Crucially, the nav/back demotion sits BELOW the positive tiers, not above
|
|
127
|
+
* them: it only sinks a candidate that would otherwise score `0` (no phrase
|
|
128
|
+
* match at all) — the empty BACK button, or a bare "Next"/"Cancel" chrome
|
|
129
|
+
* button the instruction doesn't name. A candidate that positively matches the
|
|
130
|
+
* instruction phrase — exact, ends-with, or contains — is the control the step
|
|
131
|
+
* named and keeps its positive score even if its label happens to contain a nav
|
|
132
|
+
* word (`select 'Next'` → the "Next" option scores 4; "primary Next" whose step
|
|
133
|
+
* quotes 'Next' scores 2 via containment). This makes the demotion a tie-breaker
|
|
134
|
+
* against chrome, never an override of a real target.
|
|
135
|
+
*
|
|
136
|
+
* `isNav` comes from the batched scan (`data-testid` + empty-text signals it
|
|
137
|
+
* alone can see); the text-word portion is re-derived here via
|
|
138
|
+
* {@link matchesNavText} so the legacy per-candidate path (always `isNav=false`)
|
|
139
|
+
* still demotes a bare "Back"/"Next". Exported so `flow-runner.ts` can detect a
|
|
140
|
+
* tie for the top rank among `resolveDeepLocatorCandidates`'s already-sorted
|
|
141
|
+
* output (see {@link extractTaggedPhrases}'s docblock).
|
|
95
142
|
*/
|
|
96
|
-
export declare function scoreCandidate(accessibleText: string, phrases: TaggedPhrase[]): number;
|
|
143
|
+
export declare function scoreCandidate(accessibleText: string, phrases: TaggedPhrase[], isNav?: boolean): number;
|
|
97
144
|
/**
|
|
98
145
|
* Enumerates every element `page.deepLocator()` matches inside the frame
|
|
99
146
|
* scoped by `frameSelector`, ranked by relevance to `instruction` (highest
|
|
@@ -138,19 +185,34 @@ export declare function resolveDeepLocatorCandidates(page: Page, frameSelector:
|
|
|
138
185
|
* must infer the outcome from whether this call throws plus their own
|
|
139
186
|
* downstream DOM verification, not from a returned boolean.
|
|
140
187
|
*
|
|
141
|
-
*
|
|
142
|
-
* frame seam is available (`timeoutOptions.frameTarget`, or one
|
|
143
|
-
* internally the same way {@link scanFrameCandidatesBatched} does)
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
188
|
+
* By default, prefers the one-round-trip {@link clickCandidateBatched} fast
|
|
189
|
+
* path when a frame seam is available (`timeoutOptions.frameTarget`, or one
|
|
190
|
+
* resolved internally the same way {@link scanFrameCandidatesBatched} does):
|
|
191
|
+
* that batched click is a SYNTHETIC (`isTrusted=false`) activation, but it
|
|
192
|
+
* collapses the `index + 1` serial `nth(index)` resolve round-trips and works
|
|
193
|
+
* for the overwhelmingly common control. It falls back to the trusted
|
|
194
|
+
* `deepLocator().nth(index).click()` (understudy `Input.dispatchMouseEvent`,
|
|
195
|
+
* `isTrusted=true`) — which never reports success via a return value, only via
|
|
196
|
+
* not rejecting — when no seam is available, the batched call fails/degrades, or
|
|
197
|
+
* it reports the index stale (`reason: "out-of-range"`). A batched
|
|
198
|
+
* `reason: "not-actionable"` result throws an error {@link isNodeNotActionableError}
|
|
199
|
+
* classifies, the same contract a real click against an unrendered node rejects
|
|
200
|
+
* with via the CDP `-32000 Node does not have a layout object` error, so a
|
|
201
|
+
* caller cascading through candidates treats both paths identically.
|
|
202
|
+
*
|
|
203
|
+
* When `timeoutOptions.preferTrustedClick` is set, the synthetic batched click
|
|
204
|
+
* is SKIPPED entirely and activation goes straight to the trusted CDP
|
|
205
|
+
* `nth(index).click()` — the escalation a phantom-driven retry uses after a
|
|
206
|
+
* synthetic click fired without effect (a React/design-system handler that
|
|
207
|
+
* ignores untrusted activation). A cheap not-actionable probe still runs first
|
|
208
|
+
* so an unrendered candidate is skipped without paying the scaled-timeout CDP
|
|
209
|
+
* click.
|
|
210
|
+
*
|
|
211
|
+
* Resolves on success and rejects on failure — the caller must infer the
|
|
212
|
+
* outcome from whether this call throws plus their own downstream DOM
|
|
213
|
+
* verification, not from a returned boolean.
|
|
152
214
|
*
|
|
153
|
-
* The
|
|
215
|
+
* The trusted-fallback watchdog scales with `index` (see
|
|
154
216
|
* {@link DEEP_LOCATOR_CLICK_INDEX_ROUND_TRIP_MS}) so a legitimately-reachable
|
|
155
217
|
* candidate isn't killed by a budget sized for a single round-trip. A
|
|
156
218
|
* `click()` that still exceeds that scaled budget (a wedged CDP round-trip
|
|
@@ -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;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;
|
|
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;IAC1B;;;;;;;;;;;;;OAaG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;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;IACvB;;;;;;;OAOG;IACH,KAAK,EAAE,OAAO,CAAC;CAChB;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;AAkCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,cAAc,CAC5B,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,YAAY,EAAE,EACvB,KAAK,UAAQ,GACZ,MAAM,CAiBR;AA+JD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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;AAwDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;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,CAiCf;AAED;;;;;;;;GAQG"}
|
|
@@ -89,31 +89,82 @@ function extractTaggedPhrases(instruction) {
|
|
|
89
89
|
function normalize(text) {
|
|
90
90
|
return text.replace(/\s+/g, " ").trim().toLowerCase();
|
|
91
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Nav/advance words that mark a control as chrome (BACK/Next/Cancel), matched
|
|
94
|
+
* with the same word-boundary predicate SHAPE `submit-control.ts`'s
|
|
95
|
+
* `NEGATIVE_TEXT_EXPR` uses, over a nav-word list that adds `"next"` (an advance
|
|
96
|
+
* button a "select '<option>'" step must be de-prioritized against). Kept here
|
|
97
|
+
* (not imported from that browser-context string) so {@link scoreCandidate} can
|
|
98
|
+
* re-derive the text portion of the nav check on the legacy per-candidate path,
|
|
99
|
+
* which can't read `data-testid` and so always arrives with `isNav === false`.
|
|
100
|
+
*/
|
|
101
|
+
const NAV_TEXT_WORDS = [
|
|
102
|
+
"back",
|
|
103
|
+
"cancel",
|
|
104
|
+
"previous",
|
|
105
|
+
"next",
|
|
106
|
+
"close",
|
|
107
|
+
"save draft",
|
|
108
|
+
"save for later",
|
|
109
|
+
];
|
|
110
|
+
/** True when `normalizedText` is one of {@link NAV_TEXT_WORDS} (whole-word / edge match), the text-only half of the scan's `IS_NAV_CANDIDATE_EXPR`. */
|
|
111
|
+
function matchesNavText(normalizedText) {
|
|
112
|
+
return NAV_TEXT_WORDS.some((n) => normalizedText === n || normalizedText.startsWith(`${n} `) || normalizedText.endsWith(` ${n}`));
|
|
113
|
+
}
|
|
92
114
|
/**
|
|
93
115
|
* Scores one candidate's relevance to `instruction`'s tagged phrases, higher
|
|
94
|
-
* is more relevant. Parallels `submit-control.ts`'s tiered-ranking shape
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
116
|
+
* is more relevant. Parallels `submit-control.ts`'s tiered-ranking shape but
|
|
117
|
+
* adds two tiers the markerless-widget bug report needs: a nav/back demotion
|
|
118
|
+
* (so a "select '<option>'" step never lands on the wizard's BACK/Next button,
|
|
119
|
+
* whose own text may exact-match nothing OR be empty) and an ends-with tier (so
|
|
120
|
+
* an option whose accessible name is polluted by a leading icon-glyph name —
|
|
121
|
+
* an icon-font/`<svg><title>` prefix rendering as `"<IconName><Label>"` — still
|
|
122
|
+
* beats a bare exact-text decoy by matching on the trailing label).
|
|
123
|
+
*
|
|
124
|
+
* Strict-priority tiers, first match wins:
|
|
125
|
+
* - negated-phrase match → `-2` (worst: a decoy sibling is worse than an
|
|
126
|
+
* unrelated structural node — the invariant the original tiering established),
|
|
127
|
+
* - exact positive → `4`,
|
|
128
|
+
* - ends-with a positive → `3` (the icon-glyph suffix case),
|
|
129
|
+
* - contains a positive (either direction) → `2`,
|
|
130
|
+
* - nav/back control with NO positive textual match → `-1`,
|
|
131
|
+
* - else → `0`.
|
|
132
|
+
*
|
|
133
|
+
* Crucially, the nav/back demotion sits BELOW the positive tiers, not above
|
|
134
|
+
* them: it only sinks a candidate that would otherwise score `0` (no phrase
|
|
135
|
+
* match at all) — the empty BACK button, or a bare "Next"/"Cancel" chrome
|
|
136
|
+
* button the instruction doesn't name. A candidate that positively matches the
|
|
137
|
+
* instruction phrase — exact, ends-with, or contains — is the control the step
|
|
138
|
+
* named and keeps its positive score even if its label happens to contain a nav
|
|
139
|
+
* word (`select 'Next'` → the "Next" option scores 4; "primary Next" whose step
|
|
140
|
+
* quotes 'Next' scores 2 via containment). This makes the demotion a tie-breaker
|
|
141
|
+
* against chrome, never an override of a real target.
|
|
142
|
+
*
|
|
143
|
+
* `isNav` comes from the batched scan (`data-testid` + empty-text signals it
|
|
144
|
+
* alone can see); the text-word portion is re-derived here via
|
|
145
|
+
* {@link matchesNavText} so the legacy per-candidate path (always `isNav=false`)
|
|
146
|
+
* still demotes a bare "Back"/"Next". Exported so `flow-runner.ts` can detect a
|
|
147
|
+
* tie for the top rank among `resolveDeepLocatorCandidates`'s already-sorted
|
|
148
|
+
* output (see {@link extractTaggedPhrases}'s docblock).
|
|
104
149
|
*/
|
|
105
|
-
function scoreCandidate(accessibleText, phrases) {
|
|
150
|
+
function scoreCandidate(accessibleText, phrases, isNav = false) {
|
|
106
151
|
const normalizedText = normalize(accessibleText);
|
|
107
|
-
if (!normalizedText)
|
|
108
|
-
return 0;
|
|
109
152
|
const positives = phrases.filter((p) => !p.negated).map((p) => normalize(p.text));
|
|
110
153
|
const negatives = phrases.filter((p) => p.negated).map((p) => normalize(p.text));
|
|
111
|
-
if (
|
|
154
|
+
if (normalizedText.length > 0 &&
|
|
155
|
+
negatives.some((n) => n === normalizedText || normalizedText.includes(n))) {
|
|
156
|
+
return -2;
|
|
157
|
+
}
|
|
158
|
+
if (normalizedText.length > 0) {
|
|
159
|
+
if (positives.some((p) => p === normalizedText))
|
|
160
|
+
return 4;
|
|
161
|
+
if (positives.some((p) => normalizedText.endsWith(p)))
|
|
162
|
+
return 3;
|
|
163
|
+
if (positives.some((p) => normalizedText.includes(p) || p.includes(normalizedText)))
|
|
164
|
+
return 2;
|
|
165
|
+
}
|
|
166
|
+
if (isNav || matchesNavText(normalizedText))
|
|
112
167
|
return -1;
|
|
113
|
-
if (positives.some((p) => p === normalizedText))
|
|
114
|
-
return 3;
|
|
115
|
-
if (positives.some((p) => normalizedText.includes(p) || p.includes(normalizedText)))
|
|
116
|
-
return 2;
|
|
117
168
|
return 0;
|
|
118
169
|
}
|
|
119
170
|
/**
|
|
@@ -200,6 +251,7 @@ async function scanFrameCandidatesBatched(page, frameSelector, innerSelector, ho
|
|
|
200
251
|
index: entry.index,
|
|
201
252
|
selector: candidateSelector(hopSelector, entry.index),
|
|
202
253
|
accessibleText: entry.text.trim(),
|
|
254
|
+
isNav: entry.isNav === true,
|
|
203
255
|
}));
|
|
204
256
|
}
|
|
205
257
|
/**
|
|
@@ -238,6 +290,7 @@ async function enumerateCandidatesViaLegacyLoop(delegate, hopSelector, callTimeo
|
|
|
238
290
|
index,
|
|
239
291
|
selector: candidateSelector(hopSelector, index),
|
|
240
292
|
accessibleText: accessibleText.trim(),
|
|
293
|
+
isNav: false,
|
|
241
294
|
});
|
|
242
295
|
}
|
|
243
296
|
return candidates;
|
|
@@ -297,7 +350,7 @@ async function resolveDeepLocatorCandidates(page, frameSelector, innerSelector,
|
|
|
297
350
|
.map((candidate, originalOrder) => ({
|
|
298
351
|
candidate,
|
|
299
352
|
originalOrder,
|
|
300
|
-
score: scoreCandidate(candidate.accessibleText, phrases),
|
|
353
|
+
score: scoreCandidate(candidate.accessibleText, phrases, candidate.isNav),
|
|
301
354
|
}))
|
|
302
355
|
.sort((a, b) => b.score - a.score || a.originalOrder - b.originalOrder)
|
|
303
356
|
.map(({ candidate }) => candidate);
|
|
@@ -317,10 +370,13 @@ function isFrameCandidateClickResult(entry) {
|
|
|
317
370
|
* Batched-click fast path: one `frameTarget.evaluate(buildClickFrameCandidateExpr(innerSelector, index))`
|
|
318
371
|
* round-trip replaces the legacy `nth(index).click()`, which (per
|
|
319
372
|
* {@link DEEP_LOCATOR_CLICK_INDEX_ROUND_TRIP_MS}'s docs) pays `index + 1`
|
|
320
|
-
* serial CDP round-trips through `resolveAtIndex`.
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
*
|
|
373
|
+
* serial CDP round-trips through `resolveAtIndex`. The batched click is a
|
|
374
|
+
* SYNTHETIC (`isTrusted=false`) activation; {@link clickDeepLocatorCandidate}
|
|
375
|
+
* escalates to the trusted CDP click only when this reports the index stale or
|
|
376
|
+
* the caller set `preferTrustedClick` (a phantom-driven retry). Returns `null`
|
|
377
|
+
* (never throws) when no frame seam is available, the evaluate call rejects, or
|
|
378
|
+
* the resolved payload doesn't conform to {@link FrameCandidateClickResult} —
|
|
379
|
+
* every one of those degrades the caller to the trusted `nth(index).click()`
|
|
324
380
|
* path instead of losing the click, paralleling {@link scanFrameCandidatesBatched}'s
|
|
325
381
|
* degrade contract on the enumeration side.
|
|
326
382
|
*/
|
|
@@ -351,19 +407,34 @@ async function clickCandidateBatched(page, frameSelector, innerSelector, index,
|
|
|
351
407
|
* must infer the outcome from whether this call throws plus their own
|
|
352
408
|
* downstream DOM verification, not from a returned boolean.
|
|
353
409
|
*
|
|
354
|
-
*
|
|
355
|
-
* frame seam is available (`timeoutOptions.frameTarget`, or one
|
|
356
|
-
* internally the same way {@link scanFrameCandidatesBatched} does)
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
410
|
+
* By default, prefers the one-round-trip {@link clickCandidateBatched} fast
|
|
411
|
+
* path when a frame seam is available (`timeoutOptions.frameTarget`, or one
|
|
412
|
+
* resolved internally the same way {@link scanFrameCandidatesBatched} does):
|
|
413
|
+
* that batched click is a SYNTHETIC (`isTrusted=false`) activation, but it
|
|
414
|
+
* collapses the `index + 1` serial `nth(index)` resolve round-trips and works
|
|
415
|
+
* for the overwhelmingly common control. It falls back to the trusted
|
|
416
|
+
* `deepLocator().nth(index).click()` (understudy `Input.dispatchMouseEvent`,
|
|
417
|
+
* `isTrusted=true`) — which never reports success via a return value, only via
|
|
418
|
+
* not rejecting — when no seam is available, the batched call fails/degrades, or
|
|
419
|
+
* it reports the index stale (`reason: "out-of-range"`). A batched
|
|
420
|
+
* `reason: "not-actionable"` result throws an error {@link isNodeNotActionableError}
|
|
421
|
+
* classifies, the same contract a real click against an unrendered node rejects
|
|
422
|
+
* with via the CDP `-32000 Node does not have a layout object` error, so a
|
|
423
|
+
* caller cascading through candidates treats both paths identically.
|
|
365
424
|
*
|
|
366
|
-
*
|
|
425
|
+
* When `timeoutOptions.preferTrustedClick` is set, the synthetic batched click
|
|
426
|
+
* is SKIPPED entirely and activation goes straight to the trusted CDP
|
|
427
|
+
* `nth(index).click()` — the escalation a phantom-driven retry uses after a
|
|
428
|
+
* synthetic click fired without effect (a React/design-system handler that
|
|
429
|
+
* ignores untrusted activation). A cheap not-actionable probe still runs first
|
|
430
|
+
* so an unrendered candidate is skipped without paying the scaled-timeout CDP
|
|
431
|
+
* click.
|
|
432
|
+
*
|
|
433
|
+
* Resolves on success and rejects on failure — the caller must infer the
|
|
434
|
+
* outcome from whether this call throws plus their own downstream DOM
|
|
435
|
+
* verification, not from a returned boolean.
|
|
436
|
+
*
|
|
437
|
+
* The trusted-fallback watchdog scales with `index` (see
|
|
367
438
|
* {@link DEEP_LOCATOR_CLICK_INDEX_ROUND_TRIP_MS}) so a legitimately-reachable
|
|
368
439
|
* candidate isn't killed by a budget sized for a single round-trip. A
|
|
369
440
|
* `click()` that still exceeds that scaled budget (a wedged CDP round-trip
|
|
@@ -374,11 +445,20 @@ async function clickCandidateBatched(page, frameSelector, innerSelector, index,
|
|
|
374
445
|
async function clickDeepLocatorCandidate(page, frameSelector, innerSelector, index, timeoutOptions = {}) {
|
|
375
446
|
const callTimeoutMs = timeoutOptions.callTimeoutMs ?? DEFAULT_DEEP_LOCATOR_CALL_TIMEOUT_MS;
|
|
376
447
|
const hopSelector = (0, frame_target_1.buildHopSelector)(frameSelector, innerSelector);
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
448
|
+
// The synthetic batched click is skipped when a trusted click is demanded —
|
|
449
|
+
// running BOTH would activate the element twice (on a toggle: select →
|
|
450
|
+
// deselect = net zero, the exact double-fire hazard browser-click-expr.ts
|
|
451
|
+
// warns about). Under preferTrustedClick the trusted `nth().click()` below is
|
|
452
|
+
// the sole activation, and an unrendered node surfaces as the CDP `-32000`
|
|
453
|
+
// error `isNodeNotActionableError` classifies rather than the batched probe's
|
|
454
|
+
// not-actionable reason.
|
|
455
|
+
if (!timeoutOptions.preferTrustedClick) {
|
|
456
|
+
const batchedResult = await clickCandidateBatched(page, frameSelector, innerSelector, index, hopSelector, timeoutOptions);
|
|
457
|
+
if (batchedResult?.reason === "not-actionable") {
|
|
458
|
+
throw new Error(`deepLocator batched click for ${hopSelector} nth=${index}: node does not have a layout object`);
|
|
459
|
+
}
|
|
460
|
+
if (batchedResult?.clicked)
|
|
461
|
+
return;
|
|
382
462
|
}
|
|
383
463
|
const scaledCallTimeoutMs = callTimeoutMs + index * deep_locator_scan_1.DEEP_LOCATOR_CLICK_INDEX_ROUND_TRIP_MS;
|
|
384
464
|
await (0, watchdog_1.withWatchdog)(() => page.deepLocator(hopSelector).nth(index).click(), {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deep-locator-candidates.js","sourceRoot":"","sources":["../../src/scraper/deep-locator-candidates.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;;;;AAIH,yCAA8C;AAC9C,2CAA0C;AAC1C,mEAMqC;AACrC,yDAIgC;AAChC,iDAAkD;AAElD,MAAM,MAAM,GAAG,IAAA,mBAAS,EAAC,EAAE,IAAI,EAAE,iCAAiC,EAAE,CAAC,CAAC;AAEtE;;;;;GAKG;AACH,MAAM,oCAAoC,GAAG,MAAM,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,MAAM,0CAA0C,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"deep-locator-candidates.js","sourceRoot":"","sources":["../../src/scraper/deep-locator-candidates.ts"],"names":[],"mappings":"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"}
|
|
@@ -39,20 +39,45 @@ export declare const DEFAULT_CLICK_CANDIDATE_ATTEMPT_CAP = 5;
|
|
|
39
39
|
export type ClickCandidateFn = (candidate: DeepLocatorCandidate) => Promise<void>;
|
|
40
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
41
|
export type DenyCandidatePredicate = (candidate: DeepLocatorCandidate) => boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Reads the running "N selected" counter AFTER a candidate's click resolved,
|
|
44
|
+
* returning the current count or `null` when no counter is exposed. Injected so
|
|
45
|
+
* this module stays site-agnostic and `Page`-free — `flow-runner.ts` supplies a
|
|
46
|
+
* reader that re-snapshots the frame and parses the count via
|
|
47
|
+
* `selectionCountFromSignature`. See {@link ClickCandidateCascadeOptions.readSelectionCount}.
|
|
48
|
+
*/
|
|
49
|
+
export type ReadSelectionCountFn = () => Promise<number | null>;
|
|
42
50
|
export interface ClickCandidateCascadeOptions {
|
|
43
51
|
/** Skips a candidate without clicking it when this returns `true`. Optional — omit to click every non-not-actionable candidate in rank order. */
|
|
44
52
|
denyCandidate?: DenyCandidatePredicate;
|
|
45
53
|
/** Overrides {@link DEFAULT_CLICK_CANDIDATE_ATTEMPT_CAP}. */
|
|
46
54
|
attemptCap?: number;
|
|
55
|
+
/**
|
|
56
|
+
* When supplied (alongside a numeric {@link baselineSelectionCount}), a
|
|
57
|
+
* candidate whose click RESOLVED but did not raise the selection count above
|
|
58
|
+
* the baseline is treated like a not-actionable rejection: the walk keeps
|
|
59
|
+
* going to the next candidate instead of reporting success. This is the
|
|
60
|
+
* next-best-candidate recovery for a markerless multi-select phantom (the
|
|
61
|
+
* #163/#164 counter veto knows the click didn't register; this feeds that
|
|
62
|
+
* back into re-resolution before a replan is spent). A `null` read means "no
|
|
63
|
+
* counter exposed" and never vetoes, mirroring `isSelectionCounterStalled`'s
|
|
64
|
+
* null-safety. Omit for non-selection steps — the walk then behaves exactly
|
|
65
|
+
* as before.
|
|
66
|
+
*/
|
|
67
|
+
readSelectionCount?: ReadSelectionCountFn;
|
|
68
|
+
/** The pre-walk selection count; a post-click read at or below this counts as "did not register". Ignored unless {@link readSelectionCount} is set. `null` disables the check (counter-less widget). */
|
|
69
|
+
baselineSelectionCount?: number | null;
|
|
47
70
|
}
|
|
48
71
|
/** Outcome of a {@link clickFirstActionableCandidate} walk. */
|
|
49
72
|
export interface ClickCandidateCascadeOutcome {
|
|
50
|
-
/** `true` only when a candidate's click resolved (didn't reject). */
|
|
73
|
+
/** `true` only when a candidate's click resolved (didn't reject) AND, when a selection-count check is wired, registered the selection. */
|
|
51
74
|
clicked: boolean;
|
|
52
75
|
/** The candidate that was clicked, or `null` when the walk exhausted the list/cap without a successful click. */
|
|
53
76
|
candidate: DeepLocatorCandidate | null;
|
|
54
77
|
/** Selectors of every candidate this walk actually clicked (attempted), in attempt order — denied candidates are excluded since they were never clicked. */
|
|
55
78
|
triedSelectors: string[];
|
|
79
|
+
/** Selectors of candidates that clicked cleanly but were rejected because the selection counter did not rise (a subset of {@link triedSelectors}). Empty unless {@link ClickCandidateCascadeOptions.readSelectionCount} vetoed at least one — lets the caller emit a "clicked but nothing registered" failure reason distinct from not-actionable. */
|
|
80
|
+
counterStalledSelectors: string[];
|
|
56
81
|
}
|
|
57
82
|
/**
|
|
58
83
|
* Walks `candidates` in rank order, clicking each via `click` until one
|
|
@@ -65,7 +90,20 @@ export interface ClickCandidateCascadeOutcome {
|
|
|
65
90
|
* with actionability (a detached frame, a wedged click's
|
|
66
91
|
* `WatchdogTimeoutError`). Never throws on exhaustion — an exhausted list
|
|
67
92
|
* (every candidate denied or not-actionable, or the cap reached) resolves to
|
|
68
|
-
* `{ clicked: false, candidate: null, triedSelectors }`
|
|
93
|
+
* `{ clicked: false, candidate: null, triedSelectors, counterStalledSelectors }`
|
|
94
|
+
* instead.
|
|
95
|
+
*
|
|
96
|
+
* When `readSelectionCount`/`baselineSelectionCount` are supplied, a click that
|
|
97
|
+
* RESOLVED but left the selection count at or below the baseline is treated
|
|
98
|
+
* like a not-actionable rejection — the walk advances to the next candidate
|
|
99
|
+
* instead of reporting a phantom success. A count that rose returns immediately
|
|
100
|
+
* (so a click that DID register never triggers a second click — the
|
|
101
|
+
* toggle-double-click guard), and a `null` read is never a veto (counter-less
|
|
102
|
+
* widget). Because the check runs after `click` already awaited its own settle,
|
|
103
|
+
* a synchronously-updated counter is visible; the only residual is a genuinely-
|
|
104
|
+
* registered click whose counter lags, which is the same false-negative the
|
|
105
|
+
* caller's late counter veto already tolerates today — behaviour is no worse,
|
|
106
|
+
* and now recovers via the next candidate.
|
|
69
107
|
*/
|
|
70
108
|
export declare function clickFirstActionableCandidate(candidates: readonly DeepLocatorCandidate[], click: ClickCandidateFn, options?: ClickCandidateCascadeOptions): Promise<ClickCandidateCascadeOutcome>;
|
|
71
109
|
//# sourceMappingURL=deep-locator-click.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deep-locator-click.d.ts","sourceRoot":"","sources":["../../src/scraper/deep-locator-click.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAG9E;;;;;;;;;GASG;AACH,eAAO,MAAM,mCAAmC,IAAI,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,SAAS,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAElF,qJAAqJ;AACrJ,MAAM,MAAM,sBAAsB,GAAG,CAAC,SAAS,EAAE,oBAAoB,KAAK,OAAO,CAAC;AAElF,MAAM,WAAW,4BAA4B;IAC3C,iJAAiJ;IACjJ,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"deep-locator-click.d.ts","sourceRoot":"","sources":["../../src/scraper/deep-locator-click.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAG9E;;;;;;;;;GASG;AACH,eAAO,MAAM,mCAAmC,IAAI,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,SAAS,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAElF,qJAAqJ;AACrJ,MAAM,MAAM,sBAAsB,GAAG,CAAC,SAAS,EAAE,oBAAoB,KAAK,OAAO,CAAC;AAElF;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAEhE,MAAM,WAAW,4BAA4B;IAC3C,iJAAiJ;IACjJ,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;OAWG;IACH,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;IAC1C,wMAAwM;IACxM,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AAED,+DAA+D;AAC/D,MAAM,WAAW,4BAA4B;IAC3C,0IAA0I;IAC1I,OAAO,EAAE,OAAO,CAAC;IACjB,iHAAiH;IACjH,SAAS,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACvC,4JAA4J;IAC5J,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,sVAAsV;IACtV,uBAAuB,EAAE,MAAM,EAAE,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,6BAA6B,CACjD,UAAU,EAAE,SAAS,oBAAoB,EAAE,EAC3C,KAAK,EAAE,gBAAgB,EACvB,OAAO,GAAE,4BAAiC,GACzC,OAAO,CAAC,4BAA4B,CAAC,CAmCvC"}
|