seo 0.11.0 → 0.12.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 +14 -7
- package/README.md +1 -1
- package/dist/cli.js +78 -78
- package/dist/cli.js.map +1 -1
- package/dist/{dist-BL_yf1fw.js → dist-B1bivk1S.js} +1 -1
- package/dist/{dist-DqfdgVVU.js → dist-Ban8uP1U.js} +81 -65
- package/dist/dist-Ban8uP1U.js.map +1 -0
- package/dist/{esm-ZQwQYL0U.js → esm-CQJgJ8TD.js} +2 -2
- package/dist/{esm-ZQwQYL0U.js.map → esm-CQJgJ8TD.js.map} +1 -1
- package/dist/index.d.ts +85 -45
- package/dist/index.js +195 -71
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +823 -123
- package/dist/mcp.js.map +1 -1
- package/package.json +1 -1
- package/skills/seo/SKILL.md +15 -1
- package/dist/dist-DqfdgVVU.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire as __seoCreateRequire } from 'node:module'; const require = __seoCreateRequire(import.meta.url);
|
|
2
|
-
import { n as version, t as load } from "./esm-
|
|
2
|
+
import { n as version, t as load } from "./esm-CQJgJ8TD.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";
|
|
@@ -1614,7 +1614,7 @@ async function googleJson(gfetch, url, input) {
|
|
|
1614
1614
|
return readJson(response, input.label, { maxBytes: input.maxBytes });
|
|
1615
1615
|
}
|
|
1616
1616
|
const GSC = "https://www.googleapis.com/webmasters/v3";
|
|
1617
|
-
const PAGE_ROWS = 25e3;
|
|
1617
|
+
const PAGE_ROWS$1 = 25e3;
|
|
1618
1618
|
async function searchAnalyticsRows(gfetch, site, body, options) {
|
|
1619
1619
|
const rows = [];
|
|
1620
1620
|
const { maxRows: requested, ...apiBody } = body;
|
|
@@ -1625,7 +1625,7 @@ async function searchAnalyticsRows(gfetch, site, body, options) {
|
|
|
1625
1625
|
while (true) {
|
|
1626
1626
|
const remaining = typeof maxRows === "number" ? maxRows - rows.length : void 0;
|
|
1627
1627
|
if (typeof remaining === "number" && remaining <= 0) break;
|
|
1628
|
-
const rowLimit = Math.min(apiBody.rowLimit ?? PAGE_ROWS, remaining ?? PAGE_ROWS, PAGE_ROWS);
|
|
1628
|
+
const rowLimit = Math.min(apiBody.rowLimit ?? PAGE_ROWS$1, remaining ?? PAGE_ROWS$1, PAGE_ROWS$1);
|
|
1629
1629
|
const json = await googleJson(gfetch, url, {
|
|
1630
1630
|
...options,
|
|
1631
1631
|
label: "Search Console",
|
|
@@ -1702,7 +1702,7 @@ async function runAnalyticsReport(gfetch, propertyId, body, options) {
|
|
|
1702
1702
|
//#endregion
|
|
1703
1703
|
//#region packages/core/src/bing/api.ts
|
|
1704
1704
|
const BASE = "https://ssl.bing.com/webmaster/api.svc/json";
|
|
1705
|
-
const MAX_BYTES$
|
|
1705
|
+
const MAX_BYTES$4 = 8e6;
|
|
1706
1706
|
function bingCall(auth, options) {
|
|
1707
1707
|
return async (method, params = {}) => {
|
|
1708
1708
|
const url = new URL(`${BASE}/${method}`);
|
|
@@ -1727,7 +1727,7 @@ function bingCall(auth, options) {
|
|
|
1727
1727
|
if (response.status === 429 || /throttle/i.test(detail)) throw new SeoError("RATE_LIMITED", "Bing Webmaster is limiting requests. Try again in a few minutes.");
|
|
1728
1728
|
throw new SeoError("OPTIONAL_PROVIDER_UNAVAILABLE", `Bing Webmaster answered ${response.status}. ${detail}`.trim());
|
|
1729
1729
|
}
|
|
1730
|
-
return (await readJson(response, "Bing Webmaster", { maxBytes: MAX_BYTES$
|
|
1730
|
+
return (await readJson(response, "Bing Webmaster", { maxBytes: MAX_BYTES$4 })).d;
|
|
1731
1731
|
};
|
|
1732
1732
|
}
|
|
1733
1733
|
//#endregion
|
|
@@ -4263,6 +4263,7 @@ const CACHE_MAX_BYTES = 256 * MEBIBYTE$2;
|
|
|
4263
4263
|
const CACHE_MAX_AGE_MS = 720 * 60 * 60 * 1e3;
|
|
4264
4264
|
const CACHE_LIMITS = {
|
|
4265
4265
|
gsc_cache: 48 * MEBIBYTE$2,
|
|
4266
|
+
url_inspection_cache: 16 * MEBIBYTE$2,
|
|
4266
4267
|
ga4_cache: 24 * MEBIBYTE$2,
|
|
4267
4268
|
provider_cache: 32 * MEBIBYTE$2,
|
|
4268
4269
|
http_cache: 128 * MEBIBYTE$2,
|
|
@@ -4280,6 +4281,16 @@ const CACHE_POLICIES = [
|
|
|
4280
4281
|
"response_json"
|
|
4281
4282
|
]
|
|
4282
4283
|
},
|
|
4284
|
+
{
|
|
4285
|
+
table: "url_inspection_cache",
|
|
4286
|
+
keys: ["site_url", "url"],
|
|
4287
|
+
timestamp: "fetched_at",
|
|
4288
|
+
sizeColumns: [
|
|
4289
|
+
"site_url",
|
|
4290
|
+
"url",
|
|
4291
|
+
"status_json"
|
|
4292
|
+
]
|
|
4293
|
+
},
|
|
4283
4294
|
{
|
|
4284
4295
|
table: "ga4_cache",
|
|
4285
4296
|
keys: ["property_id", "query_hash"],
|
|
@@ -4583,6 +4594,17 @@ CREATE INDEX IF NOT EXISTS idx_url_inspection_reservations_minute
|
|
|
4583
4594
|
credential_key, property_site_url, created_at
|
|
4584
4595
|
);
|
|
4585
4596
|
|
|
4597
|
+
CREATE TABLE IF NOT EXISTS url_inspection_cache (
|
|
4598
|
+
site_url TEXT NOT NULL,
|
|
4599
|
+
url TEXT NOT NULL,
|
|
4600
|
+
status_json TEXT NOT NULL,
|
|
4601
|
+
fetched_at INTEGER NOT NULL,
|
|
4602
|
+
expires_at INTEGER NOT NULL,
|
|
4603
|
+
PRIMARY KEY(site_url, url)
|
|
4604
|
+
) WITHOUT ROWID;
|
|
4605
|
+
CREATE INDEX IF NOT EXISTS idx_url_inspection_cache_expires
|
|
4606
|
+
ON url_inspection_cache(expires_at);
|
|
4607
|
+
|
|
4586
4608
|
CREATE TABLE IF NOT EXISTS performance_reports (
|
|
4587
4609
|
id TEXT PRIMARY KEY,
|
|
4588
4610
|
url TEXT NOT NULL,
|
|
@@ -4694,6 +4716,7 @@ function getCacheStats() {
|
|
|
4694
4716
|
const counts = {
|
|
4695
4717
|
sites: database.prepare("SELECT COUNT(*) AS count FROM sites").get(),
|
|
4696
4718
|
gsc_cache: database.prepare("SELECT COUNT(*) AS count FROM gsc_cache").get(),
|
|
4719
|
+
url_inspection_cache: database.prepare("SELECT COUNT(*) AS count FROM url_inspection_cache").get(),
|
|
4697
4720
|
google_analytics_cache: database.prepare("SELECT COUNT(*) AS count FROM ga4_cache").get(),
|
|
4698
4721
|
provider_cache: { count: providerCount("dataforseo") },
|
|
4699
4722
|
ahrefs_cache: { count: providerCount("ahrefs") },
|
|
@@ -4725,8 +4748,9 @@ function clearCache(provider, olderThanMs) {
|
|
|
4725
4748
|
compactCacheDatabase(database, { allowFullVacuum: true });
|
|
4726
4749
|
return info.changes;
|
|
4727
4750
|
}
|
|
4728
|
-
const tables = provider === "gsc" ? ["gsc_cache"] : provider === "google-analytics" ? ["ga4_cache"] : provider === "http" ? ["http_cache"] : [
|
|
4751
|
+
const tables = provider === "gsc" ? ["gsc_cache", "url_inspection_cache"] : provider === "google-analytics" ? ["ga4_cache"] : provider === "http" ? ["http_cache"] : [
|
|
4729
4752
|
"gsc_cache",
|
|
4753
|
+
"url_inspection_cache",
|
|
4730
4754
|
"ga4_cache",
|
|
4731
4755
|
"provider_cache",
|
|
4732
4756
|
"http_cache"
|
|
@@ -8222,6 +8246,19 @@ function reserveUrlInspectionQuota(input) {
|
|
|
8222
8246
|
if ("error" in result) throw result.error;
|
|
8223
8247
|
return result.reservation;
|
|
8224
8248
|
}
|
|
8249
|
+
function readUrlInspectionQuota(input) {
|
|
8250
|
+
const database = input.database ?? getDb();
|
|
8251
|
+
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
8252
|
+
const bucket = database.prepare(`SELECT limit_count, used_count, reserved_count, blocked_until
|
|
8253
|
+
FROM url_inspection_quota_buckets
|
|
8254
|
+
WHERE credential_key = ? AND property_site_url = ? AND quota_date = ?`).get(input.credentialKey, input.property, urlInspectionQuotaDate(now));
|
|
8255
|
+
const limit = bucket?.limit_count ?? 2e3;
|
|
8256
|
+
return {
|
|
8257
|
+
used: (bucket?.blocked_until ?? 0) > now.getTime() ? limit : Math.min(limit, (bucket?.used_count ?? 0) + (bucket?.reserved_count ?? 0)),
|
|
8258
|
+
limit,
|
|
8259
|
+
resetAt: urlInspectionQuotaResetAt(now).toISOString()
|
|
8260
|
+
};
|
|
8261
|
+
}
|
|
8225
8262
|
function finalizeUrlInspectionQuota(input) {
|
|
8226
8263
|
const database = input.database ?? getDb();
|
|
8227
8264
|
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
@@ -8266,6 +8303,13 @@ function inspectionResponseError(response, input) {
|
|
|
8266
8303
|
if (response.status >= 500) return new SeoError("PROVIDER_UNAVAILABLE", `Google URL Inspection is unavailable (${response.status}). Try again later.`);
|
|
8267
8304
|
return new SeoError("PROVIDER_UNAVAILABLE", `URL Inspection failed with ${response.status}.`);
|
|
8268
8305
|
}
|
|
8306
|
+
async function urlInspectionQuota(input) {
|
|
8307
|
+
const { quotaIdentity } = await getAuthorized(input.accountEmail ?? await cloudAccountFor(input.siteUrl));
|
|
8308
|
+
return readUrlInspectionQuota({
|
|
8309
|
+
credentialKey: credentialKey(quotaIdentity),
|
|
8310
|
+
property: input.siteUrl
|
|
8311
|
+
});
|
|
8312
|
+
}
|
|
8269
8313
|
async function inspectUrl(input) {
|
|
8270
8314
|
const inspectionUrl = assertUrlMatchesGscProperty(input.siteUrl, input.inspectionUrl);
|
|
8271
8315
|
const { client, quotaIdentity } = await getAuthorized(input.accountEmail ?? await cloudAccountFor(input.siteUrl));
|
|
@@ -10169,7 +10213,7 @@ const actionPriority = new Map([
|
|
|
10169
10213
|
"unverified",
|
|
10170
10214
|
"covered"
|
|
10171
10215
|
].map((type, index) => [type, index]));
|
|
10172
|
-
function plural$
|
|
10216
|
+
function plural$7(count, singular, pluralLabel) {
|
|
10173
10217
|
return count === 1 ? singular : pluralLabel;
|
|
10174
10218
|
}
|
|
10175
10219
|
function actionable(item) {
|
|
@@ -10203,7 +10247,7 @@ function pageOpportunityVerdict(input) {
|
|
|
10203
10247
|
if (focus === "unverified") return "GSC visibility exists, but the page was not verified and no specific change is justified yet.";
|
|
10204
10248
|
if (focus === "covered") return "The verified page covers the sampled queries and CTR is not materially below its directional benchmark.";
|
|
10205
10249
|
const count = analysis.summary.opportunities;
|
|
10206
|
-
const prefix = `${count} ${plural$
|
|
10250
|
+
const prefix = `${count} ${plural$7(count, "actionable observation", "actionable observations")} found.`;
|
|
10207
10251
|
if (focus === "content-gap") return `${prefix} Verified body coverage is the main issue; confirm intent before expanding the page.`;
|
|
10208
10252
|
if (focus === "serp-framing") return `${prefix} The body is relevant, but its title, meta description, or H1 framing is weak.`;
|
|
10209
10253
|
if (focus === "ranking") return `${prefix} Most upside requires stronger relevance or authority; no CTR-only lift is claimed for page-two queries.`;
|
|
@@ -10218,7 +10262,7 @@ function pageOpportunityRecommendations(analysis) {
|
|
|
10218
10262
|
}
|
|
10219
10263
|
const recommendations = [`Start with "${top.query}". ${top.recommendation}`];
|
|
10220
10264
|
const technical = actions.filter((item) => item.opportunityType === "technical-check").length;
|
|
10221
|
-
if (technical) recommendations.push(`Resolve the technical evidence affecting ${technical} ${plural$
|
|
10265
|
+
if (technical) recommendations.push(`Resolve the technical evidence affecting ${technical} ${plural$7(technical, "row", "rows")} before content work.`);
|
|
10222
10266
|
const gaps = actions.filter((item) => item.opportunityType === "content-gap").length;
|
|
10223
10267
|
if (gaps >= 3) recommendations.push(`Group the ${gaps} verified content gaps into useful sections rather than adding one thin paragraph per query.`);
|
|
10224
10268
|
return recommendations;
|
|
@@ -14222,7 +14266,7 @@ function availabilityOf(status) {
|
|
|
14222
14266
|
function errorText(error) {
|
|
14223
14267
|
return error instanceof Error ? error.message : String(error);
|
|
14224
14268
|
}
|
|
14225
|
-
async function get(input, url, headers, readBody = true) {
|
|
14269
|
+
async function get$1(input, url, headers, readBody = true) {
|
|
14226
14270
|
const controller = abortController({
|
|
14227
14271
|
timeoutMs: input.timeoutMs,
|
|
14228
14272
|
signal: input.signal
|
|
@@ -14279,7 +14323,7 @@ function llmsLinks(text, base) {
|
|
|
14279
14323
|
return links;
|
|
14280
14324
|
}
|
|
14281
14325
|
async function checkLlmsTxt(input) {
|
|
14282
|
-
const result = await get(input, new URL("/llms.txt", input.url).toString());
|
|
14326
|
+
const result = await get$1(input, new URL("/llms.txt", input.url).toString());
|
|
14283
14327
|
const availability = result.availability === "available" && isHtml(result.contentType) ? "absent" : result.availability;
|
|
14284
14328
|
const exists = availability === "available";
|
|
14285
14329
|
const base = {
|
|
@@ -14296,7 +14340,7 @@ async function checkLlmsTxt(input) {
|
|
|
14296
14340
|
for (const url of found.slice(0, LLMS_LINK_LIMIT)) {
|
|
14297
14341
|
if (input.signal?.aborted) break;
|
|
14298
14342
|
checked += 1;
|
|
14299
|
-
const link = brokenLink(await get(input, url, void 0, false), false);
|
|
14343
|
+
const link = brokenLink(await get$1(input, url, void 0, false), false);
|
|
14300
14344
|
if (link) broken.push(link);
|
|
14301
14345
|
}
|
|
14302
14346
|
return {
|
|
@@ -14340,7 +14384,7 @@ function sitemapUrlsFromRobots(text) {
|
|
|
14340
14384
|
return [...urls];
|
|
14341
14385
|
}
|
|
14342
14386
|
async function checkRobotsAiAccess(input) {
|
|
14343
|
-
const result = await get(input, new URL("/robots.txt", input.url).toString());
|
|
14387
|
+
const result = await get$1(input, new URL("/robots.txt", input.url).toString());
|
|
14344
14388
|
const exists = result.availability === "available";
|
|
14345
14389
|
const unknown = result.availability === "rate-limited" || result.availability === "unreachable";
|
|
14346
14390
|
const declared = exists ? declaredUserAgents(result.text) : /* @__PURE__ */ new Set();
|
|
@@ -14369,7 +14413,7 @@ async function checkAgentResources(input) {
|
|
|
14369
14413
|
const resources = [];
|
|
14370
14414
|
for (const path of AGENT_RESOURCE_PATHS) {
|
|
14371
14415
|
if (input.signal?.aborted) break;
|
|
14372
|
-
const result = await get(input, new URL(path, input.url).toString());
|
|
14416
|
+
const result = await get$1(input, new URL(path, input.url).toString());
|
|
14373
14417
|
const exists = result.availability === "available" && !isHtml(result.contentType);
|
|
14374
14418
|
let validJson;
|
|
14375
14419
|
if (exists && /\bjson\b/i.test(result.contentType)) try {
|
|
@@ -14389,7 +14433,7 @@ async function checkAgentResources(input) {
|
|
|
14389
14433
|
return resources;
|
|
14390
14434
|
}
|
|
14391
14435
|
async function checkMarkdownNegotiation(input) {
|
|
14392
|
-
const result = await get(input, input.url, { accept: "text/markdown" }, false);
|
|
14436
|
+
const result = await get$1(input, input.url, { accept: "text/markdown" }, false);
|
|
14393
14437
|
const vary = result.headers?.get("vary") ?? "";
|
|
14394
14438
|
return {
|
|
14395
14439
|
url: result.url,
|
|
@@ -14436,7 +14480,7 @@ async function checkMarkdownCopies(input) {
|
|
|
14436
14480
|
const url = page.markdownAlternates?.[0];
|
|
14437
14481
|
if (!url || !input.allows(url)) continue;
|
|
14438
14482
|
checked += 1;
|
|
14439
|
-
const result = await get({
|
|
14483
|
+
const result = await get$1({
|
|
14440
14484
|
...input,
|
|
14441
14485
|
url
|
|
14442
14486
|
}, url);
|
|
@@ -16293,7 +16337,7 @@ const installedPackageJsonSchema = z.object({
|
|
|
16293
16337
|
function packageDirectory(packagesDir, packageName) {
|
|
16294
16338
|
return join(packagesDir, "node_modules", ...packageName.split("/"));
|
|
16295
16339
|
}
|
|
16296
|
-
function inside(parent, child) {
|
|
16340
|
+
function inside$1(parent, child) {
|
|
16297
16341
|
const path = relative(parent, child);
|
|
16298
16342
|
return path === "" || !path.startsWith("..") && !isAbsolute(path);
|
|
16299
16343
|
}
|
|
@@ -16325,14 +16369,14 @@ async function loadOne(installed, registry, options) {
|
|
|
16325
16369
|
async function loadProviderPackage(input, options) {
|
|
16326
16370
|
const importModule = options.importModule ?? ((url) => import(url));
|
|
16327
16371
|
const packageDir = realpathSync(packageDirectory(options.packagesDir, input.package));
|
|
16328
|
-
if (!inside(realpathSync(options.packagesDir), packageDir)) throw new Error("Installed provider package resolves outside the provider directory.");
|
|
16372
|
+
if (!inside$1(realpathSync(options.packagesDir), packageDir)) throw new Error("Installed provider package resolves outside the provider directory.");
|
|
16329
16373
|
const packageJsonPath = join(packageDir, "package.json");
|
|
16330
16374
|
const packageJson = installedPackageJsonSchema.parse(JSON.parse(readFileSync(packageJsonPath, "utf8")));
|
|
16331
16375
|
if (packageJson.name !== input.package || packageJson.version !== input.version) throw new Error("Installed provider package does not match its saved record.");
|
|
16332
16376
|
const packageRegistry = new ProviderExtensionRegistry();
|
|
16333
16377
|
for (const entry of packageJson.seo.providers) {
|
|
16334
16378
|
const entryPath = realpathSync(resolve(dirname(packageJsonPath), entry));
|
|
16335
|
-
if (!inside(packageDir, entryPath)) throw new Error("Provider entry point resolves outside its package.");
|
|
16379
|
+
if (!inside$1(packageDir, entryPath)) throw new Error("Provider entry point resolves outside its package.");
|
|
16336
16380
|
await providerActivate((await importModule(`${pathToFileURL(entryPath).href}?seo-provider=${encodeURIComponent(input.version)}`)).default)(packageRegistry.hostFor({
|
|
16337
16381
|
package: input.package,
|
|
16338
16382
|
version: input.version
|
|
@@ -16368,7 +16412,7 @@ async function loadInstalledProviderExtensions(options = {}) {
|
|
|
16368
16412
|
const DEFAULT_MAX_REQUESTS = 8;
|
|
16369
16413
|
const MAX_RESPONSE_BYTES$1 = 10 * 1024 * 1024;
|
|
16370
16414
|
const MAX_REQUEST_BODY_BYTES = 64 * 1024;
|
|
16371
|
-
const TIMEOUT_MS$
|
|
16415
|
+
const TIMEOUT_MS$3 = 15e3;
|
|
16372
16416
|
function normalizeProviderExtensionAccount(provider, account) {
|
|
16373
16417
|
const fields = provider.connection.fields.filter((field) => field.kind === "account");
|
|
16374
16418
|
const allowed = new Set(fields.map((field) => field.id));
|
|
@@ -16415,7 +16459,7 @@ function runtimeForProvider(provider, options = {}) {
|
|
|
16415
16459
|
},
|
|
16416
16460
|
fetch: providerFetch,
|
|
16417
16461
|
maxResponseBytes: MAX_RESPONSE_BYTES$1,
|
|
16418
|
-
timeoutMs: TIMEOUT_MS$
|
|
16462
|
+
timeoutMs: TIMEOUT_MS$3,
|
|
16419
16463
|
retry: (input.method ?? "GET") === "GET" ? "safe" : "never",
|
|
16420
16464
|
schema: z.unknown()
|
|
16421
16465
|
});
|
|
@@ -17996,7 +18040,7 @@ function analyzeCtrUnderperformersFromRows(input) {
|
|
|
17996
18040
|
const MAX_GSC_ROWS$3 = 1e5;
|
|
17997
18041
|
const DEFAULT_DAYS$3 = 28;
|
|
17998
18042
|
const MAX_DAYS$2 = 548;
|
|
17999
|
-
function plural$
|
|
18043
|
+
function plural$6(count, singular, pluralLabel = `${singular}s`) {
|
|
18000
18044
|
return count === 1 ? singular : pluralLabel;
|
|
18001
18045
|
}
|
|
18002
18046
|
async function ctrUnderperformersReportWith(input, dependencies) {
|
|
@@ -18099,11 +18143,11 @@ async function ctrUnderperformersReportWith(input, dependencies) {
|
|
|
18099
18143
|
minImpressions,
|
|
18100
18144
|
limit,
|
|
18101
18145
|
brandFiltering: input.includeBrand ? "included" : "excluded",
|
|
18102
|
-
verdict: top ? `${selection.eligibleUnderperformers} CTR ${plural$
|
|
18146
|
+
verdict: top ? `${selection.eligibleUnderperformers} CTR ${plural$6(selection.eligibleUnderperformers, "underperformer")} found in retained rows, with a calculated heuristic shortfall of ${totalClickShortfall.toFixed(0)} clicks for this window. Start review with "${top.query}" because it has the largest gap.` : dataStatus === "partial" ? "No material CTR underperformers remained in the validated retained rows, but partial evidence prevents an all-clear." : `No high-impression rows at average position below 4 in the retained rows are materially below the heuristic CTR benchmark.`
|
|
18103
18147
|
},
|
|
18104
18148
|
items,
|
|
18105
18149
|
caveats: [
|
|
18106
|
-
`Date window: ${range.startDate} to ${range.endDate} (${days} ${plural$
|
|
18150
|
+
`Date window: ${range.startDate} to ${range.endDate} (${days} ${plural$6(days, "day")}), using final GSC data where available.`,
|
|
18107
18151
|
`Brand queries: ${input.includeBrand ? "included" : "excluded"}. Brand terms used: ${brand.terms.join(", ") || "none"} (${brand.source === "provided" ? "provided" : "host forms, plus the bare domain stem only when Search Console shows the site is its navigational answer"}).`,
|
|
18108
18152
|
`Only rows at average position below 4 with at least ${minImpressions} impressions were checked. Rows at position 4 to 10 belong to quick-wins, which uses the same site CTR curve.`,
|
|
18109
18153
|
minImpressionsCaveat({
|
|
@@ -18111,13 +18155,13 @@ async function ctrUnderperformersReportWith(input, dependencies) {
|
|
|
18111
18155
|
belowMinimumRows: selection.belowMinimumRows
|
|
18112
18156
|
}),
|
|
18113
18157
|
"Expected CTR uses the site CTR curve from all page-one rows (75th percentile of qualified URL CTRs per position) when enough peer data exists, otherwise the fallback position curve.",
|
|
18114
|
-
...selection.fragmentRows ? [`${selection.fragmentRows} source ${plural$
|
|
18115
|
-
...selection.flaggedRows ? [`${selection.flaggedRows} eligible ${plural$
|
|
18158
|
+
...selection.fragmentRows ? [`${selection.fragmentRows} source ${plural$6(selection.fragmentRows, "row")} with a #fragment URL ${selection.fragmentRows === 1 ? "was" : "were"} merged into the base URL.`] : [],
|
|
18159
|
+
...selection.flaggedRows ? [`${selection.flaggedRows} eligible ${plural$6(selection.flaggedRows, "row")} carry heuristic query flags (likely-automated or navigational-other) and rank after unflagged rows.`] : [],
|
|
18116
18160
|
"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.",
|
|
18117
|
-
...selection.invalidRows ? [`${selection.invalidRows} invalid provider ${plural$
|
|
18118
|
-
...selection.duplicateRows ? [`${selection.duplicateRows} repeated query/page ${plural$
|
|
18161
|
+
...selection.invalidRows ? [`${selection.invalidRows} invalid provider ${plural$6(selection.invalidRows, "row")} ${selection.invalidRows === 1 ? "was" : "were"} excluded, so this report is partial.`] : [],
|
|
18162
|
+
...selection.duplicateRows ? [`${selection.duplicateRows} repeated query/page ${plural$6(selection.duplicateRows, "row")} ${selection.duplicateRows === 1 ? "was" : "were"} aggregated before CTR and average position were calculated.`] : [],
|
|
18119
18163
|
...possiblyTruncated ? [`The retained-row limit of ${MAX_GSC_ROWS$3} was reached, so lower-ranked source rows may be absent.`] : [],
|
|
18120
|
-
...selection.limitedUnderperformers ? [`${selection.limitedUnderperformers} eligible ${plural$
|
|
18164
|
+
...selection.limitedUnderperformers ? [`${selection.limitedUnderperformers} eligible ${plural$6(selection.limitedUnderperformers, "underperformer")} ${selection.limitedUnderperformers === 1 ? "was" : "were"} omitted by the output limit.`] : []
|
|
18121
18165
|
],
|
|
18122
18166
|
recommendations: top ? [
|
|
18123
18167
|
`Review the live SERP and displayed snippet for "${top.query}" first. Test the title or meta description only if the observed framing does not match the query intent.`,
|
|
@@ -21952,7 +21996,7 @@ function anomalyEvidence(anomaly) {
|
|
|
21952
21996
|
if (anomaly.direction === "normal") return `${formatMetric(anomaly.metric)} changed ${changePhrase} (${anomaly.baselineMean.toLocaleString("en-GB")} -> ${anomaly.comparisonMean.toLocaleString("en-GB")} per observed day${anomaly.zScore === null ? "; the baseline had no observed variance" : `, z ${anomaly.zScore}`}).`;
|
|
21953
21997
|
return `${formatMetric(anomaly.metric)} ${direction} ${changePhrase} (${anomaly.baselineMean.toLocaleString("en-GB")} -> ${anomaly.comparisonMean.toLocaleString("en-GB")} per observed day${anomaly.zScore === null ? "; outside the flat observed baseline" : `, z ${anomaly.zScore}`}).`;
|
|
21954
21998
|
}
|
|
21955
|
-
function plural$
|
|
21999
|
+
function plural$5(count, singular, pluralLabel = `${singular}s`) {
|
|
21956
22000
|
return count === 1 ? singular : pluralLabel;
|
|
21957
22001
|
}
|
|
21958
22002
|
function strongestSignal(anomalies) {
|
|
@@ -22008,13 +22052,13 @@ function interpretUpdateCorrelation(input) {
|
|
|
22008
22052
|
summary,
|
|
22009
22053
|
evidence: [
|
|
22010
22054
|
...input.anomalies.map(anomalyEvidence),
|
|
22011
|
-
hasUpdate ? `${input.overlappingUpdates.length} official Google Ranking update ${plural$
|
|
22055
|
+
hasUpdate ? `${input.overlappingUpdates.length} official Google Ranking update ${plural$5(input.overlappingUpdates.length, "window")} overlapped the change date or comparison window: ${updateNames.join(", ")}.` : "No official Google Ranking update windows overlapped the change date or comparison window.",
|
|
22012
22056
|
largeMovement ? "Both clicks and impressions moved sharply in the same direction." : "Clicks and impressions did not both meet the sharp-movement evidence threshold.",
|
|
22013
22057
|
hasConfounders ? `Known overlapping site changes: ${confounders.map((confounder) => confounder.title).join("; ")}.` : "No overlapping site changes were supplied."
|
|
22014
22058
|
],
|
|
22015
22059
|
caveats: [
|
|
22016
22060
|
`GSC window: ${input.days} days; recent comparison window: ${input.recentDays} days.`,
|
|
22017
|
-
`Update overlap padding: ${input.paddingDays} ${plural$
|
|
22061
|
+
`Update overlap padding: ${input.paddingDays} ${plural$5(input.paddingDays, "day")} either side of the step-change date, or of the recent comparison window when no step change was found.`,
|
|
22018
22062
|
input.refresh ? "Data freshness: cache bypassed for GSC data." : "Data freshness: local GSC cache may have been used.",
|
|
22019
22063
|
"Source: official Google Search Status Dashboard Ranking incidents only; third-party volatility and unconfirmed chatter are not included.",
|
|
22020
22064
|
"An update-window overlap cannot establish causation, even when movement is large and no known site change was recorded.",
|
|
@@ -22182,7 +22226,7 @@ function analyzeTrafficAnomalyRows(input) {
|
|
|
22182
22226
|
if (step && changeDate) {
|
|
22183
22227
|
const previousDate = aggregated.rows[step.index - 1]?.keys[0];
|
|
22184
22228
|
const gap = previousDate ? calendarDays(previousDate, changeDate) - 2 : 0;
|
|
22185
|
-
stepCaveats.push(`${formatMetric(metric)} changed level on ${changeDate}, so its baseline is every observed day before that date and its comparison is every observed day from it.`, ...gap > 0 ? [`The ${metric} change date follows ${gap} missing ${plural$
|
|
22229
|
+
stepCaveats.push(`${formatMetric(metric)} changed level on ${changeDate}, so its baseline is every observed day before that date and its comparison is every observed day from it.`, ...gap > 0 ? [`The ${metric} change date follows ${gap} missing ${plural$5(gap, "day")}; the change may have started on one of them.`] : []);
|
|
22186
22230
|
return compareSegments({
|
|
22187
22231
|
site: input.site,
|
|
22188
22232
|
metric,
|
|
@@ -22213,9 +22257,9 @@ function analyzeTrafficAnomalyRows(input) {
|
|
|
22213
22257
|
const comparisonMissingDays = input.recentDays - comparisonRows.length;
|
|
22214
22258
|
const missingDays = expectedDays - aggregated.rows.length;
|
|
22215
22259
|
const caveats = [
|
|
22216
|
-
...missingDays > 0 ? [`${missingDays} requested calendar ${plural$
|
|
22217
|
-
...aggregated.invalidRows > 0 ? [`${aggregated.invalidRows} invalid provider ${plural$
|
|
22218
|
-
...aggregated.duplicateRows > 0 ? [`${aggregated.duplicateRows} duplicate date ${plural$
|
|
22260
|
+
...missingDays > 0 ? [`${missingDays} requested calendar ${plural$5(missingDays, "day")} had no returned date aggregate and ${missingDays === 1 ? "was" : "were"} not filled with zeros.`] : [],
|
|
22261
|
+
...aggregated.invalidRows > 0 ? [`${aggregated.invalidRows} invalid provider ${plural$5(aggregated.invalidRows, "row")} ${aggregated.invalidRows === 1 ? "was" : "were"} excluded.`] : [],
|
|
22262
|
+
...aggregated.duplicateRows > 0 ? [`${aggregated.duplicateRows} duplicate date ${plural$5(aggregated.duplicateRows, "row")} ${aggregated.duplicateRows === 1 ? "was" : "were"} aggregated before analysis.`] : [],
|
|
22219
22263
|
...stepCaveats
|
|
22220
22264
|
];
|
|
22221
22265
|
return {
|
|
@@ -23296,7 +23340,7 @@ const VOID = /* @__PURE__ */ new Set([
|
|
|
23296
23340
|
"col",
|
|
23297
23341
|
"embed"
|
|
23298
23342
|
]);
|
|
23299
|
-
const ENTITIES = {
|
|
23343
|
+
const ENTITIES$1 = {
|
|
23300
23344
|
amp: "&",
|
|
23301
23345
|
lt: "<",
|
|
23302
23346
|
gt: ">",
|
|
@@ -23321,7 +23365,7 @@ function decode(text) {
|
|
|
23321
23365
|
const point = Number.parseInt(code.slice(hex ? 2 : 1), hex ? 16 : 10);
|
|
23322
23366
|
return point > 0 && point <= 1114111 ? String.fromCodePoint(point) : "";
|
|
23323
23367
|
}
|
|
23324
|
-
return ENTITIES[code.toLowerCase()] ?? match;
|
|
23368
|
+
return ENTITIES$1[code.toLowerCase()] ?? match;
|
|
23325
23369
|
});
|
|
23326
23370
|
}
|
|
23327
23371
|
const clean$1 = (value) => value.split(A_OPEN).join("").replace(/\s+/g, " ").trim();
|
|
@@ -23562,7 +23606,7 @@ var SentenceStore = class SentenceStore {
|
|
|
23562
23606
|
});
|
|
23563
23607
|
}
|
|
23564
23608
|
};
|
|
23565
|
-
const bareHost = (host) => host.toLowerCase().replace(/^www\./, "");
|
|
23609
|
+
const bareHost$1 = (host) => host.toLowerCase().replace(/^www\./, "");
|
|
23566
23610
|
function crawlUrl(value, base) {
|
|
23567
23611
|
try {
|
|
23568
23612
|
const url = base ? new URL(value, base) : new URL(value);
|
|
@@ -23628,10 +23672,10 @@ async function crawlForLinks(input) {
|
|
|
23628
23672
|
async function crawl(input, robots, sentences, renderer, warnings) {
|
|
23629
23673
|
const start = crawlUrl(input.startUrl) ?? input.startUrl;
|
|
23630
23674
|
let origin = new URL(start).origin;
|
|
23631
|
-
const site = bareHost(new URL(start).host);
|
|
23675
|
+
const site = bareHost$1(new URL(start).host);
|
|
23632
23676
|
const sameSite = (url) => {
|
|
23633
23677
|
try {
|
|
23634
|
-
return bareHost(new URL(url).host) === site;
|
|
23678
|
+
return bareHost$1(new URL(url).host) === site;
|
|
23635
23679
|
} catch {
|
|
23636
23680
|
return false;
|
|
23637
23681
|
}
|
|
@@ -24125,15 +24169,15 @@ function internalLinksReportRange(input, fallbackDays, now) {
|
|
|
24125
24169
|
days: Math.round((Date.parse(`${endDate}T00:00:00Z`) - Date.parse(`${startDate}T00:00:00Z`)) / DAY$1) + 1
|
|
24126
24170
|
};
|
|
24127
24171
|
}
|
|
24128
|
-
const plural$
|
|
24172
|
+
const plural$4 = (count, word) => `${count} ${word}${count === 1 ? "" : "s"}`;
|
|
24129
24173
|
function internalLinksVerdict(report) {
|
|
24130
24174
|
const { counts } = report;
|
|
24131
24175
|
if (report.dataStatus === "blocked") return "robots.txt does not allow this crawler to read the site, so nothing was checked.";
|
|
24132
24176
|
if (report.dataStatus === "empty") return "No HTML page could be crawled, so there is nothing to link.";
|
|
24133
24177
|
if (report.dataStatus === "target-not-crawled") return "The target page could not be crawled as its own page (it failed, left the site, or names another page as canonical). Check it with seo page first.";
|
|
24134
24178
|
if (report.dataStatus === "target-not-linkable") return `The target is ${report.target?.noindex ? "noindex" : `a ${report.target?.type?.replaceAll("_", " ")} page`}, so no links to it are suggested.`;
|
|
24135
|
-
const judged = report.judge.status === "used" || report.judge.status === "partial" ? `${counts.strong} strong and ${counts.possible} possible link${counts.strong + counts.possible === 1 ? "" : "s"} judged by ${report.judge.model}${counts.heuristic ? `, plus ${plural$
|
|
24136
|
-
const scope = report.mode === "url" ? `to ${report.target?.url}` : `across ${plural$
|
|
24179
|
+
const judged = report.judge.status === "used" || report.judge.status === "partial" ? `${counts.strong} strong and ${counts.possible} possible link${counts.strong + counts.possible === 1 ? "" : "s"} judged by ${report.judge.model}${counts.heuristic ? `, plus ${plural$4(counts.heuristic, "heuristic link")} where a request failed` : ""}` : `${plural$4(counts.heuristic, "heuristic link")} (no decision model)`;
|
|
24180
|
+
const scope = report.mode === "url" ? `to ${report.target?.url}` : `across ${plural$4(counts.pagesCrawled, "page")}`;
|
|
24137
24181
|
const capped = report.crawl.capReached ? ` The crawl stopped at its limit of ${report.crawl.pageLimit} pages, so pages beyond it were not checked.` : "";
|
|
24138
24182
|
return `${judged} ${scope}${counts.heldBack ? `; ${counts.heldBack} held back by the evidence floors` : ""}.${capped}`;
|
|
24139
24183
|
}
|
|
@@ -26574,14 +26618,14 @@ function* retainedQueryClusterRows(input) {
|
|
|
26574
26618
|
};
|
|
26575
26619
|
}
|
|
26576
26620
|
}
|
|
26577
|
-
function plural$
|
|
26621
|
+
function plural$3(count, singular, pluralLabel = `${singular}s`) {
|
|
26578
26622
|
return count === 1 ? singular : pluralLabel;
|
|
26579
26623
|
}
|
|
26580
26624
|
function reportVerdict(input) {
|
|
26581
26625
|
if (!input.clusters.length) return "No query clusters were generated for this scope.";
|
|
26582
26626
|
if (input.highOpportunityClusters > 0) {
|
|
26583
26627
|
const verb = input.highOpportunityClusters === 1 ? "has" : "have";
|
|
26584
|
-
return `${input.highOpportunityClusters} ${plural$
|
|
26628
|
+
return `${input.highOpportunityClusters} ${plural$3(input.highOpportunityClusters, "cluster")} ${verb} enough demand and weak enough performance to review first.`;
|
|
26585
26629
|
}
|
|
26586
26630
|
return "No obvious high-opportunity clusters were found. Use this report for content structure and intent mapping rather than urgent fixes.";
|
|
26587
26631
|
}
|
|
@@ -26722,7 +26766,7 @@ function movementLine(report) {
|
|
|
26722
26766
|
}
|
|
26723
26767
|
function countPhrase(count, singular, pluralLabel = `${singular}s`) {
|
|
26724
26768
|
if (count === 0) return `no ${pluralLabel}`;
|
|
26725
|
-
return `${count} ${plural$
|
|
26769
|
+
return `${count} ${plural$2(count, singular, pluralLabel)}`;
|
|
26726
26770
|
}
|
|
26727
26771
|
function headlineLine(report) {
|
|
26728
26772
|
const parts = [
|
|
@@ -26777,7 +26821,7 @@ function decayDiagnosisLabel(diagnosis) {
|
|
|
26777
26821
|
if (diagnosis === "lost_clicks") return "clicks dropped without one dominant signal";
|
|
26778
26822
|
return diagnosis.replaceAll("_", " ");
|
|
26779
26823
|
}
|
|
26780
|
-
function plural$
|
|
26824
|
+
function plural$2(count, singular, pluralLabel = `${singular}s`) {
|
|
26781
26825
|
return count === 1 ? singular : pluralLabel;
|
|
26782
26826
|
}
|
|
26783
26827
|
function decayClusterLine(report) {
|
|
@@ -26785,7 +26829,7 @@ function decayClusterLine(report) {
|
|
|
26785
26829
|
if (!group) return "No material decay cluster stood out.";
|
|
26786
26830
|
const diagnosis = decayDiagnosisLabel(group.diagnosis);
|
|
26787
26831
|
const samples = group.sampleQueries.slice(0, 2).join("; ");
|
|
26788
|
-
return `Top decay cluster: ${group.count} ${plural$
|
|
26832
|
+
return `Top decay cluster: ${group.count} ${plural$2(group.count, "finding")} in the ${group.template.label} template (${diagnosis}), ${group.totalClickLoss.toFixed(0)} fewer observed clicks across rows retained in both windows. Action: ${group.recommendation}${samples ? ` Example queries: ${samples}.` : ""}`;
|
|
26789
26833
|
}
|
|
26790
26834
|
function cannibalSuppressionLine(report) {
|
|
26791
26835
|
const suppressed = report.cannibalization.selection.suppressedQueries;
|
|
@@ -26803,21 +26847,21 @@ function contentOpportunityBullets(report) {
|
|
|
26803
26847
|
if (unavailableSections.length) bullets.push(`Could not assess ${unavailableSections.join(", ")} opportunities in this run; see report caveats.`);
|
|
26804
26848
|
if (report.summary.decayItems) {
|
|
26805
26849
|
const top = report.decay.items[0];
|
|
26806
|
-
bullets.push(top ? `${report.summary.decayItems} observed retained query/page ${plural$
|
|
26850
|
+
bullets.push(top ? `${report.summary.decayItems} observed retained query/page ${plural$2(report.summary.decayItems, "decline")} need review. Start with "${top.query}" on ${top.url}; it had ${top.clickLoss.toFixed(0)} fewer clicks.` : `${report.summary.decayItems} observed retained query/page ${plural$2(report.summary.decayItems, "decline")} need review.`);
|
|
26807
26851
|
bullets.push(decayClusterLine(report));
|
|
26808
26852
|
}
|
|
26809
26853
|
if (report.summary.cannibalItems) {
|
|
26810
26854
|
const top = report.cannibalization.items[0];
|
|
26811
|
-
bullets.push(top ? `${report.summary.cannibalItems} multi-URL query ${plural$
|
|
26855
|
+
bullets.push(top ? `${report.summary.cannibalItems} multi-URL query ${plural$2(report.summary.cannibalItems, "candidate")} need intent and technical review. Start with "${top.query}" and decide whether ${top.pages.length} ranking URLs answer the same intent.` : `${report.summary.cannibalItems} multi-URL query ${plural$2(report.summary.cannibalItems, "candidate")} need intent and technical review.`);
|
|
26812
26856
|
}
|
|
26813
26857
|
if (report.cannibalization.selection.suppressedQueries) bullets.push(cannibalSuppressionLine(report));
|
|
26814
26858
|
if (report.summary.strikingDistanceItems) {
|
|
26815
26859
|
const top = report.strikingDistance.items[0];
|
|
26816
|
-
bullets.push(top ? `${countLabel(report.summary.strikingDistanceItems, "query/page candidate")} ${plural$
|
|
26860
|
+
bullets.push(top ? `${countLabel(report.summary.strikingDistanceItems, "query/page candidate")} ${plural$2(report.summary.strikingDistanceItems, "has", "have")} an average GSC position above 10 and at most 20. Start by investigating "${top.query}" on ${top.url}; it averages position ${top.position.toFixed(1)} with ${top.impressions.toFixed(0)} impressions.` : `${countLabel(report.summary.strikingDistanceItems, "query/page candidate")} ${plural$2(report.summary.strikingDistanceItems, "has", "have")} an average GSC position above 10 and at most 20.`);
|
|
26817
26861
|
}
|
|
26818
26862
|
if (!skippedReason(report, "quick-win opportunities") && report.summary.quickWinItems) {
|
|
26819
26863
|
const top = report.quickWins.items[0];
|
|
26820
|
-
bullets.push(top ? `${report.summary.quickWinItems} retained query/page ${plural$
|
|
26864
|
+
bullets.push(top ? `${report.summary.quickWinItems} retained query/page ${plural$2(report.summary.quickWinItems, "candidate")} fell below a site-peer or versioned heuristic CTR target. Start with "${top.query}" on ${top.url}; it had ${top.impressions.toFixed(0)} impressions at average position ${top.position.toFixed(1)}.` : `${report.summary.quickWinItems} CTR-target ${plural$2(report.summary.quickWinItems, "candidate")} need review.`);
|
|
26821
26865
|
}
|
|
26822
26866
|
const templateLine = templateOpportunityLine(report);
|
|
26823
26867
|
if (!templateLine.startsWith("No dominant")) bullets.push(templateLine);
|
|
@@ -28539,11 +28583,12 @@ function ga4MatchReason(match, site) {
|
|
|
28539
28583
|
//#endregion
|
|
28540
28584
|
//#region packages/core/src/commands/clicky.ts
|
|
28541
28585
|
const CLICKY_KEY_PREFIX = "x-clicky-";
|
|
28586
|
+
const MAX_INSPECT_LIMIT = 2e3;
|
|
28542
28587
|
//#endregion
|
|
28543
28588
|
//#region packages/core/src/commands/page-facts.ts
|
|
28544
28589
|
const MAX_PAGES = 20;
|
|
28545
|
-
const TIMEOUT_MS$
|
|
28546
|
-
const MAX_BYTES$
|
|
28590
|
+
const TIMEOUT_MS$2 = 6e3;
|
|
28591
|
+
const MAX_BYTES$2 = 1e6;
|
|
28547
28592
|
const clean = (value) => value?.trim().replace(/\s+/g, " ") || null;
|
|
28548
28593
|
async function boundedText(response) {
|
|
28549
28594
|
if (!response.body) return "";
|
|
@@ -28551,7 +28596,7 @@ async function boundedText(response) {
|
|
|
28551
28596
|
const decoder = new TextDecoder();
|
|
28552
28597
|
let text = "";
|
|
28553
28598
|
let total = 0;
|
|
28554
|
-
while (total < MAX_BYTES$
|
|
28599
|
+
while (total < MAX_BYTES$2) {
|
|
28555
28600
|
const { done, value } = await reader.read();
|
|
28556
28601
|
if (done) return text + decoder.decode();
|
|
28557
28602
|
total += value.byteLength;
|
|
@@ -28631,12 +28676,14 @@ async function facts(url) {
|
|
|
28631
28676
|
"user-agent": SEO_CRAWLER_USER_AGENT,
|
|
28632
28677
|
accept: "text/html"
|
|
28633
28678
|
},
|
|
28634
|
-
signal: AbortSignal.timeout(TIMEOUT_MS$
|
|
28679
|
+
signal: AbortSignal.timeout(TIMEOUT_MS$2)
|
|
28635
28680
|
});
|
|
28636
28681
|
out.status = response.status;
|
|
28637
28682
|
const location = response.headers.get("location");
|
|
28638
28683
|
if (location) out.redirects_to = new URL(location, url).toString();
|
|
28639
28684
|
const headerRobots = response.headers.get("x-robots-tag");
|
|
28685
|
+
const auth = response.headers.get("www-authenticate");
|
|
28686
|
+
if (auth) out.www_authenticate = auth.slice(0, 200);
|
|
28640
28687
|
if (response.status !== 200 || !response.headers.get("content-type")?.includes("html")) {
|
|
28641
28688
|
out.robots = headerRobots;
|
|
28642
28689
|
await response.body?.cancel();
|
|
@@ -28653,6 +28700,37 @@ async function facts(url) {
|
|
|
28653
28700
|
function checkPages(urls) {
|
|
28654
28701
|
return Promise.all(urls.slice(0, MAX_PAGES).map(facts));
|
|
28655
28702
|
}
|
|
28703
|
+
const MAX_HOPS = 5;
|
|
28704
|
+
const redirectKind = (status) => status === 301 || status === 308 ? "permanent" : status === 302 || status === 303 || status === 307 ? "temporary" : null;
|
|
28705
|
+
async function liveCheck(url) {
|
|
28706
|
+
const first = await facts(url);
|
|
28707
|
+
const hops = [];
|
|
28708
|
+
const seen = /* @__PURE__ */ new Set([url]);
|
|
28709
|
+
let last = first;
|
|
28710
|
+
while (last.redirects_to && last.status !== null) {
|
|
28711
|
+
const kind = redirectKind(last.status);
|
|
28712
|
+
if (!kind) break;
|
|
28713
|
+
const to = last.redirects_to;
|
|
28714
|
+
hops.push({
|
|
28715
|
+
from: last.url,
|
|
28716
|
+
status: last.status,
|
|
28717
|
+
to,
|
|
28718
|
+
kind
|
|
28719
|
+
});
|
|
28720
|
+
if (seen.has(to) || hops.length > MAX_HOPS) return {
|
|
28721
|
+
first,
|
|
28722
|
+
hops,
|
|
28723
|
+
last: null
|
|
28724
|
+
};
|
|
28725
|
+
seen.add(to);
|
|
28726
|
+
last = await facts(to);
|
|
28727
|
+
}
|
|
28728
|
+
return {
|
|
28729
|
+
first,
|
|
28730
|
+
hops,
|
|
28731
|
+
last
|
|
28732
|
+
};
|
|
28733
|
+
}
|
|
28656
28734
|
//#endregion
|
|
28657
28735
|
//#region packages/core/src/commands/report.ts
|
|
28658
28736
|
const SECTIONS = {
|
|
@@ -28685,16 +28763,23 @@ const RESEARCH_HELP = `keyword_metrics (keywords: volume, difficulty, CPC), rank
|
|
|
28685
28763
|
function indexStatusFrom(result) {
|
|
28686
28764
|
const status = result?.inspectionResult?.indexStatusResult ?? result?.indexStatusResult ?? {};
|
|
28687
28765
|
const text = (value) => typeof value === "string" ? value : null;
|
|
28766
|
+
const list = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
28688
28767
|
return {
|
|
28689
28768
|
verdict: text(status.verdict),
|
|
28690
28769
|
coverage: text(status.coverageState),
|
|
28691
28770
|
indexing: text(status.indexingState),
|
|
28692
28771
|
last_crawl: text(status.lastCrawlTime),
|
|
28693
28772
|
google_canonical: text(status.googleCanonical),
|
|
28694
|
-
user_canonical: text(status.userCanonical)
|
|
28773
|
+
user_canonical: text(status.userCanonical),
|
|
28774
|
+
page_fetch: text(status.pageFetchState),
|
|
28775
|
+
robots_txt: text(status.robotsTxtState),
|
|
28776
|
+
crawled_as: text(status.crawledAs),
|
|
28777
|
+
referring_urls: list(status.referringUrls),
|
|
28778
|
+
sitemaps: list(status.sitemap)
|
|
28695
28779
|
};
|
|
28696
28780
|
}
|
|
28697
28781
|
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.`;
|
|
28782
|
+
const INDEXING_RULE = `Google’s indexing reasons for 100 known URLs a run in a fixed order (lost-click pages, Search Console pages by clicks then impressions, sitemap URLs, then the rest), each checked live and labelled fix, check, expected or indexed from the evidence, sorted by reason then URL.`;
|
|
28698
28783
|
//#endregion
|
|
28699
28784
|
//#region packages/core/src/commands/tools.ts
|
|
28700
28785
|
const site = z.string().min(1).describe("Search Console property or site, such as sc-domain:example.com, https://example.com/ or example.com.");
|
|
@@ -28729,9 +28814,15 @@ z.object({}), `${Object.entries(SECTIONS).map(([id, text]) => `- ${id}: ${text}`
|
|
|
28729
28814
|
site,
|
|
28730
28815
|
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).describe("YYYY-MM-DD: the first day after the change."),
|
|
28731
28816
|
pages: z.string().min(1).optional().describe("Optional URL text, such as /blog/.")
|
|
28732
|
-
}), `${LOST_PAGES_RULE}`, z.object({
|
|
28817
|
+
}), `${INDEXING_RULE}${LOST_PAGES_RULE}`, z.object({
|
|
28818
|
+
site,
|
|
28819
|
+
...dates,
|
|
28820
|
+
...detail
|
|
28821
|
+
}), z.object({
|
|
28733
28822
|
site,
|
|
28734
|
-
...dates
|
|
28823
|
+
...dates,
|
|
28824
|
+
max_urls: z.number().int().min(1).max(MAX_INSPECT_LIMIT).optional().describe(`URLs to inspect with Google, 100 by default, up to the URL Inspection quota left today.`),
|
|
28825
|
+
...detail
|
|
28735
28826
|
}), `${RESEARCH_HELP}`, z.object({
|
|
28736
28827
|
provider: z.enum(RESEARCH_PROVIDERS),
|
|
28737
28828
|
action: z.enum(RESEARCH_ACTIONS),
|
|
@@ -28749,6 +28840,36 @@ z.object({}), `${Object.entries(SECTIONS).map(([id, text]) => `- ${id}: ${text}`
|
|
|
28749
28840
|
...detail
|
|
28750
28841
|
});
|
|
28751
28842
|
//#endregion
|
|
28843
|
+
//#region packages/core/src/gsc/client/inspection-cache.ts
|
|
28844
|
+
const INSPECTION_FRESH_MS = 7 * 864e5;
|
|
28845
|
+
function readSavedInspections(site, urls, now = Date.now()) {
|
|
28846
|
+
const found = /* @__PURE__ */ new Map();
|
|
28847
|
+
try {
|
|
28848
|
+
const statement = cacheDb().prepare(`SELECT status_json, fetched_at FROM url_inspection_cache
|
|
28849
|
+
WHERE site_url = ? AND url = ? AND fetched_at > ?`);
|
|
28850
|
+
for (const url of urls) {
|
|
28851
|
+
const row = statement.get(site, url, now - INSPECTION_FRESH_MS);
|
|
28852
|
+
if (!row) continue;
|
|
28853
|
+
found.set(url, {
|
|
28854
|
+
status: JSON.parse(row.status_json),
|
|
28855
|
+
inspectedAt: new Date(row.fetched_at).toISOString()
|
|
28856
|
+
});
|
|
28857
|
+
}
|
|
28858
|
+
} catch {
|
|
28859
|
+
return /* @__PURE__ */ new Map();
|
|
28860
|
+
}
|
|
28861
|
+
return found;
|
|
28862
|
+
}
|
|
28863
|
+
function saveInspection(site, url, status, now = Date.now()) {
|
|
28864
|
+
const json = JSON.stringify(status);
|
|
28865
|
+
writeToCache((database) => {
|
|
28866
|
+
database.prepare(`INSERT OR REPLACE INTO url_inspection_cache
|
|
28867
|
+
(site_url, url, status_json, fetched_at, expires_at)
|
|
28868
|
+
VALUES (?, ?, ?, ?, ?)`).run(site, url, json, now, now + INSPECTION_FRESH_MS);
|
|
28869
|
+
noteCacheWrite(Buffer.byteLength(json) + url.length, database);
|
|
28870
|
+
});
|
|
28871
|
+
}
|
|
28872
|
+
//#endregion
|
|
28752
28873
|
//#region packages/core/src/providers/api-key-credentials.ts
|
|
28753
28874
|
function createApiKeyCredentialStore(input) {
|
|
28754
28875
|
const normalize = (value) => {
|
|
@@ -28944,16 +29065,19 @@ function nodeCommandDeps(options) {
|
|
|
28944
29065
|
accountEmail: options.account
|
|
28945
29066
|
}),
|
|
28946
29067
|
checkPages,
|
|
28947
|
-
|
|
28948
|
-
|
|
28949
|
-
|
|
28950
|
-
|
|
28951
|
-
|
|
28952
|
-
|
|
28953
|
-
|
|
28954
|
-
|
|
28955
|
-
|
|
28956
|
-
|
|
29068
|
+
liveCheck,
|
|
29069
|
+
inspect: async (site, url) => indexStatusFrom(await inspectUrl({
|
|
29070
|
+
siteUrl: site,
|
|
29071
|
+
inspectionUrl: url,
|
|
29072
|
+
accountEmail: options.account
|
|
29073
|
+
})),
|
|
29074
|
+
inspectionQuota: (site) => urlInspectionQuota({
|
|
29075
|
+
siteUrl: site,
|
|
29076
|
+
accountEmail: options.account
|
|
29077
|
+
}),
|
|
29078
|
+
savedInspections: {
|
|
29079
|
+
read: (site, urls) => options.refresh ? /* @__PURE__ */ new Map() : readSavedInspections(site, urls),
|
|
29080
|
+
save: saveInspection
|
|
28957
29081
|
},
|
|
28958
29082
|
pageMarkdown: async (url) => extractMainMarkdown(await fetchPage(url, { refresh: options.refresh })),
|
|
28959
29083
|
call: options.call,
|
|
@@ -30730,6 +30854,6 @@ function isoDate(daysAgo = 0) {
|
|
|
30730
30854
|
return date.toISOString().slice(0, 10);
|
|
30731
30855
|
}
|
|
30732
30856
|
//#endregion
|
|
30733
|
-
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_MAX_PAGES, DEFAULT_SEARCH_CONSOLE_EXPORT_ROW_LIMIT, DEFAULT_UNREACHED_PAGE_LIMIT, DataForSeoClient, DecisionModelError, DecisionRunner, GSC_FINAL_DATA_LAG_DAYS, GSC_TIME_ZONE, GoogleApiError, INCIDENTS_URL, JsonFileStorageAdapter, LINK_ROLES, 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_MAX_PAGES, MAX_SEARCH_CONSOLE_EXPORT_FILES, MAX_SEARCH_CONSOLE_EXPORT_ROW_LIMIT, PAGE_TYPES, PROVIDER_SECRET_NAMES, ProviderError, ProviderExtensionRegistry, RESEARCH_IMPORT_COLUMN_KEYS, ResponseSizeLimitError, SEARCH_INTENTS, 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, TYPESAFE_API_KEY_ENV, agentActionsView, aiReferralSourceForValue, aiReferralsReport, analyticsConnectionLabel, analyticsConnectionProviderId, analyticsConnectionSchema, analyticsProviderDetails, analyticsProviderSchema, analyzeAiReferralRows, analyzeCannibalImportRows, analyzeCannibalRows, analyzeCtrUnderperformersFromRows, analyzeDecay, analyzeIndexCoverageSignals, analyzePageOpportunitiesFromRows, analyzeQueryClustersFromRows, analyzeQuickWinsFromRows, analyzeSecondPageRows, analyzeSegmentRows, analyzeStrikingDistanceRows, analyzeTrafficAnomalyRows, assertCrawlConfigLimits, assertUrlMatchesGscProperty, auditCrawlPages, auditCrawlRequests, auditPage, authStatus, bearerFetch, boundedSitemapInventory, buildAgentFindings, buildQueryClusterReportFromRows, buildTelemetryPayload, cacheDb, 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, isNewerVersion, isSkippableReportError, isTelemetryCi, isTelemetryOperation, isTelemetryReport, isoDate, isoWeek, latestFinalGscDate, linkRecover, linkRecoverRecommendation, listAnalyticsAccounts, listAnalyticsDataStreams, listGa4AccountSummaries, listGa4DataStreams, listGoogleAccounts, listInstalledProviderExtensions, listRules, listSearchConsoleSites, listSearchUpdates, listSites, loadInstalledProviderExtensions, loadProviderExtensions, loadProviderPackage, loadSearchConsoleExport, localAccounts, localBing, localCredentialsProvider, localError, localGoogleFetch, localInternalLinksDependencies, 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, siteStartUrl, startCloudLogin, strikingDistance, strikingDistanceWith, summarizeCrawlReport, pageTechnicalSignals as technicalPageSignals, telemetryErrorCategory, toSeoError, tokenSchema, tokenStoreSchema, topFixes, trackTelemetryCommandFailed, trackTelemetryReportComplete, trackTelemetryReportFailed, trackTelemetryReportStart, trackTelemetrySetupComplete, trafficAnomaly, trafficAnomalyWith, typesafeDecisionModel, unavailableSegmentImpactReport, unicodeTokens, uninstallProviderExtension, updateCorrelation, updateNotice, updatePostmortemCsvFiles, updatePostmortemWorkflow, validateResearchImportColumnSources, verifyAnalyticsProvider, verifyProviderExtension, verifyQueryContent, verifyQueryContentWith, withAgentReportContract, withRankingHistory, workflowPresentation, writeAhrefsApiKey, writeBingApiKey, writeClickySiteKey, writeConfig, writeDataForSeoCredentials, writeInstalledProviderPackages, writeOauthClient, writeProviderExtensionAccount, writeProviderExtensionCredentials, writeProviderSecret, writeToCache, writeTokens, xRobotsDirectives };
|
|
30857
|
+
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_MAX_PAGES, DEFAULT_SEARCH_CONSOLE_EXPORT_ROW_LIMIT, DEFAULT_UNREACHED_PAGE_LIMIT, DataForSeoClient, DecisionModelError, DecisionRunner, GSC_FINAL_DATA_LAG_DAYS, GSC_TIME_ZONE, GoogleApiError, INCIDENTS_URL, JsonFileStorageAdapter, LINK_ROLES, 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_MAX_PAGES, MAX_SEARCH_CONSOLE_EXPORT_FILES, MAX_SEARCH_CONSOLE_EXPORT_ROW_LIMIT, PAGE_TYPES, PROVIDER_SECRET_NAMES, ProviderError, ProviderExtensionRegistry, RESEARCH_IMPORT_COLUMN_KEYS, ResponseSizeLimitError, SEARCH_INTENTS, 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, TYPESAFE_API_KEY_ENV, agentActionsView, aiReferralSourceForValue, aiReferralsReport, analyticsConnectionLabel, analyticsConnectionProviderId, analyticsConnectionSchema, analyticsProviderDetails, analyticsProviderSchema, analyzeAiReferralRows, analyzeCannibalImportRows, analyzeCannibalRows, analyzeCtrUnderperformersFromRows, analyzeDecay, analyzeIndexCoverageSignals, analyzePageOpportunitiesFromRows, analyzeQueryClustersFromRows, analyzeQuickWinsFromRows, analyzeSecondPageRows, analyzeSegmentRows, analyzeStrikingDistanceRows, analyzeTrafficAnomalyRows, assertCrawlConfigLimits, assertUrlMatchesGscProperty, auditCrawlPages, auditCrawlRequests, auditPage, authStatus, bearerFetch, boundedSitemapInventory, buildAgentFindings, buildQueryClusterReportFromRows, buildTelemetryPayload, cacheDb, 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, isNewerVersion, isSkippableReportError, isTelemetryCi, isTelemetryOperation, isTelemetryReport, isoDate, isoWeek, latestFinalGscDate, linkRecover, linkRecoverRecommendation, listAnalyticsAccounts, listAnalyticsDataStreams, listGa4AccountSummaries, listGa4DataStreams, listGoogleAccounts, listInstalledProviderExtensions, listRules, listSearchConsoleSites, listSearchUpdates, listSites, loadInstalledProviderExtensions, loadProviderExtensions, loadProviderPackage, loadSearchConsoleExport, localAccounts, localBing, localCredentialsProvider, localError, localGoogleFetch, localInternalLinksDependencies, 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, siteStartUrl, startCloudLogin, strikingDistance, strikingDistanceWith, summarizeCrawlReport, pageTechnicalSignals as technicalPageSignals, telemetryErrorCategory, toSeoError, tokenSchema, tokenStoreSchema, topFixes, trackTelemetryCommandFailed, trackTelemetryReportComplete, trackTelemetryReportFailed, trackTelemetryReportStart, trackTelemetrySetupComplete, trafficAnomaly, trafficAnomalyWith, typesafeDecisionModel, unavailableSegmentImpactReport, unicodeTokens, uninstallProviderExtension, updateCorrelation, updateNotice, updatePostmortemCsvFiles, updatePostmortemWorkflow, urlInspectionQuota, validateResearchImportColumnSources, verifyAnalyticsProvider, verifyProviderExtension, verifyQueryContent, verifyQueryContentWith, withAgentReportContract, withRankingHistory, workflowPresentation, writeAhrefsApiKey, writeBingApiKey, writeClickySiteKey, writeConfig, writeDataForSeoCredentials, writeInstalledProviderPackages, writeOauthClient, writeProviderExtensionAccount, writeProviderExtensionCredentials, writeProviderSecret, writeToCache, writeTokens, xRobotsDirectives };
|
|
30734
30858
|
|
|
30735
30859
|
//# sourceMappingURL=index.js.map
|