afdocs 0.1.3 → 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.
Files changed (76) hide show
  1. package/README.md +48 -24
  2. package/dist/checks/authentication/auth-alternative-access.d.ts +2 -0
  3. package/dist/checks/authentication/auth-alternative-access.d.ts.map +1 -0
  4. package/dist/checks/authentication/auth-alternative-access.js +17 -0
  5. package/dist/checks/authentication/auth-alternative-access.js.map +1 -0
  6. package/dist/checks/authentication/auth-gate-detection.d.ts +2 -0
  7. package/dist/checks/authentication/auth-gate-detection.d.ts.map +1 -0
  8. package/dist/checks/authentication/auth-gate-detection.js +175 -0
  9. package/dist/checks/authentication/auth-gate-detection.js.map +1 -0
  10. package/dist/checks/content-structure/markdown-code-fence-validity.js +119 -5
  11. package/dist/checks/content-structure/markdown-code-fence-validity.js.map +1 -1
  12. package/dist/checks/index.d.ts +2 -0
  13. package/dist/checks/index.d.ts.map +1 -1
  14. package/dist/checks/index.js +3 -0
  15. package/dist/checks/index.js.map +1 -1
  16. package/dist/checks/llms-txt/llms-txt-exists.js +83 -6
  17. package/dist/checks/llms-txt/llms-txt-exists.js.map +1 -1
  18. package/dist/checks/llms-txt/llms-txt-links-markdown.js +46 -21
  19. package/dist/checks/llms-txt/llms-txt-links-markdown.js.map +1 -1
  20. package/dist/checks/llms-txt/llms-txt-links-resolve.js +10 -3
  21. package/dist/checks/llms-txt/llms-txt-links-resolve.js.map +1 -1
  22. package/dist/checks/markdown-availability/content-negotiation.js +101 -5
  23. package/dist/checks/markdown-availability/content-negotiation.js.map +1 -1
  24. package/dist/checks/markdown-availability/markdown-url-support.js +82 -5
  25. package/dist/checks/markdown-availability/markdown-url-support.js.map +1 -1
  26. package/dist/checks/observability/cache-header-hygiene.js +200 -5
  27. package/dist/checks/observability/cache-header-hygiene.js.map +1 -1
  28. package/dist/checks/page-size/content-start-position.js +161 -5
  29. package/dist/checks/page-size/content-start-position.js.map +1 -1
  30. package/dist/checks/page-size/page-size-html.js +127 -5
  31. package/dist/checks/page-size/page-size-html.js.map +1 -1
  32. package/dist/checks/page-size/page-size-markdown.js +100 -5
  33. package/dist/checks/page-size/page-size-markdown.js.map +1 -1
  34. package/dist/checks/url-stability/http-status-codes.js +106 -5
  35. package/dist/checks/url-stability/http-status-codes.js.map +1 -1
  36. package/dist/cli/commands/check.d.ts.map +1 -1
  37. package/dist/cli/commands/check.js +9 -1
  38. package/dist/cli/commands/check.js.map +1 -1
  39. package/dist/cli/formatters/text.d.ts +4 -1
  40. package/dist/cli/formatters/text.d.ts.map +1 -1
  41. package/dist/cli/formatters/text.js +94 -1
  42. package/dist/cli/formatters/text.js.map +1 -1
  43. package/dist/constants.d.ts +6 -0
  44. package/dist/constants.d.ts.map +1 -1
  45. package/dist/constants.js +3 -0
  46. package/dist/constants.js.map +1 -1
  47. package/dist/helpers/detect-markdown.d.ts +10 -0
  48. package/dist/helpers/detect-markdown.d.ts.map +1 -0
  49. package/dist/helpers/detect-markdown.js +29 -0
  50. package/dist/helpers/detect-markdown.js.map +1 -0
  51. package/dist/helpers/get-markdown-content.d.ts +23 -0
  52. package/dist/helpers/get-markdown-content.d.ts.map +1 -0
  53. package/dist/helpers/get-markdown-content.js +93 -0
  54. package/dist/helpers/get-markdown-content.js.map +1 -0
  55. package/dist/helpers/get-page-urls.d.ts +42 -0
  56. package/dist/helpers/get-page-urls.d.ts.map +1 -0
  57. package/dist/helpers/get-page-urls.js +236 -0
  58. package/dist/helpers/get-page-urls.js.map +1 -0
  59. package/dist/helpers/html-to-markdown.d.ts +7 -0
  60. package/dist/helpers/html-to-markdown.d.ts.map +1 -0
  61. package/dist/helpers/html-to-markdown.js +11 -0
  62. package/dist/helpers/html-to-markdown.js.map +1 -0
  63. package/dist/helpers/index.d.ts +5 -0
  64. package/dist/helpers/index.d.ts.map +1 -1
  65. package/dist/helpers/index.js +4 -0
  66. package/dist/helpers/index.js.map +1 -1
  67. package/dist/helpers/to-md-urls.d.ts +7 -0
  68. package/dist/helpers/to-md-urls.d.ts.map +1 -0
  69. package/dist/helpers/to-md-urls.js +24 -0
  70. package/dist/helpers/to-md-urls.js.map +1 -0
  71. package/dist/runner.d.ts.map +1 -1
  72. package/dist/runner.js +21 -14
  73. package/dist/runner.js.map +1 -1
  74. package/dist/types.d.ts +13 -1
  75. package/dist/types.d.ts.map +1 -1
  76. package/package.json +4 -2
