mcp-scraper 0.69.0 → 0.72.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/CHANGELOG.md +60 -2
- package/README.md +6 -2
- package/dist/{analytics-repository-62CAZAIP.js → analytics-repository-6YLRSWYO.js} +3 -2
- package/dist/bin/api-server.cjs +18592 -12680
- package/dist/bin/api-server.js +4 -4
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +3 -3
- package/dist/bin/mcp-scraper-install.js +2 -2
- package/dist/bin/mcp-stdio-server.cjs +10492 -9129
- package/dist/bin/mcp-stdio-server.js +9 -7
- package/dist/bin/paa-harvest.cjs +308 -41
- package/dist/bin/paa-harvest.js +4 -3
- package/dist/{chunk-M7D7WO75.js → chunk-2N4V4LVU.js} +3178 -2019
- package/dist/chunk-7RQULQF3.js +263 -0
- package/dist/{chunk-UL4ZKAWZ.js → chunk-A66DGFOU.js} +1 -1
- package/dist/{chunk-ES25GP6C.js → chunk-CXY5WV45.js} +24 -34
- package/dist/{chunk-Y46YNQMM.js → chunk-F5TK4KMT.js} +1 -1
- package/dist/{chunk-62LEXS5O.js → chunk-F6MUGMRN.js} +306 -44
- package/dist/{chunk-5RFQGIYC.js → chunk-FVL4GUTP.js} +2 -2
- package/dist/{chunk-OZIUFN6B.js → chunk-I45JV4EU.js} +2 -2
- package/dist/{chunk-2LXTOAKS.js → chunk-KIPIBPUB.js} +9 -1
- package/dist/chunk-MB72PA6S.js +2003 -0
- package/dist/chunk-OM7HVEJ3.js +26 -0
- package/dist/chunk-PJEEKOUM.js +404 -0
- package/dist/{chunk-RX2QAHML.js → chunk-RAFQEPJ4.js} +2 -2
- package/dist/{chunk-KO5CK5ZT.js → chunk-SR7GSLEA.js} +202 -441
- package/dist/chunk-T3MZISOF.js +240 -0
- package/dist/{chunk-ULYOCJUJ.js → chunk-TUTQHP4O.js} +1 -1
- package/dist/{chunk-65FTMB7G.js → chunk-WEFPBAAG.js} +180 -9
- package/dist/{chunk-LN6N3YLK.js → chunk-WFQ2E4WE.js} +1 -1
- package/dist/{db-ZGZAKYBW.js → db-566MOHHD.js} +7 -1
- package/dist/{extract-bundle-SXSJCPQI.js → extract-bundle-337ZDIIQ.js} +4 -4
- package/dist/gmail-service-E276SBJU.js +25 -0
- package/dist/index.cjs +322 -49
- package/dist/index.d.cts +15 -2
- package/dist/index.d.ts +15 -2
- package/dist/index.js +4 -3
- package/dist/{lead-list-enrichment-repository-CXLVIV3R.js → lead-list-enrichment-repository-WWEMJ7EN.js} +2 -2
- package/dist/{location-data-repository-G72M7DTN.js → location-data-repository-H4B2BWGY.js} +2 -2
- package/dist/{server-WCNYXILA.js → server-UGWC5YMD.js} +8376 -6616
- package/dist/{site-extract-repository-RLB3TFJ3.js → site-extract-repository-ACNAVNDN.js} +3 -3
- package/dist/{worker-KHSJG365.js → worker-JQORPCFQ.js} +9 -7
- package/package.json +1 -1
- package/dist/chunk-2L4C4DAZ.js +0 -103
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
browserServiceApiKey,
|
|
3
|
+
browserServiceProxyId
|
|
4
|
+
} from "./chunk-OM7HVEJ3.js";
|
|
1
5
|
import {
|
|
2
6
|
HEADLESS_HARVEST_TEST_ENV,
|
|
3
7
|
HEADLESS_OPS,
|
|
4
|
-
browserServiceApiKey,
|
|
5
|
-
browserServiceProxyId,
|
|
6
8
|
currentCostContext,
|
|
7
9
|
recordKernelSession,
|
|
8
10
|
runWithCostContext
|
|
9
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-CXY5WV45.js";
|
|
10
12
|
import {
|
|
11
13
|
DEFAULT_PROXY_MODE,
|
|
12
14
|
HarvestOptionsSchema,
|
|
@@ -21,7 +23,7 @@ import {
|
|
|
21
23
|
RECAPTCHA_INSTRUCTIONS,
|
|
22
24
|
RequestAbortedError,
|
|
23
25
|
sanitizeVendorName
|
|
24
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-WEFPBAAG.js";
|
|
25
27
|
|
|
26
28
|
// src/driver/BrowserDriver.ts
|
|
27
29
|
import { chromium } from "playwright-extra";
|
|
@@ -659,6 +661,13 @@ var BrowserDriver = class {
|
|
|
659
661
|
getKernelSessionId() {
|
|
660
662
|
return this.kernelSessionId;
|
|
661
663
|
}
|
|
664
|
+
getProviderSessionMetadata() {
|
|
665
|
+
return {
|
|
666
|
+
provider: this.kernelSessionId ? "kernel" : "local",
|
|
667
|
+
providerSessionId: this.kernelSessionId,
|
|
668
|
+
disconnectObservation: null
|
|
669
|
+
};
|
|
670
|
+
}
|
|
662
671
|
getDebugSnapshot() {
|
|
663
672
|
return this.debugSnapshot;
|
|
664
673
|
}
|
|
@@ -673,6 +682,7 @@ var BrowserDriver = class {
|
|
|
673
682
|
const proxySource = this.kernelProxySource;
|
|
674
683
|
const proxyType = this.kernelProxyType;
|
|
675
684
|
const headlessSent = this.kernelHeadlessSent;
|
|
685
|
+
const provider = this.getProviderSessionMetadata();
|
|
676
686
|
this.browser = null;
|
|
677
687
|
this.context = null;
|
|
678
688
|
this.page = null;
|
|
@@ -707,7 +717,11 @@ var BrowserDriver = class {
|
|
|
707
717
|
kernelDeleteSucceeded: deleteResult.status === "fulfilled",
|
|
708
718
|
kernelDeleteError: deleteResult.status === "rejected" ? deleteResult.reason instanceof Error ? deleteResult.reason.message : String(deleteResult.reason) : null,
|
|
709
719
|
browserCloseSucceeded: closeResult.status === "fulfilled",
|
|
710
|
-
browserCloseError: closeResult.status === "rejected" ? closeResult.reason instanceof Error ? closeResult.reason.message : String(closeResult.reason) : null
|
|
720
|
+
browserCloseError: closeResult.status === "rejected" ? closeResult.reason instanceof Error ? closeResult.reason.message : String(closeResult.reason) : null,
|
|
721
|
+
provider: provider.provider,
|
|
722
|
+
providerSessionId: provider.providerSessionId,
|
|
723
|
+
providerDisconnectObserved: false,
|
|
724
|
+
providerDisconnectMessage: null
|
|
711
725
|
};
|
|
712
726
|
if (deleteResult.status === "rejected") {
|
|
713
727
|
console.warn(JSON.stringify({
|
|
@@ -739,7 +753,11 @@ var BrowserDriver = class {
|
|
|
739
753
|
kernelDeleteSucceeded: null,
|
|
740
754
|
kernelDeleteError: null,
|
|
741
755
|
browserCloseSucceeded: true,
|
|
742
|
-
browserCloseError: null
|
|
756
|
+
browserCloseError: null,
|
|
757
|
+
provider: provider.provider,
|
|
758
|
+
providerSessionId: provider.providerSessionId,
|
|
759
|
+
providerDisconnectObserved: false,
|
|
760
|
+
providerDisconnectMessage: null
|
|
743
761
|
};
|
|
744
762
|
} else if (this.context) {
|
|
745
763
|
const ctx = this.context;
|
|
@@ -752,7 +770,11 @@ var BrowserDriver = class {
|
|
|
752
770
|
kernelDeleteSucceeded: null,
|
|
753
771
|
kernelDeleteError: null,
|
|
754
772
|
browserCloseSucceeded: true,
|
|
755
|
-
browserCloseError: null
|
|
773
|
+
browserCloseError: null,
|
|
774
|
+
provider: "local",
|
|
775
|
+
providerSessionId: null,
|
|
776
|
+
providerDisconnectObserved: false,
|
|
777
|
+
providerDisconnectMessage: null
|
|
756
778
|
};
|
|
757
779
|
}
|
|
758
780
|
return {
|
|
@@ -761,13 +783,64 @@ var BrowserDriver = class {
|
|
|
761
783
|
kernelDeleteSucceeded: null,
|
|
762
784
|
kernelDeleteError: null,
|
|
763
785
|
browserCloseSucceeded: null,
|
|
764
|
-
browserCloseError: null
|
|
786
|
+
browserCloseError: null,
|
|
787
|
+
provider: "local",
|
|
788
|
+
providerSessionId: null,
|
|
789
|
+
providerDisconnectObserved: false,
|
|
790
|
+
providerDisconnectMessage: null
|
|
765
791
|
};
|
|
766
792
|
}
|
|
767
793
|
};
|
|
768
794
|
|
|
769
795
|
// src/driver/BrightDataSerpDriver.ts
|
|
770
796
|
import { chromium as chromium2 } from "playwright";
|
|
797
|
+
|
|
798
|
+
// src/driver/browser-provider-telemetry.ts
|
|
799
|
+
var MAX_PROVIDER_SESSION_ID_LENGTH = 512;
|
|
800
|
+
var MAX_PROVIDER_MESSAGE_LENGTH = 320;
|
|
801
|
+
function boundedText(value, maxLength) {
|
|
802
|
+
if (typeof value !== "string") return null;
|
|
803
|
+
const normalized = value.replace(/[\u0000-\u001f\u007f]+/g, " ").replace(/\s+/g, " ").trim();
|
|
804
|
+
if (!normalized) return null;
|
|
805
|
+
return normalized.slice(0, maxLength);
|
|
806
|
+
}
|
|
807
|
+
function boundedProviderMessage(value) {
|
|
808
|
+
const message = boundedText(value, MAX_PROVIDER_MESSAGE_LENGTH);
|
|
809
|
+
if (!message) return null;
|
|
810
|
+
return message.replace(/\b(?:wss?|https?):\/\/\S+/gi, "[redacted endpoint]").replace(/\b(?:authorization|proxy-authorization):\s*\S+/gi, "$1: [redacted]").replace(/\bBearer\s+\S+/gi, "Bearer [redacted]").slice(0, MAX_PROVIDER_MESSAGE_LENGTH);
|
|
811
|
+
}
|
|
812
|
+
function sanitizeProviderLocalMessage(value) {
|
|
813
|
+
return boundedProviderMessage(value);
|
|
814
|
+
}
|
|
815
|
+
function validateProviderSessionId(value) {
|
|
816
|
+
if (typeof value !== "string" || value.length === 0 || value.length > MAX_PROVIDER_SESSION_ID_LENGTH || value.trim() !== value || /[\s\u0000-\u001f\u007f]/.test(value) || /:\/\//.test(value)) {
|
|
817
|
+
throw new Error("Bright Data did not return a valid browser session ID");
|
|
818
|
+
}
|
|
819
|
+
return value;
|
|
820
|
+
}
|
|
821
|
+
function makeProviderInterruptionObservation(source, message, observedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
822
|
+
const safeMessage = boundedProviderMessage(message);
|
|
823
|
+
return {
|
|
824
|
+
source,
|
|
825
|
+
message: safeMessage ?? source.replaceAll("_", " "),
|
|
826
|
+
observedAt
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
function projectProviderSessionPublic(metadata) {
|
|
830
|
+
return {
|
|
831
|
+
provider: metadata.provider,
|
|
832
|
+
providerDisconnectObserved: metadata.disconnectObservation !== null,
|
|
833
|
+
providerDisconnectMessage: metadata.disconnectObservation?.message ?? null
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
function projectProviderSessionPrivate(metadata) {
|
|
837
|
+
return {
|
|
838
|
+
...projectProviderSessionPublic(metadata),
|
|
839
|
+
providerSessionId: metadata.providerSessionId
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// src/driver/BrightDataSerpDriver.ts
|
|
771
844
|
var BLOCKED_TYPES = /* @__PURE__ */ new Set(["image", "media", "font", "stylesheet", "ping"]);
|
|
772
845
|
var BLOCKED_HOST_PARTS = [
|
|
773
846
|
"doubleclick.net",
|
|
@@ -817,8 +890,19 @@ var BrightDataSerpDriver = class {
|
|
|
817
890
|
browser = null;
|
|
818
891
|
context = null;
|
|
819
892
|
page = null;
|
|
893
|
+
cdpSession = null;
|
|
894
|
+
providerSessionId = null;
|
|
895
|
+
disconnectObservation = null;
|
|
896
|
+
closing = false;
|
|
897
|
+
observeDisconnect(source, message) {
|
|
898
|
+
if (this.closing || this.disconnectObservation) return;
|
|
899
|
+
this.disconnectObservation = makeProviderInterruptionObservation(source, message);
|
|
900
|
+
}
|
|
820
901
|
async launch(_config) {
|
|
821
902
|
if (this.page) return;
|
|
903
|
+
this.closing = false;
|
|
904
|
+
this.providerSessionId = null;
|
|
905
|
+
this.disconnectObservation = null;
|
|
822
906
|
const endpoint = brightDataBrowserEndpoint();
|
|
823
907
|
if (!endpoint) throw new Error("SERP browser service is not configured");
|
|
824
908
|
this.browser = await chromium2.connectOverCDP(endpoint);
|
|
@@ -834,6 +918,14 @@ var BrightDataSerpDriver = class {
|
|
|
834
918
|
}
|
|
835
919
|
});
|
|
836
920
|
this.page = this.context.pages()[0] ?? await this.context.newPage();
|
|
921
|
+
this.browser.on("disconnected", () => this.observeDisconnect("browser_disconnected"));
|
|
922
|
+
this.context.on("close", () => this.observeDisconnect("context_closed"));
|
|
923
|
+
this.page.on("close", () => this.observeDisconnect("page_closed"));
|
|
924
|
+
this.page.on("crash", () => this.observeDisconnect("page_crashed"));
|
|
925
|
+
this.cdpSession = await this.context.newCDPSession(this.page);
|
|
926
|
+
const customCdp = this.cdpSession;
|
|
927
|
+
const sessionResponse = await customCdp.send("Browser.getSessionId");
|
|
928
|
+
this.providerSessionId = validateProviderSessionId(sessionResponse?.sessionId);
|
|
837
929
|
await this.page.route("**/*", async (route) => {
|
|
838
930
|
const request = route.request();
|
|
839
931
|
const resourceType = request.resourceType();
|
|
@@ -889,23 +981,38 @@ var BrightDataSerpDriver = class {
|
|
|
889
981
|
getKernelSessionId() {
|
|
890
982
|
return null;
|
|
891
983
|
}
|
|
984
|
+
getProviderSessionMetadata() {
|
|
985
|
+
return {
|
|
986
|
+
provider: "bright_data_browser_api",
|
|
987
|
+
providerSessionId: this.providerSessionId,
|
|
988
|
+
disconnectObservation: this.disconnectObservation
|
|
989
|
+
};
|
|
990
|
+
}
|
|
892
991
|
getDebugSnapshot() {
|
|
893
992
|
return { kernel: null, context: null, networkLocation: null, serpNavigation: null };
|
|
894
993
|
}
|
|
895
994
|
async close() {
|
|
995
|
+
this.closing = true;
|
|
896
996
|
let error = null;
|
|
897
997
|
try {
|
|
898
998
|
await this.browser?.close();
|
|
899
999
|
} catch (caught) {
|
|
900
|
-
error = caught instanceof Error ? caught.message : String(caught);
|
|
1000
|
+
error = sanitizeProviderLocalMessage(caught instanceof Error ? caught.message : String(caught)) ?? "Bright Data browser close failed";
|
|
1001
|
+
this.disconnectObservation ??= makeProviderInterruptionObservation("browser_close_error", error);
|
|
901
1002
|
}
|
|
1003
|
+
const provider = projectProviderSessionPrivate(this.getProviderSessionMetadata());
|
|
1004
|
+
this.browser = null;
|
|
1005
|
+
this.context = null;
|
|
1006
|
+
this.page = null;
|
|
1007
|
+
this.cdpSession = null;
|
|
902
1008
|
return {
|
|
903
1009
|
kernelSessionId: null,
|
|
904
1010
|
kernelDeleteStarted: false,
|
|
905
1011
|
kernelDeleteSucceeded: null,
|
|
906
1012
|
kernelDeleteError: null,
|
|
907
1013
|
browserCloseSucceeded: error === null,
|
|
908
|
-
browserCloseError: error
|
|
1014
|
+
browserCloseError: error,
|
|
1015
|
+
...provider
|
|
909
1016
|
};
|
|
910
1017
|
}
|
|
911
1018
|
};
|
|
@@ -1801,12 +1908,14 @@ async function extractAISurfacesFromDocument(config) {
|
|
|
1801
1908
|
|
|
1802
1909
|
// src/extractor/PAAExtractor.ts
|
|
1803
1910
|
var PAAExtractor = class {
|
|
1804
|
-
constructor(driver, reporter) {
|
|
1911
|
+
constructor(driver, reporter, onPaaProgress) {
|
|
1805
1912
|
this.driver = driver;
|
|
1806
1913
|
this.reporter = reporter;
|
|
1914
|
+
this.onPaaProgress = onPaaProgress;
|
|
1807
1915
|
}
|
|
1808
1916
|
driver;
|
|
1809
1917
|
reporter;
|
|
1918
|
+
onPaaProgress;
|
|
1810
1919
|
completeness = {
|
|
1811
1920
|
paaWithoutAnswer: 0,
|
|
1812
1921
|
paaWithoutSource: 0,
|
|
@@ -1816,6 +1925,8 @@ var PAAExtractor = class {
|
|
|
1816
1925
|
aioShareUrlEarly = null;
|
|
1817
1926
|
collectedRows = [];
|
|
1818
1927
|
partialContext = null;
|
|
1928
|
+
progressSequence = 0;
|
|
1929
|
+
clickedQuestions = /* @__PURE__ */ new Set();
|
|
1819
1930
|
normalizeQuestion(q) {
|
|
1820
1931
|
return q.toLowerCase().replace(/[^\w\s]/g, "").replace(/\s+/g, " ").trim();
|
|
1821
1932
|
}
|
|
@@ -1852,6 +1963,34 @@ var PAAExtractor = class {
|
|
|
1852
1963
|
function cleanSourceLabel(value) {
|
|
1853
1964
|
return (value ?? "").replace(/\s*\(\+\d+\)\s*-\s*View related links.*$/i, "").replace(/\s*-\s*Opens in new tab.*$/i, "").trim();
|
|
1854
1965
|
}
|
|
1966
|
+
function isGoogleHost(host) {
|
|
1967
|
+
const normalized = host.toLowerCase().replace(/\.$/, "");
|
|
1968
|
+
return normalized === "google.com" || normalized.endsWith(".google.com") || /^google\.[a-z.]+$/.test(normalized) || /\.google\.[a-z.]+$/.test(normalized) || normalized === "googleusercontent.com" || normalized.endsWith(".googleusercontent.com");
|
|
1969
|
+
}
|
|
1970
|
+
function safeCitationTarget(rawHref) {
|
|
1971
|
+
let parsed;
|
|
1972
|
+
try {
|
|
1973
|
+
parsed = new URL(rawHref);
|
|
1974
|
+
} catch {
|
|
1975
|
+
return null;
|
|
1976
|
+
}
|
|
1977
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
1978
|
+
if (parsed.username || parsed.password) return null;
|
|
1979
|
+
if (isGoogleHost(parsed.hostname)) {
|
|
1980
|
+
if (parsed.pathname !== "/url") return null;
|
|
1981
|
+
const redirected = parsed.searchParams.get("q") ?? parsed.searchParams.get("url");
|
|
1982
|
+
if (!redirected) return null;
|
|
1983
|
+
try {
|
|
1984
|
+
parsed = new URL(redirected);
|
|
1985
|
+
} catch {
|
|
1986
|
+
return null;
|
|
1987
|
+
}
|
|
1988
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
1989
|
+
if (parsed.username || parsed.password) return null;
|
|
1990
|
+
}
|
|
1991
|
+
if (isGoogleHost(parsed.hostname)) return null;
|
|
1992
|
+
return { href: parsed.href, host: parsed.hostname };
|
|
1993
|
+
}
|
|
1855
1994
|
return Array.from(document.querySelectorAll(selectors.item)).filter((pair) => !onlyQuestion2 || (pair.getAttribute(selectors.itemDataQ) || pair.getAttribute(selectors.itemDataInitQ) || pair.querySelector(selectors.itemQuestionEl)?.innerText?.trim() || "") === onlyQuestion2).map((pair) => {
|
|
1856
1995
|
const clickTarget = pair.querySelector(selectors.clickTarget);
|
|
1857
1996
|
const expanded = pair.classList.contains(selectors.expandedClass) || clickTarget?.getAttribute("aria-expanded") === "true";
|
|
@@ -1873,21 +2012,14 @@ var PAAExtractor = class {
|
|
|
1873
2012
|
...Array.from(anchorRoot.querySelectorAll("a[href]"))
|
|
1874
2013
|
];
|
|
1875
2014
|
for (const a of prioritized) {
|
|
1876
|
-
const
|
|
1877
|
-
if (
|
|
1878
|
-
|
|
1879
|
-
try {
|
|
1880
|
-
host = new URL(href).hostname;
|
|
1881
|
-
} catch {
|
|
1882
|
-
continue;
|
|
1883
|
-
}
|
|
1884
|
-
if (/(^|\.)google(usercontent)?\.[a-z.]+$/.test(host)) continue;
|
|
1885
|
-
seenHrefs.add(href);
|
|
2015
|
+
const target = safeCitationTarget(a.href);
|
|
2016
|
+
if (!target || seenHrefs.has(target.href)) continue;
|
|
2017
|
+
seenHrefs.add(target.href);
|
|
1886
2018
|
anchors.push({
|
|
1887
2019
|
text: (a.textContent || "").trim(),
|
|
1888
2020
|
label: cleanSourceLabel(a.getAttribute("aria-label")),
|
|
1889
|
-
href,
|
|
1890
|
-
host
|
|
2021
|
+
href: target.href,
|
|
2022
|
+
host: target.host
|
|
1891
2023
|
});
|
|
1892
2024
|
}
|
|
1893
2025
|
const primary = anchors.find((x) => x.label.length > 2 || x.text.length > 2) ?? anchors[0];
|
|
@@ -1944,10 +2076,53 @@ var PAAExtractor = class {
|
|
|
1944
2076
|
extracted_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1945
2077
|
};
|
|
1946
2078
|
}
|
|
2079
|
+
upsertCollectedRow(row) {
|
|
2080
|
+
const key = this.normalizeQuestion(row.question);
|
|
2081
|
+
const index = this.collectedRows.findIndex((existing2) => this.normalizeQuestion(existing2.question) === key);
|
|
2082
|
+
if (index < 0) {
|
|
2083
|
+
this.collectedRows.push({ ...row });
|
|
2084
|
+
return true;
|
|
2085
|
+
}
|
|
2086
|
+
const existing = this.collectedRows[index];
|
|
2087
|
+
const merged = {
|
|
2088
|
+
...existing,
|
|
2089
|
+
...row,
|
|
2090
|
+
answer: row.answer || existing.answer,
|
|
2091
|
+
source_title: row.source_title || existing.source_title,
|
|
2092
|
+
source_site: row.source_site || existing.source_site,
|
|
2093
|
+
source_cite: row.source_cite || existing.source_cite,
|
|
2094
|
+
extracted_at: row.extracted_at || existing.extracted_at
|
|
2095
|
+
};
|
|
2096
|
+
const changed = merged.answer !== existing.answer || merged.source_title !== existing.source_title || merged.source_site !== existing.source_site || merged.source_cite !== existing.source_cite;
|
|
2097
|
+
if (changed) this.collectedRows[index] = merged;
|
|
2098
|
+
return changed;
|
|
2099
|
+
}
|
|
2100
|
+
async emitProgress(phase) {
|
|
2101
|
+
if (!this.onPaaProgress || this.collectedRows.length === 0) return;
|
|
2102
|
+
const snapshot = {
|
|
2103
|
+
sequence: ++this.progressSequence,
|
|
2104
|
+
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2105
|
+
clickedQuestions: [...this.clickedQuestions],
|
|
2106
|
+
phase,
|
|
2107
|
+
records: this.collectedRows.map((row) => ({ ...row }))
|
|
2108
|
+
};
|
|
2109
|
+
try {
|
|
2110
|
+
await this.onPaaProgress(snapshot);
|
|
2111
|
+
} catch (err) {
|
|
2112
|
+
console.warn(JSON.stringify({
|
|
2113
|
+
event: "paa_progress_sink_failed",
|
|
2114
|
+
sequence: snapshot.sequence,
|
|
2115
|
+
phase: snapshot.phase,
|
|
2116
|
+
message: err instanceof Error ? err.message : String(err)
|
|
2117
|
+
}));
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
1947
2120
|
getPartialResult() {
|
|
1948
2121
|
const ctx = this.partialContext;
|
|
1949
2122
|
if (!ctx || this.collectedRows.length === 0) return null;
|
|
1950
|
-
const flat =
|
|
2123
|
+
const flat = this.collectedRows.map((row) => ({ ...row }));
|
|
2124
|
+
const paaWithoutAnswer = ctx.questionsOnly ? 0 : flat.filter((row) => !row.answer?.trim()).length;
|
|
2125
|
+
const paaWithoutSource = ctx.questionsOnly ? 0 : flat.filter((row) => !row.source_cite?.trim()).length;
|
|
1951
2126
|
const stats = {
|
|
1952
2127
|
seed: ctx.seed,
|
|
1953
2128
|
totalQuestions: flat.length,
|
|
@@ -1962,7 +2137,17 @@ var PAAExtractor = class {
|
|
|
1962
2137
|
diagnostics: {
|
|
1963
2138
|
completionStatus: "paa_found",
|
|
1964
2139
|
problem: null,
|
|
1965
|
-
|
|
2140
|
+
resultQuality: "partial",
|
|
2141
|
+
degradedResult: false,
|
|
2142
|
+
retryRecommended: true,
|
|
2143
|
+
completeness: {
|
|
2144
|
+
...this.completeness,
|
|
2145
|
+
paaWithoutAnswer,
|
|
2146
|
+
paaWithoutSource,
|
|
2147
|
+
paaRequested: ctx.maxQuestions,
|
|
2148
|
+
paaReturned: flat.length,
|
|
2149
|
+
paaUnique: flat.length
|
|
2150
|
+
},
|
|
1966
2151
|
warnings: [{
|
|
1967
2152
|
code: "paa_partial",
|
|
1968
2153
|
surface: "paa",
|
|
@@ -1971,6 +2156,9 @@ var PAAExtractor = class {
|
|
|
1971
2156
|
}]
|
|
1972
2157
|
},
|
|
1973
2158
|
totalQuestions: flat.length,
|
|
2159
|
+
surface: "web",
|
|
2160
|
+
aiOverview: { detected: false, text: null, citations: [] },
|
|
2161
|
+
aiMode: { detected: false, text: null, citations: [] },
|
|
1974
2162
|
whatPeopleSaying: [],
|
|
1975
2163
|
tree: this.buildTree(flat, ctx.seed),
|
|
1976
2164
|
flat,
|
|
@@ -1978,7 +2166,7 @@ var PAAExtractor = class {
|
|
|
1978
2166
|
forums: [],
|
|
1979
2167
|
organicResults: [],
|
|
1980
2168
|
localPack: [],
|
|
1981
|
-
entityIds: {},
|
|
2169
|
+
entityIds: { entities: [], kgIds: [], cids: [], gcids: [] },
|
|
1982
2170
|
stats
|
|
1983
2171
|
};
|
|
1984
2172
|
}
|
|
@@ -1988,7 +2176,8 @@ var PAAExtractor = class {
|
|
|
1988
2176
|
const seenQs = /* @__PURE__ */ new Set();
|
|
1989
2177
|
const orderedQs = [];
|
|
1990
2178
|
this.collectedRows = [];
|
|
1991
|
-
|
|
2179
|
+
this.progressSequence = 0;
|
|
2180
|
+
this.clickedQuestions = /* @__PURE__ */ new Set();
|
|
1992
2181
|
const abandonedNeverReclick = /* @__PURE__ */ new Set();
|
|
1993
2182
|
const clickedOnceEver = /* @__PURE__ */ new Set();
|
|
1994
2183
|
const capturedItems = /* @__PURE__ */ new Map();
|
|
@@ -2036,12 +2225,17 @@ var PAAExtractor = class {
|
|
|
2036
2225
|
if (options.softDeadlineMs && Date.now() >= options.softDeadlineMs) break;
|
|
2037
2226
|
this.throwIfAborted(signal);
|
|
2038
2227
|
const states = await readItemStates();
|
|
2228
|
+
let discovered = false;
|
|
2039
2229
|
for (const s of states) {
|
|
2040
2230
|
if (!seenQs.has(s.q)) {
|
|
2041
2231
|
seenQs.add(s.q);
|
|
2042
2232
|
orderedQs.push(s.q);
|
|
2233
|
+
if (orderedQs.length <= options.maxQuestions) {
|
|
2234
|
+
discovered = this.upsertCollectedRow(this.toFlatRow({ question: s.q }, 1, null, options.query)) || discovered;
|
|
2235
|
+
}
|
|
2043
2236
|
}
|
|
2044
2237
|
}
|
|
2238
|
+
if (discovered) await this.emitProgress("discovered");
|
|
2045
2239
|
if (options.questionsOnly && seenQs.size >= options.maxQuestions) break;
|
|
2046
2240
|
const kept = new Set(orderedQs.slice(0, options.maxQuestions));
|
|
2047
2241
|
const clickable = states.filter((s) => kept.has(s.q) && !clickedOnceEver.has(s.q) && !abandonedNeverReclick.has(s.q));
|
|
@@ -2061,6 +2255,7 @@ var PAAExtractor = class {
|
|
|
2061
2255
|
this.reporter.onDepth(++round);
|
|
2062
2256
|
await this.throwIfCaptcha(page, "Google PAA expansion");
|
|
2063
2257
|
clickedOnceEver.add(target.q);
|
|
2258
|
+
this.clickedQuestions.add(target.q);
|
|
2064
2259
|
const expansionStatus = await expandOneItemSerially(target.q);
|
|
2065
2260
|
if (!options.questionsOnly) {
|
|
2066
2261
|
let clickedItem = (await this.extractVisibleItems(page, target.q))[0];
|
|
@@ -2068,7 +2263,12 @@ var PAAExtractor = class {
|
|
|
2068
2263
|
await page.waitForTimeout(1300);
|
|
2069
2264
|
clickedItem = (await this.extractVisibleItems(page, target.q))[0];
|
|
2070
2265
|
}
|
|
2071
|
-
if (clickedItem
|
|
2266
|
+
if (clickedItem) {
|
|
2267
|
+
capturedItems.set(target.q, clickedItem);
|
|
2268
|
+
if (this.upsertCollectedRow(this.toFlatRow(clickedItem, 1, null, options.query))) {
|
|
2269
|
+
await this.emitProgress("answer_captured");
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2072
2272
|
}
|
|
2073
2273
|
if (expansionStatus === "failed" && !capturedItems.has(target.q)) {
|
|
2074
2274
|
abandonedNeverReclick.add(target.q);
|
|
@@ -2084,14 +2284,19 @@ var PAAExtractor = class {
|
|
|
2084
2284
|
if (!seenQs.has(state.q)) {
|
|
2085
2285
|
seenQs.add(state.q);
|
|
2086
2286
|
orderedQs.push(state.q);
|
|
2287
|
+
if (orderedQs.length <= options.maxQuestions) {
|
|
2288
|
+
this.upsertCollectedRow(this.toFlatRow({ question: state.q }, 1, null, options.query));
|
|
2289
|
+
}
|
|
2087
2290
|
}
|
|
2088
2291
|
}
|
|
2292
|
+
await this.emitProgress("discovered");
|
|
2089
2293
|
}
|
|
2090
2294
|
}
|
|
2091
2295
|
const itemMap = options.questionsOnly ? /* @__PURE__ */ new Map() : new Map(capturedItems);
|
|
2092
2296
|
if (!options.questionsOnly) {
|
|
2093
2297
|
await this.fillIncompleteItems(page, orderedQs, itemMap, options, abandonedNeverReclick);
|
|
2094
2298
|
}
|
|
2299
|
+
const results = [];
|
|
2095
2300
|
for (const q of orderedQs) {
|
|
2096
2301
|
if (results.length >= options.maxQuestions) break;
|
|
2097
2302
|
const key = this.normalizeQuestion(q);
|
|
@@ -2099,14 +2304,22 @@ var PAAExtractor = class {
|
|
|
2099
2304
|
seenKeys.add(key);
|
|
2100
2305
|
const item = itemMap.get(q);
|
|
2101
2306
|
if (item) {
|
|
2102
|
-
|
|
2307
|
+
const row = this.toFlatRow(item, 1, null, options.query);
|
|
2308
|
+
this.upsertCollectedRow(row);
|
|
2309
|
+
results.push(row);
|
|
2103
2310
|
this.reporter.onQuestion({ question: item.question, answer: item.answer ?? null, sourceTitle: item.sourceTitle ?? null, sourceSite: item.sourceSite ?? null, sourceCite: item.sourceCite ?? null, depth: 1, parentQuestion: null, children: [] });
|
|
2104
2311
|
} else {
|
|
2105
|
-
|
|
2312
|
+
const row = this.toFlatRow({ question: q, answer: void 0, sourceTitle: void 0, sourceSite: void 0, sourceCite: void 0 }, 1, null, options.query);
|
|
2313
|
+
this.upsertCollectedRow(row);
|
|
2314
|
+
results.push(row);
|
|
2106
2315
|
}
|
|
2107
2316
|
}
|
|
2108
2317
|
this.completeness.paaWithoutAnswer = options.questionsOnly ? 0 : results.filter((r) => !r.answer).length;
|
|
2109
|
-
this.completeness.paaWithoutSource = options.questionsOnly ? 0 : results.filter((r) => !r.
|
|
2318
|
+
this.completeness.paaWithoutSource = options.questionsOnly ? 0 : results.filter((r) => !r.source_cite).length;
|
|
2319
|
+
this.completeness.paaRequested = options.maxQuestions;
|
|
2320
|
+
this.completeness.paaReturned = results.length;
|
|
2321
|
+
this.completeness.paaUnique = seenKeys.size;
|
|
2322
|
+
await this.emitProgress("fill_incomplete");
|
|
2110
2323
|
return results;
|
|
2111
2324
|
}
|
|
2112
2325
|
async fillIncompleteItems(page, orderedQs, itemMap, options, abandonedNeverReclick) {
|
|
@@ -2131,6 +2344,10 @@ var PAAExtractor = class {
|
|
|
2131
2344
|
sourceSite: item.sourceSite ?? existing?.sourceSite,
|
|
2132
2345
|
sourceCite: item.sourceCite ?? existing?.sourceCite
|
|
2133
2346
|
});
|
|
2347
|
+
const upgraded = itemMap.get(q);
|
|
2348
|
+
if (upgraded && this.upsertCollectedRow(this.toFlatRow(upgraded, 1, null, options.query))) {
|
|
2349
|
+
await this.emitProgress("fill_incomplete");
|
|
2350
|
+
}
|
|
2134
2351
|
}
|
|
2135
2352
|
this.completeness.paaAnswersRecovered = [...missingAnswersBefore].filter((q) => itemMap.get(q)?.answer).length;
|
|
2136
2353
|
}
|
|
@@ -2595,7 +2812,15 @@ var PAAExtractor = class {
|
|
|
2595
2812
|
this.completeness = { paaWithoutAnswer: 0, paaWithoutSource: 0, paaAnswersRecovered: 0, aioShareCaptured: null };
|
|
2596
2813
|
this.aioShareUrlEarly = null;
|
|
2597
2814
|
this.collectedRows = [];
|
|
2598
|
-
this.
|
|
2815
|
+
this.progressSequence = 0;
|
|
2816
|
+
this.clickedQuestions = /* @__PURE__ */ new Set();
|
|
2817
|
+
this.partialContext = {
|
|
2818
|
+
seed: options.query,
|
|
2819
|
+
location: options.location ?? null,
|
|
2820
|
+
startMs,
|
|
2821
|
+
maxQuestions: options.maxQuestions,
|
|
2822
|
+
questionsOnly: options.questionsOnly
|
|
2823
|
+
};
|
|
2599
2824
|
const isMobile = options.device === "mobile";
|
|
2600
2825
|
const config = {
|
|
2601
2826
|
headless: options.headless,
|
|
@@ -2667,6 +2892,7 @@ var PAAExtractor = class {
|
|
|
2667
2892
|
extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2668
2893
|
diagnostics: {
|
|
2669
2894
|
completionStatus: hasPaa ? "paa_found" : "no_paa",
|
|
2895
|
+
...!hasPaa ? { noPaaObserved: true } : {},
|
|
2670
2896
|
problem: null,
|
|
2671
2897
|
completeness: { ...this.completeness },
|
|
2672
2898
|
...options.debug ? { debug: this.buildHarvestDebugSnapshot(executionOptions, canonicalLocation, uule, void 0, locationResolution) } : {}
|
|
@@ -2786,6 +3012,7 @@ var PAAExtractor = class {
|
|
|
2786
3012
|
extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2787
3013
|
diagnostics: {
|
|
2788
3014
|
completionStatus: "no_paa",
|
|
3015
|
+
noPaaObserved: true,
|
|
2789
3016
|
problem: null,
|
|
2790
3017
|
completeness: { ...this.completeness },
|
|
2791
3018
|
...options.debug ? { debug: this.buildHarvestDebugSnapshot(executionOptions, canonicalLocation, uule, locationEvidence2, locationResolution) } : {}
|
|
@@ -2877,6 +3104,8 @@ var PAAExtractor = class {
|
|
|
2877
3104
|
};
|
|
2878
3105
|
} catch (err) {
|
|
2879
3106
|
errorCount++;
|
|
3107
|
+
await this.emitProgress("attempt_ending").catch(() => {
|
|
3108
|
+
});
|
|
2880
3109
|
this.reporter.onError(err instanceof Error ? err : new Error(String(err)));
|
|
2881
3110
|
throw err;
|
|
2882
3111
|
}
|
|
@@ -3560,6 +3789,11 @@ function getAttemptLogSink(rawOptions) {
|
|
|
3560
3789
|
const sink = rawOptions.onAttemptEvent;
|
|
3561
3790
|
return typeof sink === "function" ? sink : void 0;
|
|
3562
3791
|
}
|
|
3792
|
+
function getPaaProgressSink(rawOptions) {
|
|
3793
|
+
if (!rawOptions || typeof rawOptions !== "object") return void 0;
|
|
3794
|
+
const sink = rawOptions.onPaaProgress;
|
|
3795
|
+
return typeof sink === "function" ? sink : void 0;
|
|
3796
|
+
}
|
|
3563
3797
|
async function emitAttemptEvent(sink, event) {
|
|
3564
3798
|
if (!sink) return;
|
|
3565
3799
|
try {
|
|
@@ -3592,17 +3826,32 @@ function classifyAttemptResult(result) {
|
|
|
3592
3826
|
function resultHasUsableContent(result) {
|
|
3593
3827
|
return result.flat.length > 0 || result.organicResults.length > 0 || result.localPack.length > 0 || result.videos.length > 0 || result.forums.length > 0 || result.whatPeopleSaying.length > 0 || result.aiOverview.detected || result.aiMode.detected;
|
|
3594
3828
|
}
|
|
3595
|
-
function withResultQuality(result) {
|
|
3829
|
+
function withResultQuality(result, requestedPaaCount, questionsOnly) {
|
|
3596
3830
|
const hasUsableContent = resultHasUsableContent(result);
|
|
3597
3831
|
const hasWarnings = (result.diagnostics.warnings?.length ?? 0) > 0;
|
|
3598
|
-
const
|
|
3832
|
+
const uniquePaaCount = new Set(result.flat.map((row) => row.question.toLowerCase().replace(/[^\w\s]/g, "").replace(/\s+/g, " ").trim())).size;
|
|
3833
|
+
const paaWithoutAnswer = questionsOnly ? 0 : result.flat.filter((row) => !row.answer?.trim()).length;
|
|
3834
|
+
const paaWithoutSource = questionsOnly ? 0 : result.flat.filter((row) => !row.source_cite?.trim()).length;
|
|
3835
|
+
const noPaa = result.diagnostics.completionStatus === "no_paa" && (result.diagnostics.noPaaObserved === true || hasUsableContent);
|
|
3836
|
+
const hasPaa = result.diagnostics.completionStatus === "paa_found";
|
|
3837
|
+
const paaIncomplete = hasPaa && (uniquePaaCount < requestedPaaCount || paaWithoutAnswer > 0 || paaWithoutSource > 0 || Boolean(result.diagnostics.interruption));
|
|
3838
|
+
const resultQuality = noPaa ? "complete" : hasPaa && uniquePaaCount > 0 ? paaIncomplete || hasWarnings ? "partial" : "complete" : hasUsableContent ? hasWarnings ? "partial" : "complete" : "degraded";
|
|
3599
3839
|
const degradedResult = resultQuality === "degraded";
|
|
3600
3840
|
result.diagnostics = {
|
|
3601
3841
|
...result.diagnostics,
|
|
3602
3842
|
resultQuality,
|
|
3603
3843
|
degradedResult,
|
|
3604
3844
|
degradationReasons: degradedResult ? ["empty_primary_serp"] : [],
|
|
3605
|
-
retryRecommended: degradedResult
|
|
3845
|
+
retryRecommended: degradedResult || resultQuality === "partial" && (paaIncomplete || result.diagnostics.warnings?.some((warning) => warning.retryable) === true),
|
|
3846
|
+
completeness: {
|
|
3847
|
+
paaWithoutAnswer,
|
|
3848
|
+
paaWithoutSource,
|
|
3849
|
+
paaAnswersRecovered: result.diagnostics.completeness?.paaAnswersRecovered ?? 0,
|
|
3850
|
+
aioShareCaptured: result.diagnostics.completeness?.aioShareCaptured ?? null,
|
|
3851
|
+
paaRequested: requestedPaaCount,
|
|
3852
|
+
paaReturned: result.flat.length,
|
|
3853
|
+
paaUnique: uniquePaaCount
|
|
3854
|
+
}
|
|
3606
3855
|
};
|
|
3607
3856
|
return result;
|
|
3608
3857
|
}
|
|
@@ -3662,10 +3911,10 @@ async function cleanupDisposableProxy(kernelApiKey, proxyId) {
|
|
|
3662
3911
|
}));
|
|
3663
3912
|
}
|
|
3664
3913
|
}
|
|
3665
|
-
async function extractOnce(options, signal, forceManagedSerp = false) {
|
|
3914
|
+
async function extractOnce(options, signal, forceManagedSerp = false, onPaaProgress) {
|
|
3666
3915
|
const driver = createSerpDriver(options, { forceManaged: forceManagedSerp });
|
|
3667
3916
|
const reporter = new ProgressReporter();
|
|
3668
|
-
const extractor = new PAAExtractor(driver, reporter);
|
|
3917
|
+
const extractor = new PAAExtractor(driver, reporter, onPaaProgress);
|
|
3669
3918
|
if (signal?.aborted) {
|
|
3670
3919
|
return {
|
|
3671
3920
|
result: null,
|
|
@@ -3717,17 +3966,25 @@ async function extractOnce(options, signal, forceManagedSerp = false) {
|
|
|
3717
3966
|
}
|
|
3718
3967
|
if (error) {
|
|
3719
3968
|
const outcome = classifyAttemptError(error);
|
|
3720
|
-
const
|
|
3969
|
+
const interruptionCode = outcome === "timeout" || outcome === "request_aborted" || outcome === "browser_session_interrupted" ? outcome : null;
|
|
3721
3970
|
let salvaged = null;
|
|
3722
|
-
if (
|
|
3971
|
+
if (interruptionCode) {
|
|
3723
3972
|
try {
|
|
3724
3973
|
salvaged = typeof extractor.getPartialResult === "function" ? extractor.getPartialResult() : null;
|
|
3725
3974
|
} catch {
|
|
3726
3975
|
salvaged = null;
|
|
3727
3976
|
}
|
|
3728
3977
|
}
|
|
3729
|
-
if (salvaged) {
|
|
3730
|
-
|
|
3978
|
+
if (salvaged && interruptionCode) {
|
|
3979
|
+
salvaged.diagnostics = {
|
|
3980
|
+
...salvaged.diagnostics,
|
|
3981
|
+
interruption: {
|
|
3982
|
+
code: interruptionCode,
|
|
3983
|
+
reasonSource: "client_runtime",
|
|
3984
|
+
message: errorMessage(error)
|
|
3985
|
+
}
|
|
3986
|
+
};
|
|
3987
|
+
return { result: salvaged, error, cleanup, debug, salvagedFrom: error };
|
|
3731
3988
|
}
|
|
3732
3989
|
return { result: null, error, cleanup, debug };
|
|
3733
3990
|
}
|
|
@@ -3737,6 +3994,7 @@ async function harvest(rawOptions) {
|
|
|
3737
3994
|
const raw = typeof rawOptions === "object" && rawOptions !== null ? rawOptions : {};
|
|
3738
3995
|
const signal = getAbortSignal(rawOptions);
|
|
3739
3996
|
const onAttemptEvent = getAttemptLogSink(rawOptions);
|
|
3997
|
+
const onPaaProgress = getPaaProgressSink(rawOptions);
|
|
3740
3998
|
const forceManagedSerp = raw.forceManagedSerp === true;
|
|
3741
3999
|
const requestedProxyMode = raw.proxyMode;
|
|
3742
4000
|
const proxyMode = requestedProxyMode === "location" || requestedProxyMode === "none" || requestedProxyMode === "configured" ? requestedProxyMode : DEFAULT_PROXY_MODE;
|
|
@@ -3806,7 +4064,7 @@ async function harvest(rawOptions) {
|
|
|
3806
4064
|
...baseCtx,
|
|
3807
4065
|
forceHeadless: allowHeadlessTest,
|
|
3808
4066
|
forceHeadful: !allowHeadlessTest
|
|
3809
|
-
}, () => extractOnce(attemptOptions, signal, forceManagedSerp)) : await extractOnce(attemptOptions, signal, forceManagedSerp);
|
|
4067
|
+
}, () => extractOnce(attemptOptions, signal, forceManagedSerp, onPaaProgress)) : await extractOnce(attemptOptions, signal, forceManagedSerp, onPaaProgress);
|
|
3810
4068
|
if (attempt.error) {
|
|
3811
4069
|
const err = attempt.error;
|
|
3812
4070
|
const outcome2 = classifyAttemptError(err);
|
|
@@ -3839,7 +4097,7 @@ async function harvest(rawOptions) {
|
|
|
3839
4097
|
maxAttempts,
|
|
3840
4098
|
outcome: outcome2,
|
|
3841
4099
|
kernelSessionId: attempt.cleanup.kernelSessionId,
|
|
3842
|
-
questionCount: 0,
|
|
4100
|
+
questionCount: attempt.result?.totalQuestions ?? 0,
|
|
3843
4101
|
durationMs: Date.now() - startedAtMs,
|
|
3844
4102
|
error: errorMessage(err),
|
|
3845
4103
|
willRetry: willRetry2,
|
|
@@ -3850,6 +4108,9 @@ async function harvest(rawOptions) {
|
|
|
3850
4108
|
await cleanupDisposableProxy(kernelApiKey, resolution2.disposableProxyId);
|
|
3851
4109
|
lastError = err;
|
|
3852
4110
|
if (willRetry2) continue;
|
|
4111
|
+
if (attempt.result) {
|
|
4112
|
+
return withResultQuality(stripInternalDebug(attempt.result, requestedDebug), attemptOptions.maxQuestions, attemptOptions.questionsOnly);
|
|
4113
|
+
}
|
|
3853
4114
|
break;
|
|
3854
4115
|
}
|
|
3855
4116
|
const result = attempt.result;
|
|
@@ -3883,7 +4144,7 @@ async function harvest(rawOptions) {
|
|
|
3883
4144
|
if (willRetry2) continue;
|
|
3884
4145
|
break;
|
|
3885
4146
|
}
|
|
3886
|
-
const finalResult = withResultQuality(stripInternalDebug(result, requestedDebug));
|
|
4147
|
+
const finalResult = withResultQuality(stripInternalDebug(result, requestedDebug), attemptOptions.maxQuestions, attemptOptions.questionsOnly);
|
|
3887
4148
|
const outcome = classifyAttemptResult(finalResult);
|
|
3888
4149
|
const willRetry = outcome === "degraded_result" && i < maxAttempts - 1;
|
|
3889
4150
|
const resultError = outcome === "degraded_result" ? degradedResultMessage(finalResult) : null;
|
|
@@ -3995,6 +4256,7 @@ export {
|
|
|
3995
4256
|
serpArrivalUrl,
|
|
3996
4257
|
buildYouTubeChannelVideosUrl,
|
|
3997
4258
|
BrowserDriver,
|
|
4259
|
+
validateProviderSessionId,
|
|
3998
4260
|
brightDataSerpEnabled,
|
|
3999
4261
|
BACKUP_LAST_RESORT_ATTEMPTS,
|
|
4000
4262
|
backupProxyAvailable,
|