seo 0.2.20 → 0.2.21
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/README.md +24 -4
- package/dist/cli.js +115 -115
- package/dist/cli.js.map +1 -1
- package/dist/{dist-CtDv7OFQ.js → dist-B-zhNcyH.js} +107 -107
- package/dist/dist-B-zhNcyH.js.map +1 -0
- package/dist/{dist-DXAAiZSX.js → dist-BITHJak_.js} +1 -1
- package/dist/{esm-DvJj2-vq.js → esm-CSE-hgHf.js} +2 -2
- package/dist/{esm-DvJj2-vq.js.map → esm-CSE-hgHf.js.map} +1 -1
- package/dist/index.d.ts +558 -99
- package/dist/index.js +1377 -229
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +1435 -251
- package/dist/mcp.js.map +1 -1
- package/evals/link-evidence.json +13 -0
- package/package.json +1 -1
- package/skills/seo/SKILL.md +4 -3
- package/dist/dist-CtDv7OFQ.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire as __seoCreateRequire } from 'node:module'; const require = __seoCreateRequire(import.meta.url);
|
|
2
|
-
import { n as version, t as load } from "./esm-
|
|
2
|
+
import { n as version, t as load } from "./esm-CSE-hgHf.js";
|
|
3
3
|
import { Headers, fetch as fetch$1 } from "undici";
|
|
4
4
|
import { chmodSync, createReadStream, createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { arch, homedir, platform, totalmem } from "node:os";
|
|
@@ -2252,7 +2252,7 @@ function aiReferralSourceForValue(value) {
|
|
|
2252
2252
|
function compareText$18(left, right) {
|
|
2253
2253
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
2254
2254
|
}
|
|
2255
|
-
function metric(row, name) {
|
|
2255
|
+
function metric$1(row, name) {
|
|
2256
2256
|
const value = row[name];
|
|
2257
2257
|
const parsed = Number(value);
|
|
2258
2258
|
if (value === void 0 || value.trim() === "" || !Number.isInteger(parsed) || parsed < 0) throw new SeoError("PROVIDER_UNAVAILABLE", `Google Analytics returned an invalid ${name} value.`);
|
|
@@ -2287,8 +2287,8 @@ function analyzeAiReferralRows(input) {
|
|
|
2287
2287
|
...emptyMetrics()
|
|
2288
2288
|
};
|
|
2289
2289
|
addMetrics(total, {
|
|
2290
|
-
sessions: metric(row, "sessions"),
|
|
2291
|
-
eventCount: metric(row, "eventCount")
|
|
2290
|
+
sessions: metric$1(row, "sessions"),
|
|
2291
|
+
eventCount: metric$1(row, "eventCount")
|
|
2292
2292
|
});
|
|
2293
2293
|
total.observedSessionSources.add(row.sessionSource ?? "");
|
|
2294
2294
|
sourceTotals.set(definition.id, total);
|
|
@@ -2300,8 +2300,8 @@ function analyzeAiReferralRows(input) {
|
|
|
2300
2300
|
const definition = aiReferralSourceForValue(row.sessionSource ?? "");
|
|
2301
2301
|
if (!definition || !sourceTotals.has(definition.id)) throw new SeoError("PROVIDER_UNAVAILABLE", "Google Analytics detail rows contained an unexpected session source.");
|
|
2302
2302
|
const metrics = {
|
|
2303
|
-
sessions: metric(row, "sessions"),
|
|
2304
|
-
eventCount: metric(row, "eventCount")
|
|
2303
|
+
sessions: metric$1(row, "sessions"),
|
|
2304
|
+
eventCount: metric$1(row, "eventCount")
|
|
2305
2305
|
};
|
|
2306
2306
|
addMetrics(detailTotal, metrics);
|
|
2307
2307
|
const landingPage = row.landingPagePlusQueryString || "(not set)";
|
|
@@ -5775,7 +5775,7 @@ const AUDIT_PAGE_RULE_IDS = [
|
|
|
5775
5775
|
"canonical_outside_head",
|
|
5776
5776
|
"canonical_mismatch"
|
|
5777
5777
|
];
|
|
5778
|
-
const defaultDependencies$
|
|
5778
|
+
const defaultDependencies$17 = {
|
|
5779
5779
|
fetchPage,
|
|
5780
5780
|
queryPageMetrics,
|
|
5781
5781
|
now: () => /* @__PURE__ */ new Date()
|
|
@@ -5806,7 +5806,7 @@ function buildRecommendations(report) {
|
|
|
5806
5806
|
});
|
|
5807
5807
|
return recommendations;
|
|
5808
5808
|
}
|
|
5809
|
-
async function auditPage(input, dependencies = defaultDependencies$
|
|
5809
|
+
async function auditPage(input, dependencies = defaultDependencies$17) {
|
|
5810
5810
|
const fetched = await dependencies.fetchPage(input.url, {
|
|
5811
5811
|
js: input.js,
|
|
5812
5812
|
refresh: input.refresh
|
|
@@ -7489,7 +7489,7 @@ function pageOpportunityRecommendations(analysis) {
|
|
|
7489
7489
|
//#endregion
|
|
7490
7490
|
//#region packages/core/src/analyze/page-opportunities.ts
|
|
7491
7491
|
const MAX_BENCHMARK_ROWS = 1e5;
|
|
7492
|
-
const defaultDependencies$
|
|
7492
|
+
const defaultDependencies$16 = {
|
|
7493
7493
|
searchAnalytics: querySearchAnalytics,
|
|
7494
7494
|
fetch: fetchPage,
|
|
7495
7495
|
extract: extractPage,
|
|
@@ -7561,7 +7561,7 @@ function reportCaveats$1(input) {
|
|
|
7561
7561
|
input.benchmarkTruncated ? `The site-wide benchmark reached its ${MAX_BENCHMARK_ROWS.toLocaleString("en-US")}-row cap, so provenance is complete but the peer sample is truncated.` : ""
|
|
7562
7562
|
].filter(Boolean);
|
|
7563
7563
|
}
|
|
7564
|
-
async function pageOpportunitiesReport(input, dependencies = defaultDependencies$
|
|
7564
|
+
async function pageOpportunitiesReport(input, dependencies = defaultDependencies$16) {
|
|
7565
7565
|
const url = assertUrlMatchesGscProperty(input.site, input.url);
|
|
7566
7566
|
const days = reportDays$1(input.days);
|
|
7567
7567
|
const range = defaultDateRange$1(days);
|
|
@@ -11358,7 +11358,7 @@ function absoluteHttpUrl(value) {
|
|
|
11358
11358
|
return false;
|
|
11359
11359
|
}
|
|
11360
11360
|
}
|
|
11361
|
-
function urlKey(value) {
|
|
11361
|
+
function urlKey$1(value) {
|
|
11362
11362
|
if (!value) return void 0;
|
|
11363
11363
|
try {
|
|
11364
11364
|
const url = new URL(value);
|
|
@@ -11557,7 +11557,7 @@ function auditCrawlPages(pages, opts = {}) {
|
|
|
11557
11557
|
for (const page of pages) {
|
|
11558
11558
|
if (!isAuditableHtmlPage(page)) continue;
|
|
11559
11559
|
for (const value of [page.url, page.finalUrl]) {
|
|
11560
|
-
const key = urlKey(value);
|
|
11560
|
+
const key = urlKey$1(value);
|
|
11561
11561
|
if (key) pageByUrl.set(key, page);
|
|
11562
11562
|
}
|
|
11563
11563
|
}
|
|
@@ -11699,7 +11699,7 @@ function auditCrawlPages(pages, opts = {}) {
|
|
|
11699
11699
|
canonicalRaw: page.canonicalRaw,
|
|
11700
11700
|
canonical: page.canonical
|
|
11701
11701
|
}));
|
|
11702
|
-
const canonicalTarget = pageByUrl.get(urlKey(page.canonical) ?? "");
|
|
11702
|
+
const canonicalTarget = pageByUrl.get(urlKey$1(page.canonical) ?? "");
|
|
11703
11703
|
if (canonicalTarget?.canonical && !sameUrl(canonicalTarget.canonical, page.canonical)) issues.push(issue("canonical_chain", page, page.canonical, {
|
|
11704
11704
|
canonical: page.canonical,
|
|
11705
11705
|
nextCanonical: canonicalTarget.canonical,
|
|
@@ -11874,7 +11874,7 @@ const SENSITIVE_FIELD_NAMES = /* @__PURE__ */ new Set([
|
|
|
11874
11874
|
"refresh-token"
|
|
11875
11875
|
]);
|
|
11876
11876
|
const SENSITIVE_PARAM_PATTERN = /^(access_?token|auth|api_?key|client_?secret|code|jwt|key|password|refresh_?token|secret|session|sig|signature|token)$/i;
|
|
11877
|
-
function uniqueSorted(values = []) {
|
|
11877
|
+
function uniqueSorted$1(values = []) {
|
|
11878
11878
|
return [...new Set(values.filter(Boolean))].sort();
|
|
11879
11879
|
}
|
|
11880
11880
|
function normalizeFetchRate(input, strategy) {
|
|
@@ -11894,13 +11894,13 @@ function normalizeCrawlConfig(input) {
|
|
|
11894
11894
|
mode: input.mode ?? (strategy === "health" ? "sitemap" : "site"),
|
|
11895
11895
|
strategy,
|
|
11896
11896
|
...input.sitemapUrl ? { sitemapUrl: new URL(input.sitemapUrl).toString() } : {},
|
|
11897
|
-
urls: uniqueSorted(input.urls ?? []),
|
|
11897
|
+
urls: uniqueSorted$1(input.urls ?? []),
|
|
11898
11898
|
maxPages: input.maxPages ?? 500,
|
|
11899
11899
|
maxDepth: input.maxDepth ?? 16,
|
|
11900
11900
|
concurrency,
|
|
11901
11901
|
timeoutMs: input.timeoutMs ?? 2e4,
|
|
11902
|
-
include: uniqueSorted(input.include ?? []),
|
|
11903
|
-
exclude: uniqueSorted(input.exclude ?? []),
|
|
11902
|
+
include: uniqueSorted$1(input.include ?? []),
|
|
11903
|
+
exclude: uniqueSorted$1(input.exclude ?? []),
|
|
11904
11904
|
respectRobots: input.respectRobots ?? true,
|
|
11905
11905
|
useSitemap: input.useSitemap ?? true,
|
|
11906
11906
|
checkExternal: strategy === "health" ? false : input.checkExternal ?? true,
|
|
@@ -14154,7 +14154,7 @@ async function fetchSitemapUrls(input) {
|
|
|
14154
14154
|
}
|
|
14155
14155
|
//#endregion
|
|
14156
14156
|
//#region packages/core/src/analyze/workflows/analytics-value.ts
|
|
14157
|
-
const defaultDependencies$
|
|
14157
|
+
const defaultDependencies$15 = { runGa4Report };
|
|
14158
14158
|
function normalizePath(value) {
|
|
14159
14159
|
if (!value || value === "(not set)") return "";
|
|
14160
14160
|
const [path = ""] = value.split("?");
|
|
@@ -14214,7 +14214,7 @@ function landingPageRankingPolicy(input) {
|
|
|
14214
14214
|
warnings
|
|
14215
14215
|
};
|
|
14216
14216
|
}
|
|
14217
|
-
async function fetchLandingPageValues(input, dependencies = defaultDependencies$
|
|
14217
|
+
async function fetchLandingPageValues(input, dependencies = defaultDependencies$15) {
|
|
14218
14218
|
const retainedRowLimit = input.limit ?? 5e3;
|
|
14219
14219
|
if (!input.propertyId) return {
|
|
14220
14220
|
values: /* @__PURE__ */ new Map(),
|
|
@@ -14984,7 +14984,7 @@ async function crawlSite(input, dependencies) {
|
|
|
14984
14984
|
const REPORT_BASELINE_MAX_PAGES = 100;
|
|
14985
14985
|
const REPORT_BASELINE_MAX_DEPTH = 4;
|
|
14986
14986
|
const REPORT_BASELINE_MAX_AGE_MS = 10080 * 60 * 1e3;
|
|
14987
|
-
const defaultDependencies$
|
|
14987
|
+
const defaultDependencies$14 = {
|
|
14988
14988
|
latestCrawlReport,
|
|
14989
14989
|
crawlSite,
|
|
14990
14990
|
saveCrawlReport: (report) => saveCrawlReport(report, crawlReportStore, { retention: "baseline" }),
|
|
@@ -15033,7 +15033,7 @@ function crawlInput$1(input) {
|
|
|
15033
15033
|
refresh: input.refresh ?? false
|
|
15034
15034
|
};
|
|
15035
15035
|
}
|
|
15036
|
-
async function resolveTechnicalBaseline(input, dependencies = defaultDependencies$
|
|
15036
|
+
async function resolveTechnicalBaseline(input, dependencies = defaultDependencies$14) {
|
|
15037
15037
|
if (input.crawl === false) return {
|
|
15038
15038
|
status: "skipped",
|
|
15039
15039
|
reason: "Technical crawl evidence was skipped with --no-crawl."
|
|
@@ -18614,15 +18614,15 @@ function analyzeCtrUnderperformersFromRows(input) {
|
|
|
18614
18614
|
}
|
|
18615
18615
|
//#endregion
|
|
18616
18616
|
//#region packages/core/src/analyze/ctr-underperformers.ts
|
|
18617
|
-
const MAX_GSC_ROWS$
|
|
18617
|
+
const MAX_GSC_ROWS$5 = 1e5;
|
|
18618
18618
|
function plural$4(count, singular, pluralLabel = `${singular}s`) {
|
|
18619
18619
|
return count === 1 ? singular : pluralLabel;
|
|
18620
18620
|
}
|
|
18621
|
-
const defaultDependencies$
|
|
18621
|
+
const defaultDependencies$13 = {
|
|
18622
18622
|
searchAnalytics: querySearchAnalytics,
|
|
18623
18623
|
now: () => /* @__PURE__ */ new Date()
|
|
18624
18624
|
};
|
|
18625
|
-
async function ctrUnderperformersReport(input, dependencies = defaultDependencies$
|
|
18625
|
+
async function ctrUnderperformersReport(input, dependencies = defaultDependencies$13) {
|
|
18626
18626
|
const minImpressions = integerOption$2({
|
|
18627
18627
|
value: input.minImpressions,
|
|
18628
18628
|
fallback: 200,
|
|
@@ -18644,7 +18644,7 @@ async function ctrUnderperformersReport(input, dependencies = defaultDependencie
|
|
|
18644
18644
|
dimensions: ["query", "page"],
|
|
18645
18645
|
type: "web",
|
|
18646
18646
|
dataState: "final",
|
|
18647
|
-
maxRows: MAX_GSC_ROWS$
|
|
18647
|
+
maxRows: MAX_GSC_ROWS$5
|
|
18648
18648
|
}, { refresh: input.refresh });
|
|
18649
18649
|
const { items, totalClickShortfall, returnedClickShortfall, selection } = analyzeCtrUnderperformersFromRows({
|
|
18650
18650
|
rows: source.rows,
|
|
@@ -18655,7 +18655,7 @@ async function ctrUnderperformersReport(input, dependencies = defaultDependencie
|
|
|
18655
18655
|
includeBrand: input.includeBrand
|
|
18656
18656
|
});
|
|
18657
18657
|
const top = items[0];
|
|
18658
|
-
const possiblyTruncated = source.rowsFetched >= MAX_GSC_ROWS$
|
|
18658
|
+
const possiblyTruncated = source.rowsFetched >= MAX_GSC_ROWS$5;
|
|
18659
18659
|
const partialValidation = selection.invalidRows > 0;
|
|
18660
18660
|
const dataStatus = selection.sourceRows === 0 ? "empty" : possiblyTruncated || partialValidation ? "partial" : selection.eligibleUnderperformers === 0 ? "filtered" : "complete";
|
|
18661
18661
|
const completeness = possiblyTruncated && partialValidation ? "partial-and-possibly-truncated" : possiblyTruncated ? "possibly-truncated" : partialValidation ? "partial" : "complete";
|
|
@@ -18672,7 +18672,7 @@ async function ctrUnderperformersReport(input, dependencies = defaultDependencie
|
|
|
18672
18672
|
dataState: "final",
|
|
18673
18673
|
rowsFetched: source.rowsFetched,
|
|
18674
18674
|
calls: source.calls,
|
|
18675
|
-
maxRows: MAX_GSC_ROWS$
|
|
18675
|
+
maxRows: MAX_GSC_ROWS$5,
|
|
18676
18676
|
possiblyTruncated,
|
|
18677
18677
|
completeness,
|
|
18678
18678
|
validation: {
|
|
@@ -18702,7 +18702,7 @@ async function ctrUnderperformersReport(input, dependencies = defaultDependencie
|
|
|
18702
18702
|
"CTR benchmarks and calculated click shortfalls are directional heuristics, not forecasts or proof that clicks are available. Validate the search intent and live SERP before editing.",
|
|
18703
18703
|
...selection.invalidRows ? [`${selection.invalidRows} invalid provider ${plural$4(selection.invalidRows, "row")} ${selection.invalidRows === 1 ? "was" : "were"} excluded, so this report is partial.`] : [],
|
|
18704
18704
|
...selection.duplicateRows ? [`${selection.duplicateRows} repeated query/page ${plural$4(selection.duplicateRows, "row")} ${selection.duplicateRows === 1 ? "was" : "were"} aggregated before CTR and average position were calculated.`] : [],
|
|
18705
|
-
...possiblyTruncated ? [`The retained-row limit of ${MAX_GSC_ROWS$
|
|
18705
|
+
...possiblyTruncated ? [`The retained-row limit of ${MAX_GSC_ROWS$5} was reached, so lower-ranked source rows may be absent.`] : [],
|
|
18706
18706
|
...selection.limitedUnderperformers ? [`${selection.limitedUnderperformers} eligible ${plural$4(selection.limitedUnderperformers, "underperformer")} ${selection.limitedUnderperformers === 1 ? "was" : "were"} omitted by the output limit.`] : []
|
|
18707
18707
|
],
|
|
18708
18708
|
recommendations: top ? [
|
|
@@ -19226,7 +19226,7 @@ function unavailableSegmentImpactReport(input) {
|
|
|
19226
19226
|
//#endregion
|
|
19227
19227
|
//#region packages/core/src/analyze/segment-impact.ts
|
|
19228
19228
|
const DEFAULT_MAX_ROWS = 1e5;
|
|
19229
|
-
const defaultDependencies$
|
|
19229
|
+
const defaultDependencies$12 = {
|
|
19230
19230
|
searchAnalytics: querySearchAnalytics,
|
|
19231
19231
|
now: () => /* @__PURE__ */ new Date()
|
|
19232
19232
|
};
|
|
@@ -19266,7 +19266,7 @@ function compareSegmentRows(input) {
|
|
|
19266
19266
|
generatedAt: input.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
19267
19267
|
});
|
|
19268
19268
|
}
|
|
19269
|
-
async function segmentImpact(input, dependencies = defaultDependencies$
|
|
19269
|
+
async function segmentImpact(input, dependencies = defaultDependencies$12) {
|
|
19270
19270
|
validateSegmentSite(input.site);
|
|
19271
19271
|
const now = dependencies.now();
|
|
19272
19272
|
const ranges = resolveSegmentRanges({
|
|
@@ -19821,8 +19821,8 @@ const CANNIBAL_MINIMUM_PAGE_SHARE = MINIMUM_PAGE_SHARE;
|
|
|
19821
19821
|
const CANNIBAL_MAXIMUM_DOMINANT_SHARE = MAXIMUM_DOMINANT_SHARE;
|
|
19822
19822
|
//#endregion
|
|
19823
19823
|
//#region packages/core/src/analyze/site-diagnostics/cannibal.ts
|
|
19824
|
-
const MAX_GSC_ROWS$
|
|
19825
|
-
const defaultDependencies$
|
|
19824
|
+
const MAX_GSC_ROWS$4 = 1e5;
|
|
19825
|
+
const defaultDependencies$11 = {
|
|
19826
19826
|
searchAnalytics: querySearchAnalytics,
|
|
19827
19827
|
now: () => /* @__PURE__ */ new Date()
|
|
19828
19828
|
};
|
|
@@ -19835,7 +19835,7 @@ function verdict$4(input) {
|
|
|
19835
19835
|
const result = `${input.returned} of ${input.eligible} multi-URL query candidate${input.eligible === 1 ? "" : "s"} returned for intent and technical review.`;
|
|
19836
19836
|
return input.status === "partial" ? `${result} Provider evidence is partial.` : result;
|
|
19837
19837
|
}
|
|
19838
|
-
async function cannibalReport(input, dependencies = defaultDependencies$
|
|
19838
|
+
async function cannibalReport(input, dependencies = defaultDependencies$11) {
|
|
19839
19839
|
const days = integerOption$2({
|
|
19840
19840
|
value: input.days,
|
|
19841
19841
|
fallback: 28,
|
|
@@ -19865,7 +19865,7 @@ async function cannibalReport(input, dependencies = defaultDependencies$10) {
|
|
|
19865
19865
|
...range,
|
|
19866
19866
|
type: "web",
|
|
19867
19867
|
dataState: "final",
|
|
19868
|
-
maxRows: MAX_GSC_ROWS$
|
|
19868
|
+
maxRows: MAX_GSC_ROWS$4
|
|
19869
19869
|
};
|
|
19870
19870
|
const [pageExposure, propertyDemand] = await Promise.all([dependencies.searchAnalytics(input.site, {
|
|
19871
19871
|
...requestBase,
|
|
@@ -19888,8 +19888,8 @@ async function cannibalReport(input, dependencies = defaultDependencies$10) {
|
|
|
19888
19888
|
brandTerms: input.brandTerms,
|
|
19889
19889
|
includeBrand: input.includeBrand
|
|
19890
19890
|
});
|
|
19891
|
-
const pageTruncated = pageExposure.rowsFetched >= MAX_GSC_ROWS$
|
|
19892
|
-
const propertyTruncated = propertyDemand.rowsFetched >= MAX_GSC_ROWS$
|
|
19891
|
+
const pageTruncated = pageExposure.rowsFetched >= MAX_GSC_ROWS$4;
|
|
19892
|
+
const propertyTruncated = propertyDemand.rowsFetched >= MAX_GSC_ROWS$4;
|
|
19893
19893
|
const possiblyTruncated = pageTruncated || propertyTruncated;
|
|
19894
19894
|
const partialValidation = analysis.selection.invalidRows + analysis.selection.propertyInvalidRows > 0;
|
|
19895
19895
|
const completeness = possiblyTruncated && partialValidation ? "partial-and-possibly-truncated" : possiblyTruncated ? "possibly-truncated" : partialValidation ? "partial" : "complete";
|
|
@@ -19914,7 +19914,7 @@ async function cannibalReport(input, dependencies = defaultDependencies$10) {
|
|
|
19914
19914
|
invalidRows: analysis.selection.invalidRows
|
|
19915
19915
|
},
|
|
19916
19916
|
calls: pageExposure.calls,
|
|
19917
|
-
maxRows: MAX_GSC_ROWS$
|
|
19917
|
+
maxRows: MAX_GSC_ROWS$4,
|
|
19918
19918
|
possiblyTruncated: pageTruncated
|
|
19919
19919
|
},
|
|
19920
19920
|
propertyDemand: {
|
|
@@ -19926,7 +19926,7 @@ async function cannibalReport(input, dependencies = defaultDependencies$10) {
|
|
|
19926
19926
|
invalidRows: analysis.selection.propertyInvalidRows
|
|
19927
19927
|
},
|
|
19928
19928
|
calls: propertyDemand.calls,
|
|
19929
|
-
maxRows: MAX_GSC_ROWS$
|
|
19929
|
+
maxRows: MAX_GSC_ROWS$4,
|
|
19930
19930
|
possiblyTruncated: propertyTruncated
|
|
19931
19931
|
},
|
|
19932
19932
|
completeness
|
|
@@ -20234,8 +20234,8 @@ function analyzeDecay(input) {
|
|
|
20234
20234
|
}
|
|
20235
20235
|
//#endregion
|
|
20236
20236
|
//#region packages/core/src/analyze/site-diagnostics/decaying.ts
|
|
20237
|
-
const MAX_GSC_ROWS$
|
|
20238
|
-
const defaultDependencies$
|
|
20237
|
+
const MAX_GSC_ROWS$3 = 1e5;
|
|
20238
|
+
const defaultDependencies$10 = {
|
|
20239
20239
|
searchAnalytics: querySearchAnalytics,
|
|
20240
20240
|
now: () => /* @__PURE__ */ new Date()
|
|
20241
20241
|
};
|
|
@@ -20246,7 +20246,7 @@ function verdict$3(report) {
|
|
|
20246
20246
|
if (!report.selection.eligibleRows) return "No observed retained query/page declines matched the report filters.";
|
|
20247
20247
|
return `${report.selection.returnedRows} of ${report.selection.eligibleRows} observed retained query/page declines returned for review.`;
|
|
20248
20248
|
}
|
|
20249
|
-
async function decayingReport(input, dependencies = defaultDependencies$
|
|
20249
|
+
async function decayingReport(input, dependencies = defaultDependencies$10) {
|
|
20250
20250
|
if (input.comparison !== void 0 && input.comparison !== "previous-period" && input.comparison !== "year-over-year") throw new SeoError("INVALID_INPUT", "comparison must be previous-period or year-over-year.");
|
|
20251
20251
|
if (input.brandTerms && (input.brandTerms.length > 20 || input.brandTerms.some((term) => !term.trim() || term.trim().length > 200))) throw new SeoError("INVALID_INPUT", "brandTerms must contain at most 20 non-empty terms of 200 characters or fewer.");
|
|
20252
20252
|
const days = integerOption$2({
|
|
@@ -20299,7 +20299,7 @@ async function decayingReport(input, dependencies = defaultDependencies$9) {
|
|
|
20299
20299
|
aggregationType: "auto",
|
|
20300
20300
|
type: "web",
|
|
20301
20301
|
dataState: "final",
|
|
20302
|
-
maxRows: MAX_GSC_ROWS$
|
|
20302
|
+
maxRows: MAX_GSC_ROWS$3
|
|
20303
20303
|
};
|
|
20304
20304
|
const [current, previous] = await Promise.all([dependencies.searchAnalytics(input.site, {
|
|
20305
20305
|
...request,
|
|
@@ -20322,8 +20322,8 @@ async function decayingReport(input, dependencies = defaultDependencies$9) {
|
|
|
20322
20322
|
brandTerms: input.brandTerms,
|
|
20323
20323
|
includeBrand: input.includeBrand
|
|
20324
20324
|
});
|
|
20325
|
-
const currentTruncated = current.rowsFetched >= MAX_GSC_ROWS$
|
|
20326
|
-
const previousTruncated = previous.rowsFetched >= MAX_GSC_ROWS$
|
|
20325
|
+
const currentTruncated = current.rowsFetched >= MAX_GSC_ROWS$3;
|
|
20326
|
+
const previousTruncated = previous.rowsFetched >= MAX_GSC_ROWS$3;
|
|
20327
20327
|
const possiblyTruncated = currentTruncated || previousTruncated;
|
|
20328
20328
|
const dataStatus = current.rowsFetched === 0 && previous.rowsFetched === 0 ? "empty" : current.rowsFetched === 0 || previous.rowsFetched === 0 || possiblyTruncated || analysis.selection.currentInvalidRows > 0 || analysis.selection.previousInvalidRows > 0 || analysis.selection.currentRowNotRetained > 0 ? "partial" : analysis.selection.eligibleRows === 0 ? "filtered" : "complete";
|
|
20329
20329
|
const reportBase = {
|
|
@@ -20350,13 +20350,13 @@ async function decayingReport(input, dependencies = defaultDependencies$9) {
|
|
|
20350
20350
|
current: {
|
|
20351
20351
|
rowsFetched: current.rowsFetched,
|
|
20352
20352
|
calls: current.calls,
|
|
20353
|
-
maxRows: MAX_GSC_ROWS$
|
|
20353
|
+
maxRows: MAX_GSC_ROWS$3,
|
|
20354
20354
|
possiblyTruncated: currentTruncated
|
|
20355
20355
|
},
|
|
20356
20356
|
previous: {
|
|
20357
20357
|
rowsFetched: previous.rowsFetched,
|
|
20358
20358
|
calls: previous.calls,
|
|
20359
|
-
maxRows: MAX_GSC_ROWS$
|
|
20359
|
+
maxRows: MAX_GSC_ROWS$3,
|
|
20360
20360
|
possiblyTruncated: previousTruncated
|
|
20361
20361
|
},
|
|
20362
20362
|
completeness: possiblyTruncated ? "possibly-truncated" : "retained-query-rows-only"
|
|
@@ -20732,12 +20732,12 @@ function quickWinTemplateSummaries(items) {
|
|
|
20732
20732
|
}
|
|
20733
20733
|
//#endregion
|
|
20734
20734
|
//#region packages/core/src/analyze/site-diagnostics/quick-wins.ts
|
|
20735
|
-
const DEFAULT_DAYS$
|
|
20736
|
-
const MAX_DAYS$
|
|
20735
|
+
const DEFAULT_DAYS$3 = 28;
|
|
20736
|
+
const MAX_DAYS$3 = 548;
|
|
20737
20737
|
const DEFAULT_VERIFY_LIMIT$1 = 5;
|
|
20738
20738
|
const MAX_VERIFY_LIMIT = 100;
|
|
20739
20739
|
const MAX_SOURCE_ROWS$3 = 1e5;
|
|
20740
|
-
const defaultDependencies$
|
|
20740
|
+
const defaultDependencies$9 = {
|
|
20741
20741
|
searchAnalytics: querySearchAnalytics,
|
|
20742
20742
|
fetch: fetchPage,
|
|
20743
20743
|
extract: extractPage,
|
|
@@ -20756,13 +20756,13 @@ function applyVerification$1(item, coverage) {
|
|
|
20756
20756
|
evidenceRef: `${item.recommendation.evidenceRef} ${coverage.summary}`
|
|
20757
20757
|
};
|
|
20758
20758
|
}
|
|
20759
|
-
async function quickWinsReport(input, dependencies = defaultDependencies$
|
|
20760
|
-
const explicitRange = explicitDateRange(input, MAX_DAYS$
|
|
20759
|
+
async function quickWinsReport(input, dependencies = defaultDependencies$9) {
|
|
20760
|
+
const explicitRange = explicitDateRange(input, MAX_DAYS$3);
|
|
20761
20761
|
const days = explicitRange?.days ?? integerOption$2({
|
|
20762
20762
|
value: input.days,
|
|
20763
|
-
fallback: DEFAULT_DAYS$
|
|
20763
|
+
fallback: DEFAULT_DAYS$3,
|
|
20764
20764
|
minimum: 1,
|
|
20765
|
-
maximum: MAX_DAYS$
|
|
20765
|
+
maximum: MAX_DAYS$3,
|
|
20766
20766
|
label: "Days"
|
|
20767
20767
|
});
|
|
20768
20768
|
const verifyLimit = integerOption$2({
|
|
@@ -21129,11 +21129,11 @@ function analyzeStrikingDistanceRows(input) {
|
|
|
21129
21129
|
}
|
|
21130
21130
|
//#endregion
|
|
21131
21131
|
//#region packages/core/src/analyze/striking-distance.ts
|
|
21132
|
-
const DEFAULT_DAYS$
|
|
21133
|
-
const MAX_DAYS$
|
|
21132
|
+
const DEFAULT_DAYS$2 = 28;
|
|
21133
|
+
const MAX_DAYS$2 = 548;
|
|
21134
21134
|
const DEFAULT_VERIFY_LIMIT = 5;
|
|
21135
21135
|
const MAX_SOURCE_ROWS$2 = 1e5;
|
|
21136
|
-
const defaultDependencies$
|
|
21136
|
+
const defaultDependencies$8 = {
|
|
21137
21137
|
searchAnalytics: querySearchAnalytics,
|
|
21138
21138
|
verifyContent: verifyQueryContent,
|
|
21139
21139
|
now: () => /* @__PURE__ */ new Date()
|
|
@@ -21143,8 +21143,8 @@ function boundedInteger$3(value, fallback, minimum, maximum) {
|
|
|
21143
21143
|
return Math.min(maximum, Math.max(minimum, Math.floor(value)));
|
|
21144
21144
|
}
|
|
21145
21145
|
function reportDays(value) {
|
|
21146
|
-
if (value === void 0) return DEFAULT_DAYS$
|
|
21147
|
-
if (!Number.isInteger(value) || value < 1 || value > MAX_DAYS$
|
|
21146
|
+
if (value === void 0) return DEFAULT_DAYS$2;
|
|
21147
|
+
if (!Number.isInteger(value) || value < 1 || value > MAX_DAYS$2) throw new SeoError("INVALID_INPUT", `Days must be a whole number between 1 and ${MAX_DAYS$2}.`);
|
|
21148
21148
|
return value;
|
|
21149
21149
|
}
|
|
21150
21150
|
function dateRange(days, now) {
|
|
@@ -21183,7 +21183,7 @@ function verdict$2(analysis) {
|
|
|
21183
21183
|
if (analysis.dataStatus === "filtered") return "No retained query/page rows met the report filters.";
|
|
21184
21184
|
return `${countLabel(analysis.summary.eligibleRows, "eligible query/page row")} found; ${countLabel(analysis.summary.returnedRows, "row")} returned for review.`;
|
|
21185
21185
|
}
|
|
21186
|
-
async function strikingDistance(input, dependencies = defaultDependencies$
|
|
21186
|
+
async function strikingDistance(input, dependencies = defaultDependencies$8) {
|
|
21187
21187
|
const days = reportDays(input.days);
|
|
21188
21188
|
const generatedAt = dependencies.now().toISOString();
|
|
21189
21189
|
const range = dateRange(days, new Date(generatedAt));
|
|
@@ -22030,7 +22030,7 @@ function emptyQuickWins(input) {
|
|
|
22030
22030
|
warnings: []
|
|
22031
22031
|
};
|
|
22032
22032
|
}
|
|
22033
|
-
const defaultDependencies$
|
|
22033
|
+
const defaultDependencies$7 = {
|
|
22034
22034
|
trafficAnomaly,
|
|
22035
22035
|
updateCorrelation,
|
|
22036
22036
|
segmentImpact,
|
|
@@ -22041,7 +22041,7 @@ const defaultDependencies$6 = {
|
|
|
22041
22041
|
};
|
|
22042
22042
|
async function diagnoseProperty(input, dependencies = {}) {
|
|
22043
22043
|
const providers = {
|
|
22044
|
-
...defaultDependencies$
|
|
22044
|
+
...defaultDependencies$7,
|
|
22045
22045
|
...dependencies
|
|
22046
22046
|
};
|
|
22047
22047
|
const limit = input.limit ?? 10;
|
|
@@ -22989,6 +22989,42 @@ async function providerRequestJson(input) {
|
|
|
22989
22989
|
return parsed.data;
|
|
22990
22990
|
}
|
|
22991
22991
|
//#endregion
|
|
22992
|
+
//#region packages/core/src/providers/dataforseo/account-pricing.ts
|
|
22993
|
+
function unitPrice(components) {
|
|
22994
|
+
const prices = components ?? [];
|
|
22995
|
+
const total = (type) => {
|
|
22996
|
+
const matching = prices.filter((item) => item.cost_type === type);
|
|
22997
|
+
return matching.length ? matching.reduce((sum, item) => sum + Math.round(item.cost * 1e6), 0) : prices.length ? 0 : null;
|
|
22998
|
+
};
|
|
22999
|
+
return {
|
|
23000
|
+
perRequestMicros: total("per_request"),
|
|
23001
|
+
perResultMicros: total("per_result")
|
|
23002
|
+
};
|
|
23003
|
+
}
|
|
23004
|
+
function dataForSeoAccountPrices(account) {
|
|
23005
|
+
return {
|
|
23006
|
+
keywordOverviewPrice: unitPrice(account.price?.dataforseo_labs?.keyword_overview?.live?.priority_normal),
|
|
23007
|
+
keywordDiscoveryPrices: {
|
|
23008
|
+
ideas: unitPrice(account.price?.dataforseo_labs?.keyword_ideas?.live?.priority_normal),
|
|
23009
|
+
related: unitPrice(account.price?.dataforseo_labs?.related_keywords?.live?.priority_normal),
|
|
23010
|
+
suggestions: unitPrice(account.price?.dataforseo_labs?.keyword_suggestions?.live?.priority_normal)
|
|
23011
|
+
},
|
|
23012
|
+
domainResearchPrices: {
|
|
23013
|
+
domainOverview: unitPrice(account.price?.dataforseo_labs?.domain_rank_overview?.live?.priority_normal),
|
|
23014
|
+
rankedKeywords: unitPrice(account.price?.dataforseo_labs?.ranked_keywords?.live?.priority_normal),
|
|
23015
|
+
rankingPages: unitPrice(account.price?.dataforseo_labs?.relevant_pages?.live?.priority_normal),
|
|
23016
|
+
serpCompetitors: unitPrice(account.price?.dataforseo_labs?.serp_competitors?.live?.priority_normal)
|
|
23017
|
+
},
|
|
23018
|
+
linkPrices: {
|
|
23019
|
+
summary: unitPrice(account.price?.backlinks?.summary?.live?.priority_normal),
|
|
23020
|
+
backlinks: unitPrice(account.price?.backlinks?.backlinks?.live?.priority_normal),
|
|
23021
|
+
referringDomains: unitPrice(account.price?.backlinks?.referring_domains?.live?.priority_normal)
|
|
23022
|
+
},
|
|
23023
|
+
serpLiveAdvancedPrice: unitPrice(account.price?.serp?.live?.advanced?.priority_normal),
|
|
23024
|
+
serpTaskPostPrice: unitPrice(account.price?.serp?.task_post?.priority_normal)
|
|
23025
|
+
};
|
|
23026
|
+
}
|
|
23027
|
+
//#endregion
|
|
22992
23028
|
//#region packages/core/src/providers/dataforseo/account-schema.ts
|
|
22993
23029
|
const userDataMoneySchema = z.object({
|
|
22994
23030
|
total: z.number().finite().nonnegative().optional(),
|
|
@@ -23006,6 +23042,11 @@ const priceComponentSchema = z.object({
|
|
|
23006
23042
|
const normalPriorityPriceSchema = z.object({ priority_normal: z.array(priceComponentSchema).max(20).optional() }).passthrough();
|
|
23007
23043
|
const livePriceSchema = z.object({ live: normalPriorityPriceSchema.optional() }).passthrough();
|
|
23008
23044
|
const userDataPriceSchema = z.object({
|
|
23045
|
+
backlinks: z.object({
|
|
23046
|
+
summary: livePriceSchema.optional(),
|
|
23047
|
+
backlinks: livePriceSchema.optional(),
|
|
23048
|
+
referring_domains: livePriceSchema.optional()
|
|
23049
|
+
}).passthrough().optional(),
|
|
23009
23050
|
dataforseo_labs: z.object({
|
|
23010
23051
|
keyword_overview: livePriceSchema.optional(),
|
|
23011
23052
|
keyword_ideas: livePriceSchema.optional(),
|
|
@@ -23063,7 +23104,7 @@ function discoveryNextCursor(response) {
|
|
|
23063
23104
|
//#region packages/core/src/providers/dataforseo/schema.ts
|
|
23064
23105
|
const MAX_PROVIDER_MONTHLY_SEARCH_ROWS = 120;
|
|
23065
23106
|
const nullableNumber$1 = z.number().finite().nullable().optional();
|
|
23066
|
-
const nullableCount$
|
|
23107
|
+
const nullableCount$2 = z.union([z.number().finite(), z.string().trim().min(1)]).nullable().optional();
|
|
23067
23108
|
const monthlySearchSchema$1 = z.object({
|
|
23068
23109
|
year: z.number().int(),
|
|
23069
23110
|
month: z.number().int(),
|
|
@@ -23079,7 +23120,7 @@ const keywordOverviewItemSchema = z.object({
|
|
|
23079
23120
|
last_updated_time: z.string().trim().min(1).nullable().optional()
|
|
23080
23121
|
}).nullish(),
|
|
23081
23122
|
keyword_properties: z.object({ keyword_difficulty: nullableNumber$1 }).nullish(),
|
|
23082
|
-
serp_info: z.object({ se_results_count: nullableCount$
|
|
23123
|
+
serp_info: z.object({ se_results_count: nullableCount$2 }).nullish(),
|
|
23083
23124
|
search_intent_info: z.object({ main_intent: z.enum([
|
|
23084
23125
|
"informational",
|
|
23085
23126
|
"navigational",
|
|
@@ -23152,37 +23193,37 @@ const dataForSeoDiscoveryResponseSchema = z.object({
|
|
|
23152
23193
|
//#region packages/core/src/providers/dataforseo/domain-schema.ts
|
|
23153
23194
|
const nullableNumber = z.number().finite().nullable().optional();
|
|
23154
23195
|
const nullableNonnegativeNumber = z.number().finite().nonnegative().nullable().optional();
|
|
23155
|
-
const nullableCount = z.number().int().nonnegative().nullable().optional();
|
|
23196
|
+
const nullableCount$1 = z.number().int().nonnegative().nullable().optional();
|
|
23156
23197
|
const rankingMetricsSchema = z.object({
|
|
23157
|
-
pos_1: nullableCount,
|
|
23158
|
-
pos_2_3: nullableCount,
|
|
23159
|
-
pos_4_10: nullableCount,
|
|
23160
|
-
pos_11_20: nullableCount,
|
|
23161
|
-
pos_21_30: nullableCount,
|
|
23162
|
-
pos_31_40: nullableCount,
|
|
23163
|
-
pos_41_50: nullableCount,
|
|
23164
|
-
pos_51_60: nullableCount,
|
|
23165
|
-
pos_61_70: nullableCount,
|
|
23166
|
-
pos_71_80: nullableCount,
|
|
23167
|
-
pos_81_90: nullableCount,
|
|
23168
|
-
pos_91_100: nullableCount,
|
|
23198
|
+
pos_1: nullableCount$1,
|
|
23199
|
+
pos_2_3: nullableCount$1,
|
|
23200
|
+
pos_4_10: nullableCount$1,
|
|
23201
|
+
pos_11_20: nullableCount$1,
|
|
23202
|
+
pos_21_30: nullableCount$1,
|
|
23203
|
+
pos_31_40: nullableCount$1,
|
|
23204
|
+
pos_41_50: nullableCount$1,
|
|
23205
|
+
pos_51_60: nullableCount$1,
|
|
23206
|
+
pos_61_70: nullableCount$1,
|
|
23207
|
+
pos_71_80: nullableCount$1,
|
|
23208
|
+
pos_81_90: nullableCount$1,
|
|
23209
|
+
pos_91_100: nullableCount$1,
|
|
23169
23210
|
etv: nullableNonnegativeNumber,
|
|
23170
|
-
count: nullableCount,
|
|
23211
|
+
count: nullableCount$1,
|
|
23171
23212
|
estimated_paid_traffic_cost: nullableNonnegativeNumber,
|
|
23172
|
-
is_new: nullableCount,
|
|
23173
|
-
is_up: nullableCount,
|
|
23174
|
-
is_down: nullableCount,
|
|
23175
|
-
is_lost: nullableCount
|
|
23213
|
+
is_new: nullableCount$1,
|
|
23214
|
+
is_up: nullableCount$1,
|
|
23215
|
+
is_down: nullableCount$1,
|
|
23216
|
+
is_lost: nullableCount$1
|
|
23176
23217
|
}).passthrough();
|
|
23177
23218
|
const metricsSchema = z.object({ organic: rankingMetricsSchema.nullable().optional() }).passthrough();
|
|
23178
|
-
const paidResponseSchema = z.object({
|
|
23219
|
+
const paidResponseSchema$1 = z.object({
|
|
23179
23220
|
status_code: z.number().int(),
|
|
23180
23221
|
status_message: z.string().max(1e3),
|
|
23181
23222
|
cost: z.number().finite().nonnegative().optional(),
|
|
23182
23223
|
tasks_count: z.number().int().nonnegative(),
|
|
23183
23224
|
tasks_error: z.number().int().nonnegative()
|
|
23184
23225
|
}).passthrough();
|
|
23185
|
-
const taskFields = {
|
|
23226
|
+
const taskFields$1 = {
|
|
23186
23227
|
id: z.string().trim().min(1).max(100).optional(),
|
|
23187
23228
|
status_code: z.number().int(),
|
|
23188
23229
|
status_message: z.string().max(1e3),
|
|
@@ -23202,8 +23243,8 @@ const overviewResultSchema = z.object({
|
|
|
23202
23243
|
items_count: z.number().int().nonnegative().optional(),
|
|
23203
23244
|
items: z.array(overviewItemSchema).max(1e3).nullable().optional()
|
|
23204
23245
|
}).passthrough();
|
|
23205
|
-
const dataForSeoDomainOverviewResponseSchema = paidResponseSchema.extend({ tasks: z.array(z.object({
|
|
23206
|
-
...taskFields,
|
|
23246
|
+
const dataForSeoDomainOverviewResponseSchema = paidResponseSchema$1.extend({ tasks: z.array(z.object({
|
|
23247
|
+
...taskFields$1,
|
|
23207
23248
|
result: z.array(overviewResultSchema).max(10).nullable().optional()
|
|
23208
23249
|
}).passthrough()).max(10) });
|
|
23209
23250
|
const rankedSerpItemSchema = z.object({
|
|
@@ -23223,8 +23264,8 @@ const rankedKeywordsResultSchema = z.object({
|
|
|
23223
23264
|
items_count: z.number().int().nonnegative().optional(),
|
|
23224
23265
|
items: z.array(rankedKeywordItemSchema).max(1e3).nullable().optional()
|
|
23225
23266
|
}).passthrough();
|
|
23226
|
-
const dataForSeoRankedKeywordsResponseSchema = paidResponseSchema.extend({ tasks: z.array(z.object({
|
|
23227
|
-
...taskFields,
|
|
23267
|
+
const dataForSeoRankedKeywordsResponseSchema = paidResponseSchema$1.extend({ tasks: z.array(z.object({
|
|
23268
|
+
...taskFields$1,
|
|
23228
23269
|
result: z.array(rankedKeywordsResultSchema).max(10).nullable().optional()
|
|
23229
23270
|
}).passthrough()).max(10) });
|
|
23230
23271
|
const rankingPageItemSchema = z.object({
|
|
@@ -23237,8 +23278,8 @@ const rankingPagesResultSchema = z.object({
|
|
|
23237
23278
|
items_count: z.number().int().nonnegative().optional(),
|
|
23238
23279
|
items: z.array(rankingPageItemSchema).max(1e3).nullable().optional()
|
|
23239
23280
|
}).passthrough();
|
|
23240
|
-
const dataForSeoRankingPagesResponseSchema = paidResponseSchema.extend({ tasks: z.array(z.object({
|
|
23241
|
-
...taskFields,
|
|
23281
|
+
const dataForSeoRankingPagesResponseSchema = paidResponseSchema$1.extend({ tasks: z.array(z.object({
|
|
23282
|
+
...taskFields$1,
|
|
23242
23283
|
result: z.array(rankingPagesResultSchema).max(10).nullable().optional()
|
|
23243
23284
|
}).passthrough()).max(10) });
|
|
23244
23285
|
const competitorItemSchema = z.object({
|
|
@@ -23246,9 +23287,9 @@ const competitorItemSchema = z.object({
|
|
|
23246
23287
|
avg_position: nullableNumber,
|
|
23247
23288
|
median_position: nullableNumber,
|
|
23248
23289
|
etv: nullableNonnegativeNumber,
|
|
23249
|
-
keywords_count: nullableCount,
|
|
23290
|
+
keywords_count: nullableCount$1,
|
|
23250
23291
|
visibility: nullableNonnegativeNumber,
|
|
23251
|
-
relevant_serp_items: nullableCount,
|
|
23292
|
+
relevant_serp_items: nullableCount$1,
|
|
23252
23293
|
keywords_positions: z.record(z.string().trim().min(1).max(80), z.array(z.number().int().positive()).max(100)).nullable().optional()
|
|
23253
23294
|
}).passthrough();
|
|
23254
23295
|
const competitorsResultSchema = z.object({
|
|
@@ -23257,8 +23298,8 @@ const competitorsResultSchema = z.object({
|
|
|
23257
23298
|
items_count: z.number().int().nonnegative().optional(),
|
|
23258
23299
|
items: z.array(competitorItemSchema).max(1e3).nullable().optional()
|
|
23259
23300
|
}).passthrough();
|
|
23260
|
-
const dataForSeoSerpCompetitorsResponseSchema = paidResponseSchema.extend({ tasks: z.array(z.object({
|
|
23261
|
-
...taskFields,
|
|
23301
|
+
const dataForSeoSerpCompetitorsResponseSchema = paidResponseSchema$1.extend({ tasks: z.array(z.object({
|
|
23302
|
+
...taskFields$1,
|
|
23262
23303
|
result: z.array(competitorsResultSchema).max(10).nullable().optional()
|
|
23263
23304
|
}).passthrough()).max(10) });
|
|
23264
23305
|
const DOMAIN_OVERVIEW_PATH = "v3/dataforseo_labs/google/domain_rank_overview/live";
|
|
@@ -23448,6 +23489,209 @@ function serpCompetitorsPaidRequest(input, ttlMs) {
|
|
|
23448
23489
|
};
|
|
23449
23490
|
}
|
|
23450
23491
|
//#endregion
|
|
23492
|
+
//#region packages/core/src/providers/dataforseo/link-schema.ts
|
|
23493
|
+
const nullableCount = z.number().int().nonnegative().nullable().optional();
|
|
23494
|
+
const nullableScore = z.number().finite().nonnegative().nullable().optional();
|
|
23495
|
+
const nullableText = z.string().max(2e4).nullable().optional();
|
|
23496
|
+
const paidResponseSchema = z.object({
|
|
23497
|
+
status_code: z.number().int(),
|
|
23498
|
+
status_message: z.string().max(1e3),
|
|
23499
|
+
cost: z.number().finite().nonnegative().optional(),
|
|
23500
|
+
tasks_count: z.number().int().nonnegative(),
|
|
23501
|
+
tasks_error: z.number().int().nonnegative()
|
|
23502
|
+
}).passthrough();
|
|
23503
|
+
const taskFields = {
|
|
23504
|
+
id: z.string().trim().min(1).max(100).optional(),
|
|
23505
|
+
status_code: z.number().int(),
|
|
23506
|
+
status_message: z.string().max(1e3),
|
|
23507
|
+
cost: z.number().finite().nonnegative().optional(),
|
|
23508
|
+
result_count: z.number().int().nonnegative().optional()
|
|
23509
|
+
};
|
|
23510
|
+
const dataForSeoLinkSummaryItemSchema = z.object({
|
|
23511
|
+
target: z.string().trim().min(1).max(2048).optional(),
|
|
23512
|
+
rank: nullableScore,
|
|
23513
|
+
backlinks: nullableCount,
|
|
23514
|
+
referring_pages: nullableCount,
|
|
23515
|
+
referring_domains: nullableCount,
|
|
23516
|
+
broken_backlinks: nullableCount,
|
|
23517
|
+
broken_pages: nullableCount,
|
|
23518
|
+
backlinks_spam_score: nullableScore,
|
|
23519
|
+
info: z.object({ target_spam_score: nullableScore }).passthrough().nullable().optional()
|
|
23520
|
+
}).passthrough();
|
|
23521
|
+
const dataForSeoLinkSummaryResponseSchema = paidResponseSchema.extend({ tasks: z.array(z.object({
|
|
23522
|
+
...taskFields,
|
|
23523
|
+
result: z.array(dataForSeoLinkSummaryItemSchema).max(10).nullable().optional()
|
|
23524
|
+
}).passthrough()).max(10) });
|
|
23525
|
+
const dataForSeoBacklinkItemSchema = z.object({
|
|
23526
|
+
domain_from: nullableText,
|
|
23527
|
+
url_from: nullableText,
|
|
23528
|
+
url_to: nullableText,
|
|
23529
|
+
anchor: nullableText,
|
|
23530
|
+
item_type: z.string().max(100).nullable().optional(),
|
|
23531
|
+
dofollow: z.boolean().nullable().optional(),
|
|
23532
|
+
rank: nullableScore,
|
|
23533
|
+
domain_from_rank: nullableScore,
|
|
23534
|
+
page_from_rank: nullableScore,
|
|
23535
|
+
backlink_spam_score: nullableScore,
|
|
23536
|
+
backlinks_spam_score: nullableScore,
|
|
23537
|
+
first_seen: z.string().max(100).nullable().optional(),
|
|
23538
|
+
last_visited: z.string().max(100).nullable().optional(),
|
|
23539
|
+
lost_date: z.string().max(100).nullable().optional(),
|
|
23540
|
+
is_lost: z.boolean().nullable().optional(),
|
|
23541
|
+
is_indirect_link: z.boolean().nullable().optional(),
|
|
23542
|
+
links_count: nullableCount,
|
|
23543
|
+
group_count: nullableCount,
|
|
23544
|
+
rel_attributes: z.array(z.string().trim().min(1).max(100)).max(100).nullable().optional(),
|
|
23545
|
+
attributes: z.array(z.string().trim().min(1).max(100)).max(100).nullable().optional()
|
|
23546
|
+
}).passthrough();
|
|
23547
|
+
const backlinkResultSchema = z.object({
|
|
23548
|
+
target: z.string().trim().min(1).max(2048).optional(),
|
|
23549
|
+
total_count: nullableCount,
|
|
23550
|
+
items_count: nullableCount,
|
|
23551
|
+
search_after_token: z.string().max(2e4).nullable().optional(),
|
|
23552
|
+
items: z.array(dataForSeoBacklinkItemSchema).max(1e3).nullable().optional()
|
|
23553
|
+
}).passthrough();
|
|
23554
|
+
const dataForSeoBacklinksResponseSchema = paidResponseSchema.extend({ tasks: z.array(z.object({
|
|
23555
|
+
...taskFields,
|
|
23556
|
+
result: z.array(backlinkResultSchema).max(10).nullable().optional()
|
|
23557
|
+
}).passthrough()).max(10) });
|
|
23558
|
+
const dataForSeoReferringDomainItemSchema = z.object({
|
|
23559
|
+
domain: z.string().trim().min(1).max(2048).nullable().optional(),
|
|
23560
|
+
backlinks: nullableCount,
|
|
23561
|
+
referring_pages: nullableCount,
|
|
23562
|
+
broken_backlinks: nullableCount,
|
|
23563
|
+
broken_pages: nullableCount,
|
|
23564
|
+
rank: nullableScore,
|
|
23565
|
+
backlinks_spam_score: nullableScore,
|
|
23566
|
+
first_seen: z.string().max(100).nullable().optional()
|
|
23567
|
+
}).passthrough();
|
|
23568
|
+
const referringDomainsResultSchema = z.object({
|
|
23569
|
+
target: z.string().trim().min(1).max(2048).optional(),
|
|
23570
|
+
total_count: nullableCount,
|
|
23571
|
+
items_count: nullableCount,
|
|
23572
|
+
items: z.array(dataForSeoReferringDomainItemSchema).max(1e3).nullable().optional()
|
|
23573
|
+
}).passthrough();
|
|
23574
|
+
const dataForSeoReferringDomainsResponseSchema = paidResponseSchema.extend({ tasks: z.array(z.object({
|
|
23575
|
+
...taskFields,
|
|
23576
|
+
result: z.array(referringDomainsResultSchema).max(10).nullable().optional()
|
|
23577
|
+
}).passthrough()).max(10) });
|
|
23578
|
+
const MAX_LINK_ROWS$1 = 1e3;
|
|
23579
|
+
const MAX_LINK_OFFSET = 2e4;
|
|
23580
|
+
const LINK_ENDPOINTS = {
|
|
23581
|
+
summary: "v3/backlinks/summary/live",
|
|
23582
|
+
backlinks: "v3/backlinks/backlinks/live",
|
|
23583
|
+
referringDomains: "v3/backlinks/referring_domains/live"
|
|
23584
|
+
};
|
|
23585
|
+
function resultRows(response) {
|
|
23586
|
+
return response.tasks.reduce((taskTotal, task) => taskTotal + (task.result ?? []).reduce((resultTotal, result) => resultTotal + (result.items?.length ?? 0), 0), 0);
|
|
23587
|
+
}
|
|
23588
|
+
function summaryRows(response) {
|
|
23589
|
+
return response.tasks.reduce((total, task) => total + (task.result?.length ?? 0), 0);
|
|
23590
|
+
}
|
|
23591
|
+
function commonRequest(input) {
|
|
23592
|
+
return {
|
|
23593
|
+
target: input.target,
|
|
23594
|
+
include_subdomains: input.scope === "domain" ? input.includeSubdomains : false,
|
|
23595
|
+
include_indirect_links: true,
|
|
23596
|
+
exclude_internal_backlinks: true,
|
|
23597
|
+
backlinks_status_type: "live",
|
|
23598
|
+
rank_scale: "one_hundred"
|
|
23599
|
+
};
|
|
23600
|
+
}
|
|
23601
|
+
function validatePage(input) {
|
|
23602
|
+
if (!Number.isSafeInteger(input.limit) || input.limit < 1 || input.limit > 1e3) throw new ProviderError({
|
|
23603
|
+
provider: "dataforseo",
|
|
23604
|
+
operation: input.operation,
|
|
23605
|
+
code: "configuration",
|
|
23606
|
+
message: `Link row limit must be from 1 to ${MAX_LINK_ROWS$1}.`
|
|
23607
|
+
});
|
|
23608
|
+
if (!Number.isSafeInteger(input.offset) || input.offset < 0 || input.offset > 2e4) throw new ProviderError({
|
|
23609
|
+
provider: "dataforseo",
|
|
23610
|
+
operation: input.operation,
|
|
23611
|
+
code: "configuration",
|
|
23612
|
+
message: `Link row offset must be from 0 to ${MAX_LINK_OFFSET}.`
|
|
23613
|
+
});
|
|
23614
|
+
}
|
|
23615
|
+
function linkSummaryPaidRequest(input, ttlMs) {
|
|
23616
|
+
return {
|
|
23617
|
+
operation: "link-summary",
|
|
23618
|
+
capability: "link-summary",
|
|
23619
|
+
endpoint: LINK_ENDPOINTS.summary,
|
|
23620
|
+
request: commonRequest(input),
|
|
23621
|
+
schema: dataForSeoLinkSummaryResponseSchema,
|
|
23622
|
+
requestedRows: 1,
|
|
23623
|
+
price: (account) => account.linkPrices.summary,
|
|
23624
|
+
context: input.context,
|
|
23625
|
+
ttlMs,
|
|
23626
|
+
refresh: input.refresh,
|
|
23627
|
+
rowCount: summaryRows
|
|
23628
|
+
};
|
|
23629
|
+
}
|
|
23630
|
+
function backlinksPaidRequest(input, ttlMs) {
|
|
23631
|
+
validatePage({
|
|
23632
|
+
limit: input.limit,
|
|
23633
|
+
offset: input.offset,
|
|
23634
|
+
operation: "backlinks"
|
|
23635
|
+
});
|
|
23636
|
+
if (input.orderBy.length < 1 || input.orderBy.length > 3) throw new ProviderError({
|
|
23637
|
+
provider: "dataforseo",
|
|
23638
|
+
operation: "backlinks",
|
|
23639
|
+
code: "configuration",
|
|
23640
|
+
message: "Backlink rows need from 1 to 3 sort rules."
|
|
23641
|
+
});
|
|
23642
|
+
return {
|
|
23643
|
+
operation: "backlinks",
|
|
23644
|
+
capability: "backlinks",
|
|
23645
|
+
endpoint: LINK_ENDPOINTS.backlinks,
|
|
23646
|
+
request: {
|
|
23647
|
+
...commonRequest(input),
|
|
23648
|
+
mode: input.mode,
|
|
23649
|
+
backlinks_status_type: input.status,
|
|
23650
|
+
limit: input.limit,
|
|
23651
|
+
offset: input.offset,
|
|
23652
|
+
order_by: input.orderBy
|
|
23653
|
+
},
|
|
23654
|
+
schema: dataForSeoBacklinksResponseSchema,
|
|
23655
|
+
requestedRows: input.limit,
|
|
23656
|
+
price: (account) => account.linkPrices.backlinks,
|
|
23657
|
+
context: input.context,
|
|
23658
|
+
ttlMs,
|
|
23659
|
+
refresh: input.refresh,
|
|
23660
|
+
rowCount: resultRows
|
|
23661
|
+
};
|
|
23662
|
+
}
|
|
23663
|
+
function referringDomainsPaidRequest(input, ttlMs) {
|
|
23664
|
+
validatePage({
|
|
23665
|
+
limit: input.limit,
|
|
23666
|
+
offset: input.offset,
|
|
23667
|
+
operation: "referring-domains"
|
|
23668
|
+
});
|
|
23669
|
+
if (input.orderBy.length < 1 || input.orderBy.length > 3) throw new ProviderError({
|
|
23670
|
+
provider: "dataforseo",
|
|
23671
|
+
operation: "referring-domains",
|
|
23672
|
+
code: "configuration",
|
|
23673
|
+
message: "Referring-domain rows need from 1 to 3 sort rules."
|
|
23674
|
+
});
|
|
23675
|
+
return {
|
|
23676
|
+
operation: "referring-domains",
|
|
23677
|
+
capability: "referring-domains",
|
|
23678
|
+
endpoint: LINK_ENDPOINTS.referringDomains,
|
|
23679
|
+
request: {
|
|
23680
|
+
...commonRequest(input),
|
|
23681
|
+
limit: input.limit,
|
|
23682
|
+
offset: input.offset,
|
|
23683
|
+
order_by: input.orderBy
|
|
23684
|
+
},
|
|
23685
|
+
schema: dataForSeoReferringDomainsResponseSchema,
|
|
23686
|
+
requestedRows: input.limit,
|
|
23687
|
+
price: (account) => account.linkPrices.referringDomains,
|
|
23688
|
+
context: input.context,
|
|
23689
|
+
ttlMs,
|
|
23690
|
+
refresh: input.refresh,
|
|
23691
|
+
rowCount: resultRows
|
|
23692
|
+
};
|
|
23693
|
+
}
|
|
23694
|
+
//#endregion
|
|
23451
23695
|
//#region packages/core/src/providers/dataforseo/paid-request.ts
|
|
23452
23696
|
function usdToMicros(value) {
|
|
23453
23697
|
if (value === void 0) return null;
|
|
@@ -23971,41 +24215,6 @@ const MAX_KEYWORD_CHARACTERS = 80;
|
|
|
23971
24215
|
const MAX_KEYWORD_WORDS = 10;
|
|
23972
24216
|
const MAX_DISCOVERY_SEEDS$1 = 5;
|
|
23973
24217
|
const MAX_DISCOVERY_ROWS$1 = 100;
|
|
23974
|
-
function unitPrice(components) {
|
|
23975
|
-
const prices = components ?? [];
|
|
23976
|
-
const total = (type) => {
|
|
23977
|
-
const matching = prices.filter((item) => item.cost_type === type);
|
|
23978
|
-
return matching.length ? matching.reduce((sum, item) => sum + Math.round(item.cost * 1e6), 0) : prices.length ? 0 : null;
|
|
23979
|
-
};
|
|
23980
|
-
return {
|
|
23981
|
-
perRequestMicros: total("per_request"),
|
|
23982
|
-
perResultMicros: total("per_result")
|
|
23983
|
-
};
|
|
23984
|
-
}
|
|
23985
|
-
function keywordOverviewPrice(account) {
|
|
23986
|
-
return unitPrice(account.price?.dataforseo_labs?.keyword_overview?.live?.priority_normal);
|
|
23987
|
-
}
|
|
23988
|
-
function keywordDiscoveryPrices(account) {
|
|
23989
|
-
return {
|
|
23990
|
-
ideas: unitPrice(account.price?.dataforseo_labs?.keyword_ideas?.live?.priority_normal),
|
|
23991
|
-
related: unitPrice(account.price?.dataforseo_labs?.related_keywords?.live?.priority_normal),
|
|
23992
|
-
suggestions: unitPrice(account.price?.dataforseo_labs?.keyword_suggestions?.live?.priority_normal)
|
|
23993
|
-
};
|
|
23994
|
-
}
|
|
23995
|
-
function domainResearchPrices(account) {
|
|
23996
|
-
return {
|
|
23997
|
-
domainOverview: unitPrice(account.price?.dataforseo_labs?.domain_rank_overview?.live?.priority_normal),
|
|
23998
|
-
rankedKeywords: unitPrice(account.price?.dataforseo_labs?.ranked_keywords?.live?.priority_normal),
|
|
23999
|
-
rankingPages: unitPrice(account.price?.dataforseo_labs?.relevant_pages?.live?.priority_normal),
|
|
24000
|
-
serpCompetitors: unitPrice(account.price?.dataforseo_labs?.serp_competitors?.live?.priority_normal)
|
|
24001
|
-
};
|
|
24002
|
-
}
|
|
24003
|
-
function serpLiveAdvancedPrice(account) {
|
|
24004
|
-
return unitPrice(account.price?.serp?.live?.advanced?.priority_normal);
|
|
24005
|
-
}
|
|
24006
|
-
function serpTaskPostPrice(account) {
|
|
24007
|
-
return unitPrice(account.price?.serp?.task_post?.priority_normal);
|
|
24008
|
-
}
|
|
24009
24218
|
function responseTaskIds(response) {
|
|
24010
24219
|
return [...new Set(response.tasks.flatMap((task) => task.id ? [task.id] : []))].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
|
|
24011
24220
|
}
|
|
@@ -24025,6 +24234,7 @@ var DataForSeoClient = class {
|
|
|
24025
24234
|
serpTtlMs;
|
|
24026
24235
|
accountPricingTtlMs;
|
|
24027
24236
|
domainResearchTtlMs;
|
|
24237
|
+
linkTtlMs;
|
|
24028
24238
|
spendLimits;
|
|
24029
24239
|
accountPricing;
|
|
24030
24240
|
accountPricingRequest;
|
|
@@ -24041,6 +24251,7 @@ var DataForSeoClient = class {
|
|
|
24041
24251
|
this.serpTtlMs = options.serpTtlMs ?? DEFAULT_SERP_TTL_MS;
|
|
24042
24252
|
this.accountPricingTtlMs = options.accountPricingTtlMs ?? DEFAULT_ACCOUNT_PRICING_TTL_MS;
|
|
24043
24253
|
this.domainResearchTtlMs = options.domainResearchTtlMs ?? 6048e5;
|
|
24254
|
+
this.linkTtlMs = options.linkTtlMs ?? 864e5;
|
|
24044
24255
|
this.spendLimits = options.spendLimits;
|
|
24045
24256
|
}
|
|
24046
24257
|
async getCredentials(operation) {
|
|
@@ -24123,11 +24334,7 @@ var DataForSeoClient = class {
|
|
|
24123
24334
|
accountDailySpendMicros: dataForSeoUsdToMicros(account.money?.statistics?.day?.total),
|
|
24124
24335
|
accountDailySpendPeriod: account.money?.statistics?.day?.value ?? null,
|
|
24125
24336
|
accountDailyLimitMicros: dataForSeoUsdToMicros(account.money?.limits?.day?.total),
|
|
24126
|
-
|
|
24127
|
-
keywordDiscoveryPrices: keywordDiscoveryPrices(account),
|
|
24128
|
-
domainResearchPrices: domainResearchPrices(account),
|
|
24129
|
-
serpLiveAdvancedPrice: serpLiveAdvancedPrice(account),
|
|
24130
|
-
serpTaskPostPrice: serpTaskPostPrice(account),
|
|
24337
|
+
...dataForSeoAccountPrices(account),
|
|
24131
24338
|
backlinksSubscriptionExpiresAt: account.backlinks_subscription_expiry_date ?? null,
|
|
24132
24339
|
aiMentionsSubscriptionExpiresAt: account.llm_mentions_subscription_expiry_date ?? null,
|
|
24133
24340
|
apiVersion: response.version ?? null,
|
|
@@ -24190,6 +24397,15 @@ var DataForSeoClient = class {
|
|
|
24190
24397
|
async serpCompetitors(input) {
|
|
24191
24398
|
return this.paidPost(serpCompetitorsPaidRequest(input, this.domainResearchTtlMs));
|
|
24192
24399
|
}
|
|
24400
|
+
async linkSummary(input) {
|
|
24401
|
+
return this.paidPost(linkSummaryPaidRequest(input, this.linkTtlMs));
|
|
24402
|
+
}
|
|
24403
|
+
async backlinks(input) {
|
|
24404
|
+
return this.paidPost(backlinksPaidRequest(input, this.linkTtlMs));
|
|
24405
|
+
}
|
|
24406
|
+
async referringDomains(input) {
|
|
24407
|
+
return this.paidPost(referringDomainsPaidRequest(input, this.linkTtlMs));
|
|
24408
|
+
}
|
|
24193
24409
|
async keywordOverview(input) {
|
|
24194
24410
|
const keywords = input.keywords.map((keyword) => keyword.trim());
|
|
24195
24411
|
if (keywords.length < 1 || keywords.length > MAX_KEYWORDS_PER_OVERVIEW_REQUEST || keywords.some((keyword) => keyword.length < 1 || keyword.length > MAX_KEYWORD_CHARACTERS || keyword.split(/\s+/u).length > MAX_KEYWORD_WORDS)) throw new ProviderError({
|
|
@@ -24533,20 +24749,20 @@ function compareCodepoints$3(left, right) {
|
|
|
24533
24749
|
function normalizedKeyword$2(keyword) {
|
|
24534
24750
|
return keyword.trim().replace(/\s+/gu, " ").toLowerCase();
|
|
24535
24751
|
}
|
|
24536
|
-
function missing$
|
|
24752
|
+
function missing$2(field) {
|
|
24537
24753
|
return unavailableValue("missing", `DataForSEO omitted ${field}.`);
|
|
24538
24754
|
}
|
|
24539
24755
|
function numericValue(values, field, valid) {
|
|
24540
24756
|
const present = values.filter((value) => value !== null && value !== void 0);
|
|
24541
|
-
if (present.length === 0) return missing$
|
|
24757
|
+
if (present.length === 0) return missing$2(field);
|
|
24542
24758
|
const numbers = present.filter((value) => typeof value === "number" && valid(value));
|
|
24543
24759
|
if (numbers.length !== present.length) return unavailableValue("invalid", `DataForSEO returned an invalid ${field}.`);
|
|
24544
24760
|
const unique = [...new Set(numbers)];
|
|
24545
24761
|
return unique.length === 1 ? observedValue(unique[0]) : unavailableValue("invalid", `DataForSEO returned conflicting ${field} values.`);
|
|
24546
24762
|
}
|
|
24547
|
-
function stringValue(values, field, normalize) {
|
|
24763
|
+
function stringValue$1(values, field, normalize) {
|
|
24548
24764
|
const present = values.filter((value) => value !== null && value !== void 0);
|
|
24549
|
-
if (present.length === 0) return missing$
|
|
24765
|
+
if (present.length === 0) return missing$2(field);
|
|
24550
24766
|
const strings = present.flatMap((value) => {
|
|
24551
24767
|
if (typeof value !== "string") return [];
|
|
24552
24768
|
const normalized = normalize(value);
|
|
@@ -24558,7 +24774,7 @@ function stringValue(values, field, normalize) {
|
|
|
24558
24774
|
}
|
|
24559
24775
|
function monthlySearchesValue(rows) {
|
|
24560
24776
|
const histories = rows.map((row) => row.keyword_info?.monthly_searches).filter((value) => value !== null && value !== void 0);
|
|
24561
|
-
if (histories.length === 0) return missing$
|
|
24777
|
+
if (histories.length === 0) return missing$2("monthlySearches");
|
|
24562
24778
|
const byMonth = /* @__PURE__ */ new Map();
|
|
24563
24779
|
let invalid = false;
|
|
24564
24780
|
for (const history of histories) for (const item of history) {
|
|
@@ -24591,27 +24807,27 @@ function resultCountValues(rows) {
|
|
|
24591
24807
|
function metricForKeyword(keyword, rows) {
|
|
24592
24808
|
if (rows.length === 0) return {
|
|
24593
24809
|
keyword,
|
|
24594
|
-
monthlySearchVolume: missing$
|
|
24595
|
-
monthlySearches: missing$
|
|
24596
|
-
searchVolumeUpdatedAt: missing$
|
|
24597
|
-
cpcUsd: missing$
|
|
24598
|
-
paidCompetition: missing$
|
|
24599
|
-
keywordDifficulty: missing$
|
|
24600
|
-
intent: missing$
|
|
24601
|
-
resultCount: missing$
|
|
24810
|
+
monthlySearchVolume: missing$2("monthlySearchVolume"),
|
|
24811
|
+
monthlySearches: missing$2("monthlySearches"),
|
|
24812
|
+
searchVolumeUpdatedAt: missing$2("searchVolumeUpdatedAt"),
|
|
24813
|
+
cpcUsd: missing$2("cpcUsd"),
|
|
24814
|
+
paidCompetition: missing$2("paidCompetition"),
|
|
24815
|
+
keywordDifficulty: missing$2("keywordDifficulty"),
|
|
24816
|
+
intent: missing$2("intent"),
|
|
24817
|
+
resultCount: missing$2("resultCount")
|
|
24602
24818
|
};
|
|
24603
24819
|
return {
|
|
24604
24820
|
keyword,
|
|
24605
24821
|
monthlySearchVolume: numericValue(rows.map((row) => row.keyword_info?.search_volume), "monthlySearchVolume", (value) => Number.isSafeInteger(value) && value >= 0),
|
|
24606
24822
|
monthlySearches: monthlySearchesValue(rows),
|
|
24607
|
-
searchVolumeUpdatedAt: stringValue(rows.map((row) => row.keyword_info?.last_updated_time), "searchVolumeUpdatedAt", (value) => {
|
|
24823
|
+
searchVolumeUpdatedAt: stringValue$1(rows.map((row) => row.keyword_info?.last_updated_time), "searchVolumeUpdatedAt", (value) => {
|
|
24608
24824
|
const timestamp = Date.parse(value);
|
|
24609
24825
|
return Number.isFinite(timestamp) ? new Date(timestamp).toISOString() : null;
|
|
24610
24826
|
}),
|
|
24611
24827
|
cpcUsd: numericValue(rows.map((row) => row.keyword_info?.cpc), "cpcUsd", (value) => Number.isFinite(value) && value >= 0),
|
|
24612
24828
|
paidCompetition: numericValue(rows.map((row) => row.keyword_info?.competition), "paidCompetition", (value) => Number.isFinite(value) && value >= 0 && value <= 1),
|
|
24613
24829
|
keywordDifficulty: numericValue(rows.map((row) => row.keyword_properties?.keyword_difficulty), "keywordDifficulty", (value) => Number.isFinite(value) && value >= 0 && value <= 100),
|
|
24614
|
-
intent: stringValue(rows.map((row) => row.search_intent_info?.main_intent), "intent", (value) => value.trim().toLowerCase() || null),
|
|
24830
|
+
intent: stringValue$1(rows.map((row) => row.search_intent_info?.main_intent), "intent", (value) => value.trim().toLowerCase() || null),
|
|
24615
24831
|
resultCount: numericValue(resultCountValues(rows), "resultCount", (value) => Number.isSafeInteger(value) && value >= 0)
|
|
24616
24832
|
};
|
|
24617
24833
|
}
|
|
@@ -24664,21 +24880,21 @@ const COMPETITOR_RESULT_TYPES = /* @__PURE__ */ new Set([
|
|
|
24664
24880
|
"featured_snippet",
|
|
24665
24881
|
"local_pack"
|
|
24666
24882
|
]);
|
|
24667
|
-
function missing(field) {
|
|
24883
|
+
function missing$1(field) {
|
|
24668
24884
|
return unavailableValue("missing", `DataForSEO omitted ${field}.`);
|
|
24669
24885
|
}
|
|
24670
|
-
function numberValue(value, field) {
|
|
24671
|
-
return value === null || value === void 0 ? missing(field) : observedValue(value);
|
|
24886
|
+
function numberValue$1(value, field) {
|
|
24887
|
+
return value === null || value === void 0 ? missing$1(field) : observedValue(value);
|
|
24672
24888
|
}
|
|
24673
24889
|
function countValue(value, field) {
|
|
24674
|
-
return numberValue(value, field);
|
|
24890
|
+
return numberValue$1(value, field);
|
|
24675
24891
|
}
|
|
24676
24892
|
function sumBuckets(metrics, fields, label) {
|
|
24677
24893
|
const values = fields.map((field) => metrics[field]);
|
|
24678
|
-
return values.some((value) => value === null || value === void 0) ? missing(label) : observedValue(values.reduce((sum, value) => sum + (typeof value === "number" ? value : 0), 0));
|
|
24894
|
+
return values.some((value) => value === null || value === void 0) ? missing$1(label) : observedValue(values.reduce((sum, value) => sum + (typeof value === "number" ? value : 0), 0));
|
|
24679
24895
|
}
|
|
24680
24896
|
function rankingDistribution(metrics) {
|
|
24681
|
-
if (!metrics) return missing("organic ranking distribution");
|
|
24897
|
+
if (!metrics) return missing$1("organic ranking distribution");
|
|
24682
24898
|
const buckets = {
|
|
24683
24899
|
first: sumBuckets(metrics, ["pos_1"], "position 1 rankings"),
|
|
24684
24900
|
top3: sumBuckets(metrics, ["pos_1", "pos_2_3"], "top 3 rankings"),
|
|
@@ -24729,9 +24945,9 @@ function rankingDistribution(metrics) {
|
|
|
24729
24945
|
}
|
|
24730
24946
|
function organicFootprint(metrics) {
|
|
24731
24947
|
return {
|
|
24732
|
-
estimatedMonthlyTraffic: numberValue(metrics?.etv, "estimated organic monthly traffic"),
|
|
24948
|
+
estimatedMonthlyTraffic: numberValue$1(metrics?.etv, "estimated organic monthly traffic"),
|
|
24733
24949
|
rankedKeywords: countValue(metrics?.count, "ranked organic keywords"),
|
|
24734
|
-
estimatedMonthlyTrafficCostUsd: numberValue(metrics?.estimated_paid_traffic_cost, "estimated organic traffic cost"),
|
|
24950
|
+
estimatedMonthlyTrafficCostUsd: numberValue$1(metrics?.estimated_paid_traffic_cost, "estimated organic traffic cost"),
|
|
24735
24951
|
rankings: rankingDistribution(metrics),
|
|
24736
24952
|
newRankings: countValue(metrics?.is_new, "new rankings"),
|
|
24737
24953
|
improvedRankings: countValue(metrics?.is_up, "improved rankings"),
|
|
@@ -24786,7 +25002,7 @@ function researchMarket(input, operation) {
|
|
|
24786
25002
|
});
|
|
24787
25003
|
return parsed;
|
|
24788
25004
|
}
|
|
24789
|
-
function rowLimit(limit, offset = 0) {
|
|
25005
|
+
function rowLimit$1(limit, offset = 0) {
|
|
24790
25006
|
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 1e3) throw new ProviderError({
|
|
24791
25007
|
provider: "dataforseo",
|
|
24792
25008
|
operation: "domain-research",
|
|
@@ -24810,7 +25026,7 @@ function resultTypes(input, allowed) {
|
|
|
24810
25026
|
});
|
|
24811
25027
|
return normalized;
|
|
24812
25028
|
}
|
|
24813
|
-
function safeUrl$
|
|
25029
|
+
function safeUrl$2(value) {
|
|
24814
25030
|
if (!value) return null;
|
|
24815
25031
|
try {
|
|
24816
25032
|
const url = new URL(value);
|
|
@@ -24830,7 +25046,7 @@ function dedupeBy(rows, key) {
|
|
|
24830
25046
|
}
|
|
24831
25047
|
return [...grouped.entries()].sort(([left], [right]) => compareCodepoints$3(left, right)).map(([, matches]) => [...matches].sort((left, right) => compareCodepoints$3(JSON.stringify(left), JSON.stringify(right)))[0]);
|
|
24832
25048
|
}
|
|
24833
|
-
function observedNumber(value) {
|
|
25049
|
+
function observedNumber$1(value) {
|
|
24834
25050
|
return value.state === "observed" ? value.value : -1;
|
|
24835
25051
|
}
|
|
24836
25052
|
function duplicateWarning(available, retained, label) {
|
|
@@ -24841,7 +25057,7 @@ function duplicateWarning(available, retained, label) {
|
|
|
24841
25057
|
message: `${duplicates} duplicate ${label} row${duplicates === 1 ? "" : "s"} were collapsed deterministically.`
|
|
24842
25058
|
}] : [];
|
|
24843
25059
|
}
|
|
24844
|
-
function totalRows(values) {
|
|
25060
|
+
function totalRows$1(values) {
|
|
24845
25061
|
const present = values.filter((value) => value !== null && value !== void 0);
|
|
24846
25062
|
return present.length ? Math.max(...present) : null;
|
|
24847
25063
|
}
|
|
@@ -24857,7 +25073,7 @@ function coverage(input) {
|
|
|
24857
25073
|
nextCursor: hasMore ? String(input.offset + input.returnedRows) : null
|
|
24858
25074
|
};
|
|
24859
25075
|
}
|
|
24860
|
-
function requestContext(reportId, supplied) {
|
|
25076
|
+
function requestContext$1(reportId, supplied) {
|
|
24861
25077
|
return supplied ?? {
|
|
24862
25078
|
reportId,
|
|
24863
25079
|
reportRunId: randomUUID()
|
|
@@ -24906,7 +25122,7 @@ async function dataForSeoDomainOverview(client, input) {
|
|
|
24906
25122
|
languageCode: market.languageCode.split("-")[0],
|
|
24907
25123
|
...location,
|
|
24908
25124
|
refresh: input.refresh,
|
|
24909
|
-
context: requestContext("domain-overview", input.context)
|
|
25125
|
+
context: requestContext$1("domain-overview", input.context)
|
|
24910
25126
|
});
|
|
24911
25127
|
const results = snapshot.response.tasks.flatMap((task) => task.result ?? []);
|
|
24912
25128
|
const matchingResult = results.find((row) => !row.target || domain(row.target) === target);
|
|
@@ -24998,7 +25214,7 @@ async function dataForSeoRankedKeywords(client, input) {
|
|
|
24998
25214
|
const market = researchMarket(input.market, "ranked-keywords");
|
|
24999
25215
|
const target = rankedTarget(input.target);
|
|
25000
25216
|
const offset = input.offset ?? 0;
|
|
25001
|
-
rowLimit(input.limit, offset);
|
|
25217
|
+
rowLimit$1(input.limit, offset);
|
|
25002
25218
|
const types = resultTypes(input.resultTypes, RANKED_RESULT_TYPES);
|
|
25003
25219
|
const excluded = excludedTerms(input);
|
|
25004
25220
|
const requestedFilters = filters$1(input, excluded);
|
|
@@ -25018,7 +25234,7 @@ async function dataForSeoRankedKeywords(client, input) {
|
|
|
25018
25234
|
limit: input.limit,
|
|
25019
25235
|
offset,
|
|
25020
25236
|
refresh: input.refresh,
|
|
25021
|
-
context: requestContext("ranked-keywords", input.context)
|
|
25237
|
+
context: requestContext$1("ranked-keywords", input.context)
|
|
25022
25238
|
});
|
|
25023
25239
|
const responseResults = snapshot.response.tasks.flatMap((task) => task.result ?? []);
|
|
25024
25240
|
const rawRows = responseResults.flatMap((result) => result.items ?? []);
|
|
@@ -25027,7 +25243,7 @@ async function dataForSeoRankedKeywords(client, input) {
|
|
|
25027
25243
|
const keywordData = row.keyword_data;
|
|
25028
25244
|
const serp = row.ranked_serp_element?.serp_item;
|
|
25029
25245
|
const keyword = keywordData?.keyword ? normalizedKeyword$2(keywordData.keyword) : "";
|
|
25030
|
-
const url = safeUrl$
|
|
25246
|
+
const url = safeUrl$2(serp?.url);
|
|
25031
25247
|
if (!keywordData || !keyword || !url || !serp?.rank_group || !serp.rank_absolute || !serp.type) {
|
|
25032
25248
|
invalidRows += 1;
|
|
25033
25249
|
return [];
|
|
@@ -25038,11 +25254,11 @@ async function dataForSeoRankedKeywords(client, input) {
|
|
|
25038
25254
|
rankGroup: serp.rank_group,
|
|
25039
25255
|
rankAbsolute: serp.rank_absolute,
|
|
25040
25256
|
resultType: serp.type,
|
|
25041
|
-
estimatedMonthlyTraffic: numberValue(serp.etv, "estimated keyword traffic")
|
|
25257
|
+
estimatedMonthlyTraffic: numberValue$1(serp.etv, "estimated keyword traffic")
|
|
25042
25258
|
}];
|
|
25043
25259
|
});
|
|
25044
|
-
const rows = dedupeBy(mappedRows, (row) => `${row.keyword}\0${row.url}\0${row.resultType}`).sort((left, right) => observedNumber(right.monthlySearchVolume) - observedNumber(left.monthlySearchVolume) || left.rankGroup - right.rankGroup || compareCodepoints$3(left.keyword, right.keyword) || compareCodepoints$3(left.url, right.url));
|
|
25045
|
-
const providerTotalRows = totalRows(responseResults.map((result) => result.total_count));
|
|
25260
|
+
const rows = dedupeBy(mappedRows, (row) => `${row.keyword}\0${row.url}\0${row.resultType}`).sort((left, right) => observedNumber$1(right.monthlySearchVolume) - observedNumber$1(left.monthlySearchVolume) || left.rankGroup - right.rankGroup || compareCodepoints$3(left.keyword, right.keyword) || compareCodepoints$3(left.url, right.url));
|
|
25261
|
+
const providerTotalRows = totalRows$1(responseResults.map((result) => result.total_count));
|
|
25046
25262
|
return domainEvidence({
|
|
25047
25263
|
capability: "ranked-keywords",
|
|
25048
25264
|
data: {
|
|
@@ -25115,7 +25331,7 @@ async function dataForSeoRankingPages(client, input) {
|
|
|
25115
25331
|
const market = researchMarket(input.market, "ranking-pages");
|
|
25116
25332
|
const target = domain(input.domain);
|
|
25117
25333
|
const offset = input.offset ?? 0;
|
|
25118
|
-
rowLimit(input.limit, offset);
|
|
25334
|
+
rowLimit$1(input.limit, offset);
|
|
25119
25335
|
const requestedFilters = filters(input);
|
|
25120
25336
|
const orderBy = ["metrics.organic.etv,desc", "page_address,asc"];
|
|
25121
25337
|
const snapshot = await client.rankingPages({
|
|
@@ -25127,13 +25343,13 @@ async function dataForSeoRankingPages(client, input) {
|
|
|
25127
25343
|
limit: input.limit,
|
|
25128
25344
|
offset,
|
|
25129
25345
|
refresh: input.refresh,
|
|
25130
|
-
context: requestContext("ranking-pages", input.context)
|
|
25346
|
+
context: requestContext$1("ranking-pages", input.context)
|
|
25131
25347
|
});
|
|
25132
25348
|
const responseResults = snapshot.response.tasks.flatMap((task) => task.result ?? []);
|
|
25133
25349
|
const rawRows = responseResults.flatMap((result) => result.items ?? []);
|
|
25134
25350
|
let invalidRows = 0;
|
|
25135
25351
|
const mappedRows = rawRows.flatMap((row) => {
|
|
25136
|
-
const url = safeUrl$
|
|
25352
|
+
const url = safeUrl$2(row.page_address);
|
|
25137
25353
|
if (!url) {
|
|
25138
25354
|
invalidRows += 1;
|
|
25139
25355
|
return [];
|
|
@@ -25143,8 +25359,8 @@ async function dataForSeoRankingPages(client, input) {
|
|
|
25143
25359
|
organic: organicFootprint(row.metrics?.organic)
|
|
25144
25360
|
}];
|
|
25145
25361
|
});
|
|
25146
|
-
const rows = dedupeBy(mappedRows, (row) => row.url).sort((left, right) => observedNumber(right.organic.estimatedMonthlyTraffic) - observedNumber(left.organic.estimatedMonthlyTraffic) || compareCodepoints$3(left.url, right.url));
|
|
25147
|
-
const providerTotalRows = totalRows(responseResults.map((result) => result.total_count));
|
|
25362
|
+
const rows = dedupeBy(mappedRows, (row) => row.url).sort((left, right) => observedNumber$1(right.organic.estimatedMonthlyTraffic) - observedNumber$1(left.organic.estimatedMonthlyTraffic) || compareCodepoints$3(left.url, right.url));
|
|
25363
|
+
const providerTotalRows = totalRows$1(responseResults.map((result) => result.total_count));
|
|
25148
25364
|
return domainEvidence({
|
|
25149
25365
|
capability: "relevant-pages",
|
|
25150
25366
|
data: {
|
|
@@ -25189,7 +25405,7 @@ function keywords(input) {
|
|
|
25189
25405
|
async function dataForSeoSerpCompetitors(client, input) {
|
|
25190
25406
|
const market = researchMarket(input.market, "serp-competitors");
|
|
25191
25407
|
const offset = input.offset ?? 0;
|
|
25192
|
-
rowLimit(input.limit, offset);
|
|
25408
|
+
rowLimit$1(input.limit, offset);
|
|
25193
25409
|
const requestedKeywords = keywords(input.keywords);
|
|
25194
25410
|
const types = resultTypes(input.resultTypes, COMPETITOR_RESULT_TYPES);
|
|
25195
25411
|
const orderBy = [
|
|
@@ -25207,7 +25423,7 @@ async function dataForSeoSerpCompetitors(client, input) {
|
|
|
25207
25423
|
limit: input.limit,
|
|
25208
25424
|
offset,
|
|
25209
25425
|
refresh: input.refresh,
|
|
25210
|
-
context: requestContext("serp-competitors", input.context)
|
|
25426
|
+
context: requestContext$1("serp-competitors", input.context)
|
|
25211
25427
|
});
|
|
25212
25428
|
const responseResults = snapshot.response.tasks.flatMap((task) => task.result ?? []);
|
|
25213
25429
|
const rawRows = responseResults.flatMap((result) => result.items ?? []);
|
|
@@ -25230,16 +25446,16 @@ async function dataForSeoSerpCompetitors(client, input) {
|
|
|
25230
25446
|
return [{
|
|
25231
25447
|
domain: normalizedDomain,
|
|
25232
25448
|
matchedKeywords: row.keywords_count,
|
|
25233
|
-
averagePosition: numberValue(row.avg_position, "average position"),
|
|
25234
|
-
medianPosition: numberValue(row.median_position, "median position"),
|
|
25235
|
-
visibility: numberValue(row.visibility, "visibility"),
|
|
25236
|
-
estimatedMonthlyTraffic: numberValue(row.etv, "estimated monthly traffic"),
|
|
25449
|
+
averagePosition: numberValue$1(row.avg_position, "average position"),
|
|
25450
|
+
medianPosition: numberValue$1(row.median_position, "median position"),
|
|
25451
|
+
visibility: numberValue$1(row.visibility, "visibility"),
|
|
25452
|
+
estimatedMonthlyTraffic: numberValue$1(row.etv, "estimated monthly traffic"),
|
|
25237
25453
|
relevantResults: countValue(row.relevant_serp_items, "relevant result count"),
|
|
25238
25454
|
keywordPositions
|
|
25239
25455
|
}];
|
|
25240
25456
|
});
|
|
25241
|
-
const rows = dedupeBy(mappedRows, (row) => row.domain).sort((left, right) => observedNumber(right.visibility) - observedNumber(left.visibility) || right.matchedKeywords - left.matchedKeywords || compareCodepoints$3(left.domain, right.domain));
|
|
25242
|
-
const providerTotalRows = totalRows(responseResults.map((result) => result.total_count));
|
|
25457
|
+
const rows = dedupeBy(mappedRows, (row) => row.domain).sort((left, right) => observedNumber$1(right.visibility) - observedNumber$1(left.visibility) || right.matchedKeywords - left.matchedKeywords || compareCodepoints$3(left.domain, right.domain));
|
|
25458
|
+
const providerTotalRows = totalRows$1(responseResults.map((result) => result.total_count));
|
|
25243
25459
|
return domainEvidence({
|
|
25244
25460
|
capability: "serp-competitors",
|
|
25245
25461
|
data: {
|
|
@@ -26154,7 +26370,7 @@ function analyzeCompetitorGap(acquisition) {
|
|
|
26154
26370
|
}
|
|
26155
26371
|
//#endregion
|
|
26156
26372
|
//#region packages/core/src/analyze/domain-research/gap-report.ts
|
|
26157
|
-
const MAX_FINDINGS = 10;
|
|
26373
|
+
const MAX_FINDINGS$2 = 10;
|
|
26158
26374
|
const MAX_SOURCE_BRIEFS = 5;
|
|
26159
26375
|
function count(candidates, classification) {
|
|
26160
26376
|
return candidates.filter((item) => item.classification === classification).length;
|
|
@@ -26169,7 +26385,7 @@ function dataStatus(input) {
|
|
|
26169
26385
|
function findings$2(input) {
|
|
26170
26386
|
const result = [];
|
|
26171
26387
|
for (const [index, candidate] of input.candidates.entries()) {
|
|
26172
|
-
if (result.length >= MAX_FINDINGS) break;
|
|
26388
|
+
if (result.length >= MAX_FINDINGS$2) break;
|
|
26173
26389
|
if (candidate.classification === "relevant-gap-candidate") result.push({
|
|
26174
26390
|
code: "relevant-competitor-gap",
|
|
26175
26391
|
evidenceRefs: [`candidates[${index}]`, ...candidate.evidenceRefs],
|
|
@@ -26184,7 +26400,7 @@ function findings$2(input) {
|
|
|
26184
26400
|
});
|
|
26185
26401
|
}
|
|
26186
26402
|
for (const [index, pattern] of input.patterns.entries()) {
|
|
26187
|
-
if (result.length >= MAX_FINDINGS) break;
|
|
26403
|
+
if (result.length >= MAX_FINDINGS$2) break;
|
|
26188
26404
|
result.push({
|
|
26189
26405
|
code: "competitor-template-pattern",
|
|
26190
26406
|
evidenceRefs: [`repeatedCompetitorPatterns[${index}]`, ...pattern.evidenceRefs],
|
|
@@ -27231,7 +27447,7 @@ function measurementWindow(input) {
|
|
|
27231
27447
|
}
|
|
27232
27448
|
//#endregion
|
|
27233
27449
|
//#region packages/core/src/analyze/experiments/measure-change.ts
|
|
27234
|
-
const defaultDependencies$
|
|
27450
|
+
const defaultDependencies$6 = {
|
|
27235
27451
|
searchAnalytics: querySearchAnalytics,
|
|
27236
27452
|
googleAnalyticsReport: runGa4Report,
|
|
27237
27453
|
contentGroup: getContentGroup,
|
|
@@ -27249,7 +27465,7 @@ function counterfactualDelta(input) {
|
|
|
27249
27465
|
const expectedAfter = input.treatmentBefore * (input.controlAfter / input.controlBefore);
|
|
27250
27466
|
return fixed(input.treatmentAfter - expectedAfter);
|
|
27251
27467
|
}
|
|
27252
|
-
async function measureChange(input, dependencies = defaultDependencies$
|
|
27468
|
+
async function measureChange(input, dependencies = defaultDependencies$6) {
|
|
27253
27469
|
const now = dependencies.now();
|
|
27254
27470
|
const change = (input.id ? getChange(input.id) : void 0) ?? {
|
|
27255
27471
|
id: "adhoc",
|
|
@@ -27974,14 +28190,14 @@ async function verifyInternalLinkCandidate(input) {
|
|
|
27974
28190
|
}
|
|
27975
28191
|
//#endregion
|
|
27976
28192
|
//#region packages/core/src/analyze/internal-links.ts
|
|
27977
|
-
const MAX_GSC_ROWS$
|
|
27978
|
-
const defaultDependencies$
|
|
28193
|
+
const MAX_GSC_ROWS$2 = 1e5;
|
|
28194
|
+
const defaultDependencies$5 = {
|
|
27979
28195
|
searchAnalytics: querySearchAnalytics,
|
|
27980
28196
|
fetch: fetchPage,
|
|
27981
28197
|
extract: extractPage,
|
|
27982
28198
|
now: () => /* @__PURE__ */ new Date()
|
|
27983
28199
|
};
|
|
27984
|
-
async function internalLinksReport(input, dependencies = defaultDependencies$
|
|
28200
|
+
async function internalLinksReport(input, dependencies = defaultDependencies$5) {
|
|
27985
28201
|
const targetPage = new URL(assertUrlMatchesGscProperty(input.site, input.targetUrl));
|
|
27986
28202
|
targetPage.hash = "";
|
|
27987
28203
|
const targetUrl = targetPage.toString();
|
|
@@ -28030,7 +28246,7 @@ async function internalLinksReport(input, dependencies = defaultDependencies$4)
|
|
|
28030
28246
|
dimensions: ["query", "page"],
|
|
28031
28247
|
type: "web",
|
|
28032
28248
|
dataState: "final",
|
|
28033
|
-
maxRows: MAX_GSC_ROWS$
|
|
28249
|
+
maxRows: MAX_GSC_ROWS$2,
|
|
28034
28250
|
dimensionFilterGroups: [{
|
|
28035
28251
|
groupType: "and",
|
|
28036
28252
|
filters: [{
|
|
@@ -28062,7 +28278,7 @@ async function internalLinksReport(input, dependencies = defaultDependencies$4)
|
|
|
28062
28278
|
dimensions: ["query", "page"],
|
|
28063
28279
|
type: "web",
|
|
28064
28280
|
dataState: "final",
|
|
28065
|
-
maxRows: MAX_GSC_ROWS$
|
|
28281
|
+
maxRows: MAX_GSC_ROWS$2
|
|
28066
28282
|
}, { refresh: input.refresh }) : {
|
|
28067
28283
|
rows: [],
|
|
28068
28284
|
calls: 0,
|
|
@@ -28107,8 +28323,8 @@ async function internalLinksReport(input, dependencies = defaultDependencies$4)
|
|
|
28107
28323
|
else checkedSources += 1;
|
|
28108
28324
|
}
|
|
28109
28325
|
const uncheckedCandidates = Math.max(0, analysis.candidates.length - attemptedSources);
|
|
28110
|
-
const targetTruncated = targetRequests.some(({ result }) => result.rowsFetched >= MAX_GSC_ROWS$
|
|
28111
|
-
const sourceTruncated = sourceResult.rowsFetched >= MAX_GSC_ROWS$
|
|
28326
|
+
const targetTruncated = targetRequests.some(({ result }) => result.rowsFetched >= MAX_GSC_ROWS$2);
|
|
28327
|
+
const sourceTruncated = sourceResult.rowsFetched >= MAX_GSC_ROWS$2;
|
|
28112
28328
|
const invalidRows = analysis.selection.targetInvalidRows + analysis.selection.sourceInvalidRows;
|
|
28113
28329
|
const structuredWarnings = uniqueInternalLinksWarnings(warnings);
|
|
28114
28330
|
const dataStatus = target.verification === "technical-issue" ? "target-technical-issue" : target.verification === "failed" ? "partial" : targetRows.length === 0 ? "empty" : targetTruncated || sourceTruncated || invalidRows > 0 ? "partial" : queryCandidates && sourceResult.rows.length === 0 ? "source-empty" : analysis.selection.targetEligibleQueries === 0 || analysis.candidates.length === 0 ? "filtered" : failedChecks > 0 || uncheckedCandidates > 0 ? "partial" : "complete";
|
|
@@ -28149,18 +28365,18 @@ async function internalLinksReport(input, dependencies = defaultDependencies$4)
|
|
|
28149
28365
|
pageFilter: alias,
|
|
28150
28366
|
rowsFetched: result.rowsFetched,
|
|
28151
28367
|
calls: result.calls,
|
|
28152
|
-
possiblyTruncated: result.rowsFetched >= MAX_GSC_ROWS$
|
|
28368
|
+
possiblyTruncated: result.rowsFetched >= MAX_GSC_ROWS$2
|
|
28153
28369
|
})),
|
|
28154
28370
|
rowsFetched: targetRequests.reduce((sum, { result }) => sum + result.rowsFetched, 0),
|
|
28155
28371
|
calls: targetRequests.reduce((sum, { result }) => sum + result.calls, 0),
|
|
28156
|
-
maxRowsPerRequest: MAX_GSC_ROWS$
|
|
28372
|
+
maxRowsPerRequest: MAX_GSC_ROWS$2,
|
|
28157
28373
|
possiblyTruncated: targetTruncated
|
|
28158
28374
|
},
|
|
28159
28375
|
candidates: {
|
|
28160
28376
|
queried: queryCandidates,
|
|
28161
28377
|
rowsFetched: sourceResult.rowsFetched,
|
|
28162
28378
|
calls: sourceResult.calls,
|
|
28163
|
-
maxRows: MAX_GSC_ROWS$
|
|
28379
|
+
maxRows: MAX_GSC_ROWS$2,
|
|
28164
28380
|
possiblyTruncated: sourceTruncated
|
|
28165
28381
|
},
|
|
28166
28382
|
completeness: !queryCandidates ? "not-queried" : targetTruncated || sourceTruncated ? "possibly-truncated" : "retained-rows-only"
|
|
@@ -28179,7 +28395,7 @@ async function internalLinksReport(input, dependencies = defaultDependencies$4)
|
|
|
28179
28395
|
minImpressions,
|
|
28180
28396
|
resultLimit: limit,
|
|
28181
28397
|
checkLimit,
|
|
28182
|
-
maxGscRowsPerRequest: MAX_GSC_ROWS$
|
|
28398
|
+
maxGscRowsPerRequest: MAX_GSC_ROWS$2
|
|
28183
28399
|
},
|
|
28184
28400
|
target: {
|
|
28185
28401
|
requestedUrl: target.requestedUrl,
|
|
@@ -29119,8 +29335,8 @@ function analyzeSecondPageRows(input) {
|
|
|
29119
29335
|
}
|
|
29120
29336
|
//#endregion
|
|
29121
29337
|
//#region packages/core/src/analyze/keyword-opportunities.ts
|
|
29122
|
-
const DEFAULT_DAYS = 28;
|
|
29123
|
-
const MAX_DAYS = 548;
|
|
29338
|
+
const DEFAULT_DAYS$1 = 28;
|
|
29339
|
+
const MAX_DAYS$1 = 548;
|
|
29124
29340
|
const DEFAULT_REPORT_LIMIT = 10;
|
|
29125
29341
|
const MAX_REPORT_LIMIT = 25;
|
|
29126
29342
|
const DEFAULT_KEYWORD_LIMIT = 30;
|
|
@@ -29167,9 +29383,9 @@ function validateInput$2(input) {
|
|
|
29167
29383
|
site,
|
|
29168
29384
|
days: integerOption$1({
|
|
29169
29385
|
value: input.days,
|
|
29170
|
-
fallback: DEFAULT_DAYS,
|
|
29386
|
+
fallback: DEFAULT_DAYS$1,
|
|
29171
29387
|
minimum: 1,
|
|
29172
|
-
maximum: MAX_DAYS,
|
|
29388
|
+
maximum: MAX_DAYS$1,
|
|
29173
29389
|
label: "Days"
|
|
29174
29390
|
}),
|
|
29175
29391
|
limit: integerOption$1({
|
|
@@ -30707,7 +30923,7 @@ function analyzeIndexCoverageSignals(input) {
|
|
|
30707
30923
|
const MAX_SITEMAP_DOCUMENTS = 20;
|
|
30708
30924
|
const MAX_SEARCH_ROWS = 25e4;
|
|
30709
30925
|
const MAX_SITEMAP_URLS = 25e4;
|
|
30710
|
-
const defaultDependencies$
|
|
30926
|
+
const defaultDependencies$4 = {
|
|
30711
30927
|
loadCrawl: loadCrawlReport,
|
|
30712
30928
|
latestCrawl: latestCrawlReport,
|
|
30713
30929
|
searchAnalytics: querySearchAnalytics,
|
|
@@ -30739,7 +30955,7 @@ function sitemapCompleteness(input) {
|
|
|
30739
30955
|
if (input.inventoryTruncated) return "truncated";
|
|
30740
30956
|
return input.results.every((result) => result.dataStatus === "complete") ? "complete" : "partial";
|
|
30741
30957
|
}
|
|
30742
|
-
async function indexCoverageSignals(input, dependencies = defaultDependencies$
|
|
30958
|
+
async function indexCoverageSignals(input, dependencies = defaultDependencies$4) {
|
|
30743
30959
|
const days = integerOption$2({
|
|
30744
30960
|
value: input.days,
|
|
30745
30961
|
fallback: 90,
|
|
@@ -31223,7 +31439,7 @@ function pruneIndexWatchSnapshots(rootSite, url, database = getDb()) {
|
|
|
31223
31439
|
WHERE attempts.attempt_rank > ? AND latest_success.id IS NULL
|
|
31224
31440
|
)`).run(rootSite, url, rootSite, url, 20).changes;
|
|
31225
31441
|
}
|
|
31226
|
-
const defaultDependencies$
|
|
31442
|
+
const defaultDependencies$3 = {
|
|
31227
31443
|
inspectUrl,
|
|
31228
31444
|
now: () => /* @__PURE__ */ new Date(),
|
|
31229
31445
|
store: {
|
|
@@ -31268,7 +31484,7 @@ function isFatalBatchError(error) {
|
|
|
31268
31484
|
"AUTH_REQUIRED"
|
|
31269
31485
|
].includes(error.code);
|
|
31270
31486
|
}
|
|
31271
|
-
async function indexWatch(input, dependencies = defaultDependencies$
|
|
31487
|
+
async function indexWatch(input, dependencies = defaultDependencies$3) {
|
|
31272
31488
|
const languageCode = validateLanguageCode(input.languageCode);
|
|
31273
31489
|
const dailyLimit = integerOption$2({
|
|
31274
31490
|
value: input.dailyLimit,
|
|
@@ -33450,9 +33666,9 @@ function pseoSampleUrls(report) {
|
|
|
33450
33666
|
}
|
|
33451
33667
|
//#endregion
|
|
33452
33668
|
//#region packages/core/src/analyze/pseo/audit.ts
|
|
33453
|
-
const MAX_GSC_ROWS = 5e4;
|
|
33669
|
+
const MAX_GSC_ROWS$1 = 5e4;
|
|
33454
33670
|
const DEFAULT_SITEMAP_URLS = 5e4;
|
|
33455
|
-
const defaultDependencies$
|
|
33671
|
+
const defaultDependencies$2 = {
|
|
33456
33672
|
searchAnalytics: querySearchAnalytics,
|
|
33457
33673
|
fetchPage,
|
|
33458
33674
|
inspectUrl,
|
|
@@ -33654,7 +33870,7 @@ async function inspectSample(input) {
|
|
|
33654
33870
|
};
|
|
33655
33871
|
}
|
|
33656
33872
|
}
|
|
33657
|
-
async function acquirePseoAuditEvidence(input, dependencies = defaultDependencies$
|
|
33873
|
+
async function acquirePseoAuditEvidence(input, dependencies = defaultDependencies$2) {
|
|
33658
33874
|
const options = pseoAuditOptions(input);
|
|
33659
33875
|
const now = dependencies.now();
|
|
33660
33876
|
const generatedAt = now.toISOString();
|
|
@@ -33673,7 +33889,7 @@ async function acquirePseoAuditEvidence(input, dependencies = defaultDependencie
|
|
|
33673
33889
|
type: "web",
|
|
33674
33890
|
dataState: "final",
|
|
33675
33891
|
aggregationType: "auto",
|
|
33676
|
-
maxRows: MAX_GSC_ROWS
|
|
33892
|
+
maxRows: MAX_GSC_ROWS$1
|
|
33677
33893
|
};
|
|
33678
33894
|
input.progress?.(`Fetching ${options.days} days of GSC page and query/page rows`);
|
|
33679
33895
|
const [pageResponse, queryPageResponse] = await Promise.all([dependencies.searchAnalytics(input.site, {
|
|
@@ -33724,7 +33940,7 @@ function buildPseoAuditFromAcquisition(input, acquisition, evidence) {
|
|
|
33724
33940
|
minimumTemplateImpressions: options.minimumTemplateImpressions,
|
|
33725
33941
|
crawlSamplesPerTemplate: options.crawlSamples,
|
|
33726
33942
|
inspectionSamplesPerTemplate: options.inspectSamples,
|
|
33727
|
-
maxRowsPerRequest: MAX_GSC_ROWS,
|
|
33943
|
+
maxRowsPerRequest: MAX_GSC_ROWS$1,
|
|
33728
33944
|
pageRowsFetched: acquisition.pageRowsFetched,
|
|
33729
33945
|
queryPageRowsFetched: acquisition.queryPageRowsFetched,
|
|
33730
33946
|
sitemapsRequested: input.sitemaps?.length ?? 0,
|
|
@@ -33735,7 +33951,7 @@ function buildPseoAuditFromAcquisition(input, acquisition, evidence) {
|
|
|
33735
33951
|
caveats: [`Data freshness: ${input.refresh ? "fresh fetch requested; caches bypassed where supported" : "local cache allowed; use --refresh to bypass cached provider and HTTP data"}.`, input.sitemaps?.length ? `Sitemaps: ${countLabel(input.sitemaps.length, "sitemap URL")} requested.` : "Sitemaps: none provided; template discovery used retained GSC page rows."]
|
|
33736
33952
|
});
|
|
33737
33953
|
}
|
|
33738
|
-
async function pseoAuditReport(input, dependencies = defaultDependencies$
|
|
33954
|
+
async function pseoAuditReport(input, dependencies = defaultDependencies$2) {
|
|
33739
33955
|
const acquisition = await acquirePseoAuditEvidence(input, dependencies);
|
|
33740
33956
|
const sampleUrls = pseoSampleUrls(buildPseoAuditFromAcquisition(input, acquisition));
|
|
33741
33957
|
const topQueries = topQueryByUrl(acquisition.queryPageRows, input);
|
|
@@ -34577,7 +34793,7 @@ function pseoOpportunityVerdict(input) {
|
|
|
34577
34793
|
//#region packages/core/src/providers/dataforseo/serp-snapshot.ts
|
|
34578
34794
|
const MAX_SERP_DEPTH = 100;
|
|
34579
34795
|
const SERP_ENDPOINT = "v3/serp/google/organic/live/advanced";
|
|
34580
|
-
function safeUrl(value) {
|
|
34796
|
+
function safeUrl$1(value) {
|
|
34581
34797
|
if (!value) return null;
|
|
34582
34798
|
try {
|
|
34583
34799
|
const parsed = new URL(value);
|
|
@@ -34590,11 +34806,11 @@ function safeUrl(value) {
|
|
|
34590
34806
|
}
|
|
34591
34807
|
}
|
|
34592
34808
|
function validOrganic(item) {
|
|
34593
|
-
return item.type === "organic" && Number.isSafeInteger(item.rank_group) && (item.rank_group ?? 0) > 0 && Number.isSafeInteger(item.rank_absolute) && (item.rank_absolute ?? 0) > 0 && Number.isSafeInteger(item.page) && (item.page ?? 0) > 0 && Boolean(item.domain?.trim()) && safeUrl(item.url) !== null;
|
|
34809
|
+
return item.type === "organic" && Number.isSafeInteger(item.rank_group) && (item.rank_group ?? 0) > 0 && Number.isSafeInteger(item.rank_absolute) && (item.rank_absolute ?? 0) > 0 && Number.isSafeInteger(item.page) && (item.page ?? 0) > 0 && Boolean(item.domain?.trim()) && safeUrl$1(item.url) !== null;
|
|
34594
34810
|
}
|
|
34595
34811
|
function organicResult(item) {
|
|
34596
34812
|
if (!validOrganic(item)) return null;
|
|
34597
|
-
const url = safeUrl(item.url);
|
|
34813
|
+
const url = safeUrl$1(item.url);
|
|
34598
34814
|
if (!url) return null;
|
|
34599
34815
|
return {
|
|
34600
34816
|
rankGroup: item.rank_group,
|
|
@@ -34643,7 +34859,7 @@ function mapDataForSeoSerpSnapshot(input, snapshot, endpoint = SERP_ENDPOINT) {
|
|
|
34643
34859
|
field: "checkedAt",
|
|
34644
34860
|
message: "DataForSEO returned an invalid SERP observation time; the local response time was retained."
|
|
34645
34861
|
});
|
|
34646
|
-
const checkUrl = safeUrl(result.check_url);
|
|
34862
|
+
const checkUrl = safeUrl$1(result.check_url);
|
|
34647
34863
|
if (result.check_url && !checkUrl) warnings.push({
|
|
34648
34864
|
code: "invalid-serp-check-url",
|
|
34649
34865
|
field: "checkUrl",
|
|
@@ -37191,7 +37407,7 @@ function groupedMappings(evidence) {
|
|
|
37191
37407
|
//#endregion
|
|
37192
37408
|
//#region packages/core/src/analyze/second-page.ts
|
|
37193
37409
|
const MAX_SOURCE_ROWS = 1e5;
|
|
37194
|
-
const defaultDependencies = {
|
|
37410
|
+
const defaultDependencies$1 = {
|
|
37195
37411
|
searchAnalytics: querySearchAnalytics,
|
|
37196
37412
|
fetch: fetchPage,
|
|
37197
37413
|
extract: extractPage,
|
|
@@ -37268,7 +37484,7 @@ function reportRecommendations(items) {
|
|
|
37268
37484
|
"Treat the priority score as a triage heuristic, not an estimated traffic lift."
|
|
37269
37485
|
]);
|
|
37270
37486
|
}
|
|
37271
|
-
async function secondPage(input, dependencies = defaultDependencies) {
|
|
37487
|
+
async function secondPage(input, dependencies = defaultDependencies$1) {
|
|
37272
37488
|
const days = integerOption({
|
|
37273
37489
|
value: input.range,
|
|
37274
37490
|
fallback: 28,
|
|
@@ -40434,10 +40650,10 @@ const DEFAULT_TARGET_LIMIT = 20;
|
|
|
40434
40650
|
const MAX_TARGET_LIMIT = 50;
|
|
40435
40651
|
const DEFAULT_DETAIL_PAGES = 1;
|
|
40436
40652
|
const MAX_DETAIL_PAGES = 3;
|
|
40437
|
-
const DEFAULT_ROW_LIMIT$
|
|
40438
|
-
const MAX_ROW_LIMIT$
|
|
40653
|
+
const DEFAULT_ROW_LIMIT$3 = 500;
|
|
40654
|
+
const MAX_ROW_LIMIT$3 = 1e3;
|
|
40439
40655
|
const MAX_CONCURRENCY = 3;
|
|
40440
|
-
function integer(value, fallback, max, name) {
|
|
40656
|
+
function integer$1(value, fallback, max, name) {
|
|
40441
40657
|
const resolved = value ?? fallback;
|
|
40442
40658
|
if (!Number.isInteger(resolved) || resolved < 1 || resolved > max) throw new SeoError("INVALID_INPUT", `${name} must be between 1 and ${max}.`);
|
|
40443
40659
|
return resolved;
|
|
@@ -40498,9 +40714,9 @@ async function targetDetails(input) {
|
|
|
40498
40714
|
}
|
|
40499
40715
|
async function collectBingLinkEvidence(input) {
|
|
40500
40716
|
if (!input.site.trim()) throw new SeoError("INVALID_INPUT", "Pass a Bing Webmaster site URL.");
|
|
40501
|
-
const targetLimit = integer(input.targetLimit, DEFAULT_TARGET_LIMIT, MAX_TARGET_LIMIT, "Bing link target limit");
|
|
40502
|
-
const detailPages = integer(input.detailPagesPerTarget, DEFAULT_DETAIL_PAGES, MAX_DETAIL_PAGES, "Bing detail page limit");
|
|
40503
|
-
const rowLimit = integer(input.rowLimit, DEFAULT_ROW_LIMIT$
|
|
40717
|
+
const targetLimit = integer$1(input.targetLimit, DEFAULT_TARGET_LIMIT, MAX_TARGET_LIMIT, "Bing link target limit");
|
|
40718
|
+
const detailPages = integer$1(input.detailPagesPerTarget, DEFAULT_DETAIL_PAGES, MAX_DETAIL_PAGES, "Bing detail page limit");
|
|
40719
|
+
const rowLimit = integer$1(input.rowLimit, DEFAULT_ROW_LIMIT$3, MAX_ROW_LIMIT$3, "Bing link row limit");
|
|
40504
40720
|
const client = input.client ?? (await createBingWebmasterClient()).client;
|
|
40505
40721
|
const countRows = [];
|
|
40506
40722
|
let countPagesRequested = 0;
|
|
@@ -40621,6 +40837,884 @@ async function collectBingLinkEvidence(input) {
|
|
|
40621
40837
|
};
|
|
40622
40838
|
}
|
|
40623
40839
|
//#endregion
|
|
40840
|
+
//#region packages/core/src/links/context-sources.ts
|
|
40841
|
+
function urlKey(value) {
|
|
40842
|
+
try {
|
|
40843
|
+
const url = new URL(value);
|
|
40844
|
+
if (!["http:", "https:"].includes(url.protocol)) return null;
|
|
40845
|
+
url.hash = "";
|
|
40846
|
+
return url.toString();
|
|
40847
|
+
} catch {
|
|
40848
|
+
return null;
|
|
40849
|
+
}
|
|
40850
|
+
}
|
|
40851
|
+
function compareTargets(left, right) {
|
|
40852
|
+
return right.observedLinks - left.observedLinks || (left.targetUrl < right.targetUrl ? -1 : left.targetUrl > right.targetUrl ? 1 : 0);
|
|
40853
|
+
}
|
|
40854
|
+
function linkTargetCounts(evidence) {
|
|
40855
|
+
if (evidence.targetCounts.length) return [...evidence.targetCounts].sort(compareTargets);
|
|
40856
|
+
const counts = /* @__PURE__ */ new Map();
|
|
40857
|
+
for (const row of evidence.rows) counts.set(row.targetUrl, (counts.get(row.targetUrl) ?? 0) + 1);
|
|
40858
|
+
return [...counts].map(([targetUrl, observedLinks]) => ({
|
|
40859
|
+
targetUrl,
|
|
40860
|
+
observedLinks
|
|
40861
|
+
})).sort(compareTargets);
|
|
40862
|
+
}
|
|
40863
|
+
function crawlForLinkTargets(input) {
|
|
40864
|
+
const values = /* @__PURE__ */ new Map();
|
|
40865
|
+
if (!input.report) {
|
|
40866
|
+
for (const target of input.targets) values.set(target.targetUrl, {
|
|
40867
|
+
state: "unavailable",
|
|
40868
|
+
reason: "No matching saved crawl was available."
|
|
40869
|
+
});
|
|
40870
|
+
return {
|
|
40871
|
+
values,
|
|
40872
|
+
matched: 0
|
|
40873
|
+
};
|
|
40874
|
+
}
|
|
40875
|
+
const pages = /* @__PURE__ */ new Map();
|
|
40876
|
+
for (const page of input.report.pages) for (const value of [page.url, page.finalUrl]) {
|
|
40877
|
+
const key = urlKey(value);
|
|
40878
|
+
if (key && !pages.has(key)) pages.set(key, page);
|
|
40879
|
+
}
|
|
40880
|
+
const issues = /* @__PURE__ */ new Map();
|
|
40881
|
+
for (const issue of input.report.issues) {
|
|
40882
|
+
const key = urlKey(issue.url);
|
|
40883
|
+
if (!key) continue;
|
|
40884
|
+
const ids = issues.get(key) ?? /* @__PURE__ */ new Set();
|
|
40885
|
+
ids.add(issue.ruleId);
|
|
40886
|
+
issues.set(key, ids);
|
|
40887
|
+
}
|
|
40888
|
+
let matched = 0;
|
|
40889
|
+
for (const target of input.targets) {
|
|
40890
|
+
const key = urlKey(target.targetUrl);
|
|
40891
|
+
const page = key ? pages.get(key) : void 0;
|
|
40892
|
+
if (!page) {
|
|
40893
|
+
values.set(target.targetUrl, {
|
|
40894
|
+
state: "not-observed",
|
|
40895
|
+
reason: "The target URL was not present in the retained crawl pages."
|
|
40896
|
+
});
|
|
40897
|
+
continue;
|
|
40898
|
+
}
|
|
40899
|
+
matched += 1;
|
|
40900
|
+
values.set(target.targetUrl, {
|
|
40901
|
+
state: "observed",
|
|
40902
|
+
reportId: input.report.id,
|
|
40903
|
+
observedAt: input.report.generatedAt,
|
|
40904
|
+
status: page.status,
|
|
40905
|
+
finalUrl: page.finalUrl,
|
|
40906
|
+
indexable: page.indexable,
|
|
40907
|
+
canonical: page.canonical ?? null,
|
|
40908
|
+
issueIds: [...issues.get(key) ?? []].sort()
|
|
40909
|
+
});
|
|
40910
|
+
}
|
|
40911
|
+
return {
|
|
40912
|
+
values,
|
|
40913
|
+
matched
|
|
40914
|
+
};
|
|
40915
|
+
}
|
|
40916
|
+
function aggregateLinkSearchRows(rows) {
|
|
40917
|
+
const values = /* @__PURE__ */ new Map();
|
|
40918
|
+
for (const row of rows) {
|
|
40919
|
+
const page = row.keys[0];
|
|
40920
|
+
const key = page ? urlKey(page) : null;
|
|
40921
|
+
if (!key) continue;
|
|
40922
|
+
const current = values.get(key) ?? {
|
|
40923
|
+
clicks: 0,
|
|
40924
|
+
impressions: 0,
|
|
40925
|
+
positionWeight: 0
|
|
40926
|
+
};
|
|
40927
|
+
current.clicks += row.clicks;
|
|
40928
|
+
current.impressions += row.impressions;
|
|
40929
|
+
current.positionWeight += row.position * row.impressions;
|
|
40930
|
+
values.set(key, current);
|
|
40931
|
+
}
|
|
40932
|
+
return values;
|
|
40933
|
+
}
|
|
40934
|
+
//#endregion
|
|
40935
|
+
//#region packages/core/src/links/context-findings.ts
|
|
40936
|
+
const MAX_FINDINGS$1 = 50;
|
|
40937
|
+
function findingPriority(input) {
|
|
40938
|
+
const searchValue = input.search.state === "observed" && (input.search.clicks > 0 || input.search.impressions >= 100);
|
|
40939
|
+
if (input.code === "linked-broken-target" || searchValue) return "high";
|
|
40940
|
+
if (input.observedLinks >= 5) return "medium";
|
|
40941
|
+
return "low";
|
|
40942
|
+
}
|
|
40943
|
+
function finding(input) {
|
|
40944
|
+
const crawl = input.row.crawl;
|
|
40945
|
+
if (crawl.state !== "observed") throw new Error("A link finding requires observed crawl evidence.");
|
|
40946
|
+
const searchEvidence = input.row.searchConsole.state === "observed" ? ` Search Console retained ${input.row.searchConsole.clicks.toFixed(0)} clicks and ${input.row.searchConsole.impressions.toFixed(0)} impressions for this page in the selected window.` : "";
|
|
40947
|
+
const common = {
|
|
40948
|
+
priority: findingPriority({
|
|
40949
|
+
code: input.code,
|
|
40950
|
+
observedLinks: input.row.observedLinks,
|
|
40951
|
+
crawl,
|
|
40952
|
+
search: input.row.searchConsole
|
|
40953
|
+
}),
|
|
40954
|
+
heuristic: true,
|
|
40955
|
+
targetUrl: input.row.targetUrl,
|
|
40956
|
+
observedLinks: input.row.observedLinks,
|
|
40957
|
+
evidenceRefs: [input.row.targetUrl, crawl.reportId]
|
|
40958
|
+
};
|
|
40959
|
+
if (input.code === "linked-broken-target") return {
|
|
40960
|
+
...common,
|
|
40961
|
+
code: input.code,
|
|
40962
|
+
principle: "Linked target pages should resolve intentionally.",
|
|
40963
|
+
evidence: `The saved crawl observed status ${crawl.status} for a target with ${input.row.observedLinks} retained representative links.${searchEvidence}`,
|
|
40964
|
+
action: "Confirm whether the page should exist. Restore it or add one direct redirect to a genuinely equivalent live page when the missing URL is unintended.",
|
|
40965
|
+
verify: "Run redirect-trace for the target, then recrawl it and confirm a stable intended response."
|
|
40966
|
+
};
|
|
40967
|
+
if (input.code === "linked-redirect-target") return {
|
|
40968
|
+
...common,
|
|
40969
|
+
code: input.code,
|
|
40970
|
+
principle: "External links should reach a stable final destination.",
|
|
40971
|
+
evidence: `The saved crawl observed ${input.row.targetUrl} resolving to ${crawl.finalUrl}.${searchEvidence}`,
|
|
40972
|
+
action: "Check that the redirect is intentional, direct and semantically equivalent. Update links you control to the final URL and ask important external sources to update only when the change is worth the effort.",
|
|
40973
|
+
verify: "Run redirect-trace and confirm one stable hop to the intended indexable page."
|
|
40974
|
+
};
|
|
40975
|
+
if (input.code === "linked-canonical-conflict") return {
|
|
40976
|
+
...common,
|
|
40977
|
+
code: input.code,
|
|
40978
|
+
principle: "Canonical hints should agree with the intended linked page.",
|
|
40979
|
+
evidence: `The saved crawl observed canonical ${crawl.canonical} on a linked target that resolved to ${crawl.finalUrl}.${searchEvidence}`,
|
|
40980
|
+
action: "Confirm the preferred URL. Align the canonical and redirect only if the current disagreement is unintended.",
|
|
40981
|
+
verify: "Audit the final page and inspect the declared and Google-selected canonical before changing it."
|
|
40982
|
+
};
|
|
40983
|
+
return {
|
|
40984
|
+
...common,
|
|
40985
|
+
code: input.code,
|
|
40986
|
+
principle: "A linked page should be indexable when search visibility is intended.",
|
|
40987
|
+
evidence: `The saved crawl marked a linked target as non-indexable.${searchEvidence}`,
|
|
40988
|
+
action: "Confirm whether non-indexing is intentional. Remove the blocking signal or redirect to an equivalent indexable page only when this URL should appear in search.",
|
|
40989
|
+
verify: "Audit the page, then use URL Inspection when the intended state is indexable."
|
|
40990
|
+
};
|
|
40991
|
+
}
|
|
40992
|
+
function linkTargetFindings(rows) {
|
|
40993
|
+
return rows.flatMap((row) => {
|
|
40994
|
+
const crawl = row.crawl;
|
|
40995
|
+
if (crawl.state !== "observed") return [];
|
|
40996
|
+
if (crawl.status >= 400) return [finding({
|
|
40997
|
+
row,
|
|
40998
|
+
code: "linked-broken-target"
|
|
40999
|
+
})];
|
|
41000
|
+
if (urlKey(crawl.finalUrl) !== urlKey(row.targetUrl)) return [finding({
|
|
41001
|
+
row,
|
|
41002
|
+
code: "linked-redirect-target"
|
|
41003
|
+
})];
|
|
41004
|
+
if (crawl.canonical && urlKey(crawl.canonical) !== urlKey(crawl.finalUrl)) return [finding({
|
|
41005
|
+
row,
|
|
41006
|
+
code: "linked-canonical-conflict"
|
|
41007
|
+
})];
|
|
41008
|
+
if (!crawl.indexable) return [finding({
|
|
41009
|
+
row,
|
|
41010
|
+
code: "linked-non-indexable-target"
|
|
41011
|
+
})];
|
|
41012
|
+
return [];
|
|
41013
|
+
}).sort((left, right) => ({
|
|
41014
|
+
high: 0,
|
|
41015
|
+
medium: 1,
|
|
41016
|
+
low: 2
|
|
41017
|
+
})[left.priority] - {
|
|
41018
|
+
high: 0,
|
|
41019
|
+
medium: 1,
|
|
41020
|
+
low: 2
|
|
41021
|
+
}[right.priority] || right.observedLinks - left.observedLinks || (left.targetUrl < right.targetUrl ? -1 : left.targetUrl > right.targetUrl ? 1 : 0)).slice(0, MAX_FINDINGS$1);
|
|
41022
|
+
}
|
|
41023
|
+
//#endregion
|
|
41024
|
+
//#region packages/core/src/links/context.ts
|
|
41025
|
+
const DEFAULT_DAYS = 90;
|
|
41026
|
+
const MAX_DAYS = 548;
|
|
41027
|
+
const DEFAULT_CONTEXT_LIMIT = 100;
|
|
41028
|
+
const MAX_CONTEXT_LIMIT = 100;
|
|
41029
|
+
const MAX_GSC_ROWS = 25e3;
|
|
41030
|
+
const defaultDependencies = {
|
|
41031
|
+
latestCrawl: latestCrawlReport,
|
|
41032
|
+
searchAnalytics: querySearchAnalytics,
|
|
41033
|
+
now: () => /* @__PURE__ */ new Date()
|
|
41034
|
+
};
|
|
41035
|
+
function integer(input) {
|
|
41036
|
+
const value = input.value ?? input.fallback;
|
|
41037
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > input.maximum) throw new SeoError("INVALID_INPUT", `${input.label} must be between 1 and ${input.maximum}.`);
|
|
41038
|
+
return value;
|
|
41039
|
+
}
|
|
41040
|
+
function searchEvidence(input) {
|
|
41041
|
+
if (!input.site) return {
|
|
41042
|
+
state: "unavailable",
|
|
41043
|
+
reason: "No Search Console property was selected for this report."
|
|
41044
|
+
};
|
|
41045
|
+
if (input.error) return {
|
|
41046
|
+
state: "unavailable",
|
|
41047
|
+
reason: input.error
|
|
41048
|
+
};
|
|
41049
|
+
if (!input.aggregate) return {
|
|
41050
|
+
state: "not-retained",
|
|
41051
|
+
reason: input.retainedLimitReached ? `The bounded Search Console response reached ${MAX_GSC_ROWS} rows and did not retain this page.` : "Search Console did not retain a page row for this URL in the selected window."
|
|
41052
|
+
};
|
|
41053
|
+
return {
|
|
41054
|
+
state: "observed",
|
|
41055
|
+
clicks: input.aggregate.clicks,
|
|
41056
|
+
impressions: input.aggregate.impressions,
|
|
41057
|
+
ctr: input.aggregate.impressions > 0 ? input.aggregate.clicks / input.aggregate.impressions : 0,
|
|
41058
|
+
position: input.aggregate.impressions > 0 ? input.aggregate.positionWeight / input.aggregate.impressions : 0
|
|
41059
|
+
};
|
|
41060
|
+
}
|
|
41061
|
+
async function linkTargetContext(input, dependencies = defaultDependencies) {
|
|
41062
|
+
const days = integer({
|
|
41063
|
+
value: input.days,
|
|
41064
|
+
fallback: DEFAULT_DAYS,
|
|
41065
|
+
maximum: MAX_DAYS,
|
|
41066
|
+
label: "Link context days"
|
|
41067
|
+
});
|
|
41068
|
+
const limit = integer({
|
|
41069
|
+
value: input.limit,
|
|
41070
|
+
fallback: DEFAULT_CONTEXT_LIMIT,
|
|
41071
|
+
maximum: MAX_CONTEXT_LIMIT,
|
|
41072
|
+
label: "Link context limit"
|
|
41073
|
+
});
|
|
41074
|
+
const available = linkTargetCounts(input.evidence);
|
|
41075
|
+
const selected = available.slice(0, limit);
|
|
41076
|
+
const crawlLookup = input.crawlSite ?? input.searchConsoleSite;
|
|
41077
|
+
const crawl = crawlLookup ? dependencies.latestCrawl(crawlLookup) : void 0;
|
|
41078
|
+
const crawlValues = crawlForLinkTargets({
|
|
41079
|
+
report: crawl,
|
|
41080
|
+
targets: selected
|
|
41081
|
+
});
|
|
41082
|
+
const warnings = [];
|
|
41083
|
+
const range = finalGscDateRange(days, dependencies.now());
|
|
41084
|
+
let searchRows = /* @__PURE__ */ new Map();
|
|
41085
|
+
let searchCalls = 0;
|
|
41086
|
+
let searchRowsFetched = 0;
|
|
41087
|
+
let searchError = null;
|
|
41088
|
+
if (input.searchConsoleSite) try {
|
|
41089
|
+
const result = await dependencies.searchAnalytics(input.searchConsoleSite, {
|
|
41090
|
+
...range,
|
|
41091
|
+
dimensions: ["page"],
|
|
41092
|
+
type: "web",
|
|
41093
|
+
dataState: "final",
|
|
41094
|
+
rowLimit: MAX_GSC_ROWS,
|
|
41095
|
+
maxRows: MAX_GSC_ROWS
|
|
41096
|
+
}, { refresh: input.refresh });
|
|
41097
|
+
searchRows = aggregateLinkSearchRows(result.rows);
|
|
41098
|
+
searchCalls = result.calls;
|
|
41099
|
+
searchRowsFetched = result.rowsFetched;
|
|
41100
|
+
} catch (error) {
|
|
41101
|
+
searchError = error instanceof Error ? error.message : "Search Console evidence could not be loaded.";
|
|
41102
|
+
warnings.push(`Search Console context was unavailable. ${searchError}`);
|
|
41103
|
+
}
|
|
41104
|
+
let searchMatches = 0;
|
|
41105
|
+
const retainedLimitReached = searchRowsFetched >= MAX_GSC_ROWS;
|
|
41106
|
+
const rows = selected.map((target) => {
|
|
41107
|
+
const aggregate = searchRows.get(urlKey(target.targetUrl) ?? "");
|
|
41108
|
+
if (aggregate) searchMatches += 1;
|
|
41109
|
+
return {
|
|
41110
|
+
...target,
|
|
41111
|
+
crawl: crawlValues.values.get(target.targetUrl) ?? {
|
|
41112
|
+
state: "not-observed",
|
|
41113
|
+
reason: "The target URL was not present in the retained crawl pages."
|
|
41114
|
+
},
|
|
41115
|
+
searchConsole: searchEvidence({
|
|
41116
|
+
site: input.searchConsoleSite,
|
|
41117
|
+
error: searchError,
|
|
41118
|
+
retainedLimitReached,
|
|
41119
|
+
aggregate
|
|
41120
|
+
})
|
|
41121
|
+
};
|
|
41122
|
+
});
|
|
41123
|
+
if (!crawl) warnings.push("No matching saved crawl was available, so target response and indexability checks were skipped.");
|
|
41124
|
+
if (retainedLimitReached) warnings.push(`Search Console reached the ${MAX_GSC_ROWS}-row acquisition limit. Missing target rows remain not retained rather than zero.`);
|
|
41125
|
+
const unavailableSources = Number(!crawl) + Number(!input.searchConsoleSite);
|
|
41126
|
+
return {
|
|
41127
|
+
schemaVersion: 1,
|
|
41128
|
+
dataStatus: unavailableSources === 2 ? "unavailable" : unavailableSources > 0 || searchError || retainedLimitReached ? "partial" : "complete",
|
|
41129
|
+
selection: {
|
|
41130
|
+
availableTargets: available.length,
|
|
41131
|
+
returnedTargets: rows.length,
|
|
41132
|
+
omittedTargets: Math.max(0, available.length - rows.length),
|
|
41133
|
+
limit
|
|
41134
|
+
},
|
|
41135
|
+
provenance: {
|
|
41136
|
+
crawl: {
|
|
41137
|
+
status: crawl ? "joined" : "unavailable",
|
|
41138
|
+
reportId: crawl?.id ?? null,
|
|
41139
|
+
observedAt: crawl?.generatedAt ?? null,
|
|
41140
|
+
availablePages: crawl?.pages.length ?? 0,
|
|
41141
|
+
matchedTargets: crawlValues.matched
|
|
41142
|
+
},
|
|
41143
|
+
searchConsole: {
|
|
41144
|
+
status: !input.searchConsoleSite || searchError ? "unavailable" : retainedLimitReached ? "partial" : "joined",
|
|
41145
|
+
site: input.searchConsoleSite ?? null,
|
|
41146
|
+
range: input.searchConsoleSite ? {
|
|
41147
|
+
...range,
|
|
41148
|
+
days
|
|
41149
|
+
} : null,
|
|
41150
|
+
calls: searchCalls,
|
|
41151
|
+
rowsFetched: searchRowsFetched,
|
|
41152
|
+
retainedRowLimit: MAX_GSC_ROWS,
|
|
41153
|
+
retainedRowLimitReached: retainedLimitReached,
|
|
41154
|
+
matchedTargets: searchMatches
|
|
41155
|
+
}
|
|
41156
|
+
},
|
|
41157
|
+
rows,
|
|
41158
|
+
findings: linkTargetFindings(rows),
|
|
41159
|
+
warnings
|
|
41160
|
+
};
|
|
41161
|
+
}
|
|
41162
|
+
//#endregion
|
|
41163
|
+
//#region packages/core/src/providers/dataforseo/link-research-shared.ts
|
|
41164
|
+
function linkTarget(value, requestedScope = "domain") {
|
|
41165
|
+
const raw = value.trim();
|
|
41166
|
+
if (!raw || raw.length > 2048) throw invalidTarget();
|
|
41167
|
+
if (requestedScope === "page") try {
|
|
41168
|
+
const url = new URL(raw);
|
|
41169
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) throw new Error();
|
|
41170
|
+
url.hash = "";
|
|
41171
|
+
return {
|
|
41172
|
+
target: url.toString(),
|
|
41173
|
+
scope: "page"
|
|
41174
|
+
};
|
|
41175
|
+
} catch {
|
|
41176
|
+
throw invalidTarget();
|
|
41177
|
+
}
|
|
41178
|
+
try {
|
|
41179
|
+
const hostname = new URL(raw.includes("://") ? raw : `https://${raw}`).hostname.toLowerCase().replace(/^www\./u, "").replace(/\.$/u, "");
|
|
41180
|
+
if (!hostname || hostname.length > 253 || hostname.includes("..") || !hostname.includes(".") || !/^[a-z0-9.-]+$/u.test(hostname)) throw new Error();
|
|
41181
|
+
return {
|
|
41182
|
+
target: hostname,
|
|
41183
|
+
scope: "domain"
|
|
41184
|
+
};
|
|
41185
|
+
} catch {
|
|
41186
|
+
throw invalidTarget();
|
|
41187
|
+
}
|
|
41188
|
+
}
|
|
41189
|
+
function invalidTarget() {
|
|
41190
|
+
return new ProviderError({
|
|
41191
|
+
provider: "dataforseo",
|
|
41192
|
+
operation: "link-evidence",
|
|
41193
|
+
code: "configuration",
|
|
41194
|
+
message: "Use a valid domain, or choose page scope and pass an absolute URL."
|
|
41195
|
+
});
|
|
41196
|
+
}
|
|
41197
|
+
function linkRowLimit(limit, offset = 0) {
|
|
41198
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 1e3) throw new ProviderError({
|
|
41199
|
+
provider: "dataforseo",
|
|
41200
|
+
operation: "link-evidence",
|
|
41201
|
+
code: "configuration",
|
|
41202
|
+
message: `Link row limit must be from 1 to ${MAX_LINK_ROWS$1}.`
|
|
41203
|
+
});
|
|
41204
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || offset > 2e4) throw new ProviderError({
|
|
41205
|
+
provider: "dataforseo",
|
|
41206
|
+
operation: "link-evidence",
|
|
41207
|
+
code: "configuration",
|
|
41208
|
+
message: `Link row offset must be from 0 to ${MAX_LINK_OFFSET}.`
|
|
41209
|
+
});
|
|
41210
|
+
}
|
|
41211
|
+
function requestContext(reportId, supplied) {
|
|
41212
|
+
return supplied ?? {
|
|
41213
|
+
reportId,
|
|
41214
|
+
reportRunId: randomUUID()
|
|
41215
|
+
};
|
|
41216
|
+
}
|
|
41217
|
+
function missing(field) {
|
|
41218
|
+
return unavailableValue("missing", `DataForSEO omitted ${field}.`);
|
|
41219
|
+
}
|
|
41220
|
+
function numberValue(value, field) {
|
|
41221
|
+
return value === null || value === void 0 ? missing(field) : observedValue(value);
|
|
41222
|
+
}
|
|
41223
|
+
function stringValue(value, field) {
|
|
41224
|
+
return value ? observedValue(value) : missing(field);
|
|
41225
|
+
}
|
|
41226
|
+
function metric(id, label, value) {
|
|
41227
|
+
return value === null || value === void 0 ? [] : [{
|
|
41228
|
+
provider: "dataforseo",
|
|
41229
|
+
id,
|
|
41230
|
+
label,
|
|
41231
|
+
value,
|
|
41232
|
+
scale: {
|
|
41233
|
+
minimum: 0,
|
|
41234
|
+
maximum: 100
|
|
41235
|
+
}
|
|
41236
|
+
}];
|
|
41237
|
+
}
|
|
41238
|
+
function safeUrl(value) {
|
|
41239
|
+
if (!value) return null;
|
|
41240
|
+
try {
|
|
41241
|
+
const url = new URL(value);
|
|
41242
|
+
if (!["http:", "https:"].includes(url.protocol)) return null;
|
|
41243
|
+
url.username = "";
|
|
41244
|
+
url.password = "";
|
|
41245
|
+
url.hash = "";
|
|
41246
|
+
return url.toString();
|
|
41247
|
+
} catch {
|
|
41248
|
+
return null;
|
|
41249
|
+
}
|
|
41250
|
+
}
|
|
41251
|
+
function normalizedDate(value) {
|
|
41252
|
+
if (!value) return null;
|
|
41253
|
+
const parsed = Date.parse(value);
|
|
41254
|
+
return Number.isFinite(parsed) ? new Date(parsed).toISOString() : null;
|
|
41255
|
+
}
|
|
41256
|
+
function uniqueSorted(values = []) {
|
|
41257
|
+
return [...new Set(values.map((value) => value.trim()).filter(Boolean))].sort(compareCodepoints$3);
|
|
41258
|
+
}
|
|
41259
|
+
function totalRows(values) {
|
|
41260
|
+
const present = values.filter((value) => value !== null && value !== void 0);
|
|
41261
|
+
return present.length ? Math.max(...present) : null;
|
|
41262
|
+
}
|
|
41263
|
+
function linkCoverage(input) {
|
|
41264
|
+
const hasMore = input.providerTotalComparable !== false && input.providerTotalRows !== null ? input.offset + input.returnedRows < input.providerTotalRows : input.returnedRows >= input.requestedRows;
|
|
41265
|
+
return {
|
|
41266
|
+
requestedRows: input.requestedRows,
|
|
41267
|
+
returnedRows: input.returnedRows,
|
|
41268
|
+
retainedRows: input.retainedRows,
|
|
41269
|
+
invalidRows: input.invalidRows,
|
|
41270
|
+
providerTotalRows: input.providerTotalRows,
|
|
41271
|
+
completeness: input.invalidRows > 0 ? "partial" : hasMore ? "capped" : input.filtered ? "filtered" : "complete",
|
|
41272
|
+
nextCursor: hasMore ? String(input.offset + input.returnedRows) : null
|
|
41273
|
+
};
|
|
41274
|
+
}
|
|
41275
|
+
function linkEvidence(input) {
|
|
41276
|
+
return {
|
|
41277
|
+
schemaVersion: 1,
|
|
41278
|
+
provider: "dataforseo",
|
|
41279
|
+
capability: input.capability,
|
|
41280
|
+
data: input.data,
|
|
41281
|
+
observedAt: input.snapshot.observedAt,
|
|
41282
|
+
market: null,
|
|
41283
|
+
coverage: input.coverage,
|
|
41284
|
+
cache: input.snapshot.cache,
|
|
41285
|
+
cost: input.snapshot.cost,
|
|
41286
|
+
request: {
|
|
41287
|
+
operation: input.capability,
|
|
41288
|
+
endpoint: input.endpoint,
|
|
41289
|
+
limit: input.limit,
|
|
41290
|
+
filters: input.filters,
|
|
41291
|
+
sort: input.sort
|
|
41292
|
+
},
|
|
41293
|
+
warnings: input.warnings
|
|
41294
|
+
};
|
|
41295
|
+
}
|
|
41296
|
+
function mappingWarnings(input) {
|
|
41297
|
+
return [
|
|
41298
|
+
...input.snapshotWarnings,
|
|
41299
|
+
...input.invalidRows ? [{
|
|
41300
|
+
code: `invalid-${input.label}-rows`,
|
|
41301
|
+
field: "data.rows",
|
|
41302
|
+
message: `${input.invalidRows} ${input.label} row${input.invalidRows === 1 ? "" : "s"} lacked required fields and were omitted.`
|
|
41303
|
+
}] : [],
|
|
41304
|
+
...input.duplicateRows ? [{
|
|
41305
|
+
code: `duplicate-${input.label}-rows`,
|
|
41306
|
+
field: "data.rows",
|
|
41307
|
+
message: `${input.duplicateRows} duplicate ${input.label} row${input.duplicateRows === 1 ? "" : "s"} were collapsed deterministically.`
|
|
41308
|
+
}] : []
|
|
41309
|
+
];
|
|
41310
|
+
}
|
|
41311
|
+
//#endregion
|
|
41312
|
+
//#region packages/core/src/providers/dataforseo/backlinks.ts
|
|
41313
|
+
function metricValue(row, id) {
|
|
41314
|
+
return row.metrics.find((item) => item.id === id)?.value ?? -1;
|
|
41315
|
+
}
|
|
41316
|
+
function compareRows(left, right) {
|
|
41317
|
+
return metricValue(right, "backlink-rank") - metricValue(left, "backlink-rank") || metricValue(right, "source-domain-rank") - metricValue(left, "source-domain-rank") || compareCodepoints$3(left.sourceDomain, right.sourceDomain) || compareCodepoints$3(left.sourceUrl, right.sourceUrl) || compareCodepoints$3(left.targetUrl, right.targetUrl) || compareCodepoints$3(left.anchorText ?? "", right.anchorText ?? "");
|
|
41318
|
+
}
|
|
41319
|
+
function dedupe(rows) {
|
|
41320
|
+
const sorted = [...rows].sort(compareRows);
|
|
41321
|
+
const seen = /* @__PURE__ */ new Set();
|
|
41322
|
+
return sorted.filter((row) => {
|
|
41323
|
+
const key = `${row.sourceUrl}\0${row.targetUrl}\0${row.anchorText ?? ""}`;
|
|
41324
|
+
if (seen.has(key)) return false;
|
|
41325
|
+
seen.add(key);
|
|
41326
|
+
return true;
|
|
41327
|
+
});
|
|
41328
|
+
}
|
|
41329
|
+
async function dataForSeoBacklinks(client, input) {
|
|
41330
|
+
const normalized = linkTarget(input.target, input.scope);
|
|
41331
|
+
const includeSubdomains = normalized.scope === "domain" ? input.includeSubdomains ?? true : false;
|
|
41332
|
+
const offset = input.offset ?? 0;
|
|
41333
|
+
linkRowLimit(input.limit, offset);
|
|
41334
|
+
const mode = input.mode ?? "representative";
|
|
41335
|
+
const status = input.status ?? "live";
|
|
41336
|
+
const providerMode = mode === "representative" ? "one_per_domain" : "as_is";
|
|
41337
|
+
const orderBy = [
|
|
41338
|
+
"rank,desc",
|
|
41339
|
+
"domain_from_rank,desc",
|
|
41340
|
+
"url_from,asc"
|
|
41341
|
+
];
|
|
41342
|
+
const snapshot = await client.backlinks({
|
|
41343
|
+
...normalized,
|
|
41344
|
+
includeSubdomains,
|
|
41345
|
+
mode: providerMode,
|
|
41346
|
+
status,
|
|
41347
|
+
limit: input.limit,
|
|
41348
|
+
offset,
|
|
41349
|
+
orderBy,
|
|
41350
|
+
refresh: input.refresh,
|
|
41351
|
+
context: requestContext("link-evidence", input.context)
|
|
41352
|
+
});
|
|
41353
|
+
const results = snapshot.response.tasks.flatMap((task) => task.result ?? []);
|
|
41354
|
+
const rawRows = results.flatMap((result) => result.items ?? []);
|
|
41355
|
+
let invalidRows = 0;
|
|
41356
|
+
const mapped = rawRows.flatMap((row) => {
|
|
41357
|
+
const sourceUrl = safeUrl(row.url_from);
|
|
41358
|
+
const targetUrl = safeUrl(row.url_to);
|
|
41359
|
+
if (!sourceUrl || !targetUrl) {
|
|
41360
|
+
invalidRows += 1;
|
|
41361
|
+
return [];
|
|
41362
|
+
}
|
|
41363
|
+
return [{
|
|
41364
|
+
sourceUrl,
|
|
41365
|
+
sourceDomain: new URL(sourceUrl).hostname.toLowerCase(),
|
|
41366
|
+
targetUrl,
|
|
41367
|
+
anchorText: row.anchor ?? null,
|
|
41368
|
+
linkType: row.item_type ?? null,
|
|
41369
|
+
dofollow: row.dofollow ?? null,
|
|
41370
|
+
attributes: uniqueSorted(row.rel_attributes ?? row.attributes ?? []),
|
|
41371
|
+
firstSeenAt: normalizedDate(row.first_seen),
|
|
41372
|
+
lastSeenAt: normalizedDate(row.lost_date ?? row.last_visited),
|
|
41373
|
+
state: row.is_lost || row.lost_date ? "lost" : "live",
|
|
41374
|
+
indirect: row.is_indirect_link ?? null,
|
|
41375
|
+
linksFromPage: row.links_count ?? null,
|
|
41376
|
+
linksFromDomain: row.group_count ?? null,
|
|
41377
|
+
metrics: [
|
|
41378
|
+
...metric("backlink-rank", "DataForSEO backlink rank", row.rank),
|
|
41379
|
+
...metric("source-domain-rank", "DataForSEO source domain rank", row.domain_from_rank),
|
|
41380
|
+
...metric("source-page-rank", "DataForSEO source page rank", row.page_from_rank),
|
|
41381
|
+
...metric("backlink-spam-score", "DataForSEO backlink spam score", row.backlink_spam_score ?? row.backlinks_spam_score)
|
|
41382
|
+
]
|
|
41383
|
+
}];
|
|
41384
|
+
});
|
|
41385
|
+
const rows = dedupe(mapped);
|
|
41386
|
+
const duplicateRows = mapped.length - rows.length;
|
|
41387
|
+
const providerTotalRows = totalRows(results.map((result) => result.total_count));
|
|
41388
|
+
return linkEvidence({
|
|
41389
|
+
capability: "backlinks",
|
|
41390
|
+
data: {
|
|
41391
|
+
target: normalized.target,
|
|
41392
|
+
mode,
|
|
41393
|
+
rows,
|
|
41394
|
+
totalRows: providerTotalRows
|
|
41395
|
+
},
|
|
41396
|
+
snapshot,
|
|
41397
|
+
coverage: linkCoverage({
|
|
41398
|
+
requestedRows: input.limit,
|
|
41399
|
+
returnedRows: rawRows.length,
|
|
41400
|
+
retainedRows: rows.length,
|
|
41401
|
+
invalidRows,
|
|
41402
|
+
providerTotalRows,
|
|
41403
|
+
offset,
|
|
41404
|
+
filtered: mode === "representative" || status !== "all",
|
|
41405
|
+
providerTotalComparable: mode !== "representative"
|
|
41406
|
+
}),
|
|
41407
|
+
endpoint: LINK_ENDPOINTS.backlinks,
|
|
41408
|
+
limit: input.limit,
|
|
41409
|
+
filters: {
|
|
41410
|
+
scope: normalized.scope,
|
|
41411
|
+
includeSubdomains,
|
|
41412
|
+
mode,
|
|
41413
|
+
status,
|
|
41414
|
+
includeIndirectLinks: true,
|
|
41415
|
+
excludeInternalBacklinks: true,
|
|
41416
|
+
offset
|
|
41417
|
+
},
|
|
41418
|
+
sort: orderBy,
|
|
41419
|
+
warnings: mappingWarnings({
|
|
41420
|
+
snapshotWarnings: snapshot.warnings,
|
|
41421
|
+
invalidRows,
|
|
41422
|
+
duplicateRows,
|
|
41423
|
+
label: "backlink"
|
|
41424
|
+
})
|
|
41425
|
+
});
|
|
41426
|
+
}
|
|
41427
|
+
//#endregion
|
|
41428
|
+
//#region packages/core/src/providers/dataforseo/link-summary.ts
|
|
41429
|
+
async function dataForSeoLinkSummary(client, input) {
|
|
41430
|
+
const normalized = linkTarget(input.target, input.scope);
|
|
41431
|
+
const includeSubdomains = normalized.scope === "domain" ? input.includeSubdomains ?? true : false;
|
|
41432
|
+
const snapshot = await client.linkSummary({
|
|
41433
|
+
...normalized,
|
|
41434
|
+
includeSubdomains,
|
|
41435
|
+
refresh: input.refresh,
|
|
41436
|
+
context: requestContext("link-evidence", input.context)
|
|
41437
|
+
});
|
|
41438
|
+
const rows = snapshot.response.tasks.flatMap((task) => task.result ?? []);
|
|
41439
|
+
const row = [...rows].sort((left, right) => compareCodepoints$3(left.target ?? "", right.target ?? ""))[0];
|
|
41440
|
+
const data = {
|
|
41441
|
+
target: normalized.target,
|
|
41442
|
+
scope: normalized.scope,
|
|
41443
|
+
backlinks: numberValue(row?.backlinks, "total backlinks"),
|
|
41444
|
+
referringDomains: numberValue(row?.referring_domains, "referring domains"),
|
|
41445
|
+
referringPages: numberValue(row?.referring_pages, "referring pages"),
|
|
41446
|
+
brokenBacklinks: numberValue(row?.broken_backlinks, "broken backlinks"),
|
|
41447
|
+
brokenPages: numberValue(row?.broken_pages, "broken referring pages"),
|
|
41448
|
+
metrics: [
|
|
41449
|
+
...metric("rank", "DataForSEO rank", row?.rank),
|
|
41450
|
+
...metric("backlinks-spam-score", "DataForSEO backlink spam score", row?.backlinks_spam_score),
|
|
41451
|
+
...metric("target-spam-score", "DataForSEO target spam score", row?.info?.target_spam_score)
|
|
41452
|
+
]
|
|
41453
|
+
};
|
|
41454
|
+
const retainedRows = row ? 1 : 0;
|
|
41455
|
+
return linkEvidence({
|
|
41456
|
+
capability: "link-summary",
|
|
41457
|
+
data,
|
|
41458
|
+
snapshot,
|
|
41459
|
+
coverage: {
|
|
41460
|
+
requestedRows: 1,
|
|
41461
|
+
returnedRows: rows.length,
|
|
41462
|
+
retainedRows,
|
|
41463
|
+
invalidRows: 0,
|
|
41464
|
+
providerTotalRows: rows.length,
|
|
41465
|
+
completeness: row ? "complete" : "unavailable",
|
|
41466
|
+
nextCursor: null
|
|
41467
|
+
},
|
|
41468
|
+
endpoint: LINK_ENDPOINTS.summary,
|
|
41469
|
+
limit: 1,
|
|
41470
|
+
filters: {
|
|
41471
|
+
scope: normalized.scope,
|
|
41472
|
+
includeSubdomains,
|
|
41473
|
+
status: "live",
|
|
41474
|
+
includeIndirectLinks: true,
|
|
41475
|
+
excludeInternalBacklinks: true
|
|
41476
|
+
},
|
|
41477
|
+
sort: [],
|
|
41478
|
+
warnings: [
|
|
41479
|
+
...snapshot.warnings,
|
|
41480
|
+
...rows.length > 1 ? [{
|
|
41481
|
+
code: "multiple-link-summary-rows",
|
|
41482
|
+
field: "data",
|
|
41483
|
+
message: "DataForSEO returned more than one summary row. One row was retained deterministically."
|
|
41484
|
+
}] : [],
|
|
41485
|
+
...!row ? [{
|
|
41486
|
+
code: "missing-link-summary-row",
|
|
41487
|
+
field: "data",
|
|
41488
|
+
message: "DataForSEO returned no summary row. Counts remain unavailable rather than zero."
|
|
41489
|
+
}] : []
|
|
41490
|
+
]
|
|
41491
|
+
});
|
|
41492
|
+
}
|
|
41493
|
+
//#endregion
|
|
41494
|
+
//#region packages/core/src/providers/dataforseo/referring-domains.ts
|
|
41495
|
+
function observedNumber(row) {
|
|
41496
|
+
return row.backlinks.state === "observed" ? row.backlinks.value : -1;
|
|
41497
|
+
}
|
|
41498
|
+
async function dataForSeoReferringDomains(client, input) {
|
|
41499
|
+
const normalized = linkTarget(input.target, input.scope);
|
|
41500
|
+
const includeSubdomains = normalized.scope === "domain" ? input.includeSubdomains ?? true : false;
|
|
41501
|
+
const offset = input.offset ?? 0;
|
|
41502
|
+
linkRowLimit(input.limit, offset);
|
|
41503
|
+
const orderBy = [
|
|
41504
|
+
"backlinks,desc",
|
|
41505
|
+
"rank,desc",
|
|
41506
|
+
"domain,asc"
|
|
41507
|
+
];
|
|
41508
|
+
const snapshot = await client.referringDomains({
|
|
41509
|
+
...normalized,
|
|
41510
|
+
includeSubdomains,
|
|
41511
|
+
limit: input.limit,
|
|
41512
|
+
offset,
|
|
41513
|
+
orderBy,
|
|
41514
|
+
refresh: input.refresh,
|
|
41515
|
+
context: requestContext("link-evidence", input.context)
|
|
41516
|
+
});
|
|
41517
|
+
const results = snapshot.response.tasks.flatMap((task) => task.result ?? []);
|
|
41518
|
+
const rawRows = results.flatMap((result) => result.items ?? []);
|
|
41519
|
+
let invalidRows = 0;
|
|
41520
|
+
const mapped = rawRows.flatMap((row) => {
|
|
41521
|
+
const domain = row.domain?.trim().toLowerCase();
|
|
41522
|
+
if (!domain) {
|
|
41523
|
+
invalidRows += 1;
|
|
41524
|
+
return [];
|
|
41525
|
+
}
|
|
41526
|
+
const firstSeenAt = normalizedDate(row.first_seen);
|
|
41527
|
+
return [{
|
|
41528
|
+
domain,
|
|
41529
|
+
backlinks: numberValue(row.backlinks, "domain backlinks"),
|
|
41530
|
+
referringPages: numberValue(row.referring_pages, "domain referring pages"),
|
|
41531
|
+
brokenBacklinks: numberValue(row.broken_backlinks, "domain broken backlinks"),
|
|
41532
|
+
brokenPages: numberValue(row.broken_pages, "domain broken pages"),
|
|
41533
|
+
firstSeenAt: stringValue(firstSeenAt, "domain first seen date"),
|
|
41534
|
+
metrics: [...metric("source-domain-rank", "DataForSEO source domain rank", row.rank), ...metric("domain-backlinks-spam-score", "DataForSEO domain backlink spam score", row.backlinks_spam_score)]
|
|
41535
|
+
}];
|
|
41536
|
+
});
|
|
41537
|
+
const byDomain = /* @__PURE__ */ new Map();
|
|
41538
|
+
for (const row of mapped.sort((left, right) => observedNumber(right) - observedNumber(left) || compareCodepoints$3(left.domain, right.domain))) if (!byDomain.has(row.domain)) byDomain.set(row.domain, row);
|
|
41539
|
+
const rows = [...byDomain.values()];
|
|
41540
|
+
const duplicateRows = mapped.length - rows.length;
|
|
41541
|
+
const providerTotalRows = totalRows(results.map((result) => result.total_count));
|
|
41542
|
+
return linkEvidence({
|
|
41543
|
+
capability: "referring-domains",
|
|
41544
|
+
data: {
|
|
41545
|
+
target: normalized.target,
|
|
41546
|
+
rows,
|
|
41547
|
+
totalRows: providerTotalRows
|
|
41548
|
+
},
|
|
41549
|
+
snapshot,
|
|
41550
|
+
coverage: linkCoverage({
|
|
41551
|
+
requestedRows: input.limit,
|
|
41552
|
+
returnedRows: rawRows.length,
|
|
41553
|
+
retainedRows: rows.length,
|
|
41554
|
+
invalidRows,
|
|
41555
|
+
providerTotalRows,
|
|
41556
|
+
offset,
|
|
41557
|
+
filtered: true
|
|
41558
|
+
}),
|
|
41559
|
+
endpoint: LINK_ENDPOINTS.referringDomains,
|
|
41560
|
+
limit: input.limit,
|
|
41561
|
+
filters: {
|
|
41562
|
+
scope: normalized.scope,
|
|
41563
|
+
includeSubdomains,
|
|
41564
|
+
status: "live",
|
|
41565
|
+
includeIndirectLinks: true,
|
|
41566
|
+
excludeInternalBacklinks: true,
|
|
41567
|
+
offset
|
|
41568
|
+
},
|
|
41569
|
+
sort: orderBy,
|
|
41570
|
+
warnings: mappingWarnings({
|
|
41571
|
+
snapshotWarnings: snapshot.warnings,
|
|
41572
|
+
invalidRows,
|
|
41573
|
+
duplicateRows,
|
|
41574
|
+
label: "referring-domain"
|
|
41575
|
+
})
|
|
41576
|
+
});
|
|
41577
|
+
}
|
|
41578
|
+
//#endregion
|
|
41579
|
+
//#region packages/core/src/providers/dataforseo/link-research.ts
|
|
41580
|
+
var DataForSeoLinkProvider = class {
|
|
41581
|
+
provider = "dataforseo";
|
|
41582
|
+
capabilitySupport = [
|
|
41583
|
+
"link-summary",
|
|
41584
|
+
"referring-domains",
|
|
41585
|
+
"backlinks"
|
|
41586
|
+
].map((capability) => ({
|
|
41587
|
+
capability,
|
|
41588
|
+
status: "available",
|
|
41589
|
+
markets: "all"
|
|
41590
|
+
}));
|
|
41591
|
+
client;
|
|
41592
|
+
constructor(options = {}) {
|
|
41593
|
+
this.client = options.client ?? new DataForSeoClient(options);
|
|
41594
|
+
}
|
|
41595
|
+
linkSummary(input) {
|
|
41596
|
+
return dataForSeoLinkSummary(this.client, input);
|
|
41597
|
+
}
|
|
41598
|
+
backlinks(input) {
|
|
41599
|
+
return dataForSeoBacklinks(this.client, input);
|
|
41600
|
+
}
|
|
41601
|
+
referringDomains(input) {
|
|
41602
|
+
return dataForSeoReferringDomains(this.client, input);
|
|
41603
|
+
}
|
|
41604
|
+
};
|
|
41605
|
+
//#endregion
|
|
41606
|
+
//#region packages/core/src/links/dataforseo.ts
|
|
41607
|
+
const DEFAULT_ROW_LIMIT$2 = 100;
|
|
41608
|
+
const MAX_ROW_LIMIT$2 = 500;
|
|
41609
|
+
function rowLimit(value) {
|
|
41610
|
+
const limit = value ?? DEFAULT_ROW_LIMIT$2;
|
|
41611
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_ROW_LIMIT$2) throw new SeoError("INVALID_INPUT", `DataForSEO link row limit must be between 1 and ${MAX_ROW_LIMIT$2}.`);
|
|
41612
|
+
return limit;
|
|
41613
|
+
}
|
|
41614
|
+
async function collectDataForSeoLinkEvidence(input) {
|
|
41615
|
+
const limit = rowLimit(input.rowLimit);
|
|
41616
|
+
const provider = input.provider ?? new DataForSeoLinkProvider();
|
|
41617
|
+
const context = input.context ?? {
|
|
41618
|
+
reportId: "link-evidence",
|
|
41619
|
+
reportRunId: randomUUID()
|
|
41620
|
+
};
|
|
41621
|
+
const summary = await provider.linkSummary({
|
|
41622
|
+
target: input.target,
|
|
41623
|
+
scope: input.scope,
|
|
41624
|
+
includeSubdomains: input.includeSubdomains,
|
|
41625
|
+
refresh: input.refresh,
|
|
41626
|
+
context
|
|
41627
|
+
});
|
|
41628
|
+
const backlinks = await provider.backlinks({
|
|
41629
|
+
target: summary.data.target,
|
|
41630
|
+
scope: summary.data.scope,
|
|
41631
|
+
includeSubdomains: input.includeSubdomains,
|
|
41632
|
+
mode: "representative",
|
|
41633
|
+
status: "live",
|
|
41634
|
+
limit,
|
|
41635
|
+
refresh: input.refresh,
|
|
41636
|
+
context
|
|
41637
|
+
});
|
|
41638
|
+
const rows = backlinks.data.rows.map((row) => ({
|
|
41639
|
+
sourceUrl: row.sourceUrl,
|
|
41640
|
+
sourceDomain: row.sourceDomain,
|
|
41641
|
+
targetUrl: row.targetUrl,
|
|
41642
|
+
anchorText: row.anchorText ?? void 0,
|
|
41643
|
+
firstSeenAt: row.firstSeenAt ?? void 0,
|
|
41644
|
+
lastSeenAt: row.lastSeenAt ?? void 0,
|
|
41645
|
+
nofollow: row.dofollow === null ? void 0 : !row.dofollow,
|
|
41646
|
+
linkType: row.linkType ?? void 0,
|
|
41647
|
+
attributes: row.attributes,
|
|
41648
|
+
state: row.state,
|
|
41649
|
+
indirect: row.indirect ?? void 0,
|
|
41650
|
+
linksFromPage: row.linksFromPage ?? void 0,
|
|
41651
|
+
linksFromDomain: row.linksFromDomain ?? void 0,
|
|
41652
|
+
providerMetrics: row.metrics
|
|
41653
|
+
}));
|
|
41654
|
+
const counts = /* @__PURE__ */ new Map();
|
|
41655
|
+
for (const row of rows) counts.set(row.targetUrl, (counts.get(row.targetUrl) ?? 0) + 1);
|
|
41656
|
+
const targetCounts = [...counts].map(([targetUrl, observedLinks]) => ({
|
|
41657
|
+
targetUrl,
|
|
41658
|
+
observedLinks
|
|
41659
|
+
})).sort((left, right) => right.observedLinks - left.observedLinks || (left.targetUrl < right.targetUrl ? -1 : left.targetUrl > right.targetUrl ? 1 : 0));
|
|
41660
|
+
const completeness = backlinks.coverage.completeness;
|
|
41661
|
+
const representative = backlinks.data.mode === "representative";
|
|
41662
|
+
const partial = completeness !== "complete" || representative;
|
|
41663
|
+
return {
|
|
41664
|
+
rows,
|
|
41665
|
+
targetCounts,
|
|
41666
|
+
provenance: {
|
|
41667
|
+
provider: "dataforseo",
|
|
41668
|
+
observedAt: backlinks.observedAt > summary.observedAt ? backlinks.observedAt : summary.observedAt,
|
|
41669
|
+
cached: summary.cache.status === "hit" && backlinks.cache.status === "hit",
|
|
41670
|
+
suppliedRows: backlinks.coverage.returnedRows ?? rows.length,
|
|
41671
|
+
validRows: rows.length,
|
|
41672
|
+
invalidRows: backlinks.coverage.invalidRows,
|
|
41673
|
+
duplicateRows: Math.max(0, (backlinks.coverage.returnedRows ?? rows.length) - backlinks.coverage.invalidRows - rows.length),
|
|
41674
|
+
capped: ["capped", "partial"].includes(completeness),
|
|
41675
|
+
rowLimit: limit,
|
|
41676
|
+
completeness: partial ? "partial" : "complete",
|
|
41677
|
+
providerRequests: {
|
|
41678
|
+
methods: [summary.request.endpoint, backlinks.request.endpoint],
|
|
41679
|
+
maxConcurrentRequests: 1
|
|
41680
|
+
},
|
|
41681
|
+
providerCoverage: {
|
|
41682
|
+
targetCountRows: {
|
|
41683
|
+
returnedRows: targetCounts.length,
|
|
41684
|
+
retainedRows: targetCounts.length,
|
|
41685
|
+
invalidRows: 0
|
|
41686
|
+
},
|
|
41687
|
+
detailRows: {
|
|
41688
|
+
returnedRows: backlinks.coverage.returnedRows ?? rows.length,
|
|
41689
|
+
retainedRows: rows.length,
|
|
41690
|
+
invalidRows: backlinks.coverage.invalidRows
|
|
41691
|
+
},
|
|
41692
|
+
summaryRows: {
|
|
41693
|
+
returnedRows: summary.coverage.returnedRows ?? 0,
|
|
41694
|
+
retainedRows: summary.coverage.retainedRows ?? 0,
|
|
41695
|
+
invalidRows: summary.coverage.invalidRows
|
|
41696
|
+
},
|
|
41697
|
+
backlinkRows: {
|
|
41698
|
+
returnedRows: backlinks.coverage.returnedRows ?? rows.length,
|
|
41699
|
+
retainedRows: rows.length,
|
|
41700
|
+
invalidRows: backlinks.coverage.invalidRows,
|
|
41701
|
+
providerTotalRows: backlinks.coverage.providerTotalRows
|
|
41702
|
+
}
|
|
41703
|
+
}
|
|
41704
|
+
},
|
|
41705
|
+
externalProvider: {
|
|
41706
|
+
summary,
|
|
41707
|
+
backlinks
|
|
41708
|
+
},
|
|
41709
|
+
warnings: [
|
|
41710
|
+
...summary.warnings.map((warning) => warning.message),
|
|
41711
|
+
...backlinks.warnings.map((warning) => warning.message),
|
|
41712
|
+
...representative ? ["The link list retains one representative backlink per referring domain. Provider summary counts remain separate from retained rows."] : [],
|
|
41713
|
+
...!representative && partial ? ["The link list is bounded or partial. Provider summary counts remain separate from retained rows."] : []
|
|
41714
|
+
]
|
|
41715
|
+
};
|
|
41716
|
+
}
|
|
41717
|
+
//#endregion
|
|
40624
41718
|
//#region packages/core/src/links/import.ts
|
|
40625
41719
|
const DEFAULT_ROW_LIMIT$1 = 1e4;
|
|
40626
41720
|
const MAX_ROW_LIMIT$1 = 1e5;
|
|
@@ -40809,6 +41903,10 @@ async function importLinkEvidence(input) {
|
|
|
40809
41903
|
//#region packages/core/src/links/report.ts
|
|
40810
41904
|
const DEFAULT_OUTPUT_LIMIT = 100;
|
|
40811
41905
|
const MAX_OUTPUT_LIMIT = 500;
|
|
41906
|
+
const MAX_TARGET_COUNTS = 100;
|
|
41907
|
+
const MAX_CONTEXT_ROWS = 100;
|
|
41908
|
+
const MAX_FINDINGS = 50;
|
|
41909
|
+
const MAX_STRUCTURED_DETAIL_ROWS = 750;
|
|
40812
41910
|
function boundedLimit(value) {
|
|
40813
41911
|
const limit = value ?? DEFAULT_OUTPUT_LIMIT;
|
|
40814
41912
|
if (!Number.isInteger(limit) || limit < 1 || limit > MAX_OUTPUT_LIMIT) throw new SeoError("INVALID_INPUT", `Link report limit must be between 1 and ${MAX_OUTPUT_LIMIT}.`);
|
|
@@ -40820,7 +41918,7 @@ function targetCounts(rows) {
|
|
|
40820
41918
|
return [...counts].map(([targetUrl, observedLinks]) => ({
|
|
40821
41919
|
targetUrl,
|
|
40822
41920
|
observedLinks
|
|
40823
|
-
})).sort((a, b) => b.observedLinks - a.observedLinks || a.targetUrl.
|
|
41921
|
+
})).sort((a, b) => b.observedLinks - a.observedLinks || (a.targetUrl < b.targetUrl ? -1 : a.targetUrl > b.targetUrl ? 1 : 0));
|
|
40824
41922
|
}
|
|
40825
41923
|
function linkEvidenceReport(input) {
|
|
40826
41924
|
const limit = boundedLimit(input.limit);
|
|
@@ -40828,8 +41926,24 @@ function linkEvidenceReport(input) {
|
|
|
40828
41926
|
const domains = new Set(input.evidence.rows.map((row) => row.sourceDomain));
|
|
40829
41927
|
const observedTargets = targetCounts(input.evidence.rows);
|
|
40830
41928
|
const counts = input.evidence.targetCounts.length ? input.evidence.targetCounts : observedTargets;
|
|
41929
|
+
const providerEvidence = input.evidence.externalProvider ? {
|
|
41930
|
+
summary: input.evidence.externalProvider.summary,
|
|
41931
|
+
backlinks: {
|
|
41932
|
+
...input.evidence.externalProvider.backlinks,
|
|
41933
|
+
data: {
|
|
41934
|
+
target: input.evidence.externalProvider.backlinks.data.target,
|
|
41935
|
+
mode: input.evidence.externalProvider.backlinks.data.mode,
|
|
41936
|
+
totalRows: input.evidence.externalProvider.backlinks.data.totalRows,
|
|
41937
|
+
retainedRows: input.evidence.externalProvider.backlinks.data.rows.length
|
|
41938
|
+
}
|
|
41939
|
+
}
|
|
41940
|
+
} : null;
|
|
41941
|
+
const targetCountsOutput = counts.slice(0, MAX_TARGET_COUNTS);
|
|
41942
|
+
const contextRows = input.targetContext?.rows.slice(0, MAX_CONTEXT_ROWS) ?? [];
|
|
41943
|
+
const findings = input.targetContext?.findings.slice(0, MAX_FINDINGS) ?? [];
|
|
41944
|
+
const returnedDetailRows = rows.length + targetCountsOutput.length + contextRows.length + findings.length;
|
|
40831
41945
|
return {
|
|
40832
|
-
schemaVersion:
|
|
41946
|
+
schemaVersion: 2,
|
|
40833
41947
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
40834
41948
|
dataStatus: input.evidence.provenance.completeness === "complete" ? "complete" : "partial",
|
|
40835
41949
|
summary: {
|
|
@@ -40838,6 +41952,8 @@ function linkEvidenceReport(input) {
|
|
|
40838
41952
|
targetPages: new Set(input.evidence.rows.map((row) => row.targetUrl)).size,
|
|
40839
41953
|
providerTargetPages: counts.length
|
|
40840
41954
|
},
|
|
41955
|
+
providerSummary: providerEvidence?.summary.data ?? null,
|
|
41956
|
+
providerEvidence,
|
|
40841
41957
|
provenance: input.evidence.provenance,
|
|
40842
41958
|
selection: {
|
|
40843
41959
|
availableRows: input.evidence.rows.length,
|
|
@@ -40845,13 +41961,45 @@ function linkEvidenceReport(input) {
|
|
|
40845
41961
|
omittedRows: Math.max(0, input.evidence.rows.length - rows.length),
|
|
40846
41962
|
limit
|
|
40847
41963
|
},
|
|
40848
|
-
|
|
41964
|
+
outputBudget: {
|
|
41965
|
+
unit: "structured-detail-rows",
|
|
41966
|
+
limit: MAX_STRUCTURED_DETAIL_ROWS,
|
|
41967
|
+
returned: returnedDetailRows,
|
|
41968
|
+
omitted: Math.max(0, input.evidence.rows.length - rows.length + Math.max(0, counts.length - targetCountsOutput.length) + Math.max(0, (input.targetContext?.rows.length ?? 0) - contextRows.length) + Math.max(0, (input.targetContext?.findings.length ?? 0) - findings.length)),
|
|
41969
|
+
sections: {
|
|
41970
|
+
links: {
|
|
41971
|
+
limit: MAX_OUTPUT_LIMIT,
|
|
41972
|
+
returned: rows.length
|
|
41973
|
+
},
|
|
41974
|
+
targetCounts: {
|
|
41975
|
+
limit: MAX_TARGET_COUNTS,
|
|
41976
|
+
returned: targetCountsOutput.length
|
|
41977
|
+
},
|
|
41978
|
+
targetContext: {
|
|
41979
|
+
limit: MAX_CONTEXT_ROWS,
|
|
41980
|
+
returned: contextRows.length
|
|
41981
|
+
},
|
|
41982
|
+
findings: {
|
|
41983
|
+
limit: MAX_FINDINGS,
|
|
41984
|
+
returned: findings.length
|
|
41985
|
+
}
|
|
41986
|
+
}
|
|
41987
|
+
},
|
|
41988
|
+
targetCounts: targetCountsOutput,
|
|
40849
41989
|
links: rows,
|
|
40850
|
-
|
|
41990
|
+
targetContext: input.targetContext ? {
|
|
41991
|
+
...input.targetContext,
|
|
41992
|
+
rows: contextRows,
|
|
41993
|
+
findings
|
|
41994
|
+
} : null,
|
|
41995
|
+
findings,
|
|
41996
|
+
warnings: [...input.evidence.warnings, ...input.targetContext?.warnings ?? []],
|
|
40851
41997
|
caveats: [
|
|
40852
41998
|
"This is bounded provider or imported evidence, not a complete backlink index.",
|
|
40853
41999
|
"A missing link is not proof that no link exists, and an observed link is not a quality or ranking verdict.",
|
|
40854
|
-
"Referring-domain counts describe the retained rows only."
|
|
42000
|
+
"Referring-domain counts describe the retained rows only.",
|
|
42001
|
+
...providerEvidence ? ["Provider profile counts describe its current index. The representative link rows are a separate bounded sample.", "Provider rank and spam metrics keep their provider names and scales. They are context, not ranking factors or universal authority scores."] : [],
|
|
42002
|
+
...input.targetContext ? ["Search Console page rows are retained first-party evidence. Missing or capped rows are never treated as zero.", "Saved crawl findings describe the recorded crawl time. Verify the live target before changing it."] : []
|
|
40855
42003
|
]
|
|
40856
42004
|
};
|
|
40857
42005
|
}
|
|
@@ -42183,6 +43331,6 @@ function isoDate(daysAgo = 0) {
|
|
|
42183
43331
|
return date.toISOString().slice(0, 10);
|
|
42184
43332
|
}
|
|
42185
43333
|
//#endregion
|
|
42186
|
-
export { AI_REFERRAL_SOURCES, AI_REFERRAL_SOURCES_VERSION, AI_SEARCH_SCORECARD_METHODOLOGY_ID, AI_SEARCH_SCORECARD_METHODOLOGY_VERSION, AUDIT_PAGE_RULE_IDS, AUTOMATIC_BASELINE_SITE_RETENTION, AUTOMATIC_BASELINE_TOTAL_RETENTION, BING_API_KEY_ENV, BING_API_KEY_SECRET, BROWSER_USER_AGENT, BingWebmasterClient, CANNIBAL_MAXIMUM_DOMINANT_SHARE, CANNIBAL_MINIMUM_PAGE_SHARE, CRAWL_REPORT_STORAGE_VERSION, CRAWL_RUN_RETENTION, CRAWL_SKIP_REASONS, CTR_DEFAULT_LIMIT, CTR_DEFAULT_MIN_IMPRESSIONS, CTR_MAX_LIMIT, CTR_MAX_MIN_IMPRESSIONS, DATAFORSEO_CREDENTIAL_SECRET, DATAFORSEO_LOGIN_ENV, DATAFORSEO_PASSWORD_ENV, DEFAULT_PROVIDER_SPEND_LIMITS, DataForSeoClient, DataForSeoKeywordDiscoveryProvider, DataForSeoKeywordMetricsProvider, DataForSeoProvider, DataForSeoRankTrackingCollector, DataForSeoSerpSnapshotProvider, GSC_FINAL_DATA_LAG_DAYS, GSC_TIME_ZONE, INDEXNOW_ENDPOINT, INDEXNOW_KEYS_SECRET, INDEXNOW_KEY_ENV, INDEXNOW_MAX_URLS, INDEX_WATCH_ATTEMPT_RETENTION, INTERNAL_LINK_LEXICAL_TARGET_LIMIT, INTERNAL_LINK_MATCH_EVIDENCE_LIMIT, JsonFileStorageAdapter, KEYWORD_SET_FIELD_LIMITS, KEYWORD_SET_LIMITS, KEYWORD_SET_SCHEMA_SQL, LINK_RECOVER_RUN_RETENTION, LOG_MAX_AGE_MS, LOG_MAX_FILE_BYTES, LOG_MAX_TOTAL_BYTES, LocalCredentialsProvider, MAX_CRAWL_CONCURRENCY, MAX_CRAWL_DEPTH, MAX_CRAWL_PAGES, MAX_CRAWL_TIMEOUT_MS, OKF_DEFAULT_CONCEPTS, OKF_MAX_CONCEPTS, PROVIDER_SPEND_MAX_BYTES, PROVIDER_SPEND_MAX_ROWS, PROVIDER_SPEND_RETENTION_DAYS, ProviderError, RANK_TRACKING_LIMITS, RANK_TRACKING_SCHEMA_SQL, REPORT_BASELINE_MAX_AGE_MS, REPORT_BASELINE_MAX_DEPTH, REPORT_BASELINE_MAX_PAGES, ResponseSizeLimitError, SCORECARD_CHECK_WEIGHTS, SCORECARD_STATUS_CREDIT, SEO_CRAWLER_IDENTITY, SEO_CRAWLER_TOKEN, SEO_CRAWLER_USER_AGENT, SEO_VERSION, SERP_TITLE_REFERENCE_WIDTH_PX, SERP_TITLE_WIDTH_PROFILE, SemrushProvider, SeoError, ServiceAccountAccessTokenClient, SessionLedger, TELEMETRY_AGENTS, TELEMETRY_ENDPOINT, TELEMETRY_ERROR_CATEGORIES, TELEMETRY_EVENTS, TELEMETRY_NOTICE, TELEMETRY_REPORTS, TELEMETRY_SCHEMA_VERSION, TELEMETRY_TIMEOUT_MS, acquirePseoAuditEvidence, activeRankTrackingRun, addKeywordsToSet, addStatus, affectedUrls, agentReadiness, aiPromptsForQuery, aiReadiness, aiReferralSourceForValue, aiReferralsReport, aiSearchScorecard, allocateIndexUrlsToProperties, analyticsConnectionsSchema, analyzeAiReferralRows, analyzeBingCrawl, analyzeBingDimensions, analyzeBingTraffic, analyzeCannibalRows, analyzeCtrUnderperformersFromRows, analyzeDecay, analyzeIndexCoverageSignals, analyzeInternalLinksFromRows, analyzeKeywordTrend, analyzePageOpportunitiesFromRows, analyzeQueryClustersFromRows, analyzeQuickWinsFromRows, analyzeSecondPageRows, analyzeSegmentRows, analyzeStrikingDistanceRows, analyzeTrafficAnomalyRows, assertCrawlConfigLimits, assertUrlMatchesGscProperty, attachCrawlRecommendations, auditCrawlPages, auditCrawlRequests, auditLlmsTxt, auditPage, authStatus, bingAnalysisDefaults, bingWebmasterOverview, bingWebmasterSiteUrl, boundedSitemapInventory, buildOkfBundle, buildPseoAuditFromAcquisition, buildPseoAuditReportFromRows, buildQueryClusterReportFromRows, buildTelemetryPayload, cannibalReport, changeMeasurementCsvRows, checkDatabaseReadiness, classifyCommunityIntent, classifyCrawler, clearCache, clientProfileSchema, clusterPseoTemplates, clusterQueryRows, collectAgentDiscovery, collectBingLinkEvidence, combineRobotsValues, communityIntentReport, compareCrawlPages, compareCrawlReports, compareLinkEvidence, compareSegmentRows, competitorKeywordGapReport, configSchema, contentCoverageRecommendation, contentCoverageType, contentOptimizationFromPageOpportunities, contentOptimizationReport, countLabel, crawlConfigHash, crawlConfigSchema, crawlDefinitionId, crawlDiff, crawlFixToQueueItem, crawlImplementationQueueWorkflow, crawlIssueGroupSchema, crawlIssueSchema, crawlOne, crawlPageSnapshotSchema, crawlReportSchema, crawlReportStore, crawlReportSummarySchema, crawlRequestObservationSchema, crawlRunId, crawlSite, crawlSkipImpact, crawlSkipReasonCountsFromRecord, crawlStatusOnly, crawlTopFixSchema, crawlerJsonSchemas, crawlerRuleCategorySchema, crawlerRuleIdSchema, crawlerRuleInfoSchema, crawlerRuleRecommendationSchema, crawlerRuleSeveritySchema, crawlerSchemas, createAuthorizedClient, createBingWebmasterClient, createContentGroup, createCrawlReport, createCtrBenchmarkContext, createGoogleAccessTokenClient, createIndexNowKey, createIndexNowKeyRecord, createKeywordSet, createLogger, createPageRenderer, ctrUnderperformersReport, decayClusterDrafts, decayComparisonRange, decayingReport, deleteChange, deleteClient, deleteContentGroup, deleteCrawlReport, deleteDataForSeoCredentials, deleteKeywordSet, deleteProviderSecret, deleteTokens, deriveBrandTerms, detectBrandTerms, detectTelemetryAgent, diagnoseCsvFiles, diagnoseProperty, diagnosePropertyWorkflow, domainOverviewReport, effectiveRobotsDirectives, effectiveSnippetControl, emptyStatusBreakdown, enforceRankTrackingLogicalBytes, ensurePrivateDatabaseFiles, ensureSeoCliDirs, entityReadiness, estimateSerpTitleWidth, executeRankTracking, expectedCtrForPosition, explainOkfValidation, explainRule, failRankTrackingTask, fetchCruxFieldData, fetchPage, fetchPageStatus, fetchSearchStatusIncidents, fetchSitemapUrls, fetchText, fileSize, finalGscDateRange, finalizeProviderSpend, findOverlappingSearchUpdates, formatRelativeExpiry, ga4MatchReason, ga4PropertyIdFromName, ga4ReportQualityWarnings, ga4RequestCanUseCache, ga4RowsToObjects, generateLlmsTxt, geoGaps, geoGapsReport, getAuthModeStatus, getCacheStats, getChange, getClient, getClientConfig, getContentGroup, getDb, getKeywordProvider, getKeywordSet, getOrCreateRankTrackingConfiguration, getPreviousRun, getPrivacySnapshot, getProviderSpendLimits, getProviderSpendSummary, getRankTrackingRun, getRepeatedLinkRecoverUrls, getRunPages, getSeoCliPaths, getServiceAccountConfig, getServiceAccountStatus, getTelemetryStatus, getTokenStorageStatus, googleAnalyticsPropertyId, groupCrawlIssues, hasMetaRobotsDirective, hasRule, hasXRobotsDirective, hashKey, importLinkEvidence, importServerLog, indexCoveragePlan, indexCoverageSignals, indexMonitor, indexWatch, inferPseoTemplate, inferTemplateMovement, initializeTelemetry, insertCrawlRun, insertLinkRecoverRun, inspectUrl, internalLinksReport, interpretUpdateCorrelation, isBrandQuery, isCompatibleTechnicalBaseline, isCurrentTechnicalBaseline, isEntityEvidenceSchemaType, isSiteIdentitySchemaType, isSkippableReportError, isTelemetryCi, isTelemetryReport, isoDate, isoWeek, keywordDiscoverySourceSchema, keywordMetricsReport, keywordOpportunitiesReport, keywordResearchReport, keywordSetLogicalBytes, latestCrawlReport, latestCrawlSummaries, latestFinalGscDate, latestIndexWatchSummary, latestLinkRecoverSummary, latestRankTrackingRun, linkEntries, linkEvidenceKey, linkEvidenceReport, linkRecover, linkRecoverRecommendation, listChanges, listClients, listContentGroups, listCrawlReports, listGa4AccountSummaries, listGa4DataStreams, listIndexNowKeys, listKeywordSets, listRules, listSearchUpdates, listSites, loadCrawlReport, localCredentialsProvider, loginWithLoopback, makeRunId, mapDataForSeoSerpSnapshot, markRankTasksPosted, markRankTasksPosting, matchGa4WebStreams, measureChange, measureCoverage, metaRobotsDirectives, migrateLegacyMacConfig, missingOAuthClientMessage, missingSharedClientLoginMessage, monitoringStatus, monthlyReport, monthlyReportWorkflow, narrativeCsvFiles, normalizeCrawlConfig, normalizeCrawlSkipReasonCounts, normalizeForCoverage, normalizeHttpUrl, normalizeJavaScriptRenderingMode, normalizeLinkEvidenceRow, normalizeLoadedCrawlReport, normalizeSavedKeyword, noteCacheWrite, observedValue, okfConceptLimit, pageEvidenceRecommendation, pageOpportunitiesReport, parseCombinedLogLine, parseIndexNowSite, parseJsonLogLine, parseOlderThan, parsePseoPath, performanceAudit, performanceReportIsCacheable, planDueIndexUrls, planIndexCoverageFromUrls, plural, priorComparableRankTrackingRun, propertyMatchesUrl, providerCapabilitySchema, providerIdSchema, providerPreferenceSchema, providerRequestJson, providerRequestText, providerSpendLimitOverridesSchema, pruneIndexWatchSnapshots, pruneLogs, pseoAuditOptions, pseoAuditReport, pseoCsvFiles, pseoIndexStatus, pseoOpportunitiesReport, pseoPresentation, publicHttpFetch, queryClusterReport, queryClusterTokens, queryContentCoverageFromPage, queryContentFieldsFromPage, queryFieldCoverage, queryOpportunityRecommendation, queryPageMetrics, queryPageTopQuery, queryPagesMetrics, queryPagesMetricsBatch, queryPagesTopQueries, queryPagesTopQueriesBatch, querySearchAnalytics, quickWinsCsvRows, quickWinsReport, rankObservations, rankTrackingLogicalBytes, rankTrackingReport, rankTrackingTasks, rankedKeywordsReport, rankingPagesReport, readBoundedResponseText, readConfig, readDataForSeoCredentials, readOauthClient, readProviderSecret, readTokens, recommendCrawlDiffItem, recommendationForRule, recordChange, recoverRankTaskReceipt, redirectTrace, refreshAuthToken, refreshKeywordSet, refreshPrioritiesCsvFiles, refreshPrioritiesWorkflow, refreshRankTrackingRun, removeIndexNowKey, removeKeywordsFromSet, renderCrawlCsv, renderCrawlHtml, renderCrawlJunit, renderCrawlMarkdownTickets, renderCrawlPagesCsv, renderCrawlPretty, renderCsv, renderPresentationTablesMarkdown, renderPseoMarkdown, renderReportHtml, renderWorkflowMarkdown, reportNarrative, reportPresentation, requestHeaders, reserveProviderSpend, resolveIndexNowKey, resolveProvider, resolveSegmentRanges, resolveSeoCliPaths, resolveTechnicalBaseline, reviewObservations, roundedPosition, runDoctor, runGa4Report, safeError, saveClient, saveCrawlReport, saveIndexNowKey, saveRankObservation, saveRankObservations, savedKeywordSetReport, searchMarketSchema, secondPage, segmentComparisonRange, segmentImpact, segmentRangeDays, selectAffectedUrls, selectDueIndexUrls, seoErrorEnvelope, seoToAiQueryReport, serpCompetitorsReport, serpResultsReport, serverLogReport, sessionSourceInListFilter, setClientBingSite, setDefaultClient, setKeywordSetRefreshTime, setProviderSpendLimits, setTelemetryEnabled, setTokenStorageMode, shiftIsoDate, shouldExcludeBrandQuery, siteSchema, sqliteCrawlReportStore, startRankTrackingRun, strikingDistance, submitIndexNow, summarizeCrawlReport, targetMatchesDomain, pageTechnicalSignals as technicalPageSignals, technicalWatchWorkflow, telemetryErrorCategory, templateForUrl, toSeoError, tokenSchema, topCrawlRecommendations, topFixes, trackTelemetryReportComplete, trackTelemetryReportFailed, trackTelemetryReportStart, trackTelemetrySetupComplete, trafficAnomaly, unavailableSegmentImpactReport, unavailableValue, unicodeTokens, updateCorrelation, updatePostmortemCsvFiles, updatePostmortemWorkflow, validateIndexNowKey, validateIndexNowKeyRecord, validateOkfFiles, verifyIndexNowKey, verifyQueryContent, workflowPresentation, writeConfig, writeDataForSeoCredentials, writeOauthClient, writeProviderSecret, writeTokens, xRobotsDirectives };
|
|
43334
|
+
export { AI_REFERRAL_SOURCES, AI_REFERRAL_SOURCES_VERSION, AI_SEARCH_SCORECARD_METHODOLOGY_ID, AI_SEARCH_SCORECARD_METHODOLOGY_VERSION, AUDIT_PAGE_RULE_IDS, AUTOMATIC_BASELINE_SITE_RETENTION, AUTOMATIC_BASELINE_TOTAL_RETENTION, BING_API_KEY_ENV, BING_API_KEY_SECRET, BROWSER_USER_AGENT, BingWebmasterClient, CANNIBAL_MAXIMUM_DOMINANT_SHARE, CANNIBAL_MINIMUM_PAGE_SHARE, CRAWL_REPORT_STORAGE_VERSION, CRAWL_RUN_RETENTION, CRAWL_SKIP_REASONS, CTR_DEFAULT_LIMIT, CTR_DEFAULT_MIN_IMPRESSIONS, CTR_MAX_LIMIT, CTR_MAX_MIN_IMPRESSIONS, DATAFORSEO_CREDENTIAL_SECRET, DATAFORSEO_LOGIN_ENV, DATAFORSEO_PASSWORD_ENV, DEFAULT_PROVIDER_SPEND_LIMITS, DataForSeoClient, DataForSeoKeywordDiscoveryProvider, DataForSeoKeywordMetricsProvider, DataForSeoLinkProvider, DataForSeoProvider, DataForSeoRankTrackingCollector, DataForSeoSerpSnapshotProvider, GSC_FINAL_DATA_LAG_DAYS, GSC_TIME_ZONE, INDEXNOW_ENDPOINT, INDEXNOW_KEYS_SECRET, INDEXNOW_KEY_ENV, INDEXNOW_MAX_URLS, INDEX_WATCH_ATTEMPT_RETENTION, INTERNAL_LINK_LEXICAL_TARGET_LIMIT, INTERNAL_LINK_MATCH_EVIDENCE_LIMIT, JsonFileStorageAdapter, KEYWORD_SET_FIELD_LIMITS, KEYWORD_SET_LIMITS, KEYWORD_SET_SCHEMA_SQL, LINK_RECOVER_RUN_RETENTION, LOG_MAX_AGE_MS, LOG_MAX_FILE_BYTES, LOG_MAX_TOTAL_BYTES, LocalCredentialsProvider, MAX_CRAWL_CONCURRENCY, MAX_CRAWL_DEPTH, MAX_CRAWL_PAGES, MAX_CRAWL_TIMEOUT_MS, OKF_DEFAULT_CONCEPTS, OKF_MAX_CONCEPTS, PROVIDER_SPEND_MAX_BYTES, PROVIDER_SPEND_MAX_ROWS, PROVIDER_SPEND_RETENTION_DAYS, ProviderError, RANK_TRACKING_LIMITS, RANK_TRACKING_SCHEMA_SQL, REPORT_BASELINE_MAX_AGE_MS, REPORT_BASELINE_MAX_DEPTH, REPORT_BASELINE_MAX_PAGES, ResponseSizeLimitError, SCORECARD_CHECK_WEIGHTS, SCORECARD_STATUS_CREDIT, SEO_CRAWLER_IDENTITY, SEO_CRAWLER_TOKEN, SEO_CRAWLER_USER_AGENT, SEO_VERSION, SERP_TITLE_REFERENCE_WIDTH_PX, SERP_TITLE_WIDTH_PROFILE, SemrushProvider, SeoError, ServiceAccountAccessTokenClient, SessionLedger, TELEMETRY_AGENTS, TELEMETRY_ENDPOINT, TELEMETRY_ERROR_CATEGORIES, TELEMETRY_EVENTS, TELEMETRY_NOTICE, TELEMETRY_REPORTS, TELEMETRY_SCHEMA_VERSION, TELEMETRY_TIMEOUT_MS, acquirePseoAuditEvidence, activeRankTrackingRun, addKeywordsToSet, addStatus, affectedUrls, agentReadiness, aiPromptsForQuery, aiReadiness, aiReferralSourceForValue, aiReferralsReport, aiSearchScorecard, allocateIndexUrlsToProperties, analyticsConnectionsSchema, analyzeAiReferralRows, analyzeBingCrawl, analyzeBingDimensions, analyzeBingTraffic, analyzeCannibalRows, analyzeCtrUnderperformersFromRows, analyzeDecay, analyzeIndexCoverageSignals, analyzeInternalLinksFromRows, analyzeKeywordTrend, analyzePageOpportunitiesFromRows, analyzeQueryClustersFromRows, analyzeQuickWinsFromRows, analyzeSecondPageRows, analyzeSegmentRows, analyzeStrikingDistanceRows, analyzeTrafficAnomalyRows, assertCrawlConfigLimits, assertUrlMatchesGscProperty, attachCrawlRecommendations, auditCrawlPages, auditCrawlRequests, auditLlmsTxt, auditPage, authStatus, bingAnalysisDefaults, bingWebmasterOverview, bingWebmasterSiteUrl, boundedSitemapInventory, buildOkfBundle, buildPseoAuditFromAcquisition, buildPseoAuditReportFromRows, buildQueryClusterReportFromRows, buildTelemetryPayload, cannibalReport, changeMeasurementCsvRows, checkDatabaseReadiness, classifyCommunityIntent, classifyCrawler, clearCache, clientProfileSchema, clusterPseoTemplates, clusterQueryRows, collectAgentDiscovery, collectBingLinkEvidence, collectDataForSeoLinkEvidence, combineRobotsValues, communityIntentReport, compareCrawlPages, compareCrawlReports, compareLinkEvidence, compareSegmentRows, competitorKeywordGapReport, configSchema, contentCoverageRecommendation, contentCoverageType, contentOptimizationFromPageOpportunities, contentOptimizationReport, countLabel, crawlConfigHash, crawlConfigSchema, crawlDefinitionId, crawlDiff, crawlFixToQueueItem, crawlImplementationQueueWorkflow, crawlIssueGroupSchema, crawlIssueSchema, crawlOne, crawlPageSnapshotSchema, crawlReportSchema, crawlReportStore, crawlReportSummarySchema, crawlRequestObservationSchema, crawlRunId, crawlSite, crawlSkipImpact, crawlSkipReasonCountsFromRecord, crawlStatusOnly, crawlTopFixSchema, crawlerJsonSchemas, crawlerRuleCategorySchema, crawlerRuleIdSchema, crawlerRuleInfoSchema, crawlerRuleRecommendationSchema, crawlerRuleSeveritySchema, crawlerSchemas, createAuthorizedClient, createBingWebmasterClient, createContentGroup, createCrawlReport, createCtrBenchmarkContext, createGoogleAccessTokenClient, createIndexNowKey, createIndexNowKeyRecord, createKeywordSet, createLogger, createPageRenderer, ctrUnderperformersReport, decayClusterDrafts, decayComparisonRange, decayingReport, deleteChange, deleteClient, deleteContentGroup, deleteCrawlReport, deleteDataForSeoCredentials, deleteKeywordSet, deleteProviderSecret, deleteTokens, deriveBrandTerms, detectBrandTerms, detectTelemetryAgent, diagnoseCsvFiles, diagnoseProperty, diagnosePropertyWorkflow, domainOverviewReport, effectiveRobotsDirectives, effectiveSnippetControl, emptyStatusBreakdown, enforceRankTrackingLogicalBytes, ensurePrivateDatabaseFiles, ensureSeoCliDirs, entityReadiness, estimateSerpTitleWidth, executeRankTracking, expectedCtrForPosition, explainOkfValidation, explainRule, failRankTrackingTask, fetchCruxFieldData, fetchPage, fetchPageStatus, fetchSearchStatusIncidents, fetchSitemapUrls, fetchText, fileSize, finalGscDateRange, finalizeProviderSpend, findOverlappingSearchUpdates, formatRelativeExpiry, ga4MatchReason, ga4PropertyIdFromName, ga4ReportQualityWarnings, ga4RequestCanUseCache, ga4RowsToObjects, generateLlmsTxt, geoGaps, geoGapsReport, getAuthModeStatus, getCacheStats, getChange, getClient, getClientConfig, getContentGroup, getDb, getKeywordProvider, getKeywordSet, getOrCreateRankTrackingConfiguration, getPreviousRun, getPrivacySnapshot, getProviderSpendLimits, getProviderSpendSummary, getRankTrackingRun, getRepeatedLinkRecoverUrls, getRunPages, getSeoCliPaths, getServiceAccountConfig, getServiceAccountStatus, getTelemetryStatus, getTokenStorageStatus, googleAnalyticsPropertyId, groupCrawlIssues, hasMetaRobotsDirective, hasRule, hasXRobotsDirective, hashKey, importLinkEvidence, importServerLog, indexCoveragePlan, indexCoverageSignals, indexMonitor, indexWatch, inferPseoTemplate, inferTemplateMovement, initializeTelemetry, insertCrawlRun, insertLinkRecoverRun, inspectUrl, internalLinksReport, interpretUpdateCorrelation, isBrandQuery, isCompatibleTechnicalBaseline, isCurrentTechnicalBaseline, isEntityEvidenceSchemaType, isSiteIdentitySchemaType, isSkippableReportError, isTelemetryCi, isTelemetryReport, isoDate, isoWeek, keywordDiscoverySourceSchema, keywordMetricsReport, keywordOpportunitiesReport, keywordResearchReport, keywordSetLogicalBytes, latestCrawlReport, latestCrawlSummaries, latestFinalGscDate, latestIndexWatchSummary, latestLinkRecoverSummary, latestRankTrackingRun, linkEntries, linkEvidenceKey, linkEvidenceReport, linkRecover, linkRecoverRecommendation, linkTargetContext, listChanges, listClients, listContentGroups, listCrawlReports, listGa4AccountSummaries, listGa4DataStreams, listIndexNowKeys, listKeywordSets, listRules, listSearchUpdates, listSites, loadCrawlReport, localCredentialsProvider, loginWithLoopback, makeRunId, mapDataForSeoSerpSnapshot, markRankTasksPosted, markRankTasksPosting, matchGa4WebStreams, measureChange, measureCoverage, metaRobotsDirectives, migrateLegacyMacConfig, missingOAuthClientMessage, missingSharedClientLoginMessage, monitoringStatus, monthlyReport, monthlyReportWorkflow, narrativeCsvFiles, normalizeCrawlConfig, normalizeCrawlSkipReasonCounts, normalizeForCoverage, normalizeHttpUrl, normalizeJavaScriptRenderingMode, normalizeLinkEvidenceRow, normalizeLoadedCrawlReport, normalizeSavedKeyword, noteCacheWrite, observedValue, okfConceptLimit, pageEvidenceRecommendation, pageOpportunitiesReport, parseCombinedLogLine, parseIndexNowSite, parseJsonLogLine, parseOlderThan, parsePseoPath, performanceAudit, performanceReportIsCacheable, planDueIndexUrls, planIndexCoverageFromUrls, plural, priorComparableRankTrackingRun, propertyMatchesUrl, providerCapabilitySchema, providerIdSchema, providerPreferenceSchema, providerRequestJson, providerRequestText, providerSpendLimitOverridesSchema, pruneIndexWatchSnapshots, pruneLogs, pseoAuditOptions, pseoAuditReport, pseoCsvFiles, pseoIndexStatus, pseoOpportunitiesReport, pseoPresentation, publicHttpFetch, queryClusterReport, queryClusterTokens, queryContentCoverageFromPage, queryContentFieldsFromPage, queryFieldCoverage, queryOpportunityRecommendation, queryPageMetrics, queryPageTopQuery, queryPagesMetrics, queryPagesMetricsBatch, queryPagesTopQueries, queryPagesTopQueriesBatch, querySearchAnalytics, quickWinsCsvRows, quickWinsReport, rankObservations, rankTrackingLogicalBytes, rankTrackingReport, rankTrackingTasks, rankedKeywordsReport, rankingPagesReport, readBoundedResponseText, readConfig, readDataForSeoCredentials, readOauthClient, readProviderSecret, readTokens, recommendCrawlDiffItem, recommendationForRule, recordChange, recoverRankTaskReceipt, redirectTrace, refreshAuthToken, refreshKeywordSet, refreshPrioritiesCsvFiles, refreshPrioritiesWorkflow, refreshRankTrackingRun, removeIndexNowKey, removeKeywordsFromSet, renderCrawlCsv, renderCrawlHtml, renderCrawlJunit, renderCrawlMarkdownTickets, renderCrawlPagesCsv, renderCrawlPretty, renderCsv, renderPresentationTablesMarkdown, renderPseoMarkdown, renderReportHtml, renderWorkflowMarkdown, reportNarrative, reportPresentation, requestHeaders, reserveProviderSpend, resolveIndexNowKey, resolveProvider, resolveSegmentRanges, resolveSeoCliPaths, resolveTechnicalBaseline, reviewObservations, roundedPosition, runDoctor, runGa4Report, safeError, saveClient, saveCrawlReport, saveIndexNowKey, saveRankObservation, saveRankObservations, savedKeywordSetReport, searchMarketSchema, secondPage, segmentComparisonRange, segmentImpact, segmentRangeDays, selectAffectedUrls, selectDueIndexUrls, seoErrorEnvelope, seoToAiQueryReport, serpCompetitorsReport, serpResultsReport, serverLogReport, sessionSourceInListFilter, setClientBingSite, setDefaultClient, setKeywordSetRefreshTime, setProviderSpendLimits, setTelemetryEnabled, setTokenStorageMode, shiftIsoDate, shouldExcludeBrandQuery, siteSchema, sqliteCrawlReportStore, startRankTrackingRun, strikingDistance, submitIndexNow, summarizeCrawlReport, targetMatchesDomain, pageTechnicalSignals as technicalPageSignals, technicalWatchWorkflow, telemetryErrorCategory, templateForUrl, toSeoError, tokenSchema, topCrawlRecommendations, topFixes, trackTelemetryReportComplete, trackTelemetryReportFailed, trackTelemetryReportStart, trackTelemetrySetupComplete, trafficAnomaly, unavailableSegmentImpactReport, unavailableValue, unicodeTokens, updateCorrelation, updatePostmortemCsvFiles, updatePostmortemWorkflow, validateIndexNowKey, validateIndexNowKeyRecord, validateOkfFiles, verifyIndexNowKey, verifyQueryContent, workflowPresentation, writeConfig, writeDataForSeoCredentials, writeOauthClient, writeProviderSecret, writeTokens, xRobotsDirectives };
|
|
42187
43335
|
|
|
42188
43336
|
//# sourceMappingURL=index.js.map
|