package/README.md CHANGED
@@ -7,7 +7,12 @@ 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 19 checks across 7 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
+
12
+ > **Status: Early development (0.x)**
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.
14
+ >
15
+ > Implements [spec v0.1.0](https://agentdocsspec.com/spec) (2026-02-22).
11
16
 
12
17
  ## Quick start
13
18
 
@@ -27,8 +32,18 @@ llms-txt
27
32
  ✓ llms-txt-links-resolve: All 50 tested links resolve (177 total links)
28
33
  ✓ llms-txt-links-markdown: 50/50 links point to markdown content (100%)
29
34
 
35
+ Markdown Availability
36
+ ✗ content-negotiation: Server ignores Accept: text/markdown header (0/50 sampled pages return markdown)
37
+ ✗ markdown-url-support: No sampled pages support .md URLs (0/50 tested)
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
+
30
45
  Summary
31
- 5 passed, 14 skipped (19 total)
46
+ 9 passed, 3 failed, 9 skipped (21 total)
32
47
  ```
33
48
 
34
49
  ## Install
@@ -55,15 +70,16 @@ afdocs check https://docs.example.com --pass-threshold 30000 --fail-threshold 80
55
70
 
56
71
  ### Options
57
72
 
58
- | Option | Default | Description |
59
- | ----------------------- | -------- | ------------------------------------ |
60
- | `--format <format>` | `text` | Output format: `text` or `json` |
61
- | `--checks <ids>` | all | Comma-separated list of check IDs |
62
- | `--max-concurrency <n>` | `3` | Maximum concurrent HTTP requests |
63
- | `--request-delay <ms>` | `200` | Delay between requests |
64
- | `--max-links <n>` | `50` | Maximum links to test in link checks |
65
- | `--pass-threshold <n>` | `50000` | Size pass threshold (characters) |
66
- | `--fail-threshold <n>` | `100000` | Size fail threshold (characters) |
73
+ | Option | Default | Description |
74
+ | ----------------------- | -------- | -------------------------------------------- |
75
+ | `--format <format>` | `text` | Output format: `text` or `json` |
76
+ | `-v, --verbose` | | Show per-page details for checks with issues |
77
+ | `--checks <ids>` | all | Comma-separated list of check IDs |
78
+ | `--max-concurrency <n>` | `3` | Maximum concurrent HTTP requests |
79
+ | `--request-delay <ms>` | `200` | Delay between requests |
80
+ | `--max-links <n>` | `50` | Maximum links to test in link checks |
81
+ | `--pass-threshold <n>` | `50000` | Size pass threshold (characters) |
82
+ | `--fail-threshold <n>` | `100000` | Size fail threshold (characters) |
67
83
 
68
84
  ### Exit codes
69
85
 
@@ -128,7 +144,7 @@ describe('agent-friendliness', () => {
128
144
 
129
145
  ## Checks
130
146
 
131
- 19 checks across 7 categories. Checks marked with \* are not yet implemented and will return `skip`.
147
+ 21 checks across 8 categories. Checks marked with \* are not yet implemented and return `skip`.
132
148
 
133
149
  ### Category 1: llms.txt
134
150
 
@@ -142,18 +158,18 @@ describe('agent-friendliness', () => {
142
158
 
143
159
  ### Category 2: Markdown Availability
144
160
 
145
- | Check | Description |
146
- | ------------------------- | ------------------------------------------------- |
147
- | `markdown-url-support` \* | Whether `.md` URL variants return markdown |
148
- | `content-negotiation` \* | Whether the server honors `Accept: text/markdown` |
161
+ | Check | Description |
162
+ | ---------------------- | ------------------------------------------------- |
163
+ | `markdown-url-support` | Whether `.md` URL variants return markdown |
164
+ | `content-negotiation` | Whether the server honors `Accept: text/markdown` |
149
165
 
150
166
  ### Category 3: Page Size and Truncation Risk
151
167
 
152
- | Check | Description |
153
- | --------------------------- | ------------------------------------------------ |
154
- | `page-size-markdown` \* | Character count when served as markdown |
155
- | `page-size-html` \* | Character count of HTML and post-conversion size |
156
- | `content-start-position` \* | How far into the response actual content begins |
168
+ | Check | Description |
169
+ | ------------------------ | ------------------------------------------------ |
170
+ | `page-size-markdown` | Character count when served as markdown |
171
+ | `page-size-html` | Character count of HTML and post-conversion size |
172
+ | `content-start-position` | How far into the response actual content begins |
157
173
 
158
174
  ### Category 4: Content Structure
159
175
 
@@ -161,13 +177,13 @@ describe('agent-friendliness', () => {
161
177
  | --------------------------------- | -------------------------------------------------- |
162
178
  | `tabbed-content-serialization` \* | Whether tabbed content creates oversized output |
163
179
  | `section-header-quality` \* | Whether headers in tabbed sections include context |
164
- | `markdown-code-fence-validity` \* | Whether markdown has unclosed code fences |
180
+ | `markdown-code-fence-validity` | Whether markdown has unclosed code fences |
165
181
 
166
182
  ### Category 5: URL Stability and Redirects
167
183
 
168
184
  | Check | Description |
169
185
  | ---------------------- | ----------------------------------------------- |
170
- | `http-status-codes` \* | Whether error pages return correct status codes |
186
+ | `http-status-codes` | Whether error pages return correct status codes |
171
187
  | `redirect-behavior` \* | Whether redirects are same-host HTTP redirects |
172
188
 
173
189
  ### Category 6: Agent Discoverability Directives
@@ -182,7 +198,14 @@ describe('agent-friendliness', () => {
182
198
  | ---------------------------- | ---------------------------------------------- |
183
199
  | `llms-txt-freshness` \* | Whether `llms.txt` reflects current site state |
184
200
  | `markdown-content-parity` \* | Whether markdown and HTML versions match |
185
- | `cache-header-hygiene` \* | Whether cache headers allow timely updates |
201
+ | `cache-header-hygiene` | Whether cache headers allow timely updates |
202
+
203
+ ### Category 8: Authentication and Access
204
+
205
+ | Check | Description |
206
+ | ---------------------------- | -------------------------------------------------------------------- |
207
+ | `auth-gate-detection` | Whether documentation pages require authentication to access content |
208
+ | `auth-alternative-access` \* | Whether auth-gated sites provide alternative access paths for agents |
186
209
 
187
210
  ## Check dependencies
188
211
 
@@ -194,6 +217,7 @@ Some checks depend on others. If a dependency doesn't pass, the dependent check
194
217
  - `markdown-code-fence-validity` requires `markdown-url-support` or `content-negotiation`
195
218
  - `llms-txt-freshness` requires `llms-txt-exists`
196
219
  - `markdown-content-parity` requires `markdown-url-support` or `content-negotiation`
220
+ - `auth-alternative-access` requires `auth-gate-detection` (warn or fail)
197
221
 
198
222
  ## Responsible use
199
223
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=auth-alternative-access.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-alternative-access.d.ts","sourceRoot":"","sources":["../../../src/checks/authentication/auth-alternative-access.ts"],"names":[],"mappings":""}
@@ -0,0 +1,17 @@
1
+ import { registerCheck } from '../registry.js';
2
+ async function check(_ctx) {
3
+ return {
4
+ id: 'auth-alternative-access',
5
+ category: 'authentication',
6
+ status: 'skip',
7
+ message: 'Not yet implemented',
8
+ };
9
+ }
10
+ registerCheck({
11
+ id: 'auth-alternative-access',
12
+ category: 'authentication',
13
+ description: 'Whether an auth-gated documentation site provides alternative access paths for agents',
14
+ dependsOn: [['auth-gate-detection']],
15
+ run: check,
16
+ });
17
+ //# sourceMappingURL=auth-alternative-access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-alternative-access.js","sourceRoot":"","sources":["../../../src/checks/authentication/auth-alternative-access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAG/C,KAAK,UAAU,KAAK,CAAC,IAAkB;IACrC,OAAO;QACL,EAAE,EAAE,yBAAyB;QAC7B,QAAQ,EAAE,gBAAgB;QAC1B,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,qBAAqB;KAC/B,CAAC;AACJ,CAAC;AAED,aAAa,CAAC;IACZ,EAAE,EAAE,yBAAyB;IAC7B,QAAQ,EAAE,gBAAgB;IAC1B,WAAW,EACT,uFAAuF;IACzF,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC,CAAC;IACpC,GAAG,EAAE,KAAK;CACX,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=auth-gate-detection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-gate-detection.d.ts","sourceRoot":"","sources":["../../../src/checks/authentication/auth-gate-detection.ts"],"names":[],"mappings":""}
@@ -0,0 +1,175 @@
1
+ import { registerCheck } from '../registry.js';
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
+ }
148
+ return {
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
+ },
166
+ };
167
+ }
168
+ registerCheck({
169
+ id: 'auth-gate-detection',
170
+ category: 'authentication',
171
+ description: 'Whether documentation pages require authentication to access content',
172
+ dependsOn: [],
173
+ run: check,
174
+ });
175
+ //# sourceMappingURL=auth-gate-detection.js.map
@@ -0,0 +1 @@
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
- async function check(_ctx) {
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: 'markdown-code-fence-validity',
5
- category: 'content-structure',
6
- status: 'skip',
7
- message: 'Not yet implemented',
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;AAG/C,KAAK,UAAU,KAAK,CAAC,IAAkB;IACrC,OAAO;QACL,EAAE,EAAE,8BAA8B;QAClC,QAAQ,EAAE,mBAAmB;QAC7B,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,qBAAqB;KAC/B,CAAC;AACJ,CAAC;AAED,aAAa,CAAC;IACZ,EAAE,EAAE,8BAA8B;IAClC,QAAQ,EAAE,mBAAmB;IAC7B,WAAW,EAAE,gDAAgD;IAC7D,SAAS,EAAE,CAAC,CAAC,sBAAsB,EAAE,qBAAqB,CAAC,CAAC;IAC5D,GAAG,EAAE,KAAK;CACX,CAAC,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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"}
@@ -17,6 +17,8 @@ import './agent-discoverability/llms-txt-directive.js';
17
17
  import './observability/llms-txt-freshness.js';
18
18
  import './observability/markdown-content-parity.js';
19
19
  import './observability/cache-header-hygiene.js';
20
+ import './authentication/auth-gate-detection.js';
21
+ import './authentication/auth-alternative-access.js';
20
22
  export { getCheck, getAllChecks, getChecksSorted } from './registry.js';
21
23
  export { extractMarkdownLinks } from './llms-txt/llms-txt-valid.js';
22
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/checks/index.ts"],"names":[],"mappings":"AAGA,OAAO,+BAA+B,CAAC;AACvC,OAAO,8BAA8B,CAAC;AACtC,OAAO,6BAA6B,CAAC;AACrC,OAAO,sCAAsC,CAAC;AAC9C,OAAO,uCAAuC,CAAC;AAG/C,OAAO,iDAAiD,CAAC;AACzD,OAAO,gDAAgD,CAAC;AAGxD,OAAO,mCAAmC,CAAC;AAC3C,OAAO,+BAA+B,CAAC;AACvC,OAAO,uCAAuC,CAAC;AAG/C,OAAO,qDAAqD,CAAC;AAC7D,OAAO,+CAA+C,CAAC;AACvD,OAAO,qDAAqD,CAAC;AAG7D,OAAO,sCAAsC,CAAC;AAC9C,OAAO,sCAAsC,CAAC;AAG9C,OAAO,+CAA+C,CAAC;AAGvD,OAAO,uCAAuC,CAAC;AAC/C,OAAO,4CAA4C,CAAC;AACpD,OAAO,yCAAyC,CAAC;AAEjD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/checks/index.ts"],"names":[],"mappings":"AAGA,OAAO,+BAA+B,CAAC;AACvC,OAAO,8BAA8B,CAAC;AACtC,OAAO,6BAA6B,CAAC;AACrC,OAAO,sCAAsC,CAAC;AAC9C,OAAO,uCAAuC,CAAC;AAG/C,OAAO,iDAAiD,CAAC;AACzD,OAAO,gDAAgD,CAAC;AAGxD,OAAO,mCAAmC,CAAC;AAC3C,OAAO,+BAA+B,CAAC;AACvC,OAAO,uCAAuC,CAAC;AAG/C,OAAO,qDAAqD,CAAC;AAC7D,OAAO,+CAA+C,CAAC;AACvD,OAAO,qDAAqD,CAAC;AAG7D,OAAO,sCAAsC,CAAC;AAC9C,OAAO,sCAAsC,CAAC;AAG9C,OAAO,+CAA+C,CAAC;AAGvD,OAAO,uCAAuC,CAAC;AAC/C,OAAO,4CAA4C,CAAC;AACpD,OAAO,yCAAyC,CAAC;AAGjD,OAAO,yCAAyC,CAAC;AACjD,OAAO,6CAA6C,CAAC;AAErD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC"}
@@ -25,6 +25,9 @@ import './agent-discoverability/llms-txt-directive.js';
25
25
  import './observability/llms-txt-freshness.js';
26
26
  import './observability/markdown-content-parity.js';
27
27
  import './observability/cache-header-hygiene.js';
28
+ // Category 8: Authentication
29
+ import './authentication/auth-gate-detection.js';
30
+ import './authentication/auth-alternative-access.js';
28
31
  export { getCheck, getAllChecks, getChecksSorted } from './registry.js';
29
32
  export { extractMarkdownLinks } from './llms-txt/llms-txt-valid.js';
30
33
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/checks/index.ts"],"names":[],"mappings":"AAAA,mDAAmD;AAEnD,uBAAuB;AACvB,OAAO,+BAA+B,CAAC;AACvC,OAAO,8BAA8B,CAAC;AACtC,OAAO,6BAA6B,CAAC;AACrC,OAAO,sCAAsC,CAAC;AAC9C,OAAO,uCAAuC,CAAC;AAE/C,oCAAoC;AACpC,OAAO,iDAAiD,CAAC;AACzD,OAAO,gDAAgD,CAAC;AAExD,wBAAwB;AACxB,OAAO,mCAAmC,CAAC;AAC3C,OAAO,+BAA+B,CAAC;AACvC,OAAO,uCAAuC,CAAC;AAE/C,gCAAgC;AAChC,OAAO,qDAAqD,CAAC;AAC7D,OAAO,+CAA+C,CAAC;AACvD,OAAO,qDAAqD,CAAC;AAE7D,4BAA4B;AAC5B,OAAO,sCAAsC,CAAC;AAC9C,OAAO,sCAAsC,CAAC;AAE9C,oCAAoC;AACpC,OAAO,+CAA+C,CAAC;AAEvD,4BAA4B;AAC5B,OAAO,uCAAuC,CAAC;AAC/C,OAAO,4CAA4C,CAAC;AACpD,OAAO,yCAAyC,CAAC;AAEjD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/checks/index.ts"],"names":[],"mappings":"AAAA,mDAAmD;AAEnD,uBAAuB;AACvB,OAAO,+BAA+B,CAAC;AACvC,OAAO,8BAA8B,CAAC;AACtC,OAAO,6BAA6B,CAAC;AACrC,OAAO,sCAAsC,CAAC;AAC9C,OAAO,uCAAuC,CAAC;AAE/C,oCAAoC;AACpC,OAAO,iDAAiD,CAAC;AACzD,OAAO,gDAAgD,CAAC;AAExD,wBAAwB;AACxB,OAAO,mCAAmC,CAAC;AAC3C,OAAO,+BAA+B,CAAC;AACvC,OAAO,uCAAuC,CAAC;AAE/C,gCAAgC;AAChC,OAAO,qDAAqD,CAAC;AAC7D,OAAO,+CAA+C,CAAC;AACvD,OAAO,qDAAqD,CAAC;AAE7D,4BAA4B;AAC5B,OAAO,sCAAsC,CAAC;AAC9C,OAAO,sCAAsC,CAAC;AAE9C,oCAAoC;AACpC,OAAO,+CAA+C,CAAC;AAEvD,4BAA4B;AAC5B,OAAO,uCAAuC,CAAC;AAC/C,OAAO,4CAA4C,CAAC;AACpD,OAAO,yCAAyC,CAAC;AAEjD,6BAA6B;AAC7B,OAAO,yCAAyC,CAAC;AACjD,OAAO,6CAA6C,CAAC;AAErD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC"}