octocrawl 0.3.2 → 0.4.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/cli.js +213 -69
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3829,7 +3829,7 @@ function classifyGate(res) {
|
|
|
3829
3829
|
return { reason: "captcha", signals: px };
|
|
3830
3830
|
const wall = res.status >= 200 && res.status < 300 ? shortWallPage(res.body) : null;
|
|
3831
3831
|
if (contentful)
|
|
3832
|
-
return wall
|
|
3832
|
+
return wall;
|
|
3833
3833
|
const captcha = matched(lower, CAPTCHA_MARKERS);
|
|
3834
3834
|
if (captcha.length > 0) {
|
|
3835
3835
|
return { reason: "captcha", signals: captcha };
|
|
@@ -3845,8 +3845,6 @@ function classifyGate(res) {
|
|
|
3845
3845
|
signals.push("heading_sign_in");
|
|
3846
3846
|
return { reason: "login_wall", signals };
|
|
3847
3847
|
}
|
|
3848
|
-
if (wall !== null)
|
|
3849
|
-
return { reason: "bot_detected_generic", signals: wall };
|
|
3850
3848
|
const geo = matched(lower, GEO_MARKERS);
|
|
3851
3849
|
if (geo.length > 0) {
|
|
3852
3850
|
return { reason: "geo_restricted", signals: geo };
|
|
@@ -3873,18 +3871,29 @@ function classifyGate(res) {
|
|
|
3873
3871
|
};
|
|
3874
3872
|
}
|
|
3875
3873
|
}
|
|
3876
|
-
return
|
|
3874
|
+
return wall;
|
|
3877
3875
|
}
|
|
3878
3876
|
function shortWallPage(body) {
|
|
3879
|
-
if (
|
|
3877
|
+
if (!WALL_TRIGGER.test(body))
|
|
3880
3878
|
return null;
|
|
3881
3879
|
const page = pageText(body);
|
|
3882
3880
|
if (page === null)
|
|
3883
3881
|
return null;
|
|
3882
|
+
const bot = (signals) => ({ reason: "bot_detected_generic", signals });
|
|
3884
3883
|
if (AKAMAI_REFERENCE.test(page.text))
|
|
3885
|
-
return ["akamai_reference", "short_page"];
|
|
3884
|
+
return bot(["akamai_reference", "short_page"]);
|
|
3886
3885
|
if (page.headings.some((h) => UNUSUAL_ACTIVITY.test(h)) && AUTOMATED_TRAFFIC.test(page.text))
|
|
3887
|
-
return ["heading_unusual_activity", "text_automated_traffic", "short_page"];
|
|
3886
|
+
return bot(["heading_unusual_activity", "text_automated_traffic", "short_page"]);
|
|
3887
|
+
const shown = page.text.toLowerCase();
|
|
3888
|
+
const named = page.headings.join("\n").toLowerCase();
|
|
3889
|
+
const strong = matched(named, BOT_STRONG_MARKERS);
|
|
3890
|
+
if (strong.length > 0) {
|
|
3891
|
+
const person = shown.includes("activate and hold the button") ? ["text_activate_and_hold"] : [];
|
|
3892
|
+
return { reason: person.length > 0 ? "captcha" : "bot_detected_generic", signals: [...strong, ...person, "short_page"] };
|
|
3893
|
+
}
|
|
3894
|
+
const weak = matched(shown, WALL_WEAK_MARKERS);
|
|
3895
|
+
if (weak.length >= 2 && matched(named, WALL_WEAK_MARKERS).length > 0)
|
|
3896
|
+
return bot([...weak, "short_page"]);
|
|
3888
3897
|
return null;
|
|
3889
3898
|
}
|
|
3890
3899
|
function pageText(body) {
|
|
@@ -4002,7 +4011,7 @@ function nextLane(reason2, from) {
|
|
|
4002
4011
|
return from === "browser_proxy" ? null : "browser_proxy";
|
|
4003
4012
|
}
|
|
4004
4013
|
}
|
|
4005
|
-
var HEAD_BYTES, VENDOR_HEADERS, CF_STRONG_MARKERS, CF_INTERSTITIAL_PAIR, CAPTCHA_MARKERS, LOGIN_INTENT_PHRASES, PASSWORD_INPUT, TITLE_OR_H1, BARE_LOGIN_HEADING, GEO_MARKERS, BOT_STRONG_MARKERS, BOT_WEAK_MARKERS, GATE_SHAPED_STATUS, CF_JSD_PATH, PX_CAPTCHA_SCRIPT, PX_CAPTCHA_CONTAINER, WALL_PAGE_MAX_TEXT, AKAMAI_REFERENCE, UNUSUAL_ACTIVITY, AUTOMATED_TRAFFIC, HIDDEN_ELEMENTS, HEADING_ELEMENTS;
|
|
4014
|
+
var HEAD_BYTES, VENDOR_HEADERS, CF_STRONG_MARKERS, CF_INTERSTITIAL_PAIR, CAPTCHA_MARKERS, LOGIN_INTENT_PHRASES, PASSWORD_INPUT, TITLE_OR_H1, BARE_LOGIN_HEADING, GEO_MARKERS, BOT_STRONG_MARKERS, BOT_WEAK_MARKERS, GATE_SHAPED_STATUS, CF_JSD_PATH, PX_CAPTCHA_SCRIPT, PX_CAPTCHA_CONTAINER, WALL_PAGE_MAX_TEXT, AKAMAI_REFERENCE, UNUSUAL_ACTIVITY, WALL_TRIGGER, WALL_WEAK_MARKERS, AUTOMATED_TRAFFIC, HIDDEN_ELEMENTS, HEADING_ELEMENTS;
|
|
4006
4015
|
var init_gate = __esm({
|
|
4007
4016
|
"packages/http-core/dist/gate.js"() {
|
|
4008
4017
|
"use strict";
|
|
@@ -4096,6 +4105,8 @@ var init_gate = __esm({
|
|
|
4096
4105
|
WALL_PAGE_MAX_TEXT = 1500;
|
|
4097
4106
|
AKAMAI_REFERENCE = /\b(?:reference|incident)\b\s*(?:number|id|no\.?)?\s*[:#]?\s*#?\s*\d{1,3}\.[0-9a-f]{6,8}\.\d{10}\.[0-9a-f]{6,12}\b/i;
|
|
4098
4107
|
UNUSUAL_ACTIVITY = /\b(?:unusual|suspicious) (?:activity|traffic)\b/i;
|
|
4108
|
+
WALL_TRIGGER = /\b(?:reference|incident|unusual|suspicious|blocked|interruption|automated|robot|human|humans|bot|checking|wait|moment|security|ddos|denied)\b/i;
|
|
4109
|
+
WALL_WEAK_MARKERS = BOT_WEAK_MARKERS.filter(([, signal]) => signal !== "weak_enable_js_and_cookies" && signal !== "weak_enable_javascript");
|
|
4099
4110
|
AUTOMATED_TRAFFIC = /\bautomated (?:traffic|access|requests|queries|browsing)\b/i;
|
|
4100
4111
|
HIDDEN_ELEMENTS = /* @__PURE__ */ new Set(["script", "style", "noscript", "template", "svg"]);
|
|
4101
4112
|
HEADING_ELEMENTS = /* @__PURE__ */ new Set(["h1", "h2", "h3", "title"]);
|
|
@@ -8738,6 +8749,26 @@ function rawSignals(doc) {
|
|
|
8738
8749
|
markers.add("aria_busy");
|
|
8739
8750
|
return { scriptChars, markers: Array.from(markers), hydrationJson };
|
|
8740
8751
|
}
|
|
8752
|
+
function hasLoadingText(region) {
|
|
8753
|
+
const stack = [region];
|
|
8754
|
+
while (stack.length > 0) {
|
|
8755
|
+
const node = stack.pop();
|
|
8756
|
+
if (node.nodeType === 3) {
|
|
8757
|
+
const text2 = (node.textContent ?? "").trim();
|
|
8758
|
+
if (text2.length === 0 || text2.length > LOADING_TEXT_MAX_CHARS || !(LOADING_MESSAGE.test(text2) || LOADING_TRAILING.test(text2)))
|
|
8759
|
+
continue;
|
|
8760
|
+
if (node.parentElement?.closest("button, a") == null)
|
|
8761
|
+
return true;
|
|
8762
|
+
} else if (node.nodeType === 1) {
|
|
8763
|
+
const el = node;
|
|
8764
|
+
if (el.hasAttribute("hidden") || el.getAttribute("aria-hidden") === "true" || SCREEN_READER_ONLY.test(el.getAttribute("class") ?? ""))
|
|
8765
|
+
continue;
|
|
8766
|
+
for (const child of Array.from(el.childNodes))
|
|
8767
|
+
stack.push(child);
|
|
8768
|
+
}
|
|
8769
|
+
}
|
|
8770
|
+
return false;
|
|
8771
|
+
}
|
|
8741
8772
|
function countEmptyTables(doc) {
|
|
8742
8773
|
let empty = 0;
|
|
8743
8774
|
for (const table of qsa(doc, "table")) {
|
|
@@ -8778,6 +8809,8 @@ function detectRenderSignals(raw, cleaned, context = {}) {
|
|
|
8778
8809
|
reason2 = "hydration_shell";
|
|
8779
8810
|
else if (has2("aria_busy") && raw.scriptChars >= 1e3)
|
|
8780
8811
|
reason2 = "aria_busy";
|
|
8812
|
+
else if (context.loadingShown === true && textChars <= LOADING_PAGE_MAX_TEXT && raw.scriptChars >= 1e3)
|
|
8813
|
+
reason2 = "loading_text";
|
|
8781
8814
|
const listRecords = reason2 === null && context.listing === true && raw.hydrationJson.length > 0 ? partialRecordList(recordLists(raw), visible.toLowerCase()) : null;
|
|
8782
8815
|
if (listRecords !== null)
|
|
8783
8816
|
reason2 = "hydration_list_partial";
|
|
@@ -8791,14 +8824,14 @@ function detectRenderSignals(raw, cleaned, context = {}) {
|
|
|
8791
8824
|
reason: reason2
|
|
8792
8825
|
};
|
|
8793
8826
|
}
|
|
8794
|
-
var APP_ROOT_SELECTOR, JS_FALLBACK_CLASS, HYDRATION_SCRIPT, APP_ROOT_MAX_TEXT, HYDRATION_MIN_CHARS, NOTICE_MAX_TEXT, HYDRATION_TEXT_MIN_CHARS, HYDRATION_TEXT_MIN_SPACES, HYDRATION_MAX_JSON_CHARS, HYDRATION_MAX_PASSAGES, LIST_MIN_RECORDS, LIST_NAME_MIN_CHARS, LIST_MIN_SHOWN, LIST_MAX_NAMES, LIST_MAX_LISTS;
|
|
8827
|
+
var APP_ROOT_SELECTOR, JS_FALLBACK_CLASS, HYDRATION_SCRIPT, APP_ROOT_MAX_TEXT, HYDRATION_MIN_CHARS, NOTICE_MAX_TEXT, HYDRATION_TEXT_MIN_CHARS, HYDRATION_TEXT_MIN_SPACES, HYDRATION_MAX_JSON_CHARS, HYDRATION_MAX_PASSAGES, LIST_MIN_RECORDS, LIST_NAME_MIN_CHARS, LIST_MIN_SHOWN, LIST_MAX_NAMES, LIST_MAX_LISTS, LOADING_MESSAGE, LOADING_TRAILING, LOADING_TEXT_MAX_CHARS, LOADING_PAGE_MAX_TEXT, SCREEN_READER_ONLY;
|
|
8795
8828
|
var init_render = __esm({
|
|
8796
8829
|
"packages/extract-tf/dist/render.js"() {
|
|
8797
8830
|
"use strict";
|
|
8798
8831
|
init_dom();
|
|
8799
8832
|
APP_ROOT_SELECTOR = "#root, #app, #__next, #__nuxt, #___gatsby, [data-reactroot], [ng-app], [ng-version], [data-server-rendered]";
|
|
8800
8833
|
JS_FALLBACK_CLASS = /(^|[\s_-])(hide-if-js|hide-if-js-enabled|js-disabled|js-only|requires-js|needs-js)([\s_-]|$)/i;
|
|
8801
|
-
HYDRATION_SCRIPT = /window\.__[A-Za-z0-9_]+__\s*=|window\._[A-Za-z0-9_]*(?:STATE|CONFIG|DATA)\s*=|__NEXT_DATA__|__NUXT__/;
|
|
8834
|
+
HYDRATION_SCRIPT = /window\.__[A-Za-z0-9_]+__\s*=|window\._[A-Za-z0-9_]*(?:STATE|CONFIG|DATA)\s*=|window\.__(?:data|state)\s*=|__NEXT_DATA__|__NUXT__/;
|
|
8802
8835
|
APP_ROOT_MAX_TEXT = 200;
|
|
8803
8836
|
HYDRATION_MIN_CHARS = 2e3;
|
|
8804
8837
|
NOTICE_MAX_TEXT = 1500;
|
|
@@ -8811,6 +8844,11 @@ var init_render = __esm({
|
|
|
8811
8844
|
LIST_MIN_SHOWN = 3;
|
|
8812
8845
|
LIST_MAX_NAMES = 500;
|
|
8813
8846
|
LIST_MAX_LISTS = 50;
|
|
8847
|
+
LOADING_MESSAGE = /^(?:(?:loading|fetching|please wait|one moment|searching|retrieving)(?:\s+[\w-]+){0,3}\s*(?:\.{2,3}|\u2026)|please wait)$/i;
|
|
8848
|
+
LOADING_TRAILING = /\b(?:loading|fetching)\s*(?:\.{2,3}|\u2026)$/i;
|
|
8849
|
+
LOADING_TEXT_MAX_CHARS = 48;
|
|
8850
|
+
LOADING_PAGE_MAX_TEXT = 4e3;
|
|
8851
|
+
SCREEN_READER_ONLY = /(?:^|[\s_-])(?:sr-only|visually-?hidden|screen-?reader-(?:only|text))(?:$|[\s_-])|sronly|screenreaderonly/i;
|
|
8814
8852
|
}
|
|
8815
8853
|
});
|
|
8816
8854
|
|
|
@@ -8895,10 +8933,12 @@ function layoutParent(el) {
|
|
|
8895
8933
|
function selectMain(doc, blocks) {
|
|
8896
8934
|
if (blocks.length === 0)
|
|
8897
8935
|
return null;
|
|
8898
|
-
for (const sel of ["article", "main"]) {
|
|
8936
|
+
for (const sel of ["article", "main", '[role="main"]']) {
|
|
8899
8937
|
const el = doc.querySelector(sel);
|
|
8900
8938
|
if (!el)
|
|
8901
8939
|
continue;
|
|
8940
|
+
if (sel === '[role="main"]' && qsa(doc, "h1").some((h1) => !el.contains(h1)))
|
|
8941
|
+
continue;
|
|
8902
8942
|
const inside = blocks.filter((b) => el.contains(b.el));
|
|
8903
8943
|
if (inside.length >= blocks.length * 0.5)
|
|
8904
8944
|
return el;
|
|
@@ -9655,6 +9695,22 @@ function hasBuyBoxUnderH2(doc, prices) {
|
|
|
9655
9695
|
}
|
|
9656
9696
|
return true;
|
|
9657
9697
|
}
|
|
9698
|
+
function ownPriceAfterTitle(doc, names) {
|
|
9699
|
+
const h1 = qsa(doc, "h1")[0];
|
|
9700
|
+
if (h1 === void 0)
|
|
9701
|
+
return false;
|
|
9702
|
+
const prices = new Set(visiblePrices(doc));
|
|
9703
|
+
const all = qsa(doc, "*");
|
|
9704
|
+
const first2 = all.slice(all.indexOf(h1) + 1).find((el) => prices.has(el));
|
|
9705
|
+
if (first2 === void 0)
|
|
9706
|
+
return false;
|
|
9707
|
+
for (let up = first2.parentElement; up !== null && !up.contains(h1); up = up.parentElement) {
|
|
9708
|
+
const text2 = textOf(up).replace(/\s+/g, " ");
|
|
9709
|
+
if (names.some((name) => text2.includes(name)))
|
|
9710
|
+
return false;
|
|
9711
|
+
}
|
|
9712
|
+
return true;
|
|
9713
|
+
}
|
|
9658
9714
|
function countAll(doc) {
|
|
9659
9715
|
const textLength = (el) => (el?.textContent ?? "").replace(/\s+/g, " ").trim().length;
|
|
9660
9716
|
const textChars = textLength(doc.body);
|
|
@@ -9695,6 +9751,39 @@ function collectPageTypes(root, out) {
|
|
|
9695
9751
|
collectPageTypes(record3["@graph"], out);
|
|
9696
9752
|
}
|
|
9697
9753
|
}
|
|
9754
|
+
function ownListedProducts(root) {
|
|
9755
|
+
let most = [];
|
|
9756
|
+
for (const node of Array.isArray(root) ? root : [root]) {
|
|
9757
|
+
if (typeof node !== "object" || node === null)
|
|
9758
|
+
continue;
|
|
9759
|
+
const record3 = node;
|
|
9760
|
+
if (Array.isArray(record3["@graph"])) {
|
|
9761
|
+
const listed = ownListedProducts(record3["@graph"]);
|
|
9762
|
+
if (listed.length > most.length)
|
|
9763
|
+
most = listed;
|
|
9764
|
+
}
|
|
9765
|
+
if (!typeNames(record3).includes("itemlist"))
|
|
9766
|
+
continue;
|
|
9767
|
+
if (typeof record3["name"] === "string" && isRecommendationHeading(record3["name"]))
|
|
9768
|
+
continue;
|
|
9769
|
+
const products = [];
|
|
9770
|
+
for (const element of Array.isArray(record3["itemListElement"]) ? record3["itemListElement"] : []) {
|
|
9771
|
+
if (typeof element !== "object" || element === null)
|
|
9772
|
+
continue;
|
|
9773
|
+
const item = element["item"];
|
|
9774
|
+
const product = typeNames(element).includes("product") ? element : typeof item === "object" && item !== null && typeNames(item).includes("product") ? item : null;
|
|
9775
|
+
if (product !== null)
|
|
9776
|
+
products.push(typeof product["name"] === "string" ? product["name"].replace(/\s+/g, " ").trim() : "");
|
|
9777
|
+
}
|
|
9778
|
+
if (products.length > most.length)
|
|
9779
|
+
most = products;
|
|
9780
|
+
}
|
|
9781
|
+
return most;
|
|
9782
|
+
}
|
|
9783
|
+
function typeNames(node) {
|
|
9784
|
+
const t = node["@type"];
|
|
9785
|
+
return (typeof t === "string" ? [t] : Array.isArray(t) ? t.filter((type) => typeof type === "string") : []).map(normalizeTypeName);
|
|
9786
|
+
}
|
|
9698
9787
|
function collectJsonLdTypes(node, out, listed, inList = false) {
|
|
9699
9788
|
if (Array.isArray(node)) {
|
|
9700
9789
|
for (const item of node)
|
|
@@ -9711,7 +9800,7 @@ function collectJsonLdTypes(node, out, listed, inList = false) {
|
|
|
9711
9800
|
else
|
|
9712
9801
|
out.push(type);
|
|
9713
9802
|
}
|
|
9714
|
-
const list2 = inList || types.includes("itemlist");
|
|
9803
|
+
const list2 = inList || types.includes("itemlist") || COLLECTION_PAGE_TYPES.some((type) => types.includes(type));
|
|
9715
9804
|
for (const value of Object.values(node)) {
|
|
9716
9805
|
if (typeof value === "object" && value !== null)
|
|
9717
9806
|
collectJsonLdTypes(value, out, listed, list2);
|
|
@@ -9721,6 +9810,7 @@ function collectPageSignals(doc) {
|
|
|
9721
9810
|
const jsonLdTypes = [];
|
|
9722
9811
|
const listed = { products: 0 };
|
|
9723
9812
|
const pageTypes = [];
|
|
9813
|
+
let ownListed = [];
|
|
9724
9814
|
for (const el of qsa(doc, 'script[type="application/ld+json"]')) {
|
|
9725
9815
|
const text2 = (el.textContent ?? "").trim();
|
|
9726
9816
|
if (text2.length === 0)
|
|
@@ -9729,6 +9819,9 @@ function collectPageSignals(doc) {
|
|
|
9729
9819
|
const parsed = JSON.parse(text2);
|
|
9730
9820
|
collectJsonLdTypes(parsed, jsonLdTypes, listed);
|
|
9731
9821
|
collectPageTypes(parsed, pageTypes);
|
|
9822
|
+
const own = ownListedProducts(parsed);
|
|
9823
|
+
if (own.length > ownListed.length)
|
|
9824
|
+
ownListed = own;
|
|
9732
9825
|
} catch {
|
|
9733
9826
|
}
|
|
9734
9827
|
}
|
|
@@ -9747,7 +9840,9 @@ function collectPageSignals(doc) {
|
|
|
9747
9840
|
postArticles: qsa(doc, "article.post").length,
|
|
9748
9841
|
productCards: productCards(doc),
|
|
9749
9842
|
listedProducts: listed.products,
|
|
9750
|
-
pageTypes
|
|
9843
|
+
pageTypes,
|
|
9844
|
+
ownListedProducts: ownListed.length,
|
|
9845
|
+
ownListedNames: ownListed.filter((name) => name.length > 0)
|
|
9751
9846
|
};
|
|
9752
9847
|
}
|
|
9753
9848
|
function productCards(doc) {
|
|
@@ -9900,7 +9995,7 @@ function hasCardGrid(doc) {
|
|
|
9900
9995
|
function routeByCounts(c, s, doc) {
|
|
9901
9996
|
let grid;
|
|
9902
9997
|
const cardGrid = () => grid ??= hasCardGrid(doc);
|
|
9903
|
-
const listedOnly = s.listedProducts >= PRODUCT_CARDS && COLLECTION_PAGE_TYPES.some((type) => hasToken2(s.pageTypes, type)) && (s.productCards || !hasToken2(s.itemTypeTokens, "product"));
|
|
9998
|
+
const listedOnly = s.listedProducts >= PRODUCT_CARDS && (COLLECTION_PAGE_TYPES.some((type) => hasToken2(s.pageTypes, type)) || s.ownListedProducts >= PRODUCT_CARDS && !ownPriceAfterTitle(doc, s.ownListedNames)) && (s.productCards || !hasToken2(s.itemTypeTokens, "product"));
|
|
9904
9999
|
if ((s.productCards || listedOnly) && !c.buyBox && !hasToken2(s.jsonLdTypes, "product")) {
|
|
9905
10000
|
return { type: "collection", strategy: "article" };
|
|
9906
10001
|
}
|
|
@@ -11198,6 +11293,26 @@ function showsDeclaredProduct(main, product) {
|
|
|
11198
11293
|
const price = product.price.value.replace(/[^\d.,]/g, "");
|
|
11199
11294
|
return text2.includes(product.name.value.replace(/\s+/g, " ").trim()) && price !== "" && text2.includes(price);
|
|
11200
11295
|
}
|
|
11296
|
+
function hiddenRegion(region) {
|
|
11297
|
+
for (let el = region; el !== null; el = el.parentElement) {
|
|
11298
|
+
const hidden = el.getAttribute("hidden");
|
|
11299
|
+
if (hidden !== null && hidden.toLowerCase() !== "until-found" && !reactStreamedSegment(el))
|
|
11300
|
+
return true;
|
|
11301
|
+
}
|
|
11302
|
+
return false;
|
|
11303
|
+
}
|
|
11304
|
+
function reactStreamedSegment(el) {
|
|
11305
|
+
if (REACT_STREAMED_SEGMENT.test(el.id))
|
|
11306
|
+
return true;
|
|
11307
|
+
if (el.tagName.toLowerCase() !== "table" || el.id !== "")
|
|
11308
|
+
return false;
|
|
11309
|
+
const part = el.firstElementChild;
|
|
11310
|
+
if (part === null)
|
|
11311
|
+
return false;
|
|
11312
|
+
if (REACT_STREAMED_SEGMENT.test(part.id))
|
|
11313
|
+
return true;
|
|
11314
|
+
return part.tagName.toLowerCase() === "tbody" && part.id === "" && REACT_STREAMED_SEGMENT.test(part.firstElementChild?.id ?? "");
|
|
11315
|
+
}
|
|
11201
11316
|
function headingsOnly(region) {
|
|
11202
11317
|
const isHeading = (el) => /^h[1-6]$/.test(el.tagName.toLowerCase());
|
|
11203
11318
|
let headingChars = 0;
|
|
@@ -11231,7 +11346,7 @@ function headingsOnly(region) {
|
|
|
11231
11346
|
}
|
|
11232
11347
|
return true;
|
|
11233
11348
|
}
|
|
11234
|
-
var DEFAULT_CLASSIFY, BUY_BOX_CONFIDENCE, DESCRIPTION_MIN_CHARS, ExtractTf, extractTf, HEADINGS_ONLY_MAX_CHARS, MIN_PROSE_CHARS;
|
|
11349
|
+
var DEFAULT_CLASSIFY, BUY_BOX_CONFIDENCE, DESCRIPTION_MIN_CHARS, ExtractTf, extractTf, REACT_STREAMED_SEGMENT, HEADINGS_ONLY_MAX_CHARS, MIN_PROSE_CHARS;
|
|
11235
11350
|
var init_extract = __esm({
|
|
11236
11351
|
"packages/extract-tf/dist/extract.js"() {
|
|
11237
11352
|
"use strict";
|
|
@@ -11350,11 +11465,11 @@ var init_extract = __esm({
|
|
|
11350
11465
|
baseUrl,
|
|
11351
11466
|
metadata,
|
|
11352
11467
|
confidence: selected ? 1 : confidenceOf(blocks.filter((b) => main?.contains(b.el)).length, main, blocks.length, mainLength, decision.type, productShown, favorPrecision, favorRecall, product),
|
|
11353
|
-
// Escalate when a strategy produced nothing at all,
|
|
11354
|
-
// cascade a region that only names
|
|
11355
|
-
// last resort found is a list's).
|
|
11356
|
-
// not by itself an escalation reason.
|
|
11357
|
-
escalate: main === null || strategy === "article" && headingsOnly(main),
|
|
11468
|
+
// Escalate when a strategy produced nothing at all, a region the page
|
|
11469
|
+
// hides (hiddenRegion), or the article cascade a region that only names
|
|
11470
|
+
// the page (headingsOnly; a region the last resort found is a list's).
|
|
11471
|
+
// Routing to a non-article strategy is not by itself an escalation reason.
|
|
11472
|
+
escalate: main === null || hiddenRegion(main) || strategy === "article" && headingsOnly(main),
|
|
11358
11473
|
...lastResort ? { lastResort: true } : {},
|
|
11359
11474
|
pageType: decision.type,
|
|
11360
11475
|
strategy,
|
|
@@ -11368,7 +11483,8 @@ var init_extract = __esm({
|
|
|
11368
11483
|
productShown,
|
|
11369
11484
|
hydrationShown: dataShown,
|
|
11370
11485
|
contentShown: showsDeclaredProduct(main, product),
|
|
11371
|
-
listing: decision.type === "listing" || decision.type === "collection"
|
|
11486
|
+
listing: decision.type === "listing" || decision.type === "collection",
|
|
11487
|
+
loadingShown: main !== null && hasLoadingText(main)
|
|
11372
11488
|
}),
|
|
11373
11489
|
labelledValues: main ? collectLabelledValues(main) : [],
|
|
11374
11490
|
timings: { parseMs, extractMs: Math.max(0, performance.now() - extractionStart) }
|
|
@@ -11378,6 +11494,7 @@ var init_extract = __esm({
|
|
|
11378
11494
|
}
|
|
11379
11495
|
};
|
|
11380
11496
|
extractTf = new ExtractTf();
|
|
11497
|
+
REACT_STREAMED_SEGMENT = /S:[0-9a-f]+$/i;
|
|
11381
11498
|
HEADINGS_ONLY_MAX_CHARS = 100;
|
|
11382
11499
|
MIN_PROSE_CHARS = 3;
|
|
11383
11500
|
}
|
|
@@ -11388,7 +11505,7 @@ var EXTRACTOR_VERSION;
|
|
|
11388
11505
|
var init_version = __esm({
|
|
11389
11506
|
"packages/extract-tf/dist/version.js"() {
|
|
11390
11507
|
"use strict";
|
|
11391
|
-
EXTRACTOR_VERSION = "extract-tf/
|
|
11508
|
+
EXTRACTOR_VERSION = "extract-tf/19";
|
|
11392
11509
|
}
|
|
11393
11510
|
});
|
|
11394
11511
|
|
|
@@ -12983,6 +13100,7 @@ var init_identity = __esm({
|
|
|
12983
13100
|
var provider_exports = {};
|
|
12984
13101
|
__export(provider_exports, {
|
|
12985
13102
|
ProviderSubject: () => ProviderSubject,
|
|
13103
|
+
providerFailure: () => providerFailure,
|
|
12986
13104
|
robotsFetcherVia: () => robotsFetcherVia
|
|
12987
13105
|
});
|
|
12988
13106
|
function robotsFetcherVia(dispatcherFor) {
|
|
@@ -13013,6 +13131,40 @@ function isPlainText2(contentType) {
|
|
|
13013
13131
|
return true;
|
|
13014
13132
|
return contentType.toLowerCase().trimStart().startsWith("text/plain");
|
|
13015
13133
|
}
|
|
13134
|
+
function providerFailure(url, wallMs, trace, err, aborted) {
|
|
13135
|
+
return {
|
|
13136
|
+
requestedUrl: url,
|
|
13137
|
+
status: "failed",
|
|
13138
|
+
failureReason: aborted ? "timeout" : err instanceof Error && err.message.includes("net::ERR_NAME_NOT_RESOLVED") ? "dns_error" : "provider_error",
|
|
13139
|
+
blockReason: null,
|
|
13140
|
+
budgetExceeded: null,
|
|
13141
|
+
lane: "provider",
|
|
13142
|
+
escalations: [],
|
|
13143
|
+
markdown: null,
|
|
13144
|
+
truncated: false,
|
|
13145
|
+
truncatedAt: null,
|
|
13146
|
+
compliance: null,
|
|
13147
|
+
evidence: {
|
|
13148
|
+
finalUrl: url,
|
|
13149
|
+
httpStatus: null,
|
|
13150
|
+
redirectChain: [],
|
|
13151
|
+
contentType: null,
|
|
13152
|
+
rawBodySha256: null,
|
|
13153
|
+
artifacts: []
|
|
13154
|
+
},
|
|
13155
|
+
usage: {
|
|
13156
|
+
wallMs,
|
|
13157
|
+
bytesWire: 0,
|
|
13158
|
+
bytesDecompressed: 0,
|
|
13159
|
+
requestCount: 1,
|
|
13160
|
+
attemptCount: 1,
|
|
13161
|
+
contentTokens: null,
|
|
13162
|
+
browserMs: 0,
|
|
13163
|
+
externalCostUsd: null
|
|
13164
|
+
},
|
|
13165
|
+
trace
|
|
13166
|
+
};
|
|
13167
|
+
}
|
|
13016
13168
|
var defaultRobotsFetcher, ProviderSubject;
|
|
13017
13169
|
var init_provider2 = __esm({
|
|
13018
13170
|
"packages/bench/dist/subjects/provider.js"() {
|
|
@@ -13147,41 +13299,7 @@ var init_provider2 = __esm({
|
|
|
13147
13299
|
event: "provider_failed",
|
|
13148
13300
|
detail: { error: err instanceof Error ? err.message.slice(0, 200) : String(err) }
|
|
13149
13301
|
});
|
|
13150
|
-
return
|
|
13151
|
-
requestedUrl: url,
|
|
13152
|
-
status: "failed",
|
|
13153
|
-
// The provider broke, not the target. Reporting this as http_error
|
|
13154
|
-
// would blame the publisher for our vendor's outage. A target name
|
|
13155
|
-
// the vendor's browser could not resolve is a DNS fact, not a fault.
|
|
13156
|
-
failureReason: execution.signal?.aborted ? "timeout" : err instanceof Error && err.message.includes("net::ERR_NAME_NOT_RESOLVED") ? "dns_error" : "provider_error",
|
|
13157
|
-
blockReason: null,
|
|
13158
|
-
budgetExceeded: null,
|
|
13159
|
-
lane: "provider",
|
|
13160
|
-
escalations: [],
|
|
13161
|
-
markdown: null,
|
|
13162
|
-
truncated: false,
|
|
13163
|
-
truncatedAt: null,
|
|
13164
|
-
compliance: null,
|
|
13165
|
-
evidence: {
|
|
13166
|
-
finalUrl: url,
|
|
13167
|
-
httpStatus: null,
|
|
13168
|
-
redirectChain: [],
|
|
13169
|
-
contentType: null,
|
|
13170
|
-
rawBodySha256: null,
|
|
13171
|
-
artifacts: []
|
|
13172
|
-
},
|
|
13173
|
-
usage: {
|
|
13174
|
-
wallMs: wallMs2,
|
|
13175
|
-
bytesWire: 0,
|
|
13176
|
-
bytesDecompressed: 0,
|
|
13177
|
-
requestCount: 1,
|
|
13178
|
-
attemptCount: 1,
|
|
13179
|
-
contentTokens: null,
|
|
13180
|
-
browserMs: 0,
|
|
13181
|
-
externalCostUsd: null
|
|
13182
|
-
},
|
|
13183
|
-
trace
|
|
13184
|
-
};
|
|
13302
|
+
return providerFailure(url, wallMs2, trace, err, execution.signal?.aborted === true);
|
|
13185
13303
|
}
|
|
13186
13304
|
const wallMs = Date.now() - start;
|
|
13187
13305
|
const observedUa = res.sentUserAgent ?? null;
|
|
@@ -25202,6 +25320,7 @@ function buildChannels(mode, opts = {}) {
|
|
|
25202
25320
|
}
|
|
25203
25321
|
return await pending;
|
|
25204
25322
|
};
|
|
25323
|
+
const requestDeadlines = /* @__PURE__ */ new WeakMap();
|
|
25205
25324
|
const raw = {
|
|
25206
25325
|
id: "provider",
|
|
25207
25326
|
vendorId,
|
|
@@ -25254,13 +25373,34 @@ function buildChannels(mode, opts = {}) {
|
|
|
25254
25373
|
};
|
|
25255
25374
|
return skip;
|
|
25256
25375
|
}
|
|
25257
|
-
|
|
25258
|
-
|
|
25259
|
-
|
|
25260
|
-
|
|
25261
|
-
|
|
25376
|
+
const start = Date.now();
|
|
25377
|
+
const requestDeadlineAt = execution !== void 0 && requestDeadlines.has(execution) ? requestDeadlines.get(execution) : execution?.deadlineAt;
|
|
25378
|
+
const requestEnded = () => execution?.signal?.aborted === true || requestDeadlineAt !== void 0 && Date.now() >= requestDeadlineAt;
|
|
25379
|
+
const sessionEnded = () => execution?.deadlineAt !== void 0 && Date.now() >= execution.deadlineAt;
|
|
25380
|
+
const failed = async (err) => {
|
|
25381
|
+
const { providerFailure: providerFailure2 } = await Promise.resolve().then(() => (init_provider2(), provider_exports));
|
|
25382
|
+
const wallMs = Date.now() - start;
|
|
25383
|
+
const message2 = ops.secrets.reduce((m, secret) => scrubSecret(m, secret), err instanceof Error ? err.message : String(err)).slice(0, 200);
|
|
25384
|
+
return providerFailure2(url, wallMs, [
|
|
25385
|
+
{ at: 0, lane: "provider", event: "provider_selected", detail: { provider: vendorId } },
|
|
25386
|
+
{ at: wallMs, lane: "provider", event: "provider_failed", detail: { error: message2 } }
|
|
25387
|
+
], err, sessionEnded());
|
|
25388
|
+
};
|
|
25389
|
+
let vendor;
|
|
25390
|
+
try {
|
|
25391
|
+
if (sessionApplies && session.resume !== void 0 && session.resume !== null) {
|
|
25392
|
+
pendingResume = session.resume;
|
|
25393
|
+
persistenceAttempted = true;
|
|
25394
|
+
} else {
|
|
25395
|
+
await preparePersistence(execution);
|
|
25396
|
+
}
|
|
25397
|
+
vendor = tariff !== null ? await connectOwn(execution) : await ensureConnected(execution);
|
|
25398
|
+
} catch (err) {
|
|
25399
|
+
if (requestEnded())
|
|
25400
|
+
throw err;
|
|
25401
|
+
return await failed(err);
|
|
25262
25402
|
}
|
|
25263
|
-
const { declaration, transport } =
|
|
25403
|
+
const { declaration, transport } = vendor;
|
|
25264
25404
|
try {
|
|
25265
25405
|
if (pendingResume !== null) {
|
|
25266
25406
|
transport.useResumedSession(pendingResume);
|
|
@@ -25268,6 +25408,10 @@ function buildChannels(mode, opts = {}) {
|
|
|
25268
25408
|
const { ProviderSubject: ProviderSubject2 } = await Promise.resolve().then(() => (init_provider2(), provider_exports));
|
|
25269
25409
|
const subject = new ProviderSubject2(declaration, transport, mode, null, providerRobots);
|
|
25270
25410
|
return await subject.fetch(url, execution?.deadlineAt, execution?.signal, execution?.onRetryAfter, options);
|
|
25411
|
+
} catch (err) {
|
|
25412
|
+
if (requestEnded() || !sessionEnded())
|
|
25413
|
+
throw err;
|
|
25414
|
+
return await failed(err);
|
|
25271
25415
|
} finally {
|
|
25272
25416
|
if (tariff !== null)
|
|
25273
25417
|
await transport.close().catch(() => {
|
|
@@ -25293,7 +25437,9 @@ function buildChannels(mode, opts = {}) {
|
|
|
25293
25437
|
fetch: async (url, session, execution, options) => {
|
|
25294
25438
|
if (tariff === null || tariff.maxSessionMs === null)
|
|
25295
25439
|
return raw.fetch(url, session, execution, options);
|
|
25296
|
-
|
|
25440
|
+
const held = { ...execution, deadlineAt: Math.min(execution?.deadlineAt ?? Number.POSITIVE_INFINITY, Date.now() + tariff.maxSessionMs) };
|
|
25441
|
+
requestDeadlines.set(held, execution?.deadlineAt);
|
|
25442
|
+
return raw.fetch(url, session, held, options);
|
|
25297
25443
|
}
|
|
25298
25444
|
};
|
|
25299
25445
|
};
|
|
@@ -25708,7 +25854,7 @@ import { mkdir as mkdir7, writeFile as writeFile6 } from "fs/promises";
|
|
|
25708
25854
|
init_dist();
|
|
25709
25855
|
|
|
25710
25856
|
// packages/sdk/dist/version.js
|
|
25711
|
-
var SDK_VERSION = "0.
|
|
25857
|
+
var SDK_VERSION = "0.4.0";
|
|
25712
25858
|
|
|
25713
25859
|
// packages/sdk/dist/watcher.js
|
|
25714
25860
|
init_dist();
|
|
@@ -31638,10 +31784,8 @@ function createApp(engine, options = {}) {
|
|
|
31638
31784
|
return fail(c, "invalid_request", "cursor is not one this API issued");
|
|
31639
31785
|
if (err instanceof SyntaxError)
|
|
31640
31786
|
return fail(c, "invalid_json", "body must be JSON");
|
|
31641
|
-
if (options.exposeInternalErrors === true)
|
|
31642
|
-
return fail(c, "internal_error", err.message);
|
|
31643
31787
|
console.error(JSON.stringify({ component: "api", method: c.req.method, path: c.req.path, error: err.stack ?? String(err) }));
|
|
31644
|
-
return fail(c, "internal_error", "internal error");
|
|
31788
|
+
return fail(c, "internal_error", options.exposeInternalErrors === true ? err.message : "internal error");
|
|
31645
31789
|
});
|
|
31646
31790
|
return app;
|
|
31647
31791
|
}
|
|
@@ -32275,7 +32419,7 @@ ${LOGIN_USAGE}`);
|
|
|
32275
32419
|
}
|
|
32276
32420
|
|
|
32277
32421
|
// packages/cli/src/run.ts
|
|
32278
|
-
var CLI_VERSION = "0.
|
|
32422
|
+
var CLI_VERSION = "0.4.0";
|
|
32279
32423
|
async function runCli(argv, io) {
|
|
32280
32424
|
const [name, ...rest] = argv;
|
|
32281
32425
|
if (name === void 0 || name === "--help" || name === "help") {
|
package/package.json
CHANGED