seo 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PRIVACY.md +21 -6
- package/README.md +13 -14
- package/dist/cli.js +84 -85
- package/dist/cli.js.map +1 -1
- package/dist/{dist-BIlBtrjd.js → dist-C21OaeU2.js} +1 -1
- package/dist/{dist-CN4VawW-.js → dist-DVFMlaqF.js} +84 -80
- package/dist/dist-DVFMlaqF.js.map +1 -0
- package/dist/index.d.ts +30 -11
- package/dist/index.js +539 -98
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +882 -177
- package/dist/mcp.js.map +1 -1
- package/dist/{package-CsRkLZGn.js → package-CmplBMg7.js} +2 -2
- package/dist/{package-CsRkLZGn.js.map → package-CmplBMg7.js.map} +1 -1
- package/package.json +3 -2
- package/skills/seo/SKILL.md +2 -2
- package/dist/dist-CN4VawW-.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire as __seoCreateRequire } from 'node:module'; const require = __seoCreateRequire(import.meta.url);
|
|
2
|
-
import { n as load, t as version } from "./package-
|
|
2
|
+
import { n as load, t as version } from "./package-CmplBMg7.js";
|
|
3
3
|
import { SEO_PROVIDER_API_VERSION } from "./provider-sdk.js";
|
|
4
4
|
import crypto, { createHash, randomUUID, sign } from "node:crypto";
|
|
5
5
|
import { z } from "zod";
|
|
@@ -1598,7 +1598,7 @@ async function runAnalyticsReport(gfetch, propertyId, body, options) {
|
|
|
1598
1598
|
//#endregion
|
|
1599
1599
|
//#region packages/core/src/bing/api.ts
|
|
1600
1600
|
const BASE = "https://ssl.bing.com/webmaster/api.svc/json";
|
|
1601
|
-
const MAX_BYTES$
|
|
1601
|
+
const MAX_BYTES$2 = 8e6;
|
|
1602
1602
|
function bingCall(auth, options) {
|
|
1603
1603
|
return async (method, params = {}) => {
|
|
1604
1604
|
const url = new URL(`${BASE}/${method}`);
|
|
@@ -1623,7 +1623,7 @@ function bingCall(auth, options) {
|
|
|
1623
1623
|
if (response.status === 429 || /throttle/i.test(detail)) throw new SeoError("RATE_LIMITED", "Bing Webmaster is limiting requests. Try again in a few minutes.");
|
|
1624
1624
|
throw new SeoError("OPTIONAL_PROVIDER_UNAVAILABLE", `Bing Webmaster answered ${response.status}. ${detail}`.trim());
|
|
1625
1625
|
}
|
|
1626
|
-
return (await readJson(response, "Bing Webmaster", { maxBytes: MAX_BYTES$
|
|
1626
|
+
return (await readJson(response, "Bing Webmaster", { maxBytes: MAX_BYTES$2 })).d;
|
|
1627
1627
|
};
|
|
1628
1628
|
}
|
|
1629
1629
|
//#endregion
|
|
@@ -2088,13 +2088,13 @@ let credentials;
|
|
|
2088
2088
|
function expiresSoon(value) {
|
|
2089
2089
|
return [...value.google, ...value.bing].some((item) => "expiresAt" in item && item.expiresAt - EARLY < Date.now());
|
|
2090
2090
|
}
|
|
2091
|
-
async function cloudCredentials() {
|
|
2091
|
+
async function cloudCredentials(options = {}) {
|
|
2092
2092
|
const current = credentials ? await credentials.catch(() => null) : null;
|
|
2093
|
-
if (current && !expiresSoon(current)) return current;
|
|
2093
|
+
if (current && !expiresSoon(current) && !options.refreshClicky) return current;
|
|
2094
2094
|
credentials = (async () => {
|
|
2095
2095
|
const token = cloudToken();
|
|
2096
2096
|
if (!token) throw new SeoError("AUTH_REQUIRED", notSignedIn());
|
|
2097
|
-
const result = await call$1(
|
|
2097
|
+
const result = await call$1(`/account/cli/credentials${options.refreshClicky ? "?clicky=refresh" : ""}`, { token });
|
|
2098
2098
|
if (result.status === 401) throw new SeoError("AUTH_EXPIRED", `This machine is not linked to a seoskill.dev account any more. ${CLOUD_SIGN_IN}`);
|
|
2099
2099
|
if (!result.ok) throw new SeoError("PROVIDER_UNAVAILABLE", `seoskill.dev returned ${result.status}. Try again in a minute.`);
|
|
2100
2100
|
return result.body;
|
|
@@ -4694,7 +4694,7 @@ function extractCanonicalEvidence($, headers, baseUrl) {
|
|
|
4694
4694
|
//#region packages/core/src/extract/content-sketch.ts
|
|
4695
4695
|
const SHINGLE_SIZE = 5;
|
|
4696
4696
|
const MAX_HASHES = 32;
|
|
4697
|
-
function words(value) {
|
|
4697
|
+
function words$1(value) {
|
|
4698
4698
|
return value.normalize("NFKC").toLocaleLowerCase("en").match(/[\p{L}\p{N}]+/gu) ?? [];
|
|
4699
4699
|
}
|
|
4700
4700
|
function sampleIndexes(total, limit) {
|
|
@@ -4705,7 +4705,7 @@ function digest(value) {
|
|
|
4705
4705
|
return createHash("sha256").update(value).digest("hex").slice(0, 16);
|
|
4706
4706
|
}
|
|
4707
4707
|
function contentSketch(value) {
|
|
4708
|
-
const tokens = words(value);
|
|
4708
|
+
const tokens = words$1(value);
|
|
4709
4709
|
const hashes = sampleIndexes(Math.max(0, tokens.length - SHINGLE_SIZE + 1), MAX_HASHES).map((index) => digest(tokens.slice(index, index + SHINGLE_SIZE).join(" ")));
|
|
4710
4710
|
return {
|
|
4711
4711
|
version: 1,
|
|
@@ -4717,6 +4717,11 @@ function contentSketch(value) {
|
|
|
4717
4717
|
}
|
|
4718
4718
|
//#endregion
|
|
4719
4719
|
//#region packages/core/src/extract/main-content.ts
|
|
4720
|
+
function parseDocument$1(html) {
|
|
4721
|
+
const { document } = parseHTML(html);
|
|
4722
|
+
if (document.documentElement?.localName === "html") return document;
|
|
4723
|
+
return parseHTML(`<!doctype html><html><head></head><body>${html}</body></html>`).document;
|
|
4724
|
+
}
|
|
4720
4725
|
function readableError(error) {
|
|
4721
4726
|
return error instanceof Error ? error.message : String(error);
|
|
4722
4727
|
}
|
|
@@ -4746,10 +4751,6 @@ function resolveDocumentUrls(document, baseUrl) {
|
|
|
4746
4751
|
}
|
|
4747
4752
|
}
|
|
4748
4753
|
function readabilityResult(fetchResult, requested, fallbackReason, fallbackDetail) {
|
|
4749
|
-
const { document } = parseHTML(fetchResult.html);
|
|
4750
|
-
const bodyText = document.body.textContent ?? "";
|
|
4751
|
-
const article = new Readability(document).parse();
|
|
4752
|
-
const text = article?.textContent ?? bodyText;
|
|
4753
4754
|
const fallback = requested !== "readability";
|
|
4754
4755
|
const diagnostics = {
|
|
4755
4756
|
requested,
|
|
@@ -4762,13 +4763,27 @@ function readabilityResult(fetchResult, requested, fallbackReason, fallbackDetai
|
|
|
4762
4763
|
fallbackDetail
|
|
4763
4764
|
} : {}
|
|
4764
4765
|
};
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4766
|
+
const warnings = fallback && fallbackDetail ? [`Defuddle extraction fell back to Readability: ${fallbackDetail}`] : [];
|
|
4767
|
+
try {
|
|
4768
|
+
const document = parseDocument$1(fetchResult.html);
|
|
4769
|
+
const bodyText = document.body?.textContent ?? "";
|
|
4770
|
+
const article = new Readability(document).parse();
|
|
4771
|
+
const text = article?.textContent ?? bodyText;
|
|
4772
|
+
return {
|
|
4773
|
+
text,
|
|
4774
|
+
excerpt: normalizedText(article?.excerpt),
|
|
4775
|
+
wordCount: countCjkAwareWords(text),
|
|
4776
|
+
diagnostics,
|
|
4777
|
+
warnings
|
|
4778
|
+
};
|
|
4779
|
+
} catch (error) {
|
|
4780
|
+
return {
|
|
4781
|
+
text: "",
|
|
4782
|
+
wordCount: 0,
|
|
4783
|
+
diagnostics,
|
|
4784
|
+
warnings: [...warnings, `The page's main content could not be read: ${readableError(error)}`]
|
|
4785
|
+
};
|
|
4786
|
+
}
|
|
4772
4787
|
}
|
|
4773
4788
|
function defaultParseDefuddle(document, options) {
|
|
4774
4789
|
return new Defuddle(document, options).parse();
|
|
@@ -4776,13 +4791,13 @@ function defaultParseDefuddle(document, options) {
|
|
|
4776
4791
|
function extractMainContent(fetchResult, extractor = "defuddle", dependencies = {}) {
|
|
4777
4792
|
if (extractor === "readability") return readabilityResult(fetchResult, extractor);
|
|
4778
4793
|
try {
|
|
4779
|
-
const
|
|
4794
|
+
const document = parseDocument$1(fetchResult.html);
|
|
4780
4795
|
resolveDocumentUrls(document, fetchResult.finalUrl);
|
|
4781
4796
|
const article = (dependencies.parseDefuddle ?? defaultParseDefuddle)(document, {
|
|
4782
4797
|
url: fetchResult.finalUrl,
|
|
4783
4798
|
useAsync: false
|
|
4784
4799
|
});
|
|
4785
|
-
const text = typeof article.content === "string" ? load(article.content).text() : document.body
|
|
4800
|
+
const text = typeof article.content === "string" ? load(article.content).text() : document.body?.textContent ?? "";
|
|
4786
4801
|
if (!text.trim()) return readabilityResult(fetchResult, extractor, "defuddle_empty", "Defuddle returned no main content");
|
|
4787
4802
|
const reportedWordCount = article.wordCount;
|
|
4788
4803
|
const hasValidWordCount = typeof reportedWordCount === "number" && Number.isFinite(reportedWordCount) && reportedWordCount >= 0;
|
|
@@ -4805,6 +4820,28 @@ function extractMainContent(fetchResult, extractor = "defuddle", dependencies =
|
|
|
4805
4820
|
return readabilityResult(fetchResult, extractor, "defuddle_error", readableError(error));
|
|
4806
4821
|
}
|
|
4807
4822
|
}
|
|
4823
|
+
const MARKDOWN_LIMIT = 2e4;
|
|
4824
|
+
async function extractMainMarkdown(fetchResult) {
|
|
4825
|
+
const document = parseDocument$1(fetchResult.html);
|
|
4826
|
+
resolveDocumentUrls(document, fetchResult.finalUrl);
|
|
4827
|
+
const { Defuddle: toMarkdown } = await import("defuddle/node");
|
|
4828
|
+
const markdown = ((await toMarkdown(document, fetchResult.finalUrl, {
|
|
4829
|
+
markdown: true,
|
|
4830
|
+
useAsync: false
|
|
4831
|
+
})).content ?? "").trim();
|
|
4832
|
+
if (markdown.length <= 2e4) return {
|
|
4833
|
+
markdown,
|
|
4834
|
+
characters: markdown.length,
|
|
4835
|
+
truncated: false
|
|
4836
|
+
};
|
|
4837
|
+
const cut = markdown.slice(0, MARKDOWN_LIMIT);
|
|
4838
|
+
const end = cut.lastIndexOf("\n");
|
|
4839
|
+
return {
|
|
4840
|
+
markdown: (end > 2e4 / 2 ? cut.slice(0, end) : cut).trimEnd(),
|
|
4841
|
+
characters: markdown.length,
|
|
4842
|
+
truncated: true
|
|
4843
|
+
};
|
|
4844
|
+
}
|
|
4808
4845
|
//#endregion
|
|
4809
4846
|
//#region packages/core/src/extract/google-rich-results.ts
|
|
4810
4847
|
const SUPPORTED_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -8639,7 +8676,7 @@ const QUERY_FLAG_METHOD = {
|
|
|
8639
8676
|
"navigational-other": "Names a domain that is not the property's host, ends with a spoken domain suffix or a login/site/app word, starts with a large platform name and adds no topic word, or is one short ASCII word that is not a brand term. Such queries usually look for another site."
|
|
8640
8677
|
}
|
|
8641
8678
|
};
|
|
8642
|
-
const FUNCTION_WORDS = /* @__PURE__ */ new Set([
|
|
8679
|
+
const FUNCTION_WORDS$1 = /* @__PURE__ */ new Set([
|
|
8643
8680
|
"a",
|
|
8644
8681
|
"about",
|
|
8645
8682
|
"an",
|
|
@@ -8704,7 +8741,7 @@ function isLikelyAutomatedQuery(query) {
|
|
|
8704
8741
|
if (query.trim().split(/\s+/).filter(Boolean).some((token) => IDENTIFIER_TOKEN.test(token))) return true;
|
|
8705
8742
|
const tokens = tokenize(query);
|
|
8706
8743
|
if (tokens.length >= 20) return true;
|
|
8707
|
-
if (tokens.length >= 8 && !/[^\p{ASCII}]/u.test(query) && !tokens.some((token) => FUNCTION_WORDS.has(token))) return true;
|
|
8744
|
+
if (tokens.length >= 8 && !/[^\p{ASCII}]/u.test(query) && !tokens.some((token) => FUNCTION_WORDS$1.has(token))) return true;
|
|
8708
8745
|
return false;
|
|
8709
8746
|
}
|
|
8710
8747
|
const FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
@@ -14518,6 +14555,9 @@ async function storedSiteKeys() {
|
|
|
14518
14555
|
source: credential.source
|
|
14519
14556
|
};
|
|
14520
14557
|
}
|
|
14558
|
+
async function readClickySiteKeys() {
|
|
14559
|
+
return (await storedSiteKeys()).siteKeys;
|
|
14560
|
+
}
|
|
14521
14561
|
async function readClickySiteKey(siteIdValue, input = {}) {
|
|
14522
14562
|
const siteId = normalizeClickySiteId(siteIdValue);
|
|
14523
14563
|
const environmentKey = (input.env ?? process.env)[CLICKY_SITEKEY_ENV]?.trim();
|
|
@@ -16679,7 +16719,7 @@ function analyzeCtrUnderperformersFromRows(input) {
|
|
|
16679
16719
|
//#endregion
|
|
16680
16720
|
//#region packages/core/src/analyze/ctr-underperformers-report.ts
|
|
16681
16721
|
const MAX_GSC_ROWS$3 = 1e5;
|
|
16682
|
-
const DEFAULT_DAYS$
|
|
16722
|
+
const DEFAULT_DAYS$4 = 28;
|
|
16683
16723
|
const MAX_DAYS$2 = 548;
|
|
16684
16724
|
function plural$4(count, singular, pluralLabel = `${singular}s`) {
|
|
16685
16725
|
return count === 1 ? singular : pluralLabel;
|
|
@@ -16702,7 +16742,7 @@ async function ctrUnderperformersReportWith(input, dependencies) {
|
|
|
16702
16742
|
const explicitRange = explicitDateRange(input, MAX_DAYS$2);
|
|
16703
16743
|
const days = explicitRange?.days ?? integerOption$1({
|
|
16704
16744
|
value: input.days,
|
|
16705
|
-
fallback: DEFAULT_DAYS$
|
|
16745
|
+
fallback: DEFAULT_DAYS$4,
|
|
16706
16746
|
minimum: 1,
|
|
16707
16747
|
maximum: MAX_DAYS$2,
|
|
16708
16748
|
label: "Days"
|
|
@@ -19959,7 +19999,7 @@ function quickWinTemplateSummaries(items) {
|
|
|
19959
19999
|
}
|
|
19960
20000
|
//#endregion
|
|
19961
20001
|
//#region packages/core/src/analyze/site-diagnostics/quick-wins-report.ts
|
|
19962
|
-
const DEFAULT_DAYS$
|
|
20002
|
+
const DEFAULT_DAYS$3 = 28;
|
|
19963
20003
|
const MAX_DAYS$1 = 548;
|
|
19964
20004
|
const DEFAULT_VERIFY_LIMIT$1 = 5;
|
|
19965
20005
|
const MAX_VERIFY_LIMIT = 100;
|
|
@@ -19981,7 +20021,7 @@ async function quickWinsReportWith(input, dependencies) {
|
|
|
19981
20021
|
const explicitRange = explicitDateRange(input, MAX_DAYS$1);
|
|
19982
20022
|
const days = explicitRange?.days ?? integerOption$1({
|
|
19983
20023
|
value: input.days,
|
|
19984
|
-
fallback: DEFAULT_DAYS$
|
|
20024
|
+
fallback: DEFAULT_DAYS$3,
|
|
19985
20025
|
minimum: 1,
|
|
19986
20026
|
maximum: MAX_DAYS$1,
|
|
19987
20027
|
label: "Days"
|
|
@@ -20405,7 +20445,7 @@ function analyzeStrikingDistanceRows(input) {
|
|
|
20405
20445
|
}
|
|
20406
20446
|
//#endregion
|
|
20407
20447
|
//#region packages/core/src/analyze/striking-distance-report.ts
|
|
20408
|
-
const DEFAULT_DAYS$
|
|
20448
|
+
const DEFAULT_DAYS$2 = 28;
|
|
20409
20449
|
const MAX_DAYS = 548;
|
|
20410
20450
|
const DEFAULT_VERIFY_LIMIT = 5;
|
|
20411
20451
|
const MAX_SOURCE_ROWS$1 = 1e5;
|
|
@@ -20414,7 +20454,7 @@ function boundedInteger$2(value, fallback, minimum, maximum) {
|
|
|
20414
20454
|
return Math.min(maximum, Math.max(minimum, Math.floor(value)));
|
|
20415
20455
|
}
|
|
20416
20456
|
function reportDays(value) {
|
|
20417
|
-
if (value === void 0) return DEFAULT_DAYS$
|
|
20457
|
+
if (value === void 0) return DEFAULT_DAYS$2;
|
|
20418
20458
|
if (!Number.isInteger(value) || value < 1 || value > MAX_DAYS) throw new SeoError("INVALID_INPUT", `Days must be a whole number between 1 and ${MAX_DAYS}.`);
|
|
20419
20459
|
return value;
|
|
20420
20460
|
}
|
|
@@ -21856,20 +21896,394 @@ function analyzeInternalLinksFromRows(input) {
|
|
|
21856
21896
|
}
|
|
21857
21897
|
};
|
|
21858
21898
|
}
|
|
21859
|
-
|
|
21860
|
-
|
|
21861
|
-
|
|
21862
|
-
|
|
21863
|
-
|
|
21864
|
-
|
|
21899
|
+
const MAX_TERMS = 100;
|
|
21900
|
+
const PLACEMENT_SENTENCES = 2;
|
|
21901
|
+
const ANCHORS_PER_SENTENCE = 3;
|
|
21902
|
+
const MIN_SENTENCE_WORDS = 6;
|
|
21903
|
+
const NAME_WEIGHT = 2;
|
|
21904
|
+
const GENERIC_ANCHORS = /^(click here|here|read more|learn more|more|this|this post|this article|this page|link|website|check it out|see more|view|continue reading)$/i;
|
|
21905
|
+
function normalise(value) {
|
|
21906
|
+
return value.normalize("NFKD").replace(/\p{Diacritic}/gu, "").normalize("NFC").replace(/([\p{L}\p{N}])[’'`]([\p{L}\p{N}])/gu, "$1$2").toLowerCase().replace(/&/g, " and ").replace(/[^\p{L}\p{N}]+/gu, " ").replace(/\s+/g, " ").trim();
|
|
21907
|
+
}
|
|
21908
|
+
const words = (value) => normalise(value).split(" ").filter(Boolean);
|
|
21909
|
+
const FUNCTION_WORDS = new Set("a an and are as at be but by for from has have how if in into is it its of on or that the their then there these they this to was we were what when where which who why will with you your our can do does not just more most also than too very about after before over under again all any each other some such no nor only own same so up out off one two both every few several many much should would could because while since although though until unless whether however".split(" "));
|
|
21910
|
+
const NOT_PLURAL = /* @__PURE__ */ new Set([
|
|
21911
|
+
"always",
|
|
21912
|
+
"atlas",
|
|
21913
|
+
"canvas",
|
|
21914
|
+
"gas",
|
|
21915
|
+
"lens",
|
|
21916
|
+
"means",
|
|
21917
|
+
"news",
|
|
21918
|
+
"perhaps",
|
|
21919
|
+
"series",
|
|
21920
|
+
"species",
|
|
21921
|
+
"sometimes",
|
|
21922
|
+
"whereas",
|
|
21923
|
+
"yes",
|
|
21924
|
+
"this",
|
|
21925
|
+
"has",
|
|
21926
|
+
"was",
|
|
21927
|
+
"does",
|
|
21928
|
+
"its"
|
|
21929
|
+
]);
|
|
21930
|
+
const CHE = /* @__PURE__ */ new Set([
|
|
21931
|
+
"caches",
|
|
21932
|
+
"niches",
|
|
21933
|
+
"headaches",
|
|
21934
|
+
"cliches",
|
|
21935
|
+
"avalanches",
|
|
21936
|
+
"moustaches"
|
|
21937
|
+
]);
|
|
21938
|
+
const NOT_AGENT = /* @__PURE__ */ new Set([
|
|
21939
|
+
"customer",
|
|
21940
|
+
"together",
|
|
21941
|
+
"however",
|
|
21942
|
+
"whatever",
|
|
21943
|
+
"whenever",
|
|
21944
|
+
"wherever",
|
|
21945
|
+
"remember",
|
|
21946
|
+
"consider",
|
|
21947
|
+
"character",
|
|
21948
|
+
"register",
|
|
21949
|
+
"november",
|
|
21950
|
+
"december",
|
|
21951
|
+
"september",
|
|
21952
|
+
"october"
|
|
21953
|
+
]);
|
|
21954
|
+
function singular(word) {
|
|
21955
|
+
if (word.length <= 3 || !/^[a-z]+$/.test(word) || NOT_PLURAL.has(word) || /(ss|us|is)$/.test(word)) return word;
|
|
21956
|
+
if (word.endsWith("ies") && word.length > 4) return `${word.slice(0, -3)}y`;
|
|
21957
|
+
if (/(xes|ches|shes|sses)$/.test(word) && !CHE.has(word)) return word.slice(0, -2);
|
|
21958
|
+
return word.endsWith("s") ? word.slice(0, -1) : word;
|
|
21959
|
+
}
|
|
21960
|
+
function stem(word) {
|
|
21961
|
+
const one = singular(word);
|
|
21962
|
+
if (!/^[a-z]+$/.test(one)) return one;
|
|
21963
|
+
if (one.length >= 8 && one.endsWith("er") && !one.endsWith("eer") && one.at(-3) !== one.at(-4) && !NOT_AGENT.has(one)) return one.slice(0, -2);
|
|
21964
|
+
if (one.length > 5 && one.endsWith("ly")) return one.slice(0, -2);
|
|
21965
|
+
if (one.length > 5 && one.endsWith("ed") && !one.endsWith("eed")) return one.slice(0, -2);
|
|
21966
|
+
if (one.length > 6 && one.endsWith("ing")) return one.slice(0, -3);
|
|
21967
|
+
return one;
|
|
21968
|
+
}
|
|
21969
|
+
const terms = (text) => words(text).filter((word) => word.length > 1 && !FUNCTION_WORDS.has(word) && !/^\d+$/.test(word)).map(stem);
|
|
21970
|
+
const slugWords = (url) => {
|
|
21971
|
+
try {
|
|
21972
|
+
return new URL(url).pathname.split(/[/._-]+/).filter(Boolean).join(" ");
|
|
21973
|
+
} catch {
|
|
21974
|
+
return "";
|
|
21975
|
+
}
|
|
21976
|
+
};
|
|
21977
|
+
function keyTerms(page) {
|
|
21978
|
+
const key = /* @__PURE__ */ new Map();
|
|
21979
|
+
const title = page.title.replace(/\s+[|\u2013\u2014-]\s+[^|\u2013\u2014]*$/u, "");
|
|
21980
|
+
for (const term of terms(page.description)) key.set(term, 1);
|
|
21981
|
+
for (const term of terms(`${title} ${page.h1} ${lastSegmentWords(page.url)}`)) key.set(term, NAME_WEIGHT);
|
|
21982
|
+
for (const term of terms(siteName(page.url))) key.delete(term);
|
|
21983
|
+
return key;
|
|
21984
|
+
}
|
|
21985
|
+
function lastSegmentWords(url) {
|
|
21986
|
+
try {
|
|
21987
|
+
return (new URL(url).pathname.split("/").filter(Boolean).at(-1) ?? "").split(/[._-]+/).join(" ");
|
|
21988
|
+
} catch {
|
|
21989
|
+
return "";
|
|
21990
|
+
}
|
|
21865
21991
|
}
|
|
21866
|
-
function
|
|
21867
|
-
|
|
21868
|
-
|
|
21869
|
-
|
|
21992
|
+
function siteName(url) {
|
|
21993
|
+
try {
|
|
21994
|
+
return new URL(url).hostname.replace(/^www\./u, "").split(".")[0] ?? "";
|
|
21995
|
+
} catch {
|
|
21996
|
+
return "";
|
|
21997
|
+
}
|
|
21870
21998
|
}
|
|
21871
|
-
function
|
|
21872
|
-
|
|
21999
|
+
function termCounts(page) {
|
|
22000
|
+
const counts = {};
|
|
22001
|
+
const add = (text, weight) => {
|
|
22002
|
+
for (const term of terms(text)) counts[term] = (counts[term] ?? 0) + weight;
|
|
22003
|
+
};
|
|
22004
|
+
add(page.title, 3);
|
|
22005
|
+
add(page.h1, 3);
|
|
22006
|
+
add(slugWords(page.url), 3);
|
|
22007
|
+
add(page.description, 2);
|
|
22008
|
+
for (const heading of page.headings) add(heading, 2);
|
|
22009
|
+
for (const sentence of page.sentences) add(sentence.text, 1);
|
|
22010
|
+
return counts;
|
|
22011
|
+
}
|
|
22012
|
+
const SKIP = /* @__PURE__ */ new Set([
|
|
22013
|
+
"script",
|
|
22014
|
+
"style",
|
|
22015
|
+
"noscript",
|
|
22016
|
+
"svg",
|
|
22017
|
+
"template",
|
|
22018
|
+
"iframe",
|
|
22019
|
+
"select"
|
|
22020
|
+
]);
|
|
22021
|
+
const CHROME = /* @__PURE__ */ new Set([
|
|
22022
|
+
"nav",
|
|
22023
|
+
"header",
|
|
22024
|
+
"footer",
|
|
22025
|
+
"aside"
|
|
22026
|
+
]);
|
|
22027
|
+
const MAIN = /* @__PURE__ */ new Set(["main", "article"]);
|
|
22028
|
+
const BLOCK = new Set("p div li h1 h2 h3 h4 h5 h6 blockquote td th dd dt pre section article main aside header footer nav figcaption summary details tr ul ol table body form".split(" "));
|
|
22029
|
+
const VOID = /* @__PURE__ */ new Set([
|
|
22030
|
+
"br",
|
|
22031
|
+
"hr",
|
|
22032
|
+
"img",
|
|
22033
|
+
"input",
|
|
22034
|
+
"meta",
|
|
22035
|
+
"link",
|
|
22036
|
+
"wbr",
|
|
22037
|
+
"source",
|
|
22038
|
+
"col",
|
|
22039
|
+
"embed"
|
|
22040
|
+
]);
|
|
22041
|
+
const ENTITIES = {
|
|
22042
|
+
amp: "&",
|
|
22043
|
+
lt: "<",
|
|
22044
|
+
gt: ">",
|
|
22045
|
+
quot: "\"",
|
|
22046
|
+
apos: "'",
|
|
22047
|
+
nbsp: " ",
|
|
22048
|
+
hellip: "…",
|
|
22049
|
+
mdash: "—",
|
|
22050
|
+
ndash: "–",
|
|
22051
|
+
rsquo: "’",
|
|
22052
|
+
lsquo: "‘",
|
|
22053
|
+
rdquo: "”",
|
|
22054
|
+
ldquo: "“"
|
|
22055
|
+
};
|
|
22056
|
+
const TAG_ATTRS = String.raw`(?:[^>"'=]|=\s*"[^"]*"|=\s*'[^']*'|=(?!\s*["'])|["'])*`;
|
|
22057
|
+
const A_OPEN = "";
|
|
22058
|
+
const UNSPACED = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Thai}\p{Script=Lao}\p{Script=Khmer}\p{Script=Myanmar}]/u;
|
|
22059
|
+
function decode(text) {
|
|
22060
|
+
return text.replace(/&(#x[0-9a-f]+|#\d+|[a-z]+);/gi, (match, code) => {
|
|
22061
|
+
if (code[0] === "#") {
|
|
22062
|
+
const hex = code[1] === "x" || code[1] === "X";
|
|
22063
|
+
const point = Number.parseInt(code.slice(hex ? 2 : 1), hex ? 16 : 10);
|
|
22064
|
+
return point > 0 && point <= 1114111 ? String.fromCodePoint(point) : "";
|
|
22065
|
+
}
|
|
22066
|
+
return ENTITIES[code.toLowerCase()] ?? match;
|
|
22067
|
+
});
|
|
22068
|
+
}
|
|
22069
|
+
const clean$1 = (value) => value.split(A_OPEN).join("").replace(/\s+/g, " ").trim();
|
|
22070
|
+
function splitSentences(block) {
|
|
22071
|
+
return block.replace(/\s+/g, " ").split(/(?<=[.!?…]["”’)]?)\s+(?=["“‘(]?[A-Z0-9])|(?<=[。!?])/).map((sentence) => sentence.trim()).filter((sentence) => sentence.length <= 500);
|
|
22072
|
+
}
|
|
22073
|
+
function pageSentences(html) {
|
|
22074
|
+
const sentences = [];
|
|
22075
|
+
const hasMain = /<(main|article)[\s>]|role=["']main["']/i.test(html);
|
|
22076
|
+
const stack = [];
|
|
22077
|
+
let skip = 0;
|
|
22078
|
+
let chrome = 0;
|
|
22079
|
+
let mainDepth = 0;
|
|
22080
|
+
let main = hasMain ? 0 : 1;
|
|
22081
|
+
let section = "";
|
|
22082
|
+
let buffer = "";
|
|
22083
|
+
let inAnchor = false;
|
|
22084
|
+
const flushRaw = (tag) => {
|
|
22085
|
+
const raw = buffer;
|
|
22086
|
+
const text = clean$1(raw);
|
|
22087
|
+
buffer = "";
|
|
22088
|
+
if (!text || sentences.length >= 250) return;
|
|
22089
|
+
if (/^h[1-6]$/.test(tag)) {
|
|
22090
|
+
if (tag !== "h1") section = text;
|
|
22091
|
+
return;
|
|
22092
|
+
}
|
|
22093
|
+
for (const part of tag === "li" ? [raw] : splitSentences(raw)) {
|
|
22094
|
+
const sentence = clean$1(part);
|
|
22095
|
+
if (sentence.length >= 25 && sentence.split(" ").length >= 4 || UNSPACED.test(sentence) && sentence.length >= 10) {
|
|
22096
|
+
if (sentences.length >= 250) return;
|
|
22097
|
+
sentences.push({
|
|
22098
|
+
text: sentence,
|
|
22099
|
+
section,
|
|
22100
|
+
hasLink: part.includes(A_OPEN)
|
|
22101
|
+
});
|
|
22102
|
+
}
|
|
22103
|
+
}
|
|
22104
|
+
};
|
|
22105
|
+
const closeTo = (tag) => {
|
|
22106
|
+
if (!stack.some((entry) => entry.tag === tag)) return;
|
|
22107
|
+
while (stack.length) {
|
|
22108
|
+
const entry = stack.pop();
|
|
22109
|
+
if (!entry) break;
|
|
22110
|
+
flushRaw(entry.tag);
|
|
22111
|
+
if (entry.main) {
|
|
22112
|
+
main -= 1;
|
|
22113
|
+
mainDepth -= 1;
|
|
22114
|
+
}
|
|
22115
|
+
if (entry.chrome) chrome -= 1;
|
|
22116
|
+
if (entry.tag === tag) break;
|
|
22117
|
+
}
|
|
22118
|
+
};
|
|
22119
|
+
const tokens = html.matchAll(new RegExp(String.raw`<!--[\s\S]*?-->|<!\[CDATA\[[\s\S]*?\]\]>|<(\/?)([a-zA-Z][\w:-]*)(${TAG_ATTRS})>|([^<]+)`, "g"));
|
|
22120
|
+
for (const match of tokens) {
|
|
22121
|
+
const [whole, close, rawTag, rawAttrs = "", text] = match;
|
|
22122
|
+
if (text !== void 0) {
|
|
22123
|
+
if (!skip && main > 0 && chrome === 0) buffer += decode(text);
|
|
22124
|
+
continue;
|
|
22125
|
+
}
|
|
22126
|
+
if (!rawTag) continue;
|
|
22127
|
+
const tag = rawTag.toLowerCase();
|
|
22128
|
+
if (skip) {
|
|
22129
|
+
if (close && SKIP.has(tag)) skip -= 1;
|
|
22130
|
+
continue;
|
|
22131
|
+
}
|
|
22132
|
+
if (close) {
|
|
22133
|
+
if (tag === "a") inAnchor = false;
|
|
22134
|
+
else if (BLOCK.has(tag)) closeTo(tag);
|
|
22135
|
+
continue;
|
|
22136
|
+
}
|
|
22137
|
+
if (SKIP.has(tag)) {
|
|
22138
|
+
if (!whole.endsWith("/>")) skip += 1;
|
|
22139
|
+
continue;
|
|
22140
|
+
}
|
|
22141
|
+
if (tag === "br") buffer += " ";
|
|
22142
|
+
if (tag === "a" && !inAnchor && /\bhref\s*=/i.test(rawAttrs)) {
|
|
22143
|
+
inAnchor = true;
|
|
22144
|
+
if (main > 0 && chrome === 0) buffer += A_OPEN;
|
|
22145
|
+
}
|
|
22146
|
+
if (VOID.has(tag) || !BLOCK.has(tag)) continue;
|
|
22147
|
+
const parent = stack[stack.length - 1];
|
|
22148
|
+
if (parent) flushRaw(parent.tag);
|
|
22149
|
+
const role = /\brole\s*=\s*["']?main/i.test(rawAttrs);
|
|
22150
|
+
const isMain = MAIN.has(tag) || role;
|
|
22151
|
+
const isChrome = tag === "nav" || tag === "aside" || CHROME.has(tag) && mainDepth === 0;
|
|
22152
|
+
if (isMain) {
|
|
22153
|
+
main += 1;
|
|
22154
|
+
mainDepth += 1;
|
|
22155
|
+
}
|
|
22156
|
+
if (isChrome) chrome += 1;
|
|
22157
|
+
stack.push({
|
|
22158
|
+
tag,
|
|
22159
|
+
main: isMain,
|
|
22160
|
+
chrome: isChrome
|
|
22161
|
+
});
|
|
22162
|
+
}
|
|
22163
|
+
while (stack.length) {
|
|
22164
|
+
const last = stack[stack.length - 1];
|
|
22165
|
+
if (!last) break;
|
|
22166
|
+
closeTo(last.tag);
|
|
22167
|
+
}
|
|
22168
|
+
flushRaw("");
|
|
22169
|
+
return sentences;
|
|
22170
|
+
}
|
|
22171
|
+
var LinkCorpus = class {
|
|
22172
|
+
df = /* @__PURE__ */ new Map();
|
|
22173
|
+
size;
|
|
22174
|
+
constructor(pages) {
|
|
22175
|
+
this.size = pages.length;
|
|
22176
|
+
for (const counts of pages) for (const term in counts) this.df.set(term, (this.df.get(term) ?? 0) + 1);
|
|
22177
|
+
}
|
|
22178
|
+
idf(term) {
|
|
22179
|
+
return Math.log((this.size + 1) / ((this.df.get(term) ?? 1) + 1)) + 1;
|
|
22180
|
+
}
|
|
22181
|
+
vector(counts) {
|
|
22182
|
+
let norm = 0;
|
|
22183
|
+
const shared = [];
|
|
22184
|
+
for (const term in counts) {
|
|
22185
|
+
const weight = (1 + Math.log(counts[term] ?? 1)) * this.idf(term);
|
|
22186
|
+
norm += weight * weight;
|
|
22187
|
+
if ((this.df.get(term) ?? 0) >= 2) shared.push([term, weight]);
|
|
22188
|
+
}
|
|
22189
|
+
norm = Math.sqrt(norm) || 1;
|
|
22190
|
+
shared.sort((left, right) => right[1] - left[1] || (left[0] < right[0] ? -1 : left[0] > right[0] ? 1 : 0));
|
|
22191
|
+
return new Map(shared.slice(0, MAX_TERMS).map(([term, weight]) => [term, weight / norm]));
|
|
22192
|
+
}
|
|
22193
|
+
similarity(left, right) {
|
|
22194
|
+
let sum = 0;
|
|
22195
|
+
for (const [term, weight] of left) sum += weight * (right.get(term) ?? 0);
|
|
22196
|
+
return sum;
|
|
22197
|
+
}
|
|
22198
|
+
sentenceScore(sentence, key) {
|
|
22199
|
+
if (!key.size) return 0;
|
|
22200
|
+
const seen = new Set(terms(sentence));
|
|
22201
|
+
let hit = 0;
|
|
22202
|
+
let total = 0;
|
|
22203
|
+
for (const [term, place] of key) {
|
|
22204
|
+
const weight = this.idf(term) * place;
|
|
22205
|
+
total += weight;
|
|
22206
|
+
if (seen.has(term)) hit += weight;
|
|
22207
|
+
}
|
|
22208
|
+
return total ? hit / total : 0;
|
|
22209
|
+
}
|
|
22210
|
+
};
|
|
22211
|
+
function anchorCandidates(sentence, key, corpus, limit) {
|
|
22212
|
+
const scored = /* @__PURE__ */ new Map();
|
|
22213
|
+
const parts = sentence.split(/\s+/).map((word) => word.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu, "")).filter(Boolean);
|
|
22214
|
+
for (let size = 2; size <= 4; size += 1) for (let start = 0; start + size <= parts.length; start += 1) {
|
|
22215
|
+
const gram = parts.slice(start, start + size);
|
|
22216
|
+
const first = (gram[0] ?? "").toLowerCase();
|
|
22217
|
+
const last = (gram[size - 1] ?? "").toLowerCase();
|
|
22218
|
+
if (FUNCTION_WORDS.has(first) || FUNCTION_WORDS.has(last) || /^\d+$/.test(first) || /^\d+$/.test(last)) continue;
|
|
22219
|
+
const stems = terms(gram.join(" "));
|
|
22220
|
+
if (stems.length < 2) continue;
|
|
22221
|
+
const hits = stems.filter((term) => key.has(term)).length;
|
|
22222
|
+
const named = stems.reduce((sum, term) => sum + (key.get(term) ?? 0) * corpus.idf(term), 0);
|
|
22223
|
+
if (!stems.some((term) => key.get(term) === NAME_WEIGHT)) continue;
|
|
22224
|
+
if (Number(key.has(terms(first)[0] ?? "")) + Number(key.has(terms(last)[0] ?? "")) < 2) continue;
|
|
22225
|
+
const text = gram.join(" ");
|
|
22226
|
+
const lower = text.toLowerCase();
|
|
22227
|
+
if (GENERIC_ANCHORS.test(lower)) continue;
|
|
22228
|
+
const rarity = stems.reduce((sum, term) => sum + corpus.idf(term), 0);
|
|
22229
|
+
const score = named * (1 + hits / key.size) + rarity * .05 - (size === 4 ? .2 : 0);
|
|
22230
|
+
const existing = scored.get(lower);
|
|
22231
|
+
if (!existing || existing.score < score) scored.set(lower, {
|
|
22232
|
+
text,
|
|
22233
|
+
score
|
|
22234
|
+
});
|
|
22235
|
+
}
|
|
22236
|
+
return [...scored.values()].sort((left, right) => right.score - left.score || (left.text < right.text ? -1 : left.text > right.text ? 1 : 0)).slice(0, limit).map((item) => item.text);
|
|
22237
|
+
}
|
|
22238
|
+
function linkPlacement(source, target, corpus) {
|
|
22239
|
+
const key = keyTerms(target);
|
|
22240
|
+
const contentSimilarity = corpus.similarity(corpus.vector(termCounts(source)), corpus.vector(termCounts(target)));
|
|
22241
|
+
const seen = /* @__PURE__ */ new Set();
|
|
22242
|
+
const sentences = source.sentences.filter((sentence) => {
|
|
22243
|
+
if (seen.has(sentence.text)) return false;
|
|
22244
|
+
seen.add(sentence.text);
|
|
22245
|
+
return !sentence.hasLink && sentence.text.split(/\s+/).length >= MIN_SENTENCE_WORDS;
|
|
22246
|
+
}).map((sentence, index) => ({
|
|
22247
|
+
sentence,
|
|
22248
|
+
index,
|
|
22249
|
+
match: corpus.sentenceScore(sentence.text, key)
|
|
22250
|
+
})).filter((item) => item.match > 0).sort((left, right) => right.match - left.match || left.index - right.index).slice(0, PLACEMENT_SENTENCES).map(({ sentence, match }) => ({
|
|
22251
|
+
text: sentence.text,
|
|
22252
|
+
section: sentence.section,
|
|
22253
|
+
match: Math.round(match * 100) / 100,
|
|
22254
|
+
anchors: anchorCandidates(sentence.text, key, corpus, ANCHORS_PER_SENTENCE)
|
|
22255
|
+
})).filter((item) => item.anchors.length > 0);
|
|
22256
|
+
return {
|
|
22257
|
+
contentSimilarity: Math.round(contentSimilarity * 100) / 100,
|
|
22258
|
+
sentences
|
|
22259
|
+
};
|
|
22260
|
+
}
|
|
22261
|
+
function placementPage(input) {
|
|
22262
|
+
return {
|
|
22263
|
+
url: input.url,
|
|
22264
|
+
title: input.title ?? "",
|
|
22265
|
+
h1: input.headings.find((heading) => heading.level === 1)?.text ?? "",
|
|
22266
|
+
description: input.metaDescription ?? "",
|
|
22267
|
+
headings: input.headings.filter((heading) => heading.level === 2 || heading.level === 3).slice(0, 8).map((heading) => heading.text),
|
|
22268
|
+
sentences: pageSentences(input.html)
|
|
22269
|
+
};
|
|
22270
|
+
}
|
|
22271
|
+
//#endregion
|
|
22272
|
+
//#region packages/core/src/analyze/internal-links-report.ts
|
|
22273
|
+
const DAY$1 = 864e5;
|
|
22274
|
+
const ISO_DAY = /^\d{4}-\d{2}-\d{2}$/;
|
|
22275
|
+
function internalLinksReportRange(input, fallbackDays, now) {
|
|
22276
|
+
if (!input.startDate && !input.endDate) return {
|
|
22277
|
+
...finalGscDateRange(fallbackDays, now),
|
|
22278
|
+
days: fallbackDays
|
|
22279
|
+
};
|
|
22280
|
+
const { startDate, endDate } = input;
|
|
22281
|
+
if (!startDate || !endDate || !ISO_DAY.test(startDate) || !ISO_DAY.test(endDate) || startDate > endDate) throw new SeoError("INVALID_INPUT", "Send a start date and an end date together, in order, as YYYY-MM-DD.");
|
|
22282
|
+
return {
|
|
22283
|
+
startDate,
|
|
22284
|
+
endDate,
|
|
22285
|
+
days: Math.round((Date.parse(`${endDate}T00:00:00Z`) - Date.parse(`${startDate}T00:00:00Z`)) / DAY$1) + 1
|
|
22286
|
+
};
|
|
21873
22287
|
}
|
|
21874
22288
|
function completeInternalLinksSelection(analysis, runtime) {
|
|
21875
22289
|
return {
|
|
@@ -21927,9 +22341,8 @@ async function verifyInternalLinkTarget(input) {
|
|
|
21927
22341
|
let fetched;
|
|
21928
22342
|
try {
|
|
21929
22343
|
fetched = await input.dependencies.fetch(input.targetUrl, {
|
|
21930
|
-
js:
|
|
21931
|
-
refresh: input.refresh
|
|
21932
|
-
rate: input.rate
|
|
22344
|
+
js: "auto",
|
|
22345
|
+
refresh: input.refresh
|
|
21933
22346
|
});
|
|
21934
22347
|
} catch (error) {
|
|
21935
22348
|
return {
|
|
@@ -21999,7 +22412,12 @@ async function verifyInternalLinkTarget(input) {
|
|
|
21999
22412
|
verification: fatalSignals.length ? "technical-issue" : "verified",
|
|
22000
22413
|
technicalSignals: signals,
|
|
22001
22414
|
fetchDiagnostics: fetched.diagnostics,
|
|
22002
|
-
warnings
|
|
22415
|
+
warnings,
|
|
22416
|
+
text: placementPage({
|
|
22417
|
+
...page,
|
|
22418
|
+
url: page.finalUrl,
|
|
22419
|
+
html: fetched.html
|
|
22420
|
+
})
|
|
22003
22421
|
};
|
|
22004
22422
|
}
|
|
22005
22423
|
function linksToAliases(page, aliases) {
|
|
@@ -22033,9 +22451,8 @@ async function verifyInternalLinkCandidate(input) {
|
|
|
22033
22451
|
let fetched;
|
|
22034
22452
|
try {
|
|
22035
22453
|
fetched = await input.dependencies.fetch(input.candidate.sourceUrl, {
|
|
22036
|
-
js:
|
|
22037
|
-
refresh: input.refresh
|
|
22038
|
-
rate: input.rate
|
|
22454
|
+
js: "auto",
|
|
22455
|
+
refresh: input.refresh
|
|
22039
22456
|
});
|
|
22040
22457
|
} catch (error) {
|
|
22041
22458
|
return {
|
|
@@ -22125,12 +22542,21 @@ async function verifyInternalLinkCandidate(input) {
|
|
|
22125
22542
|
confidence
|
|
22126
22543
|
}
|
|
22127
22544
|
},
|
|
22545
|
+
text: placementPage({
|
|
22546
|
+
...page,
|
|
22547
|
+
url: page.finalUrl,
|
|
22548
|
+
html: fetched.html
|
|
22549
|
+
}),
|
|
22128
22550
|
warnings
|
|
22129
22551
|
};
|
|
22130
22552
|
}
|
|
22131
22553
|
//#endregion
|
|
22132
22554
|
//#region packages/core/src/analyze/internal-links.ts
|
|
22133
22555
|
const MAX_GSC_ROWS = 1e5;
|
|
22556
|
+
const DEFAULT_DAYS$1 = 90;
|
|
22557
|
+
const RESULT_LIMIT = 20;
|
|
22558
|
+
const CHECK_LIMIT = 40;
|
|
22559
|
+
const MIN_IMPRESSIONS = 1;
|
|
22134
22560
|
const defaultDependencies$2 = {
|
|
22135
22561
|
searchAnalytics: querySearchAnalytics,
|
|
22136
22562
|
fetch: fetchPage,
|
|
@@ -22141,43 +22567,17 @@ async function internalLinksReport(input, dependencies = defaultDependencies$2)
|
|
|
22141
22567
|
const targetPage = new URL(assertUrlMatchesGscProperty(input.site, input.targetUrl));
|
|
22142
22568
|
targetPage.hash = "";
|
|
22143
22569
|
const targetUrl = targetPage.toString();
|
|
22144
|
-
const
|
|
22145
|
-
|
|
22146
|
-
|
|
22147
|
-
minimum: 1,
|
|
22148
|
-
maximum: 548,
|
|
22149
|
-
label: "days"
|
|
22150
|
-
});
|
|
22151
|
-
const limit = internalLinksIntegerOption({
|
|
22152
|
-
value: input.limit,
|
|
22153
|
-
fallback: 20,
|
|
22154
|
-
minimum: 1,
|
|
22155
|
-
maximum: 100,
|
|
22156
|
-
label: "limit"
|
|
22157
|
-
});
|
|
22158
|
-
const checkLimit = internalLinksIntegerOption({
|
|
22159
|
-
value: input.checkLimit,
|
|
22160
|
-
fallback: Math.max(limit, 40),
|
|
22161
|
-
minimum: 1,
|
|
22162
|
-
maximum: 200,
|
|
22163
|
-
label: "checkLimit"
|
|
22164
|
-
});
|
|
22165
|
-
const minImpressions = internalLinksNumberOption({
|
|
22166
|
-
value: input.minImpressions,
|
|
22167
|
-
fallback: 1,
|
|
22168
|
-
minimum: 0,
|
|
22169
|
-
maximum: 1e9,
|
|
22170
|
-
label: "minImpressions"
|
|
22171
|
-
});
|
|
22570
|
+
const limit = RESULT_LIMIT;
|
|
22571
|
+
const checkLimit = CHECK_LIMIT;
|
|
22572
|
+
const minImpressions = MIN_IMPRESSIONS;
|
|
22172
22573
|
const now = dependencies.now();
|
|
22173
|
-
const range = internalLinksReportRange(
|
|
22574
|
+
const { days, ...range } = internalLinksReportRange(input, DEFAULT_DAYS$1, now);
|
|
22575
|
+
const brandTerms = deriveBrandTerms({ siteUrl: input.site });
|
|
22174
22576
|
const ledger = new SessionLedger();
|
|
22175
22577
|
const target = await verifyInternalLinkTarget({
|
|
22176
22578
|
site: input.site,
|
|
22177
22579
|
targetUrl,
|
|
22178
|
-
js: input.js,
|
|
22179
22580
|
refresh: input.refresh,
|
|
22180
|
-
rate: input.rate,
|
|
22181
22581
|
dependencies
|
|
22182
22582
|
});
|
|
22183
22583
|
const targetRequests = await Promise.all(target.aliases.map(async (alias) => {
|
|
@@ -22209,8 +22609,7 @@ async function internalLinksReport(input, dependencies = defaultDependencies$2)
|
|
|
22209
22609
|
site: input.site,
|
|
22210
22610
|
targetAliases: target.aliases,
|
|
22211
22611
|
minImpressions,
|
|
22212
|
-
brandTerms
|
|
22213
|
-
includeBrand: input.includeBrand
|
|
22612
|
+
brandTerms
|
|
22214
22613
|
});
|
|
22215
22614
|
const queryCandidates = target.verification === "verified" && preliminary.selection.targetEligibleQueries > 0;
|
|
22216
22615
|
const sourceResult = queryCandidates ? await dependencies.searchAnalytics(input.site, {
|
|
@@ -22231,10 +22630,9 @@ async function internalLinksReport(input, dependencies = defaultDependencies$2)
|
|
|
22231
22630
|
site: input.site,
|
|
22232
22631
|
targetAliases: target.aliases,
|
|
22233
22632
|
minImpressions,
|
|
22234
|
-
brandTerms
|
|
22235
|
-
includeBrand: input.includeBrand
|
|
22633
|
+
brandTerms
|
|
22236
22634
|
});
|
|
22237
|
-
const
|
|
22635
|
+
const found = [];
|
|
22238
22636
|
const warnings = [...target.warnings];
|
|
22239
22637
|
let attemptedSources = 0;
|
|
22240
22638
|
let checkedSources = 0;
|
|
@@ -22243,25 +22641,27 @@ async function internalLinksReport(input, dependencies = defaultDependencies$2)
|
|
|
22243
22641
|
let selfAliasExclusions = 0;
|
|
22244
22642
|
let failedChecks = 0;
|
|
22245
22643
|
for (const candidate of analysis.candidates) {
|
|
22246
|
-
if (attemptedSources >= checkLimit ||
|
|
22644
|
+
if (attemptedSources >= checkLimit || found.length >= limit) break;
|
|
22247
22645
|
attemptedSources += 1;
|
|
22248
22646
|
const verified = await verifyInternalLinkCandidate({
|
|
22249
22647
|
site: input.site,
|
|
22250
22648
|
candidate,
|
|
22251
22649
|
target,
|
|
22252
|
-
js: input.js,
|
|
22253
22650
|
refresh: input.refresh,
|
|
22254
|
-
rate: input.rate,
|
|
22255
22651
|
dependencies
|
|
22256
22652
|
});
|
|
22257
22653
|
warnings.push(...verified.warnings);
|
|
22258
|
-
if (verified.item)
|
|
22654
|
+
if (verified.item) found.push({
|
|
22655
|
+
item: verified.item,
|
|
22656
|
+
text: verified.text
|
|
22657
|
+
});
|
|
22259
22658
|
if (verified.exclusion === "existing-link") existingLinkExclusions += 1;
|
|
22260
22659
|
if (verified.exclusion === "technical") technicalExclusions += 1;
|
|
22261
22660
|
if (verified.exclusion === "self-alias") selfAliasExclusions += 1;
|
|
22262
22661
|
if (verified.exclusion === "failed") failedChecks += 1;
|
|
22263
22662
|
else checkedSources += 1;
|
|
22264
22663
|
}
|
|
22664
|
+
const items = placeLinks(found, target.text);
|
|
22265
22665
|
const uncheckedCandidates = Math.max(0, analysis.candidates.length - attemptedSources);
|
|
22266
22666
|
const targetTruncated = targetRequests.some(({ result }) => result.rowsFetched >= MAX_GSC_ROWS);
|
|
22267
22667
|
const sourceTruncated = sourceResult.rowsFetched >= MAX_GSC_ROWS;
|
|
@@ -22323,13 +22723,14 @@ async function internalLinksReport(input, dependencies = defaultDependencies$2)
|
|
|
22323
22723
|
},
|
|
22324
22724
|
methodology: {
|
|
22325
22725
|
id: "gsc_internal_link_candidates",
|
|
22326
|
-
version:
|
|
22726
|
+
version: 4,
|
|
22327
22727
|
lexicalTargetLimit: 50,
|
|
22328
22728
|
matchedQueryEvidenceLimit: 10,
|
|
22329
22729
|
observedLinkEvidenceLimit: 20,
|
|
22330
22730
|
matching: "pairwise_exact_then_precision_lexical",
|
|
22331
22731
|
ranking: "exact_matches_then_matched_query_impressions_then_relevance",
|
|
22332
|
-
contextualPlacementVerified: true
|
|
22732
|
+
contextualPlacementVerified: true,
|
|
22733
|
+
placement: "tfidf_sentence_match_then_phrase_anchors"
|
|
22333
22734
|
},
|
|
22334
22735
|
filters: {
|
|
22335
22736
|
minImpressions,
|
|
@@ -22361,18 +22762,20 @@ async function internalLinksReport(input, dependencies = defaultDependencies$2)
|
|
|
22361
22762
|
failedChecks,
|
|
22362
22763
|
uncheckedCandidates,
|
|
22363
22764
|
matchedQueryImpressions: items.reduce((sum, item) => sum + item.matchedQueryImpressions, 0),
|
|
22364
|
-
brandFiltering:
|
|
22765
|
+
brandFiltering: "excluded",
|
|
22365
22766
|
verdict: summaryVerdict
|
|
22366
22767
|
},
|
|
22367
22768
|
items,
|
|
22368
22769
|
warnings: structuredWarnings,
|
|
22369
22770
|
caveats: [
|
|
22370
|
-
`Date window: ${range.startDate} to ${range.endDate} (${days} days), using final GSC data where available
|
|
22771
|
+
`Date window: ${range.startDate} to ${range.endDate} (${days} days), using final GSC data where available.${input.startDate ? "" : ` Without dates the window is the last ${DEFAULT_DAYS$1} days.`}`,
|
|
22772
|
+
`Limits: up to ${checkLimit} source pages are checked live and up to ${limit} are returned. Queries need at least ${minImpressions} impression.`,
|
|
22371
22773
|
`Selection: ${analysis.selection.targetEligibleQueries} eligible target queries, ${analysis.selection.candidateUrls} matched source pages, ${attemptedSources} attempted, ${checkedSources} successfully checked, ${items.length} returned.`,
|
|
22372
|
-
`
|
|
22774
|
+
`Brand queries were left out. Brand terms found from the domain: ${brandTerms.join(", ") || "none"}.`,
|
|
22373
22775
|
"GSC query rows omit anonymized queries and lower-volume rows. A 100,000-row cap is reported separately when reached.",
|
|
22374
22776
|
"Exact query overlap is medium-confidence affinity evidence; lexical matches are low-confidence review evidence. Neither predicts traffic lift or link equity.",
|
|
22375
22777
|
"The fetch checks HTML link placement, but it cannot prove editorial relevance. Review intent and reader usefulness before adding a link.",
|
|
22778
|
+
"Placement sentences are the source sentences without a link that share the most rare words with the target title, h1, description and URL. Anchors are phrases from those sentences. Content similarity compares the words of the checked pages only. All three are text heuristics, not ranking evidence.",
|
|
22376
22779
|
uncheckedCandidates ? `${uncheckedCandidates} matched source page${uncheckedCandidates === 1 ? " was" : "s were"} not checked because the output or check limit was reached.` : "",
|
|
22377
22780
|
failedChecks ? `${failedChecks} source verification attempt${failedChecks === 1 ? "" : "s"} failed. Failed fetches or extraction attempts are not counted as checked pages and do not support a missing-link conclusion.` : ""
|
|
22378
22781
|
].filter(Boolean),
|
|
@@ -22380,6 +22783,29 @@ async function internalLinksReport(input, dependencies = defaultDependencies$2)
|
|
|
22380
22783
|
ledgerSummary: ledger.summary()
|
|
22381
22784
|
};
|
|
22382
22785
|
}
|
|
22786
|
+
function placeLinks(found, target) {
|
|
22787
|
+
const corpus = new LinkCorpus([target, ...found.map((entry) => entry.text)].filter((page) => Boolean(page)).map(termCounts));
|
|
22788
|
+
const placed = found.map(({ item, text }) => {
|
|
22789
|
+
const placement = target && text ? linkPlacement(text, target, corpus) : {
|
|
22790
|
+
contentSimilarity: 0,
|
|
22791
|
+
sentences: []
|
|
22792
|
+
};
|
|
22793
|
+
const best = placement.sentences[0];
|
|
22794
|
+
const recommendation = best ? {
|
|
22795
|
+
...item.recommendation,
|
|
22796
|
+
action: `${item.recommendation.action} It fits in: "${best.text}" Anchor text could be ${best.anchors.map((anchor) => `"${anchor}"`).join(" or ")}.`
|
|
22797
|
+
} : {
|
|
22798
|
+
...item.recommendation,
|
|
22799
|
+
action: `${item.recommendation.action} No sentence on this page names the target, so a link here needs new text.`
|
|
22800
|
+
};
|
|
22801
|
+
return {
|
|
22802
|
+
...item,
|
|
22803
|
+
placement,
|
|
22804
|
+
recommendation
|
|
22805
|
+
};
|
|
22806
|
+
});
|
|
22807
|
+
return [...placed.filter((item) => item.placement.sentences.length > 0), ...placed.filter((item) => item.placement.sentences.length === 0)];
|
|
22808
|
+
}
|
|
22383
22809
|
//#endregion
|
|
22384
22810
|
//#region packages/core/src/analyze/url-templates.ts
|
|
22385
22811
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -26183,6 +26609,9 @@ const DIMENSIONS = [
|
|
|
26183
26609
|
"searchAppearance"
|
|
26184
26610
|
];
|
|
26185
26611
|
//#endregion
|
|
26612
|
+
//#region packages/core/src/commands/clicky.ts
|
|
26613
|
+
const CLICKY_KEY_PREFIX = "x-clicky-";
|
|
26614
|
+
//#endregion
|
|
26186
26615
|
//#region packages/core/src/commands/page-facts.ts
|
|
26187
26616
|
const MAX_PAGES = 20;
|
|
26188
26617
|
const TIMEOUT_MS = 6e3;
|
|
@@ -26334,7 +26763,7 @@ function indexStatusFrom(result) {
|
|
|
26334
26763
|
user_canonical: text(status.userCanonical)
|
|
26335
26764
|
};
|
|
26336
26765
|
}
|
|
26337
|
-
const LOST_PAGES_RULE = `Pages that had at least 3 clicks in the previous 28 days and kept ${.2 * 100}% or less of them in the last 28 days, with a live check of the top 20 and Google’s index status for the top 10.`;
|
|
26766
|
+
const LOST_PAGES_RULE = `Pages that had at least 3 clicks in the previous 28 days and kept ${.2 * 100}% or less of them in the last 28 days, with a live check of the top 20 and Google’s index status for the top 10 and the home page.`;
|
|
26338
26767
|
//#endregion
|
|
26339
26768
|
//#region packages/core/src/commands/tools.ts
|
|
26340
26769
|
const site = z.string().min(1).describe("Search Console property or site, such as sc-domain:example.com, https://example.com/ or example.com.");
|
|
@@ -26353,7 +26782,8 @@ const detail = {
|
|
|
26353
26782
|
const DATA_SOURCES = [
|
|
26354
26783
|
"search_console",
|
|
26355
26784
|
"google_analytics",
|
|
26356
|
-
"bing"
|
|
26785
|
+
"bing",
|
|
26786
|
+
"clicky"
|
|
26357
26787
|
];
|
|
26358
26788
|
z.object({}), `${Object.entries(SECTIONS).map(([id, text]) => `- ${id}: ${text}`).join("\n")}`, z.object({
|
|
26359
26789
|
site,
|
|
@@ -26380,7 +26810,7 @@ z.object({}), `${Object.entries(SECTIONS).map(([id, text]) => `- ${id}: ${text}`
|
|
|
26380
26810
|
...detail
|
|
26381
26811
|
}), z.object({
|
|
26382
26812
|
source: z.enum(DATA_SOURCES).default("search_console"),
|
|
26383
|
-
site: z.string().min(1).describe("search_console and bing: the property or site. google_analytics: numeric property id from seo_sites."),
|
|
26813
|
+
site: z.string().min(1).describe("search_console and bing: the property or site. google_analytics: numeric property id from seo_sites. clicky: the site or its Clicky site ID."),
|
|
26384
26814
|
dimensions: z.array(z.enum(DIMENSIONS)).max(4).default(["query"]).describe("search_console; bing uses the first, query or page."),
|
|
26385
26815
|
page: z.string().min(1).optional().describe("search_console only: URL text, such as /blog/."),
|
|
26386
26816
|
query: z.string().min(1).optional().describe("search_console only: query text."),
|
|
@@ -26594,6 +27024,7 @@ function nodeCommandDeps(options) {
|
|
|
26594
27024
|
return null;
|
|
26595
27025
|
}
|
|
26596
27026
|
},
|
|
27027
|
+
pageMarkdown: async (url) => extractMainMarkdown(await fetchPage(url, { refresh: options.refresh })),
|
|
26597
27028
|
call: options.call,
|
|
26598
27029
|
bing: localBing,
|
|
26599
27030
|
bingConnect: LOCAL_BING_CONNECT
|
|
@@ -26616,12 +27047,17 @@ async function localAccounts() {
|
|
|
26616
27047
|
}));
|
|
26617
27048
|
}
|
|
26618
27049
|
async function localResearchKeys() {
|
|
26619
|
-
const [dataForSeo, ahrefs, saved] = await Promise.all([
|
|
27050
|
+
const [dataForSeo, ahrefs, clicky, saved] = await Promise.all([
|
|
26620
27051
|
readDataForSeoCredentials().catch(() => void 0),
|
|
26621
27052
|
readAhrefsApiKey().catch(() => void 0),
|
|
27053
|
+
readClickySiteKeys().catch(() => ({})),
|
|
26622
27054
|
cloudLinked() ? cloudKeys().catch(() => ({})) : {}
|
|
26623
27055
|
]);
|
|
26624
27056
|
const keys = new Headers(saved);
|
|
27057
|
+
for (const [siteId, sitekey] of Object.entries(clicky)) {
|
|
27058
|
+
const name = `${CLICKY_KEY_PREFIX}${siteId}`;
|
|
27059
|
+
if (!keys.has(name)) keys.set(name, sitekey);
|
|
27060
|
+
}
|
|
26625
27061
|
if (dataForSeo) {
|
|
26626
27062
|
keys.set("x-dataforseo-login", dataForSeo.login);
|
|
26627
27063
|
keys.set("x-dataforseo-password", dataForSeo.password);
|
|
@@ -26629,6 +27065,11 @@ async function localResearchKeys() {
|
|
|
26629
27065
|
if (ahrefs?.apiKey) keys.set("x-ahrefs-key", ahrefs.apiKey);
|
|
26630
27066
|
return keys;
|
|
26631
27067
|
}
|
|
27068
|
+
async function localRefreshClicky() {
|
|
27069
|
+
if (!cloudLinked()) return null;
|
|
27070
|
+
await cloudCredentials({ refreshClicky: true });
|
|
27071
|
+
return localResearchKeys();
|
|
27072
|
+
}
|
|
26632
27073
|
async function resolveLocalSite(site, options = {}) {
|
|
26633
27074
|
const found = matchProperty((await listSites(options.refresh, options.account)).map((entry) => entry.siteUrl), site);
|
|
26634
27075
|
if (!found) throw new CommandError("NO_ACCESS", `This Google login has no Search Console property for ${site}. Run \`seo sites\` to list the properties it can read.`);
|
|
@@ -28934,6 +29375,6 @@ function isoDate(daysAgo = 0) {
|
|
|
28934
29375
|
return date.toISOString().slice(0, 10);
|
|
28935
29376
|
}
|
|
28936
29377
|
//#endregion
|
|
28937
|
-
export { AHREFS_API_KEY_ENV, AHREFS_API_KEY_SECRET, AI_REFERRAL_SOURCES, AI_REFERRAL_SOURCES_VERSION, ANALYTICS_PROVIDER_IDS, AUDIT_PAGE_RULE_IDS, AhrefsClient, BING_API_KEY_ENV, BING_API_KEY_SECRET, BROWSER_USER_AGENT, CANNIBAL_MAXIMUM_DOMINANT_SHARE, CANNIBAL_MINIMUM_PAGE_SHARE, CLICKY_SITEKEY_ENV, CLICKY_SITE_KEYS_SECRET, CLOUD_SIGN_IN, CLOUD_URL, CRAWL_SKIP_REASONS, CTR_DEFAULT_LIMIT, CTR_MAX_LIMIT, CTR_MAX_MIN_IMPRESSIONS, DATAFORSEO_CREDENTIAL_SECRET, DATAFORSEO_LOGIN_ENV, DATAFORSEO_PASSWORD_ENV, DEFAULT_SEARCH_CONSOLE_EXPORT_ROW_LIMIT, DEFAULT_UNREACHED_PAGE_LIMIT, DataForSeoClient, GSC_FINAL_DATA_LAG_DAYS, GSC_TIME_ZONE, GoogleApiError, INCIDENTS_URL, INTERNAL_LINK_LEXICAL_TARGET_LIMIT, INTERNAL_LINK_MATCH_EVIDENCE_LIMIT, JsonFileStorageAdapter, LOCAL_BING_CONNECT, LOCAL_SIGN_IN, LOG_MAX_AGE_MS, LOG_MAX_FILE_BYTES, LOG_MAX_TOTAL_BYTES, LocalCredentialsProvider, MANAGED_PROVIDER_SECRET_NAMES, MAX_CRAWL_CONCURRENCY, MAX_CRAWL_DEPTH, MAX_CRAWL_PAGES, MAX_CRAWL_TIMEOUT_MS, MAX_SEARCH_CONSOLE_EXPORT_FILES, MAX_SEARCH_CONSOLE_EXPORT_ROW_LIMIT, PROVIDER_SECRET_NAMES, ProviderError, ProviderExtensionRegistry, RESEARCH_IMPORT_COLUMN_KEYS, ResponseSizeLimitError, SEO_CRAWLER_IDENTITY, SEO_CRAWLER_TOKEN, SEO_CRAWLER_USER_AGENT, SEO_PROVIDER_API_VERSION, SEO_VERSION, SERP_TITLE_REFERENCE_WIDTH_PX, SERP_TITLE_WIDTH_PROFILE, STEP_CHANGE_RULES, SeoError, ServiceAccountAccessTokenClient, SessionLedger, TELEMETRY_AGENTS, TELEMETRY_ENDPOINT, TELEMETRY_ERROR_CATEGORIES, TELEMETRY_EVENTS, TELEMETRY_FAILURE_CONTEXTS, TELEMETRY_FAILURE_REASONS, TELEMETRY_NOTICE, TELEMETRY_OPERATIONS, TELEMETRY_REPORTS, TELEMETRY_SCHEMA_VERSION, TELEMETRY_TIMEOUT_MS, agentActionsView, aiReferralSourceForValue, aiReferralsReport, analyticsConnectionLabel, analyticsConnectionProviderId, analyticsConnectionSchema, analyticsProviderDetails, analyticsProviderSchema, analyzeAiReferralRows, analyzeCannibalImportRows, analyzeCannibalRows, analyzeCtrUnderperformersFromRows, analyzeDecay, analyzeIndexCoverageSignals, analyzeInternalLinksFromRows, analyzePageOpportunitiesFromRows, analyzeQueryClustersFromRows, analyzeQuickWinsFromRows, analyzeSecondPageRows, analyzeSegmentRows, analyzeStrikingDistanceRows, analyzeTrafficAnomalyRows, assertCrawlConfigLimits, assertUrlMatchesGscProperty, auditCrawlPages, auditCrawlRequests, auditPage, authStatus, bearerFetch, boundedSitemapInventory, buildAgentFindings, buildQueryClusterReportFromRows, buildTelemetryPayload, cannibalImportReport, cannibalReport, cannibalReportWith, changeWindow, checkDatabaseReadiness, classifyTelemetryFailure, clearCache, cloudAccountFor, cloudBing, cloudCredentials, cloudGoogleEmails, cloudGoogleToken, cloudKeys, cloudLinked, cloudLogout, clusterQueryRows, combineRobotsValues, compareSegmentRows, configSchema, contentCoverageRecommendation, contentCoverageType, contentOptimizationFromPageOpportunities, contentOptimizationReport, countLabel, crawlConfigHash, crawlConfigSchema, crawlDefinitionId, crawlFixToQueueItem, crawlImplementationQueueWorkflow, crawlIssueGroupSchema, crawlIssueSchema, crawlOne, crawlPageSnapshotSchema, crawlReportSchema, crawlReportSummarySchema, crawlRequestObservationSchema, crawlRunId, crawlSite, crawlSkipImpact, crawlSkipReasonCountsFromRecord, crawlStatusOnly, crawlTopFixSchema, crawlerJsonSchemas, crawlerRuleCategorySchema, crawlerRuleIdSchema, crawlerRuleInfoSchema, crawlerRuleRecommendationSchema, crawlerRuleSeveritySchema, crawlerSchemas, createAuthorizedClient, createCrawlReport, createCtrBenchmarkContext, createGoogleAccessTokenClient, createLogger, createPageRenderer, ctrUnderperformersReport, ctrUnderperformersReportWith, decayClusterDrafts, decayComparisonRange, decayingReport, decayingReportWith, deleteAhrefsApiKey, deleteBingApiKey, deleteClickySiteKey, deleteDataForSeoCredentials, deleteManagedProviderSecrets, deleteProviderExtensionAccount, deleteProviderExtensionCredentials, deleteProviderSecret, deleteTokens, deriveBrandTerms, describeInstalledProviderExtension, detectBrandTerms, detectTelemetryAgent, diagnoseCsvFiles, diagnoseProperty, diagnosePropertyWorkflow, effectiveRobotsDirectives, effectiveSnippetControl, ensurePrivateDatabaseFiles, ensureSeoCliDirs, estimateSerpTitleWidth, expectedCtrForPosition, explainRule, fetchCruxFieldData, fetchPage, fetchPageStatus, fetchSearchStatusIncidents, fetchSitemapUrls, fileSize, finalGscDateRange, findOverlappingSearchUpdates, finishCloudLogin, formatRelativeExpiry, ga4MatchReason, ga4PropertyIdFromName, ga4ReportQualityWarnings, ga4RequestCanUseCache, ga4RowsToObjects, getAuthModeStatus, getCacheStats, getClientConfig, getDb, getPrivacySnapshot, getSeoCliPaths, getServiceAccountConfig, getServiceAccountStatus, getTelemetryStatus, getTokenStorageStatus, googleJson, groupCrawlIssues, hasMetaRobotsDirective, hasRule, hasXRobotsDirective, hashKey, indexCoverageSignals, inferTemplateMovement, initializeTelemetry, inspectProviderPackage, inspectSearchConsoleUrl, inspectUrl, installProviderPackage, installedProviderPackageSchema, installedProviderPackagesSchema, internalLinksReport, interpretUpdateCorrelation, isBrandQuery, isSkippableReportError, isTelemetryCi, isTelemetryOperation, isTelemetryReport, isoDate, isoWeek, latestFinalGscDate, linkRecover, linkRecoverRecommendation, listAnalyticsAccounts, listGa4AccountSummaries, listGa4DataStreams, listGoogleAccounts, listInstalledProviderExtensions, listRules, listSearchConsoleSites, listSearchUpdates, listSites, loadInstalledProviderExtensions, loadProviderExtensions, loadProviderPackage, loadSearchConsoleExport, localAccounts, localBing, localCredentialsProvider, localError, localGoogleFetch, localResearchKeys, loginWithLoopback, makeRunId, matchGa4WebStreams, measureCoverage, metaRobotsDirectives, migrateLegacyMacConfig, missingOAuthClientMessage, missingSharedClientLoginMessage, nodeCommandDeps, normalizeBrandText, normalizeClickySiteId, normalizeClickySiteKey, normalizeCrawlConfig, normalizeCrawlSkipReasonCounts, normalizeForCoverage, normalizeHttpUrl, normalizeJavaScriptRenderingMode, normalizeLoadedCrawlReport, normalizeProviderExtensionAccount, normalizedResearchColumnName, notSignedIn, noteCacheWrite, openCloudLink, pageEvidenceRecommendation, pageOpportunitiesReport, parseOlderThan, pendingCloudLogin, performanceAudit, performanceReportIsCacheable, plural, providerCapabilitySchema, providerConnectionFieldSchema, providerExactVersionSchema, providerExtensionIdSchema, providerIdSchema, providerIntegritySchema, providerKindSchema, providerPackageManifestSchema, providerPackageNameSchema, providerPreferenceSchema, providerRegistrationSchema, providerRequestJson, providerRequestText, pruneLogs, publicHttpFetch, queryClusterReport, queryClusterTokens, queryContentCoverageFromPage, queryContentFieldsFromPage, queryFieldCoverage, queryOpportunityRecommendation, queryPageMetrics, queryPageTopQuery, queryPagesMetrics, queryPagesMetricsBatch, queryPagesTopQueries, queryPagesTopQueriesBatch, querySearchAnalytics, quickWinsCsvRows, quickWinsReport, quickWinsReportWith, readAhrefsApiKey, readAllTokens, readBingApiKey, readBoundedResponseText, readClickySiteKey, readConfig, readDataForSeoCredentials, readInstalledProviderPackages, readJson, readJsonOrUndefined, readOauthClient, readProviderExtensionAccount, readProviderExtensionCredentials, readProviderSecret, readTokens, recommendationForRule, reconcileExportPagesWithCrawl, redirectTrace, refreshAuthToken, refreshPrioritiesCsvFiles, refreshPrioritiesWorkflow, registrableDomain, removeInstalledProviderPackage, renderCrawlCsv, renderCrawlHtml, renderCrawlJunit, renderCrawlMarkdownTickets, renderCrawlPagesCsv, renderCrawlPretty, renderCsv, renderPresentationTablesMarkdown, renderWorkflowMarkdown, reportNarrative, requestHeaders, requirePageVerification, researchImportColumns, resetSeoData, resolveLocalSite, resolveSegmentRanges, resolveSeoCliPaths, reviewObservations, roundedPosition, runAnalyticsProviderLandingPages, runAnalyticsReport, runDoctor, runGa4Report, runProviderExtensionAction, runtimeForProvider, safeProviderAdapterError, saveInstalledProviderPackage, searchAnalyticsRows, searchUpdatesFromIncidents, secondPage, secondPageWith, segmentComparisonRange, segmentImpact, segmentRangeDays, seoErrorEnvelope, sessionSourceInListFilter, setActiveGoogleAccount, setTelemetryEnabled, setTokenStorageMode, shiftIsoDate, shouldExcludeBrandQuery, startCloudLogin, strikingDistance, strikingDistanceWith, summarizeCrawlReport, pageTechnicalSignals as technicalPageSignals, telemetryErrorCategory, toSeoError, tokenSchema, tokenStoreSchema, topFixes, trackTelemetryCommandFailed, trackTelemetryReportComplete, trackTelemetryReportFailed, trackTelemetryReportStart, trackTelemetrySetupComplete, trafficAnomaly, trafficAnomalyWith, unavailableSegmentImpactReport, unicodeTokens, uninstallProviderExtension, updateCorrelation, updatePostmortemCsvFiles, updatePostmortemWorkflow, validateResearchImportColumnSources, verifyAnalyticsProvider, verifyProviderExtension, verifyQueryContent, verifyQueryContentWith, withAgentReportContract, workflowPresentation, writeAhrefsApiKey, writeBingApiKey, writeClickySiteKey, writeConfig, writeDataForSeoCredentials, writeInstalledProviderPackages, writeOauthClient, writeProviderExtensionAccount, writeProviderExtensionCredentials, writeProviderSecret, writeTokens, xRobotsDirectives };
|
|
29378
|
+
export { AHREFS_API_KEY_ENV, AHREFS_API_KEY_SECRET, AI_REFERRAL_SOURCES, AI_REFERRAL_SOURCES_VERSION, ANALYTICS_PROVIDER_IDS, AUDIT_PAGE_RULE_IDS, AhrefsClient, BING_API_KEY_ENV, BING_API_KEY_SECRET, BROWSER_USER_AGENT, CANNIBAL_MAXIMUM_DOMINANT_SHARE, CANNIBAL_MINIMUM_PAGE_SHARE, CLICKY_SITEKEY_ENV, CLICKY_SITE_KEYS_SECRET, CLOUD_SIGN_IN, CLOUD_URL, CRAWL_SKIP_REASONS, CTR_DEFAULT_LIMIT, CTR_MAX_LIMIT, CTR_MAX_MIN_IMPRESSIONS, DATAFORSEO_CREDENTIAL_SECRET, DATAFORSEO_LOGIN_ENV, DATAFORSEO_PASSWORD_ENV, DEFAULT_SEARCH_CONSOLE_EXPORT_ROW_LIMIT, DEFAULT_UNREACHED_PAGE_LIMIT, DataForSeoClient, GSC_FINAL_DATA_LAG_DAYS, GSC_TIME_ZONE, GoogleApiError, INCIDENTS_URL, INTERNAL_LINK_LEXICAL_TARGET_LIMIT, INTERNAL_LINK_MATCH_EVIDENCE_LIMIT, JsonFileStorageAdapter, LOCAL_BING_CONNECT, LOCAL_SIGN_IN, LOG_MAX_AGE_MS, LOG_MAX_FILE_BYTES, LOG_MAX_TOTAL_BYTES, LocalCredentialsProvider, MANAGED_PROVIDER_SECRET_NAMES, MAX_CRAWL_CONCURRENCY, MAX_CRAWL_DEPTH, MAX_CRAWL_PAGES, MAX_CRAWL_TIMEOUT_MS, MAX_SEARCH_CONSOLE_EXPORT_FILES, MAX_SEARCH_CONSOLE_EXPORT_ROW_LIMIT, PROVIDER_SECRET_NAMES, ProviderError, ProviderExtensionRegistry, RESEARCH_IMPORT_COLUMN_KEYS, ResponseSizeLimitError, SEO_CRAWLER_IDENTITY, SEO_CRAWLER_TOKEN, SEO_CRAWLER_USER_AGENT, SEO_PROVIDER_API_VERSION, SEO_VERSION, SERP_TITLE_REFERENCE_WIDTH_PX, SERP_TITLE_WIDTH_PROFILE, STEP_CHANGE_RULES, SeoError, ServiceAccountAccessTokenClient, SessionLedger, TELEMETRY_AGENTS, TELEMETRY_ENDPOINT, TELEMETRY_ERROR_CATEGORIES, TELEMETRY_EVENTS, TELEMETRY_FAILURE_CONTEXTS, TELEMETRY_FAILURE_REASONS, TELEMETRY_NOTICE, TELEMETRY_OPERATIONS, TELEMETRY_REPORTS, TELEMETRY_SCHEMA_VERSION, TELEMETRY_TIMEOUT_MS, agentActionsView, aiReferralSourceForValue, aiReferralsReport, analyticsConnectionLabel, analyticsConnectionProviderId, analyticsConnectionSchema, analyticsProviderDetails, analyticsProviderSchema, analyzeAiReferralRows, analyzeCannibalImportRows, analyzeCannibalRows, analyzeCtrUnderperformersFromRows, analyzeDecay, analyzeIndexCoverageSignals, analyzeInternalLinksFromRows, analyzePageOpportunitiesFromRows, analyzeQueryClustersFromRows, analyzeQuickWinsFromRows, analyzeSecondPageRows, analyzeSegmentRows, analyzeStrikingDistanceRows, analyzeTrafficAnomalyRows, assertCrawlConfigLimits, assertUrlMatchesGscProperty, auditCrawlPages, auditCrawlRequests, auditPage, authStatus, bearerFetch, boundedSitemapInventory, buildAgentFindings, buildQueryClusterReportFromRows, buildTelemetryPayload, cannibalImportReport, cannibalReport, cannibalReportWith, changeWindow, checkDatabaseReadiness, classifyTelemetryFailure, clearCache, cloudAccountFor, cloudBing, cloudCredentials, cloudGoogleEmails, cloudGoogleToken, cloudKeys, cloudLinked, cloudLogout, clusterQueryRows, combineRobotsValues, compareSegmentRows, configSchema, contentCoverageRecommendation, contentCoverageType, contentOptimizationFromPageOpportunities, contentOptimizationReport, countLabel, crawlConfigHash, crawlConfigSchema, crawlDefinitionId, crawlFixToQueueItem, crawlImplementationQueueWorkflow, crawlIssueGroupSchema, crawlIssueSchema, crawlOne, crawlPageSnapshotSchema, crawlReportSchema, crawlReportSummarySchema, crawlRequestObservationSchema, crawlRunId, crawlSite, crawlSkipImpact, crawlSkipReasonCountsFromRecord, crawlStatusOnly, crawlTopFixSchema, crawlerJsonSchemas, crawlerRuleCategorySchema, crawlerRuleIdSchema, crawlerRuleInfoSchema, crawlerRuleRecommendationSchema, crawlerRuleSeveritySchema, crawlerSchemas, createAuthorizedClient, createCrawlReport, createCtrBenchmarkContext, createGoogleAccessTokenClient, createLogger, createPageRenderer, ctrUnderperformersReport, ctrUnderperformersReportWith, decayClusterDrafts, decayComparisonRange, decayingReport, decayingReportWith, deleteAhrefsApiKey, deleteBingApiKey, deleteClickySiteKey, deleteDataForSeoCredentials, deleteManagedProviderSecrets, deleteProviderExtensionAccount, deleteProviderExtensionCredentials, deleteProviderSecret, deleteTokens, deriveBrandTerms, describeInstalledProviderExtension, detectBrandTerms, detectTelemetryAgent, diagnoseCsvFiles, diagnoseProperty, diagnosePropertyWorkflow, effectiveRobotsDirectives, effectiveSnippetControl, ensurePrivateDatabaseFiles, ensureSeoCliDirs, estimateSerpTitleWidth, expectedCtrForPosition, explainRule, fetchCruxFieldData, fetchPage, fetchPageStatus, fetchSearchStatusIncidents, fetchSitemapUrls, fileSize, finalGscDateRange, findOverlappingSearchUpdates, finishCloudLogin, formatRelativeExpiry, ga4MatchReason, ga4PropertyIdFromName, ga4ReportQualityWarnings, ga4RequestCanUseCache, ga4RowsToObjects, getAuthModeStatus, getCacheStats, getClientConfig, getDb, getPrivacySnapshot, getSeoCliPaths, getServiceAccountConfig, getServiceAccountStatus, getTelemetryStatus, getTokenStorageStatus, googleJson, groupCrawlIssues, hasMetaRobotsDirective, hasRule, hasXRobotsDirective, hashKey, indexCoverageSignals, inferTemplateMovement, initializeTelemetry, inspectProviderPackage, inspectSearchConsoleUrl, inspectUrl, installProviderPackage, installedProviderPackageSchema, installedProviderPackagesSchema, internalLinksReport, interpretUpdateCorrelation, isBrandQuery, isSkippableReportError, isTelemetryCi, isTelemetryOperation, isTelemetryReport, isoDate, isoWeek, latestFinalGscDate, linkRecover, linkRecoverRecommendation, listAnalyticsAccounts, listGa4AccountSummaries, listGa4DataStreams, listGoogleAccounts, listInstalledProviderExtensions, listRules, listSearchConsoleSites, listSearchUpdates, listSites, loadInstalledProviderExtensions, loadProviderExtensions, loadProviderPackage, loadSearchConsoleExport, localAccounts, localBing, localCredentialsProvider, localError, localGoogleFetch, localRefreshClicky, localResearchKeys, loginWithLoopback, makeRunId, matchGa4WebStreams, measureCoverage, metaRobotsDirectives, migrateLegacyMacConfig, missingOAuthClientMessage, missingSharedClientLoginMessage, nodeCommandDeps, normalizeBrandText, normalizeClickySiteId, normalizeClickySiteKey, normalizeCrawlConfig, normalizeCrawlSkipReasonCounts, normalizeForCoverage, normalizeHttpUrl, normalizeJavaScriptRenderingMode, normalizeLoadedCrawlReport, normalizeProviderExtensionAccount, normalizedResearchColumnName, notSignedIn, noteCacheWrite, openCloudLink, pageEvidenceRecommendation, pageOpportunitiesReport, parseOlderThan, pendingCloudLogin, performanceAudit, performanceReportIsCacheable, plural, providerCapabilitySchema, providerConnectionFieldSchema, providerExactVersionSchema, providerExtensionIdSchema, providerIdSchema, providerIntegritySchema, providerKindSchema, providerPackageManifestSchema, providerPackageNameSchema, providerPreferenceSchema, providerRegistrationSchema, providerRequestJson, providerRequestText, pruneLogs, publicHttpFetch, queryClusterReport, queryClusterTokens, queryContentCoverageFromPage, queryContentFieldsFromPage, queryFieldCoverage, queryOpportunityRecommendation, queryPageMetrics, queryPageTopQuery, queryPagesMetrics, queryPagesMetricsBatch, queryPagesTopQueries, queryPagesTopQueriesBatch, querySearchAnalytics, quickWinsCsvRows, quickWinsReport, quickWinsReportWith, readAhrefsApiKey, readAllTokens, readBingApiKey, readBoundedResponseText, readClickySiteKey, readClickySiteKeys, readConfig, readDataForSeoCredentials, readInstalledProviderPackages, readJson, readJsonOrUndefined, readOauthClient, readProviderExtensionAccount, readProviderExtensionCredentials, readProviderSecret, readTokens, recommendationForRule, reconcileExportPagesWithCrawl, redirectTrace, refreshAuthToken, refreshPrioritiesCsvFiles, refreshPrioritiesWorkflow, registrableDomain, removeInstalledProviderPackage, renderCrawlCsv, renderCrawlHtml, renderCrawlJunit, renderCrawlMarkdownTickets, renderCrawlPagesCsv, renderCrawlPretty, renderCsv, renderPresentationTablesMarkdown, renderWorkflowMarkdown, reportNarrative, requestHeaders, requirePageVerification, researchImportColumns, resetSeoData, resolveLocalSite, resolveSegmentRanges, resolveSeoCliPaths, reviewObservations, roundedPosition, runAnalyticsProviderLandingPages, runAnalyticsReport, runDoctor, runGa4Report, runProviderExtensionAction, runtimeForProvider, safeProviderAdapterError, saveInstalledProviderPackage, searchAnalyticsRows, searchUpdatesFromIncidents, secondPage, secondPageWith, segmentComparisonRange, segmentImpact, segmentRangeDays, seoErrorEnvelope, sessionSourceInListFilter, setActiveGoogleAccount, setTelemetryEnabled, setTokenStorageMode, shiftIsoDate, shouldExcludeBrandQuery, startCloudLogin, strikingDistance, strikingDistanceWith, summarizeCrawlReport, pageTechnicalSignals as technicalPageSignals, telemetryErrorCategory, toSeoError, tokenSchema, tokenStoreSchema, topFixes, trackTelemetryCommandFailed, trackTelemetryReportComplete, trackTelemetryReportFailed, trackTelemetryReportStart, trackTelemetrySetupComplete, trafficAnomaly, trafficAnomalyWith, unavailableSegmentImpactReport, unicodeTokens, uninstallProviderExtension, updateCorrelation, updatePostmortemCsvFiles, updatePostmortemWorkflow, validateResearchImportColumnSources, verifyAnalyticsProvider, verifyProviderExtension, verifyQueryContent, verifyQueryContentWith, withAgentReportContract, workflowPresentation, writeAhrefsApiKey, writeBingApiKey, writeClickySiteKey, writeConfig, writeDataForSeoCredentials, writeInstalledProviderPackages, writeOauthClient, writeProviderExtensionAccount, writeProviderExtensionCredentials, writeProviderSecret, writeTokens, xRobotsDirectives };
|
|
28938
29379
|
|
|
28939
29380
|
//# sourceMappingURL=index.js.map
|