afdocs 0.2.0 → 0.3.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 +13 -7
- package/dist/checks/authentication/auth-gate-detection.js +163 -5
- package/dist/checks/authentication/auth-gate-detection.js.map +1 -1
- package/dist/checks/content-structure/markdown-code-fence-validity.js +119 -5
- package/dist/checks/content-structure/markdown-code-fence-validity.js.map +1 -1
- package/dist/checks/observability/cache-header-hygiene.js +200 -5
- package/dist/checks/observability/cache-header-hygiene.js.map +1 -1
- package/dist/checks/page-size/page-size-markdown.js +43 -126
- package/dist/checks/page-size/page-size-markdown.js.map +1 -1
- package/dist/checks/url-stability/http-status-codes.js +106 -5
- package/dist/checks/url-stability/http-status-codes.js.map +1 -1
- package/dist/helpers/get-markdown-content.d.ts +23 -0
- package/dist/helpers/get-markdown-content.d.ts.map +1 -0
- package/dist/helpers/get-markdown-content.js +93 -0
- package/dist/helpers/get-markdown-content.js.map +1 -0
- package/dist/helpers/get-page-urls.d.ts +3 -0
- package/dist/helpers/get-page-urls.d.ts.map +1 -1
- package/dist/helpers/get-page-urls.js +7 -1
- package/dist/helpers/get-page-urls.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Test your documentation site against the [Agent-Friendly Documentation Spec](htt
|
|
|
7
7
|
|
|
8
8
|
Agents don't use docs like humans. They hit truncation limits, get walls of CSS instead of content, can't follow cross-host redirects, and don't know about quality-of-life improvements like `llms.txt` or `.md` docs pages that would make life swell. Maybe this is because the industry has lacked guidance - until now.
|
|
9
9
|
|
|
10
|
-
afdocs runs 21 checks across 8 categories to evaluate how well your docs serve agent consumers.
|
|
10
|
+
afdocs runs 21 checks across 8 categories to evaluate how well your docs serve agent consumers. 14 are fully implemented; the rest return `skip` until completed.
|
|
11
11
|
|
|
12
12
|
> **Status: Early development (0.x)**
|
|
13
13
|
> This project is under active development. Check IDs, CLI flags, and output formats may change between minor versions. Feel free to try it out, but don't build automation against specific output until 1.0.
|
|
@@ -36,8 +36,14 @@ Markdown Availability
|
|
|
36
36
|
✗ content-negotiation: Server ignores Accept: text/markdown header (0/50 sampled pages return markdown)
|
|
37
37
|
✗ markdown-url-support: No sampled pages support .md URLs (0/50 tested)
|
|
38
38
|
|
|
39
|
+
URL Stability
|
|
40
|
+
✓ http-status-codes: All 50 sampled pages return proper error codes for bad URLs
|
|
41
|
+
|
|
42
|
+
Authentication
|
|
43
|
+
✓ auth-gate-detection: All 50 sampled pages are publicly accessible
|
|
44
|
+
|
|
39
45
|
Summary
|
|
40
|
-
|
|
46
|
+
9 passed, 3 failed, 9 skipped (21 total)
|
|
41
47
|
```
|
|
42
48
|
|
|
43
49
|
## Install
|
|
@@ -138,7 +144,7 @@ describe('agent-friendliness', () => {
|
|
|
138
144
|
|
|
139
145
|
## Checks
|
|
140
146
|
|
|
141
|
-
21 checks across 8 categories. Checks marked with \* are
|
|
147
|
+
21 checks across 8 categories. Checks marked with \* are not yet implemented and return `skip`.
|
|
142
148
|
|
|
143
149
|
### Category 1: llms.txt
|
|
144
150
|
|
|
@@ -171,13 +177,13 @@ describe('agent-friendliness', () => {
|
|
|
171
177
|
| --------------------------------- | -------------------------------------------------- |
|
|
172
178
|
| `tabbed-content-serialization` \* | Whether tabbed content creates oversized output |
|
|
173
179
|
| `section-header-quality` \* | Whether headers in tabbed sections include context |
|
|
174
|
-
| `markdown-code-fence-validity`
|
|
180
|
+
| `markdown-code-fence-validity` | Whether markdown has unclosed code fences |
|
|
175
181
|
|
|
176
182
|
### Category 5: URL Stability and Redirects
|
|
177
183
|
|
|
178
184
|
| Check | Description |
|
|
179
185
|
| ---------------------- | ----------------------------------------------- |
|
|
180
|
-
| `http-status-codes`
|
|
186
|
+
| `http-status-codes` | Whether error pages return correct status codes |
|
|
181
187
|
| `redirect-behavior` \* | Whether redirects are same-host HTTP redirects |
|
|
182
188
|
|
|
183
189
|
### Category 6: Agent Discoverability Directives
|
|
@@ -192,13 +198,13 @@ describe('agent-friendliness', () => {
|
|
|
192
198
|
| ---------------------------- | ---------------------------------------------- |
|
|
193
199
|
| `llms-txt-freshness` \* | Whether `llms.txt` reflects current site state |
|
|
194
200
|
| `markdown-content-parity` \* | Whether markdown and HTML versions match |
|
|
195
|
-
| `cache-header-hygiene`
|
|
201
|
+
| `cache-header-hygiene` | Whether cache headers allow timely updates |
|
|
196
202
|
|
|
197
203
|
### Category 8: Authentication and Access
|
|
198
204
|
|
|
199
205
|
| Check | Description |
|
|
200
206
|
| ---------------------------- | -------------------------------------------------------------------- |
|
|
201
|
-
| `auth-gate-detection`
|
|
207
|
+
| `auth-gate-detection` | Whether documentation pages require authentication to access content |
|
|
202
208
|
| `auth-alternative-access` \* | Whether auth-gated sites provide alternative access paths for agents |
|
|
203
209
|
|
|
204
210
|
## Check dependencies
|
|
@@ -1,10 +1,168 @@
|
|
|
1
1
|
import { registerCheck } from '../registry.js';
|
|
2
|
-
|
|
2
|
+
import { discoverAndSamplePages } from '../../helpers/get-page-urls.js';
|
|
3
|
+
const SSO_DOMAINS = [
|
|
4
|
+
'okta.com',
|
|
5
|
+
'auth0.com',
|
|
6
|
+
'login.microsoftonline.com',
|
|
7
|
+
'accounts.google.com',
|
|
8
|
+
'login.salesforce.com',
|
|
9
|
+
'sso.',
|
|
10
|
+
'idp.',
|
|
11
|
+
'auth.',
|
|
12
|
+
'login.',
|
|
13
|
+
];
|
|
14
|
+
function isSsoDomain(url) {
|
|
15
|
+
try {
|
|
16
|
+
const hostname = new URL(url).hostname.toLowerCase();
|
|
17
|
+
return SSO_DOMAINS.find((domain) => hostname === domain || hostname.endsWith('.' + domain) || hostname.startsWith(domain));
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function detectLoginForm(body) {
|
|
24
|
+
const sample = body.slice(0, 50000).toLowerCase();
|
|
25
|
+
if (sample.includes('<input') && sample.includes('type="password"')) {
|
|
26
|
+
return 'Contains password input field';
|
|
27
|
+
}
|
|
28
|
+
// Check page title for login indicators
|
|
29
|
+
const titleMatch = /<title[^>]*>(.*?)<\/title>/i.exec(sample);
|
|
30
|
+
if (titleMatch) {
|
|
31
|
+
const title = titleMatch[1].toLowerCase();
|
|
32
|
+
if (/sign\s*in|log\s*in|authenticate/i.test(title)) {
|
|
33
|
+
return `Page title suggests login: "${titleMatch[1].trim()}"`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Check for SSO form actions
|
|
37
|
+
if (/<form[^>]*action\s*=\s*["'][^"']*(?:saml|oauth|openid|sso|auth)[^"']*["']/i.test(sample)) {
|
|
38
|
+
return 'Contains SSO-related form action';
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
async function check(ctx) {
|
|
43
|
+
const id = 'auth-gate-detection';
|
|
44
|
+
const category = 'authentication';
|
|
45
|
+
const { urls: pageUrls, totalPages, sampled, warnings } = await discoverAndSamplePages(ctx);
|
|
46
|
+
const results = [];
|
|
47
|
+
const concurrency = ctx.options.maxConcurrency;
|
|
48
|
+
for (let i = 0; i < pageUrls.length; i += concurrency) {
|
|
49
|
+
const batch = pageUrls.slice(i, i + concurrency);
|
|
50
|
+
const batchResults = await Promise.all(batch.map(async (url) => {
|
|
51
|
+
try {
|
|
52
|
+
const response = await ctx.http.fetch(url, { redirect: 'manual' });
|
|
53
|
+
const status = response.status;
|
|
54
|
+
// Auth-required status codes
|
|
55
|
+
if (status === 401 || status === 403) {
|
|
56
|
+
return { url, classification: 'auth-required', status };
|
|
57
|
+
}
|
|
58
|
+
// Redirect — check if it's to an SSO domain
|
|
59
|
+
if (status >= 300 && status < 400) {
|
|
60
|
+
const location = response.headers.get('location');
|
|
61
|
+
if (location) {
|
|
62
|
+
const resolvedLocation = location.startsWith('http')
|
|
63
|
+
? location
|
|
64
|
+
: new URL(location, url).toString();
|
|
65
|
+
const ssoDomain = isSsoDomain(resolvedLocation);
|
|
66
|
+
if (ssoDomain) {
|
|
67
|
+
return {
|
|
68
|
+
url,
|
|
69
|
+
classification: 'auth-redirect',
|
|
70
|
+
status,
|
|
71
|
+
redirectUrl: resolvedLocation,
|
|
72
|
+
ssoDomain,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Non-SSO redirect — treat as accessible (normal redirect)
|
|
77
|
+
return { url, classification: 'accessible', status };
|
|
78
|
+
}
|
|
79
|
+
// 200 — check for soft auth gate (login form)
|
|
80
|
+
if (status === 200) {
|
|
81
|
+
let body;
|
|
82
|
+
try {
|
|
83
|
+
body = await response.text();
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return { url, classification: 'accessible', status };
|
|
87
|
+
}
|
|
88
|
+
const loginHint = detectLoginForm(body);
|
|
89
|
+
if (loginHint) {
|
|
90
|
+
return { url, classification: 'soft-auth-gate', status, hint: loginHint };
|
|
91
|
+
}
|
|
92
|
+
return { url, classification: 'accessible', status };
|
|
93
|
+
}
|
|
94
|
+
// Other status codes — treat as accessible
|
|
95
|
+
return { url, classification: 'accessible', status };
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
return {
|
|
99
|
+
url,
|
|
100
|
+
classification: 'accessible',
|
|
101
|
+
status: null,
|
|
102
|
+
error: err instanceof Error ? err.message : String(err),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}));
|
|
106
|
+
results.push(...batchResults);
|
|
107
|
+
}
|
|
108
|
+
const fetchErrors = results.filter((r) => r.error).length;
|
|
109
|
+
const tested = results.filter((r) => !r.error);
|
|
110
|
+
if (tested.length === 0) {
|
|
111
|
+
return {
|
|
112
|
+
id,
|
|
113
|
+
category,
|
|
114
|
+
status: 'fail',
|
|
115
|
+
message: `Could not fetch any pages to check authentication${fetchErrors > 0 ? `; ${fetchErrors} failed to fetch` : ''}`,
|
|
116
|
+
details: {
|
|
117
|
+
totalPages,
|
|
118
|
+
testedPages: results.length,
|
|
119
|
+
sampled,
|
|
120
|
+
fetchErrors,
|
|
121
|
+
pageResults: results,
|
|
122
|
+
discoveryWarnings: warnings,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const accessible = tested.filter((r) => r.classification === 'accessible');
|
|
127
|
+
const authRequired = tested.filter((r) => r.classification === 'auth-required');
|
|
128
|
+
const softAuthGate = tested.filter((r) => r.classification === 'soft-auth-gate');
|
|
129
|
+
const authRedirect = tested.filter((r) => r.classification === 'auth-redirect');
|
|
130
|
+
const gatedCount = authRequired.length + softAuthGate.length + authRedirect.length;
|
|
131
|
+
const ssoDomains = [...new Set(authRedirect.map((r) => r.ssoDomain).filter(Boolean))];
|
|
132
|
+
let status;
|
|
133
|
+
let message;
|
|
134
|
+
const suffix = fetchErrors > 0 ? `; ${fetchErrors} failed to fetch` : '';
|
|
135
|
+
const pageLabel = sampled ? 'sampled pages' : 'pages';
|
|
136
|
+
if (gatedCount === 0) {
|
|
137
|
+
status = 'pass';
|
|
138
|
+
message = `All ${accessible.length} ${pageLabel} are publicly accessible${suffix}`;
|
|
139
|
+
}
|
|
140
|
+
else if (accessible.length > 0 && gatedCount > 0) {
|
|
141
|
+
status = 'warn';
|
|
142
|
+
message = `${gatedCount} of ${tested.length} ${pageLabel} require authentication (${accessible.length} accessible)${suffix}`;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
status = 'fail';
|
|
146
|
+
message = `All ${tested.length} ${pageLabel} require authentication${suffix}`;
|
|
147
|
+
}
|
|
3
148
|
return {
|
|
4
|
-
id
|
|
5
|
-
category
|
|
6
|
-
status
|
|
7
|
-
message
|
|
149
|
+
id,
|
|
150
|
+
category,
|
|
151
|
+
status,
|
|
152
|
+
message,
|
|
153
|
+
details: {
|
|
154
|
+
totalPages,
|
|
155
|
+
testedPages: results.length,
|
|
156
|
+
sampled,
|
|
157
|
+
accessible: accessible.length,
|
|
158
|
+
authRequired: authRequired.length,
|
|
159
|
+
softAuthGate: softAuthGate.length,
|
|
160
|
+
authRedirect: authRedirect.length,
|
|
161
|
+
ssoDomains,
|
|
162
|
+
fetchErrors,
|
|
163
|
+
pageResults: results,
|
|
164
|
+
discoveryWarnings: warnings,
|
|
165
|
+
},
|
|
8
166
|
};
|
|
9
167
|
}
|
|
10
168
|
registerCheck({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-gate-detection.js","sourceRoot":"","sources":["../../../src/checks/authentication/auth-gate-detection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"auth-gate-detection.js","sourceRoot":"","sources":["../../../src/checks/authentication/auth-gate-detection.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,124 @@
|
|
|
1
1
|
import { registerCheck } from '../registry.js';
|
|
2
|
-
|
|
2
|
+
import { getMarkdownContent } from '../../helpers/get-markdown-content.js';
|
|
3
|
+
/**
|
|
4
|
+
* Strip blockquote prefixes (`> `) from a line so that fences inside
|
|
5
|
+
* blockquotes are detected the same way a CommonMark parser would
|
|
6
|
+
* handle them. Supports nested blockquotes (`> > `).
|
|
7
|
+
*/
|
|
8
|
+
function stripBlockquotePrefix(line) {
|
|
9
|
+
return line.replace(/^(?:\s{0,3}> ?)+/, '');
|
|
10
|
+
}
|
|
11
|
+
/** Match a line that opens or closes a code fence (after blockquote stripping). */
|
|
12
|
+
const FENCE_RE = /^( {0,3})((`{3,})|(~{3,}))(.*)?$/;
|
|
13
|
+
function analyzeFences(content) {
|
|
14
|
+
const lines = content.split('\n');
|
|
15
|
+
const issues = [];
|
|
16
|
+
let fenceCount = 0;
|
|
17
|
+
let openFence = null;
|
|
18
|
+
for (let i = 0; i < lines.length; i++) {
|
|
19
|
+
const stripped = stripBlockquotePrefix(lines[i]);
|
|
20
|
+
const match = FENCE_RE.exec(stripped);
|
|
21
|
+
if (!match)
|
|
22
|
+
continue;
|
|
23
|
+
const char = match[3] ? '`' : '~';
|
|
24
|
+
const length = (match[3] || match[4]).length;
|
|
25
|
+
if (!openFence) {
|
|
26
|
+
// Opening fence
|
|
27
|
+
openFence = { line: i + 1, char, length };
|
|
28
|
+
fenceCount++;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
// Potential closing fence: must use same char and be at least as long
|
|
32
|
+
if (char === openFence.char && length >= openFence.length) {
|
|
33
|
+
// Proper close
|
|
34
|
+
openFence = null;
|
|
35
|
+
}
|
|
36
|
+
else if (char !== openFence.char && length >= openFence.length) {
|
|
37
|
+
// Inconsistent close: different delimiter type
|
|
38
|
+
issues.push({
|
|
39
|
+
line: i + 1,
|
|
40
|
+
type: 'inconsistent-close',
|
|
41
|
+
opener: openFence.char.repeat(openFence.length),
|
|
42
|
+
closer: char.repeat(length),
|
|
43
|
+
});
|
|
44
|
+
openFence = null;
|
|
45
|
+
}
|
|
46
|
+
// Otherwise: different char + shorter length = not a close, just content inside the fence
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (openFence) {
|
|
50
|
+
issues.push({
|
|
51
|
+
line: openFence.line,
|
|
52
|
+
type: 'unclosed',
|
|
53
|
+
opener: openFence.char.repeat(openFence.length),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return { fenceCount, issues };
|
|
57
|
+
}
|
|
58
|
+
function worstStatus(statuses) {
|
|
59
|
+
if (statuses.includes('fail'))
|
|
60
|
+
return 'fail';
|
|
61
|
+
if (statuses.includes('warn'))
|
|
62
|
+
return 'warn';
|
|
63
|
+
return 'pass';
|
|
64
|
+
}
|
|
65
|
+
async function check(ctx) {
|
|
66
|
+
const id = 'markdown-code-fence-validity';
|
|
67
|
+
const category = 'content-structure';
|
|
68
|
+
const mdResult = await getMarkdownContent(ctx);
|
|
69
|
+
if (mdResult.pages.length === 0) {
|
|
70
|
+
if (mdResult.mode === 'cached' && !mdResult.depPassed) {
|
|
71
|
+
return {
|
|
72
|
+
id,
|
|
73
|
+
category,
|
|
74
|
+
status: 'skip',
|
|
75
|
+
message: 'Site does not serve markdown content; nothing to analyze',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const hint = mdResult.mode === 'standalone'
|
|
79
|
+
? '; try running with markdown-url-support or content-negotiation checks'
|
|
80
|
+
: '';
|
|
81
|
+
return { id, category, status: 'skip', message: `No markdown content found${hint}` };
|
|
82
|
+
}
|
|
83
|
+
const results = mdResult.pages.map(({ url, content, source }) => {
|
|
84
|
+
const { fenceCount, issues } = analyzeFences(content);
|
|
85
|
+
const hasUnclosed = issues.some((i) => i.type === 'unclosed');
|
|
86
|
+
const hasInconsistent = issues.some((i) => i.type === 'inconsistent-close');
|
|
87
|
+
let status;
|
|
88
|
+
if (hasUnclosed)
|
|
89
|
+
status = 'fail';
|
|
90
|
+
else if (hasInconsistent)
|
|
91
|
+
status = 'warn';
|
|
92
|
+
else
|
|
93
|
+
status = 'pass';
|
|
94
|
+
return { url, source, fenceCount, issues, status };
|
|
95
|
+
});
|
|
96
|
+
const overallStatus = worstStatus(results.map((r) => r.status));
|
|
97
|
+
const totalFences = results.reduce((sum, r) => sum + r.fenceCount, 0);
|
|
98
|
+
const unclosedCount = results.reduce((sum, r) => sum + r.issues.filter((i) => i.type === 'unclosed').length, 0);
|
|
99
|
+
const inconsistentCount = results.reduce((sum, r) => sum + r.issues.filter((i) => i.type === 'inconsistent-close').length, 0);
|
|
100
|
+
let message;
|
|
101
|
+
if (overallStatus === 'pass') {
|
|
102
|
+
message = `All ${totalFences} code fences properly closed across ${results.length} pages`;
|
|
103
|
+
}
|
|
104
|
+
else if (overallStatus === 'warn') {
|
|
105
|
+
message = `${inconsistentCount} code fences use inconsistent delimiters across ${results.length} pages`;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
message = `${unclosedCount} unclosed code fences found across ${results.length} pages`;
|
|
109
|
+
}
|
|
3
110
|
return {
|
|
4
|
-
id
|
|
5
|
-
category
|
|
6
|
-
status:
|
|
7
|
-
message
|
|
111
|
+
id,
|
|
112
|
+
category,
|
|
113
|
+
status: overallStatus,
|
|
114
|
+
message,
|
|
115
|
+
details: {
|
|
116
|
+
pagesAnalyzed: results.length,
|
|
117
|
+
totalFences,
|
|
118
|
+
unclosedCount,
|
|
119
|
+
inconsistentCount,
|
|
120
|
+
pageResults: results,
|
|
121
|
+
},
|
|
8
122
|
};
|
|
9
123
|
}
|
|
10
124
|
registerCheck({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-code-fence-validity.js","sourceRoot":"","sources":["../../../src/checks/content-structure/markdown-code-fence-validity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"markdown-code-fence-validity.js","sourceRoot":"","sources":["../../../src/checks/content-structure/markdown-code-fence-validity.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,205 @@
|
|
|
1
1
|
import { registerCheck } from '../registry.js';
|
|
2
|
-
|
|
2
|
+
import { discoverAndSamplePages } from '../../helpers/get-page-urls.js';
|
|
3
|
+
function parseCacheControl(header) {
|
|
4
|
+
if (!header)
|
|
5
|
+
return { maxAge: null, sMaxAge: null, mustRevalidate: false, noCache: false, noStore: false };
|
|
6
|
+
const directives = header
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.split(',')
|
|
9
|
+
.map((d) => d.trim());
|
|
10
|
+
let maxAge = null;
|
|
11
|
+
let sMaxAge = null;
|
|
12
|
+
let mustRevalidate = false;
|
|
13
|
+
let noCache = false;
|
|
14
|
+
let noStore = false;
|
|
15
|
+
for (const d of directives) {
|
|
16
|
+
if (d.startsWith('max-age=')) {
|
|
17
|
+
maxAge = parseInt(d.split('=')[1], 10);
|
|
18
|
+
if (isNaN(maxAge))
|
|
19
|
+
maxAge = null;
|
|
20
|
+
}
|
|
21
|
+
else if (d.startsWith('s-maxage=')) {
|
|
22
|
+
sMaxAge = parseInt(d.split('=')[1], 10);
|
|
23
|
+
if (isNaN(sMaxAge))
|
|
24
|
+
sMaxAge = null;
|
|
25
|
+
}
|
|
26
|
+
else if (d === 'must-revalidate') {
|
|
27
|
+
mustRevalidate = true;
|
|
28
|
+
}
|
|
29
|
+
else if (d === 'no-cache') {
|
|
30
|
+
noCache = true;
|
|
31
|
+
}
|
|
32
|
+
else if (d === 'no-store') {
|
|
33
|
+
noStore = true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return { maxAge, sMaxAge, mustRevalidate, noCache, noStore };
|
|
37
|
+
}
|
|
38
|
+
function classifyCache(result) {
|
|
39
|
+
// no-cache or no-store = always fresh
|
|
40
|
+
if (result.noCache || result.noStore)
|
|
41
|
+
return 'pass';
|
|
42
|
+
// must-revalidate with a revalidation mechanism is good
|
|
43
|
+
if (result.mustRevalidate && (result.etag || result.lastModified))
|
|
44
|
+
return 'pass';
|
|
45
|
+
const effective = result.effectiveMaxAge;
|
|
46
|
+
if (effective === null) {
|
|
47
|
+
// No cache-related headers at all
|
|
48
|
+
if (!result.cacheControl && !result.expires && !result.etag && !result.lastModified) {
|
|
49
|
+
return 'fail';
|
|
50
|
+
}
|
|
51
|
+
// Has ETag/Last-Modified but no max-age — still ok (browser will revalidate)
|
|
52
|
+
if (result.etag || result.lastModified)
|
|
53
|
+
return 'pass';
|
|
54
|
+
return 'fail';
|
|
55
|
+
}
|
|
56
|
+
if (effective <= 3600)
|
|
57
|
+
return 'pass';
|
|
58
|
+
if (effective <= 86400)
|
|
59
|
+
return 'warn';
|
|
60
|
+
return 'fail';
|
|
61
|
+
}
|
|
62
|
+
function getEffectiveMaxAge(parsed, expires) {
|
|
63
|
+
// s-maxage takes precedence over max-age
|
|
64
|
+
if (parsed.sMaxAge !== null)
|
|
65
|
+
return parsed.sMaxAge;
|
|
66
|
+
if (parsed.maxAge !== null)
|
|
67
|
+
return parsed.maxAge;
|
|
68
|
+
// Fall back to Expires header
|
|
69
|
+
if (expires) {
|
|
70
|
+
try {
|
|
71
|
+
const expiresMs = new Date(expires).getTime();
|
|
72
|
+
const nowMs = Date.now();
|
|
73
|
+
if (!isNaN(expiresMs)) {
|
|
74
|
+
return Math.max(0, Math.round((expiresMs - nowMs) / 1000));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// invalid date
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
function worstStatus(statuses) {
|
|
84
|
+
if (statuses.includes('fail'))
|
|
85
|
+
return 'fail';
|
|
86
|
+
if (statuses.includes('warn'))
|
|
87
|
+
return 'warn';
|
|
88
|
+
return 'pass';
|
|
89
|
+
}
|
|
90
|
+
async function check(ctx) {
|
|
91
|
+
const id = 'cache-header-hygiene';
|
|
92
|
+
const category = 'observability';
|
|
93
|
+
// Collect URLs to check: llms.txt files + sampled page URLs
|
|
94
|
+
const urlsToCheck = [];
|
|
95
|
+
// llms.txt URLs
|
|
96
|
+
const existsResult = ctx.previousResults.get('llms-txt-exists');
|
|
97
|
+
const discovered = (existsResult?.details?.discoveredFiles ?? []);
|
|
98
|
+
for (const file of discovered) {
|
|
99
|
+
urlsToCheck.push(file.url);
|
|
100
|
+
}
|
|
101
|
+
// Page URLs
|
|
102
|
+
const { urls: pageUrls, totalPages, sampled, warnings } = await discoverAndSamplePages(ctx);
|
|
103
|
+
for (const url of pageUrls) {
|
|
104
|
+
if (!urlsToCheck.includes(url)) {
|
|
105
|
+
urlsToCheck.push(url);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const results = [];
|
|
109
|
+
const concurrency = ctx.options.maxConcurrency;
|
|
110
|
+
for (let i = 0; i < urlsToCheck.length; i += concurrency) {
|
|
111
|
+
const batch = urlsToCheck.slice(i, i + concurrency);
|
|
112
|
+
const batchResults = await Promise.all(batch.map(async (url) => {
|
|
113
|
+
try {
|
|
114
|
+
const response = await ctx.http.fetch(url);
|
|
115
|
+
const ccHeader = response.headers.get('cache-control');
|
|
116
|
+
const parsed = parseCacheControl(ccHeader);
|
|
117
|
+
const etag = response.headers.get('etag');
|
|
118
|
+
const lastModified = response.headers.get('last-modified');
|
|
119
|
+
const expires = response.headers.get('expires');
|
|
120
|
+
const effectiveMaxAge = getEffectiveMaxAge(parsed, expires);
|
|
121
|
+
const partial = {
|
|
122
|
+
url,
|
|
123
|
+
cacheControl: ccHeader,
|
|
124
|
+
maxAge: parsed.maxAge,
|
|
125
|
+
sMaxAge: parsed.sMaxAge,
|
|
126
|
+
mustRevalidate: parsed.mustRevalidate,
|
|
127
|
+
noCache: parsed.noCache,
|
|
128
|
+
noStore: parsed.noStore,
|
|
129
|
+
etag,
|
|
130
|
+
lastModified,
|
|
131
|
+
expires,
|
|
132
|
+
effectiveMaxAge,
|
|
133
|
+
};
|
|
134
|
+
return { ...partial, status: classifyCache(partial) };
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
return {
|
|
138
|
+
url,
|
|
139
|
+
cacheControl: null,
|
|
140
|
+
maxAge: null,
|
|
141
|
+
sMaxAge: null,
|
|
142
|
+
mustRevalidate: false,
|
|
143
|
+
noCache: false,
|
|
144
|
+
noStore: false,
|
|
145
|
+
etag: null,
|
|
146
|
+
lastModified: null,
|
|
147
|
+
expires: null,
|
|
148
|
+
effectiveMaxAge: null,
|
|
149
|
+
status: 'fail',
|
|
150
|
+
error: err instanceof Error ? err.message : String(err),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}));
|
|
154
|
+
results.push(...batchResults);
|
|
155
|
+
}
|
|
156
|
+
const successful = results.filter((r) => !r.error);
|
|
157
|
+
const fetchErrors = results.filter((r) => r.error).length;
|
|
158
|
+
if (successful.length === 0) {
|
|
159
|
+
return {
|
|
160
|
+
id,
|
|
161
|
+
category,
|
|
162
|
+
status: 'fail',
|
|
163
|
+
message: `Could not fetch any endpoints to check cache headers${fetchErrors > 0 ? `; ${fetchErrors} failed to fetch` : ''}`,
|
|
164
|
+
details: {
|
|
165
|
+
testedEndpoints: results.length,
|
|
166
|
+
fetchErrors,
|
|
167
|
+
endpointResults: results,
|
|
168
|
+
discoveryWarnings: warnings,
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const overallStatus = worstStatus(successful.map((r) => r.status));
|
|
173
|
+
const passBucket = successful.filter((r) => r.status === 'pass').length;
|
|
174
|
+
const warnBucket = successful.filter((r) => r.status === 'warn').length;
|
|
175
|
+
const failBucket = successful.filter((r) => r.status === 'fail').length;
|
|
176
|
+
const suffix = fetchErrors > 0 ? `; ${fetchErrors} failed to fetch` : '';
|
|
177
|
+
let message;
|
|
178
|
+
if (overallStatus === 'pass') {
|
|
179
|
+
message = `All ${successful.length} endpoints have appropriate cache headers${suffix}`;
|
|
180
|
+
}
|
|
181
|
+
else if (overallStatus === 'warn') {
|
|
182
|
+
message = `${warnBucket} of ${successful.length} endpoints have moderate cache lifetimes (1–24 hours)${suffix}`;
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
message = `${failBucket} of ${successful.length} endpoints have aggressive caching or missing cache headers${suffix}`;
|
|
186
|
+
}
|
|
3
187
|
return {
|
|
4
|
-
id
|
|
5
|
-
category
|
|
6
|
-
status:
|
|
7
|
-
message
|
|
188
|
+
id,
|
|
189
|
+
category,
|
|
190
|
+
status: overallStatus,
|
|
191
|
+
message,
|
|
192
|
+
details: {
|
|
193
|
+
totalPages,
|
|
194
|
+
testedEndpoints: results.length,
|
|
195
|
+
sampled,
|
|
196
|
+
passBucket,
|
|
197
|
+
warnBucket,
|
|
198
|
+
failBucket,
|
|
199
|
+
fetchErrors,
|
|
200
|
+
endpointResults: results,
|
|
201
|
+
discoveryWarnings: warnings,
|
|
202
|
+
},
|
|
8
203
|
};
|
|
9
204
|
}
|
|
10
205
|
registerCheck({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-header-hygiene.js","sourceRoot":"","sources":["../../../src/checks/observability/cache-header-hygiene.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"cache-header-hygiene.js","sourceRoot":"","sources":["../../../src/checks/observability/cache-header-hygiene.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { registerCheck } from '../registry.js';
|
|
2
|
-
import {
|
|
3
|
-
import { discoverAndSamplePages } from '../../helpers/get-page-urls.js';
|
|
4
|
-
import { toMdUrls } from '../../helpers/to-md-urls.js';
|
|
2
|
+
import { getMarkdownContent } from '../../helpers/get-markdown-content.js';
|
|
5
3
|
function sizeStatus(chars, pass, fail) {
|
|
6
4
|
if (chars <= pass)
|
|
7
5
|
return 'pass';
|
|
@@ -16,132 +14,56 @@ function worstStatus(statuses) {
|
|
|
16
14
|
return 'warn';
|
|
17
15
|
return 'pass';
|
|
18
16
|
}
|
|
17
|
+
function formatSize(chars) {
|
|
18
|
+
if (chars >= 1000)
|
|
19
|
+
return `${Math.round(chars / 1000)}K`;
|
|
20
|
+
return String(chars);
|
|
21
|
+
}
|
|
19
22
|
async function check(ctx) {
|
|
20
23
|
const id = 'page-size-markdown';
|
|
21
24
|
const category = 'page-size';
|
|
22
25
|
const { pass: passThreshold, fail: failThreshold } = ctx.options.thresholds;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const cnResult = ctx.previousResults.get('content-negotiation');
|
|
26
|
-
const depRan = mdUrlResult || cnResult;
|
|
27
|
-
if (depRan) {
|
|
28
|
-
// At least one dependency check ran. Did either pass or warn?
|
|
29
|
-
const depPassed = (mdUrlResult && (mdUrlResult.status === 'pass' || mdUrlResult.status === 'warn')) ||
|
|
30
|
-
(cnResult && (cnResult.status === 'pass' || cnResult.status === 'warn'));
|
|
31
|
-
if (!depPassed) {
|
|
32
|
-
return {
|
|
33
|
-
id,
|
|
34
|
-
category,
|
|
35
|
-
status: 'skip',
|
|
36
|
-
message: 'Site does not serve markdown; skipping markdown size check',
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
// Build a map from page URL → markdown URL using dependency results
|
|
40
|
-
const mdUrlMap = new Map();
|
|
41
|
-
const mdUrlPages = mdUrlResult?.details?.pageResults;
|
|
42
|
-
if (mdUrlPages) {
|
|
43
|
-
for (const p of mdUrlPages) {
|
|
44
|
-
if (p.supported)
|
|
45
|
-
mdUrlMap.set(p.url, p.mdUrl);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
// Use cached pages
|
|
49
|
-
const pageResults = [];
|
|
50
|
-
for (const [, cached] of ctx.pageCache) {
|
|
51
|
-
if (cached.markdown) {
|
|
52
|
-
const chars = cached.markdown.content.length;
|
|
53
|
-
const resolvedMdUrl = cached.markdown.source === 'md-url'
|
|
54
|
-
? (mdUrlMap.get(cached.url) ?? cached.url)
|
|
55
|
-
: cached.url;
|
|
56
|
-
pageResults.push({
|
|
57
|
-
url: cached.url,
|
|
58
|
-
mdUrl: resolvedMdUrl,
|
|
59
|
-
characters: chars,
|
|
60
|
-
status: sizeStatus(chars, passThreshold, failThreshold),
|
|
61
|
-
source: 'cache',
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
if (pageResults.length === 0) {
|
|
66
|
-
return {
|
|
67
|
-
id,
|
|
68
|
-
category,
|
|
69
|
-
status: 'skip',
|
|
70
|
-
message: 'No cached markdown pages available to measure',
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
return buildResult(id, category, pageResults, pageResults.length, false, passThreshold, failThreshold, []);
|
|
74
|
-
}
|
|
75
|
-
// Standalone mode: dependency checks never ran
|
|
76
|
-
const { urls: pageUrls, totalPages, sampled: wasSampled, warnings, } = await discoverAndSamplePages(ctx);
|
|
77
|
-
const pageResults = [];
|
|
78
|
-
const concurrency = ctx.options.maxConcurrency;
|
|
79
|
-
for (let i = 0; i < pageUrls.length; i += concurrency) {
|
|
80
|
-
const batch = pageUrls.slice(i, i + concurrency);
|
|
81
|
-
const batchResults = await Promise.all(batch.map(async (url) => {
|
|
82
|
-
// Try .md URL candidates
|
|
83
|
-
const candidates = toMdUrls(url);
|
|
84
|
-
for (const candidateUrl of candidates) {
|
|
85
|
-
try {
|
|
86
|
-
const response = await ctx.http.fetch(candidateUrl);
|
|
87
|
-
if (!response.ok)
|
|
88
|
-
continue;
|
|
89
|
-
const body = await response.text();
|
|
90
|
-
if (looksLikeMarkdown(body)) {
|
|
91
|
-
const chars = body.length;
|
|
92
|
-
return {
|
|
93
|
-
url,
|
|
94
|
-
mdUrl: candidateUrl,
|
|
95
|
-
characters: chars,
|
|
96
|
-
status: sizeStatus(chars, passThreshold, failThreshold),
|
|
97
|
-
source: 'fallback',
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
// Try next candidate
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
// Try content negotiation
|
|
106
|
-
try {
|
|
107
|
-
const response = await ctx.http.fetch(url, {
|
|
108
|
-
headers: { Accept: 'text/markdown' },
|
|
109
|
-
});
|
|
110
|
-
if (response.ok) {
|
|
111
|
-
const body = await response.text();
|
|
112
|
-
if (looksLikeMarkdown(body)) {
|
|
113
|
-
const chars = body.length;
|
|
114
|
-
return {
|
|
115
|
-
url,
|
|
116
|
-
mdUrl: url,
|
|
117
|
-
characters: chars,
|
|
118
|
-
status: sizeStatus(chars, passThreshold, failThreshold),
|
|
119
|
-
source: 'fallback',
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
catch {
|
|
125
|
-
// No markdown available for this page
|
|
126
|
-
}
|
|
127
|
-
return null;
|
|
128
|
-
}));
|
|
129
|
-
for (const r of batchResults) {
|
|
130
|
-
if (r)
|
|
131
|
-
pageResults.push(r);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
if (pageResults.length === 0) {
|
|
26
|
+
const mdResult = await getMarkdownContent(ctx);
|
|
27
|
+
if (mdResult.mode === 'cached' && !mdResult.depPassed) {
|
|
135
28
|
return {
|
|
136
29
|
id,
|
|
137
30
|
category,
|
|
138
31
|
status: 'skip',
|
|
139
|
-
message: '
|
|
32
|
+
message: 'Site does not serve markdown; skipping markdown size check',
|
|
140
33
|
};
|
|
141
34
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
35
|
+
if (mdResult.pages.length === 0) {
|
|
36
|
+
const hint = mdResult.mode === 'standalone'
|
|
37
|
+
? 'No markdown content found; skipping size check'
|
|
38
|
+
: 'No cached markdown pages available to measure';
|
|
39
|
+
return { id, category, status: 'skip', message: hint };
|
|
40
|
+
}
|
|
41
|
+
// Build mdUrl map from markdown-url-support results for richer reporting
|
|
42
|
+
const mdUrlMap = new Map();
|
|
43
|
+
const mdUrlResult = ctx.previousResults.get('markdown-url-support');
|
|
44
|
+
const mdUrlPages = mdUrlResult?.details?.pageResults;
|
|
45
|
+
if (mdUrlPages) {
|
|
46
|
+
for (const p of mdUrlPages) {
|
|
47
|
+
if (p.supported)
|
|
48
|
+
mdUrlMap.set(p.url, p.mdUrl);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const pageResults = mdResult.pages
|
|
52
|
+
.filter((p) => p.source !== 'llms-txt') // llms.txt files aren't doc pages to size-check
|
|
53
|
+
.map((page) => {
|
|
54
|
+
const chars = page.content.length;
|
|
55
|
+
const mdUrl = mdUrlMap.get(page.url) ?? page.url;
|
|
56
|
+
return {
|
|
57
|
+
url: page.url,
|
|
58
|
+
mdUrl,
|
|
59
|
+
characters: chars,
|
|
60
|
+
status: sizeStatus(chars, passThreshold, failThreshold),
|
|
61
|
+
source: page.source,
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
if (pageResults.length === 0) {
|
|
65
|
+
return { id, category, status: 'skip', message: 'No markdown pages available to measure' };
|
|
66
|
+
}
|
|
145
67
|
const sizes = pageResults.map((r) => r.characters).sort((a, b) => a - b);
|
|
146
68
|
const median = sizes[Math.floor(sizes.length / 2)];
|
|
147
69
|
const max = sizes[sizes.length - 1];
|
|
@@ -149,12 +71,8 @@ function buildResult(id, category, pageResults, totalPages, sampled, passThresho
|
|
|
149
71
|
const warnBucket = pageResults.filter((r) => r.status === 'warn').length;
|
|
150
72
|
const failBucket = pageResults.filter((r) => r.status === 'fail').length;
|
|
151
73
|
const overallStatus = worstStatus(pageResults.map((r) => r.status));
|
|
74
|
+
const sampled = mdResult.mode === 'standalone'; // standalone always samples via discoverAndSamplePages
|
|
152
75
|
const pageLabel = sampled ? 'sampled pages' : 'pages';
|
|
153
|
-
const formatSize = (chars) => {
|
|
154
|
-
if (chars >= 1000)
|
|
155
|
-
return `${Math.round(chars / 1000)}K`;
|
|
156
|
-
return String(chars);
|
|
157
|
-
};
|
|
158
76
|
let message;
|
|
159
77
|
if (overallStatus === 'pass') {
|
|
160
78
|
message = `All ${pageResults.length} ${pageLabel} under ${formatSize(passThreshold)} chars (median ${formatSize(median)}, max ${formatSize(max)})`;
|
|
@@ -171,7 +89,7 @@ function buildResult(id, category, pageResults, totalPages, sampled, passThresho
|
|
|
171
89
|
status: overallStatus,
|
|
172
90
|
message,
|
|
173
91
|
details: {
|
|
174
|
-
totalPages,
|
|
92
|
+
totalPages: pageResults.length,
|
|
175
93
|
testedPages: pageResults.length,
|
|
176
94
|
sampled,
|
|
177
95
|
median,
|
|
@@ -181,7 +99,6 @@ function buildResult(id, category, pageResults, totalPages, sampled, passThresho
|
|
|
181
99
|
failBucket,
|
|
182
100
|
thresholds: { pass: passThreshold, fail: failThreshold },
|
|
183
101
|
pageResults,
|
|
184
|
-
discoveryWarnings,
|
|
185
102
|
},
|
|
186
103
|
};
|
|
187
104
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-size-markdown.js","sourceRoot":"","sources":["../../../src/checks/page-size/page-size-markdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"page-size-markdown.js","sourceRoot":"","sources":["../../../src/checks/page-size/page-size-markdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,111 @@
|
|
|
1
1
|
import { registerCheck } from '../registry.js';
|
|
2
|
-
|
|
2
|
+
import { discoverAndSamplePages } from '../../helpers/get-page-urls.js';
|
|
3
|
+
/** Generate a sibling URL that almost certainly doesn't exist. */
|
|
4
|
+
function makeBadUrl(pageUrl) {
|
|
5
|
+
const u = new URL(pageUrl);
|
|
6
|
+
u.hash = ''; // strip fragment — servers don't see it anyway
|
|
7
|
+
u.pathname = u.pathname.replace(/\/?$/, '-afdocs-nonexistent-8f3a');
|
|
8
|
+
return u.toString();
|
|
9
|
+
}
|
|
10
|
+
const SOFT_404_PATTERNS = /not\s*found|page\s*not\s*found|404|does\s*not\s*exist/i;
|
|
11
|
+
async function check(ctx) {
|
|
12
|
+
const id = 'http-status-codes';
|
|
13
|
+
const category = 'url-stability';
|
|
14
|
+
const { urls: pageUrls, totalPages, sampled, warnings } = await discoverAndSamplePages(ctx);
|
|
15
|
+
const results = [];
|
|
16
|
+
const concurrency = ctx.options.maxConcurrency;
|
|
17
|
+
for (let i = 0; i < pageUrls.length; i += concurrency) {
|
|
18
|
+
const batch = pageUrls.slice(i, i + concurrency);
|
|
19
|
+
const batchResults = await Promise.all(batch.map(async (url) => {
|
|
20
|
+
const testUrl = makeBadUrl(url);
|
|
21
|
+
try {
|
|
22
|
+
// Follow redirects so we classify based on the final response.
|
|
23
|
+
// A redirect chain ending in 404 is correct; one ending in 200 is a soft 404.
|
|
24
|
+
const response = await ctx.http.fetch(testUrl);
|
|
25
|
+
const status = response.status;
|
|
26
|
+
const redirected = response.redirected || response.url !== testUrl;
|
|
27
|
+
const finalUrl = redirected ? response.url : undefined;
|
|
28
|
+
if (status >= 400) {
|
|
29
|
+
return { url, testUrl, status, classification: 'correct-error', redirected, finalUrl };
|
|
30
|
+
}
|
|
31
|
+
// Status 200 (or other 2xx/3xx) — possible soft 404
|
|
32
|
+
let bodyHint;
|
|
33
|
+
try {
|
|
34
|
+
const body = await response.text();
|
|
35
|
+
if (SOFT_404_PATTERNS.test(body.slice(0, 5000))) {
|
|
36
|
+
bodyHint = 'Body contains "not found" / "404" text';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// ignore body read errors
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
url,
|
|
44
|
+
testUrl,
|
|
45
|
+
status,
|
|
46
|
+
classification: 'soft-404',
|
|
47
|
+
redirected,
|
|
48
|
+
finalUrl,
|
|
49
|
+
bodyHint,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
return {
|
|
54
|
+
url,
|
|
55
|
+
testUrl,
|
|
56
|
+
status: null,
|
|
57
|
+
classification: 'fetch-error',
|
|
58
|
+
error: err instanceof Error ? err.message : String(err),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}));
|
|
62
|
+
results.push(...batchResults);
|
|
63
|
+
}
|
|
64
|
+
const tested = results.filter((r) => r.classification !== 'fetch-error');
|
|
65
|
+
const fetchErrors = results.filter((r) => r.classification === 'fetch-error').length;
|
|
66
|
+
const soft404s = results.filter((r) => r.classification === 'soft-404');
|
|
67
|
+
const correctErrors = results.filter((r) => r.classification === 'correct-error');
|
|
68
|
+
if (tested.length === 0) {
|
|
69
|
+
return {
|
|
70
|
+
id,
|
|
71
|
+
category,
|
|
72
|
+
status: 'fail',
|
|
73
|
+
message: `Could not test any URLs${fetchErrors > 0 ? `; ${fetchErrors} failed to fetch` : ''}`,
|
|
74
|
+
details: {
|
|
75
|
+
totalPages,
|
|
76
|
+
testedPages: results.length,
|
|
77
|
+
sampled,
|
|
78
|
+
fetchErrors,
|
|
79
|
+
pageResults: results,
|
|
80
|
+
discoveryWarnings: warnings,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const status = soft404s.length > 0 ? 'fail' : 'pass';
|
|
85
|
+
const pageLabel = sampled ? 'sampled pages' : 'pages';
|
|
86
|
+
const suffix = fetchErrors > 0 ? `; ${fetchErrors} failed to fetch` : '';
|
|
87
|
+
let message;
|
|
88
|
+
if (status === 'pass') {
|
|
89
|
+
message = `All ${tested.length} ${pageLabel} return proper error codes for bad URLs${suffix}`;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
message = `${soft404s.length} of ${tested.length} ${pageLabel} return 200 for non-existent URLs (soft 404)${suffix}`;
|
|
93
|
+
}
|
|
3
94
|
return {
|
|
4
|
-
id
|
|
5
|
-
category
|
|
6
|
-
status
|
|
7
|
-
message
|
|
95
|
+
id,
|
|
96
|
+
category,
|
|
97
|
+
status,
|
|
98
|
+
message,
|
|
99
|
+
details: {
|
|
100
|
+
totalPages,
|
|
101
|
+
testedPages: results.length,
|
|
102
|
+
sampled,
|
|
103
|
+
soft404Count: soft404s.length,
|
|
104
|
+
correctErrorCount: correctErrors.length,
|
|
105
|
+
fetchErrors,
|
|
106
|
+
pageResults: results,
|
|
107
|
+
discoveryWarnings: warnings,
|
|
108
|
+
},
|
|
8
109
|
};
|
|
9
110
|
}
|
|
10
111
|
registerCheck({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-status-codes.js","sourceRoot":"","sources":["../../../src/checks/url-stability/http-status-codes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"http-status-codes.js","sourceRoot":"","sources":["../../../src/checks/url-stability/http-status-codes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CheckContext } from '../types.js';
|
|
2
|
+
export interface MarkdownPage {
|
|
3
|
+
url: string;
|
|
4
|
+
content: string;
|
|
5
|
+
source: string;
|
|
6
|
+
}
|
|
7
|
+
export type MarkdownContentResult = {
|
|
8
|
+
mode: 'cached';
|
|
9
|
+
pages: MarkdownPage[];
|
|
10
|
+
depPassed: boolean;
|
|
11
|
+
} | {
|
|
12
|
+
mode: 'standalone';
|
|
13
|
+
pages: MarkdownPage[];
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Get markdown content for analysis, either from dependency cache or by fetching directly.
|
|
17
|
+
*
|
|
18
|
+
* When markdown-url-support or content-negotiation have already run, reads from
|
|
19
|
+
* ctx.pageCache (fast). Otherwise, discovers pages and probes for markdown itself.
|
|
20
|
+
* Always includes llms.txt content when available.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getMarkdownContent(ctx: CheckContext): Promise<MarkdownContentResult>;
|
|
23
|
+
//# sourceMappingURL=get-markdown-content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-markdown-content.d.ts","sourceRoot":"","sources":["../../src/helpers/get-markdown-content.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,aAAa,CAAC;AAEhE,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,YAAY,EAAE,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,YAAY,EAAE,CAAA;CAAE,CAAC;AAElD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAmB1F"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { looksLikeMarkdown } from './detect-markdown.js';
|
|
2
|
+
import { discoverAndSamplePages } from './get-page-urls.js';
|
|
3
|
+
import { toMdUrls } from './to-md-urls.js';
|
|
4
|
+
/**
|
|
5
|
+
* Get markdown content for analysis, either from dependency cache or by fetching directly.
|
|
6
|
+
*
|
|
7
|
+
* When markdown-url-support or content-negotiation have already run, reads from
|
|
8
|
+
* ctx.pageCache (fast). Otherwise, discovers pages and probes for markdown itself.
|
|
9
|
+
* Always includes llms.txt content when available.
|
|
10
|
+
*/
|
|
11
|
+
export async function getMarkdownContent(ctx) {
|
|
12
|
+
const mdUrlResult = ctx.previousResults.get('markdown-url-support');
|
|
13
|
+
const cnResult = ctx.previousResults.get('content-negotiation');
|
|
14
|
+
const depRan = mdUrlResult || cnResult;
|
|
15
|
+
const llmsTxtPages = collectLlmsTxtContent(ctx);
|
|
16
|
+
if (depRan) {
|
|
17
|
+
const depPassed = (mdUrlResult && (mdUrlResult.status === 'pass' || mdUrlResult.status === 'warn')) ||
|
|
18
|
+
(cnResult && (cnResult.status === 'pass' || cnResult.status === 'warn'));
|
|
19
|
+
const cachedPages = collectCachedPages(ctx);
|
|
20
|
+
return { mode: 'cached', pages: [...cachedPages, ...llmsTxtPages], depPassed: !!depPassed };
|
|
21
|
+
}
|
|
22
|
+
// Standalone mode: fetch markdown ourselves
|
|
23
|
+
const fetchedPages = await fetchMarkdownPages(ctx);
|
|
24
|
+
return { mode: 'standalone', pages: [...fetchedPages, ...llmsTxtPages] };
|
|
25
|
+
}
|
|
26
|
+
function collectCachedPages(ctx) {
|
|
27
|
+
const pages = [];
|
|
28
|
+
for (const [url, cached] of ctx.pageCache) {
|
|
29
|
+
if (cached.markdown?.content) {
|
|
30
|
+
pages.push({ url, content: cached.markdown.content, source: cached.markdown.source });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return pages;
|
|
34
|
+
}
|
|
35
|
+
function collectLlmsTxtContent(ctx) {
|
|
36
|
+
const existsResult = ctx.previousResults.get('llms-txt-exists');
|
|
37
|
+
const discovered = (existsResult?.details?.discoveredFiles ?? []);
|
|
38
|
+
const pages = [];
|
|
39
|
+
for (const file of discovered) {
|
|
40
|
+
if (file.content) {
|
|
41
|
+
pages.push({ url: file.url, content: file.content, source: 'llms-txt' });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return pages;
|
|
45
|
+
}
|
|
46
|
+
async function fetchMarkdownPages(ctx) {
|
|
47
|
+
const pages = [];
|
|
48
|
+
const { urls: pageUrls } = await discoverAndSamplePages(ctx);
|
|
49
|
+
const concurrency = ctx.options.maxConcurrency;
|
|
50
|
+
for (let i = 0; i < pageUrls.length; i += concurrency) {
|
|
51
|
+
const batch = pageUrls.slice(i, i + concurrency);
|
|
52
|
+
const batchResults = await Promise.all(batch.map(async (url) => {
|
|
53
|
+
// Try .md URL candidates
|
|
54
|
+
const candidates = toMdUrls(url);
|
|
55
|
+
for (const candidateUrl of candidates) {
|
|
56
|
+
try {
|
|
57
|
+
const response = await ctx.http.fetch(candidateUrl);
|
|
58
|
+
if (!response.ok)
|
|
59
|
+
continue;
|
|
60
|
+
const body = await response.text();
|
|
61
|
+
if (looksLikeMarkdown(body)) {
|
|
62
|
+
return { url: candidateUrl, content: body, source: 'standalone-md-url' };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Try next candidate
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Try content negotiation
|
|
70
|
+
try {
|
|
71
|
+
const response = await ctx.http.fetch(url, {
|
|
72
|
+
headers: { Accept: 'text/markdown' },
|
|
73
|
+
});
|
|
74
|
+
if (response.ok) {
|
|
75
|
+
const body = await response.text();
|
|
76
|
+
if (looksLikeMarkdown(body)) {
|
|
77
|
+
return { url, content: body, source: 'standalone-content-negotiation' };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// No markdown available for this page
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}));
|
|
86
|
+
for (const r of batchResults) {
|
|
87
|
+
if (r)
|
|
88
|
+
pages.push(r);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return pages;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=get-markdown-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-markdown-content.js","sourceRoot":"","sources":["../../src/helpers/get-markdown-content.ts"],"names":[],"mappings":"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"}
|
|
@@ -34,6 +34,9 @@ export interface SampledPages {
|
|
|
34
34
|
/**
|
|
35
35
|
* Discover page URLs and sample down to maxLinksToTest if needed.
|
|
36
36
|
* Consolidates the discover+shuffle+sample pattern used by multiple checks.
|
|
37
|
+
*
|
|
38
|
+
* The result is cached on ctx so that all checks within a single run
|
|
39
|
+
* share the same sampled page list, avoiding inconsistent results.
|
|
37
40
|
*/
|
|
38
41
|
export declare function discoverAndSamplePages(ctx: CheckContext): Promise<SampledPages>;
|
|
39
42
|
//# sourceMappingURL=get-page-urls.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-page-urls.d.ts","sourceRoot":"","sources":["../../src/helpers/get-page-urls.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,aAAa,CAAC;AAEhE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,gBAAgB,EAAE,MAAM,EAAE,CAAA;CAAE,CA2B5F;AAyDD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CASlE;AAsBD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAyED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAmB3E;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED
|
|
1
|
+
{"version":3,"file":"get-page-urls.d.ts","sourceRoot":"","sources":["../../src/helpers/get-page-urls.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,aAAa,CAAC;AAEhE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,gBAAgB,EAAE,MAAM,EAAE,CAAA;CAAE,CA2B5F;AAyDD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CASlE;AAsBD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAyED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAmB3E;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAkBrF"}
|
|
@@ -212,8 +212,13 @@ export async function getPageUrls(ctx) {
|
|
|
212
212
|
/**
|
|
213
213
|
* Discover page URLs and sample down to maxLinksToTest if needed.
|
|
214
214
|
* Consolidates the discover+shuffle+sample pattern used by multiple checks.
|
|
215
|
+
*
|
|
216
|
+
* The result is cached on ctx so that all checks within a single run
|
|
217
|
+
* share the same sampled page list, avoiding inconsistent results.
|
|
215
218
|
*/
|
|
216
219
|
export async function discoverAndSamplePages(ctx) {
|
|
220
|
+
if (ctx._sampledPages)
|
|
221
|
+
return ctx._sampledPages;
|
|
217
222
|
const discovery = await getPageUrls(ctx);
|
|
218
223
|
let urls = discovery.urls;
|
|
219
224
|
const totalPages = urls.length;
|
|
@@ -225,6 +230,7 @@ export async function discoverAndSamplePages(ctx) {
|
|
|
225
230
|
}
|
|
226
231
|
urls = urls.slice(0, ctx.options.maxLinksToTest);
|
|
227
232
|
}
|
|
228
|
-
|
|
233
|
+
ctx._sampledPages = { urls, totalPages, sampled, warnings: discovery.warnings };
|
|
234
|
+
return ctx._sampledPages;
|
|
229
235
|
}
|
|
230
236
|
//# sourceMappingURL=get-page-urls.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-page-urls.js","sourceRoot":"","sources":["../../src/helpers/get-page-urls.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"get-page-urls.js","sourceRoot":"","sources":["../../src/helpers/get-page-urls.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SampledPages } from './helpers/get-page-urls.js';
|
|
1
2
|
export type CheckStatus = 'pass' | 'warn' | 'fail' | 'skip' | 'error';
|
|
2
3
|
export interface CheckResult {
|
|
3
4
|
id: string;
|
|
@@ -27,6 +28,8 @@ export interface CheckContext {
|
|
|
27
28
|
options: CheckOptions;
|
|
28
29
|
/** Cached page content, keyed by original page URL. */
|
|
29
30
|
pageCache: Map<string, CachedPage>;
|
|
31
|
+
/** Cached sampled pages result, shared across checks within a single run. */
|
|
32
|
+
_sampledPages?: SampledPages;
|
|
30
33
|
}
|
|
31
34
|
export interface CheckOptions {
|
|
32
35
|
/** Maximum concurrent HTTP requests within a single check. */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEtE,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE;QACT,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,QAAQ,GAAG,qBAAqB,CAAC;KAC1C,CAAC;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC1C,sCAAsC;IACtC,IAAI,EAAE,UAAU,CAAC;IACjB,uBAAuB;IACvB,OAAO,EAAE,YAAY,CAAC;IACtB,uDAAuD;IACvD,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|