laxy-verify 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  `laxy-verify` is a deployment blocker gate for frontend apps.
4
4
 
5
- Every verification run includes the same build, Lighthouse, E2E, multi-viewport, security, visual diff, and stability coverage regardless of plan.
5
+ Every verification run includes build, Lighthouse, E2E, multi-viewport, security audit, visual diff, broken links, console error monitoring, and stability coverage regardless of plan. Optional opt-in checks add TypeScript type checking, secret scanning, bundle size analysis, outdated dependency detection, deep accessibility audit, deep SEO audit, and Core Web Vitals budget enforcement.
6
6
 
7
7
  ## Quick start
8
8
 
@@ -71,7 +71,17 @@ This is most useful if you ship frontend apps and want a practical gate before:
71
71
  | User-flow E2E verification | Yes | No | Manual setup | No |
72
72
  | Lighthouse scoring | Yes | Yes | No | No |
73
73
  | Visual regression check | Yes | No | No | Yes |
74
+ | Broken link detection | Yes | No | No | No |
75
+ | Console error monitoring | Yes | No | No | No |
76
+ | Cross-browser (Firefox, WebKit) | Yes | No | Yes | No |
74
77
  | Release decision (`hold` / `client-ready`) | Yes | No, score only | No | No |
78
+ | TypeScript type check | Yes | No | No | No |
79
+ | Hardcoded secret scanning | Yes | No | No | No |
80
+ | Bundle size analysis | Yes | No | No | No |
81
+ | Outdated dependency check | Yes | No | No | No |
82
+ | Deep WCAG audit (axe-core) | Yes | No | No | No |
83
+ | Deep SEO audit | Yes | No | No | No |
84
+ | Core Web Vitals budget | Yes | No | No | No |
75
85
  | Zero-config local start | Yes | No | No | No |
76
86
 
77
87
  LHCI measures Lighthouse. `laxy-verify` is for deciding whether this frontend is actually safe to ship.
@@ -85,22 +95,42 @@ Use `laxy-verify` when you want to catch things like:
85
95
  - desktop looks fine, but a mobile CTA is pushed out of view
86
96
  - Lighthouse looks acceptable, but the user-visible path is still not safe to ship
87
97
  - a PR needs a clear hold reason instead of a vague "something failed"
98
+ - hardcoded API keys or secrets are about to be pushed to a public repo
99
+ - TypeScript type errors could cause runtime crashes
100
+ - bundle size has silently grown past acceptable limits
101
+ - critical WCAG violations block real users
102
+ - missing SEO meta tags hurt discoverability
88
103
 
89
104
  ## What it actually checks
90
105
 
91
- A standard run includes:
92
-
93
- - production build success
94
- - Lighthouse thresholds (3 runs for stable evidence)
95
- - E2E scenarios for user-visible flows
96
- - multi-viewport checks (desktop, tablet, mobile)
97
- - blocker-aware reporting and release decisions
98
-
99
106
  Every run includes:
100
107
 
101
- - security audit
102
- - visual diff evidence
103
- - stability pass (second E2E run)
108
+ - **production build** — runs your actual build command, exit code determines pass/fail
109
+ - **Lighthouse** 3 runs averaged for stable performance, accessibility, SEO, and best practices scores
110
+ - **E2E scenarios** — Puppeteer-based user flow testing (auto-detected or configured via `.laxy.yml`)
111
+ - **stability pass** — E2E runs a second time to catch flaky behavior
112
+ - **multi-viewport** — Lighthouse at desktop (1350px), tablet (1024px), and mobile (390px)
113
+ - **security audit** — `npm audit` dependency vulnerability scan
114
+ - **visual diff** — pixel-level screenshot comparison against baseline
115
+ - **broken links** — crawls all internal links and validates HTTP responses
116
+ - **console error monitoring** — captures browser JS errors during E2E execution
117
+ - **cross-browser** — Playwright on Firefox and WebKit if `browsers` is configured in `.laxy.yml`
118
+
119
+ ### Opt-in checks
120
+
121
+ These checks are off by default. Enable them via CLI flags or `.laxy.yml`.
122
+
123
+ | Flag | What it checks | Blocker or Advisory |
124
+ |------|----------------|---------------------|
125
+ | `--typecheck` | TypeScript type errors via `tsc --noEmit` | **Blocker** (5+ errors → high severity) |
126
+ | `--secret-scan` | Hardcoded secrets: AWS keys, GitHub tokens, Stripe keys, private keys, Bearer tokens, JWTs, generic password/token assignments | **Blocker** (always high severity) |
127
+ | `--bundle-size` | Next.js or Vite bundle size analysis (first-load JS, largest chunk) | Advisory |
128
+ | `--outdated-check` | Outdated dependencies via `npm outdated --json` | Advisory (major behind → warning) |
129
+ | `--a11y-deep` | Deep WCAG audit via axe-core (critical/serious violations) | **Blocker** (critical → high severity) |
130
+ | `--seo-deep` | SEO meta tags, canonical, robots, Open Graph, Twitter Card, JSON-LD | Advisory |
131
+ | `--vitals-budget` | Core Web Vitals budget: LCP ≤ 2500ms, CLS ≤ 0.1, INP ≤ 200ms | Advisory |
132
+
133
+ Secret scan never exposes actual credential values — findings are masked with `***` in all output formats.
104
134
 
