laxy-verify 1.3.2 → 1.3.3

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
@@ -434,6 +434,11 @@ It is a pre-merge and pre-release verification layer, not your entire quality sy
434
434
 
435
435
  ## Changelog
436
436
 
437
+ ### v1.3.3 - Cleaner output and route validation
438
+
439
+ - Removed `Pro` and `Pro+` labels from verification logs so `npx laxy-verify .` prints plan-neutral output
440
+ - Runtime-discovered routes are now validated before they are used for Lighthouse and broken-link checks, so bogus `404` bundle snippets do not poison the verification result
441
+
437
442
  ### v1.3.2 - Auto port fallback
438
443
 
439
444
  - If port `3000` or your configured verification port is already in use, `laxy-verify` now starts its temporary dev server on the next available port automatically
@@ -38,7 +38,7 @@ exports.printMultiViewportResults = printMultiViewportResults;
38
38
  exports.allViewportsPass = allViewportsPass;
39
39
  exports.runMobileLighthouse = runMobileLighthouse;
40
40
  /**
41
- * Pro+ multi-viewport Lighthouse checks.
41
+ * Multi-viewport Lighthouse checks.
42
42
  *
43
43
  * Each viewport runs through the same direct Lighthouse execution path used by
44
44
  * the main verify flow so Windows cleanup behavior is consistent.
@@ -257,7 +257,7 @@ function compareWithBaseline(viewport, screenshot) {
257
257
  return { viewport: viewport.name, diffPercent: Math.round(diff * 100) / 100, baselineCreated: false };
258
258
  }
259
259
  async function runMultiViewportLighthouse(port) {
260
- console.log("\n [Pro+] Running multi-viewport Lighthouse checks (desktop, tablet, mobile)...");
260
+ console.log("\n Running multi-viewport Lighthouse checks (desktop, tablet, mobile)...");
261
261
  const tempRoot = path.join(process.cwd(), ".laxy-tmp", `multi-viewport-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
262
262
  fs.mkdirSync(tempRoot, { recursive: true });
263
263
  const results = { desktop: null, tablet: null, mobile: null };
@@ -299,7 +299,7 @@ function printMultiViewportResults(scores, thresholds) {
299
299
  tablet: "Tablet",
300
300
  mobile: "Mobile",
301
301
  };
302
- console.log("\n [Pro+] Multi-viewport results:");
302
+ console.log("\n Multi-viewport results:");
303
303
  for (const viewport of labels) {
304
304
  const score = scores[viewport];
305
305
  if (!score) {
@@ -329,17 +329,17 @@ function allViewportsPass(scores, thresholds) {
329
329
  * full multi-viewport overhead. Lets Pro users catch mobile regressions.
330
330
  */
331
331
  async function runMobileLighthouse(port) {
332
- console.log("\n [Pro] Running mobile Lighthouse check...");
332
+ console.log("\n Running mobile Lighthouse check...");
333
333
  const mobileViewport = VIEWPORTS.find((v) => v.name === "mobile");
334
334
  const tempRoot = path.join(process.cwd(), ".laxy-tmp", `mobile-lh-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
335
335
  fs.mkdirSync(tempRoot, { recursive: true });
336
336
  try {
337
337
  const scores = await runLighthouseForViewport(port, mobileViewport, tempRoot);
338
338
  if (scores) {
339
- console.log(` [Pro] Mobile: P=${scores.performance} A=${scores.accessibility} SEO=${scores.seo} BP=${scores.bestPractices}`);
339
+ console.log(` Mobile: P=${scores.performance} A=${scores.accessibility} SEO=${scores.seo} BP=${scores.bestPractices}`);
340
340
  }
341
341
  else {
342
- console.log(" [Pro] Mobile Lighthouse: failed to collect");
342
+ console.log(" Mobile Lighthouse: failed to collect");
343
343
  }
344
344
  return scores;
345
345
  }
@@ -4,4 +4,5 @@ export interface RuntimeRouteDiscoveryResult {
4
4
  }
5
5
  export declare function extractScriptUrlsFromHtml(html: string, baseUrl: string): string[];
6
6
  export declare function extractRoutesFromText(content: string): string[];
7
+ export declare function filterReachableRoutes(baseUrl: string, routes: string[]): Promise<string[]>;
7
8
  export declare function discoverRuntimeRoutes(baseUrl: string): Promise<RuntimeRouteDiscoveryResult>;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.extractScriptUrlsFromHtml = extractScriptUrlsFromHtml;
4
4
  exports.extractRoutesFromText = extractRoutesFromText;
5
+ exports.filterReachableRoutes = filterReachableRoutes;
5
6
  exports.discoverRuntimeRoutes = discoverRuntimeRoutes;
6
7
  const SCRIPT_SRC_REGEX = /<script[^>]+src=["']([^"'#?]+(?:\?[^"'#]*)?)["']/gi;
7
8
  const HTML_ROUTE_REGEX = /(?:href|data-href)=["'](\/[^"'#? ]*)/gi;
@@ -72,6 +73,34 @@ function extractRoutesFromText(content) {
72
73
  }
73
74
  return routes;
74
75
  }
76
+ async function probeRuntimeRoute(baseUrl, route) {
77
+ try {
78
+ const res = await fetch(new URL(route, baseUrl), {
79
+ signal: AbortSignal.timeout(5000),
80
+ redirect: "follow",
81
+ headers: { accept: "text/html,application/xhtml+xml" },
82
+ });
83
+ if (res.status === 404 || res.status === 410) {
84
+ return false;
85
+ }
86
+ const contentType = res.headers.get("content-type")?.toLowerCase() ?? "";
87
+ if (res.status >= 200 && res.status < 300 && contentType && !contentType.includes("text/html")) {
88
+ return false;
89
+ }
90
+ return true;
91
+ }
92
+ catch {
93
+ return false;
94
+ }
95
+ }
96
+ async function filterReachableRoutes(baseUrl, routes) {
97
+ const candidates = Array.from(new Set(routes));
98
+ const checks = await Promise.all(candidates.map(async (route) => ({
99
+ route,
100
+ reachable: await probeRuntimeRoute(baseUrl, route),
101
+ })));
102
+ return checks.filter((item) => item.reachable).map((item) => item.route);
103
+ }
75
104
  async function discoverRuntimeRoutes(baseUrl) {
76
105
  const htmlRes = await fetch(baseUrl, {
77
106
  signal: AbortSignal.timeout(8000),
@@ -101,8 +130,9 @@ async function discoverRuntimeRoutes(baseUrl) {
101
130
  // Skip chunk fetch failures. This is best-effort coverage expansion.
102
131
  }
103
132
  }));
133
+ const validatedRoutes = await filterReachableRoutes(baseUrl, Array.from(routes));
104
134
  return {
105
- routes: Array.from(routes).sort((a, b) => a.localeCompare(b)),
135
+ routes: validatedRoutes.sort((a, b) => a.localeCompare(b)),
106
136
  scriptUrls,
107
137
  };
108
138
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "laxy-verify",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "description": "Frontend verification CLI for build checks, Lighthouse, E2E, and release readiness",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",