@enricai/barnacle 1.12.47 → 1.12.49
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/recon/capture-filters.d.ts +48 -5
- package/dist/recon/capture-filters.d.ts.map +1 -1
- package/dist/recon/capture-filters.js +99 -8
- package/dist/recon/capture-filters.js.map +1 -1
- package/dist/scraper/captcha-callback-capture.d.ts +6 -0
- package/dist/scraper/captcha-callback-capture.d.ts.map +1 -1
- package/dist/scraper/captcha-callback-capture.js +49 -1
- package/dist/scraper/captcha-callback-capture.js.map +1 -1
- package/dist/scraper/flow-runner.d.ts +44 -0
- package/dist/scraper/flow-runner.d.ts.map +1 -1
- package/dist/scraper/flow-runner.js +72 -7
- package/dist/scraper/flow-runner.js.map +1 -1
- package/dist/scripts/recon-generate-multicall-fixture.d.ts +17 -0
- package/dist/scripts/recon-generate-multicall-fixture.d.ts.map +1 -1
- package/dist/scripts/recon-generate-multicall-fixture.js +54 -0
- package/dist/scripts/recon-generate-multicall-fixture.js.map +1 -1
- package/dist/scripts/recon-generate.d.ts +13 -1
- package/dist/scripts/recon-generate.d.ts.map +1 -1
- package/dist/scripts/recon-generate.js +154 -11
- package/dist/scripts/recon-generate.js.map +1 -1
- package/package.json +1 -1
|
@@ -89,6 +89,7 @@ exports.dispatchJqueryChangeEvent = dispatchJqueryChangeEvent;
|
|
|
89
89
|
exports.verifyDomEffect = verifyDomEffect;
|
|
90
90
|
exports.narrowInvalidFormControl = narrowInvalidFormControl;
|
|
91
91
|
exports.formatStepPrefix = formatStepPrefix;
|
|
92
|
+
exports.tryDeterministicFieldLabelActuation = tryDeterministicFieldLabelActuation;
|
|
92
93
|
exports.probeStepBeforeAttempts = probeStepBeforeAttempts;
|
|
93
94
|
exports.extractLinkFromMessage = extractLinkFromMessage;
|
|
94
95
|
exports.extractCodeFromMessage = extractCodeFromMessage;
|
|
@@ -636,6 +637,39 @@ const PROMPT_EMPTY_VALUE_RX_FLAGS = PROMPT_EMPTY_VALUE_RX.flags;
|
|
|
636
637
|
* the remaining `textContent`.
|
|
637
638
|
*/
|
|
638
639
|
const BUTTON_VALUE_EXPR = "((el) => { const c = el.cloneNode(true); for (const n of c.querySelectorAll(\"[role='option'],[role='listbox'],[aria-hidden='true'],abbr,svg\")) n.remove(); return c.textContent || \"\"; })";
|
|
640
|
+
/**
|
|
641
|
+
* Browser-side expression resolving a `role='option'` element's accessible
|
|
642
|
+
* label when its own `textContent`/`data-value` is empty — a widget-kit may
|
|
643
|
+
* carry the visible text on `aria-label`, `aria-labelledby` (standards-first,
|
|
644
|
+
* mirroring {@link labelFor}/`widgetLabel`'s id-resolution), `title`, or a
|
|
645
|
+
* descendant node instead of the option's own text node. Shared by option
|
|
646
|
+
* enumeration and by {@link PROMPT_CURRENT_TEXT_EXPR}'s
|
|
647
|
+
* `aria-activedescendant` readback so a widget can be driven and confirmed
|
|
648
|
+
* committed via the same resolved label regardless of which node carries it.
|
|
649
|
+
*/
|
|
650
|
+
const OPTION_ACCESSIBLE_LABEL_EXPR = `((el) => {
|
|
651
|
+
const norm = (s) => (s || "").replace(/\\s+/g, " ").trim();
|
|
652
|
+
const auto = el.getAttribute && el.getAttribute("data-automation-label");
|
|
653
|
+
if (auto && auto.trim()) return norm(auto);
|
|
654
|
+
const alb = el.getAttribute && el.getAttribute("aria-labelledby");
|
|
655
|
+
if (alb) {
|
|
656
|
+
const parts = [];
|
|
657
|
+
for (const id of alb.split(/\\s+/)) { const ref = document.getElementById(id); if (ref && ref.textContent) parts.push(ref.textContent); }
|
|
658
|
+
if (parts.length) return norm(parts.join(" "));
|
|
659
|
+
}
|
|
660
|
+
const al = el.getAttribute && el.getAttribute("aria-label");
|
|
661
|
+
if (al && al.trim()) return norm(al);
|
|
662
|
+
const own = el.textContent || "";
|
|
663
|
+
if (own.trim()) return norm(own);
|
|
664
|
+
const title = el.getAttribute && el.getAttribute("title");
|
|
665
|
+
if (title && title.trim()) return norm(title);
|
|
666
|
+
const desc = el.querySelector && el.querySelector("[aria-label],[title]");
|
|
667
|
+
if (desc) {
|
|
668
|
+
const dal = desc.getAttribute("aria-label") || desc.getAttribute("title");
|
|
669
|
+
if (dal && dal.trim()) return norm(dal);
|
|
670
|
+
}
|
|
671
|
+
return "";
|
|
672
|
+
})`;
|
|
639
673
|
/**
|
|
640
674
|
* Browser-side expression reading a prompt-selector widget's OWN committed-value
|
|
641
675
|
* text — the single source of truth for "is this widget filled?", shared by the
|
|
@@ -651,8 +685,9 @@ const BUTTON_VALUE_EXPR = "((el) => { const c = el.cloneNode(true); for (const n
|
|
|
651
685
|
const PROMPT_CURRENT_TEXT_EXPR = `((w, valueSel, emptyRx) => {
|
|
652
686
|
const norm = (s) => (s || "").replace(/\\s+/g, " ").trim().toLowerCase();
|
|
653
687
|
const buttonValue = ${BUTTON_VALUE_EXPR};
|
|
688
|
+
const optionLabel = ${OPTION_ACCESSIBLE_LABEL_EXPR};
|
|
654
689
|
const adid = w.getAttribute && w.getAttribute("aria-activedescendant");
|
|
655
|
-
if (adid) { const opt = document.getElementById(adid); if (opt && opt
|
|
690
|
+
if (adid) { const opt = document.getElementById(adid); if (opt && optionLabel(opt)) return norm(optionLabel(opt)); }
|
|
656
691
|
if (w.tagName === "INPUT" && (w.value || "").trim()) return norm(w.value);
|
|
657
692
|
if (w.tagName === "BUTTON" && buttonValue(w).trim()) return norm(buttonValue(w));
|
|
658
693
|
const lbl = w.matches(valueSel) ? w : w.querySelector(valueSel);
|
|
@@ -5975,6 +6010,7 @@ async function tryPromptSelectorPrimitive(params) {
|
|
|
5975
6010
|
return null;
|
|
5976
6011
|
}
|
|
5977
6012
|
const enumerateOptionsExpr = `((widgetMarkAttr, wIdx, markAttr, optionSel, searchSel) => {
|
|
6013
|
+
const optionLabel = ${OPTION_ACCESSIBLE_LABEL_EXPR};
|
|
5978
6014
|
const w = document.querySelector("[" + widgetMarkAttr + '="' + wIdx + '"]');
|
|
5979
6015
|
if (!w) return { optionsPresent: false };
|
|
5980
6016
|
// Clear stale option + filter marks from a PRIOR prompt-selector call on
|
|
@@ -6009,8 +6045,8 @@ async function tryPromptSelectorPrimitive(params) {
|
|
|
6009
6045
|
const options = [];
|
|
6010
6046
|
for (let i = 0; i < opts.length; i++) {
|
|
6011
6047
|
opts[i].setAttribute(markAttr, String(i));
|
|
6012
|
-
const label =
|
|
6013
|
-
options.push({ oIdx: i, text: label
|
|
6048
|
+
const label = optionLabel(opts[i]);
|
|
6049
|
+
options.push({ oIdx: i, text: label });
|
|
6014
6050
|
}
|
|
6015
6051
|
// scopeIsDocument distinguishes a genuinely resolved (portal or inline)
|
|
6016
6052
|
// popup from the document-wide last-resort fallback in
|
|
@@ -6209,6 +6245,7 @@ async function commitPromptOption(params) {
|
|
|
6209
6245
|
const isPairedHiddenSelect = ${exports.OPENER_PAIRED_HIDDEN_SELECT_EL_EXPR};
|
|
6210
6246
|
const norm = (s) => (s || "").replace(/\\s+/g, " ").trim().toLowerCase();
|
|
6211
6247
|
const buttonValue = ${BUTTON_VALUE_EXPR};
|
|
6248
|
+
const optionLabel = ${OPTION_ACCESSIBLE_LABEL_EXPR};
|
|
6212
6249
|
const emptyRx = new RegExp(emptyRxSrc, emptyRxFlags);
|
|
6213
6250
|
const w = document.querySelector("[" + markAttr + '="' + wIdx + '"]');
|
|
6214
6251
|
if (!w) return { ok: false, id: "" };
|
|
@@ -6216,7 +6253,7 @@ async function commitPromptOption(params) {
|
|
|
6216
6253
|
// own value text (popup-pollution-safe, see BUTTON_VALUE_EXPR), or a
|
|
6217
6254
|
// selection-label node (empty-state phrase treated as no value).
|
|
6218
6255
|
const adid = w.getAttribute && w.getAttribute("aria-activedescendant");
|
|
6219
|
-
const adText = adid && document.getElementById(adid) ? norm(document.getElementById(adid)
|
|
6256
|
+
const adText = adid && document.getElementById(adid) ? norm(optionLabel(document.getElementById(adid))) : "";
|
|
6220
6257
|
const own = w.tagName === "INPUT" ? norm(w.value || "") : w.tagName === "BUTTON" ? norm(buttonValue(w)) : "";
|
|
6221
6258
|
const lbl = w.matches(valueSel) ? w : w.querySelector(valueSel);
|
|
6222
6259
|
const lblRaw = lbl ? (lbl.textContent || "") : "";
|
|
@@ -7142,7 +7179,11 @@ async function resolveDeepLocatorCandidatesWithWidening(page, frameSelector, ins
|
|
|
7142
7179
|
* `not-fill-or-select`, never the first attempt for a step that names a
|
|
7143
7180
|
* field. `triedSelectors` (already-attempted selectors from prior
|
|
7144
7181
|
* cascade attempts) is excluded so a step that already tried and failed on
|
|
7145
|
-
* this candidate doesn't just re-pick it.
|
|
7182
|
+
* this candidate doesn't just re-pick it. A "select" match that resolves to
|
|
7183
|
+
* an opener-paired-hidden `<select>` (see the loop below) is skipped rather
|
|
7184
|
+
* than actuated — that element belongs to `tryPromptSelectorPrimitive`,
|
|
7185
|
+
* which already had first refusal on it, and empty-valued `<option>`s can
|
|
7186
|
+
* never corroborate a write landed on the right one.
|
|
7146
7187
|
*/
|
|
7147
7188
|
async function tryDeterministicFieldLabelActuation(params) {
|
|
7148
7189
|
const { page, frameTarget, step, triedSelectors, timeoutOptions } = params;
|
|
@@ -7150,8 +7191,32 @@ async function tryDeterministicFieldLabelActuation(params) {
|
|
|
7150
7191
|
if (!fieldTarget)
|
|
7151
7192
|
return { kind: "not-fill-or-select" };
|
|
7152
7193
|
const { candidates, innerSelector } = await resolveDeepLocatorCandidatesWithWidening(page, frameTarget.frameSelector, step, timeoutOptions);
|
|
7153
|
-
|
|
7154
|
-
|
|
7194
|
+
let unexcluded = candidates.filter((c) => !triedSelectors.includes(c.selector));
|
|
7195
|
+
let matched = findDeepLocatorCandidateByFieldLabel(unexcluded, fieldTarget.fieldLabel);
|
|
7196
|
+
// A "select" field-label match can resolve to a design-system combobox
|
|
7197
|
+
// opener's paired-but-hidden shadow <select> (Base Web etc.) — the same
|
|
7198
|
+
// element `trySelectPrimitive`/`applySelectValue` already refuse to write
|
|
7199
|
+
// via `OPENER_PAIRED_HIDDEN_SELECT_EL_EXPR`, because `tryPromptSelectorPrimitive`
|
|
7200
|
+
// is the primitive that owns that opener widget and already had first
|
|
7201
|
+
// refusal on it. Writing here instead bypasses that ownership: the opener's
|
|
7202
|
+
// widget never observes the write, and when every <option> carries an empty
|
|
7203
|
+
// `value` attribute the write can't even corroborate against the select's
|
|
7204
|
+
// OWN state (`sel.value` stays "" regardless of which option got picked).
|
|
7205
|
+
// Skip a matched candidate that resolves to one, falling back to the next
|
|
7206
|
+
// named candidate (or no-match) instead of ever attempting that write.
|
|
7207
|
+
while (matched && fieldTarget.kind === "select") {
|
|
7208
|
+
const matchedIsPairedHiddenSelect = await frameTarget
|
|
7209
|
+
.evaluate(`((innerSelector, index) => {
|
|
7210
|
+
const isOpenerPairedHidden = ${exports.OPENER_PAIRED_HIDDEN_SELECT_EL_EXPR};
|
|
7211
|
+
const el = Array.from(document.querySelectorAll(innerSelector))[index];
|
|
7212
|
+
return !!el && isOpenerPairedHidden(el);
|
|
7213
|
+
})(${JSON.stringify(innerSelector)}, ${JSON.stringify(matched.index)})`)
|
|
7214
|
+
.catch(() => false);
|
|
7215
|
+
if (!matchedIsPairedHiddenSelect)
|
|
7216
|
+
break;
|
|
7217
|
+
unexcluded = unexcluded.filter((c) => c.selector !== matched?.selector);
|
|
7218
|
+
matched = findDeepLocatorCandidateByFieldLabel(unexcluded, fieldTarget.fieldLabel);
|
|
7219
|
+
}
|
|
7155
7220
|
if (!matched)
|
|
7156
7221
|
return { kind: "no-match", fieldTarget };
|
|
7157
7222
|
const actuated = fieldTarget.kind === "fill"
|