afdocs 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -21
- package/dist/checks/authentication/auth-alternative-access.d.ts +2 -0
- package/dist/checks/authentication/auth-alternative-access.d.ts.map +1 -0
- package/dist/checks/authentication/auth-alternative-access.js +17 -0
- package/dist/checks/authentication/auth-alternative-access.js.map +1 -0
- package/dist/checks/authentication/auth-gate-detection.d.ts +2 -0
- package/dist/checks/authentication/auth-gate-detection.d.ts.map +1 -0
- package/dist/checks/authentication/auth-gate-detection.js +17 -0
- package/dist/checks/authentication/auth-gate-detection.js.map +1 -0
- package/dist/checks/index.d.ts +2 -0
- package/dist/checks/index.d.ts.map +1 -1
- package/dist/checks/index.js +3 -0
- package/dist/checks/index.js.map +1 -1
- package/dist/checks/llms-txt/llms-txt-exists.js +83 -6
- package/dist/checks/llms-txt/llms-txt-exists.js.map +1 -1
- package/dist/checks/llms-txt/llms-txt-links-markdown.js +46 -21
- package/dist/checks/llms-txt/llms-txt-links-markdown.js.map +1 -1
- package/dist/checks/llms-txt/llms-txt-links-resolve.js +10 -3
- package/dist/checks/llms-txt/llms-txt-links-resolve.js.map +1 -1
- package/dist/checks/markdown-availability/content-negotiation.js +101 -5
- package/dist/checks/markdown-availability/content-negotiation.js.map +1 -1
- package/dist/checks/markdown-availability/markdown-url-support.js +82 -5
- package/dist/checks/markdown-availability/markdown-url-support.js.map +1 -1
- package/dist/checks/page-size/content-start-position.js +161 -5
- package/dist/checks/page-size/content-start-position.js.map +1 -1
- package/dist/checks/page-size/page-size-html.js +127 -5
- package/dist/checks/page-size/page-size-html.js.map +1 -1
- package/dist/checks/page-size/page-size-markdown.js +183 -5
- package/dist/checks/page-size/page-size-markdown.js.map +1 -1
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +9 -1
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/formatters/text.d.ts +4 -1
- package/dist/cli/formatters/text.d.ts.map +1 -1
- package/dist/cli/formatters/text.js +94 -1
- package/dist/cli/formatters/text.js.map +1 -1
- package/dist/constants.d.ts +6 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -1
- package/dist/helpers/detect-markdown.d.ts +10 -0
- package/dist/helpers/detect-markdown.d.ts.map +1 -0
- package/dist/helpers/detect-markdown.js +29 -0
- package/dist/helpers/detect-markdown.js.map +1 -0
- package/dist/helpers/get-page-urls.d.ts +39 -0
- package/dist/helpers/get-page-urls.d.ts.map +1 -0
- package/dist/helpers/get-page-urls.js +230 -0
- package/dist/helpers/get-page-urls.js.map +1 -0
- package/dist/helpers/html-to-markdown.d.ts +7 -0
- package/dist/helpers/html-to-markdown.d.ts.map +1 -0
- package/dist/helpers/html-to-markdown.js +11 -0
- package/dist/helpers/html-to-markdown.js.map +1 -0
- package/dist/helpers/index.d.ts +5 -0
- package/dist/helpers/index.d.ts.map +1 -1
- package/dist/helpers/index.js +4 -0
- package/dist/helpers/index.js.map +1 -1
- package/dist/helpers/to-md-urls.d.ts +7 -0
- package/dist/helpers/to-md-urls.d.ts.map +1 -0
- package/dist/helpers/to-md-urls.js +24 -0
- package/dist/helpers/to-md-urls.js.map +1 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +21 -14
- package/dist/runner.js.map +1 -1
- package/dist/types.d.ts +10 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -2
|
@@ -1,10 +1,106 @@
|
|
|
1
1
|
import { registerCheck } from '../registry.js';
|
|
2
|
-
|
|
2
|
+
import { looksLikeMarkdown } from '../../helpers/detect-markdown.js';
|
|
3
|
+
import { discoverAndSamplePages } from '../../helpers/get-page-urls.js';
|
|
4
|
+
async function check(ctx) {
|
|
5
|
+
const id = 'content-negotiation';
|
|
6
|
+
const category = 'markdown-availability';
|
|
7
|
+
const { urls: pageUrls, totalPages, sampled: wasSampled, warnings, } = await discoverAndSamplePages(ctx);
|
|
8
|
+
const results = [];
|
|
9
|
+
const concurrency = ctx.options.maxConcurrency;
|
|
10
|
+
for (let i = 0; i < pageUrls.length; i += concurrency) {
|
|
11
|
+
const batch = pageUrls.slice(i, i + concurrency);
|
|
12
|
+
const batchResults = await Promise.all(batch.map(async (url) => {
|
|
13
|
+
try {
|
|
14
|
+
const response = await ctx.http.fetch(url, {
|
|
15
|
+
headers: { Accept: 'text/markdown' },
|
|
16
|
+
});
|
|
17
|
+
const body = await response.text();
|
|
18
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
19
|
+
const isMarkdownType = contentType.includes('text/markdown');
|
|
20
|
+
const isMarkdownBody = looksLikeMarkdown(body);
|
|
21
|
+
let classification;
|
|
22
|
+
if (isMarkdownType && isMarkdownBody) {
|
|
23
|
+
classification = 'markdown-with-correct-type';
|
|
24
|
+
// Cache the markdown content (only if not already cached by md-url check)
|
|
25
|
+
if (!ctx.pageCache.has(url)) {
|
|
26
|
+
ctx.pageCache.set(url, {
|
|
27
|
+
url,
|
|
28
|
+
markdown: { content: body, source: 'content-negotiation' },
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else if (isMarkdownBody) {
|
|
33
|
+
classification = 'markdown-with-wrong-type';
|
|
34
|
+
if (!ctx.pageCache.has(url)) {
|
|
35
|
+
ctx.pageCache.set(url, {
|
|
36
|
+
url,
|
|
37
|
+
markdown: { content: body, source: 'content-negotiation' },
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
classification = 'html';
|
|
43
|
+
}
|
|
44
|
+
return { url, classification, contentType, status: response.status };
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
return {
|
|
48
|
+
url,
|
|
49
|
+
classification: 'html',
|
|
50
|
+
contentType: '',
|
|
51
|
+
status: 0,
|
|
52
|
+
error: err instanceof Error ? err.message : String(err),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}));
|
|
56
|
+
results.push(...batchResults);
|
|
57
|
+
}
|
|
58
|
+
const markdownWithCorrectType = results.filter((r) => r.classification === 'markdown-with-correct-type').length;
|
|
59
|
+
const markdownWithWrongType = results.filter((r) => r.classification === 'markdown-with-wrong-type').length;
|
|
60
|
+
const htmlOnly = results.filter((r) => r.classification === 'html').length;
|
|
61
|
+
const negotiationRate = Math.round((markdownWithCorrectType / results.length) * 100);
|
|
62
|
+
const fetchErrors = results.filter((r) => r.error).length;
|
|
63
|
+
const rateLimited = results.filter((r) => r.status === 429).length;
|
|
64
|
+
const pageLabel = wasSampled ? 'sampled pages' : 'pages';
|
|
65
|
+
const suffix = (fetchErrors > 0 ? `; ${fetchErrors} failed to fetch` : '') +
|
|
66
|
+
(rateLimited > 0 ? `; ${rateLimited} rate-limited (HTTP 429)` : '');
|
|
67
|
+
const details = {
|
|
68
|
+
totalPages,
|
|
69
|
+
testedPages: results.length,
|
|
70
|
+
sampled: wasSampled,
|
|
71
|
+
markdownWithCorrectType,
|
|
72
|
+
markdownWithWrongType,
|
|
73
|
+
htmlOnly,
|
|
74
|
+
negotiationRate,
|
|
75
|
+
fetchErrors,
|
|
76
|
+
rateLimited,
|
|
77
|
+
pageResults: results,
|
|
78
|
+
discoveryWarnings: warnings,
|
|
79
|
+
};
|
|
80
|
+
if (negotiationRate >= 90) {
|
|
81
|
+
return {
|
|
82
|
+
id,
|
|
83
|
+
category,
|
|
84
|
+
status: 'pass',
|
|
85
|
+
message: `${markdownWithCorrectType}/${results.length} ${pageLabel} support content negotiation (${negotiationRate}%)${suffix}`,
|
|
86
|
+
details,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (markdownWithCorrectType > 0 || markdownWithWrongType > 0) {
|
|
90
|
+
return {
|
|
91
|
+
id,
|
|
92
|
+
category,
|
|
93
|
+
status: 'warn',
|
|
94
|
+
message: `Content negotiation partially supported: ${markdownWithCorrectType} correct type, ${markdownWithWrongType} wrong type, ${htmlOnly} HTML only${suffix}`,
|
|
95
|
+
details,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
3
98
|
return {
|
|
4
|
-
id
|
|
5
|
-
category
|
|
6
|
-
status: '
|
|
7
|
-
message:
|
|
99
|
+
id,
|
|
100
|
+
category,
|
|
101
|
+
status: 'fail',
|
|
102
|
+
message: `Server ignores Accept: text/markdown header (0/${results.length} ${pageLabel} return markdown)${suffix}`,
|
|
103
|
+
details,
|
|
8
104
|
};
|
|
9
105
|
}
|
|
10
106
|
registerCheck({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-negotiation.js","sourceRoot":"","sources":["../../../src/checks/markdown-availability/content-negotiation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"content-negotiation.js","sourceRoot":"","sources":["../../../src/checks/markdown-availability/content-negotiation.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,87 @@
|
|
|
1
1
|
import { registerCheck } from '../registry.js';
|
|
2
|
-
|
|
2
|
+
import { looksLikeMarkdown } from '../../helpers/detect-markdown.js';
|
|
3
|
+
import { discoverAndSamplePages } from '../../helpers/get-page-urls.js';
|
|
4
|
+
import { toMdUrls } from '../../helpers/to-md-urls.js';
|
|
5
|
+
async function check(ctx) {
|
|
6
|
+
const id = 'markdown-url-support';
|
|
7
|
+
const category = 'markdown-availability';
|
|
8
|
+
const { urls: pageUrls, totalPages, sampled: wasSampled, warnings, } = await discoverAndSamplePages(ctx);
|
|
9
|
+
const results = [];
|
|
10
|
+
const concurrency = ctx.options.maxConcurrency;
|
|
11
|
+
for (let i = 0; i < pageUrls.length; i += concurrency) {
|
|
12
|
+
const batch = pageUrls.slice(i, i + concurrency);
|
|
13
|
+
const batchResults = await Promise.all(batch.map(async (url) => {
|
|
14
|
+
const candidates = toMdUrls(url);
|
|
15
|
+
let lastError;
|
|
16
|
+
for (const mdUrl of candidates) {
|
|
17
|
+
try {
|
|
18
|
+
const response = await ctx.http.fetch(mdUrl);
|
|
19
|
+
const body = await response.text();
|
|
20
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
21
|
+
const isMarkdownType = contentType.includes('text/markdown');
|
|
22
|
+
const isMarkdownBody = looksLikeMarkdown(body);
|
|
23
|
+
const supported = response.ok && (isMarkdownType || isMarkdownBody);
|
|
24
|
+
if (supported) {
|
|
25
|
+
ctx.pageCache.set(url, {
|
|
26
|
+
url,
|
|
27
|
+
markdown: { content: body, source: 'md-url' },
|
|
28
|
+
});
|
|
29
|
+
return { url, mdUrl, supported: true, status: response.status };
|
|
30
|
+
}
|
|
31
|
+
lastError = undefined; // Got a response, not a fetch error
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
lastError = err instanceof Error ? err.message : String(err);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return { url, mdUrl: candidates[0], supported: false, status: 0, error: lastError };
|
|
38
|
+
}));
|
|
39
|
+
results.push(...batchResults);
|
|
40
|
+
}
|
|
41
|
+
const mdSupported = results.filter((r) => r.supported).length;
|
|
42
|
+
const mdUnsupported = results.length - mdSupported;
|
|
43
|
+
const supportRate = Math.round((mdSupported / results.length) * 100);
|
|
44
|
+
const fetchErrors = results.filter((r) => r.error).length;
|
|
45
|
+
const rateLimited = results.filter((r) => r.status === 429).length;
|
|
46
|
+
const pageLabel = wasSampled ? 'sampled pages' : 'pages';
|
|
47
|
+
const suffix = (fetchErrors > 0 ? `; ${fetchErrors} failed to fetch` : '') +
|
|
48
|
+
(rateLimited > 0 ? `; ${rateLimited} rate-limited (HTTP 429)` : '');
|
|
49
|
+
const details = {
|
|
50
|
+
totalPages,
|
|
51
|
+
testedPages: results.length,
|
|
52
|
+
sampled: wasSampled,
|
|
53
|
+
mdSupported,
|
|
54
|
+
mdUnsupported,
|
|
55
|
+
supportRate,
|
|
56
|
+
fetchErrors,
|
|
57
|
+
rateLimited,
|
|
58
|
+
pageResults: results,
|
|
59
|
+
discoveryWarnings: warnings,
|
|
60
|
+
};
|
|
61
|
+
if (supportRate >= 90) {
|
|
62
|
+
return {
|
|
63
|
+
id,
|
|
64
|
+
category,
|
|
65
|
+
status: 'pass',
|
|
66
|
+
message: `${mdSupported}/${results.length} ${pageLabel} support .md URLs (${supportRate}%)${suffix}`,
|
|
67
|
+
details,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (mdSupported > 0) {
|
|
71
|
+
return {
|
|
72
|
+
id,
|
|
73
|
+
category,
|
|
74
|
+
status: 'warn',
|
|
75
|
+
message: `${mdSupported}/${results.length} ${pageLabel} support .md URLs (${supportRate}%); inconsistent support${suffix}`,
|
|
76
|
+
details,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
3
79
|
return {
|
|
4
|
-
id
|
|
5
|
-
category
|
|
6
|
-
status: '
|
|
7
|
-
message:
|
|
80
|
+
id,
|
|
81
|
+
category,
|
|
82
|
+
status: 'fail',
|
|
83
|
+
message: `No ${pageLabel} support .md URLs (0/${results.length} tested)${suffix}`,
|
|
84
|
+
details,
|
|
8
85
|
};
|
|
9
86
|
}
|
|
10
87
|
registerCheck({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-url-support.js","sourceRoot":"","sources":["../../../src/checks/markdown-availability/markdown-url-support.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"markdown-url-support.js","sourceRoot":"","sources":["../../../src/checks/markdown-availability/markdown-url-support.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,166 @@
|
|
|
1
1
|
import { registerCheck } from '../registry.js';
|
|
2
|
-
|
|
2
|
+
import { looksLikeHtml } from '../../helpers/detect-markdown.js';
|
|
3
|
+
import { discoverAndSamplePages } from '../../helpers/get-page-urls.js';
|
|
4
|
+
import { htmlToMarkdown } from '../../helpers/html-to-markdown.js';
|
|
5
|
+
const CSS_PATTERN = /[{}\s]*[a-z-]+\s*:\s*[^;]+;/;
|
|
6
|
+
const JS_PATTERNS = [/^\s*(function|var|const|let|import|export)\b/, /^\s*\/\//, /[{};]\s*$/];
|
|
7
|
+
const NAV_MAX_LENGTH = 40;
|
|
8
|
+
/**
|
|
9
|
+
* Find the character position where meaningful content begins in converted markdown.
|
|
10
|
+
* Meaningful content is a heading or a prose paragraph (not CSS, JS, or short nav text).
|
|
11
|
+
*/
|
|
12
|
+
function findContentStart(markdown) {
|
|
13
|
+
const lines = markdown.split('\n');
|
|
14
|
+
let charPos = 0;
|
|
15
|
+
for (let idx = 0; idx < lines.length; idx++) {
|
|
16
|
+
const line = lines[idx];
|
|
17
|
+
const trimmed = line.trim();
|
|
18
|
+
if (trimmed.length === 0) {
|
|
19
|
+
charPos += line.length + 1;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
// ATX heading: starts with # at beginning of line
|
|
23
|
+
if (/^#{1,6}\s+\S/.test(trimmed)) {
|
|
24
|
+
return charPos;
|
|
25
|
+
}
|
|
26
|
+
// Setext heading: current line is text, next line is === or ---
|
|
27
|
+
const nextLine = idx + 1 < lines.length ? lines[idx + 1].trim() : '';
|
|
28
|
+
if (/^[=-]+$/.test(nextLine) && nextLine.length >= 2 && trimmed.length > 0) {
|
|
29
|
+
return charPos;
|
|
30
|
+
}
|
|
31
|
+
// Skip CSS-like lines
|
|
32
|
+
if (CSS_PATTERN.test(trimmed)) {
|
|
33
|
+
charPos += line.length + 1;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
// Skip JS-like lines
|
|
37
|
+
if (JS_PATTERNS.some((p) => p.test(trimmed))) {
|
|
38
|
+
charPos += line.length + 1;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
// Skip very short nav-like tokens (e.g., "Home", "Docs", "API")
|
|
42
|
+
if (trimmed.length <= NAV_MAX_LENGTH && !/[.!?]/.test(trimmed) && !trimmed.includes(' ')) {
|
|
43
|
+
charPos += line.length + 1;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
// Prose-like paragraph: contains spaces (multiple words) and is reasonably long
|
|
47
|
+
if (trimmed.length > NAV_MAX_LENGTH || (trimmed.includes(' ') && trimmed.length > 20)) {
|
|
48
|
+
return charPos;
|
|
49
|
+
}
|
|
50
|
+
charPos += line.length + 1;
|
|
51
|
+
}
|
|
52
|
+
// If nothing matched, content starts at the end (all boilerplate)
|
|
53
|
+
return charPos;
|
|
54
|
+
}
|
|
55
|
+
function positionStatus(percent) {
|
|
56
|
+
if (percent <= 10)
|
|
57
|
+
return 'pass';
|
|
58
|
+
if (percent <= 50)
|
|
59
|
+
return 'warn';
|
|
60
|
+
return 'fail';
|
|
61
|
+
}
|
|
62
|
+
function worstStatus(statuses) {
|
|
63
|
+
if (statuses.includes('fail'))
|
|
64
|
+
return 'fail';
|
|
65
|
+
if (statuses.includes('warn'))
|
|
66
|
+
return 'warn';
|
|
67
|
+
return 'pass';
|
|
68
|
+
}
|
|
69
|
+
async function check(ctx) {
|
|
70
|
+
const id = 'content-start-position';
|
|
71
|
+
const category = 'page-size';
|
|
72
|
+
const { urls: pageUrls, totalPages, sampled: wasSampled, warnings, } = await discoverAndSamplePages(ctx);
|
|
73
|
+
const results = [];
|
|
74
|
+
const concurrency = ctx.options.maxConcurrency;
|
|
75
|
+
for (let i = 0; i < pageUrls.length; i += concurrency) {
|
|
76
|
+
const batch = pageUrls.slice(i, i + concurrency);
|
|
77
|
+
const batchResults = await Promise.all(batch.map(async (url) => {
|
|
78
|
+
try {
|
|
79
|
+
const response = await ctx.http.fetch(url);
|
|
80
|
+
const body = await response.text();
|
|
81
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
82
|
+
const isMarkdownType = contentType.includes('text/markdown') || contentType.includes('text/plain');
|
|
83
|
+
const isHtml = !isMarkdownType && (contentType.includes('text/html') || looksLikeHtml(body));
|
|
84
|
+
const markdown = isHtml ? htmlToMarkdown(body) : body;
|
|
85
|
+
const totalChars = markdown.length;
|
|
86
|
+
const contentStartChar = findContentStart(markdown);
|
|
87
|
+
const contentStartPercent = totalChars > 0 ? Math.round((contentStartChar / totalChars) * 100) : 0;
|
|
88
|
+
return {
|
|
89
|
+
url,
|
|
90
|
+
contentStartChar,
|
|
91
|
+
totalChars,
|
|
92
|
+
contentStartPercent,
|
|
93
|
+
status: positionStatus(contentStartPercent),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
return {
|
|
98
|
+
url,
|
|
99
|
+
contentStartChar: 0,
|
|
100
|
+
totalChars: 0,
|
|
101
|
+
contentStartPercent: 100,
|
|
102
|
+
status: 'fail',
|
|
103
|
+
error: err instanceof Error ? err.message : String(err),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}));
|
|
107
|
+
results.push(...batchResults);
|
|
108
|
+
}
|
|
109
|
+
const successful = results.filter((r) => !r.error);
|
|
110
|
+
const fetchErrors = results.filter((r) => r.error).length;
|
|
111
|
+
if (successful.length === 0) {
|
|
112
|
+
return {
|
|
113
|
+
id,
|
|
114
|
+
category,
|
|
115
|
+
status: 'fail',
|
|
116
|
+
message: `Could not fetch any pages to analyze${fetchErrors > 0 ? `; ${fetchErrors} failed to fetch` : ''}`,
|
|
117
|
+
details: {
|
|
118
|
+
totalPages,
|
|
119
|
+
testedPages: results.length,
|
|
120
|
+
sampled: wasSampled,
|
|
121
|
+
fetchErrors,
|
|
122
|
+
pageResults: results,
|
|
123
|
+
discoveryWarnings: warnings,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const percents = successful.map((r) => r.contentStartPercent);
|
|
128
|
+
const medianPercent = percents.sort((a, b) => a - b)[Math.floor(percents.length / 2)];
|
|
129
|
+
const maxPercent = Math.max(...percents);
|
|
130
|
+
const overallStatus = worstStatus(successful.map((r) => r.status));
|
|
131
|
+
const pageLabel = wasSampled ? 'sampled pages' : 'pages';
|
|
132
|
+
const passBucket = successful.filter((r) => r.status === 'pass').length;
|
|
133
|
+
const warnBucket = successful.filter((r) => r.status === 'warn').length;
|
|
134
|
+
const failBucket = successful.filter((r) => r.status === 'fail').length;
|
|
135
|
+
const suffix = fetchErrors > 0 ? `; ${fetchErrors} failed to fetch` : '';
|
|
136
|
+
let message;
|
|
137
|
+
if (overallStatus === 'pass') {
|
|
138
|
+
message = `Content starts within first 10% on all ${successful.length} ${pageLabel} (median ${medianPercent}%)${suffix}`;
|
|
139
|
+
}
|
|
140
|
+
else if (overallStatus === 'warn') {
|
|
141
|
+
message = `${warnBucket} of ${successful.length} ${pageLabel} have content starting at 10–50% (worst ${maxPercent}%)${suffix}`;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
message = `${failBucket} of ${successful.length} ${pageLabel} have content starting past 50% (worst ${maxPercent}%)${suffix}`;
|
|
145
|
+
}
|
|
3
146
|
return {
|
|
4
|
-
id
|
|
5
|
-
category
|
|
6
|
-
status:
|
|
7
|
-
message
|
|
147
|
+
id,
|
|
148
|
+
category,
|
|
149
|
+
status: overallStatus,
|
|
150
|
+
message,
|
|
151
|
+
details: {
|
|
152
|
+
totalPages,
|
|
153
|
+
testedPages: results.length,
|
|
154
|
+
sampled: wasSampled,
|
|
155
|
+
medianPercent,
|
|
156
|
+
maxPercent,
|
|
157
|
+
passBucket,
|
|
158
|
+
warnBucket,
|
|
159
|
+
failBucket,
|
|
160
|
+
fetchErrors,
|
|
161
|
+
pageResults: results,
|
|
162
|
+
discoveryWarnings: warnings,
|
|
163
|
+
},
|
|
8
164
|
};
|
|
9
165
|
}
|
|
10
166
|
registerCheck({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-start-position.js","sourceRoot":"","sources":["../../../src/checks/page-size/content-start-position.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"content-start-position.js","sourceRoot":"","sources":["../../../src/checks/page-size/content-start-position.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,132 @@
|
|
|
1
1
|
import { registerCheck } from '../registry.js';
|
|
2
|
-
|
|
2
|
+
import { looksLikeHtml } from '../../helpers/detect-markdown.js';
|
|
3
|
+
import { discoverAndSamplePages } from '../../helpers/get-page-urls.js';
|
|
4
|
+
import { htmlToMarkdown } from '../../helpers/html-to-markdown.js';
|
|
5
|
+
function sizeStatus(chars, pass, fail) {
|
|
6
|
+
if (chars <= pass)
|
|
7
|
+
return 'pass';
|
|
8
|
+
if (chars <= fail)
|
|
9
|
+
return 'warn';
|
|
10
|
+
return 'fail';
|
|
11
|
+
}
|
|
12
|
+
function worstStatus(statuses) {
|
|
13
|
+
if (statuses.includes('fail'))
|
|
14
|
+
return 'fail';
|
|
15
|
+
if (statuses.includes('warn'))
|
|
16
|
+
return 'warn';
|
|
17
|
+
return 'pass';
|
|
18
|
+
}
|
|
19
|
+
function formatSize(chars) {
|
|
20
|
+
if (chars >= 1000)
|
|
21
|
+
return `${Math.round(chars / 1000)}K`;
|
|
22
|
+
return String(chars);
|
|
23
|
+
}
|
|
24
|
+
async function check(ctx) {
|
|
25
|
+
const id = 'page-size-html';
|
|
26
|
+
const category = 'page-size';
|
|
27
|
+
const { pass: passThreshold, fail: failThreshold } = ctx.options.thresholds;
|
|
28
|
+
const { urls: pageUrls, totalPages, sampled: wasSampled, warnings, } = await discoverAndSamplePages(ctx);
|
|
29
|
+
const results = [];
|
|
30
|
+
const concurrency = ctx.options.maxConcurrency;
|
|
31
|
+
for (let i = 0; i < pageUrls.length; i += concurrency) {
|
|
32
|
+
const batch = pageUrls.slice(i, i + concurrency);
|
|
33
|
+
const batchResults = await Promise.all(batch.map(async (url) => {
|
|
34
|
+
try {
|
|
35
|
+
const response = await ctx.http.fetch(url);
|
|
36
|
+
const body = await response.text();
|
|
37
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
38
|
+
const isMarkdownType = contentType.includes('text/markdown') || contentType.includes('text/plain');
|
|
39
|
+
const isHtml = !isMarkdownType && (contentType.includes('text/html') || looksLikeHtml(body));
|
|
40
|
+
// Skip conversion if the response is already markdown
|
|
41
|
+
const html = isHtml ? body : '';
|
|
42
|
+
const htmlChars = html.length;
|
|
43
|
+
const converted = isHtml ? htmlToMarkdown(body) : body;
|
|
44
|
+
const convertedChars = converted.length;
|
|
45
|
+
const ratio = htmlChars > 0 ? Math.round((1 - convertedChars / htmlChars) * 100) : 0;
|
|
46
|
+
return {
|
|
47
|
+
url,
|
|
48
|
+
htmlCharacters: htmlChars,
|
|
49
|
+
convertedCharacters: convertedChars,
|
|
50
|
+
conversionRatio: ratio,
|
|
51
|
+
status: sizeStatus(convertedChars, passThreshold, failThreshold),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
return {
|
|
56
|
+
url,
|
|
57
|
+
htmlCharacters: 0,
|
|
58
|
+
convertedCharacters: 0,
|
|
59
|
+
conversionRatio: 0,
|
|
60
|
+
status: 'fail',
|
|
61
|
+
error: err instanceof Error ? err.message : String(err),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}));
|
|
65
|
+
results.push(...batchResults);
|
|
66
|
+
}
|
|
67
|
+
const successful = results.filter((r) => !r.error);
|
|
68
|
+
const fetchErrors = results.filter((r) => r.error).length;
|
|
69
|
+
const rateLimited = results.filter((r) => r.error && r.error.includes('429')).length;
|
|
70
|
+
if (successful.length === 0) {
|
|
71
|
+
const suffix = fetchErrors > 0 ? `; ${fetchErrors} failed to fetch` : '';
|
|
72
|
+
return {
|
|
73
|
+
id,
|
|
74
|
+
category,
|
|
75
|
+
status: 'fail',
|
|
76
|
+
message: `Could not fetch any pages to measure${suffix}`,
|
|
77
|
+
details: {
|
|
78
|
+
totalPages,
|
|
79
|
+
testedPages: results.length,
|
|
80
|
+
sampled: wasSampled,
|
|
81
|
+
fetchErrors,
|
|
82
|
+
rateLimited,
|
|
83
|
+
pageResults: results,
|
|
84
|
+
discoveryWarnings: warnings,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const convertedSizes = successful.map((r) => r.convertedCharacters).sort((a, b) => a - b);
|
|
89
|
+
const median = convertedSizes[Math.floor(convertedSizes.length / 2)];
|
|
90
|
+
const max = convertedSizes[convertedSizes.length - 1];
|
|
91
|
+
const avgRatio = Math.round(successful.reduce((sum, r) => sum + r.conversionRatio, 0) / successful.length);
|
|
92
|
+
const overallStatus = worstStatus(successful.map((r) => r.status));
|
|
93
|
+
const pageLabel = wasSampled ? 'sampled pages' : 'pages';
|
|
94
|
+
const passBucket = successful.filter((r) => r.status === 'pass').length;
|
|
95
|
+
const warnBucket = successful.filter((r) => r.status === 'warn').length;
|
|
96
|
+
const failBucket = successful.filter((r) => r.status === 'fail').length;
|
|
97
|
+
const suffix = (fetchErrors > 0 ? `; ${fetchErrors} failed to fetch` : '') +
|
|
98
|
+
(rateLimited > 0 ? `; ${rateLimited} rate-limited (HTTP 429)` : '');
|
|
99
|
+
let message;
|
|
100
|
+
if (overallStatus === 'pass') {
|
|
101
|
+
message = `All ${successful.length} ${pageLabel} convert under ${formatSize(passThreshold)} chars (median ${formatSize(median)}, ${avgRatio}% boilerplate)${suffix}`;
|
|
102
|
+
}
|
|
103
|
+
else if (overallStatus === 'warn') {
|
|
104
|
+
message = `${warnBucket} of ${successful.length} ${pageLabel} convert to ${formatSize(passThreshold)}–${formatSize(failThreshold)} chars (max ${formatSize(max)}, ${avgRatio}% boilerplate)${suffix}`;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
message = `${failBucket} of ${successful.length} ${pageLabel} convert to over ${formatSize(failThreshold)} chars (max ${formatSize(max)}, ${avgRatio}% boilerplate)${suffix}`;
|
|
108
|
+
}
|
|
3
109
|
return {
|
|
4
|
-
id
|
|
5
|
-
category
|
|
6
|
-
status:
|
|
7
|
-
message
|
|
110
|
+
id,
|
|
111
|
+
category,
|
|
112
|
+
status: overallStatus,
|
|
113
|
+
message,
|
|
114
|
+
details: {
|
|
115
|
+
totalPages,
|
|
116
|
+
testedPages: results.length,
|
|
117
|
+
sampled: wasSampled,
|
|
118
|
+
median,
|
|
119
|
+
max,
|
|
120
|
+
avgBoilerplatePercent: avgRatio,
|
|
121
|
+
passBucket,
|
|
122
|
+
warnBucket,
|
|
123
|
+
failBucket,
|
|
124
|
+
fetchErrors,
|
|
125
|
+
rateLimited,
|
|
126
|
+
thresholds: { pass: passThreshold, fail: failThreshold },
|
|
127
|
+
pageResults: results,
|
|
128
|
+
discoveryWarnings: warnings,
|
|
129
|
+
},
|
|
8
130
|
};
|
|
9
131
|
}
|
|
10
132
|
registerCheck({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-size-html.js","sourceRoot":"","sources":["../../../src/checks/page-size/page-size-html.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"page-size-html.js","sourceRoot":"","sources":["../../../src/checks/page-size/page-size-html.ts"],"names":[],"mappings":"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"}
|