seo-crawl-audit 0.1.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +309 -211
- package/bin/seo-audit.js +18 -3
- package/bundle/cli.js +154 -0
- package/package.json +10 -8
- package/examples/quotes-toscrape-report.html +0 -383
- package/src/audit.js +0 -125
- package/src/baseline.js +0 -64
- package/src/checkpoint.js +0 -90
- package/src/cli.js +0 -827
- package/src/compare.js +0 -205
- package/src/crawler.js +0 -298
- package/src/html-report.js +0 -434
- package/src/html.js +0 -105
- package/src/report.js +0 -43
- package/src/robots.js +0 -80
- package/src/sitemap.js +0 -209
- package/src/target.js +0 -35
- package/src/urls.js +0 -45
package/README.md
CHANGED
|
@@ -1,47 +1,74 @@
|
|
|
1
1
|
# SEO Crawl Audit
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
account
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
3
|
+
Free, open-source, local-first SEO crawler and regression audit tool.
|
|
4
|
+
|
|
5
|
+
`seo-audit` crawls server-rendered pages, saves a versioned JSON snapshot,
|
|
6
|
+
finds concrete SEO problems, and detects regressions after a deployment. It
|
|
7
|
+
needs no account or API key. Page HTML is processed in memory; normalized
|
|
8
|
+
results stay in local JSON, checkpoint, CSV, and self-contained HTML files.
|
|
9
|
+
|
|
10
|
+
[Open the interactive example report](https://melnixdev.github.io/seo-crawl-audit/)
|
|
11
|
+
or [view its source file](examples/quotes-toscrape-report.html).
|
|
12
|
+
|
|
13
|
+
## Highlights
|
|
14
|
+
|
|
15
|
+
- automatic `robots.txt`, sitemap, sitemap-index, and gzip sitemap support;
|
|
16
|
+
- bounded concurrency with a per-origin request delay;
|
|
17
|
+
- retries for timeouts, `429`, and temporary `5xx`, including `Retry-After`;
|
|
18
|
+
- redirect-chain and redirect-loop detection;
|
|
19
|
+
- 40+ explicit audit and regression rules with stable fingerprints;
|
|
20
|
+
- SnapshotV2 with automatic baseline v1 migration;
|
|
21
|
+
- `new`, `ongoing`, `resolved`, and `unchanged` issue lifecycle;
|
|
22
|
+
- resumable scans and a useful report even after interruption;
|
|
23
|
+
- local SnapshotV2 history with issue, page-count, sitemap, and crawl-depth
|
|
24
|
+
trends—without a hosted account;
|
|
25
|
+
- English/Ukrainian report localization, filters, CSV export, print layout,
|
|
26
|
+
and local report branding;
|
|
27
|
+
- interactive issue statistics for severity, frequent checks, ownership, and
|
|
28
|
+
regression lifecycle, with chart-to-table filtering;
|
|
29
|
+
- a production-versus-preview release guard with safe environment-based
|
|
30
|
+
authentication for private deployments;
|
|
31
|
+
- configuration, suppressions with expiry, severity overrides, and budgets;
|
|
32
|
+
- a self-contained GitHub Action that runs inside the GitHub runner;
|
|
33
|
+
- typed engine API with injectable `fetch`, checkpoints, events, logger, and
|
|
34
|
+
`AbortSignal`.
|
|
35
|
+
|
|
36
|
+
The project intentionally does not calculate an opaque overall SEO score.
|
|
37
|
+
Every finding names the affected URL, rule, risk, evidence, owner, and suggested
|
|
38
|
+
remediation.
|
|
21
39
|
|
|
22
40
|
## Requirements and installation
|
|
23
41
|
|
|
24
42
|
Node.js 20.19 or newer is required.
|
|
25
43
|
|
|
44
|
+
Run without installing:
|
|
45
|
+
|
|
26
46
|
```bash
|
|
27
|
-
|
|
28
|
-
cd seo-crawl-audit
|
|
29
|
-
npm install
|
|
30
|
-
npm link
|
|
47
|
+
npx seo-crawl-audit https://example.com/
|
|
31
48
|
```
|
|
32
49
|
|
|
33
|
-
|
|
50
|
+
Or install the CLI globally:
|
|
34
51
|
|
|
35
52
|
```bash
|
|
53
|
+
npm install --global seo-crawl-audit
|
|
36
54
|
seo-audit --version
|
|
37
|
-
seo-audit --help
|
|
38
55
|
```
|
|
39
56
|
|
|
40
|
-
|
|
57
|
+
For repository development:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
git clone https://github.com/MelnixDev/seo-crawl-audit.git
|
|
61
|
+
cd seo-crawl-audit
|
|
62
|
+
npm install
|
|
63
|
+
npm run build
|
|
64
|
+
npm link --workspace seo-crawl-audit
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Five-minute example
|
|
41
68
|
|
|
42
69
|
[Quotes to Scrape](https://quotes.toscrape.com/) is a public educational site
|
|
43
|
-
made for crawler practice. It is unrelated to this project and keeps the
|
|
44
|
-
small
|
|
70
|
+
made for crawler practice. It is unrelated to this project and keeps the
|
|
71
|
+
example small.
|
|
45
72
|
|
|
46
73
|
```bash
|
|
47
74
|
seo-audit scan https://quotes.toscrape.com/ \
|
|
@@ -51,15 +78,7 @@ seo-audit scan https://quotes.toscrape.com/ \
|
|
|
51
78
|
--report quotes-report.html
|
|
52
79
|
```
|
|
53
80
|
|
|
54
|
-
|
|
55
|
-
HTML report. In the current demo scan, the tool finds missing descriptions and
|
|
56
|
-
canonical URLs while the pages themselves remain reachable.
|
|
57
|
-
|
|
58
|
-
[View the included example report](examples/quotes-toscrape-report.html). GitHub
|
|
59
|
-
may show the HTML source; download the file and open it in any browser to use
|
|
60
|
-
the filters.
|
|
61
|
-
|
|
62
|
-
Run a fresh comparison later:
|
|
81
|
+
Run a comparison later:
|
|
63
82
|
|
|
64
83
|
```bash
|
|
65
84
|
seo-audit check https://quotes.toscrape.com/ \
|
|
@@ -72,7 +91,7 @@ seo-audit check https://quotes.toscrape.com/ \
|
|
|
72
91
|
|
|
73
92
|
### `seo-audit <url>`
|
|
74
93
|
|
|
75
|
-
|
|
94
|
+
Shortcut for `seo-audit scan <url>`.
|
|
76
95
|
|
|
77
96
|
```bash
|
|
78
97
|
seo-audit https://example.com/
|
|
@@ -80,7 +99,8 @@ seo-audit https://example.com/
|
|
|
80
99
|
|
|
81
100
|
### `seo-audit scan <url>`
|
|
82
101
|
|
|
83
|
-
Crawls a site, audits its current
|
|
102
|
+
Crawls a site, audits its current state, saves SnapshotV2, and optionally writes
|
|
103
|
+
an HTML report.
|
|
84
104
|
|
|
85
105
|
```bash
|
|
86
106
|
seo-audit scan https://example.com/
|
|
@@ -88,25 +108,14 @@ seo-audit scan https://example.com/ --pages 250
|
|
|
88
108
|
seo-audit scan https://example.com/ --all
|
|
89
109
|
```
|
|
90
110
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
3. tries `/sitemap.xml` and `/sitemap_index.xml` when needed;
|
|
96
|
-
4. shows an interactive scan-size menu when a sitemap is found;
|
|
97
|
-
5. saves the baseline to `.seo-audit.json`;
|
|
98
|
-
6. creates `seo-audit-report.html` in an interactive terminal.
|
|
99
|
-
|
|
100
|
-
The menu offers the first 100 pages, the whole sitemap, batches of 100 with
|
|
101
|
-
confirmation, or any positive number entered by the user. `--pages` and `--all`
|
|
102
|
-
skip that menu.
|
|
103
|
-
|
|
104
|
-
If no sitemap is found, the interactive command asks for its full URL. Pressing
|
|
105
|
-
Enter instead starts a same-origin internal-link crawl.
|
|
111
|
+
When a sitemap is found in an interactive terminal, the menu offers the first
|
|
112
|
+
100 pages, every sitemap URL, groups of 100 with confirmation, or a custom
|
|
113
|
+
number. `--pages` and `--all` skip the menu. If no sitemap is found, press Enter
|
|
114
|
+
to continue through same-origin internal links, or enter the full sitemap URL.
|
|
106
115
|
|
|
107
116
|
### `seo-audit check [url]`
|
|
108
117
|
|
|
109
|
-
Crawls
|
|
118
|
+
Crawls again and compares the result with a saved baseline.
|
|
110
119
|
|
|
111
120
|
```bash
|
|
112
121
|
seo-audit check
|
|
@@ -114,236 +123,325 @@ seo-audit check https://preview.example.com/
|
|
|
114
123
|
seo-audit check --baseline production-seo.json --strict
|
|
115
124
|
```
|
|
116
125
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
`--strict` is enabled.
|
|
126
|
+
Without `url`, the saved site URL is used. A different origin supports
|
|
127
|
+
production-to-preview comparison while keeping paths comparable. New
|
|
128
|
+
error-level findings return exit code `1`; `--strict` also blocks on warnings.
|
|
129
|
+
If `scan` receives SIGINT or SIGTERM, it flushes completed pages, writes the
|
|
130
|
+
partial snapshot/report, keeps the checkpoint, and exits with code `130`.
|
|
123
131
|
|
|
124
|
-
### `seo-audit
|
|
132
|
+
### `seo-audit compare`
|
|
125
133
|
|
|
126
|
-
|
|
127
|
-
|
|
134
|
+
Scans production, requests the same paths from a preview deployment, and
|
|
135
|
+
reports only SEO regressions introduced by the preview:
|
|
128
136
|
|
|
129
137
|
```bash
|
|
130
|
-
seo-audit
|
|
131
|
-
|
|
132
|
-
|
|
138
|
+
seo-audit compare \
|
|
139
|
+
--production https://example.com/ \
|
|
140
|
+
--preview https://preview-123.example.dev/ \
|
|
141
|
+
--pages 100 \
|
|
142
|
+
--report preview-seo-report.html
|
|
133
143
|
```
|
|
134
144
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
## HTML report
|
|
139
|
-
|
|
140
|
-
The report is one portable HTML file with no external scripts, fonts, tracking,
|
|
141
|
-
or uploaded data. It contains:
|
|
142
|
-
|
|
143
|
-
- summary cards for checked and affected pages;
|
|
144
|
-
- error, warning, and informational issue counts;
|
|
145
|
-
- free-text search across URL, rule, message, before, and after values;
|
|
146
|
-
- severity and rule filters;
|
|
147
|
-
- paginated results with 100, 250, or 500 rows per page;
|
|
148
|
-
- direct links to affected pages;
|
|
149
|
-
- before/after values for regression checks.
|
|
150
|
-
|
|
151
|
-
During an interactive `scan`, the report is created immediately and refreshed
|
|
152
|
-
after the first completed request batch, then periodically as the scan grows.
|
|
153
|
-
Writes are atomic, so interruption should not leave a half-written HTML file.
|
|
154
|
-
A partial report clearly says that it contains saved results and shows
|
|
155
|
-
`checked / target` page counts.
|
|
156
|
-
|
|
157
|
-
Interactive `scan` and `check` commands create the default report automatically.
|
|
158
|
-
For scripts or CI, request one explicitly:
|
|
145
|
+
For a protected deployment, put request headers in an environment variable as
|
|
146
|
+
a JSON object and pass only its name on the command line:
|
|
159
147
|
|
|
160
148
|
```bash
|
|
161
|
-
|
|
162
|
-
seo-audit
|
|
149
|
+
export SEO_AUDIT_PREVIEW_HEADERS='{"Authorization":"Bearer …"}'
|
|
150
|
+
seo-audit compare \
|
|
151
|
+
--production https://example.com/ \
|
|
152
|
+
--preview https://preview-123.example.dev/ \
|
|
153
|
+
--preview-headers-env SEO_AUDIT_PREVIEW_HEADERS
|
|
163
154
|
```
|
|
164
155
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
156
|
+
Header values are used only by the injected request adapter. They are never
|
|
157
|
+
written to snapshots, reports, checkpoints, or JSON output. Errors block the
|
|
158
|
+
release with exit code `1`; `--strict` also blocks on warnings.
|
|
168
159
|
|
|
169
|
-
`
|
|
170
|
-
baseline. For the default `.seo-audit.json` output, its name is:
|
|
171
|
-
|
|
172
|
-
```text
|
|
173
|
-
.seo-audit.checkpoint.ndjson
|
|
174
|
-
```
|
|
160
|
+
### `seo-audit report [baseline]`
|
|
175
161
|
|
|
176
|
-
|
|
177
|
-
page HTML. Each completed request batch is appended immediately. If the process
|
|
178
|
-
is stopped, run the same compatible scan command again:
|
|
162
|
+
Regenerates HTML from a saved snapshot without network requests.
|
|
179
163
|
|
|
180
164
|
```bash
|
|
181
|
-
seo-audit
|
|
165
|
+
seo-audit report
|
|
166
|
+
seo-audit report quotes-baseline.json --report quotes-report.html
|
|
182
167
|
```
|
|
183
168
|
|
|
184
|
-
|
|
185
|
-
is reused only when the start URL, sitemap, query-string policy, and robots
|
|
186
|
-
policy match. An incompatible checkpoint is replaced automatically.
|
|
169
|
+
### `seo-audit history [url]`
|
|
187
170
|
|
|
188
|
-
|
|
189
|
-
when a stepped scan is stopped before its target or when the process is
|
|
190
|
-
interrupted. Checkpoint files are excluded by the included `.gitignore`.
|
|
191
|
-
|
|
192
|
-
To deliberately make every request without reading or writing a checkpoint:
|
|
171
|
+
Lists locally saved runs and creates an HTML report with issue trends:
|
|
193
172
|
|
|
194
173
|
```bash
|
|
195
|
-
seo-audit https://example.com/
|
|
174
|
+
seo-audit history https://example.com/
|
|
175
|
+
seo-audit history --history-dir ./audit-history --report history.html
|
|
196
176
|
```
|
|
197
177
|
|
|
198
|
-
|
|
199
|
-
observe the current response from every baseline URL.
|
|
200
|
-
|
|
201
|
-
## What is checked
|
|
178
|
+
Compare any two saved runs without crawling:
|
|
202
179
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
-
|
|
206
|
-
-
|
|
207
|
-
-
|
|
208
|
-
|
|
209
|
-
- missing meta descriptions;
|
|
210
|
-
- missing canonical URLs;
|
|
211
|
-
- missing or multiple H1 headings;
|
|
212
|
-
- `noindex` directives.
|
|
213
|
-
|
|
214
|
-
The regression check detects:
|
|
215
|
-
|
|
216
|
-
- a previously working page becoming unavailable;
|
|
217
|
-
- a new `noindex` directive;
|
|
218
|
-
- a page becoming blocked by `robots.txt`;
|
|
219
|
-
- a removed or changed title;
|
|
220
|
-
- a removed meta description;
|
|
221
|
-
- a removed or changed canonical URL;
|
|
222
|
-
- a removed H1 heading;
|
|
223
|
-
- a changed redirect destination;
|
|
224
|
-
- a changed `robots.txt`.
|
|
225
|
-
|
|
226
|
-
Errors represent changes or conditions likely to block indexing or make a page
|
|
227
|
-
unusable. Warnings identify important metadata problems. Informational findings
|
|
228
|
-
are worth reviewing but do not fail a normal check.
|
|
180
|
+
```bash
|
|
181
|
+
seo-audit history \
|
|
182
|
+
--from .seo-audit/history/older.snapshot.json \
|
|
183
|
+
--to .seo-audit/history/newer.snapshot.json \
|
|
184
|
+
--report selected-runs.html
|
|
185
|
+
```
|
|
229
186
|
|
|
230
187
|
## Options
|
|
231
188
|
|
|
232
189
|
```text
|
|
233
190
|
--baseline <file> Baseline input for check (default: .seo-audit.json)
|
|
234
|
-
--
|
|
191
|
+
--config <file> Config file (default: seo-audit.config.json)
|
|
192
|
+
--output <file> Snapshot output for scan (default: .seo-audit.json)
|
|
235
193
|
--report <file> HTML report output (default: seo-audit-report.html)
|
|
236
194
|
--no-report Disable automatic HTML report generation
|
|
237
|
-
--no-cache Disable
|
|
195
|
+
--no-cache Disable checkpoint caching and resume
|
|
238
196
|
--pages <number> Scan an exact number of pages
|
|
239
197
|
--all Scan every URL found in the sitemap
|
|
240
198
|
--max-pages <number> Deprecated alias for --pages
|
|
241
199
|
--concurrency <number> Concurrent requests (default: 5)
|
|
200
|
+
--delay <ms> Delay between starts per origin (default: 100)
|
|
242
201
|
--timeout <ms> Per-request timeout (default: 10000)
|
|
243
202
|
--sitemap <url> Use a specific sitemap or sitemap index
|
|
244
203
|
--no-sitemap Skip sitemap discovery and crawl internal links
|
|
245
204
|
--include-query Treat query-string URLs as separate pages
|
|
246
205
|
--ignore-robots Ignore robots.txt disallow rules
|
|
247
206
|
--strict Fail check on warnings as well as errors
|
|
248
|
-
--
|
|
249
|
-
--
|
|
207
|
+
--production <url> Production URL for compare
|
|
208
|
+
--preview <url> Preview deployment URL for compare
|
|
209
|
+
--production-headers-env <name>
|
|
210
|
+
Read production headers from a JSON environment variable
|
|
211
|
+
--preview-headers-env <name>
|
|
212
|
+
Read preview headers from a JSON environment variable
|
|
213
|
+
--history-dir <path> Local snapshot history directory
|
|
214
|
+
--no-history Do not save this scan to local history
|
|
215
|
+
--from <snapshot> Older snapshot for an explicit history comparison
|
|
216
|
+
--to <snapshot> Newer snapshot for an explicit history comparison
|
|
217
|
+
--json Print machine-readable output
|
|
218
|
+
--help Show help
|
|
250
219
|
--version Show the installed version
|
|
251
220
|
```
|
|
252
221
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
222
|
+
## Configuration
|
|
223
|
+
|
|
224
|
+
The repository includes [`seo-audit.config.json`](seo-audit.config.json) and a
|
|
225
|
+
[JSON Schema](packages/core/config.schema.json). CLI flags take precedence over
|
|
226
|
+
the config file, which takes precedence over the saved baseline, followed by
|
|
227
|
+
safe defaults.
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"$schema": "./packages/core/config.schema.json",
|
|
232
|
+
"url": "https://example.com/",
|
|
233
|
+
"sitemap": "auto",
|
|
234
|
+
"maxPages": 100,
|
|
235
|
+
"concurrency": 5,
|
|
236
|
+
"delay": 100,
|
|
237
|
+
"timeout": 10000,
|
|
238
|
+
"respectRobots": true,
|
|
239
|
+
"includeQuery": false,
|
|
240
|
+
"enabledRules": null,
|
|
241
|
+
"severityOverrides": {
|
|
242
|
+
"low-word-count": "info"
|
|
243
|
+
},
|
|
244
|
+
"suppressions": [
|
|
245
|
+
{
|
|
246
|
+
"rule": "missing-description",
|
|
247
|
+
"urlPattern": "/legal/**",
|
|
248
|
+
"reason": "Description intentionally omitted",
|
|
249
|
+
"expiresAt": "2027-01-01"
|
|
250
|
+
}
|
|
251
|
+
],
|
|
252
|
+
"regressionBudgets": {
|
|
253
|
+
"error": 0,
|
|
254
|
+
"warning": 3
|
|
255
|
+
},
|
|
256
|
+
"report": {
|
|
257
|
+
"agencyName": "Example Agency",
|
|
258
|
+
"logo": "https://example.com/logo.png",
|
|
259
|
+
"primaryColor": "#3157d5"
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
264
263
|
|
|
265
|
-
|
|
264
|
+
Expired suppressions stop matching automatically, so the issue appears again.
|
|
265
|
+
`urlPattern` accepts `*` within one path segment and `**` across segments.
|
|
266
266
|
|
|
267
|
-
|
|
268
|
-
large scans and should not be committed.
|
|
267
|
+
## What is checked
|
|
269
268
|
|
|
270
|
-
|
|
269
|
+
The rule registry covers access and HTTP failures, meta and header `noindex`,
|
|
270
|
+
robots blocking, canonical validity and targets, redirects, sitemap consistency,
|
|
271
|
+
titles, descriptions, headings, internal links, orphan pages, hreflang,
|
|
272
|
+
structured data syntax, language declarations, exact duplicate content, social
|
|
273
|
+
metadata, image alt attributes, and low word count.
|
|
274
|
+
|
|
275
|
+
Regression-only checks cover newly introduced indexing blocks, status changes,
|
|
276
|
+
metadata removal or edits, redirect changes, robots changes, and sharp sitemap
|
|
277
|
+
URL loss.
|
|
278
|
+
|
|
279
|
+
[Read the rule-by-rule documentation](https://github.com/MelnixDev/seo-crawl-audit/blob/main/docs/rules.md) ·
|
|
280
|
+
[Українська версія довідника правил](https://github.com/MelnixDev/seo-crawl-audit/blob/main/docs/rules.uk.md).
|
|
281
|
+
|
|
282
|
+
## Snapshots, lifecycle, and checkpoints
|
|
283
|
+
|
|
284
|
+
SnapshotV2 records schema, engine and rule-set versions, generated time, config
|
|
285
|
+
hash, robots and sitemap state, normalized page data, link-graph summary, and
|
|
286
|
+
crawl statistics. Baseline schema v1 is upgraded automatically when read.
|
|
287
|
+
|
|
288
|
+
Issues use a stable 24-character fingerprint. A comparison separates:
|
|
289
|
+
|
|
290
|
+
- `newIssues` — absent from the previous snapshot;
|
|
291
|
+
- `ongoingIssues` — same issue identity with changed evidence;
|
|
292
|
+
- `resolvedIssues` — present before but absent now;
|
|
293
|
+
- `unchangedIssues` — same identity and evidence.
|
|
294
|
+
|
|
295
|
+
`DiffResult.complete` is `false` for partial or truncated comparisons. Findings
|
|
296
|
+
on checked pages are still reported, while unchecked pages are not falsely
|
|
297
|
+
classified as missing or resolved.
|
|
298
|
+
|
|
299
|
+
During `scan`, completed normalized page results are appended to
|
|
300
|
+
`.seo-audit.checkpoint.ndjson`. Full HTML is not cached. Running the same
|
|
301
|
+
compatible command resumes without requesting saved pages again. The checkpoint
|
|
302
|
+
is removed after completion and retained after interruption. Successful pages
|
|
303
|
+
are reused; transient failures and HTTP 5xx results are refreshed. Only an
|
|
304
|
+
unfinished final NDJSON record is recoverable—earlier corruption is reported.
|
|
305
|
+
|
|
306
|
+
Every completed scan and check also saves a full SnapshotV2 beside the selected
|
|
307
|
+
baseline under `.seo-audit/history/`. Use `--history-dir` to move it or
|
|
308
|
+
`--no-history` for an ephemeral run. History is read directly from disk; no URL,
|
|
309
|
+
snapshot, or metric is uploaded. The HTML trend view shows errors, warnings,
|
|
310
|
+
informational findings, new/resolved issues, page count, sitemap count, and
|
|
311
|
+
maximum crawl depth.
|
|
312
|
+
|
|
313
|
+
## HTML and CSV report
|
|
314
|
+
|
|
315
|
+
The portable HTML file contains no external script, font, or tracking request.
|
|
316
|
+
It includes:
|
|
317
|
+
|
|
318
|
+
- an `English / Українська` language selector that translates the complete
|
|
319
|
+
interface, rule names, findings, remediation, lifecycle labels, and CSV;
|
|
320
|
+
- dependency-free interactive charts for severity, most frequent checks,
|
|
321
|
+
inferred page templates, and owner or regression-lifecycle distribution;
|
|
322
|
+
selecting a chart item filters
|
|
323
|
+
the issue table and selecting it again clears that filter;
|
|
324
|
+
- summary cards and partial-scan state;
|
|
325
|
+
- current and lifecycle tabs;
|
|
326
|
+
- severity, rule, inferred template, owner, and URL/text filters;
|
|
327
|
+
- evidence, before/after values, remediation, and fingerprint;
|
|
328
|
+
- engine and rule-set versions;
|
|
329
|
+
- client-side CSV export;
|
|
330
|
+
- print-friendly PDF layout;
|
|
331
|
+
- optional local agency name, logo, and primary color;
|
|
332
|
+
- clear clean-report and no-filter-match states.
|
|
333
|
+
|
|
334
|
+
Template grouping turns repeated findings such as `/products/red-shoe` and
|
|
335
|
+
`/products/blue-shirt` into `/products/:slug`. Numeric IDs, UUIDs, dates, and
|
|
336
|
+
hashes have stable placeholders. Grouping changes only presentation: rule IDs,
|
|
337
|
+
evidence, snapshots, and issue fingerprints remain unchanged.
|
|
338
|
+
|
|
339
|
+
Writes are atomic. Interactive scans refresh a partial report while results are
|
|
340
|
+
being checkpointed, so interruption does not discard already completed work.
|
|
341
|
+
|
|
342
|
+
## GitHub Action
|
|
343
|
+
|
|
344
|
+
The Action crawls entirely inside the GitHub runner. It produces exit status,
|
|
345
|
+
JSON summary, HTML report, job summary, and annotations for error-level
|
|
346
|
+
regressions.
|
|
271
347
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
348
|
+
```yaml
|
|
349
|
+
name: SEO regression check
|
|
350
|
+
on: [pull_request]
|
|
275
351
|
|
|
276
|
-
|
|
352
|
+
jobs:
|
|
353
|
+
seo:
|
|
354
|
+
runs-on: ubuntu-latest
|
|
355
|
+
steps:
|
|
356
|
+
- uses: actions/checkout@v4
|
|
357
|
+
- uses: MelnixDev/seo-crawl-audit@v0
|
|
358
|
+
id: seo
|
|
359
|
+
with:
|
|
360
|
+
url: https://preview.example.com/
|
|
361
|
+
baseline: .seo-audit.json
|
|
362
|
+
config: seo-audit.config.json
|
|
363
|
+
fail-on: error
|
|
364
|
+
report: seo-audit-report.html
|
|
365
|
+
- uses: actions/upload-artifact@v4
|
|
366
|
+
with:
|
|
367
|
+
name: seo-crawl-audit-report
|
|
368
|
+
path: |
|
|
369
|
+
${{ steps.seo.outputs.report }}
|
|
370
|
+
${{ steps.seo.outputs.summary }}
|
|
371
|
+
```
|
|
277
372
|
|
|
278
|
-
|
|
373
|
+
[Read all Action inputs and outputs](docs/github-action.md).
|
|
279
374
|
|
|
280
|
-
|
|
281
|
-
seo-audit check --json
|
|
282
|
-
```
|
|
375
|
+
## Core API and repository structure
|
|
283
376
|
|
|
284
|
-
|
|
377
|
+
The npm workspace separates reusable concerns:
|
|
285
378
|
|
|
286
379
|
```text
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
380
|
+
packages/core @seo-crawl-audit/core — scan, audit, diff, snapshots, reports
|
|
381
|
+
packages/cli seo-crawl-audit — terminal UX and npm executable
|
|
382
|
+
packages/action @seo-crawl-audit/action — GitHub runner entry point
|
|
290
383
|
```
|
|
291
384
|
|
|
292
|
-
|
|
385
|
+
```ts
|
|
386
|
+
import { audit, planScan, scan } from "@seo-crawl-audit/core";
|
|
387
|
+
import { createFileCheckpointStore } from "@seo-crawl-audit/core/node";
|
|
293
388
|
|
|
294
|
-
|
|
389
|
+
const plan = await planScan({ url: "https://example.com/", maxPages: 100 });
|
|
390
|
+
const result = await scan(plan, {
|
|
391
|
+
signal: controller.signal,
|
|
392
|
+
checkpointStore: createFileCheckpointStore(".seo-audit.checkpoint.ndjson"),
|
|
393
|
+
onEvent(event) {
|
|
394
|
+
if (event.type === "progress") console.log(event.completed, event.total);
|
|
395
|
+
},
|
|
396
|
+
});
|
|
295
397
|
|
|
296
|
-
|
|
297
|
-
|
|
398
|
+
const issues = audit(result.snapshot);
|
|
399
|
+
```
|
|
298
400
|
|
|
299
|
-
|
|
300
|
-
|
|
401
|
+
[Read the typed public API guide](docs/public-api.md) and the
|
|
402
|
+
[architecture notes](docs/architecture.md).
|
|
301
403
|
|
|
302
|
-
|
|
303
|
-
pull_request:
|
|
404
|
+
## Responsible crawling and safe defaults
|
|
304
405
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
```
|
|
406
|
+
- delay: `100 ms` between request starts per origin;
|
|
407
|
+
- concurrency: `5`;
|
|
408
|
+
- timeout: `10 s`;
|
|
409
|
+
- `robots.txt` respected;
|
|
410
|
+
- same-origin page discovery;
|
|
411
|
+
- query strings excluded;
|
|
412
|
+
- HTML limited to `5 MiB` and robots to `512 KiB`;
|
|
413
|
+
- retries are bounded and use exponential backoff with jitter;
|
|
414
|
+
- `Retry-After` is honored up to 30 seconds;
|
|
415
|
+
- redirects are limited to 10.
|
|
316
416
|
|
|
317
|
-
|
|
318
|
-
when a human-readable CI report is useful.
|
|
417
|
+
Use a longer delay for a small site or a server you do not control:
|
|
319
418
|
|
|
320
|
-
|
|
419
|
+
```bash
|
|
420
|
+
seo-audit https://example.com/ --delay 500
|
|
421
|
+
```
|
|
321
422
|
|
|
322
|
-
|
|
323
|
-
limits concurrency to five requests, and applies a ten-second timeout. Increase
|
|
324
|
-
concurrency carefully and only on sites you are allowed to test.
|
|
423
|
+
Use `--delay 0` only for local or explicitly controlled fixtures.
|
|
325
424
|
|
|
326
425
|
## Current scope
|
|
327
426
|
|
|
328
|
-
- server-rendered HTML
|
|
329
|
-
- same-origin
|
|
330
|
-
- sitemap and
|
|
331
|
-
-
|
|
332
|
-
-
|
|
333
|
-
-
|
|
334
|
-
- `robots.txt` responses are limited to 512 KiB;
|
|
335
|
-
- authenticated pages are not supported.
|
|
427
|
+
- server-rendered HTML; JavaScript rendering is not included;
|
|
428
|
+
- same-origin crawl discovery;
|
|
429
|
+
- sitemap and internal-link seeding;
|
|
430
|
+
- non-HTML responses are recorded but not parsed for page metadata;
|
|
431
|
+
- authenticated pages are not supported;
|
|
432
|
+
- results are normalized and ordered for reproducible output.
|
|
336
433
|
|
|
337
434
|
## Development
|
|
338
435
|
|
|
339
436
|
```bash
|
|
437
|
+
npm run build
|
|
340
438
|
npm test
|
|
341
439
|
npm run check
|
|
342
440
|
```
|
|
343
441
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
and
|
|
442
|
+
CI covers Node.js 20, 22, and 24. Tests use local HTTP fixtures for robots,
|
|
443
|
+
sitemaps, gzip, retries, redirects, cancellation, checkpoint resume, migration,
|
|
444
|
+
rules, lifecycle, HTML escaping, npm packaging, and the GitHub Action.
|
|
347
445
|
|
|
348
446
|
## License
|
|
349
447
|
|