recker 1.0.27 → 1.0.28-next.3bf98c7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/scrape/extractors.js +2 -1
- package/dist/browser/scrape/types.d.ts +2 -1
- package/dist/cli/index.js +142 -3
- package/dist/cli/tui/shell.d.ts +2 -0
- package/dist/cli/tui/shell.js +492 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/scrape/extractors.js +2 -1
- package/dist/scrape/index.d.ts +2 -0
- package/dist/scrape/index.js +1 -0
- package/dist/scrape/spider.d.ts +61 -0
- package/dist/scrape/spider.js +250 -0
- package/dist/scrape/types.d.ts +2 -1
- package/dist/seo/analyzer.d.ts +42 -0
- package/dist/seo/analyzer.js +742 -0
- package/dist/seo/index.d.ts +7 -0
- package/dist/seo/index.js +3 -0
- package/dist/seo/rules/accessibility.d.ts +2 -0
- package/dist/seo/rules/accessibility.js +694 -0
- package/dist/seo/rules/best-practices.d.ts +2 -0
- package/dist/seo/rules/best-practices.js +188 -0
- package/dist/seo/rules/content.d.ts +2 -0
- package/dist/seo/rules/content.js +236 -0
- package/dist/seo/rules/crawl.d.ts +2 -0
- package/dist/seo/rules/crawl.js +307 -0
- package/dist/seo/rules/cwv.d.ts +2 -0
- package/dist/seo/rules/cwv.js +337 -0
- package/dist/seo/rules/ecommerce.d.ts +2 -0
- package/dist/seo/rules/ecommerce.js +252 -0
- package/dist/seo/rules/i18n.d.ts +2 -0
- package/dist/seo/rules/i18n.js +222 -0
- package/dist/seo/rules/images.d.ts +2 -0
- package/dist/seo/rules/images.js +180 -0
- package/dist/seo/rules/index.d.ts +52 -0
- package/dist/seo/rules/index.js +143 -0
- package/dist/seo/rules/internal-linking.d.ts +2 -0
- package/dist/seo/rules/internal-linking.js +375 -0
- package/dist/seo/rules/links.d.ts +2 -0
- package/dist/seo/rules/links.js +150 -0
- package/dist/seo/rules/local.d.ts +2 -0
- package/dist/seo/rules/local.js +265 -0
- package/dist/seo/rules/meta.d.ts +2 -0
- package/dist/seo/rules/meta.js +523 -0
- package/dist/seo/rules/mobile.d.ts +2 -0
- package/dist/seo/rules/mobile.js +71 -0
- package/dist/seo/rules/performance.d.ts +2 -0
- package/dist/seo/rules/performance.js +246 -0
- package/dist/seo/rules/pwa.d.ts +2 -0
- package/dist/seo/rules/pwa.js +302 -0
- package/dist/seo/rules/readability.d.ts +2 -0
- package/dist/seo/rules/readability.js +255 -0
- package/dist/seo/rules/schema.d.ts +2 -0
- package/dist/seo/rules/schema.js +54 -0
- package/dist/seo/rules/security.d.ts +2 -0
- package/dist/seo/rules/security.js +525 -0
- package/dist/seo/rules/social.d.ts +2 -0
- package/dist/seo/rules/social.js +373 -0
- package/dist/seo/rules/structural.d.ts +2 -0
- package/dist/seo/rules/structural.js +155 -0
- package/dist/seo/rules/technical.d.ts +2 -0
- package/dist/seo/rules/technical.js +223 -0
- package/dist/seo/rules/thresholds.d.ts +196 -0
- package/dist/seo/rules/thresholds.js +118 -0
- package/dist/seo/rules/types.d.ts +346 -0
- package/dist/seo/rules/types.js +11 -0
- package/dist/seo/seo-spider.d.ts +47 -0
- package/dist/seo/seo-spider.js +362 -0
- package/dist/seo/types.d.ts +184 -0
- package/dist/seo/types.js +1 -0
- package/dist/utils/columns.d.ts +14 -0
- package/dist/utils/columns.js +69 -0
- package/package.json +1 -1
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { createResult } from './types.js';
|
|
2
|
+
import { SEO_THRESHOLDS } from './thresholds.js';
|
|
3
|
+
export const technicalRules = [
|
|
4
|
+
{
|
|
5
|
+
id: 'canonical-exists',
|
|
6
|
+
name: 'Canonical URL',
|
|
7
|
+
category: 'technical',
|
|
8
|
+
severity: 'warning',
|
|
9
|
+
description: 'Page should have a canonical URL',
|
|
10
|
+
check: (ctx) => {
|
|
11
|
+
if (!ctx.hasCanonical) {
|
|
12
|
+
return createResult({ id: 'canonical-exists', name: 'Canonical URL', category: 'technical', severity: 'warning' }, 'warn', 'No canonical URL defined', { recommendation: 'Add <link rel="canonical" href="..."> to prevent duplicate content' });
|
|
13
|
+
}
|
|
14
|
+
return createResult({ id: 'canonical-exists', name: 'Canonical URL', category: 'technical', severity: 'warning' }, 'pass', 'Canonical URL is defined', { value: ctx.canonicalUrl });
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 'lang-exists',
|
|
19
|
+
name: 'Language',
|
|
20
|
+
category: 'technical',
|
|
21
|
+
severity: 'warning',
|
|
22
|
+
description: 'HTML should have lang attribute',
|
|
23
|
+
check: (ctx) => {
|
|
24
|
+
if (!ctx.hasLang) {
|
|
25
|
+
return createResult({ id: 'lang-exists', name: 'Language', category: 'technical', severity: 'warning' }, 'warn', 'Missing lang attribute on <html>', { recommendation: 'Add lang attribute: <html lang="en">' });
|
|
26
|
+
}
|
|
27
|
+
return createResult({ id: 'lang-exists', name: 'Language', category: 'technical', severity: 'warning' }, 'pass', `Language attribute set (${ctx.langValue})`);
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'charset-exists',
|
|
32
|
+
name: 'Charset',
|
|
33
|
+
category: 'technical',
|
|
34
|
+
severity: 'warning',
|
|
35
|
+
description: 'Page should declare character encoding',
|
|
36
|
+
check: (ctx) => {
|
|
37
|
+
if (!ctx.hasCharset) {
|
|
38
|
+
return createResult({ id: 'charset-exists', name: 'Charset', category: 'technical', severity: 'warning' }, 'warn', 'Missing charset declaration', { recommendation: 'Add <meta charset="UTF-8"> in <head>' });
|
|
39
|
+
}
|
|
40
|
+
if (ctx.charset && ctx.charset.toLowerCase() !== 'utf-8') {
|
|
41
|
+
return createResult({ id: 'charset-exists', name: 'Charset', category: 'technical', severity: 'warning' }, 'warn', `Non-UTF-8 charset: ${ctx.charset}`, { recommendation: 'Use UTF-8 charset for best compatibility' });
|
|
42
|
+
}
|
|
43
|
+
return createResult({ id: 'charset-exists', name: 'Charset', category: 'technical', severity: 'warning' }, 'pass', 'UTF-8 charset declared');
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'robots-noindex',
|
|
48
|
+
name: 'Robots Noindex',
|
|
49
|
+
category: 'technical',
|
|
50
|
+
severity: 'warning',
|
|
51
|
+
description: 'Check if page is set to noindex',
|
|
52
|
+
check: (ctx) => {
|
|
53
|
+
if (!ctx.metaRobots || ctx.metaRobots.length === 0)
|
|
54
|
+
return null;
|
|
55
|
+
if (ctx.metaRobots.includes('noindex')) {
|
|
56
|
+
return createResult({ id: 'robots-noindex', name: 'Robots', category: 'technical', severity: 'warning' }, 'warn', 'Page is set to noindex', { value: ctx.metaRobots.join(', '), recommendation: 'Remove noindex if you want the page to be indexed' });
|
|
57
|
+
}
|
|
58
|
+
return createResult({ id: 'robots-noindex', name: 'Robots', category: 'technical', severity: 'info' }, 'info', `Robots meta: ${ctx.metaRobots.join(', ')}`);
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'favicon-exists',
|
|
63
|
+
name: 'Favicon',
|
|
64
|
+
category: 'technical',
|
|
65
|
+
severity: 'warning',
|
|
66
|
+
description: 'Page should have a favicon defined',
|
|
67
|
+
check: (ctx) => {
|
|
68
|
+
if (!ctx.hasFavicon) {
|
|
69
|
+
return createResult({ id: 'favicon-exists', name: 'Favicon', category: 'technical', severity: 'warning' }, 'warn', 'No favicon defined', { recommendation: 'Add <link rel="icon" href="/favicon.ico"> for browser tab icon' });
|
|
70
|
+
}
|
|
71
|
+
return createResult({ id: 'favicon-exists', name: 'Favicon', category: 'technical', severity: 'warning' }, 'pass', 'Favicon is defined', { value: ctx.faviconUrl });
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: 'url-length',
|
|
76
|
+
name: 'URL Length',
|
|
77
|
+
category: 'technical',
|
|
78
|
+
severity: 'info',
|
|
79
|
+
description: 'URL should be under 75 characters',
|
|
80
|
+
check: (ctx) => {
|
|
81
|
+
if (!ctx.url)
|
|
82
|
+
return null;
|
|
83
|
+
const len = ctx.urlLength ?? ctx.url.length;
|
|
84
|
+
const max = SEO_THRESHOLDS.url.maxLength;
|
|
85
|
+
if (len > max) {
|
|
86
|
+
return createResult({ id: 'url-length', name: 'URL Length', category: 'technical', severity: 'info' }, 'info', `URL is long (${len} chars)`, { value: len, recommendation: `Keep URLs under ${max} characters when possible` });
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'url-lowercase',
|
|
93
|
+
name: 'URL Lowercase',
|
|
94
|
+
category: 'technical',
|
|
95
|
+
severity: 'warning',
|
|
96
|
+
description: 'URLs should be lowercase for consistency',
|
|
97
|
+
check: (ctx) => {
|
|
98
|
+
if (ctx.urlHasUppercase) {
|
|
99
|
+
return createResult({ id: 'url-lowercase', name: 'URL Lowercase', category: 'technical', severity: 'warning' }, 'warn', 'URL contains uppercase characters', { recommendation: 'Use lowercase URLs for better SEO consistency' });
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: 'url-clean',
|
|
106
|
+
name: 'URL Clean',
|
|
107
|
+
category: 'technical',
|
|
108
|
+
severity: 'warning',
|
|
109
|
+
description: 'URLs should not contain special characters or accents',
|
|
110
|
+
check: (ctx) => {
|
|
111
|
+
if (ctx.urlHasAccents || ctx.urlHasSpecialChars) {
|
|
112
|
+
const issues = [];
|
|
113
|
+
if (ctx.urlHasAccents)
|
|
114
|
+
issues.push('accents');
|
|
115
|
+
if (ctx.urlHasSpecialChars)
|
|
116
|
+
issues.push('special characters');
|
|
117
|
+
return createResult({ id: 'url-clean', name: 'URL Clean', category: 'technical', severity: 'warning' }, 'warn', `URL contains ${issues.join(' and ')}`, { recommendation: 'Use clean URLs without accents or special characters' });
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: 'url-no-params',
|
|
124
|
+
name: 'URL Parameters',
|
|
125
|
+
category: 'technical',
|
|
126
|
+
severity: 'warning',
|
|
127
|
+
description: 'URLs should not contain query parameters',
|
|
128
|
+
check: (ctx) => {
|
|
129
|
+
if (!ctx.url)
|
|
130
|
+
return null;
|
|
131
|
+
try {
|
|
132
|
+
const urlObj = new URL(ctx.url);
|
|
133
|
+
if (urlObj.search && urlObj.search.length > 1) {
|
|
134
|
+
return createResult({ id: 'url-no-params', name: 'URL Parameters', category: 'technical', severity: 'warning' }, 'warn', 'URL contains query parameters', { value: urlObj.search, recommendation: 'Use clean URLs (rewritten paths) without query parameters' });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: 'technical-meta-robots-directives',
|
|
144
|
+
name: 'Meta Robots Directives',
|
|
145
|
+
category: 'technical',
|
|
146
|
+
severity: 'warning',
|
|
147
|
+
description: 'Check for restrictive meta robots directives like noindex, nofollow, noarchive etc.',
|
|
148
|
+
check: (ctx) => {
|
|
149
|
+
if (!ctx.metaRobots || ctx.metaRobots.length === 0)
|
|
150
|
+
return null;
|
|
151
|
+
const restrictiveDirectives = ['noindex', 'nofollow', 'noarchive', 'nosnippet', 'noimageindex'];
|
|
152
|
+
const foundRestrictive = ctx.metaRobots.filter(directive => restrictiveDirectives.includes(directive));
|
|
153
|
+
if (foundRestrictive.length > 0) {
|
|
154
|
+
return createResult({ id: 'technical-meta-robots-directives', name: 'Meta Robots Directives', category: 'technical', severity: 'warning' }, 'warn', `Restrictive meta robots directives found: ${foundRestrictive.join(', ')}`, { recommendation: 'Ensure these directives are intentional to prevent unintended blocking of indexing or crawling.' });
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
id: 'technical-x-robots-tag',
|
|
161
|
+
name: 'X-Robots-Tag Header',
|
|
162
|
+
category: 'technical',
|
|
163
|
+
severity: 'info',
|
|
164
|
+
description: 'X-Robots-Tag header can be used to control indexing, especially for non-HTML content.',
|
|
165
|
+
check: (ctx) => {
|
|
166
|
+
if (!ctx.responseHeaders)
|
|
167
|
+
return null;
|
|
168
|
+
const xRobotsTag = ctx.responseHeaders['x-robots-tag'] || ctx.responseHeaders['X-Robots-Tag'];
|
|
169
|
+
if (xRobotsTag) {
|
|
170
|
+
return createResult({ id: 'technical-x-robots-tag', name: 'X-Robots-Tag Header', category: 'technical', severity: 'info' }, 'info', `X-Robots-Tag header found: ${xRobotsTag}`, { recommendation: 'Ensure X-Robots-Tag directives (e.g., noindex) are intentional.' });
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
id: 'technical-trust-signals',
|
|
177
|
+
name: 'Trust Signals (Links)',
|
|
178
|
+
category: 'technical',
|
|
179
|
+
severity: 'info',
|
|
180
|
+
description: 'Presence of links to "About", "Contact", "Privacy Policy", "Terms of Service" pages builds trust.',
|
|
181
|
+
check: (ctx) => {
|
|
182
|
+
const missingSignals = [];
|
|
183
|
+
if (!ctx.hasAboutPageLink)
|
|
184
|
+
missingSignals.push('About Page');
|
|
185
|
+
if (!ctx.hasContactPageLink)
|
|
186
|
+
missingSignals.push('Contact Page');
|
|
187
|
+
if (!ctx.hasPrivacyPolicyLink)
|
|
188
|
+
missingSignals.push('Privacy Policy');
|
|
189
|
+
if (!ctx.hasTermsOfServiceLink)
|
|
190
|
+
missingSignals.push('Terms of Service');
|
|
191
|
+
if (missingSignals.length > 0) {
|
|
192
|
+
return createResult({ id: 'technical-trust-signals', name: 'Trust Signals (Links)', category: 'technical', severity: 'info' }, 'info', `Missing links to key trust pages: ${missingSignals.join(', ')}`, { recommendation: 'Add clear links to "About Us", "Contact", "Privacy Policy", and "Terms of Service" pages to build user and search engine trust.' });
|
|
193
|
+
}
|
|
194
|
+
return null;
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: 'technical-text-html-ratio',
|
|
199
|
+
name: 'Text/HTML Ratio',
|
|
200
|
+
category: 'technical',
|
|
201
|
+
severity: 'info',
|
|
202
|
+
description: 'A higher text to HTML ratio indicates more content relative to code, which is good for SEO.',
|
|
203
|
+
check: (ctx) => {
|
|
204
|
+
if (ctx.textHtmlRatio === undefined)
|
|
205
|
+
return null;
|
|
206
|
+
const threshold = 15;
|
|
207
|
+
if (ctx.textHtmlRatio < threshold) {
|
|
208
|
+
return createResult({ id: 'technical-text-html-ratio', name: 'Text/HTML Ratio', category: 'technical', severity: 'info' }, 'warn', `Low Text/HTML ratio: ${ctx.textHtmlRatio.toFixed(2)}% (target > ${threshold}%)`, { recommendation: 'Increase text content relative to HTML code. Reduce unnecessary markup, or add more textual content.' });
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: 'technical-robots-txt-hint',
|
|
215
|
+
name: 'Robots.txt Hint',
|
|
216
|
+
category: 'technical',
|
|
217
|
+
severity: 'info',
|
|
218
|
+
description: 'Ensure a robots.txt file exists at the root of the domain to guide crawlers.',
|
|
219
|
+
check: (ctx) => {
|
|
220
|
+
return createResult({ id: 'technical-robots-txt-hint', name: 'Robots.txt Hint', category: 'technical', severity: 'info' }, 'info', 'Robots.txt existence cannot be verified from HTML alone.', { recommendation: 'Ensure a valid `robots.txt` file is present at your domain root (e.g., `https://example.com/robots.txt`) to guide search engine crawlers and define your sitemap location.' });
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
];
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
export declare const SEO_THRESHOLDS: {
|
|
2
|
+
readonly title: {
|
|
3
|
+
readonly min: 30;
|
|
4
|
+
readonly ideal: {
|
|
5
|
+
readonly min: 50;
|
|
6
|
+
readonly max: 60;
|
|
7
|
+
};
|
|
8
|
+
readonly max: 70;
|
|
9
|
+
};
|
|
10
|
+
readonly metaDescription: {
|
|
11
|
+
readonly min: 70;
|
|
12
|
+
readonly ideal: {
|
|
13
|
+
readonly min: 120;
|
|
14
|
+
readonly max: 155;
|
|
15
|
+
};
|
|
16
|
+
readonly max: 160;
|
|
17
|
+
};
|
|
18
|
+
readonly og: {
|
|
19
|
+
readonly title: {
|
|
20
|
+
readonly min: 30;
|
|
21
|
+
readonly ideal: {
|
|
22
|
+
readonly min: 40;
|
|
23
|
+
readonly max: 60;
|
|
24
|
+
};
|
|
25
|
+
readonly max: 90;
|
|
26
|
+
};
|
|
27
|
+
readonly description: {
|
|
28
|
+
readonly min: 55;
|
|
29
|
+
readonly ideal: {
|
|
30
|
+
readonly min: 110;
|
|
31
|
+
readonly max: 155;
|
|
32
|
+
};
|
|
33
|
+
readonly max: 200;
|
|
34
|
+
};
|
|
35
|
+
readonly image: {
|
|
36
|
+
readonly dimensions: {
|
|
37
|
+
readonly width: 1200;
|
|
38
|
+
readonly height: 630;
|
|
39
|
+
};
|
|
40
|
+
readonly minDimensions: {
|
|
41
|
+
readonly width: 600;
|
|
42
|
+
readonly height: 315;
|
|
43
|
+
};
|
|
44
|
+
readonly ratio: 1.91;
|
|
45
|
+
readonly maxSizeKb: 300;
|
|
46
|
+
readonly maxSizeKbAbsolute: 1000;
|
|
47
|
+
readonly formats: readonly ["jpg", "jpeg", "png", "webp"];
|
|
48
|
+
};
|
|
49
|
+
readonly meta: {
|
|
50
|
+
readonly maxUrlLength: 2000;
|
|
51
|
+
readonly maxDescriptionEmojis: 2;
|
|
52
|
+
readonly maxCapsPercentage: 30;
|
|
53
|
+
};
|
|
54
|
+
readonly fallbacks: {
|
|
55
|
+
readonly requireMetaTitle: true;
|
|
56
|
+
readonly requireMetaDescription: true;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
readonly twitter: {
|
|
60
|
+
readonly title: {
|
|
61
|
+
readonly min: 30;
|
|
62
|
+
readonly ideal: {
|
|
63
|
+
readonly min: 40;
|
|
64
|
+
readonly max: 55;
|
|
65
|
+
};
|
|
66
|
+
readonly max: 70;
|
|
67
|
+
};
|
|
68
|
+
readonly description: {
|
|
69
|
+
readonly min: 70;
|
|
70
|
+
readonly ideal: {
|
|
71
|
+
readonly min: 125;
|
|
72
|
+
readonly max: 200;
|
|
73
|
+
};
|
|
74
|
+
readonly max: 200;
|
|
75
|
+
};
|
|
76
|
+
readonly image: {
|
|
77
|
+
readonly summary: {
|
|
78
|
+
readonly min: {
|
|
79
|
+
readonly width: 144;
|
|
80
|
+
readonly height: 144;
|
|
81
|
+
};
|
|
82
|
+
readonly max: {
|
|
83
|
+
readonly width: 4096;
|
|
84
|
+
readonly height: 4096;
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
readonly summaryLarge: {
|
|
88
|
+
readonly min: {
|
|
89
|
+
readonly width: 300;
|
|
90
|
+
readonly height: 157;
|
|
91
|
+
};
|
|
92
|
+
readonly max: {
|
|
93
|
+
readonly width: 4096;
|
|
94
|
+
readonly height: 4096;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
readonly maxSizeMb: 5;
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
readonly headings: {
|
|
101
|
+
readonly h1: {
|
|
102
|
+
readonly count: 1;
|
|
103
|
+
readonly minLength: 20;
|
|
104
|
+
readonly maxLength: 70;
|
|
105
|
+
};
|
|
106
|
+
readonly h2: {
|
|
107
|
+
readonly min: 2;
|
|
108
|
+
readonly max: 8;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
readonly images: {
|
|
112
|
+
readonly alt: {
|
|
113
|
+
readonly minLength: 10;
|
|
114
|
+
readonly idealLength: {
|
|
115
|
+
readonly min: 80;
|
|
116
|
+
readonly max: 120;
|
|
117
|
+
};
|
|
118
|
+
readonly maxLength: 150;
|
|
119
|
+
};
|
|
120
|
+
readonly maxSizeKb: 100;
|
|
121
|
+
readonly maxSizeKbAcceptable: 500;
|
|
122
|
+
};
|
|
123
|
+
readonly links: {
|
|
124
|
+
readonly minInternal: 3;
|
|
125
|
+
readonly maxExternal: 100;
|
|
126
|
+
readonly genericTexts: readonly ["clique aqui", "click here", "saiba mais", "learn more", "read more", "leia mais", "here", "aqui"];
|
|
127
|
+
};
|
|
128
|
+
readonly content: {
|
|
129
|
+
readonly minWordsSimple: 150;
|
|
130
|
+
readonly minWordsRanking: 300;
|
|
131
|
+
readonly minWordsAuthority: 800;
|
|
132
|
+
readonly idealWordsAuthority: 2500;
|
|
133
|
+
readonly maxWordsPerSentence: 25;
|
|
134
|
+
readonly maxWordsPerParagraph: 90;
|
|
135
|
+
readonly minWordsPerParagraph: 40;
|
|
136
|
+
readonly minWordsPerH2Section: 150;
|
|
137
|
+
readonly minWordsPerH3Section: 50;
|
|
138
|
+
readonly imageWordRatio: {
|
|
139
|
+
readonly min: 200;
|
|
140
|
+
readonly max: 300;
|
|
141
|
+
};
|
|
142
|
+
readonly keywordDensity: {
|
|
143
|
+
readonly min: 0.8;
|
|
144
|
+
readonly max: 2;
|
|
145
|
+
};
|
|
146
|
+
readonly redundancyTolerance: 3;
|
|
147
|
+
};
|
|
148
|
+
readonly url: {
|
|
149
|
+
readonly maxLength: 75;
|
|
150
|
+
readonly maxLengthAbsolute: 2048;
|
|
151
|
+
};
|
|
152
|
+
readonly mobile: {
|
|
153
|
+
readonly minFontSize: 16;
|
|
154
|
+
readonly minTouchTarget: 48;
|
|
155
|
+
};
|
|
156
|
+
readonly thinContent: {
|
|
157
|
+
readonly minWords: 150;
|
|
158
|
+
readonly veryThinWords: 100;
|
|
159
|
+
};
|
|
160
|
+
readonly timing: {
|
|
161
|
+
readonly ttfb: {
|
|
162
|
+
readonly good: 200;
|
|
163
|
+
readonly needsImprovement: 500;
|
|
164
|
+
readonly poor: 600;
|
|
165
|
+
};
|
|
166
|
+
readonly total: {
|
|
167
|
+
readonly good: 1000;
|
|
168
|
+
readonly needsImprovement: 2500;
|
|
169
|
+
readonly poor: 4000;
|
|
170
|
+
};
|
|
171
|
+
readonly dnsLookup: {
|
|
172
|
+
readonly good: 50;
|
|
173
|
+
readonly poor: 200;
|
|
174
|
+
};
|
|
175
|
+
readonly tcpConnect: {
|
|
176
|
+
readonly good: 100;
|
|
177
|
+
readonly poor: 300;
|
|
178
|
+
};
|
|
179
|
+
readonly tlsHandshake: {
|
|
180
|
+
readonly good: 100;
|
|
181
|
+
readonly poor: 300;
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
readonly responseSize: {
|
|
185
|
+
readonly html: {
|
|
186
|
+
readonly good: number;
|
|
187
|
+
readonly warning: number;
|
|
188
|
+
readonly poor: number;
|
|
189
|
+
};
|
|
190
|
+
readonly total: {
|
|
191
|
+
readonly good: number;
|
|
192
|
+
readonly warning: number;
|
|
193
|
+
readonly poor: number;
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
export const SEO_THRESHOLDS = {
|
|
2
|
+
title: {
|
|
3
|
+
min: 30,
|
|
4
|
+
ideal: { min: 50, max: 60 },
|
|
5
|
+
max: 70,
|
|
6
|
+
},
|
|
7
|
+
metaDescription: {
|
|
8
|
+
min: 70,
|
|
9
|
+
ideal: { min: 120, max: 155 },
|
|
10
|
+
max: 160,
|
|
11
|
+
},
|
|
12
|
+
og: {
|
|
13
|
+
title: { min: 30, ideal: { min: 40, max: 60 }, max: 90 },
|
|
14
|
+
description: { min: 55, ideal: { min: 110, max: 155 }, max: 200 },
|
|
15
|
+
image: {
|
|
16
|
+
dimensions: { width: 1200, height: 630 },
|
|
17
|
+
minDimensions: { width: 600, height: 315 },
|
|
18
|
+
ratio: 1.91,
|
|
19
|
+
maxSizeKb: 300,
|
|
20
|
+
maxSizeKbAbsolute: 1000,
|
|
21
|
+
formats: ['jpg', 'jpeg', 'png', 'webp'],
|
|
22
|
+
},
|
|
23
|
+
meta: {
|
|
24
|
+
maxUrlLength: 2000,
|
|
25
|
+
maxDescriptionEmojis: 2,
|
|
26
|
+
maxCapsPercentage: 30,
|
|
27
|
+
},
|
|
28
|
+
fallbacks: {
|
|
29
|
+
requireMetaTitle: true,
|
|
30
|
+
requireMetaDescription: true,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
twitter: {
|
|
34
|
+
title: { min: 30, ideal: { min: 40, max: 55 }, max: 70 },
|
|
35
|
+
description: { min: 70, ideal: { min: 125, max: 200 }, max: 200 },
|
|
36
|
+
image: {
|
|
37
|
+
summary: { min: { width: 144, height: 144 }, max: { width: 4096, height: 4096 } },
|
|
38
|
+
summaryLarge: { min: { width: 300, height: 157 }, max: { width: 4096, height: 4096 } },
|
|
39
|
+
maxSizeMb: 5,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
headings: {
|
|
43
|
+
h1: { count: 1, minLength: 20, maxLength: 70 },
|
|
44
|
+
h2: { min: 2, max: 8 },
|
|
45
|
+
},
|
|
46
|
+
images: {
|
|
47
|
+
alt: { minLength: 10, idealLength: { min: 80, max: 120 }, maxLength: 150 },
|
|
48
|
+
maxSizeKb: 100,
|
|
49
|
+
maxSizeKbAcceptable: 500,
|
|
50
|
+
},
|
|
51
|
+
links: {
|
|
52
|
+
minInternal: 3,
|
|
53
|
+
maxExternal: 100,
|
|
54
|
+
genericTexts: ['clique aqui', 'click here', 'saiba mais', 'learn more', 'read more', 'leia mais', 'here', 'aqui'],
|
|
55
|
+
},
|
|
56
|
+
content: {
|
|
57
|
+
minWordsSimple: 150,
|
|
58
|
+
minWordsRanking: 300,
|
|
59
|
+
minWordsAuthority: 800,
|
|
60
|
+
idealWordsAuthority: 2500,
|
|
61
|
+
maxWordsPerSentence: 25,
|
|
62
|
+
maxWordsPerParagraph: 90,
|
|
63
|
+
minWordsPerParagraph: 40,
|
|
64
|
+
minWordsPerH2Section: 150,
|
|
65
|
+
minWordsPerH3Section: 50,
|
|
66
|
+
imageWordRatio: { min: 200, max: 300 },
|
|
67
|
+
keywordDensity: { min: 0.8, max: 2.0 },
|
|
68
|
+
redundancyTolerance: 3,
|
|
69
|
+
},
|
|
70
|
+
url: {
|
|
71
|
+
maxLength: 75,
|
|
72
|
+
maxLengthAbsolute: 2048,
|
|
73
|
+
},
|
|
74
|
+
mobile: {
|
|
75
|
+
minFontSize: 16,
|
|
76
|
+
minTouchTarget: 48,
|
|
77
|
+
},
|
|
78
|
+
thinContent: {
|
|
79
|
+
minWords: 150,
|
|
80
|
+
veryThinWords: 100,
|
|
81
|
+
},
|
|
82
|
+
timing: {
|
|
83
|
+
ttfb: {
|
|
84
|
+
good: 200,
|
|
85
|
+
needsImprovement: 500,
|
|
86
|
+
poor: 600,
|
|
87
|
+
},
|
|
88
|
+
total: {
|
|
89
|
+
good: 1000,
|
|
90
|
+
needsImprovement: 2500,
|
|
91
|
+
poor: 4000,
|
|
92
|
+
},
|
|
93
|
+
dnsLookup: {
|
|
94
|
+
good: 50,
|
|
95
|
+
poor: 200,
|
|
96
|
+
},
|
|
97
|
+
tcpConnect: {
|
|
98
|
+
good: 100,
|
|
99
|
+
poor: 300,
|
|
100
|
+
},
|
|
101
|
+
tlsHandshake: {
|
|
102
|
+
good: 100,
|
|
103
|
+
poor: 300,
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
responseSize: {
|
|
107
|
+
html: {
|
|
108
|
+
good: 100 * 1024,
|
|
109
|
+
warning: 500 * 1024,
|
|
110
|
+
poor: 1024 * 1024,
|
|
111
|
+
},
|
|
112
|
+
total: {
|
|
113
|
+
good: 1024 * 1024,
|
|
114
|
+
warning: 3 * 1024 * 1024,
|
|
115
|
+
poor: 5 * 1024 * 1024,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
};
|