105
135
  ## What you get from one run
106
136
 
@@ -111,48 +141,26 @@ Every run includes:
111
141
 
112
142
  Grades still exist, but they are not the main point. The main point is whether the run found blockers you should stop on.
113
143
 
114
- ## Quick start
115
-
116
- Run it on a frontend app:
144
+ ## Example workflow
117
145
 
118
- ```bash
119
- cd your-project
120
- npx laxy-verify .
121
- ```
146
+ 1. Run `npx laxy-verify .` locally before opening or merging a PR.
147
+ 2. Fix broken builds, broken flows, and visible regressions.
148
+ 3. Commit `.laxy.yml`.
149
+ 4. Run `npx laxy-verify --init`.
150
+ 5. Let the GitHub Action apply the same gate on every PR.
122
151
 
123
- Generate config plus CI workflow:
152
+ Full verification with all opt-in checks:
124
153
 
125
154
  ```bash
126
- npx laxy-verify --init
155
+ npx laxy-verify . --typecheck --secret-scan --bundle-size --outdated-check --a11y-deep --seo-deep --vitals-budget
127
156
  ```
128
157
 
129
- That creates:
130
-
131
- - `.laxy.yml`
132
- - `.github/workflows/laxy-verify.yml`
133
-
134
- Optional: log in to connect the CLI to your Laxy account:
158
+ Or enable them in `.laxy.yml` and just run:
135
159
 
136
160
  ```bash
137
- npx laxy-verify login
138
- npx laxy-verify whoami
139
- ```
140
-
141
- For CI, set `LAXY_TOKEN` instead of interactive login:
142
-
143
- ```yaml
144
- env:
145
- LAXY_TOKEN: ${{ secrets.LAXY_TOKEN }}
161
+ npx laxy-verify .
146
162
  ```
147
163
 
148
- ## Example workflow
149
-
150
- 1. Run `npx laxy-verify .` locally before opening or merging a PR.
151
- 2. Fix broken builds, broken flows, and visible regressions.
152
- 3. Commit `.laxy.yml`.
153
- 4. Run `npx laxy-verify --init`.
154
- 5. Let the GitHub Action apply the same gate on every PR.
155
-
156
164
  ## Example output
157
165
 
158
166
  ```text
@@ -161,13 +169,35 @@ Grade: Gold
161
169
 
162
170
  Passed:
163
171
  - production build
164
- - Lighthouse thresholds
165
- - core user flows
172
+ - Lighthouse thresholds (Performance 92, Accessibility 97, SEO 90, Best Practices 95)
173
+ - core user flows (5/5 scenarios passed)
174
+ - stability pass (second E2E run passed)
166
175
  - desktop, tablet, and mobile viewport checks
176
+ - security audit (no known vulnerabilities)
177
+ - visual diff (no regressions)
178
+ - broken links (0 broken / 12 checked)
179
+ - console errors (0 detected)
180
+ - TypeScript (0 errors)
181
+ - Secret scan (0 findings, 42 files)
182
+ - Bundle size (vite, within thresholds)
183
+ - Outdated deps (0 outdated)
184
+ - WCAG deep (0 critical)
185
+ - SEO deep (0 errors)
186
+ - Core Web Vitals budget (LCP 1200ms, CLS 0.05, INP 80ms)
167
187
 
168
188
  Artifacts:
169
189
  - .laxy-result.json
170
190
  - laxy-verify-report.md
191
+
192
+ Badge (auto-updates with each run):
193
+ [![Laxy Verify](https://laxy.app/api/badge/your-repo-id)](https://laxy.app)
194
+ ```
195
+
196
+ For Free accounts, the CLI prints a tip instead:
197
+
198
+ ```text
199
+ Tip: Pro tracks your last 30 runs so you can see if Performance or Grade is improving.
200
+ https://laxy.app/pricing
171
201
  ```
