@eightmedia/flightcheck 0.0.0-stage → 1.0.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/CHANGELOG.md +16 -0
- package/LICENSE +691 -0
- package/README.md +152 -2
- package/dist/analyze/fixManifest.d.ts +65 -0
- package/dist/analyze/fixManifest.d.ts.map +1 -0
- package/dist/analyze/fixManifest.js +588 -0
- package/dist/analyze/report.d.ts +283 -0
- package/dist/analyze/report.d.ts.map +1 -0
- package/dist/analyze/report.js +726 -0
- package/dist/analyze/reportTier1.d.ts +46 -0
- package/dist/analyze/reportTier1.d.ts.map +1 -0
- package/dist/analyze/reportTier1.js +251 -0
- package/dist/analyze/stack.d.ts +33 -0
- package/dist/analyze/stack.d.ts.map +1 -0
- package/dist/analyze/stack.js +188 -0
- package/dist/analyze/structuredReport.d.ts +62 -0
- package/dist/analyze/structuredReport.d.ts.map +1 -0
- package/dist/analyze/structuredReport.js +524 -0
- package/dist/analyze.d.ts +3 -0
- package/dist/analyze.d.ts.map +1 -0
- package/dist/analyze.js +40 -0
- package/dist/checks/agent-ready.d.ts +63 -0
- package/dist/checks/agent-ready.d.ts.map +1 -0
- package/dist/checks/agent-ready.js +219 -0
- package/dist/checks/analyze.d.ts +15 -0
- package/dist/checks/analyze.d.ts.map +1 -0
- package/dist/checks/analyze.js +63 -0
- package/dist/checks/crawl.d.ts +28 -0
- package/dist/checks/crawl.d.ts.map +1 -0
- package/dist/checks/crawl.js +86 -0
- package/dist/checks/dns.d.ts +25 -0
- package/dist/checks/dns.d.ts.map +1 -0
- package/dist/checks/dns.js +80 -0
- package/dist/checks/index.d.ts +24 -0
- package/dist/checks/index.d.ts.map +1 -0
- package/dist/checks/index.js +12 -0
- package/dist/checks/issues.d.ts +8 -0
- package/dist/checks/issues.d.ts.map +1 -0
- package/dist/checks/issues.js +3 -0
- package/dist/checks/redirect.d.ts +38 -0
- package/dist/checks/redirect.d.ts.map +1 -0
- package/dist/checks/redirect.js +209 -0
- package/dist/checks/registry.d.ts +8 -0
- package/dist/checks/registry.d.ts.map +1 -0
- package/dist/checks/registry.js +275 -0
- package/dist/checks/robots-txt.d.ts +16 -0
- package/dist/checks/robots-txt.d.ts.map +1 -0
- package/dist/checks/robots-txt.js +99 -0
- package/dist/checks/runner.d.ts +9 -0
- package/dist/checks/runner.d.ts.map +1 -0
- package/dist/checks/runner.js +110 -0
- package/dist/checks/security-headers.d.ts +12 -0
- package/dist/checks/security-headers.d.ts.map +1 -0
- package/dist/checks/security-headers.js +53 -0
- package/dist/checks/sitemap.d.ts +36 -0
- package/dist/checks/sitemap.d.ts.map +1 -0
- package/dist/checks/sitemap.js +140 -0
- package/dist/checks/types.d.ts +72 -0
- package/dist/checks/types.d.ts.map +1 -0
- package/dist/checks/types.js +20 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +125 -0
- package/dist/crawler/checkGroups.d.ts +7 -0
- package/dist/crawler/checkGroups.d.ts.map +1 -0
- package/dist/crawler/checkGroups.js +29 -0
- package/dist/crawler/crawlState.d.ts +7 -0
- package/dist/crawler/crawlState.d.ts.map +1 -0
- package/dist/crawler/crawlState.js +6 -0
- package/dist/crawler/defaults.d.ts +11 -0
- package/dist/crawler/defaults.d.ts.map +1 -0
- package/dist/crawler/defaults.js +10 -0
- package/dist/crawler/extractors.d.ts +5 -0
- package/dist/crawler/extractors.d.ts.map +1 -0
- package/dist/crawler/extractors.js +169 -0
- package/dist/crawler/jsAssets.d.ts +2 -0
- package/dist/crawler/jsAssets.d.ts.map +1 -0
- package/dist/crawler/jsAssets.js +22 -0
- package/dist/crawler/outputPaths.d.ts +14 -0
- package/dist/crawler/outputPaths.d.ts.map +1 -0
- package/dist/crawler/outputPaths.js +51 -0
- package/dist/crawler/reporter.d.ts +3 -0
- package/dist/crawler/reporter.d.ts.map +1 -0
- package/dist/crawler/reporter.js +12 -0
- package/dist/crawler/robots.d.ts +5 -0
- package/dist/crawler/robots.d.ts.map +1 -0
- package/dist/crawler/robots.js +29 -0
- package/dist/crawler/runCrawler.d.ts +22 -0
- package/dist/crawler/runCrawler.d.ts.map +1 -0
- package/dist/crawler/runCrawler.js +172 -0
- package/dist/crawler/schema.d.ts +87 -0
- package/dist/crawler/schema.d.ts.map +1 -0
- package/dist/crawler/schema.js +44 -0
- package/dist/crawler/secrets.d.ts +4 -0
- package/dist/crawler/secrets.d.ts.map +1 -0
- package/dist/crawler/secrets.js +70 -0
- package/dist/crawler/sitemap.d.ts +8 -0
- package/dist/crawler/sitemap.d.ts.map +1 -0
- package/dist/crawler/sitemap.js +105 -0
- package/dist/crawler/stack.d.ts +3 -0
- package/dist/crawler/stack.d.ts.map +1 -0
- package/dist/crawler/stack.js +1 -0
- package/dist/crawler/stackDetect.d.ts +24 -0
- package/dist/crawler/stackDetect.d.ts.map +1 -0
- package/dist/crawler/stackDetect.js +604 -0
- package/dist/crawler/tier1Extractors.d.ts +5 -0
- package/dist/crawler/tier1Extractors.d.ts.map +1 -0
- package/dist/crawler/tier1Extractors.js +255 -0
- package/dist/crawler/tracking.d.ts +8 -0
- package/dist/crawler/tracking.d.ts.map +1 -0
- package/dist/crawler/tracking.js +50 -0
- package/dist/crawler/urlUtils.d.ts +12 -0
- package/dist/crawler/urlUtils.d.ts.map +1 -0
- package/dist/crawler/urlUtils.js +53 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/docs/agent-fixes.md +17 -0
- package/docs/agent-ready.md +44 -0
- package/docs/analyze.md +103 -0
- package/docs/check-api.md +142 -0
- package/docs/checks.md +116 -0
- package/docs/crawler.md +158 -0
- package/docs/index.md +24 -0
- package/docs/qc-checks-plan.md +796 -0
- package/docs/stack.md +99 -0
- package/package.json +86 -4
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
# QC Checks Implementation Plan
|
|
2
|
+
|
|
3
|
+
Plan to extend Flightcheck into a full quality-control tool for delivered and maintained
|
|
4
|
+
sites. Covers hreflang, robots.txt, stack detection, sitemap validation, legal pages,
|
|
5
|
+
image alt text, broken links, accessibility, security headers, DNS, and optional
|
|
6
|
+
performance auditing.
|
|
7
|
+
|
|
8
|
+
**Status:** in progress — Phase 1 shipped, Phase 2 mostly shipped
|
|
9
|
+
**Last updated:** 2026-09-28
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Goal
|
|
14
|
+
|
|
15
|
+
Add the checks below as modular, exportable functions (see `docs/check-api.md`) so they can:
|
|
16
|
+
|
|
17
|
+
- Run from the CLI (`pnpm run flightcheck`)
|
|
18
|
+
- Run independently via API endpoints in a consuming project
|
|
19
|
+
- Execute in parallel where dependencies allow
|
|
20
|
+
|
|
21
|
+
Each check produces structured JSON output and feeds into the Markdown TODO report via
|
|
22
|
+
`analyze`.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Tiered audit strategy
|
|
27
|
+
|
|
28
|
+
Lighthouse is powerful but heavy (~30s+ per URL, requires Chrome). Most of its **SEO**
|
|
29
|
+
and **best-practices** signal is static HTML analysis — which Flightcheck already does
|
|
30
|
+
during crawl. Running Lighthouse on every page (or even as the primary a11y engine) is
|
|
31
|
+
the wrong default.
|
|
32
|
+
|
|
33
|
+
Instead, use four tiers:
|
|
34
|
+
|
|
35
|
+
| Tier | Engine | Chrome? | Covers |
|
|
36
|
+
| --- | --- | --- | --- |
|
|
37
|
+
| **0** | Crawl + analyze (existing) | No | Meta, OG, tracking, secrets, main landmark |
|
|
38
|
+
| **1** | Fetch + cheerio (implemented) | No | SEO depth, headers, DNS, links, robots, sitemap, legal, stack, static a11y hints |
|
|
39
|
+
| **2** | axe-core + Playwright (planned) | Yes, 3–5 URLs | Real WCAG a11y (contrast, focus, ARIA in rendered DOM) |
|
|
40
|
+
| **3** | Lighthouse or PageSpeed API (optional) | Yes / remote | Performance scores + Core Web Vitals only |
|
|
41
|
+
|
|
42
|
+
**Design principle:** SEO and best practices default to **Tier 0 + 1**. Accessibility
|
|
43
|
+
defaults to **Tier 1 static rules + Tier 2 sampled axe**. Lighthouse is **opt-in** for
|
|
44
|
+
client-facing performance reports.
|
|
45
|
+
|
|
46
|
+
### What each tier replaces from Lighthouse
|
|
47
|
+
|
|
48
|
+
| Lighthouse category | Flightcheck approach |
|
|
49
|
+
| --- | --- |
|
|
50
|
+
| **SEO** | Tier 0/1 — crawl meta groups, hreflang, robots, sitemap, structured data (future) |
|
|
51
|
+
| **Best practices** | Tier 1 — security headers, HTTPS, mixed content, image alt, deprecated patterns |
|
|
52
|
+
| **Accessibility** | Tier 1 static + Tier 2 `@axe-core/playwright` on sampled URLs |
|
|
53
|
+
| **Performance** | Tier 3 only — Lighthouse or PageSpeed Insights API on sampled URLs |
|
|
54
|
+
|
|
55
|
+
### Weight comparison
|
|
56
|
+
|
|
57
|
+
| Approach | Speed | Chrome? | Best for |
|
|
58
|
+
| --- | --- | --- | --- |
|
|
59
|
+
| Crawl + custom rules | Fast | No | SEO, metadata, links, infra |
|
|
60
|
+
| axe on crawled HTML (jsdom) | Fast | No | Structure-only a11y (partial) |
|
|
61
|
+
| `@axe-core/playwright` | ~5–15s × N URLs | Yes | Full a11y on samples |
|
|
62
|
+
| Lighthouse | ~30s+ × N URLs | Yes | CWV + performance score |
|
|
63
|
+
| PageSpeed Insights API | ~10–20s × N URLs | Remote | CWV without local Chrome |
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Current baseline
|
|
68
|
+
|
|
69
|
+
| Area | Today |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| Metadata / SEO | Crawl `meta`, `social`, `icons` groups + analyze report |
|
|
72
|
+
| Tracking | GTM, GA4, Matomo, etc. detection |
|
|
73
|
+
| Security | Secret scan in HTML + same-origin JS |
|
|
74
|
+
| Landmarks | `<main>` count |
|
|
75
|
+
| Infra | `robots-txt`, `security-headers`, `dns` checks implemented and reported |
|
|
76
|
+
| Sitemap | `sitemap` check implemented — validates and diffs against crawled URLs |
|
|
77
|
+
| Standalone checks | `security-headers`, `dns`, `robots-txt`, `sitemap`, `redirect` implemented; `lighthouse` registered but unsupported |
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Architecture
|
|
82
|
+
|
|
83
|
+
### Two integration patterns
|
|
84
|
+
|
|
85
|
+
**Pattern A — Crawl extensions** (per-page data during crawl)
|
|
86
|
+
|
|
87
|
+
Best when the check inspects each HTML page:
|
|
88
|
+
|
|
89
|
+
- hreflang
|
|
90
|
+
- image alt (+ static a11y hints: headings, multiple h1, form labels, lang, positive tabindex)
|
|
91
|
+
- broken links (internal + optional external)
|
|
92
|
+
- stack fingerprints (per-page signals aggregated site-wide)
|
|
93
|
+
|
|
94
|
+
Add new check groups in `checkGroups.ts`:
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
meta, icons, social, tracking, security (existing)
|
|
98
|
+
links, images, hreflang, stack, a11y-static (new crawl groups)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Pattern B — Standalone checks** (origin-level or sampled URLs)
|
|
102
|
+
|
|
103
|
+
Best when the check hits one URL, DNS, or a small URL sample:
|
|
104
|
+
|
|
105
|
+
- `robots-txt`
|
|
106
|
+
- `sitemap`
|
|
107
|
+
- `legal-pages`
|
|
108
|
+
- `security-headers`
|
|
109
|
+
- `dns`
|
|
110
|
+
- `a11y-axe` (sampled URLs, Tier 2)
|
|
111
|
+
- `performance` (sampled URLs, Tier 3 — optional)
|
|
112
|
+
|
|
113
|
+
Each gets `src/checks/<id>.ts` + registry entry + analyze report section.
|
|
114
|
+
|
|
115
|
+
### Execution waves (updated)
|
|
116
|
+
|
|
117
|
+
```text
|
|
118
|
+
Wave 1 (parallel, no browser):
|
|
119
|
+
crawl, robots-txt, security-headers, dns, legal-pages
|
|
120
|
+
|
|
121
|
+
Wave 2 (after crawl, parallel where possible):
|
|
122
|
+
sitemap (compare with crawledUrls)
|
|
123
|
+
a11y-axe (3–5 sampled URLs via Playwright + axe-core)
|
|
124
|
+
|
|
125
|
+
Wave 3 (optional, after crawl):
|
|
126
|
+
performance (Lighthouse or PageSpeed API on same URL sample)
|
|
127
|
+
|
|
128
|
+
Wave 4:
|
|
129
|
+
analyze (merges all check outputs into TODO report)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Wave 2 and 3 share a URL sampler (`src/checks/sampleUrls.ts`):
|
|
133
|
+
|
|
134
|
+
1. Homepage (`startUrl`)
|
|
135
|
+
2. One URL per unique `<meta name="template">` value
|
|
136
|
+
3. Max 5 URLs (configurable)
|
|
137
|
+
|
|
138
|
+
### Shared context
|
|
139
|
+
|
|
140
|
+
Extend `CheckOutputs` in `src/checks/types.ts`:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
type CheckOutputs = {
|
|
144
|
+
crawl?: CrawlCheckResult;
|
|
145
|
+
analyze?: AnalyzeCheckResult;
|
|
146
|
+
robotsTxt?: RobotsTxtCheckResult;
|
|
147
|
+
sitemap?: SitemapCheckResult;
|
|
148
|
+
legalPages?: LegalPagesCheckResult;
|
|
149
|
+
a11yAxe?: A11yAxeCheckResult;
|
|
150
|
+
performance?: PerformanceCheckResult;
|
|
151
|
+
securityHeaders?: SecurityHeadersCheckResult;
|
|
152
|
+
dns?: DnsCheckResult;
|
|
153
|
+
};
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Crawl result gains optional site-level aggregates:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
type CrawlCheckResult = {
|
|
160
|
+
records: VerificationRecord[];
|
|
161
|
+
crawledUrls: string[];
|
|
162
|
+
stack?: StackDetectionResult;
|
|
163
|
+
// ...
|
|
164
|
+
};
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Check specifications
|
|
170
|
+
|
|
171
|
+
### 1. Hreflang (`hreflang` — crawl group)
|
|
172
|
+
|
|
173
|
+
**Purpose:** Validate multilingual SEO linking.
|
|
174
|
+
|
|
175
|
+
**Per page, extract:**
|
|
176
|
+
|
|
177
|
+
- `<link rel="alternate" hreflang="…" href="…">`
|
|
178
|
+
- `<html lang="…">` as fallback context
|
|
179
|
+
|
|
180
|
+
**Rules:**
|
|
181
|
+
|
|
182
|
+
| Rule | Severity |
|
|
183
|
+
| --- | --- |
|
|
184
|
+
| Page has hreflang tags but no self-referencing entry | warning |
|
|
185
|
+
| `hreflang` value invalid (not BCP 47-ish) | warning |
|
|
186
|
+
| Target URL returns non-200 (optional fetch, rate-limited) | error |
|
|
187
|
+
| Missing return link (A→B but B→A missing) | error |
|
|
188
|
+
| No `x-default` on multilingual sites (≥2 languages detected site-wide) | warning |
|
|
189
|
+
| Duplicate `hreflang` for same page | error |
|
|
190
|
+
|
|
191
|
+
**Output:** `record.hreflang: { tags: HreflangTag[]; issues: HreflangIssue[] }`
|
|
192
|
+
|
|
193
|
+
**Analyze todos:**
|
|
194
|
+
|
|
195
|
+
- `Fix hreflang self-reference on page`
|
|
196
|
+
- `Add x-default hreflang in site layout`
|
|
197
|
+
- `Add return hreflang link: {lang} ↔ {url}`
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
### 2. Robots.txt (`robots-txt` — standalone check) — IMPLEMENTED
|
|
202
|
+
|
|
203
|
+
**Status:** Implemented in `src/checks/robots-txt.ts` (`runRobotsTxtCheck()`).
|
|
204
|
+
|
|
205
|
+
**Purpose:** Report robots.txt presence and common misconfigurations.
|
|
206
|
+
|
|
207
|
+
**Note:** The robots.txt report logic lives in the standalone `src/checks/robots-txt.ts`.
|
|
208
|
+
`src/crawler/robots.ts` is separate and only provides `isAllowed()` for `--respect-robots-txt`
|
|
209
|
+
crawl behaviour — it is **not** extended by this check.
|
|
210
|
+
|
|
211
|
+
**Fetch:** `{origin}/robots.txt`
|
|
212
|
+
|
|
213
|
+
**Rules:**
|
|
214
|
+
|
|
215
|
+
| Rule | Severity |
|
|
216
|
+
| --- | --- |
|
|
217
|
+
| robots.txt missing (404) | warning |
|
|
218
|
+
| robots.txt not parseable | error |
|
|
219
|
+
| `Disallow: /` for `User-agent: *` on production | critical |
|
|
220
|
+
| Sitemap directive missing | warning |
|
|
221
|
+
| Sitemap URL not reachable | error |
|
|
222
|
+
| Crawl-delay extremely high | review |
|
|
223
|
+
|
|
224
|
+
**Output:**
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
type RobotsTxtCheckResult = {
|
|
228
|
+
url: string;
|
|
229
|
+
status: number | null;
|
|
230
|
+
body?: string;
|
|
231
|
+
sitemapUrls: string[];
|
|
232
|
+
issues: Issue[];
|
|
233
|
+
};
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Note:** Keep `--respect-robots-txt` as crawl behaviour; this check is **report-only**
|
|
237
|
+
by default.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
### 3. Stack detection (`stack` — crawl group + aggregation) — IMPLEMENTED
|
|
242
|
+
|
|
243
|
+
**Status:** Implemented — per-page fingerprinting in `src/crawler/stackDetect.ts`,
|
|
244
|
+
site-wide aggregation in `src/analyze/stack.ts`.
|
|
245
|
+
|
|
246
|
+
**Purpose:** Report CMS, framework, and hosting signals for handover docs and debugging.
|
|
247
|
+
|
|
248
|
+
**Approach:** Heuristic fingerprinting (no Wappalyzer dependency initially). Aggregate
|
|
249
|
+
signals across all crawled pages; confidence score per detection.
|
|
250
|
+
|
|
251
|
+
**Signal sources:**
|
|
252
|
+
|
|
253
|
+
| Source | Examples |
|
|
254
|
+
| --- | --- |
|
|
255
|
+
| `<meta name="generator">` | WordPress, Sanity Studio |
|
|
256
|
+
| HTML comments / data attributes | `__NEXT_DATA__`, `data-sanity`, `data-nuxt` |
|
|
257
|
+
| Script/link paths | `/wp-content/`, `/_next/`, `/@vite/` |
|
|
258
|
+
| Response headers (once per origin) | `x-powered-by`, `server`, `x-vercel-id` |
|
|
259
|
+
| Cookie names | `__prerender_bypass`, `wp-settings` |
|
|
260
|
+
| Existing `<meta name="template">` | Already in crawl |
|
|
261
|
+
|
|
262
|
+
**Output:**
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
type StackDetectionResult = {
|
|
266
|
+
detections: Array<{
|
|
267
|
+
name: string;
|
|
268
|
+
category: "cms" | "framework" | "hosting" | "analytics" | "other";
|
|
269
|
+
confidence: "high" | "medium" | "low";
|
|
270
|
+
evidence: string[];
|
|
271
|
+
}>;
|
|
272
|
+
};
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Analyze:** New `## Stack` section (informational, not necessarily todos unless
|
|
276
|
+
conflicting signals).
|
|
277
|
+
|
|
278
|
+
**Future:** Optional `@wappalyzer/wappalyzer-core` if heuristics are insufficient.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
### 4. Sitemap (`sitemap` — standalone check) — IMPLEMENTED
|
|
283
|
+
|
|
284
|
+
**Status:** Implemented in `src/checks/sitemap.ts` (`runSitemapCheck()`).
|
|
285
|
+
|
|
286
|
+
**Purpose:** Verify sitemap exists and matches crawled site.
|
|
287
|
+
|
|
288
|
+
**Reuse:** `src/crawler/sitemap.ts` → `collectUrlsFromSitemap()`
|
|
289
|
+
|
|
290
|
+
**Inputs:**
|
|
291
|
+
|
|
292
|
+
- `startUrl` / origin
|
|
293
|
+
- `crawledUrls` from crawl output (normalized, no query/hash)
|
|
294
|
+
- Optional `robotsTxt.sitemapUrls` from robots check
|
|
295
|
+
|
|
296
|
+
**Rules:**
|
|
297
|
+
|
|
298
|
+
| Rule | Severity |
|
|
299
|
+
| --- | --- |
|
|
300
|
+
| No sitemap found (robots + `/sitemap.xml` both fail) | error |
|
|
301
|
+
| Sitemap URLs not reachable (4xx/5xx) | error |
|
|
302
|
+
| In sitemap but not crawled (and not excluded) | warning |
|
|
303
|
+
| Crawled but not in sitemap | warning |
|
|
304
|
+
| Over N% mismatch | error (configurable threshold, default 10%) |
|
|
305
|
+
|
|
306
|
+
**Dependency:** Runs in wave 2 after crawl when comparing lists.
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
### 5. Legal pages (`legal-pages` — standalone check)
|
|
311
|
+
|
|
312
|
+
**Purpose:** Confirm required legal pages exist and are linked.
|
|
313
|
+
|
|
314
|
+
**Configurable paths (defaults):**
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
const DEFAULT_LEGAL_PATHS = [
|
|
318
|
+
"/privacy", "/privacy-policy", "/privacybeleid",
|
|
319
|
+
"/cookies", "/cookie-policy", "/cookiebeleid",
|
|
320
|
+
"/terms", "/algemene-voorwaarden",
|
|
321
|
+
"/disclaimer",
|
|
322
|
+
];
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Rules:**
|
|
326
|
+
|
|
327
|
+
| Rule | Severity |
|
|
328
|
+
| --- | --- |
|
|
329
|
+
| No privacy page reachable | error |
|
|
330
|
+
| No cookie policy (EU/NL sites) | error |
|
|
331
|
+
| Linked from footer but 404 | critical |
|
|
332
|
+
| Page exists but not linked from any crawled page | warning |
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
### 6. Image alt + static a11y (`images`, `a11y-static` — crawl groups) — IMPLEMENTED
|
|
337
|
+
|
|
338
|
+
**Status:** Implemented in `src/crawler/tier1Extractors.ts`; output fields `images` and
|
|
339
|
+
`a11yStatic` are defined on `VerificationRecord` in `src/crawler/schema.ts`.
|
|
340
|
+
|
|
341
|
+
**Purpose:** Tier 1 accessibility and SEO — no browser required.
|
|
342
|
+
|
|
343
|
+
**Image alt (`images` group):**
|
|
344
|
+
|
|
345
|
+
- `<img>` without `alt` attribute
|
|
346
|
+
- `<img alt="">` on non-decorative images
|
|
347
|
+
- Generic alt text (`image`, `photo`, filename)
|
|
348
|
+
|
|
349
|
+
Issue messages include a clickable markdown link `[filename](url)` when the image URL can be
|
|
350
|
+
resolved from `src`, `srcset`, `<picture>` / `<source>` elements, or nested `/_next/image?url=…`
|
|
351
|
+
proxy URLs.
|
|
352
|
+
|
|
353
|
+
**Static a11y hints (`a11y-static` group):** implemented rule IDs:
|
|
354
|
+
|
|
355
|
+
- `heading-skip` — heading hierarchy skips a rank (h1 → h3)
|
|
356
|
+
- `multiple-h1` — multiple h1 elements
|
|
357
|
+
- `unlabeled-input` — form inputs without associated labels
|
|
358
|
+
- `missing-lang` — missing `lang` on `<html>`
|
|
359
|
+
- `positive-tabindex` — positive tabindex values
|
|
360
|
+
|
|
361
|
+
**Decorative image exclusions:**
|
|
362
|
+
|
|
363
|
+
- `aria-hidden="true"`, `role="presentation"`, ≤2px dimensions
|
|
364
|
+
|
|
365
|
+
**Output:** `record.images`, `record.a11yStatic: { issues[] }`
|
|
366
|
+
|
|
367
|
+
**Limitation:** Cannot detect color contrast, focus visibility, or JS-rendered widgets.
|
|
368
|
+
Those require Tier 2 axe.
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
### 7. Broken links (`links` — crawl group)
|
|
373
|
+
|
|
374
|
+
**Purpose:** Find internal broken links and optionally external failures.
|
|
375
|
+
|
|
376
|
+
**Fetch strategy:**
|
|
377
|
+
|
|
378
|
+
- Internal: check against crawl status map first; fetch uncached with concurrency limit
|
|
379
|
+
- External: optional (`--check-external-links`), HEAD request, timeout 10s, max N per site
|
|
380
|
+
|
|
381
|
+
**Rules:**
|
|
382
|
+
|
|
383
|
+
| Rule | Severity |
|
|
384
|
+
| --- | --- |
|
|
385
|
+
| Internal link → 404/500 | error |
|
|
386
|
+
| Internal link → redirect loop | error |
|
|
387
|
+
| External link → 4xx/5xx | warning |
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
### 8. Accessibility — axe (`a11y-axe` — standalone check, Tier 2)
|
|
392
|
+
|
|
393
|
+
**Purpose:** Real WCAG accessibility audit on a small URL sample.
|
|
394
|
+
|
|
395
|
+
**Why not Lighthouse for a11y:** Lighthouse embeds axe-core but adds performance overhead
|
|
396
|
+
and aggregates away detail. Running axe directly via Playwright is faster and gives
|
|
397
|
+
better violation output.
|
|
398
|
+
|
|
399
|
+
**Implementation:** `@axe-core/playwright`
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
import { AxeBuilder } from "@axe-core/playwright";
|
|
403
|
+
|
|
404
|
+
const results = await new AxeBuilder({ page })
|
|
405
|
+
.withTags(["wcag2a", "wcag2aa", "best-practice"])
|
|
406
|
+
.analyze();
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**Scope:** Same URL sampler as performance (max 5 URLs).
|
|
410
|
+
|
|
411
|
+
**Output per URL:**
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
type A11yAxePageResult = {
|
|
415
|
+
url: string;
|
|
416
|
+
violations: Array<{
|
|
417
|
+
id: string;
|
|
418
|
+
impact: "critical" | "serious" | "moderate" | "minor";
|
|
419
|
+
description: string;
|
|
420
|
+
helpUrl: string;
|
|
421
|
+
nodes: Array<{ html: string; target: string[]; failureSummary?: string }>;
|
|
422
|
+
}>;
|
|
423
|
+
passes: number;
|
|
424
|
+
incomplete: number;
|
|
425
|
+
};
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
**Thresholds (configurable):**
|
|
429
|
+
|
|
430
|
+
- Any `critical` violation → error
|
|
431
|
+
- Any `serious` violation → warning
|
|
432
|
+
|
|
433
|
+
**Analyze:** `## Accessibility (axe)` section — group violations by rule ID across
|
|
434
|
+
sampled pages; todos reference WCAG rule and affected selector.
|
|
435
|
+
|
|
436
|
+
**Optional Tier 1 supplement:** Run axe-core against crawled HTML strings via jsdom for
|
|
437
|
+
extra coverage on all pages. Disable rules that need computed styles (`color-contrast`,
|
|
438
|
+
`link-in-text-block`, `target-size`, etc.). Document as `--a11y-static-axe` experiment;
|
|
439
|
+
not required for v1.
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
### 9. Performance (`performance` — standalone check, Tier 3, optional)
|
|
444
|
+
|
|
445
|
+
**Purpose:** Core Web Vitals and performance scores for client reports. **Not required
|
|
446
|
+
for delivery QC** — opt-in via `--checks performance` or `--profile delivery-full`.
|
|
447
|
+
|
|
448
|
+
**Scope:** Same URL sampler (max 5 URLs).
|
|
449
|
+
|
|
450
|
+
**Implementation options:**
|
|
451
|
+
|
|
452
|
+
| Option | Pros | Cons |
|
|
453
|
+
| --- | --- | --- |
|
|
454
|
+
| `lighthouse` + `chrome-launcher` | Full control, offline | Heavy, needs Chrome |
|
|
455
|
+
| PageSpeed Insights API | No local Chrome | API key, rate limits, sends URL to Google |
|
|
456
|
+
| `web-vitals` + Playwright | Lightweight CWV only | No full audit categories |
|
|
457
|
+
|
|
458
|
+
**Recommended default:** PageSpeed Insights API for CI/cloud; local Lighthouse for
|
|
459
|
+
offline/dev when Chrome is available.
|
|
460
|
+
|
|
461
|
+
**Output per URL:**
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
type PerformancePageResult = {
|
|
465
|
+
url: string;
|
|
466
|
+
source: "lighthouse" | "pagespeed-insights";
|
|
467
|
+
scores: { performance: number | null };
|
|
468
|
+
metrics: {
|
|
469
|
+
lcp?: string;
|
|
470
|
+
cls?: string;
|
|
471
|
+
inp?: string;
|
|
472
|
+
ttfb?: string;
|
|
473
|
+
fcp?: string;
|
|
474
|
+
};
|
|
475
|
+
opportunities?: Array<{ id: string; title: string; displayValue?: string }>;
|
|
476
|
+
};
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
**Thresholds (configurable):**
|
|
480
|
+
|
|
481
|
+
- Performance score < 80 → warning
|
|
482
|
+
- LCP > 2.5s → warning
|
|
483
|
+
- CLS > 0.1 → warning
|
|
484
|
+
|
|
485
|
+
**Analyze:** `## Performance` section — scores table + top opportunities as todos.
|
|
486
|
+
|
|
487
|
+
**Note:** Do not duplicate SEO or a11y scores from Lighthouse here; those categories
|
|
488
|
+
are covered by Tier 0/1 and `a11y-axe`.
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
### 10. Security headers (`security-headers` — standalone check) — IMPLEMENTED
|
|
493
|
+
|
|
494
|
+
**Status:** Implemented in `src/checks/security-headers.ts` (`runSecurityHeadersCheck()`).
|
|
495
|
+
|
|
496
|
+
**Purpose:** Tier 1 best practices — verify HTTP security headers.
|
|
497
|
+
|
|
498
|
+
**Headers to check:**
|
|
499
|
+
|
|
500
|
+
| Header | Severity if missing |
|
|
501
|
+
| --- | --- |
|
|
502
|
+
| `Strict-Transport-Security` | error (if HTTPS) |
|
|
503
|
+
| `Content-Security-Policy` | warning |
|
|
504
|
+
| `X-Frame-Options` or CSP `frame-ancestors` | warning |
|
|
505
|
+
| `X-Content-Type-Options: nosniff` | warning |
|
|
506
|
+
| `Referrer-Policy` | review |
|
|
507
|
+
| `Permissions-Policy` | review |
|
|
508
|
+
|
|
509
|
+
Pure fetch — no browser needed.
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
### 11. DNS (`dns` — standalone check) — IMPLEMENTED
|
|
514
|
+
|
|
515
|
+
**Status:** Implemented in `src/checks/dns.ts` (`runDnsCheck()`).
|
|
516
|
+
|
|
517
|
+
**Purpose:** Record locations, ownership signals, and email security.
|
|
518
|
+
|
|
519
|
+
**Queries (Node `dns.promises` + optional `whoiser`):**
|
|
520
|
+
|
|
521
|
+
| Query | Data |
|
|
522
|
+
| --- | --- |
|
|
523
|
+
| A / AAAA | IP addresses |
|
|
524
|
+
| CNAME | CDN / hosting chain |
|
|
525
|
+
| MX / TXT / NS | Mail + SPF/DMARC |
|
|
526
|
+
| WHOIS (optional) | Registrar, expiry |
|
|
527
|
+
|
|
528
|
+
**Geo/locations:** Resolve A records → optional IP geolocation (informational).
|
|
529
|
+
|
|
530
|
+
**Note:** WHOIS opt-in (`dns: { whois: true }`).
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
### 12. Redirect (`redirect` — standalone check) — IMPLEMENTED
|
|
535
|
+
|
|
536
|
+
**Status:** Implemented in `src/checks/redirect.ts`. Probes the start URL (and www/non-www
|
|
537
|
+
alias), and when the origin is a pure redirect it drives **redirect-only mode** — short-circuiting
|
|
538
|
+
the pipeline to a reduced check set instead of a full crawl/analyze. Output feeds the
|
|
539
|
+
"Domain redirect" analyze section.
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
## Analyze report integration
|
|
544
|
+
|
|
545
|
+
Extend `src/analyze/report.ts` to consume a unified `FlightcheckReport` shape:
|
|
546
|
+
|
|
547
|
+
```typescript
|
|
548
|
+
type FlightcheckReport = {
|
|
549
|
+
crawl: VerificationRecord[];
|
|
550
|
+
robotsTxt?: RobotsTxtCheckResult;
|
|
551
|
+
sitemap?: SitemapCheckResult;
|
|
552
|
+
legalPages?: LegalPagesCheckResult;
|
|
553
|
+
a11yAxe?: A11yAxeCheckResult;
|
|
554
|
+
performance?: PerformanceCheckResult;
|
|
555
|
+
securityHeaders?: SecurityHeadersCheckResult;
|
|
556
|
+
dns?: DnsCheckResult;
|
|
557
|
+
stack?: StackDetectionResult;
|
|
558
|
+
};
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
The **Issues summary** and Tier 1 infra report sections (redirect, DNS, security headers,
|
|
562
|
+
robots.txt, sitemap, agent readiness) are implemented in `src/analyze/reportTier1.ts` and wired into the main
|
|
563
|
+
report via `src/analyze/report.ts`.
|
|
564
|
+
|
|
565
|
+
Report sections (top to bottom):
|
|
566
|
+
|
|
567
|
+
1. Summary (pages analyzed, fix scopes, **Issues summary** by category: infra, SEO, a11y, best practices, performance)
|
|
568
|
+
2. Security (secrets — existing)
|
|
569
|
+
3. DNS (infra overview)
|
|
570
|
+
4. Security headers
|
|
571
|
+
5. Stack (informational)
|
|
572
|
+
6. Robots.txt + sitemap
|
|
573
|
+
7. Agent readiness
|
|
574
|
+
8. Legal pages
|
|
575
|
+
9. Accessibility (axe) — Tier 2
|
|
576
|
+
10. Performance — Tier 3, if run
|
|
577
|
+
11. Fix site-wide
|
|
578
|
+
12. Fix in templates
|
|
579
|
+
13. Fix per page (hreflang, images, static a11y, broken links)
|
|
580
|
+
14. Issue coverage
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
## Implementation phases
|
|
585
|
+
|
|
586
|
+
### Phase 1 — Tier 1 quick wins (1–2 weeks) — SHIPPED
|
|
587
|
+
|
|
588
|
+
No browser required. Covers most SEO + best practices.
|
|
589
|
+
|
|
590
|
+
| Task | Type | Files | Status |
|
|
591
|
+
| --- | --- | --- | --- |
|
|
592
|
+
| Security headers check | standalone | `src/checks/security-headers.ts` | done |
|
|
593
|
+
| Robots.txt check | standalone | `src/checks/robots-txt.ts` | done |
|
|
594
|
+
| DNS check | standalone | `src/checks/dns.ts` | done |
|
|
595
|
+
| Image alt + static a11y crawl groups | crawl | `src/crawler/tier1Extractors.ts`, `schema.ts` | done |
|
|
596
|
+
| Register checks + wave plan | infra | `types.ts`, `registry.ts`, `runner.ts` | done |
|
|
597
|
+
| Analyze sections | report | `src/analyze/report.ts`, `src/analyze/reportTier1.ts` | done |
|
|
598
|
+
| Tests + docs | | `*.test.ts`, `docs/check-api.md` | done |
|
|
599
|
+
|
|
600
|
+
**Deliverable:**
|
|
601
|
+
|
|
602
|
+
```bash
|
|
603
|
+
pnpm run flightcheck -- https://example.com --checks crawl,security-headers,dns,robots-txt --parallel
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
---
|
|
607
|
+
|
|
608
|
+
### Phase 2 — Crawl depth + infra (2–3 weeks) — MOSTLY SHIPPED
|
|
609
|
+
|
|
610
|
+
| Task | Type | Files | Status |
|
|
611
|
+
| --- | --- | --- | --- |
|
|
612
|
+
| Stack detection | crawl + aggregate | `src/crawler/stackDetect.ts`, `src/analyze/stack.ts` | done |
|
|
613
|
+
| Sitemap compare | standalone | `src/checks/sitemap.ts` | done |
|
|
614
|
+
| Agent readiness | standalone | `src/checks/agent-ready.ts` | done |
|
|
615
|
+
| Image alt + static a11y | crawl | `src/crawler/tier1Extractors.ts` | done |
|
|
616
|
+
| Broken links group | crawl | `src/crawler/links.ts` | not implemented |
|
|
617
|
+
| Hreflang group | crawl | `src/crawler/hreflang.ts` | not implemented |
|
|
618
|
+
| Legal pages | standalone | `src/checks/legal-pages.ts` | not implemented |
|
|
619
|
+
| URL sampler (shared) | util | `src/checks/sampleUrls.ts` | not implemented |
|
|
620
|
+
| Analyze integration | report | `src/analyze/report.ts` | done |
|
|
621
|
+
|
|
622
|
+
**Deliverable:** Full metadata + link + infra report without any browser.
|
|
623
|
+
|
|
624
|
+
---
|
|
625
|
+
|
|
626
|
+
### Phase 3 — Tier 2 accessibility (1 week)
|
|
627
|
+
|
|
628
|
+
| Task | Notes |
|
|
629
|
+
| --- | --- |
|
|
630
|
+
| Add `@axe-core/playwright`, `playwright` | Document browser install (`pnpm exec playwright install chromium`) |
|
|
631
|
+
| `runA11yAxeCheck()` | Uses shared URL sampler |
|
|
632
|
+
| Analyze a11y section | Group violations by rule; WCAG-linked todos |
|
|
633
|
+
| Smoke test | Mark `@slow`, skip in default CI |
|
|
634
|
+
|
|
635
|
+
**Deliverable:**
|
|
636
|
+
|
|
637
|
+
```bash
|
|
638
|
+
pnpm run flightcheck -- https://example.com --checks crawl,a11y-axe --parallel
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
---
|
|
642
|
+
|
|
643
|
+
### Phase 4 — Tier 3 performance, optional (1 week)
|
|
644
|
+
|
|
645
|
+
| Task | Notes |
|
|
646
|
+
| --- | --- |
|
|
647
|
+
| `runPerformanceCheck()` | Lighthouse or PageSpeed Insights adapter pattern |
|
|
648
|
+
| `--performance-provider lighthouse\|pagespeed` | Default `pagespeed` in CI, `lighthouse` locally |
|
|
649
|
+
| Analyze performance section | CWV metrics + opportunities only |
|
|
650
|
+
| Deprecate standalone `lighthouse` check ID | Rename registry entry to `performance` (not yet done — `lighthouse` is still the registered id with `implemented: false` in `src/checks/registry.ts`) |
|
|
651
|
+
|
|
652
|
+
**Deliverable:**
|
|
653
|
+
|
|
654
|
+
```bash
|
|
655
|
+
pnpm run flightcheck -- https://example.com --profile delivery-full
|
|
656
|
+
# runs crawl + tier 1 + a11y-axe + performance on sampled URLs
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
---
|
|
660
|
+
|
|
661
|
+
### Phase 5 — Polish & maintenance (ongoing)
|
|
662
|
+
|
|
663
|
+
- Baseline diff (compare to last approved JSON)
|
|
664
|
+
- External link checking toggle
|
|
665
|
+
- Config profiles: `delivery`, `delivery-full`, `maintenance`
|
|
666
|
+
- Per-client `.flightcheck.json` (legal paths, thresholds)
|
|
667
|
+
- API endpoint wiring guide for consuming project
|
|
668
|
+
|
|
669
|
+
---
|
|
670
|
+
|
|
671
|
+
## Profiles (CLI)
|
|
672
|
+
|
|
673
|
+
| Profile | Checks | Browser? |
|
|
674
|
+
| --- | --- | --- |
|
|
675
|
+
| `default` | crawl + security-headers + dns + robots-txt + sitemap + analyze (`DEFAULT_CHECK_IDS` in `src/checks/types.ts`) | No |
|
|
676
|
+
| `delivery` | crawl + all Tier 1 + sitemap + legal + a11y-axe + analyze | Yes, 3–5 URLs |
|
|
677
|
+
| `delivery-full` | `delivery` + performance | Yes, 3–5 URLs |
|
|
678
|
+
| `maintenance` | crawl + analyze + baseline-diff (future) | No |
|
|
679
|
+
|
|
680
|
+
```bash
|
|
681
|
+
pnpm run flightcheck -- https://example.com --profile delivery
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
---
|
|
685
|
+
|
|
686
|
+
## New check IDs (registry)
|
|
687
|
+
|
|
688
|
+
Currently registered in `src/checks/registry.ts`:
|
|
689
|
+
|
|
690
|
+
```text
|
|
691
|
+
crawl (implemented)
|
|
692
|
+
analyze (implemented)
|
|
693
|
+
security-headers (implemented)
|
|
694
|
+
dns (implemented)
|
|
695
|
+
robots-txt (implemented)
|
|
696
|
+
sitemap (implemented)
|
|
697
|
+
lighthouse (registered, implemented: false — NOT yet renamed to performance)
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
Still to add / rename:
|
|
701
|
+
|
|
702
|
+
```text
|
|
703
|
+
legal-pages
|
|
704
|
+
a11y-axe
|
|
705
|
+
performance (optional Tier 3; planned to replace lighthouse — rename not done yet)
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
Crawl groups:
|
|
709
|
+
|
|
710
|
+
```text
|
|
711
|
+
links, images, hreflang, stack, a11y-static
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
Updated wave plan:
|
|
715
|
+
|
|
716
|
+
```typescript
|
|
717
|
+
// Wave 1
|
|
718
|
+
["crawl", "robots-txt", "security-headers", "dns", "legal-pages"]
|
|
719
|
+
|
|
720
|
+
// Wave 2 (after crawl)
|
|
721
|
+
["sitemap", "a11y-axe"]
|
|
722
|
+
|
|
723
|
+
// Wave 3 (optional)
|
|
724
|
+
["performance"]
|
|
725
|
+
|
|
726
|
+
// Wave 4
|
|
727
|
+
["analyze"]
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
Still to do in `src/checks/types.ts` — rename `lighthouse` → `performance` (not yet done),
|
|
731
|
+
add `a11y-axe` and `legal-pages`. (`robots-txt` and `sitemap` are already present.)
|
|
732
|
+
|
|
733
|
+
---
|
|
734
|
+
|
|
735
|
+
## Dependencies (estimated)
|
|
736
|
+
|
|
737
|
+
| Package | Used by | Tier | Notes |
|
|
738
|
+
| --- | --- | --- | --- |
|
|
739
|
+
| None extra | security-headers, robots, dns, links, hreflang | 1 | fetch + cheerio |
|
|
740
|
+
| `@axe-core/playwright` | a11y-axe | 2 | Real a11y on sampled URLs |
|
|
741
|
+
| `playwright` | a11y-axe, performance (optional) | 2–3 | `playwright install chromium` |
|
|
742
|
+
| `lighthouse` + `chrome-launcher` | performance (local adapter) | 3 | Optional, heavy |
|
|
743
|
+
| PageSpeed Insights API | performance (remote adapter) | 3 | `GOOGLE_PAGESPEED_API_KEY` |
|
|
744
|
+
| `whoiser` | dns (optional) | 1 | WHOIS opt-in |
|
|
745
|
+
|
|
746
|
+
**Not recommended as dependencies:**
|
|
747
|
+
|
|
748
|
+
- Full Lighthouse for SEO/a11y — overlap with Tier 0/1/2
|
|
749
|
+
- `@capgo/seo-checker` — useful reference for rule ideas; crawl already covers live sites
|
|
750
|
+
- Wappalyzer — defer; heuristics sufficient initially
|
|
751
|
+
|
|
752
|
+
---
|
|
753
|
+
|
|
754
|
+
## Testing strategy
|
|
755
|
+
|
|
756
|
+
| Layer | Approach |
|
|
757
|
+
| --- | --- |
|
|
758
|
+
| Pure extractors | vitest + HTML fixtures |
|
|
759
|
+
| Standalone checks (Tier 1) | vitest + mocked `fetch` / DNS |
|
|
760
|
+
| Sitemap compare | Fixture XML + crawl URL sets |
|
|
761
|
+
| a11y-axe (Tier 2) | Smoke test on local HTML fixture server, `@slow` |
|
|
762
|
+
| performance (Tier 3) | Mock PageSpeed API response; Lighthouse smoke `@slow` |
|
|
763
|
+
| Integration | Golden-file report snapshot for eight.nl fixture |
|
|
764
|
+
|
|
765
|
+
---
|
|
766
|
+
|
|
767
|
+
## Open decisions
|
|
768
|
+
|
|
769
|
+
1. **External links** — opt-in (recommended).
|
|
770
|
+
2. **Performance provider** — PageSpeed API default in CI; Lighthouse for local dev (recommended).
|
|
771
|
+
3. **WHOIS** — opt-in (recommended).
|
|
772
|
+
4. **Legal page paths** — per-client `.flightcheck.json` (recommended).
|
|
773
|
+
5. **axe on all crawled HTML via jsdom** — experiment later; Tier 2 sample is sufficient for v1.
|
|
774
|
+
6. **Stack detection** — heuristics first; Wappalyzer as optional plugin later.
|
|
775
|
+
|
|
776
|
+
---
|
|
777
|
+
|
|
778
|
+
## Success criteria
|
|
779
|
+
|
|
780
|
+
A site like `https://eight.nl` run with `--profile delivery` should report:
|
|
781
|
+
|
|
782
|
+
- Missing Twitter/OG tags (Tier 0 — existing)
|
|
783
|
+
- robots.txt and sitemap status (Tier 1)
|
|
784
|
+
- Sitemap vs crawl diff (Tier 1)
|
|
785
|
+
- Privacy/cookie pages present and linked (Tier 1)
|
|
786
|
+
- Image alt gaps + static a11y hints on all pages (Tier 1)
|
|
787
|
+
- Broken internal links (Tier 1)
|
|
788
|
+
- Detected stack — Next.js + Sanity + Vercel (Tier 1)
|
|
789
|
+
- Security header gaps (Tier 1)
|
|
790
|
+
- DNS / SPF / DMARC overview (Tier 1)
|
|
791
|
+
- axe WCAG violations on homepage + template samples (Tier 2)
|
|
792
|
+
- Core Web Vitals on homepage + template samples (Tier 3, `--profile delivery-full` only)
|
|
793
|
+
- Single Markdown report ready for an AI agent or client handover doc
|
|
794
|
+
|
|
795
|
+
Without `--profile delivery-full`, the run completes **without Chrome** and still covers
|
|
796
|
+
the majority of delivery QC.
|