@rankcli/agent-runtime 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +242 -0
- package/dist/analyzer-2CSWIQGD.mjs +6 -0
- package/dist/chunk-YNZYHEYM.mjs +774 -0
- package/dist/index.d.mts +4012 -0
- package/dist/index.d.ts +4012 -0
- package/dist/index.js +29672 -0
- package/dist/index.mjs +28602 -0
- package/package.json +53 -0
- package/scripts/build-deno.ts +134 -0
- package/src/audit/ai/analyzer.ts +347 -0
- package/src/audit/ai/index.ts +29 -0
- package/src/audit/ai/prompts/content-analysis.ts +271 -0
- package/src/audit/ai/types.ts +179 -0
- package/src/audit/checks/additional-checks.ts +439 -0
- package/src/audit/checks/ai-citation-worthiness.ts +399 -0
- package/src/audit/checks/ai-content-structure.ts +325 -0
- package/src/audit/checks/ai-readiness.ts +339 -0
- package/src/audit/checks/anchor-text.ts +179 -0
- package/src/audit/checks/answer-conciseness.ts +322 -0
- package/src/audit/checks/asset-minification.ts +270 -0
- package/src/audit/checks/bing-optimization.ts +206 -0
- package/src/audit/checks/brand-mention-optimization.ts +349 -0
- package/src/audit/checks/caching-headers.ts +305 -0
- package/src/audit/checks/canonical-advanced.ts +150 -0
- package/src/audit/checks/canonical-domain.ts +196 -0
- package/src/audit/checks/citation-quality.ts +358 -0
- package/src/audit/checks/client-rendering.ts +542 -0
- package/src/audit/checks/color-contrast.ts +342 -0
- package/src/audit/checks/content-freshness.ts +170 -0
- package/src/audit/checks/content-science.ts +589 -0
- package/src/audit/checks/conversion-elements.ts +526 -0
- package/src/audit/checks/crawlability.ts +220 -0
- package/src/audit/checks/directory-listing.ts +172 -0
- package/src/audit/checks/dom-analysis.ts +191 -0
- package/src/audit/checks/dom-size.ts +246 -0
- package/src/audit/checks/duplicate-content.ts +194 -0
- package/src/audit/checks/eeat-signals.ts +990 -0
- package/src/audit/checks/entity-seo.ts +396 -0
- package/src/audit/checks/featured-snippet.ts +473 -0
- package/src/audit/checks/freshness-signals.ts +443 -0
- package/src/audit/checks/funnel-intent.ts +463 -0
- package/src/audit/checks/hreflang.ts +174 -0
- package/src/audit/checks/html-compliance.ts +302 -0
- package/src/audit/checks/image-dimensions.ts +167 -0
- package/src/audit/checks/images.ts +160 -0
- package/src/audit/checks/indexnow.ts +275 -0
- package/src/audit/checks/interactive-tools.ts +475 -0
- package/src/audit/checks/internal-link-graph.ts +436 -0
- package/src/audit/checks/keyword-analysis.ts +239 -0
- package/src/audit/checks/keyword-cannibalization.ts +385 -0
- package/src/audit/checks/keyword-placement.ts +471 -0
- package/src/audit/checks/links.ts +203 -0
- package/src/audit/checks/llms-txt.ts +224 -0
- package/src/audit/checks/local-seo.ts +296 -0
- package/src/audit/checks/mobile.ts +167 -0
- package/src/audit/checks/modern-images.ts +226 -0
- package/src/audit/checks/navboost-signals.ts +395 -0
- package/src/audit/checks/on-page.ts +209 -0
- package/src/audit/checks/page-resources.ts +285 -0
- package/src/audit/checks/pagination.ts +180 -0
- package/src/audit/checks/performance.ts +153 -0
- package/src/audit/checks/platform-presence.ts +580 -0
- package/src/audit/checks/redirect-analysis.ts +153 -0
- package/src/audit/checks/redirect-chain.ts +389 -0
- package/src/audit/checks/resource-hints.ts +420 -0
- package/src/audit/checks/responsive-css.ts +247 -0
- package/src/audit/checks/responsive-images.ts +396 -0
- package/src/audit/checks/review-ecosystem.ts +415 -0
- package/src/audit/checks/robots-validation.ts +373 -0
- package/src/audit/checks/security-headers.ts +172 -0
- package/src/audit/checks/security.ts +144 -0
- package/src/audit/checks/serp-preview.ts +251 -0
- package/src/audit/checks/site-maturity.ts +444 -0
- package/src/audit/checks/social-meta.test.ts +275 -0
- package/src/audit/checks/social-meta.ts +134 -0
- package/src/audit/checks/soft-404.ts +151 -0
- package/src/audit/checks/structured-data.ts +238 -0
- package/src/audit/checks/tech-detection.ts +496 -0
- package/src/audit/checks/topical-clusters.ts +435 -0
- package/src/audit/checks/tracker-bloat.ts +462 -0
- package/src/audit/checks/tracking-verification.test.ts +371 -0
- package/src/audit/checks/tracking-verification.ts +636 -0
- package/src/audit/checks/url-safety.ts +682 -0
- package/src/audit/deno-entry.ts +66 -0
- package/src/audit/discovery/index.ts +15 -0
- package/src/audit/discovery/link-crawler.ts +232 -0
- package/src/audit/discovery/repo-routes.ts +347 -0
- package/src/audit/engine.ts +620 -0
- package/src/audit/fixes/index.ts +209 -0
- package/src/audit/fixes/social-meta-fixes.test.ts +329 -0
- package/src/audit/fixes/social-meta-fixes.ts +463 -0
- package/src/audit/index.ts +74 -0
- package/src/audit/runner.test.ts +299 -0
- package/src/audit/runner.ts +130 -0
- package/src/audit/types.ts +1953 -0
- package/src/content/featured-snippet.ts +367 -0
- package/src/content/generator.test.ts +534 -0
- package/src/content/generator.ts +501 -0
- package/src/content/headline.ts +317 -0
- package/src/content/index.ts +62 -0
- package/src/content/intent.ts +258 -0
- package/src/content/keyword-density.ts +349 -0
- package/src/content/readability.ts +262 -0
- package/src/executor.ts +336 -0
- package/src/fixer.ts +416 -0
- package/src/frameworks/detector.test.ts +248 -0
- package/src/frameworks/detector.ts +371 -0
- package/src/frameworks/index.ts +68 -0
- package/src/frameworks/recipes/angular.yaml +171 -0
- package/src/frameworks/recipes/astro.yaml +206 -0
- package/src/frameworks/recipes/django.yaml +180 -0
- package/src/frameworks/recipes/laravel.yaml +137 -0
- package/src/frameworks/recipes/nextjs.yaml +268 -0
- package/src/frameworks/recipes/nuxt.yaml +175 -0
- package/src/frameworks/recipes/rails.yaml +188 -0
- package/src/frameworks/recipes/react.yaml +202 -0
- package/src/frameworks/recipes/sveltekit.yaml +154 -0
- package/src/frameworks/recipes/vue.yaml +137 -0
- package/src/frameworks/recipes/wordpress.yaml +209 -0
- package/src/frameworks/suggestion-engine.ts +320 -0
- package/src/geo/geo-content.test.ts +305 -0
- package/src/geo/geo-content.ts +266 -0
- package/src/geo/geo-history.test.ts +473 -0
- package/src/geo/geo-history.ts +433 -0
- package/src/geo/geo-tracker.test.ts +359 -0
- package/src/geo/geo-tracker.ts +411 -0
- package/src/geo/index.ts +10 -0
- package/src/git/commit-helper.test.ts +261 -0
- package/src/git/commit-helper.ts +329 -0
- package/src/git/index.ts +12 -0
- package/src/git/pr-helper.test.ts +284 -0
- package/src/git/pr-helper.ts +307 -0
- package/src/index.ts +66 -0
- package/src/keywords/ai-keyword-engine.ts +1062 -0
- package/src/keywords/ai-summarizer.ts +387 -0
- package/src/keywords/ci-mode.ts +555 -0
- package/src/keywords/engine.ts +359 -0
- package/src/keywords/index.ts +151 -0
- package/src/keywords/llm-judge.ts +357 -0
- package/src/keywords/nlp-analysis.ts +706 -0
- package/src/keywords/prioritizer.ts +295 -0
- package/src/keywords/site-crawler.ts +342 -0
- package/src/keywords/sources/autocomplete.ts +139 -0
- package/src/keywords/sources/competitive-search.ts +450 -0
- package/src/keywords/sources/competitor-analysis.ts +374 -0
- package/src/keywords/sources/dataforseo.ts +206 -0
- package/src/keywords/sources/free-sources.ts +294 -0
- package/src/keywords/sources/gsc.ts +123 -0
- package/src/keywords/topic-grouping.ts +327 -0
- package/src/keywords/types.ts +144 -0
- package/src/keywords/wizard.ts +457 -0
- package/src/loader.ts +40 -0
- package/src/reports/index.ts +7 -0
- package/src/reports/report-generator.test.ts +293 -0
- package/src/reports/report-generator.ts +713 -0
- package/src/scheduler/alerts.test.ts +458 -0
- package/src/scheduler/alerts.ts +328 -0
- package/src/scheduler/index.ts +8 -0
- package/src/scheduler/scheduled-audit.test.ts +377 -0
- package/src/scheduler/scheduled-audit.ts +149 -0
- package/src/test/integration-test.ts +325 -0
- package/src/tools/analyzer.ts +373 -0
- package/src/tools/crawl.ts +293 -0
- package/src/tools/files.ts +301 -0
- package/src/tools/h1-fixer.ts +249 -0
- package/src/tools/index.ts +67 -0
- package/src/tracking/github-action.ts +326 -0
- package/src/tracking/google-analytics.ts +265 -0
- package/src/tracking/index.ts +45 -0
- package/src/tracking/report-generator.ts +386 -0
- package/src/tracking/search-console.ts +335 -0
- package/src/types.ts +134 -0
- package/src/utils/http.ts +302 -0
- package/src/wasm-adapter.ts +297 -0
- package/src/wasm-entry.ts +14 -0
- package/tsconfig.json +17 -0
- package/tsup.wasm.config.ts +26 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,1953 @@
|
|
|
1
|
+
// Comprehensive SEO Audit Types - Matching Ahrefs 170+ checks
|
|
2
|
+
|
|
3
|
+
export type IssueSeverity = 'error' | 'warning' | 'notice';
|
|
4
|
+
|
|
5
|
+
export type IssueCategory =
|
|
6
|
+
| 'crawlability'
|
|
7
|
+
| 'indexability'
|
|
8
|
+
| 'on-page'
|
|
9
|
+
| 'content'
|
|
10
|
+
| 'links'
|
|
11
|
+
| 'images'
|
|
12
|
+
| 'structured-data'
|
|
13
|
+
| 'performance'
|
|
14
|
+
| 'security'
|
|
15
|
+
| 'mobile'
|
|
16
|
+
| 'international'
|
|
17
|
+
| 'ai-readiness'
|
|
18
|
+
| 'social'
|
|
19
|
+
| 'local-seo'
|
|
20
|
+
| 'accessibility'
|
|
21
|
+
| 'framework';
|
|
22
|
+
|
|
23
|
+
export interface AuditIssue {
|
|
24
|
+
code: string;
|
|
25
|
+
severity: IssueSeverity;
|
|
26
|
+
category: IssueCategory;
|
|
27
|
+
title: string;
|
|
28
|
+
description: string;
|
|
29
|
+
impact: string;
|
|
30
|
+
howToFix: string;
|
|
31
|
+
affectedUrls?: string[];
|
|
32
|
+
details?: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface PageData {
|
|
36
|
+
url: string;
|
|
37
|
+
statusCode: number;
|
|
38
|
+
redirectChain?: string[];
|
|
39
|
+
html: string;
|
|
40
|
+
loadTime: number;
|
|
41
|
+
size: number;
|
|
42
|
+
headers: Record<string, string>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface CrawlStats {
|
|
46
|
+
totalUrls: number;
|
|
47
|
+
crawledUrls: number;
|
|
48
|
+
errorUrls: number;
|
|
49
|
+
redirectUrls: number;
|
|
50
|
+
blockedUrls: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface HealthScore {
|
|
54
|
+
overall: number; // 0-100
|
|
55
|
+
crawlability: number;
|
|
56
|
+
indexability: number;
|
|
57
|
+
onPage: number;
|
|
58
|
+
content: number;
|
|
59
|
+
links: number;
|
|
60
|
+
performance: number;
|
|
61
|
+
security: number;
|
|
62
|
+
aiReadiness: number;
|
|
63
|
+
social: number;
|
|
64
|
+
localSeo: number;
|
|
65
|
+
accessibility: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface AuditReport {
|
|
69
|
+
url: string;
|
|
70
|
+
domain: string;
|
|
71
|
+
timestamp: string;
|
|
72
|
+
crawlStats: CrawlStats;
|
|
73
|
+
healthScore: HealthScore;
|
|
74
|
+
issues: AuditIssue[];
|
|
75
|
+
pages: PageAudit[];
|
|
76
|
+
summary: {
|
|
77
|
+
errors: number;
|
|
78
|
+
warnings: number;
|
|
79
|
+
notices: number;
|
|
80
|
+
passed: number;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface PageAudit {
|
|
85
|
+
url: string;
|
|
86
|
+
statusCode: number;
|
|
87
|
+
title?: string;
|
|
88
|
+
description?: string;
|
|
89
|
+
canonical?: string;
|
|
90
|
+
h1?: string[];
|
|
91
|
+
wordCount: number;
|
|
92
|
+
loadTime: number;
|
|
93
|
+
issues: string[]; // Issue codes
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Issue definitions
|
|
97
|
+
export interface IssueDefinition {
|
|
98
|
+
code: string;
|
|
99
|
+
severity: IssueSeverity;
|
|
100
|
+
category: IssueCategory;
|
|
101
|
+
title: string;
|
|
102
|
+
description: string;
|
|
103
|
+
impact: string;
|
|
104
|
+
howToFix: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// All 100+ issue definitions
|
|
108
|
+
export const ISSUE_DEFINITIONS: Record<string, IssueDefinition> = {
|
|
109
|
+
// ==================== CRAWLABILITY ====================
|
|
110
|
+
ROBOTS_TXT_MISSING: {
|
|
111
|
+
code: 'ROBOTS_TXT_MISSING',
|
|
112
|
+
severity: 'warning',
|
|
113
|
+
category: 'crawlability',
|
|
114
|
+
title: 'Missing robots.txt',
|
|
115
|
+
description: 'No robots.txt file found at the root of the domain.',
|
|
116
|
+
impact: 'Search engines cannot determine crawling rules for your site.',
|
|
117
|
+
howToFix: 'Create a robots.txt file at the root of your domain with appropriate directives.',
|
|
118
|
+
},
|
|
119
|
+
ROBOTS_TXT_BLOCKS_ALL: {
|
|
120
|
+
code: 'ROBOTS_TXT_BLOCKS_ALL',
|
|
121
|
+
severity: 'error',
|
|
122
|
+
category: 'crawlability',
|
|
123
|
+
title: 'robots.txt blocks all crawlers',
|
|
124
|
+
description: 'The robots.txt file contains "Disallow: /" which blocks all search engines.',
|
|
125
|
+
impact: 'Your entire site will not be indexed by search engines.',
|
|
126
|
+
howToFix: 'Update robots.txt to allow crawling of important pages.',
|
|
127
|
+
},
|
|
128
|
+
SITEMAP_MISSING: {
|
|
129
|
+
code: 'SITEMAP_MISSING',
|
|
130
|
+
severity: 'warning',
|
|
131
|
+
category: 'crawlability',
|
|
132
|
+
title: 'Missing XML sitemap',
|
|
133
|
+
description: 'No sitemap.xml found at standard locations.',
|
|
134
|
+
impact: 'Search engines may not discover all your pages efficiently.',
|
|
135
|
+
howToFix: 'Create an XML sitemap and submit it to search engines.',
|
|
136
|
+
},
|
|
137
|
+
SITEMAP_NOT_IN_ROBOTS: {
|
|
138
|
+
code: 'SITEMAP_NOT_IN_ROBOTS',
|
|
139
|
+
severity: 'notice',
|
|
140
|
+
category: 'crawlability',
|
|
141
|
+
title: 'Sitemap not referenced in robots.txt',
|
|
142
|
+
description: 'The sitemap URL is not listed in robots.txt.',
|
|
143
|
+
impact: 'Search engines may not find your sitemap automatically.',
|
|
144
|
+
howToFix: 'Add "Sitemap: https://yoursite.com/sitemap.xml" to robots.txt.',
|
|
145
|
+
},
|
|
146
|
+
REDIRECT_CHAIN: {
|
|
147
|
+
code: 'REDIRECT_CHAIN',
|
|
148
|
+
severity: 'warning',
|
|
149
|
+
category: 'crawlability',
|
|
150
|
+
title: 'Redirect chain detected',
|
|
151
|
+
description: 'Multiple redirects before reaching the final destination.',
|
|
152
|
+
impact: 'Wastes crawl budget and reduces link equity passed to the final page.',
|
|
153
|
+
howToFix: 'Update redirects to point directly to the final destination URL.',
|
|
154
|
+
},
|
|
155
|
+
REDIRECT_LOOP: {
|
|
156
|
+
code: 'REDIRECT_LOOP',
|
|
157
|
+
severity: 'error',
|
|
158
|
+
category: 'crawlability',
|
|
159
|
+
title: 'Redirect loop detected',
|
|
160
|
+
description: 'Redirects create an infinite loop.',
|
|
161
|
+
impact: 'Page is completely inaccessible to users and search engines.',
|
|
162
|
+
howToFix: 'Fix the redirect configuration to break the loop.',
|
|
163
|
+
},
|
|
164
|
+
ORPHAN_PAGE: {
|
|
165
|
+
code: 'ORPHAN_PAGE',
|
|
166
|
+
severity: 'warning',
|
|
167
|
+
category: 'crawlability',
|
|
168
|
+
title: 'Orphan page (no internal links)',
|
|
169
|
+
description: 'This page has no internal links pointing to it.',
|
|
170
|
+
impact: 'Search engines may not discover this page, and it receives no link equity.',
|
|
171
|
+
howToFix: 'Add internal links from relevant pages to this page.',
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
// ==================== INDEXABILITY ====================
|
|
175
|
+
NOINDEX_TAG: {
|
|
176
|
+
code: 'NOINDEX_TAG',
|
|
177
|
+
severity: 'notice',
|
|
178
|
+
category: 'indexability',
|
|
179
|
+
title: 'Page has noindex tag',
|
|
180
|
+
description: 'The page contains a noindex meta tag or X-Robots-Tag header.',
|
|
181
|
+
impact: 'This page will not appear in search results.',
|
|
182
|
+
howToFix: 'Remove the noindex tag if you want this page indexed.',
|
|
183
|
+
},
|
|
184
|
+
CANONICAL_MISSING: {
|
|
185
|
+
code: 'CANONICAL_MISSING',
|
|
186
|
+
severity: 'warning',
|
|
187
|
+
category: 'indexability',
|
|
188
|
+
title: 'Missing canonical tag',
|
|
189
|
+
description: 'No canonical URL specified for this page.',
|
|
190
|
+
impact: 'May cause duplicate content issues if the page is accessible via multiple URLs.',
|
|
191
|
+
howToFix: 'Add a self-referencing canonical tag or point to the preferred URL.',
|
|
192
|
+
},
|
|
193
|
+
CANONICAL_POINTS_TO_REDIRECT: {
|
|
194
|
+
code: 'CANONICAL_POINTS_TO_REDIRECT',
|
|
195
|
+
severity: 'error',
|
|
196
|
+
category: 'indexability',
|
|
197
|
+
title: 'Canonical points to redirect',
|
|
198
|
+
description: 'The canonical URL redirects to a different page.',
|
|
199
|
+
impact: 'Confuses search engines and may cause indexing issues.',
|
|
200
|
+
howToFix: 'Update the canonical to point to the final destination URL.',
|
|
201
|
+
},
|
|
202
|
+
CANONICAL_POINTS_TO_4XX: {
|
|
203
|
+
code: 'CANONICAL_POINTS_TO_4XX',
|
|
204
|
+
severity: 'error',
|
|
205
|
+
category: 'indexability',
|
|
206
|
+
title: 'Canonical points to 4XX page',
|
|
207
|
+
description: 'The canonical URL returns a 4XX error.',
|
|
208
|
+
impact: 'Search engines cannot index this page properly.',
|
|
209
|
+
howToFix: 'Update the canonical to point to a valid URL.',
|
|
210
|
+
},
|
|
211
|
+
MULTIPLE_CANONICALS: {
|
|
212
|
+
code: 'MULTIPLE_CANONICALS',
|
|
213
|
+
severity: 'error',
|
|
214
|
+
category: 'indexability',
|
|
215
|
+
title: 'Multiple canonical tags',
|
|
216
|
+
description: 'Page has more than one canonical tag.',
|
|
217
|
+
impact: 'Search engines may ignore all canonical hints.',
|
|
218
|
+
howToFix: 'Remove duplicate canonical tags, keeping only one.',
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
// ==================== ON-PAGE SEO ====================
|
|
222
|
+
TITLE_MISSING: {
|
|
223
|
+
code: 'TITLE_MISSING',
|
|
224
|
+
severity: 'error',
|
|
225
|
+
category: 'on-page',
|
|
226
|
+
title: 'Missing title tag',
|
|
227
|
+
description: 'The page has no title tag.',
|
|
228
|
+
impact: 'Critical ranking factor missing. Page will have poor search visibility.',
|
|
229
|
+
howToFix: 'Add a descriptive title tag (50-60 characters).',
|
|
230
|
+
},
|
|
231
|
+
TITLE_TOO_SHORT: {
|
|
232
|
+
code: 'TITLE_TOO_SHORT',
|
|
233
|
+
severity: 'warning',
|
|
234
|
+
category: 'on-page',
|
|
235
|
+
title: 'Title tag too short',
|
|
236
|
+
description: 'Title tag is less than 30 characters.',
|
|
237
|
+
impact: 'Not utilizing the full space available in search results.',
|
|
238
|
+
howToFix: 'Expand the title to 50-60 characters with relevant keywords.',
|
|
239
|
+
},
|
|
240
|
+
TITLE_TOO_LONG: {
|
|
241
|
+
code: 'TITLE_TOO_LONG',
|
|
242
|
+
severity: 'warning',
|
|
243
|
+
category: 'on-page',
|
|
244
|
+
title: 'Title tag too long',
|
|
245
|
+
description: 'Title tag exceeds 60 characters.',
|
|
246
|
+
impact: 'Title will be truncated in search results.',
|
|
247
|
+
howToFix: 'Shorten the title to under 60 characters.',
|
|
248
|
+
},
|
|
249
|
+
TITLE_DUPLICATE: {
|
|
250
|
+
code: 'TITLE_DUPLICATE',
|
|
251
|
+
severity: 'warning',
|
|
252
|
+
category: 'on-page',
|
|
253
|
+
title: 'Duplicate title tag',
|
|
254
|
+
description: 'Multiple pages have the same title.',
|
|
255
|
+
impact: 'Can cause keyword cannibalization and confuse search engines.',
|
|
256
|
+
howToFix: 'Ensure each page has a unique, descriptive title.',
|
|
257
|
+
},
|
|
258
|
+
META_DESC_MISSING: {
|
|
259
|
+
code: 'META_DESC_MISSING',
|
|
260
|
+
severity: 'warning',
|
|
261
|
+
category: 'on-page',
|
|
262
|
+
title: 'Missing meta description',
|
|
263
|
+
description: 'No meta description tag found.',
|
|
264
|
+
impact: 'Search engines may use random page content as the snippet.',
|
|
265
|
+
howToFix: 'Add a compelling meta description (150-160 characters).',
|
|
266
|
+
},
|
|
267
|
+
META_DESC_TOO_SHORT: {
|
|
268
|
+
code: 'META_DESC_TOO_SHORT',
|
|
269
|
+
severity: 'notice',
|
|
270
|
+
category: 'on-page',
|
|
271
|
+
title: 'Meta description too short',
|
|
272
|
+
description: 'Meta description is less than 120 characters.',
|
|
273
|
+
impact: 'Not fully utilizing the snippet space in search results.',
|
|
274
|
+
howToFix: 'Expand to 150-160 characters with a compelling call-to-action.',
|
|
275
|
+
},
|
|
276
|
+
META_DESC_TOO_LONG: {
|
|
277
|
+
code: 'META_DESC_TOO_LONG',
|
|
278
|
+
severity: 'notice',
|
|
279
|
+
category: 'on-page',
|
|
280
|
+
title: 'Meta description too long',
|
|
281
|
+
description: 'Meta description exceeds 160 characters.',
|
|
282
|
+
impact: 'Description may be truncated in search results.',
|
|
283
|
+
howToFix: 'Shorten to under 160 characters.',
|
|
284
|
+
},
|
|
285
|
+
META_DESC_DUPLICATE: {
|
|
286
|
+
code: 'META_DESC_DUPLICATE',
|
|
287
|
+
severity: 'warning',
|
|
288
|
+
category: 'on-page',
|
|
289
|
+
title: 'Duplicate meta description',
|
|
290
|
+
description: 'Multiple pages have the same meta description.',
|
|
291
|
+
impact: 'Reduces the uniqueness of your pages in search results.',
|
|
292
|
+
howToFix: 'Write unique meta descriptions for each page.',
|
|
293
|
+
},
|
|
294
|
+
H1_MISSING: {
|
|
295
|
+
code: 'H1_MISSING',
|
|
296
|
+
severity: 'error',
|
|
297
|
+
category: 'on-page',
|
|
298
|
+
title: 'Missing H1 heading',
|
|
299
|
+
description: 'No H1 tag found on the page.',
|
|
300
|
+
impact: 'Search engines use H1 to understand the main topic of the page.',
|
|
301
|
+
howToFix: 'Add exactly one H1 tag that describes the main content.',
|
|
302
|
+
},
|
|
303
|
+
H1_MULTIPLE: {
|
|
304
|
+
code: 'H1_MULTIPLE',
|
|
305
|
+
severity: 'warning',
|
|
306
|
+
category: 'on-page',
|
|
307
|
+
title: 'Multiple H1 headings',
|
|
308
|
+
description: 'Page has more than one H1 tag.',
|
|
309
|
+
impact: 'Can dilute the topical focus of the page.',
|
|
310
|
+
howToFix: 'Use only one H1 per page. Use H2-H6 for subheadings.',
|
|
311
|
+
},
|
|
312
|
+
H1_DUPLICATE_OF_TITLE: {
|
|
313
|
+
code: 'H1_DUPLICATE_OF_TITLE',
|
|
314
|
+
severity: 'notice',
|
|
315
|
+
category: 'on-page',
|
|
316
|
+
title: 'H1 identical to title tag',
|
|
317
|
+
description: 'The H1 heading is exactly the same as the title tag.',
|
|
318
|
+
impact: 'Missed opportunity to target additional keywords.',
|
|
319
|
+
howToFix: 'Consider varying the H1 to cover related keywords.',
|
|
320
|
+
},
|
|
321
|
+
HEADING_SKIP: {
|
|
322
|
+
code: 'HEADING_SKIP',
|
|
323
|
+
severity: 'notice',
|
|
324
|
+
category: 'on-page',
|
|
325
|
+
title: 'Skipped heading level',
|
|
326
|
+
description: 'Heading hierarchy skips levels (e.g., H1 to H3).',
|
|
327
|
+
impact: 'May confuse screen readers and reduce content structure clarity.',
|
|
328
|
+
howToFix: 'Maintain proper heading hierarchy (H1 > H2 > H3, etc.).',
|
|
329
|
+
},
|
|
330
|
+
H2_MISSING: {
|
|
331
|
+
code: 'H2_MISSING',
|
|
332
|
+
severity: 'warning',
|
|
333
|
+
category: 'on-page',
|
|
334
|
+
title: 'No H2 headings found',
|
|
335
|
+
description: 'No H2 tags were found on the page.',
|
|
336
|
+
impact: 'H2 headings help structure content and are important for SEO and readability.',
|
|
337
|
+
howToFix: 'Break your content into sections using H2 headings with relevant keywords.',
|
|
338
|
+
},
|
|
339
|
+
|
|
340
|
+
// ==================== CONTENT ====================
|
|
341
|
+
THIN_CONTENT: {
|
|
342
|
+
code: 'THIN_CONTENT',
|
|
343
|
+
severity: 'warning',
|
|
344
|
+
category: 'content',
|
|
345
|
+
title: 'Thin content',
|
|
346
|
+
description: 'Page has less than 300 words of text content.',
|
|
347
|
+
impact: 'May be seen as low-quality by search engines.',
|
|
348
|
+
howToFix: 'Add more valuable, relevant content to the page.',
|
|
349
|
+
},
|
|
350
|
+
DUPLICATE_CONTENT: {
|
|
351
|
+
code: 'DUPLICATE_CONTENT',
|
|
352
|
+
severity: 'warning',
|
|
353
|
+
category: 'content',
|
|
354
|
+
title: 'Duplicate content detected',
|
|
355
|
+
description: 'Significant content overlap with other pages.',
|
|
356
|
+
impact: 'Search engines may filter out duplicate pages from results.',
|
|
357
|
+
howToFix: 'Use canonical tags or differentiate the content.',
|
|
358
|
+
},
|
|
359
|
+
LOW_TEXT_HTML_RATIO: {
|
|
360
|
+
code: 'LOW_TEXT_HTML_RATIO',
|
|
361
|
+
severity: 'notice',
|
|
362
|
+
category: 'content',
|
|
363
|
+
title: 'Low text-to-HTML ratio',
|
|
364
|
+
description: 'Text content is less than 10% of total HTML.',
|
|
365
|
+
impact: 'May indicate poor content or bloated HTML.',
|
|
366
|
+
howToFix: 'Add more text content or reduce unnecessary HTML/scripts.',
|
|
367
|
+
},
|
|
368
|
+
|
|
369
|
+
// ==================== LINKS ====================
|
|
370
|
+
BROKEN_INTERNAL_LINK: {
|
|
371
|
+
code: 'BROKEN_INTERNAL_LINK',
|
|
372
|
+
severity: 'error',
|
|
373
|
+
category: 'links',
|
|
374
|
+
title: 'Broken internal link',
|
|
375
|
+
description: 'Internal link points to a page that returns 4XX error.',
|
|
376
|
+
impact: 'Poor user experience and wasted crawl budget.',
|
|
377
|
+
howToFix: 'Fix the broken link or remove it.',
|
|
378
|
+
},
|
|
379
|
+
BROKEN_EXTERNAL_LINK: {
|
|
380
|
+
code: 'BROKEN_EXTERNAL_LINK',
|
|
381
|
+
severity: 'warning',
|
|
382
|
+
category: 'links',
|
|
383
|
+
title: 'Broken external link',
|
|
384
|
+
description: 'External link points to a page that returns 4XX error.',
|
|
385
|
+
impact: 'Poor user experience and potential trust issues.',
|
|
386
|
+
howToFix: 'Update or remove the broken external link.',
|
|
387
|
+
},
|
|
388
|
+
TOO_MANY_LINKS: {
|
|
389
|
+
code: 'TOO_MANY_LINKS',
|
|
390
|
+
severity: 'notice',
|
|
391
|
+
category: 'links',
|
|
392
|
+
title: 'Too many on-page links',
|
|
393
|
+
description: 'Page has more than 100 links.',
|
|
394
|
+
impact: 'Dilutes PageRank passed to each linked page.',
|
|
395
|
+
howToFix: 'Reduce the number of links to the most important ones.',
|
|
396
|
+
},
|
|
397
|
+
NOFOLLOW_INTERNAL: {
|
|
398
|
+
code: 'NOFOLLOW_INTERNAL',
|
|
399
|
+
severity: 'notice',
|
|
400
|
+
category: 'links',
|
|
401
|
+
title: 'Nofollow on internal link',
|
|
402
|
+
description: 'Internal link has rel="nofollow" attribute.',
|
|
403
|
+
impact: 'Prevents PageRank from flowing to important pages.',
|
|
404
|
+
howToFix: 'Remove nofollow from internal links unless intentional.',
|
|
405
|
+
},
|
|
406
|
+
|
|
407
|
+
// ==================== IMAGES ====================
|
|
408
|
+
IMG_ALT_MISSING: {
|
|
409
|
+
code: 'IMG_ALT_MISSING',
|
|
410
|
+
severity: 'warning',
|
|
411
|
+
category: 'images',
|
|
412
|
+
title: 'Image missing alt text',
|
|
413
|
+
description: 'Image has no alt attribute.',
|
|
414
|
+
impact: 'Poor accessibility and missed SEO opportunity.',
|
|
415
|
+
howToFix: 'Add descriptive alt text to all images.',
|
|
416
|
+
},
|
|
417
|
+
IMG_ALT_EMPTY: {
|
|
418
|
+
code: 'IMG_ALT_EMPTY',
|
|
419
|
+
severity: 'notice',
|
|
420
|
+
category: 'images',
|
|
421
|
+
title: 'Image has empty alt text',
|
|
422
|
+
description: 'Image has alt="" (empty alt attribute).',
|
|
423
|
+
impact: 'Acceptable for decorative images but not for content images.',
|
|
424
|
+
howToFix: 'Add descriptive alt text if the image conveys information.',
|
|
425
|
+
},
|
|
426
|
+
IMG_NO_DIMENSIONS: {
|
|
427
|
+
code: 'IMG_NO_DIMENSIONS',
|
|
428
|
+
severity: 'notice',
|
|
429
|
+
category: 'images',
|
|
430
|
+
title: 'Image missing dimensions',
|
|
431
|
+
description: 'Image lacks width and height attributes.',
|
|
432
|
+
impact: 'Causes layout shift (CLS) when image loads.',
|
|
433
|
+
howToFix: 'Add width and height attributes to all images.',
|
|
434
|
+
},
|
|
435
|
+
IMG_TOO_LARGE: {
|
|
436
|
+
code: 'IMG_TOO_LARGE',
|
|
437
|
+
severity: 'warning',
|
|
438
|
+
category: 'images',
|
|
439
|
+
title: 'Image file too large',
|
|
440
|
+
description: 'Image file size exceeds 200KB.',
|
|
441
|
+
impact: 'Slows down page load time.',
|
|
442
|
+
howToFix: 'Compress and optimize images. Consider WebP format.',
|
|
443
|
+
},
|
|
444
|
+
IMG_BROKEN: {
|
|
445
|
+
code: 'IMG_BROKEN',
|
|
446
|
+
severity: 'error',
|
|
447
|
+
category: 'images',
|
|
448
|
+
title: 'Broken image',
|
|
449
|
+
description: 'Image URL returns 4XX error.',
|
|
450
|
+
impact: 'Poor user experience with missing images.',
|
|
451
|
+
howToFix: 'Fix the image URL or replace with a working image.',
|
|
452
|
+
},
|
|
453
|
+
|
|
454
|
+
// ==================== STRUCTURED DATA ====================
|
|
455
|
+
SCHEMA_MISSING: {
|
|
456
|
+
code: 'SCHEMA_MISSING',
|
|
457
|
+
severity: 'warning',
|
|
458
|
+
category: 'structured-data',
|
|
459
|
+
title: 'No structured data found',
|
|
460
|
+
description: 'Page has no JSON-LD or microdata.',
|
|
461
|
+
impact: 'Missing opportunity for rich snippets in search results.',
|
|
462
|
+
howToFix: 'Add appropriate Schema.org markup for your content type.',
|
|
463
|
+
},
|
|
464
|
+
SCHEMA_INVALID: {
|
|
465
|
+
code: 'SCHEMA_INVALID',
|
|
466
|
+
severity: 'error',
|
|
467
|
+
category: 'structured-data',
|
|
468
|
+
title: 'Invalid structured data',
|
|
469
|
+
description: 'JSON-LD contains syntax errors.',
|
|
470
|
+
impact: 'Search engines cannot parse the structured data.',
|
|
471
|
+
howToFix: 'Fix JSON syntax errors and validate with Google Rich Results Test.',
|
|
472
|
+
},
|
|
473
|
+
SCHEMA_MISSING_REQUIRED: {
|
|
474
|
+
code: 'SCHEMA_MISSING_REQUIRED',
|
|
475
|
+
severity: 'warning',
|
|
476
|
+
category: 'structured-data',
|
|
477
|
+
title: 'Missing required schema properties',
|
|
478
|
+
description: 'Schema markup is missing required properties.',
|
|
479
|
+
impact: 'May not qualify for rich snippets.',
|
|
480
|
+
howToFix: 'Add all required properties for the schema type.',
|
|
481
|
+
},
|
|
482
|
+
|
|
483
|
+
// ==================== PERFORMANCE ====================
|
|
484
|
+
SLOW_PAGE: {
|
|
485
|
+
code: 'SLOW_PAGE',
|
|
486
|
+
severity: 'warning',
|
|
487
|
+
category: 'performance',
|
|
488
|
+
title: 'Slow page load time',
|
|
489
|
+
description: 'Page takes more than 3 seconds to load.',
|
|
490
|
+
impact: 'High bounce rates and potential ranking penalty.',
|
|
491
|
+
howToFix: 'Optimize images, enable caching, minimize CSS/JS.',
|
|
492
|
+
},
|
|
493
|
+
SLOW_TTFB: {
|
|
494
|
+
code: 'SLOW_TTFB',
|
|
495
|
+
severity: 'warning',
|
|
496
|
+
category: 'performance',
|
|
497
|
+
title: 'Slow Time to First Byte (TTFB)',
|
|
498
|
+
description: 'Server response time exceeds 600ms.',
|
|
499
|
+
impact: 'Delays entire page load and affects Core Web Vitals.',
|
|
500
|
+
howToFix: 'Optimize server configuration, use caching, consider CDN.',
|
|
501
|
+
},
|
|
502
|
+
LCP_POOR: {
|
|
503
|
+
code: 'LCP_POOR',
|
|
504
|
+
severity: 'error',
|
|
505
|
+
category: 'performance',
|
|
506
|
+
title: 'Poor Largest Contentful Paint (LCP)',
|
|
507
|
+
description: 'LCP exceeds 2.5 seconds.',
|
|
508
|
+
impact: 'Direct Core Web Vitals ranking factor.',
|
|
509
|
+
howToFix: 'Optimize hero images, preload critical resources.',
|
|
510
|
+
},
|
|
511
|
+
CLS_POOR: {
|
|
512
|
+
code: 'CLS_POOR',
|
|
513
|
+
severity: 'error',
|
|
514
|
+
category: 'performance',
|
|
515
|
+
title: 'Poor Cumulative Layout Shift (CLS)',
|
|
516
|
+
description: 'CLS exceeds 0.1.',
|
|
517
|
+
impact: 'Direct Core Web Vitals ranking factor.',
|
|
518
|
+
howToFix: 'Set image dimensions, avoid inserting content above existing content.',
|
|
519
|
+
},
|
|
520
|
+
HTML_TOO_LARGE: {
|
|
521
|
+
code: 'HTML_TOO_LARGE',
|
|
522
|
+
severity: 'warning',
|
|
523
|
+
category: 'performance',
|
|
524
|
+
title: 'HTML document too large',
|
|
525
|
+
description: 'HTML size exceeds 100KB.',
|
|
526
|
+
impact: 'Increases load time and memory usage.',
|
|
527
|
+
howToFix: 'Reduce inline CSS/JS, remove unnecessary HTML.',
|
|
528
|
+
},
|
|
529
|
+
UNCOMPRESSED: {
|
|
530
|
+
code: 'UNCOMPRESSED',
|
|
531
|
+
severity: 'warning',
|
|
532
|
+
category: 'performance',
|
|
533
|
+
title: 'Uncompressed resources',
|
|
534
|
+
description: 'Page is not served with gzip/brotli compression.',
|
|
535
|
+
impact: 'Larger file transfer sizes slow down the page.',
|
|
536
|
+
howToFix: 'Enable gzip or brotli compression on your server.',
|
|
537
|
+
},
|
|
538
|
+
NO_CACHE: {
|
|
539
|
+
code: 'NO_CACHE',
|
|
540
|
+
severity: 'notice',
|
|
541
|
+
category: 'performance',
|
|
542
|
+
title: 'Missing cache headers',
|
|
543
|
+
description: 'Page lacks Cache-Control or Expires headers.',
|
|
544
|
+
impact: 'Browser cannot cache resources, causing repeated downloads.',
|
|
545
|
+
howToFix: 'Add appropriate cache headers for static resources.',
|
|
546
|
+
},
|
|
547
|
+
|
|
548
|
+
// ==================== SECURITY ====================
|
|
549
|
+
NOT_HTTPS: {
|
|
550
|
+
code: 'NOT_HTTPS',
|
|
551
|
+
severity: 'error',
|
|
552
|
+
category: 'security',
|
|
553
|
+
title: 'Page not served over HTTPS',
|
|
554
|
+
description: 'Page is accessible via HTTP instead of HTTPS.',
|
|
555
|
+
impact: 'Browser warnings, user distrust, and ranking penalty.',
|
|
556
|
+
howToFix: 'Install SSL certificate and redirect HTTP to HTTPS.',
|
|
557
|
+
},
|
|
558
|
+
MIXED_CONTENT: {
|
|
559
|
+
code: 'MIXED_CONTENT',
|
|
560
|
+
severity: 'error',
|
|
561
|
+
category: 'security',
|
|
562
|
+
title: 'Mixed content',
|
|
563
|
+
description: 'HTTPS page loads resources over HTTP.',
|
|
564
|
+
impact: 'Browser may block resources or show security warnings.',
|
|
565
|
+
howToFix: 'Update all resource URLs to use HTTPS.',
|
|
566
|
+
},
|
|
567
|
+
CERT_EXPIRING: {
|
|
568
|
+
code: 'CERT_EXPIRING',
|
|
569
|
+
severity: 'warning',
|
|
570
|
+
category: 'security',
|
|
571
|
+
title: 'SSL certificate expiring soon',
|
|
572
|
+
description: 'SSL certificate expires within 30 days.',
|
|
573
|
+
impact: 'Site will become insecure when certificate expires.',
|
|
574
|
+
howToFix: 'Renew your SSL certificate before expiration.',
|
|
575
|
+
},
|
|
576
|
+
HSTS_MISSING: {
|
|
577
|
+
code: 'HSTS_MISSING',
|
|
578
|
+
severity: 'notice',
|
|
579
|
+
category: 'security',
|
|
580
|
+
title: 'Missing HSTS header',
|
|
581
|
+
description: 'Strict-Transport-Security header not set.',
|
|
582
|
+
impact: 'Users may be vulnerable to downgrade attacks.',
|
|
583
|
+
howToFix: 'Add HSTS header to enforce HTTPS.',
|
|
584
|
+
},
|
|
585
|
+
|
|
586
|
+
// ==================== MOBILE ====================
|
|
587
|
+
VIEWPORT_MISSING: {
|
|
588
|
+
code: 'VIEWPORT_MISSING',
|
|
589
|
+
severity: 'error',
|
|
590
|
+
category: 'mobile',
|
|
591
|
+
title: 'Missing viewport meta tag',
|
|
592
|
+
description: 'No viewport meta tag found.',
|
|
593
|
+
impact: 'Page will not render properly on mobile devices.',
|
|
594
|
+
howToFix: 'Add <meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
595
|
+
},
|
|
596
|
+
VIEWPORT_NOT_RESPONSIVE: {
|
|
597
|
+
code: 'VIEWPORT_NOT_RESPONSIVE',
|
|
598
|
+
severity: 'warning',
|
|
599
|
+
category: 'mobile',
|
|
600
|
+
title: 'Non-responsive viewport',
|
|
601
|
+
description: 'Viewport uses fixed width instead of device-width.',
|
|
602
|
+
impact: 'Page may not adapt to different screen sizes.',
|
|
603
|
+
howToFix: 'Use width=device-width in viewport meta tag.',
|
|
604
|
+
},
|
|
605
|
+
TAP_TARGETS_TOO_SMALL: {
|
|
606
|
+
code: 'TAP_TARGETS_TOO_SMALL',
|
|
607
|
+
severity: 'warning',
|
|
608
|
+
category: 'mobile',
|
|
609
|
+
title: 'Tap targets too small',
|
|
610
|
+
description: 'Clickable elements are smaller than 48x48 pixels.',
|
|
611
|
+
impact: 'Difficult for mobile users to tap links/buttons.',
|
|
612
|
+
howToFix: 'Increase the size of tap targets to at least 48x48 pixels.',
|
|
613
|
+
},
|
|
614
|
+
|
|
615
|
+
// ==================== INTERNATIONAL ====================
|
|
616
|
+
HREFLANG_MISSING: {
|
|
617
|
+
code: 'HREFLANG_MISSING',
|
|
618
|
+
severity: 'notice',
|
|
619
|
+
category: 'international',
|
|
620
|
+
title: 'Missing hreflang tags',
|
|
621
|
+
description: 'Multi-language site without hreflang annotations.',
|
|
622
|
+
impact: 'Search engines may show wrong language version to users.',
|
|
623
|
+
howToFix: 'Add hreflang tags to specify language/region versions.',
|
|
624
|
+
},
|
|
625
|
+
HREFLANG_INVALID: {
|
|
626
|
+
code: 'HREFLANG_INVALID',
|
|
627
|
+
severity: 'warning',
|
|
628
|
+
category: 'international',
|
|
629
|
+
title: 'Invalid hreflang value',
|
|
630
|
+
description: 'Hreflang contains invalid language or region code.',
|
|
631
|
+
impact: 'Search engines will ignore the hreflang directive.',
|
|
632
|
+
howToFix: 'Use valid ISO 639-1 language and ISO 3166-1 Alpha-2 region codes.',
|
|
633
|
+
},
|
|
634
|
+
HREFLANG_NO_RETURN: {
|
|
635
|
+
code: 'HREFLANG_NO_RETURN',
|
|
636
|
+
severity: 'warning',
|
|
637
|
+
category: 'international',
|
|
638
|
+
title: 'Missing hreflang return link',
|
|
639
|
+
description: 'Hreflang page does not link back to this page.',
|
|
640
|
+
impact: 'Hreflang signals may be ignored by search engines.',
|
|
641
|
+
howToFix: 'Ensure all hreflang pages have reciprocal links.',
|
|
642
|
+
},
|
|
643
|
+
HREFLANG_SELF_MISSING: {
|
|
644
|
+
code: 'HREFLANG_SELF_MISSING',
|
|
645
|
+
severity: 'notice',
|
|
646
|
+
category: 'international',
|
|
647
|
+
title: 'Missing self-referencing hreflang',
|
|
648
|
+
description: 'Page does not include hreflang pointing to itself.',
|
|
649
|
+
impact: 'Best practice to include self-referencing hreflang.',
|
|
650
|
+
howToFix: 'Add hreflang for the current page language/region.',
|
|
651
|
+
},
|
|
652
|
+
HREFLANG_X_DEFAULT_MISSING: {
|
|
653
|
+
code: 'HREFLANG_X_DEFAULT_MISSING',
|
|
654
|
+
severity: 'notice',
|
|
655
|
+
category: 'international',
|
|
656
|
+
title: 'Missing x-default hreflang',
|
|
657
|
+
description: 'Multi-language page missing x-default fallback.',
|
|
658
|
+
impact: 'Users with unsupported language may not see appropriate version.',
|
|
659
|
+
howToFix: 'Add hreflang="x-default" pointing to your default language page.',
|
|
660
|
+
},
|
|
661
|
+
HREFLANG_TO_NON_200: {
|
|
662
|
+
code: 'HREFLANG_TO_NON_200',
|
|
663
|
+
severity: 'error',
|
|
664
|
+
category: 'international',
|
|
665
|
+
title: 'Hreflang points to non-200 page',
|
|
666
|
+
description: 'Hreflang URL returns error or redirect.',
|
|
667
|
+
impact: 'Search engines cannot serve the alternate language version.',
|
|
668
|
+
howToFix: 'Update hreflang to point to valid, accessible URLs.',
|
|
669
|
+
},
|
|
670
|
+
|
|
671
|
+
// ==================== SOFT 404 ====================
|
|
672
|
+
SOFT_404: {
|
|
673
|
+
code: 'SOFT_404',
|
|
674
|
+
severity: 'error',
|
|
675
|
+
category: 'content',
|
|
676
|
+
title: 'Soft 404 detected',
|
|
677
|
+
description: 'Page returns 200 but appears to be an error page.',
|
|
678
|
+
impact: 'Wastes crawl budget and may index low-value error pages.',
|
|
679
|
+
howToFix: 'Return proper 404 status for error pages or add meaningful content.',
|
|
680
|
+
},
|
|
681
|
+
|
|
682
|
+
// ==================== OPEN GRAPH / SOCIAL ====================
|
|
683
|
+
OG_TITLE_MISSING: {
|
|
684
|
+
code: 'OG_TITLE_MISSING',
|
|
685
|
+
severity: 'warning',
|
|
686
|
+
category: 'on-page',
|
|
687
|
+
title: 'Missing Open Graph title',
|
|
688
|
+
description: 'No og:title meta tag found.',
|
|
689
|
+
impact: 'Social shares may display incorrect or missing title.',
|
|
690
|
+
howToFix: 'Add <meta property="og:title" content="Your Title">',
|
|
691
|
+
},
|
|
692
|
+
OG_DESCRIPTION_MISSING: {
|
|
693
|
+
code: 'OG_DESCRIPTION_MISSING',
|
|
694
|
+
severity: 'warning',
|
|
695
|
+
category: 'on-page',
|
|
696
|
+
title: 'Missing Open Graph description',
|
|
697
|
+
description: 'No og:description meta tag found.',
|
|
698
|
+
impact: 'Social shares may display incorrect or missing description.',
|
|
699
|
+
howToFix: 'Add <meta property="og:description" content="Your description">',
|
|
700
|
+
},
|
|
701
|
+
OG_IMAGE_MISSING: {
|
|
702
|
+
code: 'OG_IMAGE_MISSING',
|
|
703
|
+
severity: 'warning',
|
|
704
|
+
category: 'on-page',
|
|
705
|
+
title: 'Missing Open Graph image',
|
|
706
|
+
description: 'No og:image meta tag found.',
|
|
707
|
+
impact: 'Social shares will not display a preview image.',
|
|
708
|
+
howToFix: 'Add <meta property="og:image" content="https://yoursite.com/image.jpg">',
|
|
709
|
+
},
|
|
710
|
+
OG_IMAGE_TOO_SMALL: {
|
|
711
|
+
code: 'OG_IMAGE_TOO_SMALL',
|
|
712
|
+
severity: 'notice',
|
|
713
|
+
category: 'on-page',
|
|
714
|
+
title: 'Open Graph image too small',
|
|
715
|
+
description: 'OG image should be at least 1200x630 pixels.',
|
|
716
|
+
impact: 'Image may appear low quality on social platforms.',
|
|
717
|
+
howToFix: 'Use an image at least 1200x630 pixels for optimal display.',
|
|
718
|
+
},
|
|
719
|
+
OG_URL_MISSING: {
|
|
720
|
+
code: 'OG_URL_MISSING',
|
|
721
|
+
severity: 'notice',
|
|
722
|
+
category: 'on-page',
|
|
723
|
+
title: 'Missing Open Graph URL',
|
|
724
|
+
description: 'No og:url meta tag found.',
|
|
725
|
+
impact: 'Social platforms may use inconsistent URL for shares.',
|
|
726
|
+
howToFix: 'Add <meta property="og:url" content="https://yoursite.com/page">',
|
|
727
|
+
},
|
|
728
|
+
OG_TYPE_MISSING: {
|
|
729
|
+
code: 'OG_TYPE_MISSING',
|
|
730
|
+
severity: 'notice',
|
|
731
|
+
category: 'on-page',
|
|
732
|
+
title: 'Missing Open Graph type',
|
|
733
|
+
description: 'No og:type meta tag found.',
|
|
734
|
+
impact: 'Social platforms default to "website" type.',
|
|
735
|
+
howToFix: 'Add <meta property="og:type" content="website"> (or article, product, etc.)',
|
|
736
|
+
},
|
|
737
|
+
TWITTER_CARD_MISSING: {
|
|
738
|
+
code: 'TWITTER_CARD_MISSING',
|
|
739
|
+
severity: 'notice',
|
|
740
|
+
category: 'on-page',
|
|
741
|
+
title: 'Missing Twitter Card tags',
|
|
742
|
+
description: 'No twitter:card meta tag found.',
|
|
743
|
+
impact: 'Twitter shares may not display optimally.',
|
|
744
|
+
howToFix: 'Add <meta name="twitter:card" content="summary_large_image">',
|
|
745
|
+
},
|
|
746
|
+
TWITTER_IMAGE_MISSING: {
|
|
747
|
+
code: 'TWITTER_IMAGE_MISSING',
|
|
748
|
+
severity: 'notice',
|
|
749
|
+
category: 'on-page',
|
|
750
|
+
title: 'Missing Twitter Card image',
|
|
751
|
+
description: 'No twitter:image meta tag found.',
|
|
752
|
+
impact: 'Twitter shares will not display a preview image.',
|
|
753
|
+
howToFix: 'Add <meta name="twitter:image" content="https://yoursite.com/image.jpg">',
|
|
754
|
+
},
|
|
755
|
+
|
|
756
|
+
// ==================== CANONICAL ADVANCED ====================
|
|
757
|
+
CANONICAL_CHAIN: {
|
|
758
|
+
code: 'CANONICAL_CHAIN',
|
|
759
|
+
severity: 'warning',
|
|
760
|
+
category: 'indexability',
|
|
761
|
+
title: 'Canonical chain detected',
|
|
762
|
+
description: 'Canonical URL points to a page with a different canonical.',
|
|
763
|
+
impact: 'Creates confusion for search engines about the preferred URL.',
|
|
764
|
+
howToFix: 'Update canonical to point directly to the final preferred URL.',
|
|
765
|
+
},
|
|
766
|
+
CANONICAL_CROSS_DOMAIN: {
|
|
767
|
+
code: 'CANONICAL_CROSS_DOMAIN',
|
|
768
|
+
severity: 'warning',
|
|
769
|
+
category: 'indexability',
|
|
770
|
+
title: 'Cross-domain canonical',
|
|
771
|
+
description: 'Canonical points to a different domain.',
|
|
772
|
+
impact: 'Transfers ranking signals to another domain. Ensure this is intentional.',
|
|
773
|
+
howToFix: 'Verify cross-domain canonical is intentional or update to same domain.',
|
|
774
|
+
},
|
|
775
|
+
CANONICAL_MISMATCH_SITEMAP: {
|
|
776
|
+
code: 'CANONICAL_MISMATCH_SITEMAP',
|
|
777
|
+
severity: 'warning',
|
|
778
|
+
category: 'indexability',
|
|
779
|
+
title: 'Canonical URL not in sitemap',
|
|
780
|
+
description: 'The canonical URL differs from the sitemap URL.',
|
|
781
|
+
impact: 'Conflicting signals about which URL should be indexed.',
|
|
782
|
+
howToFix: 'Ensure sitemap contains only canonical URLs.',
|
|
783
|
+
},
|
|
784
|
+
|
|
785
|
+
// ==================== ANCHOR TEXT ====================
|
|
786
|
+
ANCHOR_TEXT_GENERIC: {
|
|
787
|
+
code: 'ANCHOR_TEXT_GENERIC',
|
|
788
|
+
severity: 'notice',
|
|
789
|
+
category: 'links',
|
|
790
|
+
title: 'Generic anchor text detected',
|
|
791
|
+
description: 'Many internal links use generic text like "click here" or "read more".',
|
|
792
|
+
impact: 'Misses opportunity to provide context to search engines.',
|
|
793
|
+
howToFix: 'Use descriptive anchor text that indicates the target page content.',
|
|
794
|
+
},
|
|
795
|
+
ANCHOR_TEXT_OVER_OPTIMIZED: {
|
|
796
|
+
code: 'ANCHOR_TEXT_OVER_OPTIMIZED',
|
|
797
|
+
severity: 'warning',
|
|
798
|
+
category: 'links',
|
|
799
|
+
title: 'Over-optimized anchor text',
|
|
800
|
+
description: 'Excessive exact-match keyword anchor text detected.',
|
|
801
|
+
impact: 'May trigger spam filters and algorithmic penalties.',
|
|
802
|
+
howToFix: 'Diversify anchor text with natural variations.',
|
|
803
|
+
},
|
|
804
|
+
ANCHOR_TEXT_EMPTY: {
|
|
805
|
+
code: 'ANCHOR_TEXT_EMPTY',
|
|
806
|
+
severity: 'warning',
|
|
807
|
+
category: 'links',
|
|
808
|
+
title: 'Empty anchor text',
|
|
809
|
+
description: 'Link has no text content (empty anchor).',
|
|
810
|
+
impact: 'Provides no context for users or search engines.',
|
|
811
|
+
howToFix: 'Add descriptive text to the link or use aria-label for icon links.',
|
|
812
|
+
},
|
|
813
|
+
|
|
814
|
+
// ==================== PAGINATION ====================
|
|
815
|
+
PAGINATION_NO_LINKS: {
|
|
816
|
+
code: 'PAGINATION_NO_LINKS',
|
|
817
|
+
severity: 'warning',
|
|
818
|
+
category: 'crawlability',
|
|
819
|
+
title: 'Pagination without HTML links',
|
|
820
|
+
description: 'Paginated content uses JavaScript without HTML fallback links.',
|
|
821
|
+
impact: 'Search engines cannot discover paginated content.',
|
|
822
|
+
howToFix: 'Add <a href> links for pagination alongside JavaScript.',
|
|
823
|
+
},
|
|
824
|
+
PAGINATION_CANONICAL_ISSUE: {
|
|
825
|
+
code: 'PAGINATION_CANONICAL_ISSUE',
|
|
826
|
+
severity: 'error',
|
|
827
|
+
category: 'indexability',
|
|
828
|
+
title: 'Pagination canonical points to page 1',
|
|
829
|
+
description: 'Paginated pages have canonical pointing to page 1.',
|
|
830
|
+
impact: 'Paginated content may not be indexed properly.',
|
|
831
|
+
howToFix: 'Each paginated page should self-reference or use View All canonical.',
|
|
832
|
+
},
|
|
833
|
+
INFINITE_SCROLL_NO_FALLBACK: {
|
|
834
|
+
code: 'INFINITE_SCROLL_NO_FALLBACK',
|
|
835
|
+
severity: 'warning',
|
|
836
|
+
category: 'crawlability',
|
|
837
|
+
title: 'Infinite scroll without fallback',
|
|
838
|
+
description: 'Infinite scroll detected without pagination links.',
|
|
839
|
+
impact: 'Search engines cannot access content loaded via infinite scroll.',
|
|
840
|
+
howToFix: 'Implement replaceState/pushState and provide pagination links.',
|
|
841
|
+
},
|
|
842
|
+
|
|
843
|
+
// ==================== DUPLICATE/CANNIBALIZATION ====================
|
|
844
|
+
NEAR_DUPLICATE: {
|
|
845
|
+
code: 'NEAR_DUPLICATE',
|
|
846
|
+
severity: 'warning',
|
|
847
|
+
category: 'content',
|
|
848
|
+
title: 'Near-duplicate content',
|
|
849
|
+
description: 'Page has 90%+ content similarity with another page.',
|
|
850
|
+
impact: 'May cause keyword cannibalization and dilute ranking signals.',
|
|
851
|
+
howToFix: 'Consolidate pages, differentiate content, or use canonical tags.',
|
|
852
|
+
},
|
|
853
|
+
KEYWORD_CANNIBALIZATION: {
|
|
854
|
+
code: 'KEYWORD_CANNIBALIZATION',
|
|
855
|
+
severity: 'warning',
|
|
856
|
+
category: 'content',
|
|
857
|
+
title: 'Potential keyword cannibalization',
|
|
858
|
+
description: 'Multiple pages target the same primary keyword.',
|
|
859
|
+
impact: 'Pages compete against each other in search results.',
|
|
860
|
+
howToFix: 'Consolidate pages or differentiate keyword targeting.',
|
|
861
|
+
},
|
|
862
|
+
|
|
863
|
+
// ==================== LINK ANALYSIS ADVANCED ====================
|
|
864
|
+
LINK_DEPTH_TOO_DEEP: {
|
|
865
|
+
code: 'LINK_DEPTH_TOO_DEEP',
|
|
866
|
+
severity: 'warning',
|
|
867
|
+
category: 'links',
|
|
868
|
+
title: 'Page too deep in site structure',
|
|
869
|
+
description: 'Page requires more than 3 clicks from homepage.',
|
|
870
|
+
impact: 'Deep pages receive less link equity and may be crawled less frequently.',
|
|
871
|
+
howToFix: 'Improve internal linking to reduce click depth.',
|
|
872
|
+
},
|
|
873
|
+
INTERNAL_REDIRECT: {
|
|
874
|
+
code: 'INTERNAL_REDIRECT',
|
|
875
|
+
severity: 'notice',
|
|
876
|
+
category: 'links',
|
|
877
|
+
title: 'Internal link to redirect',
|
|
878
|
+
description: 'Internal link points to a URL that redirects.',
|
|
879
|
+
impact: 'Wastes crawl budget and dilutes link equity.',
|
|
880
|
+
howToFix: 'Update internal links to point directly to final URLs.',
|
|
881
|
+
},
|
|
882
|
+
|
|
883
|
+
// ==================== JAVASCRIPT SEO ====================
|
|
884
|
+
JS_RENDERED_CONTENT: {
|
|
885
|
+
code: 'JS_RENDERED_CONTENT',
|
|
886
|
+
severity: 'notice',
|
|
887
|
+
category: 'content',
|
|
888
|
+
title: 'JavaScript-rendered content detected',
|
|
889
|
+
description: 'Significant content only visible after JavaScript execution.',
|
|
890
|
+
impact: 'Search engines may not see all content or delay indexing.',
|
|
891
|
+
howToFix: 'Consider server-side rendering or prerendering for critical content.',
|
|
892
|
+
},
|
|
893
|
+
JS_LINKS_ONLY: {
|
|
894
|
+
code: 'JS_LINKS_ONLY',
|
|
895
|
+
severity: 'warning',
|
|
896
|
+
category: 'links',
|
|
897
|
+
title: 'Links only in JavaScript',
|
|
898
|
+
description: 'Internal links exist only in rendered DOM, not in source HTML.',
|
|
899
|
+
impact: 'Search engines may not discover these links.',
|
|
900
|
+
howToFix: 'Ensure important links are in static HTML or use server-side rendering.',
|
|
901
|
+
},
|
|
902
|
+
|
|
903
|
+
// ==================== FAVICON & MISC ====================
|
|
904
|
+
FAVICON_MISSING: {
|
|
905
|
+
code: 'FAVICON_MISSING',
|
|
906
|
+
severity: 'notice',
|
|
907
|
+
category: 'on-page',
|
|
908
|
+
title: 'Missing favicon',
|
|
909
|
+
description: 'No favicon.ico or link rel="icon" found.',
|
|
910
|
+
impact: 'Browser tabs show generic icon; poor brand presence.',
|
|
911
|
+
howToFix: 'Add a favicon.ico or link rel="icon" to your pages.',
|
|
912
|
+
},
|
|
913
|
+
LANG_ATTR_MISSING: {
|
|
914
|
+
code: 'LANG_ATTR_MISSING',
|
|
915
|
+
severity: 'notice',
|
|
916
|
+
category: 'on-page',
|
|
917
|
+
title: 'Missing lang attribute',
|
|
918
|
+
description: 'HTML tag lacks lang attribute.',
|
|
919
|
+
impact: 'Accessibility issue; browsers/screen readers may use wrong language.',
|
|
920
|
+
howToFix: 'Add lang attribute to HTML tag (e.g., <html lang="en">).',
|
|
921
|
+
},
|
|
922
|
+
|
|
923
|
+
// ==================== AI/LLM READINESS ====================
|
|
924
|
+
LLMS_TXT_MISSING: {
|
|
925
|
+
code: 'LLMS_TXT_MISSING',
|
|
926
|
+
severity: 'notice',
|
|
927
|
+
category: 'ai-readiness',
|
|
928
|
+
title: 'Missing llms.txt file',
|
|
929
|
+
description: 'No llms.txt file found at the root of the domain.',
|
|
930
|
+
impact: 'AI crawlers like ChatGPT and Claude may not understand how to best use your content.',
|
|
931
|
+
howToFix: 'Create an llms.txt file at your domain root with guidance for AI systems. See llmstxt.org for format.',
|
|
932
|
+
},
|
|
933
|
+
LLMS_TXT_INVALID: {
|
|
934
|
+
code: 'LLMS_TXT_INVALID',
|
|
935
|
+
severity: 'warning',
|
|
936
|
+
category: 'ai-readiness',
|
|
937
|
+
title: 'Invalid llms.txt format',
|
|
938
|
+
description: 'The llms.txt file exists but has invalid format or syntax.',
|
|
939
|
+
impact: 'AI crawlers may not be able to parse your AI guidance properly.',
|
|
940
|
+
howToFix: 'Validate your llms.txt file format against the specification at llmstxt.org.',
|
|
941
|
+
},
|
|
942
|
+
AI_BOT_BLOCKED_ALL: {
|
|
943
|
+
code: 'AI_BOT_BLOCKED_ALL',
|
|
944
|
+
severity: 'notice',
|
|
945
|
+
category: 'ai-readiness',
|
|
946
|
+
title: 'All AI bots blocked in robots.txt',
|
|
947
|
+
description: 'robots.txt blocks all major AI crawlers (GPTBot, Claude-Web, PerplexityBot, etc.).',
|
|
948
|
+
impact: 'Your content will not appear in AI-powered search and assistants.',
|
|
949
|
+
howToFix: 'Consider allowing some AI bots if you want visibility in AI-generated responses.',
|
|
950
|
+
},
|
|
951
|
+
AI_BOT_GPTBOT_BLOCKED: {
|
|
952
|
+
code: 'AI_BOT_GPTBOT_BLOCKED',
|
|
953
|
+
severity: 'notice',
|
|
954
|
+
category: 'ai-readiness',
|
|
955
|
+
title: 'GPTBot blocked in robots.txt',
|
|
956
|
+
description: 'robots.txt blocks OpenAI\'s GPTBot crawler.',
|
|
957
|
+
impact: 'Your content will not be used to train or cited by ChatGPT.',
|
|
958
|
+
howToFix: 'Remove "User-agent: GPTBot Disallow: /" if you want ChatGPT visibility.',
|
|
959
|
+
},
|
|
960
|
+
AI_BOT_CLAUDEBOT_BLOCKED: {
|
|
961
|
+
code: 'AI_BOT_CLAUDEBOT_BLOCKED',
|
|
962
|
+
severity: 'notice',
|
|
963
|
+
category: 'ai-readiness',
|
|
964
|
+
title: 'ClaudeBot blocked in robots.txt',
|
|
965
|
+
description: 'robots.txt blocks Anthropic\'s ClaudeBot crawler.',
|
|
966
|
+
impact: 'Your content will not be used by Claude AI.',
|
|
967
|
+
howToFix: 'Remove "User-agent: ClaudeBot Disallow: /" if you want Claude visibility.',
|
|
968
|
+
},
|
|
969
|
+
AI_BOT_PERPLEXITY_BLOCKED: {
|
|
970
|
+
code: 'AI_BOT_PERPLEXITY_BLOCKED',
|
|
971
|
+
severity: 'notice',
|
|
972
|
+
category: 'ai-readiness',
|
|
973
|
+
title: 'PerplexityBot blocked in robots.txt',
|
|
974
|
+
description: 'robots.txt blocks Perplexity AI\'s crawler.',
|
|
975
|
+
impact: 'Your content will not appear in Perplexity AI search results.',
|
|
976
|
+
howToFix: 'Remove "User-agent: PerplexityBot Disallow: /" if you want Perplexity visibility.',
|
|
977
|
+
},
|
|
978
|
+
AI_BOT_GOOGLE_EXTENDED_BLOCKED: {
|
|
979
|
+
code: 'AI_BOT_GOOGLE_EXTENDED_BLOCKED',
|
|
980
|
+
severity: 'notice',
|
|
981
|
+
category: 'ai-readiness',
|
|
982
|
+
title: 'Google-Extended blocked in robots.txt',
|
|
983
|
+
description: 'robots.txt blocks Google\'s AI training crawler (Google-Extended).',
|
|
984
|
+
impact: 'Your content will not be used for Bard/Gemini AI training (regular search unaffected).',
|
|
985
|
+
howToFix: 'Remove "User-agent: Google-Extended Disallow: /" if you want Google AI visibility.',
|
|
986
|
+
},
|
|
987
|
+
HIGH_JS_RENDERING_RATIO: {
|
|
988
|
+
code: 'HIGH_JS_RENDERING_RATIO',
|
|
989
|
+
severity: 'warning',
|
|
990
|
+
category: 'ai-readiness',
|
|
991
|
+
title: 'High JavaScript rendering required',
|
|
992
|
+
description: 'More than 50% of page content requires JavaScript to render.',
|
|
993
|
+
impact: 'AI crawlers may not see all content; they often use static HTML only.',
|
|
994
|
+
howToFix: 'Implement server-side rendering or prerendering for critical content.',
|
|
995
|
+
},
|
|
996
|
+
|
|
997
|
+
// ==================== PIXEL-BASED META CHECKS ====================
|
|
998
|
+
TITLE_TRUNCATED_SERP: {
|
|
999
|
+
code: 'TITLE_TRUNCATED_SERP',
|
|
1000
|
+
severity: 'warning',
|
|
1001
|
+
category: 'on-page',
|
|
1002
|
+
title: 'Title will be truncated in SERP',
|
|
1003
|
+
description: 'Title exceeds 580 pixels and will be cut off in Google search results.',
|
|
1004
|
+
impact: 'Important information at the end of your title will not be visible.',
|
|
1005
|
+
howToFix: 'Shorten title to fit within 580 pixel width (approximately 50-60 characters).',
|
|
1006
|
+
},
|
|
1007
|
+
META_DESC_TRUNCATED_SERP: {
|
|
1008
|
+
code: 'META_DESC_TRUNCATED_SERP',
|
|
1009
|
+
severity: 'notice',
|
|
1010
|
+
category: 'on-page',
|
|
1011
|
+
title: 'Meta description will be truncated in SERP',
|
|
1012
|
+
description: 'Meta description exceeds 920 pixels on desktop.',
|
|
1013
|
+
impact: 'Your description may be cut off in search results.',
|
|
1014
|
+
howToFix: 'Shorten description to fit within 920 pixels (approximately 150-160 characters).',
|
|
1015
|
+
},
|
|
1016
|
+
|
|
1017
|
+
// ==================== SOCIAL META TAGS (Enhanced) ====================
|
|
1018
|
+
TWITTER_TITLE_MISSING: {
|
|
1019
|
+
code: 'TWITTER_TITLE_MISSING',
|
|
1020
|
+
severity: 'notice',
|
|
1021
|
+
category: 'social',
|
|
1022
|
+
title: 'Missing Twitter Card title',
|
|
1023
|
+
description: 'No twitter:title meta tag found.',
|
|
1024
|
+
impact: 'Twitter/X shares will use og:title or page title as fallback.',
|
|
1025
|
+
howToFix: 'Add <meta name="twitter:title" content="Your Title">',
|
|
1026
|
+
},
|
|
1027
|
+
TWITTER_DESCRIPTION_MISSING: {
|
|
1028
|
+
code: 'TWITTER_DESCRIPTION_MISSING',
|
|
1029
|
+
severity: 'notice',
|
|
1030
|
+
category: 'social',
|
|
1031
|
+
title: 'Missing Twitter Card description',
|
|
1032
|
+
description: 'No twitter:description meta tag found.',
|
|
1033
|
+
impact: 'Twitter/X shares will use og:description or meta description as fallback.',
|
|
1034
|
+
howToFix: 'Add <meta name="twitter:description" content="Your description">',
|
|
1035
|
+
},
|
|
1036
|
+
OG_IMAGE_WRONG_DIMENSIONS: {
|
|
1037
|
+
code: 'OG_IMAGE_WRONG_DIMENSIONS',
|
|
1038
|
+
severity: 'notice',
|
|
1039
|
+
category: 'social',
|
|
1040
|
+
title: 'Open Graph image wrong aspect ratio',
|
|
1041
|
+
description: 'OG image is not the recommended 1.91:1 aspect ratio (1200x630).',
|
|
1042
|
+
impact: 'Image may be cropped awkwardly on social platforms.',
|
|
1043
|
+
howToFix: 'Use an image with 1200x630 pixels (1.91:1 aspect ratio).',
|
|
1044
|
+
},
|
|
1045
|
+
OG_IMAGE_TOO_LARGE: {
|
|
1046
|
+
code: 'OG_IMAGE_TOO_LARGE',
|
|
1047
|
+
severity: 'warning',
|
|
1048
|
+
category: 'social',
|
|
1049
|
+
title: 'Open Graph image file too large',
|
|
1050
|
+
description: 'OG image exceeds 5MB file size limit.',
|
|
1051
|
+
impact: 'Social platforms may not display the image.',
|
|
1052
|
+
howToFix: 'Compress the image to under 5MB while maintaining quality.',
|
|
1053
|
+
},
|
|
1054
|
+
SOCIAL_PROFILES_NOT_LINKED: {
|
|
1055
|
+
code: 'SOCIAL_PROFILES_NOT_LINKED',
|
|
1056
|
+
severity: 'notice',
|
|
1057
|
+
category: 'social',
|
|
1058
|
+
title: 'No social profile links found',
|
|
1059
|
+
description: 'Page has no links to social media profiles.',
|
|
1060
|
+
impact: 'Missed opportunity for social proof and brand presence.',
|
|
1061
|
+
howToFix: 'Add links to your social media profiles (Facebook, Twitter, LinkedIn, etc.).',
|
|
1062
|
+
},
|
|
1063
|
+
|
|
1064
|
+
// ==================== LOCAL SEO ====================
|
|
1065
|
+
LOCAL_BUSINESS_SCHEMA_MISSING: {
|
|
1066
|
+
code: 'LOCAL_BUSINESS_SCHEMA_MISSING',
|
|
1067
|
+
severity: 'notice',
|
|
1068
|
+
category: 'local-seo',
|
|
1069
|
+
title: 'Missing LocalBusiness schema',
|
|
1070
|
+
description: 'No LocalBusiness structured data found.',
|
|
1071
|
+
impact: 'Local business information won\'t appear in Google\'s local knowledge panel.',
|
|
1072
|
+
howToFix: 'Add LocalBusiness schema with name, address, phone, and hours.',
|
|
1073
|
+
},
|
|
1074
|
+
ORGANIZATION_SCHEMA_MISSING: {
|
|
1075
|
+
code: 'ORGANIZATION_SCHEMA_MISSING',
|
|
1076
|
+
severity: 'notice',
|
|
1077
|
+
category: 'local-seo',
|
|
1078
|
+
title: 'Missing Organization schema',
|
|
1079
|
+
description: 'No Organization or Person structured data found.',
|
|
1080
|
+
impact: 'Search engines may not understand your brand identity.',
|
|
1081
|
+
howToFix: 'Add Organization schema with logo, contact info, and social profiles.',
|
|
1082
|
+
},
|
|
1083
|
+
NAP_INCONSISTENT: {
|
|
1084
|
+
code: 'NAP_INCONSISTENT',
|
|
1085
|
+
severity: 'warning',
|
|
1086
|
+
category: 'local-seo',
|
|
1087
|
+
title: 'Inconsistent NAP information',
|
|
1088
|
+
description: 'Name, Address, Phone detected in multiple formats on page.',
|
|
1089
|
+
impact: 'Inconsistent business info can hurt local search rankings.',
|
|
1090
|
+
howToFix: 'Use consistent NAP format across all pages and match your Google Business Profile.',
|
|
1091
|
+
},
|
|
1092
|
+
ADDRESS_NOT_DETECTED: {
|
|
1093
|
+
code: 'ADDRESS_NOT_DETECTED',
|
|
1094
|
+
severity: 'notice',
|
|
1095
|
+
category: 'local-seo',
|
|
1096
|
+
title: 'No business address detected',
|
|
1097
|
+
description: 'Could not find a physical address on this page.',
|
|
1098
|
+
impact: 'Local businesses should display their address for local SEO.',
|
|
1099
|
+
howToFix: 'Add your business address in a visible location and in LocalBusiness schema.',
|
|
1100
|
+
},
|
|
1101
|
+
PHONE_NOT_DETECTED: {
|
|
1102
|
+
code: 'PHONE_NOT_DETECTED',
|
|
1103
|
+
severity: 'notice',
|
|
1104
|
+
category: 'local-seo',
|
|
1105
|
+
title: 'No phone number detected',
|
|
1106
|
+
description: 'Could not find a phone number on this page.',
|
|
1107
|
+
impact: 'Contact information helps with local SEO and user trust.',
|
|
1108
|
+
howToFix: 'Add your phone number in visible location and in LocalBusiness schema.',
|
|
1109
|
+
},
|
|
1110
|
+
|
|
1111
|
+
// ==================== SECURITY HEADERS ====================
|
|
1112
|
+
CSP_MISSING: {
|
|
1113
|
+
code: 'CSP_MISSING',
|
|
1114
|
+
severity: 'notice',
|
|
1115
|
+
category: 'security',
|
|
1116
|
+
title: 'Missing Content-Security-Policy header',
|
|
1117
|
+
description: 'No Content-Security-Policy (CSP) header found.',
|
|
1118
|
+
impact: 'Site is more vulnerable to XSS and code injection attacks.',
|
|
1119
|
+
howToFix: 'Add a Content-Security-Policy header to control which resources can be loaded.',
|
|
1120
|
+
},
|
|
1121
|
+
X_FRAME_OPTIONS_MISSING: {
|
|
1122
|
+
code: 'X_FRAME_OPTIONS_MISSING',
|
|
1123
|
+
severity: 'notice',
|
|
1124
|
+
category: 'security',
|
|
1125
|
+
title: 'Missing X-Frame-Options header',
|
|
1126
|
+
description: 'No X-Frame-Options header found.',
|
|
1127
|
+
impact: 'Site may be vulnerable to clickjacking attacks.',
|
|
1128
|
+
howToFix: 'Add X-Frame-Options: DENY or SAMEORIGIN header.',
|
|
1129
|
+
},
|
|
1130
|
+
X_CONTENT_TYPE_OPTIONS_MISSING: {
|
|
1131
|
+
code: 'X_CONTENT_TYPE_OPTIONS_MISSING',
|
|
1132
|
+
severity: 'notice',
|
|
1133
|
+
category: 'security',
|
|
1134
|
+
title: 'Missing X-Content-Type-Options header',
|
|
1135
|
+
description: 'No X-Content-Type-Options header found.',
|
|
1136
|
+
impact: 'Browser may MIME-sniff content types, potentially executing malicious files.',
|
|
1137
|
+
howToFix: 'Add X-Content-Type-Options: nosniff header.',
|
|
1138
|
+
},
|
|
1139
|
+
REFERRER_POLICY_MISSING: {
|
|
1140
|
+
code: 'REFERRER_POLICY_MISSING',
|
|
1141
|
+
severity: 'notice',
|
|
1142
|
+
category: 'security',
|
|
1143
|
+
title: 'Missing Referrer-Policy header',
|
|
1144
|
+
description: 'No Referrer-Policy header found.',
|
|
1145
|
+
impact: 'Browser may leak referrer information to third-party sites.',
|
|
1146
|
+
howToFix: 'Add Referrer-Policy: strict-origin-when-cross-origin header.',
|
|
1147
|
+
},
|
|
1148
|
+
PERMISSIONS_POLICY_MISSING: {
|
|
1149
|
+
code: 'PERMISSIONS_POLICY_MISSING',
|
|
1150
|
+
severity: 'notice',
|
|
1151
|
+
category: 'security',
|
|
1152
|
+
title: 'Missing Permissions-Policy header',
|
|
1153
|
+
description: 'No Permissions-Policy (formerly Feature-Policy) header found.',
|
|
1154
|
+
impact: 'Cannot control which browser features your site can use.',
|
|
1155
|
+
howToFix: 'Add Permissions-Policy header to control camera, microphone, geolocation, etc.',
|
|
1156
|
+
},
|
|
1157
|
+
|
|
1158
|
+
// ==================== CONTENT FRESHNESS ====================
|
|
1159
|
+
CONTENT_STALE: {
|
|
1160
|
+
code: 'CONTENT_STALE',
|
|
1161
|
+
severity: 'notice',
|
|
1162
|
+
category: 'content',
|
|
1163
|
+
title: 'Content appears stale',
|
|
1164
|
+
description: 'Last-Modified header shows content over 1 year old.',
|
|
1165
|
+
impact: 'Search engines may prioritize fresher content in results.',
|
|
1166
|
+
howToFix: 'Update content regularly and ensure Last-Modified header is accurate.',
|
|
1167
|
+
},
|
|
1168
|
+
NO_LAST_MODIFIED: {
|
|
1169
|
+
code: 'NO_LAST_MODIFIED',
|
|
1170
|
+
severity: 'notice',
|
|
1171
|
+
category: 'content',
|
|
1172
|
+
title: 'No Last-Modified header',
|
|
1173
|
+
description: 'Server does not send Last-Modified header.',
|
|
1174
|
+
impact: 'Browsers and CDNs cannot efficiently cache content.',
|
|
1175
|
+
howToFix: 'Configure your server to send Last-Modified headers.',
|
|
1176
|
+
},
|
|
1177
|
+
OG_UPDATED_TIME_MISSING: {
|
|
1178
|
+
code: 'OG_UPDATED_TIME_MISSING',
|
|
1179
|
+
severity: 'notice',
|
|
1180
|
+
category: 'content',
|
|
1181
|
+
title: 'Missing article:modified_time',
|
|
1182
|
+
description: 'No article:modified_time Open Graph tag found.',
|
|
1183
|
+
impact: 'Social platforms may not show content update dates.',
|
|
1184
|
+
howToFix: 'Add <meta property="article:modified_time" content="2024-01-01T12:00:00Z">',
|
|
1185
|
+
},
|
|
1186
|
+
|
|
1187
|
+
// ==================== PERFORMANCE (Enhanced) ====================
|
|
1188
|
+
RENDER_BLOCKING_RESOURCES: {
|
|
1189
|
+
code: 'RENDER_BLOCKING_RESOURCES',
|
|
1190
|
+
severity: 'warning',
|
|
1191
|
+
category: 'performance',
|
|
1192
|
+
title: 'Render-blocking resources detected',
|
|
1193
|
+
description: 'CSS or JavaScript files block initial page render.',
|
|
1194
|
+
impact: 'Delays First Contentful Paint and user perception of speed.',
|
|
1195
|
+
howToFix: 'Inline critical CSS, defer non-critical JS, or use async/defer attributes.',
|
|
1196
|
+
},
|
|
1197
|
+
DOM_SIZE_EXCESSIVE: {
|
|
1198
|
+
code: 'DOM_SIZE_EXCESSIVE',
|
|
1199
|
+
severity: 'warning',
|
|
1200
|
+
category: 'performance',
|
|
1201
|
+
title: 'Excessive DOM size',
|
|
1202
|
+
description: 'Page has more than 1500 DOM nodes.',
|
|
1203
|
+
impact: 'Large DOM increases memory usage and slows style calculations.',
|
|
1204
|
+
howToFix: 'Simplify page structure, lazy-load content, or paginate long lists.',
|
|
1205
|
+
},
|
|
1206
|
+
DOM_DEPTH_EXCESSIVE: {
|
|
1207
|
+
code: 'DOM_DEPTH_EXCESSIVE',
|
|
1208
|
+
severity: 'notice',
|
|
1209
|
+
category: 'performance',
|
|
1210
|
+
title: 'Excessive DOM depth',
|
|
1211
|
+
description: 'DOM tree is more than 32 levels deep.',
|
|
1212
|
+
impact: 'Deep nesting slows CSS selector matching and layout calculations.',
|
|
1213
|
+
howToFix: 'Flatten your HTML structure where possible.',
|
|
1214
|
+
},
|
|
1215
|
+
IMAGE_NOT_WEBP_AVIF: {
|
|
1216
|
+
code: 'IMAGE_NOT_WEBP_AVIF',
|
|
1217
|
+
severity: 'notice',
|
|
1218
|
+
category: 'images',
|
|
1219
|
+
title: 'Images not using modern formats',
|
|
1220
|
+
description: 'Images use JPEG/PNG instead of WebP/AVIF.',
|
|
1221
|
+
impact: 'Modern formats provide 25-50% smaller file sizes.',
|
|
1222
|
+
howToFix: 'Convert images to WebP or AVIF format with fallbacks.',
|
|
1223
|
+
},
|
|
1224
|
+
NO_CDN_DETECTED: {
|
|
1225
|
+
code: 'NO_CDN_DETECTED',
|
|
1226
|
+
severity: 'notice',
|
|
1227
|
+
category: 'performance',
|
|
1228
|
+
title: 'No CDN detected',
|
|
1229
|
+
description: 'Static resources are not served from a CDN.',
|
|
1230
|
+
impact: 'Users far from your server experience slower load times.',
|
|
1231
|
+
howToFix: 'Use a CDN like Cloudflare, Fastly, or AWS CloudFront.',
|
|
1232
|
+
},
|
|
1233
|
+
|
|
1234
|
+
// ==================== TECHNOLOGY DETECTION ====================
|
|
1235
|
+
WORDPRESS_DETECTED: {
|
|
1236
|
+
code: 'WORDPRESS_DETECTED',
|
|
1237
|
+
severity: 'notice',
|
|
1238
|
+
category: 'on-page',
|
|
1239
|
+
title: 'WordPress detected',
|
|
1240
|
+
description: 'Site is built with WordPress CMS.',
|
|
1241
|
+
impact: 'Informational - WordPress powers 40% of the web.',
|
|
1242
|
+
howToFix: 'Ensure WordPress core, themes, and plugins are up to date.',
|
|
1243
|
+
},
|
|
1244
|
+
OUTDATED_JQUERY: {
|
|
1245
|
+
code: 'OUTDATED_JQUERY',
|
|
1246
|
+
severity: 'warning',
|
|
1247
|
+
category: 'security',
|
|
1248
|
+
title: 'Outdated jQuery version detected',
|
|
1249
|
+
description: 'Page uses a jQuery version with known vulnerabilities.',
|
|
1250
|
+
impact: 'Site may be vulnerable to security exploits.',
|
|
1251
|
+
howToFix: 'Update jQuery to the latest version (3.x or higher).',
|
|
1252
|
+
},
|
|
1253
|
+
OUTDATED_FRAMEWORK: {
|
|
1254
|
+
code: 'OUTDATED_FRAMEWORK',
|
|
1255
|
+
severity: 'warning',
|
|
1256
|
+
category: 'security',
|
|
1257
|
+
title: 'Outdated JavaScript framework detected',
|
|
1258
|
+
description: 'Page uses an outdated version of a JavaScript framework.',
|
|
1259
|
+
impact: 'May have security vulnerabilities or performance issues.',
|
|
1260
|
+
howToFix: 'Update to the latest stable version of the framework.',
|
|
1261
|
+
},
|
|
1262
|
+
|
|
1263
|
+
// ==================== KEYWORD DENSITY ====================
|
|
1264
|
+
KEYWORD_STUFFING: {
|
|
1265
|
+
code: 'KEYWORD_STUFFING',
|
|
1266
|
+
severity: 'warning',
|
|
1267
|
+
category: 'content',
|
|
1268
|
+
title: 'Potential keyword stuffing detected',
|
|
1269
|
+
description: 'A keyword appears more than 3% of total word count.',
|
|
1270
|
+
impact: 'May trigger search engine spam filters.',
|
|
1271
|
+
howToFix: 'Write naturally and use keyword variations instead of repetition.',
|
|
1272
|
+
},
|
|
1273
|
+
NO_KEYWORDS_IN_TITLE: {
|
|
1274
|
+
code: 'NO_KEYWORDS_IN_TITLE',
|
|
1275
|
+
severity: 'warning',
|
|
1276
|
+
category: 'on-page',
|
|
1277
|
+
title: 'Top keywords not in title',
|
|
1278
|
+
description: 'Most common page keywords are not present in the title tag.',
|
|
1279
|
+
impact: 'Title may not accurately reflect page content for search engines.',
|
|
1280
|
+
howToFix: 'Include your primary keyword in the title tag.',
|
|
1281
|
+
},
|
|
1282
|
+
NO_KEYWORDS_IN_H1: {
|
|
1283
|
+
code: 'NO_KEYWORDS_IN_H1',
|
|
1284
|
+
severity: 'notice',
|
|
1285
|
+
category: 'on-page',
|
|
1286
|
+
title: 'Top keywords not in H1',
|
|
1287
|
+
description: 'Most common page keywords are not present in the H1 heading.',
|
|
1288
|
+
impact: 'H1 may not signal page topic effectively.',
|
|
1289
|
+
howToFix: 'Include your primary keyword in the H1 heading.',
|
|
1290
|
+
},
|
|
1291
|
+
|
|
1292
|
+
// ==================== ADDITIONAL CHECKS ====================
|
|
1293
|
+
AMP_MISSING: {
|
|
1294
|
+
code: 'AMP_MISSING',
|
|
1295
|
+
severity: 'notice',
|
|
1296
|
+
category: 'mobile',
|
|
1297
|
+
title: 'No AMP version detected',
|
|
1298
|
+
description: 'Page does not have an Accelerated Mobile Page version.',
|
|
1299
|
+
impact: 'May load slower on mobile and miss Top Stories carousel eligibility.',
|
|
1300
|
+
howToFix: 'Consider implementing AMP for content-heavy pages.',
|
|
1301
|
+
},
|
|
1302
|
+
AMP_INVALID: {
|
|
1303
|
+
code: 'AMP_INVALID',
|
|
1304
|
+
severity: 'warning',
|
|
1305
|
+
category: 'mobile',
|
|
1306
|
+
title: 'Invalid AMP markup',
|
|
1307
|
+
description: 'AMP page contains validation errors.',
|
|
1308
|
+
impact: 'Invalid AMP pages may not be served from Google AMP cache.',
|
|
1309
|
+
howToFix: 'Validate and fix AMP errors using the AMP Validator.',
|
|
1310
|
+
},
|
|
1311
|
+
ADS_TXT_MISSING: {
|
|
1312
|
+
code: 'ADS_TXT_MISSING',
|
|
1313
|
+
severity: 'notice',
|
|
1314
|
+
category: 'crawlability',
|
|
1315
|
+
title: 'Missing ads.txt file',
|
|
1316
|
+
description: 'No ads.txt file found for ad inventory authorization.',
|
|
1317
|
+
impact: 'If running ads, unauthorized sellers may claim your inventory.',
|
|
1318
|
+
howToFix: 'Create an ads.txt file listing authorized ad sellers.',
|
|
1319
|
+
},
|
|
1320
|
+
ADS_TXT_INVALID: {
|
|
1321
|
+
code: 'ADS_TXT_INVALID',
|
|
1322
|
+
severity: 'warning',
|
|
1323
|
+
category: 'crawlability',
|
|
1324
|
+
title: 'Invalid ads.txt format',
|
|
1325
|
+
description: 'ads.txt file contains syntax errors.',
|
|
1326
|
+
impact: 'Ad platforms may not recognize your authorized sellers.',
|
|
1327
|
+
howToFix: 'Validate ads.txt format using IAB ads.txt validator.',
|
|
1328
|
+
},
|
|
1329
|
+
DMARC_MISSING: {
|
|
1330
|
+
code: 'DMARC_MISSING',
|
|
1331
|
+
severity: 'notice',
|
|
1332
|
+
category: 'security',
|
|
1333
|
+
title: 'Missing DMARC record',
|
|
1334
|
+
description: 'No DMARC DNS record found for email authentication.',
|
|
1335
|
+
impact: 'Emails from your domain may be spoofed or marked as spam.',
|
|
1336
|
+
howToFix: 'Add a DMARC record to your DNS: _dmarc.yourdomain.com TXT "v=DMARC1; p=none"',
|
|
1337
|
+
},
|
|
1338
|
+
SPF_MISSING: {
|
|
1339
|
+
code: 'SPF_MISSING',
|
|
1340
|
+
severity: 'notice',
|
|
1341
|
+
category: 'security',
|
|
1342
|
+
title: 'Missing SPF record',
|
|
1343
|
+
description: 'No SPF DNS record found for email authentication.',
|
|
1344
|
+
impact: 'Emails from your domain may be marked as spam.',
|
|
1345
|
+
howToFix: 'Add an SPF record to your DNS with authorized mail servers.',
|
|
1346
|
+
},
|
|
1347
|
+
SAFE_BROWSING_FLAGGED: {
|
|
1348
|
+
code: 'SAFE_BROWSING_FLAGGED',
|
|
1349
|
+
severity: 'error',
|
|
1350
|
+
category: 'security',
|
|
1351
|
+
title: 'Site flagged by Google Safe Browsing',
|
|
1352
|
+
description: 'Google has flagged this site for malware or phishing.',
|
|
1353
|
+
impact: 'Browsers will show warnings and traffic will plummet.',
|
|
1354
|
+
howToFix: 'Remove malware/phishing content and request review in Search Console.',
|
|
1355
|
+
},
|
|
1356
|
+
PLAINTEXT_EMAIL: {
|
|
1357
|
+
code: 'PLAINTEXT_EMAIL',
|
|
1358
|
+
severity: 'notice',
|
|
1359
|
+
category: 'security',
|
|
1360
|
+
title: 'Email addresses in plain text',
|
|
1361
|
+
description: 'Email addresses found in page HTML without obfuscation.',
|
|
1362
|
+
impact: 'Email addresses can be harvested by spam bots.',
|
|
1363
|
+
howToFix: 'Use contact forms or obfuscate emails (e.g., name[at]domain.com).',
|
|
1364
|
+
},
|
|
1365
|
+
DIRECTORY_LISTING_ENABLED: {
|
|
1366
|
+
code: 'DIRECTORY_LISTING_ENABLED',
|
|
1367
|
+
severity: 'warning',
|
|
1368
|
+
category: 'security',
|
|
1369
|
+
title: 'Directory listing enabled',
|
|
1370
|
+
description: 'Web server is configured to list directory contents.',
|
|
1371
|
+
impact: 'Exposes file structure and potentially sensitive files.',
|
|
1372
|
+
howToFix: 'Disable directory listing in your web server configuration.',
|
|
1373
|
+
},
|
|
1374
|
+
FAVICON_NOT_ICO: {
|
|
1375
|
+
code: 'FAVICON_NOT_ICO',
|
|
1376
|
+
severity: 'notice',
|
|
1377
|
+
category: 'on-page',
|
|
1378
|
+
title: 'Favicon not in ICO format',
|
|
1379
|
+
description: 'Favicon is not in the traditional .ico format.',
|
|
1380
|
+
impact: 'Some older browsers may not display the favicon.',
|
|
1381
|
+
howToFix: 'Provide favicon.ico at site root in addition to PNG/SVG versions.',
|
|
1382
|
+
},
|
|
1383
|
+
APPLE_TOUCH_ICON_MISSING: {
|
|
1384
|
+
code: 'APPLE_TOUCH_ICON_MISSING',
|
|
1385
|
+
severity: 'notice',
|
|
1386
|
+
category: 'mobile',
|
|
1387
|
+
title: 'Missing Apple Touch Icon',
|
|
1388
|
+
description: 'No apple-touch-icon link found.',
|
|
1389
|
+
impact: 'iOS users adding site to home screen will see a generic icon.',
|
|
1390
|
+
howToFix: 'Add <link rel="apple-touch-icon" href="/apple-touch-icon.png">',
|
|
1391
|
+
},
|
|
1392
|
+
|
|
1393
|
+
// ==================== ADVANCED SEO CHECKS ====================
|
|
1394
|
+
|
|
1395
|
+
// Featured Snippet Optimization
|
|
1396
|
+
FAQ_SCHEMA_MISSING: {
|
|
1397
|
+
code: 'FAQ_SCHEMA_MISSING',
|
|
1398
|
+
severity: 'warning',
|
|
1399
|
+
category: 'structured-data',
|
|
1400
|
+
title: 'FAQ content without FAQPage schema',
|
|
1401
|
+
description: 'Question-format headings found but no FAQPage schema markup.',
|
|
1402
|
+
impact: 'Missing out on FAQ rich results and PAA box opportunities.',
|
|
1403
|
+
howToFix: 'Add FAQPage structured data for your Q&A content.',
|
|
1404
|
+
},
|
|
1405
|
+
INVERTED_PYRAMID_MISSING: {
|
|
1406
|
+
code: 'INVERTED_PYRAMID_MISSING',
|
|
1407
|
+
severity: 'notice',
|
|
1408
|
+
category: 'content',
|
|
1409
|
+
title: 'Content not structured for featured snippets',
|
|
1410
|
+
description: 'First paragraph does not contain a direct answer or summary.',
|
|
1411
|
+
impact: 'Featured snippets pull from early content; burying answers reduces snippet capture rate.',
|
|
1412
|
+
howToFix: 'Start with a concise answer (40-80 words) that directly addresses the page topic.',
|
|
1413
|
+
},
|
|
1414
|
+
NO_SNIPPET_BAIT_HEADERS: {
|
|
1415
|
+
code: 'NO_SNIPPET_BAIT_HEADERS',
|
|
1416
|
+
severity: 'notice',
|
|
1417
|
+
category: 'on-page',
|
|
1418
|
+
title: 'No snippet-targeting headers found',
|
|
1419
|
+
description: 'Headings do not use patterns that commonly trigger featured snippets.',
|
|
1420
|
+
impact: 'Missing opportunities for high-visibility snippet placements.',
|
|
1421
|
+
howToFix: 'Use headers like "What is [topic]?", "How to [action]", or "X Ways to [achieve goal]".',
|
|
1422
|
+
},
|
|
1423
|
+
|
|
1424
|
+
// Internal Link Graph
|
|
1425
|
+
NO_CONTEXTUAL_INTERNAL_LINKS: {
|
|
1426
|
+
code: 'NO_CONTEXTUAL_INTERNAL_LINKS',
|
|
1427
|
+
severity: 'warning',
|
|
1428
|
+
category: 'links',
|
|
1429
|
+
title: 'No contextual internal links',
|
|
1430
|
+
description: 'All internal links are in navigation/footer. No editorial links in content.',
|
|
1431
|
+
impact: 'Editorial links in content pass more SEO value than navigational links.',
|
|
1432
|
+
howToFix: 'Add relevant internal links within your main content to related pages.',
|
|
1433
|
+
},
|
|
1434
|
+
LOW_EDITORIAL_LINKS: {
|
|
1435
|
+
code: 'LOW_EDITORIAL_LINKS',
|
|
1436
|
+
severity: 'notice',
|
|
1437
|
+
category: 'links',
|
|
1438
|
+
title: 'Few editorial internal links',
|
|
1439
|
+
description: 'Only a few editorial (in-content) internal links found.',
|
|
1440
|
+
impact: 'Editorial links with surrounding context provide stronger topical signals.',
|
|
1441
|
+
howToFix: 'Add 2-5 relevant internal links within your body content.',
|
|
1442
|
+
},
|
|
1443
|
+
FIRST_LINK_GENERIC_ANCHOR: {
|
|
1444
|
+
code: 'FIRST_LINK_GENERIC_ANCHOR',
|
|
1445
|
+
severity: 'notice',
|
|
1446
|
+
category: 'links',
|
|
1447
|
+
title: 'First link uses generic anchor text',
|
|
1448
|
+
description: 'First link to a page uses generic anchor like "click here" or "read more".',
|
|
1449
|
+
impact: 'Google may prioritize first link anchor for topic signals.',
|
|
1450
|
+
howToFix: 'Use descriptive, keyword-relevant anchor text for internal links.',
|
|
1451
|
+
},
|
|
1452
|
+
TOO_MANY_INTERNAL_LINKS: {
|
|
1453
|
+
code: 'TOO_MANY_INTERNAL_LINKS',
|
|
1454
|
+
severity: 'warning',
|
|
1455
|
+
category: 'links',
|
|
1456
|
+
title: 'Excessive internal links',
|
|
1457
|
+
description: 'Page has over 100 internal links, which may dilute PageRank distribution.',
|
|
1458
|
+
impact: 'Too many links reduce the value passed to each linked page.',
|
|
1459
|
+
howToFix: 'Reduce internal links to the most important and relevant pages.',
|
|
1460
|
+
},
|
|
1461
|
+
NO_INTERNAL_LINKS: {
|
|
1462
|
+
code: 'NO_INTERNAL_LINKS',
|
|
1463
|
+
severity: 'error',
|
|
1464
|
+
category: 'links',
|
|
1465
|
+
title: 'No internal links found',
|
|
1466
|
+
description: 'Page has no internal links to other pages on the site.',
|
|
1467
|
+
impact: 'Creates a dead end for users and search engine crawlers.',
|
|
1468
|
+
howToFix: 'Add relevant internal links to related content.',
|
|
1469
|
+
},
|
|
1470
|
+
|
|
1471
|
+
// Resource Hints
|
|
1472
|
+
MISSING_PRECONNECT: {
|
|
1473
|
+
code: 'MISSING_PRECONNECT',
|
|
1474
|
+
severity: 'warning',
|
|
1475
|
+
category: 'performance',
|
|
1476
|
+
title: 'Missing preconnect hints for third-party origins',
|
|
1477
|
+
description: 'Third-party origins used without preconnect hints.',
|
|
1478
|
+
impact: 'Connection setup to third-party origins adds latency to resource loading.',
|
|
1479
|
+
howToFix: 'Add preconnect hints for critical third-party origins.',
|
|
1480
|
+
},
|
|
1481
|
+
GOOGLE_FONTS_NO_PRECONNECT: {
|
|
1482
|
+
code: 'GOOGLE_FONTS_NO_PRECONNECT',
|
|
1483
|
+
severity: 'warning',
|
|
1484
|
+
category: 'performance',
|
|
1485
|
+
title: 'Google Fonts without preconnect',
|
|
1486
|
+
description: 'Google Fonts are used but preconnect hints are missing.',
|
|
1487
|
+
impact: 'Adds significant latency to font loading, affecting LCP.',
|
|
1488
|
+
howToFix: 'Add preconnect hints for fonts.googleapis.com and fonts.gstatic.com.',
|
|
1489
|
+
},
|
|
1490
|
+
HERO_IMAGE_NOT_PRELOADED: {
|
|
1491
|
+
code: 'HERO_IMAGE_NOT_PRELOADED',
|
|
1492
|
+
severity: 'notice',
|
|
1493
|
+
category: 'performance',
|
|
1494
|
+
title: 'Hero image not preloaded',
|
|
1495
|
+
description: 'Page has images but none are preloaded for faster LCP.',
|
|
1496
|
+
impact: 'Preloading LCP images can improve Largest Contentful Paint.',
|
|
1497
|
+
howToFix: 'Add <link rel="preload" href="hero-image.jpg" as="image"> for your LCP image.',
|
|
1498
|
+
},
|
|
1499
|
+
NO_CRITICAL_RESOURCE_PRELOAD: {
|
|
1500
|
+
code: 'NO_CRITICAL_RESOURCE_PRELOAD',
|
|
1501
|
+
severity: 'notice',
|
|
1502
|
+
category: 'performance',
|
|
1503
|
+
title: 'No critical resources preloaded',
|
|
1504
|
+
description: 'Page has render-blocking resources but no preload hints.',
|
|
1505
|
+
impact: 'Preloading critical resources can improve initial render time.',
|
|
1506
|
+
howToFix: 'Consider preloading critical CSS, fonts, or hero images.',
|
|
1507
|
+
},
|
|
1508
|
+
EXCESSIVE_PRELOADS: {
|
|
1509
|
+
code: 'EXCESSIVE_PRELOADS',
|
|
1510
|
+
severity: 'warning',
|
|
1511
|
+
category: 'performance',
|
|
1512
|
+
title: 'Too many preload hints',
|
|
1513
|
+
description: 'Page has more than 10 preload hints.',
|
|
1514
|
+
impact: 'Preloading too many resources competes for bandwidth and can delay critical resources.',
|
|
1515
|
+
howToFix: 'Limit preloads to truly critical resources (2-5 maximum).',
|
|
1516
|
+
},
|
|
1517
|
+
ES_MODULES_NO_MODULEPRELOAD: {
|
|
1518
|
+
code: 'ES_MODULES_NO_MODULEPRELOAD',
|
|
1519
|
+
severity: 'notice',
|
|
1520
|
+
category: 'performance',
|
|
1521
|
+
title: 'ES modules without modulepreload',
|
|
1522
|
+
description: 'Page uses ES modules but no modulepreload hints are present.',
|
|
1523
|
+
impact: 'Modulepreload can parallelize module loading for faster execution.',
|
|
1524
|
+
howToFix: 'Add <link rel="modulepreload" href="critical-module.js"> for critical modules.',
|
|
1525
|
+
},
|
|
1526
|
+
|
|
1527
|
+
// E-E-A-T Signals
|
|
1528
|
+
YMYL_NO_AUTHOR: {
|
|
1529
|
+
code: 'YMYL_NO_AUTHOR',
|
|
1530
|
+
severity: 'warning',
|
|
1531
|
+
category: 'content',
|
|
1532
|
+
title: 'YMYL content without author information',
|
|
1533
|
+
description: 'This appears to be YMYL (Your Money Your Life) content but has no visible author.',
|
|
1534
|
+
impact: 'Google prioritizes E-E-A-T signals heavily for YMYL topics.',
|
|
1535
|
+
howToFix: 'Add author byline with credentials, bio, and author schema.',
|
|
1536
|
+
},
|
|
1537
|
+
YMYL_NO_EXPERTISE: {
|
|
1538
|
+
code: 'YMYL_NO_EXPERTISE',
|
|
1539
|
+
severity: 'warning',
|
|
1540
|
+
category: 'content',
|
|
1541
|
+
title: 'YMYL content without expertise signals',
|
|
1542
|
+
description: 'YMYL content lacks visible expertise indicators (credentials, qualifications).',
|
|
1543
|
+
impact: 'Expertise is crucial for YMYL content ranking.',
|
|
1544
|
+
howToFix: 'Include author credentials, "reviewed by" statements, or cite expert sources.',
|
|
1545
|
+
},
|
|
1546
|
+
YMYL_NO_CITATIONS: {
|
|
1547
|
+
code: 'YMYL_NO_CITATIONS',
|
|
1548
|
+
severity: 'warning',
|
|
1549
|
+
category: 'content',
|
|
1550
|
+
title: 'YMYL content without authoritative citations',
|
|
1551
|
+
description: 'YMYL content has no links to authoritative sources (.gov, .edu, etc.).',
|
|
1552
|
+
impact: 'Citing authoritative sources builds trust for sensitive topics.',
|
|
1553
|
+
howToFix: 'Add citations to government, academic, or recognized industry sources.',
|
|
1554
|
+
},
|
|
1555
|
+
NO_ENTITY_IDENTIFIED: {
|
|
1556
|
+
code: 'NO_ENTITY_IDENTIFIED',
|
|
1557
|
+
severity: 'notice',
|
|
1558
|
+
category: 'content',
|
|
1559
|
+
title: 'No author or organization identified',
|
|
1560
|
+
description: 'Page lacks clear attribution to an author or organization.',
|
|
1561
|
+
impact: 'Anonymous content may be seen as less trustworthy.',
|
|
1562
|
+
howToFix: 'Add author byline or organization information with structured data.',
|
|
1563
|
+
},
|
|
1564
|
+
NO_ORGANIZATION_SCHEMA: {
|
|
1565
|
+
code: 'NO_ORGANIZATION_SCHEMA',
|
|
1566
|
+
severity: 'notice',
|
|
1567
|
+
category: 'structured-data',
|
|
1568
|
+
title: 'Missing Organization schema',
|
|
1569
|
+
description: 'No Organization structured data found.',
|
|
1570
|
+
impact: 'Missed opportunity to establish organizational identity for E-E-A-T.',
|
|
1571
|
+
howToFix: 'Add Organization schema with name, logo, contact, and social profiles.',
|
|
1572
|
+
},
|
|
1573
|
+
AUTHOR_NO_SCHEMA: {
|
|
1574
|
+
code: 'AUTHOR_NO_SCHEMA',
|
|
1575
|
+
severity: 'notice',
|
|
1576
|
+
category: 'structured-data',
|
|
1577
|
+
title: 'Author without Person schema',
|
|
1578
|
+
description: 'Author name is present but no Person structured data.',
|
|
1579
|
+
impact: 'Person schema helps Google understand author entity relationships.',
|
|
1580
|
+
howToFix: 'Add Person schema with sameAs links to author profiles.',
|
|
1581
|
+
},
|
|
1582
|
+
NO_MODIFIED_DATE: {
|
|
1583
|
+
code: 'NO_MODIFIED_DATE',
|
|
1584
|
+
severity: 'notice',
|
|
1585
|
+
category: 'content',
|
|
1586
|
+
title: 'No content modification date',
|
|
1587
|
+
description: 'Content has published date but no last-modified date.',
|
|
1588
|
+
impact: 'Modified dates signal content freshness to search engines.',
|
|
1589
|
+
howToFix: 'Add article:modified_time meta tag or dateModified in schema.',
|
|
1590
|
+
},
|
|
1591
|
+
NO_EXPERIENCE_SIGNALS: {
|
|
1592
|
+
code: 'NO_EXPERIENCE_SIGNALS',
|
|
1593
|
+
severity: 'notice',
|
|
1594
|
+
category: 'content',
|
|
1595
|
+
title: 'No first-hand experience signals detected',
|
|
1596
|
+
description: 'Content lacks indicators of personal/first-hand experience.',
|
|
1597
|
+
impact: 'The "Experience" in E-E-A-T values authentic, first-hand knowledge.',
|
|
1598
|
+
howToFix: 'Add personal insights, original photos, or hands-on testing details.',
|
|
1599
|
+
},
|
|
1600
|
+
|
|
1601
|
+
// IndexNow
|
|
1602
|
+
INDEXNOW_NOT_IMPLEMENTED: {
|
|
1603
|
+
code: 'INDEXNOW_NOT_IMPLEMENTED',
|
|
1604
|
+
severity: 'notice',
|
|
1605
|
+
category: 'crawlability',
|
|
1606
|
+
title: 'IndexNow not implemented',
|
|
1607
|
+
description: 'Site does not appear to have IndexNow instant indexing set up.',
|
|
1608
|
+
impact: 'Content changes may take longer to appear in Bing, Yandex, and other search engines.',
|
|
1609
|
+
howToFix: 'Create a key at indexnow.org and implement the IndexNow protocol.',
|
|
1610
|
+
},
|
|
1611
|
+
INDEXNOW_INVALID_KEY: {
|
|
1612
|
+
code: 'INDEXNOW_INVALID_KEY',
|
|
1613
|
+
severity: 'warning',
|
|
1614
|
+
category: 'crawlability',
|
|
1615
|
+
title: 'IndexNow key format invalid',
|
|
1616
|
+
description: 'The IndexNow key does not match expected formats.',
|
|
1617
|
+
impact: 'IndexNow submissions may fail with invalid key format.',
|
|
1618
|
+
howToFix: 'Use a valid key format: 32-character hex or UUID format.',
|
|
1619
|
+
},
|
|
1620
|
+
|
|
1621
|
+
// Content Science (Novel Checks)
|
|
1622
|
+
ZIPF_KEYWORD_STUFFING: {
|
|
1623
|
+
code: 'ZIPF_KEYWORD_STUFFING',
|
|
1624
|
+
severity: 'warning',
|
|
1625
|
+
category: 'content',
|
|
1626
|
+
title: 'Unnatural keyword distribution detected',
|
|
1627
|
+
description: 'Content deviates from natural language patterns (Zipf\'s law). Possible keyword stuffing.',
|
|
1628
|
+
impact: 'Search engines can detect unnatural text patterns, potentially triggering spam filters.',
|
|
1629
|
+
howToFix: 'Reduce repetition of over-used keywords and use more natural language.',
|
|
1630
|
+
},
|
|
1631
|
+
LOW_CONTENT_ENTROPY: {
|
|
1632
|
+
code: 'LOW_CONTENT_ENTROPY',
|
|
1633
|
+
severity: 'warning',
|
|
1634
|
+
category: 'content',
|
|
1635
|
+
title: 'Low vocabulary diversity (thin content signal)',
|
|
1636
|
+
description: 'Content entropy is low, indicating repetitive or thin content.',
|
|
1637
|
+
impact: 'Low diversity content may be seen as low-quality by search engines.',
|
|
1638
|
+
howToFix: 'Expand vocabulary, add more unique insights, and reduce repetitive phrases.',
|
|
1639
|
+
},
|
|
1640
|
+
HIGH_CONTENT_REPETITION: {
|
|
1641
|
+
code: 'HIGH_CONTENT_REPETITION',
|
|
1642
|
+
severity: 'notice',
|
|
1643
|
+
category: 'content',
|
|
1644
|
+
title: 'High content repetition detected',
|
|
1645
|
+
description: 'Content shows high repetition pattern.',
|
|
1646
|
+
impact: 'Highly repetitive content provides less value and may hurt engagement.',
|
|
1647
|
+
howToFix: 'Vary your vocabulary and sentence structures. Avoid repeating the same phrases.',
|
|
1648
|
+
},
|
|
1649
|
+
BM25_KEYWORD_MISSING: {
|
|
1650
|
+
code: 'BM25_KEYWORD_MISSING',
|
|
1651
|
+
severity: 'warning',
|
|
1652
|
+
category: 'on-page',
|
|
1653
|
+
title: 'Target keyword not found',
|
|
1654
|
+
description: 'A target keyword is not present in your content at all.',
|
|
1655
|
+
impact: 'Page unlikely to rank for this keyword without any mention of it.',
|
|
1656
|
+
howToFix: 'Add natural mentions of the target keyword in your content.',
|
|
1657
|
+
},
|
|
1658
|
+
BM25_KEYWORD_SATURATED: {
|
|
1659
|
+
code: 'BM25_KEYWORD_SATURATED',
|
|
1660
|
+
severity: 'notice',
|
|
1661
|
+
category: 'on-page',
|
|
1662
|
+
title: 'Keyword over-optimized',
|
|
1663
|
+
description: 'Keyword found many times. Additional mentions provide diminishing returns.',
|
|
1664
|
+
impact: 'Over-optimization can trigger spam signals.',
|
|
1665
|
+
howToFix: 'Use semantic variations and related terms instead of exact repetition.',
|
|
1666
|
+
},
|
|
1667
|
+
|
|
1668
|
+
// Site Maturity
|
|
1669
|
+
NEW_SITE_STRATEGY: {
|
|
1670
|
+
code: 'NEW_SITE_STRATEGY',
|
|
1671
|
+
severity: 'notice',
|
|
1672
|
+
category: 'content',
|
|
1673
|
+
title: 'New website detected - Authority-first strategy recommended',
|
|
1674
|
+
description: 'Your site appears to be new (<90 days). Standard SEO advice won\'t work well.',
|
|
1675
|
+
impact: 'Creating lots of content without authority is wasted effort.',
|
|
1676
|
+
howToFix: 'Focus on building 10+ quality backlinks to ONE great page before expanding content.',
|
|
1677
|
+
},
|
|
1678
|
+
CONTENT_AUTHORITY_IMBALANCE: {
|
|
1679
|
+
code: 'CONTENT_AUTHORITY_IMBALANCE',
|
|
1680
|
+
severity: 'warning',
|
|
1681
|
+
category: 'content',
|
|
1682
|
+
title: 'Content-to-authority imbalance',
|
|
1683
|
+
description: 'Site has many pages but limited backlink signals. Content is unlikely to rank.',
|
|
1684
|
+
impact: 'Most of your content may be invisible to search engines due to low domain authority.',
|
|
1685
|
+
howToFix: 'Stop publishing new content. Focus 100% on link building until you see ranking improvements.',
|
|
1686
|
+
},
|
|
1687
|
+
GROWING_SITE_NO_BLOG: {
|
|
1688
|
+
code: 'GROWING_SITE_NO_BLOG',
|
|
1689
|
+
severity: 'notice',
|
|
1690
|
+
category: 'content',
|
|
1691
|
+
title: 'Growing site without blog/content hub',
|
|
1692
|
+
description: 'Site is in growth phase but lacks a blog or content section.',
|
|
1693
|
+
impact: 'Blog content is essential for attracting backlinks and building topical authority.',
|
|
1694
|
+
howToFix: 'Create a blog section with linkable content assets (guides, research, tools).',
|
|
1695
|
+
},
|
|
1696
|
+
|
|
1697
|
+
// Keyword Cannibalization
|
|
1698
|
+
INTERNAL_CANNIBALIZATION_SIGNAL: {
|
|
1699
|
+
code: 'INTERNAL_CANNIBALIZATION_SIGNAL',
|
|
1700
|
+
severity: 'warning',
|
|
1701
|
+
category: 'content',
|
|
1702
|
+
title: 'Duplicate H1 tags on page',
|
|
1703
|
+
description: 'Page has multiple H1 tags with duplicates, which can dilute keyword focus.',
|
|
1704
|
+
impact: 'Multiple similar H1s can confuse search engines about page topic.',
|
|
1705
|
+
howToFix: 'Use a single, unique H1 that clearly defines the page topic.',
|
|
1706
|
+
},
|
|
1707
|
+
TITLE_H1_KEYWORD_MISMATCH: {
|
|
1708
|
+
code: 'TITLE_H1_KEYWORD_MISMATCH',
|
|
1709
|
+
severity: 'warning',
|
|
1710
|
+
category: 'on-page',
|
|
1711
|
+
title: 'Title and H1 target different keywords',
|
|
1712
|
+
description: 'The title tag and H1 heading have no overlapping keywords.',
|
|
1713
|
+
impact: 'Search engines may be confused about the primary topic of the page.',
|
|
1714
|
+
howToFix: 'Align title and H1 to target the same primary keyword or topic.',
|
|
1715
|
+
},
|
|
1716
|
+
|
|
1717
|
+
// ==================== ASSET MINIFICATION ====================
|
|
1718
|
+
CSS_NOT_MINIFIED: {
|
|
1719
|
+
code: 'CSS_NOT_MINIFIED',
|
|
1720
|
+
severity: 'warning',
|
|
1721
|
+
category: 'performance',
|
|
1722
|
+
title: 'CSS files not minified',
|
|
1723
|
+
description: 'CSS files contain unnecessary whitespace, comments, or unoptimized code.',
|
|
1724
|
+
impact: 'Unminified CSS increases file size and page load time.',
|
|
1725
|
+
howToFix: 'Use a CSS minifier (cssnano, clean-css) or ensure build tools minify CSS in production.',
|
|
1726
|
+
},
|
|
1727
|
+
JS_NOT_MINIFIED: {
|
|
1728
|
+
code: 'JS_NOT_MINIFIED',
|
|
1729
|
+
severity: 'warning',
|
|
1730
|
+
category: 'performance',
|
|
1731
|
+
title: 'JavaScript files not minified',
|
|
1732
|
+
description: 'JavaScript files contain unnecessary whitespace, comments, or long variable names.',
|
|
1733
|
+
impact: 'Unminified JavaScript increases file size, parse time, and page load time.',
|
|
1734
|
+
howToFix: 'Use a JS minifier (Terser, UglifyJS) or ensure build tools minify JS in production.',
|
|
1735
|
+
},
|
|
1736
|
+
|
|
1737
|
+
// ==================== PAGE RESOURCES ====================
|
|
1738
|
+
PAGE_RESOURCES_EXCESSIVE: {
|
|
1739
|
+
code: 'PAGE_RESOURCES_EXCESSIVE',
|
|
1740
|
+
severity: 'error',
|
|
1741
|
+
category: 'performance',
|
|
1742
|
+
title: 'Excessive number of page resources',
|
|
1743
|
+
description: 'Page makes over 100 HTTP requests for resources.',
|
|
1744
|
+
impact: 'Too many HTTP requests cause slow page loads due to connection overhead and browser limits.',
|
|
1745
|
+
howToFix: 'Combine CSS/JS files, use image sprites, lazy-load resources, remove unused dependencies.',
|
|
1746
|
+
},
|
|
1747
|
+
PAGE_RESOURCES_HIGH: {
|
|
1748
|
+
code: 'PAGE_RESOURCES_HIGH',
|
|
1749
|
+
severity: 'warning',
|
|
1750
|
+
category: 'performance',
|
|
1751
|
+
title: 'High number of page resources',
|
|
1752
|
+
description: 'Page makes over 50 HTTP requests for resources.',
|
|
1753
|
+
impact: 'Many HTTP requests increase page load time, especially on mobile networks.',
|
|
1754
|
+
howToFix: 'Bundle CSS/JS files, lazy-load images below the fold, consider critical CSS inlining.',
|
|
1755
|
+
},
|
|
1756
|
+
PAGE_RESOURCES_THIRD_PARTY_HIGH: {
|
|
1757
|
+
code: 'PAGE_RESOURCES_THIRD_PARTY_HIGH',
|
|
1758
|
+
severity: 'warning',
|
|
1759
|
+
category: 'performance',
|
|
1760
|
+
title: 'Many third-party resources',
|
|
1761
|
+
description: 'Page loads many resources from third-party domains.',
|
|
1762
|
+
impact: 'Third-party resources add latency and are outside your control.',
|
|
1763
|
+
howToFix: 'Self-host critical third-party resources when possible. Use preconnect hints for remaining.',
|
|
1764
|
+
},
|
|
1765
|
+
PAGE_RESOURCES_MANY_IFRAMES: {
|
|
1766
|
+
code: 'PAGE_RESOURCES_MANY_IFRAMES',
|
|
1767
|
+
severity: 'warning',
|
|
1768
|
+
category: 'performance',
|
|
1769
|
+
title: 'Multiple iframes detected',
|
|
1770
|
+
description: 'Page contains multiple iframes, each loading its own document.',
|
|
1771
|
+
impact: 'Iframes significantly increase page weight and can block the main thread.',
|
|
1772
|
+
howToFix: 'Lazy-load iframes below the fold. Consider native embeds or facade patterns.',
|
|
1773
|
+
},
|
|
1774
|
+
|
|
1775
|
+
// ==================== RESPONSIVE CSS ====================
|
|
1776
|
+
RESPONSIVE_NO_VIEWPORT: {
|
|
1777
|
+
code: 'RESPONSIVE_NO_VIEWPORT',
|
|
1778
|
+
severity: 'error',
|
|
1779
|
+
category: 'mobile',
|
|
1780
|
+
title: 'Missing viewport meta tag',
|
|
1781
|
+
description: 'No viewport meta tag found for responsive design.',
|
|
1782
|
+
impact: 'Without viewport meta, mobile browsers render at desktop width and scale down.',
|
|
1783
|
+
howToFix: 'Add <meta name="viewport" content="width=device-width, initial-scale=1"> to <head>.',
|
|
1784
|
+
},
|
|
1785
|
+
RESPONSIVE_NO_MEDIA_QUERIES: {
|
|
1786
|
+
code: 'RESPONSIVE_NO_MEDIA_QUERIES',
|
|
1787
|
+
severity: 'warning',
|
|
1788
|
+
category: 'mobile',
|
|
1789
|
+
title: 'No CSS media queries detected',
|
|
1790
|
+
description: 'No @media queries found in stylesheets.',
|
|
1791
|
+
impact: 'Without media queries, the site uses fixed layouts that do not adapt to screen sizes.',
|
|
1792
|
+
howToFix: 'Add CSS media queries for different screen sizes. Use mobile-first approach.',
|
|
1793
|
+
},
|
|
1794
|
+
RESPONSIVE_NO_MOBILE_BREAKPOINT: {
|
|
1795
|
+
code: 'RESPONSIVE_NO_MOBILE_BREAKPOINT',
|
|
1796
|
+
severity: 'warning',
|
|
1797
|
+
category: 'mobile',
|
|
1798
|
+
title: 'No mobile breakpoints detected',
|
|
1799
|
+
description: 'Media queries exist but none target mobile screen sizes.',
|
|
1800
|
+
impact: 'Site may not be optimized for mobile devices, hurting majority of traffic.',
|
|
1801
|
+
howToFix: 'Add media queries for mobile: @media (max-width: 768px) { ... }',
|
|
1802
|
+
},
|
|
1803
|
+
|
|
1804
|
+
// ==================== LINKS RATIO ====================
|
|
1805
|
+
LINKS_NO_INTERNAL: {
|
|
1806
|
+
code: 'LINKS_NO_INTERNAL',
|
|
1807
|
+
severity: 'warning',
|
|
1808
|
+
category: 'links',
|
|
1809
|
+
title: 'No internal links found',
|
|
1810
|
+
description: 'Page has no internal links to other pages on the site.',
|
|
1811
|
+
impact: 'Internal links help search engines discover pages and distribute link equity.',
|
|
1812
|
+
howToFix: 'Add relevant internal links to related content or navigation.',
|
|
1813
|
+
},
|
|
1814
|
+
LINKS_NO_EXTERNAL: {
|
|
1815
|
+
code: 'LINKS_NO_EXTERNAL',
|
|
1816
|
+
severity: 'notice',
|
|
1817
|
+
category: 'links',
|
|
1818
|
+
title: 'No external links found',
|
|
1819
|
+
description: 'Page has no links to external websites.',
|
|
1820
|
+
impact: 'Linking to authoritative sources improves credibility and content context.',
|
|
1821
|
+
howToFix: 'Add links to high-quality external sources that support your content.',
|
|
1822
|
+
},
|
|
1823
|
+
LINKS_RATIO_POOR: {
|
|
1824
|
+
code: 'LINKS_RATIO_POOR',
|
|
1825
|
+
severity: 'warning',
|
|
1826
|
+
category: 'links',
|
|
1827
|
+
title: 'Poor internal-to-external link ratio',
|
|
1828
|
+
description: 'Page has more external links than internal links.',
|
|
1829
|
+
impact: 'Too many external links can dilute page authority and may look spammy.',
|
|
1830
|
+
howToFix: 'Add more internal links. Aim for at least 2 internal links per external link.',
|
|
1831
|
+
},
|
|
1832
|
+
|
|
1833
|
+
// ==================== URL SAFETY (Local Hash Database) ====================
|
|
1834
|
+
URL_SAFETY_KNOWN_THREAT: {
|
|
1835
|
+
code: 'URL_SAFETY_KNOWN_THREAT',
|
|
1836
|
+
severity: 'error',
|
|
1837
|
+
category: 'security',
|
|
1838
|
+
title: 'Website URL matches known threat database',
|
|
1839
|
+
description: 'Your website URL matches entries in the local threat database.',
|
|
1840
|
+
impact: 'Browsers and security tools may block access to your site. Search engines may remove you from results.',
|
|
1841
|
+
howToFix: 'Scan your site for malware, remove any malicious content, and request removal from threat databases.',
|
|
1842
|
+
},
|
|
1843
|
+
URL_SAFETY_EXTERNAL_THREAT: {
|
|
1844
|
+
code: 'URL_SAFETY_EXTERNAL_THREAT',
|
|
1845
|
+
severity: 'error',
|
|
1846
|
+
category: 'security',
|
|
1847
|
+
title: 'External links to known malicious URLs',
|
|
1848
|
+
description: 'External links point to URLs flagged in threat databases.',
|
|
1849
|
+
impact: 'Linking to malicious sites harms visitors and damages your reputation and rankings.',
|
|
1850
|
+
howToFix: 'Remove all links to flagged URLs immediately.',
|
|
1851
|
+
},
|
|
1852
|
+
URL_SAFETY_SUSPICIOUS_DOMAIN: {
|
|
1853
|
+
code: 'URL_SAFETY_SUSPICIOUS_DOMAIN',
|
|
1854
|
+
severity: 'warning',
|
|
1855
|
+
category: 'security',
|
|
1856
|
+
title: 'Website URL has suspicious characteristics',
|
|
1857
|
+
description: 'Your URL shows patterns commonly associated with phishing or malicious sites.',
|
|
1858
|
+
impact: 'Users and security tools may distrust your site.',
|
|
1859
|
+
howToFix: 'Use a trustworthy domain structure. Avoid patterns that mimic other brands or use suspicious TLDs.',
|
|
1860
|
+
},
|
|
1861
|
+
URL_SAFETY_SUSPICIOUS_EXTERNAL: {
|
|
1862
|
+
code: 'URL_SAFETY_SUSPICIOUS_EXTERNAL',
|
|
1863
|
+
severity: 'warning',
|
|
1864
|
+
category: 'security',
|
|
1865
|
+
title: 'External links with suspicious characteristics',
|
|
1866
|
+
description: 'External links show patterns associated with phishing or malicious sites.',
|
|
1867
|
+
impact: 'Linking to suspicious sites can harm visitors and rankings.',
|
|
1868
|
+
howToFix: 'Review and remove or replace suspicious external links.',
|
|
1869
|
+
},
|
|
1870
|
+
URL_SAFETY_SHORTENERS: {
|
|
1871
|
+
code: 'URL_SAFETY_SHORTENERS',
|
|
1872
|
+
severity: 'notice',
|
|
1873
|
+
category: 'security',
|
|
1874
|
+
title: 'External links use URL shorteners',
|
|
1875
|
+
description: 'External links use URL shorteners, hiding the actual destination.',
|
|
1876
|
+
impact: 'URL shorteners reduce trust and SEO link value.',
|
|
1877
|
+
howToFix: 'Replace shortened URLs with direct links to the destination.',
|
|
1878
|
+
},
|
|
1879
|
+
|
|
1880
|
+
// ==================== FRAMEWORK-SPECIFIC SEO ====================
|
|
1881
|
+
REACT_CSR_DETECTED: {
|
|
1882
|
+
code: 'REACT_CSR_DETECTED',
|
|
1883
|
+
severity: 'error',
|
|
1884
|
+
category: 'framework',
|
|
1885
|
+
title: 'Client-side rendered React app detected',
|
|
1886
|
+
description: 'React apps without SSR send empty HTML to crawlers. Search engines may not index your content.',
|
|
1887
|
+
impact: 'Critical - your content is likely invisible to Google.',
|
|
1888
|
+
howToFix: 'Migrate to Next.js for automatic SSR/SSG, add prerendering with react-snap, or use react-helmet-async with a prerender service.',
|
|
1889
|
+
},
|
|
1890
|
+
NEXTJS_NO_METADATA_API: {
|
|
1891
|
+
code: 'NEXTJS_NO_METADATA_API',
|
|
1892
|
+
severity: 'warning',
|
|
1893
|
+
category: 'framework',
|
|
1894
|
+
title: 'Not using Next.js Metadata API',
|
|
1895
|
+
description: 'Next.js 13+ has a built-in Metadata API that is more reliable than third-party solutions.',
|
|
1896
|
+
impact: 'Missing out on Next.js optimized SEO features.',
|
|
1897
|
+
howToFix: 'Use export const metadata or generateMetadata in your page/layout files.',
|
|
1898
|
+
},
|
|
1899
|
+
VUE_CSR_DETECTED: {
|
|
1900
|
+
code: 'VUE_CSR_DETECTED',
|
|
1901
|
+
severity: 'error',
|
|
1902
|
+
category: 'framework',
|
|
1903
|
+
title: 'Client-side rendered Vue app detected',
|
|
1904
|
+
description: 'Vue apps without SSR send empty HTML to crawlers.',
|
|
1905
|
+
impact: 'Your content may not be indexed by search engines.',
|
|
1906
|
+
howToFix: 'Migrate to Nuxt for automatic SSR/SSG, or add SSR with @vue/server-renderer.',
|
|
1907
|
+
},
|
|
1908
|
+
ANGULAR_NO_UNIVERSAL: {
|
|
1909
|
+
code: 'ANGULAR_NO_UNIVERSAL',
|
|
1910
|
+
severity: 'error',
|
|
1911
|
+
category: 'framework',
|
|
1912
|
+
title: 'Angular app without Universal (SSR)',
|
|
1913
|
+
description: 'Client-side Angular apps send empty HTML to crawlers.',
|
|
1914
|
+
impact: 'Your content may not be indexed by search engines.',
|
|
1915
|
+
howToFix: 'Add Angular Universal for SSR: ng add @angular/ssr',
|
|
1916
|
+
},
|
|
1917
|
+
WORDPRESS_NO_SEO_PLUGIN: {
|
|
1918
|
+
code: 'WORDPRESS_NO_SEO_PLUGIN',
|
|
1919
|
+
severity: 'warning',
|
|
1920
|
+
category: 'framework',
|
|
1921
|
+
title: 'No SEO plugin detected',
|
|
1922
|
+
description: 'WordPress benefits greatly from an SEO plugin like Yoast or Rank Math.',
|
|
1923
|
+
impact: 'Missing automated SEO features like sitemaps, schema, and meta tag management.',
|
|
1924
|
+
howToFix: 'Install Yoast SEO or Rank Math plugin.',
|
|
1925
|
+
},
|
|
1926
|
+
WORDPRESS_NO_CACHING: {
|
|
1927
|
+
code: 'WORDPRESS_NO_CACHING',
|
|
1928
|
+
severity: 'warning',
|
|
1929
|
+
category: 'framework',
|
|
1930
|
+
title: 'No caching plugin detected',
|
|
1931
|
+
description: 'WordPress without caching is slow, hurting Core Web Vitals.',
|
|
1932
|
+
impact: 'Poor performance affects user experience and rankings.',
|
|
1933
|
+
howToFix: 'Install WP Super Cache, W3 Total Cache, or LiteSpeed Cache.',
|
|
1934
|
+
},
|
|
1935
|
+
SPA_EMPTY_BODY: {
|
|
1936
|
+
code: 'SPA_EMPTY_BODY',
|
|
1937
|
+
severity: 'error',
|
|
1938
|
+
category: 'framework',
|
|
1939
|
+
title: 'Single Page App with empty body',
|
|
1940
|
+
description: 'Page body contains minimal content, suggesting client-side rendering without SSR.',
|
|
1941
|
+
impact: 'Search engines see an empty page and cannot index your content.',
|
|
1942
|
+
howToFix: 'Implement server-side rendering, static site generation, or prerendering.',
|
|
1943
|
+
},
|
|
1944
|
+
FRAMEWORK_OUTDATED: {
|
|
1945
|
+
code: 'FRAMEWORK_OUTDATED',
|
|
1946
|
+
severity: 'warning',
|
|
1947
|
+
category: 'framework',
|
|
1948
|
+
title: 'Outdated framework version detected',
|
|
1949
|
+
description: 'Running an outdated version of your framework may have security vulnerabilities and SEO bugs.',
|
|
1950
|
+
impact: 'Potential security issues and missing SEO improvements.',
|
|
1951
|
+
howToFix: 'Update to the latest stable version of your framework.',
|
|
1952
|
+
},
|
|
1953
|
+
};
|