@enricai/barnacle 1.6.12 → 1.6.14

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.
Files changed (108) hide show
  1. package/README.md +113 -9
  2. package/dist/api/schemas/submissions.d.ts +14 -0
  3. package/dist/api/schemas/submissions.d.ts.map +1 -1
  4. package/dist/api/schemas/submissions.js +1 -0
  5. package/dist/api/schemas/submissions.js.map +1 -1
  6. package/dist/config.d.ts +33 -0
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +4 -0
  9. package/dist/config.js.map +1 -1
  10. package/dist/lib/bedrock.d.ts +8 -5
  11. package/dist/lib/bedrock.d.ts.map +1 -1
  12. package/dist/lib/bedrock.js +8 -5
  13. package/dist/lib/bedrock.js.map +1 -1
  14. package/dist/lib/http.d.ts +4 -3
  15. package/dist/lib/http.d.ts.map +1 -1
  16. package/dist/lib/http.js +25 -3
  17. package/dist/lib/http.js.map +1 -1
  18. package/dist/lib/telemetry/beacon-capture.d.ts +45 -2
  19. package/dist/lib/telemetry/beacon-capture.d.ts.map +1 -1
  20. package/dist/lib/telemetry/beacon-capture.js +36 -0
  21. package/dist/lib/telemetry/beacon-capture.js.map +1 -1
  22. package/dist/lib/telemetry/reconciliation-record.d.ts +41 -1
  23. package/dist/lib/telemetry/reconciliation-record.d.ts.map +1 -1
  24. package/dist/lib/telemetry/reconciliation-record.js +29 -2
  25. package/dist/lib/telemetry/reconciliation-record.js.map +1 -1
  26. package/dist/lib/telemetry/reconciliation-source.d.ts +7 -1
  27. package/dist/lib/telemetry/reconciliation-source.d.ts.map +1 -1
  28. package/dist/lib/telemetry/reconciliation-source.js +21 -5
  29. package/dist/lib/telemetry/reconciliation-source.js.map +1 -1
  30. package/dist/lib/telemetry/run-telemetry.d.ts +67 -0
  31. package/dist/lib/telemetry/run-telemetry.d.ts.map +1 -0
  32. package/dist/lib/telemetry/run-telemetry.js +55 -0
  33. package/dist/lib/telemetry/run-telemetry.js.map +1 -0
  34. package/dist/lib/telemetry/submission-capture.d.ts +11 -4
  35. package/dist/lib/telemetry/submission-capture.d.ts.map +1 -1
  36. package/dist/lib/telemetry/submission-capture.js +1 -0
  37. package/dist/lib/telemetry/submission-capture.js.map +1 -1
  38. package/dist/lib/telemetry/submission-reader.d.ts +11 -3
  39. package/dist/lib/telemetry/submission-reader.d.ts.map +1 -1
  40. package/dist/lib/telemetry/submission-reader.js +32 -8
  41. package/dist/lib/telemetry/submission-reader.js.map +1 -1
  42. package/dist/lib/tracking-click.d.ts.map +1 -1
  43. package/dist/lib/tracking-click.js +19 -0
  44. package/dist/lib/tracking-click.js.map +1 -1
  45. package/dist/plugins/config-plugin.js +3 -3
  46. package/dist/plugins/loader.d.ts +20 -0
  47. package/dist/plugins/loader.d.ts.map +1 -1
  48. package/dist/plugins/loader.js +94 -17
  49. package/dist/plugins/loader.js.map +1 -1
  50. package/dist/scraper/deep-locator-actuate.d.ts +94 -0
  51. package/dist/scraper/deep-locator-actuate.d.ts.map +1 -0
  52. package/dist/scraper/deep-locator-actuate.js +216 -0
  53. package/dist/scraper/deep-locator-actuate.js.map +1 -0
  54. package/dist/scraper/deep-locator-candidates.d.ts +105 -18
  55. package/dist/scraper/deep-locator-candidates.d.ts.map +1 -1
  56. package/dist/scraper/deep-locator-candidates.js +238 -46
  57. package/dist/scraper/deep-locator-candidates.js.map +1 -1
  58. package/dist/scraper/deep-locator-click.d.ts +71 -0
  59. package/dist/scraper/deep-locator-click.d.ts.map +1 -0
  60. package/dist/scraper/deep-locator-click.js +71 -0
  61. package/dist/scraper/deep-locator-click.js.map +1 -0
  62. package/dist/scraper/deep-locator-fake.d.ts +344 -14
  63. package/dist/scraper/deep-locator-fake.d.ts.map +1 -1
  64. package/dist/scraper/deep-locator-fake.js +509 -7
  65. package/dist/scraper/deep-locator-fake.js.map +1 -1
  66. package/dist/scraper/deep-locator-scan.d.ts +220 -0
  67. package/dist/scraper/deep-locator-scan.d.ts.map +1 -0
  68. package/dist/scraper/deep-locator-scan.js +386 -0
  69. package/dist/scraper/deep-locator-scan.js.map +1 -0
  70. package/dist/scraper/fake-timer-advance.d.ts +47 -0
  71. package/dist/scraper/fake-timer-advance.d.ts.map +1 -0
  72. package/dist/scraper/fake-timer-advance.js +44 -0
  73. package/dist/scraper/fake-timer-advance.js.map +1 -0
  74. package/dist/scraper/flow-runner.d.ts +80 -9
  75. package/dist/scraper/flow-runner.d.ts.map +1 -1
  76. package/dist/scraper/flow-runner.js +417 -63
  77. package/dist/scraper/flow-runner.js.map +1 -1
  78. package/dist/scraper/frame-target.d.ts +28 -0
  79. package/dist/scraper/frame-target.d.ts.map +1 -1
  80. package/dist/scraper/frame-target.js +30 -0
  81. package/dist/scraper/frame-target.js.map +1 -1
  82. package/dist/scraper/session-browserbase.d.ts +13 -0
  83. package/dist/scraper/session-browserbase.d.ts.map +1 -1
  84. package/dist/scraper/session-browserbase.js +29 -0
  85. package/dist/scraper/session-browserbase.js.map +1 -1
  86. package/dist/scraper/session-ip.d.ts +69 -0
  87. package/dist/scraper/session-ip.d.ts.map +1 -0
  88. package/dist/scraper/session-ip.js +88 -0
  89. package/dist/scraper/session-ip.js.map +1 -0
  90. package/dist/scraper/session-shared.d.ts +9 -0
  91. package/dist/scraper/session-shared.d.ts.map +1 -1
  92. package/dist/scraper/session-shared.js.map +1 -1
  93. package/dist/scraper/submit-control.d.ts +14 -2
  94. package/dist/scraper/submit-control.d.ts.map +1 -1
  95. package/dist/scraper/submit-control.js +33 -2
  96. package/dist/scraper/submit-control.js.map +1 -1
  97. package/dist/scripts/recon-browser.d.ts +1 -1
  98. package/dist/scripts/recon-browser.d.ts.map +1 -1
  99. package/dist/scripts/recon-browser.js +48 -16
  100. package/dist/scripts/recon-browser.js.map +1 -1
  101. package/dist/scripts/recon-generate.js +3 -3
  102. package/dist/scripts/recon-generate.js.map +1 -1
  103. package/dist/site-plugin.d.ts +20 -0
  104. package/dist/site-plugin.d.ts.map +1 -1
  105. package/dist/testing/integration-runner.d.ts.map +1 -1
  106. package/dist/testing/integration-runner.js +6 -7
  107. package/dist/testing/integration-runner.js.map +1 -1
  108. package/package.json +3 -2
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+ /**
3
+ * Fill/select actuation seam over `deepLocator`, mirroring
4
+ * `clickDeepLocatorCandidate`'s contract (`deep-locator-candidates.ts`) for
5
+ * the two other primitives a cross-origin OOPIF form's text/select steps
6
+ * need. Stagehand `act`/`observe` are measured blind inside that OOPIF, and
7
+ * `verifyDomEffect` (`flow-runner.ts`) can't locate a `deeplocator=` selector
8
+ * to confirm the write, so both actuators here read the written value back
9
+ * through the same delegate before reporting success — the caller gets a
10
+ * trustworthy boolean instead of a downstream verifier that can never fire.
11
+ *
12
+ * Both actuators prefer one batched `frameTarget.evaluate(buildFillFrameCandidateExpr(...) |
13
+ * buildSelectFrameCandidateExpr(...))` round-trip (`deep-locator-scan.ts`) over
14
+ * the legacy `deepLocator(hop).nth(index).fill()`/`.selectOption()` +
15
+ * `.inputValue()` pair, which pays Stagehand's `index + 1` serial
16
+ * `resolveAtIndex` round-trips per call — the same cost
17
+ * `clickDeepLocatorCandidate` already batches away. The legacy fallback's
18
+ * watchdog budget scales with `index` using
19
+ * {@link DEEP_LOCATOR_CLICK_INDEX_ROUND_TRIP_MS} — the same per-round-trip
20
+ * constant `clickDeepLocatorCandidate` uses — so a legitimately-reachable
21
+ * candidate deep in a dense OOPIF form isn't killed by a budget sized for one
22
+ * round-trip.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.fillDeepLocatorCandidate = fillDeepLocatorCandidate;
26
+ exports.selectDeepLocatorCandidateOption = selectDeepLocatorCandidateOption;
27
+ const errors_1 = require("../lib/errors");
28
+ const logging_1 = require("../lib/logging");
29
+ const deep_locator_scan_1 = require("../scraper/deep-locator-scan");
30
+ const frame_target_1 = require("../scraper/frame-target");
31
+ const watchdog_1 = require("../scraper/watchdog");
32
+ const logger = (0, logging_1.getLogger)({ name: "scraper/deep-locator-actuate" });
33
+ /**
34
+ * Per-CDP-call watchdog default: bounds a single `fill()`/`selectOption()`/
35
+ * `inputValue()` round-trip. Owned locally (not imported from
36
+ * `deep-locator-candidates.ts` or `flow-runner.ts`) so this module stays a
37
+ * self-contained leaf, matching how `deep-locator-candidates.ts` already
38
+ * avoids the `flow-runner.ts` import cycle by owning its own timeout
39
+ * constant.
40
+ */
41
+ const DEFAULT_DEEP_LOCATOR_CALL_TIMEOUT_MS = 10_000;
42
+ /**
43
+ * Runs `write`, then `readBack`, and reports whether the read-back matches
44
+ * `expected` — the shared write/verify shape `fillDeepLocatorCandidate` and
45
+ * `selectDeepLocatorCandidateOption` both need, differing only in which
46
+ * delegate method performs the write. A {@link WatchdogTimeoutError} from
47
+ * either await (a wedged CDP round-trip) propagates so the caller can tell a
48
+ * genuine hang apart from an ordinary failed write — everything else (the
49
+ * delegate rejecting, or a read-back that disagrees with `expected`) resolves
50
+ * to `false` instead of throwing.
51
+ */
52
+ async function writeAndVerify(write, readBack, expected) {
53
+ try {
54
+ await write();
55
+ }
56
+ catch (error) {
57
+ if (error instanceof watchdog_1.WatchdogTimeoutError)
58
+ throw error;
59
+ return false;
60
+ }
61
+ try {
62
+ return (await readBack()) === expected;
63
+ }
64
+ catch (error) {
65
+ if (error instanceof watchdog_1.WatchdogTimeoutError)
66
+ throw error;
67
+ return false;
68
+ }
69
+ }
70
+ /**
71
+ * Resolves the `FrameTarget` a batched actuation evaluate should run
72
+ * against: `timeoutOptions.frameTarget` when the caller already resolved
73
+ * one, else a single non-polling `probeAttachedFrameTarget(page,
74
+ * frameSelector)` pass (`frame-target.ts`) so existing call sites — which
75
+ * pass only a `frameSelector` string — still get the batched fast path
76
+ * without themselves changing. Unlike a bare `resolveFrameTarget(page,
77
+ * frameSelector, { timeoutMs: 0 })` pass, the probe carries a real per-probe
78
+ * budget, so it can land against an already-attached frame under genuine CDP
79
+ * latency instead of always losing a zero-budget race. Returns `null` (never
80
+ * throws) when `frameSelector` is unset, the probe rejects (e.g. a fake
81
+ * `Page` in a legacy-path test lacking `evaluate`/`frames`), or nothing
82
+ * attaches within the probe's budget — each of those means "no frame seam
83
+ * available", and the caller degrades to the legacy delegate path. Mirrors
84
+ * `deep-locator-candidates.ts`'s `resolveScanFrameTarget` exactly; duplicated
85
+ * (not imported) so this module stays a leaf that never depends on
86
+ * `deep-locator-candidates.ts`.
87
+ */
88
+ async function resolveActuateFrameTarget(page, frameSelector, timeoutOptions) {
89
+ if (timeoutOptions.frameTarget)
90
+ return timeoutOptions.frameTarget;
91
+ if (!frameSelector)
92
+ return null;
93
+ try {
94
+ return await (0, frame_target_1.probeAttachedFrameTarget)(page, frameSelector);
95
+ }
96
+ catch {
97
+ return null;
98
+ }
99
+ }
100
+ /** Narrows a batched fill/select evaluate result to {@link FrameCandidateWriteResult}'s shape, guarding against a non-conforming payload (the same degrade-to-legacy contract `deep-locator-candidates.ts`'s `isFrameCandidateScanResult`/`isFrameCandidateClickResult` enforce). */
101
+ function isFrameCandidateWriteResult(entry) {
102
+ if (typeof entry !== "object" || entry === null)
103
+ return false;
104
+ const result = entry;
105
+ if (typeof result.written !== "boolean")
106
+ return false;
107
+ if (result.written)
108
+ return typeof result.readBack === "string";
109
+ return result.reason === "out-of-range" || result.reason === "not-actionable";
110
+ }
111
+ /**
112
+ * Batched fill/select fast path: one `frameTarget.evaluate(expression)`
113
+ * round-trip replaces the legacy `nth(index).fill()`/`.selectOption()` +
114
+ * `.inputValue()` pair. Returns `null` (never throws) when no frame seam is
115
+ * available, the evaluate call rejects, or the resolved payload doesn't
116
+ * conform to {@link FrameCandidateWriteResult} — every one of those degrades
117
+ * the caller to the legacy delegate path instead of losing the write,
118
+ * mirroring `clickCandidateBatched`'s degrade contract
119
+ * (`deep-locator-candidates.ts`).
120
+ */
121
+ async function actuateCandidateBatched(page, frameSelector, hopSelector, index, timeoutOptions, expression, actionLabel) {
122
+ const frameTarget = await resolveActuateFrameTarget(page, frameSelector, timeoutOptions);
123
+ if (!frameTarget)
124
+ return null;
125
+ let result;
126
+ try {
127
+ result = await frameTarget.evaluate(expression);
128
+ }
129
+ catch (err) {
130
+ logger.warn(`deepLocator batched ${actionLabel} for ${hopSelector} nth=${index} failed, degrading to delegate ${actionLabel}: ${(0, errors_1.toErrorMessage)(err)}`);
131
+ return null;
132
+ }
133
+ if (!isFrameCandidateWriteResult(result)) {
134
+ logger.warn(`deepLocator batched ${actionLabel} for ${hopSelector} nth=${index} returned a non-conforming payload, degrading to delegate ${actionLabel}`);
135
+ return null;
136
+ }
137
+ return result;
138
+ }
139
+ /**
140
+ * Fills the candidate at `index` inside the frame scoped by `frameSelector`
141
+ * with `value`, re-deriving the same hop selector
142
+ * `resolveDeepLocatorCandidates` used (`deep-locator-candidates.ts`) rather
143
+ * than trusting a candidate's display `selector` (`deeplocator=`-prefixed,
144
+ * deliberately not an xpath).
145
+ *
146
+ * Prefers the one-round-trip {@link actuateCandidateBatched} fast path when a
147
+ * frame seam is available: a `written: true` result whose inline `readBack`
148
+ * already matches `value` resolves `true` immediately (no second round-trip
149
+ * needed); a `reason: "not-actionable"` result (the matched element has no
150
+ * layout box) resolves `false` immediately — no delegate write against that
151
+ * same node could succeed either. Every other batched outcome — no frame
152
+ * seam, a rejecting or non-conforming evaluate, `reason: "out-of-range"`, or
153
+ * a `written: true` result whose inline `readBack` disagrees with `value`
154
+ * (e.g. a controlled component's `onChange` reverted the write on a tick the
155
+ * single synchronous evaluate call couldn't observe) — degrades to the
156
+ * legacy `deepLocator(hop).nth(index).fill()` + `.inputValue()` pair rather
157
+ * than trusting the batched call's verdict outright: returns `true` only
158
+ * when that separate read-back equals `value`, and `false` — never a throw —
159
+ * when the delegate rejects the fill/read-back or the read-back disagrees.
160
+ * The legacy path's watchdog budget scales with `index`
161
+ * ({@link DEEP_LOCATOR_CLICK_INDEX_ROUND_TRIP_MS}, the same per-round-trip
162
+ * cost `clickDeepLocatorCandidate`'s legacy fallback charges), so a candidate
163
+ * deep in a dense OOPIF form isn't killed by a budget sized for a single
164
+ * round-trip. A wedged `fill()`/`inputValue()` call that still exceeds that
165
+ * scaled budget rejects with a `WatchdogTimeoutError` instead of hanging the
166
+ * caller, the same "rejects on a genuine hang" contract
167
+ * `clickDeepLocatorCandidate` uses.
168
+ */
169
+ async function fillDeepLocatorCandidate(page, frameSelector, innerSelector, index, value, timeoutOptions = {}) {
170
+ const callTimeoutMs = timeoutOptions.callTimeoutMs ?? DEFAULT_DEEP_LOCATOR_CALL_TIMEOUT_MS;
171
+ const hopSelector = (0, frame_target_1.buildHopSelector)(frameSelector, innerSelector);
172
+ const batchedResult = await actuateCandidateBatched(page, frameSelector, hopSelector, index, timeoutOptions, (0, deep_locator_scan_1.buildFillFrameCandidateExpr)(innerSelector, index, value), "fill");
173
+ if (batchedResult?.written && batchedResult.readBack === value)
174
+ return true;
175
+ if (batchedResult?.reason === "not-actionable")
176
+ return false;
177
+ const scaledCallTimeoutMs = callTimeoutMs + index * deep_locator_scan_1.DEEP_LOCATOR_CLICK_INDEX_ROUND_TRIP_MS;
178
+ return writeAndVerify(() => (0, watchdog_1.withWatchdog)(() => page.deepLocator(hopSelector).nth(index).fill(value), {
179
+ timeoutMs: scaledCallTimeoutMs,
180
+ label: `deepLocator fill() for ${hopSelector} nth=${index}`,
181
+ }), () => (0, watchdog_1.withWatchdog)(() => page.deepLocator(hopSelector).nth(index).inputValue(), {
182
+ timeoutMs: scaledCallTimeoutMs,
183
+ label: `deepLocator inputValue() for ${hopSelector} nth=${index}`,
184
+ }), value);
185
+ }
186
+ /**
187
+ * Selects `value` on the `<select>`-shaped candidate at `index` inside the
188
+ * frame scoped by `frameSelector`, under the same re-derived-hop,
189
+ * batched-first, index-scaled-watchdog-guarded contract as
190
+ * {@link fillDeepLocatorCandidate} — `selectOption()` is the legacy write,
191
+ * and `inputValue()` (which reads a `<select>`'s selected value the same as
192
+ * any other form control) is the legacy confirmation. Differs from
193
+ * `fillDeepLocatorCandidate` in one respect: a batched `written: true` result
194
+ * resolves `true` on its own, without comparing `readBack` to `value` —
195
+ * {@link buildSelectFrameCandidateExpr} matches an option by value OR trimmed
196
+ * label, so `readBack` (the MATCHED option's value) can legitimately differ
197
+ * from a caller-supplied label string even on a successful write.
198
+ */
199
+ async function selectDeepLocatorCandidateOption(page, frameSelector, innerSelector, index, value, timeoutOptions = {}) {
200
+ const callTimeoutMs = timeoutOptions.callTimeoutMs ?? DEFAULT_DEEP_LOCATOR_CALL_TIMEOUT_MS;
201
+ const hopSelector = (0, frame_target_1.buildHopSelector)(frameSelector, innerSelector);
202
+ const batchedResult = await actuateCandidateBatched(page, frameSelector, hopSelector, index, timeoutOptions, (0, deep_locator_scan_1.buildSelectFrameCandidateExpr)(innerSelector, index, value), "select");
203
+ if (batchedResult?.written)
204
+ return true;
205
+ if (batchedResult?.reason === "not-actionable")
206
+ return false;
207
+ const scaledCallTimeoutMs = callTimeoutMs + index * deep_locator_scan_1.DEEP_LOCATOR_CLICK_INDEX_ROUND_TRIP_MS;
208
+ return writeAndVerify(() => (0, watchdog_1.withWatchdog)(() => page.deepLocator(hopSelector).nth(index).selectOption(value), {
209
+ timeoutMs: scaledCallTimeoutMs,
210
+ label: `deepLocator selectOption() for ${hopSelector} nth=${index}`,
211
+ }), () => (0, watchdog_1.withWatchdog)(() => page.deepLocator(hopSelector).nth(index).inputValue(), {
212
+ timeoutMs: scaledCallTimeoutMs,
213
+ label: `deepLocator inputValue() for ${hopSelector} nth=${index}`,
214
+ }), value);
215
+ }
216
+ //# sourceMappingURL=deep-locator-actuate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deep-locator-actuate.js","sourceRoot":"","sources":["../../src/scraper/deep-locator-actuate.ts"],"names":[],"mappings":"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"}
@@ -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
- /** Accessible text read via the delegate's `textContent()`. */
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. `DeepLocatorDelegate.click()` resolves `Promise<void>` on
75
- * success and rejects on failure it never reports success via a return
76
- * value so callers must infer the outcome from whether this call throws
77
- * plus their own downstream DOM verification, not from a returned boolean.
78
- * A `click()` that exceeds `timeoutOptions.callTimeoutMs` (a wedged CDP
79
- * round-trip against a racy OOPIF frame) rejects with a `WatchdogTimeoutError`
80
- * the same as any other failure, preserving the "rejects on failure" contract
81
- * instead of hanging the caller forever.
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;AA2BrD,sJAAsJ;AACtJ,MAAM,WAAW,yBAAyB;IACxC,mIAAmI;IACnI,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gJAAgJ;IAChJ,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,oBAAoB;IACnC,kJAAkJ;IAClJ,KAAK,EAAE,MAAM,CAAC;IACd,gLAAgL;IAChL,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,cAAc,EAAE,MAAM,CAAC;CACxB;AA6ED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;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,CAoDjC;AAED;;;;;;;;;;;;;GAaG;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,CAOf"}
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"}