172
202
 
173
203
  ## The decision it helps you make
@@ -208,7 +238,7 @@ package_manager: auto
208
238
  port: 3000
209
239
  build_timeout: 300
210
240
  dev_timeout: 60
211
- lighthouse_runs: 1
241
+ lighthouse_runs: 3
212
242
  fail_on: bronze
213
243
 
214
244
  thresholds:
@@ -222,6 +252,31 @@ max_crawl_depth: 3
222
252
  max_crawl_pages: 10
223
253
  browsers:
224
254
  - chromium
255
+
256
+ # Explicit route list for Lighthouse (optional)
257
+ lighthouse_routes: []
258
+ # Extra routes not discoverable by the crawler (optional)
259
+ extra_routes: []
260
+ # Max crawl-discovered routes to run Lighthouse on (default: 5)
261
+ max_lighthouse_routes: 5
262
+
263
+ # Visual diff fine-tuning (optional)
264
+ visual_diff:
265
+ pixelmatch_threshold: 0.1
266
+ warn_threshold: 30
267
+ rollback_threshold: 60
268
+ ignore_selectors: []
269
+ disable_animations: true
270
+
271
+ # Opt-in checks (off by default)
272
+ typecheck: false
273
+ secret_scan: false
274
+ secret_scan_ignore_paths: []
275
+ bundle_size: false
276
+ outdated_check: false
277
+ a11y_deep: false
278
+ seo_deep: false
279
+ vitals_budget: false
225
280
  ```
226
281
 
227
282
  Useful adjustments:
@@ -246,6 +301,16 @@ Options:
246
301
  --badge
247
302
  --init
248
303
  --multi-viewport
304
+ --crawl Crawl the app to discover routes before E2E
305
+ --typecheck Run TypeScript type check (tsc --noEmit)
306
+ --secret-scan Scan for hardcoded secrets and credentials
307
+ --bundle-size Analyze bundle size (Next.js/Vite)
308
+ --outdated-check Check for outdated dependencies
309
+ --a11y-deep Deep accessibility audit (axe-core)
310
+ --seo-deep Deep SEO audit (meta, OG, JSON-LD)
311
+ --vitals-budget Core Web Vitals budget check (LCP, CLS, INP)
312
+ --share Save result and get a shareable URL (Pro)
313
+ --compare <url> Compare Lighthouse scores against a reference URL (Pro)
249
314
  --help
250
315
 
251
316
  Subcommands:
@@ -256,6 +321,60 @@ Subcommands:
256
321
 
257
322
  `--plan-override` is only for plan-label and automation testing. Verification coverage stays the same on every plan.
258
323
 
324
+ ## Pro features
325
+
326
+ Pro and Team accounts unlock additional capabilities on top of the same verification run:
327
+
328
+ - **Result saving and sharing** — `--share` saves the run to your dashboard and returns a shareable URL
329
+ - **Environment comparison** — `--compare <url>` runs Lighthouse against a reference URL (e.g. staging or production) and shows score deltas between your local build and the reference
330
+ - **AI failure analysis** — when a run ends in `hold`, Claude analyzes the failure context and returns a root cause summary with top fix suggestions
331
+ - **History trend tracking** — the last 30 runs are stored so you can see whether your grade and performance scores are improving or regressing over time
332
+ - **Dynamic README badge** — after each run, the CLI prints a Markdown badge snippet that links to your live verification status. The badge auto-updates with every run so your README always reflects current grade
333
+ - **Team Slack / Discord alerts** — grade drops and `hold` verdicts fire webhook notifications with score deltas and blocker details
334
+ - **Weekly team report** — automated weekly summary of verification activity across your team's repos
335
+
336
+ Free accounts see a hint after each run pointing to the history trend feature.
337
+
338
+ ## Secret scan patterns
339
+
340
+ When `--secret-scan` is enabled, the following patterns are detected:
341
+
342
+ | Pattern | Example |
343
+ |---------|--------|
344
+ | AWS Access Key | `AKIA...` (20 chars) |
345
+ | AWS Secret Key | `aws_secret_access_key = '...'` |
346
+ | GitHub Token | `ghp_`, `gho_`, `ghs_`, `ghu_` |
347
+ | Slack Token / Webhook | `xoxb-...`, `hooks.slack.com/...` |
348
+ | Private Key Block | `-----BEGIN RSA PRIVATE KEY-----` |
349
+ | Google API Key | `AIza...` |
350
+ | Stripe Key | `sk_live_...`, `pk_live_...` |
351
+ | Twilio / SendGrid / Mailgun | `SK...`, `SG...`, `key-...` |
352
+ | Hardcoded Bearer Token | `Bearer abc123...` |
353
+ | JWT-like Secret | `eyJ...` |
354
+ | Generic Secret Assignment | `password = '...'`, `api_key = '...'` |
355
+
356
+ False positive filtering:
357
+
358
+ - `process.env.*`, `import.meta.env.*`, `NEXT_PUBLIC_*`, `VITE_*` references are ignored
359
+ - GitHub Actions template variables (`${{ secrets.* }}`) are ignored
360
+ - `test/`, `tests/`, `__tests__/`, `spec/` directories are excluded
361
+ - Comment lines (`//`, `*`, `<!--`) are excluded — commented-out secrets are not flagged
362
+ - Placeholder/example values are ignored
363
+ - All secret values in findings are **masked with `***`** — never exposed in output
364
+
365
+ ## Test fixture
366
+
367
+ A sample Vite + React + TypeScript app is included at `fixtures/sample-app/` for testing all verification checks:
368
+
369
+ ```bash
370
+ cd fixtures/sample-app
371
+ npm install
372
+ npm run build
373
+ npx laxy-verify . --typecheck --secret-scan --bundle-size --outdated-check --skip-lighthouse
374
+ ```
375
+
376
+ See `fixtures/sample-app/README.md` for details on what each check finds in the fixture.
377
+
259
378
  ## Result files
