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/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-DvJj2-vq.js";
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$16 = {
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$16) {
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$15 = {
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$15) {
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$14 = { runGa4Report };
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$14) {
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$13 = {
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$13) {
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$4 = 1e5;
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$12 = {
18621
+ const defaultDependencies$13 = {
18622
18622
  searchAnalytics: querySearchAnalytics,
18623
18623
  now: () => /* @__PURE__ */ new Date()
18624
18624
  };
18625
- async function ctrUnderperformersReport(input, dependencies = defaultDependencies$12) {
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$4
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$4;
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$4,
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$4} was reached, so lower-ranked source rows may be absent.`] : [],
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$11 = {
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$11) {
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$3 = 1e5;
19825
- const defaultDependencies$10 = {
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$10) {
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$3
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$3;
19892
- const propertyTruncated = propertyDemand.rowsFetched >= MAX_GSC_ROWS$3;
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$3,
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$3,
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$2 = 1e5;
20238
- const defaultDependencies$9 = {
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$9) {
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$2
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$2;
20326
- const previousTruncated = previous.rowsFetched >= MAX_GSC_ROWS$2;
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$2,
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$2,
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$2 = 28;
20736
- const MAX_DAYS$2 = 548;
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$8 = {
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$8) {
20760
- const explicitRange = explicitDateRange(input, MAX_DAYS$2);
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$2,
20763
+ fallback: DEFAULT_DAYS$3,
20764
20764
  minimum: 1,
20765
- maximum: MAX_DAYS$2,
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$1 = 28;
21133
- const MAX_DAYS$1 = 548;
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$7 = {
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$1;
21147
- if (!Number.isInteger(value) || value < 1 || value > MAX_DAYS$1) throw new SeoError("INVALID_INPUT", `Days must be a whole number between 1 and ${MAX_DAYS$1}.`);
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$7) {
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$6 = {
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$6,
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$1 = z.union([z.number().finite(), z.string().trim().min(1)]).nullable().optional();
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$1 }).nullish(),
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
- keywordOverviewPrice: keywordOverviewPrice(account),
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$1(field) {
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$1(field);
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$1(field);
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$1("monthlySearches");
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$1("monthlySearchVolume"),
24595
- monthlySearches: missing$1("monthlySearches"),
24596
- searchVolumeUpdatedAt: missing$1("searchVolumeUpdatedAt"),
24597
- cpcUsd: missing$1("cpcUsd"),
24598
- paidCompetition: missing$1("paidCompetition"),
24599
- keywordDifficulty: missing$1("keywordDifficulty"),
24600
- intent: missing$1("intent"),
24601
- resultCount: missing$1("resultCount")
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$1(value) {
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$1(serp?.url);
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$1(row.page_address);
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$5 = {
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$5) {
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$1 = 1e5;
27978
- const defaultDependencies$4 = {
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$4) {
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$1,
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$1
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$1);
28111
- const sourceTruncated = sourceResult.rowsFetched >= MAX_GSC_ROWS$1;
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$1
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$1,
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$1,
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$1
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$3 = {
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$3) {
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$2 = {
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$2) {
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$1 = {
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$1) {
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$1) {
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$2 = 500;
40438
- const MAX_ROW_LIMIT$2 = 1e3;
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$2, MAX_ROW_LIMIT$2, "Bing link 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.localeCompare(b.targetUrl, "en"));
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: 1,
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
- targetCounts: counts.slice(0, 100),
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
- warnings: input.evidence.warnings,
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