geopilot 0.0.0-stage → 0.0.1-bootstrap.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/LICENSE +202 -0
- package/README.md +72 -2
- package/dist/agent/install.d.ts +23 -0
- package/dist/agent/install.js +75 -0
- package/dist/agent/install.js.map +1 -0
- package/dist/cache/runs.d.ts +18 -0
- package/dist/cache/runs.js +91 -0
- package/dist/cache/runs.js.map +1 -0
- package/dist/cli/args.d.ts +20 -0
- package/dist/cli/args.js +116 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/commands/agent.d.ts +2 -0
- package/dist/cli/commands/agent.js +48 -0
- package/dist/cli/commands/agent.js.map +1 -0
- package/dist/cli/commands/fix.d.ts +2 -0
- package/dist/cli/commands/fix.js +284 -0
- package/dist/cli/commands/fix.js.map +1 -0
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.js +62 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/insights.d.ts +17 -0
- package/dist/cli/commands/insights.js +143 -0
- package/dist/cli/commands/insights.js.map +1 -0
- package/dist/cli/commands/rules.d.ts +5 -0
- package/dist/cli/commands/rules.js +86 -0
- package/dist/cli/commands/rules.js.map +1 -0
- package/dist/cli/commands/scan.d.ts +14 -0
- package/dist/cli/commands/scan.js +127 -0
- package/dist/cli/commands/scan.js.map +1 -0
- package/dist/cli/commands-list.d.ts +6 -0
- package/dist/cli/commands-list.js +15 -0
- package/dist/cli/commands-list.js.map +1 -0
- package/dist/cli/context.d.ts +26 -0
- package/dist/cli/context.js +108 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/cli/io.d.ts +16 -0
- package/dist/cli/io.js +28 -0
- package/dist/cli/io.js.map +1 -0
- package/dist/cli/main.d.ts +15 -0
- package/dist/cli/main.js +118 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/collect/crawl.d.ts +51 -0
- package/dist/collect/crawl.js +187 -0
- package/dist/collect/crawl.js.map +1 -0
- package/dist/config/config.d.ts +86 -0
- package/dist/config/config.js +396 -0
- package/dist/config/config.js.map +1 -0
- package/dist/core/errors.d.ts +20 -0
- package/dist/core/errors.js +32 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/hash.d.ts +7 -0
- package/dist/core/hash.js +28 -0
- package/dist/core/hash.js.map +1 -0
- package/dist/core/redact.d.ts +3 -0
- package/dist/core/redact.js +23 -0
- package/dist/core/redact.js.map +1 -0
- package/dist/core/tty.d.ts +12 -0
- package/dist/core/tty.js +33 -0
- package/dist/core/tty.js.map +1 -0
- package/dist/core/version.d.ts +6 -0
- package/dist/core/version.js +10 -0
- package/dist/core/version.js.map +1 -0
- package/dist/fix/apply.d.ts +119 -0
- package/dist/fix/apply.js +334 -0
- package/dist/fix/apply.js.map +1 -0
- package/dist/fix/diff.d.ts +1 -0
- package/dist/fix/diff.js +35 -0
- package/dist/fix/diff.js.map +1 -0
- package/dist/fix/plan.d.ts +42 -0
- package/dist/fix/plan.js +385 -0
- package/dist/fix/plan.js.map +1 -0
- package/dist/fix/serve.d.ts +13 -0
- package/dist/fix/serve.js +112 -0
- package/dist/fix/serve.js.map +1 -0
- package/dist/fix/variants.d.ts +13 -0
- package/dist/fix/variants.js +8 -0
- package/dist/fix/variants.js.map +1 -0
- package/dist/frameworks/ast.d.ts +84 -0
- package/dist/frameworks/ast.js +231 -0
- package/dist/frameworks/ast.js.map +1 -0
- package/dist/frameworks/nextjs-app/analyze.d.ts +92 -0
- package/dist/frameworks/nextjs-app/analyze.js +350 -0
- package/dist/frameworks/nextjs-app/analyze.js.map +1 -0
- package/dist/frameworks/static/analyze.d.ts +18 -0
- package/dist/frameworks/static/analyze.js +66 -0
- package/dist/frameworks/static/analyze.js.map +1 -0
- package/dist/html/parse.d.ts +50 -0
- package/dist/html/parse.js +205 -0
- package/dist/html/parse.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/insights/validate.d.ts +74 -0
- package/dist/insights/validate.js +213 -0
- package/dist/insights/validate.js.map +1 -0
- package/dist/net/address.d.ts +10 -0
- package/dist/net/address.js +116 -0
- package/dist/net/address.js.map +1 -0
- package/dist/net/fetch.d.ts +53 -0
- package/dist/net/fetch.js +281 -0
- package/dist/net/fetch.js.map +1 -0
- package/dist/repo/detect.d.ts +3 -0
- package/dist/repo/detect.js +92 -0
- package/dist/repo/detect.js.map +1 -0
- package/dist/repo/reader.d.ts +12 -0
- package/dist/repo/reader.js +97 -0
- package/dist/repo/reader.js.map +1 -0
- package/dist/report/render.d.ts +20 -0
- package/dist/report/render.js +176 -0
- package/dist/report/render.js.map +1 -0
- package/dist/robots/crawlers.d.ts +10 -0
- package/dist/robots/crawlers.js +20 -0
- package/dist/robots/crawlers.js.map +1 -0
- package/dist/robots/robots.d.ts +33 -0
- package/dist/robots/robots.js +133 -0
- package/dist/robots/robots.js.map +1 -0
- package/dist/rules/catalogue.d.ts +43 -0
- package/dist/rules/catalogue.js +361 -0
- package/dist/rules/catalogue.js.map +1 -0
- package/dist/rules/context.d.ts +36 -0
- package/dist/rules/context.js +129 -0
- package/dist/rules/context.js.map +1 -0
- package/dist/rules/evaluate.d.ts +11 -0
- package/dist/rules/evaluate.js +1070 -0
- package/dist/rules/evaluate.js.map +1 -0
- package/dist/rules/finalize.d.ts +34 -0
- package/dist/rules/finalize.js +93 -0
- package/dist/rules/finalize.js.map +1 -0
- package/dist/scan.d.ts +27 -0
- package/dist/scan.js +147 -0
- package/dist/scan.js.map +1 -0
- package/dist/scoring/compare.d.ts +21 -0
- package/dist/scoring/compare.js +47 -0
- package/dist/scoring/compare.js.map +1 -0
- package/dist/scoring/score.d.ts +21 -0
- package/dist/scoring/score.js +112 -0
- package/dist/scoring/score.js.map +1 -0
- package/dist/sitemap/sitemap.d.ts +10 -0
- package/dist/sitemap/sitemap.js +48 -0
- package/dist/sitemap/sitemap.js.map +1 -0
- package/dist/types.d.ts +155 -0
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -0
- package/hosts/claude-project/.claude/agents/geopilot-explainer.md +36 -0
- package/hosts/claude-project/.claude/agents/geopilot-fixer.md +42 -0
- package/hosts/claude-project/.claude/agents/geopilot-investigator.md +45 -0
- package/hosts/claude-project/.claude/agents/geopilot-reviewer.md +41 -0
- package/hosts/claude-project/.claude/agents/geopilot-scanner.md +47 -0
- package/hosts/claude-project/.claude/skills/geo/SKILL.md +95 -0
- package/hosts/claude-project/.claude/skills/geo/references/frameworks.md +20 -0
- package/hosts/claude-project/.claude/skills/geo/references/insight-contract.md +42 -0
- package/hosts/claude-project/.claude/skills/geo/references/safety.md +30 -0
- package/hosts/claude-project/.claude/skills/geo/references/workflow-explain.md +8 -0
- package/hosts/claude-project/.claude/skills/geo/references/workflow-fix.md +20 -0
- package/hosts/claude-project/.claude/skills/geo/references/workflow-init.md +6 -0
- package/hosts/claude-project/.claude/skills/geo/references/workflow-insights.md +13 -0
- package/hosts/claude-project/.claude/skills/geo/references/workflow-plan.md +13 -0
- package/hosts/claude-project/.claude/skills/geo/references/workflow-review.md +8 -0
- package/hosts/claude-project/.claude/skills/geo/references/workflow-scan.md +18 -0
- package/hosts/claude-project/.claude/skills/geo/schemas/insights.schema.json +255 -0
- package/hosts/claude-project/.claude/skills/geo/scripts/geo.ps1 +11 -0
- package/hosts/claude-project/.claude/skills/geo/scripts/geo.sh +11 -0
- package/hosts/claude-project/.claude/skills/geopilot-explain/SKILL.md +9 -0
- package/hosts/claude-project/.claude/skills/geopilot-fix/SKILL.md +12 -0
- package/hosts/claude-project/.claude/skills/geopilot-init/SKILL.md +15 -0
- package/hosts/claude-project/.claude/skills/geopilot-insights/SKILL.md +11 -0
- package/hosts/claude-project/.claude/skills/geopilot-review/SKILL.md +11 -0
- package/hosts/claude-project/.claude/skills/geopilot-scan/SKILL.md +11 -0
- package/hosts/geopilot/.claude-plugin/plugin.json +22 -0
- package/hosts/geopilot/README.md +25 -0
- package/hosts/geopilot/agents/explainer.agent.md +36 -0
- package/hosts/geopilot/agents/fixer.agent.md +42 -0
- package/hosts/geopilot/agents/investigator.agent.md +45 -0
- package/hosts/geopilot/agents/reviewer.agent.md +41 -0
- package/hosts/geopilot/agents/scanner.agent.md +47 -0
- package/hosts/geopilot/assets/icon.png +0 -0
- package/hosts/geopilot/skills/explain/SKILL.md +9 -0
- package/hosts/geopilot/skills/fix/SKILL.md +12 -0
- package/hosts/geopilot/skills/geo/SKILL.md +95 -0
- package/hosts/geopilot/skills/geo/references/frameworks.md +20 -0
- package/hosts/geopilot/skills/geo/references/insight-contract.md +42 -0
- package/hosts/geopilot/skills/geo/references/safety.md +30 -0
- package/hosts/geopilot/skills/geo/references/workflow-explain.md +8 -0
- package/hosts/geopilot/skills/geo/references/workflow-fix.md +20 -0
- package/hosts/geopilot/skills/geo/references/workflow-init.md +6 -0
- package/hosts/geopilot/skills/geo/references/workflow-insights.md +13 -0
- package/hosts/geopilot/skills/geo/references/workflow-plan.md +13 -0
- package/hosts/geopilot/skills/geo/references/workflow-review.md +8 -0
- package/hosts/geopilot/skills/geo/references/workflow-scan.md +18 -0
- package/hosts/geopilot/skills/geo/schemas/insights.schema.json +255 -0
- package/hosts/geopilot/skills/geo/scripts/geo.ps1 +11 -0
- package/hosts/geopilot/skills/geo/scripts/geo.sh +11 -0
- package/hosts/geopilot/skills/init/SKILL.md +15 -0
- package/hosts/geopilot/skills/insights/SKILL.md +11 -0
- package/hosts/geopilot/skills/review/SKILL.md +11 -0
- package/hosts/geopilot/skills/scan/SKILL.md +11 -0
- package/hosts/opencode/.opencode/agents/geopilot-explainer.md +39 -0
- package/hosts/opencode/.opencode/agents/geopilot-fixer.md +45 -0
- package/hosts/opencode/.opencode/agents/geopilot-investigator.md +48 -0
- package/hosts/opencode/.opencode/agents/geopilot-reviewer.md +44 -0
- package/hosts/opencode/.opencode/agents/geopilot-scanner.md +50 -0
- package/hosts/opencode/.opencode/commands/geopilot-explain.md +8 -0
- package/hosts/opencode/.opencode/commands/geopilot-fix.md +10 -0
- package/hosts/opencode/.opencode/commands/geopilot-init.md +12 -0
- package/hosts/opencode/.opencode/commands/geopilot-insights.md +10 -0
- package/hosts/opencode/.opencode/commands/geopilot-review.md +10 -0
- package/hosts/opencode/.opencode/commands/geopilot-scan.md +10 -0
- package/hosts/opencode/.opencode/skills/geo/SKILL.md +95 -0
- package/hosts/opencode/.opencode/skills/geo/references/frameworks.md +20 -0
- package/hosts/opencode/.opencode/skills/geo/references/insight-contract.md +42 -0
- package/hosts/opencode/.opencode/skills/geo/references/safety.md +30 -0
- package/hosts/opencode/.opencode/skills/geo/references/workflow-explain.md +8 -0
- package/hosts/opencode/.opencode/skills/geo/references/workflow-fix.md +20 -0
- package/hosts/opencode/.opencode/skills/geo/references/workflow-init.md +6 -0
- package/hosts/opencode/.opencode/skills/geo/references/workflow-insights.md +13 -0
- package/hosts/opencode/.opencode/skills/geo/references/workflow-plan.md +13 -0
- package/hosts/opencode/.opencode/skills/geo/references/workflow-review.md +8 -0
- package/hosts/opencode/.opencode/skills/geo/references/workflow-scan.md +18 -0
- package/hosts/opencode/.opencode/skills/geo/schemas/insights.schema.json +255 -0
- package/hosts/opencode/.opencode/skills/geo/scripts/geo.ps1 +11 -0
- package/hosts/opencode/.opencode/skills/geo/scripts/geo.sh +11 -0
- package/package.json +65 -4
- package/schemas/config-v1.json +392 -0
- package/schemas/insights-v1.json +255 -0
- package/schemas/merged-v1.json +71 -0
- package/schemas/report-v1.json +836 -0
- package/skill/geo/SKILL.md +95 -0
- package/skill/geo/references/frameworks.md +20 -0
- package/skill/geo/references/insight-contract.md +42 -0
- package/skill/geo/references/safety.md +30 -0
- package/skill/geo/references/workflow-explain.md +8 -0
- package/skill/geo/references/workflow-fix.md +20 -0
- package/skill/geo/references/workflow-init.md +6 -0
- package/skill/geo/references/workflow-insights.md +13 -0
- package/skill/geo/references/workflow-plan.md +13 -0
- package/skill/geo/references/workflow-review.md +8 -0
- package/skill/geo/references/workflow-scan.md +18 -0
- package/skill/geo/schemas/insights.schema.json +255 -0
- package/skill/geo/scripts/geo.ps1 +11 -0
- package/skill/geo/scripts/geo.sh +11 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
export const DOCS_BASE = 'https://moelzanaty3.github.io/geopilot';
|
|
2
|
+
export const ruleDocsUrl = (id) => `${DOCS_BASE}/docs/rules/${id.toLowerCase()}/`;
|
|
3
|
+
const RFC9309 = { title: 'RFC 9309: Robots Exclusion Protocol', url: 'https://www.rfc-editor.org/rfc/rfc9309', kind: 'standard' };
|
|
4
|
+
const GOOGLE_AI = { title: 'Google: AI features and your website', url: 'https://developers.google.com/search/docs/appearance/ai-features', kind: 'platform-policy' };
|
|
5
|
+
const GOOGLE_ROBOTS_META = { title: 'Google: robots meta tag and X-Robots-Tag', url: 'https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag', kind: 'platform-policy' };
|
|
6
|
+
const GOOGLE_CANONICAL = { title: 'Google: consolidate duplicate URLs', url: 'https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls', kind: 'platform-policy' };
|
|
7
|
+
const SITEMAPS_ORG = { title: 'sitemaps.org protocol', url: 'https://www.sitemaps.org/protocol.html', kind: 'standard' };
|
|
8
|
+
const GOOGLE_SD = { title: 'Google: structured data general guidelines', url: 'https://developers.google.com/search/docs/appearance/structured-data/sd-policies', kind: 'platform-policy' };
|
|
9
|
+
const GOOGLE_JS = { title: 'Google: JavaScript SEO basics', url: 'https://developers.google.com/search/docs/crawling-indexing/javascript/javascript-seo-basics', kind: 'guidance' };
|
|
10
|
+
const GOOGLE_LINKS = { title: 'Google: make your links crawlable', url: 'https://developers.google.com/search/docs/crawling-indexing/links-crawlable', kind: 'platform-policy' };
|
|
11
|
+
const OPENAI = { title: 'OpenAI crawlers', url: 'https://developers.openai.com/docs/gptbot', kind: 'platform-policy' };
|
|
12
|
+
const ANTHROPIC = { title: 'Anthropic crawlers', url: 'https://support.claude.com/en/articles/8896518-anthropic-crawlers', kind: 'platform-policy' };
|
|
13
|
+
const PERPLEXITY = { title: 'Perplexity crawlers', url: 'https://docs.perplexity.ai/docs/resources/perplexity-crawlers.md', kind: 'platform-policy' };
|
|
14
|
+
const HTML_LANG = { title: 'WHATWG HTML: the lang attribute', url: 'https://html.spec.whatwg.org/multipage/dom.html#the-lang-and-xml:lang-attributes', kind: 'standard' };
|
|
15
|
+
const GOOGLE_TITLE = { title: 'Google: influencing title links', url: 'https://developers.google.com/search/docs/appearance/title-link', kind: 'guidance' };
|
|
16
|
+
const GOOGLE_SNIPPET = { title: 'Google: control your snippets', url: 'https://developers.google.com/search/docs/appearance/snippet', kind: 'guidance' };
|
|
17
|
+
const AXE = { title: 'axe-core rule descriptions', url: 'https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md', kind: 'standard' };
|
|
18
|
+
const APPLE_AASA = { title: 'Apple: supporting associated domains', url: 'https://developer.apple.com/documentation/xcode/supporting-associated-domains', kind: 'platform-policy' };
|
|
19
|
+
const ANDROID_APPLINKS = { title: 'Android: verify App Links', url: 'https://developer.android.com/training/app-links/verify-android-applinks', kind: 'platform-policy' };
|
|
20
|
+
const LLMS_TXT = { title: 'llms.txt proposal', url: 'https://llmstxt.org/', kind: 'guidance' };
|
|
21
|
+
export const RULES = [
|
|
22
|
+
{
|
|
23
|
+
id: 'GEO-013',
|
|
24
|
+
title: 'robots.txt is reachable, parseable and within size limits',
|
|
25
|
+
purpose: 'Crawlers treat a robots.txt that answers 5xx or cannot be reached as a complete disallow, so a broken robots.txt blocks a whole site.',
|
|
26
|
+
evidenceClass: 'E1',
|
|
27
|
+
weight: 3,
|
|
28
|
+
scored: true,
|
|
29
|
+
subject: 'site',
|
|
30
|
+
scope: 'URL/repo',
|
|
31
|
+
severity: 'high',
|
|
32
|
+
fixClass: 'contextual',
|
|
33
|
+
fixVariants: [],
|
|
34
|
+
subChecks: [
|
|
35
|
+
{ id: 'reachability', scope: 'url', description: '/robots.txt answers 2xx or 4xx, not 5xx or a network error' },
|
|
36
|
+
{ id: 'parse_size', scope: 'both', description: 'robots.txt is plain text within 500 KiB' },
|
|
37
|
+
],
|
|
38
|
+
platforms: 'Any web stack',
|
|
39
|
+
preconditions: [],
|
|
40
|
+
falsePositives: ['A CDN serving a transient 5xx during the scan; re-run to confirm.'],
|
|
41
|
+
remediation: 'Serve robots.txt as text/plain with a 200 (or a 404 if you want no rules). Fix the server error behind a 5xx before anything else.',
|
|
42
|
+
references: [RFC9309],
|
|
43
|
+
lifecycle: 'stable',
|
|
44
|
+
blockerCap: 'robots.txt answered 5xx',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'GEO-009',
|
|
48
|
+
title: 'Search crawlers have the access your declared policy says they should',
|
|
49
|
+
purpose: 'Compares effective RFC 9309 access for each search crawler with the owner-declared policy. Training crawlers are reported for information only.',
|
|
50
|
+
evidenceClass: 'E1',
|
|
51
|
+
weight: 3,
|
|
52
|
+
scored: true,
|
|
53
|
+
subject: 'site',
|
|
54
|
+
scope: 'URL/repo',
|
|
55
|
+
severity: 'high',
|
|
56
|
+
fixClass: 'guarded (Phase 2)',
|
|
57
|
+
fixVariants: ['G5'],
|
|
58
|
+
subChecks: [{ id: 'search_access', scope: 'both', description: 'every search crawler is allowed (or blocked) on sampled paths as policy.aiSearchCrawlers declares' }],
|
|
59
|
+
platforms: 'Any web stack',
|
|
60
|
+
preconditions: ['policy.aiSearchCrawlers is allow or block; undecided makes the rule not applicable'],
|
|
61
|
+
falsePositives: ['A user-agent group written for a crawler token GEO does not list.'],
|
|
62
|
+
remediation: 'Adjust the user-agent groups in robots.txt so the crawlers your policy allows can reach the paths you want found. Crawler-specific fixes stay manual until the Phase 2 equivalence proof ships.',
|
|
63
|
+
references: [RFC9309, OPENAI, ANTHROPIC, PERPLEXITY, GOOGLE_AI],
|
|
64
|
+
lifecycle: 'stable',
|
|
65
|
+
blockerCap: 'a search crawler is blocked site-wide against declared policy',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'GEO-014',
|
|
69
|
+
title: 'Sitemap-listed and linked routes are indexable',
|
|
70
|
+
purpose: 'A route you list in a sitemap or link to, but answer with an error, noindex or a robots disallow, sends search engines contradictory signals.',
|
|
71
|
+
evidenceClass: 'E1',
|
|
72
|
+
weight: 3,
|
|
73
|
+
scored: true,
|
|
74
|
+
subject: 'routeGroup',
|
|
75
|
+
scope: 'URL/repo',
|
|
76
|
+
severity: 'high',
|
|
77
|
+
fixClass: 'literal noindex guarded; otherwise contextual',
|
|
78
|
+
fixVariants: ['G1'],
|
|
79
|
+
subChecks: [
|
|
80
|
+
{ id: 'http_status', scope: 'url', description: 'sampled URLs answer 200 after redirects' },
|
|
81
|
+
{ id: 'meta_noindex', scope: 'both', description: 'no noindex or none in robots metadata' },
|
|
82
|
+
{ id: 'header_noindex', scope: 'url', description: 'no noindex in X-Robots-Tag' },
|
|
83
|
+
{ id: 'robots_disallow', scope: 'both', description: 'robots.txt does not disallow Googlebot or Bingbot from the route' },
|
|
84
|
+
],
|
|
85
|
+
platforms: 'Any web stack',
|
|
86
|
+
preconditions: ['Subjects are routes listed in a sitemap or linked from crawled server HTML'],
|
|
87
|
+
falsePositives: ['Intentionally noindexed pages that remain in a sitemap; remove them from the sitemap or suppress with a reason.'],
|
|
88
|
+
remediation: 'Either make the route indexable (remove noindex, fix the status, allow it in robots.txt) or remove it from the sitemap and internal links.',
|
|
89
|
+
references: [GOOGLE_ROBOTS_META, RFC9309, SITEMAPS_ORG],
|
|
90
|
+
lifecycle: 'stable',
|
|
91
|
+
blockerCap: 'every sampled route is noindex',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: 'GEO-015',
|
|
95
|
+
title: 'An absolute canonical resolves to an indexable URL on the declared origin',
|
|
96
|
+
purpose: 'A canonical that is relative, points off-origin, or resolves to an error or noindex page makes the preferred URL ambiguous.',
|
|
97
|
+
evidenceClass: 'E2',
|
|
98
|
+
weight: 2,
|
|
99
|
+
scored: true,
|
|
100
|
+
subject: 'routeGroup',
|
|
101
|
+
scope: 'URL/repo',
|
|
102
|
+
severity: 'medium',
|
|
103
|
+
fixClass: 'guarded',
|
|
104
|
+
fixVariants: ['G2'],
|
|
105
|
+
subChecks: [
|
|
106
|
+
{ id: 'canonical_present', scope: 'both', description: 'exactly one absolute canonical URL' },
|
|
107
|
+
{ id: 'declared_origin', scope: 'both', description: 'the canonical is on site.origin' },
|
|
108
|
+
{ id: 'target_indexable', scope: 'url', description: 'the canonical target answers 200 and is not noindex' },
|
|
109
|
+
],
|
|
110
|
+
platforms: 'Any web stack',
|
|
111
|
+
preconditions: ['site.origin is declared in geo.config.json; GEO never infers it'],
|
|
112
|
+
falsePositives: ['Deliberate cross-domain canonicals for syndicated content.'],
|
|
113
|
+
remediation: 'Emit one absolute canonical per page on your declared origin, pointing at a 200, indexable URL.',
|
|
114
|
+
references: [GOOGLE_CANONICAL],
|
|
115
|
+
lifecycle: 'stable',
|
|
116
|
+
blockerCap: null,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: 'GEO-003',
|
|
120
|
+
title: 'Primary content is present in server HTML',
|
|
121
|
+
purpose: 'Compares raw and rendered primary text. Content that only appears after JavaScript runs is at risk for crawlers that do not render.',
|
|
122
|
+
evidenceClass: 'E2',
|
|
123
|
+
weight: 3,
|
|
124
|
+
scored: true,
|
|
125
|
+
subject: 'routeGroup',
|
|
126
|
+
scope: 'URL render; repo inferred',
|
|
127
|
+
severity: 'medium',
|
|
128
|
+
fixClass: 'contextual',
|
|
129
|
+
fixVariants: [],
|
|
130
|
+
subChecks: [{ id: 'server_text_ratio', scope: 'url', description: 'raw/rendered primary-text ratio is at least 0.5 when rendered text is 200+ characters' }],
|
|
131
|
+
platforms: 'Any web stack; needs a renderer',
|
|
132
|
+
preconditions: ['A renderer is available. Without one the rule is not_evaluated, never failed. This release ships no renderer.'],
|
|
133
|
+
falsePositives: ['Pages whose primary content is intentionally interactive.'],
|
|
134
|
+
remediation: 'Render primary content on the server (a Server Component, SSR or static generation) and hydrate interactivity on top.',
|
|
135
|
+
references: [GOOGLE_JS],
|
|
136
|
+
lifecycle: 'stable',
|
|
137
|
+
blockerCap: null,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 'GEO-004',
|
|
141
|
+
title: 'Present JSON-LD parses and meets documented requirements',
|
|
142
|
+
purpose: 'Structured data that does not parse, or lacks the properties its type requires, is ignored. Absence is not a finding.',
|
|
143
|
+
evidenceClass: 'E1',
|
|
144
|
+
weight: 2,
|
|
145
|
+
scored: true,
|
|
146
|
+
subject: 'routeGroup',
|
|
147
|
+
scope: 'URL/repo',
|
|
148
|
+
severity: 'medium',
|
|
149
|
+
fixClass: 'contextual',
|
|
150
|
+
fixVariants: [],
|
|
151
|
+
subChecks: [
|
|
152
|
+
{ id: 'parses', scope: 'both', description: 'every application/ld+json block is valid JSON' },
|
|
153
|
+
{ id: 'required_properties', scope: 'both', description: 'known types carry their documented required properties' },
|
|
154
|
+
],
|
|
155
|
+
platforms: 'Any web stack',
|
|
156
|
+
preconditions: ['At least one JSON-LD block is present; otherwise not applicable'],
|
|
157
|
+
falsePositives: ['Types GEO does not model are checked for parsing only.'],
|
|
158
|
+
remediation: 'Fix the JSON syntax, then add the required properties for the declared @type.',
|
|
159
|
+
references: [GOOGLE_SD],
|
|
160
|
+
lifecycle: 'stable',
|
|
161
|
+
blockerCap: null,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: 'GEO-011',
|
|
165
|
+
title: 'A valid sitemap is discoverable and its URLs are reachable',
|
|
166
|
+
purpose: 'A sitemap referenced from robots.txt, that parses and lists reachable URLs, is the documented way to tell crawlers what exists.',
|
|
167
|
+
evidenceClass: 'E2',
|
|
168
|
+
weight: 2,
|
|
169
|
+
scored: true,
|
|
170
|
+
subject: 'site',
|
|
171
|
+
scope: 'URL/repo',
|
|
172
|
+
severity: 'medium',
|
|
173
|
+
fixClass: 'safe sitemap line / guarded sitemap generation',
|
|
174
|
+
fixVariants: ['S1', 'G3'],
|
|
175
|
+
subChecks: [
|
|
176
|
+
{ id: 'sitemap_valid', scope: 'both', description: 'a sitemap exists and parses as urlset or sitemapindex' },
|
|
177
|
+
{ id: 'discoverable', scope: 'both', description: 'robots.txt carries a Sitemap: line' },
|
|
178
|
+
{ id: 'listed_accessible', scope: 'url', description: 'sampled listed URLs answer 200' },
|
|
179
|
+
],
|
|
180
|
+
platforms: 'Any web stack',
|
|
181
|
+
preconditions: [],
|
|
182
|
+
falsePositives: ['Sitemaps submitted only through a search console; GEO cannot see those.'],
|
|
183
|
+
remediation: 'Publish a sitemap, reference it with an absolute Sitemap: line in robots.txt, and keep only reachable URLs in it.',
|
|
184
|
+
references: [SITEMAPS_ORG, RFC9309],
|
|
185
|
+
lifecycle: 'stable',
|
|
186
|
+
blockerCap: null,
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
id: 'GEO-012',
|
|
190
|
+
title: 'Title, meta description and html lang are present',
|
|
191
|
+
purpose: 'The document title, description and language are the baseline metadata every consumer of the page reads.',
|
|
192
|
+
evidenceClass: 'E2',
|
|
193
|
+
weight: 2,
|
|
194
|
+
scored: true,
|
|
195
|
+
subject: 'routeGroup',
|
|
196
|
+
scope: 'URL/repo',
|
|
197
|
+
severity: 'medium',
|
|
198
|
+
fixClass: 'safe literal lang / editorial copy',
|
|
199
|
+
fixVariants: ['S2'],
|
|
200
|
+
subChecks: [
|
|
201
|
+
{ id: 'title', scope: 'both', description: 'a non-empty <title>' },
|
|
202
|
+
{ id: 'description', scope: 'both', description: 'a non-empty meta description' },
|
|
203
|
+
{ id: 'lang', scope: 'both', description: 'a non-empty lang on <html>' },
|
|
204
|
+
],
|
|
205
|
+
platforms: 'Any web stack',
|
|
206
|
+
preconditions: [],
|
|
207
|
+
falsePositives: [],
|
|
208
|
+
remediation: 'Give every route a specific title and description, and set lang on the root <html>.',
|
|
209
|
+
references: [HTML_LANG, GOOGLE_TITLE, GOOGLE_SNIPPET],
|
|
210
|
+
lifecycle: 'stable',
|
|
211
|
+
blockerCap: null,
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: 'GEO-016',
|
|
215
|
+
title: 'Key routes are reachable through server-rendered links',
|
|
216
|
+
purpose: 'Crawlers discover pages through <a href> links in served HTML. A route reachable only through a sitemap or client-side navigation is weakly connected.',
|
|
217
|
+
evidenceClass: 'E2',
|
|
218
|
+
weight: 2,
|
|
219
|
+
scored: true,
|
|
220
|
+
subject: 'routeGroup',
|
|
221
|
+
scope: 'URL',
|
|
222
|
+
severity: 'medium',
|
|
223
|
+
fixClass: 'contextual',
|
|
224
|
+
fixVariants: [],
|
|
225
|
+
subChecks: [{ id: 'server_link', scope: 'url', description: 'at least one other crawled page links to the route in its raw server HTML' }],
|
|
226
|
+
platforms: 'Any web stack',
|
|
227
|
+
preconditions: ['Evaluated from raw HTML: without a renderer, raw HTML is the server HTML. The entry route is not applicable.'],
|
|
228
|
+
falsePositives: ['Routes linked only from pages outside the crawl budget; raise --max-pages.'],
|
|
229
|
+
remediation: 'Link the route from server-rendered navigation or content with a plain <a href>.',
|
|
230
|
+
references: [GOOGLE_LINKS],
|
|
231
|
+
lifecycle: 'stable',
|
|
232
|
+
blockerCap: null,
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
id: 'GEO-017',
|
|
236
|
+
title: 'Snippet restrictions do not conflict with an AI-search inclusion policy',
|
|
237
|
+
purpose: 'nosnippet and max-snippet:0 limit how content can be shown in search features, including AI features. That contradicts a declared policy of allowing AI search.',
|
|
238
|
+
evidenceClass: 'E1',
|
|
239
|
+
weight: 2,
|
|
240
|
+
scored: true,
|
|
241
|
+
subject: 'routeGroup',
|
|
242
|
+
scope: 'URL/repo',
|
|
243
|
+
severity: 'medium',
|
|
244
|
+
fixClass: 'contextual',
|
|
245
|
+
fixVariants: [],
|
|
246
|
+
subChecks: [
|
|
247
|
+
{ id: 'snippet_meta', scope: 'both', description: 'no nosnippet or max-snippet:0 in robots metadata' },
|
|
248
|
+
{ id: 'snippet_header', scope: 'both', description: 'no nosnippet or max-snippet:0 in X-Robots-Tag (from source: no middleware or headers() that could set it)' },
|
|
249
|
+
],
|
|
250
|
+
platforms: 'Any web stack',
|
|
251
|
+
preconditions: ['policy.aiSearchCrawlers is allow; otherwise not applicable'],
|
|
252
|
+
falsePositives: ['data-nosnippet on specific elements is not flagged; it is a scoped choice.'],
|
|
253
|
+
remediation: 'Remove page-wide nosnippet / max-snippet:0, or change the declared policy if exclusion is intended.',
|
|
254
|
+
references: [GOOGLE_AI, GOOGLE_SNIPPET, GOOGLE_ROBOTS_META],
|
|
255
|
+
lifecycle: 'stable',
|
|
256
|
+
blockerCap: null,
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
id: 'GEO-002',
|
|
260
|
+
title: 'Selected accessibility checks pass on the rendered page',
|
|
261
|
+
purpose: 'A subset of axe-core checks that also affect how machines read a page: image alternatives, document title, link and button names.',
|
|
262
|
+
evidenceClass: 'E2',
|
|
263
|
+
weight: 1,
|
|
264
|
+
scored: true,
|
|
265
|
+
subject: 'routeGroup',
|
|
266
|
+
scope: 'URL render',
|
|
267
|
+
severity: 'low',
|
|
268
|
+
fixClass: 'contextual/editorial',
|
|
269
|
+
fixVariants: [],
|
|
270
|
+
subChecks: [{ id: 'axe_subset', scope: 'url', description: 'image-alt, document-title, link-name, button-name on the rendered page' }],
|
|
271
|
+
platforms: 'Any web stack; needs a renderer',
|
|
272
|
+
preconditions: ['A renderer is available. This release ships no renderer, so the rule is not_evaluated.'],
|
|
273
|
+
falsePositives: [],
|
|
274
|
+
remediation: 'Fix the reported elements; see the axe-core rule pages.',
|
|
275
|
+
references: [AXE],
|
|
276
|
+
lifecycle: 'stable',
|
|
277
|
+
blockerCap: null,
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
id: 'GEO-101',
|
|
281
|
+
title: 'Mobile app association files are served correctly',
|
|
282
|
+
purpose: 'apple-app-site-association and assetlinks.json must be served without redirects, as JSON, naming the configured apps.',
|
|
283
|
+
evidenceClass: 'E1',
|
|
284
|
+
weight: 2,
|
|
285
|
+
scored: true,
|
|
286
|
+
subject: 'site',
|
|
287
|
+
scope: 'URL',
|
|
288
|
+
severity: 'medium',
|
|
289
|
+
fixClass: 'guarded (Phase 2)',
|
|
290
|
+
fixVariants: ['G4'],
|
|
291
|
+
subChecks: [
|
|
292
|
+
{ id: 'aasa', scope: 'url', description: '/.well-known/apple-app-site-association names the configured appIDs' },
|
|
293
|
+
{ id: 'assetlinks', scope: 'url', description: '/.well-known/assetlinks.json names the configured package and fingerprints' },
|
|
294
|
+
],
|
|
295
|
+
platforms: 'Any web stack',
|
|
296
|
+
preconditions: ['profile is app and appLinks is configured; otherwise not applicable'],
|
|
297
|
+
falsePositives: [],
|
|
298
|
+
remediation: 'Serve the association files at the documented paths with a 200, application/json, and no redirect.',
|
|
299
|
+
references: [APPLE_AASA, ANDROID_APPLINKS],
|
|
300
|
+
lifecycle: 'stable',
|
|
301
|
+
blockerCap: null,
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
id: 'GEO-010',
|
|
305
|
+
title: 'llms.txt presence (informational)',
|
|
306
|
+
purpose: 'Reports whether /llms.txt exists. It is an experimental proposal; absence is never a finding and it is never scored.',
|
|
307
|
+
evidenceClass: 'E4',
|
|
308
|
+
weight: 0,
|
|
309
|
+
scored: false,
|
|
310
|
+
subject: 'site',
|
|
311
|
+
scope: 'URL',
|
|
312
|
+
severity: 'info',
|
|
313
|
+
fixClass: 'none',
|
|
314
|
+
fixVariants: [],
|
|
315
|
+
subChecks: [],
|
|
316
|
+
platforms: 'Any web stack',
|
|
317
|
+
preconditions: [],
|
|
318
|
+
falsePositives: [],
|
|
319
|
+
remediation: 'None required.',
|
|
320
|
+
references: [LLMS_TXT],
|
|
321
|
+
lifecycle: 'informational',
|
|
322
|
+
blockerCap: null,
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
id: 'GEO-102',
|
|
326
|
+
title: 'App-specific structured data (informational, app profile)',
|
|
327
|
+
purpose: 'Reports SoftwareApplication / MobileApplication JSON-LD when the app profile is enabled. Context-dependent and never scored.',
|
|
328
|
+
evidenceClass: 'E3',
|
|
329
|
+
weight: 0,
|
|
330
|
+
scored: false,
|
|
331
|
+
subject: 'site',
|
|
332
|
+
scope: 'URL',
|
|
333
|
+
severity: 'info',
|
|
334
|
+
fixClass: 'none',
|
|
335
|
+
fixVariants: [],
|
|
336
|
+
subChecks: [],
|
|
337
|
+
platforms: 'Any web stack',
|
|
338
|
+
preconditions: ['profile is app'],
|
|
339
|
+
falsePositives: [],
|
|
340
|
+
remediation: 'None required.',
|
|
341
|
+
references: [GOOGLE_SD],
|
|
342
|
+
lifecycle: 'informational',
|
|
343
|
+
blockerCap: null,
|
|
344
|
+
},
|
|
345
|
+
];
|
|
346
|
+
export const RETIRED = [
|
|
347
|
+
{ id: 'GEO-001', title: 'Q&A format mandate' },
|
|
348
|
+
{ id: 'GEO-005', title: 'Word-count requirement' },
|
|
349
|
+
{ id: 'GEO-006', title: 'Paragraph-length requirement' },
|
|
350
|
+
{ id: 'GEO-007', title: 'FAQ / summary mandate' },
|
|
351
|
+
{ id: 'GEO-008', title: 'Numbers / citations mandate' },
|
|
352
|
+
];
|
|
353
|
+
const BY_ID = new Map(RULES.map((r) => [r.id, r]));
|
|
354
|
+
export function ruleMeta(id) {
|
|
355
|
+
return BY_ID.get(id);
|
|
356
|
+
}
|
|
357
|
+
export function findRule(id) {
|
|
358
|
+
return BY_ID.get(id.toUpperCase());
|
|
359
|
+
}
|
|
360
|
+
export const SCORED_RULES = RULES.filter((r) => r.scored);
|
|
361
|
+
//# sourceMappingURL=catalogue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalogue.js","sourceRoot":"","sources":["../../src/rules/catalogue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { GeoConfig } from '../config/config.ts';
|
|
2
|
+
import type { UrlEvidence } from '../collect/crawl.ts';
|
|
3
|
+
import type { RepoReader } from '../repo/reader.ts';
|
|
4
|
+
import type { FrameworkInfo } from '../types.ts';
|
|
5
|
+
import { type NextAnalysis, type NextRoute } from '../frameworks/nextjs-app/analyze.ts';
|
|
6
|
+
import { type StaticAnalysis, type StaticPage } from '../frameworks/static/analyze.ts';
|
|
7
|
+
export interface RepoModel {
|
|
8
|
+
readonly reader: RepoReader;
|
|
9
|
+
readonly framework: FrameworkInfo;
|
|
10
|
+
readonly next: NextAnalysis | null;
|
|
11
|
+
readonly static: StaticAnalysis | null;
|
|
12
|
+
}
|
|
13
|
+
export interface RouteGroup {
|
|
14
|
+
readonly route: string;
|
|
15
|
+
readonly urls: string[];
|
|
16
|
+
readonly next: NextRoute | null;
|
|
17
|
+
readonly static: StaticPage | null;
|
|
18
|
+
/** true/false when a sitemap was read; null when no sitemap could be read. */
|
|
19
|
+
listed: boolean | null;
|
|
20
|
+
linked: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface ScanContext {
|
|
23
|
+
readonly config: GeoConfig;
|
|
24
|
+
readonly url: UrlEvidence | null;
|
|
25
|
+
readonly repo: RepoModel | null;
|
|
26
|
+
readonly groups: readonly RouteGroup[];
|
|
27
|
+
readonly rendererAvailable: boolean;
|
|
28
|
+
readonly now: Date;
|
|
29
|
+
}
|
|
30
|
+
/** `/**` matches everything, `*` one segment; `/admin/**` also matches `/admin`. */
|
|
31
|
+
export declare function globMatch(pattern: string, path: string): boolean;
|
|
32
|
+
export declare function includedPath(config: GeoConfig, path: string): boolean;
|
|
33
|
+
/** URL-only route clustering: id-like segments collapse to `[id]`. */
|
|
34
|
+
export declare function clusterPath(pathname: string): string;
|
|
35
|
+
export declare function loadRepoModel(reader: RepoReader, framework: FrameworkInfo): RepoModel;
|
|
36
|
+
export declare function buildGroups(config: GeoConfig, url: UrlEvidence | null, repo: RepoModel | null): RouteGroup[];
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { analyzeNextApp, matchRoute } from "../frameworks/nextjs-app/analyze.js";
|
|
2
|
+
import { analyzeStatic } from "../frameworks/static/analyze.js";
|
|
3
|
+
import { parseSitemap } from "../sitemap/sitemap.js";
|
|
4
|
+
/** `/**` matches everything, `*` one segment; `/admin/**` also matches `/admin`. */
|
|
5
|
+
export function globMatch(pattern, path) {
|
|
6
|
+
const p = pattern.replace(/\/+$/, '') || '/';
|
|
7
|
+
const target = path.replace(/\/+$/, '') || '/';
|
|
8
|
+
if (p === '/**')
|
|
9
|
+
return true;
|
|
10
|
+
let re = '';
|
|
11
|
+
for (let i = 0; i < p.length; i++) {
|
|
12
|
+
const c = p[i];
|
|
13
|
+
if (c === '*' && p[i + 1] === '*') {
|
|
14
|
+
if (re.endsWith('/'))
|
|
15
|
+
re = `${re.slice(0, -1)}(?:/.*)?`;
|
|
16
|
+
else
|
|
17
|
+
re += '.*';
|
|
18
|
+
i++;
|
|
19
|
+
}
|
|
20
|
+
else if (c === '*')
|
|
21
|
+
re += '[^/]*';
|
|
22
|
+
else
|
|
23
|
+
re += c.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
|
|
24
|
+
}
|
|
25
|
+
return new RegExp(`^${re}$`).test(target);
|
|
26
|
+
}
|
|
27
|
+
export function includedPath(config, path) {
|
|
28
|
+
if (!config.routes.include.some((g) => globMatch(g, path)))
|
|
29
|
+
return false;
|
|
30
|
+
return !config.routes.exclude.some((g) => globMatch(g, path));
|
|
31
|
+
}
|
|
32
|
+
const DYNAMIC_SEGMENT = /^(\d+|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|(?=[0-9a-f]*\d)[0-9a-f]{12,})$/i;
|
|
33
|
+
/** URL-only route clustering: id-like segments collapse to `[id]`. */
|
|
34
|
+
export function clusterPath(pathname) {
|
|
35
|
+
const parts = pathname.split('/').filter(Boolean).map((s) => (DYNAMIC_SEGMENT.test(s) ? '[id]' : decodeURIComponentSafe(s)));
|
|
36
|
+
return `/${parts.join('/')}`;
|
|
37
|
+
}
|
|
38
|
+
function decodeURIComponentSafe(s) {
|
|
39
|
+
try {
|
|
40
|
+
return decodeURIComponent(s);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return s;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function staticPathKey(pathname) {
|
|
47
|
+
const p = pathname.replace(/\/index\.html?$/, '/').replace(/\.html?$/, '').replace(/\/+$/, '');
|
|
48
|
+
return p === '' ? '/' : p;
|
|
49
|
+
}
|
|
50
|
+
export function loadRepoModel(reader, framework) {
|
|
51
|
+
return {
|
|
52
|
+
reader,
|
|
53
|
+
framework,
|
|
54
|
+
next: framework.id === 'nextjs-app' ? analyzeNextApp(reader) : null,
|
|
55
|
+
static: framework.id === 'static' ? analyzeStatic(reader) : null,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function literalSitemapPaths(repo) {
|
|
59
|
+
const files = repo.next ? [...repo.next.sitemap.publicXml, ...(repo.next.sitemap.appXml ? [repo.next.sitemap.appXml] : [])] : repo.static?.sitemapFiles ?? [];
|
|
60
|
+
if (repo.next && repo.next.sitemap.appCode.length > 0)
|
|
61
|
+
return null;
|
|
62
|
+
if (files.length === 0)
|
|
63
|
+
return null;
|
|
64
|
+
const out = new Set();
|
|
65
|
+
for (const f of files) {
|
|
66
|
+
const text = repo.reader.read(f);
|
|
67
|
+
if (text === null)
|
|
68
|
+
return null;
|
|
69
|
+
for (const loc of parseSitemap(text).locs)
|
|
70
|
+
out.add(new URL(loc).pathname.replace(/\/+$/, '') || '/');
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
export function buildGroups(config, url, repo) {
|
|
75
|
+
const groups = new Map();
|
|
76
|
+
const add = (route, init) => {
|
|
77
|
+
let g = groups.get(route);
|
|
78
|
+
if (!g) {
|
|
79
|
+
g = { route, urls: [], next: init.next ?? null, static: init.static ?? null, listed: null, linked: false };
|
|
80
|
+
groups.set(route, g);
|
|
81
|
+
}
|
|
82
|
+
return g;
|
|
83
|
+
};
|
|
84
|
+
if (repo?.next)
|
|
85
|
+
for (const r of repo.next.routes)
|
|
86
|
+
if (includedPath(config, r.route))
|
|
87
|
+
add(r.route, { next: r });
|
|
88
|
+
if (repo?.static)
|
|
89
|
+
for (const p of repo.static.pages)
|
|
90
|
+
if (includedPath(config, p.route))
|
|
91
|
+
add(p.route, { static: p });
|
|
92
|
+
if (url) {
|
|
93
|
+
const listed = new Set(url.sitemapUrls.map((u) => staticPathKey(new URL(u).pathname)));
|
|
94
|
+
const sitemapRead = url.sitemaps.some((s) => s.parsed && s.parsed.kind !== 'invalid');
|
|
95
|
+
for (const page of url.pages.values()) {
|
|
96
|
+
if (!page.isHtml && page.result.status === 200)
|
|
97
|
+
continue;
|
|
98
|
+
const path = new URL(page.url).pathname;
|
|
99
|
+
if (!includedPath(config, path))
|
|
100
|
+
continue;
|
|
101
|
+
let route;
|
|
102
|
+
if (repo?.next)
|
|
103
|
+
route = matchRoute(repo.next.routes, path)?.route ?? clusterPath(path);
|
|
104
|
+
else if (repo?.static)
|
|
105
|
+
route = staticPathKey(path);
|
|
106
|
+
else
|
|
107
|
+
route = clusterPath(path);
|
|
108
|
+
const g = add(route, {});
|
|
109
|
+
g.urls.push(page.url);
|
|
110
|
+
if (sitemapRead)
|
|
111
|
+
g.listed = (g.listed ?? false) || listed.has(staticPathKey(path));
|
|
112
|
+
if (url.inbound.get(page.url) && [...url.inbound.get(page.url)].some((from) => from !== page.url))
|
|
113
|
+
g.linked = true;
|
|
114
|
+
}
|
|
115
|
+
for (const g of groups.values())
|
|
116
|
+
if (g.listed === null && sitemapRead)
|
|
117
|
+
g.listed = false;
|
|
118
|
+
}
|
|
119
|
+
else if (repo) {
|
|
120
|
+
const paths = literalSitemapPaths(repo);
|
|
121
|
+
if (paths)
|
|
122
|
+
for (const g of groups.values())
|
|
123
|
+
g.listed = paths.has(g.route);
|
|
124
|
+
}
|
|
125
|
+
for (const g of groups.values())
|
|
126
|
+
g.urls.sort();
|
|
127
|
+
return [...groups.values()].sort((a, b) => (a.route < b.route ? -1 : a.route > b.route ? 1 : 0));
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/rules/context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Directives } from '../html/parse.ts';
|
|
2
|
+
import type { Evaluation, InformationalObservation, RuleId } from '../types.ts';
|
|
3
|
+
import type { ScanContext } from './context.ts';
|
|
4
|
+
/** X-Robots-Tag: comma-separated, where `agent:` starts a new scope. */
|
|
5
|
+
export declare function parseXRobotsTag(value: string): Directives[];
|
|
6
|
+
export declare const RULE_ORDER: readonly RuleId[];
|
|
7
|
+
export interface RuleRun {
|
|
8
|
+
readonly evaluations: Evaluation[];
|
|
9
|
+
readonly informational: InformationalObservation[];
|
|
10
|
+
}
|
|
11
|
+
export declare function runRules(ctx: ScanContext, only: ReadonlySet<RuleId> | null, skip: ReadonlySet<RuleId>): RuleRun;
|