260
379
 
261
380
  Every run writes `.laxy-result.json`.
@@ -311,6 +430,44 @@ It is a pre-merge and pre-release verification layer, not your entire quality sy
311
430
  - a frontend app with a runnable build flow
312
431
  - optional: `playwright` if your project already uses it
313
432
 
433
+ ## Changelog
434
+
435
+ ### v1.3.0 — Pro history trend, dynamic badge, CLI nudge
436
+
437
+ - **History trend tracking** — Pro accounts now store the last 30 verification runs. Grade and performance scores accumulate so you can see whether your app is improving or regressing over time
438
+ - **Dynamic README badge** — after each run, Pro accounts see a Markdown badge snippet in the CLI output. The badge auto-updates with every run via the `/api/badge/:id` endpoint
439
+ - **CLI nudge** — Free accounts see a one-line tip after each run pointing to the history trend feature
440
+ - Added `generateDynamicBadgeMarkdown(repoId, apiUrl)` export to the `badge` module
441
+ - Added `share_result` and `history_trend` flags to `EntitlementFeatures` interface
442
+ - 17 test files, 88 unit tests
443
+
444
+ ### v1.2.3 — Bug fix
445
+
446
+ - Fix E2E and visual diff connection failure on Windows with Node.js 17+: `verifyUrl` now uses `localhost` instead of `127.0.0.1`. On Windows, Vite binds to `::1` (IPv6) which does not accept IPv4 connections.
447
+
448
+ ### v1.2.2 — Opt-in verification checks
449
+
450
+ Added 7 opt-in checks (off by default, enable via CLI flags or `.laxy.yml`):
451
+
452
+ - `--typecheck` — TypeScript type error detection via `tsc --noEmit`
453
+ - `--secret-scan` — Hardcoded secret scanning with 11 regex patterns and `***` masking
454
+ - `--bundle-size` — Next.js/Vite bundle size analysis
455
+ - `--outdated-check` — Outdated dependency detection via `npm outdated`
456
+ - `--a11y-deep` — Deep WCAG audit via axe-core + Puppeteer
457
+ - `--seo-deep` — SEO meta/OG/JSON-LD audit
458
+ - `--vitals-budget` — Core Web Vitals budget enforcement (LCP, CLS, INP)
459
+
460
+ Other changes:
461
+
462
+ - Secret scan findings are **masked** — real credential values never appear in JSON, markdown, or console output
463
+ - Comment lines excluded from secret scanning to reduce false positives
464
+ - `test/`, `tests/`, `__tests__/`, `spec/` directories excluded from scanning
465
+ - `.laxy.yml` supports `secret_scan_ignore_paths` for custom exclusions
466
+ - Verification report includes all new checks in passes checklist and improvement recommendations
467
+ - Markdown report includes all new checks in the delivery evidence table
468
+ - Added `fixtures/sample-app/` test fixture (Vite + React + TypeScript)
469
+ - 17 test files, 88 unit tests
470
+
314
471
  ## Links
