seo-crawl-audit 0.1.2 → 0.7.2

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