315
472
 
316
473
  - GitHub: https://github.com/SUNgm24/Laxy/tree/main/laxy-verify
@@ -0,0 +1,20 @@
1
+ export interface A11yViolation {
2
+ id: string;
3
+ impact: "critical" | "serious" | "moderate" | "minor";
4
+ description: string;
5
+ helpUrl: string;
6
+ nodeCount: number;
7
+ selector: string;
8
+ }
9
+ export interface A11yDeepResult {
10
+ passed: boolean;
11
+ criticalCount: number;
12
+ seriousCount: number;
13
+ moderateCount: number;
14
+ minorCount: number;
15
+ violations: A11yViolation[];
16
+ url: string;
17
+ skipped: boolean;
18
+ summary: string;
19
+ }
20
+ export declare function runA11yDeep(url: string): Promise<A11yDeepResult>;
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runA11yDeep = runA11yDeep;
4
+ /**
5
+ * Deep accessibility audit using axe-core + Puppeteer.
6
+ *
7
+ * Runs axe-core against the running dev server to produce a detailed
8
+ * WCAG violation report beyond what Lighthouse provides.
9
+ * Blocker-capable: critical/serious violations can elevate to warnings.
10
+ */
11
+ let puppeteerModule = null;
12
+ try {
13
+ puppeteerModule = require("puppeteer");
14
+ }
15
+ catch {
16
+ // Puppeteer not available — a11y deep will be skipped
17
+ }
18
+ const IMPACT_ORDER = {
19
+ critical: 0,
20
+ serious: 1,
21
+ moderate: 2,
22
+ minor: 3,
23
+ };
24
+ async function runA11yDeep(url) {
25
+ if (!puppeteerModule) {
26
+ return {
27
+ passed: true,
28
+ criticalCount: 0,
29
+ seriousCount: 0,
30
+ moderateCount: 0,
31
+ minorCount: 0,
32
+ violations: [],
33
+ url,
34
+ skipped: true,
35
+ summary: "Skipped (puppeteer not available)",
36
+ };
37
+ }
38
+ console.log(" Running deep accessibility audit (axe-core)...");
39
+ const puppeteer = puppeteerModule.default || puppeteerModule;
40
+ let browser;
41
+ try {
42
+ browser = await puppeteer.launch({
43
+ headless: true,
44
+ args: ["--no-sandbox", "--disable-setuid-sandbox"],
45
+ });
46
+ const page = await browser.newPage();
47
+ await page.goto(url, { waitUntil: "networkidle2", timeout: 20000 });
48
+ // Inject and run axe-core
49
+ const results = await page.evaluate(async () => {
50
+ // @ts-expect-error — axe is injected at runtime
51
+ if (typeof window.axe === "undefined") {
52
+ try {
53
+ // Dynamically import axe-core from CDN as fallback
54
+ const script = document.createElement("script");
55
+ script.src = "https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.9.1/axe.min.js";
56
+ script.crossOrigin = "anonymous";
57
+ document.head.appendChild(script);
58
+ await new Promise((resolve, reject) => {
59
+ script.onload = () => resolve();
60
+ script.onerror = () => reject(new Error("axe-core CDN load failed"));
61
+ setTimeout(() => reject(new Error("axe-core load timed out")), 5000);
62
+ });
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ }
68
+ // @ts-expect-error — axe is now available
69
+ const result = await window.axe.run(document, {
70
+ runOnly: {
71
+ type: "tag",
72
+ values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"],
73
+ },
74
+ });
75
+ return {
76
+ violations: result.violations.map((v) => ({
77
+ id: v.id,
78
+ impact: v.impact,
79
+ description: v.description,
80
+ helpUrl: v.helpUrl,
81
+ nodeCount: v.nodes?.length ?? 0,
82
+ selector: v.nodes?.[0]?.target?.[0] ?? "",
83
+ })),
84
+ };
85
+ });
86
+ if (!results) {
87
+ console.log(" A11y deep: skipped (axe-core could not be loaded)");
88
+ return {
89
+ passed: true,
90
+ criticalCount: 0,
91
+ seriousCount: 0,
92
+ moderateCount: 0,
93
+ minorCount: 0,
94
+ violations: [],
95
+ url,
96
+ skipped: true,
97
+ summary: "Skipped (axe-core could not be loaded)",
98
+ };
99
+ }
100
+ const violations = results.violations
101
+ .map((v) => ({
102
+ id: v.id,
103
+ impact: v.impact ?? "moderate",
104
+ description: v.description,
105
+ helpUrl: v.helpUrl,
106
+ nodeCount: v.nodeCount,
107
+ selector: v.selector,
108
+ }))
109
+ .filter((v) => ["critical", "serious", "moderate", "minor"].includes(v.impact))
110
+ .sort((a, b) => (IMPACT_ORDER[a.impact] ?? 3) - (IMPACT_ORDER[b.impact] ?? 3));
111
+ const criticalCount = violations.filter((v) => v.impact === "critical").length;
112
+ const seriousCount = violations.filter((v) => v.impact === "serious").length;
113
+ const moderateCount = violations.filter((v) => v.impact === "moderate").length;
114
+ const minorCount = violations.filter((v) => v.impact === "minor").length;
115
+ const passed = criticalCount === 0 && seriousCount === 0;
116
+ const parts = [];
117
+ if (criticalCount > 0)
118
+ parts.push(`${criticalCount} critical`);
119
+ if (seriousCount > 0)
120
+ parts.push(`${seriousCount} serious`);
121
+ if (moderateCount > 0)
122
+ parts.push(`${moderateCount} moderate`);
123
+ if (minorCount > 0)
124
+ parts.push(`${minorCount} minor`);
125
+ const summary = parts.length > 0
126
+ ? `${parts.join(", ")} WCAG violation(s)`
127
+ : "No WCAG violations found";
128
+ console.log(` A11y deep: ${summary}`);
129
+ for (const v of violations.slice(0, 5)) {
130
+ console.error(` [${v.impact}] ${v.id}: ${v.description} (${v.nodeCount} nodes)`);
131
+ }
132
+ return {
133
+ passed,
134
+ criticalCount,
135
+ seriousCount,
136
+ moderateCount,
137
+ minorCount,
138
+ violations,
139
+ url,
140
+ skipped: false,
141
+ summary,
142
+ };
143
+ }
144
+ catch (err) {
145
+ console.error(` A11y deep error: ${err instanceof Error ? err.message : String(err)}`);
146
+ return {
147
+ passed: true,
148
+ criticalCount: 0,
149
+ seriousCount: 0,
150
+ moderateCount: 0,
151
+ minorCount: 0,
152
+ violations: [],
153
+ url,
154
+ skipped: true,
155
+ summary: `Error: ${err instanceof Error ? err.message : String(err)}`,
156
+ };
157
+ }
158
+ finally {
159
+ await browser?.close();
160
+ }
161
+ }
@@ -1,21 +1,25 @@
1
- /**
2
- * Broken-links audit.
3
- * Uses the crawl result to find all internal links, then validates each
4
- * with an HTTP HEAD/GET request. Links that return 4xx/5xx or timeout are
5
- * reported as blockers.
6
- */
7
- import { type CrawlResult } from "../crawler.js";
8
- export interface BrokenLink {
9
- url: string;
10
- path: string;
11
- status: number;
12
- statusText: string;
13
- severity: "critical" | "high";
14
- }
15
- export interface BrokenLinksResult {
16
- brokenLinks: BrokenLink[];
17
- checkedCount: number;
18
- hasBrokenLinks: boolean;
19
- summary: string;
20
- }
21
- export declare function auditBrokenLinks(crawlResult: CrawlResult, baseUrl: string, abortSignal?: AbortSignal): Promise<BrokenLinksResult>;
1
+ /**
2
+ * Broken-links audit.
3
+ * Uses the crawl result to find all internal links, then validates each
4
+ * with an HTTP HEAD/GET request. Links that return 4xx/5xx or timeout are
5
+ * reported as blockers.
6
+ */
7
+ import { type CrawlResult } from "../crawler.js";
8
+ export interface BrokenLink {
9
+ url: string;
10
+ path: string;
11
+ status: number;
12
+ statusText: string;
13
+ severity: "critical" | "high";
14
+ }
15
+ export interface BrokenLinksResult {
16
+ brokenLinks: BrokenLink[];
17
+ checkedCount: number;
18
+ hasBrokenLinks: boolean;
19
+ summary: string;
20
+ }
21
+ export interface BrokenLinksAuditOptions {
22
+ extraRoutes?: string[];
23
+ abortSignal?: AbortSignal;
24
+ }
25
+ export declare function auditBrokenLinks(crawlResult: CrawlResult | undefined, baseUrl: string, options?: BrokenLinksAuditOptions): Promise<BrokenLinksResult>;